Commit b84a17ebdd for frr
commit b84a17ebdd82e17010d944839655b76165d96f39
Author: Yoann Lopez <yoann.lopez@gmail.com>
Date: Mon Nov 2 14:12:29 2026 +0100
isisd: add an option to disable periodic CSNPs on p2p circuits
A point-to-point circuit sends a complete set of CSNPs every
csnp-interval, and this cannot be turned off. Each CSNP lists every
LSP, so it is a steady cost even when nothing changes.
Add "no isis periodic-csnp": on a point-to-point circuit, only the
CSNP sent when an adjacency comes up is kept (RFC 5303 section 1);
flooding stays reliable through PSNP acknowledgements. Broadcast
circuits are not affected, and the default is unchanged. Warn when a
setting has no effect: csnp-interval on a point-to-point circuit with
periodic CSNPs disabled, or no periodic-csnp on a broadcast circuit.
Assisted-by: Claude <noreply@anthropic.com>
Signed-off-by: Yoann Lopez <yoann.lopez@gmail.com>
diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h
index dc396666ef..2f82ef28a2 100644
--- a/isisd/isis_circuit.h
+++ b/isisd/isis_circuit.h
@@ -148,6 +148,8 @@ struct isis_circuit {
#define ISIS_CIRCUIT_IF_DOWN_FROM_Z 0x02
uint8_t flags;
bool disable_threeway_adj;
+ /* p2p: CSNPs only when an adjacency comes up */
+ bool disable_p2p_periodic_csnp;
/* route IPv4 over IPv6 link-local nexthops where IPv4 has none? */
bool ipv4_over_ipv6_nexthop;
struct {
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index 038d82acf2..965f03873d 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -107,6 +107,45 @@ static const struct lyd_node *isis_iface_cfg_dnode(struct vty *vty)
return yang_dnode_getf(vty->candidate_config->dnode, "%s/frr-isisd:isis", VTY_CURR_XPATH);
}
+/* Say that the CSNP interval is unused on p2p while periodic CSNPs are off. */
+static void isis_warn_csnp_interval_unused(struct vty *vty, bool disabled, bool interval_set)
+{
+ if (disabled && interval_set)
+ vty_out(vty,
+ "%% isis csnp-interval is not used on point-to-point circuits while periodic CSNPs are disabled\n");
+}
+
+/*
+ * Would this interface make a broadcast circuit by default? From its flags,
+ * as isis_circuit_if_add() decides: the circuit may not exist yet. Callers
+ * check the configured network type.
+ */
+static bool isis_iface_broadcast_capable(struct vty *vty)
+{
+ struct interface *ifp = VTY_GET_CONTEXT(interface);
+
+ if (!ifp || if_is_loopback(ifp))
+ return false;
+ if (isis_option_check(ISIS_OPT_DUMMY_AS_LOOPBACK) &&
+ CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_DUMMY))
+ return false;
+ return !if_is_pointopoint(ifp);
+}
+
+/* Is this interface a broadcast circuit, given its configured network type? */
+static bool isis_iface_broadcast(struct vty *vty, const struct lyd_node *dnode)
+{
+ return isis_iface_broadcast_capable(vty) &&
+ yang_dnode_get_enum(dnode, "network-type") != CIRCUIT_T_P2P;
+}
+
+/* Say that periodic CSNPs cannot be disabled on a broadcast circuit. */
+static void isis_warn_periodic_csnp_broadcast(struct vty *vty)
+{
+ vty_out(vty,
+ "%% periodic CSNPs cannot be disabled on broadcast circuits; no isis periodic-csnp only applies to point-to-point\n");
+}
+
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/
* XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv4-routing
@@ -2556,6 +2595,44 @@ void cli_show_ip_isis_threeway_shake(struct vty *vty, const struct lyd_node *dno
vty_out(vty, " isis three-way-handshake\n");
}
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/disable-p2p-periodic-csnp
+ */
+DEFPY_YANG(isis_periodic_csnp, isis_periodic_csnp_cmd, "[no] isis periodic-csnp",
+ NO_STR
+ "IS-IS commands\n"
+ "Enable/Disable periodic CSNPs on point-to-point circuits\n")
+{
+ const struct lyd_node *dnode = isis_iface_cfg_dnode(vty);
+ bool interval_set;
+
+ if (!dnode) {
+ vty_out(vty, "ISIS is not enabled on this circuit\n");
+ return CMD_SUCCESS;
+ }
+
+ interval_set = !yang_dnode_is_default(dnode, "csnp-interval/level-1") ||
+ !yang_dnode_is_default(dnode, "csnp-interval/level-2");
+ if (no && isis_iface_broadcast(vty, dnode))
+ isis_warn_periodic_csnp_broadcast(vty);
+ else
+ isis_warn_csnp_interval_unused(vty, !!no, interval_set);
+
+ nb_cli_enqueue_change(vty, "./frr-isisd:isis/disable-p2p-periodic-csnp", NB_OP_MODIFY,
+ no ? "true" : "false");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_ip_isis_periodic_csnp(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ if (yang_dnode_get_bool(dnode, NULL))
+ vty_out(vty, " no");
+ vty_out(vty, " isis periodic-csnp\n");
+}
+
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv4-over-ipv6-nexthop
*/
@@ -2634,6 +2711,15 @@ DEFPY_YANG(csnp_interval, csnp_interval_cmd,
"Specify interval for level-1 CSNPs\n"
"Specify interval for level-2 CSNPs\n")
{
+ const struct lyd_node *dnode = isis_iface_cfg_dnode(vty);
+ bool disabled;
+
+ /* On a broadcast circuit the DIS uses the interval: nothing to warn. */
+ if (dnode && !isis_iface_broadcast(vty, dnode)) {
+ disabled = yang_dnode_get_bool(dnode, "disable-p2p-periodic-csnp");
+ isis_warn_csnp_interval_unused(vty, disabled, intv != DEFAULT_CSNP_INTERVAL);
+ }
+
if (!level || strmatch(level, "level-1"))
nb_cli_enqueue_change(vty, "./frr-isisd:isis/csnp-interval/level-1", NB_OP_MODIFY,
intv_str);
@@ -2897,9 +2983,14 @@ DEFPY_YANG(isis_network, isis_network_cmd, "[no] isis network point-to-point",
"Set network type\n"
"point-to-point network type\n")
{
- if (no)
+ const struct lyd_node *dnode = isis_iface_cfg_dnode(vty);
+
+ if (no) {
+ if (dnode && yang_dnode_get_bool(dnode, "disable-p2p-periodic-csnp") &&
+ isis_iface_broadcast_capable(vty))
+ isis_warn_periodic_csnp_broadcast(vty);
nb_cli_enqueue_change(vty, "./frr-isisd:isis/network-type", NB_OP_DESTROY, NULL);
- else
+ } else
nb_cli_enqueue_change(vty, "./frr-isisd:isis/network-type", NB_OP_MODIFY,
"point-to-point");
@@ -3789,6 +3880,7 @@ void isis_cli_init(void)
install_element(INTERFACE_NODE, &no_isis_hello_multiplier_cmd);
install_element(INTERFACE_NODE, &isis_threeway_adj_cmd);
+ install_element(INTERFACE_NODE, &isis_periodic_csnp_cmd);
install_element(INTERFACE_NODE, &isis_ipv4_over_ipv6_nexthop_cmd);
install_element(INTERFACE_NODE, &isis_hello_padding_cmd);
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
index 332a9152a3..4fd1deeca4 100644
--- a/isisd/isis_nb.c
+++ b/isisd/isis_nb.c
@@ -1139,6 +1139,13 @@ const struct frr_yang_module_info frr_isisd_info = {
.modify = lib_interface_isis_disable_three_way_handshake_modify,
},
},
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/disable-p2p-periodic-csnp",
+ .cbs = {
+ .cli_show = cli_show_ip_isis_periodic_csnp,
+ .modify = lib_interface_isis_disable_p2p_periodic_csnp_modify,
+ },
+ },
{
.xpath = "/frr-interface:lib/interface/frr-isisd:isis/ipv4-over-ipv6-nexthop",
.cbs = {
diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h
index 71ef26453f..e97cd3d28e 100644
--- a/isisd/isis_nb.h
+++ b/isisd/isis_nb.h
@@ -257,6 +257,7 @@ int lib_interface_isis_password_destroy(struct nb_cb_destroy_args *args);
int lib_interface_isis_password_password_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_password_password_type_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_disable_three_way_handshake_modify(struct nb_cb_modify_args *args);
+int lib_interface_isis_disable_p2p_periodic_csnp_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_ipv4_over_ipv6_nexthop_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_multi_topology_standard_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_multi_topology_ipv4_multicast_modify(struct nb_cb_modify_args *args);
@@ -473,6 +474,8 @@ void cli_show_ip_isis_hello_multi(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void cli_show_ip_isis_threeway_shake(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
+void cli_show_ip_isis_periodic_csnp(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
void cli_show_ip_isis_ipv4_over_ipv6_nexthop(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void cli_show_ip_isis_hello_padding(struct vty *vty, const struct lyd_node *dnode,
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index 97acaa656f..061f13105f 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -33,6 +33,7 @@
#include "isisd/isis_misc.h"
#include "isisd/isis_csm.h"
#include "isisd/isis_adjacency.h"
+#include "isisd/isis_pdu.h"
#include "isisd/isis_spf.h"
#include "isisd/isis_spf_private.h"
#include "isisd/isis_srv6.h"
@@ -4024,6 +4025,42 @@ int lib_interface_isis_disable_three_way_handshake_modify(struct nb_cb_modify_ar
return NB_OK;
}
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/disable-p2p-periodic-csnp
+ */
+int lib_interface_isis_disable_p2p_periodic_csnp_modify(struct nb_cb_modify_args *args)
+{
+ struct isis_circuit *circuit;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ circuit->disable_p2p_periodic_csnp = yang_dnode_get_bool(args->dnode, NULL);
+
+ if (circuit->circ_type != CIRCUIT_T_P2P)
+ return NB_OK;
+
+ /* Turned off: drop the pending periodic CSNPs. */
+ if (circuit->disable_p2p_periodic_csnp) {
+ event_cancel(&circuit->t_send_csnp[0]);
+ event_cancel(&circuit->t_send_csnp[1]);
+ return NB_OK;
+ }
+
+ /* Turned back on: re-arm the timer on the circuit's levels with an adjacency up. */
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+ if (!(circuit->is_type & level) || !circuit->upadjcount[level - 1])
+ continue;
+ event_add_timer(master, level == IS_LEVEL_1 ? send_l1_csnp : send_l2_csnp, circuit,
+ isis_jitter(circuit->csnp_interval[level - 1], CSNP_JITTER),
+ &circuit->t_send_csnp[level - 1]);
+ }
+
+ return NB_OK;
+}
+
/*
* XPath:
* /frr-interface:lib/interface/frr-isisd:isis/ipv4-over-ipv6-nexthop
diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c
index 23b0d5f49b..a6b0e813c7 100644
--- a/isisd/isis_pdu.c
+++ b/isisd/isis_pdu.c
@@ -2311,6 +2311,9 @@ void send_l1_csnp(struct event *event)
|| circuit->circ_type == CIRCUIT_T_P2P) {
send_csnp(circuit, 1);
}
+ /* p2p, periodic CSNPs disabled: no re-arm, next CSNP on adjacency up */
+ if (circuit->circ_type == CIRCUIT_T_P2P && circuit->disable_p2p_periodic_csnp)
+ return;
/* set next timer event */
event_add_timer(master, send_l1_csnp, circuit,
isis_jitter(circuit->csnp_interval[0], CSNP_JITTER),
@@ -2331,6 +2334,9 @@ void send_l2_csnp(struct event *event)
|| circuit->circ_type == CIRCUIT_T_P2P) {
send_csnp(circuit, 2);
}
+ /* p2p, periodic CSNPs disabled: no re-arm, next CSNP on adjacency up */
+ if (circuit->circ_type == CIRCUIT_T_P2P && circuit->disable_p2p_periodic_csnp)
+ return;
/* set next timer event */
event_add_timer(master, send_l2_csnp, circuit,
isis_jitter(circuit->csnp_interval[1], CSNP_JITTER),
diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang
index 7d77997a7a..39152cb2e2 100644
--- a/yang/frr-isisd.yang
+++ b/yang/frr-isisd.yang
@@ -793,6 +793,18 @@ module frr-isisd {
"Disables three-way handshake when creating new adjacencies.";
}
+ leaf disable-p2p-periodic-csnp {
+ type boolean;
+ default "false";
+ description
+ "On a point-to-point circuit, send a complete set of CSNPs only
+ when an adjacency comes up (RFC 5303 section 1), not every
+ CSNP interval.";
+ reference
+ "RFC 5303: Three-Way Handshake for IS-IS Point-to-Point
+ Adjacencies, section 1";
+ }
+
leaf ipv4-over-ipv6-nexthop {
type boolean;
default "false";