[ previous ] [ next ] [ threads ]
 
 From:  bmah at acm dot org (Bruce A. Mah)
 To:  m0n0wall at lists dot m0n0 dot ch
 Cc:  bmah at acm dot org
 Subject:  [PATCHES] m0n0wall as a filtering bridge
 Date:  Tue, 22 Jul 2003 08:14:23 -0700
Here's how I turned my m0n0wall into a filtering bridge.

My hardware is a Soekris 4501.  I wanted to use it as a filtering
bridge between my WAN port and some externally visible workstations
and servers, connected to the OPT1 port.  The problem of course is
that m0n0wall (pb13) doesn't support firewall rules on bridged
interfaces.  This is because the ng_bridge(4) netgraph node doesn't
pass packets through the IPFilter rulesets.

My approach was to switch from ng_bridge(4) bridging to
bridge(4)-style filtering.  bridge(4) can be made to do input
filtering using either ipfw or IPFilter.

Here's roughly the steps I did.  I'm going to assume you (the reader)
have a little experience at hacking on m0n0wall, updating the
filesystem images, etc.

First, I copied the bridge.ko kernel module from a FreeBSD 4.8-RELEASE
CDROM to /modules on the mfsroot filesystem image.

Then I applied three patches (attached to this email).

1.  /etc/rc now loads the bridge.ko kernel module and enables IPFilter
    for bridged packets.

2.  /etc/inc/interfaces.inc now generates the correct bridge
    configuration string to hand to the net.link.ether.bridge_cfg
    sysctl variable.  Note that after this patch, /etc/inc/bridge.inc
    can go away.

3.  /etc/inc/filter.inc modifies the filter rules to handle the
    concept that the same prefix can be associated with multiple
    interfaces (due to the bridging).  I might not have gotten this
    completely right, in that bridging to the LAN interface probably
    won't work.  I don't know about bridging between multiple OPTx
    interfaces.

After this, I gave the m0n0wall WAN port an IP address out of my
allocation (and the correct subnet mask), and bridged the OPT1 port to
the WAN port.

Other notes:

1.  Remember you now need to permit traffic from your OPTx interface.
    You also need to write rules for the WAN interface that will
    permit traffic going to the machines on the OPTx interface.

2.  If you have multicast traffic arriving on your WAN interface,
    bridge(4) drops each packet and writes a syslog message announcing
    this fact.  This is way too chatty.

3.  I didn't change the UI pages so they still claim that firewall
    rules on bridging interfaces have no effect.  This should be
    trivial to fix.

4.  It'd probably be better just to compile the bridge device into the
    kernel.  But this worked pretty well for a proof-of-concept.

5.  In theory this should be able to handle multiple "bridging groups"
    but each bridging group can only contain two interfaces, exactly
    one of which must have an IP address associated with it.

Comments/suggestions appreciated!

Bruce.
--- /scratch0/m0n0wall/rootfs-pb13r443/etc/inc/filter.inc	Sun Jun 29 09:24:33 2003
+++ /mnt2/etc/inc/filter.inc	Sun Jul 20 21:21:26 2003
@@ -212,12 +212,20 @@
 	for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
 		$oc = $config['interfaces']['opt' . $i];
 		
-		if (isset($oc['enable']) && $oc['if'] && (!$oc['bridge'])) {
+		if (isset($oc['enable']) && $oc['if']) {
 			$oic = array();
-			$oic['ip'] = $oc['ipaddr'];
 			$oic['if'] = $oc['if'];
-			$oic['sa'] = gen_subnet($oc['ipaddr'], $oc['subnet']);
-			$oic['sn'] = $oc['subnet'];
+
+			if ($oc['bridge']) {
+				$oic['ip'] = $config['interfaces'][$oc['bridge']]['ipaddr'];
+				$oic['sn'] = $config['interfaces'][$oc['bridge']]['subnet'];
+				$oic['bridge'] = 1;
+			}
+			else {
+				$oic['ip'] = $oc['ipaddr'];
+				$oic['sn'] = $oc['subnet'];
+			}
+			$oic['sa'] = gen_subnet($oic['ip'], $oic['sn']);
 			
 			$optcfg['opt' . $i] = $oic;
 			
@@ -253,7 +261,9 @@
 EOD;
 
 	foreach ($optcfg as $oc) {
-		$ipfrules .= "block in log quick on $wanif from {$oc['sa']}/{$oc['sn']} to any\n";
+		if (!$oc['bridge']) {
+			$ipfrules .= "block in log quick on $wanif from {$oc['sa']}/{$oc['sn']} to any\n";
+		}
 	}
 	
 	/* allow PPTP traffic if PPTP client is enabled on WAN */
--- /scratch0/m0n0wall/rootfs-pb13r443/etc/inc/interfaces.inc	Sun Jun 29 09:24:33 2003
+++ /mnt2/etc/inc/interfaces.inc	Sun Jul 20 15:42:26 2003
@@ -83,16 +83,23 @@
 
 function interfaces_optional_configure() {
 	global $config, $g;
+	global $bridgeconfig;
 	
+	/* Reset bridge configuration.  Interfaces will add to it. */
+	$bridgeconfig = "";
+
 	for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
 		interfaces_optional_configure_if($i);
 	}
 	
+	/* Set the system bridge configuration. */
+	mwexec("/sbin/sysctl net.link.ether.bridge_cfg=" . $bridgeconfig);
 	return 0;
 }
 
 function interfaces_optional_configure_if($opti) {
 	global $config, $g;
+	global $bridgeconfig;
 	
 	$optcfg = $config['interfaces']['opt' . $opti];
 	
@@ -112,11 +119,17 @@
 		if ($optcfg['bridge']) {
 			mwexec("/sbin/ifconfig " . escapeshellarg($optcfg['if']) . 
 				" delete up");
-			bridge_start("bridge" . $opti, $optcfg['if'], 
-				(is_array($optcfg['wireless'])) ? false : true,
-				$config['interfaces'][$optcfg['bridge']]['if'],
-				(is_array($config['interfaces'][$optcfg['bridge']]['wireless']))
-					? false : true);
+#			bridge_start("bridge" . $opti, $optcfg['if'], 
+#				(is_array($optcfg['wireless'])) ? false : true,
+#				$config['interfaces'][$optcfg['bridge']]['if'],
+#				(is_array($config['interfaces'][$optcfg['bridge']]['wireless']))
+#					? false : true);
+			if ($bridgeconfig != "")
+				$bridgeconfig = $bridgeconfig . ",";
+			$bridgeconfig = $bridgeconfig . $optcfg['if'] .
+				":" . $opti . "," .
+				$config['interfaces'][$optcfg['bridge']]['if'] .
+				":" . $opti;
 		} else {
 			mwexec("/sbin/ifconfig " . escapeshellarg($optcfg['if']) . " " . 
 				escapeshellarg($optcfg['ipaddr'] . "/" . $optcfg['subnet']));
--- /scratch0/m0n0wall/rootfs-pb13r443/etc/rc	Wed Jun 25 12:40:42 2003
+++ /mnt2/etc/rc	Sun Jul 20 15:42:40 2003
@@ -43,6 +43,11 @@
 # Run ldconfig
 /sbin/ldconfig -elf /usr/lib
 
+# Load bridging module, enable bridging
+/sbin/kldload /modules/bridge.ko
+/sbin/sysctl net.link.ether.bridge=1
+/sbin/sysctl net.link.ether.bridge_ipf=1
+
 # let the PHP-based configuration subsystem set up the system now
 /etc/rc.bootup