aboutsummaryrefslogtreecommitdiffstats
path: root/net/switchdev
diff options
context:
space:
mode:
authorScott Feldman <sfeldma@gmail.com>2015-06-22 03:27:17 -0400
committerDavid S. Miller <davem@davemloft.net>2015-06-23 09:56:18 -0400
commit7d4f8d871ab15bd50a5771382ca2c9355b38d73c (patch)
treedc2b70efc634d14085fc8e2abe0050e7039dc7f7 /net/switchdev
parent3e3a78b49508e58f798cf519876bbb9ca0f931af (diff)
switchdev; add VLAN support for port's bridge_getlink
One more missing piece of the puzzle. Add vlan dump support to switchdev port's bridge_getlink. iproute2 "bridge vlan show" cmd already knows how to show the vlans installed on the bridge and the device , but (until now) no one implemented the port vlan part of the netlink PF_BRIDGE:RTM_GETLINK msg. Before this patch, "bridge vlan show": $ bridge -c vlan show port vlan ids sw1p1 30-34 << bridge side vlans 57 sw1p1 << device side vlans (missing) sw1p2 57 sw1p2 sw1p3 sw1p4 br0 None (When the port is bridged, the output repeats the vlan list for the vlans on the bridge side of the port and the vlans on the device side of the port. The listing above show no vlans for the device side even though they are installed). After this patch: $ bridge -c vlan show port vlan ids sw1p1 30-34 << bridge side vlan 57 sw1p1 30-34 << device side vlans 57 3840 PVID sw1p2 57 sw1p2 57 3840 PVID sw1p3 3842 PVID sw1p4 3843 PVID br0 None I re-used ndo_dflt_bridge_getlink to add vlan fill call-back func. switchdev support adds an obj dump for VLAN objects, using the same call-back scheme as FDB dump. Support included for both compressed and un-compressed vlan dumps. Signed-off-by: Scott Feldman <sfeldma@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/switchdev')
-rw-r--r--net/switchdev/switchdev.c123
1 files changed, 122 insertions, 1 deletions
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index 448d9199cea2..3e4b35a0c8cb 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -391,6 +391,126 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
391} 391}
392EXPORT_SYMBOL_GPL(call_switchdev_notifiers); 392EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
393 393
394struct switchdev_vlan_dump {
395 struct switchdev_obj obj;
396 struct sk_buff *skb;
397 u32 filter_mask;
398 u16 flags;
399 u16 begin;
400 u16 end;
401};
402
403static int switchdev_port_vlan_dump_put(struct net_device *dev,
404 struct switchdev_vlan_dump *dump)
405{
406 struct bridge_vlan_info vinfo;
407
408 vinfo.flags = dump->flags;
409
410 if (dump->begin == 0 && dump->end == 0) {
411 return 0;
412 } else if (dump->begin == dump->end) {
413 vinfo.vid = dump->begin;
414 if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO,
415 sizeof(vinfo), &vinfo))
416 return -EMSGSIZE;
417 } else {
418 vinfo.vid = dump->begin;
419 vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
420 if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO,
421 sizeof(vinfo), &vinfo))
422 return -EMSGSIZE;
423 vinfo.vid = dump->end;
424 vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
425 vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
426 if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO,
427 sizeof(vinfo), &vinfo))
428 return -EMSGSIZE;
429 }
430
431 return 0;
432}
433
434static int switchdev_port_vlan_dump_cb(struct net_device *dev,
435 struct switchdev_obj *obj)
436{
437 struct switchdev_vlan_dump *dump =
438 container_of(obj, struct switchdev_vlan_dump, obj);
439 struct switchdev_obj_vlan *vlan = &dump->obj.u.vlan;
440 int err = 0;
441
442 if (vlan->vid_begin > vlan->vid_end)
443 return -EINVAL;
444
445 if (dump->filter_mask & RTEXT_FILTER_BRVLAN) {
446 dump->flags = vlan->flags;
447 for (dump->begin = dump->end = vlan->vid_begin;
448 dump->begin <= vlan->vid_end;
449 dump->begin++, dump->end++) {
450 err = switchdev_port_vlan_dump_put(dev, dump);
451 if (err)
452 return err;
453 }
454 } else if (dump->filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED) {
455 if (dump->begin > vlan->vid_begin &&
456 dump->begin >= vlan->vid_end) {
457 if ((dump->begin - 1) == vlan->vid_end &&
458 dump->flags == vlan->flags) {
459 /* prepend */
460 dump->begin = vlan->vid_begin;
461 } else {
462 err = switchdev_port_vlan_dump_put(dev, dump);
463 dump->flags = vlan->flags;
464 dump->begin = vlan->vid_begin;
465 dump->end = vlan->vid_end;
466 }
467 } else if (dump->end <= vlan->vid_begin &&
468 dump->end < vlan->vid_end) {
469 if ((dump->end + 1) == vlan->vid_begin &&
470 dump->flags == vlan->flags) {
471 /* append */
472 dump->end = vlan->vid_end;
473 } else {
474 err = switchdev_port_vlan_dump_put(dev, dump);
475 dump->flags = vlan->flags;
476 dump->begin = vlan->vid_begin;
477 dump->end = vlan->vid_end;
478 }
479 } else {
480 err = -EINVAL;
481 }
482 }
483
484 return err;
485}
486
487static int switchdev_port_vlan_fill(struct sk_buff *skb, struct net_device *dev,
488 u32 filter_mask)
489{
490 struct switchdev_vlan_dump dump = {
491 .obj = {
492 .id = SWITCHDEV_OBJ_PORT_VLAN,
493 .cb = switchdev_port_vlan_dump_cb,
494 },
495 .skb = skb,
496 .filter_mask = filter_mask,
497 };
498 int err = 0;
499
500 if ((filter_mask & RTEXT_FILTER_BRVLAN) ||
501 (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) {
502 err = switchdev_port_obj_dump(dev, &dump.obj);
503 if (err)
504 goto err_out;
505 if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
506 /* last one */
507 err = switchdev_port_vlan_dump_put(dev, &dump);
508 }
509
510err_out:
511 return err == -EOPNOTSUPP ? 0 : err;
512}
513
394/** 514/**
395 * switchdev_port_bridge_getlink - Get bridge port attributes 515 * switchdev_port_bridge_getlink - Get bridge port attributes
396 * 516 *
@@ -415,7 +535,8 @@ int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
415 return err; 535 return err;
416 536
417 return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode, 537 return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode,
418 attr.u.brport_flags, mask, nlflags); 538 attr.u.brport_flags, mask, nlflags,
539 filter_mask, switchdev_port_vlan_fill);
419} 540}
420EXPORT_SYMBOL_GPL(switchdev_port_bridge_getlink); 541EXPORT_SYMBOL_GPL(switchdev_port_bridge_getlink);
421 542