1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * IS-IS Rout(e)ing protocol - isis_redist.c
5 * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org>
19 #include "srcdest_table.h"
21 #include "isisd/isis_constants.h"
22 #include "isisd/isis_common.h"
23 #include "isisd/isis_flags.h"
24 #include "isisd/isis_misc.h"
25 #include "isisd/isis_circuit.h"
26 #include "isisd/isisd.h"
27 #include "isisd/isis_lsp.h"
28 #include "isisd/isis_route.h"
29 #include "isisd/isis_zebra.h"
31 DEFINE_MTYPE_STATIC(ISISD
, ISIS_EXT_ROUTE
, "ISIS redistributed route");
32 DEFINE_MTYPE_STATIC(ISISD
, ISIS_EXT_INFO
, "ISIS redistributed route info");
33 DEFINE_MTYPE_STATIC(ISISD
, ISIS_RMAP_NAME
, "ISIS redistribute route-map name");
35 static int redist_protocol(int family
)
37 if (family
== AF_INET
)
39 if (family
== AF_INET6
)
42 assert(!"Unsupported address family!");
46 afi_t
afi_for_redist_protocol(int protocol
)
53 assert(!"Unknown redist protocol!");
57 static struct route_table
*get_ext_info(struct isis
*i
, int family
)
59 int protocol
= redist_protocol(family
);
61 return i
->ext_info
[protocol
];
64 static struct isis_redist
*get_redist_settings(struct isis_area
*area
,
65 int family
, int type
, int level
)
67 int protocol
= redist_protocol(family
);
69 return &area
->redist_settings
[protocol
][type
][level
- 1];
72 struct route_table
*get_ext_reach(struct isis_area
*area
, int family
, int level
)
74 int protocol
= redist_protocol(family
);
76 return area
->ext_reach
[protocol
][level
- 1];
79 /* Install external reachability information into a
80 * specific area for a specific level.
81 * Schedule an lsp regenerate if necessary */
82 static void isis_redist_install(struct isis_area
*area
, int level
,
83 const struct prefix
*p
,
84 const struct prefix_ipv6
*src_p
,
85 struct isis_ext_info
*info
)
87 int family
= p
->family
;
88 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
89 struct route_node
*er_node
;
93 "%s: External reachability table of area %s is not initialized.",
94 __func__
, area
->area_tag
);
98 er_node
= srcdest_rnode_get(er_table
, p
, src_p
);
100 route_unlock_node(er_node
);
102 /* Don't update/reschedule lsp generation if nothing changed. */
103 if (!memcmp(er_node
->info
, info
, sizeof(*info
)))
106 er_node
->info
= XMALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(*info
));
109 memcpy(er_node
->info
, info
, sizeof(*info
));
110 lsp_regenerate_schedule(area
, level
, 0);
113 /* Remove external reachability information from a
114 * specific area for a specific level.
115 * Schedule an lsp regenerate if necessary. */
116 static void isis_redist_uninstall(struct isis_area
*area
, int level
,
117 const struct prefix
*p
,
118 const struct prefix_ipv6
*src_p
)
120 int family
= p
->family
;
121 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
122 struct route_node
*er_node
;
126 "%s: External reachability table of area %s is not initialized.",
127 __func__
, area
->area_tag
);
131 er_node
= srcdest_rnode_lookup(er_table
, p
, src_p
);
135 route_unlock_node(er_node
);
140 XFREE(MTYPE_ISIS_EXT_INFO
, er_node
->info
);
141 route_unlock_node(er_node
);
142 lsp_regenerate_schedule(area
, level
, 0);
145 /* Update external reachability info of area for a given level
146 * and prefix, using the given redistribution settings. */
147 static void isis_redist_update_ext_reach(struct isis_area
*area
, int level
,
148 struct isis_redist
*redist
,
149 const struct prefix
*p
,
150 const struct prefix_ipv6
*src_p
,
151 struct isis_ext_info
*info
)
153 struct isis_ext_info area_info
;
154 route_map_result_t map_ret
;
156 memcpy(&area_info
, info
, sizeof(area_info
));
157 area_info
.metric
= redist
->metric
;
159 if (redist
->map_name
) {
160 map_ret
= route_map_apply(redist
->map
, p
, &area_info
);
161 if (map_ret
== RMAP_DENYMATCH
)
162 area_info
.distance
= 255;
165 /* Allow synthesized default routes only on always orignate */
166 if (area_info
.origin
== DEFAULT_ROUTE
167 && redist
->redist
!= DEFAULT_ORIGINATE_ALWAYS
)
168 area_info
.distance
= 255;
170 if (area_info
.distance
< 255)
171 isis_redist_install(area
, level
, p
, src_p
, &area_info
);
173 isis_redist_uninstall(area
, level
, p
, src_p
);
176 static void isis_redist_ensure_default(struct isis
*isis
, int family
)
179 struct route_table
*ei_table
= get_ext_info(isis
, family
);
180 struct route_node
*ei_node
;
181 struct isis_ext_info
*info
;
183 if (family
== AF_INET
) {
186 memset(&p
.u
.prefix4
, 0, sizeof(p
.u
.prefix4
));
187 } else if (family
== AF_INET6
) {
190 memset(&p
.u
.prefix6
, 0, sizeof(p
.u
.prefix6
));
192 assert(!"Unknown family!");
194 ei_node
= srcdest_rnode_get(ei_table
, &p
, NULL
);
196 route_unlock_node(ei_node
);
201 XCALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(struct isis_ext_info
));
203 info
= ei_node
->info
;
204 info
->origin
= DEFAULT_ROUTE
;
205 info
->distance
= 254;
206 info
->metric
= MAX_WIDE_PATH_METRIC
;
209 /* Handle notification about route being added */
210 void isis_redist_add(struct isis
*isis
, int type
, struct prefix
*p
,
211 struct prefix_ipv6
*src_p
, uint8_t distance
,
212 uint32_t metric
, const route_tag_t tag
)
214 int family
= p
->family
;
215 struct route_table
*ei_table
= get_ext_info(isis
, family
);
216 struct route_node
*ei_node
;
217 struct isis_ext_info
*info
;
218 struct listnode
*node
;
219 struct isis_area
*area
;
221 struct isis_redist
*redist
;
223 zlog_debug("%s: New route %pFX from %s: distance %d.", __func__
, p
,
224 zebra_route_string(type
), distance
);
227 zlog_warn("%s: External information table not initialized.",
232 ei_node
= srcdest_rnode_get(ei_table
, p
, src_p
);
234 route_unlock_node(ei_node
);
236 ei_node
->info
= XCALLOC(MTYPE_ISIS_EXT_INFO
,
237 sizeof(struct isis_ext_info
));
239 info
= ei_node
->info
;
241 info
->distance
= distance
;
242 info
->metric
= metric
;
245 if (is_default_prefix(p
)
246 && (!src_p
|| !src_p
->prefixlen
)) {
247 type
= DEFAULT_ROUTE
;
250 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
251 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
252 redist
= get_redist_settings(area
, family
, type
, level
);
256 isis_redist_update_ext_reach(area
, level
, redist
, p
,
261 void isis_redist_delete(struct isis
*isis
, int type
, struct prefix
*p
,
262 struct prefix_ipv6
*src_p
)
264 int family
= p
->family
;
265 struct route_table
*ei_table
= get_ext_info(isis
, family
);
266 struct route_node
*ei_node
;
267 struct listnode
*node
;
268 struct isis_area
*area
;
270 struct isis_redist
*redist
;
272 zlog_debug("%s: Removing route %pFX from %s.", __func__
, p
,
273 zebra_route_string(type
));
275 if (is_default_prefix(p
)
276 && (!src_p
|| !src_p
->prefixlen
)) {
277 /* Don't remove default route but add synthetic route for use
278 * by "default-information originate always". Areas without the
279 * "always" setting will ignore routes with origin
281 isis_redist_add(isis
, DEFAULT_ROUTE
, p
, NULL
, 254,
282 MAX_WIDE_PATH_METRIC
, 0);
287 zlog_warn("%s: External information table not initialized.",
292 ei_node
= srcdest_rnode_lookup(ei_table
, p
, src_p
);
293 if (!ei_node
|| !ei_node
->info
) {
295 "%s: Got a delete for %s route %pFX, but that route was never added.",
296 __func__
, zebra_route_string(type
), p
);
298 route_unlock_node(ei_node
);
301 route_unlock_node(ei_node
);
303 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
304 for (level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
305 redist
= get_redist_settings(area
, family
, type
, level
);
309 isis_redist_uninstall(area
, level
, p
, src_p
);
312 XFREE(MTYPE_ISIS_EXT_INFO
, ei_node
->info
);
313 route_unlock_node(ei_node
);
316 static void isis_redist_routemap_set(struct isis_redist
*redist
,
317 const char *routemap
)
319 if (redist
->map_name
) {
320 XFREE(MTYPE_ISIS_RMAP_NAME
, redist
->map_name
);
321 route_map_counter_decrement(redist
->map
);
325 if (routemap
&& strlen(routemap
)) {
326 redist
->map_name
= XSTRDUP(MTYPE_ISIS_RMAP_NAME
, routemap
);
327 redist
->map
= route_map_lookup_by_name(routemap
);
328 route_map_counter_increment(redist
->map
);
332 static void isis_redist_update_zebra_subscriptions(struct isis
*isis
)
334 struct listnode
*node
;
335 struct isis_area
*area
;
340 if (isis
->vrf_id
== VRF_UNKNOWN
)
343 char do_subscribe
[REDIST_PROTOCOL_COUNT
][ZEBRA_ROUTE_MAX
+ 1];
345 memset(do_subscribe
, 0, sizeof(do_subscribe
));
347 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
348 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
349 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++)
350 for (level
= 0; level
< ISIS_LEVELS
; level
++)
351 if (area
->redist_settings
[protocol
]
355 do_subscribe
[protocol
][type
] =
358 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
359 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
360 /* This field is actually controlling transmission of
362 * routes to Zebra and has nothing to do with
365 if (type
== PROTO_TYPE
)
368 afi_t afi
= afi_for_redist_protocol(protocol
);
370 if (do_subscribe
[protocol
][type
])
371 isis_zebra_redistribute_set(afi
, type
,
374 isis_zebra_redistribute_unset(afi
, type
,
379 void isis_redist_free(struct isis
*isis
)
381 struct route_node
*rn
;
384 for (i
= 0; i
< REDIST_PROTOCOL_COUNT
; i
++) {
385 if (!isis
->ext_info
[i
])
388 for (rn
= route_top(isis
->ext_info
[i
]); rn
;
389 rn
= srcdest_route_next(rn
)) {
391 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
394 route_table_finish(isis
->ext_info
[i
]);
395 isis
->ext_info
[i
] = NULL
;
399 void isis_redist_set(struct isis_area
*area
, int level
, int family
, int type
,
400 uint32_t metric
, const char *routemap
, int originate_type
)
402 int protocol
= redist_protocol(family
);
403 struct isis_redist
*redist
=
404 get_redist_settings(area
, family
, type
, level
);
406 struct route_table
*ei_table
;
407 struct route_node
*rn
;
408 struct isis_ext_info
*info
;
410 redist
->redist
= (type
== DEFAULT_ROUTE
) ? originate_type
: 1;
411 redist
->metric
= metric
;
412 isis_redist_routemap_set(redist
, routemap
);
414 if (!area
->ext_reach
[protocol
][level
- 1]) {
415 area
->ext_reach
[protocol
][level
- 1] = srcdest_table_init();
418 for (i
= 0; i
< REDIST_PROTOCOL_COUNT
; i
++) {
419 if (!area
->isis
->ext_info
[i
]) {
420 area
->isis
->ext_info
[i
] = srcdest_table_init();
424 isis_redist_update_zebra_subscriptions(area
->isis
);
426 if (type
== DEFAULT_ROUTE
&& originate_type
== DEFAULT_ORIGINATE_ALWAYS
)
427 isis_redist_ensure_default(area
->isis
, family
);
429 ei_table
= get_ext_info(area
->isis
, family
);
430 for (rn
= route_top(ei_table
); rn
; rn
= srcdest_route_next(rn
)) {
435 const struct prefix
*p
, *src_p
;
437 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
439 if (type
== DEFAULT_ROUTE
) {
440 if (!is_default_prefix(p
)
441 || (src_p
&& src_p
->prefixlen
)) {
445 if (info
->origin
!= type
)
449 isis_redist_update_ext_reach(area
, level
, redist
, p
,
450 (const struct prefix_ipv6
*)src_p
,
455 void isis_redist_unset(struct isis_area
*area
, int level
, int family
, int type
)
457 struct isis_redist
*redist
=
458 get_redist_settings(area
, family
, type
, level
);
459 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
460 struct route_node
*rn
;
461 struct isis_ext_info
*info
;
468 zlog_warn("%s: External reachability table uninitialized.",
473 for (rn
= route_top(er_table
); rn
; rn
= srcdest_route_next(rn
)) {
478 const struct prefix
*p
, *src_p
;
479 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
481 if (type
== DEFAULT_ROUTE
) {
482 if (!is_default_prefix(p
)
483 || (src_p
&& src_p
->prefixlen
)) {
487 if (info
->origin
!= type
)
491 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
492 route_unlock_node(rn
);
495 lsp_regenerate_schedule(area
, level
, 0);
496 isis_redist_update_zebra_subscriptions(area
->isis
);
499 void isis_redist_area_finish(struct isis_area
*area
)
501 struct route_node
*rn
;
506 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
507 for (level
= 0; level
< ISIS_LEVELS
; level
++) {
508 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
509 struct isis_redist
*redist
;
511 redist
= &area
->redist_settings
[protocol
][type
]
514 XFREE(MTYPE_ISIS_RMAP_NAME
, redist
->map_name
);
516 if (!area
->ext_reach
[protocol
][level
])
518 for (rn
= route_top(area
->ext_reach
[protocol
][level
]);
519 rn
; rn
= srcdest_route_next(rn
)) {
521 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
523 route_table_finish(area
->ext_reach
[protocol
][level
]);
524 area
->ext_reach
[protocol
][level
] = NULL
;
527 isis_redist_update_zebra_subscriptions(area
->isis
);
531 DEFUN (isis_redistribute
,
532 isis_redistribute_cmd
,
533 "redistribute <ipv4 " PROTO_IP_REDIST_STR
"|ipv6 " PROTO_IP6_REDIST_STR
">"
534 " [{metric (0-16777215)|route-map RMAP_NAME}]",
536 "Redistribute IPv4 routes\n"
538 "Redistribute IPv6 routes\n"
539 PROTO_IP6_REDIST_HELP
540 "Metric for redistributed routes\n"
541 "ISIS default metric\n"
542 "Route map reference\n"
543 "Pointer to route-map entries\n")
546 int idx_protocol
= 2;
547 int idx_metric_rmap
= 1;
548 VTY_DECLVAR_CONTEXT(isis_area
, area
);
553 unsigned long metric
= 0;
554 const char *routemap
= NULL
;
556 family
= str2family(argv
[idx_afi
]->text
);
558 return CMD_WARNING_CONFIG_FAILED
;
560 afi
= family2afi(family
);
562 return CMD_WARNING_CONFIG_FAILED
;
564 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
566 return CMD_WARNING_CONFIG_FAILED
;
570 if ((area
->is_type
& level
) != level
) {
571 vty_out(vty
, "Node is not a level-%d IS\n", level
);
572 return CMD_WARNING_CONFIG_FAILED
;
575 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
576 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
580 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
581 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
584 isis_redist_set(area
, level
, family
, type
, metric
, routemap
, 0);
588 DEFUN (no_isis_redistribute
,
589 no_isis_redistribute_cmd
,
590 "no redistribute <ipv4 " PROTO_IP_REDIST_STR
"|ipv6 " PROTO_IP6_REDIST_STR
">",
593 "Redistribute IPv4 routes\n"
595 "Redistribute IPv6 routes\n"
596 PROTO_IP6_REDIST_HELP
)
599 int idx_protocol
= 3;
600 VTY_DECLVAR_CONTEXT(isis_area
, area
);
606 family
= str2family(argv
[idx_afi
]->arg
);
608 return CMD_WARNING_CONFIG_FAILED
;
610 afi
= family2afi(family
);
612 return CMD_WARNING_CONFIG_FAILED
;
614 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
616 return CMD_WARNING_CONFIG_FAILED
;
620 isis_redist_unset(area
, level
, family
, type
);
624 DEFUN (isis_default_originate
,
625 isis_default_originate_cmd
,
626 "default-information originate <ipv4|ipv6> [always] [{metric (0-16777215)|route-map RMAP_NAME}]",
627 "Control distribution of default information\n"
628 "Distribute a default route\n"
629 "Distribute default route for IPv4\n"
630 "Distribute default route for IPv6\n"
631 "Always advertise default route\n"
632 "Metric for default route\n"
633 "ISIS default metric\n"
634 "Route map reference\n"
635 "Pointer to route-map entries\n")
638 int idx_always
= fabricd
? 3 : 4;
639 int idx_metric_rmap
= 1;
640 VTY_DECLVAR_CONTEXT(isis_area
, area
);
642 int originate_type
= DEFAULT_ORIGINATE
;
644 unsigned long metric
= 0;
645 const char *routemap
= NULL
;
647 family
= str2family(argv
[idx_afi
]->text
);
649 return CMD_WARNING_CONFIG_FAILED
;
653 if ((area
->is_type
& level
) != level
) {
654 vty_out(vty
, "Node is not a level-%d IS\n", level
);
655 return CMD_WARNING_CONFIG_FAILED
;
658 if (argc
> idx_always
&& strmatch(argv
[idx_always
]->text
, "always")) {
659 originate_type
= DEFAULT_ORIGINATE_ALWAYS
;
663 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
664 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
668 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
669 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
672 if (family
== AF_INET6
&& originate_type
!= DEFAULT_ORIGINATE_ALWAYS
) {
674 "Zebra doesn't implement default-originate for IPv6 yet\n");
676 "so use with care or use default-originate always.\n");
679 isis_redist_set(area
, level
, family
, DEFAULT_ROUTE
, metric
, routemap
,
684 DEFUN (no_isis_default_originate
,
685 no_isis_default_originate_cmd
,
686 "no default-information originate <ipv4|ipv6>",
688 "Control distribution of default information\n"
689 "Distribute a default route\n"
690 "Distribute default route for IPv4\n"
691 "Distribute default route for IPv6\n")
694 VTY_DECLVAR_CONTEXT(isis_area
, area
);
698 family
= str2family(argv
[idx_afi
]->text
);
700 return CMD_WARNING_CONFIG_FAILED
;
704 isis_redist_unset(area
, level
, family
, DEFAULT_ROUTE
);
707 #endif /* ifdef FABRICD */
709 int isis_redist_config_write(struct vty
*vty
, struct isis_area
*area
,
715 struct isis_redist
*redist
;
716 const char *family_str
;
718 if (family
== AF_INET
)
720 else if (family
== AF_INET6
)
725 for (type
= 0; type
< ZEBRA_ROUTE_MAX
; type
++) {
726 if (type
== PROTO_TYPE
)
729 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
730 redist
= get_redist_settings(area
, family
, type
, level
);
733 vty_out(vty
, " redistribute %s %s", family_str
,
734 zebra_route_string(type
));
736 vty_out(vty
, " level-%d", level
);
738 vty_out(vty
, " metric %u", redist
->metric
);
739 if (redist
->map_name
)
740 vty_out(vty
, " route-map %s", redist
->map_name
);
746 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
748 get_redist_settings(area
, family
, DEFAULT_ROUTE
, level
);
751 vty_out(vty
, " default-information originate %s",
754 vty_out(vty
, " level-%d", level
);
755 if (redist
->redist
== DEFAULT_ORIGINATE_ALWAYS
)
756 vty_out(vty
, " always");
758 vty_out(vty
, " metric %u", redist
->metric
);
759 if (redist
->map_name
)
760 vty_out(vty
, " route-map %s", redist
->map_name
);
768 void isis_redist_init(void)
771 install_element(ROUTER_NODE
, &isis_redistribute_cmd
);
772 install_element(ROUTER_NODE
, &no_isis_redistribute_cmd
);
774 install_element(ROUTER_NODE
, &isis_default_originate_cmd
);
775 install_element(ROUTER_NODE
, &no_isis_default_originate_cmd
);
776 #endif /* ifdef FABRICD */