tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: TCP timestamp starting value



OK, I'm now running with the attached patch (against 6.1_STABLE, in case that 
matters). It allows anyone to select and test his favourite or most hated 
variant. I've implemeted every timebase initialization method that came to 
my mind (minus joerg@'s variant with periodic re-keying, but that could be
added).

Default is the old behaviour (start counting from 1 for each connection).

Comments and reviews welcome, especially test results with real-world peers.
Index: tcp_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/tcp_input.c,v
retrieving revision 1.321.2.1
diff -u -p -r1.321.2.1 tcp_input.c
--- tcp_input.c	24 Jul 2015 07:40:17 -0000	1.321.2.1
+++ tcp_input.c	26 Jul 2016 14:57:55 -0000
@@ -4344,6 +4344,8 @@ syn_cache_add(struct sockaddr *src, stru
 	struct mbuf *ipopts;
 	struct tcp_opt_info opti;
 	int s;
+	/* if not set below, tcp_gettimebase() will not use them */
+	void *src_addr = NULL, *dst_addr = NULL;
 
 	tp = sototcpcb(so);
 
@@ -4445,6 +4447,9 @@ syn_cache_add(struct sockaddr *src, stru
 		struct sockaddr_in *srcin = (void *) src;
 		struct sockaddr_in *dstin = (void *) dst;
 
+		src_addr = &srcin->sin_addr;
+		dst_addr = &dstin->sin_addr;
+
 		sc->sc_iss = tcp_new_iss1(&dstin->sin_addr,
 		    &srcin->sin_addr, dstin->sin_port,
 		    srcin->sin_port, sizeof(dstin->sin_addr), 0);
@@ -4457,6 +4462,9 @@ syn_cache_add(struct sockaddr *src, stru
 		struct sockaddr_in6 *srcin6 = (void *) src;
 		struct sockaddr_in6 *dstin6 = (void *) dst;
 
+		src_addr = &srcin6->sin6_addr;
+		dst_addr = &dstin6->sin6_addr;
+
 		sc->sc_iss = tcp_new_iss1(&dstin6->sin6_addr,
 		    &srcin6->sin6_addr, dstin6->sin6_port,
 		    srcin6->sin6_port, sizeof(dstin6->sin6_addr), 0);
@@ -4469,7 +4477,7 @@ syn_cache_add(struct sockaddr *src, stru
 						m->m_pkthdr.rcvif : NULL,
 						sc->sc_src.sa.sa_family);
 	sc->sc_win = win;
-	sc->sc_timebase = tcp_now - 1;	/* see tcp_newtcpcb() */
+	sc->sc_timebase = tcp_gettimebase(src->sa_family, src_addr, dst_addr);
 	sc->sc_timestamp = tb.ts_recent;
 	if ((tb.t_flags & (TF_REQ_TSTMP|TF_RCVD_TSTMP)) ==
 	    (TF_REQ_TSTMP|TF_RCVD_TSTMP))
Index: tcp_subr.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/tcp_subr.c,v
retrieving revision 1.246.2.1
diff -u -p -r1.246.2.1 tcp_subr.c
--- tcp_subr.c	31 Oct 2012 17:30:20 -0000	1.246.2.1
+++ tcp_subr.c	26 Jul 2016 14:57:55 -0000
@@ -112,6 +112,7 @@ __KERNEL_RCSID(0, "$NetBSD: tcp_subr.c,v
 #include <sys/pool.h>
 #include <sys/md5.h>
 #include <sys/cprng.h>
+#include <sys/time.h>
 
 #include <net/route.h>
 #include <net/if.h>
@@ -1011,6 +1012,7 @@ tcp_newtcpcb(int family, void *aux)
 #endif
 	struct tcpcb *tp;
 	int i;
+	void *src, *dst;
 
 	/* XXX Consider using a pool_cache for speed. */
 	tp = pool_get(&tcpcb_pool, PR_NOWAIT);	/* splsoftnet via tcp_usrreq */
@@ -1040,6 +1042,9 @@ tcp_newtcpcb(int family, void *aux)
 
 		tp->t_inpcb = inp;
 		tp->t_mtudisc = ip_mtudisc;
+
+		src = &inp->inp_ip.ip_src;
+		dst = &inp->inp_ip.ip_dst;
 		break;
 	    }
 #ifdef INET6
@@ -1056,6 +1061,9 @@ tcp_newtcpcb(int family, void *aux)
 		tp->t_in6pcb = in6p;
 		/* for IPv6, always try to run path MTU discovery */
 		tp->t_mtudisc = 1;
+
+		src = &in6p->in6p_ip6.ip6_src;
+		dst = &in6p->in6p_ip6.ip6_dst;
 		break;
 	    }
 #endif /* INET6 */
@@ -1067,18 +1075,7 @@ tcp_newtcpcb(int family, void *aux)
 		return (NULL);
 	}
 
-	/*
-	 * Initialize our timebase.  When we send timestamps, we take
-	 * the delta from tcp_now -- this means each connection always
-	 * gets a timebase of 1, which makes it, among other things,
-	 * more difficult to determine how long a system has been up,
-	 * and thus how many TCP sequence increments have occurred.
-	 *
-	 * We start with 1, because 0 doesn't work with linux, which
-	 * considers timestamp 0 in a SYN packet as a bug and disables
-	 * timestamps.
-	 */
-	tp->ts_timebase = tcp_now - 1;
+	tp->ts_timebase = tcp_gettimebase(family, src, dst);
 	
 	tcp_congctl_select(tp, tcp_congctl_global_name);
 
@@ -2430,3 +2427,96 @@ tcp_statadd(u_int stat, uint64_t val)
 	KASSERT(stat < TCP_NSTATS);
 	TCP_STATADD(stat, val);
 }
