unsigned long conf_bgp_debug_filter;
unsigned long conf_bgp_debug_keepalive;
unsigned long conf_bgp_debug_update;
+unsigned long conf_bgp_debug_bestpath;
unsigned long conf_bgp_debug_zebra;
unsigned long conf_bgp_debug_nht;
unsigned long conf_bgp_debug_update_groups;
unsigned long term_bgp_debug_filter;
unsigned long term_bgp_debug_keepalive;
unsigned long term_bgp_debug_update;
+unsigned long term_bgp_debug_bestpath;
unsigned long term_bgp_debug_zebra;
unsigned long term_bgp_debug_nht;
unsigned long term_bgp_debug_update_groups;
struct list *bgp_debug_update_out_peers = NULL;
struct list *bgp_debug_update_in_peers = NULL;
struct list *bgp_debug_update_prefixes = NULL;
+struct list *bgp_debug_bestpath_prefixes = NULL;
struct list *bgp_debug_zebra_prefixes = NULL;
/* messages for BGP-4 status */
return CMD_SUCCESS;
}
+/* debug bgp bestpath */
+DEFUN (debug_bgp_bestpath_prefix,
+ debug_bgp_bestpath_prefix_cmd,
+ "debug bgp bestpath (A.B.C.D/M|X:X::X:X/M)",
+ DEBUG_STR
+ BGP_STR
+ "BGP bestpath\n"
+ "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+ "IPv6 prefix <network>/<length>\n")
+
+{
+ struct prefix *argv_p;
+ int ret;
+
+ argv_p = prefix_new();
+ ret = str2prefix (argv[0], argv_p);
+ if (!ret)
+ {
+ prefix_free(argv_p);
+ vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+
+ if (!bgp_debug_bestpath_prefixes)
+ bgp_debug_bestpath_prefixes = list_new ();
+
+ if (bgp_debug_list_has_entry(bgp_debug_bestpath_prefixes, NULL, argv_p))
+ {
+ vty_out (vty, "BGP bestptah debugging is already enabled for %s%s", argv[0], VTY_NEWLINE);
+ return CMD_SUCCESS;
+ }
+
+ bgp_debug_list_add_entry(bgp_debug_bestpath_prefixes, NULL, argv_p);
+
+ if (vty->node == CONFIG_NODE)
+ {
+ DEBUG_ON (bestpath, BESTPATH);
+ }
+ else
+ {
+ TERM_DEBUG_ON (bestpath, BESTPATH);
+ vty_out (vty, "BGP bestpath debugging is on for %s%s", argv[0], VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_bestpath_prefix,
+ no_debug_bgp_bestpath_prefix_cmd,
+ "no debug bgp bestpath (A.B.C.D/M|X:X::X:X/M)",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP bestpath\n"
+ "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+ "IPv6 prefix <network>/<length>\n")
+
+{
+ struct prefix *argv_p;
+ int found_prefix = 0;
+ int ret;
+
+ argv_p = prefix_new();
+ ret = str2prefix (argv[0], argv_p);
+ if (!ret)
+ {
+ prefix_free(argv_p);
+ vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (bgp_debug_bestpath_prefixes && !list_isempty(bgp_debug_bestpath_prefixes))
+ {
+ found_prefix = bgp_debug_list_remove_entry(bgp_debug_bestpath_prefixes, NULL, argv_p);
+
+ if (list_isempty(bgp_debug_bestpath_prefixes))
+ {
+ if (vty->node == CONFIG_NODE)
+ {
+ DEBUG_OFF (bestpath, BESTPATH);
+ }
+ else
+ {
+ TERM_DEBUG_OFF (bestpath, BESTPATH);
+ vty_out (vty, "BGP bestpath debugging (per prefix) is off%s", VTY_NEWLINE);
+ }
+ }
+ }
+
+ if (found_prefix)
+ vty_out (vty, "BGP bestpath debugging is off for %s%s", argv[0], VTY_NEWLINE);
+ else
+ vty_out (vty, "BGP bestpath debugging was not enabled for %s%s", argv[0], VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_bestpath,
+ no_debug_bgp_bestpath_cmd,
+ "no debug bgp bestpath",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP bestpath\n")
+{
+ bgp_debug_list_free(bgp_debug_bestpath_prefixes);
+
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF (bestpath, BESTPATH);
+ else
+ {
+ TERM_DEBUG_OFF (bestpath, BESTPATH);
+ vty_out (vty, "BGP bestpath debugging is off%s", VTY_NEWLINE);
+ }
+ return CMD_SUCCESS;
+}
+
+
+
/* debug bgp updates */
DEFUN (debug_bgp_update,
debug_bgp_update_cmd,
bgp_debug_list_free(bgp_debug_update_in_peers);
bgp_debug_list_free(bgp_debug_update_out_peers);
bgp_debug_list_free(bgp_debug_update_prefixes);
+ bgp_debug_list_free(bgp_debug_bestpath_prefixes);
bgp_debug_list_free(bgp_debug_zebra_prefixes);
bgp_debug_clear_updgrp_update_dbg(vty->index);
TERM_DEBUG_OFF (update, UPDATE_IN);
TERM_DEBUG_OFF (update, UPDATE_OUT);
TERM_DEBUG_OFF (update, UPDATE_PREFIX);
+ TERM_DEBUG_OFF (bestpath, BESTPATH);
TERM_DEBUG_OFF (as4, AS4);
TERM_DEBUG_OFF (as4, AS4_SEGMENT);
TERM_DEBUG_OFF (neighbor_events, NEIGHBOR_EVENTS);
bgp_debug_list_print (vty, " BGP updates debugging is on (outbound)",
bgp_debug_update_out_peers);
+ if (BGP_DEBUG (bestpath, BESTPATH))
+ bgp_debug_list_print (vty, " BGP bestpath debugging is on for",
+ bgp_debug_bestpath_prefixes);
+
if (BGP_DEBUG (zebra, ZEBRA))
bgp_debug_list_print (vty, " BGP zebra debugging is on",
bgp_debug_zebra_prefixes);
write++;
}
+ if (CONF_BGP_DEBUG (bestpath, BESTPATH))
+ {
+ vty_out (vty, "debug bgp bestpath%s", VTY_NEWLINE);
+ write++;
+ }
+
if (CONF_BGP_DEBUG (neighbor_events, NEIGHBOR_EVENTS))
{
vty_out (vty, "debug bgp neighbor-events%s", VTY_NEWLINE);
install_element (CONFIG_NODE, &debug_bgp_zebra_cmd);
install_element (ENABLE_NODE, &debug_bgp_update_groups_cmd);
install_element (CONFIG_NODE, &debug_bgp_update_groups_cmd);
+ install_element (ENABLE_NODE, &debug_bgp_bestpath_prefix_cmd);
+ install_element (CONFIG_NODE, &debug_bgp_bestpath_prefix_cmd);
/* deb bgp updates [in|out] A.B.C.D */
install_element (ENABLE_NODE, &debug_bgp_update_direct_peer_cmd);
install_element (ENABLE_NODE, &no_debug_bgp_update_groups_cmd);
install_element (CONFIG_NODE, &no_debug_bgp_update_groups_cmd);
install_element (ENABLE_NODE, &no_debug_bgp_cmd);
+ install_element (ENABLE_NODE, &no_debug_bgp_bestpath_cmd);
+ install_element (CONFIG_NODE, &no_debug_bgp_bestpath_cmd);
+ install_element (ENABLE_NODE, &no_debug_bgp_bestpath_prefix_cmd);
+ install_element (CONFIG_NODE, &no_debug_bgp_bestpath_prefix_cmd);
}
/* Return true if this prefix is on the per_prefix_list of prefixes to debug
return 0;
}
+int
+bgp_debug_bestpath (struct prefix *p)
+{
+ if (BGP_DEBUG (bestpath, BESTPATH))
+ {
+ if (bgp_debug_per_prefix (p, term_bgp_debug_bestpath,
+ BGP_DEBUG_BESTPATH,
+ bgp_debug_bestpath_prefixes))
+ return 1;
+ }
+
+ return 0;
+}
+
int
bgp_debug_zebra (struct prefix *p)
{
}
}
-/* Compare two bgp route entity. br is preferable then return 1. */
+/* Compare two bgp route entity. If 'new' is preferable over 'exist' return 1. */
static int
bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
- int *paths_eq, struct bgp_maxpaths_cfg *mpath_cfg)
+ int *paths_eq, struct bgp_maxpaths_cfg *mpath_cfg, int debug,
+ char *pfx_buf)
{
struct attr *newattr, *existattr;
struct attr_extra *newattre, *existattre;
/* 0. Null check. */
if (new == NULL)
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: new is NULL", pfx_buf);
+ return 0;
+ }
+
if (exist == NULL)
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s is the initial bestpath",
+ pfx_buf, new->peer->host);
+ return 1;
+ }
newattr = new->attr;
existattr = exist->attr;
exist_weight = existattre->weight;
if (new_weight > exist_weight)
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to weight %d > %d",
+ pfx_buf, new->peer->host, exist->peer->host, new_weight,
+ exist_weight);
+ return 1;
+ }
+
if (new_weight < exist_weight)
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to weight %d < %d",
+ pfx_buf, new->peer->host, exist->peer->host, new_weight,
+ exist_weight);
+ return 0;
+ }
/* 2. Local preference check. */
new_pref = exist_pref = bgp->default_local_pref;
exist_pref = existattr->local_pref;
if (new_pref > exist_pref)
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to localpref %d > %d",
+ pfx_buf, new->peer->host, exist->peer->host, new_pref,
+ exist_pref);
+ return 1;
+ }
+
if (new_pref < exist_pref)
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to localpref %d < %d",
+ pfx_buf, new->peer->host, exist->peer->host, new_pref,
+ exist_pref);
+ return 0;
+ }
/* 3. Local route check. We prefer:
* - BGP_ROUTE_STATIC
* - BGP_ROUTE_REDISTRIBUTE
*/
if (! (new->sub_type == BGP_ROUTE_NORMAL))
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to preferred BGP_ROUTE type",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 1;
+ }
+
if (! (exist->sub_type == BGP_ROUTE_NORMAL))
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to preferred BGP_ROUTE type",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 0;
+ }
/* 4. AS path length check. */
if (! bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE))
aspath_hops += aspath_count_confeds (newattr->aspath);
if ( aspath_hops < (exist_hops + exist_confeds))
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to aspath (with confeds) hopcount %d < %d",
+ pfx_buf, new->peer->host, exist->peer->host,
+ aspath_hops, (exist_hops + exist_confeds));
+ return 1;
+ }
+
if ( aspath_hops > (exist_hops + exist_confeds))
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to aspath (with confeds) hopcount %d > %d",
+ pfx_buf, new->peer->host, exist->peer->host,
+ aspath_hops, (exist_hops + exist_confeds));
+ return 0;
+ }
}
else
{
int newhops = aspath_count_hops (newattr->aspath);
if (newhops < exist_hops)
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to aspath hopcount %d < %d",
+ pfx_buf, new->peer->host, exist->peer->host,
+ newhops, exist_hops);
+ return 1;
+ }
+
if (newhops > exist_hops)
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to aspath hopcount %d > %d",
+ pfx_buf, new->peer->host, exist->peer->host,
+ newhops, exist_hops);
+ return 0;
+ }
}
}
/* 5. Origin check. */
if (newattr->origin < existattr->origin)
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to ORIGIN %s < %s",
+ pfx_buf, new->peer->host, exist->peer->host,
+ bgp_origin_long_str[newattr->origin],
+ bgp_origin_long_str[existattr->origin]);
+ return 1;
+ }
+
if (newattr->origin > existattr->origin)
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to ORIGIN %s > %s",
+ pfx_buf, new->peer->host, exist->peer->host,
+ bgp_origin_long_str[newattr->origin],
+ bgp_origin_long_str[existattr->origin]);
+ return 0;
+ }
/* 6. MED check. */
internal_as_route = (aspath_count_hops (newattr->aspath) == 0
exist_med = bgp_med_value (exist->attr, bgp);
if (new_med < exist_med)
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to MED %d < %d",
+ pfx_buf, new->peer->host, exist->peer->host, new_med,
+ exist_med);
+ return 1;
+ }
+
if (new_med > exist_med)
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to MED %d > %d",
+ pfx_buf, new->peer->host, exist->peer->host, new_med,
+ exist_med);
+ return 0;
+ }
}
/* 7. Peer type check. */
if (new_sort == BGP_PEER_EBGP
&& (exist_sort == BGP_PEER_IBGP || exist_sort == BGP_PEER_CONFED))
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to eBGP peer > iBGP peeer",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 1;
+ }
+
if (exist_sort == BGP_PEER_EBGP
&& (new_sort == BGP_PEER_IBGP || new_sort == BGP_PEER_CONFED))
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to iBGP peer < eBGP peeer",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 0;
+ }
/* 8. IGP metric check. */
newm = existm = 0;
existm = exist->extra->igpmetric;
if (newm < existm)
- ret = 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to IGP metric %d < %d",
+ pfx_buf, new->peer->host, exist->peer->host, newm, existm);
+ ret = 1;
+ }
+
if (newm > existm)
- ret = 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to IGP metric %d > %d",
+ pfx_buf, new->peer->host, exist->peer->host, newm, existm);
+ ret = 0;
+ }
/* 8.1. Same IGP metric. Compare the cluster list length as
representative of IGP hops metric. Rewrite the metric value
{
newm = BGP_CLUSTER_LIST_LENGTH(new->attr);
existm = BGP_CLUSTER_LIST_LENGTH(exist->attr);
+
if (newm < existm)
- ret = 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to CLUSTER_LIST length %d < %d",
+ pfx_buf, new->peer->host, exist->peer->host, newm,
+ existm);
+ ret = 1;
+ }
+
if (newm > existm)
- ret = 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to CLUSTER_LIST length %d > %d",
+ pfx_buf, new->peer->host, exist->peer->host, newm,
+ existm);
+ ret = 0;
+ }
}
}
* array.
*/
*paths_eq = 1;
+
+ if (debug)
+ zlog_debug("%s: path %s and path %s are equal via multipath-relax",
+ pfx_buf, new->peer->host, exist->peer->host);
}
else if (new->peer->sort == BGP_PEER_IBGP)
{
if (aspath_cmp (new->attr->aspath, exist->attr->aspath))
- *paths_eq = 1;
+ {
+ *paths_eq = 1;
+
+ if (debug)
+ zlog_debug("%s: path %s and path %s are equal via matching aspaths",
+ pfx_buf, new->peer->host, exist->peer->host);
+ }
}
else if (new->peer->as == exist->peer->as)
- *paths_eq = 1;
+ {
+ *paths_eq = 1;
+
+ if (debug)
+ zlog_debug("%s: path %s and path %s are equal via same remote-as",
+ pfx_buf, new->peer->host, exist->peer->host);
+ }
}
else
{
&& exist_sort == BGP_PEER_EBGP)
{
if (CHECK_FLAG (new->flags, BGP_INFO_SELECTED))
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to oldest external",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 1;
+ }
+
if (CHECK_FLAG (exist->flags, BGP_INFO_SELECTED))
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to oldest external",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 0;
+ }
}
/* 11. Router-ID comparision. */
exist_id.s_addr = exist->peer->remote_id.s_addr;
if (ntohl (new_id.s_addr) < ntohl (exist_id.s_addr))
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to Router-ID comparison",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 1;
+ }
+
if (ntohl (new_id.s_addr) > ntohl (exist_id.s_addr))
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to Router-ID comparison",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 0;
+ }
/* 12. Cluster length comparision. */
new_cluster = BGP_CLUSTER_LIST_LENGTH(new->attr);
exist_cluster = BGP_CLUSTER_LIST_LENGTH(exist->attr);
if (new_cluster < exist_cluster)
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to CLUSTER_LIST length %d < %d",
+ pfx_buf, new->peer->host, exist->peer->host, new_cluster,
+ exist_cluster);
+ return 1;
+ }
+
if (new_cluster > exist_cluster)
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to CLUSTER_LIST length %d > %d",
+ pfx_buf, new->peer->host, exist->peer->host, new_cluster,
+ exist_cluster);
+ return 0;
+ }
/* 13. Neighbor address comparision. */
/* Do this only if neither path is "stale" as stale paths do not have
* valid peer information (as the connection may or may not be up).
*/
if (CHECK_FLAG (exist->flags, BGP_INFO_STALE))
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to latter path being STALE",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 1;
+ }
+
if (CHECK_FLAG (new->flags, BGP_INFO_STALE))
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to former path being STALE",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 0;
+ }
ret = sockunion_cmp (new->peer->su_remote, exist->peer->su_remote);
if (ret == 1)
- return 0;
+ {
+ if (debug)
+ zlog_debug("%s: path %s loses to path %s due to Neighor IP comparison",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 0;
+ }
+
if (ret == -1)
- return 1;
+ {
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to Neighor IP comparison",
+ pfx_buf, new->peer->host, exist->peer->host);
+ return 1;
+ }
+
+ if (debug)
+ zlog_debug("%s: path %s wins over path %s due to nothing left to compare",
+ pfx_buf, new->peer->host, exist->peer->host);
return 1;
}
struct bgp_info *ri1;
struct bgp_info *ri2;
struct bgp_info *nextri = NULL;
- int paths_eq, do_mpath;
+ int paths_eq, do_mpath, debug;
struct list mp_list;
char buf[INET6_BUFSIZ];
+ char pfx_buf[INET6_ADDRSTRLEN];
bgp_mp_list_init (&mp_list);
do_mpath = (mpath_cfg->maxpaths_ebgp != BGP_DEFAULT_MAXPATHS ||
mpath_cfg->maxpaths_ibgp != BGP_DEFAULT_MAXPATHS);
+ debug = bgp_debug_bestpath(&rn->p);
+
+ if (debug)
+ prefix2str (&rn->p, pfx_buf, sizeof (pfx_buf));
+
/* bgp deterministic-med */
new_select = NULL;
if (bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED))
if (CHECK_FLAG (ri2->flags, BGP_INFO_SELECTED))
old_select = ri2;
if (bgp_info_cmp (bgp, ri2, new_select, &paths_eq,
- mpath_cfg))
+ mpath_cfg, debug, pfx_buf))
{
bgp_info_unset_flag (rn, new_select, BGP_INFO_DMED_SELECTED);
new_select = ri2;
bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_CHECK);
bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_SELECTED);
- if (bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg))
+ if (bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg, debug, pfx_buf))
{
new_select = ri;
}
*/
if (do_mpath && new_select)
{
+ if (debug)
+ zlog_debug("%s: path %s is the bestpath, now find multipaths",
+ pfx_buf, new_select->peer->host);
+
for (ri = rn->info; (ri != NULL) && (nextri = ri->next, 1); ri = nextri)
{
if (ri == new_select)
{
+ if (debug)
+ zlog_debug("%s: path %s is the bestpath, add to the multipath list",
+ pfx_buf, ri->peer->host);
bgp_mp_list_add (&mp_list, ri);
continue;
}
&& (! CHECK_FLAG (ri->flags, BGP_INFO_DMED_SELECTED)))
continue;
- bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg);
+ bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg, debug, pfx_buf);
if (paths_eq)
{
+ if (debug)
+ zlog_debug("%s: %s path is equivalent to the bestpath, add to the multipath list",
+ pfx_buf, ri->peer->host);
bgp_mp_list_add (&mp_list, ri);
}
}