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 8955 : static int GetOriginVnIndex(const BgpTable *table, const BgpRoute *route) {
45 8955 : const BgpPath *path = route->BestPath();
46 8956 : if (!path)
47 0 : return 0;
48 :
49 8956 : const BgpAttr *attr = path->GetAttr();
50 8955 : const ExtCommunity *ext_community = attr->ext_community();
51 8955 : if (ext_community) {
52 26800 : BOOST_FOREACH(const ExtCommunity::ExtCommunityValue &comm,
53 : ext_community->communities()) {
54 11953 : if (!ExtCommunity::is_origin_vn(comm))
55 8928 : continue;
56 3025 : OriginVn origin_vn(comm);
57 3026 : return origin_vn.vn_index();
58 : }
59 : }
60 5927 : if (path->IsVrfOriginated())
61 5911 : 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 17414 : 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 17414 : RoutingInstance *src_ri = src_routing_instance();
94 17414 : IpAddress addr = prefix.addr();
95 17414 : int plen = prefix.prefixlen();
96 17414 : if (GetSCFamily() == SCAddress::EVPN) {
97 : /**
98 : * EVPN route carrying inet prefix
99 : */
100 4327 : table = src_ri->GetTable(Address::INET);
101 4327 : Ip4Prefix inet_prefix = Ip4Prefix(addr.to_v4(), plen);
102 4327 : InetRoute inet_route(inet_prefix);
103 4327 : partition = static_cast<DBTablePartition *>
104 4327 : (table->GetTablePartition(&inet_route));
105 4327 : route = static_cast<BgpRoute *>(partition->Find(&inet_route));
106 4327 : if (create) {
107 : /**
108 : * Create case. Create route if not found.
109 : */
110 1913 : if (route == NULL) {
111 1635 : route = new InetRoute(inet_prefix);
112 1635 : partition->Add(route);
113 : } else {
114 278 : route->ClearDelete();
115 : }
116 : }
117 17414 : } else if (GetSCFamily() == SCAddress::EVPN6) {
118 : /**
119 : * EVPN route carrying inet6 prefix
120 : */
121 4319 : table = src_ri->GetTable(Address::INET6);
122 4319 : Inet6Prefix inet6_prefix = Inet6Prefix(addr.to_v6(), plen);
123 4319 : Inet6Route inet6_route(inet6_prefix);
124 4319 : partition = static_cast<DBTablePartition *>
125 4319 : (table->GetTablePartition(&inet6_route));
126 4319 : route = static_cast<BgpRoute *>(partition->Find(&inet6_route));
127 4319 : if (create) {
128 : /**
129 : * Create case. Create route if not found.
130 : */
131 1906 : if (route == NULL) {
132 1635 : route = new Inet6Route(inet6_prefix);
133 1635 : partition->Add(route);
134 : } else {
135 271 : route->ClearDelete();
136 : }
137 : }
138 4319 : } else {
139 : /**
140 : * INET/INET6 prefix at head end of service-chain.
141 : * Replicate route to <vrf>.evpn.0 table.
142 : */
143 8768 : table = src_ri->GetTable(Address::EVPN);
144 8768 : string type_rd_tag("5-0:0-0-");
145 17536 : string prefix_str = type_rd_tag + addr.to_string() + "/" +
146 : boost::lexical_cast<std::string>(plen);
147 8768 : EvpnPrefix evpn_prefix(EvpnPrefix::FromString(prefix_str));
148 8768 : EvpnRoute evpn_route(evpn_prefix);
149 8768 : partition = static_cast<DBTablePartition *>
150 8768 : (table->GetTablePartition(&evpn_route));
151 8768 : route = static_cast<BgpRoute *>(partition->Find(&evpn_route));
152 8768 : if (create) {
153 : /**
154 : * Create case. Create route if not found.
155 : */
156 3933 : if (route == NULL) {
157 3327 : route = new EvpnRoute(evpn_prefix);
158 3327 : partition->Add(route);
159 : } else {
160 606 : route->ClearDelete();
161 : }
162 : }
163 8768 : }
164 17414 : }
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 18542 : 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 18542 : route->FindPath(BgpPath::ServiceChain, NULL, path_id);
184 18542 : uint32_t label = path->GetLabel();
185 18542 : 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 18542 : if (bgptable->family() == Address::EVPN) {
192 : const RoutingInstance *conn_ri =
193 16468 : bgptable->server()->routing_instance_mgr()->GetRoutingInstance(
194 : RoutingInstanceMgr::GetPrimaryRoutingInstanceName(
195 8234 : connected_->name()));
196 8234 : if (!conn_ri) {
197 : // conn_ri is not expected to be found only in unit tests.
198 8234 : assert(bgp_log_test::unit_test());
199 8234 : conn_ri = connected_routing_instance();
200 : }
201 8234 : label = conn_ri->vxlan_id();
202 8234 : 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 18542 : if (existing_path != NULL) {
211 : // Existing path can be reused.
212 7287 : if ((attr.get() == existing_path->GetAttr()) &&
213 7287 : (path->GetLabel() == existing_path->GetLabel()) &&
214 2137 : (path->GetFlags() == existing_path->GetFlags())) {
215 2137 : return;
216 : }
217 :
218 : /**
219 : * Remove existing path, new path will be added below.
220 : */
221 2041 : path_updated = true;
222 2041 : 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 32810 : BgpPath *new_path =
230 16405 : new BgpPath(path_id, BgpPath::ServiceChain, attr.get(),
231 16405 : path->GetFlags(), label);
232 16405 : route->InsertPath(new_path);
233 16405 : partition->Notify(route);
234 :
235 16405 : 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 38972 : explicit DeleteActor(ServiceChainMgr<T> *manager)
248 38972 : : LifetimeActor(manager->server_->lifetime_manager()),
249 38972 : manager_(manager) {
250 38972 : }
251 40950 : virtual bool MayDelete() const {
252 40950 : return manager_->MayDelete();
253 : }
254 38556 : virtual void Shutdown() {
255 38556 : }
256 38556 : virtual void Destroy() {
257 38556 : manager_->Terminate();
258 38556 : }
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 23421 : BgpTable *ServiceChain<T>::src_table() const {
315 23421 : return src_->GetTable(GetFamily());
316 : }
317 :
318 : template <typename T>
319 36543 : BgpTable *ServiceChain<T>::connected_table() const {
320 36543 : return connected_->GetTable(GetConnectedFamily());
321 : }
322 :
323 : template <typename T>
324 63613 : BgpTable *ServiceChain<T>::dest_table() const {
325 63613 : 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 54441 : bool ServiceChain<T>::Match(BgpServer *server, BgpTable *table, BgpRoute *route,
373 : bool deleted) {
374 54441 : CHECK_CONCURRENCY("db::DBTable");
375 :
376 : typename ServiceChainRequestT::RequestType type;
377 54447 : PrefixT aggregate_match;
378 :
379 54438 : 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 27078 : if (GetFamily() == Address::EVPN) {
383 11152 : if (!IsEvpnType5Route(route)) {
384 0 : return (false);
385 : }
386 : }
387 :
388 : // Skip connected routes
389 27070 : if (IsConnectedRoute(route)) {
390 369 : return false;
391 : }
392 :
393 : // Skip aggregate routes
394 26724 : if (aggregate_enable() && IsAggregate(route))
395 32 : return false;
396 :
397 26679 : if (aggregate_enable() && IsMoreSpecific(route, &aggregate_match)) {
398 : // More specific
399 2995 : if (deleted) {
400 1357 : type = ServiceChainRequestT::MORE_SPECIFIC_DELETE;
401 : } else {
402 1638 : type = ServiceChainRequestT::MORE_SPECIFIC_ADD_CHG;
403 : }
404 : } else {
405 : // External connecting routes
406 23679 : if (!deleted) {
407 8955 : if (!route->BestPath() || !route->BestPath()->IsFeasible()) {
408 0 : deleted = true;
409 : } else {
410 8956 : const BgpAttr *attr = route->BestPath()->GetAttr();
411 8956 : const Community *comm = attr ? attr->community() : NULL;
412 8956 : if (comm) {
413 3857 : if ((comm->ContainsValue(CommunityType::NoAdvertise)) ||
414 1920 : (comm->ContainsValue(CommunityType::NoReOriginate)))
415 32 : deleted = true;
416 : }
417 :
418 8955 : int vn_index = GetOriginVnIndex(table, route);
419 8952 : int src_vn_index = src_->virtual_network_index();
420 8952 : int dest_vn_index = dest_->virtual_network_index();
421 8952 : if (!vn_index || dest_vn_index != vn_index) {
422 1954 : if (src_vn_index == vn_index)
423 1202 : deleted = true;
424 1954 : if (!dest_->virtual_network_allow_transit())
425 1445 : deleted = true;
426 1954 : if (!dest_vn_index)
427 0 : deleted = true;
428 : }
429 :
430 8952 : const OriginVnPath *ovnpath =
431 8952 : attr ? attr->origin_vn_path() : NULL;
432 8952 : if (ovnpath && ovnpath->Contains(
433 : server->autonomous_system(), src_vn_index)) {
434 1179 : deleted = true;
435 : }
436 : }
437 : }
438 :
439 23676 : if (deleted) {
440 16309 : type = ServiceChainRequestT::EXT_CONNECT_ROUTE_DELETE;
441 : } else {
442 7367 : type = ServiceChainRequestT::EXT_CONNECT_ROUTE_ADD_CHG;
443 : }
444 : }
445 27359 : } else if ((table == connected_table()) &&
446 54694 : !connected_table_unregistered() &&
447 27329 : IsConnectedRoute(route, true)) {
448 : // Connected routes from source table
449 5131 : if (!deleted) {
450 6792 : if (!route->IsValid() ||
451 3396 : route->BestPath()->GetSource() != BgpPath::BGP_XMPP) {
452 0 : deleted = true;
453 : }
454 : }
455 :
456 : // Connected route for service chain
457 5131 : if (deleted) {
458 1735 : type = ServiceChainRequestT::CONNECTED_ROUTE_DELETE;
459 : } else {
460 3396 : type = ServiceChainRequestT::CONNECTED_ROUTE_ADD_CHG;
461 : }
462 : } else {
463 22234 : return false;
464 : }
465 :
466 31802 : BgpConditionListener *listener = manager_->GetListener();
467 : ServiceChainState *state = static_cast<ServiceChainState *>(
468 31801 : listener->GetMatchState(table, route, this));
469 31859 : 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 12412 : if (!state) {
474 8965 : state = new ServiceChainState(ServiceChainPtr(this));
475 8964 : 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 19447 : if (state == NULL) {
481 : // Not seen ADD ignore DELETE
482 6247 : 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 25612 : 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 51224 : ServiceChainRequestT *req = new ServiceChainRequestT(
496 51222 : type, table, route, aggregate_match, ServiceChainPtr(this));
497 25611 : manager_->Enqueue(req);
498 25612 : 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 5063 : void ServiceChain<T>::SetConnectedRoute(BgpRoute *connected) {
508 5063 : connected_route_ = connected;
509 5063 : connected_path_ids_.clear();
510 5063 : if (!connected_route_)
511 1732 : return;
512 :
513 3331 : for (Route::PathList::iterator it = connected->GetPathList().begin();
514 16320 : it != connected->GetPathList().end(); ++it) {
515 4917 : BgpPath *path = static_cast<BgpPath *>(it.operator->());
516 :
517 : // Infeasible paths are not considered.
518 4917 : if (!path->IsFeasible())
519 88 : break;
520 :
521 : // Bail if it's not ECMP with the best path.
522 4913 : if (connected_route_->BestPath()->PathCompare(*path, true))
523 84 : break;
524 :
525 : // Use nexthop attribute of connected path as path id.
526 4829 : uint32_t path_id = path->GetAttr()->nexthop().to_v4().to_ulong();
527 4829 : connected_path_ids_.insert(path_id);
528 : }
529 : }
530 :
531 : template <typename T>
532 9085 : bool ServiceChain<T>::IsConnectedRouteValid() const {
533 9085 : return (connected_route_ && connected_route_->IsValid());
534 : }
535 :
536 : template <typename T>
537 23241 : bool ServiceChain<T>::IsMoreSpecific(BgpRoute *route,
538 : PrefixT *aggregate_match) const {
539 23241 : const RouteT *ip_route = static_cast<RouteT *>(route);
540 23241 : const PrefixT &ip_prefix = ip_route->GetPrefix();
541 23238 : for (typename PrefixToRouteListMap::const_iterator it =
542 23240 : prefix_to_route_list_map()->begin();
543 61548 : it != prefix_to_route_list_map()->end(); ++it) {
544 41306 : if (ip_prefix.IsMoreSpecific(it->first)) {
545 2995 : *aggregate_match = it->first;
546 2995 : return true;
547 : }
548 : }
549 20243 : return false;
550 : }
551 :
552 : template <typename T>
553 23286 : bool ServiceChain<T>::IsAggregate(BgpRoute *route) const {
554 23286 : RouteT *ip_route = dynamic_cast<RouteT *>(route);
555 23288 : for (typename PrefixToRouteListMap::const_iterator it =
556 23286 : prefix_to_route_list_map()->begin();
557 66348 : it != prefix_to_route_list_map()->end(); ++it) {
558 43101 : if (it->first == ip_route->GetPrefix())
559 32 : return true;
560 : }
561 23244 : 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 54396 : bool ServiceChain<T>::IsConnectedRoute(BgpRoute *route,
571 : bool is_conn_table) const {
572 54396 : if (is_conn_table && GetFamily() == Address::EVPN) {
573 10894 : if (GetConnectedFamily() == Address::INET) {
574 5439 : InetRoute *inet_route = dynamic_cast<InetRoute *>(route);
575 5439 : return (service_chain_addr() == inet_route->GetPrefix().addr());
576 : } else {
577 5453 : Inet6Route *inet6_route = dynamic_cast<Inet6Route *>(route);
578 5453 : return (service_chain_addr() == inet6_route->GetPrefix().addr());
579 : }
580 : } else {
581 : /**
582 : * Non-EVPN and EVPN destination table case
583 : */
584 43501 : RouteT *ip_route = dynamic_cast<RouteT *>(route);
585 43501 : 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 11152 : bool ServiceChain<T>::IsEvpnType5Route(BgpRoute *route) const {
597 11152 : if (GetFamily() != Address::EVPN) {
598 0 : return false;
599 : }
600 :
601 11152 : EvpnRoute *evpn_route = static_cast<EvpnRoute *>(route);
602 11152 : EvpnPrefix prefix = evpn_route->GetPrefix();
603 11147 : if (prefix.type() != EvpnPrefix::IpPrefixRoute) {
604 0 : return false;
605 : }
606 16714 : if (GetSCFamily() == SCAddress::EVPN &&
607 5567 : prefix.family() == Address::INET6) {
608 0 : return false;
609 : }
610 16727 : if (GetSCFamily() == SCAddress::EVPN6 &&
611 5580 : prefix.family() == Address::INET) {
612 0 : return false;
613 : }
614 11144 : return true;
615 : }
616 :
617 : template <typename T>
618 13200 : void ServiceChain<T>::RemoveMatchState(BgpRoute *route,
619 : ServiceChainState *state) {
620 13200 : 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 13110 : state->set_deleted();
625 : }
626 13200 : }
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 11171 : void ServiceChain<T>::DeleteServiceChainRoute(PrefixT prefix, bool aggregate) {
653 :
654 11171 : 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 11171 : if (is_sc_head() || GetFamily() != Address::EVPN) {
663 11171 : BgpTable *bgptable = src_table();
664 11171 : RouteT rt_key(prefix);
665 : DBTablePartition *partition =
666 : static_cast<DBTablePartition *>(bgptable->
667 11171 : GetTablePartition(&rt_key));
668 : BgpRoute *service_chain_route =
669 11171 : static_cast<BgpRoute *>(partition->Find(&rt_key));
670 :
671 11171 : if (service_chain_route && !service_chain_route->IsDeleted()) {
672 7035 : DeleteServiceChainRouteInternal(service_chain_route, partition,
673 : bgptable, aggregate);
674 : }
675 11171 : }
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 11171 : if (is_sc_head() || GetFamily() == Address::EVPN) {
684 : BgpTable *repl_table;
685 : BgpRoute *repl_sc_route;
686 : DBTablePartition *repl_partition;
687 9662 : GetReplicationFamilyInfo(repl_partition, repl_sc_route, repl_table,
688 : prefix, false);
689 9662 : if (repl_sc_route && !repl_sc_route->IsDeleted()) {
690 6605 : DeleteServiceChainRouteInternal(repl_sc_route, repl_partition,
691 : repl_table, aggregate);
692 : }
693 : }
694 11171 : }
695 :
696 : template <typename T>
697 13640 : void ServiceChain<T>::DeleteServiceChainRouteInternal(
698 : BgpRoute *service_chain_route,
699 : DBTablePartition *partition,
700 : BgpTable *bgptable,
701 : bool aggregate) {
702 13640 : CHECK_CONCURRENCY("bgp::ServiceChain");
703 :
704 13640 : for (ConnectedPathIdList::const_iterator it = GetConnectedPathIds().begin();
705 27517 : it != GetConnectedPathIds().end(); ++it) {
706 13877 : uint32_t path_id = *it;
707 13877 : service_chain_route->RemovePath(BgpPath::ServiceChain, NULL, path_id);
708 13877 : 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 13640 : if (!service_chain_route->HasPaths()) {
716 13243 : partition->Delete(service_chain_route);
717 : } else {
718 397 : partition->Notify(service_chain_route);
719 : }
720 13640 : }
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 9699 : void ServiceChain<T>::UpdateServiceChainRoute(PrefixT prefix,
747 : const RouteT *orig_route, const ConnectedPathIdList &old_path_ids,
748 : bool aggregate) {
749 :
750 9699 : 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 9699 : if (is_sc_head() || GetFamily() != Address::EVPN) {
758 9699 : BgpTable *bgptable = src_table();
759 9699 : RouteT rt_key(prefix);
760 : DBTablePartition *partition =
761 : static_cast<DBTablePartition *>
762 9699 : (bgptable->GetTablePartition(&rt_key));
763 : BgpRoute *service_chain_route =
764 9699 : static_cast<BgpRoute *>(partition->Find(&rt_key));
765 :
766 9699 : if (service_chain_route == NULL) {
767 7126 : service_chain_route = new RouteT(prefix);
768 7126 : partition->Add(service_chain_route);
769 : } else {
770 2573 : service_chain_route->ClearDelete();
771 : }
772 :
773 9699 : UpdateServiceChainRouteInternal(orig_route, old_path_ids,
774 : service_chain_route, partition,
775 : bgptable, aggregate);
776 9699 : }
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 9699 : if (is_sc_head() || GetFamily() == Address::EVPN) {
785 : BgpTable *repl_table;
786 : BgpRoute *repl_sc_route;
787 : DBTablePartition *repl_partition;
788 7752 : GetReplicationFamilyInfo(repl_partition, repl_sc_route, repl_table,
789 : prefix, true);
790 7752 : UpdateServiceChainRouteInternal(orig_route, old_path_ids,
791 : repl_sc_route, repl_partition,
792 : repl_table, aggregate);
793 : }
794 9699 : }
795 :
796 : template <typename T>
797 17451 : 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 17451 : CHECK_CONCURRENCY("bgp::ServiceChain");
801 :
802 17451 : int vn_index = dest_routing_instance()->virtual_network_index();
803 17451 : BgpServer *server = dest_routing_instance()->server();
804 17451 : OriginVn origin_vn(server->autonomous_system(), vn_index);
805 17451 : OriginVn origin_vn4(server->autonomous_system(), AS_TRANS);
806 17451 : OriginVn origin_vn_trans(AS_TRANS, vn_index);
807 17451 : const OriginVnPath::OriginVnValue origin_vn_bytes = origin_vn.GetExtCommunity();
808 17451 : const OriginVnPath::OriginVnValue origin_vn_trans_bytes =
809 17451 : origin_vn_trans.GetExtCommunity();
810 17451 : const OriginVnPath::OriginVnValue origin_vn4_bytes =
811 17451 : origin_vn4.GetExtCommunity();
812 :
813 17451 : SiteOfOrigin soo;
814 17451 : ExtCommunity::ExtCommunityList sgid_list;
815 17451 : LargeCommunity::LargeCommunityList tag_list;
816 17451 : LoadBalance load_balance;
817 17451 : bool load_balance_present = false;
818 17451 : const Community *orig_community = nullptr;
819 17451 : const OriginVnPath *orig_ovnpath = nullptr;
820 17451 : const AsPath *orig_aspath = nullptr;
821 17451 : RouteDistinguisher orig_rd;
822 17451 : if (orig_route) {
823 13586 : const BgpPath *orig_path = orig_route->BestPath();
824 13586 : const BgpAttr *orig_attr = nullptr;
825 13586 : const ExtCommunity *ext_community = nullptr;
826 13586 : const LargeCommunity *large_community = nullptr;
827 13586 : if (orig_path != nullptr)
828 13529 : orig_attr = orig_path->GetAttr();
829 13586 : if (orig_attr != nullptr) {
830 13529 : orig_community = orig_attr->community();
831 13529 : ext_community = orig_attr->ext_community();
832 13529 : large_community = orig_attr->large_community();
833 13529 : orig_ovnpath = orig_attr->origin_vn_path();
834 13529 : orig_rd = orig_attr->source_rd();
835 13529 : orig_aspath = orig_attr->as_path();
836 : }
837 13586 : if (ext_community != nullptr) {
838 21344 : for(const ExtCommunity::ExtCommunityValue &comm:
839 13497 : ext_community->communities()) {
840 7847 : if (ExtCommunity::is_security_group(comm))
841 2656 : sgid_list.push_back(comm);
842 7847 : if (ExtCommunity::is_site_of_origin(comm) && soo.IsNull())
843 161 : soo = SiteOfOrigin(comm);
844 7847 : if (ExtCommunity::is_load_balance(comm)) {
845 128 : load_balance = LoadBalance(comm);
846 128 : load_balance_present = true;
847 : }
848 : }
849 : }
850 13586 : if (large_community != nullptr) {
851 13561 : 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 17451 : BgpAttrDB *attr_db = server->attr_db();
860 17451 : CommunityDB *comm_db = server->comm_db();
861 17451 : CommunityPtr new_community = comm_db->AppendAndLocate(
862 : orig_community, CommunityType::AcceptOwnNexthop);
863 17451 : ExtCommunityDB *extcomm_db = server->extcomm_db();
864 17451 : LargeCommunityDB *largecomm_db = server->largecomm_db();
865 17451 : BgpMembershipManager *membership_mgr = server->membership_mgr();
866 17451 : OriginVnPathDB *ovnpath_db = server->ovnpath_db();
867 17451 : OriginVnPathPtr new_ovnpath;
868 17451 : 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 17451 : new_ovnpath = ovnpath_db->PrependAndLocate(
875 : orig_ovnpath, origin_vn_bytes);
876 : }
877 :
878 17451 : ConnectedPathIdList new_path_ids;
879 36761 : for (Route::PathList::iterator it =
880 17451 : connected_route()->GetPathList().begin();
881 75660 : it != connected_route()->GetPathList().end(); ++it) {
882 20563 : BgpPath *connected_path = static_cast<BgpPath *>(it.operator->());
883 :
884 : // Infeasible paths are not considered
885 20563 : if (!connected_path->IsFeasible())
886 184 : break;
887 :
888 : // take snapshot of all ECMP paths
889 20547 : 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 20379 : if (connected_route()->DuplicateForwardingPath(connected_path))
897 1837 : continue;
898 :
899 18798 : const BgpAttr *attr = connected_path->GetAttr();
900 :
901 18798 : ExtCommunityPtr new_ext_community;
902 18798 : LargeCommunityPtr new_large_community;
903 :
904 : // Strip any RouteTargets from the connected attributes.
905 18798 : new_ext_community = extcomm_db->ReplaceRTargetAndLocate(
906 37596 : 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 18798 : if (is_sc_head() && bgptable->family() == Address::EVPN) {
920 8242 : ExtCommunity::ExtCommunityList export_list;
921 : const RoutingInstance *conn_ri =
922 16484 : server->routing_instance_mgr()->GetRoutingInstance(
923 : RoutingInstanceMgr::GetPrimaryRoutingInstanceName(
924 8242 : connected_->name()));
925 8242 : if (!conn_ri) {
926 : // conn_ri is not expected to be found only in unit tests.
927 8242 : assert(bgp_log_test::unit_test());
928 8242 : conn_ri = connected_routing_instance();
929 : }
930 8242 : 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 24726 : BOOST_FOREACH(const RouteTarget &rtarget,
934 : conn_ri->GetExportList()) {
935 8242 : if (ExtCommunity::get_rtarget_val(
936 16484 : 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 8242 : new_ext_community = extcomm_db->AppendAndLocate(
943 : new_ext_community.get(), export_list);
944 8242 : }
945 :
946 : // Replace the SGID list with the list from the original route.
947 18798 : 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 18798 : 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 18798 : if (soo.IsNull()) {
956 18571 : new_ext_community = extcomm_db->RemoveSiteOfOriginAndLocate(
957 : new_ext_community.get());
958 : } else {
959 227 : new_ext_community = extcomm_db->ReplaceSiteOfOriginAndLocate(
960 227 : 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 18798 : 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 18798 : 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 18798 : 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 18798 : if (is_sc_head() && bgptable->family() == Address::EVPN) {
987 8242 : ExtCommunity::ExtCommunityList encaps_list;
988 8242 : vector<string> tunnel_encaps = boost::assign::list_of("vxlan");
989 24726 : BOOST_FOREACH(string encap, tunnel_encaps) {
990 8242 : encaps_list.push_back(TunnelEncap(encap).GetExtCommunity());
991 : }
992 8242 : new_ext_community = extcomm_db->
993 : ReplaceTunnelEncapsulationAndLocate(new_ext_community.get(),
994 : encaps_list);
995 8242 : }
996 :
997 : // Replace extended community, community and origin vn path.
998 18798 : BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(
999 : attr, new_ext_community);
1000 18798 : new_attr =
1001 : attr_db->ReplaceLargeCommunityAndLocate(new_attr.get(),
1002 : new_large_community);
1003 18798 : new_attr =
1004 : attr_db->ReplaceCommunityAndLocate(new_attr.get(), new_community);
1005 18798 : 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 18798 : if (retain_as_path() && orig_aspath) {
1012 16 : new_attr = attr_db->ReplaceAsPathAndLocate(new_attr.get(),
1013 : orig_aspath);
1014 : } else {
1015 18782 : new_attr = attr_db->ReplaceAsPathAndLocate(new_attr.get(),
1016 37564 : 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 18798 : const IPeer *peer = connected_path->GetPeer();
1024 18798 : if (src_ != connected_ && peer && peer->IsXmppPeer()) {
1025 1625 : int instance_id = -1;
1026 1625 : bool is_registered = membership_mgr->GetRegistrationInfo(peer,
1027 : bgptable, &instance_id);
1028 1625 : if (!is_registered)
1029 240 : continue;
1030 1385 : RouteDistinguisher connected_rd = attr->source_rd();
1031 1385 : if (connected_rd.Type() != RouteDistinguisher::TypeIpAddressBased)
1032 0 : continue;
1033 :
1034 1385 : RouteDistinguisher rd(connected_rd.GetAddress(), instance_id);
1035 1385 : 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 18558 : if (connected_path->IsReplicated()) {
1041 13246 : const BgpSecondaryPath *spath =
1042 : static_cast<const BgpSecondaryPath *>(connected_path);
1043 13246 : const RoutingInstance *ri = spath->src_table()->routing_instance();
1044 13246 : if (ri->IsMasterRoutingInstance()) {
1045 : const VpnRouteT *vpn_route =
1046 8773 : static_cast<const VpnRouteT *>(spath->src_rt());
1047 8773 : new_attr = attr_db->ReplaceSourceRdAndLocate(new_attr.get(),
1048 8773 : vpn_route->GetPrefix().route_distinguisher());
1049 : }
1050 : }
1051 :
1052 : // Skip paths with Source RD same as source RD of the connected path
1053 18558 : 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 18542 : uint32_t path_id =
1058 18542 : connected_path->GetAttr()->nexthop().to_v4().to_ulong();
1059 18542 : ProcessServiceChainPath(path_id, connected_path, new_attr,
1060 : service_chain_route, partition,
1061 : aggregate, bgptable);
1062 18542 : new_path_ids.insert(path_id);
1063 : }
1064 :
1065 : // Remove stale paths.
1066 17451 : for (ConnectedPathIdList::const_iterator it = old_path_ids.begin();
1067 22081 : it != old_path_ids.end(); ++it) {
1068 4630 : uint32_t path_id = *it;
1069 4630 : if (new_path_ids.find(path_id) != new_path_ids.end())
1070 3917 : continue;
1071 713 : service_chain_route->RemovePath(BgpPath::ServiceChain, NULL, path_id);
1072 713 : partition->Notify(service_chain_route);
1073 :
1074 713 : 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 17451 : if (!service_chain_route->HasPaths())
1084 206 : partition->Delete(service_chain_route);
1085 17451 : }
1086 :
1087 : template <typename T>
1088 1639 : bool ServiceChain<T>::AddMoreSpecific(PrefixT aggregate,
1089 : BgpRoute *more_specific) {
1090 : typename PrefixToRouteListMap::iterator it =
1091 1639 : prefix_to_routelist_map_.find(aggregate);
1092 1639 : assert(it != prefix_to_routelist_map_.end());
1093 1639 : bool ret = false;
1094 1639 : if (it->second.empty()) {
1095 : // Add the aggregate for the first time
1096 1032 : ret = true;
1097 : }
1098 1639 : it->second.insert(more_specific);
1099 1639 : 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 747 : void ServiceChainGroup::UpdateOperState() {
1226 747 : bool old_oper_state_up = oper_state_up_;
1227 747 : oper_state_up_ = true;
1228 1730 : BOOST_FOREACH(RoutingInstance *rtinstance, chain_set_) {
1229 839 : if (manager_->ServiceChainIsUp(rtinstance))
1230 144 : continue;
1231 695 : oper_state_up_ = false;
1232 695 : break;
1233 : }
1234 :
1235 747 : if (old_oper_state_up == oper_state_up_)
1236 651 : 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 70773 : BgpConditionListener *ServiceChainMgr<T>::GetListener() {
1253 70773 : return server_->condition_listener(GetSCFamily());
1254 : }
1255 :
1256 : template <typename T>
1257 43450 : bool ServiceChainMgr<T>::RequestHandler(ServiceChainRequestT *req) {
1258 43450 : CHECK_CONCURRENCY("bgp::ServiceChain");
1259 43450 : BgpTable *table = NULL;
1260 43450 : BgpRoute *route = NULL;
1261 43450 : PrefixT aggregate_match = req->aggregate_match_;
1262 43450 : ServiceChainT *info = NULL;
1263 :
1264 43450 : table = req->table_;
1265 43450 : route = req->rt_;
1266 43450 : info = static_cast<ServiceChainT *>(req->info_.get());
1267 :
1268 : // Table where the aggregate route needs to be added
1269 43450 : aggregate_match = req->aggregate_match_;
1270 :
1271 43450 : ServiceChainState *state = NULL;
1272 43450 : if (route) {
1273 : state = static_cast<ServiceChainState *>
1274 25612 : (listener_->GetMatchState(table, route, info));
1275 : }
1276 :
1277 43450 : switch (req->type_) {
1278 1639 : case ServiceChainRequestT::MORE_SPECIFIC_ADD_CHG: {
1279 1639 : assert(state);
1280 1639 : if (state->deleted()) {
1281 0 : state->reset_deleted();
1282 : }
1283 1639 : if (!info->AddMoreSpecific(aggregate_match, route))
1284 607 : break;
1285 1032 : if (!info->IsConnectedRouteValid())
1286 604 : break;
1287 428 : if (!info->group_oper_state_up())
1288 7 : break;
1289 :
1290 421 : typename ServiceChainT::ConnectedPathIdList path_ids;
1291 421 : info->UpdateServiceChainRoute(
1292 : aggregate_match, NULL, path_ids, true);
1293 421 : break;
1294 421 : }
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 3396 : case ServiceChainRequestT::CONNECTED_ROUTE_ADD_CHG: {
1305 3396 : assert(state);
1306 6735 : if (route->IsDeleted() || !route->BestPath() ||
1307 3339 : !route->BestPath()->IsFeasible()) {
1308 65 : break;
1309 : }
1310 3331 : UpdateServiceChainGroup(info->group());
1311 :
1312 3331 : if (state->deleted()) {
1313 0 : state->reset_deleted();
1314 : }
1315 :
1316 : // Store the old path id list and populate the new one.
1317 3331 : typename ServiceChainT::ConnectedPathIdList path_ids =
1318 3331 : info->GetConnectedPathIds();
1319 3331 : info->SetConnectedRoute(route);
1320 :
1321 3331 : if (!info->group_oper_state_up())
1322 84 : break;
1323 :
1324 3247 : UpdateServiceChainRoutes(info, path_ids);
1325 3247 : break;
1326 3331 : }
1327 1732 : case ServiceChainRequestT::CONNECTED_ROUTE_DELETE: {
1328 1732 : assert(state);
1329 1732 : UpdateServiceChainGroup(info->group());
1330 1732 : DeleteServiceChainRoutes(info);
1331 1732 : info->RemoveMatchState(route, state);
1332 1732 : info->SetConnectedRoute(NULL);
1333 1732 : break;
1334 : }
1335 7377 : case ServiceChainRequestT::EXT_CONNECT_ROUTE_ADD_CHG: {
1336 7377 : assert(state);
1337 7377 : if (state->deleted()) {
1338 16 : state->reset_deleted();
1339 : }
1340 7377 : info->ext_connecting_routes()->insert(route);
1341 7377 : if (!info->IsConnectedRouteValid())
1342 5449 : break;
1343 1928 : if (!info->group_oper_state_up())
1344 22 : break;
1345 1906 : RouteT *ext_route = dynamic_cast<RouteT *>(route);
1346 1906 : typename ServiceChainT::ConnectedPathIdList path_ids;
1347 1906 : info->UpdateServiceChainRoute(
1348 : ext_route->GetPrefix(), ext_route, path_ids, false);
1349 1906 : break;
1350 1906 : }
1351 10108 : case ServiceChainRequestT::EXT_CONNECT_ROUTE_DELETE: {
1352 10108 : assert(state);
1353 10108 : if (info->ext_connecting_routes()->erase(route)) {
1354 6008 : RouteT *inet_route = dynamic_cast<RouteT *>(route);
1355 6008 : info->DeleteServiceChainRoute(inet_route->GetPrefix(), false);
1356 : }
1357 10108 : info->RemoveMatchState(route, state);
1358 10108 : 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 12242 : break;
1367 974 : if (!info->group_oper_state_up())
1368 0 : break;
1369 :
1370 974 : typename ServiceChainT::ConnectedPathIdList path_ids =
1371 974 : info->GetConnectedPathIds();
1372 974 : UpdateServiceChainRoutes(info, path_ids);
1373 974 : break;
1374 974 : }
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 2226 : 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 2140 : 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 43450 : if (state) {
1406 25612 : state->DecrementRefCnt();
1407 25612 : if (state->refcnt() == 0 && state->deleted()) {
1408 8965 : listener_->RemoveMatchState(table, route, info);
1409 8965 : delete state;
1410 8965 : if (!info->num_matchstate()) {
1411 1817 : if (info->dest_table_unregistered()) {
1412 123 : listener_->UnregisterMatchCondition(
1413 : info->dest_table(), info);
1414 : }
1415 1817 : if (info->connected_table_unregistered()) {
1416 37 : listener_->UnregisterMatchCondition(
1417 : info->connected_table(), info);
1418 : }
1419 1817 : if (info->unregistered()) {
1420 16 : chain_set_.erase(info->src_routing_instance());
1421 16 : StartResolve();
1422 : }
1423 : }
1424 : }
1425 : }
1426 43450 : delete req;
1427 43450 : return true;
1428 : }
1429 :
1430 : template <typename T>
1431 38972 : ServiceChainMgr<T>::ServiceChainMgr(BgpServer *server)
1432 38972 : : server_(server),
1433 38972 : listener_(GetListener()),
1434 77944 : resolve_trigger_(new TaskTrigger(
1435 : bind(&ServiceChainMgr::ResolvePendingServiceChain, this),
1436 77944 : TaskScheduler::GetInstance()->GetTaskId("bgp::Config"), 0)),
1437 77944 : group_trigger_(new TaskTrigger(
1438 : bind(&ServiceChainMgr::ProcessServiceChainGroups, this),
1439 77944 : TaskScheduler::GetInstance()->GetTaskId("bgp::ServiceChain"), 0)),
1440 38972 : aggregate_host_route_(false),
1441 38972 : deleter_(new DeleteActor(this)),
1442 155888 : server_delete_ref_(this, server->deleter()) {
1443 38972 : 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 77944 : process_queue_.reset(
1449 38972 : new WorkQueue<ServiceChainRequestT *>(service_chain_task_id_, 0,
1450 : bind(&ServiceChainMgr::RequestHandler, this, _1)));
1451 :
1452 38972 : id_ = server->routing_instance_mgr()->RegisterInstanceOpCallback(
1453 : bind(&ServiceChainMgr::RoutingInstanceCallback, this, _1, _2));
1454 :
1455 38972 : BgpMembershipManager *membership_mgr = server->membership_mgr();
1456 38972 : registration_id_ = membership_mgr->RegisterPeerRegistrationCallback(
1457 : bind(&ServiceChainMgr::PeerRegistrationCallback, this, _1, _2, _3));
1458 38972 : }
1459 :
1460 : template <typename T>
1461 77944 : ServiceChainMgr<T>::~ServiceChainMgr() {
1462 38972 : assert(group_set_.empty());
1463 38972 : assert(group_map_.empty());
1464 116916 : }
1465 :
1466 : template <typename T>
1467 38556 : void ServiceChainMgr<T>::Terminate() {
1468 38556 : process_queue_->Shutdown();
1469 38556 : RoutingInstanceMgr *ri_mgr = server_->routing_instance_mgr();
1470 38556 : ri_mgr->UnregisterInstanceOpCallback(id_);
1471 38556 : BgpMembershipManager *membership_mgr = server_->membership_mgr();
1472 38556 : membership_mgr->UnregisterPeerRegistrationCallback(registration_id_);
1473 38556 : server_delete_ref_.Reset(NULL);
1474 38556 : }
1475 :
1476 : template <typename T>
1477 38556 : void ServiceChainMgr<T>::ManagedDelete() {
1478 38556 : deleter_->Delete();
1479 38556 : }
1480 :
1481 : template <typename T>
1482 40950 : bool ServiceChainMgr<T>::MayDelete() const {
1483 40950 : if (!chain_set_.empty() || !pending_chains_.empty())
1484 2378 : return false;
1485 38572 : if (!group_set_.empty() || !group_map_.empty())
1486 16 : return false;
1487 38556 : return true;
1488 : }
1489 :
1490 : template <typename T>
1491 6737 : void ServiceChainMgr<T>::RetryDelete() {
1492 6737 : if (!deleter_->IsDeleted())
1493 3051 : return;
1494 3686 : deleter_->RetryDelete();
1495 : }
1496 :
1497 : template <typename T>
1498 3650 : ServiceChainGroup *ServiceChainMgr<T>::FindServiceChainGroup(
1499 : RoutingInstance *rtinstance) {
1500 3650 : if (ServiceChainIsPending(rtinstance)) {
1501 514 : PendingChainState state = GetPendingServiceChain(rtinstance);
1502 514 : return state.group;
1503 514 : } else {
1504 3136 : const ServiceChain<T> *chain = FindServiceChain(rtinstance);
1505 3136 : 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 6327 : void ServiceChainMgr<T>::UpdateServiceChainGroup(ServiceChainGroup *group) {
1533 6327 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper", "bgp::ServiceChain");
1534 :
1535 6327 : if (!group)
1536 4903 : return;
1537 1424 : group_set_.insert(group);
1538 1424 : group_trigger_->Set();
1539 : }
1540 :
1541 : template <typename T>
1542 987 : bool ServiceChainMgr<T>::ProcessServiceChainGroups() {
1543 987 : CHECK_CONCURRENCY("bgp::ServiceChain");
1544 :
1545 3089 : BOOST_FOREACH(ServiceChainGroup *group, group_set_) {
1546 1051 : if (group->empty()) {
1547 304 : string temp_group_name(group->name());
1548 304 : group_map_.erase(temp_group_name);
1549 304 : } else {
1550 747 : group->UpdateOperState();
1551 : }
1552 : }
1553 :
1554 987 : group_set_.clear();
1555 987 : RetryDelete();
1556 987 : return true;
1557 : }
1558 :
1559 : /**
1560 : * Address Family - used for GetTable().
1561 : */
1562 : template <>
1563 47524 : Address::Family ServiceChainMgr<ServiceChainInet>::GetFamily() const {
1564 47524 : return Address::INET;
1565 : }
1566 :
1567 : template <>
1568 46983 : Address::Family ServiceChainMgr<ServiceChainInet6>::GetFamily() const {
1569 46983 : return Address::INET6;
1570 : }
1571 :
1572 : template <>
1573 35462 : Address::Family ServiceChainMgr<ServiceChainEvpn>::GetFamily() const {
1574 35462 : return Address::EVPN;
1575 : }
1576 :
1577 : template <>
1578 35505 : Address::Family ServiceChainMgr<ServiceChainEvpn6>::GetFamily() const {
1579 35505 : return Address::EVPN;
1580 : }
1581 :
1582 : /**
1583 : * Connected Table Family. For EVPN, it is INET or INET6.
1584 : */
1585 : template <>
1586 11805 : Address::Family ServiceChainMgr<ServiceChainInet>::GetConnectedFamily() const {
1587 11805 : return Address::INET;
1588 : }
1589 :
1590 : template <>
1591 11519 : Address::Family ServiceChainMgr<ServiceChainInet6>::GetConnectedFamily() const {
1592 11519 : return Address::INET6;
1593 : }
1594 :
1595 : template <>
1596 13161 : Address::Family ServiceChainMgr<ServiceChainEvpn>::GetConnectedFamily() const {
1597 13161 : return Address::INET;
1598 : }
1599 :
1600 : template <>
1601 13190 : Address::Family ServiceChainMgr<ServiceChainEvpn6>::GetConnectedFamily() const {
1602 13190 : return Address::INET6;
1603 : }
1604 :
1605 : /**
1606 : * Service Chain Family
1607 : */
1608 : template <>
1609 28237 : SCAddress::Family ServiceChainMgr<ServiceChainInet>::GetSCFamily() const {
1610 28237 : return SCAddress::INET;
1611 : }
1612 :
1613 : template <>
1614 28047 : SCAddress::Family ServiceChainMgr<ServiceChainInet6>::GetSCFamily() const {
1615 28047 : return SCAddress::INET6;
1616 : }
1617 :
1618 : template <>
1619 32780 : SCAddress::Family ServiceChainMgr<ServiceChainEvpn>::GetSCFamily() const {
1620 32780 : return SCAddress::EVPN;
1621 : }
1622 :
1623 : template <>
1624 37128 : SCAddress::Family ServiceChainMgr<ServiceChainEvpn6>::GetSCFamily() const {
1625 37128 : return SCAddress::EVPN6;
1626 : }
1627 :
1628 : template <>
1629 : template <typename T>
1630 43449 : void ServiceChainMgr<T>::Enqueue(ServiceChainRequestT *req) {
1631 43449 : process_queue_->Enqueue(req);
1632 43450 : }
1633 :
1634 : template <typename T>
1635 417791 : bool ServiceChainMgr<T>::ServiceChainIsPending(RoutingInstance *rtinstance,
1636 : string *reason) const {
1637 : typename PendingChainList::const_iterator loc =
1638 417791 : pending_chains_.find(rtinstance);
1639 417790 : if (loc != pending_chains_.end()) {
1640 1455 : if (reason)
1641 192 : *reason = loc->second.reason;
1642 1455 : return true;
1643 : }
1644 416336 : return false;
1645 : }
1646 :
1647 : template <typename T>
1648 1479 : bool ServiceChainMgr<T>::ServiceChainIsUp(RoutingInstance *rtinstance) const {
1649 1479 : if (ServiceChainIsPending(rtinstance))
1650 419 : return false;
1651 1060 : const ServiceChain<T> *service_chain = FindServiceChain(rtinstance);
1652 1060 : if (!service_chain)
1653 608 : return false;
1654 452 : 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 3993 : bool ServiceChainMgr<T>::LocateServiceChain(RoutingInstance *rtinstance,
1677 : const ServiceChainConfig &config) {
1678 3993 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
1679 :
1680 : // Verify whether the entry already exists.
1681 3993 : std::scoped_lock lock(mutex_);
1682 3993 : ServiceChainMap::iterator it = chain_set_.find(rtinstance);
1683 3993 : 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 3144 : RoutingInstanceMgr *mgr = server_->routing_instance_mgr();
1722 3144 : RoutingInstance *dest = mgr->GetRoutingInstance(config.routing_instance);
1723 :
1724 : // Dissociate from the old ServiceChainGroup.
1725 3144 : ServiceChainGroup *group = FindServiceChainGroup(rtinstance);
1726 3144 : 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 3144 : DeletePendingServiceChain(rtinstance);
1735 :
1736 : // Locate the new ServiceChainGroup.
1737 3144 : if (config.service_chain_id.empty()) {
1738 2592 : 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 3144 : if (!dest) {
1746 710 : string reason = "Destination routing instance does not exist";
1747 710 : AddPendingServiceChain(rtinstance, group, reason);
1748 710 : return false;
1749 710 : }
1750 :
1751 : // Destination routing instance is being deleted.
1752 2434 : 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 2434 : 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 2402 : RoutingInstance *connected_ri = NULL;
1766 2402 : if (config.source_routing_instance == "") {
1767 32 : connected_ri = rtinstance;
1768 32 : assert(!rtinstance->deleted());
1769 : } else {
1770 2370 : connected_ri = mgr->GetRoutingInstance(config.source_routing_instance);
1771 : }
1772 :
1773 : // Connected routing instance does not exist.
1774 2402 : if (!connected_ri) {
1775 3 : string reason = "Connected routing instance does not exist";
1776 3 : AddPendingServiceChain(rtinstance, group, reason);
1777 3 : return false;
1778 3 : }
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 3993 : }
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 962424 : ServiceChain<T> *ServiceChainMgr<T>::FindServiceChain(
1844 : RoutingInstance *rtinstance) const {
1845 962424 : ServiceChainMap::const_iterator it = chain_set_.find(rtinstance);
1846 962426 : if (it == chain_set_.end())
1847 946351 : return NULL;
1848 16075 : ServiceChainT *chain = static_cast<ServiceChainT *>(it->second.get());
1849 16075 : return chain;
1850 : }
1851 :
1852 : template <typename T>
1853 1086 : bool ServiceChainMgr<T>::ResolvePendingServiceChain() {
1854 1086 : CHECK_CONCURRENCY("bgp::Config");
1855 2269 : for (typename PendingChainList::iterator it = pending_chains_.begin(), next;
1856 2269 : it != pending_chains_.end(); it = next) {
1857 1183 : next = it;
1858 1183 : ++next;
1859 1183 : RoutingInstance *rtinstance = it->first;
1860 1183 : ServiceChainGroup *group = it->second.group;
1861 1183 : if (group)
1862 248 : group->DeleteRoutingInstance(rtinstance);
1863 1183 : pending_chains_.erase(it);
1864 : const ServiceChainConfig *sc_config =
1865 1183 : rtinstance->config()->service_chain_info(GetSCFamily());
1866 1183 : if (sc_config)
1867 1183 : LocateServiceChain(rtinstance, *sc_config);
1868 : }
1869 1086 : RetryDelete();
1870 1086 : return true;
1871 : }
1872 :
1873 : template <typename T>
1874 369192 : void ServiceChainMgr<T>::RoutingInstanceCallback(string name, int op) {
1875 369192 : if (op != RoutingInstanceMgr::INSTANCE_DELETE)
1876 280137 : StartResolve();
1877 369193 : }
1878 :
1879 : template <typename T>
1880 282415 : void ServiceChainMgr<T>::StartResolve() {
1881 282415 : if (pending_chains_.empty())
1882 280386 : return;
1883 2030 : 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 408835 : void ServiceChainMgr<T>::StopServiceChain(RoutingInstance *rtinstance) {
1899 408835 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
1900 :
1901 : // Remove the routing instance from pending chains list.
1902 408838 : std::scoped_lock lock(mutex_);
1903 408838 : 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 408838 : ServiceChainT *chain = FindServiceChain(rtinstance);
1912 408838 : if (!chain)
1913 406847 : 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 408838 : }
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 4221 : 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 4221 : chain->prefix_to_route_list_map();
1959 4221 : for (typename ServiceChainT::PrefixToRouteListMap::iterator it =
1960 16418 : vn_prefix_list->begin(); it != vn_prefix_list->end(); ++it) {
1961 7976 : if (!it->second.empty())
1962 1828 : chain->UpdateServiceChainRoute(it->first, NULL, old_path_ids, true);
1963 : }
1964 :
1965 : // Update ServiceChain routes for external connecting routes.
1966 4221 : for (typename ServiceChainT::ExtConnectRouteList::iterator it =
1967 4221 : chain->ext_connecting_routes()->begin();
1968 9765 : it != chain->ext_connecting_routes()->end(); ++it) {
1969 5544 : RouteT *ext_route = static_cast<RouteT *>(*it);
1970 5544 : chain->UpdateServiceChainRoute(
1971 : ext_route->GetPrefix(), ext_route, old_path_ids, false);
1972 : }
1973 4221 : }
1974 :
1975 : template <typename T>
1976 1828 : void ServiceChainMgr<T>::DeleteServiceChainRoutes(ServiceChainT *chain) {
1977 : // Delete ServiceChain routes for aggregates.
1978 : typename ServiceChainT::PrefixToRouteListMap *vn_prefix_list =
1979 1828 : chain->prefix_to_route_list_map();
1980 1828 : for (typename ServiceChainT::PrefixToRouteListMap::iterator it =
1981 6919 : vn_prefix_list->begin(); it != vn_prefix_list->end(); ++it) {
1982 3263 : chain->DeleteServiceChainRoute(it->first, true);
1983 : }
1984 :
1985 : // Delete ServiceChain routes for external connecting routes.
1986 1828 : for (typename ServiceChainT::ExtConnectRouteList::iterator it =
1987 1828 : chain->ext_connecting_routes()->begin();
1988 2696 : it != chain->ext_connecting_routes()->end(); ++it) {
1989 868 : RouteT *ext_route = static_cast<RouteT *>(*it);
1990 868 : chain->DeleteServiceChainRoute(ext_route->GetPrefix(), false);
1991 : }
1992 1828 : }
1993 :
1994 : template <typename T>
1995 549056 : void ServiceChainMgr<T>::PeerRegistrationCallback(IPeer *peer, BgpTable *table,
1996 : bool unregister) {
1997 549056 : CHECK_CONCURRENCY("bgp::PeerMembership");
1998 :
1999 : // Bail if it's not an XMPP peer.
2000 549056 : if (!peer->IsXmppPeer())
2001 0 : return;
2002 :
2003 : // Bail if there's no service chain for the instance.
2004 549056 : ServiceChainT *chain = FindServiceChain(table->routing_instance());
2005 549056 : if (!chain)
2006 535936 : 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 1388 : uint32_t ServiceChainMgr<T>::GetDownServiceChainCount() const {
2037 1388 : uint32_t count = 0;
2038 1388 : for (ServiceChainMap::const_iterator it = chain_set_.begin();
2039 1468 : 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 1388 : 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>;
|