diff --git a/cfgmgr/vxlanmgr.cpp b/cfgmgr/vxlanmgr.cpp index 40d3362406..ca270f3162 100644 --- a/cfgmgr/vxlanmgr.cpp +++ b/cfgmgr/vxlanmgr.cpp @@ -19,10 +19,12 @@ using namespace swss; // Fields name #define VXLAN_TUNNEL "vxlan_tunnel" #define SOURCE_IP "src_ip" +#define DST_IP "dst_ip" #define VNI "vni" #define VNET "vnet" #define VXLAN "vxlan" #define VXLAN_IF "vxlan_if" +#define VLAN "vlan" #define VXLAN_NAME_PREFIX "Vxlan" #define VXLAN_IF_NAME_PREFIX "Brvxlan" @@ -386,6 +388,78 @@ bool VxlanMgr::doVxlanTunnelMapCreateTask(const KeyOpFieldsValuesTuple & t) m_appVxlanTunnelMapTable.set(vxlanTunnelMapName, kfvFieldsValues(t)); SWSS_LOG_NOTICE("Create vxlan tunnel map %s", vxlanTunnelMapName.c_str()); + + /*Create vxlan tunnel in Linux kernel, the foramt is vxlanTunnelName-vni, such as VTTNL0001-1000*/ + std::string vlan, vlan_id, vni_id, src_ip, dst_ip; + for (auto i : kfvFieldsValues(t)) + { + const std::string & field = fvField(i); + const std::string & value = fvValue(i); + if (field == VLAN) + { + vlan = value; + } + else if (field == VNI) + { + vni_id = value; + } + } + + const auto vlan_prefix = std::string("Vlan"); + const auto prefix_len = vlan_prefix.length(); + vlan_id = vlan.substr(prefix_len); + + size_t found = vxlanTunnelMapName.find(delimiter); + const auto vxlanTunnelName = vxlanTunnelMapName.substr(0, found); + + // If the vxlan tunnel has been created + auto it = m_vxlanTunnelCache.find(vxlanTunnelName); + if (it == m_vxlanTunnelCache.end()) + { + SWSS_LOG_DEBUG("Vxlan tunnel %s has not been created", vxlanTunnelName.c_str()); + // Suspend this message util the vxlan tunnel is created + return false; + } + + auto sourceIp = std::find_if( + it->second.begin(), + it->second.end(), + [](const FieldValueTuple & fvt){ return fvt.first == SOURCE_IP; }); + if (sourceIp == it->second.end()) + { + SWSS_LOG_DEBUG("Vxlan tunnel %s has no field src_ip", vxlanTunnelName.c_str()); + return true; + } + else + { + src_ip = sourceIp->second; + } + auto dstIp = std::find_if( + it->second.begin(), + it->second.end(), + [](const FieldValueTuple & fvt){ return fvt.first == DST_IP; }); + if (dstIp != it->second.end()) + { + dst_ip = dstIp->second; + } + + //ip link add type vxlan id local remote dstport 4789 + //ip link set master DOT1Q_BRIDGE_NAME + //bridge vlan add vid dev + //ip link set up + std::string vxlan_dev_name; + vxlan_dev_name = std::string("") + std::string(vxlanTunnelName) + "-" + std::string(vni_id); + const std::string cmds = std::string("") + + BASH_CMD + " -c \"" + + IP_CMD + " link add " + vxlan_dev_name + " type vxlan id " + std::string(vni_id) + + " local " + src_ip + (dstIp == it->second.end() ? "" : (" remote " + dst_ip)) + " dstport 4789 " + " && " + + IP_CMD + " link set " + vxlan_dev_name + " master Bridge " + " && " + + BRIDGE_CMD + " vlan add vid " + std::string(vlan_id) + " dev " + vxlan_dev_name + " && " + + IP_CMD + " link set " + vxlan_dev_name + " up " + "\""; + + std::string res; + EXEC_WITH_ERROR_THROW(cmds, res); + return true; } @@ -398,6 +472,16 @@ bool VxlanMgr::doVxlanTunnelMapDeleteTask(const KeyOpFieldsValuesTuple & t) m_appVxlanTunnelMapTable.del(vxlanTunnelMapName); SWSS_LOG_NOTICE("Delete vxlan tunnel map %s", vxlanTunnelMapName.c_str()); + + // ip link del dev {{VXLAN}} + size_t found = vxlanTunnelMapName.find(delimiter); + const auto vxlanTunnelName = vxlanTunnelMapName.substr(0, found); + std::string res; + const std::string cmd = std::string("") + + IP_CMD " link del dev " + + vxlanTunnelName; + EXECUTE(cmd, res); + return true; } diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index 7498eb7e05..4d0df9105b 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -10,12 +10,18 @@ #include "crmorch.h" #include "notifier.h" #include "sai_serialize.h" +#include "directory.h" +#include "vxlanorch.h" +#include "swssnet.h" + +using namespace swss; extern sai_fdb_api_t *sai_fdb_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; extern CrmOrch * gCrmOrch; +extern Directory gDirectory; const int fdborch_pri = 20; @@ -441,14 +447,6 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const { SWSS_LOG_ENTER(); - if (m_entries.count(entry) != 0) // we already have such entries - { - // FIXME: should we check that the entry are moving to another port? - // FIXME: should we check that the entry are changing its type? - SWSS_LOG_ERROR("FDB entry already exists. mac=%s bv_id=0x%lx", entry.mac.to_string().c_str(), entry.bv_id); - return true; - } - sai_fdb_entry_t fdb_entry; fdb_entry.switch_id = gSwitchId; @@ -456,22 +454,52 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const fdb_entry.bv_id = entry.bv_id; Port port; - /* Retry until port is created */ - if (!m_portsOrch->getPort(port_name, port)) + sai_object_id_t bridge_port_id = SAI_NULL_OBJECT_ID; + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + VxlanTunnel *tunnel_obj = NULL; + if(strncmp(port_name.c_str(),VXLAN_TUNNEL_PREFIX,strlen(VXLAN_TUNNEL_PREFIX)) == 0) { - SWSS_LOG_DEBUG("Saving a fdb entry until port %s becomes active", port_name.c_str()); - saved_fdb_entries[port_name].push_back({entry, type}); + /* Retry until tunnel is created */ + if (!tunnel_orch->isTunnelExists(port_name)) + { + SWSS_LOG_WARN("Vxlan tunnel '%s' doesn't exist", port_name.c_str()); + return true; + } - return true; - } + tunnel_obj = tunnel_orch->getVxlanTunnel(port_name); + + bridge_port_id = tunnel_obj->getBridgePortId(); - /* Retry until port is added to the VLAN */ - if (!port.m_bridge_port_id) + /* Retry until tunnel bridge port is created */ + if (!bridge_port_id) + { + SWSS_LOG_WARN("Vxlan tunnel '%s' bridge port doesn't exist", port_name.c_str()); + return true; + } + else + { + SWSS_LOG_NOTICE("Get tunnel bridge port id is %lu", bridge_port_id); + } + } + else { - SWSS_LOG_DEBUG("Saving a fdb entry until port %s has got a bridge port ID", port_name.c_str()); - saved_fdb_entries[port_name].push_back({entry, type}); + /* Retry until port is created */ + if (!m_portsOrch->getPort(port_name, port)) + { + SWSS_LOG_DEBUG("Saving a fdb entry until port %s becomes active", port_name.c_str()); + saved_fdb_entries[port_name].push_back({entry, type}); - return true; + return true; + } + + /* Retry until port is added to the VLAN */ + if (!port.m_bridge_port_id) + { + SWSS_LOG_DEBUG("Saving a fdb entry until port %s has got a bridge port ID", port_name.c_str()); + saved_fdb_entries[port_name].push_back({entry, type}); + + return true; + } } sai_attribute_t attr; @@ -482,13 +510,36 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const attrs.push_back(attr); attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; - attr.value.oid = port.m_bridge_port_id; - attrs.push_back(attr); + if(strncmp(port_name.c_str(),VXLAN_TUNNEL_PREFIX,strlen(VXLAN_TUNNEL_PREFIX)) != 0) + { + attr.value.oid = port.m_bridge_port_id; + attrs.push_back(attr); + } + else + { + attr.value.oid = bridge_port_id; + attrs.push_back(attr); + + if(tunnel_obj) + { + attr.id = SAI_FDB_ENTRY_ATTR_ENDPOINT_IP; + swss::copy(attr.value.ipaddr, tunnel_obj->getDstIp()); + attrs.push_back(attr); + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET, &attr.value.ipaddr.addr.ip4, buf, INET_ADDRSTRLEN); + SWSS_LOG_NOTICE("Create tunnel SAI_FDB_ENTRY_ATTR_ENDPOINT_IP %s, family %d", buf, attr.value.ipaddr.addr_family); + } + } attr.id = SAI_FDB_ENTRY_ATTR_PACKET_ACTION; attr.value.s32 = SAI_PACKET_ACTION_FORWARD; attrs.push_back(attr); + if (m_entries.count(entry) != 0) // we already have such entries + { + removeFdbEntry(entry); + } + sai_status_t status = sai_fdb_api->create_fdb_entry(&fdb_entry, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { @@ -503,10 +554,14 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); - FdbUpdate update = {entry, port, true}; - for (auto observer: m_observers) + /*TBD: Vxlan tunnel is not in portlist, here skip observer, refine later*/ + if(strncmp(port_name.c_str(),VXLAN_TUNNEL_PREFIX,strlen(VXLAN_TUNNEL_PREFIX)) != 0) { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + FdbUpdate update = {entry, port, true}; + for (auto observer: m_observers) + { + observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + } } return true; @@ -541,12 +596,18 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry) gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); Port port; - m_portsOrch->getPortByBridgePortId(entry.bv_id, port); + bool res = false; - FdbUpdate update = {entry, port, false}; - for (auto observer: m_observers) + res = m_portsOrch->getPortByBridgePortId(entry.bv_id, port); + + /*TBD: Vxlan tunnel is not in portlist, here skip observer, refine later*/ + if(res == true) { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + FdbUpdate update = {entry, port, false}; + for (auto observer: m_observers) + { + observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + } } return true; diff --git a/orchagent/vnetorch.cpp b/orchagent/vnetorch.cpp index b922738307..1e78afe07c 100644 --- a/orchagent/vnetorch.cpp +++ b/orchagent/vnetorch.cpp @@ -470,36 +470,7 @@ VnetBridgeInfo VNetBitmapObject::getBridgeInfoByVni(uint32_t vni, string tunnelN tunnel->createTunnel(MAP_T::BRIDGE_TO_VNI, MAP_T::VNI_TO_BRIDGE); } - attr.id = SAI_BRIDGE_PORT_ATTR_TYPE; - attr.value.s32 = SAI_BRIDGE_PORT_TYPE_TUNNEL; - bpt_attrs.push_back(attr); - - attr.id = SAI_BRIDGE_PORT_ATTR_BRIDGE_ID; - attr.value.oid = info.bridge_id; - bpt_attrs.push_back(attr); - - attr.id = SAI_BRIDGE_PORT_ATTR_ADMIN_STATE; - attr.value.booldata = true; - bpt_attrs.push_back(attr); - - attr.id = SAI_BRIDGE_PORT_ATTR_TUNNEL_ID; - attr.value.oid = tunnel->getTunnelId(); - bpt_attrs.push_back(attr); - - attr.id = SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE; - attr.value.s32 = SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE; - bpt_attrs.push_back(attr); - - status = sai_bridge_api->create_bridge_port( - &info.bridge_port_tunnel_id, - gSwitchId, - (uint32_t)bpt_attrs.size(), - bpt_attrs.data()); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to create tunnel bridge port for vni %u", vni); - throw std::runtime_error("vni creation failed"); - } + info.bridge_port_tunnel_id = tunnel->getBridgePortId(); // FIXME: Use "createVxlanTunnelMap()" for tunnel mapper creation auto tunnelEncapMapperEntry = tunnel->addEncapMapperEntry(info.bridge_id, vni); @@ -534,13 +505,6 @@ bool VNetBitmapObject::clearBridgeInfoByVni(uint32_t vni, string tunnelName) sai_status_t status; - status = sai_bridge_api->remove_bridge_port(bridgeInfo.bridge_port_tunnel_id); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to remove tunnel bridge port for VNI %u, SAI rc: %d", vni, status); - return false; - } - status = sai_bridge_api->remove_bridge_port(bridgeInfo.bridge_port_rif_id); if (status != SAI_STATUS_SUCCESS) { diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index a27603e008..005a88e240 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -14,6 +14,11 @@ #include "vxlanorch.h" #include "directory.h" #include "swssnet.h" +#include "exec.h" +#include "../cfgmgr/shellcmd.h" +#include "sai_serialize.h" + +using namespace swss; /* Global variables */ extern sai_object_id_t gSwitchId; @@ -23,6 +28,8 @@ extern sai_next_hop_api_t *sai_next_hop_api; extern Directory gDirectory; extern PortsOrch* gPortsOrch; extern sai_object_id_t gUnderlayIfId; +extern sai_bridge_api_t *sai_bridge_api; +extern sai_switch_api_t *sai_switch_api; const map vxlanTunnelMap = { @@ -348,8 +355,63 @@ create_tunnel_termination( return term_table_id; } -void -remove_tunnel_termination(sai_object_id_t term_table_id) +static sai_object_id_t create_tunnel_bridge_port(sai_object_id_t tunnel_oid) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + vector attrs; + + vector attrs_bridge; + sai_object_id_t default1QBridge; + sai_status_t status; + + attr.id = SAI_SWITCH_ATTR_DEFAULT_1Q_BRIDGE_ID; + attrs_bridge.push_back(attr); + + status = sai_switch_api->get_switch_attribute(gSwitchId, (uint32_t)attrs_bridge.size(), attrs_bridge.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get default 1Q bridge, rv:%d", status); + throw "VxlanOrch initialization failure"; + } + + default1QBridge = attrs_bridge[0].value.oid; + + attr.id = SAI_BRIDGE_PORT_ATTR_TYPE; + attr.value.s32 = SAI_BRIDGE_PORT_TYPE_TUNNEL; + attrs.push_back(attr); + + attr.id = SAI_BRIDGE_PORT_ATTR_TUNNEL_ID; + attr.value.oid = tunnel_oid; + attrs.push_back(attr); + + attr.id = SAI_BRIDGE_PORT_ATTR_BRIDGE_ID; + attr.value.oid = default1QBridge; + attrs.push_back(attr); + + /* Create a bridge port with admin status set to UP */ + attr.id = SAI_BRIDGE_PORT_ATTR_ADMIN_STATE; + attr.value.booldata = true; + attrs.push_back(attr); + + attr.id = SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE; + attr.value.s32 = SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE; + attrs.push_back(attr); + + sai_object_id_t m_bridge_port_id; + status = sai_bridge_api->create_bridge_port(&m_bridge_port_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Failed to add tunnel bridge port"); + } + + SWSS_LOG_NOTICE("Add tunnel bridge port to default 1Q bridge, bridge port id is %lu", m_bridge_port_id); + + return m_bridge_port_id; +} + +void remove_tunnel_termination(sai_object_id_t term_table_id) { sai_status_t status = sai_tunnel_api->remove_tunnel_term_table_entry(term_table_id); if (status != SAI_STATUS_SUCCESS) @@ -380,6 +442,15 @@ bool VxlanTunnel::createTunnel(MAP_T encap, MAP_T decap) ids_.tunnel_id = create_tunnel(ids_.tunnel_encap_id, ids_.tunnel_decap_id, ip, gUnderlayIfId); + shared_ptr m_counter_db = shared_ptr(new DBConnector(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)); + unique_ptr m_counterTable = unique_ptr
(new Table(m_counter_db.get(), COUNTERS_PORT_NAME_MAP)); + + /* Add tunnel name map to counter table */ + FieldValueTuple tuple(tunnel_name_, sai_serialize_object_id(ids_.tunnel_id)); + vector fields; + fields.push_back(tuple); + m_counterTable->set("", fields); + ip = nullptr; if (!dst_ip_.isZero()) { @@ -390,6 +461,10 @@ bool VxlanTunnel::createTunnel(MAP_T encap, MAP_T decap) ids_.tunnel_term_id = create_tunnel_termination(ids_.tunnel_id, ips, ip, gVirtualRouterId); active_ = true; tunnel_map_ = { encap, decap }; + + /*Create bridge port of vxlan tunnel*/ + ids_.tunnel_bridge_port_id = create_tunnel_bridge_port(ids_.tunnel_id); + SWSS_LOG_NOTICE("Create tunnel bridge port id is %lu", ids_.tunnel_bridge_port_id); } catch (const std::runtime_error& error) { diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index 99387a3d56..d3b0a68db2 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -25,6 +25,7 @@ struct tunnel_ids_t sai_object_id_t tunnel_decap_id; sai_object_id_t tunnel_id; sai_object_id_t tunnel_term_id; + sai_object_id_t tunnel_bridge_port_id; }; struct nh_key_t @@ -104,12 +105,26 @@ class VxlanTunnel return ids_.tunnel_encap_id; } + sai_object_id_t getBridgePortId() const + { + return ids_.tunnel_bridge_port_id; + } + + IpAddress getSrcIp() const + { + return src_ip_; + } + + IpAddress getDstIp() const + { + return dst_ip_; + } + sai_object_id_t getTunnelTermId() const { return ids_.tunnel_term_id; } - void updateNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni, sai_object_id_t nhId); bool removeNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni); sai_object_id_t getNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) const; diff --git a/tests/test_vxlan_tunnel.py b/tests/test_vxlan_tunnel.py index c1306b8966..99b61d4234 100644 --- a/tests/test_vxlan_tunnel.py +++ b/tests/test_vxlan_tunnel.py @@ -101,12 +101,33 @@ def create_vlan(dvs, vlan_name, vlan_ids): return +def get_default_1q_bridge_id(db): + table = 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE' + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert len(keys) == 1, "Wrong number of virtual routers found" + + return keys[0] + +def get_tunnel_bridge_port_id(db, tunnel_id): + table = 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT' + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + + for key in keys: + (status, fvs) = tbl.get(key) + for fv in fvs: + if fv[0] == "SAI_BRIDGE_PORT_ATTR_TUNNEL_ID": + if fv[1] == tunnel_id: + return key + return None def check_vxlan_tunnel(dvs, src_ip, dst_ip, tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, lo_id): asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) tunnel_map_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP", tunnel_map_ids) tunnel_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL", tunnel_ids) tunnel_term_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY", tunnel_term_ids) + tunnel_bridge_port_id = get_tunnel_bridge_port_id(asic_db, tunnel_id) # check that the vxlan tunnel termination are there assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") == (len(tunnel_map_ids) + 1), "The TUNNEL_MAP wasn't created" @@ -144,6 +165,18 @@ def check_vxlan_tunnel(dvs, src_ip, dst_ip, tunnel_map_ids, tunnel_map_entry_ids check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY", tunnel_term_id, expected_attributes) + # check that the vxlan tunnel bridge port is there + assert tunnel_bridge_port_id is not None, "Vxlan tunnel bridage port is Null" + + default_1q_bridge_id = get_default_1q_bridge_id(asic_db) + expected_attributes = { + 'SAI_BRIDGE_PORT_ATTR_TYPE': 'SAI_BRIDGE_PORT_TYPE_TUNNEL', + 'SAI_BRIDGE_PORT_ATTR_TUNNEL_ID': tunnel_id, + 'SAI_BRIDGE_PORT_ATTR_BRIDGE_ID': default_1q_bridge_id, + 'SAI_BRIDGE_PORT_ATTR_ADMIN_STATE': 'true', + } + check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT", tunnel_bridge_port_id, expected_attributes) + tunnel_map_ids.add(tunnel_map_id) tunnel_ids.add(tunnel_id) tunnel_term_ids.add(tunnel_term_id)