Skip to content

Commit dc81898

Browse files
Support update operation for link objects
When bridge interfaces are in vlan-aware mode, one of the rtnetlink updates they produce was not handled correctly. When the set of vlans active on a vlan-aware bridge itself it being changed it produces RTM_NEWLINK message with AF_BRIDGE family (e.g. with `bridge vlan add dev br0 vid 3 self`). Unlike other messages arriving with AF_BRIDGE family - this one doesn't refer to one of the member interfaces, but to the bridge itself as if it was its own member. This message doesn't contain full information about bridge - mostly about vlans in IFLA_AF_SPEC attribute. It does, however, refer to the bridge interface itself via ifi_index parameter. `libnl` parses such message into a link object with (AF_BRIDGE, <br-ifindex>) key. At the same time, the regular rtnetlink message describing bridge interface itself (one that arrives with AF_UNSPEC family and IFLA_LINKINFO/IFLA_INFO_KIND == bridge) will also be parsed into link object with the same key (AF_BRIDGE, <br-ifindex>). That is caused by the libnl classifying it internally as bridge (via IFLA_INFO_KIND) and overriding its family to AF_BRIDGE. Such collision results in an unexpected behavior when we have a cache with the information about bridge (from AF_UNSPEC message) and then receive notification produced by `bridge vlan add/del ... self`. As both link entries have the same key - new one will override the old - and we will have a misleading entry in cache, which doesn't contain most of the information we've had about the bridge. It also defies some established expectations about bridge interfaces in libnl. E.g. `rtnl_link_is_bridge` is 1 for such entries, but `rtnl_link_get_type` is NULL. This situation can be amended by implementing update mechanism for link objects. With that, it is possible to properly handle such special rtnetlink messages and merge information available in them into full bridge description.
1 parent 49518ca commit dc81898

File tree

3 files changed

+51
-0
lines changed

3 files changed

+51
-0
lines changed

lib/route/link.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,20 @@ static void link_keygen(struct nl_object *obj, uint32_t *hashkey,
11261126
return;
11271127
}
11281128

1129+
static int link_update(struct nl_object *old_obj, struct nl_object *new_obj)
1130+
{
1131+
struct rtnl_link *src = (struct rtnl_link *) new_obj;
1132+
struct rtnl_link *dst = (struct rtnl_link *) old_obj;
1133+
1134+
if (!dst->l_af_ops)
1135+
return -NLE_OPNOTSUPP;
1136+
1137+
if (dst->l_af_ops->ao_update)
1138+
return dst->l_af_ops->ao_update(dst, src);
1139+
1140+
return -NLE_OPNOTSUPP;
1141+
}
1142+
11291143
static uint64_t link_compare(struct nl_object *_a, struct nl_object *_b,
11301144
uint64_t attrs, int flags)
11311145
{
@@ -3215,6 +3229,7 @@ static struct nl_object_ops link_obj_ops = {
32153229
},
32163230
.oo_compare = link_compare,
32173231
.oo_keygen = link_keygen,
3232+
.oo_update = link_update,
32183233
.oo_attrs2str = link_attrs2str,
32193234
.oo_id_attrs = LINK_ATTR_IFINDEX | LINK_ATTR_FAMILY,
32203235
};

lib/route/link/bridge.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,29 @@ static int bridge_compare(struct rtnl_link *_a, struct rtnl_link *_b,
973973
}
974974
/** @endcond */
975975

976+
static int bridge_update(struct rtnl_link *old_obj, struct rtnl_link *new_obj)
977+
{
978+
struct bridge_data *dst = bridge_data(old_obj);
979+
struct bridge_data *src = bridge_data(new_obj);
980+
981+
/*
982+
* Update only when we're dealing with a case of bridge
983+
* notification on changes to itself as a bridge port.
984+
* E.g. after `bridge vlan add dev br0 vid 3 self`
985+
* Such notifications don't have full bridge details
986+
* and thus have to be merged with parent rather than replace it.
987+
* Reliable criterion to detect the case we're interested in
988+
* is to look if there is IFLA_MASTER attr equal to ifi_index
989+
* in the same message.
990+
*/
991+
if (new_obj->l_index != new_obj->l_master)
992+
return -NLE_OPNOTSUPP;
993+
994+
*dst = *src;
995+
996+
return NLE_SUCCESS;
997+
}
998+
976999
/**
9771000
* Allocate link object of type bridge
9781001
*
@@ -1851,6 +1874,7 @@ static struct rtnl_link_af_ops bridge_ops = {
18511874
.ao_parse_protinfo = &bridge_parse_protinfo,
18521875
.ao_dump[NL_DUMP_DETAILS] = &bridge_dump_details,
18531876
.ao_compare = &bridge_compare,
1877+
.ao_update = &bridge_update,
18541878
.ao_parse_af_full = &bridge_parse_af_full,
18551879
.ao_get_af = &bridge_get_af,
18561880
.ao_fill_af = &bridge_fill_af,

lib/route/link/link-api.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,18 @@ struct rtnl_link_af_ops
141141
int (*ao_compare)(struct rtnl_link *,
142142
struct rtnl_link *, int, uint32_t, int);
143143

144+
/**
145+
* Update function
146+
*
147+
* Will be called when af data of the link object given by first argument
148+
* needs to be updated with the af data of the second supplied link object
149+
*
150+
* The function must return 0 for success and error for failure
151+
* to update. In case of failure its assumed that the original
152+
* object is not touched
153+
*/
154+
int (*ao_update)(struct rtnl_link *, struct rtnl_link *);
155+
144156
/* RTM_NEWLINK override
145157
*
146158
* Called if a change link request is set to the kernel. If this returns

0 commit comments

Comments
 (0)