]> Pileus Git - ~andy/linux/blobdiff - net/core/rtnetlink.c
bridge: Add netlink interface to configure vlans on bridge ports
[~andy/linux] / net / core / rtnetlink.c
index c1e4db60eeca894dfef2ada514660d58779a545d..2c9ccbfbd93cc5c56531dbc8d3812e5bd8a4a4d8 100644 (file)
@@ -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);
 }