Skip to content

Commit

Permalink
net: bridge: extend MLD/IGMP query stats
Browse files Browse the repository at this point in the history
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.1

Suggested-by: Linus Lüssing <linus.luessing@c0d3.blue>
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Acked-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
NikAleksandrov authored and davem330 committed Jul 9, 2016
1 parent f1533cc commit a65056e
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 21 deletions.
7 changes: 5 additions & 2 deletions include/uapi/linux/if_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,14 +261,17 @@ enum {

/* IGMP/MLD statistics */
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_v1reports[BR_MCAST_DIR_SIZE];
__u64 igmp_v2reports[BR_MCAST_DIR_SIZE];
__u64 igmp_v3reports[BR_MCAST_DIR_SIZE];
__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_v1reports[BR_MCAST_DIR_SIZE];
__u64 mld_v2reports[BR_MCAST_DIR_SIZE];
Expand Down
7 changes: 2 additions & 5 deletions net/bridge/br_forward.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
bool unicast)
{
u8 igmp_type = br_multicast_igmp_type(skb);
__be16 proto = skb->protocol;
struct net_bridge_port *prev;
struct net_bridge_port *p;

Expand All @@ -221,7 +220,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
if (IS_ERR(prev))
goto out;
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);
}

Expand Down Expand Up @@ -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_port *prev = NULL;
struct net_bridge_port_group *p;
__be16 proto = skb->protocol;

struct hlist_node *rp;

rp = rcu_dereference(hlist_first_rcu(&br->router_list));
Expand All @@ -286,7 +283,7 @@ static void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
if (IS_ERR(prev))
goto out;
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);

if ((unsigned long)lport >= (unsigned long)port)
Expand Down
2 changes: 1 addition & 1 deletion net/bridge/br_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ static int br_pass_frame_up(struct sk_buff *skb)
if (!skb)
return NET_RX_DROP;
/* 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);

return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
Expand Down
48 changes: 37 additions & 11 deletions net/bridge/br_multicast.c
Original file line number Diff line number Diff line change
Expand Up @@ -843,14 +843,14 @@ static void __br_multicast_send_query(struct net_bridge *br,

if (port) {
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);
NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
dev_net(port->dev), NULL, skb, NULL, skb->dev,
br_dev_queue_push_xmit);
} else {
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);
netif_rx(skb);
}
Expand Down Expand Up @@ -1676,7 +1676,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
if (skb_trimmed && skb_trimmed != skb)
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);

return err;
Expand Down Expand Up @@ -1725,7 +1725,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
if (skb_trimmed && skb_trimmed != skb)
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);

return err;
Expand Down Expand Up @@ -2251,13 +2251,16 @@ bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
EXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent);

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);
__be16 proto = skb->protocol;
unsigned int t_len;

u64_stats_update_begin(&pstats->syncp);
switch (proto) {
case htons(ETH_P_IP):
t_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb);
switch (type) {
case IGMP_HOST_MEMBERSHIP_REPORT:
pstats->mstats.igmp_v1reports[dir]++;
Expand All @@ -2269,7 +2272,21 @@ static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats,
pstats->mstats.igmp_v3reports[dir]++;
break;
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;
case IGMP_HOST_LEAVE_MESSAGE:
pstats->mstats.igmp_leaves[dir]++;
Expand All @@ -2278,6 +2295,9 @@ static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats,
break;
#if IS_ENABLED(CONFIG_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) {
case ICMPV6_MGM_REPORT:
pstats->mstats.mld_v1reports[dir]++;
Expand All @@ -2286,7 +2306,10 @@ static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats,
pstats->mstats.mld_v2reports[dir]++;
break;
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;
case ICMPV6_MGM_REDUCTION:
pstats->mstats.mld_leaves[dir]++;
Expand All @@ -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,
__be16 proto, u8 type, u8 dir)
const struct sk_buff *skb, u8 type, u8 dir)
{
struct bridge_mcast_stats __percpu *stats;

Expand All @@ -2314,7 +2337,7 @@ void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p,
if (WARN_ON(!stats))
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)
Expand Down Expand Up @@ -2359,14 +2382,17 @@ void br_multicast_get_stats(const struct net_bridge *br,
memcpy(&temp, &cpu_stats->mstats, sizeof(temp));
} 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_v1reports, temp.igmp_v1reports);
mcast_stats_add_dir(tdst.igmp_v2reports, temp.igmp_v2reports);
mcast_stats_add_dir(tdst.igmp_v3reports, temp.igmp_v3reports);
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_v1reports, temp.mld_v1reports);
mcast_stats_add_dir(tdst.mld_v2reports, temp.mld_v2reports);
Expand Down
5 changes: 3 additions & 2 deletions net/bridge/br_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
int type);
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);
void br_multicast_get_stats(const struct net_bridge *br,
const struct net_bridge_port *p,
Expand Down Expand Up @@ -719,7 +719,8 @@ static inline void br_mdb_uninit(void)

static inline 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)
{
}

Expand Down

0 comments on commit a65056e

Please sign in to comment.