diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 4f8c93ce5f..6b5516fcc8 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -272,6 +272,34 @@ void IntfMgr::removeSubIntfState(const string &alias) } } +bool IntfMgr::setIntfProxyArp(const string &alias, const string &vrf_name, const string &proxy_arp) +{ + stringstream cmd; + string res; + string proxy_arp_pvlan; + + if (proxy_arp == "enabled" || !vrf_name.compare(0, strlen(VNET_PREFIX), VNET_PREFIX)) + { + proxy_arp_pvlan = "1"; + } + else if (proxy_arp == "disabled" && vrf_name.compare(0, strlen(VNET_PREFIX), VNET_PREFIX)) + { + 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; @@ -346,6 +374,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); @@ -363,6 +393,10 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { adminStatus = value; } + else if (field == "proxy_arp") + { + proxy_arp = value; + } if (field == "nat_zone") { @@ -420,7 +454,22 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, FieldValueTuple fvTuple("mac_addr", MacAddress().to_string()); data.push_back(fvTuple); } - + + if (!proxy_arp.empty()) + { + if (!setIntfProxyArp(alias, vrf_name, 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 0f4cc7b58a..46b07e5d36 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -46,6 +46,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 &vrf_name, const std::string &proxy_arp); }; } diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 0faf0ddb3c..b96995a8d3 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,81 @@ bool IntfsOrch::setRouterIntfsAdminStatus(const Port &port) return true; } +bool IntfsOrch::setIntfVlanFloodType(const Port &port) +{ + 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 = port.m_vlan_info.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 (proxy_arp != "enabled" || proxy_arp != "disabled") + { + SWSS_LOG_ERROR("Proxy ARP state is invalid: \"%s\"", proxy_arp.c_str()); + return false; + } + + 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) + { + if (proxy_arp == "enabled") + { + port.m_vlan_info.vlan_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_NONE; + } + else + { + port.m_vlan_info.vlan_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + } + + if (!setIntfVlanFloodType(port)) + { + return false; + } + } + + m_syncdIntfses[alias].proxy_arp = proxy_arp == "enabled" ? true : false; + + return true; +} + set IntfsOrch:: getSubnetRoutes() { SWSS_LOG_ENTER(); @@ -440,6 +516,7 @@ void IntfsOrch::doTask(Consumer &consumer) uint32_t mtu; bool adminUp; uint32_t nat_zone_id = 0; + string proxy_arp = ""; for (auto idx : data) { @@ -515,6 +592,10 @@ void IntfsOrch::doTask(Consumer &consumer) { nat_zone = value; } + else if (field == "proxy_arp") + { + proxy_arp = value; + } } if (alias == "eth0" || alias == "docker0") @@ -666,6 +747,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) @@ -724,6 +810,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 8209b5cfa1..7585079b45 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; @@ -87,6 +88,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); + bool setIntfProxyArp(const string &alias, const string &proxy_arp); }; #endif /* SWSS_INTFSORCH_H */ diff --git a/orchagent/port.h b/orchagent/port.h index 74f805ddec..7ba44685fe 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -32,8 +32,9 @@ typedef std::map vlan_members_t; struct VlanInfo { - sai_object_id_t vlan_oid = 0; - sai_vlan_id_t vlan_id = 0; + sai_object_id_t vlan_oid = 0; + sai_vlan_id_t vlan_id = 0; + sai_vlan_flood_control_type_t vlan_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; }; class Port diff --git a/tests/test_vnet.py b/tests/test_vnet.py index cc91ab7092..772e965218 100644 --- a/tests/test_vnet.py +++ b/tests/test_vnet.py @@ -393,6 +393,11 @@ def get_switch_mac(dvs): return mac +def check_linux_intf_arp_proxy(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 @@ -563,7 +568,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 @@ -577,6 +582,7 @@ def check_router_interface(self, dvs, name, vlan_oid=0): if vlan_oid: expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_VLAN'}) expected_attr.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_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_PORT'}) @@ -589,6 +595,9 @@ def check_router_interface(self, dvs, name, vlan_oid=0): self.rifs.add(new_rif) self.routes.update(new_route) + check_linux_intf_arp_proxy(intf_name) + + def check_del_router_interface(self, dvs, name): asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) @@ -853,6 +862,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'}) @@ -968,10 +978,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') @@ -991,7 +1001,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") @@ -1051,7 +1061,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') @@ -1078,7 +1088,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') @@ -1154,10 +1164,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') @@ -1218,10 +1228,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) @@ -1243,7 +1253,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")