diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index fb14df97da8..e1b008df83b 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -13,6 +13,7 @@ using namespace swss; #define VLAN_PREFIX "Vlan" #define LAG_PREFIX "PortChannel" +#define VNET_PREFIX "Vnet" IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : Orch(cfgDb, tableNames), @@ -21,6 +22,7 @@ IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c m_statePortTable(stateDb, STATE_PORT_TABLE_NAME), m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME), m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME), + m_stateVrfTable(stateDb, STATE_VRF_TABLE_NAME), m_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME) { } @@ -63,6 +65,14 @@ bool IntfMgr::isIntfStateOk(const string &alias) return true; } } + else if (!alias.compare(0, strlen(VNET_PREFIX), VNET_PREFIX)) + { + if (m_stateVrfTable.get(alias, temp)) + { + SWSS_LOG_DEBUG("Vnet %s is ready", alias.c_str()); + return true; + } + } else if (m_statePortTable.get(alias, temp)) { SWSS_LOG_DEBUG("Port %s is ready", alias.c_str()); @@ -89,6 +99,19 @@ void IntfMgr::doTask(Consumer &consumer) continue; } + const vector& data = kfvFieldsValues(t); + string vnet_name = ""; + + for (auto idx : data) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + if (field == "vnet_name") + { + vnet_name = value; + } + } + string alias(keys[0]); IpPrefix ip_prefix(keys[1]); @@ -107,6 +130,14 @@ void IntfMgr::doTask(Consumer &consumer) it++; continue; } + + if (!vnet_name.empty() && !isIntfStateOk(vnet_name)) + { + SWSS_LOG_DEBUG("VRF is not ready, skipping %s", kfvKey(t).c_str()); + it++; + continue; + } + setIntfIp(alias, "add", ip_prefix.to_string(), ip_prefix.isV4()); } else if (op == DEL_COMMAND) diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index 3d79d6d4779..9dc355d2b95 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -19,7 +19,7 @@ class IntfMgr : public Orch private: ProducerStateTable m_appIntfTableProducer; Table m_cfgIntfTable, m_cfgVlanIntfTable; - Table m_statePortTable, m_stateLagTable, m_stateVlanTable; + Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateVrfTable; bool setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr, const bool ipv4 = true); void doTask(Consumer &consumer); diff --git a/cfgmgr/intfmgrd.cpp b/cfgmgr/intfmgrd.cpp index 8d7c2ef2fc3..3b16e32199a 100644 --- a/cfgmgr/intfmgrd.cpp +++ b/cfgmgr/intfmgrd.cpp @@ -42,6 +42,7 @@ int main(int argc, char **argv) { vector cfg_intf_tables = { CFG_INTF_TABLE_NAME, + CFG_VNET_INTF_TABLE_NAME, CFG_LAG_INTF_TABLE_NAME, CFG_VLAN_INTF_TABLE_NAME, }; diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index cab07f62ae0..6269d21f7da 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -27,8 +27,8 @@ extern BufferOrch *gBufferOrch; const int intfsorch_pri = 35; -IntfsOrch::IntfsOrch(DBConnector *db, string tableName) : - Orch(db, tableName, intfsorch_pri) +IntfsOrch::IntfsOrch(DBConnector *db, string tableName, VRFOrch *vrf_orch) : + Orch(db, tableName, intfsorch_pri), m_vrfOrch(vrf_orch) { SWSS_LOG_ENTER(); } @@ -114,6 +114,18 @@ void IntfsOrch::doTask(Consumer &consumer) vector keys = tokenize(kfvKey(t), ':'); string alias(keys[0]); IpPrefix ip_prefix(kfvKey(t).substr(kfvKey(t).find(':')+1)); + const vector& data = kfvFieldsValues(t); + string vrf_name = ""; + + for (auto idx : data) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + if (field == "vrf_name") + { + vrf_name = value; + } + } if (alias == "eth0" || alias == "docker0") { @@ -126,7 +138,7 @@ void IntfsOrch::doTask(Consumer &consumer) { if (alias == "lo") { - addIp2MeRoute(ip_prefix); + addIp2MeRoute(vrf_name, ip_prefix); it = consumer.m_toSync.erase(it); continue; } @@ -149,7 +161,7 @@ void IntfsOrch::doTask(Consumer &consumer) auto it_intfs = m_syncdIntfses.find(alias); if (it_intfs == m_syncdIntfses.end()) { - if (addRouterIntfs(port)) + if (addRouterIntfs(vrf_name, port)) { IntfsEntry intfs_entry; intfs_entry.ref_count = 0; @@ -198,7 +210,7 @@ void IntfsOrch::doTask(Consumer &consumer) } addSubnetRoute(port, ip_prefix); - addIp2MeRoute(ip_prefix); + addIp2MeRoute(vrf_name, ip_prefix); if (port.m_type == Port::VLAN && ip_prefix.isV4()) { @@ -212,7 +224,7 @@ void IntfsOrch::doTask(Consumer &consumer) { if (alias == "lo") { - removeIp2MeRoute(ip_prefix); + removeIp2MeRoute(vrf_name, ip_prefix); it = consumer.m_toSync.erase(it); continue; } @@ -230,7 +242,7 @@ void IntfsOrch::doTask(Consumer &consumer) if (m_syncdIntfses[alias].ip_addresses.count(ip_prefix)) { removeSubnetRoute(port, ip_prefix); - removeIp2MeRoute(ip_prefix); + removeIp2MeRoute(vrf_name, ip_prefix); if(port.m_type == Port::VLAN && ip_prefix.isV4()) { removeDirectedBroadcast(port, ip_prefix.getBroadcastIp()); @@ -262,7 +274,7 @@ void IntfsOrch::doTask(Consumer &consumer) } } -bool IntfsOrch::addRouterIntfs(Port &port) +bool IntfsOrch::addRouterIntfs(string& vrf_name, Port &port) { SWSS_LOG_ENTER(); @@ -274,12 +286,14 @@ bool IntfsOrch::addRouterIntfs(Port &port) return true; } + sai_object_id_t vr_id = m_vrfOrch->getVRFid(vrf_name); + /* Create router interface if the router interface doesn't exist */ sai_attribute_t attr; vector attrs; attr.id = SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID; - attr.value.oid = gVirtualRouterId; + attr.value.oid = vr_id; attrs.push_back(attr); attr.id = SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS; @@ -334,6 +348,8 @@ bool IntfsOrch::addRouterIntfs(Port &port) throw runtime_error("Failed to create router interface."); } + port.m_vr_id = vr_id; + gPortsOrch->setPort(port.m_alias, port); SWSS_LOG_NOTICE("Create router interface %s MTU %u", port.m_alias.c_str(), port.m_mtu); @@ -370,7 +386,7 @@ void IntfsOrch::addSubnetRoute(const Port &port, const IpPrefix &ip_prefix) { sai_route_entry_t unicast_route_entry; unicast_route_entry.switch_id = gSwitchId; - unicast_route_entry.vr_id = gVirtualRouterId; + unicast_route_entry.vr_id = port.m_vr_id; copy(unicast_route_entry.destination, ip_prefix); subnet(unicast_route_entry.destination, unicast_route_entry.destination); @@ -413,7 +429,7 @@ void IntfsOrch::removeSubnetRoute(const Port &port, const IpPrefix &ip_prefix) { sai_route_entry_t unicast_route_entry; unicast_route_entry.switch_id = gSwitchId; - unicast_route_entry.vr_id = gVirtualRouterId; + unicast_route_entry.vr_id = port.m_vr_id; copy(unicast_route_entry.destination, ip_prefix); subnet(unicast_route_entry.destination, unicast_route_entry.destination); @@ -441,11 +457,11 @@ void IntfsOrch::removeSubnetRoute(const Port &port, const IpPrefix &ip_prefix) gRouteOrch->notifyNextHopChangeObservers(ip_prefix, IpAddresses(), false); } -void IntfsOrch::addIp2MeRoute(const IpPrefix &ip_prefix) +void IntfsOrch::addIp2MeRoute(string &vrf_name, const IpPrefix &ip_prefix) { sai_route_entry_t unicast_route_entry; unicast_route_entry.switch_id = gSwitchId; - unicast_route_entry.vr_id = gVirtualRouterId; + unicast_route_entry.vr_id = m_vrfOrch->getVRFid(vrf_name);; copy(unicast_route_entry.destination, ip_prefix.getIp()); sai_attribute_t attr; @@ -481,11 +497,11 @@ void IntfsOrch::addIp2MeRoute(const IpPrefix &ip_prefix) } } -void IntfsOrch::removeIp2MeRoute(const IpPrefix &ip_prefix) +void IntfsOrch::removeIp2MeRoute(string &vrf_name, const IpPrefix &ip_prefix) { sai_route_entry_t unicast_route_entry; unicast_route_entry.switch_id = gSwitchId; - unicast_route_entry.vr_id = gVirtualRouterId; + unicast_route_entry.vr_id = m_vrfOrch->getVRFid(vrf_name); copy(unicast_route_entry.destination, ip_prefix.getIp()); sai_status_t status = sai_route_api->remove_route_entry(&unicast_route_entry); diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 424cb62a7b5..8595b912555 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -3,6 +3,7 @@ #include "orch.h" #include "portsorch.h" +#include "vrforch.h" #include "ipaddresses.h" #include "ipprefix.h" @@ -25,7 +26,7 @@ typedef map IntfsTable; class IntfsOrch : public Orch { public: - IntfsOrch(DBConnector *db, string tableName); + IntfsOrch(DBConnector *db, string tableName, VRFOrch *vrf_orch); sai_object_id_t getRouterIntfsId(const string&); @@ -35,19 +36,20 @@ class IntfsOrch : public Orch bool setRouterIntfsMtu(Port &port); std::set getSubnetRoutes(); private: + VRFOrch *m_vrfOrch; IntfsTable m_syncdIntfses; void doTask(Consumer &consumer); int getRouterIntfsRefCount(const string&); - bool addRouterIntfs(Port &port); + bool addRouterIntfs(string &vrf_name, Port &port); bool removeRouterIntfs(Port &port); void addSubnetRoute(const Port &port, const IpPrefix &ip_prefix); void removeSubnetRoute(const Port &port, const IpPrefix &ip_prefix); - void addIp2MeRoute(const IpPrefix &ip_prefix); - void removeIp2MeRoute(const IpPrefix &ip_prefix); + void addIp2MeRoute(string &vrf_name, const IpPrefix &ip_prefix); + void removeIp2MeRoute(string &vrf_name, const IpPrefix &ip_prefix); void addDirectedBroadcast(const Port &port, const IpAddress &ip_addr); void removeDirectedBroadcast(const Port &port, const IpAddress &ip_addr); diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index ee219178a2b..af83bae4a42 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -67,7 +67,10 @@ bool OrchDaemon::init() gCrmOrch = new CrmOrch(m_configDb, CFG_CRM_TABLE_NAME); gPortsOrch = new PortsOrch(m_applDb, ports_tables); gFdbOrch = new FdbOrch(m_applDb, APP_FDB_TABLE_NAME, gPortsOrch); - gIntfsOrch = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME); + VRFOrch *vrf_orch = new VRFOrch(m_applDb, APP_VRF_TABLE_NAME); + gDirectory.set(vrf_orch); + + gIntfsOrch = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME, vrf_orch); gNeighOrch = new NeighOrch(m_applDb, APP_NEIGH_TABLE_NAME, gIntfsOrch); gRouteOrch = new RouteOrch(m_applDb, APP_ROUTE_TABLE_NAME, gNeighOrch); CoppOrch *copp_orch = new CoppOrch(m_applDb, APP_COPP_TABLE_NAME); @@ -77,6 +80,8 @@ bool OrchDaemon::init() gDirectory.set(vxlan_tunnel_orch); VxlanTunnelMapOrch *vxlan_tunnel_map_orch = new VxlanTunnelMapOrch(m_configDb, CFG_VXLAN_TUNNEL_MAP_TABLE_NAME); gDirectory.set(vxlan_tunnel_map_orch); + VxlanVrfMapOrch *vxlan_vrf_orch = new VxlanVrfMapOrch(m_applDb, APP_VXLAN_VRF_TABLE_NAME); + gDirectory.set(vxlan_vrf_orch); vector qos_tables = { CFG_TC_TO_QUEUE_MAP_TABLE_NAME, @@ -104,7 +109,6 @@ bool OrchDaemon::init() TableConnector appDbMirrorSession(m_applDb, APP_MIRROR_SESSION_TABLE_NAME); TableConnector confDbMirrorSession(m_configDb, CFG_MIRROR_SESSION_TABLE_NAME); MirrorOrch *mirror_orch = new MirrorOrch(appDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch); - VRFOrch *vrf_orch = new VRFOrch(m_configDb, CFG_VRF_TABLE_NAME); TableConnector confDbAclTable(m_configDb, CFG_ACL_TABLE_NAME); TableConnector confDbAclRuleTable(m_configDb, CFG_ACL_RULE_TABLE_NAME); diff --git a/orchagent/port.h b/orchagent/port.h index f99aaaa0370..7f9110b6655 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -74,6 +74,7 @@ class Port sai_object_id_t m_bridge_port_id = 0; // TODO: port could have multiple bridge port IDs sai_vlan_id_t m_port_vlan_id = DEFAULT_PORT_VLAN_ID; // Port VLAN ID sai_object_id_t m_rif_id = 0; + sai_object_id_t m_vr_id = 0; sai_object_id_t m_hif_id = 0; sai_object_id_t m_lag_id = 0; sai_object_id_t m_lag_member_id = 0; diff --git a/orchagent/request_parser.cpp b/orchagent/request_parser.cpp index a7be4daf3c5..a0d4700e802 100644 --- a/orchagent/request_parser.cpp +++ b/orchagent/request_parser.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "sai.h" @@ -145,6 +144,9 @@ void Request::parseAttrs(const KeyOpFieldsValuesTuple& request) case REQ_T_UINT: attr_item_uint_[fvField(*i)] = parseUint(fvValue(*i)); break; + case REQ_T_SET: + attr_item_set_[fvField(*i)] = parseSet(fvValue(*i)); + break; default: throw std::logic_error(std::string("Not implemented attribute type parser for attribute:") + fvField(*i)); } @@ -207,6 +209,25 @@ IpAddress Request::parseIpAddress(const std::string& str) } } +set Request::parseSet(const std::string& str) +{ + try + { + set str_set; + string substr; + std::istringstream iss(str); + while (getline(iss, substr, ',')) + { + str_set.insert(str); + } + return str_set; + } + catch (std::invalid_argument& _) + { + throw std::invalid_argument(std::string("Invalid string set")); + } +} + uint64_t Request::parseUint(const std::string& str) { try diff --git a/orchagent/request_parser.h b/orchagent/request_parser.h index 1be08f39c91..30cc9f3ab0c 100644 --- a/orchagent/request_parser.h +++ b/orchagent/request_parser.h @@ -2,6 +2,8 @@ #define __REQUEST_PARSER_H #include "ipaddress.h" +#include +#include typedef enum _request_types_t { @@ -13,6 +15,7 @@ typedef enum _request_types_t REQ_T_IP, REQ_T_VLAN, REQ_T_UINT, + REQ_T_SET, } request_types_t; typedef struct _request_description @@ -112,6 +115,12 @@ class Request return attr_item_uint_.at(attr_name); } + const set& getAttrSet(const std::string& attr_name) const + { + assert(is_parsed_); + return attr_item_set_.at(attr_name); + } + protected: Request(const request_description_t& request_description, const char key_separator) : request_description_(request_description), @@ -131,6 +140,7 @@ class Request IpAddress parseIpAddress(const std::string& str); uint64_t parseUint(const std::string& str); uint16_t parseVlan(const std::string& str); + set parseSet(const std::string& str); sai_packet_action_t parsePacketAction(const std::string& str); @@ -154,6 +164,7 @@ class Request std::unordered_map attr_item_vlan_; std::unordered_map attr_item_ip_; std::unordered_map attr_item_uint_; + std::unordered_map> attr_item_set_; }; #endif // __REQUEST_PARSER_H diff --git a/orchagent/vrforch.cpp b/orchagent/vrforch.cpp index a67e6734bd7..0a0dccabf39 100644 --- a/orchagent/vrforch.cpp +++ b/orchagent/vrforch.cpp @@ -20,6 +20,8 @@ bool VRFOrch::addOperation(const Request& request) sai_attribute_t attr; vector attrs; + string vnet_name = ""; + set peer_list = {}; for (const auto& name: request.getAttrFieldNames()) { @@ -54,6 +56,14 @@ bool VRFOrch::addOperation(const Request& request) attr.id = SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION; attr.value.s32 = request.getAttrPacketAction("l3_mc_action"); } + else if (name == "vnet_name") + { + vnet_name = request.getAttrString("vnet_name"); + } + else if (name == "peer_list") + { + peer_list = request.getAttrSet("peer_list"); + } else { SWSS_LOG_ERROR("Logic error: Unknown attribute: %s", name.c_str()); @@ -79,6 +89,31 @@ bool VRFOrch::addOperation(const Request& request) } vrf_table_[vrf_name] = router_id; + + /* + * If VNET, then both ingress and egress VRF must be created + */ + + if (!vnet_name.empty()) + { + sai_object_id_t e_router_id; + sai_status_t status = sai_virtual_router_api->create_virtual_router(&e_router_id, + gSwitchId, + static_cast(attrs.size()), + attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create virtual router name: %s, rv: %d", vrf_name.c_str(), status); + return false; + } + + VNetObject_T vnet_obj(new VNetObject(vrf_name, router_id, e_router_id, peer_list)); + vnet_table_[vnet_name] = std::move(vnet_obj); + + SWSS_LOG_NOTICE("VRF '%s' was added to Vnet '%s'", vrf_name.c_str(), vnet_name.c_str()); + + } + SWSS_LOG_NOTICE("VRF '%s' was added", vrf_name.c_str()); } else @@ -97,6 +132,8 @@ bool VRFOrch::addOperation(const Request& request) } } + // FIXME - Handle VNET case + SWSS_LOG_NOTICE("VRF '%s' was updated", vrf_name.c_str()); } @@ -122,6 +159,21 @@ bool VRFOrch::delOperation(const Request& request) return false; } + if (isMapexists(vrf_name)) + { + auto vnet = getVnetName(vrf_name); + router_id = getVRFidEgress(vnet); + sai_status_t status = sai_virtual_router_api->remove_virtual_router(router_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove virtual router name: %s, rv:%d", vnet.c_str(), status); + return false; + } + + vnet_table_.erase(vnet); + map_table_.erase(vrf_name); + } + vrf_table_.erase(vrf_name); SWSS_LOG_NOTICE("VRF '%s' was removed", vrf_name.c_str()); diff --git a/orchagent/vrforch.h b/orchagent/vrforch.h index 49df85492ea..8eadc7b99f3 100644 --- a/orchagent/vrforch.h +++ b/orchagent/vrforch.h @@ -3,6 +3,7 @@ #include "request_parser.h" +extern sai_object_id_t gVirtualRouterId; typedef std::unordered_map VRFTable; const request_description_t request_description = { @@ -14,6 +15,9 @@ const request_description_t request_description = { { "ttl_action", REQ_T_PACKET_ACTION }, { "ip_opt_action", REQ_T_PACKET_ACTION }, { "l3_mc_action", REQ_T_PACKET_ACTION }, + { "vnet_name", REQ_T_STRING }, + { "fall_through", REQ_T_BOOL }, + { "peer_list", REQ_T_SET }, }, { } // no mandatory attributes }; @@ -21,9 +25,43 @@ const request_description_t request_description = { class VRFRequest : public Request { public: - VRFRequest() : Request(request_description, '|') { } + VRFRequest() : Request(request_description, ':') { } }; +class VNetObject +{ +public: + VNetObject(string vr_name, sai_object_id_t ivr_id, sai_object_id_t evr_id, set& list) + :vrf_name(vr_name), i_vrf_id(ivr_id),e_vrf_id(evr_id) + { + peer_list = list; + } + + sai_object_id_t getVRFidIngress() const + { + return i_vrf_id; + } + + sai_object_id_t getVRFidEgress() const + { + return e_vrf_id; + } + + string getVRFname() const + { + return vrf_name; + } + +private: + string vrf_name; + set peer_list = {}; + sai_object_id_t i_vrf_id; + sai_object_id_t e_vrf_id; +}; + +using VNetObject_T = std::unique_ptr; +typedef std::unordered_map VNetTable; +typedef std::unordered_map VRFMapTable; class VRFOrch : public Orch2 { @@ -39,13 +77,48 @@ class VRFOrch : public Orch2 sai_object_id_t getVRFid(const std::string& name) const { - return vrf_table_.at(name); + if (vrf_table_.find(name) != std::end(vrf_table_)) + { + return vrf_table_.at(name); + } + else + { + return gVirtualRouterId; + } } + + bool isVnetexists(const std::string& name) const + { + return vnet_table_.find(name) != std::end(vnet_table_); + } + + sai_object_id_t getVRFidIngress(const std::string& name) const + { + return vnet_table_.at(name)->getVRFidIngress(); + } + + sai_object_id_t getVRFidEgress(const std::string& name) const + { + return vnet_table_.at(name)->getVRFidEgress(); + } + + bool isMapexists(const std::string& name) const + { + return map_table_.find(name) != std::end(map_table_); + } + + string getVnetName(const std::string& vrf) const + { + return map_table_.at(vrf); + } + private: virtual bool addOperation(const Request& request); virtual bool delOperation(const Request& request); VRFTable vrf_table_; + VNetTable vnet_table_; + VRFMapTable map_table_; //VRF to Vnet Map VRFRequest request_; }; diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index a489c26f820..651cc052541 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -18,17 +18,36 @@ extern sai_object_id_t gSwitchId; extern sai_object_id_t gVirtualRouterId; extern sai_tunnel_api_t *sai_tunnel_api; +extern sai_next_hop_api_t *sai_next_hop_api; extern Directory gDirectory; extern PortsOrch* gPortsOrch; static sai_object_id_t -create_tunnel_map() +create_tunnel_map(map_type maptype) { sai_attribute_t attr; std::vector tunnel_map_attrs; attr.id = SAI_TUNNEL_MAP_ATTR_TYPE; - attr.value.s32 = SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID; + + switch (maptype) + { + case map_type::VNI_TO_VLAN_ID: + attr.value.s32 = SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID; + break; + case map_type::VLAN_ID_TO_VNI: + attr.value.s32 = SAI_TUNNEL_MAP_TYPE_VLAN_ID_TO_VNI; + break; + case map_type::VRID_TO_VNI: + attr.value.s32 = SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI; + break; + case map_type::VNI_TO_VRID: + attr.value.s32 = SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID; + break; + default: + SWSS_LOG_NOTICE("Invalid map type %d", maptype); + return 0; + } tunnel_map_attrs.push_back(attr); sai_object_id_t tunnel_map_id; @@ -46,6 +65,113 @@ create_tunnel_map() return tunnel_map_id; } +sai_object_id_t create_encap_tunnel_map_entry( + sai_object_id_t tunnel_map_id, + sai_object_id_t router_id, + sai_uint32_t vni) +{ + sai_attribute_t attr; + sai_object_id_t tunnel_map_entry_id; + std::vector tunnel_map_entry_attrs; + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE; + attr.value.s32 = SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI; + tunnel_map_entry_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP; + attr.value.oid = tunnel_map_id; + tunnel_map_entry_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY; + attr.value.oid = router_id; + tunnel_map_entry_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE; + attr.value.u32 = vni; + tunnel_map_entry_attrs.push_back(attr); + + sai_tunnel_api->create_tunnel_map_entry(&tunnel_map_entry_id, gSwitchId, + static_cast (tunnel_map_entry_attrs.size()), + tunnel_map_entry_attrs.data()); + + return tunnel_map_entry_id; +} + +sai_object_id_t create_decap_tunnel_map_entry( + sai_object_id_t tunnel_map_id, + sai_object_id_t router_id, + sai_uint32_t vni) +{ + sai_attribute_t attr; + sai_object_id_t tunnel_map_entry_id; + std::vector tunnel_map_entry_attrs; + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE; + attr.value.s32 = SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID; + tunnel_map_entry_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP; + attr.value.oid = tunnel_map_id; + tunnel_map_entry_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY; + attr.value.u32 = vni; + tunnel_map_entry_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE; + attr.value.oid = router_id; + tunnel_map_entry_attrs.push_back(attr); + + sai_tunnel_api->create_tunnel_map_entry(&tunnel_map_entry_id, gSwitchId, + static_cast (tunnel_map_entry_attrs.size()), + tunnel_map_entry_attrs.data()); + + return tunnel_map_entry_id; +} + +sai_status_t create_nexthop_tunnel( + sai_ip4_t host_ip, + sai_uint32_t vni, // optional vni + sai_mac_t mac, // inner destination mac + sai_object_id_t tunnel_id, + sai_object_id_t *next_hop_id) +{ + std::vector next_hop_attrs; + sai_attribute_t next_hop_attr; + + next_hop_attr.id = SAI_NEXT_HOP_ATTR_TYPE; + next_hop_attr.value.s32 = SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP; + next_hop_attrs.push_back(next_hop_attr); + + next_hop_attr.id = SAI_NEXT_HOP_ATTR_IP; + next_hop_attr.value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + next_hop_attr.value.ipaddr.addr.ip4 = htonl(host_ip); + next_hop_attrs.push_back(next_hop_attr); + + next_hop_attr.id = SAI_NEXT_HOP_ATTR_TUNNEL_ID; + next_hop_attr.value.oid = tunnel_id; + next_hop_attrs.push_back(next_hop_attr); + + if (vni != 0) + { + next_hop_attr.id = SAI_NEXT_HOP_ATTR_TUNNEL_VNI; + next_hop_attr.value.u32 = vni; + next_hop_attrs.push_back(next_hop_attr); + } + + if (mac != NULL) + { + next_hop_attr.id = SAI_NEXT_HOP_ATTR_TUNNEL_MAC; + memcpy(next_hop_attr.value.mac, mac, sizeof(sai_mac_t)); + next_hop_attrs.push_back(next_hop_attr); + } + + sai_status_t status = sai_next_hop_api->create_next_hop(next_hop_id, gSwitchId, + static_cast(next_hop_attrs.size()), + next_hop_attrs.data()); + return status; +} + static sai_object_id_t create_tunnel_map_entry( sai_object_id_t tunnel_map_id, @@ -88,7 +214,11 @@ create_tunnel_map_entry( // Create Tunnel static sai_object_id_t -create_tunnel(sai_object_id_t tunnel_map_id) +create_tunnel( + sai_object_id_t tunnel_encap_id, + sai_object_id_t tunnel_decap_id, + sai_ip4_t ip, + sai_object_id_t underlay_rif = 0x0) { sai_attribute_t attr; std::vector tunnel_attrs; @@ -97,17 +227,36 @@ create_tunnel(sai_object_id_t tunnel_map_id) attr.value.s32 = SAI_TUNNEL_TYPE_VXLAN; tunnel_attrs.push_back(attr); - sai_object_id_t decap_list[] = { tunnel_map_id }; + sai_object_id_t decap_list[] = { tunnel_decap_id }; attr.id = SAI_TUNNEL_ATTR_DECAP_MAPPERS; attr.value.objlist.count = 1; attr.value.objlist.list = decap_list; tunnel_attrs.push_back(attr); - sai_object_id_t encap_list[] = { tunnel_map_id }; - attr.id = SAI_TUNNEL_ATTR_ENCAP_MAPPERS; - attr.value.objlist.count = 1; - attr.value.objlist.list = encap_list; - tunnel_attrs.push_back(attr); + if (tunnel_encap_id != 0x0) + { + sai_object_id_t encap_list[] = { tunnel_encap_id }; + attr.id = SAI_TUNNEL_ATTR_ENCAP_MAPPERS; + attr.value.objlist.count = 1; + attr.value.objlist.list = encap_list; + tunnel_attrs.push_back(attr); + } + + if (underlay_rif != 0x0) + { + attr.id = SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE; + attr.value.oid = underlay_rif; + tunnel_attrs.push_back(attr); + } + + // source ip + if (ip != 0x0) + { + attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; + sai_ip_address_t ip_addr = {SAI_IP_ADDR_FAMILY_IPV4, 0x0a0a0a0a}; + attr.value.ipaddr = ip_addr; + tunnel_attrs.push_back(attr); + } sai_object_id_t tunnel_id; sai_status_t status = sai_tunnel_api->create_tunnel( @@ -186,6 +335,41 @@ create_tunnel_termination( return term_table_id; } +bool VxlanTunnel::createTunnel(map_type encap, map_type decap) +{ + try + { + ids.tunnel_decap_id = create_tunnel_map(decap); + ids.tunnel_encap_id = create_tunnel_map(encap); + ids.tunnel_id = create_tunnel(ids.tunnel_encap_id, ids.tunnel_decap_id, 0x0); + ids.tunnel_term_id = create_tunnel_termination(ids.tunnel_id, src_ip.getV4Addr(), + dst_ip.getV4Addr(), gVirtualRouterId); + active = true; + } + catch (const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error creating tunnel %s: %s", tunnel_name.c_str(), error.what()); + // FIXME: add code to remove already created objects + return false; + } + + SWSS_LOG_NOTICE("Vxlan tunnel '%s' was created", tunnel_name.c_str()); + + return true; +} + +sai_object_id_t VxlanTunnel::addEncapMapperEntry(sai_object_id_t vrf, uint32_t vni) +{ + const auto encap_id = getEncapMapId(tunnel_name); + return create_encap_tunnel_map_entry(encap_id, vrf, vni); +} + +sai_object_id_t VxlanTunnel::addDecapMapperEntry(sai_object_id_t vrf, uint32_t vni) +{ + const auto decap_id = getDecapMapId(tunnel_name); + return create_decap_tunnel_map_entry(decap_id, vrf, vni); +} + bool VxlanTunnelOrch::addOperation(const Request& request) { SWSS_LOG_ENTER(); @@ -221,23 +405,10 @@ bool VxlanTunnelOrch::addOperation(const Request& request) return true; } - tunnel_ids_t ids; - try - { - ids.tunnel_map_id = create_tunnel_map(); - ids.tunnel_id = create_tunnel(ids.tunnel_map_id); - ids.tunnel_term_id = create_tunnel_termination(ids.tunnel_id, src_ip.getV4Addr(), dst_ip.getV4Addr(), gVirtualRouterId); - } - catch (const std::runtime_error& error) - { - SWSS_LOG_ERROR("Error creating tunnel %s: %s", tunnel_name.c_str(), error.what()); - // FIXME: add code to remove already created objects - return false; - } + std::unique_ptr tunnel_obj (new VxlanTunnel(tunnel_name, src_ip, dst_ip)); + vxlan_tunnel_table_[tunnel_name] = std::move(tunnel_obj); - vxlan_tunnel_table_[tunnel_name] = ids; - - SWSS_LOG_NOTICE("Vxlan tunnel '%s' was created", tunnel_name.c_str()); + SWSS_LOG_NOTICE("Vxlan tunnel '%s' saved", tunnel_name.c_str()); return true; } @@ -278,6 +449,13 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request) return false; } + auto& tunnel_obj = tunnel_orch->getVxlanTunnel(tunnel_name); + if (!tunnel_obj->isActive()) + { + //@Todo, currently only decap mapper is allowed + tunnel_obj->createTunnel(map_type::MAP_TYPE_INVALID, map_type::VNI_TO_VLAN_ID); + } + const auto full_tunnel_map_entry_name = request.getFullKey(); if (isTunnelMapExists(full_tunnel_map_entry_name)) { @@ -285,7 +463,7 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request) return true; } - const auto tunnel_map_id = tunnel_orch->getTunnelMapId(tunnel_name); + const auto tunnel_map_id = tunnel_obj->getDecapMapId(tunnel_name); const auto tunnel_map_entry_name = request.getKeyString(1); try @@ -313,3 +491,92 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) return true; } + +bool VxlanVrfMapOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + auto tunnel_name = request.getKeyString(0); + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + if (!tunnel_orch->isTunnelExists(tunnel_name)) + { + SWSS_LOG_ERROR("Vxlan tunnel '%s' doesn't exist", tunnel_name.c_str()); + return false; + } + + auto vni_id = static_cast(request.getAttrUint("vni")); + if (vni_id >= 1<<24) + { + SWSS_LOG_ERROR("Vxlan vni id is too big: %d", vni_id); + return true; + } + + auto& tunnel_obj = tunnel_orch->getVxlanTunnel(tunnel_name); + if (!tunnel_obj->isActive()) + { + tunnel_obj->createTunnel(map_type::VRID_TO_VNI, map_type::VNI_TO_VRID); + } + + auto vrf_name = request.getAttrString("vrf"); + VRFOrch* vrf_orch = gDirectory.get(); + if (!vrf_orch->isVRFexists(vrf_name)) + { + SWSS_LOG_INFO("VRF not created for VNET %s", vrf_name.c_str()); + return false; + } + + const auto full_map_entry_name = request.getFullKey(); + if (isVrfMapExists(full_map_entry_name)) + { + SWSS_LOG_ERROR("Vxlan VRF map '%s' is already exist", full_map_entry_name.c_str()); + return true; + } + + const auto tunnel_map_entry_name = request.getKeyString(1); + sai_object_id_t ivrf, evrf; + if (!vrf_orch->isMapexists(vrf_name)) + { + ivrf = evrf = vrf_orch->getVRFid(vrf_name); + } + else + { + auto vnet_name = vrf_orch->getVnetName(vrf_name); + ivrf = vrf_orch->getVRFidEgress(vnet_name); + evrf = vrf_orch->getVRFidIngress(vnet_name); + } + + vrf_map_entry_t entry; + try + { + /* + * Create encap and decap mapper for each VRF per VNET entry + */ + entry.encap_id = tunnel_obj->addEncapMapperEntry(ivrf, vni_id); + entry.decap_id = tunnel_obj->addDecapMapperEntry(evrf, vni_id); + + SWSS_LOG_INFO("Vxlan tunnel encap entry '%lx' decap entry '0x%lx'", + entry.encap_id, entry.decap_id); + + vxlan_vrf_table_[full_map_entry_name] = entry; + } + catch(const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error adding tunnel map entry. Tunnel: %s. Entry: %s. Error: %s", + tunnel_name.c_str(), tunnel_map_entry_name.c_str(), error.what()); + return false; + } + + SWSS_LOG_NOTICE("Vxlan vrf map entry '%s' for tunnel '%s' was created", + tunnel_map_entry_name.c_str(), tunnel_name.c_str()); + + return true; +} + +bool VxlanVrfMapOrch::delOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_ERROR("DEL operation is not implemented"); + + return true; +} diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index 6eaac403f52..c5306ae9b92 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -1,8 +1,64 @@ #pragma once #include +#include +#include #include "request_parser.h" #include "portsorch.h" +#include "vrforch.h" + +enum class map_type +{ + MAP_TYPE_INVALID, + VNI_TO_VLAN_ID, + VLAN_ID_TO_VNI, + VRID_TO_VNI, + VNI_TO_VRID +}; + +struct tunnel_ids_t +{ + sai_object_id_t tunnel_encap_id; + sai_object_id_t tunnel_decap_id; + sai_object_id_t tunnel_id; + sai_object_id_t tunnel_term_id; +}; + +class VxlanTunnel +{ +public: + VxlanTunnel(string name, IpAddress src_ip, IpAddress dst_ip) + :tunnel_name(name), src_ip(src_ip), dst_ip(dst_ip) { } + + bool isActive() const + { + return active; + } + + bool createTunnel(map_type encap, map_type decap); + sai_object_id_t addEncapMapperEntry(sai_object_id_t vrf, uint32_t vni); + sai_object_id_t addDecapMapperEntry(sai_object_id_t vrf, uint32_t vni); + + sai_object_id_t getDecapMapId(const std::string& tunnel_name) const + { + return ids.tunnel_decap_id; + } + + sai_object_id_t getEncapMapId(const std::string& tunnel_name) const + { + return ids.tunnel_encap_id; + } + +private: + string tunnel_name; + + bool active = false; + + tunnel_ids_t ids = {0x0, 0x0, 0x0, 0x0}; + + IpAddress src_ip; + IpAddress dst_ip = 0x0; +}; const request_description_t vxlan_tunnel_request_description = { { REQ_T_STRING }, @@ -19,13 +75,8 @@ class VxlanTunnelRequest : public Request VxlanTunnelRequest() : Request(vxlan_tunnel_request_description, '|') { } }; -struct tunnel_ids_t -{ - sai_object_id_t tunnel_map_id; - sai_object_id_t tunnel_id; - sai_object_id_t tunnel_term_id; -}; -typedef std::map VxlanTunnelTable; +using VxlanTunnel_T = std::unique_ptr; +typedef std::map VxlanTunnelTable; class VxlanTunnelOrch : public Orch2 { @@ -37,9 +88,9 @@ class VxlanTunnelOrch : public Orch2 return vxlan_tunnel_table_.find(tunnel_name) != std::end(vxlan_tunnel_table_); } - sai_object_id_t getTunnelMapId(const std::string& tunnel_name) const + VxlanTunnel_T& getVxlanTunnel(const std::string& tunnel_name) { - return vxlan_tunnel_table_.at(tunnel_name).tunnel_map_id; + return vxlan_tunnel_table_.at(tunnel_name); } private: @@ -83,3 +134,43 @@ class VxlanTunnelMapOrch : public Orch2 VxlanTunnelMapTable vxlan_tunnel_map_table_; VxlanTunnelMapRequest request_; }; + +const request_description_t vxlan_vrf_request_description = { + { REQ_T_STRING, REQ_T_STRING }, + { + { "vni", REQ_T_UINT }, + { "vrf", REQ_T_STRING }, + }, + { "vni", "vrf" } +}; + +class VxlanVrfRequest : public Request +{ +public: + VxlanVrfRequest() : Request(vxlan_vrf_request_description, ':') { } +}; + +struct vrf_map_entry_t { + sai_object_id_t encap_id; + sai_object_id_t decap_id; +}; + +typedef std::map VxlanVrfTable; + +class VxlanVrfMapOrch : public Orch2 +{ +public: + VxlanVrfMapOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } + + bool isVrfMapExists(const std::string& name) const + { + return vxlan_vrf_table_.find(name) != std::end(vxlan_vrf_table_); + } + +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + VxlanVrfTable vxlan_vrf_table_; + VxlanVrfRequest request_; +};