Correct mbuf packet header propagation. Previously, packet headers

were sometimes propagated using M_COPY_PKTHDR which actually did
something between a "move" and a  "copy" operation.  This is replaced
by M_MOVE_PKTHDR (which copies the pkthdr contents and "removes" it
from the source mbuf) and m_dup_pkthdr which copies the packet
header contents including any m_tag chain.  This corrects numerous
problems whereby mbuf tags could be lost during packet manipulations.

These changes also introduce arguments to m_tag_copy and m_tag_copy_chain
to specify if the tag copy work should potentially block.  This
introduces an incompatibility with openbsd which we may want to revisit.

Note that move/dup of packet headers does not handle target mbufs
that have a cluster bound to them.  We may want to support this;
for now we watch for it with an assert.

Finally, M_COPYFLAGS was updated to include M_FIRSTFRAG|M_LASTFRAG.

Supported by:	Vernier Networks
Reviewed by:	Robert Watson <rwatson@FreeBSD.org>
This commit is contained in:
Sam Leffler 2002-12-30 20:22:40 +00:00
parent 28788ed563
commit 9967cafc49
18 changed files with 127 additions and 68 deletions

View File

@ -1326,7 +1326,7 @@ awi_fix_rxhdr(sc, m0)
m_freem(m0);
return NULL;
}
M_COPY_PKTHDR(n, m0);
M_MOVE_PKTHDR(n, m0);
n->m_len = MHLEN;
} else {
MGET(n, M_DONTWAIT, MT_DATA);

View File

@ -323,7 +323,7 @@ awi_wep_encrypt(sc, m0, txflag)
n0 = n;
if (n == NULL)
goto fail;
M_COPY_PKTHDR(n, m);
M_MOVE_PKTHDR(n, m);
len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN;
if (txflag) {
n->m_pkthdr.len += len;

View File

@ -1843,7 +1843,7 @@ STATIC int en_makeexclusive(sc, mm, prev)
return(0);
}
if (m->m_flags & M_PKTHDR)
M_COPY_PKTHDR(new, m);
M_MOVE_PKTHDR(new, m);
MCLGET(new, M_DONTWAIT);
if ((new->m_flags & M_EXT) == 0) {
m_free(new);

View File

@ -1668,6 +1668,10 @@ hifn_crypto(
if (cmd->src_m->m_flags & M_PKTHDR) {
len = MHLEN;
MGETHDR(m0, M_DONTWAIT, MT_DATA);
if (m0 && !m_dup_pkthdr(m0, cmd->src_m, M_DONTWAIT)) {
m_free(m0);
m0 = NULL;
}
} else {
len = MLEN;
MGET(m0, M_DONTWAIT, MT_DATA);
@ -1677,9 +1681,6 @@ hifn_crypto(
err = dma->cmdu ? ERESTART : ENOMEM;
goto err_srcmap;
}
if (len == MHLEN) {
M_COPY_PKTHDR(m0, cmd->src_m);
}
if (totlen >= MINCLSIZE) {
MCLGET(m0, M_DONTWAIT);
if ((m0->m_flags & M_EXT) == 0) {

View File

@ -1269,6 +1269,10 @@ ubsec_process(void *arg, struct cryptop *crp, int hint)
if (q->q_src_m->m_flags & M_PKTHDR) {
len = MHLEN;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m && !m_dup_pkthdr(m, q->q_src_m, M_DONTWAIT)) {
m_free(m);
m = NULL;
}
} else {
len = MLEN;
MGET(m, M_DONTWAIT, MT_DATA);
@ -1278,8 +1282,6 @@ ubsec_process(void *arg, struct cryptop *crp, int hint)
err = sc->sc_nqueue ? ERESTART : ENOMEM;
goto errout;
}
if (len == MHLEN)
M_COPY_PKTHDR(m, q->q_src_m);
if (totlen >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {

View File

@ -66,30 +66,70 @@ SYSCTL_INT(_kern_ipc, KIPC_MAX_DATALEN, max_datalen, CTLFLAG_RW,
&max_datalen, 0, "");
/*
* Copy mbuf pkthdr from "from" to "to".
* "Move" mbuf pkthdr from "from" to "to".
* "from" must have M_PKTHDR set, and "to" must be empty.
* aux pointer will be moved to "to".
*/
void
m_copy_pkthdr(struct mbuf *to, struct mbuf *from)
m_move_pkthdr(struct mbuf *to, struct mbuf *from)
{
#if 0
/* see below for why these are not enabled */
KASSERT(to->m_flags & M_PKTHDR,
("m_copy_pkthdr() called on non-header"));
("m_move_pkthdr: called on non-header"));
KASSERT(SLIST_EMPTY(&to->m_pkthdr.tags),
("m_move_pkthdr: to has tags"));
#endif
KASSERT((to->m_flags & M_EXT) == 0, ("m_move_pkthdr: to has cluster"));
#ifdef MAC
if (to->m_flags & M_PKTHDR)
mac_destroy_mbuf(to);
#endif
to->m_data = to->m_pktdat;
to->m_flags = from->m_flags & M_COPYFLAGS;
to->m_data = to->m_pktdat;
to->m_pkthdr = from->m_pkthdr; /* especially tags */
#ifdef MAC
mac_init_mbuf(to, 1); /* XXXMAC no way to fail */
mac_create_mbuf_from_mbuf(from, to);
#endif
SLIST_INIT(&from->m_pkthdr.tags); /* purge tags from src */
from->m_flags &= ~M_PKTHDR;
}
/*
* Duplicate "from"'s mbuf pkthdr in "to".
* "from" must have M_PKTHDR set, and "to" must be empty.
* In particular, this does a deep copy of the packet tags.
*/
int
m_dup_pkthdr(struct mbuf *to, struct mbuf *from, int how)
{
#if 0
/*
* The mbuf allocator only initializes the pkthdr
* when the mbuf is allocated with MGETHDR. Many users
* (e.g. m_copy*, m_prepend) use MGET and then
* smash the pkthdr as needed causing these
* assertions to trip. For now just disable them.
*/
KASSERT(to->m_flags & M_PKTHDR, ("m_dup_pkthdr: called on non-header"));
KASSERT(SLIST_EMPTY(&to->m_pkthdr.tags), ("m_dup_pkthdr: to has tags"));
#endif
KASSERT((to->m_flags & M_EXT) == 0, ("m_dup_pkthdr: to has cluster"));
#ifdef MAC
if (to->m_flags & M_PKTHDR)
mac_destroy_mbuf(to);
#endif
to->m_flags = from->m_flags & M_COPYFLAGS;
to->m_data = to->m_pktdat;
to->m_pkthdr = from->m_pkthdr;
#ifdef MAC
mac_init_mbuf(to, 1); /* XXXMAC no way to fail */
mac_create_mbuf_from_mbuf(from, to);
#endif
SLIST_INIT(&from->m_pkthdr.tags);
SLIST_INIT(&to->m_pkthdr.tags);
return (m_tag_copy_chain(to, from, how));
}
/*
@ -108,11 +148,10 @@ m_prepend(struct mbuf *m, int len, int how)
return (NULL);
}
if (m->m_flags & M_PKTHDR) {
M_COPY_PKTHDR(mn, m);
M_MOVE_PKTHDR(mn, m);
#ifdef MAC
mac_destroy_mbuf(m);
#endif
m->m_flags &= ~M_PKTHDR;
}
mn->m_next = m;
m = mn;
@ -161,7 +200,8 @@ m_copym(struct mbuf *m, int off0, int len, int wait)
if (n == NULL)
goto nospace;
if (copyhdr) {
M_COPY_PKTHDR(n, m);
if (!m_dup_pkthdr(n, m, wait))
goto nospace;
if (len == M_COPYALL)
n->m_pkthdr.len -= off0;
else
@ -212,7 +252,8 @@ m_copypacket(struct mbuf *m, int how)
if (n == NULL)
goto nospace;
M_COPY_PKTHDR(n, m);
if (!m_dup_pkthdr(n, m, how))
goto nospace;
n->m_len = m->m_len;
if (m->m_flags & M_EXT) {
n->m_data = m->m_data;
@ -309,7 +350,8 @@ m_dup(struct mbuf *m, int how)
if (n == NULL)
goto nospace;
if (top == NULL) { /* first one, must be PKTHDR */
M_COPY_PKTHDR(n, m);
if (!m_dup_pkthdr(n, m, how))
goto nospace;
nsize = MHLEN;
} else /* not the first one */
nsize = MLEN;
@ -484,10 +526,8 @@ m_pullup(struct mbuf *n, int len)
if (m == NULL)
goto bad;
m->m_len = 0;
if (n->m_flags & M_PKTHDR) {
M_COPY_PKTHDR(m, n);
n->m_flags &= ~M_PKTHDR;
}
if (n->m_flags & M_PKTHDR)
M_MOVE_PKTHDR(m, n);
}
space = &m->m_dat[MLEN] - (m->m_data + m->m_len);
do {

View File

@ -298,8 +298,10 @@ m_dup1(struct mbuf *m, int off, int len, int wait)
if (!n)
return NULL;
if (copyhdr)
M_COPY_PKTHDR(n, m);
if (copyhdr && !m_dup_pkthdr(n, m, wait)) {
m_free(n);
return NULL;
}
m_copydata(m, off, len, mtod(n, caddr_t));
return n;
}
@ -392,12 +394,12 @@ m_tag_locate(struct mbuf *m, u_int32_t cookie, int type, struct m_tag *t)
/* Copy a single tag. */
struct m_tag *
m_tag_copy(struct m_tag *t)
m_tag_copy(struct m_tag *t, int how)
{
struct m_tag *p;
KASSERT(t, ("m_tag_copy: null tag"));
p = m_tag_alloc(t->m_tag_cookie, t->m_tag_id, t->m_tag_len, M_NOWAIT);
p = m_tag_alloc(t->m_tag_cookie, t->m_tag_id, t->m_tag_len, how);
if (p == NULL)
return (NULL);
bcopy(t + 1, p + 1, t->m_tag_len); /* Copy the data */
@ -411,7 +413,7 @@ m_tag_copy(struct m_tag *t)
* destination mbuf.
*/
int
m_tag_copy_chain(struct mbuf *to, struct mbuf *from)
m_tag_copy_chain(struct mbuf *to, struct mbuf *from, int how)
{
struct m_tag *p, *t, *tprev = NULL;
@ -419,7 +421,7 @@ m_tag_copy_chain(struct mbuf *to, struct mbuf *from)
("m_tag_copy_chain: null argument, to %p from %p", to, from));
m_tag_delete_chain(to, NULL);
SLIST_FOREACH(p, &from->m_pkthdr.tags, m_tag_link) {
t = m_tag_copy(p);
t = m_tag_copy(p, how);
if (t == NULL) {
m_tag_delete_chain(to, NULL);
return 0;

View File

@ -214,19 +214,11 @@ looutput(ifp, m, dst, rt)
if (m && m->m_next != NULL && m->m_pkthdr.len < MCLBYTES) {
struct mbuf *n;
/* XXX MT_HEADER should be m->m_type */
MGETHDR(n, M_DONTWAIT, MT_HEADER);
if (!n)
goto contiguousfail;
MCLGET(n, M_DONTWAIT);
if (! (n->m_flags & M_EXT)) {
m_freem(n);
goto contiguousfail;
}
m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t));
n->m_pkthdr = m->m_pkthdr;
n->m_len = m->m_pkthdr.len;
SLIST_INIT(&m->m_pkthdr.tags);
M_MOVE_PKTHDR(n, m);
#ifdef MAC
/*
* XXXMAC: Once we put labels in tags and proper
@ -235,6 +227,14 @@ looutput(ifp, m, dst, rt)
*/
m->m_pkthdr.label.l_flags &= ~MAC_FLAG_INITIALIZED;
#endif
MCLGET(n, M_DONTWAIT);
if (! (n->m_flags & M_EXT)) {
m_freem(n);
goto contiguousfail;
}
m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t));
n->m_len = m->m_pkthdr.len;
m_freem(m);
m = n;
}

View File

@ -178,8 +178,7 @@ typedef struct mbuf KBuffer;
}
#define KB_LINKHEAD(new, head) { \
if ((head) && KB_ISPKT(new) && KB_ISPKT(head)) {\
M_COPY_PKTHDR((new), (head)); \
(head)->m_flags &= ~M_PKTHDR; \
M_MOVE_PKTHDR((new), (head)); \
} \
(new)->m_next = (head); \
}

View File

@ -1763,8 +1763,17 @@ ip_forward(struct mbuf *m, int srcrt, struct sockaddr_in *next_hop)
* data in a cluster may change before we reach icmp_error().
*/
MGET(mcopy, M_DONTWAIT, m->m_type);
if (mcopy != NULL && !m_dup_pkthdr(mcopy, m, M_DONTWAIT)) {
/*
* It's probably ok if the pkthdr dup fails (because
* the deep copy of the tag chain failed), but for now
* be conservative and just discard the copy since
* code below may some day want the tags.
*/
m_free(mcopy);
mcopy = NULL;
}
if (mcopy != NULL) {
M_COPY_PKTHDR(mcopy, m);
mcopy->m_len = imin((ip->ip_hl << 2) + 8,
(int)ip->ip_len);
m_copydata(m, 0, mcopy->m_len, mtod(mcopy, caddr_t));

View File

@ -813,7 +813,7 @@ noreplaycheck:
MGETHDR(n, M_DONTWAIT, MT_HEADER);
maxlen = MHLEN;
if (n)
M_COPY_PKTHDR(n, m);
M_MOVE_PKTHDR(n, m);
if (n && m->m_pkthdr.len > maxlen) {
MCLGET(n, M_DONTWAIT);
maxlen = MCLBYTES;
@ -839,7 +839,6 @@ noreplaycheck:
n->m_pkthdr.len = m->m_pkthdr.len;
n->m_next = m;
m_adj(m, maxlen);
m->m_flags &= ~M_PKTHDR;
}
m = n;
}

View File

@ -574,7 +574,7 @@ icmp6_input(mp, offp, proto)
m_freem(n0);
break;
}
M_COPY_PKTHDR(n, n0);
M_MOVE_PKTHDR(n, n0);
/*
* Copy IPv6 and ICMPv6 only.
*/
@ -592,7 +592,6 @@ icmp6_input(mp, offp, proto)
m_adj(n0, off + sizeof(struct icmp6_hdr));
n->m_pkthdr.len += n0->m_pkthdr.len;
n->m_next = n0;
n0->m_flags &= ~M_PKTHDR;
} else {
nip6 = mtod(n, struct ip6_hdr *);
nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off);
@ -690,6 +689,17 @@ icmp6_input(mp, offp, proto)
n = NULL;
}
}
if (!m_dup_pkthdr(n, m, M_DONTWAIT)) {
/*
* Previous code did a blind M_COPY_PKTHDR
* and said "just for rcvif". If true, then
* we could tolerate the dup failing (due to
* the deep copy of the tag chain). For now
* be conservative and just fail.
*/
m_free(n);
n = NULL;
}
if (n == NULL) {
/* Give up remote */
break;
@ -710,7 +720,6 @@ icmp6_input(mp, offp, proto)
bzero(p, 4);
bcopy(hostname, p + 4, maxhlen); /* meaningless TTL */
noff = sizeof(struct ip6_hdr);
M_COPY_PKTHDR(n, m); /* just for rcvif */
n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
sizeof(struct icmp6_hdr) + 4 + maxhlen;
nicmp6->icmp6_type = ICMP6_WRUREPLY;
@ -1387,7 +1396,7 @@ ni6_input(m, off)
m_freem(m);
return(NULL);
}
M_COPY_PKTHDR(n, m); /* just for recvif */
M_MOVE_PKTHDR(n, m); /* just for recvif */
if (replylen > MHLEN) {
if (replylen > MCLBYTES) {
/*

View File

@ -319,7 +319,7 @@ ip6_input(m)
MGETHDR(n, M_DONTWAIT, MT_HEADER);
if (n)
M_COPY_PKTHDR(n, m);
M_MOVE_PKTHDR(n, m);
if (n && m->m_pkthdr.len > MHLEN) {
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {

View File

@ -2575,7 +2575,7 @@ ip6_splithdr(m, exthdrs)
m_freem(m);
return ENOBUFS;
}
M_COPY_PKTHDR(mh, m);
M_MOVE_PKTHDR(mh, m);
MH_ALIGN(mh, sizeof(*ip6));
m->m_flags &= ~M_PKTHDR;
m->m_len -= sizeof(*ip6);

View File

@ -3124,7 +3124,7 @@ ipsec4_splithdr(m)
m_freem(m);
return NULL;
}
M_COPY_PKTHDR(mh, m);
M_MOVE_PKTHDR(mh, m);
MH_ALIGN(mh, hlen);
m->m_flags &= ~M_PKTHDR;
m->m_len -= hlen;
@ -3161,7 +3161,7 @@ ipsec6_splithdr(m)
m_freem(m);
return NULL;
}
M_COPY_PKTHDR(mh, m);
M_MOVE_PKTHDR(mh, m);
MH_ALIGN(mh, hlen);
m->m_flags &= ~M_PKTHDR;
m->m_len -= hlen;
@ -3371,16 +3371,10 @@ ipsec_copypkt(m)
MGETHDR(mnew, M_DONTWAIT, MT_HEADER);
if (mnew == NULL)
goto fail;
mnew->m_pkthdr = n->m_pkthdr;
#if 0
if (n->m_pkthdr.aux) {
mnew->m_pkthdr.aux =
m_copym(n->m_pkthdr.aux,
0, M_COPYALL, M_DONTWAIT);
if (!m_dup_pkthdr(mnew, n, M_DONTWAIT)) {
m_free(mnew);
goto fail;
}
#endif
M_COPY_PKTHDR(mnew, n);
mnew->m_flags = n->m_flags & M_COPYFLAGS;
}
else {
MGET(mnew, M_DONTWAIT, MT_DATA);

View File

@ -101,7 +101,7 @@ m_clone(struct mbuf *m0)
m_freem(m0);
return (NULL);
}
M_COPY_PKTHDR(n, m);
M_MOVE_PKTHDR(n, m);
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);

View File

@ -474,7 +474,7 @@ ipsec6_splithdr(struct mbuf *m)
m_freem(m);
return NULL;
}
M_COPY_PKTHDR(mh, m);
M_MOVE_PKTHDR(mh, m);
MH_ALIGN(mh, hlen);
m->m_len -= hlen;
m->m_data += hlen;

View File

@ -177,8 +177,9 @@ struct mbuf {
/*
* Flags copied when copying m_pkthdr.
*/
#define M_COPYFLAGS (M_PKTHDR|M_EOR|M_PROTO1|M_PROTO1|M_PROTO2|M_PROTO3 | \
M_PROTO4|M_PROTO5|M_BCAST|M_MCAST|M_FRAG|M_RDONLY)
#define M_COPYFLAGS (M_PKTHDR|M_EOR|M_RDONLY|M_PROTO1|M_PROTO1|M_PROTO2|\
M_PROTO3|M_PROTO4|M_PROTO5|M_BCAST|M_MCAST|\
M_FRAG|M_FIRSTFRAG|M_LASTFRAG)
/*
* Flags indicating hw checksum support and sw checksum requirements.
@ -294,7 +295,8 @@ struct mbstat {
* mbuf, cluster, and external object allocation macros
* (for compatibility purposes).
*/
#define M_COPY_PKTHDR(to, from) m_copy_pkthdr((to), (from))
/* NB: M_COPY_PKTHDR is deprecated, use M_MOVE_PKTHDR or m_dup_pktdr */
#define M_MOVE_PKTHDR(to, from) m_move_pkthdr((to), (from))
#define m_getclr(how, type) m_get_clrd((how), (type))
#define MGET(m, how, type) ((m) = m_get((how), (type)))
#define MGETHDR(m, how, type) ((m) = m_gethdr((how), (type)))
@ -428,6 +430,7 @@ void m_copy_pkthdr(struct mbuf *, struct mbuf *);
struct mbuf *m_devget(char *, int, int, struct ifnet *,
void (*)(char *, caddr_t, u_int));
struct mbuf *m_dup(struct mbuf *, int);
int m_dup_pkthdr(struct mbuf *, struct mbuf *, int);
u_int m_fixhdr(struct mbuf *);
struct mbuf *m_free(struct mbuf *);
void m_freem(struct mbuf *);
@ -438,6 +441,7 @@ struct mbuf *m_gethdr(int, short);
struct mbuf *m_gethdr_clrd(int, short);
struct mbuf *m_getm(struct mbuf *, int, int, short);
u_int m_length(struct mbuf *, struct mbuf **);
void m_move_pkthdr(struct mbuf *, struct mbuf *);
struct mbuf *m_prepend(struct mbuf *, int, int);
void m_print(const struct mbuf *);
struct mbuf *m_pulldown(struct mbuf *, int, int, int *);
@ -528,8 +532,8 @@ void m_tag_unlink(struct mbuf *, struct m_tag *);
void m_tag_delete(struct mbuf *, struct m_tag *);
void m_tag_delete_chain(struct mbuf *, struct m_tag *);
struct m_tag *m_tag_locate(struct mbuf *, u_int32_t, int, struct m_tag *);
struct m_tag *m_tag_copy(struct m_tag *);
int m_tag_copy_chain(struct mbuf *, struct mbuf *);
struct m_tag *m_tag_copy(struct m_tag *, int);
int m_tag_copy_chain(struct mbuf *, struct mbuf *, int);
void m_tag_init(struct mbuf *);
struct m_tag *m_tag_first(struct mbuf *);
struct m_tag *m_tag_next(struct mbuf *, struct m_tag *);