diff options
149 files changed, 6681 insertions, 1008 deletions
diff --git a/.gitignore b/.gitignore index 40f6475a26..fb40ee52fe 100644 --- a/.gitignore +++ b/.gitignore @@ -113,3 +113,5 @@ refix .emacs.desktop* /test-suite.log +pceplib/test/*.log +pceplib/test/*.trs diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c index 329bd3d696..82eb8a815e 100644 --- a/bgpd/bgp_conditional_adv.c +++ b/bgpd/bgp_conditional_adv.c @@ -49,9 +49,9 @@ bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table *table, RESET_FLAG(dummy_attr.rmap_change_flags); ret = route_map_apply(rmap, dest_p, &path); - if (ret != RMAP_PERMITMATCH) - bgp_attr_flush(&dummy_attr); - else { + bgp_attr_flush(&dummy_attr); + + if (ret == RMAP_PERMITMATCH) { bgp_dest_unlock_node(dest); if (BGP_DEBUG(update, UPDATE_OUT)) zlog_debug( @@ -84,6 +84,7 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi, struct update_subgroup *subgrp; struct attr dummy_attr = {0}, attr = {0}; struct bgp_path_info_extra path_extra = {0}; + route_map_result_t ret; paf = peer_af_find(peer, afi, safi); if (!paf) @@ -114,11 +115,11 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi, RESET_FLAG(dummy_attr.rmap_change_flags); - if (route_map_apply(rmap, dest_p, &path) - != RMAP_PERMITMATCH) { - bgp_attr_flush(&dummy_attr); + ret = route_map_apply(rmap, dest_p, &path); + bgp_attr_flush(&dummy_attr); + + if (ret != RMAP_PERMITMATCH) continue; - } if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) || (addpath_capable diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index cbd29c146a..6248ad927b 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -6048,10 +6048,12 @@ bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx) * type-5 routes. It may be tweaked later on for other routes, or * even removed completely when all routes are handled. */ - if (pfx && pfx->family == AF_EVPN && - (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE || - evp->prefix.route_type == BGP_EVPN_IMET_ROUTE || - evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) + if (pfx && pfx->family == AF_EVPN + && (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE + || evp->prefix.route_type == BGP_EVPN_AD_ROUTE + || evp->prefix.route_type == BGP_EVPN_ES_ROUTE + || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE + || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) return true; return false; diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 867efb6099..aced0177ea 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -358,7 +358,7 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n"); vty_out(vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n"); vty_out(vty, - "EVPN type-1 prefix: [1]:[ESI]:[EthTag]:[IPlen]:[VTEP-IP]\n"); + "EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]\n"); vty_out(vty, "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n"); vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n"); @@ -2723,7 +2723,7 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, /* RD header and legend - once overall. */ if (rd_header && !json) { vty_out(vty, - "EVPN type-1 prefix: [1]:[ESI]:[EthTag]:[IPlen]:[VTEP-IP]\n"); + "EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]\n"); vty_out(vty, "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]\n"); vty_out(vty, diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 1af2ab384f..b0bab02fb8 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -714,6 +714,15 @@ static void setsids(struct bgp_path_info *bpi, extra->num_sids = num_sids; } +static void unsetsids(struct bgp_path_info *bpi) +{ + struct bgp_path_info_extra *extra; + + extra = bgp_path_info_extra_get(bpi); + extra->num_sids = 0; + memset(extra->sid, 0, sizeof(extra->sid)); +} + /* * returns pointer to new bgp_path_info upon success */ @@ -821,7 +830,8 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ else if (new_attr->srv6_vpn) setsids(bpi, &new_attr->srv6_vpn->sid, num_sids); - } + } else + unsetsids(bpi); if (nexthop_self_flag) bgp_path_info_set_flag(bn, bpi, BGP_PATH_ANNC_NH_SELF); @@ -847,6 +857,17 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ nh_valid = bgp_find_or_add_nexthop( bgp, bgp_nexthop, afi, safi, bpi, NULL, 0, p); + /* + * If you are using SRv6 VPN instead of MPLS, it need to check + * the SID allocation. If the sid is not allocated, the rib + * will be invalid. + */ + if (bgp->srv6_enabled + && (!new_attr->srv6_l3vpn && !new_attr->srv6_vpn)) { + bgp_path_info_unset_flag(bn, bpi, BGP_PATH_VALID); + nh_valid = false; + } + if (debug) zlog_debug("%s: nexthop is %svalid (in vrf %s)", __func__, (nh_valid ? "" : "not "), @@ -893,7 +914,8 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ setsids(new, &new_attr->srv6_l3vpn->sid, num_sids); else if (new_attr->srv6_vpn) setsids(new, &new_attr->srv6_vpn->sid, num_sids); - } + } else + unsetsids(new); if (num_labels) setlabels(new, label, num_labels); @@ -933,6 +955,17 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ nh_valid = bgp_find_or_add_nexthop(bgp, bgp_nexthop, afi, safi, new, NULL, 0, p); + /* + * If you are using SRv6 VPN instead of MPLS, it need to check + * the SID allocation. If the sid is not allocated, the rib + * will be invalid. + */ + if (bgp->srv6_enabled + && (!new->attr->srv6_l3vpn && !new->attr->srv6_vpn)) { + bgp_path_info_unset_flag(bn, new, BGP_PATH_VALID); + nh_valid = false; + } + if (debug) zlog_debug("%s: nexthop is %svalid (in vrf %s)", __func__, (nh_valid ? "" : "not "), diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 38193721b3..b0e0f74f77 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -243,6 +243,10 @@ static inline void vpn_leak_postchange(vpn_policy_direction_t direction, if (!bgp_vrf->vpn_policy[afi].tovpn_sid) ensure_vrf_tovpn_sid(bgp_vpn, bgp_vrf, afi); + if (!bgp_vrf->vpn_policy[afi].tovpn_sid + && bgp_vrf->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent) + vpn_leak_zebra_vrf_sid_withdraw(bgp_vrf, afi); + if (sid_diff(bgp_vrf->vpn_policy[afi].tovpn_sid, bgp_vrf->vpn_policy[afi] .tovpn_zebra_vrf_sid_last_sent)) { diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index f72f44f8a2..09abb69968 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -46,6 +46,7 @@ #include "bgpd/bgp_errors.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_nht.h" extern struct zebra_privs_t bgpd_privs; @@ -603,6 +604,12 @@ static int bgp_accept(struct thread *thread) BGP_EVENT_ADD(peer, TCP_connection_open); } + /* + * If we are doing nht for a peer that is v6 LL based + * massage the event system to make things happy + */ + bgp_nht_interface_events(peer); + return 0; } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index db0ee58e72..5a16fecc26 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -8980,8 +8980,6 @@ void route_vty_out(struct vty *vty, const struct prefix *p, vty_out(vty, "\n"); if (safi == SAFI_EVPN) { - struct bgp_path_es_info *path_es_info = NULL; - if (bgp_evpn_is_esi_valid(&attr->esi)) { /* XXX - add these params to the json out */ vty_out(vty, "%*s", 20, " "); @@ -8989,13 +8987,6 @@ void route_vty_out(struct vty *vty, const struct prefix *p, esi_to_str(&attr->esi, esi_buf, sizeof(esi_buf))); - if (path->extra && path->extra->mh_info) - path_es_info = - path->extra->mh_info->es_info; - - if (path_es_info && path_es_info->es) - vty_out(vty, " VNI: %u", - path_es_info->vni); vty_out(vty, "\n"); } if (attr->flag & @@ -10815,6 +10806,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, path.attr = &dummy_attr; ret = route_map_apply(rmap, dest_p, &path); + bgp_attr_flush(&dummy_attr); if (ret == RMAP_DENYMATCH) continue; } diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 3b7ee8b0b6..96febcd5df 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -798,6 +798,9 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); + /* make coverity happy */ + assert(attr.aspath); + attr.local_pref = bgp->default_local_pref; if ((afi == AFI_IP6) || peer_cap_enhe(peer, afi, safi)) { diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 2f9b8b86cf..8a3e74e8a4 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -282,6 +282,57 @@ static const char *get_afi_safi_json_str(afi_t afi, safi_t safi) return "Unknown"; } +/* unset srv6 locator */ +static int bgp_srv6_locator_unset(struct bgp *bgp) +{ + int ret; + struct listnode *node, *nnode; + struct prefix_ipv6 *chunk; + struct bgp_srv6_function *func; + struct bgp *bgp_vrf; + struct in6_addr *tovpn_sid; + + /* release chunk notification via ZAPI */ + ret = bgp_zebra_srv6_manager_release_locator_chunk( + bgp->srv6_locator_name); + if (ret < 0) + return -1; + + /* refresh chunks */ + for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk)) + listnode_delete(bgp->srv6_locator_chunks, chunk); + + /* refresh functions */ + for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) + listnode_delete(bgp->srv6_functions, func); + + /* refresh tovpn_sid */ + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) { + if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + /* refresh vpnv4 tovpn_sid */ + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid; + if (tovpn_sid) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); + + /* refresh vpnv6 tovpn_sid */ + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid; + if (tovpn_sid) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); + } + + /* update vpn bgp processes */ + vpn_leak_postchange_all(); + + /* clear locator name */ + memset(bgp->srv6_locator_name, 0, sizeof(bgp->srv6_locator_name)); + + return 0; +} + /* Utility function to get address family from current node. */ afi_t bgp_node_afi(struct vty *vty) { @@ -9096,6 +9147,23 @@ DEFUN_NOSH (bgp_segment_routing_srv6, return CMD_SUCCESS; } +DEFUN (no_bgp_segment_routing_srv6, + no_bgp_segment_routing_srv6_cmd, + "no segment-routing srv6", + NO_STR + "Segment-Routing configuration\n" + "Segment-Routing SRv6 configuration\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (strlen(bgp->srv6_locator_name) > 0) + if (bgp_srv6_locator_unset(bgp) < 0) + return CMD_WARNING_CONFIG_FAILED; + + bgp->srv6_enabled = false; + return CMD_SUCCESS; +} + DEFPY (bgp_srv6_locator, bgp_srv6_locator_cmd, "locator NAME$name", @@ -9121,6 +9189,32 @@ DEFPY (bgp_srv6_locator, return CMD_SUCCESS; } +DEFPY (no_bgp_srv6_locator, + no_bgp_srv6_locator_cmd, + "no locator NAME$name", + NO_STR + "Specify SRv6 locator\n" + "Specify SRv6 locator\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* when locator isn't configured, do nothing */ + if (strlen(bgp->srv6_locator_name) < 1) + return CMD_SUCCESS; + + /* name validation */ + if (strcmp(name, bgp->srv6_locator_name) != 0) { + vty_out(vty, "%% No srv6 locator is configured\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* unset locator */ + if (bgp_srv6_locator_unset(bgp) < 0) + return CMD_WARNING_CONFIG_FAILED; + + return CMD_SUCCESS; +} + DEFPY (show_bgp_srv6, show_bgp_srv6_cmd, "show bgp segment-routing srv6", @@ -18915,7 +19009,9 @@ void bgp_vty_init(void) /* srv6 commands */ install_element(VIEW_NODE, &show_bgp_srv6_cmd); install_element(BGP_NODE, &bgp_segment_routing_srv6_cmd); + install_element(BGP_NODE, &no_bgp_segment_routing_srv6_cmd); install_element(BGP_SRV6_NODE, &bgp_srv6_locator_cmd); + install_element(BGP_SRV6_NODE, &no_bgp_srv6_locator_cmd); install_element(BGP_IPV4_NODE, &af_sid_vpn_export_cmd); install_element(BGP_IPV6_NODE, &af_sid_vpn_export_cmd); } diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index b0d0c844f3..b367b393d9 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -3088,6 +3088,88 @@ static void bgp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS) vpn_leak_postchange_all(); } +static int bgp_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS) +{ + struct srv6_locator loc = {}; + struct bgp *bgp = bgp_get_default(); + const char *loc_name = bgp->srv6_locator_name; + + if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + + if (!bgp || !bgp->srv6_enabled) + return 0; + + if (bgp_zebra_srv6_manager_get_locator_chunk(loc_name) < 0) + return -1; + + return 0; +} + +static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) +{ + struct srv6_locator loc = {}; + struct bgp *bgp = bgp_get_default(); + struct listnode *node, *nnode; + struct prefix_ipv6 *chunk; + struct bgp_srv6_function *func; + struct bgp *bgp_vrf; + struct in6_addr *tovpn_sid; + struct prefix_ipv6 tmp_prefi; + + if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + + // refresh chunks + for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk)) + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)chunk)) + listnode_delete(bgp->srv6_locator_chunks, chunk); + + // refresh functions + for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) { + tmp_prefi.family = AF_INET6; + tmp_prefi.prefixlen = 128; + tmp_prefi.prefix = func->sid; + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)&tmp_prefi)) + listnode_delete(bgp->srv6_functions, func); + } + + // refresh tovpn_sid + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) { + if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + // refresh vpnv4 tovpn_sid + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid; + if (tovpn_sid) { + tmp_prefi.family = AF_INET6; + tmp_prefi.prefixlen = 128; + tmp_prefi.prefix = *tovpn_sid; + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)&tmp_prefi)) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); + } + + // refresh vpnv6 tovpn_sid + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid; + if (tovpn_sid) { + tmp_prefi.family = AF_INET6; + tmp_prefi.prefixlen = 128; + tmp_prefi.prefix = *tovpn_sid; + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)&tmp_prefi)) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); + } + } + + vpn_leak_postchange_all(); + return 0; +} + void bgp_zebra_init(struct thread_master *master, unsigned short instance) { zclient_num_connects = 0; @@ -3130,6 +3212,8 @@ void bgp_zebra_init(struct thread_master *master, unsigned short instance) zclient->iptable_notify_owner = iptable_notify_owner; zclient->route_notify_owner = bgp_zebra_route_notify_owner; zclient->instance = instance; + zclient->srv6_locator_add = bgp_zebra_process_srv6_locator_add; + zclient->srv6_locator_delete = bgp_zebra_process_srv6_locator_delete; zclient->process_srv6_locator_chunk = bgp_zebra_process_srv6_locator_chunk; } @@ -3541,3 +3625,8 @@ int bgp_zebra_srv6_manager_get_locator_chunk(const char *name) { return srv6_manager_get_locator_chunk(zclient, name); } + +int bgp_zebra_srv6_manager_release_locator_chunk(const char *name) +{ + return srv6_manager_release_locator_chunk(zclient, name); +} diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 02b6484943..9c0a1d8f1f 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -114,4 +114,5 @@ extern int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable); extern int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type); extern int bgp_zebra_stale_timer_update(struct bgp *bgp); extern int bgp_zebra_srv6_manager_get_locator_chunk(const char *name); +extern int bgp_zebra_srv6_manager_release_locator_chunk(const char *name); #endif /* _QUAGGA_BGP_ZEBRA_H */ diff --git a/configure.ac b/configure.ac index 917e791182..609d3c3230 100644 --- a/configure.ac +++ b/configure.ac @@ -1926,7 +1926,7 @@ dnl ----- dnl LTTng dnl ----- if test "$enable_lttng" = "yes"; then - PKG_CHECK_MODULES([UST], [lttng-ust >= 2.12.0], [ + PKG_CHECK_MODULES([UST], [lttng-ust >= 2.10.0], [ AC_DEFINE([HAVE_LTTNG], [1], [Enable LTTng support]) LTTNG=true ], [ @@ -2519,6 +2519,7 @@ AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir%s%s/ldpd.sock"], [ldpd control AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra api socket]) AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket]) AC_DEFINE_UNQUOTED([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information]) +AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information]) AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory]) AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory]) diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index b827afd6cc..681fc1173c 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -191,6 +191,10 @@ Networking data types ``%pNHs``: :frrfmtout:`1.2.3.4 if 15` — same as :c:func:`nexthop2str()` + ``%pNHcg``: :frrfmtout:`1.2.3.4` — compact gateway only + + ``%pNHci``: :frrfmtout:`eth0` — compact interface only + .. frrfmt:: %pBD (struct bgp_dest *) :frrfmtout:`fe80::1234/64` diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 4fd86ffb13..ede2144107 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -264,8 +264,17 @@ Redistribute routes to OSPF6 argument injects the default route regardless of it being present in the router. Metric values and route-map can also be specified optionally. -Graceful Restart Helper -======================= +Graceful Restart +================ + +.. clicmd:: graceful-restart [grace-period (1-1800)] + + + Configure Graceful Restart (RFC 5187) restarting support. + When enabled, the default grace period is 120 seconds. + + To perform a graceful shutdown, the "graceful-restart prepare ipv6 ospf" + EXEC-level command needs to be issued before restarting the ospf6d daemon. .. clicmd:: graceful-restart helper-only [A.B.C.D] @@ -297,6 +306,16 @@ Graceful Restart Helper restarts. By default, it supports both planned and unplanned outages. +.. clicmd:: graceful-restart prepare ipv6 ospf + + + Initiate a graceful restart for all OSPFv3 instances configured with the + "graceful-restart" command. The ospf6d daemon should be restarted during + the instance-specific grace period, otherwise the graceful restart will fail. + + This is an EXEC-level command. + + .. _showing-ospf6-information: Showing OSPF6 information diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 2714b81dbe..e1fe4bbbdb 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -279,6 +279,10 @@ Route Map Set Command Set the BGP AS path to prepend. +.. clicmd:: set as-path exclude AS-NUMBER... + + Drop AS-NUMBER from the BGP AS path. + .. clicmd:: set community COMMUNITY Set the BGP community attribute. diff --git a/doc/user/setup.rst b/doc/user/setup.rst index 64a33765c2..dbbfca21e7 100644 --- a/doc/user/setup.rst +++ b/doc/user/setup.rst @@ -176,6 +176,27 @@ Operations This section covers a few common operational tasks and how to perform them. +Interactive Shell +^^^^^^^^^^^^^^^^^ +FRR offers an IOS-like interactive shell called ``vtysh`` where a user can run +individual configuration or show commands. To get into this shell, issue the +``vtysh`` command from either a privilege user (root, or with sudo) or a user +account that is part of the ``frrvty`` group. +e.g. + +.. code-block:: console + + root@ub18:~# vtysh + + Hello, this is FRRouting (version 8.1-dev). + Copyright 1996-2005 Kunihiro Ishiguro, et al. + + ub18# + +.. note:: + The default install location for vtysh is /usr/bin/vtysh + + Restarting ^^^^^^^^^^ diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 79036320b8..3a9cd11055 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -1241,36 +1241,103 @@ For protocols requiring an IPv6 router-id, the following commands are available: .. _zebra-sysctl: -Expected sysctl settings -======================== +sysctl settings +=============== The linux kernel has a variety of sysctl's that affect it's operation as a router. This section is meant to act as a starting point for those sysctl's that must be used in order to provide FRR with smooth operation as a router. This section is not meant as the full documentation for sysctl's. The operator must use the sysctl documentation -with the linux kernel for that. +with the linux kernel for that. The following link has helpful references to many relevant +sysctl values: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt + +Expected sysctl settings +------------------------ .. option:: net.ipv4.ip_forward = 1 - This option allows the linux kernel to forward ipv4 packets incoming from one interface - to an outgoing interface. Without this no forwarding will take place from off box packets. + This global option allows the linux kernel to forward (route) ipv4 packets incoming from one + interface to an outgoing interface. If this is set to 0, the system will not route transit + ipv4 packets, i.e. packets that are not sent to/from a process running on the local system. -.. option:: net.ipv6.conf.all_forwarding=1 +.. option:: net.ipv4.conf.{all,default,<interface>}.forwarding = 1 - This option allows the linux kernel to forward ipv6 packets incoming from one interface - to an outgoing interface. Without this no forwarding will take place from off box packets. + The linux kernel can selectively enable forwarding (routing) of ipv4 packets on a per + interface basis. The forwarding check in the kernel dataplane occurs against the ingress + Layer 3 interface, i.e. if the ingress L3 interface has forwarding set to 0, packets will not + be routed. -.. option:: net.ipv6.conf.all.keep_addr_on_down=1 +.. option:: net.ipv6.conf.{all,default,<interface>}.forwarding = 1 + + This per interface option allows the linux kernel to forward (route) transit ipv6 packets + i.e. incoming from one Layer 3 interface to an outgoing Layer 3 interface. + The forwarding check in the kernel dataplane occurs against the ingress Layer 3 interface, + i.e. if the ingress L3 interface has forwarding set to 0, packets will not be routed. + +.. option:: net.ipv6.conf.all.keep_addr_on_down = 1 When an interface is taken down, do not remove the v6 addresses associated with the interface. This option is recommended because this is the default behavior for v4 as well. -.. option:: net.ipv6.route.skip_notify_on_dev_down=1 +.. option:: net.ipv6.route.skip_notify_on_dev_down = 1 When an interface is taken down, the linux kernel will not notify, via netlink, about routes that used that interface being removed from the FIB. This option is recommended because this is the default behavior for v4 as well. +Optional sysctl settings +------------------------ + +.. option:: net.ipv4.conf.{all,default,<interface>}.bc_forwarding = 0 + + This per interface option allows the linux kernel to optionally allow Directed Broadcast + (i.e. Routed Broadcast or Subnet Broadcast) packets to be routed onto the connected network + segment where the subnet exists. + If the local router receives a routed packet destined for a broadcast address of a connected + subnet, setting bc_forwarding to 1 on the interface with the target subnet assigned to it will + allow non locally-generated packets to be routed via the broadcast route. + If bc_forwarding is set to 0, routed packets destined for a broadcast route will be dropped. + e.g. + Host1 (SIP:192.0.2.10, DIP:10.0.0.255) -> (eth0:192.0.2.1/24) Router1 (eth1:10.0.0.1/24) -> BC + If net.ipv4.conf.{all,default,<interface>}.bc_forwarding=1, then Router1 will forward each + packet destined to 10.0.0.255 onto the eth1 interface with a broadcast DMAC (ff:ff:ff:ff:ff:ff). + +.. option:: net.ipv4.conf.{all,default,<interface>}.arp_accept = 1 + + This per interface option allows the linux kernel to optionally skip the creation of ARP + entries upon the receipt of a Gratuitous ARP (GARP) frame carrying an IP that is not already + present in the ARP cache. Setting arp_accept to 0 on an interface will ensure NEW ARP entries + are not created due to the arrival of a GARP frame. + Note: This does not impact how the kernel reacts to GARP frames that carry a "known" IP + (that is already in the ARP cache) -- an existing ARP entry will always be updated + when a GARP for that IP is received. + +.. option:: net.ipv4.conf.{all,default,<interface>}.arp_ignore = 0 + + This per interface option allows the linux kernel to control what conditions must be met in + order for an ARP reply to be sent in response to an ARP request targeting a local IP address. + When arp_ignore is set to 0, the kernel will send ARP replies in response to any ARP Request + with a Target-IP matching a local address. + When arp_ignore is set to 1, the kernel will send ARP replies if the Target-IP in the ARP + Request matches an IP address on the interface the Request arrived at. + When arp_ignore is set to 2, the kernel will send ARP replies only if the Target-IP matches an + IP address on the interface where the Request arrived AND the Sender-IP falls within the subnet + assigned to the local IP/interface. + +.. option:: net.ipv4.conf.{all,default,<interface>}.arp_notify = 1 + + This per interface option allows the linux kernel to decide whether to send a Gratuitious ARP + (GARP) frame when the Layer 3 interface comes UP. + When arp_notify is set to 0, no GARP is sent. + When arp_notify is set to 1, a GARP is sent when the interface comes UP. + +.. option:: net.ipv6.conf.{all,default,<interface>}.ndisc_notify = 1 + + This per interface option allows the linux kernel to decide whether to send an Unsolicited + Neighbor Advertisement (U-NA) frame when the Layer 3 interface comes UP. + When ndisc_notify is set to 0, no U-NA is sent. + When ndisc_notify is set to 1, a U-NA is sent when the interface comes UP. + Debugging ========= diff --git a/lib/command_match.c b/lib/command_match.c index 56a7ae422b..f221e0a02c 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -81,7 +81,7 @@ static enum match_type match_mac(const char *, bool); static bool is_neg(vector vline, size_t idx) { - if (idx >= vector_active(vline)) + if (idx >= vector_active(vline) || !vector_slot(vline, idx)) return false; return !strcmp(vector_slot(vline, idx), "no"); } diff --git a/lib/nexthop.c b/lib/nexthop.c index 98409c76c5..2e09cb4bcc 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -634,9 +634,6 @@ const char *nexthop2str(const struct nexthop *nexthop, char *str, int size) case NEXTHOP_TYPE_BLACKHOLE: snprintf(str, size, "blackhole"); break; - default: - snprintf(str, size, "unknown"); - break; } return str; @@ -939,6 +936,12 @@ int nexthop_str2backups(const char *str, int *num_backups, * unreachable (blackhole) * %pNHs * nexthop2str() + * %pNHcg + * 1.2.3.4 + * (0-length if no IP address present) + * %pNHci + * eth0 + * (0-length if no interface present) */ printfrr_ext_autoreg_p("NH", printfrr_nh) static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, @@ -993,12 +996,10 @@ static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, case BLACKHOLE_NULL: ret += bputs(buf, " (blackhole)"); break; - default: + case BLACKHOLE_UNSPEC: break; } break; - default: - break; } if (do_ifi && nexthop->ifindex) ret += bprintfrr(buf, ", %s%s", v_viaif, @@ -1029,9 +1030,54 @@ static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, case NEXTHOP_TYPE_BLACKHOLE: ret += bputs(buf, "blackhole"); break; - default: - ret += bputs(buf, "unknown"); - break; + } + return ret; + case 'c': + ea->fmt++; + if (*ea->fmt == 'g') { + ea->fmt++; + if (!nexthop) + return bputs(buf, "(null)"); + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + ret += bprintfrr(buf, "%pI4", + &nexthop->gate.ipv4); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + ret += bprintfrr(buf, "%pI6", + &nexthop->gate.ipv6); + break; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + } else if (*ea->fmt == 'i') { + ea->fmt++; + if (!nexthop) + return bputs(buf, "(null)"); + switch (nexthop->type) { + case NEXTHOP_TYPE_IFINDEX: + ret += bprintfrr( + buf, "%s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (nexthop->ifindex) + ret += bprintfrr( + buf, "%s", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + break; + } } return ret; } diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index b74a0e6c23..6676c0b072 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -550,6 +550,13 @@ void nb_cli_show_config_prepare(struct nb_config *config, bool with_defaults) LYD_VALIDATE_NO_STATE, NULL); } +static int lyd_node_cmp(struct lyd_node **dnode1, struct lyd_node **dnode2) +{ + struct nb_node *nb_node = (*dnode1)->schema->priv; + + return nb_node->cbs.cli_cmp(*dnode1, *dnode2); +} + static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root, bool with_defaults) { @@ -567,6 +574,10 @@ static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root, * it's time to print the config. */ if (sort_node && nb_node != sort_node) { + list_sort(sort_list, + (int (*)(const void **, + const void **))lyd_node_cmp); + for (ALL_LIST_ELEMENTS_RO(sort_list, listnode, data)) nb_cli_show_dnode_cmds(vty, data, with_defaults); @@ -584,11 +595,9 @@ static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root, if (!sort_node) { sort_node = nb_node; sort_list = list_new(); - sort_list->cmp = (int (*)(void *, void *)) - nb_node->cbs.cli_cmp; } - listnode_add_sort(sort_list, child); + listnode_add(sort_list, child); continue; } @@ -596,6 +605,9 @@ static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root, } if (sort_node) { + list_sort(sort_list, + (int (*)(const void **, const void **))lyd_node_cmp); + for (ALL_LIST_ELEMENTS_RO(sort_list, listnode, data)) nb_cli_show_dnode_cmds(vty, data, with_defaults); diff --git a/lib/zclient.c b/lib/zclient.c index a1e7194890..dde60a6c90 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1105,6 +1105,33 @@ stream_failure: return -1; } +int zapi_srv6_locator_encode(struct stream *s, const struct srv6_locator *l) +{ + stream_putw(s, strlen(l->name)); + stream_put(s, l->name, strlen(l->name)); + stream_putw(s, l->prefix.prefixlen); + stream_put(s, &l->prefix.prefix, sizeof(l->prefix.prefix)); + return 0; +} + +int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l) +{ + uint16_t len = 0; + + STREAM_GETW(s, len); + if (len > SRV6_LOCNAME_SIZE) + goto stream_failure; + + STREAM_GET(l->name, s, len); + STREAM_GETW(s, l->prefix.prefixlen); + STREAM_GET(&l->prefix.prefix, s, sizeof(l->prefix.prefix)); + l->prefix.family = AF_INET6; + return 0; + +stream_failure: + return -1; +} + static int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg) { int i; diff --git a/lib/zclient.h b/lib/zclient.h index 71187ccae7..f9438d5db7 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -1090,6 +1090,9 @@ extern int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl); extern int zapi_labels_decode(struct stream *s, struct zapi_labels *zl); +extern int zapi_srv6_locator_encode(struct stream *s, + const struct srv6_locator *l); +extern int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l); extern int zapi_srv6_locator_chunk_encode(struct stream *s, const struct srv6_locator_chunk *c); extern int zapi_srv6_locator_chunk_decode(struct stream *s, diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 71d32b409c..7423a1a9e9 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -780,10 +780,6 @@ void ospf6_area_plist_update(struct prefix_list *plist, int add) const char *name = prefix_list_name(plist); struct ospf6 *ospf6 = NULL; - - if (!om6->ospf6) - return; - for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) { if (PREFIX_NAME_IN(oa) @@ -988,7 +984,6 @@ DEFUN(show_ipv6_ospf6_spf_tree, show_ipv6_ospf6_spf_tree_cmd, int idx_vrf = 0; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { @@ -1048,7 +1043,6 @@ DEFUN(show_ipv6_ospf6_area_spf_tree, show_ipv6_ospf6_area_spf_tree_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_ipv4 += 2; @@ -1132,7 +1126,6 @@ DEFUN(show_ipv6_ospf6_simulate_spf_tree_root, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_ipv4 += 2; @@ -1340,8 +1333,6 @@ void ospf6_area_interface_delete(struct ospf6_interface *oi) struct listnode *node, *nnode; struct ospf6 *ospf6; - if (!om6->ospf6) - return; for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) if (listnode_lookup(oa->if_list, oi)) diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 90c9fdf23a..5a7023126d 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -52,6 +52,7 @@ #include "ospf6d.h" #include "ospf6_spf.h" #include "ospf6_nssa.h" +#include "ospf6_gr.h" #include "lib/json.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info"); @@ -102,6 +103,13 @@ struct ospf6_lsa *ospf6_as_external_lsa_originate(struct ospf6_route *route, struct ospf6_as_external_lsa *as_external_lsa; caddr_t p; + if (ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return NULL; + } + if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL)) zlog_debug("Originate AS-External-LSA for %pFX", &route->prefix); @@ -2522,7 +2530,6 @@ DEFUN(show_ipv6_ospf6_redistribute, show_ipv6_ospf6_redistribute_cmd, json_object *json_array_routes = NULL; json_object *json_array_redistribute = NULL; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (uj) { diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index f13ed3e3bb..186eac35a5 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -85,7 +85,7 @@ struct ospf6_lsdb *ospf6_get_scoped_lsdb_self(struct ospf6_lsa *lsa) return lsdb_self; } -void ospf6_lsa_originate(struct ospf6_lsa *lsa) +void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa) { struct ospf6_lsa *old; struct ospf6_lsdb *lsdb_self; @@ -106,7 +106,8 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa) /* if the new LSA does not differ from previous, suppress this update of the LSA */ - if (old && !OSPF6_LSA_IS_DIFFER(lsa, old)) { + if (old && !OSPF6_LSA_IS_DIFFER(lsa, old) + && !ospf6->gr_info.finishing_restart) { if (IS_OSPF6_DEBUG_ORIGINATE_TYPE(lsa->header->type)) zlog_debug("Suppress updating LSA: %s", lsa->name); ospf6_lsa_delete(lsa); @@ -134,20 +135,20 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa) void ospf6_lsa_originate_process(struct ospf6_lsa *lsa, struct ospf6 *process) { lsa->lsdb = process->lsdb; - ospf6_lsa_originate(lsa); + ospf6_lsa_originate(process, lsa); } void ospf6_lsa_originate_area(struct ospf6_lsa *lsa, struct ospf6_area *oa) { lsa->lsdb = oa->lsdb; - ospf6_lsa_originate(lsa); + ospf6_lsa_originate(oa->ospf6, lsa); } void ospf6_lsa_originate_interface(struct ospf6_lsa *lsa, struct ospf6_interface *oi) { lsa->lsdb = oi->lsdb; - ospf6_lsa_originate(lsa); + ospf6_lsa_originate(oi->area->ospf6, lsa); } void ospf6_remove_id_from_external_id_table(struct ospf6 *ospf6, @@ -326,7 +327,8 @@ void ospf6_install_lsa(struct ospf6_lsa *lsa) lsa->installed = now; /* Topo change handling */ - if (CHECK_LSA_TOPO_CHG_ELIGIBLE(ntohs(lsa->header->type))) { + if (CHECK_LSA_TOPO_CHG_ELIGIBLE(ntohs(lsa->header->type)) + && !CHECK_FLAG(lsa->flag, OSPF6_LSA_DUPLICATE)) { /* check if it is new lsa ? or existing lsa got modified ?*/ if (!old || OSPF6_LSA_IS_CHANGED(old, lsa)) { @@ -991,6 +993,8 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, /* if no database copy or received is more recent */ if (old == NULL || ismore_recent < 0) { + bool self_originated; + /* in case we have no database copy */ ismore_recent = -1; @@ -1029,12 +1033,13 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, reoriginated instance of the LSA not to be rejected by other routers due to MinLSArrival. */ - if (new->header->adv_router - != from->ospf6_if->area->ospf6->router_id) + self_originated = (new->header->adv_router + == from->ospf6_if->area->ospf6->router_id); + if (!self_originated) ospf6_flood(from, new); - /* Received Grace-LSA */ - if (IS_GRACE_LSA(new)) { + /* Received non-self-originated Grace LSA. */ + if (IS_GRACE_LSA(new) && !self_originated) { struct ospf6 *ospf6; ospf6 = ospf6_get_by_lsdb(new); @@ -1088,8 +1093,16 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, ospf6_acknowledge_lsa(new, ismore_recent, from); /* (f) Self Originated LSA, section 13.4 */ - if (new->header->adv_router - == from->ospf6_if->area->ospf6->router_id) { + if (self_originated) { + if (from->ospf6_if->area->ospf6->gr_info + .restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress -- not flushing self-originated LSA: %s", + new->name); + return; + } + /* Self-originated LSA (newer than ours) is received from another router. We have to make a new instance of the @@ -1105,6 +1118,11 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, &new->refresh); } + struct ospf6 *ospf6 = from->ospf6_if->area->ospf6; + struct ospf6_area *area = from->ospf6_if->area; + if (ospf6->gr_info.restart_in_progress) + ospf6_gr_check_lsdb_consistency(ospf6, area); + return; } diff --git a/ospf6d/ospf6_flood.h b/ospf6d/ospf6_flood.h index 4e4fc55ed4..775d0d289d 100644 --- a/ospf6d/ospf6_flood.h +++ b/ospf6d/ospf6_flood.h @@ -32,7 +32,7 @@ extern struct ospf6_lsdb *ospf6_get_scoped_lsdb(struct ospf6_lsa *lsa); extern struct ospf6_lsdb *ospf6_get_scoped_lsdb_self(struct ospf6_lsa *lsa); /* origination & purging */ -extern void ospf6_lsa_originate(struct ospf6_lsa *lsa); +extern void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa); extern void ospf6_lsa_originate_process(struct ospf6_lsa *lsa, struct ospf6 *process); extern void ospf6_lsa_originate_area(struct ospf6_lsa *lsa, diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c new file mode 100644 index 0000000000..40893ed998 --- /dev/null +++ b/ospf6d/ospf6_gr.c @@ -0,0 +1,749 @@ +/* + * This is an implementation of RFC 5187 Graceful Restart. + * + * Copyright 2021 NetDEF (c), All rights reserved. + * + * 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 "memory.h" +#include "command.h" +#include "table.h" +#include "vty.h" +#include "log.h" +#include "hook.h" +#include "printfrr.h" + +#include "ospf6d/ospf6_lsa.h" +#include "ospf6d/ospf6_lsdb.h" +#include "ospf6d/ospf6_route.h" +#include "ospf6d/ospf6_area.h" +#include "ospf6d/ospf6_interface.h" +#include "ospf6d/ospf6d.h" +#include "ospf6d/ospf6_asbr.h" +#include "ospf6d/ospf6_zebra.h" +#include "ospf6d/ospf6_message.h" +#include "ospf6d/ospf6_neighbor.h" +#include "ospf6d/ospf6_flood.h" +#include "ospf6d/ospf6_intra.h" +#include "ospf6d/ospf6_spf.h" +#include "ospf6d/ospf6_gr.h" +#ifndef VTYSH_EXTRACT_PL +#include "ospf6d/ospf6_gr_clippy.c" +#endif + +static void ospf6_gr_nvm_delete(struct ospf6 *ospf6); + +/* Originate and install Grace-LSA for a given interface. */ +static int ospf6_gr_lsa_originate(struct ospf6_interface *oi) +{ + struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info; + struct ospf6_lsa_header *lsa_header; + struct ospf6_grace_lsa *grace_lsa; + struct ospf6_lsa *lsa; + char buffer[OSPF6_MAX_LSASIZE]; + + if (IS_OSPF6_DEBUG_ORIGINATE(LINK)) + zlog_debug("Originate Link-LSA for Interface %s", + oi->interface->name); + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + grace_lsa = + (struct ospf6_grace_lsa *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + + /* Put grace period. */ + grace_lsa->tlv_period.header.type = htons(GRACE_PERIOD_TYPE); + grace_lsa->tlv_period.header.length = htons(GRACE_PERIOD_LENGTH); + grace_lsa->tlv_period.interval = htonl(gr_info->grace_period); + + /* Put restart reason. */ + grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE); + grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH); + if (gr_info->restart_support) + grace_lsa->tlv_reason.reason = OSPF6_GR_SW_RESTART; + else + grace_lsa->tlv_reason.reason = OSPF6_GR_UNKNOWN_RESTART; + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA); + lsa_header->id = htonl(oi->interface->ifindex); + lsa_header->adv_router = oi->area->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, oi->lsdb); + lsa_header->length = htons(sizeof(*lsa_header) + sizeof(*grace_lsa)); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + /* Originate */ + ospf6_lsa_originate_interface(lsa, oi); + + return 0; +} + +/* Flush all self-originated Grace-LSAs. */ +static void ospf6_gr_flush_grace_lsas(struct ospf6 *ospf6) +{ + struct ospf6_area *area; + struct listnode *anode; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) { + struct ospf6_lsa *lsa; + struct ospf6_interface *oi; + struct listnode *inode; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: flushing self-originated Grace-LSAs [area %pI4]", + &area->area_id); + + for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) { + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_GRACE_LSA), + htonl(oi->interface->ifindex), + oi->area->ospf6->router_id, + oi->lsdb); + if (!lsa) { + zlog_warn( + "%s: Grace-LSA not found [interface %s] [area %pI4]", + __func__, oi->interface->name, + &area->area_id); + continue; + } + + ospf6_lsa_purge(lsa); + } + } +} + +/* Exit from the Graceful Restart mode. */ +static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) +{ + struct ospf6_area *area; + struct listnode *onode, *anode; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("GR: exiting graceful restart: %s", reason); + + ospf6->gr_info.restart_in_progress = false; + ospf6->gr_info.finishing_restart = true; + THREAD_OFF(ospf6->gr_info.t_grace_period); + + /* Record in non-volatile memory that the restart is complete. */ + ospf6_gr_nvm_delete(ospf6); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) { + struct ospf6_interface *oi; + + /* + * 1) The router should reoriginate its router-LSAs for all + * attached areas in order to make sure they have the correct + * contents. + */ + OSPF6_ROUTER_LSA_EXECUTE(area); + + for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) { + OSPF6_LINK_LSA_EXECUTE(oi); + + /* + * 2) The router should reoriginate network-LSAs on all + * segments where it is the Designated Router. + */ + if (oi->state == OSPF6_INTERFACE_DR) + OSPF6_NETWORK_LSA_EXECUTE(oi); + } + } + + /* + * 3) The router reruns its OSPF routing calculations, this time + * installing the results into the system forwarding table, and + * originating summary-LSAs, Type-7 LSAs and AS-external-LSAs as + * necessary. + * + * 4) Any remnant entries in the system forwarding table that were + * installed before the restart, but that are no longer valid, + * should be removed. + */ + ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_GR_FINISH); + + /* 6) Any grace-LSAs that the router originated should be flushed. */ + ospf6_gr_flush_grace_lsas(ospf6); +} + +#define RTR_LSA_MISSING 0 +#define RTR_LSA_ADJ_FOUND 1 +#define RTR_LSA_ADJ_NOT_FOUND 2 + +/* Check if a Router-LSA exists and if it contains a given link. */ +static int ospf6_router_lsa_contains_adj(struct ospf6_area *area, + in_addr_t adv_router, + in_addr_t neighbor_router_id) +{ + uint16_t type; + struct ospf6_lsa *lsa; + bool empty = true; + + type = ntohs(OSPF6_LSTYPE_ROUTER); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, adv_router, lsa)) { + struct ospf6_router_lsa *router_lsa; + char *start, *end, *current; + + empty = false; + router_lsa = (struct ospf6_router_lsa + *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + + /* Iterate over all interfaces in the Router-LSA. */ + start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_router_lsdesc) <= end; + current += sizeof(struct ospf6_router_lsdesc)) { + struct ospf6_router_lsdesc *lsdesc; + + lsdesc = (struct ospf6_router_lsdesc *)current; + if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT) + continue; + + if (lsdesc->neighbor_router_id == neighbor_router_id) + return RTR_LSA_ADJ_FOUND; + } + } + + if (empty) + return RTR_LSA_MISSING; + + return RTR_LSA_ADJ_NOT_FOUND; +} + +static bool ospf6_gr_check_router_lsa_consistency(struct ospf6 *ospf6, + struct ospf6_area *area, + struct ospf6_lsa *lsa) +{ + if (lsa->header->adv_router == ospf6->router_id) { + struct ospf6_router_lsa *router_lsa; + char *start, *end, *current; + + router_lsa = (struct ospf6_router_lsa + *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + + /* Iterate over all interfaces in the Router-LSA. */ + start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_router_lsdesc) <= end; + current += sizeof(struct ospf6_router_lsdesc)) { + struct ospf6_router_lsdesc *lsdesc; + + lsdesc = (struct ospf6_router_lsdesc *)current; + if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT) + continue; + + if (ospf6_router_lsa_contains_adj( + area, lsdesc->neighbor_router_id, + ospf6->router_id) + == RTR_LSA_ADJ_NOT_FOUND) + return false; + } + } else { + int adj1, adj2; + + adj1 = ospf6_router_lsa_contains_adj(area, ospf6->router_id, + lsa->header->adv_router); + adj2 = ospf6_router_lsa_contains_adj( + area, lsa->header->adv_router, ospf6->router_id); + if ((adj1 == RTR_LSA_ADJ_FOUND && adj2 == RTR_LSA_ADJ_NOT_FOUND) + || (adj1 == RTR_LSA_ADJ_NOT_FOUND + && adj2 == RTR_LSA_ADJ_FOUND)) + return false; + } + + return true; +} + +/* + * Check for LSAs that are inconsistent with the pre-restart LSAs, and abort the + * ongoing graceful restart when that's the case. + */ +void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf6, + struct ospf6_area *area) +{ + uint16_t type; + struct ospf6_lsa *lsa; + + type = ntohs(OSPF6_LSTYPE_ROUTER); + for (ALL_LSDB_TYPED(area->lsdb, type, lsa)) { + if (!ospf6_gr_check_router_lsa_consistency(ospf6, area, lsa)) { + char reason[256]; + + snprintfrr(reason, sizeof(reason), + "detected inconsistent LSA %s [area %pI4]", + lsa->name, &area->area_id); + ospf6_gr_restart_exit(ospf6, reason); + return; + } + } +} + +/* Check if there's a fully formed adjacency with the given neighbor ID. */ +static bool ospf6_gr_check_adj_id(struct ospf6_area *area, + in_addr_t neighbor_router_id) +{ + struct ospf6_neighbor *nbr; + + nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id); + if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("GR: missing adjacency to router %pI4", + &neighbor_router_id); + return false; + } + + return true; +} + +static bool ospf6_gr_check_adjs_lsa_transit(struct ospf6_area *area, + in_addr_t neighbor_router_id, + uint32_t neighbor_interface_id) +{ + struct ospf6 *ospf6 = area->ospf6; + + /* Check if we are the DR. */ + if (neighbor_router_id == ospf6->router_id) { + struct ospf6_lsa *lsa; + char *start, *end, *current; + struct ospf6_network_lsa *network_lsa; + struct ospf6_network_lsdesc *lsdesc; + + /* Lookup Network LSA corresponding to this interface. */ + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK), + neighbor_interface_id, + neighbor_router_id, area->lsdb); + if (!lsa) + return false; + + /* Iterate over all routers present in the network. */ + network_lsa = (struct ospf6_network_lsa + *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + start = (char *)network_lsa + sizeof(struct ospf6_network_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_network_lsdesc) <= end; + current += sizeof(struct ospf6_network_lsdesc)) { + lsdesc = (struct ospf6_network_lsdesc *)current; + + /* Skip self in the pseudonode. */ + if (lsdesc->router_id == ospf6->router_id) + continue; + + /* + * Check if there's a fully formed adjacency with this + * router. + */ + if (!ospf6_gr_check_adj_id(area, lsdesc->router_id)) + return false; + } + } else { + struct ospf6_neighbor *nbr; + + /* Check if there's a fully formed adjacency with the DR. */ + nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id); + if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: missing adjacency to DR router %pI4", + &neighbor_router_id); + return false; + } + } + + return true; +} + +static bool ospf6_gr_check_adjs_lsa(struct ospf6_area *area, + struct ospf6_lsa *lsa) +{ + struct ospf6_router_lsa *router_lsa; + char *start, *end, *current; + + router_lsa = + (struct ospf6_router_lsa *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + + /* Iterate over all interfaces in the Router-LSA. */ + start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_router_lsdesc) <= end; + current += sizeof(struct ospf6_router_lsdesc)) { + struct ospf6_router_lsdesc *lsdesc; + + lsdesc = (struct ospf6_router_lsdesc *)current; + switch (lsdesc->type) { + case OSPF6_ROUTER_LSDESC_POINTTOPOINT: + if (!ospf6_gr_check_adj_id(area, + lsdesc->neighbor_router_id)) + return false; + break; + case OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK: + if (!ospf6_gr_check_adjs_lsa_transit( + area, lsdesc->neighbor_router_id, + lsdesc->neighbor_interface_id)) + return false; + break; + default: + break; + } + } + + return true; +} + +/* + * Check if all adjacencies prior to the restart were reestablished. + * + * This is done using pre-restart Router LSAs and pre-restart Network LSAs + * received from the helping neighbors. + */ +static bool ospf6_gr_check_adjs(struct ospf6 *ospf6) +{ + struct ospf6_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) { + uint16_t type; + uint32_t router; + struct ospf6_lsa *lsa_self; + bool found = false; + + type = ntohs(OSPF6_LSTYPE_ROUTER); + router = ospf6->router_id; + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, router, + lsa_self)) { + found = true; + if (!ospf6_gr_check_adjs_lsa(area, lsa_self)) + return false; + } + if (!found) + return false; + } + + return true; +} + +/* Handling of grace period expiry. */ +static int ospf6_gr_grace_period_expired(struct thread *thread) +{ + struct ospf6 *ospf6 = THREAD_ARG(thread); + + ospf6->gr_info.t_grace_period = NULL; + ospf6_gr_restart_exit(ospf6, "grace period has expired"); + + return 0; +} + +/* + * Record in non-volatile memory that the given OSPF instance is attempting to + * perform a graceful restart. + */ +static void ospf6_gr_nvm_update(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6D_GR_STATE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + /* + * Record not only the grace period, but also a UNIX timestamp + * corresponding to the end of that period. That way, once ospf6d is + * restarted, it will be possible to take into account the time that + * passed while ospf6d wasn't running. + */ + json_object_int_add(json_instance, "gracePeriod", + ospf6->gr_info.grace_period); + json_object_int_add(json_instance, "timestamp", + time(NULL) + ospf6->gr_info.grace_period); + + json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Delete GR status information about the given OSPF instance from non-volatile + * memory. + */ +static void ospf6_gr_nvm_delete(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6D_GR_STATE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_del(json_instances, inst_name); + + json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Fetch from non-volatile memory whether the given OSPF instance is performing + * a graceful shutdown or not. + */ +void ospf6_gr_nvm_read(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + json_object *json_timestamp; + time_t timestamp = 0; + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6D_GR_STATE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + json_object_object_get_ex(json_instance, "timestamp", &json_timestamp); + if (json_timestamp) { + time_t now; + unsigned long remaining_time; + + /* Check if the grace period has already expired. */ + now = time(NULL); + timestamp = json_object_get_int(json_timestamp); + if (now > timestamp) { + ospf6_gr_restart_exit( + ospf6, "grace period has expired already"); + } else { + /* Schedule grace period timeout. */ + ospf6->gr_info.restart_in_progress = true; + remaining_time = timestamp - time(NULL); + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: remaining time until grace period expires: %lu(s)", + remaining_time); + thread_add_timer(master, ospf6_gr_grace_period_expired, + ospf6, remaining_time, + &ospf6->gr_info.t_grace_period); + } + } + + json_object_object_del(json_instances, inst_name); + + json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* Prepare to start a Graceful Restart. */ +static void ospf6_gr_prepare(void) +{ + struct ospf6 *ospf6; + struct ospf6_interface *oi; + struct listnode *onode, *anode, *inode; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, onode, ospf6)) { + struct ospf6_area *area; + + if (!ospf6->gr_info.restart_support + || ospf6->gr_info.prepare_in_progress) + continue; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]", + ospf6->gr_info.grace_period, + ospf6_vrf_id_to_name(ospf6->vrf_id)); + + /* Freeze OSPF routes in the RIB. */ + if (ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period)) { + zlog_warn( + "%s: failed to activate graceful restart: not connected to zebra", + __func__); + continue; + } + + /* Send a Grace-LSA to all neighbors. */ + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) { + for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) { + if (oi->state < OSPF6_INTERFACE_POINTTOPOINT) + continue; + ospf6_gr_lsa_originate(oi); + } + } + + /* Record end of the grace period in non-volatile memory. */ + ospf6_gr_nvm_update(ospf6); + + /* + * Mark that a Graceful Restart preparation is in progress, to + * prevent ospf6d from flushing its self-originated LSAs on + * exit. + */ + ospf6->gr_info.prepare_in_progress = true; + } +} + +static int ospf6_gr_neighbor_change(struct ospf6_neighbor *on, int next_state, + int prev_state) +{ + struct ospf6 *ospf6 = on->ospf6_if->area->ospf6; + + if (next_state == OSPF6_NEIGHBOR_FULL + && ospf6->gr_info.restart_in_progress) { + if (ospf6_gr_check_adjs(ospf6)) { + ospf6_gr_restart_exit( + ospf6, "all adjacencies were reestablished"); + } else { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: not all adjacencies were reestablished yet"); + } + } + + return 0; +} + +int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6) +{ + if (!ospf6->gr_info.restart_support) + return 0; + + if (ospf6->gr_info.grace_period == OSPF6_DFLT_GRACE_INTERVAL) + vty_out(vty, " graceful-restart\n"); + else + vty_out(vty, " graceful-restart grace-period %u\n", + ospf6->gr_info.grace_period); + + return 0; +} + +DEFPY(ospf6_graceful_restart_prepare, ospf6_graceful_restart_prepare_cmd, + "graceful-restart prepare ipv6 ospf", + "Graceful Restart commands\n" + "Prepare upcoming graceful restart\n" IPV6_STR + "Prepare to restart the OSPFv3 process") +{ + ospf6_gr_prepare(); + + return CMD_SUCCESS; +} + +DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd, + "graceful-restart [grace-period (1-1800)$grace_period]", + OSPF_GR_STR + "Maximum length of the 'grace period'\n" + "Maximum length of the 'grace period' in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6); + + /* Check and get restart period if present. */ + if (!grace_period_str) + grace_period = OSPF6_DFLT_GRACE_INTERVAL; + + ospf6->gr_info.restart_support = true; + ospf6->gr_info.grace_period = grace_period; + + return CMD_SUCCESS; +} + +DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd, + "no graceful-restart [period (1-1800)]", + NO_STR OSPF_GR_STR + "Maximum length of the 'grace period'\n" + "Maximum length of the 'grace period' in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6); + + if (!ospf6->gr_info.restart_support) + return CMD_SUCCESS; + + if (ospf6->gr_info.prepare_in_progress) { + vty_out(vty, + "%% Error: Graceful Restart preparation in progress\n"); + return CMD_WARNING; + } + + ospf6->gr_info.restart_support = false; + ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL; + + return CMD_SUCCESS; +} + +void ospf6_gr_init(void) +{ + hook_register(ospf6_neighbor_change, ospf6_gr_neighbor_change); + + install_element(ENABLE_NODE, &ospf6_graceful_restart_prepare_cmd); + install_element(OSPF6_NODE, &ospf6_graceful_restart_cmd); + install_element(OSPF6_NODE, &ospf6_no_graceful_restart_cmd); +} diff --git a/ospf6d/ospf6_gr.h b/ospf6d/ospf6_gr.h index 378b7193cd..6406e8efee 100644 --- a/ospf6d/ospf6_gr.h +++ b/ospf6d/ospf6_gr.h @@ -32,6 +32,10 @@ #define OSPF6_MAX_GRACE_INTERVAL 1800 #define OSPF6_MIN_GRACE_INTERVAL 1 +#define OSPF6_DFLT_GRACE_INTERVAL 120 + +/* Forward declaration(s). */ +struct ospf6_neighbor; /* Debug option */ extern unsigned char conf_debug_ospf6_gr; @@ -67,7 +71,8 @@ enum ospf6_gr_helper_rejected_reason { OSPF6_HELPER_NOT_A_VALID_NEIGHBOUR, OSPF6_HELPER_PLANNED_ONLY_RESTART, OSPF6_HELPER_TOPO_CHANGE_RTXMT_LIST, - OSPF6_HELPER_LSA_AGE_MORE + OSPF6_HELPER_LSA_AGE_MORE, + OSPF6_HELPER_RESTARTING, }; #ifdef roundup @@ -119,6 +124,11 @@ struct grace_tlv_restart_reason { #define OSPF6_GRACE_LSA_MIN_SIZE \ GRACE_PERIOD_TLV_SIZE + GRACE_RESTART_REASON_TLV_SIZE +struct ospf6_grace_lsa { + struct grace_tlv_graceperiod tlv_period; + struct grace_tlv_restart_reason tlv_reason; +}; + struct advRtr { in_addr_t advRtrAddr; }; @@ -156,6 +166,13 @@ extern void ospf6_process_maxage_grace_lsa(struct ospf6 *ospf, struct ospf6_neighbor *nbr); extern void ospf6_helper_handle_topo_chg(struct ospf6 *ospf6, struct ospf6_lsa *lsa); +extern int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6); extern int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6); extern int config_write_ospf6_debug_gr_helper(struct vty *vty); + +extern void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf, + struct ospf6_area *area); +extern void ospf6_gr_nvm_read(struct ospf6 *ospf); +extern void ospf6_gr_init(void); + #endif /* OSPF6_GR_H */ diff --git a/ospf6d/ospf6_gr_helper.c b/ospf6d/ospf6_gr_helper.c index 76496c050f..4522bd2619 100644 --- a/ospf6d/ospf6_gr_helper.c +++ b/ospf6d/ospf6_gr_helper.c @@ -360,6 +360,16 @@ int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa, return OSPF6_GR_NOT_HELPER; } + if (ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s: router is in the process of graceful restart", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_RESTARTING; + return OSPF6_GR_NOT_HELPER; + } + /* check supported grace period configured * if configured, use this to start the grace * timer otherwise use the interval received @@ -1155,7 +1165,10 @@ DEFPY(show_ipv6_ospf6_gr_helper, bool detail = false; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); - OSPF6_CMD_CHECK_RUNNING(); + if (ospf6 == NULL) { + vty_out(vty, "OSPFv3 is not configured\n"); + return CMD_SUCCESS; + } if (argv_find(argv, argc, "detail", &idx)) detail = true; diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index b427a0c9bd..b63a3c02db 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -1330,7 +1330,6 @@ DEFUN(show_ipv6_ospf6_interface, show_ipv6_ospf6_interface_ifname_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_ifname += 2; @@ -1547,7 +1546,6 @@ DEFUN(show_ipv6_ospf6_interface_traffic, show_ipv6_ospf6_interface_traffic_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { @@ -1590,7 +1588,6 @@ DEFUN(show_ipv6_ospf6_interface_ifname_prefix, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_ifname += 2; @@ -1651,7 +1648,6 @@ DEFUN(show_ipv6_ospf6_interface_prefix, show_ipv6_ospf6_interface_prefix_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_prefix += 2; diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 06a950156b..bea8cf0edd 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -47,6 +47,7 @@ #include "ospf6_flood.h" #include "ospf6d.h" #include "ospf6_spf.h" +#include "ospf6_gr.h" unsigned char conf_debug_ospf6_brouter = 0; uint32_t conf_debug_ospf6_brouter_specific_router_id; @@ -249,6 +250,13 @@ int ospf6_router_lsa_originate(struct thread *thread) oa = (struct ospf6_area *)THREAD_ARG(thread); oa->thread_router_lsa = NULL; + if (oa->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return 0; + } + if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER)) zlog_debug("Originate Router-LSA for Area %s", oa->name); @@ -532,6 +540,13 @@ int ospf6_network_lsa_originate(struct thread *thread) by ospf6_lsa_refresh (), and does not come here. */ assert(oi->area); + if (oi->area->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return 0; + } + old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK), htonl(oi->interface->ifindex), oi->area->ospf6->router_id, oi->area->lsdb); @@ -773,6 +788,14 @@ int ospf6_link_lsa_originate(struct thread *thread) assert(oi->area); + if (oi->area->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return 0; + } + + /* find previous LSA */ old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_LINK), htonl(oi->interface->ifindex), @@ -1009,6 +1032,13 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread) oa = (struct ospf6_area *)THREAD_ARG(thread); oa->thread_intra_prefix_lsa = NULL; + if (oa->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return 0; + } + /* find previous LSA */ old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX), htonl(0), oa->ospf6->router_id, oa->lsdb); @@ -1243,6 +1273,13 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread) assert(oi->area); + if (oi->area->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return 0; + } + /* find previous LSA */ old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX), htonl(oi->interface->ifindex), diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h index 4031ffe689..f15bf0b9b4 100644 --- a/ospf6d/ospf6_intra.h +++ b/ospf6d/ospf6_intra.h @@ -192,12 +192,26 @@ struct ospf6_intra_prefix_lsa { oi, 0, &(oi)->thread_as_extern_lsa); \ } while (0) +#define OSPF6_ROUTER_LSA_EXECUTE(oa) \ + do { \ + if (CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ + thread_execute(master, ospf6_router_lsa_originate, oa, \ + 0); \ + } while (0) + #define OSPF6_NETWORK_LSA_EXECUTE(oi) \ do { \ THREAD_OFF((oi)->thread_network_lsa); \ thread_execute(master, ospf6_network_lsa_originate, oi, 0); \ } while (0) +#define OSPF6_LINK_LSA_EXECUTE(oi) \ + do { \ + if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ + thread_execute(master, ospf6_link_lsa_originate, oi, \ + 0); \ + } while (0) + #define OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi) \ do { \ THREAD_OFF((oi)->thread_intra_prefix_lsa); \ diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 1bc1ce9cdf..ac07704d2c 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -1021,6 +1021,30 @@ static char *ospf6_lsa_handler_name(const struct ospf6_lsa_handler *h) return buf; } +DEFPY (debug_ospf6_lsa_all, + debug_ospf6_lsa_all_cmd, + "[no$no] debug ospf6 lsa all", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "Display for all types of LSAs\n") +{ + unsigned int i; + struct ospf6_lsa_handler *handler = NULL; + + for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) { + handler = vector_slot(ospf6_lsa_handler_vector, i); + if (handler == NULL) + continue; + if (!no) + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL); + else + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL); + } + return CMD_SUCCESS; +} + DEFPY (debug_ospf6_lsa_aggregation, debug_ospf6_lsa_aggregation_cmd, "[no] debug ospf6 lsa aggregation", @@ -1152,6 +1176,8 @@ DEFUN (no_debug_ospf6_lsa_type, void install_element_ospf6_debug_lsa(void) { + install_element(ENABLE_NODE, &debug_ospf6_lsa_all_cmd); + install_element(CONFIG_NODE, &debug_ospf6_lsa_all_cmd); install_element(ENABLE_NODE, &debug_ospf6_lsa_hex_cmd); install_element(ENABLE_NODE, &no_debug_ospf6_lsa_hex_cmd); install_element(CONFIG_NODE, &debug_ospf6_lsa_hex_cmd); @@ -1165,6 +1191,23 @@ int config_write_ospf6_debug_lsa(struct vty *vty) { unsigned int i; const struct ospf6_lsa_handler *handler; + bool debug_all = true; + + for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) { + handler = vector_slot(ospf6_lsa_handler_vector, i); + if (handler == NULL) + continue; + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL) + < OSPF6_LSA_DEBUG_ALL) { + debug_all = false; + break; + } + } + + if (debug_all) { + vty_out(vty, "debug ospf6 lsa all\n"); + return 0; + } for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) { handler = vector_slot(ospf6_lsa_handler_vector, i); diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index c271965fe7..2316040694 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -28,6 +28,9 @@ #define OSPF6_LSA_DEBUG_ORIGINATE 0x02 #define OSPF6_LSA_DEBUG_EXAMIN 0x04 #define OSPF6_LSA_DEBUG_FLOOD 0x08 +#define OSPF6_LSA_DEBUG_ALL \ + (OSPF6_LSA_DEBUG | OSPF6_LSA_DEBUG_ORIGINATE | OSPF6_LSA_DEBUG_EXAMIN \ + | OSPF6_LSA_DEBUG_FLOOD) #define OSPF6_LSA_DEBUG_AGGR 0x10 /* OSPF LSA Default metric values */ diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 3dcc74589a..53f3c3468a 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -553,6 +553,21 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, return; } + /* + * RFC 3623 - Section 2: + * "If the restarting router determines that it was the Designated + * Router on a given segment prior to the restart, it elects + * itself as the Designated Router again. The restarting router + * knows that it was the Designated Router if, while the + * associated interface is in Waiting state, a Hello packet is + * received from a neighbor listing the router as the Designated + * Router". + */ + if (oi->area->ospf6->gr_info.restart_in_progress + && oi->state == OSPF6_INTERFACE_WAITING + && hello->drouter == oi->area->ospf6->router_id) + oi->drouter = hello->drouter; + /* Schedule interface events */ if (backupseen) thread_add_event(master, backup_seen, oi, 0, NULL); diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 4ea615f32b..35fbd3991c 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -90,6 +90,22 @@ struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t router_id, return (struct ospf6_neighbor *)NULL; } +struct ospf6_neighbor *ospf6_area_neighbor_lookup(struct ospf6_area *area, + uint32_t router_id) +{ + struct ospf6_interface *oi; + struct ospf6_neighbor *nbr; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(area->if_list, node, oi)) { + nbr = ospf6_neighbor_lookup(router_id, oi); + if (nbr) + return nbr; + } + + return NULL; +} + /* create ospf6_neighbor */ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, struct ospf6_interface *oi) @@ -1081,7 +1097,6 @@ DEFUN(show_ipv6_ospf6_neighbor, show_ipv6_ospf6_neighbor_cmd, bool detail = false; bool drchoice = false; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (argv_find(argv, argc, "detail", &idx_type)) @@ -1156,7 +1171,6 @@ DEFUN(show_ipv6_ospf6_neighbor_one, show_ipv6_ospf6_neighbor_one_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_ipv4 += 2; diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 30e044da4b..f7735b87b9 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -23,6 +23,9 @@ #include "hook.h" +/* Forward declaration(s). */ +struct ospf6_area; + /* Debug option */ extern unsigned char conf_debug_ospf6_neighbor; #define OSPF6_DEBUG_NEIGHBOR_STATE 0x01 @@ -185,6 +188,8 @@ void ospf6_neighbor_dbex_init(struct ospf6_neighbor *on); struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t router_id, struct ospf6_interface *oi); +struct ospf6_neighbor *ospf6_area_neighbor_lookup(struct ospf6_area *area, + uint32_t router_id); struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, struct ospf6_interface *oi); void ospf6_neighbor_delete(struct ospf6_neighbor *on); diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 13003b4151..8bfd3b7124 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -37,6 +37,9 @@ #include "ospf6_interface.h" #include "ospf6d.h" #include "ospf6_zebra.h" +#ifndef VTYSH_EXTRACT_PL +#include "ospf6d/ospf6_route_clippy.c" +#endif DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE, "OSPF6 route"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE_TABLE, "OSPF6 route table"); @@ -1117,11 +1120,6 @@ void ospf6_route_show(struct vty *vty, struct ospf6_route *route, json_object *json_array_next_hops = NULL; json_object *json_next_hop; - if (om6->ospf6 == NULL) { - vty_out(vty, "OSPFv3 is not running\n"); - return; - } - monotime(&now); timersub(&now, &route->changed, &res); timerstring(&res, duration, sizeof(duration)); @@ -1205,11 +1203,6 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route, json_object *json_array_next_hops = NULL; json_object *json_next_hop; - if (om6->ospf6 == NULL) { - vty_out(vty, "OSPFv3 is not running\n"); - return; - } - monotime(&now); /* destination */ @@ -1837,49 +1830,27 @@ void ospf6_brouter_show(struct vty *vty, struct ospf6_route *route) OSPF6_PATH_TYPE_NAME(route->path.type), area); } -DEFUN (debug_ospf6_route, - debug_ospf6_route_cmd, - "debug ospf6 route <table|intra-area|inter-area|memory>", - DEBUG_STR - OSPF6_STR - "Debug routes\n" - "Debug route table calculation\n" - "Debug intra-area route calculation\n" - "Debug inter-area route calculation\n" - "Debug route memory use\n" - ) +DEFPY(debug_ospf6_route, + debug_ospf6_route_cmd, + "[no$no] debug ospf6 route <all|table|intra-area|inter-area|memory>", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug routes\n" + "Debug for all types of route calculation\n" + "Debug route table calculation\n" + "Debug intra-area route calculation\n" + "Debug inter-area route calculation\n" + "Debug route memory use\n") { - int idx_type = 3; + int idx_type; unsigned char level = 0; - if (!strcmp(argv[idx_type]->text, "table")) - level = OSPF6_DEBUG_ROUTE_TABLE; - else if (!strcmp(argv[idx_type]->text, "intra-area")) - level = OSPF6_DEBUG_ROUTE_INTRA; - else if (!strcmp(argv[idx_type]->text, "inter-area")) - level = OSPF6_DEBUG_ROUTE_INTER; - else if (!strcmp(argv[idx_type]->text, "memory")) - level = OSPF6_DEBUG_ROUTE_MEMORY; - OSPF6_DEBUG_ROUTE_ON(level); - return CMD_SUCCESS; -} - -DEFUN (no_debug_ospf6_route, - no_debug_ospf6_route_cmd, - "no debug ospf6 route <table|intra-area|inter-area|memory>", - NO_STR - DEBUG_STR - OSPF6_STR - "Debug routes\n" - "Debug route table calculation\n" - "Debug intra-area route calculation\n" - "Debug inter-area route calculation\n" - "Debug route memory use\n") -{ - int idx_type = 4; - unsigned char level = 0; + idx_type = ((no) ? 4 : 3); - if (!strcmp(argv[idx_type]->text, "table")) + if (!strcmp(argv[idx_type]->text, "all")) + level = OSPF6_DEBUG_ROUTE_ALL; + else if (!strcmp(argv[idx_type]->text, "table")) level = OSPF6_DEBUG_ROUTE_TABLE; else if (!strcmp(argv[idx_type]->text, "intra-area")) level = OSPF6_DEBUG_ROUTE_INTRA; @@ -1887,12 +1858,20 @@ DEFUN (no_debug_ospf6_route, level = OSPF6_DEBUG_ROUTE_INTER; else if (!strcmp(argv[idx_type]->text, "memory")) level = OSPF6_DEBUG_ROUTE_MEMORY; - OSPF6_DEBUG_ROUTE_OFF(level); + + if (no) + OSPF6_DEBUG_ROUTE_OFF(level); + else + OSPF6_DEBUG_ROUTE_ON(level); return CMD_SUCCESS; } int config_write_ospf6_debug_route(struct vty *vty) { + if (IS_OSPF6_DEBUG_ROUTE(ALL) == OSPF6_DEBUG_ROUTE_ALL) { + vty_out(vty, "debug ospf6 route all\n"); + return 0; + } if (IS_OSPF6_DEBUG_ROUTE(TABLE)) vty_out(vty, "debug ospf6 route table\n"); if (IS_OSPF6_DEBUG_ROUTE(INTRA)) @@ -1908,7 +1887,5 @@ int config_write_ospf6_debug_route(struct vty *vty) void install_element_ospf6_debug_route(void) { install_element(ENABLE_NODE, &debug_ospf6_route_cmd); - install_element(ENABLE_NODE, &no_debug_ospf6_route_cmd); install_element(CONFIG_NODE, &debug_ospf6_route_cmd); - install_element(CONFIG_NODE, &no_debug_ospf6_route_cmd); } diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index 54baaee638..e29439b95e 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -33,7 +33,10 @@ extern unsigned char conf_debug_ospf6_route; #define OSPF6_DEBUG_ROUTE_TABLE 0x01 #define OSPF6_DEBUG_ROUTE_INTRA 0x02 #define OSPF6_DEBUG_ROUTE_INTER 0x04 -#define OSPF6_DEBUG_ROUTE_MEMORY 0x80 +#define OSPF6_DEBUG_ROUTE_MEMORY 0x08 +#define OSPF6_DEBUG_ROUTE_ALL \ + (OSPF6_DEBUG_ROUTE_TABLE | OSPF6_DEBUG_ROUTE_INTRA \ + | OSPF6_DEBUG_ROUTE_INTER | OSPF6_DEBUG_ROUTE_MEMORY) #define OSPF6_DEBUG_ROUTE_ON(level) (conf_debug_ospf6_route |= (level)) #define OSPF6_DEBUG_ROUTE_OFF(level) (conf_debug_ospf6_route &= ~(level)) #define IS_OSPF6_DEBUG_ROUTE(e) (conf_debug_ospf6_route & OSPF6_DEBUG_ROUTE_##e) diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 1412298802..e4de6ccf91 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -44,6 +44,7 @@ #include "ospf6d.h" #include "ospf6_abr.h" #include "ospf6_nssa.h" +#include "ospf6_zebra.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex"); @@ -438,8 +439,8 @@ void ospf6_spf_table_finish(struct ospf6_route_table *result_table) } } -static const char *const ospf6_spf_reason_str[] = {"R+", "R-", "N+", "N-", "L+", - "L-", "R*", "N*", "C"}; +static const char *const ospf6_spf_reason_str[] = { + "R+", "R-", "N+", "N-", "L+", "L-", "R*", "N*", "C", "A", "GR"}; void ospf6_spf_reason_string(unsigned int reason, char *buf, int size) { @@ -1255,6 +1256,17 @@ static int ospf6_ase_calculate_timer(struct thread *t) ospf6_ase_calculate_route(ospf6, lsa, area); } } + + if (ospf6->gr_info.finishing_restart) { + /* + * The routing table computation is complete. Uninstall remnant + * routes that were installed before the restart, but that are + * no longer valid. + */ + ospf6_zebra_gr_disable(ospf6); + ospf6->gr_info.finishing_restart = false; + } + return 0; } diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h index d6fbc5c13b..cc52d16861 100644 --- a/ospf6d/ospf6_spf.h +++ b/ospf6d/ospf6_spf.h @@ -93,6 +93,7 @@ struct ospf6_vertex { #define OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED (1 << 7) #define OSPF6_SPF_FLAGS_CONFIG_CHANGE (1 << 8) #define OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE (1 << 9) +#define OSPF6_SPF_FLAGS_GR_FINISH (1 << 10) static inline void ospf6_set_spf_reason(struct ospf6 *ospf, unsigned int reason) { diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 6ff3789a80..3122d616cb 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -472,6 +472,12 @@ struct ospf6 *ospf6_instance_create(const char *name) if (ospf6->fd < 0) return ospf6; + /* + * Read from non-volatile memory whether this instance is performing a + * graceful restart or not. + */ + ospf6_gr_nvm_read(ospf6); + thread_add_read(master, ospf6_receive, ospf6, ospf6->fd, &ospf6->t_ospf6_receive); @@ -488,7 +494,8 @@ void ospf6_delete(struct ospf6 *o) QOBJ_UNREG(o); ospf6_gr_helper_deinit(o); - ospf6_flush_self_originated_lsas_now(o); + if (!o->gr_info.prepare_in_progress) + ospf6_flush_self_originated_lsas_now(o); ospf6_disable(o); ospf6_del(o); @@ -555,6 +562,7 @@ static void ospf6_disable(struct ospf6 *o) THREAD_OFF(o->t_distribute_update); THREAD_OFF(o->t_ospf6_receive); THREAD_OFF(o->t_external_aggr); + THREAD_OFF(o->gr_info.t_grace_period); } } @@ -1519,7 +1527,6 @@ DEFUN(show_ipv6_ospf6, show_ipv6_ospf6_cmd, bool uj = use_json(argc, argv); json_object *json = NULL; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { @@ -1560,7 +1567,6 @@ DEFUN(show_ipv6_ospf6_route, show_ipv6_ospf6_route_cmd, int idx_arg_start = 4; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_arg_start += 2; @@ -1594,7 +1600,6 @@ DEFUN(show_ipv6_ospf6_route_match, show_ipv6_ospf6_route_match_cmd, int idx_start_arg = 4; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_start_arg += 2; @@ -1629,7 +1634,6 @@ DEFUN(show_ipv6_ospf6_route_match_detail, int idx_start_arg = 4; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_start_arg += 2; @@ -1665,7 +1669,6 @@ DEFUN(show_ipv6_ospf6_route_type_detail, show_ipv6_ospf6_route_type_detail_cmd, int idx_start_arg = 4; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_start_arg += 2; @@ -2078,7 +2081,6 @@ DEFPY (show_ipv6_ospf6_external_aggregator, if (uj) json = json_object_new_object(); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { @@ -2236,6 +2238,7 @@ static int config_write_ospf6(struct vty *vty) ospf6_distance_config_write(vty, ospf6); ospf6_distribute_config_write(vty, ospf6); ospf6_asbr_summary_config_write(vty, ospf6); + config_write_ospf6_gr(vty, ospf6); config_write_ospf6_gr_helper(vty, ospf6); vty_out(vty, "exit\n"); diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 58ecf08495..3188b1f58f 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -60,6 +60,15 @@ struct ospf6_redist { #define ROUTEMAP(R) (R->route_map.map) }; +struct ospf6_gr_info { + bool restart_support; + bool restart_in_progress; + bool prepare_in_progress; + bool finishing_restart; + uint32_t grace_period; + struct thread *t_grace_period; +}; + struct ospf6_gr_helper { /* Gracefull restart Helper supported configs*/ /* Supported grace interval*/ @@ -192,6 +201,9 @@ struct ospf6 { */ uint16_t max_multipath; + /* OSPF Graceful Restart info (restarting mode) */ + struct ospf6_gr_info gr_info; + /*ospf6 Graceful restart helper info */ struct ospf6_gr_helper ospf6_helper_cfg; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 5403e643dc..c2e91d09bb 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -40,6 +40,7 @@ #include "ospf6_zebra.h" #include "ospf6d.h" #include "ospf6_area.h" +#include "ospf6_gr.h" #include "lib/json.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DISTANCE, "OSPF6 distance"); @@ -173,6 +174,36 @@ static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS) return 0; } +static int ospf6_zebra_gr_update(struct ospf6 *ospf6, int command, + uint32_t stale_time) +{ + struct zapi_cap api; + + if (!zclient || zclient->sock < 0 || !ospf6) + return 1; + + memset(&api, 0, sizeof(struct zapi_cap)); + api.cap = command; + api.stale_removal_time = stale_time; + api.vrf_id = ospf6->vrf_id; + + (void)zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, + &api); + + return 0; +} + +int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time) +{ + return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_CAPABILITIES, + stale_time); +} + +int ospf6_zebra_gr_disable(struct ospf6 *ospf6) +{ + return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_DISABLE, 0); +} + static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; @@ -384,12 +415,30 @@ static void ospf6_zebra_route_update(int type, struct ospf6_route *request, void ospf6_zebra_route_update_add(struct ospf6_route *request, struct ospf6 *ospf6) { + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not installing %pFX", + &request->prefix); + return; + } + ospf6_zebra_route_update(ADD, request, ospf6); } void ospf6_zebra_route_update_remove(struct ospf6_route *request, struct ospf6 *ospf6) { + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not uninstalling %pFX", + &request->prefix); + return; + } + ospf6_zebra_route_update(REM, request, ospf6); } @@ -398,6 +447,15 @@ void ospf6_zebra_add_discard(struct ospf6_route *request, struct ospf6 *ospf6) struct zapi_route api; struct prefix *dest = &request->prefix; + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not installing %pFX", + &request->prefix); + return; + } + if (!CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { memset(&api, 0, sizeof(api)); api.vrf_id = ospf6->vrf_id; @@ -426,6 +484,15 @@ void ospf6_zebra_delete_discard(struct ospf6_route *request, struct zapi_route api; struct prefix *dest = &request->prefix; + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not uninstalling %pFX", + &request->prefix); + return; + } + if (CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { memset(&api, 0, sizeof(api)); api.vrf_id = ospf6->vrf_id; diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h index e25f6bf80d..77e48673c8 100644 --- a/ospf6d/ospf6_zebra.h +++ b/ospf6d/ospf6_zebra.h @@ -65,6 +65,8 @@ extern uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, struct ospf6_route * or, struct ospf6 *ospf6); +extern int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time); +extern int ospf6_zebra_gr_disable(struct ospf6 *ospf6); extern int ospf6_distance_set(struct vty *vty, struct ospf6 *ospf6, const char *distance_str, const char *ip_str, const char *access_list_str); diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index 0e8185cfeb..2c8c9b9d45 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -413,7 +413,6 @@ DEFUN(show_ipv6_ospf6_database, show_ipv6_ospf6_database_cmd, int idx_vrf = 0; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_level += 2; @@ -460,7 +459,6 @@ DEFUN(show_ipv6_ospf6_database_type, show_ipv6_ospf6_database_type_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -505,7 +503,6 @@ DEFUN(show_ipv6_ospf6_database_id, show_ipv6_ospf6_database_id_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (argv[idx_ipv4]->type == IPV4_TKN) inet_pton(AF_INET, argv[idx_ipv4]->arg, &id); @@ -548,7 +545,6 @@ DEFUN(show_ipv6_ospf6_database_router, show_ipv6_ospf6_database_router_cmd, int idx_vrf = 0; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_ipv4 += 2; @@ -619,7 +615,6 @@ DEFUN_HIDDEN( bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_ipv4 += 2; @@ -672,7 +667,6 @@ DEFUN(show_ipv6_ospf6_database_type_id, show_ipv6_ospf6_database_type_id_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -731,7 +725,6 @@ DEFUN(show_ipv6_ospf6_database_type_router, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -782,7 +775,6 @@ DEFUN(show_ipv6_ospf6_database_id_router, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_ls_id += 2; @@ -833,7 +825,6 @@ DEFUN(show_ipv6_ospf6_database_adv_router_linkstate_id, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_adv_rtr += 2; @@ -891,7 +882,6 @@ DEFUN(show_ipv6_ospf6_database_type_id_router, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -960,7 +950,6 @@ DEFUN (show_ipv6_ospf6_database_type_adv_router_linkstate_id, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -1008,7 +997,6 @@ DEFUN(show_ipv6_ospf6_database_self_originated, uint32_t adv_router = 0; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_level += 2; @@ -1063,7 +1051,6 @@ DEFUN(show_ipv6_ospf6_database_type_self_originated, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -1123,7 +1110,6 @@ DEFUN(show_ipv6_ospf6_database_type_self_originated_linkstate_id, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -1185,7 +1171,6 @@ DEFUN(show_ipv6_ospf6_database_type_id_self_originated, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -1268,7 +1253,6 @@ DEFUN(show_ipv6_ospf6_border_routers, show_ipv6_ospf6_border_routers_cmd, int idx_vrf = 0; int idx_argc = 5; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_argc += 2; @@ -1308,7 +1292,6 @@ DEFUN(show_ipv6_ospf6_linkstate, show_ipv6_ospf6_linkstate_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_ipv4 += 2; @@ -1348,8 +1331,6 @@ DEFUN(show_ipv6_ospf6_linkstate_detail, show_ipv6_ospf6_linkstate_detail_cmd, bool all_vrf = false; int idx_vrf = 0; - - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_detail += 2; @@ -1402,6 +1383,7 @@ void ospf6_init(struct thread_master *master) ospf6_intra_init(); ospf6_asbr_init(); ospf6_abr_init(); + ospf6_gr_init(); ospf6_gr_helper_config_init(); /* initialize hooks for modifying filter rules */ diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index d5170be7cc..041a9b1df9 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -93,12 +93,6 @@ extern struct thread_master *master; #define OSPF6_ROUTER_ID_STR "Specify Router-ID\n" #define OSPF6_LS_ID_STR "Specify Link State ID\n" -#define OSPF6_CMD_CHECK_RUNNING() \ - if (om6->ospf6 == NULL) { \ - vty_out(vty, "OSPFv3 is not running\n"); \ - return CMD_SUCCESS; \ - } - #define IS_OSPF6_ASBR(O) ((O)->flag & OSPF6_FLAG_ASBR) #define OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) \ if (argv_find(argv, argc, "vrf", &idx_vrf)) { \ diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 5a4e4db69b..be626646a0 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -12,6 +12,7 @@ vtysh_scan += \ ospf6d/ospf6_area.c \ ospf6d/ospf6_bfd.c \ ospf6d/ospf6_flood.c \ + ospf6d/ospf6_gr.c \ ospf6d/ospf6_gr_helper.c \ ospf6d/ospf6_interface.c \ ospf6d/ospf6_intra.c \ @@ -40,6 +41,7 @@ ospf6d_libospf6_a_SOURCES = \ ospf6d/ospf6_routemap_nb_config.c \ ospf6d/ospf6_bfd.c \ ospf6d/ospf6_flood.c \ + ospf6d/ospf6_gr.c \ ospf6d/ospf6_gr_helper.c \ ospf6d/ospf6_interface.c \ ospf6d/ospf6_intra.c \ @@ -96,6 +98,8 @@ clippy_scan += \ ospf6d/ospf6_asbr.c \ ospf6d/ospf6_lsa.c \ ospf6d/ospf6_gr_helper.c \ + ospf6d/ospf6_gr.c \ + ospf6d/ospf6_route.c \ # end nodist_ospf6d_ospf6d_SOURCES = \ diff --git a/pimd/pim_assert.h b/pimd/pim_assert.h index 63fda3fe34..c07cbeb013 100644 --- a/pimd/pim_assert.h +++ b/pimd/pim_assert.h @@ -24,8 +24,22 @@ #include "if.h" -#include "pim_neighbor.h" -#include "pim_ifchannel.h" +struct pim_ifchannel; +struct pim_neighbor; + +enum pim_ifassert_state { + PIM_IFASSERT_NOINFO, + PIM_IFASSERT_I_AM_WINNER, + PIM_IFASSERT_I_AM_LOSER +}; + +struct pim_assert_metric { + uint32_t rpt_bit_flag; + uint32_t metric_preference; + uint32_t route_metric; + struct in_addr ip_address; /* neighbor router that sourced the Assert + message */ +}; /* RFC 4601: 4.11. Timer Values diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c index dfe2d5f2fa..c7fcbba71e 100644 --- a/pimd/pim_bfd.c +++ b/pimd/pim_bfd.c @@ -28,6 +28,7 @@ #include "zclient.h" #include "pim_instance.h" +#include "pim_neighbor.h" #include "pim_cmd.h" #include "pim_vty.h" #include "pim_iface.h" diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c index f2845ee6e1..a3a3426f39 100644 --- a/pimd/pim_bsm.c +++ b/pimd/pim_bsm.c @@ -28,6 +28,7 @@ #include "pimd.h" #include "pim_iface.h" #include "pim_instance.h" +#include "pim_neighbor.h" #include "pim_rpf.h" #include "pim_hello.h" #include "pim_pim.h" diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 1238e03a5b..14aa710524 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3430,112 +3430,87 @@ static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) pim->igmp_watermark_limit ? "Set" : "Not Set", pim->igmp_watermark_limit); vty_out(vty, - "Interface Address Group Mode Timer Srcs V Uptime \n"); + "Interface Group Mode Timer Srcs V Uptime \n"); } /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; if (!pim_ifp) continue; - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - char ifaddr_str[INET_ADDRSTRLEN]; - struct listnode *grpnode; - struct igmp_group *grp; - - pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, - sizeof(ifaddr_str)); - - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - char group_str[INET_ADDRSTRLEN]; - char hhmmss[10]; - char uptime[10]; + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, + grp)) { + char group_str[INET_ADDRSTRLEN]; + char hhmmss[10]; + char uptime[10]; - pim_inet4_dump("<group?>", grp->group_addr, - group_str, sizeof(group_str)); - pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), - grp->t_group_timer); - pim_time_uptime(uptime, sizeof(uptime), - now - grp->group_creation); + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); + pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), + grp->t_group_timer); + pim_time_uptime(uptime, sizeof(uptime), + now - grp->group_creation); - if (uj) { - json_object_object_get_ex( - json, ifp->name, &json_iface); - - if (!json_iface) { - json_iface = - json_object_new_object(); - json_object_pim_ifp_add( - json_iface, ifp); - json_object_object_add( - json, ifp->name, - json_iface); - json_groups = - json_object_new_array(); - json_object_object_add( - json_iface, - "groups", - json_groups); - } + if (uj) { + json_object_object_get_ex(json, ifp->name, + &json_iface); - json_group = json_object_new_object(); - json_object_string_add(json_group, - "source", - ifaddr_str); - json_object_string_add(json_group, - "group", - group_str); - - if (grp->igmp_version == 3) - json_object_string_add( - json_group, "mode", - grp->group_filtermode_isexcl + if (!json_iface) { + json_iface = json_object_new_object(); + json_object_pim_ifp_add(json_iface, + ifp); + json_object_object_add(json, ifp->name, + json_iface); + json_groups = json_object_new_array(); + json_object_object_add(json_iface, + "groups", + json_groups); + } + + json_group = json_object_new_object(); + json_object_string_add(json_group, "group", + group_str); + + if (grp->igmp_version == 3) + json_object_string_add( + json_group, "mode", + grp->group_filtermode_isexcl ? "EXCLUDE" : "INCLUDE"); - json_object_string_add(json_group, - "timer", hhmmss); - json_object_int_add( - json_group, "sourcesCount", - grp->group_source_list - ? listcount( - grp->group_source_list) - : 0); - json_object_int_add( - json_group, "version", - grp->igmp_version); - json_object_string_add( - json_group, "uptime", uptime); - json_object_array_add(json_groups, - json_group); - } else { - vty_out(vty, - "%-16s %-15s %-15s %4s %8s %4d %d %8s\n", - ifp->name, ifaddr_str, - group_str, - grp->igmp_version == 3 + json_object_string_add(json_group, "timer", + hhmmss); + json_object_int_add( + json_group, "sourcesCount", + grp->group_source_list ? listcount( + grp->group_source_list) + : 0); + json_object_int_add(json_group, "version", + grp->igmp_version); + json_object_string_add(json_group, "uptime", + uptime); + json_object_array_add(json_groups, json_group); + } else { + vty_out(vty, "%-16s %-15s %4s %8s %4d %d %8s\n", + ifp->name, group_str, + grp->igmp_version == 3 ? (grp->group_filtermode_isexcl - ? "EXCL" - : "INCL") + ? "EXCL" + : "INCL") : "----", - hhmmss, - grp->group_source_list - ? listcount( - grp->group_source_list) - : 0, - grp->igmp_version, uptime); - } - } /* scan igmp groups */ - } /* scan igmp sockets */ - } /* scan interfaces */ + hhmmss, + grp->group_source_list ? listcount( + grp->group_source_list) + : 0, + grp->igmp_version, uptime); + } + } /* scan igmp groups */ + } /* scan interfaces */ if (uj) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -3550,63 +3525,49 @@ static void igmp_show_group_retransmission(struct pim_instance *pim, struct interface *ifp; vty_out(vty, - "Interface Address Group RetTimer Counter RetSrcs\n"); + "Interface Group RetTimer Counter RetSrcs\n"); /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; if (!pim_ifp) continue; - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - char ifaddr_str[INET_ADDRSTRLEN]; - struct listnode *grpnode; - struct igmp_group *grp; - - pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, - sizeof(ifaddr_str)); - - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - char group_str[INET_ADDRSTRLEN]; - char grp_retr_mmss[10]; - struct listnode *src_node; - struct igmp_source *src; - int grp_retr_sources = 0; - - pim_inet4_dump("<group?>", grp->group_addr, - group_str, sizeof(group_str)); - pim_time_timer_to_mmss( - grp_retr_mmss, sizeof(grp_retr_mmss), - grp->t_group_query_retransmit_timer); - - - /* count group sources with retransmission state - */ - for (ALL_LIST_ELEMENTS_RO( - grp->group_source_list, src_node, - src)) { - if (src->source_query_retransmit_count - > 0) { - ++grp_retr_sources; - } + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, + grp)) { + char group_str[INET_ADDRSTRLEN]; + char grp_retr_mmss[10]; + struct listnode *src_node; + struct igmp_source *src; + int grp_retr_sources = 0; + + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); + pim_time_timer_to_mmss( + grp_retr_mmss, sizeof(grp_retr_mmss), + grp->t_group_query_retransmit_timer); + + + /* count group sources with retransmission state + */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, + src_node, src)) { + if (src->source_query_retransmit_count > 0) { + ++grp_retr_sources; } + } - vty_out(vty, "%-16s %-15s %-15s %-8s %7d %7d\n", - ifp->name, ifaddr_str, group_str, - grp_retr_mmss, - grp->group_specific_query_retransmit_count, - grp_retr_sources); + vty_out(vty, "%-16s %-15s %-8s %7d %7d\n", ifp->name, + group_str, grp_retr_mmss, + grp->group_specific_query_retransmit_count, + grp_retr_sources); - } /* scan igmp groups */ - } /* scan igmp sockets */ - } /* scan interfaces */ + } /* scan igmp groups */ + } /* scan interfaces */ } static void igmp_show_sources(struct pim_instance *pim, struct vty *vty) @@ -3617,71 +3578,54 @@ static void igmp_show_sources(struct pim_instance *pim, struct vty *vty) now = pim_time_monotonic_sec(); vty_out(vty, - "Interface Address Group Source Timer Fwd Uptime \n"); + "Interface Group Source Timer Fwd Uptime \n"); /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; if (!pim_ifp) continue; - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - char ifaddr_str[INET_ADDRSTRLEN]; - struct listnode *grpnode; - struct igmp_group *grp; + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, + grp)) { + char group_str[INET_ADDRSTRLEN]; + struct listnode *srcnode; + struct igmp_source *src; + + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); - pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, - sizeof(ifaddr_str)); + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, + srcnode, src)) { + char source_str[INET_ADDRSTRLEN]; + char mmss[10]; + char uptime[10]; - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - char group_str[INET_ADDRSTRLEN]; - struct listnode *srcnode; - struct igmp_source *src; + pim_inet4_dump("<source?>", src->source_addr, + source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", grp->group_addr, - group_str, sizeof(group_str)); + pim_time_timer_to_mmss(mmss, sizeof(mmss), + src->t_source_timer); - /* scan group sources */ - for (ALL_LIST_ELEMENTS_RO( - grp->group_source_list, srcnode, - src)) { - char source_str[INET_ADDRSTRLEN]; - char mmss[10]; - char uptime[10]; - - pim_inet4_dump( - "<source?>", src->source_addr, - source_str, sizeof(source_str)); - - pim_time_timer_to_mmss( - mmss, sizeof(mmss), - src->t_source_timer); - - pim_time_uptime( - uptime, sizeof(uptime), + pim_time_uptime(uptime, sizeof(uptime), now - src->source_creation); - vty_out(vty, - "%-16s %-15s %-15s %-15s %5s %3s %8s\n", - ifp->name, ifaddr_str, - group_str, source_str, mmss, - IGMP_SOURCE_TEST_FORWARDING( - src->source_flags) + vty_out(vty, "%-16s %-15s %-15s %5s %3s %8s\n", + ifp->name, group_str, source_str, mmss, + IGMP_SOURCE_TEST_FORWARDING( + src->source_flags) ? "Y" : "N", - uptime); + uptime); - } /* scan group sources */ - } /* scan igmp groups */ - } /* scan igmp sockets */ - } /* scan interfaces */ + } /* scan group sources */ + } /* scan igmp groups */ + } /* scan interfaces */ } static void igmp_show_source_retransmission(struct pim_instance *pim, @@ -3690,57 +3634,42 @@ static void igmp_show_source_retransmission(struct pim_instance *pim, struct interface *ifp; vty_out(vty, - "Interface Address Group Source Counter\n"); + "Interface Group Source Counter\n"); /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; if (!pim_ifp) continue; - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - char ifaddr_str[INET_ADDRSTRLEN]; - struct listnode *grpnode; - struct igmp_group *grp; - - pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, - sizeof(ifaddr_str)); + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, + grp)) { + char group_str[INET_ADDRSTRLEN]; + struct listnode *srcnode; + struct igmp_source *src; - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - char group_str[INET_ADDRSTRLEN]; - struct listnode *srcnode; - struct igmp_source *src; + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); - pim_inet4_dump("<group?>", grp->group_addr, - group_str, sizeof(group_str)); + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, + srcnode, src)) { + char source_str[INET_ADDRSTRLEN]; - /* scan group sources */ - for (ALL_LIST_ELEMENTS_RO( - grp->group_source_list, srcnode, - src)) { - char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("<source?>", src->source_addr, + source_str, sizeof(source_str)); - pim_inet4_dump( - "<source?>", src->source_addr, - source_str, sizeof(source_str)); + vty_out(vty, "%-16s %-15s %-15s %7d\n", + ifp->name, group_str, source_str, + src->source_query_retransmit_count); - vty_out(vty, - "%-16s %-15s %-15s %-15s %7d\n", - ifp->name, ifaddr_str, - group_str, source_str, - src->source_query_retransmit_count); - - } /* scan group sources */ - } /* scan igmp groups */ - } /* scan igmp sockets */ - } /* scan interfaces */ + } /* scan group sources */ + } /* scan igmp groups */ + } /* scan interfaces */ } static void pim_show_bsr(struct pim_instance *pim, @@ -3993,8 +3922,7 @@ static void clear_mroute(struct pim_instance *pim) /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct igmp_group *grp; struct pim_ifchannel *ch; if (!pim_ifp) @@ -4008,20 +3936,12 @@ static void clear_mroute(struct pim_instance *pim) } /* clean up all igmp groups */ - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - - struct igmp_group *grp; - if (igmp->igmp_group_list) { - while (igmp->igmp_group_list->count) { - grp = listnode_head( - igmp->igmp_group_list); - igmp_group_delete(grp); - } + if (pim_ifp->igmp_group_list) { + while (pim_ifp->igmp_group_list->count) { + grp = listnode_head(pim_ifp->igmp_group_list); + igmp_group_delete(grp); } - } } diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 0b28a3e84c..eb19cf4ddf 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -156,14 +156,12 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options); pim_ifp->igmp_join_list = NULL; - pim_ifp->igmp_socket_list = NULL; pim_ifp->pim_neighbor_list = NULL; pim_ifp->upstream_switch_list = NULL; pim_ifp->pim_generation_id = 0; /* list of struct igmp_sock */ - pim_ifp->igmp_socket_list = list_new(); - pim_ifp->igmp_socket_list->del = (void (*)(void *))igmp_sock_free; + pim_igmp_if_init(pim_ifp, ifp); /* list of struct pim_neighbor */ pim_ifp->pim_neighbor_list = list_new(); @@ -214,7 +212,8 @@ void pim_if_delete(struct interface *ifp) pim_if_del_vif(ifp); pim_ifp->pim->mcast_if_count--; - list_delete(&pim_ifp->igmp_socket_list); + pim_igmp_if_fini(pim_ifp); + list_delete(&pim_ifp->pim_neighbor_list); list_delete(&pim_ifp->upstream_switch_list); list_delete(&pim_ifp->sec_addr_list); diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 92784103fe..55c278d6e2 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -30,6 +30,7 @@ #include "pim_igmp.h" #include "pim_upstream.h" +#include "pim_instance.h" #include "bfd.h" #define PIM_IF_MASK_PIM (1 << 0) @@ -102,6 +103,8 @@ struct pim_interface { int igmp_last_member_query_count; /* IGMP last member query count */ struct list *igmp_socket_list; /* list of struct igmp_sock */ struct list *igmp_join_list; /* list of struct igmp_join */ + struct list *igmp_group_list; /* list of struct igmp_group */ + struct hash *igmp_group_hash; int pim_sock_fd; /* PIM socket file descriptor */ struct thread *t_pim_sock_read; /* thread for reading PIM socket */ diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h index 7ec8191e56..52f02a660b 100644 --- a/pimd/pim_ifchannel.h +++ b/pimd/pim_ifchannel.h @@ -25,6 +25,8 @@ #include "if.h" #include "prefix.h" +#include "pim_assert.h" + struct pim_ifchannel; #include "pim_upstream.h" @@ -39,20 +41,6 @@ enum pim_ifjoin_state { PIM_IFJOIN_PRUNE_PENDING_TMP, }; -enum pim_ifassert_state { - PIM_IFASSERT_NOINFO, - PIM_IFASSERT_I_AM_WINNER, - PIM_IFASSERT_I_AM_LOSER -}; - -struct pim_assert_metric { - uint32_t rpt_bit_flag; - uint32_t metric_preference; - uint32_t route_metric; - struct in_addr ip_address; /* neighbor router that sourced the Assert - message */ -}; - /* Flag to detect change in CouldAssert(S,G,I) */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 71b2d9187a..50de7124d2 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -810,13 +810,8 @@ static void igmp_group_free(struct igmp_group *group) XFREE(MTYPE_PIM_IGMP_GROUP, group); } -static void igmp_group_count_incr(struct igmp_sock *igmp) +static void igmp_group_count_incr(struct pim_interface *pim_ifp) { - struct pim_interface *pim_ifp = igmp->interface->info; - - if (!pim_ifp) - return; - ++pim_ifp->pim->igmp_group_count; if (pim_ifp->pim->igmp_group_count == pim_ifp->pim->igmp_watermark_limit) { @@ -827,13 +822,8 @@ static void igmp_group_count_incr(struct igmp_sock *igmp) } } -static void igmp_group_count_decr(struct igmp_sock *igmp) +static void igmp_group_count_decr(struct pim_interface *pim_ifp) { - struct pim_interface *pim_ifp = igmp->interface->info; - - if (!pim_ifp) - return; - if (pim_ifp->pim->igmp_group_count == 0) { zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)", VRF_LOGNAME(pim_ifp->pim->vrf)); @@ -848,14 +838,14 @@ void igmp_group_delete(struct igmp_group *group) struct listnode *src_node; struct listnode *src_nextnode; struct igmp_source *src; + struct pim_interface *pim_ifp = group->interface->info; if (PIM_DEBUG_IGMP_TRACE) { char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); - zlog_debug("Deleting IGMP group %s from socket %d interface %s", - group_str, group->group_igmp_sock->fd, - group->group_igmp_sock->interface->name); + zlog_debug("Deleting IGMP group %s from interface %s", + group_str, group->interface->name); } for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, @@ -866,9 +856,9 @@ void igmp_group_delete(struct igmp_group *group) THREAD_OFF(group->t_group_query_retransmit_timer); group_timer_off(group); - igmp_group_count_decr(group->group_igmp_sock); - listnode_delete(group->group_igmp_sock->igmp_group_list, group); - hash_release(group->group_igmp_sock->igmp_group_hash, group); + igmp_group_count_decr(pim_ifp); + listnode_delete(pim_ifp->igmp_group_list, group); + hash_release(pim_ifp->igmp_group_hash, group); igmp_group_free(group); } @@ -886,11 +876,6 @@ void igmp_sock_free(struct igmp_sock *igmp) assert(!igmp->t_igmp_read); assert(!igmp->t_igmp_query_timer); assert(!igmp->t_other_querier_timer); - assert(igmp->igmp_group_list); - assert(!listcount(igmp->igmp_group_list)); - - list_delete(&igmp->igmp_group_list); - hash_free(igmp->igmp_group_hash); XFREE(MTYPE_PIM_IGMP_SOCKET, igmp); } @@ -898,14 +883,6 @@ void igmp_sock_free(struct igmp_sock *igmp) void igmp_sock_delete(struct igmp_sock *igmp) { struct pim_interface *pim_ifp; - struct listnode *grp_node; - struct listnode *grp_nextnode; - struct igmp_group *grp; - - for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, - grp)) { - igmp_group_delete(grp); - } sock_close(igmp); @@ -914,6 +891,9 @@ void igmp_sock_delete(struct igmp_sock *igmp) listnode_delete(pim_ifp->igmp_socket_list, igmp); igmp_sock_free(igmp); + + if (!listcount(pim_ifp->igmp_socket_list)) + pim_igmp_if_reset(pim_ifp); } void igmp_sock_delete_all(struct interface *ifp) @@ -948,12 +928,50 @@ static bool igmp_group_hash_equal(const void *arg1, const void *arg2) return false; } +void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp) +{ + char hash_name[64]; + + pim_ifp->igmp_socket_list = list_new(); + pim_ifp->igmp_socket_list->del = (void (*)(void *))igmp_sock_free; + + pim_ifp->igmp_group_list = list_new(); + pim_ifp->igmp_group_list->del = (void (*)(void *))igmp_group_free; + + snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name); + pim_ifp->igmp_group_hash = hash_create( + igmp_group_hash_key, igmp_group_hash_equal, hash_name); +} + +void pim_igmp_if_reset(struct pim_interface *pim_ifp) +{ + struct listnode *grp_node, *grp_nextnode; + struct igmp_group *grp; + + for (ALL_LIST_ELEMENTS(pim_ifp->igmp_group_list, grp_node, grp_nextnode, + grp)) { + igmp_group_delete(grp); + } +} + +void pim_igmp_if_fini(struct pim_interface *pim_ifp) +{ + pim_igmp_if_reset(pim_ifp); + + assert(pim_ifp->igmp_group_list); + assert(!listcount(pim_ifp->igmp_group_list)); + + list_delete(&pim_ifp->igmp_group_list); + hash_free(pim_ifp->igmp_group_hash); + + list_delete(&pim_ifp->igmp_socket_list); +} + static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr, struct interface *ifp, int mtrace_only) { struct pim_interface *pim_ifp; struct igmp_sock *igmp; - char hash_name[64]; pim_ifp = ifp->info; @@ -965,13 +983,6 @@ static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr, igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp)); - igmp->igmp_group_list = list_new(); - igmp->igmp_group_list->del = (void (*)(void *))igmp_group_free; - - snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name); - igmp->igmp_group_hash = hash_create(igmp_group_hash_key, - igmp_group_hash_equal, hash_name); - igmp->fd = fd; igmp->interface = ifp; igmp->ifaddr = ifaddr; @@ -1114,7 +1125,7 @@ static int igmp_group_timer(struct thread *t) pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); zlog_debug("%s: Timer for group %s on interface %s", __func__, - group_str, group->group_igmp_sock->interface->name); + group_str, group->interface->name); } assert(group->group_filtermode_isexcl); @@ -1151,7 +1162,7 @@ static void group_timer_off(struct igmp_group *group) pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); zlog_debug("Cancelling TIMER event for group %s on %s", - group_str, group->group_igmp_sock->interface->name); + group_str, group->interface->name); } THREAD_OFF(group->t_group_timer); } @@ -1188,16 +1199,18 @@ struct igmp_group *find_group_by_addr(struct igmp_sock *igmp, struct in_addr group_addr) { struct igmp_group lookup; + struct pim_interface *pim_ifp = igmp->interface->info; lookup.group_addr.s_addr = group_addr.s_addr; - return hash_lookup(igmp->igmp_group_hash, &lookup); + return hash_lookup(pim_ifp->igmp_group_hash, &lookup); } struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, struct in_addr group_addr) { struct igmp_group *group; + struct pim_interface *pim_ifp = igmp->interface->info; group = find_group_by_addr(igmp, group_addr); if (group) { @@ -1239,7 +1252,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, group->t_group_query_retransmit_timer = NULL; group->group_specific_query_retransmit_count = 0; group->group_addr = group_addr; - group->group_igmp_sock = igmp; + group->interface = igmp->interface; group->last_igmp_v1_report_dsec = -1; group->last_igmp_v2_report_dsec = -1; group->group_creation = pim_time_monotonic_sec(); @@ -1248,8 +1261,8 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, /* initialize new group as INCLUDE {empty} */ group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */ - listnode_add(igmp->igmp_group_list, group); - group = hash_get(igmp->igmp_group_hash, group, hash_alloc_intern); + listnode_add(pim_ifp->igmp_group_list, group); + group = hash_get(pim_ifp->igmp_group_hash, group, hash_alloc_intern); if (PIM_DEBUG_IGMP_TRACE) { char group_str[INET_ADDRSTRLEN]; @@ -1260,7 +1273,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, group_str, igmp->fd, igmp->interface->name); } - igmp_group_count_incr(igmp); + igmp_group_count_incr(pim_ifp); /* RFC 3376: 6.2.2. Definition of Group Timers diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index abb8af836b..dfe986e8f5 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -99,12 +99,15 @@ struct igmp_sock { bool mtrace_only; - struct list *igmp_group_list; /* list of struct igmp_group */ - struct hash *igmp_group_hash; - struct igmp_stats rx_stats; }; +struct pim_interface; + +void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp); +void pim_igmp_if_reset(struct pim_interface *pim_ifp); +void pim_igmp_if_fini(struct pim_interface *pim_ifp); + struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list, struct in_addr ifaddr); struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, int fd); @@ -178,7 +181,7 @@ struct igmp_group { int group_filtermode_isexcl; /* 0=INCLUDE, 1=EXCLUDE */ struct list *group_source_list; /* list of struct igmp_source */ time_t group_creation; - struct igmp_sock *group_igmp_sock; /* back pointer */ + struct interface *interface; int64_t last_igmp_v1_report_dsec; int64_t last_igmp_v2_report_dsec; }; @@ -188,6 +191,10 @@ struct igmp_group *find_group_by_addr(struct igmp_sock *igmp, struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, struct in_addr group_addr); +struct igmp_source *igmp_get_source_by_addr(struct igmp_group *group, + struct in_addr src_addr, + bool *created); + void igmp_group_delete_empty_include(struct igmp_group *group); void igmp_startup_mode_on(struct igmp_sock *igmp); @@ -195,9 +202,6 @@ void igmp_startup_mode_on(struct igmp_sock *igmp); void igmp_group_timer_on(struct igmp_group *group, long interval_msec, const char *ifname); -struct igmp_source *source_new(struct igmp_group *group, - struct in_addr src_addr); - void igmp_send_query(int igmp_version, struct igmp_group *group, int fd, const char *ifname, char *query_buf, int query_buf_size, int num_sources, struct in_addr dst_addr, diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index bc67a1dd1d..13db11fa80 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -57,16 +57,28 @@ static void on_trace(const char *label, struct interface *ifp, } } +static inline long igmp_gmi_msec(struct igmp_group *group) +{ + struct pim_interface *pim_ifp = group->interface->info; + struct igmp_sock *igmp; + struct listnode *sock_node; + + long qrv = 0, qqi = 0; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + qrv = MAX(qrv, igmp->querier_robustness_variable); + qqi = MAX(qqi, igmp->querier_query_interval); + } + return PIM_IGMP_GMI_MSEC(qrv, qqi, + pim_ifp->igmp_query_max_response_time_dsec); +} + void igmp_group_reset_gmi(struct igmp_group *group) { long group_membership_interval_msec; - struct pim_interface *pim_ifp; - struct igmp_sock *igmp; struct interface *ifp; - igmp = group->group_igmp_sock; - ifp = igmp->interface; - pim_ifp = ifp->info; + ifp = group->interface; /* RFC 3376: 8.4. Group Membership Interval @@ -82,9 +94,7 @@ void igmp_group_reset_gmi(struct igmp_group *group) (1000 * querier_query_interval) + 100 * query_response_interval_dsec; */ - group_membership_interval_msec = PIM_IGMP_GMI_MSEC( - igmp->querier_robustness_variable, igmp->querier_query_interval, - pim_ifp->igmp_query_max_response_time_dsec); + group_membership_interval_msec = igmp_gmi_msec(group); if (PIM_DEBUG_IGMP_TRACE) { char group_str[INET_ADDRSTRLEN]; @@ -127,7 +137,7 @@ static int igmp_source_timer(struct thread *t) zlog_debug( "%s: Source timer expired for group %s source %s on %s", __func__, group_str, source_str, - group->group_igmp_sock->interface->name); + group->interface->name); } /* @@ -188,8 +198,7 @@ static void source_timer_off(struct igmp_group *group, sizeof(source_str)); zlog_debug( "Cancelling TIMER event for group %s source %s on %s", - group_str, source_str, - group->group_igmp_sock->interface->name); + group_str, source_str, group->interface->name); } THREAD_OFF(source->t_source_timer); @@ -199,7 +208,7 @@ static void igmp_source_timer_on(struct igmp_group *group, struct igmp_source *source, long interval_msec) { source_timer_off(group, source); - struct pim_interface *pim_ifp = group->group_igmp_sock->interface->info; + struct pim_interface *pim_ifp = group->interface->info; if (PIM_DEBUG_IGMP_EVENTS) { char group_str[INET_ADDRSTRLEN]; @@ -211,7 +220,7 @@ static void igmp_source_timer_on(struct igmp_group *group, zlog_debug( "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s", interval_msec / 1000, interval_msec % 1000, group_str, - source_str, group->group_igmp_sock->interface->name); + source_str, group->interface->name); } thread_add_timer_msec(router->master, igmp_source_timer, source, @@ -225,19 +234,14 @@ static void igmp_source_timer_on(struct igmp_group *group, igmp_source_forward_start(pim_ifp->pim, source); } -void igmp_source_reset_gmi(struct igmp_sock *igmp, struct igmp_group *group, - struct igmp_source *source) +void igmp_source_reset_gmi(struct igmp_group *group, struct igmp_source *source) { long group_membership_interval_msec; - struct pim_interface *pim_ifp; struct interface *ifp; - ifp = igmp->interface; - pim_ifp = ifp->info; + ifp = group->interface; - group_membership_interval_msec = PIM_IGMP_GMI_MSEC( - igmp->querier_robustness_variable, igmp->querier_query_interval, - pim_ifp->igmp_query_max_response_time_dsec); + group_membership_interval_msec = igmp_gmi_msec(group); if (PIM_DEBUG_IGMP_TRACE) { char group_str[INET_ADDRSTRLEN]; @@ -312,7 +316,7 @@ static void source_clear_send_flag(struct list *source_list) */ static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group) { - struct pim_interface *pim_ifp = group->group_igmp_sock->interface->info; + struct pim_interface *pim_ifp = group->interface->info; assert(group->group_filtermode_isexcl); @@ -356,9 +360,8 @@ void igmp_source_delete(struct igmp_source *source) pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str)); zlog_debug( - "Deleting IGMP source %s for group %s from socket %d interface %s c_oil ref_count %d", - source_str, group_str, group->group_igmp_sock->fd, - group->group_igmp_sock->interface->name, + "Deleting IGMP source %s for group %s from interface %s c_oil ref_count %d", + source_str, group_str, group->interface->name, source->source_channel_oil ? source->source_channel_oil->oil_ref_count : 0); @@ -376,10 +379,9 @@ void igmp_source_delete(struct igmp_source *source) pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str)); zlog_warn( - "%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s", + "%s: forwarding=ON(!) IGMP source %s for group %s from interface %s", __func__, source_str, group_str, - group->group_igmp_sock->fd, - group->group_igmp_sock->interface->name); + group->interface->name); /* warning only */ } @@ -439,11 +441,18 @@ struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group, return 0; } -struct igmp_source *source_new(struct igmp_group *group, - struct in_addr src_addr) +struct igmp_source *igmp_get_source_by_addr(struct igmp_group *group, + struct in_addr src_addr, bool *new) { struct igmp_source *src; + if (new) + *new = false; + + src = igmp_find_source_by_addr(group, src_addr); + if (src) + return src; + if (PIM_DEBUG_IGMP_TRACE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -452,9 +461,8 @@ struct igmp_source *source_new(struct igmp_group *group, pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str)); zlog_debug( - "Creating new IGMP source %s for group %s on socket %d interface %s", - source_str, group_str, group->group_igmp_sock->fd, - group->group_igmp_sock->interface->name); + "Creating new IGMP source %s for group %s on interface %s", + source_str, group_str, group->interface->name); } src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src)); @@ -471,23 +479,6 @@ struct igmp_source *source_new(struct igmp_group *group, /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ igmp_anysource_forward_stop(group); - - return src; -} - -static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp, - struct igmp_group *group, - struct in_addr src_addr) -{ - struct igmp_source *src; - - src = igmp_find_source_by_addr(group, src_addr); - if (src) { - return src; - } - - src = source_new(group, src_addr); - return src; } @@ -518,8 +509,7 @@ static void allow(struct igmp_sock *igmp, struct in_addr from, source = igmp_find_source_by_addr(group, star); if (source) - igmp_source_reset_gmi(igmp, group, - source); + igmp_source_reset_gmi(group, source); } } else { igmp_group_delete(group); @@ -540,10 +530,9 @@ static void allow(struct igmp_sock *igmp, struct in_addr from, src_addr = sources + i; - source = add_source_by_addr(igmp, group, *src_addr); - if (!source) { + source = igmp_get_source_by_addr(group, *src_addr, NULL); + if (!source) continue; - } /* RFC 3376: 6.4.1. Reception of Current-State Records @@ -555,7 +544,7 @@ static void allow(struct igmp_sock *igmp, struct in_addr from, igmp_source_reset_gmi() below, resetting the source timers to GMI, accomplishes this. */ - igmp_source_reset_gmi(igmp, group, source); + igmp_source_reset_gmi(group, source); } /* scan received sources */ } @@ -585,21 +574,23 @@ static void isex_excl(struct igmp_group *group, int num_sources, /* scan received sources (A) */ for (i = 0; i < num_sources; ++i) { struct in_addr *src_addr; + bool new; src_addr = sources + i; /* E.2: lookup reported source from (A) in (X,Y) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (!new) { /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */ IGMP_SOURCE_DONT_DELETE(source->source_flags); } else { /* E.4: if not found, create source with timer=GMI: * (A-X-Y) */ - source = source_new(group, *src_addr); assert(!source->t_source_timer); /* timer == 0 */ - igmp_source_reset_gmi(group->group_igmp_sock, group, - source); + igmp_source_reset_gmi(group, source); assert(source->t_source_timer); /* (A-X-Y) timer > 0 */ } @@ -615,8 +606,7 @@ static void isex_excl(struct igmp_group *group, int num_sources, source = igmp_find_source_by_addr(group, star); if (source) { IGMP_SOURCE_DONT_DELETE(source->source_flags); - igmp_source_reset_gmi(group->group_igmp_sock, group, - source); + igmp_source_reset_gmi(group, source); } } @@ -639,18 +629,21 @@ static void isex_incl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* I.2: lookup reported source (B) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (!new) { /* I.3: if found, clear deletion flag (A*B) */ IGMP_SOURCE_DONT_DELETE(source->source_flags); } else { /* I.4: if not found, create source with timer=0 (B-A) */ - source = source_new(group, *src_addr); assert(!source->t_source_timer); /* (B-A) timer=0 */ } @@ -706,7 +699,6 @@ void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, static void toin_incl(struct igmp_group *group, int num_sources, struct in_addr *sources) { - struct igmp_sock *igmp = group->group_igmp_sock; int num_sources_tosend = listcount(group->group_source_list); int i; @@ -717,22 +709,23 @@ static void toin_incl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* Lookup reported source (B) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (!new) { /* If found, clear SEND flag (A*B) */ IGMP_SOURCE_DONT_SEND(source->source_flags); --num_sources_tosend; - } else { - /* If not found, create new source */ - source = source_new(group, *src_addr); } /* (B)=GMI */ - igmp_source_reset_gmi(igmp, group, source); + igmp_source_reset_gmi(group, source); } /* Send sources marked with SEND flag: Q(G,A-B) */ @@ -744,7 +737,6 @@ static void toin_incl(struct igmp_group *group, int num_sources, static void toin_excl(struct igmp_group *group, int num_sources, struct in_addr *sources) { - struct igmp_sock *igmp = group->group_igmp_sock; int num_sources_tosend; int i; @@ -755,25 +747,24 @@ static void toin_excl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* Lookup reported source (A) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { - if (source->t_source_timer) { - /* If found and timer running, clear SEND flag - * (X*A) */ - IGMP_SOURCE_DONT_SEND(source->source_flags); - --num_sources_tosend; - } - } else { - /* If not found, create new source */ - source = source_new(group, *src_addr); + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (source->t_source_timer) { + /* If found and timer running, clear SEND flag + * (X*A) */ + IGMP_SOURCE_DONT_SEND(source->source_flags); + --num_sources_tosend; } /* (A)=GMI */ - igmp_source_reset_gmi(igmp, group, source); + igmp_source_reset_gmi(group, source); } /* Send sources marked with SEND flag: Q(G,X-A) */ @@ -839,22 +830,18 @@ static void toex_incl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* Lookup reported source (B) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!new) { /* If found, clear deletion flag: (A*B) */ IGMP_SOURCE_DONT_DELETE(source->source_flags); /* and set SEND flag (A*B) */ IGMP_SOURCE_DO_SEND(source->source_flags); ++num_sources_tosend; - } else { - /* If source not found, create source with timer=0: - * (B-A)=0 */ - source = source_new(group, *src_addr); - assert(!source->t_source_timer); /* (B-A) timer=0 */ } } /* Scan received sources (B) */ @@ -899,12 +886,16 @@ static void toex_excl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* lookup reported source (A) in known sources (X,Y) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (!new) { /* if found, clear off DELETE flag from reported source * (A) */ IGMP_SOURCE_DONT_DELETE(source->source_flags); @@ -912,7 +903,6 @@ static void toex_excl(struct igmp_group *group, int num_sources, /* if not found, create source with Group Timer: * (A-X-Y)=Group Timer */ long group_timer_msec; - source = source_new(group, *src_addr); assert(!source->t_source_timer); /* timer == 0 */ group_timer_msec = igmp_group_timer_remain_msec(group); @@ -986,6 +976,26 @@ void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from, allow(igmp, from, group_addr, num_sources, sources); } +static void igmp_send_query_group(struct igmp_group *group, char *query_buf, + size_t query_buf_size, int num_sources, + int s_flag) +{ + struct interface *ifp = group->interface; + struct pim_interface *pim_ifp = ifp->info; + struct igmp_sock *igmp; + struct listnode *sock_node; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + igmp_send_query( + pim_ifp->igmp_version, group, igmp->fd, ifp->name, + query_buf, query_buf_size, num_sources, + group->group_addr, group->group_addr, + pim_ifp->igmp_specific_query_max_response_time_dsec, + s_flag, igmp->querier_robustness_variable, + igmp->querier_query_interval); + } +} + /* RFC3376: 6.6.3.1. Building and Sending Group Specific Queries @@ -995,7 +1005,6 @@ void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from, */ static void group_retransmit_group(struct igmp_group *group) { - struct igmp_sock *igmp; struct pim_interface *pim_ifp; long lmqc; /* Last Member Query Count */ long lmqi_msec; /* Last Member Query Interval */ @@ -1003,8 +1012,7 @@ static void group_retransmit_group(struct igmp_group *group) int s_flag; int query_buf_size; - igmp = group->group_igmp_sock; - pim_ifp = igmp->interface->info; + pim_ifp = group->interface->info; if (pim_ifp->igmp_version == 3) { query_buf_size = PIM_IGMP_BUFSIZE_WRITE; @@ -1033,7 +1041,7 @@ static void group_retransmit_group(struct igmp_group *group) sizeof(group_str)); zlog_debug( "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d", - group_str, igmp->interface->name, s_flag, + group_str, group->interface->name, s_flag, group->group_specific_query_retransmit_count); } @@ -1045,14 +1053,7 @@ static void group_retransmit_group(struct igmp_group *group) interest. */ - igmp_send_query(pim_ifp->igmp_version, group, igmp->fd, - igmp->interface->name, query_buf, sizeof(query_buf), - 0 /* num_sources_tosend */, - group->group_addr /* dst_addr */, - group->group_addr /* group_addr */, - pim_ifp->igmp_specific_query_max_response_time_dsec, - s_flag, igmp->querier_robustness_variable, - igmp->querier_query_interval); + igmp_send_query_group(group, query_buf, sizeof(query_buf), 0, s_flag); } /* @@ -1070,7 +1071,6 @@ static void group_retransmit_group(struct igmp_group *group) static int group_retransmit_sources(struct igmp_group *group, int send_with_sflag_set) { - struct igmp_sock *igmp; struct pim_interface *pim_ifp; long lmqc; /* Last Member Query Count */ long lmqi_msec; /* Last Member Query Interval */ @@ -1090,8 +1090,7 @@ static int group_retransmit_sources(struct igmp_group *group, source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); - igmp = group->group_igmp_sock; - pim_ifp = igmp->interface->info; + pim_ifp = group->interface->info; lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; @@ -1131,7 +1130,7 @@ static int group_retransmit_sources(struct igmp_group *group, sizeof(group_str)); zlog_debug( "retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d", - group_str, igmp->interface->name, num_sources_tosend1, + group_str, group->interface->name, num_sources_tosend1, num_sources_tosend2, send_with_sflag_set, num_retransmit_sources_left); } @@ -1154,7 +1153,7 @@ static int group_retransmit_sources(struct igmp_group *group, zlog_warn( "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)", __func__, group_str, - igmp->interface->name, + group->interface->name, num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources); } else { @@ -1169,15 +1168,9 @@ static int group_retransmit_sources(struct igmp_group *group, interest. */ - igmp_send_query( - pim_ifp->igmp_version, group, igmp->fd, - igmp->interface->name, query_buf1, - sizeof(query_buf1), num_sources_tosend1, - group->group_addr, group->group_addr, - pim_ifp->igmp_specific_query_max_response_time_dsec, - 1 /* s_flag */, - igmp->querier_robustness_variable, - igmp->querier_query_interval); + igmp_send_query_group( + group, query_buf1, sizeof(query_buf1), + num_sources_tosend1, 1 /* s_flag */); } } /* send_with_sflag_set */ @@ -1197,7 +1190,7 @@ static int group_retransmit_sources(struct igmp_group *group, sizeof(group_str)); zlog_warn( "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)", - __func__, group_str, igmp->interface->name, + __func__, group_str, group->interface->name, num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources); } else { @@ -1211,15 +1204,9 @@ static int group_retransmit_sources(struct igmp_group *group, interest. */ - igmp_send_query( - pim_ifp->igmp_version, group, igmp->fd, - igmp->interface->name, query_buf2, - sizeof(query_buf2), num_sources_tosend2, - group->group_addr, group->group_addr, - pim_ifp->igmp_specific_query_max_response_time_dsec, - 0 /* s_flag */, - igmp->querier_robustness_variable, - igmp->querier_query_interval); + igmp_send_query_group( + group, query_buf2, sizeof(query_buf2), + num_sources_tosend2, 0 /* s_flag */); } } @@ -1239,7 +1226,7 @@ static int igmp_group_retransmit(struct thread *t) pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); zlog_debug("group_retransmit_timer: group %s on %s", group_str, - group->group_igmp_sock->interface->name); + group->interface->name); } /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */ @@ -1287,7 +1274,6 @@ static int igmp_group_retransmit(struct thread *t) */ static void group_retransmit_timer_on(struct igmp_group *group) { - struct igmp_sock *igmp; struct pim_interface *pim_ifp; long lmqi_msec; /* Last Member Query Interval */ @@ -1296,8 +1282,7 @@ static void group_retransmit_timer_on(struct igmp_group *group) return; } - igmp = group->group_igmp_sock; - pim_ifp = igmp->interface->info; + pim_ifp = group->interface->info; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; @@ -1308,7 +1293,7 @@ static void group_retransmit_timer_on(struct igmp_group *group) zlog_debug( "Scheduling %ld.%03ld sec retransmit timer for group %s on %s", lmqi_msec / 1000, lmqi_msec % 1000, group_str, - igmp->interface->name); + group->interface->name); } thread_add_timer_msec(router->master, igmp_group_retransmit, group, @@ -1332,11 +1317,9 @@ static long igmp_source_timer_remain_msec(struct igmp_source *source) static void group_query_send(struct igmp_group *group) { struct pim_interface *pim_ifp; - struct igmp_sock *igmp; long lmqc; /* Last Member Query Count */ - igmp = group->group_igmp_sock; - pim_ifp = igmp->interface->info; + pim_ifp = group->interface->info; lmqc = pim_ifp->igmp_last_member_query_count; /* lower group timer to lmqt */ @@ -1359,7 +1342,6 @@ static void group_query_send(struct igmp_group *group) static void source_query_send_by_flag(struct igmp_group *group, int num_sources_tosend) { - struct igmp_sock *igmp; struct pim_interface *pim_ifp; struct listnode *src_node; struct igmp_source *src; @@ -1369,8 +1351,7 @@ static void source_query_send_by_flag(struct igmp_group *group, assert(num_sources_tosend > 0); - igmp = group->group_igmp_sock; - pim_ifp = igmp->interface->info; + pim_ifp = group->interface->info; lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; @@ -1417,16 +1398,19 @@ static void block_excl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* lookup reported source (A) in known sources (X,Y) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (!source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (new) { /* 3: if not found, create source with Group Timer: * (A-X-Y)=Group Timer */ long group_timer_msec; - source = source_new(group, *src_addr); assert(!source->t_source_timer); /* timer == 0 */ group_timer_msec = igmp_group_timer_remain_msec(group); @@ -1504,7 +1488,6 @@ void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from, void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) { - struct igmp_sock *igmp; struct interface *ifp; struct pim_interface *pim_ifp; char *ifname; @@ -1523,8 +1506,7 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) return; } - igmp = group->group_igmp_sock; - ifp = igmp->interface; + ifp = group->interface; pim_ifp = ifp->info; ifname = ifp->name; @@ -1551,7 +1533,6 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) { struct igmp_group *group; - struct igmp_sock *igmp; struct interface *ifp; struct pim_interface *pim_ifp; char *ifname; @@ -1560,8 +1541,7 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) int lmqt_msec; /* Last Member Query Time */ group = source->source_group; - igmp = group->group_igmp_sock; - ifp = igmp->interface; + ifp = group->interface; pim_ifp = ifp->info; ifname = ifp->name; diff --git a/pimd/pim_igmpv3.h b/pimd/pim_igmpv3.h index 6abaef6e26..273f944b3c 100644 --- a/pimd/pim_igmpv3.h +++ b/pimd/pim_igmpv3.h @@ -23,6 +23,8 @@ #include <zebra.h> #include "if.h" +#include "pim_igmp.h" + #define IGMP_V3_CHECKSUM_OFFSET (2) #define IGMP_V3_REPORT_NUMGROUPS_OFFSET (6) #define IGMP_V3_REPORT_GROUPPRECORD_OFFSET (8) @@ -52,7 +54,7 @@ #define PIM_IGMP_OHPI_DSEC(qrv,qqi,qri_dsec) ((qrv) * (10 * (qqi)) + (qri_dsec)) void igmp_group_reset_gmi(struct igmp_group *group); -void igmp_source_reset_gmi(struct igmp_sock *igmp, struct igmp_group *group, +void igmp_source_reset_gmi(struct igmp_group *group, struct igmp_source *source); void igmp_source_free(struct igmp_source *source); diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index 52ded08ae3..68c5b9167b 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -210,6 +210,8 @@ struct pim_instance { void pim_vrf_init(void); void pim_vrf_terminate(void); +extern struct pim_router *router; + struct pim_instance *pim_get_pim_instance(vrf_id_t vrf_id); #endif diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h index 2d8e1b01fb..4cd6b9f0ac 100644 --- a/pimd/pim_mroute.h +++ b/pimd/pim_mroute.h @@ -167,6 +167,8 @@ struct igmpmsg { Above: from <linux/mroute.h> */ +struct channel_oil; + int pim_mroute_socket_enable(struct pim_instance *pim); int pim_mroute_socket_disable(struct pim_instance *pim); diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index 4b4c1ec7db..f4627cbcc2 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -23,6 +23,7 @@ #include "pim_nb.h" #include "lib/northbound_cli.h" #include "pim_igmpv3.h" +#include "pim_neighbor.h" #include "pim_pim.h" #include "pim_mlag.h" #include "pim_bfd.h" @@ -60,8 +61,9 @@ static void pim_if_membership_clear(struct interface *ifp) static void pim_if_membership_refresh(struct interface *ifp) { struct pim_interface *pim_ifp; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; + pim_ifp = ifp->info; assert(pim_ifp); @@ -83,36 +85,27 @@ static void pim_if_membership_refresh(struct interface *ifp) * the interface */ - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { - struct listnode *grpnode; - struct igmp_group *grp; - - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, - grp)) { - struct listnode *srcnode; - struct igmp_source *src; - - /* scan group sources */ - for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, - srcnode, src)) { - - if (IGMP_SOURCE_TEST_FORWARDING( - src->source_flags)) { - struct prefix_sg sg; - - memset(&sg, 0, - sizeof(struct prefix_sg)); - sg.src = src->source_addr; - sg.grp = grp->group_addr; - pim_ifchannel_local_membership_add( - ifp, &sg, false /*is_vxlan*/); - } - - } /* scan group sources */ - } /* scan igmp groups */ - } /* scan igmp sockets */ + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, grp)) { + struct listnode *srcnode; + struct igmp_source *src; + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, + src)) { + + if (IGMP_SOURCE_TEST_FORWARDING(src->source_flags)) { + struct prefix_sg sg; + + memset(&sg, 0, sizeof(struct prefix_sg)); + sg.src = src->source_addr; + sg.grp = grp->group_addr; + pim_ifchannel_local_membership_add( + ifp, &sg, false /*is_vxlan*/); + } + + } /* scan group sources */ + } /* scan igmp groups */ /* * Finally delete every PIM (S,G) entry lacking all state info @@ -458,6 +451,8 @@ static void change_query_max_response_time(struct pim_interface *pim_ifp, { struct listnode *sock_node; struct igmp_sock *igmp; + struct listnode *grp_node; + struct igmp_group *grp; if (pim_ifp->igmp_query_max_response_time_dsec == query_max_response_time_dsec) @@ -474,32 +469,28 @@ static void change_query_max_response_time(struct pim_interface *pim_ifp, /* scan all sockets */ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { - struct listnode *grp_node; - struct igmp_group *grp; - /* reschedule socket general query */ igmp_sock_query_reschedule(igmp); + } - /* scan socket groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grp_node, - grp)) { - struct listnode *src_node; - struct igmp_source *src; - - /* reset group timers for groups in EXCLUDE mode */ - if (grp->group_filtermode_isexcl) - igmp_group_reset_gmi(grp); - - /* scan group sources */ - for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, - src_node, src)) { - - /* reset source timers for sources with running - * timers - */ - if (src->t_source_timer) - igmp_source_reset_gmi(igmp, grp, src); - } + /* scan socket groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grp_node, grp)) { + struct listnode *src_node; + struct igmp_source *src; + + /* reset group timers for groups in EXCLUDE mode */ + if (grp->group_filtermode_isexcl) + igmp_group_reset_gmi(grp); + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, src_node, + src)) { + + /* reset source timers for sources with running + * timers + */ + if (src->t_source_timer) + igmp_source_reset_gmi(grp, src); } } } diff --git a/pimd/pim_neighbor.h b/pimd/pim_neighbor.h index b461098a60..d71b2b87c3 100644 --- a/pimd/pim_neighbor.h +++ b/pimd/pim_neighbor.h @@ -27,6 +27,7 @@ #include "prefix.h" #include "pim_tlv.h" +#include "pim_iface.h" struct pim_neighbor { int64_t creation; /* timestamp of creation */ diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index b0aa2b17c5..af8ac84594 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -20,8 +20,9 @@ #ifndef PIM_OIL_H #define PIM_OIL_H +struct pim_interface; + #include "pim_mroute.h" -#include "pim_iface.h" /* * Where did we get this (S,G) from? diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 3e3b6dddb5..f2a969e04a 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -42,7 +42,7 @@ #include "pim_rpf.h" #include "pim_sock.h" #include "pim_memory.h" -#include "pim_iface.h" +#include "pim_neighbor.h" #include "pim_msdp.h" #include "pim_nht.h" #include "pim_mroute.h" diff --git a/pimd/pim_rp.h b/pimd/pim_rp.h index dd7cd5d75e..595025e5c9 100644 --- a/pimd/pim_rp.h +++ b/pimd/pim_rp.h @@ -24,9 +24,10 @@ #include "prefix.h" #include "vty.h" #include "plist.h" -#include "pim_iface.h" #include "pim_rpf.h" +struct pim_interface; + enum rp_source { RP_SRC_NONE = 0, RP_SRC_STATIC, diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index 66c6df65ad..b93f85e48c 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -31,6 +31,7 @@ #include "pim_pim.h" #include "pim_str.h" #include "pim_iface.h" +#include "pim_neighbor.h" #include "pim_zlookup.h" #include "pim_ifchannel.h" #include "pim_time.h" diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h index f006519b71..006aa1b636 100644 --- a/pimd/pim_rpf.h +++ b/pimd/pim_rpf.h @@ -22,9 +22,6 @@ #include <zebra.h> -#include "pim_upstream.h" -#include "pim_neighbor.h" - /* RFC 4601: diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index 56039d5605..ea3b564f8a 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -24,7 +24,7 @@ #include <prefix.h> #include "plist.h" -#include <pimd/pim_rpf.h> +#include "pim_rpf.h" #include "pim_str.h" #include "pim_ifchannel.h" diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 6f933e9e72..aa041df857 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -474,7 +474,7 @@ void igmp_anysource_forward_start(struct pim_instance *pim, assert(group->group_filtermode_isexcl); assert(listcount(group->group_source_list) < 1); - source = source_new(group, src_addr); + source = igmp_get_source_by_addr(group, src_addr, NULL); if (!source) { zlog_warn("%s: Failure to create * source", __func__); return; @@ -508,7 +508,7 @@ static void igmp_source_forward_reevaluate_one(struct pim_instance *pim, sg.src = source->source_addr; sg.grp = group->group_addr; - ch = pim_ifchannel_find(group->group_igmp_sock->interface, &sg); + ch = pim_ifchannel_find(group->interface, &sg); if (pim_is_grp_ssm(pim, group->group_addr)) { /* If SSM group withdraw local membership */ if (ch @@ -517,8 +517,8 @@ static void igmp_source_forward_reevaluate_one(struct pim_instance *pim, zlog_debug( "local membership del for %s as G is now SSM", pim_str_sg_dump(&sg)); - pim_ifchannel_local_membership_del( - group->group_igmp_sock->interface, &sg); + pim_ifchannel_local_membership_del(group->interface, + &sg); } } else { /* If ASM group add local membership */ @@ -529,8 +529,7 @@ static void igmp_source_forward_reevaluate_one(struct pim_instance *pim, "local membership add for %s as G is now ASM", pim_str_sg_dump(&sg)); pim_ifchannel_local_membership_add( - group->group_igmp_sock->interface, &sg, - false /*is_vxlan*/); + group->interface, &sg, false /*is_vxlan*/); } } } @@ -541,33 +540,24 @@ void igmp_source_forward_reevaluate_all(struct pim_instance *pim) FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; if (!pim_ifp) continue; - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - struct listnode *grpnode; - struct igmp_group *grp; - - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - struct listnode *srcnode; - struct igmp_source *src; - - /* scan group sources */ - for (ALL_LIST_ELEMENTS_RO( - grp->group_source_list, srcnode, - src)) { - igmp_source_forward_reevaluate_one(pim, - src); - } /* scan group sources */ - } /* scan igmp groups */ - } /* scan igmp sockets */ + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, + grp)) { + struct listnode *srcnode; + struct igmp_source *src; + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, + srcnode, src)) { + igmp_source_forward_reevaluate_one(pim, src); + } /* scan group sources */ + } /* scan igmp groups */ } /* scan interfaces */ } @@ -585,12 +575,10 @@ void igmp_source_forward_start(struct pim_instance *pim, sg.grp = source->source_group->group_addr; if (PIM_DEBUG_IGMP_TRACE) { - zlog_debug( - "%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", __func__, - pim_str_sg_dump(&sg), - source->source_group->group_igmp_sock->fd, - source->source_group->group_igmp_sock->interface->name, - IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + zlog_debug("%s: (S,G)=%s oif=%s fwd=%d", __func__, + pim_str_sg_dump(&sg), + source->source_group->interface->name, + IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); } /* Prevent IGMP interface from installing multicast route multiple @@ -600,13 +588,12 @@ void igmp_source_forward_start(struct pim_instance *pim, } group = source->source_group; - pim_oif = group->group_igmp_sock->interface->info; + pim_oif = group->interface->info; if (!pim_oif) { if (PIM_DEBUG_IGMP_TRACE) { zlog_debug("%s: multicast not enabled on oif=%s ?", __func__, - source->source_group->group_igmp_sock - ->interface->name); + source->source_group->interface->name); } return; } @@ -688,14 +675,10 @@ void igmp_source_forward_start(struct pim_instance *pim, */ if (PIM_DEBUG_IGMP_TRACE) { zlog_debug( - "%s: ignoring request for looped MFC entry (S,G)=%s: igmp_sock=%d oif=%s vif_index=%d", + "%s: ignoring request for looped MFC entry (S,G)=%s: oif=%s vif_index=%d", __func__, pim_str_sg_dump(&sg), source->source_group - ->group_igmp_sock - ->fd, - source->source_group - ->group_igmp_sock ->interface->name, input_iface_vif_index); } @@ -719,7 +702,7 @@ void igmp_source_forward_start(struct pim_instance *pim, if (PIM_I_am_DR(pim_oif) || PIM_I_am_DualActive(pim_oif)) { result = pim_channel_add_oif(source->source_channel_oil, - group->group_igmp_sock->interface, + group->interface, PIM_OIF_FLAG_PROTO_IGMP, __func__); if (result) { if (PIM_DEBUG_MROUTE) { @@ -733,7 +716,7 @@ void igmp_source_forward_start(struct pim_instance *pim, zlog_debug( "%s: %s was received on %s interface but we are not DR for that interface", __func__, pim_str_sg_dump(&sg), - group->group_igmp_sock->interface->name); + group->interface->name); return; } @@ -741,16 +724,15 @@ void igmp_source_forward_start(struct pim_instance *pim, Feed IGMPv3-gathered local membership information into PIM per-interface (S,G) state. */ - if (!pim_ifchannel_local_membership_add( - group->group_igmp_sock->interface, &sg, + if (!pim_ifchannel_local_membership_add(group->interface, &sg, false /*is_vxlan*/)) { if (PIM_DEBUG_MROUTE) zlog_warn("%s: Failure to add local membership for %s", __func__, pim_str_sg_dump(&sg)); pim_channel_del_oif(source->source_channel_oil, - group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP, __func__); + group->interface, PIM_OIF_FLAG_PROTO_IGMP, + __func__); return; } @@ -772,12 +754,10 @@ void igmp_source_forward_stop(struct igmp_source *source) sg.grp = source->source_group->group_addr; if (PIM_DEBUG_IGMP_TRACE) { - zlog_debug( - "%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", __func__, - pim_str_sg_dump(&sg), - source->source_group->group_igmp_sock->fd, - source->source_group->group_igmp_sock->interface->name, - IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + zlog_debug("%s: (S,G)=%s oif=%s fwd=%d", __func__, + pim_str_sg_dump(&sg), + source->source_group->interface->name, + IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); } /* Prevent IGMP interface from removing multicast route multiple @@ -800,9 +780,8 @@ void igmp_source_forward_stop(struct igmp_source *source) pim_forward_stop below. */ result = pim_channel_del_oif(source->source_channel_oil, - group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP, - __func__); + group->interface, PIM_OIF_FLAG_PROTO_IGMP, + __func__); if (result) { if (PIM_DEBUG_IGMP_TRACE) zlog_debug( @@ -815,8 +794,7 @@ void igmp_source_forward_stop(struct igmp_source *source) Feed IGMPv3-gathered local membership information into PIM per-interface (S,G) state. */ - pim_ifchannel_local_membership_del(group->group_igmp_sock->interface, - &sg); + pim_ifchannel_local_membership_del(group->interface, &sg); IGMP_SOURCE_DONT_FORWARDING(source->source_flags); } diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index dce936b8a9..abf9577bd5 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -31,6 +31,7 @@ #include "pimd.h" #include "pim_iface.h" +#include "pim_neighbor.h" #include "pim_pim.h" #include "pim_str.h" #include "pim_oil.h" diff --git a/pimd/pimd.h b/pimd/pimd.h index 88e692b50d..4cb860a6b7 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -136,7 +136,6 @@ extern const char *const PIM_ALL_ROUTERS; extern const char *const PIM_ALL_PIM_ROUTERS; extern const char *const PIM_ALL_IGMP_ROUTERS; -extern struct pim_router *router; extern struct zebra_privs_t pimd_privs; extern struct in_addr qpim_all_pim_routers_addr; extern uint8_t qpim_ecmp_enable; diff --git a/staticd/static_nb.c b/staticd/static_nb.c index c1a6253a1d..5935364d5a 100644 --- a/staticd/static_nb.c +++ b/staticd/static_nb.c @@ -21,7 +21,7 @@ #include "northbound.h" #include "libfrr.h" #include "static_nb.h" - +#include "static_vty.h" /* clang-format off */ @@ -29,10 +29,18 @@ const struct frr_yang_module_info frr_staticd_info = { .name = "frr-staticd", .nodes = { { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd", + .cbs = { + .cli_show = static_cli_show, + .cli_show_end = static_cli_show_end, + } + }, + { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list", .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy, + .cli_cmp = static_route_list_cli_cmp, } }, { @@ -40,6 +48,7 @@ const struct frr_yang_module_info frr_staticd_info = { .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy, + .cli_cmp = static_path_list_cli_cmp, } }, { @@ -55,6 +64,8 @@ const struct frr_yang_module_info frr_staticd_info = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy, .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate, + .cli_show = static_nexthop_cli_show, + .cli_cmp = static_nexthop_cli_cmp, } }, { @@ -110,6 +121,7 @@ const struct frr_yang_module_info frr_staticd_info = { .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy, + .cli_cmp = static_src_list_cli_cmp, } }, { @@ -117,6 +129,7 @@ const struct frr_yang_module_info frr_staticd_info = { .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy, + .cli_cmp = static_path_list_cli_cmp, } }, { @@ -132,6 +145,8 @@ const struct frr_yang_module_info frr_staticd_info = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy, .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate, + .cli_show = static_src_nexthop_cli_show, + .cli_cmp = static_nexthop_cli_cmp, } }, { diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c index deeca97b0e..470c7bdad5 100644 --- a/staticd/static_nb_config.c +++ b/staticd/static_nb_config.c @@ -122,7 +122,7 @@ struct nexthop_iter { static int nexthop_iter_cb(const struct lyd_node *dnode, void *arg) { struct nexthop_iter *iter = arg; - int nh_type; + enum static_nh_type nh_type; nh_type = yang_dnode_get_enum(dnode, "./nh-type"); @@ -141,7 +141,7 @@ static bool static_nexthop_create(struct nb_cb_create_args *args) struct static_path *pn; struct ipaddr ipaddr; struct static_nexthop *nh; - int nh_type; + enum static_nh_type nh_type; const char *ifname; const char *nh_vrf; @@ -304,7 +304,7 @@ static int static_nexthop_mpls_label_modify(struct nb_cb_modify_args *args) static int static_nexthop_onlink_modify(struct nb_cb_modify_args *args) { struct static_nexthop *nh; - static_types nh_type; + enum static_nh_type nh_type; switch (args->event) { case NB_EV_VALIDATE: @@ -352,7 +352,7 @@ static int static_nexthop_color_destroy(struct nb_cb_destroy_args *args) static int static_nexthop_bh_type_modify(struct nb_cb_modify_args *args) { struct static_nexthop *nh; - static_types nh_type; + enum static_nh_type nh_type; switch (args->event) { case NB_EV_VALIDATE: diff --git a/staticd/static_routes.c b/staticd/static_routes.c index 77a10092f8..60f384e517 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -186,7 +186,8 @@ void static_del_route(struct route_node *rn) route_unlock_node(rn); } -bool static_add_nexthop_validate(const char *nh_vrf_name, static_types type, +bool static_add_nexthop_validate(const char *nh_vrf_name, + enum static_nh_type type, struct ipaddr *ipaddr) { struct vrf *vrf; @@ -257,7 +258,7 @@ void static_del_path(struct static_path *pn) } struct static_nexthop *static_add_nexthop(struct static_path *pn, - static_types type, + enum static_nh_type type, struct ipaddr *ipaddr, const char *ifname, const char *nh_vrf, uint32_t color) @@ -772,7 +773,7 @@ void static_ifindex_update(struct interface *ifp, bool up) static_ifindex_update_af(ifp, up, AFI_IP6, SAFI_MULTICAST); } -void static_get_nh_type(static_types stype, char *type, size_t size) +void static_get_nh_type(enum static_nh_type stype, char *type, size_t size) { switch (stype) { case STATIC_IFNAME: diff --git a/staticd/static_routes.h b/staticd/static_routes.h index 2211384916..c901a8926a 100644 --- a/staticd/static_routes.h +++ b/staticd/static_routes.h @@ -47,14 +47,14 @@ enum static_blackhole_type { * The order for below macros should be in sync with * yang model typedef nexthop-type */ -typedef enum { +enum static_nh_type { STATIC_IFNAME = 1, STATIC_IPV4_GATEWAY, STATIC_IPV4_GATEWAY_IFNAME, STATIC_IPV6_GATEWAY, STATIC_IPV6_GATEWAY_IFNAME, STATIC_BLACKHOLE, -} static_types; +}; /* * Route Creation gives us: @@ -123,7 +123,7 @@ struct static_nexthop { enum static_install_states state; /* Flag for this static route's type. */ - static_types type; + enum static_nh_type type; /* * Nexthop value. @@ -169,7 +169,7 @@ extern struct zebra_privs_t static_privs; void static_fixup_vrf_ids(struct static_vrf *svrf); extern struct static_nexthop * -static_add_nexthop(struct static_path *pn, static_types type, +static_add_nexthop(struct static_path *pn, enum static_nh_type type, struct ipaddr *ipaddr, const char *ifname, const char *nh_vrf, uint32_t color); extern void static_install_nexthop(struct static_nexthop *nh); @@ -194,9 +194,10 @@ extern struct static_path *static_add_path(struct route_node *rn, uint32_t table_id, uint8_t distance); extern void static_del_path(struct static_path *pn); -extern void static_get_nh_type(static_types stype, char *type, size_t size); +extern void static_get_nh_type(enum static_nh_type stype, char *type, + size_t size); extern bool static_add_nexthop_validate(const char *nh_vrf_name, - static_types type, + enum static_nh_type type, struct ipaddr *ipaddr); extern struct stable_info *static_get_stable_info(struct route_node *rn); diff --git a/staticd/static_vrf.c b/staticd/static_vrf.c index 740d904690..4bea3075c9 100644 --- a/staticd/static_vrf.c +++ b/staticd/static_vrf.c @@ -23,11 +23,11 @@ #include "nexthop.h" #include "table.h" #include "srcdest_table.h" +#include "northbound_cli.h" #include "static_vrf.h" #include "static_routes.h" #include "static_zebra.h" -#include "static_vty.h" DEFINE_MTYPE_STATIC(STATIC, STATIC_RTABLE_INFO, "Static Route Table Info"); @@ -150,24 +150,16 @@ struct static_vrf *static_vrf_lookup_by_name(const char *name) static int static_vrf_config_write(struct vty *vty) { - struct vrf *vrf; - - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - if (vrf->vrf_id != VRF_DEFAULT) - vty_frame(vty, "vrf %s\n", vrf->name); + struct lyd_node *dnode; + int written = 0; - static_config(vty, vrf->info, AFI_IP, - SAFI_UNICAST, "ip route"); - static_config(vty, vrf->info, AFI_IP, - SAFI_MULTICAST, "ip mroute"); - static_config(vty, vrf->info, AFI_IP6, - SAFI_UNICAST, "ipv6 route"); - - if (vrf->vrf_id != VRF_DEFAULT) - vty_endframe(vty, "exit-vrf\n!\n"); + dnode = yang_dnode_get(running_config->dnode, "/frr-routing:routing"); + if (dnode) { + nb_cli_show_dnode_cmds(vty, dnode, false); + written = 1; } - return 0; + return written; } void static_vrf_init(void) diff --git a/staticd/static_vty.c b/staticd/static_vty.c index f16b40a23f..751a262775 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -56,7 +56,7 @@ static int static_route_leak(struct vty *vty, const char *svrf, int ret; struct prefix p, src; struct in_addr mask; - uint8_t type; + enum static_nh_type type; const char *bh_type; char xpath_prefix[XPATH_MAXLEN]; char xpath_nexthop[XPATH_MAXLEN]; @@ -357,129 +357,6 @@ static int static_route(struct vty *vty, afi_t afi, safi_t safi, table_str, false, NULL); } -/* Write static route configuration. */ -int static_config(struct vty *vty, struct static_vrf *svrf, afi_t afi, - safi_t safi, const char *cmd) -{ - char spacing[100]; - struct route_node *rn; - struct static_nexthop *nh; - struct static_path *pn; - struct route_table *stable; - struct static_route_info *si; - char buf[SRCDEST2STR_BUFFER]; - int write = 0; - struct stable_info *info; - - stable = svrf->stable[afi][safi]; - if (stable == NULL) - return write; - - snprintf(spacing, sizeof(spacing), "%s%s", - (svrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ", cmd); - - for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { - si = static_route_info_from_rnode(rn); - if (!si) - continue; - info = static_get_stable_info(rn); - frr_each(static_path_list, &si->path_list, pn) { - frr_each(static_nexthop_list, &pn->nexthop_list, nh) { - vty_out(vty, "%s %s", spacing, - srcdest_rnode2str(rn, buf, - sizeof(buf))); - - switch (nh->type) { - case STATIC_IPV4_GATEWAY: - vty_out(vty, " %pI4", &nh->addr.ipv4); - break; - case STATIC_IPV6_GATEWAY: - vty_out(vty, " %s", - inet_ntop(AF_INET6, - &nh->addr.ipv6, buf, - sizeof(buf))); - break; - case STATIC_IFNAME: - vty_out(vty, " %s", nh->ifname); - break; - case STATIC_BLACKHOLE: - switch (nh->bh_type) { - case STATIC_BLACKHOLE_DROP: - vty_out(vty, " blackhole"); - break; - case STATIC_BLACKHOLE_NULL: - vty_out(vty, " Null0"); - break; - case STATIC_BLACKHOLE_REJECT: - vty_out(vty, " reject"); - break; - } - break; - case STATIC_IPV4_GATEWAY_IFNAME: - vty_out(vty, " %s %s", - inet_ntop(AF_INET, - &nh->addr.ipv4, buf, - sizeof(buf)), - nh->ifname); - break; - case STATIC_IPV6_GATEWAY_IFNAME: - vty_out(vty, " %s %s", - inet_ntop(AF_INET6, - &nh->addr.ipv6, buf, - sizeof(buf)), - nh->ifname); - break; - } - - if (pn->tag) - vty_out(vty, " tag %" ROUTE_TAG_PRI, - pn->tag); - - if (pn->distance - != ZEBRA_STATIC_DISTANCE_DEFAULT) - vty_out(vty, " %u", pn->distance); - - /* Label information */ - if (nh->snh_label.num_labels) - vty_out(vty, " label %s", - mpls_label2str( - nh->snh_label - .num_labels, - nh->snh_label.label, - buf, sizeof(buf), 0)); - - if (!strmatch(nh->nh_vrfname, - info->svrf->vrf->name)) - vty_out(vty, " nexthop-vrf %s", - nh->nh_vrfname); - - /* - * table ID from VRF overrides - * configured - */ - if (pn->table_id - && svrf->vrf->data.l.table_id - == RT_TABLE_MAIN) - vty_out(vty, " table %u", pn->table_id); - - if (nh->onlink) - vty_out(vty, " onlink"); - - /* - * SR-TE color - */ - if (nh->color != 0) - vty_out(vty, " color %u", nh->color); - - vty_out(vty, "\n"); - - write = 1; - } - } - } - return write; -} - /* Static unicast routes for multicast RPF lookup. */ DEFPY_YANG (ip_mroute_dist, ip_mroute_dist_cmd, @@ -1124,6 +1001,278 @@ DEFPY_YANG(ipv6_route_vrf, ifname, flag, tag_str, distance_str, label, table_str, false, color_str); } + +void static_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "../vrf"); + if (strcmp(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, "vrf %s\n", vrf); +} + +void static_cli_show_end(struct vty *vty, struct lyd_node *dnode) +{ + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "../vrf"); + if (strcmp(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, "exit-vrf\n"); +} + +struct mpls_label_iter { + struct vty *vty; + bool first; +}; + +static int mpls_label_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct mpls_label_iter *iter = arg; + + if (yang_dnode_exists(dnode, "./label")) { + if (iter->first) + vty_out(iter->vty, " label %s", + yang_dnode_get_string(dnode, "./label")); + else + vty_out(iter->vty, "/%s", + yang_dnode_get_string(dnode, "./label")); + iter->first = false; + } + + return YANG_ITER_CONTINUE; +} + +static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, + const struct lyd_node *src, + const struct lyd_node *path, + const struct lyd_node *nexthop, bool show_defaults) +{ + const char *vrf; + const char *afi_safi; + afi_t afi; + safi_t safi; + enum static_nh_type nh_type; + enum static_blackhole_type bh_type; + uint32_t tag; + uint8_t distance; + struct mpls_label_iter iter; + const char *nexthop_vrf; + uint32_t table_id; + bool onlink; + + vrf = yang_dnode_get_string(route, "../../vrf"); + + afi_safi = yang_dnode_get_string(route, "./afi-safi"); + yang_afi_safi_identity2value(afi_safi, &afi, &safi); + + if (afi == AFI_IP) + vty_out(vty, "%sip", + strmatch(vrf, VRF_DEFAULT_NAME) ? "" : " "); + else + vty_out(vty, "%sipv6", + strmatch(vrf, VRF_DEFAULT_NAME) ? "" : " "); + + if (safi == SAFI_UNICAST) + vty_out(vty, " route"); + else + vty_out(vty, " mroute"); + + vty_out(vty, " %s", yang_dnode_get_string(route, "./prefix")); + + if (src) + vty_out(vty, " from %s", + yang_dnode_get_string(src, "./src-prefix")); + + nh_type = yang_dnode_get_enum(nexthop, "./nh-type"); + switch (nh_type) { + case STATIC_IFNAME: + vty_out(vty, " %s", + yang_dnode_get_string(nexthop, "./interface")); + break; + case STATIC_IPV4_GATEWAY: + case STATIC_IPV6_GATEWAY: + vty_out(vty, " %s", + yang_dnode_get_string(nexthop, "./gateway")); + break; + case STATIC_IPV4_GATEWAY_IFNAME: + case STATIC_IPV6_GATEWAY_IFNAME: + vty_out(vty, " %s", + yang_dnode_get_string(nexthop, "./gateway")); + vty_out(vty, " %s", + yang_dnode_get_string(nexthop, "./interface")); + break; + case STATIC_BLACKHOLE: + bh_type = yang_dnode_get_enum(nexthop, "./bh-type"); + switch (bh_type) { + case STATIC_BLACKHOLE_DROP: + vty_out(vty, " blackhole"); + break; + case STATIC_BLACKHOLE_NULL: + vty_out(vty, " Null0"); + break; + case STATIC_BLACKHOLE_REJECT: + vty_out(vty, " reject"); + break; + } + break; + } + + if (yang_dnode_exists(path, "./tag")) { + tag = yang_dnode_get_uint32(path, "./tag"); + if (tag != 0 || show_defaults) + vty_out(vty, " tag %" PRIu32, tag); + } + + distance = yang_dnode_get_uint8(path, "./distance"); + if (distance != ZEBRA_STATIC_DISTANCE_DEFAULT || show_defaults) + vty_out(vty, " %" PRIu8, distance); + + iter.vty = vty; + iter.first = true; + yang_dnode_iterate(mpls_label_iter_cb, &iter, nexthop, + "./mpls-label-stack/entry"); + + nexthop_vrf = yang_dnode_get_string(nexthop, "./vrf"); + if (strcmp(vrf, nexthop_vrf)) + vty_out(vty, " nexthop-vrf %s", nexthop_vrf); + + table_id = yang_dnode_get_uint32(path, "./table-id"); + if (table_id || show_defaults) + vty_out(vty, " table %" PRIu32, table_id); + + if (yang_dnode_exists(nexthop, "./onlink")) { + onlink = yang_dnode_get_bool(nexthop, "./onlink"); + if (onlink) + vty_out(vty, " onlink"); + } + + if (yang_dnode_exists(nexthop, "./srte-color")) + vty_out(vty, " color %s", + yang_dnode_get_string(nexthop, "./srte-color")); + + vty_out(vty, "\n"); +} + +void static_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list"); + const struct lyd_node *route = + yang_dnode_get_parent(path, "route-list"); + + nexthop_cli_show(vty, route, NULL, path, dnode, show_defaults); +} + +void static_src_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list"); + const struct lyd_node *src = yang_dnode_get_parent(path, "src-list"); + const struct lyd_node *route = yang_dnode_get_parent(src, "route-list"); + + nexthop_cli_show(vty, route, src, path, dnode, show_defaults); +} + +int static_nexthop_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +{ + enum static_nh_type nh_type1, nh_type2; + struct prefix prefix1, prefix2; + int ret = 0; + + nh_type1 = yang_dnode_get_enum(dnode1, "./nh-type"); + nh_type2 = yang_dnode_get_enum(dnode2, "./nh-type"); + + if (nh_type1 != nh_type2) + return (int)nh_type1 - (int)nh_type2; + + switch (nh_type1) { + case STATIC_IFNAME: + ret = if_cmp_name_func( + yang_dnode_get_string(dnode1, "./interface"), + yang_dnode_get_string(dnode2, "./interface")); + break; + case STATIC_IPV4_GATEWAY: + case STATIC_IPV6_GATEWAY: + yang_dnode_get_prefix(&prefix1, dnode1, "./gateway"); + yang_dnode_get_prefix(&prefix2, dnode2, "./gateway"); + ret = prefix_cmp(&prefix1, &prefix2); + break; + case STATIC_IPV4_GATEWAY_IFNAME: + case STATIC_IPV6_GATEWAY_IFNAME: + yang_dnode_get_prefix(&prefix1, dnode1, "./gateway"); + yang_dnode_get_prefix(&prefix2, dnode2, "./gateway"); + ret = prefix_cmp(&prefix1, &prefix2); + if (!ret) + ret = if_cmp_name_func( + yang_dnode_get_string(dnode1, "./interface"), + yang_dnode_get_string(dnode2, "./interface")); + break; + case STATIC_BLACKHOLE: + /* There's only one blackhole nexthop per route */ + ret = 0; + break; + } + + if (ret) + return ret; + + return if_cmp_name_func(yang_dnode_get_string(dnode1, "./vrf"), + yang_dnode_get_string(dnode2, "./vrf")); +} + +int static_route_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +{ + const char *afi_safi1, *afi_safi2; + afi_t afi1, afi2; + safi_t safi1, safi2; + struct prefix prefix1, prefix2; + + afi_safi1 = yang_dnode_get_string(dnode1, "./afi-safi"); + yang_afi_safi_identity2value(afi_safi1, &afi1, &safi1); + + afi_safi2 = yang_dnode_get_string(dnode2, "./afi-safi"); + yang_afi_safi_identity2value(afi_safi2, &afi2, &safi2); + + if (afi1 != afi2) + return (int)afi1 - (int)afi2; + + if (safi1 != safi2) + return (int)safi1 - (int)safi2; + + yang_dnode_get_prefix(&prefix1, dnode1, "./prefix"); + yang_dnode_get_prefix(&prefix2, dnode2, "./prefix"); + + return prefix_cmp(&prefix1, &prefix2); +} + +int static_src_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +{ + struct prefix prefix1, prefix2; + + yang_dnode_get_prefix(&prefix1, dnode1, "./src-prefix"); + yang_dnode_get_prefix(&prefix2, dnode2, "./src-prefix"); + + return prefix_cmp(&prefix1, &prefix2); +} + +int static_path_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +{ + uint32_t table_id1, table_id2; + uint8_t distance1, distance2; + + table_id1 = yang_dnode_get_uint32(dnode1, "./table-id"); + table_id2 = yang_dnode_get_uint32(dnode2, "./table-id"); + + if (table_id1 != table_id2) + return (int)table_id1 - (int)table_id2; + + distance1 = yang_dnode_get_uint8(dnode1, "./distance"); + distance2 = yang_dnode_get_uint8(dnode2, "./distance"); + + return (int)distance1 - (int)distance2; +} + DEFPY_YANG(debug_staticd, debug_staticd_cmd, "[no] debug static [{events$events|route$route}]", NO_STR DEBUG_STR STATICD_STR diff --git a/staticd/static_vty.h b/staticd/static_vty.h index 01577685e5..8861afa468 100644 --- a/staticd/static_vty.h +++ b/staticd/static_vty.h @@ -23,8 +23,17 @@ extern "C" { #endif -int static_config(struct vty *vty, struct static_vrf *svrf, - afi_t afi, safi_t safi, const char *cmd); +void static_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void static_cli_show_end(struct vty *vty, struct lyd_node *dnode); +void static_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void static_src_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +int static_nexthop_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); +int static_route_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); +int static_src_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); +int static_path_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); void static_vty_init(void); diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c index 92efd4c3d6..77fd876594 100644 --- a/tests/bgpd/test_mpath.c +++ b/tests/bgpd/test_mpath.c @@ -310,7 +310,7 @@ static int setup_bgp_path_info_mpath_update(testcase_t *t) str2prefix("42.1.1.0/24", &test_rn.p); rt_node = bgp_dest_to_rnode(&test_rn); memcpy((struct route_table *)&rt_node->table, &rt->route_table, - sizeof(struct route_table *)); + sizeof(struct route_table)); setup_bgp_mp_list(t); for (i = 0; i < test_mp_list_info_count; i++) bgp_path_info_add(&test_rn, &test_mp_list_info[i]); diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c index 21b3a916b8..06996a2f13 100644 --- a/tests/lib/test_printfrr.c +++ b/tests/lib/test_printfrr.c @@ -24,6 +24,7 @@ #include "lib/printfrr.h" #include "lib/memory.h" #include "lib/prefix.h" +#include "lib/nexthop.h" static int errors; @@ -253,5 +254,25 @@ int main(int argc, char **argv) printchk("\"\"", "%pSQqn", (char *)NULL); printchk("(null)", "%pSQq", (char *)NULL); + /* + * %pNH<foo> tests + * + * gateway addresses only for now: interfaces require more setup + */ + printchk("(null)", "%pNHcg", NULL); + printchk("(null)", "%pNHci", NULL); + + struct nexthop nh; + + memset(&nh, 0, sizeof(nh)); + + nh.type = NEXTHOP_TYPE_IPV4; + inet_aton("3.2.1.0", &nh.gate.ipv4); + printchk("3.2.1.0", "%pNHcg", &nh); + + nh.type = NEXTHOP_TYPE_IPV6; + inet_pton(AF_INET6, "fe2c::34", &nh.gate.ipv6); + printchk("fe2c::34", "%pNHcg", &nh); + return !!errors; } diff --git a/tests/subdir.am b/tests/subdir.am index b0be63c695..1edfda9bc2 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -183,7 +183,7 @@ TESTS_CXXFLAGS = \ # note no -Werror ALL_TESTS_LDADD = lib/libfrr.la $(LIBCAP) -BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm +BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) $(UST_LIBS) -lm ISISD_TEST_LDADD = isisd/libisis.a $(ALL_TESTS_LDADD) if GRPC GRPC_TESTS_LDADD = staticd/libstatic.a grpc/libfrrgrpc_pb.la -lgrpc++ -lprotobuf $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json new file mode 100644 index 0000000000..f2df9be49d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json @@ -0,0 +1,160 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json new file mode 100644 index 0000000000..0fdd3d6dc0 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json @@ -0,0 +1,169 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf index ec36870369..68b5730a63 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf @@ -34,7 +34,9 @@ segment-routing ip forwarding ipv6 forwarding ! +ipv6 route 2001:db8:2:1::/64 2001::2 ipv6 route 2001:db8:2:2::/64 2001::2 +ipv6 route 2001:db8:2:3::/64 2001::2 ! line vty ! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json new file mode 100644 index 0000000000..25cdf031c3 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json @@ -0,0 +1,93 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json new file mode 100644 index 0000000000..03bbcc008d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json @@ -0,0 +1,169 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf index f3e025d23a..91fd92d422 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf @@ -35,6 +35,8 @@ ip forwarding ipv6 forwarding ! ipv6 route 2001:db8:1:1::/64 2001::1 +ipv6 route 2001:db8:1:2::/64 2001::1 +ipv6 route 2001:db8:1:3::/64 2001::1 ! line vty ! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py index 2d544c1ccf..e0cf8c88e6 100755 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py @@ -129,6 +129,10 @@ def setup_module(mod): tgen.gears["r2"].run("ip link set eth3 master vrf20") tgen.start_router() + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + def teardown_module(mod): tgen = get_topogen() @@ -143,7 +147,22 @@ def open_json_file(filename): assert False, "Could not read file {}".format(filename) -def test_rib(): +def check_ping(name, dest_addr, expect_connected): + def _check(name, dest_addr, match): + tgen = get_topogen() + output = tgen.gears[name].run("ping6 {} -c 1 -w 1".format(dest_addr)) + logger.info(output) + assert match in output, "ping fail" + + match = "{} packet loss".format("0%" if expect_connected else "100%") + logger.info("[+] check {} {} {}".format(name, dest_addr, match)) + tgen = get_topogen() + func = functools.partial(_check, name, dest_addr, match) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def check_rib(name, cmd, expected_file): def _check(name, cmd, expected_file): logger.info("polling") tgen = get_topogen() @@ -152,51 +171,131 @@ def test_rib(): expected = open_json_file("{}/{}".format(CWD, expected_file)) return topotest.json_cmp(output, expected) - def check(name, cmd, expected_file): - logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file)) - tgen = get_topogen() - func = functools.partial(_check, name, cmd, expected_file) - success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) - assert result is None, "Failed" - - check("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json") - check("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json") - check("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json") - check("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json") - check("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json") - check("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json") - check("ce1", "show ipv6 route json", "ce1/ipv6_rib.json") - check("ce2", "show ipv6 route json", "ce2/ipv6_rib.json") - check("ce3", "show ipv6 route json", "ce3/ipv6_rib.json") - check("ce4", "show ipv6 route json", "ce4/ipv6_rib.json") - check("ce5", "show ipv6 route json", "ce5/ipv6_rib.json") - check("ce6", "show ipv6 route json", "ce6/ipv6_rib.json") + logger.info("[+] check {} \"{}\" {}".format(name, cmd, expected_file)) + tgen = get_topogen() + func = functools.partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def test_rib(): + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json") + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json") + check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json") + check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json") + check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json") + check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json") + check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json") + check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json") + check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json") + check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json") + check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json") def test_ping(): - def _check(name, dest_addr, match): - tgen = get_topogen() - output = tgen.gears[name].run("ping6 {} -c 1 -w 1".format(dest_addr)) - logger.info(output) - assert match in output, "ping fail" + check_ping("ce1", "2001:2::2", True) + check_ping("ce1", "2001:3::2", True) + check_ping("ce1", "2001:4::2", False) + check_ping("ce1", "2001:5::2", False) + check_ping("ce1", "2001:6::2", False) + check_ping("ce4", "2001:1::2", False) + check_ping("ce4", "2001:2::2", False) + check_ping("ce4", "2001:3::2", False) + check_ping("ce4", "2001:5::2", True) + check_ping("ce4", "2001:6::2", True) - def check(name, dest_addr, match): - logger.info("[+] check {} {} {}".format(name, dest_addr, match)) - tgen = get_topogen() - func = functools.partial(_check, name, dest_addr, match) - success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) - assert result is None, "Failed" - - check("ce1", "2001:2::2", " 0% packet loss") - check("ce1", "2001:3::2", " 0% packet loss") - check("ce1", "2001:4::2", " 100% packet loss") - check("ce1", "2001:5::2", " 100% packet loss") - check("ce1", "2001:6::2", " 100% packet loss") - check("ce4", "2001:1::2", " 100% packet loss") - check("ce4", "2001:2::2", " 100% packet loss") - check("ce4", "2001:3::2", " 100% packet loss") - check("ce4", "2001:5::2", " 0% packet loss") - check("ce4", "2001:6::2", " 0% packet loss") + +def test_locator_delete(): + check_ping("ce1", "2001:2::2", True) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + no locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False) + + +def test_locator_recreate(): + check_ping("ce1", "2001:2::2", False) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True) + + +def test_bgp_locator_unset(): + check_ping("ce1", "2001:2::2", True) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + no locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False) + + +def test_bgp_locator_reset(): + check_ping("ce1", "2001:2::2", False) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True) + + +def test_bgp_srv6_unset(): + check_ping("ce1", "2001:2::2", True) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + no segment-routing srv6 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False) + + +def test_bgp_srv6_reset(): + check_ping("ce1", "2001:2::2", False) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True) if __name__ == "__main__": diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index d05332388e..556240bfb5 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -2672,7 +2672,7 @@ def verify_best_path_as_per_admin_distance( return True -@retry(retry_timeout=10, initial_wait=2) +@retry(retry_timeout=30) def verify_bgp_rib( tgen, addr_type, diff --git a/tests/topotests/ospf6_gr_topo1/__init__.py b/tests/topotests/ospf6_gr_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/__init__.py diff --git a/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf new file mode 100644 index 0000000000..1ee1189766 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf @@ -0,0 +1,30 @@ +:assword 1 +hostname rt1 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 1 + ipv6 ospf network point-to-point +! +interface eth-rt2 + ipv6 ospf network point-to-point + ipv6 ospf area 1 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 1.1.1.1 + redistribute connected + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..58fc114a44 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json @@ -0,0 +1,95 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"1", + "lsa":[ + { + "type":"Rtr", + "advRouter":"1.1.1.1" + }, + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"1", + "interface":"eth-rt2", + "lsa":[ + { + "type":"Lnk", + "advRouter":"1.1.1.1" + }, + { + "type":"Lnk", + "advRouter":"2.2.2.2" + } + ] + }, + { + "areaId":"1", + "interface":"lo", + "lsa":[ + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..cb88358639 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json @@ -0,0 +1,12 @@ +{ + "neighbors":[ + { + "neighborId":"2.2.2.2", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt2", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..0c69310eb4 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json new file mode 100644 index 0000000000..66ee57ce84 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf new file mode 100644 index 0000000000..f29f5b73fb --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt1 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 1.1.1.1/32 + ipv6 address 2001:db8:1000::1/128 +! +interface stub1 +! +interface eth-rt2 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf new file mode 100644 index 0000000000..6cd8d1a8e3 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf @@ -0,0 +1,35 @@ +password 1 +hostname rt2 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 0 + ipv6 ospf network point-to-point +! +interface eth-rt1 + ipv6 ospf network point-to-point + ipv6 ospf area 1 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +interface eth-rt3 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 2.2.2.2 + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..fb16326196 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json @@ -0,0 +1,183 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"0", + "lsa":[ + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"INP", + "advRouter":"3.3.3.3", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"INP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"INP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + } + ] + }, + { + "areaId":"1", + "lsa":[ + { + "type":"Rtr", + "advRouter":"1.1.1.1" + }, + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"0", + "interface":"eth-rt3", + "lsa":[ + { + "type":"Lnk", + "advRouter":"2.2.2.2" + }, + { + "type":"Lnk", + "advRouter":"3.3.3.3" + } + ] + }, + { + "areaId":"0", + "interface":"lo", + "lsa":[ + ] + }, + { + "areaId":"1", + "interface":"eth-rt1", + "lsa":[ + { + "type":"Lnk", + "advRouter":"1.1.1.1" + }, + { + "type":"Lnk", + "advRouter":"2.2.2.2" + } + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..e4f27bf37f --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json @@ -0,0 +1,20 @@ +{ + "neighbors":[ + { + "neighborId":"3.3.3.3", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt3", + "interfaceState":"PointToPoint" + }, + { + "neighborId":"1.1.1.1", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt1", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..34013a19de --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt1" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json new file mode 100644 index 0000000000..624ff709e3 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt1", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf new file mode 100644 index 0000000000..e4fe7620da --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt2 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 2.2.2.2/32 + ipv6 address 2001:db8:1000::2/128 +! +interface eth-rt1 +! +interface eth-rt3 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf new file mode 100644 index 0000000000..6a63d8f788 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf @@ -0,0 +1,41 @@ +password 1 +hostname rt3 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 0 + ipv6 ospf network point-to-point +! +interface eth-rt2 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +interface eth-rt4 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +interface eth-rt6 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 3.3.3.3 + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..f8a8f76093 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json @@ -0,0 +1,144 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"0", + "lsa":[ + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"INP", + "advRouter":"3.3.3.3", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"INP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"INP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"0", + "interface":"eth-rt2", + "lsa":[ + { + "type":"Lnk", + "advRouter":"2.2.2.2" + }, + { + "type":"Lnk", + "advRouter":"3.3.3.3" + } + ] + }, + { + "areaId":"0", + "interface":"eth-rt4", + "lsa":[ + { + "type":"Lnk", + "advRouter":"3.3.3.3" + }, + { + "type":"Lnk", + "advRouter":"4.4.4.4" + } + ] + }, + { + "areaId":"0", + "interface":"eth-rt6", + "lsa":[ + { + "type":"Lnk", + "advRouter":"3.3.3.3" + }, + { + "type":"Lnk", + "advRouter":"6.6.6.6" + } + ] + }, + { + "areaId":"0", + "interface":"lo", + "lsa":[ + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..d0d7f45b0e --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json @@ -0,0 +1,28 @@ +{ + "neighbors":[ + { + "neighborId":"2.2.2.2", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt2", + "interfaceState":"PointToPoint" + }, + { + "neighborId":"4.4.4.4", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt4", + "interfaceState":"PointToPoint" + }, + { + "neighborId":"6.6.6.6", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt6", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..ee516b9d66 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json new file mode 100644 index 0000000000..f9b43dcdb9 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf new file mode 100644 index 0000000000..3a9de21d30 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf @@ -0,0 +1,24 @@ +password 1 +hostname rt3 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 3.3.3.3/32 + ipv6 address 2001:db8:1000::3/128 +! +interface eth-rt2 +! +interface eth-rt4 +! +interface eth-rt6 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf new file mode 100644 index 0000000000..dff33d4094 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf @@ -0,0 +1,35 @@ +password 1 +hostname rt4 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 0 + ipv6 ospf network point-to-point +! +interface eth-rt3 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +interface eth-rt5 + ipv6 ospf network point-to-point + ipv6 ospf area 2 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 4.4.4.4 + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..0954d1b8eb --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json @@ -0,0 +1,188 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"0", + "lsa":[ + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"INP", + "advRouter":"3.3.3.3", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"INP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"INP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + } + ] + }, + { + "areaId":"2", + "lsa":[ + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"5.5.5.5" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"4.4.4.4", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"4.4.4.4", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"5.5.5.5", + "payload":"2001:db8:1000::5\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"0", + "interface":"eth-rt3", + "lsa":[ + { + "type":"Lnk", + "advRouter":"3.3.3.3" + }, + { + "type":"Lnk", + "advRouter":"4.4.4.4" + } + ] + }, + { + "areaId":"0", + "interface":"lo", + "lsa":[ + ] + }, + { + "areaId":"2", + "interface":"eth-rt5", + "lsa":[ + { + "type":"Lnk", + "advRouter":"4.4.4.4" + }, + { + "type":"Lnk", + "advRouter":"5.5.5.5" + } + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..36abba4f87 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json @@ -0,0 +1,20 @@ +{ + "neighbors":[ + { + "neighborId":"3.3.3.3", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt3", + "interfaceState":"PointToPoint" + }, + { + "neighborId":"5.5.5.5", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt5", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..3e5f17f491 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt5" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json new file mode 100644 index 0000000000..f5212da4f6 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf new file mode 100644 index 0000000000..eeea417b70 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt4 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 4.4.4.4/32 + ipv6 address 2001:db8:1000::4/128 +! +interface eth-rt3 +! +interface eth-rt5 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf new file mode 100644 index 0000000000..49c3a8b86f --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf @@ -0,0 +1,29 @@ +password 1 +hostname rt5 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 2 + ipv6 ospf network point-to-point +! +interface eth-rt4 + ipv6 ospf network point-to-point + ipv6 ospf area 2 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 5.5.5.5 + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..4a163b984e --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json @@ -0,0 +1,100 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"2", + "lsa":[ + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"5.5.5.5" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"4.4.4.4", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"4.4.4.4", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"5.5.5.5", + "payload":"2001:db8:1000::5\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"2", + "interface":"eth-rt4", + "lsa":[ + { + "type":"Lnk", + "advRouter":"4.4.4.4" + }, + { + "type":"Lnk", + "advRouter":"5.5.5.5" + } + ] + }, + { + "areaId":"2", + "interface":"lo", + "lsa":[ + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..9b6ac911d1 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json @@ -0,0 +1,12 @@ +{ + "neighbors":[ + { + "neighborId":"4.4.4.4", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt4", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..a56c3262c6 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json new file mode 100644 index 0000000000..5ea4f699fe --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf new file mode 100644 index 0000000000..0cdb90b129 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf @@ -0,0 +1,20 @@ +password 1 +hostname rt5 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 5.5.5.5/32 + ipv6 address 2001:db8:1000::5/128 +! +interface eth-rt4 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf new file mode 100644 index 0000000000..5d6d3280b9 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf @@ -0,0 +1,35 @@ +password 1 +hostname rt6 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 0 + ipv6 ospf network point-to-point +! +interface eth-rt3 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +interface eth-rt7 + ipv6 ospf network point-to-point + ipv6 ospf area 3 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 6.6.6.6 + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..71872d19d0 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json @@ -0,0 +1,183 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"0", + "lsa":[ + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"INP", + "advRouter":"3.3.3.3", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"INP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"INP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + } + ] + }, + { + "areaId":"3", + "lsa":[ + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"Rtr", + "advRouter":"7.7.7.7" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"1.1.1.1" + }, + { + "type":"INP", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"0", + "interface":"eth-rt3", + "lsa":[ + { + "type":"Lnk", + "advRouter":"3.3.3.3" + }, + { + "type":"Lnk", + "advRouter":"6.6.6.6" + } + ] + }, + { + "areaId":"0", + "interface":"lo", + "lsa":[ + ] + }, + { + "areaId":"3", + "interface":"eth-rt7", + "lsa":[ + { + "type":"Lnk", + "advRouter":"6.6.6.6" + }, + { + "type":"Lnk", + "advRouter":"7.7.7.7" + } + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..aba181ba3f --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json @@ -0,0 +1,20 @@ +{ + "neighbors":[ + { + "neighborId":"3.3.3.3", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt3", + "interfaceState":"PointToPoint" + }, + { + "neighborId":"7.7.7.7", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt7", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..c9494a9d57 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt7" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json new file mode 100644 index 0000000000..862f1baffb --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt7", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf new file mode 100644 index 0000000000..3c2312da8a --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt6 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 6.6.6.6/32 + ipv6 address 2001:db8:1000::6/128 +! +interface eth-rt3 +! +interface eth-rt7 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf new file mode 100644 index 0000000000..f504fba4de --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf @@ -0,0 +1,30 @@ +password 1 +hostname rt7 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 3 + ipv6 ospf network point-to-point +! +interface eth-rt6 + ipv6 ospf network point-to-point + ipv6 ospf area 3 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 7.7.7.7 + redistribute connected + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..e70eb57b29 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json @@ -0,0 +1,95 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"3", + "lsa":[ + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"Rtr", + "advRouter":"7.7.7.7" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"1.1.1.1" + }, + { + "type":"INP", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"3", + "interface":"eth-rt6", + "lsa":[ + { + "type":"Lnk", + "advRouter":"6.6.6.6" + }, + { + "type":"Lnk", + "advRouter":"7.7.7.7" + } + ] + }, + { + "areaId":"3", + "interface":"lo", + "lsa":[ + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..5548691ef3 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json @@ -0,0 +1,12 @@ +{ + "neighbors":[ + { + "neighborId":"6.6.6.6", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt6", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..42ca54fded --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json new file mode 100644 index 0000000000..f5f8f710e5 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf new file mode 100644 index 0000000000..9cc8c29c1e --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt7 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 7.7.7.7/32 + ipv6 address 2001:db8:1000::7/128 +! +interface stub1 +! +interface eth-rt6 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py new file mode 100755 index 0000000000..ccbcadb8b1 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py @@ -0,0 +1,381 @@ +#!/usr/bin/env python + +# +# test_ospf6_gr_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or 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 NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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. +# + +""" +test_ospf6_gr_topo1.py: + + +---------+ + | RT1 | + | 1.1.1.1 | + +---------+ + |eth-rt2 + | + |eth-rt1 + +---------+ + | RT2 | + | 2.2.2.2 | + +---------+ + |eth-rt3 + | + |eth-rt2 + +---------+ + | RT3 | + | 3.3.3.3 | + +---------+ + eth-rt4| |eth-rt6 + | | + +---------+ +--------+ + | | + |eth-rt3 |eth-rt3 + +---------+ +---------+ + | RT4 | | RT6 | + | 4.4.4.4 | | 6.6.6.6 | + +---------+ +---------+ + |eth-rt5 |eth-rt7 + | | + |eth-rt4 |eth-rt6 + +---------+ +---------+ + | RT5 | | RT7 | + | 5.5.5.5 | | 7.7.7.7 | + +---------+ +---------+ +""" + +import os +import sys +import pytest +import json +from time import sleep +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import ( + kill_router_daemons, + start_router_daemons, +) + +pytestmark = [pytest.mark.ospf6d] + +# Global multi-dimensional dictionary containing all expected outputs +outputs = {} + + +def build_topo(tgen): + # + # Define FRR Routers + # + for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1") + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["rt1"], nodeif="stub1") + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2") + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3") + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt6") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt3") + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4") + + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt7") + switch.add_link(tgen.gears["rt7"], nodeif="eth-rt6") + + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["rt7"], nodeif="stub1") + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def router_compare_json_output(rname, command, reference, tries): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=tries, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def check_routers(initial_convergence=False, exiting=None, restarting=None): + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]: + # Check the RIB first, which should be preserved across restarts in + # all routers of the routing domain. + if initial_convergence == True: + tries = 240 + else: + tries = 1 + router_compare_json_output( + rname, "show ipv6 route ospf json", "show_ipv6_route.json", tries + ) + + # Check that all adjacencies are up and running (except when there's + # an OSPF instance that is shutting down). + if exiting == None: + tries = 240 + router_compare_json_output( + rname, + "show ipv6 ospf neighbor json", + "show_ipv6_ospf_neighbor.json", + tries, + ) + + # Check the OSPF RIB and LSDB. + # In the restarting router, wait up to one minute for the LSDB to converge. + if exiting != rname: + if initial_convergence == True or restarting == rname: + tries = 240 + else: + tries = 1 + router_compare_json_output( + rname, + "show ipv6 ospf database json", + "show_ipv6_ospf_database.json", + tries, + ) + router_compare_json_output( + rname, "show ipv6 ospf route json", "show_ipv6_ospf_route.json", tries + ) + + +# +# Test initial network convergence +# +def test_initial_convergence(): + logger.info("Test: verify initial network convergence") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + check_routers(initial_convergence=True) + + +# +# Test rt1 performing a graceful restart +# +def test_gr_rt1(): + logger.info("Test: verify rt1 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt1"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt1", ["ospf6d"], save_config=False) + check_routers(exiting="rt1") + + start_router_daemons(tgen, "rt1", ["ospf6d"]) + check_routers(restarting="rt1") + + +# +# Test rt2 performing a graceful restart +# +def test_gr_rt2(): + logger.info("Test: verify rt2 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt2"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt2", ["ospf6d"], save_config=False) + check_routers(exiting="rt2") + + start_router_daemons(tgen, "rt2", ["ospf6d"]) + check_routers(restarting="rt2") + + +# +# Test rt3 performing a graceful restart +# +def test_gr_rt3(): + logger.info("Test: verify rt3 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt3"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt3", ["ospf6d"], save_config=False) + check_routers(exiting="rt3") + + start_router_daemons(tgen, "rt3", ["ospf6d"]) + check_routers(restarting="rt3") + + +# +# Test rt4 performing a graceful restart +# +def test_gr_rt4(): + logger.info("Test: verify rt4 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt4"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt4", ["ospf6d"], save_config=False) + check_routers(exiting="rt4") + + start_router_daemons(tgen, "rt4", ["ospf6d"]) + check_routers(restarting="rt4") + + +# +# Test rt5 performing a graceful restart +# +def test_gr_rt5(): + logger.info("Test: verify rt5 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt5"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt5", ["ospf6d"], save_config=False) + check_routers(exiting="rt5") + + start_router_daemons(tgen, "rt5", ["ospf6d"]) + check_routers(restarting="rt5") + + +# +# Test rt6 performing a graceful restart +# +def test_gr_rt6(): + logger.info("Test: verify rt6 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt6"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt6", ["ospf6d"], save_config=False) + check_routers(exiting="rt6") + + start_router_daemons(tgen, "rt6", ["ospf6d"]) + check_routers(restarting="rt6") + + +# +# Test rt7 performing a graceful restart +# +def test_gr_rt7(): + logger.info("Test: verify rt7 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt7"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt7", ["ospf6d"], save_config=False) + check_routers(exiting="rt7") + + start_router_daemons(tgen, "rt7", ["ospf6d"]) + check_routers(restarting="rt7") + + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/srv6_locator/expected_chunks4.json b/tests/topotests/srv6_locator/expected_chunks4.json index 6e49738f37..0d4f101c7a 100644 --- a/tests/topotests/srv6_locator/expected_chunks4.json +++ b/tests/topotests/srv6_locator/expected_chunks4.json @@ -1,6 +1,2 @@ [ - { - "name": "loc3", - "chunks": [] - } ] diff --git a/tests/topotests/srv6_locator/expected_chunks5.json b/tests/topotests/srv6_locator/expected_chunks5.json index a18221859e..0d4f101c7a 100644 --- a/tests/topotests/srv6_locator/expected_chunks5.json +++ b/tests/topotests/srv6_locator/expected_chunks5.json @@ -1,8 +1,2 @@ [ - { - "name": "loc3", - "chunks": [ - "2001:db8:3:3::/64" - ] - } ] diff --git a/tests/topotests/srv6_locator/expected_chunks6.json b/tests/topotests/srv6_locator/expected_chunks6.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/tests/topotests/srv6_locator/expected_chunks6.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/topotests/srv6_locator/expected_locators4.json b/tests/topotests/srv6_locator/expected_locators4.json index 7989f9021b..4b0f95f7be 100644 --- a/tests/topotests/srv6_locator/expected_locators4.json +++ b/tests/topotests/srv6_locator/expected_locators4.json @@ -23,11 +23,13 @@ ] }, { - "name":"loc3", - "statusUp":false, - "chunks":[ + "name": "loc3", + "prefix": "2001:db8:3:3::/64", + "statusUp": true, + "chunks": [ { - "proto":"sharp" + "prefix": "2001:db8:3:3::/64", + "proto": "system" } ] } diff --git a/tests/topotests/srv6_locator/expected_locators5.json b/tests/topotests/srv6_locator/expected_locators5.json index 8c512ebc46..bcffa004bd 100644 --- a/tests/topotests/srv6_locator/expected_locators5.json +++ b/tests/topotests/srv6_locator/expected_locators5.json @@ -1,17 +1,6 @@ { "locators":[ { - "name": "loc1", - "prefix": "2001:db8:1:1::/64", - "statusUp": true, - "chunks": [ - { - "prefix": "2001:db8:1:1::/64", - "proto": "system" - } - ] - }, - { "name": "loc2", "prefix": "2001:db8:2:2::/64", "statusUp": true, @@ -29,7 +18,7 @@ "chunks":[ { "prefix": "2001:db8:3:3::/64", - "proto": "sharp" + "proto": "system" } ] } diff --git a/tests/topotests/srv6_locator/expected_locators6.json b/tests/topotests/srv6_locator/expected_locators6.json new file mode 100644 index 0000000000..66d23d5556 --- /dev/null +++ b/tests/topotests/srv6_locator/expected_locators6.json @@ -0,0 +1,5 @@ +{ + "locators":[ + ] +} + diff --git a/tests/topotests/srv6_locator/test_srv6_locator.py b/tests/topotests/srv6_locator/test_srv6_locator.py index b48cd09bf9..bc5fa409d2 100755 --- a/tests/topotests/srv6_locator/test_srv6_locator.py +++ b/tests/topotests/srv6_locator/test_srv6_locator.py @@ -102,6 +102,10 @@ def test_srv6(): success, result = topotest.run_and_expect(func, None, count=5, wait=0.5) assert result is None, "Failed" + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + logger.info("Test1 for Locator Configuration") check_srv6_locator(router, "expected_locators1.json") check_sharpd_chunk(router, "expected_chunks1.json") @@ -116,25 +120,44 @@ def test_srv6(): check_srv6_locator(router, "expected_locators3.json") check_sharpd_chunk(router, "expected_chunks3.json") - logger.info("Test4 get chunk for non-exist locator by zclient") - router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc3") + logger.info("Test4 additional locator loc3") + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc3 + prefix 2001:db8:3:3::/64 + """ + ) check_srv6_locator(router, "expected_locators4.json") check_sharpd_chunk(router, "expected_chunks4.json") - logger.info("Test5 Test for Zclient. after locator loc3 was configured") + logger.info("Test5 delete locator and chunk is released automatically") router.vtysh_cmd( """ configure terminal segment-routing srv6 locators - locator loc3 - prefix 2001:db8:3:3::/64 + no locator loc1 """ ) check_srv6_locator(router, "expected_locators5.json") check_sharpd_chunk(router, "expected_chunks5.json") + logger.info("Test6 delete srv6 all configuration") + router.vtysh_cmd( + """ + configure terminal + segment-routing + no srv6 + """ + ) + check_srv6_locator(router, "expected_locators6.json") + check_sharpd_chunk(router, "expected_chunks6.json") + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 86cf8c9657..334bd7affa 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -42,11 +42,13 @@ sub scan_file { $cppadd = $fabricd ? "-DFABRICD=1" : ""; - open (FH, "@CPP@ -P -std=gnu11 -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ @LIBYANG_CFLAGS@ $cppadd $file |"); + $command_line = "@CPP@ -P -std=gnu11 -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ @LIBYANG_CFLAGS@ $cppadd $file |"; + open (FH, $command_line) + || die "Open to the pipeline failed: $!\n\nCommand Issued:\n$command_line"; local $/; undef $/; $line = <FH>; if (!close (FH)) { - printf "File: $file failed to compile, when extracting cli from it please inspect\n" + die "File: $file failed to compile:\n$!\nwhen extracting cli from it please inspect\n" } # ?: makes a group non-capturing diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 9ffbcc9223..496849251a 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1137,6 +1137,31 @@ static int zsend_table_manager_connect_response(struct zserv *client, return zserv_send_message(client, s); } +/* SRv6 locator add notification from zebra daemon. */ +int zsend_zebra_srv6_locator_add(struct zserv *client, struct srv6_locator *loc) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_SRV6_LOCATOR_ADD, VRF_DEFAULT); + zapi_srv6_locator_encode(s, loc); + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +} + +/* SRv6 locator delete notification from zebra daemon. */ +int zsend_zebra_srv6_locator_delete(struct zserv *client, + struct srv6_locator *loc) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_SRV6_LOCATOR_DELETE, VRF_DEFAULT); + zapi_srv6_locator_encode(s, loc); + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +} + /* Inbound message handling ------------------------------------------------ */ const int cmd2type[] = { diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 8caabf19e7..43958fdfde 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -1997,7 +1997,8 @@ static int zfpm_init(struct thread_master *master) zfpm_stats_init(&zfpm_g->cumulative_stats); memset(&ipv4ll_gateway, 0, sizeof(ipv4ll_gateway)); - inet_pton(AF_INET, ipv4_ll_buf, &ipv4ll_gateway.ipv4); + if (inet_pton(AF_INET, ipv4_ll_buf, &ipv4ll_gateway.ipv4) != 1) + zlog_warn("inet_pton failed for %s", ipv4_ll_buf); install_node(&zebra_node); install_element(ENABLE_NODE, &show_zebra_fpm_stats_cmd); diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index c916c9a12f..aa015992d5 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1773,6 +1773,14 @@ static struct nexthop *nexthop_set_resolved(afi_t afi, nexthop_add_labels(resolved_hop, label_type, num_labels, labels); + if (nexthop->nh_srv6) { + nexthop_add_srv6_seg6local(resolved_hop, + nexthop->nh_srv6->seg6local_action, + &nexthop->nh_srv6->seg6local_ctx); + nexthop_add_srv6_seg6(resolved_hop, + &nexthop->nh_srv6->seg6_segs); + } + resolved_hop->rparent = nexthop; _nexthop_add(&nexthop->resolved, resolved_hop); diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index b11331a180..219d047694 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -106,15 +106,60 @@ void zebra_srv6_locator_add(struct srv6_locator *locator) { struct zebra_srv6 *srv6 = zebra_srv6_get_default(); struct srv6_locator *tmp; + struct listnode *node; + struct zserv *client; tmp = zebra_srv6_locator_lookup(locator->name); if (!tmp) listnode_add(srv6->locators, locator); + + /* + * Notify new locator info to zclients. + * + * The srv6 locators and their prefixes are managed by zserv(zebra). + * And an actual configuration the srv6 sid in the srv6 locator is done + * by zclient(bgpd, isisd, etc). The configuration of each locator + * allocation and specify it by zserv and zclient should be + * asynchronous. For that, zclient should be received the event via + * ZAPI when a srv6 locator is added on zebra. + * Basically, in SRv6, adding/removing SRv6 locators is performed less + * frequently than adding rib entries, so a broad to all zclients will + * not degrade the overall performance of FRRouting. + */ + for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) + zsend_zebra_srv6_locator_add(client, locator); } void zebra_srv6_locator_delete(struct srv6_locator *locator) { + struct listnode *n; + struct srv6_locator_chunk *c; struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct zserv *client; + + /* + * Notify deleted locator info to zclients if needed. + * + * zclient(bgpd,isisd,etc) allocates a sid from srv6 locator chunk and + * uses it for its own purpose. For example, in the case of BGP L3VPN, + * the SID assigned to vpn unicast rib will be given. + * And when the locator is deleted by zserv(zebra), those SIDs need to + * be withdrawn. The zclient must initiate the withdrawal of the SIDs + * by ZEBRA_SRV6_LOCATOR_DELETE, and this notification is sent to the + * owner of each chunk. + */ + for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, n, c)) { + if (c->proto == ZEBRA_ROUTE_SYSTEM) + continue; + client = zserv_find_client(c->proto, c->instance); + if (!client) { + zlog_warn( + "%s: Not found zclient(proto=%u, instance=%u).", + __func__, c->proto, c->instance); + continue; + } + zsend_zebra_srv6_locator_delete(client, locator); + } listnode_delete(srv6->locators, locator); } @@ -171,19 +216,7 @@ assign_srv6_locator_chunk(uint8_t proto, if (!loc) { zlog_info("%s: locator %s was not found", __func__, locator_name); - - loc = srv6_locator_alloc(locator_name); - if (!loc) { - zlog_info("%s: locator %s can't allocated", - __func__, locator_name); - return NULL; - } - - loc->status_up = false; - chunk = srv6_locator_chunk_alloc(); - chunk->proto = NO_PROTO; - listnode_add(loc->chunks, chunk); - zebra_srv6_locator_add(loc); + return NULL; } for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) { diff --git a/zebra/zebra_srv6_vty.c b/zebra/zebra_srv6_vty.c index d2b91b6c07..cb1e6c4228 100644 --- a/zebra/zebra_srv6_vty.c +++ b/zebra/zebra_srv6_vty.c @@ -197,6 +197,21 @@ DEFUN_NOSH (srv6, return CMD_SUCCESS; } +DEFUN (no_srv6, + no_srv6_cmd, + "no srv6", + NO_STR + "Segment Routing SRv6\n") +{ + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct srv6_locator *locator; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(srv6->locators, node, nnode, locator)) + zebra_srv6_locator_delete(locator); + return CMD_SUCCESS; +} + DEFUN_NOSH (srv6_locators, srv6_locators_cmd, "locators", @@ -233,6 +248,23 @@ DEFUN_NOSH (srv6_locator, return CMD_SUCCESS; } +DEFUN (no_srv6_locator, + no_srv6_locator_cmd, + "no locator WORD", + NO_STR + "Segment Routing SRv6 locator\n" + "Specify locator-name\n") +{ + struct srv6_locator *locator = zebra_srv6_locator_lookup(argv[2]->arg); + if (!locator) { + vty_out(vty, "%% Can't find SRv6 locator\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + zebra_srv6_locator_delete(locator); + return CMD_SUCCESS; +} + DEFPY (locator_prefix, locator_prefix_cmd, "prefix X:X::X:X/M$prefix [func-bits (16-64)$func_bit_len]", @@ -348,8 +380,10 @@ void zebra_srv6_vty_init(void) /* Command for change node */ install_element(CONFIG_NODE, &segment_routing_cmd); install_element(SEGMENT_ROUTING_NODE, &srv6_cmd); + install_element(SEGMENT_ROUTING_NODE, &no_srv6_cmd); install_element(SRV6_NODE, &srv6_locators_cmd); install_element(SRV6_LOCS_NODE, &srv6_locator_cmd); + install_element(SRV6_LOCS_NODE, &no_srv6_locator_cmd); /* Command for configuration */ install_element(SRV6_LOC_NODE, &locator_prefix_cmd); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 1c8a1ad09a..79087c5849 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -353,8 +353,6 @@ static void show_nexthop_detail_helper(struct vty *vty, break; } break; - default: - break; } if (re->vrf_id != nexthop->vrf_id) { @@ -605,8 +603,6 @@ static void show_route_nexthop_helper(struct vty *vty, break; } break; - default: - break; } if ((re == NULL || (nexthop->vrf_id != re->vrf_id))) @@ -777,8 +773,6 @@ static void show_nexthop_json_helper(json_object *json_nexthop, break; } break; - default: - break; } if (nexthop->vrf_id != re->vrf_id) @@ -2247,8 +2241,6 @@ static void show_ip_route_nht_dump(struct vty *vty, struct nexthop *nexthop, break; } break; - default: - break; } } |
