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 EVENT_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 EVENT_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 event
*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
= EVENT_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 event_add_timer(master
, isis_ldp_sync_holddown_timer
, circuit
,
377 ldp_sync_info
->holddown
, &ldp_sync_info
->t_holddown
);
381 * LDP-SYNC handle client close routine
383 void isis_ldp_sync_handle_client_close(struct zapi_client_close_info
*info
)
385 struct isis_area
*area
;
386 struct listnode
*anode
, *cnode
;
387 struct isis_circuit
*circuit
;
388 struct isis
*isis
= isis_lookup_by_vrfid(VRF_DEFAULT
);
390 /* if isis is not enabled ignore */
394 /* Check if the LDP main client session closed */
395 if (info
->proto
!= ZEBRA_ROUTE_LDP
|| info
->session_id
== 0)
398 /* Handle the zebra notification that the LDP client session closed.
399 * set cost to LSInfinity
400 * send request to LDP for LDP-SYNC state for each interface
402 zlog_err("%s: LDP down", __func__
);
404 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, anode
, area
)) {
405 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
408 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, cnode
, circuit
))
409 isis_ldp_sync_ldp_fail(circuit
);
414 * LDP-SYNC routes used by set commands.
417 void isis_area_ldp_sync_enable(struct isis_area
*area
)
419 struct isis_circuit
*circuit
;
420 struct listnode
*node
;
422 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
)) {
423 SET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
);
425 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
426 isis_if_ldp_sync_enable(circuit
);
430 void isis_area_ldp_sync_disable(struct isis_area
*area
)
432 struct isis_circuit
*circuit
;
433 struct listnode
*node
;
435 if (CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
)) {
436 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
437 isis_if_ldp_sync_disable(circuit
);
439 UNSET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
);
441 UNSET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
);
442 area
->ldp_sync_cmd
.holddown
= LDP_IGP_SYNC_HOLDDOWN_DEFAULT
;
446 void isis_area_ldp_sync_set_holddown(struct isis_area
*area
, uint16_t holddown
)
448 struct isis_circuit
*circuit
;
449 struct listnode
*node
;
451 if (holddown
== LDP_IGP_SYNC_HOLDDOWN_DEFAULT
)
452 UNSET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
);
454 SET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
);
456 area
->ldp_sync_cmd
.holddown
= holddown
;
458 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
459 isis_if_set_ldp_sync_holddown(circuit
);
462 void isis_if_ldp_sync_enable(struct isis_circuit
*circuit
)
464 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
465 struct isis_area
*area
= circuit
->area
;
467 /* called when setting LDP-SYNC at the global level:
468 * specified on interface overrides global config
469 * if ptop link send msg to LDP indicating ldp-sync enabled
471 if (if_is_loopback(circuit
->interface
))
474 if (circuit
->interface
->vrf
->vrf_id
!= VRF_DEFAULT
)
477 ils_debug("%s: enable if %s", __func__
, circuit
->interface
->name
);
479 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
482 /* config on interface, overrides global config. */
483 if (CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_IF_CONFIG
))
484 if (ldp_sync_info
->enabled
!= LDP_IGP_SYNC_ENABLED
)
487 if (!CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_HOLDDOWN
))
488 ldp_sync_info
->holddown
= area
->ldp_sync_cmd
.holddown
;
490 if (circuit
->circ_type
== CIRCUIT_T_P2P
491 || if_is_pointopoint(circuit
->interface
)) {
492 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
;
493 isis_ldp_sync_state_req_msg(circuit
);
495 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_NOT_REQUIRED
;
496 ils_debug("%s: Sync only runs on P2P links %s", __func__
,
497 circuit
->interface
->name
);
501 void isis_if_ldp_sync_disable(struct isis_circuit
*circuit
)
503 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
504 struct isis_area
*area
= circuit
->area
;
506 /* Stop LDP-SYNC on this interface:
507 * if holddown timer is running stop it
508 * delete ldp instance on interface
511 if (if_is_loopback(circuit
->interface
))
514 ils_debug("%s: remove if %s", __func__
, circuit
->interface
->name
);
516 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
519 EVENT_OFF(ldp_sync_info
->t_holddown
);
520 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_NOT_REQUIRED
;
521 isis_ldp_sync_set_if_metric(circuit
, true);
524 void isis_if_set_ldp_sync_holddown(struct isis_circuit
*circuit
)
526 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
527 struct isis_area
*area
= circuit
->area
;
529 /* called when setting LDP-SYNC at the global level:
530 * specified on interface overrides global config.
532 if (if_is_loopback(circuit
->interface
))
535 /* config on interface, overrides global config. */
536 if (CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_HOLDDOWN
))
538 if (CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
))
539 ldp_sync_info
->holddown
= area
->ldp_sync_cmd
.holddown
;
541 ldp_sync_info
->holddown
= LDP_IGP_SYNC_HOLDDOWN_DEFAULT
;
545 * LDP-SYNC routines used by show commands.
548 static void isis_circuit_ldp_sync_print_vty(struct isis_circuit
*circuit
,
551 struct ldp_sync_info
*ldp_sync_info
;
552 const char *ldp_state
;
554 if (circuit
->ldp_sync_info
== NULL
||
555 if_is_loopback(circuit
->interface
))
558 ldp_sync_info
= circuit
->ldp_sync_info
;
559 vty_out(vty
, "%-16s\n", circuit
->interface
->name
);
560 if (circuit
->state
== C_STATE_CONF
) {
561 vty_out(vty
, " Interface down\n");
565 vty_out(vty
, " LDP-IGP Synchronization enabled: %s\n",
566 ldp_sync_info
->enabled
== LDP_IGP_SYNC_ENABLED
569 vty_out(vty
, " holddown timer in seconds: %u\n",
570 ldp_sync_info
->holddown
);
572 switch (ldp_sync_info
->state
) {
573 case LDP_IGP_SYNC_STATE_REQUIRED_UP
:
574 vty_out(vty
, " State: Sync achieved\n");
576 case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
:
577 if (ldp_sync_info
->t_holddown
!= NULL
) {
578 struct timeval remain
=
579 event_timer_remain(ldp_sync_info
->t_holddown
);
581 " Holddown timer is running %lld.%03lld remaining\n",
582 (long long)remain
.tv_sec
,
583 (long long)remain
.tv_usec
/1000);
585 vty_out(vty
, " State: Holding down until Sync\n");
587 vty_out(vty
, " State: Sync not achieved\n");
589 case LDP_IGP_SYNC_STATE_NOT_REQUIRED
:
591 if ((circuit
->circ_type
!= CIRCUIT_T_P2P
&&
592 !if_is_pointopoint(circuit
->interface
)) &&
593 circuit
->circ_type
!= CIRCUIT_T_UNKNOWN
)
594 ldp_state
= "Sync not required: non-p2p link";
596 ldp_state
= "Sync not required";
597 vty_out(vty
, " State: %s\n", ldp_state
);
602 DEFUN (show_isis_mpls_ldp_interface
,
603 show_isis_mpls_ldp_interface_cmd
,
604 "show " PROTO_NAME
" mpls ldp-sync [interface <INTERFACE|all>]",
608 "LDP-IGP Sync information\n"
609 "Interface information\n"
615 struct listnode
*anode
, *cnode
;
616 struct isis_area
*area
;
617 struct isis_circuit
*circuit
;
618 struct isis
*isis
= isis_lookup_by_vrfid(VRF_DEFAULT
);
622 vty_out(vty
, "IS-IS Routing Process not enabled\n");
626 if (argv_find(argv
, argc
, "INTERFACE", &idx_intf
))
627 ifname
= argv
[idx_intf
]->arg
;
629 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, anode
, area
)) {
630 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, cnode
, circuit
))
632 isis_circuit_ldp_sync_print_vty(circuit
, vty
);
633 else if (strcmp(circuit
->interface
->name
, ifname
)
635 isis_circuit_ldp_sync_print_vty(circuit
, vty
);
640 if (found
== false && ifname
)
641 vty_out(vty
, "%-16s\n ISIS not enabled\n", ifname
);
646 void isis_ldp_sync_init(void)
649 /* "show ip isis mpls ldp interface" commands. */
650 install_element(VIEW_NODE
, &show_isis_mpls_ldp_interface_cmd
);
652 /* register for adjacency state changes */
653 hook_register(isis_adj_state_change_hook
,
654 isis_ldp_sync_adj_state_change
);