bridge: netlink: export topology_change and topology_change_detected
[deliverable/linux.git] / net / bridge / br_netlink.c
index ea748c93a07f1dcd988cc18130d629d6a3556d98..8bcaa5171f351700183cf36ad50f88c62c99657d 100644 (file)
 #include "br_private.h"
 #include "br_private_stp.h"
 
-static int br_get_num_vlan_infos(const struct net_port_vlans *pv,
-                                u32 filter_mask)
+static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
+                               u32 filter_mask)
 {
-       u16 vid_range_start = 0, vid_range_end = 0;
-       u16 vid_range_flags = 0;
-       u16 pvid, vid, flags;
+       struct net_bridge_vlan *v;
+       u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0;
+       u16 flags, pvid;
        int num_vlans = 0;
 
-       if (filter_mask & RTEXT_FILTER_BRVLAN)
-               return pv->num_vlans;
-
        if (!(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED))
                return 0;
 
-       /* Count number of vlan info's
-        */
-       pvid = br_get_pvid(pv);
-       for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
+       pvid = br_get_pvid(vg);
+       /* Count number of vlan infos */
+       list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
                flags = 0;
-               if (vid == pvid)
+               /* only a context, bridge vlan not activated */
+               if (!br_vlan_should_use(v))
+                       continue;
+               if (v->vid == pvid)
                        flags |= BRIDGE_VLAN_INFO_PVID;
 
-               if (test_bit(vid, pv->untagged_bitmap))
+               if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
                        flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 
                if (vid_range_start == 0) {
                        goto initvars;
-               } else if ((vid - vid_range_end) == 1 &&
+               } else if ((v->vid - vid_range_end) == 1 &&
                        flags == vid_range_flags) {
-                       vid_range_end = vid;
+                       vid_range_end = v->vid;
                        continue;
                } else {
                        if ((vid_range_end - vid_range_start) > 0)
@@ -59,8 +58,8 @@ static int br_get_num_vlan_infos(const struct net_port_vlans *pv,
                                num_vlans += 1;
                }
 initvars:
-               vid_range_start = vid;
-               vid_range_end = vid;
+               vid_range_start = v->vid;
+               vid_range_end = v->vid;
                vid_range_flags = flags;
        }
 
@@ -74,28 +73,43 @@ initvars:
        return num_vlans;
 }
 
+static int br_get_num_vlan_infos(struct net_bridge_vlan_group *vg,
+                                u32 filter_mask)
+{
+       int num_vlans;
+
+       if (!vg)
+               return 0;
+
+       if (filter_mask & RTEXT_FILTER_BRVLAN)
+               return vg->num_vlans;
+
+       rcu_read_lock();
+       num_vlans = __get_num_vlan_infos(vg, filter_mask);
+       rcu_read_unlock();
+
+       return num_vlans;
+}
+
 static size_t br_get_link_af_size_filtered(const struct net_device *dev,
                                           u32 filter_mask)
 {
-       struct net_port_vlans *pv;
+       struct net_bridge_vlan_group *vg = NULL;
+       struct net_bridge_port *p;
+       struct net_bridge *br;
        int num_vlan_infos;
 
        rcu_read_lock();
-       if (br_port_exists(dev))
-               pv = nbp_get_vlan_info(br_port_get_rcu(dev));
-       else if (dev->priv_flags & IFF_EBRIDGE)
-               pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev));
-       else
-               pv = NULL;
-       if (pv)
-               num_vlan_infos = br_get_num_vlan_infos(pv, filter_mask);
-       else
-               num_vlan_infos = 0;
+       if (br_port_exists(dev)) {
+               p = br_port_get_rcu(dev);
+               vg = nbp_vlan_group(p);
+       } else if (dev->priv_flags & IFF_EBRIDGE) {
+               br = netdev_priv(dev);
+               vg = br_vlan_group(br);
+       }
+       num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask);
        rcu_read_unlock();
 
-       if (!num_vlan_infos)
-               return 0;
-
        /* Each VLAN is returned in bridge_vlan_info along with flags */
        return num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info));
 }
@@ -185,31 +199,33 @@ nla_put_failure:
 }
 
 static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
