summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--isisd/isis_circuit.c17
-rw-r--r--isisd/isis_circuit.h1
-rw-r--r--isisd/isis_cli.c180
-rw-r--r--isisd/isis_ldp_sync.c788
-rw-r--r--isisd/isis_ldp_sync.h54
-rw-r--r--isisd/isis_main.c2
-rw-r--r--isisd/isis_nb.c30
-rw-r--r--isisd/isis_nb.h16
-rw-r--r--isisd/isis_nb_config.c246
-rw-r--r--isisd/isis_zebra.c41
-rw-r--r--isisd/isisd.c44
-rw-r--r--isisd/isisd.h5
-rw-r--r--isisd/subdir.am3
-rw-r--r--ldpd/adjacency.c3
-rw-r--r--ldpd/control.c3
-rw-r--r--ldpd/hello.c1
-rw-r--r--ldpd/interface.c417
-rw-r--r--ldpd/ldp.h2
-rw-r--r--ldpd/ldp_debug.c12
-rw-r--r--ldpd/ldp_debug.h10
-rw-r--r--ldpd/ldp_vty.h2
-rw-r--r--ldpd/ldp_vty_cmds.c28
-rw-r--r--ldpd/ldp_vty_conf.c24
-rw-r--r--ldpd/ldp_vty_exec.c121
-rw-r--r--ldpd/ldp_zebra.c110
-rw-r--r--ldpd/ldpd.c17
-rw-r--r--ldpd/ldpd.h52
-rw-r--r--ldpd/ldpe.c24
-rw-r--r--ldpd/ldpe.h11
-rw-r--r--ldpd/neighbor.c2
-rw-r--r--ldpd/packet.c2
-rw-r--r--lib/command.h5
-rw-r--r--lib/ldp_sync.c93
-rw-r--r--lib/ldp_sync.h91
-rw-r--r--lib/subdir.am2
-rw-r--r--lib/zclient.h8
-rw-r--r--ospfd/ospf_dump.c43
-rw-r--r--ospfd/ospf_dump.h4
-rw-r--r--ospfd/ospf_interface.c9
-rw-r--r--ospfd/ospf_interface.h3
-rw-r--r--ospfd/ospf_ldp_sync.c1142
-rw-r--r--ospfd/ospf_ldp_sync.h56
-rw-r--r--ospfd/ospf_main.c4
-rw-r--r--ospfd/ospf_vty.c11
-rw-r--r--ospfd/ospf_zebra.c42
-rw-r--r--ospfd/ospfd.c9
-rw-r--r--ospfd/ospfd.h4
-rw-r--r--ospfd/subdir.am4
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/ce1/zebra.conf12
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/ce2/zebra.conf12
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/ce3/zebra.conf12
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/isisd.conf26
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/ldpd.conf33
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_ip_route.ref143
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r1/zebra.conf29
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/isisd.conf27
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/ldpd.conf33
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/ospfd.conf19
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_ip_route.ref143
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r2/zebra.conf28
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/isisd.conf28
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/ldpd.conf25
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_ip_route.ref143
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_binding.ref2
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_vc.ref2
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_discovery.ref18
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/r3/zebra.conf32
-rw-r--r--tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.dot111
-rwxr-xr-xtests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.py625
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/ce1/zebra.conf12
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/ce2/zebra.conf12
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/ce3/zebra.conf12
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/ldpd.conf33
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/ospf-nbrs.txt0
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/ospfd.conf20
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface.ref11
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref8
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_neighbor.json26
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_route.ref157
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync.ref12
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref7
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref12
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r1/zebra.conf29
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/ldpd.conf33
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/ospfd.conf19
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface.ref11
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref8
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_neighbor.json26
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_route.ref157
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync.ref12
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref12
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref7
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r2/zebra.conf28
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/ldpd.conf25
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/ospfd.conf18
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface.ref11
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_neighbor.json26
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_route.ref157
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_binding.ref2
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_vc.ref2
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_discovery.ref18
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync.ref12
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref12
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref12
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/r3/zebra.conf32
-rw-r--r--tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.dot111
-rwxr-xr-xtests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.py429
-rw-r--r--yang/frr-isisd.yang40
170 files changed, 8049 insertions, 18 deletions
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
index 1214c01a12..d6988095e5 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 da358f411b..5766d1962f 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 31fe41db82..f493c7ae98 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 0000000000..42928e069a
--- /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 0000000000..6017cdf001
--- /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 26f5227aae..22df3ff37e 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 33b0b4d02c..14ea1170c4 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,
@@ -906,6 +921,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 a79cb8ff57..8a6d24b845 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 170fe92c28..0988fe8578 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;
}
@@ -1826,6 +1835,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)
@@ -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 a50eb607d9..15b51589ae 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 2a2c71b1fd..7bba783b39 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 <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 *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 c26a62dfac..d8df6eead9 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 9e855ad8cf..50d4746534 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 0bdd2423c7..4e09a6c4c9 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 cde99dc8a9..6554f0a6f1 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 ac24704bca..caf63c13d7 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 371c7d0bb1..bc8f26bc58 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 5d1bf81ec7..4bad3afc3c 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 b9ef60ff94..a8d9833dde 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 8ae144d93a..977734bddf 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 f6ba8f8c97..882874f1be 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 53a384fe55..1f102f86fa 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 <errors|event|labels|zebra>$type",
+ "[no] debug mpls ldp <errors|event|labels|sync|zebra>$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 03dee23b47..c217cfc78a 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 d8ed5ccccc..609598a768 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 *);
@@ -207,6 +211,87 @@ show_interface_msg_json(struct imsg *imsg, struct show_params *params,
}
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)
{
@@ -1437,6 +1522,20 @@ ldp_vty_dispatch_iface(struct vty *vty, struct imsg *imsg,
}
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);
}
@@ -1946,6 +2047,22 @@ ldp_vty_show_neighbor(struct vty *vty, const char *lsr_id, int capabilities,
}
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(&params, 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, &params));
+}
+
+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 d828fbe977..16e9adc9d9 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 dca379e4eb..940333f83c 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 c1bcc56c44..f8a94b4e2a 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 9078e711fb..d3374a62db 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);
@@ -975,6 +985,20 @@ ldpe_nbr_ctl(struct ctl_conn *c)
}
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)
{
struct mapping_entry *me;
diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h
index 11650069b7..ef4702341b 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 6143beb6b9..236d3eaa58 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 c00008d120..3f73f8cd88 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 e20bfe3318..d828989118 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 0000000000..5dd045d88d
--- /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 <zebra.h>
+
+#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 0000000000..daede566f0
--- /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 1feaa56d13..b8bcee139b 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 c6a67790a1..b8444e8a86 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 dcc479def6..e8798e023e 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 8c01977ff8..faae27e2cf 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 af801da8d7..adc3037598 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 4b3dbcc5c2..1d28eac6b3 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 0000000000..a8c9df1c56
--- /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 <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 "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 <INTERFACE|all>] [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 0000000000..d4efa55311
--- /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 45382e48d3..6be5486b55 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 8be7748c87..9d00ff65f9 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 8cf8430247..0487ace301 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 31d8417eb6..cc5839a810 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 e5e07875e8..55bc64317e 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -25,6 +25,7 @@
#include <zebra.h>
#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 447ddf9cbb..236b76a1f7 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 0000000000..6f165e2724
--- /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 0000000000..ac02d0f9a4
--- /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 0000000000..c6a5824d15
--- /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 0000000000..af8d117bc1
--- /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 0000000000..b9c32d3000
--- /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 0000000000..dc8f19dad0
--- /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 0000000000..d8fb27af8c
--- /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 0000000000..f77d65ebc1
--- /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 0000000000..f77d65ebc1
--- /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 0000000000..b699e8c145
--- /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 0000000000..c63bbea77f
--- /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 0000000000..c63bbea77f
--- /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 0000000000..b3de7e2c66
--- /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 0000000000..29e9df1089
--- /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 0000000000..b3a12ec53f
--- /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 0000000000..9301e60c67
--- /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 0000000000..54d015fef9
--- /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 0000000000..2232069f68
--- /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 0000000000..40d8ebeb90
--- /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 0000000000..6138d03672
--- /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 0000000000..ea047355ad
--- /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 0000000000..e477bce827
--- /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 0000000000..52398b1b72
--- /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 0000000000..f93f6aed56
--- /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 0000000000..2bcee96064
--- /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 0000000000..844aa9402a
--- /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 0000000000..821ec70ba5
--- /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 0000000000..821ec70ba5
--- /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 0000000000..433d89bd16
--- /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 0000000000..2f3eae47c8
--- /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 0000000000..2f3eae47c8
--- /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 0000000000..42c5a1cbd9
--- /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 0000000000..942ed23a1e
--- /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 0000000000..c641fb47e6
--- /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 0000000000..26801acade
--- /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 0000000000..f2b24d7d62
--- /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 0000000000..b5508dd35c
--- /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 0000000000..f2b24d7d62
--- /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 0000000000..eed35289ea
--- /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 0000000000..4dd6ddd76b
--- /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 0000000000..c244442876
--- /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 0000000000..e50fb077ba
--- /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 0000000000..2935caf13b
--- /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 0000000000..da46f1dfe2
--- /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 0000000000..e323f61f25
--- /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 0000000000..e323f61f25
--- /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 0000000000..e323f61f25
--- /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 0000000000..9cb70a4758
--- /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 0000000000..9cb70a4758
--- /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 0000000000..9cb70a4758
--- /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 0000000000..2c63c08510
--- /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 0000000000..2c63c08510
--- /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 0000000000..e54bd6e755
--- /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 0000000000..42fa98d4da
--- /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 0000000000..73261830c9
--- /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 0000000000..73261830c9
--- /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 0000000000..5c482da697
--- /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 0000000000..0922192361
--- /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 0000000000..b1919bd296
--- /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 0000000000..4f1bd22f7c
--- /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 0000000000..1dce698c17
--- /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 0000000000..6f165e2724
--- /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 0000000000..ac02d0f9a4
--- /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 0000000000..c6a5824d15
--- /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 0000000000..b9c32d3000
--- /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 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/ldp-sync-ospf-topo1/r1/ospf-nbrs.txt
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 0000000000..eefcd1e71c
--- /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 0000000000..8b2884786d
--- /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 0000000000..b1a263e422
--- /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 0000000000..0c147338e3
--- /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 0000000000..3bfda39071
--- /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 0000000000..8ccd60ca41
--- /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 0000000000..b3de7e2c66
--- /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 0000000000..29e9df1089
--- /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 0000000000..b3a12ec53f
--- /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 0000000000..9301e60c67
--- /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 0000000000..54d015fef9
--- /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 0000000000..2232069f68
--- /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 0000000000..40d8ebeb90
--- /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 0000000000..3782071bf9
--- /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 0000000000..c2642c6483
--- /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 0000000000..6f180b048a
--- /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 0000000000..ea047355ad
--- /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 0000000000..52398b1b72
--- /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 0000000000..f93f6aed56
--- /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 0000000000..82806721e7
--- /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 0000000000..71e8af1778
--- /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 0000000000..af9a9c80e5
--- /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 0000000000..5b7a5ebbb9
--- /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 0000000000..7147c6a595
--- /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 0000000000..42c5a1cbd9
--- /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 0000000000..942ed23a1e
--- /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 0000000000..c641fb47e6
--- /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 0000000000..26801acade
--- /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 0000000000..f2b24d7d62
--- /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 0000000000..b5508dd35c
--- /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 0000000000..eed35289ea
--- /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 0000000000..6c27a10427
--- /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 0000000000..889f69ed7f
--- /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 0000000000..d9036e124b
--- /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 0000000000..c244442876
--- /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 0000000000..2935caf13b
--- /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 0000000000..09eea759ad
--- /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 0000000000..aec97b30cb
--- /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 0000000000..aec97b30cb
--- /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 0000000000..aec97b30cb
--- /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 0000000000..1b29b9f947
--- /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 0000000000..d77de7c9e3
--- /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 0000000000..2c63c08510
--- /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 0000000000..2c63c08510
--- /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 0000000000..e54bd6e755
--- /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 0000000000..42fa98d4da
--- /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 0000000000..73261830c9
--- /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 0000000000..73261830c9
--- /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 0000000000..5c482da697
--- /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 0000000000..b417ab040a
--- /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 0000000000..b417ab040a
--- /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 0000000000..b417ab040a
--- /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 0000000000..b1919bd296
--- /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 0000000000..4f1bd22f7c
--- /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 0000000000..dc6a76eb49
--- /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 00296516ef..cc959bd9fe 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.";
+ }
+ }
+ }
+
}
}