On Wed, 27 Jun 2001, Jonathan Lemon wrote: > I don't object; while the security provided by the new scheme is nice, > breaking TIME_WAIT assassination is a serious bug in some environments, > and there should be a way to work around it now. > -- > Jonathan Ok, attached is a patch for 4.3-stable which makes the generation scheme sysctl selectable. You use the sysctl net.inet.tcp.tcp_seq_genscheme to set which scheme you want. 0 is the old random positive increments scheme, 1 is the more random OpenBSD scheme. 1 is the default setting, so those encountering the TIME_WAIT problem will have to put something in a boot script to set the variable to 0. There's one slight difference in this implementation of the old scheme versus the old implementation of it. Before, we used TCP_ISSINCR/2 for outgoing incrementations, and /4 for incoming. We use /2 in both cases now, for simplicity's sake. Please review, especially if you're experiencing the TIME_WAIT problem. Thanks, Mike "Silby" Silbersack
diff -u -r netinet.old/tcp_input.c netinet/tcp_input.c --- netinet.old/tcp_input.c Sun Jul 1 20:44:50 2001 +++ netinet/tcp_input.c Sun Jul 1 21:17:58 2001 @@ -1080,7 +1080,7 @@ if (iss) tp->iss = iss; else { - tp->iss = tcp_rndiss_next(); + tp->iss = tcp_new_isn(); } tp->irs = th->th_seq; tcp_sendseqinit(tp); @@ -1612,7 +1612,7 @@ if (thflags & TH_SYN && tp->t_state == TCPS_TIME_WAIT && SEQ_GT(th->th_seq, tp->rcv_nxt)) { - iss = tcp_rndiss_next(); + iss = tcp_new_isn(); tp = tcp_close(tp); goto findpcb; } diff -u -r netinet.old/tcp_seq.h netinet/tcp_seq.h --- netinet.old/tcp_seq.h Sun Jul 1 20:44:50 2001 +++ netinet/tcp_seq.h Sun Jul 1 21:08:02 2001 @@ -81,6 +81,24 @@ #ifdef _KERNEL extern tcp_cc tcp_ccgen; /* global connection count */ +/* + * Increment for tcp_iss each second. + * This is designed to increment at the standard 250 KB/s, + * but with a random component averaging 128 KB. + * We also increment tcp_iss by a quarter of this amount + * each time we use the value for a new connection. + * If defined, the tcp_random18() macro should produce a + * number in the range [0-0x3ffff] that is hard to predict. + * + * The variable tcp_iss and tcp_random18() are only used + * by sequence number generation scheme 0. + */ +#ifndef tcp_random18 +#define tcp_random18() (arc4random() & 0x3ffff) +#endif +#define TCP_ISSINCR (122*1024 + tcp_random18()) + +extern tcp_seq tcp_iss; #else #define TCP_ISSINCR (250*1024) /* increment for tcp_iss each second */ #endif /* _KERNEL */ diff -u -r netinet.old/tcp_subr.c netinet/tcp_subr.c --- netinet.old/tcp_subr.c Sun Jul 1 20:44:50 2001 +++ netinet/tcp_subr.c Sun Jul 1 21:35:14 2001 @@ -139,6 +139,10 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, icmp_may_rst, CTLFLAG_RW, &icmp_may_rst, 0, "Certain ICMP unreachable messages may abort connections in SYN_SENT"); +static int tcp_seq_genscheme = 1; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcp_seq_genscheme, CTLFLAG_RW, + &tcp_seq_genscheme, 0, "TCP ISN generation scheme"); + static void tcp_cleartaocache __P((void)); static void tcp_notify __P((struct inpcb *, int)); @@ -182,6 +186,7 @@ { int hashsize; + tcp_iss = arc4random(); /* wrong, but better than a constant */ tcp_ccgen = 1; tcp_cleartaocache(); @@ -1086,6 +1091,26 @@ 0, cmd, notify); } #endif /* INET6 */ + +tcp_seq +tcp_new_isn() +{ + if ((tcp_seq_genscheme > 1) || (tcp_seq_genscheme < 0)) + tcp_seq_genscheme = 1; + + switch (tcp_seq_genscheme) { + case 0: /* + * Random positive increments + */ + tcp_iss += TCP_ISSINCR/2; + return tcp_iss; + case 1: /* + * OpemBSD randomized scheme + */ + return tcp_rndiss_next(); + } + +} #define TCP_RNDISS_ROUNDS 16 #define TCP_RNDISS_OUT 7200 diff -u -r netinet.old/tcp_timer.c netinet/tcp_timer.c --- netinet.old/tcp_timer.c Sun Jul 1 20:44:50 2001 +++ netinet/tcp_timer.c Sun Jul 1 21:12:16 2001 @@ -132,6 +132,8 @@ tcp_maxidle = tcp_keepcnt * tcp_keepintvl; + tcp_iss += TCP_ISSINCR/PR_SLOWHZ; + splx(s); } diff -u -r netinet.old/tcp_usrreq.c netinet/tcp_usrreq.c --- netinet.old/tcp_usrreq.c Sun Jul 1 20:44:50 2001 +++ netinet/tcp_usrreq.c Sun Jul 1 21:18:20 2001 @@ -759,7 +759,7 @@ tcpstat.tcps_connattempt++; tp->t_state = TCPS_SYN_SENT; callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp); - tp->iss = tcp_rndiss_next(); + tp->iss = tcp_new_isn(); tcp_sendseqinit(tp); /* @@ -851,7 +851,7 @@ tcpstat.tcps_connattempt++; tp->t_state = TCPS_SYN_SENT; callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp); - tp->iss = tcp_rndiss_next(); + tp->iss = tcp_new_isn(); tcp_sendseqinit(tp); /* diff -u -r netinet.old/tcp_var.h netinet/tcp_var.h --- netinet.old/tcp_var.h Sun Jul 1 20:44:50 2001 +++ netinet/tcp_var.h Sun Jul 1 21:13:25 2001 @@ -413,6 +413,7 @@ tcp_seq tcp_rndiss_next __P((void)); u_int16_t tcp_rndiss_encrypt __P((u_int16_t)); +tcp_seq tcp_new_isn __P((void)); #endif /* _KERNEL */