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;