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