/src/frr/bgpd/bgp_mpath.c
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * BGP Multipath |
4 | | * Copyright (C) 2010 Google Inc. |
5 | | * |
6 | | * This file is part of Quagga |
7 | | */ |
8 | | |
9 | | #include <zebra.h> |
10 | | |
11 | | #include "command.h" |
12 | | #include "prefix.h" |
13 | | #include "linklist.h" |
14 | | #include "sockunion.h" |
15 | | #include "memory.h" |
16 | | #include "queue.h" |
17 | | #include "filter.h" |
18 | | |
19 | | #include "bgpd/bgpd.h" |
20 | | #include "bgpd/bgp_table.h" |
21 | | #include "bgpd/bgp_route.h" |
22 | | #include "bgpd/bgp_attr.h" |
23 | | #include "bgpd/bgp_debug.h" |
24 | | #include "bgpd/bgp_aspath.h" |
25 | | #include "bgpd/bgp_community.h" |
26 | | #include "bgpd/bgp_ecommunity.h" |
27 | | #include "bgpd/bgp_lcommunity.h" |
28 | | #include "bgpd/bgp_mpath.h" |
29 | | |
30 | | /* |
31 | | * bgp_maximum_paths_set |
32 | | * |
33 | | * Record maximum-paths configuration for BGP instance |
34 | | */ |
35 | | int bgp_maximum_paths_set(struct bgp *bgp, afi_t afi, safi_t safi, int peertype, |
36 | | uint16_t maxpaths, bool same_clusterlen) |
37 | 0 | { |
38 | 0 | if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX)) |
39 | 0 | return -1; |
40 | | |
41 | 0 | switch (peertype) { |
42 | 0 | case BGP_PEER_IBGP: |
43 | 0 | bgp->maxpaths[afi][safi].maxpaths_ibgp = maxpaths; |
44 | 0 | bgp->maxpaths[afi][safi].same_clusterlen = same_clusterlen; |
45 | 0 | break; |
46 | 0 | case BGP_PEER_EBGP: |
47 | 0 | bgp->maxpaths[afi][safi].maxpaths_ebgp = maxpaths; |
48 | 0 | break; |
49 | 0 | default: |
50 | 0 | return -1; |
51 | 0 | } |
52 | | |
53 | 0 | return 0; |
54 | 0 | } |
55 | | |
56 | | /* |
57 | | * bgp_maximum_paths_unset |
58 | | * |
59 | | * Remove maximum-paths configuration from BGP instance |
60 | | */ |
61 | | int bgp_maximum_paths_unset(struct bgp *bgp, afi_t afi, safi_t safi, |
62 | | int peertype) |
63 | 0 | { |
64 | 0 | if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX)) |
65 | 0 | return -1; |
66 | | |
67 | 0 | switch (peertype) { |
68 | 0 | case BGP_PEER_IBGP: |
69 | 0 | bgp->maxpaths[afi][safi].maxpaths_ibgp = multipath_num; |
70 | 0 | bgp->maxpaths[afi][safi].same_clusterlen = false; |
71 | 0 | break; |
72 | 0 | case BGP_PEER_EBGP: |
73 | 0 | bgp->maxpaths[afi][safi].maxpaths_ebgp = multipath_num; |
74 | 0 | break; |
75 | 0 | default: |
76 | 0 | return -1; |
77 | 0 | } |
78 | | |
79 | 0 | return 0; |
80 | 0 | } |
81 | | |
82 | | /* |
83 | | * bgp_interface_same |
84 | | * |
85 | | * Return true if ifindex for ifp1 and ifp2 are the same, else return false. |
86 | | */ |
87 | | static int bgp_interface_same(struct interface *ifp1, struct interface *ifp2) |
88 | 0 | { |
89 | 0 | if (!ifp1 && !ifp2) |
90 | 0 | return 1; |
91 | | |
92 | 0 | if (!ifp1 && ifp2) |
93 | 0 | return 0; |
94 | | |
95 | 0 | if (ifp1 && !ifp2) |
96 | 0 | return 0; |
97 | | |
98 | 0 | return (ifp1->ifindex == ifp2->ifindex); |
99 | 0 | } |
100 | | |
101 | | |
102 | | /* |
103 | | * bgp_path_info_nexthop_cmp |
104 | | * |
105 | | * Compare the nexthops of two paths. Return value is less than, equal to, |
106 | | * or greater than zero if bpi1 is respectively less than, equal to, |
107 | | * or greater than bpi2. |
108 | | */ |
109 | | int bgp_path_info_nexthop_cmp(struct bgp_path_info *bpi1, |
110 | | struct bgp_path_info *bpi2) |
111 | 0 | { |
112 | 0 | int compare; |
113 | 0 | struct in6_addr addr1, addr2; |
114 | |
|
115 | 0 | compare = IPV4_ADDR_CMP(&bpi1->attr->nexthop, &bpi2->attr->nexthop); |
116 | 0 | if (!compare) { |
117 | 0 | if (bpi1->attr->mp_nexthop_len == bpi2->attr->mp_nexthop_len) { |
118 | 0 | switch (bpi1->attr->mp_nexthop_len) { |
119 | 0 | case BGP_ATTR_NHLEN_IPV4: |
120 | 0 | case BGP_ATTR_NHLEN_VPNV4: |
121 | 0 | compare = IPV4_ADDR_CMP( |
122 | 0 | &bpi1->attr->mp_nexthop_global_in, |
123 | 0 | &bpi2->attr->mp_nexthop_global_in); |
124 | 0 | break; |
125 | 0 | case BGP_ATTR_NHLEN_IPV6_GLOBAL: |
126 | 0 | case BGP_ATTR_NHLEN_VPNV6_GLOBAL: |
127 | 0 | compare = IPV6_ADDR_CMP( |
128 | 0 | &bpi1->attr->mp_nexthop_global, |
129 | 0 | &bpi2->attr->mp_nexthop_global); |
130 | 0 | break; |
131 | 0 | case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: |
132 | 0 | addr1 = (bpi1->attr->mp_nexthop_prefer_global) |
133 | 0 | ? bpi1->attr->mp_nexthop_global |
134 | 0 | : bpi1->attr->mp_nexthop_local; |
135 | 0 | addr2 = (bpi2->attr->mp_nexthop_prefer_global) |
136 | 0 | ? bpi2->attr->mp_nexthop_global |
137 | 0 | : bpi2->attr->mp_nexthop_local; |
138 | |
|
139 | 0 | if (!bpi1->attr->mp_nexthop_prefer_global |
140 | 0 | && !bpi2->attr->mp_nexthop_prefer_global) |
141 | 0 | compare = !bgp_interface_same( |
142 | 0 | bpi1->peer->ifp, |
143 | 0 | bpi2->peer->ifp); |
144 | |
|
145 | 0 | if (!compare) |
146 | 0 | compare = IPV6_ADDR_CMP(&addr1, &addr2); |
147 | 0 | break; |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | | /* This can happen if one IPv6 peer sends you global and |
152 | | * link-local |
153 | | * nexthops but another IPv6 peer only sends you global |
154 | | */ |
155 | 0 | else if (bpi1->attr->mp_nexthop_len |
156 | 0 | == BGP_ATTR_NHLEN_IPV6_GLOBAL |
157 | 0 | || bpi1->attr->mp_nexthop_len |
158 | 0 | == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) { |
159 | 0 | compare = IPV6_ADDR_CMP(&bpi1->attr->mp_nexthop_global, |
160 | 0 | &bpi2->attr->mp_nexthop_global); |
161 | 0 | if (!compare) { |
162 | 0 | if (bpi1->attr->mp_nexthop_len |
163 | 0 | < bpi2->attr->mp_nexthop_len) |
164 | 0 | compare = -1; |
165 | 0 | else |
166 | 0 | compare = 1; |
167 | 0 | } |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | | /* |
172 | | * If both nexthops are same then check |
173 | | * if they belong to same VRF |
174 | | */ |
175 | 0 | if (!compare && bpi1->attr->nh_type != NEXTHOP_TYPE_BLACKHOLE) { |
176 | 0 | if (bpi1->extra && bpi1->extra->bgp_orig && bpi2->extra |
177 | 0 | && bpi2->extra->bgp_orig) { |
178 | 0 | if (bpi1->extra->bgp_orig->vrf_id |
179 | 0 | != bpi2->extra->bgp_orig->vrf_id) { |
180 | 0 | compare = 1; |
181 | 0 | } |
182 | 0 | } |
183 | 0 | } |
184 | |
|
185 | 0 | return compare; |
186 | 0 | } |
187 | | |
188 | | /* |
189 | | * bgp_path_info_mpath_cmp |
190 | | * |
191 | | * This function determines our multipath list ordering. By ordering |
192 | | * the list we can deterministically select which paths are included |
193 | | * in the multipath set. The ordering also helps in detecting changes |
194 | | * in the multipath selection so we can detect whether to send an |
195 | | * update to zebra. |
196 | | * |
197 | | * The order of paths is determined first by received nexthop, and then |
198 | | * by peer address if the nexthops are the same. |
199 | | */ |
200 | | static int bgp_path_info_mpath_cmp(void *val1, void *val2) |
201 | 0 | { |
202 | 0 | struct bgp_path_info *bpi1, *bpi2; |
203 | 0 | int compare; |
204 | |
|
205 | 0 | bpi1 = val1; |
206 | 0 | bpi2 = val2; |
207 | |
|
208 | 0 | compare = bgp_path_info_nexthop_cmp(bpi1, bpi2); |
209 | |
|
210 | 0 | if (!compare) { |
211 | 0 | if (!bpi1->peer->su_remote && !bpi2->peer->su_remote) |
212 | 0 | compare = 0; |
213 | 0 | else if (!bpi1->peer->su_remote) |
214 | 0 | compare = 1; |
215 | 0 | else if (!bpi2->peer->su_remote) |
216 | 0 | compare = -1; |
217 | 0 | else |
218 | 0 | compare = sockunion_cmp(bpi1->peer->su_remote, |
219 | 0 | bpi2->peer->su_remote); |
220 | 0 | } |
221 | |
|
222 | 0 | return compare; |
223 | 0 | } |
224 | | |
225 | | /* |
226 | | * bgp_mp_list_init |
227 | | * |
228 | | * Initialize the mp_list, which holds the list of multipaths |
229 | | * selected by bgp_best_selection |
230 | | */ |
231 | | void bgp_mp_list_init(struct list *mp_list) |
232 | 0 | { |
233 | 0 | assert(mp_list); |
234 | 0 | memset(mp_list, 0, sizeof(struct list)); |
235 | 0 | mp_list->cmp = bgp_path_info_mpath_cmp; |
236 | 0 | } |
237 | | |
238 | | /* |
239 | | * bgp_mp_list_clear |
240 | | * |
241 | | * Clears all entries out of the mp_list |
242 | | */ |
243 | | void bgp_mp_list_clear(struct list *mp_list) |
244 | 0 | { |
245 | 0 | assert(mp_list); |
246 | 0 | list_delete_all_node(mp_list); |
247 | 0 | } |
248 | | |
249 | | /* |
250 | | * bgp_mp_list_add |
251 | | * |
252 | | * Adds a multipath entry to the mp_list |
253 | | */ |
254 | | void bgp_mp_list_add(struct list *mp_list, struct bgp_path_info *mpinfo) |
255 | 0 | { |
256 | 0 | assert(mp_list && mpinfo); |
257 | 0 | listnode_add_sort(mp_list, mpinfo); |
258 | 0 | } |
259 | | |
260 | | /* |
261 | | * bgp_path_info_mpath_new |
262 | | * |
263 | | * Allocate and zero memory for a new bgp_path_info_mpath element |
264 | | */ |
265 | | static struct bgp_path_info_mpath *bgp_path_info_mpath_new(void) |
266 | 0 | { |
267 | 0 | struct bgp_path_info_mpath *new_mpath; |
268 | |
|
269 | 0 | new_mpath = XCALLOC(MTYPE_BGP_MPATH_INFO, |
270 | 0 | sizeof(struct bgp_path_info_mpath)); |
271 | |
|
272 | 0 | return new_mpath; |
273 | 0 | } |
274 | | |
275 | | /* |
276 | | * bgp_path_info_mpath_free |
277 | | * |
278 | | * Release resources for a bgp_path_info_mpath element and zero out pointer |
279 | | */ |
280 | | void bgp_path_info_mpath_free(struct bgp_path_info_mpath **mpath) |
281 | 0 | { |
282 | 0 | if (mpath && *mpath) { |
283 | 0 | if ((*mpath)->mp_attr) |
284 | 0 | bgp_attr_unintern(&(*mpath)->mp_attr); |
285 | 0 | XFREE(MTYPE_BGP_MPATH_INFO, *mpath); |
286 | 0 | } |
287 | 0 | } |
288 | | |
289 | | /* |
290 | | * bgp_path_info_mpath_get |
291 | | * |
292 | | * Fetch the mpath element for the given bgp_path_info. Used for |
293 | | * doing lazy allocation. |
294 | | */ |
295 | | static struct bgp_path_info_mpath * |
296 | | bgp_path_info_mpath_get(struct bgp_path_info *path) |
297 | 0 | { |
298 | 0 | struct bgp_path_info_mpath *mpath; |
299 | |
|
300 | 0 | if (!path) |
301 | 0 | return NULL; |
302 | | |
303 | 0 | if (!path->mpath) { |
304 | 0 | mpath = bgp_path_info_mpath_new(); |
305 | 0 | path->mpath = mpath; |
306 | 0 | mpath->mp_info = path; |
307 | 0 | } |
308 | 0 | return path->mpath; |
309 | 0 | } |
310 | | |
311 | | /* |
312 | | * bgp_path_info_mpath_enqueue |
313 | | * |
314 | | * Enqueue a path onto the multipath list given the previous multipath |
315 | | * list entry |
316 | | */ |
317 | | static void bgp_path_info_mpath_enqueue(struct bgp_path_info *prev_info, |
318 | | struct bgp_path_info *path) |
319 | 0 | { |
320 | 0 | struct bgp_path_info_mpath *prev, *mpath; |
321 | |
|
322 | 0 | prev = bgp_path_info_mpath_get(prev_info); |
323 | 0 | mpath = bgp_path_info_mpath_get(path); |
324 | 0 | if (!prev || !mpath) |
325 | 0 | return; |
326 | | |
327 | 0 | mpath->mp_next = prev->mp_next; |
328 | 0 | mpath->mp_prev = prev; |
329 | 0 | if (prev->mp_next) |
330 | 0 | prev->mp_next->mp_prev = mpath; |
331 | 0 | prev->mp_next = mpath; |
332 | |
|
333 | 0 | SET_FLAG(path->flags, BGP_PATH_MULTIPATH); |
334 | 0 | } |
335 | | |
336 | | /* |
337 | | * bgp_path_info_mpath_dequeue |
338 | | * |
339 | | * Remove a path from the multipath list |
340 | | */ |
341 | | void bgp_path_info_mpath_dequeue(struct bgp_path_info *path) |
342 | 0 | { |
343 | 0 | struct bgp_path_info_mpath *mpath = path->mpath; |
344 | 0 | if (!mpath) |
345 | 0 | return; |
346 | 0 | if (mpath->mp_prev) |
347 | 0 | mpath->mp_prev->mp_next = mpath->mp_next; |
348 | 0 | if (mpath->mp_next) |
349 | 0 | mpath->mp_next->mp_prev = mpath->mp_prev; |
350 | 0 | mpath->mp_next = mpath->mp_prev = NULL; |
351 | 0 | UNSET_FLAG(path->flags, BGP_PATH_MULTIPATH); |
352 | 0 | } |
353 | | |
354 | | /* |
355 | | * bgp_path_info_mpath_next |
356 | | * |
357 | | * Given a bgp_path_info, return the next multipath entry |
358 | | */ |
359 | | struct bgp_path_info *bgp_path_info_mpath_next(struct bgp_path_info *path) |
360 | 0 | { |
361 | 0 | if (!path->mpath || !path->mpath->mp_next) |
362 | 0 | return NULL; |
363 | 0 | return path->mpath->mp_next->mp_info; |
364 | 0 | } |
365 | | |
366 | | /* |
367 | | * bgp_path_info_mpath_first |
368 | | * |
369 | | * Given bestpath bgp_path_info, return the first multipath entry. |
370 | | */ |
371 | | struct bgp_path_info *bgp_path_info_mpath_first(struct bgp_path_info *path) |
372 | 0 | { |
373 | 0 | return bgp_path_info_mpath_next(path); |
374 | 0 | } |
375 | | |
376 | | /* |
377 | | * bgp_path_info_mpath_count |
378 | | * |
379 | | * Given the bestpath bgp_path_info, return the number of multipath entries |
380 | | */ |
381 | | uint32_t bgp_path_info_mpath_count(struct bgp_path_info *path) |
382 | 0 | { |
383 | 0 | if (!path->mpath) |
384 | 0 | return 0; |
385 | 0 | return path->mpath->mp_count; |
386 | 0 | } |
387 | | |
388 | | /* |
389 | | * bgp_path_info_mpath_count_set |
390 | | * |
391 | | * Sets the count of multipaths into bestpath's mpath element |
392 | | */ |
393 | | static void bgp_path_info_mpath_count_set(struct bgp_path_info *path, |
394 | | uint16_t count) |
395 | 0 | { |
396 | 0 | struct bgp_path_info_mpath *mpath; |
397 | 0 | if (!count && !path->mpath) |
398 | 0 | return; |
399 | 0 | mpath = bgp_path_info_mpath_get(path); |
400 | 0 | if (!mpath) |
401 | 0 | return; |
402 | 0 | mpath->mp_count = count; |
403 | 0 | } |
404 | | |
405 | | /* |
406 | | * bgp_path_info_mpath_lb_update |
407 | | * |
408 | | * Update cumulative info related to link-bandwidth |
409 | | */ |
410 | | static void bgp_path_info_mpath_lb_update(struct bgp_path_info *path, bool set, |
411 | | bool all_paths_lb, uint64_t cum_bw) |
412 | 0 | { |
413 | 0 | struct bgp_path_info_mpath *mpath; |
414 | |
|
415 | 0 | mpath = path->mpath; |
416 | 0 | if (mpath == NULL) { |
417 | 0 | if (!set || (cum_bw == 0 && !all_paths_lb)) |
418 | 0 | return; |
419 | | |
420 | 0 | mpath = bgp_path_info_mpath_get(path); |
421 | 0 | if (!mpath) |
422 | 0 | return; |
423 | 0 | } |
424 | 0 | if (set) { |
425 | 0 | if (cum_bw) |
426 | 0 | SET_FLAG(mpath->mp_flags, BGP_MP_LB_PRESENT); |
427 | 0 | else |
428 | 0 | UNSET_FLAG(mpath->mp_flags, BGP_MP_LB_PRESENT); |
429 | 0 | if (all_paths_lb) |
430 | 0 | SET_FLAG(mpath->mp_flags, BGP_MP_LB_ALL); |
431 | 0 | else |
432 | 0 | UNSET_FLAG(mpath->mp_flags, BGP_MP_LB_ALL); |
433 | 0 | mpath->cum_bw = cum_bw; |
434 | 0 | } else { |
435 | 0 | mpath->mp_flags = 0; |
436 | 0 | mpath->cum_bw = 0; |
437 | 0 | } |
438 | 0 | } |
439 | | |
440 | | /* |
441 | | * bgp_path_info_mpath_attr |
442 | | * |
443 | | * Given bestpath bgp_path_info, return aggregated attribute set used |
444 | | * for advertising the multipath route |
445 | | */ |
446 | | struct attr *bgp_path_info_mpath_attr(struct bgp_path_info *path) |
447 | 0 | { |
448 | 0 | if (!path->mpath) |
449 | 0 | return NULL; |
450 | 0 | return path->mpath->mp_attr; |
451 | 0 | } |
452 | | |
453 | | /* |
454 | | * bgp_path_info_chkwtd |
455 | | * |
456 | | * Return if we should attempt to do weighted ECMP or not |
457 | | * The path passed in is the bestpath. |
458 | | */ |
459 | | bool bgp_path_info_mpath_chkwtd(struct bgp *bgp, struct bgp_path_info *path) |
460 | 0 | { |
461 | | /* Check if told to ignore weights or not multipath */ |
462 | 0 | if (bgp->lb_handling == BGP_LINK_BW_IGNORE_BW || !path->mpath) |
463 | 0 | return false; |
464 | | |
465 | | /* All paths in multipath should have associated weight (bandwidth) |
466 | | * unless told explicitly otherwise. |
467 | | */ |
468 | 0 | if (bgp->lb_handling != BGP_LINK_BW_SKIP_MISSING && |
469 | 0 | bgp->lb_handling != BGP_LINK_BW_DEFWT_4_MISSING) |
470 | 0 | return (path->mpath->mp_flags & BGP_MP_LB_ALL); |
471 | | |
472 | | /* At least one path should have bandwidth. */ |
473 | 0 | return (path->mpath->mp_flags & BGP_MP_LB_PRESENT); |
474 | 0 | } |
475 | | |
476 | | /* |
477 | | * bgp_path_info_mpath_attr |
478 | | * |
479 | | * Given bestpath bgp_path_info, return cumulative bandwidth |
480 | | * computed for all multipaths with bandwidth info |
481 | | */ |
482 | | uint64_t bgp_path_info_mpath_cumbw(struct bgp_path_info *path) |
483 | 0 | { |
484 | 0 | if (!path->mpath) |
485 | 0 | return 0; |
486 | 0 | return path->mpath->cum_bw; |
487 | 0 | } |
488 | | |
489 | | /* |
490 | | * bgp_path_info_mpath_attr_set |
491 | | * |
492 | | * Sets the aggregated attribute into bestpath's mpath element |
493 | | */ |
494 | | static void bgp_path_info_mpath_attr_set(struct bgp_path_info *path, |
495 | | struct attr *attr) |
496 | 0 | { |
497 | 0 | struct bgp_path_info_mpath *mpath; |
498 | 0 | if (!attr && !path->mpath) |
499 | 0 | return; |
500 | 0 | mpath = bgp_path_info_mpath_get(path); |
501 | 0 | if (!mpath) |
502 | 0 | return; |
503 | 0 | mpath->mp_attr = attr; |
504 | 0 | } |
505 | | |
506 | | /* |
507 | | * bgp_path_info_mpath_update |
508 | | * |
509 | | * Compare and sync up the multipath list with the mp_list generated by |
510 | | * bgp_best_selection |
511 | | */ |
512 | | void bgp_path_info_mpath_update(struct bgp *bgp, struct bgp_dest *dest, |
513 | | struct bgp_path_info *new_best, |
514 | | struct bgp_path_info *old_best, |
515 | | struct list *mp_list, |
516 | | struct bgp_maxpaths_cfg *mpath_cfg) |
517 | 0 | { |
518 | 0 | uint16_t maxpaths, mpath_count, old_mpath_count; |
519 | 0 | uint32_t bwval; |
520 | 0 | uint64_t cum_bw, old_cum_bw; |
521 | 0 | struct listnode *mp_node, *mp_next_node; |
522 | 0 | struct bgp_path_info *cur_mpath, *new_mpath, *next_mpath, *prev_mpath; |
523 | 0 | int mpath_changed, debug; |
524 | 0 | bool all_paths_lb; |
525 | 0 | char path_buf[PATH_ADDPATH_STR_BUFFER]; |
526 | |
|
527 | 0 | mpath_changed = 0; |
528 | 0 | maxpaths = multipath_num; |
529 | 0 | mpath_count = 0; |
530 | 0 | cur_mpath = NULL; |
531 | 0 | old_mpath_count = 0; |
532 | 0 | old_cum_bw = cum_bw = 0; |
533 | 0 | prev_mpath = new_best; |
534 | 0 | mp_node = listhead(mp_list); |
535 | 0 | debug = bgp_debug_bestpath(dest); |
536 | |
|
537 | 0 | if (new_best) { |
538 | 0 | mpath_count++; |
539 | 0 | if (new_best != old_best) |
540 | 0 | bgp_path_info_mpath_dequeue(new_best); |
541 | 0 | maxpaths = (new_best->peer->sort == BGP_PEER_IBGP) |
542 | 0 | ? mpath_cfg->maxpaths_ibgp |
543 | 0 | : mpath_cfg->maxpaths_ebgp; |
544 | 0 | } |
545 | |
|
546 | 0 | if (old_best) { |
547 | 0 | cur_mpath = bgp_path_info_mpath_first(old_best); |
548 | 0 | old_mpath_count = bgp_path_info_mpath_count(old_best); |
549 | 0 | old_cum_bw = bgp_path_info_mpath_cumbw(old_best); |
550 | 0 | bgp_path_info_mpath_count_set(old_best, 0); |
551 | 0 | bgp_path_info_mpath_lb_update(old_best, false, false, 0); |
552 | 0 | bgp_path_info_mpath_dequeue(old_best); |
553 | 0 | } |
554 | |
|
555 | 0 | if (debug) |
556 | 0 | zlog_debug( |
557 | 0 | "%pRN(%s): starting mpath update, newbest %s num candidates %d old-mpath-count %d old-cum-bw %" PRIu64, |
558 | 0 | bgp_dest_to_rnode(dest), bgp->name_pretty, |
559 | 0 | new_best ? new_best->peer->host : "NONE", |
560 | 0 | mp_list ? listcount(mp_list) : 0, old_mpath_count, |
561 | 0 | old_cum_bw); |
562 | | |
563 | | /* |
564 | | * We perform an ordered walk through both lists in parallel. |
565 | | * The reason for the ordered walk is that if there are paths |
566 | | * that were previously multipaths and are still multipaths, the walk |
567 | | * should encounter them in both lists at the same time. Otherwise |
568 | | * there will be paths that are in one list or another, and we |
569 | | * will deal with these separately. |
570 | | * |
571 | | * Note that new_best might be somewhere in the mp_list, so we need |
572 | | * to skip over it |
573 | | */ |
574 | 0 | all_paths_lb = true; /* We'll reset if any path doesn't have LB. */ |
575 | 0 | while (mp_node || cur_mpath) { |
576 | 0 | struct bgp_path_info *tmp_info; |
577 | | |
578 | | /* |
579 | | * We can bail out of this loop if all existing paths on the |
580 | | * multipath list have been visited (for cleanup purposes) and |
581 | | * the maxpath requirement is fulfulled |
582 | | */ |
583 | 0 | if (!cur_mpath && (mpath_count >= maxpaths)) |
584 | 0 | break; |
585 | | |
586 | 0 | mp_next_node = mp_node ? listnextnode(mp_node) : NULL; |
587 | 0 | next_mpath = |
588 | 0 | cur_mpath ? bgp_path_info_mpath_next(cur_mpath) : NULL; |
589 | 0 | tmp_info = mp_node ? listgetdata(mp_node) : NULL; |
590 | | |
591 | 0 | if (debug) |
592 | 0 | zlog_debug( |
593 | 0 | "%pRN(%s): comparing candidate %s with existing mpath %s", |
594 | 0 | bgp_dest_to_rnode(dest), bgp->name_pretty, |
595 | 0 | tmp_info ? tmp_info->peer->host : "NONE", |
596 | 0 | cur_mpath ? cur_mpath->peer->host : "NONE"); |
597 | | |
598 | | /* |
599 | | * If equal, the path was a multipath and is still a multipath. |
600 | | * Insert onto new multipath list if maxpaths allows. |
601 | | */ |
602 | 0 | if (mp_node && (listgetdata(mp_node) == cur_mpath)) { |
603 | 0 | list_delete_node(mp_list, mp_node); |
604 | 0 | bgp_path_info_mpath_dequeue(cur_mpath); |
605 | 0 | if ((mpath_count < maxpaths) |
606 | 0 | && prev_mpath |
607 | 0 | && bgp_path_info_nexthop_cmp(prev_mpath, |
608 | 0 | cur_mpath)) { |
609 | 0 | bgp_path_info_mpath_enqueue(prev_mpath, |
610 | 0 | cur_mpath); |
611 | 0 | prev_mpath = cur_mpath; |
612 | 0 | mpath_count++; |
613 | 0 | if (ecommunity_linkbw_present( |
614 | 0 | bgp_attr_get_ecommunity( |
615 | 0 | cur_mpath->attr), |
616 | 0 | &bwval)) |
617 | 0 | cum_bw += bwval; |
618 | 0 | else |
619 | 0 | all_paths_lb = false; |
620 | 0 | if (debug) { |
621 | 0 | bgp_path_info_path_with_addpath_rx_str( |
622 | 0 | cur_mpath, path_buf, |
623 | 0 | sizeof(path_buf)); |
624 | 0 | zlog_debug( |
625 | 0 | "%pRN: %s is still multipath, cur count %d", |
626 | 0 | bgp_dest_to_rnode(dest), |
627 | 0 | path_buf, mpath_count); |
628 | 0 | } |
629 | 0 | } else { |
630 | 0 | mpath_changed = 1; |
631 | 0 | if (debug) { |
632 | 0 | bgp_path_info_path_with_addpath_rx_str( |
633 | 0 | cur_mpath, path_buf, |
634 | 0 | sizeof(path_buf)); |
635 | 0 | zlog_debug( |
636 | 0 | "%pRN: remove mpath %s nexthop %pI4, cur count %d", |
637 | 0 | bgp_dest_to_rnode(dest), |
638 | 0 | path_buf, |
639 | 0 | &cur_mpath->attr->nexthop, |
640 | 0 | mpath_count); |
641 | 0 | } |
642 | 0 | } |
643 | 0 | mp_node = mp_next_node; |
644 | 0 | cur_mpath = next_mpath; |
645 | 0 | continue; |
646 | 0 | } |
647 | | |
648 | 0 | if (cur_mpath |
649 | 0 | && (!mp_node |
650 | 0 | || (bgp_path_info_mpath_cmp(cur_mpath, |
651 | 0 | listgetdata(mp_node)) |
652 | 0 | < 0))) { |
653 | | /* |
654 | | * If here, we have an old multipath and either the |
655 | | * mp_list |
656 | | * is finished or the next mp_node points to a later |
657 | | * multipath, so we need to purge this path from the |
658 | | * multipath list |
659 | | */ |
660 | 0 | bgp_path_info_mpath_dequeue(cur_mpath); |
661 | 0 | mpath_changed = 1; |
662 | 0 | if (debug) { |
663 | 0 | bgp_path_info_path_with_addpath_rx_str( |
664 | 0 | cur_mpath, path_buf, sizeof(path_buf)); |
665 | 0 | zlog_debug( |
666 | 0 | "%pRN: remove mpath %s nexthop %pI4, cur count %d", |
667 | 0 | bgp_dest_to_rnode(dest), path_buf, |
668 | 0 | &cur_mpath->attr->nexthop, mpath_count); |
669 | 0 | } |
670 | 0 | cur_mpath = next_mpath; |
671 | 0 | } else { |
672 | | /* |
673 | | * If here, we have a path on the mp_list that was not |
674 | | * previously |
675 | | * a multipath (due to non-equivalance or maxpaths |
676 | | * exceeded), |
677 | | * or the matching multipath is sorted later in the |
678 | | * multipath |
679 | | * list. Before we enqueue the path on the new multipath |
680 | | * list, |
681 | | * make sure its not on the old_best multipath list or |
682 | | * referenced |
683 | | * via next_mpath: |
684 | | * - If next_mpath points to this new path, update |
685 | | * next_mpath to |
686 | | * point to the multipath after this one |
687 | | * - Dequeue the path from the multipath list just to |
688 | | * make sure |
689 | | */ |
690 | 0 | new_mpath = listgetdata(mp_node); |
691 | 0 | list_delete_node(mp_list, mp_node); |
692 | 0 | assert(new_mpath); |
693 | 0 | assert(prev_mpath); |
694 | 0 | if ((mpath_count < maxpaths) && (new_mpath != new_best) |
695 | 0 | && bgp_path_info_nexthop_cmp(prev_mpath, |
696 | 0 | new_mpath)) { |
697 | 0 | bgp_path_info_mpath_dequeue(new_mpath); |
698 | |
|
699 | 0 | bgp_path_info_mpath_enqueue(prev_mpath, |
700 | 0 | new_mpath); |
701 | 0 | prev_mpath = new_mpath; |
702 | 0 | mpath_changed = 1; |
703 | 0 | mpath_count++; |
704 | 0 | if (ecommunity_linkbw_present( |
705 | 0 | bgp_attr_get_ecommunity( |
706 | 0 | new_mpath->attr), |
707 | 0 | &bwval)) |
708 | 0 | cum_bw += bwval; |
709 | 0 | else |
710 | 0 | all_paths_lb = false; |
711 | 0 | if (debug) { |
712 | 0 | bgp_path_info_path_with_addpath_rx_str( |
713 | 0 | new_mpath, path_buf, |
714 | 0 | sizeof(path_buf)); |
715 | 0 | zlog_debug( |
716 | 0 | "%pRN: add mpath %s nexthop %pI4, cur count %d", |
717 | 0 | bgp_dest_to_rnode(dest), |
718 | 0 | path_buf, |
719 | 0 | &new_mpath->attr->nexthop, |
720 | 0 | mpath_count); |
721 | 0 | } |
722 | 0 | } |
723 | 0 | mp_node = mp_next_node; |
724 | 0 | } |
725 | 0 | } |
726 | | |
727 | 0 | if (new_best) { |
728 | 0 | bgp_path_info_mpath_count_set(new_best, mpath_count - 1); |
729 | 0 | if (mpath_count <= 1 || |
730 | 0 | !ecommunity_linkbw_present( |
731 | 0 | bgp_attr_get_ecommunity(new_best->attr), &bwval)) |
732 | 0 | all_paths_lb = false; |
733 | 0 | else |
734 | 0 | cum_bw += bwval; |
735 | 0 | bgp_path_info_mpath_lb_update(new_best, true, |
736 | 0 | all_paths_lb, cum_bw); |
737 | |
|
738 | 0 | if (debug) |
739 | 0 | zlog_debug( |
740 | 0 | "%pRN(%s): New mpath count (incl newbest) %d mpath-change %s all_paths_lb %d cum_bw %" PRIu64, |
741 | 0 | bgp_dest_to_rnode(dest), bgp->name_pretty, |
742 | 0 | mpath_count, mpath_changed ? "YES" : "NO", |
743 | 0 | all_paths_lb, cum_bw); |
744 | |
|
745 | 0 | if (mpath_changed |
746 | 0 | || (bgp_path_info_mpath_count(new_best) != old_mpath_count)) |
747 | 0 | SET_FLAG(new_best->flags, BGP_PATH_MULTIPATH_CHG); |
748 | 0 | if ((mpath_count - 1) != old_mpath_count || |
749 | 0 | old_cum_bw != cum_bw) |
750 | 0 | SET_FLAG(new_best->flags, BGP_PATH_LINK_BW_CHG); |
751 | 0 | } |
752 | 0 | } |
753 | | |
754 | | /* |
755 | | * bgp_mp_dmed_deselect |
756 | | * |
757 | | * Clean up multipath information for BGP_PATH_DMED_SELECTED path that |
758 | | * is not selected as best path |
759 | | */ |
760 | | void bgp_mp_dmed_deselect(struct bgp_path_info *dmed_best) |
761 | 0 | { |
762 | 0 | struct bgp_path_info *mpinfo, *mpnext; |
763 | |
|
764 | 0 | if (!dmed_best) |
765 | 0 | return; |
766 | | |
767 | 0 | for (mpinfo = bgp_path_info_mpath_first(dmed_best); mpinfo; |
768 | 0 | mpinfo = mpnext) { |
769 | 0 | mpnext = bgp_path_info_mpath_next(mpinfo); |
770 | 0 | bgp_path_info_mpath_dequeue(mpinfo); |
771 | 0 | } |
772 | |
|
773 | 0 | bgp_path_info_mpath_count_set(dmed_best, 0); |
774 | 0 | UNSET_FLAG(dmed_best->flags, BGP_PATH_MULTIPATH_CHG); |
775 | 0 | UNSET_FLAG(dmed_best->flags, BGP_PATH_LINK_BW_CHG); |
776 | 0 | assert(bgp_path_info_mpath_first(dmed_best) == NULL); |
777 | 0 | } |
778 | | |
779 | | /* |
780 | | * bgp_path_info_mpath_aggregate_update |
781 | | * |
782 | | * Set the multipath aggregate attribute. We need to see if the |
783 | | * aggregate has changed and then set the ATTR_CHANGED flag on the |
784 | | * bestpath info so that a peer update will be generated. The |
785 | | * change is detected by generating the current attribute, |
786 | | * interning it, and then comparing the interned pointer with the |
787 | | * current value. We can skip this generate/compare step if there |
788 | | * is no change in multipath selection and no attribute change in |
789 | | * any multipath. |
790 | | */ |
791 | | void bgp_path_info_mpath_aggregate_update(struct bgp_path_info *new_best, |
792 | | struct bgp_path_info *old_best) |
793 | 0 | { |
794 | 0 | struct bgp_path_info *mpinfo; |
795 | 0 | struct aspath *aspath; |
796 | 0 | struct aspath *asmerge; |
797 | 0 | struct attr *new_attr, *old_attr; |
798 | 0 | uint8_t origin; |
799 | 0 | struct community *community, *commerge; |
800 | 0 | struct ecommunity *ecomm, *ecommerge; |
801 | 0 | struct lcommunity *lcomm, *lcommerge; |
802 | 0 | struct attr attr = {0}; |
803 | |
|
804 | 0 | if (old_best && (old_best != new_best) |
805 | 0 | && (old_attr = bgp_path_info_mpath_attr(old_best))) { |
806 | 0 | bgp_attr_unintern(&old_attr); |
807 | 0 | bgp_path_info_mpath_attr_set(old_best, NULL); |
808 | 0 | } |
809 | |
|
810 | 0 | if (!new_best) |
811 | 0 | return; |
812 | | |
813 | 0 | if (!bgp_path_info_mpath_count(new_best)) { |
814 | 0 | if ((new_attr = bgp_path_info_mpath_attr(new_best))) { |
815 | 0 | bgp_attr_unintern(&new_attr); |
816 | 0 | bgp_path_info_mpath_attr_set(new_best, NULL); |
817 | 0 | SET_FLAG(new_best->flags, BGP_PATH_ATTR_CHANGED); |
818 | 0 | } |
819 | 0 | return; |
820 | 0 | } |
821 | | |
822 | 0 | attr = *new_best->attr; |
823 | |
|
824 | 0 | if (new_best->peer |
825 | 0 | && CHECK_FLAG(new_best->peer->bgp->flags, |
826 | 0 | BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { |
827 | | |
828 | | /* aggregate attribute from multipath constituents */ |
829 | 0 | aspath = aspath_dup(attr.aspath); |
830 | 0 | origin = attr.origin; |
831 | 0 | community = |
832 | 0 | bgp_attr_get_community(&attr) |
833 | 0 | ? community_dup(bgp_attr_get_community(&attr)) |
834 | 0 | : NULL; |
835 | 0 | ecomm = (bgp_attr_get_ecommunity(&attr)) |
836 | 0 | ? ecommunity_dup(bgp_attr_get_ecommunity(&attr)) |
837 | 0 | : NULL; |
838 | 0 | lcomm = (bgp_attr_get_lcommunity(&attr)) |
839 | 0 | ? lcommunity_dup(bgp_attr_get_lcommunity(&attr)) |
840 | 0 | : NULL; |
841 | |
|
842 | 0 | for (mpinfo = bgp_path_info_mpath_first(new_best); mpinfo; |
843 | 0 | mpinfo = bgp_path_info_mpath_next(mpinfo)) { |
844 | 0 | asmerge = |
845 | 0 | aspath_aggregate(aspath, mpinfo->attr->aspath); |
846 | 0 | aspath_free(aspath); |
847 | 0 | aspath = asmerge; |
848 | |
|
849 | 0 | if (origin < mpinfo->attr->origin) |
850 | 0 | origin = mpinfo->attr->origin; |
851 | |
|
852 | 0 | if (bgp_attr_get_community(mpinfo->attr)) { |
853 | 0 | if (community) { |
854 | 0 | commerge = community_merge( |
855 | 0 | community, |
856 | 0 | bgp_attr_get_community( |
857 | 0 | mpinfo->attr)); |
858 | 0 | community = |
859 | 0 | community_uniq_sort(commerge); |
860 | 0 | community_free(&commerge); |
861 | 0 | } else |
862 | 0 | community = community_dup( |
863 | 0 | bgp_attr_get_community( |
864 | 0 | mpinfo->attr)); |
865 | 0 | } |
866 | |
|
867 | 0 | if (bgp_attr_get_ecommunity(mpinfo->attr)) { |
868 | 0 | if (ecomm) { |
869 | 0 | ecommerge = ecommunity_merge( |
870 | 0 | ecomm, bgp_attr_get_ecommunity( |
871 | 0 | mpinfo->attr)); |
872 | 0 | ecomm = ecommunity_uniq_sort(ecommerge); |
873 | 0 | ecommunity_free(&ecommerge); |
874 | 0 | } else |
875 | 0 | ecomm = ecommunity_dup( |
876 | 0 | bgp_attr_get_ecommunity( |
877 | 0 | mpinfo->attr)); |
878 | 0 | } |
879 | 0 | if (bgp_attr_get_lcommunity(mpinfo->attr)) { |
880 | 0 | if (lcomm) { |
881 | 0 | lcommerge = lcommunity_merge( |
882 | 0 | lcomm, bgp_attr_get_lcommunity( |
883 | 0 | mpinfo->attr)); |
884 | 0 | lcomm = lcommunity_uniq_sort(lcommerge); |
885 | 0 | lcommunity_free(&lcommerge); |
886 | 0 | } else |
887 | 0 | lcomm = lcommunity_dup( |
888 | 0 | bgp_attr_get_lcommunity( |
889 | 0 | mpinfo->attr)); |
890 | 0 | } |
891 | 0 | } |
892 | |
|
893 | 0 | attr.aspath = aspath; |
894 | 0 | attr.origin = origin; |
895 | 0 | if (community) |
896 | 0 | bgp_attr_set_community(&attr, community); |
897 | 0 | if (ecomm) |
898 | 0 | bgp_attr_set_ecommunity(&attr, ecomm); |
899 | 0 | if (lcomm) |
900 | 0 | bgp_attr_set_lcommunity(&attr, lcomm); |
901 | | |
902 | | /* Zap multipath attr nexthop so we set nexthop to self */ |
903 | 0 | attr.nexthop.s_addr = INADDR_ANY; |
904 | 0 | memset(&attr.mp_nexthop_global, 0, sizeof(struct in6_addr)); |
905 | | |
906 | | /* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */ |
907 | 0 | } |
908 | |
|
909 | 0 | new_attr = bgp_attr_intern(&attr); |
910 | |
|
911 | 0 | if (new_attr != bgp_path_info_mpath_attr(new_best)) { |
912 | 0 | if ((old_attr = bgp_path_info_mpath_attr(new_best))) |
913 | 0 | bgp_attr_unintern(&old_attr); |
914 | 0 | bgp_path_info_mpath_attr_set(new_best, new_attr); |
915 | 0 | SET_FLAG(new_best->flags, BGP_PATH_ATTR_CHANGED); |
916 | 0 | } else |
917 | 0 | bgp_attr_unintern(&new_attr); |
918 | 0 | } |