Line data Source code
1 : /*
2 : * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include <boost/foreach.hpp>
6 : #include <boost/uuid/uuid_io.hpp>
7 :
8 : #include <base/address_util.h>
9 : #include <base/task_annotations.h>
10 : #include <cmn/agent_cmn.h>
11 : #include <route/route.h>
12 : #include <oper/ecmp.h>
13 : #include <oper/ecmp_load_balance.h>
14 : #include <oper/route_common.h>
15 : #include <oper/vrf.h>
16 : #include <oper/tunnel_nh.h>
17 : #include <oper/mpls.h>
18 : #include <oper/vxlan.h>
19 : #include <oper/mirror_table.h>
20 : #include <oper/multicast.h>
21 : #include <oper/agent_sandesh.h>
22 :
23 : using namespace std;
24 : using namespace boost::asio;
25 :
26 205 : EcmpData::EcmpData(Agent *agent,
27 : const string &vrf_name,
28 : const string &route_str,
29 : AgentPath *path,
30 205 : bool del) :
31 205 : path_(path), ecmp_path_(NULL), delete_(del),
32 205 : alloc_label_(true), label_(MplsTable::kInvalidLabel),
33 205 : vrf_name_(vrf_name), route_str_(route_str),
34 205 : vn_list_(path->dest_vn_list()),
35 205 : sg_list_(path->sg_list()),
36 205 : tag_list_(path->tag_list()),
37 205 : community_list_(path->communities()),
38 205 : path_preference_(path->path_preference()),
39 205 : tunnel_bmap_(path->tunnel_bmap()),
40 205 : ecmp_load_balance_(path->ecmp_load_balance()),
41 410 : nh_req_(), agent_(agent) {
42 205 : }
43 :
44 205 : bool EcmpData::Update(AgentRoute *rt) {
45 205 : if (path_->peer() == NULL) {
46 0 : return false;
47 : }
48 :
49 205 : if (path_->peer()->GetType() == Peer::LOCAL_VM_PORT_PEER) {
50 47 : return LocalVmPortPeerEcmp(rt);
51 : }
52 :
53 158 : if (path_->peer()->GetType() == Peer::BGP_PEER) {
54 9 : alloc_label_ = false;
55 9 : return BgpPeerEcmp();
56 : }
57 :
58 149 : return false;
59 : }
60 :
61 1 : bool EcmpData::UpdateWithParams(const SecurityGroupList &sg_list,
62 : const TagList &tag_list,
63 : const CommunityList &community_list,
64 : const PathPreference &path_preference,
65 : const TunnelType::TypeBmap bmap,
66 : const EcmpLoadBalance &ecmp_load_balance,
67 : const VnListType &vn_list,
68 : DBRequest &nh_req) {
69 1 : sg_list_ = sg_list;
70 1 : vn_list_ = vn_list;
71 1 : tag_list_ = tag_list;
72 1 : community_list_ = community_list;
73 1 : path_preference_ = path_preference;
74 1 : tunnel_bmap_ = bmap;
75 1 : ecmp_load_balance_ = ecmp_load_balance;
76 1 : nh_req_.Swap(&nh_req);
77 :
78 1 : return Update(NULL);
79 : }
80 :
81 47 : bool EcmpData::LocalVmPortPeerEcmp(AgentRoute *rt) {
82 47 : ecmp_path_ = rt->FindPath(agent_->ecmp_peer());
83 47 : if (delete_) {
84 14 : return EcmpDeletePath(rt);
85 : } else {
86 33 : if (path_->path_preference().is_ecmp() == false) {
87 29 : return false;
88 : }
89 4 : return EcmpAddPath(rt);
90 : }
91 : }
92 :
93 9 : bool EcmpData::BgpPeerEcmp() {
94 9 : ecmp_path_ = path_;
95 : //Bgp peer update for ecmp should always accompany nh_req.
96 : //Any other request like sync is to be ignored.
97 9 : NextHopKey *key = static_cast<NextHopKey *>(nh_req_.key.get());
98 9 : if (!key)
99 8 : return false;
100 1 : return ModifyEcmpPath();
101 : }
102 :
103 : // Handle add/update of a path in route.
104 : // If there are more than one path of type LOCAL_VM_PORT_PEER, creates/updates
105 : // Composite-NH for them
106 4 : bool EcmpData::EcmpAddPath(AgentRoute *rt) {
107 4 : if (path_->tunnel_bmap() & TunnelType::NativeType()) {
108 0 : path_->set_tunnel_bmap(TunnelType::MplsType() |
109 0 : TunnelType::NativeType());
110 : } else {
111 4 : path_->set_tunnel_bmap(TunnelType::MplsType());
112 : }
113 :
114 : // Count number of paths from LOCAL_VM_PORT_PEER already present
115 4 : const AgentPath *vm_port_path = NULL;
116 4 : int count = 0;
117 19 : for(Route::PathList::const_iterator it = rt->GetPathList().begin();
118 30 : it != rt->GetPathList().end(); it++) {
119 : const AgentPath *it_path =
120 11 : static_cast<const AgentPath *>(it.operator->());
121 :
122 11 : if (it_path->peer() == agent_->ecmp_peer())
123 1 : assert(ecmp_path_ == it_path);
124 :
125 22 : if (it_path->peer() &&
126 22 : it_path->peer()->GetType() == Peer::LOCAL_VM_PORT_PEER &&
127 6 : it_path->path_preference().is_ecmp() == true) {
128 6 : count++;
129 6 : if (it_path != path_)
130 2 : vm_port_path = it_path;
131 : }
132 : }
133 :
134 4 : if (count == 0) {
135 0 : return false;
136 : }
137 :
138 : // Sanity check. When more than one LOCAL_VM_PORT_PEER, ECMP must be present
139 4 : if (count > 2) {
140 0 : assert(ecmp_path_ != NULL);
141 : }
142 :
143 4 : if (count == 1) {
144 2 : assert(ecmp_path_ == NULL);
145 2 : return false;
146 : }
147 :
148 2 : bool ret = false;
149 2 : if (count == 2 && ecmp_path_ == NULL) {
150 : // This is second path being added, make ECMP
151 1 : AllocateEcmpPath(rt, vm_port_path);
152 1 : ret = true;
153 1 : } else if (count > 2) {
154 : // ECMP already present, add/update Component-NH for the path
155 0 : AppendEcmpPath(rt, path_);
156 0 : ret = true;
157 1 : } else if (ecmp_path_) {
158 1 : bool updated = UpdateComponentNH(rt, path_);
159 : //No update happened for component NH, so verify if params are to be
160 : //synced. If update of component NH is done, then params would also have
161 : //been updated, so no need to do it again.
162 1 : if (!updated) {
163 1 : updated = SyncParams();
164 : }
165 1 : if (updated) {
166 1 : ret = true;
167 : }
168 : }
169 :
170 2 : return ret;
171 : }
172 :
173 : // Function to create a ECMP path from path and path2
174 : // Creates Composite-NH with 2 Component-NH (one for each of path and path2)
175 : // Creates a new MPLS Label for the ECMP path
176 1 : void EcmpData::AllocateEcmpPath(AgentRoute *rt, const AgentPath *path2) {
177 : // Allocate and insert a path
178 1 : ecmp_path_ = new AgentPath(agent_->ecmp_peer(), rt);
179 1 : rt->InsertPath(ecmp_path_);
180 :
181 1 : const NextHop* path1_nh = path_->ComputeNextHop(agent_);
182 1 : bool composite_nh_policy = path1_nh->NexthopToInterfacePolicy();
183 :
184 : // Create Component NH to be added to ECMP path
185 1 : DBEntryBase::KeyPtr key1 = path1_nh->GetDBRequestKey();
186 1 : NextHopKey *nh_key1 = static_cast<NextHopKey *>(key1.release());
187 1 : std::unique_ptr<const NextHopKey> nh_akey1(nh_key1);
188 1 : nh_key1->SetPolicy(false);
189 1 : ComponentNHKeyPtr component_nh_data1(new ComponentNHKey(path_->label(),
190 1 : std::move(nh_akey1)));
191 :
192 1 : const NextHop* path2_nh = path2->ComputeNextHop(agent_);
193 1 : if (!composite_nh_policy) {
194 0 : composite_nh_policy = path2_nh->NexthopToInterfacePolicy();
195 : }
196 1 : DBEntryBase::KeyPtr key2 = path2_nh->GetDBRequestKey();
197 1 : NextHopKey *nh_key2 = static_cast<NextHopKey *>(key2.release());
198 1 : std::unique_ptr<const NextHopKey> nh_akey2(nh_key2);
199 1 : nh_key2->SetPolicy(false);
200 1 : ComponentNHKeyPtr component_nh_data2(new ComponentNHKey(path2->label(),
201 1 : std::move(nh_akey2)));
202 :
203 1 : ComponentNHKeyList component_nh_list;
204 1 : component_nh_list.push_back(component_nh_data2);
205 1 : component_nh_list.push_back(component_nh_data1);
206 :
207 : // Directly call AddChangePath to update NH in the ECMP path
208 : // It will also create CompositeNH if necessary
209 1 : DBRequest nh_req(DBRequest::DB_ENTRY_ADD_CHANGE);
210 1 : nh_req.key.reset(new CompositeNHKey(Composite::LOCAL_ECMP,
211 : composite_nh_policy, component_nh_list,
212 1 : vrf_name_));
213 1 : nh_req.data.reset(new CompositeNHData());
214 1 : nh_req_.Swap(&nh_req);
215 :
216 1 : label_ = MplsTable::kInvalidLabel;
217 1 : ModifyEcmpPath();
218 :
219 1 : RouteInfo rt_info;
220 1 : rt->FillTrace(rt_info, AgentRoute::CHANGE_PATH, ecmp_path_);
221 1 : AgentRouteTable *table = static_cast<AgentRouteTable *>(rt->get_table());
222 1 : OPER_TRACE_ROUTE_ENTRY(Route, table, rt_info);
223 1 : AGENT_ROUTE_LOG(table, "Path Add", rt->ToString(), vrf_name_,
224 : GETPEERNAME(agent_->ecmp_peer()));
225 1 : }
226 :
227 0 : void EcmpData::AppendEcmpPath(AgentRoute *rt, AgentPath *path) {
228 0 : assert(ecmp_path_);
229 0 : const NextHop* path_nh = path->ComputeNextHop(agent_);
230 0 : DBEntryBase::KeyPtr key = path_nh->GetDBRequestKey();
231 0 : NextHopKey *nh_key = static_cast<NextHopKey *>(key.release());
232 0 : std::unique_ptr<const NextHopKey> nh_akey(nh_key);
233 0 : nh_key->SetPolicy(false);
234 0 : ComponentNHKeyPtr comp_nh_key_ptr(new ComponentNHKey(path->label(), std::move(nh_akey)));
235 :
236 0 : ComponentNHKeyList component_nh_key_list;
237 : const CompositeNH *comp_nh =
238 0 : static_cast<const CompositeNH *>(ecmp_path_->ComputeNextHop(agent_));
239 0 : DBEntryBase::KeyPtr comp_nh_key = comp_nh->GetDBRequestKey();
240 0 : NextHopKey *cnh_key = static_cast<NextHopKey *>(comp_nh_key.get());
241 0 : bool composite_nh_policy = false;
242 0 : component_nh_key_list = comp_nh->AddComponentNHKey(comp_nh_key_ptr,
243 0 : composite_nh_policy);
244 : // Form the request for Inet4UnicastEcmpRoute and invoke AddChangePath
245 : // Get the existing comp_nh key and do a resync
246 0 : DBRequest nh_req(DBRequest::DB_ENTRY_ADD_CHANGE);
247 0 : cnh_key->sub_op_ = AgentKey::RESYNC;
248 : CompositeNHKey *composite_nh_key = new CompositeNHKey(Composite::LOCAL_ECMP,
249 : composite_nh_policy,
250 : component_nh_key_list,
251 0 : vrf_name_);
252 0 : nh_req.key = std::move(comp_nh_key);
253 0 : nh_req.data.reset(new CompositeNHData(component_nh_key_list));
254 0 : nh_req_.Swap(&nh_req);
255 :
256 0 : label_ = ecmp_path_->label();
257 0 : ModifyEcmpPath(composite_nh_key);
258 0 : path->SyncRoute(true);
259 :
260 0 : RouteInfo rt_info;
261 0 : rt->FillTrace(rt_info, AgentRoute::CHANGE_PATH, path);
262 0 : AgentRouteTable *table = static_cast<AgentRouteTable *>(rt->get_table());
263 0 : OPER_TRACE_ROUTE_ENTRY(Route, table, rt_info);
264 0 : AGENT_ROUTE_LOG(table, "Path change", rt->ToString(), vrf_name_,
265 : GETPEERNAME(agent_->ecmp_peer()));
266 0 : }
267 :
268 : // Handle deletion of a path in route. If the path being deleted is part of
269 : // ECMP, then deletes the Component-NH for the path.
270 : // Delete ECMP path if there is single Component-NH in Composite-NH
271 14 : bool EcmpData::EcmpDeletePath(AgentRoute *rt) {
272 14 : if (path_->peer() == NULL) {
273 0 : return false;
274 : }
275 :
276 14 : if (path_->peer()->GetType() != Peer::LOCAL_VM_PORT_PEER) {
277 0 : return false;
278 : }
279 :
280 : // Composite-NH is made from LOCAL_VM_PORT_PEER, count number of paths
281 : // with LOCAL_VM_PORT_PEER
282 14 : int count = 0;
283 46 : for(Route::PathList::const_iterator it = rt->GetPathList().begin();
284 64 : it != rt->GetPathList().end(); it++) {
285 : const AgentPath *it_path =
286 18 : static_cast<const AgentPath *>(it.operator->());
287 :
288 36 : if (it_path->peer() &&
289 18 : it_path->peer()->GetType() == Peer::LOCAL_VM_PORT_PEER &&
290 39 : it_path->path_preference().is_ecmp() == true &&
291 3 : it_path != path_)
292 1 : count++;
293 : }
294 :
295 : // Sanity check. When more than one LOCAL_VM_PORT_PEER, ECMP must be present
296 14 : if (count >= 1) {
297 1 : if (ecmp_path_ == NULL) {
298 0 : return false;
299 : }
300 : }
301 :
302 14 : if (count == 1 && ecmp_path_) {
303 : // There is single path of type LOCAL_VM_PORT_PEER. Delete the ECMP path
304 1 : rt->RemovePath(ecmp_path_);
305 : //Enqueue MPLS label delete request
306 1 : agent_->mpls_table()->FreeLabel(ecmp_path_->label());
307 1 : delete ecmp_path_;
308 13 : } else if (count > 1) {
309 : // Remove Component-NH for the path being deleted
310 0 : DeleteComponentNH(rt, path_);
311 : }
312 :
313 14 : return true;
314 : }
315 :
316 2 : bool EcmpData::UpdateNh(CompositeNHKey *composite_nh_key) {
317 2 : NextHop *nh = NULL;
318 2 : bool ret = false;
319 :
320 2 : agent_->nexthop_table()->Process(nh_req_);
321 2 : NextHopKey *key = static_cast<NextHopKey *>(nh_req_.key.get());
322 :
323 : // Create MPLS label and point it to Composite NH
324 2 : if (alloc_label_) {
325 2 : label_ = agent_->mpls_table()->CreateRouteLabel(label_, key, vrf_name_,
326 1 : route_str_);
327 : }
328 :
329 2 : if (composite_nh_key) {
330 0 : key = static_cast<NextHopKey *>(composite_nh_key);
331 : }
332 :
333 4 : nh = static_cast<NextHop *>(agent_->nexthop_table()->
334 2 : FindActiveEntry(key));
335 2 : if (nh == NULL) {
336 0 : VrfEntry *vrf = agent_->vrf_table()->FindVrfFromName(vrf_name_);
337 0 : if (vrf->IsDeleted())
338 0 : return ret;
339 0 : assert(0);
340 : }
341 :
342 2 : MplsLabel *mpls = agent_->mpls_table()->
343 2 : FindMplsLabel(label_);
344 2 : if (mpls && (ecmp_path_->local_ecmp_mpls_label() == NULL)) {
345 1 : ecmp_path_->set_local_ecmp_mpls_label(mpls);
346 : }
347 2 : if (ecmp_path_->ChangeNH(agent_, nh) == true)
348 2 : ret = true;
349 :
350 2 : return ret;
351 : }
352 :
353 3 : bool EcmpData::SyncParams() {
354 3 : bool ret = false;
355 :
356 3 : ecmp_path_->set_tunnel_bmap(tunnel_bmap_);
357 : TunnelType::Type new_tunnel_type =
358 3 : TunnelType::ComputeType(tunnel_bmap_);
359 3 : if (ecmp_path_->tunnel_type() != new_tunnel_type) {
360 0 : ecmp_path_->set_tunnel_type(new_tunnel_type);
361 0 : ret = true;
362 : }
363 :
364 3 : if (ecmp_path_->dest_vn_list() != vn_list_) {
365 2 : ecmp_path_->set_dest_vn_list(vn_list_);
366 2 : ret = true;
367 : }
368 :
369 3 : if (ecmp_path_->sg_list() != sg_list_) {
370 0 : ecmp_path_->set_sg_list(sg_list_);
371 0 : ret = true;
372 : }
373 :
374 3 : if (ecmp_path_->tag_list() != tag_list_) {
375 0 : ecmp_path_->set_tag_list(tag_list_);
376 0 : ret = true;
377 : }
378 :
379 3 : if (ecmp_path_->communities() != community_list_) {
380 0 : ecmp_path_->set_communities(community_list_);
381 0 : ret = true;
382 : }
383 :
384 3 : if (path_preference_ != ecmp_path_->path_preference()) {
385 2 : ecmp_path_->set_path_preference(path_preference_);
386 2 : ret = true;
387 : }
388 :
389 3 : if (ecmp_path_->ecmp_load_balance() != ecmp_load_balance_) {
390 0 : ecmp_path_->set_ecmp_load_balance(ecmp_load_balance_);
391 0 : ret = true;
392 : }
393 :
394 3 : return ret;
395 : }
396 :
397 2 : bool EcmpData::ModifyEcmpPath(CompositeNHKey *composite_nh_key) {
398 2 : bool ret = false;
399 :
400 2 : if (UpdateNh(composite_nh_key)) {
401 2 : ret = true;
402 : }
403 :
404 2 : if (ecmp_path_->label() != label_) {
405 1 : ecmp_path_->set_label(label_);
406 1 : ret = true;
407 : }
408 :
409 2 : if (SyncParams()) {
410 2 : ret = true;
411 : }
412 :
413 2 : ecmp_path_->set_unresolved(false);
414 2 : ret = true;
415 :
416 2 : return ret;
417 : }
418 :
419 : /* When label of VMI changes and if that VMI (ie VMI's InterfaceNH) is part of
420 : * ECMP, then update the CompositeNH for ECMP route to point to right label for
421 : * that VMI. Label of VMI can change when policy-status of VMI changes */
422 1 : bool EcmpData::UpdateComponentNH(AgentRoute *rt, AgentPath *path) {
423 1 : if (!ecmp_path_) {
424 0 : return false;
425 : }
426 : //Build ComponentNHKey for new path
427 1 : const NextHop* path_nh = path->ComputeNextHop(agent_);
428 1 : DBEntryBase::KeyPtr key = path_nh->GetDBRequestKey();
429 1 : NextHopKey *nh_key = static_cast<NextHopKey *>(key.get());
430 1 : nh_key->SetPolicy(false);
431 :
432 1 : ComponentNHKeyList component_nh_key_list;
433 : const CompositeNH *comp_nh =
434 1 : static_cast<const CompositeNH *>(ecmp_path_->ComputeNextHop(agent_));
435 1 : bool composite_nh_policy = false;
436 1 : bool updated = comp_nh->UpdateComponentNHKey(path->label(), nh_key,
437 : component_nh_key_list,
438 : composite_nh_policy);
439 :
440 1 : if (!updated) {
441 1 : return false;
442 : }
443 : // Form the request for Inet4UnicastEcmpRoute and invoke AddChangePath
444 0 : DBRequest nh_req(DBRequest::DB_ENTRY_ADD_CHANGE);
445 0 : nh_req.key.reset(new CompositeNHKey(Composite::LOCAL_ECMP,
446 : composite_nh_policy,
447 : component_nh_key_list,
448 0 : vrf_name_));
449 0 : nh_req.data.reset(new CompositeNHData());
450 0 : nh_req_.Swap(&nh_req);
451 0 : label_ = ecmp_path_->label();
452 0 : ModifyEcmpPath();
453 :
454 0 : RouteInfo rt_info;
455 0 : rt->FillTrace(rt_info, AgentRoute::CHANGE_PATH, path);
456 0 : AgentRouteTable *table = static_cast<AgentRouteTable *>(rt->get_table());
457 0 : OPER_TRACE_ROUTE_ENTRY(Route, table, rt_info);
458 0 : AGENT_ROUTE_LOG(table, "Path Update", rt->ToString(), vrf_name_,
459 : GETPEERNAME(agent_->ecmp_peer()));
460 0 : return true;
461 1 : }
462 :
463 0 : void EcmpData::DeleteComponentNH(AgentRoute *rt, AgentPath *path) {
464 0 : assert(ecmp_path_);
465 0 : DBEntryBase::KeyPtr key = path->ComputeNextHop(agent_)->GetDBRequestKey();
466 0 : NextHopKey *nh_key = static_cast<NextHopKey *>(key.release());
467 0 : std::unique_ptr<const NextHopKey> nh_akey(nh_key);
468 0 : nh_key->SetPolicy(false);
469 0 : ComponentNHKeyPtr comp_nh_key_ptr(new ComponentNHKey(path->label(), std::move(nh_akey)));
470 :
471 0 : ComponentNHKeyList component_nh_key_list;
472 0 : bool comp_nh_policy = false;
473 : const CompositeNH *comp_nh =
474 0 : static_cast<const CompositeNH *>(ecmp_path_->ComputeNextHop(agent_));
475 0 : component_nh_key_list = comp_nh->DeleteComponentNHKey(comp_nh_key_ptr,
476 0 : comp_nh_policy);
477 :
478 : // Form the request for Inet4UnicastEcmpRoute and invoke AddChangePath
479 0 : DBRequest nh_req(DBRequest::DB_ENTRY_ADD_CHANGE);
480 0 : nh_req.key.reset(new CompositeNHKey(Composite::LOCAL_ECMP,
481 : comp_nh_policy, component_nh_key_list,
482 0 : vrf_name_));
483 0 : nh_req.data.reset(new CompositeNHData());
484 0 : nh_req_.Swap(&nh_req);
485 0 : label_ = ecmp_path_->label();
486 0 : UpdateNh();
487 :
488 0 : RouteInfo rt_info;
489 0 : rt->FillTrace(rt_info, AgentRoute::CHANGE_PATH, path);
490 0 : AgentRouteTable *table = static_cast<AgentRouteTable *>(rt->get_table());
491 0 : OPER_TRACE_ROUTE_ENTRY(Route, table, rt_info);
492 0 : AGENT_ROUTE_LOG(table, "Path change", rt->ToString(), vrf_name_,
493 : GETPEERNAME(agent_->ecmp_peer()));
494 0 : }
495 :
496 9 : const NextHop* EcmpData::GetLocalNextHop(const AgentRoute *rt) {
497 : Agent *agent =
498 9 : (static_cast<InetUnicastAgentRouteTable *> (rt->get_table()))->agent();
499 :
500 9 : if (rt->FindPath(agent->ecmp_peer())) {
501 4 : return rt->FindPath(agent->ecmp_peer())->ComputeNextHop(agent);
502 : }
503 :
504 : //If a route is leaked, and it points to local composite nexthop
505 : //then choose that
506 5 : if (rt->GetActivePath()->local_ecmp_mpls_label()) {
507 0 : return rt->GetActivePath()->local_ecmp_mpls_label()->nexthop();
508 : }
509 :
510 : //Choose the first local vm peer path
511 12 : for (Route::PathList::const_iterator it = rt->GetPathList().begin();
512 24 : it != rt->GetPathList().end(); it++) {
513 7 : const AgentPath *path = static_cast<const AgentPath *>(it.operator->());
514 7 : if (path) {
515 14 : if (path->peer() &&
516 7 : path->peer()->GetType() == Peer::LOCAL_VM_PORT_PEER) {
517 0 : return path->ComputeNextHop(agent);
518 : }
519 : }
520 : }
521 :
522 5 : const NextHop *nh = rt->GetActiveNextHop();
523 5 : if (nh && nh->GetType() == NextHop::COMPOSITE ) {
524 5 : const CompositeNH *comp_nh = static_cast<const CompositeNH *>(nh);
525 : //Get the local composite NH
526 5 : return comp_nh->GetLocalNextHop();
527 : }
528 0 : return NULL;
529 : }
|