[PATCH] bridge-nf: Allow IPTables to filter PPPoE traffic

"Michael Milner" <[email protected]>
Newsgroups gmane.linux.network.bridge.ebtables.devel
Message-ID <[email protected]>
Hi,

I've put together some code to allow IPTables to filter PPPoE traffic in
the same way that bridge-nf allows iptables to filter VLAN traffic.

I don't have much experience with this code so my approach is very
simplistic and modelled after the VLAN code.  I have some questions as
comments in the patches.

I also added a new sysctl entry to allow the functionality to be disabled.

The patch is against 2.6.15.  Comments appreciated.  It works fine for me
but I wanted some input before submitting it "officially".

Thanks,

Mike Milner

--- a/include/linux/sysctl.h	2006-03-02 16:18:41.000000000 -0500
+++ b/include/linux/sysctl.h	2007-01-16 15:42:27.000000000 -0500
@@ -726,6 +726,7 @@ enum {
 	NET_BRIDGE_NF_CALL_IPTABLES = 2,
 	NET_BRIDGE_NF_CALL_IP6TABLES = 3,
 	NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4,
+	NET_BRIDGE_NF_FILTER_PPPOE_TAGGED = 5,
 };

 /* CTL_PROC names: */

--- a/include/linux/netfilter_bridge.h	2006-03-02 16:18:41.000000000 -0500
+++ b/include/linux/netfilter_bridge.h	2007-02-02 14:31:15.000000000 -0500
@@ -72,6 +72,19 @@ void nf_bridge_maybe_copy_header(struct
 		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
 			memcpy(skb->data - 18, skb->nf_bridge->data, 18);
 			skb_push(skb, 4);
+		}
+		else if (skb->protocol == __constant_htons(ETH_P_PPP_SES)) {
+			/*
+			 * Not sure about '24'.  Default else block below
+			 * copies 16 bytes.  Block above copies 18 (2 more) but
+			 * skb_push's 4 bytes.  VLAN header is 4 bytes, so why
+			 * aren't 4 extra bytes being memcpy'd?
+			 *
+			 * I'm memcpy'ing AND skb_push'ing 8 extra bytes, the size
+			 * of the PPPoE header.
+			 */
+			memcpy(skb->data - 24, skb->nf_bridge->data, 24);
+			skb_push(skb, 8);
 		} else
 			memcpy(skb->data - 16, skb->nf_bridge->data, 16);
 	}
@@ -84,6 +97,13 @@ void nf_bridge_save_header(struct sk_buf

 	if (skb->protocol == __constant_htons(ETH_P_8021Q))
 		header_size = 18;
+	else if (skb->protocol == __constant_htons(ETH_P_PPP_SES))
+		header_size = 24;
+	/*
+	 * Why does the VLAN code only increase the header by 2 bytes?
+	 * AFAIK the VLAN header is 4 bytes.  I reserve 8 extra bytes,
+	 * the size of the PPPoE header.
+	 */

 	memcpy(skb->nf_bridge->data, skb->data - header_size, header_size);
 }

--- a/net/bridge/br_netfilter.c	2006-03-09 08:19:49.000000000 -0500
+++ b/net/bridge/br_netfilter.c	2007-02-05 16:26:13.000000000 -0500
@@ -12,6 +12,7 @@
  *	Oct 06 2003: filter encapsulated IP/ARP VLAN traffic on untagged bridge
  *	             (bdschuym)
  *	Sep 01 2004: add IPv6 filtering (bdschuym)
+ *	Jan 16 2007: let iptables see bridged PPPoE traffic (mdmilner)
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -28,6 +29,7 @@
 #include <linux/skbuff.h>
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
+#include <linux/ppp_defs.h>
 #include <linux/netfilter_bridge.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
@@ -53,6 +55,7 @@ static int brnf_call_iptables = 1;
 static int brnf_call_ip6tables = 1;
 static int brnf_call_arptables = 1;
 static int brnf_filter_vlan_tagged = 1;
+static int brnf_filter_pppoe_tagged = 1;
 #else
 #define brnf_filter_vlan_tagged 1
 #endif
@@ -67,6 +70,43 @@ static int brnf_filter_vlan_tagged = 1;
 	hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_ARP) && \
 	brnf_filter_vlan_tagged)