-                                        const struct net_port_vlans *pv)
+                                        struct net_bridge_vlan_group *vg)
 {
-       u16 vid_range_start = 0, vid_range_end = 0;
-       u16 vid_range_flags = 0;
-       u16 pvid, vid, flags;
+       struct net_bridge_vlan *v;
+       u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0;
+       u16 flags, pvid;
        int err = 0;
 
        /* Pack IFLA_BRIDGE_VLAN_INFO's for every vlan
         * and mark vlan info with begin and end flags
         * if vlaninfo represents a range
         */
-       pvid = br_get_pvid(pv);
-       for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
+       pvid = br_get_pvid(vg);
+       list_for_each_entry(v, &vg->vlan_list, vlist) {
                flags = 0;
-               if (vid == pvid)
+               if (!br_vlan_should_use(v))
+                       continue;
+               if (v->vid == pvid)
                        flags |= BRIDGE_VLAN_INFO_PVID;
 
-               if (test_bit(vid, pv->untagged_bitmap))
+               if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
                        flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 
                if (vid_range_start == 0) {
                        goto initvars;
-               } else if ((vid - vid_range_end) == 1 &&
+               } else if ((v->vid - vid_range_end) == 1 &&
                        flags == vid_range_flags) {
-                       vid_range_end = vid;
+                       vid_range_end = v->vid;
                        continue;
                } else {
                        err = br_fill_ifvlaninfo_range(skb, vid_range_start,
@@ -220,8 +236,8 @@ static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
                }
 
 initvars:
-               vid_range_start = vid;
-               vid_range_end = vid;
+               vid_range_start = v->vid;
+               vid_range_end = v->vid;
                vid_range_flags = flags;
        }
 
@@ -238,19 +254,23 @@ initvars:
 }
 
 static int br_fill_ifvlaninfo(struct sk_buff *skb,
-                             const struct net_port_vlans *pv)
+                             struct net_bridge_vlan_group *vg)
 {
        struct bridge_vlan_info vinfo;
-       u16 pvid, vid;
+       struct net_bridge_vlan *v;
+       u16 pvid;
+
+       pvid = br_get_pvid(vg);
+       list_for_each_entry(v, &vg->vlan_list, vlist) {
+               if (!br_vlan_should_use(v))
+                       continue;
 
-       pvid = br_get_pvid(pv);
-       for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
-               vinfo.vid = vid;
+               vinfo.vid = v->vid;
                vinfo.flags = 0;
-               if (vid == pvid)
+               if (v->vid == pvid)
                        vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
 
-               if (test_bit(vid, pv->untagged_bitmap))
+               if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
                        vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 
                if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
@@ -269,11 +289,11 @@ nla_put_failure:
  * Contains port and master info as well as carrier and bridge state.
  */
 static int br_fill_ifinfo(struct sk_buff *skb,
-                         const struct net_bridge_port *port,
+                         struct net_bridge_port *port,
                          u32 pid, u32 seq, int event, unsigned int flags,
                          u32 filter_mask, const struct net_device *dev)
 {
-       const struct net_bridge *br;
+       struct net_bridge *br;
        struct ifinfomsg *hdr;
        struct nlmsghdr *nlh;
        u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
@@ -320,16 +340,16 @@ static int br_fill_ifinfo(struct sk_buff *skb,
        /* Check if  the VID information is requested */
        if ((filter_mask & RTEXT_FILTER_BRVLAN) ||
            (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) {
-               const struct net_port_vlans *pv;
+               struct net_bridge_vlan_group *vg;
                struct nlattr *af;
                int err;
 
                if (port)
-                       pv = nbp_get_vlan_info(port);
+                       vg = nbp_vlan_group(port);
                else
-                       pv = br_get_vlan_info(br);
+                       vg = br_vlan_group(br);
 
-               if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID))
+               if (!vg || !vg->num_vlans)
                        goto done;
 
                af = nla_nest_start(skb, IFLA_AF_SPEC);
@@ -337,9 +357,9 @@ static int br_fill_ifinfo(struct sk_buff *skb,
                        goto nla_put_failure;
 
                if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
-                       err = br_fill_ifvlaninfo_compressed(skb, pv);
+                       err = br_fill_ifvlaninfo_compressed(skb, vg);
                else
-                       err = br_fill_ifvlaninfo(skb, pv);
+                       err = br_fill_ifvlaninfo(skb, vg);
                if (err)
                        goto nla_put_failure;
                nla_nest_end(skb, af);
@@ -413,14 +433,14 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
        switch (cmd) {
        case RTM_SETLINK:
                if (p) {
+                       /* if the MASTER flag is set this will act on the global
+                        * per-VLAN entry as well
+                        */
                        err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
                        if (err)
                                break;
-
-                       if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
-                               err = br_vlan_add(p->br, vinfo->vid,
-                                                 vinfo->flags);
                } else {
+                       vinfo->flags |= BRIDGE_VLAN_INFO_BRENTRY;
                        err = br_vlan_add(br, vinfo->vid, vinfo->flags);
                }
                break;
@@ -744,6 +764,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
        [IFLA_BR_PRIORITY] = { .type = NLA_U16 },
        [IFLA_BR_VLAN_FILTERING] = { .type = NLA_U8 },
        [IFLA_BR_VLAN_PROTOCOL] = { .type = NLA_U16 },
+       [IFLA_BR_GROUP_FWD_MASK] = { .type = NLA_U16 },
 };
 
 static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
@@ -809,6 +830,14 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
        }
 #endif
 
+       if (data[IFLA_BR_GROUP_FWD_MASK]) {
+               u16 fwd_mask = nla_get_u16(data[IFLA_BR_GROUP_FWD_MASK]);
+
+               if (fwd_mask & BR_GROUPFWD_RESTRICTED)
+                       return -EINVAL;
+               br->group_fwd_mask = fwd_mask;
+       }
+
        return 0;
 }
 
@@ -824,6 +853,13 @@ static size_t br_get_size(const struct net_device *brdev)
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
               nla_total_size(sizeof(__be16)) + /* IFLA_BR_VLAN_PROTOCOL */
 #endif
+              nla_total_size(sizeof(u16)) +    /* IFLA_BR_GROUP_FWD_MASK */
+              nla_total_size(sizeof(struct ifla_bridge_id)) +   /* IFLA_BR_ROOT_ID */
+              nla_total_size(sizeof(struct ifla_bridge_id)) +   /* IFLA_BR_BRIDGE_ID */
+              nla_total_size(sizeof(u16)) +    /* IFLA_BR_ROOT_PORT */
+              nla_total_size(sizeof(u32)) +    /* IFLA_BR_ROOT_PATH_COST */
+              nla_total_size(sizeof(u8)) +    /* IFLA_BR_TOPOLOGY_CHANGE */
+              nla_total_size(sizeof(u8)) +    /* IFLA_BR_TOPOLOGY_CHANGE_DETECTED */
               0;
 }
 
@@ -836,7 +872,16 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
        u32 ageing_time = jiffies_to_clock_t(br->ageing_time);
        u32 stp_enabled = br->stp_enabled;
        u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1];
+       u16 group_fwd_mask = br->group_fwd_mask;
        u8 vlan_enabled = br_vlan_enabled(br);
+       struct ifla_bridge_id root_id, bridge_id;
+
+       memset(&bridge_id, 0, sizeof(bridge_id));
+       memset(&root_id, 0, sizeof(root_id));
+       memcpy(root_id.prio, br->designated_root.prio, sizeof(root_id.prio));
+       memcpy(root_id.addr, br->designated_root.addr, sizeof(root_id.addr));
+       memcpy(bridge_id.prio, br->bridge_id.prio, sizeof(bridge_id.prio));
+       memcpy(bridge_id.addr, br->bridge_id.addr, sizeof(bridge_id.addr));
 
        if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) ||
            nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) ||
