2 * isis_ldp_sync.c: ISIS LDP-IGP Sync handling routines
3 * Copyright (C) 2020 Volta Networks, Inc.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
37 #include "isisd/isis_constants.h"
38 #include "isisd/isis_common.h"
39 #include "isisd/isis_flags.h"
40 #include "isisd/isis_circuit.h"
41 #include "isisd/isis_lsp.h"
42 #include "isisd/isis_pdu.h"
43 #include "isisd/isis_network.h"
44 #include "isisd/isis_misc.h"
45 #include "isisd/isis_constants.h"
46 #include "isisd/isis_adjacency.h"
47 #include "isisd/isis_dr.h"
48 #include "isisd/isisd.h"
49 #include "isisd/isis_csm.h"
50 #include "isisd/isis_events.h"
51 #include "isisd/isis_te.h"
52 #include "isisd/isis_mt.h"
53 #include "isisd/isis_errors.h"
54 #include "isisd/isis_tx_queue.h"
55 #include "isisd/isis_nb.h"
56 #include "isisd/isis_ldp_sync.h"
58 extern struct zclient
*zclient
;
61 * LDP-SYNC msg between IGP and LDP
63 int isis_ldp_sync_state_update(struct ldp_igp_sync_if_state state
)
65 struct interface
*ifp
;
66 struct isis_circuit
*circuit
= NULL
;
67 struct isis_area
*area
;
70 ifp
= if_lookup_by_index(state
.ifindex
, VRF_DEFAULT
);
78 /* if isis is not enabled or LDP-SYNC is not configured ignore */
81 || !CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
84 /* received ldp-sync interface state from LDP */
85 ils_debug("ldp_sync: rcvd %s from LDP if %s",
86 state
.sync_start
? "sync-start" : "sync-complete", ifp
->name
);
88 isis_ldp_sync_if_start(circuit
, false);
90 isis_ldp_sync_if_complete(circuit
);
95 int isis_ldp_sync_announce_update(struct ldp_igp_sync_announce announce
)
97 struct isis_area
*area
;
98 struct listnode
*anode
, *cnode
;
99 struct isis_circuit
*circuit
;
100 struct isis
*isis
= isis_lookup_by_vrfid(VRF_DEFAULT
);
102 /* if isis is not enabled ignore */
106 if (announce
.proto
!= ZEBRA_ROUTE_LDP
)
109 ils_debug("ldp_sync: rcvd announce from LDP");
111 /* LDP just started up:
112 * set cost to LSInfinity
113 * send request to LDP for LDP-SYNC state for each interface
115 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, anode
, area
)) {
116 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
119 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, cnode
, circuit
))
120 isis_ldp_sync_if_start(circuit
, true);
126 void isis_ldp_sync_state_req_msg(struct isis_circuit
*circuit
)
128 struct ldp_igp_sync_if_state_req request
;
129 struct interface
*ifp
= circuit
->interface
;
131 ils_debug("ldp_sync: send state request to LDP for %s",
134 memset(&request
, 0, sizeof(request
));
135 strlcpy(request
.name
, ifp
->name
, sizeof(ifp
->name
));
136 request
.proto
= LDP_IGP_SYNC_IF_STATE_REQUEST
;
137 request
.ifindex
= ifp
->ifindex
;
139 zclient_send_opaque(zclient
, LDP_IGP_SYNC_IF_STATE_REQUEST
,
140 (uint8_t *)&request
, sizeof(request
));
144 * LDP-SYNC general interface routines
146 void isis_ldp_sync_if_start(struct isis_circuit
*circuit
,
149 struct ldp_sync_info
*ldp_sync_info
;
151 ldp_sync_info
= circuit
->ldp_sync_info
;
153 /* Start LDP-SYNC on this interface:
154 * set cost of interface to LSInfinity so traffic will use different
155 * interface until LDP has learned all labels from peer
156 * start holddown timer if configured
157 * send msg to LDP to get LDP-SYNC state
160 ldp_sync_info
->enabled
== LDP_IGP_SYNC_ENABLED
&&
161 ldp_sync_info
->state
!= LDP_IGP_SYNC_STATE_NOT_REQUIRED
) {
162 ils_debug("ldp_sync: start on if %s state: %s",
163 circuit
->interface
->name
, "Holding down until Sync");
164 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
;
165 isis_ldp_sync_set_if_metric(circuit
, true);
166 isis_ldp_sync_holddown_timer_add(circuit
);
169 isis_ldp_sync_state_req_msg(circuit
);
173 void isis_ldp_sync_if_complete(struct isis_circuit
*circuit
)
175 struct ldp_sync_info
*ldp_sync_info
;
177 ldp_sync_info
= circuit
->ldp_sync_info
;
179 /* received sync-complete from LDP:
182 * restore interface cost to original value
184 if (ldp_sync_info
&& ldp_sync_info
->enabled
== LDP_IGP_SYNC_ENABLED
) {
185 if (ldp_sync_info
->state
== LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
)
186 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_REQUIRED_UP
;
188 THREAD_OFF(ldp_sync_info
->t_holddown
);
190 isis_ldp_sync_set_if_metric(circuit
, true);
194 void isis_ldp_sync_ldp_fail(struct isis_circuit
*circuit
)
196 struct ldp_sync_info
*ldp_sync_info
;
198 ldp_sync_info
= circuit
->ldp_sync_info
;
200 /* LDP client close detected:
201 * stop holddown timer
202 * set cost of interface to LSInfinity so traffic will use different
203 * interface until LDP restarts and has learned all labels from peer
206 ldp_sync_info
->enabled
== LDP_IGP_SYNC_ENABLED
&&
207 ldp_sync_info
->state
!= LDP_IGP_SYNC_STATE_NOT_REQUIRED
) {
208 THREAD_OFF(ldp_sync_info
->t_holddown
);
209 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
;
210 isis_ldp_sync_set_if_metric(circuit
, true);
214 static int isis_ldp_sync_adj_state_change(struct isis_adjacency
*adj
)
216 struct isis_circuit
*circuit
= adj
->circuit
;
217 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
218 struct isis_area
*area
= circuit
->area
;
220 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
)
221 || circuit
->interface
->vrf
->vrf_id
!= VRF_DEFAULT
222 || if_is_loopback(circuit
->interface
))
225 if (ldp_sync_info
->enabled
!= LDP_IGP_SYNC_ENABLED
)
228 if (adj
->adj_state
== ISIS_ADJ_UP
) {
229 if (circuit
->circ_type
== CIRCUIT_T_P2P
||
230 if_is_pointopoint(circuit
->interface
)) {
231 /* If LDP-SYNC is configure on interface then start */
232 ldp_sync_info
->state
=
233 LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
;
234 isis_ldp_sync_if_start(circuit
, true);
236 /* non ptop link so don't run ldp-sync */
237 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_NOT_REQUIRED
;
238 isis_ldp_sync_set_if_metric(circuit
, true);
241 /* If LDP-SYNC is configure on this interface then stop it */
242 if (circuit
->circ_type
== CIRCUIT_T_P2P
||
243 if_is_pointopoint(circuit
->interface
))
244 ldp_sync_info
->state
=
245 LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
;
247 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_NOT_REQUIRED
;
249 ils_debug("ldp_sync: down on if %s", circuit
->interface
->name
);
250 ldp_sync_if_down(circuit
->ldp_sync_info
);
256 bool isis_ldp_sync_if_metric_config(struct isis_circuit
*circuit
, int level
,
259 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
260 struct isis_area
*area
= circuit
->area
;
262 /* configured interface metric has been changed:
263 * if LDP-IGP Sync is running and metric has been set to LSInfinity
264 * change saved value so when ldp-sync completes proper metric is
267 if (area
&& CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
)
268 && ldp_sync_info
!= NULL
) {
270 if (CHECK_FLAG(ldp_sync_info
->flags
,
271 LDP_SYNC_FLAG_SET_METRIC
)) {
272 ldp_sync_info
->metric
[level
-1] = metric
;
273 ldp_sync_info
->metric
[level
-1] = metric
;
280 void isis_ldp_sync_set_if_metric(struct isis_circuit
*circuit
, bool run_regen
)
282 struct ldp_sync_info
*ldp_sync_info
;
284 /* set interface metric:
285 * if LDP-IGP Sync is starting set metric so interface
286 * is used only as last resort
287 * else restore metric to original value
289 if (circuit
->ldp_sync_info
== NULL
|| circuit
->area
== NULL
)
292 ldp_sync_info
= circuit
->ldp_sync_info
;
293 if (ldp_sync_if_is_enabled(ldp_sync_info
)) {
294 /* if metric already set to LSInfinity just return */
295 if (CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_SET_METRIC
))
298 SET_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_SET_METRIC
);
299 if (circuit
->is_type
& IS_LEVEL_1
) {
300 if (circuit
->area
->newmetric
) {
301 ldp_sync_info
->metric
[0] =
302 circuit
->te_metric
[0];
303 circuit
->te_metric
[0] =
304 ISIS_WIDE_METRIC_INFINITY
;
306 ldp_sync_info
->metric
[0] = circuit
->metric
[0];
308 ISIS_NARROW_METRIC_INFINITY
;
311 if (circuit
->is_type
& IS_LEVEL_2
) {
312 if (circuit
->area
->newmetric
) {
313 ldp_sync_info
->metric
[1] =
314 circuit
->te_metric
[1];
315 circuit
->te_metric
[1] =
316 ISIS_WIDE_METRIC_INFINITY
;
318 ldp_sync_info
->metric
[1] = circuit
->metric
[1];
320 ISIS_NARROW_METRIC_INFINITY
;
324 /* if metric already restored just return */
325 if (!CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_SET_METRIC
))
328 UNSET_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_SET_METRIC
);
329 if (circuit
->is_type
& IS_LEVEL_1
) {
330 circuit
->te_metric
[0] = ldp_sync_info
->metric
[0];
331 circuit
->metric
[0] = ldp_sync_info
->metric
[0];
333 if (circuit
->is_type
& IS_LEVEL_2
) {
334 circuit
->te_metric
[1] = ldp_sync_info
->metric
[1];
335 circuit
->metric
[1] = ldp_sync_info
->metric
[1];
340 lsp_regenerate_schedule(circuit
->area
, circuit
->is_type
, 0);
345 * LDP-SYNC holddown timer routines
347 static int isis_ldp_sync_holddown_timer(struct thread
*thread
)
349 struct isis_circuit
*circuit
;
350 struct ldp_sync_info
*ldp_sync_info
;
352 /* holddown timer expired:
353 * didn't receive msg from LDP indicating sync-complete
354 * restore interface cost to original value
356 circuit
= THREAD_ARG(thread
);
357 if (circuit
->ldp_sync_info
== NULL
)
360 ldp_sync_info
= circuit
->ldp_sync_info
;
362 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_REQUIRED_UP
;
363 ldp_sync_info
->t_holddown
= NULL
;
365 ils_debug("ldp_sync: holddown timer expired for %s state:sync achieved",
366 circuit
->interface
->name
);
368 isis_ldp_sync_set_if_metric(circuit
, true);
372 void isis_ldp_sync_holddown_timer_add(struct isis_circuit
*circuit
)
374 struct ldp_sync_info
*ldp_sync_info
;
376 ldp_sync_info
= circuit
->ldp_sync_info
;
378 /* Start holddown timer:
379 * this timer is used to keep interface cost at LSInfinity
380 * once expires returns cost to original value
381 * if timer is already running or holddown time is off just return
383 if (ldp_sync_info
->t_holddown
||
384 ldp_sync_info
->holddown
== LDP_IGP_SYNC_HOLDDOWN_DEFAULT
)
387 ils_debug("ldp_sync: start holddown timer for %s time %d",
388 circuit
->interface
->name
, ldp_sync_info
->holddown
);
390 thread_add_timer(master
, isis_ldp_sync_holddown_timer
,
391 circuit
, ldp_sync_info
->holddown
,
392 &ldp_sync_info
->t_holddown
);
396 * LDP-SYNC handle client close routine
398 void isis_ldp_sync_handle_client_close(struct zapi_client_close_info
*info
)
400 struct isis_area
*area
;
401 struct listnode
*anode
, *cnode
;
402 struct isis_circuit
*circuit
;
403 struct isis
*isis
= isis_lookup_by_vrfid(VRF_DEFAULT
);
405 /* if isis is not enabled ignore */
409 /* Check if the LDP main client session closed */
410 if (info
->proto
!= ZEBRA_ROUTE_LDP
|| info
->session_id
== 0)
413 /* Handle the zebra notification that the LDP client session closed.
414 * set cost to LSInfinity
415 * send request to LDP for LDP-SYNC state for each interface
417 zlog_err("ldp_sync: LDP down");
419 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, anode
, area
)) {
420 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
423 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, cnode
, circuit
))
424 isis_ldp_sync_ldp_fail(circuit
);
429 * LDP-SYNC routes used by set commands.
432 void isis_area_ldp_sync_enable(struct isis_area
*area
)
434 struct isis_circuit
*circuit
;
435 struct listnode
*node
;
437 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
)) {
438 SET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
);
440 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
441 isis_if_ldp_sync_enable(circuit
);
445 void isis_area_ldp_sync_disable(struct isis_area
*area
)
447 struct isis_circuit
*circuit
;
448 struct listnode
*node
;
450 if (CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
)) {
451 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
452 isis_if_ldp_sync_disable(circuit
);
454 UNSET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
);
456 UNSET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
);
457 area
->ldp_sync_cmd
.holddown
= LDP_IGP_SYNC_HOLDDOWN_DEFAULT
;
461 void isis_area_ldp_sync_set_holddown(struct isis_area
*area
, uint16_t holddown
)
463 struct isis_circuit
*circuit
;
464 struct listnode
*node
;
466 if (holddown
== LDP_IGP_SYNC_HOLDDOWN_DEFAULT
)
467 UNSET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
);
469 SET_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
);
471 area
->ldp_sync_cmd
.holddown
= holddown
;
473 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
474 isis_if_set_ldp_sync_holddown(circuit
);
477 void isis_if_ldp_sync_enable(struct isis_circuit
*circuit
)
479 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
480 struct isis_area
*area
= circuit
->area
;
482 /* called when setting LDP-SYNC at the global level:
483 * specifed on interface overrides global config
484 * if ptop link send msg to LDP indicating ldp-sync enabled
486 if (if_is_loopback(circuit
->interface
))
489 if (circuit
->interface
->vrf
->vrf_id
!= VRF_DEFAULT
)
492 ils_debug("ldp_sync: enable if %s", circuit
->interface
->name
);
494 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
497 /* config on interface, overrides global config. */
498 if (CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_IF_CONFIG
))
499 if (ldp_sync_info
->enabled
!= LDP_IGP_SYNC_ENABLED
)
502 if (!CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_HOLDDOWN
))
503 ldp_sync_info
->holddown
= area
->ldp_sync_cmd
.holddown
;
505 if (circuit
->circ_type
== CIRCUIT_T_P2P
506 || if_is_pointopoint(circuit
->interface
)) {
507 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
;
508 isis_ldp_sync_state_req_msg(circuit
);
510 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_NOT_REQUIRED
;
511 ils_debug("ldp_sync: Sync only runs on P2P links %s",
512 circuit
->interface
->name
);
516 void isis_if_ldp_sync_disable(struct isis_circuit
*circuit
)
518 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
519 struct isis_area
*area
= circuit
->area
;
521 /* Stop LDP-SYNC on this interface:
522 * if holddown timer is running stop it
523 * delete ldp instance on interface
526 if (if_is_loopback(circuit
->interface
))
529 ils_debug("ldp_sync: remove if %s", circuit
->interface
->name
);
531 if (!CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_ENABLE
))
534 THREAD_OFF(ldp_sync_info
->t_holddown
);
535 ldp_sync_info
->state
= LDP_IGP_SYNC_STATE_NOT_REQUIRED
;
536 isis_ldp_sync_set_if_metric(circuit
, true);
539 void isis_if_set_ldp_sync_holddown(struct isis_circuit
*circuit
)
541 struct ldp_sync_info
*ldp_sync_info
= circuit
->ldp_sync_info
;
542 struct isis_area
*area
= circuit
->area
;
544 /* called when setting LDP-SYNC at the global level:
545 * specifed on interface overrides global config.
547 if (if_is_loopback(circuit
->interface
))
550 /* config on interface, overrides global config. */
551 if (CHECK_FLAG(ldp_sync_info
->flags
, LDP_SYNC_FLAG_HOLDDOWN
))
553 if (CHECK_FLAG(area
->ldp_sync_cmd
.flags
, LDP_SYNC_FLAG_HOLDDOWN
))
554 ldp_sync_info
->holddown
= area
->ldp_sync_cmd
.holddown
;
556 ldp_sync_info
->holddown
= LDP_IGP_SYNC_HOLDDOWN_DEFAULT
;
560 * LDP-SYNC routines used by show commands.
563 static void isis_circuit_ldp_sync_print_vty(struct isis_circuit
*circuit
,
566 struct ldp_sync_info
*ldp_sync_info
;
567 const char *ldp_state
;
569 if (circuit
->ldp_sync_info
== NULL
||
570 if_is_loopback(circuit
->interface
))
573 ldp_sync_info
= circuit
->ldp_sync_info
;
574 vty_out(vty
, "%-16s\n", circuit
->interface
->name
);
575 if (circuit
->state
== C_STATE_CONF
) {
576 vty_out(vty
, " Interface down\n");
580 vty_out(vty
, " LDP-IGP Synchronization enabled: %s\n",
581 ldp_sync_info
->enabled
== LDP_IGP_SYNC_ENABLED
584 vty_out(vty
, " holddown timer in seconds: %u\n",
585 ldp_sync_info
->holddown
);
587 switch (ldp_sync_info
->state
) {
588 case LDP_IGP_SYNC_STATE_REQUIRED_UP
:
589 vty_out(vty
, " State: Sync achieved\n");
591 case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP
:
592 if (ldp_sync_info
->t_holddown
!= NULL
) {
593 struct timeval remain
= thread_timer_remain(
594 ldp_sync_info
->t_holddown
);
596 " Holddown timer is running %lld.%03lld remaining\n",
597 (long long)remain
.tv_sec
,
598 (long long)remain
.tv_usec
/1000);
600 vty_out(vty
, " State: Holding down until Sync\n");
602 vty_out(vty
, " State: Sync not achieved\n");
604 case LDP_IGP_SYNC_STATE_NOT_REQUIRED
:
606 if ((circuit
->circ_type
!= CIRCUIT_T_P2P
&&
607 !if_is_pointopoint(circuit
->interface
)) &&
608 circuit
->circ_type
!= CIRCUIT_T_UNKNOWN
)
609 ldp_state
= "Sync not required: non-p2p link";
611 ldp_state
= "Sync not required";
612 vty_out(vty
, " State: %s\n", ldp_state
);
617 DEFUN (show_isis_mpls_ldp_interface
,
618 show_isis_mpls_ldp_interface_cmd
,
619 "show " PROTO_NAME
" mpls ldp-sync [interface <INTERFACE|all>]",
623 "LDP-IGP Sync information\n"
624 "Interface information\n"
630 struct listnode
*anode
, *cnode
;
631 struct isis_area
*area
;
632 struct isis_circuit
*circuit
;
633 struct isis
*isis
= isis_lookup_by_vrfid(VRF_DEFAULT
);
637 vty_out(vty
, "IS-IS Routing Process not enabled\n");
641 if (argv_find(argv
, argc
, "INTERFACE", &idx_intf
))
642 ifname
= argv
[idx_intf
]->arg
;
644 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, anode
, area
)) {
645 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, cnode
, circuit
))
647 isis_circuit_ldp_sync_print_vty(circuit
, vty
);
648 else if (strcmp(circuit
->interface
->name
, ifname
)
650 isis_circuit_ldp_sync_print_vty(circuit
, vty
);
655 if (found
== false && ifname
)
656 vty_out(vty
, "%-16s\n ISIS not enabled\n", ifname
);
661 void isis_ldp_sync_init(void)
664 /* "show ip isis mpls ldp interface" commands. */
665 install_element(VIEW_NODE
, &show_isis_mpls_ldp_interface_cmd
);
667 /* register for adjacency state changes */
668 hook_register(isis_adj_state_change_hook
,
669 isis_ldp_sync_adj_state_change
);