+/**
+ * Entire Ethernet + PPPoE + PPP header
+ */
+struct pppoe_ethhdr {
+	unsigned char        h_dest[ETH_ALEN];   /* destination eth addr    */
+	unsigned char        h_source[ETH_ALEN]; /* source ether addr       */
+	__be16               h_pppoe_proto;      /* Should always be 0x8864 */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 ver : 4;
+	__u8 type : 4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	__u8 type : 4;
+	__u8 ver : 4;
+#else
+#error  "Please fix <asm/byteorder.h>"
+#endif
+	__u8 code;
+	__u16 sid;
+	__u16 length;
+	__be16 h_pppoe_encapsulated_proto;
+} __attribute__ ((packed));
+
+#define PPPOE_HLEN (sizeof(struct pppoe_ethhdr) - ETH_HLEN)
+
+/* Extracts the pppoe_ethhdr from an sk_buff */
+static inline struct pppoe_ethhdr *pppoe_eth_hdr(const struct sk_buff *skb)
+{
+	return (struct pppoe_ethhdr *)skb->mac.raw;
+}
+
+#define IS_PPPOE_IP (skb->protocol == __constant_htons(ETH_P_PPP_SES) && \
+	pppoe->h_pppoe_encapsulated_proto == __constant_htons(PPP_IP) && \
+	brnf_filter_pppoe_tagged)
+//#define IS_PPPOE_IPV6 (skb->protocol == __constant_htons(ETH_P_PPP_SES)
&& \
+//	pppoe->h_pppoe_encapsulated_proto == __constant_htons(PPP_P_IPV6) && \
+//	brnf_filter_pppoe_tagged)
+
 /* We need these fake structures to make netfilter happy --
  * lots of places assume that skb->dst != NULL, which isn't
  * all that unreasonable.
@@ -195,6 +235,10 @@ static int br_nf_pre_routing_finish_brid
 			skb_pull(skb, VLAN_HLEN);
 			skb->nh.raw += VLAN_HLEN;
 		}
+		else if (skb->protocol == __constant_htons(ETH_P_PPP_SES)) {
+			skb_pull(skb, PPPOE_HLEN);
+			skb->nh.raw += PPPOE_HLEN;
+		}
 		skb->dst->output(skb);
 	}
 	return 0;
@@ -246,6 +290,11 @@ bridged_dnat:
 					skb_push(skb, VLAN_HLEN);
 					skb->nh.raw -= VLAN_HLEN;
 				}
+				else if (skb->protocol ==
+				    __constant_htons(ETH_P_PPP_SES)) {
+					skb_push(skb, PPPOE_HLEN);
+					skb->nh.raw -= PPPOE_HLEN;
+				}
 				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
 					       skb, skb->dev, NULL,
 					       br_nf_pre_routing_finish_bridge,
@@ -266,6 +315,10 @@ bridged_dnat:
 		skb_push(skb, VLAN_HLEN);
 		skb->nh.raw -= VLAN_HLEN;
 	}
+	else if (skb->protocol == __constant_htons(ETH_P_PPP_SES)) {
+		skb_push(skb, PPPOE_HLEN);
+		skb->nh.raw -= PPPOE_HLEN;
+	}
 	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
 		       br_handle_frame_finish, 1);

@@ -408,6 +461,7 @@ static unsigned int br_nf_pre_routing(un
 	struct sk_buff *skb = *pskb;
 	struct nf_bridge_info *nf_bridge;
 	struct vlan_ethhdr *hdr = vlan_eth_hdr(*pskb);
+	struct pppoe_ethhdr *pppoe = pppoe_eth_hdr(*pskb);

 	if (skb->protocol == __constant_htons(ETH_P_IPV6) || IS_VLAN_IPV6) {
 #ifdef CONFIG_SYSCTL
@@ -427,7 +481,8 @@ static unsigned int br_nf_pre_routing(un
 		return NF_ACCEPT;
 #endif

-	if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
+	if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP &&
+	    !IS_PPPOE_IP)
 		return NF_ACCEPT;

 	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
@@ -436,6 +491,13 @@ static unsigned int br_nf_pre_routing(un
 	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
 		skb_pull(skb, VLAN_HLEN);
 	}
+	else if (skb->protocol == __constant_htons(ETH_P_PPP_SES)) {
+		/* Not sure why VLAN doesn't have the '+=' line, but PPPoE
+		 * doesn't work without it
+		 */
+		skb_pull(skb, PPPOE_HLEN);
+		skb->nh.raw += PPPOE_HLEN;
+	}

 	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
 		goto inhdr_error;
@@ -522,6 +584,10 @@ static int br_nf_forward_finish(struct s
 		skb_push(skb, VLAN_HLEN);
 		skb->nh.raw -= VLAN_HLEN;
 	}
+	else if (skb->protocol == __constant_htons(ETH_P_PPP_SES)) {
+		skb_push(skb, PPPOE_HLEN);
+		skb->nh.raw -= PPPOE_HLEN;
+	}
 	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in,
 			skb->dev, br_forward_finish, 1);
 	return 0;
