net: bridge: mcast: dump ipv4 querier state
authorNikolay Aleksandrov <nikolay@nvidia.com>
Fri, 13 Aug 2021 15:00:00 +0000 (18:00 +0300)
committerDavid S. Miller <davem@davemloft.net>
Sat, 14 Aug 2021 13:02:43 +0000 (14:02 +0100)
Add support for dumping global IPv4 querier state, we dump the state
only if our own querier is enabled or there has been another external
querier which has won the election. For the bridge global state we use
a new attribute IFLA_BR_MCAST_QUERIER_STATE and embed the state inside.
The structure is:
 [IFLA_BR_MCAST_QUERIER_STATE]
  `[BRIDGE_QUERIER_IP_ADDRESS] - ip address of the querier
  `[BRIDGE_QUERIER_IP_PORT]    - bridge port ifindex where the querier was
                                 seen (set only if external querier)
  `[BRIDGE_QUERIER_IP_OTHER_TIMER]   -  other querier timeout

Signed-off-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/if_bridge.h
include/uapi/linux/if_link.h
net/bridge/br_multicast.c
net/bridge/br_netlink.c
net/bridge/br_private.h

index 620d86e825b89e9cb9fe8d59bc1f42c9ddbbb074..e0fff67fcd888492b327290fc7ac89e0108ed456 100644 (file)
@@ -770,4 +770,14 @@ struct br_boolopt_multi {
        __u32 optval;
        __u32 optmask;
 };
+
+enum {
+       BRIDGE_QUERIER_UNSPEC,
+       BRIDGE_QUERIER_IP_ADDRESS,
+       BRIDGE_QUERIER_IP_PORT,
+       BRIDGE_QUERIER_IP_OTHER_TIMER,
+       BRIDGE_QUERIER_PAD,
+       __BRIDGE_QUERIER_MAX
+};
+#define BRIDGE_QUERIER_MAX (__BRIDGE_QUERIER_MAX - 1)
 #endif /* _UAPI_LINUX_IF_BRIDGE_H */
index 5310003523ce0c927e8e9668ea70a94ed176b898..8aad65b690540e8b845df3caad9629ba384db150 100644 (file)
@@ -479,6 +479,7 @@ enum {
        IFLA_BR_MCAST_MLD_VERSION,
        IFLA_BR_VLAN_STATS_PER_PORT,
        IFLA_BR_MULTI_BOOLOPT,
+       IFLA_BR_MCAST_QUERIER_STATE,
        __IFLA_BR_MAX,
 };
 
index 3705b7ace62da12bb19d58318e36bfa4d1ab23df..4513bc13b6d3452661c6c10586b05017a92fd32c 100644 (file)
@@ -2905,6 +2905,79 @@ update:
        return true;
 }
 
+static struct net_bridge_port *
+__br_multicast_get_querier_port(struct net_bridge *br,
+                               const struct bridge_mcast_querier *querier)
+{
+       int port_ifidx = READ_ONCE(querier->port_ifidx);
+       struct net_bridge_port *p;
+       struct net_device *dev;
+
+       if (port_ifidx == 0)
+               return NULL;
+
+       dev = dev_get_by_index_rcu(dev_net(br->dev), port_ifidx);
+       if (!dev)
+               return NULL;
+       p = br_port_get_rtnl_rcu(dev);
+       if (!p || p->br != br)
+               return NULL;
+
+       return p;
+}
+
+size_t br_multicast_querier_state_size(void)
+{
+       return nla_total_size(sizeof(0)) +      /* nest attribute */
+              nla_total_size(sizeof(__be32)) + /* BRIDGE_QUERIER_IP_ADDRESS */
+              nla_total_size(sizeof(int)) +    /* BRIDGE_QUERIER_IP_PORT */
+              nla_total_size_64bit(sizeof(u64)); /* BRIDGE_QUERIER_IP_OTHER_TIMER */
+}
+
+/* protected by rtnl or rcu */
+int br_multicast_dump_querier_state(struct sk_buff *skb,
+                                   const struct net_bridge_mcast *brmctx,
+                                   int nest_attr)
+{
+       struct bridge_mcast_querier querier = {};
+       struct net_bridge_port *p;
+       struct nlattr *nest;
+
+       if (!brmctx->multicast_querier &&
+           !timer_pending(&brmctx->ip4_other_query.timer))
+               return 0;
+
+       nest = nla_nest_start(skb, nest_attr);
+       if (!nest)
+               return -EMSGSIZE;
+
+       rcu_read_lock();
+       br_multicast_read_querier(&brmctx->ip4_querier, &querier);
+       if (nla_put_in_addr(skb, BRIDGE_QUERIER_IP_ADDRESS,
+                           querier.addr.src.ip4)) {
+               rcu_read_unlock();
+               goto out_err;
+       }
+
+       p = __br_multicast_get_querier_port(brmctx->br, &querier);
+       if (timer_pending(&brmctx->ip4_other_query.timer) &&
+           (nla_put_u64_64bit(skb, BRIDGE_QUERIER_IP_OTHER_TIMER,
+                              br_timer_value(&brmctx->ip4_other_query.timer),
+                              BRIDGE_QUERIER_PAD) ||
+            (p && nla_put_u32(skb, BRIDGE_QUERIER_IP_PORT, p->dev->ifindex)))) {
+               rcu_read_unlock();
+               goto out_err;
+       }
+       rcu_read_unlock();
+       nla_nest_end(skb, nest);
+
+       return 0;
+
+out_err:
+       nla_nest_cancel(skb, nest);
+       return -EMSGSIZE;
+}
+
 static void
 br_multicast_update_query_timer(struct net_bridge_mcast *brmctx,
                                struct bridge_mcast_other_query *query,
index 8ae026fa2ad7f8ef5b826186624b6b9e72d5a486..2f184ad8ae29c79ebfdd4da8b2d86e7701f1e8fa 100644 (file)
@@ -1501,6 +1501,7 @@ static size_t br_get_size(const struct net_device *brdev)
               nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_STARTUP_QUERY_INTVL */
               nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_IGMP_VERSION */
               nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_MLD_VERSION */
+              br_multicast_querier_state_size() + /* IFLA_BR_MCAST_QUERIER_STATE */
 #endif
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
               nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_IPTABLES */
@@ -1587,7 +1588,9 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
            nla_put_u32(skb, IFLA_BR_MCAST_STARTUP_QUERY_CNT,
                        br->multicast_ctx.multicast_startup_query_count) ||
            nla_put_u8(skb, IFLA_BR_MCAST_IGMP_VERSION,
-                      br->multicast_ctx.multicast_igmp_version))
+                      br->multicast_ctx.multicast_igmp_version) ||
+           br_multicast_dump_querier_state(skb, &br->multicast_ctx,
+                                           IFLA_BR_MCAST_QUERIER_STATE))
                return -EMSGSIZE;
 #if IS_ENABLED(CONFIG_IPV6)
        if (nla_put_u8(skb, IFLA_BR_MCAST_MLD_VERSION,
index 70e2d9a7f1055ab60991154bce24f228cba760ba..9b1bf98a2c5aa7786a81dea6589b4afaa779346b 100644 (file)
@@ -948,6 +948,10 @@ int br_mdb_replay(struct net_device *br_dev, struct net_device *dev,
                  struct netlink_ext_ack *extack);
 int br_rports_fill_info(struct sk_buff *skb,
                        const struct net_bridge_mcast *brmctx);
+int br_multicast_dump_querier_state(struct sk_buff *skb,
+                                   const struct net_bridge_mcast *brmctx,
+                                   int nest_attr);
+size_t br_multicast_querier_state_size(void);
 
 static inline bool br_group_is_l2(const struct br_ip *group)
 {