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

Generated by: LCOV version 1.14