Module Name:    src
Committed By:   christos
Date:           Mon Jan  2 01:18:42 UTC 2017

Modified Files:
        src/sys/netinet: tcp.h tcp_input.c tcp_output.c tcp_subr.c

Log Message:
Fix TCP signature code:
1. pack options more tightly instead of being generous with no/op
2. put TCP_SIGNATURE option before SACK
3. fix computation of options length, by deferring it
XXX: Really we should move the options setting code in one place instead
of having two copies one for input and one for output.
XXX: tcp_optlen/tcp_hdrsiz need to be fixed; they were wrong before too.


To generate a diff of this commit:
cvs rdiff -u -r1.31 -r1.32 src/sys/netinet/tcp.h
cvs rdiff -u -r1.351 -r1.352 src/sys/netinet/tcp_input.c
cvs rdiff -u -r1.187 -r1.188 src/sys/netinet/tcp_output.c
cvs rdiff -u -r1.268 -r1.269 src/sys/netinet/tcp_subr.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/netinet/tcp.h
diff -u src/sys/netinet/tcp.h:1.31 src/sys/netinet/tcp.h:1.32
--- src/sys/netinet/tcp.h:1.31	Sat Feb 14 07:57:53 2015
+++ src/sys/netinet/tcp.h	Sun Jan  1 20:18:42 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: tcp.h,v 1.31 2015/02/14 12:57:53 he Exp $	*/
+/*	$NetBSD: tcp.h,v 1.32 2017/01/02 01:18:42 christos Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1993
@@ -75,7 +75,11 @@ struct tcphdr {
 } __packed;
 
 #define	TCPOPT_EOL		0
+#define	   TCPOLEN_EOL			1
+#define	TCPOPT_PAD		0
+#define	   TCPOLEN_PAD			1
 #define	TCPOPT_NOP		1
+#define	   TCPOLEN_NOP			1
 #define	TCPOPT_MAXSEG		2
 #define	   TCPOLEN_MAXSEG		4
 #define	TCPOPT_WINDOW		3

Index: src/sys/netinet/tcp_input.c
diff -u src/sys/netinet/tcp_input.c:1.351 src/sys/netinet/tcp_input.c:1.352
--- src/sys/netinet/tcp_input.c:1.351	Sat Dec 31 17:46:46 2016
+++ src/sys/netinet/tcp_input.c	Sun Jan  1 20:18:42 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: tcp_input.c,v 1.351 2016/12/31 22:46:46 christos Exp $	*/
+/*	$NetBSD: tcp_input.c,v 1.352 2017/01/02 01:18:42 christos Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -148,7 +148,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tcp_input.c,v 1.351 2016/12/31 22:46:46 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tcp_input.c,v 1.352 2017/01/02 01:18:42 christos Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -4534,6 +4534,10 @@ syn_cache_respond(struct syn_cache *sc, 
 	struct tcphdr *th;
 	u_int hlen;
 	struct socket *so;
+#ifdef TCP_SIGNATURE
+	struct secasvar *sav = NULL;
+	u_int8_t *sigp = NULL;
+#endif
 
 	ro = &sc->sc_route;
 	switch (sc->sc_src.sa.sa_family) {
@@ -4551,15 +4555,8 @@ syn_cache_respond(struct syn_cache *sc, 
 		return (EAFNOSUPPORT);
 	}
 
-	/* Compute the size of the TCP options. */
-	optlen = 4 + (sc->sc_request_r_scale != 15 ? 4 : 0) +
-	    ((sc->sc_flags & SCF_SACK_PERMIT) ? (TCPOLEN_SACK_PERMITTED + 2) : 0) +
-#ifdef TCP_SIGNATURE
-	    ((sc->sc_flags & SCF_SIGNATURE) ? TCPOLEN_SIGLEN : 0) +
-#endif
-	    ((sc->sc_flags & SCF_TIMESTAMP) ? TCPOLEN_TSTAMP_APPA : 0);
-
-	tlen = hlen + sizeof(struct tcphdr) + optlen;
+	/* worst case scanario, since we don't know the option size yet  */
+	tlen = hlen + sizeof(struct tcphdr) + MAX_TCPOPTLEN;
 
 	/*
 	 * Create the IP+TCP header from scratch.
@@ -4568,8 +4565,9 @@ syn_cache_respond(struct syn_cache *sc, 
 		m_freem(m);
 #ifdef DIAGNOSTIC
 	if (max_linkhdr + tlen > MCLBYTES)
-		return (ENOBUFS);
-#endif
+		return ENOBUFS;
+#endif  
+
 	MGETHDR(m, M_DONTWAIT, MT_DATA);
 	if (m && (max_linkhdr + tlen) > MHLEN) {
 		MCLGET(m, M_DONTWAIT);
@@ -4579,12 +4577,11 @@ syn_cache_respond(struct syn_cache *sc, 
 		}
 	}
 	if (m == NULL)
-		return (ENOBUFS);
+		return ENOBUFS;
 	MCLAIM(m, &tcp_tx_mowner);
 
 	/* Fixup the mbuf. */
 	m->m_data += max_linkhdr;
