summaryrefslogtreecommitdiffstats
path: root/sys/netinet/tcp_subr.c
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2016-05-26 18:35:37 +0000
committerjhb <jhb@FreeBSD.org>2016-05-26 18:35:37 +0000
commit7a26d36370351fa81dba61972dc9e78b3090d5db (patch)
tree02830d3adaf4488bdd45c3ed8c2693803751218c /sys/netinet/tcp_subr.c
parent7bab6f903636c3b21cdca0d91c8eef7833fe26fb (diff)
downloadFreeBSD-src-7a26d36370351fa81dba61972dc9e78b3090d5db.zip
FreeBSD-src-7a26d36370351fa81dba61972dc9e78b3090d5db.tar.gz
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
Diffstat (limited to 'sys/netinet/tcp_subr.c')
-rw-r--r--sys/netinet/tcp_subr.c42
1 files changed, 40 insertions, 2 deletions
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);
OpenPOWER on IntegriCloud