Merge tag 'clk-for-linus-20151104' of git://git.kernel.org/pub/scm/linux/kernel/git...
[firefly-linux-kernel-4.4.55.git] / net / bridge / br_netlink.c
index 330abf4b033a671f2d1eab50993bfbb9a9277c2c..40197ff8918abab433c563dd92186479652ed84d 100644 (file)
@@ -102,10 +102,10 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev,
        rcu_read_lock();
        if (br_port_exists(dev)) {
                p = br_port_get_rcu(dev);
-               vg = nbp_vlan_group(p);
+               vg = nbp_vlan_group_rcu(p);
        } else if (dev->priv_flags & IFF_EBRIDGE) {
                br = netdev_priv(dev);
-               vg = br_vlan_group(br);
+               vg = br_vlan_group_rcu(br);
        }
        num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask);
        rcu_read_unlock();
@@ -127,6 +127,20 @@ static inline size_t br_port_info_size(void)
                + nla_total_size(1)     /* IFLA_BRPORT_UNICAST_FLOOD */
                + nla_total_size(1)     /* IFLA_BRPORT_PROXYARP */
                + nla_total_size(1)     /* IFLA_BRPORT_PROXYARP_WIFI */
+               + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */
+               + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */
+               + nla_total_size(sizeof(u16))   /* IFLA_BRPORT_DESIGNATED_PORT */
+               + nla_total_size(sizeof(u16))   /* IFLA_BRPORT_DESIGNATED_COST */
+               + nla_total_size(sizeof(u16))   /* IFLA_BRPORT_ID */
+               + nla_total_size(sizeof(u16))   /* IFLA_BRPORT_NO */
+               + nla_total_size(sizeof(u8))    /* IFLA_BRPORT_TOPOLOGY_CHANGE_ACK */
+               + nla_total_size(sizeof(u8))    /* IFLA_BRPORT_CONFIG_PENDING */
+               + nla_total_size(sizeof(u64))   /* IFLA_BRPORT_MESSAGE_AGE_TIMER */
+               + nla_total_size(sizeof(u64))   /* IFLA_BRPORT_FORWARD_DELAY_TIMER */
+               + nla_total_size(sizeof(u64))   /* IFLA_BRPORT_HOLD_TIMER */
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+               + nla_total_size(sizeof(u8))    /* IFLA_BRPORT_MULTICAST_ROUTER */
+#endif
                + 0;
 }
 
@@ -148,6 +162,7 @@ static int br_port_fill_attrs(struct sk_buff *skb,
                              const struct net_bridge_port *p)
 {
        u8 mode = !!(p->flags & BR_HAIRPIN_MODE);
+       u64 timerval;
 
        if (nla_put_u8(skb, IFLA_BRPORT_STATE, p->state) ||
            nla_put_u16(skb, IFLA_BRPORT_PRIORITY, p->priority) ||
@@ -160,8 +175,35 @@ static int br_port_fill_attrs(struct sk_buff *skb,
            nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD)) ||
            nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(p->flags & BR_PROXYARP)) ||
            nla_put_u8(skb, IFLA_BRPORT_PROXYARP_WIFI,
-                      !!(p->flags & BR_PROXYARP_WIFI)))
+                      !!(p->flags & BR_PROXYARP_WIFI)) ||
+           nla_put(skb, IFLA_BRPORT_ROOT_ID, sizeof(struct ifla_bridge_id),
+                   &p->designated_root) ||
+           nla_put(skb, IFLA_BRPORT_BRIDGE_ID, sizeof(struct ifla_bridge_id),
+                   &p->designated_bridge) ||
+           nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_PORT, p->designated_port) ||
+           nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_COST, p->designated_cost) ||
+           nla_put_u16(skb, IFLA_BRPORT_ID, p->port_id) ||
+           nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no) ||
+           nla_put_u8(skb, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+                      p->topology_change_ack) ||
+           nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending))
+               return -EMSGSIZE;
+
+       timerval = br_timer_value(&p->message_age_timer);
+       if (nla_put_u64(skb, IFLA_BRPORT_MESSAGE_AGE_TIMER, timerval))
+               return -EMSGSIZE;
+       timerval = br_timer_value(&p->forward_delay_timer);
+       if (nla_put_u64(skb, IFLA_BRPORT_FORWARD_DELAY_TIMER, timerval))
                return -EMSGSIZE;
