From 6fb8106aa712ffcb10a2be51255211b2e33fcd18 Mon Sep 17 00:00:00 2001 From: Sonic Build Admin Date: Tue, 4 Feb 2025 23:02:59 +0000 Subject: [PATCH] Avoid removing a VRF routing table when there are pending creation entries in gRouteBulker **What I did** Avoid removing a VRF routing table when there are pending creation entries in gRouteBulker 1. Remove a VRF routing table when a routing entry is removed only if there is no pending creation entry in gRouteBulker 2. Avoid uninitialized value SAI IP address/prefix structure **Why I did it** Fix issue: out of range exception can be thrown in `addRoutePost` due to non exist VRF ``` (gdb) bt #0 0x00007f5791aedebc in ?? () from /lib/x86_64-linux-gnu/libc.so.6 #1 0x00007f5791a9efb2 in raise () from /lib/x86_64-linux-gnu/libc.so.6 #2 0x00007f5791a89472 in abort () from /lib/x86_64-linux-gnu/libc.so.6 #3 0x00007f5791de0919 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6 #4 0x00007f5791debe1a in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6 #5 0x00007f5791debe85 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6 #6 0x00007f5791dec0d8 in __cxa_throw () from /lib/x86_64-linux-gnu/libstdc++.so.6 #7 0x00007f5791de3240 in std::__throw_out_of_range(char const*) () from /lib/x86_64-linux-gnu/libstdc++.so.6 #8 0x00005594e856d956 in std::map, std::allocator > >, std::less, std::allocator, std::allocator > > > > >::at (this=, __k=) at /usr/include/c++/12/bits/stl_map.h:551 #9 0x00005594e8564beb in RouteOrch::addRoutePost (this=this@entry=0x5594ea13e080, ctx=..., nextHops=...) at ./orchagent/routeorch.cpp:2145 #10 0x00005594e856b0b2 in RouteOrch::doTask (this=0x5594ea13e080, consumer=...) at ./orchagent/routeorch.cpp:1021 #11 0x00005594e85282d2 in Orch::doTask (this=0x5594ea13e080) at ./orchagent/orch.cpp:553 #12 0x00005594e851909a in OrchDaemon::start (this=this@entry=0x5594ea0a0950) at ./orchagent/orchdaemon.cpp:895 #13 0x00005594e8485632 in main (argc=, argv=) at ./orchagent/main.cpp:818 ``` **How I verified it** Unit (mock) test **Details if related** Originally, it cleaned up a VRF routing table whenever a prefix of the VRF was removed if 1. there was no routing entry in the VRF routing table and 2. the prefix was not pending creation in gRouteBulker The motivation is to remove a VRF routing table if there is no routing entry in the VRF and no routing entry pending creation for that VRF. However, condition 2 does not guarantee that. The ideal way of the 2nd condition is to check pending creation entries of a certain VRF, which we can not do. So, we are using strict conditions here as the following: 1. there is no routing entry in the VRF routing table and 2. there is no pending creating routing entry in gRouteBulker regardless of which VRF it belongs to --- orchagent/routeorch.cpp | 18 +++++++++++++----- orchagent/swssnet.h | 3 +++ tests/mock_tests/routeorch_ut.cpp | 31 +++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index b40f27fb0f..fcdb6d47d8 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -2431,13 +2431,21 @@ bool RouteOrch::removeRoute(RouteBulkContext& ctx) size_t creating = gRouteBulker.creating_entries_count(route_entry); if (it_route == it_route_table->second.end() && creating == 0) { - if (it_route_table->second.size() == 0) - { + /* + * Clean up the VRF routing table if + * 1. there is no routing entry in the VRF routing table and + * 2. there is no pending bulk creation routing entry in gRouteBulker + * The ideal way of the 2nd condition is to check pending bulk creation entries of a certain VRF. + * However, we can not do that unless going over all entries in gRouteBulker. + * So, we use above strict conditions here + */ + if (it_route_table->second.size() == 0 && gRouteBulker.creating_entries_count() == 0) + { m_syncdRoutes.erase(vrf_id); m_vrfOrch->decreaseVrfRefCount(vrf_id); - } - SWSS_LOG_INFO("Failed to find route entry, vrf_id 0x%" PRIx64 ", prefix %s\n", vrf_id, - ipPrefix.to_string().c_str()); + } + SWSS_LOG_INFO("Failed to find route entry, vrf_id 0x%" PRIx64 ", prefix %s\n", vrf_id, + ipPrefix.to_string().c_str()); return true; } diff --git a/orchagent/swssnet.h b/orchagent/swssnet.h index 82b5b6f94f..8084b7fb4e 100644 --- a/orchagent/swssnet.h +++ b/orchagent/swssnet.h @@ -21,6 +21,7 @@ inline static sai_ip_address_t& copy(sai_ip_address_t& dst, const IpAddress& src switch(sip.family) { case AF_INET: + memset((void*)&dst.addr, 0, sizeof(dst.addr)); dst.addr_family = SAI_IP_ADDR_FAMILY_IPV4; dst.addr.ip4 = sip.ip_addr.ipv4_addr; break; @@ -41,6 +42,7 @@ inline static sai_ip_prefix_t& copy(sai_ip_prefix_t& dst, const IpPrefix& src) switch(ia.family) { case AF_INET: + memset((void*)&dst, 0, sizeof(dst)); dst.addr_family = SAI_IP_ADDR_FAMILY_IPV4; dst.addr.ip4 = ia.ip_addr.ipv4_addr; dst.mask.ip4 = ma.ip_addr.ipv4_addr; @@ -62,6 +64,7 @@ inline static sai_ip_prefix_t& copy(sai_ip_prefix_t& dst, const IpAddress& src) switch(sip.family) { case AF_INET: + memset((void*)&dst, 0, sizeof(dst)); dst.addr_family = SAI_IP_ADDR_FAMILY_IPV4; dst.addr.ip4 = sip.ip_addr.ipv4_addr; dst.mask.ip4 = 0xFFFFFFFF; diff --git a/tests/mock_tests/routeorch_ut.cpp b/tests/mock_tests/routeorch_ut.cpp index 043b7b6b0a..f66fc9ac38 100644 --- a/tests/mock_tests/routeorch_ut.cpp +++ b/tests/mock_tests/routeorch_ut.cpp @@ -306,6 +306,11 @@ namespace routeorch_test {"mac_addr", "00:00:00:00:00:00" }}); intfTable.set("Ethernet4:11.0.0.1/32", { { "scope", "global" }, { "family", "IPv4" }}); + intfTable.set("Ethernet8", { {"NULL", "NULL" }, + {"vrf_name", "Vrf1"}, + {"mac_addr", "00:00:00:00:00:00" }}); + intfTable.set("Ethernet8:20.0.0.1/24", { { "scope", "global" }, + { "family", "IPv4" }}); gIntfsOrch->addExistingData(&intfTable); static_cast(gIntfsOrch)->doTask(); @@ -552,4 +557,30 @@ namespace routeorch_test ASSERT_EQ(current_create_count, create_route_count); ASSERT_EQ(current_set_count, set_route_count); } + + TEST_F(RouteOrchTest, RouteOrchTestVrfRoute) + { + std::deque entries; + entries.push_back({"Vrf2", "SET", { {"vni", "500200"}}}); + auto vrfConsumer = dynamic_cast(gVrfOrch->getExecutor(APP_VRF_TABLE_NAME)); + vrfConsumer->addToSync(entries); + static_cast(gVrfOrch)->doTask(); + entries.clear(); + entries.push_back({"Ethernet8", "SET", { {"vrf_name", "Vrf2"}}}); + auto intfConsumer = dynamic_cast(gIntfsOrch->getExecutor(APP_INTF_TABLE_NAME)); + intfConsumer->addToSync(entries); + static_cast(gIntfsOrch)->doTask(); + auto routeConsumer = dynamic_cast(gRouteOrch->getExecutor(APP_ROUTE_TABLE_NAME)); + entries.clear(); + entries.push_back({"Vrf2:fe80::/64", "DEL", {}}); + entries.push_back({"Vrf2:20.0.0.0/24", "DEL", {}}); + entries.push_back({"Vrf2:fe80::/64", "SET", { {"protocol", "kernel"}, + {"nexthop", "::"}, + {"ifname", "Ethernet8"}}}); + entries.push_back({"Vrf2:20.0.0.0/24", "SET", { {"protocol", "kernel"}, + {"nexthop", "0.0.0.0"}, + {"ifname", "Ethernet8"}}}); + routeConsumer->addToSync(entries); + static_cast(gRouteOrch)->doTask(); + } }