blob: 209464ef5242729904dd1cbd7c77204bb16f7e50 [file] [log] [blame]
Vlad Yasevich243a2e62013-02-13 12:00:09 +00001#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}