blob: 8195e47f4ab1e57f1d43493274ba62b6e508e9e1 [file] [log] [blame]
Josh Bailey165b5ff2011-07-20 20:43:22 -07001/* $QuaggaId: Format:%an, %ai, %h$ $
2 *
3 * BGP Multipath
4 * Copyright (C) 2010 Google Inc.
5 *
6 * This file is part of Quagga
7 *
8 * Quagga is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * Quagga is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Quagga; see the file COPYING. If not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 * 02111-1307, USA.
22 */
23
24#include <zebra.h>
25
26#include "command.h"
Josh Bailey96450fa2011-07-20 20:45:12 -070027#include "prefix.h"
28#include "linklist.h"
29#include "sockunion.h"
Josh Baileyde8d5df2011-07-20 20:46:01 -070030#include "memory.h"
Donald Sharp04907292016-01-07 10:03:01 -050031#include "filter.h"
Josh Bailey165b5ff2011-07-20 20:43:22 -070032
33#include "bgpd/bgpd.h"
Josh Bailey96450fa2011-07-20 20:45:12 -070034#include "bgpd/bgp_table.h"
35#include "bgpd/bgp_route.h"
36#include "bgpd/bgp_attr.h"
Josh Baileyde8d5df2011-07-20 20:46:01 -070037#include "bgpd/bgp_debug.h"
Josh Bailey0b597ef2011-07-20 20:49:11 -070038#include "bgpd/bgp_aspath.h"
39#include "bgpd/bgp_community.h"
40#include "bgpd/bgp_ecommunity.h"
Job Snijders3334bab2017-01-20 14:47:12 +000041#include "bgpd/bgp_lcommunity.h"
Josh Bailey165b5ff2011-07-20 20:43:22 -070042#include "bgpd/bgp_mpath.h"
43
Paul Jakma6d4742b2015-11-25 17:14:37 +000044bool
45bgp_mpath_is_configured_sort (struct bgp *bgp, bgp_peer_sort_t sort,
46 afi_t afi, safi_t safi)
47{
48 struct bgp_maxpaths_cfg *cfg = &bgp->maxpaths[afi][safi];
49
50 /* XXX: BGP_DEFAULT_MAXPATHS is 1, and this test only seems to make sense
51 * if if it stays 1, so not sure the DEFAULT define is that useful.
52 */
53 switch (sort)
54 {
55 case BGP_PEER_IBGP:
56 return cfg->maxpaths_ibgp != BGP_DEFAULT_MAXPATHS;
57 case BGP_PEER_EBGP:
58 return cfg->maxpaths_ebgp != BGP_DEFAULT_MAXPATHS;
59 default:
60 return false;
61 }
62}
63
64bool
65bgp_mpath_is_configured (struct bgp *bgp, afi_t afi, safi_t safi)
66{
67 return bgp_mpath_is_configured_sort (bgp, BGP_PEER_IBGP, afi, safi)
68 || bgp_mpath_is_configured_sort (bgp, BGP_PEER_EBGP, afi, safi);
69}
70
Josh Bailey165b5ff2011-07-20 20:43:22 -070071/*
72 * bgp_maximum_paths_set
73 *
74 * Record maximum-paths configuration for BGP instance
75 */
76int
77bgp_maximum_paths_set (struct bgp *bgp, afi_t afi, safi_t safi,
78 int peertype, u_int16_t maxpaths)
79{
80 if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX))
81 return -1;
82
83 switch (peertype)
84 {
85 case BGP_PEER_IBGP:
86 bgp->maxpaths[afi][safi].maxpaths_ibgp = maxpaths;
87 break;
88 case BGP_PEER_EBGP:
89 bgp->maxpaths[afi][safi].maxpaths_ebgp = maxpaths;
90 break;
91 default:
92 return -1;
93 }
94
95 return 0;
96}
97
98/*
99 * bgp_maximum_paths_unset
100 *
101 * Remove maximum-paths configuration from BGP instance
102 */
103int
104bgp_maximum_paths_unset (struct bgp *bgp, afi_t afi, safi_t safi,
105 int peertype)
106{
107 if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX))
108 return -1;
109
110 switch (peertype)
111 {
112 case BGP_PEER_IBGP:
113 bgp->maxpaths[afi][safi].maxpaths_ibgp = BGP_DEFAULT_MAXPATHS;
114 break;
115 case BGP_PEER_EBGP:
116 bgp->maxpaths[afi][safi].maxpaths_ebgp = BGP_DEFAULT_MAXPATHS;
117 break;
118 default:
119 return -1;
120 }
121
122 return 0;
123}
Josh Bailey96450fa2011-07-20 20:45:12 -0700124
125/*
126 * bgp_info_nexthop_cmp
127 *
128 * Compare the nexthops of two paths. Return value is less than, equal to,
129 * or greater than zero if bi1 is respectively less than, equal to,
130 * or greater than bi2.
131 */
132static int
133bgp_info_nexthop_cmp (struct bgp_info *bi1, struct bgp_info *bi2)
134{
135 struct attr_extra *ae1, *ae2;
136 int compare;
137
Josh Bailey0b597ef2011-07-20 20:49:11 -0700138 ae1 = bi1->attr->extra;
139 ae2 = bi2->attr->extra;
Josh Bailey96450fa2011-07-20 20:45:12 -0700140
141 compare = IPV4_ADDR_CMP (&bi1->attr->nexthop, &bi2->attr->nexthop);
142
Donald Sharpd8c5f272016-03-16 08:10:56 -0400143 if (!compare && ae1 && ae2)
Josh Bailey96450fa2011-07-20 20:45:12 -0700144 {
Donald Sharpd8c5f272016-03-16 08:10:56 -0400145 if (ae1->mp_nexthop_len == ae2->mp_nexthop_len)
Josh Bailey96450fa2011-07-20 20:45:12 -0700146 {
Donald Sharpd8c5f272016-03-16 08:10:56 -0400147 switch (ae1->mp_nexthop_len)
148 {
149 case 4:
150 case 12:
151 compare = IPV4_ADDR_CMP (&ae1->mp_nexthop_global_in,
152 &ae2->mp_nexthop_global_in);
153 break;
154 case 16:
155 compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
156 &ae2->mp_nexthop_global);
157 break;
158 case 32:
159 compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
160 &ae2->mp_nexthop_global);
161 if (!compare)
162 compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_local,
163 &ae2->mp_nexthop_local);
164 break;
165 }
166 }
167
168 /* This can happen if one IPv6 peer sends you global and link-local
169 * nexthops but another IPv6 peer only sends you global
170 */
171 else if (ae1->mp_nexthop_len == 16 || ae1->mp_nexthop_len == 32)
172 {
Josh Bailey96450fa2011-07-20 20:45:12 -0700173 compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
174 &ae2->mp_nexthop_global);
175 if (!compare)
Donald Sharpd8c5f272016-03-16 08:10:56 -0400176 {
177 if (ae1->mp_nexthop_len < ae2->mp_nexthop_len)
178 compare = -1;
179 else
180 compare = 1;
181 }
Josh Bailey96450fa2011-07-20 20:45:12 -0700182 }
183 }
184
185 return compare;
186}
187
188/*
189 * bgp_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 */
200static int
201bgp_info_mpath_cmp (void *val1, void *val2)
202{
203 struct bgp_info *bi1, *bi2;
204 int compare;
205
206 bi1 = val1;
207 bi2 = val2;
208
209 compare = bgp_info_nexthop_cmp (bi1, bi2);
210
211 if (!compare)
212 compare = sockunion_cmp (bi1->peer->su_remote, bi2->peer->su_remote);
213
214 return compare;
215}
216
217/*
218 * bgp_mp_list_init
219 *
220 * Initialize the mp_list, which holds the list of multipaths
221 * selected by bgp_best_selection
222 */
223void
224bgp_mp_list_init (struct list *mp_list)
225{
226 assert (mp_list);
227 memset (mp_list, 0, sizeof (struct list));
228 mp_list->cmp = bgp_info_mpath_cmp;
229}
230
231/*
232 * bgp_mp_list_clear
233 *
234 * Clears all entries out of the mp_list
235 */
236void
237bgp_mp_list_clear (struct list *mp_list)
238{
239 assert (mp_list);
240 list_delete_all_node (mp_list);
241}
242
243/*
244 * bgp_mp_list_add
245 *
246 * Adds a multipath entry to the mp_list
247 */
248void
249bgp_mp_list_add (struct list *mp_list, struct bgp_info *mpinfo)
250{
251 assert (mp_list && mpinfo);
252 listnode_add_sort (mp_list, mpinfo);
253}
Josh Baileyde8d5df2011-07-20 20:46:01 -0700254
255/*
256 * bgp_info_mpath_new
257 *
258 * Allocate and zero memory for a new bgp_info_mpath element
259 */
260static struct bgp_info_mpath *
261bgp_info_mpath_new (void)
262{
263 struct bgp_info_mpath *new_mpath;
264 new_mpath = XCALLOC (MTYPE_BGP_MPATH_INFO, sizeof (struct bgp_info_mpath));
265 return new_mpath;
266}
267
268/*
269 * bgp_info_mpath_free
270 *
271 * Release resources for a bgp_info_mpath element and zero out pointer
272 */
273void
274bgp_info_mpath_free (struct bgp_info_mpath **mpath)
275{
276 if (mpath && *mpath)
277 {
Josh Bailey0b597ef2011-07-20 20:49:11 -0700278 if ((*mpath)->mp_attr)
David Lamparterfac3c242012-04-28 22:37:20 +0200279 bgp_attr_unintern (&(*mpath)->mp_attr);
Josh Baileyde8d5df2011-07-20 20:46:01 -0700280 XFREE (MTYPE_BGP_MPATH_INFO, *mpath);
281 *mpath = NULL;
282 }
283}
284
285/*
286 * bgp_info_mpath_get
287 *
288 * Fetch the mpath element for the given bgp_info. Used for
289 * doing lazy allocation.
290 */
291static struct bgp_info_mpath *
292bgp_info_mpath_get (struct bgp_info *binfo)
293{
294 struct bgp_info_mpath *mpath;
295 if (!binfo->mpath)
296 {
297 mpath = bgp_info_mpath_new();
298 if (!mpath)
299 return NULL;
300 binfo->mpath = mpath;
301 mpath->mp_info = binfo;
302 }
303 return binfo->mpath;
304}
305
306/*
307 * bgp_info_mpath_enqueue
308 *
309 * Enqueue a path onto the multipath list given the previous multipath
310 * list entry
311 */
312static void
313bgp_info_mpath_enqueue (struct bgp_info *prev_info, struct bgp_info *binfo)
314{
315 struct bgp_info_mpath *prev, *mpath;
316
317 prev = bgp_info_mpath_get (prev_info);
318 mpath = bgp_info_mpath_get (binfo);
319 if (!prev || !mpath)
320 return;
321
322 mpath->mp_next = prev->mp_next;
323 mpath->mp_prev = prev;
324 if (prev->mp_next)
325 prev->mp_next->mp_prev = mpath;
326 prev->mp_next = mpath;
327
328 SET_FLAG (binfo->flags, BGP_INFO_MULTIPATH);
329}
330
331/*
332 * bgp_info_mpath_dequeue
333 *
334 * Remove a path from the multipath list
335 */
336void
337bgp_info_mpath_dequeue (struct bgp_info *binfo)
338{
339 struct bgp_info_mpath *mpath = binfo->mpath;
340 if (!mpath)
341 return;
342 if (mpath->mp_prev)
343 mpath->mp_prev->mp_next = mpath->mp_next;
344 if (mpath->mp_next)
345 mpath->mp_next->mp_prev = mpath->mp_prev;
346 mpath->mp_next = mpath->mp_prev = NULL;
347 UNSET_FLAG (binfo->flags, BGP_INFO_MULTIPATH);
348}
349
350/*
351 * bgp_info_mpath_next
352 *
353 * Given a bgp_info, return the next multipath entry
354 */
355struct bgp_info *
356bgp_info_mpath_next (struct bgp_info *binfo)
357{
358 if (!binfo->mpath || !binfo->mpath->mp_next)
359 return NULL;
360 return binfo->mpath->mp_next->mp_info;
361}
362
363/*
364 * bgp_info_mpath_first
365 *
366 * Given bestpath bgp_info, return the first multipath entry.
367 */
368struct bgp_info *
369bgp_info_mpath_first (struct bgp_info *binfo)
370{
371 return bgp_info_mpath_next (binfo);
372}
373
374/*
375 * bgp_info_mpath_count
376 *
377 * Given the bestpath bgp_info, return the number of multipath entries
378 */
379u_int32_t
380bgp_info_mpath_count (struct bgp_info *binfo)
381{
382 if (!binfo->mpath)
383 return 0;
384 return binfo->mpath->mp_count;
385}
386
387/*
388 * bgp_info_mpath_count_set
389 *
390 * Sets the count of multipaths into bestpath's mpath element
391 */
392static void
393bgp_info_mpath_count_set (struct bgp_info *binfo, u_int32_t count)
394{
395 struct bgp_info_mpath *mpath;
396 if (!count && !binfo->mpath)
397 return;
398 mpath = bgp_info_mpath_get (binfo);
399 if (!mpath)
400 return;
401 mpath->mp_count = count;
402}
403
404/*
Josh Bailey0b597ef2011-07-20 20:49:11 -0700405 * bgp_info_mpath_attr
406 *
407 * Given bestpath bgp_info, return aggregated attribute set used
408 * for advertising the multipath route
409 */
410struct attr *
411bgp_info_mpath_attr (struct bgp_info *binfo)
412{
413 if (!binfo->mpath)
414 return NULL;
415 return binfo->mpath->mp_attr;
416}
417
418/*
419 * bgp_info_mpath_attr_set
420 *
421 * Sets the aggregated attribute into bestpath's mpath element
422 */
423static void
424bgp_info_mpath_attr_set (struct bgp_info *binfo, struct attr *attr)
425{
426 struct bgp_info_mpath *mpath;
427 if (!attr && !binfo->mpath)
428 return;
429 mpath = bgp_info_mpath_get (binfo);
430 if (!mpath)
431 return;
432 mpath->mp_attr = attr;
433}
434
435/*
Josh Baileyde8d5df2011-07-20 20:46:01 -0700436 * bgp_info_mpath_update
437 *
438 * Compare and sync up the multipath list with the mp_list generated by
439 * bgp_best_selection
440 */
441void
442bgp_info_mpath_update (struct bgp_node *rn, struct bgp_info *new_best,
443 struct bgp_info *old_best, struct list *mp_list,
Paul Jakma6d4742b2015-11-25 17:14:37 +0000444 afi_t afi, safi_t safi)
Josh Baileyde8d5df2011-07-20 20:46:01 -0700445{
446 u_int16_t maxpaths, mpath_count, old_mpath_count;
447 struct listnode *mp_node, *mp_next_node;
448 struct bgp_info *cur_mpath, *new_mpath, *next_mpath, *prev_mpath;
449 int mpath_changed, debug;
Donald Sharpd8c5f272016-03-16 08:10:56 -0400450 char pfx_buf[INET6_ADDRSTRLEN], nh_buf[2][INET6_ADDRSTRLEN];
Donald Sharpe98dce62016-01-20 07:53:51 -0500451 struct bgp_maxpaths_cfg *mpath_cfg = NULL;
Josh Baileyde8d5df2011-07-20 20:46:01 -0700452
453 mpath_changed = 0;
454 maxpaths = BGP_DEFAULT_MAXPATHS;
455 mpath_count = 0;
456 cur_mpath = NULL;
457 old_mpath_count = 0;
458 prev_mpath = new_best;
459 mp_node = listhead (mp_list);
Josh Baileyde8d5df2011-07-20 20:46:01 -0700460
Donald Sharpe98dce62016-01-20 07:53:51 -0500461 debug = BGP_DEBUG (events, EVENTS);
Paul Jakma6d4742b2015-11-25 17:14:37 +0000462
Josh Baileyde8d5df2011-07-20 20:46:01 -0700463 if (debug)
464 prefix2str (&rn->p, pfx_buf, sizeof (pfx_buf));
465
466 if (new_best)
467 {
Donald Sharpe98dce62016-01-20 07:53:51 -0500468 mpath_cfg = &new_best->peer->bgp->maxpaths[afi][safi];
Josh Baileyde8d5df2011-07-20 20:46:01 -0700469 mpath_count++;
470 if (new_best != old_best)
471 bgp_info_mpath_dequeue (new_best);
Jorge Boncompte [DTI2]6d85b152012-05-07 16:52:54 +0000472 maxpaths = (new_best->peer->sort == BGP_PEER_IBGP) ?
Josh Baileyde8d5df2011-07-20 20:46:01 -0700473 mpath_cfg->maxpaths_ibgp : mpath_cfg->maxpaths_ebgp;
474 }
475
476 if (old_best)
477 {
478 cur_mpath = bgp_info_mpath_first (old_best);
479 old_mpath_count = bgp_info_mpath_count (old_best);
480 bgp_info_mpath_count_set (old_best, 0);
481 bgp_info_mpath_dequeue (old_best);
482 }
483
484 /*
485 * We perform an ordered walk through both lists in parallel.
486 * The reason for the ordered walk is that if there are paths
487 * that were previously multipaths and are still multipaths, the walk
488 * should encounter them in both lists at the same time. Otherwise
489 * there will be paths that are in one list or another, and we
490 * will deal with these separately.
491 *
492 * Note that new_best might be somewhere in the mp_list, so we need
493 * to skip over it
494 */
495 while (mp_node || cur_mpath)
496 {
497 /*
498 * We can bail out of this loop if all existing paths on the
499 * multipath list have been visited (for cleanup purposes) and
500 * the maxpath requirement is fulfulled
501 */
502 if (!cur_mpath && (mpath_count >= maxpaths))
503 break;
504
505 mp_next_node = mp_node ? listnextnode (mp_node) : NULL;
506 next_mpath = cur_mpath ? bgp_info_mpath_next (cur_mpath) : NULL;
507
508 /*
509 * If equal, the path was a multipath and is still a multipath.
510 * Insert onto new multipath list if maxpaths allows.
511 */
512 if (mp_node && (listgetdata (mp_node) == cur_mpath))
513 {
514 list_delete_node (mp_list, mp_node);
515 bgp_info_mpath_dequeue (cur_mpath);
516 if ((mpath_count < maxpaths) &&
517 bgp_info_nexthop_cmp (prev_mpath, cur_mpath))
518 {
519 bgp_info_mpath_enqueue (prev_mpath, cur_mpath);
520 prev_mpath = cur_mpath;
521 mpath_count++;
522 }
523 else
524 {
525 mpath_changed = 1;
526 if (debug)
527 zlog_debug ("%s remove mpath nexthop %s peer %s", pfx_buf,
528 inet_ntop (AF_INET, &cur_mpath->attr->nexthop,
529 nh_buf[0], sizeof (nh_buf[0])),
530 sockunion2str (cur_mpath->peer->su_remote,
531 nh_buf[1], sizeof (nh_buf[1])));
532 }
533 mp_node = mp_next_node;
534 cur_mpath = next_mpath;
535 continue;
536 }
537
538 if (cur_mpath && (!mp_node ||
539 (bgp_info_mpath_cmp (cur_mpath,
540 listgetdata (mp_node)) < 0)))
541 {
542 /*
543 * If here, we have an old multipath and either the mp_list
544 * is finished or the next mp_node points to a later
545 * multipath, so we need to purge this path from the
546 * multipath list
547 */
548 bgp_info_mpath_dequeue (cur_mpath);
549 mpath_changed = 1;
550 if (debug)
551 zlog_debug ("%s remove mpath nexthop %s peer %s", pfx_buf,
552 inet_ntop (AF_INET, &cur_mpath->attr->nexthop,
553 nh_buf[0], sizeof (nh_buf[0])),
554 sockunion2str (cur_mpath->peer->su_remote,
555 nh_buf[1], sizeof (nh_buf[1])));
556 cur_mpath = next_mpath;
557 }
558 else
559 {
560 /*
561 * If here, we have a path on the mp_list that was not previously
562 * a multipath (due to non-equivalance or maxpaths exceeded),
563 * or the matching multipath is sorted later in the multipath
564 * list. Before we enqueue the path on the new multipath list,
565 * make sure its not on the old_best multipath list or referenced
566 * via next_mpath:
567 * - If next_mpath points to this new path, update next_mpath to
568 * point to the multipath after this one
569 * - Dequeue the path from the multipath list just to make sure
570 */
571 new_mpath = listgetdata (mp_node);
572 list_delete_node (mp_list, mp_node);
Josh Baileyde8d5df2011-07-20 20:46:01 -0700573 if ((mpath_count < maxpaths) && (new_mpath != new_best) &&
574 bgp_info_nexthop_cmp (prev_mpath, new_mpath))
575 {
Josh Bailey78d92e12011-07-20 20:51:07 -0700576 if (new_mpath == next_mpath)
577 next_mpath = bgp_info_mpath_next (new_mpath);
578 bgp_info_mpath_dequeue (new_mpath);
579
Josh Baileyde8d5df2011-07-20 20:46:01 -0700580 bgp_info_mpath_enqueue (prev_mpath, new_mpath);
581 prev_mpath = new_mpath;
582 mpath_changed = 1;
583 mpath_count++;
584 if (debug)
585 zlog_debug ("%s add mpath nexthop %s peer %s", pfx_buf,
586 inet_ntop (AF_INET, &new_mpath->attr->nexthop,
587 nh_buf[0], sizeof (nh_buf[0])),
588 sockunion2str (new_mpath->peer->su_remote,
589 nh_buf[1], sizeof (nh_buf[1])));
590 }
591 mp_node = mp_next_node;
592 }
593 }
594
595 if (new_best)
596 {
597 bgp_info_mpath_count_set (new_best, mpath_count-1);
598 if (mpath_changed || (bgp_info_mpath_count (new_best) != old_mpath_count))
599 SET_FLAG (new_best->flags, BGP_INFO_MULTIPATH_CHG);
600 }
601}
Josh Bailey6918e742011-07-20 20:48:20 -0700602
603/*
604 * bgp_mp_dmed_deselect
605 *
606 * Clean up multipath information for BGP_INFO_DMED_SELECTED path that
607 * is not selected as best path
608 */
609void
610bgp_mp_dmed_deselect (struct bgp_info *dmed_best)
611{
612 struct bgp_info *mpinfo, *mpnext;
613
614 if (!dmed_best)
615 return;
616
617 for (mpinfo = bgp_info_mpath_first (dmed_best); mpinfo; mpinfo = mpnext)
618 {
619 mpnext = bgp_info_mpath_next (mpinfo);
620 bgp_info_mpath_dequeue (mpinfo);
621 }
622
623 bgp_info_mpath_count_set (dmed_best, 0);
624 UNSET_FLAG (dmed_best->flags, BGP_INFO_MULTIPATH_CHG);
625 assert (bgp_info_mpath_first (dmed_best) == 0);
626}
Josh Bailey0b597ef2011-07-20 20:49:11 -0700627
628/*
629 * bgp_info_mpath_aggregate_update
630 *
631 * Set the multipath aggregate attribute. We need to see if the
632 * aggregate has changed and then set the ATTR_CHANGED flag on the
633 * bestpath info so that a peer update will be generated. The
634 * change is detected by generating the current attribute,
635 * interning it, and then comparing the interned pointer with the
636 * current value. We can skip this generate/compare step if there
637 * is no change in multipath selection and no attribute change in
638 * any multipath.
639 */
640void
641bgp_info_mpath_aggregate_update (struct bgp_info *new_best,
642 struct bgp_info *old_best)
643{
644 struct bgp_info *mpinfo;
645 struct aspath *aspath;
646 struct aspath *asmerge;
647 struct attr *new_attr, *old_attr;
648 u_char origin, attr_chg;
649 struct community *community, *commerge;
650 struct ecommunity *ecomm, *ecommerge;
Job Snijders3334bab2017-01-20 14:47:12 +0000651 struct lcommunity *lcomm, *lcommerge;
Josh Bailey0b597ef2011-07-20 20:49:11 -0700652 struct attr_extra *ae;
653 struct attr attr = { 0 };
654
655 if (old_best && (old_best != new_best) &&
656 (old_attr = bgp_info_mpath_attr (old_best)))
657 {
David Lamparterfac3c242012-04-28 22:37:20 +0200658 bgp_attr_unintern (&old_attr);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700659 bgp_info_mpath_attr_set (old_best, NULL);
660 }
661
662 if (!new_best)
663 return;
664
665 if (!bgp_info_mpath_count (new_best))
666 {
667 if ((new_attr = bgp_info_mpath_attr (new_best)))
668 {
David Lamparterfac3c242012-04-28 22:37:20 +0200669 bgp_attr_unintern (&new_attr);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700670 bgp_info_mpath_attr_set (new_best, NULL);
671 SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED);
672 }
673 return;
674 }
675
676 /*
677 * Bail out here if the following is true:
678 * - MULTIPATH_CHG bit is not set on new_best, and
Josh Bailey01d7ff02011-07-20 20:52:06 -0700679 * - No change in bestpath, and
Josh Bailey0b597ef2011-07-20 20:49:11 -0700680 * - ATTR_CHANGED bit is not set on new_best or any of the multipaths
681 */
Josh Bailey01d7ff02011-07-20 20:52:06 -0700682 if (!CHECK_FLAG (new_best->flags, BGP_INFO_MULTIPATH_CHG) &&
683 (old_best == new_best))
Josh Bailey0b597ef2011-07-20 20:49:11 -0700684 {
Josh Bailey01d7ff02011-07-20 20:52:06 -0700685 attr_chg = 0;
686
687 if (CHECK_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED))
688 attr_chg = 1;
689 else
690 for (mpinfo = bgp_info_mpath_first (new_best); mpinfo;
691 mpinfo = bgp_info_mpath_next (mpinfo))
692 {
693 if (CHECK_FLAG (mpinfo->flags, BGP_INFO_ATTR_CHANGED))
694 {
695 attr_chg = 1;
696 break;
697 }
698 }
699
700 if (!attr_chg)
701 {
702 assert (bgp_info_mpath_attr (new_best));
703 return;
704 }
Josh Bailey0b597ef2011-07-20 20:49:11 -0700705 }
706
707 bgp_attr_dup (&attr, new_best->attr);
708
709 /* aggregate attribute from multipath constituents */
710 aspath = aspath_dup (attr.aspath);
711 origin = attr.origin;
712 community = attr.community ? community_dup (attr.community) : NULL;
713 ae = attr.extra;
714 ecomm = (ae && ae->ecommunity) ? ecommunity_dup (ae->ecommunity) : NULL;
715
Job Snijders3334bab2017-01-20 14:47:12 +0000716 lcomm = (ae && ae->lcommunity) ? lcommunity_dup (ae->lcommunity) : NULL;
717
Josh Bailey0b597ef2011-07-20 20:49:11 -0700718 for (mpinfo = bgp_info_mpath_first (new_best); mpinfo;
719 mpinfo = bgp_info_mpath_next (mpinfo))
720 {
Boian Boneva3936d02014-06-25 20:26:44 +0300721 asmerge = aspath_aggregate_mpath (aspath, mpinfo->attr->aspath);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700722 aspath_free (aspath);
723 aspath = asmerge;
724
725 if (origin < mpinfo->attr->origin)
726 origin = mpinfo->attr->origin;
727
728 if (mpinfo->attr->community)
729 {
730 if (community)
731 {
732 commerge = community_merge (community, mpinfo->attr->community);
733 community = community_uniq_sort (commerge);
734 community_free (commerge);
735 }
736 else
737 community = community_dup (mpinfo->attr->community);
738 }
739
740 ae = mpinfo->attr->extra;
741 if (ae && ae->ecommunity)
742 {
743 if (ecomm)
744 {
745 ecommerge = ecommunity_merge (ecomm, ae->ecommunity);
746 ecomm = ecommunity_uniq_sort (ecommerge);
David Lamparterfac3c242012-04-28 22:37:20 +0200747 ecommunity_free (&ecommerge);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700748 }
749 else
750 ecomm = ecommunity_dup (ae->ecommunity);
751 }
Job Snijders3334bab2017-01-20 14:47:12 +0000752
753 if (ae && ae->lcommunity)
754 {
755 if (lcomm)
756 {
757 lcommerge = lcommunity_merge (lcomm, ae->lcommunity);
758 lcomm = lcommunity_uniq_sort (lcommerge);
759 lcommunity_free (&lcommerge);
760 }
761 else
762 lcomm = lcommunity_dup (ae->lcommunity);
763 }
Josh Bailey0b597ef2011-07-20 20:49:11 -0700764 }
765
766 attr.aspath = aspath;
767 attr.origin = origin;
768 if (community)
769 {
770 attr.community = community;
771 attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
772 }
773 if (ecomm)
774 {
775 ae = bgp_attr_extra_get (&attr);
776 ae->ecommunity = ecomm;
777 attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
778 }
779
780 /* Zap multipath attr nexthop so we set nexthop to self */
781 attr.nexthop.s_addr = 0;
Josh Bailey0b597ef2011-07-20 20:49:11 -0700782 if (attr.extra)
783 memset (&attr.extra->mp_nexthop_global, 0, sizeof (struct in6_addr));
Josh Bailey0b597ef2011-07-20 20:49:11 -0700784
785 /* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */
786
787 new_attr = bgp_attr_intern (&attr);
788 bgp_attr_extra_free (&attr);
789
790 if (new_attr != bgp_info_mpath_attr (new_best))
791 {
792 if ((old_attr = bgp_info_mpath_attr (new_best)))
David Lamparterfac3c242012-04-28 22:37:20 +0200793 bgp_attr_unintern (&old_attr);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700794 bgp_info_mpath_attr_set (new_best, new_attr);
795 SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED);
796 }
797 else
David Lamparterfac3c242012-04-28 22:37:20 +0200798 bgp_attr_unintern (&new_attr);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700799}