Index: share/man/man4/tcp.4 =================================================================== --- share/man/man4/tcp.4 +++ share/man/man4/tcp.4 @@ -34,7 +34,7 @@ .\" From: @(#)tcp.4 8.1 (Berkeley) 6/5/93 .\" $FreeBSD$ .\" -.Dd July 23, 2020 +.Dd August 8, 2020 .Dt TCP 4 .Os .Sh NAME @@ -665,6 +665,20 @@ specific connection. This is needed to help with connection establishment when a broken firewall is in the network path. +.It Va ecn.plusplus +Enable sending of SYN/ACK or all control segments and retransmissions as +ECN capable transport. +Settings: +.Bl -tag -compact +.It 0 +Regular RFC3168 operation. Send only data segements as ECN capable +transport. +.It 1 +Support ECN+ (RFC5562), send SYN/ACK packets as ECN capable transport. +.It 2 +Support ECN++ (Generalized ECN), all segments of an ECN-enabled session +are sent as ECN capable transport. +.El .It Va pmtud_blackhole_detection Enable automatic path MTU blackhole detection. In case of retransmits of MSS sized segments, Index: sys/netinet/tcp_input.c =================================================================== --- sys/netinet/tcp_input.c +++ sys/netinet/tcp_input.c @@ -202,6 +202,11 @@ &VNET_NAME(tcp_ecn_maxretries), 0, "Max retries before giving up on ECN"); +VNET_DEFINE(int, tcp_ecn_plusplus) = 0; +SYSCTL_INT(_net_inet_tcp_ecn, OID_AUTO, plusplus, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(tcp_ecn_plusplus), 0, + "Send SYN,ACK and control packets as ECT"); + VNET_DEFINE(int, tcp_insecure_syn) = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, insecure_syn, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(tcp_insecure_syn), 0, Index: sys/netinet/tcp_output.c =================================================================== --- sys/netinet/tcp_output.c +++ sys/netinet/tcp_output.c @@ -1165,17 +1165,32 @@ tp->t_flags2 &= ~TF2_ECN_SND_ECE; } - if (tp->t_state == TCPS_ESTABLISHED && - (tp->t_flags2 & TF2_ECN_PERMIT)) { + if (((tp->t_state == TCPS_ESTABLISHED) && + (tp->t_flags2 & TF2_ECN_PERMIT)) || + ((tp->t_state > TCPS_ESTABLISHED) && + (tp->t_flags2 & TF2_ECN_PERMIT) && + (V_tcp_ecn_plusplus == 2)) || + /* + * Note that a passive open SYN,ACK + * is actually sent from tcp_syncache + */ + (((flags & (TH_SYN|TH_ACK)) == (TH_SYN)) && + ((flags & (TH_ECE|TH_CWR)) == (TH_ECE|TH_CWR)) && + (V_tcp_ecn_plusplus == 2)) || + (((flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) && + (flags & (TH_CWR|TH_ECE)) && + (V_tcp_ecn_plusplus))) { /* * If the peer has ECN, mark data packets with * ECN capable transmission (ECT). * Ignore pure ack packets, retransmissions and window probes. */ - if (len > 0 && SEQ_GEQ(tp->snd_nxt, tp->snd_max) && + if ((V_tcp_ecn_plusplus == 2) || + (V_tcp_ecn_plusplus && tp->t_state < TCPS_ESTABLISHED) || + ((len > 0 && SEQ_GEQ(tp->snd_nxt, tp->snd_max) && (sack_rxmit == 0) && !((tp->t_flags & TF_FORCEDATA) && len == 1 && - SEQ_LT(tp->snd_una, tp->snd_max))) { + SEQ_LT(tp->snd_una, tp->snd_max))))) { #ifdef INET6 if (isipv6) ip6->ip6_flow |= htonl(IPTOS_ECN_ECT0 << 20); Index: sys/netinet/tcp_stacks/rack.c =================================================================== --- sys/netinet/tcp_stacks/rack.c +++ sys/netinet/tcp_stacks/rack.c @@ -13576,15 +13576,30 @@ flags |= TH_ECE; tp->t_flags2 &= ~TF2_ECN_SND_ECE; } - if (tp->t_state == TCPS_ESTABLISHED && - (tp->t_flags2 & TF2_ECN_PERMIT)) { + if ((tp->t_state == TCPS_ESTABLISHED && + (tp->t_flags2 & TF2_ECN_PERMIT)) || + ((tp->t_state > TCPS_ESTABLISHED) && + (tp->t_flags2 & TF2_ECN_PERMIT) && + (V_tcp_ecn_plusplus == 2)) || + /* + * Note that a passive open SYN,ACK + * is actually sent from tcp_syncache + */ + (((flags & (TH_SYN|TH_ACK)) == (TH_SYN)) && + ((flags & (TH_ECE|TH_CWR)) == (TH_ECE|TH_CWR)) && + (V_tcp_ecn_plusplus == 2)) || + (((flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) && + (flags & (TH_CWR|TH_ECE)) && + (V_tcp_ecn_plusplus))) { /* * If the peer has ECN, mark data packets with ECN capable * transmission (ECT). Ignore pure ack packets, * retransmissions. */ - if (len > 0 && SEQ_GEQ(tp->snd_nxt, tp->snd_max) && - (sack_rxmit == 0)) { + if ((V_tcp_ecn_plusplus == 2) || + (V_tcp_ecn_plusplus && tp->t_state < TCPS_ESTABLISHED) || + ((len > 0 && SEQ_GEQ(tp->snd_nxt, tp->snd_max) && + (sack_rxmit == 0)))) { #ifdef INET6 if (isipv6) ip6->ip6_flow |= htonl(IPTOS_ECN_ECT0 << 20); Index: sys/netinet/tcp_syncache.c =================================================================== --- sys/netinet/tcp_syncache.c +++ sys/netinet/tcp_syncache.c @@ -1849,6 +1849,22 @@ if ((flags & TH_SYN) && (sc->sc_flags & SCF_ECN)) { th->th_flags |= TH_ECE; TCPSTAT_INC(tcps_ecn_shs); + + if ((V_tcp_ecn_plusplus == 2) || + ((V_tcp_ecn_plusplus) && + (flags & TH_ACK))) { +#ifdef INET6 + if (sc->sc_inc.inc_flags & INC_ISIPV6) + ip6->ip6_flow |= htonl(IPTOS_ECN_ECT0 << 20); +#endif +#if defined(INET6) && defined(INET) + else +#endif +#ifdef INET + ip->ip_tos |= IPTOS_ECN_ECT0; +#endif + TCPSTAT_INC(tcps_ecn_ect0); + } } /* Tack on the TCP options. */ Index: sys/netinet/tcp_var.h =================================================================== --- sys/netinet/tcp_var.h +++ sys/netinet/tcp_var.h @@ -838,6 +838,7 @@ VNET_DECLARE(int, tcp_do_sack); VNET_DECLARE(int, tcp_do_tso); VNET_DECLARE(int, tcp_ecn_maxretries); +VNET_DECLARE(int, tcp_ecn_plusplus); VNET_DECLARE(int, tcp_initcwnd_segments); VNET_DECLARE(int, tcp_insecure_rst); VNET_DECLARE(int, tcp_insecure_syn); @@ -880,6 +881,7 @@ #define V_tcp_do_sack VNET(tcp_do_sack) #define V_tcp_do_tso VNET(tcp_do_tso) #define V_tcp_ecn_maxretries VNET(tcp_ecn_maxretries) +#define V_tcp_ecn_plusplus VNET(tcp_ecn_plusplus) #define V_tcp_initcwnd_segments VNET(tcp_initcwnd_segments) #define V_tcp_insecure_rst VNET(tcp_insecure_rst) #define V_tcp_insecure_syn VNET(tcp_insecure_syn)