+       timerval = br_timer_value(&p->hold_timer);
+       if (nla_put_u64(skb, IFLA_BRPORT_HOLD_TIMER, timerval))
+               return -EMSGSIZE;
+
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+       if (nla_put_u8(skb, IFLA_BRPORT_MULTICAST_ROUTER,
+                      p->multicast_router))
+               return -EMSGSIZE;
+#endif
 
        return 0;
 }
@@ -211,7 +253,7 @@ static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
         * if vlaninfo represents a range
         */
        pvid = br_get_pvid(vg);
-       list_for_each_entry(v, &vg->vlan_list, vlist) {
+       list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
                flags = 0;
                if (!br_vlan_should_use(v))
                        continue;
@@ -261,7 +303,7 @@ static int br_fill_ifvlaninfo(struct sk_buff *skb,
        u16 pvid;
 
        pvid = br_get_pvid(vg);
-       list_for_each_entry(v, &vg->vlan_list, vlist) {
+       list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
                if (!br_vlan_should_use(v))
                        continue;
 
@@ -344,22 +386,27 @@ static int br_fill_ifinfo(struct sk_buff *skb,
                struct nlattr *af;
                int err;
 
+               /* RCU needed because of the VLAN locking rules (rcu || rtnl) */
+               rcu_read_lock();
                if (port)
-                       vg = nbp_vlan_group(port);
+                       vg = nbp_vlan_group_rcu(port);
                else
-                       vg = br_vlan_group(br);
+                       vg = br_vlan_group_rcu(br);
 
-               if (!vg || !vg->num_vlans)
+               if (!vg || !vg->num_vlans) {
+                       rcu_read_unlock();
                        goto done;
-
+               }
                af = nla_nest_start(skb, IFLA_AF_SPEC);
-               if (!af)
+               if (!af) {
+                       rcu_read_unlock();
                        goto nla_put_failure;
-
+               }
                if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
                        err = br_fill_ifvlaninfo_compressed(skb, vg);
                else
                        err = br_fill_ifvlaninfo(skb, vg);
+               rcu_read_unlock();
                if (err)
                        goto nla_put_failure;
                nla_nest_end(skb, af);
@@ -482,6 +529,9 @@ static int br_afspec(struct net_bridge *br,
                        if (vinfo_start)
                                return -EINVAL;
                        vinfo_start = vinfo;
+                       /* don't allow range of pvids */
+                       if (vinfo_start->flags & BRIDGE_VLAN_INFO_PVID)
+                               return -EINVAL;
                        continue;
                }
 
@@ -527,6 +577,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = {
        [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 },
        [IFLA_BRPORT_PROXYARP]  = { .type = NLA_U8 },
        [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NLA_U8 },
+       [IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NLA_U8 },
 };
 
 /* Change the state of the port and notify spanning tree */
@@ -598,6 +649,18 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
                        return err;
        }
 
+       if (tb[IFLA_BRPORT_FLUSH])
+               br_fdb_delete_by_port(p->br, p, 0, 0);
+
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+       if (tb[IFLA_BRPORT_MULTICAST_ROUTER]) {
+               u8 mcast_router = nla_get_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER]);
+
+               err = br_multicast_set_port_router(p, mcast_router);
+               if (err)
+                       return err;
+       }
+#endif
        br_port_flags_change(p, old_flags ^ p->flags);
        return 0;
 }
@@ -815,9 +878,9 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
        }
 
        if (data[IFLA_BR_AGEING_TIME]) {
-               u32 ageing_time = nla_get_u32(data[IFLA_BR_AGEING_TIME]);
-
-               br->ageing_time = clock_t_to_jiffies(ageing_time);
+               err = br_set_ageing_time(br, nla_get_u32(data[IFLA_BR_AGEING_TIME]));
+               if (err)
+                       return err;
        }
 
        if (data[IFLA_BR_STP_STATE]) {
@@ -1151,29 +1214,10 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
        return 0;
 }
 
-static size_t br_get_link_af_size(const struct net_device *dev)
-{
-       struct net_bridge_port *p;
-       struct net_bridge *br;
-       int num_vlans = 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 num_vlans * nla_total_size(sizeof(struct bridge_vlan_info));
-}
 
 static struct rtnl_af_ops br_af_ops __read_mostly = {
        .family                 = AF_BRIDGE,
-       .get_link_af_size       = br_get_link_af_size,
+       .get_link_af_size       = br_get_link_af_size_filtered,
 };
 
 struct rtnl_link_ops br_link_ops __read_mostly = {