Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 407af329 authored by Vlad Yasevich's avatar Vlad Yasevich Committed by David S. Miller
Browse files

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: default avatarVlad Yasevich <vyasevic@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 85f46c6b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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);
};
+9 −0
Original line number Diff line number Diff line
@@ -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] = {
+1 −0
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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"

+121 −18
Original line number Diff line number Diff line
@@ -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 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
 */
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 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port)
		rtnl_set_sk_err(net, RTNLGRP_LINK, err);
}


/*
 * Dump information about all ports, in response to GETLINK
 */
@@ -162,6 +168,64 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
	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,13 +313,18 @@ 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 (p && protinfo) {
		if (protinfo->nla_type & NLA_F_NESTED) {
			err = nla_parse_nested(tb, IFLA_BRPORT_MAX,
					       protinfo, ifla_brport_policy);
@@ -273,13 +343,46 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
			err = br_set_port_state(p, nla_get_u8(protinfo));
			spin_unlock_bh(&p->br->lock);
		}
		if (err)
			goto out;
	}

	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]) {
Loading