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
This commit is contained in:
parent
28a235e1a7
commit
052a5418e8
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user