LCOV - code coverage report
Current view: top level - bgp - bgp_multicast.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 570 582 97.9 %
Date: 2026-06-04 02:06:09 Functions: 78 80 97.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include "bgp/bgp_multicast.h"
       6             : 
       7             : #include <boost/bind/bind.hpp>
       8             : #include <boost/foreach.hpp>
       9             : 
      10             : #include "base/string_util.h"
      11             : #include "base/task_annotations.h"
      12             : #include "bgp/bgp_log.h"
      13             : #include "bgp/bgp_mvpn.h"
      14             : #include "bgp/bgp_server.h"
      15             : #include "bgp/bgp_update.h"
      16             : #include "bgp/ermvpn/ermvpn_table.h"
      17             : #include "bgp/mvpn/mvpn_route.h"
      18             : #include "bgp/routing-instance/routing_instance.h"
      19             : #include "bgp/routing-instance/routing_instance_analytics_types.h"
      20             : #include "bgp/routing-instance/routing_instance_log.h"
      21             : #include "bgp/tunnel_encap/tunnel_encap.h"
      22             : 
      23             : using std::string;
      24             : using std::vector;
      25             : using namespace boost::placeholders;
      26             : 
      27             : class McastTreeManager::DeleteActor : public LifetimeActor {
      28             : public:
      29       42907 :     explicit DeleteActor(McastTreeManager *tree_manager)
      30       42907 :         : LifetimeActor(tree_manager->table_->routing_instance()->server()->
      31             :                 lifetime_manager()),
      32       42907 :           tree_manager_(tree_manager) {
      33       42908 :     }
      34       85816 :     virtual ~DeleteActor() {
      35       85816 :     }
      36             : 
      37       42908 :     virtual bool MayDelete() const {
      38       42908 :         return tree_manager_->MayDelete();
      39             :     }
      40             : 
      41       42908 :     virtual void Shutdown() {
      42       42908 :         tree_manager_->Shutdown();
      43       42908 :     }
      44             : 
      45       42908 :     virtual void Destroy() {
      46       42908 :         tree_manager_->table_->DestroyTreeManager();
      47       42908 :     }
      48             : 
      49             : private:
      50             :     McastTreeManager *tree_manager_;
      51             : };
      52             : 
      53             : //
      54             : // Constructor for McastForwarder.  The level is determined by the route type.
      55             : // We get the address of the forwarder and the label_block from the attributes
      56             : // of the active path.  The LabelBLockPtr needs to be copied so that we can
      57             : // release the label when processing a delete notification - we won't have the
      58             : // path at that point.
      59             : //
      60             : // The RD will be zero for BGP learnt routes and the RouterId will be zero for
      61             : // XMPP learnt routes.
      62             : //
      63       10394 : McastForwarder::McastForwarder(McastSGEntry *sg_entry, ErmVpnRoute *route)
      64       10394 :     : sg_entry_(sg_entry),
      65       10394 :       route_(route),
      66       10394 :       global_tree_route_(NULL),
      67       10394 :       label_(0),
      68       10394 :       address_(0),
      69       10394 :       rd_(route->GetPrefix().route_distinguisher()),
      70       20788 :       router_id_(route->GetPrefix().router_id()) {
      71       10394 :     const BgpPath *path = route->BestPath();
      72       10394 :     const BgpAttr *attr = path->GetAttr();
      73             : 
      74       10394 :     if (route_->GetPrefix().type() == ErmVpnPrefix::NativeRoute) {
      75        5264 :         level_ = McastTreeManager::LevelNative;
      76        5264 :         address_ = attr->nexthop().to_v4();
      77        5264 :         label_block_ = attr->label_block();
      78             :     } else {
      79        5130 :         level_ = McastTreeManager::LevelLocal;
      80        5130 :         const EdgeDiscovery::Edge *edge = attr->edge_discovery()->edge_list[0];
      81        5130 :         address_ = edge->address;
      82        5130 :         label_block_ = edge->label_block;
      83             :     }
      84             : 
      85       10394 :     if (path->GetAttr()->ext_community())
      86        5216 :         encap_ = path->GetAttr()->ext_community()->GetTunnelEncap();
      87       10394 : }
      88             : 
      89             : //
      90             : // Destructor for McastForwarder. Flushes forward and reverse links to and
      91             : // from other McastForwarders.
      92             : //
      93       12317 : McastForwarder::~McastForwarder() {
      94       10394 :     DeleteGlobalTreeRoute();
      95       10394 :     FlushLinks();
      96       10394 :     ReleaseLabel();
      97       12317 : }
      98             : 
      99             : //
     100             : // Update the McastForwarder based on information in the ErmVpnRoute.
     101             : // Return true if something changed.
     102             : //
     103        8471 : bool McastForwarder::Update(ErmVpnRoute *route) {
     104        8471 :     McastForwarder forwarder(sg_entry_, route);
     105             : 
     106        8471 :     bool changed = false;
     107        8471 :     if (label_block_ != forwarder.label_block_) {
     108        1070 :         ReleaseLabel();
     109        1070 :         label_block_ = forwarder.label_block_;
     110        1070 :         changed = true;
     111             :     }
     112        8471 :     if (address_ != forwarder.address_) {
     113         458 :         address_ = forwarder.address_;
     114         458 :         changed = true;
     115             :     }
     116        8471 :     if (encap_ != forwarder.encap_) {
     117          14 :         encap_ = forwarder.encap_;
     118          14 :         changed = true;
     119             :     }
     120             : 
     121        8471 :     return changed;
     122        8471 : }
     123             : 
     124             : //
     125             : // Printable string for McastForwarder.
     126             : //
     127        2792 : std::string McastForwarder::ToString() const {
     128        2792 :     if (level_ == McastTreeManager::LevelNative) {
     129        5584 :         return rd_.ToString() + " -> " + integerToString(label_);
     130             :     } else {
     131           0 :         return router_id_.to_string() + " -> " + integerToString(label_);
     132             :     }
     133             : }
     134             : 
     135             : //
     136             : // Find a link to the given McastForwarder.
     137             : //
     138        4244 : McastForwarder *McastForwarder::FindLink(McastForwarder *forwarder) {
     139        4244 :     for (McastForwarderList::iterator it = tree_links_.begin();
     140        7025 :          it != tree_links_.end(); ++it) {
     141        2781 :         if (*it == forwarder) return forwarder;
     142             :     }
     143        4244 :     return NULL;
     144             : }
     145             : 
     146             : //
     147             : // Add a link to the given McastForwarder.
     148             : //
     149        4244 : void McastForwarder::AddLink(McastForwarder *forwarder) {
     150        4244 :     assert(!FindLink(forwarder));
     151        4244 :     tree_links_.push_back(forwarder);
     152        4244 : }
     153             : 
     154             : //
     155             : // Remove a link to the given McastForwarder.
     156             : //
     157        2122 : void McastForwarder::RemoveLink(McastForwarder *forwarder) {
     158        2122 :     for (McastForwarderList::iterator it = tree_links_.begin();
     159        2261 :          it != tree_links_.end(); ++it) {
     160        2261 :         if (*it == forwarder) {
     161        2122 :             tree_links_.erase(it);
     162        2122 :             return;
     163             :         }
     164             :     }
     165             : }
     166             : 
     167             : //
     168             : // Flush all links from this McastForwarder.  Takes care of removing the
     169             : // reverse links as well.
     170             : //
     171       16333 : void McastForwarder::FlushLinks() {
     172       16333 :     for (McastForwarderList::iterator it = tree_links_.begin();
     173       18455 :          it != tree_links_.end(); ++it) {
     174        2122 :         (*it)->RemoveLink(this);
     175             :     }
     176       16333 :     tree_links_.clear();
     177       16333 : }
     178             : 
     179             : //
     180             : // Allocate a label for this McastForwarder.  The label gets allocated from
     181             : // the LabelBlock corresponding to the label range advertised by the peer.
     182             : // This is used when updating the distribution tree for the McastSGEntry to
     183             : // this McastForwarder belongs.
     184             : //
     185        3937 : void McastForwarder::AllocateLabel() {
     186        3937 :     label_ = label_block_->AllocateLabel();
     187        3937 : }
     188             : 
     189             : //
     190             : // Release the label, if any, for this McastForwarder. This is required when
     191             : // updating the distribution tree for the McastSGEntry to which we belong.
     192             : //
     193       17403 : void McastForwarder::ReleaseLabel() {
     194       17403 :     if (label_ != 0) {
     195        3928 :         label_block_->ReleaseLabel(label_);
     196        3928 :         label_ = 0;
     197             :     }
     198       17403 : }
     199             : 
     200             : //
     201             : // Add the GlobalTreeRoute for this McastForwarder. The GlobalTreeRoute is
     202             : // used by the tree builder to tell the associated control-node about the
     203             : // forwarding edges for Native McastForwarders attached it.
     204             : //
     205        3457 : void McastForwarder::AddGlobalTreeRoute() {
     206        3457 :     assert(level_ == McastTreeManager::LevelLocal);
     207        3457 :     assert(!global_tree_route_);
     208             : 
     209             :     // Bail if there's no label allocated.
     210        3457 :     if (label_ == 0)
     211        2091 :         return;
     212             : 
     213             :     // Bail if we can't build a source RD.
     214        1455 :     if (sg_entry_->GetSourceRd().IsZero())
     215          89 :         return;
     216             : 
     217             :     // Construct the prefix and route key.
     218        1366 :     BgpTable *table = static_cast<BgpTable *>(route_->get_table());
     219             :     ErmVpnPrefix prefix(ErmVpnPrefix::GlobalTreeRoute,
     220        1366 :         RouteDistinguisher::kZeroRd, router_id_,
     221        1366 :         sg_entry_->group(), sg_entry_->source());
     222        1366 :     ErmVpnRoute rt_key(prefix);
     223             : 
     224             :     // Find or create the route.
     225        1366 :     McastManagerPartition *partition = sg_entry_->partition();
     226             :     DBTablePartition *tbl_partition =
     227        1366 :         static_cast<DBTablePartition *>(partition->GetTablePartition());
     228             :     ErmVpnRoute *route =
     229        1366 :         static_cast<ErmVpnRoute *>(tbl_partition->Find(&rt_key));
     230        1366 :     if (!route) {
     231         407 :         route = new ErmVpnRoute(prefix);
     232         407 :         tbl_partition->Add(route);
     233             :     } else {
     234         959 :         route->ClearDelete();
     235             :     }
     236             : 
     237             :     // Build the attributes.  Need to go through the tree links to build the
     238             :     // EdgeForwardingSpec.
     239        1366 :     BgpServer *server = table->routing_instance()->server();
     240        1366 :     BgpAttrSpec attr_spec;
     241        1366 :     BgpAttrNextHop nexthop(server->bgp_identifier());
     242        1366 :     attr_spec.push_back(&nexthop);
     243        1366 :     BgpAttrSourceRd source_rd(sg_entry_->GetSourceRd());
     244        1366 :     attr_spec.push_back(&source_rd);
     245        1366 :     EdgeForwardingSpec efspec;
     246        1366 :     for (McastForwarderList::const_iterator it = tree_links_.begin();
     247        2364 :          it != tree_links_.end(); ++it) {
     248         998 :         EdgeForwardingSpec::Edge *edge = new EdgeForwardingSpec::Edge;
     249         998 :         edge->SetInboundIp4Address(address_);
     250         998 :         edge->inbound_label = label_;
     251         998 :         edge->SetOutboundIp4Address((*it)->address());
     252         998 :         edge->outbound_label = (*it)->label();
     253         998 :         efspec.edge_list.push_back(edge);
     254             :     }
     255        1366 :     attr_spec.push_back(&efspec);
     256             :     // Add tunnel encaps for remote nodes
     257        1366 :     ExtCommunitySpec ext;
     258        1366 :     ext.AddTunnelEncaps(encap_);
     259        1366 :     if (!ext.communities.empty())
     260         104 :         attr_spec.push_back(&ext);
     261        1366 :     BgpAttrPtr attr = server->attr_db()->Locate(attr_spec);
     262             : 
     263             :     // Add a path with source BgpPath::Local.
     264        1366 :     BgpPath *path = new BgpPath(0, BgpPath::Local, attr);
     265        1366 :     route->InsertPath(path);
     266        1366 :     tbl_partition->Notify(route);
     267        1366 :     global_tree_route_ = route;
     268        1366 : }
     269             : 
     270             : //
     271             : // Delete the GlobalTreeRoute for this McastForwarder.
     272             : //
     273       13851 : void McastForwarder::DeleteGlobalTreeRoute() {
     274       13851 :     if (!global_tree_route_)
     275       12485 :         return;
     276             : 
     277        1366 :     McastManagerPartition *partition = sg_entry_->partition();
     278             :     DBTablePartition *tbl_partition =
     279        1366 :         static_cast<DBTablePartition *>(partition->GetTablePartition());
     280        1366 :     global_tree_route_->RemovePath(BgpPath::Local);
     281             : 
     282        1366 :     if (!global_tree_route_->HasPaths()) {
     283        1317 :         tbl_partition->Delete(global_tree_route_);
     284             :     } else {
     285          49 :         tbl_partition->Notify(global_tree_route_);
     286             :     }
     287        1366 :     global_tree_route_ = NULL;
     288             : }
     289             : 
     290             : //
     291             : // Append list of BgpOListElems from the Local tree to the BgpOListSpec. The
     292             : // list is built based on the tree links in this McastForwarder.
     293             : //
     294        4239 : void McastForwarder::AddLocalOListElems(BgpOListSpec *olist_spec) {
     295        4239 :     assert(level_ == McastTreeManager::LevelNative);
     296             : 
     297        4239 :     for (McastForwarderList::const_iterator it = tree_links_.begin();
     298        7882 :          it != tree_links_.end(); ++it) {
     299        3643 :         BgpOListElem elem((*it)->address(), (*it)->label(), (*it)->encap());
     300        3643 :         olist_spec->elements.push_back(elem);
     301        3643 :     }
     302        4239 : }
     303             : 
     304             : //
     305             : // Append list of BgpOListElems from the Global tree to the BgpOListSpec. The
     306             : // list is built based on EdgeForwarding attribute in the GlobalTreeRoute.
     307             : //
     308        4239 : void McastForwarder::AddGlobalOListElems(BgpOListSpec *olist_spec) {
     309        4239 :     assert(level_ == McastTreeManager::LevelNative);
     310             : 
     311             :     // Bail if this is not the forest node for the Local tree.
     312        4239 :     if (!sg_entry_->IsForestNode(this))
     313        2478 :         return;
     314             : 
     315        2267 :     const ErmVpnRoute *route = sg_entry_->tree_result_route();
     316        2267 :     if (!route)
     317         502 :         return;
     318             : 
     319        1765 :     const BgpPath *path = route->BestPath();
     320        1765 :     if (!path)
     321           4 :         return;
     322        1761 :     const BgpAttr *attr = path->GetAttr();
     323        1761 :     vector<string> encaps;
     324        1761 :     if (attr && attr->ext_community())
     325         590 :         encaps = attr->ext_community()->GetTunnelEncap();
     326             : 
     327             :     // Go through each forwarding edge and add it to the list.
     328        1761 :     const EdgeForwarding *eforwarding = path->GetAttr()->edge_forwarding();
     329        1761 :     for (EdgeForwarding::EdgeList::const_iterator it =
     330        4706 :          eforwarding->edge_list.begin(); it != eforwarding->edge_list.end();
     331        1184 :          ++it) {
     332        1184 :         const EdgeForwarding::Edge *edge = *it;
     333        1184 :         if (edge->inbound_address == address_) {
     334        1008 :             BgpOListElem elem(edge->outbound_address, edge->outbound_label,
     335        1008 :                               encaps);
     336        1008 :             olist_spec->elements.push_back(elem);
     337        1008 :         }
     338             :     }
     339        1761 : }
     340             : 
     341             : //
     342             : // Construct an UpdateInfo with the RibOutAttr that needs to be advertised to
     343             : // the IPeer for the ErmVpnRoute associated with this McastForwarder. This is
     344             : // used as Export method of the ErmVpnTable.  It is expected that the caller
     345             : // fills in the target RibPeerSet in the UpdateInfo.
     346             : //
     347             : // The main functionality here is to transform the McastForwarderList for the
     348             : // distribution tree and the EdgeForwarding attribute from the GlobalTreeRoute
     349             : // into a BgpOList.
     350             : //
     351        4239 : UpdateInfo *McastForwarder::GetUpdateInfo(ErmVpnTable *table) {
     352        4239 :     CHECK_CONCURRENCY("db::DBTable");
     353             : 
     354        4239 :     assert(level_ == McastTreeManager::LevelNative);
     355             : 
     356        4239 :     BgpOListSpec olist_spec(BgpAttribute::OList);
     357        4239 :     AddLocalOListElems(&olist_spec);
     358        4239 :     AddGlobalOListElems(&olist_spec);
     359             : 
     360             :     // Bail if there is no label allocated.
     361        4239 :     if (label_ == 0)
     362         641 :         return NULL;
     363             : 
     364        3598 :     BgpAttrSpec attr_spec;
     365        3598 :     attr_spec.push_back(&olist_spec);
     366        3598 :     BgpAttrPtr attr = table->server()->attr_db()->Locate(attr_spec);
     367             : 
     368        3598 :     UpdateInfo *uinfo = new UpdateInfo;
     369        3598 :     uinfo->roattr = RibOutAttr(table, route_, attr.get(), label_, true, true);
     370        5840 :     if (route_ && sg_entry_->IsForestNode(this) &&
     371        2242 :             sg_entry_->IsTreeBuilder(McastTreeManager::LevelLocal)) {
     372        1321 :         table->GetMvpnSourceAddress(route_, uinfo->roattr.source_address());
     373             :     }
     374        3598 :     return uinfo;
     375        4239 : }
     376             : 
     377             : //
     378             : // Constructor for McastSGEntry.
     379             : //
     380       24789 : McastSGEntry::McastSGEntry(McastManagerPartition *partition,
     381       24789 :         Ip4Address group, Ip4Address source)
     382       24789 :     : partition_(partition),
     383       24789 :       group_(group),
     384       24789 :       source_(source),
     385       24789 :       forest_node_(NULL),
     386       24789 :       local_tree_route_(NULL),
     387       24789 :       tree_result_route_(NULL),
     388       49578 :       on_work_queue_(false) {
     389       74367 :     for (int level = McastTreeManager::LevelFirst;
     390       74367 :          level < McastTreeManager::LevelCount; ++level) {
     391       49578 :         ForwarderSet *forwarders = new ForwarderSet;
     392       49578 :         forwarder_sets_.push_back(forwarders);
     393       49578 :         update_needed_.push_back(false);
     394             :     }
     395       24789 : }
     396             : 
     397             : //
     398             : // Destructor for McastSGEntry.
     399             : //
     400       25195 : McastSGEntry::~McastSGEntry() {
     401       24789 :     STLDeleteValues(&forwarder_sets_);
     402       25195 : }
     403             : 
     404             : //
     405             : // Printable string for McastSGEntry.
     406             : //
     407          56 : std::string McastSGEntry::ToString() const {
     408         112 :     return group_.to_string() + "," + source_.to_string();
     409             : }
     410             : 
     411             : //
     412             : // Add the given McastForwarder under this McastSGEntry and trigger update
     413             : // of the distribution tree.
     414             : //
     415        1923 : void McastSGEntry::AddForwarder(McastForwarder *forwarder) {
     416        1923 :     uint8_t level = forwarder->level();
     417        1923 :     forwarder_sets_[level]->insert(forwarder);
     418        1923 :     update_needed_[level] = true;
     419        1923 :     partition_->EnqueueSGEntry(this);
     420        1923 : }
     421             : 
     422             : //
     423             : // Handle change for the given McastForwarder under this McastSGEntry. Trigger
     424             : // update of the distribution tree.
     425             : //
     426             : // Note that this method only handles the change = the caller determines that
     427             : // there has been a change.
     428             : //
     429        1125 : void McastSGEntry::ChangeForwarder(McastForwarder *forwarder) {
     430        1125 :     uint8_t level = forwarder->level();
     431        1125 :     update_needed_[level] = true;
     432        1125 :     partition_->EnqueueSGEntry(this);
     433        1125 : }
     434             : 
     435             : //
     436             : // Delete the given McastForwarder from this McastSGEntry and trigger update
     437             : // of the distribution tree.
     438             : //
     439        1923 : void McastSGEntry::DeleteForwarder(McastForwarder *forwarder) {
     440        1923 :     if (forwarder == forest_node_)
     441         405 :         forest_node_ = NULL;
     442        1923 :     uint8_t level = forwarder->level();
     443        1923 :     forwarder_sets_[level]->erase(forwarder);
     444        1923 :     update_needed_[level] = true;
     445        1923 :     partition_->EnqueueSGEntry(this);
     446        1923 : }
     447             : 
     448             : //
     449             : // Get the SourceRD to be used when adding [Local|Global]TreeRoutes.  This
     450             : // SourceRD gets used as the RD when the ErmVpnRoute is replicated from the
     451             : // VRF table to the VPN table.
     452             : //
     453             : // We simply use the RD for the forest node.
     454             : //
     455        3693 : const RouteDistinguisher &McastSGEntry::GetSourceRd() const {
     456        3693 :     if (!forest_node_)
     457          89 :         return RouteDistinguisher::kZeroRd;
     458        3604 :     return forest_node_->route()->GetPrefix().route_distinguisher();
     459             : }
     460             : 
     461             : //
     462             : // Add the LocalTreeRoute for this McastSGEntry.  This route advertises a set
     463             : // of candidate edges from McastForwarders attached to this control-node that
     464             : // can be used by the tree builder to build the higher level tree.  We simply
     465             : // advertise edges McastTreeManager::kDegree - 1 edges from the forest node.
     466             : //
     467             : // We advertise kDegree-1 candidate edges via the EdgeDiscovery attribute. All
     468             : // the edges are for the forest node for the tree of native McastForwarders.
     469             : // The label block for each edge in the EdgeDiscovery attribute is of size 1 -
     470             : // this is label that has been allocated for the forest node.  Using a single
     471             : // label is acceptable because the tree builder algorithm does not change the
     472             : // relative order of nodes in the tree.
     473             : //
     474        1219 : void McastSGEntry::AddLocalTreeRoute() {
     475        1219 :     assert(!forest_node_);
     476        1219 :     assert(!local_tree_route_);
     477             : 
     478             :     // Select last usable leaf in the distribution tree as the forest node.
     479             :     // A leaf is considered usable if it has a valid label i.e. it has not
     480             :     // run out of labels.
     481        1219 :     uint8_t level = McastTreeManager::LevelNative;
     482        1219 :     ForwarderSet *forwarders = forwarder_sets_[level];
     483        1219 :     for (ForwarderSet::reverse_iterator rit = forwarders->rbegin();
     484        1227 :          rit != forwarders->rend(); ++rit) {
     485         880 :         McastForwarder *forwarder = *rit;
     486         880 :         if (forwarder->label()) {
     487         872 :             forest_node_ = forwarder;
     488         872 :             break;
     489             :         }
     490             :     }
     491             : 
     492             :     // Bail if we couldn't designate a forest node.
     493        1219 :     if (!forest_node_)
     494         347 :         return;
     495             : 
     496             :     // Construct the prefix and route key.
     497         872 :     BgpServer *server = partition_->server();
     498         872 :     Ip4Address router_id(server->bgp_identifier());
     499             :     ErmVpnPrefix prefix(ErmVpnPrefix::LocalTreeRoute,
     500         872 :         RouteDistinguisher::kZeroRd, router_id, group_, source_);
     501         872 :     ErmVpnRoute rt_key(prefix);
     502             : 
     503             :     // Find or create the route.
     504             :     DBTablePartition *tbl_partition =
     505         872 :         static_cast<DBTablePartition *>(partition_->GetTablePartition());
     506             :     ErmVpnRoute *route =
     507         872 :         static_cast<ErmVpnRoute *>(tbl_partition->Find(&rt_key));
     508         872 :     if (!route) {
     509         347 :         route = new ErmVpnRoute(prefix);
     510         347 :         tbl_partition->Add(route);
     511             :     } else {
     512         525 :         route->ClearDelete();
     513             :     }
     514             : 
     515             :     // Build the attributes.
     516         872 :     BgpAttrSpec attr_spec;
     517         872 :     BgpAttrNextHop nexthop(server->bgp_identifier());
     518         872 :     attr_spec.push_back(&nexthop);
     519         872 :     BgpAttrSourceRd source_rd(GetSourceRd());
     520         872 :     attr_spec.push_back(&source_rd);
     521         872 :     EdgeDiscoverySpec edspec;
     522        3488 :     for (int idx = 1; idx <= McastTreeManager::kDegree - 1; ++idx) {
     523        2616 :         EdgeDiscoverySpec::Edge *edge = new EdgeDiscoverySpec::Edge;
     524        2616 :         edge->SetIp4Address(forest_node_->address());
     525        2616 :         edge->SetLabels(forest_node_->label(), forest_node_->label());
     526        2616 :         edspec.edge_list.push_back(edge);
     527             :     }
     528         872 :     attr_spec.push_back(&edspec);
     529             :     // Add tunnel encaps for remote nodes
     530         872 :     ExtCommunitySpec ext;
     531         872 :     ext.AddTunnelEncaps(forest_node_->encap());
     532         872 :     if (!ext.communities.empty())
     533         109 :         attr_spec.push_back(&ext);
     534         872 :     BgpAttrPtr attr = server->attr_db()->Locate(attr_spec);
     535             : 
     536             :     // Add a path with source BgpPath::Local.
     537         872 :     BgpPath *path = new BgpPath(0, BgpPath::Local, attr);
     538         872 :     route->InsertPath(path);
     539         872 :     tbl_partition->Notify(route);
     540         872 :     local_tree_route_ = route;
     541         872 : }
     542             : 
     543             : //
     544             : // Delete the LocalTreeRoute for this McastSGEntry.
     545             : //
     546        1219 : void McastSGEntry::DeleteLocalTreeRoute() {
     547        1219 :     if (!local_tree_route_)
     548         347 :         return;
     549             : 
     550         872 :     forest_node_ = NULL;
     551             :     DBTablePartition *tbl_partition =
     552         872 :         static_cast<DBTablePartition *>(partition_->GetTablePartition());
     553         872 :     local_tree_route_->RemovePath(BgpPath::Local);
     554         872 :     if (!local_tree_route_->HasPaths()) {
     555         872 :         tbl_partition->Delete(local_tree_route_);
     556             :     } else {
     557           0 :         tbl_partition->Notify(local_tree_route_);
     558             :     }
     559         872 :     local_tree_route_ = NULL;
     560             : }
     561             : 
     562             : //
     563             : // Update the LocalTreeRoute for this McastSGEntry if RouterId has changed.
     564             : //
     565         725 : void McastSGEntry::UpdateLocalTreeRoute() {
     566         725 :     if (!local_tree_route_)
     567         725 :         return;
     568             : 
     569             :     // Bail if the RouterId hasn't changed.
     570         617 :     const BgpServer *server = partition_->server();
     571         617 :     Ip4Address router_id = local_tree_route_->GetPrefix().router_id();
     572         617 :     if (router_id.to_ulong() == server->bgp_identifier())
     573         617 :         return;
     574             : 
     575             :     // Add and delete the route.
     576           0 :     DeleteLocalTreeRoute();
     577           0 :     AddLocalTreeRoute();
     578             : }
     579             : 
     580             : //
     581             : // Update relevant [Local|Global]TreeRoutes for the McastSGEntry.
     582             : //
     583        3494 : void McastSGEntry::UpdateRoutes(uint8_t level) {
     584        3494 :     if (level == McastTreeManager::LevelNative) {
     585        1219 :         DeleteLocalTreeRoute();
     586        1219 :         AddLocalTreeRoute();
     587             :     } else {
     588        2275 :         ForwarderSet *forwarders = forwarder_sets_[level];
     589        2275 :         for (ForwarderSet::iterator it = forwarders->begin();
     590        5732 :              it != forwarders->end(); ++it) {
     591        3457 :             (*it)->DeleteGlobalTreeRoute();
     592        3457 :             (*it)->AddGlobalTreeRoute();
     593             :         }
     594             :     }
     595        3494 : }
     596             : 
     597        3378 : ErmVpnRoute *McastSGEntry::GetGlobalTreeRootRoute() const {
     598        3378 :     if (!IsTreeBuilder(McastTreeManager::LevelLocal))
     599        1644 :         return NULL;
     600        1734 :     ForwarderSet *forwarders = forwarder_sets_[McastTreeManager::LevelLocal];
     601        1734 :     assert(!forwarders->empty());
     602        1734 :     ForwarderSet::const_iterator it = forwarders->begin();
     603        1734 :     return (*it)->global_tree_route();
     604             : }
     605             : 
     606             : //
     607             : // Implement tree builder election.
     608             : //
     609        9118 : bool McastSGEntry::IsTreeBuilder(uint8_t level) const {
     610        9118 :     if (level == McastTreeManager::LevelNative)
     611        1221 :         return true;
     612             : 
     613        7897 :     const ForwarderSet *forwarders = forwarder_sets_[level];
     614        7897 :     ForwarderSet::const_iterator it = forwarders->begin();
     615        7897 :     if (it == forwarders->end())
     616         684 :         return false;
     617             : 
     618        7213 :     Ip4Address router_id(partition_->server()->bgp_identifier());
     619        7213 :     if ((*it)->router_id() != router_id)
     620        3222 :         return false;
     621             : 
     622        3991 :     return true;
     623             : }
     624             : 
     625             : //
     626             : //
     627             : // Update specified distribution tree for the McastSGEntry.  We traverse all
     628             : // McastForwarders in sorted order and arrange them in breadth first fashion
     629             : // in a k-ary tree.  Building the tree in this manner guarantees that we get
     630             : // the same tree for a given set of forwarders, independent of the order in
     631             : // in which they joined. This predictability is deemed to be more important
     632             : // than other criteria such as minimizing disruption of traffic, minimizing
     633             : // the cost/weight of the tree etc.
     634             : //
     635        6734 : void McastSGEntry::UpdateTree(uint8_t level) {
     636        6734 :     CHECK_CONCURRENCY("db::DBTable");
     637             : 
     638        6734 :     if (!update_needed_[level])
     639        4581 :         return;
     640        3494 :     update_needed_[level] = false;
     641             : 
     642             :     int degree;
     643        3494 :     if (level == McastTreeManager::LevelNative) {
     644        1219 :         degree = McastTreeManager::kDegree;
     645             :     } else {
     646        2275 :         degree = McastTreeManager::kDegree - 1;
     647             :     }
     648             : 
     649             :     // First get rid of the previous distribution tree and enqueue all the
     650             :     // associated ErmVpnRoutes for notification. Note that DBListeners will
     651             :     // not get invoked until after this routine is done.
     652        3494 :     ForwarderSet *forwarders = forwarder_sets_[level];
     653        3494 :     for (ForwarderSet::iterator it = forwarders->begin();
     654        9433 :          it != forwarders->end(); ++it) {
     655        5939 :        (*it)->FlushLinks();
     656        5939 :        (*it)->ReleaseLabel();
     657        5939 :        partition_->GetTablePartition()->Notify((*it)->route());
     658             :     }
     659             : 
     660             :     // Bail if we're not the tree builder.
     661        3494 :     if (!IsTreeBuilder(level)) {
     662        1341 :         UpdateRoutes(level);
     663        1341 :         return;
     664             :     }
     665             : 
     666             :     // Create a vector of pointers to the McastForwarders in sorted order.
     667             :     // We do this because std::set doesn't support random access iterators.
     668             :     // Skip if we can't allocate a label for the McastForwarder.
     669        2153 :     McastForwarderList vec;
     670        2153 :     vec.reserve(forwarders->size());
     671        2153 :     for (ForwarderSet::iterator it = forwarders->begin();
     672        6090 :          it != forwarders->end(); ++it) {
     673        3937 :         McastForwarder *forwarder = *it;
     674        3937 :         forwarder->AllocateLabel();
     675        3937 :         if (!forwarder->label())
     676           9 :             continue;
     677        3928 :         vec.push_back(forwarder);
     678             :     }
     679             : 
     680             :     // Go through each McastForwarder in the vector and link it to it's parent
     681             :     // McastForwarder in the k-ary tree. We also add a link from the parent to
     682             :     // the entry in question.
     683        6081 :     for (McastForwarderList::iterator it = vec.begin(); it != vec.end(); ++it) {
     684        3928 :         int idx = it - vec.begin();
     685        3928 :         if (idx == 0)
     686        1806 :             continue;
     687             : 
     688        2122 :         int parent_idx = (idx - 1) / degree;
     689        2122 :         McastForwarderList::iterator parent_it = vec.begin() + parent_idx;
     690        2122 :         assert(parent_it != vec.end());
     691        2122 :         McastForwarder *forwarder = *it;
     692        2122 :         McastForwarder *parent_forwarder = *parent_it;
     693        2122 :         forwarder->AddLink(parent_forwarder);
     694        2122 :         parent_forwarder->AddLink(forwarder);
     695             :     }
     696             : 
     697             :     // Update [Local|Global]TreeRoutes.
     698        2153 :     UpdateRoutes(level);
     699        2153 : }
     700             : 
     701             : //
     702             : // Update distribution trees for both levels.
     703             : //
     704        3367 : void McastSGEntry::UpdateTree() {
     705       10101 :     for (uint8_t level = McastTreeManager::LevelFirst;
     706       10101 :          level < McastTreeManager::LevelCount; ++level) {
     707        6734 :         UpdateTree(level);
     708             :     }
     709        3367 : }
     710             : 
     711             : //
     712             : // Trigger notification of the ErmVpnRoute associated with the McastForwarder
     713             : // that is the forest node. This is used to trigger a rebuild of the BgpOlist
     714             : // when the GlobalTreeRoute is updated.
     715             : //
     716        2077 : void McastSGEntry::NotifyForestNode() {
     717        2077 :     if (!forest_node_)
     718         426 :         return;
     719        1651 :     partition_->GetTablePartition()->Notify(forest_node_->route());
     720             : }
     721             : 
     722         175 : bool McastSGEntry::GetForestNodePMSI(uint32_t *label, Ip4Address *address,
     723             :         vector<string> *tunnel_encap) const {
     724         175 :     if (!forest_node_)
     725           0 :         return false;
     726         175 :     *label = forest_node_->label();
     727         175 :     *address = forest_node_->address();
     728         175 :     *tunnel_encap = forest_node_->encap();
     729         175 :     return true;
     730             : }
     731             : 
     732        7837 : bool McastSGEntry::IsForestNode(McastForwarder *forwarder) {
     733        7837 :     return (forwarder == forest_node_);
     734             : }
     735             : 
     736        3367 : bool McastSGEntry::empty() const {
     737        3367 :     if (local_tree_route_ || tree_result_route_)
     738        2676 :         return false;
     739         691 :     if (!forwarder_sets_[McastTreeManager::LevelNative]->empty())
     740           3 :         return false;
     741         688 :     if (!forwarder_sets_[McastTreeManager::LevelLocal]->empty())
     742         282 :         return false;
     743         406 :     return true;
     744             : }
     745             : 
     746             : //
     747             : // Constructor for McastManagerPartition.
     748             : //
     749       42873 : McastManagerPartition::McastManagerPartition(McastTreeManager *tree_manager,
     750       42873 :         size_t part_id)
     751       42873 :     : tree_manager_(tree_manager),
     752       42873 :       part_id_(part_id),
     753       42873 :       update_count_(0),
     754       85745 :       work_queue_(TaskScheduler::GetInstance()->GetTaskId("db::DBTable"),
     755       42872 :               part_id_,
     756       42873 :               boost::bind(&McastManagerPartition::ProcessSGEntry, this, _1)) {
     757       42874 : }
     758             : 
     759             : //
     760             : // Destructor for McastManagerPartition.
     761             : //
     762       42875 : McastManagerPartition::~McastManagerPartition() {
     763       42875 :     work_queue_.Shutdown();
     764       42875 : }
     765             : 
     766             : // Find the McastSGEntry for the given group and source.
     767       20830 : McastSGEntry *McastManagerPartition::FindSGEntry(
     768             :         const Ip4Address &group, const Ip4Address &source) {
     769             :     return const_cast<McastSGEntry *>(
     770       20830 :         static_cast<const McastManagerPartition *>(this)->FindSGEntry(group,
     771       20830 :                                                                       source));
     772             : }
     773             : 
     774             : //
     775             : // Find the McastSGEntry for the given group and source.
     776             : //
     777       24383 : const McastSGEntry *McastManagerPartition::FindSGEntry(
     778             :         const Ip4Address &group, const Ip4Address &source) const {
     779             :     McastSGEntry temp_sg_entry(const_cast<McastManagerPartition *>(this),
     780       24383 :                                group, source);
     781       24383 :     SGList::const_iterator it = sg_list_.find(&temp_sg_entry);
     782       48766 :     return (it != sg_list_.end() ? *it : NULL);
     783       24383 : }
     784             : 
     785             : //
     786             : // Find or create the McastSGEntry for the given group and source.
     787             : //
     788        2369 : McastSGEntry *McastManagerPartition::LocateSGEntry(
     789             :         Ip4Address group, Ip4Address source) {
     790        2369 :     McastSGEntry *sg_entry = FindSGEntry(group, source);
     791        2369 :     if (!sg_entry) {
     792         406 :         sg_entry = new McastSGEntry(this, group, source);
     793         406 :         sg_list_.insert(sg_entry);
     794             :     }
     795        2369 :     return sg_entry;
     796             : }
     797             : 
     798        3378 : ErmVpnRoute *McastManagerPartition::GetGlobalTreeRootRoute(
     799             :         const Ip4Address &source, const Ip4Address &group) const {
     800        3378 :     const McastSGEntry *sg = FindSGEntry(group, source);
     801        3378 :     return sg ? sg->GetGlobalTreeRootRoute() : NULL;
     802             : }
     803             : 
     804        7984 : void McastManagerPartition::NotifyForestNode(
     805             :         const Ip4Address &source, const Ip4Address &group) {
     806        7984 :     McastSGEntry *sg = FindSGEntry(group, source);
     807        7984 :     if (sg)
     808         315 :         sg->NotifyForestNode();
     809        7984 : }
     810             : 
     811         175 : bool McastManagerPartition::GetForestNodePMSI(ErmVpnRoute *rt, uint32_t *label,
     812             :         Ip4Address *address, vector<string> *encap) const {
     813         175 :     const McastSGEntry *sg = FindSGEntry(rt->GetPrefix().group(),
     814         175 :                                          rt->GetPrefix().source());
     815         175 :     return sg ? sg->GetForestNodePMSI(label, address, encap) : false;
     816             : }
     817             : 
     818             : //
     819             : // Enqueue the given McastSGEntry on the WorkQueue if it's not already on it.
     820             : //
     821        5417 : void McastManagerPartition::EnqueueSGEntry(McastSGEntry *sg_entry) {
     822        5417 :     if (sg_entry->on_work_queue())
     823        2050 :         return;
     824        3367 :     work_queue_.Enqueue(sg_entry);
     825        3367 :     sg_entry->set_on_work_queue();
     826             : }
     827             : 
     828             : //
     829             : // Callback for the WorkQueue. Updates distribution trees for the McastSGEntry.
     830             : // Also gets rid of the McastSGEntry if it is eligible to be deleted.
     831             : //
     832        3367 : bool McastManagerPartition::ProcessSGEntry(McastSGEntry *sg_entry) {
     833        3367 :     CHECK_CONCURRENCY("db::DBTable");
     834             : 
     835        3367 :     sg_entry->clear_on_work_queue();
     836        3367 :     sg_entry->UpdateTree();
     837        3367 :     update_count_++;
     838             : 
     839        3367 :     if (sg_entry->empty()) {
     840         406 :         sg_list_.erase(sg_entry);
     841         406 :         delete sg_entry;
     842             :     }
     843             : 
     844        3367 :     if (sg_list_.empty())
     845         289 :         tree_manager_->RetryDelete();
     846             : 
     847        3367 :     return true;
     848             : }
     849             : 
     850             : //
     851             : // Get the DBTablePartBase for the ErmVpnTable for our partition id.
     852             : //
     853       12066 : DBTablePartBase *McastManagerPartition::GetTablePartition() {
     854       12066 :     return tree_manager_->GetTablePartition(part_id_);
     855             : }
     856             : 
     857           0 : const RoutingInstance *McastManagerPartition::routing_instance() const {
     858           0 :     return tree_manager_->table()->routing_instance();
     859             : }
     860             : 
     861        8702 : BgpServer *McastManagerPartition::server() {
     862        8702 :     return tree_manager_->table()->server();
     863             : }
     864             : 
     865           0 : const BgpServer *McastManagerPartition::server() const {
     866           0 :     return tree_manager_->table()->server();
     867             : }
     868             : 
     869             : //
     870             : // Constructor for McastTreeManager.
     871             : //
     872       42908 : McastTreeManager::McastTreeManager(ErmVpnTable *table)
     873       42908 :     : table_(table),
     874       42908 :       listener_id_(DBTable::kInvalidId),
     875       42908 :       table_delete_ref_(this, table->deleter()) {
     876       42908 :     deleter_.reset(new DeleteActor(this));
     877       42906 : }
     878             : 
     879             : //
     880             : // Destructor for McastTreeManager.
     881             : //
     882       66865 : McastTreeManager::~McastTreeManager() {
     883       66865 : }
     884             : 
     885             : //
     886             : // Initialize the McastTreeManager. We allocate the McastManagerPartitions
     887             : // and register a DBListener for the ErmVpnTable.
     888             : //
     889       42873 : void McastTreeManager::Initialize() {
     890       42873 :     AllocPartitions();
     891       42873 :     listener_id_ = table_->Register(
     892             :         boost::bind(&McastTreeManager::RouteListener, this, _1, _2),
     893             :         "McastTreeManager");
     894       42874 : }
     895             : 
     896             : //
     897             : // Terminate the McastTreeManager. We free the McastManagerPartitions
     898             : // and unregister from the ErmVpnTable.
     899             : //
     900       42875 : void McastTreeManager::Terminate() {
     901       42875 :     table_->Unregister(listener_id_);
     902       42875 :     FreePartitions();
     903       42875 : }
     904             : 
     905             : //
     906             : // Allocate the McastManagerPartitions.
     907             : //
     908       42873 : void McastTreeManager::AllocPartitions() {
     909       85746 :     for (int part_id = 0; part_id < table_->PartitionCount(); part_id++) {
     910       42873 :         partitions_.push_back(new McastManagerPartition(this, part_id));
     911             :     }
     912       42872 : }
     913             : 
     914             : //
     915             : // Free the McastManagerPartitions.
     916             : //
     917       42875 : void McastTreeManager::FreePartitions() {
     918       85750 :     for (size_t part_id = 0; part_id < partitions_.size(); part_id++) {
     919       42875 :         delete partitions_[part_id];
     920             :     }
     921       42875 :     partitions_.clear();
     922       42875 : }
     923             : 
     924        7986 : McastManagerPartition *McastTreeManager::GetPartition(int part_id) {
     925        7986 :     return partitions_[part_id];
     926             : }
     927             : 
     928        3553 : const McastManagerPartition *McastTreeManager::GetPartition(int part_id) const {
     929        3553 :     return partitions_[part_id];
     930             : }
     931             : 
     932             : //
     933             : // Get the DBTablePartBase for the ErmVpnTable for given partition id.
     934             : //
     935       12066 : DBTablePartBase *McastTreeManager::GetTablePartition(size_t part_id) {
     936       12066 :     return table_->GetTablePartition(part_id);
     937             : }
     938             : 
     939             : //
     940             : // Construct export state for the given ErmVpnRoute. Note that the route
     941             : // only needs to be exported to the IPeer from which it was learnt.
     942             : //
     943        3269 : UpdateInfo *McastTreeManager::GetUpdateInfo(ErmVpnRoute *route) {
     944        3269 :     CHECK_CONCURRENCY("db::DBTable");
     945             : 
     946        3269 :     DBState *dbstate = route->GetState(table_, listener_id_);
     947        3269 :     McastForwarder *forwarder = dynamic_cast<McastForwarder *>(dbstate);
     948             : 
     949        3269 :     if (!forwarder)
     950           0 :         return NULL;
     951             : 
     952        3269 :     return forwarder->GetUpdateInfo(table_);
     953             : }
     954             : 
     955             : //
     956             : // DBListener callback handler for Native and Local routes in the ErmVpnTable.
     957             : // It creates, updates or deletes the associated McastForwarder as appropriate.
     958             : //
     959             : // Creates a McastSGEntry if one doesn't already exist. However, McastSGEntrys
     960             : // don't get deleted from here.  They only get deleted from WorkQueue callback
     961             : // routine i.e. McastManagerPartition::ProcessSGEntry.
     962             : //
     963       12338 : void McastTreeManager::TreeNodeListener(McastManagerPartition *partition,
     964             :         ErmVpnRoute *route) {
     965       12338 :     CHECK_CONCURRENCY("db::DBTable");
     966             : 
     967       12338 :     DBState *dbstate = route->GetState(table_, listener_id_);
     968       12338 :     if (!dbstate) {
     969             :         // We have no previous DBState for this route.
     970             :         // Bail if the route is not valid.
     971        1944 :         if (!route->IsValid())
     972          21 :             return;
     973             : 
     974             :         // Create a new McastForwarder and associate it with the route.
     975        1923 :         McastSGEntry *sg_entry = partition->LocateSGEntry(
     976        1923 :             route->GetPrefix().group(), route->GetPrefix().source());
     977        1923 :         McastForwarder *forwarder = new McastForwarder(sg_entry, route);
     978        1923 :         sg_entry->AddForwarder(forwarder);
     979        1923 :         route->SetState(table_, listener_id_, forwarder);
     980             : 
     981             :         // Update local tree route if our RouterId has changed. Ideally,
     982             :         // we should trigger an update of all local trees routes when we
     983             :         // detect a change in RouterId. Instead, we currently check and
     984             :         // update the local route when we detect a new local route from
     985             :         // another node.
     986        1923 :         if (route->GetPrefix().type() == ErmVpnPrefix::LocalTreeRoute)
     987         725 :             sg_entry->UpdateLocalTreeRoute();
     988             :     } else {
     989       10394 :         McastSGEntry *sg_entry = partition->FindSGEntry(
     990       10394 :             route->GetPrefix().group(), route->GetPrefix().source());
     991       10394 :         assert(sg_entry);
     992       10394 :         McastForwarder *forwarder = dynamic_cast<McastForwarder *>(dbstate);
     993       10394 :         assert(forwarder);
     994             : 
     995       10394 :         if (!route->IsValid()) {
     996             :             // Delete the McastForwarder associated with the route.
     997        1923 :             route->ClearState(table_, listener_id_);
     998        1923 :             sg_entry->DeleteForwarder(forwarder);
     999        1923 :             delete forwarder;
    1000        8471 :         } else if (forwarder->Update(route)) {
    1001             :             // Trigger update of the distribution tree.
    1002        1125 :             sg_entry->ChangeForwarder(forwarder);
    1003             :         }
    1004             :     }
    1005             : }
    1006             : 
    1007             : //
    1008             : // DBListener callback handler for GlobalTreeRoutes in the ErmVpnTable. It
    1009             : // updates the tree_result_route_ and triggers re-evaluation of the forest
    1010             : // node McastForwarder's BgpOlist.
    1011             : //
    1012        9581 : void McastTreeManager::TreeResultListener(McastManagerPartition *partition,
    1013             :         ErmVpnRoute *route) {
    1014        9581 :     CHECK_CONCURRENCY("db::DBTable");
    1015             : 
    1016        9581 :     DBState *dbstate = route->GetState(table_, listener_id_);
    1017        9581 :     if (!dbstate) {
    1018             :         // We have no previous DBState for this route.
    1019             :         // Bail if the route is not valid.
    1020        8265 :         if (!route->IsValid())
    1021        6001 :             return;
    1022             : 
    1023             :         // Ignore GlobalTreeRoute if it's not applicable to this control-node.
    1024        2264 :         BgpServer *server = table_->routing_instance()->server();
    1025        2264 :         if (route->GetPrefix().router_id().to_ulong() !=
    1026        2264 :             server->bgp_identifier())
    1027        1818 :             return;
    1028             : 
    1029         446 :         McastSGEntry *sg_entry = partition->LocateSGEntry(
    1030         446 :             route->GetPrefix().group(), route->GetPrefix().source());
    1031         446 :         route->SetState(table_, listener_id_, sg_entry);
    1032         446 :         sg_entry->set_tree_result_route(route);
    1033         446 :         sg_entry->NotifyForestNode();
    1034             :     } else {
    1035        1316 :         McastSGEntry *sg_entry = dynamic_cast<McastSGEntry *>(dbstate);
    1036        1316 :         assert(sg_entry);
    1037             : 
    1038        1316 :         if (!route->IsValid()) {
    1039         446 :             sg_entry->clear_tree_result_route();
    1040         446 :             route->ClearState(table_, listener_id_);
    1041         446 :             partition->EnqueueSGEntry(sg_entry);
    1042             :         }
    1043        1316 :         sg_entry->NotifyForestNode();
    1044             :     }
    1045             : }
    1046             : 
    1047             : //
    1048             : // DBListener callback handler for the ErmVpnTable. GlobalTreeRoutes provide
    1049             : // result information and hence are handled differently than Native and Local
    1050             : // routes, which result in update of a McastForwarder.
    1051             : //
    1052       21919 : void McastTreeManager::RouteListener(
    1053             :         DBTablePartBase *tpart, DBEntryBase *db_entry) {
    1054       21919 :     CHECK_CONCURRENCY("db::DBTable");
    1055             : 
    1056       21919 :     McastManagerPartition *partition = partitions_[tpart->index()];
    1057       21919 :     ErmVpnRoute *route = dynamic_cast<ErmVpnRoute *>(db_entry);
    1058       21919 :     if (route->GetPrefix().type() == ErmVpnPrefix::GlobalTreeRoute) {
    1059        9581 :         TreeResultListener(partition, route);
    1060             :     } else {
    1061       12338 :         TreeNodeListener(partition, route);
    1062             :     }
    1063       21919 : }
    1064             : 
    1065             : 
    1066             : //
    1067             : // Check if the McastTreeManager can be deleted. This can happen only if all
    1068             : // the McastManagerPartitions are empty.
    1069             : //
    1070       42908 : bool McastTreeManager::MayDelete() const {
    1071       42908 :     CHECK_CONCURRENCY("bgp::Config");
    1072             : 
    1073       42908 :     for (PartitionList::const_iterator it = partitions_.begin();
    1074       85783 :          it != partitions_.end(); ++it) {
    1075       42875 :         if (!(*it)->empty())
    1076           0 :             return false;
    1077             :     }
    1078             : 
    1079       42908 :     return true;
    1080             : }
    1081             : 
    1082             : //
    1083             : // Initiate shutdown for the McastTreeManager.
    1084             : //
    1085       42908 : void McastTreeManager::Shutdown() {
    1086       42908 :     CHECK_CONCURRENCY("bgp::Config");
    1087       42908 : }
    1088             : 
    1089             : //
    1090             : // Trigger deletion of the McastTreeManager and propagate the delete to any
    1091             : // dependents.
    1092             : //
    1093       42908 : void McastTreeManager::ManagedDelete() {
    1094       42908 :     deleter_->Delete();
    1095       42908 : }
    1096             : 
    1097             : //
    1098             : // Attempt to enqueue a delete for the McastTreeManager.
    1099             : //
    1100         289 : void McastTreeManager::RetryDelete() {
    1101         289 :     if (!deleter()->IsDeleted())
    1102         289 :         return;
    1103           0 :     deleter()->RetryDelete();
    1104             : }
    1105             : 
    1106             : //
    1107             : // Return the LifetimeActor for the McastTreeManager.
    1108             : //
    1109        6756 : LifetimeActor *McastTreeManager::deleter() {
    1110        6756 :     return deleter_.get();
    1111             : }
    1112             : 
    1113             : //
    1114             : // Return the LifetimeActor for the McastTreeManager.
    1115             : // Const version.
    1116             : //
    1117         190 : const LifetimeActor *McastTreeManager::deleter() const {
    1118         190 :     return deleter_.get();
    1119             : }
    1120             : 
    1121             : //
    1122             : // Return true if the McastTreeManager is deleted.
    1123             : //
    1124         190 : bool McastTreeManager::deleted() const {
    1125         190 :     return deleter_->IsDeleted();
    1126             : }
    1127             : 
    1128        3378 : ErmVpnRoute *McastTreeManager::GetGlobalTreeRootRoute(
    1129             :     const Ip4Address &source, const Ip4Address &group) const {
    1130        3378 :     const McastManagerPartition *partition = GetPartition(table_->Hash(group));
    1131        3378 :     return partition->GetGlobalTreeRootRoute(source, group);
    1132             : }
    1133             : 
    1134        7984 : void McastTreeManager::NotifyForestNode(int part_id, const Ip4Address &source,
    1135             :                                         const Ip4Address &group) {
    1136        7984 :     McastManagerPartition *partition = GetPartition(part_id);
    1137        7984 :     partition->NotifyForestNode(source, group);
    1138        7984 : }
    1139             : 
    1140         244 : bool McastTreeManager::GetForestNodePMSI(ErmVpnRoute *rt, uint32_t *label,
    1141             :         Ip4Address *address, vector<string> *encap) const {
    1142         244 :     if (!rt || !rt->IsUsable())
    1143          69 :         return false;
    1144             :     const McastManagerPartition *partition =
    1145         175 :         GetPartition(rt->get_table_partition()->index());
    1146         175 :     return partition->GetForestNodePMSI(rt, label, address, encap);
    1147             : }

Generated by: LCOV version 1.14