Skip to content

Commit ff22378

Browse files
committed
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: David S. Miller <[email protected]>
2 parents 02c72d5 + 1ed1ccb commit ff22378

File tree

8 files changed

+149
-3
lines changed

8 files changed

+149
-3
lines changed

include/uapi/linux/if_bridge.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,25 @@ struct br_mcast_stats {
292292
__u64 mcast_bytes[BR_MCAST_DIR_SIZE];
293293
__u64 mcast_packets[BR_MCAST_DIR_SIZE];
294294
};
295+
296+
/* bridge boolean options
297+
* BR_BOOLOPT_NO_LL_LEARN - disable learning from link-local packets
298+
*
299+
* IMPORTANT: if adding a new option do not forget to handle
300+
* it in br_boolopt_toggle/get and bridge sysfs
301+
*/
302+
enum br_boolopt_id {
303+
BR_BOOLOPT_NO_LL_LEARN,
304+
BR_BOOLOPT_MAX
305+
};
306+
307+
/* struct br_boolopt_multi - change multiple bridge boolean options
308+
*
309+
* @optval: new option values (bit per option)
310+
* @optmask: options to change (bit per option)
311+
*/
312+
struct br_boolopt_multi {
313+
__u32 optval;
314+
__u32 optmask;
315+
};
295316
#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
@@ -288,6 +288,7 @@ enum {
288288
IFLA_BR_MCAST_IGMP_VERSION,
289289
IFLA_BR_MCAST_MLD_VERSION,
290290
IFLA_BR_VLAN_STATS_PER_PORT,
291+
IFLA_BR_MULTI_BOOLOPT,
291292
__IFLA_BR_MAX,
292293
};
293294

net/bridge/br.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,82 @@ static struct notifier_block br_switchdev_notifier = {
175175
.notifier_call = br_switchdev_event,
176176
};
177177

178+
/* br_boolopt_toggle - change user-controlled boolean option
179+
*
180+
* @br: bridge device
181+
* @opt: id of the option to change
182+
* @on: new option value
183+
* @extack: extack for error messages
184+
*
185+
* Changes the value of the respective boolean option to @on taking care of
186+
* any internal option value mapping and configuration.
187+
*/
188+
int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
189+
struct netlink_ext_ack *extack)
190+
{
191+
switch (opt) {
192+
case BR_BOOLOPT_NO_LL_LEARN:
193+
br_opt_toggle(br, BROPT_NO_LL_LEARN, on);
194+
break;
195+
default:
196+
/* shouldn't be called with unsupported options */
197+
WARN_ON(1);
198+
break;
199+
}
200+
201+
return 0;
202+
}
203+
204+
int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
205+
{
206+
switch (opt) {
207+
case BR_BOOLOPT_NO_LL_LEARN:
208+
return br_opt_get(br, BROPT_NO_LL_LEARN);
209+
default:
210+
/* shouldn't be called with unsupported options */
211+
WARN_ON(1);
212+
break;
213+
}
214+
215+
return 0;
216+
}
217+
218+
int br_boolopt_multi_toggle(struct net_bridge *br,
219+
struct br_boolopt_multi *bm,
220+
struct netlink_ext_ack *extack)
221+
{
222+
unsigned long bitmap = bm->optmask;
223+
int err = 0;
224+
int opt_id;
225+
226+
for_each_set_bit(opt_id, &bitmap, BR_BOOLOPT_MAX) {
227+
bool on = !!(bm->optval & BIT(opt_id));
228+
229+
err = br_boolopt_toggle(br, opt_id, on, extack);
230+
if (err) {
231+
br_debug(br, "boolopt multi-toggle error: option: %d current: %d new: %d error: %d\n",
232+
opt_id, br_boolopt_get(br, opt_id), on, err);
233+
break;
234+
}
235+
}
236+
237+
return err;
238+
}
239+
240+
void br_boolopt_multi_get(const struct net_bridge *br,
241+
struct br_boolopt_multi *bm)
242+
{
243+
u32 optval = 0;
244+
int opt_id;
245+
246+
for (opt_id = 0; opt_id < BR_BOOLOPT_MAX; opt_id++)
247+
optval |= (br_boolopt_get(br, opt_id) << opt_id);
248+
249+
bm->optval = optval;
250+
bm->optmask = GENMASK((BR_BOOLOPT_MAX - 1), 0);
251+
}
252+
253+
/* private bridge options, controlled by the kernel */
178254
void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on)
179255
{
180256
bool cur = !!br_opt_get(br, opt);

net/bridge/br_input.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ static void __br_handle_local_finish(struct sk_buff *skb)
188188
u16 vid = 0;
189189

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

net/bridge/br_netlink.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,8 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
10351035
[IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 },
10361036
[IFLA_BR_MCAST_MLD_VERSION] = { .type = NLA_U8 },
10371037
[IFLA_BR_VLAN_STATS_PER_PORT] = { .type = NLA_U8 },
1038+
[IFLA_BR_MULTI_BOOLOPT] = { .type = NLA_EXACT_LEN,
1039+
.len = sizeof(struct br_boolopt_multi) },
10381040
};
10391041

