Skip to content

Commit

Permalink
Merge pull request #438 from bisdn/jogo_handle_mdb_updates
Browse files Browse the repository at this point in the history
adapt mdb handling to fixed upstream working
  • Loading branch information
rubensfig authored Jun 3, 2024
2 parents 23a5e05 + ebdcdc7 commit 1b49171
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 91 deletions.
29 changes: 22 additions & 7 deletions src/netlink/cnetlink.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1796,20 +1796,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
157 changes: 75 additions & 82 deletions src/netlink/nl_bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -870,32 +870,37 @@ 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
// rtnl_mdb_get_ifindex() returns an uint32_t, while all other *_get_ifindex()
// functions return an int
if (static_cast<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 +923,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 +957,88 @@ 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);
// go through both ordered sets in parallel, and whenever the iterator points
// to an entry not present in both, we are at one that changed
while (it_old != old_entries.end() && it_new != new_entries.end()) {
// entry only in old set => remove
if (*it_old < *it_new) {
uint32_t port_id = std::get<0>(*it_old);
uint16_t vid = std::get<1>(*it_old);
rofl::caddress_ll mc_ll = std::get<2>(*it_old);

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];
VLOG(2) << __FUNCTION__ << ": port " << port_id << ", vid " << vid
<< " leaving " << mc_ll;

if (port_ifindex == get_ifindex()) {
sw->l2_multicast_group_leave(port_id, vid, mc_ll);
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;
// entry only in new set => add
if (*it_new < *it_old) {
uint32_t port_id = std::get<0>(*it_new);
uint16_t vid = std::get<1>(*it_new);
rofl::caddress_ll mc_ll = std::get<2>(*it_new);

rofl::caddress_in4 ipv4_dst = libnl_in4addr_2_rofl(addr, &rv);
if (rv < 0) {
LOG(ERROR) << __FUNCTION__ << ": could not parse addr " << addr;
return rv;
}
VLOG(2) << __FUNCTION__ << ": port " << port_id << ", vid " << vid
<< " joining " << mc_ll;

multicast_ipv4_to_ll(ipv4_dst.get_addr_hbo(), buf);
} else if (rtnl_mdb_entry_get_proto(i) == ETH_P_IPV6) {
sw->l2_multicast_group_join(port_id, vid, mc_ll);
it_new++;
continue;
}

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;
// entry in both => keep
it_old++;
it_new++;
}

struct in6_addr *v6_addr =
(struct in6_addr *)nl_addr_get_binary_addr(addr);
// any remaining entries in old => remove
while (it_old != old_entries.end()) {
uint32_t port_id = std::get<0>(*it_old);
uint16_t vid = std::get<1>(*it_old);
rofl::caddress_ll mc_ll = std::get<2>(*it_old);

multicast_ipv6_to_ll(v6_addr, buf);
}
VLOG(2) << __FUNCTION__ << ": port " << port_id << ", vid " << vid
<< " leaving " << mc_ll;

rofl::caddress_ll mc_ll = rofl::caddress_ll(buf, ETH_ALEN);
sw->l2_multicast_group_leave(port_id, vid, mc_ll);
it_old++;
}

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;
}
// any remaining entries in new => add
while (it_new != new_entries.end()) {
uint32_t port_id = std::get<0>(*it_new);
uint16_t vid = std::get<1>(*it_new);
rofl::caddress_ll mc_ll = std::get<2>(*it_new);

VLOG(2) << __FUNCTION__ << ": port " << port_id << ", vid " << vid
<< " joining " << mc_ll;

VLOG(2) << __FUNCTION__ << ": mdb entry removed, port" << port_id
<< " grp= " << addr;
sw->l2_multicast_group_join(port_id, vid, mc_ll);
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 1b49171

Please sign in to comment.