-	m->m_len = m->m_pkthdr.len = tlen;
 	if (sc->sc_tp) {
 		tp = sc->sc_tp;
 		if (tp->t_inpcb)
@@ -4625,51 +4622,103 @@ syn_cache_respond(struct syn_cache *sc, 
 		break;
 #endif
 	default:
-		th = NULL;
+		return ENOBUFS;
 	}
 
 	th->th_seq = htonl(sc->sc_iss);
 	th->th_ack = htonl(sc->sc_irs + 1);
-	th->th_off = (sizeof(struct tcphdr) + optlen) >> 2;
 	th->th_flags = TH_SYN|TH_ACK;
 	th->th_win = htons(sc->sc_win);
-	/* th_sum already 0 */
-	/* th_urp already 0 */
+	/* th_x2, th_sum, th_urp already 0 from memset */
 
 	/* Tack on the TCP options. */
 	optp = (u_int8_t *)(th + 1);
+	optlen = 0;
 	*optp++ = TCPOPT_MAXSEG;
-	*optp++ = 4;
+	*optp++ = TCPOLEN_MAXSEG;
 	*optp++ = (sc->sc_ourmaxseg >> 8) & 0xff;
 	*optp++ = sc->sc_ourmaxseg & 0xff;
+	optlen += TCPOLEN_MAXSEG;
 
 	if (sc->sc_request_r_scale != 15) {
 		*((u_int32_t *)optp) = htonl(TCPOPT_NOP << 24 |
 		    TCPOPT_WINDOW << 16 | TCPOLEN_WINDOW << 8 |
 		    sc->sc_request_r_scale);
-		optp += 4;
+		optp += TCPOLEN_WINDOW + TCPOLEN_NOP;
+		optlen += TCPOLEN_WINDOW + TCPOLEN_NOP;
+	}
+
+	if (sc->sc_flags & SCF_SACK_PERMIT) {
+		/* Let the peer know that we will SACK. */
+		*optp++ = TCPOPT_SACK_PERMITTED;
+		*optp++ = TCPOLEN_SACK_PERMITTED;
+		optlen += TCPOLEN_SACK_PERMITTED;
 	}
 
 	if (sc->sc_flags & SCF_TIMESTAMP) {
+                while (!optlen || optlen % 4 != 2) {
+                        optlen += TCPOLEN_NOP;
+                        *optp++ = TCPOPT_NOP;
+                }
+		*optp++ = TCPOPT_TIMESTAMP;
+		*optp++ = TCPOLEN_TIMESTAMP;
 		u_int32_t *lp = (u_int32_t *)(optp);
 		/* Form timestamp option as shown in appendix A of RFC 1323. */
-		*lp++ = htonl(TCPOPT_TSTAMP_HDR);
 		*lp++ = htonl(SYN_CACHE_TIMESTAMP(sc));
 		*lp   = htonl(sc->sc_timestamp);
-		optp += TCPOLEN_TSTAMP_APPA;
+		optp += TCPOLEN_TIMESTAMP - 2;
+		optlen += TCPOLEN_TIMESTAMP;
 	}
 
-	if (sc->sc_flags & SCF_SACK_PERMIT) {
-		u_int8_t *p = optp;
+#ifdef TCP_SIGNATURE
+	if (sc->sc_flags & SCF_SIGNATURE) {
 
-		/* Let the peer know that we will SACK. */
-		p[0] = TCPOPT_SACK_PERMITTED;
-		p[1] = 2;
-		p[2] = TCPOPT_NOP;
-		p[3] = TCPOPT_NOP;
-		optp += 4;
+		sav = tcp_signature_getsav(m, th);
+
+		if (sav == NULL) {
+			if (m)
+				m_freem(m);
+			return (EPERM);
+		}
+
+		*optp++ = TCPOPT_SIGNATURE;
+		*optp++ = TCPOLEN_SIGNATURE;
+		sigp = optp;
+		memset(optp, 0, TCP_SIGLEN);
+		optp += TCP_SIGLEN;
+		optlen += TCPOLEN_SIGNATURE;
+
+	}
+#endif
+	/* Terminate and pad TCP options to a 4 byte boundary. */
+	if (optlen % 4) {
+		optlen += TCPOLEN_EOL;
+		*optp++ = TCPOPT_EOL;
+	}
+	/*
+	 * According to RFC 793 (STD0007):
+	 *   "The content of the header beyond the End-of-Option option
+	 *    must be header padding (i.e., zero)."
+	 *   and later: "The padding is composed of zeros."
+	 */
+	while (optlen % 4) {
+		optlen += TCPOLEN_PAD;
+		*optp++ = TCPOPT_PAD;
 	}
 
+	/* compute the actual values now that we've added the options */
+	tlen = hlen + sizeof(struct tcphdr) + optlen;
+	m->m_len = m->m_pkthdr.len = tlen;
+	th->th_off = (sizeof(struct tcphdr) + optlen) >> 2;
+
+#ifdef TCP_SIGNATURE
+	if (sav) {
+		(void)tcp_signature(m, th, hlen, sav, sigp);
+		key_sa_recordxfer(sav, m);
+		KEY_FREESAV(&sav);
+	}
+#endif
+
 	/*
 	 * Send ECN SYN-ACK setup packet.
 	 * Routes can be asymetric, so, even if we receive a packet
@@ -4719,33 +4768,6 @@ syn_cache_respond(struct syn_cache *sc, 
 		TCP_STATINC(TCP_STAT_ECN_ECT);
 	}
 
-#ifdef TCP_SIGNATURE
-	if (sc->sc_flags & SCF_SIGNATURE) {
-		struct secasvar *sav;
-		u_int8_t *sigp;
-
-		sav = tcp_signature_getsav(m, th);
-
-		if (sav == NULL) {
-			if (m)
-				m_freem(m);
-			return (EPERM);
-		}
-
-		*optp++ = TCPOPT_SIGNATURE;
-		*optp++ = TCPOLEN_SIGNATURE;
-		sigp = optp;
-		memset(optp, 0, TCP_SIGLEN);
-		optp += TCP_SIGLEN;
-		*optp++ = TCPOPT_NOP;
-		*optp++ = TCPOPT_EOL;
-
-		(void)tcp_signature(m, th, hlen, sav, sigp);
-
-		key_sa_recordxfer(sav, m);
-		KEY_FREESAV(&sav);
-	}
-#endif
 
 	/* Compute the packet's checksum. */
 	switch (sc->sc_src.sa.sa_family) {

Index: src/sys/netinet/tcp_output.c
diff -u src/sys/netinet/tcp_output.c:1.187 src/sys/netinet/tcp_output.c:1.188
--- src/sys/netinet/tcp_output.c:1.187	Thu Dec  8 00:16:33 2016
+++ src/sys/netinet/tcp_output.c	Sun Jan  1 20:18:42 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: tcp_output.c,v 1.187 2016/12/08 05:16:33 ozaki-r Exp $	*/
+/*	$NetBSD: tcp_output.c,v 1.188 2017/01/02 01:18:42 christos Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -135,7 +135,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tcp_output.c,v 1.187 2016/12/08 05:16:33 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tcp_output.c,v 1.188 2017/01/02 01:18:42 christos Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -566,8 +566,8 @@ tcp_output(struct tcpcb *tp)
 	struct ip6_hdr *ip6;
 #endif
 	struct tcphdr *th;
-	u_char opt[MAX_TCPOPTLEN];
-#define OPT_FITS(more)	((optlen + (more)) < sizeof(opt))
+	u_char opt[MAX_TCPOPTLEN], *optp;
+#define OPT_FITS(more)	((optlen + (more)) <= sizeof(opt))
 	unsigned optlen, hdrlen, packetlen;
 	unsigned int sack_numblks;
 	int idle, sendalot, txsegsize, rxsegsize;
@@ -1116,6 +1116,7 @@ send:
 	 *	max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MCLBYTES
 	 */
 	optlen = 0;
+	optp = opt;
 	switch (af) {
 #ifdef INET
 	case AF_INET:
@@ -1157,31 +1158,28 @@ send:
 			in6_pcbrtentry_unref(synrt, tp->t_in6pcb);
 #endif
 		if ((tp->t_flags & TF_NOOPT) == 0 && OPT_FITS(4)) {
-			opt[0] = TCPOPT_MAXSEG;
-			opt[1] = 4;
-			opt[2] = (tp->t_ourmss >> 8) & 0xff;
-			opt[3] = tp->t_ourmss & 0xff;
-			optlen = 4;
+			*optp++ = TCPOPT_MAXSEG;
+			*optp++ = TCPOLEN_MAXSEG;
+			*optp++ = (tp->t_ourmss >> 8) & 0xff;
+			*optp++ = tp->t_ourmss & 0xff;
+			optlen += TCPOLEN_MAXSEG;
 
 			if ((tp->t_flags & TF_REQ_SCALE) &&
 			    ((flags & TH_ACK) == 0 ||
 			    (tp->t_flags & TF_RCVD_SCALE)) &&
 			    OPT_FITS(4)) {
-				*((u_int32_t *) (opt + optlen)) = htonl(
+				*((uint32_t *)optp) = htonl(
 					TCPOPT_NOP << 24 |
 					TCPOPT_WINDOW << 16 |
 					TCPOLEN_WINDOW << 8 |
 					tp->request_r_scale);
-				optlen += 4;
+				optp += TCPOLEN_WINDOW + TCPOLEN_NOP;
+				optlen += TCPOLEN_WINDOW + TCPOLEN_NOP;
 			}
-			if (tcp_do_sack && OPT_FITS(4)) {
-				u_int8_t *cp = (u_int8_t *)(opt + optlen);
-
-				cp[0] = TCPOPT_SACK_PERMITTED;
-				cp[1] = 2;
-				cp[2] = TCPOPT_NOP;
-				cp[3] = TCPOPT_NOP;
-				optlen += 4;
+			if (tcp_do_sack && OPT_FITS(2)) {
+				*optp++ = TCPOPT_SACK_PERMITTED;
+				*optp++ = TCPOLEN_SACK_PERMITTED;
+				optlen += TCPOLEN_SACK_PERMITTED;
 			}
 		}
 	}
@@ -1194,35 +1192,71 @@ send:
 	if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
 	     (flags & TH_RST) == 0 &&
 	    ((flags & (TH_SYN|TH_ACK)) == TH_SYN ||
-	     (tp->t_flags & TF_RCVD_TSTMP)) && OPT_FITS(TCPOLEN_TSTAMP_APPA)) {
-		u_int32_t *lp = (u_int32_t *)(opt + optlen);
+	     (tp->t_flags & TF_RCVD_TSTMP))) {
+		int alen = 0;
+		while (!optlen || optlen % 4 != 2) {
+			optlen += TCPOLEN_NOP;
+			*optp++ = TCPOPT_NOP;
+			alen++;
+		}
+		if (OPT_FITS(TCPOLEN_TIMESTAMP)) {
+			*optp++ = TCPOPT_TIMESTAMP;
+			*optp++ = TCPOLEN_TIMESTAMP;
+			uint32_t *lp = (uint32_t *)optp;
+			/* Form timestamp option (appendix A of RFC 1323) */
+			*lp++ = htonl(TCP_TIMESTAMP(tp));
+			*lp   = htonl(tp->ts_recent);
+			optp += TCPOLEN_TIMESTAMP - 2;
+			optlen += TCPOLEN_TIMESTAMP;
+
+			/* Set receive buffer autosizing timestamp. */
+			if (tp->rfbuf_ts == 0 &&
+			    (so->so_rcv.sb_flags & SB_AUTOSIZE))
+				tp->rfbuf_ts = TCP_TIMESTAMP(tp);
+		} else {
+			optp -= alen;
+			optlen -= alen;
+		}
+	}
 
-		/* Form timestamp option as shown in appendix A of RFC 1323. */
-		*lp++ = htonl(TCPOPT_TSTAMP_HDR);
-		*lp++ = htonl(TCP_TIMESTAMP(tp));
-		*lp   = htonl(tp->ts_recent);
-		optlen += TCPOLEN_TSTAMP_APPA;
-
-		/* Set receive buffer autosizing timestamp. */
-		if (tp->rfbuf_ts == 0 && (so->so_rcv.sb_flags & SB_AUTOSIZE))
-			tp->rfbuf_ts = TCP_TIMESTAMP(tp);
+
+#ifdef TCP_SIGNATURE
+	if (tp->t_flags & TF_SIGNATURE) {
+		/*
+		 * Initialize TCP-MD5 option (RFC2385)
+		 */
+		if (OPT_FITS(TCPOLEN_SIGNATURE)) {
+			*optp++ = TCPOPT_SIGNATURE;
+			*optp++ = TCPOLEN_SIGNATURE;
+			sigoff = optlen + 2;
+			memset(optp, 0, TCP_SIGLEN);
+			optlen += TCPOLEN_SIGNATURE;
+			optp += TCP_SIGLEN;
+		} else {
+reset:
+			TCP_REASS_UNLOCK(tp);
+			error = ECONNABORTED;
+			goto out;
+		}
 	}
+#endif /* TCP_SIGNATURE */
 
 	/*
 	 * Tack on the SACK block if it is necessary.
 	 */
 	if (sack_numblks) {
-		int sack_len;
-		u_char *bp = (u_char *)(opt + optlen);
-		u_int32_t *lp = (u_int32_t *)(bp + 4);
-		struct ipqent *tiqe;
-
-		sack_len = sack_numblks * 8 + 2;
+		int alen = 0;
+		int sack_len = sack_numblks * 8;
+		while (!optlen || optlen % 4 != 2) {
+			optlen += TCPOLEN_NOP;
+			*optp++ = TCPOPT_NOP;
+			alen++;
+		}
 		if (OPT_FITS(sack_len + 2)) {
-			bp[0] = TCPOPT_NOP;
-			bp[1] = TCPOPT_NOP;
-			bp[2] = TCPOPT_SACK;
-			bp[3] = sack_len;
+			struct ipqent *tiqe;
+			*optp++ = TCPOPT_SACK;
+			*optp++ = sack_len + 2;
+			uint32_t *lp = (uint32_t *)optp;
 			if ((tp->rcv_sack_flags & TCPSACK_HAVED) != 0) {
 				sack_numblks--;
 				*lp++ = htonl(tp->rcv_dsack_block.left);
@@ -1238,35 +1272,35 @@ send:
 				*lp++ = htonl(tiqe->ipqe_seq + tiqe->ipqe_len +
 				    ((tiqe->ipqe_flags & TH_FIN) != 0 ? 1 : 0));
 			}
-			optlen += sack_len + 2;
+			optlen += sack_len;
+			optp += sack_len;
+		} else {
+			optp -= alen;
+			optlen -= alen;
 		}
 	}
-	TCP_REASS_UNLOCK(tp);
 
-#ifdef TCP_SIGNATURE
-	if ((tp->t_flags & TF_SIGNATURE) && OPT_FITS(TCPOLEN_SIGNATURE + 2)) {
-		u_char *bp;
-		/*
-		 * Initialize TCP-MD5 option (RFC2385)
-		 */
-		bp = (u_char *)opt + optlen;
-		*bp++ = TCPOPT_SIGNATURE;
-		*bp++ = TCPOLEN_SIGNATURE;
-		sigoff = optlen + 2;
-		memset(bp, 0, TCP_SIGLEN);
-		bp += TCP_SIGLEN;
-		optlen += TCPOLEN_SIGNATURE;
-		/*
-		 * Terminate options list and maintain 32-bit alignment.
- 		 */
-		*bp++ = TCPOPT_NOP;
-		*bp++ = TCPOPT_EOL;
- 		optlen += 2;
- 	} else if ((tp->t_flags & TF_SIGNATURE) != 0) {
-		error = ECONNABORTED;
-		goto out;
+	/* Terminate and pad TCP options to a 4 byte boundary. */
+	if (optlen % 4) {
+		if (!OPT_FITS(1))
+			goto reset;
+		optlen += TCPOLEN_EOL;
+		*optp++ = TCPOPT_EOL;
 	}
-#endif /* TCP_SIGNATURE */
+	/*
+	 * According to RFC 793 (STD0007):
+	 *   "The content of the header beyond the End-of-Option option
+	 *    must be header padding (i.e., zero)."
+	 *   and later: "The padding is composed of zeros."
+	 */
+	while (optlen % 4) {
+		if (!OPT_FITS(1))
+			goto reset;
+		optlen += TCPOLEN_PAD;
+		*optp++ = TCPOPT_PAD;
+	}
+
+	TCP_REASS_UNLOCK(tp);
 
 	hdrlen += optlen;
 

Index: src/sys/netinet/tcp_subr.c
diff -u src/sys/netinet/tcp_subr.c:1.268 src/sys/netinet/tcp_subr.c:1.269
--- src/sys/netinet/tcp_subr.c:1.268	Thu Dec  8 00:16:33 2016
+++ src/sys/netinet/tcp_subr.c	Sun Jan  1 20:18:42 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: tcp_subr.c,v 1.268 2016/12/08 05:16:33 ozaki-r Exp $	*/
+/*	$NetBSD: tcp_subr.c,v 1.269 2017/01/02 01:18:42 christos Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -91,7 +91,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tcp_subr.c,v 1.268 2016/12/08 05:16:33 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tcp_subr.c,v 1.269 2017/01/02 01:18:42 christos Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -2439,7 +2439,7 @@ tcp_optlen(struct tcpcb *tp)
 
 #ifdef TCP_SIGNATURE
 	if (tp->t_flags & TF_SIGNATURE)
-		optlen += TCPOLEN_SIGNATURE + 2;
+		optlen += TCPOLEN_SIGLEN;
 #endif /* TCP_SIGNATURE */
 
 	return optlen;

Reply via email to