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/routing-instance/routepath_replicator.h"
7 :
8 : #include <boost/foreach.hpp>
9 :
10 : #include <utility>
11 :
12 : #include "base/set_util.h"
13 : #include "base/task_annotations.h"
14 : #include "base/task_trigger.h"
15 : #include "bgp/bgp_config.h"
16 : #include "bgp/bgp_log.h"
17 : #include "bgp/bgp_route.h"
18 : #include "bgp/bgp_server.h"
19 : #include "bgp/origin-vn/origin_vn.h"
20 : #include "bgp/routing-instance/path_resolver.h"
21 : #include "bgp/routing-instance/routing_instance.h"
22 : #include "bgp/routing-instance/rtarget_group_mgr.h"
23 : #include "bgp/routing-instance/routing_instance_analytics_types.h"
24 :
25 : using std::ostringstream;
26 : using std::make_pair;
27 : using std::pair;
28 : using std::string;
29 : using std::vector;
30 :
31 : //
32 : // RoutePathReplication trace macro. Optionally logs the server name as well for
33 : // easier debugging in multi server unit tests
34 : //
35 : #define RPR_TRACE(obj, ...) \
36 : do { \
37 : if (LoggingDisabled()) break; \
38 : bgp_log_test::LogServerName(server()); \
39 : Rpr##obj##Log::Send("RoutingInstance", \
40 : SandeshLevel::SYS_DEBUG, __FILE__, __LINE__, __VA_ARGS__); \
41 : Rpr##obj::TraceMsg(trace_buf_, __FILE__, __LINE__, __VA_ARGS__); \
42 : } while (false)
43 :
44 : #define RPR_TRACE_ONLY(obj, ...) \
45 : do { \
46 : if (LoggingDisabled()) break; \
47 : bgp_log_test::LogServerName(server()); \
48 : Rpr##obj::TraceMsg(trace_buf_, __FILE__, __LINE__, __VA_ARGS__); \
49 : } while (false)
50 :
51 : class TableState::DeleteActor : public LifetimeActor {
52 : public:
53 254220 : explicit DeleteActor(TableState *ts)
54 254220 : : LifetimeActor(ts->replicator()->server()->lifetime_manager()),
55 254220 : ts_(ts) {
56 254222 : }
57 :
58 254307 : virtual bool MayDelete() const {
59 254307 : return ts_->MayDelete();
60 : }
61 :
62 254225 : virtual void Shutdown() {
63 254225 : }
64 :
65 254225 : virtual void Destroy() {
66 254225 : ts_->replicator()->DeleteTableState(ts_->table());
67 254225 : }
68 :
69 : private:
70 : TableState *ts_;
71 : };
72 :
73 254206 : TableState::TableState(RoutePathReplicator *replicator, BgpTable *table)
74 254206 : : replicator_(replicator),
75 254206 : table_(table),
76 254206 : listener_id_(DBTableBase::kInvalidId),
77 254206 : deleter_(new DeleteActor(this)),
78 254220 : table_delete_ref_(this, table->deleter()),
79 254217 : export_target_gen_(0) {
80 254207 : assert(table->deleter() != NULL);
81 254204 : }
82 :
83 254225 : TableState::~TableState() {
84 254225 : if (walk_ref() != NULL) {
85 22603 : table()->ReleaseWalker(walk_ref());
86 : }
87 254225 : }
88 :
89 254225 : void TableState::ManagedDelete() {
90 254225 : deleter()->Delete();
91 254225 : }
92 :
93 0 : bool TableState::deleted() const {
94 0 : return deleter()->IsDeleted();
95 : }
96 :
97 910312 : LifetimeActor *TableState::deleter() {
98 910312 : return deleter_.get();
99 : }
100 :
101 0 : const LifetimeActor *TableState::deleter() const {
102 0 : return deleter_.get();
103 : }
104 :
105 254307 : bool TableState::MayDelete() const {
106 531235 : if (list_.empty() && !route_count() &&
107 276928 : ((walk_ref() == NULL) || !walk_ref()->walk_is_active()))
108 254225 : return true;
109 82 : return false;
110 : }
111 :
112 637044 : void TableState::RetryDelete() {
113 637044 : if (!deleter()->IsDeleted())
114 617974 : return;
115 19051 : deleter()->RetryDelete();
116 : }
117 :
118 668221 : void TableState::AddGroup(RtGroup *group) {
119 668221 : list_.insert(group);
120 668401 : }
121 :
122 668440 : void TableState::RemoveGroup(RtGroup *group) {
123 668440 : list_.erase(group);
124 668439 : }
125 :
126 429494 : const RtGroup *TableState::FindGroup(RtGroup *group) const {
127 429494 : GroupList::const_iterator it = list_.find(group);
128 429448 : return (it != list_.end() ? *it : NULL);
129 : }
130 :
131 254318 : uint32_t TableState::route_count() const {
132 254318 : return table_->GetDBStateCount(listener_id());
133 : }
134 :
135 1057887 : RtReplicated::RtReplicated(RoutePathReplicator *replicator)
136 1057879 : : replicator_(replicator),
137 1057887 : export_target_gen_(0) {
138 1057845 : }
139 :
140 517638 : void RtReplicated::AddRouteInfo(BgpTable *table, BgpRoute *rt,
141 : ReplicatedRtPathList::const_iterator it) {
142 517638 : pair<ReplicatedRtPathList::iterator, bool> result;
143 517639 : result = replicate_list_.insert(*it);
144 517676 : assert(result.second);
145 517676 : }
146 :
147 517114 : void RtReplicated::DeleteRouteInfo(BgpTable *table, BgpRoute *rt,
148 : ReplicatedRtPathList::const_iterator it) {
149 517114 : replicator_->DeleteSecondaryPath(table, rt, *it);
150 517546 : replicate_list_.erase(it);
151 517558 : }
152 :
153 : //
154 : // Return the list of secondary table names for the given primary path.
155 : // We go through all SecondaryRouteInfos and skip the ones that don't
156 : // match the primary path.
157 : //
158 283 : vector<string> RtReplicated::GetTableNameList(const BgpPath *path) const {
159 283 : vector<string> table_list;
160 1373 : BOOST_FOREACH(const SecondaryRouteInfo &rinfo, replicate_list_) {
161 545 : if (rinfo.peer_ != path->GetPeer())
162 0 : continue;
163 545 : if (rinfo.path_id_ != path->GetPathId())
164 16 : continue;
165 529 : if (rinfo.src_ != path->GetSource())
166 0 : continue;
167 529 : table_list.push_back(rinfo.table_->name());
168 : }
169 283 : return table_list;
170 0 : }
171 :
172 48715 : RoutePathReplicator::RoutePathReplicator(BgpServer *server,
173 48715 : Address::Family family)
174 48715 : : server_(server),
175 48715 : family_(family),
176 48715 : vpn_table_(NULL),
177 48715 : trace_buf_(SandeshTraceBufferCreate("RoutePathReplicator", 500)) {
178 48715 : }
179 :
180 97430 : RoutePathReplicator::~RoutePathReplicator() {
181 48715 : assert(table_state_list_.empty());
182 97430 : }
183 :
184 41140 : void RoutePathReplicator::Initialize() {
185 41140 : assert(!vpn_table_);
186 41140 : RoutingInstanceMgr *mgr = server_->routing_instance_mgr();
187 41140 : assert(mgr);
188 41140 : RoutingInstance *master = mgr->GetDefaultRoutingInstance();
189 41140 : assert(master);
190 41140 : vpn_table_ = master->GetTable(family_);
191 41140 : assert(vpn_table_);
192 41140 : assert(AddTableState(vpn_table_));
193 41140 : }
194 :
195 709527 : TableState *RoutePathReplicator::AddTableState(BgpTable *table,
196 : RtGroup *group) {
197 709527 : assert(table->IsVpnTable() || group);
198 :
199 709508 : TableStateList::iterator loc = table_state_list_.find(table);
200 709228 : if (loc == table_state_list_.end()) {
201 254213 : TableState *ts = new TableState(this, table);
202 254204 : DBTableBase::ListenerId id = table->Register(
203 : boost::bind(&RoutePathReplicator::RouteListener, this, ts, _1, _2),
204 : "RoutePathReplicator");
205 254196 : ts->set_listener_id(id);
206 254193 : if (group)
207 213053 : ts->AddGroup(group);
208 254216 : table_state_list_.insert(make_pair(table, ts));
209 254212 : RPR_TRACE(RegTable, table->name());
210 254219 : return ts;
211 : } else {
212 455178 : assert(group);
213 455178 : TableState *ts = loc->second;
214 455178 : ts->AddGroup(group);
215 455328 : return ts;
216 : }
217 : }
218 :
219 668439 : void RoutePathReplicator::RemoveTableState(BgpTable *table, RtGroup *group) {
220 668439 : TableState *ts = FindTableState(table);
221 668440 : assert(ts);
222 668440 : ts->RemoveGroup(group);
223 668439 : }
224 :
225 254225 : void RoutePathReplicator::DeleteTableState(BgpTable *table) {
226 254225 : TableState *ts = FindTableState(table);
227 254225 : assert(ts);
228 254225 : RPR_TRACE(UnregTable, table->name());
229 254225 : table->Unregister(ts->listener_id());
230 254225 : table_state_list_.erase(table);
231 254225 : delete ts;
232 254225 : }
233 :
234 2097128 : TableState *RoutePathReplicator::FindTableState(BgpTable *table) {
235 2097128 : TableStateList::iterator loc = table_state_list_.find(table);
236 2097028 : return (loc != table_state_list_.end() ? loc->second : NULL);
237 : }
238 :
239 5651 : const TableState *RoutePathReplicator::FindTableState(
240 : const BgpTable *table) const {
241 : TableStateList::const_iterator loc =
242 5651 : table_state_list_.find(const_cast<BgpTable *>(table));
243 5642 : return (loc != table_state_list_.end() ? loc->second : NULL);
244 : }
245 :
246 : void
247 51296 : RoutePathReplicator::RequestWalk(BgpTable *table) {
248 51296 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
249 51296 : TableState *ts = FindTableState(table);
250 51296 : assert(ts);
251 51296 : if (!ts->walk_ref()) {
252 : DBTable::DBTableWalkRef walk_ref = table->AllocWalker(
253 : boost::bind(&RoutePathReplicator::RouteListener, this, ts, _1, _2),
254 45206 : boost::bind(&RoutePathReplicator::BulkReplicationDone, this, _2));
255 22603 : table->WalkTable(walk_ref);
256 22603 : ts->set_walk_ref(walk_ref);
257 22603 : } else {
258 28693 : table->WalkAgain(ts->walk_ref());
259 : }
260 51296 : }
261 :
262 : void
263 24558 : RoutePathReplicator::BulkReplicationDone(DBTableBase *dbtable) {
264 24558 : CHECK_CONCURRENCY("db::Walker");
265 24558 : BgpTable *table = static_cast<BgpTable *>(dbtable);
266 24558 : RPR_TRACE(WalkDone, table->name());
267 24558 : TableState *ts = FindTableState(table);
268 24558 : ts->RetryDelete();
269 24558 : }
270 :
271 429921 : void RoutePathReplicator::JoinVpnTable(RtGroup *group) {
272 429921 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
273 429974 : TableState *vpn_ts = FindTableState(vpn_table_);
274 429869 : if (!vpn_ts || vpn_ts->FindGroup(group))
275 370 : return;
276 429429 : RPR_TRACE(TableJoin, vpn_table_->name(), group->rt().ToString(), true);
277 429603 : group->AddImportTable(family(), vpn_table_);
278 429764 : RPR_TRACE(TableJoin, vpn_table_->name(), group->rt().ToString(), false);
279 429750 : group->AddExportTable(family(), vpn_table_);
280 429776 : AddTableState(vpn_table_, group);
281 : }
282 :
283 429966 : void RoutePathReplicator::LeaveVpnTable(RtGroup *group) {
284 429966 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
285 429967 : TableState *vpn_ts = FindTableState(vpn_table_);
286 429966 : if (!vpn_ts)
287 185 : return;
288 429781 : RPR_TRACE(TableLeave, vpn_table_->name(), group->rt().ToString(), true);
289 429781 : group->RemoveImportTable(family(), vpn_table_);
290 429783 : RPR_TRACE(TableLeave, vpn_table_->name(), group->rt().ToString(), false);
291 429783 : group->RemoveExportTable(family(), vpn_table_);
292 429784 : RemoveTableState(vpn_table_, group);
293 : }
294 :
295 : //
296 : // Add a given BgpTable to RtGroup of given RouteTarget.
297 : // It will create a new RtGroup if none exists.
298 : // In case of export RouteTarget, create TableState if it doesn't exist.
299 : //
300 928010 : void RoutePathReplicator::Join(BgpTable *table, const RouteTarget &rt,
301 : bool import) {
302 928010 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
303 :
304 927942 : std::scoped_lock lock(mutex_);
305 928526 : RPR_TRACE(TableJoin, table->name(), rt.ToString(), import);
306 :
307 928549 : bool first = false;
308 928549 : RtGroup *group = server()->rtarget_group_mgr()->LocateRtGroup(rt);
309 928647 : if (import) {
310 689992 : first = group->AddImportTable(family(), table);
311 689995 : if (group->HasDepRoutes())
312 115 : server()->rtarget_group_mgr()->NotifyRtGroup(rt);
313 689862 : if (family_ == Address::INETVPN)
314 138000 : server_->NotifyAllStaticRoutes();
315 1338432 : BOOST_FOREACH(BgpTable *sec_table, group->GetExportTables(family())) {
316 324278 : if (sec_table->IsVpnTable() || sec_table->empty())
317 323099 : continue;
318 1171 : RequestWalk(sec_table);
319 : }
320 : } else {
321 238655 : first = group->AddExportTable(family(), table);
322 238655 : TableState *ts = AddTableState(table, group);
323 : // Export targets changed, so the attributes of the paths replicated
324 : // earlier are stale. Bump before the walk, so that RouteListener does
325 : // not take the optimized path for them.
326 238649 : ts->bump_export_target_gen();
327 238653 : if (!table->empty()) {
328 60 : RequestWalk(table);
329 : }
330 : }
331 :
332 : // Join the vpn table when group is created.
333 928182 : if (first)
334 429920 : JoinVpnTable(group);
335 928376 : }
336 :
337 : //
338 : // Remove a BgpTable from RtGroup of given RouteTarget.
339 : // If the last group is going away, the RtGroup will be removed
340 : // In case of export RouteTarget, trigger remove of TableState appropriate.
341 : //
342 928657 : void RoutePathReplicator::Leave(BgpTable *table, const RouteTarget &rt,
343 : bool import) {
344 928657 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
345 :
346 928653 : std::scoped_lock lock(mutex_);
347 928660 : RtGroup *group = server()->rtarget_group_mgr()->GetRtGroup(rt);
348 928660 : assert(group);
349 928660 : RPR_TRACE(TableLeave, table->name(), rt.ToString(), import);
350 :
351 928660 : if (import) {
352 690005 : group->RemoveImportTable(family(), table);
353 690000 : if (group->HasDepRoutes())
354 132265 : server()->rtarget_group_mgr()->NotifyRtGroup(rt);
355 689998 : if (family_ == Address::INETVPN)
356 138001 : server_->NotifyAllStaticRoutes();
357 2674026 : BOOST_FOREACH(BgpTable *sec_table, group->GetExportTables(family())) {
358 992013 : if (sec_table->IsVpnTable() || sec_table->empty())
359 965132 : continue;
360 26882 : RequestWalk(sec_table);
361 : }
362 : } else {
363 238655 : group->RemoveExportTable(family(), table);
364 238655 : RemoveTableState(table, group);
365 : // The paths replicated earlier still carry the target being removed.
366 238655 : FindTableState(table)->bump_export_target_gen();
367 238655 : if (!table->empty()) {
368 23160 : RequestWalk(table);
369 : }
370 : }
371 :
372 : // Leave the vpn table when the last VRF has left the group.
373 928654 : if (!group->HasVrfTables(family())) {
374 429966 : LeaveVpnTable(group);
375 429969 : server()->rtarget_group_mgr()->RemoveRtGroup(rt);
376 : }
377 928653 : }
378 :
379 1447044 : void RoutePathReplicator::DBStateSync(BgpTable *table, TableState *ts,
380 : BgpRoute *rt, RtReplicated *dbstate,
381 : const RtReplicated::ReplicatedRtPathList *future) {
382 1447044 : set_synchronize(dbstate->GetMutableList(), future,
383 : boost::bind(&RtReplicated::AddRouteInfo, dbstate, table, rt, _1),
384 : boost::bind(&RtReplicated::DeleteRouteInfo, dbstate, table, rt, _1));
385 :
386 1446938 : if (dbstate->GetList().empty()) {
387 1058138 : rt->ClearState(table, ts->listener_id());
388 1058352 : delete dbstate;
389 1058243 : if (table->GetDBStateCount(ts->listener_id()) == 0)
390 612505 : ts->RetryDelete();
391 : }
392 1447206 : }
393 :
394 225654 : static ExtCommunityPtr UpdateOriginVn(BgpServer *server,
395 : const ExtCommunity *ext_community,
396 : int vn_index) {
397 225654 : as_t asn = server->autonomous_system();
398 225663 : ExtCommunityPtr extcomm_ptr;
399 225663 : if (vn_index) {
400 53603 : if (asn > AS2_MAX && vn_index > 0xffff) {
401 0 : OriginVn origin_vn(asn, AS_TRANS);
402 0 : extcomm_ptr = server->extcomm_db()->ReplaceOriginVnAndLocate(
403 0 : ext_community, origin_vn.GetExtCommunity());
404 0 : OriginVn origin_vn2(AS_TRANS, vn_index);
405 0 : extcomm_ptr = server->extcomm_db()->AppendAndLocate(
406 0 : extcomm_ptr.get(), origin_vn2.GetExtCommunity());
407 0 : } else {
408 53603 : OriginVn origin_vn(asn, vn_index);
409 160810 : extcomm_ptr = server->extcomm_db()->ReplaceOriginVnAndLocate(
410 107208 : ext_community, origin_vn.GetExtCommunity());
411 53606 : return extcomm_ptr;
412 : }
413 : }
414 172060 : extcomm_ptr = server->extcomm_db()->RemoveOriginVnAndLocate(ext_community);
415 172128 : return extcomm_ptr;
416 0 : }
417 :
418 : //
419 : // Update the ExtCommunity with the RouteTargets from the export list
420 : // and the OriginVn. The OriginVn is derived from the RouteTargets in
421 : // vpn routes.
422 : //
423 517876 : static ExtCommunityPtr UpdateExtCommunity(BgpServer *server,
424 : const RoutingInstance *rtinstance, const ExtCommunity *ext_community,
425 : const ExtCommunity::ExtCommunityList &export_list) {
426 : // Add RouteTargets exported by the instance for a non-master instance.
427 517876 : ExtCommunityPtr extcomm_ptr;
428 517876 : if (!rtinstance->IsMasterRoutingInstance()) {
429 : extcomm_ptr =
430 322201 : server->extcomm_db()->AppendAndLocate(ext_community, export_list);
431 322275 : return extcomm_ptr;
432 : }
433 :
434 : // Bail if we have a vpn route without extended communities.
435 195679 : if (!ext_community)
436 1051 : return ExtCommunityPtr(NULL);
437 :
438 : // Nothing to do if we already have the OriginVn community with our AS
439 : // or with a vn index from the global range.
440 3362036 : BOOST_FOREACH(const ExtCommunity::ExtCommunityValue &comm,
441 : ext_community->communities()) {
442 1600256 : if (!ExtCommunity::is_origin_vn(comm))
443 1583705 : continue;
444 18522 : OriginVn origin_vn(comm);
445 37060 : if (!origin_vn.IsGlobal() &&
446 18530 : origin_vn.as_number() != server->autonomous_system()) {
447 1995 : continue;
448 : }
449 16535 : return ExtCommunityPtr(ext_community);
450 : }
451 :
452 : // Add the OriginVn if we have a valid vn index.
453 : int vn_index =
454 178128 : server->routing_instance_mgr()->GetVnIndexByExtCommunity(ext_community);
455 178267 : return UpdateOriginVn(server, ext_community, vn_index);
456 518193 : }
457 :
458 : //
459 : // Concurrency: Called in the context of the DB partition task.
460 : //
461 : // This function handles
462 : // 1. Table Notification for path replication
463 : // 2. Table walk for import/export of new targets
464 : //
465 : // Replicate a path (clone the BgpPath) to secondary BgpTables based on the
466 : // export targets of the primary BgpTable.
467 : // If primary table is a VRF table attach it's export targets to replicated
468 : // path in the VPN table.
469 : //
470 2009042 : bool RoutePathReplicator::RouteListener(TableState *ts,
471 : DBTablePartBase *root, DBEntryBase *entry) {
472 2009042 : CHECK_CONCURRENCY("db::DBTable");
473 :
474 2008817 : BgpTable *table = static_cast<BgpTable *>(root->parent());
475 2008786 : BgpRoute *rt = static_cast<BgpRoute *>(entry);
476 2008786 : const RoutingInstance *rtinstance = table->routing_instance();
477 :
478 2008742 : DBTableBase::ListenerId id = ts->listener_id();
479 2008702 : assert(id != DBTableBase::kInvalidId);
480 :
481 : // Get the DBState.
482 : RtReplicated *dbstate =
483 2008702 : static_cast<RtReplicated *>(rt->GetState(table, id));
484 2010191 : RtReplicated::ReplicatedRtPathList replicated_path_list;
485 :
486 : //Flag to track if any change happenned to route in last 30 minutes
487 2009719 : bool route_unchanged = false;
488 : //List of tables that needs replication of route
489 2009719 : RtGroup::RtGroupMemberList addedTables;
490 : //List of tables that doesn't need replication of route
491 2009098 : RtGroup::RtGroupMemberList deletedTables;
492 : //List of tables having the latest changes in route
493 2008657 : RtGroup::RtGroupMemberList previousTables;
494 : //Threshold used for optimised replication
495 2008503 : uint64_t optimizationThresholdTime = 30ULL * 60 * 1000000;
496 2008503 : uint64_t start = UTCTimestampUsec();
497 : //Read once: a change racing with this walk must not be recorded as
498 : //applied. The walk it requests runs after this one and sees the mismatch.
499 2010084 : uint64_t export_target_gen = ts->export_target_gen();
500 :
501 : //Optimize only if last route change occurred 30 mins before and the
502 : //export targets of the primary table are unchanged since the route was
503 : //last replicated - the optimized path skips every destination table that
504 : //is in both the old and the new set, which for a route in a VRF always
505 : //includes the VPN table, leaving it with the previous export targets.
506 : //We update the route_unchanged flag to true
507 2010050 : if ( (start - rt->last_update_at() > optimizationThresholdTime) &&
508 2009984 : dbstate != NULL &&
509 86 : dbstate->export_target_gen() == export_target_gen)
510 : {
511 84 : RPR_TRACE(RouteListener, "Route change occurred before threshold \
512 : time, optimising replication");
513 84 : route_unchanged = true;
514 : }
515 :
516 : //
517 : // Cleanup if the route is not usable.
518 : // If route aggregation is enabled, contributing route/more specific route
519 : // for a aggregate route will NOT be replicated to destination table
520 : //
521 2472844 : if (!rt->IsUsable() || (table->IsRouteAggregationSupported() &&
522 463125 : !rtinstance->deleted() &&
523 460041 : table->IsContributingRoute(rt))) {
524 717505 : if (!dbstate) {
525 562838 : return true;
526 : }
527 154667 : DBStateSync(table, ts, rt, dbstate, &replicated_path_list);
528 154656 : return true;
529 : }
530 :
531 : // Create and set new DBState on the route. This will get cleaned up via
532 : // via the call to DBStateSync if we don't need to replicate the route to
533 : // any tables.
534 1291756 : if (dbstate == NULL) {
535 1057893 : dbstate = new RtReplicated(this);
536 1057830 : rt->SetState(table, id, dbstate);
537 : }
538 :
539 : // Get the export route target list from the routing instance.
540 1292086 : ExtCommunity::ExtCommunityList export_list;
541 1292062 : if (!rtinstance->IsMasterRoutingInstance()) {
542 5802247 : BOOST_FOREACH(RouteTarget rtarget, rtinstance->GetExportList()) {
543 2493647 : export_list.push_back(rtarget.GetExtCommunity());
544 : }
545 : }
546 :
547 : // Updating previousTables with the list of tables having the information
548 : // about the route.
549 1291883 : if (route_unchanged){
550 84 : replicated_path_list = dbstate->GetList();
551 270 : BOOST_FOREACH (RtReplicated::SecondaryRouteInfo path,
552 : replicated_path_list){
553 93 : previousTables.insert(path.table_);
554 : }
555 : }
556 :
557 : // Replicate all feasible and non-replicated paths.
558 1291883 : for (Route::PathList::iterator it = rt->GetPathList().begin();
559 6145510 : it != rt->GetPathList().end(); ++it) {
560 1786714 : BgpPath *path = static_cast<BgpPath *>(it.operator->());
561 :
562 : // Skip if the source peer is down.
563 2642028 : if (!path->IsStale() && !path->IsLlgrStale() && path->GetPeer() &&
564 854958 : !path->GetPeer()->IsReady())
565 1294244 : continue;
566 :
567 : // No need to replicate aliased or replicated paths.
568 1669407 : if (path->IsReplicated() || path->IsAliased())
569 1145246 : continue;
570 :
571 : // Do not replicate non-ecmp paths.
572 524148 : if (rt->BestPath()->PathCompare(*path, true))
573 6178 : break;
574 :
575 : // Do not replicate if nexthop is not hitched by ermvpn tree.
576 517887 : if (path->CheckErmVpn())
577 0 : break;
578 :
579 517884 : const BgpAttr *attr = path->GetAttr();
580 517887 : const ExtCommunity *ext_community = attr->ext_community();
581 :
582 : ExtCommunityPtr extcomm_ptr =
583 517879 : UpdateExtCommunity(server(), rtinstance, ext_community, export_list);
584 518193 : ext_community = extcomm_ptr.get();
585 518192 : if (!ext_community)
586 1051 : continue;
587 :
588 : // Go through all extended communities.
589 : //
590 : // Get the vn_index from the OriginVn extended community.
591 : // For each RouteTarget extended community, get the list of tables
592 : // to which we need to replicate the path.
593 517141 : int vn_index = 0;
594 517141 : RtGroup::RtGroupMemberList secondary_tables;
595 5940347 : BOOST_FOREACH(const ExtCommunity::ExtCommunityValue &comm,
596 : ext_community->communities()) {
597 2711578 : if (ExtCommunity::is_origin_vn(comm)) {
598 79536 : OriginVn origin_vn(comm);
599 79534 : vn_index = origin_vn.vn_index();
600 2631989 : } else if (ExtCommunity::is_route_target(comm)) {
601 : RtGroup *group =
602 2173581 : server()->rtarget_group_mgr()->GetRtGroup(comm);
603 2173953 : if (!group)
604 625029 : continue;
605 : RtGroup::RtGroupMemberList import_list =
606 2171971 : group->GetImportTables(family());
607 2171685 : if (import_list.empty())
608 623047 : continue;
609 1548627 : secondary_tables.insert(import_list.begin(), import_list.end());
610 2171462 : }
611 : }
612 :
613 : // Update with family specific secondary tables.
614 517091 : table->UpdateSecondaryTablesForReplication(rt, &secondary_tables);
615 :
616 : // Skip if we don't need to replicate the path to any tables.
617 517088 : if (secondary_tables.empty())
618 30298 : continue;
619 :
620 : // Add OriginVn when replicating self-originated routes from a VRF.
621 407459 : if (!vn_index && !rtinstance->IsMasterRoutingInstance() &&
622 894246 : path->IsVrfOriginated() && rtinstance->virtual_network_index()) {
623 47035 : vn_index = rtinstance->virtual_network_index();
624 47035 : extcomm_ptr = UpdateOriginVn(server_, extcomm_ptr.get(), vn_index);
625 : }
626 :
627 486786 : if (route_unchanged){
628 86 : addedTables = RtGroup::RtGroupMemberList();
629 : // Finding the difference between sets, secondary_tables and
630 : // previousTables
631 86 : std::set_difference(secondary_tables.begin(), secondary_tables.end(),
632 : previousTables.begin(), previousTables.end(),
633 : std::inserter(addedTables, addedTables.end()));
634 86 : std::set_difference(previousTables.begin(), previousTables.end(),
635 : secondary_tables.begin(), secondary_tables.end(),
636 : std::inserter(deletedTables, deletedTables.end()));
637 296 : BOOST_FOREACH (RtReplicated::SecondaryRouteInfo path,
638 : dbstate->GetList()){
639 105 : if (deletedTables.find(path.table_)!=deletedTables.end()) {
640 1 : replicated_path_list.erase(path);
641 : }
642 : }
643 : //Updating the secondary tables with the difference.
644 86 : secondary_tables = addedTables;
645 :
646 : }
647 :
648 :
649 : // Replicate path to all destination tables.
650 3736888 : BOOST_FOREACH(BgpTable *dest, secondary_tables) {
651 : // Skip if destination is same as source table.
652 1624980 : if (dest == table)
653 669176 : continue;
654 :
655 1138325 : const RoutingInstance *dest_rtinstance = dest->routing_instance();
656 1138325 : ExtCommunityPtr new_extcomm_ptr = extcomm_ptr;
657 :
658 : // If the origin vn is unresolved, see if route has a RouteTarget
659 : // that's in the set of export RouteTargets for the dest instance.
660 : // If so, we set the origin vn for the replicated route to be the
661 : // vn for the dest instance.
662 1139061 : if (!vn_index && dest_rtinstance->virtual_network_index() &&
663 468 : dest_rtinstance->HasExportTarget(ext_community)) {
664 368 : int dest_vn_index = dest_rtinstance->virtual_network_index();
665 736 : new_extcomm_ptr = UpdateOriginVn(server_, extcomm_ptr.get(),
666 368 : dest_vn_index);
667 : }
668 :
669 : // Replicate the route to the destination table. The destination
670 : // table may decide to not replicate based on it's own policy e.g.
671 : // multicast routes are never leaked across routing-instances.
672 1138580 : BgpRoute *replicated_rt = dest->RouteReplicate(
673 : server_, table, rt, path, new_extcomm_ptr);
674 1138600 : if (!replicated_rt)
675 182533 : continue;
676 :
677 : // Add information about the secondary path to the replicated path
678 : // list.
679 956053 : RtReplicated::SecondaryRouteInfo rtinfo(dest, path->GetPeer(),
680 1912116 : path->GetPathId(), path->GetSource(), replicated_rt);
681 956043 : pair<RtReplicated::ReplicatedRtPathList::iterator, bool> result;
682 956028 : result = replicated_path_list.insert(rtinfo);
683 : // Assert if the insertion to replication path list fails
684 956034 : if (!route_unchanged)
685 956034 : assert(result.second);
686 956034 : RPR_TRACE_ONLY(Replicate, table->name(), rt->ToString(),
687 : path->ToString(),
688 : BgpPath::PathIdString(path->GetPathId()),
689 : dest->name(), replicated_rt->ToString());
690 1138599 : }
691 548487 : }
692 :
693 : // Record what this replication was done with, for the next walk.
694 1291979 : dbstate->set_export_target_gen(export_target_gen);
695 :
696 : // Update the DBState to reflect the new list of secondary paths. The
697 : // DBState will get cleared if the list is empty.
698 1292303 : DBStateSync(table, ts, rt, dbstate, &replicated_path_list);
699 1292396 : return true;
700 2009890 : }
701 176 : const RtReplicated *RoutePathReplicator::GetReplicationState(
702 : BgpTable *table, BgpRoute *rt) const {
703 176 : const TableState *ts = FindTableState(table);
704 176 : if (!ts)
705 0 : return NULL;
706 : RtReplicated *dbstate =
707 176 : static_cast<RtReplicated *>(rt->GetState(table, ts->listener_id()));
708 176 : return dbstate;
709 : }
710 :
711 : //
712 : // Return the list of secondary table names for the given primary path.
713 : //
714 5475 : vector<string> RoutePathReplicator::GetReplicatedTableNameList(
715 : const BgpTable *table, const BgpRoute *rt, const BgpPath *path) const {
716 5475 : const TableState *ts = FindTableState(table);
717 5465 : if (!ts)
718 5161 : return vector<string>();
719 : const RtReplicated *dbstate = static_cast<const RtReplicated *>(
720 304 : rt->GetState(table, ts->listener_id()));
721 304 : if (!dbstate)
722 21 : return vector<string>();
723 283 : return dbstate->GetTableNameList(path);
724 : }
725 :
726 517098 : void RoutePathReplicator::DeleteSecondaryPath(BgpTable *table, BgpRoute *rt,
727 : const RtReplicated::SecondaryRouteInfo &rtinfo) {
728 517098 : BgpRoute *rt_secondary = rtinfo.rt_;
729 517098 : BgpTable *secondary_table = rtinfo.table_;
730 517098 : const IPeer *peer = rtinfo.peer_;
731 517098 : uint32_t path_id = rtinfo.path_id_;
732 517098 : BgpPath::PathSource src = rtinfo.src_;
733 :
734 : DBTablePartBase *partition =
735 517098 : secondary_table->GetTablePartition(rt_secondary);
736 517032 : BgpPath *secondary_path = rt_secondary->FindSecondaryPath(rt, src,
737 : peer, path_id);
738 517198 : if (secondary_path && secondary_path->NeedsResolution()) {
739 0 : secondary_table->path_resolver()->StopPathResolution(partition->index(),
740 : secondary_path);
741 : }
742 517180 : assert(rt_secondary->RemoveSecondaryPath(rt, src, peer, path_id));
743 517512 : if (rt_secondary->count() == 0) {
744 493456 : RPR_TRACE_ONLY(Flush, secondary_table->name(), rt_secondary->ToString(),
745 : peer ? peer->ToString() : "Nil",
746 : BgpPath::PathIdString(path_id), table->name(),
747 : rt->ToString(), "Delete");
748 493661 : partition->Delete(rt_secondary);
749 : } else {
750 23888 : partition->Notify(rt_secondary);
751 23896 : RPR_TRACE_ONLY(Flush, secondary_table->name(), rt_secondary->ToString(),
752 : peer ? peer->ToString() : "Nil",
753 : BgpPath::PathIdString(path_id), table->name(),
754 : rt->ToString(), "Path update");
755 : }
756 517562 : }
757 :
758 0 : string RtReplicated::SecondaryRouteInfo::ToString() const {
759 0 : ostringstream out;
760 0 : out << table_->name() << "(" << table_ << ")" << ":" <<
761 0 : peer_->ToString() << "(" << peer_ << ")" << ":" <<
762 0 : rt_->ToString() << "(" << rt_ << ")";
763 0 : return out.str();
764 0 : }
|