aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge/br_vlan.c
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 /net/bridge/br_vlan.c
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>
Diffstat (limited to 'net/bridge/br_vlan.c')
-rw-r--r--net/bridge/br_vlan.c199
1 files changed, 199 insertions, 0 deletions
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}