diff options
Diffstat (limited to 'sys/netinet/tcp_subr.c')
| -rw-r--r-- | sys/netinet/tcp_subr.c | 167 |
1 files changed, 96 insertions, 71 deletions
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 5872d7983825..8896f3f400e2 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -98,6 +98,7 @@ #endif /*IPSEC*/ #include <machine/in_cksum.h> +#include <sys/md5.h> int tcp_mssdflt = TCP_MSS; SYSCTL_INT(_net_inet_tcp, TCPCTL_MSSDFLT, mssdflt, CTLFLAG_RW, @@ -139,9 +140,13 @@ static int icmp_may_rst = 1; 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 int tcp_strict_rfc1948 = 0; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, strict_rfc1948, CTLFLAG_RW, + &tcp_strict_rfc1948, 0, "Determines if RFC1948 is followed exactly"); + +static int tcp_isn_reseed_interval = 0; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, isn_reseed_interval, CTLFLAG_RW, + &tcp_isn_reseed_interval, 0, "Seconds between reseeding of ISN secret"); static void tcp_cleartaocache __P((void)); static void tcp_notify __P((struct inpcb *, int)); @@ -186,7 +191,6 @@ tcp_init() { int hashsize = TCBHASHSIZE; - tcp_iss = arc4random(); /* wrong, but better than a constant */ tcp_ccgen = 1; tcp_cleartaocache(); @@ -1112,81 +1116,102 @@ tcp6_ctlinput(cmd, sa, d) } #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: /* OpenBSD randomized scheme */ - return tcp_rndiss_next(); - default: - panic("cannot happen"); - } -} - -#define TCP_RNDISS_ROUNDS 16 -#define TCP_RNDISS_OUT 7200 -#define TCP_RNDISS_MAX 30000 - -u_int8_t tcp_rndiss_sbox[128]; -u_int16_t tcp_rndiss_msb; -u_int16_t tcp_rndiss_cnt; -long tcp_rndiss_reseed; - -u_int16_t -tcp_rndiss_encrypt(val) - u_int16_t val; -{ - u_int16_t sum = 0, i; - - for (i = 0; i < TCP_RNDISS_ROUNDS; i++) { - sum += 0x79b9; - val ^= ((u_int16_t)tcp_rndiss_sbox[(val^sum) & 0x7f]) << 7; - val = ((val & 0xff) << 7) | (val >> 8); - } - - return val; -} -void -tcp_rndiss_init() -{ - struct timeval time; +/* + * Following is where TCP initial sequence number generation occurs. + * + * There are two places where we must use initial sequence numbers: + * 1. In SYN-ACK packets. + * 2. In SYN packets. + * + * The ISNs in SYN-ACK packets have no monotonicity requirement, + * and should be as unpredictable as possible to avoid the possibility + * of spoofing and/or connection hijacking. To satisfy this + * requirement, SYN-ACK ISNs are generated via the arc4random() + * function. If exact RFC 1948 compliance is requested via sysctl, + * these ISNs will be generated just like those in SYN packets. + * + * The ISNs in SYN packets must be monotonic; TIME_WAIT recycling + * depends on this property. In addition, these ISNs should be + * unguessable so as to prevent connection hijacking. To satisfy + * the requirements of this situation, the algorithm outlined in + * RFC 1948 is used to generate sequence numbers. + * + * For more information on the theory of operation, please see + * RFC 1948. + * + * Implementation details: + * + * Time is based off the system timer, and is corrected so that it + * increases by one megabyte per second. This allows for proper + * recycling on high speed LANs while still leaving over an hour + * before rollover. + * + * Two sysctls control the generation of ISNs: + * + * net.inet.tcp.isn_reseed_interval controls the number of seconds + * between seeding of isn_secret. This is normally set to zero, + * as reseeding should not be necessary. + * + * net.inet.tcp.strict_rfc1948 controls whether RFC 1948 is followed + * strictly. When strict compliance is requested, reseeding is + * disabled and SYN-ACKs will be generated in the same manner as + * SYNs. Strict mode is disabled by default. + * + */ - getmicrotime(&time); - read_random(tcp_rndiss_sbox, sizeof(tcp_rndiss_sbox)); +#define ISN_BYTES_PER_SECOND 1048576 - tcp_rndiss_reseed = time.tv_sec + TCP_RNDISS_OUT; - tcp_rndiss_msb = tcp_rndiss_msb == 0x8000 ? 0 : 0x8000; - tcp_rndiss_cnt = 0; -} +u_char isn_secret[32]; +int isn_last_reseed; +MD5_CTX isn_ctx; tcp_seq -tcp_rndiss_next() +tcp_new_isn(tp) + struct tcpcb *tp; { - u_int16_t tmp; - struct timeval time; - - getmicrotime(&time); - - if (tcp_rndiss_cnt >= TCP_RNDISS_MAX || - time.tv_sec > tcp_rndiss_reseed) - tcp_rndiss_init(); - - read_random(&tmp, sizeof(tmp)); - - /* (tmp & 0x7fff) ensures a 32768 byte gap between ISS */ - return ((tcp_rndiss_encrypt(tcp_rndiss_cnt++) | tcp_rndiss_msb) <<16) | - (tmp & 0x7fff); + u_int32_t md5_buffer[4]; + tcp_seq new_isn; + + /* Use arc4random for SYN-ACKs when not in exact RFC1948 mode. */ + if (((tp->t_state == TCPS_LISTEN) || (tp->t_state == TCPS_TIME_WAIT)) + && tcp_strict_rfc1948 == 0) + return arc4random(); + + /* Seed if this is the first use, reseed if requested. */ + if ((isn_last_reseed == 0) || + ((tcp_strict_rfc1948 == 0) && (tcp_isn_reseed_interval > 0) && + (((u_int)isn_last_reseed + (u_int)tcp_isn_reseed_interval*hz) + < (u_int)ticks))) { + read_random(&isn_secret, sizeof(isn_secret)); + isn_last_reseed = ticks; + } + + /* Compute the md5 hash and return the ISN. */ + MD5Init(&isn_ctx); + MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_fport, sizeof(u_short)); + MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_lport, sizeof(u_short)); +#ifdef INET6 + if ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) { + MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->in6p_faddr, + sizeof(struct in6_addr)); + MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->in6p_laddr, + sizeof(struct in6_addr)); + } else +#endif + { + MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_faddr, + sizeof(struct in_addr)); + MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_laddr, + sizeof(struct in_addr)); + } + MD5Update(&isn_ctx, (u_char *) &isn_secret, sizeof(isn_secret)); + MD5Final((u_char *) &md5_buffer, &isn_ctx); + new_isn = (tcp_seq) md5_buffer[0]; + new_isn += ticks * (ISN_BYTES_PER_SECOND / hz); + return new_isn; } - /* * When a source quench is received, close congestion window * to one segment. We will gradually open it again as we proceed. |
