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-18 01:51:13 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       15096 : const ErmVpnTable *EvpnState::table() const {
      46       15096 :     return manager_ ? manager_->ermvpn_table() : NULL;
      47             : }
      48             : 
      49             : class EvpnManager::DeleteActor : public LifetimeActor {
      50             : public:
      51       42906 :     explicit DeleteActor(EvpnManager *evpn_manager)
      52       42906 :         : LifetimeActor(evpn_manager->server()->lifetime_manager()),
      53       42906 :           evpn_manager_(evpn_manager) {
      54       42907 :     }
      55       85814 :     virtual ~DeleteActor() {
      56       85814 :     }
      57             : 
      58       42907 :     virtual bool MayDelete() const {
      59       42907 :         CHECK_CONCURRENCY("bgp::Config");
      60       42907 :         return evpn_manager_->MayDelete();
      61             :     }
      62             : 
      63       42907 :     virtual void Shutdown() {
      64       42907 :         evpn_manager_->Shutdown();
      65       42907 :     }
      66             : 
      67       42907 :     virtual void Destroy() {
      68       42907 :         evpn_manager_->table_->DestroyEvpnManager();
      69       42907 :     }
      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       41585 : bool EvpnMcastNode::UpdateAttributes(EvpnRoute *route) {
     118       41585 :     bool changed = false;
     119       41585 :     const BgpPath *path = route->BestPath();
     120       41585 :     if (path->GetLabel() != label_) {
     121        6533 :         label_ = path->GetLabel();
     122        6533 :         changed = true;
     123             :     }
     124       41585 :     if (path->GetAttr() != attr_) {
     125       24316 :         attr_ = path->GetAttr();
     126       24316 :         changed = true;
     127             :     }
     128             : 
     129       41585 :     const PmsiTunnel *pmsi_tunnel = attr_->pmsi_tunnel();
     130       41585 :     uint8_t ar_type = 0;
     131       41585 :     bool edge_replication_not_supported = false;
     132       41585 :     if (pmsi_tunnel) {
     133       40943 :         ar_type = pmsi_tunnel->tunnel_flags() &
     134             :                   PmsiTunnelSpec::AssistedReplicationType;
     135       40943 :         if ((pmsi_tunnel->tunnel_flags() &
     136       40943 :              PmsiTunnelSpec::EdgeReplicationSupported) == 0) {
     137        6311 :             edge_replication_not_supported = true;
     138             :         }
     139       40943 :         if (replicator_address_ != pmsi_tunnel->identifier()) {
     140        5099 :             replicator_address_ = pmsi_tunnel->identifier();
     141        5099 :             changed = true;
     142             :         }
     143             :     }
     144             : 
     145       41585 :     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       41585 :     bool assisted_replication_supported = false;
     151       41585 :     if (ar_type == PmsiTunnelSpec::ARReplicator)
     152        3520 :         assisted_replication_supported = true;
     153       41585 :     if (assisted_replication_supported != assisted_replication_supported_) {
     154         184 :         assisted_replication_supported_ = assisted_replication_supported;
     155         184 :         changed = true;
     156             :     }
     157             : 
     158       41585 :     bool assisted_replication_leaf = false;
     159       41585 :     if (ar_type == PmsiTunnelSpec::ARLeaf)
     160         965 :         assisted_replication_leaf = true;
     161       41585 :     if (assisted_replication_leaf != assisted_replication_leaf_) {
     162         531 :         assisted_replication_leaf_ = assisted_replication_leaf;
     163         531 :         changed = true;
     164             :     }
     165             : 
     166       41585 :     if (address_ != path->GetAttr()->nexthop().to_v4()) {
     167        5139 :         address_ = path->GetAttr()->nexthop().to_v4();
     168        5139 :         changed = true;
     169             :     }
     170             : 
     171       41585 :     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       20567 : void EvpnLocalMcastNode::AddInclusiveMulticastRoute() {
     215             :     // No need to create IMET route if group is specified
     216       20567 :     if (!route_->GetPrefix().group().is_unspecified())
     217         618 :         return;
     218       19949 :     assert(!inclusive_mcast_route_);
     219       19949 :     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       19949 :     const EvpnPrefix &mac_prefix = route_->GetPrefix();
     227             :     RouteDistinguisher rd(
     228       19949 :         address_.to_ulong(), mac_prefix.route_distinguisher().GetVrfId());
     229       19949 :     EvpnPrefix prefix(rd, mac_prefix.tag(), address_);
     230       19949 :     EvpnRoute rt_key(prefix);
     231             : 
     232             :     // Find or create the route.
     233       19949 :     DBTablePartition *tbl_partition = partition_->GetTablePartition();
     234       19949 :     EvpnRoute *route = static_cast<EvpnRoute *>(tbl_partition->Find(&rt_key));
     235       19949 :     if (!route) {
     236        2149 :         route = new EvpnRoute(prefix);
     237        2149 :         tbl_partition->Add(route);
     238             :     } else {
     239       17800 :         route->ClearDelete();
     240             :     }
     241             :     // Add MulticastFlags community to specify that it supports SMET route
     242       19949 :     ExtCommunity::ExtCommunityList mcastFlags;
     243       19949 :     mcastFlags.push_back(MulticastFlags().GetExtCommunity());
     244       19949 :     ExtCommunityPtr ext_community = partition_->server()->extcomm_db()->
     245       19949 :             ReplaceMFlagsAndLocate(attr_->ext_community(), mcastFlags);
     246       39898 :     attr_ = partition_->server()->attr_db()->ReplaceExtCommunityAndLocate(
     247       19949 :         attr_.get(), ext_community);
     248             : 
     249             :     // Add a path with source BgpPath::Local and the peer address as path_id.
     250       19949 :     uint32_t path_id = mac_prefix.route_distinguisher().GetAddress();
     251       19949 :     BgpPath *path = new BgpPath(path_id, BgpPath::Local, attr_, 0, label_);
     252       19949 :     route->InsertPath(path);
     253       19949 :     inclusive_mcast_route_ = route;
     254       19949 :     tbl_partition->Notify(inclusive_mcast_route_);
     255       19949 :     BGP_LOG_ROUTE(partition_->table(), static_cast<IPeer *>(NULL),
     256             :         route, "Insert new Local path");
     257       19949 : }
     258             : 
     259             : //
     260             : // Delete Inclusive Multicast route for this EvpnLocalMcastNode.
     261             : //
     262       23263 : void EvpnLocalMcastNode::DeleteInclusiveMulticastRoute() {
     263       23263 :     if (!inclusive_mcast_route_)
     264        3314 :         return;
     265             : 
     266       19949 :     const EvpnPrefix &mac_prefix = route_->GetPrefix();
     267       19949 :     uint32_t path_id = mac_prefix.route_distinguisher().GetAddress();
     268       19949 :     DBTablePartition *tbl_partition = partition_->GetTablePartition();
     269       19949 :     inclusive_mcast_route_->RemovePath(BgpPath::Local, path_id);
     270       19949 :     BGP_LOG_ROUTE(partition_->table(), static_cast<IPeer *>(NULL),
     271             :         inclusive_mcast_route_, "Delete Local path");
     272             : 
     273       19949 :     if (!inclusive_mcast_route_->HasPaths()) {
     274       19948 :         tbl_partition->Delete(inclusive_mcast_route_);
     275             :     } else {
     276           1 :         tbl_partition->Notify(inclusive_mcast_route_);
     277             :     }
     278       19949 :     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       17799 : void EvpnLocalMcastNode::TriggerUpdate() {
     289       17799 :     DeleteInclusiveMulticastRoute();
     290       17799 :     AddInclusiveMulticastRoute();
     291       17799 : }
     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        7132 : UpdateInfo *EvpnLocalMcastNode::GetUpdateInfo(EvpnRoute *route) {
     303        7132 :     CHECK_CONCURRENCY("db::DBTable");
     304             : 
     305             :     // Nothing to send for a leaf as it already knows the replicator-address.
     306        7132 :     if (assisted_replication_leaf_)
     307          18 :         return NULL;
     308             : 
     309        7114 :     const RoutingInstance *rti = partition_->table()->routing_instance();
     310        7114 :     bool pbb_evpn_enable = rti->virtual_network_pbb_evpn_enable();
     311        7114 :     uint32_t local_ethernet_tag = route_->GetPrefix().tag();
     312             : 
     313        7114 :     EvpnState::SG sg = EvpnState::SG(route->GetPrefix().source(),
     314       14228 :                                      route->GetPrefix().group());
     315             :     // Go through list of EvpnRemoteMcastNodes and build the BgpOList.
     316        7114 :     BgpOListSpec olist_spec(BgpAttribute::OList);
     317        7114 :     if (partition_->remote_mcast_node_list()->count(sg)) {
     318        6947 :         set<EvpnMcastNode*> nodes = (*partition_->remote_mcast_node_list())[sg];
     319      209603 :         BOOST_FOREACH(EvpnMcastNode *node, nodes) {
     320      101328 :             if (node->address() == address_)
     321       83820 :                 continue;
     322       94511 :             if (node->assisted_replication_leaf())
     323        1648 :                 continue;
     324      180046 :             if (!edge_replication_not_supported_ &&
     325       87183 :                 !node->edge_replication_not_supported())
     326       75099 :                 continue;
     327       17764 :             uint32_t remote_ethernet_tag = node->route()->GetPrefix().tag();
     328             : 
     329       17764 :             if (pbb_evpn_enable && remote_ethernet_tag &&
     330             :                 (local_ethernet_tag != remote_ethernet_tag))
     331         256 :                 continue;
     332             : 
     333       17508 :             const ExtCommunity *extcomm = node->attr()->ext_community();
     334       35016 :             BgpOListElem elem(node->address(), node->label(),
     335       35016 :                     extcomm ? extcomm->GetTunnelEncap() : vector<string>());
     336       17508 :             olist_spec.elements.push_back(elem);
     337       17508 :         }
     338        6947 :     }
     339             : 
     340             :     // Go through list of leaf EvpnMcastNodes and build the leaf BgpOList.
     341        7114 :     BgpOListSpec leaf_olist_spec(BgpAttribute::LeafOList);
     342        7114 :     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        7114 :     if (olist_spec.elements.empty() && leaf_olist_spec.elements.empty())
     359        2496 :         return NULL;
     360             : 
     361             :     // Add BgpOList and leaf BgpOList to RibOutAttr for broadcast MAC route.
     362        4618 :     BgpAttrDB *attr_db = partition_->server()->attr_db();
     363        4618 :     BgpAttrPtr attr = attr_db->ReplaceOListAndLocate(attr_.get(), &olist_spec);
     364        4618 :     attr = attr_db->ReplaceLeafOListAndLocate(attr.get(), &leaf_olist_spec);
     365             : 
     366        4618 :     UpdateInfo *uinfo = new UpdateInfo;
     367             :     uinfo->roattr =
     368        4618 :         RibOutAttr(partition_->table(), route_, attr.get(), 0, false, true);
     369        4618 :     return uinfo;
     370        7114 : }
     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          35 : EvpnSegment::EvpnSegment(EvpnManager *manager, const EthernetSegmentId &esi)
     401          35 :   : evpn_manager_(manager),
     402          35 :     esi_(esi),
     403          35 :     esi_ad_route_(NULL),
     404          35 :     single_active_(true),
     405          35 :     route_lists_(DB::PartitionCount()) {
     406          35 : }
     407             : 
     408             : //
     409             : // Destructor for EvpnSegment.
     410             : //
     411          70 : EvpnSegment::~EvpnSegment() {
     412          35 :     assert(!esi_ad_route_);
     413          35 :     assert(pe_list_.empty());
     414          70 : }
     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          63 : void EvpnSegment::TriggerMacRouteUpdate() {
     442          63 :     CHECK_CONCURRENCY("bgp::EvpnSegment");
     443             : 
     444         315 :     for (size_t part_id = 0; part_id < route_lists_.size(); ++part_id) {
     445         252 :         EvpnManagerPartition *partition = evpn_manager_->GetPartition(part_id);
     446         328 :         BOOST_FOREACH(EvpnRoute *route, route_lists_[part_id]) {
     447          38 :             partition->TriggerMacRouteUpdate(route);
     448             :         }
     449             :     }
     450          63 : }
     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          65 : bool EvpnSegment::UpdatePeList() {
     459          65 :     CHECK_CONCURRENCY("bgp::EvpnSegment");
     460             : 
     461             :     // Mark all entries as invalid.
     462          65 :     for (RemotePeList::iterator it = pe_list_.begin();
     463         128 :         it != pe_list_.end(); ++it) {
     464          63 :         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          65 :     bool changed = false;
     471          65 :     for (Route::PathList::const_iterator path_it =
     472         130 :          esi_ad_route_->GetPathList().begin();
     473         260 :          path_it != esi_ad_route_->GetPathList().end(); ++path_it) {
     474             :         const BgpPath *path =
     475          65 :             static_cast<const BgpPath *>(path_it.operator->());
     476          65 :         const BgpAttr *attr = path->GetAttr();
     477             : 
     478             :         // Skip non-VXLAN paths for now.
     479          65 :         const ExtCommunity *extcomm = attr->ext_community();
     480          65 :         if (!extcomm || !extcomm->ContainsTunnelEncapVxlan())
     481           2 :             continue;
     482             : 
     483             :         // Go through existing pe list and try to find the RemotePe.
     484          63 :         bool found = false;
     485          63 :         RemotePe remote_pe(path);
     486          63 :         for (RemotePeList::iterator it = pe_list_.begin();
     487          98 :             it != pe_list_.end(); ++it) {
     488             :             // Skip if the nexthop doesn't match.
     489          54 :             if (it->attr->nexthop() != remote_pe.attr->nexthop())
     490          31 :                 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          23 :             if (it->esi_valid) {
     495           0 :                 found = true;
     496           0 :                 break;
     497             :             }
     498             : 
     499             :             // Check if we have a match.
     500          23 :             if (*it == remote_pe) {
     501          19 :                 it->esi_valid = true;
     502          19 :                 found = true;
     503          19 :                 break;
     504             :             }
     505             :         }
     506             : 
     507             :         // Add a new entry to the pe list if we didn't find the RemotePe.
     508          63 :         if (!found) {
     509          44 :             pe_list_.push_back(remote_pe);
     510          44 :             changed = true;
     511             :         }
     512          63 :     }
     513             : 
     514             :     // Erase invalid entries from the list.
     515          65 :     for (RemotePeList::iterator it = pe_list_.begin(), next = it;
     516         172 :         it != pe_list_.end(); it = next) {
     517         107 :         ++next;
     518         107 :         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          65 :     single_active_ = false;
     526          65 :     for (RemotePeList::iterator it = pe_list_.begin();
     527         118 :         it != pe_list_.end(); ++it) {
     528          62 :         if (it->single_active) {
     529           9 :             single_active_ = true;
     530           9 :             break;
     531             :         }
     532             :     }
     533             : 
     534          65 :     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          13 :         return false;
     546             : 
     547             :     // Bail if the dependent route list for any partition is not empty.
     548         176 :     for (size_t part_id = 0; part_id < route_lists_.size(); ++part_id) {
     549         141 :         if (!route_lists_[part_id].empty())
     550           1 :             return false;
     551             :     }
     552             : 
     553          35 :     return true;
     554             : }
     555             : 
     556             : //
     557             : // Constructor for EvpnSegment::RemotePe.
     558             : //
     559          63 : EvpnSegment::RemotePe::RemotePe(const BgpPath *path)
     560          63 :   : esi_valid(true),
     561          63 :     single_active(path->GetAttr()->evpn_single_active()),
     562          63 :     peer(path->GetPeer()),
     563          63 :     attr(path->GetAttr()),
     564          63 :     flags(path->GetFlags()),
     565          63 :     src(path->GetSource()) {
     566          63 : }
     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          23 : bool EvpnSegment::RemotePe::operator==(const RemotePe &rhs) const {
     574          23 :     if (peer != rhs.peer)
     575           0 :         return false;
     576          23 :     if (attr != rhs.attr)
     577           4 :         return false;
     578          19 :     if (flags != rhs.flags)
     579           0 :         return false;
     580          19 :     if (src != rhs.src)
     581           0 :         return false;
     582          19 :     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          28 : void EvpnMacState::DeleteAliasedPath(AliasedPathList::const_iterator it) {
     622          28 :     BgpPath *path = *it;
     623          28 :     const IPeer *peer = path->GetPeer();
     624          28 :     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         170 : bool EvpnMacState::ProcessMacRouteAliasing() {
     663         170 :     CHECK_CONCURRENCY("db::DBTable");
     664             : 
     665         170 :     const BgpPath *path = route_->BestPath();
     666             : 
     667             :     // Find the remote PE entry that corresponds to the primary path.
     668         170 :     bool found_primary_pe = false;
     669         170 :     EvpnSegment::const_iterator it;
     670         170 :     if (path && segment_ && !segment_->single_active())
     671          64 :         it = segment_->begin();
     672         244 :     for (; path && segment_ && !segment_->single_active() &&
     673         249 :          it != segment_->end(); ++it) {
     674          50 :         if (path->GetAttr()->nexthop() != it->attr->nexthop())
     675           5 :             continue;
     676          45 :         found_primary_pe = true;
     677          45 :         break;
     678             :     }
     679             : 
     680             :     // Go through the remote PE list for the EvpnSegment and build the
     681             :     // list of future aliased paths.
     682         170 :     AliasedPathList future_aliased_path_list;
     683         170 :     if (found_primary_pe && path && segment_ && !segment_->single_active())
     684          45 :         it = segment_->begin();
     685         320 :     for (; found_primary_pe && path && segment_ &&
     686         425 :          !segment_->single_active() && it != segment_->end(); ++it) {
     687             :         // Skip if there's a BGP_XMPP path with the remote PE as nexthop.
     688         105 :         if (route_->FindPath(it->attr->nexthop()))
     689          47 :             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         170 :     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         170 :     return modified;
     705         170 : }
     706             : 
     707             : //
     708             : // Constructor for EvpnManagerPartition.
     709             : //
     710      170326 : EvpnManagerPartition::EvpnManagerPartition(EvpnManager *evpn_manager,
     711      170326 :     size_t part_id)
     712      170326 :     : evpn_manager_(evpn_manager),
     713      170326 :       part_id_(part_id),
     714      340653 :       mac_update_trigger_(new TaskTrigger(
     715             :           boost::bind(&EvpnManagerPartition::ProcessMacUpdateList, this),
     716      340651 :           TaskScheduler::GetInstance()->GetTaskId("db::DBTable"),
     717      340658 :           part_id)) {
     718      170329 :     table_partition_ = evpn_manager_->GetTablePartition(part_id);
     719      170329 : }
     720             : 
     721             : //
     722             : // Destructor for EvpnManagerPartition.
     723             : //
     724      170333 : EvpnManagerPartition::~EvpnManagerPartition() {
     725      170333 :     assert(local_mcast_node_list_.empty());
     726      170333 :     assert(remote_mcast_node_list_.empty());
     727      170333 :     assert(mac_update_list_.empty());
     728      170333 : }
     729             : 
     730             : //
     731             : // Get the DBTablePartition for the EvpnTable for our partition id.
     732             : //
     733       71355 : DBTablePartition *EvpnManagerPartition::GetTablePartition() {
     734       71355 :     return evpn_manager_->GetTablePartition(part_id_);
     735             : }
     736             : 
     737             : //
     738             : // Notify the Broadcast MAC route for the given EvpnMcastNode.
     739             : //
     740       20567 : void EvpnManagerPartition::NotifyNodeRoute(EvpnMcastNode *node) {
     741       20567 :     DBTablePartition *tbl_partition = GetTablePartition();
     742       20567 :     tbl_partition->Notify(node->route());
     743       20567 : }
     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       13018 :     for (; it != ir_client_node_list_.end(); it++) {
     767      116924 :         BOOST_FOREACH(EvpnMcastNode *node, it->second) {
     768       94646 :             if (exclude_edge_replication_supported &&
     769       39145 :                 !node->edge_replication_not_supported()) {
     770       37575 :                 continue;
     771             :             }
     772       17926 :             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       54373 : bool EvpnManagerPartition::RemoveMcastNodeFromList(EvpnState::SG &sg,
     808             :                                                    EvpnMcastNode *node,
     809             :                                                    EvpnMcastNodeList *list) {
     810       54373 :     size_t deleted = 0;
     811       54373 :     if (list->count(sg)) {
     812       30955 :         deleted = (*list)[sg].erase(node);
     813       30955 :         if ((*list)[sg].size() == 0)
     814        2193 :             list->erase(sg);
     815             :     }
     816       54373 :     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       18575 : void EvpnManagerPartition::UpdateMcastNode(EvpnMcastNode *node, EvpnRoute *rt) {
     851       18575 :     node->TriggerUpdate();
     852       18575 :     EvpnState::SG sg = EvpnState::SG(rt->GetPrefix().source(),
     853       37150 :                                      rt->GetPrefix().group());
     854       18575 :     if (node->type() == EvpnMcastNode::LocalNode) {
     855       17799 :         RemoveMcastNodeFromList(sg, node, &replicator_node_list_);
     856       17799 :         if (node->assisted_replication_supported())
     857        1668 :             replicator_node_list_[sg].insert(node);
     858       17799 :         RemoveMcastNodeFromList(sg, node, &ir_client_node_list_);
     859       17799 :         if (!node->assisted_replication_leaf())
     860       17655 :             ir_client_node_list_[sg].insert(node);
     861       17799 :         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       18575 : }
     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         179 : void EvpnManagerPartition::TriggerMacRouteUpdate(EvpnRoute *route) {
     885         179 :     CHECK_CONCURRENCY("db::DBTable", "bgp::EvpnSegment");
     886             : 
     887         179 :     mac_update_list_.insert(route);
     888         179 :     mac_update_trigger_->Set();
     889         179 : }
     890             : 
     891             : //
     892             : // Process the MAC route update list for this EvpnManagerPartition.
     893             : //
     894         131 : bool EvpnManagerPartition::ProcessMacUpdateList() {
     895         131 :     CHECK_CONCURRENCY("db::DBTable");
     896             : 
     897         131 :     BgpTable *table = evpn_manager_->table();
     898         131 :     int listener_id = evpn_manager_->listener_id();
     899             : 
     900         471 :     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         170 :         if (route->is_onlist())
     906           0 :             continue;
     907             : 
     908             :         EvpnMacState *mac_state =
     909         170 :             dynamic_cast<EvpnMacState *>(route->GetState(table, listener_id));
     910         170 :         assert(mac_state);
     911         170 :         bool modified = mac_state->ProcessMacRouteAliasing();
     912         170 :         if (route->HasPaths()) {
     913         127 :             if (!mac_state->segment()) {
     914           9 :                 route->ClearState(table, listener_id);
     915           9 :                 delete mac_state;
     916             :             }
     917         127 :             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         130 :     mac_update_list_.clear();
     928         130 :     evpn_manager_->RetryDelete();
     929         130 :     return true;
     930             : }
     931             : 
     932        1400 : bool EvpnManagerPartition::GetForestNodeAddress(ErmVpnRoute *rt,
     933             :                                                  Ip4Address *address) const {
     934        1400 :     if (!evpn_manager_->ermvpn_table()->tree_manager())
     935           0 :         return false;
     936             :     uint32_t label;
     937        1400 :     vector<string> te;
     938        1400 :     return evpn_manager_->ermvpn_table()->tree_manager()->GetForestNodePMSI(
     939        1400 :                                            rt, &label, address, &te);
     940        1400 : }
     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        5075 : EvpnStatePtr EvpnManagerPartition::GetState(const SG &sg) {
     948        5075 :     EvpnState::StatesMap::iterator iter = states_.find(sg);
     949        5075 :     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        4433 : EvpnStatePtr EvpnManagerPartition::LocateState(const SG &sg) {
     966        4433 :     EvpnStatePtr evpn_state = GetState(sg);
     967        4433 :     if (evpn_state)
     968        1729 :         return evpn_state;
     969        2704 :     evpn_state = CreateState(sg);
     970        2704 :     assert(evpn_state);
     971        2704 :     return evpn_state;
     972           0 : }
     973             : 
     974        1737 : EvpnStatePtr EvpnManagerPartition::LocateState(EvpnRoute *rt) {
     975        1737 :     EvpnState::SG sg = EvpnState::SG(rt->GetPrefix().source(),
     976        3474 :                                      rt->GetPrefix().group());
     977        3474 :     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      176074 : bool EvpnManagerPartition::empty() const {
    1001      176074 :     if (!local_mcast_node_list_.empty())
    1002        4640 :         return false;
    1003      171434 :     if (!remote_mcast_node_list_.empty())
    1004         590 :         return false;
    1005      170844 :     if (!mac_update_list_.empty())
    1006           0 :         return false;
    1007      170844 :     assert(leaf_node_list_.empty());
    1008      170844 :     assert(replicator_node_list_.empty());
    1009      170844 :     assert(regular_node_list_.empty());
    1010      170844 :     assert(ir_client_node_list_.empty());
    1011      170844 :     return true;
    1012             : }
    1013             : 
    1014             : //
    1015             : // Return the BgpServer for the EvpnManagerPartition.
    1016             : //
    1017       45142 : BgpServer *EvpnManagerPartition::server() {
    1018       45142 :     return evpn_manager_->server();
    1019             : }
    1020             : 
    1021             : //
    1022             : // Return the EvpnTable for the EvpnManagerPartition.
    1023             : //
    1024       26840 : const EvpnTable *EvpnManagerPartition::table() const {
    1025       26840 :     return evpn_manager_->table();
    1026             : }
    1027             : 
    1028             : //
    1029             : // Constructor for EvpnManager.
    1030             : //
    1031       42907 : EvpnManager::EvpnManager(EvpnTable *table)
    1032       42907 :     : table_(table),
    1033       42907 :       listener_id_(DBTable::kInvalidId),
    1034       85814 :       segment_delete_trigger_(new TaskTrigger(
    1035             :           boost::bind(&EvpnManager::ProcessSegmentDeleteSet, this),
    1036       85814 :           TaskScheduler::GetInstance()->GetTaskId("bgp::EvpnSegment"), 0)),
    1037       85814 :       segment_update_trigger_(new TaskTrigger(
    1038             :           boost::bind(&EvpnManager::ProcessSegmentUpdateSet, this),
    1039       85814 :           TaskScheduler::GetInstance()->GetTaskId("bgp::EvpnSegment"), 0)),
    1040       85814 :       table_delete_ref_(this, table->deleter()) {
    1041       42907 :     deleter_.reset(new DeleteActor(this));
    1042       42906 :     db_states_count_ = 0;
    1043       42907 : }
    1044             : 
    1045             : //
    1046             : // Destructor for EvpnManager.
    1047             : //
    1048       85662 : EvpnManager::~EvpnManager() {
    1049       42907 :     assert(segment_map_.empty());
    1050       42907 :     assert(segment_delete_set_.empty());
    1051       42907 :     assert(segment_update_set_.empty());
    1052       85662 : }
    1053             : 
    1054             : //
    1055             : // Initialize the EvpnManager. We allocate the EvpnManagerPartitions
    1056             : // and register a DBListener for the EvpnTable.
    1057             : //
    1058       42755 : void EvpnManager::Initialize() {
    1059       42755 :     AllocPartitions();
    1060       42754 :     listener_id_ = table_->Register(
    1061             :         boost::bind(&EvpnManager::RouteListener, this, _1, _2),
    1062             :         "EvpnManager");
    1063       42753 :     ermvpn_table_ = dynamic_cast<ErmVpnTable *>(
    1064       42754 :         table_->routing_instance()->GetTable(Address::ERMVPN));
    1065       42753 :     if (ermvpn_table_)
    1066       42753 :         ermvpn_listener_id_ = ermvpn_table_->Register(
    1067             :             boost::bind(&EvpnManager::ErmVpnRouteListener, this, _1, _2),
    1068             :             "EvpnManager");
    1069       42753 : }
    1070             : 
    1071             : //
    1072             : // Terminate the EvpnManager. We free the EvpnManagerPartitions
    1073             : // and unregister from the EvpnTable.
    1074             : //
    1075       42755 : void EvpnManager::Terminate() {
    1076       42755 :     CHECK_CONCURRENCY("bgp::Config");
    1077       42755 :     table_->Unregister(listener_id_);
    1078       42755 :     listener_id_ = DBTable::kInvalidId;
    1079       42755 :     if (ermvpn_listener_id_ != DBTable::kInvalidId) {
    1080       42755 :         ermvpn_table_->Unregister(ermvpn_listener_id_);
    1081       42755 :         ermvpn_listener_id_ = DBTable::kInvalidId;
    1082             :     }
    1083       42755 :     FreePartitions();
    1084       42755 : }
    1085             : 
    1086             : //
    1087             : // Allocate the EvpnManagerPartitions.
    1088             : //
    1089       42755 : void EvpnManager::AllocPartitions() {
    1090      213082 :     for (int part_id = 0; part_id < DB::PartitionCount(); part_id++) {
    1091      170328 :         partitions_.push_back(new EvpnManagerPartition(this, part_id));
    1092             :     }
    1093       42743 : }
    1094             : 
    1095             : //
    1096             : // Free the EvpnManagerPartitions.
    1097             : //
    1098       42755 : void EvpnManager::FreePartitions() {
    1099       42755 :     STLDeleteValues(&partitions_);
    1100       42755 : }
    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      116676 : EvpnManagerPartition *EvpnManager::GetPartition(size_t part_id) {
    1126      116676 :     return partitions_[part_id];
    1127             : }
    1128             : 
    1129             : //
    1130             : // Get the DBTablePartition for the EvpnTable for given partition id.
    1131             : //
    1132      241684 : DBTablePartition *EvpnManager::GetTablePartition(size_t part_id) {
    1133      241684 :     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        7132 : UpdateInfo *EvpnManager::GetUpdateInfo(EvpnRoute *route) {
    1141        7132 :     CHECK_CONCURRENCY("db::DBTable");
    1142             : 
    1143        7132 :     DBState *dbstate = route->GetState(table_, listener_id_);
    1144             :     EvpnLocalMcastNode *local_node =
    1145        7132 :         dynamic_cast<EvpnLocalMcastNode *>(dbstate);
    1146             : 
    1147        7132 :     if (!local_node)
    1148           0 :         return NULL;
    1149             : 
    1150        7132 :     return local_node->GetUpdateInfo(route);
    1151             : }
    1152             : 
    1153       88041 : BgpServer *EvpnManager::server() {
    1154       88041 :     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          35 :         segment = new EvpnSegment(this, esi);
    1167          35 :         segment_map_.insert(esi, segment);
    1168             :     }
    1169          76 :     return segment;
    1170          76 : }
    1171             : 
    1172             : //
    1173             : // Find the EvpnSegment for the given EthernetSegmentId.
    1174             : //
    1175          75 : EvpnSegment *EvpnManager::FindSegment(const EthernetSegmentId &esi) {
    1176          75 :     assert(!esi.IsZero());
    1177          75 :     tbb::spin_rw_mutex::scoped_lock read_lock(segment_rw_mutex_, false);
    1178          75 :     SegmentMap::iterator loc = segment_map_.find(esi);
    1179         150 :     return (loc != segment_map_.end()) ? loc->second : NULL;
    1180          75 : }
    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          35 :             segment_update_set_.erase(segment);
    1211          35 :             EthernetSegmentId esi = segment->esi();
    1212          35 :             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          65 : void EvpnManager::TriggerSegmentUpdate(EvpnSegment *segment) {
    1247          65 :     CHECK_CONCURRENCY("db::DBTable");
    1248             : 
    1249          65 :     segment_update_set_.insert(segment);
    1250          65 :     segment_update_trigger_->Set();
    1251          65 : }
    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          65 : bool EvpnManager::ProcessSegmentUpdateSet() {
    1260          65 :     CHECK_CONCURRENCY("bgp::EvpnSegment");
    1261             : 
    1262         195 :     BOOST_FOREACH(EvpnSegment *segment, segment_update_set_) {
    1263          65 :         EvpnRoute *esi_ad_route = segment->esi_ad_route();
    1264          65 :         bool changed = segment->UpdatePeList();
    1265          65 :         if (changed)
    1266          63 :             segment->TriggerMacRouteUpdate();
    1267          65 :         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          65 :     segment_update_set_.clear();
    1274          65 :     RetryDelete();
    1275          65 :     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          65 : void EvpnManager::AutoDiscoveryRouteListener(EvpnRoute *route) {
    1298          65 :     CHECK_CONCURRENCY("db::DBTable");
    1299             : 
    1300          65 :     DBState *dbstate = route->GetState(table_, listener_id_);
    1301          65 :     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          43 :         EvpnSegment *segment = dynamic_cast<EvpnSegment *>(dbstate);
    1318          43 :         assert(segment);
    1319          43 :         TriggerSegmentUpdate(segment);
    1320             :     }
    1321             : }
    1322             : 
    1323             : //
    1324             : // DBListener callback handler for MacAdvertisement routes in the EvpnTable.
    1325             : //
    1326        9327 : void EvpnManager::MacAdvertisementRouteListener(
    1327             :     EvpnManagerPartition *partition, EvpnRoute *route) {
    1328        9327 :     CHECK_CONCURRENCY("db::DBTable");
    1329             : 
    1330        9327 :     DBState *dbstate = route->GetState(table_, listener_id_);
    1331        9327 :     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        9238 :         if (!route->IsValid())
    1335        3204 :             return;
    1336        6034 :         const BgpPath *path = route->BestPath();
    1337        6034 :         if (path->GetAttr()->esi().IsZero())
    1338        5982 :             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       46042 : void EvpnManager::InclusiveMulticastRouteListener(
    1385             :     EvpnManagerPartition *partition, EvpnRoute *route) {
    1386       46042 :     CHECK_CONCURRENCY("db::DBTable");
    1387             : 
    1388       46042 :     DBState *dbstate = route->GetState(table_, listener_id_);
    1389       46042 :     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       40943 :         EvpnMcastNode *node = dynamic_cast<EvpnMcastNode *>(dbstate);
    1406       40943 :         assert(node);
    1407             : 
    1408       40943 :         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       35844 :         } else if (node->UpdateAttributes(route)) {
    1414             :             // Update the EvpnMcastNode associated with the route.
    1415       18575 :             partition->UpdateMcastNode(node, route);
    1416             :         }
    1417             :     }
    1418             : }
    1419             : 
    1420             : //
    1421             : // DBListener callback handler for SelectiveMulticast routes in the EvpnTable.
    1422             : //
    1423        2379 : void EvpnManager::SelectiveMulticastRouteListener(
    1424             :     EvpnManagerPartition *partition, EvpnRoute *route) {
    1425             : 
    1426        2379 :     CHECK_CONCURRENCY("db::DBTable");
    1427        2379 :     EvpnMcastNode *dbstate = dynamic_cast<EvpnMcastNode *>(
    1428        2379 :             route->GetState(table_, listener_id_));
    1429        2379 :     bool is_usable = route->IsUsable();
    1430        2379 :     bool is_deleted = route->IsDeleted();
    1431        2379 :     bool checkErmvpnRoute = false;
    1432        2379 :     if (route->BestPath()) {
    1433        1737 :         checkErmvpnRoute = route->BestPath()->GetFlags() &
    1434             :             BgpPath::CheckGlobalErmVpnRoute;
    1435             :     }
    1436        2379 :     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        1737 :     EvpnStatePtr evpn_state = partition->LocateState(route);
    1452        1737 :     assert(evpn_state);
    1453        1737 :     evpn_state->smet_routes().insert(route);
    1454        1737 :     BgpPath *path = const_cast<BgpPath *>(route->BestPath());
    1455        1737 :     if (path && path->CheckErmVpn()) {
    1456        1400 :         ErmVpnRoute *global_rt = evpn_state->global_ermvpn_tree_rt();
    1457        1400 :         Ip4Address address;
    1458        1400 :         bool nh_found = partition->GetForestNodeAddress(global_rt, &address);
    1459        1400 :         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        1737 :     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        1737 : }
    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       10181 : bool EvpnManager::IsUsableGlobalTreeRootRoute(
    1512             :         ErmVpnRoute *ermvpn_route) const {
    1513       10181 :     if (!ermvpn_route || !ermvpn_route->IsUsable())
    1514        3517 :         return false;
    1515        6664 :     if (!ermvpn_table()->tree_manager())
    1516           0 :         return false;
    1517        6664 :     if (ermvpn_table()->tree_manager()->begin() ==
    1518       13328 :             ermvpn_table()->tree_manager()->end())
    1519         321 :         return false;
    1520        6343 :     ErmVpnRoute *global_rt = ermvpn_table()->tree_manager()->
    1521        6343 :                                GetGlobalTreeRootRoute(
    1522        6343 :                                ermvpn_route->GetPrefix().source(),
    1523        6343 :                                ermvpn_route->GetPrefix().group());
    1524        6343 :     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       24042 : void EvpnManager::ErmVpnRouteListener(DBTablePartBase *tpart,
    1531             :                                       DBEntryBase *db_entry) {
    1532       24042 :     CHECK_CONCURRENCY("db::DBTable");
    1533             : 
    1534       24042 :     ErmVpnRoute *ermvpn_route = dynamic_cast<ErmVpnRoute *>(db_entry);
    1535       24042 :     assert(ermvpn_route);
    1536             : 
    1537             :     // We only care about global tree routes for evpn stitching.
    1538       24042 :     if (ermvpn_route->GetPrefix().type() != ErmVpnPrefix::GlobalTreeRoute) {
    1539       19915 :         return;
    1540             :     }
    1541             : 
    1542       10181 :     EvpnMcastNode *dbstate = dynamic_cast<EvpnMcastNode *>(
    1543       10181 :         ermvpn_route->GetState(ermvpn_table(), ermvpn_listener_id()));
    1544             : 
    1545             :     // Handle GlobalTreeRoute route deletion.
    1546       10181 :     if (!IsUsableGlobalTreeRootRoute(ermvpn_route)) {
    1547             :         // Ignore if there is no DB State associated with route.
    1548        6054 :         if (!dbstate)
    1549        3358 :             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        3040 :         BOOST_FOREACH(EvpnRoute *route, evpn_state->smet_routes()) {
    1557         172 :             BgpPath *path = const_cast<BgpPath *>(route->BestPath());
    1558         172 :             if (path && (path->GetPathId() == 0)) {
    1559         172 :                 path->SetCheckErmVpn();
    1560             :             }
    1561         172 :             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        5965 :     BOOST_FOREACH(EvpnRoute *route, evpn_state->smet_routes()) {
    1587         919 :         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      112322 : void EvpnManager::RouteListener(DBTablePartBase *tpart, DBEntryBase *db_entry) {
    1597      112322 :     CHECK_CONCURRENCY("db::DBTable");
    1598             : 
    1599      112300 :     EvpnManagerPartition *partition = GetPartition(tpart->index());
    1600      112305 :     EvpnRoute *route = dynamic_cast<EvpnRoute *>(db_entry);
    1601      112305 :     assert(route);
    1602             : 
    1603      112305 :     switch (route->GetPrefix().type()) {
    1604          65 :     case EvpnPrefix::AutoDiscoveryRoute:
    1605          65 :         AutoDiscoveryRouteListener(route);
    1606          65 :         break;
    1607       31426 :     case EvpnPrefix::MacAdvertisementRoute:
    1608       31426 :         if (route->GetPrefix().mac_addr().IsBroadcast()) {
    1609       22099 :             InclusiveMulticastRouteListener(partition, route);
    1610             :         } else {
    1611        9327 :             MacAdvertisementRouteListener(partition, route);
    1612             :         }
    1613       31426 :         break;
    1614       23943 :     case EvpnPrefix::InclusiveMulticastRoute:
    1615       23943 :         InclusiveMulticastRouteListener(partition, route);
    1616       23943 :         break;
    1617        2379 :     case EvpnPrefix::SelectiveMulticastRoute:
    1618        2379 :         SelectiveMulticastRouteListener(partition, route);
    1619        2379 :         break;
    1620       54491 :     default:
    1621       54491 :         break;
    1622             :     }
    1623      112304 : }
    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       42907 : bool EvpnManager::MayDelete() const {
    1665       42907 :     CHECK_CONCURRENCY("bgp::Config");
    1666             : 
    1667       42907 :     if (!segment_map_.empty())
    1668           0 :         return false;
    1669             : 
    1670       42907 :     if (!segment_update_set_.empty())
    1671           0 :         return false;
    1672       42907 :     if (segment_update_trigger_->IsSet())
    1673           0 :         return false;
    1674             : 
    1675       42907 :     if (!segment_delete_set_.empty())
    1676           0 :         return false;
    1677       42907 :     if (segment_delete_trigger_->IsSet())
    1678           0 :         return false;
    1679             : 
    1680      383573 :     BOOST_FOREACH(const EvpnManagerPartition *partition, partitions_) {
    1681      170333 :         if (!partition->empty())
    1682           0 :             return false;
    1683      170333 :         if (!partition->states().empty())
    1684           0 :             return false;
    1685             :     }
    1686       42907 :     if (db_states_count_)
    1687           0 :         return false;
    1688       42907 :     return true;
    1689             : }
    1690             : 
    1691             : //
    1692             : // Initiate shutdown for the EvpnManager.
    1693             : //
    1694       42907 : void EvpnManager::Shutdown() {
    1695       42907 :     CHECK_CONCURRENCY("bgp::Config");
    1696       42907 : }
    1697             : 
    1698             : //
    1699             : // Trigger deletion of the EvpnManager and propagate the delete to any
    1700             : // dependents.
    1701             : //
    1702       42907 : void EvpnManager::ManagedDelete() {
    1703       42907 :     deleter_->Delete();
    1704       42907 : }
    1705             : 
    1706             : //
    1707             : // Attempt to enqueue a delete for the EvpnManager.
    1708             : //
    1709         754 : void EvpnManager::RetryDelete() {
    1710         754 :     if (!deleter()->IsDeleted())
    1711         754 :         return;
    1712           0 :     deleter()->RetryDelete();
    1713             : }
    1714             : 
    1715             : //
    1716             : // Return the LifetimeActor for the EvpnManager.
    1717             : //
    1718        5717 : LifetimeActor *EvpnManager::deleter() {
    1719        5717 :     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       41314 : EvpnState::SG::SG(const IpAddress &source, const IpAddress &group) :
    1736       41314 :     source(source), group(group) {
    1737       41314 : }
    1738             : 
    1739      307114 : bool EvpnState::SG::operator<(const SG &other) const {
    1740      307114 :     if (source < other.source)
    1741          39 :         return true;
    1742      307075 :     if (source > other.source)
    1743          29 :         return false;
    1744      307046 :     if (group < other.group)
    1745        9280 :         return true;
    1746      297766 :     if (group > other.group)
    1747        8752 :         return false;
    1748      289014 :     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        1400 : ErmVpnRoute *EvpnState::global_ermvpn_tree_rt() {
    1760        1400 :     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