From owner-svn-src-stable-10@freebsd.org Mon Jan 11 23:34:31 2016 Return-Path: Delivered-To: svn-src-stable-10@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 27BF4A6CED3; Mon, 11 Jan 2016 23:34:31 +0000 (UTC) (envelope-from hiren@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 02AB81E90; Mon, 11 Jan 2016 23:34:30 +0000 (UTC) (envelope-from hiren@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u0BNYUQG042231; Mon, 11 Jan 2016 23:34:30 GMT (envelope-from hiren@FreeBSD.org) Received: (from hiren@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u0BNYTuD042228; Mon, 11 Jan 2016 23:34:29 GMT (envelope-from hiren@FreeBSD.org) Message-Id: <201601112334.u0BNYTuD042228@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: hiren set sender to hiren@FreeBSD.org using -f From: Hiren Panchasara Date: Mon, 11 Jan 2016 23:34:29 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r293710 - stable/10/sys/netinet X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-10@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for only the 10-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 11 Jan 2016 23:34:31 -0000 Author: hiren Date: Mon Jan 11 23:34:29 2016 New Revision: 293710 URL: https://svnweb.freebsd.org/changeset/base/293710 Log: MFC: r292003 Improve tcp duplicate ack processing when SACK is present. Modified: stable/10/sys/netinet/tcp_input.c stable/10/sys/netinet/tcp_sack.c stable/10/sys/netinet/tcp_var.h Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/netinet/tcp_input.c ============================================================================== --- stable/10/sys/netinet/tcp_input.c Mon Jan 11 23:31:13 2016 (r293709) +++ stable/10/sys/netinet/tcp_input.c Mon Jan 11 23:34:29 2016 (r293710) @@ -1448,7 +1448,7 @@ tcp_do_segment(struct mbuf *m, struct tc struct tcpcb *tp, int drop_hdrlen, int tlen, uint8_t iptos, int ti_locked) { - int thflags, acked, ourfinisacked, needoutput = 0; + int thflags, acked, ourfinisacked, needoutput = 0, sack_changed; int rstreason, todrop, win; u_long tiwin; char *s; @@ -1469,6 +1469,7 @@ tcp_do_segment(struct mbuf *m, struct tc thflags = th->th_flags; inc = &tp->t_inpcb->inp_inc; tp->sackhint.last_sack_ack = 0; + sack_changed = 0; /* * If this is either a state-changing packet or current state isn't @@ -2459,7 +2460,7 @@ tcp_do_segment(struct mbuf *m, struct tc if ((tp->t_flags & TF_SACK_PERMIT) && ((to.to_flags & TOF_SACK) || !TAILQ_EMPTY(&tp->snd_holes))) - tcp_sack_doack(tp, &to, th->th_ack); + sack_changed = tcp_sack_doack(tp, &to, th->th_ack); else /* * Reset the value so that previous (valid) value @@ -2471,7 +2472,9 @@ tcp_do_segment(struct mbuf *m, struct tc hhook_run_tcp_est_in(tp, th, &to); if (SEQ_LEQ(th->th_ack, tp->snd_una)) { - if (tlen == 0 && tiwin == tp->snd_wnd) { + if (tlen == 0 && + (tiwin == tp->snd_wnd || + (tp->t_flags & TF_SACK_PERMIT))) { TCPSTAT_INC(tcps_rcvdupack); /* * If we have outstanding data (other than @@ -2500,8 +2503,20 @@ tcp_do_segment(struct mbuf *m, struct tc * When using TCP ECN, notify the peer that * we reduced the cwnd. */ - if (!tcp_timer_active(tp, TT_REXMT) || - th->th_ack != tp->snd_una) + /* + * Following 2 kinds of acks should not affect + * dupack counting: + * 1) Old acks + * 2) Acks with SACK but without any new SACK + * information in them. These could result from + * any anomaly in the network like a switch + * duplicating packets or a possible DoS attack. + */ + if (th->th_ack != tp->snd_una || + ((tp->t_flags & TF_SACK_PERMIT) && + !sack_changed)) + break; + else if (!tcp_timer_active(tp, TT_REXMT)) tp->t_dupacks = 0; else if (++tp->t_dupacks > tcprexmtthresh || IN_FASTRECOVERY(tp->t_flags)) { @@ -2660,9 +2675,20 @@ tcp_do_segment(struct mbuf *m, struct tc tp->snd_cwnd = oldcwnd; goto drop; } - } else - tp->t_dupacks = 0; + } break; + } else { + /* + * This ack is advancing the left edge, reset the + * counter. + */ + tp->t_dupacks = 0; + /* + * If this ack also has new SACK info, increment the + * counter as per rfc6675. + */ + if ((tp->t_flags & TF_SACK_PERMIT) && sack_changed) + tp->t_dupacks++; } KASSERT(SEQ_GT(th->th_ack, tp->snd_una), @@ -2681,7 +2707,6 @@ tcp_do_segment(struct mbuf *m, struct tc } else cc_post_recovery(tp, th); } - tp->t_dupacks = 0; /* * If we reach this point, ACK is not a duplicate, * i.e., it ACKs something we sent. Modified: stable/10/sys/netinet/tcp_sack.c ============================================================================== --- stable/10/sys/netinet/tcp_sack.c Mon Jan 11 23:31:13 2016 (r293709) +++ stable/10/sys/netinet/tcp_sack.c Mon Jan 11 23:34:29 2016 (r293710) @@ -344,17 +344,22 @@ tcp_sackhole_remove(struct tcpcb *tp, st * Process cumulative ACK and the TCP SACK option to update the scoreboard. * tp->snd_holes is an ordered list of holes (oldest to newest, in terms of * the sequence space). + * Returns 1 if incoming ACK has previously unknown SACK information, + * 0 otherwise. Note: We treat (snd_una, th_ack) as a sack block so any changes + * to that (i.e. left edge moving) would also be considered a change in SACK + * information which is slightly different than rfc6675. */ -void +int tcp_sack_doack(struct tcpcb *tp, struct tcpopt *to, tcp_seq th_ack) { struct sackhole *cur, *temp; struct sackblk sack, sack_blocks[TCP_MAX_SACK + 1], *sblkp; - int i, j, num_sack_blks; + int i, j, num_sack_blks, sack_changed; INP_WLOCK_ASSERT(tp->t_inpcb); num_sack_blks = 0; + sack_changed = 0; /* * If SND.UNA will be advanced by SEG.ACK, and if SACK holes exist, * treat [SND.UNA, SEG.ACK) as if it is a SACK block. @@ -391,7 +396,7 @@ tcp_sack_doack(struct tcpcb *tp, struct * received. */ if (num_sack_blks == 0) - return; + return (sack_changed); /* * Sort the SACK blocks so we can update the scoreboard with just one @@ -442,6 +447,7 @@ tcp_sack_doack(struct tcpcb *tp, struct tp->snd_fack = sblkp->end; /* Go to the previous sack block. */ sblkp--; + sack_changed = 1; } else { /* * We failed to add a new hole based on the current @@ -458,9 +464,11 @@ tcp_sack_doack(struct tcpcb *tp, struct SEQ_LT(tp->snd_fack, sblkp->end)) tp->snd_fack = sblkp->end; } - } else if (SEQ_LT(tp->snd_fack, sblkp->end)) + } else if (SEQ_LT(tp->snd_fack, sblkp->end)) { /* fack is advanced. */ tp->snd_fack = sblkp->end; + sack_changed = 1; + } /* We must have at least one SACK hole in scoreboard. */ KASSERT(!TAILQ_EMPTY(&tp->snd_holes), ("SACK scoreboard must not be empty")); @@ -489,6 +497,7 @@ tcp_sack_doack(struct tcpcb *tp, struct tp->sackhint.sack_bytes_rexmit -= (cur->rxmit - cur->start); KASSERT(tp->sackhint.sack_bytes_rexmit >= 0, ("sackhint bytes rtx >= 0")); + sack_changed = 1; if (SEQ_LEQ(sblkp->start, cur->start)) { /* Data acks at least the beginning of hole. */ if (SEQ_GEQ(sblkp->end, cur->end)) { @@ -544,6 +553,7 @@ tcp_sack_doack(struct tcpcb *tp, struct else sblkp--; } + return (sack_changed); } /* Modified: stable/10/sys/netinet/tcp_var.h ============================================================================== --- stable/10/sys/netinet/tcp_var.h Mon Jan 11 23:31:13 2016 (r293709) +++ stable/10/sys/netinet/tcp_var.h Mon Jan 11 23:34:29 2016 (r293710) @@ -756,7 +756,7 @@ void tcp_hc_update(struct in_conninfo * extern struct pr_usrreqs tcp_usrreqs; tcp_seq tcp_new_isn(struct tcpcb *); -void tcp_sack_doack(struct tcpcb *, struct tcpopt *, tcp_seq); +int tcp_sack_doack(struct tcpcb *, struct tcpopt *, tcp_seq); void tcp_update_sack_list(struct tcpcb *tp, tcp_seq rcv_laststart, tcp_seq rcv_lastend); void tcp_clean_sackreport(struct tcpcb *tp); void tcp_sack_adjust(struct tcpcb *tp);