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