From d049a43dcf06a3e155f5496aade5184755a288c4 Mon Sep 17 00:00:00 2001 From: Bart De Schuymer Date: Wed, 24 Dec 2003 03:32:34 -0800 Subject: [PATCH] [BRIDGE]: Add 4 sysctl entries for bridge netfilter behavioral control: bridge-nf-call-arptables - pass or don't pass bridged ARP traffic to arptables' FORWARD chain. bridge-nf-call-iptables - pass or don't pass bridged IPv4 traffic to iptables' chains. bridge-nf-filter-vlan-tagged - pass or don't pass bridged vlan-tagged ARP/IP traffic to arptables/iptables. --- Documentation/networking/ip-sysctl.txt | 19 +++ include/linux/sysctl.h | 8 + net/bridge/br_netfilter.c | 209 ++++++++++++++++++++----- 3 files changed, 201 insertions(+), 35 deletions(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index bbe9902d9fa41..030a5b67313a2 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -678,4 +678,23 @@ IPv6 Update by: Pekka Savola YOSHIFUJI Hideaki / USAGI Project + +/proc/sys/net/bridge/* Variables: + +bridge-nf-call-arptables - BOOLEAN + 1 : pass bridged ARP traffic to arptables' FORWARD chain. + 0 : disable this. + Default: 1 + +bridge-nf-call-iptables - BOOLEAN + 1 : pass bridged IPv4 traffic to iptables' chains. + 0 : disable this. + Default: 1 + +bridge-nf-filter-vlan-tagged - BOOLEAN + 1 : pass bridged vlan-tagged ARP/IP traffic to arptables/iptables. + 0 : disable this. + Default: 1 + + $Id: ip-sysctl.txt,v 1.20 2001/12/13 09:00:18 davem Exp $ diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 4c1b633d5fbfc..c94fb1d1862a7 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -579,6 +579,14 @@ enum { NET_SCTP_MAX_BURST = 12, }; +/* /proc/sys/net/bridge */ +enum { + NET_BRIDGE_NF_CALL_ARPTABLES = 1, + NET_BRIDGE_NF_CALL_IPTABLES = 2, + NET_BRIDGE_NF_CALL_IP6TABLES = 3, + NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4, +}; + /* CTL_PROC names: */ /* CTL_FS names: */ diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 1ca3964f31e62..932f3ae2bbea0 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -35,6 +35,9 @@ #include #include #include "br_private.h" +#ifdef CONFIG_SYSCTL +#include +#endif #define skb_origaddr(skb) (((struct bridge_skb_cb *) \ @@ -47,10 +50,21 @@ #define has_bridge_parent(device) ((device)->br_port != NULL) #define bridge_parent(device) ((device)->br_port->br->dev) -#define IS_VLAN_IP (skb->protocol == __constant_htons(ETH_P_8021Q) && \ - hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP)) -#define IS_VLAN_ARP (skb->protocol == __constant_htons(ETH_P_8021Q) && \ - hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_ARP)) +#ifdef CONFIG_SYSCTL +static struct ctl_table_header *brnf_sysctl_header; +static int brnf_call_iptables = 1; +static int brnf_call_arptables = 1; +static int brnf_filter_vlan_tagged = 1; +#else +#define brnf_filter_vlan_tagged 1 +#endif + +#define IS_VLAN_IP (skb->protocol == __constant_htons(ETH_P_8021Q) && \ + hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP) && \ + brnf_filter_vlan_tagged) +#define IS_VLAN_ARP (skb->protocol == __constant_htons(ETH_P_8021Q) && \ + hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_ARP) && \ + brnf_filter_vlan_tagged) /* We need these fake structures to make netfilter happy -- * lots of places assume that skb->dst != NULL, which isn't @@ -74,8 +88,7 @@ static struct rtable __fake_rtable = { .metrics = {[RTAX_MTU - 1] = 1500}, } }, - - .rt_flags = 0 + .rt_flags = 0, }; @@ -251,6 +264,11 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, struct sk_buff *skb = *pskb; struct nf_bridge_info *nf_bridge; +#ifdef CONFIG_SYSCTL + if (!brnf_call_iptables) + return NF_ACCEPT; +#endif + if (skb->protocol != __constant_htons(ETH_P_IP)) { struct vlan_ethhdr *hdr = (struct vlan_ethhdr *) ((*pskb)->mac.ethernet); @@ -373,7 +391,7 @@ static int br_nf_forward_finish(struct sk_buff *skb) * because of the ipt_physdev.c module. For ARP, indev and outdev are the * bridge ports. */ -static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb, +static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { @@ -381,9 +399,13 @@ static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb, struct nf_bridge_info *nf_bridge; struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet); - if (skb->protocol != __constant_htons(ETH_P_IP) && - skb->protocol != __constant_htons(ETH_P_ARP)) { - if (!IS_VLAN_IP && !IS_VLAN_ARP) +#ifdef CONFIG_SYSCTL + if (!skb->nf_bridge) + return NF_ACCEPT; +#endif + + if (skb->protocol != __constant_htons(ETH_P_IP)) { + if (!IS_VLAN_IP) return NF_ACCEPT; skb_pull(*pskb, VLAN_HLEN); (*pskb)->nh.raw += VLAN_HLEN; @@ -392,39 +414,58 @@ static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb, #ifdef CONFIG_NETFILTER_DEBUG skb->nf_debug ^= (1 << NF_BR_FORWARD); #endif - if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) { - nf_bridge = skb->nf_bridge; - if (skb->pkt_type == PACKET_OTHERHOST) { - skb->pkt_type = PACKET_HOST; - nf_bridge->mask |= BRNF_PKT_TYPE; - } + nf_bridge = skb->nf_bridge; + if (skb->pkt_type == PACKET_OTHERHOST) { + skb->pkt_type = PACKET_HOST; + nf_bridge->mask |= BRNF_PKT_TYPE; + } - /* The physdev module checks on this */ - nf_bridge->mask |= BRNF_BRIDGED; - nf_bridge->physoutdev = skb->dev; + /* The physdev module checks on this */ + nf_bridge->mask |= BRNF_BRIDGED; + nf_bridge->physoutdev = skb->dev; - NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(in), - bridge_parent(out), br_nf_forward_finish); - } else { - struct net_device **d = (struct net_device **)(skb->cb); - struct arphdr *arp = skb->nh.arph; + NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(in), + bridge_parent(out), br_nf_forward_finish); - if (arp->ar_pln != 4) { - if (IS_VLAN_ARP) { - skb_push(*pskb, VLAN_HLEN); - (*pskb)->nh.raw -= VLAN_HLEN; - } + return NF_STOLEN; +} + +static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff **pskb, + const struct net_device *in, const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct sk_buff *skb = *pskb; + struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet); + struct net_device **d = (struct net_device **)(skb->cb); + + if (!brnf_call_arptables) + return NF_ACCEPT; + + if (skb->protocol != __constant_htons(ETH_P_ARP)) { + if (!IS_VLAN_ARP) return NF_ACCEPT; + skb_pull(*pskb, VLAN_HLEN); + (*pskb)->nh.raw += VLAN_HLEN; + } + +#ifdef CONFIG_NETFILTER_DEBUG + skb->nf_debug ^= (1 << NF_BR_FORWARD); +#endif + + if (skb->nh.arph->ar_pln != 4) { + if (IS_VLAN_ARP) { + skb_push(*pskb, VLAN_HLEN); + (*pskb)->nh.raw -= VLAN_HLEN; } - *d = (struct net_device *)in; - NF_HOOK(NF_ARP, NF_ARP_FORWARD, skb, (struct net_device *)in, - (struct net_device *)out, br_nf_forward_finish); + return NF_ACCEPT; } + *d = (struct net_device *)in; + NF_HOOK(NF_ARP, NF_ARP_FORWARD, skb, (struct net_device *)in, + (struct net_device *)out, br_nf_forward_finish); return NF_STOLEN; } - /* PF_BRIDGE/LOCAL_OUT ***********************************************/ static int br_nf_local_out_finish(struct sk_buff *skb) { @@ -475,6 +516,11 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, struct nf_bridge_info *nf_bridge; struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet); +#ifdef CONFIG_SYSCTL + if (!skb->nf_bridge) + return NF_ACCEPT; +#endif + if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP) return NF_ACCEPT; @@ -485,6 +531,7 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, return NF_ACCEPT; nf_bridge = skb->nf_bridge; + nf_bridge->physoutdev = skb->dev; realindev = nf_bridge->physindev; @@ -567,6 +614,11 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, return NF_ACCEPT; } +#ifdef CONFIG_SYSCTL + if (!nf_bridge) + return NF_ACCEPT; +#endif + if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP) return NF_ACCEPT; @@ -632,6 +684,13 @@ static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { + struct sk_buff *skb = *pskb; + +#ifdef CONFIG_SYSCTL + if (!brnf_call_iptables && !skb->nf_bridge) + return NF_ACCEPT; +#endif + if ((out->hard_start_xmit == br_dev_xmit && okfn != br_nf_forward_finish && okfn != br_nf_local_out_finish && @@ -641,7 +700,6 @@ static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb, VLAN_DEV_INFO(out)->real_dev->hard_start_xmit == br_dev_xmit) #endif ) { - struct sk_buff *skb = *pskb; struct nf_bridge_info *nf_bridge; if (!skb->nf_bridge && !nf_bridge_alloc(skb)) @@ -687,7 +745,12 @@ static struct nf_hook_ops br_nf_ops[] = { .pf = PF_BRIDGE, .hooknum = NF_BR_LOCAL_IN, .priority = NF_BR_PRI_BRNF, }, - { .hook = br_nf_forward, + { .hook = br_nf_forward_ip, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_FORWARD, + .priority = NF_BR_PRI_BRNF - 1, }, + { .hook = br_nf_forward_arp, .owner = THIS_MODULE, .pf = PF_BRIDGE, .hooknum = NF_BR_FORWARD, @@ -724,6 +787,69 @@ static struct nf_hook_ops br_nf_ops[] = { .priority = NF_IP_PRI_FIRST, }, }; +#ifdef CONFIG_SYSCTL +static +int brnf_sysctl_call_tables(ctl_table *ctl, int write, struct file * filp, + void *buffer, size_t *lenp) +{ + int ret; + + ret = proc_dointvec(ctl, write, filp, buffer, lenp); + + if (write && *(int *)(ctl->data)) + *(int *)(ctl->data) = 1; + return ret; +} + +static ctl_table brnf_table[] = { + { + .ctl_name = NET_BRIDGE_NF_CALL_ARPTABLES, + .procname = "bridge-nf-call-arptables", + .data = &brnf_call_arptables, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &brnf_sysctl_call_tables, + }, + { + .ctl_name = NET_BRIDGE_NF_CALL_IPTABLES, + .procname = "bridge-nf-call-iptables", + .data = &brnf_call_iptables, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &brnf_sysctl_call_tables, + }, + { + .ctl_name = NET_BRIDGE_NF_FILTER_VLAN_TAGGED, + .procname = "bridge-nf-filter-vlan-tagged", + .data = &brnf_filter_vlan_tagged, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &brnf_sysctl_call_tables, + }, + { .ctl_name = 0 } +}; + +static ctl_table brnf_bridge_table[] = { + { + .ctl_name = NET_BRIDGE, + .procname = "bridge", + .mode = 0555, + .child = brnf_table, + }, + { .ctl_name = 0 } +}; + +static ctl_table brnf_net_table[] = { + { + .ctl_name = CTL_NET, + .procname = "net", + .mode = 0555, + .child = brnf_bridge_table, + }, + { .ctl_name = 0 } +}; +#endif + int br_netfilter_init(void) { int i; @@ -740,6 +866,16 @@ int br_netfilter_init(void) return ret; } +#ifdef CONFIG_SYSCTL + brnf_sysctl_header = register_sysctl_table(brnf_net_table, 0); + if (brnf_sysctl_header == NULL) { + printk(KERN_WARNING "br_netfilter: can't register to sysctl.\n"); + for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++) + nf_unregister_hook(&br_nf_ops[i]); + return -EFAULT; + } +#endif + printk(KERN_NOTICE "Bridge firewalling registered\n"); return 0; @@ -751,4 +887,7 @@ void br_netfilter_fini(void) for (i = ARRAY_SIZE(br_nf_ops) - 1; i >= 0; i--) nf_unregister_hook(&br_nf_ops[i]); +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(brnf_sysctl_header); +#endif } -- 2.53.0