diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net/if_bridge.c | 86 |
1 files changed, 55 insertions, 31 deletions
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 14d9967..6fae5ef 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -331,7 +331,7 @@ static int bridge_ip_checkbasic(struct mbuf **mp); #ifdef INET6 static int bridge_ip6_checkbasic(struct mbuf **mp); #endif /* INET6 */ -static int bridge_fragment(struct ifnet *, struct mbuf *, +static int bridge_fragment(struct ifnet *, struct mbuf **mp, struct ether_header *, int, struct llc *); static void bridge_linkstate(struct ifnet *ifp); static void bridge_linkcheck(struct bridge_softc *sc); @@ -1869,6 +1869,7 @@ bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m) m->m_flags &= ~M_VLANTAG; } + M_ASSERTPKTHDR(m); /* We shouldn't transmit mbuf without pkthdr */ if ((err = dst_ifp->if_transmit(dst_ifp, m))) { m_freem(m0); sc->sc_ifp->if_oerrors++; @@ -3225,10 +3226,12 @@ bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir) break; /* check if we need to fragment the packet */ + /* bridge_fragment generates a mbuf chain of packets */ + /* that already include eth headers */ if (pfil_member && ifp != NULL && dir == PFIL_OUT) { i = (*mp)->m_pkthdr.len; if (i > ifp->if_mtu) { - error = bridge_fragment(ifp, *mp, &eh2, snap, + error = bridge_fragment(ifp, mp, &eh2, snap, &llc1); return (error); } @@ -3467,56 +3470,77 @@ bad: /* * bridge_fragment: * - * Return a fragmented mbuf chain. + * Fragment mbuf chain in multiple packets and prepend ethernet header. */ static int -bridge_fragment(struct ifnet *ifp, struct mbuf *m, struct ether_header *eh, +bridge_fragment(struct ifnet *ifp, struct mbuf **mp, struct ether_header *eh, int snap, struct llc *llc) { - struct mbuf *m0; + struct mbuf *m = *mp, *nextpkt = NULL, *mprev = NULL, *mcur = NULL; struct ip *ip; int error = -1; if (m->m_len < sizeof(struct ip) && (m = m_pullup(m, sizeof(struct ip))) == NULL) - goto out; + goto dropit; ip = mtod(m, struct ip *); m->m_pkthdr.csum_flags |= CSUM_IP; error = ip_fragment(ip, &m, ifp->if_mtu, ifp->if_hwassist); if (error) - goto out; + goto dropit; - /* walk the chain and re-add the Ethernet header */ - for (m0 = m; m0; m0 = m0->m_nextpkt) { - if (error == 0) { - if (snap) { - M_PREPEND(m0, sizeof(struct llc), M_NOWAIT); - if (m0 == NULL) { - error = ENOBUFS; - continue; - } - bcopy(llc, mtod(m0, caddr_t), - sizeof(struct llc)); - } - M_PREPEND(m0, ETHER_HDR_LEN, M_NOWAIT); - if (m0 == NULL) { + /* + * Walk the chain and re-add the Ethernet header for + * each mbuf packet. + */ + for (mcur = m; mcur; mcur = mcur->m_nextpkt) { + nextpkt = mcur->m_nextpkt; + mcur->m_nextpkt = NULL; + if (snap) { + M_PREPEND(mcur, sizeof(struct llc), M_NOWAIT); + if (mcur == NULL) { error = ENOBUFS; - continue; + if (mprev != NULL) + mprev->m_nextpkt = nextpkt; + goto dropit; } - bcopy(eh, mtod(m0, caddr_t), ETHER_HDR_LEN); - } else - m_freem(m); - } + bcopy(llc, mtod(mcur, caddr_t),sizeof(struct llc)); + } - if (error == 0) - KMOD_IPSTAT_INC(ips_fragmented); + M_PREPEND(mcur, ETHER_HDR_LEN, M_NOWAIT); + if (mcur == NULL) { + error = ENOBUFS; + if (mprev != NULL) + mprev->m_nextpkt = nextpkt; + goto dropit; + } + bcopy(eh, mtod(mcur, caddr_t), ETHER_HDR_LEN); + /* + * The previous two M_PREPEND could have inserted one or two + * mbufs in front so we have to update the previous packet's + * m_nextpkt. + */ + mcur->m_nextpkt = nextpkt; + if (mprev != NULL) + mprev->m_nextpkt = mcur; + else { + /* The first mbuf in the original chain needs to be + * updated. */ + *mp = mcur; + } + mprev = mcur; + } + + KMOD_IPSTAT_INC(ips_fragmented); return (error); -out: - if (m != NULL) - m_freem(m); +dropit: + for (mcur = *mp; mcur; mcur = m) { /* droping the full packet chain */ + m = mcur->m_nextpkt; + m_freem(mcur); + } return (error); } |