Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ff223789 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'bridge-bools'



Nikolay Aleksandrov says:

====================
net: bridge: add an option to disabe linklocal learning

This set adds a new bridge option which can control learning from
link-local packets, by default learning is on to be consistent and avoid
breaking users expectations. If the new no_linklocal_learn option is
enabled then the bridge will stop learning from link-local packets.

In order to save space for future boolean options, patch 01 adds a new
bool option API that uses a bitmask to control boolean options. The
bridge is by far the largest netlink attr user and we keep adding simple
boolean options which waste nl attr ids and space. We're not directly
mapping these to the in-kernel bridge flags because some might require
more complex configuration changes (e.g. if we were to add the per port
vlan stats now, it'd require multiple checks before changing value).
Any new bool option needs to be handled by both br_boolopt_toggle and get
in order to be able to retrieve its state later. All such options are
automatically exported via netlink. The behaviour of setting such
options is consistent with netlink option handling when a missing
option is being set (silently ignored), e.g. when a newer iproute2 is used
on older kernel. All supported options are exported via bm's optmask
when dumping the new attribute.

v2: address Andrew Lunn's comments, squash a minor change into patch 01,
    export all supported options via optmask when dumping, add patch 03,
    pass down extack so options can return meaningful errors, add
    WARN_ON on unsupported options (should not happen)
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 02c72d5e 1ed1ccb9
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -292,4 +292,25 @@ struct br_mcast_stats {
	__u64 mcast_bytes[BR_MCAST_DIR_SIZE];
	__u64 mcast_packets[BR_MCAST_DIR_SIZE];
};

/* bridge boolean options
 * BR_BOOLOPT_NO_LL_LEARN - disable learning from link-local packets
 *
 * IMPORTANT: if adding a new option do not forget to handle
 *            it in br_boolopt_toggle/get and bridge sysfs
 */
enum br_boolopt_id {
	BR_BOOLOPT_NO_LL_LEARN,
	BR_BOOLOPT_MAX
};

/* struct br_boolopt_multi - change multiple bridge boolean options
 *
 * @optval: new option values (bit per option)
 * @optmask: options to change (bit per option)
 */
struct br_boolopt_multi {
	__u32 optval;
	__u32 optmask;
};
#endif /* _UAPI_LINUX_IF_BRIDGE_H */
+1 −0
Original line number Diff line number Diff line
@@ -288,6 +288,7 @@ enum {
	IFLA_BR_MCAST_IGMP_VERSION,
	IFLA_BR_MCAST_MLD_VERSION,
	IFLA_BR_VLAN_STATS_PER_PORT,
	IFLA_BR_MULTI_BOOLOPT,
	__IFLA_BR_MAX,
};

+76 −0
Original line number Diff line number Diff line
@@ -175,6 +175,82 @@ static struct notifier_block br_switchdev_notifier = {
	.notifier_call = br_switchdev_event,
};

/* br_boolopt_toggle - change user-controlled boolean option
 *
 * @br: bridge device
 * @opt: id of the option to change
 * @on: new option value
 * @extack: extack for error messages
 *
 * Changes the value of the respective boolean option to @on taking care of
 * any internal option value mapping and configuration.
 */
int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
		      struct netlink_ext_ack *extack)
{
	switch (opt) {
	case BR_BOOLOPT_NO_LL_LEARN:
		br_opt_toggle(br, BROPT_NO_LL_LEARN, on);
		break;
	default:
		/* shouldn't be called with unsupported options */
		WARN_ON(1);
		break;
	}

	return 0;
}

int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
{
	switch (opt) {
	case BR_BOOLOPT_NO_LL_LEARN:
		return br_opt_get(br, BROPT_NO_LL_LEARN);
	default:
		/* shouldn't be called with unsupported options */
		WARN_ON(1);
		break;
	}

	return 0;
}

