Subject: IPFilter'ed Bridge
To: None <tech-net@netbsd.org>
From: Adam Lebsack <adam@lebsack.com>
List: tech-net
Date: 12/16/2002 11:42:10
Hey guys,

I was reading an article on daemonnews.org  
(http://ezine.daemonnews.org/200211/ipfilter-bridge.html)  about 
IPFilter'ing bridges.   I figured this would be a good thing to have 
for my current setup.  I could bridge multiple cable modem DHCP leases 
thru one NetBSD box and not have to rely on each machine's ipfilter 
rules.

I know NetBSD lacks this support, so I hacked it together, tested it, 
and it seems to work well.  In fact, it's a very simple patch.  If 
anyone would like to try it, or debate its place in the NetBSD src 
tree, the patch is below.  However, you must think in terms of what the 
bridge is doing before writing ipfilter rules.  For instance, "out" 
rules don't work.  For instance, consider this setup:

              | NetBSD Bridge and firewall |
internet <-> | le0 <-> bridge0 <-> le1    | <-> switch/hub and other 
internal machines
              |                            |

To block traffic to your internal machines, use a " block in on le0" 
rule to keep that traffic from crossing the bridge (or use a 'block in 
on all' rule at the end and just use individual 'pass in quick' 
statements before that).

Adam Lebsack
adam@lebsack.com


--- /vol/src/sys/net/if_bridge.c	Mon Jun 10 10:28:33 2002
+++ /home/adam/sys/net/if_bridge.c	Mon Dec 16 11:31:48 2002
@@ -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.
   */
@@ -1298,6 +1306,10 @@
  	struct bridge_iflist *bif;
  	struct ether_header *eh;
  	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);
@@ -1354,18 +1366,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 +1399,20 @@
  			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) {
+		if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN) 
!= 0 || (m == NULL)) {
+			return (NULL);
+		}
+	}
+#endif /* INET */
+#endif /* PFIL_HOOKS */

  	/* Perform the bridge forwarding function. */
  	bridge_forward(sc, m);