@@ -844,7 +889,15 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
            nla_put_u32(skb, IFLA_BR_AGEING_TIME, ageing_time) ||
            nla_put_u32(skb, IFLA_BR_STP_STATE, stp_enabled) ||
            nla_put_u16(skb, IFLA_BR_PRIORITY, priority) ||
-           nla_put_u8(skb, IFLA_BR_VLAN_FILTERING, vlan_enabled))
+           nla_put_u8(skb, IFLA_BR_VLAN_FILTERING, vlan_enabled) ||
+           nla_put_u16(skb, IFLA_BR_GROUP_FWD_MASK, group_fwd_mask) ||
+           nla_put(skb, IFLA_BR_ROOT_ID, sizeof(root_id), &root_id) ||
+           nla_put(skb, IFLA_BR_BRIDGE_ID, sizeof(bridge_id), &bridge_id) ||
+           nla_put_u16(skb, IFLA_BR_ROOT_PORT, br->root_port) ||
+           nla_put_u32(skb, IFLA_BR_ROOT_PATH_COST, br->root_path_cost) ||
+           nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE, br->topology_change) ||
+           nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+                      br->topology_change_detected))
                return -EMSGSIZE;
 
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
@@ -857,20 +910,22 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
 
 static size_t br_get_link_af_size(const struct net_device *dev)
 {
-       struct net_port_vlans *pv;
-
-       if (br_port_exists(dev))
-               pv = nbp_get_vlan_info(br_port_get_rtnl(dev));
-       else if (dev->priv_flags & IFF_EBRIDGE)
-               pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev));
-       else
-               return 0;
+       struct net_bridge_port *p;
+       struct net_bridge *br;
+       int num_vlans = 0;
 
-       if (!pv)
-               return 0;
+       if (br_port_exists(dev)) {
+               p = br_port_get_rtnl(dev);
+               num_vlans = br_get_num_vlan_infos(nbp_vlan_group(p),
+                                                 RTEXT_FILTER_BRVLAN);
+       } else if (dev->priv_flags & IFF_EBRIDGE) {
+               br = netdev_priv(dev);
+               num_vlans = br_get_num_vlan_infos(br_vlan_group(br),
+                                                 RTEXT_FILTER_BRVLAN);
+       }
 
        /* Each VLAN is returned in bridge_vlan_info along with flags */
-       return pv->num_vlans * nla_total_size(sizeof(struct bridge_vlan_info));
+       return num_vlans * nla_total_size(sizeof(struct bridge_vlan_info));
 }
 
 static struct rtnl_af_ops br_af_ops __read_mostly = {
This page took 0.033591 seconds and 5 git commands to generate.