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/static_route.h"
7 :
8 : #include <boost/assign/list_of.hpp>
9 : #include <boost/foreach.hpp>
10 :
11 : #include <algorithm>
12 : #include <string>
13 : #include <vector>
14 :
15 : #include "base/map_util.h"
16 : #include "base/task_annotations.h"
17 : #include "base/task_trigger.h"
18 : #include "bgp/bgp_config.h"
19 : #include "bgp/bgp_log.h"
20 : #include "bgp/bgp_server.h"
21 : #include "bgp/inet6vpn/inet6vpn_route.h"
22 : #include "bgp/l3vpn/inetvpn_route.h"
23 : #include "bgp/origin-vn/origin_vn.h"
24 : #include "bgp/routing-instance/routepath_replicator.h"
25 : #include "bgp/routing-instance/routing_instance.h"
26 : #include "bgp/routing-instance/static_route_types.h"
27 : #include "bgp/routing-policy/routing_policy_match.h"
28 : #include "net/community_type.h"
29 :
30 : using boost::assign::list_of;
31 : using boost::system::error_code;
32 : using std::make_pair;
33 : using std::set;
34 : using std::string;
35 : using std::vector;
36 :
37 : class StaticRouteState : public ConditionMatchState {
38 : public:
39 128 : explicit StaticRouteState(StaticRoutePtr info) : info_(info) {
40 128 : }
41 : StaticRoutePtr info() {
42 : return info_;
43 : }
44 :
45 : private:
46 : StaticRoutePtr info_;
47 : DISALLOW_COPY_AND_ASSIGN(StaticRouteState);
48 : };
49 :
50 : template <typename T>
51 : class StaticRoute : public ConditionMatch {
52 : public:
53 : typedef typename T::RouteT RouteT;
54 : typedef typename T::VpnRouteT VpnRouteT;
55 : typedef typename T::PrefixT PrefixT;
56 : typedef typename T::AddressT AddressT;
57 : typedef StaticRouteMgr<T> StaticRouteMgrT;
58 :
59 : // List of Route targets
60 : typedef set<RouteTarget> RouteTargetList;
61 :
62 : // List of path ids for the Nexthop
63 : typedef set<uint32_t> NexthopPathIdList;
64 :
65 : enum CompareResult {
66 : NoChange = 0,
67 : PrefixChange = 1,
68 : NexthopChange = 2,
69 : AttributeChange = 3
70 : };
71 :
72 : StaticRoute(RoutingInstance *rtinstance, StaticRouteMgrT *manager,
73 : const PrefixT &static_route, const StaticRouteConfig &config);
74 8414 : Address::Family GetFamily() const { return manager_->GetFamily(); }
75 60 : AddressT GetAddress(IpAddress addr) const {
76 60 : return manager_->GetAddress(addr);
77 : }
78 :
79 : CompareResult CompareConfig(const StaticRouteConfig &config);
80 : void FillShowInfo(StaticRouteInfo *info) const;
81 :
82 196 : const PrefixT &prefix() const {
83 196 : return prefix_;
84 : }
85 :
86 196 : const IpAddress &nexthop() const {
87 196 : return nexthop_;
88 : }
89 :
90 1115 : RoutingInstance *routing_instance() const {
91 1115 : return routing_instance_;
92 : }
93 :
94 8014 : BgpTable *bgp_table() const {
95 8014 : return routing_instance_->GetTable(this->GetFamily());
96 : }
97 :
98 1406 : BgpRoute *nexthop_route() const {
99 1406 : return nexthop_route_;
100 : }
101 :
102 979 : NexthopPathIdList *NexthopPathIds() { return &nexthop_path_ids_; }
103 :
104 353 : void set_nexthop_route(BgpRoute *nexthop_route) {
105 353 : nexthop_route_ = nexthop_route;
106 :
107 353 : if (!nexthop_route_) {
108 130 : nexthop_path_ids_.clear();
109 130 : return;
110 : }
111 :
112 223 : assert(nexthop_path_ids_.empty());
113 :
114 543 : for (Route::PathList::iterator it =
115 223 : nexthop_route->GetPathList().begin();
116 1086 : it != nexthop_route->GetPathList().end(); it++) {
117 320 : BgpPath *path = static_cast<BgpPath *>(it.operator->());
118 :
119 : // Infeasible paths are not considered
120 320 : if (!path->IsFeasible()) break;
121 :
122 : // take snapshot of all ECMP paths
123 320 : if (nexthop_route_->BestPath()->PathCompare(*path, true)) break;
124 :
125 : // Use the nexthop attribute of the nexthop path as the path id.
126 320 : uint32_t path_id = path->GetAttr()->nexthop().to_v4().to_ulong();
127 320 : nexthop_path_ids_.insert(path_id);
128 : }
129 : }
130 :
131 1181 : const RouteTargetList &rtarget_list() const {
132 1181 : return rtarget_list_;
133 : }
134 :
135 : void UpdateAttributes(const StaticRouteConfig &config);
136 : void AddStaticRoute(NexthopPathIdList *list);
137 : void UpdateStaticRoute();
138 : void RemoveStaticRoute();
139 : void NotifyRoute();
140 : bool IsPending() const;
141 :
142 : virtual bool Match(BgpServer *server, BgpTable *table,
143 : BgpRoute *route, bool deleted);
144 :
145 0 : virtual string ToString() const {
146 0 : return (string("StaticRoute ") + nexthop_.to_string());
147 : }
148 :
149 196 : void set_unregistered() {
150 196 : unregistered_ = true;
151 196 : }
152 :
153 686 : bool unregistered() const {
154 686 : return unregistered_;
155 : }
156 :
157 : private:
158 : // Helper function to match
159 1243 : bool is_nexthop_route(BgpRoute *route) {
160 1243 : RouteT *ip_route = dynamic_cast<RouteT *>(route);
161 1243 : return (nexthop_ == ip_route->GetPrefix().addr());
162 : }
163 :
164 : CommunityPtr GetCommunity(const StaticRouteConfig &config);
165 : ExtCommunityPtr UpdateExtCommunity(const BgpAttr *attr) const;
166 :
167 : RoutingInstance *routing_instance_;
168 : StaticRouteMgrT *manager_;
169 : PrefixT prefix_;
170 : IpAddress nexthop_;
171 : BgpRoute *nexthop_route_;
172 : NexthopPathIdList nexthop_path_ids_;
173 : RouteTargetList rtarget_list_;
174 : CommunityPtr community_;
175 : bool unregistered_;
176 :
177 : DISALLOW_COPY_AND_ASSIGN(StaticRoute);
178 : };
179 :
180 : template <typename T>
181 196 : StaticRoute<T>::StaticRoute(RoutingInstance *rtinstance,
182 : StaticRouteMgrT *manager, const PrefixT &prefix,
183 : const StaticRouteConfig &config)
184 196 : : routing_instance_(rtinstance),
185 196 : manager_(manager),
186 196 : prefix_(prefix),
187 196 : nexthop_(config.nexthop),
188 196 : nexthop_route_(NULL),
189 392 : unregistered_(false) {
190 196 : UpdateAttributes(config);
191 196 : }
192 :
193 : //
194 : // Compare the given config against the current state of the StaticRoute.
195 : // Return the appropriate value from CompareResult.
196 : //
197 : template <typename T>
198 60 : typename StaticRoute<T>::CompareResult StaticRoute<T>::CompareConfig(
199 : const StaticRouteConfig &config) {
200 60 : AddressT address = this->GetAddress(config.address);
201 60 : PrefixT prefix(address, config.prefix_length);
202 60 : if (prefix_ != prefix) {
203 0 : return PrefixChange;
204 : }
205 60 : if (nexthop_ != config.nexthop) {
206 0 : return NexthopChange;
207 : }
208 60 : if (rtarget_list_.size() != config.route_targets.size()) {
209 8 : return AttributeChange;
210 : }
211 52 : for (vector<string>::const_iterator it = config.route_targets.begin();
212 172 : it != config.route_targets.end(); ++it) {
213 122 : error_code ec;
214 122 : RouteTarget rtarget = RouteTarget::FromString(*it, &ec);
215 122 : if (rtarget_list_.find(rtarget) == rtarget_list_.end()) {
216 2 : return AttributeChange;
217 : }
218 : }
219 50 : if (community_ != GetCommunity(config)) {
220 2 : return AttributeChange;
221 : }
222 :
223 48 : return NoChange;
224 : }
225 :
226 : template <typename T>
227 48 : void StaticRoute<T>::FillShowInfo(StaticRouteInfo *info) const {
228 48 : BgpTable *table = bgp_table();
229 48 : RouteT rt_key(prefix_);
230 48 : const BgpRoute *route = static_cast<const BgpRoute *>(table->Find(&rt_key));
231 48 : const BgpPath *path = route ? route->FindPath(BgpPath::StaticRoute) : NULL;
232 :
233 48 : info->set_prefix(prefix_.ToString());
234 48 : info->set_static_rt(path ? true : false);
235 48 : info->set_nexthop(nexthop_.to_string());
236 48 : if (nexthop_route_) {
237 38 : ShowRouteBrief show_route;
238 38 : nexthop_route_->FillRouteInfo(table, &show_route);
239 38 : info->set_nexthop_rt(show_route);
240 38 : }
241 :
242 48 : vector<string> community_list;
243 144 : BOOST_FOREACH(uint32_t value, community_->communities()) {
244 48 : community_list.push_back(CommunityType::CommunityToString(value));
245 : }
246 48 : info->set_community_list(community_list);
247 :
248 48 : vector<string> route_target_list;
249 48 : for (RouteTargetList::const_iterator it = rtarget_list_.begin();
250 172 : it != rtarget_list_.end(); ++it) {
251 124 : route_target_list.push_back(it->ToString());
252 : }
253 48 : info->set_route_target_list(route_target_list);
254 :
255 48 : if (path) {
256 44 : const RoutePathReplicator *replicator = table->server()->replicator(
257 : Address::VpnFamilyFromFamily(GetFamily()));
258 44 : info->set_secondary_tables(
259 : replicator->GetReplicatedTableNameList(table, route, path));
260 : }
261 48 : }
262 :
263 : // Match function called from BgpConditionListener
264 : // Concurrency : db::DBTable
265 : template <typename T>
266 1240 : bool StaticRoute<T>::Match(BgpServer *server, BgpTable *table,
267 : BgpRoute *route, bool deleted) {
268 1240 : CHECK_CONCURRENCY("db::DBTable");
269 : StaticRouteRequest::RequestType type;
270 :
271 1243 : if (is_nexthop_route(route) && !unregistered()) {
272 356 : if (deleted)
273 130 : type = StaticRouteRequest::NEXTHOP_DELETE;
274 : else
275 226 : type = StaticRouteRequest::NEXTHOP_ADD_CHG;
276 : } else {
277 887 : return false;
278 : }
279 :
280 356 : BgpConditionListener *listener = server->condition_listener(GetFamily());
281 : StaticRouteState *state = static_cast<StaticRouteState *>
282 356 : (listener->GetMatchState(table, route, this));
283 356 : if (!deleted) {
284 : // MatchState is added to the Route to ensure that DBEntry is not
285 : // deleted before the module processes the WorkQueue request.
286 226 : if (!state) {
287 128 : state = new StaticRouteState(StaticRoutePtr(this));
288 128 : listener->SetMatchState(table, route, this, state);
289 : }
290 : } else {
291 : // MatchState is set on all the Routes that matches the conditions
292 : // Retrieve to check and ignore delete of unseen Add Match
293 130 : if (state == NULL) {
294 : // Not seen ADD ignore DELETE
295 0 : return false;
296 : }
297 : }
298 :
299 : // The MatchState reference is taken to ensure that the route is not
300 : // deleted when request is still present in the queue
301 : // This is to handle the case where MatchState already exists and
302 : // deleted entry gets reused or reused entry gets deleted.
303 356 : state->IncrementRefCnt();
304 :
305 : // Post the Match result to StaticRoute processing task to take Action
306 : // Nexthop route found in NAT instance ==> Add Static Route
307 : // and stitch the Path Attribute from nexthop route
308 712 : StaticRouteRequest *req =
309 356 : new StaticRouteRequest(type, table, route, StaticRoutePtr(this));
310 356 : manager_->EnqueueStaticRouteReq(req);
311 :
312 356 : return true;
313 : }
314 :
315 : //
316 : // Build a Community for the given StaticRouteConfig.
317 : // Always add the AcceptOwnNexthop community in addition to the configured
318 : // list.
319 : //
320 : template <typename T>
321 258 : CommunityPtr StaticRoute<T>::GetCommunity(const StaticRouteConfig &config) {
322 258 : CommunitySpec comm_spec;
323 258 : comm_spec.communities.push_back(CommunityType::AcceptOwnNexthop);
324 258 : for (vector<string>::const_iterator it = config.communities.begin();
325 270 : it != config.communities.end(); ++it) {
326 12 : uint32_t value = CommunityType::CommunityFromString(*it);
327 12 : if (!value)
328 0 : continue;
329 12 : comm_spec.communities.push_back(value);
330 : }
331 258 : CommunityDB *comm_db = routing_instance()->server()->comm_db();
332 516 : return comm_db->Locate(comm_spec);
333 258 : }
334 :
335 : //
336 : // Build an updated ExtCommunity for the static route.
337 : //
338 : // Replace any RouteTargets with the list of RouteTargets for the StaticRoute.
339 : // If the StaticRoute has an empty RouteTarget list, then we infer that this
340 : // is not a snat use case and add the OriginVn as well. We don't want to add
341 : // OriginVn in snat scenario because the route has to be imported into many
342 : // VRFs and we want to set the OriginVn differently for each imported route.
343 : //
344 : template <typename T>
345 310 : ExtCommunityPtr StaticRoute<T>::UpdateExtCommunity(const BgpAttr *attr) const {
346 310 : ExtCommunity::ExtCommunityList export_list;
347 310 : for (RouteTargetList::const_iterator it = rtarget_list().begin();
348 871 : it != rtarget_list().end(); it++) {
349 561 : export_list.push_back(it->GetExtCommunity());
350 : }
351 :
352 310 : BgpServer *server = routing_instance()->server();
353 310 : ExtCommunityDB *extcomm_db = server->extcomm_db();
354 310 : ExtCommunityPtr new_ext_community = extcomm_db->ReplaceRTargetAndLocate(
355 : attr->ext_community(), export_list);
356 :
357 310 : int vn_index = routing_instance()->virtual_network_index();
358 310 : if (export_list.empty() && vn_index) {
359 57 : as_t asn = server->autonomous_system();
360 57 : if (asn > AS2_MAX && vn_index > 0xffff) {
361 0 : OriginVn origin_vn(asn, AS_TRANS);
362 0 : new_ext_community = extcomm_db->ReplaceOriginVnAndLocate(
363 0 : new_ext_community.get(), origin_vn.GetExtCommunity());
364 0 : OriginVn origin_vn2(AS_TRANS, vn_index);
365 0 : new_ext_community = extcomm_db->AppendAndLocate(
366 0 : new_ext_community.get(), origin_vn2.GetExtCommunity());
367 0 : } else {
368 57 : OriginVn origin_vn(server->autonomous_system(), vn_index);
369 57 : new_ext_community = extcomm_db->ReplaceOriginVnAndLocate(
370 57 : new_ext_community.get(), origin_vn.GetExtCommunity());
371 : }
372 : }
373 620 : return new_ext_community;
374 310 : }
375 :
376 : template <typename T>
377 208 : void StaticRoute<T>::UpdateAttributes(const StaticRouteConfig &config) {
378 208 : rtarget_list_.clear();
379 208 : for (vector<string>::const_iterator it = config.route_targets.begin();
380 632 : it != config.route_targets.end(); ++it) {
381 424 : error_code ec;
382 424 : RouteTarget rtarget = RouteTarget::FromString(*it, &ec);
383 424 : if (ec.failed())
384 6 : continue;
385 418 : rtarget_list_.insert(rtarget);
386 : }
387 208 : community_ = GetCommunity(config);
388 208 : }
389 :
390 : // RemoveStaticRoute
391 : template <typename T>
392 130 : void StaticRoute<T>::RemoveStaticRoute() {
393 130 : CHECK_CONCURRENCY("bgp::StaticRoute");
394 130 : RouteT rt_key(prefix_);
395 : DBTablePartition *partition =
396 130 : static_cast<DBTablePartition *>(bgp_table()->GetTablePartition(&rt_key));
397 : BgpRoute *static_route =
398 130 : static_cast<BgpRoute *>(partition->Find(&rt_key));
399 130 : if (!static_route || static_route->IsDeleted()) return;
400 :
401 128 : for (NexthopPathIdList::iterator it = NexthopPathIds()->begin();
402 262 : it != NexthopPathIds()->end(); it++) {
403 134 : static_route->RemovePath(BgpPath::StaticRoute, NULL, *it);
404 134 : BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
405 : "Removed Static route path " << static_route->ToString() <<
406 : " path_id " << BgpPath::PathIdString(*it) <<
407 : " in table " << bgp_table()->name());
408 : }
409 :
410 128 : if (!static_route->HasPaths()) {
411 110 : partition->Delete(static_route);
412 : } else {
413 18 : partition->Notify(static_route);
414 : }
415 130 : }
416 :
417 : // UpdateStaticRoute
418 : template <typename T>
419 14 : void StaticRoute<T>::UpdateStaticRoute() {
420 14 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
421 14 : RouteT rt_key(prefix_);
422 : DBTablePartition *partition =
423 14 : static_cast<DBTablePartition *>(bgp_table()->GetTablePartition(&rt_key));
424 : BgpRoute *static_route =
425 14 : static_cast<BgpRoute *>(partition->Find(&rt_key));
426 14 : if (static_route == NULL) return;
427 :
428 14 : static_route->ClearDelete();
429 :
430 14 : BgpAttrDB *attr_db = routing_instance()->server()->attr_db();
431 28 : for (NexthopPathIdList::iterator it = NexthopPathIds()->begin();
432 28 : it != NexthopPathIds()->end(); it++) {
433 : BgpPath *existing_path =
434 14 : static_route->FindPath(BgpPath::StaticRoute, NULL, *it);
435 14 : BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
436 : "Update attributes of StaticRoute path "
437 : << static_route->ToString() << " path_id "
438 : << BgpPath::PathIdString(*it) << " in table "
439 : << bgp_table()->name());
440 :
441 : // Add the RouteTarget and OrignVn in the ExtCommunity attribute.
442 14 : ExtCommunityPtr ptr = UpdateExtCommunity(existing_path->GetAttr());
443 14 : BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(
444 : existing_path->GetAttr(), ptr);
445 :
446 : // Use pre-calculated community from the StaticRoute.
447 14 : new_attr =
448 14 : attr_db->ReplaceCommunityAndLocate(new_attr.get(), community_);
449 :
450 28 : BgpPath *new_path =
451 28 : new BgpPath(*it, BgpPath::StaticRoute, new_attr.get(),
452 14 : existing_path->GetFlags(), existing_path->GetLabel());
453 :
454 14 : static_route->RemovePath(BgpPath::StaticRoute, NULL, *it);
455 :
456 14 : static_route->InsertPath(new_path);
457 : }
458 14 : partition->Notify(static_route);
459 14 : }
460 :
461 : // AddStaticRoute
462 : template <typename T>
463 223 : void StaticRoute<T>::AddStaticRoute(NexthopPathIdList *old_path_ids) {
464 223 : CHECK_CONCURRENCY("bgp::StaticRoute");
465 :
466 223 : RouteT rt_key(prefix_);
467 : DBTablePartition *partition =
468 223 : static_cast<DBTablePartition *>(bgp_table()->GetTablePartition(&rt_key));
469 : BgpRoute *static_route =
470 223 : static_cast<BgpRoute *>(partition->Find(&rt_key));
471 :
472 223 : if (static_route == NULL) {
473 123 : static_route = new RouteT(prefix_);
474 123 : partition->Add(static_route);
475 : } else {
476 100 : static_route->ClearDelete();
477 : }
478 :
479 223 : BgpAttrDB *attr_db = routing_instance()->server()->attr_db();
480 839 : for (Route::PathList::iterator it = nexthop_route()->GetPathList().begin();
481 1086 : it != nexthop_route()->GetPathList().end(); it++) {
482 320 : BgpPath *nexthop_route_path = static_cast<BgpPath *>(it.operator->());
483 :
484 : // Infeasible paths are not considered
485 320 : if (!nexthop_route_path->IsFeasible()) break;
486 :
487 : // take snapshot of all ECMP paths
488 320 : if (nexthop_route()->BestPath()->PathCompare(*nexthop_route_path, true))
489 0 : break;
490 :
491 : // Skip paths with duplicate forwarding information. This ensures
492 : // that we generate only one path with any given next hop and label
493 : // when there are multiple nexthop paths from the original source
494 : // received via different peers e.g. directly via XMPP and via BGP.
495 320 : if (nexthop_route()->DuplicateForwardingPath(nexthop_route_path))
496 24 : continue;
497 :
498 : // Add the route target in the ExtCommunity attribute.
499 296 : ExtCommunityPtr ptr = UpdateExtCommunity(nexthop_route_path->GetAttr());
500 296 : BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(
501 : nexthop_route_path->GetAttr(), ptr);
502 :
503 : // Use pre-calculated community from the static route.
504 296 : new_attr =
505 296 : attr_db->ReplaceCommunityAndLocate(new_attr.get(), community_);
506 :
507 : // Strip aspath. This is required when the nexthop route is learnt
508 : // via BGP.
509 296 : new_attr = attr_db->ReplaceAsPathAndLocate(new_attr.get(), AsPathPtr());
510 :
511 : // Replace the source rd if the nexthop path is a secondary path
512 : // of a primary path in the l3vpn table. Use the RD of the primary.
513 296 : if (nexthop_route_path->IsReplicated()) {
514 73 : const BgpSecondaryPath *spath =
515 : static_cast<const BgpSecondaryPath *>(nexthop_route_path);
516 73 : const RoutingInstance *ri = spath->src_table()->routing_instance();
517 73 : if (ri->IsMasterRoutingInstance()) {
518 : const VpnRouteT *vpn_route =
519 53 : static_cast<const VpnRouteT *>(spath->src_rt());
520 53 : new_attr = attr_db->ReplaceSourceRdAndLocate(new_attr.get(),
521 53 : vpn_route->GetPrefix().route_distinguisher());
522 : }
523 : }
524 :
525 : // Check whether we already have a path with the associated path id.
526 296 : uint32_t path_id =
527 296 : nexthop_route_path->GetAttr()->nexthop().to_v4().to_ulong();
528 296 : BgpPath *existing_path = static_route->FindPath(BgpPath::StaticRoute,
529 : NULL, path_id);
530 296 : if (existing_path != NULL) {
531 132 : if ((new_attr.get() != existing_path->GetAttr()) ||
532 132 : (nexthop_route_path->GetFlags() != existing_path->GetFlags()) ||
533 0 : (nexthop_route_path->GetLabel() != existing_path->GetLabel())) {
534 : // Update Attributes and notify (if needed)
535 132 : static_route->RemovePath(BgpPath::StaticRoute, NULL, path_id);
536 : } else {
537 0 : continue;
538 : }
539 : }
540 :
541 : // Populate SubProtocol for StaticRoute.
542 296 : new_attr = attr_db->ReplaceSubProtocolAndLocate(new_attr.get(),
543 : MatchProtocolToString(MatchProtocol::StaticRoute));
544 :
545 592 : BgpPath *new_path =
546 296 : new BgpPath(path_id, BgpPath::StaticRoute, new_attr.get(),
547 296 : nexthop_route_path->GetFlags(), nexthop_route_path->GetLabel());
548 296 : static_route->InsertPath(new_path);
549 296 : partition->Notify(static_route);
550 :
551 296 : BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
552 : "Added Static Route path " << static_route->ToString() <<
553 : " path_id " << BgpPath::PathIdString(path_id) <<
554 : " in table " << bgp_table()->name());
555 : }
556 :
557 223 : if (!old_path_ids) return;
558 :
559 223 : for (NexthopPathIdList::iterator it = old_path_ids->begin();
560 385 : it != old_path_ids->end(); it++) {
561 162 : if (NexthopPathIds()->find(*it) != NexthopPathIds()->end())
562 132 : continue;
563 30 : static_route->RemovePath(BgpPath::StaticRoute, NULL, *it);
564 30 : partition->Notify(static_route);
565 :
566 30 : BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
567 : "Removed StaticRoute path " << static_route->ToString() <<
568 : " path_id " << BgpPath::PathIdString(*it) <<
569 : " in table " << bgp_table()->name());
570 : }
571 223 : }
572 :
573 : template <typename T>
574 6643 : void StaticRoute<T>::NotifyRoute() {
575 6643 : RouteT rt_key(prefix_);
576 : DBTablePartition *partition =
577 6643 : static_cast<DBTablePartition *>(bgp_table()->GetTablePartition(&rt_key));
578 6643 : BgpRoute *static_route = static_cast<BgpRoute *>(partition->Find(&rt_key));
579 6643 : if (!static_route)
580 6635 : return;
581 8 : partition->Notify(static_route);
582 6643 : }
583 :
584 : template <typename T>
585 24 : bool StaticRoute<T>::IsPending() const {
586 24 : RouteT rt_key(prefix_);
587 : DBTablePartition *partition =
588 24 : static_cast<DBTablePartition *>(bgp_table()->GetTablePartition(&rt_key));
589 24 : const BgpRoute *route = static_cast<BgpRoute *>(partition->Find(&rt_key));
590 48 : return (!route || !route->FindPath(BgpPath::StaticRoute));
591 24 : }
592 :
593 : template <>
594 : int StaticRouteMgr<StaticRouteInet>::static_route_task_id_ = -1;
595 : template <>
596 : int StaticRouteMgr<StaticRouteInet6>::static_route_task_id_ = -1;
597 :
598 : template <typename T>
599 122 : StaticRouteMgr<T>::StaticRouteMgr(RoutingInstance *rtinstance)
600 122 : : rtinstance_(rtinstance),
601 122 : listener_(rtinstance_->server()->condition_listener(GetFamily())),
602 244 : unregister_list_trigger_(new TaskTrigger(
603 : boost::bind(&StaticRouteMgr::ProcessUnregisterList, this),
604 488 : TaskScheduler::GetInstance()->GetTaskId("bgp::Config"), 0)) {
605 122 : if (static_route_task_id_ == -1) {
606 11 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
607 11 : static_route_task_id_ = scheduler->GetTaskId("bgp::StaticRoute");
608 : }
609 :
610 122 : static_route_queue_ = new WorkQueue<StaticRouteRequest *>
611 : (static_route_task_id_, 0,
612 : boost::bind(&StaticRouteMgr::StaticRouteEventCallback, this, _1));
613 122 : }
614 :
615 : template <>
616 7489 : Address::Family StaticRouteMgr<StaticRouteInet>::GetFamily() const {
617 7489 : return Address::INET;
618 : }
619 :
620 : template <>
621 1243 : Address::Family StaticRouteMgr<StaticRouteInet6>::GetFamily() const {
622 1243 : return Address::INET6;
623 : }
624 :
625 : template <>
626 218 : Ip4Address StaticRouteMgr<StaticRouteInet>::GetAddress(IpAddress addr) const {
627 218 : assert(addr.is_v4());
628 218 : return addr.to_v4();
629 : }
630 :
631 : template <>
632 150 : Ip6Address StaticRouteMgr<StaticRouteInet6>::GetAddress(IpAddress addr) const {
633 150 : assert(addr.is_v6());
634 150 : return addr.to_v6();
635 : }
636 :
637 : template <typename T>
638 356 : void StaticRouteMgr<T>::EnqueueStaticRouteReq(StaticRouteRequest *req) {
639 356 : static_route_queue_->Enqueue(req);
640 356 : }
641 :
642 : template <typename T>
643 356 : bool StaticRouteMgr<T>::StaticRouteEventCallback(StaticRouteRequest *req) {
644 356 : CHECK_CONCURRENCY("bgp::StaticRoute");
645 356 : BgpTable *table = req->table_;
646 356 : BgpRoute *route = req->rt_;
647 356 : StaticRouteT *info = static_cast<StaticRouteT *>(req->info_.get());
648 :
649 356 : StaticRouteState *state = NULL;
650 356 : if (route) {
651 : state = static_cast<StaticRouteState *>
652 356 : (listener_->GetMatchState(table, route, info));
653 : }
654 :
655 356 : switch (req->type_) {
656 226 : case StaticRouteRequest::NEXTHOP_ADD_CHG: {
657 226 : assert(state);
658 226 : state->reset_deleted();
659 449 : if (route->IsDeleted() || !route->BestPath() ||
660 223 : !route->BestPath()->IsFeasible()) {
661 3 : break;
662 : }
663 :
664 : // Store the old path list
665 223 : typename StaticRouteT::NexthopPathIdList path_ids;
666 223 : path_ids.swap(*(info->NexthopPathIds()));
667 :
668 : // Populate the Nexthop PathID
669 223 : info->set_nexthop_route(route);
670 :
671 223 : info->AddStaticRoute(&path_ids);
672 223 : break;
673 223 : }
674 130 : case StaticRouteRequest::NEXTHOP_DELETE: {
675 130 : assert(state);
676 130 : info->RemoveStaticRoute();
677 130 : if (info->deleted() || route->IsDeleted()) {
678 130 : state->set_deleted();
679 : }
680 130 : info->set_nexthop_route(NULL);
681 130 : break;
682 : }
683 0 : default: {
684 0 : assert(0);
685 : break;
686 : }
687 : }
688 :
689 356 : if (state) {
690 356 : state->DecrementRefCnt();
691 356 : if (state->refcnt() == 0 && state->deleted()) {
692 128 : listener_->RemoveMatchState(table, route, info);
693 128 : delete state;
694 128 : if (!info->num_matchstate() && info->unregistered()) {
695 2 : UnregisterAndResolveStaticRoute(info);
696 : }
697 : }
698 : }
699 :
700 356 : delete req;
701 356 : return true;
702 : }
703 :
704 : template <typename T>
705 138 : bool StaticRouteMgr<T>::ProcessUnregisterList() {
706 138 : CHECK_CONCURRENCY("bgp::Config");
707 :
708 138 : for (StaticRouteProcessList::iterator
709 138 : it = unregister_static_route_list_.begin();
710 334 : it != unregister_static_route_list_.end(); ++it) {
711 196 : StaticRouteT *info = static_cast<StaticRouteT *>(it->get());
712 196 : listener_->UnregisterMatchCondition(info->bgp_table(), info);
713 196 : static_route_map_.erase(RouteKey(info->prefix(), info->nexthop()));
714 : }
715 :
716 138 : unregister_static_route_list_.clear();
717 :
718 138 : if (static_route_map_.empty()) {
719 122 : rtinstance_->server()->RemoveStaticRouteMgr(this);
720 : }
721 :
722 160 : if (!routing_instance()->deleted() &&
723 22 : routing_instance()->config()) {
724 22 : ProcessStaticRouteConfig();
725 : }
726 138 : return true;
727 : }
728 :
729 : template <typename T>
730 196 : void StaticRouteMgr<T>::UnregisterAndResolveStaticRoute(StaticRoutePtr entry) {
731 196 : std::scoped_lock lock(mutex_);
732 196 : unregister_static_route_list_.insert(entry);
733 196 : unregister_list_trigger_->Set();
734 196 : }
735 :
736 : template <typename T>
737 256 : void StaticRouteMgr<T>::LocateStaticRoutePrefix(
738 : const StaticRouteConfig &config) {
739 256 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
740 256 : AddressT address = this->GetAddress(config.address);
741 256 : PrefixT prefix(address, config.prefix_length);
742 256 : RouteKey route_key(prefix, config.nexthop);
743 :
744 : // Verify whether the entry already exists
745 256 : typename StaticRouteMap::iterator it = static_route_map_.find(route_key);
746 256 : if (it != static_route_map_.end()) {
747 : // Wait for the delete complete cb
748 60 : if (it->second->deleted()) return;
749 :
750 : StaticRouteT *match =
751 60 : static_cast<StaticRouteT *>(it->second.get());
752 : // Check whether the config has got updated
753 : typename StaticRouteT::CompareResult change =
754 60 : match->CompareConfig(config);
755 :
756 : // StaticRoutePrefix is the key, it can't change.
757 60 : assert(change != StaticRouteT::PrefixChange);
758 :
759 : // Skip if there's no change.
760 60 : if (change == StaticRouteT::NoChange)
761 48 : return;
762 :
763 : // Update the attributes and any existing BgpPaths.
764 12 : if (change == StaticRouteT::AttributeChange) {
765 12 : match->UpdateAttributes(config);
766 12 : match->UpdateStaticRoute();
767 12 : return;
768 : }
769 :
770 : // If the nexthop changes, remove the static route, if already added.
771 : // To do this, remove match condition and wait for remove completion.
772 0 : BgpConditionListener::RequestDoneCb callback =
773 : boost::bind(&StaticRouteMgr::StopStaticRouteDone, this, _1, _2);
774 0 : listener_->RemoveMatchCondition(
775 0 : match->bgp_table(), it->second.get(), callback);
776 0 : return;
777 0 : }
778 :
779 196 : StaticRouteT *match =
780 196 : new StaticRouteT(routing_instance(), this, prefix, config);
781 196 : StaticRoutePtr static_route_match = StaticRoutePtr(match);
782 :
783 196 : if (static_route_map_.empty())
784 122 : rtinstance_->server()->InsertStaticRouteMgr(this);
785 196 : static_route_map_.insert(make_pair(route_key, static_route_match));
786 :
787 196 : listener_->AddMatchCondition(match->bgp_table(), static_route_match.get(),
788 392 : BgpConditionListener::RequestDoneCb());
789 196 : }
790 :
791 : template <typename T>
792 196 : void StaticRouteMgr<T>::StopStaticRouteDone(BgpTable *table,
793 : ConditionMatch *info) {
794 196 : CHECK_CONCURRENCY("db::Walker");
795 196 : StaticRoute<T> *match = static_cast<StaticRoute<T> *>(info);
796 196 : match->set_unregistered();
797 196 : if (!match->num_matchstate() && match->unregistered()) {
798 194 : UnregisterAndResolveStaticRoute(match);
799 : }
800 196 : return;
801 : }
802 :
803 : template <typename T>
804 287 : void StaticRouteMgr<T>::RemoveStaticRoutePrefix(const RouteKey &static_route) {
805 287 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
806 287 : typename StaticRouteMap::iterator it = static_route_map_.find(static_route);
807 378 : if (it == static_route_map_.end()) return;
808 :
809 287 : if (it->second->deleted()) return;
810 :
811 196 : BgpConditionListener::RequestDoneCb callback =
812 : boost::bind(&StaticRouteMgr::StopStaticRouteDone, this, _1, _2);
813 :
814 196 : StaticRouteT *match = static_cast<StaticRouteT *>(it->second.get());
815 196 : listener_->RemoveMatchCondition(match->bgp_table(), match, callback);
816 196 : }
817 :
818 : template <typename T>
819 58 : void StaticRouteMgr<T>::ProcessStaticRouteConfig() {
820 58 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
821 58 : if (routing_instance()->deleted() || !routing_instance()->config()) return;
822 : const BgpInstanceConfig::StaticRouteList &list =
823 58 : routing_instance()->config()->static_routes(GetFamily());
824 : typedef BgpInstanceConfig::StaticRouteList::const_iterator iterator_t;
825 158 : for (iterator_t iter = list.begin(); iter != list.end(); ++iter) {
826 100 : LocateStaticRoutePrefix(*iter);
827 : }
828 : }
829 :
830 : template <typename T>
831 244 : StaticRouteMgr<T>::~StaticRouteMgr() {
832 122 : if (static_route_queue_)
833 122 : delete static_route_queue_;
834 366 : }
835 :
836 : template <typename T>
837 138 : void StaticRouteMgr<T>::UpdateStaticRouteConfig() {
838 138 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
839 138 : StaticRouteConfigList config_list =
840 138 : routing_instance()->config()->static_routes(GetFamily());
841 :
842 138 : map_difference(&static_route_map_,
843 : config_list.begin(), config_list.end(),
844 : boost::bind(&StaticRouteMgr<T>::CompareStaticRoute, this, _1, _2),
845 : boost::bind(&StaticRouteMgr<T>::AddStaticRoute, this, _1),
846 : boost::bind(&StaticRouteMgr<T>::DelStaticRoute, this, _1),
847 : boost::bind(&StaticRouteMgr<T>::UpdateStaticRoute, this, _1, _2));
848 138 : }
849 :
850 : template <typename T>
851 167 : void StaticRouteMgr<T>::FlushStaticRouteConfig() {
852 167 : CHECK_CONCURRENCY("bgp::Config");
853 167 : for (typename StaticRouteMap::iterator it = static_route_map_.begin();
854 418 : it != static_route_map_.end(); it++) {
855 251 : RemoveStaticRoutePrefix(it->first);
856 : }
857 167 : }
858 :
859 : template <typename T>
860 52 : int StaticRouteMgr<T>::CompareStaticRoute(
861 : typename StaticRouteMap::iterator loc,
862 : StaticRouteConfigList::iterator it) {
863 52 : AddressT address = this->GetAddress(it->address);
864 52 : PrefixT prefix(address, it->prefix_length);
865 52 : RouteKey route_key(prefix, it->nexthop);
866 52 : KEY_COMPARE(loc->first, route_key);
867 30 : return 0;
868 : }
869 :
870 : template <typename T>
871 126 : void StaticRouteMgr<T>::AddStaticRoute(StaticRouteConfigList::iterator it) {
872 126 : LocateStaticRoutePrefix(*it);
873 126 : }
874 :
875 : template <typename T>
876 36 : void StaticRouteMgr<T>::DelStaticRoute(typename StaticRouteMap::iterator loc) {
877 36 : RemoveStaticRoutePrefix(loc->first);
878 36 : }
879 :
880 : template <typename T>
881 30 : void StaticRouteMgr<T>::UpdateStaticRoute(typename StaticRouteMap::iterator loc,
882 : StaticRouteConfigList::iterator it) {
883 30 : LocateStaticRoutePrefix(*it);
884 30 : }
885 :
886 : template <typename T>
887 3653 : void StaticRouteMgr<T>::NotifyAllRoutes() {
888 3653 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
889 3653 : for (typename StaticRouteMap::iterator it = static_route_map_.begin();
890 10296 : it != static_route_map_.end(); ++it) {
891 : StaticRouteT *static_route =
892 6643 : static_cast<StaticRouteT *>(it->second.get());
893 6643 : static_route->NotifyRoute();
894 : }
895 3653 : }
896 :
897 : template <typename T>
898 2 : void StaticRouteMgr<T>::UpdateAllRoutes() {
899 2 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
900 2 : for (typename StaticRouteMap::iterator it = static_route_map_.begin();
901 4 : it != static_route_map_.end(); ++it) {
902 : StaticRouteT *static_route =
903 2 : static_cast<StaticRouteT *>(it->second.get());
904 2 : static_route->UpdateStaticRoute();
905 : }
906 2 : }
907 :
908 : template <typename T>
909 12 : void StaticRouteMgr<T>::DisableUnregisterTrigger() {
910 12 : unregister_list_trigger_->set_disable();
911 12 : }
912 :
913 : template <typename T>
914 12 : void StaticRouteMgr<T>::EnableUnregisterTrigger() {
915 12 : unregister_list_trigger_->set_enable();
916 12 : }
917 :
918 : template <typename T>
919 40 : uint32_t StaticRouteMgr<T>::GetRouteCount() const {
920 40 : CHECK_CONCURRENCY("bgp::ShowCommand");
921 40 : return static_route_map_.size();
922 : }
923 :
924 : template <typename T>
925 8 : uint32_t StaticRouteMgr<T>::GetDownRouteCount() const {
926 8 : CHECK_CONCURRENCY("bgp::ShowCommand");
927 8 : uint32_t count = 0;
928 8 : for (typename StaticRouteMap::const_iterator it = static_route_map_.begin();
929 32 : it != static_route_map_.end(); ++it) {
930 : const StaticRouteT *static_route =
931 24 : static_cast<const StaticRouteT *>(it->second.get());
932 24 : if (static_route->IsPending())
933 16 : count++;
934 : }
935 8 : return count;
936 : }
937 :
938 38 : template <typename T> bool StaticRouteMgr<T>::FillStaticRouteInfo(
939 : RoutingInstance *ri, StaticRouteEntriesInfo *info) const {
940 38 : if (static_route_map_.empty())
941 0 : return false;
942 :
943 38 : info->set_ri_name(ri->name());
944 86 : for (typename StaticRouteMgr<T>::StaticRouteMap::const_iterator it =
945 124 : static_route_map_.begin(); it != static_route_map_.end(); ++it) {
946 : StaticRoute<T> *match =
947 48 : static_cast<StaticRoute<T> *>(it->second.get());
948 48 : StaticRouteInfo static_info;
949 48 : match->FillShowInfo(&static_info);
950 48 : info->static_route_list.push_back(static_info);
951 : }
952 38 : return true;
953 : }
954 :
955 : // Explicit instantiation of StaticRouteMgr for INET and INET6.
956 : template class StaticRouteMgr<StaticRouteInet>;
957 : template class StaticRouteMgr<StaticRouteInet6>;
|