platform: add support for configuring bridge settings via netlink

NMDeviceBridge is currently using sysfs. The plan is to use netlink in
in the future
This commit is contained in:
Sayed Shah 2020-08-05 13:46:28 -04:00 committed by Thomas Haller
parent ec582d788c
commit adf0420258
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
12 changed files with 256 additions and 4 deletions

View file

@ -81,19 +81,23 @@
#define NM_BRIDGE_HELLO_TIME_MIN 1u
#define NM_BRIDGE_HELLO_TIME_DEF 2u
#define NM_BRIDGE_HELLO_TIME_MAX 10u
#define NM_BRIDGE_HELLO_TIME_DEF_SYS (NM_BRIDGE_HELLO_TIME_DEF * 100u)
#define NM_BRIDGE_FORWARD_DELAY_MIN 2u
#define NM_BRIDGE_FORWARD_DELAY_DEF 15u
#define NM_BRIDGE_FORWARD_DELAY_MAX 30u
#define NM_BRIDGE_FORWARD_DELAY_DEF_SYS (NM_BRIDGE_FORWARD_DELAY_DEF * 100u)
#define NM_BRIDGE_MAX_AGE_MIN 6u
#define NM_BRIDGE_MAX_AGE_DEF 20u
#define NM_BRIDGE_MAX_AGE_MAX 40u
#define NM_BRIDGE_MAX_AGE_DEF_SYS (NM_BRIDGE_MAX_AGE_DEF * 100u)
/* IEEE 802.1D-1998 Table 7.4 */
#define NM_BRIDGE_AGEING_TIME_MIN 0u
#define NM_BRIDGE_AGEING_TIME_DEF 300u
#define NM_BRIDGE_AGEING_TIME_MAX 1000000u
#define NM_BRIDGE_AGEING_TIME_DEF_SYS (NM_BRIDGE_AGEING_TIME_DEF * 100u)
#define NM_BRIDGE_PORT_PRIORITY_MIN 0u
#define NM_BRIDGE_PORT_PRIORITY_DEF 32u

View file

