Subject: Filtering bridge - updated
To: None <tech-net@netbsd.org>
From: Pavel Cahyna <pcah8322@artax.karlin.mff.cuni.cz>
List: tech-net
Date: 01/28/2003 09:32:51
--0F1p//8PRICkK4MW
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hello,

here is the updated patch for firewalling bridge, originally by Adam
Lebsack. There are two main changes:

1) the original patch did not work, because it sent raw Ethernet frames
to the filter. Now it strips the ethernet header from the mbuf, pass the
mbuf to the filter, an then it re-prepends the ethernet header to it.

2) it filters also broadcast (and multicast, but this is not tested)
traffic through the bridge. The original version let it pass unfiltered.

This probably won't work if the filter modifies the packet in any way
other than freeing it (eg. NAT, reassembling fragments).

This patch is against 1.6_STABLE. It will probably work under -current
too, but the RCSID and some line offsets will be different.

Hope it will be useful.
Pavel


--0F1p//8PRICkK4MW
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="ipf-bridge2.diff"

--- /usr/src/sys/net/if_bridge.c.orig	Tue Jun 11 07:35:18 2002
+++ /usr/src/sys/net/if_bridge.c	Tue Jan 28 08:01:47 2003
@@ -84,6 +84,9 @@
 #include <sys/cdefs.h>
 __KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.5.4.1 2002/06/10 16:28:33 tv Exp $");
 
+#include "opt_inet.h"
+#include "opt_pfil_hooks.h"
+
 #include "bpfilter.h"
 #include "rnd.h"
 
@@ -109,9 +112,14 @@
 #include <net/if_types.h>
 #include <net/if_llc.h>
 
+#include <net/pfil.h>
 #include <net/if_ether.h>
 #include <net/if_bridgevar.h>
 
+#ifdef PFIL_HOOKS
+extern struct pfil_head inet_pfil_hook;
+#endif
+
 /*
  * Size of the route hash table.  Must be a power of two.
  */
@@ -1297,7 +1305,18 @@
 	struct bridge_softc *sc = ifp->if_bridge;
 	struct bridge_iflist *bif;
 	struct ether_header *eh;
+	/* like eh, but for mc */
+	struct ether_header *ehc;
+	/*
+	 * temporary storage for ethernet header during processing of 
+	 * packet by pfil_hook
+	 */
+	struct ether_header eh_data;
 	struct mbuf *mc;
+	u_int16_t etype;
+	struct ifnet *temp_if = NULL;
+	struct ifnet *orig_if = NULL;
+	int learning = 0;
 
 	if ((sc->sc_if.if_flags & IFF_RUNNING) == 0)
 		return (m);
@@ -1335,6 +1354,25 @@
 		mc = m_dup(m, 0, M_COPYALL, M_NOWAIT);
 		if (mc == NULL)
 			return (m);
+#ifdef PFIL_HOOKS
+		ehc = mtod(mc, struct ether_header *);
+		etype = ntohs(ehc->ether_type);	
+#ifdef INET
+		if (etype == ETHERTYPE_IP) {
+			memcpy(&eh_data, ehc, sizeof(struct ether_header));
+			m_adj(mc, sizeof(struct ether_header));
+			if (pfil_run_hooks(&inet_pfil_hook, &mc, 
+			    mc->m_pkthdr.rcvif, PFIL_IN) != 0 || (mc == NULL)) 
+				return (m);
+			M_PREPEND(mc, sizeof(struct ether_header), M_DONTWAIT);
+			if (mc == NULL) 
+				return (m);
+			ehc = mtod(mc, struct ether_header *);
+			memcpy(ehc, &eh_data, sizeof(struct ether_header));
+			
+		}
+#endif /* INET */
+#endif /* PFIL_HOOKS */
 
 		/* Perform the bridge forwarding function with the copy. */
 		bridge_forward(sc, mc);
@@ -1354,18 +1392,32 @@
 
 	/*
 	 * Unicast.  Make sure it's not for us.
+	 * XXX Changed this a bit for archs which have the same MAC address on all NIC's.
+	 * XXX I had a problem on sparc where this would send the packet to the wrong
+	 * XXX interface for later IPF processing.
+	 * XXX Adam Lebsack <adam@lebsack.com>
 	 */
+	orig_if = m->m_pkthdr.rcvif;
+	temp_if = NULL;
 	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
 		/* It is destined for us. */
 		if (memcmp(LLADDR(bif->bif_ifp->if_sadl), eh->ether_dhost,
 		    ETHER_ADDR_LEN) == 0) {
-			if (bif->bif_flags & IFBIF_LEARNING)
-				(void) bridge_rtupdate(sc,
-				    eh->ether_shost, ifp, 0, IFBAF_DYNAMIC);
-			m->m_pkthdr.rcvif = bif->bif_ifp;
-			return (m);
+		    if((temp_if && (bif->bif_ifp == orig_if)) || !temp_if) {
+				temp_if =  bif->bif_ifp;
+				learning = (bif->bif_flags & IFBIF_LEARNING);
+			}
 		}
-
+	}
+	if(temp_if) {
+		if (learning)
+			(void) bridge_rtupdate(sc,
+				eh->ether_shost, ifp, 0, IFBAF_DYNAMIC);
+		m->m_pkthdr.rcvif = temp_if;
+		return (m);
+	}
+	
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
 		/* We just received a packet that we sent out. */
 		if (memcmp(LLADDR(bif->bif_ifp->if_sadl), eh->ether_shost,
 		    ETHER_ADDR_LEN) == 0) {
@@ -1373,6 +1425,28 @@
 			return (NULL);
 		}
 	}
+	
+	/*
+	 * It's not for us, so let's filter it before it goes out.
+	 */
+#ifdef PFIL_HOOKS
+	etype = ntohs(eh->ether_type);	
+#ifdef INET
+	if (etype == ETHERTYPE_IP) {
+		memcpy(&eh_data, eh, sizeof(struct ether_header));
+		m_adj(m, sizeof(struct ether_header));
+		if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif, 
+		    PFIL_IN) != 0 || (m == NULL)) 
+			return (NULL);
+		M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
+		if (m == NULL) 
+			return (NULL);
+		eh = mtod(m, struct ether_header *);
+		memcpy(eh, &eh_data, sizeof(struct ether_header));
+		
+	}
+#endif /* INET */
+#endif /* PFIL_HOOKS */
 
 	/* Perform the bridge forwarding function. */
 	bridge_forward(sc, m);

--0F1p//8PRICkK4MW--