+
+/*
+ * 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.
+ *
+ */
+
+#define ISN_BYTES_PER_SECOND 1048576
+
+u_char isn_secret[32];
+int isn_last_reseed;
+MD5_CTX isn_ctx;
+
+tcp_seq
+tcp_new_isn(tp)
+ struct tcpcb *tp;
+{
+ u_int32_t md5_buffer[4];
+ tcp_seq new_isn;
+ struct timeval time;
+
+ /* 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)
+#ifdef __APPLE__
+ return random();
+#else
+ return arc4random();
+#endif
+
+ /* 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)time.tv_sec))) {
+#ifdef __APPLE__
+ read_random(&isn_secret, sizeof(isn_secret));
+#else
+ read_random_unlimited(&isn_secret, sizeof(isn_secret));
+#endif
+ isn_last_reseed = time.tv_sec;
+ }
+
+ /* 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));
+#if 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 += time.tv_sec * (ISN_BYTES_PER_SECOND / hz);
+ return new_isn;
+}
+