LCOV - code coverage report
Current view: top level - bgp/routing-instance - service_chaining.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 82 1077 7.6 %
Date: 2026-09-21 02:11:39 Functions: 68 266 25.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include "bgp/routing-instance/service_chaining.h"
       6             : 
       7             : #include <boost/foreach.hpp>
       8             : #include <boost/assign/list_of.hpp>
       9             : 
      10             : #include <algorithm>
      11             : 
      12             : #include "base/task_annotations.h"
      13             : #include "base/task_trigger.h"
      14             : #include "bgp/bgp_config.h"
      15             : #include "bgp/bgp_log.h"
      16             : #include "bgp/bgp_membership.h"
      17             : #include "bgp/bgp_server.h"
      18             : #include "bgp/extended-community/load_balance.h"
      19             : #include "bgp/extended-community/site_of_origin.h"
      20             : #include "bgp/inet6vpn/inet6vpn_route.h"
      21             : #include "bgp/l3vpn/inetvpn_route.h"
      22             : #include "bgp/origin-vn/origin_vn.h"
      23             : #include "bgp/routing-instance/routing_instance.h"
      24             : #include "bgp/routing-instance/service_chaining_types.h"
      25             : #include "bgp/tunnel_encap/tunnel_encap.h"
      26             : #include "net/community_type.h"
      27             : 
      28             : using boost::bind;
      29             : using boost::system::error_code;
      30             : using std::make_pair;
      31             : using std::sort;
      32             : using std::string;
      33             : using std::vector;
      34             : 
      35             : template<>
      36             : int ServiceChainMgr<ServiceChainInet>::service_chain_task_id_ = -1;
      37             : template<>
      38             : int ServiceChainMgr<ServiceChainInet6>::service_chain_task_id_ = -1;
      39             : template<>
      40             : int ServiceChainMgr<ServiceChainEvpn>::service_chain_task_id_ = -1;
      41             : template<>
      42             : int ServiceChainMgr<ServiceChainEvpn6>::service_chain_task_id_ = -1;
      43             : 
      44           0 : static int GetOriginVnIndex(const BgpTable *table, const BgpRoute *route) {
      45           0 :     const BgpPath *path = route->BestPath();
      46           0 :     if (!path)
      47           0 :         return 0;
      48             : 
      49           0 :     const BgpAttr *attr = path->GetAttr();
      50           0 :     const ExtCommunity *ext_community = attr->ext_community();
      51           0 :     if (ext_community) {
      52           0 :         BOOST_FOREACH(const ExtCommunity::ExtCommunityValue &comm,
      53             :                       ext_community->communities()) {
      54           0 :             if (!ExtCommunity::is_origin_vn(comm))
      55           0 :                 continue;
      56           0 :             OriginVn origin_vn(comm);
      57           0 :             return origin_vn.vn_index();
      58             :         }
      59             :     }
      60           0 :     if (path->IsVrfOriginated())
      61           0 :         return table->routing_instance()->virtual_network_index();
      62           0 :     return 0;
      63             : }
      64             : 
      65             : /**
      66             :   * Replicate prefix received at head-end of service chain to appropriate
      67             :   * table depending on address-family.
      68             :   * ----------------------------------------------------------------------
      69             :   *      Family       service-chain address-family     Replication Table
      70             :   * ----------------------------------------------------------------------
      71             :   *      EVPN                  INET                      InetTable
      72             :   *      EVPN                  INET6                     Inet6Table
      73             :   *      INET                  INET                      EvpnTable
      74             :   *      INET6                 INET6                     EvpnTable
      75             :   * ----------------------------------------------------------------------
      76             :   * @param partition           -   Reference to DBTablePartition where the
      77             :   *                                service-chain route is to be replicated
      78             :   * @param route               -   Reference to service-chain route
      79             :   * @param table               -   Reference to BgpTable where service-chain
      80             :   *                                route is to be replicated
      81             :   * @param prefix              -   Prefix
      82             :   * @param create              -   when "true" indicates that route should be
      83             :   *                                created if not found and
      84             :   */
      85             : template<typename T>
      86           0 : void ServiceChain<T>::GetReplicationFamilyInfo(DBTablePartition  *&partition,
      87             :     BgpRoute *&route, BgpTable *&table, PrefixT prefix, bool create) {
      88             :     /**
      89             :       * EVPN prefix received at head end of service-chain.
      90             :       * Replicate route to <vrf>.inet[6] table depending on
      91             :       * whether the EVPN prefix carries inet[6] traffic.
      92             :       */
      93           0 :     RoutingInstance *src_ri = src_routing_instance();
      94           0 :     IpAddress addr = prefix.addr();
      95           0 :     int plen = prefix.prefixlen();
      96           0 :     if (GetSCFamily() == SCAddress::EVPN) {
      97             :         /**
      98             :          * EVPN route carrying inet prefix
      99             :          */
     100           0 :         table = src_ri->GetTable(Address::INET);
     101           0 :         Ip4Prefix inet_prefix = Ip4Prefix(addr.to_v4(), plen);
     102           0 :         InetRoute inet_route(inet_prefix);
     103           0 :         partition = static_cast<DBTablePartition *>
     104           0 :                         (table->GetTablePartition(&inet_route));
     105           0 :         route = static_cast<BgpRoute *>(partition->Find(&inet_route));
     106           0 :         if (create) {
     107             :             /**
     108             :              * Create case. Create route if not found.
     109             :              */
     110           0 :             if (route == NULL) {
     111           0 :                 route = new InetRoute(inet_prefix);
     112           0 :                 partition->Add(route);
     113             :             } else {
     114           0 :                 route->ClearDelete();
     115             :             }
     116             :         }
     117           0 :     } else if (GetSCFamily() == SCAddress::EVPN6) {
     118             :         /**
     119             :          * EVPN route carrying inet6 prefix
     120             :          */
     121           0 :         table = src_ri->GetTable(Address::INET6);
     122           0 :         Inet6Prefix inet6_prefix = Inet6Prefix(addr.to_v6(), plen);
     123           0 :         Inet6Route inet6_route(inet6_prefix);
     124           0 :         partition = static_cast<DBTablePartition *>
     125           0 :                         (table->GetTablePartition(&inet6_route));
     126           0 :         route = static_cast<BgpRoute *>(partition->Find(&inet6_route));
     127           0 :         if (create) {
     128             :             /**
     129             :              * Create case. Create route if not found.
     130             :              */
     131           0 :             if (route == NULL) {
     132           0 :                 route = new Inet6Route(inet6_prefix);
     133           0 :                 partition->Add(route);
     134             :             } else {
     135           0 :                 route->ClearDelete();
     136             :             }
     137             :         }
     138           0 :     } else {
     139             :         /**
     140             :           * INET/INET6 prefix at head end of service-chain.
     141             :           * Replicate route to <vrf>.evpn.0 table.
     142             :           */
     143           0 :         table = src_ri->GetTable(Address::EVPN);
     144           0 :         string type_rd_tag("5-0:0-0-");
     145           0 :         string prefix_str = type_rd_tag + addr.to_string() + "/" +
     146             :                             boost::lexical_cast<std::string>(plen);
     147           0 :         EvpnPrefix evpn_prefix(EvpnPrefix::FromString(prefix_str));
     148           0 :         EvpnRoute evpn_route(evpn_prefix);
     149           0 :         partition = static_cast<DBTablePartition *>
     150           0 :                         (table->GetTablePartition(&evpn_route));
     151           0 :         route = static_cast<BgpRoute *>(partition->Find(&evpn_route));
     152           0 :         if (create) {
     153             :             /**
     154             :              * Create case. Create route if not found.
     155             :              */
     156           0 :             if (route == NULL) {
     157           0 :                 route = new EvpnRoute(evpn_prefix);
     158           0 :                 partition->Add(route);
     159             :             } else {
     160           0 :                 route->ClearDelete();
     161             :             }
     162             :         }
     163           0 :     }
     164           0 : }
     165             : 
     166             : /**
     167             :   * Process service-chain path and add to service-chain route if needed.
     168             :   *
     169             :   * @param path_id    -  PathID
     170             :   * @param path       -  Path to be processed
     171             :   * @param attr       -  Path attribute
     172             :   * @param route      -  Reference to Service-Chain route
     173             :   * @param partition  -  Reference to DBTablePartition where the
     174             :   *                      service-chain route is to be replicated
     175             :   * @param aggregate  -  "true" indicates aggregate route
     176             :   * @param bgptable   -  BgpTable the service-chain route belongs to
     177             :   */
     178             : template<typename T>
     179           0 : void ServiceChain<T>::ProcessServiceChainPath(uint32_t path_id, BgpPath *path,
     180             :     BgpAttrPtr attr, BgpRoute *&route, DBTablePartition *&partition,
     181             :     bool aggregate, BgpTable *bgptable) {
     182             :     BgpPath *existing_path =
     183           0 :             route->FindPath(BgpPath::ServiceChain, NULL, path_id);
     184           0 :     uint32_t label = path->GetLabel();
     185           0 :     bool path_updated = false;
     186             : 
     187             :     /**
     188             :       * If inserting into EVPN table, the label should the vxlan_id of
     189             :       * the connected RI if non-zero and if not, the VNI.
     190             :       */
     191           0 :     if (bgptable->family() == Address::EVPN) {
     192             :         const RoutingInstance *conn_ri =
     193           0 :             bgptable->server()->routing_instance_mgr()->GetRoutingInstance(
     194             :                 RoutingInstanceMgr::GetPrimaryRoutingInstanceName(
     195           0 :                     connected_->name()));
     196           0 :         if (!conn_ri) {
     197             :             // conn_ri is not expected to be found only in unit tests.
     198           0 :             assert(bgp_log_test::unit_test());
     199           0 :             conn_ri = connected_routing_instance();
     200             :         }
     201           0 :         label = conn_ri->vxlan_id();
     202           0 :         if (!label) {
     203           0 :             label = conn_ri->virtual_network_index();
     204             :         }
     205             :     }
     206             : 
     207             :     /**
     208             :      * Check if there is an existing path that can be reused.
     209             :      */
     210           0 :     if (existing_path != NULL) {
     211             :         // Existing path can be reused.
     212           0 :         if ((attr.get() == existing_path->GetAttr()) &&
     213           0 :             (path->GetLabel() == existing_path->GetLabel()) &&
     214           0 :             (path->GetFlags() == existing_path->GetFlags())) {
     215           0 :             return;
     216             :         }
     217             : 
     218             :         /**
     219             :          * Remove existing path, new path will be added below.
     220             :          */
     221           0 :         path_updated = true;
     222           0 :         route->RemovePath(BgpPath::ServiceChain, NULL, path_id);
     223             :     }
     224             : 
     225             :     /**
     226             :      * No existing path or un-usable existing path
     227             :      * Create new path and insert into service-chain route
     228             :      */
     229           0 :     BgpPath *new_path =
     230           0 :         new BgpPath(path_id, BgpPath::ServiceChain, attr.get(),
     231           0 :                     path->GetFlags(), label);
     232           0 :     route->InsertPath(new_path);
     233           0 :     partition->Notify(route);
     234             : 
     235           0 :     BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
     236             :         (path_updated ? "Updated " : "Added ") <<
     237             :         (aggregate ? "Aggregate" : "ExtConnected") <<
     238             :         " ServiceChain path " << route->ToString() <<
     239             :         " path_id " << BgpPath::PathIdString(path_id) <<
     240             :         " in table " << bgptable->name() <<
     241             :         " .Path label: " << label);
     242             : }
     243             : 
     244             : template <typename T>
     245             : class ServiceChainMgr<T>::DeleteActor : public LifetimeActor {
     246             : public:
     247         128 :     explicit DeleteActor(ServiceChainMgr<T> *manager)
     248         128 :         : LifetimeActor(manager->server_->lifetime_manager()),
     249         128 :           manager_(manager) {
     250         128 :     }
     251         128 :     virtual bool MayDelete() const {
     252         128 :         return manager_->MayDelete();
     253             :     }
     254         128 :     virtual void Shutdown() {
     255         128 :     }
     256         128 :     virtual void Destroy() {
     257         128 :         manager_->Terminate();
     258         128 :     }
     259             : 
     260             : private:
     261             :     ServiceChainMgr<T> *manager_;
     262             : };
     263             : 
     264             : template <typename T>
     265           0 : ServiceChain<T>::ServiceChain(ServiceChainMgrT *manager,
     266             :     ServiceChainGroup *group, RoutingInstance *src, RoutingInstance *dest,
     267             :     RoutingInstance *connected, const vector<string> &subnets, AddressT addr,
     268             :     bool head, bool retain_as_path)
     269           0 :     : manager_(manager),
     270           0 :       group_(group),
     271           0 :       src_(src),
     272           0 :       dest_(dest),
     273           0 :       connected_(connected),
     274           0 :       connected_route_(NULL),
     275           0 :       service_chain_addr_(addr),
     276           0 :       group_oper_state_up_(group ? false : true),
     277           0 :       connected_table_unregistered_(false),
     278           0 :       dest_table_unregistered_(false),
     279           0 :       aggregate_(false),
     280           0 :       sc_head_(head),
     281           0 :       retain_as_path_(retain_as_path),
     282           0 :       src_table_delete_ref_(this, src_table()->deleter()),
     283           0 :       dest_table_delete_ref_(this, dest_table()->deleter()),
     284           0 :       connected_table_delete_ref_(this, connected_table()->deleter()) {
     285           0 :     for (vector<string>::const_iterator it = subnets.begin();
     286           0 :          it != subnets.end(); ++it) {
     287           0 :         string prefix = *it;
     288           0 :         error_code ec;
     289             :         /**
     290             :           * For EVPN, need to construct EVPN prefix from IPv4 or IPv6 subnet.
     291             :           * Make sure AF in prefix matches service_chain AF.
     292             :           */
     293           0 :         if (GetFamily() == Address::EVPN) {
     294           0 :             prefix = "5-0:0-0-" + prefix;
     295           0 :             EvpnPrefix subnet = EvpnPrefix::FromString(prefix, &ec);
     296           0 :             if (GetSCFamily() == SCAddress::EVPN) {
     297           0 :                 if (subnet.family() == Address::INET6) {
     298           0 :                     continue;
     299             :                 }
     300             :             } else {
     301           0 :                 if (subnet.family() != Address::INET6) {
     302           0 :                     continue;
     303             :                 }
     304             :             }
     305             :         }
     306           0 :         PrefixT ipam_subnet = PrefixT::FromString(prefix, &ec);
     307           0 :         if (ec.failed())
     308           0 :             continue;
     309           0 :         prefix_to_routelist_map_[ipam_subnet] = RouteList();
     310             :     }
     311           0 : }
     312             : 
     313             : template <typename T>
     314           0 : BgpTable *ServiceChain<T>::src_table() const {
     315           0 :     return src_->GetTable(GetFamily());
     316             : }
     317             : 
     318             : template <typename T>
     319           0 : BgpTable *ServiceChain<T>::connected_table() const {
     320           0 :     return connected_->GetTable(GetConnectedFamily());
     321             : }
     322             : 
     323             : template <typename T>
     324           0 : BgpTable *ServiceChain<T>::dest_table() const {
     325           0 :     return dest_->GetTable(GetFamily());
     326             : }
     327             : 
     328             : //
     329             : // Compare this ServiceChain against the ServiceChainConfig.
     330             : // Return true if the configuration has not changed, false otherwise.
     331             : //
     332             : template <typename T>
     333           0 : bool ServiceChain<T>::CompareServiceChainConfig(
     334             :     const ServiceChainConfig &config) {
     335           0 :     if (deleted())
     336           0 :         return false;
     337           0 :     if (dest_->name() != config.routing_instance)
     338           0 :         return false;
     339           0 :     if (connected_->name() != config.source_routing_instance)
     340           0 :         return false;
     341           0 :     if (service_chain_addr_.to_string() != config.service_chain_address)
     342           0 :         return false;
     343           0 :     if (!group_ && !config.service_chain_id.empty())
     344           0 :         return false;
     345           0 :     if (group_ && config.service_chain_id.empty())
     346           0 :         return false;
     347           0 :     if (group_ && group_->name() != config.service_chain_id)
     348           0 :         return false;
     349             : 
     350           0 :     if (prefix_to_routelist_map_.size() != config.prefix.size())
     351           0 :         return false;
     352           0 :     for (vector<string>::const_iterator it = config.prefix.begin();
     353           0 :          it != config.prefix.end(); ++it) {
     354           0 :         error_code ec;
     355           0 :         PrefixT ipam_subnet = PrefixT::FromString(*it, &ec);
     356           0 :         if (prefix_to_routelist_map_.find(ipam_subnet) ==
     357           0 :             prefix_to_routelist_map_.end()) {
     358           0 :             return false;
     359             :         }
     360             :     }
     361           0 :     return true;
     362             : }
     363             : 
     364             : //
     365             : // Match function called from BgpConditionListener
     366             : // Concurrency : db::DBTable
     367             : // For the purpose of route aggregation, two condition needs to be matched
     368             : //      1. More specific route present in any of the Dest BgpTable partition
     369             : //      2. Connected route(for nexthop) present in Src BgpTable
     370             : //
     371             : template <typename T>
     372           0 : bool ServiceChain<T>::Match(BgpServer *server, BgpTable *table, BgpRoute *route,
     373             :     bool deleted) {
     374           0 :     CHECK_CONCURRENCY("db::DBTable");
     375             : 
     376             :     typename ServiceChainRequestT::RequestType type;
     377           0 :     PrefixT aggregate_match;
     378             : 
     379           0 :     if (table == dest_table() && !dest_table_unregistered()) {
     380             :         // For EVPN service-chaining, we are only interested in Type 5 routes
     381             :         // from the destination table. Ignore any other route.
     382           0 :         if (GetFamily() == Address::EVPN) {
     383           0 :             if (!IsEvpnType5Route(route)) {
     384           0 :                 return (false);
     385             :             }
     386             :         }
     387             : 
     388             :         // Skip connected routes
     389           0 :         if (IsConnectedRoute(route)) {
     390           0 :             return false;
     391             :         }
     392             : 
     393             :         // Skip aggregate routes
     394           0 :         if (aggregate_enable() && IsAggregate(route))
     395           0 :             return false;
     396             : 
     397           0 :         if (aggregate_enable() && IsMoreSpecific(route, &aggregate_match)) {
     398             :             // More specific
     399           0 :             if (deleted) {
     400           0 :                 type = ServiceChainRequestT::MORE_SPECIFIC_DELETE;
     401             :             } else {
     402           0 :                 type = ServiceChainRequestT::MORE_SPECIFIC_ADD_CHG;
     403             :             }
     404             :         } else {
     405             :             // External connecting routes
     406           0 :             if (!deleted) {
     407           0 :                 if (!route->BestPath() || !route->BestPath()->IsFeasible()) {
     408           0 :                     deleted = true;
     409             :                 } else {
     410           0 :                     const BgpAttr *attr = route->BestPath()->GetAttr();
     411           0 :                     const Community *comm = attr ? attr->community() : NULL;
     412           0 :                     if (comm) {
     413           0 :                         if ((comm->ContainsValue(CommunityType::NoAdvertise)) ||
     414           0 :                            (comm->ContainsValue(CommunityType::NoReOriginate)))
     415           0 :                         deleted = true;
     416             :                     }
     417             : 
     418           0 :                     int vn_index = GetOriginVnIndex(table, route);
     419           0 :                     int src_vn_index = src_->virtual_network_index();
     420           0 :                     int dest_vn_index = dest_->virtual_network_index();
     421           0 :                     if (!vn_index || dest_vn_index != vn_index) {
     422           0 :                         if (src_vn_index == vn_index)
     423           0 :                             deleted = true;
     424           0 :                         if (!dest_->virtual_network_allow_transit())
     425           0 :                             deleted = true;
     426           0 :                         if (!dest_vn_index)
     427           0 :                             deleted = true;
     428             :                     }
     429             : 
     430           0 :                     const OriginVnPath *ovnpath =
     431           0 :                         attr ? attr->origin_vn_path() : NULL;
     432           0 :                     if (ovnpath && ovnpath->Contains(
     433             :                                 server->autonomous_system(), src_vn_index)) {
     434           0 :                         deleted = true;
     435             :                     }
     436             :                 }
     437             :             }
     438             : 
     439           0 :             if (deleted) {
     440           0 :                 type = ServiceChainRequestT::EXT_CONNECT_ROUTE_DELETE;
     441             :             } else {
     442           0 :                 type = ServiceChainRequestT::EXT_CONNECT_ROUTE_ADD_CHG;
     443             :             }
     444             :         }
     445           0 :     } else if ((table == connected_table()) &&
     446           0 :                !connected_table_unregistered() &&
     447           0 :                IsConnectedRoute(route, true)) {
     448             :         // Connected routes from source table
     449           0 :         if (!deleted) {
     450           0 :             if (!route->IsValid() ||
     451           0 :                 route->BestPath()->GetSource() != BgpPath::BGP_XMPP) {
     452           0 :                 deleted = true;
     453             :             }
     454             :         }
     455             : 
     456             :         // Connected route for service chain
     457           0 :         if (deleted) {
     458           0 :             type = ServiceChainRequestT::CONNECTED_ROUTE_DELETE;
     459             :         } else {
     460           0 :             type = ServiceChainRequestT::CONNECTED_ROUTE_ADD_CHG;
     461             :         }
     462             :     } else {
     463           0 :         return false;
     464             :     }
     465             : 
     466           0 :     BgpConditionListener *listener = manager_->GetListener();
     467             :     ServiceChainState *state = static_cast<ServiceChainState *>(
     468           0 :         listener->GetMatchState(table, route, this));
     469           0 :     if (!deleted) {
     470             :         // MatchState is added to the Route to ensure that DBEntry is not
     471             :         // deleted before the ServiceChain module processes the WorkQueue
     472             :         // request.
     473           0 :         if (!state) {
     474           0 :             state = new ServiceChainState(ServiceChainPtr(this));
     475           0 :             listener->SetMatchState(table, route, this, state);
     476             :         }
     477             :     } else {
     478             :         // MatchState is set on all the Routes that matches the conditions
     479             :         // Retrieve to check and ignore delete of unseen Add Match
     480           0 :         if (state == NULL) {
     481             :             // Not seen ADD ignore DELETE
     482           0 :             return false;
     483             :         }
     484             :     }
     485             : 
     486             :     // The MatchState reference is taken to ensure that the route is not
     487             :     // deleted when request is still present in the queue
     488             :     // This is to handle the case where MatchState already exists and
     489             :     // deleted entry gets reused or reused entry gets deleted.
     490           0 :     state->IncrementRefCnt();
     491             : 
     492             :     // Post the Match result to ServiceChain task to take Action
     493             :     // More_Specific_Present + Connected_Route_exists ==> Add Aggregate Route
     494             :     // and stitch the nexthop from connected route
     495           0 :     ServiceChainRequestT *req = new ServiceChainRequestT(
     496           0 :         type, table, route, aggregate_match, ServiceChainPtr(this));
     497           0 :     manager_->Enqueue(req);
     498           0 :     return true;
     499             : }
     500             : 
     501             : template <typename T>
     502           0 : string ServiceChain<T>::ToString() const {
     503           0 :     return (string("ServiceChain " ) + service_chain_addr_.to_string());
     504             : }
     505             : 
     506             : template <typename T>
     507           0 : void ServiceChain<T>::SetConnectedRoute(BgpRoute *connected) {
     508           0 :     connected_route_ = connected;
     509           0 :     connected_path_ids_.clear();
     510           0 :     if (!connected_route_)
     511           0 :         return;
     512             : 
     513           0 :     for (Route::PathList::iterator it = connected->GetPathList().begin();
     514           0 :         it != connected->GetPathList().end(); ++it) {
     515           0 :         BgpPath *path = static_cast<BgpPath *>(it.operator->());
     516             : 
     517             :         // Infeasible paths are not considered.
     518           0 :         if (!path->IsFeasible())
     519           0 :             break;
     520             : 
     521             :         // Bail if it's not ECMP with the best path.
     522           0 :         if (connected_route_->BestPath()->PathCompare(*path, true))
     523           0 :             break;
     524             : 
     525             :         // Use nexthop attribute of connected path as path id.
     526           0 :         uint32_t path_id = path->GetAttr()->nexthop().to_v4().to_ulong();
     527           0 :         connected_path_ids_.insert(path_id);
     528             :     }
     529             : }
     530             : 
     531             : template <typename T>
     532           0 : bool ServiceChain<T>::IsConnectedRouteValid() const {
     533           0 :     return (connected_route_ && connected_route_->IsValid());
     534             : }
     535             : 
     536             : template <typename T>
     537           0 : bool ServiceChain<T>::IsMoreSpecific(BgpRoute *route,
     538             :     PrefixT *aggregate_match) const {
     539           0 :     const RouteT *ip_route = static_cast<RouteT *>(route);
     540           0 :     const PrefixT &ip_prefix = ip_route->GetPrefix();
     541           0 :     for (typename PrefixToRouteListMap::const_iterator it =
     542           0 :          prefix_to_route_list_map()->begin();
     543           0 :          it != prefix_to_route_list_map()->end(); ++it) {
     544           0 :         if (ip_prefix.IsMoreSpecific(it->first)) {
     545           0 :             *aggregate_match = it->first;
     546           0 :             return true;
     547             :         }
     548             :     }
     549           0 :     return false;
     550             : }
     551             : 
     552             : template <typename T>
     553           0 : bool ServiceChain<T>::IsAggregate(BgpRoute *route) const {
     554           0 :     RouteT *ip_route = dynamic_cast<RouteT *>(route);
     555           0 :     for (typename PrefixToRouteListMap::const_iterator it =
     556           0 :          prefix_to_route_list_map()->begin();
     557           0 :          it != prefix_to_route_list_map()->end(); ++it) {
     558           0 :         if (it->first == ip_route->GetPrefix())
     559           0 :             return true;
     560             :     }
     561           0 :     return false;
     562             : }
     563             : 
     564             : /**
     565             :  * Check if route belongs to connected table.
     566             :  * For EVPN, connected routes could belong to INET or INET6 AF.
     567             :  * For routes in the destination table we can use template AF.
     568             :  */
     569             : template <typename T>
     570           0 : bool ServiceChain<T>::IsConnectedRoute(BgpRoute *route,
     571             :                                        bool is_conn_table) const {
     572           0 :     if (is_conn_table && GetFamily() == Address::EVPN) {
     573           0 :         if (GetConnectedFamily() == Address::INET) {
     574           0 :             InetRoute *inet_route = dynamic_cast<InetRoute *>(route);
     575           0 :             return (service_chain_addr() == inet_route->GetPrefix().addr());
     576             :         } else {
     577           0 :             Inet6Route *inet6_route = dynamic_cast<Inet6Route *>(route);
     578           0 :             return (service_chain_addr() == inet6_route->GetPrefix().addr());
     579             :         }
     580             :     } else {
     581             :         /**
     582             :          * Non-EVPN and EVPN destination table case
     583             :          */
     584           0 :         RouteT *ip_route = dynamic_cast<RouteT *>(route);
     585           0 :         return (service_chain_addr() == ip_route->GetPrefix().addr());
     586             :     }
     587             : }
     588             : 
     589             : /**
     590             :  * Check if EVPN route being re-originated is Type 5
     591             :  * Also make sure that the address-family of the prefix carried in the EVPN
     592             :  * route matches the service-chain family. This will avoid the routes from
     593             :  * being re-originated twice.
     594             :  */
     595             : template <typename T>
     596           0 : bool ServiceChain<T>::IsEvpnType5Route(BgpRoute *route) const {
     597           0 :     if (GetFamily() != Address::EVPN) {
     598           0 :         return false;
     599             :     }
     600             : 
     601           0 :     EvpnRoute *evpn_route = static_cast<EvpnRoute *>(route);
     602           0 :     EvpnPrefix prefix = evpn_route->GetPrefix();
     603           0 :     if (prefix.type() != EvpnPrefix::IpPrefixRoute) {
     604           0 :         return false;
     605             :     }
     606           0 :     if (GetSCFamily() == SCAddress::EVPN &&
     607           0 :         prefix.family() == Address::INET6) {
     608           0 :        return false;
     609             :     }
     610           0 :     if (GetSCFamily() == SCAddress::EVPN6 &&
     611           0 :         prefix.family() == Address::INET) {
     612           0 :        return false;
     613             :     }
     614           0 :     return true;
     615             : }
     616             : 
     617             : template <typename T>
     618           0 : void ServiceChain<T>::RemoveMatchState(BgpRoute *route,
     619             :     ServiceChainState *state) {
     620           0 :     if (deleted() || route->IsDeleted()) {
     621             :         // At this point we are ready to release the MatchState on the DBEntry
     622             :         // So mark it as deleted.. Actual removal of the state is done when
     623             :         // ref count is 0
     624           0 :         state->set_deleted();
     625             :     }
     626           0 : }
     627             : 
     628             : /*
     629             :  * To support BMS to VM service-chaining, we could have traffic being
     630             :  * chained between different address-families. This entails the need for
     631             :  * replicating the service-chain route across address-families.
     632             :  * The different possibilities are listed below.
     633             :  *                    ---------------------------------------------------
     634             :  *                    |             service-chain info                  |
     635             :  * ----------------------------------------------------------------------
     636             :  *  Traffic direction | Destination AF    Source AF   Replication Table |
     637             :  * ----------------------------------------------------------------------
     638             :  *    VM(v4) --> BMS  |   EVPN             INET        InetTable        |
     639             :  *    VM(v6) --> BMS  |   EVPN             INET6       Inet6Table       |
     640             :  *    BMS --> VM (v4) |   INET             INET        EvpnTable        |
     641             :  *    BMS --> VM (v6) |   INET6            INET6       EvpnTable        |
     642             :  * ----------------------------------------------------------------------
     643             :  *
     644             :  * This is only done at the RI belonging to the head SI in the service
     645             :  * chain. At the RIs belonging to other SIs in the chain, we always
     646             :  * install the route only in the INET or INET6 table and not in the EVPN
     647             :  * table. This is because, at the RI belonging to the first SI in the
     648             :  * service-chain, we may need to originate a Type 5 route if a BMS happens
     649             :  * to be connected to it.
     650             :  */
     651             : template <typename T>
     652           0 : void ServiceChain<T>::DeleteServiceChainRoute(PrefixT prefix, bool aggregate) {
     653             : 
     654           0 :     CHECK_CONCURRENCY("bgp::ServiceChain");
     655             : 
     656             :     /*
     657             :      * For deletion within the same AF.
     658             :      * At the RIs belonging to SIs NOT at the head of the service-chain, do
     659             :      * not need to delete EVPN SC routes since only INET or INET6 routes
     660             :      * would have been installed.
     661             :      */
     662           0 :     if (is_sc_head() || GetFamily() != Address::EVPN) {
     663           0 :         BgpTable *bgptable = src_table();
     664           0 :         RouteT rt_key(prefix);
     665             :         DBTablePartition *partition =
     666             :             static_cast<DBTablePartition *>(bgptable->
     667           0 :                 GetTablePartition(&rt_key));
     668             :         BgpRoute *service_chain_route =
     669           0 :             static_cast<BgpRoute *>(partition->Find(&rt_key));
     670             : 
     671           0 :         if (service_chain_route && !service_chain_route->IsDeleted()) {
     672           0 :             DeleteServiceChainRouteInternal(service_chain_route, partition,
     673             :                                             bgptable, aggregate);
     674             :         }
     675           0 :     }
     676             : 
     677             :     /*
     678             :      * For deletion from replication table.
     679             :      * At the RIs belonging to SIs NOT at the head of the service-chain, do
     680             :      * not need to delete SC routes in the EVPN table since they would have
     681             :      * been installed only in the INET or INET6 table..
     682             :      */
     683           0 :     if (is_sc_head() || GetFamily() == Address::EVPN) {
     684             :         BgpTable *repl_table;
     685             :         BgpRoute *repl_sc_route;
     686             :         DBTablePartition *repl_partition;
     687           0 :         GetReplicationFamilyInfo(repl_partition, repl_sc_route, repl_table,
     688             :                              prefix, false);
     689           0 :         if (repl_sc_route && !repl_sc_route->IsDeleted()) {
     690           0 :             DeleteServiceChainRouteInternal(repl_sc_route, repl_partition,
     691             :                                             repl_table, aggregate);
     692             :         }
     693             :     }
     694           0 : }
     695             : 
     696             : template <typename T>
     697           0 : void ServiceChain<T>::DeleteServiceChainRouteInternal(
     698             :                                      BgpRoute          *service_chain_route,
     699             :                                      DBTablePartition  *partition,
     700             :                                      BgpTable          *bgptable,
     701             :                                      bool              aggregate) {
     702           0 :     CHECK_CONCURRENCY("bgp::ServiceChain");
     703             : 
     704           0 :     for (ConnectedPathIdList::const_iterator it = GetConnectedPathIds().begin();
     705           0 :          it != GetConnectedPathIds().end(); ++it) {
     706           0 :         uint32_t path_id = *it;
     707           0 :         service_chain_route->RemovePath(BgpPath::ServiceChain, NULL, path_id);
     708           0 :         BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
     709             :             "Removed " << (aggregate ? "Aggregate" : "ExtConnected") <<
     710             :             " ServiceChain path " << service_chain_route->ToString() <<
     711             :             " path_id " << BgpPath::PathIdString(path_id) <<
     712             :             " in table " << bgptable->name());
     713             :     }
     714             : 
     715           0 :     if (!service_chain_route->HasPaths()) {
     716           0 :         partition->Delete(service_chain_route);
     717             :     } else {
     718           0 :         partition->Notify(service_chain_route);
     719             :     }
     720           0 : }
     721             : 
     722             : /*
     723             :  * To support BMS to VM service-chaining, we could have traffic being
     724             :  * chained between different address-families. This entails the need for
     725             :  * replicating the service-chain route across address-families.
     726             :  * The different possibilities are listed below.
     727             :  *                    ---------------------------------------------------
     728             :  *                    |             service-chain info                  |
     729             :  * ----------------------------------------------------------------------
     730             :  *  Traffic direction | Destination AF    Source AF   Replication Table |
     731             :  * ----------------------------------------------------------------------
     732             :  *    VM(v4) --> BMS  |   EVPN             INET        InetTable        |
     733             :  *    VM(v6) --> BMS  |   EVPN             INET6       Inet6Table       |
     734             :  *    BMS --> VM (v4) |   INET             INET        EvpnTable        |
     735             :  *    BMS --> VM (v6) |   INET6            INET6       EvpnTable        |
     736             :  * ----------------------------------------------------------------------
     737             :  *
     738             :  * This is only done at the RI belonging to the head SI in the service
     739             :  * chain. At the RIs belonging to other SIs in the chain, we always
     740             :  * install the route only in the INET or INET6 table and not in the EVPN
     741             :  * table. This is because, at the RI belonging to the first SI in the
     742             :  * service-chain, we may need to originate a Type 5 route if a BMS happens
     743             :  * to be connected to it.
     744             :  */
     745             : template <typename T>
     746           0 : void ServiceChain<T>::UpdateServiceChainRoute(PrefixT prefix,
     747             :     const RouteT *orig_route, const ConnectedPathIdList &old_path_ids,
     748             :     bool aggregate) {
     749             : 
     750           0 :     CHECK_CONCURRENCY("bgp::ServiceChain");
     751             : 
     752             :     /*
     753             :      * For re-origination within the same AF.
     754             :      * At the RIs belonging to SIs NOT at the head of the service-chain, do
     755             :      * not install EVPN SC routes. Only install INET or INET6 routes.
     756             :      */
     757           0 :     if (is_sc_head() || GetFamily() != Address::EVPN) {
     758           0 :         BgpTable *bgptable = src_table();
     759           0 :         RouteT rt_key(prefix);
     760             :         DBTablePartition *partition =
     761             :             static_cast<DBTablePartition *>
     762           0 :                 (bgptable->GetTablePartition(&rt_key));
     763             :         BgpRoute *service_chain_route =
     764           0 :             static_cast<BgpRoute *>(partition->Find(&rt_key));
     765             : 
     766           0 :         if (service_chain_route == NULL) {
     767           0 :             service_chain_route = new RouteT(prefix);
     768           0 :             partition->Add(service_chain_route);
     769             :         } else {
     770           0 :             service_chain_route->ClearDelete();
     771             :         }
     772             : 
     773           0 :         UpdateServiceChainRouteInternal(orig_route, old_path_ids,
     774             :                                         service_chain_route, partition,
     775             :                                         bgptable, aggregate);
     776           0 :     }
     777             : 
     778             :     /*
     779             :      * For re-origination to replication table.
     780             :      * At the RIs belonging to SIs NOT at the head of the service-chain, do
     781             :      * not install SC routes in the EVPN table. Only install INET or INET6
     782             :      * routes.
     783             :      */
     784           0 :     if (is_sc_head() || GetFamily() == Address::EVPN) {
     785             :         BgpTable *repl_table;
     786             :         BgpRoute *repl_sc_route;
     787             :         DBTablePartition *repl_partition;
     788           0 :         GetReplicationFamilyInfo(repl_partition, repl_sc_route, repl_table,
     789             :                                  prefix, true);
     790           0 :         UpdateServiceChainRouteInternal(orig_route, old_path_ids,
     791             :                                         repl_sc_route, repl_partition,
     792             :                                         repl_table, aggregate);
     793             :    }
     794           0 : }
     795             : 
     796             : template <typename T>
     797           0 : void ServiceChain<T>::UpdateServiceChainRouteInternal(const RouteT *orig_route,
     798             :     const ConnectedPathIdList &old_path_ids, BgpRoute *service_chain_route,
     799             :     DBTablePartition *partition, BgpTable *bgptable, bool aggregate) {
     800           0 :     CHECK_CONCURRENCY("bgp::ServiceChain");
     801             : 
     802           0 :     int vn_index = dest_routing_instance()->virtual_network_index();
     803           0 :     BgpServer *server = dest_routing_instance()->server();
     804           0 :     OriginVn origin_vn(server->autonomous_system(), vn_index);
     805           0 :     OriginVn origin_vn4(server->autonomous_system(), AS_TRANS);
     806           0 :     OriginVn origin_vn_trans(AS_TRANS, vn_index);
     807           0 :     const OriginVnPath::OriginVnValue origin_vn_bytes = origin_vn.GetExtCommunity();
     808           0 :     const OriginVnPath::OriginVnValue origin_vn_trans_bytes =
     809           0 :               origin_vn_trans.GetExtCommunity();
     810           0 :     const OriginVnPath::OriginVnValue origin_vn4_bytes =
     811           0 :               origin_vn4.GetExtCommunity();
     812             : 
     813           0 :     SiteOfOrigin soo;
     814           0 :     ExtCommunity::ExtCommunityList sgid_list;
     815           0 :     LargeCommunity::LargeCommunityList tag_list;
     816           0 :     LoadBalance load_balance;
     817           0 :     bool load_balance_present = false;
     818           0 :     const Community *orig_community = nullptr;
     819           0 :     const OriginVnPath *orig_ovnpath = nullptr;
     820           0 :     const AsPath *orig_aspath = nullptr;
     821           0 :     RouteDistinguisher orig_rd;
     822           0 :     if (orig_route) {
     823           0 :         const BgpPath *orig_path = orig_route->BestPath();
     824           0 :         const BgpAttr *orig_attr = nullptr;
     825           0 :         const ExtCommunity *ext_community = nullptr;
     826           0 :         const LargeCommunity *large_community = nullptr;
     827           0 :         if (orig_path != nullptr)
     828           0 :             orig_attr = orig_path->GetAttr();
     829           0 :         if (orig_attr != nullptr) {
     830           0 :             orig_community = orig_attr->community();
     831           0 :             ext_community = orig_attr->ext_community();
     832           0 :             large_community = orig_attr->large_community();
     833           0 :             orig_ovnpath = orig_attr->origin_vn_path();
     834           0 :             orig_rd = orig_attr->source_rd();
     835           0 :             orig_aspath = orig_attr->as_path();
     836             :         }
     837           0 :         if (ext_community != nullptr) {
     838           0 :             for(const ExtCommunity::ExtCommunityValue &comm:
     839           0 :                           ext_community->communities()) {
     840           0 :                 if (ExtCommunity::is_security_group(comm))
     841           0 :                     sgid_list.push_back(comm);
     842           0 :                 if (ExtCommunity::is_site_of_origin(comm) && soo.IsNull())
     843           0 :                     soo = SiteOfOrigin(comm);
     844           0 :                 if (ExtCommunity::is_load_balance(comm)) {
     845           0 :                     load_balance = LoadBalance(comm);
     846           0 :                     load_balance_present = true;
     847             :                 }
     848             :             }
     849             :         }
     850           0 :         if (large_community != nullptr) {
     851           0 :             for(const auto &comm : large_community->communities()) {
     852           0 :                 if (LargeCommunity::is_tag(comm)) {
     853           0 :                     tag_list.push_back(comm);
     854             :                 }
     855             :             }
     856             :         }
     857             :     }
     858             : 
     859           0 :     BgpAttrDB *attr_db = server->attr_db();
     860           0 :     CommunityDB *comm_db = server->comm_db();
     861           0 :     CommunityPtr new_community = comm_db->AppendAndLocate(
     862             :         orig_community, CommunityType::AcceptOwnNexthop);
     863           0 :     ExtCommunityDB *extcomm_db = server->extcomm_db();
     864           0 :     LargeCommunityDB *largecomm_db = server->largecomm_db();
     865           0 :     BgpMembershipManager *membership_mgr = server->membership_mgr();
     866           0 :     OriginVnPathDB *ovnpath_db = server->ovnpath_db();
     867           0 :     OriginVnPathPtr new_ovnpath;
     868           0 :     if (server->autonomous_system() > AS2_MAX && vn_index > 0xffff) {
     869           0 :         new_ovnpath = ovnpath_db->PrependAndLocate(orig_ovnpath,
     870             :                                                    origin_vn4_bytes);
     871           0 :         new_ovnpath = ovnpath_db->PrependAndLocate(new_ovnpath.get(),
     872             :                                                    origin_vn_trans_bytes);
     873             :     } else {
     874           0 :         new_ovnpath = ovnpath_db->PrependAndLocate(
     875             :                                   orig_ovnpath, origin_vn_bytes);
     876             :     }
     877             : 
     878           0 :     ConnectedPathIdList new_path_ids;
     879           0 :     for (Route::PathList::iterator it =
     880           0 :          connected_route()->GetPathList().begin();
     881           0 :          it != connected_route()->GetPathList().end(); ++it) {
     882           0 :         BgpPath *connected_path = static_cast<BgpPath *>(it.operator->());
     883             : 
     884             :         // Infeasible paths are not considered
     885           0 :         if (!connected_path->IsFeasible())
     886           0 :             break;
     887             : 
     888             :         // take snapshot of all ECMP paths
     889           0 :         if (connected_route()->BestPath()->PathCompare(*connected_path, true))
     890           0 :             break;
     891             : 
     892             :         // Skip paths with duplicate forwarding information.  This ensures
     893             :         // that we generate only one path with any given next hop and label
     894             :         // when there are multiple connected paths from the original source
     895             :         // received via different peers e.g. directly via XMPP and via BGP.
     896           0 :         if (connected_route()->DuplicateForwardingPath(connected_path))
     897           0 :             continue;
     898             : 
     899           0 :         const BgpAttr *attr = connected_path->GetAttr();
     900             : 
     901           0 :         ExtCommunityPtr new_ext_community;
     902           0 :         LargeCommunityPtr new_large_community;
     903             : 
     904             :         // Strip any RouteTargets from the connected attributes.
     905           0 :         new_ext_community = extcomm_db->ReplaceRTargetAndLocate(
     906           0 :             attr->ext_community(), ExtCommunity::ExtCommunityList());
     907             : 
     908             :         // Add the export route target list from the source routing instance
     909             :         // when inserting into EVPN table. This is required for the case when
     910             :         // service-chain routes are replicated to the BGP table to be used on
     911             :         // the BMS. The TOR switch does not import the cooked-up RT of the
     912             :         // service-RI. It only imports the RTs of the primary RI. Hence, we
     913             :         // add the primary RIs RTs to the route.
     914             :         // NOTE: There is an assumption that connected_ri on the head SI
     915             :         // will always point to the primary RI. Need to change that if the
     916             :         // assumption is not true.
     917             :         // Also, we pick only the export route targets in the range used by
     918             :         // schema transformer for non user-configured RTs.
     919           0 :         if (is_sc_head() && bgptable->family() == Address::EVPN) {
     920           0 :             ExtCommunity::ExtCommunityList export_list;
     921             :             const RoutingInstance *conn_ri =
     922           0 :                 server->routing_instance_mgr()->GetRoutingInstance(
     923             :                     RoutingInstanceMgr::GetPrimaryRoutingInstanceName(
     924           0 :                         connected_->name()));
     925           0 :             if (!conn_ri) {
     926             :                 // conn_ri is not expected to be found only in unit tests.
     927           0 :                 assert(bgp_log_test::unit_test());
     928           0 :                 conn_ri = connected_routing_instance();
     929             :             }
     930           0 :             BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
     931             :                 "Adding primary RI " << conn_ri->name() << " route targets " <<
     932             :                 "to service-chain route for EVPN table " << bgptable->name());
     933           0 :             BOOST_FOREACH(const RouteTarget &rtarget,
     934             :                           conn_ri->GetExportList()) {
     935           0 :                 if (ExtCommunity::get_rtarget_val(
     936           0 :                     rtarget.GetExtCommunity()) != 0) {
     937           0 :                     BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG,
     938             :                         BGP_LOG_FLAG_TRACE, "RT value " << rtarget.ToString());
     939           0 :                     export_list.push_back(rtarget.GetExtCommunity());
     940             :                 }
     941             :             }
     942           0 :             new_ext_community = extcomm_db->AppendAndLocate(
     943             :                 new_ext_community.get(), export_list);
     944           0 :         }
     945             : 
     946             :         // Replace the SGID list with the list from the original route.
     947           0 :         new_ext_community = extcomm_db->ReplaceSGIDListAndLocate(
     948             :             new_ext_community.get(), sgid_list);
     949             : 
     950             :         // Replace the Tag list with the list from the original route.
     951           0 :         new_large_community = largecomm_db->ReplaceTagListAndLocate(
     952             :             new_large_community.get(), tag_list);
     953             : 
     954             :         // Replace SiteOfOrigin with value from original route if any.
     955           0 :         if (soo.IsNull()) {
     956           0 :             new_ext_community = extcomm_db->RemoveSiteOfOriginAndLocate(
     957             :                 new_ext_community.get());
     958             :         } else {
     959           0 :             new_ext_community = extcomm_db->ReplaceSiteOfOriginAndLocate(
     960           0 :                 new_ext_community.get(), soo.GetExtCommunity());
     961             :         }
     962             : 
     963             :         // Inherit load balance attribute of orig_route if connected path
     964             :         // does not have one already.
     965           0 :         if (!LoadBalance::IsPresent(connected_path) && load_balance_present) {
     966           0 :             new_ext_community = extcomm_db->AppendAndLocate(
     967           0 :                     new_ext_community.get(), load_balance.GetExtCommunity());
     968             :         }
     969             : 
     970             :         // Replace the OriginVn with the value from the original route
     971             :         // or the value associated with the dest routing instance.
     972           0 :         if (server->autonomous_system() > AS2_MAX && vn_index > 0xffff) {
     973           0 :             new_ext_community = extcomm_db->ReplaceOriginVnAndLocate(
     974             :                                 new_ext_community.get(), origin_vn4_bytes);
     975           0 :             new_ext_community = extcomm_db->AppendAndLocate(
     976             :                                 new_ext_community.get(), origin_vn_trans_bytes);
     977             :         } else {
     978           0 :             new_ext_community = extcomm_db->ReplaceOriginVnAndLocate(
     979             :                                 new_ext_community.get(), origin_vn_bytes);
     980             :         }
     981             : 
     982             :         // Connected routes always have mpls (udp or gre)  as encap.
     983             :         // If updating service-chain route in the EVPN table, change
     984             :         // tunnel encap to include VxLAN and MPLS. BMS only supports VxLAN.
     985             :         // Vrouter has the choice of using VxLAN or MPLS based on config.
     986           0 :         if (is_sc_head() && bgptable->family() == Address::EVPN) {
     987           0 :             ExtCommunity::ExtCommunityList encaps_list;
     988           0 :             vector<string> tunnel_encaps = boost::assign::list_of("vxlan");
     989           0 :             BOOST_FOREACH(string encap, tunnel_encaps) {
     990           0 :                 encaps_list.push_back(TunnelEncap(encap).GetExtCommunity());
     991             :             }
     992           0 :             new_ext_community = extcomm_db->
     993             :                 ReplaceTunnelEncapsulationAndLocate(new_ext_community.get(),
     994             :                                                     encaps_list);
     995           0 :         }
     996             : 
     997             :         // Replace extended community, community and origin vn path.
     998           0 :         BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(
     999             :             attr, new_ext_community);
    1000           0 :         new_attr =
    1001             :             attr_db->ReplaceLargeCommunityAndLocate(new_attr.get(),
    1002             :                                                     new_large_community);
    1003           0 :         new_attr =
    1004             :             attr_db->ReplaceCommunityAndLocate(new_attr.get(), new_community);
    1005           0 :         new_attr = attr_db->ReplaceOriginVnPathAndLocate(new_attr.get(),
    1006             :             new_ovnpath);
    1007             : 
    1008             :         // Strip as_path if needed. This is required when the connected route is
    1009             :         // learnt via BGP. If retain_as_path knob is configured replace the
    1010             :         // AsPath with the value from the original route.
    1011           0 :         if (retain_as_path() && orig_aspath) {
    1012           0 :             new_attr = attr_db->ReplaceAsPathAndLocate(new_attr.get(),
    1013             :                                                        orig_aspath);
    1014             :         } else {
    1015           0 :             new_attr = attr_db->ReplaceAsPathAndLocate(new_attr.get(),
    1016           0 :                                                        AsPathPtr());
    1017             :         }
    1018             : 
    1019             :         // If the connected path is learnt via XMPP, construct RD based on
    1020             :         // the id registered with source table instead of connected table.
    1021             :         // This allows chaining of multiple in-network service instances
    1022             :         // that are on the same compute node.
    1023           0 :         const IPeer *peer = connected_path->GetPeer();
    1024           0 :         if (src_ != connected_ && peer && peer->IsXmppPeer()) {
    1025           0 :             int instance_id = -1;
    1026           0 :             bool is_registered = membership_mgr->GetRegistrationInfo(peer,
    1027             :                                                        bgptable, &instance_id);
    1028           0 :             if (!is_registered)
    1029           0 :                 continue;
    1030           0 :             RouteDistinguisher connected_rd = attr->source_rd();
    1031           0 :             if (connected_rd.Type() != RouteDistinguisher::TypeIpAddressBased)
    1032           0 :                 continue;
    1033             : 
    1034           0 :             RouteDistinguisher rd(connected_rd.GetAddress(), instance_id);
    1035           0 :             new_attr = attr_db->ReplaceSourceRdAndLocate(new_attr.get(), rd);
    1036             :         }
    1037             : 
    1038             :         // Replace the source rd if the connected path is a secondary path
    1039             :         // of a primary path in the l3vpn table. Use the RD of the primary.
    1040           0 :         if (connected_path->IsReplicated()) {
    1041           0 :             const BgpSecondaryPath *spath =
    1042             :                 static_cast<const BgpSecondaryPath *>(connected_path);
    1043           0 :             const RoutingInstance *ri = spath->src_table()->routing_instance();
    1044           0 :             if (ri->IsMasterRoutingInstance()) {
    1045             :                 const VpnRouteT *vpn_route =
    1046           0 :                     static_cast<const VpnRouteT *>(spath->src_rt());
    1047           0 :                 new_attr = attr_db->ReplaceSourceRdAndLocate(new_attr.get(),
    1048           0 :                     vpn_route->GetPrefix().route_distinguisher());
    1049             :             }
    1050             :         }
    1051             : 
    1052             :         // Skip paths with Source RD same as source RD of the connected path
    1053           0 :         if (!orig_rd.IsZero() && new_attr->source_rd() == orig_rd)
    1054           0 :             continue;
    1055             : 
    1056             :         // Check whether we already have a path with the associated path id.
    1057           0 :         uint32_t path_id =
    1058           0 :             connected_path->GetAttr()->nexthop().to_v4().to_ulong();
    1059           0 :         ProcessServiceChainPath(path_id, connected_path, new_attr,
    1060             :                                 service_chain_route, partition,
    1061             :                                 aggregate, bgptable);
    1062           0 :         new_path_ids.insert(path_id);
    1063             :     }
    1064             : 
    1065             :     // Remove stale paths.
    1066           0 :     for (ConnectedPathIdList::const_iterator it = old_path_ids.begin();
    1067           0 :          it != old_path_ids.end(); ++it) {
    1068           0 :         uint32_t path_id = *it;
    1069           0 :         if (new_path_ids.find(path_id) != new_path_ids.end())
    1070           0 :             continue;
    1071           0 :         service_chain_route->RemovePath(BgpPath::ServiceChain, NULL, path_id);
    1072           0 :         partition->Notify(service_chain_route);
    1073             : 
    1074           0 :         BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
    1075             :             "Removed " << (aggregate ? "Aggregate" : "ExtConnected") <<
    1076             :             " ServiceChain path " << service_chain_route->ToString() <<
    1077             :             " path_id " << BgpPath::PathIdString(path_id) <<
    1078             :             " in table " << bgptable->name());
    1079             : 
    1080             :     }
    1081             : 
    1082             :     // Delete the route if there's no paths.
    1083           0 :     if (!service_chain_route->HasPaths())
    1084           0 :         partition->Delete(service_chain_route);
    1085           0 : }
    1086             : 
    1087             : template <typename T>
    1088           0 : bool ServiceChain<T>::AddMoreSpecific(PrefixT aggregate,
    1089             :     BgpRoute *more_specific) {
    1090             :     typename PrefixToRouteListMap::iterator it =
    1091           0 :         prefix_to_routelist_map_.find(aggregate);
    1092           0 :     assert(it != prefix_to_routelist_map_.end());
    1093           0 :     bool ret = false;
    1094           0 :     if (it->second.empty()) {
    1095             :         // Add the aggregate for the first time
    1096           0 :         ret = true;
    1097             :     }
    1098           0 :     it->second.insert(more_specific);
    1099           0 :     return ret;
    1100             : }
    1101             : 
    1102             : template <typename T>
    1103           0 : bool ServiceChain<T>::DeleteMoreSpecific(PrefixT aggregate,
    1104             :     BgpRoute *more_specific) {
    1105             :     typename PrefixToRouteListMap::iterator it =
    1106           0 :         prefix_to_routelist_map_.find(aggregate);
    1107           0 :     assert(it != prefix_to_routelist_map_.end());
    1108           0 :     it->second.erase(more_specific);
    1109           0 :     return it->second.empty();
    1110             : }
    1111             : 
    1112             : template <typename T>
    1113           0 : void ServiceChain<T>::FillServiceChainInfo(ShowServicechainInfo *info) const {
    1114           0 :     if (deleted()) {
    1115           0 :         info->set_state("deleted");
    1116           0 :     } else if (!IsConnectedRouteValid()) {
    1117           0 :         info->set_state("down");
    1118           0 :     } else if (!group_oper_state_up()) {
    1119           0 :         info->set_state("group down");
    1120             :     } else {
    1121           0 :         info->set_state("active");
    1122             :     }
    1123             : 
    1124           0 :     ConnectedRouteInfo connected_rt_info;
    1125           0 :     connected_rt_info.set_service_chain_addr(
    1126           0 :         service_chain_addr().to_string());
    1127           0 :     if (connected_route()) {
    1128           0 :         ShowRoute show_route;
    1129           0 :         connected_route()->FillRouteInfo(connected_table(), &show_route);
    1130           0 :         connected_rt_info.set_connected_rt(show_route);
    1131           0 :     }
    1132           0 :     info->set_connected_route(connected_rt_info);
    1133             : 
    1134           0 :     vector<PrefixToRouteListInfo> more_vec;
    1135           0 :     for (typename PrefixToRouteListMap::const_iterator it =
    1136           0 :          prefix_to_route_list_map()->begin();
    1137           0 :         it != prefix_to_route_list_map()->end(); ++it) {
    1138           0 :         PrefixToRouteListInfo prefix_list_info;
    1139           0 :         prefix_list_info.set_prefix(it->first.ToString());
    1140             : 
    1141           0 :         BgpTable *bgptable = src_table();
    1142           0 :         RouteT rt_key(it->first);
    1143           0 :         BgpRoute *aggregate = static_cast<BgpRoute *>(bgptable->Find(&rt_key));
    1144           0 :         if (aggregate) {
    1145           0 :             prefix_list_info.set_aggregate(true);
    1146           0 :             ShowRoute show_route;
    1147           0 :             aggregate->FillRouteInfo(bgptable, &show_route);
    1148           0 :             prefix_list_info.set_aggregate_rt(show_route);
    1149           0 :         } else {
    1150           0 :             prefix_list_info.set_aggregate(false);
    1151             :         }
    1152             : 
    1153           0 :         vector<string> rt_list;
    1154           0 :         for (RouteList::iterator rt_it = it->second.begin();
    1155           0 :              rt_it != it->second.end(); ++rt_it) {
    1156           0 :             rt_list.push_back((*rt_it)->ToString());
    1157             :         }
    1158           0 :         prefix_list_info.set_more_specific_list(rt_list);
    1159           0 :         more_vec.push_back(prefix_list_info);
    1160             :     }
    1161           0 :     info->set_more_specifics(more_vec);
    1162             : 
    1163           0 :     vector<ExtConnectRouteInfo> ext_connecting_rt_info_list;
    1164           0 :     for (ExtConnectRouteList::const_iterator it =
    1165           0 :          ext_connecting_routes().begin();
    1166           0 :          it != ext_connecting_routes().end(); ++it) {
    1167           0 :         ExtConnectRouteInfo ext_rt_info;
    1168           0 :         ext_rt_info.set_ext_rt_prefix((*it)->ToString());
    1169           0 :         BgpTable *bgptable = src_table();
    1170           0 :         RouteT *ext_route = static_cast<RouteT *>(*it);
    1171           0 :         RouteT rt_key(ext_route->GetPrefix());
    1172             :         BgpRoute *ext_connecting =
    1173           0 :             static_cast<BgpRoute *>(bgptable->Find(&rt_key));
    1174           0 :         if (ext_connecting) {
    1175           0 :             ShowRoute show_route;
    1176           0 :             ext_connecting->FillRouteInfo(bgptable, &show_route);
    1177           0 :             ext_rt_info.set_ext_rt_svc_rt(show_route);
    1178           0 :         }
    1179           0 :         ext_connecting_rt_info_list.push_back(ext_rt_info);
    1180             :     }
    1181           0 :     info->set_ext_connecting_rt_info_list(ext_connecting_rt_info_list);
    1182           0 :     info->set_aggregate_enable(aggregate_enable());
    1183           0 : }
    1184             : 
    1185           0 : ServiceChainGroup::ServiceChainGroup(IServiceChainMgr *manager,
    1186           0 :     const string &name)
    1187           0 :     : manager_(manager),
    1188           0 :       name_(name),
    1189           0 :       oper_state_up_(false) {
    1190           0 : }
    1191             : 
    1192           0 : ServiceChainGroup::~ServiceChainGroup() {
    1193           0 :     assert(chain_set_.empty());
    1194           0 : }
    1195             : 
    1196             : //
    1197             : // Add a RoutingInstance to this ServiceChainGroup.
    1198             : // The caller must ensure that multiple bgp::ConfigHelper tasks do not
    1199             : // invoke this method in parallel.
    1200             : //
    1201           0 : void ServiceChainGroup::AddRoutingInstance(RoutingInstance *rtinstance) {
    1202           0 :     CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
    1203           0 :     chain_set_.insert(rtinstance);
    1204           0 :     manager_->UpdateServiceChainGroup(this);
    1205           0 : }
    1206             : 
    1207             : //
    1208             : // Delete a RoutingInstance from this ServiceChainGroup.
    1209             : // The caller must ensure that multiple bgp::ConfigHelper tasks do not
    1210             : // invoke this method in parallel.
    1211             : //
    1212           0 : void ServiceChainGroup::DeleteRoutingInstance(RoutingInstance *rtinstance) {
    1213           0 :     CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
    1214           0 :     chain_set_.erase(rtinstance);
    1215           0 :     manager_->UpdateServiceChainGroup(this);
    1216           0 : }
    1217             : 
    1218             : //
    1219             : // Update the operational state of this ServiceChainGroup.
    1220             : // It's considered to be up if all ServiceChains in the ServiceChainGroup
    1221             : // are up.
    1222             : // Trigger an update on the operational state of each ServiceChain if the
    1223             : // operational state of the ServiceChainGroup changed.
    1224             : //
    1225           0 : void ServiceChainGroup::UpdateOperState() {
    1226           0 :     bool old_oper_state_up = oper_state_up_;
    1227           0 :     oper_state_up_ = true;
    1228           0 :     BOOST_FOREACH(RoutingInstance *rtinstance, chain_set_) {
    1229           0 :         if (manager_->ServiceChainIsUp(rtinstance))
    1230           0 :             continue;
    1231           0 :         oper_state_up_ = false;
    1232           0 :         break;
    1233             :     }
    1234             : 
    1235           0 :     if (old_oper_state_up == oper_state_up_)
    1236           0 :         return;
    1237             : 
    1238           0 :     BOOST_FOREACH(RoutingInstance *rtinstance, chain_set_) {
    1239           0 :         manager_->UpdateServiceChain(rtinstance, oper_state_up_);
    1240             :     }
    1241             : }
    1242             : 
    1243             : /**
    1244             :   * Get appropriate listener for watching routes.
    1245             :   * For EVPN connected table, listener will depend on the connected AF
    1246             :   *
    1247             :   * @param addr          -  service-chain address
    1248             :   * @param is_conn_table -  "true" indicates connected table
    1249             :   * @return              -  Pointer to BgpConditionListener object
    1250             :   */
    1251             : template <typename T>
    1252         128 : BgpConditionListener *ServiceChainMgr<T>::GetListener() {
    1253         128 :     return server_->condition_listener(GetSCFamily());
    1254             : }
    1255             : 
    1256             : template <typename T>
    1257           0 : bool ServiceChainMgr<T>::RequestHandler(ServiceChainRequestT *req) {
    1258           0 :     CHECK_CONCURRENCY("bgp::ServiceChain");
    1259           0 :     BgpTable *table = NULL;
    1260           0 :     BgpRoute *route = NULL;
    1261           0 :     PrefixT aggregate_match = req->aggregate_match_;
    1262           0 :     ServiceChainT *info = NULL;
    1263             : 
    1264           0 :     table = req->table_;
    1265           0 :     route = req->rt_;
    1266           0 :     info = static_cast<ServiceChainT *>(req->info_.get());
    1267             : 
    1268             :     // Table where the aggregate route needs to be added
    1269           0 :     aggregate_match = req->aggregate_match_;
    1270             : 
    1271           0 :     ServiceChainState *state = NULL;
    1272           0 :     if (route) {
    1273             :         state = static_cast<ServiceChainState *>
    1274           0 :             (listener_->GetMatchState(table, route, info));
    1275             :     }
    1276             : 
    1277           0 :     switch (req->type_) {
    1278           0 :         case ServiceChainRequestT::MORE_SPECIFIC_ADD_CHG: {
    1279           0 :             assert(state);
    1280           0 :             if (state->deleted()) {
    1281           0 :                 state->reset_deleted();
    1282             :             }
    1283           0 :             if (!info->AddMoreSpecific(aggregate_match, route))
    1284           0 :                 break;
    1285           0 :             if (!info->IsConnectedRouteValid())
    1286           0 :                 break;
    1287           0 :             if (!info->group_oper_state_up())
    1288           0 :                 break;
    1289             : 
    1290           0 :             typename ServiceChainT::ConnectedPathIdList path_ids;
    1291           0 :             info->UpdateServiceChainRoute(
    1292             :                 aggregate_match, NULL, path_ids, true);
    1293           0 :             break;
    1294           0 :         }
    1295           0 :         case ServiceChainRequestT::MORE_SPECIFIC_DELETE: {
    1296           0 :             assert(state);
    1297           0 :             if (info->DeleteMoreSpecific(aggregate_match, route)) {
    1298             :                 // Delete the aggregate route
    1299           0 :                 info->DeleteServiceChainRoute(aggregate_match, true);
    1300             :             }
    1301           0 :             info->RemoveMatchState(route, state);
    1302           0 :             break;
    1303             :         }
    1304           0 :         case ServiceChainRequestT::CONNECTED_ROUTE_ADD_CHG: {
    1305           0 :             assert(state);
    1306           0 :             if (route->IsDeleted() || !route->BestPath() ||
    1307           0 :                 !route->BestPath()->IsFeasible())  {
    1308           0 :                 break;
    1309             :             }
    1310           0 :             UpdateServiceChainGroup(info->group());
    1311             : 
    1312           0 :             if (state->deleted()) {
    1313           0 :                 state->reset_deleted();
    1314             :             }
    1315             : 
    1316             :             // Store the old path id list and populate the new one.
    1317           0 :             typename ServiceChainT::ConnectedPathIdList path_ids =
    1318           0 :                 info->GetConnectedPathIds();
    1319           0 :             info->SetConnectedRoute(route);
    1320             : 
    1321           0 :             if (!info->group_oper_state_up())
    1322           0 :                 break;
    1323             : 
    1324           0 :             UpdateServiceChainRoutes(info, path_ids);
    1325           0 :             break;
    1326           0 :         }
    1327           0 :         case ServiceChainRequestT::CONNECTED_ROUTE_DELETE: {
    1328           0 :             assert(state);
    1329           0 :             UpdateServiceChainGroup(info->group());
    1330           0 :             DeleteServiceChainRoutes(info);
    1331           0 :             info->RemoveMatchState(route, state);
    1332           0 :             info->SetConnectedRoute(NULL);
    1333           0 :             break;
    1334             :         }
    1335           0 :         case ServiceChainRequestT::EXT_CONNECT_ROUTE_ADD_CHG: {
    1336           0 :             assert(state);
    1337           0 :             if (state->deleted()) {
    1338           0 :                 state->reset_deleted();
    1339             :             }
    1340           0 :             info->ext_connecting_routes()->insert(route);
    1341           0 :             if (!info->IsConnectedRouteValid())
    1342           0 :                 break;
    1343           0 :             if (!info->group_oper_state_up())
    1344           0 :                 break;
    1345           0 :             RouteT *ext_route = dynamic_cast<RouteT *>(route);
    1346           0 :             typename ServiceChainT::ConnectedPathIdList path_ids;
    1347           0 :             info->UpdateServiceChainRoute(
    1348             :                 ext_route->GetPrefix(), ext_route, path_ids, false);
    1349           0 :             break;
    1350           0 :         }
    1351           0 :         case ServiceChainRequestT::EXT_CONNECT_ROUTE_DELETE: {
    1352           0 :             assert(state);
    1353           0 :             if (info->ext_connecting_routes()->erase(route)) {
    1354           0 :                 RouteT *inet_route = dynamic_cast<RouteT *>(route);
    1355           0 :                 info->DeleteServiceChainRoute(inet_route->GetPrefix(), false);
    1356             :             }
    1357           0 :             info->RemoveMatchState(route, state);
    1358           0 :             break;
    1359             :         }
    1360           0 :         case ServiceChainRequestT::UPDATE_ALL_ROUTES: {
    1361           0 :             if (info->dest_table_unregistered())
    1362           0 :                 break;
    1363           0 :             if (info->connected_table_unregistered())
    1364           0 :                 break;
    1365           0 :             if (!info->connected_route())
    1366           0 :                 break;
    1367           0 :             if (!info->group_oper_state_up())
    1368           0 :                 break;
    1369             : 
    1370           0 :             typename ServiceChainT::ConnectedPathIdList path_ids =
    1371           0 :                 info->GetConnectedPathIds();
    1372           0 :             UpdateServiceChainRoutes(info, path_ids);
    1373           0 :             break;
    1374           0 :         }
    1375           0 :         case ServiceChainRequestT::DELETE_ALL_ROUTES: {
    1376           0 :             DeleteServiceChainRoutes(info);
    1377           0 :             break;
    1378             :         }
    1379           0 :         case ServiceChainRequestT::STOP_CHAIN_DONE: {
    1380           0 :             if (table == info->connected_table()) {
    1381           0 :                 info->set_connected_table_unregistered();
    1382           0 :                 if (!info->num_matchstate()) {
    1383           0 :                     listener_->UnregisterMatchCondition(table, info);
    1384             :                 }
    1385             :             }
    1386           0 :             if (table == info->dest_table()) {
    1387           0 :                 info->set_dest_table_unregistered();
    1388           0 :                 if (!info->num_matchstate()) {
    1389           0 :                     listener_->UnregisterMatchCondition(table, info);
    1390             :                 }
    1391             :             }
    1392           0 :             if (info->unregistered()) {
    1393           0 :                 chain_set_.erase(info->src_routing_instance());
    1394           0 :                 StartResolve();
    1395             :             }
    1396           0 :             RetryDelete();
    1397           0 :             break;
    1398             :         }
    1399           0 :         default: {
    1400           0 :             assert(false);
    1401             :             break;
    1402             :         }
    1403             :     }
    1404             : 
    1405           0 :     if (state) {
    1406           0 :         state->DecrementRefCnt();
    1407           0 :         if (state->refcnt() == 0 && state->deleted()) {
    1408           0 :             listener_->RemoveMatchState(table, route, info);
    1409           0 :             delete state;
    1410           0 :             if (!info->num_matchstate()) {
    1411           0 :                 if (info->dest_table_unregistered()) {
    1412           0 :                     listener_->UnregisterMatchCondition(
    1413             :                         info->dest_table(), info);
    1414             :                 }
    1415           0 :                 if (info->connected_table_unregistered()) {
    1416           0 :                     listener_->UnregisterMatchCondition(
    1417             :                         info->connected_table(), info);
    1418             :                 }
    1419           0 :                 if (info->unregistered()) {
    1420           0 :                     chain_set_.erase(info->src_routing_instance());
    1421           0 :                     StartResolve();
    1422             :                 }
    1423             :             }
    1424             :         }
    1425             :     }
    1426           0 :     delete req;
    1427           0 :     return true;
    1428             : }
    1429             : 
    1430             : template <typename T>
    1431         128 : ServiceChainMgr<T>::ServiceChainMgr(BgpServer *server)
    1432         128 :     : server_(server),
    1433         128 :       listener_(GetListener()),
    1434         256 :       resolve_trigger_(new TaskTrigger(
    1435             :           bind(&ServiceChainMgr::ResolvePendingServiceChain, this),
    1436         256 :           TaskScheduler::GetInstance()->GetTaskId("bgp::Config"), 0)),
    1437         256 :       group_trigger_(new TaskTrigger(
    1438             :           bind(&ServiceChainMgr::ProcessServiceChainGroups, this),
    1439         256 :           TaskScheduler::GetInstance()->GetTaskId("bgp::ServiceChain"), 0)),
    1440         128 :       aggregate_host_route_(false),
    1441         128 :       deleter_(new DeleteActor(this)),
    1442         512 :       server_delete_ref_(this, server->deleter()) {
    1443         128 :     if (service_chain_task_id_ == -1) {
    1444           4 :         TaskScheduler *scheduler = TaskScheduler::GetInstance();
    1445           4 :         service_chain_task_id_ = scheduler->GetTaskId("bgp::ServiceChain");
    1446             :     }
    1447             : 
    1448         256 :     process_queue_.reset(
    1449         128 :         new WorkQueue<ServiceChainRequestT *>(service_chain_task_id_, 0,
    1450             :                      bind(&ServiceChainMgr::RequestHandler, this, _1)));
    1451             : 
    1452         128 :     id_ = server->routing_instance_mgr()->RegisterInstanceOpCallback(
    1453             :         bind(&ServiceChainMgr::RoutingInstanceCallback, this, _1, _2));
    1454             : 
    1455         128 :     BgpMembershipManager *membership_mgr = server->membership_mgr();
    1456         128 :     registration_id_ = membership_mgr->RegisterPeerRegistrationCallback(
    1457             :         bind(&ServiceChainMgr::PeerRegistrationCallback, this, _1, _2, _3));
    1458         128 : }
    1459             : 
    1460             : template <typename T>
    1461         256 : ServiceChainMgr<T>::~ServiceChainMgr() {
    1462         128 :     assert(group_set_.empty());
    1463         128 :     assert(group_map_.empty());
    1464         384 : }
    1465             : 
    1466             : template <typename T>
    1467         128 : void ServiceChainMgr<T>::Terminate() {
    1468         128 :     process_queue_->Shutdown();
    1469         128 :     RoutingInstanceMgr *ri_mgr = server_->routing_instance_mgr();
    1470         128 :     ri_mgr->UnregisterInstanceOpCallback(id_);
    1471         128 :     BgpMembershipManager *membership_mgr = server_->membership_mgr();
    1472         128 :     membership_mgr->UnregisterPeerRegistrationCallback(registration_id_);
    1473         128 :     server_delete_ref_.Reset(NULL);
    1474         128 : }
    1475             : 
    1476             : template <typename T>
    1477         128 : void ServiceChainMgr<T>::ManagedDelete() {
    1478         128 :     deleter_->Delete();
    1479         128 : }
    1480             : 
    1481             : template <typename T>
    1482         128 : bool ServiceChainMgr<T>::MayDelete() const {
    1483         128 :     if (!chain_set_.empty() || !pending_chains_.empty())
    1484           0 :         return false;
    1485         128 :     if (!group_set_.empty() || !group_map_.empty())
    1486           0 :         return false;
    1487         128 :     return true;
    1488             : }
    1489             : 
    1490             : template <typename T>
    1491           0 : void ServiceChainMgr<T>::RetryDelete() {
    1492           0 :     if (!deleter_->IsDeleted())
    1493           0 :         return;
    1494           0 :     deleter_->RetryDelete();
    1495             : }
    1496             : 
    1497             : template <typename T>
    1498           0 : ServiceChainGroup *ServiceChainMgr<T>::FindServiceChainGroup(
    1499             :     RoutingInstance *rtinstance) {
    1500           0 :     if (ServiceChainIsPending(rtinstance)) {
    1501           0 :         PendingChainState state = GetPendingServiceChain(rtinstance);
    1502           0 :         return state.group;
    1503           0 :     } else {
    1504           0 :         const ServiceChain<T> *chain = FindServiceChain(rtinstance);
    1505           0 :         return (chain ? chain->group() : NULL);
    1506             :     }
    1507             : }
    1508             : 
    1509             : template <typename T>
    1510           0 : ServiceChainGroup *ServiceChainMgr<T>::FindServiceChainGroup(
    1511             :     const string &group_name) {
    1512           0 :     GroupMap::iterator loc = group_map_.find(group_name);
    1513           0 :     return (loc != group_map_.end() ? loc->second : NULL);
    1514             : }
    1515             : 
    1516             : template <typename T>
    1517           0 : ServiceChainGroup *ServiceChainMgr<T>::LocateServiceChainGroup(
    1518             :     const string &group_name) {
    1519           0 :     CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
    1520             : 
    1521           0 :     GroupMap::iterator loc = group_map_.find(group_name);
    1522           0 :     ServiceChainGroup *group = (loc != group_map_.end()) ? loc->second : NULL;
    1523           0 :     if (!group) {
    1524           0 :         string temp_group_name(group_name);
    1525           0 :         group = new ServiceChainGroup(this, temp_group_name);
    1526           0 :         group_map_.insert(temp_group_name, group);
    1527           0 :     }
    1528           0 :     return group;
    1529             : }
    1530             : 
    1531             : template <typename T>
    1532           0 : void ServiceChainMgr<T>::UpdateServiceChainGroup(ServiceChainGroup *group) {
    1533           0 :     CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper", "bgp::ServiceChain");
    1534             : 
    1535           0 :     if (!group)
    1536           0 :         return;
    1537           0 :     group_set_.insert(group);
    1538           0 :     group_trigger_->Set();
    1539             : }
    1540             : 
    1541             : template <typename T>
    1542           0 : bool ServiceChainMgr<T>::ProcessServiceChainGroups() {
    1543           0 :     CHECK_CONCURRENCY("bgp::ServiceChain");
    1544             : 
    1545           0 :     BOOST_FOREACH(ServiceChainGroup *group, group_set_) {
    1546           0 :         if (group->empty()) {
    1547           0 :             string temp_group_name(group->name());
    1548           0 :             group_map_.erase(temp_group_name);
    1549           0 :         } else {
    1550           0 :             group->UpdateOperState();
    1551             :         }
    1552             :     }
    1553             : 
    1554           0 :     group_set_.clear();
    1555           0 :     RetryDelete();
    1556           0 :     return true;
    1557             : }
    1558             : 
    1559             : /**
    1560             :  * Address Family - used for GetTable().
    1561             :  */
    1562             : template <>
    1563           0 : Address::Family ServiceChainMgr<ServiceChainInet>::GetFamily() const {
    1564           0 :     return Address::INET;
    1565             : }
    1566             : 
    1567             : template <>
    1568           0 : Address::Family ServiceChainMgr<ServiceChainInet6>::GetFamily() const {
    1569           0 :     return Address::INET6;
    1570             : }
    1571             : 
    1572             : template <>
    1573           0 : Address::Family ServiceChainMgr<ServiceChainEvpn>::GetFamily() const {
    1574           0 :     return Address::EVPN;
    1575             : }
    1576             : 
    1577             : template <>
    1578           0 : Address::Family ServiceChainMgr<ServiceChainEvpn6>::GetFamily() const {
    1579           0 :     return Address::EVPN;
    1580             : }
    1581             : 
    1582             : /**
    1583             :  * Connected Table Family. For EVPN, it is INET or INET6.
    1584             :  */
    1585             : template <>
    1586           0 : Address::Family ServiceChainMgr<ServiceChainInet>::GetConnectedFamily() const {
    1587           0 :     return Address::INET;
    1588             : }
    1589             : 
    1590             : template <>
    1591           0 : Address::Family ServiceChainMgr<ServiceChainInet6>::GetConnectedFamily() const {
    1592           0 :     return Address::INET6;
    1593             : }
    1594             : 
    1595             : template <>
    1596           0 : Address::Family ServiceChainMgr<ServiceChainEvpn>::GetConnectedFamily() const {
    1597           0 :     return Address::INET;
    1598             : }
    1599             : 
    1600             : template <>
    1601           0 : Address::Family ServiceChainMgr<ServiceChainEvpn6>::GetConnectedFamily() const {
    1602           0 :     return Address::INET6;
    1603             : }
    1604             : 
    1605             : /**
    1606             :  * Service Chain Family
    1607             :  */
    1608             : template <>
    1609          32 : SCAddress::Family ServiceChainMgr<ServiceChainInet>::GetSCFamily() const {
    1610          32 :     return SCAddress::INET;
    1611             : }
    1612             : 
    1613             : template <>
    1614          32 : SCAddress::Family ServiceChainMgr<ServiceChainInet6>::GetSCFamily() const {
    1615          32 :     return SCAddress::INET6;
    1616             : }
    1617             : 
    1618             : template <>
    1619          32 : SCAddress::Family ServiceChainMgr<ServiceChainEvpn>::GetSCFamily() const {
    1620          32 :     return SCAddress::EVPN;
    1621             : }
    1622             : 
    1623             : template <>
    1624          32 : SCAddress::Family ServiceChainMgr<ServiceChainEvpn6>::GetSCFamily() const {
    1625          32 :     return SCAddress::EVPN6;
    1626             : }
    1627             : 
    1628             : template <>
    1629             : template <typename T>
    1630           0 : void ServiceChainMgr<T>::Enqueue(ServiceChainRequestT *req) {
    1631           0 :     process_queue_->Enqueue(req);
    1632           0 : }
    1633             : 
    1634             : template <typename T>
    1635         768 : bool ServiceChainMgr<T>::ServiceChainIsPending(RoutingInstance *rtinstance,
    1636             :     string *reason) const {
    1637             :     typename PendingChainList::const_iterator loc =
    1638         768 :         pending_chains_.find(rtinstance);
    1639         768 :     if (loc != pending_chains_.end()) {
    1640           0 :         if (reason)
    1641           0 :             *reason = loc->second.reason;
    1642           0 :         return true;
    1643             :     }
    1644         768 :     return false;
    1645             : }
    1646             : 
    1647             : template <typename T>
    1648           0 : bool ServiceChainMgr<T>::ServiceChainIsUp(RoutingInstance *rtinstance) const {
    1649           0 :     if (ServiceChainIsPending(rtinstance))
    1650           0 :         return false;
    1651           0 :     const ServiceChain<T> *service_chain = FindServiceChain(rtinstance);
    1652           0 :     if (!service_chain)
    1653           0 :         return false;
    1654           0 :     return service_chain->IsConnectedRouteValid();
    1655             : }
    1656             : 
    1657             : template <typename T>
    1658           0 : bool ServiceChainMgr<T>::FillServiceChainInfo(RoutingInstance *rtinstance,
    1659             :         ShowServicechainInfo *info) const {
    1660           0 :     string pending_reason;
    1661           0 :     if (ServiceChainIsPending(rtinstance, &pending_reason)) {
    1662           0 :         info->set_state("pending");
    1663           0 :         info->set_pending_reason(pending_reason);
    1664           0 :         return true;
    1665             :     }
    1666           0 :     const ServiceChain<T> *service_chain = FindServiceChain(rtinstance);
    1667           0 :     if (!service_chain)
    1668           0 :         return false;
    1669           0 :     service_chain->FillServiceChainInfo(info);
    1670           0 :     return true;
    1671           0 : }
    1672             : 
    1673             : 
    1674             : 
    1675             : template <typename T>
    1676           0 : bool ServiceChainMgr<T>::LocateServiceChain(RoutingInstance *rtinstance,
    1677             :     const ServiceChainConfig &config) {
    1678           0 :     CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
    1679             : 
    1680             :     // Verify whether the entry already exists.
    1681           0 :     std::scoped_lock lock(mutex_);
    1682           0 :     ServiceChainMap::iterator it = chain_set_.find(rtinstance);
    1683           0 :     if (it != chain_set_.end()) {
    1684           0 :         ServiceChainT *chain = static_cast<ServiceChainT *>(it->second.get());
    1685           0 :         if (chain->CompareServiceChainConfig(config)) {
    1686           0 :             BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
    1687             :                 "No update in ServiceChain config : " << rtinstance->name());
    1688           0 :             return true;
    1689             :         }
    1690             : 
    1691           0 :         ServiceChainGroup *group = chain->group();
    1692           0 :         if (group) {
    1693           0 :             group->DeleteRoutingInstance(rtinstance);
    1694           0 :             chain->clear_group();
    1695             :         }
    1696             : 
    1697             :         // Update of ServiceChainConfig.
    1698             :         // Add the routing instance to pending list so that service chain is
    1699             :         // created after stop done callback for the current incarnation gets
    1700             :         // invoked.
    1701           0 :         if (config.service_chain_id.empty()) {
    1702           0 :             group = NULL;
    1703             :         } else {
    1704           0 :             group = LocateServiceChainGroup(config.service_chain_id);
    1705           0 :             group->AddRoutingInstance(rtinstance);
    1706             :         }
    1707           0 :         string reason = "Waiting for deletion of previous incarnation";
    1708           0 :         AddPendingServiceChain(rtinstance, group, reason);
    1709             : 
    1710             :         // Wait for the delete complete callback.
    1711           0 :         if (chain->deleted())
    1712           0 :             return false;
    1713             : 
    1714           0 :         BgpConditionListener::RequestDoneCb cb =
    1715             :             bind(&ServiceChainMgr::StopServiceChainDone, this, _1, _2);
    1716           0 :         listener_->RemoveMatchCondition(chain->dest_table(), chain, cb);
    1717           0 :         listener_->RemoveMatchCondition(chain->connected_table(), chain, cb);
    1718           0 :         return true;
    1719           0 :     }
    1720             : 
    1721           0 :     RoutingInstanceMgr *mgr = server_->routing_instance_mgr();
    1722           0 :     RoutingInstance *dest = mgr->GetRoutingInstance(config.routing_instance);
    1723             : 
    1724             :     // Dissociate from the old ServiceChainGroup.
    1725           0 :     ServiceChainGroup *group = FindServiceChainGroup(rtinstance);
    1726           0 :     if (group)
    1727           0 :         group->DeleteRoutingInstance(rtinstance);
    1728             : 
    1729             :     // Delete from the pending list. The instance would already have been
    1730             :     // removed from the pending list if this method is called when trying
    1731             :     // to resolve items in the pending list.  However, if this method is
    1732             :     // called when processing a change in the service chain config, then
    1733             :     // we may need to remove it from the pending list.
    1734           0 :     DeletePendingServiceChain(rtinstance);
    1735             : 
    1736             :     // Locate the new ServiceChainGroup.
    1737           0 :     if (config.service_chain_id.empty()) {
    1738           0 :         group = NULL;
    1739             :     } else {
    1740           0 :         group = LocateServiceChainGroup(config.service_chain_id);
    1741           0 :         group->AddRoutingInstance(rtinstance);
    1742             :     }
    1743             : 
    1744             :     // Destination routing instance does not exist.
    1745           0 :     if (!dest) {
    1746           0 :         string reason = "Destination routing instance does not exist";
    1747           0 :         AddPendingServiceChain(rtinstance, group, reason);
    1748           0 :         return false;
    1749           0 :     }
    1750             : 
    1751             :     // Destination routing instance is being deleted.
    1752           0 :     if (dest->deleted()) {
    1753           0 :         string reason = "Destination routing instance is being deleted";
    1754           0 :         AddPendingServiceChain(rtinstance, group, reason);
    1755           0 :         return false;
    1756           0 :     }
    1757             : 
    1758             :     // Destination virtual network index is unknown.
    1759           0 :     if (!dest->virtual_network_index()) {
    1760           0 :         string reason = "Destination virtual network index is unknown";
    1761           0 :         AddPendingServiceChain(rtinstance, group, reason);
    1762           0 :         return false;
    1763           0 :     }
    1764             : 
    1765           0 :     RoutingInstance *connected_ri = NULL;
    1766           0 :     if (config.source_routing_instance == "") {
    1767           0 :         connected_ri = rtinstance;
    1768           0 :         assert(!rtinstance->deleted());
    1769             :     } else {
    1770           0 :         connected_ri = mgr->GetRoutingInstance(config.source_routing_instance);
    1771             :     }
    1772             : 
    1773             :     // Connected routing instance does not exist.
    1774           0 :     if (!connected_ri) {
    1775           0 :         string reason = "Connected routing instance does not exist";
    1776           0 :         AddPendingServiceChain(rtinstance, group, reason);
    1777           0 :         return false;
    1778           0 :     }
    1779             : 
    1780             :     // Connected routing instance is being deleted.
    1781           0 :     if (connected_ri->deleted()) {
    1782           0 :         string reason = "Connected routing instance is being deleted";
    1783           0 :         AddPendingServiceChain(rtinstance, group, reason);
    1784           0 :         return false;
    1785           0 :     }
    1786             : 
    1787             :     // Add to pending queue if the service chain address is invalid.
    1788           0 :     error_code ec;
    1789           0 :     AddressT chain_addr =
    1790           0 :         AddressT::from_string(config.service_chain_address, ec);
    1791           0 :     if (ec.failed()) {
    1792           0 :         string reason = "Service chain address is invalid";
    1793           0 :         AddPendingServiceChain(rtinstance, group, reason);
    1794           0 :         return false;
    1795           0 :     }
    1796             : 
    1797             :     /**
    1798             :      * Get the BGP Tables to add condition
    1799             :      * For EVPN, connected table will be INET/INET6 depending on
    1800             :      * whether prefix carried is v4/v6.
    1801             :      */
    1802           0 :     BgpTable *connected_table = NULL;
    1803           0 :     connected_table = connected_ri->GetTable(GetConnectedFamily());
    1804           0 :     assert(connected_table);
    1805           0 :     BgpTable *dest_table = dest->GetTable(GetFamily());
    1806           0 :     assert(dest_table);
    1807             : 
    1808             :     // Allocate the new service chain.
    1809           0 :     ServiceChainPtr chain = ServiceChainPtr(new ServiceChainT(this, group,
    1810           0 :         rtinstance, dest, connected_ri, config.prefix, chain_addr,
    1811           0 :         config.sc_head, config.retain_as_path));
    1812             : 
    1813           0 :     if (aggregate_host_route()) {
    1814           0 :         ServiceChainT *obj = static_cast<ServiceChainT *>(chain.get());
    1815           0 :         obj->set_aggregate_enable();
    1816             :     }
    1817             : 
    1818             :     // Add the new service chain request
    1819           0 :     chain_set_.insert(make_pair(rtinstance, chain));
    1820           0 :     listener_->AddMatchCondition(
    1821           0 :         connected_table, chain.get(), BgpConditionListener::RequestDoneCb());
    1822           0 :     listener_->AddMatchCondition(
    1823           0 :         dest_table, chain.get(), BgpConditionListener::RequestDoneCb());
    1824             : 
    1825           0 :     return true;
    1826           0 : }
    1827             : 
    1828             : template <typename T>
    1829           0 : ServiceChain<T> *ServiceChainMgr<T>::FindServiceChain(
    1830             :         const string &instance) const {
    1831           0 :     RoutingInstance *rtinstance =
    1832           0 :         server_->routing_instance_mgr()->GetRoutingInstance(instance);
    1833           0 :     if (!rtinstance)
    1834           0 :         return NULL;
    1835           0 :     ServiceChainMap::const_iterator it = chain_set_.find(rtinstance);
    1836           0 :     if (it == chain_set_.end())
    1837           0 :         return NULL;
    1838           0 :     ServiceChainT *chain = static_cast<ServiceChainT *>(it->second.get());
    1839           0 :     return chain;
    1840             : }
    1841             : 
    1842             : template <typename T>
    1843         768 : ServiceChain<T> *ServiceChainMgr<T>::FindServiceChain(
    1844             :     RoutingInstance *rtinstance) const {
    1845         768 :     ServiceChainMap::const_iterator it = chain_set_.find(rtinstance);
    1846         768 :     if (it == chain_set_.end())
    1847         768 :         return NULL;
    1848           0 :     ServiceChainT *chain = static_cast<ServiceChainT *>(it->second.get());
    1849           0 :     return chain;
    1850             : }
    1851             : 
    1852             : template <typename T>
    1853           0 : bool ServiceChainMgr<T>::ResolvePendingServiceChain() {
    1854           0 :     CHECK_CONCURRENCY("bgp::Config");
    1855           0 :     for (typename PendingChainList::iterator it = pending_chains_.begin(), next;
    1856           0 :          it != pending_chains_.end(); it = next) {
    1857           0 :         next = it;
    1858           0 :         ++next;
    1859           0 :         RoutingInstance *rtinstance = it->first;
    1860           0 :         ServiceChainGroup *group = it->second.group;
    1861           0 :         if (group)
    1862           0 :             group->DeleteRoutingInstance(rtinstance);
    1863           0 :         pending_chains_.erase(it);
    1864             :         const ServiceChainConfig *sc_config =
    1865           0 :             rtinstance->config()->service_chain_info(GetSCFamily());
    1866           0 :         if (sc_config)
    1867           0 :             LocateServiceChain(rtinstance, *sc_config);
    1868             :     }
    1869           0 :     RetryDelete();
    1870           0 :     return true;
    1871             : }
    1872             : 
    1873             : template <typename T>
    1874         768 : void ServiceChainMgr<T>::RoutingInstanceCallback(string name, int op) {
    1875         768 :     if (op != RoutingInstanceMgr::INSTANCE_DELETE)
    1876         768 :         StartResolve();
    1877         768 : }
    1878             : 
    1879             : template <typename T>
    1880         768 : void ServiceChainMgr<T>::StartResolve() {
    1881         768 :     if (pending_chains_.empty())
    1882         768 :         return;
    1883           0 :     resolve_trigger_->Set();
    1884             : }
    1885             : 
    1886             : template <typename T>
    1887           0 : void ServiceChainMgr<T>::StopServiceChainDone(BgpTable *table,
    1888             :                                            ConditionMatch *info) {
    1889             :     // Post the RequestDone event to ServiceChain task to take Action
    1890           0 :     ServiceChainRequestT *req =
    1891           0 :         new ServiceChainRequestT(ServiceChainRequestT::STOP_CHAIN_DONE, table,
    1892           0 :                                 NULL, PrefixT(), ServiceChainPtr(info));
    1893           0 :     Enqueue(req);
    1894           0 :     return;
    1895             : }
    1896             : 
    1897             : template <typename T>
    1898         768 : void ServiceChainMgr<T>::StopServiceChain(RoutingInstance *rtinstance) {
    1899         768 :     CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
    1900             : 
    1901             :     // Remove the routing instance from pending chains list.
    1902         768 :     std::scoped_lock lock(mutex_);
    1903         768 :     if (ServiceChainIsPending(rtinstance)) {
    1904           0 :         ServiceChainGroup *group = FindServiceChainGroup(rtinstance);
    1905           0 :         if (group)
    1906           0 :             group->DeleteRoutingInstance(rtinstance);
    1907           0 :         DeletePendingServiceChain(rtinstance);
    1908           0 :         RetryDelete();
    1909             :     }
    1910             : 
    1911         768 :     ServiceChainT *chain = FindServiceChain(rtinstance);
    1912         768 :     if (!chain)
    1913         768 :         return;
    1914             : 
    1915           0 :     ServiceChainGroup *group = chain->group();
    1916           0 :     if (group) {
    1917           0 :         group->DeleteRoutingInstance(rtinstance);
    1918           0 :         chain->clear_group();
    1919             :     }
    1920             : 
    1921           0 :     if (chain->deleted())
    1922           0 :         return;
    1923             : 
    1924           0 :     BgpConditionListener::RequestDoneCb cb =
    1925             :         bind(&ServiceChainMgr::StopServiceChainDone, this, _1, _2);
    1926           0 :     listener_->RemoveMatchCondition(chain->dest_table(), chain, cb);
    1927           0 :     listener_->RemoveMatchCondition(chain->connected_table(), chain, cb);
    1928         768 : }
    1929             : 
    1930             : template <typename T>
    1931           0 : void ServiceChainMgr<T>::UpdateServiceChain(RoutingInstance *rtinstance,
    1932             :     bool group_oper_state_up) {
    1933             :     // Bail if there's no service chain for the instance.
    1934           0 :     ServiceChainT *chain = FindServiceChain(rtinstance);
    1935           0 :     if (!chain)
    1936           0 :         return;
    1937             : 
    1938             :     // Update the state in the service chain.
    1939           0 :     chain->set_group_oper_state_up(group_oper_state_up);
    1940             : 
    1941             :     // Post event to ServiceChain task to update/delete all routes.
    1942             :     typename ServiceChainRequestT::RequestType req_type;
    1943           0 :     if (group_oper_state_up) {
    1944           0 :         req_type = ServiceChainRequestT::UPDATE_ALL_ROUTES;
    1945             :     } else {
    1946           0 :         req_type = ServiceChainRequestT::DELETE_ALL_ROUTES;
    1947             :     }
    1948           0 :     ServiceChainRequestT *req = new ServiceChainRequestT(
    1949           0 :         req_type, NULL, NULL, PrefixT(), ServiceChainPtr(chain));
    1950           0 :     Enqueue(req);
    1951             : }
    1952             : 
    1953             : template <typename T>
    1954           0 : void ServiceChainMgr<T>::UpdateServiceChainRoutes(ServiceChainT *chain,
    1955             :     const typename ServiceChainT::ConnectedPathIdList &old_path_ids) {
    1956             :     // Update ServiceChain routes for aggregates.
    1957             :     typename ServiceChainT::PrefixToRouteListMap *vn_prefix_list =
    1958           0 :         chain->prefix_to_route_list_map();
    1959           0 :     for (typename ServiceChainT::PrefixToRouteListMap::iterator it =
    1960           0 :         vn_prefix_list->begin(); it != vn_prefix_list->end(); ++it) {
    1961           0 :         if (!it->second.empty())
    1962           0 :             chain->UpdateServiceChainRoute(it->first, NULL, old_path_ids, true);
    1963             :     }
    1964             : 
    1965             :     // Update ServiceChain routes for external connecting routes.
    1966           0 :     for (typename ServiceChainT::ExtConnectRouteList::iterator it =
    1967           0 :         chain->ext_connecting_routes()->begin();
    1968           0 :         it != chain->ext_connecting_routes()->end(); ++it) {
    1969           0 :         RouteT *ext_route = static_cast<RouteT *>(*it);
    1970           0 :         chain->UpdateServiceChainRoute(
    1971             :             ext_route->GetPrefix(), ext_route, old_path_ids, false);
    1972             :     }
    1973           0 : }
    1974             : 
    1975             : template <typename T>
    1976           0 : void ServiceChainMgr<T>::DeleteServiceChainRoutes(ServiceChainT *chain) {
    1977             :     // Delete ServiceChain routes for aggregates.
    1978             :     typename ServiceChainT::PrefixToRouteListMap *vn_prefix_list =
    1979           0 :         chain->prefix_to_route_list_map();
    1980           0 :     for (typename ServiceChainT::PrefixToRouteListMap::iterator it =
    1981           0 :         vn_prefix_list->begin(); it != vn_prefix_list->end(); ++it) {
    1982           0 :         chain->DeleteServiceChainRoute(it->first, true);
    1983             :     }
    1984             : 
    1985             :     // Delete ServiceChain routes for external connecting routes.
    1986           0 :     for (typename ServiceChainT::ExtConnectRouteList::iterator it =
    1987           0 :         chain->ext_connecting_routes()->begin();
    1988           0 :         it != chain->ext_connecting_routes()->end(); ++it) {
    1989           0 :         RouteT *ext_route = static_cast<RouteT *>(*it);
    1990           0 :         chain->DeleteServiceChainRoute(ext_route->GetPrefix(), false);
    1991             :     }
    1992           0 : }
    1993             : 
    1994             : template <typename T>
    1995           0 : void ServiceChainMgr<T>::PeerRegistrationCallback(IPeer *peer, BgpTable *table,
    1996             :                                                bool unregister) {
    1997           0 :     CHECK_CONCURRENCY("bgp::PeerMembership");
    1998             : 
    1999             :     // Bail if it's not an XMPP peer.
    2000           0 :     if (!peer->IsXmppPeer())
    2001           0 :         return;
    2002             : 
    2003             :     // Bail if there's no service chain for the instance.
    2004           0 :     ServiceChainT *chain = FindServiceChain(table->routing_instance());
    2005           0 :     if (!chain)
    2006           0 :         return;
    2007             : 
    2008             :     // Post event to ServiceChain task to update all routes.
    2009           0 :     ServiceChainRequestT *req =
    2010           0 :         new ServiceChainRequestT(ServiceChainRequestT::UPDATE_ALL_ROUTES, NULL,
    2011           0 :                                 NULL, PrefixT(), ServiceChainPtr(chain));
    2012           0 :     Enqueue(req);
    2013             : }
    2014             : 
    2015             : template <typename T>
    2016           0 : void ServiceChainMgr<T>::DisableResolveTrigger() {
    2017           0 :     resolve_trigger_->set_disable();
    2018           0 : }
    2019             : 
    2020             : template <typename T>
    2021           0 : void ServiceChainMgr<T>::EnableResolveTrigger() {
    2022           0 :     resolve_trigger_->set_enable();
    2023           0 : }
    2024             : 
    2025             : template <typename T>
    2026           0 : void ServiceChainMgr<T>::DisableGroupTrigger() {
    2027           0 :     group_trigger_->set_disable();
    2028           0 : }
    2029             : 
    2030             : template <typename T>
    2031           0 : void ServiceChainMgr<T>::EnableGroupTrigger() {
    2032           0 :     group_trigger_->set_enable();
    2033           0 : }
    2034             : 
    2035             : template <typename T>
    2036           0 : uint32_t ServiceChainMgr<T>::GetDownServiceChainCount() const {
    2037           0 :     uint32_t count = 0;
    2038           0 :     for (ServiceChainMap::const_iterator it = chain_set_.begin();
    2039           0 :          it != chain_set_.end(); ++it) {
    2040             :         const ServiceChainT *chain =
    2041           0 :              static_cast<const ServiceChainT *>(it->second.get());
    2042           0 :         if (!chain->IsConnectedRouteValid())
    2043           0 :             count++;
    2044             :     }
    2045           0 :     return count;
    2046             : }
    2047             : 
    2048             : // Explicit instantiation of ServiceChainMgr for INET and INET6.
    2049             : template class ServiceChainMgr<ServiceChainInet>;
    2050             : template class ServiceChainMgr<ServiceChainInet6>;
    2051             : template class ServiceChainMgr<ServiceChainEvpn>;
    2052             : template class ServiceChainMgr<ServiceChainEvpn6>;

Generated by: LCOV version 1.14