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