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/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/isisd.h"
41 #include "isisd/isis_lsp.h"
42 #include "isisd/isis_route.h"
43 #include "isisd/isis_zebra.h"
45 static int redist_protocol(int family
)
47 if (family
== AF_INET
)
49 if (family
== AF_INET6
)
52 assert(!"Unsupported address family!");
56 static afi_t
afi_for_redist_protocol(int protocol
)
63 assert(!"Unknown redist protocol!");
67 static struct route_table
*get_ext_info(struct isis
*i
, int family
)
69 int protocol
= redist_protocol(family
);
71 return i
->ext_info
[protocol
];
74 static struct isis_redist
*get_redist_settings(struct isis_area
*area
,
75 int family
, int type
, int level
)
77 int protocol
= redist_protocol(family
);
79 return &area
->redist_settings
[protocol
][type
][level
- 1];
82 struct route_table
*get_ext_reach(struct isis_area
*area
, int family
, int level
)
84 int protocol
= redist_protocol(family
);
86 return area
->ext_reach
[protocol
][level
- 1];
89 /* Install external reachability information into a
90 * specific area for a specific level.
91 * Schedule an lsp regenerate if necessary */
92 static void isis_redist_install(struct isis_area
*area
, int level
,
93 const struct prefix
*p
,
94 const struct prefix_ipv6
*src_p
,
95 struct isis_ext_info
*info
)
97 int family
= p
->family
;
98 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
99 struct route_node
*er_node
;
103 "%s: External reachability table of area %s"
104 " is not initialized.",
105 __func__
, area
->area_tag
);
109 er_node
= srcdest_rnode_get(er_table
, p
, src_p
);
111 route_unlock_node(er_node
);
113 /* Don't update/reschedule lsp generation if nothing changed. */
114 if (!memcmp(er_node
->info
, info
, sizeof(*info
)))
117 er_node
->info
= XMALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(*info
));
120 memcpy(er_node
->info
, info
, sizeof(*info
));
121 lsp_regenerate_schedule(area
, level
, 0);
124 /* Remove external reachability information from a
125 * specific area for a specific level.
126 * Schedule an lsp regenerate if necessary. */
127 static void isis_redist_uninstall(struct isis_area
*area
, int level
,
128 const struct prefix
*p
,
129 const struct prefix_ipv6
*src_p
)
131 int family
= p
->family
;
132 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
133 struct route_node
*er_node
;
137 "%s: External reachability table of area %s"
138 " is not initialized.",
139 __func__
, area
->area_tag
);
143 er_node
= srcdest_rnode_lookup(er_table
, p
, src_p
);
147 route_unlock_node(er_node
);
152 XFREE(MTYPE_ISIS_EXT_INFO
, er_node
->info
);
153 route_unlock_node(er_node
);
154 lsp_regenerate_schedule(area
, level
, 0);
157 /* Update external reachability info of area for a given level
158 * and prefix, using the given redistribution settings. */
159 static void isis_redist_update_ext_reach(struct isis_area
*area
, int level
,
160 struct isis_redist
*redist
,
161 const struct prefix
*p
,
162 const struct prefix_ipv6
*src_p
,
163 struct isis_ext_info
*info
)
165 struct isis_ext_info area_info
;
166 route_map_result_t map_ret
;
168 memcpy(&area_info
, info
, sizeof(area_info
));
169 area_info
.metric
= redist
->metric
;
171 if (redist
->map_name
) {
173 route_map_apply(redist
->map
, p
, RMAP_ISIS
, &area_info
);
174 if (map_ret
== RMAP_DENYMATCH
)
175 area_info
.distance
= 255;
178 /* Allow synthesized default routes only on always orignate */
179 if (area_info
.origin
== DEFAULT_ROUTE
180 && redist
->redist
!= DEFAULT_ORIGINATE_ALWAYS
)
181 area_info
.distance
= 255;
183 if (area_info
.distance
< 255)
184 isis_redist_install(area
, level
, p
, src_p
, &area_info
);
186 isis_redist_uninstall(area
, level
, p
, src_p
);
189 static void isis_redist_ensure_default(struct isis
*isis
, int family
)
192 struct route_table
*ei_table
= get_ext_info(isis
, family
);
193 struct route_node
*ei_node
;
194 struct isis_ext_info
*info
;
196 if (family
== AF_INET
) {
199 memset(&p
.u
.prefix4
, 0, sizeof(p
.u
.prefix4
));
200 } else if (family
== AF_INET6
) {
203 memset(&p
.u
.prefix6
, 0, sizeof(p
.u
.prefix6
));
205 assert(!"Unknown family!");
207 ei_node
= srcdest_rnode_get(ei_table
, &p
, NULL
);
209 route_unlock_node(ei_node
);
214 XCALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(struct isis_ext_info
));
216 info
= ei_node
->info
;
217 info
->origin
= DEFAULT_ROUTE
;
218 info
->distance
= 254;
219 info
->metric
= MAX_WIDE_PATH_METRIC
;
222 /* Handle notification about route being added */
223 void isis_redist_add(int type
, struct prefix
*p
, struct prefix_ipv6
*src_p
,
224 uint8_t distance
, uint32_t metric
)
226 int family
= p
->family
;
227 struct route_table
*ei_table
= get_ext_info(isis
, family
);
228 struct route_node
*ei_node
;
229 struct isis_ext_info
*info
;
230 struct listnode
*node
;
231 struct isis_area
*area
;
233 struct isis_redist
*redist
;
235 char debug_buf
[BUFSIZ
];
236 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
238 zlog_debug("%s: New route %s from %s: distance %d.", __func__
,
239 debug_buf
, zebra_route_string(type
), distance
);
242 zlog_warn("%s: External information table not initialized.",
247 ei_node
= srcdest_rnode_get(ei_table
, p
, src_p
);
249 route_unlock_node(ei_node
);
251 ei_node
->info
= XCALLOC(MTYPE_ISIS_EXT_INFO
,
252 sizeof(struct isis_ext_info
));
254 info
= ei_node
->info
;
256 info
->distance
= distance
;
257 info
->metric
= metric
;
259 if (is_default_prefix(p
)
260 && (!src_p
|| !src_p
->prefixlen
)) {
261 type
= DEFAULT_ROUTE
;
264 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
265 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
266 redist
= get_redist_settings(area
, family
, type
, level
);
270 isis_redist_update_ext_reach(area
, level
, redist
, p
,
275 void isis_redist_delete(int type
, struct prefix
*p
, struct prefix_ipv6
*src_p
)
277 int family
= p
->family
;
278 struct route_table
*ei_table
= get_ext_info(isis
, family
);
279 struct route_node
*ei_node
;
280 struct listnode
*node
;
281 struct isis_area
*area
;
283 struct isis_redist
*redist
;
285 char debug_buf
[BUFSIZ
];
286 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
288 zlog_debug("%s: Removing route %s from %s.", __func__
, debug_buf
,
289 zebra_route_string(type
));
291 if (is_default_prefix(p
)
292 && (!src_p
|| !src_p
->prefixlen
)) {
293 /* Don't remove default route but add synthetic route for use
294 * by "default-information originate always". Areas without the
295 * "always" setting will ignore routes with origin
297 isis_redist_add(DEFAULT_ROUTE
, p
, NULL
,
298 254, MAX_WIDE_PATH_METRIC
);
303 zlog_warn("%s: External information table not initialized.",
308 ei_node
= srcdest_rnode_lookup(ei_table
, p
, src_p
);
309 if (!ei_node
|| !ei_node
->info
) {
311 prefix2str(p
, buf
, sizeof(buf
));
313 "%s: Got a delete for %s route %s, but that route"
315 __func__
, zebra_route_string(type
), buf
);
317 route_unlock_node(ei_node
);
320 route_unlock_node(ei_node
);
322 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
323 for (level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
324 redist
= get_redist_settings(area
, family
, type
, level
);
328 isis_redist_uninstall(area
, level
, p
, src_p
);
331 XFREE(MTYPE_ISIS_EXT_INFO
, ei_node
->info
);
332 route_unlock_node(ei_node
);
335 static void isis_redist_routemap_set(struct isis_redist
*redist
,
336 const char *routemap
)
338 if (redist
->map_name
) {
339 XFREE(MTYPE_ISIS
, redist
->map_name
);
340 route_map_counter_decrement(redist
->map
);
344 if (routemap
&& strlen(routemap
)) {
345 redist
->map_name
= XSTRDUP(MTYPE_ISIS
, routemap
);
346 redist
->map
= route_map_lookup_by_name(routemap
);
347 route_map_counter_increment(redist
->map
);
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
== PROTO_TYPE
)
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 void isis_redist_set(struct isis_area
*area
, int level
, int family
, int type
,
393 uint32_t metric
, const char *routemap
, int originate_type
)
395 int protocol
= redist_protocol(family
);
396 struct isis_redist
*redist
=
397 get_redist_settings(area
, family
, type
, level
);
399 struct route_table
*ei_table
;
400 struct route_node
*rn
;
401 struct isis_ext_info
*info
;
403 redist
->redist
= (type
== DEFAULT_ROUTE
) ? originate_type
: 1;
404 redist
->metric
= metric
;
405 isis_redist_routemap_set(redist
, routemap
);
407 if (!area
->ext_reach
[protocol
][level
- 1]) {
408 area
->ext_reach
[protocol
][level
- 1] = srcdest_table_init();
411 for (i
= 0; i
< REDIST_PROTOCOL_COUNT
; i
++) {
412 if (!area
->isis
->ext_info
[i
]) {
413 area
->isis
->ext_info
[i
] = srcdest_table_init();
417 isis_redist_update_zebra_subscriptions(area
->isis
);
419 if (type
== DEFAULT_ROUTE
&& originate_type
== DEFAULT_ORIGINATE_ALWAYS
)
420 isis_redist_ensure_default(area
->isis
, family
);
422 ei_table
= get_ext_info(area
->isis
, family
);
423 for (rn
= route_top(ei_table
); rn
; rn
= srcdest_route_next(rn
)) {
428 const struct prefix
*p
, *src_p
;
430 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
432 if (type
== DEFAULT_ROUTE
) {
433 if (!is_default_prefix(p
)
434 || (src_p
&& src_p
->prefixlen
)) {
438 if (info
->origin
!= type
)
442 isis_redist_update_ext_reach(area
, level
, redist
, p
,
443 (const struct prefix_ipv6
*)src_p
,
448 void isis_redist_unset(struct isis_area
*area
, int level
, int family
, int type
)
450 struct isis_redist
*redist
=
451 get_redist_settings(area
, family
, type
, level
);
452 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
453 struct route_node
*rn
;
454 struct isis_ext_info
*info
;
461 zlog_warn("%s: External reachability table uninitialized.",
466 for (rn
= route_top(er_table
); rn
; rn
= srcdest_route_next(rn
)) {
471 const struct prefix
*p
, *src_p
;
472 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
474 if (type
== DEFAULT_ROUTE
) {
475 if (!is_default_prefix(p
)
476 || (src_p
&& src_p
->prefixlen
)) {
480 if (info
->origin
!= type
)
484 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
485 route_unlock_node(rn
);
488 lsp_regenerate_schedule(area
, level
, 0);
489 isis_redist_update_zebra_subscriptions(area
->isis
);
492 void isis_redist_area_finish(struct isis_area
*area
)
498 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
499 for (level
= 0; level
< ISIS_LEVELS
; level
++) {
500 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
501 struct isis_redist
*redist
;
503 redist
= &area
->redist_settings
[protocol
][type
]
506 XFREE(MTYPE_ISIS
, redist
->map_name
);
508 route_table_finish(area
->ext_reach
[protocol
][level
]);
511 isis_redist_update_zebra_subscriptions(area
->isis
);
515 DEFUN (isis_redistribute
,
516 isis_redistribute_cmd
,
517 "redistribute <ipv4|ipv6> " PROTO_REDIST_STR
518 " [{metric (0-16777215)|route-map WORD}]",
520 "Redistribute IPv4 routes\n"
521 "Redistribute IPv6 routes\n"
523 "Metric for redistributed routes\n"
524 "ISIS default metric\n"
525 "Route map reference\n"
526 "Pointer to route-map entries\n")
529 int idx_protocol
= 2;
530 int idx_metric_rmap
= 1;
531 VTY_DECLVAR_CONTEXT(isis_area
, area
);
536 unsigned long metric
= 0;
537 const char *routemap
= NULL
;
539 family
= str2family(argv
[idx_afi
]->text
);
541 return CMD_WARNING_CONFIG_FAILED
;
543 afi
= family2afi(family
);
545 return CMD_WARNING_CONFIG_FAILED
;
547 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
549 return CMD_WARNING_CONFIG_FAILED
;
553 if ((area
->is_type
& level
) != level
) {
554 vty_out(vty
, "Node is not a level-%d IS\n", level
);
555 return CMD_WARNING_CONFIG_FAILED
;
558 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
559 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
563 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
564 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
567 isis_redist_set(area
, level
, family
, type
, metric
, routemap
, 0);
571 DEFUN (no_isis_redistribute
,
572 no_isis_redistribute_cmd
,
573 "no redistribute <ipv4|ipv6> " PROTO_REDIST_STR
,
576 "Redistribute IPv4 routes\n"
577 "Redistribute IPv6 routes\n"
581 int idx_protocol
= 3;
582 VTY_DECLVAR_CONTEXT(isis_area
, area
);
588 family
= str2family(argv
[idx_afi
]->arg
);
590 return CMD_WARNING_CONFIG_FAILED
;
592 afi
= family2afi(family
);
594 return CMD_WARNING_CONFIG_FAILED
;
596 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
598 return CMD_WARNING_CONFIG_FAILED
;
602 isis_redist_unset(area
, level
, family
, type
);
606 DEFUN (isis_default_originate
,
607 isis_default_originate_cmd
,
608 "default-information originate <ipv4|ipv6>"
609 " [always] [{metric (0-16777215)|route-map WORD}]",
610 "Control distribution of default information\n"
611 "Distribute a default route\n"
612 "Distribute default route for IPv4\n"
613 "Distribute default route for IPv6\n"
614 "Always advertise default route\n"
615 "Metric for default route\n"
616 "ISIS default metric\n"
617 "Route map reference\n"
618 "Pointer to route-map entries\n")
621 int idx_always
= fabricd
? 3 : 4;
622 int idx_metric_rmap
= 1;
623 VTY_DECLVAR_CONTEXT(isis_area
, area
);
625 int originate_type
= DEFAULT_ORIGINATE
;
627 unsigned long metric
= 0;
628 const char *routemap
= NULL
;
630 family
= str2family(argv
[idx_afi
]->text
);
632 return CMD_WARNING_CONFIG_FAILED
;
636 if ((area
->is_type
& level
) != level
) {
637 vty_out(vty
, "Node is not a level-%d IS\n", level
);
638 return CMD_WARNING_CONFIG_FAILED
;
641 if (argc
> idx_always
&& strmatch(argv
[idx_always
]->text
, "always")) {
642 originate_type
= DEFAULT_ORIGINATE_ALWAYS
;
646 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
647 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
651 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
652 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
655 if (family
== AF_INET6
&& originate_type
!= DEFAULT_ORIGINATE_ALWAYS
) {
657 "Zebra doesn't implement default-originate for IPv6 yet\n");
659 "so use with care or use default-originate always.\n");
662 isis_redist_set(area
, level
, family
, DEFAULT_ROUTE
, metric
, routemap
,
667 DEFUN (no_isis_default_originate
,
668 no_isis_default_originate_cmd
,
669 "no default-information originate <ipv4|ipv6>",
671 "Control distribution of default information\n"
672 "Distribute a default route\n"
673 "Distribute default route for IPv4\n"
674 "Distribute default route for IPv6\n")
677 VTY_DECLVAR_CONTEXT(isis_area
, area
);
681 family
= str2family(argv
[idx_afi
]->text
);
683 return CMD_WARNING_CONFIG_FAILED
;
687 isis_redist_unset(area
, level
, family
, DEFAULT_ROUTE
);
690 #endif /* ifdef FABRICD */
692 int isis_redist_config_write(struct vty
*vty
, struct isis_area
*area
,
698 struct isis_redist
*redist
;
699 const char *family_str
;
701 if (family
== AF_INET
)
703 else if (family
== AF_INET6
)
708 for (type
= 0; type
< ZEBRA_ROUTE_MAX
; type
++) {
709 if (type
== PROTO_TYPE
)
712 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
713 redist
= get_redist_settings(area
, family
, type
, level
);
716 vty_out(vty
, " redistribute %s %s", family_str
,
717 zebra_route_string(type
));
719 vty_out(vty
, " level-%d", level
);
721 vty_out(vty
, " metric %u", redist
->metric
);
722 if (redist
->map_name
)
723 vty_out(vty
, " route-map %s", redist
->map_name
);
729 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
731 get_redist_settings(area
, family
, DEFAULT_ROUTE
, level
);
734 vty_out(vty
, " default-information originate %s",
737 vty_out(vty
, " level-%d", level
);
738 if (redist
->redist
== DEFAULT_ORIGINATE_ALWAYS
)
739 vty_out(vty
, " always");
741 vty_out(vty
, " metric %u", redist
->metric
);
742 if (redist
->map_name
)
743 vty_out(vty
, " route-map %s", redist
->map_name
);
751 void isis_redist_init(void)
754 install_element(ROUTER_NODE
, &isis_redistribute_cmd
);
755 install_element(ROUTER_NODE
, &no_isis_redistribute_cmd
);
757 install_element(ROUTER_NODE
, &isis_default_originate_cmd
);
758 install_element(ROUTER_NODE
, &no_isis_default_originate_cmd
);
759 #endif /* ifdef FABRICD */