LCOV - code coverage report
Current view: top level - bgpd - bgp_mpath.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 141 276 51.1 %
Date: 2015-11-19 Functions: 16 21 76.2 %

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

Generated by: LCOV version 1.10