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(int type
, struct prefix
*p
, struct prefix_ipv6
*src_p
,
222 uint8_t distance
, uint32_t metric
)
224 int family
= p
->family
;
225 struct route_table
*ei_table
= get_ext_info(isis
, family
);
226 struct route_node
*ei_node
;
227 struct isis_ext_info
*info
;
228 struct listnode
*node
;
229 struct isis_area
*area
;
231 struct isis_redist
*redist
;
233 char debug_buf
[BUFSIZ
];
234 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
236 zlog_debug("%s: New route %s from %s: distance %d.", __func__
,
237 debug_buf
, zebra_route_string(type
), distance
);
240 zlog_warn("%s: External information table not initialized.",
245 ei_node
= srcdest_rnode_get(ei_table
, p
, src_p
);
247 route_unlock_node(ei_node
);
249 ei_node
->info
= XCALLOC(MTYPE_ISIS_EXT_INFO
,
250 sizeof(struct isis_ext_info
));
252 info
= ei_node
->info
;
254 info
->distance
= distance
;
255 info
->metric
= metric
;
257 if (is_default_prefix(p
)
258 && (!src_p
|| !src_p
->prefixlen
)) {
259 type
= DEFAULT_ROUTE
;
262 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
263 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
264 redist
= get_redist_settings(area
, family
, type
, level
);
268 isis_redist_update_ext_reach(area
, level
, redist
, p
,
273 void isis_redist_delete(int type
, struct prefix
*p
, struct prefix_ipv6
*src_p
)
275 int family
= p
->family
;
276 struct route_table
*ei_table
= get_ext_info(isis
, family
);
277 struct route_node
*ei_node
;
278 struct listnode
*node
;
279 struct isis_area
*area
;
281 struct isis_redist
*redist
;
283 char debug_buf
[BUFSIZ
];
284 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
286 zlog_debug("%s: Removing route %s from %s.", __func__
, debug_buf
,
287 zebra_route_string(type
));
289 if (is_default_prefix(p
)
290 && (!src_p
|| !src_p
->prefixlen
)) {
291 /* Don't remove default route but add synthetic route for use
292 * by "default-information originate always". Areas without the
293 * "always" setting will ignore routes with origin
295 isis_redist_add(DEFAULT_ROUTE
, p
, NULL
,
296 254, MAX_WIDE_PATH_METRIC
);
301 zlog_warn("%s: External information table not initialized.",
306 ei_node
= srcdest_rnode_lookup(ei_table
, p
, src_p
);
307 if (!ei_node
|| !ei_node
->info
) {
309 prefix2str(p
, buf
, sizeof(buf
));
311 "%s: Got a delete for %s route %s, but that route was never added.",
312 __func__
, zebra_route_string(type
), buf
);
314 route_unlock_node(ei_node
);
317 route_unlock_node(ei_node
);
319 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
320 for (level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
321 redist
= get_redist_settings(area
, family
, type
, level
);
325 isis_redist_uninstall(area
, level
, p
, src_p
);
328 XFREE(MTYPE_ISIS_EXT_INFO
, ei_node
->info
);
329 route_unlock_node(ei_node
);
332 static void isis_redist_routemap_set(struct isis_redist
*redist
,
333 const char *routemap
)
335 if (redist
->map_name
) {
336 XFREE(MTYPE_ISIS
, redist
->map_name
);
337 route_map_counter_decrement(redist
->map
);
341 if (routemap
&& strlen(routemap
)) {
342 redist
->map_name
= XSTRDUP(MTYPE_ISIS
, routemap
);
343 redist
->map
= route_map_lookup_by_name(routemap
);
344 route_map_counter_increment(redist
->map
);
348 static void isis_redist_update_zebra_subscriptions(struct isis
*isis
)
350 struct listnode
*node
;
351 struct isis_area
*area
;
356 char do_subscribe
[REDIST_PROTOCOL_COUNT
][ZEBRA_ROUTE_MAX
+ 1];
358 memset(do_subscribe
, 0, sizeof(do_subscribe
));
360 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
361 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
362 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++)
363 for (level
= 0; level
< ISIS_LEVELS
; level
++)
364 if (area
->redist_settings
[protocol
]
367 do_subscribe
[protocol
][type
] =
370 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
371 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
372 /* This field is actually controlling transmission of
374 * routes to Zebra and has nothing to do with
377 if (type
== PROTO_TYPE
)
380 afi_t afi
= afi_for_redist_protocol(protocol
);
382 if (do_subscribe
[protocol
][type
])
383 isis_zebra_redistribute_set(afi
, type
);
385 isis_zebra_redistribute_unset(afi
, type
);
389 void isis_redist_set(struct isis_area
*area
, int level
, int family
, int type
,
390 uint32_t metric
, const char *routemap
, int originate_type
)
392 int protocol
= redist_protocol(family
);
393 struct isis_redist
*redist
=
394 get_redist_settings(area
, family
, type
, level
);
396 struct route_table
*ei_table
;
397 struct route_node
*rn
;
398 struct isis_ext_info
*info
;
400 redist
->redist
= (type
== DEFAULT_ROUTE
) ? originate_type
: 1;
401 redist
->metric
= metric
;
402 isis_redist_routemap_set(redist
, routemap
);
404 if (!area
->ext_reach
[protocol
][level
- 1]) {
405 area
->ext_reach
[protocol
][level
- 1] = srcdest_table_init();
408 for (i
= 0; i
< REDIST_PROTOCOL_COUNT
; i
++) {
409 if (!area
->isis
->ext_info
[i
]) {
410 area
->isis
->ext_info
[i
] = srcdest_table_init();
414 isis_redist_update_zebra_subscriptions(area
->isis
);
416 if (type
== DEFAULT_ROUTE
&& originate_type
== DEFAULT_ORIGINATE_ALWAYS
)
417 isis_redist_ensure_default(area
->isis
, family
);
419 ei_table
= get_ext_info(area
->isis
, family
);
420 for (rn
= route_top(ei_table
); rn
; rn
= srcdest_route_next(rn
)) {
425 const struct prefix
*p
, *src_p
;
427 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
429 if (type
== DEFAULT_ROUTE
) {
430 if (!is_default_prefix(p
)
431 || (src_p
&& src_p
->prefixlen
)) {
435 if (info
->origin
!= type
)
439 isis_redist_update_ext_reach(area
, level
, redist
, p
,
440 (const struct prefix_ipv6
*)src_p
,
445 void isis_redist_unset(struct isis_area
*area
, int level
, int family
, int type
)
447 struct isis_redist
*redist
=
448 get_redist_settings(area
, family
, type
, level
);
449 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
450 struct route_node
*rn
;
451 struct isis_ext_info
*info
;
458 zlog_warn("%s: External reachability table uninitialized.",
463 for (rn
= route_top(er_table
); rn
; rn
= srcdest_route_next(rn
)) {
468 const struct prefix
*p
, *src_p
;
469 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
471 if (type
== DEFAULT_ROUTE
) {
472 if (!is_default_prefix(p
)
473 || (src_p
&& src_p
->prefixlen
)) {
477 if (info
->origin
!= type
)
481 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
482 route_unlock_node(rn
);
485 lsp_regenerate_schedule(area
, level
, 0);
486 isis_redist_update_zebra_subscriptions(area
->isis
);
489 void isis_redist_area_finish(struct isis_area
*area
)
495 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
496 for (level
= 0; level
< ISIS_LEVELS
; level
++) {
497 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
498 struct isis_redist
*redist
;
500 redist
= &area
->redist_settings
[protocol
][type
]
503 XFREE(MTYPE_ISIS
, redist
->map_name
);
505 route_table_finish(area
->ext_reach
[protocol
][level
]);
508 isis_redist_update_zebra_subscriptions(area
->isis
);
512 DEFUN (isis_redistribute
,
513 isis_redistribute_cmd
,
514 "redistribute <ipv4|ipv6> " PROTO_REDIST_STR
515 " [{metric (0-16777215)|route-map WORD}]",
517 "Redistribute IPv4 routes\n"
518 "Redistribute IPv6 routes\n"
520 "Metric for redistributed routes\n"
521 "ISIS default metric\n"
522 "Route map reference\n"
523 "Pointer to route-map entries\n")
526 int idx_protocol
= 2;
527 int idx_metric_rmap
= 1;
528 VTY_DECLVAR_CONTEXT(isis_area
, area
);
533 unsigned long metric
= 0;
534 const char *routemap
= NULL
;
536 family
= str2family(argv
[idx_afi
]->text
);
538 return CMD_WARNING_CONFIG_FAILED
;
540 afi
= family2afi(family
);
542 return CMD_WARNING_CONFIG_FAILED
;
544 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
546 return CMD_WARNING_CONFIG_FAILED
;
550 if ((area
->is_type
& level
) != level
) {
551 vty_out(vty
, "Node is not a level-%d IS\n", level
);
552 return CMD_WARNING_CONFIG_FAILED
;
555 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
556 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
560 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
561 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
564 isis_redist_set(area
, level
, family
, type
, metric
, routemap
, 0);
568 DEFUN (no_isis_redistribute
,
569 no_isis_redistribute_cmd
,
570 "no redistribute <ipv4|ipv6> " PROTO_REDIST_STR
,
573 "Redistribute IPv4 routes\n"
574 "Redistribute IPv6 routes\n"
578 int idx_protocol
= 3;
579 VTY_DECLVAR_CONTEXT(isis_area
, area
);
585 family
= str2family(argv
[idx_afi
]->arg
);
587 return CMD_WARNING_CONFIG_FAILED
;
589 afi
= family2afi(family
);
591 return CMD_WARNING_CONFIG_FAILED
;
593 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
595 return CMD_WARNING_CONFIG_FAILED
;
599 isis_redist_unset(area
, level
, family
, type
);
603 DEFUN (isis_default_originate
,
604 isis_default_originate_cmd
,
605 "default-information originate <ipv4|ipv6> [always] [{metric (0-16777215)|route-map WORD}]",
606 "Control distribution of default information\n"
607 "Distribute a default route\n"
608 "Distribute default route for IPv4\n"
609 "Distribute default route for IPv6\n"
610 "Always advertise default route\n"
611 "Metric for default route\n"
612 "ISIS default metric\n"
613 "Route map reference\n"
614 "Pointer to route-map entries\n")
617 int idx_always
= fabricd
? 3 : 4;
618 int idx_metric_rmap
= 1;
619 VTY_DECLVAR_CONTEXT(isis_area
, area
);
621 int originate_type
= DEFAULT_ORIGINATE
;
623 unsigned long metric
= 0;
624 const char *routemap
= NULL
;
626 family
= str2family(argv
[idx_afi
]->text
);
628 return CMD_WARNING_CONFIG_FAILED
;
632 if ((area
->is_type
& level
) != level
) {
633 vty_out(vty
, "Node is not a level-%d IS\n", level
);
634 return CMD_WARNING_CONFIG_FAILED
;
637 if (argc
> idx_always
&& strmatch(argv
[idx_always
]->text
, "always")) {
638 originate_type
= DEFAULT_ORIGINATE_ALWAYS
;
642 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
643 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
647 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
648 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
651 if (family
== AF_INET6
&& originate_type
!= DEFAULT_ORIGINATE_ALWAYS
) {
653 "Zebra doesn't implement default-originate for IPv6 yet\n");
655 "so use with care or use default-originate always.\n");
658 isis_redist_set(area
, level
, family
, DEFAULT_ROUTE
, metric
, routemap
,
663 DEFUN (no_isis_default_originate
,
664 no_isis_default_originate_cmd
,
665 "no default-information originate <ipv4|ipv6>",
667 "Control distribution of default information\n"
668 "Distribute a default route\n"
669 "Distribute default route for IPv4\n"
670 "Distribute default route for IPv6\n")
673 VTY_DECLVAR_CONTEXT(isis_area
, area
);
677 family
= str2family(argv
[idx_afi
]->text
);
679 return CMD_WARNING_CONFIG_FAILED
;
683 isis_redist_unset(area
, level
, family
, DEFAULT_ROUTE
);
686 #endif /* ifdef FABRICD */
688 int isis_redist_config_write(struct vty
*vty
, struct isis_area
*area
,
694 struct isis_redist
*redist
;
695 const char *family_str
;
697 if (family
== AF_INET
)
699 else if (family
== AF_INET6
)
704 for (type
= 0; type
< ZEBRA_ROUTE_MAX
; type
++) {
705 if (type
== PROTO_TYPE
)
708 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
709 redist
= get_redist_settings(area
, family
, type
, level
);
712 vty_out(vty
, " redistribute %s %s", family_str
,
713 zebra_route_string(type
));
715 vty_out(vty
, " level-%d", level
);
717 vty_out(vty
, " metric %u", redist
->metric
);
718 if (redist
->map_name
)
719 vty_out(vty
, " route-map %s", redist
->map_name
);
725 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
727 get_redist_settings(area
, family
, DEFAULT_ROUTE
, level
);
730 vty_out(vty
, " default-information originate %s",
733 vty_out(vty
, " level-%d", level
);
734 if (redist
->redist
== DEFAULT_ORIGINATE_ALWAYS
)
735 vty_out(vty
, " always");
737 vty_out(vty
, " metric %u", redist
->metric
);
738 if (redist
->map_name
)
739 vty_out(vty
, " route-map %s", redist
->map_name
);
747 void isis_redist_init(void)
750 install_element(ROUTER_NODE
, &isis_redistribute_cmd
);
751 install_element(ROUTER_NODE
, &no_isis_redistribute_cmd
);
753 install_element(ROUTER_NODE
, &isis_default_originate_cmd
);
754 install_element(ROUTER_NODE
, &no_isis_default_originate_cmd
);
755 #endif /* ifdef FABRICD */