diff options
134 files changed, 4194 insertions, 2510 deletions
diff --git a/Makefile.am b/Makefile.am index 34f112bf01..6dc8e0d354 100644 --- a/Makefile.am +++ b/Makefile.am @@ -228,8 +228,6 @@ EXTRA_DIST += \ vrrpd/Makefile \ # end -noinst_HEADERS += defaults.h - clean-local: clean-python .PHONY: clean-python clean-python: diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index bfa578085d..331c0ece2d 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -421,14 +421,15 @@ static struct transit *transit_intern(struct transit *transit) return find; } -void transit_unintern(struct transit *transit) +static void transit_unintern(struct transit **transit) { - if (transit->refcnt) - transit->refcnt--; + if ((*transit)->refcnt) + (*transit)->refcnt--; - if (transit->refcnt == 0) { - hash_release(transit_hash, transit); - transit_free(transit); + if ((*transit)->refcnt == 0) { + hash_release(transit_hash, *transit); + transit_free(*transit); + *transit = NULL; } } @@ -464,15 +465,6 @@ static void transit_finish(void) /* Attribute hash routines. */ static struct hash *attrhash; -/* Shallow copy of an attribute - * Though, not so shallow that it doesn't copy the contents - * of the attr_extra pointed to by 'extra' - */ -void bgp_attr_dup(struct attr *new, struct attr *orig) -{ - *new = *orig; -} - unsigned long int attr_count(void) { return attrhash->count; @@ -860,7 +852,7 @@ void bgp_attr_unintern_sub(struct attr *attr) UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)); if (attr->transit) - transit_unintern(attr->transit); + transit_unintern(&attr->transit); if (attr->encap_subtlvs) encap_unintern(&attr->encap_subtlvs, ENCAP_SUBTLV_TYPE); @@ -2492,7 +2484,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } /* Fetch attribute flag and type. */ @@ -2515,7 +2508,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } /* Check extended attribue length bit. */ @@ -2536,7 +2530,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } /* Set type to bitmap to check duplicate attribute. `type' is @@ -2593,7 +2588,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, ndata, ndl + lfl + 1); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } struct bgp_attr_parser_args attr_args = { @@ -2618,7 +2614,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, attr_args.total); if (ret == BGP_ATTR_PARSE_PROCEED) continue; - return ret; + goto done; } /* OK check attribute and store it's value. */ @@ -2696,32 +2692,25 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); ret = BGP_ATTR_PARSE_ERROR; + goto done; } if (ret == BGP_ATTR_PARSE_EOR) { - if (as4_path) - aspath_unintern(&as4_path); - return ret; + goto done; } - /* If hard error occurred immediately return to the caller. */ if (ret == BGP_ATTR_PARSE_ERROR) { flog_warn(EC_BGP_ATTRIBUTE_PARSE_ERROR, "%s: Attribute %s, parse error", peer->host, lookup_msg(attr_str, type, NULL)); - if (as4_path) - aspath_unintern(&as4_path); - return ret; + goto done; } if (ret == BGP_ATTR_PARSE_WITHDRAW) { - flog_warn( EC_BGP_ATTRIBUTE_PARSE_WITHDRAW, "%s: Attribute %s, parse error - treating as withdrawal", peer->host, lookup_msg(attr_str, type, NULL)); - if (as4_path) - aspath_unintern(&as4_path); - return ret; + goto done; } /* Check the fetched length. */ @@ -2731,9 +2720,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, peer->host, lookup_msg(attr_str, type, NULL)); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - if (as4_path) - aspath_unintern(&as4_path); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } } @@ -2744,9 +2732,9 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, lookup_msg(attr_str, type, NULL)); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - if (as4_path) - aspath_unintern(&as4_path); - return BGP_ATTR_PARSE_ERROR; + + ret = BGP_ATTR_PARSE_ERROR; + goto done; } /* @@ -2765,16 +2753,14 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) && !CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI))) { if (bgp_attr_nexthop_valid(peer, attr) < 0) { - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } } /* Check all mandatory well-known attributes are present */ - if ((ret = bgp_attr_check(peer, attr)) < 0) { - if (as4_path) - aspath_unintern(&as4_path); - return ret; - } + if ((ret = bgp_attr_check(peer, attr)) < 0) + goto done; /* * At this place we can see whether we got AS4_PATH and/or @@ -2797,28 +2783,10 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, &as4_aggregator_addr)) { bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); - if (as4_path) - aspath_unintern(&as4_path); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } - /* At this stage, we have done all fiddling with as4, and the - * resulting info is in attr->aggregator resp. attr->aspath - * so we can chuck as4_aggregator and as4_path alltogether in - * order to save memory - */ - if (as4_path) { - aspath_unintern(&as4_path); /* unintern - it is in the hash */ - /* The flag that we got this is still there, but that does not - * do any trouble - */ - } - /* - * The "rest" of the code does nothing with as4_aggregator. - * there is no memory attached specifically which is not part - * of the attr. - * so ignoring just means do nothing. - */ /* * Finally do the checks on the aspath we did not do yet * because we waited for a potentially synthesized aspath. @@ -2826,21 +2794,59 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH))) { ret = bgp_attr_aspath_check(peer, attr); if (ret != BGP_ATTR_PARSE_PROCEED) - return ret; + goto done; + } + + ret = BGP_ATTR_PARSE_PROCEED; +done: + + /* + * At this stage, we have done all fiddling with as4, and the + * resulting info is in attr->aggregator resp. attr->aspath so + * we can chuck as4_aggregator and as4_path alltogether in order + * to save memory + */ + if (as4_path) { + /* + * unintern - it is in the hash + * The flag that we got this is still there, but that + * does not do any trouble + */ + aspath_unintern(&as4_path); } - /* Finally intern unknown attribute. */ + + if (ret != BGP_ATTR_PARSE_ERROR) { + /* Finally intern unknown attribute. */ + if (attr->transit) + attr->transit = transit_intern(attr->transit); + if (attr->encap_subtlvs) + attr->encap_subtlvs = encap_intern(attr->encap_subtlvs, + ENCAP_SUBTLV_TYPE); +#if ENABLE_BGP_VNC + if (attr->vnc_subtlvs) + attr->vnc_subtlvs = encap_intern(attr->vnc_subtlvs, + VNC_SUBTLV_TYPE); +#endif + } else { + if (attr->transit) { + transit_free(attr->transit); + attr->transit = NULL; + } + + bgp_attr_flush_encap(attr); + }; + + /* Sanity checks */ if (attr->transit) - attr->transit = transit_intern(attr->transit); + assert(attr->transit->refcnt > 0); if (attr->encap_subtlvs) - attr->encap_subtlvs = - encap_intern(attr->encap_subtlvs, ENCAP_SUBTLV_TYPE); + assert(attr->encap_subtlvs->refcnt > 0); #if ENABLE_BGP_VNC if (attr->vnc_subtlvs) - attr->vnc_subtlvs = - encap_intern(attr->vnc_subtlvs, VNC_SUBTLV_TYPE); + assert(attr->vnc_subtlvs->refcnt > 0); #endif - return BGP_ATTR_PARSE_PROCEED; + return ret; } /* diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 375a2272e1..40e87e190a 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -272,7 +272,6 @@ extern void bgp_attr_finish(void); extern bgp_attr_parse_ret_t bgp_attr_parse(struct peer *, struct attr *, bgp_size_t, struct bgp_nlri *, struct bgp_nlri *); -extern void bgp_attr_dup(struct attr *, struct attr *); extern void bgp_attr_undup(struct attr *new, struct attr *old); extern struct attr *bgp_attr_intern(struct attr *attr); extern void bgp_attr_unintern_sub(struct attr *); @@ -305,9 +304,6 @@ extern unsigned long int attr_unknown_count(void); extern int cluster_loop_check(struct cluster_list *, struct in_addr); extern void cluster_unintern(struct cluster_list *); -/* Transit attribute prototypes. */ -void transit_unintern(struct transit *); - /* Below exported for unit-test purposes only */ struct bgp_attr_parser_args { struct peer *peer; diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 48b714c7df..b8798a7ced 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -49,6 +49,7 @@ #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_mac.h" +#include "bgpd/bgp_vty.h" /* * Definitions and external declarations. @@ -1571,7 +1572,7 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, * present, else treat as locally originated. */ if (src_attr) - bgp_attr_dup(&attr, src_attr); + attr = *src_attr; else { memset(&attr, 0, sizeof(struct attr)); bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); @@ -2661,7 +2662,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, * address for the rest of the code to flow through. In the case of IPv4, * make sure to set the flag for next hop attribute. */ - bgp_attr_dup(&attr, parent_pi->attr); + attr = *parent_pi->attr; if (afi == AFI_IP6) evpn_convert_nexthop_to_ipv6(&attr); else @@ -5709,9 +5710,10 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, int ret = 0; - ret = bgp_get(&bgp_vrf, &as, vrf_id_to_name(vrf_id), - vrf_id == VRF_DEFAULT ? BGP_INSTANCE_TYPE_DEFAULT - : BGP_INSTANCE_TYPE_VRF); + ret = bgp_get_vty(&bgp_vrf, &as, vrf_id_to_name(vrf_id), + vrf_id == VRF_DEFAULT + ? BGP_INSTANCE_TYPE_DEFAULT + : BGP_INSTANCE_TYPE_VRF); switch (ret) { case BGP_ERR_AS_MISMATCH: flog_err(EC_BGP_EVPN_AS_MISMATCH, diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 6460ff76fe..8b84d696dc 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -164,6 +164,14 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) bgp_writes_off(from_peer); bgp_reads_off(from_peer); + /* + * Before exchanging FD remove doppelganger from + * keepalive peer hash. It could be possible conf peer + * fd is set to -1. If blocked on lock then keepalive + * thread can access peer pointer with fd -1. + */ + bgp_keepalives_off(from_peer); + BGP_TIMER_OFF(peer->t_routeadv); BGP_TIMER_OFF(peer->t_connect); BGP_TIMER_OFF(peer->t_connect_check_r); diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 08c5d3468d..9cb3957a86 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -27,7 +27,6 @@ #include "thread.h" #include <lib/version.h> #include "memory.h" -#include "memory_vty.h" #include "prefix.h" #include "log.h" #include "privs.h" diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index d37bf54734..77448ec15d 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -720,7 +720,7 @@ void bgp_path_info_mpath_aggregate_update(struct bgp_path_info *new_best, return; } - bgp_attr_dup(&attr, new_best->attr); + attr = *new_best->attr; if (new_best->peer && bgp_flag_check(new_best->peer->bgp, BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 1d15361416..59ed433e58 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -699,7 +699,8 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ return; } - bgp_attr_dup(&static_attr, path_vrf->attr); /* shallow copy */ + /* shallow copy */ + static_attr = *path_vrf->attr; /* * route map handling @@ -1082,7 +1083,8 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ buf_prefix, bgp_vrf->name_pretty); } - bgp_attr_dup(&static_attr, path_vpn->attr); /* shallow copy */ + /* shallow copy */ + static_attr = *path_vpn->attr; /* * Nexthop: stash and clear diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index ae70ea0690..3ef1ac39a5 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1695,7 +1695,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, } /* For modify attribute, copy it to temporary structure. */ - bgp_attr_dup(attr, piattr); + *attr = *piattr; /* If local-preference is not set. */ if ((peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) @@ -1815,7 +1815,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, if ((from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP) && !bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { - bgp_attr_dup(&dummy_attr, attr); + dummy_attr = *attr; rmap_path.attr = &dummy_attr; } @@ -3178,7 +3178,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, goto filtered; } - bgp_attr_dup(&new_attr, attr); + new_attr = *attr; /* Apply incoming route-map. * NB: new_attr may now contain newly allocated values from route-map @@ -6829,7 +6829,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, struct attr attr_new; /* Copy attribute for modification. */ - bgp_attr_dup(&attr_new, &attr); + attr_new = attr; if (red->redist_metric_flag) attr_new.med = red->redist_metric; @@ -9218,7 +9218,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, struct attr dummy_attr; route_map_result_t ret; - bgp_attr_dup(&dummy_attr, pi->attr); + dummy_attr = *pi->attr; path.peer = pi->peer; path.attr = &dummy_attr; @@ -11361,7 +11361,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, header2 = 0; } - bgp_attr_dup(&attr, ain->attr); + attr = *ain->attr; route_filtered = false; /* Filter prefix using distribute list, @@ -11463,7 +11463,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, header2 = 0; } - bgp_attr_dup(&attr, adj->attr); + attr = *adj->attr; ret = bgp_output_modifier( peer, &rn->p, &attr, afi, safi, rmap_name); diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 5c1483a768..49e87adc3c 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -764,7 +764,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) /* Provide dummy so the route-map can't modify * the attributes */ - bgp_attr_dup(&dummy_attr, ri->attr); + dummy_attr = *ri->attr; tmp_info.peer = ri->peer; tmp_info.attr = &dummy_attr; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index fa236a24b7..0ff64c527b 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -32,7 +32,7 @@ #include "thread.h" #include "log.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" #include "hash.h" #include "queue.h" #include "filter.h" @@ -64,8 +64,46 @@ #include "bgpd/bgp_bfd.h" #include "bgpd/bgp_io.h" #include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_evpn_vty.h" #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_mac.h" +#include "bgpd/bgp_flowspec.h" +#if ENABLE_BGP_VNC +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#endif + +FRR_CFG_DEFAULT_BOOL(BGP_IMPORT_CHECK, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) +FRR_CFG_DEFAULT_BOOL(BGP_SHOW_HOSTNAME, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) +FRR_CFG_DEFAULT_BOOL(BGP_LOG_NEIGHBOR_CHANGES, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) +FRR_CFG_DEFAULT_BOOL(BGP_DETERMINISTIC_MED, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) +FRR_CFG_DEFAULT_ULONG(BGP_CONNECT_RETRY, + { .val_ulong = 10, .match_profile = "datacenter", }, + { .val_ulong = 120 }, +) +FRR_CFG_DEFAULT_ULONG(BGP_HOLDTIME, + { .val_ulong = 9, .match_profile = "datacenter", }, + { .val_ulong = 180 }, +) +FRR_CFG_DEFAULT_ULONG(BGP_KEEPALIVE, + { .val_ulong = 3, .match_profile = "datacenter", }, + { .val_ulong = 60 }, +) + +DEFINE_HOOK(bgp_inst_config_write, + (struct bgp *bgp, struct vty *vty), + (bgp, vty)) static struct peer_group *listen_range_exists(struct bgp *bgp, struct prefix *range, int exact); @@ -347,6 +385,29 @@ int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, return ret; } +int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, + enum bgp_instance_type inst_type) +{ + int ret = bgp_get(bgp, as, name, inst_type); + + if (ret == BGP_CREATED) { + bgp_timers_set(*bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME, + DFLT_BGP_CONNECT_RETRY); + + if (DFLT_BGP_IMPORT_CHECK) + bgp_flag_set(*bgp, BGP_FLAG_IMPORT_CHECK); + if (DFLT_BGP_SHOW_HOSTNAME) + bgp_flag_set(*bgp, BGP_FLAG_SHOW_HOSTNAME); + if (DFLT_BGP_LOG_NEIGHBOR_CHANGES) + bgp_flag_set(*bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); + if (DFLT_BGP_DETERMINISTIC_MED) + bgp_flag_set(*bgp, BGP_FLAG_DETERMINISTIC_MED); + + ret = BGP_SUCCESS; + } + return ret; +} + /* * bgp_vty_find_and_parse_afi_safi_bgp * @@ -1068,7 +1129,7 @@ DEFUN_NOSH (router_bgp, if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) is_new_bgp = (bgp_lookup(as, name) == NULL); - ret = bgp_get(&bgp, &as, name, inst_type); + ret = bgp_get_vty(&bgp, &as, name, inst_type); switch (ret) { case BGP_ERR_AS_MISMATCH: vty_out(vty, "BGP is already running; AS is %u\n", as); @@ -1777,8 +1838,8 @@ ALIAS_HIDDEN(no_bgp_maxpaths_ibgp, no_bgp_maxpaths_ibgp_hidden_cmd, "Number of paths\n" "Match the cluster length\n") -void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi) +static void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi) { if (bgp->maxpaths[afi][safi].maxpaths_ebgp != MULTIPATH_NUM) { vty_out(vty, " maximum-paths %d\n", @@ -1821,7 +1882,7 @@ DEFUN (bgp_timers, return CMD_WARNING_CONFIG_FAILED; } - bgp_timers_set(bgp, keepalive, holdtime); + bgp_timers_set(bgp, keepalive, holdtime, DFLT_BGP_CONNECT_RETRY); return CMD_SUCCESS; } @@ -1836,7 +1897,8 @@ DEFUN (no_bgp_timers, "Holdtime\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_timers_unset(bgp); + bgp_timers_set(bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME, + DFLT_BGP_CONNECT_RETRY); return CMD_SUCCESS; } @@ -6956,8 +7018,8 @@ DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd, as_t as = bgp->as; /* Auto-create assuming the same AS */ - ret = bgp_get(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT); + ret = bgp_get_vty(&bgp_default, &as, NULL, + BGP_INSTANCE_TYPE_DEFAULT); if (ret) { vty_out(vty, @@ -7042,8 +7104,8 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, bgp_default = bgp_get_default(); if (!bgp_default) { /* Auto-create assuming the same AS */ - ret = bgp_get(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT); + ret = bgp_get_vty(&bgp_default, &as, NULL, + BGP_INSTANCE_TYPE_DEFAULT); if (ret) { vty_out(vty, @@ -7058,7 +7120,7 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, vrf_bgp = bgp_default; else /* Auto-create assuming the same AS */ - ret = bgp_get(&vrf_bgp, &as, import_name, bgp_type); + ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type); if (ret) { vty_out(vty, @@ -9725,9 +9787,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_neigh, "bgpTimerConfiguredKeepAliveIntervalMsecs", p->keepalive * 1000); - } else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME) - || (bgp->default_keepalive - != BGP_DEFAULT_KEEPALIVE)) { + } else if ((bgp->default_holdtime != SAVE_BGP_HOLDTIME) + || (bgp->default_keepalive != SAVE_BGP_KEEPALIVE)) { json_object_int_add(json_neigh, "bgpTimerConfiguredHoldTimeMsecs", bgp->default_holdtime); @@ -9789,9 +9850,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, p->holdtime); vty_out(vty, ", keepalive interval is %d seconds\n", p->keepalive); - } else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME) - || (bgp->default_keepalive - != BGP_DEFAULT_KEEPALIVE)) { + } else if ((bgp->default_holdtime != SAVE_BGP_HOLDTIME) + || (bgp->default_keepalive != SAVE_BGP_KEEPALIVE)) { vty_out(vty, " Configured hold time is %d", bgp->default_holdtime); vty_out(vty, ", keepalive interval is %d seconds\n", @@ -12809,8 +12869,8 @@ DEFUN (no_bgp_redistribute_ipv6, return bgp_redistribute_unset(bgp, AFI_IP6, type, 0); } -void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi) +static void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi) { int i; @@ -12847,8 +12907,86 @@ void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, } } +/* peer-group helpers for config-write */ + +static bool peergroup_flag_check(struct peer *peer, uint32_t flag) +{ + if (!peer_group_active(peer)) { + if (CHECK_FLAG(peer->flags_invert, flag)) + return !CHECK_FLAG(peer->flags, flag); + else + return !!CHECK_FLAG(peer->flags, flag); + } + + return !!CHECK_FLAG(peer->flags_override, flag); +} + +static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, + uint32_t flag) +{ + if (!peer_group_active(peer)) { + if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag)) + return !peer_af_flag_check(peer, afi, safi, flag); + else + return !!peer_af_flag_check(peer, afi, safi, flag); + } + + return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag); +} + +static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi, + uint8_t type, int direct) +{ + struct bgp_filter *filter; + + if (peer_group_active(peer)) + return !!CHECK_FLAG(peer->filter_override[afi][safi][direct], + type); + + filter = &peer->filter[afi][safi]; + switch (type) { + case PEER_FT_DISTRIBUTE_LIST: + return !!(filter->dlist[direct].name); + case PEER_FT_FILTER_LIST: + return !!(filter->aslist[direct].name); + case PEER_FT_PREFIX_LIST: + return !!(filter->plist[direct].name); + case PEER_FT_ROUTE_MAP: + return !!(filter->map[direct].name); + case PEER_FT_UNSUPPRESS_MAP: + return !!(filter->usmap.name); + default: + return false; + } +} + +/* Return true if the addpath type is set for peer and different from + * peer-group. + */ +static int peergroup_af_addpath_check(struct peer *peer, afi_t afi, safi_t safi) +{ + enum bgp_addpath_strat type, g_type; + + type = peer->addpath_type[afi][safi]; + + if (type != BGP_ADDPATH_NONE) { + if (peer_group_active(peer)) { + g_type = peer->group->conf->addpath_type[afi][safi]; + + if (type != g_type) + return 1; + else + return 0; + } + + return 1; + } + + return 0; +} + /* This is part of the address-family block (unicast only) */ -void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, +static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, afi_t afi) { int indent = 2; @@ -12948,6 +13086,970 @@ void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, } } +static void bgp_config_write_filter(struct vty *vty, struct peer *peer, + afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + char *addr; + + addr = peer->host; + filter = &peer->filter[afi][safi]; + + /* distribute-list. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s distribute-list %s in\n", addr, + filter->dlist[FILTER_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, + FILTER_OUT)) + vty_out(vty, " neighbor %s distribute-list %s out\n", addr, + filter->dlist[FILTER_OUT].name); + + /* prefix-list. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s prefix-list %s in\n", addr, + filter->plist[FILTER_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, + FILTER_OUT)) + vty_out(vty, " neighbor %s prefix-list %s out\n", addr, + filter->plist[FILTER_OUT].name); + + /* route-map. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN)) + vty_out(vty, " neighbor %s route-map %s in\n", addr, + filter->map[RMAP_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, + RMAP_OUT)) + vty_out(vty, " neighbor %s route-map %s out\n", addr, + filter->map[RMAP_OUT].name); + + /* unsuppress-map */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0)) + vty_out(vty, " neighbor %s unsuppress-map %s\n", addr, + filter->usmap.name); + + /* filter-list. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s filter-list %s in\n", addr, + filter->aslist[FILTER_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, + FILTER_OUT)) + vty_out(vty, " neighbor %s filter-list %s out\n", addr, + filter->aslist[FILTER_OUT].name); +} + +/* BGP peer configuration display function. */ +static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, + struct peer *peer) +{ + struct peer *g_peer = NULL; + char buf[SU_ADDRSTRLEN]; + char *addr; + int if_pg_printed = false; + int if_ras_printed = false; + + /* Skip dynamic neighbors. */ + if (peer_dynamic_neighbor(peer)) + return; + + if (peer->conf_if) + addr = peer->conf_if; + else + addr = peer->host; + + /************************************ + ****** Global to the neighbor ****** + ************************************/ + if (peer->conf_if) { + if (CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY)) + vty_out(vty, " neighbor %s interface v6only", addr); + else + vty_out(vty, " neighbor %s interface", addr); + + if (peer_group_active(peer)) { + vty_out(vty, " peer-group %s", peer->group->name); + if_pg_printed = true; + } else if (peer->as_type == AS_SPECIFIED) { + vty_out(vty, " remote-as %u", peer->as); + if_ras_printed = true; + } else if (peer->as_type == AS_INTERNAL) { + vty_out(vty, " remote-as internal"); + if_ras_printed = true; + } else if (peer->as_type == AS_EXTERNAL) { + vty_out(vty, " remote-as external"); + if_ras_printed = true; + } + + vty_out(vty, "\n"); + } + + /* remote-as and peer-group */ + /* peer is a member of a peer-group */ + if (peer_group_active(peer)) { + g_peer = peer->group->conf; + + if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) { + if (peer->as_type == AS_SPECIFIED) { + vty_out(vty, " neighbor %s remote-as %u\n", + addr, peer->as); + } else if (peer->as_type == AS_INTERNAL) { + vty_out(vty, + " neighbor %s remote-as internal\n", + addr); + } else if (peer->as_type == AS_EXTERNAL) { + vty_out(vty, + " neighbor %s remote-as external\n", + addr); + } + } + + /* For swpX peers we displayed the peer-group + * via 'neighbor swpX interface peer-group PGNAME' */ + if (!if_pg_printed) + vty_out(vty, " neighbor %s peer-group %s\n", addr, + peer->group->name); + } + + /* peer is NOT a member of a peer-group */ + else { + /* peer is a peer-group, declare the peer-group */ + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + vty_out(vty, " neighbor %s peer-group\n", addr); + } + + if (!if_ras_printed) { + if (peer->as_type == AS_SPECIFIED) { + vty_out(vty, " neighbor %s remote-as %u\n", + addr, peer->as); + } else if (peer->as_type == AS_INTERNAL) { + vty_out(vty, + " neighbor %s remote-as internal\n", + addr); + } else if (peer->as_type == AS_EXTERNAL) { + vty_out(vty, + " neighbor %s remote-as external\n", + addr); + } + } + } + + /* local-as */ + if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) { + vty_out(vty, " neighbor %s local-as %u", addr, + peer->change_local_as); + if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND)) + vty_out(vty, " no-prepend"); + if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS)) + vty_out(vty, " replace-as"); + vty_out(vty, "\n"); + } + + /* description */ + if (peer->desc) { + vty_out(vty, " neighbor %s description %s\n", addr, peer->desc); + } + + /* shutdown */ + if (peergroup_flag_check(peer, PEER_FLAG_SHUTDOWN)) { + if (peer->tx_shutdown_message) + vty_out(vty, " neighbor %s shutdown message %s\n", addr, + peer->tx_shutdown_message); + else + vty_out(vty, " neighbor %s shutdown\n", addr); + } + + /* bfd */ + if (peer->bfd_info) { + if (!peer_group_active(peer) || !g_peer->bfd_info) { + bgp_bfd_peer_config_write(vty, peer, addr); + } + } + + /* password */ + if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD)) + vty_out(vty, " neighbor %s password %s\n", addr, + peer->password); + + /* neighbor solo */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)) { + if (!peer_group_active(peer)) { + vty_out(vty, " neighbor %s solo\n", addr); + } + } + + /* BGP port */ + if (peer->port != BGP_PORT_DEFAULT) { + vty_out(vty, " neighbor %s port %d\n", addr, peer->port); + } + + /* Local interface name */ + if (peer->ifname) { + vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname); + } + + /* passive */ + if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE)) + vty_out(vty, " neighbor %s passive\n", addr); + + /* ebgp-multihop */ + if (peer->sort != BGP_PEER_IBGP && peer->ttl != BGP_DEFAULT_TTL + && !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL)) { + if (!peer_group_active(peer) || g_peer->ttl != peer->ttl) { + vty_out(vty, " neighbor %s ebgp-multihop %d\n", addr, + peer->ttl); + } + } + + /* ttl-security hops */ + if (peer->gtsm_hops != 0) { + if (!peer_group_active(peer) + || g_peer->gtsm_hops != peer->gtsm_hops) { + vty_out(vty, " neighbor %s ttl-security hops %d\n", + addr, peer->gtsm_hops); + } + } + + /* disable-connected-check */ + if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK)) + vty_out(vty, " neighbor %s disable-connected-check\n", addr); + + /* enforce-first-as */ + if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS)) + vty_out(vty, " neighbor %s enforce-first-as\n", addr); + + /* update-source */ + if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) { + if (peer->update_source) + vty_out(vty, " neighbor %s update-source %s\n", addr, + sockunion2str(peer->update_source, buf, + SU_ADDRSTRLEN)); + else if (peer->update_if) + vty_out(vty, " neighbor %s update-source %s\n", addr, + peer->update_if); + } + + /* advertisement-interval */ + if (peergroup_flag_check(peer, PEER_FLAG_ROUTEADV)) + vty_out(vty, " neighbor %s advertisement-interval %u\n", addr, + peer->routeadv); + + /* timers */ + if (peergroup_flag_check(peer, PEER_FLAG_TIMER)) + vty_out(vty, " neighbor %s timers %u %u\n", addr, + peer->keepalive, peer->holdtime); + + /* timers connect */ + if (peergroup_flag_check(peer, PEER_FLAG_TIMER_CONNECT)) + vty_out(vty, " neighbor %s timers connect %u\n", addr, + peer->connect); + /* need special-case handling for changed default values due to + * config profile / version (because there is no "timers bgp connect" + * command, we need to save this per-peer :/) + */ + else if (!peer_group_active(peer) && !peer->connect && + peer->bgp->default_connect_retry != SAVE_BGP_CONNECT_RETRY) + vty_out(vty, " neighbor %s timers connect %u\n", addr, + peer->bgp->default_connect_retry); + + /* capability dynamic */ + if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY)) + vty_out(vty, " neighbor %s capability dynamic\n", addr); + + /* capability extended-nexthop */ + if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_ENHE)) { + if (!peer->conf_if) { + if (CHECK_FLAG(peer->flags_invert, + PEER_FLAG_CAPABILITY_ENHE)) + vty_out(vty, + " no neighbor %s capability extended-nexthop\n", + addr); + else + vty_out(vty, + " neighbor %s capability extended-nexthop\n", + addr); + } + } + + /* dont-capability-negotiation */ + if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY)) + vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr); + + /* override-capability */ + if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY)) + vty_out(vty, " neighbor %s override-capability\n", addr); + + /* strict-capability-match */ + if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH)) + vty_out(vty, " neighbor %s strict-capability-match\n", addr); + + /* Sender side AS path loop detection. */ + if (peer->as_path_loop_detection) + vty_out(vty, " neighbor %s sender-as-path-loop-detection\n", + addr); +} + +/* BGP peer configuration display function. */ +static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, + struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer *g_peer = NULL; + char *addr; + bool flag_scomm, flag_secomm, flag_slcomm; + + /* Skip dynamic neighbors. */ + if (peer_dynamic_neighbor(peer)) + return; + + if (peer->conf_if) + addr = peer->conf_if; + else + addr = peer->host; + + /************************************ + ****** Per AF to the neighbor ****** + ************************************/ + if (peer_group_active(peer)) { + g_peer = peer->group->conf; + + /* If the peer-group is active but peer is not, print a 'no + * activate' */ + if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) { + vty_out(vty, " no neighbor %s activate\n", addr); + } + + /* If the peer-group is not active but peer is, print an + 'activate' */ + else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) { + vty_out(vty, " neighbor %s activate\n", addr); + } + } else { + if (peer->afc[afi][safi]) { + if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { + if (bgp_flag_check(bgp, + BGP_FLAG_NO_DEFAULT_IPV4)) { + vty_out(vty, " neighbor %s activate\n", + addr); + } + } else + vty_out(vty, " neighbor %s activate\n", addr); + } else { + if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { + if (!bgp_flag_check(bgp, + BGP_FLAG_NO_DEFAULT_IPV4)) { + vty_out(vty, + " no neighbor %s activate\n", + addr); + } + } + } + } + + /* addpath TX knobs */ + if (peergroup_af_addpath_check(peer, afi, safi)) { + switch (peer->addpath_type[afi][safi]) { + case BGP_ADDPATH_ALL: + vty_out(vty, " neighbor %s addpath-tx-all-paths\n", + addr); + break; + case BGP_ADDPATH_BEST_PER_AS: + vty_out(vty, + " neighbor %s addpath-tx-bestpath-per-AS\n", + addr); + break; + case BGP_ADDPATH_MAX: + case BGP_ADDPATH_NONE: + break; + } + } + + /* ORF capability. */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_RM)) { + vty_out(vty, " neighbor %s capability orf prefix-list", addr); + + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_SM) + && peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_RM)) + vty_out(vty, " both"); + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_SM)) + vty_out(vty, " send"); + else + vty_out(vty, " receive"); + vty_out(vty, "\n"); + } + + /* BGP flag dampening. */ + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_DAMPENING)) + bgp_config_write_damp(vty, afi, safi); + + /* Route reflector client. */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REFLECTOR_CLIENT)) { + vty_out(vty, " neighbor %s route-reflector-client\n", addr); + } + + /* next-hop-self force */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_FORCE_NEXTHOP_SELF)) { + vty_out(vty, " neighbor %s next-hop-self force\n", addr); + } + + /* next-hop-self */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) { + vty_out(vty, " neighbor %s next-hop-self\n", addr); + } + + /* remove-private-AS */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) { + vty_out(vty, " neighbor %s remove-private-AS all replace-AS\n", + addr); + } + + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) { + vty_out(vty, " neighbor %s remove-private-AS replace-AS\n", + addr); + } + + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) { + vty_out(vty, " neighbor %s remove-private-AS all\n", addr); + } + + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS)) { + vty_out(vty, " neighbor %s remove-private-AS\n", addr); + } + + /* as-override */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) { + vty_out(vty, " neighbor %s as-override\n", addr); + } + + /* send-community print. */ + flag_scomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_COMMUNITY); + flag_secomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY); + flag_slcomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_LARGE_COMMUNITY); + + if (flag_scomm && flag_secomm && flag_slcomm) { + vty_out(vty, " no neighbor %s send-community all\n", addr); + } else { + if (flag_scomm) + vty_out(vty, " no neighbor %s send-community\n", addr); + if (flag_secomm) + vty_out(vty, + " no neighbor %s send-community extended\n", + addr); + + if (flag_slcomm) + vty_out(vty, " no neighbor %s send-community large\n", + addr); + } + + /* Default information */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_DEFAULT_ORIGINATE)) { + vty_out(vty, " neighbor %s default-originate", addr); + + if (peer->default_rmap[afi][safi].name) + vty_out(vty, " route-map %s", + peer->default_rmap[afi][safi].name); + + vty_out(vty, "\n"); + } + + /* Soft reconfiguration inbound. */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) { + vty_out(vty, " neighbor %s soft-reconfiguration inbound\n", + addr); + } + + /* maximum-prefix. */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) { + vty_out(vty, " neighbor %s maximum-prefix %" PRIu32, addr, + peer->pmax[afi][safi]); + + if (peer->pmax_threshold[afi][safi] + != MAXIMUM_PREFIX_THRESHOLD_DEFAULT) + vty_out(vty, " %u", peer->pmax_threshold[afi][safi]); + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_MAX_PREFIX_WARNING)) + vty_out(vty, " warning-only"); + if (peer->pmax_restart[afi][safi]) + vty_out(vty, " restart %u", + peer->pmax_restart[afi][safi]); + + vty_out(vty, "\n"); + } + + /* Route server client. */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_RSERVER_CLIENT)) { + vty_out(vty, " neighbor %s route-server-client\n", addr); + } + + /* Nexthop-local unchanged. */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) { + vty_out(vty, " neighbor %s nexthop-local unchanged\n", addr); + } + + /* allowas-in <1-10> */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) { + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_ALLOWAS_IN_ORIGIN)) { + vty_out(vty, " neighbor %s allowas-in origin\n", addr); + } else if (peer->allowas_in[afi][safi] == 3) { + vty_out(vty, " neighbor %s allowas-in\n", addr); + } else { + vty_out(vty, " neighbor %s allowas-in %d\n", addr, + peer->allowas_in[afi][safi]); + } + } + + /* weight */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT)) + vty_out(vty, " neighbor %s weight %lu\n", addr, + peer->weight[afi][safi]); + + /* Filter. */ + bgp_config_write_filter(vty, peer, afi, safi); + + /* atribute-unchanged. */ + if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED) + || (safi != SAFI_EVPN + && peer_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED)) + || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) { + + if (!peer_group_active(peer) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_MED_UNCHANGED)) { + + vty_out(vty, + " neighbor %s attribute-unchanged%s%s%s\n", + addr, + peer_af_flag_check(peer, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED) + ? " as-path" + : "", + peer_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED) + ? " next-hop" + : "", + peer_af_flag_check(peer, afi, safi, + PEER_FLAG_MED_UNCHANGED) + ? " med" + : ""); + } + } +} + +/* Address family based peer configuration display. */ +static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) +{ + struct peer *peer; + struct peer_group *group; + struct listnode *node, *nnode; + + + vty_frame(vty, " !\n address-family "); + if (afi == AFI_IP) { + if (safi == SAFI_UNICAST) + vty_frame(vty, "ipv4 unicast"); + else if (safi == SAFI_LABELED_UNICAST) + vty_frame(vty, "ipv4 labeled-unicast"); + else if (safi == SAFI_MULTICAST) + vty_frame(vty, "ipv4 multicast"); + else if (safi == SAFI_MPLS_VPN) + vty_frame(vty, "ipv4 vpn"); + else if (safi == SAFI_ENCAP) + vty_frame(vty, "ipv4 encap"); + else if (safi == SAFI_FLOWSPEC) + vty_frame(vty, "ipv4 flowspec"); + } else if (afi == AFI_IP6) { + if (safi == SAFI_UNICAST) + vty_frame(vty, "ipv6 unicast"); + else if (safi == SAFI_LABELED_UNICAST) + vty_frame(vty, "ipv6 labeled-unicast"); + else if (safi == SAFI_MULTICAST) + vty_frame(vty, "ipv6 multicast"); + else if (safi == SAFI_MPLS_VPN) + vty_frame(vty, "ipv6 vpn"); + else if (safi == SAFI_ENCAP) + vty_frame(vty, "ipv6 encap"); + else if (safi == SAFI_FLOWSPEC) + vty_frame(vty, "ipv6 flowspec"); + } else if (afi == AFI_L2VPN) { + if (safi == SAFI_EVPN) + vty_frame(vty, "l2vpn evpn"); + } + vty_frame(vty, "\n"); + + bgp_config_write_distance(vty, bgp, afi, safi); + + bgp_config_write_network(vty, bgp, afi, safi); + + bgp_config_write_redistribute(vty, bgp, afi, safi); + + for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) + bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi); + + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + /* Skip dynamic neighbors. */ + if (peer_dynamic_neighbor(peer)) + continue; + + /* Do not display doppelganger peers */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + bgp_config_write_peer_af(vty, bgp, peer, afi, safi); + } + + bgp_config_write_maxpaths(vty, bgp, afi, safi); + bgp_config_write_table_map(vty, bgp, afi, safi); + + if (safi == SAFI_EVPN) + bgp_config_write_evpn_info(vty, bgp, afi, safi); + + if (safi == SAFI_FLOWSPEC) + bgp_fs_config_write_pbr(vty, bgp, afi, safi); + + if (safi == SAFI_UNICAST) { + bgp_vpn_policy_config_write_afi(vty, bgp, afi); + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { + + vty_out(vty, " export vpn\n"); + } + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) { + + vty_out(vty, " import vpn\n"); + } + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + char *name; + + for (ALL_LIST_ELEMENTS_RO( + bgp->vpn_policy[afi].import_vrf, node, + name)) + vty_out(vty, " import vrf %s\n", name); + } + } + + vty_endframe(vty, " exit-address-family\n"); +} + +int bgp_config_write(struct vty *vty) +{ + struct bgp *bgp; + struct peer_group *group; + struct peer *peer; + struct listnode *node, *nnode; + struct listnode *mnode, *mnnode; + + if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER) + vty_out(vty, "bgp route-map delay-timer %u\n", + bm->rmap_update_timer); + + /* BGP configuration. */ + for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { + + /* skip all auto created vrf as they dont have user config */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) + continue; + + /* Router bgp ASN */ + vty_out(vty, "router bgp %u", bgp->as); + + if (bgp->name) + vty_out(vty, " %s %s", + (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) + ? "view" : "vrf", bgp->name); + vty_out(vty, "\n"); + + /* BGP fast-external-failover. */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) + vty_out(vty, " no bgp fast-external-failover\n"); + + /* BGP router ID. */ + if (bgp->router_id_static.s_addr != 0) + vty_out(vty, " bgp router-id %s\n", + inet_ntoa(bgp->router_id_static)); + + /* BGP log-neighbor-changes. */ + if (!!bgp_flag_check(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES) + != SAVE_BGP_LOG_NEIGHBOR_CHANGES) + vty_out(vty, " %sbgp log-neighbor-changes\n", + bgp_flag_check(bgp, + BGP_FLAG_LOG_NEIGHBOR_CHANGES) + ? "" + : "no "); + + /* BGP configuration. */ + if (bgp_flag_check(bgp, BGP_FLAG_ALWAYS_COMPARE_MED)) + vty_out(vty, " bgp always-compare-med\n"); + + /* RFC8212 default eBGP policy. */ + if (bgp->ebgp_requires_policy + == DEFAULT_EBGP_POLICY_ENABLED) + vty_out(vty, " bgp ebgp-requires-policy\n"); + + /* draft-ietf-idr-deprecate-as-set-confed-set */ + if (bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED) + vty_out(vty, " bgp reject-as-sets\n"); + + /* BGP default ipv4-unicast. */ + if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4)) + vty_out(vty, " no bgp default ipv4-unicast\n"); + + /* BGP default local-preference. */ + if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF) + vty_out(vty, " bgp default local-preference %u\n", + bgp->default_local_pref); + + /* BGP default show-hostname */ + if (!!bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME) + != SAVE_BGP_SHOW_HOSTNAME) + vty_out(vty, " %sbgp default show-hostname\n", + bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME) + ? "" + : "no "); + + /* BGP default subgroup-pkt-queue-max. */ + if (bgp->default_subgroup_pkt_queue_max + != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX) + vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n", + bgp->default_subgroup_pkt_queue_max); + + /* BGP client-to-client reflection. */ + if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT)) + vty_out(vty, " no bgp client-to-client reflection\n"); + + /* BGP cluster ID. */ + if (CHECK_FLAG(bgp->config, BGP_CONFIG_CLUSTER_ID)) + vty_out(vty, " bgp cluster-id %s\n", + inet_ntoa(bgp->cluster_id)); + + /* Disable ebgp connected nexthop check */ + if (bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) + vty_out(vty, + " bgp disable-ebgp-connected-route-check\n"); + + /* Confederation identifier*/ + if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) + vty_out(vty, " bgp confederation identifier %u\n", + bgp->confed_id); + + /* Confederation peer */ + if (bgp->confed_peers_cnt > 0) { + int i; + + vty_out(vty, " bgp confederation peers"); + + for (i = 0; i < bgp->confed_peers_cnt; i++) + vty_out(vty, " %u", bgp->confed_peers[i]); + + vty_out(vty, "\n"); + } + + /* BGP deterministic-med. */ + if (!!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED) + != SAVE_BGP_DETERMINISTIC_MED) + vty_out(vty, " %sbgp deterministic-med\n", + bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED) + ? "" + : "no "); + + /* BGP update-delay. */ + bgp_config_write_update_delay(vty, bgp); + + if (bgp->v_maxmed_onstartup + != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) { + vty_out(vty, " bgp max-med on-startup %u", + bgp->v_maxmed_onstartup); + if (bgp->maxmed_onstartup_value + != BGP_MAXMED_VALUE_DEFAULT) + vty_out(vty, " %u", + bgp->maxmed_onstartup_value); + vty_out(vty, "\n"); + } + if (bgp->v_maxmed_admin != BGP_MAXMED_ADMIN_UNCONFIGURED) { + vty_out(vty, " bgp max-med administrative"); + if (bgp->maxmed_admin_value != BGP_MAXMED_VALUE_DEFAULT) + vty_out(vty, " %u", bgp->maxmed_admin_value); + vty_out(vty, "\n"); + } + + /* write quanta */ + bgp_config_write_wpkt_quanta(vty, bgp); + /* read quanta */ + bgp_config_write_rpkt_quanta(vty, bgp); + + /* coalesce time */ + bgp_config_write_coalesce_time(vty, bgp); + + /* BGP graceful-restart. */ + if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME) + vty_out(vty, + " bgp graceful-restart stalepath-time %u\n", + bgp->stalepath_time); + if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME) + vty_out(vty, " bgp graceful-restart restart-time %u\n", + bgp->restart_time); + if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) + vty_out(vty, " bgp graceful-restart\n"); + + /* BGP graceful-shutdown */ + if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) + vty_out(vty, " bgp graceful-shutdown\n"); + + /* BGP graceful-restart Preserve State F bit. */ + if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD)) + vty_out(vty, + " bgp graceful-restart preserve-fw-state\n"); + + /* BGP bestpath method. */ + if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE)) + vty_out(vty, " bgp bestpath as-path ignore\n"); + if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_CONFED)) + vty_out(vty, " bgp bestpath as-path confed\n"); + + if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { + if (bgp_flag_check(bgp, + BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { + vty_out(vty, + " bgp bestpath as-path multipath-relax as-set\n"); + } else { + vty_out(vty, + " bgp bestpath as-path multipath-relax\n"); + } + } + + if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { + vty_out(vty, + " bgp route-reflector allow-outbound-policy\n"); + } + if (bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID)) + vty_out(vty, " bgp bestpath compare-routerid\n"); + if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED) + || bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) { + vty_out(vty, " bgp bestpath med"); + if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED)) + vty_out(vty, " confed"); + if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) + vty_out(vty, " missing-as-worst"); + vty_out(vty, "\n"); + } + + /* BGP network import check. */ + if (!!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) + != SAVE_BGP_IMPORT_CHECK) + vty_out(vty, " %sbgp network import-check\n", + bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) + ? "" + : "no "); + + /* BGP timers configuration. */ + if (bgp->default_keepalive != SAVE_BGP_KEEPALIVE + && bgp->default_holdtime != SAVE_BGP_HOLDTIME) + vty_out(vty, " timers bgp %u %u\n", + bgp->default_keepalive, bgp->default_holdtime); + + /* peer-group */ + for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) { + bgp_config_write_peer_global(vty, bgp, group->conf); + } + + /* Normal neighbor configuration. */ + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + bgp_config_write_peer_global(vty, bgp, peer); + } + + /* listen range and limit for dynamic BGP neighbors */ + bgp_config_write_listen(vty, bgp); + + /* + * BGP default autoshutdown neighbors + * + * This must be placed after any peer and peer-group + * configuration, to avoid setting all peers to shutdown after + * a daemon restart, which is undesired behavior. (see #2286) + */ + if (bgp->autoshutdown) + vty_out(vty, " bgp default shutdown\n"); + + /* IPv4 unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); + + /* IPv4 multicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST); + + /* IPv4 labeled-unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST); + + /* IPv4 VPN configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN); + + /* ENCAPv4 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP); + + /* FLOWSPEC v4 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC); + + /* IPv6 unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST); + + /* IPv6 multicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST); + + /* IPv6 labeled-unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, + SAFI_LABELED_UNICAST); + + /* IPv6 VPN configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN); + + /* ENCAPv6 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP); + + /* FLOWSPEC v6 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC); + + /* EVPN configuration. */ + bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN); + + hook_call(bgp_inst_config_write, bgp, vty); + +#if ENABLE_BGP_VNC + bgp_rfapi_cfg_write(vty, bgp); +#endif + + vty_out(vty, "!\n"); + } + return 0; +} + /* BGP node structure. */ static struct cmd_node bgp_node = { diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 27b5ea47b2..5f3ce9cd8e 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -21,6 +21,8 @@ #ifndef _QUAGGA_BGP_VTY_H #define _QUAGGA_BGP_VTY_H +#include "bgpd/bgpd.h" + struct bgp; #define BGP_INSTANCE_HELP_STR "BGP view\nBGP VRF\nView/VRF name\n" @@ -46,6 +48,8 @@ struct bgp; extern void bgp_vty_init(void); extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); +extern int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, + enum bgp_instance_type inst_type); extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp); @@ -72,6 +76,5 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty, bool use_json); extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, safi_t safi, bool show_failed, bool use_json); -extern void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, - afi_t afi); + #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index e886733ced..069c53d7df 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1072,7 +1072,7 @@ static int update_ipv4nh_for_route_install(int nh_othervrf, */ if (is_evpn) { api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; - api_nh->onlink = true; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; } else if (nh_othervrf && api_nh->gate.ipv4.s_addr == INADDR_ANY) { @@ -1098,7 +1098,7 @@ update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, if (is_evpn) { api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; - api_nh->onlink = true; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; } else if (nh_othervrf) { if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) { @@ -1347,6 +1347,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, has_valid_label = 1; label = label_pton(&mpinfo->extra->label[0]); + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL); + api_nh->label_num = 1; api_nh->labels[0] = label; } @@ -1355,11 +1357,6 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, valid_nh_count++; } - - /* if this is a evpn route we don't have to include the label */ - if (has_valid_label && !(CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))) - SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); - /* * When we create an aggregate route we must also * install a Null0 route in the RIB, so overwrite @@ -1693,7 +1690,7 @@ int bgp_redistribute_metric_set(struct bgp *bgp, struct bgp_redist *red, struct attr *old_attr; struct attr new_attr; - bgp_attr_dup(&new_attr, pi->attr); + new_attr = *pi->attr; new_attr.med = red->redist_metric; old_attr = pi->attr; pi->attr = bgp_attr_intern(&new_attr); diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index b912870b80..62c311cc1d 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -32,10 +32,6 @@ extern void bgp_zebra_destroy(void); extern int bgp_zebra_get_table_range(uint32_t chunk_size, uint32_t *start, uint32_t *end); extern int bgp_if_update_all(void); -extern void bgp_config_write_maxpaths(struct vty *, struct bgp *, afi_t, - safi_t); -extern void bgp_config_write_redistribute(struct vty *, struct bgp *, afi_t, - safi_t); extern void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, struct bgp_path_info *path, struct bgp *bgp, afi_t afi, safi_t safi); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 4283fe169e..9b0e81491a 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -95,9 +95,6 @@ DEFINE_QOBJ_TYPE(bgp_master) DEFINE_QOBJ_TYPE(bgp) DEFINE_QOBJ_TYPE(peer) DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp)) -DEFINE_HOOK(bgp_inst_config_write, - (struct bgp *bgp, struct vty *vty), - (bgp, vty)) /* BGP process wide configuration. */ static struct bgp_master bgp_master; @@ -407,19 +404,23 @@ time_t bgp_clock(void) } /* BGP timer configuration. */ -int bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime) +int bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime, + uint32_t connect_retry) { bgp->default_keepalive = (keepalive < holdtime / 3 ? keepalive : holdtime / 3); bgp->default_holdtime = holdtime; + bgp->default_connect_retry = connect_retry; return 0; } +/* mostly for completeness - CLI uses its own defaults */ int bgp_timers_unset(struct bgp *bgp) { bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; + bgp->default_connect_retry = BGP_DEFAULT_CONNECT_RETRY; return 0; } @@ -883,82 +884,6 @@ void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi, COND_FLAG(peer->af_flags[afi][safi], flag, group_val); } -static bool peergroup_flag_check(struct peer *peer, uint32_t flag) -{ - if (!peer_group_active(peer)) { - if (CHECK_FLAG(peer->flags_invert, flag)) - return !CHECK_FLAG(peer->flags, flag); - else - return !!CHECK_FLAG(peer->flags, flag); - } - - return !!CHECK_FLAG(peer->flags_override, flag); -} - -static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, - uint32_t flag) -{ - if (!peer_group_active(peer)) { - if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag)) - return !peer_af_flag_check(peer, afi, safi, flag); - else - return !!peer_af_flag_check(peer, afi, safi, flag); - } - - return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag); -} - -static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi, - uint8_t type, int direct) -{ - struct bgp_filter *filter; - - if (peer_group_active(peer)) - return !!CHECK_FLAG(peer->filter_override[afi][safi][direct], - type); - - filter = &peer->filter[afi][safi]; - switch (type) { - case PEER_FT_DISTRIBUTE_LIST: - return !!(filter->dlist[direct].name); - case PEER_FT_FILTER_LIST: - return !!(filter->aslist[direct].name); - case PEER_FT_PREFIX_LIST: - return !!(filter->plist[direct].name); - case PEER_FT_ROUTE_MAP: - return !!(filter->map[direct].name); - case PEER_FT_UNSUPPRESS_MAP: - return !!(filter->usmap.name); - default: - return false; - } -} - -/* Return true if the addpath type is set for peer and different from - * peer-group. - */ -static int peergroup_af_addpath_check(struct peer *peer, afi_t afi, safi_t safi) -{ - enum bgp_addpath_strat type, g_type; - - type = peer->addpath_type[afi][safi]; - - if (type != BGP_ADDPATH_NONE) { - if (peer_group_active(peer)) { - g_type = peer->group->conf->addpath_type[afi][safi]; - - if (type != g_type) - return 1; - else - return 0; - } - - return 1; - } - - return 0; -} - /* Check peer's AS number and determines if this peer is IBGP or EBGP */ static inline bgp_peer_sort_t peer_calc_sort(struct peer *peer) { @@ -1197,7 +1122,7 @@ struct peer *peer_new(struct bgp *bgp) /* Set default value. */ peer->fd = -1; peer->v_start = BGP_INIT_START_TIMER; - peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + peer->v_connect = bgp->default_connect_retry; peer->status = Idle; peer->ostatus = Idle; peer->cur_event = peer->last_event = peer->last_major_event = 0; @@ -2496,7 +2421,7 @@ static void peer_group2peer_config_copy(struct peer_group *group, if (CHECK_FLAG(conf->flags, PEER_FLAG_TIMER_CONNECT)) peer->v_connect = conf->connect; else - peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + peer->v_connect = peer->bgp->default_connect_retry; } /* advertisement-interval apply */ @@ -2983,26 +2908,13 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF; bgp->default_subgroup_pkt_queue_max = BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX; - bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; - bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; + bgp_timers_unset(bgp); bgp->restart_time = BGP_DEFAULT_RESTART_TIME; bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT; bgp->dynamic_neighbors_count = 0; bgp->ebgp_requires_policy = DEFAULT_EBGP_POLICY_DISABLED; bgp->reject_as_sets = BGP_REJECT_AS_SETS_DISABLED; -#if DFLT_BGP_IMPORT_CHECK - bgp_flag_set(bgp, BGP_FLAG_IMPORT_CHECK); -#endif -#if DFLT_BGP_SHOW_HOSTNAME - bgp_flag_set(bgp, BGP_FLAG_SHOW_HOSTNAME); -#endif -#if DFLT_BGP_LOG_NEIGHBOR_CHANGES - bgp_flag_set(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); -#endif -#if DFLT_BGP_DETERMINISTIC_MED - bgp_flag_set(bgp, BGP_FLAG_DETERMINISTIC_MED); -#endif bgp_addpath_init_bgp_data(&bgp->tx_addpath); bgp->as = *as; @@ -3255,7 +3167,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, bgp_zebra_instance_register(bgp); } - return BGP_SUCCESS; + return BGP_CREATED; } /* @@ -5059,7 +4971,7 @@ int peer_timers_connect_unset(struct peer *peer) if (peer->connect) peer->v_connect = peer->connect; else - peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + peer->v_connect = peer->bgp->default_connect_retry; /* Skip peer-group mechanics for regular peers. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) @@ -5077,7 +4989,7 @@ int peer_timers_connect_unset(struct peer *peer) /* Remove flag and configuration on peer-group member. */ UNSET_FLAG(member->flags, PEER_FLAG_TIMER_CONNECT); member->connect = 0; - member->v_connect = BGP_DEFAULT_CONNECT_RETRY; + member->v_connect = peer->bgp->default_connect_retry; } return 0; @@ -6899,962 +6811,6 @@ char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json, return buf; } -static void bgp_config_write_filter(struct vty *vty, struct peer *peer, - afi_t afi, safi_t safi) -{ - struct bgp_filter *filter; - char *addr; - - addr = peer->host; - filter = &peer->filter[afi][safi]; - - /* distribute-list. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, - FILTER_IN)) - vty_out(vty, " neighbor %s distribute-list %s in\n", addr, - filter->dlist[FILTER_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, - FILTER_OUT)) - vty_out(vty, " neighbor %s distribute-list %s out\n", addr, - filter->dlist[FILTER_OUT].name); - - /* prefix-list. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, - FILTER_IN)) - vty_out(vty, " neighbor %s prefix-list %s in\n", addr, - filter->plist[FILTER_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, - FILTER_OUT)) - vty_out(vty, " neighbor %s prefix-list %s out\n", addr, - filter->plist[FILTER_OUT].name); - - /* route-map. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN)) - vty_out(vty, " neighbor %s route-map %s in\n", addr, - filter->map[RMAP_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, - RMAP_OUT)) - vty_out(vty, " neighbor %s route-map %s out\n", addr, - filter->map[RMAP_OUT].name); - - /* unsuppress-map */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0)) - vty_out(vty, " neighbor %s unsuppress-map %s\n", addr, - filter->usmap.name); - - /* filter-list. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, - FILTER_IN)) - vty_out(vty, " neighbor %s filter-list %s in\n", addr, - filter->aslist[FILTER_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, - FILTER_OUT)) - vty_out(vty, " neighbor %s filter-list %s out\n", addr, - filter->aslist[FILTER_OUT].name); -} - -/* BGP peer configuration display function. */ -static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, - struct peer *peer) -{ - struct peer *g_peer = NULL; - char buf[SU_ADDRSTRLEN]; - char *addr; - int if_pg_printed = false; - int if_ras_printed = false; - - /* Skip dynamic neighbors. */ - if (peer_dynamic_neighbor(peer)) - return; - - if (peer->conf_if) - addr = peer->conf_if; - else - addr = peer->host; - - /************************************ - ****** Global to the neighbor ****** - ************************************/ - if (peer->conf_if) { - if (CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY)) - vty_out(vty, " neighbor %s interface v6only", addr); - else - vty_out(vty, " neighbor %s interface", addr); - - if (peer_group_active(peer)) { - vty_out(vty, " peer-group %s", peer->group->name); - if_pg_printed = true; - } else if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " remote-as %u", peer->as); - if_ras_printed = true; - } else if (peer->as_type == AS_INTERNAL) { - vty_out(vty, " remote-as internal"); - if_ras_printed = true; - } else if (peer->as_type == AS_EXTERNAL) { - vty_out(vty, " remote-as external"); - if_ras_printed = true; - } - - vty_out(vty, "\n"); - } - - /* remote-as and peer-group */ - /* peer is a member of a peer-group */ - if (peer_group_active(peer)) { - g_peer = peer->group->conf; - - if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) { - if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " neighbor %s remote-as %u\n", - addr, peer->as); - } else if (peer->as_type == AS_INTERNAL) { - vty_out(vty, - " neighbor %s remote-as internal\n", - addr); - } else if (peer->as_type == AS_EXTERNAL) { - vty_out(vty, - " neighbor %s remote-as external\n", - addr); - } - } - - /* For swpX peers we displayed the peer-group - * via 'neighbor swpX interface peer-group PGNAME' */ - if (!if_pg_printed) - vty_out(vty, " neighbor %s peer-group %s\n", addr, - peer->group->name); - } - - /* peer is NOT a member of a peer-group */ - else { - /* peer is a peer-group, declare the peer-group */ - if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - vty_out(vty, " neighbor %s peer-group\n", addr); - } - - if (!if_ras_printed) { - if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " neighbor %s remote-as %u\n", - addr, peer->as); - } else if (peer->as_type == AS_INTERNAL) { - vty_out(vty, - " neighbor %s remote-as internal\n", - addr); - } else if (peer->as_type == AS_EXTERNAL) { - vty_out(vty, - " neighbor %s remote-as external\n", - addr); - } - } - } - - /* local-as */ - if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) { - vty_out(vty, " neighbor %s local-as %u", addr, - peer->change_local_as); - if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND)) - vty_out(vty, " no-prepend"); - if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS)) - vty_out(vty, " replace-as"); - vty_out(vty, "\n"); - } - - /* description */ - if (peer->desc) { - vty_out(vty, " neighbor %s description %s\n", addr, peer->desc); - } - - /* shutdown */ - if (peergroup_flag_check(peer, PEER_FLAG_SHUTDOWN)) { - if (peer->tx_shutdown_message) - vty_out(vty, " neighbor %s shutdown message %s\n", addr, - peer->tx_shutdown_message); - else - vty_out(vty, " neighbor %s shutdown\n", addr); - } - - /* bfd */ - if (peer->bfd_info) { - if (!peer_group_active(peer) || !g_peer->bfd_info) { - bgp_bfd_peer_config_write(vty, peer, addr); - } - } - - /* password */ - if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD)) - vty_out(vty, " neighbor %s password %s\n", addr, - peer->password); - - /* neighbor solo */ - if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)) { - if (!peer_group_active(peer)) { - vty_out(vty, " neighbor %s solo\n", addr); - } - } - - /* BGP port */ - if (peer->port != BGP_PORT_DEFAULT) { - vty_out(vty, " neighbor %s port %d\n", addr, peer->port); - } - - /* Local interface name */ - if (peer->ifname) { - vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname); - } - - /* passive */ - if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE)) - vty_out(vty, " neighbor %s passive\n", addr); - - /* ebgp-multihop */ - if (peer->sort != BGP_PEER_IBGP && peer->ttl != BGP_DEFAULT_TTL - && !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL)) { - if (!peer_group_active(peer) || g_peer->ttl != peer->ttl) { - vty_out(vty, " neighbor %s ebgp-multihop %d\n", addr, - peer->ttl); - } - } - - /* ttl-security hops */ - if (peer->gtsm_hops != 0) { - if (!peer_group_active(peer) - || g_peer->gtsm_hops != peer->gtsm_hops) { - vty_out(vty, " neighbor %s ttl-security hops %d\n", - addr, peer->gtsm_hops); - } - } - - /* disable-connected-check */ - if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK)) - vty_out(vty, " neighbor %s disable-connected-check\n", addr); - - /* enforce-first-as */ - if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS)) - vty_out(vty, " neighbor %s enforce-first-as\n", addr); - - /* update-source */ - if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) { - if (peer->update_source) - vty_out(vty, " neighbor %s update-source %s\n", addr, - sockunion2str(peer->update_source, buf, - SU_ADDRSTRLEN)); - else if (peer->update_if) - vty_out(vty, " neighbor %s update-source %s\n", addr, - peer->update_if); - } - - /* advertisement-interval */ - if (peergroup_flag_check(peer, PEER_FLAG_ROUTEADV)) - vty_out(vty, " neighbor %s advertisement-interval %u\n", addr, - peer->routeadv); - - /* timers */ - if (peergroup_flag_check(peer, PEER_FLAG_TIMER)) - vty_out(vty, " neighbor %s timers %u %u\n", addr, - peer->keepalive, peer->holdtime); - - /* timers connect */ - if (peergroup_flag_check(peer, PEER_FLAG_TIMER_CONNECT)) - vty_out(vty, " neighbor %s timers connect %u\n", addr, - peer->connect); - - /* capability dynamic */ - if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY)) - vty_out(vty, " neighbor %s capability dynamic\n", addr); - - /* capability extended-nexthop */ - if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_ENHE)) { - if (!peer->conf_if) { - if (CHECK_FLAG(peer->flags_invert, - PEER_FLAG_CAPABILITY_ENHE)) - vty_out(vty, - " no neighbor %s capability extended-nexthop\n", - addr); - else - vty_out(vty, - " neighbor %s capability extended-nexthop\n", - addr); - } - } - - /* dont-capability-negotiation */ - if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY)) - vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr); - - /* override-capability */ - if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY)) - vty_out(vty, " neighbor %s override-capability\n", addr); - - /* strict-capability-match */ - if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH)) - vty_out(vty, " neighbor %s strict-capability-match\n", addr); - - /* Sender side AS path loop detection. */ - if (peer->as_path_loop_detection) - vty_out(vty, " neighbor %s sender-as-path-loop-detection\n", - addr); -} - -/* BGP peer configuration display function. */ -static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, - struct peer *peer, afi_t afi, safi_t safi) -{ - struct peer *g_peer = NULL; - char *addr; - bool flag_scomm, flag_secomm, flag_slcomm; - - /* Skip dynamic neighbors. */ - if (peer_dynamic_neighbor(peer)) - return; - - if (peer->conf_if) - addr = peer->conf_if; - else - addr = peer->host; - - /************************************ - ****** Per AF to the neighbor ****** - ************************************/ - if (peer_group_active(peer)) { - g_peer = peer->group->conf; - - /* If the peer-group is active but peer is not, print a 'no - * activate' */ - if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) { - vty_out(vty, " no neighbor %s activate\n", addr); - } - - /* If the peer-group is not active but peer is, print an - 'activate' */ - else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) { - vty_out(vty, " neighbor %s activate\n", addr); - } - } else { - if (peer->afc[afi][safi]) { - if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { - if (bgp_flag_check(bgp, - BGP_FLAG_NO_DEFAULT_IPV4)) { - vty_out(vty, " neighbor %s activate\n", - addr); - } - } else - vty_out(vty, " neighbor %s activate\n", addr); - } else { - if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { - if (!bgp_flag_check(bgp, - BGP_FLAG_NO_DEFAULT_IPV4)) { - vty_out(vty, - " no neighbor %s activate\n", - addr); - } - } - } - } - - /* addpath TX knobs */ - if (peergroup_af_addpath_check(peer, afi, safi)) { - switch (peer->addpath_type[afi][safi]) { - case BGP_ADDPATH_ALL: - vty_out(vty, " neighbor %s addpath-tx-all-paths\n", - addr); - break; - case BGP_ADDPATH_BEST_PER_AS: - vty_out(vty, - " neighbor %s addpath-tx-bestpath-per-AS\n", - addr); - break; - case BGP_ADDPATH_MAX: - case BGP_ADDPATH_NONE: - break; - } - } - - /* ORF capability. */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_RM)) { - vty_out(vty, " neighbor %s capability orf prefix-list", addr); - - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_SM) - && peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_RM)) - vty_out(vty, " both"); - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_SM)) - vty_out(vty, " send"); - else - vty_out(vty, " receive"); - vty_out(vty, "\n"); - } - - /* BGP flag dampening. */ - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_DAMPENING)) - bgp_config_write_damp(vty, afi, safi); - - /* Route reflector client. */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REFLECTOR_CLIENT)) { - vty_out(vty, " neighbor %s route-reflector-client\n", addr); - } - - /* next-hop-self force */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_FORCE_NEXTHOP_SELF)) { - vty_out(vty, " neighbor %s next-hop-self force\n", addr); - } - - /* next-hop-self */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) { - vty_out(vty, " neighbor %s next-hop-self\n", addr); - } - - /* remove-private-AS */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) { - vty_out(vty, " neighbor %s remove-private-AS all replace-AS\n", - addr); - } - - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) { - vty_out(vty, " neighbor %s remove-private-AS replace-AS\n", - addr); - } - - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) { - vty_out(vty, " neighbor %s remove-private-AS all\n", addr); - } - - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS)) { - vty_out(vty, " neighbor %s remove-private-AS\n", addr); - } - - /* as-override */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) { - vty_out(vty, " neighbor %s as-override\n", addr); - } - - /* send-community print. */ - flag_scomm = peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_COMMUNITY); - flag_secomm = peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_EXT_COMMUNITY); - flag_slcomm = peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_LARGE_COMMUNITY); - - if (flag_scomm && flag_secomm && flag_slcomm) { - vty_out(vty, " no neighbor %s send-community all\n", addr); - } else { - if (flag_scomm) - vty_out(vty, " no neighbor %s send-community\n", addr); - if (flag_secomm) - vty_out(vty, - " no neighbor %s send-community extended\n", - addr); - - if (flag_slcomm) - vty_out(vty, " no neighbor %s send-community large\n", - addr); - } - - /* Default information */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_DEFAULT_ORIGINATE)) { - vty_out(vty, " neighbor %s default-originate", addr); - - if (peer->default_rmap[afi][safi].name) - vty_out(vty, " route-map %s", - peer->default_rmap[afi][safi].name); - - vty_out(vty, "\n"); - } - - /* Soft reconfiguration inbound. */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) { - vty_out(vty, " neighbor %s soft-reconfiguration inbound\n", - addr); - } - - /* maximum-prefix. */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) { - vty_out(vty, " neighbor %s maximum-prefix %" PRIu32, addr, - peer->pmax[afi][safi]); - - if (peer->pmax_threshold[afi][safi] - != MAXIMUM_PREFIX_THRESHOLD_DEFAULT) - vty_out(vty, " %u", peer->pmax_threshold[afi][safi]); - if (peer_af_flag_check(peer, afi, safi, - PEER_FLAG_MAX_PREFIX_WARNING)) - vty_out(vty, " warning-only"); - if (peer->pmax_restart[afi][safi]) - vty_out(vty, " restart %u", - peer->pmax_restart[afi][safi]); - - vty_out(vty, "\n"); - } - - /* Route server client. */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_RSERVER_CLIENT)) { - vty_out(vty, " neighbor %s route-server-client\n", addr); - } - - /* Nexthop-local unchanged. */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) { - vty_out(vty, " neighbor %s nexthop-local unchanged\n", addr); - } - - /* allowas-in <1-10> */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) { - if (peer_af_flag_check(peer, afi, safi, - PEER_FLAG_ALLOWAS_IN_ORIGIN)) { - vty_out(vty, " neighbor %s allowas-in origin\n", addr); - } else if (peer->allowas_in[afi][safi] == 3) { - vty_out(vty, " neighbor %s allowas-in\n", addr); - } else { - vty_out(vty, " neighbor %s allowas-in %d\n", addr, - peer->allowas_in[afi][safi]); - } - } - - /* weight */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT)) - vty_out(vty, " neighbor %s weight %lu\n", addr, - peer->weight[afi][safi]); - - /* Filter. */ - bgp_config_write_filter(vty, peer, afi, safi); - - /* atribute-unchanged. */ - if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED) - || (safi != SAFI_EVPN - && peer_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_UNCHANGED)) - || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) { - - if (!peer_group_active(peer) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_AS_PATH_UNCHANGED) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_UNCHANGED) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_MED_UNCHANGED)) { - - vty_out(vty, - " neighbor %s attribute-unchanged%s%s%s\n", - addr, - peer_af_flag_check(peer, afi, safi, - PEER_FLAG_AS_PATH_UNCHANGED) - ? " as-path" - : "", - peer_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_UNCHANGED) - ? " next-hop" - : "", - peer_af_flag_check(peer, afi, safi, - PEER_FLAG_MED_UNCHANGED) - ? " med" - : ""); - } - } -} - -/* Address family based peer configuration display. */ -static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi) -{ - struct peer *peer; - struct peer_group *group; - struct listnode *node, *nnode; - - - vty_frame(vty, " !\n address-family "); - if (afi == AFI_IP) { - if (safi == SAFI_UNICAST) - vty_frame(vty, "ipv4 unicast"); - else if (safi == SAFI_LABELED_UNICAST) - vty_frame(vty, "ipv4 labeled-unicast"); - else if (safi == SAFI_MULTICAST) - vty_frame(vty, "ipv4 multicast"); - else if (safi == SAFI_MPLS_VPN) - vty_frame(vty, "ipv4 vpn"); - else if (safi == SAFI_ENCAP) - vty_frame(vty, "ipv4 encap"); - else if (safi == SAFI_FLOWSPEC) - vty_frame(vty, "ipv4 flowspec"); - } else if (afi == AFI_IP6) { - if (safi == SAFI_UNICAST) - vty_frame(vty, "ipv6 unicast"); - else if (safi == SAFI_LABELED_UNICAST) - vty_frame(vty, "ipv6 labeled-unicast"); - else if (safi == SAFI_MULTICAST) - vty_frame(vty, "ipv6 multicast"); - else if (safi == SAFI_MPLS_VPN) - vty_frame(vty, "ipv6 vpn"); - else if (safi == SAFI_ENCAP) - vty_frame(vty, "ipv6 encap"); - else if (safi == SAFI_FLOWSPEC) - vty_frame(vty, "ipv6 flowspec"); - } else if (afi == AFI_L2VPN) { - if (safi == SAFI_EVPN) - vty_frame(vty, "l2vpn evpn"); - } - vty_frame(vty, "\n"); - - bgp_config_write_distance(vty, bgp, afi, safi); - - bgp_config_write_network(vty, bgp, afi, safi); - - bgp_config_write_redistribute(vty, bgp, afi, safi); - - for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) - bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi); - - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - /* Skip dynamic neighbors. */ - if (peer_dynamic_neighbor(peer)) - continue; - - /* Do not display doppelganger peers */ - if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) - bgp_config_write_peer_af(vty, bgp, peer, afi, safi); - } - - bgp_config_write_maxpaths(vty, bgp, afi, safi); - bgp_config_write_table_map(vty, bgp, afi, safi); - - if (safi == SAFI_EVPN) - bgp_config_write_evpn_info(vty, bgp, afi, safi); - - if (safi == SAFI_FLOWSPEC) - bgp_fs_config_write_pbr(vty, bgp, afi, safi); - - if (safi == SAFI_UNICAST) { - bgp_vpn_policy_config_write_afi(vty, bgp, afi); - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { - - vty_out(vty, " export vpn\n"); - } - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) { - - vty_out(vty, " import vpn\n"); - } - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_VRF_TO_VRF_IMPORT)) { - char *name; - - for (ALL_LIST_ELEMENTS_RO( - bgp->vpn_policy[afi].import_vrf, node, - name)) - vty_out(vty, " import vrf %s\n", name); - } - } - - vty_endframe(vty, " exit-address-family\n"); -} - -int bgp_config_write(struct vty *vty) -{ - struct bgp *bgp; - struct peer_group *group; - struct peer *peer; - struct listnode *node, *nnode; - struct listnode *mnode, *mnnode; - - if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER) - vty_out(vty, "bgp route-map delay-timer %u\n", - bm->rmap_update_timer); - - /* BGP configuration. */ - for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { - - /* skip all auto created vrf as they dont have user config */ - if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) - continue; - - /* Router bgp ASN */ - vty_out(vty, "router bgp %u", bgp->as); - - if (bgp->name) - vty_out(vty, " %s %s", - (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) - ? "view" : "vrf", bgp->name); - vty_out(vty, "\n"); - - /* BGP fast-external-failover. */ - if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) - vty_out(vty, " no bgp fast-external-failover\n"); - - /* BGP router ID. */ - if (bgp->router_id_static.s_addr != 0) - vty_out(vty, " bgp router-id %s\n", - inet_ntoa(bgp->router_id_static)); - - /* BGP log-neighbor-changes. */ - if (!!bgp_flag_check(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES) - != DFLT_BGP_LOG_NEIGHBOR_CHANGES) - vty_out(vty, " %sbgp log-neighbor-changes\n", - bgp_flag_check(bgp, - BGP_FLAG_LOG_NEIGHBOR_CHANGES) - ? "" - : "no "); - - /* BGP configuration. */ - if (bgp_flag_check(bgp, BGP_FLAG_ALWAYS_COMPARE_MED)) - vty_out(vty, " bgp always-compare-med\n"); - - /* RFC8212 default eBGP policy. */ - if (bgp->ebgp_requires_policy - == DEFAULT_EBGP_POLICY_ENABLED) - vty_out(vty, " bgp ebgp-requires-policy\n"); - - /* draft-ietf-idr-deprecate-as-set-confed-set */ - if (bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED) - vty_out(vty, " bgp reject-as-sets\n"); - - /* BGP default ipv4-unicast. */ - if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4)) - vty_out(vty, " no bgp default ipv4-unicast\n"); - - /* BGP default local-preference. */ - if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF) - vty_out(vty, " bgp default local-preference %u\n", - bgp->default_local_pref); - - /* BGP default show-hostname */ - if (!!bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME) - != DFLT_BGP_SHOW_HOSTNAME) - vty_out(vty, " %sbgp default show-hostname\n", - bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME) - ? "" - : "no "); - - /* BGP default subgroup-pkt-queue-max. */ - if (bgp->default_subgroup_pkt_queue_max - != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX) - vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n", - bgp->default_subgroup_pkt_queue_max); - - /* BGP client-to-client reflection. */ - if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT)) - vty_out(vty, " no bgp client-to-client reflection\n"); - - /* BGP cluster ID. */ - if (CHECK_FLAG(bgp->config, BGP_CONFIG_CLUSTER_ID)) - vty_out(vty, " bgp cluster-id %s\n", - inet_ntoa(bgp->cluster_id)); - - /* Disable ebgp connected nexthop check */ - if (bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) - vty_out(vty, - " bgp disable-ebgp-connected-route-check\n"); - - /* Confederation identifier*/ - if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) - vty_out(vty, " bgp confederation identifier %u\n", - bgp->confed_id); - - /* Confederation peer */ - if (bgp->confed_peers_cnt > 0) { - int i; - - vty_out(vty, " bgp confederation peers"); - - for (i = 0; i < bgp->confed_peers_cnt; i++) - vty_out(vty, " %u", bgp->confed_peers[i]); - - vty_out(vty, "\n"); - } - - /* BGP deterministic-med. */ - if (!!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED) - != DFLT_BGP_DETERMINISTIC_MED) - vty_out(vty, " %sbgp deterministic-med\n", - bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED) - ? "" - : "no "); - - /* BGP update-delay. */ - bgp_config_write_update_delay(vty, bgp); - - if (bgp->v_maxmed_onstartup - != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) { - vty_out(vty, " bgp max-med on-startup %u", - bgp->v_maxmed_onstartup); - if (bgp->maxmed_onstartup_value - != BGP_MAXMED_VALUE_DEFAULT) - vty_out(vty, " %u", - bgp->maxmed_onstartup_value); - vty_out(vty, "\n"); - } - if (bgp->v_maxmed_admin != BGP_MAXMED_ADMIN_UNCONFIGURED) { - vty_out(vty, " bgp max-med administrative"); - if (bgp->maxmed_admin_value != BGP_MAXMED_VALUE_DEFAULT) - vty_out(vty, " %u", bgp->maxmed_admin_value); - vty_out(vty, "\n"); - } - - /* write quanta */ - bgp_config_write_wpkt_quanta(vty, bgp); - /* read quanta */ - bgp_config_write_rpkt_quanta(vty, bgp); - - /* coalesce time */ - bgp_config_write_coalesce_time(vty, bgp); - - /* BGP graceful-restart. */ - if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME) - vty_out(vty, - " bgp graceful-restart stalepath-time %u\n", - bgp->stalepath_time); - if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME) - vty_out(vty, " bgp graceful-restart restart-time %u\n", - bgp->restart_time); - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) - vty_out(vty, " bgp graceful-restart\n"); - - /* BGP graceful-shutdown */ - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) - vty_out(vty, " bgp graceful-shutdown\n"); - - /* BGP graceful-restart Preserve State F bit. */ - if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD)) - vty_out(vty, - " bgp graceful-restart preserve-fw-state\n"); - - /* BGP bestpath method. */ - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE)) - vty_out(vty, " bgp bestpath as-path ignore\n"); - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_CONFED)) - vty_out(vty, " bgp bestpath as-path confed\n"); - - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { - if (bgp_flag_check(bgp, - BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { - vty_out(vty, - " bgp bestpath as-path multipath-relax as-set\n"); - } else { - vty_out(vty, - " bgp bestpath as-path multipath-relax\n"); - } - } - - if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { - vty_out(vty, - " bgp route-reflector allow-outbound-policy\n"); - } - if (bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID)) - vty_out(vty, " bgp bestpath compare-routerid\n"); - if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED) - || bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) { - vty_out(vty, " bgp bestpath med"); - if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED)) - vty_out(vty, " confed"); - if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) - vty_out(vty, " missing-as-worst"); - vty_out(vty, "\n"); - } - - /* BGP network import check. */ - if (!!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) - != DFLT_BGP_IMPORT_CHECK) - vty_out(vty, " %sbgp network import-check\n", - bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) - ? "" - : "no "); - - /* BGP timers configuration. */ - if (bgp->default_keepalive != BGP_DEFAULT_KEEPALIVE - && bgp->default_holdtime != BGP_DEFAULT_HOLDTIME) - vty_out(vty, " timers bgp %u %u\n", - bgp->default_keepalive, bgp->default_holdtime); - - /* peer-group */ - for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) { - bgp_config_write_peer_global(vty, bgp, group->conf); - } - - /* Normal neighbor configuration. */ - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) - bgp_config_write_peer_global(vty, bgp, peer); - } - - /* listen range and limit for dynamic BGP neighbors */ - bgp_config_write_listen(vty, bgp); - - /* - * BGP default autoshutdown neighbors - * - * This must be placed after any peer and peer-group - * configuration, to avoid setting all peers to shutdown after - * a daemon restart, which is undesired behavior. (see #2286) - */ - if (bgp->autoshutdown) - vty_out(vty, " bgp default shutdown\n"); - - /* IPv4 unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); - - /* IPv4 multicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST); - - /* IPv4 labeled-unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST); - - /* IPv4 VPN configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN); - - /* ENCAPv4 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP); - - /* FLOWSPEC v4 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC); - - /* IPv6 unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST); - - /* IPv6 multicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST); - - /* IPv6 labeled-unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, - SAFI_LABELED_UNICAST); - - /* IPv6 VPN configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN); - - /* ENCAPv6 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP); - - /* FLOWSPEC v6 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC); - - /* EVPN configuration. */ - bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN); - - hook_call(bgp_inst_config_write, bgp, vty); - -#if ENABLE_BGP_VNC - bgp_rfapi_cfg_write(vty, bgp); -#endif - - vty_out(vty, "!\n"); - } - return 0; -} - void bgp_master_init(struct thread_master *master, const int buffer_size) { qobj_init(); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 568438b0ce..7d81579009 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -457,6 +457,7 @@ struct bgp { /* BGP default timer. */ uint32_t default_holdtime; uint32_t default_keepalive; + uint32_t default_connect_retry; /* BGP graceful restart */ uint32_t restart_time; @@ -1429,13 +1430,17 @@ struct bgp_nlri { #define BGP_EVENTS_MAX 15 /* BGP timers default value. */ -/* note: the DFLT_ ones depend on compile-time "defaults" selection */ #define BGP_INIT_START_TIMER 1 -#define BGP_DEFAULT_HOLDTIME DFLT_BGP_HOLDTIME -#define BGP_DEFAULT_KEEPALIVE DFLT_BGP_KEEPALIVE +/* The following 3 are RFC defaults that are overridden in bgp_vty.c with + * version-/profile-specific values. The values here do not matter, they only + * exist to provide a clear layering separation between core and CLI. + */ +#define BGP_DEFAULT_HOLDTIME 180 +#define BGP_DEFAULT_KEEPALIVE 60 +#define BGP_DEFAULT_CONNECT_RETRY 120 + #define BGP_DEFAULT_EBGP_ROUTEADV 0 #define BGP_DEFAULT_IBGP_ROUTEADV 0 -#define BGP_DEFAULT_CONNECT_RETRY DFLT_BGP_TIMERS_CONNECT /* BGP default local preference. */ #define BGP_DEFAULT_LOCAL_PREF 100 @@ -1480,6 +1485,7 @@ enum bgp_clear_type { /* BGP error codes. */ #define BGP_SUCCESS 0 +#define BGP_CREATED 1 #define BGP_ERR_INVALID_VALUE -1 #define BGP_ERR_INVALID_FLAG -2 #define BGP_ERR_INVALID_AS -3 @@ -1626,7 +1632,8 @@ extern int bgp_confederation_peers_check(struct bgp *, as_t); extern int bgp_confederation_peers_add(struct bgp *, as_t); extern int bgp_confederation_peers_remove(struct bgp *, as_t); -extern int bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime); +extern int bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime, + uint32_t connect_retry); extern int bgp_timers_unset(struct bgp *); extern int bgp_default_local_preference_set(struct bgp *, uint32_t); diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index b97c8c3030..352f5e8328 100644 --- a/bgpd/rfapi/vnc_export_bgp.c +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -78,7 +78,7 @@ static void encap_attr_export_ce(struct attr *new, struct attr *orig, * Make "new" a ghost attr copy of "orig" */ memset(new, 0, sizeof(struct attr)); - bgp_attr_dup(new, orig); + *new = *orig; /* * Set nexthop @@ -616,7 +616,7 @@ encap_attr_export(struct attr *new, struct attr *orig, * Make "new" a ghost attr copy of "orig" */ memset(new, 0, sizeof(struct attr)); - bgp_attr_dup(new, orig); + *new = *orig; /* * Set nexthop diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index 6e8969ad18..ba6ef14257 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -356,7 +356,8 @@ static int process_unicast_route(struct bgp *bgp, /* in */ * all of the possible returns above. */ memset(&hattr, 0, sizeof(struct attr)); - bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */ + /* hattr becomes a ghost attr */ + hattr = *attr; if (rmap) { struct bgp_path_info info; @@ -798,7 +799,8 @@ static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp, * all of the possible returns above. */ memset(&hattr, 0, sizeof(struct attr)); - bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */ + /* hattr becomes a ghost attr */ + hattr = *attr; if (rmap) { struct bgp_path_info info; @@ -1000,7 +1002,8 @@ vnc_import_bgp_add_route_mode_nvegroup(struct bgp *bgp, struct prefix *prefix, * all of the possible returns above. */ memset(&hattr, 0, sizeof(struct attr)); - bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */ + /* hattr becomes a ghost attr */ + hattr = *attr; if (rmap) { struct bgp_path_info path; @@ -1779,7 +1782,7 @@ static void vnc_import_bgp_exterior_add_route_it( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (info->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { new_attr.local_pref = @@ -2104,7 +2107,7 @@ void vnc_import_bgp_exterior_add_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { @@ -2240,7 +2243,7 @@ void vnc_import_bgp_exterior_add_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { @@ -2360,7 +2363,7 @@ void vnc_import_bgp_exterior_add_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { @@ -2550,7 +2553,7 @@ void vnc_import_bgp_exterior_del_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi->attr); + new_attr = *bpi->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { diff --git a/configure.ac b/configure.ac index 6147ebf0d8..34c8bb6932 100755 --- a/configure.ac +++ b/configure.ac @@ -643,6 +643,7 @@ fi if test "${enable_datacenter}" = "yes" ; then AC_DEFINE([HAVE_DATACENTER], [1], [Compile extensions for a DataCenter]) + AC_MSG_WARN([The --enable-datacenter compile time option is deprecated. Please modify the init script to pass -F datacenter to the daemons instead.]) DFLT_NAME="datacenter" else DFLT_NAME="traditional" @@ -1060,6 +1061,8 @@ FRR_INCLUDES dnl V6 headers are checked below, after we check for v6 +is_linux=false + AC_MSG_CHECKING([which operating system interface to use]) case "$host_os" in sunos* | solaris2*) @@ -1088,6 +1091,8 @@ case "$host_os" in dnl how to fix it but no real progress on implementation dnl when they fix it, remove this AC_DEFINE([IPV6_MINHOPCOUNT], [73], [Linux ipv6 Min Hop Count]) + + is_linux=true ;; openbsd*) AC_MSG_RESULT([OpenBSD]) @@ -1115,6 +1120,7 @@ case "$host_os" in ;; esac AM_CONDITIONAL([SOLARIS], [test "${SOLARIS}" = "solaris"]) +AM_CONDITIONAL([LINUX], [${is_linux}]) AC_SYS_LARGEFILE diff --git a/debian/frr.install b/debian/frr.install index 09bddf0fc6..5917c0da84 100644 --- a/debian/frr.install +++ b/debian/frr.install @@ -8,6 +8,7 @@ usr/lib/frr/*.sh usr/lib/frr/*d usr/lib/frr/watchfrr usr/lib/frr/zebra +usr/lib/*/frr/modules/zebra_cumulus_mlag.so usr/lib/*/frr/modules/zebra_irdp.so usr/lib/*/frr/modules/zebra_fpm.so usr/lib/*/frr/modules/bgpd_bmp.so diff --git a/defaults.h b/defaults.h deleted file mode 100644 index fd38aaed70..0000000000 --- a/defaults.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * FRR switchable defaults. - * Copyright (C) 2017 David Lamparter for NetDEF, Inc. - * - * This file is part of FRRouting (FRR). - * - * FRR is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2, or (at your option) any later version. - * - * FRR is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _FRR_DEFAULTS_H -#define _FRR_DEFAULTS_H - -#include "config.h" - -#ifdef HAVE_DATACENTER - -#define DFLT_BGP_IMPORT_CHECK 1 -#define DFLT_BGP_TIMERS_CONNECT 10 -#define DFLT_BGP_HOLDTIME 9 -#define DFLT_BGP_KEEPALIVE 3 -#define DFLT_BGP_LOG_NEIGHBOR_CHANGES 1 -#define DFLT_BGP_SHOW_HOSTNAME 1 -#define DFLT_BGP_DETERMINISTIC_MED 1 - -#define DFLT_OSPF_LOG_ADJACENCY_CHANGES 1 -#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES 1 - -#else /* !HAVE_DATACENTER */ - -#define DFLT_BGP_IMPORT_CHECK 0 -#define DFLT_BGP_TIMERS_CONNECT 120 -#define DFLT_BGP_HOLDTIME 180 -#define DFLT_BGP_KEEPALIVE 60 -#define DFLT_BGP_LOG_NEIGHBOR_CHANGES 0 -#define DFLT_BGP_SHOW_HOSTNAME 0 -#define DFLT_BGP_DETERMINISTIC_MED 0 - -#define DFLT_OSPF_LOG_ADJACENCY_CHANGES 0 -#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES 0 - -#endif /* !HAVE_DATACENTER */ - -#endif /* _FRR_DEFAULTS_H */ diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index 6eef7532b3..40378f0219 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -293,6 +293,11 @@ Code submitted to FRR must be signed off. We have the same requirements for using the signed-off-by process as the Linux kernel. In short, you must include a ``Signed-off-by`` tag in every patch. +An easy way to do this is to use ``git commit -s`` where ``-s`` will automatically +append a signed-off line to the end of your commit message. Also, if you commit +and forgot to add the line you can use ``git commit --amend -s`` to add the +signed-off line to the last commit. + ``Signed-off-by`` is a developer's certification that they have the right to submit the patch for inclusion into the project. It is an agreement to the :ref:`Developer's Certificate of Origin <developers-certificate-of-origin>`. diff --git a/doc/user/basic.rst b/doc/user/basic.rst index f946c37a73..edcfce45ad 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -286,6 +286,9 @@ Below is a sample configuration file for the zebra daemon. ! ! Zebra configuration file ! + frr version 6.0 + frr defaults traditional + ! hostname Router password zebra enable password zebra @@ -307,6 +310,137 @@ If a comment character is not the first character of the word, it's a normal character. So in the above example ``!`` will not be regarded as a comment and the password is set to ``zebra!password``. + +Configuration versioning, profiles and upgrade behavior +------------------------------------------------------- + +All |PACKAGE_NAME| daemons share a mechanism to specify a configuration profile +and version for loading and saving configuration. Specific configuration +settings take different default values depending on the selected profile and +version. + +While the profile can be selected by user configuration and will remain over +upgrades, |PACKAGE_NAME| will always write configurations using its current +version. This means that, after upgrading, a ``write file`` may write out a +slightly different configuration than what was read in. + +Since the previous configuration is loaded with its version's defaults, but +the new configuration is written with the new defaults, any default that +changed between versions will result in an appropriate configuration entry +being written out. **FRRouting configuration is sticky, staying consistent +over upgrades.** Changed defaults will only affect new configuration. + +Note that the loaded version persists into interactive configuration +sessions. Commands executed in an interactive configuration session are +no different from configuration loaded at startup. This means that when, +say, you configure a new BGP peer, the defaults used for configuration +are the ones selected by the last ``frr version`` command. + +.. warning:: + + Saving the configuration does not bump the daemons forward to use the new + version for their defaults, but restarting them will, since they will then + apply the new ``frr version`` command that was written out. Manually + execute the ``frr version`` command in ``show running-config`` to avoid + this intermediate state. + +This is visible in ``show running-config``: + +.. code-block:: frr + + Current configuration: + ! + ! loaded from 6.0 + frr version 6.1-dev + frr defaults traditional + ! + +If you save and then restart with this configuration, the old defaults will +no longer apply. Similarly, you could execute ``frr version 6.1-dev``, causing +the new defaults to apply and the ``loaded from 6.0`` comment to disappear. + + +Profiles +^^^^^^^^ + +|PACKAGE_NAME| provides configuration profiles to adapt its default settings +to various usage scenarios. Currently, the following profiles are +implemented: + +* ``traditional`` - reflects defaults adhering mostly to IETF standards or + common practices in wide-area internet routing. +* ``datacenter`` - reflects a single administrative domain with intradomain + links using aggressive timers. + +Your distribution/installation may pre-set a profile through the ``-F`` command +line option on all daemons. All daemons must be configured for the same +profile. The value specified on the command line is only a pre-set and any +``frr defaults`` statement in the configuration will take precedence. + +.. note:: + + The profile must be the same across all daemons. Mismatches may result + in undefined behavior. + +You can freely switch between profiles without causing any interruption or +configuration changes. All settings remain at their previous values, and +``show running-configuration`` output will have new output listing the previous +default values as explicit configuration. New configuration, e.g. adding a +BGP peer, will use the new defaults. To apply the new defaults for existing +configuration, the previously-invisible old defaults that are now shown must +be removed from the configuration. + + +Upgrade practices for interactive configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you configure |PACKAGE_NAME| interactively and use the configuration +writing functionality to make changes persistent, the following +recommendations apply in regards to upgrades: + +1. Skipping major versions should generally work but is still inadvisable. + To avoid unneeded issue, upgrade one major version at a time and write + out the configuration after each update. + +2. After installing a new |PACKAGE_NAME| version, check the configuration + for differences against your old configuration. If any defaults changed + that affect your setup, lines may appear or disappear. If a new line + appears, it was previously the default (or not supported) and is now + neccessary to retain previous behavior. If a line disappears, it + previously wasn't the default, but now is, so it is no longer necessary. + +3. Check the log files for deprecation warnings by using ``grep -i deprecat``. + +4. After completing each upgrade, save the configuration and either restart + |PACKAGE_NAME| or execute ``frr version <CURRENT>`` to ensure defaults of + the new version are fully applied. + + +Upgrade practices for autogenerated configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When using |PACKAGE_NAME| with generated configurations (e.g. Ansible, +Puppet, etc.), upgrade considerations differ somewhat: + +1. Always write out a ``frr version`` statement in the configurations you + generate. This ensures that defaults are applied consistently. + +2. Try to not run more distinct versions of |PACKAGE_NAME| than necessary. + Each version may need to be checked individually. If running a mix of + older and newer installations, use the oldest version for the + ``frr version`` statement. + +3. When rolling out upgrades, generate a configuration as usual with the old + version identifier and load it. Check for any differences or deprecation + warnings. If there are differences in the configuration, propagate these + back to the configuration generator to minimize relying on actual default + values. + +4. After the last installation of an old version is removed, change the + configuration generation to a newer ``frr version`` as appropriate. Perform + the same checks as when rolling out upgrades. + + .. _terminal-mode-commands: Terminal Mode Commands diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 525a31d486..b3fc7f15a6 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -94,6 +94,10 @@ BFDd Commands Show status for a specific BFD peer. +.. index:: show bfd [vrf NAME] peers brief [json] +.. clicmd:: show bfd [vrf NAME] peers brief [json] + + Show all configured BFD peers information and current status in brief. .. _bfd-peer-config: @@ -340,11 +344,14 @@ You can inspect the current BFD peer status with the following commands: Uptime: 1 minute(s), 51 second(s) Diagnostics: ok Remote diagnostics: ok + Peer Type: dynamic Local timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: disabled Remote timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: 50ms @@ -357,11 +364,14 @@ You can inspect the current BFD peer status with the following commands: Uptime: 1 minute(s), 53 second(s) Diagnostics: ok Remote diagnostics: ok + Peer Type: configured Local timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: disabled Remote timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: 50ms @@ -376,17 +386,31 @@ You can inspect the current BFD peer status with the following commands: Uptime: 3 minute(s), 4 second(s) Diagnostics: ok Remote diagnostics: ok + Peer Type: dynamic Local timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: disabled Remote timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: 50ms frr# show bfd peer 192.168.0.1 json - {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50} + {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"detect-multiplier":3,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50,"remote-detect-multiplier":3,"peer-type":"dynamic"} + + +You can inspect the current BFD peer status in brief with the following commands: + +:: + + frr# show bfd peers brief + Session count: 1 + SessionId LocalAddress PeerAddress Status + ========= ============ =========== ====== + 1 192.168.0.1 192.168.0.2 up You can also inspect peer session counters with the following commands: @@ -425,3 +449,30 @@ You can also inspect peer session counters with the following commands: frr# show bfd peer 192.168.0.1 counters json {"multihop":false,"peer":"192.168.0.1","control-packet-input":348,"control-packet-output":685,"echo-packet-input":6815,"echo-packet-output":6816,"session-up":1,"session-down":0,"zebra-notifications":4} + +You can also clear packet counters per session with the following commands, only the packet counters will be reset: + +:: + + frr# clear bfd peers counters + + frr# show bfd peers counters + BFD Peers: + peer 192.168.2.1 interface r2-eth2 + Control packet input: 0 packets + Control packet output: 0 packets + Echo packet input: 0 packets + Echo packet output: 0 packets + Session up events: 1 + Session down events: 0 + Zebra notifications: 2 + + peer 192.168.0.1 + Control packet input: 0 packets + Control packet output: 0 packets + Echo packet input: 0 packets + Echo packet output: 0 packets + Session up events: 1 + Session down events: 0 + Zebra notifications: 4 + diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index de75c12e62..97ce036f52 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -417,7 +417,7 @@ Require policy on EBGP This command requires incoming and outgoing filters to be applied for eBGP sessions. Without the incoming filter, no routes will be accepted. Without the outgoing filter, no routes will be announced. Reject routes with AS_SET or AS_CONFED_SET types -------------------------------- +------------------------------------------------ .. index:: [no] bgp reject-as-sets .. clicmd:: [no] bgp reject-as-sets diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 68e460748c..68659fbf50 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -39,7 +39,7 @@ listing of ECMP nexthops used to forward packets for when a pbr-map is matched. sub-mode where you can specify individual nexthops. To exit this mode type exit or end as per normal conventions for leaving a sub-mode. -.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] +.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] [label LABELS] Create a v4 or v6 nexthop. All normal rules for creating nexthops that you are used to are allowed here. The syntax was intentionally kept the same as @@ -115,6 +115,12 @@ end destination. Not supported with NETNS VRF backend. +.. clicmd:: show pbr map [NAME] [detail] + + Display pbr maps either all or by ``NAME``. If ``detail`` is set, it will + give information about the rules unique ID used internally and some extra + debugging information about install state for the nexthop/nexthop group. + .. _pbr-policy: PBR Policy diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 4f9c573a24..1e2b697d18 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -197,8 +197,8 @@ is in a vrf, enter the interface command with the vrf keyword at the end. Set the pim hello and hold interval for a interface. -.. index:: ip pim sm -.. clicmd:: ip pim sm +.. index:: ip pim +.. clicmd:: ip pim Tell pim that we would like to use this interface to form pim neighbors over. Please note that this command does not enable the reception of IGMP @@ -252,6 +252,13 @@ is in a vrf, enter the interface command with the vrf keyword at the end. 10 deciseconds. 'no' form of this command is used to to configure back to the default value. +.. index:: ip mroute INTERFACE A.B.C.D [A.B.C.D] +.. clicmd:: ip mroute INTERFACE A.B.C.D [A.B.C.D] + + Set a static multicast route for a traffic coming on the current interface to + be forwarded on the given interface if the traffic matches the group address + and optionally the source address. + .. _pim-multicast-rib-insertion: PIM Multicast RIB insertion: diff --git a/eigrpd/eigrp_interface.c b/eigrpd/eigrp_interface.c index 6294c0dd0f..ece0b4b0c4 100644 --- a/eigrpd/eigrp_interface.c +++ b/eigrpd/eigrp_interface.c @@ -99,6 +99,9 @@ struct eigrp_interface *eigrp_if_new(struct eigrp *eigrp, struct interface *ifp, ei->params.auth_type = EIGRP_AUTH_TYPE_NONE; ei->params.auth_keychain = NULL; + ei->curr_bandwidth = ifp->bandwidth; + ei->curr_mtu = ifp->mtu; + return ei; } @@ -139,45 +142,40 @@ static int eigrp_ifp_create(struct interface *ifp) static int eigrp_ifp_up(struct interface *ifp) { - /* Interface is already up. */ - if (if_is_operative(ifp)) { - /* Temporarily keep ifp values. */ - struct interface if_tmp; - memcpy(&if_tmp, ifp, sizeof(struct interface)); + struct eigrp_interface *ei = ifp->info; - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug("Zebra: Interface[%s] state update.", - ifp->name); + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to up.", + ifp->name); - if (if_tmp.bandwidth != ifp->bandwidth) { - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: Interface[%s] bandwidth change %d -> %d.", - ifp->name, if_tmp.bandwidth, - ifp->bandwidth); + if (!ei) + return 0; - // eigrp_if_recalculate_output_cost (ifp); - } + if (ei->curr_bandwidth != ifp->bandwidth) { + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: Interface[%s] bandwidth change %d -> %d.", + ifp->name, ei->curr_bandwidth, + ifp->bandwidth); - if (if_tmp.mtu != ifp->mtu) { - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: Interface[%s] MTU change %u -> %u.", - ifp->name, if_tmp.mtu, ifp->mtu); + ei->curr_bandwidth = ifp->bandwidth; + // eigrp_if_recalculate_output_cost (ifp); + } - /* Must reset the interface (simulate down/up) when MTU - * changes. */ - eigrp_if_reset(ifp); - } + if (ei->curr_mtu != ifp->mtu) { + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: Interface[%s] MTU change %u -> %u.", + ifp->name, ei->curr_mtu, ifp->mtu); + + ei->curr_mtu = ifp->mtu; + /* Must reset the interface (simulate down/up) when MTU + * changes. */ + eigrp_if_reset(ifp); return 0; } - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug("Zebra: Interface[%s] state change to up.", - ifp->name); - - if (ifp->info) - eigrp_if_up(ifp->info); + eigrp_if_up(ifp->info); return 0; } diff --git a/eigrpd/eigrp_structs.h b/eigrpd/eigrp_structs.h index e50858f071..82bddaaae3 100644 --- a/eigrpd/eigrp_structs.h +++ b/eigrpd/eigrp_structs.h @@ -176,6 +176,8 @@ struct eigrp_interface { /* To which multicast groups do we currently belong? */ + uint32_t curr_bandwidth; + uint32_t curr_mtu; uint8_t multicast_memberships; diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 7e79fdea15..364441f79d 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -29,7 +29,6 @@ #include "command.h" #include "vty.h" #include "memory.h" -#include "memory_vty.h" #include "stream.h" #include "if.h" #include "privs.h" diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 820cfaa426..d14704b4ee 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -1550,9 +1550,13 @@ int lib_interface_isis_destroy(enum nb_event event, circuit = nb_running_unset_entry(dnode); if (!circuit) return NB_ERR_INCONSISTENCY; - if (circuit->state == C_STATE_UP || circuit->state == C_STATE_CONF) - isis_csm_state_change(ISIS_DISABLE, circuit, circuit->area); + /* disable both AFs for this circuit. this will also update the + * CSM state by sending an ISIS_DISABLED signal. If there is no + * area associated to the circuit there is nothing to do + */ + if (circuit->area) + isis_circuit_af_set(circuit, false, false); return NB_OK; } diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index b4c699ccbb..630264768f 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -184,7 +184,7 @@ void isis_zebra_route_add_route(struct prefix *prefix, break; api_nh = &api.nexthops[count]; if (fabricd) - api_nh->onlink = true; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); api_nh->vrf_id = VRF_DEFAULT; switch (nexthop->family) { diff --git a/lib/command.c b/lib/command.c index 9238ae412a..d2145d9f5a 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1575,18 +1575,6 @@ DEFUN (show_version, return CMD_SUCCESS; } -/* "Set" version ... ignore version tags */ -DEFUN (frr_version_defaults, - frr_version_defaults_cmd, - "frr <version|defaults> LINE...", - "FRRouting global parameters\n" - "version configuration was written by\n" - "set of configuration defaults used\n" - "version string\n") -{ - return CMD_SUCCESS; -} - /* Help display function for all node. */ DEFUN (config_help, config_help_cmd, @@ -1721,8 +1709,10 @@ static int vty_write_config(struct vty *vty) vty_out(vty, "!\n"); } + if (strcmp(frr_defaults_version(), FRR_VER_SHORT)) + vty_out(vty, "! loaded from %s\n", frr_defaults_version()); vty_out(vty, "frr version %s\n", FRR_VER_SHORT); - vty_out(vty, "frr defaults %s\n", DFLT_NAME); + vty_out(vty, "frr defaults %s\n", frr_defaults_profile()); vty_out(vty, "!\n"); for (i = 0; i < vector_active(cmdvec); i++) @@ -2941,7 +2931,6 @@ void cmd_init(int terminal) install_element(CONFIG_NODE, &no_hostname_cmd); install_element(CONFIG_NODE, &domainname_cmd); install_element(CONFIG_NODE, &no_domainname_cmd); - install_element(CONFIG_NODE, &frr_version_defaults_cmd); if (terminal > 0) { install_element(CONFIG_NODE, &debug_memstats_cmd); diff --git a/lib/defaults.c b/lib/defaults.c new file mode 100644 index 0000000000..71ccc73cc6 --- /dev/null +++ b/lib/defaults.c @@ -0,0 +1,229 @@ +/* + * FRR switchable defaults. + * Copyright (c) 2017-2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <zebra.h> + +#include "defaults.h" +#include "version.h" + +static char df_version[128] = FRR_VER_SHORT, df_profile[128] = DFLT_NAME; +static struct frr_default *dflt_first = NULL, **dflt_next = &dflt_first; + +/* these are global for all FRR daemons. they have to be, since we write an + * integrated config with the same value for all daemons. + */ +const char *frr_defaults_profiles[] = { + "traditional", + "datacenter", + NULL, +}; + +static int version_value(int ch) +{ + /* non-ASCII shouldn't happen */ + if (ch < 0 || ch >= 128) + return 2; + + /* ~foo sorts older than nothing */ + if (ch == '~') + return 0; + if (ch == '\0') + return 1; + if (isalpha(ch)) + return 0x100 + tolower(ch); + + /* punctuation and digits (and everything else) */ + return 0x200 + ch; +} + +int frr_version_cmp(const char *aa, const char *bb) +{ + const char *apos = aa, *bpos = bb; + + /* || is correct, we won't scan past the end of a string since that + * doesn't compare equal to anything else */ + while (apos[0] || bpos[0]) { + if (isdigit((unsigned char)apos[0]) && + isdigit((unsigned char)bpos[0])) { + unsigned long av, bv; + char *aend = NULL, *bend = NULL; + + av = strtoul(apos, &aend, 10); + bv = strtoul(bpos, &bend, 10); + if (av < bv) + return -1; + if (av > bv) + return 1; + + apos = aend; + bpos = bend; + continue; + } + + int a = version_value(*apos++); + int b = version_value(*bpos++); + + if (a < b) + return -1; + if (a > b) + return 1; + } + return 0; +} + +static void frr_default_apply_one(struct frr_default *dflt, bool check); + +void frr_default_add(struct frr_default *dflt) +{ + dflt->next = NULL; + *dflt_next = dflt; + dflt_next = &dflt->next; + + frr_default_apply_one(dflt, true); +} + +static bool frr_match_version(const char *name, const char *vspec, + const char *version, bool check) +{ + int cmp; + static struct spec { + const char *str; + bool dir, eq; + } *s, specs[] = { + {"<=", -1, 1}, + {">=", 1, 1}, + {"==", 0, 1}, + {"<", -1, 0}, + {">", 1, 0}, + {"=", 0, 1}, + {NULL, 0, 0}, + }; + + if (!vspec) + /* NULL = all versions */ + return true; + + for (s = specs; s->str; s++) + if (!strncmp(s->str, vspec, strlen(s->str))) + break; + if (!s->str) { + if (check) + fprintf(stderr, "invalid version specifier for %s: %s", + name, vspec); + /* invalid version spec, never matches */ + return false; + } + + vspec += strlen(s->str); + while (isspace((unsigned char)*vspec)) + vspec++; + + cmp = frr_version_cmp(version, vspec); + if (cmp == s->dir || (s->eq && cmp == 0)) + return true; + + return false; +} + +static void frr_default_apply_one(struct frr_default *dflt, bool check) +{ + struct frr_default_entry *entry = dflt->entries; + struct frr_default_entry *dfltentry = NULL, *saveentry = NULL; + + for (; entry->match_version || entry->match_profile; entry++) { + if (entry->match_profile + && strcmp(entry->match_profile, df_profile)) + continue; + + if (!dfltentry && frr_match_version(dflt->name, + entry->match_version, df_version, check)) + dfltentry = entry; + if (!saveentry && frr_match_version(dflt->name, + entry->match_version, FRR_VER_SHORT, check)) + saveentry = entry; + + if (dfltentry && saveentry && !check) + break; + } + /* found default or arrived at last entry that has NULL,NULL spec */ + + if (!dfltentry) + dfltentry = entry; + if (!saveentry) + saveentry = entry; + + if (dflt->dflt_bool) + *dflt->dflt_bool = dfltentry->val_bool; + if (dflt->dflt_str) + *dflt->dflt_str = dfltentry->val_str; + if (dflt->dflt_long) + *dflt->dflt_long = dfltentry->val_long; + if (dflt->dflt_ulong) + *dflt->dflt_ulong = dfltentry->val_ulong; + if (dflt->dflt_float) + *dflt->dflt_float = dfltentry->val_float; + if (dflt->save_bool) + *dflt->save_bool = saveentry->val_bool; + if (dflt->save_str) + *dflt->save_str = saveentry->val_str; + if (dflt->save_long) + *dflt->save_long = saveentry->val_long; + if (dflt->save_ulong) + *dflt->save_ulong = saveentry->val_ulong; + if (dflt->save_float) + *dflt->save_float = saveentry->val_float; +} + +void frr_defaults_apply(void) +{ + struct frr_default *dflt; + + for (dflt = dflt_first; dflt; dflt = dflt->next) + frr_default_apply_one(dflt, false); +} + +bool frr_defaults_profile_valid(const char *profile) +{ + const char **p; + + for (p = frr_defaults_profiles; *p; p++) + if (!strcmp(profile, *p)) + return true; + return false; +} + +const char *frr_defaults_version(void) +{ + return df_version; +} + +const char *frr_defaults_profile(void) +{ + return df_profile; +} + +void frr_defaults_version_set(const char *version) +{ + strlcpy(df_version, version, sizeof(df_version)); + frr_defaults_apply(); +} + +void frr_defaults_profile_set(const char *profile) +{ + strlcpy(df_profile, profile, sizeof(df_profile)); + frr_defaults_apply(); +} diff --git a/lib/defaults.h b/lib/defaults.h new file mode 100644 index 0000000000..7cdd18120e --- /dev/null +++ b/lib/defaults.h @@ -0,0 +1,138 @@ +/* + * FRR switchable defaults. + * Copyright (C) 2017-2019 David Lamparter for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_DEFAULTS_H +#define _FRR_DEFAULTS_H + +#include <stdbool.h> + +#include "compiler.h" + +/* frr_default wraps information about a default that has different + * values depending on FRR version or default-set + * + * frr_default_entry describes one match rule and the resulting value; + * entries are evaluated in order and the first matching is used. + * + * If both match_version and match_profile are specified, they must both + * match. A NULL value matches everything. + */ +struct frr_default_entry { + /* syntax: "(<|<=|==|>=|>) [whitespace] version", e.g. + * ">= 6.1-dev" "<6.0" + */ + const char *match_version; + /* exact profile string to compare against */ + const char *match_profile; + + /* value to use */ + bool val_bool; + const char *val_str; + long val_long; + unsigned long val_ulong; + float val_float; +}; + +/* one struct frr_default exists for each malleable default value */ +struct frr_default { + struct frr_default *next; + + /* for UI/debug use */ + const char *name; + + /* the following two sets of variables differ because the written + * config always targets the *current* FRR version + * + * e.g. if you load a config that has "frr version 5.0" on 6.0 + * *dflt_long => set to the default value in 5.0 + * *save_long => set to the default value in 6.0 + * config save will write "frr version 6.0" with 6.0 defaults + */ + + /* variable holding the default value for reading/use */ + bool *dflt_bool; + const char **dflt_str; + long *dflt_long; + unsigned long *dflt_ulong; + float *dflt_float; + + /* variable to use when comparing for config save */ + bool *save_bool; + const char **save_str; + long *save_long; + unsigned long *save_ulong; + float *save_float; + + struct frr_default_entry entries[]; +}; + +#define _FRR_CFG_DEFAULT(type, typname, varname, ...) \ + static type DFLT_##varname; \ + static type SAVE_##varname; \ + static struct frr_default _dflt_##varname = { \ + .name = #varname, \ + .dflt_##typname = &DFLT_##varname, \ + .save_##typname = &SAVE_##varname, \ + .entries = { __VA_ARGS__ }, \ + }; \ + static void _dfltinit_##varname(void) \ + __attribute__((_CONSTRUCTOR(1000))); \ + static void _dfltinit_##varname(void) \ + { \ + frr_default_add(&_dflt_##varname); \ + } + +/* use: + * FRR_CFG_DEFAULT_LONG(SHARP_BLUNTNESS, + * { .val_long = 2, .match_version = ">= 10.0" }, + * { .val_long = 1, .match_profile = "datacenter" }, + * { .val_long = 0 }, + * ) + * + * This will create DFLT_SHARP_BLUNTNESS and SAVE_SHARP_BLUNTNESS variables. + * + * Note: preprocessor defines cannot be used as variable names because they + * will be expanded and blow up with a compile error. Use an enum or add an + * extra _ at the beginning (e.g. _SHARP_BLUNTNESS => DFLT__SHARP_BLUNTNESS) + */ +#define FRR_CFG_DEFAULT_BOOL(varname, ...) \ + _FRR_CFG_DEFAULT(bool, bool, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_LONG(varname, ...) \ + _FRR_CFG_DEFAULT(long, long, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_ULONG(varname, ...) \ + _FRR_CFG_DEFAULT(unsigned long, ulong, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_FLOAT(varname, ...) \ + _FRR_CFG_DEFAULT(float, float, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_STR(varname, ...) \ + _FRR_CFG_DEFAULT(const char *, str, varname, ## __VA_ARGS__) + + +/* daemons don't need to call any of these, libfrr handles that */ +extern void frr_default_add(struct frr_default *dflt); +extern void frr_defaults_version_set(const char *version); +extern void frr_defaults_profile_set(const char *profile); +extern const char *frr_defaults_version(void); +extern const char *frr_defaults_profile(void); +extern void frr_defaults_apply(void); + +extern const char *frr_defaults_profiles[]; +extern bool frr_defaults_profile_valid(const char *profile); + +/* like strcmp(), but with version ordering */ +extern int frr_version_cmp(const char *aa, const char *bb); + +#endif /* _FRR_DEFAULTS_H */ diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index c6fd3c04ad..a23874a517 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -28,7 +28,6 @@ #endif #include "command.h" -#include "memory_vty.h" #include "graph.h" #include "linklist.h" #include "command_match.h" diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index 6d28a667b3..4bd8f5138a 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -28,7 +28,7 @@ #endif #include "command.h" -#include "memory_vty.h" +#include "lib_vty.h" static void vty_do_exit(int isexit) { @@ -57,7 +57,7 @@ int main(int argc, char **argv) host.domainname = strdup("testdomainname"); vty_init(master, true); - memory_init(); + lib_cmd_init(); yang_init(); nb_init(master, NULL, 0); @@ -262,7 +262,9 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf", ifp->name, old_vrf->name); if (if_dnode) { + nb_running_unset_entry(if_dnode->parent); yang_dnode_change_leaf(if_dnode, vrf->name); + nb_running_set_entry(if_dnode->parent, ifp); running_config->version++; } } diff --git a/lib/memory_vty.c b/lib/lib_vty.c index 1adc0d7b74..787da08e28 100644 --- a/lib/memory_vty.c +++ b/lib/lib_vty.c @@ -1,5 +1,5 @@ /* - * Memory and dynamic module VTY routine + * Assorted library VTY commands * * Copyright (C) 1998 Kunihiro Ishiguro * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc. @@ -35,7 +35,8 @@ #include "log.h" #include "memory.h" #include "module.h" -#include "memory_vty.h" +#include "defaults.h" +#include "lib_vty.h" /* Looking up memory status from vty interface. */ #include "vector.h" @@ -120,11 +121,11 @@ static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt) } -DEFUN (show_memory, - show_memory_cmd, - "show memory", - "Show running system information\n" - "Memory statistics\n") +DEFUN_NOSH (show_memory, + show_memory_cmd, + "show memory", + "Show running system information\n" + "Memory statistics\n") { #ifdef HAVE_MALLINFO show_memory_mallinfo(vty); @@ -134,11 +135,11 @@ DEFUN (show_memory, return CMD_SUCCESS; } -DEFUN (show_modules, - show_modules_cmd, - "show modules", - "Show running system information\n" - "Loaded modules\n") +DEFUN_NOSH (show_modules, + show_modules_cmd, + "show modules", + "Show running system information\n" + "Loaded modules\n") { struct frrmod_runtime *plug = frrmod_list; @@ -177,8 +178,60 @@ DEFUN (show_modules, return CMD_SUCCESS; } -void memory_init(void) +DEFUN (frr_defaults, + frr_defaults_cmd, + "frr defaults PROFILE...", + "FRRouting global parameters\n" + "set of configuration defaults used\n" + "profile string\n") { + char *profile = argv_concat(argv, argc, 2); + int rv = CMD_SUCCESS; + + if (!frr_defaults_profile_valid(profile)) { + vty_out(vty, "%% WARNING: profile %s is not known in this version\n", + profile); + rv = CMD_WARNING; + } + frr_defaults_profile_set(profile); + XFREE(MTYPE_TMP, profile); + return rv; +} + +DEFUN (frr_version, + frr_version_cmd, + "frr version VERSION...", + "FRRouting global parameters\n" + "version configuration was written by\n" + "version string\n") +{ + char *version = argv_concat(argv, argc, 2); + + frr_defaults_version_set(version); + XFREE(MTYPE_TMP, version); + return CMD_SUCCESS; +} + +static void defaults_autocomplete(vector comps, struct cmd_token *token) +{ + const char **p; + + for (p = frr_defaults_profiles; *p; p++) + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, *p)); +} + +static const struct cmd_variable_handler default_var_handlers[] = { + {.tokenname = "PROFILE", .completions = defaults_autocomplete}, + {.completions = NULL}, +}; + +void lib_cmd_init(void) +{ + cmd_variable_handler_register(default_var_handlers); + + install_element(CONFIG_NODE, &frr_defaults_cmd); + install_element(CONFIG_NODE, &frr_version_cmd); + install_element(VIEW_NODE, &show_memory_cmd); install_element(VIEW_NODE, &show_modules_cmd); } diff --git a/lib/memory_vty.h b/lib/lib_vty.h index 941255be1d..48e409ec52 100644 --- a/lib/memory_vty.h +++ b/lib/lib_vty.h @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _ZEBRA_MEMORY_VTY_H -#define _ZEBRA_MEMORY_VTY_H +#ifndef _ZEBRA_LIB_VTY_H +#define _ZEBRA_LIB_VTY_H #include "memory.h" @@ -27,7 +27,7 @@ extern "C" { #endif -extern void memory_init(void); +extern void lib_cmd_init(void); /* Human friendly string for given byte count */ #define MTYPE_MEMSTR_LEN 20 @@ -37,4 +37,4 @@ extern const char *mtype_memstr(char *, size_t, unsigned long); } #endif -#endif /* _ZEBRA_MEMORY_VTY_H */ +#endif /* _ZEBRA_LIB_VTY_H */ diff --git a/lib/libfrr.c b/lib/libfrr.c index 8ef32eaa8a..4fb43edff2 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -30,7 +30,7 @@ #include "vty.h" #include "command.h" #include "version.h" -#include "memory_vty.h" +#include "lib_vty.h" #include "log_vty.h" #include "zclient.h" #include "log_int.h" @@ -43,6 +43,7 @@ #include "debug.h" #include "frrcu.h" #include "frr_pthread.h" +#include "defaults.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) DEFINE_KOOH(frr_early_fini, (), ()) @@ -104,6 +105,7 @@ static const struct option lo_always[] = { {"version", no_argument, NULL, 'v'}, {"daemon", no_argument, NULL, 'd'}, {"module", no_argument, NULL, 'M'}, + {"profile", required_argument, NULL, 'F'}, {"vty_socket", required_argument, NULL, OPTION_VTYSOCK}, {"moduledir", required_argument, NULL, OPTION_MODULEDIR}, {"log", required_argument, NULL, OPTION_LOG}, @@ -112,11 +114,12 @@ static const struct option lo_always[] = { {"command-log-always", no_argument, NULL, OPTION_LOGGING}, {NULL}}; static const struct optspec os_always = { - "hvdM:", + "hvdM:F:", " -h, --help Display this help and exit\n" " -v, --version Print program version\n" " -d, --daemon Runs in daemon mode\n" " -M, --module Load specified module\n" + " -F, --profile Use specified configuration profile\n" " --vty_socket Override vty socket path\n" " --moduledir Override modules directory\n" " --log Set Logging to stdout, syslog, or file:<name>\n" @@ -175,7 +178,6 @@ static const struct optspec os_user = {"u:g:", " -g, --group Group to run as\n", lo_user}; - bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, const char *path) { @@ -390,6 +392,32 @@ static int frr_opt(int opt) *modnext = oc; modnext = &oc->next; break; + case 'F': + if (!frr_defaults_profile_valid(optarg)) { + const char **p; + FILE *ofd = stderr; + + if (!strcmp(optarg, "help")) + ofd = stdout; + else + fprintf(stderr, + "The \"%s\" configuration profile is not valid for this FRR version.\n", + optarg); + + fprintf(ofd, "Available profiles are:\n"); + for (p = frr_defaults_profiles; *p; p++) + fprintf(ofd, "%s%s\n", + strcmp(*p, DFLT_NAME) ? " " : " * ", + *p); + + if (ofd == stdout) + exit(0); + fprintf(ofd, "\n"); + errors++; + break; + } + frr_defaults_profile_set(optarg); + break; case 'i': if (di->flags & FRR_NO_CFG_PID_DRY) return 1; @@ -608,6 +636,7 @@ struct thread_master *frr_init(void) dir = di->module_path ? di->module_path : frr_moduledir; srandom(time(NULL)); + frr_defaults_apply(); if (di->instance) { snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]", @@ -679,7 +708,7 @@ struct thread_master *frr_init(void) cmd_init(1); vty_init(master, di->log_always); - memory_init(); + lib_cmd_init(); log_filter_cmd_init(); frr_pthread_init(); @@ -1077,7 +1106,6 @@ void frr_fini(void) hook_call(frr_fini); - /* memory_init -> nothing needed */ vty_terminate(); cmd_terminate(); nb_terminate(); diff --git a/lib/nexthop.c b/lib/nexthop.c index f314fea697..718cda7355 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -118,6 +118,12 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1, if (next1->type > next2->type) return 1; + if (next1->weight < next2->weight) + return -1; + + if (next1->weight > next2->weight) + return 1; + switch (next1->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV6: @@ -376,6 +382,9 @@ void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type, struct mpls_label_stack *nh_label; int i; + if (num_labels == 0) + return; + nexthop->nh_label_type = type; nh_label = XCALLOC(MTYPE_NH_LABEL, sizeof(struct mpls_label_stack) @@ -547,6 +556,7 @@ void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop, copy->ifindex = nexthop->ifindex; copy->type = nexthop->type; copy->flags = nexthop->flags; + copy->weight = nexthop->weight; memcpy(©->gate, &nexthop->gate, sizeof(nexthop->gate)); memcpy(©->src, &nexthop->src, sizeof(nexthop->src)); memcpy(©->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src)); diff --git a/lib/nexthop.h b/lib/nexthop.h index 72a4acedb2..040b643a84 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -110,6 +110,9 @@ struct nexthop { /* Label(s) associated with this nexthop. */ struct mpls_label_stack *nh_label; + + /* Weight of the nexthop ( for unequal cost ECMP ) */ + uint8_t weight; }; struct nexthop *nexthop_new(void); diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 3e08f1811e..991843a047 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -34,6 +34,17 @@ DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group") +/* + * Internal struct used to hold nhg config strings + */ +struct nexthop_hold { + char *nhvrf_name; + union sockunion *addr; + char *intf; + char *labels; + uint32_t weight; +}; + struct nexthop_group_hooks { void (*new)(const char *name); void (*add_nexthop)(const struct nexthop_group_cmd *nhg, @@ -425,7 +436,11 @@ static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2) if (ret) return ret; - return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name); + ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name); + if (ret) + return ret; + + return nhgc_cmp_helper(nh1->labels, nh2->labels); } static void nhgl_delete(struct nexthop_hold *nh) @@ -437,6 +452,8 @@ static void nhgl_delete(struct nexthop_hold *nh) if (nh->addr) sockunion_free(nh->addr); + XFREE(MTYPE_TMP, nh->labels); + XFREE(MTYPE_TMP, nh); } @@ -510,7 +527,8 @@ DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME", static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, const char *nhvrf_name, const union sockunion *addr, - const char *intf) + const char *intf, const char *labels, + const uint32_t weight) { struct nexthop_hold *nh; @@ -522,6 +540,10 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, nh->intf = XSTRDUP(MTYPE_TMP, intf); if (addr) nh->addr = sockunion_dup(addr); + if (labels) + nh->labels = XSTRDUP(MTYPE_TMP, labels); + + nh->weight = weight; listnode_add_sort(nhgc->nhg_list, nh); } @@ -529,15 +551,18 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, const char *nhvrf_name, const union sockunion *addr, - const char *intf) + const char *intf, const char *labels, + const uint32_t weight) { struct nexthop_hold *nh; struct listnode *node; for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) { - if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 && - nhgc_addr_cmp_helper(addr, nh->addr) == 0 && - nhgc_cmp_helper(intf, nh->intf) == 0) + if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 + && nhgc_addr_cmp_helper(addr, nh->addr) == 0 + && nhgc_cmp_helper(intf, nh->intf) == 0 + && nhgc_cmp_helper(labels, nh->labels) == 0 + && weight == nh->weight) break; } @@ -551,10 +576,19 @@ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, nhgl_delete(nh); } +/* + * Parse the config strings we support for a single nexthop. This gets used + * in a couple of different ways, and we distinguish between transient + * failures - such as a still-unprocessed interface - and fatal errors + * from label-string parsing. + */ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, const union sockunion *addr, - const char *intf, const char *name) + const char *intf, const char *name, + const char *labels, int *lbl_ret, + uint32_t weight) { + int ret = 0; struct vrf *vrf; memset(nhop, 0, sizeof(*nhop)); @@ -592,16 +626,50 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, } else nhop->type = NEXTHOP_TYPE_IFINDEX; + if (labels) { + uint8_t num = 0; + mpls_label_t larray[MPLS_MAX_LABELS]; + + ret = mpls_str2label(labels, &num, larray); + + /* Return label parse result */ + if (lbl_ret) + *lbl_ret = ret; + + if (ret < 0) + return false; + else if (num > 0) + nexthop_add_labels(nhop, ZEBRA_LSP_NONE, + num, larray); + } + + nhop->weight = weight; + return true; } +/* + * Wrapper to parse the strings in a 'nexthop_hold' + */ +static bool nexthop_group_parse_nhh(struct nexthop *nhop, + const struct nexthop_hold *nhh) +{ + return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf, + nhh->nhvrf_name, nhh->labels, NULL, + nhh->weight)); +} + DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "[no] nexthop\ <\ <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\ |INTERFACE$intf\ >\ - [nexthop-vrf NAME$vrf_name]", + [{ \ + nexthop-vrf NAME$vrf_name \ + |label WORD \ + |weight (1-255) \ + }]", NO_STR "Specify one of the nexthops in this ECMP group\n" "v4 Address\n" @@ -609,14 +677,20 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "Interface to use\n" "Interface to use\n" "If the nexthop is in a different vrf tell us\n" - "The nexthop-vrf Name\n") + "The nexthop-vrf Name\n" + "Specify label(s) for this nexthop\n" + "One or more labels in the range (16-1048575) separated by '/'\n" + "Weight to be used by the nexthop for purposes of ECMP\n" + "Weight value to be used\n") { VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); struct nexthop nhop; struct nexthop *nh; + int lbl_ret = 0; bool legal; - legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name); + legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label, + &lbl_ret, weight); if (nhop.type == NEXTHOP_TYPE_IPV6 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) { @@ -625,10 +699,32 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, return CMD_WARNING_CONFIG_FAILED; } + /* Handle label-string errors */ + if (!legal && lbl_ret < 0) { + switch (lbl_ret) { + case -1: + vty_out(vty, "%% Malformed label(s)\n"); + break; + case -2: + vty_out(vty, + "%% Cannot use reserved label(s) (%d-%d)\n", + MPLS_LABEL_RESERVED_MIN, + MPLS_LABEL_RESERVED_MAX); + break; + case -3: + vty_out(vty, + "%% Too many labels. Enter %d or fewer\n", + MPLS_MAX_LABELS); + break; + } + return CMD_WARNING_CONFIG_FAILED; + } + nh = nexthop_exists(&nhgc->nhg, &nhop); if (no) { - nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf); + nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label, + weight); if (nh) { _nexthop_del(&nhgc->nhg, nh); @@ -646,7 +742,8 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, _nexthop_add(&nhgc->nhg.nexthop, nh); } - nexthop_group_save_nhop(nhgc, vrf_name, addr, intf); + nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label, + weight); if (legal && nhg_hooks.add_nexthop) nhg_hooks.add_nexthop(nhgc, nh); @@ -696,6 +793,19 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) vrf = vrf_lookup_by_id(nh->vrf_id); vty_out(vty, " nexthop-vrf %s", vrf->name); } + + if (nh->nh_label && nh->nh_label->num_labels > 0) { + char buf[200]; + + mpls_label2str(nh->nh_label->num_labels, + nh->nh_label->label, + buf, sizeof(buf), 0); + vty_out(vty, " label %s", buf); + } + + if (nh->weight) + vty_out(vty, " weight %u", nh->weight); + vty_out(vty, "\n"); } @@ -715,6 +825,12 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty, if (nh->nhvrf_name) vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name); + if (nh->labels) + vty_out(vty, " label %s", nh->labels); + + if (nh->weight) + vty_out(vty, " weight %u", nh->weight); + vty_out(vty, "\n"); } @@ -751,9 +867,7 @@ void nexthop_group_enable_vrf(struct vrf *vrf) struct nexthop nhop; struct nexthop *nh; - if (!nexthop_group_parse_nexthop(&nhop, nhh->addr, - nhh->intf, - nhh->nhvrf_name)) + if (!nexthop_group_parse_nhh(&nhop, nhh)) continue; nh = nexthop_exists(&nhgc->nhg, &nhop); @@ -787,9 +901,7 @@ void nexthop_group_disable_vrf(struct vrf *vrf) struct nexthop nhop; struct nexthop *nh; - if (!nexthop_group_parse_nexthop(&nhop, nhh->addr, - nhh->intf, - nhh->nhvrf_name)) + if (!nexthop_group_parse_nhh(&nhop, nhh)) continue; nh = nexthop_exists(&nhgc->nhg, &nhop); @@ -824,9 +936,7 @@ void nexthop_group_interface_state_change(struct interface *ifp, for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { struct nexthop nhop; - if (!nexthop_group_parse_nexthop( - &nhop, nhh->addr, nhh->intf, - nhh->nhvrf_name)) + if (!nexthop_group_parse_nhh(&nhop, nhh)) continue; switch (nhop.type) { diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index 5105cbf188..c90b21737a 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -72,12 +72,6 @@ void nexthop_group_add_sorted(struct nexthop_group *nhg, (nhop) = nexthop_next(nhop) -struct nexthop_hold { - char *nhvrf_name; - union sockunion *addr; - char *intf; -}; - struct nexthop_group_cmd { RB_ENTRY(nexthop_group_cmd) nhgc_entry; diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 009e5bd824..17dc256281 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -21,6 +21,7 @@ #include "libfrr.h" #include "version.h" +#include "defaults.h" #include "log.h" #include "lib_errors.h" #include "command.h" @@ -486,7 +487,7 @@ static void nb_cli_show_config_cmds(struct vty *vty, struct nb_config *config, vty_out(vty, "Configuration:\n"); vty_out(vty, "!\n"); vty_out(vty, "frr version %s\n", FRR_VER_SHORT); - vty_out(vty, "frr defaults %s\n", DFLT_NAME); + vty_out(vty, "frr defaults %s\n", frr_defaults_profile()); LY_TREE_FOR (config->dnode, root) nb_cli_show_dnode_cmds(vty, root, with_defaults); diff --git a/lib/plist.c b/lib/plist.c index 64571a05b7..a0976cd6bd 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -1891,6 +1891,8 @@ int prefix_bgp_orf_set(char *name, afi_t afi, struct orf_prefix *orfp, if (!plist) return CMD_WARNING_CONFIG_FAILED; + apply_mask(&orfp->p); + if (set) { pentry = prefix_list_entry_make( &orfp->p, (permit ? PREFIX_PERMIT : PREFIX_DENY), diff --git a/lib/skiplist.c b/lib/skiplist.c index dda442580a..6efa2c362d 100644 --- a/lib/skiplist.c +++ b/lib/skiplist.c @@ -608,7 +608,7 @@ void skiplist_test(struct vty *vty) struct skiplist *l; register int i, k; void *keys[sampleSize]; - void *v; + void *v = NULL; zlog_debug("%s: entry", __func__); diff --git a/lib/stream.c b/lib/stream.c index 2e1a0193a2..dd4d5bd96d 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -812,7 +812,7 @@ int stream_put_ipv4(struct stream *s, uint32_t l) } /* Put long word to the stream. */ -int stream_put_in_addr(struct stream *s, struct in_addr *addr) +int stream_put_in_addr(struct stream *s, const struct in_addr *addr) { STREAM_VERIFY_SANE(s); @@ -828,7 +828,8 @@ int stream_put_in_addr(struct stream *s, struct in_addr *addr) } /* Put in_addr at location in the stream. */ -int stream_put_in_addr_at(struct stream *s, size_t putp, struct in_addr *addr) +int stream_put_in_addr_at(struct stream *s, size_t putp, + const struct in_addr *addr) { STREAM_VERIFY_SANE(s); @@ -842,7 +843,8 @@ int stream_put_in_addr_at(struct stream *s, size_t putp, struct in_addr *addr) } /* Put in6_addr at location in the stream. */ -int stream_put_in6_addr_at(struct stream *s, size_t putp, struct in6_addr *addr) +int stream_put_in6_addr_at(struct stream *s, size_t putp, + const struct in6_addr *addr) { STREAM_VERIFY_SANE(s); @@ -856,7 +858,7 @@ int stream_put_in6_addr_at(struct stream *s, size_t putp, struct in6_addr *addr) } /* Put prefix by nlri type format. */ -int stream_put_prefix_addpath(struct stream *s, struct prefix *p, +int stream_put_prefix_addpath(struct stream *s, const struct prefix *p, int addpath_encode, uint32_t addpath_tx_id) { size_t psize; @@ -890,7 +892,7 @@ int stream_put_prefix_addpath(struct stream *s, struct prefix *p, return psize; } -int stream_put_prefix(struct stream *s, struct prefix *p) +int stream_put_prefix(struct stream *s, const struct prefix *p) { return stream_put_prefix_addpath(s, p, 0, 0); } diff --git a/lib/stream.h b/lib/stream.h index 1144e43ef0..c0d25e0579 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -186,13 +186,16 @@ extern int stream_putl_at(struct stream *, size_t, uint32_t); extern int stream_putq(struct stream *, uint64_t); extern int stream_putq_at(struct stream *, size_t, uint64_t); extern int stream_put_ipv4(struct stream *, uint32_t); -extern int stream_put_in_addr(struct stream *, struct in_addr *); -extern int stream_put_in_addr_at(struct stream *, size_t, struct in_addr *); -extern int stream_put_in6_addr_at(struct stream *, size_t, struct in6_addr *); -extern int stream_put_prefix_addpath(struct stream *, struct prefix *, +extern int stream_put_in_addr(struct stream *s, const struct in_addr *addr); +extern int stream_put_in_addr_at(struct stream *s, size_t putp, + const struct in_addr *addr); +extern int stream_put_in6_addr_at(struct stream *s, size_t putp, + const struct in6_addr *addr); +extern int stream_put_prefix_addpath(struct stream *s, + const struct prefix *p, int addpath_encode, uint32_t addpath_tx_id); -extern int stream_put_prefix(struct stream *, struct prefix *); +extern int stream_put_prefix(struct stream *s, const struct prefix *p); extern int stream_put_labeled_prefix(struct stream *, struct prefix *, mpls_label_t *, int addpath_encode, uint32_t addpath_tx_id); diff --git a/lib/subdir.am b/lib/subdir.am index 23b1950384..cb6fa7a3b8 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -18,6 +18,7 @@ lib_libfrr_la_SOURCES = \ lib/command_parse.y \ lib/csv.c \ lib/debug.c \ + lib/defaults.c \ lib/distribute.c \ lib/ferr.c \ lib/filter.c \ @@ -40,13 +41,13 @@ lib_libfrr_la_SOURCES = \ lib/json.c \ lib/keychain.c \ lib/lib_errors.c \ + lib/lib_vty.c \ lib/libfrr.c \ lib/linklist.c \ lib/log.c \ lib/log_vty.c \ lib/md5.c \ lib/memory.c \ - lib/memory_vty.c \ lib/mlag.c \ lib/module.c \ lib/mpls.c \ @@ -114,6 +115,7 @@ vtysh_scan += \ $(top_srcdir)/lib/if.c \ $(top_srcdir)/lib/if_rmap.c \ $(top_srcdir)/lib/keychain.c \ + $(top_srcdir)/lib/lib_vty.c \ $(top_srcdir)/lib/nexthop_group.c \ $(top_srcdir)/lib/plist.c \ $(top_srcdir)/lib/routemap.c \ @@ -156,6 +158,7 @@ pkginclude_HEADERS += \ lib/csv.h \ lib/db.h \ lib/debug.h \ + lib/defaults.h \ lib/distribute.h \ lib/ferr.h \ lib/filter.h \ @@ -179,6 +182,7 @@ pkginclude_HEADERS += \ lib/json.h \ lib/keychain.h \ lib/lib_errors.h \ + lib/lib_vty.h \ lib/libfrr.h \ lib/libospf.h \ lib/linklist.h \ @@ -186,7 +190,6 @@ pkginclude_HEADERS += \ lib/log_vty.h \ lib/md5.h \ lib/memory.h \ - lib/memory_vty.h \ lib/module.h \ lib/monotime.h \ lib/mpls.h \ diff --git a/lib/zclient.c b/lib/zclient.c index 7a62e408ea..6982d287a2 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -802,6 +802,12 @@ static int zapi_nexthop_cmp_no_labels(const struct zapi_nexthop *next1, if (next1->type > next2->type) return 1; + if (next1->weight < next2->weight) + return -1; + + if (next1->weight > next2->weight) + return 1; + switch (next1->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV6: @@ -859,6 +865,82 @@ static void zapi_nexthop_group_sort(struct zapi_nexthop *nh_grp, &zapi_nexthop_cmp); } +/* + * Encode a single zapi nexthop + */ +int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, + uint32_t api_flags) +{ + int ret = 0; + int nh_flags = api_nh->flags; + + stream_putl(s, api_nh->vrf_id); + stream_putc(s, api_nh->type); + + /* If needed, set 'labelled nexthop' flag */ + if (api_nh->label_num > 0) { + SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_LABEL); + + /* Validate label count */ + if (api_nh->label_num > MPLS_MAX_LABELS) { + ret = -1; + goto done; + } + } + + if (api_nh->weight) + SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_WEIGHT); + + /* Note that we're only encoding a single octet */ + stream_putc(s, nh_flags); + + switch (api_nh->type) { + case NEXTHOP_TYPE_BLACKHOLE: + stream_putc(s, api_nh->bh_type); + break; + case NEXTHOP_TYPE_IPV4: + stream_put_in_addr(s, &api_nh->gate.ipv4); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + stream_put_in_addr(s, &api_nh->gate.ipv4); + stream_putl(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IFINDEX: + stream_putl(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IPV6: + stream_write(s, (uint8_t *)&api_nh->gate.ipv6, + 16); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + stream_write(s, (uint8_t *)&api_nh->gate.ipv6, + 16); + stream_putl(s, api_nh->ifindex); + break; + } + + /* We only encode labels if we have >0 - we use + * the per-nexthop flag above to signal that the count + * is present in the payload. + */ + if (api_nh->label_num > 0) { + stream_putc(s, api_nh->label_num); + stream_put(s, &api_nh->labels[0], + api_nh->label_num * sizeof(mpls_label_t)); + } + + if (api_nh->weight) + stream_putl(s, api_nh->weight); + + /* Router MAC for EVPN routes. */ + if (CHECK_FLAG(api_flags, ZEBRA_FLAG_EVPN_ROUTE)) + stream_put(s, &(api_nh->rmac), + sizeof(struct ethaddr)); + +done: + return ret; +} + int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) { struct zapi_nexthop *api_nh; @@ -921,59 +1003,22 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) for (i = 0; i < api->nexthop_num; i++) { api_nh = &api->nexthops[i]; - stream_putl(s, api_nh->vrf_id); - stream_putc(s, api_nh->type); - stream_putc(s, api_nh->onlink); - switch (api_nh->type) { - case NEXTHOP_TYPE_BLACKHOLE: - stream_putc(s, api_nh->bh_type); - break; - case NEXTHOP_TYPE_IPV4: - stream_put_in_addr(s, &api_nh->gate.ipv4); - break; - case NEXTHOP_TYPE_IPV4_IFINDEX: - stream_put_in_addr(s, &api_nh->gate.ipv4); - stream_putl(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - stream_putl(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IPV6: - stream_write(s, (uint8_t *)&api_nh->gate.ipv6, - 16); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - stream_write(s, (uint8_t *)&api_nh->gate.ipv6, - 16); - stream_putl(s, api_nh->ifindex); - break; - } - /* MPLS labels for BGP-LU or Segment Routing */ - if (CHECK_FLAG(api->message, ZAPI_MESSAGE_LABEL)) { - if (api_nh->label_num > MPLS_MAX_LABELS) { - char buf[PREFIX2STR_BUFFER]; - prefix2str(&api->prefix, buf, - sizeof(buf)); - flog_err(EC_LIB_ZAPI_ENCODE, - "%s: prefix %s: can't encode " - "%u labels (maximum is %u)", - __func__, buf, - api_nh->label_num, - MPLS_MAX_LABELS); - return -1; - } - - stream_putc(s, api_nh->label_num); - stream_put(s, &api_nh->labels[0], - api_nh->label_num - * sizeof(mpls_label_t)); + if (api_nh->label_num > MPLS_MAX_LABELS) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&api->prefix, buf, sizeof(buf)); + + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: prefix %s: can't encode %u labels (maximum is %u)", + __func__, buf, + api_nh->label_num, + MPLS_MAX_LABELS); + return -1; } - /* Router MAC for EVPN routes. */ - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) - stream_put(s, &(api_nh->rmac), - sizeof(struct ethaddr)); + if (zapi_nexthop_encode(s, api_nh, api->flags) != 0) + return -1; } } @@ -995,6 +1040,76 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return 0; } +/* + * Decode a single zapi nexthop object + */ +static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, + uint32_t api_flags) +{ + int ret = -1; + + STREAM_GETL(s, api_nh->vrf_id); + STREAM_GETC(s, api_nh->type); + + /* Note that we're only using a single octet of flags */ + STREAM_GETC(s, api_nh->flags); + + switch (api_nh->type) { + case NEXTHOP_TYPE_BLACKHOLE: + STREAM_GETC(s, api_nh->bh_type); + break; + case NEXTHOP_TYPE_IPV4: + STREAM_GET(&api_nh->gate.ipv4.s_addr, s, + IPV4_MAX_BYTELEN); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + STREAM_GET(&api_nh->gate.ipv4.s_addr, s, + IPV4_MAX_BYTELEN); + STREAM_GETL(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IFINDEX: + STREAM_GETL(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IPV6: + STREAM_GET(&api_nh->gate.ipv6, s, 16); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + STREAM_GET(&api_nh->gate.ipv6, s, 16); + STREAM_GETL(s, api_nh->ifindex); + break; + } + + /* MPLS labels for BGP-LU or Segment Routing */ + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)) { + STREAM_GETC(s, api_nh->label_num); + if (api_nh->label_num > MPLS_MAX_LABELS) { + flog_err( + EC_LIB_ZAPI_ENCODE, + "%s: invalid number of MPLS labels (%u)", + __func__, api_nh->label_num); + return -1; + } + + STREAM_GET(&api_nh->labels[0], s, + api_nh->label_num * sizeof(mpls_label_t)); + } + + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT)) + STREAM_GETL(s, api_nh->weight); + + /* Router MAC for EVPN routes. */ + if (CHECK_FLAG(api_flags, ZEBRA_FLAG_EVPN_ROUTE)) + STREAM_GET(&(api_nh->rmac), s, + sizeof(struct ethaddr)); + + /* Success */ + ret = 0; + +stream_failure: + + return ret; +} + int zapi_route_decode(struct stream *s, struct zapi_route *api) { struct zapi_nexthop *api_nh; @@ -1088,55 +1203,8 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) for (i = 0; i < api->nexthop_num; i++) { api_nh = &api->nexthops[i]; - STREAM_GETL(s, api_nh->vrf_id); - STREAM_GETC(s, api_nh->type); - STREAM_GETC(s, api_nh->onlink); - switch (api_nh->type) { - case NEXTHOP_TYPE_BLACKHOLE: - STREAM_GETC(s, api_nh->bh_type); - break; - case NEXTHOP_TYPE_IPV4: - STREAM_GET(&api_nh->gate.ipv4.s_addr, s, - IPV4_MAX_BYTELEN); - break; - case NEXTHOP_TYPE_IPV4_IFINDEX: - STREAM_GET(&api_nh->gate.ipv4.s_addr, s, - IPV4_MAX_BYTELEN); - STREAM_GETL(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - STREAM_GETL(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IPV6: - STREAM_GET(&api_nh->gate.ipv6, s, 16); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - STREAM_GET(&api_nh->gate.ipv6, s, 16); - STREAM_GETL(s, api_nh->ifindex); - break; - } - - /* MPLS labels for BGP-LU or Segment Routing */ - if (CHECK_FLAG(api->message, ZAPI_MESSAGE_LABEL)) { - STREAM_GETC(s, api_nh->label_num); - - if (api_nh->label_num > MPLS_MAX_LABELS) { - flog_err( - EC_LIB_ZAPI_ENCODE, - "%s: invalid number of MPLS labels (%u)", - __func__, api_nh->label_num); - return -1; - } - - STREAM_GET(&api_nh->labels[0], s, - api_nh->label_num - * sizeof(mpls_label_t)); - } - - /* Router MAC for EVPN routes. */ - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) - stream_get(&(api_nh->rmac), s, - sizeof(struct ethaddr)); + if (zapi_nexthop_decode(s, api_nh, api->flags) != 0) + return -1; } } @@ -1335,6 +1403,35 @@ struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh) return n; } +/* + * Convert nexthop to zapi nexthop + */ +int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, + const struct nexthop *nh) +{ + int i; + + memset(znh, 0, sizeof(*znh)); + + znh->type = nh->type; + znh->vrf_id = nh->vrf_id; + znh->ifindex = nh->ifindex; + znh->gate = nh->gate; + + if (nh->nh_label && (nh->nh_label->num_labels > 0)) { + for (i = 0; i < nh->nh_label->num_labels; i++) + znh->labels[i] = nh->nh_label->label[i]; + + znh->label_num = i; + SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_LABEL); + } + + return 0; +} + +/* + * Decode the nexthop-tracking update message + */ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) { uint32_t i; @@ -1361,38 +1458,8 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) STREAM_GETC(s, nhr->nexthop_num); for (i = 0; i < nhr->nexthop_num; i++) { - STREAM_GETL(s, nhr->nexthops[i].vrf_id); - STREAM_GETC(s, nhr->nexthops[i].type); - switch (nhr->nexthops[i].type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - STREAM_GET(&nhr->nexthops[i].gate.ipv4.s_addr, s, - IPV4_MAX_BYTELEN); - STREAM_GETL(s, nhr->nexthops[i].ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - STREAM_GETL(s, nhr->nexthops[i].ifindex); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - STREAM_GET(&nhr->nexthops[i].gate.ipv6, s, - IPV6_MAX_BYTELEN); - STREAM_GETL(s, nhr->nexthops[i].ifindex); - break; - case NEXTHOP_TYPE_BLACKHOLE: - break; - } - STREAM_GETC(s, nhr->nexthops[i].label_num); - if (nhr->nexthops[i].label_num > MPLS_MAX_LABELS) { - flog_err(EC_LIB_ZAPI_ENCODE, - "%s: invalid number of MPLS labels (%u)", - __func__, nhr->nexthops[i].label_num); - return false; - } - if (nhr->nexthops[i].label_num) - STREAM_GET(&nhr->nexthops[i].labels[0], s, - nhr->nexthops[i].label_num - * sizeof(mpls_label_t)); + if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0) != 0) + return -1; } return true; diff --git a/lib/zclient.h b/lib/zclient.h index 7adb294a31..70304127a2 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -289,7 +289,6 @@ struct zclient { #define ZAPI_MESSAGE_TAG 0x08 #define ZAPI_MESSAGE_MTU 0x10 #define ZAPI_MESSAGE_SRCPFX 0x20 -#define ZAPI_MESSAGE_LABEL 0x40 /* * This should only be used by a DAEMON that needs to communicate * the table being used is not in the VRF. You must pass the @@ -313,7 +312,7 @@ struct zapi_nexthop { enum nexthop_types_t type; vrf_id_t vrf_id; ifindex_t ifindex; - bool onlink; + uint8_t flags; union { union g_addr gate; enum blackhole_type bh_type; @@ -324,9 +323,18 @@ struct zapi_nexthop { mpls_label_t labels[MPLS_MAX_LABELS]; struct ethaddr rmac; + + uint32_t weight; }; /* + * ZAPI nexthop flags values + */ +#define ZAPI_NEXTHOP_FLAG_ONLINK 0x01 +#define ZAPI_NEXTHOP_FLAG_LABEL 0x02 +#define ZAPI_NEXTHOP_FLAG_WEIGHT 0x04 + +/* * Some of these data structures do not map easily to * a actual data structure size giving different compilers * and systems. For those data structures we need @@ -485,6 +493,29 @@ enum zapi_iptable_notify_owner { ZAPI_IPTABLE_FAIL_REMOVE, }; +static inline const char * +zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note) +{ + const char *ret = "UNKNOWN"; + + switch (note) { + case ZAPI_RULE_FAIL_INSTALL: + ret = "ZAPI_RULE_FAIL_INSTALL"; + break; + case ZAPI_RULE_INSTALLED: + ret = "ZAPI_RULE_INSTALLED"; + break; + case ZAPI_RULE_FAIL_REMOVE: + ret = "ZAPI_RULE_FAIL_REMOVE"; + break; + case ZAPI_RULE_REMOVED: + ret = "ZAPI_RULE_REMOVED"; + break; + } + + return ret; +} + /* Zebra MAC types */ #define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/ #define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/ @@ -666,6 +697,8 @@ extern int zclient_route_send(uint8_t, struct zclient *, struct zapi_route *); extern int zclient_send_rnh(struct zclient *zclient, int command, struct prefix *p, bool exact_match, vrf_id_t vrf_id); +int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, + uint32_t api_flags); extern int zapi_route_encode(uint8_t, struct stream *, struct zapi_route *); extern int zapi_route_decode(struct stream *, struct zapi_route *); bool zapi_route_notify_decode(struct stream *s, struct prefix *p, @@ -690,6 +723,8 @@ bool zapi_iptable_notify_decode(struct stream *s, enum zapi_iptable_notify_owner *note); extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh); +int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, + const struct nexthop *nh); extern bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr); diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index a781122b16..b58fe776ab 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -21,7 +21,6 @@ #include "version.h" #include "log.h" #include "memory.h" -#include "memory_vty.h" #include "command.h" #include "libfrr.h" diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 0fa5585b80..0aaefeb3c2 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -28,7 +28,6 @@ #include "command.h" #include "vty.h" #include "memory.h" -#include "memory_vty.h" #include "if.h" #include "filter.h" #include "prefix.h" diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 95dafff84e..95537eb86e 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -51,6 +51,11 @@ DEFINE_QOBJ_TYPE(ospf6) +FRR_CFG_DEFAULT_BOOL(OSPF6_LOG_ADJACENCY_CHANGES, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) + /* global ospf6d variable */ struct ospf6 *ospf6; static struct ospf6_master ospf6_master; @@ -179,11 +184,6 @@ static struct ospf6 *ospf6_create(vrf_id_t vrf_id) o->distance_table = route_table_init(); -/* Enable "log-adjacency-changes" */ -#if DFLT_OSPF6_LOG_ADJACENCY_CHANGES - SET_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); -#endif - QOBJ_REG(o, ospf6); return o; @@ -327,6 +327,9 @@ DEFUN_NOSH (router_ospf6, { if (ospf6 == NULL) { ospf6 = ospf6_create(VRF_DEFAULT); + if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES) + SET_FLAG(ospf6->config_flags, + OSPF6_LOG_ADJACENCY_CHANGES); if (ospf6->router_id == 0) ospf6_router_id_update(); } @@ -1079,9 +1082,9 @@ static int config_write_ospf6(struct vty *vty) if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) { if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL)) vty_out(vty, " log-adjacency-changes detail\n"); - else if (!DFLT_OSPF6_LOG_ADJACENCY_CHANGES) + else if (!SAVE_OSPF6_LOG_ADJACENCY_CHANGES) vty_out(vty, " log-adjacency-changes\n"); - } else if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES) { + } else if (SAVE_OSPF6_LOG_ADJACENCY_CHANGES) { vty_out(vty, " no log-adjacency-changes\n"); } diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index ba41fca65b..18c0697025 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -29,6 +29,12 @@ struct ospf6_master { uint32_t zebra_router_id; }; +/* ospf6->config_flags */ +enum { + OSPF6_LOG_ADJACENCY_CHANGES = (1 << 0), + OSPF6_LOG_ADJACENCY_DETAIL = (1 << 1), +}; + /* OSPFv3 top level data structure */ struct ospf6 { /* The relevant vrf_id */ @@ -68,10 +74,8 @@ struct ospf6 { uint8_t flag; - /* Configured flags */ + /* Configuration bitmask, refer to enum above */ uint8_t config_flags; -#define OSPF6_LOG_ADJACENCY_CHANGES (1 << 0) -#define OSPF6_LOG_ADJACENCY_DETAIL (1 << 1) /* LSA timer parameters */ unsigned int lsa_minarrival; /* LSA minimum arrival in milliseconds. */ diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index bd703bc89d..49730063b9 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -2325,7 +2325,7 @@ void ospf_apiserver_clients_notify_ism_change(struct ospf_interface *oi) void ospf_apiserver_clients_notify_nsm_change(struct ospf_neighbor *nbr) { struct msg *msg; - struct in_addr ifaddr = {.s_addr = 0L}; + struct in_addr ifaddr; struct in_addr nbraddr; assert(nbr); diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 7ddffbcdbd..cfcffcdb3d 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -636,9 +636,13 @@ void ospf_if_update_params(struct interface *ifp, struct in_addr addr) int ospf_if_new_hook(struct interface *ifp) { int rc = 0; + struct ospf_if_info *oii; ifp->info = XCALLOC(MTYPE_OSPF_IF_INFO, sizeof(struct ospf_if_info)); + oii = ifp->info; + oii->curr_mtu = ifp->mtu; + IF_OIFS(ifp) = route_table_init(); IF_OIFS_PARAMS(ifp) = route_table_init(); @@ -1261,31 +1265,21 @@ static int ospf_ifp_up(struct interface *ifp) { struct ospf_interface *oi; struct route_node *rn; + struct ospf_if_info *oii = ifp->info; - /* Interface is already up. */ - if (if_is_operative(ifp)) { - /* Temporarily keep ifp values. */ - struct interface if_tmp; - memcpy(&if_tmp, ifp, sizeof(struct interface)); + ospf_if_recalculate_output_cost(ifp); + if (oii && oii->curr_mtu != ifp->mtu) { if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) zlog_debug( - "Zebra: Interface[%s] state update speed %u -> %u, bw %d -> %d", - ifp->name, if_tmp.speed, ifp->speed, - if_tmp.bandwidth, ifp->bandwidth); + "Zebra: Interface[%s] MTU change %u -> %u.", + ifp->name, oii->curr_mtu, ifp->mtu); - ospf_if_recalculate_output_cost(ifp); + oii->curr_mtu = ifp->mtu; + /* Must reset the interface (simulate down/up) when MTU + * changes. */ + ospf_if_reset(ifp); - if (if_tmp.mtu != ifp->mtu) { - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: Interface[%s] MTU change %u -> %u.", - ifp->name, if_tmp.mtu, ifp->mtu); - - /* Must reset the interface (simulate down/up) when MTU - * changes. */ - ospf_if_reset(ifp); - } return 0; } diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index cde52dbb9e..4b3dbcc5c2 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -117,6 +117,8 @@ struct ospf_if_info { struct route_table *oifs; unsigned int membership_counts[MEMBER_MAX]; /* multicast group refcnts */ + + uint32_t curr_mtu; }; struct ospf_interface; diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index a542b4da83..d02ffe0448 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -35,7 +35,6 @@ #include "stream.h" #include "log.h" #include "memory.h" -#include "memory_vty.h" #include "privs.h" #include "sigevent.h" #include "zclient.h" diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 4c97615ed1..152a7e83b7 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -53,6 +53,11 @@ #include "ospfd/ospf_dump.h" #include "ospfd/ospf_bfd.h" +FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) + static const char *const ospf_network_type_str[] = { "Null", "POINTOPOINT", "BROADCAST", "NBMA", "POINTOMULTIPOINT", "VIRTUALLINK", "LOOPBACK"}; @@ -138,6 +143,7 @@ static struct ospf *ospf_cmd_lookup_ospf(struct vty *vty, struct ospf *ospf = NULL; int idx_vrf = 0, idx_inst = 0; const char *vrf_name = NULL; + bool created = false; *instance = 0; if (argv_find(argv, argc, "(1-65535)", &idx_inst)) @@ -149,18 +155,23 @@ static struct ospf *ospf_cmd_lookup_ospf(struct vty *vty, vrf_name = NULL; if (enable) { /* Allocate VRF aware instance */ - ospf = ospf_get(*instance, vrf_name); + ospf = ospf_get(*instance, vrf_name, &created); } else { ospf = ospf_lookup_by_inst_name(*instance, vrf_name); } } else { if (enable) { - ospf = ospf_get(*instance, NULL); + ospf = ospf_get(*instance, NULL, &created); } else { ospf = ospf_lookup_instance(*instance); } } + if (created) { + if (DFLT_OSPF_LOG_ADJACENCY_CHANGES) + SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES); + } + return ospf; } @@ -4154,7 +4165,7 @@ DEFUN (show_ip_ospf_interface_traffic, static void show_ip_ospf_neighbour_header(struct vty *vty) { - vty_out(vty, "\n%-15s %3s %-15s %9s %-15s %-20s %5s %5s %5s\n", + vty_out(vty, "\n%-15s %3s %-15s %9s %-15s %-32s %5s %5s %5s\n", "Neighbor ID", "Pri", "State", "Dead Time", "Address", "Interface", "RXmtL", "RqstL", "DBsmL"); } @@ -4260,7 +4271,7 @@ static void show_ip_ospf_neighbor_sub(struct vty *vty, timebuf, sizeof(timebuf))); vty_out(vty, "%-15s ", inet_ntoa(nbr->src)); - vty_out(vty, "%-20s %5ld %5ld %5d\n", + vty_out(vty, "%-32s %5ld %5ld %5d\n", IF_NAME(oi), ospf_ls_retransmit_count(nbr), ospf_ls_request_count(nbr), @@ -4524,7 +4535,7 @@ static int show_ip_ospf_neighbor_all_common(struct vty *vty, struct ospf *ospf, "-", nbr_nbma->priority, "Down", "-"); vty_out(vty, - "%-15s %-20s %5d %5d %5d\n", + "%-32s %-20s %5d %5d %5d\n", inet_ntoa(nbr_nbma->addr), IF_NAME(oi), 0, 0, 0); } @@ -10358,9 +10369,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES)) { if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) vty_out(vty, " log-adjacency-changes detail\n"); - else if (!DFLT_OSPF_LOG_ADJACENCY_CHANGES) + else if (!SAVE_OSPF_LOG_ADJACENCY_CHANGES) vty_out(vty, " log-adjacency-changes\n"); - } else if (DFLT_OSPF_LOG_ADJACENCY_CHANGES) { + } else if (SAVE_OSPF_LOG_ADJACENCY_CHANGES) { vty_out(vty, " no log-adjacency-changes\n"); } diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 5058886f36..6a4e63372a 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -293,11 +293,6 @@ static struct ospf *ospf_new(unsigned short instance, const char *name) new->oi_write_q = list_new(); new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT; -/* Enable "log-adjacency-changes" */ -#if DFLT_OSPF_LOG_ADJACENCY_CHANGES - SET_FLAG(new->config, OSPF_LOG_ADJACENCY_CHANGES); -#endif - QOBJ_REG(new, ospf); new->fd = -1; @@ -368,7 +363,7 @@ struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name) return NULL; } -struct ospf *ospf_get(unsigned short instance, const char *name) +struct ospf *ospf_get(unsigned short instance, const char *name, bool *created) { struct ospf *ospf; @@ -379,6 +374,7 @@ struct ospf *ospf_get(unsigned short instance, const char *name) else ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + *created = (ospf == NULL); if (ospf == NULL) { ospf = ospf_new(instance, name); ospf_add(ospf); @@ -392,27 +388,18 @@ struct ospf *ospf_get(unsigned short instance, const char *name) return ospf; } -struct ospf *ospf_get_instance(unsigned short instance) +struct ospf *ospf_get_instance(unsigned short instance, bool *created) { struct ospf *ospf; ospf = ospf_lookup_instance(instance); + *created = (ospf == NULL); if (ospf == NULL) { ospf = ospf_new(instance, NULL /* VRF_DEFAULT*/); ospf_add(ospf); - if (ospf->router_id_static.s_addr == 0) { - if (vrf_lookup_by_id(ospf->vrf_id)) - ospf_router_id_update(ospf); - else { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "%s: ospf VRF (id %d) is not active yet, skip router id update", - __PRETTY_FUNCTION__, - ospf->vrf_id); - } + if (ospf->router_id_static.s_addr == 0) ospf_router_id_update(ospf); - } ospf_opaque_type11_lsa_init(ospf); } @@ -584,7 +571,6 @@ static void ospf_finish_final(struct ospf *ospf) struct ospf_vl_data *vl_data; struct listnode *node, *nnode; int i; - unsigned short instance = 0; QOBJ_UNREG(ospf); @@ -755,9 +741,6 @@ static void ospf_finish_final(struct ospf *ospf) ospf_distance_reset(ospf); route_table_finish(ospf->distance_table); - if (!CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN)) - instance = ospf->instance; - list_delete(&ospf->areas); list_delete(&ospf->oi_write_q); @@ -778,9 +761,6 @@ static void ospf_finish_final(struct ospf *ospf) } XFREE(MTYPE_OSPF_TOP, ospf); - - if (!CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN)) - ospf_get_instance(instance); } diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 937d363b4c..5e91e6f8e6 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -117,6 +117,14 @@ struct ospf_redist { #define ROUTEMAP(R) (R->route_map.map) }; +/* ospf->config */ +enum { + OSPF_RFC1583_COMPATIBLE = (1 << 0), + OSPF_OPAQUE_CAPABLE = (1 << 2), + OSPF_LOG_ADJACENCY_CHANGES = (1 << 3), + OSPF_LOG_ADJACENCY_DETAIL = (1 << 4), +}; + /* OSPF instance structure. */ struct ospf { /* OSPF's running state based on the '[no] router ospf [<instance>]' @@ -151,12 +159,8 @@ struct ospf { /* NSSA ABR */ uint8_t anyNSSA; /* Bump for every NSSA attached. */ - /* Configured variables. */ + /* Configuration bitmask, refer to enum above */ uint8_t config; -#define OSPF_RFC1583_COMPATIBLE (1 << 0) -#define OSPF_OPAQUE_CAPABLE (1 << 2) -#define OSPF_LOG_ADJACENCY_CHANGES (1 << 3) -#define OSPF_LOG_ADJACENCY_DETAIL (1 << 4) /* Opaque-LSA administrative flags. */ uint8_t opaque; @@ -500,8 +504,9 @@ extern struct zebra_privs_t ospfd_privs; /* Prototypes. */ extern const char *ospf_redist_string(unsigned int route_type); extern struct ospf *ospf_lookup_instance(unsigned short); -extern struct ospf *ospf_get(unsigned short instance, const char *name); -extern struct ospf *ospf_get_instance(unsigned short); +extern struct ospf *ospf_get(unsigned short instance, const char *name, + bool *created); +extern struct ospf *ospf_get_instance(unsigned short, bool *created); extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name); extern struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id); diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 7e34066b47..37b6be6949 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -100,6 +100,37 @@ static void pbr_map_interface_list_delete(struct pbr_map_interface *pmi) } } +static bool pbrms_is_installed(const struct pbr_map_sequence *pbrms, + const struct pbr_map_interface *pmi) +{ + uint64_t is_installed = (uint64_t)1 << pmi->install_bit; + + is_installed &= pbrms->installed; + + if (is_installed) + return true; + + return false; +} + +/* If any sequence is installed on the interface, assume installed */ +static bool +pbr_map_interface_is_installed(const struct pbr_map *pbrm, + const struct pbr_map_interface *check_pmi) +{ + + struct pbr_map_sequence *pbrms; + struct pbr_map_interface *pmi; + struct listnode *node, *inode; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + if (pmi == check_pmi && pbrms_is_installed(pbrms, pmi)) + return true; + + return false; +} + static bool pbr_map_interface_is_valid(const struct pbr_map_interface *pmi) { /* Don't install rules without a real ifindex on the incoming interface. @@ -171,7 +202,7 @@ void pbr_map_reason_string(unsigned int reason, char *buf, int size) void pbr_map_final_interface_deletion(struct pbr_map *pbrm, struct pbr_map_interface *pmi) { - if (pmi->delete == true) { + if (pmi->delete == true && !pbr_map_interface_is_installed(pbrm, pmi)) { listnode_delete(pbrm->incoming, pmi); pmi->pbrm = NULL; diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index bc4aa947a9..c1922d8728 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -127,11 +127,16 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, pbrms->family = prefix->family; if (!no) { - if (prefix_same(pbrms->src, prefix)) - return CMD_SUCCESS; + if (pbrms->src) { + if (prefix_same(pbrms->src, prefix)) + return CMD_SUCCESS; + + vty_out(vty, + "A `match src-ip XX` command already exists, please remove that first\n"); + return CMD_WARNING_CONFIG_FAILED; + } - if (!pbrms->src) - pbrms->src = prefix_new(); + pbrms->src = prefix_new(); prefix_copy(pbrms->src, prefix); } else prefix_free(&pbrms->src); @@ -145,7 +150,7 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, "[no] match dst-ip <A.B.C.D/M|X:X::X:X/M>$prefix", NO_STR "Match the rest of the command\n" - "Choose the src ip or ipv6 prefix to use\n" + "Choose the dst ip or ipv6 prefix to use\n" "v4 Prefix\n" "v6 Prefix\n") { @@ -154,11 +159,16 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, pbrms->family = prefix->family; if (!no) { - if (prefix_same(pbrms->dst, prefix)) - return CMD_SUCCESS; + if (pbrms->dst) { + if (prefix_same(pbrms->dst, prefix)) + return CMD_SUCCESS; - if (!pbrms->dst) - pbrms->dst = prefix_new(); + vty_out(vty, + "A `match dst-ip XX` command already exists, please remove that first\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + pbrms->dst = prefix_new(); prefix_copy(pbrms->dst, prefix); } else prefix_free(&pbrms->dst); @@ -183,12 +193,18 @@ DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd, #endif if (!no) { - if (pbrms->mark == (uint32_t) mark) - return CMD_SUCCESS; - pbrms->mark = (uint32_t) mark; - } else { + if (pbrms->mark) { + if (pbrms->mark == (uint32_t)mark) + return CMD_SUCCESS; + + vty_out(vty, + "A `match mark XX` command already exists, please remove that first\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + pbrms->mark = (uint32_t)mark; + } else pbrms->mark = 0; - } pbr_map_check(pbrms); @@ -232,7 +248,7 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, pbr_map_delete_nexthops(pbrms); else { vty_out(vty, - "Nexthop Group specified: %s does not exist to remove", + "Nexthop Group specified: %s does not exist to remove\n", name); return CMD_WARNING_CONFIG_FAILED; } @@ -240,7 +256,7 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, if (pbrms->nhgrp_name) { if (strcmp(name, pbrms->nhgrp_name) != 0) { vty_out(vty, - "Please delete current nexthop group before modifying current one"); + "Please delete current nexthop group before modifying current one\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -277,7 +293,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, if (pbrms->nhgrp_name) { vty_out(vty, - "Please unconfigure the nexthop group before adding an individual nexthop"); + "Please unconfigure the nexthop group before adding an individual nexthop\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -359,7 +375,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, if (pbrms->nhg->nexthop) { vty_out(vty, - "If you would like more than one nexthop please use nexthop-groups"); + "If you would like more than one nexthop please use nexthop-groups\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -508,6 +524,88 @@ DEFPY (show_pbr, return CMD_SUCCESS; } +static void vty_show_pbrms(struct vty *vty, + const struct pbr_map_sequence *pbrms, bool detail) +{ + char buf[PREFIX_STRLEN]; + char rbuf[64]; + + if (pbrms->reason) + pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf)); + + vty_out(vty, " Seq: %u rule: %u\n", pbrms->seqno, pbrms->ruleno); + + if (detail) + vty_out(vty, " Installed: %" PRIu64 "(%u) Reason: %s\n", + pbrms->installed, pbrms->unique, + pbrms->reason ? rbuf : "Valid"); + else + vty_out(vty, " Installed: %s Reason: %s\n", + pbrms->installed ? "yes" : "no", + pbrms->reason ? rbuf : "Valid"); + + if (pbrms->src) + vty_out(vty, " SRC Match: %s\n", + prefix2str(pbrms->src, buf, sizeof(buf))); + if (pbrms->dst) + vty_out(vty, " DST Match: %s\n", + prefix2str(pbrms->dst, buf, sizeof(buf))); + if (pbrms->mark) + vty_out(vty, " MARK Match: %u\n", pbrms->mark); + + if (pbrms->nhgrp_name) { + vty_out(vty, " Nexthop-Group: %s\n", pbrms->nhgrp_name); + + if (detail) + vty_out(vty, + " Installed: %u(%d) Tableid: %d\n", + pbrms->nhs_installed, + pbr_nht_get_installed(pbrms->nhgrp_name), + pbr_nht_get_table(pbrms->nhgrp_name)); + else + vty_out(vty, " Installed: %s Tableid: %d\n", + pbr_nht_get_installed(pbrms->nhgrp_name) ? "yes" + : "no", + pbr_nht_get_table(pbrms->nhgrp_name)); + + } else if (pbrms->nhg) { + vty_out(vty, " "); + nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop); + if (detail) + vty_out(vty, + " Installed: %u(%d) Tableid: %d\n", + pbrms->nhs_installed, + pbr_nht_get_installed(pbrms->internal_nhg_name), + pbr_nht_get_table(pbrms->internal_nhg_name)); + else + vty_out(vty, " Installed: %s Tableid: %d\n", + pbr_nht_get_installed(pbrms->internal_nhg_name) + ? "yes" + : "no", + pbr_nht_get_table(pbrms->internal_nhg_name)); + + } else if (pbrms->vrf_unchanged) { + vty_out(vty, " VRF Unchanged (use interface vrf)\n"); + } else if (pbrms->vrf_lookup) { + vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name); + } else { + vty_out(vty, " Nexthop-Group: Unknown Installed: no\n"); + } +} + +static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm, + bool detail) +{ + struct pbr_map_sequence *pbrms; + struct listnode *node; + + vty_out(vty, " pbr-map %s valid: %s\n", pbrm->name, + pbrm->valid ? "yes" : "no"); + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + vty_show_pbrms(vty, pbrms, detail); +} + DEFPY (show_pbr_map, show_pbr_map_cmd, "show pbr map [NAME$name] [detail$detail]", @@ -517,69 +615,13 @@ DEFPY (show_pbr_map, "PBR Map Name\n" "Detailed information\n") { - struct pbr_map_sequence *pbrms; struct pbr_map *pbrm; - struct listnode *node; - char buf[PREFIX_STRLEN]; - char rbuf[64]; RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { if (name && strcmp(name, pbrm->name) != 0) continue; - vty_out(vty, " pbr-map %s valid: %d\n", pbrm->name, - pbrm->valid); - - for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { - if (pbrms->reason) - pbr_map_reason_string(pbrms->reason, rbuf, - sizeof(rbuf)); - vty_out(vty, - " Seq: %u rule: %u Installed: %" PRIu64 "(%u) Reason: %s\n", - pbrms->seqno, pbrms->ruleno, pbrms->installed, - pbrms->unique, pbrms->reason ? rbuf : "Valid"); - - if (pbrms->src) - vty_out(vty, "\tSRC Match: %s\n", - prefix2str(pbrms->src, buf, - sizeof(buf))); - if (pbrms->dst) - vty_out(vty, "\tDST Match: %s\n", - prefix2str(pbrms->dst, buf, - sizeof(buf))); - if (pbrms->mark) - vty_out(vty, "\tMARK Match: %u\n", pbrms->mark); - - if (pbrms->nhgrp_name) { - vty_out(vty, - "\tNexthop-Group: %s(%u) Installed: %u(%d)\n", - pbrms->nhgrp_name, - pbr_nht_get_table(pbrms->nhgrp_name), - pbrms->nhs_installed, - pbr_nht_get_installed( - pbrms->nhgrp_name)); - } else if (pbrms->nhg) { - vty_out(vty, " "); - nexthop_group_write_nexthop( - vty, pbrms->nhg->nexthop); - vty_out(vty, - "\tInstalled: %u(%d) Tableid: %d\n", - pbrms->nhs_installed, - pbr_nht_get_installed( - pbrms->internal_nhg_name), - pbr_nht_get_table( - pbrms->internal_nhg_name)); - } else if (pbrms->vrf_unchanged) { - vty_out(vty, - "\tVRF Unchanged (use interface vrf)\n"); - } else if (pbrms->vrf_lookup) { - vty_out(vty, "\tVRF Lookup: %s\n", - pbrms->vrf_name); - } else { - vty_out(vty, - "\tNexthop-Group: Unknown Installed: 0(0)\n"); - } - } + vty_show_pbr_map(vty, pbrm, detail); } return CMD_SUCCESS; } diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index b0a689a7e4..ec0327d74f 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -234,23 +234,21 @@ static int rule_notify_owner(ZAPI_CALLBACK_ARGS) switch (note) { case ZAPI_RULE_FAIL_INSTALL: pbrms->installed &= ~installed; - DEBUGD(&pbr_dbg_zebra, - "%s: Received RULE_FAIL_INSTALL: %" PRIu64, - __PRETTY_FUNCTION__, pbrms->installed); break; case ZAPI_RULE_INSTALLED: pbrms->installed |= installed; - DEBUGD(&pbr_dbg_zebra, "%s: Received RULE_INSTALLED: %" PRIu64, - __PRETTY_FUNCTION__, pbrms->installed); break; case ZAPI_RULE_FAIL_REMOVE: + /* Don't change state on rule removal failure */ + break; case ZAPI_RULE_REMOVED: pbrms->installed &= ~installed; - DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED: %" PRIu64, - __PRETTY_FUNCTION__, pbrms->installed); break; } + DEBUGD(&pbr_dbg_zebra, "%s: Received %s: %" PRIu64, __func__, + zapi_rule_notify_owner2str(note), pbrms->installed); + pbr_map_final_interface_deletion(pbrms->parent, pmi); return 0; @@ -281,6 +279,7 @@ static void route_add_helper(struct zapi_route *api, struct nexthop_group nhg, api_nh = &api->nexthops[i]; api_nh->vrf_id = nhop->vrf_id; api_nh->type = nhop->type; + api_nh->weight = nhop->weight; switch (nhop->type) { case NEXTHOP_TYPE_IPV4: api_nh->gate.ipv4 = nhop->gate.ipv4; diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index ca86017f10..7375e6d9b8 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1989,9 +1989,9 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, json = json_object_new_object(); } else { vty_out(vty, - "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN"); + "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted"); vty_out(vty, - "\nInstalled Source Group IIF OIL\n"); + "\nActive Source Group RPT IIF OIL\n"); } for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { @@ -2001,8 +2001,16 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, char out_ifname[INTERFACE_NAMSIZ + 1]; int oif_vif_index; struct interface *ifp_in; + bool isRpt; first_oif = 1; + if ((c_oil->up && + PIM_UPSTREAM_FLAG_TEST_USE_RPT(c_oil->up->flags)) || + c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) + isRpt = true; + else + isRpt = false; + pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str)); pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, src_str, @@ -2056,6 +2064,12 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, json_ifp_in); json_object_int_add(json_source, "Installed", c_oil->installed); + if (isRpt) + json_object_boolean_true_add( + json_source, "isRpt"); + else + json_object_boolean_false_add( + json_source, "isRpt"); json_object_int_add(json_source, "RefCount", c_oil->oil_ref_count); json_object_int_add(json_source, "OilListSize", @@ -2074,8 +2088,9 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, c_oil->cc.wrong_if); } } else { - vty_out(vty, "%-9d %-15s %-15s %-16s ", - c_oil->installed, src_str, grp_str, in_ifname); + vty_out(vty, "%-6d %-15s %-15s %-3s %-16s ", + c_oil->installed, src_str, grp_str, + isRpt ? "y" : "n", in_ifname); } for (oif_vif_index = 0; oif_vif_index < MAXVIFS; @@ -2118,7 +2133,8 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, } else { if (first_oif) { first_oif = 0; - vty_out(vty, "%s(%c%c%c%c)", out_ifname, + vty_out(vty, "%s(%c%c%c%c%c)", + out_ifname, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) ? 'I' @@ -2134,9 +2150,13 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) ? '*' + : ' ', + (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_MUTE) + ? 'M' : ' '); } else - vty_out(vty, ", %s(%c%c%c%c)", + vty_out(vty, ", %s(%c%c%c%c%c)", out_ifname, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) @@ -2153,6 +2173,10 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) ? '*' + : ' ', + (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_MUTE) + ? 'M' : ' '); } } @@ -2554,7 +2578,7 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, } } -static void pim_show_join_desired_helper(struct pim_instance *pim, +static void pim_show_channel_helper(struct pim_instance *pim, struct vty *vty, struct pim_interface *pim_ifp, struct pim_ifchannel *ch, @@ -2613,7 +2637,7 @@ static void pim_show_join_desired_helper(struct pim_instance *pim, } } -static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, +static void pim_show_channel(struct pim_instance *pim, struct vty *vty, bool uj) { struct pim_interface *pim_ifp; @@ -2637,7 +2661,7 @@ static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { /* scan all interfaces */ - pim_show_join_desired_helper(pim, vty, pim_ifp, ch, + pim_show_channel_helper(pim, vty, pim_ifp, ch, json, uj); } } @@ -2649,6 +2673,73 @@ static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, } } +static void pim_show_join_desired_helper(struct pim_instance *pim, + struct vty *vty, + struct pim_upstream *up, + json_object *json, bool uj) +{ + json_object *json_group = NULL; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + json_object *json_row = NULL; + + pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str)); + + if (uj) { + json_object_object_get_ex(json, grp_str, &json_group); + + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); + } + + json_row = json_object_new_object(); + json_object_pim_upstream_add(json_row, up); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + + if (pim_upstream_evaluate_join_desired(pim, up)) + json_object_boolean_true_add(json_row, + "evaluateJoinDesired"); + + json_object_object_add(json_group, src_str, json_row); + + } else { + vty_out(vty, "%-15s %-15s %-6s\n", + src_str, grp_str, + pim_upstream_evaluate_join_desired(pim, up) ? "yes" + : "no"); + } +} + +static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, + bool uj) +{ + struct listnode *upnode; + struct pim_upstream *up; + + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); + else + vty_out(vty, + "Source Group EvalJD\n"); + + for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + /* scan all interfaces */ + pim_show_join_desired_helper(pim, vty, up, + json, uj); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + static void pim_show_upstream_rpf(struct pim_instance *pim, struct vty *vty, bool uj) { @@ -4438,6 +4529,90 @@ DEFUN (show_ip_pim_join_vrf_all, return CMD_WARNING; } +static void pim_show_jp_agg_helper(struct vty *vty, + struct interface *ifp, + struct pim_neighbor *neigh, + struct pim_upstream *up, + int is_join) +{ + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + char rpf_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str)); + /* pius->address.s_addr */ + pim_inet4_dump("<rpf?>", neigh->source_addr, rpf_str, sizeof(rpf_str)); + + vty_out(vty, "%-16s %-15s %-15s %-15s %5s\n", + ifp->name, rpf_str, src_str, + grp_str, is_join?"J":"P"); +} + +static void pim_show_jp_agg_list(struct pim_instance *pim, struct vty *vty) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + struct listnode *n_node; + struct pim_neighbor *neigh; + struct listnode *jag_node; + struct pim_jp_agg_group *jag; + struct listnode *js_node; + struct pim_jp_sources *js; + + vty_out(vty, + "Interface RPF Nbr Source Group State\n"); + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, + n_node, neigh)) { + for (ALL_LIST_ELEMENTS_RO(neigh->upstream_jp_agg, + jag_node, jag)) { + for (ALL_LIST_ELEMENTS_RO(jag->sources, + js_node, js)) { + pim_show_jp_agg_helper(vty, + ifp, neigh, js->up, + js->is_join); + } + } + } + } +} + +DEFPY (show_ip_pim_jp_agg, + show_ip_pim_jp_agg_cmd, + "show ip pim [vrf NAME] jp-agg", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "join prune aggregation list\n") +{ + struct vrf *v; + struct pim_instance *pim; + + v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME); + + if (!v) { + vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf); + return CMD_WARNING; + } + pim = pim_get_pim_instance(v->vrf_id); + + if (!pim) { + vty_out(vty, "%% Unable to find pim instance\n"); + return CMD_WARNING; + } + + pim_show_jp_agg_list(pim, vty); + + return CMD_SUCCESS; +} + DEFUN (show_ip_pim_local_membership, show_ip_pim_local_membership_cmd, "show ip pim [vrf NAME] local-membership [json]", @@ -4702,6 +4877,28 @@ DEFUN (show_ip_pim_upstream_vrf_all, return CMD_SUCCESS; } +DEFUN (show_ip_pim_channel, + show_ip_pim_channel_cmd, + "show ip pim [vrf NAME] channel [json]", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "PIM downstream channel info\n" + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + pim_show_channel(vrf->info, vty, uj); + + return CMD_SUCCESS; +} + DEFUN (show_ip_pim_upstream_join_desired, show_ip_pim_upstream_join_desired_cmd, "show ip pim [vrf NAME] upstream-join-desired [json]", @@ -5293,6 +5490,16 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, if (ttl < 1) continue; + /* do not display muted OIFs */ + if (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_MUTE) + continue; + + if (c_oil->oil.mfcc_parent == oif_vif_index && + !pim_mroute_allow_iif_in_oil(c_oil, + oif_vif_index)) + continue; + ifp_out = pim_if_find_by_vif_index(pim, oif_vif_index); pim_time_uptime( mroute_uptime, sizeof(mroute_uptime), @@ -10344,6 +10551,7 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_interface_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_pim_join_cmd); install_element(VIEW_NODE, &show_ip_pim_join_vrf_all_cmd); + install_element(VIEW_NODE, &show_ip_pim_jp_agg_cmd); install_element(VIEW_NODE, &show_ip_pim_local_membership_cmd); install_element(VIEW_NODE, &show_ip_pim_neighbor_cmd); install_element(VIEW_NODE, &show_ip_pim_neighbor_vrf_all_cmd); @@ -10354,6 +10562,7 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_state_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_vrf_all_cmd); + install_element(VIEW_NODE, &show_ip_pim_channel_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_rpf_cmd); install_element(VIEW_NODE, &show_ip_pim_rp_cmd); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 3ee9caebcf..c615540149 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -1481,6 +1481,13 @@ void pim_if_create_pimreg(struct pim_instance *pim) pim_if_new(pim->regiface, false, false, true, false /*vxlan_term*/); + /* + * On vrf moves we delete the interface if there + * is nothing going on with it. We cannot have + * the pimregiface deleted. + */ + pim->regiface->configured = true; + } } @@ -1581,6 +1588,7 @@ int pim_ifp_create(struct interface *ifp) int pim_ifp_up(struct interface *ifp) { + struct pim_interface *pim_ifp; struct pim_instance *pim; uint32_t table_id; @@ -1593,24 +1601,21 @@ int pim_ifp_up(struct interface *ifp) } pim = pim_get_pim_instance(ifp->vrf_id); - if (if_is_operative(ifp)) { - struct pim_interface *pim_ifp; - pim_ifp = ifp->info; - /* - * If we have a pim_ifp already and this is an if_add - * that means that we probably have a vrf move event - * If that is the case, set the proper vrfness. - */ - if (pim_ifp) - pim_ifp->pim = pim; + pim_ifp = ifp->info; + /* + * If we have a pim_ifp already and this is an if_add + * that means that we probably have a vrf move event + * If that is the case, set the proper vrfness. + */ + if (pim_ifp) + pim_ifp->pim = pim; - /* - pim_if_addr_add_all() suffices for bringing up both IGMP and - PIM - */ - pim_if_addr_add_all(ifp); - } + /* + pim_if_addr_add_all() suffices for bringing up both IGMP and + PIM + */ + pim_if_addr_add_all(ifp); /* * If we have a pimreg device callback and it's for a specific diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 48bd031f1e..3a7eb45f27 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -147,10 +147,11 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) ch->upstream, ch, ch->parent)) pim_channel_add_oif(ch->upstream->channel_oil, ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + PIM_OIF_FLAG_PROTO_STAR, + __func__); pim_channel_del_oif(ch->upstream->channel_oil, - ch->interface, mask); + ch->interface, mask, __func__); /* * Do we have any S,G's that are inheriting? * Nuke from on high too. @@ -161,9 +162,10 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) for (ALL_LIST_ELEMENTS_RO(ch->upstream->sources, up_node, child)) - pim_channel_del_oif(child->channel_oil, - ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + pim_channel_del_inherited_oif( + child->channel_oil, + ch->interface, + __func__); } } @@ -179,9 +181,7 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) listnode_delete(ch->upstream->ifchannels, ch); - if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) { - pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream); - } + pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream); /* upstream is common across ifchannels, check if upstream's ifchannel list is empty before deleting upstream_del @@ -250,6 +250,7 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, { enum pim_ifjoin_state old_state = ch->ifjoin_state; struct pim_interface *pim_ifp = ch->interface->info; + struct pim_ifchannel *child_ch; if (PIM_DEBUG_PIM_EVENTS) zlog_debug( @@ -293,28 +294,18 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, if (!c_oil) continue; - if (!pim_upstream_evaluate_join_desired( - pim_ifp->pim, child)) { - pim_channel_del_oif( - c_oil, ch->interface, - PIM_OIF_FLAG_PROTO_STAR); - pim_upstream_update_join_desired( - pim_ifp->pim, child); - } - /* * If the S,G has no if channel and the * c_oil still * has output here then the *,G was * supplying the implied * if channel. So remove it. - * I think this is dead code now. is it? */ if (c_oil->oil.mfcc_ttls [pim_ifp->mroute_vif_index]) - pim_channel_del_oif( + pim_channel_del_inherited_oif( c_oil, ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + __func__); } } if (ch->ifjoin_state == PIM_IFJOIN_JOIN) { @@ -328,12 +319,19 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, child->sg_str, up->sg_str); - if (pim_upstream_evaluate_join_desired( - pim_ifp->pim, child)) { + /* check if the channel can be + * inherited into the SG's OIL + */ + child_ch = pim_ifchannel_find( + ch->interface, + &child->sg); + if (pim_upstream_eval_inherit_if( + child, child_ch, ch)) { pim_channel_add_oif( child->channel_oil, ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + PIM_OIF_FLAG_PROTO_STAR, + __func__); pim_upstream_update_join_desired( pim_ifp->pim, child); } @@ -783,6 +781,28 @@ static int nonlocal_upstream(int is_join, struct interface *recv_ifp, return 1; /* non-local */ } +static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel *ch, + struct pim_interface *pim_ifp) +{ + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, + PIM_IFJOIN_JOIN); + PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags); + /* check if the interface qualifies as an immediate + * OIF + */ + if (pim_upstream_evaluate_join_desired_interface( + ch->upstream, ch, + NULL /*starch*/)) { + pim_channel_add_oif(ch->upstream->channel_oil, + ch->interface, + PIM_OIF_FLAG_PROTO_PIM, + __func__); + pim_upstream_update_join_desired(pim_ifp->pim, + ch->upstream); + } +} + + void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, struct in_addr upstream, struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime) @@ -890,25 +910,8 @@ void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, if (source_flags & PIM_ENCODE_RPT_BIT) pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); - else { - /* - * We have received a S,G join and we are in - * S,G RPT Prune state. Which means we need - * to transition to Join state and setup - * state as appropriate. - */ - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, - PIM_IFJOIN_JOIN); - PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags); - if (pim_upstream_evaluate_join_desired(pim_ifp->pim, - ch->upstream)) { - pim_channel_add_oif(ch->upstream->channel_oil, - ch->interface, - PIM_OIF_FLAG_PROTO_PIM); - pim_upstream_update_join_desired(pim_ifp->pim, - ch->upstream); - } - } + else + pim_ifchannel_ifjoin_handler(ch, pim_ifp); break; case PIM_IFJOIN_PRUNE_PENDING: THREAD_OFF(ch->t_ifjoin_prune_pending_timer); @@ -916,9 +919,9 @@ void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, THREAD_OFF(ch->t_ifjoin_expiry_timer); pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); - } else - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, - PIM_IFJOIN_JOIN); + } else { + pim_ifchannel_ifjoin_handler(ch, pim_ifp); + } break; case PIM_IFJOIN_PRUNE_TMP: break; @@ -1105,9 +1108,9 @@ int pim_ifchannel_local_membership_add(struct interface *ifp, if (pim_upstream_evaluate_join_desired_interface( child, ch, starch)) { pim_channel_add_oif(child->channel_oil, ifp, - PIM_OIF_FLAG_PROTO_STAR); - pim_upstream_switch(pim, child, - PIM_UPSTREAM_JOINED); + PIM_OIF_FLAG_PROTO_STAR, + __func__); + pim_upstream_update_join_desired(pim, child); } } @@ -1124,12 +1127,14 @@ int pim_ifchannel_local_membership_add(struct interface *ifp, == PREFIX_DENY) { pim_channel_add_oif( up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); } } } else pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); } return 1; @@ -1174,22 +1179,19 @@ void pim_ifchannel_local_membership_del(struct interface *ifp, child->sg_str); ch = pim_ifchannel_find(ifp, &child->sg); - if (c_oil - && !pim_upstream_evaluate_join_desired_interface( - child, ch, starch)) - pim_channel_del_oif(c_oil, ifp, - PIM_OIF_FLAG_PROTO_STAR); - /* * If the S,G has no if channel and the c_oil still * has output here then the *,G was supplying the * implied * if channel. So remove it. */ - if (!chchannel && c_oil - && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]) - pim_channel_del_oif(c_oil, ifp, - PIM_OIF_FLAG_PROTO_STAR); + if (!pim_upstream_evaluate_join_desired_interface( + child, ch, starch) || + (!chchannel && + c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])) { + pim_channel_del_inherited_oif(c_oil, ifp, + __func__); + } /* Child node removal/ref count-- will happen as part of * parent' delete_no_info */ @@ -1408,12 +1410,10 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom, child->upstream))) { pim_channel_add_oif( child->upstream->channel_oil, - ch->interface, PIM_OIF_FLAG_PROTO_STAR); - pim_upstream_switch(pim, child->upstream, - PIM_UPSTREAM_JOINED); - pim_jp_agg_single_upstream_send( - &child->upstream->rpf, child->upstream, - true); + ch->interface, PIM_OIF_FLAG_PROTO_STAR, + __func__); + pim_upstream_update_join_desired(pim, + child->upstream); } send_upstream_starg = true; diff --git a/pimd/pim_join.c b/pimd/pim_join.c index 89be42842e..fbb547c80e 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -327,7 +327,6 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, recv_prune(ifp, neigh, msg_holdtime, msg_upstream_addr.u.prefix4, &sg, msg_source_flags); - /* * So if we are receiving a S,G,RPT prune * before we have any data for that S,G @@ -348,10 +347,10 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, "%s: SGRpt flag is set, del inherit oif from up %s", __PRETTY_FUNCTION__, up->sg_str); - pim_channel_del_oif( + pim_channel_del_inherited_oif( up->channel_oil, starg_ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + __func__); } } } diff --git a/pimd/pim_jp_agg.c b/pimd/pim_jp_agg.c index 418f66df05..2662c9eae6 100644 --- a/pimd/pim_jp_agg.c +++ b/pimd/pim_jp_agg.c @@ -117,10 +117,16 @@ void pim_jp_agg_clear_group(struct list *group) static struct pim_iface_upstream_switch * pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf) { - struct pim_interface *pim_ifp = rpf->source_nexthop.interface->info; + struct interface *ifp = rpf->source_nexthop.interface; + struct pim_interface *pim_ifp; struct pim_iface_upstream_switch *pius; struct listnode *node, *nnode; + if (!ifp) + return NULL; + + pim_ifp = ifp->info; + /* Old interface is pim disabled */ if (!pim_ifp) return NULL; @@ -142,7 +148,8 @@ pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf) return pius; } -void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up) +void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up, + struct pim_neighbor *nbr) { struct listnode *node, *nnode; struct pim_jp_agg_group *jag = NULL; @@ -161,6 +168,20 @@ void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up) break; } + if (nbr) { + if (PIM_DEBUG_TRACE) { + char src_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("<src?>", nbr->source_addr, src_str, + sizeof(src_str)); + zlog_debug( + "up %s remove from nbr %s/%s jp-agg-list", + up->sg_str, + nbr->interface->name, + src_str); + } + } + if (js) { js->up = NULL; listnode_delete(jag->sources, js); @@ -248,7 +269,7 @@ void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore) } void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up, - bool is_join) + bool is_join, struct pim_neighbor *nbr) { struct listnode *node, *nnode; struct pim_jp_agg_group *jag = NULL; @@ -274,6 +295,20 @@ void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up, break; } + if (nbr) { + if (PIM_DEBUG_TRACE) { + char src_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("<src?>", nbr->source_addr, src_str, + sizeof(src_str)); + zlog_debug( + "up %s add to nbr %s/%s jp-agg-list", + up->sg_str, + up->rpf.source_nexthop.interface->name, + src_str); + } + } + if (!js) { js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE, sizeof(struct pim_jp_sources)); @@ -314,10 +349,11 @@ void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf, /* send Prune(S,G) to the old upstream neighbor */ if (opius) - pim_jp_agg_add_group(opius->us, up, false); + pim_jp_agg_add_group(opius->us, up, false, NULL); /* send Join(S,G) to the current upstream neighbor */ - pim_jp_agg_add_group(npius->us, up, true); + if (npius) + pim_jp_agg_add_group(npius->us, up, true, NULL); } @@ -331,10 +367,10 @@ void pim_jp_agg_single_upstream_send(struct pim_rpf *rpf, static bool first = true; /* skip JP upstream messages if source is directly connected */ - if (!up || !rpf->source_nexthop.interface || pim_if_connected_to_source( - rpf->source_nexthop - .interface, - up->sg.src)) + if (!up || !rpf->source_nexthop.interface || + pim_if_connected_to_source(rpf->source_nexthop.interface, + up->sg.src) || + if_is_loopback_or_vrf(rpf->source_nexthop.interface)) return; if (first) { diff --git a/pimd/pim_jp_agg.h b/pimd/pim_jp_agg.h index aa21aa9816..d88ff8892b 100644 --- a/pimd/pim_jp_agg.h +++ b/pimd/pim_jp_agg.h @@ -37,10 +37,11 @@ void pim_jp_agg_group_list_free(struct pim_jp_agg_group *jag); int pim_jp_agg_group_list_cmp(void *arg1, void *arg2); void pim_jp_agg_clear_group(struct list *group); -void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up); +void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up, + struct pim_neighbor *nbr); void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up, - bool is_join); + bool is_join, struct pim_neighbor *nbr); void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf, struct pim_upstream *up); diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 3f79b304e5..f0ca367258 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -29,7 +29,6 @@ #include "memory.h" #include "vrf.h" -#include "memory_vty.h" #include "filter.h" #include "vty.h" #include "sigevent.h" diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 2859d26eba..1fe2289a8e 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -207,7 +207,7 @@ static int pim_mroute_msg_nocache(int fd, struct interface *ifp, up = pim_upstream_find_or_add( &sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, __PRETTY_FUNCTION__); - pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); return 0; } @@ -228,16 +228,10 @@ static int pim_mroute_msg_nocache(int fd, struct interface *ifp, pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time); up->channel_oil->cc.pktcnt++; - PIM_UPSTREAM_FLAG_SET_FHR(up->flags); // resolve mfcc_parent prior to mroute_add in channel_add_oif if (up->rpf.source_nexthop.interface && up->channel_oil->oil.mfcc_parent >= MAXVIFS) { - int vif_index = 0; - vif_index = pim_if_find_vifindex_by_ifindex( - pim_ifp->pim, - up->rpf.source_nexthop.interface->ifindex); - pim_channel_oil_change_iif(pim_ifp->pim, up->channel_oil, - vif_index, __PRETTY_FUNCTION__); + pim_upstream_mroute_iif_update(up->channel_oil, __func__); } pim_register_join(up); @@ -283,8 +277,7 @@ static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, pim_upstream_keep_alive_timer_start( up, pim_ifp->pim->keep_alive_time); pim_upstream_inherited_olist(pim_ifp->pim, up); - pim_upstream_switch(pim_ifp->pim, up, - PIM_UPSTREAM_JOINED); + pim_upstream_update_join_desired(pim_ifp->pim, up); if (PIM_DEBUG_MROUTE) zlog_debug("%s: Creating %s upstream on LHR", @@ -330,6 +323,15 @@ static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, pim_str_sg_dump(&sg)); return 0; } + + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) { + if (PIM_DEBUG_PIM_REG) + zlog_debug( + "%s register forward skipped, not FHR", + up->sg_str); + return 0; + } + pim_register_send((uint8_t *)buf + sizeof(struct ip), ntohs(ip_hdr->ip_len) - sizeof(struct ip), pim_ifp->primary_address, rpg, 0, up); @@ -518,7 +520,7 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, pim_upstream_inherited_olist(pim_ifp->pim, up); if (!up->channel_oil->installed) - pim_mroute_add(up->channel_oil, + pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); } else { if (I_am_RP(pim_ifp->pim, up->sg.grp)) { @@ -557,6 +559,8 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, up->channel_oil->cc.pktcnt++; pim_register_join(up); pim_upstream_inherited_olist(pim_ifp->pim, up); + if (!up->channel_oil->installed) + pim_upstream_mroute_add(up->channel_oil, __func__); // Send the packet to the RP pim_mroute_msg_wholepkt(fd, ifp, buf); @@ -565,7 +569,8 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, __PRETTY_FUNCTION__, NULL); if (!up->channel_oil->installed) - pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, + __PRETTY_FUNCTION__); } return 0; @@ -882,72 +887,92 @@ int pim_mroute_del_vif(struct interface *ifp) return 0; } -int pim_mroute_add(struct channel_oil *c_oil, const char *name) +/* + * Prevent creating MFC entry with OIF=IIF. + * + * This is a protection against implementation mistakes. + * + * PIM protocol implicitely ensures loopfree multicast topology. + * + * IGMP must be protected against adding looped MFC entries created + * by both source and receiver attached to the same interface. See + * TODO T22. + * We shall allow igmp to create upstream when it is DR for the intf. + * Assume RP reachable via non DR. + */ +bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil, + int oif_index) +{ +#ifdef PIM_ENFORCE_LOOPFREE_MFC + struct interface *ifp_out; + struct pim_interface *pim_ifp; + + if (c_oil->up && + PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags)) + return true; + + ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index); + if (!ifp_out) + return false; + pim_ifp = ifp_out->info; + if (!pim_ifp) + return false; + if ((c_oil->oif_flags[oif_index] & PIM_OIF_FLAG_PROTO_IGMP) && + PIM_I_am_DR(pim_ifp)) + return true; + + return false; +#else + return true; +#endif +} + +static inline void pim_mroute_copy(struct mfcctl *oil, + struct channel_oil *c_oil) +{ + int i; + + oil->mfcc_origin = c_oil->oil.mfcc_origin; + oil->mfcc_mcastgrp = c_oil->oil.mfcc_mcastgrp; + oil->mfcc_parent = c_oil->oil.mfcc_parent; + + for (i = 0; i < MAXVIFS; ++i) { + if ((oil->mfcc_parent == i) && + !pim_mroute_allow_iif_in_oil(c_oil, i)) { + oil->mfcc_ttls[i] = 0; + continue; + } + + if (c_oil->oif_flags[i] & PIM_OIF_FLAG_MUTE) + oil->mfcc_ttls[i] = 0; + else + oil->mfcc_ttls[i] = c_oil->oil.mfcc_ttls[i]; + } +} + +/* This function must not be called directly 0 + * use pim_upstream_mroute_add or pim_static_mroute_add instead + */ +static int pim_mroute_add(struct channel_oil *c_oil, const char *name) { struct pim_instance *pim = c_oil->pim; + struct mfcctl tmp_oil; int err; - int orig = 0; - int orig_iif_vif = 0; - struct pim_interface *pim_reg_ifp = NULL; - int orig_pimreg_ttl = 0; - bool pimreg_ttl_reset = false; - struct pim_interface *vxlan_ifp = NULL; - int orig_term_ttl = 0; - bool orig_term_ttl_reset = false; pim->mroute_add_last = pim_time_monotonic_sec(); ++pim->mroute_add_events; - /* Do not install route if incoming interface is undefined. */ - if (c_oil->oil.mfcc_parent >= MAXVIFS) { - if (PIM_DEBUG_MROUTE) { - char buf[1000]; - zlog_debug( - "%s(%s) %s Attempting to add vifi that is invalid to mroute table", - __PRETTY_FUNCTION__, name, - pim_channel_oil_dump(c_oil, buf, sizeof(buf))); - } - return -2; - } + /* Copy the oil to a temporary structure to fixup (without need to + * later restore) before sending the mroute add to the dataplane + */ + pim_mroute_copy(&tmp_oil, c_oil); /* The linux kernel *expects* the incoming * vif to be part of the outgoing list * in the case of a (*,G). */ if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) { - orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent]; - c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1; - } - - if (c_oil->up) { - /* suppress pimreg in the OIL if the mroute is not supposed to - * trigger register encapsulated data - */ - if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags)) { - pim_reg_ifp = pim->regiface->info; - orig_pimreg_ttl = - c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index]; - c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = 0; - /* remember to flip it back after MFC programming */ - pimreg_ttl_reset = true; - } - - vxlan_ifp = pim_vxlan_get_term_ifp(pim); - /* 1. vxlan termination device must never be added to the - * origination mroute (and that can actually happen because - * of XG inheritance from the termination mroute) otherwise - * traffic will end up looping. - * 2. vxlan termination device should be removed from the non-DF - * to prevent duplicates to the overlay rxer - */ - if (vxlan_ifp && - (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) || - PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags))) { - orig_term_ttl_reset = true; - orig_term_ttl = - c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index]; - c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = 0; - } + tmp_oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1; } /* @@ -959,33 +984,19 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name) */ if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY && c_oil->oil.mfcc_parent != 0) { - orig_iif_vif = c_oil->oil.mfcc_parent; - c_oil->oil.mfcc_parent = 0; + tmp_oil.mfcc_parent = 0; } err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC, - &c_oil->oil, sizeof(c_oil->oil)); + &tmp_oil, sizeof(tmp_oil)); if (!err && !c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY - && orig_iif_vif != 0) { - c_oil->oil.mfcc_parent = orig_iif_vif; + && c_oil->oil.mfcc_parent != 0) { + tmp_oil.mfcc_parent = c_oil->oil.mfcc_parent; err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC, - &c_oil->oil, sizeof(c_oil->oil)); - } - - if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) - c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig; - - if (pimreg_ttl_reset) { - assert(pim_reg_ifp); - c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = - orig_pimreg_ttl; + &tmp_oil, sizeof(tmp_oil)); } - if (orig_term_ttl_reset) - c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = - orig_term_ttl; - if (err) { zlog_warn( "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s", @@ -1007,6 +1018,142 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name) return 0; } +static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil, + const char *name) +{ + vifi_t iif = MAXVIFS; + struct interface *ifp = NULL; + struct pim_interface *pim_ifp; + struct pim_upstream *up = c_oil->up; + + if (up) { + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) { + if (up->parent) + ifp = up->parent->rpf.source_nexthop.interface; + } else { + ifp = up->rpf.source_nexthop.interface; + } + if (ifp) { + pim_ifp = (struct pim_interface *)ifp->info; + if (pim_ifp) + iif = pim_ifp->mroute_vif_index; + } + } + return iif; +} + +static int pim_upstream_mroute_update(struct channel_oil *c_oil, + const char *name) +{ + char buf[1000]; + + if (c_oil->oil.mfcc_parent >= MAXVIFS) { + /* the c_oil cannot be installed as a mroute yet */ + if (PIM_DEBUG_MROUTE) + zlog_debug( + "%s(%s) %s mroute not ready to be installed; %s", + __func__, name, + pim_channel_oil_dump(c_oil, buf, + sizeof(buf)), + c_oil->installed ? + "uninstall" : "skip"); + /* if already installed flush it out as we are going to stop + * updates to it leaving it in a stale state + */ + if (c_oil->installed) + pim_mroute_del(c_oil, name); + /* return success (skipped) */ + return 0; + } + + return pim_mroute_add(c_oil, name); +} + +/* IIF associated with SGrpt entries are re-evaluated when the parent + * (*,G) entries IIF changes + */ +static void pim_upstream_all_sources_iif_update(struct pim_upstream *up) +{ + struct listnode *listnode; + struct pim_upstream *child; + + for (ALL_LIST_ELEMENTS_RO(up->sources, listnode, + child)) { + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) + pim_upstream_mroute_iif_update(child->channel_oil, + __func__); + } +} + +/* In the case of "PIM state machine" added mroutes an upstream entry + * must be present to decide on the SPT-forwarding vs. RPT-forwarding. + */ +int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name) +{ + vifi_t iif; + + iif = pim_upstream_get_mroute_iif(c_oil, name); + + if (c_oil->oil.mfcc_parent != iif) { + c_oil->oil.mfcc_parent = iif; + if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY && + c_oil->up) + pim_upstream_all_sources_iif_update(c_oil->up); + } else { + c_oil->oil.mfcc_parent = iif; + } + + return pim_upstream_mroute_update(c_oil, name); +} + +/* Look for IIF changes and update the dateplane entry only if the IIF + * has changed. + */ +int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name) +{ + vifi_t iif; + char buf[1000]; + + iif = pim_upstream_get_mroute_iif(c_oil, name); + if (c_oil->oil.mfcc_parent == iif) { + /* no change */ + return 0; + } + c_oil->oil.mfcc_parent = iif; + + if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY && + c_oil->up) + pim_upstream_all_sources_iif_update(c_oil->up); + + if (PIM_DEBUG_MROUTE_DETAIL) + zlog_debug("%s(%s) %s mroute iif update %d", + __func__, name, + pim_channel_oil_dump(c_oil, buf, + sizeof(buf)), iif); + /* XXX: is this hack needed? */ + c_oil->oil_inherited_rescan = 1; + return pim_upstream_mroute_update(c_oil, name); +} + +int pim_static_mroute_add(struct channel_oil *c_oil, const char *name) +{ + return pim_mroute_add(c_oil, name); +} + +void pim_static_mroute_iif_update(struct channel_oil *c_oil, + int input_vif_index, + const char *name) +{ + if (c_oil->oil.mfcc_parent == input_vif_index) + return; + + c_oil->oil.mfcc_parent = input_vif_index; + if (input_vif_index == MAXVIFS) + pim_mroute_del(c_oil, name); + else + pim_static_mroute_add(c_oil, name); +} + int pim_mroute_del(struct channel_oil *c_oil, const char *name) { struct pim_instance *pim = c_oil->pim; diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h index bd71acbf82..2d8e1b01fb 100644 --- a/pimd/pim_mroute.h +++ b/pimd/pim_mroute.h @@ -174,8 +174,15 @@ int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags); int pim_mroute_del_vif(struct interface *ifp); -int pim_mroute_add(struct channel_oil *c_oil, const char *name); +int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name); +int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name); +int pim_static_mroute_add(struct channel_oil *c_oil, const char *name); +void pim_static_mroute_iif_update(struct channel_oil *c_oil, + int input_vif_index, + const char *name); int pim_mroute_del(struct channel_oil *c_oil, const char *name); void pim_mroute_update_counters(struct channel_oil *c_oil); +bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil, + int oif_index); #endif /* PIM_MROUTE_H */ diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 74a3a9836b..8a18594fd7 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -126,7 +126,12 @@ static void pim_msdp_sa_upstream_del(struct pim_msdp_sa *sa) if (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags)) { PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(up->flags); sa->flags |= PIM_MSDP_SAF_UP_DEL_IN_PROG; - pim_upstream_del(sa->pim, up, __PRETTY_FUNCTION__); + up = pim_upstream_del(sa->pim, up, __PRETTY_FUNCTION__); + /* re-eval joinDesired; clearing peer-msdp-sa flag can + * cause JD to change + */ + if (up) + pim_upstream_update_join_desired(sa->pim, up); sa->flags &= ~PIM_MSDP_SAF_UP_DEL_IN_PROG; } @@ -445,10 +450,9 @@ static bool pim_msdp_sa_local_add_ok(struct pim_upstream *up) return false; } - if (!up->t_ka_timer) { + if (!pim_upstream_is_kat_running(up)) /* stream is not active */ return false; - } if (!I_am_RP(pim, up->sg.grp)) { /* we are not RP for the group */ diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c index 2e467502b1..fba76d84dd 100644 --- a/pimd/pim_msg.c +++ b/pimd/pim_msg.c @@ -126,8 +126,17 @@ size_t pim_msg_get_jp_group_size(struct list *sources) __PRETTY_FUNCTION__, up->sg_str); for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) { - if (child->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) { - if (!pim_rpf_is_same(&up->rpf, &child->rpf)) { + if (!PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) { + /* If we are using SPT and the SPT and RPT IIFs + * are different we can prune the source off + * of the RPT. + * If RPF_interface(S) is not resolved hold + * decision to prune as SPT may end up on the + * same IIF as RPF_interface(RP). + */ + if (child->rpf.source_nexthop.interface && + !pim_rpf_is_same(&up->rpf, + &child->rpf)) { size += sizeof( struct pim_encoded_source_ipv4); PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( @@ -143,37 +152,25 @@ size_t pim_msg_get_jp_group_size(struct list *sources) "%s: SPT Bit and RPF'(%s) == RPF'(S,G): Not adding Prune for (%s,rpt)", __PRETTY_FUNCTION__, up->sg_str, child->sg_str); - } else if (pim_upstream_is_sg_rpt(child)) { - if (pim_upstream_empty_inherited_olist(child)) { - size += sizeof( + } else if (pim_upstream_empty_inherited_olist(child)) { + /* S is supposed to be forwarded along the RPT + * but it's inherited OIL is empty. So just + * prune it off. + */ + size += sizeof( struct pim_encoded_source_ipv4); - PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( + PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( child->flags); - if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug( "%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message", __PRETTY_FUNCTION__, child->sg_str); - } else if (!pim_rpf_is_same(&up->rpf, - &child->rpf)) { - size += sizeof( - struct pim_encoded_source_ipv4); - PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( - child->flags); - if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( - "%s: RPF'(%s) != RPF'(%s,rpt), Add Prune to compound message", - __PRETTY_FUNCTION__, - up->sg_str, - child->sg_str); - } else if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( - "%s: RPF'(%s) == RPF'(%s,rpt), Do not add Prune to compound message", - __PRETTY_FUNCTION__, up->sg_str, - child->sg_str); } else if (PIM_DEBUG_PIM_PACKETS) - zlog_debug("%s: SPT bit is not set for (%s)", - __PRETTY_FUNCTION__, child->sg_str); + zlog_debug( + "%s: Do not add Prune %s to compound message %s", + __PRETTY_FUNCTION__, child->sg_str, + up->sg_str); } } return size; diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 7900e39231..bd8424b3e4 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -441,37 +441,22 @@ static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg) { struct pim_instance *pim = (struct pim_instance *)arg; struct pim_upstream *up = (struct pim_upstream *)bucket->data; - int vif_index = 0; enum pim_rpf_result rpf_result; struct pim_rpf old; old.source_nexthop.interface = up->rpf.source_nexthop.interface; - rpf_result = pim_rpf_update(pim, up, &old); - if (rpf_result == PIM_RPF_FAILURE) { - pim_upstream_rpf_clear(pim, up); - return HASHWALK_CONTINUE; - } - - /* update kernel multicast forwarding cache (MFC) */ - if (up->rpf.source_nexthop.interface) { - ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex; + rpf_result = pim_rpf_update(pim, up, &old, __func__); - vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex); - /* Pass Current selected NH vif index to mroute download - */ - if (vif_index) - pim_scan_individual_oil(up->channel_oil, vif_index); - else { - if (PIM_DEBUG_PIM_NHT) - zlog_debug( - "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid", - __PRETTY_FUNCTION__, up->sg_str, - up->rpf.source_nexthop.interface->name); - } - } + /* update kernel multicast forwarding cache (MFC); if the + * RPF nbr is now unreachable the MFC has already been updated + * by pim_rpf_clear + */ + if (rpf_result != PIM_RPF_FAILURE) + pim_upstream_mroute_iif_update(up->channel_oil, __func__); - if (rpf_result == PIM_RPF_CHANGED) + if (rpf_result == PIM_RPF_CHANGED || + (rpf_result == PIM_RPF_FAILURE && old.source_nexthop.interface)) pim_zebra_upstream_rpf_changed(pim, up, &old); @@ -480,7 +465,8 @@ static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg) __PRETTY_FUNCTION__, up->sg_str, pim->vrf->name, old.source_nexthop.interface ? old.source_nexthop.interface->name : "Unknown", - up->rpf.source_nexthop.interface->name); + up->rpf.source_nexthop.interface + ? up->rpf.source_nexthop.interface->name : "Unknown"); } return HASHWALK_CONTINUE; diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index d142934916..65c6bdd2bc 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -31,10 +31,13 @@ #include "pim_str.h" #include "pim_iface.h" #include "pim_time.h" +#include "pim_vxlan.h" // struct list *pim_channel_oil_list = NULL; // struct hash *pim_channel_oil_hash = NULL; +static void pim_channel_update_mute(struct channel_oil *c_oil); + char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size) { char *out; @@ -146,66 +149,31 @@ struct channel_oil *pim_find_channel_oil(struct pim_instance *pim, return c_oil; } -void pim_channel_oil_change_iif(struct pim_instance *pim, - struct channel_oil *c_oil, - int input_vif_index, - const char *name) -{ - int old_vif_index = c_oil->oil.mfcc_parent; - struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp, - .grp = c_oil->oil.mfcc_origin}; - - if (c_oil->oil.mfcc_parent == input_vif_index) { - if (PIM_DEBUG_MROUTE_DETAIL) - zlog_debug("%s(%s): Existing channel oil %pSG4 already using %d as IIF", - __PRETTY_FUNCTION__, name, &sg, - input_vif_index); - - return; - } - - if (PIM_DEBUG_MROUTE_DETAIL) - zlog_debug("%s(%s): Changing channel oil %pSG4 IIF from %d to %d installed: %d", - __PRETTY_FUNCTION__, name, &sg, - c_oil->oil.mfcc_parent, input_vif_index, - c_oil->installed); - - c_oil->oil.mfcc_parent = input_vif_index; - if (c_oil->installed) { - if (input_vif_index == MAXVIFS) - pim_mroute_del(c_oil, name); - else - pim_mroute_add(c_oil, name); - } else - if (old_vif_index == MAXVIFS) - pim_mroute_add(c_oil, name); - - return; -} - struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, struct prefix_sg *sg, - int input_vif_index, const char *name) + const char *name) { struct channel_oil *c_oil; - struct interface *ifp; c_oil = pim_find_channel_oil(pim, sg); if (c_oil) { - if (c_oil->oil.mfcc_parent != input_vif_index) { - c_oil->oil_inherited_rescan = 1; - if (PIM_DEBUG_MROUTE_DETAIL) - zlog_debug( - "%s: Existing channel oil %pSG4 points to %d, modifying to point at %d", - __PRETTY_FUNCTION__, sg, - c_oil->oil.mfcc_parent, - input_vif_index); - } - pim_channel_oil_change_iif(pim, c_oil, input_vif_index, - name); ++c_oil->oil_ref_count; - /* channel might be present prior to upstream */ - c_oil->up = pim_upstream_find(pim, sg); + + if (!c_oil->up) { + /* channel might be present prior to upstream */ + c_oil->up = pim_upstream_find( + pim, sg); + /* if the upstream entry is being anchored to an + * already existing channel OIL we need to re-evaluate + * the "Mute" state on AA OIFs + */ + pim_channel_update_mute(c_oil); + } + + /* check if the IIF has changed + * XXX - is this really needed + */ + pim_upstream_mroute_iif_update(c_oil, __func__); if (PIM_DEBUG_MROUTE) zlog_debug( @@ -215,23 +183,13 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, return c_oil; } - if (input_vif_index != MAXVIFS) { - ifp = pim_if_find_by_vif_index(pim, input_vif_index); - if (!ifp) { - /* warning only */ - zlog_warn( - "%s:%s (S,G)=%pSG4 could not find input interface for input_vif_index=%d", - __PRETTY_FUNCTION__, name, sg, input_vif_index); - } - } - c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil)); c_oil->oil.mfcc_mcastgrp = sg->grp; c_oil->oil.mfcc_origin = sg->src; c_oil = hash_get(pim->channel_oil_hash, c_oil, hash_alloc_intern); - c_oil->oil.mfcc_parent = input_vif_index; + c_oil->oil.mfcc_parent = MAXVIFS; c_oil->oil_ref_count = 1; c_oil->installed = 0; c_oil->up = pim_upstream_find(pim, sg); @@ -240,13 +198,14 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, listnode_add_sort(pim->channel_oil_list, c_oil); if (PIM_DEBUG_MROUTE) - zlog_debug( - "%s(%s): New oil for %pSG4 vif_index: %d Ref Count: 1 (Post Increment)", - __PRETTY_FUNCTION__, name, sg, input_vif_index); + zlog_debug("%s(%s): c_oil %s add", + __func__, name, pim_str_sg_dump(sg)); + return c_oil; } -void pim_channel_oil_del(struct channel_oil *c_oil, const char *name) +struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, + const char *name) { if (PIM_DEBUG_MROUTE) { struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp, @@ -269,11 +228,29 @@ void pim_channel_oil_del(struct channel_oil *c_oil, const char *name) hash_release(c_oil->pim->channel_oil_hash, c_oil); pim_channel_oil_free(c_oil); + return NULL; + } + + return c_oil; +} + +void pim_channel_oil_upstream_deref(struct channel_oil *c_oil) +{ + /* The upstream entry associated with a channel_oil is abt to be + * deleted. If the channel_oil is kept around because of other + * references we need to remove upstream based states out of it. + */ + c_oil = pim_channel_oil_del(c_oil, __func__); + if (c_oil) { + /* note: here we assume that c_oil->up has already been + * cleared + */ + pim_channel_update_mute(c_oil); } } int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, - uint32_t proto_mask) + uint32_t proto_mask, const char *caller) { struct pim_interface *pim_ifp; @@ -311,7 +288,8 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask; - if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]) { + if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & + PIM_OIF_FLAG_PROTO_ANY) { if (PIM_DEBUG_MROUTE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -333,8 +311,10 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, } channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0; + /* clear mute; will be re-evaluated when the OIF becomes valid again */ + channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE; - if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { + if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { if (PIM_DEBUG_MROUTE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -363,8 +343,8 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug( - "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, source_str, group_str, + "%s(%s): (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d", + __PRETTY_FUNCTION__, caller, source_str, group_str, proto_mask, channel_oil->oil.mfcc_parent, oif->name, pim_ifp->mroute_vif_index); } @@ -372,13 +352,114 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, return 0; } +void pim_channel_del_inherited_oif(struct channel_oil *c_oil, + struct interface *oif, const char *caller) +{ + struct pim_upstream *up = c_oil->up; + + pim_channel_del_oif(c_oil, oif, PIM_OIF_FLAG_PROTO_STAR, + caller); + + /* if an inherited OIF is being removed join-desired can change + * if the inherited OIL is now empty and KAT is running + */ + if (up && up->sg.src.s_addr != INADDR_ANY && + pim_upstream_empty_inherited_olist(up)) + pim_upstream_update_join_desired(up->pim, up); +} + +static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil, + struct pim_interface *pim_ifp) +{ + struct pim_interface *pim_reg_ifp; + struct pim_interface *vxlan_ifp; + bool do_mute = false; + struct pim_instance *pim = c_oil->pim; + + if (!c_oil->up) + return do_mute; + + pim_reg_ifp = pim->regiface->info; + if (pim_ifp == pim_reg_ifp) { + /* suppress pimreg in the OIL if the mroute is not supposed to + * trigger register encapsulated data + */ + if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags)) + do_mute = true; + + return do_mute; + } + + vxlan_ifp = pim_vxlan_get_term_ifp(pim); + if (pim_ifp == vxlan_ifp) { + /* 1. vxlan termination device must never be added to the + * origination mroute (and that can actually happen because + * of XG inheritance from the termination mroute) otherwise + * traffic will end up looping. + * PS: This check has also been extended to non-orig mroutes + * that have a local SIP as such mroutes can move back and + * forth between orig<=>non-orig type. + * 2. vxlan termination device should be removed from the non-DF + * to prevent duplicates to the overlay rxer + */ + if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) || + PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags) || + pim_vxlan_is_local_sip(c_oil->up)) + do_mute = true; + + return do_mute; + } + + return do_mute; +} + +void pim_channel_update_oif_mute(struct channel_oil *c_oil, + struct pim_interface *pim_ifp) +{ + bool old_mute; + bool new_mute; + + /* If pim_ifp is not a part of the OIL there is nothing to do */ + if (!c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]) + return; + + old_mute = !!(c_oil->oif_flags[pim_ifp->mroute_vif_index] & + PIM_OIF_FLAG_MUTE); + new_mute = pim_channel_eval_oif_mute(c_oil, pim_ifp); + if (old_mute == new_mute) + return; + + if (new_mute) + c_oil->oif_flags[pim_ifp->mroute_vif_index] |= + PIM_OIF_FLAG_MUTE; + else + c_oil->oif_flags[pim_ifp->mroute_vif_index] &= + ~PIM_OIF_FLAG_MUTE; + + pim_upstream_mroute_add(c_oil, __PRETTY_FUNCTION__); +} + +/* pim_upstream has been set or cleared on the c_oil. re-eval mute state + * on all existing OIFs + */ +static void pim_channel_update_mute(struct channel_oil *c_oil) +{ + struct pim_interface *pim_reg_ifp; + struct pim_interface *vxlan_ifp; + + pim_reg_ifp = c_oil->pim->regiface->info; + if (pim_reg_ifp) + pim_channel_update_oif_mute(c_oil, pim_reg_ifp); + vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim); + if (vxlan_ifp) + pim_channel_update_oif_mute(c_oil, vxlan_ifp); +} int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, - uint32_t proto_mask) + uint32_t proto_mask, const char *caller) { struct pim_interface *pim_ifp; int old_ttl; - bool allow_iif_in_oil = false; /* * If we've gotten here we've gone bad, but let's @@ -391,48 +472,6 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, pim_ifp = oif->info; -#ifdef PIM_ENFORCE_LOOPFREE_MFC - /* - Prevent creating MFC entry with OIF=IIF. - - This is a protection against implementation mistakes. - - PIM protocol implicitely ensures loopfree multicast topology. - - IGMP must be protected against adding looped MFC entries created - by both source and receiver attached to the same interface. See - TODO T22. - We shall allow igmp to create upstream when it is DR for the intf. - Assume RP reachable via non DR. - */ - if ((channel_oil->up && - PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(channel_oil->up->flags)) || - ((proto_mask == PIM_OIF_FLAG_PROTO_IGMP) && PIM_I_am_DR(pim_ifp))) { - allow_iif_in_oil = true; - } - - if (!allow_iif_in_oil && - pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) { - channel_oil->oil_inherited_rescan = 1; - if (PIM_DEBUG_MROUTE) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<group?>", - channel_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - pim_inet4_dump("<source?>", - channel_oil->oil.mfcc_origin, source_str, - sizeof(source_str)); - zlog_debug( - "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, proto_mask, - oif->name, pim_ifp->mroute_vif_index, - source_str, group_str); - } - return -2; - } -#endif - /* Prevent single protocol from subscribing same interface to channel (S,G) multiple times */ if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) { @@ -517,11 +556,23 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL; + /* Some OIFs are held in a muted state i.e. the PIM state machine + * decided to include the OIF but additional status check such as + * MLAG DF role prevent it from being activated for traffic + * forwarding. + */ + if (pim_channel_eval_oif_mute(channel_oil, pim_ifp)) + channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= + PIM_OIF_FLAG_MUTE; + else + channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= + ~PIM_OIF_FLAG_MUTE; + /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not * valid to get installed in kernel. */ if (channel_oil->oil.mfcc_parent != MAXVIFS) { - if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { + if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { if (PIM_DEBUG_MROUTE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -557,8 +608,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug( - "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", - __FILE__, __PRETTY_FUNCTION__, source_str, group_str, + "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", + __PRETTY_FUNCTION__, caller, source_str, group_str, proto_mask, oif->name, pim_ifp->mroute_vif_index); } @@ -567,19 +618,15 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, int pim_channel_oil_empty(struct channel_oil *c_oil) { - static uint32_t zero[MAXVIFS]; - static int inited = 0; + static struct mfcctl null_oil; if (!c_oil) return 1; - /* - * Not sure that this is necessary, but I would rather ensure - * that this works. - */ - if (!inited) { - memset(&zero, 0, sizeof(uint32_t) * MAXVIFS); - inited = 1; - } - return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t)); + /* exclude pimreg from the OIL when checking if the inherited_oil is + * non-NULL. + * pimreg device (in all vrfs) uses a vifi of + * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */ + return !memcmp(&c_oil->oil.mfcc_ttls[1], &null_oil.mfcc_ttls[1], + sizeof(null_oil.mfcc_ttls) - sizeof(null_oil.mfcc_ttls[0])); } diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index 319a1c91a3..de7fde05da 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -21,6 +21,7 @@ #define PIM_OIL_H #include "pim_mroute.h" +#include "pim_iface.h" /* * Where did we get this (S,G) from? @@ -38,6 +39,8 @@ (PIM_OIF_FLAG_PROTO_IGMP | PIM_OIF_FLAG_PROTO_PIM \ | PIM_OIF_FLAG_PROTO_STAR | PIM_OIF_FLAG_PROTO_VXLAN) +/* OIF is present in the OIL but must not be used for forwarding traffic */ +#define PIM_OIF_FLAG_MUTE (1 << 4) /* * We need a pimreg vif id from the kernel. * Since ifindex == vif id for most cases and the number @@ -113,18 +116,27 @@ struct channel_oil *pim_find_channel_oil(struct pim_instance *pim, struct prefix_sg *sg); struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, struct prefix_sg *sg, - int input_vif_index, const char *name); + const char *name); void pim_channel_oil_change_iif(struct pim_instance *pim, struct channel_oil *c_oil, int input_vif_index, const char *name); -void pim_channel_oil_del(struct channel_oil *c_oil, const char *name); +struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, + const char *name); int pim_channel_add_oif(struct channel_oil *c_oil, struct interface *oif, - uint32_t proto_mask); + uint32_t proto_mask, const char *caller); int pim_channel_del_oif(struct channel_oil *c_oil, struct interface *oif, - uint32_t proto_mask); + uint32_t proto_mask, const char *caller); int pim_channel_oil_empty(struct channel_oil *c_oil); char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size); + +void pim_channel_update_oif_mute(struct channel_oil *c_oil, + struct pim_interface *pim_ifp); + +void pim_channel_oil_upstream_deref(struct channel_oil *c_oil); +void pim_channel_del_inherited_oif(struct channel_oil *c_oil, + struct interface *oif, const char *caller); + #endif /* PIM_OIL_H */ diff --git a/pimd/pim_register.c b/pimd/pim_register.c index aff3fbed6c..013f04bb00 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -59,7 +59,7 @@ void pim_register_join(struct pim_upstream *up) } pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); up->reg_state = PIM_REG_JOIN; pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/); } @@ -145,7 +145,7 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size) case PIM_REG_JOIN: upstream->reg_state = PIM_REG_PRUNE; pim_channel_del_oif(upstream->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); pim_upstream_start_register_stop_timer(upstream, 0); pim_vxlan_update_sg_reg_state(pim, upstream, false/*reg_join*/); @@ -452,10 +452,9 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, } if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) - || ((SwitchToSptDesired(pim_ifp->pim, &sg)) + || ((SwitchToSptDesiredOnRp(pim_ifp->pim, &sg)) && pim_upstream_inherited_olist(pim_ifp->pim, upstream) == 0)) { - // pim_scan_individual_oil (upstream->channel_oil); pim_register_stop_send(ifp, &sg, dest_addr, src_addr); sentRegisterStop = 1; } else { @@ -464,7 +463,7 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, upstream->sptbit); } if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) - || (SwitchToSptDesired(pim_ifp->pim, &sg))) { + || (SwitchToSptDesiredOnRp(pim_ifp->pim, &sg))) { if (sentRegisterStop) { pim_upstream_keep_alive_timer_start( upstream, diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 09529055e0..bd295b0fef 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -274,6 +274,7 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim, static void pim_rp_refresh_group_to_rp_mapping(struct pim_instance *pim) { pim_msdp_i_am_rp_changed(pim); + pim_upstream_reeval_use_rpt(pim); } void pim_rp_prefix_list_update(struct pim_instance *pim, @@ -381,27 +382,17 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up) old_rpf.source_nexthop.interface = up->rpf.source_nexthop.interface; - rpf_result = pim_rpf_update(pim, up, &old_rpf); + rpf_result = pim_rpf_update(pim, up, &old_rpf, __func__); if (rpf_result == PIM_RPF_FAILURE) pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__); /* update kernel multicast forwarding cache (MFC) */ - if (up->rpf.source_nexthop.interface && up->channel_oil) { - ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex; - int vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex); - /* Pass Current selected NH vif index to mroute download */ - if (vif_index) - pim_scan_individual_oil(up->channel_oil, vif_index); - else { - if (PIM_DEBUG_PIM_NHT) - zlog_debug( - "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid", - __PRETTY_FUNCTION__, up->sg_str, - up->rpf.source_nexthop.interface->name); - } - } + if (up->rpf.source_nexthop.interface && up->channel_oil) + pim_upstream_mroute_iif_update(up->channel_oil, __func__); - if (rpf_result == PIM_RPF_CHANGED) + if (rpf_result == PIM_RPF_CHANGED || + (rpf_result == PIM_RPF_FAILURE && + old_rpf.source_nexthop.interface)) pim_zebra_upstream_rpf_changed(pim, up, &old_rpf); pim_zebra_update_all_interfaces(pim); @@ -1057,6 +1048,7 @@ void pim_rp_check_on_if_add(struct pim_interface *pim_ifp) if (i_am_rp_changed) { pim_msdp_i_am_rp_changed(pim); + pim_upstream_reeval_use_rpt(pim); } } @@ -1099,6 +1091,7 @@ void pim_i_am_rp_re_evaluate(struct pim_instance *pim) if (i_am_rp_changed) { pim_msdp_i_am_rp_changed(pim); + pim_upstream_reeval_use_rpt(pim); } } diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index d383ef5249..1eb5006b94 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -195,7 +195,8 @@ static int nexthop_mismatch(const struct pim_nexthop *nh1, } enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, - struct pim_upstream *up, struct pim_rpf *old) + struct pim_upstream *up, struct pim_rpf *old, + const char *caller) { struct pim_rpf *rpf = &up->rpf; struct pim_rpf saved; @@ -207,13 +208,17 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, return PIM_RPF_OK; if (up->upstream_addr.s_addr == INADDR_ANY) { - zlog_debug("%s: RP is not configured yet for %s", - __PRETTY_FUNCTION__, up->sg_str); + zlog_debug("%s(%s): RP is not configured yet for %s", + __func__, caller, up->sg_str); return PIM_RPF_OK; } saved.source_nexthop = rpf->source_nexthop; saved.rpf_addr = rpf->rpf_addr; + if (old) { + old->source_nexthop = saved.source_nexthop; + old->rpf_addr = saved.rpf_addr; + } nht_p.family = AF_INET; nht_p.prefixlen = IPV4_MAX_BITLEN; @@ -238,8 +243,8 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, rpf->rpf_addr.u.prefix4 = pim_rpf_find_rpf_addr(up); if (pim_rpf_addr_is_inaddr_any(rpf) && PIM_DEBUG_ZEBRA) { /* RPF'(S,G) not found */ - zlog_debug("%s %s: RPF'%s not found: won't send join upstream", - __FILE__, __PRETTY_FUNCTION__, up->sg_str); + zlog_debug("%s(%s): RPF'%s not found: won't send join upstream", + __func__, caller, up->sg_str); /* warning only */ } @@ -251,8 +256,8 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, pim_addr_dump("<addr?>", &rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str)); - zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d", - __FILE__, __PRETTY_FUNCTION__, + zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d", + __func__, caller, up->sg_str, rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>", nhaddr_str, @@ -269,8 +274,8 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, if (saved.source_nexthop.interface != rpf->source_nexthop.interface) { if (PIM_DEBUG_ZEBRA) { - zlog_debug("%s %s: (S,G)=%s RPF_interface(S) changed from %s to %s", - __FILE__, __PRETTY_FUNCTION__, + zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s", + __func__, caller, up->sg_str, saved.source_nexthop.interface ? saved.source_nexthop.interface->name : "<oldif?>", rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<newif?>"); @@ -286,11 +291,6 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, || saved.source_nexthop .interface != rpf->source_nexthop.interface) { - /* return old rpf to caller ? */ - if (old) { - old->source_nexthop = saved.source_nexthop; - old->rpf_addr = saved.rpf_addr; - } return PIM_RPF_CHANGED; } @@ -307,12 +307,6 @@ void pim_upstream_rpf_clear(struct pim_instance *pim, struct pim_upstream *up) { if (up->rpf.source_nexthop.interface) { - if (up->channel_oil) - pim_channel_oil_change_iif(pim, up->channel_oil, - MAXVIFS, - __PRETTY_FUNCTION__); - - pim_upstream_switch(pim, up, PIM_UPSTREAM_NOTJOINED); up->rpf.source_nexthop.interface = NULL; up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY; @@ -321,6 +315,7 @@ void pim_upstream_rpf_clear(struct pim_instance *pim, up->rpf.source_nexthop.mrib_route_metric = router->infinite_assert_metric.route_metric; up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY; + pim_upstream_mroute_iif_update(up->channel_oil, __func__); } } diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h index df7b249289..f006519b71 100644 --- a/pimd/pim_rpf.h +++ b/pimd/pim_rpf.h @@ -63,7 +63,7 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, struct in_addr addr, int neighbor_needed); enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, struct pim_upstream *up, - struct pim_rpf *old); + struct pim_rpf *old, const char *caller); void pim_upstream_rpf_clear(struct pim_instance *pim, struct pim_upstream *up); int pim_rpf_addr_is_inaddr_none(struct pim_rpf *rpf); diff --git a/pimd/pim_static.c b/pimd/pim_static.c index e3138360c8..7b121c9136 100644 --- a/pimd/pim_static.c +++ b/pimd/pim_static.c @@ -138,7 +138,7 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, } else { /* input interface changed */ s_route->iif = iif_index; - pim_channel_oil_change_iif(pim, &s_route->c_oil, + pim_static_mroute_iif_update(&s_route->c_oil, iif_index, __PRETTY_FUNCTION__); @@ -179,7 +179,7 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, s_route->c_oil.pim = pim; - if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) { + if (pim_static_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) { char gifaddr_str[INET_ADDRSTRLEN]; char sifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("<ifaddr?>", group, gifaddr_str, @@ -264,7 +264,7 @@ int pim_static_del(struct pim_instance *pim, struct interface *iif, if (s_route->c_oil.oil_ref_count <= 0 ? pim_mroute_del(&s_route->c_oil, __PRETTY_FUNCTION__) - : pim_mroute_add(&s_route->c_oil, + : pim_static_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) { char gifaddr_str[INET_ADDRSTRLEN]; char sifaddr_str[INET_ADDRSTRLEN]; diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index 40b7e3d236..a0387cdd4a 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -78,8 +78,13 @@ static void pim_upstream_remove_children(struct pim_instance *pim, child = pim_upstream_del(pim, child, __PRETTY_FUNCTION__); } - if (child) + if (child) { child->parent = NULL; + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) + pim_upstream_mroute_iif_update( + child->channel_oil, + __func__); + } } list_delete(&up->sources); } @@ -109,6 +114,10 @@ static void pim_upstream_find_new_children(struct pim_instance *pim, && (child != up)) { child->parent = up; listnode_add_sort(up->sources, child); + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) + pim_upstream_mroute_iif_update( + child->channel_oil, + __func__); } } } @@ -141,14 +150,22 @@ static struct pim_upstream *pim_upstream_find_parent(struct pim_instance *pim, static void upstream_channel_oil_detach(struct pim_upstream *up) { - if (up->channel_oil) { + struct channel_oil *channel_oil = up->channel_oil; + + if (channel_oil) { /* Detaching from channel_oil, channel_oil may exist post del, but upstream would not keep reference of it */ - up->channel_oil->up = NULL; - pim_channel_oil_del(up->channel_oil, __PRETTY_FUNCTION__); + channel_oil->up = NULL; up->channel_oil = NULL; + + /* attempt to delete channel_oil; if channel_oil is being held + * because of other references cleanup info such as "Mute" + * inferred from the parent upstream + */ + pim_channel_oil_upstream_deref(channel_oil); } + } struct pim_upstream *pim_upstream_del(struct pim_instance *pim, @@ -173,6 +190,11 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, if (up->ref_count >= 1) return up; + if (PIM_DEBUG_TRACE) + zlog_debug( + "pim_upstream free vrf:%s %s flags 0x%x", + pim->vrf->name, up->sg_str, up->flags); + THREAD_OFF(up->t_ka_timer); THREAD_OFF(up->t_rs_timer); THREAD_OFF(up->t_msdp_reg_timer); @@ -316,7 +338,7 @@ static void join_timer_stop(struct pim_upstream *up) up->rpf.rpf_addr.u.prefix4); if (nbr) - pim_jp_agg_remove_group(nbr->upstream_jp_agg, up); + pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr); pim_jp_agg_upstream_verification(up, false); } @@ -338,7 +360,7 @@ void join_timer_start(struct pim_upstream *up) } if (nbr) - pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1); + pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1, nbr); else { THREAD_OFF(up->t_join_timer); thread_add_timer(router->master, on_join_timer, up, @@ -519,7 +541,8 @@ void pim_upstream_register_reevaluate(struct pim_instance *pim) * is actually active; if it is not kat setup will trigger * source * registration whenever the flow becomes active. */ - if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || !up->t_ka_timer) + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || + !pim_upstream_is_kat_running(up)) continue; if (pim_is_grp_ssm(pim, up->sg.grp)) { @@ -532,7 +555,8 @@ void pim_upstream_register_reevaluate(struct pim_instance *pim) /* remove regiface from the OIL if it is there*/ pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, + __func__); up->reg_state = PIM_REG_NOINFO; } } else { @@ -544,13 +568,87 @@ void pim_upstream_register_reevaluate(struct pim_instance *pim) up->sg_str); pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, + __func__); up->reg_state = PIM_REG_JOIN; } } } } +/* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should + * forward a S - + * 1. along the SPT if SPTbit is set + * 2. and along the RPT if SPTbit is not set + * If forwarding is hw accelerated i.e. control and dataplane components + * are separate you may not be able to reliably set SPT bit on intermediate + * routers while still fowarding on the (S,G,rpt). + * + * This macro is a slight deviation on the RFC and uses "traffic-agnostic" + * criteria to decide between using the RPT vs. SPT for forwarding. + */ +void pim_upstream_update_use_rpt(struct pim_upstream *up, + bool update_mroute) +{ + bool old_use_rpt; + bool new_use_rpt; + + if (up->sg.src.s_addr == INADDR_ANY) + return; + + old_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + + /* We will use the SPT (IIF=RPF_interface(S) if - + * 1. We have decided to join the SPT + * 2. We are FHR + * 3. Source is directly connected + * 4. We are RP (parent's IIF is lo or vrf-device) + * In all other cases the source will stay along the RPT and + * IIF=RPF_interface(RP). + */ + if (up->join_state == PIM_UPSTREAM_JOINED || + PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || + pim_if_connected_to_source( + up->rpf.source_nexthop.interface, + up->sg.src) || + /* XXX - need to switch this to a more efficient + * lookup API + */ + I_am_RP(up->pim, up->sg.grp)) + /* use SPT */ + PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up->flags); + else + /* use RPT */ + PIM_UPSTREAM_FLAG_SET_USE_RPT(up->flags); + + new_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + if (old_use_rpt != new_use_rpt) { + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug("%s switched from %s to %s", + up->sg_str, + old_use_rpt?"RPT":"SPT", + new_use_rpt?"RPT":"SPT"); + if (update_mroute) + pim_upstream_mroute_add(up->channel_oil, __func__); + } +} + +/* some events like RP change require re-evaluation of SGrpt across + * all groups + */ +void pim_upstream_reeval_use_rpt(struct pim_instance *pim) +{ + struct pim_upstream *up; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, node, up)) { + if (up->sg.src.s_addr == INADDR_ANY) + continue; + + pim_upstream_update_use_rpt(up, true /*update_mroute*/); + } +} + void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, enum pim_upstream_state new_state) { @@ -603,21 +701,47 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, join_timer_start(up); } } + if (old_state != new_state) + pim_upstream_update_use_rpt(up, true /*update_mroute*/); } else { + bool old_use_rpt; + bool new_use_rpt; + bool send_xg_jp = false; forward_off(up); if (old_state == PIM_UPSTREAM_JOINED) pim_msdp_up_join_state_changed(pim, up); + if (old_state != new_state) { + old_use_rpt = + !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + pim_upstream_update_use_rpt(up, true /*update_mroute*/); + new_use_rpt = + !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + if (new_use_rpt && + (new_use_rpt != old_use_rpt) && + up->parent) + /* we have decided to switch from the SPT back + * to the RPT which means we need to cancel + * any previously sent SGrpt prunes immediately + */ + send_xg_jp = true; + } + /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards RP. If I am RP for G then send S,G prune to its IIF. */ - if (pim_upstream_is_sg_rpt(up) && up->parent - && !I_am_RP(pim, up->sg.grp)) { + if (pim_upstream_is_sg_rpt(up) && up->parent && + !I_am_RP(pim, up->sg.grp)) + send_xg_jp = true; + else + pim_jp_agg_single_upstream_send(&up->rpf, up, + 0 /* prune */); + + if (send_xg_jp) { if (PIM_DEBUG_PIM_TRACE_DETAIL) zlog_debug( - "%s: *,G IIF %s S,G IIF %s ", - __PRETTY_FUNCTION__, + "re-join RPT; *,G IIF %s S,G IIF %s ", up->parent->rpf.source_nexthop.interface ? up->parent->rpf.source_nexthop.interface->name : "Unknown", @@ -627,9 +751,7 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, pim_jp_agg_single_upstream_send(&up->parent->rpf, up->parent, 1 /* (W,G) Join */); - } else - pim_jp_agg_single_upstream_send(&up->rpf, up, - 0 /* prune */); + } join_timer_stop(up); } } @@ -683,6 +805,7 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up)); + up->pim = pim; up->sg = *sg; pim_str_sg_set(sg, up->sg_str); if (ch) @@ -717,7 +840,7 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, up->reg_state = PIM_REG_NOINFO; up->state_transition = pim_time_monotonic_sec(); up->channel_oil = - pim_channel_oil_add(pim, &up->sg, MAXVIFS, __PRETTY_FUNCTION__); + pim_channel_oil_add(pim, &up->sg, __PRETTY_FUNCTION__); up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE; up->rpf.source_nexthop.interface = NULL; @@ -742,15 +865,17 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, pim_upstream_fill_static_iif(up, incoming); pim_ifp = up->rpf.source_nexthop.interface->info; assert(pim_ifp); - pim_channel_oil_change_iif(pim, up->channel_oil, - pim_ifp->mroute_vif_index, - __PRETTY_FUNCTION__); + pim_upstream_update_use_rpt(up, + false /*update_mroute*/); + pim_upstream_mroute_iif_update(up->channel_oil, __func__); if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags)) pim_upstream_keep_alive_timer_start( up, pim->keep_alive_time); } else if (up->upstream_addr.s_addr != INADDR_ANY) { - rpf_result = pim_rpf_update(pim, up, NULL); + pim_upstream_update_use_rpt(up, + false /*update_mroute*/); + rpf_result = pim_rpf_update(pim, up, NULL, __func__); if (rpf_result == PIM_RPF_FAILURE) { if (PIM_DEBUG_PIM_TRACE) zlog_debug( @@ -759,12 +884,8 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, } if (up->rpf.source_nexthop.interface) { - pim_ifp = up->rpf.source_nexthop.interface->info; - if (pim_ifp) - pim_channel_oil_change_iif( - pim, up->channel_oil, - pim_ifp->mroute_vif_index, - __PRETTY_FUNCTION__); + pim_upstream_mroute_iif_update(up->channel_oil, + __func__); } } @@ -792,35 +913,35 @@ struct pim_upstream *pim_upstream_find(struct pim_instance *pim, } struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg, - struct interface *incoming, - int flags, const char *name) + struct interface *incoming, + int flags, const char *name) { - struct pim_upstream *up; - struct pim_interface *pim_ifp; - - pim_ifp = incoming->info; - - up = pim_upstream_find(pim_ifp->pim, sg); - - if (up) { - if (!(up->flags & flags)) { - up->flags |= flags; - up->ref_count++; - if (PIM_DEBUG_PIM_TRACE) - zlog_debug( - "%s(%s): upstream %s ref count %d increment", - __PRETTY_FUNCTION__, name, up->sg_str, - up->ref_count); - } - } else - up = pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name, - NULL); + struct pim_interface *pim_ifp = incoming->info; - return up; + return (pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name, + NULL)); } void pim_upstream_ref(struct pim_upstream *up, int flags, const char *name) { + /* when we go from non-FHR to FHR we need to re-eval traffic + * forwarding path + */ + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) && + PIM_UPSTREAM_FLAG_TEST_FHR(flags)) { + PIM_UPSTREAM_FLAG_SET_FHR(up->flags); + pim_upstream_update_use_rpt(up, true /*update_mroute*/); + } + + /* re-eval joinDesired; clearing peer-msdp-sa flag can + * cause JD to change + */ + if (!PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags) && + PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags)) { + PIM_UPSTREAM_FLAG_SET_SRC_MSDP(up->flags); + pim_upstream_update_join_desired(up->pim, up); + } + up->flags |= flags; ++up->ref_count; if (PIM_DEBUG_PIM_TRACE) @@ -867,6 +988,36 @@ struct pim_upstream *pim_upstream_add(struct pim_instance *pim, /* * Passed in up must be the upstream for ch. starch is NULL if no * information + * This function is copied over from + * pim_upstream_evaluate_join_desired_interface but limited to + * parent (*,G)'s includes/joins. + */ +int pim_upstream_eval_inherit_if(struct pim_upstream *up, + struct pim_ifchannel *ch, + struct pim_ifchannel *starch) +{ + /* if there is an explicit prune for this interface we cannot + * add it to the OIL + */ + if (ch) { + if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) + return 0; + } + + /* Check if the OIF can be inherited fron the (*,G) entry + */ + if (starch) { + if (!pim_macro_ch_lost_assert(starch) + && pim_macro_chisin_joins_or_include(starch)) + return 1; + } + + return 0; +} + +/* + * Passed in up must be the upstream for ch. starch is NULL if no + * information */ int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, struct pim_ifchannel *ch, @@ -885,8 +1036,14 @@ int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, * joins (*,G) */ if (starch) { + /* XXX: check on this with donald + * we are looking for PIM_IF_FLAG_MASK_S_G_RPT in + * upstream flags? + */ +#if 0 if (PIM_IF_FLAG_TEST_S_G_RPT(starch->upstream->flags)) return 0; +#endif if (!pim_macro_ch_lost_assert(starch) && pim_macro_chisin_joins_or_include(starch)) @@ -896,56 +1053,77 @@ int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, return 0; } -/* - Evaluate JoinDesired(S,G): - - JoinDesired(S,G) is true if there is a downstream (S,G) interface I - in the set: - - inherited_olist(S,G) = - joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) - - JoinDesired(S,G) may be affected by changes in the following: - - pim_ifp->primary_address - pim_ifp->pim_dr_addr - ch->ifassert_winner_metric - ch->ifassert_winner - ch->local_ifmembership - ch->ifjoin_state - ch->upstream->rpf.source_nexthop.mrib_metric_preference - ch->upstream->rpf.source_nexthop.mrib_route_metric - ch->upstream->rpf.source_nexthop.interface - - See also pim_upstream_update_join_desired() below. +/* Returns true if immediate OIL is empty and is used to evaluate + * JoinDesired. See pim_upstream_evaluate_join_desired. */ -int pim_upstream_evaluate_join_desired(struct pim_instance *pim, +static bool pim_upstream_empty_immediate_olist(struct pim_instance *pim, struct pim_upstream *up) { struct interface *ifp; - struct pim_ifchannel *ch, *starch; - struct pim_upstream *starup = up->parent; - int ret = 0; + struct pim_ifchannel *ch; FOR_ALL_INTERFACES (pim->vrf, ifp) { if (!ifp->info) continue; ch = pim_ifchannel_find(ifp, &up->sg); - - if (starup) - starch = pim_ifchannel_find(ifp, &starup->sg); - else - starch = NULL; - - if (!ch && !starch) + if (!ch) continue; - ret += pim_upstream_evaluate_join_desired_interface(up, ch, - starch); + /* If we have even one immediate OIF we can return with + * not-empty + */ + if (pim_upstream_evaluate_join_desired_interface(up, ch, + NULL /* starch */)) + return false; } /* scan iface channel list */ - return ret; /* false */ + /* immediate_oil is empty */ + return true; +} + + +static inline bool pim_upstream_is_msdp_peer_sa(struct pim_upstream *up) +{ + return PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags); +} + +/* + * bool JoinDesired(*,G) { + * if (immediate_olist(*,G) != NULL) + * return TRUE + * else + * return FALSE + * } + * + * bool JoinDesired(S,G) { + * return( immediate_olist(S,G) != NULL + * OR ( KeepaliveTimer(S,G) is running + * AND inherited_olist(S,G) != NULL ) ) + * } + */ +int pim_upstream_evaluate_join_desired(struct pim_instance *pim, + struct pim_upstream *up) +{ + bool empty_imm_oil; + bool empty_inh_oil; + + empty_imm_oil = pim_upstream_empty_immediate_olist(pim, up); + + /* (*,G) */ + if (up->sg.src.s_addr == INADDR_ANY) + return !empty_imm_oil; + + /* (S,G) */ + if (!empty_imm_oil) + return true; + empty_inh_oil = pim_upstream_empty_inherited_olist(up); + if (!empty_inh_oil && + (pim_upstream_is_kat_running(up) || + pim_upstream_is_msdp_peer_sa(up))) + return true; + + return false; } /* @@ -1116,7 +1294,7 @@ static void pim_upstream_fhr_kat_expiry(struct pim_instance *pim, THREAD_OFF(up->t_rs_timer); /* remove regiface from the OIL if it is there*/ pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); /* clear the register state */ up->reg_state = PIM_REG_NOINFO; PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags); @@ -1136,6 +1314,7 @@ static void pim_upstream_fhr_kat_start(struct pim_upstream *up) PIM_UPSTREAM_FLAG_SET_FHR(up->flags); if (up->reg_state == PIM_REG_NOINFO) pim_register_join(up); + pim_upstream_update_use_rpt(up, true /*update_mroute*/); } } @@ -1171,6 +1350,9 @@ struct pim_upstream *pim_upstream_keep_alive_timer_proc( /* source is no longer active - pull the SA from MSDP's cache */ pim_msdp_sa_local_del(pim, &up->sg); + /* JoinDesired can change when KAT is started or stopped */ + pim_upstream_update_join_desired(pim, up); + /* if entry was created because of activity we need to deref it */ if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { pim_upstream_fhr_kat_expiry(pim, up); @@ -1233,6 +1415,8 @@ void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time) /* any time keepalive is started against a SG we will have to * re-evaluate our active source database */ pim_msdp_sa_local_update(up); + /* JoinDesired can change when KAT is started or stopped */ + pim_upstream_update_join_desired(up->pim, up); } /* MSDP on RP needs to know if a source is registerable to this RP */ @@ -1282,7 +1466,7 @@ void pim_upstream_msdp_reg_timer_start(struct pim_upstream *up) * SwitchToSptDesired(S,G) return true once a single packet has been * received for the source and group. */ -int pim_upstream_switch_to_spt_desired(struct pim_instance *pim, +int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim, struct prefix_sg *sg) { if (I_am_RP(pim, sg->grp)) @@ -1445,7 +1629,8 @@ static int pim_upstream_register_stop_timer(struct thread *t) case PIM_REG_JOIN_PENDING: up->reg_state = PIM_REG_JOIN; pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, + __func__); pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/); break; case PIM_REG_JOIN: @@ -1546,7 +1731,8 @@ int pim_upstream_inherited_olist_decide(struct pim_instance *pim, if (!ch) flag = PIM_OIF_FLAG_PROTO_STAR; - pim_channel_add_oif(up->channel_oil, ifp, flag); + pim_channel_add_oif(up->channel_oil, ifp, flag, + __func__); output_intf++; } } @@ -1581,9 +1767,9 @@ int pim_upstream_inherited_olist(struct pim_instance *pim, * switch on a stick so turn on forwarding to just accept the * incoming packets so we don't bother the other stuff! */ - if (output_intf) - pim_upstream_switch(pim, up, PIM_UPSTREAM_JOINED); - else + pim_upstream_update_join_desired(pim, up); + + if (!output_intf) forward_on(up); return output_intf; @@ -1605,6 +1791,8 @@ void pim_upstream_find_new_rpf(struct pim_instance *pim) struct listnode *up_node; struct listnode *up_nextnode; struct pim_upstream *up; + struct pim_rpf old; + enum pim_rpf_result rpf_result; /* * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr @@ -1623,9 +1811,19 @@ void pim_upstream_find_new_rpf(struct pim_instance *pim) zlog_debug( "%s: Upstream %s without a path to send join, checking", __PRETTY_FUNCTION__, up->sg_str); - pim_rpf_update(pim, up, NULL); + old.source_nexthop.interface = + up->rpf.source_nexthop.interface; + rpf_result = pim_rpf_update(pim, up, &old, __func__); + if (rpf_result == PIM_RPF_CHANGED || + (rpf_result == PIM_RPF_FAILURE && + old.source_nexthop.interface)) + pim_zebra_upstream_rpf_changed(pim, up, &old); + /* update kernel multicast forwarding cache (MFC) */ + pim_upstream_mroute_iif_update(up->channel_oil, + __func__); } } + pim_zebra_update_all_interfaces(pim); } unsigned int pim_upstream_hash_key(const void *arg) @@ -1685,30 +1883,30 @@ bool pim_upstream_equal(const void *arg1, const void *arg2) */ static bool pim_upstream_kat_start_ok(struct pim_upstream *up) { - struct pim_instance *pim = up->channel_oil->pim; + struct channel_oil *c_oil = up->channel_oil; + struct interface *ifp = up->rpf.source_nexthop.interface; + struct pim_interface *pim_ifp; + + /* "iif == RPF_interface(S)" check is not easy to do as the info + * we get from the kernel/ASIC is really a "lookup/key hit". + * So we will do an approximate check here to avoid starting KAT + * because of (S,G,rpt) forwarding on a non-LHR. + */ + if (!ifp) + return false; - /* "iif == RPF_interface(S)" check has to be done by the kernel or hw - * so we will skip that here */ - if (up->rpf.source_nexthop.interface && - pim_if_connected_to_source(up->rpf.source_nexthop.interface, + pim_ifp = ifp->info; + if (pim_ifp->mroute_vif_index != c_oil->oil.mfcc_parent) + return false; + + if (pim_if_connected_to_source(up->rpf.source_nexthop.interface, up->sg.src)) { return true; } if ((up->join_state == PIM_UPSTREAM_JOINED) - && !pim_upstream_empty_inherited_olist(up)) { - /* XXX: I have added this RP check just for 3.2 and it's a - * digression from - * what rfc-4601 says. Till now we were only running KAT on FHR - * and RP and - * there is some angst around making the change to run it all - * routers that - * maintain the (S, G) state. This is tracked via CM-13601 and - * MUST be - * removed to handle spt turn-arounds correctly in a 3-tier clos - */ - if (I_am_RP(pim, up->sg.grp)) - return true; + && !pim_upstream_empty_inherited_olist(up)) { + return true; } return false; @@ -1803,7 +2001,7 @@ void pim_upstream_add_lhr_star_pimreg(struct pim_instance *pim) continue; pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); } } @@ -1852,17 +2050,18 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim, if (!nlist) { pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); continue; } g.u.prefix4 = up->sg.grp; apply_new = prefix_list_apply(np, &g); if (apply_new == PREFIX_DENY) pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); else pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); } } diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index c6c9291eed..ab50820518 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -80,6 +80,12 @@ * associated with an upstream */ #define PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE (1 << 19) +/* By default as SG entry will use the SPT for forwarding traffic + * unless it was setup as a result of a Prune(S,G,rpt) from a + * downstream router and has JoinDesired(S,G) as False. + * This flag is only relevant for (S,G) entries. + */ +#define PIM_UPSTREAM_FLAG_MASK_USE_RPT (1 << 20) #define PIM_UPSTREAM_FLAG_ALL 0xFFFFFFFF @@ -103,6 +109,7 @@ #define PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) #define PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) #define PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(flags) ((flags) &PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE) +#define PIM_UPSTREAM_FLAG_TEST_USE_RPT(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_USE_RPT) #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) @@ -122,6 +129,7 @@ #define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) #define PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) #define PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) +#define PIM_UPSTREAM_FLAG_SET_USE_RPT(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_USE_RPT) #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) @@ -142,6 +150,7 @@ #define PIM_UPSTREAM_FLAG_UNSET_MLAG_VXLAN(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) #define PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) #define PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE) +#define PIM_UPSTREAM_FLAG_UNSET_USE_RPT(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_USE_RPT) enum pim_upstream_state { PIM_UPSTREAM_NOTJOINED, @@ -188,6 +197,7 @@ enum pim_upstream_sptbit { */ struct pim_upstream { + struct pim_instance *pim; struct pim_upstream *parent; struct in_addr upstream_addr; /* Who we are talking to */ struct in_addr upstream_register; /*Who we received a register from*/ @@ -232,6 +242,11 @@ struct pim_upstream { int64_t state_transition; /* Record current state uptime */ }; +static inline bool pim_upstream_is_kat_running(struct pim_upstream *up) +{ + return (up->t_ka_timer != NULL); +} + struct pim_upstream *pim_upstream_find(struct pim_instance *pim, struct prefix_sg *sg); struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg, @@ -252,6 +267,9 @@ int pim_upstream_evaluate_join_desired(struct pim_instance *pim, int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, struct pim_ifchannel *ch, struct pim_ifchannel *starch); +int pim_upstream_eval_inherit_if(struct pim_upstream *up, + struct pim_ifchannel *ch, + struct pim_ifchannel *starch); void pim_upstream_update_join_desired(struct pim_instance *pim, struct pim_upstream *up); @@ -274,9 +292,9 @@ void pim_upstream_update_my_assert_metric(struct pim_upstream *up); void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time); -int pim_upstream_switch_to_spt_desired(struct pim_instance *pim, +int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim, struct prefix_sg *sg); -#define SwitchToSptDesired(pim, sg) pim_upstream_switch_to_spt_desired (pim, sg) +#define SwitchToSptDesiredOnRp(pim, sg) pim_upstream_switch_to_spt_desired_on_rp (pim, sg) int pim_upstream_is_sg_rpt(struct pim_upstream *up); void pim_upstream_set_sptbit(struct pim_upstream *up, @@ -324,4 +342,7 @@ struct pim_upstream *pim_upstream_keep_alive_timer_proc( struct pim_upstream *up); void pim_upstream_fill_static_iif(struct pim_upstream *up, struct interface *incoming); +void pim_upstream_update_use_rpt(struct pim_upstream *up, + bool update_mroute); +void pim_upstream_reeval_use_rpt(struct pim_instance *pim); #endif /* PIM_UPSTREAM_H */ diff --git a/pimd/pim_vxlan.c b/pimd/pim_vxlan.c index 33e3b2340c..fc34f3f600 100644 --- a/pimd/pim_vxlan.c +++ b/pimd/pim_vxlan.c @@ -245,26 +245,20 @@ static void pim_vxlan_orig_mr_up_del(struct pim_vxlan_sg *vxlan_sg) * for nht */ if (up) - pim_rpf_update(vxlan_sg->pim, up, NULL); + pim_rpf_update(vxlan_sg->pim, up, NULL, __func__); } } static void pim_vxlan_orig_mr_up_iif_update(struct pim_vxlan_sg *vxlan_sg) { - int vif_index; - /* update MFC with the new IIF */ pim_upstream_fill_static_iif(vxlan_sg->up, vxlan_sg->iif); - vif_index = pim_if_find_vifindex_by_ifindex(vxlan_sg->pim, - vxlan_sg->iif->ifindex); - if (vif_index > 0) - pim_scan_individual_oil(vxlan_sg->up->channel_oil, - vif_index); + pim_upstream_mroute_iif_update(vxlan_sg->up->channel_oil, __func__); if (PIM_DEBUG_VXLAN) - zlog_debug("vxlan SG %s orig mroute-up updated with iif %s vifi %d", + zlog_debug("vxlan SG %s orig mroute-up updated with iif %s", vxlan_sg->sg_str, - vxlan_sg->iif?vxlan_sg->iif->name:"-", vif_index); + vxlan_sg->iif?vxlan_sg->iif->name:"-"); } @@ -292,6 +286,7 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg) struct pim_upstream *up; int flags = 0; struct prefix nht_p; + struct pim_instance *pim = vxlan_sg->pim; if (vxlan_sg->up) { /* nothing to do */ @@ -346,9 +341,15 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg) pim_delete_tracked_nexthop(vxlan_sg->pim, &nht_p, up, NULL, false); } + /* We are acting FHR; clear out use_rpt setting if any */ + pim_upstream_update_use_rpt(up, false /*update_mroute*/); pim_upstream_ref(up, flags, __PRETTY_FUNCTION__); vxlan_sg->up = up; pim_vxlan_orig_mr_up_iif_update(vxlan_sg); + /* mute pimreg on origination mroutes */ + if (pim->regiface) + pim_channel_update_oif_mute(up->channel_oil, + pim->regiface->info); } else { up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg, vxlan_sg->iif, flags, @@ -373,6 +374,8 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg) /* update the inherited OIL */ pim_upstream_inherited_olist(vxlan_sg->pim, up); + if (!up->channel_oil->installed) + pim_upstream_mroute_add(up->channel_oil, __func__); } static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg) @@ -386,7 +389,8 @@ static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg) vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED; pim_channel_add_oif(vxlan_sg->up->channel_oil, - vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN); + vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN, + __func__); } static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg) @@ -405,7 +409,7 @@ static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg) vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED; pim_channel_del_oif(vxlan_sg->up->channel_oil, - orig_oif, PIM_OIF_FLAG_PROTO_VXLAN); + orig_oif, PIM_OIF_FLAG_PROTO_VXLAN, __func__); } static inline struct interface *pim_vxlan_orig_mr_oif_get( diff --git a/pimd/pim_vxlan.h b/pimd/pim_vxlan.h index f0a66e6b77..c6507a474c 100644 --- a/pimd/pim_vxlan.h +++ b/pimd/pim_vxlan.h @@ -115,6 +115,13 @@ static inline bool pim_vxlan_is_orig_mroute(struct pim_vxlan_sg *vxlan_sg) return (vxlan_sg->sg.src.s_addr != 0); } +static inline bool pim_vxlan_is_local_sip(struct pim_upstream *up) +{ + return (up->sg.src.s_addr != INADDR_ANY) && + up->rpf.source_nexthop.interface && + if_is_loopback_or_vrf(up->rpf.source_nexthop.interface); +} + extern struct pim_vxlan *pim_vxlan_p; extern struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim, struct prefix_sg *sg); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index b999188a9b..0417d0d063 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -272,7 +272,7 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, nbr = pim_neighbor_find(old->source_nexthop.interface, old->rpf_addr.u.prefix4); if (nbr) - pim_jp_agg_remove_group(nbr->upstream_jp_agg, up); + pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr); /* * We have detected a case where we might need @@ -290,7 +290,7 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, * so install it. */ if (!up->channel_oil->installed) - pim_mroute_add(up->channel_oil, + pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); /* @@ -325,8 +325,12 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, up->channel_oil->oil_inherited_rescan = 0; } + if (up->join_state == PIM_UPSTREAM_JOINED) + pim_jp_agg_switch_interface(old, &up->rpf, up); + if (!up->channel_oil->installed) - pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, + __PRETTY_FUNCTION__); } /* FIXME can join_desired actually be changed by pim_rpf_update() @@ -386,137 +390,17 @@ static void pim_zebra_vxlan_replay(void) zclient_send_message(zclient); } -void pim_scan_individual_oil(struct channel_oil *c_oil, int in_vif_index) -{ - struct in_addr vif_source; - int input_iface_vif_index; - - pim_rp_set_upstream_addr(c_oil->pim, &vif_source, - c_oil->oil.mfcc_origin, - c_oil->oil.mfcc_mcastgrp); - - if (in_vif_index) - input_iface_vif_index = in_vif_index; - else { - struct prefix src, grp; - - src.family = AF_INET; - src.prefixlen = IPV4_MAX_BITLEN; - src.u.prefix4 = vif_source; - grp.family = AF_INET; - grp.prefixlen = IPV4_MAX_BITLEN; - grp.u.prefix4 = c_oil->oil.mfcc_mcastgrp; - - if (PIM_DEBUG_ZEBRA) { - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_debug( - "%s: channel_oil (%s,%s) upstream info is not present.", - __PRETTY_FUNCTION__, source_str, group_str); - } - input_iface_vif_index = pim_ecmp_fib_lookup_if_vif_index( - c_oil->pim, &src, &grp); - } - - if (input_iface_vif_index < 1) { - if (PIM_DEBUG_ZEBRA) { - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_debug( - "%s %s: could not find input interface(%d) for (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, - c_oil->oil.mfcc_parent, source_str, group_str); - } - pim_mroute_del(c_oil, __PRETTY_FUNCTION__); - return; - } - - if (input_iface_vif_index == c_oil->oil.mfcc_parent) { - if (!c_oil->installed) - pim_mroute_add(c_oil, __PRETTY_FUNCTION__); - - /* RPF unchanged */ - return; - } - - if (PIM_DEBUG_ZEBRA) { - struct interface *old_iif = pim_if_find_by_vif_index( - c_oil->pim, c_oil->oil.mfcc_parent); - struct interface *new_iif = pim_if_find_by_vif_index( - c_oil->pim, input_iface_vif_index); - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, - sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, - sizeof(group_str)); - zlog_debug( - "%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, source_str, group_str, - (old_iif) ? old_iif->name : "<old_iif?>", - c_oil->oil.mfcc_parent, - (new_iif) ? new_iif->name : "<new_iif?>", - input_iface_vif_index); - } - - /* new iif loops to existing oif ? */ - if (c_oil->oil.mfcc_ttls[input_iface_vif_index]) { - struct interface *new_iif = pim_if_find_by_vif_index( - c_oil->pim, input_iface_vif_index); - - if (PIM_DEBUG_ZEBRA) { - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_debug( - "%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str, - (new_iif) ? new_iif->name : "<new_iif?>", - input_iface_vif_index); - } - } - - /* update iif vif_index */ - pim_channel_oil_change_iif(c_oil->pim, c_oil, input_iface_vif_index, - __PRETTY_FUNCTION__); - pim_mroute_add(c_oil, __PRETTY_FUNCTION__); -} - void pim_scan_oil(struct pim_instance *pim) { struct listnode *node; struct listnode *nextnode; struct channel_oil *c_oil; - ifindex_t ifindex; - int vif_index = 0; pim->scan_oil_last = pim_time_monotonic_sec(); ++pim->scan_oil_events; for (ALL_LIST_ELEMENTS(pim->channel_oil_list, node, nextnode, c_oil)) { - if (c_oil->up && c_oil->up->rpf.source_nexthop.interface) { - ifindex = c_oil->up->rpf.source_nexthop - .interface->ifindex; - vif_index = - pim_if_find_vifindex_by_ifindex(pim, ifindex); - /* Pass Current selected NH vif index to mroute - * download */ - if (vif_index) - pim_scan_individual_oil(c_oil, vif_index); - } else - pim_scan_individual_oil(c_oil, 0); + pim_upstream_mroute_iif_update(c_oil, __func__); } } @@ -758,7 +642,7 @@ void igmp_source_forward_start(struct pim_instance *pim, source->source_addr, sg.grp)) { /*Create a dummy channel oil */ source->source_channel_oil = pim_channel_oil_add( - pim, &sg, MAXVIFS, __PRETTY_FUNCTION__); + pim, &sg, __PRETTY_FUNCTION__); } else { @@ -809,7 +693,7 @@ void igmp_source_forward_start(struct pim_instance *pim, } source->source_channel_oil = pim_channel_oil_add( - pim, &sg, MAXVIFS, + pim, &sg, __PRETTY_FUNCTION__); } @@ -843,7 +727,7 @@ void igmp_source_forward_start(struct pim_instance *pim, source->source_channel_oil = pim_channel_oil_add( - pim, &sg, input_iface_vif_index, + pim, &sg, __PRETTY_FUNCTION__); if (!source->source_channel_oil) { if (PIM_DEBUG_IGMP_TRACE) { @@ -862,7 +746,7 @@ void igmp_source_forward_start(struct pim_instance *pim, if (PIM_I_am_DR(pim_oif)) { result = pim_channel_add_oif(source->source_channel_oil, group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); if (result) { if (PIM_DEBUG_MROUTE) { zlog_warn("%s: add_oif() failed with return=%d", @@ -891,7 +775,7 @@ void igmp_source_forward_start(struct pim_instance *pim, pim_channel_del_oif(source->source_channel_oil, group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); return; } @@ -942,7 +826,8 @@ void igmp_source_forward_stop(struct igmp_source *source) */ result = pim_channel_del_oif(source->source_channel_oil, group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); if (result) { if (PIM_DEBUG_IGMP_TRACE) zlog_debug( @@ -985,7 +870,8 @@ void pim_forward_start(struct pim_ifchannel *ch) if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) mask = PIM_OIF_FLAG_PROTO_IGMP; - pim_channel_add_oif(up->channel_oil, ch->interface, mask); + pim_channel_add_oif(up->channel_oil, ch->interface, + mask, __func__); } void pim_forward_stop(struct pim_ifchannel *ch, bool install_it) @@ -1004,13 +890,13 @@ void pim_forward_stop(struct pim_ifchannel *ch, bool install_it) */ if (pim_upstream_evaluate_join_desired_interface(up, ch, ch->parent)) pim_channel_add_oif(up->channel_oil, ch->interface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); else pim_channel_del_oif(up->channel_oil, ch->interface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); if (install_it && !up->channel_oil->installed) - pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); } void pim_zebra_zclient_update(struct vty *vty) diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index b3f9ac7630..1a374ba26b 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -633,6 +633,7 @@ fi %if %{with_rpki} %{_libdir}/frr/modules/bgpd_rpki.so %endif +%{_libdir}/frr/modules/zebra_cumulus_mlag.so %{_libdir}/frr/modules/zebra_irdp.so %{_libdir}/frr/modules/bgpd_bmp.so %{_bindir}/* diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 26504a857d..060bb76585 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -25,7 +25,6 @@ #include "thread.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" #include "prefix.h" #include "filter.h" #include "keychain.h" diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index 0024345500..9daeeb9580 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -27,7 +27,6 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" #include "thread.h" #include "log.h" #include "prefix.h" diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index cd577e9051..4fc8f40ae1 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -243,6 +243,8 @@ void route_add(struct prefix *p, vrf_id_t vrf_id, api_nh = &api.nexthops[i]; api_nh->vrf_id = nh->vrf_id; api_nh->type = nh->type; + api_nh->weight = nh->weight; + switch (nh->type) { case NEXTHOP_TYPE_IPV4: api_nh->gate = nh->gate; @@ -265,6 +267,17 @@ void route_add(struct prefix *p, vrf_id_t vrf_id, api_nh->bh_type = nh->bh_type; break; } + + if (nh->nh_label && nh->nh_label->num_labels > 0) { + int j; + + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL); + + api_nh->label_num = nh->nh_label->num_labels; + for (j = 0; j < nh->nh_label->num_labels; j++) + api_nh->labels[j] = nh->nh_label->label[j]; + } + i++; } api.nexthop_num = i; diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index a474613b4d..42646d15bc 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -388,7 +388,8 @@ extern void static_zebra_route_add(struct route_node *rn, continue; api_nh->vrf_id = si->nh_vrf_id; - api_nh->onlink = si->onlink; + if (si->onlink) + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); si->state = STATIC_SENT_TO_ZEBRA; @@ -441,7 +442,7 @@ extern void static_zebra_route_add(struct route_node *rn, if (si->snh_label.num_labels) { int i; - SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL); api_nh->label_num = si->snh_label.num_labels; for (i = 0; i < api_nh->label_num; i++) api_nh->labels[i] = si->snh_label.label[i]; diff --git a/tests/.gitignore b/tests/.gitignore index 6252cea823..5414cb8cc9 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -45,6 +45,7 @@ /lib/test_timer_performance /lib/test_ttable /lib/test_typelist +/lib/test_versioncmp /lib/test_zlog /lib/test_zmq /ospf6d/test_lsdb diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c index 96e398512b..1b3f90434b 100644 --- a/tests/bgpd/test_capability.c +++ b/tests/bgpd/test_capability.c @@ -923,7 +923,7 @@ int main(void) if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT)) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index fbf2a9fed2..af9e5791b7 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -1087,7 +1087,7 @@ int main(void) if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT)) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c index 7a038fb02e..d2c093fbea 100644 --- a/tests/bgpd/test_packet.c +++ b/tests/bgpd/test_packet.c @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) vrf_init(NULL, NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT)) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index b1dcfcf707..2de29cbdbc 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -24,7 +24,7 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" extern void test_init(void); @@ -154,7 +154,7 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); vty_init(master, false); - memory_init(); + lib_cmd_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 2071ae08cd..e091372ab8 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -25,7 +25,7 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" #include "log.h" #include "common_cli.h" @@ -83,7 +83,7 @@ int main(int argc, char **argv) cmd_domainname_set("test.domain"); vty_init(master, false); - memory_init(); + lib_cmd_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c index 88126e84bc..391ccd9268 100644 --- a/tests/lib/cxxcompat.c +++ b/tests/lib/cxxcompat.c @@ -51,13 +51,13 @@ #include "lib/json.h" #include "lib/keychain.h" #include "lib/lib_errors.h" +#include "lib/lib_vty.h" #include "lib/libfrr.h" #include "lib/libospf.h" #include "lib/linklist.h" #include "lib/log.h" #include "lib/md5.h" #include "lib/memory.h" -#include "lib/memory_vty.h" #include "lib/mlag.h" #include "lib/module.h" #include "lib/monotime.h" diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index a28830e088..18d3180889 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -23,7 +23,7 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" #include "log.h" #include "northbound.h" @@ -412,7 +412,7 @@ int main(int argc, char **argv) cmd_init(1); cmd_hostname_set("test"); vty_init(master, false); - memory_init(); + lib_cmd_init(); yang_init(); nb_init(master, modules, array_size(modules)); diff --git a/tests/lib/test_buffer.c b/tests/lib/test_buffer.c index b56cc30cf3..7fb9a769d3 100644 --- a/tests/lib/test_buffer.c +++ b/tests/lib/test_buffer.c @@ -20,7 +20,7 @@ #include <zebra.h> #include <memory.h> -#include <memory_vty.h> +#include <lib_vty.h> #include <buffer.h> struct thread_master *master; @@ -32,7 +32,7 @@ int main(int argc, char **argv) char junk[3]; char c = 'a'; - memory_init(); + lib_cmd_init(); if ((argc != 2) || (sscanf(argv[1], "%d%1s", &n, junk) != 1)) { fprintf(stderr, "Usage: %s <number of chars to simulate>\n", diff --git a/tests/lib/test_privs.c b/tests/lib/test_privs.c index de638bc67a..c06ebbeb38 100644 --- a/tests/lib/test_privs.c +++ b/tests/lib/test_privs.c @@ -22,7 +22,7 @@ #include "getopt.h" #include "privs.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" zebra_capabilities_t _caps_p[] = { ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_DAC_OVERRIDE, @@ -105,7 +105,7 @@ int main(int argc, char **argv) } /* Library inits. */ - memory_init(); + lib_cmd_init(); zprivs_preinit(&test_privs); zprivs_init(&test_privs); diff --git a/tests/lib/test_versioncmp.c b/tests/lib/test_versioncmp.c new file mode 100644 index 0000000000..bb819e36f5 --- /dev/null +++ b/tests/lib/test_versioncmp.c @@ -0,0 +1,66 @@ +/* + * frr_version_cmp() tests + * Copyright (C) 2018 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> +#include <defaults.h> + +static const char *rel(int x) +{ + if (x < 0) + return "<"; + if (x > 0) + return ">"; + return "=="; +} + +static int fail; + +static void compare(const char *a, const char *b, int expect) +{ + int result = frr_version_cmp(a, b); + + if (expect == result) + printf("\"%s\" %s \"%s\"\n", a, rel(result), b); + else { + printf("\"%s\" %s \"%s\", expected %s!\n", a, rel(result), b, + rel(expect)); + fail = 1; + } +} + +int main(int argc, char **argv) +{ + compare("", "", 0); + compare("1", "1", 0); + compare("1.0", "1.00", 0); + compare("10.0", "1", 1); + compare("10.0", "2", 1); + compare("2.1", "10.0", -1); + compare("1.1.1", "1.1.0", 1); + compare("1.0a", "1.0", 1); + compare("1.0a", "1.0b", -1); + compare("1.0a10", "1.0a2", 1); + compare("1.00a2", "1.0a2", 0); + compare("1.00a2", "1.0a3", -1); + compare("1.0-dev", "1.0", 1); + compare("1.0~foo", "1.0", -1); + compare("1.0~1", "1.0~0", 1); + compare("1.00~1", "1.0~0", 1); + printf("final tally: %s\n", fail ? "FAILED" : "ok"); + return fail; +} diff --git a/tests/lib/test_versioncmp.py b/tests/lib/test_versioncmp.py new file mode 100644 index 0000000000..0990757000 --- /dev/null +++ b/tests/lib/test_versioncmp.py @@ -0,0 +1,6 @@ +import frrtest + +class TestVersionCmp(frrtest.TestMultiOut): + program = './test_versioncmp' + +TestVersionCmp.exit_cleanly() diff --git a/tests/subdir.am b/tests/subdir.am index 270c0811b4..d87d348949 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -69,6 +69,7 @@ check_PROGRAMS = \ tests/lib/test_timer_performance \ tests/lib/test_ttable \ tests/lib/test_typelist \ + tests/lib/test_versioncmp \ tests/lib/test_zlog \ tests/lib/test_graph \ tests/lib/cli/test_cli \ @@ -293,6 +294,10 @@ tests_lib_test_typelist_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_typelist_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_typelist_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_typelist_SOURCES = tests/lib/test_typelist.c tests/helpers/c/prng.c +tests_lib_test_versioncmp_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_versioncmp_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_versioncmp_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_versioncmp_SOURCES = tests/lib/test_versioncmp.c tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD) @@ -344,6 +349,7 @@ EXTRA_DIST += \ tests/lib/test_ttable.py \ tests/lib/test_ttable.refout \ tests/lib/test_typelist.py \ + tests/lib/test_versioncmp.py \ tests/lib/test_zlog.py \ tests/lib/test_graph.py \ tests/lib/test_graph.refout \ diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py index 02fba97316..e62d139a0c 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py @@ -153,8 +153,10 @@ def ltemplatePreRouterStartHook(): if topotest.version_cmp(krel, '4.15') >= 0 and \ topotest.version_cmp(krel, '4.18') <= 0: l3mdev_accept = 1 - else: - l3mdev_accept = 0 + + if topotest.version_cmp(krel, '5.0') >= 0: + l3mdev_accept = 1 + logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) #check for mpls if tgen.hasmpls != True: diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py index 6b4df78c69..2944b3729c 100644 --- a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py @@ -31,6 +31,7 @@ import sys import json from functools import partial import pytest +import platform # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) @@ -68,6 +69,19 @@ def setup_module(mod): router_list = tgen.routers() logger.info('Testing with VRF Lite support') + krel = platform.release() + + # May need to adjust handling of vrf traffic depending on kernel version + l3mdev_accept = 0 + if topotest.version_cmp(krel, '4.15') >= 0 and \ + topotest.version_cmp(krel, '4.18') <= 0: + l3mdev_accept = 1 + + if topotest.version_cmp(krel, '5.0') >= 0: + l3mdev_accept = 1 + + logger.info('krel \'{0}\' setting net.ipv4.tcp_l3mdev_accept={1}'.format( + krel, l3mdev_accept)) cmds = ['ip link add {0}-cust1 type vrf table 1001', 'ip link add loop1 type dummy', @@ -78,6 +92,15 @@ def setup_module(mod): for cmd in cmds: output = tgen.net[rname].cmd(cmd.format(rname)) + output = tgen.net[rname].cmd('sysctl -n net.ipv4.tcp_l3mdev_accept') + logger.info( + 'router {0}: existing tcp_l3mdev_accept was {1}'.format( + rname, output)) + + if l3mdev_accept: + output = tgen.net[rname].cmd( + 'sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) + for rname, router in router_list.iteritems(): router.load_config( TopoRouter.RD_ZEBRA, diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 9f2fef52ea..fc7581b1f2 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -28,6 +28,7 @@ from subprocess import PIPE as SUB_PIPE from subprocess import Popen from functools import wraps from re import search as re_search +from tempfile import mkdtemp import StringIO import os @@ -276,11 +277,19 @@ def reset_config_on_routers(tgen, routerName=None): run_cfg_file = "{}/{}/frr.sav".format(TMPDIR, rname) init_cfg_file = "{}/{}/frr_json_initial.conf".format(TMPDIR, rname) - command = "/usr/lib/frr/frr-reload.py --input {} --test {} > {}". \ - format(run_cfg_file, init_cfg_file, dname) + + tempdir = mkdtemp() + with open(os.path.join(tempdir, 'vtysh.conf'), 'w') as fd: + pass + + command = "/usr/lib/frr/frr-reload.py --confdir {} --input {} --test {} > {}". \ + format(tempdir, run_cfg_file, init_cfg_file, dname) result = call(command, shell=True, stderr=SUB_STDOUT, stdout=SUB_PIPE) + os.unlink(os.path.join(tempdir, 'vtysh.conf')) + os.rmdir(tempdir) + # Assert if command fail if result > 0: logger.error("Delta file creation failed. Command executed %s", diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons index 79c52d30d1..8bec3c5bb6 100644 --- a/tools/etc/frr/daemons +++ b/tools/etc/frr/daemons @@ -56,6 +56,11 @@ bfdd_options=" -A 127.0.0.1" fabricd_options="-A 127.0.0.1" vrrpd_options=" -A 127.0.0.1" +# configuration profile +# +#frr_profile="traditional" +#frr_profile="datacenter" + # # This is the maximum number of FD's that will be available. # Upon startup this is read by the control files and ulimit diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 412cde0910..3e97635dfe 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -155,7 +155,7 @@ class Config(object): try: config_text = subprocess.check_output( bindir + "/vtysh --config_dir " + confdir + " -c 'show run " + daemon + "' | /usr/bin/tail -n +4 | " + bindir + "/vtysh --config_dir " + confdir + " -m -f -", - shell=True, stderr=subprocess.STDOUT) + shell=True) except subprocess.CalledProcessError as e: ve = VtyshMarkException(e) ve.output = e.output @@ -1105,7 +1105,7 @@ def vtysh_config_available(bindir, confdir): try: cmd = [str(bindir + '/vtysh'), '--config_dir', confdir, '-c', 'conf t'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip() + output = subprocess.check_output(cmd).strip() if 'VTY configuration is locked by other VTY' in output.decode('utf-8'): print(output) @@ -1363,7 +1363,7 @@ if __name__ == '__main__': while True: try: - _ = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + _ = subprocess.check_output(cmd) except subprocess.CalledProcessError: @@ -1408,7 +1408,7 @@ if __name__ == '__main__': fh.write(line + '\n') try: - subprocess.check_output([str(args.bindir + '/vtysh'), '--config_dir', args.confdir, '-f', filename], stderr=subprocess.STDOUT) + subprocess.check_output([str(args.bindir + '/vtysh'), '--config_dir', args.confdir, '-f', filename]) except subprocess.CalledProcessError as e: log.warning("frr-reload.py failed due to\n%s" % e.output) reload_ok = False diff --git a/tools/frr.in b/tools/frr.in index 50f7ecaa9f..d9816c2568 100755 --- a/tools/frr.in +++ b/tools/frr.in @@ -21,6 +21,7 @@ VTYSH="@vtysh_bin@" # /usr/bin/vtysh FRR_USER="@enable_user@" # frr FRR_GROUP="@enable_group@" # frr FRR_VTY_GROUP="@enable_vty_group@" # frrvty +FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter # Local Daemon selection may be done by using /etc/frr/daemons. # See /usr/share/doc/frr/README.Debian.gz for further information. @@ -151,7 +152,7 @@ start() --pidfile=`pidfile $dmn-$inst` \ --exec "$D_PATH/$dmn" \ -- \ - `eval echo "$""$dmn""_options"` -n "$inst" + `eval echo "$""$dmn""_options"` $frr_global_options -n "$inst" else if ! check_daemon $dmn; then echo -n " (binary does not exist)" @@ -164,14 +165,14 @@ start() --pidfile=`pidfile $dmn` \ --exec "$valgrind" \ -- --trace-children=no --leak-check=full --log-file=/var/log/frr/$dmn-valgrind.log $D_PATH/$dmn \ - `eval echo "$""$dmn""_options"` + `eval echo "$""$dmn""_options"` $frr_global_options else ${SSD} \ --start \ --pidfile=`pidfile $dmn` \ --exec "$D_PATH/$dmn" \ -- \ - `eval echo "$""$dmn""_options"` + `eval echo "$""$dmn""_options"` $frr_global_options fi fi @@ -188,7 +189,7 @@ start() --pidfile=`pidfile staticd` \ --exec "$D_PATH/staticd" \ -- \ - `eval echo "$"staticd"_options"` + `eval echo "$"staticd"_options"` $frr_global_options fi } @@ -511,6 +512,18 @@ fi # Read configuration variable file if it is present [ -r /etc/default/frr ] && . /etc/default/frr +if test -z "$frr_profile"; then + # try to autodetect config profile + if test -d /etc/cumulus; then + frr_profile=datacenter + # elif test ...; then + # -- add your distro/system here + elif test -n "$FRR_DEFAULT_PROFILE"; then + frr_profile="$FRR_DEFAULT_PROFILE" + fi +fi +test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile" + MAX_INSTANCES=${MAX_INSTANCES:=5} # Set priority of un-startable daemons to 'no' and substitute 'yes' to '0' @@ -561,7 +574,7 @@ case "$1" in stop_prio 0 $dmn fi - if [ [ -n "$dmn" ] && [ "$dmn" != "zebra" ] ]; then + if [ -n "$dmn" -a "$dmn" != "zebra" ]; then [ -n "$dmn" ] && eval "${dmn/-/_}=0" start_watchfrr fi diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index 3fc38d4bed..0dfdfd0efa 100644 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -24,6 +24,7 @@ VTYSH="@vtysh_bin@" # /usr/bin/vtysh FRR_USER="@enable_user@" # frr FRR_GROUP="@enable_group@" # frr FRR_VTY_GROUP="@enable_vty_group@" # frrvty +FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter # ORDER MATTERS FOR $DAEMONS! # - keep zebra first @@ -151,7 +152,7 @@ daemon_start() { instopt="${inst:+-n $inst}" eval args="\$${daemon}_options" - if eval "$all_wrap $wrap $bin -d $instopt $args"; then + if eval "$all_wrap $wrap $bin -d $frr_global_options $instopt $args"; then log_success_msg "Started $dmninst" vtysh_b "$daemon" else @@ -298,6 +299,18 @@ if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare \-a' unset watchfrr_options fi +if test -z "$frr_profile"; then + # try to autodetect config profile + if test -d /etc/cumulus; then + frr_profile=datacenter + # elif test ...; then + # -- add your distro/system here + elif test -n "$FRR_DEFAULT_PROFILE"; then + frr_profile="$FRR_DEFAULT_PROFILE" + fi +fi +test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile" + # # other defaults and dispatch # diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index d0b0c701a7..e8df08ef60 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -96,7 +96,7 @@ sub scan_file { elsif ($file =~ /lib\/if\.c$/) { $protocol = "VTYSH_INTERFACE"; } - elsif ($file =~ /lib\/filter\.c$/) { + elsif ($file =~ /lib\/(filter|lib_vty)\.c$/) { $protocol = "VTYSH_ALL"; } elsif ($file =~ /lib\/agentx\.c$/) { diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index c7e1d252c7..0ba1b9d9c8 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -45,7 +45,6 @@ #include "command.h" #include "memory.h" #include "linklist.h" -#include "memory_vty.h" #include "libfrr.h" #include "ferr.h" #include "lib_errors.h" diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index 4e8502107a..ed9616963d 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -25,7 +25,6 @@ #include <sigevent.h> #include <lib/version.h> #include "command.h" -#include "memory_vty.h" #include "libfrr.h" #include "lib_errors.h" diff --git a/zebra/interface.c b/zebra/interface.c index 20b05dfb32..bcb833b8d8 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1977,6 +1977,8 @@ DEFUN (shutdown_if, struct zebra_if *if_data; if (ifp->ifindex != IFINDEX_INTERNAL) { + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv_stop_ra(ifp); ret = if_unset_flags(ifp, IFF_UP); if (ret < 0) { vty_out(vty, "Can't shutdown interface\n"); diff --git a/zebra/main.c b/zebra/main.c index 731c4e1614..75f825e507 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -27,7 +27,6 @@ #include "filter.h" #include "memory.h" #include "zebra_memory.h" -#include "memory_vty.h" #include "prefix.h" #include "log.h" #include "plist.h" @@ -145,6 +144,9 @@ static void sigint(void) atomic_store_explicit(&zrouter.in_shutdown, true, memory_order_relaxed); + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv_stop_ra_all(); + frr_early_fini(); zebra_dplane_pre_finish(); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index f3a255fd29..e77c923230 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -467,6 +467,8 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, nh = nexthop_from_ifindex(index, nh_vrf_id); if (nh) { + nh->weight = rtnh->rtnh_hops + 1; + if (num_labels) nexthop_add_labels(nh, ZEBRA_LSP_STATIC, num_labels, labels); @@ -1419,6 +1421,9 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, "nexthop via if %u", routedesc, nexthop->ifindex); } + + if (nexthop->weight) + rtnh->rtnh_hops = nexthop->weight - 1; } static inline void _netlink_mpls_build_singlepath(const char *routedesc, @@ -1921,7 +1926,7 @@ static void _netlink_nexthop_build_group(struct nlmsghdr *n, size_t req_size, if (count) { for (int i = 0; i < count; i++) { grp[i].id = z_grp[i].id; - grp[i].weight = z_grp[i].weight; + grp[i].weight = z_grp[i].weight - 1; if (IS_ZEBRA_DEBUG_KERNEL) { if (i == 0) @@ -2347,7 +2352,7 @@ static int netlink_nexthop_process_group(struct rtattr **tb, for (int i = 0; ((i < count) && (i < z_grp_size)); i++) { z_grp[i].id = n_grp[i].id; - z_grp[i].weight = n_grp[i].weight; + z_grp[i].weight = n_grp[i].weight + 1; } return count; } diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 0adf654aaf..5dd6012f62 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -166,7 +166,8 @@ static int rtadv_recv_packet(struct zebra_vrf *zvrf, int sock, uint8_t *buf, #define RTADV_MSG_SIZE 4096 /* Send router advertisement packet. */ -static void rtadv_send_packet(int sock, struct interface *ifp) +static void rtadv_send_packet(int sock, struct interface *ifp, + ipv6_nd_suppress_ra_status stop) { struct msghdr msg; struct iovec iov; @@ -252,7 +253,10 @@ static void rtadv_send_packet(int sock, struct interface *ifp) zif->rtadv.AdvDefaultLifetime != -1 ? zif->rtadv.AdvDefaultLifetime : MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval); - rtadv->nd_ra_router_lifetime = htons(pkt_RouterLifetime); + + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv->nd_ra_router_lifetime = + (stop == RA_SUPPRESS) ? htons(0) : htons(pkt_RouterLifetime); rtadv->nd_ra_reachable = htonl(zif->rtadv.AdvReachableTime); rtadv->nd_ra_retransmit = htonl(0); @@ -512,7 +516,7 @@ static int rtadv_timer(struct thread *thread) ifp->name); rtadv_send_packet(rtadv_get_socket(zvrf), - ifp); + ifp, RA_ENABLE); } else { zif->rtadv.AdvIntervalTimer -= period; if (zif->rtadv.AdvIntervalTimer <= 0) { @@ -526,7 +530,7 @@ static int rtadv_timer(struct thread *thread) .MaxRtrAdvInterval; rtadv_send_packet( rtadv_get_socket(zvrf), - ifp); + ifp, RA_ENABLE); } } } @@ -556,7 +560,7 @@ static void rtadv_process_solicit(struct interface *ifp) if ((zif->rtadv.UseFastRexmit) || (zif->rtadv.AdvIntervalTimer <= (zif->rtadv.MaxRtrAdvInterval - MIN_DELAY_BETWEEN_RAS))) { - rtadv_send_packet(rtadv_get_socket(zvrf), ifp); + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_ENABLE); zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; } else zif->rtadv.AdvIntervalTimer = MIN_DELAY_BETWEEN_RAS; @@ -911,6 +915,8 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp, if (status == RA_SUPPRESS) { /* RA is currently enabled */ if (zif->rtadv.AdvSendAdvertisements) { + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, + RA_SUPPRESS); zif->rtadv.AdvSendAdvertisements = 0; zif->rtadv.AdvIntervalTimer = 0; zvrf->rtadv.adv_if_count--; @@ -1012,6 +1018,38 @@ stream_failure: return; } +/* + * send router lifetime value of zero in RAs on this interface since we're + * ceasing to advertise and want to let our neighbors know. + * RFC 4861 secion 6.2.5 + */ +void rtadv_stop_ra(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vrf *zvrf; + + zif = ifp->info; + zvrf = vrf_info_lookup(ifp->vrf_id); + + if (zif->rtadv.AdvSendAdvertisements) + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_SUPPRESS); +} + +/* + * send router lifetime value of zero in RAs on all interfaces since we're + * ceasing to advertise globally and want to let all of our neighbors know + * RFC 4861 secion 6.2.5 + */ +void rtadv_stop_ra_all(void) +{ + struct vrf *vrf; + struct interface *ifp; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) + FOR_ALL_INTERFACES (vrf, ifp) + rtadv_stop_ra(ifp); +} + void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS) { zebra_interface_radv_set(client, hdr, msg, zvrf, 0); diff --git a/zebra/rtadv.h b/zebra/rtadv.h index 409959d08d..63cec94434 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -140,6 +140,8 @@ typedef enum { extern void rtadv_init(struct zebra_vrf *zvrf); extern void rtadv_terminate(struct zebra_vrf *zvrf); +extern void rtadv_stop_ra(struct interface *ifp); +extern void rtadv_stop_ra_all(void); extern void rtadv_cmd_init(void); extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS); extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS); diff --git a/zebra/subdir.am b/zebra/subdir.am index d0f32d6a14..e3ca01ff73 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -32,6 +32,9 @@ endif if FPM module_LTLIBRARIES += zebra/zebra_fpm.la endif +if LINUX +module_LTLIBRARIES += zebra/zebra_cumulus_mlag.la +endif man8 += $(MANBUILD)/zebra.8 ## endif ZEBRA @@ -69,7 +72,6 @@ zebra_zebra_SOURCES = \ zebra/rule_netlink.c \ zebra/rule_socket.c \ zebra/zebra_mlag.c \ - zebra/zebra_mlag_private.c \ zebra/zebra_l2.c \ zebra/zebra_memory.c \ zebra/zebra_dplane.c \ @@ -134,7 +136,6 @@ noinst_HEADERS += \ zebra/rtadv.h \ zebra/rule_netlink.h \ zebra/zebra_mlag.h \ - zebra/zebra_mlag_private.h \ zebra/zebra_fpm_private.h \ zebra/zebra_l2.h \ zebra/zebra_dplane.h \ @@ -185,3 +186,6 @@ if DEV_BUILD zebra_zebra_fpm_la_SOURCES += zebra/zebra_fpm_dt.c endif endif + +zebra_zebra_cumulus_mlag_la_SOURCES = zebra/zebra_mlag_private.c +zebra_zebra_cumulus_mlag_la_LDFLAGS = -avoid-version -module -shared -export-dynamic diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index bf29bda77b..1dbe41f462 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -567,6 +567,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, api_nh = &api.nexthops[count]; api_nh->vrf_id = nexthop->vrf_id; api_nh->type = nexthop->type; + api_nh->weight = nexthop->weight; switch (nexthop->type) { case NEXTHOP_TYPE_BLACKHOLE: api_nh->bh_type = nexthop->bh_type; @@ -1411,11 +1412,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) char buf_prefix[PREFIX_STRLEN]; prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: p=%s, ZAPI_MESSAGE_LABEL: %sset, flags=0x%x", - __func__, buf_prefix, - (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL) ? "" - : "un"), - api.flags); + zlog_debug("%s: p=%s, flags=0x%x", + __func__, buf_prefix, api.flags); } /* Allocate new route. */ @@ -1544,11 +1542,14 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) return; } - if (api_nh->onlink) + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT)) + nexthop->weight = api_nh->weight; + /* MPLS labels for BGP-LU or Segment Routing */ - if (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL) + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && api_nh->type != NEXTHOP_TYPE_IFINDEX && api_nh->type != NEXTHOP_TYPE_BLACKHOLE) { enum lsp_types_t label_type; diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index bc4f51f70f..417c206467 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -2508,7 +2508,7 @@ enum zebra_dplane_result dplane_neigh_add(const struct interface *ifp, enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, - ifp, mac, ip, flags, 0); + ifp, mac, ip, flags, DPLANE_NUD_NOARP); return result; } @@ -2814,6 +2814,7 @@ int dplane_provider_register(const char *name, TAILQ_INIT(&(p->dp_ctx_in_q)); TAILQ_INIT(&(p->dp_ctx_out_q)); + p->dp_flags = flags; p->dp_priority = prio; p->dp_fp = fp; p->dp_start = start_fp; diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c index 1a911e429f..7fbc9c118b 100644 --- a/zebra/zebra_mlag.c +++ b/zebra/zebra_mlag.c @@ -27,7 +27,6 @@ #include "mlag.h" #include "zebra/zebra_mlag.h" -#include "zebra/zebra_mlag_private.h" #include "zebra/zebra_router.h" #include "zebra/zebra_memory.h" #include "zebra/zapi_msg.h" @@ -37,6 +36,13 @@ #include "zebra/zebra_mlag_clippy.c" #endif +DEFINE_HOOK(zebra_mlag_private_write_data, + (uint8_t *data, uint32_t len), (data, len)) +DEFINE_HOOK(zebra_mlag_private_monitor_state, (), ()) +DEFINE_HOOK(zebra_mlag_private_open_channel, (), ()) +DEFINE_HOOK(zebra_mlag_private_close_channel, (), ()) +DEFINE_HOOK(zebra_mlag_private_cleanup_data, (), ()) + #define ZEBRA_MLAG_METADATA_LEN 4 #define ZEBRA_MLAG_MSG_BCAST 0xFFFFFFFF @@ -175,7 +181,8 @@ static int zebra_mlag_client_msg_handler(struct thread *event) * write to MCLAGD */ if (len > 0) { - zebra_mlag_private_write_data(mlag_wr_buffer, len); + hook_call(zebra_mlag_private_write_data, + mlag_wr_buffer, len); /* * If message type is De-register, send a signal to main @@ -220,7 +227,7 @@ void zebra_mlag_handle_process_state(enum zebra_mlag_state state) } else if (state == MLAG_DOWN) { zrouter.mlag_info.connected = false; zebra_mlag_publish_process_state(NULL, ZEBRA_MLAG_PROCESS_DOWN); - zebra_mlag_private_monitor_state(); + hook_call(zebra_mlag_private_monitor_state); } } @@ -412,7 +419,7 @@ static int zebra_mlag_terminate_pthread(struct thread *event) /* * Send Notification to clean private data */ - zebra_mlag_private_cleanup_data(); + hook_call(zebra_mlag_private_cleanup_data); return 0; } @@ -470,7 +477,7 @@ void zebra_mlag_client_register(ZAPI_HANDLER_ARGS) "First client, opening the channel with MLAG"); zebra_mlag_spawn_pthread(); - rc = zebra_mlag_private_open_channel(); + rc = hook_call(zebra_mlag_private_open_channel); if (rc < 0) { /* * For some reason, zebra not able to open the @@ -530,7 +537,7 @@ void zebra_mlag_client_unregister(ZAPI_HANDLER_ARGS) * signal back to main thread to do the thread cleanup * this was mainly to make sure De-register is posted to MCLAGD. */ - zebra_mlag_private_close_channel(); + hook_call(zebra_mlag_private_close_channel); } if (IS_ZEBRA_DEBUG_MLAG) @@ -627,13 +634,13 @@ DEFPY_HIDDEN(test_mlag, test_mlag_cmd, zebra_mlag_spawn_pthread(); zrouter.mlag_info.clients_interested_cnt++; test_mlag_in_progress = true; - zebra_mlag_private_open_channel(); + hook_call(zebra_mlag_private_open_channel); } } else { if (test_mlag_in_progress == true) { test_mlag_in_progress = false; zrouter.mlag_info.clients_interested_cnt--; - zebra_mlag_private_close_channel(); + hook_call(zebra_mlag_private_close_channel); } } } diff --git a/zebra/zebra_mlag.h b/zebra/zebra_mlag.h index 6f7ef8319f..1f024516c5 100644 --- a/zebra/zebra_mlag.h +++ b/zebra/zebra_mlag.h @@ -33,6 +33,13 @@ #define ZEBRA_MLAG_BUF_LIMIT 2048 #define ZEBRA_MLAG_LEN_SIZE 4 +DECLARE_HOOK(zebra_mlag_private_write_data, + (uint8_t *data, uint32_t len), (data, len)) +DECLARE_HOOK(zebra_mlag_private_monitor_state, (), ()) +DECLARE_HOOK(zebra_mlag_private_open_channel, (), ()) +DECLARE_HOOK(zebra_mlag_private_close_channel, (), ()) +DECLARE_HOOK(zebra_mlag_private_cleanup_data, (), ()) + extern uint8_t mlag_wr_buffer[ZEBRA_MLAG_BUF_LIMIT]; extern uint8_t mlag_rd_buffer[ZEBRA_MLAG_BUF_LIMIT]; extern uint32_t mlag_rd_buf_offset; diff --git a/zebra/zebra_mlag_private.c b/zebra/zebra_mlag_private.c index 4df7b6dd11..3024407ada 100644 --- a/zebra/zebra_mlag_private.c +++ b/zebra/zebra_mlag_private.c @@ -36,7 +36,6 @@ #include "zebra/debug.h" #include "zebra/zebra_router.h" #include "zebra/zebra_mlag.h" -#include "zebra/zebra_mlag_private.h" #include <sys/un.h> @@ -46,8 +45,6 @@ * */ -#ifdef HAVE_CUMULUS - static struct thread_master *zmlag_master; static int mlag_socket; @@ -57,7 +54,7 @@ static int zebra_mlag_read(struct thread *thread); /* * Write the data to MLAGD */ -int zebra_mlag_private_write_data(uint8_t *data, uint32_t len) +static int zebra_mlag_private_write_data(uint8_t *data, uint32_t len) { int rc = 0; @@ -103,7 +100,7 @@ static int zebra_mlag_read(struct thread *thread) return -1; } mlag_rd_buf_offset += data_len; - if (data_len != (ssize_t)ZEBRA_MLAG_LEN_SIZE - curr_len) { + if (data_len != (ssize_t)(ZEBRA_MLAG_LEN_SIZE - curr_len)) { /* Try again later */ zebra_mlag_sched_read(); return 0; @@ -132,7 +129,7 @@ static int zebra_mlag_read(struct thread *thread) return -1; } mlag_rd_buf_offset += data_len; - if (data_len != (ssize_t)tot_len - curr_len) { + if (data_len != (ssize_t)(tot_len - curr_len)) { /* Try again later */ zebra_mlag_sched_read(); return 0; @@ -207,13 +204,14 @@ static int zebra_mlag_connect(struct thread *thread) /* * Currently we are doing polling later we will look for better options */ -void zebra_mlag_private_monitor_state(void) +static int zebra_mlag_private_monitor_state(void) { thread_add_event(zmlag_master, zebra_mlag_connect, NULL, 0, &zrouter.mlag_info.t_read); + return 0; } -int zebra_mlag_private_open_channel(void) +static int zebra_mlag_private_open_channel(void) { zmlag_master = zrouter.mlag_info.th_master; @@ -242,7 +240,7 @@ int zebra_mlag_private_open_channel(void) return 0; } -int zebra_mlag_private_close_channel(void) +static int zebra_mlag_private_close_channel(void) { if (zmlag_master == NULL) return -1; @@ -263,37 +261,34 @@ int zebra_mlag_private_close_channel(void) return 0; } -void zebra_mlag_private_cleanup_data(void) +static int zebra_mlag_private_cleanup_data(void) { zmlag_master = NULL; zrouter.mlag_info.connected = false; zrouter.mlag_info.timer_running = false; close(mlag_socket); -} - -#else /*HAVE_CUMULUS */ - -int zebra_mlag_private_write_data(uint8_t *data, uint32_t len) -{ return 0; } -void zebra_mlag_private_monitor_state(void) -{ -} - -int zebra_mlag_private_open_channel(void) +static int zebra_mlag_module_init(void) { + hook_register(zebra_mlag_private_write_data, + zebra_mlag_private_write_data); + hook_register(zebra_mlag_private_monitor_state, + zebra_mlag_private_monitor_state); + hook_register(zebra_mlag_private_open_channel, + zebra_mlag_private_open_channel); + hook_register(zebra_mlag_private_close_channel, + zebra_mlag_private_close_channel); + hook_register(zebra_mlag_private_cleanup_data, + zebra_mlag_private_cleanup_data); return 0; } -int zebra_mlag_private_close_channel(void) -{ - return 0; -} - -void zebra_mlag_private_cleanup_data(void) -{ -} -#endif /*HAVE_CUMULUS*/ +FRR_MODULE_SETUP( + .name = "zebra_cumulus_mlag", + .version = FRR_VERSION, + .description = "zebra Cumulus MLAG interface", + .init = zebra_mlag_module_init, +) diff --git a/zebra/zebra_mlag_private.h b/zebra/zebra_mlag_private.h deleted file mode 100644 index f7b68e9ba0..0000000000 --- a/zebra/zebra_mlag_private.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This is an implementation of MLAG Functionality - * - * Module name: Zebra MLAG - * - * Author: sathesh Kumar karra <sathk@cumulusnetworks.com> - * - * Copyright (C) 2019 Cumulus Networks http://www.cumulusnetworks.com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __ZEBRA_MLAG_PRIVATE_H__ -#define __ZEBRA_MLAG_PRIVATE_H__ - - -/* - * all the platform specific API's - */ - -int zebra_mlag_private_open_channel(void); -int zebra_mlag_private_close_channel(void); -void zebra_mlag_private_monitor_state(void); -int zebra_mlag_private_write_data(uint8_t *data, uint32_t len); -void zebra_mlag_private_cleanup_data(void); -#endif diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 1a70250627..9065a265ad 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1790,7 +1790,7 @@ uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe, if (!duplicate) { grp[i].id = depend->id; /* We aren't using weights for anything right now */ - grp[i].weight = 0; + grp[i].weight = depend->nhg->nexthop->weight; i++; } diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 9a6631a59a..2d9c83becb 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -1034,6 +1034,8 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, break; } if (re) { + struct zapi_nexthop znh; + stream_putc(s, re->type); stream_putw(s, re->instance); stream_putc(s, re->distance); @@ -1043,37 +1045,8 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, stream_putc(s, 0); for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nh)) if (rnh_nexthop_valid(re, nh)) { - stream_putl(s, nh->vrf_id); - stream_putc(s, nh->type); - switch (nh->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - stream_put_in_addr(s, &nh->gate.ipv4); - stream_putl(s, nh->ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - stream_putl(s, nh->ifindex); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - stream_put(s, &nh->gate.ipv6, 16); - stream_putl(s, nh->ifindex); - break; - default: - /* do nothing */ - break; - } - if (nh->nh_label) { - stream_putc(s, - nh->nh_label->num_labels); - if (nh->nh_label->num_labels) - stream_put( - s, - &nh->nh_label->label[0], - nh->nh_label->num_labels - * sizeof(mpls_label_t)); - } else - stream_putc(s, 0); + zapi_nexthop_from_nexthop(&znh, nh); + zapi_nexthop_encode(s, &znh, 0 /* flags */); num++; } stream_putc_at(s, nump, num); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index eca86d5fe2..78fd57a6bd 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -380,6 +380,9 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, sizeof buf, 1)); } + if (nexthop->weight) + vty_out(vty, ", weight %u", nexthop->weight); + vty_out(vty, "\n"); } vty_out(vty, "\n"); @@ -1240,6 +1243,9 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) sizeof(buf), 1)); } + if (nexthop->weight) + vty_out(vty, ", weight %u", nexthop->weight); + vty_out(vty, "\n"); } |
