From 0bd2882cfadce3f5c571afd7dd91abfd64a0c6ee Mon Sep 17 00:00:00 2001 From: Volodymyr Samotiy Date: Thu, 28 May 2020 17:40:57 +0300 Subject: [PATCH] [proxy_arp] Implement proxy ARP feature Signed-off-by: Volodymyr Samotiy --- cfgmgr/intfmgr.cpp | 48 +++++++++++++++++++++++ cfgmgr/intfmgr.h | 2 + orchagent/intfsorch.cpp | 85 +++++++++++++++++++++++++++++++++++++++++ orchagent/intfsorch.h | 4 ++ tests/test_vnet.py | 46 ++++++++++++++++------ 5 files changed, 173 insertions(+), 12 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 7997f2e19b..055141dab1 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -344,6 +344,33 @@ void IntfMgr::removeSubIntfState(const string &alias) } } +bool IntfMgr::setIntfProxyArp(const string &alias, const string &proxy_arp) +{ + stringstream cmd; + string res; + string proxy_arp_pvlan; + + if (proxy_arp == "enabled") + { + proxy_arp_pvlan = "1"; + } + else if (proxy_arp == "disabled") + { + proxy_arp_pvlan = "0"; + } + else + { + SWSS_LOG_ERROR("Proxy ARP state is invalid: \"%s\"", proxy_arp.c_str()); + return false; + } + + cmd << ECHO_CMD << " " << proxy_arp_pvlan << " > /proc/sys/net/ipv4/conf/" << alias << "/proxy_arp_pvlan"; + EXEC_WITH_ERROR_THROW(cmd.str(), res); + + SWSS_LOG_INFO("Proxy ARP set to \"%s\" on interface \"%s\"", proxy_arp.c_str(), alias.c_str()); + return true; +} + bool IntfMgr::isIntfStateOk(const string &alias) { vector temp; @@ -418,6 +445,8 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, string mtu = ""; string adminStatus = ""; string nat_zone = ""; + string proxy_arp = ""; + for (auto idx : data) { const auto &field = fvField(idx); @@ -432,6 +461,10 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { adminStatus = value; } + else if (field == "proxy_arp") + { + proxy_arp = value; + } if (field == "nat_zone") { @@ -479,6 +512,21 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, setIntfVrf(alias, vrf_name); } + if (!proxy_arp.empty()) + { + if (!setIntfProxyArp(alias, proxy_arp)) + { + SWSS_LOG_ERROR("Failed to set proxy ARP to \"%s\" state for the \"%s\" interface", proxy_arp.c_str(), alias.c_str()); + return false; + } + + if (!alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) + { + FieldValueTuple fvTuple("proxy_arp", proxy_arp); + data.push_back(fvTuple); + } + } + if (!subIntfAlias.empty()) { if (m_subIntfList.find(subIntfAlias) == m_subIntfList.end()) diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index 73448b74c9..e20ce0d4a4 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -42,6 +42,8 @@ class IntfMgr : public Orch void removeHostSubIntf(const std::string &subIntf); void setSubIntfStateOk(const std::string &alias); void removeSubIntfState(const std::string &alias); + + bool setIntfProxyArp(const std::string &alias, const std::string &proxy_arp); }; } diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 8bd7f49df8..ce8b11805a 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -24,6 +24,7 @@ extern sai_router_interface_api_t* sai_router_intfs_api; extern sai_route_api_t* sai_route_api; extern sai_neighbor_api_t* sai_neighbor_api; extern sai_switch_api_t* sai_switch_api; +extern sai_vlan_api_t* sai_vlan_api; extern sai_object_id_t gSwitchId; extern PortsOrch *gPortsOrch; @@ -226,6 +227,75 @@ bool IntfsOrch::setRouterIntfsAdminStatus(const Port &port) return true; } +bool IntfsOrch::setIntfVlanFloodType(const Port &port, sai_vlan_flood_control_type_t vlan_flood_type) +{ + SWSS_LOG_ENTER(); + + if (port.m_type != Port::VLAN) + { + SWSS_LOG_ERROR("VLAN flood type cannot be set for non VLAN interface \"%s\"", port.m_alias.c_str()); + return false; + } + + sai_attribute_t attr; + attr.id = SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE; + attr.value.s32 = vlan_flood_type; + + sai_status_t status = sai_vlan_api->set_vlan_attribute(port.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set flood type for VLAN %u, rv:%d", port.m_vlan_info.vlan_id, status); + return false; + } + + return true; +} + +bool IntfsOrch::setIntfProxyArp(const string &alias, const string &proxy_arp) +{ + SWSS_LOG_ENTER(); + + if (m_syncdIntfses.find(alias) == m_syncdIntfses.end()) + { + SWSS_LOG_ERROR("Interface \"%s\" doesn't exist", alias.c_str()); + return false; + } + + if (m_syncdIntfses[alias].proxy_arp == (proxy_arp == "enabled" ? true : false)) + { + SWSS_LOG_INFO("Proxy ARP is already set to \"%s\" on interface \"%s\"", proxy_arp.c_str(), alias.c_str()); + return true; + } + + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_ERROR("Failed to get port info for the interface \"%s\"", alias.c_str()); + return false; + } + + if (port.m_type == Port::VLAN) + { + sai_vlan_flood_control_type_t vlan_flood_type; + if (proxy_arp == "enabled") + { + vlan_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_NONE; + } + else + { + vlan_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + } + + if (!setIntfVlanFloodType(port, vlan_flood_type)) + { + return false; + } + } + + m_syncdIntfses[alias].proxy_arp = (proxy_arp == "enabled") ? true : false; + return true; +} + set IntfsOrch:: getSubnetRoutes() { SWSS_LOG_ENTER(); @@ -439,6 +509,7 @@ void IntfsOrch::doTask(Consumer &consumer) uint32_t mtu; bool adminUp; uint32_t nat_zone_id = 0; + string proxy_arp = ""; for (auto idx : data) { @@ -502,6 +573,10 @@ void IntfsOrch::doTask(Consumer &consumer) { nat_zone = value; } + else if (field == "proxy_arp") + { + proxy_arp = value; + } } if (alias == "eth0" || alias == "docker0") @@ -624,6 +699,11 @@ void IntfsOrch::doTask(Consumer &consumer) } } + if (!proxy_arp.empty()) + { + setIntfProxyArp(alias, proxy_arp); + } + it = consumer.m_toSync.erase(it); } else if (op == DEL_COMMAND) @@ -682,6 +762,11 @@ void IntfsOrch::doTask(Consumer &consumer) vnet_name = m_vnetInfses.at(alias); } + if (m_syncdIntfses[alias].proxy_arp) + { + setIntfProxyArp(alias, "disabled"); + } + if (!vnet_name.empty()) { VNetOrch* vnet_orch = gDirectory.get(); diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 9b2716eb1e..ee1f1540ff 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -23,6 +23,7 @@ struct IntfsEntry std::set ip_addresses; int ref_count; sai_object_id_t vrf_id; + bool proxy_arp; }; typedef map IntfsTable; @@ -89,6 +90,9 @@ class IntfsOrch : public Orch void addDirectedBroadcast(const Port &port, const IpPrefix &ip_prefix); void removeDirectedBroadcast(const Port &port, const IpPrefix &ip_prefix); + + bool setIntfVlanFloodType(const Port &port, sai_vlan_flood_control_type_t vlan_flood_type); + bool setIntfProxyArp(const string &alias, const string &proxy_arp); }; #endif /* SWSS_INTFSORCH_H */ diff --git a/tests/test_vnet.py b/tests/test_vnet.py index 6e3555e1ee..7b695943c7 100644 --- a/tests/test_vnet.py +++ b/tests/test_vnet.py @@ -220,6 +220,7 @@ def create_vlan_interface(dvs, vlan_name, ifname, vnet_name, ipaddr): "VLAN_INTERFACE", '|', vlan_name, [ ("vnet_name", vnet_name), + ("proxy_arp", "enabled"), ], ) @@ -230,6 +231,7 @@ def create_vlan_interface(dvs, vlan_name, ifname, vnet_name, ipaddr): "INTF_TABLE", ':', vlan_name, [ ("vnet_name", vnet_name), + ("proxy_arp", "enabled"), ], ) time.sleep(2) @@ -392,6 +394,11 @@ def get_switch_mac(dvs): return mac +def check_linux_intf_arp_proxy(dvs, ifname): + (exitcode, out) = dvs.runcmd("cat /proc/sys/net/ipv4/conf/{0}/proxy_arp_pvlan".format(ifname)) + assert out != "1", "ARP proxy is not enabled for VNET interface in Linux kernel" + + loopback_id = 0 def_vr_id = 0 switch_mac = None @@ -407,6 +414,7 @@ class VnetVxlanVrfTunnel(object): ASIC_VRF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER" ASIC_ROUTE_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" ASIC_NEXT_HOP = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP" + ASIC_VLAN_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_VLAN" tunnel_map_ids = set() tunnel_map_entry_ids = set() @@ -562,7 +570,7 @@ def vnet_route_ids(self, dvs, name, local=False): return vr_set - def check_router_interface(self, dvs, name, vlan_oid=0): + def check_router_interface(self, dvs, intf_name, name, vlan_oid=0): # Check RIF in ingress VRF asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) global switch_mac @@ -585,6 +593,12 @@ def check_router_interface(self, dvs, name, vlan_oid=0): #IP2ME route will be created with every router interface new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, 1) + if vlan_oid: + expected_attr = { 'SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE': 'SAI_VLAN_FLOOD_CONTROL_TYPE_NONE' } + check_object(asic_db, self.ASIC_VLAN_TABLE, vlan_oid, expected_attr) + + check_linux_intf_arp_proxy(dvs, intf_name) + self.rifs.add(new_rif) self.routes.update(new_route) @@ -687,6 +701,7 @@ class VnetBitmapVxlanTunnel(object): ASIC_BITMAP_ROUTER_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TABLE_BITMAP_ROUTER_ENTRY" ASIC_FDB_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY" ASIC_NEIGH_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY" + ASIC_VLAN_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_VLAN" tunnel_map_ids = set() tunnel_map_entry_ids = set() @@ -840,7 +855,7 @@ def check_del_vnet_entry(self, dvs, name): self.vnet_bitmap_class_ids.remove(old_bitmap_class_id[0]) - def check_router_interface(self, dvs, name, vlan_oid=0): + def check_router_interface(self, dvs, intf_name, name, vlan_oid=0): asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) expected_attrs = { @@ -852,6 +867,7 @@ def check_router_interface(self, dvs, name, vlan_oid=0): if vlan_oid: expected_attrs.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_VLAN'}) expected_attrs.update({'SAI_ROUTER_INTERFACE_ATTR_VLAN_ID': vlan_oid}) + expected_attrs.update({'SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE': 'SAI_VLAN_FLOOD_CONTROL_TYPE_NONE'}) else: expected_attrs.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_PORT'}) @@ -860,6 +876,12 @@ def check_router_interface(self, dvs, name, vlan_oid=0): new_bitmap_class_id = get_created_entries(asic_db, self.ASIC_BITMAP_CLASS_ENTRY, self.vnet_bitmap_class_ids, 1) + if vlan_oid: + expected_attr = { 'SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE': 'SAI_VLAN_FLOOD_CONTROL_TYPE_NONE' } + check_object(asic_db, self.ASIC_VLAN_TABLE, vlan_oid, expected_attr) + + check_linux_intf_arp_proxy(dvs, intf_name) + self.rifs.add(new_rif) self.vnet_bitmap_class_ids.update(new_bitmap_class_id) @@ -967,10 +989,10 @@ def test_vnet_orch_1(self, dvs, testlog): vnet_obj.check_vxlan_tunnel(dvs, tunnel_name, '10.10.10.10') vid = create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vnet_2000", "100.100.3.1/24") - vnet_obj.check_router_interface(dvs, 'Vnet_2000', vid) + vnet_obj.check_router_interface(dvs, "Vlan100", 'Vnet_2000', vid) vid = create_vlan_interface(dvs, "Vlan101", "Ethernet28", "Vnet_2000", "100.100.4.1/24") - vnet_obj.check_router_interface(dvs, 'Vnet_2000', vid) + vnet_obj.check_router_interface(dvs, "Vlan101", 'Vnet_2000', vid) vnet_obj.fetch_exist_entries(dvs) create_vnet_routes(dvs, "100.100.1.1/32", 'Vnet_2000', '10.10.10.1') @@ -990,7 +1012,7 @@ def test_vnet_orch_1(self, dvs, testlog): vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, 'Vnet_2001', '2001') create_phy_interface(dvs, "Ethernet4", "Vnet_2001", "100.102.1.1/24") - vnet_obj.check_router_interface(dvs, 'Vnet_2001') + vnet_obj.check_router_interface(dvs, "Ethernet4", 'Vnet_2001') vnet_obj.fetch_exist_entries(dvs) create_vnet_routes(dvs, "100.100.2.1/32", 'Vnet_2001', '10.10.10.2', "00:12:34:56:78:9A") @@ -1050,7 +1072,7 @@ def test_vnet_orch_2(self, dvs, testlog): tun_id = vnet_obj.check_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') vid = create_vlan_interface(dvs, "Vlan1001", "Ethernet0", "Vnet_1", "1.1.10.1/24") - vnet_obj.check_router_interface(dvs, 'Vnet_1', vid) + vnet_obj.check_router_interface(dvs, "Vlan1001", 'Vnet_1', vid) vnet_obj.fetch_exist_entries(dvs) create_vnet_routes(dvs, "1.1.1.10/32", 'Vnet_1', '100.1.1.10') @@ -1077,7 +1099,7 @@ def test_vnet_orch_2(self, dvs, testlog): vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, 'Vnet_2', '2222') vid = create_vlan_interface(dvs, "Vlan1002", "Ethernet4", "Vnet_2", "2.2.10.1/24") - vnet_obj.check_router_interface(dvs, 'Vnet_2', vid) + vnet_obj.check_router_interface(dvs, "Vlan1002", 'Vnet_2', vid) vnet_obj.fetch_exist_entries(dvs) create_vnet_routes(dvs, "2.2.2.10/32", 'Vnet_2', '100.1.1.20') @@ -1153,10 +1175,10 @@ def test_vnet_orch_3(self, dvs, testlog): tun_id = vnet_obj.check_vxlan_tunnel(dvs, tunnel_name, '7.7.7.7') vid = create_vlan_interface(dvs, "Vlan2001", "Ethernet8", "Vnet_10", "5.5.10.1/24") - vnet_obj.check_router_interface(dvs, 'Vnet_10', vid) + vnet_obj.check_router_interface(dvs, "Vlan2001", 'Vnet_10', vid) vid = create_vlan_interface(dvs, "Vlan2002", "Ethernet12", "Vnet_20", "8.8.10.1/24") - vnet_obj.check_router_interface(dvs, 'Vnet_20', vid) + vnet_obj.check_router_interface(dvs, "Vlan2002", 'Vnet_20', vid) vnet_obj.fetch_exist_entries(dvs) create_vnet_routes(dvs, "5.5.5.10/32", 'Vnet_10', '50.1.1.10') @@ -1217,10 +1239,10 @@ def test_vnet_orch_4(self, dvs, testlog): vnet_obj.check_vxlan_tunnel(dvs, tunnel_name, 'fd:2::32') vid = create_vlan_interface(dvs, "Vlan300", "Ethernet24", 'Vnet3001', "100.100.3.1/24") - vnet_obj.check_router_interface(dvs, 'Vnet3001', vid) + vnet_obj.check_router_interface(dvs, "Vlan300", 'Vnet3001', vid) vid = create_vlan_interface(dvs, "Vlan301", "Ethernet28", 'Vnet3001', "100.100.4.1/24") - vnet_obj.check_router_interface(dvs, 'Vnet3001', vid) + vnet_obj.check_router_interface(dvs, "Vlan301", 'Vnet3001', vid) create_vnet_routes(dvs, "100.100.1.1/32", 'Vnet3001', '2000:1000:2000:3000:4000:5000:6000:7000') vnet_obj.check_vnet_routes(dvs, 'Vnet3001', '2000:1000:2000:3000:4000:5000:6000:7000', tunnel_name) @@ -1242,7 +1264,7 @@ def test_vnet_orch_4(self, dvs, testlog): vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, 'Vnet3002', '3002') create_phy_interface(dvs, "Ethernet60", 'Vnet3002', "100.102.1.1/24") - vnet_obj.check_router_interface(dvs, 'Vnet3002') + vnet_obj.check_router_interface(dvs, "Ethernet60", 'Vnet3002') create_vnet_routes(dvs, "100.100.2.1/32", 'Vnet3002', 'fd:2::34', "00:12:34:56:78:9A") vnet_obj.check_vnet_routes(dvs, 'Vnet3002', 'fd:2::34', tunnel_name, "00:12:34:56:78:9A")