aboutsummaryrefslogtreecommitdiffstats
path: root/net/switchdev/switchdev.c
diff options
context:
space:
mode:
authorScott Feldman <sfeldma@gmail.com>2015-05-10 12:47:56 -0400
committerDavid S. Miller <davem@davemloft.net>2015-05-12 18:43:54 -0400
commit47f8328bb1a4115413e35b9b20d04b061ed544f8 (patch)
tree914bd885bca27e62e2f1c35c409d4d833b3aeb3c /net/switchdev/switchdev.c
parent6004c86718998aee1337efd3b087d6e17284632d (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.c151
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}
358EXPORT_SYMBOL_GPL(call_switchdev_notifiers); 359EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
359 360
361static 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
383static const struct nla_policy
384switchdev_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
397static 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
430static 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 */
370int switchdev_port_bridge_setlink(struct net_device *dev, 489int 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}
383EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink); 512EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink);
384 513