diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 0f7209c3614b..27b6c1b0bfac 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -513,6 +513,7 @@ void RouteOrch::doTask(Consumer& consumer) string remote_macs; bool& excp_intfs_flag = ctx.excp_intfs_flag; bool overlay_nh = false; + bool blackhole = false; for (auto i : kfvFieldsValues(t)) { @@ -529,6 +530,9 @@ void RouteOrch::doTask(Consumer& consumer) if (fvField(i) == "router_mac") remote_macs = fvValue(i); + + if (fvField(i) == "blackhole") + blackhole = fvValue(i) == "true"; } vector& ipv = ctx.ipv; @@ -544,7 +548,7 @@ void RouteOrch::doTask(Consumer& consumer) /* Resize the ip vector to match ifname vector * as tokenize(",", ',') will miss the last empty segment. */ - if (alsv.size() == 0) + if (alsv.size() == 0 && !blackhole) { SWSS_LOG_WARN("Skip the route %s, for it has an empty ifname field.", key.c_str()); it = consumer.m_toSync.erase(it); @@ -597,7 +601,11 @@ void RouteOrch::doTask(Consumer& consumer) string nhg_str = ""; NextHopGroupKey& nhg = ctx.nhg; - if (overlay_nh == false) + if (blackhole) + { + nhg = NextHopGroupKey(); + } + else if (overlay_nh == false) { if (alsv[0] == "tun0" && !(IpAddress(ipv[0]).isZero())) { @@ -630,10 +638,8 @@ void RouteOrch::doTask(Consumer& consumer) if (ipv.size() == 1 && IpAddress(ipv[0]).isZero()) { - /* blackhole to be done */ if (alsv[0] == "unknown") { - /* add addBlackholeRoute or addRoute support empty nhg */ it = consumer.m_toSync.erase(it); } /* skip direct routes to tun0 */ @@ -1349,6 +1355,7 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) bool status = false; bool curNhgIsFineGrained = false; bool prevNhgWasFineGrained = false; + bool blackhole = false; if (m_syncdRoutes.find(vrf_id) == m_syncdRoutes.end()) { @@ -1377,6 +1384,11 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) return false; } } + else if (nextHops.getSize() == 0) + { + /* The route is pointing to a blackhole */ + blackhole = true; + } else if (nextHops.getSize() == 1) { /* The route is pointing to a next hop */ @@ -1544,8 +1556,16 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) */ if (it_route == m_syncdRoutes.at(vrf_id).end()) { - route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; - route_attr.value.oid = next_hop_id; + if (blackhole) + { + route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; + route_attr.value.s32 = SAI_PACKET_ACTION_DROP; + } + else + { + route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; + route_attr.value.oid = next_hop_id; + } /* Default SAI_ROUTE_ATTR_PACKET_ACTION is SAI_PACKET_ACTION_FORWARD */ object_statuses.emplace_back(); @@ -1559,8 +1579,8 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) } else { - /* Set the packet action to forward when there was no next hop (dropped) */ - if (it_route->second.getSize() == 0) + /* Set the packet action to forward when there was no next hop (dropped) and not pointing to blackhole*/ + if (it_route->second.getSize() == 0 && !blackhole) { route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; route_attr.value.s32 = SAI_PACKET_ACTION_FORWARD; @@ -1584,6 +1604,15 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) object_statuses.emplace_back(); gRouteBulker.set_entry_attribute(&object_statuses.back(), &route_entry, &route_attr); } + + if (blackhole) + { + route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; + route_attr.value.s32 = SAI_PACKET_ACTION_DROP; + + object_statuses.emplace_back(); + gRouteBulker.set_entry_attribute(&object_statuses.back(), &route_entry, &route_attr); + } } return false; } @@ -1595,6 +1624,7 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey const sai_object_id_t& vrf_id = ctx.vrf_id; const IpPrefix& ipPrefix = ctx.ip_prefix; bool isFineGrained = false; + bool blackhole = false; const auto& object_statuses = ctx.object_statuses; @@ -1612,6 +1642,11 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey /* Route is pointing to Fine Grained ECMP nexthop group */ isFineGrained = true; } + else if (nextHops.getSize() == 0) + { + /* The route is pointing to a blackhole */ + blackhole = true; + } else if (nextHops.getSize() == 1) { /* The route is pointing to a next hop */ @@ -1739,8 +1774,8 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey { sai_status_t status; - /* Set the packet action to forward when there was no next hop (dropped) */ - if (it_route->second.getSize() == 0) + /* Set the packet action to forward when there was no next hop (dropped) and not pointing to blackhole */ + if (it_route->second.getSize() == 0 && !blackhole) { status = *it_status++; if (status != SAI_STATUS_SUCCESS) @@ -1790,6 +1825,22 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey } } + if (blackhole) + { + /* Set the packet action to drop for blackhole routes */ + status = *it_status++; + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set blackhole route %s with packet action drop, %d", + ipPrefix.to_string().c_str(), status); + task_process_status handle_status = handleSaiSetStatus(SAI_API_ROUTE, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + } + SWSS_LOG_INFO("Post set route %s with next hop(s) %s", ipPrefix.to_string().c_str(), nextHops.to_string().c_str()); } diff --git a/tests/test_route.py b/tests/test_route.py index 341ef53bbd81..56d3af5c5b9e 100644 --- a/tests/test_route.py +++ b/tests/test_route.py @@ -62,6 +62,13 @@ def _access_function(): wait_for_result(_access_function) + def get_asic_db_key(self, destination): + route_entries = self.adb.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for route_entry in route_entries: + if json.loads(route_entry)["dest"] == destination: + return route_entry + return None + def check_route_entries_with_vrf(self, destinations, vrf_oids): def _access_function(): route_entries = self.adb.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") @@ -839,6 +846,60 @@ def test_RouteAndNexthopInDifferentVrf(self, dvs, testlog): dvs.servers[3].runcmd("ip route del default dev eth0") dvs.servers[3].runcmd("ip address del 20.0.1.2/24 dev eth0") + def test_RouteAddRemoveIpv4BlackholeRoute(self, dvs, testlog): + self.setup_db(dvs) + + self.clear_srv_config(dvs) + + # add route entry + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 2.2.2.0/24 blackhole\"") + + # check application database + self.pdb.wait_for_entry("ROUTE_TABLE", "2.2.2.0/24") + + # check ASIC route database + self.check_route_entries(["2.2.2.0/24"]) + key = self.get_asic_db_key("2.2.2.0/24") + assert key + fvs = self.adb.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY", key) + assert fvs.get("SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION") == "SAI_PACKET_ACTION_DROP" + + # remove route entry + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route 2.2.2.0/24 blackhole\"") + + # check application database + self.pdb.wait_for_deleted_entry("ROUTE_TABLE", "2.2.2.0/24") + + # check ASIC route database + self.check_deleted_route_entries(["2.2.2.0/24"]) + + def test_RouteAddRemoveIpv6BlackholeRoute(self, dvs, testlog): + self.setup_db(dvs) + + self.clear_srv_config(dvs) + + # add route entry + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ipv6 route 3000::0/64 blackhole\"") + + # check application database + self.pdb.wait_for_entry("ROUTE_TABLE", "3000::/64") + + # check ASIC route database + self.check_route_entries(["3000::/64"]) + key = self.get_asic_db_key("3000::/64") + assert key + fvs = self.adb.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY", key) + assert fvs.get("SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION") == "SAI_PACKET_ACTION_DROP" + + # remove route entry + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ipv6 route 3000::0/64 blackhole\"") + + # check application database + self.pdb.wait_for_deleted_entry("ROUTE_TABLE", "3000::/64") + + # check ASIC route database + self.check_deleted_route_entries(["3000::/64"]) + class TestRoutePerf(TestRouteBase): """ Performance tests for route """ def test_PerfAddRemoveRoute(self, dvs, testlog):