2 * IS-IS Rout(e)ing protocol - isis_redist.c
4 * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
11 * This program is distributed in the hope that it will be useful,but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 #include "isis_memory.h"
34 #include "isisd/dict.h"
35 #include "isisd/isis_constants.h"
36 #include "isisd/isis_common.h"
37 #include "isisd/isis_flags.h"
38 #include "isisd/isis_misc.h"
39 #include "isisd/isis_circuit.h"
40 #include "isisd/isis_tlv.h"
41 #include "isisd/isisd.h"
42 #include "isisd/isis_lsp.h"
43 #include "isisd/isis_route.h"
44 #include "isisd/isis_zebra.h"
46 static int redist_protocol(int family
)
48 if (family
== AF_INET
)
50 if (family
== AF_INET6
)
53 assert(!"Unsupported address family!");
57 static afi_t
afi_for_redist_protocol(int protocol
)
64 assert(!"Unknown redist protocol!");
68 static int is_default(struct prefix
*p
)
70 if (p
->family
== AF_INET
)
71 if (p
->u
.prefix4
.s_addr
== 0 && p
->prefixlen
== 0)
73 if (p
->family
== AF_INET6
)
74 if (IN6_IS_ADDR_UNSPECIFIED(&p
->u
.prefix6
) && p
->prefixlen
== 0)
79 static struct route_table
*get_ext_info(struct isis
*i
, int family
)
81 int protocol
= redist_protocol(family
);
83 return i
->ext_info
[protocol
];
86 static struct isis_redist
*get_redist_settings(struct isis_area
*area
,
87 int family
, int type
, int level
)
89 int protocol
= redist_protocol(family
);
91 return &area
->redist_settings
[protocol
][type
][level
- 1];
94 struct route_table
*get_ext_reach(struct isis_area
*area
, int family
, int level
)
96 int protocol
= redist_protocol(family
);
98 return area
->ext_reach
[protocol
][level
- 1];
101 static struct route_node
*
102 isis_redist_route_node_create(route_table_delegate_t
*delegate
,
103 struct route_table
*table
)
105 struct route_node
*node
;
106 node
= XCALLOC(MTYPE_ISIS_EXT_ROUTE
, sizeof(*node
));
110 static void isis_redist_route_node_destroy(route_table_delegate_t
*delegate
,
111 struct route_table
*table
,
112 struct route_node
*node
)
115 XFREE(MTYPE_ISIS_EXT_INFO
, node
->info
);
116 XFREE(MTYPE_ISIS_EXT_ROUTE
, node
);
119 static route_table_delegate_t isis_redist_rt_delegate
= {
120 .create_node
= isis_redist_route_node_create
,
121 .destroy_node
= isis_redist_route_node_destroy
};
123 /* Install external reachability information into a
124 * specific area for a specific level.
125 * Schedule an lsp regenerate if necessary */
126 static void isis_redist_install(struct isis_area
*area
, int level
,
127 struct prefix
*p
, struct isis_ext_info
*info
)
129 int family
= p
->family
;
130 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
131 struct route_node
*er_node
;
135 "%s: External reachability table of area %s"
136 " is not initialized.",
137 __func__
, area
->area_tag
);
141 er_node
= route_node_get(er_table
, p
);
143 route_unlock_node(er_node
);
145 /* Don't update/reschedule lsp generation if nothing changed. */
146 if (!memcmp(er_node
->info
, info
, sizeof(*info
)))
149 er_node
->info
= XMALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(*info
));
152 memcpy(er_node
->info
, info
, sizeof(*info
));
153 lsp_regenerate_schedule(area
, level
, 0);
156 /* Remove external reachability information from a
157 * specific area for a specific level.
158 * Schedule an lsp regenerate if necessary. */
159 static void isis_redist_uninstall(struct isis_area
*area
, int level
,
162 int family
= p
->family
;
163 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
164 struct route_node
*er_node
;
168 "%s: External reachability table of area %s"
169 " is not initialized.",
170 __func__
, area
->area_tag
);
174 er_node
= route_node_lookup(er_table
, p
);
178 route_unlock_node(er_node
);
183 XFREE(MTYPE_ISIS_EXT_INFO
, er_node
->info
);
184 route_unlock_node(er_node
);
185 lsp_regenerate_schedule(area
, level
, 0);
188 /* Update external reachability info of area for a given level
189 * and prefix, using the given redistribution settings. */
190 static void isis_redist_update_ext_reach(struct isis_area
*area
, int level
,
191 struct isis_redist
*redist
,
193 struct isis_ext_info
*info
)
195 struct isis_ext_info area_info
;
196 route_map_result_t map_ret
;
198 memcpy(&area_info
, info
, sizeof(area_info
));
199 if (redist
->metric
!= 0xffffffff)
200 area_info
.metric
= redist
->metric
;
202 if (redist
->map_name
) {
204 route_map_apply(redist
->map
, p
, RMAP_ISIS
, &area_info
);
205 if (map_ret
== RMAP_DENYMATCH
)
206 area_info
.distance
= 255;
209 /* Allow synthesized default routes only on always orignate */
210 if (area_info
.origin
== DEFAULT_ROUTE
211 && redist
->redist
!= DEFAULT_ORIGINATE_ALWAYS
)
212 area_info
.distance
= 255;
214 if (area_info
.distance
< 255)
215 isis_redist_install(area
, level
, p
, &area_info
);
217 isis_redist_uninstall(area
, level
, p
);
220 static void isis_redist_ensure_default(struct isis
*isis
, int family
)
223 struct route_table
*ei_table
= get_ext_info(isis
, family
);
224 struct route_node
*ei_node
;
225 struct isis_ext_info
*info
;
227 if (family
== AF_INET
) {
230 memset(&p
.u
.prefix4
, 0, sizeof(p
.u
.prefix4
));
231 } else if (family
== AF_INET6
) {
234 memset(&p
.u
.prefix6
, 0, sizeof(p
.u
.prefix6
));
236 assert(!"Unknown family!");
238 ei_node
= route_node_get(ei_table
, &p
);
240 route_unlock_node(ei_node
);
245 XCALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(struct isis_ext_info
));
247 info
= ei_node
->info
;
248 info
->origin
= DEFAULT_ROUTE
;
249 info
->distance
= 254;
250 info
->metric
= MAX_WIDE_PATH_METRIC
;
253 /* Handle notification about route being added */
254 void isis_redist_add(int type
, struct prefix
*p
, u_char distance
,
257 int family
= p
->family
;
258 struct route_table
*ei_table
= get_ext_info(isis
, family
);
259 struct route_node
*ei_node
;
260 struct isis_ext_info
*info
;
261 struct listnode
*node
;
262 struct isis_area
*area
;
264 struct isis_redist
*redist
;
266 char debug_buf
[BUFSIZ
];
267 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
269 zlog_debug("%s: New route %s from %s.", __func__
, debug_buf
,
270 zebra_route_string(type
));
273 zlog_warn("%s: External information table not initialized.",
278 ei_node
= route_node_get(ei_table
, p
);
280 route_unlock_node(ei_node
);
282 ei_node
->info
= XCALLOC(MTYPE_ISIS_EXT_INFO
,
283 sizeof(struct isis_ext_info
));
285 info
= ei_node
->info
;
287 info
->distance
= distance
;
288 info
->metric
= metric
;
291 type
= DEFAULT_ROUTE
;
293 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
294 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
295 redist
= get_redist_settings(area
, family
, type
, level
);
299 isis_redist_update_ext_reach(area
, level
, redist
, p
,
304 void isis_redist_delete(int type
, struct prefix
*p
)
306 int family
= p
->family
;
307 struct route_table
*ei_table
= get_ext_info(isis
, family
);
308 struct route_node
*ei_node
;
309 struct listnode
*node
;
310 struct isis_area
*area
;
312 struct isis_redist
*redist
;
314 char debug_buf
[BUFSIZ
];
315 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
317 zlog_debug("%s: Removing route %s from %s.", __func__
, debug_buf
,
318 zebra_route_string(type
));
321 /* Don't remove default route but add synthetic route for use
322 * by "default-information originate always". Areas without the
323 * "always" setting will ignore routes with origin
325 isis_redist_add(DEFAULT_ROUTE
, p
, 254, MAX_WIDE_PATH_METRIC
);
330 zlog_warn("%s: External information table not initialized.",
335 ei_node
= route_node_lookup(ei_table
, p
);
336 if (!ei_node
|| !ei_node
->info
) {
338 prefix2str(p
, buf
, sizeof(buf
));
340 "%s: Got a delete for %s route %s, but that route"
342 __func__
, zebra_route_string(type
), buf
);
344 route_unlock_node(ei_node
);
347 route_unlock_node(ei_node
);
349 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
350 for (level
= 1; level
< ISIS_LEVELS
; level
++) {
351 redist
= get_redist_settings(area
, family
, type
, level
);
355 isis_redist_uninstall(area
, level
, p
);
358 XFREE(MTYPE_ISIS_EXT_INFO
, ei_node
->info
);
359 route_unlock_node(ei_node
);
362 static void isis_redist_routemap_set(struct isis_redist
*redist
,
363 const char *routemap
)
365 if (redist
->map_name
) {
366 XFREE(MTYPE_ISIS
, redist
->map_name
);
370 if (routemap
&& strlen(routemap
)) {
371 redist
->map_name
= XSTRDUP(MTYPE_ISIS
, routemap
);
372 redist
->map
= route_map_lookup_by_name(routemap
);
376 static void isis_redist_update_zebra_subscriptions(struct isis
*isis
)
378 struct listnode
*node
;
379 struct isis_area
*area
;
384 char do_subscribe
[REDIST_PROTOCOL_COUNT
][ZEBRA_ROUTE_MAX
+ 1];
386 memset(do_subscribe
, 0, sizeof(do_subscribe
));
388 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
389 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
390 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++)
391 for (level
= 0; level
< ISIS_LEVELS
; level
++)
392 if (area
->redist_settings
[protocol
]
395 do_subscribe
[protocol
][type
] =
398 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
399 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
400 /* This field is actually controlling transmission of
402 * routes to Zebra and has nothing to do with
405 if (type
== ZEBRA_ROUTE_ISIS
)
408 afi_t afi
= afi_for_redist_protocol(protocol
);
410 if (do_subscribe
[protocol
][type
])
411 isis_zebra_redistribute_set(afi
, type
);
413 isis_zebra_redistribute_unset(afi
, type
);
417 static void isis_redist_set(struct isis_area
*area
, int level
, int family
,
418 int type
, uint32_t metric
, const char *routemap
,
421 int protocol
= redist_protocol(family
);
422 struct isis_redist
*redist
=
423 get_redist_settings(area
, family
, type
, level
);
425 struct route_table
*ei_table
;
426 struct route_node
*rn
;
427 struct isis_ext_info
*info
;
429 redist
->redist
= (type
== DEFAULT_ROUTE
) ? originate_type
: 1;
430 redist
->metric
= metric
;
431 isis_redist_routemap_set(redist
, routemap
);
433 if (!area
->ext_reach
[protocol
][level
- 1]) {
434 area
->ext_reach
[protocol
][level
- 1] =
435 route_table_init_with_delegate(
436 &isis_redist_rt_delegate
);
439 for (i
= 0; i
< REDIST_PROTOCOL_COUNT
; i
++)
440 if (!area
->isis
->ext_info
[i
]) {
441 area
->isis
->ext_info
[i
] =
442 route_table_init_with_delegate(
443 &isis_redist_rt_delegate
);
446 isis_redist_update_zebra_subscriptions(area
->isis
);
448 if (type
== DEFAULT_ROUTE
&& originate_type
== DEFAULT_ORIGINATE_ALWAYS
)
449 isis_redist_ensure_default(area
->isis
, family
);
451 ei_table
= get_ext_info(area
->isis
, family
);
452 for (rn
= route_top(ei_table
); rn
; rn
= route_next(rn
)) {
457 if (type
== DEFAULT_ROUTE
) {
458 if (!is_default(&rn
->p
))
461 if (info
->origin
!= type
)
465 isis_redist_update_ext_reach(area
, level
, redist
, &rn
->p
, info
);
469 static void isis_redist_unset(struct isis_area
*area
, int level
, int family
,
472 struct isis_redist
*redist
=
473 get_redist_settings(area
, family
, type
, level
);
474 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
475 struct route_node
*rn
;
476 struct isis_ext_info
*info
;
483 zlog_warn("%s: External reachability table uninitialized.",
488 for (rn
= route_top(er_table
); rn
; rn
= route_next(rn
)) {
493 if (type
== DEFAULT_ROUTE
) {
494 if (!is_default(&rn
->p
))
497 if (info
->origin
!= type
)
501 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
502 route_unlock_node(rn
);
505 lsp_regenerate_schedule(area
, level
, 0);
506 isis_redist_update_zebra_subscriptions(area
->isis
);
509 void isis_redist_area_finish(struct isis_area
*area
)
515 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
516 for (level
= 0; level
< ISIS_LEVELS
; level
++) {
517 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
518 struct isis_redist
*redist
;
520 redist
= &area
->redist_settings
[protocol
][type
]
523 if (redist
->map_name
)
524 XFREE(MTYPE_ISIS
, redist
->map_name
);
526 route_table_finish(area
->ext_reach
[protocol
][level
]);
529 isis_redist_update_zebra_subscriptions(area
->isis
);
532 DEFUN (isis_redistribute
,
533 isis_redistribute_cmd
,
534 "redistribute <ipv4|ipv6> " FRR_REDIST_STR_ISISD
" <level-1|level-2> [<metric (0-16777215)|route-map WORD>]",
536 "Redistribute IPv4 routes\n"
537 "Redistribute IPv6 routes\n"
538 FRR_REDIST_HELP_STR_ISISD
539 "Redistribute into level-1\n"
540 "Redistribute into level-2\n"
541 "Metric for redistributed routes\n"
542 "ISIS default metric\n"
543 "Route map reference\n"
544 "Pointer to route-map entries\n")
547 int idx_protocol
= 2;
549 int idx_metric_rmap
= 4;
550 VTY_DECLVAR_CONTEXT(isis_area
, area
);
555 unsigned long metric
;
556 const char *routemap
= NULL
;
558 family
= str2family(argv
[idx_afi
]->text
);
560 return CMD_WARNING_CONFIG_FAILED
;
562 afi
= family2afi(family
);
564 return CMD_WARNING_CONFIG_FAILED
;
566 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
568 return CMD_WARNING_CONFIG_FAILED
;
570 if (!strcmp("level-1", argv
[idx_level
]->arg
))
572 else if (!strcmp("level-2", argv
[idx_level
]->arg
))
575 return CMD_WARNING_CONFIG_FAILED
;
577 if ((area
->is_type
& level
) != level
) {
578 vty_out(vty
, "Node is not a level-%d IS\n", level
);
579 return CMD_WARNING_CONFIG_FAILED
;
585 if (argc
> idx_metric_rmap
+ 1) {
586 if (argv
[idx_metric_rmap
+ 1]->arg
[0] == '\0')
587 return CMD_WARNING_CONFIG_FAILED
;
589 if (strmatch(argv
[idx_metric_rmap
]->text
, "metric")) {
591 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, &endp
,
595 return CMD_WARNING_CONFIG_FAILED
;
597 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
601 isis_redist_set(area
, level
, family
, type
, metric
, routemap
, 0);
605 DEFUN (no_isis_redistribute
,
606 no_isis_redistribute_cmd
,
607 "no redistribute <ipv4|ipv6> " FRR_REDIST_STR_ISISD
" <level-1|level-2>",
610 "Redistribute IPv4 routes\n"
611 "Redistribute IPv6 routes\n"
612 FRR_REDIST_HELP_STR_ISISD
613 "Redistribute into level-1\n"
614 "Redistribute into level-2\n")
617 int idx_protocol
= 3;
619 VTY_DECLVAR_CONTEXT(isis_area
, area
);
625 family
= str2family(argv
[idx_afi
]->arg
);
627 return CMD_WARNING_CONFIG_FAILED
;
629 afi
= family2afi(family
);
631 return CMD_WARNING_CONFIG_FAILED
;
633 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
635 return CMD_WARNING_CONFIG_FAILED
;
637 level
= strmatch("level-1", argv
[idx_level
]->text
) ? 1 : 2;
639 isis_redist_unset(area
, level
, family
, type
);
643 DEFUN (isis_default_originate
,
644 isis_default_originate_cmd
,
645 "default-information originate <ipv4|ipv6> <level-1|level-2> [always] [<metric (0-16777215)|route-map WORD>]",
646 "Control distribution of default information\n"
647 "Distribute a default route\n"
648 "Distribute default route for IPv4\n"
649 "Distribute default route for IPv6\n"
650 "Distribute default route into level-1\n"
651 "Distribute default route into level-2\n"
652 "Always advertise default route\n"
653 "Metric for default route\n"
654 "ISIS default metric\n"
655 "Route map reference\n"
656 "Pointer to route-map entries\n")
661 int idx_metric_rmap
= 4;
662 VTY_DECLVAR_CONTEXT(isis_area
, area
);
664 int originate_type
= DEFAULT_ORIGINATE
;
666 unsigned long metric
= 0xffffffff;
667 const char *routemap
= NULL
;
669 family
= str2family(argv
[idx_afi
]->text
);
671 return CMD_WARNING_CONFIG_FAILED
;
673 level
= strmatch("level-1", argv
[idx_level
]->text
) ? 1 : 2;
675 if ((area
->is_type
& level
) != level
) {
676 vty_out(vty
, "Node is not a level-%d IS\n", level
);
677 return CMD_WARNING_CONFIG_FAILED
;
680 if (argc
> idx_always
&& strmatch(argv
[idx_always
]->text
, "always")) {
681 originate_type
= DEFAULT_ORIGINATE_ALWAYS
;
685 if (argc
> idx_metric_rmap
) {
686 if (strmatch(argv
[idx_metric_rmap
]->text
, "metric"))
687 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
,
690 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
693 if (family
== AF_INET6
&& originate_type
!= DEFAULT_ORIGINATE_ALWAYS
) {
695 "Zebra doesn't implement default-originate for IPv6 yet\n");
697 "so use with care or use default-originate always.\n");
700 isis_redist_set(area
, level
, family
, DEFAULT_ROUTE
, metric
, routemap
,
705 DEFUN (no_isis_default_originate
,
706 no_isis_default_originate_cmd
,
707 "no default-information originate <ipv4|ipv6> <level-1|level-2>",
709 "Control distribution of default information\n"
710 "Distribute a default route\n"
711 "Distribute default route for IPv4\n"
712 "Distribute default route for IPv6\n"
713 "Distribute default route into level-1\n"
714 "Distribute default route into level-2\n")
718 VTY_DECLVAR_CONTEXT(isis_area
, area
);
722 family
= str2family(argv
[idx_afi
]->text
);
724 return CMD_WARNING_CONFIG_FAILED
;
726 if (strmatch("level-1", argv
[idx_level
]->text
))
728 else if (strmatch("level-2", argv
[idx_level
]->text
))
731 return CMD_WARNING_CONFIG_FAILED
;
733 isis_redist_unset(area
, level
, family
, DEFAULT_ROUTE
);
737 int isis_redist_config_write(struct vty
*vty
, struct isis_area
*area
,
743 struct isis_redist
*redist
;
744 const char *family_str
;
746 if (family
== AF_INET
)
748 else if (family
== AF_INET6
)
753 for (type
= 0; type
< ZEBRA_ROUTE_MAX
; type
++) {
754 if (type
== ZEBRA_ROUTE_ISIS
)
757 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
758 redist
= get_redist_settings(area
, family
, type
, level
);
761 vty_out(vty
, " redistribute %s %s level-%d", family_str
,
762 zebra_route_string(type
), level
);
763 if (redist
->metric
!= 0xffffffff)
764 vty_out(vty
, " metric %u", redist
->metric
);
765 if (redist
->map_name
)
766 vty_out(vty
, " route-map %s", redist
->map_name
);
772 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
774 get_redist_settings(area
, family
, DEFAULT_ROUTE
, level
);
777 vty_out(vty
, " default-information originate %s level-%d",
779 if (redist
->redist
== DEFAULT_ORIGINATE_ALWAYS
)
780 vty_out(vty
, " always");
781 if (redist
->metric
!= 0xffffffff)
782 vty_out(vty
, " metric %u", redist
->metric
);
783 if (redist
->map_name
)
784 vty_out(vty
, " route-map %s", redist
->map_name
);
792 void isis_redist_init(void)
794 install_element(ISIS_NODE
, &isis_redistribute_cmd
);
795 install_element(ISIS_NODE
, &no_isis_redistribute_cmd
);
796 install_element(ISIS_NODE
, &isis_default_originate_cmd
);
797 install_element(ISIS_NODE
, &no_isis_default_originate_cmd
);