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 62 : RouteExport::State::State() :
24 62 : DBState(), exported_(false), ingress_replication_exported_(false),
25 62 : multicast_exported_(false),
26 62 : force_chg_(false), label_(MplsTable::kInvalidLabel), vn_(), sg_list_(),
27 124 : tag_list_(), tunnel_type_(TunnelType::INVALID), path_preference_(),
28 186 : destination_(), source_(), ecmp_load_balance_(), isid_(0), tunnel_bmap_(0) {
29 62 : }
30 :
31 112 : bool RouteExport::State::Changed(const AgentRoute *route, const AgentPath *path) const {
32 112 : if (exported_ == false)
33 35 : return true;
34 :
35 77 : if (force_chg_ == true)
36 0 : return true;
37 :
38 77 : if (label_ != path->GetActiveLabel())
39 2 : return true;
40 :
41 75 : if (tunnel_type_ != path->tunnel_type()) {
42 0 : return true;
43 : };
44 :
45 75 : if (vn_ != path->dest_vn_name())
46 0 : return true;
47 :
48 75 : if (sg_list_ != path->sg_list())
49 15 : return true;
50 :
51 60 : if (tag_list_ != path->tag_list())
52 0 : return true;
53 :
54 60 : if (communities_ != path->communities())
55 0 : return true;
56 :
57 60 : if (path_preference_ != path->path_preference())
58 13 : return true;
59 :
60 47 : if(ecmp_load_balance_ != path->ecmp_load_balance())
61 0 : return true;
62 :
63 47 : if (etree_leaf_ != path->etree_leaf())
64 0 : return true;
65 :
66 47 : if (tunnel_bmap_ != path->tunnel_bmap())
67 0 : return true;
68 :
69 47 : return false;
70 : }
71 :
72 65 : void RouteExport::State::Update(const AgentRoute *route, const AgentPath *path) {
73 65 : force_chg_ = false;
74 65 : label_ = path->GetActiveLabel();
75 65 : vn_ = path->dest_vn_name();
76 65 : sg_list_ = path->sg_list();
77 65 : tag_list_ = path->tag_list();
78 65 : communities_ = path->communities();
79 65 : tunnel_type_ = path->tunnel_type();
80 65 : path_preference_ = path->path_preference();
81 65 : ecmp_load_balance_ = path->ecmp_load_balance();
82 65 : etree_leaf_ = path->etree_leaf();
83 65 : tunnel_bmap_ = path->tunnel_bmap();
84 65 : }
85 :
86 36 : RouteExport::RouteExport(AgentRouteTable *rt_table):
87 36 : rt_table_(rt_table), marked_delete_(false),
88 36 : table_delete_ref_(this, rt_table->deleter()) {
89 72 : walk_ref_ = rt_table->AllocWalker(
90 : boost::bind(&RouteExport::DeleteState, this, _1, _2),
91 36 : boost::bind(&RouteExport::Walkdone, _1, _2, this));
92 36 : }
93 :
94 36 : RouteExport::~RouteExport() {
95 36 : if (rt_table_) {
96 36 : rt_table_->Unregister(id_);
97 36 : rt_table_->ReleaseWalker(walk_ref_);
98 : }
99 36 : table_delete_ref_.Reset(NULL);
100 36 : }
101 :
102 36 : void RouteExport::ManagedDelete() {
103 36 : marked_delete_ = true;
104 36 : }
105 :
106 : // Route entry add/change/del notification handler
107 432 : 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 432 : AgentRoute *route = static_cast<AgentRoute *>(e);
114 :
115 : // Primitive checks for non-delete notification
116 432 : if (!route->IsDeleted()) {
117 : // If there is no active BGP peer attached to channel, ignore
118 : // non-delete notification for this channel
119 330 : if (!AgentXmppChannel::IsBgpPeerActive(agent, bgp_xmpp_peer))
120 0 : return;
121 :
122 330 : 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 330 : VrfEntry *vrf = route->vrf();
132 : BgpPeer *bgp_peer = static_cast<BgpPeer *>(bgp_xmpp_peer->
133 330 : bgp_peer_id());
134 330 : DBTableBase::ListenerId vrf_id = bgp_peer->GetVrfExportListenerId();
135 : VrfExport::State *vs =
136 330 : 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 330 : if (!vs)
143 0 : return;
144 :
145 : //Make sure that vrf has been subscribed before route is published.
146 330 : 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 330 : DBTableBase::ListenerId id = vs->rt_export_[route->GetTableType()]->
154 330 : GetListenerId();
155 330 : 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 432 : if (!AgentXmppChannel::IsBgpPeerActive(agent, bgp_xmpp_peer))
162 0 : return;
163 :
164 432 : if (route->is_multicast()) {
165 47 : MulticastNotify(bgp_xmpp_peer, associate, partition, e);
166 : } else {
167 385 : UnicastNotify(bgp_xmpp_peer, partition, e, type);
168 : }
169 : }
170 :
171 385 : void RouteExport::UnicastNotify(AgentXmppChannel *bgp_xmpp_peer,
172 : DBTablePartBase *partition, DBEntryBase *e,
173 : Agent::RouteTableType type) {
174 385 : 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 385 : if (route->GetTableType() == Agent::BRIDGE)
179 163 : return;
180 :
181 222 : if (route->vrf()->ShouldExportRoute() == false) {
182 0 : return;
183 : }
184 :
185 : AgentRouteTable *table = static_cast<AgentRouteTable *>
186 222 : (partition->parent());
187 222 : State *state = static_cast<State *>(route->GetState(partition->parent(),
188 : id_));
189 222 : AgentPath *path = NULL;
190 222 : path = route->FindLocalVmPortPath();
191 :
192 222 : std::stringstream path_str;
193 222 : if (path && path->peer())
194 112 : path_str << path->peer()->GetName();
195 : else
196 110 : path_str << "None";
197 :
198 222 : if (marked_delete_) {
199 : //Ignore route updates on delete marked vrf
200 10 : goto done;
201 : }
202 : // if vrf is fabric vrf and route is not labelled inet route
203 : // then don't export the route
204 112 : if (path && (route->vrf()->GetName() == table->agent()->fabric_vrf_name())
205 324 : && (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 212 : if (!state && route->IsDeleted()) {
215 0 : goto done;
216 : }
217 :
218 212 : if (state == NULL) {
219 56 : state = new State();
220 56 : route->SetState(partition->parent(), id_, state);
221 : }
222 :
223 212 : if (path && !path->is_local()) {
224 112 : if (state->Changed(route, path)) {
225 65 : state->Update(route, path);
226 65 : VnListType vn_list;
227 65 : vn_list.insert(state->vn_);
228 65 : uint32_t label = state->label_;
229 65 : if ((route->vrf()->GetName() == table->agent()->fabric_vrf_name()) &&
230 65 : (label != MplsTable::kInvalidLabel )&& (label != MplsTable::kImplicitNullLabel )) {
231 0 : label = 0;
232 : }
233 65 : state->exported_ =
234 130 : AgentXmppChannel::ControllerSendRouteAdd(bgp_xmpp_peer,
235 : static_cast<AgentRoute * >(route),
236 : path->NexthopIp(table->agent()), vn_list,
237 : label, path->GetTunnelBmap(),
238 65 : &path->sg_list(), &path->tag_list(), &path->communities(),
239 65 : type, state->path_preference_,
240 65 : state->ecmp_load_balance_,
241 : path->native_vrf_id());
242 65 : }
243 : } else {
244 100 : if (state->exported_ == true) {
245 35 : VnListType vn_list;
246 35 : vn_list.insert(state->vn_);
247 35 : AgentXmppChannel::ControllerSendRouteDelete(bgp_xmpp_peer,
248 : static_cast<AgentRoute *>(route), vn_list,
249 35 : (state->tunnel_type_ == TunnelType::VXLAN ?
250 : state->label_ : 0),
251 : TunnelType::AllType(), NULL, NULL, NULL,
252 35 : type, state->path_preference_);
253 35 : state->exported_ = false;
254 35 : }
255 : }
256 65 : done:
257 222 : if (route->IsDeleted()) {
258 56 : if (state) {
259 55 : route->ClearState(partition->parent(), id_);
260 55 : delete state;
261 : }
262 : }
263 222 : }
264 :
265 110 : static const AgentPath *GetMulticastExportablePath(const Agent *agent,
266 : const AgentRoute *route) {
267 110 : const AgentPath *active_path = route->FindPath(agent->local_vm_peer());
268 :
269 : //OVS peer path
270 110 : if (active_path == NULL) {
271 : const EvpnRouteEntry *evpn_route =
272 22 : dynamic_cast<const EvpnRouteEntry *>(route);
273 22 : 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 110 : if (active_path == NULL)
278 22 : active_path = route->FindPath(agent->multicast_tor_peer());
279 : //Subnet discard
280 110 : if (active_path == NULL) {
281 22 : const AgentPath *local_path = route->FindPath(agent->local_peer());
282 22 : if (local_path && !agent->tor_agent_enabled()) {
283 16 : return local_path;
284 : }
285 : }
286 :
287 94 : return active_path;
288 : }
289 :
290 47 : bool RouteExport::MulticastRouteCanDissociate(const AgentRoute *route) {
291 47 : bool can_dissociate = route->IsDeleted();
292 47 : Agent *agent = static_cast<AgentRouteTable*>(route->get_table())->
293 47 : agent();
294 47 : const AgentPath *local_path = route->FindPath(agent->local_peer());
295 :
296 47 : 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 47 : if (local_path && !agent->tor_agent_enabled()) {
305 19 : return can_dissociate;
306 : }
307 28 : if (route->is_multicast()) {
308 28 : const AgentPath *active_path = GetMulticastExportablePath(agent, route);
309 28 : if (active_path == NULL)
310 6 : 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 41 : void RouteExport::SubscribeMulticastRouting(const Agent *agent,
320 : AgentXmppChannel *bgp_xmpp_peer,
321 : AgentRoute *route,
322 : RouteExport::State *state) {
323 41 : 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 82 : if (!(agent->simulate_evpn_tor()) &&
329 82 : (active_path->peer()->GetType() != Peer::OVS_PEER) &&
330 41 : ((state->multicast_exported_ == false) ||
331 35 : (state->force_chg_ == true))) {
332 : //TODO optimize by checking for force_chg? In other cases duplicate
333 : //request can be filtered.
334 6 : 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 0 : AgentXmppChannel::ControllerSendMcastRouteDelete(bgp_xmpp_peer,
338 : route);
339 0 : state->multicast_exported_ = false;
340 : }
341 : //Sending 255.255.255.255 for fabric tree
342 6 : state->multicast_exported_ =
343 6 : AgentXmppChannel::ControllerSendMcastRouteAdd(bgp_xmpp_peer,
344 : route);
345 : }
346 41 : }
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 47 : void RouteExport::MulticastNotify(AgentXmppChannel *bgp_xmpp_peer,
357 : bool associate,
358 : DBTablePartBase *partition,
359 : DBEntryBase *e) {
360 47 : Agent *agent = bgp_xmpp_peer->agent();
361 47 : AgentRoute *route = static_cast<AgentRoute *>(e);
362 47 : State *state = static_cast<State *>(route->GetState(partition->parent(), id_));
363 :
364 : // Bridge Multicast routes are not sent to control.
365 47 : BridgeRouteEntry *l2_route = dynamic_cast<BridgeRouteEntry *>(route);
366 94 : if ((route->GetTableType() == Agent::BRIDGE) &&
367 47 : (l2_route->prefix_address() != MacAddress::BroadcastMac())) {
368 :
369 0 : return;
370 : }
371 :
372 47 : bool route_can_be_dissociated = MulticastRouteCanDissociate(route);
373 :
374 47 : if (marked_delete_ && state) {
375 0 : route->ClearState(partition->parent(), id_);
376 0 : delete state;
377 0 : state = NULL;
378 0 : 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 47 : 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 41 : if (marked_delete_) {
418 : //Ignore route updates on delete marked vrf
419 0 : return;
420 : }
421 :
422 41 : if (state == NULL) {
423 6 : state = new State();
424 6 : 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 41 : if (route->vrf()->ShouldExportRoute()) {
433 41 : SubscribeMulticastRouting(agent, bgp_xmpp_peer, route, state);
434 : }
435 :
436 41 : bool ingress_replication = true;
437 41 : if ((route->GetTableType() == Agent::INET4_MULTICAST) &&
438 0 : (agent->params()->mvpn_ipv4_enable())) {
439 :
440 0 : ingress_replication = false;
441 : }
442 41 : if (ingress_replication) {
443 41 : SubscribeIngressReplication(agent, bgp_xmpp_peer, route, state);
444 : }
445 :
446 41 : state->force_chg_ = false;
447 41 : return;
448 : }
449 :
450 41 : 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 41 : bool bridging = false;
457 41 : if (route->vrf() && route->vrf()->vn() && route->vrf()->vn()->bridging())
458 19 : bridging = true;
459 :
460 41 : const AgentPath *active_path = GetMulticastExportablePath(agent, route);
461 : //Sending ff:ff:ff:ff:ff:ff for evpn replication
462 41 : TunnelType::Type old_tunnel_type = state->tunnel_type_;
463 41 : uint32_t old_label = state->label_;
464 41 : TunnelType::Type new_tunnel_type = active_path->tunnel_type();
465 41 : 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 41 : if ((state->ingress_replication_exported_ == true) ||
471 25 : (state->force_chg_ == true)) {
472 16 : bool withdraw = false;
473 16 : uint32_t withdraw_label = 0;
474 :
475 16 : if (old_tunnel_type == TunnelType::VXLAN) {
476 5 : if ((new_tunnel_type != TunnelType::VXLAN) ||
477 : (old_label != new_label)) {
478 2 : withdraw_label = old_label;
479 2 : withdraw = true;
480 : }
481 11 : } else if (new_tunnel_type == TunnelType::VXLAN) {
482 2 : withdraw = true;
483 : }
484 :
485 16 : 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 16 : if (bridging == false)
497 0 : withdraw = true;
498 :
499 16 : if (withdraw) {
500 : AgentXmppChannel::ControllerSendEvpnRouteDelete
501 4 : (bgp_xmpp_peer, route, state->vn_, withdraw_label,
502 4 : state->destination_, state->source_,
503 4 : state->tunnel_type_);
504 4 : state->ingress_replication_exported_ = false;
505 : }
506 : }
507 :
508 41 : 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 41 : if (active_path->tunnel_type() != state->tunnel_type_) {
512 10 : state->force_chg_ = true;
513 10 : state->tunnel_type_ = active_path->tunnel_type();
514 : }
515 :
516 41 : if (active_path->GetActiveLabel() != state->label_) {
517 10 : state->force_chg_ = true;
518 10 : state->label_ = active_path->GetActiveLabel();
519 : }
520 :
521 41 : 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 41 : if (bridging &&
532 19 : ((state->ingress_replication_exported_ == false) ||
533 12 : (state->force_chg_ == true))) {
534 7 : state->label_ = active_path->GetActiveLabel();
535 7 : state->vn_ = route->dest_vn_name();
536 : const TunnelNH *tnh =
537 7 : dynamic_cast<const TunnelNH *>(active_path->nexthop());
538 7 : if (tnh) {
539 0 : state->destination_ = tnh->GetDip()->to_string();
540 0 : state->source_ = tnh->GetSip()->to_string();
541 : }
542 7 : SecurityGroupList sg;
543 7 : TagList tag;
544 7 : state->ingress_replication_exported_ =
545 : AgentXmppChannel::ControllerSendEvpnRouteAdd
546 7 : (bgp_xmpp_peer, route,
547 : active_path->NexthopIp(agent),
548 7 : route->dest_vn_name(), state->label_,
549 : TunnelType::GetTunnelBmap(state->tunnel_type_),
550 7 : &sg, &tag, NULL, state->destination_,
551 14 : state->source_, PathPreference());
552 7 : }
553 : }
554 :
555 1 : bool RouteExport::DeleteState(DBTablePartBase *partition,
556 : DBEntryBase *entry) {
557 : State *state = static_cast<State *>
558 1 : (entry->GetState(partition->parent(), id_));
559 1 : if (state) {
560 1 : entry->ClearState(partition->parent(), id_);
561 1 : delete state;
562 : }
563 1 : return true;
564 : }
565 :
566 36 : void RouteExport::Walkdone(DBTable::DBTableWalkRef walk_ref,
567 : DBTableBase *partition,
568 : RouteExport *rt_export) {
569 36 : delete rt_export;
570 36 : }
571 :
572 36 : void RouteExport::Unregister() {
573 : //Start unregister process
574 36 : rt_table_->WalkTable(walk_ref_);
575 36 : }
576 :
577 36 : RouteExport* RouteExport::Init(AgentRouteTable *table,
578 : AgentXmppChannel *bgp_xmpp_peer) {
579 36 : RouteExport *rt_export = new RouteExport(table);
580 36 : bool associate = true;
581 72 : rt_export->id_ = table->Register(boost::bind(&RouteExport::Notify,
582 : rt_export, table->agent(), bgp_xmpp_peer,
583 36 : associate, table->GetTableType(), _1, _2));
584 36 : return rt_export;
585 : }
586 :
|