Commit 99b40610 authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by Jakub Kicinski
Browse files

net: bridge: mcast: add and enforce query interval minimum

As reported[1] if query interval is set too low and we have multiple
bridges or even a single bridge with multiple querier vlans configured
we can crash the machine. Add a 1 second minimum which must be enforced
by overwriting the value if set lower (i.e. without returning an error) to
avoid breaking user-space. If that happens a log message is emitted to let
the administrator know that the interval has been set to the minimum.
The issue has been present since these intervals could be user-controlled.

[1] https://lore.kernel.org/netdev/e8b9ce41-57b9-b6e2-a46a-ff9c791cf0ba@gmail.com/



Fixes: d902eee4 ("bridge: Add multicast count/interval sysfs entries")
Reported-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarNikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent fb7bc920
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -4522,6 +4522,22 @@ int br_multicast_set_mld_version(struct net_bridge_mcast *brmctx,
}
#endif

void br_multicast_set_query_intvl(struct net_bridge_mcast *brmctx,
				  unsigned long val)
{
	unsigned long intvl_jiffies = clock_t_to_jiffies(val);

	if (intvl_jiffies < BR_MULTICAST_QUERY_INTVL_MIN) {
		br_info(brmctx->br,
			"trying to set multicast query interval below minimum, setting to %lu (%ums)\n",
			jiffies_to_clock_t(BR_MULTICAST_QUERY_INTVL_MIN),
			jiffies_to_msecs(BR_MULTICAST_QUERY_INTVL_MIN));
		intvl_jiffies = BR_MULTICAST_QUERY_INTVL_MIN;
	}

	brmctx->multicast_query_interval = intvl_jiffies;
}

/**
 * br_multicast_list_adjacent - Returns snooped multicast addresses
 * @dev:	The bridge port adjacent to which to retrieve addresses
+1 −1
Original line number Diff line number Diff line
@@ -1357,7 +1357,7 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
	if (data[IFLA_BR_MCAST_QUERY_INTVL]) {
		u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERY_INTVL]);

		br->multicast_ctx.multicast_query_interval = clock_t_to_jiffies(val);
		br_multicast_set_query_intvl(&br->multicast_ctx, val);
	}

	if (data[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) {
+3 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#define BR_MAX_PORTS	(1<<BR_PORT_BITS)

#define BR_MULTICAST_DEFAULT_HASH_MAX 4096
#define BR_MULTICAST_QUERY_INTVL_MIN msecs_to_jiffies(1000)

#define BR_HWDOM_MAX BITS_PER_LONG

@@ -963,6 +964,8 @@ int br_multicast_dump_querier_state(struct sk_buff *skb,
				    int nest_attr);
size_t br_multicast_querier_state_size(void);
size_t br_rports_size(const struct net_bridge_mcast *brmctx);
void br_multicast_set_query_intvl(struct net_bridge_mcast *brmctx,
				  unsigned long val);

static inline bool br_group_is_l2(const struct br_ip *group)
{
+1 −1
Original line number Diff line number Diff line
@@ -658,7 +658,7 @@ static ssize_t multicast_query_interval_show(struct device *d,
static int set_query_interval(struct net_bridge *br, unsigned long val,
			      struct netlink_ext_ack *extack)
{
	br->multicast_ctx.multicast_query_interval = clock_t_to_jiffies(val);
	br_multicast_set_query_intvl(&br->multicast_ctx, val);
	return 0;
}

+1 −1
Original line number Diff line number Diff line
@@ -521,7 +521,7 @@ static int br_vlan_process_global_one_opts(const struct net_bridge *br,
		u64 val;

		val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL]);
		v->br_mcast_ctx.multicast_query_interval = clock_t_to_jiffies(val);
		br_multicast_set_query_intvl(&v->br_mcast_ctx, val);
		*changed = true;
	}
	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL]) {