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"
33 #include "srcdest_table.h"
35 #include "isisd/dict.h"
36 #include "isisd/isis_constants.h"
37 #include "isisd/isis_common.h"
38 #include "isisd/isis_flags.h"
39 #include "isisd/isis_misc.h"
40 #include "isisd/isis_circuit.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 struct route_table
*get_ext_info(struct isis
*i
, int family
)
70 int protocol
= redist_protocol(family
);
72 return i
->ext_info
[protocol
];
75 static struct isis_redist
*get_redist_settings(struct isis_area
*area
,
76 int family
, int type
, int level
)
78 int protocol
= redist_protocol(family
);
80 return &area
->redist_settings
[protocol
][type
][level
- 1];
83 struct route_table
*get_ext_reach(struct isis_area
*area
, int family
, int level
)
85 int protocol
= redist_protocol(family
);
87 return area
->ext_reach
[protocol
][level
- 1];
90 /* Install external reachability information into a
91 * specific area for a specific level.
92 * Schedule an lsp regenerate if necessary */
93 static void isis_redist_install(struct isis_area
*area
, int level
,
94 const struct prefix
*p
,
95 const struct prefix_ipv6
*src_p
,
96 struct isis_ext_info
*info
)
98 int family
= p
->family
;
99 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
100 struct route_node
*er_node
;
104 "%s: External reachability table of area %s"
105 " is not initialized.",
106 __func__
, area
->area_tag
);
110 er_node
= srcdest_rnode_get(er_table
, p
, src_p
);
112 route_unlock_node(er_node
);
114 /* Don't update/reschedule lsp generation if nothing changed. */
115 if (!memcmp(er_node
->info
, info
, sizeof(*info
)))
118 er_node
->info
= XMALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(*info
));
121 memcpy(er_node
->info
, info
, sizeof(*info
));
122 lsp_regenerate_schedule(area
, level
, 0);
125 /* Remove external reachability information from a
126 * specific area for a specific level.
127 * Schedule an lsp regenerate if necessary. */
128 static void isis_redist_uninstall(struct isis_area
*area
, int level
,
129 const struct prefix
*p
,
130 const struct prefix_ipv6
*src_p
)
132 int family
= p
->family
;
133 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
134 struct route_node
*er_node
;
138 "%s: External reachability table of area %s"
139 " is not initialized.",
140 __func__
, area
->area_tag
);
144 er_node
= srcdest_rnode_lookup(er_table
, p
, src_p
);
148 route_unlock_node(er_node
);
153 XFREE(MTYPE_ISIS_EXT_INFO
, er_node
->info
);
154 route_unlock_node(er_node
);
155 lsp_regenerate_schedule(area
, level
, 0);
158 /* Update external reachability info of area for a given level
159 * and prefix, using the given redistribution settings. */
160 static void isis_redist_update_ext_reach(struct isis_area
*area
, int level
,
161 struct isis_redist
*redist
,
162 const struct prefix
*p
,
163 const struct prefix_ipv6
*src_p
,
164 struct isis_ext_info
*info
)
166 struct isis_ext_info area_info
;
167 route_map_result_t map_ret
;
169 memcpy(&area_info
, info
, sizeof(area_info
));
170 area_info
.metric
= redist
->metric
;
172 if (redist
->map_name
) {
174 route_map_apply(redist
->map
, (struct prefix
*)p
,
175 RMAP_ISIS
, &area_info
);
176 if (map_ret
== RMAP_DENYMATCH
)
177 area_info
.distance
= 255;
180 /* Allow synthesized default routes only on always orignate */
181 if (area_info
.origin
== DEFAULT_ROUTE
182 && redist
->redist
!= DEFAULT_ORIGINATE_ALWAYS
)
183 area_info
.distance
= 255;
185 if (area_info
.distance
< 255)
186 isis_redist_install(area
, level
, p
, src_p
, &area_info
);
188 isis_redist_uninstall(area
, level
, p
, src_p
);
191 static void isis_redist_ensure_default(struct isis
*isis
, int family
)
194 struct route_table
*ei_table
= get_ext_info(isis
, family
);
195 struct route_node
*ei_node
;
196 struct isis_ext_info
*info
;
198 if (family
== AF_INET
) {
201 memset(&p
.u
.prefix4
, 0, sizeof(p
.u
.prefix4
));
202 } else if (family
== AF_INET6
) {
205 memset(&p
.u
.prefix6
, 0, sizeof(p
.u
.prefix6
));
207 assert(!"Unknown family!");
209 ei_node
= srcdest_rnode_get(ei_table
, &p
, NULL
);
211 route_unlock_node(ei_node
);
216 XCALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(struct isis_ext_info
));
218 info
= ei_node
->info
;
219 info
->origin
= DEFAULT_ROUTE
;
220 info
->distance
= 254;
221 info
->metric
= MAX_WIDE_PATH_METRIC
;
224 /* Handle notification about route being added */
225 void isis_redist_add(int type
, struct prefix
*p
, struct prefix_ipv6
*src_p
,
226 uint8_t distance
, uint32_t metric
)
228 int family
= p
->family
;
229 struct route_table
*ei_table
= get_ext_info(isis
, family
);
230 struct route_node
*ei_node
;
231 struct isis_ext_info
*info
;
232 struct listnode
*node
;
233 struct isis_area
*area
;
235 struct isis_redist
*redist
;
237 char debug_buf
[BUFSIZ
];
238 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
240 zlog_debug("%s: New route %s from %s: distance %d.", __func__
,
241 debug_buf
, zebra_route_string(type
), distance
);
244 zlog_warn("%s: External information table not initialized.",
249 ei_node
= srcdest_rnode_get(ei_table
, p
, src_p
);
251 route_unlock_node(ei_node
);
253 ei_node
->info
= XCALLOC(MTYPE_ISIS_EXT_INFO
,
254 sizeof(struct isis_ext_info
));
256 info
= ei_node
->info
;
258 info
->distance
= distance
;
259 info
->metric
= metric
;
261 if (is_default_prefix(p
)
262 && (!src_p
|| !src_p
->prefixlen
)) {
263 type
= DEFAULT_ROUTE
;
266 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
267 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
268 redist
= get_redist_settings(area
, family
, type
, level
);
272 isis_redist_update_ext_reach(area
, level
, redist
, p
,
277 void isis_redist_delete(int type
, struct prefix
*p
, struct prefix_ipv6
*src_p
)
279 int family
= p
->family
;
280 struct route_table
*ei_table
= get_ext_info(isis
, family
);
281 struct route_node
*ei_node
;
282 struct listnode
*node
;
283 struct isis_area
*area
;
285 struct isis_redist
*redist
;
287 char debug_buf
[BUFSIZ
];
288 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
290 zlog_debug("%s: Removing route %s from %s.", __func__
, debug_buf
,
291 zebra_route_string(type
));
293 if (is_default_prefix(p
)
294 && (!src_p
|| !src_p
->prefixlen
)) {
295 /* Don't remove default route but add synthetic route for use
296 * by "default-information originate always". Areas without the
297 * "always" setting will ignore routes with origin
299 isis_redist_add(DEFAULT_ROUTE
, p
, NULL
,
300 254, MAX_WIDE_PATH_METRIC
);
305 zlog_warn("%s: External information table not initialized.",
310 ei_node
= srcdest_rnode_lookup(ei_table
, p
, src_p
);
311 if (!ei_node
|| !ei_node
->info
) {
313 prefix2str(p
, buf
, sizeof(buf
));
315 "%s: Got a delete for %s route %s, but that route"
317 __func__
, zebra_route_string(type
), buf
);
319 route_unlock_node(ei_node
);
322 route_unlock_node(ei_node
);
324 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
325 for (level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
326 redist
= get_redist_settings(area
, family
, type
, level
);
330 isis_redist_uninstall(area
, level
, p
, src_p
);
333 XFREE(MTYPE_ISIS_EXT_INFO
, ei_node
->info
);
334 route_unlock_node(ei_node
);
337 static void isis_redist_routemap_set(struct isis_redist
*redist
,
338 const char *routemap
)
340 if (redist
->map_name
) {
341 XFREE(MTYPE_ISIS
, redist
->map_name
);
345 if (routemap
&& strlen(routemap
)) {
346 redist
->map_name
= XSTRDUP(MTYPE_ISIS
, routemap
);
347 redist
->map
= route_map_lookup_by_name(routemap
);
351 static void isis_redist_update_zebra_subscriptions(struct isis
*isis
)
353 struct listnode
*node
;
354 struct isis_area
*area
;
359 char do_subscribe
[REDIST_PROTOCOL_COUNT
][ZEBRA_ROUTE_MAX
+ 1];
361 memset(do_subscribe
, 0, sizeof(do_subscribe
));
363 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
364 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
365 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++)
366 for (level
= 0; level
< ISIS_LEVELS
; level
++)
367 if (area
->redist_settings
[protocol
]
370 do_subscribe
[protocol
][type
] =
373 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
374 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
375 /* This field is actually controlling transmission of
377 * routes to Zebra and has nothing to do with
380 if (type
== ZEBRA_ROUTE_ISIS
)
383 afi_t afi
= afi_for_redist_protocol(protocol
);
385 if (do_subscribe
[protocol
][type
])
386 isis_zebra_redistribute_set(afi
, type
);
388 isis_zebra_redistribute_unset(afi
, type
);
392 static void isis_redist_set(struct isis_area
*area
, int level
, int family
,
393 int type
, uint32_t metric
, const char *routemap
,
396 int protocol
= redist_protocol(family
);
397 struct isis_redist
*redist
=
398 get_redist_settings(area
, family
, type
, level
);
400 struct route_table
*ei_table
;
401 struct route_node
*rn
;
402 struct isis_ext_info
*info
;
404 redist
->redist
= (type
== DEFAULT_ROUTE
) ? originate_type
: 1;
405 redist
->metric
= metric
;
406 isis_redist_routemap_set(redist
, routemap
);
408 if (!area
->ext_reach
[protocol
][level
- 1]) {
409 area
->ext_reach
[protocol
][level
- 1] = srcdest_table_init();
412 for (i
= 0; i
< REDIST_PROTOCOL_COUNT
; i
++) {
413 if (!area
->isis
->ext_info
[i
]) {
414 area
->isis
->ext_info
[i
] = srcdest_table_init();
418 isis_redist_update_zebra_subscriptions(area
->isis
);
420 if (type
== DEFAULT_ROUTE
&& originate_type
== DEFAULT_ORIGINATE_ALWAYS
)
421 isis_redist_ensure_default(area
->isis
, family
);
423 ei_table
= get_ext_info(area
->isis
, family
);
424 for (rn
= route_top(ei_table
); rn
; rn
= srcdest_route_next(rn
)) {
429 const struct prefix
*p
, *src_p
;
431 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
433 if (type
== DEFAULT_ROUTE
) {
434 if (!is_default_prefix(p
)
435 || (src_p
&& src_p
->prefixlen
)) {
439 if (info
->origin
!= type
)
443 isis_redist_update_ext_reach(area
, level
, redist
, p
,
444 (struct prefix_ipv6
*)src_p
, info
);
448 static void isis_redist_unset(struct isis_area
*area
, int level
, int family
,
451 struct isis_redist
*redist
=
452 get_redist_settings(area
, family
, type
, level
);
453 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
454 struct route_node
*rn
;
455 struct isis_ext_info
*info
;
462 zlog_warn("%s: External reachability table uninitialized.",
467 for (rn
= route_top(er_table
); rn
; rn
= srcdest_route_next(rn
)) {
472 const struct prefix
*p
, *src_p
;
473 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
475 if (type
== DEFAULT_ROUTE
) {
476 if (!is_default_prefix(p
)
477 || (src_p
&& src_p
->prefixlen
)) {
481 if (info
->origin
!= type
)
485 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
486 route_unlock_node(rn
);
489 lsp_regenerate_schedule(area
, level
, 0);
490 isis_redist_update_zebra_subscriptions(area
->isis
);
493 void isis_redist_area_finish(struct isis_area
*area
)
499 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
500 for (level
= 0; level
< ISIS_LEVELS
; level
++) {
501 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
502 struct isis_redist
*redist
;
504 redist
= &area
->redist_settings
[protocol
][type
]
507 if (redist
->map_name
)
508 XFREE(MTYPE_ISIS
, redist
->map_name
);
510 route_table_finish(area
->ext_reach
[protocol
][level
]);
513 isis_redist_update_zebra_subscriptions(area
->isis
);
516 DEFUN (isis_redistribute
,
517 isis_redistribute_cmd
,
518 "redistribute <ipv4|ipv6> " FRR_REDIST_STR_ISISD
" <level-1|level-2> [<metric (0-16777215)|route-map WORD>]",
520 "Redistribute IPv4 routes\n"
521 "Redistribute IPv6 routes\n"
522 FRR_REDIST_HELP_STR_ISISD
523 "Redistribute into level-1\n"
524 "Redistribute into level-2\n"
525 "Metric for redistributed routes\n"
526 "ISIS default metric\n"
527 "Route map reference\n"
528 "Pointer to route-map entries\n")
531 int idx_protocol
= 2;
533 int idx_metric_rmap
= 4;
534 VTY_DECLVAR_CONTEXT(isis_area
, area
);
539 unsigned long metric
= 0;
540 const char *routemap
= NULL
;
542 family
= str2family(argv
[idx_afi
]->text
);
544 return CMD_WARNING_CONFIG_FAILED
;
546 afi
= family2afi(family
);
548 return CMD_WARNING_CONFIG_FAILED
;
550 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
552 return CMD_WARNING_CONFIG_FAILED
;
554 if (!strcmp("level-1", argv
[idx_level
]->arg
))
556 else if (!strcmp("level-2", argv
[idx_level
]->arg
))
559 return CMD_WARNING_CONFIG_FAILED
;
561 if ((area
->is_type
& level
) != level
) {
562 vty_out(vty
, "Node is not a level-%d IS\n", level
);
563 return CMD_WARNING_CONFIG_FAILED
;
566 if (argc
> idx_metric_rmap
+ 1) {
567 if (argv
[idx_metric_rmap
+ 1]->arg
[0] == '\0')
568 return CMD_WARNING_CONFIG_FAILED
;
570 if (strmatch(argv
[idx_metric_rmap
]->text
, "metric")) {
572 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, &endp
,
576 return CMD_WARNING_CONFIG_FAILED
;
578 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
582 isis_redist_set(area
, level
, family
, type
, metric
, routemap
, 0);
586 DEFUN (no_isis_redistribute
,
587 no_isis_redistribute_cmd
,
588 "no redistribute <ipv4|ipv6> " FRR_REDIST_STR_ISISD
" <level-1|level-2>",
591 "Redistribute IPv4 routes\n"
592 "Redistribute IPv6 routes\n"
593 FRR_REDIST_HELP_STR_ISISD
594 "Redistribute into level-1\n"
595 "Redistribute into level-2\n")
598 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
;
618 level
= strmatch("level-1", argv
[idx_level
]->text
) ? 1 : 2;
620 isis_redist_unset(area
, level
, family
, type
);
624 DEFUN (isis_default_originate
,
625 isis_default_originate_cmd
,
626 "default-information originate <ipv4|ipv6> <level-1|level-2> [always] [<metric (0-16777215)|route-map WORD>]",
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 "Distribute default route into level-1\n"
632 "Distribute default route into level-2\n"
633 "Always advertise default route\n"
634 "Metric for default route\n"
635 "ISIS default metric\n"
636 "Route map reference\n"
637 "Pointer to route-map entries\n")
642 int idx_metric_rmap
= 4;
643 VTY_DECLVAR_CONTEXT(isis_area
, area
);
645 int originate_type
= DEFAULT_ORIGINATE
;
647 unsigned long metric
= 0;
648 const char *routemap
= NULL
;
650 family
= str2family(argv
[idx_afi
]->text
);
652 return CMD_WARNING_CONFIG_FAILED
;
654 level
= strmatch("level-1", argv
[idx_level
]->text
) ? 1 : 2;
656 if ((area
->is_type
& level
) != level
) {
657 vty_out(vty
, "Node is not a level-%d IS\n", level
);
658 return CMD_WARNING_CONFIG_FAILED
;
661 if (argc
> idx_always
&& strmatch(argv
[idx_always
]->text
, "always")) {
662 originate_type
= DEFAULT_ORIGINATE_ALWAYS
;
666 if (argc
> idx_metric_rmap
) {
667 if (strmatch(argv
[idx_metric_rmap
]->text
, "metric"))
668 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
,
671 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
674 if (family
== AF_INET6
&& originate_type
!= DEFAULT_ORIGINATE_ALWAYS
) {
676 "Zebra doesn't implement default-originate for IPv6 yet\n");
678 "so use with care or use default-originate always.\n");
681 isis_redist_set(area
, level
, family
, DEFAULT_ROUTE
, metric
, routemap
,
686 DEFUN (no_isis_default_originate
,
687 no_isis_default_originate_cmd
,
688 "no default-information originate <ipv4|ipv6> <level-1|level-2>",
690 "Control distribution of default information\n"
691 "Distribute a default route\n"
692 "Distribute default route for IPv4\n"
693 "Distribute default route for IPv6\n"
694 "Distribute default route into level-1\n"
695 "Distribute default route into level-2\n")
699 VTY_DECLVAR_CONTEXT(isis_area
, area
);
703 family
= str2family(argv
[idx_afi
]->text
);
705 return CMD_WARNING_CONFIG_FAILED
;
707 if (strmatch("level-1", argv
[idx_level
]->text
))
709 else if (strmatch("level-2", argv
[idx_level
]->text
))
712 return CMD_WARNING_CONFIG_FAILED
;
714 isis_redist_unset(area
, level
, family
, DEFAULT_ROUTE
);
718 int isis_redist_config_write(struct vty
*vty
, struct isis_area
*area
,
724 struct isis_redist
*redist
;
725 const char *family_str
;
727 if (family
== AF_INET
)
729 else if (family
== AF_INET6
)
734 for (type
= 0; type
< ZEBRA_ROUTE_MAX
; type
++) {
735 if (type
== ZEBRA_ROUTE_ISIS
)
738 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
739 redist
= get_redist_settings(area
, family
, type
, level
);
742 vty_out(vty
, " redistribute %s %s level-%d", family_str
,
743 zebra_route_string(type
), level
);
745 vty_out(vty
, " metric %u", redist
->metric
);
746 if (redist
->map_name
)
747 vty_out(vty
, " route-map %s", redist
->map_name
);
753 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
755 get_redist_settings(area
, family
, DEFAULT_ROUTE
, level
);
758 vty_out(vty
, " default-information originate %s level-%d",
760 if (redist
->redist
== DEFAULT_ORIGINATE_ALWAYS
)
761 vty_out(vty
, " always");
763 vty_out(vty
, " metric %u", redist
->metric
);
764 if (redist
->map_name
)
765 vty_out(vty
, " route-map %s", redist
->map_name
);
773 void isis_redist_init(void)
775 install_element(ISIS_NODE
, &isis_redistribute_cmd
);
776 install_element(ISIS_NODE
, &no_isis_redistribute_cmd
);
777 install_element(ISIS_NODE
, &isis_default_originate_cmd
);
778 install_element(ISIS_NODE
, &no_isis_default_originate_cmd
);