diff options
author | lynne <lynne@voltanet.io> | 2020-07-22 20:32:35 +0200 |
---|---|---|
committer | lynne <lynne@voltanet.io> | 2020-09-09 20:38:44 +0200 |
commit | 1cbf96a8adf020ce6107ce26fb065d44373e14ce (patch) | |
tree | 95fee3dcf2509698b274c76586944eba57fd9cb0 | |
parent | ospfd: ldp-igp-sync feature: adding ospf support (diff) | |
download | frr-1cbf96a8adf020ce6107ce26fb065d44373e14ce.tar.xz frr-1cbf96a8adf020ce6107ce26fb065d44373e14ce.zip |
isisd: ldp-igp-sync feature: adding isis support
Signed-off-by: Lynne Morrison <lynne@voltanet.io>
Signed-off-by: Karen Schoener <karen@voltanet.io>
-rw-r--r-- | isisd/isis_circuit.c | 17 | ||||
-rw-r--r-- | isisd/isis_circuit.h | 1 | ||||
-rw-r--r-- | isisd/isis_cli.c | 180 | ||||
-rw-r--r-- | isisd/isis_ldp_sync.c | 788 | ||||
-rw-r--r-- | isisd/isis_ldp_sync.h | 54 | ||||
-rw-r--r-- | isisd/isis_main.c | 2 | ||||
-rw-r--r-- | isisd/isis_nb.c | 30 | ||||
-rw-r--r-- | isisd/isis_nb.h | 16 | ||||
-rw-r--r-- | isisd/isis_nb_config.c | 246 | ||||
-rw-r--r-- | isisd/isis_zebra.c | 41 | ||||
-rw-r--r-- | isisd/isisd.c | 44 | ||||
-rw-r--r-- | isisd/isisd.h | 5 | ||||
-rw-r--r-- | isisd/subdir.am | 3 | ||||
-rw-r--r-- | yang/frr-isisd.yang | 40 |
14 files changed, 1457 insertions, 10 deletions
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 985e07820..80d70095c 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) @@ -1276,6 +1277,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; } @@ -1346,11 +1348,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 da358f411..5766d1962 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 4d0275800..6b4df7809 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -2320,6 +2320,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); @@ -2423,6 +2595,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 000000000..42928e069 --- /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 <zebra.h> +#include <string.h> + +#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 <lib/json.h> +#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 <INTERFACE|all>]", + 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 000000000..6017cdf00 --- /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 ed4b20685..fb173eae6 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" @@ -265,6 +266,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 2b8b02e3f..82491acd8 100644 --- a/isisd/isis_nb.c +++ b/isisd/isis_nb.c @@ -538,6 +538,21 @@ const struct frr_yang_module_info frr_isisd_info = { }, }, { + .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 = { .create = lib_interface_isis_create, @@ -899,6 +914,21 @@ const struct frr_yang_module_info frr_isisd_info = { } }, { + .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 a9401bc86..db470d138 100644 --- a/isisd/isis_nb.h +++ b/isisd/isis_nb.h @@ -205,6 +205,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( @@ -249,6 +252,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( @@ -433,6 +439,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 ffc3d5b2e..a2ddc4057 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 @@ -79,6 +84,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; } @@ -1824,6 +1833,113 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_mo } /* + * 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 */ int lib_interface_isis_create(struct nb_cb_create_args *args) @@ -1904,6 +2020,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 @@ -2597,3 +2716,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 3aa21a9ae..e31372d64 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; @@ -582,6 +583,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. */ @@ -608,6 +647,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 0d39aba20..438d87d9f 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -75,6 +75,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) @@ -1087,6 +1088,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, @@ -1124,6 +1127,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; } @@ -1192,6 +1197,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; @@ -1548,11 +1557,32 @@ DEFUN (no_debug_isis_bfd, return CMD_SUCCESS; } -DEFUN(show_hostname, show_hostname_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] 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 *nnode, *inode; const char *vrf_name = VRF_DEFAULT_NAME; @@ -2710,6 +2740,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); @@ -2737,6 +2769,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 41b69df2b..02b5990f1 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; @@ -268,6 +270,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) @@ -282,6 +285,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) @@ -297,6 +301,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 9e855ad8c..50d474653 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/yang/frr-isisd.yang b/yang/frr-isisd.yang index 1bb693a1e..c0f128f3d 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -605,6 +605,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 { @@ -1311,6 +1331,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."; + } + } + } + } } |