summaryrefslogtreecommitdiffstats
path: root/sys/net/if_bridge.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/net/if_bridge.c')
-rw-r--r--sys/net/if_bridge.c86
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);
}
OpenPOWER on IntegriCloud