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 char debug_buf
[BUFSIZ
];
235 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
237 zlog_debug("%s: New route %s from %s: distance %d.", __func__
,
238 debug_buf
, zebra_route_string(type
), distance
);
241 zlog_warn("%s: External information table not initialized.",
246 ei_node
= srcdest_rnode_get(ei_table
, p
, src_p
);
248 route_unlock_node(ei_node
);
250 ei_node
->info
= XCALLOC(MTYPE_ISIS_EXT_INFO
,
251 sizeof(struct isis_ext_info
));
253 info
= ei_node
->info
;
255 info
->distance
= distance
;
256 info
->metric
= metric
;
258 if (is_default_prefix(p
)
259 && (!src_p
|| !src_p
->prefixlen
)) {
260 type
= DEFAULT_ROUTE
;
263 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
264 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
265 redist
= get_redist_settings(area
, family
, type
, level
);
269 isis_redist_update_ext_reach(area
, level
, redist
, p
,
274 void isis_redist_delete(struct isis
*isis
, int type
, struct prefix
*p
,
275 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(isis
, DEFAULT_ROUTE
, p
, NULL
, 254,
298 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 was never added.",
314 __func__
, zebra_route_string(type
), buf
);
316 route_unlock_node(ei_node
);
319 route_unlock_node(ei_node
);
321 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
322 for (level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
323 redist
= get_redist_settings(area
, family
, type
, level
);
327 isis_redist_uninstall(area
, level
, p
, src_p
);
330 XFREE(MTYPE_ISIS_EXT_INFO
, ei_node
->info
);
331 route_unlock_node(ei_node
);
334 static void isis_redist_routemap_set(struct isis_redist
*redist
,
335 const char *routemap
)
337 if (redist
->map_name
) {
338 XFREE(MTYPE_ISIS
, redist
->map_name
);
339 route_map_counter_decrement(redist
->map
);
343 if (routemap
&& strlen(routemap
)) {
344 redist
->map_name
= XSTRDUP(MTYPE_ISIS
, routemap
);
345 redist
->map
= route_map_lookup_by_name(routemap
);
346 route_map_counter_increment(redist
->map
);
350 static void isis_redist_update_zebra_subscriptions(struct isis
*isis
)
352 struct listnode
*node
;
353 struct isis_area
*area
;
358 char do_subscribe
[REDIST_PROTOCOL_COUNT
][ZEBRA_ROUTE_MAX
+ 1];
360 memset(do_subscribe
, 0, sizeof(do_subscribe
));
362 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
363 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
364 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++)
365 for (level
= 0; level
< ISIS_LEVELS
; level
++)
366 if (area
->redist_settings
[protocol
]
369 do_subscribe
[protocol
][type
] =
372 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
373 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
374 /* This field is actually controlling transmission of
376 * routes to Zebra and has nothing to do with
379 if (type
== PROTO_TYPE
)
382 afi_t afi
= afi_for_redist_protocol(protocol
);
384 if (do_subscribe
[protocol
][type
])
385 isis_zebra_redistribute_set(afi
, type
);
387 isis_zebra_redistribute_unset(afi
, type
);
391 void isis_redist_set(struct isis_area
*area
, int level
, int family
, int type
,
392 uint32_t metric
, const char *routemap
, int originate_type
)
394 int protocol
= redist_protocol(family
);
395 struct isis_redist
*redist
=
396 get_redist_settings(area
, family
, type
, level
);
398 struct route_table
*ei_table
;
399 struct route_node
*rn
;
400 struct isis_ext_info
*info
;
402 redist
->redist
= (type
== DEFAULT_ROUTE
) ? originate_type
: 1;
403 redist
->metric
= metric
;
404 isis_redist_routemap_set(redist
, routemap
);
406 if (!area
->ext_reach
[protocol
][level
- 1]) {
407 area
->ext_reach
[protocol
][level
- 1] = srcdest_table_init();
410 for (i
= 0; i
< REDIST_PROTOCOL_COUNT
; i
++) {
411 if (!area
->isis
->ext_info
[i
]) {
412 area
->isis
->ext_info
[i
] = srcdest_table_init();
416 isis_redist_update_zebra_subscriptions(area
->isis
);
418 if (type
== DEFAULT_ROUTE
&& originate_type
== DEFAULT_ORIGINATE_ALWAYS
)
419 isis_redist_ensure_default(area
->isis
, family
);
421 ei_table
= get_ext_info(area
->isis
, family
);
422 for (rn
= route_top(ei_table
); rn
; rn
= srcdest_route_next(rn
)) {
427 const struct prefix
*p
, *src_p
;
429 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
431 if (type
== DEFAULT_ROUTE
) {
432 if (!is_default_prefix(p
)
433 || (src_p
&& src_p
->prefixlen
)) {
437 if (info
->origin
!= type
)
441 isis_redist_update_ext_reach(area
, level
, redist
, p
,
442 (const struct prefix_ipv6
*)src_p
,
447 void isis_redist_unset(struct isis_area
*area
, int level
, int family
, int type
)
449 struct isis_redist
*redist
=
450 get_redist_settings(area
, family
, type
, level
);
451 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
452 struct route_node
*rn
;
453 struct isis_ext_info
*info
;
460 zlog_warn("%s: External reachability table uninitialized.",
465 for (rn
= route_top(er_table
); rn
; rn
= srcdest_route_next(rn
)) {
470 const struct prefix
*p
, *src_p
;
471 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
473 if (type
== DEFAULT_ROUTE
) {
474 if (!is_default_prefix(p
)
475 || (src_p
&& src_p
->prefixlen
)) {
479 if (info
->origin
!= type
)
483 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
484 route_unlock_node(rn
);
487 lsp_regenerate_schedule(area
, level
, 0);
488 isis_redist_update_zebra_subscriptions(area
->isis
);
491 void isis_redist_area_finish(struct isis_area
*area
)
497 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
498 for (level
= 0; level
< ISIS_LEVELS
; level
++) {
499 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
500 struct isis_redist
*redist
;
502 redist
= &area
->redist_settings
[protocol
][type
]
505 XFREE(MTYPE_ISIS
, redist
->map_name
);
507 route_table_finish(area
->ext_reach
[protocol
][level
]);
510 isis_redist_update_zebra_subscriptions(area
->isis
);
514 DEFUN (isis_redistribute
,
515 isis_redistribute_cmd
,
516 "redistribute <ipv4|ipv6> " PROTO_REDIST_STR
517 " [{metric (0-16777215)|route-map WORD}]",
519 "Redistribute IPv4 routes\n"
520 "Redistribute IPv6 routes\n"
522 "Metric for redistributed routes\n"
523 "ISIS default metric\n"
524 "Route map reference\n"
525 "Pointer to route-map entries\n")
528 int idx_protocol
= 2;
529 int idx_metric_rmap
= 1;
530 VTY_DECLVAR_CONTEXT(isis_area
, area
);
535 unsigned long metric
= 0;
536 const char *routemap
= NULL
;
538 family
= str2family(argv
[idx_afi
]->text
);
540 return CMD_WARNING_CONFIG_FAILED
;
542 afi
= family2afi(family
);
544 return CMD_WARNING_CONFIG_FAILED
;
546 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
548 return CMD_WARNING_CONFIG_FAILED
;
552 if ((area
->is_type
& level
) != level
) {
553 vty_out(vty
, "Node is not a level-%d IS\n", level
);
554 return CMD_WARNING_CONFIG_FAILED
;
557 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
558 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
562 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
563 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
566 isis_redist_set(area
, level
, family
, type
, metric
, routemap
, 0);
570 DEFUN (no_isis_redistribute
,
571 no_isis_redistribute_cmd
,
572 "no redistribute <ipv4|ipv6> " PROTO_REDIST_STR
,
575 "Redistribute IPv4 routes\n"
576 "Redistribute IPv6 routes\n"
580 int idx_protocol
= 3;
581 VTY_DECLVAR_CONTEXT(isis_area
, area
);
587 family
= str2family(argv
[idx_afi
]->arg
);
589 return CMD_WARNING_CONFIG_FAILED
;
591 afi
= family2afi(family
);
593 return CMD_WARNING_CONFIG_FAILED
;
595 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
597 return CMD_WARNING_CONFIG_FAILED
;
601 isis_redist_unset(area
, level
, family
, type
);
605 DEFUN (isis_default_originate
,
606 isis_default_originate_cmd
,
607 "default-information originate <ipv4|ipv6> [always] [{metric (0-16777215)|route-map WORD}]",
608 "Control distribution of default information\n"
609 "Distribute a default route\n"
610 "Distribute default route for IPv4\n"
611 "Distribute default route for IPv6\n"
612 "Always advertise default route\n"
613 "Metric for default route\n"
614 "ISIS default metric\n"
615 "Route map reference\n"
616 "Pointer to route-map entries\n")
619 int idx_always
= fabricd
? 3 : 4;
620 int idx_metric_rmap
= 1;
621 VTY_DECLVAR_CONTEXT(isis_area
, area
);
623 int originate_type
= DEFAULT_ORIGINATE
;
625 unsigned long metric
= 0;
626 const char *routemap
= NULL
;
628 family
= str2family(argv
[idx_afi
]->text
);
630 return CMD_WARNING_CONFIG_FAILED
;
634 if ((area
->is_type
& level
) != level
) {
635 vty_out(vty
, "Node is not a level-%d IS\n", level
);
636 return CMD_WARNING_CONFIG_FAILED
;
639 if (argc
> idx_always
&& strmatch(argv
[idx_always
]->text
, "always")) {
640 originate_type
= DEFAULT_ORIGINATE_ALWAYS
;
644 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
645 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
649 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
650 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
653 if (family
== AF_INET6
&& originate_type
!= DEFAULT_ORIGINATE_ALWAYS
) {
655 "Zebra doesn't implement default-originate for IPv6 yet\n");
657 "so use with care or use default-originate always.\n");
660 isis_redist_set(area
, level
, family
, DEFAULT_ROUTE
, metric
, routemap
,
665 DEFUN (no_isis_default_originate
,
666 no_isis_default_originate_cmd
,
667 "no default-information originate <ipv4|ipv6>",
669 "Control distribution of default information\n"
670 "Distribute a default route\n"
671 "Distribute default route for IPv4\n"
672 "Distribute default route for IPv6\n")
675 VTY_DECLVAR_CONTEXT(isis_area
, area
);
679 family
= str2family(argv
[idx_afi
]->text
);
681 return CMD_WARNING_CONFIG_FAILED
;
685 isis_redist_unset(area
, level
, family
, DEFAULT_ROUTE
);
688 #endif /* ifdef FABRICD */
690 int isis_redist_config_write(struct vty
*vty
, struct isis_area
*area
,
696 struct isis_redist
*redist
;
697 const char *family_str
;
699 if (family
== AF_INET
)
701 else if (family
== AF_INET6
)
706 for (type
= 0; type
< ZEBRA_ROUTE_MAX
; type
++) {
707 if (type
== PROTO_TYPE
)
710 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
711 redist
= get_redist_settings(area
, family
, type
, level
);
714 vty_out(vty
, " redistribute %s %s", family_str
,
715 zebra_route_string(type
));
717 vty_out(vty
, " level-%d", level
);
719 vty_out(vty
, " metric %u", redist
->metric
);
720 if (redist
->map_name
)
721 vty_out(vty
, " route-map %s", redist
->map_name
);
727 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
729 get_redist_settings(area
, family
, DEFAULT_ROUTE
, level
);
732 vty_out(vty
, " default-information originate %s",
735 vty_out(vty
, " level-%d", level
);
736 if (redist
->redist
== DEFAULT_ORIGINATE_ALWAYS
)
737 vty_out(vty
, " always");
739 vty_out(vty
, " metric %u", redist
->metric
);
740 if (redist
->map_name
)
741 vty_out(vty
, " route-map %s", redist
->map_name
);
749 void isis_redist_init(void)
752 install_element(ROUTER_NODE
, &isis_redistribute_cmd
);
753 install_element(ROUTER_NODE
, &no_isis_redistribute_cmd
);
755 install_element(ROUTER_NODE
, &isis_default_originate_cmd
);
756 install_element(ROUTER_NODE
, &no_isis_default_originate_cmd
);
757 #endif /* ifdef FABRICD */