[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