Skip to content

Commit a428afe

Browse files
Nikolay Aleksandrovdavem330
authored andcommitted
net: bridge: add support for user-controlled bool options
We have been adding many new bridge options, a big number of which are boolean but still take up netlink attribute ids and waste space in the skb. Recently we discussed learning from link-local packets[1] and decided yet another new boolean option will be needed, thus introducing this API to save some bridge nl space. The API supports changing the value of multiple boolean options at once via the br_boolopt_multi struct which has an optmask (which options to set, bit per opt) and optval (options' new values). Future boolean options will only be added to the br_boolopt_id enum and then will have to be handled in br_boolopt_toggle/get. The API will automatically add the ability to change and export them via netlink, sysfs can use the single boolopt function versions to do the same. The behaviour with failing/succeeding is the same as with normal netlink option changing. If an option requires mapping to internal kernel flag or needs special configuration to be enabled then it should be handled in br_boolopt_toggle. It should also be able to retrieve an option's current state via br_boolopt_get. v2: WARN_ON() on unsupported option as that shouldn't be possible and also will help catch people who add new options without handling them for both set and get. Pass down extack so if an option desires it could set it on error and be more user-friendly. [1] https://www.spinics.net/lists/netdev/msg532698.html Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 02c72d5 commit a428afe

File tree

6 files changed

+115
-2
lines changed

6 files changed

+115
-2
lines changed

include/uapi/linux/if_bridge.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,22 @@ 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+
* IMPORTANT: if adding a new option do not forget to handle
298+
* it in br_boolopt_toggle/get and bridge sysfs
299+
*/
300+
enum br_boolopt_id {
301+
BR_BOOLOPT_MAX
302+
};
303+
304+
/* struct br_boolopt_multi - change multiple bridge boolean options
305+
*
306+
* @optval: new option values (bit per option)
307+
* @optmask: options to change (bit per option)
308+
*/
309+
struct br_boolopt_multi {
310+
__u32 optval;
311+
__u32 optmask;
312+
};
295313
#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: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,77 @@ 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+
default:
193+
/* shouldn't be called with unsupported options */
194+
WARN_ON(1);
195+
break;
196+
}
197+
198+
return 0;
199+
}
200+
201+
int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
202+
{
203+
switch (opt) {
204+
default:
205+
/* shouldn't be called with unsupported options */
206+
WARN_ON(1);
207+
break;
208+
}
209+
210+
return 0;
211+
}
212+
213+
int br_boolopt_multi_toggle(struct net_bridge *br,
214+
struct br_boolopt_multi *bm,
215+
struct netlink_ext_ack *extack)
216+
{
217+
unsigned long bitmap = bm->optmask;
218+
int err = 0;
219+
int opt_id;
220+
221+
for_each_set_bit(opt_id, &bitmap, BR_BOOLOPT_MAX) {
222+
bool on = !!(bm->optval & BIT(opt_id));
223+
224+
err = br_boolopt_toggle(br, opt_id, on, extack);
225+
if (err) {
226+
br_debug(br, "boolopt multi-toggle error: option: %d current: %d new: %d error: %d\n",
227+
opt_id, br_boolopt_get(br, opt_id), on, err);
228+
break;
229+
}
230+
}
231+
232+
return err;
233+
}
234+
235+
void br_boolopt_multi_get(const struct net_bridge *br,
236+
struct br_boolopt_multi *bm)
237+
{
238+
u32 optval = 0;
239+
int opt_id;
240+
241+
for (opt_id = 0; opt_id < BR_BOOLOPT_MAX; opt_id++)
242+
optval |= (br_boolopt_get(br, opt_id) << opt_id);
243+
244+
bm->optval = optval;
245+
bm->optmask = 0;
246+
}
247+
248+
/* private bridge options, controlled by the kernel */
178249
void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on)
179250
{
180251
bool cur = !!br_opt_get(br, opt);

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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,14 @@ static inline int br_opt_get(const struct net_bridge *br,
507507
return test_bit(opt, &br->options);
508508
}
509509

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

512520
/* br_device.c */

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)