LCOV - code coverage report
Current view: top level - bgp - bgp_evpn.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 931 980 95.0 %
Date: 2026-11-09 02:10:49 Functions: 111 117 94.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
       3             :  * Copyright Contributors to the OpenSDN project
       4             :  */
       5             : 
       6             : #include "bgp/bgp_evpn.h"
       7             : 
       8             : #include <boost/foreach.hpp>
       9             : 
      10             : #include <algorithm>
      11             : #include <string>
      12             : 
      13             : #include "base/set_util.h"
      14             : #include "base/task_annotations.h"
      15             : #include "bgp/bgp_log.h"
      16             : #include "bgp/bgp_multicast.h"
      17             : #include "bgp/bgp_peer_types.h"
      18             : #include "bgp/bgp_server.h"
      19             : #include "bgp/bgp_update.h"
      20             : #include "bgp/ermvpn/ermvpn_route.h"
      21             : #include "bgp/ermvpn/ermvpn_table.h"
      22             : #include "bgp/evpn/evpn_table.h"
      23             : #include "bgp/extended-community/multicast_flags.h"
      24             : #include "bgp/routing-instance/routing_instance.h"
      25             : #include "bgp/routing-instance/routing_instance_analytics_types.h"
      26             : #include "bgp/routing-instance/routing_instance_log.h"
      27             : 
      28             : using std::pair;
      29             : using std::set;
      30             : using std::sort;
      31             : using std::string;
      32             : using std::vector;
      33             : 
      34             : // A global MVPN state for a given <S.G> within a EvpnProjectManager.
      35        2686 : EvpnState::EvpnState(const SG &sg, StatesMap *states, EvpnManager *manager) :
      36        8058 :         sg_(sg), global_ermvpn_tree_rt_(NULL), states_(states),
      37        2686 :         manager_(manager) {
      38        2686 :     refcount_ = 0;
      39        2686 : }
      40             : 
      41        5372 : EvpnState::~EvpnState() {
      42        2686 :     assert(!global_ermvpn_tree_rt_);
      43        2686 :     EVPN_TRACE(EvpnStateCreate, sg_.source.to_string(), sg_.group.to_string());
      44        5372 : }
      45             : 
      46       15084 : const ErmVpnTable *EvpnState::table() const {
      47       15084 :     return manager_ ? manager_->ermvpn_table() : NULL;
      48             : }
      49             : 
      50             : class EvpnManager::DeleteActor : public LifetimeActor {
      51             : public:
      52       42905 :     explicit DeleteActor(EvpnManager *evpn_manager)
      53       42905 :         : LifetimeActor(evpn_manager->server()->lifetime_manager()),
      54       42905 :           evpn_manager_(evpn_manager) {
      55       42909 :     }
      56       85818 :     virtual ~DeleteActor() {
      57       85818 :     }
      58             : 
      59       42910 :     virtual bool MayDelete() const {
      60       42910 :         CHECK_CONCURRENCY("bgp::Config");
      61       42910 :         return evpn_manager_->MayDelete();
      62             :     }
      63             : 
      64       42909 :     virtual void Shutdown() {
      65       42909 :         evpn_manager_->Shutdown();
      66       42909 :     }
      67             : 
      68       42909 :     virtual void Destroy() {
      69       42909 :         evpn_manager_->table_->DestroyEvpnManager();
      70       42909 :     }
      71             : 
      72             : private:
      73             :     EvpnManager *evpn_manager_;
      74             : };
      75             : 
      76             : //
      77             : // Constructor for EvpnMcastNode. The type indicates whether this is a local
      78             : // or remote EvpnMcastNode.
      79             : //
      80        5099 : EvpnMcastNode::EvpnMcastNode(EvpnManagerPartition *partition,
      81        5099 :     EvpnRoute *route, uint8_t type)
      82        5099 :     : partition_(partition),
      83        5099 :       state_(NULL),
      84        5099 :       route_(route),
      85        5099 :       type_(type),
      86        5099 :       label_(0),
      87        5099 :       edge_replication_not_supported_(false),
      88        5099 :       assisted_replication_supported_(false),
      89        5099 :       assisted_replication_leaf_(false) {
      90        5099 :     UpdateAttributes(route);
      91        5099 : }
      92             : 
      93        3320 : EvpnMcastNode::EvpnMcastNode(EvpnManagerPartition *partition,
      94        3320 :     EvpnRoute *route, uint8_t type, EvpnStatePtr state)
      95        3320 :     : partition_(partition),
      96        3320 :       state_(state),
      97        3320 :       route_(route),
      98        3320 :       type_(type),
      99        3320 :       label_(0),
     100        3320 :       edge_replication_not_supported_(false),
     101        3320 :       assisted_replication_supported_(false),
     102        3320 :       assisted_replication_leaf_(false) {
     103        3320 :     if (route)
     104         642 :         UpdateAttributes(route);
     105        3320 : }
     106             : 
     107             : //
     108             : // Destructor for EvpnMcastNode.
     109             : //
     110        8419 : EvpnMcastNode::~EvpnMcastNode() {
     111        8419 :     set_state(NULL);
     112        8419 : }
     113             : 
     114             : //
     115             : // Update the label and attributes for a EvpnMcastNode.
     116             : // Return true if either of them changed.
     117             : //
     118       41466 : bool EvpnMcastNode::UpdateAttributes(EvpnRoute *route) {
     119       41466 :     bool changed = false;
     120       41466 :     const BgpPath *path = route->BestPath();
     121       41466 :     if (path->GetLabel() != label_) {
     122        6533 :         label_ = path->GetLabel();
     123        6533 :         changed = true;
     124             :     }
     125       41466 :     if (path->GetAttr() != attr_) {
     126       24257 :         attr_ = path->GetAttr();
     127       24257 :         changed = true;
     128             :     }
     129             : 
     130       41466 :     const PmsiTunnel *pmsi_tunnel = attr_->pmsi_tunnel();
     131       41466 :     uint8_t ar_type = 0;
     132       41466 :     bool edge_replication_not_supported = false;
     133       41466 :     if (pmsi_tunnel) {
     134       40824 :         ar_type = pmsi_tunnel->tunnel_flags() &
     135             :                   PmsiTunnelSpec::AssistedReplicationType;
     136       40824 :         if ((pmsi_tunnel->tunnel_flags() &
     137       40824 :              PmsiTunnelSpec::EdgeReplicationSupported) == 0) {
     138        6278 :             edge_replication_not_supported = true;
     139             :         }
     140       40824 :         if (replicator_address_ != pmsi_tunnel->identifier()) {
     141        5099 :             replicator_address_ = pmsi_tunnel->identifier();
     142        5099 :             changed = true;
     143             :         }
     144             :     }
     145             : 
     146       41466 :     if (edge_replication_not_supported != edge_replication_not_supported_) {
     147        1655 :         edge_replication_not_supported_ = edge_replication_not_supported;
     148        1655 :         changed = true;
     149             :     }
     150             : 
     151       41466 :     bool assisted_replication_supported = false;
     152       41466 :     if (ar_type == PmsiTunnelSpec::ARReplicator)
     153        3464 :         assisted_replication_supported = true;
     154       41466 :     if (assisted_replication_supported != assisted_replication_supported_) {
     155         184 :         assisted_replication_supported_ = assisted_replication_supported;
     156         184 :         changed = true;
     157             :     }
     158             : 
     159       41466 :     bool assisted_replication_leaf = false;
     160       41466 :     if (ar_type == PmsiTunnelSpec::ARLeaf)
     161         964 :         assisted_replication_leaf = true;
     162       41466 :     if (assisted_replication_leaf != assisted_replication_leaf_) {
     163         531 :         assisted_replication_leaf_ = assisted_replication_leaf;
     164         531 :         changed = true;
     165             :     }
     166             : 
     167       41466 :     if (address_ != path->GetAttr()->nexthop().to_v4()) {
     168        5132 :         address_ = path->GetAttr()->nexthop().to_v4();
     169        5132 :         changed = true;
     170             :     }
     171             : 
     172       41466 :     return changed;
     173             : }
     174             : 
     175             : //
     176             : // Constructor for EvpnLocalMcastNode.
     177             : //
     178             : // Add an Inclusive Multicast route corresponding to Broadcast MAC route.
     179             : //
     180             : // Need to Notify the Broadcast MAC route so that the table Export method
     181             : // can run and build the OList. OList is not built till EvpnLocalMcastNode
     182             : // has been created.
     183             : //
     184        2150 : EvpnLocalMcastNode::EvpnLocalMcastNode(EvpnManagerPartition *partition,
     185        2150 :     EvpnRoute *route)
     186             :     : EvpnMcastNode(partition, route, EvpnMcastNode::LocalNode),
     187        2150 :       inclusive_mcast_route_(NULL) {
     188        2150 :     AddInclusiveMulticastRoute();
     189        2150 :     DBTablePartition *tbl_partition = partition_->GetTablePartition();
     190        2150 :     tbl_partition->Notify(route_);
     191        2150 : }
     192             : 
     193        3296 : EvpnLocalMcastNode::EvpnLocalMcastNode(EvpnManagerPartition *partition,
     194        3296 :     EvpnRoute *route, EvpnStatePtr state)
     195             :     : EvpnMcastNode(partition, route, EvpnMcastNode::LocalNode, state),
     196        3296 :       inclusive_mcast_route_(NULL) {
     197        3296 :     if (!route)
     198        2678 :         return;
     199         618 :     AddInclusiveMulticastRoute();
     200         618 :     DBTablePartition *tbl_partition = partition_->GetTablePartition();
     201         618 :     tbl_partition->Notify(route_);
     202           0 : }
     203             : 
     204             : //
     205             : // Destructor for EvpnLocalMcastNode.
     206             : //
     207       10892 : EvpnLocalMcastNode::~EvpnLocalMcastNode() {
     208        5446 :     DeleteInclusiveMulticastRoute();
     209       10892 : }
     210             : 
     211             : //
     212             : // Add Inclusive Multicast route for this EvpnLocalMcastNode.
     213             : // The attributes are based on the Broadcast MAC route.
     214             : //
     215       20508 : void EvpnLocalMcastNode::AddInclusiveMulticastRoute() {
     216             :     // No need to create IMET route if group is specified
     217       20508 :     if (!route_->GetPrefix().group().is_unspecified())
     218         618 :         return;
     219       19890 :     assert(!inclusive_mcast_route_);
     220       19890 :     if (label_ == 0)
     221           0 :         return;
     222             : 
     223             :     // Construct the prefix and route key.
     224             :     // Build the RD using the TOR IP address, not the TOR agent IP address.
     225             :     // This ensures that the MAC broadcast route from the primary and backup
     226             :     // TOR Agents results in the same Inclusive Multicast prefix.
     227       19890 :     const EvpnPrefix &mac_prefix = route_->GetPrefix();
     228             :     RouteDistinguisher rd(
     229       19890 :         address_.to_ulong(), mac_prefix.route_distinguisher().GetVrfId());
     230       19890 :     EvpnPrefix prefix(rd, mac_prefix.tag(), address_);
     231       19890 :     EvpnRoute rt_key(prefix);
     232             : 
     233             :     // Find or create the route.
     234       19890 :     DBTablePartition *tbl_partition = partition_->GetTablePartition();
     235       19890 :     EvpnRoute *route = static_cast<EvpnRoute *>(tbl_partition->Find(&rt_key));
     236       19890 :     if (!route) {
     237        2149 :         route = new EvpnRoute(prefix);
     238        2149 :         tbl_partition->Add(route);
     239             :     } else {
     240       17741 :         route->ClearDelete();
     241             :     }
     242             :     // Add MulticastFlags community to specify that it supports SMET route
     243       19890 :     ExtCommunity::ExtCommunityList mcastFlags;
     244       19890 :     mcastFlags.push_back(MulticastFlags().GetExtCommunity());
     245       19890 :     ExtCommunityPtr ext_community = partition_->server()->extcomm_db()->
     246       19890 :             ReplaceMFlagsAndLocate(attr_->ext_community(), mcastFlags);
     247       39780 :     attr_ = partition_->server()->attr_db()->ReplaceExtCommunityAndLocate(
     248       19890 :         attr_.get(), ext_community);
     249             : 
     250             :     // Add a path with source BgpPath::Local and the peer address as path_id.
     251       19890 :     uint32_t path_id = mac_prefix.route_distinguisher().GetAddress();
     252       19890 :     BgpPath *path = new BgpPath(path_id, BgpPath::Local, attr_, 0, label_);
     253       19890 :     route->InsertPath(path);
     254       19890 :     inclusive_mcast_route_ = route;
     255       19890 :     tbl_partition->Notify(inclusive_mcast_route_);
     256       19890 :     BGP_LOG_ROUTE(partition_->table(), static_cast<IPeer *>(NULL),
     257             :         route, "Insert new Local path");
     258       19890 : }
     259             : 
     260             : //
     261             : // Delete Inclusive Multicast route for this EvpnLocalMcastNode.
     262             : //
     263       23186 : void EvpnLocalMcastNode::DeleteInclusiveMulticastRoute() {
     264       23186 :     if (!inclusive_mcast_route_)
     265        3296 :         return;
     266             : 
     267       19890 :     const EvpnPrefix &mac_prefix = route_->GetPrefix();
     268       19890 :     uint32_t path_id = mac_prefix.route_distinguisher().GetAddress();
     269       19890 :     DBTablePartition *tbl_partition = partition_->GetTablePartition();
     270       19890 :     inclusive_mcast_route_->RemovePath(BgpPath::Local, path_id);
     271       19890 :     BGP_LOG_ROUTE(partition_->table(), static_cast<IPeer *>(NULL),
     272             :         inclusive_mcast_route_, "Delete Local path");
     273             : 
     274       19890 :     if (!inclusive_mcast_route_->HasPaths()) {
     275       19889 :         tbl_partition->Delete(inclusive_mcast_route_);
     276             :     } else {
     277           1 :         tbl_partition->Notify(inclusive_mcast_route_);
     278             :     }
     279       19890 :     inclusive_mcast_route_ = NULL;
     280             : }
     281             : 
     282             : //
     283             : // Handle update of EvpnLocalMcastNode.
     284             : //
     285             : // We delete and add the Inclusive Multicast route to ensure that all the
     286             : // attributes are updated. An in-place update is not always possible since
     287             : // the vRouter address is part of the key for the Inclusive Multicast route.
     288             : //
     289       17740 : void EvpnLocalMcastNode::TriggerUpdate() {
     290       17740 :     DeleteInclusiveMulticastRoute();
     291       17740 :     AddInclusiveMulticastRoute();
     292       17740 : }
     293             : 
     294             : //
     295             : // Construct an UpdateInfo with the RibOutAttr that needs to be advertised to
     296             : // the IPeer for the EvpnRoute associated with this EvpnLocalMcastNode. This
     297             : // is used the Export method of the EvpnTable. It is expected that the caller
     298             : // fills in the target RibPeerSet in the UpdateInfo.
     299             : //
     300             : // The main functionality here is to build a per-IPeer BgpOList from the list
     301             : // of EvpnRemoteMcastNodes.
     302             : //
     303        7113 : UpdateInfo *EvpnLocalMcastNode::GetUpdateInfo(EvpnRoute *route) {
     304        7113 :     CHECK_CONCURRENCY("db::DBTable");
     305             : 
     306             :     // Nothing to send for a leaf as it already knows the replicator-address.
     307        7113 :     if (assisted_replication_leaf_)
     308          18 :         return NULL;
     309             : 
     310        7095 :     const RoutingInstance *rti = partition_->table()->routing_instance();
     311        7095 :     bool pbb_evpn_enable = rti->virtual_network_pbb_evpn_enable();
     312        7095 :     uint32_t local_ethernet_tag = route_->GetPrefix().tag();
     313             : 
     314        7095 :     EvpnState::SG sg = EvpnState::SG(route->GetPrefix().source(),
     315       14190 :                                      route->GetPrefix().group());
     316             :     // Go through list of EvpnRemoteMcastNodes and build the BgpOList.
     317        7095 :     BgpOListSpec olist_spec(BgpAttribute::OList);
     318        7095 :     if (partition_->remote_mcast_node_list()->count(sg)) {
     319        6935 :         set<EvpnMcastNode*> nodes = (*partition_->remote_mcast_node_list())[sg];
     320      209375 :         BOOST_FOREACH(EvpnMcastNode *node, nodes) {
     321      101220 :             if (node->address() == address_)
     322       83763 :                 continue;
     323       94434 :             if (node->assisted_replication_leaf())
     324        1648 :                 continue;
     325      179953 :             if (!edge_replication_not_supported_ &&
     326       87167 :                 !node->edge_replication_not_supported())
     327       75073 :                 continue;
     328       17713 :             uint32_t remote_ethernet_tag = node->route()->GetPrefix().tag();
     329             : 
     330       17713 :             if (pbb_evpn_enable && remote_ethernet_tag &&
     331             :                 (local_ethernet_tag != remote_ethernet_tag))
     332         256 :                 continue;
     333             : 
     334       17457 :             const ExtCommunity *extcomm = node->attr()->ext_community();
     335       34914 :             BgpOListElem elem(node->address(), node->label(),
     336       34914 :                     extcomm ? extcomm->GetTunnelEncap() : vector<string>());
     337       17457 :             olist_spec.elements.push_back(elem);
     338       17457 :         }
     339        6935 :     }
     340             : 
     341             :     // Go through list of leaf EvpnMcastNodes and build the leaf BgpOList.
     342        7095 :     BgpOListSpec leaf_olist_spec(BgpAttribute::LeafOList);
     343        7095 :     if (assisted_replication_supported_) {
     344         384 :         if (partition_->leaf_node_list()->count(sg)) {
     345         212 :             set<EvpnMcastNode*> nodes = (*partition_->leaf_node_list())[sg];
     346        3508 :             BOOST_FOREACH(EvpnMcastNode *node, nodes) {
     347        1648 :                 if (node->replicator_address() != address_)
     348         816 :                     continue;
     349             : 
     350         832 :                 const ExtCommunity *extcomm = node->attr()->ext_community();
     351        1664 :                 BgpOListElem elem(node->address(), node->label(),
     352        1664 :                         extcomm ? extcomm->GetTunnelEncap() : vector<string>());
     353         832 :                 leaf_olist_spec.elements.push_back(elem);
     354         832 :             }
     355         212 :         }
     356             :     }
     357             : 
     358             :     // Bail if both BgpOLists are empty.
     359        7095 :     if (olist_spec.elements.empty() && leaf_olist_spec.elements.empty())
     360        2495 :         return NULL;
     361             : 
     362             :     // Add BgpOList and leaf BgpOList to RibOutAttr for broadcast MAC route.
     363        4600 :     BgpAttrDB *attr_db = partition_->server()->attr_db();
     364        4600 :     BgpAttrPtr attr = attr_db->ReplaceOListAndLocate(attr_.get(), &olist_spec);
     365        4600 :     attr = attr_db->ReplaceLeafOListAndLocate(attr.get(), &leaf_olist_spec);
     366             : 
     367        4600 :     UpdateInfo *uinfo = new UpdateInfo;
     368             :     uinfo->roattr =
     369        4600 :         RibOutAttr(partition_->table(), route_, attr.get(), 0, false, true);
     370        4600 :     return uinfo;
     371        7095 : }
     372             : 
     373             : //
     374             : // Constructor for EvpnRemoteMcastNode.
     375             : //
     376        2949 : EvpnRemoteMcastNode::EvpnRemoteMcastNode(EvpnManagerPartition *partition,
     377        2949 :     EvpnRoute *route)
     378        2949 :     : EvpnMcastNode(partition, route, EvpnMcastNode::RemoteNode) {
     379        2949 : }
     380             : 
     381          24 : EvpnRemoteMcastNode::EvpnRemoteMcastNode(EvpnManagerPartition *partition,
     382          24 :     EvpnRoute *route, EvpnStatePtr state)
     383          24 :     : EvpnMcastNode(partition, route, EvpnMcastNode::RemoteNode, state) {
     384          24 : }
     385             : 
     386             : //
     387             : // Destructor for EvpnRemoteMcastNode.
     388             : //
     389        5946 : EvpnRemoteMcastNode::~EvpnRemoteMcastNode() {
     390        5946 : }
     391             : 
     392             : //
     393             : // Handle update of EvpnRemoteMcastNode.
     394             : //
     395         776 : void EvpnRemoteMcastNode::TriggerUpdate() {
     396         776 : }
     397             : 
     398             : //
     399             : // Constructor for EvpnSegment.
     400             : //
     401          34 : EvpnSegment::EvpnSegment(EvpnManager *manager, const EthernetSegmentId &esi)
     402          34 :   : evpn_manager_(manager),
     403          34 :     esi_(esi),
     404          34 :     esi_ad_route_(NULL),
     405          34 :     single_active_(true),
     406          34 :     route_lists_(DB::PartitionCount()) {
     407          34 : }
     408             : 
     409             : //
     410             : // Destructor for EvpnSegment.
     411             : //
     412          68 : EvpnSegment::~EvpnSegment() {
     413          34 :     assert(!esi_ad_route_);
     414          34 :     assert(pe_list_.empty());
     415          68 : }
     416             : 
     417             : //
     418             : // Add the given MAC route as a dependent of this EvpnSegment.
     419             : //
     420          54 : void EvpnSegment::AddMacRoute(size_t part_id, EvpnRoute *route) {
     421          54 :     pair<RouteList::iterator, bool> ret = route_lists_[part_id].insert(route);
     422          54 :     assert(ret.second);
     423          54 : }
     424             : 
     425             : //
     426             : // Delete the given MAC route as a dependent of this EvpnSegment.
     427             : // Trigger deletion of the EvpnSegment if there are no dependent
     428             : // routes in the partition.
     429             : //
     430          54 : void EvpnSegment::DeleteMacRoute(size_t part_id, EvpnRoute *route) {
     431          54 :     size_t count = route_lists_[part_id].erase(route);
     432          54 :     assert(count == 1);
     433          54 :     if (route_lists_[part_id].empty())
     434          38 :         evpn_manager_->TriggerSegmentDelete(this);
     435          54 : }
     436             : 
     437             : //
     438             : // Trigger an update of all dependent MAC routes for this EvpnSegment.
     439             : // Note that the bgp::EvpnSegment task is mutually exclusive with the
     440             : // db::DBTable task.
     441             : //
     442          54 : void EvpnSegment::TriggerMacRouteUpdate() {
     443          54 :     CHECK_CONCURRENCY("bgp::EvpnSegment");
     444             : 
     445         270 :     for (size_t part_id = 0; part_id < route_lists_.size(); ++part_id) {
     446         216 :         EvpnManagerPartition *partition = evpn_manager_->GetPartition(part_id);
     447         286 :         BOOST_FOREACH(EvpnRoute *route, route_lists_[part_id]) {
     448          35 :             partition->TriggerMacRouteUpdate(route);
     449             :         }
     450             :     }
     451          54 : }
     452             : 
     453             : //
     454             : // Update the PE list for this EvpnSegment. This should be called when
     455             : // the AutoDisocvery route is updated.
     456             : // Return true if there's a change in the PE list i.e. if an entry is
     457             : // added, deleted or updated.
     458             : //
     459          56 : bool EvpnSegment::UpdatePeList() {
     460          56 :     CHECK_CONCURRENCY("bgp::EvpnSegment");
     461             : 
     462             :     // Mark all entries as invalid.
     463          56 :     for (RemotePeList::iterator it = pe_list_.begin();
     464         108 :         it != pe_list_.end(); ++it) {
     465          52 :         it->esi_valid = false;
     466             :     }
     467             : 
     468             :     // Go through all paths for the route and refresh/update existing entries
     469             :     // or add new ones as necessary. Remember that there was a change in the
     470             :     // list if any element changed or is added/deleted.
     471          56 :     bool changed = false;
     472          56 :     for (Route::PathList::const_iterator path_it =
     473         112 :          esi_ad_route_->GetPathList().begin();
     474         220 :          path_it != esi_ad_route_->GetPathList().end(); ++path_it) {
     475             :         const BgpPath *path =
     476          54 :             static_cast<const BgpPath *>(path_it.operator->());
     477          54 :         const BgpAttr *attr = path->GetAttr();
     478             : 
     479             :         // Skip non-VXLAN paths for now.
     480          54 :         const ExtCommunity *extcomm = attr->ext_community();
     481          54 :         if (!extcomm || !extcomm->ContainsTunnelEncapVxlan())
     482           2 :             continue;
     483             : 
     484             :         // Go through existing pe list and try to find the RemotePe.
     485          52 :         bool found = false;
     486          52 :         RemotePe remote_pe(path);
     487          52 :         for (RemotePeList::iterator it = pe_list_.begin();
     488          82 :             it != pe_list_.end(); ++it) {
     489             :             // Skip if the nexthop doesn't match.
     490          38 :             if (it->attr->nexthop() != remote_pe.attr->nexthop())
     491          26 :                 continue;
     492             : 
     493             :             // We're done if there are multiple paths for the same remote PE
     494             :             // e.g. when a pair of route reflectors is being used.
     495          12 :             if (it->esi_valid) {
     496           0 :                 found = true;
     497           0 :                 break;
     498             :             }
     499             : 
     500             :             // Check if we have a match.
     501          12 :             if (*it == remote_pe) {
     502           8 :                 it->esi_valid = true;
     503           8 :                 found = true;
     504           8 :                 break;
     505             :             }
     506             :         }
     507             : 
     508             :         // Add a new entry to the pe list if we didn't find the RemotePe.
     509          52 :         if (!found) {
     510          44 :             pe_list_.push_back(remote_pe);
     511          44 :             changed = true;
     512             :         }
     513          52 :     }
     514             : 
     515             :     // Erase invalid entries from the list.
     516          56 :     for (RemotePeList::iterator it = pe_list_.begin(), next = it;
     517         152 :         it != pe_list_.end(); it = next) {
     518          96 :         ++next;
     519          96 :         if (!it->esi_valid) {
     520          44 :             pe_list_.erase(it);
     521          44 :             changed = true;
     522             :         }
     523             :     }
     524             : 
     525             :     // Update the single active status of the EvpnSegment itself.
     526          56 :     single_active_ = false;
     527          56 :     for (RemotePeList::iterator it = pe_list_.begin();
     528         100 :         it != pe_list_.end(); ++it) {
     529          51 :         if (it->single_active) {
     530           7 :             single_active_ = true;
     531           7 :             break;
     532             :         }
     533             :     }
     534             : 
     535          56 :     return changed;
     536             : }
     537             : 
     538             : //
     539             : // Return true if it's safe to delete this EvpnSegment.
     540             : //
     541          49 : bool EvpnSegment::MayDelete() const {
     542          49 :     CHECK_CONCURRENCY("bgp::EvpnSegment");
     543             : 
     544             :     // Bail if we have state set on the per-ESI AD route.
     545          49 :     if (esi_ad_route_)
     546          14 :         return false;
     547             : 
     548             :     // Bail if the dependent route list for any partition is not empty.
     549         171 :     for (size_t part_id = 0; part_id < route_lists_.size(); ++part_id) {
     550         137 :         if (!route_lists_[part_id].empty())
     551           1 :             return false;
     552             :     }
     553             : 
     554          34 :     return true;
     555             : }
     556             : 
     557             : //
     558             : // Constructor for EvpnSegment::RemotePe.
     559             : //
     560          52 : EvpnSegment::RemotePe::RemotePe(const BgpPath *path)
     561          52 :   : esi_valid(true),
     562          52 :     single_active(path->GetAttr()->evpn_single_active()),
     563          52 :     peer(path->GetPeer()),
     564          52 :     attr(path->GetAttr()),
     565          52 :     flags(path->GetFlags()),
     566          52 :     src(path->GetSource()) {
     567          52 : }
     568             : 
     569             : //
     570             : // Equality operator for EvpnSegment::RemotePe.
     571             : // Do not compare esi_valid and single_active fields since they are
     572             : // derived.
     573             : //
     574          12 : bool EvpnSegment::RemotePe::operator==(const RemotePe &rhs) const {
     575          12 :     if (peer != rhs.peer)
     576           0 :         return false;
     577          12 :     if (attr != rhs.attr)
     578           4 :         return false;
     579           8 :     if (flags != rhs.flags)
     580           0 :         return false;
     581           8 :     if (src != rhs.src)
     582           0 :         return false;
     583           8 :     return true;
     584             : }
     585             : 
     586             : //
     587             : // Constructor for EvpnMacState.
     588             : //
     589          52 : EvpnMacState::EvpnMacState(EvpnManager *evpn_manager, EvpnRoute *route)
     590          52 :   : evpn_manager_(evpn_manager), route_(route), segment_(NULL) {
     591          52 : }
     592             : 
     593             : //
     594             : // Destructor for EvpnMacState.
     595             : //
     596         104 : EvpnMacState::~EvpnMacState() {
     597          52 :     assert(segment_ == NULL);
     598          52 :     assert(aliased_path_list_.empty());
     599         104 : }
     600             : 
     601             : //
     602             : // Add the BgpPath specified by the iterator to the aliased path list.
     603             : // Also inserts the BgpPath to the BgpRoute.
     604             : //
     605          28 : void EvpnMacState::AddAliasedPath(AliasedPathList::const_iterator it) {
     606          28 :     BgpPath *path = *it;
     607          28 :     const IPeer *peer = path->GetPeer();
     608          28 :     aliased_path_list_.insert(path);
     609          28 :     route_->InsertPath(path);
     610          28 :     BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
     611             :         "Added aliased path " << route_->ToString() <<
     612             :         " peer " << (peer ? peer->ToString() : "None") <<
     613             :         " nexthop " << path->GetAttr()->nexthop().to_string() <<
     614             :         " label " << path->GetLabel() <<
     615             :         " in table " << evpn_manager_->table()->name());
     616          28 : }
     617             : 
     618             : //
     619             : // Delete the BgpPath specified by the iterator from the aliased path list.
     620             : // Also deletes the BgpPath from the BgpRoute.
     621             : //
     622          28 : void EvpnMacState::DeleteAliasedPath(AliasedPathList::const_iterator it) {
     623          28 :     BgpPath *path = *it;
     624          28 :     const IPeer *peer = path->GetPeer();
     625          28 :     BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
     626             :         "Deleted aliased path " << route_->ToString() <<
     627             :         " peer " << (peer ? peer->ToString() : "None") <<
     628             :         " nexthop " << path->GetAttr()->nexthop().to_string() <<
     629             :         " label " << path->GetLabel() <<
     630             :         " in table " << evpn_manager_->table()->name());
     631          28 :     route_->DeletePath(path);
     632          28 :     aliased_path_list_.erase(it);
     633          28 : }
     634             : 
     635             : //
     636             : // Find or create the matching aliased BgpPath.
     637             : //
     638          58 : BgpPath *EvpnMacState::LocateAliasedPath(
     639             :     const EvpnSegment::RemotePe *remote_pe, uint32_t label) {
     640          58 :     const IPeer *peer = remote_pe->peer;
     641          58 :     const BgpAttr *attr = remote_pe->attr.get();
     642          58 :     uint32_t flags = remote_pe->flags | BgpPath::AliasedPath;
     643          58 :     BgpPath::PathSource src = remote_pe->src;
     644          58 :     for (AliasedPathList::iterator it = aliased_path_list_.begin();
     645          69 :         it != aliased_path_list_.end(); ++it) {
     646          41 :         BgpPath *path = *it;
     647          71 :         if (path->GetPeer() == peer &&
     648          60 :             path->GetAttr() == attr &&
     649         101 :             path->GetFlags() == flags &&
     650          30 :             path->GetLabel() == label) {
     651          30 :             return path;
     652             :         }
     653             :     }
     654             : 
     655          28 :     return (new BgpPath(peer, src, attr, flags, label));
     656             : }
     657             : 
     658             : //
     659             : // Update aliased BgpPaths for the EvpnRoute based on the remote PEs
     660             : // for the EvpnSegment.
     661             : // Return true if the list of aliased paths is modified.
     662             : //
     663         170 : bool EvpnMacState::ProcessMacRouteAliasing() {
     664         170 :     CHECK_CONCURRENCY("db::DBTable");
     665             : 
     666         169 :     const BgpPath *path = route_->BestPath();
     667             : 
     668             :     // Find the remote PE entry that corresponds to the primary path.
     669         169 :     bool found_primary_pe = false;
     670         169 :     EvpnSegment::const_iterator it;
     671         169 :     if (path && segment_ && !segment_->single_active())
     672          62 :         it = segment_->begin();
     673         241 :     for (; path && segment_ && !segment_->single_active() &&
     674         246 :          it != segment_->end(); ++it) {
     675          48 :         if (path->GetAttr()->nexthop() != it->attr->nexthop())
     676           5 :             continue;
     677          43 :         found_primary_pe = true;
     678          43 :         break;
     679             :     }
     680             : 
     681             :     // Go through the remote PE list for the EvpnSegment and build the
     682             :     // list of future aliased paths.
     683         169 :     AliasedPathList future_aliased_path_list;
     684         170 :     if (found_primary_pe && path && segment_ && !segment_->single_active())
     685          43 :         it = segment_->begin();
     686         316 :     for (; found_primary_pe && path && segment_ &&
     687         419 :          !segment_->single_active() && it != segment_->end(); ++it) {
     688             :         // Skip if there's a BGP_XMPP path with the remote PE as nexthop.
     689         103 :         if (route_->FindPath(it->attr->nexthop()))
     690          45 :             continue;
     691             : 
     692             :         // Locate the aliased path using attributes for the remote PE and
     693             :         // label from the primary path.
     694             :         BgpPath *aliased_path =
     695          58 :             LocateAliasedPath(it.operator->(), path->GetLabel());
     696          58 :         future_aliased_path_list.insert(aliased_path);
     697             :     }
     698             : 
     699             :     // Reconcile the current and future aliased paths and notify/delete the
     700             :     // route as appropriate.
     701         170 :     bool modified = set_synchronize(&aliased_path_list_,
     702             :         &future_aliased_path_list,
     703             :         boost::bind(&EvpnMacState::AddAliasedPath, this, _1),
     704             :         boost::bind(&EvpnMacState::DeleteAliasedPath, this, _1));
     705         170 :     return modified;
     706         170 : }
     707             : 
     708             : //
     709             : // Constructor for EvpnManagerPartition.
     710             : //
     711      170330 : EvpnManagerPartition::EvpnManagerPartition(EvpnManager *evpn_manager,
     712      170330 :     size_t part_id)
     713      170330 :     : evpn_manager_(evpn_manager),
     714      170330 :       part_id_(part_id),
     715      340659 :       mac_update_trigger_(new TaskTrigger(
     716             :           boost::bind(&EvpnManagerPartition::ProcessMacUpdateList, this),
     717      340641 :           TaskScheduler::GetInstance()->GetTaskId("db::DBTable"),
     718      340670 :           part_id)) {
     719      170334 :     table_partition_ = evpn_manager_->GetTablePartition(part_id);
     720      170333 : }
     721             : 
     722             : //
     723             : // Destructor for EvpnManagerPartition.
     724             : //
     725      170341 : EvpnManagerPartition::~EvpnManagerPartition() {
     726      170341 :     assert(local_mcast_node_list_.empty());
     727      170341 :     assert(remote_mcast_node_list_.empty());
     728      170341 :     assert(mac_update_list_.empty());
     729      170341 : }
     730             : 
     731             : //
     732             : // Get the DBTablePartition for the EvpnTable for our partition id.
     733             : //
     734       71178 : DBTablePartition *EvpnManagerPartition::GetTablePartition() {
     735       71178 :     return evpn_manager_->GetTablePartition(part_id_);
     736             : }
     737             : 
     738             : //
     739             : // Notify the Broadcast MAC route for the given EvpnMcastNode.
     740             : //
     741       20508 : void EvpnManagerPartition::NotifyNodeRoute(EvpnMcastNode *node) {
     742       20508 :     DBTablePartition *tbl_partition = GetTablePartition();
     743       20508 :     tbl_partition->Notify(node->route());
     744       20508 : }
     745             : 
     746             : //
     747             : // Go through all replicator EvpnMcastNodes and notify associated Broadcast
     748             : // MAC route.
     749             : //
     750        1026 : void EvpnManagerPartition::NotifyReplicatorNodeRoutes() {
     751        1026 :     DBTablePartition *tbl_partition = GetTablePartition();
     752        1026 :     EvpnMcastNodeList::const_iterator it = replicator_node_list_.begin();
     753        1986 :     for (; it != replicator_node_list_.end(); it++) {
     754        4800 :         BOOST_FOREACH(EvpnMcastNode *node, it->second)
     755        1920 :             tbl_partition->Notify(node->route());
     756             :     }
     757        1026 : }
     758             : 
     759             : //
     760             : // Go through all ingress replication client EvpnMcastNodes and notify the
     761             : // associated Broadcast MAC route.
     762             : //
     763        7096 : void EvpnManagerPartition::NotifyIrClientNodeRoutes(
     764             :     bool exclude_edge_replication_supported) {
     765        7096 :     DBTablePartition *tbl_partition = GetTablePartition();
     766        7096 :     EvpnMcastNodeList::const_iterator it = ir_client_node_list_.begin();
     767       12993 :     for (; it != ir_client_node_list_.end(); it++) {
     768      116723 :         BOOST_FOREACH(EvpnMcastNode *node, it->second) {
     769       94532 :             if (exclude_edge_replication_supported &&
     770       39119 :                 !node->edge_replication_not_supported()) {
     771       37586 :                 continue;
     772             :             }
     773       17827 :             tbl_partition->Notify(node->route());
     774             :         }
     775             :     }
     776        7096 : }
     777             : 
     778             : //
     779             : // Add an EvpnMcastNode to the EvpnManagerPartition.
     780             : //
     781        5741 : void EvpnManagerPartition::AddMcastNode(EvpnMcastNode *node, EvpnRoute *rt) {
     782        5741 :     EvpnState::SG sg = EvpnState::SG(rt->GetPrefix().source(),
     783       11482 :                                      rt->GetPrefix().group());
     784        5741 :     if (node->type() == EvpnMcastNode::LocalNode) {
     785        2768 :         local_mcast_node_list_[sg].insert(node);
     786        2768 :         if (node->assisted_replication_supported())
     787          92 :             replicator_node_list_[sg].insert(node);
     788        2768 :         if (!node->assisted_replication_leaf())
     789        2606 :             ir_client_node_list_[sg].insert(node);
     790        2768 :         NotifyNodeRoute(node);
     791             :     } else {
     792        2973 :         remote_mcast_node_list_[sg].insert(node);
     793        2973 :         if (node->assisted_replication_leaf()) {
     794         369 :             leaf_node_list_[sg].insert(node);
     795         369 :             NotifyReplicatorNodeRoutes();
     796        2604 :         } else if (node->edge_replication_not_supported()) {
     797         728 :             regular_node_list_[sg].insert(node);
     798         728 :             NotifyIrClientNodeRoutes(false);
     799        1876 :         } else if (!node->assisted_replication_leaf()) {
     800        1876 :             NotifyIrClientNodeRoutes(true);
     801             :         }
     802             :     }
     803        5741 : }
     804             : 
     805             : //
     806             : // Delete an EvpnMcastNode from the EvpnManagerPartition.
     807             : //
     808       54255 : bool EvpnManagerPartition::RemoveMcastNodeFromList(EvpnState::SG &sg,
     809             :                                                    EvpnMcastNode *node,
     810             :                                                    EvpnMcastNodeList *list) {
     811       54255 :     size_t deleted = 0;
     812       54255 :     if (list->count(sg)) {
     813       30864 :         deleted = (*list)[sg].erase(node);
     814       30864 :         if ((*list)[sg].size() == 0)
     815        2195 :             list->erase(sg);
     816             :     }
     817       54255 :     return (deleted > 0);
     818             : }
     819             : 
     820             : //
     821             : // Delete an EvpnMcastNode from the EvpnManagerPartition.
     822             : //
     823        5741 : void EvpnManagerPartition::DeleteMcastNode(EvpnMcastNode *node,
     824             :                                            EvpnRoute * rt) {
     825        5741 :     EvpnState::SG sg = EvpnState::SG(rt->GetPrefix().source(),
     826       11482 :                                      rt->GetPrefix().group());
     827        5741 :     if (node->type() == EvpnMcastNode::LocalNode) {
     828        2768 :         RemoveMcastNodeFromList(sg, node, &local_mcast_node_list_);
     829        2768 :         RemoveMcastNodeFromList(sg, node, &replicator_node_list_);
     830        2768 :         RemoveMcastNodeFromList(sg, node, &ir_client_node_list_);
     831             :     } else {
     832        2973 :         RemoveMcastNodeFromList(sg, node, &remote_mcast_node_list_);
     833        2973 :         if (RemoveMcastNodeFromList(sg, node, &leaf_node_list_)) {
     834         369 :             NotifyReplicatorNodeRoutes();
     835             :         } else {
     836        2604 :             NotifyIrClientNodeRoutes(true);
     837             :         }
     838        2973 :         if (RemoveMcastNodeFromList(sg, node, &regular_node_list_)) {
     839         912 :             NotifyIrClientNodeRoutes(false);
     840             :         }
     841             :     }
     842        5741 :     if (empty())
     843         511 :         evpn_manager_->RetryDelete();
     844        5741 : }
     845             : 
     846             : //
     847             : // Update an EvpnMcastNode in the EvpnManagerPartition.
     848             : // Need to remove/add EvpnMcastNode from the replicator, leaf and ir client
     849             : // lists as appropriate.
     850             : //
     851       18516 : void EvpnManagerPartition::UpdateMcastNode(EvpnMcastNode *node, EvpnRoute *rt) {
     852       18516 :     node->TriggerUpdate();
     853       18516 :     EvpnState::SG sg = EvpnState::SG(rt->GetPrefix().source(),
     854       37032 :                                      rt->GetPrefix().group());
     855       18516 :     if (node->type() == EvpnMcastNode::LocalNode) {
     856       17740 :         RemoveMcastNodeFromList(sg, node, &replicator_node_list_);
     857       17740 :         if (node->assisted_replication_supported())
     858        1640 :             replicator_node_list_[sg].insert(node);
     859       17740 :         RemoveMcastNodeFromList(sg, node, &ir_client_node_list_);
     860       17740 :         if (!node->assisted_replication_leaf())
     861       17596 :             ir_client_node_list_[sg].insert(node);
     862       17740 :         NotifyNodeRoute(node);
     863             :     } else {
     864         776 :         bool was_leaf = RemoveMcastNodeFromList(sg, node, &leaf_node_list_);
     865         776 :         if (node->assisted_replication_leaf())
     866         288 :             leaf_node_list_[sg].insert(node);
     867         776 :         if (was_leaf || node->assisted_replication_leaf())
     868         288 :             NotifyReplicatorNodeRoutes();
     869         776 :         if (!was_leaf || !node->assisted_replication_leaf())
     870         488 :             NotifyIrClientNodeRoutes(true);
     871         776 :         bool was_regular = RemoveMcastNodeFromList(
     872             :                                    sg, node, &regular_node_list_);
     873         776 :         if (node->edge_replication_not_supported())
     874         456 :             regular_node_list_[sg].insert(node);
     875         776 :         if (was_regular || node->edge_replication_not_supported())
     876         488 :             NotifyIrClientNodeRoutes(false);
     877             :     }
     878       18516 : }
     879             : 
     880             : //
     881             : // Add the given MAC route to the update list.
     882             : // This method gets called either when the MAC route itself changes or when
     883             : // the remote PE list for the EvpnSegment of the MAC route gets updated.
     884             : //
     885         176 : void EvpnManagerPartition::TriggerMacRouteUpdate(EvpnRoute *route) {
     886         176 :     CHECK_CONCURRENCY("db::DBTable", "bgp::EvpnSegment");
     887             : 
     888         176 :     mac_update_list_.insert(route);
     889         176 :     mac_update_trigger_->Set();
     890         176 : }
     891             : 
     892             : //
     893             : // Process the MAC route update list for this EvpnManagerPartition.
     894             : //
     895         134 : bool EvpnManagerPartition::ProcessMacUpdateList() {
     896         134 :     CHECK_CONCURRENCY("db::DBTable");
     897             : 
     898         133 :     BgpTable *table = evpn_manager_->table();
     899         133 :     int listener_id = evpn_manager_->listener_id();
     900             : 
     901         471 :     BOOST_FOREACH(EvpnRoute *route, mac_update_list_) {
     902             :         // Skip if the route is on the change list. We will get another
     903             :         // chance to process it after the MacAdvertisement listener sees
     904             :         // it and changes the EvpnMacState to point to the updated value
     905             :         // of EvpnSegment.
     906         168 :         if (route->is_onlist())
     907           0 :             continue;
     908             : 
     909             :         EvpnMacState *mac_state =
     910         168 :             dynamic_cast<EvpnMacState *>(route->GetState(table, listener_id));
     911         170 :         assert(mac_state);
     912         170 :         bool modified = mac_state->ProcessMacRouteAliasing();
     913         170 :         if (route->HasPaths()) {
     914         127 :             if (!mac_state->segment()) {
     915           9 :                 route->ClearState(table, listener_id);
     916           9 :                 delete mac_state;
     917             :             }
     918         127 :             if (modified) {
     919          32 :                 table_partition_->Notify(route);
     920             :             }
     921             :         } else {
     922          43 :             route->ClearState(table, listener_id);
     923          43 :             table_partition_->Delete(route);
     924          43 :             delete mac_state;
     925             :         }
     926             :     }
     927             : 
     928         133 :     mac_update_list_.clear();
     929         133 :     evpn_manager_->RetryDelete();
     930         133 :     return true;
     931             : }
     932             : 
     933        1409 : bool EvpnManagerPartition::GetForestNodeAddress(ErmVpnRoute *rt,
     934             :                                                  Ip4Address *address) const {
     935        1409 :     if (!evpn_manager_->ermvpn_table()->tree_manager())
     936           0 :         return false;
     937             :     uint32_t label;
     938        1409 :     vector<string> te;
     939        1409 :     return evpn_manager_->ermvpn_table()->tree_manager()->GetForestNodePMSI(
     940        1409 :                                            rt, &label, address, &te);
     941        1409 : }
     942             : 
     943           0 : EvpnStatePtr EvpnManagerPartition::GetState(const SG &sg) const {
     944           0 :     EvpnState::StatesMap::const_iterator iter = states_.find(sg);
     945           0 :     return iter != states_.end() ?  iter->second : NULL;
     946             : }
     947             : 
     948        5069 : EvpnStatePtr EvpnManagerPartition::GetState(const SG &sg) {
     949        5069 :     EvpnState::StatesMap::iterator iter = states_.find(sg);
     950        5069 :     return iter != states_.end() ?  iter->second : NULL;
     951             : }
     952             : 
     953         642 : EvpnStatePtr EvpnManagerPartition::GetState(EvpnRoute *rt) {
     954         642 :     EvpnState::SG sg = EvpnState::SG(rt->GetPrefix().source(),
     955        1284 :                                      rt->GetPrefix().group());
     956        1284 :     return GetState(sg);
     957             : }
     958             : 
     959        2686 : EvpnStatePtr EvpnManagerPartition::CreateState(const SG &sg) {
     960        2686 :     EvpnStatePtr state(new EvpnState(sg, &states_, evpn_manager_));
     961        2686 :     assert(states_.insert(make_pair(sg, state.get())).second);
     962        2686 :     EVPN_TRACE(EvpnStateCreate, sg.source.to_string(), sg.group.to_string());
     963        2686 :     return state;
     964           0 : }
     965             : 
     966        4427 : EvpnStatePtr EvpnManagerPartition::LocateState(const SG &sg) {
     967        4427 :     EvpnStatePtr evpn_state = GetState(sg);
     968        4427 :     if (evpn_state)
     969        1741 :         return evpn_state;
     970        2686 :     evpn_state = CreateState(sg);
     971        2686 :     assert(evpn_state);
     972        2686 :     return evpn_state;
     973           0 : }
     974             : 
     975        1749 : EvpnStatePtr EvpnManagerPartition::LocateState(EvpnRoute *rt) {
     976        1749 :     EvpnState::SG sg = EvpnState::SG(rt->GetPrefix().source(),
     977        3498 :                                      rt->GetPrefix().group());
     978        3498 :     return LocateState(sg);
     979             : }
     980             : 
     981             : //
     982             : // Disable processing of the update list.
     983             : // For testing only.
     984             : //
     985          12 : void EvpnManagerPartition::DisableMacUpdateProcessing() {
     986          12 :     mac_update_trigger_->set_disable();
     987          12 : }
     988             : 
     989             : //
     990             : // Enable processing of the update list.
     991             : // For testing only.
     992             : //
     993          12 : void EvpnManagerPartition::EnableMacUpdateProcessing() {
     994          12 :     mac_update_trigger_->set_enable();
     995          12 : }
     996             : 
     997             : //
     998             : // Return true if the EvpnManagerPartition is empty i.e. it has no local
     999             : // or remote EvpnMcastNodes and no MAC routes that need to be updated.
    1000             : //
    1001      176083 : bool EvpnManagerPartition::empty() const {
    1002      176083 :     if (!local_mcast_node_list_.empty())
    1003        4628 :         return false;
    1004      171455 :     if (!remote_mcast_node_list_.empty())
    1005         602 :         return false;
    1006      170853 :     if (!mac_update_list_.empty())
    1007           0 :         return false;
    1008      170853 :     assert(leaf_node_list_.empty());
    1009      170853 :     assert(replicator_node_list_.empty());
    1010      170853 :     assert(regular_node_list_.empty());
    1011      170853 :     assert(ir_client_node_list_.empty());
    1012      170853 :     return true;
    1013             : }
    1014             : 
    1015             : //
    1016             : // Return the BgpServer for the EvpnManagerPartition.
    1017             : //
    1018       45006 : BgpServer *EvpnManagerPartition::server() {
    1019       45006 :     return evpn_manager_->server();
    1020             : }
    1021             : 
    1022             : //
    1023             : // Return the EvpnTable for the EvpnManagerPartition.
    1024             : //
    1025       26791 : const EvpnTable *EvpnManagerPartition::table() const {
    1026       26791 :     return evpn_manager_->table();
    1027             : }
    1028             : 
    1029             : //
    1030             : // Constructor for EvpnManager.
    1031             : //
    1032       42909 : EvpnManager::EvpnManager(EvpnTable *table)
    1033       42909 :     : table_(table),
    1034       42909 :       listener_id_(DBTable::kInvalidId),
    1035       85817 :       segment_delete_trigger_(new TaskTrigger(
    1036             :           boost::bind(&EvpnManager::ProcessSegmentDeleteSet, this),
    1037       85816 :           TaskScheduler::GetInstance()->GetTaskId("bgp::EvpnSegment"), 0)),
    1038       85817 :       segment_update_trigger_(new TaskTrigger(
    1039             :           boost::bind(&EvpnManager::ProcessSegmentUpdateSet, this),
    1040       85815 :           TaskScheduler::GetInstance()->GetTaskId("bgp::EvpnSegment"), 0)),
    1041       85818 :       table_delete_ref_(this, table->deleter()) {
    1042       42909 :     deleter_.reset(new DeleteActor(this));
    1043       42909 :     db_states_count_ = 0;
    1044       42909 : }
    1045             : 
    1046             : //
    1047             : // Destructor for EvpnManager.
    1048             : //
    1049       85666 : EvpnManager::~EvpnManager() {
    1050       42909 :     assert(segment_map_.empty());
    1051       42909 :     assert(segment_delete_set_.empty());
    1052       42909 :     assert(segment_update_set_.empty());
    1053       85666 : }
    1054             : 
    1055             : //
    1056             : // Initialize the EvpnManager. We allocate the EvpnManagerPartitions
    1057             : // and register a DBListener for the EvpnTable.
    1058             : //
    1059       42755 : void EvpnManager::Initialize() {
    1060       42755 :     AllocPartitions();
    1061       42756 :     listener_id_ = table_->Register(
    1062             :         boost::bind(&EvpnManager::RouteListener, this, _1, _2),
    1063             :         "EvpnManager");
    1064       42757 :     ermvpn_table_ = dynamic_cast<ErmVpnTable *>(
    1065       42757 :         table_->routing_instance()->GetTable(Address::ERMVPN));
    1066       42757 :     if (ermvpn_table_)
    1067       42757 :         ermvpn_listener_id_ = ermvpn_table_->Register(
    1068             :             boost::bind(&EvpnManager::ErmVpnRouteListener, this, _1, _2),
    1069             :             "EvpnManager");
    1070       42756 : }
    1071             : 
    1072             : //
    1073             : // Terminate the EvpnManager. We free the EvpnManagerPartitions
    1074             : // and unregister from the EvpnTable.
    1075             : //
    1076       42757 : void EvpnManager::Terminate() {
    1077       42757 :     CHECK_CONCURRENCY("bgp::Config");
    1078       42757 :     table_->Unregister(listener_id_);
    1079       42757 :     listener_id_ = DBTable::kInvalidId;
    1080       42757 :     if (ermvpn_listener_id_ != DBTable::kInvalidId) {
    1081       42757 :         ermvpn_table_->Unregister(ermvpn_listener_id_);
    1082       42757 :         ermvpn_listener_id_ = DBTable::kInvalidId;
    1083             :     }
    1084       42757 :     FreePartitions();
    1085       42757 : }
    1086             : 
    1087             : //
    1088             : // Allocate the EvpnManagerPartitions.
    1089             : //
    1090       42754 : void EvpnManager::AllocPartitions() {
    1091      213081 :     for (int part_id = 0; part_id < DB::PartitionCount(); part_id++) {
    1092      170327 :         partitions_.push_back(new EvpnManagerPartition(this, part_id));
    1093             :     }
    1094       42755 : }
    1095             : 
    1096             : //
    1097             : // Free the EvpnManagerPartitions.
    1098             : //
    1099       42757 : void EvpnManager::FreePartitions() {
    1100       42757 :     STLDeleteValues(&partitions_);
    1101       42757 : }
    1102             : 
    1103             : //
    1104             : // Disable processing of the update lists in all partitions.
    1105             : // For testing only.
    1106             : //
    1107           3 : void EvpnManager::DisableMacUpdateProcessing() {
    1108          15 :     for (int part_id = 0; part_id < DB::PartitionCount(); part_id++) {
    1109          12 :         partitions_[part_id]->DisableMacUpdateProcessing();
    1110             :     }
    1111           3 : }
    1112             : 
    1113             : //
    1114             : // Enable processing of the update lists in all partitions.
    1115             : // For testing only.
    1116             : //
    1117           3 : void EvpnManager::EnableMacUpdateProcessing() {
    1118          15 :     for (int part_id = 0; part_id < DB::PartitionCount(); part_id++) {
    1119          12 :         partitions_[part_id]->EnableMacUpdateProcessing();
    1120             :     }
    1121           3 : }
    1122             : 
    1123             : //
    1124             : // Get the EvpnManagerPartition for the given partition id.
    1125             : //
    1126      116465 : EvpnManagerPartition *EvpnManager::GetPartition(size_t part_id) {
    1127      116465 :     return partitions_[part_id];
    1128             : }
    1129             : 
    1130             : //
    1131             : // Get the DBTablePartition for the EvpnTable for given partition id.
    1132             : //
    1133      241511 : DBTablePartition *EvpnManager::GetTablePartition(size_t part_id) {
    1134      241511 :     return static_cast<DBTablePartition *>(table_->GetTablePartition(part_id));
    1135             : }
    1136             : 
    1137             : //
    1138             : // Construct export state for the given EvpnRoute. Note that the route
    1139             : // only needs to be exported to the IPeer from which it was learnt.
    1140             : //
    1141        7113 : UpdateInfo *EvpnManager::GetUpdateInfo(EvpnRoute *route) {
    1142        7113 :     CHECK_CONCURRENCY("db::DBTable");
    1143             : 
    1144        7113 :     DBState *dbstate = route->GetState(table_, listener_id_);
    1145             :     EvpnLocalMcastNode *local_node =
    1146        7113 :         dynamic_cast<EvpnLocalMcastNode *>(dbstate);
    1147             : 
    1148        7113 :     if (!local_node)
    1149           0 :         return NULL;
    1150             : 
    1151        7113 :     return local_node->GetUpdateInfo(route);
    1152             : }
    1153             : 
    1154       87913 : BgpServer *EvpnManager::server() {
    1155       87913 :     return table_->server();
    1156             : }
    1157             : 
    1158             : //
    1159             : // Find or create the EvpnSegment for the given EthernetSegmentId.
    1160             : //
    1161          76 : EvpnSegment *EvpnManager::LocateSegment(const EthernetSegmentId &esi) {
    1162          76 :     assert(!esi.IsZero());
    1163          76 :     tbb::spin_rw_mutex::scoped_lock write_lock(segment_rw_mutex_, true);
    1164          76 :     SegmentMap::iterator loc = segment_map_.find(esi);
    1165          76 :     EvpnSegment *segment = (loc != segment_map_.end()) ? loc->second : NULL;
    1166          76 :     if (!segment) {
    1167          34 :         segment = new EvpnSegment(this, esi);
    1168          34 :         segment_map_.insert(esi, segment);
    1169             :     }
    1170          76 :     return segment;
    1171          76 : }
    1172             : 
    1173             : //
    1174             : // Find the EvpnSegment for the given EthernetSegmentId.
    1175             : //
    1176          77 : EvpnSegment *EvpnManager::FindSegment(const EthernetSegmentId &esi) {
    1177          77 :     assert(!esi.IsZero());
    1178          77 :     tbb::spin_rw_mutex::scoped_lock read_lock(segment_rw_mutex_, false);
    1179          77 :     SegmentMap::iterator loc = segment_map_.find(esi);
    1180         154 :     return (loc != segment_map_.end()) ? loc->second : NULL;
    1181          77 : }
    1182             : 
    1183             : //
    1184             : // Trigger deletion of the given EvpnSegment.
    1185             : // The EvpnSegment is added to a set of EvpnSegments that can potentially
    1186             : // be deleted. This method can be invoked from multiple db::DBTable tasks
    1187             : // in parallel when a MAC routes are removed from the dependency list in an
    1188             : // EvpnSegment. Hence we ensure exclusive access using a write lock.
    1189             : //
    1190             : // The list is processed from the context of bgp::EvpnSegment task which is
    1191             : // mutually exclusive with db::DBTable task.
    1192             : //
    1193          60 : void EvpnManager::TriggerSegmentDelete(EvpnSegment *segment) {
    1194          60 :     CHECK_CONCURRENCY("db::DBTable", "bgp::EvpnSegment");
    1195             : 
    1196          60 :     tbb::spin_rw_mutex::scoped_lock write_lock(segment_rw_mutex_, true);
    1197          60 :     segment_delete_set_.insert(segment);
    1198          60 :     segment_delete_trigger_->Set();
    1199          60 : }
    1200             : 
    1201             : //
    1202             : // Process the set of EvpnSegments that can potentially be deleted.
    1203             : // Remove the EvpnSegment from the map and destroy if it's fine to
    1204             : // to delete the EvpnSegment.
    1205             : //
    1206          48 : bool EvpnManager::ProcessSegmentDeleteSet() {
    1207          48 :     CHECK_CONCURRENCY("bgp::EvpnSegment");
    1208             : 
    1209         146 :     BOOST_FOREACH(EvpnSegment *segment, segment_delete_set_) {
    1210          49 :         if (segment->MayDelete()) {
    1211          34 :             segment_update_set_.erase(segment);
    1212          34 :             EthernetSegmentId esi = segment->esi();
    1213          34 :             segment_map_.erase(esi);
    1214             :         }
    1215             :     }
    1216          48 :     segment_delete_set_.clear();
    1217          48 :     RetryDelete();
    1218          48 :     return true;
    1219             : }
    1220             : 
    1221             : //
    1222             : // Disable processing of the delete list.
    1223             : // For testing only.
    1224             : //
    1225           0 : void EvpnManager::DisableSegmentDeleteProcessing() {
    1226           0 :     segment_delete_trigger_->set_disable();
    1227           0 : }
    1228             : 
    1229             : //
    1230             : // Enable processing of the delete list.
    1231             : // For testing only.
    1232             : //
    1233           0 : void EvpnManager::EnableSegmentDeleteProcessing() {
    1234           0 :     segment_delete_trigger_->set_enable();
    1235           0 : }
    1236             : 
    1237             : //
    1238             : // Trigger update of the given EvpnSegment.
    1239             : // The EvpnSegment is added to a set of EvpnSegments for which updates
    1240             : // need triggered. This method is called in the context of db::DBTable
    1241             : // task and a task instance of 0 since all AutoDisocvery routes always
    1242             : // get sharded to partition 0.
    1243             : //
    1244             : // The set is processed in the context of bgp::EvpnSegment task, which
    1245             : // is mutually exclusive with db::DBTable task.
    1246             : //
    1247          56 : void EvpnManager::TriggerSegmentUpdate(EvpnSegment *segment) {
    1248          56 :     CHECK_CONCURRENCY("db::DBTable");
    1249             : 
    1250          56 :     segment_update_set_.insert(segment);
    1251          56 :     segment_update_trigger_->Set();
    1252          56 : }
    1253             : 
    1254             : //
    1255             : // Process the set of EvpnSegments that need to be updated.
    1256             : //
    1257             : // Go through each EvpnSegment and update it's PE list. Trigger updates
    1258             : // of all it's dependent MAC routes if there's a change in the PE list.
    1259             : //
    1260          56 : bool EvpnManager::ProcessSegmentUpdateSet() {
    1261          56 :     CHECK_CONCURRENCY("bgp::EvpnSegment");
    1262             : 
    1263         168 :     BOOST_FOREACH(EvpnSegment *segment, segment_update_set_) {
    1264          56 :         EvpnRoute *esi_ad_route = segment->esi_ad_route();
    1265          56 :         bool changed = segment->UpdatePeList();
    1266          56 :         if (changed)
    1267          54 :             segment->TriggerMacRouteUpdate();
    1268          56 :         if (!esi_ad_route->IsValid()) {
    1269          22 :             segment->clear_esi_ad_route();
    1270          22 :             esi_ad_route->ClearState(table_, listener_id_);
    1271          22 :             TriggerSegmentDelete(segment);
    1272             :         }
    1273             :     }
    1274          56 :     segment_update_set_.clear();
    1275          56 :     RetryDelete();
    1276          56 :     return true;
    1277             : }
    1278             : 
    1279             : //
    1280             : // Disable processing of the update list.
    1281             : // For testing only.
    1282             : //
    1283           2 : void EvpnManager::DisableSegmentUpdateProcessing() {
    1284           2 :     segment_update_trigger_->set_disable();
    1285           2 : }
    1286             : 
    1287             : //
    1288             : // Enable processing of the update list.
    1289             : // For testing only.
    1290             : //
    1291           2 : void EvpnManager::EnableSegmentUpdateProcessing() {
    1292           2 :     segment_update_trigger_->set_enable();
    1293           2 : }
    1294             : 
    1295             : //
    1296             : // DBListener callback handler for AutoDisocvery routes in the EvpnTable.
    1297             : //
    1298          56 : void EvpnManager::AutoDiscoveryRouteListener(EvpnRoute *route) {
    1299          56 :     CHECK_CONCURRENCY("db::DBTable");
    1300             : 
    1301          56 :     DBState *dbstate = route->GetState(table_, listener_id_);
    1302          56 :     if (!dbstate) {
    1303             :         // We have no previous DBState for this route.
    1304             :         // Bail if the route is not valid.
    1305          22 :         if (!route->IsValid())
    1306           0 :             return;
    1307             : 
    1308             :         // Locate the EvpnSegment and associate it with the route.
    1309             :         // Trigger an update of EvpnSegment so that it's PE list
    1310             :         // gets updated.
    1311          22 :         EvpnSegment *segment = LocateSegment(route->GetPrefix().esi());
    1312          22 :         segment->set_esi_ad_route(route);
    1313          22 :         route->SetState(table_, listener_id_, segment);
    1314          22 :         TriggerSegmentUpdate(segment);
    1315             :     } else {
    1316             :         // Trigger an update of EvpnSegment so that it's PE list
    1317             :         // gets updated.
    1318          34 :         EvpnSegment *segment = dynamic_cast<EvpnSegment *>(dbstate);
    1319          34 :         assert(segment);
    1320          34 :         TriggerSegmentUpdate(segment);
    1321             :     }
    1322             : }
    1323             : 
    1324             : //
    1325             : // DBListener callback handler for MacAdvertisement routes in the EvpnTable.
    1326             : //
    1327        9318 : void EvpnManager::MacAdvertisementRouteListener(
    1328             :     EvpnManagerPartition *partition, EvpnRoute *route) {
    1329        9318 :     CHECK_CONCURRENCY("db::DBTable");
    1330             : 
    1331        9318 :     DBState *dbstate = route->GetState(table_, listener_id_);
    1332        9318 :     if (!dbstate) {
    1333             :         // We have no previous DBState for this route.
    1334             :         // Bail if the route is not valid or if it doesn't have an ESI.
    1335        9229 :         if (!route->IsValid())
    1336        3204 :             return;
    1337        6025 :         const BgpPath *path = route->BestPath();
    1338        6025 :         if (path->GetAttr()->esi().IsZero())
    1339        5973 :             return;
    1340             : 
    1341             :         // Create a new EvpnMacState and associate it with the route.
    1342          52 :         EvpnMacState *mac_state = new EvpnMacState(this, route);
    1343          52 :         route->SetState(table_, listener_id_, mac_state);
    1344             : 
    1345             :         // Locate the EvpnSegment and add the MAC route as a dependent
    1346             :         // of the EvpnSegment.
    1347          52 :         EvpnSegment *segment = LocateSegment(path->GetAttr()->esi());
    1348          52 :         mac_state->set_segment(segment);
    1349          52 :         segment->AddMacRoute(partition->part_id(), route);
    1350          52 :         partition->TriggerMacRouteUpdate(route);
    1351             :     } else {
    1352          89 :         EvpnMacState *mac_state = dynamic_cast<EvpnMacState *>(dbstate);
    1353          89 :         assert(mac_state);
    1354          89 :         EvpnSegment *segment = mac_state->segment();
    1355             : 
    1356             :         // Handle change in the ESI and update the dependency on the
    1357             :         // EvpnSegment as appropriate.
    1358             :         // Note that the DBState on the EvpnRoute doesn't get cleared
    1359             :         // here. That only happens when aliasing for the route is being
    1360             :         // processed.
    1361          89 :         const BgpPath *path = route->BestPath();
    1362          89 :         if (!route->IsValid() || (path && path->GetAttr()->esi().IsZero())) {
    1363          54 :             if (segment) {
    1364          53 :                 segment->DeleteMacRoute(partition->part_id(), route);
    1365          53 :                 mac_state->clear_segment();
    1366             :             }
    1367          35 :         } else if (!segment && !path->GetAttr()->esi().IsZero()) {
    1368           1 :             segment = LocateSegment(path->GetAttr()->esi());
    1369           1 :             mac_state->set_segment(segment);
    1370           1 :             segment->AddMacRoute(partition->part_id(), route);
    1371          34 :         } else if (segment && segment->esi() != path->GetAttr()->esi()) {
    1372           1 :             segment->DeleteMacRoute(partition->part_id(), route);
    1373           1 :             mac_state->clear_segment();
    1374           1 :             segment = LocateSegment(path->GetAttr()->esi());
    1375           1 :             mac_state->set_segment(segment);
    1376           1 :             segment->AddMacRoute(partition->part_id(), route);
    1377             :         }
    1378          89 :         partition->TriggerMacRouteUpdate(route);
    1379             :     }
    1380             : }
    1381             : 
    1382             : //
    1383             : // DBListener callback handler for InclusiveMulticast routes in the EvpnTable.
    1384             : //
    1385       45923 : void EvpnManager::InclusiveMulticastRouteListener(
    1386             :     EvpnManagerPartition *partition, EvpnRoute *route) {
    1387       45923 :     CHECK_CONCURRENCY("db::DBTable");
    1388             : 
    1389       45923 :     DBState *dbstate = route->GetState(table_, listener_id_);
    1390       45923 :     if (!dbstate) {
    1391             :         // We have no previous DBState for this route.
    1392             :         // Bail if the route is not valid.
    1393        5099 :         if (!route->IsValid())
    1394           0 :             return;
    1395             : 
    1396             :         // Create a new EvpnMcastNode and associate it with the route.
    1397             :         EvpnMcastNode *node;
    1398        5099 :         if (route->GetPrefix().type() == EvpnPrefix::MacAdvertisementRoute) {
    1399        2150 :             node = new EvpnLocalMcastNode(partition, route);
    1400             :         } else {
    1401        2949 :             node = new EvpnRemoteMcastNode(partition, route);
    1402             :         }
    1403        5099 :         partition->AddMcastNode(node, route);
    1404        5099 :         route->SetState(table_, listener_id_, node);
    1405             :     } else {
    1406       40824 :         EvpnMcastNode *node = dynamic_cast<EvpnMcastNode *>(dbstate);
    1407       40824 :         assert(node);
    1408             : 
    1409       40824 :         if (!route->IsValid()) {
    1410             :             // Delete the EvpnMcastNode associated with the route.
    1411        5099 :             route->ClearState(table_, listener_id_);
    1412        5099 :             partition->DeleteMcastNode(node, route);
    1413        5099 :             delete node;
    1414       35725 :         } else if (node->UpdateAttributes(route)) {
    1415             :             // Update the EvpnMcastNode associated with the route.
    1416       18516 :             partition->UpdateMcastNode(node, route);
    1417             :         }
    1418             :     }
    1419             : }
    1420             : 
    1421             : //
    1422             : // DBListener callback handler for SelectiveMulticast routes in the EvpnTable.
    1423             : //
    1424        2391 : void EvpnManager::SelectiveMulticastRouteListener(
    1425             :     EvpnManagerPartition *partition, EvpnRoute *route) {
    1426             : 
    1427        2391 :     CHECK_CONCURRENCY("db::DBTable");
    1428        2391 :     EvpnMcastNode *dbstate = dynamic_cast<EvpnMcastNode *>(
    1429        2391 :             route->GetState(table_, listener_id_));
    1430        2391 :     bool is_usable = route->IsUsable();
    1431        2391 :     bool is_deleted = route->IsDeleted();
    1432        2391 :     bool checkErmvpnRoute = false;
    1433        2391 :     if (route->BestPath()) {
    1434        1749 :         checkErmvpnRoute = route->BestPath()->GetFlags() &
    1435             :             BgpPath::CheckGlobalErmVpnRoute;
    1436             :     }
    1437        2391 :     if ((!is_usable && !checkErmvpnRoute) || is_deleted) {
    1438         642 :         if (!dbstate)
    1439           0 :             return;
    1440             : 
    1441         642 :         EvpnStatePtr evpn_state = partition->GetState(route);
    1442         642 :         if (evpn_state)
    1443         642 :             evpn_state->smet_routes().erase(route);
    1444         642 :         EVPN_RT_LOG(route, "Processed Smet route deletion");
    1445         642 :         ClearDBState(route);
    1446         642 :         partition->NotifyForestNode(route, ermvpn_table());
    1447         642 :         partition->DeleteMcastNode(dbstate, route);
    1448         642 :         delete dbstate;
    1449         642 :         return;
    1450         642 :     }
    1451             : 
    1452        1749 :     EvpnStatePtr evpn_state = partition->LocateState(route);
    1453        1749 :     assert(evpn_state);
    1454        1749 :     evpn_state->smet_routes().insert(route);
    1455        1749 :     BgpPath *path = const_cast<BgpPath *>(route->BestPath());
    1456        1749 :     if (path && path->CheckErmVpn()) {
    1457        1409 :         ErmVpnRoute *global_rt = evpn_state->global_ermvpn_tree_rt();
    1458        1409 :         Ip4Address address;
    1459        1409 :         bool nh_found = partition->GetForestNodeAddress(global_rt, &address);
    1460        1409 :         if (nh_found) {
    1461         626 :             BgpAttrPtr attr = path->GetAttr();
    1462             :             BgpAttrPtr new_attr = partition->server()->attr_db()->
    1463         626 :                                   ReplaceNexthopAndLocate(attr.get(), address);
    1464         626 :             path->SetAttr(new_attr, attr);
    1465         626 :             path->ResetCheckErmVpn();
    1466         626 :             partition->NotifyForestNode(route, ermvpn_table());
    1467         626 :         }
    1468             :     }
    1469             : 
    1470        1749 :     if (!dbstate) {
    1471             :         // Create a new DBState and associate it with the route.
    1472             :         EvpnMcastNode *node;
    1473         642 :         if (route->GetPrefix().ip_address() ==
    1474        1284 :                 Ip4Address(table_->server()->bgp_identifier())) {
    1475         618 :             node = new EvpnLocalMcastNode(partition, route, evpn_state);
    1476             :         } else {
    1477          24 :             node = new EvpnRemoteMcastNode(partition, route, evpn_state);
    1478             :         }
    1479         642 :         SetDBState(route, node);
    1480         642 :         partition->AddMcastNode(node, route);
    1481         642 :         EVPN_RT_LOG(route, "Processed Smet route creation");
    1482             :     }
    1483        1749 : }
    1484             : 
    1485        1268 : void EvpnManagerPartition::NotifyForestNode(EvpnRoute *route,
    1486             :                                             ErmVpnTable *table) {
    1487        1268 :     const Ip4Address &source = route->GetPrefix().source().to_v4();
    1488        1268 :     const Ip4Address &group = route->GetPrefix().group().to_v4();
    1489        1268 :     if (table->tree_manager())
    1490        1268 :         table->tree_manager()->NotifyForestNode(part_id_, source, group);
    1491        1268 : }
    1492             : 
    1493             : // Set DB State and update count.
    1494         642 : void EvpnManager::SetDBState(EvpnRoute *route, EvpnMcastNode *dbstate) {
    1495         642 :     route->SetState(table_, listener_id_, dbstate);
    1496         642 :     db_states_count_++;
    1497         642 : }
    1498             : 
    1499             : // Create DB State and update count. If there is no DB State associated in the
    1500             : // table, resume table deletion if the deletion was pending.
    1501         642 : void EvpnManager::ClearDBState(EvpnRoute *route) {
    1502         642 :     route->ClearState(table_, listener_id_);
    1503         642 :     assert(db_states_count_);
    1504         642 :     db_states_count_--;
    1505             : 
    1506             :     // Retry deletion now as there is no more attached db state in the table.
    1507         642 :     if (!db_states_count_ && deleter_->IsDeleted())
    1508           0 :         deleter_->RetryDelete();
    1509         642 : }
    1510             : 
    1511             : // Check whether an ErmVpnRoute is locally originated GlobalTreeRoute.
    1512        9972 : bool EvpnManager::IsUsableGlobalTreeRootRoute(
    1513             :         ErmVpnRoute *ermvpn_route) const {
    1514        9972 :     if (!ermvpn_route || !ermvpn_route->IsUsable())
    1515        3495 :         return false;
    1516        6477 :     if (!ermvpn_table()->tree_manager())
    1517           0 :         return false;
    1518        6477 :     if (ermvpn_table()->tree_manager()->begin() ==
    1519       12954 :             ermvpn_table()->tree_manager()->end())
    1520         321 :         return false;
    1521        6156 :     ErmVpnRoute *global_rt = ermvpn_table()->tree_manager()->
    1522        6156 :                                GetGlobalTreeRootRoute(
    1523        6156 :                                ermvpn_route->GetPrefix().source(),
    1524        6156 :                                ermvpn_route->GetPrefix().group());
    1525        6156 :     return (global_rt && global_rt == ermvpn_route);
    1526             : }
    1527             : 
    1528             : // ErmVpnTable route listener callback function.
    1529             : //
    1530             : // Process changes (create/update/delete) to GlobalErmVpnRoute in vrf.ermvpn.0
    1531       22893 : void EvpnManager::ErmVpnRouteListener(DBTablePartBase *tpart,
    1532             :                                       DBEntryBase *db_entry) {
    1533       22893 :     CHECK_CONCURRENCY("db::DBTable");
    1534             : 
    1535       22893 :     ErmVpnRoute *ermvpn_route = dynamic_cast<ErmVpnRoute *>(db_entry);
    1536       22893 :     assert(ermvpn_route);
    1537             : 
    1538             :     // We only care about global tree routes for evpn stitching.
    1539       22893 :     if (ermvpn_route->GetPrefix().type() != ErmVpnPrefix::GlobalTreeRoute) {
    1540       18855 :         return;
    1541             :     }
    1542             : 
    1543        9972 :     EvpnMcastNode *dbstate = dynamic_cast<EvpnMcastNode *>(
    1544        9972 :         ermvpn_route->GetState(ermvpn_table(), ermvpn_listener_id()));
    1545             : 
    1546             :     // Handle GlobalTreeRoute route deletion.
    1547        9972 :     if (!IsUsableGlobalTreeRootRoute(ermvpn_route)) {
    1548             :         // Ignore if there is no DB State associated with route.
    1549        5934 :         if (!dbstate)
    1550        3256 :             return;
    1551        2678 :         EvpnStatePtr evpn_state = dbstate->state();
    1552        2678 :         evpn_state->set_global_ermvpn_tree_rt(NULL);
    1553             : 
    1554             :         // Notify all received smet routes for PMSI re-computation.
    1555             :         // Since usable global ermvpn is no longer available, any advertised
    1556             :         // smet routes must now be withdrawn.
    1557        3026 :         BOOST_FOREACH(EvpnRoute *route, evpn_state->smet_routes()) {
    1558         174 :             BgpPath *path = const_cast<BgpPath *>(route->BestPath());
    1559         174 :             if (path && (path->GetPathId() == 0)) {
    1560         174 :                 path->SetCheckErmVpn();
    1561             :             }
    1562         174 :             route->Notify();
    1563             :         }
    1564        2678 :         ermvpn_route->ClearState(ermvpn_table(), ermvpn_listener_id());
    1565        2678 :         EVPN_ERMVPN_RT_LOG(ermvpn_route,
    1566             :                            "Processed EVPN GlobalErmVpnRoute deletion");
    1567        2678 :         delete dbstate;
    1568        2678 :         return;
    1569        2678 :     }
    1570             : 
    1571             :     // Set DB State in the route if not already done so before.
    1572        4038 :     EvpnManagerPartition *partition = GetPartition(tpart->index());
    1573        4038 :     EvpnStatePtr evpn_state;
    1574        4038 :     if (!dbstate) {
    1575        2678 :         EvpnState::SG sg(ermvpn_route);
    1576        2678 :         evpn_state = partition->LocateState(sg);
    1577        2678 :         dbstate = new EvpnLocalMcastNode(partition, NULL, evpn_state);
    1578        2678 :         ermvpn_route->SetState(ermvpn_table(), ermvpn_listener_id(), dbstate);
    1579             :     } else {
    1580        1360 :         evpn_state = dbstate->state();
    1581             :     }
    1582             : 
    1583             :     // Note down current usable ermvpn route for stitching to evpn.
    1584        4038 :     dbstate->state()->set_global_ermvpn_tree_rt(ermvpn_route);
    1585             : 
    1586             :     // Notify all originated Type6 routes.
    1587        5900 :     BOOST_FOREACH(EvpnRoute *route, evpn_state->smet_routes()) {
    1588         931 :         route->Notify();
    1589             :     }
    1590        4038 :     EVPN_ERMVPN_RT_LOG(ermvpn_route,
    1591             :                        "Processed EVPN GlobalErmVpnRoute creation");
    1592        4038 : }
    1593             : 
    1594             : //
    1595             : // DBListener callback handler for the EvpnTable.
    1596             : //
    1597      112211 : void EvpnManager::RouteListener(DBTablePartBase *tpart, DBEntryBase *db_entry) {
    1598      112211 :     CHECK_CONCURRENCY("db::DBTable");
    1599             : 
    1600      112212 :     EvpnManagerPartition *partition = GetPartition(tpart->index());
    1601      112213 :     EvpnRoute *route = dynamic_cast<EvpnRoute *>(db_entry);
    1602      112213 :     assert(route);
    1603             : 
    1604      112213 :     switch (route->GetPrefix().type()) {
    1605          56 :     case EvpnPrefix::AutoDiscoveryRoute:
    1606          56 :         AutoDiscoveryRouteListener(route);
    1607          56 :         break;
    1608       31358 :     case EvpnPrefix::MacAdvertisementRoute:
    1609       31358 :         if (route->GetPrefix().mac_addr().IsBroadcast()) {
    1610       22040 :             InclusiveMulticastRouteListener(partition, route);
    1611             :         } else {
    1612        9318 :             MacAdvertisementRouteListener(partition, route);
    1613             :         }
    1614       31358 :         break;
    1615       23883 :     case EvpnPrefix::InclusiveMulticastRoute:
    1616       23883 :         InclusiveMulticastRouteListener(partition, route);
    1617       23883 :         break;
    1618        2391 :     case EvpnPrefix::SelectiveMulticastRoute:
    1619        2391 :         SelectiveMulticastRouteListener(partition, route);
    1620        2391 :         break;
    1621       54518 :     default:
    1622       54518 :         break;
    1623             :     }
    1624      112206 : }
    1625             : 
    1626             : //
    1627             : // Fill information for introspect command.
    1628             : // Note that all IM routes are always in partition 0.
    1629             : //
    1630         190 : void EvpnManager::FillShowInfo(ShowEvpnTable *sevt) const {
    1631         190 :     CHECK_CONCURRENCY("db::DBTable");
    1632             : 
    1633         190 :     vector<string> regular_nves;
    1634         190 :     vector<string> ar_replicators;
    1635         190 :     vector<ShowEvpnMcastLeaf> ar_leafs;
    1636             :     EvpnManagerPartition::EvpnMcastNodeList::const_iterator it =
    1637         190 :                       partitions_[0]->remote_mcast_node_list()->begin();
    1638         190 :     for (; it != partitions_[0]->remote_mcast_node_list()->end(); it++) {
    1639           0 :         BOOST_FOREACH(const EvpnMcastNode *node, it->second) {
    1640           0 :             if (node->assisted_replication_leaf()) {
    1641           0 :                 ShowEvpnMcastLeaf leaf;
    1642           0 :                 leaf.set_address(node->address().to_string());
    1643           0 :                 leaf.set_replicator(node->replicator_address().to_string());
    1644           0 :                 ar_leafs.push_back(leaf);
    1645           0 :             } else if (node->assisted_replication_supported()) {
    1646           0 :                 ar_replicators.push_back(node->address().to_string());
    1647           0 :             } else if (node->edge_replication_not_supported()) {
    1648           0 :                 regular_nves.push_back(node->address().to_string());
    1649             :             }
    1650             :         }
    1651             :     }
    1652             : 
    1653         190 :     sort(regular_nves.begin(), regular_nves.end());
    1654         190 :     sort(ar_replicators.begin(), ar_replicators.end());
    1655         190 :     sort(ar_leafs.begin(), ar_leafs.end());
    1656         190 :     sevt->set_regular_nves(regular_nves);
    1657         190 :     sevt->set_ar_replicators(ar_replicators);
    1658         190 :     sevt->set_ar_leafs(ar_leafs);
    1659         190 : }
    1660             : 
    1661             : //
    1662             : // Check if the EvpnManager can be deleted. This can happen only if all the
    1663             : // EvpnManagerPartitions are empty.
    1664             : //
    1665       42910 : bool EvpnManager::MayDelete() const {
    1666       42910 :     CHECK_CONCURRENCY("bgp::Config");
    1667             : 
    1668       42910 :     if (!segment_map_.empty())
    1669           0 :         return false;
    1670             : 
    1671       42910 :     if (!segment_update_set_.empty())
    1672           0 :         return false;
    1673       42910 :     if (segment_update_trigger_->IsSet())
    1674           0 :         return false;
    1675             : 
    1676       42910 :     if (!segment_delete_set_.empty())
    1677           0 :         return false;
    1678       42910 :     if (segment_delete_trigger_->IsSet())
    1679           0 :         return false;
    1680             : 
    1681      383592 :     BOOST_FOREACH(const EvpnManagerPartition *partition, partitions_) {
    1682      170342 :         if (!partition->empty())
    1683           1 :             return false;
    1684      170342 :         if (!partition->states().empty())
    1685           1 :             return false;
    1686             :     }
    1687       42909 :     if (db_states_count_)
    1688           0 :         return false;
    1689       42909 :     return true;
    1690             : }
    1691             : 
    1692             : //
    1693             : // Initiate shutdown for the EvpnManager.
    1694             : //
    1695       42909 : void EvpnManager::Shutdown() {
    1696       42909 :     CHECK_CONCURRENCY("bgp::Config");
    1697       42909 : }
    1698             : 
    1699             : //
    1700             : // Trigger deletion of the EvpnManager and propagate the delete to any
    1701             : // dependents.
    1702             : //
    1703       42909 : void EvpnManager::ManagedDelete() {
    1704       42909 :     deleter_->Delete();
    1705       42909 : }
    1706             : 
    1707             : //
    1708             : // Attempt to enqueue a delete for the EvpnManager.
    1709             : //
    1710         748 : void EvpnManager::RetryDelete() {
    1711         748 :     if (!deleter()->IsDeleted())
    1712         748 :         return;
    1713           0 :     deleter()->RetryDelete();
    1714             : }
    1715             : 
    1716             : //
    1717             : // Return the LifetimeActor for the EvpnManager.
    1718             : //
    1719        5684 : LifetimeActor *EvpnManager::deleter() {
    1720        5684 :     return deleter_.get();
    1721             : }
    1722             : 
    1723        1469 : EvpnState::SG::SG(const Ip4Address &source, const Ip4Address &group) :
    1724        1469 :     source(IpAddress(source)), group(IpAddress(group)) {
    1725        1469 : }
    1726             : 
    1727        2678 : EvpnState::SG::SG(const ErmVpnRoute *route) :
    1728        2678 :         source(route->GetPrefix().source()),
    1729        2678 :         group(route->GetPrefix().group()) {
    1730        2678 : }
    1731             : 
    1732           0 : EvpnState::SG::SG(const EvpnRoute *route) :
    1733           0 :         source(route->GetPrefix().source()), group(route->GetPrefix().group()) {
    1734           0 : }
    1735             : 
    1736       41248 : EvpnState::SG::SG(const IpAddress &source, const IpAddress &group) :
    1737       41248 :     source(source), group(group) {
    1738       41248 : }
    1739             : 
    1740      306327 : bool EvpnState::SG::operator<(const SG &other) const {
    1741      306327 :     if (source < other.source)
    1742          35 :         return true;
    1743      306292 :     if (source > other.source)
    1744          28 :         return false;
    1745      306264 :     if (group < other.group)
    1746        9279 :         return true;
    1747      296985 :     if (group > other.group)
    1748        8753 :         return false;
    1749      288232 :     return false;
    1750             : }
    1751             : 
    1752        2686 : const EvpnState::SG &EvpnState::sg() const {
    1753        2686 :     return sg_;
    1754             : }
    1755             : 
    1756        6716 : void EvpnState::set_global_ermvpn_tree_rt(ErmVpnRoute *global_ermvpn_tree_rt) {
    1757        6716 :     global_ermvpn_tree_rt_ = global_ermvpn_tree_rt;
    1758        6716 : }
    1759             : 
    1760        1409 : ErmVpnRoute *EvpnState::global_ermvpn_tree_rt() {
    1761        1409 :     return global_ermvpn_tree_rt_;
    1762             : }
    1763             : 
    1764           0 : const ErmVpnRoute *EvpnState::global_ermvpn_tree_rt() const {
    1765           0 :     return global_ermvpn_tree_rt_;
    1766             : }

Generated by: LCOV version 1.14