diff options
author | jasoni <jasoni@cvs.openbsd.org> | 2002-05-28 15:46:25 +0000 |
---|---|---|
committer | jasoni <jasoni@cvs.openbsd.org> | 2002-05-28 15:46:25 +0000 |
commit | 426354710bff0a16ce8153a5bd7b50664b16ffc7 (patch) | |
tree | 1e7e3c8d57254a50ed4e2057b46e9408421c59a8 /sys/net | |
parent | 938b04a0e7658576f59e1e1f7a3af222cf460f80 (diff) |
refragment ip packets if too large for the outgoing interface
- ok jason@, dhartmei@
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/if_bridge.c | 166 |
1 files changed, 140 insertions, 26 deletions
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 1917f6f666a..5cce4c7fcce 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bridge.c,v 1.88 2002/04/08 17:49:42 jason Exp $ */ +/* $OpenBSD: if_bridge.c,v 1.89 2002/05/28 15:46:24 jasoni Exp $ */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) @@ -156,6 +156,8 @@ u_int8_t bridge_filterrule(struct brl_head *, struct ether_header *); struct mbuf *bridge_filter(struct bridge_softc *, int, struct ifnet *, struct ether_header *, struct mbuf *m); #endif +void bridge_fragment(struct bridge_softc *, struct ifnet *, + struct altq_pktattr *, struct ether_header *, struct mbuf *); #define ETHERADDR_IS_IP_MCAST(a) \ /* struct etheraddr *a; */ \ @@ -1220,23 +1222,28 @@ bridgeintr_frame(sc, m) if (ALTQ_IS_ENABLED(&dst_if->if_snd)) altq_etherclassify(&dst_if->if_snd, m, &pktattr); #endif + len = m->m_pkthdr.len; - mflags = m->m_flags; - s = splimp(); - IFQ_ENQUEUE(&dst_if->if_snd, m, &pktattr, error); - if (error) { - sc->sc_if.if_oerrors++; + if ((len - sizeof(struct ether_header)) > dst_if->if_mtu) + bridge_fragment(sc, dst_if, &pktattr, &eh, m); + else { + mflags = m->m_flags; + s = splimp(); + IFQ_ENQUEUE(&dst_if->if_snd, m, &pktattr, error); + if (error) { + sc->sc_if.if_oerrors++; + splx(s); + return; + } + sc->sc_if.if_opackets++; + sc->sc_if.if_obytes += len; + dst_if->if_obytes += len; + if (mflags & M_MCAST) + dst_if->if_omcasts++; + if ((dst_if->if_flags & IFF_OACTIVE) == 0) + (*dst_if->if_start)(dst_if); splx(s); - return; } - sc->sc_if.if_opackets++; - sc->sc_if.if_obytes += len; - dst_if->if_obytes += len; - if (mflags & M_MCAST) - dst_if->if_omcasts++; - if ((dst_if->if_flags & IFF_OACTIVE) == 0) - (*dst_if->if_start)(dst_if); - splx(s); } /* @@ -1487,18 +1494,22 @@ bridge_broadcast(sc, ifp, eh, m) if (ALTQ_IS_ENABLED(&dst_if->if_snd)) altq_etherclassify(&dst_if->if_snd, mc, &pktattr); #endif - IFQ_ENQUEUE(&dst_if->if_snd, mc, &pktattr, error); - if (error) { - sc->sc_if.if_oerrors++; - continue; + if ((len - sizeof(struct ether_header)) > dst_if->if_mtu) + bridge_fragment(sc, dst_if, &pktattr, eh, mc); + else { + IFQ_ENQUEUE(&dst_if->if_snd, mc, &pktattr, error); + if (error) { + sc->sc_if.if_oerrors++; + continue; + } + sc->sc_if.if_opackets++; + sc->sc_if.if_obytes += len; + dst_if->if_obytes += len; + if (mflags & M_MCAST) + dst_if->if_omcasts++; + if ((dst_if->if_flags & IFF_OACTIVE) == 0) + (*dst_if->if_start)(dst_if); } - sc->sc_if.if_opackets++; - sc->sc_if.if_obytes += len; - dst_if->if_obytes += len; - if (mflags & M_MCAST) - dst_if->if_omcasts++; - if ((dst_if->if_flags & IFF_OACTIVE) == 0) - (*dst_if->if_start)(dst_if); } if (!used) @@ -2307,3 +2318,106 @@ dropit: return (NULL); } #endif /* NPF > 0 */ + +void +bridge_fragment(struct bridge_softc *sc, struct ifnet *ifp, + struct altq_pktattr *pktattr, struct ether_header *eh, struct mbuf *m) +{ + struct llc llc; + struct mbuf *m0 = m; + int s, len, error = 0; + int hassnap = 0; + short mflags = m->m_flags; +#ifdef INET + struct ip *ip; +#endif + +#ifndef INET + goto dropit; +#else + if (eh->ether_type != htons(ETHERTYPE_IP)) { + if (eh->ether_type > ETHERMTU || + m->m_pkthdr.len < (LLC_SNAPFRAMELEN + + sizeof(struct ether_header))) + goto dropit; + + m_copydata(m, sizeof(struct ether_header), + LLC_SNAPFRAMELEN, (caddr_t)&llc); + + if (llc.llc_dsap != LLC_SNAP_LSAP || + llc.llc_ssap != LLC_SNAP_LSAP || + llc.llc_control != LLC_UI || + llc.llc_snap.org_code[0] || + llc.llc_snap.org_code[1] || + llc.llc_snap.org_code[2] || + llc.llc_snap.ether_type != htons(ETHERTYPE_IP)) + goto dropit; + + hassnap = 1; + } + + m_adj(m, sizeof(struct ether_header)); + if (hassnap) + m_adj(m, LLC_SNAPFRAMELEN); + + ip = mtod(m, struct ip *); + NTOHS(ip->ip_len); + NTOHS(ip->ip_off); + + /* Respect IP_DF */ + if (ip->ip_off & IP_DF) + goto dropit; + + error = ip_fragment(m, ifp); + if (error == EMSGSIZE) + goto dropit; + + for (m = m0; m; m = m0) { + m0 = m->m_nextpkt; + m->m_nextpkt = 0; + if (error == 0) { + if (hassnap) { + M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT); + if (m == NULL) { + error = ENOBUFS; + continue; + } + bcopy(&llc, mtod(m, caddr_t), + LLC_SNAPFRAMELEN); + } + M_PREPEND(m, sizeof(*eh), M_DONTWAIT); + if (m == NULL) { + error = ENOBUFS; + continue; + } + len = m->m_pkthdr.len; + bcopy(eh, mtod(m, caddr_t), sizeof(*eh)); + s = splimp(); + IFQ_ENQUEUE(&ifp->if_snd, m, pktattr, error); + if (error) { + sc->sc_if.if_oerrors++; + splx(s); + continue; + } + sc->sc_if.if_opackets++; + sc->sc_if.if_obytes += len; + ifp->if_obytes += len; + if (mflags & M_MCAST) + ifp->if_omcasts++; + if ((ifp->if_flags & IFF_OACTIVE) == 0) + (*ifp->if_start)(ifp); + splx(s); + + } else + m_freem(m); + } + + if (error == 0) + ipstat.ips_fragmented++; + + return; +#endif /* INET */ + dropit: + if (m != NULL) + m_freem(m); +} |