[PATCH FOR REVIEW] layer2 ipfw 'fwd' support

Luigi Rizzo rizzo at icir.org
Mon Sep 22 16:07:50 PDT 2003

for those interested in using ipfw 'fwd' instructions in a bridge
(e.g. to create a transparent proxy with a bridge) here is a patch
to try for ip_fw2.c (and a trivial one-line change in ip_input.c)

The change to ip_input.c matches more closely what the comment says:
if the packet is tagged by the firewall as 'PACKET_TAG_IPFORWARD'
than you skip the pass through the firewall, but still check to see
where the packet goes.

The ip_fw2.c change does the following: when the bridge detects a
layer2 packet, it passes it to ip_input() [!!!layering violation!!!]
with a proper tag so that the packet is subject to the same processing
it would have in a router.

[BTW i believe the same approach could be used to implement 'divert'
within a bridge if we only care for IP packets -- i.e. we tag the
packet and pass it to the upper layer]

The usual restrictions apply -- if you 'forward' to another box
then it must be on the same subnet as one of the interfaces of the
middlebox, because IP addresses are unchanged. Also, despite the
fact you are doing the forwarding on a bridge, if the packet must
go out you need to set net.inet.ip.forwarding=1 because the forwarding
is done at layer 3.


sample topology:

                rl0      rl1
   [clients]------[bridge]--+----[rest of the world]]

on clients:
   no configuration necessary, as the proxy is transparent!

on bridge:
   sysctl net.link.ether.bridge_cfg="rl0 rl1"
   sysctl net.link.ether.bridge_ipfw=1
   sysctl net.link.ether.bridge=1
   sysctl net.inet.ip.forwarding=1
   ipfw add forward proxy proto tcp from any to any 80

on proxy:
   ipfw add forward localhost,8080 tcp from not me to any 80



Index: ip_fw2.c
RCS file: /home/ncvs/src/sys/netinet/ip_fw2.c,v
retrieving revision
diff -u -r1.6.2.16 ip_fw2.c
--- ip_fw2.c	17 Jul 2003 06:03:39 -0000
+++ ip_fw2.c	22 Sep 2003 22:21:38 -0000
@@ -1977,12 +2022,33 @@
 				goto done;
 			case O_FORWARD_IP:
+#if 0
 				if (args->eh)	/* not valid on layer2 pkts */
 				if (!q || dyn_dir == MATCH_FORWARD)
 					args->next_hop =
 					    &((ipfw_insn_sa *)cmd)->sa;
 				retval = 0;
+				if (args->eh) {
+				    struct m_hdr tag;
+				    if (hlen == 0)	/* non IP */
+					break;
+				    /*
+				     * tag with PACKET_TAG_IPFORWARD
+				     * call ip_input() (need ip_forwarding=1
+				     * if this has to go out)
+				     * mark packet as comsumed by the firewall
+				     */
+				    tag.mh_type = MT_TAG;
+				    tag.mh_flags = PACKET_TAG_IPFORWARD;
+				    tag.mh_data = (caddr_t)args->next_hop;
+				    tag.mh_next = m;
+				    args->m = NULL;
+				    retval = IP_FW_PORT_DENY_FLAG;
+				    ip_input((struct mbuf *)&tag);
+				}
 				goto done;
Index: ip_input.c
RCS file: /home/ncvs/src/sys/netinet/ip_input.c,v
retrieving revision
diff -u -r1.130.2.53 ip_input.c
--- ip_input.c	23 Jun 2003 17:53:50 -0000
+++ ip_input.c	22 Sep 2003 22:23:23 -0000
@@ -462,7 +462,7 @@
 		 * skip the firewall a second time
 		if (args.next_hop)
-			goto ours;
+			goto pass;	/* XXX was 'ours' */
 		args.m = m;
 		i = ip_fw_chk_ptr(&args);

More information about the freebsd-ipfw mailing list