diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 93281dbcd9..105f4f4cdd 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -708,6 +708,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, string grat_arp = ""; string mpls = ""; string ipv6_link_local_mode = ""; + string loopback_action = ""; for (auto idx : data) { @@ -750,6 +751,10 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { vlanId = value; } + else if (field == "loopback_action") + { + loopback_action = value; + } } if (op == SET_COMMAND) @@ -791,6 +796,13 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, data.push_back(fvTuple); } + /* Set loopback action */ + if (!loopback_action.empty()) + { + FieldValueTuple fvTuple("loopback_action", loopback_action); + data.push_back(fvTuple); + } + /* Set mpls */ if (!setIntfMpls(alias, mpls)) { diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 587e04e140..2fb329199d 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -416,6 +416,37 @@ bool IntfsOrch::setIntfProxyArp(const string &alias, const string &proxy_arp) return true; } +bool IntfsOrch::setIntfLoopbackAction(const Port &port, string actionStr) +{ + sai_attribute_t attr; + sai_packet_action_t action; + + if (!getSaiLoopbackAction(actionStr, action)) + { + return false; + } + + attr.id = SAI_ROUTER_INTERFACE_ATTR_LOOPBACK_PACKET_ACTION; + attr.value.s32 = action; + + sai_status_t status = sai_router_intfs_api->set_router_interface_attribute(port.m_rif_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Loopback action [%s] set failed, interface [%s], rc [%d]", + actionStr.c_str(), port.m_alias.c_str(), status); + + task_process_status handle_status = handleSaiSetStatus(SAI_API_ROUTER_INTERFACE, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + SWSS_LOG_NOTICE("Loopback action [%s] set success, interface [%s]", + actionStr.c_str(), port.m_alias.c_str()); + return true; +} + set IntfsOrch:: getSubnetRoutes() { SWSS_LOG_ENTER(); @@ -433,7 +464,9 @@ set IntfsOrch:: getSubnetRoutes() return subnet_routes; } -bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPrefix *ip_prefix, const bool adminUp, const uint32_t mtu) +bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPrefix *ip_prefix, + const bool adminUp, const uint32_t mtu, string loopbackAction) + { SWSS_LOG_ENTER(); @@ -443,7 +476,7 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre auto it_intfs = m_syncdIntfses.find(alias); if (it_intfs == m_syncdIntfses.end()) { - if (!ip_prefix && addRouterIntfs(vrf_id, port)) + if (!ip_prefix && addRouterIntfs(vrf_id, port, loopbackAction)) { gPortsOrch->increasePortRefCount(alias); IntfsEntry intfs_entry; @@ -665,6 +698,7 @@ void IntfsOrch::doTask(Consumer &consumer) string inband_type = ""; bool mpls = false; string vlan = ""; + string loopbackAction = ""; for (auto idx : data) { @@ -757,6 +791,10 @@ void IntfsOrch::doTask(Consumer &consumer) { vlan = value; } + else if (field == "loopback_action") + { + loopbackAction = value; + } } if (alias == "eth0" || alias == "docker0") @@ -874,7 +912,8 @@ void IntfsOrch::doTask(Consumer &consumer) { adminUp = port.m_admin_state_up; } - if (!setIntf(alias, vrf_id, ip_prefix_in_key ? &ip_prefix : nullptr, adminUp, mtu)) + + if (!setIntf(alias, vrf_id, ip_prefix_in_key ? &ip_prefix : nullptr, adminUp, mtu, loopbackAction)) { it++; continue; @@ -906,6 +945,12 @@ void IntfsOrch::doTask(Consumer &consumer) setRouterIntfsMpls(port); gPortsOrch->setPort(alias, port); } + + /* Set loopback action */ + if (!loopbackAction.empty()) + { + setIntfLoopbackAction(port, loopbackAction); + } } } @@ -1047,7 +1092,28 @@ void IntfsOrch::doTask(Consumer &consumer) } } -bool IntfsOrch::addRouterIntfs(sai_object_id_t vrf_id, Port &port) +bool IntfsOrch::getSaiLoopbackAction(const string &actionStr, sai_packet_action_t &action) +{ + const unordered_map loopbackActionMap = + { + {"drop", SAI_PACKET_ACTION_DROP}, + {"forward", SAI_PACKET_ACTION_FORWARD}, + }; + + auto it = loopbackActionMap.find(actionStr); + if (it != loopbackActionMap.end()) + { + action = loopbackActionMap.at(actionStr); + return true; + } + else + { + SWSS_LOG_WARN("Unsupported loopback action [%s]", actionStr.c_str()); + return false; + } +} + +bool IntfsOrch::addRouterIntfs(sai_object_id_t vrf_id, Port &port, string loopbackActionStr) { SWSS_LOG_ENTER(); @@ -1067,6 +1133,17 @@ bool IntfsOrch::addRouterIntfs(sai_object_id_t vrf_id, Port &port) attr.value.oid = vrf_id; attrs.push_back(attr); + if (!loopbackActionStr.empty()) + { + sai_packet_action_t loopbackAction; + if (getSaiLoopbackAction(loopbackActionStr, loopbackAction)) + { + attr.id = SAI_ROUTER_INTERFACE_ATTR_LOOPBACK_PACKET_ACTION; + attr.value.s32 = loopbackAction; + attrs.push_back(attr); + } + } + attr.id = SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS; if (port.m_mac) { diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 5605abf133..246474c0ee 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -54,7 +54,9 @@ class IntfsOrch : public Orch void addRifToFlexCounter(const string&, const string&, const string&); void removeRifFromFlexCounter(const string&, const string&); - bool setIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr, const bool adminUp = true, const uint32_t mtu = 0); + bool setIntfLoopbackAction(const Port &port, string actionStr); + bool getSaiLoopbackAction(const string &actionStr, sai_packet_action_t &action); + bool setIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr, const bool adminUp = true, const uint32_t mtu = 0, string loopbackAction = ""); bool removeIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr); void addIp2MeRoute(sai_object_id_t vrf_id, const IpPrefix &ip_prefix); @@ -91,7 +93,7 @@ class IntfsOrch : public Orch std::string getRifFlexCounterTableKey(std::string s); - bool addRouterIntfs(sai_object_id_t vrf_id, Port &port); + bool addRouterIntfs(sai_object_id_t vrf_id, Port &port, string loopbackAction); bool removeRouterIntfs(Port &port); void addDirectedBroadcast(const Port &port, const IpPrefix &ip_prefix); diff --git a/orchagent/port.h b/orchagent/port.h index 83f61e1b1c..561f9df022 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -150,7 +150,6 @@ class Port sai_port_interface_type_t m_interface_type; std::vector m_adv_interface_types; bool m_mpls = false; - /* * Following two bit vectors are used to lock * the PG/queue from being changed in BufferOrch. diff --git a/tests/test_interface.py b/tests/test_interface.py index a57970b1e5..98f1527152 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -4,6 +4,8 @@ from swsscommon import swsscommon +VLAN_SUB_INTERFACE_SEPARATOR = '.' + class TestRouterInterface(object): def setup_db(self, dvs): self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) @@ -2193,6 +2195,100 @@ def test_VLanInterfaceIpv6LinkLocalOnly(self, dvs, testlog): # one loopback router interface assert len(intf_entries) == 1 + def set_loopback_action(self, interface, action): + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + else: + sub_intf_sep_idx = interface.find(VLAN_SUB_INTERFACE_SEPARATOR) + if sub_intf_sep_idx != -1: + tbl_name = "VLAN_SUB_INTERFACE" + else: + tbl_name = "INTERFACE" + + fvs = swsscommon.FieldValuePairs([("loopback_action", action)]) + tbl = swsscommon.Table(self.cdb, tbl_name) + tbl.set(interface, fvs) + time.sleep(1) + + def loopback_action_test(self, iface, action): + # create interface + self.create_l3_intf(iface, "") + + # set interface loopback action in config db + self.set_loopback_action(iface, action) + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get(iface) + assert status == True + + action_found = False + for fv in fvs: + if fv[0] == "loopback_action": + action_found = True + assert fv[1] == action + assert action_found == True + + # check asic db + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + + action_map = {"drop": "SAI_PACKET_ACTION_DROP", "forward": "SAI_PACKET_ACTION_FORWARD"} + action_found = False + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_LOOPBACK_PACKET_ACTION": + action_found = True + assert fv[1] == action_map[action] + assert action_found == True + + # remove interface + self.remove_l3_intf(iface) + + def test_interfaceLoopbackActionDrop(self, dvs, testlog): + self.setup_db(dvs) + self.loopback_action_test("Ethernet8", "drop") + + def test_interfaceLoopbackActionForward(self, dvs, testlog): + self.setup_db(dvs) + self.loopback_action_test("Ethernet8", "forward") + + def test_subInterfaceLoopbackActionDrop(self, dvs, testlog): + self.setup_db(dvs) + self.loopback_action_test("Ethernet8.1", "drop") + + def test_subInterfaceLoopbackActionForward(self, dvs, testlog): + self.setup_db(dvs) + self.loopback_action_test("Ethernet8.1", "forward") + + def test_vlanInterfaceLoopbackActionDrop(self, dvs, testlog): + self.setup_db(dvs) + self.create_vlan("10") + self.loopback_action_test("Vlan10", "drop") + self.remove_vlan("10") + + def test_vlanInterfaceLoopbackActionForward(self, dvs, testlog): + self.setup_db(dvs) + self.create_vlan("20") + self.loopback_action_test("Vlan20", "forward") + self.remove_vlan("20") + + def test_portChannelInterfaceLoopbackActionDrop(self, dvs, testlog): + self.setup_db(dvs) + self.create_port_channel("PortChannel009") + self.loopback_action_test("PortChannel009", "drop") + self.remove_port_channel("PortChannel009") + + def test_portChannelInterfaceLoopbackActionForward(self, dvs, testlog): + self.setup_db(dvs) + self.create_port_channel("PortChannel010") + self.loopback_action_test("PortChannel010", "forward") + self.remove_port_channel("PortChannel010") # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying