bridge: netlink: add support for igmp's intervals
[firefly-linux-kernel-4.4.55.git] / net / bridge / br_netlink.c
index ea748c93a07f1dcd988cc18130d629d6a3556d98..30def4fb5901ed263c1ad9efee898d045b118a3e 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(pv);
-       for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
-               vinfo.vid = vid;
+       pvid = br_get_pvid(vg);
+       list_for_each_entry(v, &vg->vlan_list, vlist) {
+               if (!br_vlan_should_use(v))
+                       continue;
+
+               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,23 @@ 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 },
+       [IFLA_BR_GROUP_ADDR] = { .type = NLA_BINARY,
+                                .len  = ETH_ALEN },
+       [IFLA_BR_MCAST_ROUTER] = { .type = NLA_U8 },
+       [IFLA_BR_MCAST_SNOOPING] = { .type = NLA_U8 },
+       [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NLA_U8 },
+       [IFLA_BR_MCAST_QUERIER] = { .type = NLA_U8 },
+       [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NLA_U32 },
+       [IFLA_BR_MCAST_HASH_MAX] = { .type = NLA_U32 },
+       [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NLA_U32 },
+       [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NLA_U32 },
+       [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NLA_U64 },
+       [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NLA_U64 },
+       [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NLA_U64 },
+       [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NLA_U64 },
+       [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NLA_U64 },
+       [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NLA_U64 },
 };
 
 static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
@@ -809,6 +846,131 @@ 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;
+       }
+
+       if (data[IFLA_BR_GROUP_ADDR]) {
+               u8 new_addr[ETH_ALEN];
+
+               if (nla_len(data[IFLA_BR_GROUP_ADDR]) != ETH_ALEN)
+                       return -EINVAL;
+               memcpy(new_addr, nla_data(data[IFLA_BR_GROUP_ADDR]), ETH_ALEN);
+               if (!is_link_local_ether_addr(new_addr))
+                       return -EINVAL;
+               if (new_addr[5] == 1 ||         /* 802.3x Pause address */
+                   new_addr[5] == 2 ||         /* 802.3ad Slow protocols */
+                   new_addr[5] == 3)           /* 802.1X PAE address */
+                       return -EINVAL;
+               spin_lock_bh(&br->lock);
+               memcpy(br->group_addr, new_addr, sizeof(br->group_addr));
+               spin_unlock_bh(&br->lock);
+               br->group_addr_set = true;
+               br_recalculate_fwd_mask(br);
+       }
+
+       if (data[IFLA_BR_FDB_FLUSH])
+               br_fdb_flush(br);
+
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+       if (data[IFLA_BR_MCAST_ROUTER]) {
+               u8 multicast_router = nla_get_u8(data[IFLA_BR_MCAST_ROUTER]);
+
+               err = br_multicast_set_router(br, multicast_router);
+               if (err)
+                       return err;
+       }
+
+       if (data[IFLA_BR_MCAST_SNOOPING]) {
+               u8 mcast_snooping = nla_get_u8(data[IFLA_BR_MCAST_SNOOPING]);
+
+               err = br_multicast_toggle(br, mcast_snooping);
+               if (err)
+                       return err;
+       }
+
+       if (data[IFLA_BR_MCAST_QUERY_USE_IFADDR]) {
+               u8 val;
+
+               val = nla_get_u8(data[IFLA_BR_MCAST_QUERY_USE_IFADDR]);
+               br->multicast_query_use_ifaddr = !!val;
+       }
+
+       if (data[IFLA_BR_MCAST_QUERIER]) {
+               u8 mcast_querier = nla_get_u8(data[IFLA_BR_MCAST_QUERIER]);
+
+               err = br_multicast_set_querier(br, mcast_querier);
+               if (err)
+                       return err;
+       }
+
+       if (data[IFLA_BR_MCAST_HASH_ELASTICITY]) {
+               u32 val = nla_get_u32(data[IFLA_BR_MCAST_HASH_ELASTICITY]);
+
+               br->hash_elasticity = val;
+       }
+
+       if (data[IFLA_BR_MCAST_HASH_MAX]) {
+               u32 hash_max = nla_get_u32(data[IFLA_BR_MCAST_HASH_MAX]);
+
+               err = br_multicast_set_hash_max(br, hash_max);
+               if (err)
+                       return err;
+       }
+
+       if (data[IFLA_BR_MCAST_LAST_MEMBER_CNT]) {
+               u32 val = nla_get_u32(data[IFLA_BR_MCAST_LAST_MEMBER_CNT]);
+
+               br->multicast_last_member_count = val;
+       }
+
+       if (data[IFLA_BR_MCAST_STARTUP_QUERY_CNT]) {
+               u32 val = nla_get_u32(data[IFLA_BR_MCAST_STARTUP_QUERY_CNT]);
+
+               br->multicast_startup_query_count = val;
+       }
+
+       if (data[IFLA_BR_MCAST_LAST_MEMBER_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_LAST_MEMBER_INTVL]);
+
+               br->multicast_last_member_interval = clock_t_to_jiffies(val);
+       }
+
+       if (data[IFLA_BR_MCAST_MEMBERSHIP_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_MEMBERSHIP_INTVL]);
+
+               br->multicast_membership_interval = clock_t_to_jiffies(val);
+       }
+
+       if (data[IFLA_BR_MCAST_QUERIER_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERIER_INTVL]);
+
+               br->multicast_querier_interval = clock_t_to_jiffies(val);
+       }
+
+       if (data[IFLA_BR_MCAST_QUERY_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERY_INTVL]);
+
+               br->multicast_query_interval = clock_t_to_jiffies(val);
+       }
+
+       if (data[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]);
+
+               br->multicast_query_response_interval = clock_t_to_jiffies(val);
+       }
+
+       if (data[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]) {
+               u64 val = nla_get_u64(data[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]);
+
+               br->multicast_startup_query_interval = clock_t_to_jiffies(val);
+       }
+#endif
+
        return 0;
 }
 
