Skip to content

Commit

Permalink
cnetlink: adapt mdb handling to fixed upstream working
Browse files Browse the repository at this point in the history
With a recent fix, libnl will now send the updated object instead of the
update as new for callback_v2(). Unfortuntely the mdb code relied on
getting incremental updates as new, so this change broke mdb handling.

To fix this, rework the mdb handling the following:

1. convert all entries of old and new into seperate, ordered sets
2. for any entry in the old, but not new leave the group
3. for any entry in the new, but not old join the group

This with empty sets for old respective new this can also be used new
and delete of the object.

Signed-off-by: Jonas Gorski <[email protected]>
  • Loading branch information
KanjiMonster committed May 29, 2024
1 parent b85be60 commit 76b9ba0
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 97 deletions.
29 changes: 22 additions & 7 deletions src/netlink/cnetlink.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1880,20 +1880,35 @@ std::deque<rtnl_neigh *> cnetlink::search_fdb(uint16_t vid, nl_addr *lladdr) {

void cnetlink::route_mdb_apply(const nl_obj &obj) {

switch (obj.get_msg_type()) {
case RTM_NEWMDB:
switch (obj.get_action()) {
case NL_ACT_NEW:
assert(obj.get_new_obj());

VLOG(2) << __FUNCTION__ << ": add mdb " << obj.get_old_obj();

if (bridge)
bridge->mdb_update(nullptr, MDB_CAST(obj.get_new_obj()));
break;
case NL_ACT_CHANGE:
assert(obj.get_new_obj());
LOG(INFO) << __FUNCTION__ << ": new mdb entry";
assert(obj.get_old_obj());

VLOG(2) << __FUNCTION__ << ": change new mdb " << obj.get_new_obj();
VLOG(2) << __FUNCTION__ << ": change old mdb " << obj.get_old_obj();

if (bridge)
bridge->mdb_entry_add(MDB_CAST(obj.get_new_obj()));
bridge->mdb_update(MDB_CAST(obj.get_old_obj()),
MDB_CAST(obj.get_new_obj()));

break;
case RTM_DELMDB:
case NL_ACT_DEL:
assert(obj.get_old_obj());
LOG(INFO) << __FUNCTION__ << ": deleting mdb entry";

VLOG(2) << __FUNCTION__ << ": del mdb " << obj.get_old_obj();

if (bridge)
bridge->mdb_entry_remove(MDB_CAST(obj.get_new_obj()));
bridge->mdb_update(MDB_CAST(obj.get_old_obj()), nullptr);

break;
default:
LOG(ERROR) << __FUNCTION__ << ": invalid action " << obj.get_action();
Expand Down
139 changes: 51 additions & 88 deletions src/netlink/nl_bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -870,32 +870,35 @@ void nl_bridge::clear_tpid_entries() {
}
}

int nl_bridge::mdb_entry_add(rtnl_mdb *mdb_entry) {
int rv = 0;
#ifdef HAVE_NETLINK_ROUTE_MDB_H
std::deque<struct rtnl_mdb_entry *> mdb;
uint32_t bridge = rtnl_mdb_get_ifindex(mdb_entry);
int nl_bridge::mdb_to_set(
rtnl_mdb *mdb,
std::set<std::tuple<uint32_t, uint16_t, rofl::caddress_ll>> *set) {
assert(mdb);
assert(set);

if (!is_bridge_interface(nl->get_link_by_ifindex(bridge).get())) {
#ifdef HAVE_NETLINK_ROUTE_MDB_H
if ((int)rtnl_mdb_get_ifindex(mdb) != get_ifindex()) {
LOG(ERROR) << ": bridge not set correctly";
return -EINVAL;
}

// parse MDB object to get all nested information
std::deque<struct rtnl_mdb_entry *> mdb_entries;

rtnl_mdb_foreach_entry(
mdb_entry,
mdb,
[](struct rtnl_mdb_entry *it, void *arg) {
auto m_database =
static_cast<std::deque<struct rtnl_mdb_entry *> *>(arg);
m_database->push_back(it);
},
&mdb);
&mdb_entries);

for (auto i : mdb) {
for (auto i : mdb_entries) {
int port_ifindex = rtnl_mdb_entry_get_ifindex(i);
uint32_t port_id = nl->get_port_id(port_ifindex);
uint16_t vid = rtnl_mdb_entry_get_vid(i);
unsigned char buf[ETH_ALEN];
int rv;

if (port_ifindex == get_ifindex()) {
continue;
Expand All @@ -918,7 +921,7 @@ int nl_bridge::mdb_entry_add(rtnl_mdb *mdb_entry) {
rofl::caddress_in4 ipv4_dst = libnl_in4addr_2_rofl(addr, &rv);
if (rv < 0) {
LOG(ERROR) << __FUNCTION__ << ": could not parse addr " << addr;
return rv;
continue;
}

unsigned char buf[ETH_ALEN];
Expand Down Expand Up @@ -952,100 +955,60 @@ int nl_bridge::mdb_entry_add(rtnl_mdb *mdb_entry) {
multicast_ipv6_to_ll(v6_addr, buf);
}

rofl::caddress_ll mc_ll = rofl::caddress_ll(buf, ETH_ALEN);

rv = sw->l2_multicast_group_join(port_id, vid, mc_ll);
if (rv < 0) {
LOG(ERROR) << __FUNCTION__ << ": failed to join Layer 2 Multicast Group";
return rv;
}

VLOG(2) << __FUNCTION__ << ": mdb entry added, port" << port_id
<< " grp= " << addr;
set->emplace(port_id, vid, rofl::caddress_ll(buf, ETH_ALEN));
}
#endif

return rv;
return 0;
}

int nl_bridge::mdb_entry_remove(rtnl_mdb *mdb_entry) {
int rv = 0;
#ifdef HAVE_NETLINK_ROUTE_MDB_H
uint32_t bridge = rtnl_mdb_get_ifindex(mdb_entry);
int nl_bridge::mdb_update(rtnl_mdb *old_mdb, rtnl_mdb *new_mdb) {
std::set<std::tuple<uint32_t, uint16_t, rofl::caddress_ll>> old_entries;
std::set<std::tuple<uint32_t, uint16_t, rofl::caddress_ll>> new_entries;

if (!is_bridge_interface(nl->get_link_by_ifindex(bridge).get())) {
LOG(ERROR) << ": bridge not set correctly";
return -EINVAL;
}
if (old_mdb)
mdb_to_set(old_mdb, &old_entries);
if (new_mdb)
mdb_to_set(new_mdb, &new_entries);

std::deque<struct rtnl_mdb_entry *> mdb;
auto it_old = old_entries.begin();
auto it_new = new_entries.begin();

// parse MDB object to get all nested information
rtnl_mdb_foreach_entry(
mdb_entry,
[](struct rtnl_mdb_entry *it, void *arg) {
auto m_database =
static_cast<std::deque<struct rtnl_mdb_entry *> *>(arg);
m_database->push_back(it);
},
&mdb);

for (auto i : mdb) {
int port_ifindex = rtnl_mdb_entry_get_ifindex(i);
uint32_t port_id = nl->get_port_id(port_ifindex);
uint16_t vid = rtnl_mdb_entry_get_vid(i);
unsigned char buf[ETH_ALEN];

if (port_ifindex == get_ifindex()) {
while (it_old != old_entries.end() && it_new != new_entries.end()) {
// entry only in old set => remove
if (*it_old < *it_new) {
sw->l2_multicast_group_leave(std::get<0>(*it_old), std::get<1>(*it_old),
std::get<2>(*it_old));
it_old++;
continue;
}

auto addr = rtnl_mdb_entry_get_addr(i);
if (rtnl_mdb_entry_get_proto(i) == ETH_P_IP) {
auto p = nl_addr_alloc(4);
nl_addr_parse("224.0.0.0/24", AF_INET, &p);
std::unique_ptr<nl_addr, decltype(&nl_addr_put)> tm_addr(p, nl_addr_put);
if (!nl_addr_cmp_prefix(addr, tm_addr.get()))
continue;

rofl::caddress_in4 ipv4_dst = libnl_in4addr_2_rofl(addr, &rv);
if (rv < 0) {
LOG(ERROR) << __FUNCTION__ << ": could not parse addr " << addr;
return rv;
}

multicast_ipv4_to_ll(ipv4_dst.get_addr_hbo(), buf);
} else if (rtnl_mdb_entry_get_proto(i) == ETH_P_IPV6) {

auto p = nl_addr_alloc(16);
nl_addr_parse("ff02::1/128", AF_INET6, &p);
std::unique_ptr<nl_addr, decltype(&nl_addr_put)> tm_addr(p, nl_addr_put);
if (!nl_addr_cmp(addr, tm_addr.get()))
continue;
nl_addr_parse("ff00::/15", AF_INET6, &p);
if (!nl_addr_cmp_prefix(addr, tm_addr.get()))
continue;

struct in6_addr *v6_addr =
(struct in6_addr *)nl_addr_get_binary_addr(addr);

multicast_ipv6_to_ll(v6_addr, buf);
// entry only in new set => add
if (*it_new < *it_old) {
sw->l2_multicast_group_join(std::get<0>(*it_new), std::get<1>(*it_new),
std::get<2>(*it_new));
it_new++;
continue;
}

rofl::caddress_ll mc_ll = rofl::caddress_ll(buf, ETH_ALEN);
// entry in both => keep
it_old++;
it_new++;
}

rv = sw->l2_multicast_group_leave(port_id, vid, mc_ll);
if (rv < 0) {
LOG(ERROR) << __FUNCTION__ << ": failed to leave Layer 2 Multicast Group";
return rv;
}
while (it_old != old_entries.end()) {
sw->l2_multicast_group_leave(std::get<0>(*it_old), std::get<1>(*it_old),
std::get<2>(*it_old));
it_old++;
}

VLOG(2) << __FUNCTION__ << ": mdb entry removed, port" << port_id
<< " grp= " << addr;
while (it_new != new_entries.end()) {
sw->l2_multicast_group_join(std::get<0>(*it_new), std::get<1>(*it_new),
std::get<2>(*it_new));
it_new++;
}
#endif

return rv;
return 0;
}

int nl_bridge::set_port_vlan_stp_state(uint32_t port_id, uint16_t vid,
Expand Down
6 changes: 4 additions & 2 deletions src/netlink/nl_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,7 @@ class nl_bridge {
void add_neigh_to_fdb(rtnl_neigh *, bool update = false);
void remove_neigh_from_fdb(rtnl_neigh *);

int mdb_entry_add(rtnl_mdb *mdb_entry);
int mdb_entry_remove(rtnl_mdb *mdb_entry);
int mdb_update(rtnl_mdb *old_mdb, rtnl_mdb *new_mdb);

bool is_mac_in_l2_cache(rtnl_neigh *n);
int fdb_timeout(rtnl_link *br_link, uint16_t vid,
Expand Down Expand Up @@ -213,6 +212,9 @@ class nl_bridge {
int del_port_vlan_stp_state(uint32_t port_id, uint16_t vid);
int set_port_vlan_stp_state(uint32_t port_id, uint16_t vid, uint8_t state);

int mdb_to_set(rtnl_mdb *,
std::set<std::tuple<uint32_t, uint16_t, rofl::caddress_ll>> *);

rtnl_link *bridge;
switch_interface *sw;
std::shared_ptr<port_manager> port_man;
Expand Down

0 comments on commit 76b9ba0

Please sign in to comment.