Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Scott Feldman and committed by
David S. Miller
7d4f8d87 3e3a78b4

+172 -9
+1 -1
drivers/net/ethernet/emulex/benet/be_main.c
··· 5096 5096 return ndo_dflt_bridge_getlink(skb, pid, seq, dev, 5097 5097 hsw_mode == PORT_FWD_TYPE_VEPA ? 5098 5098 BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB, 5099 - 0, 0, nlflags); 5099 + 0, 0, nlflags, filter_mask, NULL); 5100 5100 } 5101 5101 5102 5102 #ifdef CONFIG_BE2NET_VXLAN
+2 -2
drivers/net/ethernet/intel/i40e/i40e_main.c
··· 8069 8069 #ifdef HAVE_BRIDGE_FILTER 8070 8070 static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, 8071 8071 struct net_device *dev, 8072 - u32 __always_unused filter_mask, int nlflags) 8072 + u32 filter_mask, int nlflags) 8073 8073 #else 8074 8074 static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, 8075 8075 struct net_device *dev, int nlflags) ··· 8095 8095 return 0; 8096 8096 8097 8097 return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode, 8098 - nlflags); 8098 + nlflags, 0, 0, filter_mask, NULL); 8099 8099 } 8100 8100 #endif /* HAVE_BRIDGE_ATTRIBS */ 8101 8101
+2 -1
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
··· 8095 8095 return 0; 8096 8096 8097 8097 return ndo_dflt_bridge_getlink(skb, pid, seq, dev, 8098 - adapter->bridge_mode, 0, 0, nlflags); 8098 + adapter->bridge_mode, 0, 0, nlflags, 8099 + filter_mask, NULL); 8099 8100 } 8100 8101 8101 8102 static void *ixgbe_fwd_add(struct net_device *pdev, struct net_device *vdev)
+25
drivers/net/ethernet/rocker/rocker.c
··· 4456 4456 return err; 4457 4457 } 4458 4458 4459 + static int rocker_port_vlan_dump(const struct rocker_port *rocker_port, 4460 + struct switchdev_obj *obj) 4461 + { 4462 + struct switchdev_obj_vlan *vlan = &obj->u.vlan; 4463 + u16 vid; 4464 + int err = 0; 4465 + 4466 + for (vid = 1; vid < VLAN_N_VID; vid++) { 4467 + if (!test_bit(vid, rocker_port->vlan_bitmap)) 4468 + continue; 4469 + vlan->flags = 0; 4470 + if (rocker_vlan_id_is_internal(htons(vid))) 4471 + vlan->flags |= BRIDGE_VLAN_INFO_PVID; 4472 + vlan->vid_begin = vlan->vid_end = vid; 4473 + err = obj->cb(rocker_port->dev, obj); 4474 + if (err) 4475 + break; 4476 + } 4477 + 4478 + return err; 4479 + } 4480 + 4459 4481 static int rocker_port_obj_dump(struct net_device *dev, 4460 4482 struct switchdev_obj *obj) 4461 4483 { ··· 4487 4465 switch (obj->id) { 4488 4466 case SWITCHDEV_OBJ_PORT_FDB: 4489 4467 err = rocker_port_fdb_dump(rocker_port, obj); 4468 + break; 4469 + case SWITCHDEV_OBJ_PORT_VLAN: 4470 + err = rocker_port_vlan_dump(rocker_port, obj); 4490 4471 break; 4491 4472 default: 4492 4473 err = -EOPNOTSUPP;
+5 -1
include/linux/rtnetlink.h
··· 114 114 115 115 extern int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, 116 116 struct net_device *dev, u16 mode, 117 - u32 flags, u32 mask, int nlflags); 117 + u32 flags, u32 mask, int nlflags, 118 + u32 filter_mask, 119 + int (*vlan_fill)(struct sk_buff *skb, 120 + struct net_device *dev, 121 + u32 filter_mask)); 118 122 #endif /* __LINUX_RTNETLINK_H */
+15 -3
net/core/rtnetlink.c
··· 2908 2908 2909 2909 int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, 2910 2910 struct net_device *dev, u16 mode, 2911 - u32 flags, u32 mask, int nlflags) 2911 + u32 flags, u32 mask, int nlflags, 2912 + u32 filter_mask, 2913 + int (*vlan_fill)(struct sk_buff *skb, 2914 + struct net_device *dev, 2915 + u32 filter_mask)) 2912 2916 { 2913 2917 struct nlmsghdr *nlh; 2914 2918 struct ifinfomsg *ifm; ··· 2920 2916 struct nlattr *protinfo; 2921 2917 u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; 2922 2918 struct net_device *br_dev = netdev_master_upper_dev_get(dev); 2919 + int err = 0; 2923 2920 2924 2921 nlh = nlmsg_put(skb, pid, seq, RTM_NEWLINK, sizeof(*ifm), nlflags); 2925 2922 if (nlh == NULL) ··· 2961 2956 goto nla_put_failure; 2962 2957 } 2963 2958 } 2959 + if (vlan_fill) { 2960 + err = vlan_fill(skb, dev, filter_mask); 2961 + if (err) { 2962 + nla_nest_cancel(skb, br_afspec); 2963 + goto nla_put_failure; 2964 + } 2965 + } 2964 2966 nla_nest_end(skb, br_afspec); 2965 2967 2966 2968 protinfo = nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED); ··· 3001 2989 return 0; 3002 2990 nla_put_failure: 3003 2991 nlmsg_cancel(skb, nlh); 3004 - return -EMSGSIZE; 2992 + return err ? err : -EMSGSIZE; 3005 2993 } 3006 - EXPORT_SYMBOL(ndo_dflt_bridge_getlink); 2994 + EXPORT_SYMBOL_GPL(ndo_dflt_bridge_getlink); 3007 2995 3008 2996 static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) 3009 2997 {
+122 -1
net/switchdev/switchdev.c
··· 391 391 } 392 392 EXPORT_SYMBOL_GPL(call_switchdev_notifiers); 393 393 394 + struct 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 + 403 + static 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 + 434 + static 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 + 487 + static 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 + 510 + err_out: 511 + return err == -EOPNOTSUPP ? 0 : err; 512 + } 513 + 394 514 /** 395 515 * switchdev_port_bridge_getlink - Get bridge port attributes 396 516 * ··· 535 415 return err; 536 416 537 417 return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode, 538 - attr.u.brport_flags, mask, nlflags); 418 + attr.u.brport_flags, mask, nlflags, 419 + filter_mask, switchdev_port_vlan_fill); 539 420 } 540 421 EXPORT_SYMBOL_GPL(switchdev_port_bridge_getlink); 541 422