@ -988,6 +988,7 @@ create_and_realize (NMDevice *device,
const char *hwaddr;
gs_free char *hwaddr_cloned = NULL;
guint8 mac_address[NM_UTILS_HWADDR_LEN_MAX];
NMPlatformLnkBridge props;
int r;
nm_assert (iface);
@ -1016,10 +1017,18 @@ create_and_realize (NMDevice *device,
}
}
props = (NMPlatformLnkBridge) {
.forward_delay = nm_setting_bridge_get_forward_delay (s_bridge) * 100u,
.hello_time = nm_setting_bridge_get_hello_time (s_bridge) * 100u,
.max_age = nm_setting_bridge_get_max_age (s_bridge) * 100u,
.ageing_time = nm_setting_bridge_get_ageing_time (s_bridge) * 100u,
};
r = nm_platform_link_bridge_add (nm_device_get_platform (device),
iface,
hwaddr ? mac_address : NULL,
hwaddr ? ETH_ALEN : 0,
&props,
out_plink);
if (r < 0) {
g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,

View file

@ -221,6 +221,7 @@ typedef enum {
NMP_OBJECT_TYPE_TFILTER,
NMP_OBJECT_TYPE_LNK_BRIDGE,
NMP_OBJECT_TYPE_LNK_GRE,
NMP_OBJECT_TYPE_LNK_GRETAP,
NMP_OBJECT_TYPE_LNK_INFINIBAND,

View file

@ -309,6 +309,14 @@ link_add (NMPlatform *platform,
g_assert ((parent != 0) == NM_IN_SET (type, NM_LINK_TYPE_VLAN));
switch (type) {
case NM_LINK_TYPE_BRIDGE: {
const NMPlatformLnkBridge *props = extra_data;
g_assert (props);
dev_lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_BRIDGE, props);
break;
}
case NM_LINK_TYPE_VETH:
veth_peer = extra_data;
g_assert (veth_peer);

View file

@ -1297,6 +1297,44 @@ _parse_af_inet6 (NMPlatform *platform,
/*****************************************************************************/
static NMPObject *
_parse_lnk_bridge (const char *kind, struct nlattr *info_data)
{
static const struct nla_policy policy[] = {
[IFLA_BR_FORWARD_DELAY] = { .type = NLA_U32 },
[IFLA_BR_HELLO_TIME] = { .type = NLA_U32 },
[IFLA_BR_MAX_AGE] = { .type = NLA_U32 },
[IFLA_BR_AGEING_TIME] = { .type = NLA_U32 },
};
NMPlatformLnkBridge *props;
struct nlattr *tb[G_N_ELEMENTS (policy)];
NMPObject *obj;
if ( !info_data
|| !nm_streq0 (kind, "bridge"))
return NULL;
if (nla_parse_nested_arr (tb, info_data, policy) < 0)
return NULL;
obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_BRIDGE, NULL);
props = &obj->lnk_bridge;
if (tb[IFLA_BR_FORWARD_DELAY])
props->forward_delay = nla_get_u32 (tb[IFLA_BR_FORWARD_DELAY]);
if (tb[IFLA_BR_HELLO_TIME])
props->hello_time = nla_get_u32 (tb[IFLA_BR_HELLO_TIME]);
if (tb[IFLA_BR_MAX_AGE])
props->max_age = nla_get_u32 (tb[IFLA_BR_MAX_AGE]);
if (tb[IFLA_BR_AGEING_TIME])
props->ageing_time = nla_get_u32 (tb[IFLA_BR_AGEING_TIME]);
return obj;
}
/***********************************************************************************/
static NMPObject *
_parse_lnk_gre (const char *kind, struct nlattr *info_data)
{
@ -2933,6 +2971,9 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
}
switch (obj->link.type) {
case NM_LINK_TYPE_BRIDGE:
lnk_data = _parse_lnk_bridge (nl_info_kind, nl_info_data);
break;
case NM_LINK_TYPE_GRE:
case NM_LINK_TYPE_GRETAP:
lnk_data = _parse_lnk_gre (nl_info_kind, nl_info_data);
@ -3954,6 +3995,20 @@ _nl_msg_new_link_set_linkinfo (struct nl_msg *msg,
NLA_PUT_STRING (msg, IFLA_INFO_KIND, kind);
switch (link_type) {
case NM_LINK_TYPE_BRIDGE: {
const NMPlatformLnkBridge *props = extra_data;
nm_assert (extra_data);
if (!(data = nla_nest_start (msg, IFLA_INFO_DATA)))
goto nla_put_failure;
NLA_PUT_U32 (msg, IFLA_BR_FORWARD_DELAY, props->forward_delay);
NLA_PUT_U32 (msg, IFLA_BR_HELLO_TIME, props->hello_time);
NLA_PUT_U32 (msg, IFLA_BR_MAX_AGE, props->max_age);
NLA_PUT_U32 (msg, IFLA_BR_AGEING_TIME, props->ageing_time);
break;
}
case NM_LINK_TYPE_VLAN: {
const NMPlatformLnkVlan *props = extra_data;

View file

@ -1224,6 +1224,10 @@ nm_platform_link_add (NMPlatform *self,
buf[0] = '\0';
switch (type) {
case NM_LINK_TYPE_BRIDGE:
nm_utils_strbuf_append_str (&buf_p, &buf_len, ", ");
nm_platform_lnk_bridge_to_string ((const NMPlatformLnkBridge *) extra_data, buf_p, buf_len);
break;
case NM_LINK_TYPE_VLAN:
nm_utils_strbuf_append_str (&buf_p, &buf_len, ", ");
nm_platform_lnk_vlan_to_string ((const NMPlatformLnkVlan *) extra_data, buf_p, buf_len);
@ -2203,6 +2207,12 @@ _link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlat
return lnk ? &lnk->object : NULL;
}
const NMPlatformLnkBridge *
nm_platform_link_get_lnk_bridge (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
{
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_BRIDGE, out_link);
}
const NMPlatformLnkGre *
nm_platform_link_get_lnk_gre (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
{
@ -5372,6 +5382,32 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len)
return buf;
}
const NMPlatformLnkBridge nm_platform_lnk_bridge_default = {
.forward_delay = NM_BRIDGE_FORWARD_DELAY_DEF_SYS,
.hello_time = NM_BRIDGE_HELLO_TIME_DEF_SYS,
.max_age = NM_BRIDGE_MAX_AGE_DEF_SYS,
.ageing_time = NM_BRIDGE_AGEING_TIME_DEF_SYS,
};
const char *
nm_platform_lnk_bridge_to_string (const NMPlatformLnkBridge *lnk, char *buf, gsize len)
{
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
return buf;
g_snprintf (buf, len,
"forward_delay %u"
" hello_time %u"
" max_age %u"
" ageing_time %u"
"",
lnk->forward_delay,
lnk->hello_time,
lnk->max_age,
lnk->ageing_time);
return buf;
}
const char *
nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len)
{
@ -6861,6 +6897,27 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b)
return 0;
}
void
nm_platform_lnk_bridge_hash_update (const NMPlatformLnkBridge *obj, NMHashState *h)
{
nm_hash_update_vals (h,
obj->forward_delay,
obj->hello_time,
obj->max_age,
obj->ageing_time);
}
int
nm_platform_lnk_bridge_cmp (const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b)
{
NM_CMP_SELF (a, b);
NM_CMP_FIELD (a, b, forward_delay);
NM_CMP_FIELD (a, b, hello_time);
NM_CMP_FIELD (a, b, max_age);
NM_CMP_FIELD (a, b, ageing_time);
return 0;
}
void
nm_platform_lnk_gre_hash_update (const NMPlatformLnkGre *obj, NMHashState *h)
{

View file

@ -762,6 +762,15 @@ typedef struct {
bool pvid:1;
} NMPlatformBridgeVlan;
typedef struct {
guint32 forward_delay;
guint32 hello_time;
guint32 max_age;
guint32 ageing_time;
} NMPlatformLnkBridge;
extern const NMPlatformLnkBridge nm_platform_lnk_bridge_default;
typedef struct {
in_addr_t local;
in_addr_t remote;
@ -1453,9 +1462,10 @@ nm_platform_link_bridge_add (NMPlatform *self,
const char *name,
const void *address,
size_t address_len,
const NMPlatformLnkBridge *props,
const NMPlatformLink **out_link)
{
return nm_platform_link_add (self, NM_LINK_TYPE_BRIDGE, name, 0, address, address_len, NULL, out_link);
return nm_platform_link_add (self, NM_LINK_TYPE_BRIDGE, name, 0, address, address_len, props, out_link);
}
static inline int
@ -1717,6 +1727,7 @@ gboolean nm_platform_sysctl_slave_set_option (NMPlatform *self, int ifindex, con
char *nm_platform_sysctl_slave_get_option (NMPlatform *self, int ifindex, const char *option);
const NMPObject *nm_platform_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link);
const NMPlatformLnkBridge *nm_platform_link_get_lnk_bridge (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkGre *nm_platform_link_get_lnk_gre (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkGre *nm_platform_link_get_lnk_gretap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkIp6Tnl *nm_platform_link_get_lnk_ip6tnl (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
@ -1941,6 +1952,7 @@ gboolean nm_platform_tfilter_sync (NMPlatform *self,
GPtrArray *known_tfilters);
const char *nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len);
const char *nm_platform_lnk_bridge_to_string (const NMPlatformLnkBridge *lnk, char *buf, gsize len);
const char *nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len);
const char *nm_platform_lnk_infiniband_to_string (const NMPlatformLnkInfiniband *lnk, char *buf, gsize len);
const char *nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len);
@ -1974,6 +1986,7 @@ const char *nm_platform_wireguard_peer_to_string (const struct _NMPWireGuardPeer
gsize len);
int nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b);
int nm_platform_lnk_bridge_cmp (const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b);
int nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b);
int nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b);
int nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b);
@ -2031,6 +2044,7 @@ void nm_platform_ip6_address_hash_update (const NMPlatformIP6Address *obj, NMHas
void nm_platform_ip4_route_hash_update (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h);
void nm_platform_ip6_route_hash_update (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h);
void nm_platform_routing_rule_hash_update (const NMPlatformRoutingRule *obj, NMPlatformRoutingRuleCmpType cmp_type, NMHashState *h);
void nm_platform_lnk_bridge_hash_update (const NMPlatformLnkBridge *obj, NMHashState *h);
void nm_platform_lnk_gre_hash_update (const NMPlatformLnkGre *obj, NMHashState *h);
void nm_platform_lnk_infiniband_hash_update (const NMPlatformLnkInfiniband *obj, NMHashState *h);
void nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState *h);

View file

@ -3210,6 +3210,17 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_tfilter_hash_update,
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_tfilter_cmp,
},
[NMP_OBJECT_TYPE_LNK_BRIDGE - 1] = {
.parent = DEDUP_MULTI_OBJ_CLASS_INIT (),
.obj_type = NMP_OBJECT_TYPE_LNK_BRIDGE,
.sizeof_data = sizeof (NMPObjectLnkBridge),
.sizeof_public = sizeof (NMPlatformLnkBridge),
.obj_type_name = "bridge",
.lnk_link_type = NM_LINK_TYPE_BRIDGE,
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_bridge_to_string,
.cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_bridge_hash_update,
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_bridge_cmp,
},
[NMP_OBJECT_TYPE_LNK_GRE - 1] = {
.parent = DEDUP_MULTI_OBJ_CLASS_INIT (),
.obj_type = NMP_OBJECT_TYPE_LNK_GRE,

View file

@ -243,6 +243,10 @@ typedef struct {
int wireguard_family_id;
} NMPObjectLink;
typedef struct {
NMPlatformLnkBridge _public;
} NMPObjectLnkBridge;
typedef struct {
NMPlatformLnkGre _public;
} NMPObjectLnkGre;
@ -343,6 +347,9 @@ struct _NMPObject {
NMPlatformLink link;
NMPObjectLink _link;
NMPlatformLnkBridge lnk_bridge;
NMPObjectLnkBridge _lnk_bridge;
NMPlatformLnkGre lnk_gre;
NMPObjectLnkGre _lnk_gre;
@ -487,6 +494,7 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type)
case NMP_OBJECT_TYPE_TFILTER:
case NMP_OBJECT_TYPE_LNK_BRIDGE:
case NMP_OBJECT_TYPE_LNK_GRE:
case NMP_OBJECT_TYPE_LNK_GRETAP:
case NMP_OBJECT_TYPE_LNK_INFINIBAND:

View file

@ -1172,6 +1172,42 @@ nmtstp_ip6_address_del (NMPlatform *platform,
} \
} G_STMT_END
const NMPlatformLink *
nmtstp_link_bridge_add (NMPlatform *platform,
gboolean external_command,
const char *name,
const NMPlatformLnkBridge *lnk,
gboolean *out_not_supported)
{
const NMPlatformLink *pllink = NULL;
int r = 0;
g_assert (nm_utils_ifname_valid_kernel (name, NULL));
NM_SET_OUT (out_not_supported, FALSE);
external_command = nmtstp_run_command_check_external (external_command);
_init_platform (&platform, external_command);
if (external_command) {
r = nmtstp_run_command ("ip link add %s type bridge forward_delay %u hello_time %u max_age %u ageing_time %u",
name,
lnk->forward_delay,
lnk->hello_time,
lnk->max_age,
lnk->ageing_time);
g_assert_cmpint (r, ==, 0);
pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_BRIDGE, 100);
}
if (!pllink) {
r = nm_platform_link_bridge_add (platform, name, NULL, 0, lnk, &pllink);
}
_assert_pllink (platform, r == 0, pllink, name, NM_LINK_TYPE_BRIDGE);
return pllink;
}
const NMPlatformLink *
nmtstp_link_veth_add (NMPlatform *platform,
gboolean external_command,

View file

@ -323,6 +323,11 @@ void nmtstp_link_set_updown (NMPlatform *platform,
int ifindex,
gboolean up);
const NMPlatformLink *nmtstp_link_bridge_add (NMPlatform *platform,
gboolean external_command,
const char *name,
const NMPlatformLnkBridge *lnk,
gboolean *out_not_supported);
const NMPlatformLink *nmtstp_link_veth_add (NMPlatform *platform,
gboolean external_command,
const char *name,

View file

@ -95,7 +95,12 @@ software_add (NMLinkType link_type, const char *name)
case NM_LINK_TYPE_DUMMY:
return NMTST_NM_ERR_SUCCESS (nm_platform_link_dummy_add (NM_PLATFORM_GET, name, NULL));
case NM_LINK_TYPE_BRIDGE:
return NMTST_NM_ERR_SUCCESS (nm_platform_link_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL));
return NMTST_NM_ERR_SUCCESS (nm_platform_link_bridge_add (NM_PLATFORM_GET,
name,
NULL,
0,
&nm_platform_lnk_bridge_default,
NULL));
case NM_LINK_TYPE_BOND:
{
gboolean bond0_exists = !!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "bond0");
@ -116,7 +121,12 @@ software_add (NMLinkType link_type, const char *name)
/* Don't call link_callback for the bridge interface */
parent_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, PARENT_NAME);
if (NMTST_NM_ERR_SUCCESS (nm_platform_link_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL)))
if (NMTST_NM_ERR_SUCCESS (nm_platform_link_bridge_add (NM_PLATFORM_GET,
PARENT_NAME,
NULL,
0,
&nm_platform_lnk_bridge_default,
NULL)))
accept_signal (parent_added);
free_signal (parent_added);
@ -510,7 +520,12 @@ test_bridge_addr (void)
nm_utils_hwaddr_aton ("de:ad:be:ef:00:11", addr, sizeof (addr));
g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_link_bridge_add (NM_PLATFORM_GET, DEVICE_NAME, addr, sizeof (addr), &plink)));
g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_link_bridge_add (NM_PLATFORM_GET,
DEVICE_NAME,
addr,
sizeof (addr),
&nm_platform_lnk_bridge_default,
&plink)));
g_assert (plink);
link = *plink;
g_assert_cmpstr (link.name, ==, DEVICE_NAME);
@ -946,6 +961,24 @@ test_software_detect (gconstpointer user_data)
ifindex_parent = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex;
switch (test_data->link_type) {
case NM_LINK_TYPE_BRIDGE: {
NMPlatformLnkBridge lnk_bridge = { };
gboolean not_supported;
lnk_bridge.forward_delay = 1560;
lnk_bridge.hello_time = 150;
lnk_bridge.max_age = 2100;
lnk_bridge.ageing_time = 2200;
if (!nmtstp_link_bridge_add (NULL, ext, DEVICE_NAME, &lnk_bridge, &not_supported)) {
if (not_supported) {
g_test_skip ("Cannot create Bridge interface because of missing kernel support");
goto out_delete_parent;
}
g_error ("Failed adding Bridge interface");
}
break;
}
case NM_LINK_TYPE_GRE: {
gboolean gracefully_skip = FALSE;
@ -1311,6 +1344,16 @@ test_software_detect (gconstpointer user_data)
g_assert (lnk);
switch (test_data->link_type) {
case NM_LINK_TYPE_BRIDGE: {
const NMPlatformLnkBridge *plnk = &lnk->lnk_bridge;
g_assert (plnk == nm_platform_link_get_lnk_bridge (NM_PLATFORM_GET, ifindex, NULL));
g_assert_cmpint (plnk->forward_delay, ==, 1560);
g_assert_cmpint (plnk->hello_time , ==, 150);
g_assert_cmpint (plnk->max_age , ==, 2100);
g_assert_cmpint (plnk->ageing_time , ==, 2200);
break;
}
case NM_LINK_TYPE_GRE: {
const NMPlatformLnkGre *plnk = &lnk->lnk_gre;
@ -3352,6 +3395,7 @@ _nmtstp_setup_tests (void)
if (nmtstp_is_root_test ()) {
g_test_add_func ("/link/external", test_external);
test_software_detect_add ("/link/software/detect/bridge", NM_LINK_TYPE_BRIDGE, 0);
test_software_detect_add ("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0);
test_software_detect_add ("/link/software/detect/gretap", NM_LINK_TYPE_GRETAP, 0);
test_software_detect_add ("/link/software/detect/ip6tnl/0", NM_LINK_TYPE_IP6TNL, 0);