@@ -823,6 +985,34 @@ static size_t br_get_size(const struct net_device *brdev)
               nla_total_size(sizeof(u8)) +     /* IFLA_BR_VLAN_FILTERING */
 #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 */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_HELLO_TIMER */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_TCN_TIMER */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_TOPOLOGY_CHANGE_TIMER */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_GC_TIMER */
+              nla_total_size(ETH_ALEN) +       /* IFLA_BR_GROUP_ADDR */
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_ROUTER */
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_SNOOPING */
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_QUERY_USE_IFADDR */
+              nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_QUERIER */
+              nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_HASH_ELASTICITY */
+              nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_HASH_MAX */
+              nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_LAST_MEMBER_CNT */
+              nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_STARTUP_QUERY_CNT */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_LAST_MEMBER_INTVL */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_MEMBERSHIP_INTVL */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_QUERIER_INTVL */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_QUERY_INTVL */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_QUERY_RESPONSE_INTVL */
+              nla_total_size(sizeof(u64)) +    /* IFLA_BR_MCAST_STARTUP_QUERY_INTVL */
 #endif
               0;
 }
@@ -830,13 +1020,28 @@ static size_t br_get_size(const struct net_device *brdev)
 static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
 {
        struct net_bridge *br = netdev_priv(brdev);
+       u64 hello_timer, tcn_timer, topology_change_timer, gc_timer, clockval;
        u32 forward_delay = jiffies_to_clock_t(br->forward_delay);
        u32 hello_time = jiffies_to_clock_t(br->hello_time);
        u32 age_time = jiffies_to_clock_t(br->max_age);
        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));
+       hello_timer = br_timer_value(&br->hello_timer);
+       tcn_timer = br_timer_value(&br->tcn_timer);
+       topology_change_timer = br_timer_value(&br->topology_change_timer);
+       gc_timer = br_timer_value(&br->gc_timer);
+       clockval = 0;
 
        if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) ||
            nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) ||
@@ -844,7 +1049,21 @@ 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) ||
+           nla_put_u64(skb, IFLA_BR_HELLO_TIMER, hello_timer) ||
+           nla_put_u64(skb, IFLA_BR_TCN_TIMER, tcn_timer) ||
+           nla_put_u64(skb, IFLA_BR_TOPOLOGY_CHANGE_TIMER,
+                       topology_change_timer) ||
+           nla_put_u64(skb, IFLA_BR_GC_TIMER, gc_timer) ||
+           nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr))
                return -EMSGSIZE;
 
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
@@ -852,25 +1071,62 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
                return -EMSGSIZE;
 #endif
 
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+       if (nla_put_u8(skb, IFLA_BR_MCAST_ROUTER, br->multicast_router) ||
+           nla_put_u8(skb, IFLA_BR_MCAST_SNOOPING, !br->multicast_disabled) ||
+           nla_put_u8(skb, IFLA_BR_MCAST_QUERY_USE_IFADDR,
+                      br->multicast_query_use_ifaddr) ||
+           nla_put_u8(skb, IFLA_BR_MCAST_QUERIER, br->multicast_querier) ||
+           nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY,
+                       br->hash_elasticity) ||
+           nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max) ||
+           nla_put_u32(skb, IFLA_BR_MCAST_LAST_MEMBER_CNT,
+                       br->multicast_last_member_count) ||
+           nla_put_u32(skb, IFLA_BR_MCAST_STARTUP_QUERY_CNT,
+                       br->multicast_startup_query_count))
+               return -EMSGSIZE;
+
+       clockval = jiffies_to_clock_t(br->multicast_last_member_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_LAST_MEMBER_INTVL, clockval))
+               return -EMSGSIZE;
+       clockval = jiffies_to_clock_t(br->multicast_membership_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_MEMBERSHIP_INTVL, clockval))
+               return -EMSGSIZE;
+       clockval = jiffies_to_clock_t(br->multicast_querier_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_QUERIER_INTVL, clockval))
+               return -EMSGSIZE;
+       clockval = jiffies_to_clock_t(br->multicast_query_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_QUERY_INTVL, clockval))
+               return -EMSGSIZE;
+       clockval = jiffies_to_clock_t(br->multicast_query_response_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, clockval))
+               return -EMSGSIZE;
+       clockval = jiffies_to_clock_t(br->multicast_startup_query_interval);
+       if (nla_put_u64(skb, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, clockval))
+               return -EMSGSIZE;
+#endif
+
        return 0;
 }
 
 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 = {