Author: bz
Date: Wed Feb 15 16:09:56 2012
New Revision: 231767
URL: http://svn.freebsd.org/changeset/base/231767

Log:
  Fix PAWS (Protect Against Wrapped Sequence numbers) in cases when
  hz >> 1000 and thus getting outside the timestamp clock frequenceny of
  1ms < x < 1s per tick as mandated by RFC1323, leading to connection
  resets on idle connections.
  
  Always use a granularity of 1ms using getmicrouptime() making all but
  relevant callouts independent of hz.
  
  Use getmicrouptime(), not getmicrotime() as the latter may make a jump
  possibly breaking TCP nfsroot mounts having our timestamps move forward
  for more than 24.8 days in a second without having been idle for that
  long.
  
  PR:           kern/61404
  Reviewed by:  jhb, mav, rrs
  Discussed with:       silby, lstewart
  Sponsored by: Sandvine Incorporated (originally in 2011)
  MFC after:    6 weeks

Modified:
  head/sys/netinet/tcp_input.c
  head/sys/netinet/tcp_output.c
  head/sys/netinet/tcp_seq.h
  head/sys/netinet/tcp_syncache.c
  head/sys/netinet/tcp_timewait.c

Modified: head/sys/netinet/tcp_input.c
==============================================================================
--- head/sys/netinet/tcp_input.c        Wed Feb 15 15:54:57 2012        
(r231766)
+++ head/sys/netinet/tcp_input.c        Wed Feb 15 16:09:56 2012        
(r231767)
@@ -1493,7 +1493,7 @@ tcp_do_segment(struct mbuf *m, struct tc
         */
        if ((to.to_flags & TOF_TS) && (to.to_tsecr != 0)) {
                to.to_tsecr -= tp->ts_offset;
-               if (TSTMP_GT(to.to_tsecr, ticks))
+               if (TSTMP_GT(to.to_tsecr, tcp_ts_getticks()))
                        to.to_tsecr = 0;
        }
 
@@ -1518,7 +1518,7 @@ tcp_do_segment(struct mbuf *m, struct tc
                if (to.to_flags & TOF_TS) {
                        tp->t_flags |= TF_RCVD_TSTMP;
                        tp->ts_recent = to.to_tsval;
-                       tp->ts_recent_age = ticks;
+                       tp->ts_recent_age = tcp_ts_getticks();
                }
                if (to.to_flags & TOF_MSS)
                        tcp_mss(tp, to.to_mss);
@@ -1562,7 +1562,7 @@ tcp_do_segment(struct mbuf *m, struct tc
                 */
                if ((to.to_flags & TOF_TS) != 0 &&
                    SEQ_LEQ(th->th_seq, tp->last_ack_sent)) {
-                       tp->ts_recent_age = ticks;
+                       tp->ts_recent_age = tcp_ts_getticks();
                        tp->ts_recent = to.to_tsval;
                }
 
@@ -1600,11 +1600,13 @@ tcp_do_segment(struct mbuf *m, struct tc
                                 */
                                if ((to.to_flags & TOF_TS) != 0 &&
                                    to.to_tsecr) {
-                                       if (!tp->t_rttlow ||
-                                           tp->t_rttlow > ticks - to.to_tsecr)
-                                               tp->t_rttlow = ticks - 
to.to_tsecr;
+                                       u_int t;
+
+                                       t = tcp_ts_getticks() - to.to_tsecr;
+                                       if (!tp->t_rttlow || tp->t_rttlow > t)
+                                               tp->t_rttlow = t;
                                        tcp_xmit_timer(tp,
-                                           ticks - to.to_tsecr + 1);
+                                           TCP_TS_TO_TICKS(t) + 1);
                                } else if (tp->t_rtttime &&
                                    SEQ_GT(th->th_ack, tp->t_rtseq)) {
                                        if (!tp->t_rttlow ||
@@ -2070,7 +2072,7 @@ tcp_do_segment(struct mbuf *m, struct tc
            TSTMP_LT(to.to_tsval, tp->ts_recent)) {
 
                /* Check to see if ts_recent is over 24 days old.  */
-               if (ticks - tp->ts_recent_age > TCP_PAWS_IDLE) {
+               if (tcp_ts_getticks() - tp->ts_recent_age > TCP_PAWS_IDLE) {
                        /*
                         * Invalidate ts_recent.  If this segment updates
                         * ts_recent, the age will be reset later and ts_recent
@@ -2229,7 +2231,7 @@ tcp_do_segment(struct mbuf *m, struct tc
            SEQ_LEQ(th->th_seq, tp->last_ack_sent) &&
            SEQ_LEQ(tp->last_ack_sent, th->th_seq + tlen +
                ((thflags & (TH_SYN|TH_FIN)) != 0))) {
-               tp->ts_recent_age = ticks;
+               tp->ts_recent_age = tcp_ts_getticks();
                tp->ts_recent = to.to_tsval;
        }
 
@@ -2543,11 +2545,13 @@ process_ACK:
                 * timestamps of 0 or we could calculate a
                 * huge RTT and blow up the retransmit timer.
                 */
-               if ((to.to_flags & TOF_TS) != 0 &&
-                   to.to_tsecr) {
-                       if (!tp->t_rttlow || tp->t_rttlow > ticks - to.to_tsecr)
-                               tp->t_rttlow = ticks - to.to_tsecr;
-                       tcp_xmit_timer(tp, ticks - to.to_tsecr + 1);
+               if ((to.to_flags & TOF_TS) != 0 && to.to_tsecr) {
+                       u_int t;
+
+                       t = tcp_ts_getticks() - to.to_tsecr;
+                       if (!tp->t_rttlow || tp->t_rttlow > t)
+                               tp->t_rttlow = t;
+                       tcp_xmit_timer(tp, TCP_TS_TO_TICKS(t) + 1);
                } else if (tp->t_rtttime && SEQ_GT(th->th_ack, tp->t_rtseq)) {
                        if (!tp->t_rttlow || tp->t_rttlow > ticks - 
tp->t_rtttime)
                                tp->t_rttlow = ticks - tp->t_rtttime;

Modified: head/sys/netinet/tcp_output.c
==============================================================================
--- head/sys/netinet/tcp_output.c       Wed Feb 15 15:54:57 2012        
(r231766)
+++ head/sys/netinet/tcp_output.c       Wed Feb 15 16:09:56 2012        
(r231767)
@@ -680,13 +680,13 @@ send:
                /* Timestamps. */
                if ((tp->t_flags & TF_RCVD_TSTMP) ||
                    ((flags & TH_SYN) && (tp->t_flags & TF_REQ_TSTMP))) {
-                       to.to_tsval = ticks + tp->ts_offset;
+                       to.to_tsval = tcp_ts_getticks() + tp->ts_offset;
                        to.to_tsecr = tp->ts_recent;
                        to.to_flags |= TOF_TS;
                        /* Set receive buffer autosizing timestamp. */
                        if (tp->rfbuf_ts == 0 &&
                            (so->so_rcv.sb_flags & SB_AUTOSIZE))
-                               tp->rfbuf_ts = ticks;
+                               tp->rfbuf_ts = tcp_ts_getticks();
                }
                /* Selective ACK's. */
                if (tp->t_flags & TF_SACK_PERMIT) {

Modified: head/sys/netinet/tcp_seq.h
==============================================================================
--- head/sys/netinet/tcp_seq.h  Wed Feb 15 15:54:57 2012        (r231766)
+++ head/sys/netinet/tcp_seq.h  Wed Feb 15 16:09:56 2012        (r231767)
@@ -62,7 +62,34 @@
        (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = \
            (tp)->snd_recover = (tp)->iss
 
-#define TCP_PAWS_IDLE  (24 * 24 * 60 * 60 * hz)
-                                       /* timestamp wrap-around time */
+#ifdef _KERNEL
+/*
+ * Clock macros for RFC 1323 timestamps.
+ */
+#define        TCP_TS_TO_TICKS(_t)     ((_t) * hz / 1000)
+
+/* Timestamp wrap-around time, 24 days. */
+#define TCP_PAWS_IDLE  (24 * 24 * 60 * 60 * 1000)
+
+/*
+ * tcp_ts_getticks() in ms, should be 1ms < x < 1000ms according to RFC 1323.
+ * We always use 1ms granularity independent of hz.
+ */
+static __inline u_int
+tcp_ts_getticks(void)
+{
+       struct timeval tv;
+       u_long ms;
+
+       /*
+        * getmicrouptime() should be good enough for any 1-1000ms granularity.
+        * Do not use getmicrotime() here as it might break nfsroot/tcp.
+        */
+       getmicrouptime(&tv);
+       ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+       return (ms);
+}
+#endif /* _KERNEL */
 
 #endif /* _NETINET_TCP_SEQ_H_ */

Modified: head/sys/netinet/tcp_syncache.c
==============================================================================
--- head/sys/netinet/tcp_syncache.c     Wed Feb 15 15:54:57 2012        
(r231766)
+++ head/sys/netinet/tcp_syncache.c     Wed Feb 15 16:09:56 2012        
(r231767)
@@ -819,7 +819,7 @@ syncache_socket(struct syncache *sc, str
                if (sc->sc_flags & SCF_TIMESTAMP) {
                        tp->t_flags |= TF_REQ_TSTMP|TF_RCVD_TSTMP;
                        tp->ts_recent = sc->sc_tsreflect;
-                       tp->ts_recent_age = ticks;
+                       tp->ts_recent_age = tcp_ts_getticks();
                        tp->ts_offset = sc->sc_tsoff;
                }
 #ifdef TCP_SIGNATURE
@@ -1226,7 +1226,7 @@ _syncache_add(struct in_conninfo *inc, s
                 */
                if (to->to_flags & TOF_TS) {
                        sc->sc_tsreflect = to->to_tsval;
-                       sc->sc_ts = ticks;
+                       sc->sc_ts = tcp_ts_getticks();
                        sc->sc_flags |= SCF_TIMESTAMP;
                }
                if (to->to_flags & TOF_SCALE) {
@@ -1667,7 +1667,7 @@ syncookie_generate(struct syncache_head 
                data |= md5_buffer[2] << 10;            /* more digest bits */
                data ^= md5_buffer[3];
                sc->sc_ts = data;
-               sc->sc_tsoff = data - ticks;            /* after XOR */
+               sc->sc_tsoff = data - tcp_ts_getticks();        /* after XOR */
        }
 
        TCPSTAT_INC(tcps_sc_sendcookie);
@@ -1752,7 +1752,7 @@ syncookie_lookup(struct in_conninfo *inc
                sc->sc_flags |= SCF_TIMESTAMP;
                sc->sc_tsreflect = to->to_tsval;
                sc->sc_ts = to->to_tsecr;
-               sc->sc_tsoff = to->to_tsecr - ticks;
+               sc->sc_tsoff = to->to_tsecr - tcp_ts_getticks();
                sc->sc_flags |= (data & 0x1) ? SCF_SIGNATURE : 0;
                sc->sc_flags |= ((data >> 1) & 0x1) ? SCF_SACK : 0;
                sc->sc_requested_s_scale = min((data >> 2) & 0xf,

Modified: head/sys/netinet/tcp_timewait.c
==============================================================================
--- head/sys/netinet/tcp_timewait.c     Wed Feb 15 15:54:57 2012        
(r231766)
+++ head/sys/netinet/tcp_timewait.c     Wed Feb 15 16:09:56 2012        
(r231767)
@@ -558,7 +558,7 @@ tcp_twrespond(struct tcptw *tw, int flag
         */
        if (tw->t_recent && flags == TH_ACK) {
                to.to_flags |= TOF_TS;
-               to.to_tsval = ticks + tw->ts_offset;
+               to.to_tsval = tcp_ts_getticks() + tw->ts_offset;
                to.to_tsecr = tw->t_recent;
        }
        optlen = tcp_addoptions(&to, (u_char *)(th + 1));
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to