Date: Fri, 7 Oct 2016 10:47:32 +0000 (UTC) From: "Jonathan T. Looney" <jtl@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r306802 - stable/10/sys/netinet Message-ID: <201610071047.u97AlW30080812@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jtl Date: Fri Oct 7 10:47:32 2016 New Revision: 306802 URL: https://svnweb.freebsd.org/changeset/base/306802 Log: MFC r296454: Some cleanup in tcp_respond() in preparation for another change: - Reorder variables by size - Move initializer closer to where it is used - Remove unneeded variable MFC r296455: As reported on the transport@ and current@ mailing lists, the FreeBSD TCP stack is not compliant with RFC 7323, which requires that TCP stacks send a timestamp option on all packets (except, optionally, RSTs) after the session is established. This patch adds that support. It also adds a TCP signature option to the packet, if appropriate. MFC r300764 (by jhb@): Don't reuse the source mbuf in tcp_respond() if it is not writable. Not all mbufs passed up from device drivers are M_WRITABLE(). In particular, the Chelsio T4/T5 driver uses a feature called "buffer packing" to receive multiple frames in a single receive buffer. The mbufs to receive multiple frames in a single receive buffer. The mbufs for these frames all share the same external storage so are treated as read-only by the rest of the stack when multiple frames are in flight. Previously tcp_respond() would blindly overwrite read-only mbufs when INVARIANTS was disabled or panic with an assertion failure if INVARIANTS was enabled. Note that the new case is a bit of a mix of the two other cases in tcp_respond(). The TCP and IP headers must be copied explicitly into the new mbuf instead of being inherited (similar to the m == NULL case), but the addresses and ports must be swapped in the reply (similar to the m != NULL case). Modified: stable/10/sys/netinet/tcp_subr.c Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/netinet/tcp_subr.c ============================================================================== --- stable/10/sys/netinet/tcp_subr.c Fri Oct 7 06:29:24 2016 (r306801) +++ stable/10/sys/netinet/tcp_subr.c Fri Oct 7 10:47:32 2016 (r306802) @@ -556,16 +556,18 @@ void tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m, tcp_seq ack, tcp_seq seq, int flags) { - int tlen; - int win = 0; + struct tcpopt to; + struct inpcb *inp; struct ip *ip; + struct mbuf *optm; struct tcphdr *nth; + u_char *optp; #ifdef INET6 struct ip6_hdr *ip6; int isipv6; #endif /* INET6 */ - int ipflags = 0; - struct inpcb *inp; + int optlen, tlen, win; + bool incl_opts; KASSERT(tp != NULL || m != NULL, ("tcp_respond: tp and m both NULL")); @@ -582,18 +584,21 @@ tcp_respond(struct tcpcb *tp, void *ipge } else inp = NULL; + incl_opts = false; + win = 0; if (tp != NULL) { if (!(flags & TH_RST)) { win = sbspace(&inp->inp_socket->so_rcv); if (win > (long)TCP_MAXWIN << tp->rcv_scale) win = (long)TCP_MAXWIN << tp->rcv_scale; } + if ((tp->t_flags & TF_NOOPT) == 0) + incl_opts = true; } if (m == NULL) { m = m_gethdr(M_NOWAIT, MT_DATA); if (m == NULL) return; - tlen = 0; m->m_data += max_linkhdr; #ifdef INET6 if (isipv6) { @@ -610,17 +615,54 @@ tcp_respond(struct tcpcb *tp, void *ipge } bcopy((caddr_t)th, (caddr_t)nth, sizeof(struct tcphdr)); flags = TH_ACK; + } else if (!M_WRITABLE(m)) { + struct mbuf *n; + + /* Can't reuse 'm', allocate a new mbuf. */ + n = m_gethdr(M_NOWAIT, MT_DATA); + if (n == NULL) { + m_freem(m); + return; + } + + if (!m_dup_pkthdr(n, m, M_NOWAIT)) { + m_freem(m); + m_freem(n); + return; + } + + n->m_data += max_linkhdr; + /* m_len is set later */ +#define xchg(a,b,type) { type t; t=a; a=b; b=t; } +#ifdef INET6 + if (isipv6) { + bcopy((caddr_t)ip6, mtod(n, caddr_t), + sizeof(struct ip6_hdr)); + ip6 = mtod(n, struct ip6_hdr *); + xchg(ip6->ip6_dst, ip6->ip6_src, struct in6_addr); + nth = (struct tcphdr *)(ip6 + 1); + } else +#endif /* INET6 */ + { + bcopy((caddr_t)ip, mtod(n, caddr_t), sizeof(struct ip)); + ip = mtod(n, struct ip *); + xchg(ip->ip_dst.s_addr, ip->ip_src.s_addr, uint32_t); + nth = (struct tcphdr *)(ip + 1); + } + bcopy((caddr_t)th, (caddr_t)nth, sizeof(struct tcphdr)); + xchg(nth->th_dport, nth->th_sport, uint16_t); + th = nth; + m_freem(m); + m = n; } else { /* * reuse the mbuf. - * XXX MRT We inherrit the FIB, which is lucky. + * XXX MRT We inherit the FIB, which is lucky. */ m_freem(m->m_next); m->m_next = NULL; m->m_data = (caddr_t)ipgen; /* m_len is set later */ - tlen = 0; -#define xchg(a,b,type) { type t; t=a; a=b; b=t; } #ifdef INET6 if (isipv6) { xchg(ip6->ip6_dst, ip6->ip6_src, struct in6_addr); @@ -643,12 +685,64 @@ tcp_respond(struct tcpcb *tp, void *ipge xchg(nth->th_dport, nth->th_sport, uint16_t); #undef xchg } + tlen = 0; +#ifdef INET6 + if (isipv6) + tlen = sizeof (struct ip6_hdr) + sizeof (struct tcphdr); +#endif +#if defined(INET) && defined(INET6) + else +#endif +#ifdef INET + tlen = sizeof (struct tcpiphdr); +#endif +#ifdef INVARIANTS + m->m_len = 0; + KASSERT(M_TRAILINGSPACE(m) >= tlen, + ("Not enough trailing space for message (m=%p, need=%d, have=%ld)", + m, tlen, (long)M_TRAILINGSPACE(m))); +#endif + m->m_len = tlen; + to.to_flags = 0; + if (incl_opts) { + /* Make sure we have room. */ + if (M_TRAILINGSPACE(m) < TCP_MAXOLEN) { + m->m_next = m_get(M_NOWAIT, MT_DATA); + if (m->m_next) { + optp = mtod(m->m_next, u_char *); + optm = m->m_next; + } else + incl_opts = false; + } else { + optp = (u_char *) (nth + 1); + optm = m; + } + } + if (incl_opts) { + /* Timestamps. */ + if (tp->t_flags & TF_RCVD_TSTMP) { + to.to_tsval = tcp_ts_getticks() + tp->ts_offset; + to.to_tsecr = tp->ts_recent; + to.to_flags |= TOF_TS; + } +#ifdef TCP_SIGNATURE + /* TCP-MD5 (RFC2385). */ + if (tp->t_flags & TF_SIGNATURE) + to.to_flags |= TOF_SIGNATURE; +#endif + + /* Add the options. */ + tlen += optlen = tcp_addoptions(&to, optp); + + /* Update m_len in the correct mbuf. */ + optm->m_len += optlen; + } else + optlen = 0; #ifdef INET6 if (isipv6) { ip6->ip6_flow = 0; ip6->ip6_vfc = IPV6_VERSION; ip6->ip6_nxt = IPPROTO_TCP; - tlen += sizeof (struct ip6_hdr) + sizeof (struct tcphdr); ip6->ip6_plen = htons(tlen - sizeof(*ip6)); } #endif @@ -657,14 +751,12 @@ tcp_respond(struct tcpcb *tp, void *ipge #endif #ifdef INET { - tlen += sizeof (struct tcpiphdr); ip->ip_len = htons(tlen); ip->ip_ttl = V_ip_defttl; if (V_path_mtu_discovery) ip->ip_off |= htons(IP_DF); } #endif - m->m_len = tlen; m->m_pkthdr.len = tlen; m->m_pkthdr.rcvif = NULL; #ifdef MAC @@ -686,7 +778,7 @@ tcp_respond(struct tcpcb *tp, void *ipge nth->th_seq = htonl(seq); nth->th_ack = htonl(ack); nth->th_x2 = 0; - nth->th_off = sizeof (struct tcphdr) >> 2; + nth->th_off = (sizeof (struct tcphdr) + optlen) >> 2; nth->th_flags = flags; if (tp != NULL) nth->th_win = htons((u_short) (win >> tp->rcv_scale)); @@ -694,6 +786,13 @@ tcp_respond(struct tcpcb *tp, void *ipge nth->th_win = htons((u_short)win); nth->th_urp = 0; +#ifdef TCP_SIGNATURE + if (to.to_flags & TOF_SIGNATURE) { + tcp_signature_compute(m, 0, 0, optlen, to.to_signature, + IPSEC_DIR_OUTBOUND); + } +#endif + m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum); #ifdef INET6 if (isipv6) { @@ -725,13 +824,13 @@ tcp_respond(struct tcpcb *tp, void *ipge TCP_PROBE5(send, NULL, tp, mtod(m, const char *), tp, nth); #ifdef INET6 if (isipv6) - (void) ip6_output(m, NULL, NULL, ipflags, NULL, NULL, inp); + (void) ip6_output(m, NULL, NULL, 0, NULL, NULL, inp); #endif /* INET6 */ #if defined(INET) && defined(INET6) else #endif #ifdef INET - (void) ip_output(m, NULL, NULL, ipflags, NULL, inp); + (void) ip_output(m, NULL, NULL, 0, NULL, inp); #endif }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201610071047.u97AlW30080812>