diff --git a/meson.build b/meson.build index ef44c899..e4769639 100644 --- a/meson.build +++ b/meson.build @@ -29,6 +29,7 @@ sources = files(''' src/netlink/nl_hashing.h src/netlink/nl_interface.cc src/netlink/nl_interface.h + src/netlink/nl_fdb_flush.h src/netlink/nl_l3.cc src/netlink/nl_l3.h src/netlink/nl_l3_interfaces.h diff --git a/src/netlink/nl_bridge.cc b/src/netlink/nl_bridge.cc index 0b0f5ee3..48510334 100644 --- a/src/netlink/nl_bridge.cc +++ b/src/netlink/nl_bridge.cc @@ -25,6 +25,7 @@ #include "cnetlink.h" #include "netlink-utils.h" #include "nl_bridge.h" +#include "nl_fdb_flush.h" #include "nl_output.h" #include "nl_vlan.h" #include "nl_vxlan.h" @@ -485,6 +486,15 @@ void nl_bridge::update_vlans(rtnl_link *old_link, rtnl_link *new_link) { // the PVID is already being handled outside of the loop vlan->remove_bridge_vlan(_link, vid, false, !egress_untagged); + + // remove all fdb entries by us + nl_fdb_flush ff; + + auto ret = ff.flush_fdb(rtnl_link_get_ifindex(_link), vid, + NTF_MASTER | NTF_EXT_LEARNED); + if (ret < 0) + LOG(WARNING) << __FUNCTION__ << ": failed to flush vid=" << vid + << " on port " << _link; } } } @@ -781,13 +791,20 @@ void nl_bridge::remove_neigh_from_fdb(rtnl_neigh *neigh) { return; } - // lookup l2_cache as well - std::unique_ptr n_lookup( - NEIGH_CAST(nl_cache_search(l2_cache.get(), OBJ_CAST(neigh))), - rtnl_neigh_put); + if ((rtnl_neigh_get_flags(neigh) & NTF_EXT_LEARNED) == NTF_EXT_LEARNED) { + // lookup l2_cache as well + std::unique_ptr n_lookup( + NEIGH_CAST(nl_cache_search(l2_cache.get(), OBJ_CAST(neigh))), + rtnl_neigh_put); - if (n_lookup) { - nl_cache_remove(OBJ_CAST(n_lookup.get())); + if (n_lookup) { + nl_cache_remove(OBJ_CAST(n_lookup.get())); + } else { + // if we flushed the entry, we already removed it from cache and flows, so + // no need to do anything here + VLOG(2) << __FUNCTION__ << ": neigh not found in cache" << neigh; + return; + } } const uint32_t port = nl->get_port_id(rtnl_neigh_get_ifindex(neigh)); diff --git a/src/netlink/nl_fdb_flush.h b/src/netlink/nl_fdb_flush.h new file mode 100644 index 00000000..8a7f10a6 --- /dev/null +++ b/src/netlink/nl_fdb_flush.h @@ -0,0 +1,79 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "nl_output.h" + +namespace basebox { + +class nl_fdb_flush final { + struct nl_sock *sock; + +public: + nl_fdb_flush() { + sock = nl_socket_alloc(); + int err; + if ((err = nl_connect(sock, NETLINK_ROUTE)) < 0) + LOG(FATAL) << __FUNCTION__ << ": Unable to connect netlink socket: %s" + << nl_geterror(err); + } + + ~nl_fdb_flush() { nl_socket_free(sock); } + + /** + * flush fdb + */ + int flush_fdb(int ifindex, uint16_t vlan, uint8_t flags) { + struct nl_msg *m; + struct ndmsg ndm; + int err; + + memset(&ndm, 0, sizeof(ndm)); + ndm.ndm_family = PF_BRIDGE; + ndm.ndm_ifindex = ifindex; + ndm.ndm_flags = flags; + + m = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_REQUEST | NLM_F_BULK); + if (!m) + LOG(FATAL) << __FUNCTION__ << ": out of memory"; + + if (nlmsg_append(m, &ndm, sizeof(ndm), NLMSG_ALIGNTO) < 0) + LOG(FATAL) << __FUNCTION__ << ": out of memory"; + + if (vlan > 0) { + if (nla_put_u16(m, NDA_VLAN, vlan) < 0) + LOG(FATAL) << __FUNCTION__ << ": out of memory"; + } + + if (flags > 0) { + if (nla_put_u8(m, NDA_NDM_FLAGS_MASK, flags) < 0) + LOG(FATAL) << __FUNCTION__ << ": out of memory"; + } + + err = nl_send_auto_complete(sock, m); + nlmsg_free(m); + if (err < 0) + LOG(FATAL) << __FUNCTION__ << ": " << nl_geterror(err); + + if ((err = nl_recvmsgs_default(sock)) < 0) { + LOG(FATAL) << __FUNCTION__ << ": " << nl_geterror(err); + } + + return 0; + } +}; + +} // namespace basebox