diff options
author | Scott Feldman <sfeldma@gmail.com> | 2015-05-10 12:47:56 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-05-12 18:43:54 -0400 |
commit | 47f8328bb1a4115413e35b9b20d04b061ed544f8 (patch) | |
tree | 914bd885bca27e62e2f1c35c409d4d833b3aeb3c /net/switchdev/switchdev.c | |
parent | 6004c86718998aee1337efd3b087d6e17284632d (diff) |
switchdev: add new switchdev bridge setlink
Add new switchdev_port_bridge_setlink that can be used by drivers
implementing .ndo_bridge_setlink to set switchdev bridge attributes.
Basically turn the raw rtnl_bridge_setlink netlink into switchdev attr
sets. Proper netlink attr policy checking is done on the protinfo part of
the netlink msg.
Currently, for protinfo, only bridge port attrs BR_LEARNING and
BR_LEARNING_SYNC are parsed and passed to port driver.
For afspec, VLAN objs are passed so switchdev driver can set VLANs assigned
to SELF. To illustrate with iproute2 cmd, we have:
bridge vlan add vid 10 dev sw1p1 self master
To add VLAN 10 to port sw1p1 for both the bridge (master) and the device
(self).
Signed-off-by: Scott Feldman <sfeldma@gmail.com>
Acked-by: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/switchdev/switchdev.c')
-rw-r--r-- | net/switchdev/switchdev.c | 151 |
1 files changed, 140 insertions, 11 deletions
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 3d4d99a70b80..b01791a9b56d 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/mutex.h> | 15 | #include <linux/mutex.h> |
16 | #include <linux/notifier.h> | 16 | #include <linux/notifier.h> |
17 | #include <linux/netdevice.h> | 17 | #include <linux/netdevice.h> |
18 | #include <linux/if_bridge.h> | ||
18 | #include <net/ip_fib.h> | 19 | #include <net/ip_fib.h> |
19 | #include <net/switchdev.h> | 20 | #include <net/switchdev.h> |
20 | 21 | ||
@@ -357,28 +358,156 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev, | |||
357 | } | 358 | } |
358 | EXPORT_SYMBOL_GPL(call_switchdev_notifiers); | 359 | EXPORT_SYMBOL_GPL(call_switchdev_notifiers); |
359 | 360 | ||
361 | static int switchdev_port_br_setflag(struct net_device *dev, | ||
362 | struct nlattr *nlattr, | ||
363 | unsigned long brport_flag) | ||
364 | { | ||
365 | struct switchdev_attr attr = { | ||
366 | .id = SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS, | ||
367 | }; | ||
368 | u8 flag = nla_get_u8(nlattr); | ||
369 | int err; | ||
370 | |||
371 | err = switchdev_port_attr_get(dev, &attr); | ||
372 | if (err) | ||
373 | return err; | ||
374 | |||
375 | if (flag) | ||
376 | attr.brport_flags |= brport_flag; | ||
377 | else | ||
378 | attr.brport_flags &= ~brport_flag; | ||
379 | |||
380 | return switchdev_port_attr_set(dev, &attr); | ||
381 | } | ||
382 | |||
383 | static const struct nla_policy | ||
384 | switchdev_port_bridge_policy[IFLA_BRPORT_MAX + 1] = { | ||
385 | [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, | ||
386 | [IFLA_BRPORT_COST] = { .type = NLA_U32 }, | ||
387 | [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 }, | ||
388 | [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, | ||
389 | [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, | ||
390 | [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, | ||
391 | [IFLA_BRPORT_FAST_LEAVE] = { .type = NLA_U8 }, | ||
392 | [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 }, | ||
393 | [IFLA_BRPORT_LEARNING_SYNC] = { .type = NLA_U8 }, | ||
394 | [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 }, | ||
395 | }; | ||
396 | |||
397 | static int switchdev_port_br_setlink_protinfo(struct net_device *dev, | ||
398 | struct nlattr *protinfo) | ||
399 | { | ||
400 | struct nlattr *attr; | ||
401 | int rem; | ||
402 | int err; | ||
403 | |||
404 | err = nla_validate_nested(protinfo, IFLA_BRPORT_MAX, | ||
405 | switchdev_port_bridge_policy); | ||
406 | if (err) | ||
407 | return err; | ||
408 | |||
409 | nla_for_each_nested(attr, protinfo, rem) { | ||
410 | switch (nla_type(attr)) { | ||
411 | case IFLA_BRPORT_LEARNING: | ||
412 | err = switchdev_port_br_setflag(dev, attr, | ||
413 | BR_LEARNING); | ||
414 | break; | ||
415 | case IFLA_BRPORT_LEARNING_SYNC: | ||
416 | err = switchdev_port_br_setflag(dev, attr, | ||
417 | BR_LEARNING_SYNC); | ||
418 | break; | ||
419 | default: | ||
420 | err = -EOPNOTSUPP; | ||
421 | break; | ||
422 | } | ||
423 | if (err) | ||
424 | return err; | ||
425 | } | ||
426 | |||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | static int switchdev_port_br_afspec(struct net_device *dev, | ||
431 | struct nlattr *afspec, | ||
432 | int (*f)(struct net_device *dev, | ||
433 | struct switchdev_obj *obj)) | ||
434 | { | ||
435 | struct nlattr *attr; | ||
436 | struct bridge_vlan_info *vinfo; | ||
437 | struct switchdev_obj obj = { | ||
438 | .id = SWITCHDEV_OBJ_PORT_VLAN, | ||
439 | }; | ||
440 | int rem; | ||
441 | int err; | ||
442 | |||
443 | nla_for_each_nested(attr, afspec, rem) { | ||
444 | if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO) | ||
445 | continue; | ||
446 | if (nla_len(attr) != sizeof(struct bridge_vlan_info)) | ||
447 | return -EINVAL; | ||
448 | vinfo = nla_data(attr); | ||
449 | obj.vlan.flags = vinfo->flags; | ||
450 | if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { | ||
451 | if (obj.vlan.vid_start) | ||
452 | return -EINVAL; | ||
453 | obj.vlan.vid_start = vinfo->vid; | ||
454 | } else if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) { | ||
455 | if (!obj.vlan.vid_start) | ||
456 | return -EINVAL; | ||
457 | obj.vlan.vid_end = vinfo->vid; | ||
458 | if (obj.vlan.vid_end <= obj.vlan.vid_start) | ||
459 | return -EINVAL; | ||
460 | err = f(dev, &obj); | ||
461 | if (err) | ||
462 | return err; | ||
463 | memset(&obj.vlan, 0, sizeof(obj.vlan)); | ||
464 | } else { | ||
465 | if (obj.vlan.vid_start) | ||
466 | return -EINVAL; | ||
467 | obj.vlan.vid_start = vinfo->vid; | ||
468 | obj.vlan.vid_end = vinfo->vid; | ||
469 | err = f(dev, &obj); | ||
470 | if (err) | ||
471 | return err; | ||
472 | memset(&obj.vlan, 0, sizeof(obj.vlan)); | ||
473 | } | ||
474 | } | ||
475 | |||
476 | return 0; | ||
477 | } | ||
478 | |||
360 | /** | 479 | /** |
361 | * switchdev_port_bridge_setlink - Notify switch device port of bridge | 480 | * switchdev_port_bridge_setlink - Set bridge port attributes |
362 | * port attributes | ||
363 | * | 481 | * |
364 | * @dev: port device | 482 | * @dev: port device |
365 | * @nlh: netlink msg with bridge port attributes | 483 | * @nlh: netlink header |
366 | * @flags: bridge setlink flags | 484 | * @flags: netlink flags |
367 | * | 485 | * |
368 | * Notify switch device port of bridge port attributes | 486 | * Called for SELF on rtnl_bridge_setlink to set bridge port |
487 | * attributes. | ||
369 | */ | 488 | */ |
370 | int switchdev_port_bridge_setlink(struct net_device *dev, | 489 | int switchdev_port_bridge_setlink(struct net_device *dev, |
371 | struct nlmsghdr *nlh, u16 flags) | 490 | struct nlmsghdr *nlh, u16 flags) |
372 | { | 491 | { |
373 | const struct net_device_ops *ops = dev->netdev_ops; | 492 | struct nlattr *protinfo; |
493 | struct nlattr *afspec; | ||
494 | int err = 0; | ||
374 | 495 | ||
375 | if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) | 496 | protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), |
376 | return 0; | 497 | IFLA_PROTINFO); |
498 | if (protinfo) { | ||
499 | err = switchdev_port_br_setlink_protinfo(dev, protinfo); | ||
500 | if (err) | ||
501 | return err; | ||
502 | } | ||
377 | 503 | ||
378 | if (!ops->ndo_bridge_setlink) | 504 | afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), |
379 | return -EOPNOTSUPP; | 505 | IFLA_AF_SPEC); |
506 | if (afspec) | ||
507 | err = switchdev_port_br_afspec(dev, afspec, | ||
508 | switchdev_port_obj_add); | ||
380 | 509 | ||
381 | return ops->ndo_bridge_setlink(dev, nlh, flags); | 510 | return err; |
382 | } | 511 | } |
383 | EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink); | 512 | EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink); |
384 | 513 | ||