]> Pileus Git - ~andy/linux/blobdiff - net/bridge/br_netlink.c
bridge: Add netlink interface to configure vlans on bridge ports
[~andy/linux] / net / bridge / br_netlink.c
index 39ca9796f3f7a4cfe9c9be437749fbda939d5152..534a9f4587a9ae3250a8037dd1849488fd31b8eb 100644 (file)
@@ -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;
-
-               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;
+                       goto out;
+       }
 
-               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]) {