aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVlad Yasevich <vyasevic@redhat.com>2013-02-13 07:00:09 -0500
committerDavid S. Miller <davem@davemloft.net>2013-02-13 19:41:46 -0500
commit243a2e63f5f47763b802e9dee8dbf1611a1c1322 (patch)
tree547c44d694683fe852414b7fade126f3b0d2a56b
parent222229974824a4f30b417531cdc9b5b869d6a6b7 (diff)
bridge: Add vlan filtering infrastructure
Adds an optional infrustructure component to bridge that would allow native vlan filtering in the bridge. Each bridge port (as well as the bridge device) now get a VLAN bitmap. Each bit in the bitmap is associated with a vlan id. This way if the bit corresponding to the vid is set in the bitmap that the packet with vid is allowed to enter and exit the port. Write access the bitmap is protected by RTNL and read access protected by RCU. Vlan functionality is disabled by default. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/bridge/Kconfig14
-rw-r--r--net/bridge/Makefile2
-rw-r--r--net/bridge/br_if.c1
-rw-r--r--net/bridge/br_private.h59
-rw-r--r--net/bridge/br_sysfs_br.c21
-rw-r--r--net/bridge/br_vlan.c199
6 files changed, 296 insertions, 0 deletions
diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig
index 6dee7bf648a9..aa0d3b2f1bb7 100644
--- a/net/bridge/Kconfig
+++ b/net/bridge/Kconfig
@@ -46,3 +46,17 @@ config BRIDGE_IGMP_SNOOPING
46 Say N to exclude this support and reduce the binary size. 46 Say N to exclude this support and reduce the binary size.
47 47
48 If unsure, say Y. 48 If unsure, say Y.
49
50config BRIDGE_VLAN_FILTERING
51 bool "VLAN filtering"
52 depends on BRIDGE
53 depends on VLAN_8021Q
54 default n
55 ---help---
56 If you say Y here, then the Ethernet bridge will be able selectively
57 receive and forward traffic based on VLAN information in the packet
58 any VLAN information configured on the bridge port or bridge device.
59
60 Say N to exclude this support and reduce the binary size.
61
62 If unsure, say Y.
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index e859098f5ee9..e85498b2f166 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -14,4 +14,6 @@ bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
14 14
15bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o 15bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
16 16
17bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o
18
17obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ 19obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 2148d474a04f..af9d65ab4001 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -139,6 +139,7 @@ static void del_nbp(struct net_bridge_port *p)
139 139
140 br_ifinfo_notify(RTM_DELLINK, p); 140 br_ifinfo_notify(RTM_DELLINK, p);
141 141
142 nbp_vlan_flush(p);
142 br_fdb_delete_by_port(br, p, 1); 143 br_fdb_delete_by_port(br, p, 1);
143 144
144 list_del_rcu(&p->list); 145 list_del_rcu(&p->list);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 06e85d9c05aa..1f3b309beea8 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -18,6 +18,7 @@
18#include <linux/netpoll.h> 18#include <linux/netpoll.h>
19#include <linux/u64_stats_sync.h> 19#include <linux/u64_stats_sync.h>
20#include <net/route.h> 20#include <net/route.h>
21#include <linux/if_vlan.h>
21 22
22#define BR_HASH_BITS 8 23#define BR_HASH_BITS 8
23#define BR_HASH_SIZE (1 << BR_HASH_BITS) 24#define BR_HASH_SIZE (1 << BR_HASH_BITS)
@@ -26,6 +27,7 @@
26 27
27#define BR_PORT_BITS 10 28#define BR_PORT_BITS 10
28#define BR_MAX_PORTS (1<<BR_PORT_BITS) 29#define BR_MAX_PORTS (1<<BR_PORT_BITS)
30#define BR_VLAN_BITMAP_LEN BITS_TO_LONGS(VLAN_N_VID)
29 31
30#define BR_VERSION "2.3" 32#define BR_VERSION "2.3"
31 33
@@ -63,6 +65,16 @@ struct br_ip
63 __be16 proto; 65 __be16 proto;
64}; 66};
65 67
68struct net_port_vlans {
69 u16 port_idx;
70 union {
71 struct net_bridge_port *port;
72 struct net_bridge *br;
73 } parent;
74 struct rcu_head rcu;
75 unsigned long vlan_bitmap[BR_VLAN_BITMAP_LEN];
76};
77
66struct net_bridge_fdb_entry 78struct net_bridge_fdb_entry
67{ 79{
68 struct hlist_node hlist; 80 struct hlist_node hlist;
@@ -156,6 +168,9 @@ struct net_bridge_port
156#ifdef CONFIG_NET_POLL_CONTROLLER 168#ifdef CONFIG_NET_POLL_CONTROLLER
157 struct netpoll *np; 169 struct netpoll *np;
158#endif 170#endif
171#ifdef CONFIG_BRIDGE_VLAN_FILTERING
172 struct net_port_vlans __rcu *vlan_info;
173#endif
159}; 174};
160 175
161#define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT) 176#define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
@@ -257,6 +272,10 @@ struct net_bridge
257 struct timer_list topology_change_timer; 272 struct timer_list topology_change_timer;
258 struct timer_list gc_timer; 273 struct timer_list gc_timer;
259 struct kobject *ifobj; 274 struct kobject *ifobj;
275#ifdef CONFIG_BRIDGE_VLAN_FILTERING
276 u8 vlan_enabled;
277 struct net_port_vlans __rcu *vlan_info;
278#endif
260}; 279};
261 280
262struct br_input_skb_cb { 281struct br_input_skb_cb {
@@ -531,6 +550,46 @@ static inline void br_mdb_uninit(void)
531} 550}
532#endif 551#endif
533 552
553/* br_vlan.c */
554#ifdef CONFIG_BRIDGE_VLAN_FILTERING
555extern int br_vlan_add(struct net_bridge *br, u16 vid);
556extern int br_vlan_delete(struct net_bridge *br, u16 vid);
557extern void br_vlan_flush(struct net_bridge *br);
558extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
559extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid);
560extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
561extern void nbp_vlan_flush(struct net_bridge_port *port);
562#else
563static inline int br_vlan_add(struct net_bridge *br, u16 vid)
564{
565 return -EOPNOTSUPP;
566}
567
568static inline int br_vlan_delete(struct net_bridge *br, u16 vid)
569{
570 return -EOPNOTSUPP;
571}
572
573static inline void br_vlan_flush(struct net_bridge *br)
574{
575}
576
577static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid)
578{
579 return -EOPNOTSUPP;
580}
581
582static inline int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
583{
584 return -EOPNOTSUPP;
585}
586
587static inline void nbp_vlan_flush(struct net_bridge_port *port)
588{
589}
590
591#endif
592
534/* br_netfilter.c */ 593/* br_netfilter.c */
535#ifdef CONFIG_BRIDGE_NETFILTER 594#ifdef CONFIG_BRIDGE_NETFILTER
536extern int br_netfilter_init(void); 595extern int br_netfilter_init(void);
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 5913a3a0047b..8baa9c08e1a4 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -691,6 +691,24 @@ static ssize_t store_nf_call_arptables(
691static DEVICE_ATTR(nf_call_arptables, S_IRUGO | S_IWUSR, 691static DEVICE_ATTR(nf_call_arptables, S_IRUGO | S_IWUSR,
692 show_nf_call_arptables, store_nf_call_arptables); 692 show_nf_call_arptables, store_nf_call_arptables);
693#endif 693#endif
694#ifdef CONFIG_BRIDGE_VLAN_FILTERING
695static ssize_t show_vlan_filtering(struct device *d,
696 struct device_attribute *attr,
697 char *buf)
698{
699 struct net_bridge *br = to_bridge(d);
700 return sprintf(buf, "%d\n", br->vlan_enabled);
701}
702
703static ssize_t store_vlan_filtering(struct device *d,
704 struct device_attribute *attr,
705 const char *buf, size_t len)
706{
707 return store_bridge_parm(d, buf, len, br_vlan_filter_toggle);
708}
709static DEVICE_ATTR(vlan_filtering, S_IRUGO | S_IWUSR,
710 show_vlan_filtering, store_vlan_filtering);
711#endif
694 712
695static struct attribute *bridge_attrs[] = { 713static struct attribute *bridge_attrs[] = {
696 &dev_attr_forward_delay.attr, 714 &dev_attr_forward_delay.attr,
@@ -732,6 +750,9 @@ static struct attribute *bridge_attrs[] = {
732 &dev_attr_nf_call_ip6tables.attr, 750 &dev_attr_nf_call_ip6tables.attr,
733 &dev_attr_nf_call_arptables.attr, 751 &dev_attr_nf_call_arptables.attr,
734#endif 752#endif
753#ifdef CONFIG_BRIDGE_VLAN_FILTERING
754 &dev_attr_vlan_filtering.attr,
755#endif
735 NULL 756 NULL
736}; 757};
737 758
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
new file mode 100644
index 000000000000..209464ef5242
--- /dev/null
+++ b/net/bridge/br_vlan.c
@@ -0,0 +1,199 @@
1#include <linux/kernel.h>
2#include <linux/netdevice.h>
3#include <linux/rtnetlink.h>
4#include <linux/slab.h>
5
6#include "br_private.h"
7
8static int __vlan_add(struct net_port_vlans *v, u16 vid)
9{
10 int err;
11
12 if (test_bit(vid, v->vlan_bitmap))
13 return -EEXIST;
14
15 if (v->port_idx && vid) {
16 struct net_device *dev = v->parent.port->dev;
17
18 /* Add VLAN to the device filter if it is supported.
19 * Stricly speaking, this is not necessary now, since devices
20 * are made promiscuous by the bridge, but if that ever changes
21 * this code will allow tagged traffic to enter the bridge.
22 */
23 if (dev->features & NETIF_F_HW_VLAN_FILTER) {
24 err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid);
25 if (err)
26 return err;
27 }
28 }
29
30 set_bit(vid, v->vlan_bitmap);
31 return 0;
32}
33
34static int __vlan_del(struct net_port_vlans *v, u16 vid)
35{
36 if (!test_bit(vid, v->vlan_bitmap))
37 return -EINVAL;
38
39 if (v->port_idx && vid) {
40 struct net_device *dev = v->parent.port->dev;
41
42 if (dev->features & NETIF_F_HW_VLAN_FILTER)
43 dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
44 }
45
46 clear_bit(vid, v->vlan_bitmap);
47 if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
48 if (v->port_idx)
49 rcu_assign_pointer(v->parent.port->vlan_info, NULL);
50 else
51 rcu_assign_pointer(v->parent.br->vlan_info, NULL);
52 kfree_rcu(v, rcu);
53 }
54 return 0;
55}
56
57static void __vlan_flush(struct net_port_vlans *v)
58{
59 bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
60 if (v->port_idx)
61 rcu_assign_pointer(v->parent.port->vlan_info, NULL);
62 else
63 rcu_assign_pointer(v->parent.br->vlan_info, NULL);
64 kfree_rcu(v, rcu);
65}
66
67/* Must be protected by RTNL */
68int br_vlan_add(struct net_bridge *br, u16 vid)
69{
70 struct net_port_vlans *pv = NULL;
71 int err;
72
73 ASSERT_RTNL();
74
75 pv = rtnl_dereference(br->vlan_info);
76 if (pv)
77 return __vlan_add(pv, vid);
78
79 /* Create port vlan infomration
80 */
81 pv = kzalloc(sizeof(*pv), GFP_KERNEL);
82 if (!pv)
83 return -ENOMEM;
84
85 pv->parent.br = br;
86 err = __vlan_add(pv, vid);
87 if (err)
88 goto out;
89
90 rcu_assign_pointer(br->vlan_info, pv);
91 return 0;
92out:
93 kfree(pv);
94 return err;
95}
96
97/* Must be protected by RTNL */
98int br_vlan_delete(struct net_bridge *br, u16 vid)
99{
100 struct net_port_vlans *pv;
101
102 ASSERT_RTNL();
103
104 pv = rtnl_dereference(br->vlan_info);
105 if (!pv)
106 return -EINVAL;
107
108 __vlan_del(pv, vid);
109 return 0;
110}
111
112void br_vlan_flush(struct net_bridge *br)
113{
114 struct net_port_vlans *pv;
115
116 ASSERT_RTNL();
117
118 pv = rtnl_dereference(br->vlan_info);
119 if (!pv)
120 return;
121
122 __vlan_flush(pv);
123}
124
125int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
126{
127 if (!rtnl_trylock())
128 return restart_syscall();
129
130 if (br->vlan_enabled == val)
131 goto unlock;
132
133 br->vlan_enabled = val;
134
135unlock:
136 rtnl_unlock();
137 return 0;
138}
139
140/* Must be protected by RTNL */
141int nbp_vlan_add(struct net_bridge_port *port, u16 vid)
142{
143 struct net_port_vlans *pv = NULL;
144 int err;
145
146 ASSERT_RTNL();
147
148 pv = rtnl_dereference(port->vlan_info);
149 if (pv)
150 return __vlan_add(pv, vid);
151
152 /* Create port vlan infomration
153 */
154 pv = kzalloc(sizeof(*pv), GFP_KERNEL);
155 if (!pv) {
156 err = -ENOMEM;
157 goto clean_up;
158 }
159
160 pv->port_idx = port->port_no;
161 pv->parent.port = port;
162 err = __vlan_add(pv, vid);
163 if (err)
164 goto clean_up;
165
166 rcu_assign_pointer(port->vlan_info, pv);
167 return 0;
168
169clean_up:
170 kfree(pv);
171 return err;
172}
173
174/* Must be protected by RTNL */
175int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
176{
177 struct net_port_vlans *pv;
178
179 ASSERT_RTNL();
180
181 pv = rtnl_dereference(port->vlan_info);
182 if (!pv)
183 return -EINVAL;
184
185 return __vlan_del(pv, vid);
186}
187
188void nbp_vlan_flush(struct net_bridge_port *port)
189{
190 struct net_port_vlans *pv;
191
192 ASSERT_RTNL();
193
194 pv = rtnl_dereference(port->vlan_info);
195 if (!pv)
196 return;
197
198 __vlan_flush(pv);
199}