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