1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * IS-IS Rout(e)ing protocol - isis_route.c
4 * Copyright (C) 2001,2002 Sampo Saaristo
5 * Tampere University of Technology
6 * Institute of Communications Engineering
8 * based on ../ospf6d/ospf6_route.[ch]
18 #include "lib_errors.h"
24 #include "srcdest_table.h"
26 #include "isis_constants.h"
27 #include "isis_common.h"
28 #include "isis_flags.h"
30 #include "isis_misc.h"
31 #include "isis_adjacency.h"
32 #include "isis_circuit.h"
36 #include "isis_spf_private.h"
37 #include "isis_route.h"
38 #include "isis_zebra.h"
40 DEFINE_MTYPE_STATIC(ISISD
, ISIS_NEXTHOP
, "ISIS nexthop");
41 DEFINE_MTYPE_STATIC(ISISD
, ISIS_ROUTE_INFO
, "ISIS route info");
43 DEFINE_HOOK(isis_route_update_hook
,
44 (struct isis_area
* area
, struct prefix
*prefix
,
45 struct isis_route_info
*route_info
),
46 (area
, prefix
, route_info
));
48 static struct isis_nexthop
*nexthoplookup(struct list
*nexthops
, int family
,
49 union g_addr
*ip
, ifindex_t ifindex
);
50 static void isis_route_update(struct isis_area
*area
, struct prefix
*prefix
,
51 struct prefix_ipv6
*src_p
,
52 struct isis_route_info
*route_info
);
54 static struct isis_nexthop
*isis_nexthop_create(int family
, union g_addr
*ip
,
57 struct isis_nexthop
*nexthop
;
59 nexthop
= XCALLOC(MTYPE_ISIS_NEXTHOP
, sizeof(struct isis_nexthop
));
61 nexthop
->family
= family
;
62 nexthop
->ifindex
= ifindex
;
68 void isis_nexthop_delete(struct isis_nexthop
*nexthop
)
70 XFREE(MTYPE_ISIS_NEXTHOP_LABELS
, nexthop
->label_stack
);
71 XFREE(MTYPE_ISIS_NEXTHOP
, nexthop
);
74 static struct isis_nexthop
*nexthoplookup(struct list
*nexthops
, int family
,
75 union g_addr
*ip
, ifindex_t ifindex
)
77 struct listnode
*node
;
78 struct isis_nexthop
*nh
;
80 for (ALL_LIST_ELEMENTS_RO(nexthops
, node
, nh
)) {
81 if (nh
->ifindex
!= ifindex
)
84 /* if the IP is unspecified, return the first nexthop found on
90 if (nh
->family
!= family
)
95 if (IPV4_ADDR_CMP(&nh
->ip
.ipv4
, &ip
->ipv4
))
99 if (IPV6_ADDR_CMP(&nh
->ip
.ipv6
, &ip
->ipv6
))
103 flog_err(EC_LIB_DEVELOPMENT
,
104 "%s: unknown address family [%d]", __func__
,
115 void adjinfo2nexthop(int family
, struct list
*nexthops
,
116 struct isis_adjacency
*adj
, struct isis_sr_psid_info
*sr
,
117 struct mpls_label_stack
*label_stack
)
119 struct isis_nexthop
*nh
;
120 union g_addr ip
= {};
124 for (unsigned int i
= 0; i
< adj
->ipv4_address_count
; i
++) {
125 ip
.ipv4
= adj
->ipv4_addresses
[i
];
127 if (!nexthoplookup(nexthops
, AF_INET
, &ip
,
128 adj
->circuit
->interface
->ifindex
)) {
129 nh
= isis_nexthop_create(
131 adj
->circuit
->interface
->ifindex
);
132 memcpy(nh
->sysid
, adj
->sysid
, sizeof(nh
->sysid
));
135 nh
->label_stack
= label_stack
;
136 listnode_add(nexthops
, nh
);
142 for (unsigned int i
= 0; i
< adj
->ll_ipv6_count
; i
++) {
143 ip
.ipv6
= adj
->ll_ipv6_addrs
[i
];
145 if (!nexthoplookup(nexthops
, AF_INET6
, &ip
,
146 adj
->circuit
->interface
->ifindex
)) {
147 nh
= isis_nexthop_create(
149 adj
->circuit
->interface
->ifindex
);
150 memcpy(nh
->sysid
, adj
->sysid
, sizeof(nh
->sysid
));
153 nh
->label_stack
= label_stack
;
154 listnode_add(nexthops
, nh
);
160 flog_err(EC_LIB_DEVELOPMENT
, "%s: unknown address family [%d]",
166 static void isis_route_add_dummy_nexthops(struct isis_route_info
*rinfo
,
167 const uint8_t *sysid
,
168 struct isis_sr_psid_info
*sr
,
169 struct mpls_label_stack
*label_stack
)
171 struct isis_nexthop
*nh
;
173 nh
= XCALLOC(MTYPE_ISIS_NEXTHOP
, sizeof(struct isis_nexthop
));
174 memcpy(nh
->sysid
, sysid
, sizeof(nh
->sysid
));
176 nh
->label_stack
= label_stack
;
177 listnode_add(rinfo
->nexthops
, nh
);
180 static struct isis_route_info
*
181 isis_route_info_new(struct prefix
*prefix
, struct prefix_ipv6
*src_p
,
182 uint32_t cost
, uint32_t depth
, struct isis_sr_psid_info
*sr
,
183 struct list
*adjacencies
, bool allow_ecmp
)
185 struct isis_route_info
*rinfo
;
186 struct isis_vertex_adj
*vadj
;
187 struct listnode
*node
;
189 rinfo
= XCALLOC(MTYPE_ISIS_ROUTE_INFO
, sizeof(struct isis_route_info
));
191 rinfo
->nexthops
= list_new();
192 for (ALL_LIST_ELEMENTS_RO(adjacencies
, node
, vadj
)) {
193 struct isis_spf_adj
*sadj
= vadj
->sadj
;
194 struct isis_adjacency
*adj
= sadj
->adj
;
195 struct isis_sr_psid_info
*sr
= &vadj
->sr
;
196 struct mpls_label_stack
*label_stack
= vadj
->label_stack
;
199 * Create dummy nexthops when running SPF on a testing
202 if (CHECK_FLAG(im
->options
, F_ISIS_UNIT_TEST
)) {
203 isis_route_add_dummy_nexthops(rinfo
, sadj
->id
, sr
,
210 /* check for force resync this route */
211 if (CHECK_FLAG(adj
->circuit
->flags
,
212 ISIS_CIRCUIT_FLAPPED_AFTER_SPF
))
213 SET_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ZEBRA_RESYNC
);
215 /* update neighbor router address */
216 switch (prefix
->family
) {
218 if (depth
== 2 && prefix
->prefixlen
== IPV4_MAX_BITLEN
)
219 adj
->router_address
= prefix
->u
.prefix4
;
222 if (depth
== 2 && prefix
->prefixlen
== IPV6_MAX_BITLEN
223 && (!src_p
|| !src_p
->prefixlen
)) {
224 adj
->router_address6
= prefix
->u
.prefix6
;
228 flog_err(EC_LIB_DEVELOPMENT
,
229 "%s: unknown address family [%d]", __func__
,
233 adjinfo2nexthop(prefix
->family
, rinfo
->nexthops
, adj
, sr
,
240 rinfo
->depth
= depth
;
246 static void isis_route_info_delete(struct isis_route_info
*route_info
)
248 if (route_info
->nexthops
) {
249 route_info
->nexthops
->del
=
250 (void (*)(void *))isis_nexthop_delete
;
251 list_delete(&route_info
->nexthops
);
254 XFREE(MTYPE_ISIS_ROUTE_INFO
, route_info
);
257 void isis_route_node_cleanup(struct route_table
*table
, struct route_node
*node
)
260 isis_route_info_delete(node
->info
);
263 static bool isis_sr_psid_info_same(struct isis_sr_psid_info
*new,
264 struct isis_sr_psid_info
*old
)
266 if (new->present
!= old
->present
)
269 if (new->label
!= old
->label
)
272 if (new->sid
.flags
!= old
->sid
.flags
273 || new->sid
.value
!= old
->sid
.value
)
279 static bool isis_label_stack_same(struct mpls_label_stack
*new,
280 struct mpls_label_stack
*old
)
286 if (new->num_labels
!= old
->num_labels
)
288 if (memcmp(&new->label
, &old
->label
,
289 sizeof(mpls_label_t
) * new->num_labels
))
295 static int isis_route_info_same(struct isis_route_info
*new,
296 struct isis_route_info
*old
, char *buf
,
299 struct listnode
*node
;
300 struct isis_nexthop
*new_nh
, *old_nh
;
302 if (new->cost
!= old
->cost
) {
304 snprintf(buf
, buf_size
, "cost (old: %u, new: %u)",
305 old
->cost
, new->cost
);
309 if (new->depth
!= old
->depth
) {
311 snprintf(buf
, buf_size
, "depth (old: %u, new: %u)",
312 old
->depth
, new->depth
);
316 if (!isis_sr_psid_info_same(&new->sr
, &old
->sr
)) {
318 snprintf(buf
, buf_size
, "SR input label");
322 if (new->nexthops
->count
!= old
->nexthops
->count
) {
324 snprintf(buf
, buf_size
, "nhops num (old: %u, new: %u)",
325 old
->nexthops
->count
, new->nexthops
->count
);
329 for (ALL_LIST_ELEMENTS_RO(new->nexthops
, node
, new_nh
)) {
330 old_nh
= nexthoplookup(old
->nexthops
, new_nh
->family
,
331 &new_nh
->ip
, new_nh
->ifindex
);
334 snprintf(buf
, buf_size
,
335 "new nhop"); /* TODO: print nhop */
338 if (!isis_sr_psid_info_same(&new_nh
->sr
, &old_nh
->sr
)) {
340 snprintf(buf
, buf_size
, "nhop SR label");
343 if (!isis_label_stack_same(new_nh
->label_stack
,
344 old_nh
->label_stack
)) {
346 snprintf(buf
, buf_size
, "nhop label stack");
351 /* only the resync flag needs to be checked */
352 if (CHECK_FLAG(new->flag
, ISIS_ROUTE_FLAG_ZEBRA_RESYNC
)
353 != CHECK_FLAG(old
->flag
, ISIS_ROUTE_FLAG_ZEBRA_RESYNC
)) {
355 snprintf(buf
, buf_size
, "resync flag");
362 struct isis_route_info
*
363 isis_route_create(struct prefix
*prefix
, struct prefix_ipv6
*src_p
,
364 uint32_t cost
, uint32_t depth
, struct isis_sr_psid_info
*sr
,
365 struct list
*adjacencies
, bool allow_ecmp
,
366 struct isis_area
*area
, struct route_table
*table
)
368 struct route_node
*route_node
;
369 struct isis_route_info
*rinfo_new
, *rinfo_old
, *route_info
= NULL
;
375 rinfo_new
= isis_route_info_new(prefix
, src_p
, cost
, depth
, sr
,
376 adjacencies
, allow_ecmp
);
377 route_node
= srcdest_rnode_get(table
, prefix
, src_p
);
379 rinfo_old
= route_node
->info
;
381 if (IS_DEBUG_RTE_EVENTS
)
382 zlog_debug("ISIS-Rte (%s) route created: %pFX",
383 area
->area_tag
, prefix
);
384 route_info
= rinfo_new
;
385 UNSET_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
387 route_unlock_node(route_node
);
389 if (IS_DEBUG_RTE_EVENTS
)
390 zlog_debug("ISIS-Rte (%s) route already exists: %pFX",
391 area
->area_tag
, prefix
);
392 #endif /* EXTREME_DEBUG */
393 if (isis_route_info_same(rinfo_new
, rinfo_old
, change_buf
,
394 sizeof(change_buf
))) {
396 if (IS_DEBUG_RTE_EVENTS
)
398 "ISIS-Rte (%s) route unchanged: %pFX",
399 area
->area_tag
, prefix
);
400 #endif /* EXTREME_DEBUG */
401 isis_route_info_delete(rinfo_new
);
402 route_info
= rinfo_old
;
404 if (IS_DEBUG_RTE_EVENTS
)
406 "ISIS-Rte (%s): route changed: %pFX, change: %s",
407 area
->area_tag
, prefix
, change_buf
);
408 rinfo_new
->sr_previous
= rinfo_old
->sr
;
409 isis_route_info_delete(rinfo_old
);
410 route_info
= rinfo_new
;
411 UNSET_FLAG(route_info
->flag
,
412 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
416 SET_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ACTIVE
);
417 route_node
->info
= route_info
;
422 void isis_route_delete(struct isis_area
*area
, struct route_node
*rode
,
423 struct route_table
*table
)
425 struct isis_route_info
*rinfo
;
426 char buff
[SRCDEST2STR_BUFFER
];
427 struct prefix
*prefix
;
428 struct prefix_ipv6
*src_p
;
431 srcdest_rnode2str(rode
, buff
, sizeof(buff
));
433 srcdest_rnode_prefixes(rode
, (const struct prefix
**)&prefix
,
434 (const struct prefix
**)&src_p
);
438 if (IS_DEBUG_RTE_EVENTS
)
440 "ISIS-Rte: tried to delete non-existent route %s",
445 if (CHECK_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
)) {
446 UNSET_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ACTIVE
);
447 if (IS_DEBUG_RTE_EVENTS
)
448 zlog_debug("ISIS-Rte: route delete %s", buff
);
449 isis_route_update(area
, prefix
, src_p
, rinfo
);
451 isis_route_info_delete(rinfo
);
453 route_unlock_node(rode
);
456 static void isis_route_remove_previous_sid(struct isis_area
*area
,
457 struct prefix
*prefix
,
458 struct isis_route_info
*route_info
)
461 * Explicitly uninstall previous Prefix-SID label if it has
462 * changed or was removed.
464 if (route_info
->sr_previous
.present
&&
465 (!route_info
->sr
.present
||
466 route_info
->sr_previous
.label
!= route_info
->sr
.label
))
467 isis_zebra_prefix_sid_uninstall(area
, prefix
, route_info
,
468 &route_info
->sr_previous
);
471 static void isis_route_update(struct isis_area
*area
, struct prefix
*prefix
,
472 struct prefix_ipv6
*src_p
,
473 struct isis_route_info
*route_info
)
478 if (CHECK_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ACTIVE
)) {
479 if (CHECK_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
))
482 isis_route_remove_previous_sid(area
, prefix
, route_info
);
485 isis_zebra_route_add_route(area
->isis
, prefix
, src_p
,
487 /* Install/reinstall Prefix-SID label. */
488 if (route_info
->sr
.present
)
489 isis_zebra_prefix_sid_install(area
, prefix
, route_info
,
491 hook_call(isis_route_update_hook
, area
, prefix
, route_info
);
493 SET_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
494 UNSET_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_RESYNC
);
496 /* Uninstall Prefix-SID label. */
497 if (route_info
->sr
.present
)
498 isis_zebra_prefix_sid_uninstall(
499 area
, prefix
, route_info
, &route_info
->sr
);
500 /* Uninstall route. */
501 isis_zebra_route_del_route(area
->isis
, prefix
, src_p
,
503 hook_call(isis_route_update_hook
, area
, prefix
, route_info
);
505 UNSET_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
509 static void _isis_route_verify_table(struct isis_area
*area
,
510 struct route_table
*table
,
511 struct route_table
*table_backup
,
512 struct route_table
**tables
)
514 struct route_node
*rnode
, *drnode
;
515 struct isis_route_info
*rinfo
;
517 char buff
[SRCDEST2STR_BUFFER
];
518 #endif /* EXTREME_DEBUG */
520 for (rnode
= route_top(table
); rnode
;
521 rnode
= srcdest_route_next(rnode
)) {
522 if (rnode
->info
== NULL
)
526 struct prefix
*dst_p
;
527 struct prefix_ipv6
*src_p
;
529 srcdest_rnode_prefixes(rnode
,
530 (const struct prefix
**)&dst_p
,
531 (const struct prefix
**)&src_p
);
533 /* Link primary route to backup route. */
535 struct route_node
*rnode_bck
;
537 rnode_bck
= srcdest_rnode_lookup(table_backup
, dst_p
,
540 rinfo
->backup
= rnode_bck
->info
;
541 UNSET_FLAG(rinfo
->flag
,
542 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
543 } else if (rinfo
->backup
) {
544 rinfo
->backup
= NULL
;
545 UNSET_FLAG(rinfo
->flag
,
546 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
551 if (IS_DEBUG_RTE_EVENTS
) {
552 srcdest2str(dst_p
, src_p
, buff
, sizeof(buff
));
554 "ISIS-Rte (%s): route validate: %s %s %s %s",
556 (CHECK_FLAG(rinfo
->flag
,
557 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
)
560 (CHECK_FLAG(rinfo
->flag
,
561 ISIS_ROUTE_FLAG_ZEBRA_RESYNC
)
564 (CHECK_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ACTIVE
)
569 #endif /* EXTREME_DEBUG */
571 isis_route_update(area
, dst_p
, src_p
, rinfo
);
573 if (CHECK_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ACTIVE
))
576 /* Area is either L1 or L2 => we use level route tables
578 * validating => no problems with deleting routes. */
580 isis_route_delete(area
, rnode
, table
);
584 /* If area is L1L2, we work with merge table and
586 * delete node from level tables as well before deleting
588 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
589 drnode
= srcdest_rnode_lookup(tables
[level
- 1],
594 route_unlock_node(drnode
);
596 if (drnode
->info
!= rnode
->info
)
600 route_unlock_node(drnode
);
603 isis_route_delete(area
, rnode
, table
);
607 void isis_route_verify_table(struct isis_area
*area
, struct route_table
*table
,
608 struct route_table
*table_backup
)
610 _isis_route_verify_table(area
, table
, table_backup
, NULL
);
613 /* Function to validate route tables for L1L2 areas. In this case we can't use
614 * level route tables directly, we have to merge them at first. L1 routes are
615 * preferred over the L2 ones.
617 * Merge algorithm is trivial (at least for now). All L1 paths are copied into
618 * merge table at first, then L2 paths are added if L1 path for same prefix
619 * doesn't already exists there.
621 * FIXME: Is it right place to do it at all? Maybe we should push both levels
622 * to the RIB with different zebra route types and let RIB handle this? */
623 void isis_route_verify_merge(struct isis_area
*area
,
624 struct route_table
*level1_table
,
625 struct route_table
*level1_table_backup
,
626 struct route_table
*level2_table
,
627 struct route_table
*level2_table_backup
)
629 struct route_table
*tables
[] = {level1_table
, level2_table
};
630 struct route_table
*tables_backup
[] = {level1_table_backup
,
631 level2_table_backup
};
632 struct route_table
*merge
;
633 struct route_node
*rnode
, *mrnode
;
635 merge
= srcdest_table_init();
637 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
638 for (rnode
= route_top(tables
[level
- 1]); rnode
;
639 rnode
= srcdest_route_next(rnode
)) {
640 struct isis_route_info
*rinfo
= rnode
->info
;
641 struct route_node
*rnode_bck
;
646 struct prefix
*prefix
;
647 struct prefix_ipv6
*src_p
;
649 srcdest_rnode_prefixes(rnode
,
650 (const struct prefix
**)&prefix
,
651 (const struct prefix
**)&src_p
);
653 /* Link primary route to backup route. */
654 rnode_bck
= srcdest_rnode_lookup(
655 tables_backup
[level
- 1], prefix
, src_p
);
657 rinfo
->backup
= rnode_bck
->info
;
658 UNSET_FLAG(rinfo
->flag
,
659 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
660 } else if (rinfo
->backup
) {
661 rinfo
->backup
= NULL
;
662 UNSET_FLAG(rinfo
->flag
,
663 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
666 mrnode
= srcdest_rnode_get(merge
, prefix
, src_p
);
667 struct isis_route_info
*mrinfo
= mrnode
->info
;
669 route_unlock_node(mrnode
);
670 if (CHECK_FLAG(mrinfo
->flag
,
671 ISIS_ROUTE_FLAG_ACTIVE
)) {
672 /* Clear the ZEBRA_SYNCED flag on the
673 * L2 route when L1 wins, otherwise L2
674 * won't get reinstalled when L1
679 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
682 } else if (CHECK_FLAG(rinfo
->flag
,
683 ISIS_ROUTE_FLAG_ACTIVE
)) {
684 /* Clear the ZEBRA_SYNCED flag on the L1
685 * route when L2 wins, otherwise L1
686 * won't get reinstalled when it
691 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
696 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
)) {
700 mrnode
->info
= rnode
->info
;
704 _isis_route_verify_table(area
, merge
, NULL
, tables
);
705 route_table_finish(merge
);
708 void isis_route_invalidate_table(struct isis_area
*area
,
709 struct route_table
*table
)
711 struct route_node
*rode
;
712 struct isis_route_info
*rinfo
;
713 for (rode
= route_top(table
); rode
; rode
= srcdest_route_next(rode
)) {
714 if (rode
->info
== NULL
)
719 rinfo
->backup
= NULL
;
721 * For now, always force routes that have backup
722 * nexthops to be reinstalled.
724 UNSET_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
726 UNSET_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ACTIVE
);
730 void isis_route_switchover_nexthop(struct isis_area
*area
,
731 struct route_table
*table
, int family
,
732 union g_addr
*nexthop_addr
,
735 const char *ifname
= NULL
, *vrfname
= NULL
;
736 struct isis_route_info
*rinfo
;
737 struct prefix_ipv6
*src_p
;
738 struct route_node
*rnode
;
740 struct prefix
*prefix
;
742 if (IS_DEBUG_EVENTS
) {
743 if (area
&& area
->isis
) {
744 vrf_id
= area
->isis
->vrf_id
;
745 vrfname
= vrf_id_to_name(vrf_id
);
746 ifname
= ifindex2ifname(ifindex
, vrf_id
);
748 zlog_debug("%s: initiating fast-reroute %s on VRF %s iface %s",
749 __func__
, family2str(family
), vrfname
? vrfname
: "",
750 ifname
? ifname
: "");
753 for (rnode
= route_top(table
); rnode
;
754 rnode
= srcdest_route_next(rnode
)) {
762 if (!nexthoplookup(rinfo
->nexthops
, family
, nexthop_addr
,
766 srcdest_rnode_prefixes(rnode
, (const struct prefix
**)&prefix
,
767 (const struct prefix
**)&src_p
);
769 /* Switchover route. */
770 isis_route_remove_previous_sid(area
, prefix
, rinfo
);
771 UNSET_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
772 isis_route_update(area
, prefix
, src_p
, rinfo
->backup
);
774 isis_route_info_delete(rinfo
);
777 route_unlock_node(rnode
);