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 is not initialized.",
104 __func__
, area
->area_tag
);
108 er_node
= srcdest_rnode_get(er_table
, p
, src_p
);
110 route_unlock_node(er_node
);
112 /* Don't update/reschedule lsp generation if nothing changed. */
113 if (!memcmp(er_node
->info
, info
, sizeof(*info
)))
116 er_node
->info
= XMALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(*info
));
119 memcpy(er_node
->info
, info
, sizeof(*info
));
120 lsp_regenerate_schedule(area
, level
, 0);
123 /* Remove external reachability information from a
124 * specific area for a specific level.
125 * Schedule an lsp regenerate if necessary. */
126 static void isis_redist_uninstall(struct isis_area
*area
, int level
,
127 const struct prefix
*p
,
128 const struct prefix_ipv6
*src_p
)
130 int family
= p
->family
;
131 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
132 struct route_node
*er_node
;
136 "%s: External reachability table of area %s is not initialized.",
137 __func__
, area
->area_tag
);
141 er_node
= srcdest_rnode_lookup(er_table
, p
, src_p
);
145 route_unlock_node(er_node
);
150 XFREE(MTYPE_ISIS_EXT_INFO
, er_node
->info
);
151 route_unlock_node(er_node
);
152 lsp_regenerate_schedule(area
, level
, 0);
155 /* Update external reachability info of area for a given level
156 * and prefix, using the given redistribution settings. */
157 static void isis_redist_update_ext_reach(struct isis_area
*area
, int level
,
158 struct isis_redist
*redist
,
159 const struct prefix
*p
,
160 const struct prefix_ipv6
*src_p
,
161 struct isis_ext_info
*info
)
163 struct isis_ext_info area_info
;
164 route_map_result_t map_ret
;
166 memcpy(&area_info
, info
, sizeof(area_info
));
167 area_info
.metric
= redist
->metric
;
169 if (redist
->map_name
) {
171 route_map_apply(redist
->map
, p
, RMAP_ISIS
, &area_info
);
172 if (map_ret
== RMAP_DENYMATCH
)
173 area_info
.distance
= 255;
176 /* Allow synthesized default routes only on always orignate */
177 if (area_info
.origin
== DEFAULT_ROUTE
178 && redist
->redist
!= DEFAULT_ORIGINATE_ALWAYS
)
179 area_info
.distance
= 255;
181 if (area_info
.distance
< 255)
182 isis_redist_install(area
, level
, p
, src_p
, &area_info
);
184 isis_redist_uninstall(area
, level
, p
, src_p
);
187 static void isis_redist_ensure_default(struct isis
*isis
, int family
)
190 struct route_table
*ei_table
= get_ext_info(isis
, family
);
191 struct route_node
*ei_node
;
192 struct isis_ext_info
*info
;
194 if (family
== AF_INET
) {
197 memset(&p
.u
.prefix4
, 0, sizeof(p
.u
.prefix4
));
198 } else if (family
== AF_INET6
) {
201 memset(&p
.u
.prefix6
, 0, sizeof(p
.u
.prefix6
));
203 assert(!"Unknown family!");
205 ei_node
= srcdest_rnode_get(ei_table
, &p
, NULL
);
207 route_unlock_node(ei_node
);
212 XCALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(struct isis_ext_info
));
214 info
= ei_node
->info
;
215 info
->origin
= DEFAULT_ROUTE
;
216 info
->distance
= 254;
217 info
->metric
= MAX_WIDE_PATH_METRIC
;
220 /* Handle notification about route being added */
221 void isis_redist_add(struct isis
*isis
, int type
, struct prefix
*p
,
222 struct prefix_ipv6
*src_p
, uint8_t distance
,
225 int family
= p
->family
;
226 struct route_table
*ei_table
= get_ext_info(isis
, family
);
227 struct route_node
*ei_node
;
228 struct isis_ext_info
*info
;
229 struct listnode
*node
;
230 struct isis_area
*area
;
232 struct isis_redist
*redist
;
234 zlog_debug("%s: New route %pFX from %s: distance %d.", __func__
, p
,
235 zebra_route_string(type
), distance
);
238 zlog_warn("%s: External information table not initialized.",
243 ei_node
= srcdest_rnode_get(ei_table
, p
, src_p
);
245 route_unlock_node(ei_node
);
247 ei_node
->info
= XCALLOC(MTYPE_ISIS_EXT_INFO
,
248 sizeof(struct isis_ext_info
));
250 info
= ei_node
->info
;
252 info
->distance
= distance
;
253 info
->metric
= metric
;
255 if (is_default_prefix(p
)
256 && (!src_p
|| !src_p
->prefixlen
)) {
257 type
= DEFAULT_ROUTE
;
260 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
261 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
262 redist
= get_redist_settings(area
, family
, type
, level
);
266 isis_redist_update_ext_reach(area
, level
, redist
, p
,
271 void isis_redist_delete(struct isis
*isis
, int type
, struct prefix
*p
,
272 struct prefix_ipv6
*src_p
)
274 int family
= p
->family
;
275 struct route_table
*ei_table
= get_ext_info(isis
, family
);
276 struct route_node
*ei_node
;
277 struct listnode
*node
;
278 struct isis_area
*area
;
280 struct isis_redist
*redist
;
282 zlog_debug("%s: Removing route %pFX from %s.", __func__
, p
,
283 zebra_route_string(type
));
285 if (is_default_prefix(p
)
286 && (!src_p
|| !src_p
->prefixlen
)) {
287 /* Don't remove default route but add synthetic route for use
288 * by "default-information originate always". Areas without the
289 * "always" setting will ignore routes with origin
291 isis_redist_add(isis
, DEFAULT_ROUTE
, p
, NULL
, 254,
292 MAX_WIDE_PATH_METRIC
);
297 zlog_warn("%s: External information table not initialized.",
302 ei_node
= srcdest_rnode_lookup(ei_table
, p
, src_p
);
303 if (!ei_node
|| !ei_node
->info
) {
305 "%s: Got a delete for %s route %pFX, but that route was never added.",
306 __func__
, zebra_route_string(type
), p
);
308 route_unlock_node(ei_node
);
311 route_unlock_node(ei_node
);
313 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
314 for (level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
315 redist
= get_redist_settings(area
, family
, type
, level
);
319 isis_redist_uninstall(area
, level
, p
, src_p
);
322 XFREE(MTYPE_ISIS_EXT_INFO
, ei_node
->info
);
323 route_unlock_node(ei_node
);
326 static void isis_redist_routemap_set(struct isis_redist
*redist
,
327 const char *routemap
)
329 if (redist
->map_name
) {
330 XFREE(MTYPE_ISIS
, redist
->map_name
);
331 route_map_counter_decrement(redist
->map
);
335 if (routemap
&& strlen(routemap
)) {
336 redist
->map_name
= XSTRDUP(MTYPE_ISIS
, routemap
);
337 redist
->map
= route_map_lookup_by_name(routemap
);
338 route_map_counter_increment(redist
->map
);
342 static void isis_redist_update_zebra_subscriptions(struct isis
*isis
)
344 struct listnode
*node
;
345 struct isis_area
*area
;
350 char do_subscribe
[REDIST_PROTOCOL_COUNT
][ZEBRA_ROUTE_MAX
+ 1];
352 memset(do_subscribe
, 0, sizeof(do_subscribe
));
354 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
355 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
356 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++)
357 for (level
= 0; level
< ISIS_LEVELS
; level
++)
358 if (area
->redist_settings
[protocol
]
361 do_subscribe
[protocol
][type
] =
364 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
365 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
366 /* This field is actually controlling transmission of
368 * routes to Zebra and has nothing to do with
371 if (type
== PROTO_TYPE
)
374 afi_t afi
= afi_for_redist_protocol(protocol
);
376 if (do_subscribe
[protocol
][type
])
377 isis_zebra_redistribute_set(afi
, type
);
379 isis_zebra_redistribute_unset(afi
, type
);
383 void isis_redist_set(struct isis_area
*area
, int level
, int family
, int type
,
384 uint32_t metric
, const char *routemap
, int originate_type
)
386 int protocol
= redist_protocol(family
);
387 struct isis_redist
*redist
=
388 get_redist_settings(area
, family
, type
, level
);
390 struct route_table
*ei_table
;
391 struct route_node
*rn
;
392 struct isis_ext_info
*info
;
394 redist
->redist
= (type
== DEFAULT_ROUTE
) ? originate_type
: 1;
395 redist
->metric
= metric
;
396 isis_redist_routemap_set(redist
, routemap
);
398 if (!area
->ext_reach
[protocol
][level
- 1]) {
399 area
->ext_reach
[protocol
][level
- 1] = srcdest_table_init();
402 for (i
= 0; i
< REDIST_PROTOCOL_COUNT
; i
++) {
403 if (!area
->isis
->ext_info
[i
]) {
404 area
->isis
->ext_info
[i
] = srcdest_table_init();
408 isis_redist_update_zebra_subscriptions(area
->isis
);
410 if (type
== DEFAULT_ROUTE
&& originate_type
== DEFAULT_ORIGINATE_ALWAYS
)
411 isis_redist_ensure_default(area
->isis
, family
);
413 ei_table
= get_ext_info(area
->isis
, family
);
414 for (rn
= route_top(ei_table
); rn
; rn
= srcdest_route_next(rn
)) {
419 const struct prefix
*p
, *src_p
;
421 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
423 if (type
== DEFAULT_ROUTE
) {
424 if (!is_default_prefix(p
)
425 || (src_p
&& src_p
->prefixlen
)) {
429 if (info
->origin
!= type
)
433 isis_redist_update_ext_reach(area
, level
, redist
, p
,
434 (const struct prefix_ipv6
*)src_p
,
439 void isis_redist_unset(struct isis_area
*area
, int level
, int family
, int type
)
441 struct isis_redist
*redist
=
442 get_redist_settings(area
, family
, type
, level
);
443 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
444 struct route_node
*rn
;
445 struct isis_ext_info
*info
;
452 zlog_warn("%s: External reachability table uninitialized.",
457 for (rn
= route_top(er_table
); rn
; rn
= srcdest_route_next(rn
)) {
462 const struct prefix
*p
, *src_p
;
463 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
465 if (type
== DEFAULT_ROUTE
) {
466 if (!is_default_prefix(p
)
467 || (src_p
&& src_p
->prefixlen
)) {
471 if (info
->origin
!= type
)
475 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
476 route_unlock_node(rn
);
479 lsp_regenerate_schedule(area
, level
, 0);
480 isis_redist_update_zebra_subscriptions(area
->isis
);
483 void isis_redist_area_finish(struct isis_area
*area
)
489 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
490 for (level
= 0; level
< ISIS_LEVELS
; level
++) {
491 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
492 struct isis_redist
*redist
;
494 redist
= &area
->redist_settings
[protocol
][type
]
497 XFREE(MTYPE_ISIS
, redist
->map_name
);
499 route_table_finish(area
->ext_reach
[protocol
][level
]);
502 isis_redist_update_zebra_subscriptions(area
->isis
);
506 DEFUN (isis_redistribute
,
507 isis_redistribute_cmd
,
508 "redistribute <ipv4|ipv6> " PROTO_REDIST_STR
509 " [{metric (0-16777215)|route-map WORD}]",
511 "Redistribute IPv4 routes\n"
512 "Redistribute IPv6 routes\n"
514 "Metric for redistributed routes\n"
515 "ISIS default metric\n"
516 "Route map reference\n"
517 "Pointer to route-map entries\n")
520 int idx_protocol
= 2;
521 int idx_metric_rmap
= 1;
522 VTY_DECLVAR_CONTEXT(isis_area
, area
);
527 unsigned long metric
= 0;
528 const char *routemap
= NULL
;
530 family
= str2family(argv
[idx_afi
]->text
);
532 return CMD_WARNING_CONFIG_FAILED
;
534 afi
= family2afi(family
);
536 return CMD_WARNING_CONFIG_FAILED
;
538 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
540 return CMD_WARNING_CONFIG_FAILED
;
544 if ((area
->is_type
& level
) != level
) {
545 vty_out(vty
, "Node is not a level-%d IS\n", level
);
546 return CMD_WARNING_CONFIG_FAILED
;
549 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
550 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
554 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
555 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
558 isis_redist_set(area
, level
, family
, type
, metric
, routemap
, 0);
562 DEFUN (no_isis_redistribute
,
563 no_isis_redistribute_cmd
,
564 "no redistribute <ipv4|ipv6> " PROTO_REDIST_STR
,
567 "Redistribute IPv4 routes\n"
568 "Redistribute IPv6 routes\n"
572 int idx_protocol
= 3;
573 VTY_DECLVAR_CONTEXT(isis_area
, area
);
579 family
= str2family(argv
[idx_afi
]->arg
);
581 return CMD_WARNING_CONFIG_FAILED
;
583 afi
= family2afi(family
);
585 return CMD_WARNING_CONFIG_FAILED
;
587 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
589 return CMD_WARNING_CONFIG_FAILED
;
593 isis_redist_unset(area
, level
, family
, type
);
597 DEFUN (isis_default_originate
,
598 isis_default_originate_cmd
,
599 "default-information originate <ipv4|ipv6> [always] [{metric (0-16777215)|route-map WORD}]",
600 "Control distribution of default information\n"
601 "Distribute a default route\n"
602 "Distribute default route for IPv4\n"
603 "Distribute default route for IPv6\n"
604 "Always advertise default route\n"
605 "Metric for default route\n"
606 "ISIS default metric\n"
607 "Route map reference\n"
608 "Pointer to route-map entries\n")
611 int idx_always
= fabricd
? 3 : 4;
612 int idx_metric_rmap
= 1;
613 VTY_DECLVAR_CONTEXT(isis_area
, area
);
615 int originate_type
= DEFAULT_ORIGINATE
;
617 unsigned long metric
= 0;
618 const char *routemap
= NULL
;
620 family
= str2family(argv
[idx_afi
]->text
);
622 return CMD_WARNING_CONFIG_FAILED
;
626 if ((area
->is_type
& level
) != level
) {
627 vty_out(vty
, "Node is not a level-%d IS\n", level
);
628 return CMD_WARNING_CONFIG_FAILED
;
631 if (argc
> idx_always
&& strmatch(argv
[idx_always
]->text
, "always")) {
632 originate_type
= DEFAULT_ORIGINATE_ALWAYS
;
636 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
637 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
641 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
642 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
645 if (family
== AF_INET6
&& originate_type
!= DEFAULT_ORIGINATE_ALWAYS
) {
647 "Zebra doesn't implement default-originate for IPv6 yet\n");
649 "so use with care or use default-originate always.\n");
652 isis_redist_set(area
, level
, family
, DEFAULT_ROUTE
, metric
, routemap
,
657 DEFUN (no_isis_default_originate
,
658 no_isis_default_originate_cmd
,
659 "no default-information originate <ipv4|ipv6>",
661 "Control distribution of default information\n"
662 "Distribute a default route\n"
663 "Distribute default route for IPv4\n"
664 "Distribute default route for IPv6\n")
667 VTY_DECLVAR_CONTEXT(isis_area
, area
);
671 family
= str2family(argv
[idx_afi
]->text
);
673 return CMD_WARNING_CONFIG_FAILED
;
677 isis_redist_unset(area
, level
, family
, DEFAULT_ROUTE
);
680 #endif /* ifdef FABRICD */
682 int isis_redist_config_write(struct vty
*vty
, struct isis_area
*area
,
688 struct isis_redist
*redist
;
689 const char *family_str
;
691 if (family
== AF_INET
)
693 else if (family
== AF_INET6
)
698 for (type
= 0; type
< ZEBRA_ROUTE_MAX
; type
++) {
699 if (type
== PROTO_TYPE
)
702 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
703 redist
= get_redist_settings(area
, family
, type
, level
);
706 vty_out(vty
, " redistribute %s %s", family_str
,
707 zebra_route_string(type
));
709 vty_out(vty
, " level-%d", level
);
711 vty_out(vty
, " metric %u", redist
->metric
);
712 if (redist
->map_name
)
713 vty_out(vty
, " route-map %s", redist
->map_name
);
719 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
721 get_redist_settings(area
, family
, DEFAULT_ROUTE
, level
);
724 vty_out(vty
, " default-information originate %s",
727 vty_out(vty
, " level-%d", level
);
728 if (redist
->redist
== DEFAULT_ORIGINATE_ALWAYS
)
729 vty_out(vty
, " always");
731 vty_out(vty
, " metric %u", redist
->metric
);
732 if (redist
->map_name
)
733 vty_out(vty
, " route-map %s", redist
->map_name
);
741 void isis_redist_init(void)
744 install_element(ROUTER_NODE
, &isis_redistribute_cmd
);
745 install_element(ROUTER_NODE
, &no_isis_redistribute_cmd
);
747 install_element(ROUTER_NODE
, &isis_default_originate_cmd
);
748 install_element(ROUTER_NODE
, &no_isis_default_originate_cmd
);
749 #endif /* ifdef FABRICD */