Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/tcp_ecn.c
Show First 20 Lines • Show All 103 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Process incoming SYN,ACK packet | * Process incoming SYN,ACK packet | ||||
*/ | */ | ||||
void | void | ||||
tcp_ecn_input_syn_sent(struct tcpcb *tp, uint16_t thflags, int iptos) | tcp_ecn_input_syn_sent(struct tcpcb *tp, uint16_t thflags, int iptos) | ||||
{ | { | ||||
thflags &= (TH_CWR|TH_ECE); | |||||
if (((thflags & (TH_CWR | TH_ECE)) == TH_ECE) && | if (V_tcp_do_ecn == 0) | ||||
V_tcp_do_ecn) { | return; | ||||
if ((V_tcp_do_ecn == 1) || | |||||
(V_tcp_do_ecn == 2)) { | |||||
/* RFC3168 ECN handling */ | |||||
if ((thflags & (TH_CWR | TH_ECE)) == (0 | TH_ECE)) { | |||||
tp->t_flags2 |= TF2_ECN_PERMIT; | tp->t_flags2 |= TF2_ECN_PERMIT; | ||||
TCPSTAT_INC(tcps_ecn_shs); | TCPSTAT_INC(tcps_ecn_shs); | ||||
} | } | ||||
} else | |||||
/* decoding Accurate ECN according to table in section 3.1.1 */ | |||||
if ((V_tcp_do_ecn == 3) || | |||||
(V_tcp_do_ecn == 4)) { | |||||
/* | |||||
* on the SYN,ACK, process the AccECN | |||||
* flags indicating the state the SYN | |||||
* was delivered. | |||||
* Reactions to Path ECN mangling can | |||||
* come here. | |||||
*/ | |||||
switch (thflags & (TH_AE | TH_CWR | TH_ECE)) { | |||||
/* non-ECT SYN */ | |||||
case (0|TH_CWR|0): | |||||
tp->t_flags2 |= TF2_ACE_PERMIT; | |||||
tp->t_scep = 5; | |||||
TCPSTAT_INC(tcps_ecn_shs); | |||||
TCPSTAT_INC(tcps_ace_nect); | |||||
break; | |||||
/* ECT0 SYN */ | |||||
case (TH_AE|0|0): | |||||
tp->t_flags2 |= TF2_ACE_PERMIT; | |||||
tp->t_scep = 5; | |||||
TCPSTAT_INC(tcps_ecn_shs); | |||||
TCPSTAT_INC(tcps_ace_ect0); | |||||
break; | |||||
/* ECT1 SYN */ | |||||
case (0|TH_CWR|TH_ECE): | |||||
tp->t_flags2 |= TF2_ACE_PERMIT; | |||||
tp->t_scep = 5; | |||||
TCPSTAT_INC(tcps_ecn_shs); | |||||
TCPSTAT_INC(tcps_ace_ect1); | |||||
break; | |||||
/* CE SYN */ | |||||
case (TH_AE|TH_CWR|0): | |||||
tp->t_flags2 |= TF2_ACE_PERMIT; | |||||
tp->t_scep = 6; | |||||
/* | |||||
* reduce the IW to 2 MSS (to | |||||
* account for delayed acks) if | |||||
* the SYN,ACK was CE marked | |||||
*/ | |||||
tp->snd_cwnd = 2 * tcp_maxseg(tp); | |||||
TCPSTAT_INC(tcps_ecn_shs); | |||||
TCPSTAT_INC(tcps_ace_nect); | |||||
break; | |||||
default: | |||||
break; | |||||
} | } | ||||
/* | |||||
* Set the AccECN Codepoints on | |||||
* the outgoing <ACK> to the ECN | |||||
* state of the <SYN,ACK> | |||||
* according to table 3 in the | |||||
* AccECN draft | |||||
*/ | |||||
switch (iptos & IPTOS_ECN_MASK) { | |||||
case (IPTOS_ECN_NOTECT): | |||||
tp->t_rcep = 0b010; | |||||
break; | |||||
case (IPTOS_ECN_ECT0): | |||||
tp->t_rcep = 0b100; | |||||
break; | |||||
case (IPTOS_ECN_ECT1): | |||||
tp->t_rcep = 0b011; | |||||
break; | |||||
case (IPTOS_ECN_CE): | |||||
tp->t_rcep = 0b110; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
/* | /* | ||||
* Handle parallel SYN for ECN | * Handle parallel SYN for ECN | ||||
*/ | */ | ||||
void | void | ||||
tcp_ecn_input_parallel_syn(struct tcpcb *tp, uint16_t thflags, int iptos) | tcp_ecn_input_parallel_syn(struct tcpcb *tp, uint16_t thflags, int iptos) | ||||
{ | { | ||||
if (thflags & TH_ACK) | if (thflags & TH_ACK) | ||||
return; | return; | ||||
if (V_tcp_do_ecn == 0) | if (V_tcp_do_ecn == 0) | ||||
return; | return; | ||||
if ((V_tcp_do_ecn == 1) || (V_tcp_do_ecn == 2)) { | if ((V_tcp_do_ecn == 1) || | ||||
(V_tcp_do_ecn == 2)) { | |||||
/* RFC3168 ECN handling */ | /* RFC3168 ECN handling */ | ||||
if ((thflags & (TH_CWR | TH_ECE)) == (TH_CWR | TH_ECE)) { | if ((thflags & (TH_CWR | TH_ECE)) == (TH_CWR | TH_ECE)) { | ||||
tp->t_flags2 |= TF2_ECN_PERMIT; | tp->t_flags2 |= TF2_ECN_PERMIT; | ||||
tp->t_flags2 |= TF2_ECN_SND_ECE; | tp->t_flags2 |= TF2_ECN_SND_ECE; | ||||
TCPSTAT_INC(tcps_ecn_shs); | TCPSTAT_INC(tcps_ecn_shs); | ||||
} | } | ||||
} else | |||||
if ((V_tcp_do_ecn == 3) || | |||||
(V_tcp_do_ecn == 4)) { | |||||
/* AccECN handling */ | |||||
switch (thflags & (TH_AE | TH_CWR | TH_ECE)) { | |||||
default: | |||||
case (0|0|0): | |||||
break; | |||||
case (0|TH_CWR|TH_ECE): | |||||
tp->t_flags2 |= TF2_ECN_PERMIT; | |||||
tp->t_flags2 |= TF2_ECN_SND_ECE; | |||||
TCPSTAT_INC(tcps_ecn_shs); | |||||
break; | |||||
case (TH_AE|TH_CWR|TH_ECE): | |||||
tp->t_flags2 |= TF2_ACE_PERMIT; | |||||
TCPSTAT_INC(tcps_ecn_shs); | |||||
/* | |||||
* Set the AccECN Codepoints on | |||||
* the outgoing <ACK> to the ECN | |||||
* state of the <SYN,ACK> | |||||
* according to table 3 in the | |||||
* AccECN draft | |||||
*/ | |||||
switch (iptos & IPTOS_ECN_MASK) { | |||||
case (IPTOS_ECN_NOTECT): | |||||
tp->t_rcep = 0b010; | |||||
break; | |||||
case (IPTOS_ECN_ECT0): | |||||
tp->t_rcep = 0b100; | |||||
break; | |||||
case (IPTOS_ECN_ECT1): | |||||
tp->t_rcep = 0b011; | |||||
break; | |||||
case (IPTOS_ECN_CE): | |||||
tp->t_rcep = 0b110; | |||||
break; | |||||
} | } | ||||
break; | |||||
} | } | ||||
} | |||||
} | |||||
/* | /* | ||||
* TCP ECN processing. | * TCP ECN processing. | ||||
*/ | */ | ||||
int | int | ||||
tcp_ecn_input_segment(struct tcpcb *tp, uint16_t thflags, int iptos) | tcp_ecn_input_segment(struct tcpcb *tp, uint16_t thflags, int iptos) | ||||
{ | { | ||||
int delta_ace = 0; | int delta_ace = 0; | ||||
if (tp->t_flags2 & TF2_ECN_PERMIT) { | if (tp->t_flags2 & (TF2_ECN_PERMIT | TF2_ACE_PERMIT)) { | ||||
switch (iptos & IPTOS_ECN_MASK) { | switch (iptos & IPTOS_ECN_MASK) { | ||||
case IPTOS_ECN_CE: | case IPTOS_ECN_CE: | ||||
TCPSTAT_INC(tcps_ecn_ce); | TCPSTAT_INC(tcps_ecn_ce); | ||||
break; | break; | ||||
case IPTOS_ECN_ECT0: | case IPTOS_ECN_ECT0: | ||||
TCPSTAT_INC(tcps_ecn_ect0); | TCPSTAT_INC(tcps_ecn_ect0); | ||||
break; | break; | ||||
case IPTOS_ECN_ECT1: | case IPTOS_ECN_ECT1: | ||||
TCPSTAT_INC(tcps_ecn_ect1); | TCPSTAT_INC(tcps_ecn_ect1); | ||||
break; | break; | ||||
} | } | ||||
if (tp->t_flags2 & TF2_ACE_PERMIT) { | |||||
if ((iptos & IPTOS_ECN_MASK) == IPTOS_ECN_CE) | |||||
tp->t_rcep += 1; | |||||
if (tp->t_flags2 & TF2_ECN_PERMIT) { | |||||
delta_ace = (tcp_ecn_get_ace(thflags) + 8 - | |||||
(tp->t_scep & 0x07)) & 0x07; | |||||
tp->t_scep += delta_ace; | |||||
} else { | |||||
/* | |||||
* process the final ACK of the 3WHS | |||||
* see table 3 in draft-ietf-tcpm-accurate-ecn | |||||
*/ | |||||
switch (tcp_ecn_get_ace(thflags)) { | |||||
case 0b010: | |||||
/* nonECT SYN or SYN,ACK */ | |||||
/* Fallthrough */ | |||||
case 0b011: | |||||
/* ECT1 SYN or SYN,ACK */ | |||||
/* Fallthrough */ | |||||
case 0b100: | |||||
/* ECT0 SYN or SYN,ACK */ | |||||
tp->t_scep = 5; | |||||
break; | |||||
case 0b110: | |||||
/* CE SYN or SYN,ACK */ | |||||
tp->t_scep = 6; | |||||
tp->snd_cwnd = 2 * tcp_maxseg(tp); | |||||
break; | |||||
default: | |||||
/* mangled AccECN handshake */ | |||||
tp->t_scep = 5; | |||||
break; | |||||
} | |||||
tp->t_flags2 |= TF2_ECN_PERMIT; | |||||
} | |||||
} else { | |||||
/* RFC3168 ECN handling */ | /* RFC3168 ECN handling */ | ||||
if (thflags & TH_ECE) | if (thflags & TH_ECE) | ||||
delta_ace = 1; | delta_ace = 1; | ||||
if (thflags & TH_CWR) { | if (thflags & TH_CWR) { | ||||
tp->t_flags2 &= ~TF2_ECN_SND_ECE; | tp->t_flags2 &= ~TF2_ECN_SND_ECE; | ||||
tp->t_flags |= TF_ACKNOW; | tp->t_flags |= TF_ACKNOW; | ||||
} | } | ||||
if ((iptos & IPTOS_ECN_MASK) == IPTOS_ECN_CE) | if ((iptos & IPTOS_ECN_MASK) == IPTOS_ECN_CE) | ||||
tp->t_flags2 |= TF2_ECN_SND_ECE; | tp->t_flags2 |= TF2_ECN_SND_ECE; | ||||
} | |||||
/* Process a packet differently from RFC3168. */ | /* Process a packet differently from RFC3168. */ | ||||
cc_ecnpkt_handler_flags(tp, thflags, iptos); | cc_ecnpkt_handler_flags(tp, thflags, iptos); | ||||
} | } | ||||
return delta_ace; | return delta_ace; | ||||
} | } | ||||
/* | /* | ||||
* Send ECN setup <SYN> packet header flags | * Send ECN setup <SYN> packet header flags | ||||
*/ | */ | ||||
uint16_t | uint16_t | ||||
tcp_ecn_output_syn_sent(struct tcpcb *tp) | tcp_ecn_output_syn_sent(struct tcpcb *tp) | ||||
{ | { | ||||
uint16_t thflags = 0; | uint16_t thflags = 0; | ||||
if (V_tcp_do_ecn == 0) | |||||
return thflags; | |||||
if (V_tcp_do_ecn == 1) { | if (V_tcp_do_ecn == 1) { | ||||
/* Send a RFC3168 ECN setup <SYN> packet */ | /* Send a RFC3168 ECN setup <SYN> packet */ | ||||
if (tp->t_rxtshift >= 1) { | if (tp->t_rxtshift >= 1) { | ||||
if (tp->t_rxtshift <= V_tcp_ecn_maxretries) | if (tp->t_rxtshift <= V_tcp_ecn_maxretries) | ||||
thflags = TH_ECE|TH_CWR; | thflags = TH_ECE|TH_CWR; | ||||
} else | } else | ||||
thflags = TH_ECE|TH_CWR; | thflags = TH_ECE|TH_CWR; | ||||
} else | |||||
if (V_tcp_do_ecn == 3) { | |||||
/* Send an Accurate ECN setup <SYN> packet */ | |||||
if (tp->t_rxtshift >= 1) { | |||||
if (tp->t_rxtshift <= V_tcp_ecn_maxretries) | |||||
thflags = TH_ECE|TH_CWR|TH_AE; | |||||
} else | |||||
thflags = TH_ECE|TH_CWR|TH_AE; | |||||
} | } | ||||
return thflags; | return thflags; | ||||
} | } | ||||
/* | /* | ||||
* output processing of ECN feature | * output processing of ECN feature | ||||
* returning IP ECN header codepoint | * returning IP ECN header codepoint | ||||
*/ | */ | ||||
int | int | ||||
tcp_ecn_output_established(struct tcpcb *tp, uint16_t *thflags, int len) | tcp_ecn_output_established(struct tcpcb *tp, uint16_t *thflags, int len) | ||||
{ | { | ||||
int ipecn = IPTOS_ECN_NOTECT; | int ipecn = IPTOS_ECN_NOTECT; | ||||
bool newdata; | bool newdata; | ||||
/* | /* | ||||
* If the peer has ECN, mark data packets with | * If the peer has ECN, mark data packets with | ||||
* ECN capable transmission (ECT). | * ECN capable transmission (ECT). | ||||
* Ignore pure control packets, retransmissions | * Ignore pure control packets, retransmissions | ||||
* and window probes. | * and window probes. | ||||
*/ | */ | ||||
newdata = (len > 0 && SEQ_GEQ(tp->snd_nxt, tp->snd_max) && | newdata = (len > 0 && SEQ_GEQ(tp->snd_nxt, tp->snd_max) && | ||||
!((tp->t_flags & TF_FORCEDATA) && len == 1)); | !((tp->t_flags & TF_FORCEDATA) && len == 1)); | ||||
/* RFC3168 ECN marking, only new data segments */ | |||||
if (newdata) { | if (newdata) { | ||||
ipecn = IPTOS_ECN_ECT0; | ipecn = IPTOS_ECN_ECT0; | ||||
TCPSTAT_INC(tcps_ecn_ect0); | TCPSTAT_INC(tcps_ecn_ect0); | ||||
} | } | ||||
/* | /* | ||||
* Reply with proper ECN notifications. | * Reply with proper ECN notifications. | ||||
*/ | */ | ||||
if (tp->t_flags2 & TF2_ACE_PERMIT) { | |||||
*thflags &= ~(TH_AE|TH_CWR|TH_ECE); | |||||
if (tp->t_rcep & 0x01) | |||||
*thflags |= TH_ECE; | |||||
if (tp->t_rcep & 0x02) | |||||
*thflags |= TH_CWR; | |||||
if (tp->t_rcep & 0x04) | |||||
*thflags |= TH_AE; | |||||
if (!(tp->t_flags2 & TF2_ECN_PERMIT)) { | |||||
/* | |||||
* here we process the final | |||||
* ACK of the 3WHS | |||||
*/ | |||||
if (tp->t_rcep == 0b110) { | |||||
tp->t_rcep = 6; | |||||
} else { | |||||
tp->t_rcep = 5; | |||||
} | |||||
tp->t_flags2 |= TF2_ECN_PERMIT; | |||||
} | |||||
} else { | |||||
if (newdata && | if (newdata && | ||||
(tp->t_flags2 & TF2_ECN_SND_CWR)) { | (tp->t_flags2 & TF2_ECN_SND_CWR)) { | ||||
*thflags |= TH_CWR; | *thflags |= TH_CWR; | ||||
tp->t_flags2 &= ~TF2_ECN_SND_CWR; | tp->t_flags2 &= ~TF2_ECN_SND_CWR; | ||||
} | } | ||||
if (tp->t_flags2 & TF2_ECN_SND_ECE) | if (tp->t_flags2 & TF2_ECN_SND_ECE) | ||||
*thflags |= TH_ECE; | *thflags |= TH_ECE; | ||||
} | |||||
return ipecn; | return ipecn; | ||||
} | } | ||||
/* | /* | ||||
* Set up the ECN related tcpcb fields from | * Set up the ECN related tcpcb fields from | ||||
* a syncache entry | * a syncache entry | ||||
*/ | */ | ||||
void | void | ||||
tcp_ecn_syncache_socket(struct tcpcb *tp, struct syncache *sc) | tcp_ecn_syncache_socket(struct tcpcb *tp, struct syncache *sc) | ||||
{ | { | ||||
if (sc->sc_flags & SCF_ECN_MASK) { | if (sc->sc_flags & SCF_ECN_MASK) { | ||||
switch (sc->sc_flags & SCF_ECN_MASK) { | switch (sc->sc_flags & SCF_ECN_MASK) { | ||||
case SCF_ECN: | case SCF_ECN: | ||||
tp->t_flags2 |= TF2_ECN_PERMIT; | tp->t_flags2 |= TF2_ECN_PERMIT; | ||||
break; | break; | ||||
case SCF_ACE_N: | |||||
/* Fallthrough */ | |||||
case SCF_ACE_0: | |||||
/* Fallthrough */ | |||||
case SCF_ACE_1: | |||||
tp->t_flags2 |= TF2_ACE_PERMIT; | |||||
tp->t_scep = 5; | |||||
tp->t_rcep = 5; | |||||
break; | |||||
case SCF_ACE_CE: | |||||
tp->t_flags2 |= TF2_ACE_PERMIT; | |||||
tp->t_scep = 6; | |||||
tp->t_rcep = 6; | |||||
break; | |||||
/* undefined SCF codepoint */ | /* undefined SCF codepoint */ | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Process a <SYN> packets ECN information, and provide the | * Process a <SYN> packets ECN information, and provide the | ||||
* syncache with the relevant information. | * syncache with the relevant information. | ||||
*/ | */ | ||||
int | int | ||||
tcp_ecn_syncache_add(uint16_t thflags, int iptos) | tcp_ecn_syncache_add(uint16_t thflags, int iptos) | ||||
{ | { | ||||
int scflags = 0; | int scflags = 0; | ||||
switch (thflags & (TH_CWR|TH_ECE)) { | switch (thflags & (TH_AE|TH_CWR|TH_ECE)) { | ||||
/* no ECN */ | /* no ECN */ | ||||
case (0|0): | case (0|0|0): | ||||
break; | break; | ||||
/* legacy ECN */ | /* legacy ECN */ | ||||
case (TH_CWR|TH_ECE): | case (0|TH_CWR|TH_ECE): | ||||
scflags = SCF_ECN; | scflags = SCF_ECN; | ||||
break; | break; | ||||
/* Accurate ECN */ | |||||
case (TH_AE|TH_CWR|TH_ECE): | |||||
if ((V_tcp_do_ecn == 3) || | |||||
(V_tcp_do_ecn == 4)) { | |||||
switch (iptos & IPTOS_ECN_MASK) { | |||||
case IPTOS_ECN_CE: | |||||
scflags = SCF_ACE_CE; | |||||
break; | |||||
case IPTOS_ECN_ECT0: | |||||
scflags = SCF_ACE_0; | |||||
break; | |||||
case IPTOS_ECN_ECT1: | |||||
scflags = SCF_ACE_1; | |||||
break; | |||||
case IPTOS_ECN_NOTECT: | |||||
scflags = SCF_ACE_N; | |||||
break; | |||||
} | |||||
} else | |||||
scflags = SCF_ECN; | |||||
break; | |||||
/* Default Case (section 3.1.2) */ | |||||
default: | default: | ||||
if ((V_tcp_do_ecn == 3) || | |||||
(V_tcp_do_ecn == 4)) { | |||||
switch (iptos & IPTOS_ECN_MASK) { | |||||
case IPTOS_ECN_CE: | |||||
scflags = SCF_ACE_CE; | |||||
break; | break; | ||||
case IPTOS_ECN_ECT0: | |||||
scflags = SCF_ACE_0; | |||||
break; | |||||
case IPTOS_ECN_ECT1: | |||||
scflags = SCF_ACE_1; | |||||
break; | |||||
case IPTOS_ECN_NOTECT: | |||||
scflags = SCF_ACE_N; | |||||
break; | |||||
} | } | ||||
} | |||||
break; | |||||
} | |||||
return scflags; | return scflags; | ||||
} | } | ||||
/* | /* | ||||
* Set up the ECN information for the <SYN,ACK> from | * Set up the ECN information for the <SYN,ACK> from | ||||
* syncache information. | * syncache information. | ||||
*/ | */ | ||||
uint16_t | uint16_t | ||||
tcp_ecn_syncache_respond(uint16_t thflags, struct syncache *sc) | tcp_ecn_syncache_respond(uint16_t thflags, struct syncache *sc) | ||||
{ | { | ||||
if ((thflags & TH_SYN) && | if ((thflags & TH_SYN) && | ||||
(sc->sc_flags & SCF_ECN_MASK)) { | (sc->sc_flags & SCF_ECN_MASK)) { | ||||
switch (sc->sc_flags & SCF_ECN_MASK) { | switch (sc->sc_flags & SCF_ECN_MASK) { | ||||
case SCF_ECN: | case SCF_ECN: | ||||
thflags |= (0 | TH_ECE); | thflags |= (0 | 0 | TH_ECE); | ||||
TCPSTAT_INC(tcps_ecn_shs); | TCPSTAT_INC(tcps_ecn_shs); | ||||
break; | break; | ||||
case SCF_ACE_N: | |||||
thflags |= (0 | TH_CWR | 0); | |||||
TCPSTAT_INC(tcps_ecn_shs); | |||||
TCPSTAT_INC(tcps_ace_nect); | |||||
break; | |||||
case SCF_ACE_0: | |||||
thflags |= (TH_AE | 0 | 0); | |||||
TCPSTAT_INC(tcps_ecn_shs); | |||||
TCPSTAT_INC(tcps_ace_ect0); | |||||
break; | |||||
case SCF_ACE_1: | |||||
thflags |= (0 | TH_ECE | TH_CWR); | |||||
TCPSTAT_INC(tcps_ecn_shs); | |||||
TCPSTAT_INC(tcps_ace_ect1); | |||||
break; | |||||
case SCF_ACE_CE: | |||||
thflags |= (TH_AE | TH_CWR | 0); | |||||
TCPSTAT_INC(tcps_ecn_shs); | |||||
TCPSTAT_INC(tcps_ace_ce); | |||||
break; | |||||
/* undefined SCF codepoint */ | /* undefined SCF codepoint */ | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
return thflags; | return thflags; | ||||
} | |||||
int | |||||
tcp_ecn_get_ace(uint16_t thflags) | |||||
{ | |||||
int ace = 0; | |||||
if (thflags & TH_ECE) | |||||
ace += 1; | |||||
if (thflags & TH_CWR) | |||||
ace += 2; | |||||
if (thflags & TH_AE) | |||||
ace += 4; | |||||
return ace; | |||||
} | } |