@@ -539,6 +605,7 @@ static unsigned int br_nf_forward_ip(uns
 	struct sk_buff *skb = *pskb;
 	struct nf_bridge_info *nf_bridge;
 	struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
+	struct pppoe_ethhdr *pppoe = pppoe_eth_hdr(skb);
 	struct net_device *parent;
 	int pf;

@@ -549,7 +616,8 @@ static unsigned int br_nf_forward_ip(uns
 	if (!parent)
 		return NF_DROP;

-	if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
+	if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP ||
+	    IS_PPPOE_IP)
 		pf = PF_INET;
 	else
 		pf = PF_INET6;
@@ -558,6 +626,10 @@ static unsigned int br_nf_forward_ip(uns
 		skb_pull(*pskb, VLAN_HLEN);
 		(*pskb)->nh.raw += VLAN_HLEN;
 	}
+	else if (skb->protocol == __constant_htons(ETH_P_PPP_SES)) {
+		skb_pull(*pskb, PPPOE_HLEN);
+		(*pskb)->nh.raw += PPPOE_HLEN;
+	}

 	nf_bridge = skb->nf_bridge;
 	if (skb->pkt_type == PACKET_OTHERHOST) {
@@ -617,6 +689,10 @@ static int br_nf_local_out_finish(struct
 		skb_push(skb, VLAN_HLEN);
 		skb->nh.raw -= VLAN_HLEN;
 	}
+	else if (skb->protocol == __constant_htons(ETH_P_PPP_SES)) {
+		skb_push(skb, PPPOE_HLEN);
+		skb->nh.raw -= PPPOE_HLEN;
+	}

 	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
 			br_forward_finish, NF_BR_PRI_FIRST + 1);
@@ -652,12 +728,14 @@ static unsigned int br_nf_local_out(unsi
 	struct sk_buff *skb = *pskb;
 	struct nf_bridge_info *nf_bridge;
 	struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
+	struct pppoe_ethhdr *pppoe = pppoe_eth_hdr(skb);
 	int pf;

 	if (!skb->nf_bridge)
 		return NF_ACCEPT;

-	if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
+	if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP ||
+	    IS_PPPOE_IP)
 		pf = PF_INET;
 	else
 		pf = PF_INET6;
@@ -687,6 +765,10 @@ static unsigned int br_nf_local_out(unsi
 			skb_push(skb, VLAN_HLEN);
 			skb->nh.raw -= VLAN_HLEN;
 		}
+		else if (skb->protocol == __constant_htons(ETH_P_PPP_SES)) {
+			skb_push(skb, PPPOE_HLEN);
+			skb->nh.raw -= PPPOE_HLEN;
+		}

 		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
 			skb->dev, br_forward_finish);
@@ -705,6 +787,10 @@ static unsigned int br_nf_local_out(unsi
 		skb_pull(skb, VLAN_HLEN);
 		(*pskb)->nh.raw += VLAN_HLEN;
 	}
+	else if (skb->protocol == __constant_htons(ETH_P_PPP_SES)) {
+		skb_pull(skb, PPPOE_HLEN);
+		(*pskb)->nh.raw += PPPOE_HLEN;
+	}
 	/* IP forwarded traffic has a physindev, locally
 	 * generated traffic hasn't. */
 	if (realindev != NULL) {
@@ -736,6 +822,7 @@ static unsigned int br_nf_post_routing(u
 	struct sk_buff *skb = *pskb;
 	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
 	struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
+	struct pppoe_ethhdr *pppoe = pppoe_eth_hdr(skb);
 	struct net_device *realoutdev = bridge_parent(skb->dev);
 	int pf;

@@ -755,7 +842,8 @@ static unsigned int br_nf_post_routing(u
 	if (!realoutdev)
 		return NF_DROP;

-	if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
+	if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP ||
+	    IS_PPPOE_IP)
 		pf = PF_INET;
 	else
 		pf = PF_INET6;
@@ -778,6 +866,10 @@ static unsigned int br_nf_post_routing(u
 		skb_pull(skb, VLAN_HLEN);
 		skb->nh.raw += VLAN_HLEN;
 	}
+	else if (skb->protocol == __constant_htons(ETH_P_PPP_SES)) {
+		skb_pull(skb, PPPOE_HLEN);
+		skb->nh.raw += PPPOE_HLEN;
+	}

 	nf_bridge_save_header(skb);

@@ -1004,6 +1096,14 @@ static ctl_table brnf_table[] = {
 		.mode		= 0644,
 		.proc_handler	= &brnf_sysctl_call_tables,
 	},
+	{
+		.ctl_name	= NET_BRIDGE_NF_FILTER_PPPOE_TAGGED,
+		.procname	= "bridge-nf-filter-pppoe-tagged",
+		.data		= &brnf_filter_pppoe_tagged,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &brnf_sysctl_call_tables,
+	},
 	{ .ctl_name = 0 }
 };




-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier.
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.