diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 1214c01a121d..d6988095e536 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -59,6 +59,7 @@ #include "isisd/isis_errors.h" #include "isisd/isis_tx_queue.h" #include "isisd/isis_nb.h" +#include "isisd/isis_ldp_sync.h" DEFINE_QOBJ_TYPE(isis_circuit) @@ -1280,6 +1281,7 @@ struct isis_circuit *isis_circuit_create(struct isis_area *area, isis_circuit_if_bind(circuit, ifp); if (circuit->area->mta && circuit->area->mta->status) isis_link_params_update(circuit, ifp); + return circuit; } @@ -1350,11 +1352,16 @@ ferr_r isis_circuit_metric_set(struct isis_circuit *circuit, int level, return ferr_cfg_invalid("metric %d too large for narrow metric", metric); - circuit->te_metric[level - 1] = metric; - circuit->metric[level - 1] = metric; - - if (circuit->area) - lsp_regenerate_schedule(circuit->area, level, 0); + /* inform ldp-sync of metric change + * if ldp-sync is running need to save metric + * and restore new values after ldp-sync completion. + */ + if (isis_ldp_sync_if_metric_config(circuit, level, metric)) { + circuit->te_metric[level - 1] = metric; + circuit->metric[level - 1] = metric; + if (circuit->area) + lsp_regenerate_schedule(circuit->area, level, 0); + } return ferr_ok(); } diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index da358f411b01..5766d1962f27 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -139,6 +139,7 @@ struct isis_circuit { uint8_t flags; bool disable_threeway_adj; struct bfd_info *bfd_info; + struct ldp_sync_info *ldp_sync_info; /* * Counters as in 10589--11.2.5.9 */ diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index 31fe41db82d3..f493c7ae9829 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -2386,6 +2386,178 @@ void cli_show_isis_log_adjacency(struct vty *vty, struct lyd_node *dnode, vty_out(vty, " log-adjacency-changes\n"); } +/* + * XPath: /frr-isisd:isis/instance/mpls/ldp-sync + */ +DEFPY(isis_mpls_ldp_sync, isis_mpls_ldp_sync_cmd, "mpls ldp-sync", + MPLS_STR MPLS_LDP_SYNC_STR) +{ + nb_cli_enqueue_change(vty, "./mpls/ldp-sync", NB_OP_CREATE, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(no_isis_mpls_ldp_sync, no_isis_mpls_ldp_sync_cmd, "no mpls ldp-sync", + NO_STR MPLS_STR NO_MPLS_LDP_SYNC_STR) +{ + nb_cli_enqueue_change(vty, "./mpls/ldp-sync", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_mpls_ldp_sync(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " mpls ldp-sync\n"); +} + +DEFPY(isis_mpls_ldp_sync_holddown, isis_mpls_ldp_sync_holddown_cmd, + "mpls ldp-sync holddown (0-10000)", + MPLS_STR MPLS_LDP_SYNC_STR + "Time to wait for LDP-SYNC to occur before restoring interface metric\n" + "Time in seconds\n") +{ + nb_cli_enqueue_change(vty, "./mpls/ldp-sync/holddown", NB_OP_MODIFY, + holddown_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(no_isis_mpls_ldp_sync_holddown, no_isis_mpls_ldp_sync_holddown_cmd, + "no mpls ldp-sync holddown [<(1-10000)>]", + NO_STR MPLS_STR MPLS_LDP_SYNC_STR NO_MPLS_LDP_SYNC_HOLDDOWN_STR) +{ + nb_cli_enqueue_change(vty, "./mpls/ldp-sync/holddown", NB_OP_DESTROY, + NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_mpls_ldp_sync_holddown(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " mpls ldp-sync holddown %s\n", + yang_dnode_get_string(dnode, NULL)); +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/mpls/ldp-sync + */ +DEFPY(isis_mpls_if_ldp_sync, isis_mpls_if_ldp_sync_cmd, + "[no] isis mpls ldp-sync", + NO_STR "IS-IS routing protocol\n" MPLS_STR MPLS_LDP_SYNC_STR) +{ + const struct lyd_node *dnode; + struct interface *ifp; + + dnode = yang_dnode_get(vty->candidate_config->dnode, + "%s/frr-isisd:isis", VTY_CURR_XPATH); + if (dnode == NULL) { + vty_out(vty, "ISIS is not enabled on this circuit\n"); + return CMD_SUCCESS; + } + + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync does not run on loopback interface\n"); + return CMD_SUCCESS; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_SUCCESS; + } + + nb_cli_enqueue_change(vty, "./frr-isisd:isis/mpls/ldp-sync", + NB_OP_MODIFY, no ? "false" : "true"); + + return nb_cli_apply_changes(vty, NULL); +} + + +void cli_show_isis_mpls_if_ldp_sync(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + if (!yang_dnode_get_bool(dnode, NULL)) + vty_out(vty, " no"); + + vty_out(vty, " isis mpls ldp-sync\n"); +} + +DEFPY(isis_mpls_if_ldp_sync_holddown, isis_mpls_if_ldp_sync_holddown_cmd, + "isis mpls ldp-sync holddown (0-10000)", + "IS-IS routing protocol\n" MPLS_STR MPLS_LDP_SYNC_STR + "Time to wait for LDP-SYNC to occur before restoring interface metric\n" + "Time in seconds\n") +{ + const struct lyd_node *dnode; + struct interface *ifp; + + dnode = yang_dnode_get(vty->candidate_config->dnode, + "%s/frr-isisd:isis", VTY_CURR_XPATH); + if (dnode == NULL) { + vty_out(vty, "ISIS is not enabled on this circuit\n"); + return CMD_SUCCESS; + } + + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync does not run on loopback interface\n"); + return CMD_SUCCESS; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_SUCCESS; + } + + nb_cli_enqueue_change(vty, "./frr-isisd:isis/mpls/holddown", + NB_OP_MODIFY, holddown_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(no_isis_mpls_if_ldp_sync_holddown, no_isis_mpls_if_ldp_sync_holddown_cmd, + "no isis mpls ldp-sync holddown [<(1-10000)>]", + NO_STR "IS-IS routing protocol\n" MPLS_STR NO_MPLS_LDP_SYNC_STR + NO_MPLS_LDP_SYNC_HOLDDOWN_STR) +{ + const struct lyd_node *dnode; + struct interface *ifp; + + dnode = yang_dnode_get(vty->candidate_config->dnode, + "%s/frr-isisd:isis", VTY_CURR_XPATH); + if (dnode == NULL) { + vty_out(vty, "ISIS is not enabled on this circuit\n"); + return CMD_SUCCESS; + } + + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync does not run on loopback interface\n"); + return CMD_SUCCESS; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_SUCCESS; + } + + nb_cli_enqueue_change(vty, "./frr-isisd:isis/mpls/holddown", + NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " isis mpls ldp-sync holddown %s\n", + yang_dnode_get_string(dnode, NULL)); +} + void isis_cli_init(void) { install_element(CONFIG_NODE, &router_isis_cmd); @@ -2489,6 +2661,14 @@ void isis_cli_init(void) install_element(INTERFACE_NODE, &no_isis_priority_cmd); install_element(ISIS_NODE, &log_adj_changes_cmd); + + install_element(ISIS_NODE, &isis_mpls_ldp_sync_cmd); + install_element(ISIS_NODE, &no_isis_mpls_ldp_sync_cmd); + install_element(ISIS_NODE, &isis_mpls_ldp_sync_holddown_cmd); + install_element(ISIS_NODE, &no_isis_mpls_ldp_sync_holddown_cmd); + install_element(INTERFACE_NODE, &isis_mpls_if_ldp_sync_cmd); + install_element(INTERFACE_NODE, &isis_mpls_if_ldp_sync_holddown_cmd); + install_element(INTERFACE_NODE, &no_isis_mpls_if_ldp_sync_holddown_cmd); } #endif /* ifndef FABRICD */ diff --git a/isisd/isis_ldp_sync.c b/isisd/isis_ldp_sync.c new file mode 100644 index 000000000000..42928e069afb --- /dev/null +++ b/isisd/isis_ldp_sync.c @@ -0,0 +1,788 @@ +/** + * isis_ldp_sync.c: ISIS LDP-IGP Sync handling routines + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "monotime.h" +#include "memory.h" +#include "thread.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "plist.h" +#include "log.h" +#include "zclient.h" +#include +#include "defaults.h" +#include "ldp_sync.h" + +#include "isisd/isis_constants.h" +#include "isisd/isis_common.h" +#include "isisd/isis_flags.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_pdu.h" +#include "isisd/isis_network.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_dr.h" +#include "isisd/isisd.h" +#include "isisd/isis_csm.h" +#include "isisd/isis_events.h" +#include "isisd/isis_te.h" +#include "isisd/isis_mt.h" +#include "isisd/isis_errors.h" +#include "isisd/isis_tx_queue.h" +#include "isisd/isis_nb.h" +#include "isisd/isis_ldp_sync.h" + +extern struct zclient *zclient; + +/* + * LDP-SYNC msg between IGP and LDP + */ +int isis_ldp_sync_state_update(struct ldp_igp_sync_if_state state) +{ + struct interface *ifp; + struct isis_circuit *circuit = NULL; + struct isis_area *area; + struct listnode *node; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* if isis is not enabled or LDP-SYNC is not configured ignore */ + if (!isis || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + /* lookup circuit */ + ifp = if_lookup_by_index(state.ifindex, VRF_DEFAULT); + if (ifp == NULL) + return 0; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + circuit = circuit_lookup_by_ifp(ifp, area->circuit_list); + if (circuit != NULL) + break; + } + + /* if isis is not enabled or LDP-SYNC is not configured ignore */ + if (circuit == NULL || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + /* received ldp-sync interface state from LDP */ + ils_debug("ldp_sync: rcvd %s from LDP if %s", + state.sync_start ? "sync-start" : "sync-complete", ifp->name); + if (state.sync_start) + isis_ldp_sync_if_start(circuit, false); + else + isis_ldp_sync_if_complete(circuit); + + return 0; +} + +int isis_ldp_sync_announce_update(struct ldp_igp_sync_announce announce) +{ + struct isis_area *area; + struct listnode *node; + struct vrf *vrf; + struct interface *ifp; + struct isis_circuit *circuit; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* if isis is not enabled or LDP-SYNC is not configured ignore */ + if (!isis || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + if (announce.proto != ZEBRA_ROUTE_LDP) + return 0; + + ils_debug("ldp_sync: rcvd announce from LDP"); + + /* LDP just started up: + * set cost to LSInfinity + * send request to LDP for LDP-SYNC state for each interface + * start hello timer + */ + vrf = vrf_lookup_by_id(VRF_DEFAULT); + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + circuit = circuit_lookup_by_ifp(ifp, + area->circuit_list); + if (circuit == NULL) + continue; + isis_ldp_sync_if_start(circuit, true); + } + } + + THREAD_TIMER_OFF(isis->ldp_sync_cmd.t_hello); + isis->ldp_sync_cmd.t_hello = NULL; + isis->ldp_sync_cmd.sequence = 0; + isis_ldp_sync_hello_timer_add(); + + return 0; +} + +int isis_ldp_sync_hello_update(struct ldp_igp_sync_hello hello) +{ + struct isis_area *area; + struct listnode *node; + struct vrf *vrf; + struct interface *ifp; + struct isis_circuit *circuit; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* if isis is not enabled or LDP-SYNC is not configured ignore */ + if (!isis || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + if (hello.proto != ZEBRA_ROUTE_LDP) + return 0; + + /* Received Hello from LDP: + * if current sequence number is greater than received hello + * sequence number then assume LDP restarted + * set cost to LSInfinity + * send request to LDP for LDP-SYNC state for each interface + * else all is fine just restart hello timer + */ + if (hello.sequence == 0) + /* rolled over */ + isis->ldp_sync_cmd.sequence = 0; + + if (isis->ldp_sync_cmd.sequence > hello.sequence) { + zlog_err("ldp_sync: LDP restarted"); + + vrf = vrf_lookup_by_id(VRF_DEFAULT); + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + circuit = circuit_lookup_by_ifp(ifp, + area->circuit_list); + if (circuit == NULL) + continue; + isis_ldp_sync_if_start(circuit, true); + } + } + } else { + THREAD_TIMER_OFF(isis->ldp_sync_cmd.t_hello); + isis_ldp_sync_hello_timer_add(); + } + isis->ldp_sync_cmd.sequence = hello.sequence; + + return 0; +} + +void isis_ldp_sync_state_req_msg(struct isis_circuit *circuit) +{ + struct ldp_igp_sync_if_state_req request; + struct interface *ifp = circuit->interface; + + ils_debug("ldp_sync: send state request to LDP for %s", + ifp->name); + + strlcpy(request.name, ifp->name, sizeof(ifp->name)); + request.proto = LDP_IGP_SYNC_IF_STATE_REQUEST; + request.ifindex = ifp->ifindex; + + zclient_send_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST, + (uint8_t *)&request, sizeof(request)); +} + +/* + * LDP-SYNC general interface routines + */ +void isis_ldp_sync_if_init(struct isis_circuit *circuit, struct isis *isis) +{ + struct ldp_sync_info *ldp_sync_info; + struct interface *ifp = circuit->interface; + + /* called when ISIS is configured on an interface + * if LDP-IGP Sync is configured globally set state + * and if ptop interface LDP LDP-SYNC is enabled + */ + ils_debug("ldp_sync: init if %s ", ifp->name); + if (circuit->ldp_sync_info == NULL) + circuit->ldp_sync_info = ldp_sync_info_create(); + ldp_sync_info = circuit->ldp_sync_info; + + /* specifed on interface overrides global config. */ + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = isis->ldp_sync_cmd.holddown; + + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + + if ((circuit->circ_type == CIRCUIT_T_P2P || if_is_pointopoint(ifp)) && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; +} + +void isis_ldp_sync_if_start(struct isis_circuit *circuit, + bool send_state_req) +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = circuit->ldp_sync_info; + + /* Start LDP-SYNC on this interface: + * set cost of interface to LSInfinity so traffic will use different + * interface until LDP has learned all labels from peer + * start holddown timer if configured + * send msg to LDP to get LDP-SYNC state + */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { + ils_debug("ldp_sync: start on if %s state: %s", + circuit->interface->name, "Holding down until Sync"); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + isis_ldp_sync_set_if_metric(circuit, true); + isis_ldp_sync_holddown_timer_add(circuit); + + if (send_state_req) + isis_ldp_sync_state_req_msg(circuit); + } +} + +void isis_ldp_sync_if_complete(struct isis_circuit *circuit) +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = circuit->ldp_sync_info; + + /* received sync-complete from LDP: + * set state to up + * stop timer + * restore interface cost to original value + */ + if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) { + if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP) + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + isis_ldp_sync_set_if_metric(circuit, true); + } +} + +void isis_ldp_sync_ldp_fail(struct isis_circuit *circuit) +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = circuit->ldp_sync_info; + + /* LDP failed to send hello: + * stop holddown timer + * set cost of interface to LSInfinity so traffic will use different + * interface until LDP restarts and has learned all labels from peer + */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { + if (ldp_sync_info->t_holddown != NULL) { + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + } + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + isis_ldp_sync_set_if_metric(circuit, true); + } +} + +void isis_ldp_sync_if_remove(struct isis_circuit *circuit, bool remove) +{ + struct ldp_sync_info *ldp_sync_info; + + if (circuit->ldp_sync_info == NULL) + return; + + ldp_sync_info = circuit->ldp_sync_info; + + /* Stop LDP-SYNC on this interface: + * if holddown timer is running stop it + * delete ldp instance on interface + * restore metric + */ + ils_debug("ldp_sync: remove if %s", circuit->interface + ? circuit->interface->name : ""); + + if (ldp_sync_info->t_holddown) + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + isis_ldp_sync_set_if_metric(circuit, true); + if (remove) { + /* ISIS instance being removed free ldp-sync info */ + ldp_sync_info_free((struct ldp_sync_info **)&(ldp_sync_info)); + circuit->ldp_sync_info = NULL; + } +} + +static int isis_ldp_sync_adj_state_change(struct isis_adjacency *adj) +{ + struct isis_circuit *circuit = adj->circuit; + struct ldp_sync_info *ldp_sync_info; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (!isis || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE) || + circuit->interface->vrf_id != VRF_DEFAULT || + if_is_loopback(circuit->interface)) + return 0; + + if (circuit->ldp_sync_info == NULL) + isis_ldp_sync_if_init(circuit, isis); + ldp_sync_info = circuit->ldp_sync_info; + + if (ldp_sync_info->enabled != LDP_IGP_SYNC_ENABLED) + return 0; + + if (adj->adj_state == ISIS_ADJ_UP) { + if (circuit->circ_type == CIRCUIT_T_P2P || + if_is_pointopoint(circuit->interface)) { + /* If LDP-SYNC is configure on interface then start */ + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + isis_ldp_sync_if_start(circuit, true); + } else { + /* non ptop link so don't run ldp-sync */ + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + isis_ldp_sync_set_if_metric(circuit, true); + } + } else { + /* If LDP-SYNC is configure on this interface then stop it */ + if (circuit->circ_type == CIRCUIT_T_P2P || + if_is_pointopoint(circuit->interface)) + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + else + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + + ils_debug("ldp_sync: down on if %s", circuit->interface->name); + ldp_sync_if_down(circuit->ldp_sync_info); + } + + return 0; +} + +bool isis_ldp_sync_if_metric_config(struct isis_circuit *circuit, int level, + int metric) +{ + struct ldp_sync_info *ldp_sync_info = circuit->ldp_sync_info; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* configured interface metric has been changed: + * if LDP-IGP Sync is running and metric has been set to LSInfinity + * change saved value so when ldp-sync completes proper metric is + * restored + */ + if (isis && + CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE) && + ldp_sync_info != NULL) { + + if (CHECK_FLAG(ldp_sync_info->flags, + LDP_SYNC_FLAG_SET_METRIC)) { + ldp_sync_info->metric[level-1] = metric; + ldp_sync_info->metric[level-1] = metric; + return false; + } + } + return true; +} + +void isis_ldp_sync_set_if_metric(struct isis_circuit *circuit, bool run_regen) +{ + struct ldp_sync_info *ldp_sync_info; + + /* set interface metric: + * if LDP-IGP Sync is starting set metric so interface + * is used only as last resort + * else restore metric to original value + */ + if (circuit->ldp_sync_info == NULL || circuit->area == NULL) + return; + + ldp_sync_info = circuit->ldp_sync_info; + if (ldp_sync_if_is_enabled(ldp_sync_info)) { + /* if metric already set to LSInfinity just return */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_SET_METRIC)) + return; + + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_SET_METRIC); + if (circuit->is_type & IS_LEVEL_1) { + if (circuit->area->newmetric) { + ldp_sync_info->metric[0] = + circuit->te_metric[0]; + circuit->te_metric[0] = LDP_ISIS_LSINFINITY; + } else { + ldp_sync_info->metric[0] = circuit->metric[0]; + circuit->metric[0] = LDP_ISIS_LSINFINITY_NL; + } + } + if (circuit->is_type & IS_LEVEL_2) { + if (circuit->area->newmetric) { + ldp_sync_info->metric[1] = + circuit->te_metric[1]; + circuit->te_metric[1] = LDP_ISIS_LSINFINITY; + } else { + ldp_sync_info->metric[1] = circuit->metric[1]; + circuit->metric[1] = LDP_ISIS_LSINFINITY_NL; + } + } + } else { + /* if metric already restored just return */ + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_SET_METRIC)) + return; + + UNSET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_SET_METRIC); + if (circuit->is_type & IS_LEVEL_1) { + circuit->te_metric[0] = ldp_sync_info->metric[0]; + circuit->metric[0] = ldp_sync_info->metric[0]; + } + if (circuit->is_type & IS_LEVEL_2) { + circuit->te_metric[1] = ldp_sync_info->metric[1]; + circuit->metric[1] = ldp_sync_info->metric[1]; + } + } + + if (run_regen) + lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); +} + + +/* + * LDP-SYNC holddown timer routines + */ +static int isis_ldp_sync_holddown_timer(struct thread *thread) +{ + struct isis_circuit *circuit; + struct ldp_sync_info *ldp_sync_info; + + /* holddown timer expired: + * didn't receive msg from LDP indicating sync-complete + * restore interface cost to original value + */ + circuit = THREAD_ARG(thread); + if (circuit->ldp_sync_info == NULL) + return 0; + + ldp_sync_info = circuit->ldp_sync_info; + + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; + ldp_sync_info->t_holddown = NULL; + + ils_debug("ldp_sync: holddown timer expired for %s state:sync achieved", + circuit->interface->name); + + isis_ldp_sync_set_if_metric(circuit, true); + return 0; +} + +void isis_ldp_sync_holddown_timer_add(struct isis_circuit *circuit) +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = circuit->ldp_sync_info; + + /* Start holddown timer: + * this timer is used to keep interface cost at LSInfinity + * once expires returns cost to original value + * if timer is already running or holddown time is off just return + */ + if (ldp_sync_info->t_holddown || + ldp_sync_info->holddown == LDP_IGP_SYNC_HOLDDOWN_DEFAULT) + return; + + ils_debug("ldp_sync: start holddown timer for %s time %d", + circuit->interface->name, ldp_sync_info->holddown); + + thread_add_timer(master, isis_ldp_sync_holddown_timer, + circuit, ldp_sync_info->holddown, + &ldp_sync_info->t_holddown); +} + +/* + * LDP-SYNC hello timer routines + */ +static int isis_ldp_sync_hello_timer(struct thread *thread) +{ + struct isis_area *area; + struct listnode *node; + struct isis_circuit *circuit; + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (!isis) + return 0; + + /* hello timer expired: + * didn't receive hello msg from LDP + * set cost of all interfaces to LSInfinity + */ + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + circuit = circuit_lookup_by_ifp(ifp, + area->circuit_list); + if (circuit == NULL) + continue; + + isis_ldp_sync_ldp_fail(circuit); + } + } + + zlog_debug("ldp_sync: hello timer expired, LDP down"); + + return 0; +} + +void isis_ldp_sync_hello_timer_add(void) +{ + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* Start hello timer: + * this timer is used to make sure LDP is up + * if expires set interface cost to LSInfinity + */ + if (!isis || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return; + + thread_add_timer(master, isis_ldp_sync_hello_timer, + NULL, LDP_IGP_SYNC_HELLO_TIMEOUT, + &isis->ldp_sync_cmd.t_hello); +} + +/* + * LDP-SYNC routes used by set commands. + */ + +void isis_if_set_ldp_sync_enable(struct isis_circuit *circuit) +{ + struct ldp_sync_info *ldp_sync_info; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* called when setting LDP-SYNC at the global level: + * specifed on interface overrides global config + * if ptop link send msg to LDP indicating ldp-sync enabled + */ + if (!isis || if_is_loopback(circuit->interface)) + return; + + if (CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + if (circuit->ldp_sync_info == NULL) + isis_ldp_sync_if_init(circuit, isis); + ldp_sync_info = circuit->ldp_sync_info; + + /* config on interface, overrides global config. */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + if (ldp_sync_info->enabled != LDP_IGP_SYNC_ENABLED) + return; + + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + ils_debug("ldp_sync: enable if %s", circuit->interface->name); + + /* send message to LDP if ptop link */ + if (circuit->circ_type == CIRCUIT_T_P2P || + if_is_pointopoint(circuit->interface)) { + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + isis_ldp_sync_state_req_msg(circuit); + } else { + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + zlog_debug("ldp_sync: Sync only runs on P2P links %s", + circuit->interface->name); + } + } else + /* delete LDP sync even if configured on an interface */ + isis_ldp_sync_if_remove(circuit, false); +} + +void isis_if_set_ldp_sync_holddown(struct isis_circuit *circuit) +{ + struct ldp_sync_info *ldp_sync_info; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* called when setting LDP-SYNC at the global level: + * specifed on interface overrides global config. + */ + if (!isis || if_is_loopback(circuit->interface)) + return; + + if (circuit->ldp_sync_info == NULL) + isis_ldp_sync_if_init(circuit, isis); + ldp_sync_info = circuit->ldp_sync_info; + + /* config on interface, overrides global config. */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + return; + if (CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = isis->ldp_sync_cmd.holddown; + else + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; +} + +void isis_ldp_sync_gbl_exit(bool remove) +{ + struct isis_area *area; + struct listnode *node; + struct isis_circuit *circuit; + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* if you delete LDP-SYNC at a gobal level is clears all LDP-SYNC + * configuration, even interface configuration + */ + if (isis && + CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + /* register with opaque client to recv LDP-IGP Sync msgs */ + zclient_unregister_opaque(zclient, + LDP_IGP_SYNC_IF_STATE_UPDATE); + zclient_unregister_opaque(zclient, + LDP_IGP_SYNC_ANNOUNCE_UPDATE); + zclient_unregister_opaque(zclient, LDP_IGP_SYNC_HELLO_UPDATE); + + /* disable LDP-SYNC globally */ + UNSET_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE); + UNSET_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN); + isis->ldp_sync_cmd.holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + THREAD_TIMER_OFF(isis->ldp_sync_cmd.t_hello); + isis->ldp_sync_cmd.t_hello = NULL; + + /* remove LDP-SYNC on all ISIS interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + circuit = circuit_lookup_by_ifp(ifp, + area->circuit_list); + if (circuit == NULL) + continue; + isis_ldp_sync_if_remove(circuit, remove); + } + } + } +} + +/* + * LDP-SYNC routines used by show commands. + */ + +static void isis_circuit_ldp_sync_print_vty(struct isis_circuit *circuit, + struct vty *vty) +{ + struct ldp_sync_info *ldp_sync_info; + const char *ldp_state; + + if (circuit->ldp_sync_info == NULL || + if_is_loopback(circuit->interface)) + return; + + ldp_sync_info = circuit->ldp_sync_info; + vty_out(vty, "%-10s\n", circuit->interface->name); + vty_out(vty, " LDP-IGP Synchronization enabled: %s\n", + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED + ? "yes" + : "no"); + vty_out(vty, " holddown timer in seconds: %u\n", + ldp_sync_info->holddown); + + switch (ldp_sync_info->state) { + case LDP_IGP_SYNC_STATE_REQUIRED_UP: + vty_out(vty, " State: Sync achieved\n"); + break; + case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP: + if (ldp_sync_info->t_holddown != NULL) { + struct timeval remain = thread_timer_remain( + ldp_sync_info->t_holddown); + vty_out(vty, + " Holddown timer is running %lld.%03lld remaining\n", + (long long)remain.tv_sec, + (long long)remain.tv_usec/1000); + + vty_out(vty, " State: Holding down until Sync\n"); + } else + vty_out(vty, " State: Sync not achieved\n"); + break; + case LDP_IGP_SYNC_STATE_NOT_REQUIRED: + default: + if ((circuit->circ_type != CIRCUIT_T_P2P && + !if_is_pointopoint(circuit->interface)) && + circuit->circ_type != CIRCUIT_T_UNKNOWN) + ldp_state = "Sync not required: non-p2p link"; + else + ldp_state = "Sync not required"; + vty_out(vty, " State: %s\n", ldp_state); + break; + } +} + +DEFUN (show_isis_mpls_ldp_interface, + show_isis_mpls_ldp_interface_cmd, + "show " PROTO_NAME " mpls ldp-sync [interface ]", + SHOW_STR + PROTO_HELP + MPLS_STR + "LDP-IGP Sync information\n" + "Interface name\n") +{ + char *ifname = NULL; + int idx_intf = 0; + struct listnode *anode, *cnode; + struct isis_area *area; + struct isis_circuit *circuit; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (!isis) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + if (!CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + vty_out(vty, "LDP-sync is disabled\n"); + return CMD_SUCCESS; + } + + if (argv_find(argv, argc, "INTERFACE", &idx_intf)) + ifname = argv[idx_intf]->arg; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) + if (!ifname) + isis_circuit_ldp_sync_print_vty(circuit, vty); + else if (strcmp(circuit->interface->name, ifname) == 0) + isis_circuit_ldp_sync_print_vty(circuit, vty); + } + + return CMD_SUCCESS; +} + +void isis_ldp_sync_init(void) +{ + + /* "show ip isis mpls ldp interface" commands. */ + install_element(VIEW_NODE, &show_isis_mpls_ldp_interface_cmd); + + /* register for adjacency state changes */ + hook_register(isis_adj_state_change_hook, + isis_ldp_sync_adj_state_change); +} diff --git a/isisd/isis_ldp_sync.h b/isisd/isis_ldp_sync.h new file mode 100644 index 000000000000..6017cdf00186 --- /dev/null +++ b/isisd/isis_ldp_sync.h @@ -0,0 +1,54 @@ +/* + * isis_ldp_sync.h: ISIS LDP-IGP Sync handling routines + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ZEBRA_ISIS_LDP_SYNC_H +#define _ZEBRA_ISIS_LDP_SYNC_H + +#define LDP_ISIS_LSINFINITY 0xFFFFFE /* wide link metric */ +#define LDP_ISIS_LSINFINITY_NL 62 /* narrow link metric */ + +/* Macro to log debug message */ +#define ils_debug(...) \ + do { \ + if (IS_DEBUG_LDP_SYNC) \ + zlog_debug(__VA_ARGS__); \ + } while (0) + +extern void isis_if_set_ldp_sync_enable(struct isis_circuit *circuit); +extern void isis_if_set_ldp_sync_holddown(struct isis_circuit *circuit); +extern void isis_ldp_sync_if_init(struct isis_circuit *circuit, + struct isis *isis); +extern void isis_ldp_sync_if_start(struct isis_circuit *circuit, + bool send_state_req); +extern void isis_ldp_sync_if_remove(struct isis_circuit *circuit, bool remove); +extern void isis_ldp_sync_if_complete(struct isis_circuit *circuit); +extern void isis_ldp_sync_holddown_timer_add(struct isis_circuit *circuit); +extern void isis_ldp_sync_hello_timer_add(void); +extern void isis_ldp_sync_ldp_fail(struct isis_circuit *circuit); +extern int isis_ldp_sync_state_update(struct ldp_igp_sync_if_state state); +extern int isis_ldp_sync_announce_update(struct ldp_igp_sync_announce announce); +extern int isis_ldp_sync_hello_update(struct ldp_igp_sync_hello hello); +extern void isis_ldp_sync_state_req_msg(struct isis_circuit *circuit); +extern void isis_ldp_sync_set_if_metric(struct isis_circuit *circuit, + bool run_regen); +extern bool isis_ldp_sync_if_metric_config(struct isis_circuit *circuit, + int level, int metric); +extern void isis_ldp_sync_init(void); +extern void isis_ldp_sync_gbl_exit(bool remove); +#endif /* _ZEBRA_ISIS_LDP_SYNC_H */ diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 26f5227aaede..22df3ff37e4d 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -58,6 +58,7 @@ #include "isisd/isis_mt.h" #include "isisd/fabricd.h" #include "isisd/isis_nb.h" +#include "isisd/isis_ldp_sync.h" /* Default configuration file name */ #define ISISD_DEFAULT_CONFIG "isisd.conf" @@ -264,6 +265,7 @@ int main(int argc, char **argv, char **envp) isis_zebra_init(master, instance); isis_bfd_init(); + isis_ldp_sync_init(); fabricd_init(); frr_config_fork(); diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c index 33b0b4d02c85..14ea1170c4ed 100644 --- a/isisd/isis_nb.c +++ b/isisd/isis_nb.c @@ -537,6 +537,21 @@ const struct frr_yang_module_info frr_isisd_info = { .modify = isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_modify, }, }, + { + .xpath = "/frr-isisd:isis/instance/mpls/ldp-sync", + .cbs = { + .cli_show = cli_show_isis_mpls_ldp_sync, + .create = isis_instance_mpls_ldp_sync_create, + .destroy = isis_instance_mpls_ldp_sync_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/mpls/ldp-sync/holddown", + .cbs = { + .cli_show = cli_show_isis_mpls_ldp_sync_holddown, + .modify = isis_instance_mpls_ldp_sync_holddown_modify, + }, + }, { .xpath = "/frr-interface:lib/interface/frr-isisd:isis", .cbs = { @@ -905,6 +920,21 @@ const struct frr_yang_module_info frr_isisd_info = { .get_elem = lib_interface_state_isis_event_counters_authentication_fails_get_elem, } }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/mpls/ldp-sync", + .cbs = { + .cli_show = cli_show_isis_mpls_if_ldp_sync, + .modify = lib_interface_isis_mpls_ldp_sync_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/mpls/holddown", + .cbs = { + .cli_show = cli_show_isis_mpls_if_ldp_sync_holddown, + .modify = lib_interface_isis_mpls_holddown_modify, + .destroy = lib_interface_isis_mpls_holddown_destroy, + } + }, { .xpath = NULL, }, diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h index a79cb8ff57cd..8a6d24b84585 100644 --- a/isisd/isis_nb.h +++ b/isisd/isis_nb.h @@ -206,6 +206,9 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_modify( struct nb_cb_modify_args *args); int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_modify( struct nb_cb_modify_args *args); +int isis_instance_mpls_ldp_sync_destroy(struct nb_cb_destroy_args *args); +int isis_instance_mpls_ldp_sync_create(struct nb_cb_create_args *args); +int isis_instance_mpls_ldp_sync_holddown_modify(struct nb_cb_modify_args *args); int lib_interface_isis_csnp_interval_level_1_modify( struct nb_cb_modify_args *args); int lib_interface_isis_csnp_interval_level_2_modify( @@ -250,6 +253,9 @@ int lib_interface_isis_multi_topology_ipv6_management_modify( struct nb_cb_modify_args *args); int lib_interface_isis_multi_topology_ipv6_dstsrc_modify( struct nb_cb_modify_args *args); +int lib_interface_isis_mpls_ldp_sync_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_mpls_holddown_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_mpls_holddown_destroy(struct nb_cb_destroy_args *args); struct yang_data * lib_interface_state_isis_get_elem(struct nb_cb_get_elem_args *args); const void *lib_interface_state_isis_adjacencies_adjacency_get_next( @@ -434,6 +440,16 @@ void cli_show_ip_isis_priority(struct vty *vty, struct lyd_node *dnode, bool show_defaults); void cli_show_isis_log_adjacency(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_mpls_ldp_sync(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mpls_ldp_sync_holddown(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mpls_if_ldp_sync(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); /* Notifications. */ void isis_notif_db_overload(const struct isis_area *area, bool overload); diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 170fe92c2821..0988fe857840 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -29,6 +29,8 @@ #include "spf_backoff.h" #include "lib_errors.h" #include "vrf.h" +#include "zclient.h" +#include "ldp_sync.h" #include "isisd/isisd.h" #include "isisd/isis_nb.h" @@ -45,6 +47,9 @@ #include "isisd/isis_memory.h" #include "isisd/isis_mt.h" #include "isisd/isis_redist.h" +#include "isisd/isis_ldp_sync.h" + +extern struct zclient *zclient; /* * XPath: /frr-isisd:isis/instance @@ -81,6 +86,10 @@ int isis_instance_destroy(struct nb_cb_destroy_args *args) area = nb_running_unset_entry(args->dnode); isis_area_destroy(area); + /* remove ldp-sync config */ + if (area->isis->vrf_id == VRF_DEFAULT) + isis_ldp_sync_gbl_exit(true); + return NB_OK; } @@ -1825,6 +1834,113 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_mo return NB_OK; } +/* + * XPath: /frr-isisd:isis/instance/mpls/ldp-sync + */ +int isis_instance_mpls_ldp_sync_create(struct nb_cb_create_args *args) +{ + struct isis_area *area; + struct listnode *node; + struct isis_circuit *circuit; + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + switch (args->event) { + case NB_EV_VALIDATE: + if (isis == NULL) + return NB_ERR_VALIDATION; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* register with opaque client to recv LDP-IGP Sync msgs */ + zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_UPDATE); + zclient_register_opaque(zclient, LDP_IGP_SYNC_ANNOUNCE_UPDATE); + zclient_register_opaque(zclient, LDP_IGP_SYNC_HELLO_UPDATE); + + if (!CHECK_FLAG(isis->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_ENABLE)) { + SET_FLAG(isis->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_ENABLE); + + /* turn on LDP-IGP Sync on all ptop ISIS interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + circuit = circuit_lookup_by_ifp( + ifp, area->circuit_list); + if (circuit == NULL) + continue; + isis_if_set_ldp_sync_enable(circuit); + } + } + } + break; + } + return NB_OK; +} + +int isis_instance_mpls_ldp_sync_destroy(struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* remove ldp-sync config */ + isis_ldp_sync_gbl_exit(false); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/mpls/ldp-sync/holddown + */ +int isis_instance_mpls_ldp_sync_holddown_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + struct listnode *node; + struct isis_circuit *circuit; + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + uint16_t holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + switch (args->event) { + case NB_EV_VALIDATE: + if (isis == NULL) + return NB_ERR_VALIDATION; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + holddown = yang_dnode_get_uint16(args->dnode, NULL); + + if (holddown == LDP_IGP_SYNC_HOLDDOWN_DEFAULT) + UNSET_FLAG(isis->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_HOLDDOWN); + else + SET_FLAG(isis->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_HOLDDOWN); + isis->ldp_sync_cmd.holddown = holddown; + + /* set holddown time on all ISIS interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + circuit = circuit_lookup_by_ifp(ifp, + area->circuit_list); + if (circuit == NULL) + continue; + isis_if_set_ldp_sync_holddown(circuit); + } + } + break; + } + return NB_OK; +} + /* * XPath: /frr-interface:lib/interface/frr-isisd:isis */ @@ -1915,6 +2031,9 @@ int lib_interface_isis_destroy(struct nb_cb_destroy_args *args) if (!circuit) return NB_ERR_INCONSISTENCY; + /* remove ldp-sync config */ + isis_ldp_sync_if_remove(circuit, true); + /* disable both AFs for this circuit. this will also update the * CSM state by sending an ISIS_DISABLED signal. If there is no * area associated to the circuit there is nothing to do @@ -2646,3 +2765,130 @@ int lib_interface_isis_multi_topology_ipv6_dstsrc_modify( args->event, args->dnode, args->errmsg, args->errmsg_len, ISIS_MT_IPV6_DSTSRC); } + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/mpls/ldp-sync + */ +int lib_interface_isis_mpls_ldp_sync_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + struct ldp_sync_info *ldp_sync_info; + bool ldp_sync_enable; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + switch (args->event) { + case NB_EV_VALIDATE: + if (isis == NULL) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + circuit = nb_running_get_entry(args->dnode, NULL, true); + ldp_sync_enable = yang_dnode_get_bool(args->dnode, NULL); + + if (circuit->ldp_sync_info == NULL) + isis_ldp_sync_if_init(circuit, isis); + ldp_sync_info = circuit->ldp_sync_info; + + if (ldp_sync_enable) { + /* enable LDP-SYNC on an interface + * if ptop interface send message to LDP to get state + */ + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG); + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + if (circuit->circ_type == CIRCUIT_T_P2P) { + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + isis_ldp_sync_state_req_msg(circuit); + } else { + zlog_debug("ldp_sync: only runs on P2P links %s", + circuit->interface->name); + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_NOT_REQUIRED; + } + } else { + /* disable LDP-SYNC on an interface + * stop holddown timer if running + * restore isis metric + */ + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG); + ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + isis_ldp_sync_set_if_metric(circuit, true); + } + break; + } + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/mpls/holddown + */ +int lib_interface_isis_mpls_holddown_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + struct ldp_sync_info *ldp_sync_info; + uint16_t holddown; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + switch (args->event) { + case NB_EV_VALIDATE: + if (isis == NULL) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + circuit = nb_running_get_entry(args->dnode, NULL, true); + holddown = yang_dnode_get_uint16(args->dnode, NULL); + + if (circuit->ldp_sync_info == NULL) + isis_ldp_sync_if_init(circuit, isis); + ldp_sync_info = circuit->ldp_sync_info; + + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN); + ldp_sync_info->holddown = holddown; + break; + } + return NB_OK; +} + +int lib_interface_isis_mpls_holddown_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_circuit *circuit; + struct ldp_sync_info *ldp_sync_info; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + switch (args->event) { + case NB_EV_VALIDATE: + if (isis == NULL) + return NB_ERR_VALIDATION; + circuit = nb_running_get_entry(args->dnode, NULL, true); + if (circuit->ldp_sync_info == NULL) + return NB_ERR_VALIDATION; + + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + circuit = nb_running_get_entry(args->dnode, NULL, true); + ldp_sync_info = circuit->ldp_sync_info; + UNSET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN); + + if (CHECK_FLAG(isis->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = isis->ldp_sync_cmd.holddown; + else + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + break; + } + return NB_OK; +} diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index a50eb607d9fb..15b51589ae3d 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -52,6 +52,7 @@ #include "isisd/isis_adjacency.h" #include "isisd/isis_te.h" #include "isisd/isis_sr.h" +#include "isisd/isis_ldp_sync.h" struct zclient *zclient; static struct zclient *zclient_sync; @@ -596,6 +597,44 @@ static void isis_zebra_connected(struct zclient *zclient) zclient_send_reg_requests(zclient, VRF_DEFAULT); } +/* + * opaque messages between processes + */ +static int isis_opaque_msg_handler(ZAPI_CALLBACK_ARGS) +{ + struct stream *s; + struct zapi_opaque_msg info; + struct ldp_igp_sync_if_state state; + struct ldp_igp_sync_announce announce; + struct ldp_igp_sync_hello hello; + int ret = 0; + + s = zclient->ibuf; + if (zclient_opaque_decode(s, &info) != 0) + return -1; + + switch (info.type) { + case LDP_IGP_SYNC_IF_STATE_UPDATE: + STREAM_GET(&state, s, sizeof(state)); + ret = isis_ldp_sync_state_update(state); + break; + case LDP_IGP_SYNC_ANNOUNCE_UPDATE: + STREAM_GET(&announce, s, sizeof(announce)); + ret = isis_ldp_sync_announce_update(announce); + break; + case LDP_IGP_SYNC_HELLO_UPDATE: + STREAM_GET(&hello, s, sizeof(hello)); + ret = isis_ldp_sync_hello_update(hello); + break; + default: + break; + } + +stream_failure: + + return ret; +} + void isis_zebra_init(struct thread_master *master, int instance) { /* Initialize asynchronous zclient. */ @@ -622,6 +661,8 @@ void isis_zebra_init(struct thread_master *master, int instance) */ zclient_sync->session_id = 1; zclient_sync->privs = &isisd_privs; + + zclient->opaque_msg_handler = isis_opaque_msg_handler; } void isis_zebra_stop(void) diff --git a/isisd/isisd.c b/isisd/isisd.c index 2a2c71b1fd55..7bba783b39ce 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -76,6 +76,7 @@ unsigned long debug_flooding; unsigned long debug_bfd; unsigned long debug_tx_queue; unsigned long debug_sr; +unsigned long debug_ldp_sync; DEFINE_QOBJ_TYPE(isis_area) @@ -1207,6 +1208,8 @@ void print_debug(struct vty *vty, int flags, int onoff) vty_out(vty, "IS-IS Flooding debugging is %s\n", onoffs); if (flags & DEBUG_BFD) vty_out(vty, "IS-IS BFD debugging is %s\n", onoffs); + if (flags & DEBUG_LDP_SYNC) + vty_out(vty, "IS-IS ldp-sync debugging is %s\n", onoffs); } DEFUN_NOSH (show_debugging, @@ -1244,6 +1247,8 @@ DEFUN_NOSH (show_debugging, print_debug(vty, DEBUG_FLOODING, 1); if (IS_DEBUG_BFD) print_debug(vty, DEBUG_BFD, 1); + if (IS_DEBUG_LDP_SYNC) + print_debug(vty, DEBUG_LDP_SYNC, 1); return CMD_SUCCESS; } @@ -1312,6 +1317,10 @@ static int config_write_debug(struct vty *vty) vty_out(vty, "debug " PROTO_NAME " bfd\n"); write++; } + if (IS_DEBUG_LDP_SYNC) { + vty_out(vty, "debug " PROTO_NAME " ldp-sync\n"); + write++; + } write += spf_backoff_write_config(vty); return write; @@ -1668,11 +1677,32 @@ DEFUN (no_debug_isis_bfd, return CMD_SUCCESS; } -DEFUN(show_hostname, show_hostname_cmd, - "show " PROTO_NAME " [vrf ] hostname", - SHOW_STR PROTO_HELP VRF_CMD_HELP_STR - "All VRFs\n" - "IS-IS Dynamic hostname mapping\n") +DEFUN(debug_isis_ldp_sync, debug_isis_ldp_sync_cmd, + "debug " PROTO_NAME " ldp-sync", + DEBUG_STR PROTO_HELP PROTO_NAME " interaction with LDP-Sync\n") +{ + debug_ldp_sync |= DEBUG_LDP_SYNC; + print_debug(vty, DEBUG_LDP_SYNC, 1); + + return CMD_SUCCESS; +} + +DEFUN(no_debug_isis_ldp_sync, no_debug_isis_ldp_sync_cmd, + "no debug " PROTO_NAME " ldp-sync", + NO_STR UNDEBUG_STR PROTO_HELP PROTO_NAME " interaction with LDP-Sync\n") +{ + debug_ldp_sync &= ~DEBUG_LDP_SYNC; + print_debug(vty, DEBUG_LDP_SYNC, 0); + + return CMD_SUCCESS; +} + +DEFUN (show_hostname, + show_hostname_cmd, + "show " PROTO_NAME " hostname", + SHOW_STR + PROTO_HELP + "IS-IS Dynamic hostname mapping\n") { struct listnode *node; const char *vrf_name = VRF_DEFAULT_NAME; @@ -2830,6 +2860,8 @@ void isis_init(void) install_element(ENABLE_NODE, &no_debug_isis_lsp_sched_cmd); install_element(ENABLE_NODE, &debug_isis_bfd_cmd); install_element(ENABLE_NODE, &no_debug_isis_bfd_cmd); + install_element(ENABLE_NODE, &debug_isis_ldp_sync_cmd); + install_element(ENABLE_NODE, &no_debug_isis_ldp_sync_cmd); install_element(CONFIG_NODE, &debug_isis_adj_cmd); install_element(CONFIG_NODE, &no_debug_isis_adj_cmd); @@ -2857,6 +2889,8 @@ void isis_init(void) install_element(CONFIG_NODE, &no_debug_isis_lsp_sched_cmd); install_element(CONFIG_NODE, &debug_isis_bfd_cmd); install_element(CONFIG_NODE, &no_debug_isis_bfd_cmd); + install_element(CONFIG_NODE, &debug_isis_ldp_sync_cmd); + install_element(CONFIG_NODE, &no_debug_isis_ldp_sync_cmd); install_default(ROUTER_NODE); diff --git a/isisd/isisd.h b/isisd/isisd.h index c26a62dfac28..d8df6eead90e 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -35,6 +35,7 @@ #include "isis_lsp.h" #include "isis_memory.h" #include "qobj.h" +#include "ldp_sync.h" #ifdef FABRICD static const bool fabricd = true; @@ -93,6 +94,7 @@ struct isis { uint32_t circuit_ids_used[8]; /* 256 bits to track circuit ids 1 through 255 */ struct route_table *ext_info[REDIST_PROTOCOL_COUNT]; + struct ldp_sync_info_cmd ldp_sync_cmd; /* MPLS LDP-IGP Sync */ }; extern struct isis_master *im; @@ -275,6 +277,7 @@ extern unsigned long debug_flooding; extern unsigned long debug_bfd; extern unsigned long debug_tx_queue; extern unsigned long debug_sr; +extern unsigned long debug_ldp_sync; #define DEBUG_ADJ_PACKETS (1<<0) #define DEBUG_SNP_PACKETS (1<<1) @@ -289,6 +292,7 @@ extern unsigned long debug_sr; #define DEBUG_BFD (1<<10) #define DEBUG_TX_QUEUE (1<<11) #define DEBUG_SR (1<<12) +#define DEBUG_LDP_SYNC (1 << 13) /* Debug related macro. */ #define IS_DEBUG_ADJ_PACKETS (debug_adj_pkt & DEBUG_ADJ_PACKETS) @@ -304,6 +308,7 @@ extern unsigned long debug_sr; #define IS_DEBUG_BFD (debug_bfd & DEBUG_BFD) #define IS_DEBUG_TX_QUEUE (debug_tx_queue & DEBUG_TX_QUEUE) #define IS_DEBUG_SR (debug_sr & DEBUG_SR) +#define IS_DEBUG_LDP_SYNC (debug_ldp_sync & DEBUG_LDP_SYNC) #define lsp_debug(...) \ do { \ diff --git a/isisd/subdir.am b/isisd/subdir.am index 9e855ad8cf26..50d474653465 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -8,6 +8,7 @@ sbin_PROGRAMS += isisd/isisd dist_examples_DATA += isisd/isisd.conf.sample vtysh_scan += \ isisd/isis_cli.c \ + isisd/isis_ldp_sync.c \ isisd/isis_redist.c \ isisd/isis_spf.c \ isisd/isis_te.c \ @@ -36,6 +37,7 @@ noinst_HEADERS += \ isisd/isis_errors.h \ isisd/isis_events.h \ isisd/isis_flags.h \ + isisd/isis_ldp_sync.h \ isisd/isis_lsp.h \ isisd/isis_memory.h \ isisd/isis_misc.h \ @@ -69,6 +71,7 @@ LIBISIS_SOURCES = \ isisd/isis_errors.c \ isisd/isis_events.c \ isisd/isis_flags.c \ + isisd/isis_ldp_sync.c \ isisd/isis_lsp.c \ isisd/isis_memory.c \ isisd/isis_misc.c \ diff --git a/ldpd/adjacency.c b/ldpd/adjacency.c index 0bdd2423c7c1..4e09a6c4c9e1 100644 --- a/ldpd/adjacency.c +++ b/ldpd/adjacency.c @@ -125,6 +125,9 @@ adj_del(struct adj *adj, uint32_t notif_status) switch (adj->source.type) { case HELLO_LINK: RB_REMOVE(ia_adj_head, &adj->source.link.ia->adj_tree, adj); + + if (nbr) + ldp_sync_fsm_adj_event(adj, LDP_SYNC_EVT_ADJ_DEL); break; case HELLO_TARGETED: adj->source.target->adj = NULL; diff --git a/ldpd/control.c b/ldpd/control.c index cde99dc8a992..6554f0a6f18c 100644 --- a/ldpd/control.c +++ b/ldpd/control.c @@ -263,6 +263,9 @@ control_dispatch_imsg(struct thread *thread) nbr_clear_ctl(imsg.data); break; + case IMSG_CTL_SHOW_LDP_SYNC: + ldpe_ldp_sync_ctl(c); + break; case IMSG_CTL_LOG_VERBOSE: /* ignore */ break; diff --git a/ldpd/hello.c b/ldpd/hello.c index ac24704bca3c..caf63c13d7b9 100644 --- a/ldpd/hello.c +++ b/ldpd/hello.c @@ -378,6 +378,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, adj->nbr = nbr; RB_INSERT(nbr_adj_head, &nbr->adj_tree, adj); } + ldp_sync_fsm_adj_event(adj, LDP_SYNC_EVT_ADJ_NEW); } adj->ds_tlv = ds_tlv; diff --git a/ldpd/interface.c b/ldpd/interface.c index 371c7d0bb140..bc8f26bc58a6 100644 --- a/ldpd/interface.c +++ b/ldpd/interface.c @@ -23,6 +23,7 @@ #include "ldpd.h" #include "ldpe.h" #include "log.h" +#include "ldp_debug.h" #include "sockopt.h" @@ -40,6 +41,17 @@ static int if_leave_ipv4_group(struct iface *, struct in_addr *); static int if_join_ipv6_group(struct iface *, struct in6_addr *); static int if_leave_ipv6_group(struct iface *, struct in6_addr *); +static int ldp_sync_fsm_init(struct iface *iface, int state); +static int ldp_sync_act_iface_start_sync(struct iface *iface); +static int iface_wait_for_ldp_sync_timer(struct thread *thread); +static void start_wait_for_ldp_sync_timer(struct iface *iface); +static void stop_wait_for_ldp_sync_timer(struct iface *iface); +static int ldp_sync_act_ldp_start_sync(struct iface *iface); +static int ldp_sync_act_ldp_complete_sync(struct iface *iface); +static int iface_to_oper_nbr_count(struct iface *iface, unsigned int type); +static void ldp_sync_get_peer_ldp_id(struct iface *iface, + struct in_addr *peer_ldp_id); + RB_GENERATE(iface_head, iface, entry, iface_compare) static __inline int @@ -87,6 +99,9 @@ ldpe_if_init(struct iface *iface) iface->ipv6.iface = iface; iface->ipv6.state = IF_STA_DOWN; RB_INIT(ia_adj_head, &iface->ipv6.adj_tree); + + /* LGP IGP Sync */ + ldp_sync_fsm_init(iface, LDP_SYNC_STA_NOT_ACH); } void @@ -96,6 +111,8 @@ ldpe_if_exit(struct iface *iface) log_debug("%s: interface %s", __func__, iface->name); + ldp_sync_fsm(iface, LDP_SYNC_EVT_CONFIG_LDP_OFF); + if (iface->ipv4.state == IF_STA_ACTIVE) if_reset(iface, AF_INET); if (iface->ipv6.state == IF_STA_ACTIVE) @@ -138,6 +155,10 @@ if_update_info(struct iface *iface, struct kif *kif) kif->flags & IFF_MULTICAST) iface->type = IF_TYPE_BROADCAST; + if (ldpd_process == PROC_LDP_ENGINE && iface->operative && + !kif->operative) + ldp_sync_fsm(iface, LDP_SYNC_EVT_IFACE_SHUTDOWN); + /* get index and flags */ iface->ifindex = kif->ifindex; iface->operative = kif->operative; @@ -426,6 +447,12 @@ if_get_hello_interval(struct iface_af *ia) return (leconf->lhello_interval); } +uint16_t +if_get_wait_for_sync_interval(void) +{ + return (leconf->wait_for_sync_interval); +} + /* timers */ /* ARGSUSED */ static int @@ -484,6 +511,55 @@ if_to_ctl(struct iface_af *ia) return (&ictl); } +static void +ldp_sync_get_peer_ldp_id(struct iface *iface, struct in_addr *peer_ldp_id) +{ + struct iface_af *ia; + struct adj *adj; + + if (iface->ipv4.state == IF_STA_ACTIVE) { + ia = iface_af_get(iface, AF_INET); + RB_FOREACH(adj, ia_adj_head, &ia->adj_tree) + if (adj->nbr && adj->nbr->state == NBR_STA_OPER) { + *peer_ldp_id = adj->nbr->id; + return; + } + } + + if (iface->ipv6.state == IF_STA_ACTIVE) { + ia = iface_af_get(iface, AF_INET6); + RB_FOREACH(adj, ia_adj_head, &ia->adj_tree) + if (adj->nbr && adj->nbr->state == NBR_STA_OPER) { + *peer_ldp_id = adj->nbr->id; + return; + } + } +} + +struct ctl_ldp_sync * +ldp_sync_to_ctl(struct iface *iface) +{ + static struct ctl_ldp_sync ictl; + + memcpy(ictl.name, iface->name, sizeof(ictl.name)); + ictl.ifindex = iface->ifindex; + ictl.in_sync = (iface->ldp_sync.state == LDP_SYNC_STA_ACH); + ictl.wait_time = if_get_wait_for_sync_interval(); + ictl.timer_running = iface->ldp_sync.wait_for_sync_timer ? true : false; + + if (iface->ldp_sync.wait_for_sync_timer) + ictl.wait_time_remaining = + thread_timer_remain_second(iface->ldp_sync.wait_for_sync_timer); + else + ictl.wait_time_remaining = 0; + + memset(&ictl.peer_ldp_id, 0, sizeof(ictl.peer_ldp_id)); + + ldp_sync_get_peer_ldp_id(iface, &ictl.peer_ldp_id); + + return (&ictl); +} + /* multicast membership sockopts */ in_addr_t if_get_ipv4_addr(struct iface *iface) @@ -576,3 +652,344 @@ if_leave_ipv6_group(struct iface *iface, struct in6_addr *addr) return (0); } + +const struct { + int state; + enum ldp_sync_event event; + enum ldp_sync_action action; + int new_state; +} ldp_sync_fsm_tbl[] = { + /* current state event that happened action to take resulting state */ +/* LDP IGP Sync not achieved */ + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_LDP_SYNC_START, LDP_SYNC_ACT_LDP_START_SYNC, 0}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_LDP_SYNC_COMPLETE, LDP_SYNC_ACT_LDP_COMPLETE_SYNC, LDP_SYNC_STA_ACH}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_CONFIG_LDP_OFF, LDP_SYNC_ACT_CONFIG_LDP_OFF, 0}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_IFACE_SHUTDOWN, LDP_SYNC_ACT_IFACE_SHUTDOWN, 0}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_SESSION_CLOSE, LDP_SYNC_ACT_NOTHING, 0}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_ADJ_DEL, LDP_SYNC_ACT_NOTHING, 0}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_ADJ_NEW, LDP_SYNC_ACT_NOTHING, 0}, +/* LDP IGP Sync achieved */ + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_CONFIG_LDP_OFF, LDP_SYNC_ACT_CONFIG_LDP_OFF, LDP_SYNC_STA_NOT_ACH}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_LDP_SYNC_COMPLETE, LDP_SYNC_ACT_NOTHING, 0}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_LDP_SYNC_START, LDP_SYNC_ACT_NOTHING, 0}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_IFACE_SHUTDOWN, LDP_SYNC_ACT_IFACE_SHUTDOWN, LDP_SYNC_STA_NOT_ACH}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_SESSION_CLOSE, LDP_SYNC_ACT_IFACE_START_SYNC, LDP_SYNC_STA_NOT_ACH}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_ADJ_DEL, LDP_SYNC_ACT_IFACE_START_SYNC, LDP_SYNC_STA_NOT_ACH}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_ADJ_NEW, LDP_SYNC_ACT_NOTHING, 0}, + {-1, LDP_SYNC_EVT_NOTHING, LDP_SYNC_ACT_NOTHING, 0}, +}; + +const char * const ldp_sync_event_names[] = { + "NOTHING", + "LDP SYNC START", + "LDP SYNC COMPLETE", + "CONFIG LDP OFF", + "IFACE SYNC START (ADJ DEL)", + "IFACE SYNC START (ADJ NEW)", + "IFACE SYNC START (SESSION CLOSE)", + "IFACE SYNC START (CONFIG LDP ON)", + "IFACE SHUTDOWN", + "N/A" +}; + +const char * const ldp_sync_action_names[] = { + "NOTHING", + "IFACE SYNC START", + "LDP START SYNC", + "LDP COMPLETE SYNC", + "CONFIG LDP OFF", + "IFACE SHUTDOWN", + "N/A" +}; + +const char * +ldp_sync_state_name(int state) +{ + switch (state) { + case LDP_SYNC_STA_NOT_ACH: + return ("NOT ACHIEVED"); + case LDP_SYNC_STA_ACH: + return ("ACHIEVED"); + default: + return ("UNKNOWN"); + } +} + +static int +send_ldp_sync_state_update(char *name, int ifindex, int sync_start) +{ + debug_evt_ldp_sync("%s: interface %s (%d), sync_start=%d", + __func__, name, ifindex, sync_start); + + struct ldp_igp_sync_if_state state; + + state.ifindex = ifindex; + state.sync_start = sync_start; + + return ldpe_imsg_compose_parent(IMSG_LDP_SYNC_IF_STATE_UPDATE, + getpid(), &state, sizeof(state)); +} + +static int +ldp_sync_act_iface_start_sync(struct iface *iface) +{ + send_ldp_sync_state_update(iface->name, iface->ifindex, true); + + return (0); +} + +static int +iface_wait_for_ldp_sync_timer(struct thread *thread) +{ + struct iface *iface = THREAD_ARG(thread); + + ldp_sync_fsm(iface, LDP_SYNC_EVT_LDP_SYNC_COMPLETE); + + return (0); +} + +static void start_wait_for_ldp_sync_timer(struct iface *iface) +{ + if (iface->ldp_sync.wait_for_sync_timer) + return; + + THREAD_TIMER_OFF(iface->ldp_sync.wait_for_sync_timer); + iface->ldp_sync.wait_for_sync_timer = NULL; + thread_add_timer(master, iface_wait_for_ldp_sync_timer, iface, + if_get_wait_for_sync_interval(), + &iface->ldp_sync.wait_for_sync_timer); +} + +static void stop_wait_for_ldp_sync_timer(struct iface *iface) +{ + THREAD_TIMER_OFF(iface->ldp_sync.wait_for_sync_timer); + iface->ldp_sync.wait_for_sync_timer = NULL; +} + +static int +ldp_sync_act_ldp_start_sync(struct iface *iface) +{ + start_wait_for_ldp_sync_timer(iface); + + return 0; +} + +static int +ldp_sync_act_ldp_complete_sync(struct iface *iface) +{ + send_ldp_sync_state_update(iface->name, iface->ifindex, false); + + return 0; +} + +static int +iface_to_oper_nbr_count(struct iface *iface, unsigned int type) +{ + int oper_nbr_count = 0; + struct adj *adj; + + RB_FOREACH(adj, ia_adj_head, &iface->ipv4.adj_tree) { + if (type == adj->source.type && adj->nbr && + adj->nbr->state == NBR_STA_OPER) + oper_nbr_count++; + } + + RB_FOREACH(adj, ia_adj_head, &iface->ipv6.adj_tree) { + if (type == adj->source.type && adj->nbr && + adj->nbr->state == NBR_STA_OPER) + oper_nbr_count++; + } + + return oper_nbr_count; +} + +int +ldp_sync_fsm_adj_event(struct adj *adj, enum ldp_sync_event event) +{ + if (adj->source.type != HELLO_LINK) + return -1; + + struct iface *iface = adj->source.link.ia->iface; + + if (!iface->operative) + return 0; + + if (event == LDP_SYNC_EVT_ADJ_NEW) { + struct nbr *nbr = adj->nbr; + if (nbr && nbr->state == NBR_STA_OPER) { + event = LDP_SYNC_EVT_LDP_SYNC_START; + } + } else if (event == LDP_SYNC_EVT_ADJ_DEL) { + /* Ignore if an operational neighbor exists. + */ + int oper_nbr_count = iface_to_oper_nbr_count(iface, HELLO_LINK); + if (oper_nbr_count > 0) + return 0; + } + + debug_evt_ldp_sync("%s: event %s, " + "adj iface %s (%d) lsr-id %s " + "source address %s transport address %s", + __func__, ldp_sync_event_names[event], + adj->source.link.ia->iface->name, + adj->source.link.ia->iface->ifindex, + inet_ntoa(adj->lsr_id), + log_addr(adj_get_af(adj), &adj->source.link.src_addr), + log_addr(adj_get_af(adj), &adj->trans_addr)); + + return ldp_sync_fsm(iface, event); +} + +int +ldp_sync_fsm_nbr_event(struct nbr *nbr, enum ldp_sync_event event) +{ + struct adj *adj; + struct iface *iface = NULL; + RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) { + if (HELLO_LINK != adj->source.type) + continue; + + iface = adj->source.link.ia->iface; + + if (!iface || !iface->operative) + continue; + + int oper_nbr_count = iface_to_oper_nbr_count(iface, HELLO_LINK); + + if (event == LDP_SYNC_EVT_SESSION_CLOSE && oper_nbr_count > 0) + /* Ignore if an operational neighbor exists. + */ + continue; + + debug_evt_ldp_sync("%s: event %s, iface %s, lsr-id %s", + __func__, ldp_sync_event_names[event], + iface->name, inet_ntoa(nbr->id)); + + ldp_sync_fsm(iface, event); + } + + return 0; +} + +int +ldp_sync_fsm_state_req(struct ldp_igp_sync_if_state_req *state_req) +{ + debug_evt_ldp_sync("%s: interface %s (%d) proto %s", + __func__, state_req->name, state_req->ifindex, + zebra_route_string(state_req->proto)); + + struct iface *iface = if_lookup_name(leconf, state_req->name); + + if (!iface) { + debug_evt_ldp_sync("%s: Warning: Ignoring LDP IGP SYNC " + "interface state request for interface %s (%d). " + "Interface does not exist in LDP.", + __func__, state_req->name, state_req->ifindex); + + return 0; + } + + return send_ldp_sync_state_update(state_req->name, + state_req->ifindex, + (iface->ldp_sync.state != LDP_SYNC_STA_ACH)); +} + +static int +ldp_sync_fsm_init(struct iface *iface, int state) +{ + int old_state = iface->ldp_sync.state; + + iface->ldp_sync.state = state; + stop_wait_for_ldp_sync_timer(iface); + + send_ldp_sync_state_update(iface->name, iface->ifindex, + (iface->ldp_sync.state != LDP_SYNC_STA_ACH)); + + if (old_state != iface->ldp_sync.state) { + debug_evt_ldp_sync("%s: resulted in " + "changing state for interface %s (%d) from %s to %s", + __func__, + iface->name, iface->ifindex, + ldp_sync_state_name(old_state), + ldp_sync_state_name(iface->ldp_sync.state)); + } + + return 0; +} + +int +ldp_sync_fsm(struct iface *iface, enum ldp_sync_event event) +{ + int old_state = iface->ldp_sync.state; + int new_state = 0; + int i; + + for (i = 0; ldp_sync_fsm_tbl[i].state != -1; i++) + if ((ldp_sync_fsm_tbl[i].state & old_state) && + (ldp_sync_fsm_tbl[i].event == event)) { + new_state = ldp_sync_fsm_tbl[i].new_state; + break; + } + + if (ldp_sync_fsm_tbl[i].state == -1) { + /* event outside of the defined fsm, ignore it. */ + log_warnx("%s: interface %s, event %s not expected in " + "state %s ", __func__, iface->name, + ldp_sync_event_names[event], + ldp_sync_state_name(old_state)); + return (0); + } + + if (new_state != 0) + iface->ldp_sync.state = new_state; + + switch (ldp_sync_fsm_tbl[i].action) { + case LDP_SYNC_ACT_IFACE_START_SYNC: + ldp_sync_act_iface_start_sync(iface); + break; + case LDP_SYNC_ACT_LDP_START_SYNC: + ldp_sync_act_ldp_start_sync(iface); + break; + case LDP_SYNC_ACT_LDP_COMPLETE_SYNC: + ldp_sync_act_ldp_complete_sync(iface); + break; + case LDP_SYNC_ACT_CONFIG_LDP_OFF: + ldp_sync_fsm_init(iface, LDP_SYNC_STA_NOT_ACH); + break; + case LDP_SYNC_ACT_IFACE_SHUTDOWN: + ldp_sync_fsm_init(iface, iface->ldp_sync.state); + break; + case LDP_SYNC_ACT_NOTHING: + /* do nothing */ + break; + } + + if (old_state != iface->ldp_sync.state) { + + debug_evt_ldp_sync("%s: event %s resulted in action %s " + "for interface %s, changing state from %s to %s", + __func__, ldp_sync_event_names[event], + ldp_sync_action_names[ldp_sync_fsm_tbl[i].action], + iface->name, ldp_sync_state_name(old_state), + ldp_sync_state_name(iface->ldp_sync.state)); + + } else { + debug_evt_ldp_sync("%s: event %s resulted in action %s " + "for interface %s, remaining in state %s", + __func__, ldp_sync_event_names[event], + ldp_sync_action_names[ldp_sync_fsm_tbl[i].action], + iface->name, + ldp_sync_state_name(iface->ldp_sync.state)); + } + + return (0); +} + +void +ldp_sync_fsm_reset_all(void) +{ + struct iface *iface; + + RB_FOREACH(iface, iface_head, &leconf->iface_tree) + ldp_sync_fsm(iface, LDP_SYNC_EVT_CONFIG_LDP_OFF); +} diff --git a/ldpd/ldp.h b/ldpd/ldp.h index 5d1bf81ec78d..4bad3afc3c87 100644 --- a/ldpd/ldp.h +++ b/ldpd/ldp.h @@ -51,6 +51,8 @@ #define INIT_DELAY_TMR 15 #define MAX_DELAY_TMR 120 +#define DFLT_WAIT_FOR_SYNC 10 + #define MIN_PWID_ID 1 #define MAX_PWID_ID 0xffffffff diff --git a/ldpd/ldp_debug.c b/ldpd/ldp_debug.c index b9ef60ff9430..a8d9833ddea4 100644 --- a/ldpd/ldp_debug.c +++ b/ldpd/ldp_debug.c @@ -99,6 +99,11 @@ ldp_vty_debug(struct vty *vty, const char *negate, const char *type_str, DEBUG_ON(msg, LDP_DEBUG_MSG_SEND_ALL); } } + } else if (strcmp(type_str, "sync") == 0) { + if (negate) + DEBUG_OFF(sync, LDP_DEBUG_SYNC); + else + DEBUG_ON(sync, LDP_DEBUG_SYNC); } else if (strcmp(type_str, "zebra") == 0) { if (negate) DEBUG_OFF(zebra, LDP_DEBUG_ZEBRA); @@ -137,6 +142,8 @@ ldp_vty_show_debugging(struct vty *vty) " LDP detailed messages debugging is on (outbound)\n"); else if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND)) vty_out (vty," LDP messages debugging is on (outbound)\n"); + if (LDP_DEBUG(sync, LDP_DEBUG_SYNC)) + vty_out (vty, " LDP sync debugging is on\n"); if (LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) vty_out (vty, " LDP zebra debugging is on\n"); vty_out (vty, "\n"); @@ -195,5 +202,10 @@ ldp_debug_config_write(struct vty *vty) write = 1; } + if (CONF_LDP_DEBUG(sync, LDP_DEBUG_SYNC)) { + vty_out (vty, "debug mpls ldp sync\n"); + write = 1; + } + return (write); } diff --git a/ldpd/ldp_debug.h b/ldpd/ldp_debug.h index 8ae144d93a3d..977734bddfdf 100644 --- a/ldpd/ldp_debug.h +++ b/ldpd/ldp_debug.h @@ -42,6 +42,10 @@ struct ldp_debug { int zebra; #define LDP_DEBUG_ZEBRA 0x01 + + int sync; +#define LDP_DEBUG_SYNC 0x01 + }; extern struct ldp_debug conf_ldp_debug; extern struct ldp_debug ldp_debug; @@ -143,4 +147,10 @@ do { \ log_debug("zebra[out]: " emsg, __VA_ARGS__); \ } while (0) +#define debug_evt_ldp_sync(emsg, ...) \ +do { \ + if (LDP_DEBUG(sync, LDP_DEBUG_SYNC)) \ + log_debug("sync: " emsg, __VA_ARGS__); \ +} while (0) + #endif /* _LDP_DEBUG_H_ */ diff --git a/ldpd/ldp_vty.h b/ldpd/ldp_vty.h index f6ba8f8c97c8..882874f1be49 100644 --- a/ldpd/ldp_vty.h +++ b/ldpd/ldp_vty.h @@ -50,6 +50,7 @@ int ldp_vty_label_accept(struct vty *, const char *, const char *, const char * int ldp_vty_ttl_security(struct vty *, const char *); int ldp_vty_router_id(struct vty *, const char *, struct in_addr); int ldp_vty_ordered_control(struct vty *, const char *); +int ldp_vty_wait_for_sync_interval(struct vty *, const char *, long); int ldp_vty_ds_cisco_interop(struct vty *, const char *); int ldp_vty_trans_pref_ipv4(struct vty *, const char *); int ldp_vty_neighbor_password(struct vty *, const char *, struct in_addr, const char *); @@ -73,6 +74,7 @@ int ldp_vty_show_discovery(struct vty *, const char *, const char *, const char int ldp_vty_show_interface(struct vty *, const char *, const char *); int ldp_vty_show_capabilities(struct vty *, const char *); int ldp_vty_show_neighbor(struct vty *, const char *, int, const char *, const char *); +int ldp_vty_show_ldp_sync(struct vty *, const char *); int ldp_vty_show_atom_binding(struct vty *, const char *, unsigned long, unsigned long, const char *); int ldp_vty_show_atom_vc(struct vty *, const char *, const char *, diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c index 53a384fe5521..1f102f86fa89 100644 --- a/ldpd/ldp_vty_cmds.c +++ b/ldpd/ldp_vty_cmds.c @@ -230,6 +230,17 @@ DEFPY (ldp_ordered_control, return (ldp_vty_ordered_control(vty, no)); } +DEFPY (ldp_wait_for_sync, + ldp_wait_for_sync_cmd, + "[no] wait-for-sync (1-10000)$waitforsync", + NO_STR + "Time to wait for LDP-IGP Sync to complete label exchange\n" + "Time (seconds)\n") +{ + return (ldp_vty_wait_for_sync_interval(vty, no, waitforsync)); + +} + DEFPY (ldp_discovery_targeted_hello_accept, ldp_discovery_targeted_hello_accept_cmd, "[no] discovery targeted-hello accept [from <(1-199)|(1300-2699)|WORD>$from_acl]", @@ -547,7 +558,7 @@ DEFPY (ldp_debug_mpls_ldp_discovery_hello, DEFPY (ldp_debug_mpls_ldp_type, ldp_debug_mpls_ldp_type_cmd, - "[no] debug mpls ldp $type", + "[no] debug mpls ldp $type", NO_STR "Debugging functions\n" "MPLS information\n" @@ -555,6 +566,7 @@ DEFPY (ldp_debug_mpls_ldp_type, "Errors\n" "LDP event information\n" "LDP label allocation information\n" + "LDP sync information\n" "LDP zebra information\n") { return (ldp_vty_debug(vty, no, type, NULL, NULL)); @@ -695,6 +707,18 @@ DEFPY (ldp_show_mpls_ldp_neighbor_capabilities, return (ldp_vty_show_neighbor(vty, lsr_id_str, 1, NULL, json)); } +DEFPY (ldp_show_mpls_ldp_igp_sync, + ldp_show_mpls_ldp_igp_sync_cmd, + "show mpls ldp igp-sync [json]$json", + "Show mpls ldp ldp-sync information\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "LDP-IGP Sync information\n" + JSON_STR) +{ + return (ldp_vty_show_ldp_sync(vty, json)); +} + DEFPY (ldp_show_l2vpn_atom_binding, ldp_show_l2vpn_atom_binding_cmd, "show l2vpn atom binding\ @@ -819,6 +843,7 @@ ldp_vty_init (void) install_element(LDP_NODE, &ldp_neighbor_ttl_security_cmd); install_element(LDP_NODE, &ldp_router_id_cmd); install_element(LDP_NODE, &ldp_ordered_control_cmd); + install_element(LDP_NODE, &ldp_wait_for_sync_cmd); install_element(LDP_IPV4_NODE, &ldp_discovery_link_holdtime_cmd); install_element(LDP_IPV4_NODE, &ldp_discovery_targeted_holdtime_cmd); @@ -888,4 +913,5 @@ ldp_vty_init (void) install_element(VIEW_NODE, &ldp_show_l2vpn_atom_binding_cmd); install_element(VIEW_NODE, &ldp_show_l2vpn_atom_vc_cmd); install_element(VIEW_NODE, &ldp_show_debugging_mpls_ldp_cmd); + install_element(VIEW_NODE, &ldp_show_mpls_ldp_igp_sync_cmd); } diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index 03dee23b47ec..c217cfc78ab5 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -285,6 +285,11 @@ ldp_config_write(struct vty *vty) if (ldpd_conf->flags & F_LDPD_ORDERED_CONTROL) vty_out (vty, " ordered-control\n"); + if (ldpd_conf->wait_for_sync_interval != DFLT_WAIT_FOR_SYNC && + ldpd_conf->wait_for_sync_interval != 0) + vty_out (vty, " wait-for-sync %u\n", + ldpd_conf->wait_for_sync_interval); + RB_FOREACH(nbrp, nbrp_head, &ldpd_conf->nbrp_tree) { if (nbrp->flags & F_NBRP_KEEPALIVE) vty_out (vty, " neighbor %s session holdtime %u\n", @@ -477,7 +482,6 @@ int ldp_vty_disc_holdtime(struct vty *vty, const char *negate, struct iface *iface; struct iface_af *ia; int af; - switch (vty->node) { case LDP_NODE: if (negate) { @@ -1014,6 +1018,24 @@ ldp_vty_ordered_control(struct vty *vty, const char *negate) return (CMD_SUCCESS); } +int ldp_vty_wait_for_sync_interval(struct vty *vty, const char *negate, + long secs) +{ + switch (vty->node) { + case LDP_NODE: + if (negate) + vty_conf->wait_for_sync_interval = DFLT_WAIT_FOR_SYNC; + else + vty_conf->wait_for_sync_interval = secs; + + ldp_config_apply(vty, vty_conf); + break; + default: + fatalx("ldp_vty_wait_for_sync_interval: unexpected node"); + } + return (CMD_SUCCESS); +} + int ldp_vty_ds_cisco_interop(struct vty *vty, const char * negate) { diff --git a/ldpd/ldp_vty_exec.c b/ldpd/ldp_vty_exec.c index d8ed5cccccb6..609598a76817 100644 --- a/ldpd/ldp_vty_exec.c +++ b/ldpd/ldp_vty_exec.c @@ -37,7 +37,8 @@ enum show_command { SHOW_NBR, SHOW_LIB, SHOW_L2VPN_PW, - SHOW_L2VPN_BINDING + SHOW_L2VPN_BINDING, + SHOW_LDP_SYNC }; struct show_params { @@ -86,6 +87,10 @@ static void show_discovery_detail_adj_json(json_object *, struct ctl_adj *); static int show_discovery_detail_msg_json(struct imsg *, struct show_params *, json_object *); +static int show_ldp_sync_msg(struct vty *, struct imsg *, + struct show_params *); +static int show_ldp_sync_msg_json(struct imsg *, + struct show_params *, json_object *); static int show_nbr_msg(struct vty *, struct imsg *, struct show_params *); @@ -122,7 +127,6 @@ static int show_l2vpn_pw_msg(struct vty *, struct imsg *, struct show_params *); static int show_l2vpn_pw_msg_json(struct imsg *, struct show_params *, json_object *); -static int ldp_vty_connect(struct imsgbuf *); static int ldp_vty_dispatch_msg(struct vty *, struct imsg *, enum show_command, struct show_params *, json_object *); @@ -206,6 +210,87 @@ show_interface_msg_json(struct imsg *imsg, struct show_params *params, return (0); } +static int +show_ldp_sync_msg(struct vty *vty, struct imsg *imsg, + struct show_params *params) +{ + struct ctl_ldp_sync *iface; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_LDP_SYNC: + iface = imsg->data; + + vty_out (vty, "%s:\n", iface->name); + if (iface->in_sync) + vty_out (vty, " Status: initial label exchange complete\n"); + else + vty_out (vty, " Status: label exchange not complete\n"); + + if (iface->timer_running) { + vty_out (vty, " Wait time: %d seconds (%d seconds left)\n", + iface->wait_time, iface->wait_time_remaining); + vty_out (vty, " Timer is running\n"); + } else { + vty_out (vty, " Wait time: %d seconds\n", + iface->wait_time); + vty_out (vty, " Timer is not running\n"); + } + + if (iface->peer_ldp_id.s_addr) + vty_out (vty, " Peer LDP Identifier: %s:0\n", + inet_ntoa(iface->peer_ldp_id)); + + break; + case IMSG_CTL_END: + return (1); + default: + break; + } + + return (0); +} + +static int +show_ldp_sync_msg_json(struct imsg *imsg, struct show_params *params, + json_object *json) +{ + struct ctl_ldp_sync *iface; + json_object *json_iface; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_LDP_SYNC: + iface = imsg->data; + + json_iface = json_object_new_object(); + json_object_string_add(json_iface, "state", + iface->in_sync + ? "labelExchangeComplete" + : "labelExchangeNotComplete"); + json_object_int_add(json_iface, "waitTime", + iface->wait_time); + json_object_int_add(json_iface, "waitTimeRemaining", + iface->wait_time_remaining); + + if (iface->timer_running) + json_object_boolean_true_add(json_iface, "timerRunning"); + else + json_object_boolean_false_add(json_iface, "timerRunning"); + + json_object_string_add(json_iface, "peerLdpId", + iface->peer_ldp_id.s_addr ? + inet_ntoa(iface->peer_ldp_id) : ""); + + json_object_object_add(json, iface->name, json_iface); + break; + case IMSG_CTL_END: + return (1); + default: + break; + } + + return (0); +} + static int show_discovery_msg(struct vty *vty, struct imsg *imsg, struct show_params *params) @@ -1436,6 +1521,20 @@ ldp_vty_dispatch_iface(struct vty *vty, struct imsg *imsg, return (ret); } +static int +ldp_vty_dispatch_ldp_sync(struct vty *vty, struct imsg *imsg, + struct show_params *params, json_object *json) +{ + int ret; + + if (params->json) + ret = show_ldp_sync_msg_json(imsg, params, json); + else + ret = show_ldp_sync_msg(vty, imsg, params); + + return (ret); +} + static int ldp_vty_dispatch_disc(struct vty *vty, struct imsg *imsg, struct show_params *params, json_object *json) @@ -1684,6 +1783,8 @@ ldp_vty_dispatch_msg(struct vty *vty, struct imsg *imsg, enum show_command cmd, case SHOW_L2VPN_BINDING: return (ldp_vty_dispatch_l2vpn_binding(vty, imsg, params, json)); + case SHOW_LDP_SYNC: + return (ldp_vty_dispatch_ldp_sync(vty, imsg, params, json)); default: return (0); } @@ -1945,6 +2046,22 @@ ldp_vty_show_neighbor(struct vty *vty, const char *lsr_id, int capabilities, return (ldp_vty_dispatch(vty, &ibuf, SHOW_NBR, ¶ms)); } +int +ldp_vty_show_ldp_sync(struct vty *vty, const char *json) +{ + struct imsgbuf ibuf; + struct show_params params; + + if (ldp_vty_connect(&ibuf) < 0) + return (CMD_WARNING); + + memset(¶ms, 0, sizeof(params)); + params.json = (json) ? 1 : 0; + + imsg_compose(&ibuf, IMSG_CTL_SHOW_LDP_SYNC, 0, 0, -1, NULL, 0); + return (ldp_vty_dispatch(vty, &ibuf, SHOW_LDP_SYNC, ¶ms)); +} + int ldp_vty_show_atom_binding(struct vty *vty, const char *peer, unsigned long local_label, unsigned long remote_label, const char *json) diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index d828fbe977ed..16e9adc9d97d 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -31,6 +31,7 @@ #include "ldpd.h" #include "ldpe.h" #include "lde.h" +#include "ldp_sync.h" #include "log.h" #include "ldp_debug.h" @@ -46,6 +47,14 @@ static int ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS); static void ldp_zebra_connected(struct zclient *); static void ldp_zebra_filter_update(struct access_list *access); +static void ldp_zebra_opaque_register(void); +static void ldp_zebra_opaque_unregister(void); +static int ldp_sync_zebra_send_announce(void); +static int ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS); +static void ldp_sync_zebra_start_hello_timer(void); +static int ldp_sync_zebra_hello(struct thread *thread); +static void ldp_sync_zebra_init(void); + static struct zclient *zclient; static void @@ -103,6 +112,95 @@ pw2zpw(struct l2vpn_pw *pw, struct zapi_pw *zpw) sizeof(zpw->data.ldp.vpn_name)); } +static void +ldp_zebra_opaque_register(void) +{ + zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST); +} + +static void +ldp_zebra_opaque_unregister(void) +{ + zclient_unregister_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST); +} + +int +ldp_sync_zebra_send_state_update(struct ldp_igp_sync_if_state *state) +{ + return zclient_send_opaque(zclient, LDP_IGP_SYNC_IF_STATE_UPDATE, + (const uint8_t *) state, sizeof(*state)); +} + +static int +ldp_sync_zebra_send_announce(void) +{ + struct ldp_igp_sync_announce announce; + announce.proto = ZEBRA_ROUTE_LDP; + + return zclient_send_opaque(zclient, LDP_IGP_SYNC_ANNOUNCE_UPDATE, + (const uint8_t *) &announce, sizeof(announce)); +} + +static int +ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS) +{ + struct stream *s; + struct zapi_opaque_msg info; + struct ldp_igp_sync_if_state_req state_req; + + s = zclient->ibuf; + + if (zclient_opaque_decode(s, &info) != 0) + return -1; + + switch (info.type) { + case LDP_IGP_SYNC_IF_STATE_REQUEST: + STREAM_GET(&state_req, s, sizeof(state_req)); + main_imsg_compose_ldpe(IMSG_LDP_SYNC_IF_STATE_REQUEST, 0, &state_req, + sizeof(state_req)); + break; + default: + break; + } + +stream_failure: + return 0; +} + +static void +ldp_sync_zebra_start_hello_timer(void) +{ + thread_add_timer_msec(master, ldp_sync_zebra_hello, NULL, 250, NULL); +} + +static int +ldp_sync_zebra_hello(struct thread *thread) +{ + static unsigned int sequence = 0; + struct ldp_igp_sync_hello hello; + + sequence++; + + hello.proto = ZEBRA_ROUTE_LDP; + hello.sequence = sequence; + + zclient_send_opaque(zclient, LDP_IGP_SYNC_HELLO_UPDATE, + (const uint8_t *) &hello, sizeof(hello)); + + ldp_sync_zebra_start_hello_timer(); + + return (0); +} + +static void +ldp_sync_zebra_init(void) +{ + ldp_sync_zebra_send_announce(); + + ldp_sync_zebra_start_hello_timer(); +} + + static int ldp_zebra_send_mpls_labels(int cmd, struct kroute *kr) { @@ -299,7 +397,7 @@ ldp_ifp_destroy(struct interface *ifp) } static int -ldp_interface_status_change_helper(struct interface *ifp) +ldp_interface_status_change(struct interface *ifp) { struct listnode *node; struct connected *ifc; @@ -330,12 +428,12 @@ ldp_interface_status_change_helper(struct interface *ifp) static int ldp_ifp_up(struct interface *ifp) { - return ldp_interface_status_change_helper(ifp); + return ldp_interface_status_change(ifp); } static int ldp_ifp_down(struct interface *ifp) { - return ldp_interface_status_change_helper(ifp); + return ldp_interface_status_change(ifp); } static int @@ -525,6 +623,10 @@ ldp_zebra_connected(struct zclient *zclient) ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); + + ldp_zebra_opaque_register(); + + ldp_sync_zebra_init(); } static void @@ -563,6 +665,7 @@ ldp_zebra_init(struct thread_master *master) zclient->redistribute_route_add = ldp_zebra_read_route; zclient->redistribute_route_del = ldp_zebra_read_route; zclient->pw_status_update = ldp_zebra_read_pw_status_update; + zclient->opaque_msg_handler = ldp_zebra_opaque_msg_handler; /* Access list initialize. */ access_list_add_hook(ldp_zebra_filter_update); @@ -572,6 +675,7 @@ ldp_zebra_init(struct thread_master *master) void ldp_zebra_destroy(void) { + ldp_zebra_opaque_unregister(); zclient_stop(zclient); zclient_free(zclient); zclient = NULL; diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index dca379e4ebaf..940333f83ca4 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -586,6 +586,13 @@ main_dispatch_ldpe(struct thread *thread) fatalx("IMSG_ACL_CHECK imsg with wrong len"); ldp_acl_reply(iev, (struct acl_check *)imsg.data); break; + case IMSG_LDP_SYNC_IF_STATE_UPDATE: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct ldp_igp_sync_if_state)) + fatalx("IMSG_LDP_SYNC_IF_STATE_UPDATE imsg with wrong len"); + + ldp_sync_zebra_send_state_update((struct ldp_igp_sync_if_state *)imsg.data); + break; default: log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); @@ -1148,6 +1155,7 @@ ldp_config_reset_main(struct ldpd_conf *conf) conf->lhello_interval = DEFAULT_HELLO_INTERVAL; conf->thello_holdtime = TARGETED_DFLT_HOLDTIME; conf->thello_interval = DEFAULT_HELLO_INTERVAL; + conf->wait_for_sync_interval = DFLT_WAIT_FOR_SYNC; conf->trans_pref = DUAL_STACK_LDPOV6; conf->flags = 0; } @@ -1278,6 +1286,14 @@ merge_config(struct ldpd_conf *conf, struct ldpd_conf *xconf) static void merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) { + /* Removing global LDP config requires resetting LDP IGP Sync FSM */ + if ((conf->flags & F_LDPD_ENABLED) && + (!(xconf->flags & F_LDPD_ENABLED))) + { + if (ldpd_process == PROC_LDP_ENGINE) + ldp_sync_fsm_reset_all(); + } + /* change of router-id requires resetting all neighborships */ if (conf->rtr_id.s_addr != xconf->rtr_id.s_addr) { if (ldpd_process == PROC_LDP_ENGINE) { @@ -1303,6 +1319,7 @@ merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) conf->lhello_interval = xconf->lhello_interval; conf->thello_holdtime = xconf->thello_holdtime; conf->thello_interval = xconf->thello_interval; + conf->wait_for_sync_interval = xconf->wait_for_sync_interval; if (conf->trans_pref != xconf->trans_pref) { if (ldpd_process == PROC_LDP_ENGINE) diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index c1bcc56c448b..f8a94b4e2a50 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -34,6 +34,7 @@ #include "zclient.h" #include "ldp.h" +#include "lib/ldp_sync.h" #define CONF_FILE "/etc/ldpd.conf" #define LDPD_USER "_ldpd" @@ -93,6 +94,7 @@ enum imsg_type { IMSG_CTL_SHOW_LIB_END, IMSG_CTL_SHOW_L2VPN_PW, IMSG_CTL_SHOW_L2VPN_BINDING, + IMSG_CTL_SHOW_LDP_SYNC, IMSG_CTL_CLEAR_NBR, IMSG_CTL_FIB_COUPLE, IMSG_CTL_FIB_DECOUPLE, @@ -153,7 +155,9 @@ enum imsg_type { IMSG_INIT, IMSG_PW_UPDATE, IMSG_FILTER_UPDATE, - IMSG_NBR_SHUTDOWN + IMSG_NBR_SHUTDOWN, + IMSG_LDP_SYNC_IF_STATE_REQUEST, + IMSG_LDP_SYNC_IF_STATE_UPDATE }; struct ldpd_init { @@ -227,6 +231,34 @@ enum nbr_action { NBR_ACT_CLOSE_SESSION }; +/* LDP IGP Sync states */ +#define LDP_SYNC_STA_UNKNOWN 0x0000 +#define LDP_SYNC_STA_NOT_ACH 0x0001 +#define LDP_SYNC_STA_ACH 0x0002 + +/* LDP IGP Sync events */ +enum ldp_sync_event { + LDP_SYNC_EVT_NOTHING, + LDP_SYNC_EVT_LDP_SYNC_START, + LDP_SYNC_EVT_LDP_SYNC_COMPLETE, + LDP_SYNC_EVT_CONFIG_LDP_OFF, + LDP_SYNC_EVT_ADJ_DEL, + LDP_SYNC_EVT_ADJ_NEW, + LDP_SYNC_EVT_SESSION_CLOSE, + LDP_SYNC_EVT_CONFIG_LDP_ON, + LDP_SYNC_EVT_IFACE_SHUTDOWN +}; + +/* LDP IGP Sync actions */ +enum ldp_sync_action { + LDP_SYNC_ACT_NOTHING, + LDP_SYNC_ACT_IFACE_START_SYNC, + LDP_SYNC_ACT_LDP_START_SYNC, + LDP_SYNC_ACT_LDP_COMPLETE_SYNC, + LDP_SYNC_ACT_CONFIG_LDP_OFF, + LDP_SYNC_ACT_IFACE_SHUTDOWN +}; + /* forward declarations */ RB_HEAD(global_adj_head, adj); RB_HEAD(nbr_adj_head, adj); @@ -310,6 +342,11 @@ struct iface_af { uint16_t hello_interval; }; +struct iface_ldp_sync { + int state; + struct thread *wait_for_sync_timer; +}; + struct iface { RB_ENTRY(iface) entry; char name[IF_NAMESIZE]; @@ -320,6 +357,7 @@ struct iface { int operative; struct iface_af ipv4; struct iface_af ipv6; + struct iface_ldp_sync ldp_sync; QOBJ_FIELDS }; RB_HEAD(iface_head, iface); @@ -518,6 +556,7 @@ struct ldpd_conf { uint16_t thello_holdtime; uint16_t thello_interval; uint16_t trans_pref; + uint16_t wait_for_sync_interval; int flags; QOBJ_FIELDS }; @@ -672,6 +711,16 @@ struct ctl_pw { uint8_t reason; }; +struct ctl_ldp_sync { + char name[IF_NAMESIZE]; + ifindex_t ifindex; + bool in_sync; + bool timer_running; + uint16_t wait_time; + uint16_t wait_time_remaining; + struct in_addr peer_ldp_id; +}; + extern struct ldpd_conf *ldpd_conf, *vty_conf; extern struct ldpd_global global; extern struct ldpd_init init; @@ -825,6 +874,7 @@ extern char ctl_sock_path[MAXPATHLEN]; /* ldp_zebra.c */ void ldp_zebra_init(struct thread_master *); void ldp_zebra_destroy(void); +int ldp_sync_zebra_send_state_update(struct ldp_igp_sync_if_state *); /* compatibility */ #ifndef __OpenBSD__ diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index 9078e711fbbb..d3374a62db38 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -297,6 +297,7 @@ ldpe_dispatch_main(struct thread *thread) #endif int n, shut = 0; struct ldp_access *laccess; + struct ldp_igp_sync_if_state_req *ldp_sync_if_state_req; iev->ev_read = NULL; @@ -559,6 +560,15 @@ ldpe_dispatch_main(struct thread *thread) ldpe_check_filter_af(AF_INET6, &leconf->ipv6, laccess->name); break; + case IMSG_LDP_SYNC_IF_STATE_REQUEST: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct ldp_igp_sync_if_state_req)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + ldp_sync_if_state_req = imsg.data; + ldp_sync_fsm_state_req(ldp_sync_if_state_req); + break; default: log_debug("ldpe_dispatch_main: error handling imsg %d", imsg.hdr.type); @@ -974,6 +984,20 @@ ldpe_nbr_ctl(struct ctl_conn *c) imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); } +void +ldpe_ldp_sync_ctl(struct ctl_conn *c) +{ + struct iface *iface; + struct ctl_ldp_sync *ictl; + + RB_FOREACH(iface, iface_head, &leconf->iface_tree) { + ictl = ldp_sync_to_ctl(iface); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_LDP_SYNC, + 0, 0, -1, ictl, sizeof(struct ctl_ldp_sync)); + } + imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); +} + void mapping_list_add(struct mapping_head *mh, struct map *map) { diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h index 11650069b7a0..ef4702341b8e 100644 --- a/ldpd/ldpe.h +++ b/ldpd/ldpe.h @@ -28,6 +28,7 @@ #endif #include "ldpd.h" +#include "lib/ldp_sync.h" #define min(x,y) ((x) <= (y) ? (x) : (y)) #define max(x,y) ((x) > (y) ? (x) : (y)) @@ -212,6 +213,7 @@ void ldpe_iface_ctl(struct ctl_conn *c, ifindex_t ifidx); void ldpe_adj_ctl(struct ctl_conn *); void ldpe_adj_detail_ctl(struct ctl_conn *); void ldpe_nbr_ctl(struct ctl_conn *); +void ldpe_ldp_sync_ctl(struct ctl_conn *); void mapping_list_add(struct mapping_head *, struct map *); void mapping_list_clr(struct mapping_head *); @@ -229,8 +231,17 @@ void ldp_if_update(struct iface *, int); void if_update_all(int); uint16_t if_get_hello_holdtime(struct iface_af *); uint16_t if_get_hello_interval(struct iface_af *); +uint16_t if_get_wait_for_sync_interval(void); struct ctl_iface *if_to_ctl(struct iface_af *); in_addr_t if_get_ipv4_addr(struct iface *); +int ldp_sync_fsm_adj_event(struct adj *, enum ldp_sync_event); +int ldp_sync_fsm_nbr_event(struct nbr *, enum ldp_sync_event); +int ldp_sync_fsm_state_req(struct ldp_igp_sync_if_state_req *); +int ldp_sync_fsm(struct iface *, enum ldp_sync_event); +void ldp_sync_fsm_reset_all(void); +const char *ldp_sync_state_name(int); +const char *ldp_sync_event_name(int); +struct ctl_ldp_sync *ldp_sync_to_ctl(struct iface *); /* adjacency.c */ struct adj *adj_new(struct in_addr, struct hello_source *, diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c index 6143beb6b9af..236d3eaa58a3 100644 --- a/ldpd/neighbor.c +++ b/ldpd/neighbor.c @@ -764,6 +764,8 @@ nbr_act_session_operational(struct nbr *nbr) /* this is necessary to avoid ipc synchronization issues */ nbr_update_peerid(nbr); + ldp_sync_fsm_nbr_event(nbr, LDP_SYNC_EVT_LDP_SYNC_START); + memset(&lde_nbr, 0, sizeof(lde_nbr)); lde_nbr.id = nbr->id; lde_nbr.v4_enabled = nbr->v4_enabled; diff --git a/ldpd/packet.c b/ldpd/packet.c index c00008d120b8..3f73f8cd8850 100644 --- a/ldpd/packet.c +++ b/ldpd/packet.c @@ -683,6 +683,8 @@ session_close(struct nbr *nbr) log_debug("%s: closing session with lsr-id %s", __func__, inet_ntoa(nbr->id)); + ldp_sync_fsm_nbr_event(nbr, LDP_SYNC_EVT_SESSION_CLOSE); + tcp_close(nbr->tcp); nbr_stop_ktimer(nbr); nbr_stop_ktimeout(nbr); diff --git a/lib/command.h b/lib/command.h index e20bfe3318d4..d828989118b2 100644 --- a/lib/command.h +++ b/lib/command.h @@ -421,6 +421,11 @@ struct cmd_node { #define CMD_VNI_RANGE "(1-16777215)" #define CONF_BACKUP_EXT ".sav" +#define MPLS_LDP_SYNC_STR "Enable MPLS LDP-SYNC\n" +#define NO_MPLS_LDP_SYNC_STR "Disable MPLS LDP-SYNC\n" +#define MPLS_LDP_SYNC_HOLDDOWN_STR \ + "Time to wait for LDP-SYNC to occur before restoring if cost\n" +#define NO_MPLS_LDP_SYNC_HOLDDOWN_STR "holddown timer disable\n" /* Command warnings. */ #define NO_PASSWD_CMD_WARNING \ diff --git a/lib/ldp_sync.c b/lib/ldp_sync.c new file mode 100644 index 000000000000..5dd045d88d85 --- /dev/null +++ b/lib/ldp_sync.c @@ -0,0 +1,93 @@ +/* + * ldp_sync.c: LDP-SYNC handling routines + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "command.h" +#include "memory.h" +#include "prefix.h" +#include "log.h" +#include "thread.h" +#include "stream.h" +#include "zclient.h" +#include "table.h" +#include "vty.h" +#include "ldp_sync.h" + +/* Library code */ +DEFINE_MTYPE_STATIC(LIB, LDP_SYNC_INFO, "LDP SYNC info") + +/* + * ldp_sync_info_create - Allocate the LDP_SYNC information + */ +struct ldp_sync_info *ldp_sync_info_create(void) +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = XCALLOC(MTYPE_LDP_SYNC_INFO, + sizeof(struct ldp_sync_info)); + assert(ldp_sync_info); + + ldp_sync_info->flags = 0; + ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + ldp_sync_info->t_holddown = NULL; + return ldp_sync_info; +} + +/* + * ldp_sync_info_free - Free the LDP_SYNC information. + */ +void ldp_sync_info_free(struct ldp_sync_info **ldp_sync_info) +{ + if (*ldp_sync_info) + XFREE(MTYPE_LDP_SYNC_INFO, *ldp_sync_info); +} + +bool ldp_sync_if_is_enabled(struct ldp_sync_info *ldp_sync_info) +{ + /* return true if LDP-SYNC is configured on this interface */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP) + return true; + + return false; +} + +bool ldp_sync_if_down(struct ldp_sync_info *ldp_sync_info) +{ + /* Stop LDP-SYNC on this interface: + * if holddown timer is running stop it + * update state + */ + if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) { + if (ldp_sync_info->t_holddown != NULL) { + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + } + if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_UP) + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + return true; + } + + return false; +} diff --git a/lib/ldp_sync.h b/lib/ldp_sync.h new file mode 100644 index 000000000000..daede566f0a2 --- /dev/null +++ b/lib/ldp_sync.h @@ -0,0 +1,91 @@ +/* + * Defines and structures common to LDP-Sync for OSPFv2 and OSPFv3 and ISIS + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _LIBLDPSYNC_H +#define _LIBLDPSYNC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* LDP-IGP Sync values */ +#define LDP_SYNC_FLAG_ENABLE (1 << 0) /* LDP-SYNC enabled */ +#define LDP_SYNC_FLAG_HOLDDOWN (1 << 1) /* Holddown timer enabled */ +#define LDP_SYNC_FLAG_IF_CONFIG (1 << 2) /* LDP-SYNC enabled on interface */ +#define LDP_SYNC_FLAG_SET_METRIC (1 << 3) /* Metric has been set on ISIS intf */ + +#define LDP_IGP_SYNC_DEFAULT 0 +#define LDP_IGP_SYNC_ENABLED 1 + +#define LDP_IGP_SYNC_STATE_NOT_REQUIRED 0 +#define LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP 1 +#define LDP_IGP_SYNC_STATE_REQUIRED_UP 2 + +#define LDP_IGP_SYNC_HOLDDOWN_DEFAULT 0 + +#define LDP_IGP_SYNC_HELLO_TIMEOUT 1 + +/* LDP-IGP Sync structures */ +struct ldp_sync_info_cmd { + uint16_t flags; + uint16_t holddown; /* timer value */ + uint32_t sequence; /* hello sequence number */ + struct thread *t_hello; /* hello timer for detecting LDP going down */ +}; + +struct ldp_sync_info { + uint16_t flags; /* indicate if set on interface or globally */ + uint8_t enabled; /* enabled */ + uint8_t state; /* running state */ + uint16_t holddown; /* timer value */ + struct thread *t_holddown; /* holddown timer*/ + uint32_t metric[2]; /* isis interface metric */ +}; + +/* Prototypes. */ +extern struct ldp_sync_info *ldp_sync_info_create(void); +extern bool ldp_sync_if_is_enabled(struct ldp_sync_info *ldp_sync_info); +extern bool ldp_sync_if_down(struct ldp_sync_info *ldp_sync_info); +extern void ldp_sync_info_free(struct ldp_sync_info **ldp_sync_info); + +struct ldp_igp_sync_announce { + int proto; +}; + +struct ldp_igp_sync_if_state { + ifindex_t ifindex; + bool sync_start; +}; + +struct ldp_igp_sync_if_state_req { + int proto; + ifindex_t ifindex; + char name[INTERFACE_NAMSIZ]; +}; + +struct ldp_igp_sync_hello { + int proto; + unsigned int sequence; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBLDPSYNC_H */ diff --git a/lib/subdir.am b/lib/subdir.am index 1feaa56d13c6..b8bcee139bf4 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -42,6 +42,7 @@ lib_libfrr_la_SOURCES = \ lib/jhash.c \ lib/json.c \ lib/keychain.c \ + lib/ldp_sync.c \ lib/lib_errors.c \ lib/lib_vty.c \ lib/libfrr.c \ @@ -198,6 +199,7 @@ pkginclude_HEADERS += \ lib/jhash.h \ lib/json.h \ lib/keychain.h \ + lib/ldp_sync.h \ lib/lib_errors.h \ lib/lib_vty.h \ lib/libfrr.h \ diff --git a/lib/zclient.h b/lib/zclient.h index c6a67790a11e..b8444e8a86fb 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -955,6 +955,14 @@ enum zapi_opaque_registry { LINK_STATE_REQUEST = 1, /* Update containing link-state db info */ LINK_STATE_UPDATE = 2, + /* Request LDP-SYNC state from LDP */ + LDP_IGP_SYNC_IF_STATE_REQUEST = 3, + /* Update containing LDP IGP Sync State info */ + LDP_IGP_SYNC_IF_STATE_UPDATE = 4, + /* Announce that LDP is up */ + LDP_IGP_SYNC_ANNOUNCE_UPDATE = 5, + /* Heartbeat indicating that LDP is running */ + LDP_IGP_SYNC_HELLO_UPDATE = 6, }; /* Send the hello message. diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index dcc479def6b0..e8798e023e2c 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -54,6 +54,7 @@ unsigned long conf_debug_ospf_te = 0; unsigned long conf_debug_ospf_ext = 0; unsigned long conf_debug_ospf_sr = 0; unsigned long conf_debug_ospf_defaultinfo = 0; +unsigned long conf_debug_ospf_ldp_sync = 0; /* Enable debug option variables -- valid only session. */ unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0}; @@ -67,6 +68,7 @@ unsigned long term_debug_ospf_te = 0; unsigned long term_debug_ospf_ext = 0; unsigned long term_debug_ospf_sr = 0; unsigned long term_debug_ospf_defaultinfo; +unsigned long term_debug_ospf_ldp_sync; const char *ospf_redist_string(unsigned int route_type) { @@ -1476,6 +1478,32 @@ DEFUN (no_debug_ospf_default_info, return CMD_SUCCESS; } +DEFUN(debug_ospf_ldp_sync, + debug_ospf_ldp_sync_cmd, + "debug ospf ldp-sync", + DEBUG_STR OSPF_STR + "OSPF LDP-Sync information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_ON(ldp_sync, LDP_SYNC); + TERM_DEBUG_ON(ldp_sync, LDP_SYNC); + return CMD_SUCCESS; +} + +DEFUN(no_debug_ospf_ldp_sync, + no_debug_ospf_ldp_sync_cmd, + "no debug ospf ldp-sync", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF LDP-Sync information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_OFF(ldp_sync, LDP_SYNC); + TERM_DEBUG_OFF(ldp_sync, LDP_SYNC); + return CMD_SUCCESS; +} + DEFUN (no_debug_ospf, no_debug_ospf_cmd, "no debug ospf", @@ -1505,6 +1533,7 @@ DEFUN (no_debug_ospf, DEBUG_OFF(zebra, ZEBRA_INTERFACE); DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE); DEBUG_OFF(defaultinfo, DEFAULTINFO); + DEBUG_OFF(ldp_sync, LDP_SYNC); for (i = 0; i < 5; i++) DEBUG_PACKET_OFF(i, flag); @@ -1532,6 +1561,7 @@ DEFUN (no_debug_ospf, TERM_DEBUG_OFF(zebra, ZEBRA_INTERFACE); TERM_DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE); TERM_DEBUG_OFF(defaultinfo, DEFAULTINFO); + TERM_DEBUG_OFF(ldp_sync, LDP_SYNC); return CMD_SUCCESS; } @@ -1633,6 +1663,10 @@ static int show_debugging_ospf_common(struct vty *vty, struct ospf *ospf) if (IS_DEBUG_OSPF(nssa, NSSA) == OSPF_DEBUG_NSSA) vty_out(vty, " OSPF NSSA debugging is on\n"); + /* Show debug status for LDP-SYNC. */ + if (IS_DEBUG_OSPF(ldp_sync, LDP_SYNC) == OSPF_DEBUG_LDP_SYNC) + vty_out(vty, " OSPF ldp-sync debugging is on\n"); + vty_out(vty, "\n"); return CMD_SUCCESS; @@ -1814,6 +1848,11 @@ static int config_write_debug(struct vty *vty) write = 1; } + /* debug ospf ldp-sync */ + if (IS_CONF_DEBUG_OSPF(ldp_sync, LDP_SYNC) == OSPF_DEBUG_LDP_SYNC) { + vty_out(vty, "debug ospf%s ldp-sync\n", str); + write = 1; + } return write; } @@ -1832,6 +1871,7 @@ void ospf_debug_init(void) install_element(ENABLE_NODE, &debug_ospf_te_cmd); install_element(ENABLE_NODE, &debug_ospf_sr_cmd); install_element(ENABLE_NODE, &debug_ospf_default_info_cmd); + install_element(ENABLE_NODE, &debug_ospf_ldp_sync_cmd); install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd); install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd); install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd); @@ -1841,6 +1881,7 @@ void ospf_debug_init(void) install_element(ENABLE_NODE, &no_debug_ospf_te_cmd); install_element(ENABLE_NODE, &no_debug_ospf_sr_cmd); install_element(ENABLE_NODE, &no_debug_ospf_default_info_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_ldp_sync_cmd); install_element(ENABLE_NODE, &show_debugging_ospf_instance_cmd); install_element(ENABLE_NODE, &debug_ospf_packet_cmd); @@ -1871,6 +1912,7 @@ void ospf_debug_init(void) install_element(CONFIG_NODE, &debug_ospf_te_cmd); install_element(CONFIG_NODE, &debug_ospf_sr_cmd); install_element(CONFIG_NODE, &debug_ospf_default_info_cmd); + install_element(CONFIG_NODE, &debug_ospf_ldp_sync_cmd); install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd); install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd); install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd); @@ -1879,6 +1921,7 @@ void ospf_debug_init(void) install_element(CONFIG_NODE, &no_debug_ospf_te_cmd); install_element(CONFIG_NODE, &no_debug_ospf_sr_cmd); install_element(CONFIG_NODE, &no_debug_ospf_default_info_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_ldp_sync_cmd); install_element(CONFIG_NODE, &debug_ospf_instance_nsm_cmd); install_element(CONFIG_NODE, &debug_ospf_instance_lsa_cmd); diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h index 8c01977ff883..faae27e2cf59 100644 --- a/ospfd/ospf_dump.h +++ b/ospfd/ospf_dump.h @@ -60,6 +60,7 @@ #define OSPF_DEBUG_EXT 0x08 #define OSPF_DEBUG_SR 0x10 #define OSPF_DEBUG_DEFAULTINFO 0x20 +#define OSPF_DEBUG_LDP_SYNC 0x40 /* Macro for setting debug option. */ #define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b) @@ -107,6 +108,8 @@ #define IS_DEBUG_OSPF_DEFAULT_INFO IS_DEBUG_OSPF(defaultinfo, DEFAULTINFO) +#define IS_DEBUG_OSPF_LDP_SYNC IS_DEBUG_OSPF(ldp_sync, LDP_SYNC) + #define IS_CONF_DEBUG_OSPF_PACKET(a, b) \ (conf_debug_ospf_packet[a] & OSPF_DEBUG_##b) #define IS_CONF_DEBUG_OSPF(a, b) (conf_debug_ospf_##a & OSPF_DEBUG_##b) @@ -126,6 +129,7 @@ extern unsigned long term_debug_ospf_te; extern unsigned long term_debug_ospf_ext; extern unsigned long term_debug_ospf_sr; extern unsigned long term_debug_ospf_defaultinfo; +extern unsigned long term_debug_ospf_ldp_sync; /* Message Strings. */ extern char *ospf_lsa_type_str[]; diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index af801da8d702..adc303759863 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -32,6 +32,7 @@ #include "log.h" #include "zclient.h" #include "bfd.h" +#include "ldp_sync.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_spf.h" @@ -46,6 +47,7 @@ #include "ospfd/ospf_abr.h" #include "ospfd/ospf_network.h" #include "ospfd/ospf_dump.h" +#include "ospfd/ospf_ldp_sync.h" DEFINE_QOBJ_TYPE(ospf_interface) DEFINE_HOOK(ospf_vl_add, (struct ospf_vl_data * vd), (vd)) @@ -81,6 +83,12 @@ int ospf_if_get_output_cost(struct ospf_interface *oi) uint32_t cost; uint32_t bw, refbw; + /* if LDP-IGP Sync is running on interface set cost so interface + * is used only as last resort + */ + if (ldp_sync_if_is_enabled(IF_DEF_PARAMS(oi->ifp)->ldp_sync_info)) + return (LDP_OSPF_LSINFINITY); + /* ifp speed and bw can be 0 in some platforms, use ospf default bw if bw is configured under interface it would be used. */ @@ -539,6 +547,7 @@ void ospf_del_if_params(struct ospf_if_params *oip) { list_delete(&oip->auth_crypt); bfd_info_free(&(oip->bfd_info)); + ldp_sync_info_free(&(oip->ldp_sync_info)); XFREE(MTYPE_OSPF_IF_PARAMS, oip); } diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 4b3dbcc5c272..1d28eac6b3f6 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -104,6 +104,9 @@ struct ospf_if_params { /* BFD configuration */ struct bfd_info *bfd_info; + + /* MPLS LDP-IGP Sync configuration */ + struct ldp_sync_info *ldp_sync_info; }; enum { MEMBER_ALLROUTERS = 0, diff --git a/ospfd/ospf_ldp_sync.c b/ospfd/ospf_ldp_sync.c new file mode 100644 index 000000000000..a8c9df1c5652 --- /dev/null +++ b/ospfd/ospf_ldp_sync.c @@ -0,0 +1,1142 @@ +/* + * ospf_ldp_sync.c: OSPF LDP-IGP Sync handling routines + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "monotime.h" +#include "memory.h" +#include "thread.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "plist.h" +#include "log.h" +#include "zclient.h" +#include +#include "defaults.h" +#include "ldp_sync.h" + +#include "ospfd.h" +#include "ospf_interface.h" +#include "ospf_vty.h" +#include "ospf_ldp_sync.h" +#include "ospf_dump.h" +#include "ospf_ism.h" + +extern struct zclient *zclient; + +/* + * LDP-SYNC msg between IGP and LDP + */ +int ospf_ldp_sync_state_update(struct ldp_igp_sync_if_state state) +{ + struct ospf *ospf; + struct interface *ifp; + + /* if ospf is not enabled or LDP-SYNC is not configured ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || + !CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + /* received ldp-sync interface state from LDP */ + ifp = if_lookup_by_index(state.ifindex, VRF_DEFAULT); + if (ifp == NULL || if_is_loopback(ifp)) + return 0; + + ols_debug("ldp_sync: rcvd %s from LDP if %s", + state.sync_start ? "sync-start" : "sync-complete", + ifp->name); + if (state.sync_start) + ospf_ldp_sync_if_start(ifp, false); + else + ospf_ldp_sync_if_complete(ifp); + + return 0; +} + +int ospf_ldp_sync_announce_update(struct ldp_igp_sync_announce announce) +{ + struct ospf *ospf; + struct vrf *vrf; + struct interface *ifp; + + /* if ospf is not enabled or LDP-SYNC is not configured ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || + !CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + if (announce.proto != ZEBRA_ROUTE_LDP) + return 0; + + ols_debug("ldp_sync: rcvd announce from LDP"); + + /* LDP just started up: + * set cost to LSInfinity + * send request to LDP for LDP-SYNC state for each interface + * start hello timer + */ + vrf = vrf_lookup_by_id(ospf->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf_ldp_sync_if_start(ifp, true); + + THREAD_TIMER_OFF(ospf->ldp_sync_cmd.t_hello); + ospf->ldp_sync_cmd.t_hello = NULL; + ospf->ldp_sync_cmd.sequence = 0; + ospf_ldp_sync_hello_timer_add(ospf); + + return 0; +} + +int ospf_ldp_sync_hello_update(struct ldp_igp_sync_hello hello) +{ + struct ospf *ospf; + struct vrf *vrf; + struct interface *ifp; + + /* if ospf is not enabled or LDP-SYNC is not configured ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || + !CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + if (hello.proto != ZEBRA_ROUTE_LDP) + return 0; + + /* Received Hello from LDP: + * if current sequence number is greater than received hello + * sequence number then assume LDP restarted + * set cost to LSInfinity + * send request to LDP for LDP-SYNC state for each interface + * else all is fine just restart hello timer + */ + if (hello.sequence == 0) + /* rolled over */ + ospf->ldp_sync_cmd.sequence = 0; + + if (ospf->ldp_sync_cmd.sequence > hello.sequence) { + zlog_err("ldp_sync: LDP restarted"); + + vrf = vrf_lookup_by_id(ospf->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf_ldp_sync_if_start(ifp, true); + } else { + THREAD_TIMER_OFF(ospf->ldp_sync_cmd.t_hello); + ospf_ldp_sync_hello_timer_add(ospf); + } + ospf->ldp_sync_cmd.sequence = hello.sequence; + + return 0; +} + +void ospf_ldp_sync_state_req_msg(struct interface *ifp) +{ + struct ldp_igp_sync_if_state_req request; + + ols_debug("ldp_sync: send state request to LDP for %s", ifp->name); + + strlcpy(request.name, ifp->name, sizeof(ifp->name)); + request.proto = LDP_IGP_SYNC_IF_STATE_REQUEST; + request.ifindex = ifp->ifindex; + + zclient_send_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST, + (uint8_t *)&request, sizeof(request)); +} + +/* + * LDP-SYNC general interface routines + */ +void ospf_ldp_sync_if_init(struct ospf_interface *oi) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + struct interface *ifp = oi->ifp; + + /* called when OSPF is configured on an interface: + * if LDP-IGP Sync is configured globally set state + * if ptop interface inform LDP LDP-SYNC is enabled + */ + if (if_is_loopback(ifp) || (ifp->vrf_id != VRF_DEFAULT) || + !(CHECK_FLAG(oi->ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE))) + return; + + ols_debug("ldp_sync: init if %s",ifp->name); + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + /* specifed on interface overrides global config. */ + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = oi->ospf->ldp_sync_cmd.holddown; + + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + + if ((params->type == OSPF_IFTYPE_POINTOPOINT || + if_is_pointopoint(ifp)) && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; +} + +void ospf_ldp_sync_if_start(struct interface *ifp, bool send_state_req) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* Start LDP-SYNC on this interface: + * set cost of interface to LSInfinity so traffic will use different + * interface until LDP has learned all labels from peer + * start holddown timer if configured + * send msg to LDP to get LDP-SYNC state + */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { + ols_debug("ldp_sync: start on if %s state: %s", + ifp->name, "Holding down until Sync"); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_if_recalculate_output_cost(ifp); + ospf_ldp_sync_holddown_timer_add(ifp); + + if (send_state_req) + ospf_ldp_sync_state_req_msg(ifp); + } +} + +void ospf_ldp_sync_if_complete(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* received sync-complete from LDP: + * set state to up + * stop timer + * restore interface cost to original value + */ + if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) { + if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP) + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + ospf_if_recalculate_output_cost(ifp); + } +} + +void ospf_ldp_sync_ldp_fail(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* LDP failed to send hello: + * stop holddown timer + * set cost of interface to LSInfinity so traffic will use different + * interface until LDP has learned all labels from peer + */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { + if (ldp_sync_info->t_holddown != NULL) { + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + } + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_if_recalculate_output_cost(ifp); + } +} + +void ospf_ldp_sync_if_down(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + if (ldp_sync_if_down(ldp_sync_info) == false) + return; + + ols_debug("ldp_sync: down on if %s", ifp->name); + + /* Interface down: + * can occur from a link down or changing config + * ospf network type change interface is brought down/up + */ + switch (ldp_sync_info->state) { + case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP: + case LDP_IGP_SYNC_STATE_REQUIRED_UP: + if (params->type != OSPF_IFTYPE_POINTOPOINT && + !if_is_pointopoint(ifp)) + /* LDP-SYNC not able to run on non-ptop interface */ + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + break; + case LDP_IGP_SYNC_STATE_NOT_REQUIRED: + if (params->type == OSPF_IFTYPE_POINTOPOINT || + if_is_pointopoint(ifp)) + /* LDP-SYNC is able to run on ptop interface */ + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + break; + default: + break; + } +} + +void ospf_ldp_sync_if_remove(struct interface *ifp, bool remove) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + return; + + ldp_sync_info = params->ldp_sync_info; + + /* Stop LDP-SYNC on this interface: + * if holddown timer is running stop it + * delete ldp instance on interface + * restore cost + */ + ols_debug("ldp_sync: Removed from if %s", ifp->name); + if (ldp_sync_info->t_holddown) + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + ospf_if_recalculate_output_cost(ifp); + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; + if (remove) { + ldp_sync_info_free((struct ldp_sync_info **)&(ldp_sync_info)); + params->ldp_sync_info = NULL; + } +} + +static int ospf_ldp_sync_ism_change(struct ospf_interface *oi, int state, + int old_state) +{ + /* Terminal state or regression */ + switch (state) { + case ISM_PointToPoint: + /* If LDP-SYNC is configure on interface then start */ + ospf_ldp_sync_if_start(oi->ifp, true); + break; + case ISM_Down: + /* If LDP-SYNC is configure on this interface then stop it */ + ospf_ldp_sync_if_down(oi->ifp); + break; + default: + break; + } + return 0; +} + +/* + * LDP-SYNC holddown timer routines + */ +static int ospf_ldp_sync_holddown_timer(struct thread *thread) +{ + struct interface *ifp; + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + /* holddown timer expired: + * didn't receive msg from LDP indicating sync-complete + * restore interface cost to original value + */ + ifp = THREAD_ARG(thread); + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info) { + ldp_sync_info = params->ldp_sync_info; + + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; + ldp_sync_info->t_holddown = NULL; + + ols_debug("ldp_sync: holddown timer expired for %s state: %s", + ifp->name, "Sync achieved"); + + ospf_if_recalculate_output_cost(ifp); + } + return 0; +} + +void ospf_ldp_sync_holddown_timer_add(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* Start holddown timer: + * this timer is used to keep interface cost at LSInfinity + * once expires returns cost to original value + * if timer is already running or holddown time is off just return + */ + if (ldp_sync_info->t_holddown || + ldp_sync_info->holddown == LDP_IGP_SYNC_HOLDDOWN_DEFAULT) + return; + + ols_debug("ldp_sync: start holddown timer for %s time %d", + ifp->name, ldp_sync_info->holddown); + + thread_add_timer(master, ospf_ldp_sync_holddown_timer, + ifp, ldp_sync_info->holddown, + &ldp_sync_info->t_holddown); +} + +/* + * LDP-SYNC hello timer routines + */ +static int ospf_ldp_sync_hello_timer(struct thread *thread) +{ + struct ospf *ospf; + struct vrf *vrf; + struct interface *ifp; + + /* hello timer expired: + * didn't receive hello msg from LDP + * set cost of all interfaces to LSInfinity + */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf) { + ospf->ldp_sync_cmd.t_hello = NULL; + vrf = vrf_lookup_by_id(ospf->vrf_id); + + FOR_ALL_INTERFACES (vrf, ifp) + ospf_ldp_sync_ldp_fail(ifp); + + zlog_err("ldp_sync: hello timer expired, LDP down"); + } + return 0; +} + +void ospf_ldp_sync_hello_timer_add(struct ospf *ospf) +{ + + /* Start hello timer: + * this timer is used to make sure LDP is up + * if expires set interface cost to LSInfinity + */ + if (!CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return; + + thread_add_timer(master, ospf_ldp_sync_hello_timer, + NULL, LDP_IGP_SYNC_HELLO_TIMEOUT, + &ospf->ldp_sync_cmd.t_hello); +} + +/* + * LDP-SYNC exit routes. + */ +void ospf_ldp_sync_gbl_exit(struct ospf *ospf, bool remove) +{ + struct interface *ifp; + struct vrf *vrf; + + /* ospf is being removed + * stop hello timer + * stop any holddown timers + */ + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + /* unregister with opaque client to recv LDP-IGP Sync msgs */ + zclient_unregister_opaque(zclient, + LDP_IGP_SYNC_IF_STATE_UPDATE); + zclient_unregister_opaque(zclient, + LDP_IGP_SYNC_ANNOUNCE_UPDATE); + zclient_unregister_opaque(zclient, LDP_IGP_SYNC_HELLO_UPDATE); + + /* disable LDP globally */ + UNSET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE); + UNSET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf->ldp_sync_cmd.holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + THREAD_TIMER_OFF(ospf->ldp_sync_cmd.t_hello); + ospf->ldp_sync_cmd.t_hello = NULL; + + /* turn off LDP-IGP Sync on all OSPF interfaces */ + vrf = vrf_lookup_by_id(ospf->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf_ldp_sync_if_remove(ifp, remove); + } +} + +/* + * LDP-SYNC routes used by set commands. + */ +void ospf_if_set_ldp_sync_enable(struct ospf *ospf, struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + /* called when setting LDP-SYNC at the global level: + * specifed on interface overrides global config + * if ptop link send msg to LDP indicating ldp-sync enabled + */ + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + ldp_sync_info = params->ldp_sync_info; + + /* config on interface, overrides global config. */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + if (ldp_sync_info->enabled != LDP_IGP_SYNC_ENABLED) + return; + + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + + ols_debug("ldp_sync: enable if %s", ifp->name); + + /* send message to LDP if ptop link */ + if (params->type == OSPF_IFTYPE_POINTOPOINT || + if_is_pointopoint(ifp)) { + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_ldp_sync_state_req_msg(ifp); + } else { + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + zlog_debug("ldp_sync: Sync only runs on P2P links %s", + ifp->name); + } +} + +void ospf_if_set_ldp_sync_holddown(struct ospf *ospf, struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + /* called when setting LDP-SYNC at the global level: + * specifed on interface overrides global config. + */ + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + ldp_sync_info = params->ldp_sync_info; + + /* config on interface, overrides global config. */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + return; + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = ospf->ldp_sync_cmd.holddown; + else + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; +} + +/* + * LDP-SYNC routines used by show commands. + */ + +void ospf_ldp_sync_show_info(struct vty *vty, struct ospf *ospf, + json_object *json_vrf, bool use_json) +{ + + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + if (use_json) { + json_object_boolean_true_add(json_vrf, + "MplsLdpIgpSyncEnabled"); + json_object_int_add(json_vrf, "MplsLdpIgpSyncHolddown", + ospf->ldp_sync_cmd.holddown); + } else { + vty_out(vty, " MPLS LDP-IGP Sync is enabled\n"); + if (ospf->ldp_sync_cmd.holddown == 0) + vty_out(vty, + " MPLS LDP-IGP Sync holddown timer is disabled\n"); + else + vty_out(vty, + " MPLS LDP-IGP Sync holddown timer %d sec\n", + ospf->ldp_sync_cmd.holddown); + } + } +} + +static void show_ip_ospf_mpls_ldp_interface_sub(struct vty *vty, + struct ospf_interface *oi, + struct interface *ifp, + json_object *json_interface_sub, + bool use_json) +{ + const char *ldp_state; + struct ospf_if_params *params; + char timebuf[OSPF_TIME_DUMP_SIZE]; + struct ldp_sync_info *ldp_sync_info; + + params = IF_DEF_PARAMS(oi->ifp); + if (params->ldp_sync_info == NULL) + return; + + ldp_sync_info = params->ldp_sync_info; + if (use_json) { + if (ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) + json_object_boolean_true_add(json_interface_sub, + "ldpIgpSyncEnabled"); + else + json_object_boolean_false_add(json_interface_sub, + "ldpIgpSyncEnabled"); + + json_object_int_add(json_interface_sub, "holdDownTimeInSec", + ldp_sync_info->holddown); + + } else { + vty_out(vty, "%-10s\n", ifp->name); + vty_out(vty, " LDP-IGP Synchronization enabled: %s\n", + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED + ? "yes" + : "no"); + vty_out(vty, " Holddown timer in seconds: %u\n", + ldp_sync_info->holddown); + } + + switch (ldp_sync_info->state) { + case LDP_IGP_SYNC_STATE_REQUIRED_UP: + if (use_json) + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + "Sync achieved"); + else + vty_out(vty, " State: Sync achieved\n"); + break; + case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP: + if (ldp_sync_info->t_holddown != NULL) { + if (use_json) { + long time_store; + + time_store = monotime_until( + &ldp_sync_info->t_holddown->u.sands, + NULL) + /1000LL; + + json_object_int_add(json_interface_sub, + "ldpIgpSyncTimeRemainInMsec", + time_store); + + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + "Holding down until Sync"); + } else { + vty_out(vty, + " Holddown timer is running %s remaining\n", + ospf_timer_dump( + ldp_sync_info->t_holddown, + timebuf, + sizeof(timebuf))); + + vty_out(vty, + " State: Holding down until Sync\n"); + } + } else { + if (use_json) + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + "Sync not achieved"); + else + vty_out(vty, " State: Sync not achieved\n"); + } + break; + case LDP_IGP_SYNC_STATE_NOT_REQUIRED: + default: + if (IF_DEF_PARAMS(ifp)->type != OSPF_IFTYPE_POINTOPOINT && + !if_is_pointopoint(ifp)) + ldp_state = "Sync not required: non-p2p link"; + else + ldp_state = "Sync not required"; + + if (use_json) + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + ldp_state); + else + vty_out(vty, " State: %s\n", ldp_state); + break; + } +} + +static int show_ip_ospf_mpls_ldp_interface_common(struct vty *vty, + struct ospf *ospf, + char *intf_name, + json_object *json, + bool use_json) +{ + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + json_object *json_interface_sub = NULL; + + if (intf_name == NULL) { + /* Show All Interfaces.*/ + FOR_ALL_INTERFACES (vrf, ifp) { + struct route_node *rn; + struct ospf_interface *oi; + + if (ospf_oi_count(ifp) == 0) + continue; + for (rn = route_top(IF_OIFS(ifp)); rn; + rn = route_next(rn)) { + oi = rn->info; + + if (use_json) { + json_interface_sub = + json_object_new_object(); + } + show_ip_ospf_mpls_ldp_interface_sub( + vty, oi, ifp, json_interface_sub, + use_json); + + if (use_json) { + json_object_object_add( + json, ifp->name, + json_interface_sub); + } + } + } + } else { + /* Interface name is specified. */ + ifp = if_lookup_by_name(intf_name, ospf->vrf_id); + if (ifp != NULL) { + struct route_node *rn; + struct ospf_interface *oi; + + if (ospf_oi_count(ifp) == 0 && !use_json) { + vty_out(vty, + " OSPF not enabled on this interface %s\n", + ifp->name); + return CMD_SUCCESS; + } + for (rn = route_top(IF_OIFS(ifp)); rn; + rn = route_next(rn)) { + oi = rn->info; + + if (use_json) + json_interface_sub = + json_object_new_object(); + + show_ip_ospf_mpls_ldp_interface_sub( + vty, oi, ifp, json_interface_sub, + use_json); + + if (use_json) { + json_object_object_add( + json, ifp->name, + json_interface_sub); + } + } + } + } + return CMD_SUCCESS; +} + +/* + * Write the global LDP-SYNC configuration. + */ +void ospf_ldp_sync_write_config(struct vty *vty, struct ospf *ospf) +{ + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + vty_out(vty, " mpls ldp-sync\n"); + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN)) + vty_out(vty, " mpls ldp-sync holddown %u\n", + ospf->ldp_sync_cmd.holddown); +} + +/* + * Write the interface LDP-SYNC configuration. + */ +void ospf_ldp_sync_if_write_config(struct vty *vty, + struct ospf_if_params *params) + +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = params->ldp_sync_info; + if (ldp_sync_info == NULL) + return; + + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) { + if (ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) + vty_out(vty, " ip ospf mpls ldp-sync\n"); + else + vty_out(vty, " no ip ospf mpls ldp-sync\n"); + } + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + vty_out(vty, " ip ospf mpls ldp-sync holddown %u\n", + ldp_sync_info->holddown); +} + +/* + * LDP-SYNC commands. + */ +#ifndef VTYSH_EXTRACT_PL +#include "ospfd/ospf_ldp_sync_clippy.c" +#endif + +DEFPY (ospf_mpls_ldp_sync, + ospf_mpls_ldp_sync_cmd, + "mpls ldp-sync", + "MPLS specific commands\n" + "Enable MPLS LDP-IGP Sync\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct interface *ifp; + + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + /* register with opaque client to recv LDP-IGP Sync msgs */ + zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_UPDATE); + zclient_register_opaque(zclient, LDP_IGP_SYNC_ANNOUNCE_UPDATE); + zclient_register_opaque(zclient, LDP_IGP_SYNC_HELLO_UPDATE); + + if (!CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + SET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE); + /* turn on LDP-IGP Sync on all ptop OSPF interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf_if_set_ldp_sync_enable(ospf, ifp); + } + return CMD_SUCCESS; +} + +DEFPY (no_ospf_mpls_ldp_sync, + no_ospf_mpls_ldp_sync_cmd, + "no mpls ldp-sync", + NO_STR + "MPLS specific commands\n" + "Disable MPLS LDP-IGP Sync\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + ospf_ldp_sync_gbl_exit(ospf, false); + return CMD_SUCCESS; +} + +DEFPY (ospf_mpls_ldp_sync_holddown, + ospf_mpls_ldp_sync_holddown_cmd, + "mpls ldp-sync holddown (1-10000)", + "MPLS specific commands\n" + "Enable MPLS LDP-IGP Sync\n" + "Set holddown timer\n" + "seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct interface *ifp; + + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + SET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf->ldp_sync_cmd.holddown = holddown; + /* set holddown time on all OSPF interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf_if_set_ldp_sync_holddown(ospf, ifp); + + return CMD_SUCCESS; +} + +DEFPY (no_ospf_mpls_ldp_sync_holddown, + no_ospf_mpls_ldp_sync_holddown_cmd, + "no mpls ldp-sync holddown [<(1-10000)>]", + NO_STR + "MPLS specific commands\n" + "Disable MPLS LDP-IGP Sync\n" + "holddown timer disable\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct interface *ifp; + + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN)) { + UNSET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf->ldp_sync_cmd.holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + /* turn off holddown timer on all OSPF interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf_if_set_ldp_sync_holddown(ospf, ifp); + } + return CMD_SUCCESS; +} + + +DEFPY (mpls_ldp_sync, + mpls_ldp_sync_cmd, + "ip ospf mpls ldp-sync", + IP_STR + "OSPF interface commands\n" + MPLS_STR + MPLS_LDP_SYNC_STR + MPLS_LDP_SYNC_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG); + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + if (params->type == OSPF_IFTYPE_POINTOPOINT || if_is_pointopoint(ifp)) { + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_ldp_sync_state_req_msg(ifp); + } else { + zlog_debug("ldp_sync: only runs on P2P links %s", ifp->name); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + } + return CMD_SUCCESS; +} + +DEFPY (no_mpls_ldp_sync, + no_mpls_ldp_sync_cmd, + "no ip ospf mpls ldp-sync", + NO_STR + IP_STR + "OSPF interface commands\n" + MPLS_STR + NO_MPLS_LDP_SYNC_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync: does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + /* disable LDP-SYNC on an interface + * stop holddown timer if running + * restore ospf cost + */ + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG); + ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + ospf_if_recalculate_output_cost(ifp); + + return CMD_SUCCESS; +} + +DEFPY (mpls_ldp_sync_holddown, + mpls_ldp_sync_holddown_cmd, + "ip ospf mpls ldp-sync holddown (0-10000)", + IP_STR + "OSPF interface commands\n" + MPLS_STR + MPLS_LDP_SYNC_STR + "Time to wait for LDP-SYNC to occur before restoring interface cost\n" + "Time in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync: does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN); + ldp_sync_info->holddown = holddown; + + return CMD_SUCCESS; +} + +DEFPY (no_mpls_ldp_sync_holddown, + no_mpls_ldp_sync_holddown_cmd, + "no ip ospf mpls ldp-sync holddown [<(1-10000)>]", + NO_STR + IP_STR + "OSPF interface commands\n" + MPLS_STR + NO_MPLS_LDP_SYNC_STR + NO_MPLS_LDP_SYNC_HOLDDOWN_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + struct ospf *ospf; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync: does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + if (ldp_sync_info == NULL) + return CMD_SUCCESS; + + /* use global configured value if set */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) { + UNSET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf && CHECK_FLAG(ospf->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = ospf->ldp_sync_cmd.holddown; + else + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + } + return CMD_SUCCESS; +} + +DEFPY (show_ip_ospf_mpls_ldp_interface, + show_ip_ospf_mpls_ldp_interface_cmd, + "show ip ospf mpls ldp-sync [interface ] [json]", + SHOW_STR + IP_STR + "OSPF information\n" + MPLS_STR + "LDP-IGP Sync information\n" + "Interface name\n" + JSON_STR) +{ + struct ospf *ospf; + bool uj = use_json(argc, argv); + char *intf_name = NULL; + int ret = CMD_SUCCESS; + int idx_intf = 0; + json_object *json = NULL; + + if (argv_find(argv, argc, "INTERFACE", &idx_intf)) + intf_name = argv[idx_intf]->arg; + + if (uj) + json = json_object_new_object(); + + /* Display default ospf (instance 0) info */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else + vty_out(vty, "%% OSPF instance not found\n"); + return CMD_SUCCESS; + } + + if (!CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else + vty_out(vty, "LDP-sync is disabled\n"); + return CMD_SUCCESS; + } + + ret = show_ip_ospf_mpls_ldp_interface_common(vty, ospf, intf_name, + json, uj); + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return ret; +} + +void ospf_ldp_sync_init(void) +{ + /* Install global ldp-igp sync commands */ + install_element(OSPF_NODE, &ospf_mpls_ldp_sync_cmd); + install_element(OSPF_NODE, &no_ospf_mpls_ldp_sync_cmd); + install_element(OSPF_NODE, &ospf_mpls_ldp_sync_holddown_cmd); + install_element(OSPF_NODE, &no_ospf_mpls_ldp_sync_holddown_cmd); + + /* Interface lsp-igp sync commands */ + install_element(INTERFACE_NODE, &mpls_ldp_sync_cmd); + install_element(INTERFACE_NODE, &no_mpls_ldp_sync_cmd); + install_element(INTERFACE_NODE, &mpls_ldp_sync_holddown_cmd); + install_element(INTERFACE_NODE, &no_mpls_ldp_sync_holddown_cmd); + + /* "show ip ospf mpls ldp interface" commands. */ + install_element(VIEW_NODE, &show_ip_ospf_mpls_ldp_interface_cmd); + + hook_register(ospf_ism_change, ospf_ldp_sync_ism_change); + +} diff --git a/ospfd/ospf_ldp_sync.h b/ospfd/ospf_ldp_sync.h new file mode 100644 index 000000000000..d4efa55311d8 --- /dev/null +++ b/ospfd/ospf_ldp_sync.h @@ -0,0 +1,56 @@ +/* + * ospf_ldp_sync.h: OSPF LDP-IGP Sync handling routines + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ZEBRA_OSPF_LDP_SYNC_H +#define _ZEBRA_OSPF_LDP_SYNC_H + +#define LDP_OSPF_LSINFINITY 65535 + +/* Macro to log debug message */ +#define ols_debug(...) \ + do { \ + if (IS_DEBUG_OSPF_LDP_SYNC) \ + zlog_debug(__VA_ARGS__); \ + } while (0) + + +extern void ospf_if_set_ldp_sync_enable(struct ospf *ospf, + struct interface *ifp); +extern void ospf_if_set_ldp_sync_holddown(struct ospf *ospf, + struct interface *ifp); +extern void ospf_ldp_sync_if_init(struct ospf_interface *ospf); +extern void ospf_ldp_sync_if_start(struct interface *ifp, bool send_state_req); +extern void ospf_ldp_sync_if_remove(struct interface *ifp, bool remove); +extern void ospf_ldp_sync_if_down(struct interface *ifp); +extern void ospf_ldp_sync_if_complete(struct interface *ifp); +extern void ospf_ldp_sync_holddown_timer_add(struct interface *ifp); +extern void ospf_ldp_sync_hello_timer_add(struct ospf *ospf); +extern void ospf_ldp_sync_ldp_fail(struct interface *ifp); +extern void ospf_ldp_sync_show_info(struct vty *vty, struct ospf *ospf, + json_object *json_vrf, bool use_json); +extern void ospf_ldp_sync_write_config(struct vty *vty, struct ospf *ospf); +extern void ospf_ldp_sync_if_write_config(struct vty *vty, + struct ospf_if_params *params); +extern int ospf_ldp_sync_state_update(struct ldp_igp_sync_if_state state); +extern int ospf_ldp_sync_announce_update(struct ldp_igp_sync_announce announce); +extern int ospf_ldp_sync_hello_update(struct ldp_igp_sync_hello hello); +extern void ospf_ldp_sync_state_req_msg(struct interface *ifp); +extern void ospf_ldp_sync_init(void); +extern void ospf_ldp_sync_gbl_exit(struct ospf *ospf, bool remove); +#endif /* _ZEBRA_OSPF_LDP_SYNC_H */ diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 45382e48d3f7..6be5486b55ac 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -54,6 +54,7 @@ #include "ospfd/ospf_vty.h" #include "ospfd/ospf_bfd.h" #include "ospfd/ospf_errors.h" +#include "ospfd/ospf_ldp_sync.h" /* ospfd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, @@ -217,6 +218,9 @@ int main(int argc, char **argv) /* OSPF BFD init */ ospf_bfd_init(); + /* OSPF LDP IGP Sync init */ + ospf_ldp_sync_init(); + ospf_route_map_init(); ospf_opaque_init(); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 8be7748c8775..9d00ff65f932 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -52,6 +52,7 @@ #include "ospfd/ospf_vty.h" #include "ospfd/ospf_dump.h" #include "ospfd/ospf_bfd.h" +#include "ospfd/ospf_ldp_sync.h" FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES, { .val_bool = true, .match_profile = "datacenter", }, @@ -3222,6 +3223,10 @@ static int show_ip_ospf_common(struct vty *vty, struct ospf *ospf, vty_out(vty, " Adjacency changes are logged\n"); } } + + /* show LDP-Sync status */ + ospf_ldp_sync_show_info(vty, ospf, json_vrf, json ? 1 : 0); + /* Show each area status. */ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) show_ip_ospf_area(vty, area, json_areas, json ? 1 : 0); @@ -9975,6 +9980,9 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) vty_out(vty, "\n"); } + /* LDP-Sync print */ + if (params && params->ldp_sync_info) + ospf_ldp_sync_if_write_config(vty, params); while (1) { if (rn == NULL) @@ -10498,6 +10506,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) ospf_opaque_config_write_router(vty, ospf); + /* LDP-Sync print */ + ospf_ldp_sync_write_config(vty, ospf); + write++; return write; } diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 8cf8430247ac..0487ace301cb 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -51,6 +51,7 @@ #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_te.h" #include "ospfd/ospf_sr.h" +#include "ospfd/ospf_ldp_sync.h" DEFINE_MTYPE_STATIC(OSPFD, OSPF_EXTERNAL, "OSPF External route table") DEFINE_MTYPE_STATIC(OSPFD, OSPF_REDISTRIBUTE, "OSPF Redistriute") @@ -1833,6 +1834,45 @@ static void ospf_zebra_connected(struct zclient *zclient) zclient_send_reg_requests(zclient, VRF_DEFAULT); } +/* + * opaque messages between processes + */ +static int ospf_opaque_msg_handler(ZAPI_CALLBACK_ARGS) +{ + struct stream *s; + struct zapi_opaque_msg info; + struct ldp_igp_sync_if_state state; + struct ldp_igp_sync_announce announce; + struct ldp_igp_sync_hello hello; + int ret = 0; + + s = zclient->ibuf; + + if (zclient_opaque_decode(s, &info) != 0) + return -1; + + switch (info.type) { + case LDP_IGP_SYNC_IF_STATE_UPDATE: + STREAM_GET(&state, s, sizeof(state)); + ret = ospf_ldp_sync_state_update(state); + break; + case LDP_IGP_SYNC_ANNOUNCE_UPDATE: + STREAM_GET(&announce, s, sizeof(announce)); + ret = ospf_ldp_sync_announce_update(announce); + break; + case LDP_IGP_SYNC_HELLO_UPDATE: + STREAM_GET(&hello, s, sizeof(hello)); + ret = ospf_ldp_sync_hello_update(hello); + break; + default: + break; + } + +stream_failure: + + return ret; +} + void ospf_zebra_init(struct thread_master *master, unsigned short instance) { /* Allocate zebra structure. */ @@ -1866,6 +1906,8 @@ void ospf_zebra_init(struct thread_master *master, unsigned short instance) access_list_delete_hook(ospf_filter_update); prefix_list_add_hook(ospf_prefix_list_update); prefix_list_delete_hook(ospf_prefix_list_update); + + zclient->opaque_msg_handler = ospf_opaque_msg_handler; } void ospf_zebra_send_arp(const struct interface *ifp, const struct prefix *p) diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 31d8417eb65c..cc5839a810c4 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -39,6 +39,7 @@ #include "libfrr.h" #include "defaults.h" #include "lib_errors.h" +#include "ldp_sync.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_network.h" @@ -57,6 +58,7 @@ #include "ospfd/ospf_abr.h" #include "ospfd/ospf_flood.h" #include "ospfd/ospf_ase.h" +#include "ospfd/ospf_ldp_sync.h" DEFINE_QOBJ_TYPE(ospf) @@ -619,6 +621,10 @@ static void ospf_finish_final(struct ospf *ospf) list_delete(&ospf->vlinks); + /* shutdown LDP-Sync */ + if (ospf->vrf_id == VRF_DEFAULT) + ospf_ldp_sync_gbl_exit(ospf, true); + /* Remove any ospf interface config params */ FOR_ALL_INTERFACES (vrf, ifp) { struct ospf_if_params *params; @@ -948,6 +954,9 @@ static void add_ospf_interface(struct connected *co, struct ospf_area *area) ospf_area_add_if(oi->area, oi); + /* if LDP-IGP Sync is configured globally inherit config */ + ospf_ldp_sync_if_init(oi); + /* * if router_id is not configured, dont bring up * interfaces. diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index e5e07875e87b..55bc64317ed3 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -25,6 +25,7 @@ #include #include "qobj.h" #include "libospf.h" +#include "ldp_sync.h" #include "filter.h" #include "log.h" @@ -320,6 +321,9 @@ struct ospf { struct list *external[ZEBRA_ROUTE_MAX + 1]; #define EXTERNAL_INFO(E) (E->external_info) + /* MPLS LDP-IGP Sync */ + struct ldp_sync_info_cmd ldp_sync_cmd; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(ospf) diff --git a/ospfd/subdir.am b/ospfd/subdir.am index 447ddf9cbbba..236b76a1f7ba 100644 --- a/ospfd/subdir.am +++ b/ospfd/subdir.am @@ -9,6 +9,7 @@ dist_examples_DATA += ospfd/ospfd.conf.sample vtysh_scan += \ ospfd/ospf_bfd.c \ ospfd/ospf_dump.c \ + ospfd/ospf_ldp_sync.c \ ospfd/ospf_opaque.c \ ospfd/ospf_ri.c \ ospfd/ospf_routemap.c \ @@ -37,6 +38,7 @@ ospfd_libfrrospf_a_SOURCES = \ ospfd/ospf_ia.c \ ospfd/ospf_interface.c \ ospfd/ospf_ism.c \ + ospfd/ospf_ldp_sync.c \ ospfd/ospf_lsa.c \ ospfd/ospf_lsdb.c \ ospfd/ospf_memory.c \ @@ -74,6 +76,7 @@ endif clippy_scan += \ ospfd/ospf_vty.c \ + ospfd/ospf_ldp_sync.c \ # end noinst_HEADERS += \ @@ -86,6 +89,7 @@ noinst_HEADERS += \ ospfd/ospf_flood.h \ ospfd/ospf_ia.h \ ospfd/ospf_interface.h \ + ospfd/ospf_ldp_sync.h \ ospfd/ospf_memory.h \ ospfd/ospf_neighbor.h \ ospfd/ospf_network.h \ diff --git a/tests/topotests/ldp-sync-isis-topo1/ce1/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/ce1/zebra.conf new file mode 100644 index 000000000000..6f165e272439 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/ce1/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce1 +! +interface ce1-eth0 + ip address 172.16.1.1/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/ce2/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/ce2/zebra.conf new file mode 100644 index 000000000000..ac02d0f9a4f7 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/ce2/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce2 +! +interface ce2-eth0 + ip address 172.16.1.2/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/ce3/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/ce3/zebra.conf new file mode 100644 index 000000000000..c6a5824d15c5 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/ce3/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce3 +! +interface ce3-eth0 + ip address 172.16.1.3/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/isisd.conf b/tests/topotests/ldp-sync-isis-topo1/r1/isisd.conf new file mode 100644 index 000000000000..af8d117bc1b3 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/isisd.conf @@ -0,0 +1,26 @@ +hostname r1 +log file isisd.log +debug isis adj-packets +debug isis events +debug isis update-packets +debug isis ldp-sync +! +router isis 1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00 + metric-style wide + redistribute ipv4 connected level-1 + redistribute ipv6 connected level-1 + mpls ldp-sync +! +interface r1-eth1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 +! +interface r1-eth2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/ldpd.conf b/tests/topotests/ldp-sync-isis-topo1/r1/ldpd.conf new file mode 100644 index 000000000000..b9c32d30006a --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/ldpd.conf @@ -0,0 +1,33 @@ +hostname r1 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 1.1.1.1 + ! + address-family ipv4 + discovery transport-address 1.1.1.1 + label local allocate host-routes + ! + ttl-security disable + ! + interface r1-eth1 + ! + interface r1-eth2 + ! + ! +! +l2vpn CUST_A type vpls + member interface r1-eth0 + ! + member pseudowire r1-mpw0 + neighbor lsr-id 2.2.2.2 + pw-id 100 + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ip_route.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ip_route.ref new file mode 100644 index 000000000000..dc8f19dad02f --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ip_route.ref @@ -0,0 +1,143 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r1-eth1" + } + ] + }, + { + "prefix":"10.0.1.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r1-eth2" + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail.ref new file mode 100644 index 000000000000..d8fb27af8cb7 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r1-eth1": [ + { + "level": "Level-1", + "metric": "10" + } + ], + "r1-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..f77d65ebc1cf --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r1-eth1": [ + { + "level": "Level-1", + "metric": "16777214" + } + ], + "r1-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..f77d65ebc1cf --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r1-eth1": [ + { + "level": "Level-1", + "metric": "16777214" + } + ], + "r1-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync.ref new file mode 100644 index 000000000000..b699e8c1454d --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync.ref @@ -0,0 +1,13 @@ +{ + "r1-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + }, + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..c63bbea77f42 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r1-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not achieved" + }, + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..c63bbea77f42 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r1-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not achieved" + }, + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_binding.ref new file mode 100644 index 000000000000..b3de7e2c666f --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_binding.ref @@ -0,0 +1,16 @@ +{ + "2.2.2.2: 100":{ + "destination":"2.2.2.2", + "vcId":100, + "localLabel":16, + "localControlWord":1, + "localVcType":"Ethernet", + "localGroupID":0, + "localIfMtu":1500, + "remoteLabel":16, + "remoteControlWord":1, + "remoteVcType":"Ethernet", + "remoteGroupID":0, + "remoteIfMtu":1500 + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_vc.ref new file mode 100644 index 000000000000..29e9df10899e --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_vc.ref @@ -0,0 +1,8 @@ +{ + "r1-mpw0":{ + "peerId":"2.2.2.2", + "vcId":100, + "VpnName":"CUST_A", + "status":"up" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_binding.ref new file mode 100644 index 000000000000..b3a12ec53f8e --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"3.3.3.3", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"3.3.3.3", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"2.2.2.2", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_discovery.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_discovery.ref new file mode 100644 index 000000000000..9301e60c6730 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_discovery.ref @@ -0,0 +1,25 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r1-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"targeted", + "peer":"2.2.2.2", + "helloHoldtime":45 + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "type":"link", + "interface":"r1-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync.ref new file mode 100644 index 000000000000..54d015fef92e --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r1-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + }, + "r1-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..2232069f6827 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r1-eth1":{ + "state":"labelExchangeNotComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"" + }, + "r1-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_neighbor.ref new file mode 100644 index 000000000000..40d8ebeb9074 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "state":"OPERATIONAL", + "transportAddress":"3.3.3.3" + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 000000000000..6138d0367243 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,42 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "r1-eth2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/r1/zebra.conf new file mode 100644 index 000000000000..ea047355ad90 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/zebra.conf @@ -0,0 +1,29 @@ +log file zebra.log +! +hostname r1 +! +debug zebra kernel +debug zebra rib detailed +debug zebra dplane detailed +debug zebra nht +debug zebra pseudowires +debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 +! +interface r1-eth0 + description to s1 +! +interface r1-eth1 + description to s4 + ip address 10.0.1.1/24 +! +interface r1-eth2 + description to s5 + ip address 10.0.2.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/isisd.conf b/tests/topotests/ldp-sync-isis-topo1/r2/isisd.conf new file mode 100644 index 000000000000..e477bce8279f --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/isisd.conf @@ -0,0 +1,27 @@ +hostname r2 +log file isisd.log +debug isis adj-packets +debug isis events +debug isis update-packets +debug isis ldp-sync +! +router isis 1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00 + metric-style wide + redistribute ipv4 connected level-1 + redistribute ipv6 connected level-1 + mpls ldp-sync +! +interface r2-eth1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 +! +interface r2-eth2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 + no isis mpls ldp-sync +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/ldpd.conf b/tests/topotests/ldp-sync-isis-topo1/r2/ldpd.conf new file mode 100644 index 000000000000..52398b1b721e --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/ldpd.conf @@ -0,0 +1,33 @@ +hostname r2 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 2.2.2.2 + ! + address-family ipv4 + discovery transport-address 2.2.2.2 + label local allocate host-routes + ! + ttl-security disable + ! + interface r2-eth1 + ! + interface r2-eth2 + ! + ! +! +l2vpn CUST_A type vpls + member interface r2-eth0 + ! + member pseudowire r2-mpw0 + neighbor lsr-id 1.1.1.1 + pw-id 100 + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/ospfd.conf b/tests/topotests/ldp-sync-isis-topo1/r2/ospfd.conf new file mode 100644 index 000000000000..f93f6aed563e --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/ospfd.conf @@ -0,0 +1,19 @@ +hostname r2 +log file ospfd.log +debug ospf zebra interface +debug ospf ldp-sync +! +router ospf + router-id 2.2.2.2 + network 0.0.0.0/0 area 0 + mpls ldp-sync + mpls ldp-sync holddown 50 +! +interface r2-eth1 + ip ospf network point-to-point + ip ospf mpls ldp-sync holddown 300 +! +interface r2-eth2 + ip ospf network point-to-point + no ip ospf mpls ldp-sync +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ip_route.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ip_route.ref new file mode 100644 index 000000000000..2bcee96064dc --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ip_route.ref @@ -0,0 +1,143 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r2-eth1" + } + ] + }, + { + "prefix":"10.0.1.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.3.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r2-eth2" + } + ] + }, + { + "prefix":"10.0.3.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail.ref new file mode 100644 index 000000000000..844aa9402a6e --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r2-eth1": [ + { + "level": "Level-1", + "metric": "10" + } + ], + "r2-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..821ec70ba58e --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r2-eth1": [ + { + "level": "Level-1", + "metric": "16777214" + } + ], + "r2-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..821ec70ba58e --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r2-eth1": [ + { + "level": "Level-1", + "metric": "16777214" + } + ], + "r2-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync.ref new file mode 100644 index 000000000000..433d89bd16ee --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync.ref @@ -0,0 +1,13 @@ +{ + "r2-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + }, + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not required" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..2f3eae47c8d5 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r2-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not achieved" + }, + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not required" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..2f3eae47c8d5 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r2-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not achieved" + }, + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not required" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_binding.ref new file mode 100644 index 000000000000..42c5a1cbd970 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_binding.ref @@ -0,0 +1,16 @@ +{ + "1.1.1.1: 100":{ + "destination":"1.1.1.1", + "vcId":100, + "localLabel":16, + "localControlWord":1, + "localVcType":"Ethernet", + "localGroupID":0, + "localIfMtu":1500, + "remoteLabel":16, + "remoteControlWord":1, + "remoteVcType":"Ethernet", + "remoteGroupID":0, + "remoteIfMtu":1500 + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_vc.ref new file mode 100644 index 000000000000..942ed23a1e28 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_vc.ref @@ -0,0 +1,8 @@ +{ + "r2-mpw0":{ + "peerId":"1.1.1.1", + "vcId":100, + "VpnName":"CUST_A", + "status":"up" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_binding.ref new file mode 100644 index 000000000000..c641fb47e680 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"3.3.3.3", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"1.1.1.1", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"3.3.3.3", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"1.1.1.1", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_discovery.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_discovery.ref new file mode 100644 index 000000000000..26801acade10 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_discovery.ref @@ -0,0 +1,25 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"link", + "interface":"r2-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"targeted", + "peer":"1.1.1.1", + "helloHoldtime":45 + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "type":"link", + "interface":"r2-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync.ref new file mode 100644 index 000000000000..f2b24d7d62fb --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r2-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r2-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..b5508dd35c17 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r2-eth1":{ + "state":"labelExchangeNotComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"" + }, + "r2-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..f2b24d7d62fb --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r2-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r2-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_neighbor.ref new file mode 100644 index 000000000000..eed35289ea6f --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "state":"OPERATIONAL", + "transportAddress":"1.1.1.1" + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "state":"OPERATIONAL", + "transportAddress":"3.3.3.3" + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 000000000000..4dd6ddd76b6b --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,42 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r2-eth1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0001", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "r2-eth2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/r2/zebra.conf new file mode 100644 index 000000000000..c24444287675 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log +! +hostname r2 +! +debug zebra rib detailed +debug zebra dplane detailed +debug zebra kernel +debug zebra nht +debug zebra pseudowires +! +interface lo + ip address 2.2.2.2/32 +! +interface r2-eth0 + description to s2 +! +interface r2-eth1 + description to s4 + ip address 10.0.1.2/24 +! +interface r2-eth2 + description to s6 + ip address 10.0.3.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/isisd.conf b/tests/topotests/ldp-sync-isis-topo1/r3/isisd.conf new file mode 100644 index 000000000000..e50fb077ba51 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/isisd.conf @@ -0,0 +1,28 @@ +hostname r3 +log file isisd.log +debug isis adj-packets +debug isis events +debug isis update-packets +debug isis ldp-sync +! +router isis 1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0003.00 + metric-style wide + redistribute ipv4 connected level-1 + redistribute ipv6 connected level-1 + mpls ldp-sync + mpls ldp-sync holddown 50 +! +interface r3-eth1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 + no isis mpls ldp-sync +! +interface r3-eth2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/ldpd.conf b/tests/topotests/ldp-sync-isis-topo1/r3/ldpd.conf new file mode 100644 index 000000000000..2935caf13bfe --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/ldpd.conf @@ -0,0 +1,25 @@ +hostname r3 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 3.3.3.3 + ! + address-family ipv4 + discovery transport-address 3.3.3.3 + label local allocate host-routes + ! + ttl-security disable + ! + interface r3-eth1 + ! + interface r3-eth2 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ip_route.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ip_route.ref new file mode 100644 index 000000000000..da46f1dfe2b8 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ip_route.ref @@ -0,0 +1,143 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.2.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r3-eth1" + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r3-eth2" + } + ] + }, + { + "prefix":"10.0.3.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail.ref new file mode 100644 index 000000000000..e323f61f255d --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r3-eth1": [ + { + "level": "Level-1", + "metric": "10" + } + ], + "r3-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..e323f61f255d --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r3-eth1": [ + { + "level": "Level-1", + "metric": "10" + } + ], + "r3-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..e323f61f255d --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r3-eth1": [ + { + "level": "Level-1", + "metric": "10" + } + ], + "r3-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync.ref new file mode 100644 index 000000000000..9cb70a4758d7 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync.ref @@ -0,0 +1,13 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..9cb70a4758d7 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..9cb70a4758d7 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_binding.ref new file mode 100644 index 000000000000..2c63c0851048 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_binding.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_vc.ref new file mode 100644 index 000000000000..2c63c0851048 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_vc.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_binding.ref new file mode 100644 index 000000000000..e54bd6e75561 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"2.2.2.2", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"1.1.1.1", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"1.1.1.1", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "inUse":0 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_discovery.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_discovery.ref new file mode 100644 index 000000000000..42fa98d4dafa --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_discovery.ref @@ -0,0 +1,18 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"link", + "interface":"r3-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r3-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync.ref new file mode 100644 index 000000000000..73261830c9c1 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r3-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r3-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..73261830c9c1 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r3-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r3-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_neighbor.ref new file mode 100644 index 000000000000..5c482da69723 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "state":"OPERATIONAL", + "transportAddress":"1.1.1.1" + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 000000000000..092219236136 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,42 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r3-eth1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0001", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "r3-eth2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/r3/zebra.conf new file mode 100644 index 000000000000..b1919bd2964a --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/zebra.conf @@ -0,0 +1,32 @@ +log file zebra.log +! +hostname r3 +! +debug zebra rib detailed +debug zebra dplane detailed +debug zebra kernel +debug zebra nht +debug zebra pseudowires +! +interface lo + ip address 3.3.3.3/32 +! +interface r3-eth0 + description to s3 +! +interface r3-eth1 + description to s5 + ip address 10.0.2.3/24 +! +interface r3-eth2 + description to s6 + ip address 10.0.3.3/24 +! +!!interface r3-eth3 +!! description to s4 +!! ip address 10.0.1.3/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.dot b/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.dot new file mode 100644 index 000000000000..4f1bd22f7c97 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.dot @@ -0,0 +1,111 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="Test Topology - LDP-VPLS 1"; + + # Routers + ce1 [ + shape=doubleoctagon, + label="ce1", + fillcolor="#f08080", + style=filled, + ]; + ce2 [ + shape=doubleoctagon + label="ce2", + fillcolor="#f08080", + style=filled, + ]; + ce3 [ + shape=doubleoctagon + label="ce3", + fillcolor="#f08080", + style=filled, + ]; + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + + + # Switches + s1 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s3 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s4 [ + shape=oval, + label="s4\n10.0.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s5 [ + shape=oval, + label="s5\n10.0.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s6 [ + shape=oval, + label="s6\n10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + ce1 -- s1 [label="eth0\n.1"]; + ce2 -- s2 [label="eth0\n.2"]; + ce3 -- s3 [label="eth0\n.3"]; + + r1 -- s1 [label="eth0"]; + r1 -- s4 [label="eth1\n.1"]; + r1 -- s5 [label="eth2\n.1"]; + + r2 -- s2 [label="eth0"]; + r2 -- s4 [label="eth1\n.2"]; + r2 -- s6 [label="eth2\n.2"]; + + r3 -- s3 [label="eth0"]; + r3 -- s5 [label="eth1\n.3"]; + r3 -- s6 [label="eth2\n.3"]; +} diff --git a/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.py b/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.py new file mode 100755 index 000000000000..1dce698c177d --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.py @@ -0,0 +1,625 @@ +#!/usr/bin/env python + +# +# test_ldp_isis_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# 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_ldp_vpls_topo1.py: + + +---------+ +---------+ + | | | | + | CE1 | | CE2 | + | | | | + +---------+ +---------+ +ce1-eth0 (172.16.1.1/24)| |ce2-eth0 (172.16.1.2/24) + | | + | | + rt1-eth0| |rt2-eth0 + +---------+ 10.0.1.0/24 +---------+ + | |rt1-eth1 | | + | RT1 +----------------+ RT2 | + | 1.1.1.1 | rt2-eth1| 2.2.2.2 | + | | | | + +---------+ +---------+ + rt1-eth2| |rt2-eth2 + | | + | | + 10.0.2.0/24| +---------+ |10.0.3.0/24 + | | | | + | | RT3 | | + +--------+ 3.3.3.3 +-------+ + rt3-eth2| |rt3-eth1 + +---------+ + |rt3-eth0 + | + | + ce3-eth0 (172.16.1.3/24)| + +---------+ + | | + | CE3 | + | | + +---------+ +""" + +import os +import re +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 + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["ce1", "ce2", "ce3", "r1", "r2", "r3"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["ce1"]) + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["ce2"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["ce3"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + # Don't start isisd and ldpd in the CE nodes + if router.name[0] == "r": + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.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): + "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()) + + # Run test function until we get an result. Wait at most 80 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def test_isis_convergence(): + logger.info("Test: check ISIS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, + "show yang operational-data /frr-interface:lib isisd", + "show_yang_interface_isis_adjacencies.ref") + +def test_rib(): + logger.info("Test: verify RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output(rname, "show ip route json", "show_ip_route.ref") + + +def test_ldp_adjacencies(): + logger.info("Test: verify LDP adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp discovery json", "show_ldp_discovery.ref" + ) + + +def test_ldp_neighbors(): + logger.info("Test: verify LDP neighbors") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref" + ) + + +def test_ldp_bindings(): + logger.info("Test: verify LDP bindings") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp binding json", "show_ldp_binding.ref" + ) + + +def test_ldp_pwid_bindings(): + logger.info("Test: verify LDP PW-ID bindings") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom binding json", "show_l2vpn_binding.ref" + ) + + +def test_ldp_pseudowires(): + logger.info("Test: verify LDP pseudowires") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref" + ) + +def test_ldp_igp_sync(): + logger.info("Test: verify LDP igp-sync") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + +def test_isis_ldp_sync(): + logger.info("Test: verify ISIS igp-sync") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_ldp_sync( + rname, "show_isis_ldp_sync.ref" + ) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_interface_detail( + rname, "show_isis_interface_detail.ref" + ) + assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff) + + +def test_r1_eth1_shutdown(): + logger.info("Test: verify behaviour after r1-eth1 is shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Shut down r1-r2 link */ + tgen = get_topogen() + tgen.gears["r1"].peer_link_enable("r1-eth1", False) + topotest.sleep(5, "Waiting for the network to reconverge") + + # check if the pseudowire is still up (using an alternate path for nexthop resolution) + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync_r1_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_ldp_sync( + rname, "show_isis_ldp_sync_r1_eth1_shutdown.ref" + ) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_interface_detail( + rname, "show_isis_interface_detail_r1_eth1_shutdown.ref" + ) + assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff) + + +def test_r1_eth1_no_shutdown(): + logger.info("Test: verify behaviour after r1-eth1 is no shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Run no shutdown on r1-eth1 interface */ + tgen = get_topogen() + tgen.gears["r1"].peer_link_enable("r1-eth1", True) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_ldp_sync( + rname, "show_isis_ldp_sync.ref" + ) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_interface_detail( + rname, "show_isis_interface_detail.ref" + ) + assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff) + + +def test_r2_eth1_shutdown(): + logger.info("Test: verify behaviour after r2-eth1 is shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Shut down r1-r2 link */ + tgen = get_topogen() + tgen.gears["r2"].peer_link_enable("r2-eth1", False) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync_r1_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_ldp_sync( + rname, "show_isis_ldp_sync_r2_eth1_shutdown.ref" + ) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_interface_detail( + rname, "show_isis_interface_detail_r2_eth1_shutdown.ref" + ) + assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff) + + +def test_r2_eth1_no_shutdown(): + logger.info("Test: verify behaviour after r2-eth1 is no shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Run no shutdown on r2-eth1 interface */ + tgen = get_topogen() + tgen.gears["r2"].peer_link_enable("r2-eth1", True) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_ldp_sync( + rname, "show_isis_ldp_sync.ref" + ) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_interface_detail( + rname, "show_isis_interface_detail.ref" + ) + assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff) + + +# 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)) + + +# +# Auxiliary functions +# + +def parse_show_isis_ldp_sync(lines, rname): + """ + Parse the output of 'show isis mpls ldp sync' into a Python dict. + """ + interfaces = {} + + it = iter(lines) + + while True: + try: + interface = {} + interface_name = None + + line = it.next(); + + if line.startswith(rname + "-eth"): + interface_name = line + + line = it.next(); + + if line.startswith(" LDP-IGP Synchronization enabled: "): + interface["ldpIgpSyncEnabled"] = line.endswith("yes") + + line = it.next(); + + if line.startswith(" holddown timer in seconds: "): + interface["holdDownTimeInSec"] = int(line.split(": ")[-1]) + + line = it.next(); + + if line.startswith(" State: "): + interface["ldpIgpSyncState"] = line.split(": ")[-1] + + interfaces[interface_name] = interface + + except StopIteration: + break + + return interfaces + + +def show_isis_ldp_sync(router, rname): + """ + Get the show isis mpls ldp-sync info in a dictionary format. + + """ + out = topotest.normalize_text( + router.vtysh_cmd("show isis mpls ldp-sync") + ).splitlines() + + parsed = parse_show_isis_ldp_sync(out, rname) + + return parsed + + +def validate_show_isis_ldp_sync(rname, fname): + tgen = get_topogen() + + filename = "{0}/{1}/{2}".format(CWD, rname, fname) + expected = json.loads(open(filename).read()) + + router = tgen.gears[rname] + + def compare_isis_ldp_sync(router, expected): + "Helper function to test show isis mpls ldp-sync" + actual = show_isis_ldp_sync(router, rname) + return topotest.json_cmp(actual, expected) + + test_func = partial(compare_isis_ldp_sync, router, expected) + (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=160) + + return (result, diff) + + +def parse_show_isis_interface_detail(lines, rname): + """ + Parse the output of 'show isis interface detail' into a Python dict. + """ + areas = {} + area_id = None + + it = iter(lines) + + while True: + try: + line = it.next(); + + area_match = re.match(r"Area (.+):", line) + if not area_match: + continue + + area_id = area_match.group(1) + area = {} + + line = it.next(); + + while line.startswith(" Interface: "): + interface_name = re.split(':|,', line)[1].lstrip() + + area[interface_name]= [] + + # Look for keyword: Level-1 or Level-2 + while not line.startswith(" Level-"): + line = it.next(); + + while line.startswith(" Level-"): + + level = {} + + level_name = line.split()[0] + level['level'] = level_name + + line = it.next(); + + if line.startswith(" Metric:"): + level['metric'] = re.split(':|,', line)[1].lstrip() + + area[interface_name].append(level) + + # Look for keyword: Level-1 or Level-2 or Interface: + while not line.startswith(" Level-") and not line.startswith(" Interface: "): + line = it.next(); + + if line.startswith(" Level-"): + continue + + if line.startswith(" Interface: "): + break + + areas[area_id] = area + + except StopIteration: + + areas[area_id] = area + break + + return areas + + +def show_isis_interface_detail(router, rname): + """ + Get the show isis mpls ldp-sync info in a dictionary format. + + """ + out = topotest.normalize_text( + router.vtysh_cmd("show isis interface detail") + ).splitlines() + + logger.warning(out) + + parsed = parse_show_isis_interface_detail(out, rname) + + logger.warning(parsed) + + return parsed + + +def validate_show_isis_interface_detail(rname, fname): + tgen = get_topogen() + + filename = "{0}/{1}/{2}".format(CWD, rname, fname) + expected = json.loads(open(filename).read()) + + router = tgen.gears[rname] + + def compare_isis_interface_detail(router, expected): + "Helper function to test show isis interface detail" + actual = show_isis_interface_detail(router, rname) + return topotest.json_cmp(actual, expected) + + test_func = partial(compare_isis_interface_detail, router, expected) + (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=160) + + return (result, diff) + diff --git a/tests/topotests/ldp-sync-ospf-topo1/ce1/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/ce1/zebra.conf new file mode 100644 index 000000000000..6f165e272439 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/ce1/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce1 +! +interface ce1-eth0 + ip address 172.16.1.1/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/ce2/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/ce2/zebra.conf new file mode 100644 index 000000000000..ac02d0f9a4f7 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/ce2/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce2 +! +interface ce2-eth0 + ip address 172.16.1.2/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/ce3/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/ce3/zebra.conf new file mode 100644 index 000000000000..c6a5824d15c5 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/ce3/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce3 +! +interface ce3-eth0 + ip address 172.16.1.3/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/ldpd.conf b/tests/topotests/ldp-sync-ospf-topo1/r1/ldpd.conf new file mode 100644 index 000000000000..b9c32d30006a --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/ldpd.conf @@ -0,0 +1,33 @@ +hostname r1 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 1.1.1.1 + ! + address-family ipv4 + discovery transport-address 1.1.1.1 + label local allocate host-routes + ! + ttl-security disable + ! + interface r1-eth1 + ! + interface r1-eth2 + ! + ! +! +l2vpn CUST_A type vpls + member interface r1-eth0 + ! + member pseudowire r1-mpw0 + neighbor lsr-id 2.2.2.2 + pw-id 100 + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/ospf-nbrs.txt b/tests/topotests/ldp-sync-ospf-topo1/r1/ospf-nbrs.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/ospfd.conf b/tests/topotests/ldp-sync-ospf-topo1/r1/ospfd.conf new file mode 100644 index 000000000000..eefcd1e71c84 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/ospfd.conf @@ -0,0 +1,20 @@ +hostname r1 +log file ospfd.log +debug ospf zebra interface +debug ospf ldp-sync +! +router ospf + router-id 1.1.1.1 + network 10.0.1.1/24 area 0 + network 10.0.2.1/24 area 0 + network 1.1.1.1/32 area 0 + mpls ldp-sync + ! mpls ldp-sync holddown 50 +! +interface r1-eth1 + ip ospf network point-to-point + ! ip ospf mpls ldp-sync holddown 40 +! +interface r1-eth2 + ip ospf network point-to-point +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface.ref new file mode 100644 index 000000000000..8b2884786d9d --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r1-eth1":{ + "cost":10 + }, + "r1-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..b1a263e42247 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref @@ -0,0 +1,8 @@ +{ + "interfaces":{ + "r1-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..0c147338e389 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r1-eth1":{ + "cost":65535 + }, + "r1-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_neighbor.json new file mode 100644 index 000000000000..3bfda390714c --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_neighbor.json @@ -0,0 +1,26 @@ +{ + "neighbors": { + "2.2.2.2": [ + { + "dbSummaryCounter": 0, + "retransmitCounter": 0, + "priority": 1, + "state": "Full/DROther", + "address": "10.0.1.2", + "ifaceName": "r1-eth1:10.0.1.1", + "requestCounter": 0 + } + ], + "3.3.3.3": [ + { + "dbSummaryCounter": 0, + "retransmitCounter": 0, + "priority": 1, + "state": "Full/DROther", + "address": "10.0.2.3", + "ifaceName": "r1-eth2:10.0.2.1", + "requestCounter": 0 + } + ] + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_route.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_route.ref new file mode 100644 index 000000000000..8ccd60ca4122 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_route.ref @@ -0,0 +1,157 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"1.1.1.1/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ], + "10.0.1.0/24":[ + { + "prefix":"10.0.1.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + } + ] + }, + { + "prefix":"10.0.1.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "10.0.2.0/24":[ + { + "prefix":"10.0.2.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + }, + { + "prefix":"10.0.2.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ], + "10.0.3.0/24":[ + { + "prefix":"10.0.3.0/24", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":20, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_binding.ref new file mode 100644 index 000000000000..b3de7e2c666f --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_binding.ref @@ -0,0 +1,16 @@ +{ + "2.2.2.2: 100":{ + "destination":"2.2.2.2", + "vcId":100, + "localLabel":16, + "localControlWord":1, + "localVcType":"Ethernet", + "localGroupID":0, + "localIfMtu":1500, + "remoteLabel":16, + "remoteControlWord":1, + "remoteVcType":"Ethernet", + "remoteGroupID":0, + "remoteIfMtu":1500 + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_vc.ref new file mode 100644 index 000000000000..29e9df10899e --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_vc.ref @@ -0,0 +1,8 @@ +{ + "r1-mpw0":{ + "peerId":"2.2.2.2", + "vcId":100, + "VpnName":"CUST_A", + "status":"up" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_binding.ref new file mode 100644 index 000000000000..b3a12ec53f8e --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"3.3.3.3", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"3.3.3.3", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"2.2.2.2", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_discovery.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_discovery.ref new file mode 100644 index 000000000000..9301e60c6730 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_discovery.ref @@ -0,0 +1,25 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r1-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"targeted", + "peer":"2.2.2.2", + "helloHoldtime":45 + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "type":"link", + "interface":"r1-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync.ref new file mode 100644 index 000000000000..54d015fef92e --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r1-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + }, + "r1-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..2232069f6827 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r1-eth1":{ + "state":"labelExchangeNotComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"" + }, + "r1-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_neighbor.ref new file mode 100644 index 000000000000..40d8ebeb9074 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "state":"OPERATIONAL", + "transportAddress":"3.3.3.3" + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync.ref new file mode 100644 index 000000000000..3782071bf9bb --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync.ref @@ -0,0 +1,12 @@ +{ + "r1-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + }, + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..c2642c64836f --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,7 @@ +{ + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..6f180b048a3d --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,12 @@ +{ + "r1-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not achieved" + }, + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/r1/zebra.conf new file mode 100644 index 000000000000..ea047355ad90 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/zebra.conf @@ -0,0 +1,29 @@ +log file zebra.log +! +hostname r1 +! +debug zebra kernel +debug zebra rib detailed +debug zebra dplane detailed +debug zebra nht +debug zebra pseudowires +debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 +! +interface r1-eth0 + description to s1 +! +interface r1-eth1 + description to s4 + ip address 10.0.1.1/24 +! +interface r1-eth2 + description to s5 + ip address 10.0.2.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/ldpd.conf b/tests/topotests/ldp-sync-ospf-topo1/r2/ldpd.conf new file mode 100644 index 000000000000..52398b1b721e --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/ldpd.conf @@ -0,0 +1,33 @@ +hostname r2 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 2.2.2.2 + ! + address-family ipv4 + discovery transport-address 2.2.2.2 + label local allocate host-routes + ! + ttl-security disable + ! + interface r2-eth1 + ! + interface r2-eth2 + ! + ! +! +l2vpn CUST_A type vpls + member interface r2-eth0 + ! + member pseudowire r2-mpw0 + neighbor lsr-id 1.1.1.1 + pw-id 100 + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/ospfd.conf b/tests/topotests/ldp-sync-ospf-topo1/r2/ospfd.conf new file mode 100644 index 000000000000..f93f6aed563e --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/ospfd.conf @@ -0,0 +1,19 @@ +hostname r2 +log file ospfd.log +debug ospf zebra interface +debug ospf ldp-sync +! +router ospf + router-id 2.2.2.2 + network 0.0.0.0/0 area 0 + mpls ldp-sync + mpls ldp-sync holddown 50 +! +interface r2-eth1 + ip ospf network point-to-point + ip ospf mpls ldp-sync holddown 300 +! +interface r2-eth2 + ip ospf network point-to-point + no ip ospf mpls ldp-sync +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface.ref new file mode 100644 index 000000000000..82806721e76b --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r2-eth1":{ + "cost":10 + }, + "r2-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..71e8af177872 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r2-eth1":{ + "cost":65535 + }, + "r2-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..af9a9c80e516 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref @@ -0,0 +1,8 @@ +{ + "interfaces":{ + "r2-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_neighbor.json new file mode 100644 index 000000000000..5b7a5ebbb998 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_neighbor.json @@ -0,0 +1,26 @@ +{ + "neighbors": { + "1.1.1.1": [ + { + "priority":1, + "state":"Full/DROther", + "address":"10.0.1.1", + "ifaceName":"r2-eth1:10.0.1.2", + "retransmitCounter":0, + "requestCounter":0, + "dbSummaryCounter":0 + } + ], + "3.3.3.3": [ + { + "priority":1, + "state":"Full/DROther", + "address":"10.0.3.3", + "ifaceName":"r2-eth2:10.0.3.2", + "retransmitCounter":0, + "requestCounter":0, + "dbSummaryCounter":0 + } + ] + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_route.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_route.ref new file mode 100644 index 000000000000..7147c6a59587 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_route.ref @@ -0,0 +1,157 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"2.2.2.2/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ], + "10.0.1.0/24":[ + { + "prefix":"10.0.1.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + } + ] + }, + { + "prefix":"10.0.1.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "10.0.2.0/24":[ + { + "prefix":"10.0.2.0/24", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":20, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ], + "10.0.3.0/24":[ + { + "prefix":"10.0.3.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + }, + { + "prefix":"10.0.3.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_binding.ref new file mode 100644 index 000000000000..42c5a1cbd970 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_binding.ref @@ -0,0 +1,16 @@ +{ + "1.1.1.1: 100":{ + "destination":"1.1.1.1", + "vcId":100, + "localLabel":16, + "localControlWord":1, + "localVcType":"Ethernet", + "localGroupID":0, + "localIfMtu":1500, + "remoteLabel":16, + "remoteControlWord":1, + "remoteVcType":"Ethernet", + "remoteGroupID":0, + "remoteIfMtu":1500 + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_vc.ref new file mode 100644 index 000000000000..942ed23a1e28 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_vc.ref @@ -0,0 +1,8 @@ +{ + "r2-mpw0":{ + "peerId":"1.1.1.1", + "vcId":100, + "VpnName":"CUST_A", + "status":"up" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_binding.ref new file mode 100644 index 000000000000..c641fb47e680 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"3.3.3.3", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"1.1.1.1", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"3.3.3.3", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"1.1.1.1", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_discovery.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_discovery.ref new file mode 100644 index 000000000000..26801acade10 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_discovery.ref @@ -0,0 +1,25 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"link", + "interface":"r2-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"targeted", + "peer":"1.1.1.1", + "helloHoldtime":45 + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "type":"link", + "interface":"r2-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync.ref new file mode 100644 index 000000000000..f2b24d7d62fb --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r2-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r2-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..b5508dd35c17 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r2-eth1":{ + "state":"labelExchangeNotComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"" + }, + "r2-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_neighbor.ref new file mode 100644 index 000000000000..eed35289ea6f --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "state":"OPERATIONAL", + "transportAddress":"1.1.1.1" + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "state":"OPERATIONAL", + "transportAddress":"3.3.3.3" + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync.ref new file mode 100644 index 000000000000..6c27a1042760 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync.ref @@ -0,0 +1,12 @@ +{ + "r2-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":300, + "ldpIgpSyncState":"Sync achieved" + }, + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..889f69ed7f40 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,12 @@ +{ + "r2-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":300, + "ldpIgpSyncState":"Holding down until Sync" + }, + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..d9036e124bca --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,7 @@ +{ + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/r2/zebra.conf new file mode 100644 index 000000000000..c24444287675 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log +! +hostname r2 +! +debug zebra rib detailed +debug zebra dplane detailed +debug zebra kernel +debug zebra nht +debug zebra pseudowires +! +interface lo + ip address 2.2.2.2/32 +! +interface r2-eth0 + description to s2 +! +interface r2-eth1 + description to s4 + ip address 10.0.1.2/24 +! +interface r2-eth2 + description to s6 + ip address 10.0.3.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/ldpd.conf b/tests/topotests/ldp-sync-ospf-topo1/r3/ldpd.conf new file mode 100644 index 000000000000..2935caf13bfe --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/ldpd.conf @@ -0,0 +1,25 @@ +hostname r3 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 3.3.3.3 + ! + address-family ipv4 + discovery transport-address 3.3.3.3 + label local allocate host-routes + ! + ttl-security disable + ! + interface r3-eth1 + ! + interface r3-eth2 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/ospfd.conf b/tests/topotests/ldp-sync-ospf-topo1/r3/ospfd.conf new file mode 100644 index 000000000000..09eea759adaf --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/ospfd.conf @@ -0,0 +1,18 @@ +hostname r3 +log file ospfd.log +debug ospf zebra interface +debug ospf ldp-sync +! +router ospf + router-id 3.3.3.3 + network 0.0.0.0/0 area 0 + mpls ldp-sync + mpls ldp-sync holddown 50 +! +interface r3-eth1 + ip ospf network point-to-point + no ip ospf mpls ldp-sync +! +interface r3-eth2 + ip ospf network point-to-point +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface.ref new file mode 100644 index 000000000000..aec97b30cb11 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r3-eth1":{ + "cost":10 + }, + "r3-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..aec97b30cb11 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r3-eth1":{ + "cost":10 + }, + "r3-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..aec97b30cb11 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r3-eth1":{ + "cost":10 + }, + "r3-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_neighbor.json new file mode 100644 index 000000000000..1b29b9f94788 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_neighbor.json @@ -0,0 +1,26 @@ +{ + "neighbors": { + "1.1.1.1": [ + { + "priority":1, + "state":"Full/DROther", + "address":"10.0.2.1", + "ifaceName":"r3-eth1:10.0.2.3", + "retransmitCounter":0, + "requestCounter":0, + "dbSummaryCounter":0 + } + ], + "2.2.2.2": [ + { + "priority":1, + "state":"Full/DROther", + "address":"10.0.3.2", + "ifaceName":"r3-eth2:10.0.3.3", + "retransmitCounter":0, + "requestCounter":0, + "dbSummaryCounter":0 + } + ] + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_route.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_route.ref new file mode 100644 index 000000000000..d77de7c9e3b8 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_route.ref @@ -0,0 +1,157 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"3.3.3.3/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "10.0.1.0/24":[ + { + "prefix":"10.0.1.0/24", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":20, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ], + "10.0.2.0/24":[ + { + "prefix":"10.0.2.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + } + ] + }, + { + "prefix":"10.0.2.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "10.0.3.0/24":[ + { + "prefix":"10.0.3.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + }, + { + "prefix":"10.0.3.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_binding.ref new file mode 100644 index 000000000000..2c63c0851048 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_binding.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_vc.ref new file mode 100644 index 000000000000..2c63c0851048 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_vc.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_binding.ref new file mode 100644 index 000000000000..e54bd6e75561 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"2.2.2.2", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"1.1.1.1", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"1.1.1.1", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "inUse":0 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_discovery.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_discovery.ref new file mode 100644 index 000000000000..42fa98d4dafa --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_discovery.ref @@ -0,0 +1,18 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"link", + "interface":"r3-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r3-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync.ref new file mode 100644 index 000000000000..73261830c9c1 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r3-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r3-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..73261830c9c1 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r3-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r3-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_neighbor.ref new file mode 100644 index 000000000000..5c482da69723 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "state":"OPERATIONAL", + "transportAddress":"1.1.1.1" + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync.ref new file mode 100644 index 000000000000..b417ab040ab5 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync.ref @@ -0,0 +1,12 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 000000000000..b417ab040ab5 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,12 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 000000000000..b417ab040ab5 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,12 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/r3/zebra.conf new file mode 100644 index 000000000000..b1919bd2964a --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/zebra.conf @@ -0,0 +1,32 @@ +log file zebra.log +! +hostname r3 +! +debug zebra rib detailed +debug zebra dplane detailed +debug zebra kernel +debug zebra nht +debug zebra pseudowires +! +interface lo + ip address 3.3.3.3/32 +! +interface r3-eth0 + description to s3 +! +interface r3-eth1 + description to s5 + ip address 10.0.2.3/24 +! +interface r3-eth2 + description to s6 + ip address 10.0.3.3/24 +! +!!interface r3-eth3 +!! description to s4 +!! ip address 10.0.1.3/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.dot b/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.dot new file mode 100644 index 000000000000..4f1bd22f7c97 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.dot @@ -0,0 +1,111 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="Test Topology - LDP-VPLS 1"; + + # Routers + ce1 [ + shape=doubleoctagon, + label="ce1", + fillcolor="#f08080", + style=filled, + ]; + ce2 [ + shape=doubleoctagon + label="ce2", + fillcolor="#f08080", + style=filled, + ]; + ce3 [ + shape=doubleoctagon + label="ce3", + fillcolor="#f08080", + style=filled, + ]; + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + + + # Switches + s1 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s3 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s4 [ + shape=oval, + label="s4\n10.0.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s5 [ + shape=oval, + label="s5\n10.0.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s6 [ + shape=oval, + label="s6\n10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + ce1 -- s1 [label="eth0\n.1"]; + ce2 -- s2 [label="eth0\n.2"]; + ce3 -- s3 [label="eth0\n.3"]; + + r1 -- s1 [label="eth0"]; + r1 -- s4 [label="eth1\n.1"]; + r1 -- s5 [label="eth2\n.1"]; + + r2 -- s2 [label="eth0"]; + r2 -- s4 [label="eth1\n.2"]; + r2 -- s6 [label="eth2\n.2"]; + + r3 -- s3 [label="eth0"]; + r3 -- s5 [label="eth1\n.3"]; + r3 -- s6 [label="eth2\n.3"]; +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.py b/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.py new file mode 100755 index 000000000000..dc6a76eb490b --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.py @@ -0,0 +1,429 @@ +#!/usr/bin/env python + +# +# test_ldp_ospf_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# 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_ldp_vpls_topo1.py: + + +---------+ +---------+ + | | | | + | CE1 | | CE2 | + | | | | + +---------+ +---------+ +ce1-eth0 (172.16.1.1/24)| |ce2-eth0 (172.16.1.2/24) + | | + | | + rt1-eth0| |rt2-eth0 + +---------+ 10.0.1.0/24 +---------+ + | |rt1-eth1 | | + | RT1 +----------------+ RT2 | + | 1.1.1.1 | rt2-eth1| 2.2.2.2 | + | | | | + +---------+ +---------+ + rt1-eth2| |rt2-eth2 + | | + | | + 10.0.2.0/24| +---------+ |10.0.3.0/24 + | | | | + | | RT3 | | + +--------+ 3.3.3.3 +-------+ + rt3-eth2| |rt3-eth1 + +---------+ + |rt3-eth0 + | + | + ce3-eth0 (172.16.1.3/24)| + +---------+ + | | + | CE3 | + | | + +---------+ +""" + +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 + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["ce1", "ce2", "ce3", "r1", "r2", "r3"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["ce1"]) + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["ce2"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["ce3"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + # Don't start ospfd and ldpd in the CE nodes + if router.name[0] == "r": + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.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): + "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()) + + # Run test function until we get an result. Wait at most 80 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def test_ospf_convergence(): + logger.info("Test: check OSPF adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json" + ) + + +def test_rib(): + logger.info("Test: verify RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output(rname, "show ip route json", "show_ip_route.ref") + + +def test_ldp_adjacencies(): + logger.info("Test: verify LDP adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp discovery json", "show_ldp_discovery.ref" + ) + + +def test_ldp_neighbors(): + logger.info("Test: verify LDP neighbors") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref" + ) + + +def test_ldp_bindings(): + logger.info("Test: verify LDP bindings") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp binding json", "show_ldp_binding.ref" + ) + + +def test_ldp_pwid_bindings(): + logger.info("Test: verify LDP PW-ID bindings") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom binding json", "show_l2vpn_binding.ref" + ) + + +def test_ldp_pseudowires(): + logger.info("Test: verify LDP pseudowires") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref" + ) + +def test_ldp_igp_sync(): + logger.info("Test: verify LDP igp-sync") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + +def test_ospf_ldp_sync(): + logger.info("Test: verify OSPF igp-sync") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf interface json", "show_ip_ospf_interface.ref" + ) + + +def test_r1_eth1_shutdown(): + logger.info("Test: verify behaviour after r1-eth1 is shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Shut down r1-r2 link */ + tgen = get_topogen() + tgen.gears["r1"].peer_link_enable("r1-eth1", False) + topotest.sleep(5, "Waiting for the network to reconverge") + + # check if the pseudowire is still up (using an alternate path for nexthop resolution) + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync_r1_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync_r1_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf interface json", "show_ip_ospf_interface_r1_eth1_shutdown.ref" + ) + +def test_r1_eth1_no_shutdown(): + logger.info("Test: verify behaviour after r1-eth1 is no shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Run no shutdown on r1-eth1 interface */ + tgen = get_topogen() + tgen.gears["r1"].peer_link_enable("r1-eth1", True) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf interface json", "show_ip_ospf_interface.ref" + ) + +def test_r2_eth1_shutdown(): + logger.info("Test: verify behaviour after r2-eth1 is shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Shut down r1-r2 link */ + tgen = get_topogen() + tgen.gears["r2"].peer_link_enable("r2-eth1", False) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync_r1_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync_r2_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf interface json", "show_ip_ospf_interface_r2_eth1_shutdown.ref" + ) + +def test_r2_eth1_no_shutdown(): + logger.info("Test: verify behaviour after r2-eth1 is no shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Run no shutdown on r2-eth1 interface */ + tgen = get_topogen() + tgen.gears["r2"].peer_link_enable("r2-eth1", True) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf interface json", "show_ip_ospf_interface.ref" + ) + +# 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/yang/frr-isisd.yang b/yang/frr-isisd.yang index 00296516efe8..cc959bd9fe28 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -612,6 +612,26 @@ module frr-isisd { "IPv6 destination-source topology."; } } + + container mpls { + description + "Configuration of MPLS parameters"; + leaf ldp-sync { + type boolean; + default "true"; + description + "Enable MPLS LDP-Sync functionality on this circuit."; + } + leaf holddown { + type uint16 { + range "0..10000"; + } + units "seconds"; + description + "Time to wait for LDP-Sync to occur before restoring interface metric."; + } + } + } grouping adjacency-state { @@ -1324,6 +1344,26 @@ module frr-isisd { } } } + + container mpls { + description + "Configuration of MPLS parameters"; + container ldp-sync { + presence "Present if MPLS LDP-Sync is enabled."; + description + "Enable MPLS LDP-Sync functionality."; + leaf holddown { + type uint16 { + range "0..10000"; + } + units "seconds"; + default "0"; + description + "Time to wait for LDP-Sync to occur before restoring interface metric."; + } + } + } + } }