From ebfd753408381b2ff28dd85dc4d248224e374233 Mon Sep 17 00:00:00 2001 From: Michael Tuexen Date: Wed, 26 Apr 2017 06:20:58 +0000 Subject: [PATCH] When a SYN-ACK is received in SYN-SENT state, RFC 793 requires the validation of SEG.ACK as the first step. If the ACK is not acceptable, a RST segment should be sent and the segment should be dropped. Up to now, the segment was partially processed. This patch moves the check for the SEG.ACK validation up to the front as required. Reviewed by: hiren, gnn MFC after: 1 week Sponsored by: Netflix, Inc. Differential Revision: https://reviews.freebsd.org/D10424 --- sys/netinet/tcp_input.c | 17 ++++++++------- sys/netinet/tcp_stacks/fastpath.c | 35 ++++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index e06b990a1dec..a7bed762f49f 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -1623,6 +1623,16 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, goto drop; } + /* + * If a segment with the ACK-bit set arrives in the SYN-SENT state + * check SEQ.ACK first. + */ + if ((tp->t_state == TCPS_SYN_SENT) && (thflags & TH_ACK) && + (SEQ_LEQ(th->th_ack, tp->iss) || SEQ_GT(th->th_ack, tp->snd_max))) { + rstreason = BANDLIM_UNLIMITED; + goto dropwithreset; + } + /* * Segment received on connection. * Reset idle time and keep-alive timer. @@ -2001,7 +2011,6 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, /* * If the state is SYN_SENT: - * if seg contains an ACK, but not for our SYN, drop the input. * if seg contains a RST, then drop the connection. * if seg does not contain SYN, then drop it. * Otherwise this is an acceptable SYN segment @@ -2014,12 +2023,6 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, * continue processing rest of data/controls, beginning with URG */ case TCPS_SYN_SENT: - if ((thflags & TH_ACK) && - (SEQ_LEQ(th->th_ack, tp->iss) || - SEQ_GT(th->th_ack, tp->snd_max))) { - rstreason = BANDLIM_UNLIMITED; - goto dropwithreset; - } if ((thflags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) { TCP_PROBE5(connect__refused, NULL, tp, m, tp, th); diff --git a/sys/netinet/tcp_stacks/fastpath.c b/sys/netinet/tcp_stacks/fastpath.c index 01d16d782ab1..ed00530c7453 100644 --- a/sys/netinet/tcp_stacks/fastpath.c +++ b/sys/netinet/tcp_stacks/fastpath.c @@ -497,7 +497,6 @@ tcp_do_slowpath(struct mbuf *m, struct tcphdr *th, struct socket *so, /* * If the state is SYN_SENT: - * if seg contains an ACK, but not for our SYN, drop the input. * if seg contains a RST, then drop the connection. * if seg does not contain SYN, then drop it. * Otherwise this is an acceptable SYN segment @@ -510,12 +509,6 @@ tcp_do_slowpath(struct mbuf *m, struct tcphdr *th, struct socket *so, * continue processing rest of data/controls, beginning with URG */ case TCPS_SYN_SENT: - if ((thflags & TH_ACK) && - (SEQ_LEQ(th->th_ack, tp->iss) || - SEQ_GT(th->th_ack, tp->snd_max))) { - rstreason = BANDLIM_UNLIMITED; - goto dropwithreset; - } if ((thflags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) { TCP_PROBE5(connect__refused, NULL, tp, m, tp, th); tp = tcp_drop(tp, ECONNREFUSED); @@ -1775,6 +1768,20 @@ tcp_do_segment_fastslow(struct mbuf *m, struct tcphdr *th, struct socket *so, m_freem(m); return; } + + /* + * If a segment with the ACK-bit set arrives in the SYN-SENT state + * check SEQ.ACK first. + */ + if ((tp->t_state == TCPS_SYN_SENT) && (thflags & TH_ACK) && + (SEQ_LEQ(th->th_ack, tp->iss) || SEQ_GT(th->th_ack, tp->snd_max))) { + tcp_dropwithreset(m, th, tp, tlen, BANDLIM_UNLIMITED); + if (ti_locked == TI_RLOCKED) { + INP_INFO_RUNLOCK(&V_tcbinfo); + } + INP_WUNLOCK(tp->t_inpcb); + return; + } tp->sackhint.last_sack_ack = 0; @@ -2237,6 +2244,20 @@ tcp_do_segment_fastack(struct mbuf *m, struct tcphdr *th, struct socket *so, return; } + /* + * If a segment with the ACK-bit set arrives in the SYN-SENT state + * check SEQ.ACK first. + */ + if ((tp->t_state == TCPS_SYN_SENT) && (thflags & TH_ACK) && + (SEQ_LEQ(th->th_ack, tp->iss) || SEQ_GT(th->th_ack, tp->snd_max))) { + tcp_dropwithreset(m, th, tp, tlen, BANDLIM_UNLIMITED); + if (ti_locked == TI_RLOCKED) { + INP_INFO_RUNLOCK(&V_tcbinfo); + } + INP_WUNLOCK(tp->t_inpcb); + return; + } + tp->sackhint.last_sack_ack = 0; /*