diff options
author | thompsa <thompsa@FreeBSD.org> | 2006-04-29 05:37:25 +0000 |
---|---|---|
committer | thompsa <thompsa@FreeBSD.org> | 2006-04-29 05:37:25 +0000 |
commit | a5dbf412be417f74a0c9ba2aba90be3bddb90506 (patch) | |
tree | e31aeac32af45cf5905727b50913aed8d77158f9 /sys | |
parent | 00dbaccdc47a4909b01e3013e28beefeb8c3da09 (diff) | |
download | FreeBSD-src-a5dbf412be417f74a0c9ba2aba90be3bddb90506.zip FreeBSD-src-a5dbf412be417f74a0c9ba2aba90be3bddb90506.tar.gz |
Add support for fragmenting ipv4 packets.
The packet filter may reassemble the ip fragments and return a packet that is
larger than the MTU of the sending interface. There is no check for DF or icmp
replies as we can only get a large packet to fragment by reassembling a
previous fragment, and this only happens after a call to pfil(9).
Obtained from: OpenBSD (mostly)
Glanced at by: mlaier
MFC after: 1 month
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net/if_bridge.c | 92 |
1 files changed, 85 insertions, 7 deletions
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index a583442..80eb0b4 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -263,6 +263,8 @@ 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 *, + struct ether_header *, int, struct llc *); SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, IFT_BRIDGE, bridge, CTLFLAG_RW, 0, "Bridge"); @@ -1497,13 +1499,22 @@ bridge_stop(struct ifnet *ifp, int disable) __inline void bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m) { - int len, err; + int len, err = 0; short mflags; + struct mbuf *m0; len = m->m_pkthdr.len; mflags = m->m_flags; - IFQ_ENQUEUE(&dst_ifp->if_snd, m, err); + /* We may be sending a framgment so traverse the mbuf */ + for (; m; m = m0) { + m0 = m->m_nextpkt; + m->m_nextpkt = NULL; + + if (err == 0) + IFQ_ENQUEUE(&dst_ifp->if_snd, m, err); + } + if (err == 0) { sc->sc_ifp->if_opackets++; @@ -2761,13 +2772,24 @@ ipfwpass: error = pfil_run_hooks(&inet_pfil_hook, mp, bifp, dir, NULL); - /* Restore ip and the fields ntohs()'d. */ - if (*mp != NULL && error == 0) { - ip = mtod(*mp, struct ip *); - ip->ip_len = htons(ip->ip_len); - ip->ip_off = htons(ip->ip_off); + if (*mp == NULL || error != 0) /* filter may consume */ + break; + + /* check if we need to fragment the packet */ + 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, + &llc1); + return (error); + } } + /* Restore ip and the fields ntohs()'d. */ + ip = mtod(*mp, struct ip *); + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); + break; # ifdef INET6 case ETHERTYPE_IPV6 : @@ -2979,3 +3001,59 @@ bad: return -1; } # endif /* INET6 */ + +/* + * bridge_fragment: + * + * Return a fragmented mbuf chain. + */ +static int +bridge_fragment(struct ifnet *ifp, struct mbuf *m, struct ether_header *eh, + int snap, struct llc *llc) +{ + struct mbuf *m0; + struct ip *ip; + int error = -1; + + if (m->m_len < sizeof(struct ip) && + (m = m_pullup(m, sizeof(struct ip))) == NULL) + goto out; + ip = mtod(m, struct ip *); + + error = ip_fragment(ip, &m, ifp->if_mtu, ifp->if_hwassist, + CSUM_DELAY_IP); + if (error) + goto out; + + /* 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_DONTWAIT); + if (m0 == NULL) { + error = ENOBUFS; + continue; + } + bcopy(llc, mtod(m0, caddr_t), + sizeof(struct llc)); + } + M_PREPEND(m0, ETHER_HDR_LEN, M_DONTWAIT); + if (m0 == NULL) { + error = ENOBUFS; + continue; + } + bcopy(eh, mtod(m0, caddr_t), ETHER_HDR_LEN); + } else + m_freem(m); + } + + if (error == 0) + ipstat.ips_fragmented++; + + return (error); + +out: + if (m != NULL) + m_freem(m); + return (error); +} |