#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"
  */
 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);
 
                rtnl_set_sk_err(net, RTNLGRP_LINK, err);
 }
 
+
 /*
  * Dump information about all ports, in response to GETLINK
  */
        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 },
 {
        struct ifinfomsg *ifm;
        struct nlattr *protinfo;
+       struct nlattr *afspec;
        struct net_bridge_port *p;
        struct nlattr *tb[IFLA_BRPORT_MAX + 1];
        int err;
        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]) {
 
        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;
        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);
 }