Skip to content

Commit 8db102a

Browse files
committed
Merge branch 'bridgge-mcast'
Nikolay Aleksandrov says: ==================== net: bridge: mcast: dump querier state This set adds the ability to dump the current multicast querier state. This is extremely useful when debugging multicast issues, we've had many cases of unexpected queriers causing strange behaviour and mcast test failures. The first patch changes the querier struct to record a port device's ifindex instead of a pointer to the port itself so we can later retrieve it, I chose this way because it's much simpler and doesn't require us to do querier port ref counting, it is best effort anyway. Then patch 02 makes the querier address/port updates consistent via a combination of multicast_lock and seqcount, so readers can only use seqcount to get a consistent snapshot of address and port. Patch 03 is a minor cleanup in preparation for the dump support, it consolidates IPv4 and IPv6 querier selection paths as they share most of the logic (except address comparisons of course). Finally the last three patches add the new querier state dumping support, for the bridge's global multicast context we embed the BRIDGE_QUERIER_xxx attributes into IFLA_BR_MCAST_QUERIER_STATE and for the per-vlan global mcast contexts we embed them into BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE. The structure is: [IFLA_BR_MCAST_QUERIER_STATE / BRIDGE_VLANDB_GOPTS_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 `[BRIDGE_QUERIER_IPV6_ADDRESS] - ip address of the querier `[BRIDGE_QUERIER_IPV6_PORT] - bridge port ifindex where the querier was seen (set only if external querier) `[BRIDGE_QUERIER_IPV6_OTHER_TIMER] - other querier timeout Later we can also add IGMP version of seen queriers and last seen values from the queries. ====================
2 parents 2fa1678 + ddc649d commit 8db102a

File tree

6 files changed

+199
-44
lines changed

6 files changed

+199
-44
lines changed

include/uapi/linux/if_bridge.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,7 @@ enum {
563563
BRIDGE_VLANDB_GOPTS_MCAST_QUERIER,
564564
BRIDGE_VLANDB_GOPTS_MCAST_ROUTER,
565565
BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS,
566+
BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE,
566567
__BRIDGE_VLANDB_GOPTS_MAX
567568
};
568569
#define BRIDGE_VLANDB_GOPTS_MAX (__BRIDGE_VLANDB_GOPTS_MAX - 1)
@@ -770,4 +771,17 @@ struct br_boolopt_multi {
770771
__u32 optval;
771772
__u32 optmask;
772773
};
774+
775+
enum {
776+
BRIDGE_QUERIER_UNSPEC,
777+
BRIDGE_QUERIER_IP_ADDRESS,
778+
BRIDGE_QUERIER_IP_PORT,
779+
BRIDGE_QUERIER_IP_OTHER_TIMER,
780+
BRIDGE_QUERIER_PAD,
781+
BRIDGE_QUERIER_IPV6_ADDRESS,
782+
BRIDGE_QUERIER_IPV6_PORT,
783+
BRIDGE_QUERIER_IPV6_OTHER_TIMER,
784+
__BRIDGE_QUERIER_MAX
785+
};
786+
#define BRIDGE_QUERIER_MAX (__BRIDGE_QUERIER_MAX - 1)
773787
#endif /* _UAPI_LINUX_IF_BRIDGE_H */

include/uapi/linux/if_link.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ enum {
479479
IFLA_BR_MCAST_MLD_VERSION,
480480
IFLA_BR_VLAN_STATS_PER_PORT,
481481
IFLA_BR_MULTI_BOOLOPT,
482+
IFLA_BR_MCAST_QUERIER_STATE,
482483
__IFLA_BR_MAX,
483484
};
484485

net/bridge/br_multicast.c

Lines changed: 170 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,11 +1659,38 @@ static void __br_multicast_send_query(struct net_bridge_mcast *brmctx,
16591659
}
16601660
}
16611661

