forked from Minki/linux
bridge: Add netlink interface to configure vlans on bridge ports
Add a netlink interface to add and remove vlan configuration on bridge port. The interface uses the RTM_SETLINK message and encodes the vlan configuration inside the IFLA_AF_SPEC. It is possble to include multiple vlans to either add or remove in a single message. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
85f46c6bae
commit
407af3299e
@ -1020,6 +1020,8 @@ struct net_device_ops {
|
||||
int (*ndo_bridge_getlink)(struct sk_buff *skb,
|
||||
u32 pid, u32 seq,
|
||||
struct net_device *dev);
|
||||
int (*ndo_bridge_dellink)(struct net_device *dev,
|
||||
struct nlmsghdr *nlh);
|
||||
int (*ndo_change_carrier)(struct net_device *dev,
|
||||
bool new_carrier);
|
||||
};
|
||||
|
@ -108,15 +108,24 @@ struct __fdb_entry {
|
||||
* [IFLA_AF_SPEC] = {
|
||||
* [IFLA_BRIDGE_FLAGS]
|
||||
* [IFLA_BRIDGE_MODE]
|
||||
* [IFLA_BRIDGE_VLAN_INFO]
|
||||
* }
|
||||
*/
|
||||
enum {
|
||||
IFLA_BRIDGE_FLAGS,
|
||||
IFLA_BRIDGE_MODE,
|
||||
IFLA_BRIDGE_VLAN_INFO,
|
||||
__IFLA_BRIDGE_MAX,
|
||||
};
|
||||
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
|
||||
|
||||
#define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */
|
||||
|
||||
struct bridge_vlan_info {
|
||||
u16 flags;
|
||||
u16 vid;
|
||||
};
|
||||
|
||||
/* Bridge multicast database attributes
|
||||
* [MDBA_MDB] = {
|
||||
* [MDBA_MDB_ENTRY] = {
|
||||
|
@ -316,6 +316,7 @@ static const struct net_device_ops br_netdev_ops = {
|
||||
.ndo_fdb_dump = br_fdb_dump,
|
||||
.ndo_bridge_getlink = br_getlink,
|
||||
.ndo_bridge_setlink = br_setlink,
|
||||
.ndo_bridge_dellink = br_dellink,
|
||||
};
|
||||
|
||||
static void br_dev_free(struct net_device *dev)
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
#include "br_private.h"
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <net/rtnetlink.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/sock.h>
|
||||
#include <uapi/linux/if_bridge.h>
|
||||
|
||||
#include "br_private.h"
|
||||
#include "br_private_stp.h"
|
||||
@ -119,10 +120,14 @@ nla_put_failure:
|
||||
*/
|
||||
void br_ifinfo_notify(int event, struct net_bridge_port *port)
|
||||
{
|
||||
struct net *net = dev_net(port->dev);
|
||||
struct net *net;
|
||||
struct sk_buff *skb;
|
||||
int err = -ENOBUFS;
|
||||
|
||||
if (!port)
|
||||
return;
|
||||
|
||||
net = dev_net(port->dev);
|
||||
br_debug(port->br, "port %u(%s) event %d\n",
|
||||
(unsigned int)port->port_no, port->dev->name, event);
|
||||
|
||||
@ -144,6 +149,7 @@ errout:
|
||||
rtnl_set_sk_err(net, RTNLGRP_LINK, err);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dump information about all ports, in response to GETLINK
|
||||
*/
|
||||
@ -162,6 +168,64 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
const struct nla_policy ifla_br_policy[IFLA_MAX+1] = {
|
||||
[IFLA_BRIDGE_FLAGS] = { .type = NLA_U16 },
|
||||
[IFLA_BRIDGE_MODE] = { .type = NLA_U16 },
|
||||
[IFLA_BRIDGE_VLAN_INFO] = { .type = NLA_BINARY,
|
||||
.len = sizeof(struct bridge_vlan_info), },
|
||||
};
|
||||
|
||||
static int br_afspec(struct net_bridge *br,
|
||||
struct net_bridge_port *p,
|
||||
struct nlattr *af_spec,
|
||||
int cmd)
|
||||
{
|
||||
struct nlattr *tb[IFLA_BRIDGE_MAX+1];
|
||||
int err = 0;
|
||||
|
||||
err = nla_parse_nested(tb, IFLA_BRIDGE_MAX, af_spec, ifla_br_policy);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (tb[IFLA_BRIDGE_VLAN_INFO]) {
|
||||
struct bridge_vlan_info *vinfo;
|
||||
|
||||
vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]);
|
||||
|
||||
if (vinfo->vid >= VLAN_N_VID)
|
||||
return -EINVAL;
|
||||
|
||||
switch (cmd) {
|
||||
case RTM_SETLINK:
|
||||
if (p) {
|
||||
err = nbp_vlan_add(p, vinfo->vid);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
|
||||
err = br_vlan_add(p->br, vinfo->vid);
|
||||
} else
|
||||
err = br_vlan_add(br, vinfo->vid);
|
||||
|
||||
if (err)
|
||||
break;
|
||||
|
||||
break;
|
||||
|
||||
case RTM_DELLINK:
|
||||
if (p) {
|
||||
nbp_vlan_delete(p, vinfo->vid);
|
||||
if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
|
||||
br_vlan_delete(p->br, vinfo->vid);
|
||||
} else
|
||||
br_vlan_delete(br, vinfo->vid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = {
|
||||
[IFLA_BRPORT_STATE] = { .type = NLA_U8 },
|
||||
[IFLA_BRPORT_COST] = { .type = NLA_U32 },
|
||||
@ -241,6 +305,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
|
||||
{
|
||||
struct ifinfomsg *ifm;
|
||||
struct nlattr *protinfo;
|
||||
struct nlattr *afspec;
|
||||
struct net_bridge_port *p;
|
||||
struct nlattr *tb[IFLA_BRPORT_MAX + 1];
|
||||
int err;
|
||||
@ -248,38 +313,76 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
|
||||
ifm = nlmsg_data(nlh);
|
||||
|
||||
protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
|
||||
if (!protinfo)
|
||||
afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC);
|
||||
if (!protinfo && !afspec)
|
||||
return 0;
|
||||
|
||||
p = br_port_get_rtnl(dev);
|
||||
if (!p)
|
||||
/* We want to accept dev as bridge itself if the AF_SPEC
|
||||
* is set to see if someone is setting vlan info on the brigde
|
||||
*/
|
||||
if (!p && ((dev->priv_flags & IFF_EBRIDGE) && !afspec))
|
||||
return -EINVAL;
|
||||
|
||||
if (protinfo->nla_type & NLA_F_NESTED) {
|
||||
err = nla_parse_nested(tb, IFLA_BRPORT_MAX,
|
||||
protinfo, ifla_brport_policy);
|
||||
if (p && protinfo) {
|
||||
if (protinfo->nla_type & NLA_F_NESTED) {
|
||||
err = nla_parse_nested(tb, IFLA_BRPORT_MAX,
|
||||
protinfo, ifla_brport_policy);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
spin_lock_bh(&p->br->lock);
|
||||
err = br_setport(p, tb);
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
} else {
|
||||
/* Binary compatability with old RSTP */
|
||||
if (nla_len(protinfo) < sizeof(u8))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_bh(&p->br->lock);
|
||||
err = br_set_port_state(p, nla_get_u8(protinfo));
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_bh(&p->br->lock);
|
||||
err = br_setport(p, tb);
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
} else {
|
||||
/* Binary compatability with old RSTP */
|
||||
if (nla_len(protinfo) < sizeof(u8))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_bh(&p->br->lock);
|
||||
err = br_set_port_state(p, nla_get_u8(protinfo));
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
if (afspec) {
|
||||
err = br_afspec((struct net_bridge *)netdev_priv(dev), p,
|
||||
afspec, RTM_SETLINK);
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
br_ifinfo_notify(RTM_NEWLINK, p);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Delete port information */
|
||||
int br_dellink(struct net_device *dev, struct nlmsghdr *nlh)
|
||||
{
|
||||
struct ifinfomsg *ifm;
|
||||
struct nlattr *afspec;
|
||||
struct net_bridge_port *p;
|
||||
int err;
|
||||
|
||||
ifm = nlmsg_data(nlh);
|
||||
|
||||
afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC);
|
||||
if (!afspec)
|
||||
return 0;
|
||||
|
||||
p = br_port_get_rtnl(dev);
|
||||
/* We want to accept dev as bridge itself as well */
|
||||
if (!p && !(dev->priv_flags & IFF_EBRIDGE))
|
||||
return -EINVAL;
|
||||
|
||||
err = br_afspec((struct net_bridge *)netdev_priv(dev), p,
|
||||
afspec, RTM_DELLINK);
|
||||
|
||||
return err;
|
||||
}
|
||||
static int br_validate(struct nlattr *tb[], struct nlattr *data[])
|
||||
{
|
||||
if (tb[IFLA_ADDRESS]) {
|
||||
|
@ -713,6 +713,7 @@ extern int br_netlink_init(void);
|
||||
extern void br_netlink_fini(void);
|
||||
extern void br_ifinfo_notify(int event, struct net_bridge_port *port);
|
||||
extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg);
|
||||
extern int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg);
|
||||
extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
|
||||
struct net_device *dev);
|
||||
|
||||
|
@ -2464,6 +2464,77 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
void *arg)
|
||||
{
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct ifinfomsg *ifm;
|
||||
struct net_device *dev;
|
||||
struct nlattr *br_spec, *attr = NULL;
|
||||
int rem, err = -EOPNOTSUPP;
|
||||
u16 oflags, flags = 0;
|
||||
bool have_flags = false;
|
||||
|
||||
if (nlmsg_len(nlh) < sizeof(*ifm))
|
||||
return -EINVAL;
|
||||
|
||||
ifm = nlmsg_data(nlh);
|
||||
if (ifm->ifi_family != AF_BRIDGE)
|
||||
return -EPFNOSUPPORT;
|
||||
|
||||
dev = __dev_get_by_index(net, ifm->ifi_index);
|
||||
if (!dev) {
|
||||
pr_info("PF_BRIDGE: RTM_SETLINK with unknown ifindex\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
|
||||
if (br_spec) {
|
||||
nla_for_each_nested(attr, br_spec, rem) {
|
||||
if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
|
||||
have_flags = true;
|
||||
flags = nla_get_u16(attr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oflags = flags;
|
||||
|
||||
if (!flags || (flags & BRIDGE_FLAGS_MASTER)) {
|
||||
struct net_device *br_dev = netdev_master_upper_dev_get(dev);
|
||||
|
||||
if (!br_dev || !br_dev->netdev_ops->ndo_bridge_dellink) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = br_dev->netdev_ops->ndo_bridge_dellink(dev, nlh);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
flags &= ~BRIDGE_FLAGS_MASTER;
|
||||
}
|
||||
|
||||
if ((flags & BRIDGE_FLAGS_SELF)) {
|
||||
if (!dev->netdev_ops->ndo_bridge_dellink)
|
||||
err = -EOPNOTSUPP;
|
||||
else
|
||||
err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh);
|
||||
|
||||
if (!err)
|
||||
flags &= ~BRIDGE_FLAGS_SELF;
|
||||
}
|
||||
|
||||
if (have_flags)
|
||||
memcpy(nla_data(attr), &flags, sizeof(flags));
|
||||
/* Generate event to notify upper layer of bridge change */
|
||||
if (!err)
|
||||
err = rtnl_bridge_notify(dev, oflags);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Protected by RTNL sempahore. */
|
||||
static struct rtattr **rta_buf;
|
||||
static int rtattr_max;
|
||||
@ -2647,6 +2718,7 @@ void __init rtnetlink_init(void)
|
||||
rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL);
|
||||
|
||||
rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL);
|
||||
rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, NULL);
|
||||
rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user