OpenSDN source code
bgp_multicast.cc
Go to the documentation of this file.
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"
17 #include "bgp/mvpn/mvpn_route.h"
19 #include "bgp/routing-instance/routing_instance_analytics_types.h"
22 
23 using std::string;
24 using std::vector;
25 using namespace boost::placeholders;
26 
28 public:
29  explicit DeleteActor(McastTreeManager *tree_manager)
30  : LifetimeActor(tree_manager->table_->routing_instance()->server()->
31  lifetime_manager()),
32  tree_manager_(tree_manager) {
33  }
34  virtual ~DeleteActor() {
35  }
36 
37  virtual bool MayDelete() const {
38  return tree_manager_->MayDelete();
39  }
40 
41  virtual void Shutdown() {
42  tree_manager_->Shutdown();
43  }
44 
45  virtual void Destroy() {
46  tree_manager_->table_->DestroyTreeManager();
47  }
48 
49 private:
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 //
64  : sg_entry_(sg_entry),
65  route_(route),
66  global_tree_route_(NULL),
67  label_(0),
68  address_(0),
69  rd_(route->GetPrefix().route_distinguisher()),
70  router_id_(route->GetPrefix().router_id()) {
71  const BgpPath *path = route->BestPath();
72  const BgpAttr *attr = path->GetAttr();
73 
76  address_ = attr->nexthop().to_v4();
77  label_block_ = attr->label_block();
78  } else {
80  const EdgeDiscovery::Edge *edge = attr->edge_discovery()->edge_list[0];
81  address_ = edge->address;
82  label_block_ = edge->label_block;
83  }
84 
85  if (path->GetAttr()->ext_community())
87 }
88 
89 //
90 // Destructor for McastForwarder. Flushes forward and reverse links to and
91 // from other McastForwarders.
92 //
95  FlushLinks();
96  ReleaseLabel();
97 }
98 
99 //
100 // Update the McastForwarder based on information in the ErmVpnRoute.
101 // Return true if something changed.
102 //
104  McastForwarder forwarder(sg_entry_, route);
105 
106  bool changed = false;
107  if (label_block_ != forwarder.label_block_) {
108  ReleaseLabel();
109  label_block_ = forwarder.label_block_;
110  changed = true;
111  }
112  if (address_ != forwarder.address_) {
113  address_ = forwarder.address_;
114  changed = true;
115  }
116  if (encap_ != forwarder.encap_) {
117  encap_ = forwarder.encap_;
118  changed = true;
119  }
120 
121  return changed;
122 }
123 
124 //
125 // Printable string for McastForwarder.
126 //
127 std::string McastForwarder::ToString() const {
129  return rd_.ToString() + " -> " + integerToString(label_);
130  } else {
131  return router_id_.to_string() + " -> " + integerToString(label_);
132  }
133 }
134 
135 //
136 // Find a link to the given McastForwarder.
137 //
139  for (McastForwarderList::iterator it = tree_links_.begin();
140  it != tree_links_.end(); ++it) {
141  if (*it == forwarder) return forwarder;
142  }
143  return NULL;
144 }
145 
146 //
147 // Add a link to the given McastForwarder.
148 //
150  assert(!FindLink(forwarder));
151  tree_links_.push_back(forwarder);
152 }
153 
154 //
155 // Remove a link to the given McastForwarder.
156 //
158  for (McastForwarderList::iterator it = tree_links_.begin();
159  it != tree_links_.end(); ++it) {
160  if (*it == forwarder) {
161  tree_links_.erase(it);
162  return;
163  }
164  }
165 }
166 
167 //
168 // Flush all links from this McastForwarder. Takes care of removing the
169 // reverse links as well.
170 //
172  for (McastForwarderList::iterator it = tree_links_.begin();
173  it != tree_links_.end(); ++it) {
174  (*it)->RemoveLink(this);
175  }
176  tree_links_.clear();
177 }
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 //
186  label_ = label_block_->AllocateLabel();
187 }
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 //
194  if (label_ != 0) {
195  label_block_->ReleaseLabel(label_);
196  label_ = 0;
197  }
198 }
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 //
207  assert(!global_tree_route_);
208 
209  // Bail if there's no label allocated.
210  if (label_ == 0)
211  return;
212 
213  // Bail if we can't build a source RD.
214  if (sg_entry_->GetSourceRd().IsZero())
215  return;
216 
217  // Construct the prefix and route key.
218  BgpTable *table = static_cast<BgpTable *>(route_->get_table());
222  ErmVpnRoute rt_key(prefix);
223 
224  // Find or create the route.
225  McastManagerPartition *partition = sg_entry_->partition();
226  DBTablePartition *tbl_partition =
227  static_cast<DBTablePartition *>(partition->GetTablePartition());
228  ErmVpnRoute *route =
229  static_cast<ErmVpnRoute *>(tbl_partition->Find(&rt_key));
230  if (!route) {
231  route = new ErmVpnRoute(prefix);
232  tbl_partition->Add(route);
233  } else {
234  route->ClearDelete();
235  }
236 
237  // Build the attributes. Need to go through the tree links to build the
238  // EdgeForwardingSpec.
239  BgpServer *server = table->routing_instance()->server();
240  BgpAttrSpec attr_spec;
241  BgpAttrNextHop nexthop(server->bgp_identifier());
242  attr_spec.push_back(&nexthop);
243  BgpAttrSourceRd source_rd(sg_entry_->GetSourceRd());
244  attr_spec.push_back(&source_rd);
245  EdgeForwardingSpec efspec;
246  for (McastForwarderList::const_iterator it = tree_links_.begin();
247  it != tree_links_.end(); ++it) {
250  edge->inbound_label = label_;
251  edge->SetOutboundIp4Address((*it)->address());
252  edge->outbound_label = (*it)->label();
253  efspec.edge_list.push_back(edge);
254  }
255  attr_spec.push_back(&efspec);
256  // Add tunnel encaps for remote nodes
257  ExtCommunitySpec ext;
258  ext.AddTunnelEncaps(encap_);
259  if (!ext.communities.empty())
260  attr_spec.push_back(&ext);
261  BgpAttrPtr attr = server->attr_db()->Locate(attr_spec);
262 
263  // Add a path with source BgpPath::Local.
264  BgpPath *path = new BgpPath(0, BgpPath::Local, attr);
265  route->InsertPath(path);
266  tbl_partition->Notify(route);
268 }
269 
270 //
271 // Delete the GlobalTreeRoute for this McastForwarder.
272 //
274  if (!global_tree_route_)
275  return;
276 
277  McastManagerPartition *partition = sg_entry_->partition();
278  DBTablePartition *tbl_partition =
279  static_cast<DBTablePartition *>(partition->GetTablePartition());
281 
282  if (!global_tree_route_->HasPaths()) {
283  tbl_partition->Delete(global_tree_route_);
284  } else {
285  tbl_partition->Notify(global_tree_route_);
286  }
287  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 //
296 
297  for (McastForwarderList::const_iterator it = tree_links_.begin();
298  it != tree_links_.end(); ++it) {
299  BgpOListElem elem((*it)->address(), (*it)->label(), (*it)->encap());
300  olist_spec->elements.push_back(elem);
301  }
302 }
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 //
310 
311  // Bail if this is not the forest node for the Local tree.
312  if (!sg_entry_->IsForestNode(this))
313  return;
314 
316  if (!route)
317  return;
318 
319  const BgpPath *path = route->BestPath();
320  if (!path)
321  return;
322  const BgpAttr *attr = path->GetAttr();
323  vector<string> encaps;
324  if (attr && attr->ext_community())
325  encaps = attr->ext_community()->GetTunnelEncap();
326 
327  // Go through each forwarding edge and add it to the list.
328  const EdgeForwarding *eforwarding = path->GetAttr()->edge_forwarding();
329  for (EdgeForwarding::EdgeList::const_iterator it =
330  eforwarding->edge_list.begin(); it != eforwarding->edge_list.end();
331  ++it) {
332  const EdgeForwarding::Edge *edge = *it;
333  if (edge->inbound_address == address_) {
334  BgpOListElem elem(edge->outbound_address, edge->outbound_label,
335  encaps);
336  olist_spec->elements.push_back(elem);
337  }
338  }
339 }
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 //
352  CHECK_CONCURRENCY("db::DBTable");
353 
355 
356  BgpOListSpec olist_spec(BgpAttribute::OList);
357  AddLocalOListElems(&olist_spec);
358  AddGlobalOListElems(&olist_spec);
359 
360  // Bail if there is no label allocated.
361  if (label_ == 0)
362  return NULL;
363 
364  BgpAttrSpec attr_spec;
365  attr_spec.push_back(&olist_spec);
366  BgpAttrPtr attr = table->server()->attr_db()->Locate(attr_spec);
367 
368  UpdateInfo *uinfo = new UpdateInfo;
369  uinfo->roattr = RibOutAttr(table, route_, attr.get(), label_, true, true);
370  if (route_ && sg_entry_->IsForestNode(this) &&
373  }
374  return uinfo;
375 }
376 
377 //
378 // Constructor for McastSGEntry.
379 //
381  Ip4Address group, Ip4Address source)
382  : partition_(partition),
383  group_(group),
384  source_(source),
385  forest_node_(NULL),
386  local_tree_route_(NULL),
387  tree_result_route_(NULL),
388  on_work_queue_(false) {
389  for (int level = McastTreeManager::LevelFirst;
390  level < McastTreeManager::LevelCount; ++level) {
391  ForwarderSet *forwarders = new ForwarderSet;
392  forwarder_sets_.push_back(forwarders);
393  update_needed_.push_back(false);
394  }
395 }
396 
397 //
398 // Destructor for McastSGEntry.
399 //
402 }
403 
404 //
405 // Printable string for McastSGEntry.
406 //
407 std::string McastSGEntry::ToString() const {
408  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 //
416  uint8_t level = forwarder->level();
417  forwarder_sets_[level]->insert(forwarder);
418  update_needed_[level] = true;
419  partition_->EnqueueSGEntry(this);
420 }
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 //
430  uint8_t level = forwarder->level();
431  update_needed_[level] = true;
432  partition_->EnqueueSGEntry(this);
433 }
434 
435 //
436 // Delete the given McastForwarder from this McastSGEntry and trigger update
437 // of the distribution tree.
438 //
440  if (forwarder == forest_node_)
441  forest_node_ = NULL;
442  uint8_t level = forwarder->level();
443  forwarder_sets_[level]->erase(forwarder);
444  update_needed_[level] = true;
445  partition_->EnqueueSGEntry(this);
446 }
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 //
456  if (!forest_node_)
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 //
475  assert(!forest_node_);
476  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  uint8_t level = McastTreeManager::LevelNative;
482  ForwarderSet *forwarders = forwarder_sets_[level];
483  for (ForwarderSet::reverse_iterator rit = forwarders->rbegin();
484  rit != forwarders->rend(); ++rit) {
485  McastForwarder *forwarder = *rit;
486  if (forwarder->label()) {
487  forest_node_ = forwarder;
488  break;
489  }
490  }
491 
492  // Bail if we couldn't designate a forest node.
493  if (!forest_node_)
494  return;
495 
496  // Construct the prefix and route key.
497  BgpServer *server = partition_->server();
498  Ip4Address router_id(server->bgp_identifier());
501  ErmVpnRoute rt_key(prefix);
502 
503  // Find or create the route.
504  DBTablePartition *tbl_partition =
505  static_cast<DBTablePartition *>(partition_->GetTablePartition());
506  ErmVpnRoute *route =
507  static_cast<ErmVpnRoute *>(tbl_partition->Find(&rt_key));
508  if (!route) {
509  route = new ErmVpnRoute(prefix);
510  tbl_partition->Add(route);
511  } else {
512  route->ClearDelete();
513  }
514 
515  // Build the attributes.
516  BgpAttrSpec attr_spec;
517  BgpAttrNextHop nexthop(server->bgp_identifier());
518  attr_spec.push_back(&nexthop);
519  BgpAttrSourceRd source_rd(GetSourceRd());
520  attr_spec.push_back(&source_rd);
521  EdgeDiscoverySpec edspec;
522  for (int idx = 1; idx <= McastTreeManager::kDegree - 1; ++idx) {
526  edspec.edge_list.push_back(edge);
527  }
528  attr_spec.push_back(&edspec);
529  // Add tunnel encaps for remote nodes
530  ExtCommunitySpec ext;
532  if (!ext.communities.empty())
533  attr_spec.push_back(&ext);
534  BgpAttrPtr attr = server->attr_db()->Locate(attr_spec);
535 
536  // Add a path with source BgpPath::Local.
537  BgpPath *path = new BgpPath(0, BgpPath::Local, attr);
538  route->InsertPath(path);
539  tbl_partition->Notify(route);
540  local_tree_route_ = route;
541 }
542 
543 //
544 // Delete the LocalTreeRoute for this McastSGEntry.
545 //
547  if (!local_tree_route_)
548  return;
549 
550  forest_node_ = NULL;
551  DBTablePartition *tbl_partition =
552  static_cast<DBTablePartition *>(partition_->GetTablePartition());
554  if (!local_tree_route_->HasPaths()) {
555  tbl_partition->Delete(local_tree_route_);
556  } else {
557  tbl_partition->Notify(local_tree_route_);
558  }
559  local_tree_route_ = NULL;
560 }
561 
562 //
563 // Update the LocalTreeRoute for this McastSGEntry if RouterId has changed.
564 //
566  if (!local_tree_route_)
567  return;
568 
569  // Bail if the RouterId hasn't changed.
570  const BgpServer *server = partition_->server();
572  if (router_id.to_ulong() == server->bgp_identifier())
573  return;
574 
575  // Add and delete the route.
578 }
579 
580 //
581 // Update relevant [Local|Global]TreeRoutes for the McastSGEntry.
582 //
583 void McastSGEntry::UpdateRoutes(uint8_t level) {
584  if (level == McastTreeManager::LevelNative) {
587  } else {
588  ForwarderSet *forwarders = forwarder_sets_[level];
589  for (ForwarderSet::iterator it = forwarders->begin();
590  it != forwarders->end(); ++it) {
591  (*it)->DeleteGlobalTreeRoute();
592  (*it)->AddGlobalTreeRoute();
593  }
594  }
595 }
596 
599  return NULL;
601  assert(!forwarders->empty());
602  ForwarderSet::const_iterator it = forwarders->begin();
603  return (*it)->global_tree_route();
604 }
605 
606 //
607 // Implement tree builder election.
608 //
609 bool McastSGEntry::IsTreeBuilder(uint8_t level) const {
610  if (level == McastTreeManager::LevelNative)
611  return true;
612 
613  const ForwarderSet *forwarders = forwarder_sets_[level];
614  ForwarderSet::const_iterator it = forwarders->begin();
615  if (it == forwarders->end())
616  return false;
617 
618  Ip4Address router_id(partition_->server()->bgp_identifier());
619  if ((*it)->router_id() != router_id)
620  return false;
621 
622  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 void McastSGEntry::UpdateTree(uint8_t level) {
636  CHECK_CONCURRENCY("db::DBTable");
637 
638  if (!update_needed_[level])
639  return;
640  update_needed_[level] = false;
641 
642  int degree;
643  if (level == McastTreeManager::LevelNative) {
644  degree = McastTreeManager::kDegree;
645  } else {
646  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  ForwarderSet *forwarders = forwarder_sets_[level];
653  for (ForwarderSet::iterator it = forwarders->begin();
654  it != forwarders->end(); ++it) {
655  (*it)->FlushLinks();
656  (*it)->ReleaseLabel();
657  partition_->GetTablePartition()->Notify((*it)->route());
658  }
659 
660  // Bail if we're not the tree builder.
661  if (!IsTreeBuilder(level)) {
662  UpdateRoutes(level);
663  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  McastForwarderList vec;
670  vec.reserve(forwarders->size());
671  for (ForwarderSet::iterator it = forwarders->begin();
672  it != forwarders->end(); ++it) {
673  McastForwarder *forwarder = *it;
674  forwarder->AllocateLabel();
675  if (!forwarder->label())
676  continue;
677  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  for (McastForwarderList::iterator it = vec.begin(); it != vec.end(); ++it) {
684  int idx = it - vec.begin();
685  if (idx == 0)
686  continue;
687 
688  int parent_idx = (idx - 1) / degree;
689  McastForwarderList::iterator parent_it = vec.begin() + parent_idx;
690  assert(parent_it != vec.end());
691  McastForwarder *forwarder = *it;
692  McastForwarder *parent_forwarder = *parent_it;
693  forwarder->AddLink(parent_forwarder);
694  parent_forwarder->AddLink(forwarder);
695  }
696 
697  // Update [Local|Global]TreeRoutes.
698  UpdateRoutes(level);
699 }
700 
701 //
702 // Update distribution trees for both levels.
703 //
705  for (uint8_t level = McastTreeManager::LevelFirst;
706  level < McastTreeManager::LevelCount; ++level) {
707  UpdateTree(level);
708  }
709 }
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 //
717  if (!forest_node_)
718  return;
720 }
721 
722 bool McastSGEntry::GetForestNodePMSI(uint32_t *label, Ip4Address *address,
723  vector<string> *tunnel_encap) const {
724  if (!forest_node_)
725  return false;
726  *label = forest_node_->label();
727  *address = forest_node_->address();
728  *tunnel_encap = forest_node_->encap();
729  return true;
730 }
731 
733  return (forwarder == forest_node_);
734 }
735 
736 bool McastSGEntry::empty() const {
738  return false;
740  return false;
742  return false;
743  return true;
744 }
745 
746 //
747 // Constructor for McastManagerPartition.
748 //
750  size_t part_id)
751  : tree_manager_(tree_manager),
752  part_id_(part_id),
753  update_count_(0),
754  work_queue_(TaskScheduler::GetInstance()->GetTaskId("db::DBTable"),
755  part_id_,
756  boost::bind(&McastManagerPartition::ProcessSGEntry, this, _1)) {
757 }
758 
759 //
760 // Destructor for McastManagerPartition.
761 //
764 }
765 
766 // Find the McastSGEntry for the given group and source.
768  const Ip4Address &group, const Ip4Address &source) {
769  return const_cast<McastSGEntry *>(
770  static_cast<const McastManagerPartition *>(this)->FindSGEntry(group,
771  source));
772 }
773 
774 //
775 // Find the McastSGEntry for the given group and source.
776 //
778  const Ip4Address &group, const Ip4Address &source) const {
779  McastSGEntry temp_sg_entry(const_cast<McastManagerPartition *>(this),
780  group, source);
781  SGList::const_iterator it = sg_list_.find(&temp_sg_entry);
782  return (it != sg_list_.end() ? *it : NULL);
783 }
784 
785 //
786 // Find or create the McastSGEntry for the given group and source.
787 //
789  Ip4Address group, Ip4Address source) {
790  McastSGEntry *sg_entry = FindSGEntry(group, source);
791  if (!sg_entry) {
792  sg_entry = new McastSGEntry(this, group, source);
793  sg_list_.insert(sg_entry);
794  }
795  return sg_entry;
796 }
797 
799  const Ip4Address &source, const Ip4Address &group) const {
800  const McastSGEntry *sg = FindSGEntry(group, source);
801  return sg ? sg->GetGlobalTreeRootRoute() : NULL;
802 }
803 
805  const Ip4Address &source, const Ip4Address &group) {
806  McastSGEntry *sg = FindSGEntry(group, source);
807  if (sg)
808  sg->NotifyForestNode();
809 }
810 
812  Ip4Address *address, vector<string> *encap) const {
813  const McastSGEntry *sg = FindSGEntry(rt->GetPrefix().group(),
814  rt->GetPrefix().source());
815  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 //
822  if (sg_entry->on_work_queue())
823  return;
824  work_queue_.Enqueue(sg_entry);
825  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 //
833  CHECK_CONCURRENCY("db::DBTable");
834 
835  sg_entry->clear_on_work_queue();
836  sg_entry->UpdateTree();
837  update_count_++;
838 
839  if (sg_entry->empty()) {
840  sg_list_.erase(sg_entry);
841  delete sg_entry;
842  }
843 
844  if (sg_list_.empty())
846 
847  return true;
848 }
849 
850 //
851 // Get the DBTablePartBase for the ErmVpnTable for our partition id.
852 //
855 }
856 
858  return tree_manager_->table()->routing_instance();
859 }
860 
862  return tree_manager_->table()->server();
863 }
864 
866  return tree_manager_->table()->server();
867 }
868 
869 //
870 // Constructor for McastTreeManager.
871 //
873  : table_(table),
874  listener_id_(DBTable::kInvalidId),
875  table_delete_ref_(this, table->deleter()) {
876  deleter_.reset(new DeleteActor(this));
877 }
878 
879 //
880 // Destructor for McastTreeManager.
881 //
883 }
884 
885 //
886 // Initialize the McastTreeManager. We allocate the McastManagerPartitions
887 // and register a DBListener for the ErmVpnTable.
888 //
890  AllocPartitions();
892  boost::bind(&McastTreeManager::RouteListener, this, _1, _2),
893  "McastTreeManager");
894 }
895 
896 //
897 // Terminate the McastTreeManager. We free the McastManagerPartitions
898 // and unregister from the ErmVpnTable.
899 //
902  FreePartitions();
903 }
904 
905 //
906 // Allocate the McastManagerPartitions.
907 //
909  for (int part_id = 0; part_id < table_->PartitionCount(); part_id++) {
910  partitions_.push_back(new McastManagerPartition(this, part_id));
911  }
912 }
913 
914 //
915 // Free the McastManagerPartitions.
916 //
918  for (size_t part_id = 0; part_id < partitions_.size(); part_id++) {
919  delete partitions_[part_id];
920  }
921  partitions_.clear();
922 }
923 
925  return partitions_[part_id];
926 }
927 
929  return partitions_[part_id];
930 }
931 
932 //
933 // Get the DBTablePartBase for the ErmVpnTable for given partition id.
934 //
936  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 //
944  CHECK_CONCURRENCY("db::DBTable");
945 
946  DBState *dbstate = route->GetState(table_, listener_id_);
947  McastForwarder *forwarder = dynamic_cast<McastForwarder *>(dbstate);
948 
949  if (!forwarder)
950  return NULL;
951 
952  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 //
964  ErmVpnRoute *route) {
965  CHECK_CONCURRENCY("db::DBTable");
966 
967  DBState *dbstate = route->GetState(table_, listener_id_);
968  if (!dbstate) {
969  // We have no previous DBState for this route.
970  // Bail if the route is not valid.
971  if (!route->IsValid())
972  return;
973 
974  // Create a new McastForwarder and associate it with the route.
975  McastSGEntry *sg_entry = partition->LocateSGEntry(
976  route->GetPrefix().group(), route->GetPrefix().source());
977  McastForwarder *forwarder = new McastForwarder(sg_entry, route);
978  sg_entry->AddForwarder(forwarder);
979  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  if (route->GetPrefix().type() == ErmVpnPrefix::LocalTreeRoute)
987  sg_entry->UpdateLocalTreeRoute();
988  } else {
989  McastSGEntry *sg_entry = partition->FindSGEntry(
990  route->GetPrefix().group(), route->GetPrefix().source());
991  assert(sg_entry);
992  McastForwarder *forwarder = dynamic_cast<McastForwarder *>(dbstate);
993  assert(forwarder);
994 
995  if (!route->IsValid()) {
996  // Delete the McastForwarder associated with the route.
997  route->ClearState(table_, listener_id_);
998  sg_entry->DeleteForwarder(forwarder);
999  delete forwarder;
1000  } else if (forwarder->Update(route)) {
1001  // Trigger update of the distribution tree.
1002  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 //
1013  ErmVpnRoute *route) {
1014  CHECK_CONCURRENCY("db::DBTable");
1015 
1016  DBState *dbstate = route->GetState(table_, listener_id_);
1017  if (!dbstate) {
1018  // We have no previous DBState for this route.
1019  // Bail if the route is not valid.
1020  if (!route->IsValid())
1021  return;
1022 
1023  // Ignore GlobalTreeRoute if it's not applicable to this control-node.
1024  BgpServer *server = table_->routing_instance()->server();
1025  if (route->GetPrefix().router_id().to_ulong() !=
1026  server->bgp_identifier())
1027  return;
1028 
1029  McastSGEntry *sg_entry = partition->LocateSGEntry(
1030  route->GetPrefix().group(), route->GetPrefix().source());
1031  route->SetState(table_, listener_id_, sg_entry);
1032  sg_entry->set_tree_result_route(route);
1033  sg_entry->NotifyForestNode();
1034  } else {
1035  McastSGEntry *sg_entry = dynamic_cast<McastSGEntry *>(dbstate);
1036  assert(sg_entry);
1037 
1038  if (!route->IsValid()) {
1039  sg_entry->clear_tree_result_route();
1040  route->ClearState(table_, listener_id_);
1041  partition->EnqueueSGEntry(sg_entry);
1042  }
1043  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 //
1053  DBTablePartBase *tpart, DBEntryBase *db_entry) {
1054  CHECK_CONCURRENCY("db::DBTable");
1055 
1056  McastManagerPartition *partition = partitions_[tpart->index()];
1057  ErmVpnRoute *route = dynamic_cast<ErmVpnRoute *>(db_entry);
1058  if (route->GetPrefix().type() == ErmVpnPrefix::GlobalTreeRoute) {
1059  TreeResultListener(partition, route);
1060  } else {
1061  TreeNodeListener(partition, route);
1062  }
1063 }
1064 
1065 
1066 //
1067 // Check if the McastTreeManager can be deleted. This can happen only if all
1068 // the McastManagerPartitions are empty.
1069 //
1071  CHECK_CONCURRENCY("bgp::Config");
1072 
1073  for (PartitionList::const_iterator it = partitions_.begin();
1074  it != partitions_.end(); ++it) {
1075  if (!(*it)->empty())
1076  return false;
1077  }
1078 
1079  return true;
1080 }
1081 
1082 //
1083 // Initiate shutdown for the McastTreeManager.
1084 //
1086  CHECK_CONCURRENCY("bgp::Config");
1087 }
1088 
1089 //
1090 // Trigger deletion of the McastTreeManager and propagate the delete to any
1091 // dependents.
1092 //
1094  deleter_->Delete();
1095 }
1096 
1097 //
1098 // Attempt to enqueue a delete for the McastTreeManager.
1099 //
1101  if (!deleter()->IsDeleted())
1102  return;
1103  deleter()->RetryDelete();
1104 }
1105 
1106 //
1107 // Return the LifetimeActor for the McastTreeManager.
1108 //
1110  return deleter_.get();
1111 }
1112 
1113 //
1114 // Return the LifetimeActor for the McastTreeManager.
1115 // Const version.
1116 //
1118  return deleter_.get();
1119 }
1120 
1121 //
1122 // Return true if the McastTreeManager is deleted.
1123 //
1125  return deleter_->IsDeleted();
1126 }
1127 
1129  const Ip4Address &source, const Ip4Address &group) const {
1130  const McastManagerPartition *partition = GetPartition(table_->Hash(group));
1131  return partition->GetGlobalTreeRootRoute(source, group);
1132 }
1133 
1134 void McastTreeManager::NotifyForestNode(int part_id, const Ip4Address &source,
1135  const Ip4Address &group) {
1136  McastManagerPartition *partition = GetPartition(part_id);
1137  partition->NotifyForestNode(source, group);
1138 }
1139 
1141  Ip4Address *address, vector<string> *encap) const {
1142  if (!rt || !rt->IsUsable())
1143  return false;
1144  const McastManagerPartition *partition =
1146  return partition->GetForestNodePMSI(rt, label, address, encap);
1147 }
boost::asio::ip::address_v4 Ip4Address
Definition: address.h:14
std::vector< BgpAttribute * > BgpAttrSpec
Definition: bgp_attr.h:822
boost::intrusive_ptr< const BgpAttr > BgpAttrPtr
Definition: bgp_attr.h:997
std::vector< McastForwarder * > McastForwarderList
Definition: bgp_multicast.h:32
const EdgeForwarding * edge_forwarding() const
Definition: bgp_attr.h:926
LabelBlockPtr label_block() const
Definition: bgp_attr.h:929
const IpAddress & nexthop() const
Definition: bgp_attr.h:888
const EdgeDiscovery * edge_discovery() const
Definition: bgp_attr.h:923
const ExtCommunity * ext_community() const
Definition: bgp_attr.h:917
TypePtr Locate(Type *attr)
@ Local
Definition: bgp_path.h:43
const BgpAttr * GetAttr() const
Definition: bgp_path.h:87
bool RemovePath(BgpPath::PathSource src, const IPeer *peer=NULL, uint32_t path_id=0)
Definition: bgp_route.cc:263
const BgpPath * BestPath() const
Definition: bgp_route.cc:47
bool IsUsable() const
Definition: bgp_route.cc:325
void InsertPath(BgpPath *path)
Definition: bgp_route.cc:61
bool HasPaths() const
Definition: bgp_route.h:28
uint32_t bgp_identifier() const
Definition: bgp_server.h:215
BgpAttrDB * attr_db()
Definition: bgp_server.h:187
BgpServer * server()
Definition: bgp_table.cc:87
RoutingInstance * routing_instance()
Definition: bgp_table.h:147
DBState * GetState(DBTableBase *tbl_base, ListenerId listener) const
Definition: db_entry.cc:37
DBTableBase * get_table() const
Definition: db_entry.cc:119
void ClearState(DBTableBase *tbl_base, ListenerId listener)
Definition: db_entry.cc:73
void ClearDelete()
Definition: db_entry.h:47
DBTablePartBase * get_table_partition() const
Definition: db_entry.cc:115
void SetState(DBTableBase *tbl_base, ListenerId listener, DBState *state)
Definition: db_entry.cc:22
ListenerId Register(ChangeCallback callback, const std::string &name="unspecified")
Definition: db_table.cc:207
void Unregister(ListenerId listener)
Definition: db_table.cc:212
void Notify(DBEntryBase *entry)
void Delete(DBEntryBase *)
virtual void Add(DBEntry *entry)
DBEntry * Find(const DBEntry *entry)
virtual DBTablePartBase * GetTablePartition(const DBRequestKey *key)
Definition: db_table.cc:462
EdgeList edge_list
Definition: bgp_attr.h:493
EdgeList edge_list
Definition: bgp_attr.h:600
Ip4Address source() const
Definition: ermvpn_route.h:60
uint8_t type() const
Definition: ermvpn_route.h:56
Ip4Address router_id() const
Definition: ermvpn_route.h:58
const RouteDistinguisher & route_distinguisher() const
Definition: ermvpn_route.h:57
Ip4Address group() const
Definition: ermvpn_route.h:59
virtual bool IsValid() const
const ErmVpnPrefix & GetPrefix() const
Definition: ermvpn_route.h:81
void GetMvpnSourceAddress(ErmVpnRoute *ermvpn_route, Ip4Address *address) const
virtual int PartitionCount() const
Definition: ermvpn_table.h:44
virtual size_t Hash(const DBEntry *entry) const
Definition: ermvpn_table.cc:41
void AddTunnelEncaps(std::vector< std::string > encaps)
Definition: community.cc:214
std::vector< uint64_t > communities
Definition: community.h:145
std::vector< std::string > GetTunnelEncap() const
Definition: community.cc:596
void RetryDelete()
Definition: lifetime.cc:73
void RemoveLink(McastForwarder *forwarder)
void AddGlobalOListElems(BgpOListSpec *olist_spec)
RouteDistinguisher rd_
ErmVpnRoute * route_
void AddGlobalTreeRoute()
void DeleteGlobalTreeRoute()
McastForwarder(McastSGEntry *sg_entry, ErmVpnRoute *route)
void AddLink(McastForwarder *forwarder)
LabelBlockPtr label_block_
ErmVpnRoute * route()
Ip4Address address() const
Ip4Address address_
McastForwarder * FindLink(McastForwarder *forwarder)
uint8_t level() const
std::vector< std::string > encap() const
ErmVpnRoute * global_tree_route_
McastSGEntry * sg_entry_
McastForwarderList tree_links_
void AddLocalOListElems(BgpOListSpec *olist_spec)
std::vector< std::string > encap_
uint32_t label() const
Ip4Address router_id_
bool Update(ErmVpnRoute *route)
std::string ToString() const
UpdateInfo * GetUpdateInfo(ErmVpnTable *table)
WorkQueue< McastSGEntry * > work_queue_
McastManagerPartition(McastTreeManager *tree_manager, size_t part_id)
bool GetForestNodePMSI(ErmVpnRoute *rt, uint32_t *label, Ip4Address *address, std::vector< std::string > *encap) const
ErmVpnRoute * GetGlobalTreeRootRoute(const Ip4Address &source, const Ip4Address &group) const
void EnqueueSGEntry(McastSGEntry *sg_entry)
bool ProcessSGEntry(McastSGEntry *sg_entry)
McastSGEntry * LocateSGEntry(Ip4Address group, Ip4Address source)
DBTablePartBase * GetTablePartition()
void NotifyForestNode(const Ip4Address &source, const Ip4Address &group)
const RoutingInstance * routing_instance() const
McastSGEntry * FindSGEntry(const Ip4Address &group, const Ip4Address &source)
McastTreeManager * tree_manager_
void AddForwarder(McastForwarder *forwarder)
void NotifyForestNode()
std::vector< ForwarderSet * > forwarder_sets_
McastForwarder * forest_node_
Ip4Address group_
void set_tree_result_route(ErmVpnRoute *route)
void set_on_work_queue()
Ip4Address source() const
void AddLocalTreeRoute()
std::string ToString() const
std::set< McastForwarder *, McastForwarderCompare > ForwarderSet
McastSGEntry(McastManagerPartition *partition, Ip4Address group, Ip4Address source)
void clear_on_work_queue()
ErmVpnRoute * local_tree_route_
Ip4Address source_
std::vector< bool > update_needed_
void UpdateLocalTreeRoute()
bool IsForestNode(McastForwarder *forwarder)
void DeleteLocalTreeRoute()
bool GetForestNodePMSI(uint32_t *label, Ip4Address *address, std::vector< std::string > *encap) const
void clear_tree_result_route()
bool on_work_queue()
bool empty() const
void DeleteForwarder(McastForwarder *forwarder)
McastManagerPartition * partition_
Ip4Address group() const
const ErmVpnRoute * tree_result_route() const
void UpdateRoutes(uint8_t level)
const RouteDistinguisher & GetSourceRd() const
bool IsTreeBuilder(uint8_t level) const
void ChangeForwarder(McastForwarder *forwarder)
ErmVpnRoute * tree_result_route_
McastManagerPartition * partition()
ErmVpnRoute * GetGlobalTreeRootRoute() const
virtual bool MayDelete() const
McastTreeManager * tree_manager_
DeleteActor(McastTreeManager *tree_manager)
McastManagerPartition * GetPartition(int part_id)
ErmVpnTable * table()
void RouteListener(DBTablePartBase *tpart, DBEntryBase *db_entry)
DBTablePartBase * GetTablePartition(size_t part_id)
void TreeResultListener(McastManagerPartition *partition, ErmVpnRoute *route)
PartitionList partitions_
LifetimeActor * deleter()
void NotifyForestNode(int part_id, const Ip4Address &source, const Ip4Address &group)
void TreeNodeListener(McastManagerPartition *partition, ErmVpnRoute *route)
bool deleted() const
ErmVpnTable * table_
virtual bool GetForestNodePMSI(ErmVpnRoute *rt, uint32_t *label, Ip4Address *address, std::vector< std::string > *encap) const
static const int kDegree
McastTreeManager(ErmVpnTable *table)
virtual UpdateInfo * GetUpdateInfo(ErmVpnRoute *route)
virtual void Initialize()
virtual ErmVpnRoute * GetGlobalTreeRootRoute(const Ip4Address &source, const Ip4Address &group) const
virtual ~McastTreeManager()
boost::scoped_ptr< DeleteActor > deleter_
virtual void Terminate()
bool MayDelete() const
const Ip4Address & source_address() const
Definition: bgp_ribout.h:121
bool IsZero() const
Definition: rd.h:43
static RouteDistinguisher kZeroRd
Definition: rd.h:14
std::string ToString() const
Definition: rd.cc:56
BgpServer * server()
The TaskScheduler keeps track of what tasks are currently schedulable. When a task is enqueued it is ...
Definition: task.h:304
bool Enqueue(QueueEntryT entry)
Definition: queue_task.h:248
void Shutdown(bool delete_entries=true)
Definition: queue_task.h:152
static const std::string integerToString(const NumberType &num)
Definition: string_util.h:19
Elements elements
Definition: bgp_attr.h:694
void SetLabels(uint32_t first_label, uint32_t last_label)
Definition: bgp_attr.cc:462
void SetIp4Address(Ip4Address addr)
Definition: bgp_attr.cc:450
EdgeList edge_list
Definition: bgp_attr.h:459
LabelBlockPtr label_block
Definition: bgp_attr.h:481
Ip4Address address
Definition: bgp_attr.h:480
void SetOutboundIp4Address(Ip4Address addr)
Definition: bgp_attr.cc:609
void SetInboundIp4Address(Ip4Address addr)
Definition: bgp_attr.cc:602
EdgeList edge_list
Definition: bgp_attr.h:566
uint32_t outbound_label
Definition: bgp_attr.h:589
Ip4Address outbound_address
Definition: bgp_attr.h:588
Ip4Address inbound_address
Definition: bgp_attr.h:588
RibOutAttr roattr
Definition: bgp_update.h:100
#define CHECK_CONCURRENCY(...)
void STLDeleteValues(Container *container)
Definition: util.h:101