Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include <boost/uuid/uuid_io.hpp>
6 :
7 : #include <cmn/agent_cmn.h>
8 : #include <init/agent_param.h>
9 : #include <oper/ecmp_load_balance.h>
10 : #include <oper/route_common.h>
11 : #include <oper/peer.h>
12 : #include <oper/nexthop.h>
13 : #include <oper/peer.h>
14 : #include <oper/mirror_table.h>
15 : #include "oper/tunnel_nh.h"
16 :
17 : #include <controller/controller_vrf_export.h>
18 : #include <controller/controller_init.h>
19 : #include <controller/controller_export.h>
20 : #include <controller/controller_peer.h>
21 : #include <controller/controller_types.h>
22 :
23 72 : RouteExport::State::State() :
24 72 : DBState(), exported_(false), ingress_replication_exported_(false),
25 72 : multicast_exported_(false),
26 72 : force_chg_(false), label_(MplsTable::kInvalidLabel), vn_(), sg_list_(),
27 144 : tag_list_(), tunnel_type_(TunnelType::INVALID), path_preference_(),
28 216 : destination_(), source_(), ecmp_load_balance_(), isid_(0), tunnel_bmap_(0) {
29 72 : }
30 :
31 117 : bool RouteExport::State::Changed(const AgentRoute *route, const AgentPath *path) const {
32 117 : if (exported_ == false)
33 38 : return true;
34 :
35 79 : if (force_chg_ == true)
36 0 : return true;
37 :
38 79 : if (label_ != path->GetActiveLabel())
39 2 : return true;
40 :
41 77 : if (tunnel_type_ != path->tunnel_type()) {
42 0 : return true;
43 : };
44 :
45 77 : if (vn_ != path->dest_vn_name())
46 0 : return true;
47 :
48 77 : if (sg_list_ != path->sg_list())
49 15 : return true;
50 :
51 62 : if (tag_list_ != path->tag_list())
52 0 : return true;
53 :
54 62 : if (communities_ != path->communities())
55 0 : return true;
56 :
57 62 : if (path_preference_ != path->path_preference())
58 13 : return true;
59 :
60 49 : if(ecmp_load_balance_ != path->ecmp_load_balance())
61 0 : return true;
62 :
63 49 : if (etree_leaf_ != path->etree_leaf())
64 0 : return true;
65 :
66 49 : if (tunnel_bmap_ != path->tunnel_bmap())
67 0 : return true;
68 :
69 49 : return false;
70 : }
71 :
72 68 : void RouteExport::State::Update(const AgentRoute *route, const AgentPath *path) {
73 68 : force_chg_ = false;
74 68 : label_ = path->GetActiveLabel();
75 68 : vn_ = path->dest_vn_name();
76 68 : sg_list_ = path->sg_list();
77 68 : tag_list_ = path->tag_list();
78 68 : communities_ = path->communities();
79 68 : tunnel_type_ = path->tunnel_type();
80 68 : path_preference_ = path->path_preference();
81 68 : ecmp_load_balance_ = path->ecmp_load_balance();
82 68 : etree_leaf_ = path->etree_leaf();
83 68 : tunnel_bmap_ = path->tunnel_bmap();
84 68 : }
85 :
86 42 : RouteExport::RouteExport(AgentRouteTable *rt_table):
87 42 : rt_table_(rt_table), marked_delete_(false),
88 42 : table_delete_ref_(this, rt_table->deleter()) {
89 84 : walk_ref_ = rt_table->AllocWalker(
90 : boost::bind(&RouteExport::DeleteState, this, _1, _2),
91 42 : boost::bind(&RouteExport::Walkdone, _1, _2, this));
92 42 : }
93 :
94 42 : RouteExport::~RouteExport() {
95 42 : if (rt_table_) {
96 42 : rt_table_->Unregister(id_);
97 42 : rt_table_->ReleaseWalker(walk_ref_);
98 : }
99 42 : table_delete_ref_.Reset(NULL);
100 42 : }
101 :
102 42 : void RouteExport::ManagedDelete() {
103 42 : marked_delete_ = true;
104 42 : }
105 :
106 : // Route entry add/change/del notification handler
107 481 : void RouteExport::Notify(const Agent *agent,
108 : AgentXmppChannel *bgp_xmpp_peer,
109 : bool associate,
110 : Agent::RouteTableType type,
111 : DBTablePartBase *partition,
112 : DBEntryBase *e) {
113 481 : AgentRoute *route = static_cast<AgentRoute *>(e);
114 :
115 : // Primitive checks for non-delete notification
116 481 : if (!route->IsDeleted()) {
117 : // If there is no active BGP peer attached to channel, ignore
118 : // non-delete notification for this channel
119 363 : if (!AgentXmppChannel::IsBgpPeerActive(agent, bgp_xmpp_peer))
120 0 : return;
121 :
122 363 : if (route->GetActivePath()->inactive())
123 0 : return;
124 :
125 : // Extract the listener ID of active BGP peer for route table to which
126 : // this route entry belongs to. Listeners of route table can be active
127 : // bgp peers as well as decommisioned BGP peers(if they exist). Active
128 : // and decommisoned BGP peer can co-exist till cleanup timer is fired.
129 : // During this interval ignore notification for decommisioned bgp peer
130 : // listener id
131 363 : VrfEntry *vrf = route->vrf();
132 : BgpPeer *bgp_peer = static_cast<BgpPeer *>(bgp_xmpp_peer->
133 363 : bgp_peer_id());
134 363 : DBTableBase::ListenerId vrf_id = bgp_peer->GetVrfExportListenerId();
135 : VrfExport::State *vs =
136 363 : static_cast<VrfExport::State *>(vrf->GetState(vrf->get_table(),
137 : vrf_id));
138 : // If VRF state is not present then listener has not been added.
139 : // Addition of listener later will result in walk to notify all routes.
140 : // That in turn will add state as well by calling current routine.
141 : // Therefore return when empty VRF state is found.
142 363 : if (!vs)
143 0 : return;
144 :
145 : //Make sure that vrf has been subscribed before route is published.
146 363 : if (vs->IsExportable(bgp_xmpp_peer->sequence_number()))
147 0 : return;
148 :
149 : // There may be instances when decommisioned peer is not yet
150 : // unregistered while a new peer is already present. So there will be
151 : // two notifications. If its for decommisioned peer then ignore the same
152 : // by checking the listener id with active bgp peer listener id.
153 363 : DBTableBase::ListenerId id = vs->rt_export_[route->GetTableType()]->
154 363 : GetListenerId();
155 363 : if (id != id_)
156 0 : return;
157 : }
158 :
159 : //If channel is no more active, ignore any updates.
160 : //It may happen that notify is enqueued before channel is removed.
161 481 : if (!AgentXmppChannel::IsBgpPeerActive(agent, bgp_xmpp_peer))
162 0 : return;
163 :
164 481 : if (route->is_multicast()) {
165 54 : MulticastNotify(bgp_xmpp_peer, associate, partition, e);
166 : } else {
167 427 : UnicastNotify(bgp_xmpp_peer, partition, e, type);
168 : }
169 : }
170 :
171 427 : void RouteExport::UnicastNotify(AgentXmppChannel *bgp_xmpp_peer,
172 : DBTablePartBase *partition, DBEntryBase *e,
173 : Agent::RouteTableType type) {
174 427 : AgentRoute *route = static_cast<AgentRoute *>(e);
175 : //TODO Currently BRIDGE notifications are coming because multicast route
176 : //are installed in same. Once multicast route is shifted to EVPN table
177 : //then there will be no export from Bridge and check below can be removed.
178 427 : if (route->GetTableType() == Agent::BRIDGE)
179 180 : return;
180 :
181 247 : if (route->vrf()->ShouldExportRoute() == false) {
182 0 : return;
183 : }
184 :
185 : AgentRouteTable *table = static_cast<AgentRouteTable *>
186 247 : (partition->parent());
187 247 : State *state = static_cast<State *>(route->GetState(partition->parent(),
188 : id_));
189 247 : AgentPath *path = NULL;
190 247 : path = route->FindLocalVmPortPath();
191 :
192 247 : std::stringstream path_str;
193 247 : if (path && path->peer())
194 117 : path_str << path->peer()->GetName();
195 : else
196 130 : path_str << "None";
197 :
198 247 : if (marked_delete_) {
199 : //Ignore route updates on delete marked vrf
200 16 : goto done;
201 : }
202 : // if vrf is fabric vrf and route is not labelled inet route
203 : // then don't export the route
204 117 : if (path && (route->vrf()->GetName() == table->agent()->fabric_vrf_name())
205 348 : && (type != Agent::INET4_MPLS)) {
206 : //Dont export vhost IP path to control-node
207 : const InetUnicastRouteEntry *inet_rt =
208 0 : dynamic_cast<const InetUnicastRouteEntry *>(route);
209 0 : if (inet_rt && inet_rt->prefix_address() == table->agent()->router_id()) {
210 0 : path = NULL;
211 : }
212 : }
213 :
214 231 : if (!state && route->IsDeleted()) {
215 0 : goto done;
216 : }
217 :
218 231 : if (state == NULL) {
219 65 : state = new State();
220 65 : route->SetState(partition->parent(), id_, state);
221 : }
222 :
223 231 : if (path && !path->is_local()) {
224 117 : if (state->Changed(route, path)) {
225 68 : state->Update(route, path);
226 68 : VnListType vn_list;
227 68 : vn_list.insert(state->vn_);
228 68 : uint32_t label = state->label_;
229 68 : if ((route->vrf()->GetName() == table->agent()->fabric_vrf_name()) &&
230 68 : (label != MplsTable::kInvalidLabel )&& (label != MplsTable::kImplicitNullLabel )) {
231 0 : label = 0;
232 : }
233 68 : state->exported_ =
234 136 : AgentXmppChannel::ControllerSendRouteAdd(bgp_xmpp_peer,
235 : static_cast<AgentRoute * >(route),
236 : path->NexthopIp(table->agent()), vn_list,
237 : label, path->GetTunnelBmap(),
238 68 : &path->sg_list(), &path->tag_list(), &path->communities(),
239 68 : type, state->path_preference_,
240 68 : state->ecmp_load_balance_,
241 : path->native_vrf_id());
242 68 : }
243 : } else {
244 114 : if (state->exported_ == true) {
245 38 : VnListType vn_list;
246 38 : vn_list.insert(state->vn_);
247 38 : AgentXmppChannel::ControllerSendRouteDelete(bgp_xmpp_peer,
248 : static_cast<AgentRoute *>(route), vn_list,
249 38 : (state->tunnel_type_ == TunnelType::VXLAN ?
250 : state->label_ : 0),
251 : TunnelType::AllType(), NULL, NULL, NULL,
252 38 : type, state->path_preference_);
253 38 : state->exported_ = false;
254 38 : }
255 : }
256 76 : done:
257 247 : if (route->IsDeleted()) {
258 65 : if (state) {
259 63 : route->ClearState(partition->parent(), id_);
260 63 : delete state;
261 : }
262 : }
263 247 : }
264 :
265 123 : static const AgentPath *GetMulticastExportablePath(const Agent *agent,
266 : const AgentRoute *route) {
267 123 : const AgentPath *active_path = route->FindPath(agent->local_vm_peer());
268 :
269 : //OVS peer path
270 123 : if (active_path == NULL) {
271 : const EvpnRouteEntry *evpn_route =
272 31 : dynamic_cast<const EvpnRouteEntry *>(route);
273 31 : active_path = evpn_route ? evpn_route->FindOvsPath() : NULL;
274 : }
275 : //If no loca peer, then look for tor peer as that should also result
276 : //in export of route.
277 123 : if (active_path == NULL)
278 31 : active_path = route->FindPath(agent->multicast_tor_peer());
279 : //Subnet discard
280 123 : if (active_path == NULL) {
281 31 : const AgentPath *local_path = route->FindPath(agent->local_peer());
282 31 : if (local_path && !agent->tor_agent_enabled()) {
283 24 : return local_path;
284 : }
285 : }
286 :
287 99 : return active_path;
288 : }
289 :
290 54 : bool RouteExport::MulticastRouteCanDissociate(const AgentRoute *route) {
291 54 : bool can_dissociate = route->IsDeleted();
292 54 : Agent *agent = static_cast<AgentRouteTable*>(route->get_table())->
293 54 : agent();
294 54 : const AgentPath *local_path = route->FindPath(agent->local_peer());
295 :
296 54 : if (route->GetTableType() == Agent::INET4_MULTICAST) {
297 0 : const AgentPath *active_path = route->FindPath(agent->local_vm_peer());
298 0 : if (!active_path) {
299 0 : return true;
300 : }
301 0 : return can_dissociate;
302 : }
303 :
304 54 : if (local_path && !agent->tor_agent_enabled()) {
305 25 : return can_dissociate;
306 : }
307 29 : if (route->is_multicast()) {
308 29 : const AgentPath *active_path = GetMulticastExportablePath(agent, route);
309 29 : if (active_path == NULL)
310 7 : return true;
311 22 : const NextHop *nh = active_path ? active_path->ComputeNextHop(agent) : NULL;
312 22 : const CompositeNH *cnh = static_cast<const CompositeNH *>(nh);
313 22 : if (cnh && cnh->ComponentNHCount() == 0)
314 0 : return true;
315 : }
316 22 : return can_dissociate;
317 : }
318 :
319 47 : void RouteExport::SubscribeMulticastRouting(const Agent *agent,
320 : AgentXmppChannel *bgp_xmpp_peer,
321 : AgentRoute *route,
322 : RouteExport::State *state) {
323 47 : const AgentPath *active_path = GetMulticastExportablePath(agent, route);
324 : //Agent running as tor(simulate_evpn_tor) - dont subscribe
325 : //Route has path with peer OVS_PEER i.e. TOR agent mode - dont subscribe
326 : //Subscribe condition:
327 : //first time subscription or force change
328 94 : if (!(agent->simulate_evpn_tor()) &&
329 94 : (active_path->peer()->GetType() != Peer::OVS_PEER) &&
330 47 : ((state->multicast_exported_ == false) ||
331 40 : (state->force_chg_ == true))) {
332 : //TODO optimize by checking for force_chg? In other cases duplicate
333 : //request can be filtered.
334 8 : if (state->multicast_exported_ == true) {
335 : //Unsubscribe before re-sending subscription, this makes sure in any
336 : //corner case control-node does not see this as a duplicate request.
337 1 : AgentXmppChannel::ControllerSendMcastRouteDelete(bgp_xmpp_peer,
338 : route);
339 1 : state->multicast_exported_ = false;
340 : }
341 : //Sending 255.255.255.255 for fabric tree
342 8 : state->multicast_exported_ =
343 8 : AgentXmppChannel::ControllerSendMcastRouteAdd(bgp_xmpp_peer,
344 : route);
345 : }
346 47 : }
347 :
348 : // Handles subscription of multicast routes.
349 : // Following are the kind of subscription:
350 : // Fabric - For fabric replication tree
351 : // EVPN Ingress replication - For adding compute node in EVPN replication list.
352 : // TOR Ingress replication - For adding in TOR replication list (relevant for
353 : // TSN).
354 : //
355 : // For Tor-agent its a route with tunnel NH and there is no subscription.
356 54 : void RouteExport::MulticastNotify(AgentXmppChannel *bgp_xmpp_peer,
357 : bool associate,
358 : DBTablePartBase *partition,
359 : DBEntryBase *e) {
360 54 : Agent *agent = bgp_xmpp_peer->agent();
361 54 : AgentRoute *route = static_cast<AgentRoute *>(e);
362 54 : State *state = static_cast<State *>(route->GetState(partition->parent(), id_));
363 :
364 : // Bridge Multicast routes are not sent to control.
365 54 : BridgeRouteEntry *l2_route = dynamic_cast<BridgeRouteEntry *>(route);
366 108 : if ((route->GetTableType() == Agent::BRIDGE) &&
367 54 : (l2_route->prefix_address() != MacAddress::BroadcastMac())) {
368 :
369 0 : return;
370 : }
371 :
372 54 : bool route_can_be_dissociated = MulticastRouteCanDissociate(route);
373 :
374 54 : if (marked_delete_ && state) {
375 1 : route->ClearState(partition->parent(), id_);
376 1 : delete state;
377 1 : state = NULL;
378 1 : return;
379 : }
380 :
381 : //Handle withdraw for following cases:
382 : //- Route is not having any active multicast exportable path or is deleted.
383 : //- associate(false): Bgp Peer has gone down and state needs to be removed.
384 53 : if (route_can_be_dissociated || !associate) {
385 6 : if (state == NULL) {
386 0 : return;
387 : }
388 :
389 6 : if (state->multicast_exported_ == true) {
390 6 : AgentXmppChannel::ControllerSendMcastRouteDelete(bgp_xmpp_peer,
391 : route);
392 6 : state->multicast_exported_ = false;
393 : }
394 :
395 6 : if ((state->ingress_replication_exported_ == true)) {
396 3 : uint32_t label = state->label_;
397 3 : if (route->vrf()->IsPbbVrf()) {
398 0 : label = state->isid_;
399 : }
400 3 : state->tunnel_type_ = TunnelType::INVALID;
401 6 : AgentXmppChannel::ControllerSendEvpnRouteDelete(bgp_xmpp_peer,
402 : route,
403 3 : state->vn_,
404 : label,
405 3 : state->destination_,
406 3 : state->source_,
407 : TunnelType::AllType());
408 3 : state->ingress_replication_exported_ = false;
409 : }
410 :
411 6 : route->ClearState(partition->parent(), id_);
412 6 : delete state;
413 6 : state = NULL;
414 6 : return;
415 : }
416 :
417 47 : if (marked_delete_) {
418 : //Ignore route updates on delete marked vrf
419 0 : return;
420 : }
421 :
422 47 : if (state == NULL) {
423 7 : state = new State();
424 7 : route->SetState(partition->parent(), id_, state);
425 : }
426 :
427 : // Bridge Broadcast route is sent with 255.255.255.255 address to control.
428 : // Inet Multicast route can be because of MVPN or EVPN:
429 : // For EVPN: send route updates for ERMVPN and EVPN.
430 : // For MVPN: send route updates for ERMVPN on native VRF
431 : // and MVPN on ip-fabric VRF.
432 47 : if (route->vrf()->ShouldExportRoute()) {
433 47 : SubscribeMulticastRouting(agent, bgp_xmpp_peer, route, state);
434 : }
435 :
436 47 : bool ingress_replication = true;
437 47 : if ((route->GetTableType() == Agent::INET4_MULTICAST) &&
438 0 : (agent->params()->mvpn_ipv4_enable())) {
439 :
440 0 : ingress_replication = false;
441 : }
442 47 : if (ingress_replication) {
443 47 : SubscribeIngressReplication(agent, bgp_xmpp_peer, route, state);
444 : }
445 :
446 47 : state->force_chg_ = false;
447 47 : return;
448 : }
449 :
450 47 : void RouteExport::SubscribeIngressReplication(Agent *agent,
451 : AgentXmppChannel *bgp_xmpp_peer,
452 : AgentRoute *route,
453 : RouteExport::State *state) {
454 : //Check if bridging mode is enabled for this VN by verifying bridge flag
455 : //in local peer path.
456 47 : bool bridging = false;
457 47 : if (route->vrf() && route->vrf()->vn() && route->vrf()->vn()->bridging())
458 25 : bridging = true;
459 :
460 47 : const AgentPath *active_path = GetMulticastExportablePath(agent, route);
461 : //Sending ff:ff:ff:ff:ff:ff for evpn replication
462 47 : TunnelType::Type old_tunnel_type = state->tunnel_type_;
463 47 : uint32_t old_label = state->label_;
464 47 : TunnelType::Type new_tunnel_type = active_path->tunnel_type();
465 47 : uint32_t new_label = active_path->GetActiveLabel();
466 :
467 : //Evaluate if ingress replication subscription needs to be withdrawn.
468 : //Conditions: Tunnel type changed (VXLAN to MPLS) in turn label change,
469 : //bridging is disabled/enabled on VN
470 47 : if ((state->ingress_replication_exported_ == true) ||
471 26 : (state->force_chg_ == true)) {
472 21 : bool withdraw = false;
473 21 : uint32_t withdraw_label = 0;
474 :
475 21 : if (old_tunnel_type == TunnelType::VXLAN) {
476 8 : if ((new_tunnel_type != TunnelType::VXLAN) ||
477 : (old_label != new_label)) {
478 4 : withdraw_label = old_label;
479 4 : withdraw = true;
480 : }
481 13 : } else if (new_tunnel_type == TunnelType::VXLAN) {
482 3 : withdraw = true;
483 : }
484 :
485 21 : if (route->vrf()->IsPbbVrf()) {
486 0 : if (state->isid_ != active_path->vxlan_id()) {
487 0 : uint32_t old_isid = state->isid_;
488 0 : state->isid_ = active_path->vxlan_id();
489 0 : withdraw_label = old_isid;
490 0 : withdraw = true;
491 : } else {
492 0 : state->isid_ = active_path->vxlan_id();
493 : }
494 : }
495 :
496 21 : if (bridging == false)
497 0 : withdraw = true;
498 :
499 21 : if (withdraw) {
500 : AgentXmppChannel::ControllerSendEvpnRouteDelete
501 7 : (bgp_xmpp_peer, route, state->vn_, withdraw_label,
502 7 : state->destination_, state->source_,
503 7 : state->tunnel_type_);
504 7 : state->ingress_replication_exported_ = false;
505 : }
506 : }
507 :
508 47 : state->isid_ = active_path->vxlan_id();
509 : //Update state values with new values if there is any change.
510 : //Also force change same i.e. update.
511 47 : if (active_path->tunnel_type() != state->tunnel_type_) {
512 13 : state->force_chg_ = true;
513 13 : state->tunnel_type_ = active_path->tunnel_type();
514 : }
515 :
516 47 : if (active_path->GetActiveLabel() != state->label_) {
517 14 : state->force_chg_ = true;
518 14 : state->label_ = active_path->GetActiveLabel();
519 : }
520 :
521 47 : if (route->vrf()->IsPbbVrf()) {
522 0 : if (active_path->vxlan_id() == 0) {
523 0 : return;
524 : }
525 : }
526 :
527 : //Subcribe if:
528 : //- Bridging is enabled
529 : //- First time (ingress_replication_exported is false)
530 : //- Forced Change
531 47 : if (bridging &&
532 25 : ((state->ingress_replication_exported_ == false) ||
533 14 : (state->force_chg_ == true))) {
534 12 : state->label_ = active_path->GetActiveLabel();
535 12 : state->vn_ = route->dest_vn_name();
536 : const TunnelNH *tnh =
537 12 : dynamic_cast<const TunnelNH *>(active_path->nexthop());
538 12 : if (tnh) {
539 0 : state->destination_ = tnh->GetDip()->to_string();
540 0 : state->source_ = tnh->GetSip()->to_string();
541 : }
542 12 : SecurityGroupList sg;
543 12 : TagList tag;
544 12 : state->ingress_replication_exported_ =
545 : AgentXmppChannel::ControllerSendEvpnRouteAdd
546 12 : (bgp_xmpp_peer, route,
547 : active_path->NexthopIp(agent),
548 12 : route->dest_vn_name(), state->label_,
549 : TunnelType::GetTunnelBmap(state->tunnel_type_),
550 12 : &sg, &tag, NULL, state->destination_,
551 24 : state->source_, PathPreference());
552 12 : }
553 : }
554 :
555 2 : bool RouteExport::DeleteState(DBTablePartBase *partition,
556 : DBEntryBase *entry) {
557 : State *state = static_cast<State *>
558 2 : (entry->GetState(partition->parent(), id_));
559 2 : if (state) {
560 2 : entry->ClearState(partition->parent(), id_);
561 2 : delete state;
562 : }
563 2 : return true;
564 : }
565 :
566 42 : void RouteExport::Walkdone(DBTable::DBTableWalkRef walk_ref,
567 : DBTableBase *partition,
568 : RouteExport *rt_export) {
569 42 : delete rt_export;
570 42 : }
571 :
572 42 : void RouteExport::Unregister() {
573 : //Start unregister process
574 42 : rt_table_->WalkTable(walk_ref_);
575 42 : }
576 :
577 42 : RouteExport* RouteExport::Init(AgentRouteTable *table,
578 : AgentXmppChannel *bgp_xmpp_peer) {
579 42 : RouteExport *rt_export = new RouteExport(table);
580 42 : bool associate = true;
581 84 : rt_export->id_ = table->Register(boost::bind(&RouteExport::Notify,
582 : rt_export, table->agent(), bgp_xmpp_peer,
583 42 : associate, table->GetTableType(), _1, _2));
584 42 : return rt_export;
585 : }
586 :
|