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