10401042
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[],
12961298
}
12971299
#endif
12981300

1301+
if (data[IFLA_BR_MULTI_BOOLOPT]) {
1302+
struct br_boolopt_multi *bm;
1303+
1304+
bm = nla_data(data[IFLA_BR_MULTI_BOOLOPT]);
1305+
err = br_boolopt_multi_toggle(br, bm, extack);
1306+
if (err)
1307+
return err;
1308+
}
1309+
12991310
return 0;
13001311
}
13011312

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

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

13921405
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)
14031416
if (nla_put_u64_64bit(skb, IFLA_BR_GC_TIMER, clockval, IFLA_BR_PAD))
14041417
return -EMSGSIZE;
14051418

1419+
br_boolopt_multi_get(br, &bm);
14061420
if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) ||
14071421
nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) ||
14081422
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)
14201434
nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE, br->topology_change) ||
14211435
nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
14221436
br->topology_change_detected) ||
1423-
nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr))
1437+
nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr) ||
1438+
nla_put(skb, IFLA_BR_MULTI_BOOLOPT, sizeof(bm), &bm))
14241439
return -EMSGSIZE;
14251440

14261441
#ifdef CONFIG_BRIDGE_VLAN_FILTERING

net/bridge/br_private.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ enum net_bridge_opts {
328328
BROPT_NEIGH_SUPPRESS_ENABLED,
329329
BROPT_MTU_SET_BY_USER,
330330
BROPT_VLAN_STATS_PER_PORT,
331+
BROPT_NO_LL_LEARN,
331332
};
332333

333334
struct net_bridge {
@@ -507,6 +508,14 @@ static inline int br_opt_get(const struct net_bridge *br,
507508
return test_bit(opt, &br->options);
508509
}
509510

511+
int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
512+
struct netlink_ext_ack *extack);
513+
int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt);
514+
int br_boolopt_multi_toggle(struct net_bridge *br,
515+
struct br_boolopt_multi *bm,
516+
struct netlink_ext_ack *extack);
517+
void br_boolopt_multi_get(const struct net_bridge *br,
518+
struct br_boolopt_multi *bm);
510519
void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on);
511520

512521
/* br_device.c */

net/bridge/br_sysfs_br.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,27 @@ static ssize_t flush_store(struct device *d,
328328
}
329329
static DEVICE_ATTR_WO(flush);
330330

331+
static ssize_t no_linklocal_learn_show(struct device *d,
332+
struct device_attribute *attr,
333+
char *buf)
334+
{
335+
struct net_bridge *br = to_bridge(d);
336+
return sprintf(buf, "%d\n", br_boolopt_get(br, BR_BOOLOPT_NO_LL_LEARN));
337+
}
338+
339+
static int set_no_linklocal_learn(struct net_bridge *br, unsigned long val)
340+
{
341+
return br_boolopt_toggle(br, BR_BOOLOPT_NO_LL_LEARN, !!val, NULL);
342+
}
343+
344+
static ssize_t no_linklocal_learn_store(struct device *d,
345+
struct device_attribute *attr,
346+
const char *buf, size_t len)
347+
{
348+
return store_bridge_parm(d, buf, len, set_no_linklocal_learn);
349+
}
350+
static DEVICE_ATTR_RW(no_linklocal_learn);
351+
331352
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
332353
static ssize_t multicast_router_show(struct device *d,
333354
struct device_attribute *attr, char *buf)
@@ -841,6 +862,7 @@ static struct attribute *bridge_attrs[] = {
841862
&dev_attr_gc_timer.attr,
842863
&dev_attr_group_addr.attr,
843864
&dev_attr_flush.attr,
865+
&dev_attr_no_linklocal_learn.attr,
844866
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
845867
&dev_attr_multicast_router.attr,
846868
&dev_attr_multicast_snooping.attr,

net/core/rtnetlink.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
#include <net/rtnetlink.h>
6060
#include <net/net_namespace.h>
6161

62-
#define RTNL_MAX_TYPE 49
62+
#define RTNL_MAX_TYPE 50
6363
#define RTNL_SLAVE_MAX_TYPE 36
6464

6565
struct rtnl_link {

0 commit comments

Comments
 (0)