2 * Copyright (c) 2015-2017 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 /* TCP-cache to store and retrieve TCP-related information */
31 #include <net/flowhash.h>
32 #include <net/route.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>
45 struct in6_addr addr6
;
48 struct tcp_heuristic_key
{
50 uint8_t thk_net_signature
[IFNET_SIGNATURELEN
];
53 sa_family_t thk_family
;
56 struct tcp_heuristic
{
57 SLIST_ENTRY(tcp_heuristic
) list
;
59 uint32_t th_last_access
;
61 struct tcp_heuristic_key th_key
;
63 char th_val_start
[0]; /* Marker for memsetting to 0 */
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_mptcp_success
; /* The number of times MPTCP-negotiation has been successful */
71 uint8_t th_ecn_loss
; /* The number of times a SYN+ecn has been lost */
72 uint8_t th_ecn_aggressive
; /* The number of times we did an aggressive fallback */
73 uint8_t th_ecn_droprst
; /* The number of times ECN connections received a RST after first data pkt */
74 uint8_t th_ecn_droprxmt
; /* The number of times ECN connection is dropped after multiple retransmits */
75 uint8_t th_ecn_synrst
; /* number of times RST was received in response to an ECN enabled SYN */
76 uint32_t th_tfo_enabled_time
; /* The moment when we reenabled TFO after backing off */
77 uint32_t th_tfo_backoff_until
; /* Time until when we should not try out TFO */
78 uint32_t th_tfo_backoff
; /* Current backoff timer */
79 uint32_t th_mptcp_backoff
; /* Time until when we should not try out MPTCP */
80 uint32_t th_ecn_backoff
; /* Time until when we should not try out ECN */
82 uint8_t th_tfo_in_backoff
:1, /* Are we avoiding TFO due to the backoff timer? */
83 th_mptcp_in_backoff
:1, /* Are we avoiding MPTCP due to the backoff timer? */
84 th_mptcp_heuristic_disabled
:1; /* Are heuristics disabled? */
86 char th_val_end
[0]; /* Marker for memsetting to 0 */
89 struct tcp_heuristics_head
{
90 SLIST_HEAD(tcp_heur_bucket
, tcp_heuristic
) tcp_heuristics
;
92 /* Per-hashbucket lock to avoid lock-contention */
96 struct tcp_cache_key
{
97 sa_family_t tck_family
;
99 struct tcp_heuristic_key tck_src
;
104 SLIST_ENTRY(tcp_cache
) list
;
106 uint32_t tc_last_access
;
108 struct tcp_cache_key tc_key
;
110 uint8_t tc_tfo_cookie
[TFO_COOKIE_LEN_MAX
];
111 uint8_t tc_tfo_cookie_len
;
114 struct tcp_cache_head
{
115 SLIST_HEAD(tcp_cache_bucket
, tcp_cache
) tcp_caches
;
117 /* Per-hashbucket lock to avoid lock-contention */
121 struct tcp_cache_key_src
{
128 static uint32_t tcp_cache_hash_seed
;
130 size_t tcp_cache_size
;
133 * The maximum depth of the hash-bucket. This way we limit the tcp_cache to
134 * TCP_CACHE_BUCKET_SIZE * tcp_cache_size and have "natural" garbage collection
136 #define TCP_CACHE_BUCKET_SIZE 5
138 static struct tcp_cache_head
*tcp_cache
;
140 decl_lck_mtx_data(, tcp_cache_mtx
);
142 static lck_attr_t
*tcp_cache_mtx_attr
;
143 static lck_grp_t
*tcp_cache_mtx_grp
;
144 static lck_grp_attr_t
*tcp_cache_mtx_grp_attr
;
146 static struct tcp_heuristics_head
*tcp_heuristics
;
148 decl_lck_mtx_data(, tcp_heuristics_mtx
);
150 static lck_attr_t
*tcp_heuristic_mtx_attr
;
151 static lck_grp_t
*tcp_heuristic_mtx_grp
;
152 static lck_grp_attr_t
*tcp_heuristic_mtx_grp_attr
;
154 static uint32_t tcp_backoff_maximum
= 65536;
156 SYSCTL_UINT(_net_inet_tcp
, OID_AUTO
, backoff_maximum
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
157 &tcp_backoff_maximum
, 0, "Maximum time for which we won't try TFO");
159 static uint32_t tcp_ecn_timeout
= 60;
161 SYSCTL_UINT(_net_inet_tcp
, OID_AUTO
, ecn_timeout
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
162 &tcp_ecn_timeout
, 60, "Initial minutes to wait before re-trying ECN");
164 static int disable_tcp_heuristics
= 0;
165 SYSCTL_INT(_net_inet_tcp
, OID_AUTO
, disable_tcp_heuristics
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
166 &disable_tcp_heuristics
, 0, "Set to 1, to disable all TCP heuristics (TFO, ECN, MPTCP)");
169 tcp_min_to_hz(uint32_t minutes
)
171 if (minutes
> 65536) {
172 return (uint32_t)65536 * 60 * TCP_RETRANSHZ
;
175 return minutes
* 60 * TCP_RETRANSHZ
;
179 * This number is coupled with tcp_ecn_timeout, because we want to prevent
180 * integer overflow. Need to find an unexpensive way to prevent integer overflow
181 * while still allowing a dynamic sysctl.
183 #define TCP_CACHE_OVERFLOW_PROTECT 9
185 /* Number of SYN-losses we accept */
186 #define TFO_MAX_COOKIE_LOSS 2
187 #define ECN_MAX_SYN_LOSS 2
188 #define MPTCP_MAX_SYN_LOSS 2
189 #define MPTCP_SUCCESS_TRIGGER 10
190 #define ECN_MAX_DROPRST 1
191 #define ECN_MAX_DROPRXMT 4
192 #define ECN_MAX_SYNRST 4
194 /* Flags for setting/unsetting loss-heuristics, limited to 4 bytes */
195 #define TCPCACHE_F_TFO_REQ 0x01
196 #define TCPCACHE_F_TFO_DATA 0x02
197 #define TCPCACHE_F_ECN 0x04
198 #define TCPCACHE_F_MPTCP 0x08
199 #define TCPCACHE_F_ECN_DROPRST 0x10
200 #define TCPCACHE_F_ECN_DROPRXMT 0x20
201 #define TCPCACHE_F_TFO_REQ_RST 0x40
202 #define TCPCACHE_F_TFO_DATA_RST 0x80
203 #define TCPCACHE_F_ECN_SYNRST 0x100
205 /* Always retry ECN after backing off to this level for some heuristics */
206 #define ECN_RETRY_LIMIT 9
208 #define TCP_CACHE_INC_IFNET_STAT(_ifp_, _af_, _stat_) { \
209 if ((_ifp_) != NULL) { \
210 if ((_af_) == AF_INET6) { \
211 (_ifp_)->if_ipv6_stat->_stat_++;\
213 (_ifp_)->if_ipv4_stat->_stat_++;\
219 * Round up to next higher power-of 2. See "Bit Twiddling Hacks".
221 * Might be worth moving this to a library so that others
222 * (e.g., scale_to_powerof2()) can use this as well instead of a while-loop.
225 tcp_cache_roundup2(uint32_t a
)
239 tcp_cache_hash_src(struct tcp_cache_key_src
*tcks
, struct tcp_heuristic_key
*key
)
241 struct ifnet
*ifp
= tcks
->ifp
;
242 uint8_t len
= sizeof(key
->thk_net_signature
);
245 if (tcks
->af
== AF_INET6
) {
248 key
->thk_family
= AF_INET6
;
249 ret
= ifnet_get_netsignature(ifp
, AF_INET6
, &len
, &flags
,
250 key
->thk_net_signature
);
253 * ifnet_get_netsignature only returns EINVAL if ifn is NULL
254 * (we made sure that in the other cases it does not). So,
255 * in this case we should take the connection's address.
257 if (ret
== ENOENT
|| ret
== EINVAL
) {
258 memcpy(&key
->thk_ip
.addr6
, &tcks
->laddr
.addr6
, sizeof(struct in6_addr
));
263 key
->thk_family
= AF_INET
;
264 ret
= ifnet_get_netsignature(ifp
, AF_INET
, &len
, &flags
,
265 key
->thk_net_signature
);
268 * ifnet_get_netsignature only returns EINVAL if ifn is NULL
269 * (we made sure that in the other cases it does not). So,
270 * in this case we should take the connection's address.
272 if (ret
== ENOENT
|| ret
== EINVAL
) {
273 memcpy(&key
->thk_ip
.addr
, &tcks
->laddr
.addr
, sizeof(struct in_addr
));
279 tcp_cache_hash(struct tcp_cache_key_src
*tcks
, struct tcp_cache_key
*key
)
283 bzero(key
, sizeof(struct tcp_cache_key
));
285 tcp_cache_hash_src(tcks
, &key
->tck_src
);
287 if (tcks
->af
== AF_INET6
) {
288 key
->tck_family
= AF_INET6
;
289 memcpy(&key
->tck_dst
.addr6
, &tcks
->faddr
.addr6
,
290 sizeof(struct in6_addr
));
292 key
->tck_family
= AF_INET
;
293 memcpy(&key
->tck_dst
.addr
, &tcks
->faddr
.addr
,
294 sizeof(struct in_addr
));
297 hash
= net_flowhash(key
, sizeof(struct tcp_cache_key
),
298 tcp_cache_hash_seed
);
300 return (uint16_t)(hash
& (tcp_cache_size
- 1));
304 tcp_cache_unlock(struct tcp_cache_head
*head
)
306 lck_mtx_unlock(&head
->tch_mtx
);
310 * Make sure that everything that happens after tcp_getcache_with_lock()
311 * is short enough to justify that you hold the per-bucket lock!!!
313 * Otherwise, better build another lookup-function that does not hold the
314 * lock and you copy out the bits and bytes.
316 * That's why we provide the head as a "return"-pointer so that the caller
317 * can give it back to use for tcp_cache_unlock().
319 static struct tcp_cache
*
320 tcp_getcache_with_lock(struct tcp_cache_key_src
*tcks
,
321 int create
, struct tcp_cache_head
**headarg
)
323 struct tcp_cache
*tpcache
= NULL
;
324 struct tcp_cache_head
*head
;
325 struct tcp_cache_key key
;
329 hash
= tcp_cache_hash(tcks
, &key
);
330 head
= &tcp_cache
[hash
];
332 lck_mtx_lock(&head
->tch_mtx
);
334 /*** First step: Look for the tcp_cache in our bucket ***/
335 SLIST_FOREACH(tpcache
, &head
->tcp_caches
, list
) {
336 if (memcmp(&tpcache
->tc_key
, &key
, sizeof(key
)) == 0) {
343 /*** Second step: If it's not there, create/recycle it ***/
344 if ((tpcache
== NULL
) && create
) {
345 if (i
>= TCP_CACHE_BUCKET_SIZE
) {
346 struct tcp_cache
*oldest_cache
= NULL
;
347 uint32_t max_age
= 0;
349 /* Look for the oldest tcp_cache in the bucket */
350 SLIST_FOREACH(tpcache
, &head
->tcp_caches
, list
) {
351 uint32_t age
= tcp_now
- tpcache
->tc_last_access
;
354 oldest_cache
= tpcache
;
357 VERIFY(oldest_cache
!= NULL
);
359 tpcache
= oldest_cache
;
361 /* We recycle, thus let's indicate that there is no cookie */
362 tpcache
->tc_tfo_cookie_len
= 0;
364 /* Create a new cache and add it to the list */
365 tpcache
= _MALLOC(sizeof(struct tcp_cache
), M_TEMP
,
367 if (tpcache
== NULL
) {
368 os_log_error(OS_LOG_DEFAULT
, "%s could not allocate cache", __func__
);
372 SLIST_INSERT_HEAD(&head
->tcp_caches
, tpcache
, list
);
375 memcpy(&tpcache
->tc_key
, &key
, sizeof(key
));
378 if (tpcache
== NULL
) {
382 /* Update timestamp for garbage collection purposes */
383 tpcache
->tc_last_access
= tcp_now
;
389 tcp_cache_unlock(head
);
394 tcp_cache_key_src_create(struct tcpcb
*tp
, struct tcp_cache_key_src
*tcks
)
396 struct inpcb
*inp
= tp
->t_inpcb
;
397 memset(tcks
, 0, sizeof(*tcks
));
399 tcks
->ifp
= inp
->inp_last_outifp
;
401 if (inp
->inp_vflag
& INP_IPV6
) {
402 memcpy(&tcks
->laddr
.addr6
, &inp
->in6p_laddr
, sizeof(struct in6_addr
));
403 memcpy(&tcks
->faddr
.addr6
, &inp
->in6p_faddr
, sizeof(struct in6_addr
));
406 memcpy(&tcks
->laddr
.addr
, &inp
->inp_laddr
, sizeof(struct in_addr
));
407 memcpy(&tcks
->faddr
.addr
, &inp
->inp_faddr
, sizeof(struct in_addr
));
415 tcp_cache_set_cookie_common(struct tcp_cache_key_src
*tcks
, u_char
*cookie
, uint8_t len
)
417 struct tcp_cache_head
*head
;
418 struct tcp_cache
*tpcache
;
420 /* Call lookup/create function */
421 tpcache
= tcp_getcache_with_lock(tcks
, 1, &head
);
422 if (tpcache
== NULL
) {
426 tpcache
->tc_tfo_cookie_len
= len
> TFO_COOKIE_LEN_MAX
?
427 TFO_COOKIE_LEN_MAX
: len
;
428 memcpy(tpcache
->tc_tfo_cookie
, cookie
, tpcache
->tc_tfo_cookie_len
);
430 tcp_cache_unlock(head
);
434 tcp_cache_set_cookie(struct tcpcb
*tp
, u_char
*cookie
, uint8_t len
)
436 struct tcp_cache_key_src tcks
;
438 tcp_cache_key_src_create(tp
, &tcks
);
439 tcp_cache_set_cookie_common(&tcks
, cookie
, len
);
443 tcp_cache_get_cookie_common(struct tcp_cache_key_src
*tcks
, u_char
*cookie
, uint8_t *len
)
445 struct tcp_cache_head
*head
;
446 struct tcp_cache
*tpcache
;
448 /* Call lookup/create function */
449 tpcache
= tcp_getcache_with_lock(tcks
, 1, &head
);
450 if (tpcache
== NULL
) {
454 if (tpcache
->tc_tfo_cookie_len
== 0) {
455 tcp_cache_unlock(head
);
460 * Not enough space - this should never happen as it has been checked
461 * in tcp_tfo_check. So, fail here!
463 VERIFY(tpcache
->tc_tfo_cookie_len
<= *len
);
465 memcpy(cookie
, tpcache
->tc_tfo_cookie
, tpcache
->tc_tfo_cookie_len
);
466 *len
= tpcache
->tc_tfo_cookie_len
;
468 tcp_cache_unlock(head
);
474 * Get the cookie related to 'tp', and copy it into 'cookie', provided that len
475 * is big enough (len designates the available memory.
476 * Upon return, 'len' is set to the cookie's length.
478 * Returns 0 if we should request a cookie.
479 * Returns 1 if the cookie has been found and written.
482 tcp_cache_get_cookie(struct tcpcb
*tp
, u_char
*cookie
, uint8_t *len
)
484 struct tcp_cache_key_src tcks
;
486 tcp_cache_key_src_create(tp
, &tcks
);
487 return tcp_cache_get_cookie_common(&tcks
, cookie
, len
);
491 tcp_cache_get_cookie_len_common(struct tcp_cache_key_src
*tcks
)
493 struct tcp_cache_head
*head
;
494 struct tcp_cache
*tpcache
;
495 unsigned int cookie_len
;
497 /* Call lookup/create function */
498 tpcache
= tcp_getcache_with_lock(tcks
, 1, &head
);
499 if (tpcache
== NULL
) {
503 cookie_len
= tpcache
->tc_tfo_cookie_len
;
505 tcp_cache_unlock(head
);
511 tcp_cache_get_cookie_len(struct tcpcb
*tp
)
513 struct tcp_cache_key_src tcks
;
515 tcp_cache_key_src_create(tp
, &tcks
);
516 return tcp_cache_get_cookie_len_common(&tcks
);
520 tcp_heuristics_hash(struct tcp_cache_key_src
*tcks
, struct tcp_heuristic_key
*key
)
524 bzero(key
, sizeof(struct tcp_heuristic_key
));
526 tcp_cache_hash_src(tcks
, key
);
528 hash
= net_flowhash(key
, sizeof(struct tcp_heuristic_key
),
529 tcp_cache_hash_seed
);
531 return (uint16_t)(hash
& (tcp_cache_size
- 1));
535 tcp_heuristic_unlock(struct tcp_heuristics_head
*head
)
537 lck_mtx_unlock(&head
->thh_mtx
);
541 * Make sure that everything that happens after tcp_getheuristic_with_lock()
542 * is short enough to justify that you hold the per-bucket lock!!!
544 * Otherwise, better build another lookup-function that does not hold the
545 * lock and you copy out the bits and bytes.
547 * That's why we provide the head as a "return"-pointer so that the caller
548 * can give it back to use for tcp_heur_unlock().
551 * ToDo - way too much code-duplication. We should create an interface to handle
552 * bucketized hashtables with recycling of the oldest element.
554 static struct tcp_heuristic
*
555 tcp_getheuristic_with_lock(struct tcp_cache_key_src
*tcks
,
556 int create
, struct tcp_heuristics_head
**headarg
)
558 struct tcp_heuristic
*tpheur
= NULL
;
559 struct tcp_heuristics_head
*head
;
560 struct tcp_heuristic_key key
;
564 hash
= tcp_heuristics_hash(tcks
, &key
);
565 head
= &tcp_heuristics
[hash
];
567 lck_mtx_lock(&head
->thh_mtx
);
569 /*** First step: Look for the tcp_heur in our bucket ***/
570 SLIST_FOREACH(tpheur
, &head
->tcp_heuristics
, list
) {
571 if (memcmp(&tpheur
->th_key
, &key
, sizeof(key
)) == 0) {
578 /*** Second step: If it's not there, create/recycle it ***/
579 if ((tpheur
== NULL
) && create
) {
580 if (i
>= TCP_CACHE_BUCKET_SIZE
) {
581 struct tcp_heuristic
*oldest_heur
= NULL
;
582 uint32_t max_age
= 0;
584 /* Look for the oldest tcp_heur in the bucket */
585 SLIST_FOREACH(tpheur
, &head
->tcp_heuristics
, list
) {
586 uint32_t age
= tcp_now
- tpheur
->th_last_access
;
589 oldest_heur
= tpheur
;
592 VERIFY(oldest_heur
!= NULL
);
594 tpheur
= oldest_heur
;
596 /* We recycle - set everything to 0 */
597 bzero(tpheur
->th_val_start
,
598 tpheur
->th_val_end
- tpheur
->th_val_start
);
600 /* Create a new heuristic and add it to the list */
601 tpheur
= _MALLOC(sizeof(struct tcp_heuristic
), M_TEMP
,
603 if (tpheur
== NULL
) {
604 os_log_error(OS_LOG_DEFAULT
, "%s could not allocate cache", __func__
);
608 SLIST_INSERT_HEAD(&head
->tcp_heuristics
, tpheur
, list
);
612 * Set to tcp_now, to make sure it won't be > than tcp_now in the
615 tpheur
->th_ecn_backoff
= tcp_now
;
616 tpheur
->th_tfo_backoff_until
= tcp_now
;
617 tpheur
->th_mptcp_backoff
= tcp_now
;
618 tpheur
->th_tfo_backoff
= tcp_min_to_hz(tcp_ecn_timeout
);
620 memcpy(&tpheur
->th_key
, &key
, sizeof(key
));
623 if (tpheur
== NULL
) {
627 /* Update timestamp for garbage collection purposes */
628 tpheur
->th_last_access
= tcp_now
;
634 tcp_heuristic_unlock(head
);
639 tcp_heuristic_reset_counters(struct tcp_cache_key_src
*tcks
, uint8_t flags
)
641 struct tcp_heuristics_head
*head
;
642 struct tcp_heuristic
*tpheur
;
645 * Always create heuristics here because MPTCP needs to write success
646 * into it. Thus, we always end up creating them.
648 tpheur
= tcp_getheuristic_with_lock(tcks
, 1, &head
);
649 if (tpheur
== NULL
) {
653 if (flags
& TCPCACHE_F_TFO_DATA
) {
654 if (tpheur
->th_tfo_data_loss
>= TFO_MAX_COOKIE_LOSS
) {
655 os_log(OS_LOG_DEFAULT
, "%s: Resetting TFO-data loss to 0 from %u on heur %lx\n",
656 __func__
, tpheur
->th_tfo_data_loss
, (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
658 tpheur
->th_tfo_data_loss
= 0;
661 if (flags
& TCPCACHE_F_TFO_REQ
) {
662 if (tpheur
->th_tfo_req_loss
>= TFO_MAX_COOKIE_LOSS
) {
663 os_log(OS_LOG_DEFAULT
, "%s: Resetting TFO-req loss to 0 from %u on heur %lx\n",
664 __func__
, tpheur
->th_tfo_req_loss
, (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
666 tpheur
->th_tfo_req_loss
= 0;
669 if (flags
& TCPCACHE_F_TFO_DATA_RST
) {
670 if (tpheur
->th_tfo_data_rst
>= TFO_MAX_COOKIE_LOSS
) {
671 os_log(OS_LOG_DEFAULT
, "%s: Resetting TFO-data RST to 0 from %u on heur %lx\n",
672 __func__
, tpheur
->th_tfo_data_rst
, (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
674 tpheur
->th_tfo_data_rst
= 0;
677 if (flags
& TCPCACHE_F_TFO_REQ_RST
) {
678 if (tpheur
->th_tfo_req_rst
>= TFO_MAX_COOKIE_LOSS
) {
679 os_log(OS_LOG_DEFAULT
, "%s: Resetting TFO-req RST to 0 from %u on heur %lx\n",
680 __func__
, tpheur
->th_tfo_req_rst
, (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
682 tpheur
->th_tfo_req_rst
= 0;
685 if (flags
& TCPCACHE_F_ECN
) {
686 if (tpheur
->th_ecn_loss
>= ECN_MAX_SYN_LOSS
|| tpheur
->th_ecn_synrst
>= ECN_MAX_SYNRST
) {
687 os_log(OS_LOG_DEFAULT
, "%s: Resetting ECN-loss to 0 from %u and synrst from %u on heur %lx\n",
688 __func__
, tpheur
->th_ecn_loss
, tpheur
->th_ecn_synrst
, (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
690 tpheur
->th_ecn_loss
= 0;
691 tpheur
->th_ecn_synrst
= 0;
694 if (flags
& TCPCACHE_F_MPTCP
) {
695 tpheur
->th_mptcp_loss
= 0;
696 if (tpheur
->th_mptcp_success
< MPTCP_SUCCESS_TRIGGER
) {
697 tpheur
->th_mptcp_success
++;
699 if (tpheur
->th_mptcp_success
== MPTCP_SUCCESS_TRIGGER
) {
700 os_log(mptcp_log_handle
, "%s disabling heuristics for 12 hours", __func__
);
701 tpheur
->th_mptcp_heuristic_disabled
= 1;
702 /* Disable heuristics for 12 hours */
703 tpheur
->th_mptcp_backoff
= tcp_now
+ tcp_min_to_hz(tcp_ecn_timeout
* 12);
708 tcp_heuristic_unlock(head
);
712 tcp_heuristic_tfo_success(struct tcpcb
*tp
)
714 struct tcp_cache_key_src tcks
;
717 tcp_cache_key_src_create(tp
, &tcks
);
719 if (tp
->t_tfo_stats
& TFO_S_SYN_DATA_SENT
) {
720 flag
= (TCPCACHE_F_TFO_DATA
| TCPCACHE_F_TFO_REQ
|
721 TCPCACHE_F_TFO_DATA_RST
| TCPCACHE_F_TFO_REQ_RST
);
723 if (tp
->t_tfo_stats
& TFO_S_COOKIE_REQ
) {
724 flag
= (TCPCACHE_F_TFO_REQ
| TCPCACHE_F_TFO_REQ_RST
);
727 tcp_heuristic_reset_counters(&tcks
, flag
);
731 tcp_heuristic_mptcp_success(struct tcpcb
*tp
)
733 struct tcp_cache_key_src tcks
;
735 tcp_cache_key_src_create(tp
, &tcks
);
736 tcp_heuristic_reset_counters(&tcks
, TCPCACHE_F_MPTCP
);
740 tcp_heuristic_ecn_success(struct tcpcb
*tp
)
742 struct tcp_cache_key_src tcks
;
744 tcp_cache_key_src_create(tp
, &tcks
);
745 tcp_heuristic_reset_counters(&tcks
, TCPCACHE_F_ECN
);
749 __tcp_heuristic_tfo_middlebox_common(struct tcp_heuristic
*tpheur
)
751 if (tpheur
->th_tfo_in_backoff
) {
755 tpheur
->th_tfo_in_backoff
= 1;
757 if (tpheur
->th_tfo_enabled_time
) {
758 uint32_t old_backoff
= tpheur
->th_tfo_backoff
;
760 tpheur
->th_tfo_backoff
-= (tcp_now
- tpheur
->th_tfo_enabled_time
);
761 if (tpheur
->th_tfo_backoff
> old_backoff
) {
762 tpheur
->th_tfo_backoff
= tcp_min_to_hz(tcp_ecn_timeout
);
766 tpheur
->th_tfo_backoff_until
= tcp_now
+ tpheur
->th_tfo_backoff
;
768 /* Then, increase the backoff time */
769 tpheur
->th_tfo_backoff
*= 2;
771 if (tpheur
->th_tfo_backoff
> tcp_min_to_hz(tcp_backoff_maximum
)) {
772 tpheur
->th_tfo_backoff
= tcp_min_to_hz(tcp_ecn_timeout
);
775 os_log(OS_LOG_DEFAULT
, "%s disable TFO until %u now %u on %lx\n", __func__
,
776 tpheur
->th_tfo_backoff_until
, tcp_now
, (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
780 tcp_heuristic_tfo_middlebox_common(struct tcp_cache_key_src
*tcks
)
782 struct tcp_heuristics_head
*head
;
783 struct tcp_heuristic
*tpheur
;
785 tpheur
= tcp_getheuristic_with_lock(tcks
, 1, &head
);
786 if (tpheur
== NULL
) {
790 __tcp_heuristic_tfo_middlebox_common(tpheur
);
792 tcp_heuristic_unlock(head
);
796 tcp_heuristic_inc_counters(struct tcp_cache_key_src
*tcks
,
799 struct tcp_heuristics_head
*head
;
800 struct tcp_heuristic
*tpheur
;
802 tpheur
= tcp_getheuristic_with_lock(tcks
, 1, &head
);
803 if (tpheur
== NULL
) {
807 /* Limit to prevent integer-overflow during exponential backoff */
808 if ((flags
& TCPCACHE_F_TFO_DATA
) && tpheur
->th_tfo_data_loss
< TCP_CACHE_OVERFLOW_PROTECT
) {
809 tpheur
->th_tfo_data_loss
++;
811 if (tpheur
->th_tfo_data_loss
>= TFO_MAX_COOKIE_LOSS
) {
812 __tcp_heuristic_tfo_middlebox_common(tpheur
);
816 if ((flags
& TCPCACHE_F_TFO_REQ
) && tpheur
->th_tfo_req_loss
< TCP_CACHE_OVERFLOW_PROTECT
) {
817 tpheur
->th_tfo_req_loss
++;
819 if (tpheur
->th_tfo_req_loss
>= TFO_MAX_COOKIE_LOSS
) {
820 __tcp_heuristic_tfo_middlebox_common(tpheur
);
824 if ((flags
& TCPCACHE_F_TFO_DATA_RST
) && tpheur
->th_tfo_data_rst
< TCP_CACHE_OVERFLOW_PROTECT
) {
825 tpheur
->th_tfo_data_rst
++;
827 if (tpheur
->th_tfo_data_rst
>= TFO_MAX_COOKIE_LOSS
) {
828 __tcp_heuristic_tfo_middlebox_common(tpheur
);
832 if ((flags
& TCPCACHE_F_TFO_REQ_RST
) && tpheur
->th_tfo_req_rst
< TCP_CACHE_OVERFLOW_PROTECT
) {
833 tpheur
->th_tfo_req_rst
++;
835 if (tpheur
->th_tfo_req_rst
>= TFO_MAX_COOKIE_LOSS
) {
836 __tcp_heuristic_tfo_middlebox_common(tpheur
);
840 if ((flags
& TCPCACHE_F_ECN
) &&
841 tpheur
->th_ecn_loss
< TCP_CACHE_OVERFLOW_PROTECT
&&
842 TSTMP_LEQ(tpheur
->th_ecn_backoff
, tcp_now
)) {
843 tpheur
->th_ecn_loss
++;
844 if (tpheur
->th_ecn_loss
>= ECN_MAX_SYN_LOSS
) {
845 tcpstat
.tcps_ecn_fallback_synloss
++;
846 TCP_CACHE_INC_IFNET_STAT(tcks
->ifp
, tcks
->af
, ecn_fallback_synloss
);
847 tpheur
->th_ecn_backoff
= tcp_now
+
848 (tcp_min_to_hz(tcp_ecn_timeout
) <<
849 (tpheur
->th_ecn_loss
- ECN_MAX_SYN_LOSS
));
851 os_log(OS_LOG_DEFAULT
, "%s disable ECN until %u now %u on %lx for SYN-loss\n",
852 __func__
, tpheur
->th_ecn_backoff
, tcp_now
,
853 (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
857 if ((flags
& TCPCACHE_F_MPTCP
) &&
858 tpheur
->th_mptcp_loss
< TCP_CACHE_OVERFLOW_PROTECT
&&
859 tpheur
->th_mptcp_heuristic_disabled
== 0) {
860 tpheur
->th_mptcp_loss
++;
861 if (tpheur
->th_mptcp_loss
>= MPTCP_MAX_SYN_LOSS
) {
863 * Yes, we take tcp_ecn_timeout, to avoid adding yet
864 * another sysctl that is just used for testing.
866 tpheur
->th_mptcp_backoff
= tcp_now
+
867 (tcp_min_to_hz(tcp_ecn_timeout
) <<
868 (tpheur
->th_mptcp_loss
- MPTCP_MAX_SYN_LOSS
));
869 tpheur
->th_mptcp_in_backoff
= 1;
871 os_log(OS_LOG_DEFAULT
, "%s disable MPTCP until %u now %u on %lx\n",
872 __func__
, tpheur
->th_mptcp_backoff
, tcp_now
,
873 (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
877 if ((flags
& TCPCACHE_F_ECN_DROPRST
) &&
878 tpheur
->th_ecn_droprst
< TCP_CACHE_OVERFLOW_PROTECT
&&
879 TSTMP_LEQ(tpheur
->th_ecn_backoff
, tcp_now
)) {
880 tpheur
->th_ecn_droprst
++;
881 if (tpheur
->th_ecn_droprst
>= ECN_MAX_DROPRST
) {
882 tcpstat
.tcps_ecn_fallback_droprst
++;
883 TCP_CACHE_INC_IFNET_STAT(tcks
->ifp
, tcks
->af
,
884 ecn_fallback_droprst
);
885 tpheur
->th_ecn_backoff
= tcp_now
+
886 (tcp_min_to_hz(tcp_ecn_timeout
) <<
887 (tpheur
->th_ecn_droprst
- ECN_MAX_DROPRST
));
889 os_log(OS_LOG_DEFAULT
, "%s disable ECN until %u now %u on %lx for drop-RST\n",
890 __func__
, tpheur
->th_ecn_backoff
, tcp_now
,
891 (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
895 if ((flags
& TCPCACHE_F_ECN_DROPRXMT
) &&
896 tpheur
->th_ecn_droprxmt
< TCP_CACHE_OVERFLOW_PROTECT
&&
897 TSTMP_LEQ(tpheur
->th_ecn_backoff
, tcp_now
)) {
898 tpheur
->th_ecn_droprxmt
++;
899 if (tpheur
->th_ecn_droprxmt
>= ECN_MAX_DROPRXMT
) {
900 tcpstat
.tcps_ecn_fallback_droprxmt
++;
901 TCP_CACHE_INC_IFNET_STAT(tcks
->ifp
, tcks
->af
,
902 ecn_fallback_droprxmt
);
903 tpheur
->th_ecn_backoff
= tcp_now
+
904 (tcp_min_to_hz(tcp_ecn_timeout
) <<
905 (tpheur
->th_ecn_droprxmt
- ECN_MAX_DROPRXMT
));
907 os_log(OS_LOG_DEFAULT
, "%s disable ECN until %u now %u on %lx for drop-Rxmit\n",
908 __func__
, tpheur
->th_ecn_backoff
, tcp_now
,
909 (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
912 if ((flags
& TCPCACHE_F_ECN_SYNRST
) &&
913 tpheur
->th_ecn_synrst
< TCP_CACHE_OVERFLOW_PROTECT
) {
914 tpheur
->th_ecn_synrst
++;
915 if (tpheur
->th_ecn_synrst
>= ECN_MAX_SYNRST
) {
916 tcpstat
.tcps_ecn_fallback_synrst
++;
917 TCP_CACHE_INC_IFNET_STAT(tcks
->ifp
, tcks
->af
,
918 ecn_fallback_synrst
);
919 tpheur
->th_ecn_backoff
= tcp_now
+
920 (tcp_min_to_hz(tcp_ecn_timeout
) <<
921 (tpheur
->th_ecn_synrst
- ECN_MAX_SYNRST
));
923 os_log(OS_LOG_DEFAULT
, "%s disable ECN until %u now %u on %lx for SYN-RST\n",
924 __func__
, tpheur
->th_ecn_backoff
, tcp_now
,
925 (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
928 tcp_heuristic_unlock(head
);
932 tcp_heuristic_tfo_loss(struct tcpcb
*tp
)
934 struct tcp_cache_key_src tcks
;
937 if (symptoms_is_wifi_lossy() &&
938 IFNET_IS_WIFI(tp
->t_inpcb
->inp_last_outifp
)) {
942 tcp_cache_key_src_create(tp
, &tcks
);
944 if (tp
->t_tfo_stats
& TFO_S_SYN_DATA_SENT
) {
945 flag
= (TCPCACHE_F_TFO_DATA
| TCPCACHE_F_TFO_REQ
);
947 if (tp
->t_tfo_stats
& TFO_S_COOKIE_REQ
) {
948 flag
= TCPCACHE_F_TFO_REQ
;
951 tcp_heuristic_inc_counters(&tcks
, flag
);
955 tcp_heuristic_tfo_rst(struct tcpcb
*tp
)
957 struct tcp_cache_key_src tcks
;
960 tcp_cache_key_src_create(tp
, &tcks
);
962 if (tp
->t_tfo_stats
& TFO_S_SYN_DATA_SENT
) {
963 flag
= (TCPCACHE_F_TFO_DATA_RST
| TCPCACHE_F_TFO_REQ_RST
);
965 if (tp
->t_tfo_stats
& TFO_S_COOKIE_REQ
) {
966 flag
= TCPCACHE_F_TFO_REQ_RST
;
969 tcp_heuristic_inc_counters(&tcks
, flag
);
973 tcp_heuristic_mptcp_loss(struct tcpcb
*tp
)
975 struct tcp_cache_key_src tcks
;
977 if (symptoms_is_wifi_lossy() &&
978 IFNET_IS_WIFI(tp
->t_inpcb
->inp_last_outifp
)) {
982 tcp_cache_key_src_create(tp
, &tcks
);
984 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_MPTCP
);
988 tcp_heuristic_ecn_loss(struct tcpcb
*tp
)
990 struct tcp_cache_key_src tcks
;
992 if (symptoms_is_wifi_lossy() &&
993 IFNET_IS_WIFI(tp
->t_inpcb
->inp_last_outifp
)) {
997 tcp_cache_key_src_create(tp
, &tcks
);
999 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_ECN
);
1003 tcp_heuristic_ecn_droprst(struct tcpcb
*tp
)
1005 struct tcp_cache_key_src tcks
;
1007 tcp_cache_key_src_create(tp
, &tcks
);
1009 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_ECN_DROPRST
);
1013 tcp_heuristic_ecn_droprxmt(struct tcpcb
*tp
)
1015 struct tcp_cache_key_src tcks
;
1017 tcp_cache_key_src_create(tp
, &tcks
);
1019 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_ECN_DROPRXMT
);
1023 tcp_heuristic_ecn_synrst(struct tcpcb
*tp
)
1025 struct tcp_cache_key_src tcks
;
1027 tcp_cache_key_src_create(tp
, &tcks
);
1029 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_ECN_SYNRST
);
1033 tcp_heuristic_tfo_middlebox(struct tcpcb
*tp
)
1035 struct tcp_cache_key_src tcks
;
1037 tp
->t_tfo_flags
|= TFO_F_HEURISTIC_DONE
;
1039 tcp_cache_key_src_create(tp
, &tcks
);
1040 tcp_heuristic_tfo_middlebox_common(&tcks
);
1044 tcp_heuristic_ecn_aggressive_common(struct tcp_cache_key_src
*tcks
)
1046 struct tcp_heuristics_head
*head
;
1047 struct tcp_heuristic
*tpheur
;
1049 tpheur
= tcp_getheuristic_with_lock(tcks
, 1, &head
);
1050 if (tpheur
== NULL
) {
1054 if (TSTMP_GT(tpheur
->th_ecn_backoff
, tcp_now
)) {
1055 /* We are already in aggressive mode */
1056 tcp_heuristic_unlock(head
);
1060 /* Must be done before, otherwise we will start off with expo-backoff */
1061 tpheur
->th_ecn_backoff
= tcp_now
+
1062 (tcp_min_to_hz(tcp_ecn_timeout
) << (tpheur
->th_ecn_aggressive
));
1065 * Ugly way to prevent integer overflow... limit to prevent in
1066 * overflow during exp. backoff.
1068 if (tpheur
->th_ecn_aggressive
< TCP_CACHE_OVERFLOW_PROTECT
) {
1069 tpheur
->th_ecn_aggressive
++;
1072 tcp_heuristic_unlock(head
);
1074 os_log(OS_LOG_DEFAULT
, "%s disable ECN until %u now %u on %lx\n", __func__
,
1075 tpheur
->th_ecn_backoff
, tcp_now
, (unsigned long)VM_KERNEL_ADDRPERM(tpheur
));
1079 tcp_heuristic_ecn_aggressive(struct tcpcb
*tp
)
1081 struct tcp_cache_key_src tcks
;
1083 tcp_cache_key_src_create(tp
, &tcks
);
1084 tcp_heuristic_ecn_aggressive_common(&tcks
);
1088 tcp_heuristic_do_tfo_common(struct tcp_cache_key_src
*tcks
)
1090 struct tcp_heuristics_head
*head
;
1091 struct tcp_heuristic
*tpheur
;
1093 if (disable_tcp_heuristics
) {
1097 /* Get the tcp-heuristic. */
1098 tpheur
= tcp_getheuristic_with_lock(tcks
, 0, &head
);
1099 if (tpheur
== NULL
) {
1103 if (tpheur
->th_tfo_in_backoff
== 0) {
1107 if (TSTMP_GT(tcp_now
, tpheur
->th_tfo_backoff_until
)) {
1108 tpheur
->th_tfo_in_backoff
= 0;
1109 tpheur
->th_tfo_enabled_time
= tcp_now
;
1114 tcp_heuristic_unlock(head
);
1118 tcp_heuristic_unlock(head
);
1123 tcp_heuristic_do_tfo(struct tcpcb
*tp
)
1125 struct tcp_cache_key_src tcks
;
1127 tcp_cache_key_src_create(tp
, &tcks
);
1128 if (tcp_heuristic_do_tfo_common(&tcks
)) {
1136 * 0 Enable MPTCP (we are still discovering middleboxes)
1137 * -1 Enable MPTCP (heuristics have been temporarily disabled)
1141 tcp_heuristic_do_mptcp(struct tcpcb
*tp
)
1143 struct tcp_cache_key_src tcks
;
1144 struct tcp_heuristics_head
*head
= NULL
;
1145 struct tcp_heuristic
*tpheur
;
1148 if (disable_tcp_heuristics
||
1149 (tptomptp(tp
)->mpt_mpte
->mpte_flags
& MPTE_FORCE_ENABLE
)) {
1153 tcp_cache_key_src_create(tp
, &tcks
);
1155 /* Get the tcp-heuristic. */
1156 tpheur
= tcp_getheuristic_with_lock(&tcks
, 0, &head
);
1157 if (tpheur
== NULL
) {
1161 if (tpheur
->th_mptcp_in_backoff
== 0 ||
1162 tpheur
->th_mptcp_heuristic_disabled
== 1) {
1166 if (TSTMP_GT(tpheur
->th_mptcp_backoff
, tcp_now
)) {
1170 tpheur
->th_mptcp_in_backoff
= 0;
1173 if (tpheur
->th_mptcp_heuristic_disabled
) {
1176 if (TSTMP_GT(tcp_now
, tpheur
->th_mptcp_backoff
)) {
1177 tpheur
->th_mptcp_heuristic_disabled
= 0;
1178 tpheur
->th_mptcp_success
= 0;
1182 tcp_heuristic_unlock(head
);
1187 tcp_heuristic_unlock(head
);
1190 if (tptomptp(tp
)->mpt_mpte
->mpte_flags
& MPTE_FIRSTPARTY
) {
1191 tcpstat
.tcps_mptcp_fp_heuristic_fallback
++;
1193 tcpstat
.tcps_mptcp_heuristic_fallback
++;
1200 tcp_heuristic_do_ecn_common(struct tcp_cache_key_src
*tcks
)
1202 struct tcp_heuristics_head
*head
;
1203 struct tcp_heuristic
*tpheur
;
1204 boolean_t ret
= TRUE
;
1206 if (disable_tcp_heuristics
) {
1210 /* Get the tcp-heuristic. */
1211 tpheur
= tcp_getheuristic_with_lock(tcks
, 0, &head
);
1212 if (tpheur
== NULL
) {
1216 if (TSTMP_GT(tpheur
->th_ecn_backoff
, tcp_now
)) {
1219 /* Reset the following counters to start re-evaluating */
1220 if (tpheur
->th_ecn_droprst
>= ECN_RETRY_LIMIT
) {
1221 tpheur
->th_ecn_droprst
= 0;
1223 if (tpheur
->th_ecn_droprxmt
>= ECN_RETRY_LIMIT
) {
1224 tpheur
->th_ecn_droprxmt
= 0;
1226 if (tpheur
->th_ecn_synrst
>= ECN_RETRY_LIMIT
) {
1227 tpheur
->th_ecn_synrst
= 0;
1230 /* Make sure it follows along */
1231 tpheur
->th_ecn_backoff
= tcp_now
;
1234 tcp_heuristic_unlock(head
);
1240 tcp_heuristic_do_ecn(struct tcpcb
*tp
)
1242 struct tcp_cache_key_src tcks
;
1244 tcp_cache_key_src_create(tp
, &tcks
);
1245 return tcp_heuristic_do_ecn_common(&tcks
);
1249 tcp_heuristic_do_ecn_with_address(struct ifnet
*ifp
,
1250 union sockaddr_in_4_6
*local_address
)
1252 struct tcp_cache_key_src tcks
;
1254 memset(&tcks
, 0, sizeof(tcks
));
1257 calculate_tcp_clock();
1259 if (local_address
->sa
.sa_family
== AF_INET6
) {
1260 memcpy(&tcks
.laddr
.addr6
, &local_address
->sin6
.sin6_addr
, sizeof(struct in6_addr
));
1262 } else if (local_address
->sa
.sa_family
== AF_INET
) {
1263 memcpy(&tcks
.laddr
.addr
, &local_address
->sin
.sin_addr
, sizeof(struct in_addr
));
1267 return tcp_heuristic_do_ecn_common(&tcks
);
1271 tcp_heuristics_ecn_update(struct necp_tcp_ecn_cache
*necp_buffer
,
1272 struct ifnet
*ifp
, union sockaddr_in_4_6
*local_address
)
1274 struct tcp_cache_key_src tcks
;
1276 memset(&tcks
, 0, sizeof(tcks
));
1279 calculate_tcp_clock();
1281 if (local_address
->sa
.sa_family
== AF_INET6
) {
1282 memcpy(&tcks
.laddr
.addr6
, &local_address
->sin6
.sin6_addr
, sizeof(struct in6_addr
));
1284 } else if (local_address
->sa
.sa_family
== AF_INET
) {
1285 memcpy(&tcks
.laddr
.addr
, &local_address
->sin
.sin_addr
, sizeof(struct in_addr
));
1289 if (necp_buffer
->necp_tcp_ecn_heuristics_success
) {
1290 tcp_heuristic_reset_counters(&tcks
, TCPCACHE_F_ECN
);
1291 } else if (necp_buffer
->necp_tcp_ecn_heuristics_loss
) {
1292 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_ECN
);
1293 } else if (necp_buffer
->necp_tcp_ecn_heuristics_drop_rst
) {
1294 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_ECN_DROPRST
);
1295 } else if (necp_buffer
->necp_tcp_ecn_heuristics_drop_rxmt
) {
1296 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_ECN_DROPRXMT
);
1297 } else if (necp_buffer
->necp_tcp_ecn_heuristics_syn_rst
) {
1298 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_ECN_SYNRST
);
1299 } else if (necp_buffer
->necp_tcp_ecn_heuristics_aggressive
) {
1300 tcp_heuristic_ecn_aggressive_common(&tcks
);
1307 tcp_heuristic_do_tfo_with_address(struct ifnet
*ifp
,
1308 union sockaddr_in_4_6
*local_address
, union sockaddr_in_4_6
*remote_address
,
1309 uint8_t *cookie
, uint8_t *cookie_len
)
1311 struct tcp_cache_key_src tcks
;
1313 memset(&tcks
, 0, sizeof(tcks
));
1316 calculate_tcp_clock();
1318 if (remote_address
->sa
.sa_family
== AF_INET6
) {
1319 memcpy(&tcks
.laddr
.addr6
, &local_address
->sin6
.sin6_addr
, sizeof(struct in6_addr
));
1320 memcpy(&tcks
.faddr
.addr6
, &remote_address
->sin6
.sin6_addr
, sizeof(struct in6_addr
));
1322 } else if (remote_address
->sa
.sa_family
== AF_INET
) {
1323 memcpy(&tcks
.laddr
.addr
, &local_address
->sin
.sin_addr
, sizeof(struct in_addr
));
1324 memcpy(&tcks
.faddr
.addr
, &remote_address
->sin
.sin_addr
, sizeof(struct in_addr
));
1328 if (tcp_heuristic_do_tfo_common(&tcks
)) {
1329 if (!tcp_cache_get_cookie_common(&tcks
, cookie
, cookie_len
)) {
1339 tcp_heuristics_tfo_update(struct necp_tcp_tfo_cache
*necp_buffer
,
1340 struct ifnet
*ifp
, union sockaddr_in_4_6
*local_address
,
1341 union sockaddr_in_4_6
*remote_address
)
1343 struct tcp_cache_key_src tcks
;
1345 memset(&tcks
, 0, sizeof(tcks
));
1348 calculate_tcp_clock();
1350 if (remote_address
->sa
.sa_family
== AF_INET6
) {
1351 memcpy(&tcks
.laddr
.addr6
, &local_address
->sin6
.sin6_addr
, sizeof(struct in6_addr
));
1352 memcpy(&tcks
.faddr
.addr6
, &remote_address
->sin6
.sin6_addr
, sizeof(struct in6_addr
));
1354 } else if (remote_address
->sa
.sa_family
== AF_INET
) {
1355 memcpy(&tcks
.laddr
.addr
, &local_address
->sin
.sin_addr
, sizeof(struct in_addr
));
1356 memcpy(&tcks
.faddr
.addr
, &remote_address
->sin
.sin_addr
, sizeof(struct in_addr
));
1360 if (necp_buffer
->necp_tcp_tfo_heuristics_success
) {
1361 tcp_heuristic_reset_counters(&tcks
, TCPCACHE_F_TFO_REQ
| TCPCACHE_F_TFO_DATA
|
1362 TCPCACHE_F_TFO_REQ_RST
| TCPCACHE_F_TFO_DATA_RST
);
1365 if (necp_buffer
->necp_tcp_tfo_heuristics_success_req
) {
1366 tcp_heuristic_reset_counters(&tcks
, TCPCACHE_F_TFO_REQ
| TCPCACHE_F_TFO_REQ_RST
);
1369 if (necp_buffer
->necp_tcp_tfo_heuristics_loss
) {
1370 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_TFO_REQ
| TCPCACHE_F_TFO_DATA
);
1373 if (necp_buffer
->necp_tcp_tfo_heuristics_loss_req
) {
1374 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_TFO_REQ
);
1377 if (necp_buffer
->necp_tcp_tfo_heuristics_rst_data
) {
1378 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_TFO_REQ_RST
| TCPCACHE_F_TFO_DATA_RST
);
1381 if (necp_buffer
->necp_tcp_tfo_heuristics_rst_req
) {
1382 tcp_heuristic_inc_counters(&tcks
, TCPCACHE_F_TFO_REQ_RST
);
1385 if (necp_buffer
->necp_tcp_tfo_heuristics_middlebox
) {
1386 tcp_heuristic_tfo_middlebox_common(&tcks
);
1389 if (necp_buffer
->necp_tcp_tfo_cookie_len
!= 0) {
1390 tcp_cache_set_cookie_common(&tcks
,
1391 necp_buffer
->necp_tcp_tfo_cookie
, necp_buffer
->necp_tcp_tfo_cookie_len
);
1398 sysctl_cleartfocache(void)
1402 for (i
= 0; i
< tcp_cache_size
; i
++) {
1403 struct tcp_cache_head
*head
= &tcp_cache
[i
];
1404 struct tcp_cache
*tpcache
, *tmp
;
1405 struct tcp_heuristics_head
*hhead
= &tcp_heuristics
[i
];
1406 struct tcp_heuristic
*tpheur
, *htmp
;
1408 lck_mtx_lock(&head
->tch_mtx
);
1409 SLIST_FOREACH_SAFE(tpcache
, &head
->tcp_caches
, list
, tmp
) {
1410 SLIST_REMOVE(&head
->tcp_caches
, tpcache
, tcp_cache
, list
);
1411 _FREE(tpcache
, M_TEMP
);
1413 lck_mtx_unlock(&head
->tch_mtx
);
1415 lck_mtx_lock(&hhead
->thh_mtx
);
1416 SLIST_FOREACH_SAFE(tpheur
, &hhead
->tcp_heuristics
, list
, htmp
) {
1417 SLIST_REMOVE(&hhead
->tcp_heuristics
, tpheur
, tcp_heuristic
, list
);
1418 _FREE(tpheur
, M_TEMP
);
1420 lck_mtx_unlock(&hhead
->thh_mtx
);
1424 /* This sysctl is useful for testing purposes only */
1425 static int tcpcleartfo
= 0;
1427 static int sysctl_cleartfo SYSCTL_HANDLER_ARGS
1429 #pragma unused(arg1, arg2)
1430 int error
= 0, val
, oldval
= tcpcleartfo
;
1433 error
= sysctl_handle_int(oidp
, &val
, 0, req
);
1434 if (error
|| !req
->newptr
) {
1436 os_log_error(OS_LOG_DEFAULT
, "%s could not parse int: %d", __func__
, error
);
1442 * The actual value does not matter. If the value is set, it triggers
1443 * the clearing of the TFO cache. If a future implementation does not
1444 * use the route entry to hold the TFO cache, replace the route sysctl.
1447 if (val
!= oldval
) {
1448 sysctl_cleartfocache();
1456 SYSCTL_PROC(_net_inet_tcp
, OID_AUTO
, clear_tfocache
, CTLTYPE_INT
| CTLFLAG_RW
|
1457 CTLFLAG_LOCKED
, &tcpcleartfo
, 0, &sysctl_cleartfo
, "I",
1458 "Toggle to clear the TFO destination based heuristic cache");
1461 tcp_cache_init(void)
1463 uint64_t sane_size_meg
= sane_size
/ 1024 / 1024;
1467 * On machines with <100MB of memory this will result in a (full) cache-size
1468 * of 32 entries, thus 32 * 5 * 64bytes = 10KB. (about 0.01 %)
1469 * On machines with > 4GB of memory, we have a cache-size of 1024 entries,
1472 * Side-note: we convert to uint32_t. If sane_size is more than
1473 * 16000 TB, we loose precision. But, who cares? :)
1475 tcp_cache_size
= tcp_cache_roundup2((uint32_t)(sane_size_meg
>> 2));
1476 if (tcp_cache_size
< 32) {
1477 tcp_cache_size
= 32;
1478 } else if (tcp_cache_size
> 1024) {
1479 tcp_cache_size
= 1024;
1482 tcp_cache
= _MALLOC(sizeof(struct tcp_cache_head
) * tcp_cache_size
,
1484 if (tcp_cache
== NULL
) {
1485 panic("Allocating tcp_cache failed at boot-time!");
1488 tcp_cache_mtx_grp_attr
= lck_grp_attr_alloc_init();
1489 tcp_cache_mtx_grp
= lck_grp_alloc_init("tcpcache", tcp_cache_mtx_grp_attr
);
1490 tcp_cache_mtx_attr
= lck_attr_alloc_init();
1492 tcp_heuristics
= _MALLOC(sizeof(struct tcp_heuristics_head
) * tcp_cache_size
,
1494 if (tcp_heuristics
== NULL
) {
1495 panic("Allocating tcp_heuristic failed at boot-time!");
1498 tcp_heuristic_mtx_grp_attr
= lck_grp_attr_alloc_init();
1499 tcp_heuristic_mtx_grp
= lck_grp_alloc_init("tcpheuristic", tcp_heuristic_mtx_grp_attr
);
1500 tcp_heuristic_mtx_attr
= lck_attr_alloc_init();
1502 for (i
= 0; i
< tcp_cache_size
; i
++) {
1503 lck_mtx_init(&tcp_cache
[i
].tch_mtx
, tcp_cache_mtx_grp
,
1504 tcp_cache_mtx_attr
);
1505 SLIST_INIT(&tcp_cache
[i
].tcp_caches
);
1507 lck_mtx_init(&tcp_heuristics
[i
].thh_mtx
, tcp_heuristic_mtx_grp
,
1508 tcp_heuristic_mtx_attr
);
1509 SLIST_INIT(&tcp_heuristics
[i
].tcp_heuristics
);
1512 tcp_cache_hash_seed
= RandomULong();