From 7a26d36370351fa81dba61972dc9e78b3090d5db Mon Sep 17 00:00:00 2001 From: jhb Date: Thu, 26 May 2016 18:35:37 +0000 Subject: 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 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). Reviewed by: glebius --- sys/netinet/tcp_subr.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) (limited to 'sys/netinet/tcp_subr.c') diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 03526df..75dd3d7 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -934,16 +934,54 @@ tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m, } 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 */ -#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); -- cgit v1.1