blob: 6465aad1d99a661da137bc1da6e8c7be50ed2c0a [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"
Josh Bailey165b5ff2011-07-20 20:43:22 -070041#include "bgpd/bgp_mpath.h"
42
Paul Jakma6d4742b2015-11-25 17:14:37 +000043bool
44bgp_mpath_is_configured_sort (struct bgp *bgp, bgp_peer_sort_t sort,
45 afi_t afi, safi_t safi)
46{
47 struct bgp_maxpaths_cfg *cfg = &bgp->maxpaths[afi][safi];
48
49 /* XXX: BGP_DEFAULT_MAXPATHS is 1, and this test only seems to make sense
50 * if if it stays 1, so not sure the DEFAULT define is that useful.
51 */
52 switch (sort)
53 {
54 case BGP_PEER_IBGP:
55 return cfg->maxpaths_ibgp != BGP_DEFAULT_MAXPATHS;
56 case BGP_PEER_EBGP:
57 return cfg->maxpaths_ebgp != BGP_DEFAULT_MAXPATHS;
58 default:
59 return false;
60 }
61}
62
63bool
64bgp_mpath_is_configured (struct bgp *bgp, afi_t afi, safi_t safi)
65{
66 return bgp_mpath_is_configured_sort (bgp, BGP_PEER_IBGP, afi, safi)
67 || bgp_mpath_is_configured_sort (bgp, BGP_PEER_EBGP, afi, safi);
68}
69
Josh Bailey165b5ff2011-07-20 20:43:22 -070070/*
71 * bgp_maximum_paths_set
72 *
73 * Record maximum-paths configuration for BGP instance
74 */
75int
76bgp_maximum_paths_set (struct bgp *bgp, afi_t afi, safi_t safi,
77 int peertype, u_int16_t maxpaths)
78{
79 if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX))
80 return -1;
81
82 switch (peertype)
83 {
84 case BGP_PEER_IBGP:
85 bgp->maxpaths[afi][safi].maxpaths_ibgp = maxpaths;
86 break;
87 case BGP_PEER_EBGP:
88 bgp->maxpaths[afi][safi].maxpaths_ebgp = maxpaths;
89 break;
90 default:
91 return -1;
92 }
93
94 return 0;
95}
96
97/*
98 * bgp_maximum_paths_unset
99 *
100 * Remove maximum-paths configuration from BGP instance
101 */
102int
103bgp_maximum_paths_unset (struct bgp *bgp, afi_t afi, safi_t safi,
104 int peertype)
105{
106 if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX))
107 return -1;
108
109 switch (peertype)
110 {
111 case BGP_PEER_IBGP:
112 bgp->maxpaths[afi][safi].maxpaths_ibgp = BGP_DEFAULT_MAXPATHS;
113 break;
114 case BGP_PEER_EBGP:
115 bgp->maxpaths[afi][safi].maxpaths_ebgp = BGP_DEFAULT_MAXPATHS;
116 break;
117 default:
118 return -1;
119 }
120
121 return 0;
122}
Josh Bailey96450fa2011-07-20 20:45:12 -0700123
124/*
125 * bgp_info_nexthop_cmp
126 *
127 * Compare the nexthops of two paths. Return value is less than, equal to,
128 * or greater than zero if bi1 is respectively less than, equal to,
129 * or greater than bi2.
130 */
131static int
132bgp_info_nexthop_cmp (struct bgp_info *bi1, struct bgp_info *bi2)
133{
134 struct attr_extra *ae1, *ae2;
135 int compare;
136
Josh Bailey0b597ef2011-07-20 20:49:11 -0700137 ae1 = bi1->attr->extra;
138 ae2 = bi2->attr->extra;
Josh Bailey96450fa2011-07-20 20:45:12 -0700139
140 compare = IPV4_ADDR_CMP (&bi1->attr->nexthop, &bi2->attr->nexthop);
141
Donald Sharpd8c5f272016-03-16 08:10:56 -0400142 if (!compare && ae1 && ae2)
Josh Bailey96450fa2011-07-20 20:45:12 -0700143 {
Donald Sharpd8c5f272016-03-16 08:10:56 -0400144 if (ae1->mp_nexthop_len == ae2->mp_nexthop_len)
Josh Bailey96450fa2011-07-20 20:45:12 -0700145 {
Donald Sharpd8c5f272016-03-16 08:10:56 -0400146 switch (ae1->mp_nexthop_len)
147 {
148 case 4:
149 case 12:
150 compare = IPV4_ADDR_CMP (&ae1->mp_nexthop_global_in,
151 &ae2->mp_nexthop_global_in);
152 break;
153 case 16:
154 compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
155 &ae2->mp_nexthop_global);
156 break;
157 case 32:
158 compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
159 &ae2->mp_nexthop_global);
160 if (!compare)
161 compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_local,
162 &ae2->mp_nexthop_local);
163 break;
164 }
165 }
166
167 /* This can happen if one IPv6 peer sends you global and link-local
168 * nexthops but another IPv6 peer only sends you global
169 */
170 else if (ae1->mp_nexthop_len == 16 || ae1->mp_nexthop_len == 32)
171 {
Josh Bailey96450fa2011-07-20 20:45:12 -0700172 compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
173 &ae2->mp_nexthop_global);
174 if (!compare)
Donald Sharpd8c5f272016-03-16 08:10:56 -0400175 {
176 if (ae1->mp_nexthop_len < ae2->mp_nexthop_len)
177 compare = -1;
178 else
179 compare = 1;
180 }
Josh Bailey96450fa2011-07-20 20:45:12 -0700181 }
182 }
183
184 return compare;
185}
186
187/*
188 * bgp_info_mpath_cmp
189 *
190 * This function determines our multipath list ordering. By ordering
191 * the list we can deterministically select which paths are included
192 * in the multipath set. The ordering also helps in detecting changes
193 * in the multipath selection so we can detect whether to send an
194 * update to zebra.
195 *
196 * The order of paths is determined first by received nexthop, and then
197 * by peer address if the nexthops are the same.
198 */
199static int
200bgp_info_mpath_cmp (void *val1, void *val2)
201{
202 struct bgp_info *bi1, *bi2;
203 int compare;
204
205 bi1 = val1;
206 bi2 = val2;
207
208 compare = bgp_info_nexthop_cmp (bi1, bi2);
209
210 if (!compare)
211 compare = sockunion_cmp (bi1->peer->su_remote, bi2->peer->su_remote);
212
213 return compare;
214}
215
216/*
217 * bgp_mp_list_init
218 *
219 * Initialize the mp_list, which holds the list of multipaths
220 * selected by bgp_best_selection
221 */
222void
223bgp_mp_list_init (struct list *mp_list)
224{
225 assert (mp_list);
226 memset (mp_list, 0, sizeof (struct list));
227 mp_list->cmp = bgp_info_mpath_cmp;
228}
229
230/*
231 * bgp_mp_list_clear
232 *
233 * Clears all entries out of the mp_list
234 */
235void
236bgp_mp_list_clear (struct list *mp_list)
237{
238 assert (mp_list);
239 list_delete_all_node (mp_list);
240}
241
242/*
243 * bgp_mp_list_add
244 *
245 * Adds a multipath entry to the mp_list
246 */
247void
248bgp_mp_list_add (struct list *mp_list, struct bgp_info *mpinfo)
249{
250 assert (mp_list && mpinfo);
251 listnode_add_sort (mp_list, mpinfo);
252}
Josh Baileyde8d5df2011-07-20 20:46:01 -0700253
254/*
255 * bgp_info_mpath_new
256 *
257 * Allocate and zero memory for a new bgp_info_mpath element
258 */
259static struct bgp_info_mpath *
260bgp_info_mpath_new (void)
261{
262 struct bgp_info_mpath *new_mpath;
263 new_mpath = XCALLOC (MTYPE_BGP_MPATH_INFO, sizeof (struct bgp_info_mpath));
264 return new_mpath;
265}
266
267/*
268 * bgp_info_mpath_free
269 *
270 * Release resources for a bgp_info_mpath element and zero out pointer
271 */
272void
273bgp_info_mpath_free (struct bgp_info_mpath **mpath)
274{
275 if (mpath && *mpath)
276 {
Josh Bailey0b597ef2011-07-20 20:49:11 -0700277 if ((*mpath)->mp_attr)
David Lamparterfac3c242012-04-28 22:37:20 +0200278 bgp_attr_unintern (&(*mpath)->mp_attr);
Josh Baileyde8d5df2011-07-20 20:46:01 -0700279 XFREE (MTYPE_BGP_MPATH_INFO, *mpath);
280 *mpath = NULL;
281 }
282}
283
284/*
285 * bgp_info_mpath_get
286 *
287 * Fetch the mpath element for the given bgp_info. Used for
288 * doing lazy allocation.
289 */
290static struct bgp_info_mpath *
291bgp_info_mpath_get (struct bgp_info *binfo)
292{
293 struct bgp_info_mpath *mpath;
294 if (!binfo->mpath)
295 {
296 mpath = bgp_info_mpath_new();
297 if (!mpath)
298 return NULL;
299 binfo->mpath = mpath;
300 mpath->mp_info = binfo;
301 }
302 return binfo->mpath;
303}
304
305/*
306 * bgp_info_mpath_enqueue
307 *
308 * Enqueue a path onto the multipath list given the previous multipath
309 * list entry
310 */
311static void
312bgp_info_mpath_enqueue (struct bgp_info *prev_info, struct bgp_info *binfo)
313{
314 struct bgp_info_mpath *prev, *mpath;
315
316 prev = bgp_info_mpath_get (prev_info);
317 mpath = bgp_info_mpath_get (binfo);
318 if (!prev || !mpath)
319 return;
320
321 mpath->mp_next = prev->mp_next;
322 mpath->mp_prev = prev;
323 if (prev->mp_next)
324 prev->mp_next->mp_prev = mpath;
325 prev->mp_next = mpath;
326
327 SET_FLAG (binfo->flags, BGP_INFO_MULTIPATH);
328}
329
330/*
331 * bgp_info_mpath_dequeue
332 *
333 * Remove a path from the multipath list
334 */
335void
336bgp_info_mpath_dequeue (struct bgp_info *binfo)
337{
338 struct bgp_info_mpath *mpath = binfo->mpath;
339 if (!mpath)
340 return;
341 if (mpath->mp_prev)
342 mpath->mp_prev->mp_next = mpath->mp_next;
343 if (mpath->mp_next)
344 mpath->mp_next->mp_prev = mpath->mp_prev;
345 mpath->mp_next = mpath->mp_prev = NULL;
346 UNSET_FLAG (binfo->flags, BGP_INFO_MULTIPATH);
347}
348
349/*
350 * bgp_info_mpath_next
351 *
352 * Given a bgp_info, return the next multipath entry
353 */
354struct bgp_info *
355bgp_info_mpath_next (struct bgp_info *binfo)
356{
357 if (!binfo->mpath || !binfo->mpath->mp_next)
358 return NULL;
359 return binfo->mpath->mp_next->mp_info;
360}
361
362/*
363 * bgp_info_mpath_first
364 *
365 * Given bestpath bgp_info, return the first multipath entry.
366 */
367struct bgp_info *
368bgp_info_mpath_first (struct bgp_info *binfo)
369{
370 return bgp_info_mpath_next (binfo);
371}
372
373/*
374 * bgp_info_mpath_count
375 *
376 * Given the bestpath bgp_info, return the number of multipath entries
377 */
378u_int32_t
379bgp_info_mpath_count (struct bgp_info *binfo)
380{
381 if (!binfo->mpath)
382 return 0;
383 return binfo->mpath->mp_count;
384}
385
386/*
387 * bgp_info_mpath_count_set
388 *
389 * Sets the count of multipaths into bestpath's mpath element
390 */
391static void
392bgp_info_mpath_count_set (struct bgp_info *binfo, u_int32_t count)
393{
394 struct bgp_info_mpath *mpath;
395 if (!count && !binfo->mpath)
396 return;
397 mpath = bgp_info_mpath_get (binfo);
398 if (!mpath)
399 return;
400 mpath->mp_count = count;
401}
402
403/*
Josh Bailey0b597ef2011-07-20 20:49:11 -0700404 * bgp_info_mpath_attr
405 *
406 * Given bestpath bgp_info, return aggregated attribute set used
407 * for advertising the multipath route
408 */
409struct attr *
410bgp_info_mpath_attr (struct bgp_info *binfo)
411{
412 if (!binfo->mpath)
413 return NULL;
414 return binfo->mpath->mp_attr;
415}
416
417/*
418 * bgp_info_mpath_attr_set
419 *
420 * Sets the aggregated attribute into bestpath's mpath element
421 */
422static void
423bgp_info_mpath_attr_set (struct bgp_info *binfo, struct attr *attr)
424{
425 struct bgp_info_mpath *mpath;
426 if (!attr && !binfo->mpath)
427 return;
428 mpath = bgp_info_mpath_get (binfo);
429 if (!mpath)
430 return;
431 mpath->mp_attr = attr;
432}
433
434/*
Josh Baileyde8d5df2011-07-20 20:46:01 -0700435 * bgp_info_mpath_update
436 *
437 * Compare and sync up the multipath list with the mp_list generated by
438 * bgp_best_selection
439 */
440void
441bgp_info_mpath_update (struct bgp_node *rn, struct bgp_info *new_best,
442 struct bgp_info *old_best, struct list *mp_list,
Paul Jakma6d4742b2015-11-25 17:14:37 +0000443 afi_t afi, safi_t safi)
Josh Baileyde8d5df2011-07-20 20:46:01 -0700444{
445 u_int16_t maxpaths, mpath_count, old_mpath_count;
446 struct listnode *mp_node, *mp_next_node;
447 struct bgp_info *cur_mpath, *new_mpath, *next_mpath, *prev_mpath;
448 int mpath_changed, debug;
Donald Sharpd8c5f272016-03-16 08:10:56 -0400449 char pfx_buf[INET6_ADDRSTRLEN], nh_buf[2][INET6_ADDRSTRLEN];
Donald Sharpe98dce62016-01-20 07:53:51 -0500450 struct bgp_maxpaths_cfg *mpath_cfg = NULL;
Josh Baileyde8d5df2011-07-20 20:46:01 -0700451
452 mpath_changed = 0;
453 maxpaths = BGP_DEFAULT_MAXPATHS;
454 mpath_count = 0;
455 cur_mpath = NULL;
456 old_mpath_count = 0;
457 prev_mpath = new_best;
458 mp_node = listhead (mp_list);
Josh Baileyde8d5df2011-07-20 20:46:01 -0700459
Donald Sharpe98dce62016-01-20 07:53:51 -0500460 debug = BGP_DEBUG (events, EVENTS);
Paul Jakma6d4742b2015-11-25 17:14:37 +0000461
Josh Baileyde8d5df2011-07-20 20:46:01 -0700462 if (debug)
463 prefix2str (&rn->p, pfx_buf, sizeof (pfx_buf));
464
465 if (new_best)
466 {
Donald Sharpe98dce62016-01-20 07:53:51 -0500467 mpath_cfg = &new_best->peer->bgp->maxpaths[afi][safi];
Josh Baileyde8d5df2011-07-20 20:46:01 -0700468 mpath_count++;
469 if (new_best != old_best)
470 bgp_info_mpath_dequeue (new_best);
Jorge Boncompte [DTI2]6d85b152012-05-07 16:52:54 +0000471 maxpaths = (new_best->peer->sort == BGP_PEER_IBGP) ?
Josh Baileyde8d5df2011-07-20 20:46:01 -0700472 mpath_cfg->maxpaths_ibgp : mpath_cfg->maxpaths_ebgp;
473 }
474
475 if (old_best)
476 {
477 cur_mpath = bgp_info_mpath_first (old_best);
478 old_mpath_count = bgp_info_mpath_count (old_best);
479 bgp_info_mpath_count_set (old_best, 0);
480 bgp_info_mpath_dequeue (old_best);
481 }
482
483 /*
484 * We perform an ordered walk through both lists in parallel.
485 * The reason for the ordered walk is that if there are paths
486 * that were previously multipaths and are still multipaths, the walk
487 * should encounter them in both lists at the same time. Otherwise
488 * there will be paths that are in one list or another, and we
489 * will deal with these separately.
490 *
491 * Note that new_best might be somewhere in the mp_list, so we need
492 * to skip over it
493 */
494 while (mp_node || cur_mpath)
495 {
496 /*
497 * We can bail out of this loop if all existing paths on the
498 * multipath list have been visited (for cleanup purposes) and
499 * the maxpath requirement is fulfulled
500 */
501 if (!cur_mpath && (mpath_count >= maxpaths))
502 break;
503
504 mp_next_node = mp_node ? listnextnode (mp_node) : NULL;
505 next_mpath = cur_mpath ? bgp_info_mpath_next (cur_mpath) : NULL;
506
507 /*
508 * If equal, the path was a multipath and is still a multipath.
509 * Insert onto new multipath list if maxpaths allows.
510 */
511 if (mp_node && (listgetdata (mp_node) == cur_mpath))
512 {
513 list_delete_node (mp_list, mp_node);
514 bgp_info_mpath_dequeue (cur_mpath);
515 if ((mpath_count < maxpaths) &&
516 bgp_info_nexthop_cmp (prev_mpath, cur_mpath))
517 {
518 bgp_info_mpath_enqueue (prev_mpath, cur_mpath);
519 prev_mpath = cur_mpath;
520 mpath_count++;
521 }
522 else
523 {
524 mpath_changed = 1;
525 if (debug)
526 zlog_debug ("%s remove mpath nexthop %s peer %s", pfx_buf,
527 inet_ntop (AF_INET, &cur_mpath->attr->nexthop,
528 nh_buf[0], sizeof (nh_buf[0])),
529 sockunion2str (cur_mpath->peer->su_remote,
530 nh_buf[1], sizeof (nh_buf[1])));
531 }
532 mp_node = mp_next_node;
533 cur_mpath = next_mpath;
534 continue;
535 }
536
537 if (cur_mpath && (!mp_node ||
538 (bgp_info_mpath_cmp (cur_mpath,
539 listgetdata (mp_node)) < 0)))
540 {
541 /*
542 * If here, we have an old multipath and either the mp_list
543 * is finished or the next mp_node points to a later
544 * multipath, so we need to purge this path from the
545 * multipath list
546 */
547 bgp_info_mpath_dequeue (cur_mpath);
548 mpath_changed = 1;
549 if (debug)
550 zlog_debug ("%s remove mpath nexthop %s peer %s", pfx_buf,
551 inet_ntop (AF_INET, &cur_mpath->attr->nexthop,
552 nh_buf[0], sizeof (nh_buf[0])),
553 sockunion2str (cur_mpath->peer->su_remote,
554 nh_buf[1], sizeof (nh_buf[1])));
555 cur_mpath = next_mpath;
556 }
557 else
558 {
559 /*
560 * If here, we have a path on the mp_list that was not previously
561 * a multipath (due to non-equivalance or maxpaths exceeded),
562 * or the matching multipath is sorted later in the multipath
563 * list. Before we enqueue the path on the new multipath list,
564 * make sure its not on the old_best multipath list or referenced
565 * via next_mpath:
566 * - If next_mpath points to this new path, update next_mpath to
567 * point to the multipath after this one
568 * - Dequeue the path from the multipath list just to make sure
569 */
570 new_mpath = listgetdata (mp_node);
571 list_delete_node (mp_list, mp_node);
Josh Baileyde8d5df2011-07-20 20:46:01 -0700572 if ((mpath_count < maxpaths) && (new_mpath != new_best) &&
573 bgp_info_nexthop_cmp (prev_mpath, new_mpath))
574 {
Josh Bailey78d92e12011-07-20 20:51:07 -0700575 if (new_mpath == next_mpath)
576 next_mpath = bgp_info_mpath_next (new_mpath);
577 bgp_info_mpath_dequeue (new_mpath);
578
Josh Baileyde8d5df2011-07-20 20:46:01 -0700579 bgp_info_mpath_enqueue (prev_mpath, new_mpath);
580 prev_mpath = new_mpath;
581 mpath_changed = 1;
582 mpath_count++;
583 if (debug)
584 zlog_debug ("%s add mpath nexthop %s peer %s", pfx_buf,
585 inet_ntop (AF_INET, &new_mpath->attr->nexthop,
586 nh_buf[0], sizeof (nh_buf[0])),
587 sockunion2str (new_mpath->peer->su_remote,
588 nh_buf[1], sizeof (nh_buf[1])));
589 }
590 mp_node = mp_next_node;
591 }
592 }
593
594 if (new_best)
595 {
596 bgp_info_mpath_count_set (new_best, mpath_count-1);
597 if (mpath_changed || (bgp_info_mpath_count (new_best) != old_mpath_count))
598 SET_FLAG (new_best->flags, BGP_INFO_MULTIPATH_CHG);
599 }
600}
Josh Bailey6918e742011-07-20 20:48:20 -0700601
602/*
603 * bgp_mp_dmed_deselect
604 *
605 * Clean up multipath information for BGP_INFO_DMED_SELECTED path that
606 * is not selected as best path
607 */
608void
609bgp_mp_dmed_deselect (struct bgp_info *dmed_best)
610{
611 struct bgp_info *mpinfo, *mpnext;
612
613 if (!dmed_best)
614 return;
615
616 for (mpinfo = bgp_info_mpath_first (dmed_best); mpinfo; mpinfo = mpnext)
617 {
618 mpnext = bgp_info_mpath_next (mpinfo);
619 bgp_info_mpath_dequeue (mpinfo);
620 }
621
622 bgp_info_mpath_count_set (dmed_best, 0);
623 UNSET_FLAG (dmed_best->flags, BGP_INFO_MULTIPATH_CHG);
624 assert (bgp_info_mpath_first (dmed_best) == 0);
625}
Josh Bailey0b597ef2011-07-20 20:49:11 -0700626
627/*
628 * bgp_info_mpath_aggregate_update
629 *
630 * Set the multipath aggregate attribute. We need to see if the
631 * aggregate has changed and then set the ATTR_CHANGED flag on the
632 * bestpath info so that a peer update will be generated. The
633 * change is detected by generating the current attribute,
634 * interning it, and then comparing the interned pointer with the
635 * current value. We can skip this generate/compare step if there
636 * is no change in multipath selection and no attribute change in
637 * any multipath.
638 */
639void
640bgp_info_mpath_aggregate_update (struct bgp_info *new_best,
641 struct bgp_info *old_best)
642{
643 struct bgp_info *mpinfo;
644 struct aspath *aspath;
645 struct aspath *asmerge;
646 struct attr *new_attr, *old_attr;
647 u_char origin, attr_chg;
648 struct community *community, *commerge;
649 struct ecommunity *ecomm, *ecommerge;
650 struct attr_extra *ae;
651 struct attr attr = { 0 };
652
653 if (old_best && (old_best != new_best) &&
654 (old_attr = bgp_info_mpath_attr (old_best)))
655 {
David Lamparterfac3c242012-04-28 22:37:20 +0200656 bgp_attr_unintern (&old_attr);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700657 bgp_info_mpath_attr_set (old_best, NULL);
658 }
659
660 if (!new_best)
661 return;
662
663 if (!bgp_info_mpath_count (new_best))
664 {
665 if ((new_attr = bgp_info_mpath_attr (new_best)))
666 {
David Lamparterfac3c242012-04-28 22:37:20 +0200667 bgp_attr_unintern (&new_attr);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700668 bgp_info_mpath_attr_set (new_best, NULL);
669 SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED);
670 }
671 return;
672 }
673
674 /*
675 * Bail out here if the following is true:
676 * - MULTIPATH_CHG bit is not set on new_best, and
Josh Bailey01d7ff02011-07-20 20:52:06 -0700677 * - No change in bestpath, and
Josh Bailey0b597ef2011-07-20 20:49:11 -0700678 * - ATTR_CHANGED bit is not set on new_best or any of the multipaths
679 */
Josh Bailey01d7ff02011-07-20 20:52:06 -0700680 if (!CHECK_FLAG (new_best->flags, BGP_INFO_MULTIPATH_CHG) &&
681 (old_best == new_best))
Josh Bailey0b597ef2011-07-20 20:49:11 -0700682 {
Josh Bailey01d7ff02011-07-20 20:52:06 -0700683 attr_chg = 0;
684
685 if (CHECK_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED))
686 attr_chg = 1;
687 else
688 for (mpinfo = bgp_info_mpath_first (new_best); mpinfo;
689 mpinfo = bgp_info_mpath_next (mpinfo))
690 {
691 if (CHECK_FLAG (mpinfo->flags, BGP_INFO_ATTR_CHANGED))
692 {
693 attr_chg = 1;
694 break;
695 }
696 }
697
698 if (!attr_chg)
699 {
700 assert (bgp_info_mpath_attr (new_best));
701 return;
702 }
Josh Bailey0b597ef2011-07-20 20:49:11 -0700703 }
704
705 bgp_attr_dup (&attr, new_best->attr);
706
707 /* aggregate attribute from multipath constituents */
708 aspath = aspath_dup (attr.aspath);
709 origin = attr.origin;
710 community = attr.community ? community_dup (attr.community) : NULL;
711 ae = attr.extra;
712 ecomm = (ae && ae->ecommunity) ? ecommunity_dup (ae->ecommunity) : NULL;
713
714 for (mpinfo = bgp_info_mpath_first (new_best); mpinfo;
715 mpinfo = bgp_info_mpath_next (mpinfo))
716 {
Boian Boneva3936d02014-06-25 20:26:44 +0300717 asmerge = aspath_aggregate_mpath (aspath, mpinfo->attr->aspath);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700718 aspath_free (aspath);
719 aspath = asmerge;
720
721 if (origin < mpinfo->attr->origin)
722 origin = mpinfo->attr->origin;
723
724 if (mpinfo->attr->community)
725 {
726 if (community)
727 {
728 commerge = community_merge (community, mpinfo->attr->community);
729 community = community_uniq_sort (commerge);
730 community_free (commerge);
731 }
732 else
733 community = community_dup (mpinfo->attr->community);
734 }
735
736 ae = mpinfo->attr->extra;
737 if (ae && ae->ecommunity)
738 {
739 if (ecomm)
740 {
741 ecommerge = ecommunity_merge (ecomm, ae->ecommunity);
742 ecomm = ecommunity_uniq_sort (ecommerge);
David Lamparterfac3c242012-04-28 22:37:20 +0200743 ecommunity_free (&ecommerge);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700744 }
745 else
746 ecomm = ecommunity_dup (ae->ecommunity);
747 }
748 }
749
750 attr.aspath = aspath;
751 attr.origin = origin;
752 if (community)
753 {
754 attr.community = community;
755 attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
756 }
757 if (ecomm)
758 {
759 ae = bgp_attr_extra_get (&attr);
760 ae->ecommunity = ecomm;
761 attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
762 }
763
764 /* Zap multipath attr nexthop so we set nexthop to self */
765 attr.nexthop.s_addr = 0;
Josh Bailey0b597ef2011-07-20 20:49:11 -0700766 if (attr.extra)
767 memset (&attr.extra->mp_nexthop_global, 0, sizeof (struct in6_addr));
Josh Bailey0b597ef2011-07-20 20:49:11 -0700768
769 /* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */
770
771 new_attr = bgp_attr_intern (&attr);
772 bgp_attr_extra_free (&attr);
773
774 if (new_attr != bgp_info_mpath_attr (new_best))
775 {
776 if ((old_attr = bgp_info_mpath_attr (new_best)))
David Lamparterfac3c242012-04-28 22:37:20 +0200777 bgp_attr_unintern (&old_attr);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700778 bgp_info_mpath_attr_set (new_best, new_attr);
779 SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED);
780 }
781 else
David Lamparterfac3c242012-04-28 22:37:20 +0200782 bgp_attr_unintern (&new_attr);
Josh Bailey0b597ef2011-07-20 20:49:11 -0700783}