+
+uint32_t
+tcp_gettimebase(int family, void *src, void *dst)
+{
+	/*
+	 * Initialize our timebase.  When we send timestamps, we take
+	 * the delta from tcp_now.
+	 * Provide different methods of chosing the initial value.
+	 * Different methods provide different levels of privacy and
+	 * compatibility with existing peers (which may reject perfectly
+	 * legal timestamp values).
+	 */
+	u_int32_t timebase;
+	struct timeval tv;
+	MD5_CTX md5_ctx;
+	unsigned char md5_hash[MD5_DIGEST_LENGTH];
+
+	switch (tcp_do_timestamps) {
+	case 0:
+		return 0;
+	default:
+	case 1:	/*
+		 * Start counting from 1 for each connection.
+		 * This is the traditional behaviour, but is known
+		 * to cause problems with certain peers getting upset
+		 * by the repetitive use of the same value.
+		 * Use 1, not 0, because Linux regards 0 as invalid.
+		 */
+		return tcp_now - 1;
+	case 2:	/*
+		 * Start counting from 1 at system boot.
+		 * This resembles Linux behaviour.
+		 * However, it leaks system uptime.
+		 */
+		return 0;
+	case 3:	/*
+		 * Start counting at a fixed point in real time
+		 * (the time this code was written).
+		 * This leaks the local perception of real time but
+		 * is trasparent to re-boots.
+		 */
+		getmicrotime(&tv);
+		timebase = (tv.tv_sec - 1469021566) * 2;
+		if (tv.tv_usec >= 500000) timebase++;
+		/*
+		 * this will underflow, but subtracting it from an incremented
+		 * tcp_now is guaranteed to DTRT.
+		 */
+		return tcp_now - timebase;
+	case 4:	/*
+		 * Use a random (but constant) epoch obtained at boot.
+		 * This resembles "hardened Linux" behaviour.
+		 * It leaks information about re-boots due to
+		 * the epoch changing.
+		 */
+		return tcp_timebase_cookie & 0x3fffffff;
+	case 5:	/*
+		 * Use an individual per-connection random epoch.
+		 * May cause problems with misbehaving peers due to
+		 * timestamps decreasing.
+		 */
+		return cprng_fast32() & 0x3fffffff;
+	case 6:	/*
+		 * Use a randomized per-address pseudo-random epoch,
+		 * i.e. a hash of src/dst addresses and a random constant
+		 * obtained at boot time. May cause problems with broken
+		 * peers.
+		 * Suggested by joerg@.
+		 */
+		MD5Init(&md5_ctx);
+		switch (family) {
+#ifdef INET
+		case AF_INET:
+			MD5Update(&md5_ctx, (unsigned char *)src,
+			          sizeof(struct in_addr));
+			MD5Update(&md5_ctx, (unsigned char *)dst,
+			          sizeof(struct in_addr));
+#endif
+#ifdef INET6
+		case AF_INET6:
+			MD5Update(&md5_ctx, (unsigned char *)src,
+			          sizeof(struct in6_addr));
+			MD5Update(&md5_ctx, (unsigned char *)dst,
+			          sizeof(struct in6_addr));
+#endif
+		}
+		MD5Update(&md5_ctx, (unsigned char *)&tcp_timebase_cookie,
+		          sizeof(tcp_timebase_cookie));
+		MD5Final(md5_hash, &md5_ctx);
+		memcpy(&timebase, md5_hash, sizeof(timebase));
+		return timebase & 0x3fffffff;
+	}
+}
Index: tcp_usrreq.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/tcp_usrreq.c,v
retrieving revision 1.162.2.3
diff -u -p -r1.162.2.3 tcp_usrreq.c
--- tcp_usrreq.c	14 Dec 2013 19:29:29 -0000	1.162.2.3
+++ tcp_usrreq.c	26 Jul 2016 14:57:55 -0000
@@ -118,6 +118,7 @@ __KERNEL_RCSID(0, "$NetBSD: tcp_usrreq.c
 #include <sys/sysctl.h>
 #include <sys/kauth.h>
 #include <sys/uidinfo.h>
+#include <sys/cprng.h>
 
 #include <net/if.h>
 #include <net/route.h>
@@ -157,6 +158,8 @@ __KERNEL_RCSID(0, "$NetBSD: tcp_usrreq.c
 #include <netinet6/ipsec.h>
 #endif /*KAME_IPSEC*/
 
+u_int32_t tcp_timebase_cookie;
+
 /*
  * TCP protocol interface to socket abstraction.
  */
@@ -2146,4 +2149,6 @@ tcp_usrreq_init(void)
 #ifdef INET6
 	sysctl_net_inet_tcp_setup2(NULL, PF_INET6, "inet6", "tcp6");
 #endif
+
+	tcp_timebase_cookie = cprng_fast32();
 }
Index: tcp_var.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/tcp_var.h,v
retrieving revision 1.169
diff -u -p -r1.169 tcp_var.h
--- tcp_var.h	2 Feb 2012 19:43:08 -0000	1.169
+++ tcp_var.h	26 Jul 2016 14:57:55 -0000
@@ -849,6 +849,8 @@ extern int tcp_do_autosndbuf;
 extern int tcp_autosndbuf_inc;
 extern int tcp_autosndbuf_max;
 
+extern u_int32_t tcp_timebase_cookie;	/* random cookie for timebase initialization */
+
 
 #define	TCPCTL_VARIABLES { \
 	{ 0 },					\
@@ -993,6 +995,8 @@ void	 syn_cache_cleanup(struct tcpcb *);
 
 int	 tcp_input_checksum(int, struct mbuf *, const struct tcphdr *, int, int,
     int);
+
+uint32_t tcp_gettimebase(int, void *, void *);
 #endif
 
 #endif /* !_NETINET_TCP_VAR_H_ */


Home | Main Index | Thread Index | Old Index