Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include "bgp/routing-instance/service_chaining.h"
6 :
7 : #include <boost/foreach.hpp>
8 : #include <boost/assign/list_of.hpp>
9 :
10 : #include <algorithm>
11 :
12 : #include "base/task_annotations.h"
13 : #include "base/task_trigger.h"
14 : #include "bgp/bgp_config.h"
15 : #include "bgp/bgp_log.h"
16 : #include "bgp/bgp_membership.h"
17 : #include "bgp/bgp_server.h"
18 : #include "bgp/extended-community/load_balance.h"
19 : #include "bgp/extended-community/site_of_origin.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/routing_instance.h"
24 : #include "bgp/routing-instance/service_chaining_types.h"
25 : #include "bgp/tunnel_encap/tunnel_encap.h"
26 : #include "net/community_type.h"
27 :
28 : using boost::bind;
29 : using boost::system::error_code;
30 : using std::make_pair;
31 : using std::sort;
32 : using std::string;
33 : using std::vector;
34 :
35 : template<>
36 : int ServiceChainMgr<ServiceChainInet>::service_chain_task_id_ = -1;
37 : template<>
38 : int ServiceChainMgr<ServiceChainInet6>::service_chain_task_id_ = -1;
39 : template<>
40 : int ServiceChainMgr<ServiceChainEvpn>::service_chain_task_id_ = -1;
41 : template<>
42 : int ServiceChainMgr<ServiceChainEvpn6>::service_chain_task_id_ = -1;
43 :
44 8932 : static int GetOriginVnIndex(const BgpTable *table, const BgpRoute *route) {
45 8932 : const BgpPath *path = route->BestPath();
46 8934 : if (!path)
47 0 : return 0;
48 :
49 8934 : const BgpAttr *attr = path->GetAttr();
50 8934 : const ExtCommunity *ext_community = attr->ext_community();
51 8934 : if (ext_community) {
52 26681 : BOOST_FOREACH(const ExtCommunity::ExtCommunityValue &comm,
53 : ext_community->communities()) {
54 11873 : if (!ExtCommunity::is_origin_vn(comm))
55 8879 : continue;
56 2994 : OriginVn origin_vn(comm);
57 2994 : return origin_vn.vn_index();
58 : }
59 : }
60 5936 : if (path->IsVrfOriginated())
61 5920 : return table->routing_instance()->virtual_network_index();
62 16 : return 0;
63 : }
64 :
65 : /**
66 : * Replicate prefix received at head-end of service chain to appropriate
67 : * table depending on address-family.
68 : * ----------------------------------------------------------------------
69 : * Family service-chain address-family Replication Table
70 : * ----------------------------------------------------------------------
71 : * EVPN INET InetTable
72 : * EVPN INET6 Inet6Table
73 : * INET INET EvpnTable
74 : * INET6 INET6 EvpnTable
75 : * ----------------------------------------------------------------------
76 : * @param partition - Reference to DBTablePartition where the
77 : * service-chain route is to be replicated
78 : * @param route - Reference to service-chain route
79 : * @param table - Reference to BgpTable where service-chain
80 : * route is to be replicated
81 : * @param prefix - Prefix
82 : * @param create - when "true" indicates that route should be
83 : * created if not found and
84 : */
85 : template<typename T>
86 17381 : void ServiceChain<T>::GetReplicationFamilyInfo(DBTablePartition *&partition,
87 : BgpRoute *&route, BgpTable *&table, PrefixT prefix, bool create) {
88 : /**
89 : * EVPN prefix received at head end of service-chain.
90 : * Replicate route to <vrf>.inet[6] table depending on
91 : * whether the EVPN prefix carries inet[6] traffic.
92 : */
93 17381 : RoutingInstance *src_ri = src_routing_instance();
94 17381 : IpAddress addr = prefix.addr();
95 17381 : int plen = prefix.prefixlen();
96 17381 : if (GetSCFamily() == SCAddress::EVPN) {
97 : /**
98 : * EVPN route carrying inet prefix
99 : */
100 4322 : table = src_ri->GetTable(Address::INET);
101 4322 : Ip4Prefix inet_prefix = Ip4Prefix(addr.to_v4(), plen);
102 4322 : InetRoute inet_route(inet_prefix);
103 4322 : partition = static_cast<DBTablePartition *>
104 4322 : (table->GetTablePartition(&inet_route));
105 4322 : route = static_cast<BgpRoute *>(partition->Find(&inet_route));
106 4322 : if (create) {
107 : /**
108 : * Create case. Create route if not found.
109 : */
110 1911 : if (route == NULL) {
111 1635 : route = new InetRoute(inet_prefix);
112 1635 : partition->Add(route);
113 : } else {
114 276 : route->ClearDelete();
115 : }
116 : }
117 17381 : } else if (GetSCFamily() == SCAddress::EVPN6) {
118 : /**
119 : * EVPN route carrying inet6 prefix
120 : */
121 4305 : table = src_ri->GetTable(Address::INET6);
122 4305 : Inet6Prefix inet6_prefix = Inet6Prefix(addr.to_v6(), plen);
123 4305 : Inet6Route inet6_route(inet6_prefix);
124 4305 : partition = static_cast<DBTablePartition *>
125 4305 : (table->GetTablePartition(&inet6_route));
126 4305 : route = static_cast<BgpRoute *>(partition->Find(&inet6_route));
127 4305 : if (create) {
128 : /**
129 : * Create case. Create route if not found.
130 : */
131 1907 : if (route == NULL) {
132 1635 : route = new Inet6Route(inet6_prefix);
133 1635 : partition->Add(route);
134 : } else {
135 272 : route->ClearDelete();
136 : }
137 : }
138 4305 : } else {
139 : /**
140 : * INET/INET6 prefix at head end of service-chain.
141 : * Replicate route to <vrf>.evpn.0 table.
142 : */
143 8754 : table = src_ri->GetTable(Address::EVPN);
144 8754 : string type_rd_tag("5-0:0-0-");
145 17508 : string prefix_str = type_rd_tag + addr.to_string() + "/" +
146 : boost::lexical_cast<std::string>(plen);
147 8754 : EvpnPrefix evpn_prefix(EvpnPrefix::FromString(prefix_str));
148 8754 : EvpnRoute evpn_route(evpn_prefix);
149 8754 : partition = static_cast<DBTablePartition *>
150 8754 : (table->GetTablePartition(&evpn_route));
151 8754 : route = static_cast<BgpRoute *>(partition->Find(&evpn_route));
152 8754 : if (create) {
153 : /**
154 : * Create case. Create route if not found.
155 : */
156 3931 : if (route == NULL) {
157 3327 : route = new EvpnRoute(evpn_prefix);
158 3327 : partition->Add(route);
159 : } else {
160 604 : route->ClearDelete();
161 : }
162 : }
163 8754 : }
164 17381 : }
165 :
166 : /**
167 : * Process service-chain path and add to service-chain route if needed.
168 : *
169 : * @param path_id - PathID
170 : * @param path - Path to be processed
171 : * @param attr - Path attribute
172 : * @param route - Reference to Service-Chain route
173 : * @param partition - Reference to DBTablePartition where the
174 : * service-chain route is to be replicated
175 : * @param aggregate - "true" indicates aggregate route
176 : * @param bgptable - BgpTable the service-chain route belongs to
177 : */
178 : template<typename T>
179 18621 : void ServiceChain<T>::ProcessServiceChainPath(uint32_t path_id, BgpPath *path,
180 : BgpAttrPtr attr, BgpRoute *&route, DBTablePartition *&partition,
181 : bool aggregate, BgpTable *bgptable) {
182 : BgpPath *existing_path =
183 18621 : route->FindPath(BgpPath::ServiceChain, NULL, path_id);
184 18621 : uint32_t label = path->GetLabel();
185 18621 : bool path_updated = false;
186 :
187 : /**
188 : * If inserting into EVPN table, the label should the vxlan_id of
189 : * the connected RI if non-zero and if not, the VNI.
190 : */
191 18621 : if (bgptable->family() == Address::EVPN) {
192 : const RoutingInstance *conn_ri =
193 16460 : bgptable->server()->routing_instance_mgr()->GetRoutingInstance(
194 : RoutingInstanceMgr::GetPrimaryRoutingInstanceName(
195 8230 : connected_->name()));
196 8230 : if (!conn_ri) {
197 : // conn_ri is not expected to be found only in unit tests.
198 8230 : assert(bgp_log_test::unit_test());
199 8230 : conn_ri = connected_routing_instance();
200 : }
201 8230 : label = conn_ri->vxlan_id();
202 8230 : if (!label) {
203 0 : label = conn_ri->virtual_network_index();
204 : }
205 : }
206 :
207 : /**
208 : * Check if there is an existing path that can be reused.
209 : */
210 18621 : if (existing_path != NULL) {
211 : // Existing path can be reused.
212 7443 : if ((attr.get() == existing_path->GetAttr()) &&
213 7443 : (path->GetLabel() == existing_path->GetLabel()) &&
214 2220 : (path->GetFlags() == existing_path->GetFlags())) {
215 2220 : return;
216 : }
217 :
218 : /**
219 : * Remove existing path, new path will be added below.
220 : */
221 2035 : path_updated = true;
222 2035 : route->RemovePath(BgpPath::ServiceChain, NULL, path_id);
223 : }
224 :
225 : /**
226 : * No existing path or un-usable existing path
227 : * Create new path and insert into service-chain route
228 : */
229 32802 : BgpPath *new_path =
230 16401 : new BgpPath(path_id, BgpPath::ServiceChain, attr.get(),
231 16401 : path->GetFlags(), label);
232 16401 : route->InsertPath(new_path);
233 16401 : partition->Notify(route);
234 :
235 16401 : BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
236 : (path_updated ? "Updated " : "Added ") <<
237 : (aggregate ? "Aggregate" : "ExtConnected") <<
238 : " ServiceChain path " << route->ToString() <<
239 : " path_id " << BgpPath::PathIdString(path_id) <<
240 : " in table " << bgptable->name() <<
241 : " .Path label: " << label);
242 : }
243 :
244 : template <typename T>
245 : class ServiceChainMgr<T>::DeleteActor : public LifetimeActor {
246 : public:
247 38968 : explicit DeleteActor(ServiceChainMgr<T> *manager)
248 38968 : : LifetimeActor(manager->server_->lifetime_manager()),
249 38968 : manager_(manager) {
250 38968 : }
251 40946 : virtual bool MayDelete() const {
252 40946 : return manager_->MayDelete();
253 : }
254 38552 : virtual void Shutdown() {
255 38552 : }
256 38552 : virtual void Destroy() {
257 38552 : manager_->Terminate();
258 38552 : }
259 :
260 : private:
261 : ServiceChainMgr<T> *manager_;
262 : };
263 :
264 : template <typename T>
265 2263 : ServiceChain<T>::ServiceChain(ServiceChainMgrT *manager,
266 : ServiceChainGroup *group, RoutingInstance *src, RoutingInstance *dest,
267 : RoutingInstance *connected, const vector<string> &subnets, AddressT addr,
268 : bool head, bool retain_as_path)
269 2263 : : manager_(manager),
270 2263 : group_(group),
271 2263 : src_(src),
272 2263 : dest_(dest),
273 2263 : connected_(connected),
274 2263 : connected_route_(NULL),
275 2263 : service_chain_addr_(addr),
276 2263 : group_oper_state_up_(group ? false : true),
277 2263 : connected_table_unregistered_(false),
278 2263 : dest_table_unregistered_(false),
279 2263 : aggregate_(false),
280 2263 : sc_head_(head),
281 2263 : retain_as_path_(retain_as_path),
282 2263 : src_table_delete_ref_(this, src_table()->deleter()),
283 2263 : dest_table_delete_ref_(this, dest_table()->deleter()),
284 6789 : connected_table_delete_ref_(this, connected_table()->deleter()) {
285 5993 : for (vector<string>::const_iterator it = subnets.begin();
286 5993 : it != subnets.end(); ++it) {
287 3730 : string prefix = *it;
288 3730 : error_code ec;
289 : /**
290 : * For EVPN, need to construct EVPN prefix from IPv4 or IPv6 subnet.
291 : * Make sure AF in prefix matches service_chain AF.
292 : */
293 3730 : if (GetFamily() == Address::EVPN) {
294 1464 : prefix = "5-0:0-0-" + prefix;
295 1464 : EvpnPrefix subnet = EvpnPrefix::FromString(prefix, &ec);
296 1464 : if (GetSCFamily() == SCAddress::EVPN) {
297 732 : if (subnet.family() == Address::INET6) {
298 8 : continue;
299 : }
300 : } else {
301 732 : if (subnet.family() != Address::INET6) {
302 4 : continue;
303 : }
304 : }
305 : }
306 3722 : PrefixT ipam_subnet = PrefixT::FromString(prefix, &ec);
307 3722 : if (ec.failed())
308 16 : continue;
309 3706 : prefix_to_routelist_map_[ipam_subnet] = RouteList();
310 : }
311 2263 : }
312 :
313 : template <typename T>
314 23519 : BgpTable *ServiceChain<T>::src_table() const {
315 23519 : return src_->GetTable(GetFamily());
316 : }
317 :
318 : template <typename T>
319 36328 : BgpTable *ServiceChain<T>::connected_table() const {
320 36328 : return connected_->GetTable(GetConnectedFamily());
321 : }
322 :
323 : template <typename T>
324 63386 : BgpTable *ServiceChain<T>::dest_table() const {
325 63386 : return dest_->GetTable(GetFamily());
326 : }
327 :
328 : //
329 : // Compare this ServiceChain against the ServiceChainConfig.
330 : // Return true if the configuration has not changed, false otherwise.
331 : //
332 : template <typename T>
333 849 : bool ServiceChain<T>::CompareServiceChainConfig(
334 : const ServiceChainConfig &config) {
335 849 : if (deleted())
336 320 : return false;
337 529 : if (dest_->name() != config.routing_instance)
338 0 : return false;
339 529 : if (connected_->name() != config.source_routing_instance)
340 0 : return false;
341 529 : if (service_chain_addr_.to_string() != config.service_chain_address)
342 8 : return false;
343 521 : if (!group_ && !config.service_chain_id.empty())
344 16 : return false;
345 505 : if (group_ && config.service_chain_id.empty())
346 16 : return false;
347 489 : if (group_ && group_->name() != config.service_chain_id)
348 16 : return false;
349 :
350 473 : if (prefix_to_routelist_map_.size() != config.prefix.size())
351 32 : return false;
352 441 : for (vector<string>::const_iterator it = config.prefix.begin();
353 691 : it != config.prefix.end(); ++it) {
354 466 : error_code ec;
355 466 : PrefixT ipam_subnet = PrefixT::FromString(*it, &ec);
356 466 : if (prefix_to_routelist_map_.find(ipam_subnet) ==
357 932 : prefix_to_routelist_map_.end()) {
358 216 : return false;
359 : }
360 : }
361 225 : return true;
362 : }
363 :
364 : //
365 : // Match function called from BgpConditionListener
366 : // Concurrency : db::DBTable
367 : // For the purpose of route aggregation, two condition needs to be matched
368 : // 1. More specific route present in any of the Dest BgpTable partition
369 : // 2. Connected route(for nexthop) present in Src BgpTable
370 : //
371 : template <typename T>
372 54253 : bool ServiceChain<T>::Match(BgpServer *server, BgpTable *table, BgpRoute *route,
373 : bool deleted) {
374 54253 : CHECK_CONCURRENCY("db::DBTable");
375 :
376 : typename ServiceChainRequestT::RequestType type;
377 54225 : PrefixT aggregate_match;
378 :
379 54203 : if (table == dest_table() && !dest_table_unregistered()) {
380 : // For EVPN service-chaining, we are only interested in Type 5 routes
381 : // from the destination table. Ignore any other route.
382 27037 : if (GetFamily() == Address::EVPN) {
383 11152 : if (!IsEvpnType5Route(route)) {
384 0 : return (false);
385 : }
386 : }
387 :
388 : // Skip connected routes
389 27034 : if (IsConnectedRoute(route)) {
390 351 : return false;
391 : }
392 :
393 : // Skip aggregate routes
394 26718 : if (aggregate_enable() && IsAggregate(route))
395 32 : return false;
396 :
397 26659 : if (aggregate_enable() && IsMoreSpecific(route, &aggregate_match)) {
398 : // More specific
399 2997 : if (deleted) {
400 1358 : type = ServiceChainRequestT::MORE_SPECIFIC_DELETE;
401 : } else {
402 1639 : type = ServiceChainRequestT::MORE_SPECIFIC_ADD_CHG;
403 : }
404 : } else {
405 : // External connecting routes
406 23668 : if (!deleted) {
407 8935 : if (!route->BestPath() || !route->BestPath()->IsFeasible()) {
408 0 : deleted = true;
409 : } else {
410 8934 : const BgpAttr *attr = route->BestPath()->GetAttr();
411 8933 : const Community *comm = attr ? attr->community() : NULL;
412 8932 : if (comm) {
413 3722 : if ((comm->ContainsValue(CommunityType::NoAdvertise)) ||
414 1853 : (comm->ContainsValue(CommunityType::NoReOriginate)))
415 32 : deleted = true;
416 : }
417 :
418 8932 : int vn_index = GetOriginVnIndex(table, route);
419 8930 : int src_vn_index = src_->virtual_network_index();
420 8930 : int dest_vn_index = dest_->virtual_network_index();
421 8930 : if (!vn_index || dest_vn_index != vn_index) {
422 1870 : if (src_vn_index == vn_index)
423 1118 : deleted = true;
424 1870 : if (!dest_->virtual_network_allow_transit())
425 1366 : deleted = true;
426 1870 : if (!dest_vn_index)
427 0 : deleted = true;
428 : }
429 :
430 8930 : const OriginVnPath *ovnpath =
431 8930 : attr ? attr->origin_vn_path() : NULL;
432 8930 : if (ovnpath && ovnpath->Contains(
433 : server->autonomous_system(), src_vn_index)) {
434 1080 : deleted = true;
435 : }
436 : }
437 : }
438 :
439 23663 : if (deleted) {
440 16236 : type = ServiceChainRequestT::EXT_CONNECT_ROUTE_DELETE;
441 : } else {
442 7427 : type = ServiceChainRequestT::EXT_CONNECT_ROUTE_ADD_CHG;
443 : }
444 : }
445 27141 : } else if ((table == connected_table()) &&
446 54345 : !connected_table_unregistered() &&
447 27126 : IsConnectedRoute(route, true)) {
448 : // Connected routes from source table
449 5180 : if (!deleted) {
450 6884 : if (!route->IsValid() ||
451 3442 : route->BestPath()->GetSource() != BgpPath::BGP_XMPP) {
452 0 : deleted = true;
453 : }
454 : }
455 :
456 : // Connected route for service chain
457 5180 : if (deleted) {
458 1738 : type = ServiceChainRequestT::CONNECTED_ROUTE_DELETE;
459 : } else {
460 3442 : type = ServiceChainRequestT::CONNECTED_ROUTE_ADD_CHG;
461 : }
462 : } else {
463 22038 : return false;
464 : }
465 :
466 31840 : BgpConditionListener *listener = manager_->GetListener();
467 : ServiceChainState *state = static_cast<ServiceChainState *>(
468 31835 : listener->GetMatchState(table, route, this));
469 31909 : if (!deleted) {
470 : // MatchState is added to the Route to ensure that DBEntry is not
471 : // deleted before the ServiceChain module processes the WorkQueue
472 : // request.
473 12521 : if (!state) {
474 8992 : state = new ServiceChainState(ServiceChainPtr(this));
475 8992 : listener->SetMatchState(table, route, this, state);
476 : }
477 : } else {
478 : // MatchState is set on all the Routes that matches the conditions
479 : // Retrieve to check and ignore delete of unseen Add Match
480 19388 : if (state == NULL) {
481 : // Not seen ADD ignore DELETE
482 6172 : return false;
483 : }
484 : }
485 :
486 : // The MatchState reference is taken to ensure that the route is not
487 : // deleted when request is still present in the queue
488 : // This is to handle the case where MatchState already exists and
489 : // deleted entry gets reused or reused entry gets deleted.
490 25737 : state->IncrementRefCnt();
491 :
492 : // Post the Match result to ServiceChain task to take Action
493 : // More_Specific_Present + Connected_Route_exists ==> Add Aggregate Route
494 : // and stitch the nexthop from connected route
495 51475 : ServiceChainRequestT *req = new ServiceChainRequestT(
496 51472 : type, table, route, aggregate_match, ServiceChainPtr(this));
497 25738 : manager_->Enqueue(req);
498 25739 : return true;
499 : }
500 :
501 : template <typename T>
502 0 : string ServiceChain<T>::ToString() const {
503 0 : return (string("ServiceChain " ) + service_chain_addr_.to_string());
504 : }
505 :
506 : template <typename T>
507 5099 : void ServiceChain<T>::SetConnectedRoute(BgpRoute *connected) {
508 5099 : connected_route_ = connected;
509 5099 : connected_path_ids_.clear();
510 5099 : if (!connected_route_)
511 1731 : return;
512 :
513 3368 : for (Route::PathList::iterator it = connected->GetPathList().begin();
514 16562 : it != connected->GetPathList().end(); ++it) {
515 5001 : BgpPath *path = static_cast<BgpPath *>(it.operator->());
516 :
517 : // Infeasible paths are not considered.
518 5001 : if (!path->IsFeasible())
519 88 : break;
520 :
521 : // Bail if it's not ECMP with the best path.
522 4997 : if (connected_route_->BestPath()->PathCompare(*path, true))
523 84 : break;
524 :
525 : // Use nexthop attribute of connected path as path id.
526 4913 : uint32_t path_id = path->GetAttr()->nexthop().to_v4().to_ulong();
527 4913 : connected_path_ids_.insert(path_id);
528 : }
529 : }
530 :
531 : template <typename T>
532 9137 : bool ServiceChain<T>::IsConnectedRouteValid() const {
533 9137 : return (connected_route_ && connected_route_->IsValid());
534 : }
535 :
536 : template <typename T>
537 23230 : bool ServiceChain<T>::IsMoreSpecific(BgpRoute *route,
538 : PrefixT *aggregate_match) const {
539 23230 : const RouteT *ip_route = static_cast<RouteT *>(route);
540 23230 : const PrefixT &ip_prefix = ip_route->GetPrefix();
541 23217 : for (typename PrefixToRouteListMap::const_iterator it =
542 23227 : prefix_to_route_list_map()->begin();
543 61458 : it != prefix_to_route_list_map()->end(); ++it) {
544 41242 : if (ip_prefix.IsMoreSpecific(it->first)) {
545 2997 : *aggregate_match = it->first;
546 2997 : return true;
547 : }
548 : }
549 20240 : return false;
550 : }
551 :
552 : template <typename T>
553 23137 : bool ServiceChain<T>::IsAggregate(BgpRoute *route) const {
554 23137 : RouteT *ip_route = dynamic_cast<RouteT *>(route);
555 23277 : for (typename PrefixToRouteListMap::const_iterator it =
556 23137 : prefix_to_route_list_map()->begin();
557 66291 : it != prefix_to_route_list_map()->end(); ++it) {
558 43056 : if (it->first == ip_route->GetPrefix())
559 32 : return true;
560 : }
561 23230 : return false;
562 : }
563 :
564 : /**
565 : * Check if route belongs to connected table.
566 : * For EVPN, connected routes could belong to INET or INET6 AF.
567 : * For routes in the destination table we can use template AF.
568 : */
569 : template <typename T>
570 54163 : bool ServiceChain<T>::IsConnectedRoute(BgpRoute *route,
571 : bool is_conn_table) const {
572 54163 : if (is_conn_table && GetFamily() == Address::EVPN) {
573 10786 : if (GetConnectedFamily() == Address::INET) {
574 5387 : InetRoute *inet_route = dynamic_cast<InetRoute *>(route);
575 5387 : return (service_chain_addr() == inet_route->GetPrefix().addr());
576 : } else {
577 5398 : Inet6Route *inet6_route = dynamic_cast<Inet6Route *>(route);
578 5398 : return (service_chain_addr() == inet6_route->GetPrefix().addr());
579 : }
580 : } else {
581 : /**
582 : * Non-EVPN and EVPN destination table case
583 : */
584 43376 : RouteT *ip_route = dynamic_cast<RouteT *>(route);
585 43376 : return (service_chain_addr() == ip_route->GetPrefix().addr());
586 : }
587 : }
588 :
589 : /**
590 : * Check if EVPN route being re-originated is Type 5
591 : * Also make sure that the address-family of the prefix carried in the EVPN
592 : * route matches the service-chain family. This will avoid the routes from
593 : * being re-originated twice.
594 : */
595 : template <typename T>
596 11150 : bool ServiceChain<T>::IsEvpnType5Route(BgpRoute *route) const {
597 11150 : if (GetFamily() != Address::EVPN) {
598 0 : return false;
599 : }
600 :
601 11153 : EvpnRoute *evpn_route = static_cast<EvpnRoute *>(route);
602 11153 : EvpnPrefix prefix = evpn_route->GetPrefix();
603 11153 : if (prefix.type() != EvpnPrefix::IpPrefixRoute) {
604 0 : return false;
605 : }
606 16724 : if (GetSCFamily() == SCAddress::EVPN &&
607 5572 : prefix.family() == Address::INET6) {
608 0 : return false;
609 : }
610 16731 : if (GetSCFamily() == SCAddress::EVPN6 &&
611 5579 : prefix.family() == Address::INET) {
612 0 : return false;
613 : }
614 11151 : return true;
615 : }
616 :
617 : template <typename T>
618 13218 : void ServiceChain<T>::RemoveMatchState(BgpRoute *route,
619 : ServiceChainState *state) {
620 13218 : if (deleted() || route->IsDeleted()) {
621 : // At this point we are ready to release the MatchState on the DBEntry
622 : // So mark it as deleted.. Actual removal of the state is done when
623 : // ref count is 0
624 13134 : state->set_deleted();
625 : }
626 13218 : }
627 :
628 : /*
629 : * To support BMS to VM service-chaining, we could have traffic being
630 : * chained between different address-families. This entails the need for
631 : * replicating the service-chain route across address-families.
632 : * The different possibilities are listed below.
633 : * ---------------------------------------------------
634 : * | service-chain info |
635 : * ----------------------------------------------------------------------
636 : * Traffic direction | Destination AF Source AF Replication Table |
637 : * ----------------------------------------------------------------------
638 : * VM(v4) --> BMS | EVPN INET InetTable |
639 : * VM(v6) --> BMS | EVPN INET6 Inet6Table |
640 : * BMS --> VM (v4) | INET INET EvpnTable |
641 : * BMS --> VM (v6) | INET6 INET6 EvpnTable |
642 : * ----------------------------------------------------------------------
643 : *
644 : * This is only done at the RI belonging to the head SI in the service
645 : * chain. At the RIs belonging to other SIs in the chain, we always
646 : * install the route only in the INET or INET6 table and not in the EVPN
647 : * table. This is because, at the RI belonging to the first SI in the
648 : * service-chain, we may need to originate a Type 5 route if a BMS happens
649 : * to be connected to it.
650 : */
651 : template <typename T>
652 11161 : void ServiceChain<T>::DeleteServiceChainRoute(PrefixT prefix, bool aggregate) {
653 :
654 11161 : CHECK_CONCURRENCY("bgp::ServiceChain");
655 :
656 : /*
657 : * For deletion within the same AF.
658 : * At the RIs belonging to SIs NOT at the head of the service-chain, do
659 : * not need to delete EVPN SC routes since only INET or INET6 routes
660 : * would have been installed.
661 : */
662 11161 : if (is_sc_head() || GetFamily() != Address::EVPN) {
663 11161 : BgpTable *bgptable = src_table();
664 11161 : RouteT rt_key(prefix);
665 : DBTablePartition *partition =
666 : static_cast<DBTablePartition *>(bgptable->
667 11161 : GetTablePartition(&rt_key));
668 : BgpRoute *service_chain_route =
669 11161 : static_cast<BgpRoute *>(partition->Find(&rt_key));
670 :
671 11161 : if (service_chain_route && !service_chain_route->IsDeleted()) {
672 7050 : DeleteServiceChainRouteInternal(service_chain_route, partition,
673 : bgptable, aggregate);
674 : }
675 11161 : }
676 :
677 : /*
678 : * For deletion from replication table.
679 : * At the RIs belonging to SIs NOT at the head of the service-chain, do
680 : * not need to delete SC routes in the EVPN table since they would have
681 : * been installed only in the INET or INET6 table..
682 : */
683 11161 : if (is_sc_head() || GetFamily() == Address::EVPN) {
684 : BgpTable *repl_table;
685 : BgpRoute *repl_sc_route;
686 : DBTablePartition *repl_partition;
687 9632 : GetReplicationFamilyInfo(repl_partition, repl_sc_route, repl_table,
688 : prefix, false);
689 9632 : if (repl_sc_route && !repl_sc_route->IsDeleted()) {
690 6605 : DeleteServiceChainRouteInternal(repl_sc_route, repl_partition,
691 : repl_table, aggregate);
692 : }
693 : }
694 11161 : }
695 :
696 : template <typename T>
697 13655 : void ServiceChain<T>::DeleteServiceChainRouteInternal(
698 : BgpRoute *service_chain_route,
699 : DBTablePartition *partition,
700 : BgpTable *bgptable,
701 : bool aggregate) {
702 13655 : CHECK_CONCURRENCY("bgp::ServiceChain");
703 :
704 13655 : for (ConnectedPathIdList::const_iterator it = GetConnectedPathIds().begin();
705 27537 : it != GetConnectedPathIds().end(); ++it) {
706 13882 : uint32_t path_id = *it;
707 13882 : service_chain_route->RemovePath(BgpPath::ServiceChain, NULL, path_id);
708 13882 : BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
709 : "Removed " << (aggregate ? "Aggregate" : "ExtConnected") <<
710 : " ServiceChain path " << service_chain_route->ToString() <<
711 : " path_id " << BgpPath::PathIdString(path_id) <<
712 : " in table " << bgptable->name());
713 : }
714 :
715 13655 : if (!service_chain_route->HasPaths()) {
716 13257 : partition->Delete(service_chain_route);
717 : } else {
718 398 : partition->Notify(service_chain_route);
719 : }
720 13655 : }
721 :
722 : /*
723 : * To support BMS to VM service-chaining, we could have traffic being
724 : * chained between different address-families. This entails the need for
725 : * replicating the service-chain route across address-families.
726 : * The different possibilities are listed below.
727 : * ---------------------------------------------------
728 : * | service-chain info |
729 : * ----------------------------------------------------------------------
730 : * Traffic direction | Destination AF Source AF Replication Table |
731 : * ----------------------------------------------------------------------
732 : * VM(v4) --> BMS | EVPN INET InetTable |
733 : * VM(v6) --> BMS | EVPN INET6 Inet6Table |
734 : * BMS --> VM (v4) | INET INET EvpnTable |
735 : * BMS --> VM (v6) | INET6 INET6 EvpnTable |
736 : * ----------------------------------------------------------------------
737 : *
738 : * This is only done at the RI belonging to the head SI in the service
739 : * chain. At the RIs belonging to other SIs in the chain, we always
740 : * install the route only in the INET or INET6 table and not in the EVPN
741 : * table. This is because, at the RI belonging to the first SI in the
742 : * service-chain, we may need to originate a Type 5 route if a BMS happens
743 : * to be connected to it.
744 : */
745 : template <typename T>
746 9807 : void ServiceChain<T>::UpdateServiceChainRoute(PrefixT prefix,
747 : const RouteT *orig_route, const ConnectedPathIdList &old_path_ids,
748 : bool aggregate) {
749 :
750 9807 : CHECK_CONCURRENCY("bgp::ServiceChain");
751 :
752 : /*
753 : * For re-origination within the same AF.
754 : * At the RIs belonging to SIs NOT at the head of the service-chain, do
755 : * not install EVPN SC routes. Only install INET or INET6 routes.
756 : */
757 9807 : if (is_sc_head() || GetFamily() != Address::EVPN) {
758 9807 : BgpTable *bgptable = src_table();
759 9807 : RouteT rt_key(prefix);
760 : DBTablePartition *partition =
761 : static_cast<DBTablePartition *>
762 9807 : (bgptable->GetTablePartition(&rt_key));
763 : BgpRoute *service_chain_route =
764 9807 : static_cast<BgpRoute *>(partition->Find(&rt_key));
765 :
766 9807 : if (service_chain_route == NULL) {
767 7139 : service_chain_route = new RouteT(prefix);
768 7139 : partition->Add(service_chain_route);
769 : } else {
770 2668 : service_chain_route->ClearDelete();
771 : }
772 :
773 9807 : UpdateServiceChainRouteInternal(orig_route, old_path_ids,
774 : service_chain_route, partition,
775 : bgptable, aggregate);
776 9807 : }
777 :
778 : /*
779 : * For re-origination to replication table.
780 : * At the RIs belonging to SIs NOT at the head of the service-chain, do
781 : * not install SC routes in the EVPN table. Only install INET or INET6
782 : * routes.
783 : */
784 9807 : if (is_sc_head() || GetFamily() == Address::EVPN) {
785 : BgpTable *repl_table;
786 : BgpRoute *repl_sc_route;
787 : DBTablePartition *repl_partition;
788 7749 : GetReplicationFamilyInfo(repl_partition, repl_sc_route, repl_table,
789 : prefix, true);
790 7749 : UpdateServiceChainRouteInternal(orig_route, old_path_ids,
791 : repl_sc_route, repl_partition,
792 : repl_table, aggregate);
793 : }
794 9807 : }
795 :
796 : template <typename T>
797 17556 : void ServiceChain<T>::UpdateServiceChainRouteInternal(const RouteT *orig_route,
798 : const ConnectedPathIdList &old_path_ids, BgpRoute *service_chain_route,
799 : DBTablePartition *partition, BgpTable *bgptable, bool aggregate) {
800 17556 : CHECK_CONCURRENCY("bgp::ServiceChain");
801 :
802 17556 : int vn_index = dest_routing_instance()->virtual_network_index();
803 17556 : BgpServer *server = dest_routing_instance()->server();
804 17556 : OriginVn origin_vn(server->autonomous_system(), vn_index);
805 17556 : OriginVn origin_vn4(server->autonomous_system(), AS_TRANS);
806 17556 : OriginVn origin_vn_trans(AS_TRANS, vn_index);
807 17556 : const OriginVnPath::OriginVnValue origin_vn_bytes = origin_vn.GetExtCommunity();
808 17556 : const OriginVnPath::OriginVnValue origin_vn_trans_bytes =
809 17556 : origin_vn_trans.GetExtCommunity();
810 17556 : const OriginVnPath::OriginVnValue origin_vn4_bytes =
811 17556 : origin_vn4.GetExtCommunity();
812 :
813 17556 : SiteOfOrigin soo;
814 17556 : ExtCommunity::ExtCommunityList sgid_list;
815 17556 : LargeCommunity::LargeCommunityList tag_list;
816 17556 : LoadBalance load_balance;
817 17556 : bool load_balance_present = false;
818 17556 : const Community *orig_community = nullptr;
819 17556 : const OriginVnPath *orig_ovnpath = nullptr;
820 17556 : const AsPath *orig_aspath = nullptr;
821 17556 : RouteDistinguisher orig_rd;
822 17556 : if (orig_route) {
823 13684 : const BgpPath *orig_path = orig_route->BestPath();
824 13684 : const BgpAttr *orig_attr = nullptr;
825 13684 : const ExtCommunity *ext_community = nullptr;
826 13684 : const LargeCommunity *large_community = nullptr;
827 13684 : if (orig_path != nullptr)
828 13619 : orig_attr = orig_path->GetAttr();
829 13684 : if (orig_attr != nullptr) {
830 13619 : orig_community = orig_attr->community();
831 13619 : ext_community = orig_attr->ext_community();
832 13619 : large_community = orig_attr->large_community();
833 13619 : orig_ovnpath = orig_attr->origin_vn_path();
834 13619 : orig_rd = orig_attr->source_rd();
835 13619 : orig_aspath = orig_attr->as_path();
836 : }
837 13684 : if (ext_community != nullptr) {
838 21868 : for(const ExtCommunity::ExtCommunityValue &comm:
839 13587 : ext_community->communities()) {
840 8281 : if (ExtCommunity::is_security_group(comm))
841 2751 : sgid_list.push_back(comm);
842 8281 : if (ExtCommunity::is_site_of_origin(comm) && soo.IsNull())
843 151 : soo = SiteOfOrigin(comm);
844 8281 : if (ExtCommunity::is_load_balance(comm)) {
845 128 : load_balance = LoadBalance(comm);
846 128 : load_balance_present = true;
847 : }
848 : }
849 : }
850 13684 : if (large_community != nullptr) {
851 13651 : for(const auto &comm : large_community->communities()) {
852 64 : if (LargeCommunity::is_tag(comm)) {
853 64 : tag_list.push_back(comm);
854 : }
855 : }
856 : }
857 : }
858 :
859 17556 : BgpAttrDB *attr_db = server->attr_db();
860 17556 : CommunityDB *comm_db = server->comm_db();
861 17556 : CommunityPtr new_community = comm_db->AppendAndLocate(
862 : orig_community, CommunityType::AcceptOwnNexthop);
863 17556 : ExtCommunityDB *extcomm_db = server->extcomm_db();
864 17556 : LargeCommunityDB *largecomm_db = server->largecomm_db();
865 17556 : BgpMembershipManager *membership_mgr = server->membership_mgr();
866 17556 : OriginVnPathDB *ovnpath_db = server->ovnpath_db();
867 17556 : OriginVnPathPtr new_ovnpath;
868 17556 : if (server->autonomous_system() > AS2_MAX && vn_index > 0xffff) {
869 0 : new_ovnpath = ovnpath_db->PrependAndLocate(orig_ovnpath,
870 : origin_vn4_bytes);
871 0 : new_ovnpath = ovnpath_db->PrependAndLocate(new_ovnpath.get(),
872 : origin_vn_trans_bytes);
873 : } else {
874 17556 : new_ovnpath = ovnpath_db->PrependAndLocate(
875 : orig_ovnpath, origin_vn_bytes);
876 : }
877 :
878 17556 : ConnectedPathIdList new_path_ids;
879 36945 : for (Route::PathList::iterator it =
880 17556 : connected_route()->GetPathList().begin();
881 76066 : it != connected_route()->GetPathList().end(); ++it) {
882 20661 : BgpPath *connected_path = static_cast<BgpPath *>(it.operator->());
883 :
884 : // Infeasible paths are not considered
885 20661 : if (!connected_path->IsFeasible())
886 184 : break;
887 :
888 : // take snapshot of all ECMP paths
889 20645 : if (connected_route()->BestPath()->PathCompare(*connected_path, true))
890 168 : break;
891 :
892 : // Skip paths with duplicate forwarding information. This ensures
893 : // that we generate only one path with any given next hop and label
894 : // when there are multiple connected paths from the original source
895 : // received via different peers e.g. directly via XMPP and via BGP.
896 20477 : if (connected_route()->DuplicateForwardingPath(connected_path))
897 1856 : continue;
898 :
899 18877 : const BgpAttr *attr = connected_path->GetAttr();
900 :
901 18877 : ExtCommunityPtr new_ext_community;
902 18877 : LargeCommunityPtr new_large_community;
903 :
904 : // Strip any RouteTargets from the connected attributes.
905 18877 : new_ext_community = extcomm_db->ReplaceRTargetAndLocate(
906 37754 : attr->ext_community(), ExtCommunity::ExtCommunityList());
907 :
908 : // Add the export route target list from the source routing instance
909 : // when inserting into EVPN table. This is required for the case when
910 : // service-chain routes are replicated to the BGP table to be used on
911 : // the BMS. The TOR switch does not import the cooked-up RT of the
912 : // service-RI. It only imports the RTs of the primary RI. Hence, we
913 : // add the primary RIs RTs to the route.
914 : // NOTE: There is an assumption that connected_ri on the head SI
915 : // will always point to the primary RI. Need to change that if the
916 : // assumption is not true.
917 : // Also, we pick only the export route targets in the range used by
918 : // schema transformer for non user-configured RTs.
919 18877 : if (is_sc_head() && bgptable->family() == Address::EVPN) {
920 8238 : ExtCommunity::ExtCommunityList export_list;
921 : const RoutingInstance *conn_ri =
922 16476 : server->routing_instance_mgr()->GetRoutingInstance(
923 : RoutingInstanceMgr::GetPrimaryRoutingInstanceName(
924 8238 : connected_->name()));
925 8238 : if (!conn_ri) {
926 : // conn_ri is not expected to be found only in unit tests.
927 8238 : assert(bgp_log_test::unit_test());
928 8238 : conn_ri = connected_routing_instance();
929 : }
930 8238 : BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
931 : "Adding primary RI " << conn_ri->name() << " route targets " <<
932 : "to service-chain route for EVPN table " << bgptable->name());
933 24714 : BOOST_FOREACH(const RouteTarget &rtarget,
934 : conn_ri->GetExportList()) {
935 8238 : if (ExtCommunity::get_rtarget_val(
936 16476 : rtarget.GetExtCommunity()) != 0) {
937 0 : BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG,
938 : BGP_LOG_FLAG_TRACE, "RT value " << rtarget.ToString());
939 0 : export_list.push_back(rtarget.GetExtCommunity());
940 : }
941 : }
942 8238 : new_ext_community = extcomm_db->AppendAndLocate(
943 : new_ext_community.get(), export_list);
944 8238 : }
945 :
946 : // Replace the SGID list with the list from the original route.
947 18877 : new_ext_community = extcomm_db->ReplaceSGIDListAndLocate(
948 : new_ext_community.get(), sgid_list);
949 :
950 : // Replace the Tag list with the list from the original route.
951 18877 : new_large_community = largecomm_db->ReplaceTagListAndLocate(
952 : new_large_community.get(), tag_list);
953 :
954 : // Replace SiteOfOrigin with value from original route if any.
955 18877 : if (soo.IsNull()) {
956 18662 : new_ext_community = extcomm_db->RemoveSiteOfOriginAndLocate(
957 : new_ext_community.get());
958 : } else {
959 215 : new_ext_community = extcomm_db->ReplaceSiteOfOriginAndLocate(
960 215 : new_ext_community.get(), soo.GetExtCommunity());
961 : }
962 :
963 : // Inherit load balance attribute of orig_route if connected path
964 : // does not have one already.
965 18877 : if (!LoadBalance::IsPresent(connected_path) && load_balance_present) {
966 64 : new_ext_community = extcomm_db->AppendAndLocate(
967 64 : new_ext_community.get(), load_balance.GetExtCommunity());
968 : }
969 :
970 : // Replace the OriginVn with the value from the original route
971 : // or the value associated with the dest routing instance.
972 18877 : if (server->autonomous_system() > AS2_MAX && vn_index > 0xffff) {
973 0 : new_ext_community = extcomm_db->ReplaceOriginVnAndLocate(
974 : new_ext_community.get(), origin_vn4_bytes);
975 0 : new_ext_community = extcomm_db->AppendAndLocate(
976 : new_ext_community.get(), origin_vn_trans_bytes);
977 : } else {
978 18877 : new_ext_community = extcomm_db->ReplaceOriginVnAndLocate(
979 : new_ext_community.get(), origin_vn_bytes);
980 : }
981 :
982 : // Connected routes always have mpls (udp or gre) as encap.
983 : // If updating service-chain route in the EVPN table, change
984 : // tunnel encap to include VxLAN and MPLS. BMS only supports VxLAN.
985 : // Vrouter has the choice of using VxLAN or MPLS based on config.
986 18877 : if (is_sc_head() && bgptable->family() == Address::EVPN) {
987 8238 : ExtCommunity::ExtCommunityList encaps_list;
988 8238 : vector<string> tunnel_encaps = boost::assign::list_of("vxlan");
989 24714 : BOOST_FOREACH(string encap, tunnel_encaps) {
990 8238 : encaps_list.push_back(TunnelEncap(encap).GetExtCommunity());
991 : }
992 8238 : new_ext_community = extcomm_db->
993 : ReplaceTunnelEncapsulationAndLocate(new_ext_community.get(),
994 : encaps_list);
995 8238 : }
996 :
997 : // Replace extended community, community and origin vn path.
998 18877 : BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(
999 : attr, new_ext_community);
1000 18877 : new_attr =
1001 : attr_db->ReplaceLargeCommunityAndLocate(new_attr.get(),
1002 : new_large_community);
1003 18877 : new_attr =
1004 : attr_db->ReplaceCommunityAndLocate(new_attr.get(), new_community);
1005 18877 : new_attr = attr_db->ReplaceOriginVnPathAndLocate(new_attr.get(),
1006 : new_ovnpath);
1007 :
1008 : // Strip as_path if needed. This is required when the connected route is
1009 : // learnt via BGP. If retain_as_path knob is configured replace the
1010 : // AsPath with the value from the original route.
1011 18877 : if (retain_as_path() && orig_aspath) {
1012 16 : new_attr = attr_db->ReplaceAsPathAndLocate(new_attr.get(),
1013 : orig_aspath);
1014 : } else {
1015 18861 : new_attr = attr_db->ReplaceAsPathAndLocate(new_attr.get(),
1016 37722 : AsPathPtr());
1017 : }
1018 :
1019 : // If the connected path is learnt via XMPP, construct RD based on
1020 : // the id registered with source table instead of connected table.
1021 : // This allows chaining of multiple in-network service instances
1022 : // that are on the same compute node.
1023 18877 : const IPeer *peer = connected_path->GetPeer();
1024 18877 : if (src_ != connected_ && peer && peer->IsXmppPeer()) {
1025 1627 : int instance_id = -1;
1026 1627 : bool is_registered = membership_mgr->GetRegistrationInfo(peer,
1027 : bgptable, &instance_id);
1028 1627 : if (!is_registered)
1029 240 : continue;
1030 1387 : RouteDistinguisher connected_rd = attr->source_rd();
1031 1387 : if (connected_rd.Type() != RouteDistinguisher::TypeIpAddressBased)
1032 0 : continue;
1033 :
1034 1387 : RouteDistinguisher rd(connected_rd.GetAddress(), instance_id);
1035 1387 : new_attr = attr_db->ReplaceSourceRdAndLocate(new_attr.get(), rd);
1036 : }
1037 :
1038 : // Replace the source rd if the connected path is a secondary path
1039 : // of a primary path in the l3vpn table. Use the RD of the primary.
1040 18637 : if (connected_path->IsReplicated()) {
1041 13333 : const BgpSecondaryPath *spath =
1042 : static_cast<const BgpSecondaryPath *>(connected_path);
1043 13333 : const RoutingInstance *ri = spath->src_table()->routing_instance();
1044 13333 : if (ri->IsMasterRoutingInstance()) {
1045 : const VpnRouteT *vpn_route =
1046 8858 : static_cast<const VpnRouteT *>(spath->src_rt());
1047 8858 : new_attr = attr_db->ReplaceSourceRdAndLocate(new_attr.get(),
1048 8858 : vpn_route->GetPrefix().route_distinguisher());
1049 : }
1050 : }
1051 :
1052 : // Skip paths with Source RD same as source RD of the connected path
1053 18637 : if (!orig_rd.IsZero() && new_attr->source_rd() == orig_rd)
1054 16 : continue;
1055 :
1056 : // Check whether we already have a path with the associated path id.
1057 18621 : uint32_t path_id =
1058 18621 : connected_path->GetAttr()->nexthop().to_v4().to_ulong();
1059 18621 : ProcessServiceChainPath(path_id, connected_path, new_attr,
1060 : service_chain_route, partition,
1061 : aggregate, bgptable);
1062 18621 : new_path_ids.insert(path_id);
1063 : }
1064 :
1065 : // Remove stale paths.
1066 17556 : for (ConnectedPathIdList::const_iterator it = old_path_ids.begin();
1067 22266 : it != old_path_ids.end(); ++it) {
1068 4710 : uint32_t path_id = *it;
1069 4710 : if (new_path_ids.find(path_id) != new_path_ids.end())
1070 3998 : continue;
1071 712 : service_chain_route->RemovePath(BgpPath::ServiceChain, NULL, path_id);
1072 712 : partition->Notify(service_chain_route);
1073 :
1074 712 : BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
1075 : "Removed " << (aggregate ? "Aggregate" : "ExtConnected") <<
1076 : " ServiceChain path " << service_chain_route->ToString() <<
1077 : " path_id " << BgpPath::PathIdString(path_id) <<
1078 : " in table " << bgptable->name());
1079 :
1080 : }
1081 :
1082 : // Delete the route if there's no paths.
1083 17556 : if (!service_chain_route->HasPaths())
1084 208 : partition->Delete(service_chain_route);
1085 17556 : }
1086 :
1087 : template <typename T>
1088 1641 : bool ServiceChain<T>::AddMoreSpecific(PrefixT aggregate,
1089 : BgpRoute *more_specific) {
1090 : typename PrefixToRouteListMap::iterator it =
1091 1641 : prefix_to_routelist_map_.find(aggregate);
1092 1641 : assert(it != prefix_to_routelist_map_.end());
1093 1641 : bool ret = false;
1094 1641 : if (it->second.empty()) {
1095 : // Add the aggregate for the first time
1096 1032 : ret = true;
1097 : }
1098 1641 : it->second.insert(more_specific);
1099 1641 : return ret;
1100 : }
1101 :
1102 : template <typename T>
1103 1360 : bool ServiceChain<T>::DeleteMoreSpecific(PrefixT aggregate,
1104 : BgpRoute *more_specific) {
1105 : typename PrefixToRouteListMap::iterator it =
1106 1360 : prefix_to_routelist_map_.find(aggregate);
1107 1360 : assert(it != prefix_to_routelist_map_.end());
1108 1360 : it->second.erase(more_specific);
1109 2720 : return it->second.empty();
1110 : }
1111 :
1112 : template <typename T>
1113 144 : void ServiceChain<T>::FillServiceChainInfo(ShowServicechainInfo *info) const {
1114 144 : if (deleted()) {
1115 0 : info->set_state("deleted");
1116 144 : } else if (!IsConnectedRouteValid()) {
1117 48 : info->set_state("down");
1118 96 : } else if (!group_oper_state_up()) {
1119 0 : info->set_state("group down");
1120 : } else {
1121 96 : info->set_state("active");
1122 : }
1123 :
1124 144 : ConnectedRouteInfo connected_rt_info;
1125 144 : connected_rt_info.set_service_chain_addr(
1126 144 : service_chain_addr().to_string());
1127 144 : if (connected_route()) {
1128 96 : ShowRoute show_route;
1129 96 : connected_route()->FillRouteInfo(connected_table(), &show_route);
1130 96 : connected_rt_info.set_connected_rt(show_route);
1131 96 : }
1132 144 : info->set_connected_route(connected_rt_info);
1133 :
1134 144 : vector<PrefixToRouteListInfo> more_vec;
1135 432 : for (typename PrefixToRouteListMap::const_iterator it =
1136 144 : prefix_to_route_list_map()->begin();
1137 432 : it != prefix_to_route_list_map()->end(); ++it) {
1138 288 : PrefixToRouteListInfo prefix_list_info;
1139 288 : prefix_list_info.set_prefix(it->first.ToString());
1140 :
1141 288 : BgpTable *bgptable = src_table();
1142 288 : RouteT rt_key(it->first);
1143 288 : BgpRoute *aggregate = static_cast<BgpRoute *>(bgptable->Find(&rt_key));
1144 288 : if (aggregate) {
1145 192 : prefix_list_info.set_aggregate(true);
1146 192 : ShowRoute show_route;
1147 192 : aggregate->FillRouteInfo(bgptable, &show_route);
1148 192 : prefix_list_info.set_aggregate_rt(show_route);
1149 192 : } else {
1150 96 : prefix_list_info.set_aggregate(false);
1151 : }
1152 :
1153 288 : vector<string> rt_list;
1154 288 : for (RouteList::iterator rt_it = it->second.begin();
1155 496 : rt_it != it->second.end(); ++rt_it) {
1156 208 : rt_list.push_back((*rt_it)->ToString());
1157 : }
1158 288 : prefix_list_info.set_more_specific_list(rt_list);
1159 288 : more_vec.push_back(prefix_list_info);
1160 : }
1161 144 : info->set_more_specifics(more_vec);
1162 :
1163 144 : vector<ExtConnectRouteInfo> ext_connecting_rt_info_list;
1164 144 : for (ExtConnectRouteList::const_iterator it =
1165 144 : ext_connecting_routes().begin();
1166 144 : it != ext_connecting_routes().end(); ++it) {
1167 0 : ExtConnectRouteInfo ext_rt_info;
1168 0 : ext_rt_info.set_ext_rt_prefix((*it)->ToString());
1169 0 : BgpTable *bgptable = src_table();
1170 0 : RouteT *ext_route = static_cast<RouteT *>(*it);
1171 0 : RouteT rt_key(ext_route->GetPrefix());
1172 : BgpRoute *ext_connecting =
1173 0 : static_cast<BgpRoute *>(bgptable->Find(&rt_key));
1174 0 : if (ext_connecting) {
1175 0 : ShowRoute show_route;
1176 0 : ext_connecting->FillRouteInfo(bgptable, &show_route);
1177 0 : ext_rt_info.set_ext_rt_svc_rt(show_route);
1178 0 : }
1179 0 : ext_connecting_rt_info_list.push_back(ext_rt_info);
1180 : }
1181 144 : info->set_ext_connecting_rt_info_list(ext_connecting_rt_info_list);
1182 144 : info->set_aggregate_enable(aggregate_enable());
1183 144 : }
1184 :
1185 304 : ServiceChainGroup::ServiceChainGroup(IServiceChainMgr *manager,
1186 304 : const string &name)
1187 304 : : manager_(manager),
1188 304 : name_(name),
1189 304 : oper_state_up_(false) {
1190 304 : }
1191 :
1192 304 : ServiceChainGroup::~ServiceChainGroup() {
1193 304 : assert(chain_set_.empty());
1194 304 : }
1195 :
1196 : //
1197 : // Add a RoutingInstance to this ServiceChainGroup.
1198 : // The caller must ensure that multiple bgp::ConfigHelper tasks do not
1199 : // invoke this method in parallel.
1200 : //
1201 632 : void ServiceChainGroup::AddRoutingInstance(RoutingInstance *rtinstance) {
1202 632 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
1203 632 : chain_set_.insert(rtinstance);
1204 632 : manager_->UpdateServiceChainGroup(this);
1205 632 : }
1206 :
1207 : //
1208 : // Delete a RoutingInstance from this ServiceChainGroup.
1209 : // The caller must ensure that multiple bgp::ConfigHelper tasks do not
1210 : // invoke this method in parallel.
1211 : //
1212 632 : void ServiceChainGroup::DeleteRoutingInstance(RoutingInstance *rtinstance) {
1213 632 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
1214 632 : chain_set_.erase(rtinstance);
1215 632 : manager_->UpdateServiceChainGroup(this);
1216 632 : }
1217 :
1218 : //
1219 : // Update the operational state of this ServiceChainGroup.
1220 : // It's considered to be up if all ServiceChains in the ServiceChainGroup
1221 : // are up.
1222 : // Trigger an update on the operational state of each ServiceChain if the
1223 : // operational state of the ServiceChainGroup changed.
1224 : //
1225 751 : void ServiceChainGroup::UpdateOperState() {
1226 751 : bool old_oper_state_up = oper_state_up_;
1227 751 : oper_state_up_ = true;
1228 1726 : BOOST_FOREACH(RoutingInstance *rtinstance, chain_set_) {
1229 837 : if (manager_->ServiceChainIsUp(rtinstance))
1230 138 : continue;
1231 699 : oper_state_up_ = false;
1232 699 : break;
1233 : }
1234 :
1235 751 : if (old_oper_state_up == oper_state_up_)
1236 655 : return;
1237 :
1238 480 : BOOST_FOREACH(RoutingInstance *rtinstance, chain_set_) {
1239 192 : manager_->UpdateServiceChain(rtinstance, oper_state_up_);
1240 : }
1241 : }
1242 :
1243 : /**
1244 : * Get appropriate listener for watching routes.
1245 : * For EVPN connected table, listener will depend on the connected AF
1246 : *
1247 : * @param addr - service-chain address
1248 : * @param is_conn_table - "true" indicates connected table
1249 : * @return - Pointer to BgpConditionListener object
1250 : */
1251 : template <typename T>
1252 70805 : BgpConditionListener *ServiceChainMgr<T>::GetListener() {
1253 70805 : return server_->condition_listener(GetSCFamily());
1254 : }
1255 :
1256 : template <typename T>
1257 43577 : bool ServiceChainMgr<T>::RequestHandler(ServiceChainRequestT *req) {
1258 43577 : CHECK_CONCURRENCY("bgp::ServiceChain");
1259 43577 : BgpTable *table = NULL;
1260 43577 : BgpRoute *route = NULL;
1261 43577 : PrefixT aggregate_match = req->aggregate_match_;
1262 43577 : ServiceChainT *info = NULL;
1263 :
1264 43577 : table = req->table_;
1265 43577 : route = req->rt_;
1266 43577 : info = static_cast<ServiceChainT *>(req->info_.get());
1267 :
1268 : // Table where the aggregate route needs to be added
1269 43577 : aggregate_match = req->aggregate_match_;
1270 :
1271 43577 : ServiceChainState *state = NULL;
1272 43577 : if (route) {
1273 : state = static_cast<ServiceChainState *>
1274 25739 : (listener_->GetMatchState(table, route, info));
1275 : }
1276 :
1277 43577 : switch (req->type_) {
1278 1641 : case ServiceChainRequestT::MORE_SPECIFIC_ADD_CHG: {
1279 1641 : assert(state);
1280 1641 : if (state->deleted()) {
1281 0 : state->reset_deleted();
1282 : }
1283 1641 : if (!info->AddMoreSpecific(aggregate_match, route))
1284 609 : break;
1285 1032 : if (!info->IsConnectedRouteValid())
1286 626 : break;
1287 406 : if (!info->group_oper_state_up())
1288 8 : break;
1289 :
1290 398 : typename ServiceChainT::ConnectedPathIdList path_ids;
1291 398 : info->UpdateServiceChainRoute(
1292 : aggregate_match, NULL, path_ids, true);
1293 398 : break;
1294 398 : }
1295 1360 : case ServiceChainRequestT::MORE_SPECIFIC_DELETE: {
1296 1360 : assert(state);
1297 1360 : if (info->DeleteMoreSpecific(aggregate_match, route)) {
1298 : // Delete the aggregate route
1299 1032 : info->DeleteServiceChainRoute(aggregate_match, true);
1300 : }
1301 1360 : info->RemoveMatchState(route, state);
1302 1360 : break;
1303 : }
1304 3442 : case ServiceChainRequestT::CONNECTED_ROUTE_ADD_CHG: {
1305 3442 : assert(state);
1306 6818 : if (route->IsDeleted() || !route->BestPath() ||
1307 3376 : !route->BestPath()->IsFeasible()) {
1308 74 : break;
1309 : }
1310 3368 : UpdateServiceChainGroup(info->group());
1311 :
1312 3368 : if (state->deleted()) {
1313 0 : state->reset_deleted();
1314 : }
1315 :
1316 : // Store the old path id list and populate the new one.
1317 3368 : typename ServiceChainT::ConnectedPathIdList path_ids =
1318 3368 : info->GetConnectedPathIds();
1319 3368 : info->SetConnectedRoute(route);
1320 :
1321 3368 : if (!info->group_oper_state_up())
1322 84 : break;
1323 :
1324 3284 : UpdateServiceChainRoutes(info, path_ids);
1325 3284 : break;
1326 3368 : }
1327 1731 : case ServiceChainRequestT::CONNECTED_ROUTE_DELETE: {
1328 1731 : assert(state);
1329 1731 : UpdateServiceChainGroup(info->group());
1330 1731 : DeleteServiceChainRoutes(info);
1331 1731 : info->RemoveMatchState(route, state);
1332 1731 : info->SetConnectedRoute(NULL);
1333 1731 : break;
1334 : }
1335 7438 : case ServiceChainRequestT::EXT_CONNECT_ROUTE_ADD_CHG: {
1336 7438 : assert(state);
1337 7438 : if (state->deleted()) {
1338 16 : state->reset_deleted();
1339 : }
1340 7438 : info->ext_connecting_routes()->insert(route);
1341 7438 : if (!info->IsConnectedRouteValid())
1342 5463 : break;
1343 1975 : if (!info->group_oper_state_up())
1344 28 : break;
1345 1947 : RouteT *ext_route = dynamic_cast<RouteT *>(route);
1346 1947 : typename ServiceChainT::ConnectedPathIdList path_ids;
1347 1947 : info->UpdateServiceChainRoute(
1348 : ext_route->GetPrefix(), ext_route, path_ids, false);
1349 1947 : break;
1350 1947 : }
1351 10127 : case ServiceChainRequestT::EXT_CONNECT_ROUTE_DELETE: {
1352 10127 : assert(state);
1353 10127 : if (info->ext_connecting_routes()->erase(route)) {
1354 6026 : RouteT *inet_route = dynamic_cast<RouteT *>(route);
1355 6026 : info->DeleteServiceChainRoute(inet_route->GetPrefix(), false);
1356 : }
1357 10127 : info->RemoveMatchState(route, state);
1358 10127 : break;
1359 : }
1360 13216 : case ServiceChainRequestT::UPDATE_ALL_ROUTES: {
1361 13216 : if (info->dest_table_unregistered())
1362 0 : break;
1363 13216 : if (info->connected_table_unregistered())
1364 0 : break;
1365 13216 : if (!info->connected_route())
1366 12126 : break;
1367 1090 : if (!info->group_oper_state_up())
1368 0 : break;
1369 :
1370 1090 : typename ServiceChainT::ConnectedPathIdList path_ids =
1371 1090 : info->GetConnectedPathIds();
1372 1090 : UpdateServiceChainRoutes(info, path_ids);
1373 1090 : break;
1374 1090 : }
1375 96 : case ServiceChainRequestT::DELETE_ALL_ROUTES: {
1376 96 : DeleteServiceChainRoutes(info);
1377 96 : break;
1378 : }
1379 4526 : case ServiceChainRequestT::STOP_CHAIN_DONE: {
1380 4526 : if (table == info->connected_table()) {
1381 2263 : info->set_connected_table_unregistered();
1382 2263 : if (!info->num_matchstate()) {
1383 2231 : listener_->UnregisterMatchCondition(table, info);
1384 : }
1385 : }
1386 4526 : if (table == info->dest_table()) {
1387 2263 : info->set_dest_table_unregistered();
1388 2263 : if (!info->num_matchstate()) {
1389 2135 : listener_->UnregisterMatchCondition(table, info);
1390 : }
1391 : }
1392 4526 : if (info->unregistered()) {
1393 2263 : chain_set_.erase(info->src_routing_instance());
1394 2263 : StartResolve();
1395 : }
1396 4526 : RetryDelete();
1397 4526 : break;
1398 : }
1399 0 : default: {
1400 0 : assert(false);
1401 : break;
1402 : }
1403 : }
1404 :
1405 43577 : if (state) {
1406 25739 : state->DecrementRefCnt();
1407 25739 : if (state->refcnt() == 0 && state->deleted()) {
1408 8992 : listener_->RemoveMatchState(table, route, info);
1409 8992 : delete state;
1410 8992 : if (!info->num_matchstate()) {
1411 1835 : if (info->dest_table_unregistered()) {
1412 128 : listener_->UnregisterMatchCondition(
1413 : info->dest_table(), info);
1414 : }
1415 1835 : if (info->connected_table_unregistered()) {
1416 32 : listener_->UnregisterMatchCondition(
1417 : info->connected_table(), info);
1418 : }
1419 1835 : if (info->unregistered()) {
1420 16 : chain_set_.erase(info->src_routing_instance());
1421 16 : StartResolve();
1422 : }
1423 : }
1424 : }
1425 : }
1426 43577 : delete req;
1427 43577 : return true;
1428 : }
1429 :
1430 : template <typename T>
1431 38968 : ServiceChainMgr<T>::ServiceChainMgr(BgpServer *server)
1432 38968 : : server_(server),
1433 38968 : listener_(GetListener()),
1434 77936 : resolve_trigger_(new TaskTrigger(
1435 : bind(&ServiceChainMgr::ResolvePendingServiceChain, this),
1436 77936 : TaskScheduler::GetInstance()->GetTaskId("bgp::Config"), 0)),
1437 77936 : group_trigger_(new TaskTrigger(
1438 : bind(&ServiceChainMgr::ProcessServiceChainGroups, this),
1439 77936 : TaskScheduler::GetInstance()->GetTaskId("bgp::ServiceChain"), 0)),
1440 38968 : aggregate_host_route_(false),
1441 38968 : deleter_(new DeleteActor(this)),
1442 155872 : server_delete_ref_(this, server->deleter()) {
1443 38968 : if (service_chain_task_id_ == -1) {
1444 584 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
1445 584 : service_chain_task_id_ = scheduler->GetTaskId("bgp::ServiceChain");
1446 : }
1447 :
1448 77936 : process_queue_.reset(
1449 38968 : new WorkQueue<ServiceChainRequestT *>(service_chain_task_id_, 0,
1450 : bind(&ServiceChainMgr::RequestHandler, this, _1)));
1451 :
1452 38968 : id_ = server->routing_instance_mgr()->RegisterInstanceOpCallback(
1453 : bind(&ServiceChainMgr::RoutingInstanceCallback, this, _1, _2));
1454 :
1455 38968 : BgpMembershipManager *membership_mgr = server->membership_mgr();
1456 38968 : registration_id_ = membership_mgr->RegisterPeerRegistrationCallback(
1457 : bind(&ServiceChainMgr::PeerRegistrationCallback, this, _1, _2, _3));
1458 38968 : }
1459 :
1460 : template <typename T>
1461 77936 : ServiceChainMgr<T>::~ServiceChainMgr() {
1462 38968 : assert(group_set_.empty());
1463 38968 : assert(group_map_.empty());
1464 116904 : }
1465 :
1466 : template <typename T>
1467 38552 : void ServiceChainMgr<T>::Terminate() {
1468 38552 : process_queue_->Shutdown();
1469 38552 : RoutingInstanceMgr *ri_mgr = server_->routing_instance_mgr();
1470 38552 : ri_mgr->UnregisterInstanceOpCallback(id_);
1471 38552 : BgpMembershipManager *membership_mgr = server_->membership_mgr();
1472 38552 : membership_mgr->UnregisterPeerRegistrationCallback(registration_id_);
1473 38552 : server_delete_ref_.Reset(NULL);
1474 38552 : }
1475 :
1476 : template <typename T>
1477 38552 : void ServiceChainMgr<T>::ManagedDelete() {
1478 38552 : deleter_->Delete();
1479 38552 : }
1480 :
1481 : template <typename T>
1482 40946 : bool ServiceChainMgr<T>::MayDelete() const {
1483 40946 : if (!chain_set_.empty() || !pending_chains_.empty())
1484 2378 : return false;
1485 38568 : if (!group_set_.empty() || !group_map_.empty())
1486 16 : return false;
1487 38552 : return true;
1488 : }
1489 :
1490 : template <typename T>
1491 6742 : void ServiceChainMgr<T>::RetryDelete() {
1492 6742 : if (!deleter_->IsDeleted())
1493 3056 : return;
1494 3686 : deleter_->RetryDelete();
1495 : }
1496 :
1497 : template <typename T>
1498 3673 : ServiceChainGroup *ServiceChainMgr<T>::FindServiceChainGroup(
1499 : RoutingInstance *rtinstance) {
1500 3673 : if (ServiceChainIsPending(rtinstance)) {
1501 514 : PendingChainState state = GetPendingServiceChain(rtinstance);
1502 514 : return state.group;
1503 514 : } else {
1504 3159 : const ServiceChain<T> *chain = FindServiceChain(rtinstance);
1505 3159 : return (chain ? chain->group() : NULL);
1506 : }
1507 : }
1508 :
1509 : template <typename T>
1510 232 : ServiceChainGroup *ServiceChainMgr<T>::FindServiceChainGroup(
1511 : const string &group_name) {
1512 232 : GroupMap::iterator loc = group_map_.find(group_name);
1513 232 : return (loc != group_map_.end() ? loc->second : NULL);
1514 : }
1515 :
1516 : template <typename T>
1517 632 : ServiceChainGroup *ServiceChainMgr<T>::LocateServiceChainGroup(
1518 : const string &group_name) {
1519 632 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
1520 :
1521 632 : GroupMap::iterator loc = group_map_.find(group_name);
1522 632 : ServiceChainGroup *group = (loc != group_map_.end()) ? loc->second : NULL;
1523 632 : if (!group) {
1524 304 : string temp_group_name(group_name);
1525 304 : group = new ServiceChainGroup(this, temp_group_name);
1526 304 : group_map_.insert(temp_group_name, group);
1527 304 : }
1528 632 : return group;
1529 : }
1530 :
1531 : template <typename T>
1532 6363 : void ServiceChainMgr<T>::UpdateServiceChainGroup(ServiceChainGroup *group) {
1533 6363 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper", "bgp::ServiceChain");
1534 :
1535 6363 : if (!group)
1536 4939 : return;
1537 1424 : group_set_.insert(group);
1538 1424 : group_trigger_->Set();
1539 : }
1540 :
1541 : template <typename T>
1542 991 : bool ServiceChainMgr<T>::ProcessServiceChainGroups() {
1543 991 : CHECK_CONCURRENCY("bgp::ServiceChain");
1544 :
1545 3101 : BOOST_FOREACH(ServiceChainGroup *group, group_set_) {
1546 1055 : if (group->empty()) {
1547 304 : string temp_group_name(group->name());
1548 304 : group_map_.erase(temp_group_name);
1549 304 : } else {
1550 751 : group->UpdateOperState();
1551 : }
1552 : }
1553 :
1554 991 : group_set_.clear();
1555 991 : RetryDelete();
1556 991 : return true;
1557 : }
1558 :
1559 : /**
1560 : * Address Family - used for GetTable().
1561 : */
1562 : template <>
1563 47636 : Address::Family ServiceChainMgr<ServiceChainInet>::GetFamily() const {
1564 47636 : return Address::INET;
1565 : }
1566 :
1567 : template <>
1568 47002 : Address::Family ServiceChainMgr<ServiceChainInet6>::GetFamily() const {
1569 47002 : return Address::INET6;
1570 : }
1571 :
1572 : template <>
1573 35325 : Address::Family ServiceChainMgr<ServiceChainEvpn>::GetFamily() const {
1574 35325 : return Address::EVPN;
1575 : }
1576 :
1577 : template <>
1578 35384 : Address::Family ServiceChainMgr<ServiceChainEvpn6>::GetFamily() const {
1579 35384 : return Address::EVPN;
1580 : }
1581 :
1582 : /**
1583 : * Connected Table Family. For EVPN, it is INET or INET6.
1584 : */
1585 : template <>
1586 11819 : Address::Family ServiceChainMgr<ServiceChainInet>::GetConnectedFamily() const {
1587 11819 : return Address::INET;
1588 : }
1589 :
1590 : template <>
1591 11418 : Address::Family ServiceChainMgr<ServiceChainInet6>::GetConnectedFamily() const {
1592 11418 : return Address::INET6;
1593 : }
1594 :
1595 : template <>
1596 13045 : Address::Family ServiceChainMgr<ServiceChainEvpn>::GetConnectedFamily() const {
1597 13045 : return Address::INET;
1598 : }
1599 :
1600 : template <>
1601 13053 : Address::Family ServiceChainMgr<ServiceChainEvpn6>::GetConnectedFamily() const {
1602 13053 : return Address::INET6;
1603 : }
1604 :
1605 : /**
1606 : * Service Chain Family
1607 : */
1608 : template <>
1609 28273 : SCAddress::Family ServiceChainMgr<ServiceChainInet>::GetSCFamily() const {
1610 28273 : return SCAddress::INET;
1611 : }
1612 :
1613 : template <>
1614 28016 : SCAddress::Family ServiceChainMgr<ServiceChainInet6>::GetSCFamily() const {
1615 28016 : return SCAddress::INET6;
1616 : }
1617 :
1618 : template <>
1619 32800 : SCAddress::Family ServiceChainMgr<ServiceChainEvpn>::GetSCFamily() const {
1620 32800 : return SCAddress::EVPN;
1621 : }
1622 :
1623 : template <>
1624 37115 : SCAddress::Family ServiceChainMgr<ServiceChainEvpn6>::GetSCFamily() const {
1625 37115 : return SCAddress::EVPN6;
1626 : }
1627 :
1628 : template <>
1629 : template <typename T>
1630 43575 : void ServiceChainMgr<T>::Enqueue(ServiceChainRequestT *req) {
1631 43575 : process_queue_->Enqueue(req);
1632 43577 : }
1633 :
1634 : template <typename T>
1635 417671 : bool ServiceChainMgr<T>::ServiceChainIsPending(RoutingInstance *rtinstance,
1636 : string *reason) const {
1637 : typename PendingChainList::const_iterator loc =
1638 417671 : pending_chains_.find(rtinstance);
1639 417672 : if (loc != pending_chains_.end()) {
1640 1462 : if (reason)
1641 192 : *reason = loc->second.reason;
1642 1462 : return true;
1643 : }
1644 416210 : return false;
1645 : }
1646 :
1647 : template <typename T>
1648 1477 : bool ServiceChainMgr<T>::ServiceChainIsUp(RoutingInstance *rtinstance) const {
1649 1477 : if (ServiceChainIsPending(rtinstance))
1650 426 : return false;
1651 1051 : const ServiceChain<T> *service_chain = FindServiceChain(rtinstance);
1652 1051 : if (!service_chain)
1653 608 : return false;
1654 443 : return service_chain->IsConnectedRouteValid();
1655 : }
1656 :
1657 : template <typename T>
1658 336 : bool ServiceChainMgr<T>::FillServiceChainInfo(RoutingInstance *rtinstance,
1659 : ShowServicechainInfo *info) const {
1660 336 : string pending_reason;
1661 336 : if (ServiceChainIsPending(rtinstance, &pending_reason)) {
1662 192 : info->set_state("pending");
1663 192 : info->set_pending_reason(pending_reason);
1664 192 : return true;
1665 : }
1666 144 : const ServiceChain<T> *service_chain = FindServiceChain(rtinstance);
1667 144 : if (!service_chain)
1668 0 : return false;
1669 144 : service_chain->FillServiceChainInfo(info);
1670 144 : return true;
1671 336 : }
1672 :
1673 :
1674 :
1675 : template <typename T>
1676 4016 : bool ServiceChainMgr<T>::LocateServiceChain(RoutingInstance *rtinstance,
1677 : const ServiceChainConfig &config) {
1678 4016 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
1679 :
1680 : // Verify whether the entry already exists.
1681 4016 : std::scoped_lock lock(mutex_);
1682 4016 : ServiceChainMap::iterator it = chain_set_.find(rtinstance);
1683 4016 : if (it != chain_set_.end()) {
1684 849 : ServiceChainT *chain = static_cast<ServiceChainT *>(it->second.get());
1685 849 : if (chain->CompareServiceChainConfig(config)) {
1686 225 : BGP_LOG_STR(BgpMessage, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
1687 : "No update in ServiceChain config : " << rtinstance->name());
1688 225 : return true;
1689 : }
1690 :
1691 624 : ServiceChainGroup *group = chain->group();
1692 624 : if (group) {
1693 40 : group->DeleteRoutingInstance(rtinstance);
1694 40 : chain->clear_group();
1695 : }
1696 :
1697 : // Update of ServiceChainConfig.
1698 : // Add the routing instance to pending list so that service chain is
1699 : // created after stop done callback for the current incarnation gets
1700 : // invoked.
1701 624 : if (config.service_chain_id.empty()) {
1702 544 : group = NULL;
1703 : } else {
1704 80 : group = LocateServiceChainGroup(config.service_chain_id);
1705 80 : group->AddRoutingInstance(rtinstance);
1706 : }
1707 624 : string reason = "Waiting for deletion of previous incarnation";
1708 624 : AddPendingServiceChain(rtinstance, group, reason);
1709 :
1710 : // Wait for the delete complete callback.
1711 624 : if (chain->deleted())
1712 320 : return false;
1713 :
1714 304 : BgpConditionListener::RequestDoneCb cb =
1715 : bind(&ServiceChainMgr::StopServiceChainDone, this, _1, _2);
1716 304 : listener_->RemoveMatchCondition(chain->dest_table(), chain, cb);
1717 304 : listener_->RemoveMatchCondition(chain->connected_table(), chain, cb);
1718 304 : return true;
1719 624 : }
1720 :
1721 3167 : RoutingInstanceMgr *mgr = server_->routing_instance_mgr();
1722 3167 : RoutingInstance *dest = mgr->GetRoutingInstance(config.routing_instance);
1723 :
1724 : // Dissociate from the old ServiceChainGroup.
1725 3167 : ServiceChainGroup *group = FindServiceChainGroup(rtinstance);
1726 3167 : if (group)
1727 104 : group->DeleteRoutingInstance(rtinstance);
1728 :
1729 : // Delete from the pending list. The instance would already have been
1730 : // removed from the pending list if this method is called when trying
1731 : // to resolve items in the pending list. However, if this method is
1732 : // called when processing a change in the service chain config, then
1733 : // we may need to remove it from the pending list.
1734 3167 : DeletePendingServiceChain(rtinstance);
1735 :
1736 : // Locate the new ServiceChainGroup.
1737 3167 : if (config.service_chain_id.empty()) {
1738 2615 : group = NULL;
1739 : } else {
1740 552 : group = LocateServiceChainGroup(config.service_chain_id);
1741 552 : group->AddRoutingInstance(rtinstance);
1742 : }
1743 :
1744 : // Destination routing instance does not exist.
1745 3167 : if (!dest) {
1746 731 : string reason = "Destination routing instance does not exist";
1747 731 : AddPendingServiceChain(rtinstance, group, reason);
1748 731 : return false;
1749 731 : }
1750 :
1751 : // Destination routing instance is being deleted.
1752 2436 : if (dest->deleted()) {
1753 0 : string reason = "Destination routing instance is being deleted";
1754 0 : AddPendingServiceChain(rtinstance, group, reason);
1755 0 : return false;
1756 0 : }
1757 :
1758 : // Destination virtual network index is unknown.
1759 2436 : if (!dest->virtual_network_index()) {
1760 32 : string reason = "Destination virtual network index is unknown";
1761 32 : AddPendingServiceChain(rtinstance, group, reason);
1762 32 : return false;
1763 32 : }
1764 :
1765 2404 : RoutingInstance *connected_ri = NULL;
1766 2404 : if (config.source_routing_instance == "") {
1767 32 : connected_ri = rtinstance;
1768 32 : assert(!rtinstance->deleted());
1769 : } else {
1770 2372 : connected_ri = mgr->GetRoutingInstance(config.source_routing_instance);
1771 : }
1772 :
1773 : // Connected routing instance does not exist.
1774 2404 : if (!connected_ri) {
1775 5 : string reason = "Connected routing instance does not exist";
1776 5 : AddPendingServiceChain(rtinstance, group, reason);
1777 5 : return false;
1778 5 : }
1779 :
1780 : // Connected routing instance is being deleted.
1781 2399 : if (connected_ri->deleted()) {
1782 0 : string reason = "Connected routing instance is being deleted";
1783 0 : AddPendingServiceChain(rtinstance, group, reason);
1784 0 : return false;
1785 0 : }
1786 :
1787 : // Add to pending queue if the service chain address is invalid.
1788 2399 : error_code ec;
1789 2399 : AddressT chain_addr =
1790 2399 : AddressT::from_string(config.service_chain_address, ec);
1791 2399 : if (ec.failed()) {
1792 136 : string reason = "Service chain address is invalid";
1793 136 : AddPendingServiceChain(rtinstance, group, reason);
1794 136 : return false;
1795 136 : }
1796 :
1797 : /**
1798 : * Get the BGP Tables to add condition
1799 : * For EVPN, connected table will be INET/INET6 depending on
1800 : * whether prefix carried is v4/v6.
1801 : */
1802 2263 : BgpTable *connected_table = NULL;
1803 2263 : connected_table = connected_ri->GetTable(GetConnectedFamily());
1804 2263 : assert(connected_table);
1805 2263 : BgpTable *dest_table = dest->GetTable(GetFamily());
1806 2263 : assert(dest_table);
1807 :
1808 : // Allocate the new service chain.
1809 4526 : ServiceChainPtr chain = ServiceChainPtr(new ServiceChainT(this, group,
1810 2263 : rtinstance, dest, connected_ri, config.prefix, chain_addr,
1811 2263 : config.sc_head, config.retain_as_path));
1812 :
1813 2263 : if (aggregate_host_route()) {
1814 1672 : ServiceChainT *obj = static_cast<ServiceChainT *>(chain.get());
1815 1672 : obj->set_aggregate_enable();
1816 : }
1817 :
1818 : // Add the new service chain request
1819 2263 : chain_set_.insert(make_pair(rtinstance, chain));
1820 2263 : listener_->AddMatchCondition(
1821 4526 : connected_table, chain.get(), BgpConditionListener::RequestDoneCb());
1822 2263 : listener_->AddMatchCondition(
1823 4526 : dest_table, chain.get(), BgpConditionListener::RequestDoneCb());
1824 :
1825 2263 : return true;
1826 4016 : }
1827 :
1828 : template <typename T>
1829 0 : ServiceChain<T> *ServiceChainMgr<T>::FindServiceChain(
1830 : const string &instance) const {
1831 0 : RoutingInstance *rtinstance =
1832 0 : server_->routing_instance_mgr()->GetRoutingInstance(instance);
1833 0 : if (!rtinstance)
1834 0 : return NULL;
1835 0 : ServiceChainMap::const_iterator it = chain_set_.find(rtinstance);
1836 0 : if (it == chain_set_.end())
1837 0 : return NULL;
1838 0 : ServiceChainT *chain = static_cast<ServiceChainT *>(it->second.get());
1839 0 : return chain;
1840 : }
1841 :
1842 : template <typename T>
1843 962220 : ServiceChain<T> *ServiceChainMgr<T>::FindServiceChain(
1844 : RoutingInstance *rtinstance) const {
1845 962220 : ServiceChainMap::const_iterator it = chain_set_.find(rtinstance);
1846 962220 : if (it == chain_set_.end())
1847 946154 : return NULL;
1848 16066 : ServiceChainT *chain = static_cast<ServiceChainT *>(it->second.get());
1849 16066 : return chain;
1850 : }
1851 :
1852 : template <typename T>
1853 1087 : bool ServiceChainMgr<T>::ResolvePendingServiceChain() {
1854 1087 : CHECK_CONCURRENCY("bgp::Config");
1855 2293 : for (typename PendingChainList::iterator it = pending_chains_.begin(), next;
1856 2293 : it != pending_chains_.end(); it = next) {
1857 1206 : next = it;
1858 1206 : ++next;
1859 1206 : RoutingInstance *rtinstance = it->first;
1860 1206 : ServiceChainGroup *group = it->second.group;
1861 1206 : if (group)
1862 248 : group->DeleteRoutingInstance(rtinstance);
1863 1206 : pending_chains_.erase(it);
1864 : const ServiceChainConfig *sc_config =
1865 1206 : rtinstance->config()->service_chain_info(GetSCFamily());
1866 1206 : if (sc_config)
1867 1206 : LocateServiceChain(rtinstance, *sc_config);
1868 : }
1869 1087 : RetryDelete();
1870 1087 : return true;
1871 : }
1872 :
1873 : template <typename T>
1874 369039 : void ServiceChainMgr<T>::RoutingInstanceCallback(string name, int op) {
1875 369039 : if (op != RoutingInstanceMgr::INSTANCE_DELETE)
1876 280004 : StartResolve();
1877 369039 : }
1878 :
1879 : template <typename T>
1880 282283 : void ServiceChainMgr<T>::StartResolve() {
1881 282283 : if (pending_chains_.empty())
1882 280151 : return;
1883 2132 : resolve_trigger_->Set();
1884 : }
1885 :
1886 : template <typename T>
1887 4526 : void ServiceChainMgr<T>::StopServiceChainDone(BgpTable *table,
1888 : ConditionMatch *info) {
1889 : // Post the RequestDone event to ServiceChain task to take Action
1890 13578 : ServiceChainRequestT *req =
1891 4526 : new ServiceChainRequestT(ServiceChainRequestT::STOP_CHAIN_DONE, table,
1892 9052 : NULL, PrefixT(), ServiceChainPtr(info));
1893 4526 : Enqueue(req);
1894 4526 : return;
1895 : }
1896 :
1897 : template <typename T>
1898 408691 : void ServiceChainMgr<T>::StopServiceChain(RoutingInstance *rtinstance) {
1899 408691 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
1900 :
1901 : // Remove the routing instance from pending chains list.
1902 408695 : std::scoped_lock lock(mutex_);
1903 408698 : if (ServiceChainIsPending(rtinstance)) {
1904 138 : ServiceChainGroup *group = FindServiceChainGroup(rtinstance);
1905 138 : if (group)
1906 72 : group->DeleteRoutingInstance(rtinstance);
1907 138 : DeletePendingServiceChain(rtinstance);
1908 138 : RetryDelete();
1909 : }
1910 :
1911 408698 : ServiceChainT *chain = FindServiceChain(rtinstance);
1912 408698 : if (!chain)
1913 406707 : return;
1914 :
1915 1991 : ServiceChainGroup *group = chain->group();
1916 1991 : if (group) {
1917 168 : group->DeleteRoutingInstance(rtinstance);
1918 168 : chain->clear_group();
1919 : }
1920 :
1921 1991 : if (chain->deleted())
1922 32 : return;
1923 :
1924 1959 : BgpConditionListener::RequestDoneCb cb =
1925 : bind(&ServiceChainMgr::StopServiceChainDone, this, _1, _2);
1926 1959 : listener_->RemoveMatchCondition(chain->dest_table(), chain, cb);
1927 1959 : listener_->RemoveMatchCondition(chain->connected_table(), chain, cb);
1928 408698 : }
1929 :
1930 : template <typename T>
1931 192 : void ServiceChainMgr<T>::UpdateServiceChain(RoutingInstance *rtinstance,
1932 : bool group_oper_state_up) {
1933 : // Bail if there's no service chain for the instance.
1934 192 : ServiceChainT *chain = FindServiceChain(rtinstance);
1935 192 : if (!chain)
1936 0 : return;
1937 :
1938 : // Update the state in the service chain.
1939 192 : chain->set_group_oper_state_up(group_oper_state_up);
1940 :
1941 : // Post event to ServiceChain task to update/delete all routes.
1942 : typename ServiceChainRequestT::RequestType req_type;
1943 192 : if (group_oper_state_up) {
1944 96 : req_type = ServiceChainRequestT::UPDATE_ALL_ROUTES;
1945 : } else {
1946 96 : req_type = ServiceChainRequestT::DELETE_ALL_ROUTES;
1947 : }
1948 192 : ServiceChainRequestT *req = new ServiceChainRequestT(
1949 384 : req_type, NULL, NULL, PrefixT(), ServiceChainPtr(chain));
1950 192 : Enqueue(req);
1951 : }
1952 :
1953 : template <typename T>
1954 4374 : void ServiceChainMgr<T>::UpdateServiceChainRoutes(ServiceChainT *chain,
1955 : const typename ServiceChainT::ConnectedPathIdList &old_path_ids) {
1956 : // Update ServiceChain routes for aggregates.
1957 : typename ServiceChainT::PrefixToRouteListMap *vn_prefix_list =
1958 4374 : chain->prefix_to_route_list_map();
1959 4374 : for (typename ServiceChainT::PrefixToRouteListMap::iterator it =
1960 16966 : vn_prefix_list->begin(); it != vn_prefix_list->end(); ++it) {
1961 8218 : if (!it->second.empty())
1962 1861 : chain->UpdateServiceChainRoute(it->first, NULL, old_path_ids, true);
1963 : }
1964 :
1965 : // Update ServiceChain routes for external connecting routes.
1966 4374 : for (typename ServiceChainT::ExtConnectRouteList::iterator it =
1967 4374 : chain->ext_connecting_routes()->begin();
1968 9975 : it != chain->ext_connecting_routes()->end(); ++it) {
1969 5601 : RouteT *ext_route = static_cast<RouteT *>(*it);
1970 5601 : chain->UpdateServiceChainRoute(
1971 : ext_route->GetPrefix(), ext_route, old_path_ids, false);
1972 : }
1973 4374 : }
1974 :
1975 : template <typename T>
1976 1827 : void ServiceChainMgr<T>::DeleteServiceChainRoutes(ServiceChainT *chain) {
1977 : // Delete ServiceChain routes for aggregates.
1978 : typename ServiceChainT::PrefixToRouteListMap *vn_prefix_list =
1979 1827 : chain->prefix_to_route_list_map();
1980 1827 : for (typename ServiceChainT::PrefixToRouteListMap::iterator it =
1981 6919 : vn_prefix_list->begin(); it != vn_prefix_list->end(); ++it) {
1982 3265 : chain->DeleteServiceChainRoute(it->first, true);
1983 : }
1984 :
1985 : // Delete ServiceChain routes for external connecting routes.
1986 1827 : for (typename ServiceChainT::ExtConnectRouteList::iterator it =
1987 1827 : chain->ext_connecting_routes()->begin();
1988 2665 : it != chain->ext_connecting_routes()->end(); ++it) {
1989 838 : RouteT *ext_route = static_cast<RouteT *>(*it);
1990 838 : chain->DeleteServiceChainRoute(ext_route->GetPrefix(), false);
1991 : }
1992 1827 : }
1993 :
1994 : template <typename T>
1995 548976 : void ServiceChainMgr<T>::PeerRegistrationCallback(IPeer *peer, BgpTable *table,
1996 : bool unregister) {
1997 548976 : CHECK_CONCURRENCY("bgp::PeerMembership");
1998 :
1999 : // Bail if it's not an XMPP peer.
2000 548976 : if (!peer->IsXmppPeer())
2001 0 : return;
2002 :
2003 : // Bail if there's no service chain for the instance.
2004 548976 : ServiceChainT *chain = FindServiceChain(table->routing_instance());
2005 548976 : if (!chain)
2006 535856 : return;
2007 :
2008 : // Post event to ServiceChain task to update all routes.
2009 39360 : ServiceChainRequestT *req =
2010 13120 : new ServiceChainRequestT(ServiceChainRequestT::UPDATE_ALL_ROUTES, NULL,
2011 26240 : NULL, PrefixT(), ServiceChainPtr(chain));
2012 13120 : Enqueue(req);
2013 : }
2014 :
2015 : template <typename T>
2016 32 : void ServiceChainMgr<T>::DisableResolveTrigger() {
2017 32 : resolve_trigger_->set_disable();
2018 32 : }
2019 :
2020 : template <typename T>
2021 32 : void ServiceChainMgr<T>::EnableResolveTrigger() {
2022 32 : resolve_trigger_->set_enable();
2023 32 : }
2024 :
2025 : template <typename T>
2026 16 : void ServiceChainMgr<T>::DisableGroupTrigger() {
2027 16 : group_trigger_->set_disable();
2028 16 : }
2029 :
2030 : template <typename T>
2031 16 : void ServiceChainMgr<T>::EnableGroupTrigger() {
2032 16 : group_trigger_->set_enable();
2033 16 : }
2034 :
2035 : template <typename T>
2036 1216 : uint32_t ServiceChainMgr<T>::GetDownServiceChainCount() const {
2037 1216 : uint32_t count = 0;
2038 1216 : for (ServiceChainMap::const_iterator it = chain_set_.begin();
2039 1296 : it != chain_set_.end(); ++it) {
2040 : const ServiceChainT *chain =
2041 80 : static_cast<const ServiceChainT *>(it->second.get());
2042 80 : if (!chain->IsConnectedRouteValid())
2043 48 : count++;
2044 : }
2045 1216 : return count;
2046 : }
2047 :
2048 : // Explicit instantiation of ServiceChainMgr for INET and INET6.
2049 : template class ServiceChainMgr<ServiceChainInet>;
2050 : template class ServiceChainMgr<ServiceChainInet6>;
2051 : template class ServiceChainMgr<ServiceChainEvpn>;
2052 : template class ServiceChainMgr<ServiceChainEvpn6>;
|