aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorToshiaki Makita <makita.toshiaki@lab.ntt.co.jp>2014-06-10 07:59:25 -0400
committerDavid S. Miller <davem@davemloft.net>2014-06-11 18:22:53 -0400
commit204177f3f30c2dbd2db0aa62b5e9cf9029786450 (patch)
tree906cf482a40b1aaa120f2be009a0a959a9e48608 /net
parentf2808d226f4efe15a0ea44697a3525176d6c8eae (diff)
bridge: Support 802.1ad vlan filtering
This enables us to change the vlan protocol for vlan filtering. We come to be able to filter frames on the basis of 802.1ad vlan tags through a bridge. This also changes br->group_addr if it has not been set by user. This is needed for an 802.1ad bridge. (See IEEE 802.1Q-2011 8.13.5.) Furthermore, this sets br->group_fwd_mask_required so that an 802.1ad bridge can forward the Nearest Customer Bridge group addresses except for br->group_addr, which should be passed to higher layer. To change the vlan protocol, write a protocol in sysfs: # echo 0x88a8 > /sys/class/net/br0/bridge/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')
-rw-r--r--net/bridge/br_private.h7
-rw-r--r--net/bridge/br_sysfs_br.c26
-rw-r--r--net/bridge/br_vlan.c97
3 files changed, 130 insertions, 0 deletions
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 4eba348a985b..23caf5b0309e 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -243,6 +243,7 @@ struct net_bridge
243 unsigned long bridge_forward_delay; 243 unsigned long bridge_forward_delay;
244 244
245 u8 group_addr[ETH_ALEN]; 245 u8 group_addr[ETH_ALEN];
246 bool group_addr_set;
246 u16 root_port; 247 u16 root_port;
247 248
248 enum { 249 enum {
@@ -597,7 +598,9 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags);
597int br_vlan_delete(struct net_bridge *br, u16 vid); 598int br_vlan_delete(struct net_bridge *br, u16 vid);
598void br_vlan_flush(struct net_bridge *br); 599void br_vlan_flush(struct net_bridge *br);
599bool br_vlan_find(struct net_bridge *br, u16 vid); 600bool br_vlan_find(struct net_bridge *br, u16 vid);
601void br_recalculate_fwd_mask(struct net_bridge *br);
600int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); 602int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
603int br_vlan_set_proto(struct net_bridge *br, unsigned long val);
601void br_vlan_init(struct net_bridge *br); 604void br_vlan_init(struct net_bridge *br);
602int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); 605int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags);
603int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); 606int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
@@ -694,6 +697,10 @@ static inline bool br_vlan_find(struct net_bridge *br, u16 vid)
694 return false; 697 return false;
695} 698}
696 699
700static inline void br_recalculate_fwd_mask(struct net_bridge *br)
701{
702}
703
697static inline void br_vlan_init(struct net_bridge *br) 704static inline void br_vlan_init(struct net_bridge *br)
698{ 705{
699} 706}
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 8dac65552f19..c9e2572b15f4 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -312,10 +312,19 @@ static ssize_t group_addr_store(struct device *d,
312 new_addr[5] == 3) /* 802.1X PAE address */ 312 new_addr[5] == 3) /* 802.1X PAE address */
313 return -EINVAL; 313 return -EINVAL;
314 314
315 if (!rtnl_trylock())
316 return restart_syscall();
317
315 spin_lock_bh(&br->lock); 318 spin_lock_bh(&br->lock);
316 for (i = 0; i < 6; i++) 319 for (i = 0; i < 6; i++)
317 br->group_addr[i] = new_addr[i]; 320 br->group_addr[i] = new_addr[i];
318 spin_unlock_bh(&br->lock); 321 spin_unlock_bh(&br->lock);
322
323 br->group_addr_set = true;
324 br_recalculate_fwd_mask(br);
325
326 rtnl_unlock();
327
319 return len; 328 return len;
320} 329}
321 330
@@ -700,6 +709,22 @@ static ssize_t vlan_filtering_store(struct device *d,
700 return store_bridge_parm(d, buf, len, br_vlan_filter_toggle); 709 return store_bridge_parm(d, buf, len, br_vlan_filter_toggle);
701} 710}
702static DEVICE_ATTR_RW(vlan_filtering); 711static DEVICE_ATTR_RW(vlan_filtering);
712
713static ssize_t vlan_protocol_show(struct device *d,
714 struct device_attribute *attr,
715 char *buf)
716{
717 struct net_bridge *br = to_bridge(d);
718 return sprintf(buf, "%#06x\n", ntohs(br->vlan_proto));
719}
720
721static ssize_t vlan_protocol_store(struct device *d,
722 struct device_attribute *attr,
723 const char *buf, size_t len)
724{
725 return store_bridge_parm(d, buf, len, br_vlan_set_proto);
726}
727static DEVICE_ATTR_RW(vlan_protocol);
703#endif 728#endif
704 729
705static struct attribute *bridge_attrs[] = { 730static struct attribute *bridge_attrs[] = {
@@ -745,6 +770,7 @@ static struct attribute *bridge_attrs[] = {
745#endif 770#endif
746#ifdef CONFIG_BRIDGE_VLAN_FILTERING 771#ifdef CONFIG_BRIDGE_VLAN_FILTERING
747 &dev_attr_vlan_filtering.attr, 772 &dev_attr_vlan_filtering.attr,
773 &dev_attr_vlan_protocol.attr,
748#endif 774#endif
749 NULL 775 NULL
750}; 776};
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 63bd98137a42..2b2774fe0703 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -378,6 +378,33 @@ out:
378 return found; 378 return found;
379} 379}
380 380
381/* Must be protected by RTNL. */
382static void recalculate_group_addr(struct net_bridge *br)
383{
384 if (br->group_addr_set)
385 return;
386
387 spin_lock_bh(&br->lock);
388 if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q)) {
389 /* Bridge Group Address */
390 br->group_addr[5] = 0x00;
391 } else { /* vlan_enabled && ETH_P_8021AD */
392 /* Provider Bridge Group Address */
393 br->group_addr[5] = 0x08;
394 }
395 spin_unlock_bh(&br->lock);
396}
397
398/* Must be protected by RTNL. */
399void br_recalculate_fwd_mask(struct net_bridge *br)
400{
401 if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q))
402 br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
403 else /* vlan_enabled && ETH_P_8021AD */
404 br->group_fwd_mask_required = BR_GROUPFWD_8021AD &
405 ~(1u << br->group_addr[5]);
406}
407
381int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) 408int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
382{ 409{
383 if (!rtnl_trylock()) 410 if (!rtnl_trylock())
@@ -388,12 +415,82 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
388 415
389 br->vlan_enabled = val; 416 br->vlan_enabled = val;
390 br_manage_promisc(br); 417 br_manage_promisc(br);
418 recalculate_group_addr(br);
419 br_recalculate_fwd_mask(br);
391 420
392unlock: 421unlock:
393 rtnl_unlock(); 422 rtnl_unlock();
394 return 0; 423 return 0;
395} 424}
396 425
426int br_vlan_set_proto(struct net_bridge *br, unsigned long val)
427{
428 int err = 0;
429 struct net_bridge_port *p;
430 struct net_port_vlans *pv;
431 __be16 proto, oldproto;
432 u16 vid, errvid;
433
434 if (val != ETH_P_8021Q && val != ETH_P_8021AD)
435 return -EPROTONOSUPPORT;
436
437 if (!rtnl_trylock())
438 return restart_syscall();
439
440 proto = htons(val);
441 if (br->vlan_proto == proto)
442 goto unlock;
443
444 /* Add VLANs for the new proto to the device filter. */
445 list_for_each_entry(p, &br->port_list, list) {
446 pv = rtnl_dereference(p->vlan_info);
447 if (!pv)
448 continue;
449
450 for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
451 err = vlan_vid_add(p->dev, proto, vid);
452 if (err)
453 goto err_filt;
454 }
455 }
456
457 oldproto = br->vlan_proto;
458 br->vlan_proto = proto;
459
460 recalculate_group_addr(br);
461 br_recalculate_fwd_mask(br);
462
463 /* Delete VLANs for the old proto from the device filter. */
464 list_for_each_entry(p, &br->port_list, list) {
465 pv = rtnl_dereference(p->vlan_info);
466 if (!pv)
467 continue;
468
469 for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
470 vlan_vid_del(p->dev, oldproto, vid);
471 }
472
473unlock:
474 rtnl_unlock();
475 return err;
476
477err_filt:
478 errvid = vid;
479 for_each_set_bit(vid, pv->vlan_bitmap, errvid)
480 vlan_vid_del(p->dev, proto, vid);
481
482 list_for_each_entry_continue_reverse(p, &br->port_list, list) {
483 pv = rtnl_dereference(p->vlan_info);
484 if (!pv)
485 continue;
486
487 for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
488 vlan_vid_del(p->dev, proto, vid);
489 }
490
491 goto unlock;
492}
493
397void br_vlan_init(struct net_bridge *br) 494void br_vlan_init(struct net_bridge *br)
398{ 495{
399 br->vlan_proto = htons(ETH_P_8021Q); 496 br->vlan_proto = htons(ETH_P_8021Q);