1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * isis_ldp_sync.c: ISIS LDP-IGP Sync handling routines
4 * Copyright (C) 2020 Volta Networks, Inc.
24 #include "isisd/isis_constants.h"
25 #include "isisd/isis_common.h"
26 #include "isisd/isis_flags.h"
27 #include "isisd/isis_circuit.h"
28 #include "isisd/isis_lsp.h"
29 #include "isisd/isis_pdu.h"
30 #include "isisd/isis_network.h"
31 #include "isisd/isis_misc.h"
32 #include "isisd/isis_constants.h"
33 #include "isisd/isis_adjacency.h"
34 #include "isisd/isis_dr.h"
35 #include "isisd/isisd.h"
36 #include "isisd/isis_csm.h"
37 #include "isisd/isis_events.h"
38 #include "isisd/isis_te.h"
39 #include "isisd/isis_mt.h"
40 #include "isisd/isis_errors.h"
41 #include "isisd/isis_tx_queue.h"
42 #include "isisd/isis_nb.h"
43 #include "isisd/isis_ldp_sync.h"
45 extern struct zclient
*zclient
;
48 * LDP-SYNC msg between IGP and LDP
50 int isis_ldp_sync_state_update(struct ldp_igp_sync_if_state state
)
52 struct interface
*ifp
;
53 struct isis_circuit
*circuit
= NULL
;
54 struct isis_area
*area
;
57 ifp
= if_lookup_by_index(state
.ifindex
, VRF_DEFAULT
);
65 /* if isis is not enabled or LDP-SYNC is not configured ignore */
68 || !CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
71 /* received ldp-sync interface state from LDP */
72 ils_debug("%s: rcvd %s from LDP if %s", __func__
,
73 state
.sync_start
? "sync-start" : "sync-complete", ifp
->name
);
75 isis_ldp_sync_if_start(circuit
, false);
77 isis_ldp_sync_if_complete(circuit
);
82 int isis_ldp_sync_announce_update(struct ldp_igp_sync_announce announce
)
84 struct isis_area
*area
;
85 struct listnode
*anode
, *cnode
;
86 struct isis_circuit
*circuit
;
87 struct isis
*isis
= isis_lookup_by_vrfid(VRF_DEFAULT
);
89 /* if isis is not enabled ignore */
93 if (announce
.proto
!= ZEBRA_ROUTE_LDP
)
96 ils_debug("%s: rcvd announce from LDP", __func__
);
98 /* LDP just started up:
99 * set cost to LSInfinity
100 * send request to LDP for LDP-SYNC state for each interface
102 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, anode
, area
)) {
103 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
106 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, cnode
, circuit
))
107 isis_ldp_sync_if_start(circuit
, true);
113 void isis_ldp_sync_state_req_msg(struct isis_circuit
*circuit
)
115 struct ldp_igp_sync_if_state_req request
;
116 struct interface
*ifp
= circuit
->interface
;
118 ils_debug("%s: send state request to LDP for %s", __func__
, ifp
->name
);
120 memset(&request
, 0, sizeof(request
));
121 strlcpy(request
.name
, ifp
->name
, sizeof(ifp
->name
));
122 request
.proto
= LDP_IGP_SYNC_IF_STATE_REQUEST
;
123 request
.ifindex
= ifp
->ifindex
;
125 zclient_send_opaque(zclient
, LDP_IGP_SYNC_IF_STATE_REQUEST
,
126 (uint8_t *)&request
, sizeof(request
));
130 * LDP-SYNC general interface routines
132 void isis_ldp_sync_if_start(struct isis_circuit
*circuit
,
135 struct ldp_sync_info
*ldp_sync_info
;
137 ldp_sync_info
= circuit
->ldp_sync_info
;
139 /* Start LDP-SYNC on this interface:
140 * set cost of interface to LSInfinity so traffic will use different
141 * interface until LDP has learned all labels from peer
142 * start holddown timer if configured
143 * send msg to LDP to get LDP-SYNC state
146 ldp_sync_info
->enabled
== LDP_IGP_SYNC_ENABLED
&&
147 ldp_sync_info
->state
!= LDP_IGP_SYNC_STATE_NOT_REQUIRED
) {
148 ils_debug("%s: start on if %s state: %s", __func__
,
149 circuit
->interface
->name
, "Holding down until Sync");
150 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
;
151 isis_ldp_sync_set_if_metric(circuit
, true);
152 isis_ldp_sync_holddown_timer_add(circuit
);
155 isis_ldp_sync_state_req_msg(circuit
);
159 void isis_ldp_sync_if_complete(struct isis_circuit
*circuit
)
161 struct ldp_sync_info
*ldp_sync_info
;
163 ldp_sync_info
= circuit
->ldp_sync_info
;
165 /* received sync-complete from LDP:
168 * restore interface cost to original value
170 if (ldp_sync_info
&& ldp_sync_info
->enabled
== LDP_IGP_SYNC_ENABLED
) {
171 if (ldp_sync_info
->state
== LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
)
172 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_REQUIRED_UP
;
174 THREAD_OFF(ldp_sync_info
->t_holddown
);
176 isis_ldp_sync_set_if_metric(circuit
, true);
180 void isis_ldp_sync_ldp_fail(struct isis_circuit
*circuit
)
182 struct ldp_sync_info
*ldp_sync_info
;
184 ldp_sync_info
= circuit
->ldp_sync_info
;
186 /* LDP client close detected:
187 * stop holddown timer
188 * set cost of interface to LSInfinity so traffic will use different
189 * interface until LDP restarts and has learned all labels from peer
192 ldp_sync_info
->enabled
== LDP_IGP_SYNC_ENABLED
&&
193 ldp_sync_info
->state
!= LDP_IGP_SYNC_STATE_NOT_REQUIRED
) {
194 THREAD_OFF(ldp_sync_info
->t_holddown
);
195 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
;
196 isis_ldp_sync_set_if_metric(circuit
, true);
200 static int isis_ldp_sync_adj_state_change(struct isis_adjacency
*adj
)
202 struct isis_circuit
*circuit
= adj
->circuit
;
203 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
204 struct isis_area
*area
= circuit
->area
;
206 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
)
207 || circuit
->interface
->vrf
->vrf_id
!= VRF_DEFAULT
208 || if_is_loopback(circuit
->interface
))
211 if (ldp_sync_info
->enabled
!= LDP_IGP_SYNC_ENABLED
)
214 if (adj
->adj_state
== ISIS_ADJ_UP
) {
215 if (circuit
->circ_type
== CIRCUIT_T_P2P
||
216 if_is_pointopoint(circuit
->interface
)) {
217 /* If LDP-SYNC is configure on interface then start */
218 ldp_sync_info
->state
=
219 LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
;
220 isis_ldp_sync_if_start(circuit
, true);
222 /* non ptop link so don't run ldp-sync */
223 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_NOT_REQUIRED
;
224 isis_ldp_sync_set_if_metric(circuit
, true);
227 /* If LDP-SYNC is configure on this interface then stop it */
228 if (circuit
->circ_type
== CIRCUIT_T_P2P
||
229 if_is_pointopoint(circuit
->interface
))
230 ldp_sync_info
->state
=
231 LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
;
233 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_NOT_REQUIRED
;
235 ils_debug("%s: down on if %s", __func__
,
236 circuit
->interface
->name
);
237 ldp_sync_if_down(circuit
->ldp_sync_info
);
243 bool isis_ldp_sync_if_metric_config(struct isis_circuit
*circuit
, int level
,
246 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
247 struct isis_area
*area
= circuit
->area
;
249 /* configured interface metric has been changed:
250 * if LDP-IGP Sync is running and metric has been set to LSInfinity
251 * change saved value so when ldp-sync completes proper metric is
254 if (area
&& CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
)
255 && ldp_sync_info
!= NULL
) {
257 if (CHECK_FLAG(ldp_sync_info
->flags
,
258 LDP_SYNC_FLAG_SET_METRIC
)) {
259 ldp_sync_info
->metric
[level
-1] = metric
;
260 ldp_sync_info
->metric
[level
-1] = metric
;
267 void isis_ldp_sync_set_if_metric(struct isis_circuit
*circuit
, bool run_regen
)
269 struct ldp_sync_info
*ldp_sync_info
;
271 /* set interface metric:
272 * if LDP-IGP Sync is starting set metric so interface
273 * is used only as last resort
274 * else restore metric to original value
276 if (circuit
->ldp_sync_info
== NULL
|| circuit
->area
== NULL
)
279 ldp_sync_info
= circuit
->ldp_sync_info
;
280 if (ldp_sync_if_is_enabled(ldp_sync_info
)) {
281 /* if metric already set to LSInfinity just return */
282 if (CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_SET_METRIC
))
285 SET_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_SET_METRIC
);
286 if (circuit
->is_type
& IS_LEVEL_1
) {
287 if (circuit
->area
->newmetric
) {
288 ldp_sync_info
->metric
[0] =
289 circuit
->te_metric
[0];
290 circuit
->te_metric
[0] =
291 ISIS_WIDE_METRIC_INFINITY
;
293 ldp_sync_info
->metric
[0] = circuit
->metric
[0];
295 ISIS_NARROW_METRIC_INFINITY
;
298 if (circuit
->is_type
& IS_LEVEL_2
) {
299 if (circuit
->area
->newmetric
) {
300 ldp_sync_info
->metric
[1] =
301 circuit
->te_metric
[1];
302 circuit
->te_metric
[1] =
303 ISIS_WIDE_METRIC_INFINITY
;
305 ldp_sync_info
->metric
[1] = circuit
->metric
[1];
307 ISIS_NARROW_METRIC_INFINITY
;
311 /* if metric already restored just return */
312 if (!CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_SET_METRIC
))
315 UNSET_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_SET_METRIC
);
316 if (circuit
->is_type
& IS_LEVEL_1
) {
317 circuit
->te_metric
[0] = ldp_sync_info
->metric
[0];
318 circuit
->metric
[0] = ldp_sync_info
->metric
[0];
320 if (circuit
->is_type
& IS_LEVEL_2
) {
321 circuit
->te_metric
[1] = ldp_sync_info
->metric
[1];
322 circuit
->metric
[1] = ldp_sync_info
->metric
[1];
327 lsp_regenerate_schedule(circuit
->area
, circuit
->is_type
, 0);
332 * LDP-SYNC holddown timer routines
334 static void isis_ldp_sync_holddown_timer(struct thread
*thread
)
336 struct isis_circuit
*circuit
;
337 struct ldp_sync_info
*ldp_sync_info
;
339 /* holddown timer expired:
340 * didn't receive msg from LDP indicating sync-complete
341 * restore interface cost to original value
343 circuit
= THREAD_ARG(thread
);
344 if (circuit
->ldp_sync_info
== NULL
)
347 ldp_sync_info
= circuit
->ldp_sync_info
;
349 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_REQUIRED_UP
;
350 ldp_sync_info
->t_holddown
= NULL
;
352 ils_debug("%s: holddown timer expired for %s state:sync achieved",
353 __func__
, circuit
->interface
->name
);
355 isis_ldp_sync_set_if_metric(circuit
, true);
358 void isis_ldp_sync_holddown_timer_add(struct isis_circuit
*circuit
)
360 struct ldp_sync_info
*ldp_sync_info
;
362 ldp_sync_info
= circuit
->ldp_sync_info
;
364 /* Start holddown timer:
365 * this timer is used to keep interface cost at LSInfinity
366 * once expires returns cost to original value
367 * if timer is already running or holddown time is off just return
369 if (ldp_sync_info
->t_holddown
||
370 ldp_sync_info
->holddown
== LDP_IGP_SYNC_HOLDDOWN_DEFAULT
)
373 ils_debug("%s: start holddown timer for %s time %d", __func__
,
374 circuit
->interface
->name
, ldp_sync_info
->holddown
);
376 thread_add_timer(master
, isis_ldp_sync_holddown_timer
,
377 circuit
, ldp_sync_info
->holddown
,
378 &ldp_sync_info
->t_holddown
);
382 * LDP-SYNC handle client close routine
384 void isis_ldp_sync_handle_client_close(struct zapi_client_close_info
*info
)
386 struct isis_area
*area
;
387 struct listnode
*anode
, *cnode
;
388 struct isis_circuit
*circuit
;
389 struct isis
*isis
= isis_lookup_by_vrfid(VRF_DEFAULT
);
391 /* if isis is not enabled ignore */
395 /* Check if the LDP main client session closed */
396 if (info
->proto
!= ZEBRA_ROUTE_LDP
|| info
->session_id
== 0)
399 /* Handle the zebra notification that the LDP client session closed.
400 * set cost to LSInfinity
401 * send request to LDP for LDP-SYNC state for each interface
403 zlog_err("%s: LDP down", __func__
);
405 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, anode
, area
)) {
406 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
409 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, cnode
, circuit
))
410 isis_ldp_sync_ldp_fail(circuit
);
415 * LDP-SYNC routes used by set commands.
418 void isis_area_ldp_sync_enable(struct isis_area
*area
)
420 struct isis_circuit
*circuit
;
421 struct listnode
*node
;
423 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
)) {
424 SET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
);
426 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
427 isis_if_ldp_sync_enable(circuit
);
431 void isis_area_ldp_sync_disable(struct isis_area
*area
)
433 struct isis_circuit
*circuit
;
434 struct listnode
*node
;
436 if (CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
)) {
437 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
438 isis_if_ldp_sync_disable(circuit
);
440 UNSET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
);
442 UNSET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
);
443 area
->ldp_sync_cmd
.holddown
= LDP_IGP_SYNC_HOLDDOWN_DEFAULT
;
447 void isis_area_ldp_sync_set_holddown(struct isis_area
*area
, uint16_t holddown
)
449 struct isis_circuit
*circuit
;
450 struct listnode
*node
;
452 if (holddown
== LDP_IGP_SYNC_HOLDDOWN_DEFAULT
)
453 UNSET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
);
455 SET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
);
457 area
->ldp_sync_cmd
.holddown
= holddown
;
459 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
460 isis_if_set_ldp_sync_holddown(circuit
);
463 void isis_if_ldp_sync_enable(struct isis_circuit
*circuit
)
465 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
466 struct isis_area
*area
= circuit
->area
;
468 /* called when setting LDP-SYNC at the global level:
469 * specified on interface overrides global config
470 * if ptop link send msg to LDP indicating ldp-sync enabled
472 if (if_is_loopback(circuit
->interface
))
475 if (circuit
->interface
->vrf
->vrf_id
!= VRF_DEFAULT
)
478 ils_debug("%s: enable if %s", __func__
, circuit
->interface
->name
);
480 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
483 /* config on interface, overrides global config. */
484 if (CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_IF_CONFIG
))
485 if (ldp_sync_info
->enabled
!= LDP_IGP_SYNC_ENABLED
)
488 if (!CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_HOLDDOWN
))
489 ldp_sync_info
->holddown
= area
->ldp_sync_cmd
.holddown
;
491 if (circuit
->circ_type
== CIRCUIT_T_P2P
492 || if_is_pointopoint(circuit
->interface
)) {
493 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
;
494 isis_ldp_sync_state_req_msg(circuit
);
496 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_NOT_REQUIRED
;
497 ils_debug("%s: Sync only runs on P2P links %s", __func__
,
498 circuit
->interface
->name
);
502 void isis_if_ldp_sync_disable(struct isis_circuit
*circuit
)
504 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
505 struct isis_area
*area
= circuit
->area
;
507 /* Stop LDP-SYNC on this interface:
508 * if holddown timer is running stop it
509 * delete ldp instance on interface
512 if (if_is_loopback(circuit
->interface
))
515 ils_debug("%s: remove if %s", __func__
, circuit
->interface
->name
);
517 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
520 THREAD_OFF(ldp_sync_info
->t_holddown
);
521 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_NOT_REQUIRED
;
522 isis_ldp_sync_set_if_metric(circuit
, true);
525 void isis_if_set_ldp_sync_holddown(struct isis_circuit
*circuit
)
527 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
528 struct isis_area
*area
= circuit
->area
;
530 /* called when setting LDP-SYNC at the global level:
531 * specified on interface overrides global config.
533 if (if_is_loopback(circuit
->interface
))
536 /* config on interface, overrides global config. */
537 if (CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_HOLDDOWN
))
539 if (CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
))
540 ldp_sync_info
->holddown
= area
->ldp_sync_cmd
.holddown
;
542 ldp_sync_info
->holddown
= LDP_IGP_SYNC_HOLDDOWN_DEFAULT
;
546 * LDP-SYNC routines used by show commands.
549 static void isis_circuit_ldp_sync_print_vty(struct isis_circuit
*circuit
,
552 struct ldp_sync_info
*ldp_sync_info
;
553 const char *ldp_state
;
555 if (circuit
->ldp_sync_info
== NULL
||
556 if_is_loopback(circuit
->interface
))
559 ldp_sync_info
= circuit
->ldp_sync_info
;
560 vty_out(vty
, "%-16s\n", circuit
->interface
->name
);
561 if (circuit
->state
== C_STATE_CONF
) {
562 vty_out(vty
, " Interface down\n");
566 vty_out(vty
, " LDP-IGP Synchronization enabled: %s\n",
567 ldp_sync_info
->enabled
== LDP_IGP_SYNC_ENABLED
570 vty_out(vty
, " holddown timer in seconds: %u\n",
571 ldp_sync_info
->holddown
);
573 switch (ldp_sync_info
->state
) {
574 case LDP_IGP_SYNC_STATE_REQUIRED_UP
:
575 vty_out(vty
, " State: Sync achieved\n");
577 case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
:
578 if (ldp_sync_info
->t_holddown
!= NULL
) {
579 struct timeval remain
= thread_timer_remain(
580 ldp_sync_info
->t_holddown
);
582 " Holddown timer is running %lld.%03lld remaining\n",
583 (long long)remain
.tv_sec
,
584 (long long)remain
.tv_usec
/1000);
586 vty_out(vty
, " State: Holding down until Sync\n");
588 vty_out(vty
, " State: Sync not achieved\n");
590 case LDP_IGP_SYNC_STATE_NOT_REQUIRED
:
592 if ((circuit
->circ_type
!= CIRCUIT_T_P2P
&&
593 !if_is_pointopoint(circuit
->interface
)) &&
594 circuit
->circ_type
!= CIRCUIT_T_UNKNOWN
)
595 ldp_state
= "Sync not required: non-p2p link";
597 ldp_state
= "Sync not required";
598 vty_out(vty
, " State: %s\n", ldp_state
);
603 DEFUN (show_isis_mpls_ldp_interface
,
604 show_isis_mpls_ldp_interface_cmd
,
605 "show " PROTO_NAME
" mpls ldp-sync [interface <INTERFACE|all>]",
609 "LDP-IGP Sync information\n"
610 "Interface information\n"
616 struct listnode
*anode
, *cnode
;
617 struct isis_area
*area
;
618 struct isis_circuit
*circuit
;
619 struct isis
*isis
= isis_lookup_by_vrfid(VRF_DEFAULT
);
623 vty_out(vty
, "IS-IS Routing Process not enabled\n");
627 if (argv_find(argv
, argc
, "INTERFACE", &idx_intf
))
628 ifname
= argv
[idx_intf
]->arg
;
630 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, anode
, area
)) {
631 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, cnode
, circuit
))
633 isis_circuit_ldp_sync_print_vty(circuit
, vty
);
634 else if (strcmp(circuit
->interface
->name
, ifname
)
636 isis_circuit_ldp_sync_print_vty(circuit
, vty
);
641 if (found
== false && ifname
)
642 vty_out(vty
, "%-16s\n ISIS not enabled\n", ifname
);
647 void isis_ldp_sync_init(void)
650 /* "show ip isis mpls ldp interface" commands. */
651 install_element(VIEW_NODE
, &show_isis_mpls_ldp_interface_cmd
);
653 /* register for adjacency state changes */
654 hook_register(isis_adj_state_change_hook
,
655 isis_ldp_sync_adj_state_change
);