diff --git a/orchagent/nexthopgroupkey.h b/orchagent/nexthopgroupkey.h index d012cbe41a..0a61af6caa 100644 --- a/orchagent/nexthopgroupkey.h +++ b/orchagent/nexthopgroupkey.h @@ -13,6 +13,7 @@ class NextHopGroupKey { m_overlay_nexthops = false; m_srv6_nexthops = false; + m_srv6_vpn = false; auto nhv = tokenize(nexthops, NHG_DELIMITER); for (const auto &nh : nhv) { @@ -27,6 +28,7 @@ class NextHopGroupKey { m_overlay_nexthops = true; m_srv6_nexthops = false; + m_srv6_vpn = false; auto nhv = tokenize(nexthops, NHG_DELIMITER); for (const auto &nh_str : nhv) { @@ -38,11 +40,51 @@ class NextHopGroupKey { m_overlay_nexthops = false; m_srv6_nexthops = true; + m_srv6_vpn = false; auto nhv = tokenize(nexthops, NHG_DELIMITER); for (const auto &nh_str : nhv) { auto nh = NextHopKey(nh_str, overlay_nh, srv6_nh); m_nexthops.insert(nh); + if (nh.isSrv6Vpn()) + { + m_srv6_vpn = true; + } + } + } + } + + NextHopGroupKey(const std::string &nexthops, bool overlay_nh, bool srv6_nh, const std::string& weights) + { + auto nhv = tokenize(nexthops, NHG_DELIMITER); + auto wtv = tokenize(weights, NHG_DELIMITER); + bool set_weight = wtv.size() == nhv.size(); + if (overlay_nh) + { + m_overlay_nexthops = true; + m_srv6_nexthops = false; + m_srv6_vpn = false; + for (uint32_t i = 0; i < nhv.size(); ++i) + { + auto nh = NextHopKey(nhv[i], overlay_nh, srv6_nh); + nh.weight = set_weight ? (uint32_t)std::stoi(wtv[i]) : 0; + m_nexthops.insert(nh); + } + } + else if (srv6_nh) + { + m_overlay_nexthops = false; + m_srv6_nexthops = true; + m_srv6_vpn = false; + for (uint32_t i = 0; i < nhv.size(); ++i) + { + auto nh = NextHopKey(nhv[i], overlay_nh, srv6_nh); + nh.weight = set_weight ? (uint32_t)std::stoi(wtv[i]) : 0; + m_nexthops.insert(nh); + if (nh.isSrv6Vpn()) + { + m_srv6_vpn = true; + } } } } @@ -51,6 +93,7 @@ class NextHopGroupKey { m_overlay_nexthops = false; m_srv6_nexthops = false; + m_srv6_vpn = false; std::vector nhv = tokenize(nexthops, NHG_DELIMITER); std::vector wtv = tokenize(weights, NHG_DELIMITER); bool set_weight = wtv.size() == nhv.size(); @@ -62,6 +105,16 @@ class NextHopGroupKey } } + inline bool is_srv6_nexthop() const + { + return m_srv6_nexthops; + } + + inline bool is_srv6_vpn() const + { + return m_srv6_vpn; + } + inline const std::set &getNextHops() const { return m_nexthops; @@ -216,11 +269,6 @@ class NextHopGroupKey return m_overlay_nexthops; } - inline bool is_srv6_nexthop() const - { - return m_srv6_nexthops; - } - void clear() { m_nexthops.clear(); @@ -230,6 +278,7 @@ class NextHopGroupKey std::set m_nexthops; bool m_overlay_nexthops; bool m_srv6_nexthops; + bool m_srv6_vpn; }; #endif /* SWSS_NEXTHOPGROUPKEY_H */ diff --git a/orchagent/nexthopkey.h b/orchagent/nexthopkey.h index 2f03e9fd49..9c3e8e23ef 100644 --- a/orchagent/nexthopkey.h +++ b/orchagent/nexthopkey.h @@ -22,18 +22,34 @@ struct NextHopKey uint32_t weight; // NH weight for NHGs string srv6_segment; // SRV6 segment string string srv6_source; // SRV6 source address - - NextHopKey() : weight(0) {} + string srv6_vpn_sid; // SRV6 vpn sid + + NextHopKey() : + weight(0), + srv6_vpn_sid(""), + srv6_source(""), + srv6_segment("") + {} NextHopKey(const std::string &str, const std::string &alias) : - alias(alias), vni(0), mac_address(), weight(0) + alias(alias), vni(0), mac_address(), weight(0), + srv6_vpn_sid(""), + srv6_source(""), + srv6_segment("") { std::string ip_str = parseMplsNextHop(str); ip_address = ip_str; } NextHopKey(const IpAddress &ip, const std::string &alias) : - ip_address(ip), alias(alias), vni(0), mac_address(), weight(0) {} + ip_address(ip), alias(alias), vni(0), mac_address(), weight(0), + srv6_vpn_sid(""), + srv6_source(""), + srv6_segment("") + {} NextHopKey(const std::string &str) : - vni(0), mac_address() + vni(0), mac_address(), + srv6_vpn_sid(""), + srv6_source(""), + srv6_segment("") { if (str.find(NHG_DELIMITER) != string::npos) { @@ -76,14 +92,15 @@ struct NextHopKey vni = 0; weight = 0; auto keys = tokenize(str, NH_DELIMITER); - if (keys.size() != 3) + if (keys.size() != 4) { std::string err = "Error converting " + str + " to Nexthop"; throw std::invalid_argument(err); } ip_address = keys[0]; - srv6_segment = keys[1]; + srv6_vpn_sid = keys[1]; srv6_source = keys[2]; + srv6_segment = keys[3]; } else { @@ -99,10 +116,18 @@ struct NextHopKey vni = static_cast(std::stoul(keys[2])); mac_address = keys[3]; weight = 0; + srv6_vpn_sid = ""; + srv6_source = ""; + srv6_segment = ""; } } - NextHopKey(const IpAddress &ip, const MacAddress &mac, const uint32_t &vni, bool overlay_nh) : ip_address(ip), alias(""), vni(vni), mac_address(mac), weight(0){} + NextHopKey(const IpAddress &ip, const MacAddress &mac, const uint32_t &vni, bool overlay_nh) : + ip_address(ip), alias(""), vni(vni), mac_address(mac), weight(0), + srv6_vpn_sid(""), + srv6_source(""), + srv6_segment("") + {} const std::string to_string() const { @@ -115,7 +140,10 @@ struct NextHopKey { if (srv6_nh) { - return ip_address.to_string() + NH_DELIMITER + srv6_segment + NH_DELIMITER + srv6_source; + return ip_address.to_string() + NH_DELIMITER + + srv6_vpn_sid + NH_DELIMITER + + srv6_source + NH_DELIMITER + + srv6_segment + NH_DELIMITER; } std::string str = formatMplsNextHop(); str += (ip_address.to_string() + NH_DELIMITER + alias + NH_DELIMITER + @@ -125,8 +153,8 @@ struct NextHopKey bool operator<(const NextHopKey &o) const { - return tie(ip_address, alias, label_stack, vni, mac_address, srv6_segment, srv6_source) < - tie(o.ip_address, o.alias, o.label_stack, o.vni, o.mac_address, o.srv6_segment, o.srv6_source); + return tie(ip_address, alias, label_stack, vni, mac_address, srv6_segment, srv6_source, srv6_vpn_sid) < + tie(o.ip_address, o.alias, o.label_stack, o.vni, o.mac_address, o.srv6_segment, o.srv6_source, o.srv6_vpn_sid); } bool operator==(const NextHopKey &o) const @@ -134,7 +162,8 @@ struct NextHopKey return (ip_address == o.ip_address) && (alias == o.alias) && (label_stack == o.label_stack) && (vni == o.vni) && (mac_address == o.mac_address) && - (srv6_segment == o.srv6_segment) && (srv6_source == o.srv6_source); + (srv6_segment == o.srv6_segment) && (srv6_source == o.srv6_source) && + (srv6_vpn_sid == o.srv6_vpn_sid); } bool operator!=(const NextHopKey &o) const @@ -152,11 +181,6 @@ struct NextHopKey return (!label_stack.empty()); } - bool isSrv6NextHop() const - { - return (srv6_segment != ""); - } - std::string parseMplsNextHop(const std::string& str) { // parseMplsNextHop initializes MPLS-related member data of the NextHopKey @@ -197,6 +221,16 @@ struct NextHopKey } return str; } + + bool isSrv6NextHop() const + { + return ((srv6_segment != "") || (srv6_vpn_sid != "") || (srv6_source != "")); + } + + bool isSrv6Vpn() const + { + return (srv6_vpn_sid != ""); + } }; #endif /* SWSS_NEXTHOPKEY_H */ diff --git a/orchagent/nhgorch.cpp b/orchagent/nhgorch.cpp index 1e62547b41..a7aaa6fdb9 100644 --- a/orchagent/nhgorch.cpp +++ b/orchagent/nhgorch.cpp @@ -2,6 +2,7 @@ #include "neighorch.h" #include "crmorch.h" #include "routeorch.h" +#include "srv6orch.h" #include "bulker.h" #include "logger.h" #include "swssnet.h" @@ -12,6 +13,7 @@ extern IntfsOrch *gIntfsOrch; extern NeighOrch *gNeighOrch; extern RouteOrch *gRouteOrch; extern NhgOrch *gNhgOrch; +extern Srv6Orch *gSrv6Orch; extern size_t gMaxBulkSize; @@ -61,6 +63,9 @@ void NhgOrch::doTask(Consumer& consumer) string mpls_nhs; string nhgs; bool is_recursive = false; + string srv6_source; + bool overlay_nh = false; + bool srv6_nh = false; /* Get group's next hop IPs and aliases */ for (auto i : kfvFieldsValues(t)) @@ -77,6 +82,12 @@ void NhgOrch::doTask(Consumer& consumer) if (fvField(i) == "mpls_nh") mpls_nhs = fvValue(i); + if (fvField(i) == "seg_src") + { + srv6_source = fvValue(i); + srv6_nh = true; + } + if (fvField(i) == "nexthop_group") { nhgs = fvValue(i); @@ -96,9 +107,11 @@ void NhgOrch::doTask(Consumer& consumer) vector alsv = tokenize(aliases, ','); vector mpls_nhv = tokenize(mpls_nhs, ','); vector nhgv = tokenize(nhgs, NHG_DELIMITER); + vector srv6_srcv = tokenize(srv6_source, ','); /* Create the next hop group key. */ string nhg_str; + NextHopGroupKey nhg_key; /* Keeps track of any non-existing member of a recursive nexthop group */ bool non_existent_member = false; @@ -154,28 +167,77 @@ void NhgOrch::doTask(Consumer& consumer) /* Form nexthopgroup key with the nexthopgroup keys of available members */ nhgv = tokenize(nhgs, NHG_DELIMITER); + bool nhg_mismatch = false; for (uint32_t i = 0; i < nhgv.size(); i++) { - if (i) nhg_str += NHG_DELIMITER; + auto k = m_syncdNextHopGroups.at(nhgv[i]).nhg->getKey(); + if (i) + { + if (k.is_srv6_nexthop() != srv6_nh || k.is_overlay_nexthop() != overlay_nh) + { + SWSS_LOG_ERROR("Inconsistent nexthop group type between %s and %s", + m_syncdNextHopGroups.at(nhgv[0]).nhg->getKey().to_string().c_str(), + k.to_string().c_str()); + nhg_mismatch = true; + break; + } + nhg_str += NHG_DELIMITER; + } + else + { + srv6_nh = k.is_srv6_nexthop(); + overlay_nh = k.is_overlay_nexthop(); + } nhg_str += m_syncdNextHopGroups.at(nhgv[i]).nhg->getKey().to_string(); } + + if (nhg_mismatch) + { + it = consumer.m_toSync.erase(it); + continue; + } + + if (srv6_nh) + nhg_key = NextHopGroupKey(nhg_str, overlay_nh, srv6_nh, weights); + else + nhg_key = NextHopGroupKey(nhg_str, weights); } else { - for (uint32_t i = 0; i < ipv.size(); i++) + if (srv6_nh) { - if (i) nhg_str += NHG_DELIMITER; - if (!mpls_nhv.empty() && mpls_nhv[i] != "na") + if (ipv.size() != srv6_srcv.size()) + { + SWSS_LOG_ERROR("inconsistent number of endpoints and srv6_srcs."); + it = consumer.m_toSync.erase(it); + continue; + } + for (uint32_t i = 0; i < ipv.size(); i++) { - nhg_str += mpls_nhv[i] + LABELSTACK_DELIMITER; + if (i) nhg_str += NHG_DELIMITER; + nhg_str += ipv[i] + NH_DELIMITER; // ip address + nhg_str += NH_DELIMITER; // srv6 vpn sid + nhg_str += srv6_srcv[i] + NH_DELIMITER; // srv6 source + nhg_str += NH_DELIMITER; // srv6 segment + } + nhg_key = NextHopGroupKey(nhg_str, overlay_nh, srv6_nh, weights); + } + else + { + for (uint32_t i = 0; i < ipv.size(); i++) + { + if (i) nhg_str += NHG_DELIMITER; + if (!mpls_nhv.empty() && mpls_nhv[i] != "na") + { + nhg_str += mpls_nhv[i] + LABELSTACK_DELIMITER; + } + nhg_str += ipv[i] + NH_DELIMITER + alsv[i]; } - nhg_str += ipv[i] + NH_DELIMITER + alsv[i]; + nhg_key = NextHopGroupKey(nhg_str, weights); } } - NextHopGroupKey nhg_key = NextHopGroupKey(nhg_str, weights); - /* If the group does not exist, create one. */ if (nhg_it == m_syncdNextHopGroups.end()) { @@ -192,6 +254,13 @@ void NhgOrch::doTask(Consumer& consumer) { SWSS_LOG_DEBUG("Next hop group count reached its limit."); + // don't create temp nhg for srv6 + if (nhg_key.is_srv6_nexthop()) + { + ++it; + continue; + } + try { auto nhg = std::make_unique(createTempNhg(nhg_key)); @@ -476,6 +545,14 @@ sai_object_id_t NextHopGroupMember::getNhId() const else if (gNeighOrch->hasNextHop(m_key)) { nh_id = gNeighOrch->getNextHopId(m_key); + if (m_key.isSrv6NextHop()) + { + SWSS_LOG_INFO("Single NH: create srv6 nexthop %s", m_key.to_string(false, true).c_str()); + if (!gSrv6Orch->createSrv6NexthopWithoutVpn(m_key, nh_id)) + { + SWSS_LOG_ERROR("Failed to create SRv6 nexthop %s", m_key.to_string(false, true).c_str()); + } + } } /* * If the next hop is labeled and the IP next hop exists, create the @@ -493,7 +570,20 @@ sai_object_id_t NextHopGroupMember::getNhId() const } else { - gNeighOrch->resolveNeighbor(m_key); + if (m_key.isSrv6NextHop()) + { + SWSS_LOG_INFO("Single NH: create srv6 nexthop %s", m_key.to_string(false, true).c_str()); + if (!gSrv6Orch->createSrv6NexthopWithoutVpn(m_key, nh_id)) + { + SWSS_LOG_ERROR("Failed to create SRv6 nexthop %s", m_key.to_string(false, true).c_str()); + } + } + else + { + SWSS_LOG_INFO("Failed to get next hop %s, resolving neighbor", + m_key.to_string().c_str()); + gNeighOrch->resolveNeighbor(m_key); + } } return nh_id; @@ -569,6 +659,14 @@ NextHopGroupMember::~NextHopGroupMember() { SWSS_LOG_ENTER(); + if (m_key.isSrv6NextHop() && gNeighOrch->hasNextHop(m_key) && + !gNeighOrch->getNextHopRefCount(m_key)) + { + if (!gSrv6Orch->removeSrv6NexthopWithoutVpn(m_key)) + { + SWSS_LOG_ERROR("SRv6 Nexthop %s delete failed", m_key.to_string(false, true).c_str()); + } + } /* * If the labeled next hop is unreferenced, remove it from NeighOrch as * NhgOrch and RouteOrch are the ones controlling it's lifetime. They both @@ -576,7 +674,7 @@ NextHopGroupMember::~NextHopGroupMember() * them as they're both doing the same checks before removing a labeled * next hop. */ - if (isLabeled() && + else if (isLabeled() && gNeighOrch->hasNextHop(m_key) && (gNeighOrch->getNextHopRefCount(m_key) == 0)) { @@ -823,6 +921,7 @@ bool NextHopGroup::syncMembers(const std::set& nh_keys) */ std::map syncingMembers; + bool success = true; for (const auto& nh_key : nh_keys) { NextHopGroupMember& nhgm = m_members.at(nh_key); @@ -840,7 +939,8 @@ bool NextHopGroup::syncMembers(const std::set& nh_keys) { SWSS_LOG_WARN("Failed to get next hop %s in group %s", nhgm.to_string().c_str(), to_string().c_str()); - return false; + success = false; + continue; } /* If the neighbor's interface is down, skip from being syncd. */ @@ -867,7 +967,6 @@ bool NextHopGroup::syncMembers(const std::set& nh_keys) * Go through the synced members and increment the Crm ref count for the * successful ones. */ - bool success = true; for (const auto& mbr : syncingMembers) { /* Check that the returned member ID is valid. */ @@ -940,7 +1039,7 @@ bool NextHopGroup::update(const NextHopGroupKey& nhg_key) /* If the member is updated, update it's weight. */ else { - if (!mbr_it.second.updateWeight(new_nh_key_it->weight)) + if (new_nh_key_it->weight && mbr_it.second.getWeight() != new_nh_key_it->weight && !mbr_it.second.updateWeight(new_nh_key_it->weight)) { SWSS_LOG_WARN("Failed to update member %s weight", nh_key.to_string().c_str()); return false; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 0d2ab1c200..99c68e6eb0 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -211,7 +211,8 @@ bool OrchDaemon::init() vector srv6_tables = { APP_SRV6_SID_LIST_TABLE_NAME, - APP_SRV6_MY_SID_TABLE_NAME + APP_SRV6_MY_SID_TABLE_NAME, + APP_PIC_CONTEXT_TABLE_NAME }; gSrv6Orch = new Srv6Orch(m_applDb, srv6_tables, gSwitchOrch, vrf_orch, gNeighOrch); gDirectory.set(gSrv6Orch); diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index b1508656b3..3cad426a6e 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include "routeorch.h" @@ -594,53 +596,73 @@ void RouteOrch::doTask(Consumer& consumer) string remote_macs; string weights; string nhg_index; + string context_index; bool& excp_intfs_flag = ctx.excp_intfs_flag; bool overlay_nh = false; bool blackhole = false; string srv6_segments; string srv6_source; + string srv6_vpn_sids; + bool srv6_seg = false; + bool srv6_vpn = false; bool srv6_nh = false; for (auto i : kfvFieldsValues(t)) { - if (fvField(i) == "nexthop") + if (fvField(i) == "nexthop" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) ips = fvValue(i); - if (fvField(i) == "ifname") + if (fvField(i) == "ifname" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) aliases = fvValue(i); - if (fvField(i) == "mpls_nh") + if (fvField(i) == "mpls_nh" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) mpls_nhs = fvValue(i); - if (fvField(i) == "vni_label") { + if (fvField(i) == "vni_label" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) { vni_labels = fvValue(i); overlay_nh = true; } - if (fvField(i) == "router_mac") + if (fvField(i) == "router_mac" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) remote_macs = fvValue(i); - if (fvField(i) == "blackhole") + if (fvField(i) == "blackhole" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) blackhole = fvValue(i) == "true"; - if (fvField(i) == "weight") + if (fvField(i) == "weight" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) weights = fvValue(i); - if (fvField(i) == "nexthop_group") + if (fvField(i) == "nexthop_group" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) nhg_index = fvValue(i); - if (fvField(i) == "segment") { + if (fvField(i) == "segment" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) { srv6_segments = fvValue(i); + srv6_seg = true; srv6_nh = true; } - if (fvField(i) == "seg_src") + if (fvField(i) == "seg_src" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) { srv6_source = fvValue(i); + srv6_nh = true; + } if (fvField(i) == "protocol") { ctx.protocol = fvValue(i); } + + + if (fvField(i) == "vpn_sid" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) { + srv6_vpn_sids = fvValue(i); + srv6_nh = true; + srv6_vpn = true; + } + + if (fvField(i) == "pic_context_id" && fvValue(i) != "" && fvValue(i).find("na") == string::npos) + { + context_index = fvValue(i); + srv6_vpn = true; + } } /* @@ -655,6 +677,7 @@ void RouteOrch::doTask(Consumer& consumer) } ctx.nhg_index = nhg_index; + ctx.context_index = context_index; /* * If the nexthop_group is empty, create the next hop group key @@ -669,6 +692,7 @@ void RouteOrch::doTask(Consumer& consumer) NextHopGroupKey& nhg = ctx.nhg; vector srv6_segv; vector srv6_src; + vector srv6_vpn_sidv; bool l3Vni = true; uint32_t vni = 0; @@ -682,6 +706,7 @@ void RouteOrch::doTask(Consumer& consumer) rmacv = tokenize(remote_macs, ','); srv6_segv = tokenize(srv6_segments, ','); srv6_src = tokenize(srv6_source, ','); + srv6_vpn_sidv = tokenize(srv6_vpn_sids, ','); /* * For backward compatibility, adjust ip string from old format to @@ -768,25 +793,36 @@ void RouteOrch::doTask(Consumer& consumer) } else if (srv6_nh == true) { - string ip; - if (ipv.empty()) + if (ipv.size() != srv6_src.size()) { - ip = "0.0.0.0"; + SWSS_LOG_ERROR("inconsistent number of endpoints and srv6_srcs."); + it = consumer.m_toSync.erase(it); + continue; } - else + + if (srv6_vpn && (ipv.size() != srv6_vpn_sidv.size())) { - SWSS_LOG_ERROR("For SRV6 nexthop ipv should be empty"); + SWSS_LOG_ERROR("inconsistent number of endpoints and srv6 vpn sids."); it = consumer.m_toSync.erase(it); continue; } - nhg_str = ip + NH_DELIMITER + srv6_segv[0] + NH_DELIMITER + srv6_src[0]; - for (uint32_t i = 1; i < srv6_segv.size(); i++) + if (srv6_seg && (srv6_segv.size() != srv6_src.size())) { - nhg_str += NHG_DELIMITER + ip; - nhg_str += NH_DELIMITER + srv6_segv[i]; - nhg_str += NH_DELIMITER + srv6_src[i]; + SWSS_LOG_ERROR("inconsistent number of srv6_segv and srv6_srcs."); + it = consumer.m_toSync.erase(it); + continue; + } + + for (uint32_t i = 0; i < ipv.size(); i++) + { + if (i) nhg_str += NHG_DELIMITER; + nhg_str += ipv[i] + NH_DELIMITER; // ip address + nhg_str += (srv6_vpn ? srv6_vpn_sidv[i] : "") + NH_DELIMITER; // srv6 vpn sid + nhg_str += srv6_src[i] + NH_DELIMITER; // srv6 source + nhg_str += (srv6_seg ? srv6_segv[i] : "") + NH_DELIMITER; // srv6 segment } + nhg = NextHopGroupKey(nhg_str, overlay_nh, srv6_nh); SWSS_LOG_INFO("SRV6 route with nhg %s", nhg.to_string().c_str()); } @@ -898,7 +934,7 @@ void RouteOrch::doTask(Consumer& consumer) */ else if (m_syncdRoutes.find(vrf_id) == m_syncdRoutes.end() || m_syncdRoutes.at(vrf_id).find(ip_prefix) == m_syncdRoutes.at(vrf_id).end() || - m_syncdRoutes.at(vrf_id).at(ip_prefix) != RouteNhg(nhg, ctx.nhg_index) || + m_syncdRoutes.at(vrf_id).at(ip_prefix) != RouteNhg(nhg, ctx.nhg_index, ctx.context_index) || gRouteBulker.bulk_entry_pending_removal(route_entry) || ctx.using_temp_nhg) { @@ -943,6 +979,8 @@ void RouteOrch::doTask(Consumer& consumer) // Go through the bulker results auto it_prev = consumer.m_toSync.begin(); m_bulkNhgReducedRefCnt.clear(); + m_bulkSrv6NhgReducedVec.clear(); + while (it_prev != it) { KeyOpFieldsValuesTuple t = it_prev->second; @@ -967,6 +1005,11 @@ void RouteOrch::doTask(Consumer& consumer) const sai_object_id_t& vrf_id = ctx.vrf_id; const IpPrefix& ip_prefix = ctx.ip_prefix; + sai_route_entry_t route_entry; + route_entry.vr_id = vrf_id; + route_entry.switch_id = gSwitchId; + copy(route_entry.destination, ip_prefix); + if (op == SET_COMMAND) { const bool& excp_intfs_flag = ctx.excp_intfs_flag; @@ -993,7 +1036,8 @@ void RouteOrch::doTask(Consumer& consumer) } else if (m_syncdRoutes.find(vrf_id) == m_syncdRoutes.end() || m_syncdRoutes.at(vrf_id).find(ip_prefix) == m_syncdRoutes.at(vrf_id).end() || - m_syncdRoutes.at(vrf_id).at(ip_prefix) != RouteNhg(nhg, ctx.nhg_index) || + m_syncdRoutes.at(vrf_id).at(ip_prefix) != RouteNhg(nhg, ctx.nhg_index, ctx.context_index) || + gRouteBulker.bulk_entry_pending_removal(route_entry) || ctx.using_temp_nhg) { if (addRoutePost(ctx, nhg)) @@ -1019,25 +1063,18 @@ void RouteOrch::doTask(Consumer& consumer) { removeOverlayNextHops(it_nhg.second, it_nhg.first); } - else if (it_nhg.first.is_srv6_nexthop()) - { - if(it_nhg.first.getSize() > 1) - { - if(m_syncdNextHopGroups[it_nhg.first].ref_count == 0) - { - removeNextHopGroup(it_nhg.first); - } - else - { - SWSS_LOG_ERROR("SRV6 ECMP %s REF count is not zero", it_nhg.first.to_string().c_str()); - } - } - } else if (m_syncdNextHopGroups[it_nhg.first].ref_count == 0) { removeNextHopGroup(it_nhg.first); } } + + /* Reduce reference for srv6 next hop group */ + /* Later delete for increase refcnt early */ + if (!m_bulkSrv6NhgReducedVec.empty()) + { + m_srv6Orch->removeSrv6Nexthops(m_bulkSrv6NhgReducedVec); + } } } @@ -1521,18 +1558,6 @@ bool RouteOrch::removeNextHopGroup(const NextHopGroupKey &nexthops) } } - if (srv6_nh) - { - if (!m_srv6Orch->removeSrv6Nexthops(nexthops)) - { - SWSS_LOG_ERROR("Failed to remove Srv6 Nexthop %s", nexthops.to_string().c_str()); - } - else - { - SWSS_LOG_INFO("Remove ECMP Srv6 nexthops %s", nexthops.to_string().c_str()); - } - } - m_syncdNextHopGroups.erase(nexthops); return true; @@ -1801,6 +1826,15 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) if (m_neighOrch->hasNextHop(nexthop)) { next_hop_id = m_neighOrch->getNextHopId(nexthop); + if (srv6_nh) + { + SWSS_LOG_INFO("Single NH: create srv6 vpn %s", nextHops.to_string().c_str()); + if (!m_srv6Orch->srv6Nexthops(nextHops, next_hop_id)) + { + SWSS_LOG_ERROR("Failed to create SRV6 vpn %s", nextHops.to_string().c_str()); + return false; + } + } } /* For non-existent MPLS NH, check if IP neighbor NH exists */ else if (nexthop.isMplsNextHop() && @@ -1851,22 +1885,38 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) /* The route is pointing to a next hop group */ else { + /* Need to call srv6nexthops() always for srv6 route, */ + /* regardless of whether there is already an existing next hop group */ + /* because vpn refcount need to be add if need */ + if (srv6_nh) + { + sai_object_id_t temp_nh_id; + SWSS_LOG_INFO("ECMP SRV6 NH: handle srv6 nexthops %s", nextHops.to_string().c_str()); + if(!m_srv6Orch->srv6Nexthops(nextHops, temp_nh_id)) + { + SWSS_LOG_ERROR("Failed to handle SRV6 nexthops for %s", nextHops.to_string().c_str()); + return false; + } + } + /* Check if there is already an existing next hop group */ if (!hasNextHopGroup(nextHops)) { - if(srv6_nh) + /* Try to create a new next hop group */ + if (!addNextHopGroup(nextHops)) { - sai_object_id_t temp_nh_id; - SWSS_LOG_INFO("ECMP SRV6 NH: create srv6 nexthops %s", nextHops.to_string().c_str()); - if(!m_srv6Orch->srv6Nexthops(nextHops, temp_nh_id)) + /* If the nexthop is a srv6 nexthop, not create tempRoute + * retry to add route */ + if (nextHops.is_srv6_nexthop()) { - SWSS_LOG_ERROR("Failed to create SRV6 nexthops for %s", nextHops.to_string().c_str()); return false; } - } - /* Try to create a new next hop group */ - if (!addNextHopGroup(nextHops)) - { + + if (it_route != m_syncdRoutes.at(vrf_id).end() && it_route->second.nhg_key.is_srv6_nexthop()) + { + return false; + } + for(auto it = nextHops.getNextHops().begin(); it != nextHops.getNextHops().end(); ++it) { const NextHopKey& nextHop = *it; @@ -1929,6 +1979,8 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) copy(route_entry.destination, ipPrefix); sai_attribute_t route_attr; + vector attrs; + vector<_sai_attribute_t> route_attrs; auto& object_statuses = ctx.object_statuses; /* If the prefix is not in m_syncdRoutes, then we need to create the route @@ -1947,16 +1999,30 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) { route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; route_attr.value.s32 = SAI_PACKET_ACTION_DROP; + route_attrs.push_back(route_attr); } else { route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; route_attr.value.oid = next_hop_id; + route_attrs.push_back(route_attr); + } + + if (!ctx.context_index.empty() || nextHops.is_srv6_vpn()) + { + if (!ctx.context_index.empty() && !m_srv6Orch->contextIdExists(ctx.context_index)) + { + SWSS_LOG_INFO("Context id %s does not exist", ctx.context_index.c_str()); + return false; + } + route_attr.id = SAI_ROUTE_ENTRY_ATTR_PREFIX_AGG_ID; + route_attr.value.u32 = ctx.nhg_index.empty() ? m_srv6Orch->getAggId(nextHops) : m_srv6Orch->getAggId(ctx.context_index); + route_attrs.push_back(route_attr); } /* Default SAI_ROUTE_ATTR_PACKET_ACTION is SAI_PACKET_ACTION_FORWARD */ object_statuses.emplace_back(); - sai_status_t status = gRouteBulker.create_entry(&object_statuses.back(), &route_entry, 1, &route_attr); + sai_status_t status = gRouteBulker.create_entry(&object_statuses.back(), &route_entry, (uint32_t)route_attrs.size(), route_attrs.data()); if (status == SAI_STATUS_ITEM_ALREADY_EXISTS) { SWSS_LOG_ERROR("Failed to create route %s with next hop(s) %s: already exists in bulker", @@ -2003,6 +2069,21 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) gRouteBulker.set_entry_attribute(&object_statuses.back(), &route_entry, &route_attr); } + // Set update preifx agg id if need + if (nextHops.is_srv6_vpn() || + (it_route->second.context_index != ctx.context_index && !ctx.context_index.empty())) + { + if (!ctx.context_index.empty() && !m_srv6Orch->contextIdExists(ctx.context_index)) + { + SWSS_LOG_INFO("Context id %s does not exist", ctx.context_index.c_str()); + return false; + } + route_attr.id = SAI_ROUTE_ENTRY_ATTR_PREFIX_AGG_ID; + route_attr.value.u32 = ctx.nhg_index.empty() ? m_srv6Orch->getAggId(nextHops) : m_srv6Orch->getAggId(ctx.context_index); + object_statuses.emplace_back(); + gRouteBulker.set_entry_attribute(&object_statuses.back(), &route_entry, &route_attr); + } + route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; route_attr.value.oid = next_hop_id; @@ -2092,7 +2173,9 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey { // Previous added an temporary route auto& tmp_next_hop = ctx.tmp_next_hop; - addRoutePost(ctx, tmp_next_hop); + if (tmp_next_hop.getSize() > 0) { + addRoutePost(ctx, tmp_next_hop); + } return false; } } @@ -2181,7 +2264,7 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey } else { - incNhgRefCount(ctx.nhg_index); + incNhgRefCount(ctx.nhg_index, ctx.context_index); } SWSS_LOG_INFO("Post create route %s with next hop(s) %s", @@ -2229,6 +2312,10 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey { decreaseNextHopRefCount(it_route->second.nhg_key); auto ol_nextHops = it_route->second.nhg_key; + if (ol_nextHops.is_srv6_nexthop()) + { + m_bulkSrv6NhgReducedVec.emplace_back(ol_nextHops); + } if (ol_nextHops.getSize() > 1) { if (m_syncdNextHopGroups[ol_nextHops].ref_count == 0) @@ -2259,11 +2346,11 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey m_bulkNhgReducedRefCnt.emplace(ol_nextHops, vrf_id); } } - else if (ol_nextHops.is_srv6_nexthop()) - { - m_srv6Orch->removeSrv6Nexthops(ol_nextHops); - } - else if (ol_nextHops.getSize() == 1) + // else if (ol_nextHops.is_srv6_nexthop()) + // { + // m_srv6Orch->removeSrv6Nexthops(ol_nextHops); + // } + else if (ol_nextHops.getSize() == 1 && !ol_nextHops.is_srv6_nexthop()) { RouteKey r_key = { vrf_id, ipPrefix }; auto nexthop = NextHopKey(ol_nextHops.to_string()); @@ -2273,7 +2360,7 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey /* The next hop group is owned by (Cbf)NhgOrch. */ else { - decNhgRefCount(it_route->second.nhg_index); + decNhgRefCount(it_route->second.nhg_index, it_route->second.context_index); } if (blackhole) @@ -2299,7 +2386,7 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey } else { - incNhgRefCount(ctx.nhg_index); + incNhgRefCount(ctx.nhg_index, ctx.context_index); } SWSS_LOG_INFO("Post set route %s with next hop(s) %s", @@ -2338,7 +2425,7 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey gFlowCounterRouteOrch->handleRouteAdd(vrf_id, ipPrefix); } - m_syncdRoutes[vrf_id][ipPrefix] = RouteNhg(nextHops, ctx.nhg_index); + m_syncdRoutes[vrf_id][ipPrefix] = RouteNhg(nextHops, ctx.nhg_index, ctx.context_index); /* add subnet decap term for VIP route */ const SubnetDecapConfig &config = gTunneldecapOrch->getSubnetDecapConfig(); @@ -2504,7 +2591,7 @@ bool RouteOrch::removeRoutePost(const RouteBulkContext& ctx) /* Check if the next hop group is not owned by NhgOrch. */ else if (!it_route->second.nhg_index.empty()) { - decNhgRefCount(it_route->second.nhg_index); + decNhgRefCount(it_route->second.nhg_index, it_route->second.context_index); } /* The NHG is owned by RouteOrch */ else @@ -2515,6 +2602,12 @@ bool RouteOrch::removeRoutePost(const RouteBulkContext& ctx) decreaseNextHopRefCount(it_route->second.nhg_key); auto ol_nextHops = it_route->second.nhg_key; + + if (ol_nextHops.is_srv6_nexthop()) + { + m_bulkSrv6NhgReducedVec.emplace_back(ol_nextHops); + } + MuxOrch* mux_orch = gDirectory.get(); if (it_route->second.nhg_key.getSize() > 1) { @@ -2560,11 +2653,6 @@ bool RouteOrch::removeRoutePost(const RouteBulkContext& ctx) { m_neighOrch->removeMplsNextHop(nexthop); } - else if (nexthop.isSrv6NextHop() && - (m_neighOrch->getNextHopRefCount(nexthop) == 0)) - { - m_srv6Orch->removeSrv6Nexthops(it_route->second.nhg_key); - } RouteKey r_key = { vrf_id, ipPrefix }; removeNextHopRoute(nexthop, r_key); @@ -2709,7 +2797,7 @@ const NhgBase &RouteOrch::getNhg(const std::string &nhg_index) } } -void RouteOrch::incNhgRefCount(const std::string &nhg_index) +void RouteOrch::incNhgRefCount(const std::string &nhg_index, const std::string &context_index) { SWSS_LOG_ENTER(); @@ -2721,9 +2809,14 @@ void RouteOrch::incNhgRefCount(const std::string &nhg_index) { gCbfNhgOrch->incNhgRefCount(nhg_index); } + + if (!context_index.empty()) + { + m_srv6Orch->increasePicContextIdRefCount(context_index); + } } -void RouteOrch::decNhgRefCount(const std::string &nhg_index) +void RouteOrch::decNhgRefCount(const std::string &nhg_index, const std::string &context_index) { SWSS_LOG_ENTER(); @@ -2735,6 +2828,11 @@ void RouteOrch::decNhgRefCount(const std::string &nhg_index) { gCbfNhgOrch->decNhgRefCount(nhg_index); } + + if (!context_index.empty()) + { + m_srv6Orch->decreasePicContextIdRefCount(context_index); + } } void RouteOrch::publishRouteState(const RouteBulkContext& ctx, const ReturnCode& status) diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index 577d966a26..12c2c487c0 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -65,12 +65,14 @@ struct RouteNhg */ std::string nhg_index; + std::string context_index; + RouteNhg() = default; - RouteNhg(const NextHopGroupKey& key, const std::string& index) : - nhg_key(key), nhg_index(index) {} + RouteNhg(const NextHopGroupKey& key, const std::string& index, const std::string &context_index = "") : + nhg_key(key), nhg_index(index), context_index(context_index) {} bool operator==(const RouteNhg& rnhg) - { return ((nhg_key == rnhg.nhg_key) && (nhg_index == rnhg.nhg_index)); } + { return ((nhg_key == rnhg.nhg_key) && (nhg_index == rnhg.nhg_index) && (context_index == rnhg.context_index)); } bool operator!=(const RouteNhg& rnhg) { return !(*this == rnhg); } }; @@ -117,11 +119,17 @@ struct RouteBulkContext NextHopGroupKey tmp_next_hop; // Temporary next hop NextHopGroupKey nhg; std::string nhg_index; + std::string context_index; sai_object_id_t vrf_id; IpPrefix ip_prefix; bool excp_intfs_flag; // using_temp_nhg will track if the NhgOrch's owned NHG is temporary or not bool using_temp_nhg; + std::vector ipv; + std::vector alsv; + std::vector vni_labelv; + std::vector rmacv; + bool vrf_group_flag; std::string key; // Key in database table std::string protocol; // Protocol string @@ -141,8 +149,9 @@ struct RouteBulkContext object_statuses.clear(); tmp_next_hop.clear(); nhg.clear(); - excp_intfs_flag = false; + ipv.clear(); vrf_id = SAI_NULL_OBJECT_ID; + excp_intfs_flag = false; using_temp_nhg = false; key.clear(); protocol.clear(); @@ -253,6 +262,7 @@ class RouteOrch : public Orch, public Subject std::set m_SubnetDecapTermsCreated; ProducerStateTable m_appTunnelDecapTermProducer; + std::vector m_bulkSrv6NhgReducedVec; NextHopObserverTable m_nextHopObservers; @@ -261,7 +271,7 @@ class RouteOrch : public Orch, public Subject ObjectBulker gNextHopGroupMemberBulker; void addTempRoute(RouteBulkContext& ctx, const NextHopGroupKey&); - bool addRoute(RouteBulkContext& ctx, const NextHopGroupKey&); + bool addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops); bool removeRoute(RouteBulkContext& ctx); bool addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey &nextHops); bool removeRoutePost(const RouteBulkContext& ctx); @@ -278,14 +288,14 @@ class RouteOrch : public Orch, public Subject void doLabelTask(Consumer& consumer); const NhgBase &getNhg(const std::string& nhg_index); - void incNhgRefCount(const std::string& nhg_index); - void decNhgRefCount(const std::string& nhg_index); void publishRouteState(const RouteBulkContext& ctx, const ReturnCode& status = ReturnCode(SAI_STATUS_SUCCESS)); bool isVipRoute(const IpPrefix &ipPrefix, const NextHopGroupKey &nextHops); void createVipRouteSubnetDecapTerm(const IpPrefix &ipPrefix); void removeVipRouteSubnetDecapTerm(const IpPrefix &ipPrefix); + void incNhgRefCount(const std::string& nhg_index, const std::string &context_index = ""); + void decNhgRefCount(const std::string& nhg_index, const std::string &context_index = ""); }; #endif /* SWSS_ROUTEORCH_H */ diff --git a/orchagent/srv6orch.cpp b/orchagent/srv6orch.cpp index d1177cddc2..08e4b0afe5 100644 --- a/orchagent/srv6orch.cpp +++ b/orchagent/srv6orch.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "routeorch.h" #include "logger.h" @@ -47,8 +48,8 @@ const map end_behavior_map = const map end_flavor_map = { - {"end", SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_FLAVOR_PSP_AND_USD}, - {"end.x", SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_FLAVOR_PSP_AND_USD}, + {"end", SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_FLAVOR_PSP_AND_USP}, + {"end.x", SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_FLAVOR_PSP_AND_USP}, {"end.t", SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_FLAVOR_PSP_AND_USD}, {"un", SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_FLAVOR_PSP_AND_USD}, {"ua", SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_FLAVOR_PSP_AND_USD} @@ -132,60 +133,51 @@ bool Srv6Orch::srv6NexthopExists(const NextHopKey &nhKey) } } -bool Srv6Orch::removeSrv6Nexthops(const NextHopGroupKey &nhg) +bool Srv6Orch::removeSrv6NexthopWithoutVpn(const NextHopKey &nhKey) { SWSS_LOG_ENTER(); + return deleteSrv6Nexthop(nhKey); +} - for (auto &sr_nh : nhg.getNextHops()) - { - string srv6_source, segname; - sai_status_t status = SAI_STATUS_SUCCESS; - srv6_source = sr_nh.srv6_source; - segname = sr_nh.srv6_segment; +bool Srv6Orch::removeSrv6Nexthops(const std::vector &nhgv) +{ + SWSS_LOG_ENTER(); - SWSS_LOG_NOTICE("SRV6 Nexthop %s refcount %d", sr_nh.to_string(false,true).c_str(), m_neighOrch->getNextHopRefCount(sr_nh)); - if (m_neighOrch->getNextHopRefCount(sr_nh) == 0) + // 1. remove vpn_sid first + for (auto& it_nhg : nhgv) + { + if (it_nhg.is_srv6_vpn()) { - status = sai_next_hop_api->remove_next_hop(srv6_nexthop_table_[sr_nh]); - if (status != SAI_STATUS_SUCCESS) + for (auto &sr_nh : it_nhg.getNextHops()) { - SWSS_LOG_ERROR("Failed to remove SRV6 nexthop %s", sr_nh.to_string(false,true).c_str()); - return false; - } - - /* Update nexthop in SID table after deleting the nexthop */ - SWSS_LOG_INFO("Seg %s nexthop refcount %zu", - segname.c_str(), - sid_table_[segname].nexthops.size()); - if (sid_table_[segname].nexthops.find(sr_nh) != sid_table_[segname].nexthops.end()) - { - sid_table_[segname].nexthops.erase(sr_nh); + if (sr_nh.isSrv6Vpn()) + { + if (!deleteSrv6Vpn(sr_nh.ip_address.to_string(), sr_nh.srv6_vpn_sid, getAggId(it_nhg))) + { + deleteAggId(it_nhg); + SWSS_LOG_ERROR("Failed to delete SRV6 vpn %s", sr_nh.to_string(false, true).c_str()); + return false; + } + } } - m_neighOrch->updateSrv6Nexthop(sr_nh, 0); - srv6_nexthop_table_.erase(sr_nh); - - /* Delete NH from the tunnel map */ - SWSS_LOG_INFO("Delete NH %s from tunnel map", - sr_nh.to_string(false, true).c_str()); - srv6TunnelUpdateNexthops(srv6_source, sr_nh, false); + decreasePrefixAggIdRefCount(it_nhg); + deleteAggId(it_nhg); } + } - size_t tunnel_nhs = srv6TunnelNexthopSize(srv6_source); - if (tunnel_nhs == 0) + // 2. delete nexthop & prefix agg id + for (auto& nhg : nhgv) + { + for (auto &sr_nh : nhg.getNextHops()) { - status = sai_tunnel_api->remove_tunnel(srv6_tunnel_table_[srv6_source].tunnel_object_id); - if (status != SAI_STATUS_SUCCESS) + if (!deleteSrv6Nexthop(sr_nh)) { - SWSS_LOG_ERROR("Failed to remove SRV6 tunnel object for source %s", srv6_source.c_str()); + SWSS_LOG_ERROR("Failed to delete SRV6 nexthop %s", sr_nh.to_string(false,true).c_str()); return false; } - srv6_tunnel_table_.erase(srv6_source); - } - else - { - SWSS_LOG_INFO("Nexthops referencing this tunnel object %s: %zu", srv6_source.c_str(),tunnel_nhs); } } + return true; } @@ -194,26 +186,37 @@ bool Srv6Orch::createSrv6Nexthop(const NextHopKey &nh) SWSS_LOG_ENTER(); string srv6_segment = nh.srv6_segment; string srv6_source = nh.srv6_source; + string srv6_tunnel_endpoint; if (srv6NexthopExists(nh)) { SWSS_LOG_INFO("SRV6 nexthop already created for %s", nh.to_string(false,true).c_str()); return true; } - sai_object_id_t srv6_object_id = sid_table_[srv6_segment].sid_object_id; - sai_object_id_t srv6_tunnel_id = srv6_tunnel_table_[srv6_source].tunnel_object_id; - if (srv6_object_id == SAI_NULL_OBJECT_ID) + sai_object_id_t srv6_segment_id; + sai_object_id_t srv6_tunnel_id; + + if (srv6_segment == "") { - SWSS_LOG_ERROR("segment object doesn't exist for segment %s", srv6_segment.c_str()); - return false; + srv6_segment_id = SAI_NULL_OBJECT_ID; + } + else + { + srv6_segment_id = sid_table_[srv6_segment].sid_object_id; } - if (srv6_tunnel_id == SAI_NULL_OBJECT_ID) + if (nh.ip_address.to_string() == "0.0.0.0") { - SWSS_LOG_ERROR("tunnel object doesn't exist for source %s", srv6_source.c_str()); - return false; + srv6_tunnel_endpoint = srv6_source; + srv6_tunnel_id = srv6_tunnel_table_[srv6_tunnel_endpoint].tunnel_object_id; } + else + { + srv6_tunnel_endpoint = nh.ip_address.to_string(); + srv6_tunnel_id = srv6_p2p_tunnel_table_[srv6_tunnel_endpoint].tunnel_id; + } + SWSS_LOG_INFO("Create srv6 nh for tunnel src %s with seg %s", srv6_source.c_str(), srv6_segment.c_str()); vector nh_attrs; sai_object_id_t nexthop_id; @@ -225,7 +228,7 @@ bool Srv6Orch::createSrv6Nexthop(const NextHopKey &nh) nh_attrs.push_back(attr); attr.id = SAI_NEXT_HOP_ATTR_SRV6_SIDLIST_ID; - attr.value.oid = srv6_object_id; + attr.value.oid = srv6_segment_id; nh_attrs.push_back(attr); attr.id = SAI_NEXT_HOP_ATTR_TUNNEL_ID; @@ -242,8 +245,133 @@ bool Srv6Orch::createSrv6Nexthop(const NextHopKey &nh) } m_neighOrch->updateSrv6Nexthop(nh, nexthop_id); srv6_nexthop_table_[nh] = nexthop_id; - sid_table_[srv6_segment].nexthops.insert(nh); - srv6TunnelUpdateNexthops(srv6_source, nh, true); + if (srv6_segment != "") + { + sid_table_[srv6_segment].nexthops.insert(nh); + } + + if (nh.ip_address.to_string() == "0.0.0.0") + { + srv6TunnelUpdateNexthops(srv6_source, nh, true); + } + else + { + srv6P2ptunnelUpdateNexthops(nh, true); + } + return true; +} + +bool Srv6Orch::deleteSrv6Nexthop(const NextHopKey &nh) +{ + SWSS_LOG_ENTER(); + + sai_status_t status = SAI_STATUS_SUCCESS; + + if (!srv6NexthopExists(nh)) + { + return true; + } + + SWSS_LOG_DEBUG("SRV6 Nexthop %s refcount %d", nh.to_string(false,true).c_str(), m_neighOrch->getNextHopRefCount(nh)); + if (m_neighOrch->getNextHopRefCount(nh) == 0) + { + sai_object_id_t nexthop_id; + nexthop_id = srv6_nexthop_table_[nh]; + status = sai_next_hop_api->remove_next_hop(nexthop_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove SRV6 nexthop %s", nh.to_string(false,true).c_str()); + return false; + } + + /* Decrease srv6 segment reference */ + if (nh.srv6_segment != "") + { + /* Update nexthop in SID table after deleting the nexthop */ + SWSS_LOG_INFO("Seg %s nexthop refcount %zu", + nh.srv6_segment.c_str(), + sid_table_[nh.srv6_segment].nexthops.size()); + if (sid_table_[nh.srv6_segment].nexthops.find(nh) != sid_table_[nh.srv6_segment].nexthops.end()) + { + sid_table_[nh.srv6_segment].nexthops.erase(nh); + } + } + m_neighOrch->updateSrv6Nexthop(nh, 0); + + srv6_nexthop_table_.erase(nh); + + /* Delete NH from the tunnel map */ + SWSS_LOG_INFO("Delete NH %s from tunnel map", + nh.to_string(false, true).c_str()); + + if (nh.ip_address.to_string() == "0.0.0.0") + { + string srv6_source = nh.srv6_source; + srv6TunnelUpdateNexthops(srv6_source, nh, false); + size_t tunnel_nhs = srv6TunnelNexthopSize(srv6_source); + if (tunnel_nhs == 0) + { + status = sai_tunnel_api->remove_tunnel(srv6_tunnel_table_[srv6_source].tunnel_object_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove SRV6 tunnel object for source %s", srv6_source.c_str()); + return false; + } + srv6_tunnel_table_.erase(srv6_source); + } + else + { + SWSS_LOG_INFO("Nexthops referencing this tunnel object %s: %zu", srv6_source.c_str(),tunnel_nhs); + } + } + else + { + std::string endpoint = nh.ip_address.to_string(); + srv6P2ptunnelUpdateNexthops(nh, false); + if (!deleteSrv6P2pTunnel(endpoint)) + { + SWSS_LOG_ERROR("Failed to remove SRV6 p2p tunnel object for dst %s,", endpoint.c_str()); + return false; + } + } + } + + return true; +} + +bool Srv6Orch::createSrv6NexthopWithoutVpn(const NextHopKey &nh, sai_object_id_t &nexthop_id) +{ + SWSS_LOG_ENTER(); + + // 1. create tunnel + if (nh.ip_address.to_string() == "0.0.0.0") + { + // create srv6 tunnel + auto srv6_source = nh.srv6_source; + if (!createSrv6Tunnel(srv6_source)) + { + SWSS_LOG_ERROR("Failed to create tunnel for source %s", srv6_source.c_str()); + return false; + } + } + else + { + // create p2p tunnel + if (!createSrv6P2pTunnel(nh.srv6_source, nh.ip_address.to_string())) + { + SWSS_LOG_ERROR("Failed to create SRV6 p2p tunnel %s", nh.to_string(false, true).c_str()); + return false; + } + } + + // 2. create nexthop + if (!createSrv6Nexthop(nh)) + { + SWSS_LOG_ERROR("Failed to create SRV6 nexthop %s", nh.to_string(false,true).c_str()); + return false; + } + + nexthop_id = srv6_nexthop_table_[nh]; return true; } @@ -255,18 +383,34 @@ bool Srv6Orch::srv6Nexthops(const NextHopGroupKey &nhgKey, sai_object_id_t &next string srv6_segment; for (auto nh : nexthops) - { - srv6_source = nh.srv6_source; - if (!createSrv6Tunnel(srv6_source)) + { + // create SRv6 nexthop + if (!createSrv6NexthopWithoutVpn(nh, nexthop_id)) { - SWSS_LOG_ERROR("Failed to create tunnel for source %s", srv6_source.c_str()); + SWSS_LOG_ERROR("Failed to create SRv6 nexthop %s", nh.to_string(false, true).c_str()); return false; } - if (!createSrv6Nexthop(nh)) + } + + // create SRv6 VPN if need + if (nhgKey.is_srv6_vpn()) + { + for (auto it = nexthops.begin(); it != nexthops.end(); ++it) { - SWSS_LOG_ERROR("Failed to create SRV6 nexthop %s", nh.to_string(false,true).c_str()); - return false; + if (it->isSrv6Vpn()) + { + if (!createSrv6Vpn(it->ip_address.to_string(), it->srv6_vpn_sid, getAggId(nhgKey))) + { + for (auto itt = nexthops.begin(); itt != it; ++itt) + deleteSrv6Vpn(itt->ip_address.to_string(), itt->srv6_vpn_sid, getAggId(nhgKey)); + deleteAggId(nhgKey); + SWSS_LOG_ERROR("Failed to create SRV6 vpn %s", it->to_string(false, true).c_str()); + return false; + } + } } + + increasePrefixAggIdRefCount(nhgKey); } if (nhgKey.getSize() == 1) @@ -855,6 +999,491 @@ bool Srv6Orch::deleteMysidEntry(const string my_sid_string) return true; } +uint32_t Srv6Orch::getAggId(const NextHopGroupKey &nhg) +{ + SWSS_LOG_ENTER(); + static uint32_t g_agg_id = 1; + uint32_t agg_id; + + if (srv6_prefix_agg_id_table_.find(nhg) != srv6_prefix_agg_id_table_.end()) { + agg_id = srv6_prefix_agg_id_table_[nhg].prefix_agg_id; + SWSS_LOG_INFO("Agg id already exist, agg_id_key: %s, agg_id %u", nhg.to_string().c_str(), agg_id); + } else { + while (srv6_prefix_agg_id_set_.find(g_agg_id) != srv6_prefix_agg_id_set_.end()) { + SWSS_LOG_INFO("Agg id %d is busy, try next", g_agg_id); + g_agg_id++; + // restart with 1 if flip + if (g_agg_id == 0) { + g_agg_id = 1; + } + } + agg_id = g_agg_id; + srv6_prefix_agg_id_table_[nhg].prefix_agg_id = g_agg_id; + // initialize ref_count with 0, will be added in increasePrefixAggIdRefCount() later + srv6_prefix_agg_id_table_[nhg].ref_count = 0; + srv6_prefix_agg_id_set_.insert(g_agg_id); + SWSS_LOG_INFO("Agg id not exist, create agg_id_key: %s, agg_id %u", nhg.to_string().c_str(), agg_id); + } + + return agg_id; +} + +uint32_t Srv6Orch::getAggId(const std::string& index) +{ + SWSS_LOG_ENTER(); + static uint32_t g_agg_id = 1; + uint32_t agg_id; + + if (srv6_prefix_agg_id_table_for_nhg_.find(index) != srv6_prefix_agg_id_table_for_nhg_.end()) { + agg_id = srv6_prefix_agg_id_table_for_nhg_[index].prefix_agg_id; + SWSS_LOG_INFO("Agg id already exist, agg_id_key: %s, agg_id %u", index.c_str(), agg_id); + } else { + while (srv6_prefix_agg_id_set_.find(g_agg_id) != srv6_prefix_agg_id_set_.end()) { + SWSS_LOG_INFO("Agg id %d is busy, try next", g_agg_id); + g_agg_id++; + // restart with 1 if flip + if (g_agg_id == 0) { + g_agg_id = 1; + } + } + agg_id = g_agg_id; + srv6_prefix_agg_id_table_for_nhg_[index].prefix_agg_id = g_agg_id; + // initialize ref_count with 0, will be added in increasePrefixAggIdRefCount() later + srv6_prefix_agg_id_table_for_nhg_[index].ref_count = 0; + srv6_prefix_agg_id_set_.insert(g_agg_id); + SWSS_LOG_INFO("Agg id not exist, create agg_id_key: %s, agg_id %u", index.c_str(), agg_id); + } + + return agg_id; +} + +void Srv6Orch::deleteAggId(const NextHopGroupKey &nhg) +{ + SWSS_LOG_ENTER(); + uint32_t agg_id; + + if (srv6_prefix_agg_id_table_.find(nhg) == srv6_prefix_agg_id_table_.end()) { + return; + } + + agg_id = srv6_prefix_agg_id_table_[nhg].prefix_agg_id; + if (srv6_prefix_agg_id_table_[nhg].ref_count == 0) { + srv6_prefix_agg_id_table_.erase(nhg); + srv6_prefix_agg_id_set_.erase(agg_id); + SWSS_LOG_INFO("Delete Agg id %d, agg_id_key: %s", agg_id, nhg.to_string().c_str()); + } + else + { + SWSS_LOG_INFO("Referencing this prefix agg id %u : %u", agg_id, srv6_prefix_agg_id_table_[nhg].ref_count); + } +} + +void Srv6Orch::deleteAggId(const std::string& index) +{ + SWSS_LOG_ENTER(); + uint32_t agg_id; + + if (srv6_prefix_agg_id_table_for_nhg_.find(index) == srv6_prefix_agg_id_table_for_nhg_.end()) { + return; + } + + agg_id = srv6_prefix_agg_id_table_for_nhg_[index].prefix_agg_id; + if (srv6_prefix_agg_id_table_for_nhg_[index].ref_count == 0) { + srv6_prefix_agg_id_table_for_nhg_.erase(index); + srv6_prefix_agg_id_set_.erase(agg_id); + SWSS_LOG_INFO("Delete Agg id %d, agg_id_key: %s", agg_id, index.c_str()); + } + else + { + SWSS_LOG_INFO("Referencing this prefix agg id %u : %u", agg_id, srv6_prefix_agg_id_table_for_nhg_[index].ref_count); + } +} + +void Srv6Orch::increasePicContextIdRefCount(const std::string &index) +{ + SWSS_LOG_ENTER(); + if (srv6_pic_context_table_.find(index) == srv6_pic_context_table_.end()) + SWSS_LOG_ERROR("Unexpected refcount increase for context id %s", index.c_str()); + else + ++srv6_pic_context_table_[index].ref_count; +} + +void Srv6Orch::decreasePicContextIdRefCount(const std::string &index) +{ + SWSS_LOG_ENTER(); + if (srv6_pic_context_table_.find(index) == srv6_pic_context_table_.end()) + SWSS_LOG_ERROR("Unexpected refcount decrease for context id %s", index.c_str()); + else + --srv6_pic_context_table_[index].ref_count; +} + +void Srv6Orch::increasePrefixAggIdRefCount(const NextHopGroupKey &nhg) +{ + SWSS_LOG_ENTER(); + if (srv6_prefix_agg_id_table_.find(nhg) == srv6_prefix_agg_id_table_.end()) + { + SWSS_LOG_ERROR("Unexpected prefix agg refcount increase for nexthop %s", nhg.to_string().c_str()); + } + else + { + srv6_prefix_agg_id_table_[nhg].ref_count++; + } +} + +void Srv6Orch::increasePrefixAggIdRefCount(const std::string& index) +{ + SWSS_LOG_ENTER(); + if (srv6_prefix_agg_id_table_for_nhg_.find(index) == srv6_prefix_agg_id_table_for_nhg_.end()) + { + SWSS_LOG_ERROR("Unexpected prefix agg refcount increase for nexthop %s", index.c_str()); + } + else + { + ++srv6_prefix_agg_id_table_for_nhg_[index].ref_count; + } +} + +void Srv6Orch::decreasePrefixAggIdRefCount(const NextHopGroupKey &nhg) +{ + SWSS_LOG_ENTER(); + if (srv6_prefix_agg_id_table_.find(nhg) == srv6_prefix_agg_id_table_.end()) + { + SWSS_LOG_ERROR("Unexpected prefix agg refcount decrease for nexthop %s", nhg.to_string().c_str()); + } + else + { + srv6_prefix_agg_id_table_[nhg].ref_count--; + } +} + +void Srv6Orch::decreasePrefixAggIdRefCount(const std::string& index) +{ + SWSS_LOG_ENTER(); + if (srv6_prefix_agg_id_table_for_nhg_.find(index) == srv6_prefix_agg_id_table_for_nhg_.end()) + { + SWSS_LOG_ERROR("Unexpected prefix agg refcount decrease for nexthop %s", index.c_str()); + } + else + { + --srv6_prefix_agg_id_table_for_nhg_[index].ref_count; + } +} + +bool Srv6Orch::srv6P2pTunnelExists(const std::string &endpoint) +{ + if (srv6_p2p_tunnel_table_.find(endpoint) != srv6_p2p_tunnel_table_.end()) + { + return true; + } + return false; +} + +bool Srv6Orch::createSrv6P2pTunnel(const std::string &src, const std::string &endpoint) +{ + SWSS_LOG_ENTER(); + sai_status_t saistatus; + sai_object_id_t srv6_tunnel_map_id; + + sai_attribute_t tunnel_map_attr; + vector tunnel_map_attrs; + + if (srv6P2pTunnelExists(endpoint)) { + return true; + } + + // 0. create tunnel map + tunnel_map_attr.id = SAI_TUNNEL_MAP_ATTR_TYPE; + tunnel_map_attr.value.u32 = SAI_TUNNEL_MAP_TYPE_PREFIX_AGG_ID_TO_SRV6_VPN_SID; + tunnel_map_attrs.push_back(tunnel_map_attr); + + saistatus = sai_tunnel_api->create_tunnel_map(&srv6_tunnel_map_id, gSwitchId, + (uint32_t)tunnel_map_attrs.size(), tunnel_map_attrs.data()); + if (saistatus != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed to create srv6 p2p tunnel map for src_ip: %s dst_ip: %s", src.c_str(), endpoint.c_str()); + return false; + } + + // 1. create tunnel + sai_object_id_t tunnel_id; + sai_attribute_t tunnel_attr; + vector tunnel_attrs; + sai_ip_address_t ipaddr; + + tunnel_attr.id = SAI_TUNNEL_ATTR_TYPE; + tunnel_attr.value.s32 = SAI_TUNNEL_TYPE_SRV6; + tunnel_attrs.push_back(tunnel_attr); + + IpAddress src_ip(src); + ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + memcpy(ipaddr.addr.ip6, src_ip.getV6Addr(), sizeof(ipaddr.addr.ip6)); + tunnel_attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; + tunnel_attr.value.ipaddr = ipaddr; + tunnel_attrs.push_back(tunnel_attr); + + tunnel_attr.id = SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE; + tunnel_attr.value.oid = gUnderlayIfId; + tunnel_attrs.push_back(tunnel_attr); + + sai_object_id_t tunnel_map_list[1]; + tunnel_map_list[0] = srv6_tunnel_map_id; + tunnel_attr.id = SAI_TUNNEL_ATTR_ENCAP_MAPPERS; + tunnel_attr.value.objlist.count = 1; + tunnel_attr.value.objlist.list = tunnel_map_list; + tunnel_attrs.push_back(tunnel_attr); + + tunnel_attr.id = SAI_TUNNEL_ATTR_PEER_MODE; + tunnel_attr.value.u32 = SAI_TUNNEL_PEER_MODE_P2P; + tunnel_attrs.push_back(tunnel_attr); + + IpAddress dst_ip(endpoint); + ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + memcpy(ipaddr.addr.ip6, dst_ip.getV6Addr(), sizeof(ipaddr.addr.ip6)); + tunnel_attr.id = SAI_TUNNEL_ATTR_ENCAP_DST_IP; + tunnel_attr.value.ipaddr = ipaddr; + tunnel_attrs.push_back(tunnel_attr); + + saistatus = sai_tunnel_api->create_tunnel( + &tunnel_id, gSwitchId, (uint32_t)tunnel_attrs.size(), tunnel_attrs.data()); + if (saistatus != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create srv6 p2p tunnel for src ip: %s, dst ip: %s", + src.c_str(), endpoint.c_str()); + + sai_tunnel_api->remove_tunnel_map(srv6_tunnel_map_id); + return false; + } + + srv6_p2p_tunnel_table_[endpoint].tunnel_id = tunnel_id; + srv6_p2p_tunnel_table_[endpoint].tunnel_map_id = srv6_tunnel_map_id; + return true; +} + +bool Srv6Orch::deleteSrv6P2pTunnel(const std::string &endpoint) +{ + if (srv6_p2p_tunnel_table_.find(endpoint) == srv6_p2p_tunnel_table_.end()) + { + return true; + } + + if (srv6P2pTunnelNexthopSize(endpoint) || srv6P2pTunnelEntrySize(endpoint)) + { + SWSS_LOG_INFO("There are still SRv6 VPNs or Nexthops referencing this srv6 p2p tunnel object dst %s", endpoint.c_str()); + return true; + } + + sai_status_t status; + + // 0. remove tunnel + status = sai_tunnel_api->remove_tunnel(srv6_p2p_tunnel_table_[endpoint].tunnel_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove SRV6 p2p tunnel object for dst_ip: %s", endpoint.c_str()); + return false; + } + + // 1. remove tunnel map + status = sai_tunnel_api->remove_tunnel_map(srv6_p2p_tunnel_table_[endpoint].tunnel_map_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove SRV6 tunnel map object for dst_ip: %s", endpoint.c_str()); + return false; + } + + srv6_p2p_tunnel_table_.erase(endpoint); + return true; +} + +void Srv6Orch::srv6P2ptunnelUpdateNexthops(const NextHopKey &nhkey, bool insert) +{ + if (insert) + { + srv6_p2p_tunnel_table_[nhkey.ip_address.to_string()].nexthops.insert(nhkey); + } + else + { + srv6_p2p_tunnel_table_[nhkey.ip_address.to_string()].nexthops.erase(nhkey); + } +} + +size_t Srv6Orch::srv6P2pTunnelNexthopSize(const std::string &endpoint) +{ + return srv6_p2p_tunnel_table_[endpoint].nexthops.size(); +} + +void Srv6Orch::srv6P2pTunnelUpdateEntries(const Srv6TunnelMapEntryKey &tmek, bool insert) +{ + if (insert) + srv6_p2p_tunnel_table_[tmek.endpoint].tunnel_map_entries.insert(tmek); + else + srv6_p2p_tunnel_table_[tmek.endpoint].tunnel_map_entries.erase(tmek); +} + +size_t Srv6Orch::srv6P2pTunnelEntrySize(const std::string &endpoint) +{ + return srv6_p2p_tunnel_table_[endpoint].tunnel_map_entries.size(); +} + +bool Srv6Orch::createSrv6Vpns(const Srv6PicContextInfo &pci, const std::string &context_id) +{ + auto agg_id = getAggId(context_id); + for (size_t i = 0; i < pci.nexthops.size(); ++i) + { + if (!createSrv6Vpn(pci.nexthops[i], pci.sids[i], agg_id)) + { + for (size_t j = 0; j < i; ++j) + { + deleteSrv6Vpn(pci.nexthops[j], pci.sids[j], agg_id); + } + deleteAggId(context_id); + return false; + } + } + + increasePrefixAggIdRefCount(context_id); + + return true; +} + +bool Srv6Orch::createSrv6Vpn(const std::string &endpoint, const std::string &sid, const uint32_t prefix_agg_id) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + Srv6TunnelMapEntryKey tmek; + tmek.endpoint = endpoint; + tmek.vpn_sid = sid; + tmek.prefix_agg_id = prefix_agg_id; + + if (srv6_tunnel_map_entry_table_.find(tmek) != srv6_tunnel_map_entry_table_.end()) + { + srv6_tunnel_map_entry_table_[tmek].ref_count++; + return true; + } + + if (srv6_p2p_tunnel_table_.find(endpoint) == srv6_p2p_tunnel_table_.end()) + { + SWSS_LOG_ERROR("Tunnel map for endpoint %s does not exist", endpoint.c_str()); + return false; + } + sai_object_id_t tunnel_map_id = srv6_p2p_tunnel_table_[endpoint].tunnel_map_id; + + // 1. create vpn tunnel_map entry + sai_attribute_t tunnel_map_entry_attr; + vector tunnel_map_entry_attrs; + sai_object_id_t tunnel_entry_id; + + tunnel_map_entry_attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE; + tunnel_map_entry_attr.value.u32 = SAI_TUNNEL_MAP_TYPE_PREFIX_AGG_ID_TO_SRV6_VPN_SID; + tunnel_map_entry_attrs.push_back(tunnel_map_entry_attr); + + tunnel_map_entry_attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP; + tunnel_map_entry_attr.value.oid = tunnel_map_id; + tunnel_map_entry_attrs.push_back(tunnel_map_entry_attr); + + tunnel_map_entry_attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_PREFIX_AGG_ID_KEY; + tunnel_map_entry_attr.value.u32 = tmek.prefix_agg_id; + tunnel_map_entry_attrs.push_back(tunnel_map_entry_attr); + + IpAddress vpn_sid(tmek.vpn_sid); + tunnel_map_entry_attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_SRV6_VPN_SID_VALUE; + memcpy(tunnel_map_entry_attr.value.ip6, vpn_sid.getV6Addr(), sizeof(sai_ip6_t)); + tunnel_map_entry_attrs.push_back(tunnel_map_entry_attr); + + status = sai_tunnel_api->create_tunnel_map_entry(&tunnel_entry_id, gSwitchId, + (uint32_t)tunnel_map_entry_attrs.size(), + tunnel_map_entry_attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create vpn tunnel_map entry for vpn_sid: %s", tmek.vpn_sid.c_str()); + return false; + } + + // add reference for tunnel map entry + srv6_tunnel_map_entry_table_[tmek].tunnel_map_entry_id = tunnel_entry_id; + srv6_tunnel_map_entry_table_[tmek].ref_count = 1; + + srv6P2pTunnelUpdateEntries(tmek, true); + return true; +} + +bool Srv6Orch::deleteSrv6Vpns(const std::string &context_id) +{ + const auto &it = srv6_pic_context_table_.find(context_id); + if (it == srv6_pic_context_table_.end()) + { + SWSS_LOG_ERROR("Failed to find context id %s", context_id.c_str()); + return false; + } + + bool success = true; + auto agg_id = getAggId(context_id); + for (size_t i = 0; i < it->second.nexthops.size(); ++i) + { + if (!deleteSrv6Vpn(it->second.nexthops[i], it->second.sids[i], agg_id)) + { + success = false; + } + } + + if (success) + { + decreasePrefixAggIdRefCount(context_id); + } + deleteAggId(context_id); + + return success; +} + +bool Srv6Orch::deleteSrv6Vpn(const std::string &endpoint, const std::string &sid, const uint32_t prefix_agg_id) +{ + SWSS_LOG_ENTER(); + sai_status_t status; + + // 1. remove tunnel_map entry if need + sai_object_id_t tunnel_entry_id; + + Srv6TunnelMapEntryKey tmek; + tmek.endpoint = endpoint; + tmek.vpn_sid = sid; + tmek.prefix_agg_id = prefix_agg_id; + + if (srv6_tunnel_map_entry_table_.find(tmek) == srv6_tunnel_map_entry_table_.end()) + { + return true; + } + + srv6_tunnel_map_entry_table_[tmek].ref_count--; + if (srv6_tunnel_map_entry_table_[tmek].ref_count == 0) + { + tunnel_entry_id = srv6_tunnel_map_entry_table_[tmek].tunnel_map_entry_id; + status = sai_tunnel_api->remove_tunnel_map_entry(tunnel_entry_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove nexthop tunnel map entry (endpoint: %s, sid: %s, agg_id: %u)", + tmek.endpoint.c_str(), tmek.vpn_sid.c_str(), tmek.prefix_agg_id); + return false; + } + srv6_tunnel_map_entry_table_.erase(tmek); + + srv6P2pTunnelUpdateEntries(tmek, false); + if (!deleteSrv6P2pTunnel(tmek.endpoint)) + { + SWSS_LOG_ERROR("Failed to remove SRV6 p2p tunnel object for dst %s,", endpoint.c_str()); + return false; + } + } + else + { + SWSS_LOG_INFO("Nexthops referencing this tunnel map entry endpoint %s, vpn_sid %s, prefix_agg_id %u : %u", + tmek.endpoint.c_str(), + tmek.vpn_sid.c_str(), + tmek.prefix_agg_id, + srv6_tunnel_map_entry_table_[tmek].ref_count); + } + return true; +} + void Srv6Orch::doTaskMySidTable(const KeyOpFieldsValuesTuple & tuple) { SWSS_LOG_ENTER(); @@ -901,9 +1530,85 @@ void Srv6Orch::doTaskMySidTable(const KeyOpFieldsValuesTuple & tuple) } } +task_process_status Srv6Orch::doTaskPicContextTable(const KeyOpFieldsValuesTuple &tuple) +{ + SWSS_LOG_ENTER(); + string op = kfvOp(tuple); + string key = kfvKey(tuple); + const auto &it = srv6_pic_context_table_.find(key); + if (op == SET_COMMAND) + { + if (it != srv6_pic_context_table_.end()) + { + SWSS_LOG_ERROR("update is not allowed for pic context table"); + return task_duplicated; + } + Srv6PicContextInfo pci; + pci.ref_count = 0; + for (auto i : kfvFieldsValues(tuple)) + { + if (fvField(i) == "nexthop") + { + pci.nexthops = tokenize(fvValue(i), ','); + } + else if (fvField(i) == "vpn_sid") + { + pci.sids = tokenize(fvValue(i), ','); + } + } + if (pci.nexthops.size() != pci.sids.size()) + { + SWSS_LOG_ERROR("inconsistent number of endpoints(%lu) and vpn sids(%lu)", + pci.nexthops.size(), pci.sids.size()); + return task_failed; + } + + if (!createSrv6Vpns(pci ,key)) + { + SWSS_LOG_ERROR("Failed to create SRv6 VPNs for context id %s", key.c_str()); + return task_need_retry; + } + + srv6_pic_context_table_[key] = pci; + } + else if (op == DEL_COMMAND) + { + if (it == srv6_pic_context_table_.end()) + { + SWSS_LOG_INFO("Unable to find pic context entry for key %s", key.c_str()); + return task_ignore; + } + else if (it->second.ref_count != 0) + { + SWSS_LOG_INFO("Unable to delete context id %s, because it is referenced %u times", key.c_str(), it->second.ref_count); + return task_need_retry; + } + else if (!deleteSrv6Vpns(key)) + { + SWSS_LOG_ERROR("Failed to delete SRv6 VPNs for context id %s", key.c_str()); + return task_need_retry; + } + srv6_pic_context_table_.erase(it); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + return task_ignore; + } + return task_success; +} + +bool Srv6Orch::contextIdExists(const std::string &context_id) +{ + if (srv6_pic_context_table_.find(context_id) == srv6_pic_context_table_.end()) + return false; + return true; +} + void Srv6Orch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); + task_process_status task_status; const string &table_name = consumer.getTableName(); auto it = consumer.m_toSync.begin(); while(it != consumer.m_toSync.end()) @@ -918,6 +1623,15 @@ void Srv6Orch::doTask(Consumer &consumer) { doTaskMySidTable(t); } + else if (table_name == APP_PIC_CONTEXT_TABLE_NAME) + { + task_status = doTaskPicContextTable(t); + if (task_status == task_need_retry) + { + ++it; + continue; + } + } else { SWSS_LOG_ERROR("Unknown table : %s",table_name.c_str()); diff --git a/orchagent/srv6orch.h b/orchagent/srv6orch.h index a3e39b5632..11157ac739 100644 --- a/orchagent/srv6orch.h +++ b/orchagent/srv6orch.h @@ -46,10 +46,74 @@ struct MySidEntry string endAdjString; // Used for END.X, END.DX4, END.DX6 }; +struct Srv6TunnelMapEntryKey +{ + string endpoint; + string vpn_sid; + uint32_t prefix_agg_id; + + bool operator==(const Srv6TunnelMapEntryKey &o) const + { + return tie(endpoint, vpn_sid, prefix_agg_id) == + tie(o.endpoint, o.vpn_sid, o.prefix_agg_id); + } + + bool operator<(const Srv6TunnelMapEntryKey &o) const + { + return tie(endpoint, vpn_sid, prefix_agg_id) < + tie(o.endpoint, o.vpn_sid, o.prefix_agg_id); + } + + bool operator!=(const Srv6TunnelMapEntryKey &o) const + { + return !(*this == o); + } +}; + +struct Srv6TunnelMapEntryEntry +{ + sai_object_id_t tunnel_map_entry_id; + + // for sid remarking + sai_object_id_t inner_tunnel_map_id; + map inner_tunnel_map_entry_ids; + + uint32_t ref_count; +}; + +struct P2pTunnelEntry +{ + sai_object_id_t tunnel_id; + sai_object_id_t tunnel_map_id; + + set nexthops; + set tunnel_map_entries; +}; + +struct Srv6PrefixAggIdEntry +{ + uint32_t prefix_agg_id; + + uint32_t ref_count; +}; + +struct Srv6PicContextInfo +{ + vector nexthops; + vector sids; + uint32_t ref_count; +}; + typedef unordered_map SidTable; typedef unordered_map Srv6TunnelTable; typedef map Srv6NextHopTable; typedef unordered_map Srv6MySidTable; +typedef map Srv6P2pTunnelTable; +typedef map Srv6PrefixAggIdTable; +typedef map Srv6PrefixAggIdTableForNhg; +typedef set Srv6PrefixAggIdSet; +typedef map Srv6TunnelMapEntryTable; +typedef map Srv6PicContextTable; #define SID_LIST_DELIMITER ',' #define MY_SID_KEY_DELIMITER ':' @@ -62,7 +126,8 @@ class Srv6Orch : public Orch, public Observer m_switchOrch(switchOrch), m_neighOrch(neighOrch), m_sidTable(applDb, APP_SRV6_SID_LIST_TABLE_NAME), - m_mysidTable(applDb, APP_SRV6_MY_SID_TABLE_NAME) + m_mysidTable(applDb, APP_SRV6_MY_SID_TABLE_NAME), + m_piccontextTable(applDb, APP_PIC_CONTEXT_TABLE_NAME) { m_neighOrch->attach(this); } @@ -70,18 +135,33 @@ class Srv6Orch : public Orch, public Observer { m_neighOrch->detach(this); } + void increasePicContextIdRefCount(const std::string&); + void decreasePicContextIdRefCount(const std::string&); + void increasePrefixAggIdRefCount(const NextHopGroupKey&); + void increasePrefixAggIdRefCount(const std::string&); + void decreasePrefixAggIdRefCount(const NextHopGroupKey&); + void decreasePrefixAggIdRefCount(const std::string&); + uint32_t getAggId(const NextHopGroupKey &nhg); + uint32_t getAggId(const std::string& index); + void deleteAggId(const NextHopGroupKey &nhg); + void deleteAggId(const std::string& index); + bool createSrv6NexthopWithoutVpn(const NextHopKey &nhKey, sai_object_id_t &nexthop_id); bool srv6Nexthops(const NextHopGroupKey &nextHops, sai_object_id_t &next_hop_id); - bool removeSrv6Nexthops(const NextHopGroupKey &nhg); + bool removeSrv6NexthopWithoutVpn(const NextHopKey &nhKey); + bool removeSrv6Nexthops(const std::vector &nhgv); void update(SubjectType, void *); + bool contextIdExists(const std::string &context_id); private: void doTask(Consumer &consumer); void doTaskSidTable(const KeyOpFieldsValuesTuple &tuple); void doTaskMySidTable(const KeyOpFieldsValuesTuple &tuple); + task_process_status doTaskPicContextTable(const KeyOpFieldsValuesTuple &tuple); bool createUpdateSidList(const string seg_name, const string ips, const string sidlist_type); bool deleteSidList(const string seg_name); bool createSrv6Tunnel(const string srv6_source); bool createSrv6Nexthop(const NextHopKey &nh); + bool deleteSrv6Nexthop(const NextHopKey &nh); bool srv6NexthopExists(const NextHopKey &nh); bool createUpdateMysidEntry(string my_sid_string, const string vrf, const string adj, const string end_action); bool deleteMysidEntry(const string my_sid_string); @@ -92,15 +172,34 @@ class Srv6Orch : public Orch, public Observer bool mySidNextHopRequired(const sai_my_sid_entry_endpoint_behavior_t end_behavior); void srv6TunnelUpdateNexthops(const string srv6_source, const NextHopKey nhkey, bool insert); size_t srv6TunnelNexthopSize(const string srv6_source); + bool srv6P2pTunnelExists(const string &endpoint); + bool createSrv6P2pTunnel(const string &src, const string &endpoint); + bool deleteSrv6P2pTunnel(const string &endpoint); + void srv6P2ptunnelUpdateNexthops(const NextHopKey &nhkey, bool insert); + size_t srv6P2pTunnelNexthopSize(const string &endpoint); + void srv6P2pTunnelUpdateEntries(const Srv6TunnelMapEntryKey &tmek, bool insert); + size_t srv6P2pTunnelEntrySize(const string &endpoint); + bool createSrv6Vpn(const string &endpoint, const string &sid, const uint32_t prefix_agg_id); + bool createSrv6Vpns(const Srv6PicContextInfo &pci ,const std::string &context_id); + bool deleteSrv6Vpn(const string &endpoint, const string &sid, const uint32_t prefix_agg_id); + bool deleteSrv6Vpns(const std::string &context_id); void updateNeighbor(const NeighborUpdate& update); ProducerStateTable m_sidTable; ProducerStateTable m_mysidTable; + ProducerStateTable m_piccontextTable; SidTable sid_table_; Srv6TunnelTable srv6_tunnel_table_; Srv6NextHopTable srv6_nexthop_table_; Srv6MySidTable srv6_my_sid_table_; + Srv6P2pTunnelTable srv6_p2p_tunnel_table_; + Srv6PrefixAggIdTable srv6_prefix_agg_id_table_; + Srv6PrefixAggIdTableForNhg srv6_prefix_agg_id_table_for_nhg_; + Srv6PrefixAggIdSet srv6_prefix_agg_id_set_; + Srv6TunnelMapEntryTable srv6_tunnel_map_entry_table_; + Srv6PicContextTable srv6_pic_context_table_; + VRFOrch *m_vrfOrch; SwitchOrch *m_switchOrch; NeighOrch *m_neighOrch; diff --git a/tests/test_srv6.py b/tests/test_srv6.py index 3ce19421b0..16108eef6c 100644 --- a/tests/test_srv6.py +++ b/tests/test_srv6.py @@ -18,6 +18,13 @@ def get_created_entry(db, table, existed_entries): assert len(new_entries) == 1, "Wrong number of created entries." return new_entries[0] +def get_created_entries(db, table, existed_entries, number): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) == number, "Wrong number of created entries." + return new_entries + class TestSrv6Mysid(object): def setup_db(self, dvs): self.pdb = dvs.get_app_db() @@ -240,7 +247,7 @@ def test_mysid(self, dvs, testlog): if fv[0] == "SAI_MY_SID_ENTRY_ATTR_ENDPOINT_BEHAVIOR": assert fv[1] == "SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_X" elif fv[0] == "SAI_MY_SID_ENTRY_ATTR_ENDPOINT_BEHAVIOR_FLAVOR": - assert fv[1] == "SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_FLAVOR_PSP_AND_USD" + assert fv[1] == "SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_FLAVOR_PSP_AND_USP" # create MySID END.DX4 fvs = swsscommon.FieldValuePairs([('action', 'end.dx4'), ('adj', '192.0.2.1')]) @@ -425,7 +432,7 @@ def test_mysid_l3adj(self, dvs, testlog): if fv[0] == "SAI_MY_SID_ENTRY_ATTR_ENDPOINT_BEHAVIOR": assert fv[1] == "SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_X" elif fv[0] == "SAI_MY_SID_ENTRY_ATTR_ENDPOINT_BEHAVIOR_FLAVOR": - assert fv[1] == "SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_FLAVOR_PSP_AND_USD" + assert fv[1] == "SAI_MY_SID_ENTRY_ENDPOINT_BEHAVIOR_FLAVOR_PSP_AND_USP" # remove neighbor self.remove_neighbor("Ethernet104", "2001::1") @@ -475,7 +482,7 @@ def create_srv6_route(self, routeip,segname,segsrc): table = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" existed_entries = get_exist_entries(self.adb.db_connection, table) - fvs=swsscommon.FieldValuePairs([('seg_src',segsrc),('segment',segname)]) + fvs=swsscommon.FieldValuePairs([('seg_src',segsrc), ('segment',segname), ('nexthop','0.0.0.0'), ('ifname','unknown')]) routetbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "ROUTE_TABLE") routetbl.set(routeip,fvs) @@ -627,6 +634,532 @@ def test_srv6(self, dvs, testlog): assert nexthop_entries == get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") assert route_entries == get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") +class TestSrv6Vpn(object): + def setup_db(self, dvs): + self.pdb = dvs.get_app_db() + self.adb = dvs.get_asic_db() + self.cdb = dvs.get_config_db() + + def create_srv6_vpn_route(self, routeip, nexthop, segsrc, vpn_sid, ifname): + table = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" + existed_entries = get_exist_entries(self.adb.db_connection, table) + + fvs=swsscommon.FieldValuePairs([('seg_src', segsrc), ('nexthop', nexthop), ('vpn_sid', vpn_sid), ('ifname', ifname)]) + routetbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "ROUTE_TABLE") + routetbl.set(routeip,fvs) + + self.adb.wait_for_n_keys(table, len(existed_entries) + 1) + return get_created_entry(self.adb.db_connection, table, existed_entries) + + def create_srv6_vpn_route_with_nhg(self, routeip, nhg_index, pic_ctx_index): + table = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" + existed_entries = get_exist_entries(self.adb.db_connection, table) + + fvs=swsscommon.FieldValuePairs([('nexthop_group', nhg_index), ('pic_context_id', pic_ctx_index)]) + routetbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "ROUTE_TABLE") + routetbl.set(routeip,fvs) + + self.adb.wait_for_n_keys(table, len(existed_entries) + 1) + return get_created_entry(self.adb.db_connection, table, existed_entries) + + def update_srv6_vpn_route_attribute_with_nhg(self, routeip, nhg_index, pic_ctx_index): + fvs=swsscommon.FieldValuePairs([('nexthop_group', nhg_index), ('pic_context_id', pic_ctx_index)]) + routetbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "ROUTE_TABLE") + routetbl.set(routeip,fvs) + return True + + def update_srv6_vpn_route_attribute(self, routeip, nexthops, segsrc_list, vpn_list, ifname_list): + fvs=swsscommon.FieldValuePairs([('seg_src', ",".join(segsrc_list)), ('nexthop', ",".join(nexthops)), ('vpn_sid', ",".join(vpn_list)), ('ifname', ",".join(ifname_list))]) + routetbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "ROUTE_TABLE") + routetbl.set(routeip,fvs) + return True + + def remove_srv6_route(self, routeip): + routetbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "ROUTE_TABLE") + routetbl._del(routeip) + + def create_nhg(self, nhg_index, nexthops, segsrc_list, ifname_list): + table = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP" + existed_entries = get_exist_entries(self.adb.db_connection, table) + + fvs=swsscommon.FieldValuePairs([('seg_src', ",".join(segsrc_list)), ('nexthop', ",".join(nexthops)), ('ifname', ",".join(ifname_list))]) + nhgtbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "NEXTHOP_GROUP_TABLE") + nhgtbl.set(nhg_index,fvs) + + self.adb.wait_for_n_keys(table, len(existed_entries) + 1) + return get_created_entry(self.adb.db_connection, table, existed_entries) + + def remove_nhg(self, nhg_index): + nhgtbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "NEXTHOP_GROUP_TABLE") + nhgtbl._del(nhg_index) + + def create_pic_context(self, pic_ctx_id, nexthops, vpn_list): + table = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY" + existed_entries = get_exist_entries(self.adb.db_connection, table) + + fvs=swsscommon.FieldValuePairs([('nexthop', ",".join(nexthops)), ('vpn_sid', ",".join(vpn_list))]) + pictbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "PIC_CONTEXT_TABLE") + pictbl.set(pic_ctx_id,fvs) + + self.adb.wait_for_n_keys(table, len(existed_entries) + len(vpn_list)) + return get_created_entries(self.adb.db_connection, table, existed_entries, len(vpn_list)) + + def remove_pic_context(self, pic_ctx_id): + pictbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "PIC_CONTEXT_TABLE") + pictbl._del(pic_ctx_id) + + + def check_deleted_route_entries(self, destinations): + def _access_function(): + route_entries = self.adb.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + route_destinations = [json.loads(route_entry)["dest"] for route_entry in route_entries] + return (all(destination not in route_destinations for destination in destinations), None) + + wait_for_result(_access_function) + + def test_srv6_vpn_with_single_nh(self, dvs, testlog): + self.setup_db(dvs) + dvs.setup_db() + + # save exist asic db entries + tunnel_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") + nexthop_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") + route_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + vpn_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_TUNNEL_MAP_TYPE_PREFIX_AGG_ID_TO_SRV6_VPN_SID") + map_entry_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") + map_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") + + # create v4 route with vpn sid + route_key = self.create_srv6_vpn_route('5000::/64', '2001::1', '1001:2000::1', '3000::1', 'unknown') + nexthop_id = get_created_entry(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP", nexthop_entries) + tunnel_id = get_created_entry(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL", tunnel_entries) + map_entry_id = get_created_entry(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY", map_entry_entries) + map_id = get_created_entry(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP", map_entries) + prefix_agg_id = "1" + + # check ASIC SAI_OBJECT_TYPE_ROUTE_ENTRY database + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + (status, fvs) = tbl.get(route_key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ROUTE_ENTRY_ATTR_PREFIX_AGG_ID": + assert prefix_agg_id == fv[1] + + # check ASIC SAI_OBJECT_TYPE_TUNNEL_MAP database + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") + (status, fvs) = tbl.get(map_id) + assert status == True + for fv in fvs: + if fv[0] == "SAI_TUNNEL_MAP_ATTR_TYPE": + assert fv[1] == "SAI_TUNNEL_MAP_TYPE_PREFIX_AGG_ID_TO_SRV6_VPN_SID" + + # check ASIC SAI_OBJECT_TYPE_TUNNEL database + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") + (status, fvs) = tbl.get(tunnel_id) + assert status == True + for fv in fvs: + if fv[0] == "SAI_TUNNEL_ATTR_PEER_MODE": + assert fv[1] == "SAI_TUNNEL_PEER_MODE_P2P" + + # check vpn sid value in SRv6 route is created + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") + (status, fvs) = tbl.get(map_entry_id) + assert status == True + for fv in fvs: + if fv[0] == "SAI_TUNNEL_MAP_ENTRY_ATTR_SRV6_VPN_SID_VALUE": + assert fv[1] == "3000::1" + if fv[0] == "SAI_TUNNEL_MAP_ENTRY_ATTR_PREFIX_AGG_ID_KEY": + assert fv[1] == prefix_agg_id + + # check sid list value in ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP is created + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") + (status, fvs) = tbl.get(nexthop_id) + assert status == True + for fv in fvs: + if fv[0] == "SAI_NEXT_HOP_ATTR_TYPE": + assert fv[1] == "SAI_NEXT_HOP_TYPE_SRV6_SIDLIST" + + self.remove_srv6_route('5000::/64') + self.check_deleted_route_entries('5000::/64') + time.sleep(5) + # check ASIC SAI_OBJECT_TYPE_TUNNEL_MAP is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") + (status, fvs) = tbl.get(map_id) + assert status == False + + # check ASIC SAI_OBJECT_TYPE_TUNNEL is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") + (status, fvs) = tbl.get(tunnel_id) + assert status == False + + # check vpn sid value in SRv6 route is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") + (status, fvs) = tbl.get(map_entry_id) + assert status == False + + # check nexthop id in SRv6 route is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") + (status, fvs) = tbl.get(nexthop_id) + assert status == False + + # check ASIC SAI_OBJECT_TYPE_ROUTE_ENTRY is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + (status, fvs) = tbl.get(route_key) + assert status == False + + def test_pic(self, dvs, testlog): + self.setup_db(dvs) + dvs.setup_db() + + segsrc_list = [] + nexthop_list = [] + ifname_list = [] + vpn_list = [] + nhg_index = '100' + pic_ctx_index = '200' + + # save exist asic db entries + tunnel_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") + nexthop_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") + map_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") + nexthop_group_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP") + nexthop_group_member_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") + map_entry_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") + + segsrc_list.append('1001:2000::1') + segsrc_list.append('1001:2000::1') + + nexthop_list.append('2000::1') + nexthop_list.append('2000::2') + + ifname_list.append('unknown') + ifname_list.append('unknown') + + vpn_list.append('3000::1') + vpn_list.append('3000::2') + + nhg_key = self.create_nhg(nhg_index, nexthop_list, segsrc_list, ifname_list) + tunnel_ids = get_created_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL", tunnel_entries, 2) + nh_ids = get_created_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP", nexthop_entries, 2) + nhg_id = get_created_entry(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP", nexthop_group_entries) + nhg_mem = get_created_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER", nexthop_group_member_entries, 2) + map_ids = get_created_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP", map_entries, 2) + + nh_ids = sorted(nh_ids) + nhg_mem = sorted(nhg_mem) + + # check ASIC SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER database + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") + (status, fvs) = tbl.get(nhg_mem[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": + assert fv[1] == nhg_id + elif fv[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID": + assert fv[1] == nh_ids[0] + + # check ASIC SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER database + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") + (status, fvs) = tbl.get(nhg_mem[1]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": + assert fv[1] == nhg_id + elif fv[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID": + assert fv[1] == nh_ids[1] + + # check ASIC SAI_OBJECT_TYPE_TUNNEL_MAP database + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") + for map_id in map_ids: + (status, fvs) = tbl.get(map_id) + assert status == True + for fv in fvs: + if fv[0] == "SAI_TUNNEL_MAP_ATTR_TYPE": + assert fv[1] == "SAI_TUNNEL_MAP_TYPE_PREFIX_AGG_ID_TO_SRV6_VPN_SID" + + # check ASIC SAI_OBJECT_TYPE_TUNNEL database + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") + for tunnel_id in tunnel_ids: + (status, fvs) = tbl.get(tunnel_id) + assert status == True + for fv in fvs: + if fv[0] == "SAI_TUNNEL_ATTR_PEER_MODE": + assert fv[1] == "SAI_TUNNEL_PEER_MODE_P2P" + + # check sid list value in ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP is created + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") + for nh_id in nh_ids: + (status, fvs) = tbl.get(nh_id) + assert status == True + for fv in fvs: + if fv[0] == "SAI_NEXT_HOP_ATTR_TYPE": + assert fv[1] == "SAI_NEXT_HOP_TYPE_SRV6_SIDLIST" + + pic_ctx_key = self.create_pic_context(pic_ctx_index, nexthop_list, vpn_list) + map_entry_ids = get_created_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY", map_entry_entries, 2) + prefix_agg_id = "1" + + # check vpn sid value in SRv6 route is created + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") + for map_entry_id in map_entry_ids: + (status, fvs) = tbl.get(map_entry_id) + assert status == True + for fv in fvs: + if fv[0] == "SAI_TUNNEL_MAP_ENTRY_ATTR_PREFIX_AGG_ID_KEY": + assert fv[1] == prefix_agg_id + + # remove nhg and pic_context + self.remove_nhg(nhg_index) + self.remove_pic_context(pic_ctx_index) + + time.sleep(5) + # check ASIC SAI_OBJECT_TYPE_NEXT_HOP_GROUP is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP") + (status, fvs) = tbl.get(nhg_id) + assert status == False + + # check ASIC SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") + for nhg_mem_id in nhg_mem: + (status, fvs) = tbl.get(nhg_mem_id) + assert status == False + + # check ASIC SAI_OBJECT_TYPE_TUNNEL_MAP is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") + for map_id in map_ids: + (status, fvs) = tbl.get(map_id) + assert status == False + + # check ASIC SAI_OBJECT_TYPE_TUNNEL is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") + for tunnel_id in tunnel_ids: + (status, fvs) = tbl.get(tunnel_id) + assert status == False + + # check next hop in ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") + for nh_id in nh_ids: + (status, fvs) = tbl.get(nh_id) + assert status == False + + # check vpn sid value in SRv6 route is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") + for map_entry_id in map_entry_ids: + (status, fvs) = tbl.get(map_entry_id) + assert status == False + + def test_srv6_vpn_with_nhg(self, dvs, testlog): + self.setup_db(dvs) + dvs.setup_db() + + segsrc_list = [] + nexthop_list = [] + vpn_list = [] + ifname_list = [] + nhg_index = '100' + pic_ctx_index = '200' + + # save exist asic db entries + nexthop_group_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP") + + segsrc_list.append('1001:2000::1') + segsrc_list.append('1001:2000::1') + + nexthop_list.append('2000::1') + nexthop_list.append('2000::2') + + vpn_list.append('3000::1') + vpn_list.append('3000::2') + + ifname_list.append('unknown') + ifname_list.append('unknown') + + + nhg_key = self.create_nhg(nhg_index, nexthop_list, segsrc_list, ifname_list) + pic_ctx_key = self.create_pic_context(pic_ctx_index, nexthop_list, vpn_list) + route_key = self.create_srv6_vpn_route_with_nhg('5000::/64', nhg_index, pic_ctx_index) + + + nhg_id = get_created_entry(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP", nexthop_group_entries) + prefix_agg_id = "1" + + # check ASIC SAI_OBJECT_TYPE_ROUTE_ENTRY database + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + (status, fvs) = tbl.get(route_key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": + assert fv[1] == nhg_id + if fv[0] == "SAI_ROUTE_ENTRY_ATTR_PREFIX_AGG_ID": + assert fv[1] == prefix_agg_id + + + route_key_new = self.create_srv6_vpn_route_with_nhg('5001::/64', nhg_index, pic_ctx_index) + + # check ASIC SAI_OBJECT_TYPE_ROUTE_ENTRY database + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + (status, fvs) = tbl.get(route_key_new) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": + assert fv[1] == nhg_id + if fv[0] == "SAI_ROUTE_ENTRY_ATTR_PREFIX_AGG_ID": + assert fv[1] == prefix_agg_id + + # remove routes + self.remove_srv6_route('5001::/64') + self.check_deleted_route_entries('5001::/64') + + time.sleep(5) + # check ASIC SAI_OBJECT_TYPE_ROUTE_ENTRY is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + (status, fvs) = tbl.get(route_key_new) + assert status == False + + # remove routes + self.remove_srv6_route('5000::/64') + self.check_deleted_route_entries('5000::/64') + + time.sleep(5) + # check ASIC SAI_OBJECT_TYPE_ROUTE_ENTRY is removed + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + (status, fvs) = tbl.get(route_key) + assert status == False + + # remove nhg and pic_context + self.remove_nhg(nhg_index) + self.remove_pic_context(pic_ctx_index) + + def test_srv6_vpn_nh_update(self, dvs, testlog): + self.setup_db(dvs) + dvs.setup_db() + + segsrc_list = [] + nexthop_list = [] + vpn_list = [] + ifname_list = [] + + # save exist asic db entries + tunnel_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") + nexthop_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") + route_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + vpn_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_TUNNEL_MAP_TYPE_PREFIX_AGG_ID_TO_SRV6_VPN_SID") + map_entry_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") + map_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") + + nexthop_group_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP") + nexthop_group_member_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") + map_entry_prefix_agg_id = "1" + route_entry_prefix_agg_id = "1" + route_entry_next_hop_id = "1" + + # create v4 route with vpn sid + route_key = self.create_srv6_vpn_route('5000::/64', '2000::1', '1001:2000::1', '3000::1', 'unknown') + map_entry_id = get_created_entry(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY", map_entry_entries) + + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") + (status, fvs) = tbl.get(map_entry_id) + assert status == True + for fv in fvs: + if fv[0] == "SAI_TUNNEL_MAP_ENTRY_ATTR_PREFIX_AGG_ID_KEY": + map_entry_prefix_agg_id = fv[1] + + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + (status, fvs) = tbl.get(route_key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": + route_entry_next_hop_id = fv[1] + if fv[0] == "SAI_ROUTE_ENTRY_ATTR_PREFIX_AGG_ID": + route_entry_prefix_agg_id = fv[1] + + segsrc_list.append('1001:2000::1') + segsrc_list.append('1001:2000::1') + + nexthop_list.append('2000::1') + nexthop_list.append('2000::2') + + vpn_list.append('3000::1') + vpn_list.append('3000::2') + + ifname_list.append('unknown') + ifname_list.append('unknown') + + nhg_index = '100' + pic_ctx_index = '200' + + map_entry_entries = get_exist_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") + + nhg_key = self.create_nhg(nhg_index, nexthop_list, segsrc_list, ifname_list) + pic_ctx_key = self.create_pic_context(pic_ctx_index, nexthop_list, vpn_list) + self.update_srv6_vpn_route_attribute_with_nhg('5000::/64', nhg_index, pic_ctx_index) + + + time.sleep(5) + nh_ids = get_created_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP", nexthop_entries, 2) + nhg_id = get_created_entry(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP", nexthop_group_entries) + nhg_mem = get_created_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER", nexthop_group_member_entries, 2) + + map_entry_ids = get_created_entries(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY", map_entry_entries, 2) + map_entry_id_group = "1" + + for map_id in map_entry_ids: + if map_id != map_entry_id: + map_entry_id_group = map_id + break + + nh_ids = sorted(nh_ids) + nhg_mem = sorted(nhg_mem) + + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") + (status, fvs) = tbl.get(map_entry_id_group) + assert status == True + for fv in fvs: + if fv[0] == "SAI_TUNNEL_MAP_ENTRY_ATTR_PREFIX_AGG_ID_KEY": + assert fv[1] != map_entry_prefix_agg_id + + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP") + (status, fvs) = tbl.get(nhg_id) + assert status == True + + # check ASIC SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER database + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") + (status, fvs) = tbl.get(nhg_mem[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": + assert fv[1] == nhg_id + elif fv[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID": + assert fv[1] == nh_ids[0] + + # check ASIC SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER database + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") + (status, fvs) = tbl.get(nhg_mem[1]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": + assert fv[1] == nhg_id + elif fv[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID": + assert fv[1] == nh_ids[1] + + tbl = swsscommon.Table(self.adb.db_connection, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + (status, fvs) = tbl.get(route_key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": + assert fv[1] != route_entry_next_hop_id + if fv[0] == "SAI_ROUTE_ENTRY_ATTR_PREFIX_AGG_ID": + assert fv[1] != route_entry_prefix_agg_id + + # remove routes + self.remove_srv6_route('5000::/64') + self.check_deleted_route_entries('5000::/64') + time.sleep(5) + + # remove nhg and pic_context + self.remove_nhg(nhg_index) + self.remove_pic_context(pic_ctx_index) + # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying def test_nonflaky_dummy():