diff options
author | Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp> | 2014-06-10 07:59:23 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-06-11 18:22:53 -0400 |
commit | 8580e2117c06ac0c97a561219eaab6dab968ea3f (patch) | |
tree | 917376c58844601ef11fafce3952461730411637 /net/bridge | |
parent | 1c5abb6c77a2e79537373143d2c1708e40b9f6ca (diff) |
bridge: Prepare for 802.1ad vlan filtering support
This enables a bridge to have vlan protocol informantion and allows vlan
tag manipulation (retrieve, insert and remove tags) according to the vlan
protocol.
Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge')
-rw-r--r-- | net/bridge/br_device.c | 1 | ||||
-rw-r--r-- | net/bridge/br_private.h | 6 | ||||
-rw-r--r-- | net/bridge/br_vlan.c | 56 |
3 files changed, 51 insertions, 12 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 82a410a5ef7e..1b797c42ef5f 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c | |||
@@ -388,4 +388,5 @@ void br_dev_setup(struct net_device *dev) | |||
388 | br_netfilter_rtable_init(br); | 388 | br_netfilter_rtable_init(br); |
389 | br_stp_timer_init(br); | 389 | br_stp_timer_init(br); |
390 | br_multicast_init(br); | 390 | br_multicast_init(br); |
391 | br_vlan_init(br); | ||
391 | } | 392 | } |
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 8346e9504cdb..13e570e8b0fc 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h | |||
@@ -294,6 +294,7 @@ struct net_bridge | |||
294 | u32 auto_cnt; | 294 | u32 auto_cnt; |
295 | #ifdef CONFIG_BRIDGE_VLAN_FILTERING | 295 | #ifdef CONFIG_BRIDGE_VLAN_FILTERING |
296 | u8 vlan_enabled; | 296 | u8 vlan_enabled; |
297 | __be16 vlan_proto; | ||
297 | struct net_port_vlans __rcu *vlan_info; | 298 | struct net_port_vlans __rcu *vlan_info; |
298 | #endif | 299 | #endif |
299 | }; | 300 | }; |
@@ -594,6 +595,7 @@ int br_vlan_delete(struct net_bridge *br, u16 vid); | |||
594 | void br_vlan_flush(struct net_bridge *br); | 595 | void br_vlan_flush(struct net_bridge *br); |
595 | bool br_vlan_find(struct net_bridge *br, u16 vid); | 596 | bool br_vlan_find(struct net_bridge *br, u16 vid); |
596 | int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); | 597 | int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); |
598 | void br_vlan_init(struct net_bridge *br); | ||
597 | int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); | 599 | int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); |
598 | int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); | 600 | int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); |
599 | void nbp_vlan_flush(struct net_bridge_port *port); | 601 | void nbp_vlan_flush(struct net_bridge_port *port); |
@@ -689,6 +691,10 @@ static inline bool br_vlan_find(struct net_bridge *br, u16 vid) | |||
689 | return false; | 691 | return false; |
690 | } | 692 | } |
691 | 693 | ||
694 | static inline void br_vlan_init(struct net_bridge *br) | ||
695 | { | ||
696 | } | ||
697 | |||
692 | static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) | 698 | static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) |
693 | { | 699 | { |
694 | return -EOPNOTSUPP; | 700 | return -EOPNOTSUPP; |
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index fcc95390f862..63bd98137a42 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c | |||
@@ -60,7 +60,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) | |||
60 | * that ever changes this code will allow tagged | 60 | * that ever changes this code will allow tagged |
61 | * traffic to enter the bridge. | 61 | * traffic to enter the bridge. |
62 | */ | 62 | */ |
63 | err = vlan_vid_add(dev, htons(ETH_P_8021Q), vid); | 63 | err = vlan_vid_add(dev, br->vlan_proto, vid); |
64 | if (err) | 64 | if (err) |
65 | return err; | 65 | return err; |
66 | } | 66 | } |
@@ -80,7 +80,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) | |||
80 | 80 | ||
81 | out_filt: | 81 | out_filt: |
82 | if (p) | 82 | if (p) |
83 | vlan_vid_del(dev, htons(ETH_P_8021Q), vid); | 83 | vlan_vid_del(dev, br->vlan_proto, vid); |
84 | return err; | 84 | return err; |
85 | } | 85 | } |
86 | 86 | ||
@@ -92,8 +92,10 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid) | |||
92 | __vlan_delete_pvid(v, vid); | 92 | __vlan_delete_pvid(v, vid); |
93 | clear_bit(vid, v->untagged_bitmap); | 93 | clear_bit(vid, v->untagged_bitmap); |
94 | 94 | ||
95 | if (v->port_idx) | 95 | if (v->port_idx) { |
96 | vlan_vid_del(v->parent.port->dev, htons(ETH_P_8021Q), vid); | 96 | struct net_bridge_port *p = v->parent.port; |
97 | vlan_vid_del(p->dev, p->br->vlan_proto, vid); | ||
98 | } | ||
97 | 99 | ||
98 | clear_bit(vid, v->vlan_bitmap); | 100 | clear_bit(vid, v->vlan_bitmap); |
99 | v->num_vlans--; | 101 | v->num_vlans--; |
@@ -158,7 +160,8 @@ out: | |||
158 | bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, | 160 | bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, |
159 | struct sk_buff *skb, u16 *vid) | 161 | struct sk_buff *skb, u16 *vid) |
160 | { | 162 | { |
161 | int err; | 163 | bool tagged; |
164 | __be16 proto; | ||
162 | 165 | ||
163 | /* If VLAN filtering is disabled on the bridge, all packets are | 166 | /* If VLAN filtering is disabled on the bridge, all packets are |
164 | * permitted. | 167 | * permitted. |
@@ -172,19 +175,41 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, | |||
172 | if (!v) | 175 | if (!v) |
173 | goto drop; | 176 | goto drop; |
174 | 177 | ||
178 | proto = br->vlan_proto; | ||
179 | |||
175 | /* If vlan tx offload is disabled on bridge device and frame was | 180 | /* If vlan tx offload is disabled on bridge device and frame was |
176 | * sent from vlan device on the bridge device, it does not have | 181 | * sent from vlan device on the bridge device, it does not have |
177 | * HW accelerated vlan tag. | 182 | * HW accelerated vlan tag. |
178 | */ | 183 | */ |
179 | if (unlikely(!vlan_tx_tag_present(skb) && | 184 | if (unlikely(!vlan_tx_tag_present(skb) && |
180 | (skb->protocol == htons(ETH_P_8021Q) || | 185 | skb->protocol == proto)) { |
181 | skb->protocol == htons(ETH_P_8021AD)))) { | ||
182 | skb = vlan_untag(skb); | 186 | skb = vlan_untag(skb); |
183 | if (unlikely(!skb)) | 187 | if (unlikely(!skb)) |
184 | return false; | 188 | return false; |
185 | } | 189 | } |
186 | 190 | ||
187 | err = br_vlan_get_tag(skb, vid); | 191 | if (!br_vlan_get_tag(skb, vid)) { |
192 | /* Tagged frame */ | ||
193 | if (skb->vlan_proto != proto) { | ||
194 | /* Protocol-mismatch, empty out vlan_tci for new tag */ | ||
195 | skb_push(skb, ETH_HLEN); | ||
196 | skb = __vlan_put_tag(skb, skb->vlan_proto, | ||
197 | vlan_tx_tag_get(skb)); | ||
198 | if (unlikely(!skb)) | ||
199 | return false; | ||
200 | |||
201 | skb_pull(skb, ETH_HLEN); | ||
202 | skb_reset_mac_len(skb); | ||
203 | *vid = 0; | ||
204 | tagged = false; | ||
205 | } else { | ||
206 | tagged = true; | ||
207 | } | ||
208 | } else { | ||
209 | /* Untagged frame */ | ||
210 | tagged = false; | ||
211 | } | ||
212 | |||
188 | if (!*vid) { | 213 | if (!*vid) { |
189 | u16 pvid = br_get_pvid(v); | 214 | u16 pvid = br_get_pvid(v); |
190 | 215 | ||
@@ -199,9 +224,9 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, | |||
199 | * ingress frame is considered to belong to this vlan. | 224 | * ingress frame is considered to belong to this vlan. |
200 | */ | 225 | */ |
201 | *vid = pvid; | 226 | *vid = pvid; |
202 | if (likely(err)) | 227 | if (likely(!tagged)) |
203 | /* Untagged Frame. */ | 228 | /* Untagged Frame. */ |
204 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid); | 229 | __vlan_hwaccel_put_tag(skb, proto, pvid); |
205 | else | 230 | else |
206 | /* Priority-tagged Frame. | 231 | /* Priority-tagged Frame. |
207 | * At this point, We know that skb->vlan_tci had | 232 | * At this point, We know that skb->vlan_tci had |
@@ -254,7 +279,9 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid) | |||
254 | if (!v) | 279 | if (!v) |
255 | return false; | 280 | return false; |
256 | 281 | ||
257 | br_vlan_get_tag(skb, vid); | 282 | if (!br_vlan_get_tag(skb, vid) && skb->vlan_proto != br->vlan_proto) |
283 | *vid = 0; | ||
284 | |||
258 | if (!*vid) { | 285 | if (!*vid) { |
259 | *vid = br_get_pvid(v); | 286 | *vid = br_get_pvid(v); |
260 | if (*vid == VLAN_N_VID) | 287 | if (*vid == VLAN_N_VID) |
@@ -367,6 +394,11 @@ unlock: | |||
367 | return 0; | 394 | return 0; |
368 | } | 395 | } |
369 | 396 | ||
397 | void br_vlan_init(struct net_bridge *br) | ||
398 | { | ||
399 | br->vlan_proto = htons(ETH_P_8021Q); | ||
400 | } | ||
401 | |||
370 | /* Must be protected by RTNL. | 402 | /* Must be protected by RTNL. |
371 | * Must be called with vid in range from 1 to 4094 inclusive. | 403 | * Must be called with vid in range from 1 to 4094 inclusive. |
372 | */ | 404 | */ |
@@ -433,7 +465,7 @@ void nbp_vlan_flush(struct net_bridge_port *port) | |||
433 | return; | 465 | return; |
434 | 466 | ||
435 | for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) | 467 | for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) |
436 | vlan_vid_del(port->dev, htons(ETH_P_8021Q), vid); | 468 | vlan_vid_del(port->dev, port->br->vlan_proto, vid); |
437 | 469 | ||
438 | __vlan_flush(pv); | 470 | __vlan_flush(pv); |
439 | } | 471 | } |