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: 1014 1077 94.2 %
Date: 2026-11-09 02:10:49 Functions: 256 266 96.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14