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