diff options
Diffstat (limited to 'bgpd/bgp_mpath.c')
| -rw-r--r-- | bgpd/bgp_mpath.c | 1066 |
1 files changed, 528 insertions, 538 deletions
diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index 7ea5c9e773..6dc6dc6769 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -45,27 +45,25 @@ * * Record maximum-paths configuration for BGP instance */ -int -bgp_maximum_paths_set (struct bgp *bgp, afi_t afi, safi_t safi, - int peertype, u_int16_t maxpaths, u_int16_t options) +int bgp_maximum_paths_set(struct bgp *bgp, afi_t afi, safi_t safi, int peertype, + u_int16_t maxpaths, u_int16_t options) { - if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX)) - return -1; - - switch (peertype) - { - case BGP_PEER_IBGP: - bgp->maxpaths[afi][safi].maxpaths_ibgp = maxpaths; - bgp->maxpaths[afi][safi].ibgp_flags |= options; - break; - case BGP_PEER_EBGP: - bgp->maxpaths[afi][safi].maxpaths_ebgp = maxpaths; - break; - default: - return -1; - } - - return 0; + if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX)) + return -1; + + switch (peertype) { + case BGP_PEER_IBGP: + bgp->maxpaths[afi][safi].maxpaths_ibgp = maxpaths; + bgp->maxpaths[afi][safi].ibgp_flags |= options; + break; + case BGP_PEER_EBGP: + bgp->maxpaths[afi][safi].maxpaths_ebgp = maxpaths; + break; + default: + return -1; + } + + return 0; } /* @@ -73,27 +71,25 @@ bgp_maximum_paths_set (struct bgp *bgp, afi_t afi, safi_t safi, * * Remove maximum-paths configuration from BGP instance */ -int -bgp_maximum_paths_unset (struct bgp *bgp, afi_t afi, safi_t safi, - int peertype) +int bgp_maximum_paths_unset(struct bgp *bgp, afi_t afi, safi_t safi, + int peertype) { - if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX)) - return -1; - - switch (peertype) - { - case BGP_PEER_IBGP: - bgp->maxpaths[afi][safi].maxpaths_ibgp = multipath_num; - bgp->maxpaths[afi][safi].ibgp_flags = 0; - break; - case BGP_PEER_EBGP: - bgp->maxpaths[afi][safi].maxpaths_ebgp = multipath_num; - break; - default: - return -1; - } - - return 0; + if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX)) + return -1; + + switch (peertype) { + case BGP_PEER_IBGP: + bgp->maxpaths[afi][safi].maxpaths_ibgp = multipath_num; + bgp->maxpaths[afi][safi].ibgp_flags = 0; + break; + case BGP_PEER_EBGP: + bgp->maxpaths[afi][safi].maxpaths_ebgp = multipath_num; + break; + default: + return -1; + } + + return 0; } /* @@ -103,57 +99,58 @@ bgp_maximum_paths_unset (struct bgp *bgp, afi_t afi, safi_t safi, * or greater than zero if bi1 is respectively less than, equal to, * or greater than bi2. */ -int -bgp_info_nexthop_cmp (struct bgp_info *bi1, struct bgp_info *bi2) +int bgp_info_nexthop_cmp(struct bgp_info *bi1, struct bgp_info *bi2) { - int compare; - - compare = IPV4_ADDR_CMP (&bi1->attr->nexthop, &bi2->attr->nexthop); - if (!compare) - { - if (bi1->attr->mp_nexthop_len == bi2->attr->mp_nexthop_len) - { - switch (bi1->attr->mp_nexthop_len) - { - case BGP_ATTR_NHLEN_IPV4: - case BGP_ATTR_NHLEN_VPNV4: - compare = IPV4_ADDR_CMP (&bi1->attr->mp_nexthop_global_in, - &bi2->attr->mp_nexthop_global_in); - break; - case BGP_ATTR_NHLEN_IPV6_GLOBAL: - case BGP_ATTR_NHLEN_VPNV6_GLOBAL: - compare = IPV6_ADDR_CMP (&bi1->attr->mp_nexthop_global, - &bi2->attr->mp_nexthop_global); - break; - case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: - compare = IPV6_ADDR_CMP (&bi1->attr->mp_nexthop_global, - &bi2->attr->mp_nexthop_global); - if (!compare) - compare = IPV6_ADDR_CMP (&bi1->attr->mp_nexthop_local, - &bi2->attr->mp_nexthop_local); - break; - } - } - - /* This can happen if one IPv6 peer sends you global and link-local - * nexthops but another IPv6 peer only sends you global - */ - else if (bi1->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL || - bi1->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) - { - compare = IPV6_ADDR_CMP (&bi1->attr->mp_nexthop_global, - &bi2->attr->mp_nexthop_global); - if (!compare) - { - if (bi1->attr->mp_nexthop_len < bi2->attr->mp_nexthop_len) - compare = -1; - else - compare = 1; - } - } - } - - return compare; + int compare; + + compare = IPV4_ADDR_CMP(&bi1->attr->nexthop, &bi2->attr->nexthop); + if (!compare) { + if (bi1->attr->mp_nexthop_len == bi2->attr->mp_nexthop_len) { + switch (bi1->attr->mp_nexthop_len) { + case BGP_ATTR_NHLEN_IPV4: + case BGP_ATTR_NHLEN_VPNV4: + compare = IPV4_ADDR_CMP( + &bi1->attr->mp_nexthop_global_in, + &bi2->attr->mp_nexthop_global_in); + break; + case BGP_ATTR_NHLEN_IPV6_GLOBAL: + case BGP_ATTR_NHLEN_VPNV6_GLOBAL: + compare = IPV6_ADDR_CMP( + &bi1->attr->mp_nexthop_global, + &bi2->attr->mp_nexthop_global); + break; + case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: + compare = IPV6_ADDR_CMP( + &bi1->attr->mp_nexthop_global, + &bi2->attr->mp_nexthop_global); + if (!compare) + compare = IPV6_ADDR_CMP( + &bi1->attr->mp_nexthop_local, + &bi2->attr->mp_nexthop_local); + break; + } + } + + /* This can happen if one IPv6 peer sends you global and + * link-local + * nexthops but another IPv6 peer only sends you global + */ + else if (bi1->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL + || bi1->attr->mp_nexthop_len + == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) { + compare = IPV6_ADDR_CMP(&bi1->attr->mp_nexthop_global, + &bi2->attr->mp_nexthop_global); + if (!compare) { + if (bi1->attr->mp_nexthop_len + < bi2->attr->mp_nexthop_len) + compare = -1; + else + compare = 1; + } + } + } + + return compare; } /* @@ -168,30 +165,29 @@ bgp_info_nexthop_cmp (struct bgp_info *bi1, struct bgp_info *bi2) * The order of paths is determined first by received nexthop, and then * by peer address if the nexthops are the same. */ -static int -bgp_info_mpath_cmp (void *val1, void *val2) +static int bgp_info_mpath_cmp(void *val1, void *val2) { - struct bgp_info *bi1, *bi2; - int compare; - - bi1 = val1; - bi2 = val2; - - compare = bgp_info_nexthop_cmp (bi1, bi2); - - if (!compare) - { - if (!bi1->peer->su_remote && !bi2->peer->su_remote) - compare = 0; - else if (!bi1->peer->su_remote) - compare = 1; - else if (!bi2->peer->su_remote) - compare = -1; - else - compare = sockunion_cmp (bi1->peer->su_remote, bi2->peer->su_remote); - } - - return compare; + struct bgp_info *bi1, *bi2; + int compare; + + bi1 = val1; + bi2 = val2; + + compare = bgp_info_nexthop_cmp(bi1, bi2); + + if (!compare) { + if (!bi1->peer->su_remote && !bi2->peer->su_remote) + compare = 0; + else if (!bi1->peer->su_remote) + compare = 1; + else if (!bi2->peer->su_remote) + compare = -1; + else + compare = sockunion_cmp(bi1->peer->su_remote, + bi2->peer->su_remote); + } + + return compare; } /* @@ -200,12 +196,11 @@ bgp_info_mpath_cmp (void *val1, void *val2) * Initialize the mp_list, which holds the list of multipaths * selected by bgp_best_selection */ -void -bgp_mp_list_init (struct list *mp_list) +void bgp_mp_list_init(struct list *mp_list) { - assert (mp_list); - memset (mp_list, 0, sizeof (struct list)); - mp_list->cmp = bgp_info_mpath_cmp; + assert(mp_list); + memset(mp_list, 0, sizeof(struct list)); + mp_list->cmp = bgp_info_mpath_cmp; } /* @@ -213,11 +208,10 @@ bgp_mp_list_init (struct list *mp_list) * * Clears all entries out of the mp_list */ -void -bgp_mp_list_clear (struct list *mp_list) +void bgp_mp_list_clear(struct list *mp_list) { - assert (mp_list); - list_delete_all_node (mp_list); + assert(mp_list); + list_delete_all_node(mp_list); } /* @@ -225,11 +219,10 @@ bgp_mp_list_clear (struct list *mp_list) * * Adds a multipath entry to the mp_list */ -void -bgp_mp_list_add (struct list *mp_list, struct bgp_info *mpinfo) +void bgp_mp_list_add(struct list *mp_list, struct bgp_info *mpinfo) { - assert (mp_list && mpinfo); - listnode_add_sort (mp_list, mpinfo); + assert(mp_list && mpinfo); + listnode_add_sort(mp_list, mpinfo); } /* @@ -237,12 +230,12 @@ bgp_mp_list_add (struct list *mp_list, struct bgp_info *mpinfo) * * Allocate and zero memory for a new bgp_info_mpath element */ -static struct bgp_info_mpath * -bgp_info_mpath_new (void) +static struct bgp_info_mpath *bgp_info_mpath_new(void) { - struct bgp_info_mpath *new_mpath; - new_mpath = XCALLOC (MTYPE_BGP_MPATH_INFO, sizeof (struct bgp_info_mpath)); - return new_mpath; + struct bgp_info_mpath *new_mpath; + new_mpath = + XCALLOC(MTYPE_BGP_MPATH_INFO, sizeof(struct bgp_info_mpath)); + return new_mpath; } /* @@ -250,16 +243,14 @@ bgp_info_mpath_new (void) * * Release resources for a bgp_info_mpath element and zero out pointer */ -void -bgp_info_mpath_free (struct bgp_info_mpath **mpath) +void bgp_info_mpath_free(struct bgp_info_mpath **mpath) { - if (mpath && *mpath) - { - if ((*mpath)->mp_attr) - bgp_attr_unintern (&(*mpath)->mp_attr); - XFREE (MTYPE_BGP_MPATH_INFO, *mpath); - *mpath = NULL; - } + if (mpath && *mpath) { + if ((*mpath)->mp_attr) + bgp_attr_unintern(&(*mpath)->mp_attr); + XFREE(MTYPE_BGP_MPATH_INFO, *mpath); + *mpath = NULL; + } } /* @@ -268,19 +259,17 @@ bgp_info_mpath_free (struct bgp_info_mpath **mpath) * Fetch the mpath element for the given bgp_info. Used for * doing lazy allocation. */ -static struct bgp_info_mpath * -bgp_info_mpath_get (struct bgp_info *binfo) +static struct bgp_info_mpath *bgp_info_mpath_get(struct bgp_info *binfo) { - struct bgp_info_mpath *mpath; - if (!binfo->mpath) - { - mpath = bgp_info_mpath_new(); - if (!mpath) - return NULL; - binfo->mpath = mpath; - mpath->mp_info = binfo; - } - return binfo->mpath; + struct bgp_info_mpath *mpath; + if (!binfo->mpath) { + mpath = bgp_info_mpath_new(); + if (!mpath) + return NULL; + binfo->mpath = mpath; + mpath->mp_info = binfo; + } + return binfo->mpath; } /* @@ -289,23 +278,23 @@ bgp_info_mpath_get (struct bgp_info *binfo) * Enqueue a path onto the multipath list given the previous multipath * list entry */ -static void -bgp_info_mpath_enqueue (struct bgp_info *prev_info, struct bgp_info *binfo) +static void bgp_info_mpath_enqueue(struct bgp_info *prev_info, + struct bgp_info *binfo) { - struct bgp_info_mpath *prev, *mpath; + struct bgp_info_mpath *prev, *mpath; - prev = bgp_info_mpath_get (prev_info); - mpath = bgp_info_mpath_get (binfo); - if (!prev || !mpath) - return; + prev = bgp_info_mpath_get(prev_info); + mpath = bgp_info_mpath_get(binfo); + if (!prev || !mpath) + return; - mpath->mp_next = prev->mp_next; - mpath->mp_prev = prev; - if (prev->mp_next) - prev->mp_next->mp_prev = mpath; - prev->mp_next = mpath; + mpath->mp_next = prev->mp_next; + mpath->mp_prev = prev; + if (prev->mp_next) + prev->mp_next->mp_prev = mpath; + prev->mp_next = mpath; - SET_FLAG (binfo->flags, BGP_INFO_MULTIPATH); + SET_FLAG(binfo->flags, BGP_INFO_MULTIPATH); } /* @@ -313,18 +302,17 @@ bgp_info_mpath_enqueue (struct bgp_info *prev_info, struct bgp_info *binfo) * * Remove a path from the multipath list */ -void -bgp_info_mpath_dequeue (struct bgp_info *binfo) +void bgp_info_mpath_dequeue(struct bgp_info *binfo) { - struct bgp_info_mpath *mpath = binfo->mpath; - if (!mpath) - return; - if (mpath->mp_prev) - mpath->mp_prev->mp_next = mpath->mp_next; - if (mpath->mp_next) - mpath->mp_next->mp_prev = mpath->mp_prev; - mpath->mp_next = mpath->mp_prev = NULL; - UNSET_FLAG (binfo->flags, BGP_INFO_MULTIPATH); + struct bgp_info_mpath *mpath = binfo->mpath; + if (!mpath) + return; + if (mpath->mp_prev) + mpath->mp_prev->mp_next = mpath->mp_next; + if (mpath->mp_next) + mpath->mp_next->mp_prev = mpath->mp_prev; + mpath->mp_next = mpath->mp_prev = NULL; + UNSET_FLAG(binfo->flags, BGP_INFO_MULTIPATH); } /* @@ -332,12 +320,11 @@ bgp_info_mpath_dequeue (struct bgp_info *binfo) * * Given a bgp_info, return the next multipath entry */ -struct bgp_info * -bgp_info_mpath_next (struct bgp_info *binfo) +struct bgp_info *bgp_info_mpath_next(struct bgp_info *binfo) { - if (!binfo->mpath || !binfo->mpath->mp_next) - return NULL; - return binfo->mpath->mp_next->mp_info; + if (!binfo->mpath || !binfo->mpath->mp_next) + return NULL; + return binfo->mpath->mp_next->mp_info; } /* @@ -345,10 +332,9 @@ bgp_info_mpath_next (struct bgp_info *binfo) * * Given bestpath bgp_info, return the first multipath entry. */ -struct bgp_info * -bgp_info_mpath_first (struct bgp_info *binfo) +struct bgp_info *bgp_info_mpath_first(struct bgp_info *binfo) { - return bgp_info_mpath_next (binfo); + return bgp_info_mpath_next(binfo); } /* @@ -356,12 +342,11 @@ bgp_info_mpath_first (struct bgp_info *binfo) * * Given the bestpath bgp_info, return the number of multipath entries */ -u_int32_t -bgp_info_mpath_count (struct bgp_info *binfo) +u_int32_t bgp_info_mpath_count(struct bgp_info *binfo) { - if (!binfo->mpath) - return 0; - return binfo->mpath->mp_count; + if (!binfo->mpath) + return 0; + return binfo->mpath->mp_count; } /* @@ -369,16 +354,15 @@ bgp_info_mpath_count (struct bgp_info *binfo) * * Sets the count of multipaths into bestpath's mpath element */ -static void -bgp_info_mpath_count_set (struct bgp_info *binfo, u_int32_t count) +static void bgp_info_mpath_count_set(struct bgp_info *binfo, u_int32_t count) { - struct bgp_info_mpath *mpath; - if (!count && !binfo->mpath) - return; - mpath = bgp_info_mpath_get (binfo); - if (!mpath) - return; - mpath->mp_count = count; + struct bgp_info_mpath *mpath; + if (!count && !binfo->mpath) + return; + mpath = bgp_info_mpath_get(binfo); + if (!mpath) + return; + mpath->mp_count = count; } /* @@ -387,12 +371,11 @@ bgp_info_mpath_count_set (struct bgp_info *binfo, u_int32_t count) * Given bestpath bgp_info, return aggregated attribute set used * for advertising the multipath route */ -struct attr * -bgp_info_mpath_attr (struct bgp_info *binfo) +struct attr *bgp_info_mpath_attr(struct bgp_info *binfo) { - if (!binfo->mpath) - return NULL; - return binfo->mpath->mp_attr; + if (!binfo->mpath) + return NULL; + return binfo->mpath->mp_attr; } /* @@ -400,16 +383,15 @@ bgp_info_mpath_attr (struct bgp_info *binfo) * * Sets the aggregated attribute into bestpath's mpath element */ -static void -bgp_info_mpath_attr_set (struct bgp_info *binfo, struct attr *attr) +static void bgp_info_mpath_attr_set(struct bgp_info *binfo, struct attr *attr) { - struct bgp_info_mpath *mpath; - if (!attr && !binfo->mpath) - return; - mpath = bgp_info_mpath_get (binfo); - if (!mpath) - return; - mpath->mp_attr = attr; + struct bgp_info_mpath *mpath; + if (!attr && !binfo->mpath) + return; + mpath = bgp_info_mpath_get(binfo); + if (!mpath) + return; + mpath->mp_attr = attr; } /* @@ -418,196 +400,210 @@ bgp_info_mpath_attr_set (struct bgp_info *binfo, struct attr *attr) * Compare and sync up the multipath list with the mp_list generated by * bgp_best_selection */ -void -bgp_info_mpath_update (struct bgp_node *rn, struct bgp_info *new_best, - struct bgp_info *old_best, struct list *mp_list, - struct bgp_maxpaths_cfg *mpath_cfg) +void bgp_info_mpath_update(struct bgp_node *rn, struct bgp_info *new_best, + struct bgp_info *old_best, struct list *mp_list, + struct bgp_maxpaths_cfg *mpath_cfg) { - u_int16_t maxpaths, mpath_count, old_mpath_count; - struct listnode *mp_node, *mp_next_node; - struct bgp_info *cur_mpath, *new_mpath, *next_mpath, *prev_mpath; - int mpath_changed, debug; - char pfx_buf[PREFIX2STR_BUFFER], nh_buf[2][INET6_ADDRSTRLEN]; - char path_buf[PATH_ADDPATH_STR_BUFFER]; - - mpath_changed = 0; - maxpaths = multipath_num; - mpath_count = 0; - cur_mpath = NULL; - old_mpath_count = 0; - prev_mpath = new_best; - mp_node = listhead (mp_list); - debug = bgp_debug_bestpath(&rn->p); - - if (debug) - prefix2str (&rn->p, pfx_buf, sizeof (pfx_buf)); - - if (new_best) - { - mpath_count++; - if (new_best != old_best) - bgp_info_mpath_dequeue (new_best); - maxpaths = (new_best->peer->sort == BGP_PEER_IBGP) ? - mpath_cfg->maxpaths_ibgp : mpath_cfg->maxpaths_ebgp; - } - - if (old_best) - { - cur_mpath = bgp_info_mpath_first (old_best); - old_mpath_count = bgp_info_mpath_count (old_best); - bgp_info_mpath_count_set (old_best, 0); - bgp_info_mpath_dequeue (old_best); - } - - if (debug) - zlog_debug("%s: starting mpath update, newbest %s num candidates %d old-mpath-count %d", - pfx_buf, new_best ? new_best->peer->host : "NONE", - listcount (mp_list), old_mpath_count); - - /* - * We perform an ordered walk through both lists in parallel. - * The reason for the ordered walk is that if there are paths - * that were previously multipaths and are still multipaths, the walk - * should encounter them in both lists at the same time. Otherwise - * there will be paths that are in one list or another, and we - * will deal with these separately. - * - * Note that new_best might be somewhere in the mp_list, so we need - * to skip over it - */ - while (mp_node || cur_mpath) - { - struct bgp_info *tmp_info; - - /* - * We can bail out of this loop if all existing paths on the - * multipath list have been visited (for cleanup purposes) and - * the maxpath requirement is fulfulled - */ - if (!cur_mpath && (mpath_count >= maxpaths)) - break; - - mp_next_node = mp_node ? listnextnode (mp_node) : NULL; - next_mpath = cur_mpath ? bgp_info_mpath_next (cur_mpath) : NULL; - tmp_info = mp_node ? listgetdata (mp_node) : NULL; - - if (debug) - zlog_debug("%s: comparing candidate %s with existing mpath %s", - pfx_buf, tmp_info ? tmp_info->peer->host : "NONE", - cur_mpath ? cur_mpath->peer->host : "NONE"); - - /* - * If equal, the path was a multipath and is still a multipath. - * Insert onto new multipath list if maxpaths allows. - */ - if (mp_node && (listgetdata (mp_node) == cur_mpath)) - { - list_delete_node (mp_list, mp_node); - bgp_info_mpath_dequeue (cur_mpath); - if ((mpath_count < maxpaths) && - bgp_info_nexthop_cmp (prev_mpath, cur_mpath)) - { - bgp_info_mpath_enqueue (prev_mpath, cur_mpath); - prev_mpath = cur_mpath; - mpath_count++; - if (debug) - { - bgp_info_path_with_addpath_rx_str(cur_mpath, path_buf); - zlog_debug("%s: %s is still multipath, cur count %d", - pfx_buf, path_buf, mpath_count); - } - } - else - { - mpath_changed = 1; - if (debug) - { - bgp_info_path_with_addpath_rx_str(cur_mpath, path_buf); - zlog_debug ("%s: remove mpath %s nexthop %s, cur count %d", - pfx_buf, path_buf, - inet_ntop (AF_INET, &cur_mpath->attr->nexthop, - nh_buf[0], sizeof (nh_buf[0])), - mpath_count); - } - } - mp_node = mp_next_node; - cur_mpath = next_mpath; - continue; - } - - if (cur_mpath && (!mp_node || - (bgp_info_mpath_cmp (cur_mpath, - listgetdata (mp_node)) < 0))) - { - /* - * If here, we have an old multipath and either the mp_list - * is finished or the next mp_node points to a later - * multipath, so we need to purge this path from the - * multipath list - */ - bgp_info_mpath_dequeue (cur_mpath); - mpath_changed = 1; - if (debug) - { - bgp_info_path_with_addpath_rx_str(cur_mpath, path_buf); - zlog_debug ("%s: remove mpath %s nexthop %s, cur count %d", - pfx_buf, path_buf, - inet_ntop (AF_INET, &cur_mpath->attr->nexthop, - nh_buf[0], sizeof (nh_buf[0])), - mpath_count); - } - cur_mpath = next_mpath; - } - else - { - /* - * If here, we have a path on the mp_list that was not previously - * a multipath (due to non-equivalance or maxpaths exceeded), - * or the matching multipath is sorted later in the multipath - * list. Before we enqueue the path on the new multipath list, - * make sure its not on the old_best multipath list or referenced - * via next_mpath: - * - If next_mpath points to this new path, update next_mpath to - * point to the multipath after this one - * - Dequeue the path from the multipath list just to make sure - */ - new_mpath = listgetdata (mp_node); - list_delete_node (mp_list, mp_node); - if ((mpath_count < maxpaths) && (new_mpath != new_best) && - bgp_info_nexthop_cmp (prev_mpath, new_mpath)) - { - if (new_mpath == next_mpath) - bgp_info_mpath_next (new_mpath); - bgp_info_mpath_dequeue (new_mpath); - - bgp_info_mpath_enqueue (prev_mpath, new_mpath); - prev_mpath = new_mpath; - mpath_changed = 1; - mpath_count++; - if (debug) - { - bgp_info_path_with_addpath_rx_str(new_mpath, path_buf); - zlog_debug ("%s: add mpath %s nexthop %s, cur count %d", - pfx_buf, path_buf, - inet_ntop (AF_INET, &new_mpath->attr->nexthop, - nh_buf[0], sizeof (nh_buf[0])), - mpath_count); - } - } - mp_node = mp_next_node; - } - } - - if (new_best) - { - if (debug) - zlog_debug("%s: New mpath count (incl newbest) %d mpath-change %s", - pfx_buf, mpath_count, mpath_changed ? "YES" : "NO"); - - bgp_info_mpath_count_set (new_best, mpath_count-1); - if (mpath_changed || (bgp_info_mpath_count (new_best) != old_mpath_count)) - SET_FLAG (new_best->flags, BGP_INFO_MULTIPATH_CHG); - } + u_int16_t maxpaths, mpath_count, old_mpath_count; + struct listnode *mp_node, *mp_next_node; + struct bgp_info *cur_mpath, *new_mpath, *next_mpath, *prev_mpath; + int mpath_changed, debug; + char pfx_buf[PREFIX2STR_BUFFER], nh_buf[2][INET6_ADDRSTRLEN]; + char path_buf[PATH_ADDPATH_STR_BUFFER]; + + mpath_changed = 0; + maxpaths = multipath_num; + mpath_count = 0; + cur_mpath = NULL; + old_mpath_count = 0; + prev_mpath = new_best; + mp_node = listhead(mp_list); + debug = bgp_debug_bestpath(&rn->p); + + if (debug) + prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); + + if (new_best) { + mpath_count++; + if (new_best != old_best) + bgp_info_mpath_dequeue(new_best); + maxpaths = (new_best->peer->sort == BGP_PEER_IBGP) + ? mpath_cfg->maxpaths_ibgp + : mpath_cfg->maxpaths_ebgp; + } + + if (old_best) { + cur_mpath = bgp_info_mpath_first(old_best); + old_mpath_count = bgp_info_mpath_count(old_best); + bgp_info_mpath_count_set(old_best, 0); + bgp_info_mpath_dequeue(old_best); + } + + if (debug) + zlog_debug( + "%s: starting mpath update, newbest %s num candidates %d old-mpath-count %d", + pfx_buf, new_best ? new_best->peer->host : "NONE", + listcount(mp_list), old_mpath_count); + + /* + * We perform an ordered walk through both lists in parallel. + * The reason for the ordered walk is that if there are paths + * that were previously multipaths and are still multipaths, the walk + * should encounter them in both lists at the same time. Otherwise + * there will be paths that are in one list or another, and we + * will deal with these separately. + * + * Note that new_best might be somewhere in the mp_list, so we need + * to skip over it + */ + while (mp_node || cur_mpath) { + struct bgp_info *tmp_info; + + /* + * We can bail out of this loop if all existing paths on the + * multipath list have been visited (for cleanup purposes) and + * the maxpath requirement is fulfulled + */ + if (!cur_mpath && (mpath_count >= maxpaths)) + break; + + mp_next_node = mp_node ? listnextnode(mp_node) : NULL; + next_mpath = cur_mpath ? bgp_info_mpath_next(cur_mpath) : NULL; + tmp_info = mp_node ? listgetdata(mp_node) : NULL; + + if (debug) + zlog_debug( + "%s: comparing candidate %s with existing mpath %s", + pfx_buf, + tmp_info ? tmp_info->peer->host : "NONE", + cur_mpath ? cur_mpath->peer->host : "NONE"); + + /* + * If equal, the path was a multipath and is still a multipath. + * Insert onto new multipath list if maxpaths allows. + */ + if (mp_node && (listgetdata(mp_node) == cur_mpath)) { + list_delete_node(mp_list, mp_node); + bgp_info_mpath_dequeue(cur_mpath); + if ((mpath_count < maxpaths) + && bgp_info_nexthop_cmp(prev_mpath, cur_mpath)) { + bgp_info_mpath_enqueue(prev_mpath, cur_mpath); + prev_mpath = cur_mpath; + mpath_count++; + if (debug) { + bgp_info_path_with_addpath_rx_str( + cur_mpath, path_buf); + zlog_debug( + "%s: %s is still multipath, cur count %d", + pfx_buf, path_buf, mpath_count); + } + } else { + mpath_changed = 1; + if (debug) { + bgp_info_path_with_addpath_rx_str( + cur_mpath, path_buf); + zlog_debug( + "%s: remove mpath %s nexthop %s, cur count %d", + pfx_buf, path_buf, + inet_ntop(AF_INET, + &cur_mpath->attr + ->nexthop, + nh_buf[0], + sizeof(nh_buf[0])), + mpath_count); + } + } + mp_node = mp_next_node; + cur_mpath = next_mpath; + continue; + } + + if (cur_mpath + && (!mp_node + || (bgp_info_mpath_cmp(cur_mpath, listgetdata(mp_node)) + < 0))) { + /* + * If here, we have an old multipath and either the + * mp_list + * is finished or the next mp_node points to a later + * multipath, so we need to purge this path from the + * multipath list + */ + bgp_info_mpath_dequeue(cur_mpath); + mpath_changed = 1; + if (debug) { + bgp_info_path_with_addpath_rx_str(cur_mpath, + path_buf); + zlog_debug( + "%s: remove mpath %s nexthop %s, cur count %d", + pfx_buf, path_buf, + inet_ntop(AF_INET, + &cur_mpath->attr->nexthop, + nh_buf[0], sizeof(nh_buf[0])), + mpath_count); + } + cur_mpath = next_mpath; + } else { + /* + * If here, we have a path on the mp_list that was not + * previously + * a multipath (due to non-equivalance or maxpaths + * exceeded), + * or the matching multipath is sorted later in the + * multipath + * list. Before we enqueue the path on the new multipath + * list, + * make sure its not on the old_best multipath list or + * referenced + * via next_mpath: + * - If next_mpath points to this new path, update + * next_mpath to + * point to the multipath after this one + * - Dequeue the path from the multipath list just to + * make sure + */ + new_mpath = listgetdata(mp_node); + list_delete_node(mp_list, mp_node); + if ((mpath_count < maxpaths) && (new_mpath != new_best) + && bgp_info_nexthop_cmp(prev_mpath, new_mpath)) { + if (new_mpath == next_mpath) + bgp_info_mpath_next(new_mpath); + bgp_info_mpath_dequeue(new_mpath); + + bgp_info_mpath_enqueue(prev_mpath, new_mpath); + prev_mpath = new_mpath; + mpath_changed = 1; + mpath_count++; + if (debug) { + bgp_info_path_with_addpath_rx_str( + new_mpath, path_buf); + zlog_debug( + "%s: add mpath %s nexthop %s, cur count %d", + pfx_buf, path_buf, + inet_ntop(AF_INET, + &new_mpath->attr + ->nexthop, + nh_buf[0], + sizeof(nh_buf[0])), + mpath_count); + } + } + mp_node = mp_next_node; + } + } + + if (new_best) { + if (debug) + zlog_debug( + "%s: New mpath count (incl newbest) %d mpath-change %s", + pfx_buf, mpath_count, + mpath_changed ? "YES" : "NO"); + + bgp_info_mpath_count_set(new_best, mpath_count - 1); + if (mpath_changed + || (bgp_info_mpath_count(new_best) != old_mpath_count)) + SET_FLAG(new_best->flags, BGP_INFO_MULTIPATH_CHG); + } } /* @@ -616,23 +612,22 @@ bgp_info_mpath_update (struct bgp_node *rn, struct bgp_info *new_best, * Clean up multipath information for BGP_INFO_DMED_SELECTED path that * is not selected as best path */ -void -bgp_mp_dmed_deselect (struct bgp_info *dmed_best) +void bgp_mp_dmed_deselect(struct bgp_info *dmed_best) { - struct bgp_info *mpinfo, *mpnext; + struct bgp_info *mpinfo, *mpnext; - if (!dmed_best) - return; + if (!dmed_best) + return; - for (mpinfo = bgp_info_mpath_first (dmed_best); mpinfo; mpinfo = mpnext) - { - mpnext = bgp_info_mpath_next (mpinfo); - bgp_info_mpath_dequeue (mpinfo); - } + for (mpinfo = bgp_info_mpath_first(dmed_best); mpinfo; + mpinfo = mpnext) { + mpnext = bgp_info_mpath_next(mpinfo); + bgp_info_mpath_dequeue(mpinfo); + } - bgp_info_mpath_count_set (dmed_best, 0); - UNSET_FLAG (dmed_best->flags, BGP_INFO_MULTIPATH_CHG); - assert (bgp_info_mpath_first (dmed_best) == 0); + bgp_info_mpath_count_set(dmed_best, 0); + UNSET_FLAG(dmed_best->flags, BGP_INFO_MULTIPATH_CHG); + assert(bgp_info_mpath_first(dmed_best) == 0); } /* @@ -647,129 +642,124 @@ bgp_mp_dmed_deselect (struct bgp_info *dmed_best) * is no change in multipath selection and no attribute change in * any multipath. */ -void -bgp_info_mpath_aggregate_update (struct bgp_info *new_best, - struct bgp_info *old_best) +void bgp_info_mpath_aggregate_update(struct bgp_info *new_best, + struct bgp_info *old_best) { - struct bgp_info *mpinfo; - struct aspath *aspath; - struct aspath *asmerge; - struct attr *new_attr, *old_attr; - u_char origin; - struct community *community, *commerge; - struct ecommunity *ecomm, *ecommerge; - struct lcommunity *lcomm, *lcommerge; - struct attr attr = { 0 }; - - if (old_best && (old_best != new_best) && - (old_attr = bgp_info_mpath_attr (old_best))) - { - bgp_attr_unintern (&old_attr); - bgp_info_mpath_attr_set (old_best, NULL); - } - - if (!new_best) - return; - - if (!bgp_info_mpath_count (new_best)) - { - if ((new_attr = bgp_info_mpath_attr (new_best))) - { - bgp_attr_unintern (&new_attr); - bgp_info_mpath_attr_set (new_best, NULL); - SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED); - } - return; - } - - bgp_attr_dup (&attr, new_best->attr); - - if (new_best->peer && - bgp_flag_check (new_best->peer->bgp, BGP_FLAG_MULTIPATH_RELAX_AS_SET)) - { - - /* aggregate attribute from multipath constituents */ - aspath = aspath_dup (attr.aspath); - origin = attr.origin; - community = attr.community ? community_dup (attr.community) : NULL; - ecomm = (attr.ecommunity) ? ecommunity_dup (attr.ecommunity) : NULL; - lcomm = (attr.lcommunity) ? lcommunity_dup (attr.lcommunity) : NULL; - - for (mpinfo = bgp_info_mpath_first (new_best); mpinfo; - mpinfo = bgp_info_mpath_next (mpinfo)) - { - asmerge = aspath_aggregate (aspath, mpinfo->attr->aspath); - aspath_free (aspath); - aspath = asmerge; - - if (origin < mpinfo->attr->origin) - origin = mpinfo->attr->origin; - - if (mpinfo->attr->community) - { - if (community) - { - commerge = community_merge (community, mpinfo->attr->community); - community = community_uniq_sort (commerge); - community_free (commerge); - } - else - community = community_dup (mpinfo->attr->community); - } - - if (mpinfo->attr->ecommunity) - { - if (ecomm) - { - ecommerge = ecommunity_merge (ecomm, mpinfo->attr->ecommunity); - ecomm = ecommunity_uniq_sort (ecommerge); - ecommunity_free (&ecommerge); - } - else - ecomm = ecommunity_dup (mpinfo->attr->ecommunity); - } - if (mpinfo->attr->lcommunity) - { - if (lcomm) - { - lcommerge = lcommunity_merge (lcomm, mpinfo->attr->lcommunity); - lcomm = lcommunity_uniq_sort (lcommerge); - lcommunity_free (&lcommerge); - } - else - lcomm = lcommunity_dup (mpinfo->attr->lcommunity); - } - } - - attr.aspath = aspath; - attr.origin = origin; - if (community) - { - attr.community = community; - attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES); - } - if (ecomm) - { - attr.ecommunity = ecomm; - attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); - } - - /* Zap multipath attr nexthop so we set nexthop to self */ - attr.nexthop.s_addr = 0; - memset (&attr.mp_nexthop_global, 0, sizeof (struct in6_addr)); - - /* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */ - } - - new_attr = bgp_attr_intern (&attr); - - if (new_attr != bgp_info_mpath_attr (new_best)) - { - if ((old_attr = bgp_info_mpath_attr (new_best))) - bgp_attr_unintern (&old_attr); - bgp_info_mpath_attr_set (new_best, new_attr); - SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED); - } - else - bgp_attr_unintern (&new_attr); + struct bgp_info *mpinfo; + struct aspath *aspath; + struct aspath *asmerge; + struct attr *new_attr, *old_attr; + u_char origin; + struct community *community, *commerge; + struct ecommunity *ecomm, *ecommerge; + struct lcommunity *lcomm, *lcommerge; + struct attr attr = {0}; + + if (old_best && (old_best != new_best) + && (old_attr = bgp_info_mpath_attr(old_best))) { + bgp_attr_unintern(&old_attr); + bgp_info_mpath_attr_set(old_best, NULL); + } + + if (!new_best) + return; + + if (!bgp_info_mpath_count(new_best)) { + if ((new_attr = bgp_info_mpath_attr(new_best))) { + bgp_attr_unintern(&new_attr); + bgp_info_mpath_attr_set(new_best, NULL); + SET_FLAG(new_best->flags, BGP_INFO_ATTR_CHANGED); + } + return; + } + + bgp_attr_dup(&attr, new_best->attr); + + if (new_best->peer && bgp_flag_check(new_best->peer->bgp, + BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { + + /* aggregate attribute from multipath constituents */ + aspath = aspath_dup(attr.aspath); + origin = attr.origin; + community = + attr.community ? community_dup(attr.community) : NULL; + ecomm = (attr.ecommunity) ? ecommunity_dup(attr.ecommunity) + : NULL; + lcomm = (attr.lcommunity) ? lcommunity_dup(attr.lcommunity) + : NULL; + + for (mpinfo = bgp_info_mpath_first(new_best); mpinfo; + mpinfo = bgp_info_mpath_next(mpinfo)) { + asmerge = + aspath_aggregate(aspath, mpinfo->attr->aspath); + aspath_free(aspath); + aspath = asmerge; + + if (origin < mpinfo->attr->origin) + origin = mpinfo->attr->origin; + + if (mpinfo->attr->community) { + if (community) { + commerge = community_merge( + community, + mpinfo->attr->community); + community = + community_uniq_sort(commerge); + community_free(commerge); + } else + community = community_dup( + mpinfo->attr->community); + } + + if (mpinfo->attr->ecommunity) { + if (ecomm) { + ecommerge = ecommunity_merge( + ecomm, + mpinfo->attr->ecommunity); + ecomm = ecommunity_uniq_sort(ecommerge); + ecommunity_free(&ecommerge); + } else + ecomm = ecommunity_dup( + mpinfo->attr->ecommunity); + } + if (mpinfo->attr->lcommunity) { + if (lcomm) { + lcommerge = lcommunity_merge( + lcomm, + mpinfo->attr->lcommunity); + lcomm = lcommunity_uniq_sort(lcommerge); + lcommunity_free(&lcommerge); + } else + lcomm = lcommunity_dup( + mpinfo->attr->lcommunity); + } + } + + attr.aspath = aspath; + attr.origin = origin; + if (community) { + attr.community = community; + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES); + } + if (ecomm) { + attr.ecommunity = ecomm; + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); + } + + /* Zap multipath attr nexthop so we set nexthop to self */ + attr.nexthop.s_addr = 0; + memset(&attr.mp_nexthop_global, 0, sizeof(struct in6_addr)); + + /* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */ + } + + new_attr = bgp_attr_intern(&attr); + + if (new_attr != bgp_info_mpath_attr(new_best)) { + if ((old_attr = bgp_info_mpath_attr(new_best))) + bgp_attr_unintern(&old_attr); + bgp_info_mpath_attr_set(new_best, new_attr); + SET_FLAG(new_best->flags, BGP_INFO_ATTR_CHANGED); + } else + bgp_attr_unintern(&new_attr); } |
