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

Generated by: LCOV version 1.14