Index: sys/net/if_bridge.c =================================================================== --- sys/net/if_bridge.c +++ sys/net/if_bridge.c @@ -333,7 +333,7 @@ #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); @@ -1917,6 +1917,7 @@ 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); if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); @@ -3234,10 +3235,12 @@ break; /* check if we need to fragment the packet */ + /* bridge_fragment generates a mbuf chain of packets */ + /* that already include eth headers */ if (V_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); } @@ -3476,56 +3479,82 @@ /* * 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, *m0 = 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 packets + */ + + for (mcur = m; mcur; mcur = mcur->m_nextpkt) { + m0 = mcur; + if (snap) { + M_PREPEND(mcur, sizeof(struct llc), M_NOWAIT); + + if (mcur == NULL) { error = ENOBUFS; - continue; + 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; + goto dropit; + } + bcopy(eh, mtod(mcur, caddr_t), ETHER_HDR_LEN); + /* + * if M_PREPEND had to create a new mbuf, we need to: + * 1. update previous mbuf packet to use this new one + * 2. moving m_nextpkt from the old mbuf to the new one + * But we can have two new mbufs prepending: LLC and Ethernet HDR! + */ + if ( m0 != mcur ) { + if ( mcur->m_next->m_next && mcur->m_next->m_next->m_nextpkt ) { + /* If two new mbufs insered (LLC and Ethernet header) */ + mcur->m_nextpkt = mcur->m_next->m_next->m_nextpkt; + mcur->m_next->m_next->m_nextpkt = NULL; + } else { + /* If only one mbuf insered (Ethernet header) */ + mcur->m_nextpkt = mcur->m_next->m_nextpkt; + mcur->m_next->m_nextpkt = NULL; + } + if (mprev != NULL) { /* It's not the first packet in the chain*/ + 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); }