From 15a3b6cae4cfa103984620d8a7e4eb4c401167ba Mon Sep 17 00:00:00 2001 From: Akhilesh Samineni <47657796+AkhileshSamineni@users.noreply.github.com> Date: Wed, 1 Dec 2021 11:02:00 +0530 Subject: [PATCH] Delete the IPv6 link-local Neighbor when ipv6 link-local mode is disabled (#1897) * IPv6 link-local Neighbor deletion when ipv6 link-local mode is disabled. Signed-off-by: Akhilesh Samineni --- cfgmgr/intfmgr.cpp | 48 +++++++++++++++ cfgmgr/intfmgr.h | 3 + neighsyncd/neighsync.cpp | 2 +- tests/test_ipv6_link_local.py | 112 ++++++++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 tests/test_ipv6_link_local.py diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 1567762ffaca..8489d09bb370 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -40,6 +40,7 @@ IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c m_stateVrfTable(stateDb, STATE_VRF_TABLE_NAME), m_stateIntfTable(stateDb, STATE_INTERFACE_TABLE_NAME), m_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME), + m_neighTable(appDb, APP_NEIGH_TABLE_NAME), m_appLagTable(appDb, APP_LAG_TABLE_NAME) { auto subscriberStateTable = new swss::SubscriberStateTable(stateDb, @@ -616,6 +617,35 @@ bool IntfMgr::isIntfStateOk(const string &alias) return false; } +void IntfMgr::delIpv6LinkLocalNeigh(const string &alias) +{ + vector neighEntries; + + SWSS_LOG_INFO("Deleting ipv6 link local neighbors for %s", alias.c_str()); + + m_neighTable.getKeys(neighEntries); + for (auto neighKey : neighEntries) + { + if (!neighKey.compare(0, alias.size(), alias.c_str())) + { + vector keys = tokenize(neighKey, ':', 1); + if (keys.size() == 2) + { + IpAddress ipAddress(keys[1]); + if (ipAddress.getAddrScope() == IpAddress::AddrScope::LINK_SCOPE) + { + stringstream cmd; + string res; + + cmd << IP_CMD << " neigh del dev " << keys[0] << " " << keys[1] ; + swss::exec(cmd.str(), res); + SWSS_LOG_INFO("Deleted ipv6 link local neighbor - %s", keys[1].c_str()); + } + } + } + } +} + bool IntfMgr::doIntfGeneralTask(const vector& keys, vector data, const string& op) @@ -755,6 +785,17 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, /* Set ipv6 mode */ if (!ipv6_link_local_mode.empty()) { + if ((ipv6_link_local_mode == "enable") && (m_ipv6LinkLocalModeList.find(alias) == m_ipv6LinkLocalModeList.end())) + { + m_ipv6LinkLocalModeList.insert(alias); + SWSS_LOG_INFO("Inserted ipv6 link local mode list for %s", alias.c_str()); + } + else if ((ipv6_link_local_mode == "disable") && (m_ipv6LinkLocalModeList.find(alias) != m_ipv6LinkLocalModeList.end())) + { + m_ipv6LinkLocalModeList.erase(alias); + delIpv6LinkLocalNeigh(alias); + SWSS_LOG_INFO("Erased ipv6 link local mode list for %s", alias.c_str()); + } FieldValueTuple fvTuple("ipv6_use_link_local_only", ipv6_link_local_mode); data.push_back(fvTuple); } @@ -910,6 +951,13 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, removeSubIntfState(alias); } + if (m_ipv6LinkLocalModeList.find(alias) != m_ipv6LinkLocalModeList.end()) + { + m_ipv6LinkLocalModeList.erase(alias); + delIpv6LinkLocalNeigh(alias); + SWSS_LOG_INFO("Erased ipv6 link local mode list for %s", alias.c_str()); + } + m_appIntfTableProducer.del(alias); m_stateIntfTable.del(alias); } diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index e1a5049fff88..84c0020eb052 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -31,10 +31,12 @@ class IntfMgr : public Orch ProducerStateTable m_appIntfTableProducer; Table m_cfgIntfTable, m_cfgVlanIntfTable, m_cfgLagIntfTable, m_cfgLoopbackIntfTable; Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateVrfTable, m_stateIntfTable, m_appLagTable; + Table m_neighTable; SubIntfMap m_subIntfList; std::set m_loopbackIntfList; std::set m_pendingReplayIntfList; + std::set m_ipv6LinkLocalModeList; void setIntfIp(const std::string &alias, const std::string &opCmd, const IpPrefix &ipPrefix); void setIntfVrf(const std::string &alias, const std::string &vrfName); @@ -65,6 +67,7 @@ class IntfMgr : public Orch void removeHostSubIntf(const std::string &subIntf); void setSubIntfStateOk(const std::string &alias); void removeSubIntfState(const std::string &alias); + void delIpv6LinkLocalNeigh(const std::string &alias); bool setIntfProxyArp(const std::string &alias, const std::string &proxy_arp); bool setIntfGratArp(const std::string &alias, const std::string &grat_arp); diff --git a/neighsyncd/neighsync.cpp b/neighsyncd/neighsync.cpp index 6b1abc235faf..cb04371d41a1 100644 --- a/neighsyncd/neighsync.cpp +++ b/neighsyncd/neighsync.cpp @@ -82,7 +82,7 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) /* Ignore IPv6 link-local addresses as neighbors, if ipv6 link local mode is disabled */ if (family == IPV6_NAME && IN6_IS_ADDR_LINKLOCAL(nl_addr_get_binary_addr(rtnl_neigh_get_dst(neigh)))) { - if (isLinkLocalEnabled(intfName) == false) + if ((isLinkLocalEnabled(intfName) == false) && (nlmsg_type != RTM_DELNEIGH)) { return; } diff --git a/tests/test_ipv6_link_local.py b/tests/test_ipv6_link_local.py new file mode 100644 index 000000000000..048b8f2e1715 --- /dev/null +++ b/tests/test_ipv6_link_local.py @@ -0,0 +1,112 @@ +import time +import json +import pytest + +from swsscommon import swsscommon + +class TestIPv6LinkLocal(object): + def setup_db(self, dvs): + self.pdb = dvs.get_app_db() + self.adb = dvs.get_asic_db() + self.cdb = dvs.get_config_db() + + def set_admin_status(self, interface, status): + self.cdb.update_entry("PORT", interface, {"admin_status": status}) + + def add_ip_address(self, interface, ip): + self.cdb.create_entry("INTERFACE", interface + "|" + ip, {"NULL": "NULL"}) + time.sleep(2) + + def remove_ip_address(self, interface, ip): + self.cdb.delete_entry("INTERFACE", interface + "|" + ip) + time.sleep(2) + + def create_ipv6_link_local_intf(self, interface): + self.cdb.create_entry("INTERFACE", interface, {"ipv6_use_link_local_only": "enable"}) + time.sleep(2) + + def remove_ipv6_link_local_intf(self, interface): + self.cdb.delete_entry("INTERFACE", interface) + time.sleep(2) + + def test_NeighborAddRemoveIpv6LinkLocal(self, dvs, testlog): + self.setup_db(dvs) + + # create ipv6 link local intf + self.create_ipv6_link_local_intf("Ethernet0") + self.create_ipv6_link_local_intf("Ethernet4") + + # bring up interface + self.set_admin_status("Ethernet0", "up") + self.set_admin_status("Ethernet4", "up") + + # set ip address + self.add_ip_address("Ethernet0", "2000::1/64") + self.add_ip_address("Ethernet4", "2001::1/64") + dvs.runcmd("sysctl -w net.ipv6.conf.all.forwarding=1") + dvs.runcmd("sysctl -w net.ipv6.conf.default.forwarding=1") + + # set ip address and default route + dvs.servers[0].runcmd("ip -6 address add 2000::2/64 dev eth0") + dvs.servers[0].runcmd("ip -6 route add default via 2000::1") + + dvs.servers[1].runcmd("ip -6 address add 2001::2/64 dev eth0") + dvs.servers[1].runcmd("ip -6 route add default via 2001::1") + time.sleep(2) + + # get neighbor entry + dvs.servers[0].runcmd("ping -6 -c 1 2001::2") + time.sleep(2) + + # Neigh entries should contain Ipv6-link-local neighbors, should be 4 + neigh_entries = self.pdb.get_keys("NEIGH_TABLE") + assert (len(neigh_entries) == 4) + + found_entry = False + for key in neigh_entries: + if (key.find("Ethernet4:2001::2") or key.find("Ethernet0:2000::2")): + found_entry = True + + assert found_entry + + # remove ip address + self.remove_ip_address("Ethernet0", "2000::1/64") + self.remove_ip_address("Ethernet4", "2001::1/64") + + # remove ipv6 link local intf + self.remove_ipv6_link_local_intf("Ethernet0") + self.remove_ipv6_link_local_intf("Ethernet4") + + # Neigh entries should not contain Ipv6-link-local neighbors, should be 2 + neigh_after_entries = self.pdb.get_keys("NEIGH_TABLE") + print(neigh_after_entries) + assert (len(neigh_after_entries) == 2) + + found_existing_entry = False + for key in neigh_entries: + if (key.find("Ethernet4:2001::2") or key.find("Ethernet0:2000::2")): + found_existing_entry = True + + assert found_existing_entry + + self.set_admin_status("Ethernet0", "down") + self.set_admin_status("Ethernet4", "down") + + # remove ip address and default route + dvs.servers[0].runcmd("ip -6 route del default dev eth0") + dvs.servers[0].runcmd("ip -6 address del 2000::2/64 dev eth0") + + dvs.servers[1].runcmd("ip -6 route del default dev eth0") + dvs.servers[1].runcmd("ip -6 address del 2001::2/64 dev eth0") + + dvs.runcmd("sysctl -w net.ipv6.conf.all.forwarding=0") + dvs.runcmd("sysctl -w net.ipv6.conf.default.forwarding=0") + + neigh_entries = self.pdb.get_keys("NEIGH_TABLE") + assert (len(neigh_entries) == 0) + +# Add Dummy always-pass test at end as workaroud +# for issue when Flaky fail on final test it invokes module tear-down before retrying +def test_nonflaky_dummy(): + pass +