1662+
static void br_multicast_read_querier(const struct bridge_mcast_querier *querier,
1663+
struct bridge_mcast_querier *dest)
1664+
{
1665+
unsigned int seq;
1666+
1667+
memset(dest, 0, sizeof(*dest));
1668+
do {
1669+
seq = read_seqcount_begin(&querier->seq);
1670+
dest->port_ifidx = querier->port_ifidx;
1671+
memcpy(&dest->addr, &querier->addr, sizeof(struct br_ip));
1672+
} while (read_seqcount_retry(&querier->seq, seq));
1673+
}
1674+
1675+
static void br_multicast_update_querier(struct net_bridge_mcast *brmctx,
1676+
struct bridge_mcast_querier *querier,
1677+
int ifindex,
1678+
struct br_ip *saddr)
1679+
{
1680+
lockdep_assert_held_once(&brmctx->br->multicast_lock);
1681+
1682+
write_seqcount_begin(&querier->seq);
1683+
querier->port_ifidx = ifindex;
1684+
memcpy(&querier->addr, saddr, sizeof(*saddr));
1685+
write_seqcount_end(&querier->seq);
1686+
}
1687+
16621688
static void br_multicast_send_query(struct net_bridge_mcast *brmctx,
16631689
struct net_bridge_mcast_port *pmctx,
16641690
struct bridge_mcast_own_query *own_query)
16651691
{
16661692
struct bridge_mcast_other_query *other_query = NULL;
1693+
struct bridge_mcast_querier *querier;
16671694
struct br_ip br_group;
16681695
unsigned long time;
16691696

@@ -1676,10 +1703,12 @@ static void br_multicast_send_query(struct net_bridge_mcast *brmctx,
16761703

16771704
if (pmctx ? (own_query == &pmctx->ip4_own_query) :
16781705
(own_query == &brmctx->ip4_own_query)) {
1706+
querier = &brmctx->ip4_querier;
16791707
other_query = &brmctx->ip4_other_query;
16801708
br_group.proto = htons(ETH_P_IP);
16811709
#if IS_ENABLED(CONFIG_IPV6)
16821710
} else {
1711+
querier = &brmctx->ip6_querier;
16831712
other_query = &brmctx->ip6_other_query;
16841713
br_group.proto = htons(ETH_P_IPV6);
16851714
#endif
@@ -1688,6 +1717,13 @@ static void br_multicast_send_query(struct net_bridge_mcast *brmctx,
16881717
if (!other_query || timer_pending(&other_query->timer))
16891718
return;
16901719

1720+
/* we're about to select ourselves as querier */
1721+
if (!pmctx && querier->port_ifidx) {
1722+
struct br_ip zeroip = {};
1723+
1724+
br_multicast_update_querier(brmctx, querier, 0, &zeroip);
1725+
}
1726+
16911727
__br_multicast_send_query(brmctx, pmctx, NULL, NULL, &br_group, false,
16921728
0, NULL);
16931729

@@ -2828,58 +2864,147 @@ static int br_ip6_multicast_mld2_report(struct net_bridge_mcast *brmctx,
28282864
}
28292865
#endif
28302866

2831-
static bool br_ip4_multicast_select_querier(struct net_bridge_mcast *brmctx,
2832-
struct net_bridge_mcast_port *pmctx,
2833-
__be32 saddr)
2867+
static bool br_multicast_select_querier(struct net_bridge_mcast *brmctx,
2868+
struct net_bridge_mcast_port *pmctx,
2869+
struct br_ip *saddr)
28342870
{
2835-
struct net_bridge_port *port = pmctx ? pmctx->port : NULL;
2871+
int port_ifidx = pmctx ? pmctx->port->dev->ifindex : 0;
2872+
struct timer_list *own_timer, *other_timer;
2873+
struct bridge_mcast_querier *querier;
28362874

2837-
if (!timer_pending(&brmctx->ip4_own_query.timer) &&
2838-
!timer_pending(&brmctx->ip4_other_query.timer))
2839-
goto update;
2840-
2841-
if (!brmctx->ip4_querier.addr.src.ip4)
2842-
goto update;
2875+
switch (saddr->proto) {
2876+
case htons(ETH_P_IP):
2877+
querier = &brmctx->ip4_querier;
2878+
own_timer = &brmctx->ip4_own_query.timer;
2879+
other_timer = &brmctx->ip4_other_query.timer;
2880+
if (!querier->addr.src.ip4 ||
2881+
ntohl(saddr->src.ip4) <= ntohl(querier->addr.src.ip4))
2882+
goto update;
2883+
break;
2884+
#if IS_ENABLED(CONFIG_IPV6)
2885+
case htons(ETH_P_IPV6):
2886+
querier = &brmctx->ip6_querier;
2887+
own_timer = &brmctx->ip6_own_query.timer;
2888+
other_timer = &brmctx->ip6_other_query.timer;
2889+
if (ipv6_addr_cmp(&saddr->src.ip6, &querier->addr.src.ip6) <= 0)
2890+
goto update;
2891+
break;
2892+
#endif
2893+
default:
2894+
return false;
2895+
}
28432896

2844-
if (ntohl(saddr) <= ntohl(brmctx->ip4_querier.addr.src.ip4))
2897+
if (!timer_pending(own_timer) && !timer_pending(other_timer))
28452898
goto update;
28462899

28472900
return false;
28482901

28492902
update:
2850-
brmctx->ip4_querier.addr.src.ip4 = saddr;
2851-
2852-
/* update protected by general multicast_lock by caller */
2853-
rcu_assign_pointer(brmctx->ip4_querier.port, port);
2903+
br_multicast_update_querier(brmctx, querier, port_ifidx, saddr);
28542904

28552905
return true;
28562906
}
28572907

2858-
#if IS_ENABLED(CONFIG_IPV6)
2859-
static bool br_ip6_multicast_select_querier(struct net_bridge_mcast *brmctx,
2860-
struct net_bridge_mcast_port *pmctx,
2861-
struct in6_addr *saddr)
2908+
static struct net_bridge_port *
2909+
__br_multicast_get_querier_port(struct net_bridge *br,
2910+
const struct bridge_mcast_querier *querier)
28622911
{
2863-
struct net_bridge_port *port = pmctx ? pmctx->port : NULL;
2912+
int port_ifidx = READ_ONCE(querier->port_ifidx);
2913+
struct net_bridge_port *p;
2914+
struct net_device *dev;
28642915

2865-
if (!timer_pending(&brmctx->ip6_own_query.timer) &&
2866-
!timer_pending(&brmctx->ip6_other_query.timer))
2867-
goto update;
2916+
if (port_ifidx == 0)
2917+
return NULL;
28682918

2869-
if (ipv6_addr_cmp(saddr, &brmctx->ip6_querier.addr.src.ip6) <= 0)
2870-
goto update;
2919+
dev = dev_get_by_index_rcu(dev_net(br->dev), port_ifidx);
2920+
if (!dev)
2921+
return NULL;
2922+
p = br_port_get_rtnl_rcu(dev);
2923+
if (!p || p->br != br)
2924+
return NULL;
28712925

2872-
return false;
2926+
return p;
2927+
}
28732928

2874-
update:
2875-
brmctx->ip6_querier.addr.src.ip6 = *saddr;
2929+
size_t br_multicast_querier_state_size(void)
2930+
{
2931+
return nla_total_size(sizeof(0)) + /* nest attribute */
2932+
nla_total_size(sizeof(__be32)) + /* BRIDGE_QUERIER_IP_ADDRESS */
2933+
nla_total_size(sizeof(int)) + /* BRIDGE_QUERIER_IP_PORT */
2934+
nla_total_size_64bit(sizeof(u64)); /* BRIDGE_QUERIER_IP_OTHER_TIMER */
2935+
}
28762936

2877-
/* update protected by general multicast_lock by caller */
2878-
rcu_assign_pointer(brmctx->ip6_querier.port, port);
2937+
/* protected by rtnl or rcu */
2938+
int br_multicast_dump_querier_state(struct sk_buff *skb,
2939+
const struct net_bridge_mcast *brmctx,
2940+
int nest_attr)
2941+
{
2942+
struct bridge_mcast_querier querier = {};
2943+
struct net_bridge_port *p;
2944+
struct nlattr *nest;
28792945

2880-
return true;
2881-
}
2946+
nest = nla_nest_start(skb, nest_attr);
2947+
if (!nest)
2948+
return -EMSGSIZE;
2949+
2950+
rcu_read_lock();
2951+
if (!brmctx->multicast_querier &&
2952+
!timer_pending(&brmctx->ip4_other_query.timer))
2953+
goto out_v6;
2954+
2955+
br_multicast_read_querier(&brmctx->ip4_querier, &querier);
2956+
if (nla_put_in_addr(skb, BRIDGE_QUERIER_IP_ADDRESS,
2957+
querier.addr.src.ip4)) {
2958+
rcu_read_unlock();
2959+
goto out_err;
2960+
}
2961+
2962+
p = __br_multicast_get_querier_port(brmctx->br, &querier);
2963+
if (timer_pending(&brmctx->ip4_other_query.timer) &&
2964+
(nla_put_u64_64bit(skb, BRIDGE_QUERIER_IP_OTHER_TIMER,
2965+
br_timer_value(&brmctx->ip4_other_query.timer),
2966+
BRIDGE_QUERIER_PAD) ||
2967+
(p && nla_put_u32(skb, BRIDGE_QUERIER_IP_PORT, p->dev->ifindex)))) {
2968+
rcu_read_unlock();
2969+
goto out_err;
2970+
}
2971+
2972+
out_v6:
2973+
#if IS_ENABLED(CONFIG_IPV6)
2974+
if (!brmctx->multicast_querier &&
2975+
!timer_pending(&brmctx->ip6_other_query.timer))
2976+
goto out;
2977+
2978+
br_multicast_read_querier(&brmctx->ip6_querier, &querier);
2979+
if (nla_put_in6_addr(skb, BRIDGE_QUERIER_IPV6_ADDRESS,
2980+
&querier.addr.src.ip6)) {
2981+
rcu_read_unlock();
2982+
goto out_err;
2983+
}
2984+
2985+
p = __br_multicast_get_querier_port(brmctx->br, &querier);
2986+
if (timer_pending(&brmctx->ip6_other_query.timer) &&
2987+
(nla_put_u64_64bit(skb, BRIDGE_QUERIER_IPV6_OTHER_TIMER,
2988+
br_timer_value(&brmctx->ip6_other_query.timer),
2989+
BRIDGE_QUERIER_PAD) ||
2990+
(p && nla_put_u32(skb, BRIDGE_QUERIER_IPV6_PORT,
2991+
p->dev->ifindex)))) {
2992+
rcu_read_unlock();
2993+
goto out_err;
2994+
}
2995+
out:
28822996
#endif
2997+
rcu_read_unlock();
2998+
nla_nest_end(skb, nest);
2999+
if (!nla_len(nest))
3000+
nla_nest_cancel(skb, nest);
3001+
3002+
return 0;
3003+
3004+
out_err:
3005+
nla_nest_cancel(skb, nest);
3006+
return -EMSGSIZE;
3007+
}
28833008

28843009
static void
28853010
br_multicast_update_query_timer(struct net_bridge_mcast *brmctx,
@@ -3082,7 +3207,7 @@ br_ip4_multicast_query_received(struct net_bridge_mcast *brmctx,
30823207
struct br_ip *saddr,
30833208
unsigned long max_delay)
30843209
{
3085-
if (!br_ip4_multicast_select_querier(brmctx, pmctx, saddr->src.ip4))
3210+
if (!br_multicast_select_querier(brmctx, pmctx, saddr))
30863211
return;
30873212

30883213
br_multicast_update_query_timer(brmctx, query, max_delay);
@@ -3097,7 +3222,7 @@ br_ip6_multicast_query_received(struct net_bridge_mcast *brmctx,
30973222
struct br_ip *saddr,
30983223
unsigned long max_delay)
30993224
{
3100-
if (!br_ip6_multicast_select_querier(brmctx, pmctx, &saddr->src.ip6))
3225+
if (!br_multicast_select_querier(brmctx, pmctx, saddr))
31013226
return;
31023227

31033228
br_multicast_update_query_timer(brmctx, query, max_delay);
@@ -3117,7 +3242,7 @@ static void br_ip4_multicast_query(struct net_bridge_mcast *brmctx,
31173242
struct igmpv3_query *ih3;
31183243
struct net_bridge_port_group *p;
31193244
struct net_bridge_port_group __rcu **pp;
3120-
struct br_ip saddr;
3245+
struct br_ip saddr = {};
31213246
unsigned long max_delay;
31223247
unsigned long now = jiffies;
31233248
__be32 group;
@@ -3197,7 +3322,7 @@ static int br_ip6_multicast_query(struct net_bridge_mcast *brmctx,
31973322
struct mld2_query *mld2q;
31983323
struct net_bridge_port_group *p;
31993324
struct net_bridge_port_group __rcu **pp;
3200-
struct br_ip saddr;
3325+
struct br_ip saddr = {};
32013326
unsigned long max_delay;
32023327
unsigned long now = jiffies;
32033328
unsigned int offset = skb_transport_offset(skb);
@@ -3675,7 +3800,6 @@ static void br_multicast_query_expired(struct net_bridge_mcast *brmctx,
36753800
if (query->startup_sent < brmctx->multicast_startup_query_count)
36763801
query->startup_sent++;
36773802

3678-
RCU_INIT_POINTER(querier->port, NULL);
36793803
br_multicast_send_query(brmctx, NULL, query);
36803804
out:
36813805
spin_unlock(&brmctx->br->multicast_lock);
@@ -3732,12 +3856,14 @@ void br_multicast_ctx_init(struct net_bridge *br,
37323856
brmctx->multicast_membership_interval = 260 * HZ;
37333857

37343858
brmctx->ip4_other_query.delay_time = 0;
3735-
brmctx->ip4_querier.port = NULL;
3859+
brmctx->ip4_querier.port_ifidx = 0;
3860+
seqcount_init(&brmctx->ip4_querier.seq);
37363861
brmctx->multicast_igmp_version = 2;
37373862
#if IS_ENABLED(CONFIG_IPV6)
37383863
brmctx->multicast_mld_version = 1;
37393864
brmctx->ip6_other_query.delay_time = 0;
3740-
brmctx->ip6_querier.port = NULL;
3865+
brmctx->ip6_querier.port_ifidx = 0;
3866+
seqcount_init(&brmctx->ip6_querier.seq);
37413867
#endif
37423868

37433869
timer_setup(&brmctx->ip4_mc_router_timer,
@@ -4479,6 +4605,7 @@ bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
44794605
struct net_bridge *br;
44804606
struct net_bridge_port *port;
44814607
bool ret = false;
4608+
int port_ifidx;
44824609

44834610
rcu_read_lock();
44844611
if (!netif_is_bridge_port(dev))
@@ -4493,14 +4620,16 @@ bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
44934620

44944621
switch (proto) {
44954622
case ETH_P_IP:
4623+
port_ifidx = brmctx->ip4_querier.port_ifidx;
44964624
if (!timer_pending(&brmctx->ip4_other_query.timer) ||
4497-
rcu_dereference(brmctx->ip4_querier.port) == port)
4625+
port_ifidx == port->dev->ifindex)
44984626
goto unlock;
44994627
break;
45004628
#if IS_ENABLED(CONFIG_IPV6)
45014629
case ETH_P_IPV6:
4630+
port_ifidx = brmctx->ip6_querier.port_ifidx;
45024631
if (!timer_pending(&brmctx->ip6_other_query.timer) ||
4503-
rcu_dereference(brmctx->ip6_querier.port) == port)
4632+
port_ifidx == port->dev->ifindex)
45044633
goto unlock;
45054634
break;
45064635
#endif

net/bridge/br_netlink.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1501,6 +1501,7 @@ static size_t br_get_size(const struct net_device *brdev)
15011501
nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_STARTUP_QUERY_INTVL */
15021502
nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_IGMP_VERSION */
15031503
nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_MLD_VERSION */
1504+
br_multicast_querier_state_size() + /* IFLA_BR_MCAST_QUERIER_STATE */
15041505
#endif
15051506
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
15061507
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)
15871588
nla_put_u32(skb, IFLA_BR_MCAST_STARTUP_QUERY_CNT,
15881589
br->multicast_ctx.multicast_startup_query_count) ||
15891590
nla_put_u8(skb, IFLA_BR_MCAST_IGMP_VERSION,
1590-
br->multicast_ctx.multicast_igmp_version))
1591+
br->multicast_ctx.multicast_igmp_version) ||
1592+
br_multicast_dump_querier_state(skb, &br->multicast_ctx,
1593+
IFLA_BR_MCAST_QUERIER_STATE))
15911594
return -EMSGSIZE;
15921595
#if IS_ENABLED(CONFIG_IPV6)
15931596
if (nla_put_u8(skb, IFLA_BR_MCAST_MLD_VERSION,

0 commit comments

Comments
 (0)