]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet/tcp_cache.c
293a0fc0cda76681495047474c61dd2047111f2f
[apple/xnu.git] / bsd / netinet / tcp_cache.c
1 /*
2 * Copyright (c) 2015-2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /* TCP-cache to store and retrieve TCP-related information */
30
31 #include <net/flowhash.h>
32 #include <net/route.h>
33 #include <net/necp.h>
34 #include <netinet/in_pcb.h>
35 #include <netinet/mptcp_var.h>
36 #include <netinet/tcp_cache.h>
37 #include <netinet/tcp_seq.h>
38 #include <netinet/tcp_var.h>
39 #include <kern/locks.h>
40 #include <sys/queue.h>
41 #include <dev/random/randomdev.h>
42
43 typedef union {
44 struct in_addr addr;
45 struct in6_addr addr6;
46 } in_4_6_addr;
47
48 struct tcp_heuristic_key {
49 union {
50 uint8_t thk_net_signature[IFNET_SIGNATURELEN];
51 in_4_6_addr thk_ip;
52 };
53 sa_family_t thk_family;
54 };
55
56 struct tcp_heuristic {
57 SLIST_ENTRY(tcp_heuristic) list;
58
59 uint32_t th_last_access;
60
61 struct tcp_heuristic_key th_key;
62
63 char th_val_start[0]; /* Marker for memsetting to 0 */
64
65 uint8_t th_tfo_data_loss; /* The number of times a SYN+data has been lost */
66 uint8_t th_tfo_req_loss; /* The number of times a SYN+cookie-req has been lost */
67 uint8_t th_tfo_data_rst; /* The number of times a SYN+data has received a RST */
68 uint8_t th_tfo_req_rst; /* The number of times a SYN+cookie-req has received a RST */
69 uint8_t th_mptcp_loss; /* The number of times a SYN+MP_CAPABLE has been lost */
70 uint8_t th_ecn_loss; /* The number of times a SYN+ecn has been lost */
71 uint8_t th_ecn_aggressive; /* The number of times we did an aggressive fallback */
72 uint8_t th_ecn_droprst; /* The number of times ECN connections received a RST after first data pkt */
73 uint8_t th_ecn_droprxmt; /* The number of times ECN connection is dropped after multiple retransmits */
74 uint8_t th_ecn_synrst; /* number of times RST was received in response to an ECN enabled SYN */
75 uint32_t th_tfo_enabled_time; /* The moment when we reenabled TFO after backing off */
76 uint32_t th_tfo_backoff_until; /* Time until when we should not try out TFO */
77 uint32_t th_tfo_backoff; /* Current backoff timer */
78 uint32_t th_mptcp_backoff; /* Time until when we should not try out MPTCP */
79 uint32_t th_ecn_backoff; /* Time until when we should not try out ECN */
80
81 uint8_t th_tfo_in_backoff:1, /* Are we avoiding TFO due to the backoff timer? */
82 th_mptcp_in_backoff:1; /* Are we avoiding MPTCP due to the backoff timer? */
83
84 char th_val_end[0]; /* Marker for memsetting to 0 */
85 };
86
87 struct tcp_heuristics_head {
88 SLIST_HEAD(tcp_heur_bucket, tcp_heuristic) tcp_heuristics;
89
90 /* Per-hashbucket lock to avoid lock-contention */
91 lck_mtx_t thh_mtx;
92 };
93
94 struct tcp_cache_key {
95 sa_family_t tck_family;
96
97 struct tcp_heuristic_key tck_src;
98 in_4_6_addr tck_dst;
99 };
100
101 struct tcp_cache {
102 SLIST_ENTRY(tcp_cache) list;
103
104 u_int32_t tc_last_access;
105
106 struct tcp_cache_key tc_key;
107
108 u_int8_t tc_tfo_cookie[TFO_COOKIE_LEN_MAX];
109 u_int8_t tc_tfo_cookie_len;
110 };
111
112 struct tcp_cache_head {
113 SLIST_HEAD(tcp_cache_bucket, tcp_cache) tcp_caches;
114
115 /* Per-hashbucket lock to avoid lock-contention */
116 lck_mtx_t tch_mtx;
117 };
118
119 struct tcp_cache_key_src {
120 struct ifnet *ifp;
121 in_4_6_addr laddr;
122 in_4_6_addr faddr;
123 int af;
124 };
125
126 static u_int32_t tcp_cache_hash_seed;
127
128 size_t tcp_cache_size;
129
130 /*
131 * The maximum depth of the hash-bucket. This way we limit the tcp_cache to
132 * TCP_CACHE_BUCKET_SIZE * tcp_cache_size and have "natural" garbage collection
133 */
134 #define TCP_CACHE_BUCKET_SIZE 5
135
136 static struct tcp_cache_head *tcp_cache;
137
138 decl_lck_mtx_data(, tcp_cache_mtx);
139
140 static lck_attr_t *tcp_cache_mtx_attr;
141 static lck_grp_t *tcp_cache_mtx_grp;
142 static lck_grp_attr_t *tcp_cache_mtx_grp_attr;
143
144 static struct tcp_heuristics_head *tcp_heuristics;
145
146 decl_lck_mtx_data(, tcp_heuristics_mtx);
147
148 static lck_attr_t *tcp_heuristic_mtx_attr;
149 static lck_grp_t *tcp_heuristic_mtx_grp;
150 static lck_grp_attr_t *tcp_heuristic_mtx_grp_attr;
151
152 static uint32_t tcp_backoff_maximum = 65536;
153
154 SYSCTL_UINT(_net_inet_tcp, OID_AUTO, backoff_maximum, CTLFLAG_RW | CTLFLAG_LOCKED,
155 &tcp_backoff_maximum, 0, "Maximum time for which we won't try TFO");
156
157 SYSCTL_SKMEM_TCP_INT(OID_AUTO, ecn_timeout, CTLFLAG_RW | CTLFLAG_LOCKED,
158 static int, tcp_ecn_timeout, 60, "Initial minutes to wait before re-trying ECN");
159
160 SYSCTL_SKMEM_TCP_INT(OID_AUTO, disable_tcp_heuristics, CTLFLAG_RW | CTLFLAG_LOCKED,
161 static int, disable_tcp_heuristics, 0, "Set to 1, to disable all TCP heuristics (TFO, ECN, MPTCP)");
162
163 static uint32_t tcp_min_to_hz(uint32_t minutes)
164 {
165 if (minutes > 65536)
166 return ((uint32_t)65536 * 60 * TCP_RETRANSHZ);
167
168 return (minutes * 60 * TCP_RETRANSHZ);
169 }
170
171 /*
172 * This number is coupled with tcp_ecn_timeout, because we want to prevent
173 * integer overflow. Need to find an unexpensive way to prevent integer overflow
174 * while still allowing a dynamic sysctl.
175 */
176 #define TCP_CACHE_OVERFLOW_PROTECT 9
177
178 /* Number of SYN-losses we accept */
179 #define TFO_MAX_COOKIE_LOSS 2
180 #define ECN_MAX_SYN_LOSS 2
181 #define MPTCP_MAX_SYN_LOSS 2
182 #define ECN_MAX_DROPRST 1
183 #define ECN_MAX_DROPRXMT 4
184 #define ECN_MAX_SYNRST 4
185
186 /* Flags for setting/unsetting loss-heuristics, limited to 4 bytes */
187 #define TCPCACHE_F_TFO_REQ 0x01
188 #define TCPCACHE_F_TFO_DATA 0x02
189 #define TCPCACHE_F_ECN 0x04
190 #define TCPCACHE_F_MPTCP 0x08
191 #define TCPCACHE_F_ECN_DROPRST 0x10
192 #define TCPCACHE_F_ECN_DROPRXMT 0x20
193 #define TCPCACHE_F_TFO_REQ_RST 0x40
194 #define TCPCACHE_F_TFO_DATA_RST 0x80
195 #define TCPCACHE_F_ECN_SYNRST 0x100
196
197 /* Always retry ECN after backing off to this level for some heuristics */
198 #define ECN_RETRY_LIMIT 9
199
200 #define TCP_CACHE_INC_IFNET_STAT(_ifp_, _af_, _stat_) { \
201 if ((_ifp_) != NULL) { \
202 if ((_af_) == AF_INET6) { \
203 (_ifp_)->if_ipv6_stat->_stat_++;\
204 } else { \
205 (_ifp_)->if_ipv4_stat->_stat_++;\
206 }\
207 }\
208 }
209
210 /*
211 * Round up to next higher power-of 2. See "Bit Twiddling Hacks".
212 *
213 * Might be worth moving this to a library so that others
214 * (e.g., scale_to_powerof2()) can use this as well instead of a while-loop.
215 */
216 static u_int32_t tcp_cache_roundup2(u_int32_t a)
217 {
218 a--;
219 a |= a >> 1;
220 a |= a >> 2;
221 a |= a >> 4;
222 a |= a >> 8;
223 a |= a >> 16;
224 a++;
225
226 return a;
227 }
228
229 static void tcp_cache_hash_src(struct tcp_cache_key_src *tcks, struct tcp_heuristic_key *key)
230 {
231 struct ifnet *ifp = tcks->ifp;
232 uint8_t len = sizeof(key->thk_net_signature);
233 uint16_t flags;
234
235 if (tcks->af == AF_INET6) {
236 int ret;
237
238 key->thk_family = AF_INET6;
239 ret = ifnet_get_netsignature(ifp, AF_INET6, &len, &flags,
240 key->thk_net_signature);
241
242 /*
243 * ifnet_get_netsignature only returns EINVAL if ifn is NULL
244 * (we made sure that in the other cases it does not). So,
245 * in this case we should take the connection's address.
246 */
247 if (ret == ENOENT || ret == EINVAL)
248 memcpy(&key->thk_ip.addr6, &tcks->laddr.addr6, sizeof(struct in6_addr));
249 } else {
250 int ret;
251
252 key->thk_family = AF_INET;
253 ret = ifnet_get_netsignature(ifp, AF_INET, &len, &flags,
254 key->thk_net_signature);
255
256 /*
257 * ifnet_get_netsignature only returns EINVAL if ifn is NULL
258 * (we made sure that in the other cases it does not). So,
259 * in this case we should take the connection's address.
260 */
261 if (ret == ENOENT || ret == EINVAL)
262 memcpy(&key->thk_ip.addr, &tcks->laddr.addr, sizeof(struct in_addr));
263 }
264 }
265
266 static u_int16_t tcp_cache_hash(struct tcp_cache_key_src *tcks, struct tcp_cache_key *key)
267 {
268 u_int32_t hash;
269
270 bzero(key, sizeof(struct tcp_cache_key));
271
272 tcp_cache_hash_src(tcks, &key->tck_src);
273
274 if (tcks->af == AF_INET6) {
275 key->tck_family = AF_INET6;
276 memcpy(&key->tck_dst.addr6, &tcks->faddr.addr6,
277 sizeof(struct in6_addr));
278 } else {
279 key->tck_family = AF_INET;
280 memcpy(&key->tck_dst.addr, &tcks->faddr.addr,
281 sizeof(struct in_addr));
282 }
283
284 hash = net_flowhash(key, sizeof(struct tcp_cache_key),
285 tcp_cache_hash_seed);
286
287 return (hash & (tcp_cache_size - 1));
288 }
289
290 static void tcp_cache_unlock(struct tcp_cache_head *head)
291 {
292 lck_mtx_unlock(&head->tch_mtx);
293 }
294
295 /*
296 * Make sure that everything that happens after tcp_getcache_with_lock()
297 * is short enough to justify that you hold the per-bucket lock!!!
298 *
299 * Otherwise, better build another lookup-function that does not hold the
300 * lock and you copy out the bits and bytes.
301 *
302 * That's why we provide the head as a "return"-pointer so that the caller
303 * can give it back to use for tcp_cache_unlock().
304 */
305 static struct tcp_cache *tcp_getcache_with_lock(struct tcp_cache_key_src *tcks,
306 int create, struct tcp_cache_head **headarg)
307 {
308 struct tcp_cache *tpcache = NULL;
309 struct tcp_cache_head *head;
310 struct tcp_cache_key key;
311 u_int16_t hash;
312 int i = 0;
313
314 hash = tcp_cache_hash(tcks, &key);
315 head = &tcp_cache[hash];
316
317 lck_mtx_lock(&head->tch_mtx);
318
319 /*** First step: Look for the tcp_cache in our bucket ***/
320 SLIST_FOREACH(tpcache, &head->tcp_caches, list) {
321 if (memcmp(&tpcache->tc_key, &key, sizeof(key)) == 0)
322 break;
323
324 i++;
325 }
326
327 /*** Second step: If it's not there, create/recycle it ***/
328 if ((tpcache == NULL) && create) {
329 if (i >= TCP_CACHE_BUCKET_SIZE) {
330 struct tcp_cache *oldest_cache = NULL;
331 u_int32_t max_age = 0;
332
333 /* Look for the oldest tcp_cache in the bucket */
334 SLIST_FOREACH(tpcache, &head->tcp_caches, list) {
335 u_int32_t age = tcp_now - tpcache->tc_last_access;
336 if (age > max_age) {
337 max_age = age;
338 oldest_cache = tpcache;
339 }
340 }
341 VERIFY(oldest_cache != NULL);
342
343 tpcache = oldest_cache;
344
345 /* We recycle, thus let's indicate that there is no cookie */
346 tpcache->tc_tfo_cookie_len = 0;
347 } else {
348 /* Create a new cache and add it to the list */
349 tpcache = _MALLOC(sizeof(struct tcp_cache), M_TEMP,
350 M_NOWAIT | M_ZERO);
351 if (tpcache == NULL)
352 goto out_null;
353
354 SLIST_INSERT_HEAD(&head->tcp_caches, tpcache, list);
355 }
356
357 memcpy(&tpcache->tc_key, &key, sizeof(key));
358 }
359
360 if (tpcache == NULL)
361 goto out_null;
362
363 /* Update timestamp for garbage collection purposes */
364 tpcache->tc_last_access = tcp_now;
365 *headarg = head;
366
367 return (tpcache);
368
369 out_null:
370 tcp_cache_unlock(head);
371 return (NULL);
372 }
373
374 static void tcp_cache_key_src_create(struct tcpcb *tp, struct tcp_cache_key_src *tcks)
375 {
376 struct inpcb *inp = tp->t_inpcb;
377 memset(tcks, 0, sizeof(*tcks));
378
379 tcks->ifp = inp->inp_last_outifp;
380
381 if (inp->inp_vflag & INP_IPV6) {
382 memcpy(&tcks->laddr.addr6, &inp->in6p_laddr, sizeof(struct in6_addr));
383 memcpy(&tcks->faddr.addr6, &inp->in6p_faddr, sizeof(struct in6_addr));
384 tcks->af = AF_INET6;
385 } else {
386 memcpy(&tcks->laddr.addr, &inp->inp_laddr, sizeof(struct in_addr));
387 memcpy(&tcks->faddr.addr, &inp->inp_faddr, sizeof(struct in_addr));
388 tcks->af = AF_INET;
389 }
390
391 return;
392 }
393
394 static void tcp_cache_set_cookie_common(struct tcp_cache_key_src *tcks, u_char *cookie, u_int8_t len)
395 {
396 struct tcp_cache_head *head;
397 struct tcp_cache *tpcache;
398
399 /* Call lookup/create function */
400 tpcache = tcp_getcache_with_lock(tcks, 1, &head);
401 if (tpcache == NULL)
402 return;
403
404 tpcache->tc_tfo_cookie_len = len;
405 memcpy(tpcache->tc_tfo_cookie, cookie, len);
406
407 tcp_cache_unlock(head);
408 }
409
410 void tcp_cache_set_cookie(struct tcpcb *tp, u_char *cookie, u_int8_t len)
411 {
412 struct tcp_cache_key_src tcks;
413
414 tcp_cache_key_src_create(tp, &tcks);
415 tcp_cache_set_cookie_common(&tcks, cookie, len);
416 }
417
418 static int tcp_cache_get_cookie_common(struct tcp_cache_key_src *tcks, u_char *cookie, u_int8_t *len)
419 {
420 struct tcp_cache_head *head;
421 struct tcp_cache *tpcache;
422
423 /* Call lookup/create function */
424 tpcache = tcp_getcache_with_lock(tcks, 1, &head);
425 if (tpcache == NULL) {
426 return (0);
427 }
428
429 if (tpcache->tc_tfo_cookie_len == 0) {
430 tcp_cache_unlock(head);
431 return (0);
432 }
433
434 /*
435 * Not enough space - this should never happen as it has been checked
436 * in tcp_tfo_check. So, fail here!
437 */
438 VERIFY(tpcache->tc_tfo_cookie_len <= *len);
439
440 memcpy(cookie, tpcache->tc_tfo_cookie, tpcache->tc_tfo_cookie_len);
441 *len = tpcache->tc_tfo_cookie_len;
442
443 tcp_cache_unlock(head);
444
445 return (1);
446 }
447
448 /*
449 * Get the cookie related to 'tp', and copy it into 'cookie', provided that len
450 * is big enough (len designates the available memory.
451 * Upon return, 'len' is set to the cookie's length.
452 *
453 * Returns 0 if we should request a cookie.
454 * Returns 1 if the cookie has been found and written.
455 */
456 int tcp_cache_get_cookie(struct tcpcb *tp, u_char *cookie, u_int8_t *len)
457 {
458 struct tcp_cache_key_src tcks;
459
460 tcp_cache_key_src_create(tp, &tcks);
461 return tcp_cache_get_cookie_common(&tcks, cookie, len);
462 }
463
464 static unsigned int tcp_cache_get_cookie_len_common(struct tcp_cache_key_src *tcks)
465 {
466 struct tcp_cache_head *head;
467 struct tcp_cache *tpcache;
468 unsigned int cookie_len;
469
470 /* Call lookup/create function */
471 tpcache = tcp_getcache_with_lock(tcks, 1, &head);
472 if (tpcache == NULL)
473 return (0);
474
475 cookie_len = tpcache->tc_tfo_cookie_len;
476
477 tcp_cache_unlock(head);
478
479 return cookie_len;
480 }
481
482 unsigned int tcp_cache_get_cookie_len(struct tcpcb *tp)
483 {
484 struct tcp_cache_key_src tcks;
485
486 tcp_cache_key_src_create(tp, &tcks);
487 return tcp_cache_get_cookie_len_common(&tcks);
488 }
489
490 static u_int16_t tcp_heuristics_hash(struct tcp_cache_key_src *tcks, struct tcp_heuristic_key *key)
491 {
492 u_int32_t hash;
493
494 bzero(key, sizeof(struct tcp_heuristic_key));
495
496 tcp_cache_hash_src(tcks, key);
497
498 hash = net_flowhash(key, sizeof(struct tcp_heuristic_key),
499 tcp_cache_hash_seed);
500
501 return (hash & (tcp_cache_size - 1));
502 }
503
504 static void tcp_heuristic_unlock(struct tcp_heuristics_head *head)
505 {
506 lck_mtx_unlock(&head->thh_mtx);
507 }
508
509 /*
510 * Make sure that everything that happens after tcp_getheuristic_with_lock()
511 * is short enough to justify that you hold the per-bucket lock!!!
512 *
513 * Otherwise, better build another lookup-function that does not hold the
514 * lock and you copy out the bits and bytes.
515 *
516 * That's why we provide the head as a "return"-pointer so that the caller
517 * can give it back to use for tcp_heur_unlock().
518 *
519 *
520 * ToDo - way too much code-duplication. We should create an interface to handle
521 * bucketized hashtables with recycling of the oldest element.
522 */
523 static struct tcp_heuristic *tcp_getheuristic_with_lock(struct tcp_cache_key_src *tcks,
524 int create, struct tcp_heuristics_head **headarg)
525 {
526 struct tcp_heuristic *tpheur = NULL;
527 struct tcp_heuristics_head *head;
528 struct tcp_heuristic_key key;
529 u_int16_t hash;
530 int i = 0;
531
532 hash = tcp_heuristics_hash(tcks, &key);
533 head = &tcp_heuristics[hash];
534
535 lck_mtx_lock(&head->thh_mtx);
536
537 /*** First step: Look for the tcp_heur in our bucket ***/
538 SLIST_FOREACH(tpheur, &head->tcp_heuristics, list) {
539 if (memcmp(&tpheur->th_key, &key, sizeof(key)) == 0)
540 break;
541
542 i++;
543 }
544
545 /*** Second step: If it's not there, create/recycle it ***/
546 if ((tpheur == NULL) && create) {
547 if (i >= TCP_CACHE_BUCKET_SIZE) {
548 struct tcp_heuristic *oldest_heur = NULL;
549 u_int32_t max_age = 0;
550
551 /* Look for the oldest tcp_heur in the bucket */
552 SLIST_FOREACH(tpheur, &head->tcp_heuristics, list) {
553 u_int32_t age = tcp_now - tpheur->th_last_access;
554 if (age > max_age) {
555 max_age = age;
556 oldest_heur = tpheur;
557 }
558 }
559 VERIFY(oldest_heur != NULL);
560
561 tpheur = oldest_heur;
562
563 /* We recycle - set everything to 0 */
564 bzero(tpheur->th_val_start,
565 tpheur->th_val_end - tpheur->th_val_start);
566 } else {
567 /* Create a new heuristic and add it to the list */
568 tpheur = _MALLOC(sizeof(struct tcp_heuristic), M_TEMP,
569 M_NOWAIT | M_ZERO);
570 if (tpheur == NULL)
571 goto out_null;
572
573 SLIST_INSERT_HEAD(&head->tcp_heuristics, tpheur, list);
574 }
575
576 /*
577 * Set to tcp_now, to make sure it won't be > than tcp_now in the
578 * near future.
579 */
580 tpheur->th_ecn_backoff = tcp_now;
581 tpheur->th_tfo_backoff_until = tcp_now;
582 tpheur->th_mptcp_backoff = tcp_now;
583 tpheur->th_tfo_backoff = tcp_min_to_hz(tcp_ecn_timeout);
584
585 memcpy(&tpheur->th_key, &key, sizeof(key));
586 }
587
588 if (tpheur == NULL)
589 goto out_null;
590
591 /* Update timestamp for garbage collection purposes */
592 tpheur->th_last_access = tcp_now;
593 *headarg = head;
594
595 return (tpheur);
596
597 out_null:
598 tcp_heuristic_unlock(head);
599 return (NULL);
600 }
601
602 static void tcp_heuristic_reset_counters(struct tcp_cache_key_src *tcks, u_int8_t flags)
603 {
604 struct tcp_heuristics_head *head;
605 struct tcp_heuristic *tpheur;
606
607 /*
608 * Don't attempt to create it! Keep the heuristics clean if the
609 * server does not support TFO. This reduces the lookup-cost on
610 * our side.
611 */
612 tpheur = tcp_getheuristic_with_lock(tcks, 0, &head);
613 if (tpheur == NULL)
614 return;
615
616 if (flags & TCPCACHE_F_TFO_DATA) {
617 tpheur->th_tfo_data_loss = 0;
618 }
619
620 if (flags & TCPCACHE_F_TFO_REQ) {
621 tpheur->th_tfo_req_loss = 0;
622 }
623
624 if (flags & TCPCACHE_F_TFO_DATA_RST) {
625 tpheur->th_tfo_data_rst = 0;
626 }
627
628 if (flags & TCPCACHE_F_TFO_REQ_RST) {
629 tpheur->th_tfo_req_rst = 0;
630 }
631
632 if (flags & TCPCACHE_F_ECN) {
633 tpheur->th_ecn_loss = 0;
634 tpheur->th_ecn_synrst = 0;
635 }
636
637 if (flags & TCPCACHE_F_MPTCP)
638 tpheur->th_mptcp_loss = 0;
639
640 tcp_heuristic_unlock(head);
641 }
642
643 void tcp_heuristic_tfo_success(struct tcpcb *tp)
644 {
645 struct tcp_cache_key_src tcks;
646 uint8_t flag = 0;
647
648 tcp_cache_key_src_create(tp, &tcks);
649
650 if (tp->t_tfo_stats & TFO_S_SYN_DATA_SENT)
651 flag = (TCPCACHE_F_TFO_DATA | TCPCACHE_F_TFO_REQ |
652 TCPCACHE_F_TFO_DATA_RST | TCPCACHE_F_TFO_REQ_RST );
653 if (tp->t_tfo_stats & TFO_S_COOKIE_REQ)
654 flag = (TCPCACHE_F_TFO_REQ | TCPCACHE_F_TFO_REQ_RST);
655
656 tcp_heuristic_reset_counters(&tcks, flag);
657 }
658
659 void tcp_heuristic_mptcp_success(struct tcpcb *tp)
660 {
661 struct tcp_cache_key_src tcks;
662
663 tcp_cache_key_src_create(tp, &tcks);
664 tcp_heuristic_reset_counters(&tcks, TCPCACHE_F_MPTCP);
665 }
666
667 void tcp_heuristic_ecn_success(struct tcpcb *tp)
668 {
669 struct tcp_cache_key_src tcks;
670
671 tcp_cache_key_src_create(tp, &tcks);
672 tcp_heuristic_reset_counters(&tcks, TCPCACHE_F_ECN);
673 }
674
675 static void __tcp_heuristic_tfo_middlebox_common(struct tcp_heuristic *tpheur)
676 {
677 if (tpheur->th_tfo_in_backoff)
678 return;
679
680 tpheur->th_tfo_in_backoff = 1;
681
682 if (tpheur->th_tfo_enabled_time) {
683 uint32_t old_backoff = tpheur->th_tfo_backoff;
684
685 tpheur->th_tfo_backoff -= (tcp_now - tpheur->th_tfo_enabled_time);
686 if (tpheur->th_tfo_backoff > old_backoff)
687 tpheur->th_tfo_backoff = tcp_min_to_hz(tcp_ecn_timeout);
688 }
689
690 tpheur->th_tfo_backoff_until = tcp_now + tpheur->th_tfo_backoff;
691
692 /* Then, increase the backoff time */
693 tpheur->th_tfo_backoff *= 2;
694
695 if (tpheur->th_tfo_backoff > tcp_min_to_hz(tcp_backoff_maximum))
696 tpheur->th_tfo_backoff = tcp_min_to_hz(tcp_ecn_timeout);
697 }
698
699 static void tcp_heuristic_tfo_middlebox_common(struct tcp_cache_key_src *tcks)
700 {
701 struct tcp_heuristics_head *head;
702 struct tcp_heuristic *tpheur;
703
704 tpheur = tcp_getheuristic_with_lock(tcks, 1, &head);
705 if (tpheur == NULL)
706 return;
707
708 __tcp_heuristic_tfo_middlebox_common(tpheur);
709
710 tcp_heuristic_unlock(head);
711 }
712
713 static void tcp_heuristic_inc_counters(struct tcp_cache_key_src *tcks,
714 u_int32_t flags)
715 {
716 struct tcp_heuristics_head *head;
717 struct tcp_heuristic *tpheur;
718
719 tpheur = tcp_getheuristic_with_lock(tcks, 1, &head);
720 if (tpheur == NULL)
721 return;
722
723 /* Limit to prevent integer-overflow during exponential backoff */
724 if ((flags & TCPCACHE_F_TFO_DATA) && tpheur->th_tfo_data_loss < TCP_CACHE_OVERFLOW_PROTECT) {
725 tpheur->th_tfo_data_loss++;
726
727 if (tpheur->th_tfo_data_loss >= TFO_MAX_COOKIE_LOSS)
728 __tcp_heuristic_tfo_middlebox_common(tpheur);
729 }
730
731 if ((flags & TCPCACHE_F_TFO_REQ) && tpheur->th_tfo_req_loss < TCP_CACHE_OVERFLOW_PROTECT) {
732 tpheur->th_tfo_req_loss++;
733
734 if (tpheur->th_tfo_req_loss >= TFO_MAX_COOKIE_LOSS)
735 __tcp_heuristic_tfo_middlebox_common(tpheur);
736 }
737
738 if ((flags & TCPCACHE_F_TFO_DATA_RST) && tpheur->th_tfo_data_rst < TCP_CACHE_OVERFLOW_PROTECT) {
739 tpheur->th_tfo_data_rst++;
740
741 if (tpheur->th_tfo_data_rst >= TFO_MAX_COOKIE_LOSS)
742 __tcp_heuristic_tfo_middlebox_common(tpheur);
743 }
744
745 if ((flags & TCPCACHE_F_TFO_REQ_RST) && tpheur->th_tfo_req_rst < TCP_CACHE_OVERFLOW_PROTECT) {
746 tpheur->th_tfo_req_rst++;
747
748 if (tpheur->th_tfo_req_rst >= TFO_MAX_COOKIE_LOSS)
749 __tcp_heuristic_tfo_middlebox_common(tpheur);
750 }
751
752 if ((flags & TCPCACHE_F_ECN) && tpheur->th_ecn_loss < TCP_CACHE_OVERFLOW_PROTECT) {
753 tpheur->th_ecn_loss++;
754 if (tpheur->th_ecn_loss >= ECN_MAX_SYN_LOSS) {
755 tcpstat.tcps_ecn_fallback_synloss++;
756 TCP_CACHE_INC_IFNET_STAT(tcks->ifp, tcks->af, ecn_fallback_synloss);
757 tpheur->th_ecn_backoff = tcp_now +
758 (tcp_min_to_hz(tcp_ecn_timeout) <<
759 (tpheur->th_ecn_loss - ECN_MAX_SYN_LOSS));
760 }
761 }
762
763 if ((flags & TCPCACHE_F_MPTCP) &&
764 tpheur->th_mptcp_loss < TCP_CACHE_OVERFLOW_PROTECT) {
765 tpheur->th_mptcp_loss++;
766 if (tpheur->th_mptcp_loss >= MPTCP_MAX_SYN_LOSS) {
767 /*
768 * Yes, we take tcp_ecn_timeout, to avoid adding yet
769 * another sysctl that is just used for testing.
770 */
771 tpheur->th_mptcp_backoff = tcp_now +
772 (tcp_min_to_hz(tcp_ecn_timeout) <<
773 (tpheur->th_mptcp_loss - MPTCP_MAX_SYN_LOSS));
774 }
775 }
776
777 if ((flags & TCPCACHE_F_ECN_DROPRST) &&
778 tpheur->th_ecn_droprst < TCP_CACHE_OVERFLOW_PROTECT) {
779 tpheur->th_ecn_droprst++;
780 if (tpheur->th_ecn_droprst >= ECN_MAX_DROPRST) {
781 tcpstat.tcps_ecn_fallback_droprst++;
782 TCP_CACHE_INC_IFNET_STAT(tcks->ifp, tcks->af,
783 ecn_fallback_droprst);
784 tpheur->th_ecn_backoff = tcp_now +
785 (tcp_min_to_hz(tcp_ecn_timeout) <<
786 (tpheur->th_ecn_droprst - ECN_MAX_DROPRST));
787
788 }
789 }
790
791 if ((flags & TCPCACHE_F_ECN_DROPRXMT) &&
792 tpheur->th_ecn_droprxmt < TCP_CACHE_OVERFLOW_PROTECT) {
793 tpheur->th_ecn_droprxmt++;
794 if (tpheur->th_ecn_droprxmt >= ECN_MAX_DROPRXMT) {
795 tcpstat.tcps_ecn_fallback_droprxmt++;
796 TCP_CACHE_INC_IFNET_STAT(tcks->ifp, tcks->af,
797 ecn_fallback_droprxmt);
798 tpheur->th_ecn_backoff = tcp_now +
799 (tcp_min_to_hz(tcp_ecn_timeout) <<
800 (tpheur->th_ecn_droprxmt - ECN_MAX_DROPRXMT));
801 }
802 }
803 if ((flags & TCPCACHE_F_ECN_SYNRST) &&
804 tpheur->th_ecn_synrst < TCP_CACHE_OVERFLOW_PROTECT) {
805 tpheur->th_ecn_synrst++;
806 if (tpheur->th_ecn_synrst >= ECN_MAX_SYNRST) {
807 tcpstat.tcps_ecn_fallback_synrst++;
808 TCP_CACHE_INC_IFNET_STAT(tcks->ifp, tcks->af,
809 ecn_fallback_synrst);
810 tpheur->th_ecn_backoff = tcp_now +
811 (tcp_min_to_hz(tcp_ecn_timeout) <<
812 (tpheur->th_ecn_synrst - ECN_MAX_SYNRST));
813 }
814 }
815 tcp_heuristic_unlock(head);
816 }
817
818 void tcp_heuristic_tfo_loss(struct tcpcb *tp)
819 {
820 struct tcp_cache_key_src tcks;
821 uint32_t flag = 0;
822
823 tcp_cache_key_src_create(tp, &tcks);
824
825 if (tp->t_tfo_stats & TFO_S_SYN_DATA_SENT)
826 flag = (TCPCACHE_F_TFO_DATA | TCPCACHE_F_TFO_REQ);
827 if (tp->t_tfo_stats & TFO_S_COOKIE_REQ)
828 flag = TCPCACHE_F_TFO_REQ;
829
830 tcp_heuristic_inc_counters(&tcks, flag);
831 }
832
833 void tcp_heuristic_tfo_rst(struct tcpcb *tp)
834 {
835 struct tcp_cache_key_src tcks;
836 uint32_t flag = 0;
837
838 tcp_cache_key_src_create(tp, &tcks);
839
840 if (tp->t_tfo_stats & TFO_S_SYN_DATA_SENT)
841 flag = (TCPCACHE_F_TFO_DATA_RST | TCPCACHE_F_TFO_REQ_RST);
842 if (tp->t_tfo_stats & TFO_S_COOKIE_REQ)
843 flag = TCPCACHE_F_TFO_REQ_RST;
844
845 tcp_heuristic_inc_counters(&tcks, flag);
846 }
847
848 void tcp_heuristic_mptcp_loss(struct tcpcb *tp)
849 {
850 struct tcp_cache_key_src tcks;
851
852 tcp_cache_key_src_create(tp, &tcks);
853
854 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_MPTCP);
855 }
856
857 void tcp_heuristic_ecn_loss(struct tcpcb *tp)
858 {
859 struct tcp_cache_key_src tcks;
860
861 tcp_cache_key_src_create(tp, &tcks);
862
863 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_ECN);
864 }
865
866 void tcp_heuristic_ecn_droprst(struct tcpcb *tp)
867 {
868 struct tcp_cache_key_src tcks;
869
870 tcp_cache_key_src_create(tp, &tcks);
871
872 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_ECN_DROPRST);
873 }
874
875 void tcp_heuristic_ecn_droprxmt(struct tcpcb *tp)
876 {
877 struct tcp_cache_key_src tcks;
878
879 tcp_cache_key_src_create(tp, &tcks);
880
881 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_ECN_DROPRXMT);
882 }
883
884 void tcp_heuristic_ecn_synrst(struct tcpcb *tp)
885 {
886 struct tcp_cache_key_src tcks;
887
888 tcp_cache_key_src_create(tp, &tcks);
889
890 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_ECN_SYNRST);
891 }
892
893 void tcp_heuristic_tfo_middlebox(struct tcpcb *tp)
894 {
895 struct tcp_cache_key_src tcks;
896
897 tp->t_tfo_flags |= TFO_F_HEURISTIC_DONE;
898
899 tcp_cache_key_src_create(tp, &tcks);
900 tcp_heuristic_tfo_middlebox_common(&tcks);
901 }
902
903 static void tcp_heuristic_ecn_aggressive_common(struct tcp_cache_key_src *tcks)
904 {
905 struct tcp_heuristics_head *head;
906 struct tcp_heuristic *tpheur;
907
908 tpheur = tcp_getheuristic_with_lock(tcks, 1, &head);
909 if (tpheur == NULL)
910 return;
911
912 /* Must be done before, otherwise we will start off with expo-backoff */
913 tpheur->th_ecn_backoff = tcp_now +
914 (tcp_min_to_hz(tcp_ecn_timeout) << (tpheur->th_ecn_aggressive));
915
916 /*
917 * Ugly way to prevent integer overflow... limit to prevent in
918 * overflow during exp. backoff.
919 */
920 if (tpheur->th_ecn_aggressive < TCP_CACHE_OVERFLOW_PROTECT)
921 tpheur->th_ecn_aggressive++;
922
923 tcp_heuristic_unlock(head);
924 }
925
926 void tcp_heuristic_ecn_aggressive(struct tcpcb *tp)
927 {
928 struct tcp_cache_key_src tcks;
929
930 tcp_cache_key_src_create(tp, &tcks);
931 tcp_heuristic_ecn_aggressive_common(&tcks);
932 }
933
934 static boolean_t tcp_heuristic_do_tfo_common(struct tcp_cache_key_src *tcks)
935 {
936 struct tcp_heuristics_head *head;
937 struct tcp_heuristic *tpheur;
938
939 if (disable_tcp_heuristics)
940 return (TRUE);
941
942 /* Get the tcp-heuristic. */
943 tpheur = tcp_getheuristic_with_lock(tcks, 0, &head);
944 if (tpheur == NULL)
945 return (TRUE);
946
947 if (tpheur->th_tfo_in_backoff == 0)
948 goto tfo_ok;
949
950 if (TSTMP_GT(tcp_now, tpheur->th_tfo_backoff_until)) {
951 tpheur->th_tfo_in_backoff = 0;
952 tpheur->th_tfo_enabled_time = tcp_now;
953
954 goto tfo_ok;
955 }
956
957 tcp_heuristic_unlock(head);
958 return (FALSE);
959
960 tfo_ok:
961 tcp_heuristic_unlock(head);
962 return (TRUE);
963 }
964
965 boolean_t tcp_heuristic_do_tfo(struct tcpcb *tp)
966 {
967 struct tcp_cache_key_src tcks;
968
969 tcp_cache_key_src_create(tp, &tcks);
970 if (tcp_heuristic_do_tfo_common(&tcks))
971 return (TRUE);
972
973 return (FALSE);
974 }
975
976 boolean_t tcp_heuristic_do_mptcp(struct tcpcb *tp)
977 {
978 struct tcp_cache_key_src tcks;
979 struct tcp_heuristics_head *head = NULL;
980 struct tcp_heuristic *tpheur;
981
982 if (disable_tcp_heuristics)
983 return (TRUE);
984
985 tcp_cache_key_src_create(tp, &tcks);
986
987 /* Get the tcp-heuristic. */
988 tpheur = tcp_getheuristic_with_lock(&tcks, 0, &head);
989 if (tpheur == NULL)
990 return (TRUE);
991
992 if (TSTMP_GT(tpheur->th_mptcp_backoff, tcp_now))
993 goto fallback;
994
995 tcp_heuristic_unlock(head);
996
997 return (TRUE);
998
999 fallback:
1000 if (head)
1001 tcp_heuristic_unlock(head);
1002
1003 if (tptomptp(tp)->mpt_mpte->mpte_flags & MPTE_FIRSTPARTY)
1004 tcpstat.tcps_mptcp_fp_heuristic_fallback++;
1005 else
1006 tcpstat.tcps_mptcp_heuristic_fallback++;
1007
1008 return (FALSE);
1009 }
1010
1011 static boolean_t tcp_heuristic_do_ecn_common(struct tcp_cache_key_src *tcks)
1012 {
1013 struct tcp_heuristics_head *head;
1014 struct tcp_heuristic *tpheur;
1015 boolean_t ret = TRUE;
1016
1017 if (disable_tcp_heuristics)
1018 return (TRUE);
1019
1020 /* Get the tcp-heuristic. */
1021 tpheur = tcp_getheuristic_with_lock(tcks, 0, &head);
1022 if (tpheur == NULL)
1023 return ret;
1024
1025 if (TSTMP_GT(tpheur->th_ecn_backoff, tcp_now)) {
1026 ret = FALSE;
1027 } else {
1028 /* Reset the following counters to start re-evaluating */
1029 if (tpheur->th_ecn_droprst >= ECN_RETRY_LIMIT)
1030 tpheur->th_ecn_droprst = 0;
1031 if (tpheur->th_ecn_droprxmt >= ECN_RETRY_LIMIT)
1032 tpheur->th_ecn_droprxmt = 0;
1033 if (tpheur->th_ecn_synrst >= ECN_RETRY_LIMIT)
1034 tpheur->th_ecn_synrst = 0;
1035 }
1036
1037 tcp_heuristic_unlock(head);
1038
1039 return (ret);
1040 }
1041
1042 boolean_t tcp_heuristic_do_ecn(struct tcpcb *tp)
1043 {
1044 struct tcp_cache_key_src tcks;
1045
1046 tcp_cache_key_src_create(tp, &tcks);
1047 return tcp_heuristic_do_ecn_common(&tcks);
1048 }
1049
1050 boolean_t tcp_heuristic_do_ecn_with_address(struct ifnet *ifp,
1051 union sockaddr_in_4_6 *local_address)
1052 {
1053 struct tcp_cache_key_src tcks;
1054
1055 memset(&tcks, 0, sizeof(tcks));
1056 tcks.ifp = ifp;
1057
1058 calculate_tcp_clock();
1059
1060 if (local_address->sa.sa_family == AF_INET6) {
1061 memcpy(&tcks.laddr.addr6, &local_address->sin6.sin6_addr, sizeof(struct in6_addr));
1062 tcks.af = AF_INET6;
1063 } else if (local_address->sa.sa_family == AF_INET) {
1064 memcpy(&tcks.laddr.addr, &local_address->sin.sin_addr, sizeof(struct in_addr));
1065 tcks.af = AF_INET;
1066 }
1067
1068 return tcp_heuristic_do_ecn_common(&tcks);
1069 }
1070
1071 void tcp_heuristics_ecn_update(struct necp_tcp_ecn_cache *necp_buffer,
1072 struct ifnet *ifp, union sockaddr_in_4_6 *local_address)
1073 {
1074 struct tcp_cache_key_src tcks;
1075
1076 memset(&tcks, 0, sizeof(tcks));
1077 tcks.ifp = ifp;
1078
1079 calculate_tcp_clock();
1080
1081 if (local_address->sa.sa_family == AF_INET6) {
1082 memcpy(&tcks.laddr.addr6, &local_address->sin6.sin6_addr, sizeof(struct in6_addr));
1083 tcks.af = AF_INET6;
1084 } else if (local_address->sa.sa_family == AF_INET) {
1085 memcpy(&tcks.laddr.addr, &local_address->sin.sin_addr, sizeof(struct in_addr));
1086 tcks.af = AF_INET;
1087 }
1088
1089 if (necp_buffer->necp_tcp_ecn_heuristics_success) {
1090 tcp_heuristic_reset_counters(&tcks, TCPCACHE_F_ECN);
1091 } else if (necp_buffer->necp_tcp_ecn_heuristics_loss) {
1092 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_ECN);
1093 } else if (necp_buffer->necp_tcp_ecn_heuristics_drop_rst) {
1094 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_ECN_DROPRST);
1095 } else if (necp_buffer->necp_tcp_ecn_heuristics_drop_rxmt) {
1096 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_ECN_DROPRXMT);
1097 } else if (necp_buffer->necp_tcp_ecn_heuristics_syn_rst) {
1098 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_ECN_SYNRST);
1099 } else if (necp_buffer->necp_tcp_ecn_heuristics_aggressive) {
1100 tcp_heuristic_ecn_aggressive_common(&tcks);
1101 }
1102
1103 return;
1104 }
1105
1106 boolean_t tcp_heuristic_do_tfo_with_address(struct ifnet *ifp,
1107 union sockaddr_in_4_6 *local_address, union sockaddr_in_4_6 *remote_address,
1108 u_int8_t *cookie, u_int8_t *cookie_len)
1109 {
1110 struct tcp_cache_key_src tcks;
1111
1112 memset(&tcks, 0, sizeof(tcks));
1113 tcks.ifp = ifp;
1114
1115 calculate_tcp_clock();
1116
1117 if (remote_address->sa.sa_family == AF_INET6) {
1118 memcpy(&tcks.laddr.addr6, &local_address->sin6.sin6_addr, sizeof(struct in6_addr));
1119 memcpy(&tcks.faddr.addr6, &remote_address->sin6.sin6_addr, sizeof(struct in6_addr));
1120 tcks.af = AF_INET6;
1121 } else if (remote_address->sa.sa_family == AF_INET) {
1122 memcpy(&tcks.laddr.addr, &local_address->sin.sin_addr, sizeof(struct in_addr));
1123 memcpy(&tcks.faddr.addr, &remote_address->sin.sin_addr, sizeof(struct in_addr));
1124 tcks.af = AF_INET;
1125 }
1126
1127 if (tcp_heuristic_do_tfo_common(&tcks)) {
1128 if (!tcp_cache_get_cookie_common(&tcks, cookie, cookie_len)) {
1129 *cookie_len = 0;
1130 }
1131 return TRUE;
1132 }
1133
1134 return FALSE;
1135 }
1136
1137 void tcp_heuristics_tfo_update(struct necp_tcp_tfo_cache *necp_buffer,
1138 struct ifnet *ifp, union sockaddr_in_4_6 *local_address,
1139 union sockaddr_in_4_6 *remote_address)
1140 {
1141 struct tcp_cache_key_src tcks;
1142
1143 memset(&tcks, 0, sizeof(tcks));
1144 tcks.ifp = ifp;
1145
1146 calculate_tcp_clock();
1147
1148 if (remote_address->sa.sa_family == AF_INET6) {
1149 memcpy(&tcks.laddr.addr6, &local_address->sin6.sin6_addr, sizeof(struct in6_addr));
1150 memcpy(&tcks.faddr.addr6, &remote_address->sin6.sin6_addr, sizeof(struct in6_addr));
1151 tcks.af = AF_INET6;
1152 } else if (remote_address->sa.sa_family == AF_INET) {
1153 memcpy(&tcks.laddr.addr, &local_address->sin.sin_addr, sizeof(struct in_addr));
1154 memcpy(&tcks.faddr.addr, &remote_address->sin.sin_addr, sizeof(struct in_addr));
1155 tcks.af = AF_INET;
1156 }
1157
1158 if (necp_buffer->necp_tcp_tfo_heuristics_success)
1159 tcp_heuristic_reset_counters(&tcks, TCPCACHE_F_TFO_REQ | TCPCACHE_F_TFO_DATA |
1160 TCPCACHE_F_TFO_REQ_RST | TCPCACHE_F_TFO_DATA_RST);
1161
1162 if (necp_buffer->necp_tcp_tfo_heuristics_success_req)
1163 tcp_heuristic_reset_counters(&tcks, TCPCACHE_F_TFO_REQ | TCPCACHE_F_TFO_REQ_RST);
1164
1165 if (necp_buffer->necp_tcp_tfo_heuristics_loss)
1166 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_TFO_REQ | TCPCACHE_F_TFO_DATA);
1167
1168 if (necp_buffer->necp_tcp_tfo_heuristics_loss_req)
1169 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_TFO_REQ);
1170
1171 if (necp_buffer->necp_tcp_tfo_heuristics_rst_data)
1172 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_TFO_REQ_RST | TCPCACHE_F_TFO_DATA_RST);
1173
1174 if (necp_buffer->necp_tcp_tfo_heuristics_rst_req)
1175 tcp_heuristic_inc_counters(&tcks, TCPCACHE_F_TFO_REQ_RST);
1176
1177 if (necp_buffer->necp_tcp_tfo_heuristics_middlebox)
1178 tcp_heuristic_tfo_middlebox_common(&tcks);
1179
1180 if (necp_buffer->necp_tcp_tfo_cookie_len != 0) {
1181 tcp_cache_set_cookie_common(&tcks,
1182 necp_buffer->necp_tcp_tfo_cookie, necp_buffer->necp_tcp_tfo_cookie_len);
1183 }
1184
1185 return;
1186 }
1187
1188 static void sysctl_cleartfocache(void)
1189 {
1190 int i;
1191
1192 for (i = 0; i < tcp_cache_size; i++) {
1193 struct tcp_cache_head *head = &tcp_cache[i];
1194 struct tcp_cache *tpcache, *tmp;
1195 struct tcp_heuristics_head *hhead = &tcp_heuristics[i];
1196 struct tcp_heuristic *tpheur, *htmp;
1197
1198 lck_mtx_lock(&head->tch_mtx);
1199 SLIST_FOREACH_SAFE(tpcache, &head->tcp_caches, list, tmp) {
1200 SLIST_REMOVE(&head->tcp_caches, tpcache, tcp_cache, list);
1201 _FREE(tpcache, M_TEMP);
1202 }
1203 lck_mtx_unlock(&head->tch_mtx);
1204
1205 lck_mtx_lock(&hhead->thh_mtx);
1206 SLIST_FOREACH_SAFE(tpheur, &hhead->tcp_heuristics, list, htmp) {
1207 SLIST_REMOVE(&hhead->tcp_heuristics, tpheur, tcp_heuristic, list);
1208 _FREE(tpheur, M_TEMP);
1209 }
1210 lck_mtx_unlock(&hhead->thh_mtx);
1211 }
1212 }
1213
1214 /* This sysctl is useful for testing purposes only */
1215 static int tcpcleartfo = 0;
1216
1217 static int sysctl_cleartfo SYSCTL_HANDLER_ARGS
1218 {
1219 #pragma unused(arg1, arg2)
1220 int error = 0, val, oldval = tcpcleartfo;
1221
1222 val = oldval;
1223 error = sysctl_handle_int(oidp, &val, 0, req);
1224 if (error || !req->newptr)
1225 return (error);
1226
1227 /*
1228 * The actual value does not matter. If the value is set, it triggers
1229 * the clearing of the TFO cache. If a future implementation does not
1230 * use the route entry to hold the TFO cache, replace the route sysctl.
1231 */
1232
1233 if (val != oldval)
1234 sysctl_cleartfocache();
1235
1236 tcpcleartfo = val;
1237
1238 return (error);
1239 }
1240
1241 SYSCTL_PROC(_net_inet_tcp, OID_AUTO, clear_tfocache, CTLTYPE_INT | CTLFLAG_RW |
1242 CTLFLAG_LOCKED, &tcpcleartfo, 0, &sysctl_cleartfo, "I",
1243 "Toggle to clear the TFO destination based heuristic cache");
1244
1245 void tcp_cache_init(void)
1246 {
1247 uint64_t sane_size_meg = sane_size / 1024 / 1024;
1248 int i;
1249
1250 /*
1251 * On machines with <100MB of memory this will result in a (full) cache-size
1252 * of 32 entries, thus 32 * 5 * 64bytes = 10KB. (about 0.01 %)
1253 * On machines with > 4GB of memory, we have a cache-size of 1024 entries,
1254 * thus about 327KB.
1255 *
1256 * Side-note: we convert to u_int32_t. If sane_size is more than
1257 * 16000 TB, we loose precision. But, who cares? :)
1258 */
1259 tcp_cache_size = tcp_cache_roundup2((u_int32_t)(sane_size_meg >> 2));
1260 if (tcp_cache_size < 32)
1261 tcp_cache_size = 32;
1262 else if (tcp_cache_size > 1024)
1263 tcp_cache_size = 1024;
1264
1265 tcp_cache = _MALLOC(sizeof(struct tcp_cache_head) * tcp_cache_size,
1266 M_TEMP, M_ZERO);
1267 if (tcp_cache == NULL)
1268 panic("Allocating tcp_cache failed at boot-time!");
1269
1270 tcp_cache_mtx_grp_attr = lck_grp_attr_alloc_init();
1271 tcp_cache_mtx_grp = lck_grp_alloc_init("tcpcache", tcp_cache_mtx_grp_attr);
1272 tcp_cache_mtx_attr = lck_attr_alloc_init();
1273
1274 tcp_heuristics = _MALLOC(sizeof(struct tcp_heuristics_head) * tcp_cache_size,
1275 M_TEMP, M_ZERO);
1276 if (tcp_heuristics == NULL)
1277 panic("Allocating tcp_heuristic failed at boot-time!");
1278
1279 tcp_heuristic_mtx_grp_attr = lck_grp_attr_alloc_init();
1280 tcp_heuristic_mtx_grp = lck_grp_alloc_init("tcpheuristic", tcp_heuristic_mtx_grp_attr);
1281 tcp_heuristic_mtx_attr = lck_attr_alloc_init();
1282
1283 for (i = 0; i < tcp_cache_size; i++) {
1284 lck_mtx_init(&tcp_cache[i].tch_mtx, tcp_cache_mtx_grp,
1285 tcp_cache_mtx_attr);
1286 SLIST_INIT(&tcp_cache[i].tcp_caches);
1287
1288 lck_mtx_init(&tcp_heuristics[i].thh_mtx, tcp_heuristic_mtx_grp,
1289 tcp_heuristic_mtx_attr);
1290 SLIST_INIT(&tcp_heuristics[i].tcp_heuristics);
1291 }
1292
1293 tcp_cache_hash_seed = RandomULong();
1294 }