Commit a65056ec authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by David S. Miller

net: bridge: extend MLD/IGMP query stats

As was suggested this patch adds support for the different versions of MLD
and IGMP query types. Since the user visible structure is still in net-next
we can augment it instead of adding netlink attributes.
The distinction between the different IGMP/MLD query types is done as
suggested in Section 7.1, RFC 3376 [1] and Section 8.1, RFC 3810 [2] based
on query payload size and code for IGMP. Since all IGMP packets go through
multicast_rcv() and it uses ip_mc_check_igmp/ipv6_mc_check_mld we can be
sure that at least the ip/ipv6 header can be directly used.

[1] https://tools.ietf.org/html/rfc3376#section-7
[2] https://tools.ietf.org/html/rfc3810#section-8.1Suggested-by: default avatarLinus Lüssing <linus.luessing@c0d3.blue>
Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Acked-by: default avatarStephen Hemminger <stephen@networkplumber.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f1533cce
...@@ -261,14 +261,17 @@ enum { ...@@ -261,14 +261,17 @@ enum {
/* IGMP/MLD statistics */ /* IGMP/MLD statistics */
struct br_mcast_stats { struct br_mcast_stats {
__u64 igmp_queries[BR_MCAST_DIR_SIZE]; __u64 igmp_v1queries[BR_MCAST_DIR_SIZE];
__u64 igmp_v2queries[BR_MCAST_DIR_SIZE];
__u64 igmp_v3queries[BR_MCAST_DIR_SIZE];
__u64 igmp_leaves[BR_MCAST_DIR_SIZE]; __u64 igmp_leaves[BR_MCAST_DIR_SIZE];
__u64 igmp_v1reports[BR_MCAST_DIR_SIZE]; __u64 igmp_v1reports[BR_MCAST_DIR_SIZE];
__u64 igmp_v2reports[BR_MCAST_DIR_SIZE]; __u64 igmp_v2reports[BR_MCAST_DIR_SIZE];
__u64 igmp_v3reports[BR_MCAST_DIR_SIZE]; __u64 igmp_v3reports[BR_MCAST_DIR_SIZE];
__u64 igmp_parse_errors; __u64 igmp_parse_errors;
__u64 mld_queries[BR_MCAST_DIR_SIZE]; __u64 mld_v1queries[BR_MCAST_DIR_SIZE];
__u64 mld_v2queries[BR_MCAST_DIR_SIZE];
__u64 mld_leaves[BR_MCAST_DIR_SIZE]; __u64 mld_leaves[BR_MCAST_DIR_SIZE];
__u64 mld_v1reports[BR_MCAST_DIR_SIZE]; __u64 mld_v1reports[BR_MCAST_DIR_SIZE];
__u64 mld_v2reports[BR_MCAST_DIR_SIZE]; __u64 mld_v2reports[BR_MCAST_DIR_SIZE];
......
...@@ -199,7 +199,6 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, ...@@ -199,7 +199,6 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
bool unicast) bool unicast)
{ {
u8 igmp_type = br_multicast_igmp_type(skb); u8 igmp_type = br_multicast_igmp_type(skb);
__be16 proto = skb->protocol;
struct net_bridge_port *prev; struct net_bridge_port *prev;
struct net_bridge_port *p; struct net_bridge_port *p;
...@@ -221,7 +220,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, ...@@ -221,7 +220,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
if (IS_ERR(prev)) if (IS_ERR(prev))
goto out; goto out;
if (prev == p) if (prev == p)
br_multicast_count(p->br, p, proto, igmp_type, br_multicast_count(p->br, p, skb, igmp_type,
BR_MCAST_DIR_TX); BR_MCAST_DIR_TX);
} }
...@@ -266,8 +265,6 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, ...@@ -266,8 +265,6 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
struct net_bridge *br = netdev_priv(dev); struct net_bridge *br = netdev_priv(dev);
struct net_bridge_port *prev = NULL; struct net_bridge_port *prev = NULL;
struct net_bridge_port_group *p; struct net_bridge_port_group *p;
__be16 proto = skb->protocol;
struct hlist_node *rp; struct hlist_node *rp;
rp = rcu_dereference(hlist_first_rcu(&br->router_list)); rp = rcu_dereference(hlist_first_rcu(&br->router_list));
...@@ -286,7 +283,7 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, ...@@ -286,7 +283,7 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
if (IS_ERR(prev)) if (IS_ERR(prev))
goto out; goto out;
if (prev == port) if (prev == port)
br_multicast_count(port->br, port, proto, igmp_type, br_multicast_count(port->br, port, skb, igmp_type,
BR_MCAST_DIR_TX); BR_MCAST_DIR_TX);
if ((unsigned long)lport >= (unsigned long)port) if ((unsigned long)lport >= (unsigned long)port)
......
...@@ -61,7 +61,7 @@ static int br_pass_frame_up(struct sk_buff *skb) ...@@ -61,7 +61,7 @@ static int br_pass_frame_up(struct sk_buff *skb)
if (!skb) if (!skb)
return NET_RX_DROP; return NET_RX_DROP;
/* update the multicast stats if the packet is IGMP/MLD */ /* update the multicast stats if the packet is IGMP/MLD */
br_multicast_count(br, NULL, skb->protocol, br_multicast_igmp_type(skb), br_multicast_count(br, NULL, skb, br_multicast_igmp_type(skb),
BR_MCAST_DIR_TX); BR_MCAST_DIR_TX);
return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
......
...@@ -843,14 +843,14 @@ static void __br_multicast_send_query(struct net_bridge *br, ...@@ -843,14 +843,14 @@ static void __br_multicast_send_query(struct net_bridge *br,
if (port) { if (port) {
skb->dev = port->dev; skb->dev = port->dev;
br_multicast_count(br, port, skb->protocol, igmp_type, br_multicast_count(br, port, skb, igmp_type,
BR_MCAST_DIR_TX); BR_MCAST_DIR_TX);
NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
dev_net(port->dev), NULL, skb, NULL, skb->dev, dev_net(port->dev), NULL, skb, NULL, skb->dev,
br_dev_queue_push_xmit); br_dev_queue_push_xmit);
} else { } else {
br_multicast_select_own_querier(br, ip, skb); br_multicast_select_own_querier(br, ip, skb);
br_multicast_count(br, port, skb->protocol, igmp_type, br_multicast_count(br, port, skb, igmp_type,
BR_MCAST_DIR_RX); BR_MCAST_DIR_RX);
netif_rx(skb); netif_rx(skb);
} }
...@@ -1676,7 +1676,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, ...@@ -1676,7 +1676,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
if (skb_trimmed && skb_trimmed != skb) if (skb_trimmed && skb_trimmed != skb)
kfree_skb(skb_trimmed); kfree_skb(skb_trimmed);
br_multicast_count(br, port, skb->protocol, BR_INPUT_SKB_CB(skb)->igmp, br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp,
BR_MCAST_DIR_RX); BR_MCAST_DIR_RX);
return err; return err;
...@@ -1725,7 +1725,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, ...@@ -1725,7 +1725,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
if (skb_trimmed && skb_trimmed != skb) if (skb_trimmed && skb_trimmed != skb)
kfree_skb(skb_trimmed); kfree_skb(skb_trimmed);
br_multicast_count(br, port, skb->protocol, BR_INPUT_SKB_CB(skb)->igmp, br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp,
BR_MCAST_DIR_RX); BR_MCAST_DIR_RX);
return err; return err;
...@@ -2251,13 +2251,16 @@ bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto) ...@@ -2251,13 +2251,16 @@ bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
EXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent); EXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent);
static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats, static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats,
__be16 proto, u8 type, u8 dir) const struct sk_buff *skb, u8 type, u8 dir)
{ {
struct bridge_mcast_stats *pstats = this_cpu_ptr(stats); struct bridge_mcast_stats *pstats = this_cpu_ptr(stats);
__be16 proto = skb->protocol;
unsigned int t_len;
u64_stats_update_begin(&pstats->syncp); u64_stats_update_begin(&pstats->syncp);
switch (proto) { switch (proto) {
case htons(ETH_P_IP): case htons(ETH_P_IP):
t_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb);
switch (type) { switch (type) {
case IGMP_HOST_MEMBERSHIP_REPORT: case IGMP_HOST_MEMBERSHIP_REPORT:
pstats->mstats.igmp_v1reports[dir]++; pstats->mstats.igmp_v1reports[dir]++;
...@@ -2269,7 +2272,21 @@ static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats, ...@@ -2269,7 +2272,21 @@ static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats,
pstats->mstats.igmp_v3reports[dir]++; pstats->mstats.igmp_v3reports[dir]++;
break; break;
case IGMP_HOST_MEMBERSHIP_QUERY: case IGMP_HOST_MEMBERSHIP_QUERY:
pstats->mstats.igmp_queries[dir]++; if (t_len != sizeof(struct igmphdr)) {
pstats->mstats.igmp_v3queries[dir]++;
} else {
unsigned int offset = skb_transport_offset(skb);
struct igmphdr *ih, _ihdr;
ih = skb_header_pointer(skb, offset,
sizeof(_ihdr), &_ihdr);
if (!ih)
break;
if (!ih->code)
pstats->mstats.igmp_v1queries[dir]++;
else
pstats->mstats.igmp_v2queries[dir]++;
}
break; break;
case IGMP_HOST_LEAVE_MESSAGE: case IGMP_HOST_LEAVE_MESSAGE:
pstats->mstats.igmp_leaves[dir]++; pstats->mstats.igmp_leaves[dir]++;
...@@ -2278,6 +2295,9 @@ static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats, ...@@ -2278,6 +2295,9 @@ static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats,
break; break;
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
case htons(ETH_P_IPV6): case htons(ETH_P_IPV6):
t_len = ntohs(ipv6_hdr(skb)->payload_len) +
sizeof(struct ipv6hdr);
t_len -= skb_network_header_len(skb);
switch (type) { switch (type) {
case ICMPV6_MGM_REPORT: case ICMPV6_MGM_REPORT:
pstats->mstats.mld_v1reports[dir]++; pstats->mstats.mld_v1reports[dir]++;
...@@ -2286,7 +2306,10 @@ static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats, ...@@ -2286,7 +2306,10 @@ static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats,
pstats->mstats.mld_v2reports[dir]++; pstats->mstats.mld_v2reports[dir]++;
break; break;
case ICMPV6_MGM_QUERY: case ICMPV6_MGM_QUERY:
pstats->mstats.mld_queries[dir]++; if (t_len != sizeof(struct mld_msg))
pstats->mstats.mld_v2queries[dir]++;
else
pstats->mstats.mld_v1queries[dir]++;
break; break;
case ICMPV6_MGM_REDUCTION: case ICMPV6_MGM_REDUCTION:
pstats->mstats.mld_leaves[dir]++; pstats->mstats.mld_leaves[dir]++;
...@@ -2299,7 +2322,7 @@ static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats, ...@@ -2299,7 +2322,7 @@ static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats,
} }
void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p, void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p,
__be16 proto, u8 type, u8 dir) const struct sk_buff *skb, u8 type, u8 dir)
{ {
struct bridge_mcast_stats __percpu *stats; struct bridge_mcast_stats __percpu *stats;
...@@ -2314,7 +2337,7 @@ void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p, ...@@ -2314,7 +2337,7 @@ void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p,
if (WARN_ON(!stats)) if (WARN_ON(!stats))
return; return;
br_mcast_stats_add(stats, proto, type, dir); br_mcast_stats_add(stats, skb, type, dir);
} }
int br_multicast_init_stats(struct net_bridge *br) int br_multicast_init_stats(struct net_bridge *br)
...@@ -2359,14 +2382,17 @@ void br_multicast_get_stats(const struct net_bridge *br, ...@@ -2359,14 +2382,17 @@ void br_multicast_get_stats(const struct net_bridge *br,
memcpy(&temp, &cpu_stats->mstats, sizeof(temp)); memcpy(&temp, &cpu_stats->mstats, sizeof(temp));
} while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start));
mcast_stats_add_dir(tdst.igmp_queries, temp.igmp_queries); mcast_stats_add_dir(tdst.igmp_v1queries, temp.igmp_v1queries);
mcast_stats_add_dir(tdst.igmp_v2queries, temp.igmp_v2queries);
mcast_stats_add_dir(tdst.igmp_v3queries, temp.igmp_v3queries);
mcast_stats_add_dir(tdst.igmp_leaves, temp.igmp_leaves); mcast_stats_add_dir(tdst.igmp_leaves, temp.igmp_leaves);
mcast_stats_add_dir(tdst.igmp_v1reports, temp.igmp_v1reports); mcast_stats_add_dir(tdst.igmp_v1reports, temp.igmp_v1reports);
mcast_stats_add_dir(tdst.igmp_v2reports, temp.igmp_v2reports); mcast_stats_add_dir(tdst.igmp_v2reports, temp.igmp_v2reports);
mcast_stats_add_dir(tdst.igmp_v3reports, temp.igmp_v3reports); mcast_stats_add_dir(tdst.igmp_v3reports, temp.igmp_v3reports);
tdst.igmp_parse_errors += temp.igmp_parse_errors; tdst.igmp_parse_errors += temp.igmp_parse_errors;
mcast_stats_add_dir(tdst.mld_queries, temp.mld_queries); mcast_stats_add_dir(tdst.mld_v1queries, temp.mld_v1queries);
mcast_stats_add_dir(tdst.mld_v2queries, temp.mld_v2queries);
mcast_stats_add_dir(tdst.mld_leaves, temp.mld_leaves); mcast_stats_add_dir(tdst.mld_leaves, temp.mld_leaves);
mcast_stats_add_dir(tdst.mld_v1reports, temp.mld_v1reports); mcast_stats_add_dir(tdst.mld_v1reports, temp.mld_v1reports);
mcast_stats_add_dir(tdst.mld_v2reports, temp.mld_v2reports); mcast_stats_add_dir(tdst.mld_v2reports, temp.mld_v2reports);
......
...@@ -586,7 +586,7 @@ void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, ...@@ -586,7 +586,7 @@ void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port, void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
int type); int type);
void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p, void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p,
__be16 proto, u8 type, u8 dir); const struct sk_buff *skb, u8 type, u8 dir);
int br_multicast_init_stats(struct net_bridge *br); int br_multicast_init_stats(struct net_bridge *br);
void br_multicast_get_stats(const struct net_bridge *br, void br_multicast_get_stats(const struct net_bridge *br,
const struct net_bridge_port *p, const struct net_bridge_port *p,
...@@ -719,7 +719,8 @@ static inline void br_mdb_uninit(void) ...@@ -719,7 +719,8 @@ static inline void br_mdb_uninit(void)
static inline void br_multicast_count(struct net_bridge *br, static inline void br_multicast_count(struct net_bridge *br,
const struct net_bridge_port *p, const struct net_bridge_port *p,
__be16 proto, u8 type, u8 dir) const struct sk_buff *skb,
u8 type, u8 dir)
{ {
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment