diff options
Diffstat (limited to 'net/bridge/br_vlan.c')
-rw-r--r-- | net/bridge/br_vlan.c | 97 |
1 files changed, 97 insertions, 0 deletions
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); |