]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_circuit.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / isisd / isis_circuit.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * IS-IS Rout(e)ing protocol - isis_circuit.h
4 *
5 * Copyright (C) 2001,2002 Sampo Saaristo
6 * Tampere University of Technology
7 * Institute of Communications Engineering
8 */
9 #include <zebra.h>
10 #ifdef GNU_LINUX
11 #include <net/ethernet.h>
12 #else
13 #include <netinet/if_ether.h>
14 #endif
15
16 #include "log.h"
17 #include "memory.h"
18 #include "vrf.h"
19 #include "if.h"
20 #include "linklist.h"
21 #include "command.h"
22 #include "frrevent.h"
23 #include "vty.h"
24 #include "hash.h"
25 #include "prefix.h"
26 #include "stream.h"
27 #include "qobj.h"
28 #include "lib/northbound_cli.h"
29
30 #include "isisd/isis_constants.h"
31 #include "isisd/isis_common.h"
32 #include "isisd/isis_flags.h"
33 #include "isisd/isis_circuit.h"
34 #include "isisd/isis_lsp.h"
35 #include "isisd/isis_pdu.h"
36 #include "isisd/isis_network.h"
37 #include "isisd/isis_misc.h"
38 #include "isisd/isis_constants.h"
39 #include "isisd/isis_adjacency.h"
40 #include "isisd/isis_dr.h"
41 #include "isisd/isisd.h"
42 #include "isisd/isis_csm.h"
43 #include "isisd/isis_events.h"
44 #include "isisd/isis_te.h"
45 #include "isisd/isis_mt.h"
46 #include "isisd/isis_errors.h"
47 #include "isisd/isis_tx_queue.h"
48 #include "isisd/isis_nb.h"
49 #include "isisd/isis_ldp_sync.h"
50
51 DEFINE_MTYPE_STATIC(ISISD, ISIS_CIRCUIT, "ISIS circuit");
52
53 DEFINE_QOBJ_TYPE(isis_circuit);
54
55 DEFINE_HOOK(isis_if_new_hook, (struct interface *ifp), (ifp));
56
57 /*
58 * Prototypes.
59 */
60 int isis_if_new_hook(struct interface *);
61 int isis_if_delete_hook(struct interface *);
62
63 DEFINE_HOOK(isis_circuit_new_hook, (struct isis_circuit *circuit), (circuit));
64 DEFINE_HOOK(isis_circuit_del_hook, (struct isis_circuit *circuit), (circuit));
65
66 static void isis_circuit_enable(struct isis_circuit *circuit)
67 {
68 struct isis_area *area = circuit->area;
69 struct interface *ifp = circuit->interface;
70
71 if (!area) {
72 area = isis_area_lookup(circuit->tag, ifp->vrf->vrf_id);
73 if (area)
74 isis_area_add_circuit(area, circuit);
75 }
76
77 if (if_is_operative(ifp))
78 isis_csm_state_change(IF_UP_FROM_Z, circuit, ifp);
79 }
80
81 static void isis_circuit_disable(struct isis_circuit *circuit)
82 {
83 struct isis_area *area = circuit->area;
84 struct interface *ifp = circuit->interface;
85
86 if (if_is_operative(ifp))
87 isis_csm_state_change(IF_DOWN_FROM_Z, circuit, ifp);
88
89 if (area)
90 isis_area_del_circuit(area, circuit);
91 }
92
93 struct isis_circuit *isis_circuit_new(struct interface *ifp, const char *tag)
94 {
95 struct isis_circuit *circuit;
96 int i;
97
98 circuit = XCALLOC(MTYPE_ISIS_CIRCUIT, sizeof(struct isis_circuit));
99
100 circuit->tag = XSTRDUP(MTYPE_ISIS_CIRCUIT, tag);
101
102 /*
103 * Default values
104 */
105 #ifndef FABRICD
106 circuit->is_type_config = yang_get_default_enum(
107 "/frr-interface:lib/interface/frr-isisd:isis/circuit-type");
108 circuit->flags = 0;
109
110 circuit->pad_hellos = yang_get_default_enum(
111 "/frr-interface:lib/interface/frr-isisd:isis/hello/padding");
112 circuit->hello_interval[0] = yang_get_default_uint32(
113 "/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-1");
114 circuit->hello_interval[1] = yang_get_default_uint32(
115 "/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-2");
116 circuit->hello_multiplier[0] = yang_get_default_uint32(
117 "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-1");
118 circuit->hello_multiplier[1] = yang_get_default_uint32(
119 "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-2");
120 circuit->csnp_interval[0] = yang_get_default_uint16(
121 "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-1");
122 circuit->csnp_interval[1] = yang_get_default_uint16(
123 "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-2");
124 circuit->psnp_interval[0] = yang_get_default_uint16(
125 "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-1");
126 circuit->psnp_interval[1] = yang_get_default_uint16(
127 "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-2");
128 circuit->priority[0] = yang_get_default_uint8(
129 "/frr-interface:lib/interface/frr-isisd:isis/priority/level-1");
130 circuit->priority[1] = yang_get_default_uint8(
131 "/frr-interface:lib/interface/frr-isisd:isis/priority/level-2");
132 circuit->metric[0] = yang_get_default_uint32(
133 "/frr-interface:lib/interface/frr-isisd:isis/metric/level-1");
134 circuit->metric[1] = yang_get_default_uint32(
135 "/frr-interface:lib/interface/frr-isisd:isis/metric/level-2");
136 circuit->te_metric[0] = yang_get_default_uint32(
137 "/frr-interface:lib/interface/frr-isisd:isis/metric/level-1");
138 circuit->te_metric[1] = yang_get_default_uint32(
139 "/frr-interface:lib/interface/frr-isisd:isis/metric/level-2");
140
141 for (i = 0; i < 2; i++) {
142 circuit->level_arg[i].level = i + 1;
143 circuit->level_arg[i].circuit = circuit;
144 }
145 #else
146 circuit->is_type_config = IS_LEVEL_1_AND_2;
147 circuit->flags = 0;
148 circuit->pad_hellos = ISIS_HELLO_PADDING_ALWAYS;
149 for (i = 0; i < 2; i++) {
150 circuit->hello_interval[i] = DEFAULT_HELLO_INTERVAL;
151 circuit->hello_multiplier[i] = DEFAULT_HELLO_MULTIPLIER;
152 circuit->csnp_interval[i] = DEFAULT_CSNP_INTERVAL;
153 circuit->psnp_interval[i] = DEFAULT_PSNP_INTERVAL;
154 circuit->priority[i] = DEFAULT_PRIORITY;
155 circuit->metric[i] = DEFAULT_CIRCUIT_METRIC;
156 circuit->te_metric[i] = DEFAULT_CIRCUIT_METRIC;
157 circuit->level_arg[i].level = i + 1;
158 circuit->level_arg[i].circuit = circuit;
159 }
160 #endif /* ifndef FABRICD */
161
162 circuit->is_type = circuit->is_type_config;
163
164 circuit_mt_init(circuit);
165 isis_lfa_excluded_ifaces_init(circuit, ISIS_LEVEL1);
166 isis_lfa_excluded_ifaces_init(circuit, ISIS_LEVEL2);
167
168 circuit->ldp_sync_info = ldp_sync_info_create();
169 circuit->ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED;
170
171 QOBJ_REG(circuit, isis_circuit);
172
173 isis_circuit_if_bind(circuit, ifp);
174
175 circuit->ip_addrs = list_new();
176 circuit->ipv6_link = list_new();
177 circuit->ipv6_non_link = list_new();
178
179 if (ifp->ifindex != IFINDEX_INTERNAL)
180 isis_circuit_enable(circuit);
181
182 return circuit;
183 }
184
185 void isis_circuit_del(struct isis_circuit *circuit)
186 {
187 if (!circuit)
188 return;
189
190 if (circuit->interface->ifindex != IFINDEX_INTERNAL)
191 isis_circuit_disable(circuit);
192
193 isis_circuit_if_unbind(circuit, circuit->interface);
194
195 QOBJ_UNREG(circuit);
196
197 ldp_sync_info_free(&circuit->ldp_sync_info);
198
199 circuit_mt_finish(circuit);
200 isis_lfa_excluded_ifaces_clear(circuit, ISIS_LEVEL1);
201 isis_lfa_excluded_ifaces_clear(circuit, ISIS_LEVEL2);
202
203 list_delete(&circuit->ip_addrs);
204 list_delete(&circuit->ipv6_link);
205 list_delete(&circuit->ipv6_non_link);
206
207 if (circuit->ext) {
208 isis_del_ext_subtlvs(circuit->ext);
209 circuit->ext = NULL;
210 }
211
212 XFREE(MTYPE_TMP, circuit->bfd_config.profile);
213 XFREE(MTYPE_ISIS_CIRCUIT, circuit->tag);
214
215 /* and lastly the circuit itself */
216 XFREE(MTYPE_ISIS_CIRCUIT, circuit);
217
218 return;
219 }
220
221 void isis_circuit_configure(struct isis_circuit *circuit,
222 struct isis_area *area)
223 {
224 assert(area);
225 circuit->isis = area->isis;
226 circuit->area = area;
227
228 /*
229 * Whenever the is-type of an area is changed, the is-type of each
230 * circuit
231 * in that area is updated to a non-empty subset of the area is-type.
232 * Inversely, when configuring a new circuit, this property should be
233 * ensured as well.
234 */
235 if (area->is_type != IS_LEVEL_1_AND_2)
236 circuit->is_type = area->is_type;
237
238 /*
239 * Add the circuit into area
240 */
241 listnode_add(area->circuit_list, circuit);
242
243 circuit->idx = flags_get_index(&area->flags);
244
245 hook_call(isis_circuit_new_hook, circuit);
246
247 return;
248 }
249
250 void isis_circuit_deconfigure(struct isis_circuit *circuit,
251 struct isis_area *area)
252 {
253 hook_call(isis_circuit_del_hook, circuit);
254
255 /* Free the index of SRM and SSN flags */
256 flags_free_index(&area->flags, circuit->idx);
257 circuit->idx = 0;
258
259 /* Reset IS type to configured */
260 circuit->is_type = circuit->is_type_config;
261
262 /* Remove circuit from area */
263 assert(circuit->area == area);
264 listnode_delete(area->circuit_list, circuit);
265 circuit->area = NULL;
266 circuit->isis = NULL;
267
268 return;
269 }
270
271 struct isis_circuit *circuit_scan_by_ifp(struct interface *ifp)
272 {
273 return (struct isis_circuit *)ifp->info;
274 }
275
276 DEFINE_HOOK(isis_circuit_add_addr_hook, (struct isis_circuit *circuit),
277 (circuit));
278
279 void isis_circuit_add_addr(struct isis_circuit *circuit,
280 struct connected *connected)
281 {
282 struct listnode *node;
283 struct prefix_ipv4 *ipv4;
284 struct prefix_ipv6 *ipv6;
285
286 if (connected->address->family == AF_INET) {
287 uint32_t addr = connected->address->u.prefix4.s_addr;
288 addr = ntohl(addr);
289 if (IPV4_NET0(addr) || IPV4_NET127(addr) || IN_CLASSD(addr))
290 return;
291
292 for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, ipv4))
293 if (prefix_same((struct prefix *)ipv4,
294 connected->address))
295 return;
296
297 ipv4 = prefix_ipv4_new();
298 ipv4->prefixlen = connected->address->prefixlen;
299 ipv4->prefix = connected->address->u.prefix4;
300 listnode_add(circuit->ip_addrs, ipv4);
301
302 /* Update Local IP address parameter if MPLS TE is enable */
303 if (circuit->ext && circuit->area
304 && IS_MPLS_TE(circuit->area->mta)) {
305 circuit->ext->local_addr.s_addr = ipv4->prefix.s_addr;
306 SET_SUBTLV(circuit->ext, EXT_LOCAL_ADDR);
307 }
308
309 if (circuit->area)
310 lsp_regenerate_schedule(circuit->area, circuit->is_type,
311 0);
312
313 #ifdef EXTREME_DEBUG
314 if (IS_DEBUG_EVENTS)
315 zlog_debug("Added IP address %pFX to circuit %s",
316 connected->address,
317 circuit->interface->name);
318 #endif /* EXTREME_DEBUG */
319 }
320 if (connected->address->family == AF_INET6) {
321 if (IN6_IS_ADDR_LOOPBACK(&connected->address->u.prefix6))
322 return;
323
324 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node, ipv6))
325 if (prefix_same((struct prefix *)ipv6,
326 connected->address))
327 return;
328 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node, ipv6))
329 if (prefix_same((struct prefix *)ipv6,
330 connected->address))
331 return;
332
333 ipv6 = prefix_ipv6_new();
334 ipv6->prefixlen = connected->address->prefixlen;
335 ipv6->prefix = connected->address->u.prefix6;
336
337 if (IN6_IS_ADDR_LINKLOCAL(&ipv6->prefix))
338 listnode_add(circuit->ipv6_link, ipv6);
339 else {
340 listnode_add(circuit->ipv6_non_link, ipv6);
341 /* Update Local IPv6 address param. if MPLS TE is on */
342 if (circuit->ext && circuit->area
343 && IS_MPLS_TE(circuit->area->mta)) {
344 IPV6_ADDR_COPY(&circuit->ext->local_addr6,
345 &ipv6->prefix);
346 SET_SUBTLV(circuit->ext, EXT_LOCAL_ADDR6);
347 }
348 }
349 if (circuit->area)
350 lsp_regenerate_schedule(circuit->area, circuit->is_type,
351 0);
352
353 #ifdef EXTREME_DEBUG
354 if (IS_DEBUG_EVENTS)
355 zlog_debug("Added IPv6 address %pFX to circuit %s",
356 connected->address,
357 circuit->interface->name);
358 #endif /* EXTREME_DEBUG */
359 }
360
361 hook_call(isis_circuit_add_addr_hook, circuit);
362
363 return;
364 }
365
366 void isis_circuit_del_addr(struct isis_circuit *circuit,
367 struct connected *connected)
368 {
369 struct prefix_ipv4 *ipv4, *ip = NULL;
370 struct listnode *node;
371 struct prefix_ipv6 *ipv6, *ip6 = NULL;
372 int found = 0;
373
374 if (connected->address->family == AF_INET) {
375 ipv4 = prefix_ipv4_new();
376 ipv4->prefixlen = connected->address->prefixlen;
377 ipv4->prefix = connected->address->u.prefix4;
378
379 for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, ip))
380 if (prefix_same((struct prefix *)ip,
381 (struct prefix *)ipv4))
382 break;
383
384 if (ip) {
385 listnode_delete(circuit->ip_addrs, ip);
386 prefix_ipv4_free(&ip);
387 if (circuit->area)
388 lsp_regenerate_schedule(circuit->area,
389 circuit->is_type, 0);
390 } else {
391 zlog_warn(
392 "Nonexistent ip address %pFX removal attempt from circuit %s",
393 connected->address, circuit->interface->name);
394 zlog_warn("Current ip addresses on %s:",
395 circuit->interface->name);
396 for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node,
397 ip)) {
398 zlog_warn(" %pFX", ip);
399 }
400 zlog_warn("End of addresses");
401 }
402
403 prefix_ipv4_free(&ipv4);
404 }
405 if (connected->address->family == AF_INET6) {
406 ipv6 = prefix_ipv6_new();
407 ipv6->prefixlen = connected->address->prefixlen;
408 ipv6->prefix = connected->address->u.prefix6;
409
410 if (IN6_IS_ADDR_LINKLOCAL(&ipv6->prefix)) {
411 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node,
412 ip6)) {
413 if (prefix_same((struct prefix *)ip6,
414 (struct prefix *)ipv6))
415 break;
416 }
417 if (ip6) {
418 listnode_delete(circuit->ipv6_link, ip6);
419 prefix_ipv6_free(&ip6);
420 found = 1;
421 }
422 } else {
423 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node,
424 ip6)) {
425 if (prefix_same((struct prefix *)ip6,
426 (struct prefix *)ipv6))
427 break;
428 }
429 if (ip6) {
430 listnode_delete(circuit->ipv6_non_link, ip6);
431 prefix_ipv6_free(&ip6);
432 found = 1;
433 }
434 }
435
436 if (!found) {
437 zlog_warn(
438 "Nonexistent ip address %pFX removal attempt from circuit %s",
439 connected->address, circuit->interface->name);
440 zlog_warn("Current ip addresses on %s:",
441 circuit->interface->name);
442 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node,
443 ip6))
444 zlog_warn(" %pFX", (struct prefix *)ip6);
445 zlog_warn(" -----");
446 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node,
447 ip6))
448 zlog_warn(" %pFX", (struct prefix *)ip6);
449 zlog_warn("End of addresses");
450 } else if (circuit->area)
451 lsp_regenerate_schedule(circuit->area, circuit->is_type,
452 0);
453
454 prefix_ipv6_free(&ipv6);
455 }
456 return;
457 }
458
459 static uint8_t isis_circuit_id_gen(struct isis *isis, struct interface *ifp)
460 {
461 /* Circuit ids MUST be unique for any broadcast circuits. Otherwise,
462 * Pseudo-Node LSPs cannot be generated correctly.
463 *
464 * Currently, allocate one circuit ID for any circuit, limiting the total
465 * numer of circuits IS-IS can run on to 255.
466 *
467 * We should revisit this when implementing 3-way adjacencies for p2p, since
468 * we then have extended interface IDs available.
469 */
470 uint8_t id = ifp->ifindex;
471 unsigned int i;
472
473 for (i = 0; i < 256; i++) {
474 if (id && !_ISIS_CHECK_FLAG(isis->circuit_ids_used, id))
475 break;
476 id++;
477 }
478
479 if (i == 256) {
480 zlog_warn("Could not allocate a circuit id for '%s'",
481 ifp->name);
482 return 0;
483 }
484
485 _ISIS_SET_FLAG(isis->circuit_ids_used, id);
486 return id;
487 }
488
489 void isis_circuit_if_add(struct isis_circuit *circuit, struct interface *ifp)
490 {
491 struct listnode *node, *nnode;
492 struct connected *conn;
493
494 if (if_is_broadcast(ifp)) {
495 if (fabricd || circuit->circ_type_config == CIRCUIT_T_P2P)
496 circuit->circ_type = CIRCUIT_T_P2P;
497 else
498 circuit->circ_type = CIRCUIT_T_BROADCAST;
499 } else if (if_is_pointopoint(ifp)) {
500 circuit->circ_type = CIRCUIT_T_P2P;
501 } else if (if_is_loopback(ifp)) {
502 circuit->circ_type = CIRCUIT_T_LOOPBACK;
503 circuit->is_passive = 1;
504 } else {
505 /* It's normal in case of loopback etc. */
506 if (IS_DEBUG_EVENTS)
507 zlog_debug("%s: unsupported media", __func__);
508 circuit->circ_type = CIRCUIT_T_UNKNOWN;
509 }
510
511 for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, conn))
512 isis_circuit_add_addr(circuit, conn);
513
514 }
515
516 void isis_circuit_if_del(struct isis_circuit *circuit, struct interface *ifp)
517 {
518 struct listnode *node, *nnode;
519 struct connected *conn;
520
521 assert(circuit->interface == ifp);
522
523 /* destroy addresses */
524 for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, conn))
525 isis_circuit_del_addr(circuit, conn);
526
527 circuit->circ_type = CIRCUIT_T_UNKNOWN;
528 }
529
530 void isis_circuit_if_bind(struct isis_circuit *circuit, struct interface *ifp)
531 {
532 assert(circuit != NULL);
533 assert(ifp != NULL);
534 if (circuit->interface)
535 assert(circuit->interface == ifp);
536 else
537 circuit->interface = ifp;
538 if (ifp->info)
539 assert(ifp->info == circuit);
540 else
541 ifp->info = circuit;
542 }
543
544 void isis_circuit_if_unbind(struct isis_circuit *circuit, struct interface *ifp)
545 {
546 assert(circuit != NULL);
547 assert(ifp != NULL);
548 assert(circuit->interface == ifp);
549 assert(ifp->info == circuit);
550 circuit->interface = NULL;
551 ifp->info = NULL;
552 }
553
554 static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit,
555 int is_set)
556 {
557 struct isis_area *area;
558 struct isis_lsp *lsp;
559 int level;
560
561 assert(circuit);
562 area = circuit->area;
563 assert(area);
564 for (level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
565 if (!(level & circuit->is_type))
566 continue;
567
568 if (!lspdb_count(&area->lspdb[level - 1]))
569 continue;
570
571 frr_each (lspdb, &area->lspdb[level - 1], lsp) {
572 if (is_set) {
573 isis_tx_queue_add(circuit->tx_queue, lsp,
574 TX_LSP_NORMAL);
575 } else {
576 isis_tx_queue_del(circuit->tx_queue, lsp);
577 }
578 }
579 }
580 }
581
582 size_t isis_circuit_pdu_size(struct isis_circuit *circuit)
583 {
584 return ISO_MTU(circuit);
585 }
586
587 static bool isis_circuit_lfa_enabled(struct isis_circuit *circuit, int level)
588 {
589 return (circuit->lfa_protection[level - 1] ||
590 circuit->rlfa_protection[level - 1] ||
591 circuit->tilfa_protection[level - 1]);
592 }
593
594 void isis_circuit_switchover_routes(struct isis_circuit *circuit, int family,
595 union g_addr *nexthop_ip, ifindex_t ifindex)
596 {
597 char is_type;
598
599 if (!circuit->area)
600 return;
601
602 is_type = circuit->area->is_type;
603 if ((is_type == IS_LEVEL_1 || is_type == IS_LEVEL_1_AND_2) &&
604 isis_circuit_lfa_enabled(circuit, IS_LEVEL_1))
605 isis_area_switchover_routes(circuit->area, family, nexthop_ip,
606 ifindex, IS_LEVEL_1);
607 if ((is_type == IS_LEVEL_2 || is_type == IS_LEVEL_1_AND_2) &&
608 isis_circuit_lfa_enabled(circuit, IS_LEVEL_2))
609 isis_area_switchover_routes(circuit->area, family, nexthop_ip,
610 ifindex, IS_LEVEL_2);
611 }
612
613 void isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream)
614 {
615 size_t stream_size = isis_circuit_pdu_size(circuit);
616
617 if (!*stream) {
618 *stream = stream_new(stream_size);
619 } else {
620 if (STREAM_SIZE(*stream) != stream_size)
621 stream_resize_inplace(stream, stream_size);
622 stream_reset(*stream);
623 }
624 }
625
626 void isis_circuit_prepare(struct isis_circuit *circuit)
627 {
628 #if ISIS_METHOD != ISIS_METHOD_DLPI
629 event_add_read(master, isis_receive, circuit, circuit->fd,
630 &circuit->t_read);
631 #else
632 event_add_timer_msec(master, isis_receive, circuit,
633 listcount(circuit->area->circuit_list) * 100,
634 &circuit->t_read);
635 #endif
636 }
637
638 int isis_circuit_up(struct isis_circuit *circuit)
639 {
640 int retv;
641
642 /* Set the flags for all the lsps of the circuit. */
643 isis_circuit_update_all_srmflags(circuit, 1);
644
645 if (circuit->state == C_STATE_UP)
646 return ISIS_OK;
647
648 if (circuit->is_passive) {
649 circuit->last_uptime = time(NULL);
650 /* make sure the union fields are initialized, else we
651 * could end with garbage values from a previous circuit
652 * type, which would then cause a segfault when building
653 * LSPs or computing the SPF tree
654 */
655 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
656 circuit->u.bc.adjdb[0] = list_new();
657 circuit->u.bc.adjdb[1] = list_new();
658 } else if (circuit->circ_type == CIRCUIT_T_P2P) {
659 circuit->u.p2p.neighbor = NULL;
660 }
661 return ISIS_OK;
662 }
663
664 if (circuit->area->lsp_mtu > isis_circuit_pdu_size(circuit)) {
665 flog_err(
666 EC_ISIS_CONFIG,
667 "Interface MTU %zu on %s is too low to support area lsp mtu %u!",
668 isis_circuit_pdu_size(circuit),
669 circuit->interface->name, circuit->area->lsp_mtu);
670
671 /* Allow ISIS to continue configuration. With this
672 * configuration failure ISIS will attempt to send lsp
673 * packets but will fail until the mtu is configured properly
674 */
675 }
676
677 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
678 circuit->circuit_id =
679 isis_circuit_id_gen(circuit->isis, circuit->interface);
680 if (!circuit->circuit_id) {
681 flog_err(
682 EC_ISIS_CONFIG,
683 "There are already 255 broadcast circuits active!");
684 return ISIS_ERROR;
685 }
686
687 /*
688 * Get the Hardware Address
689 */
690 if (circuit->interface->hw_addr_len != ETH_ALEN) {
691 zlog_warn("unsupported link layer");
692 } else {
693 memcpy(circuit->u.bc.snpa, circuit->interface->hw_addr,
694 ETH_ALEN);
695 }
696 #ifdef EXTREME_DEGUG
697 if (IS_DEBUG_EVENTS)
698 zlog_debug("%s: if_id %d, isomtu %d snpa %pSY",
699 __func__, circuit->interface->ifindex,
700 ISO_MTU(circuit), circuit->u.bc.snpa);
701 #endif /* EXTREME_DEBUG */
702
703 circuit->u.bc.adjdb[0] = list_new();
704 circuit->u.bc.adjdb[1] = list_new();
705
706 /*
707 * ISO 10589 - 8.4.1 Enabling of broadcast circuits
708 */
709
710 /* initilizing the hello sending threads
711 * for a broadcast IF
712 */
713
714 /* 8.4.1 a) commence sending of IIH PDUs */
715
716 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
717 if (!(circuit->is_type & level))
718 continue;
719
720 send_hello_sched(circuit, level, TRIGGERED_IIH_DELAY);
721 circuit->u.bc.lan_neighs[level - 1] = list_new();
722
723 event_add_timer(master, isis_run_dr,
724 &circuit->level_arg[level - 1],
725 2 * circuit->hello_interval[level - 1],
726 &circuit->u.bc.t_run_dr[level - 1]);
727 }
728
729 /* 8.4.1 b) FIXME: solicit ES - 8.4.6 */
730 /* 8.4.1 c) FIXME: listen for ESH PDUs */
731 } else if (circuit->circ_type == CIRCUIT_T_P2P) {
732 /* initializing the hello send threads
733 * for a ptp IF
734 */
735 circuit->u.p2p.neighbor = NULL;
736 send_hello_sched(circuit, 0, TRIGGERED_IIH_DELAY);
737 }
738
739 /* initializing PSNP timers */
740 if (circuit->is_type & IS_LEVEL_1)
741 event_add_timer(
742 master, send_l1_psnp, circuit,
743 isis_jitter(circuit->psnp_interval[0], PSNP_JITTER),
744 &circuit->t_send_psnp[0]);
745
746 if (circuit->is_type & IS_LEVEL_2)
747 event_add_timer(
748 master, send_l2_psnp, circuit,
749 isis_jitter(circuit->psnp_interval[1], PSNP_JITTER),
750 &circuit->t_send_psnp[1]);
751
752 /* unified init for circuits; ignore warnings below this level */
753 retv = isis_sock_init(circuit);
754 if (retv != ISIS_OK) {
755 isis_circuit_down(circuit);
756 return retv;
757 }
758
759 /* initialize the circuit streams after opening connection */
760 isis_circuit_stream(circuit, &circuit->rcv_stream);
761 isis_circuit_stream(circuit, &circuit->snd_stream);
762
763 isis_circuit_prepare(circuit);
764
765 circuit->tx_queue = isis_tx_queue_new(circuit, send_lsp);
766
767 circuit->last_uptime = time(NULL);
768
769 if (circuit->area->mta && circuit->area->mta->status)
770 isis_link_params_update(circuit, circuit->interface);
771
772 isis_if_ldp_sync_enable(circuit);
773
774 #ifndef FABRICD
775 /* send northbound notification */
776 isis_notif_if_state_change(circuit, false);
777 #endif /* ifndef FABRICD */
778
779 return ISIS_OK;
780 }
781
782 void isis_circuit_down(struct isis_circuit *circuit)
783 {
784 #ifndef FABRICD
785 /* send northbound notification */
786 isis_notif_if_state_change(circuit, true);
787 #endif /* ifndef FABRICD */
788
789 isis_if_ldp_sync_disable(circuit);
790
791 /* log adjacency changes if configured to do so */
792 if (circuit->area->log_adj_changes) {
793 struct isis_adjacency *adj = NULL;
794 if (circuit->circ_type == CIRCUIT_T_P2P) {
795 adj = circuit->u.p2p.neighbor;
796 if (adj)
797 isis_log_adj_change(
798 adj, adj->adj_state, ISIS_ADJ_DOWN,
799 "circuit is being brought down");
800 } else if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
801 struct list *adj_list;
802 struct listnode *node;
803 if (circuit->u.bc.adjdb[0]) {
804 adj_list = list_new();
805 isis_adj_build_up_list(circuit->u.bc.adjdb[0],
806 adj_list);
807 for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj))
808 isis_log_adj_change(
809 adj, adj->adj_state,
810 ISIS_ADJ_DOWN,
811 "circuit is being brought down");
812 list_delete(&adj_list);
813 }
814 if (circuit->u.bc.adjdb[1]) {
815 adj_list = list_new();
816 isis_adj_build_up_list(circuit->u.bc.adjdb[1],
817 adj_list);
818 for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj))
819 isis_log_adj_change(
820 adj, adj->adj_state,
821 ISIS_ADJ_DOWN,
822 "circuit is being brought down");
823 list_delete(&adj_list);
824 }
825 }
826 }
827
828 /* Clear the flags for all the lsps of the circuit. */
829 isis_circuit_update_all_srmflags(circuit, 0);
830
831 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
832 /* destroy neighbour lists */
833 if (circuit->u.bc.lan_neighs[0]) {
834 list_delete(&circuit->u.bc.lan_neighs[0]);
835 circuit->u.bc.lan_neighs[0] = NULL;
836 }
837 if (circuit->u.bc.lan_neighs[1]) {
838 list_delete(&circuit->u.bc.lan_neighs[1]);
839 circuit->u.bc.lan_neighs[1] = NULL;
840 }
841 /* destroy adjacency databases */
842 if (circuit->u.bc.adjdb[0]) {
843 circuit->u.bc.adjdb[0]->del = isis_delete_adj;
844 list_delete(&circuit->u.bc.adjdb[0]);
845 circuit->u.bc.adjdb[0] = NULL;
846 }
847 if (circuit->u.bc.adjdb[1]) {
848 circuit->u.bc.adjdb[1]->del = isis_delete_adj;
849 list_delete(&circuit->u.bc.adjdb[1]);
850 circuit->u.bc.adjdb[1] = NULL;
851 }
852 if (circuit->u.bc.is_dr[0]) {
853 isis_dr_resign(circuit, 1);
854 circuit->u.bc.is_dr[0] = 0;
855 }
856 memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
857 if (circuit->u.bc.is_dr[1]) {
858 isis_dr_resign(circuit, 2);
859 circuit->u.bc.is_dr[1] = 0;
860 }
861 memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
862 memset(circuit->u.bc.snpa, 0, ETH_ALEN);
863
864 EVENT_OFF(circuit->u.bc.t_send_lan_hello[0]);
865 EVENT_OFF(circuit->u.bc.t_send_lan_hello[1]);
866 EVENT_OFF(circuit->u.bc.t_run_dr[0]);
867 EVENT_OFF(circuit->u.bc.t_run_dr[1]);
868 EVENT_OFF(circuit->u.bc.t_refresh_pseudo_lsp[0]);
869 EVENT_OFF(circuit->u.bc.t_refresh_pseudo_lsp[1]);
870 circuit->lsp_regenerate_pending[0] = 0;
871 circuit->lsp_regenerate_pending[1] = 0;
872
873 _ISIS_CLEAR_FLAG(circuit->isis->circuit_ids_used,
874 circuit->circuit_id);
875 circuit->circuit_id = 0;
876 } else if (circuit->circ_type == CIRCUIT_T_P2P) {
877 isis_delete_adj(circuit->u.p2p.neighbor);
878 circuit->u.p2p.neighbor = NULL;
879 EVENT_OFF(circuit->u.p2p.t_send_p2p_hello);
880 }
881
882 /*
883 * All adjacencies have to be gone, delete snmp list
884 * and reset snmpd idx generator
885 */
886 if (circuit->snmp_adj_list != NULL)
887 list_delete(&circuit->snmp_adj_list);
888
889 circuit->snmp_adj_idx_gen = 0;
890
891 /* Cancel all active threads */
892 EVENT_OFF(circuit->t_send_csnp[0]);
893 EVENT_OFF(circuit->t_send_csnp[1]);
894 EVENT_OFF(circuit->t_send_psnp[0]);
895 EVENT_OFF(circuit->t_send_psnp[1]);
896 EVENT_OFF(circuit->t_read);
897
898 if (circuit->tx_queue) {
899 isis_tx_queue_free(circuit->tx_queue);
900 circuit->tx_queue = NULL;
901 }
902
903 /* send one gratuitous hello to spead up convergence */
904 if (circuit->state == C_STATE_UP) {
905 if (circuit->is_type & IS_LEVEL_1)
906 send_hello(circuit, IS_LEVEL_1);
907 if (circuit->is_type & IS_LEVEL_2)
908 send_hello(circuit, IS_LEVEL_2);
909 }
910
911 circuit->upadjcount[0] = 0;
912 circuit->upadjcount[1] = 0;
913
914 /* close the socket */
915 if (circuit->fd) {
916 close(circuit->fd);
917 circuit->fd = 0;
918 }
919
920 if (circuit->rcv_stream != NULL) {
921 stream_free(circuit->rcv_stream);
922 circuit->rcv_stream = NULL;
923 }
924
925 if (circuit->snd_stream != NULL) {
926 stream_free(circuit->snd_stream);
927 circuit->snd_stream = NULL;
928 }
929
930 event_cancel_event(master, circuit);
931
932 return;
933 }
934
935 void circuit_update_nlpids(struct isis_circuit *circuit)
936 {
937 circuit->nlpids.count = 0;
938
939 if (circuit->ip_router) {
940 circuit->nlpids.nlpids[0] = NLPID_IP;
941 circuit->nlpids.count++;
942 }
943 if (circuit->ipv6_router) {
944 circuit->nlpids.nlpids[circuit->nlpids.count] = NLPID_IPV6;
945 circuit->nlpids.count++;
946 }
947 return;
948 }
949
950 void isis_circuit_print_json(struct isis_circuit *circuit,
951 struct json_object *json, char detail)
952 {
953 int level;
954 json_object *iface_json, *ipv4_addr_json, *ipv6_link_json,
955 *ipv6_non_link_json, *hold_json, *lan_prio_json, *levels_json,
956 *level_json;
957 char buf_prx[INET6_BUFSIZ];
958 char buf[255];
959
960 snprintfrr(buf, sizeof(buf), "0x%x", circuit->circuit_id);
961 if (detail == ISIS_UI_LEVEL_BRIEF) {
962 iface_json = json_object_new_object();
963 json_object_object_add(json, "interface", iface_json);
964 json_object_string_add(iface_json, "name",
965 circuit->interface->name);
966 json_object_string_add(iface_json, "circuit-id", buf);
967 json_object_string_add(iface_json, "state",
968 circuit_state2string(circuit->state));
969 json_object_string_add(iface_json, "type",
970 circuit_type2string(circuit->circ_type));
971 json_object_string_add(iface_json, "level",
972 circuit_t2string(circuit->is_type));
973 }
974
975 if (detail == ISIS_UI_LEVEL_DETAIL) {
976 struct listnode *node;
977 struct prefix *ip_addr;
978
979 iface_json = json_object_new_object();
980 json_object_object_add(json, "interface", iface_json);
981 json_object_string_add(iface_json, "name",
982 circuit->interface->name);
983 json_object_string_add(iface_json, "state",
984 circuit_state2string(circuit->state));
985 if (circuit->is_passive)
986 json_object_string_add(iface_json, "is-passive",
987 "passive");
988 else
989 json_object_string_add(iface_json, "is-passive",
990 "active");
991 json_object_string_add(iface_json, "circuit-id", buf);
992 json_object_string_add(iface_json, "type",
993 circuit_type2string(circuit->circ_type));
994 json_object_string_add(iface_json, "level",
995 circuit_t2string(circuit->is_type));
996 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
997 json_object_string_addf(iface_json, "snpa", "%pSY",
998 circuit->u.bc.snpa);
999
1000
1001 levels_json = json_object_new_array();
1002 json_object_object_add(iface_json, "levels", levels_json);
1003 for (level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
1004 if ((circuit->is_type & level) == 0)
1005 continue;
1006 level_json = json_object_new_object();
1007 json_object_string_add(level_json, "level",
1008 circuit_t2string(level));
1009 if (circuit->area->newmetric)
1010 json_object_int_add(level_json, "metric",
1011 circuit->te_metric[0]);
1012 else
1013 json_object_int_add(level_json, "metric",
1014 circuit->metric[0]);
1015 if (!circuit->is_passive) {
1016 json_object_int_add(level_json,
1017 "active-neighbors",
1018 circuit->upadjcount[0]);
1019 json_object_int_add(level_json,
1020 "hello-interval",
1021 circuit->hello_interval[0]);
1022 hold_json = json_object_new_object();
1023 json_object_object_add(level_json, "holddown",
1024 hold_json);
1025 json_object_int_add(
1026 hold_json, "count",
1027 circuit->hello_multiplier[0]);
1028 json_object_string_add(
1029 hold_json, "pad",
1030 isis_hello_padding2string(
1031 circuit->pad_hellos));
1032 json_object_int_add(level_json, "cnsp-interval",
1033 circuit->csnp_interval[0]);
1034 json_object_int_add(level_json, "psnp-interval",
1035 circuit->psnp_interval[0]);
1036 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
1037 lan_prio_json =
1038 json_object_new_object();
1039 json_object_object_add(level_json,
1040 "lan",
1041 lan_prio_json);
1042 json_object_int_add(
1043 lan_prio_json, "priority",
1044 circuit->priority[0]);
1045 json_object_string_add(
1046 lan_prio_json, "is-dis",
1047 (circuit->u.bc.is_dr[0]
1048 ? "yes"
1049 : "no"));
1050 }
1051 }
1052 json_object_array_add(levels_json, level_json);
1053 }
1054
1055 if (listcount(circuit->ip_addrs) > 0) {
1056 ipv4_addr_json = json_object_new_object();
1057 json_object_object_add(iface_json, "ip-prefix",
1058 ipv4_addr_json);
1059 for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node,
1060 ip_addr)) {
1061 snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX",
1062 ip_addr);
1063 json_object_string_add(ipv4_addr_json, "ip",
1064 buf_prx);
1065 }
1066 }
1067 if (listcount(circuit->ipv6_link) > 0) {
1068 ipv6_link_json = json_object_new_object();
1069 json_object_object_add(iface_json, "ipv6-link-locals",
1070 ipv6_link_json);
1071 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node,
1072 ip_addr)) {
1073 snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX",
1074 ip_addr);
1075 json_object_string_add(ipv6_link_json, "ipv6",
1076 buf_prx);
1077 }
1078 }
1079 if (listcount(circuit->ipv6_non_link) > 0) {
1080 ipv6_non_link_json = json_object_new_object();
1081 json_object_object_add(iface_json, "ipv6-prefixes",
1082 ipv6_non_link_json);
1083 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node,
1084 ip_addr)) {
1085 snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX",
1086 ip_addr);
1087 json_object_string_add(ipv6_non_link_json,
1088 "ipv6", buf_prx);
1089 }
1090 }
1091 }
1092 return;
1093 }
1094
1095 void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty,
1096 char detail)
1097 {
1098 if (detail == ISIS_UI_LEVEL_BRIEF) {
1099 vty_out(vty, " %-12s", circuit->interface->name);
1100 vty_out(vty, "0x%-7x", circuit->circuit_id);
1101 vty_out(vty, "%-9s", circuit_state2string(circuit->state));
1102 vty_out(vty, "%-9s", circuit_type2string(circuit->circ_type));
1103 vty_out(vty, "%-9s", circuit_t2string(circuit->is_type));
1104 vty_out(vty, "\n");
1105 }
1106
1107 if (detail == ISIS_UI_LEVEL_DETAIL) {
1108 struct listnode *node;
1109 struct prefix *ip_addr;
1110
1111 vty_out(vty, " Interface: %s", circuit->interface->name);
1112 vty_out(vty, ", State: %s",
1113 circuit_state2string(circuit->state));
1114 if (circuit->is_passive)
1115 vty_out(vty, ", Passive");
1116 else
1117 vty_out(vty, ", Active");
1118 vty_out(vty, ", Circuit Id: 0x%x", circuit->circuit_id);
1119 vty_out(vty, "\n");
1120 vty_out(vty, " Type: %s",
1121 circuit_type2string(circuit->circ_type));
1122 vty_out(vty, ", Level: %s", circuit_t2string(circuit->is_type));
1123 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
1124 vty_out(vty, ", SNPA: %-10pSY", circuit->u.bc.snpa);
1125 vty_out(vty, "\n");
1126 if (circuit->is_type & IS_LEVEL_1) {
1127 vty_out(vty, " Level-1 Information:\n");
1128 if (circuit->area->newmetric)
1129 vty_out(vty, " Metric: %d",
1130 circuit->te_metric[0]);
1131 else
1132 vty_out(vty, " Metric: %d",
1133 circuit->metric[0]);
1134 if (!circuit->is_passive) {
1135 vty_out(vty, ", Active neighbors: %u\n",
1136 circuit->upadjcount[0]);
1137 vty_out(vty,
1138 " Hello interval: %u, Holddown count: %u, Padding: %s\n",
1139 circuit->hello_interval[0],
1140 circuit->hello_multiplier[0],
1141 isis_hello_padding2string(
1142 circuit->pad_hellos));
1143 vty_out(vty,
1144 " CNSP interval: %u, PSNP interval: %u\n",
1145 circuit->csnp_interval[0],
1146 circuit->psnp_interval[0]);
1147 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
1148 vty_out(vty,
1149 " LAN Priority: %u, %s\n",
1150 circuit->priority[0],
1151 (circuit->u.bc.is_dr[0]
1152 ? "is DIS"
1153 : "is not DIS"));
1154 } else {
1155 vty_out(vty, "\n");
1156 }
1157 }
1158 if (circuit->is_type & IS_LEVEL_2) {
1159 vty_out(vty, " Level-2 Information:\n");
1160 if (circuit->area->newmetric)
1161 vty_out(vty, " Metric: %d",
1162 circuit->te_metric[1]);
1163 else
1164 vty_out(vty, " Metric: %d",
1165 circuit->metric[1]);
1166 if (!circuit->is_passive) {
1167 vty_out(vty, ", Active neighbors: %u\n",
1168 circuit->upadjcount[1]);
1169 vty_out(vty,
1170 " Hello interval: %u, Holddown count: %u, Padding: %s\n",
1171 circuit->hello_interval[1],
1172 circuit->hello_multiplier[1],
1173 isis_hello_padding2string(
1174 circuit->pad_hellos));
1175 vty_out(vty,
1176 " CNSP interval: %u, PSNP interval: %u\n",
1177 circuit->csnp_interval[1],
1178 circuit->psnp_interval[1]);
1179 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
1180 vty_out(vty,
1181 " LAN Priority: %u, %s\n",
1182 circuit->priority[1],
1183 (circuit->u.bc.is_dr[1]
1184 ? "is DIS"
1185 : "is not DIS"));
1186 } else {
1187 vty_out(vty, "\n");
1188 }
1189 }
1190 if (listcount(circuit->ip_addrs) > 0) {
1191 vty_out(vty, " IP Prefix(es):\n");
1192 for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node,
1193 ip_addr))
1194 vty_out(vty, " %pFX\n", ip_addr);
1195 }
1196 if (listcount(circuit->ipv6_link) > 0) {
1197 vty_out(vty, " IPv6 Link-Locals:\n");
1198 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node,
1199 ip_addr))
1200 vty_out(vty, " %pFX\n", ip_addr);
1201 }
1202 if (listcount(circuit->ipv6_non_link) > 0) {
1203 vty_out(vty, " IPv6 Prefixes:\n");
1204 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node,
1205 ip_addr))
1206 vty_out(vty, " %pFX\n", ip_addr);
1207 }
1208
1209 vty_out(vty, "\n");
1210 }
1211 return;
1212 }
1213
1214 #ifdef FABRICD
1215 DEFINE_HOOK(isis_circuit_config_write,
1216 (struct isis_circuit *circuit, struct vty *vty),
1217 (circuit, vty));
1218
1219 static int isis_interface_config_write(struct vty *vty)
1220 {
1221 struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
1222 int write = 0;
1223 struct interface *ifp;
1224 struct isis_circuit *circuit;
1225 int i;
1226
1227 FOR_ALL_INTERFACES (vrf, ifp) {
1228 /* IF name */
1229 if_vty_config_start(vty, ifp);
1230 write++;
1231 /* IF desc */
1232 if (ifp->desc) {
1233 vty_out(vty, " description %s\n", ifp->desc);
1234 write++;
1235 }
1236 /* ISIS Circuit */
1237 do {
1238 circuit = circuit_scan_by_ifp(ifp);
1239 if (circuit == NULL)
1240 break;
1241 if (circuit->ip_router) {
1242 vty_out(vty, " ip router " PROTO_NAME " %s\n",
1243 circuit->tag);
1244 write++;
1245 }
1246 if (circuit->is_passive) {
1247 vty_out(vty, " " PROTO_NAME " passive\n");
1248 write++;
1249 }
1250 if (circuit->circ_type_config == CIRCUIT_T_P2P) {
1251 vty_out(vty, " " PROTO_NAME " network point-to-point\n");
1252 write++;
1253 }
1254 if (circuit->ipv6_router) {
1255 vty_out(vty, " ipv6 router " PROTO_NAME " %s\n",
1256 circuit->tag);
1257 write++;
1258 }
1259
1260 /* ISIS - circuit type */
1261 if (!fabricd) {
1262 if (circuit->is_type == IS_LEVEL_1) {
1263 vty_out(vty, " " PROTO_NAME " circuit-type level-1\n");
1264 write++;
1265 } else {
1266 if (circuit->is_type == IS_LEVEL_2) {
1267 vty_out(vty,
1268 " " PROTO_NAME " circuit-type level-2-only\n");
1269 write++;
1270 }
1271 }
1272 }
1273
1274 /* ISIS - CSNP interval */
1275 if (circuit->csnp_interval[0]
1276 == circuit->csnp_interval[1]) {
1277 if (circuit->csnp_interval[0]
1278 != DEFAULT_CSNP_INTERVAL) {
1279 vty_out(vty, " " PROTO_NAME " csnp-interval %d\n",
1280 circuit->csnp_interval[0]);
1281 write++;
1282 }
1283 } else {
1284 for (i = 0; i < 2; i++) {
1285 if (circuit->csnp_interval[i]
1286 != DEFAULT_CSNP_INTERVAL) {
1287 vty_out(vty,
1288 " " PROTO_NAME " csnp-interval %d level-%d\n",
1289 circuit->csnp_interval
1290 [i],
1291 i + 1);
1292 write++;
1293 }
1294 }
1295 }
1296
1297 /* ISIS - PSNP interval */
1298 if (circuit->psnp_interval[0]
1299 == circuit->psnp_interval[1]) {
1300 if (circuit->psnp_interval[0]
1301 != DEFAULT_PSNP_INTERVAL) {
1302 vty_out(vty, " " PROTO_NAME " psnp-interval %d\n",
1303 circuit->psnp_interval[0]);
1304 write++;
1305 }
1306 } else {
1307 for (i = 0; i < 2; i++) {
1308 if (circuit->psnp_interval[i]
1309 != DEFAULT_PSNP_INTERVAL) {
1310 vty_out(vty,
1311 " " PROTO_NAME " psnp-interval %d level-%d\n",
1312 circuit->psnp_interval
1313 [i],
1314 i + 1);
1315 write++;
1316 }
1317 }
1318 }
1319
1320 /* ISIS - Hello padding - Defaults to always so only
1321 * display if not always */
1322 switch (circuit->pad_hellos) {
1323 case ISIS_HELLO_PADDING_DISABLED:
1324 vty_out(vty, " no " PROTO_NAME " hello padding\n");
1325 write++;
1326 break;
1327 case ISIS_HELLO_PADDING_DURING_ADJACENCY_FORMATION:
1328 vty_out(vty, PROTO_NAME
1329 " hello padding during-adjacency-formation\n");
1330 write++;
1331 break;
1332 case ISIS_HELLO_PADDING_ALWAYS:
1333 break;
1334 }
1335
1336 if (circuit->disable_threeway_adj) {
1337 vty_out(vty, " no isis three-way-handshake\n");
1338 write++;
1339 }
1340
1341 /* ISIS - Hello interval */
1342 if (circuit->hello_interval[0]
1343 == circuit->hello_interval[1]) {
1344 if (circuit->hello_interval[0]
1345 != DEFAULT_HELLO_INTERVAL) {
1346 vty_out(vty,
1347 " " PROTO_NAME " hello-interval %d\n",
1348 circuit->hello_interval[0]);
1349 write++;
1350 }
1351 } else {
1352 for (i = 0; i < 2; i++) {
1353 if (circuit->hello_interval[i]
1354 != DEFAULT_HELLO_INTERVAL) {
1355 vty_out(vty,
1356 " " PROTO_NAME " hello-interval %d level-%d\n",
1357 circuit->hello_interval
1358 [i],
1359 i + 1);
1360 write++;
1361 }
1362 }
1363 }
1364
1365 /* ISIS - Hello Multiplier */
1366 if (circuit->hello_multiplier[0]
1367 == circuit->hello_multiplier[1]) {
1368 if (circuit->hello_multiplier[0]
1369 != DEFAULT_HELLO_MULTIPLIER) {
1370 vty_out(vty,
1371 " " PROTO_NAME " hello-multiplier %d\n",
1372 circuit->hello_multiplier[0]);
1373 write++;
1374 }
1375 } else {
1376 for (i = 0; i < 2; i++) {
1377 if (circuit->hello_multiplier[i]
1378 != DEFAULT_HELLO_MULTIPLIER) {
1379 vty_out(vty,
1380 " " PROTO_NAME " hello-multiplier %d level-%d\n",
1381 circuit->hello_multiplier
1382 [i],
1383 i + 1);
1384 write++;
1385 }
1386 }
1387 }
1388
1389 /* ISIS - Priority */
1390 if (circuit->priority[0] == circuit->priority[1]) {
1391 if (circuit->priority[0] != DEFAULT_PRIORITY) {
1392 vty_out(vty, " " PROTO_NAME " priority %d\n",
1393 circuit->priority[0]);
1394 write++;
1395 }
1396 } else {
1397 for (i = 0; i < 2; i++) {
1398 if (circuit->priority[i]
1399 != DEFAULT_PRIORITY) {
1400 vty_out(vty,
1401 " " PROTO_NAME " priority %d level-%d\n",
1402 circuit->priority[i],
1403 i + 1);
1404 write++;
1405 }
1406 }
1407 }
1408
1409 /* ISIS - Metric */
1410 if (circuit->te_metric[0] == circuit->te_metric[1]) {
1411 if (circuit->te_metric[0]
1412 != DEFAULT_CIRCUIT_METRIC) {
1413 vty_out(vty, " " PROTO_NAME " metric %d\n",
1414 circuit->te_metric[0]);
1415 write++;
1416 }
1417 } else {
1418 for (i = 0; i < 2; i++) {
1419 if (circuit->te_metric[i]
1420 != DEFAULT_CIRCUIT_METRIC) {
1421 vty_out(vty,
1422 " " PROTO_NAME " metric %d level-%d\n",
1423 circuit->te_metric[i],
1424 i + 1);
1425 write++;
1426 }
1427 }
1428 }
1429 if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) {
1430 vty_out(vty, " " PROTO_NAME " password md5 %s\n",
1431 circuit->passwd.passwd);
1432 write++;
1433 } else if (circuit->passwd.type
1434 == ISIS_PASSWD_TYPE_CLEARTXT) {
1435 vty_out(vty, " " PROTO_NAME " password clear %s\n",
1436 circuit->passwd.passwd);
1437 write++;
1438 }
1439 if (circuit->bfd_config.enabled) {
1440 vty_out(vty, " " PROTO_NAME " bfd\n");
1441 write++;
1442 }
1443 write += hook_call(isis_circuit_config_write,
1444 circuit, vty);
1445 } while (0);
1446 if_vty_config_end(vty);
1447 }
1448
1449 return write;
1450 }
1451 #endif /* ifdef FABRICD */
1452
1453 void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router,
1454 bool ipv6_router)
1455 {
1456 struct isis_area *area = circuit->area;
1457 int old_ipr = circuit->ip_router;
1458 int old_ipv6r = circuit->ipv6_router;
1459
1460 /* is there something to do? */
1461 if (old_ipr == ip_router && old_ipv6r == ipv6_router)
1462 return;
1463
1464 circuit->ip_router = ip_router;
1465 circuit->ipv6_router = ipv6_router;
1466 circuit_update_nlpids(circuit);
1467
1468 if (area) {
1469 area->ip_circuits += ip_router - old_ipr;
1470 area->ipv6_circuits += ipv6_router - old_ipv6r;
1471
1472 if (ip_router || ipv6_router)
1473 lsp_regenerate_schedule(area, circuit->is_type, 0);
1474 }
1475 }
1476
1477 ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive)
1478 {
1479 if (circuit->is_passive == passive)
1480 return ferr_ok();
1481
1482 if (if_is_loopback(circuit->interface) && !passive)
1483 return ferr_cfg_invalid("loopback is always passive");
1484
1485 if (circuit->state != C_STATE_UP) {
1486 circuit->is_passive = passive;
1487 } else {
1488 struct isis_area *area = circuit->area;
1489 isis_csm_state_change(ISIS_DISABLE, circuit, area);
1490 circuit->is_passive = passive;
1491 isis_csm_state_change(ISIS_ENABLE, circuit, area);
1492 }
1493
1494 return ferr_ok();
1495 }
1496
1497 ferr_r isis_circuit_metric_set(struct isis_circuit *circuit, int level,
1498 int metric)
1499 {
1500 assert(level == IS_LEVEL_1 || level == IS_LEVEL_2);
1501 if (metric > MAX_WIDE_LINK_METRIC)
1502 return ferr_cfg_invalid("metric %d too large for wide metric",
1503 metric);
1504 if (circuit->area && circuit->area->oldmetric
1505 && metric > MAX_NARROW_LINK_METRIC)
1506 return ferr_cfg_invalid("metric %d too large for narrow metric",
1507 metric);
1508
1509 /* Don't modify metric if advertise high metrics is configured */
1510 if (circuit->area && circuit->area->advertise_high_metrics)
1511 return ferr_ok();
1512
1513 /* inform ldp-sync of metric change
1514 * if ldp-sync is running need to save metric
1515 * and restore new values after ldp-sync completion.
1516 */
1517 if (isis_ldp_sync_if_metric_config(circuit, level, metric)) {
1518 circuit->te_metric[level - 1] = metric;
1519 circuit->metric[level - 1] = metric;
1520 if (circuit->area)
1521 lsp_regenerate_schedule(circuit->area, level, 0);
1522 }
1523 return ferr_ok();
1524 }
1525
1526 ferr_r isis_circuit_passwd_unset(struct isis_circuit *circuit)
1527 {
1528 memset(&circuit->passwd, 0, sizeof(circuit->passwd));
1529 return ferr_ok();
1530 }
1531
1532 ferr_r isis_circuit_passwd_set(struct isis_circuit *circuit,
1533 uint8_t passwd_type, const char *passwd)
1534 {
1535 int len;
1536
1537 if (!passwd)
1538 return ferr_code_bug("no circuit password given");
1539
1540 len = strlen(passwd);
1541 if (len > 254)
1542 return ferr_code_bug(
1543 "circuit password too long (max 254 chars)");
1544
1545 circuit->passwd.len = len;
1546 strlcpy((char *)circuit->passwd.passwd, passwd,
1547 sizeof(circuit->passwd.passwd));
1548 circuit->passwd.type = passwd_type;
1549 return ferr_ok();
1550 }
1551
1552 ferr_r isis_circuit_passwd_cleartext_set(struct isis_circuit *circuit,
1553 const char *passwd)
1554 {
1555 return isis_circuit_passwd_set(circuit, ISIS_PASSWD_TYPE_CLEARTXT,
1556 passwd);
1557 }
1558
1559 ferr_r isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit,
1560 const char *passwd)
1561 {
1562 return isis_circuit_passwd_set(circuit, ISIS_PASSWD_TYPE_HMAC_MD5,
1563 passwd);
1564 }
1565
1566 void isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type)
1567 {
1568 if (circuit->circ_type == circ_type)
1569 return;
1570
1571 if (circuit->state != C_STATE_UP) {
1572 circuit->circ_type = circ_type;
1573 circuit->circ_type_config = circ_type;
1574 } else {
1575 struct isis_area *area = circuit->area;
1576
1577 isis_csm_state_change(ISIS_DISABLE, circuit, area);
1578 circuit->circ_type = circ_type;
1579 circuit->circ_type_config = circ_type;
1580 isis_csm_state_change(ISIS_ENABLE, circuit, area);
1581 }
1582 }
1583
1584 int isis_circuit_mt_enabled_set(struct isis_circuit *circuit, uint16_t mtid,
1585 bool enabled)
1586 {
1587 struct isis_circuit_mt_setting *setting;
1588
1589 setting = circuit_get_mt_setting(circuit, mtid);
1590 if (setting->enabled != enabled) {
1591 setting->enabled = enabled;
1592 if (circuit->area)
1593 lsp_regenerate_schedule(circuit->area,
1594 IS_LEVEL_1 | IS_LEVEL_2, 0);
1595 }
1596
1597 return CMD_SUCCESS;
1598 }
1599
1600 int isis_if_new_hook(struct interface *ifp)
1601 {
1602 return 0;
1603 }
1604
1605 int isis_if_delete_hook(struct interface *ifp)
1606 {
1607 if (ifp->info)
1608 isis_circuit_del(ifp->info);
1609
1610 return 0;
1611 }
1612
1613 static int isis_ifp_create(struct interface *ifp)
1614 {
1615 struct isis_circuit *circuit = ifp->info;
1616
1617 if (circuit)
1618 isis_circuit_enable(circuit);
1619
1620 hook_call(isis_if_new_hook, ifp);
1621
1622 return 0;
1623 }
1624
1625 static int isis_ifp_up(struct interface *ifp)
1626 {
1627 struct isis_circuit *circuit = ifp->info;
1628
1629 if (circuit) {
1630 UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_IF_DOWN_FROM_Z);
1631 isis_csm_state_change(IF_UP_FROM_Z, circuit, ifp);
1632 }
1633
1634 return 0;
1635 }
1636
1637 static int isis_ifp_down(struct interface *ifp)
1638 {
1639 afi_t afi;
1640 struct isis_circuit *circuit = ifp->info;
1641
1642 if (circuit &&
1643 !CHECK_FLAG(circuit->flags, ISIS_CIRCUIT_IF_DOWN_FROM_Z)) {
1644 SET_FLAG(circuit->flags, ISIS_CIRCUIT_IF_DOWN_FROM_Z);
1645 for (afi = AFI_IP; afi <= AFI_IP6; afi++)
1646 isis_circuit_switchover_routes(
1647 circuit, afi == AFI_IP ? AF_INET : AF_INET6,
1648 NULL, ifp->ifindex);
1649 isis_csm_state_change(IF_DOWN_FROM_Z, circuit, ifp);
1650
1651 SET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF);
1652 }
1653
1654 return 0;
1655 }
1656
1657 static int isis_ifp_destroy(struct interface *ifp)
1658 {
1659 struct isis_circuit *circuit = ifp->info;
1660
1661 if (circuit)
1662 isis_circuit_disable(circuit);
1663
1664 return 0;
1665 }
1666
1667 void isis_circuit_init(void)
1668 {
1669 /* Initialize Zebra interface data structure */
1670 hook_register_prio(if_add, 0, isis_if_new_hook);
1671 hook_register_prio(if_del, 0, isis_if_delete_hook);
1672
1673 /* Install interface node */
1674 #ifdef FABRICD
1675 if_cmd_init(isis_interface_config_write);
1676 #else
1677 if_cmd_init_default();
1678 #endif
1679 if_zapi_callbacks(isis_ifp_create, isis_ifp_up,
1680 isis_ifp_down, isis_ifp_destroy);
1681 }