aboutsummaryrefslogtreecommitdiffstats
path: root/net/switchdev/switchdev.c
diff options
context:
space:
mode:
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