int br_boolopt_multi_toggle(struct net_bridge *br,
			    struct br_boolopt_multi *bm,
			    struct netlink_ext_ack *extack)
{
	unsigned long bitmap = bm->optmask;
	int err = 0;
	int opt_id;

	for_each_set_bit(opt_id, &bitmap, BR_BOOLOPT_MAX) {
		bool on = !!(bm->optval & BIT(opt_id));

		err = br_boolopt_toggle(br, opt_id, on, extack);
		if (err) {
			br_debug(br, "boolopt multi-toggle error: option: %d current: %d new: %d error: %d\n",
				 opt_id, br_boolopt_get(br, opt_id), on, err);
			break;
		}
	}

	return err;
}

void br_boolopt_multi_get(const struct net_bridge *br,
			  struct br_boolopt_multi *bm)
{
	u32 optval = 0;
	int opt_id;

	for (opt_id = 0; opt_id < BR_BOOLOPT_MAX; opt_id++)
		optval |= (br_boolopt_get(br, opt_id) << opt_id);

	bm->optval = optval;
	bm->optmask = GENMASK((BR_BOOLOPT_MAX - 1), 0);
}

/* private bridge options, controlled by the kernel */
void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on)
{
	bool cur = !!br_opt_get(br, opt);
+3 −1
Original line number Diff line number Diff line
@@ -188,7 +188,9 @@ static void __br_handle_local_finish(struct sk_buff *skb)
	u16 vid = 0;

	/* check if vlan is allowed, to avoid spoofing */
	if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid))
	if ((p->flags & BR_LEARNING) &&
	    !br_opt_get(p->br, BROPT_NO_LL_LEARN) &&
	    br_should_learn(p, skb, &vid))
		br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false);
}

+16 −1
Original line number Diff line number Diff line
@@ -1035,6 +1035,8 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
	[IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 },
	[IFLA_BR_MCAST_MLD_VERSION] = { .type = NLA_U8 },
	[IFLA_BR_VLAN_STATS_PER_PORT] = { .type = NLA_U8 },
	[IFLA_BR_MULTI_BOOLOPT] = { .type = NLA_EXACT_LEN,
				    .len = sizeof(struct br_boolopt_multi) },
};

static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
@@ -1296,6 +1298,15 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
	}
#endif

	if (data[IFLA_BR_MULTI_BOOLOPT]) {
		struct br_boolopt_multi *bm;

		bm = nla_data(data[IFLA_BR_MULTI_BOOLOPT]);
		err = br_boolopt_multi_toggle(br, bm, extack);
		if (err)
			return err;
	}

	return 0;
}

@@ -1374,6 +1385,7 @@ static size_t br_get_size(const struct net_device *brdev)
	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_IP6TABLES */
	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_ARPTABLES */
#endif
	       nla_total_size(sizeof(struct br_boolopt_multi)) + /* IFLA_BR_MULTI_BOOLOPT */
	       0;
}

@@ -1387,6 +1399,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
	u32 stp_enabled = br->stp_enabled;
	u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1];
	u8 vlan_enabled = br_vlan_enabled(br->dev);
	struct br_boolopt_multi bm;
	u64 clockval;

	clockval = br_timer_value(&br->hello_timer);
@@ -1403,6 +1416,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
	if (nla_put_u64_64bit(skb, IFLA_BR_GC_TIMER, clockval, IFLA_BR_PAD))
		return -EMSGSIZE;

	br_boolopt_multi_get(br, &bm);
	if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) ||
	    nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) ||
	    nla_put_u32(skb, IFLA_BR_MAX_AGE, age_time) ||
@@ -1420,7 +1434,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
	    nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE, br->topology_change) ||
	    nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
		       br->topology_change_detected) ||
	    nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr))
	    nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr) ||
	    nla_put(skb, IFLA_BR_MULTI_BOOLOPT, sizeof(bm), &bm))
		return -EMSGSIZE;

#ifdef CONFIG_BRIDGE_VLAN_FILTERING
Loading