diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/br_private.h | 7 | ||||
-rw-r--r-- | net/bridge/br_sysfs_br.c | 26 | ||||
-rw-r--r-- | net/bridge/br_vlan.c | 97 |
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); | |||
597 | int br_vlan_delete(struct net_bridge *br, u16 vid); | 598 | int br_vlan_delete(struct net_bridge *br, u16 vid); |
598 | void br_vlan_flush(struct net_bridge *br); | 599 | void br_vlan_flush(struct net_bridge *br); |
599 | bool br_vlan_find(struct net_bridge *br, u16 vid); | 600 | bool br_vlan_find(struct net_bridge *br, u16 vid); |
601 | void br_recalculate_fwd_mask(struct net_bridge *br); | ||
600 | int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); | 602 | int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); |
603 | int br_vlan_set_proto(struct net_bridge *br, unsigned long val); | ||
601 | void br_vlan_init(struct net_bridge *br); | 604 | void br_vlan_init(struct net_bridge *br); |
602 | int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); | 605 | int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); |
603 | int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); | 606 | int 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 | ||
700 | static inline void br_recalculate_fwd_mask(struct net_bridge *br) | ||
701 | { | ||
702 | } | ||
703 | |||
697 | static inline void br_vlan_init(struct net_bridge *br) | 704 | static 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 | } |
702 | static DEVICE_ATTR_RW(vlan_filtering); | 711 | static DEVICE_ATTR_RW(vlan_filtering); |
712 | |||
713 | static 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 | |||
721 | static 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 | } | ||
727 | static DEVICE_ATTR_RW(vlan_protocol); | ||
703 | #endif | 728 | #endif |
704 | 729 | ||
705 | static struct attribute *bridge_attrs[] = { | 730 | static 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. */ | ||
382 | static 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. */ | ||
399 | void 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 | |||
381 | int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) | 408 | int 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 | ||
392 | unlock: | 421 | unlock: |
393 | rtnl_unlock(); | 422 | rtnl_unlock(); |
394 | return 0; | 423 | return 0; |
395 | } | 424 | } |
396 | 425 | ||
426 | int 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 | |||
473 | unlock: | ||
474 | rtnl_unlock(); | ||
475 | return err; | ||
476 | |||
477 | err_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 | |||
397 | void br_vlan_init(struct net_bridge *br) | 494 | void 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); |