aboutsummaryrefslogtreecommitdiff
path: root/sys/net/if_bridge.c
diff options
context:
space:
mode:
authorAndrew Thompson <thompsa@FreeBSD.org>2006-04-29 05:37:25 +0000
committerAndrew Thompson <thompsa@FreeBSD.org>2006-04-29 05:37:25 +0000
commit7f87a57ca386b5b87c3579f8aca93462a26b1f7c (patch)
treee31aeac32af45cf5905727b50913aed8d77158f9 /sys/net/if_bridge.c
parentde4bee5922b26e3f4ea850854bc35e0cf8a5d523 (diff)
downloadsrc-7f87a57ca386b5b87c3579f8aca93462a26b1f7c.tar.gz
src-7f87a57ca386b5b87c3579f8aca93462a26b1f7c.zip
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
Notes
Notes: svn path=/head/; revision=158140
Diffstat (limited to 'sys/net/if_bridge.c')
-rw-r--r--sys/net/if_bridge.c92
1 files changed, 85 insertions, 7 deletions
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index a583442fdd0e..80eb0b4a90a7 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);
+}