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
, p
, RMAP_ISIS
, &area_info
);
175 if (map_ret
== RMAP_DENYMATCH
)
176 area_info
.distance
= 255;
179 /* Allow synthesized default routes only on always orignate */
180 if (area_info
.origin
== DEFAULT_ROUTE
181 && redist
->redist
!= DEFAULT_ORIGINATE_ALWAYS
)
182 area_info
.distance
= 255;
184 if (area_info
.distance
< 255)
185 isis_redist_install(area
, level
, p
, src_p
, &area_info
);
187 isis_redist_uninstall(area
, level
, p
, src_p
);
190 static void isis_redist_ensure_default(struct isis
*isis
, int family
)
193 struct route_table
*ei_table
= get_ext_info(isis
, family
);
194 struct route_node
*ei_node
;
195 struct isis_ext_info
*info
;
197 if (family
== AF_INET
) {
200 memset(&p
.u
.prefix4
, 0, sizeof(p
.u
.prefix4
));
201 } else if (family
== AF_INET6
) {
204 memset(&p
.u
.prefix6
, 0, sizeof(p
.u
.prefix6
));
206 assert(!"Unknown family!");
208 ei_node
= srcdest_rnode_get(ei_table
, &p
, NULL
);
210 route_unlock_node(ei_node
);
215 XCALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(struct isis_ext_info
));
217 info
= ei_node
->info
;
218 info
->origin
= DEFAULT_ROUTE
;
219 info
->distance
= 254;
220 info
->metric
= MAX_WIDE_PATH_METRIC
;
223 /* Handle notification about route being added */
224 void isis_redist_add(int type
, struct prefix
*p
, struct prefix_ipv6
*src_p
,
225 uint8_t distance
, uint32_t metric
)
227 int family
= p
->family
;
228 struct route_table
*ei_table
= get_ext_info(isis
, family
);
229 struct route_node
*ei_node
;
230 struct isis_ext_info
*info
;
231 struct listnode
*node
;
232 struct isis_area
*area
;
234 struct isis_redist
*redist
;
236 char debug_buf
[BUFSIZ
];
237 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
239 zlog_debug("%s: New route %s from %s: distance %d.", __func__
,
240 debug_buf
, zebra_route_string(type
), distance
);
243 zlog_warn("%s: External information table not initialized.",
248 ei_node
= srcdest_rnode_get(ei_table
, p
, src_p
);
250 route_unlock_node(ei_node
);
252 ei_node
->info
= XCALLOC(MTYPE_ISIS_EXT_INFO
,
253 sizeof(struct isis_ext_info
));
255 info
= ei_node
->info
;
257 info
->distance
= distance
;
258 info
->metric
= metric
;
260 if (is_default_prefix(p
)
261 && (!src_p
|| !src_p
->prefixlen
)) {
262 type
= DEFAULT_ROUTE
;
265 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
266 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
267 redist
= get_redist_settings(area
, family
, type
, level
);
271 isis_redist_update_ext_reach(area
, level
, redist
, p
,
276 void isis_redist_delete(int type
, struct prefix
*p
, struct prefix_ipv6
*src_p
)
278 int family
= p
->family
;
279 struct route_table
*ei_table
= get_ext_info(isis
, family
);
280 struct route_node
*ei_node
;
281 struct listnode
*node
;
282 struct isis_area
*area
;
284 struct isis_redist
*redist
;
286 char debug_buf
[BUFSIZ
];
287 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
289 zlog_debug("%s: Removing route %s from %s.", __func__
, debug_buf
,
290 zebra_route_string(type
));
292 if (is_default_prefix(p
)
293 && (!src_p
|| !src_p
->prefixlen
)) {
294 /* Don't remove default route but add synthetic route for use
295 * by "default-information originate always". Areas without the
296 * "always" setting will ignore routes with origin
298 isis_redist_add(DEFAULT_ROUTE
, p
, NULL
,
299 254, MAX_WIDE_PATH_METRIC
);
304 zlog_warn("%s: External information table not initialized.",
309 ei_node
= srcdest_rnode_lookup(ei_table
, p
, src_p
);
310 if (!ei_node
|| !ei_node
->info
) {
312 prefix2str(p
, buf
, sizeof(buf
));
314 "%s: Got a delete for %s route %s, but that route"
316 __func__
, zebra_route_string(type
), buf
);
318 route_unlock_node(ei_node
);
321 route_unlock_node(ei_node
);
323 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
324 for (level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
325 redist
= get_redist_settings(area
, family
, type
, level
);
329 isis_redist_uninstall(area
, level
, p
, src_p
);
332 XFREE(MTYPE_ISIS_EXT_INFO
, ei_node
->info
);
333 route_unlock_node(ei_node
);
336 static void isis_redist_routemap_set(struct isis_redist
*redist
,
337 const char *routemap
)
339 if (redist
->map_name
) {
340 XFREE(MTYPE_ISIS
, redist
->map_name
);
344 if (routemap
&& strlen(routemap
)) {
345 redist
->map_name
= XSTRDUP(MTYPE_ISIS
, routemap
);
346 redist
->map
= route_map_lookup_by_name(routemap
);
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 if (redist
->map_name
)
506 XFREE(MTYPE_ISIS
, redist
->map_name
);
508 route_table_finish(area
->ext_reach
[protocol
][level
]);
511 isis_redist_update_zebra_subscriptions(area
->isis
);
514 DEFUN (isis_redistribute
,
515 isis_redistribute_cmd
,
516 "redistribute <ipv4|ipv6> " PROTO_REDIST_STR
520 " [<metric (0-16777215)|route-map WORD>]",
522 "Redistribute IPv4 routes\n"
523 "Redistribute IPv6 routes\n"
526 "Redistribute into level-1\n"
527 "Redistribute into level-2\n"
529 "Metric for redistributed routes\n"
530 "ISIS default metric\n"
531 "Route map reference\n"
532 "Pointer to route-map entries\n")
535 int idx_protocol
= 2;
537 int idx_metric_rmap
= fabricd
? 3 : 4;
538 VTY_DECLVAR_CONTEXT(isis_area
, area
);
543 unsigned long metric
= 0;
544 const char *routemap
= NULL
;
546 family
= str2family(argv
[idx_afi
]->text
);
548 return CMD_WARNING_CONFIG_FAILED
;
550 afi
= family2afi(family
);
552 return CMD_WARNING_CONFIG_FAILED
;
554 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
556 return CMD_WARNING_CONFIG_FAILED
;
560 else if (!strcmp("level-1", argv
[idx_level
]->arg
))
562 else if (!strcmp("level-2", argv
[idx_level
]->arg
))
565 return CMD_WARNING_CONFIG_FAILED
;
567 if ((area
->is_type
& level
) != level
) {
568 vty_out(vty
, "Node is not a level-%d IS\n", level
);
569 return CMD_WARNING_CONFIG_FAILED
;
572 if (argc
> idx_metric_rmap
+ 1) {
573 if (argv
[idx_metric_rmap
+ 1]->arg
[0] == '\0')
574 return CMD_WARNING_CONFIG_FAILED
;
576 if (strmatch(argv
[idx_metric_rmap
]->text
, "metric")) {
578 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, &endp
,
582 return CMD_WARNING_CONFIG_FAILED
;
584 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
588 isis_redist_set(area
, level
, family
, type
, metric
, routemap
, 0);
592 DEFUN (no_isis_redistribute
,
593 no_isis_redistribute_cmd
,
594 "no redistribute <ipv4|ipv6> " PROTO_REDIST_STR
600 "Redistribute IPv4 routes\n"
601 "Redistribute IPv6 routes\n"
604 "Redistribute into level-1\n"
605 "Redistribute into level-2\n"
610 int idx_protocol
= 3;
612 VTY_DECLVAR_CONTEXT(isis_area
, area
);
618 family
= str2family(argv
[idx_afi
]->arg
);
620 return CMD_WARNING_CONFIG_FAILED
;
622 afi
= family2afi(family
);
624 return CMD_WARNING_CONFIG_FAILED
;
626 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
628 return CMD_WARNING_CONFIG_FAILED
;
633 level
= strmatch("level-1", argv
[idx_level
]->text
) ? 1 : 2;
635 isis_redist_unset(area
, level
, family
, type
);
640 DEFUN (isis_default_originate
,
641 isis_default_originate_cmd
,
642 "default-information originate <ipv4|ipv6>"
643 " [always] [<metric (0-16777215)|route-map WORD>]",
644 "Control distribution of default information\n"
645 "Distribute a default route\n"
646 "Distribute default route for IPv4\n"
647 "Distribute default route for IPv6\n"
648 "Always advertise default route\n"
649 "Metric for default route\n"
650 "ISIS default metric\n"
651 "Route map reference\n"
652 "Pointer to route-map entries\n")
655 int idx_always
= fabricd
? 3 : 4;
656 int idx_metric_rmap
= fabricd
? 3 : 4;
657 VTY_DECLVAR_CONTEXT(isis_area
, area
);
659 int originate_type
= DEFAULT_ORIGINATE
;
661 unsigned long metric
= 0;
662 const char *routemap
= NULL
;
664 family
= str2family(argv
[idx_afi
]->text
);
666 return CMD_WARNING_CONFIG_FAILED
;
670 if ((area
->is_type
& level
) != level
) {
671 vty_out(vty
, "Node is not a level-%d IS\n", level
);
672 return CMD_WARNING_CONFIG_FAILED
;
675 if (argc
> idx_always
&& strmatch(argv
[idx_always
]->text
, "always")) {
676 originate_type
= DEFAULT_ORIGINATE_ALWAYS
;
680 if (argc
> idx_metric_rmap
) {
681 if (strmatch(argv
[idx_metric_rmap
]->text
, "metric"))
682 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
,
685 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
688 if (family
== AF_INET6
&& originate_type
!= DEFAULT_ORIGINATE_ALWAYS
) {
690 "Zebra doesn't implement default-originate for IPv6 yet\n");
692 "so use with care or use default-originate always.\n");
695 isis_redist_set(area
, level
, family
, DEFAULT_ROUTE
, metric
, routemap
,
700 DEFUN (no_isis_default_originate
,
701 no_isis_default_originate_cmd
,
702 "no default-information originate <ipv4|ipv6>",
704 "Control distribution of default information\n"
705 "Distribute a default route\n"
706 "Distribute default route for IPv4\n"
707 "Distribute default route for IPv6\n")
710 VTY_DECLVAR_CONTEXT(isis_area
, area
);
714 family
= str2family(argv
[idx_afi
]->text
);
716 return CMD_WARNING_CONFIG_FAILED
;
720 isis_redist_unset(area
, level
, family
, DEFAULT_ROUTE
);
723 #endif /* ifdef FABRICD */
725 int isis_redist_config_write(struct vty
*vty
, struct isis_area
*area
,
731 struct isis_redist
*redist
;
732 const char *family_str
;
734 if (family
== AF_INET
)
736 else if (family
== AF_INET6
)
741 for (type
= 0; type
< ZEBRA_ROUTE_MAX
; type
++) {
742 if (type
== PROTO_TYPE
)
745 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
746 redist
= get_redist_settings(area
, family
, type
, level
);
749 vty_out(vty
, " redistribute %s %s", family_str
,
750 zebra_route_string(type
));
752 vty_out(vty
, " level-%d", level
);
754 vty_out(vty
, " metric %u", redist
->metric
);
755 if (redist
->map_name
)
756 vty_out(vty
, " route-map %s", redist
->map_name
);
762 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
764 get_redist_settings(area
, family
, DEFAULT_ROUTE
, level
);
767 vty_out(vty
, " default-information originate %s",
770 vty_out(vty
, " level-%d", level
);
771 if (redist
->redist
== DEFAULT_ORIGINATE_ALWAYS
)
772 vty_out(vty
, " always");
774 vty_out(vty
, " metric %u", redist
->metric
);
775 if (redist
->map_name
)
776 vty_out(vty
, " route-map %s", redist
->map_name
);
784 void isis_redist_init(void)
786 install_element(ROUTER_NODE
, &isis_redistribute_cmd
);
787 install_element(ROUTER_NODE
, &no_isis_redistribute_cmd
);
789 install_element(ROUTER_NODE
, &isis_default_originate_cmd
);
790 install_element(ROUTER_NODE
, &no_isis_default_originate_cmd
);
791 #endif /* ifdef FABRICD */