/
controller_export.cc
454 lines (397 loc) · 16.6 KB
/
controller_export.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
/*
* Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
*/
#include <boost/uuid/uuid_io.hpp>
#include <cmn/agent_cmn.h>
#include <oper/route_common.h>
#include <oper/peer.h>
#include <oper/nexthop.h>
#include <oper/peer.h>
#include <oper/mirror_table.h>
#include "oper/tunnel_nh.h"
#include <controller/controller_vrf_export.h>
#include <controller/controller_init.h>
#include <controller/controller_export.h>
#include <controller/controller_peer.h>
#include <controller/controller_types.h>
RouteExport::State::State() :
DBState(), exported_(false), fabric_multicast_exported_(false),
force_chg_(false), label_(MplsTable::kInvalidLabel), vn_(""), sg_list_(),
tunnel_type_(TunnelType::INVALID), path_preference_(),
destination_(), source_() {
}
bool RouteExport::State::Changed(const AgentRoute *route, const AgentPath *path) const {
if (exported_ == false)
return true;
if (force_chg_ == true)
return true;
if (label_ != path->GetActiveLabel())
return true;
if (tunnel_type_ != path->tunnel_type()) {
return true;
};
if (vn_ != path->dest_vn_name())
return true;
if (sg_list_ != path->sg_list())
return true;
if (path_preference_ != path->path_preference())
return true;
return false;
}
void RouteExport::State::Update(const AgentRoute *route, const AgentPath *path) {
force_chg_ = false;
label_ = path->GetActiveLabel();
vn_ = path->dest_vn_name();
sg_list_ = path->sg_list();
tunnel_type_ = path->tunnel_type();
path_preference_ = path->path_preference();
}
RouteExport::RouteExport(AgentRouteTable *rt_table):
rt_table_(rt_table), marked_delete_(false),
table_delete_ref_(this, rt_table->deleter()) {
}
RouteExport::~RouteExport() {
if (rt_table_) {
rt_table_->Unregister(id_);
}
table_delete_ref_.Reset(NULL);
}
void RouteExport::ManagedDelete() {
marked_delete_ = true;
}
// Route entry add/change/del notification handler
void RouteExport::Notify(const Agent *agent,
AgentXmppChannel *bgp_xmpp_peer,
bool associate,
Agent::RouteTableType type,
DBTablePartBase *partition,
DBEntryBase *e) {
AgentRoute *route = static_cast<AgentRoute *>(e);
// Primitive checks for non-delete notification
if (!route->IsDeleted()) {
// If there is no active BGP peer attached to channel, ignore
// non-delete notification for this channel
if (!AgentXmppChannel::IsBgpPeerActive(agent, bgp_xmpp_peer))
return;
// Extract the listener ID of active BGP peer for route table to which
// this route entry belongs to. Listeners of route table can be active
// bgp peers as well as decommisioned BGP peers(if they exist). Active
// and decommisoned BGP peer can co-exist till cleanup timer is fired.
// During this interval ignore notification for decommisioned bgp peer
// listener id
VrfEntry *vrf = route->vrf();
BgpPeer *bgp_peer = static_cast<BgpPeer *>(bgp_xmpp_peer->
bgp_peer_id());
DBTableBase::ListenerId vrf_id = bgp_peer->GetVrfExportListenerId();
VrfExport::State *vs =
static_cast<VrfExport::State *>(vrf->GetState(vrf->get_table(),
vrf_id));
// If VRF state is not present then listener has not been added.
// Addition of listener later will result in walk to notify all routes.
// That in turn will add state as well by calling current routine.
// Therefore return when empty VRF state is found.
if (!vs)
return;
// There may be instances when decommisioned peer is not yet
// unregistered while a new peer is already present. So there will be
// two notifications. If its for decommisioned peer then ignore the same
// by checking the listener id with active bgp peer listener id.
DBTableBase::ListenerId id = vs->rt_export_[route->GetTableType()]->
GetListenerId();
if (id != id_)
return;
}
if (route->is_multicast()) {
MulticastNotify(bgp_xmpp_peer, associate, partition, e);
} else {
UnicastNotify(bgp_xmpp_peer, partition, e, type);
}
}
void RouteExport::UnicastNotify(AgentXmppChannel *bgp_xmpp_peer,
DBTablePartBase *partition, DBEntryBase *e,
Agent::RouteTableType type) {
AgentRoute *route = static_cast<AgentRoute *>(e);
//TODO Currently BRIDGE notifications are coming because multicast route
//are installed in same. Once multicast route is shifted to EVPN table
//then there will be no export from Bridge and check below can be removed.
if (route->GetTableType() == Agent::BRIDGE)
return;
AgentRouteTable *table = static_cast<AgentRouteTable *>
(partition->parent());
State *state = static_cast<State *>(route->GetState(partition->parent(),
id_));
AgentPath *path = route->FindLocalVmPortPath();
std::stringstream path_str;
if (path && path->peer())
path_str << path->peer()->GetName();
else
path_str << "None";
if (marked_delete_) {
//Ignore route updates on delete marked vrf
goto done;
}
if (!state && route->IsDeleted()) {
goto done;
}
if (state == NULL) {
state = new State();
route->SetState(partition->parent(), id_, state);
}
if (path) {
if (state->Changed(route, path)) {
state->Update(route, path);
state->exported_ =
AgentXmppChannel::ControllerSendRouteAdd(bgp_xmpp_peer,
static_cast<AgentRoute * >(route),
path->NexthopIp(table->agent()), state->vn_,
state->label_, path->GetTunnelBmap(),
&path->sg_list(), type, state->path_preference_);
}
} else {
if (state->exported_ == true) {
AgentXmppChannel::ControllerSendRouteDelete(bgp_xmpp_peer,
static_cast<AgentRoute *>(route), state->vn_,
(state->tunnel_type_ == TunnelType::VXLAN ?
state->label_ : 0),
TunnelType::AllType(), NULL,
type, state->path_preference_);
state->exported_ = false;
}
}
done:
if (route->IsDeleted()) {
if (state) {
route->ClearState(partition->parent(), id_);
delete state;
}
}
}
static const AgentPath *GetMulticastExportablePath(const Agent *agent,
const AgentRoute *route) {
const AgentPath *active_path = route->FindPath(agent->local_vm_peer());
//OVS peer path
if (active_path == NULL) {
const BridgeRouteEntry *bridge_route =
dynamic_cast<const BridgeRouteEntry *>(route);
if (bridge_route)
active_path = bridge_route->FindOvsPath();
}
//If no loca peer, then look for tor peer as that should also result
//in export of route.
if (active_path == NULL)
active_path = route->FindPath(agent->multicast_tor_peer());
//Subnet discard
if (active_path == NULL) {
const AgentPath *local_path = route->FindPath(agent->local_peer());
if (local_path) {
return local_path;
}
}
return active_path;
}
static bool RouteCanDissociate(const AgentRoute *route) {
bool can_dissociate = route->IsDeleted();
Agent *agent = static_cast<AgentRouteTable*>(route->get_table())->
agent();
const AgentPath *local_path = route->FindPath(agent->local_peer());
if (local_path) {
return can_dissociate;
}
if (route->is_multicast()) {
const AgentPath *active_path = GetMulticastExportablePath(agent, route);
if (active_path == NULL)
return true;
const NextHop *nh = active_path ? active_path->ComputeNextHop(agent) : NULL;
const CompositeNH *cnh = static_cast<const CompositeNH *>(nh);
if (cnh && cnh->ComponentNHCount() == 0)
return true;
}
return can_dissociate;
}
void RouteExport::SubscribeFabricMulticast(const Agent *agent,
AgentXmppChannel *bgp_xmpp_peer,
AgentRoute *route,
RouteExport::State *state) {
const AgentPath *active_path = GetMulticastExportablePath(agent, route);
//Agent running as tor(simulate_evpn_tor) - dont subscribe
//Route has path with peer OVS_PEER i.e. TOR agent mode - dont subscribe
//Subscribe condition:
//first time subscription or force change
if (!(agent->simulate_evpn_tor()) &&
(active_path->peer()->GetType() != Peer::OVS_PEER) &&
((state->fabric_multicast_exported_ == false) ||
(state->force_chg_ == true))) {
//Sending 255.255.255.255 for fabric tree
state->fabric_multicast_exported_ =
AgentXmppChannel::ControllerSendMcastRouteAdd(bgp_xmpp_peer,
route);
}
}
// Handles subscription of multicast routes.
// Following are the kind of subscription:
// Fabric - For fabric replication tree
// EVPN Ingress replication - For adding compute node in EVPN replication list.
// TOR Ingress replication - For adding in TOR replication list (relevant for
// TSN).
//
// For Tor-agent its a route with tunnel NH and there is no subscription.
void RouteExport::MulticastNotify(AgentXmppChannel *bgp_xmpp_peer,
bool associate,
DBTablePartBase *partition,
DBEntryBase *e) {
Agent *agent = bgp_xmpp_peer->agent();
AgentRoute *route = static_cast<AgentRoute *>(e);
State *state = static_cast<State *>(route->GetState(partition->parent(), id_));
bool route_can_be_dissociated = RouteCanDissociate(route);
//Currently only bridge flood route is taken, though there is a seperate
//multicast table as well. In future if multicats table is populated, get
//rid of this assert or expand it.
assert(route->GetTableType() == Agent::BRIDGE);
//Handle withdraw for following cases:
//- Route is not having any active multicast exportable path or is deleted.
//- associate(false): Bgp Peer has gone down and state needs to be removed.
if (route_can_be_dissociated || !associate) {
if (state == NULL) {
return;
}
if (state->fabric_multicast_exported_ == true) {
AgentXmppChannel::ControllerSendMcastRouteDelete(bgp_xmpp_peer,
route);
state->fabric_multicast_exported_ = false;
}
if ((state->ingress_replication_exported_ == true)) {
state->tunnel_type_ = TunnelType::INVALID;
AgentXmppChannel::ControllerSendEvpnRouteDelete(bgp_xmpp_peer,
route,
state->vn_,
state->label_,
state->destination_,
state->source_,
TunnelType::AllType());
state->ingress_replication_exported_ = false;
}
route->ClearState(partition->parent(), id_);
delete state;
state = NULL;
return;
}
if (marked_delete_) {
//Ignore route updates on delete marked vrf
return;
}
if (state == NULL) {
state = new State();
route->SetState(partition->parent(), id_, state);
}
SubscribeFabricMulticast(agent, bgp_xmpp_peer, route, state);
SubscribeIngressReplication(agent, bgp_xmpp_peer, route, state);
state->force_chg_ = false;
return;
}
void RouteExport::SubscribeIngressReplication(Agent *agent,
AgentXmppChannel *bgp_xmpp_peer,
AgentRoute *route,
RouteExport::State *state) {
//Check if bridging mode is enabled for this VN by verifying bridge flag
//in local peer path.
bool bridging = false;
if (route->vrf() && route->vrf()->vn() && route->vrf()->vn()->bridging())
bridging = true;
const AgentPath *active_path = GetMulticastExportablePath(agent, route);
//Sending ff:ff:ff:ff:ff:ff for evpn replication
TunnelType::Type old_tunnel_type = state->tunnel_type_;
uint32_t old_label = state->label_;
TunnelType::Type new_tunnel_type = active_path->tunnel_type();
uint32_t new_label = active_path->GetActiveLabel();
//Evaluate if ingress replication subscription needs to be withdrawn.
//Conditions: Tunnel type changed (VXLAN to MPLS) in turn label change,
//bridging is disabled/enabled on VN
if ((state->ingress_replication_exported_ == true) ||
(state->force_chg_ == true)) {
bool withdraw = false;
uint32_t withdraw_label = 0;
if (old_tunnel_type == TunnelType::VXLAN) {
if ((new_tunnel_type != TunnelType::VXLAN) ||
(old_label != new_label)) {
withdraw_label = old_label;
withdraw = true;
}
} else if (new_tunnel_type == TunnelType::VXLAN) {
withdraw = true;
}
if (bridging == false)
withdraw = true;
if (withdraw) {
AgentXmppChannel::ControllerSendEvpnRouteDelete
(bgp_xmpp_peer, route, state->vn_, withdraw_label,
state->destination_, state->source_,
state->tunnel_type_);
state->ingress_replication_exported_ = false;
}
}
//Update state values with new values if there is any change.
//Also force change same i.e. update.
if (active_path->tunnel_type() != state->tunnel_type_) {
state->force_chg_ = true;
state->tunnel_type_ = active_path->tunnel_type();
}
if (active_path->GetActiveLabel() != state->label_) {
state->force_chg_ = true;
state->label_ = active_path->GetActiveLabel();
}
//Subcribe if:
//- Bridging is enabled
//- First time (ingress_replication_exported is false)
//- Forced Change
if (bridging &&
((state->ingress_replication_exported_ == false) ||
(state->force_chg_ == true))) {
state->label_ = active_path->GetActiveLabel();
state->vn_ = route->dest_vn_name();
const TunnelNH *tnh =
dynamic_cast<const TunnelNH *>(active_path->nexthop());
if (tnh) {
state->destination_ = tnh->GetDip()->to_string();
state->source_ = tnh->GetSip()->to_string();
}
SecurityGroupList sg;
state->ingress_replication_exported_ =
AgentXmppChannel::ControllerSendEvpnRouteAdd
(bgp_xmpp_peer, route,
active_path->NexthopIp(agent),
route->dest_vn_name(), state->label_,
TunnelType::GetTunnelBmap(state->tunnel_type_),
&sg, state->destination_,
state->source_, PathPreference());
}
}
bool RouteExport::DeleteState(DBTablePartBase *partition,
DBEntryBase *entry) {
State *state = static_cast<State *>
(entry->GetState(partition->parent(), id_));
if (state) {
entry->ClearState(partition->parent(), id_);
delete state;
}
return true;
}
void RouteExport::Walkdone(DBTableBase *partition,
RouteExport *rt_export) {
delete rt_export;
}
void RouteExport::Unregister() {
//Start unregister process
DBTableWalker *walker = Agent::GetInstance()->db()->GetWalker();
walker->WalkTable(rt_table_, NULL,
boost::bind(&RouteExport::DeleteState, this, _1, _2),
boost::bind(&RouteExport::Walkdone, _1, this));
}
RouteExport* RouteExport::Init(AgentRouteTable *table,
AgentXmppChannel *bgp_xmpp_peer) {
RouteExport *rt_export = new RouteExport(table);
bool associate = true;
rt_export->id_ = table->Register(boost::bind(&RouteExport::Notify,
rt_export, table->agent(), bgp_xmpp_peer,
associate, table->GetTableType(), _1, _2));
return rt_export;
}