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;