Subject: IPF on if_bridge
To: None <tech-net@netbsd.org>
From: Konrad Schroder <perseant@hitl.washington.edu>
List: tech-net
Date: 02/04/2003 17:54:50
My lab is under pressure from our infrastructure providers to switch from
a routing firewall to a bridging firewall, and I thought I'd take a stab
at hooking IPF up to if_bridge before I just dropped in an OpenBSD box to
do the job.

I have something that looks like it will work for me (see attached diff
against 1.6 branch) but not being a network internals guru, I have some
questions.

1. Do I need to care about SNAP?

2. In my case, this is going to be sitting right in front of a router that
   only routes IPv4, so I am perfectly happy just to drop everything
   except [R]ARP and IPv[46].  Coincidentally, IPF only handles IP
   packets.  (Other uses for a bridging firewall---e.g. NoCat---probably
   care more, but I have no incentive to write a whole new packet filter
   for all those other protocols.)  Am I overlooking something important
   in my casual dismissal of everything not IP?

3. If I were going to register the packet filter for real (ll. 322 ff.),
   what values would be most appropriate for pfil_type and pfil_af, given
   that the filter is supposed to filter at least two (and probably four)
   different protocols?

4. I'm assuming that IPF can handle looking at packets that are not really
   IP packets (too short to hold data etc.) and throwing them away, rather
   than having to make that check myself like the OpenBSD folks do.  Am I
   confused in this?

5. In searching back through past mail it seems that Jason had/will have a
   supercool packet filtering module (ZPF?) that sounds as if it might
   replace IPF in general, but at least would do so for the bridge.  Given
   that, if I handle the rest of these concerns, is this something we want
   in tree, or should we wait for ZPF?

Any other comments would of course be welcome....

------------------------------------------------------------------------
Konrad Schroder          http://www.hitl.washington.edu/people/perseant/
Information Tech & Services   Box 352142 -or- 215 Fluke Hall, Mason Road
Human Interface Technology Lab                  University of Washington
Voice: +1.206.616.1478   Fax: +1.206.543.5380    Seattle, WA, 98195, USA


Index: net/if_bridge.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_bridge.c,v
retrieving revision 1.5.4.1
diff -u -r1.5.4.1 if_bridge.c
--- net/if_bridge.c	2002/06/10 16:28:33	1.5.4.1
+++ net/if_bridge.c	2003/02/04 23:26:48
@@ -216,6 +216,9 @@
 int	bridge_ioctl_gma(struct bridge_softc *, void *);
 int	bridge_ioctl_sma(struct bridge_softc *, void *);
 int	bridge_ioctl_sifprio(struct bridge_softc *, void *);
+#ifdef BRIDGE_IPF
+static int bridge_ipf(void *, struct mbuf **, struct ifnet *, int);
+#endif /* BRIDGE_IPF */

 struct bridge_control {
 	int	(*bc_func)(struct bridge_softc *, void *);
@@ -293,6 +296,10 @@
 struct if_clone bridge_cloner =
     IF_CLONE_INITIALIZER("bridge", bridge_clone_create, bridge_clone_destroy);

+#ifdef BRIDGE_IPF
+static struct pfil_head bridge_pfil_hook;
+#endif
+
 /*
  * bridgeattach:
  *
@@ -307,6 +314,18 @@

 	LIST_INIT(&bridge_list);
 	if_clone_attach(&bridge_cloner);
+
+#ifdef BRIDGE_IPF
+	/* Don't register our PF hook; but do register the callback with it. */
+	bridge_pfil_hook.ph_type = 0; /* AF_LINK */
+	bridge_pfil_hook.ph_af   = 0; /* ETHERTYPE_IP */
+	/* pfil_head_register(&bridge_pfil_hook); */
+	TAILQ_INIT(&bridge_pfil_hook.ph_in);
+	TAILQ_INIT(&bridge_pfil_hook.ph_out);
+
+	pfil_add_hook((void *)bridge_ipf, NULL, PFIL_IN|PFIL_OUT,
+			&bridge_pfil_hook);
+#endif /* BRIDGE_IPF */
 }

 /*
@@ -1008,6 +1027,15 @@
 	int len, error;
 	short mflags;

+#ifdef BRIDGE_IPF
+	if (pfil_run_hooks(&bridge_pfil_hook, &m, dst_ifp, PFIL_OUT) != 0) {
+		m_freem(m);
+		return;
+	}
+	if (m == NULL)
+		return;
+#endif
+
 #ifdef ALTQ
 	/*
 	 * If ALTQ is enabled on the member interface, do
@@ -1252,6 +1280,15 @@
 		dst_if = NULL;
 	}

+#ifdef BRIDGE_IPF
+	if (pfil_run_hooks(&bridge_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN) != 0) {
+		m_freem(m);
+		return;
+	}
+	if (m == NULL)
+		return;
+#endif
+
 	if (dst_if == NULL) {
 		bridge_broadcast(sc, src_if, m);
 		return;
@@ -1784,3 +1821,63 @@
 	sc->sc_brtcnt--;
 	pool_put(&bridge_rtnode_pool, brt);
 }
+
+#ifdef BRIDGE_IPF
+extern struct pfil_head inet_pfil_hook;                 /* XXX */
+
+static int bridge_ipf(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
+{
+	int error;
+        struct ether_header *eh;
+	struct mbuf *m1;
+
+	/*
+	 * If we're trying to filter bridge traffic, don't look at anything
+	 * other than IP and ARP traffic.  If the filter doesn't understand
+	 * IPv6, don't allow IPv6 through the bridge either.  This is lame
+	 * since if we really wanted, say, an AppleTalk filter, we are hosed,
+	 * but of course we don't have an AppleTalk filter to begin with.
+	 * (Note that since IPF doesn't understand ARP it will pass *ALL*
+	 * ARP traffic.)
+	 */
+	eh = mtod(*mp, struct ether_header *);
+	switch (ntohs(eh->ether_type)) {
+		case ETHERTYPE_IP:
+		case ETHERTYPE_ARP:
+		case ETHERTYPE_REVARP:
+# ifdef INET6
+		case ETHERTYPE_IPV6:
+# endif /* INET6 */
+			break;
+		default:
+			m_freem(*mp);
+			*mp = NULL;
+			return -1;
+	}
+
+	/* Strip off the Ethernet header---but keep a copy. */
+	m1 = m_split(*mp, sizeof(struct ether_header), M_NOWAIT);
+	if (m1 == NULL) {
+		m_freem(*mp);
+		*mp = NULL;
+		return -1;
+	}
+
+	/* Run the IPF hooks */
+	if ((error = pfil_run_hooks(&inet_pfil_hook, &m1, ifp, dir)) != 0) {
+		m_freem(*mp);
+		*mp = NULL;
+		return error;
+	}
+	if (m1 == NULL) {
+		m_freem(*mp);
+		*mp = NULL;
+		return -1;
+	}
+
+	/* Finally, put everything back the way it was */
+	m_cat(*mp, m1);
+
+	return 0;
+}
+#endif /* BRIDGE_IPF */