Commit 516299d2 authored by Michael Milner's avatar Michael Milner Committed by David S. Miller

[NETFILTER]: bridge-nf: filter bridged IPv4/IPv6 encapsulated in pppoe traffic

The attached patch by Michael Milner adds support for using iptables and
ip6tables on bridged traffic encapsulated in ppoe frames, similar to
what's already supported for vlan.
Signed-off-by: default avatarMichael Milner <milner@blissisland.ca>
Signed-off-by: default avatarBart De Schuymer <bdschuym@pandora.be>
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 91d73c15
...@@ -1015,7 +1015,12 @@ bridge-nf-call-ip6tables - BOOLEAN ...@@ -1015,7 +1015,12 @@ bridge-nf-call-ip6tables - BOOLEAN
Default: 1 Default: 1
bridge-nf-filter-vlan-tagged - BOOLEAN bridge-nf-filter-vlan-tagged - BOOLEAN
1 : pass bridged vlan-tagged ARP/IP traffic to arptables/iptables. 1 : pass bridged vlan-tagged ARP/IP/IPv6 traffic to {arp,ip,ip6}tables.
0 : disable this.
Default: 1
bridge-nf-filter-pppoe-tagged - BOOLEAN
1 : pass bridged pppoe-tagged IP/IPv6 traffic to {ip,ip6}tables.
0 : disable this. 0 : disable this.
Default: 1 Default: 1
......
...@@ -111,6 +111,9 @@ struct pppoe_hdr { ...@@ -111,6 +111,9 @@ struct pppoe_hdr {
struct pppoe_tag tag[0]; struct pppoe_tag tag[0];
} __attribute__ ((packed)); } __attribute__ ((packed));
/* Length of entire PPPoE + PPP header */
#define PPPOE_SES_HLEN 8
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/skbuff.h> #include <linux/skbuff.h>
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/if_pppox.h>
/* Bridge Hooks */ /* Bridge Hooks */
/* After promisc drops, checksum checks. */ /* After promisc drops, checksum checks. */
...@@ -58,8 +59,14 @@ static inline int nf_bridge_maybe_copy_header(struct sk_buff *skb) ...@@ -58,8 +59,14 @@ static inline int nf_bridge_maybe_copy_header(struct sk_buff *skb)
* enough room for the encapsulating header (if there is one). */ * enough room for the encapsulating header (if there is one). */
static inline int nf_bridge_pad(const struct sk_buff *skb) static inline int nf_bridge_pad(const struct sk_buff *skb)
{ {
return (skb->nf_bridge && skb->protocol == htons(ETH_P_8021Q)) int padding = 0;
? VLAN_HLEN : 0;
if (skb->nf_bridge && skb->protocol == htons(ETH_P_8021Q))
padding = VLAN_HLEN;
else if (skb->nf_bridge && skb->protocol == htons(ETH_P_PPP_SES))
padding = PPPOE_SES_HLEN;
return padding;
} }
struct bridge_skb_cb { struct bridge_skb_cb {
......
...@@ -792,6 +792,7 @@ enum { ...@@ -792,6 +792,7 @@ enum {
NET_BRIDGE_NF_CALL_IPTABLES = 2, NET_BRIDGE_NF_CALL_IPTABLES = 2,
NET_BRIDGE_NF_CALL_IP6TABLES = 3, NET_BRIDGE_NF_CALL_IP6TABLES = 3,
NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4, NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4,
NET_BRIDGE_NF_FILTER_PPPOE_TAGGED = 5,
}; };
/* CTL_FS names: */ /* CTL_FS names: */
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/if_pppox.h>
#include <linux/ppp_defs.h>
#include <linux/netfilter_bridge.h> #include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h>
...@@ -57,8 +59,10 @@ static int brnf_call_iptables __read_mostly = 1; ...@@ -57,8 +59,10 @@ static int brnf_call_iptables __read_mostly = 1;
static int brnf_call_ip6tables __read_mostly = 1; static int brnf_call_ip6tables __read_mostly = 1;
static int brnf_call_arptables __read_mostly = 1; static int brnf_call_arptables __read_mostly = 1;
static int brnf_filter_vlan_tagged __read_mostly = 1; static int brnf_filter_vlan_tagged __read_mostly = 1;
static int brnf_filter_pppoe_tagged __read_mostly = 1;
#else #else
#define brnf_filter_vlan_tagged 1 #define brnf_filter_vlan_tagged 1
#define brnf_filter_pppoe_tagged 1
#endif #endif
static inline __be16 vlan_proto(const struct sk_buff *skb) static inline __be16 vlan_proto(const struct sk_buff *skb)
...@@ -81,6 +85,22 @@ static inline __be16 vlan_proto(const struct sk_buff *skb) ...@@ -81,6 +85,22 @@ static inline __be16 vlan_proto(const struct sk_buff *skb)
vlan_proto(skb) == htons(ETH_P_ARP) && \ vlan_proto(skb) == htons(ETH_P_ARP) && \
brnf_filter_vlan_tagged) brnf_filter_vlan_tagged)
static inline __be16 pppoe_proto(const struct sk_buff *skb)
{
return *((__be16 *)(skb_mac_header(skb) + ETH_HLEN +
sizeof(struct pppoe_hdr)));
}
#define IS_PPPOE_IP(skb) \
(skb->protocol == htons(ETH_P_PPP_SES) && \
pppoe_proto(skb) == htons(PPP_IP) && \
brnf_filter_pppoe_tagged)
#define IS_PPPOE_IPV6(skb) \
(skb->protocol == htons(ETH_P_PPP_SES) && \
pppoe_proto(skb) == htons(PPP_IPV6) && \
brnf_filter_pppoe_tagged)
/* We need these fake structures to make netfilter happy -- /* We need these fake structures to make netfilter happy --
* lots of places assume that skb->dst != NULL, which isn't * lots of places assume that skb->dst != NULL, which isn't
* all that unreasonable. * all that unreasonable.
...@@ -128,6 +148,8 @@ static inline void nf_bridge_save_header(struct sk_buff *skb) ...@@ -128,6 +148,8 @@ static inline void nf_bridge_save_header(struct sk_buff *skb)
if (skb->protocol == htons(ETH_P_8021Q)) if (skb->protocol == htons(ETH_P_8021Q))
header_size += VLAN_HLEN; header_size += VLAN_HLEN;
else if (skb->protocol == htons(ETH_P_PPP_SES))
header_size += PPPOE_SES_HLEN;
skb_copy_from_linear_data_offset(skb, -header_size, skb_copy_from_linear_data_offset(skb, -header_size,
skb->nf_bridge->data, header_size); skb->nf_bridge->data, header_size);
...@@ -144,6 +166,8 @@ int nf_bridge_copy_header(struct sk_buff *skb) ...@@ -144,6 +166,8 @@ int nf_bridge_copy_header(struct sk_buff *skb)
if (skb->protocol == htons(ETH_P_8021Q)) if (skb->protocol == htons(ETH_P_8021Q))
header_size += VLAN_HLEN; header_size += VLAN_HLEN;
else if (skb->protocol == htons(ETH_P_PPP_SES))
header_size += PPPOE_SES_HLEN;
err = skb_cow(skb, header_size); err = skb_cow(skb, header_size);
if (err) if (err)
...@@ -154,6 +178,8 @@ int nf_bridge_copy_header(struct sk_buff *skb) ...@@ -154,6 +178,8 @@ int nf_bridge_copy_header(struct sk_buff *skb)
if (skb->protocol == htons(ETH_P_8021Q)) if (skb->protocol == htons(ETH_P_8021Q))
__skb_push(skb, VLAN_HLEN); __skb_push(skb, VLAN_HLEN);
else if (skb->protocol == htons(ETH_P_PPP_SES))
__skb_push(skb, PPPOE_SES_HLEN);
return 0; return 0;
} }
...@@ -177,6 +203,9 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) ...@@ -177,6 +203,9 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
if (skb->protocol == htons(ETH_P_8021Q)) { if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN); skb_push(skb, VLAN_HLEN);
skb->network_header -= VLAN_HLEN; skb->network_header -= VLAN_HLEN;
} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
skb_push(skb, PPPOE_SES_HLEN);
skb->network_header -= PPPOE_SES_HLEN;
} }
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish, 1); br_handle_frame_finish, 1);
...@@ -258,6 +287,9 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) ...@@ -258,6 +287,9 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
if (skb->protocol == htons(ETH_P_8021Q)) { if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN); skb_pull(skb, VLAN_HLEN);
skb->network_header += VLAN_HLEN; skb->network_header += VLAN_HLEN;
} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
skb_pull(skb, PPPOE_SES_HLEN);
skb->network_header += PPPOE_SES_HLEN;
} }
skb->dst->output(skb); skb->dst->output(skb);
} }
...@@ -328,6 +360,10 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb) ...@@ -328,6 +360,10 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
htons(ETH_P_8021Q)) { htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN); skb_push(skb, VLAN_HLEN);
skb->network_header -= VLAN_HLEN; skb->network_header -= VLAN_HLEN;
} else if(skb->protocol ==
htons(ETH_P_PPP_SES)) {
skb_push(skb, PPPOE_SES_HLEN);
skb->network_header -= PPPOE_SES_HLEN;
} }
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
skb, skb->dev, NULL, skb, skb->dev, NULL,
...@@ -347,6 +383,9 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb) ...@@ -347,6 +383,9 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
if (skb->protocol == htons(ETH_P_8021Q)) { if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN); skb_push(skb, VLAN_HLEN);
skb->network_header -= VLAN_HLEN; skb->network_header -= VLAN_HLEN;
} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
skb_push(skb, PPPOE_SES_HLEN);
skb->network_header -= PPPOE_SES_HLEN;
} }
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish, 1); br_handle_frame_finish, 1);
...@@ -489,7 +528,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -489,7 +528,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
__u32 len; __u32 len;
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb)) { if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb) ||
IS_PPPOE_IPV6(skb)) {
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
if (!brnf_call_ip6tables) if (!brnf_call_ip6tables)
return NF_ACCEPT; return NF_ACCEPT;
...@@ -500,6 +540,9 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -500,6 +540,9 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
if (skb->protocol == htons(ETH_P_8021Q)) { if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull_rcsum(skb, VLAN_HLEN); skb_pull_rcsum(skb, VLAN_HLEN);
skb->network_header += VLAN_HLEN; skb->network_header += VLAN_HLEN;
} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
skb_pull_rcsum(skb, PPPOE_SES_HLEN);
skb->network_header += PPPOE_SES_HLEN;
} }
return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn); return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn);
} }
...@@ -508,7 +551,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -508,7 +551,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
return NF_ACCEPT; return NF_ACCEPT;
#endif #endif
if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb)) if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb) &&
!IS_PPPOE_IP(skb))
return NF_ACCEPT; return NF_ACCEPT;
if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL) if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
...@@ -517,6 +561,9 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -517,6 +561,9 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
if (skb->protocol == htons(ETH_P_8021Q)) { if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull_rcsum(skb, VLAN_HLEN); skb_pull_rcsum(skb, VLAN_HLEN);
skb->network_header += VLAN_HLEN; skb->network_header += VLAN_HLEN;
} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
skb_pull_rcsum(skb, PPPOE_SES_HLEN);
skb->network_header += PPPOE_SES_HLEN;
} }
if (!pskb_may_pull(skb, sizeof(struct iphdr))) if (!pskb_may_pull(skb, sizeof(struct iphdr)))
...@@ -598,6 +645,9 @@ static int br_nf_forward_finish(struct sk_buff *skb) ...@@ -598,6 +645,9 @@ static int br_nf_forward_finish(struct sk_buff *skb)
if (skb->protocol == htons(ETH_P_8021Q)) { if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN); skb_push(skb, VLAN_HLEN);
skb->network_header -= VLAN_HLEN; skb->network_header -= VLAN_HLEN;
} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
skb_push(skb, PPPOE_SES_HLEN);
skb->network_header -= PPPOE_SES_HLEN;
} }
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in, NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in,
skb->dev, br_forward_finish, 1); skb->dev, br_forward_finish, 1);
...@@ -626,7 +676,8 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb, ...@@ -626,7 +676,8 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
if (!parent) if (!parent)
return NF_DROP; return NF_DROP;
if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb)) if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) ||
IS_PPPOE_IP(skb))
pf = PF_INET; pf = PF_INET;
else else
pf = PF_INET6; pf = PF_INET6;
...@@ -634,6 +685,9 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb, ...@@ -634,6 +685,9 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
if (skb->protocol == htons(ETH_P_8021Q)) { if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull(*pskb, VLAN_HLEN); skb_pull(*pskb, VLAN_HLEN);
(*pskb)->network_header += VLAN_HLEN; (*pskb)->network_header += VLAN_HLEN;
} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
skb_pull(*pskb, PPPOE_SES_HLEN);
(*pskb)->network_header += PPPOE_SES_HLEN;
} }
nf_bridge = skb->nf_bridge; nf_bridge = skb->nf_bridge;
...@@ -726,6 +780,9 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, ...@@ -726,6 +780,9 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
if (skb->protocol == htons(ETH_P_8021Q)) { if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN); skb_push(skb, VLAN_HLEN);
skb->network_header -= VLAN_HLEN; skb->network_header -= VLAN_HLEN;
} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
skb_push(skb, PPPOE_SES_HLEN);
skb->network_header -= PPPOE_SES_HLEN;
} }
NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev, skb->dev, NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev, skb->dev,
...@@ -771,7 +828,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -771,7 +828,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
if (!realoutdev) if (!realoutdev)
return NF_DROP; return NF_DROP;
if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb)) if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) ||
IS_PPPOE_IP(skb))
pf = PF_INET; pf = PF_INET;
else else
pf = PF_INET6; pf = PF_INET6;
...@@ -793,6 +851,9 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -793,6 +851,9 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
if (skb->protocol == htons(ETH_P_8021Q)) { if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN); skb_pull(skb, VLAN_HLEN);
skb->network_header += VLAN_HLEN; skb->network_header += VLAN_HLEN;
} else if (skb->protocol == htons(ETH_P_PPP_SES)) {
skb_pull(skb, PPPOE_SES_HLEN);
skb->network_header += PPPOE_SES_HLEN;
} }
nf_bridge_save_header(skb); nf_bridge_save_header(skb);
...@@ -930,6 +991,14 @@ static ctl_table brnf_table[] = { ...@@ -930,6 +991,14 @@ static ctl_table brnf_table[] = {
.mode = 0644, .mode = 0644,
.proc_handler = &brnf_sysctl_call_tables, .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 } { .ctl_name = 0 }
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment