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"
34 #include "isisd/dict.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 static struct route_node
*
90 isis_redist_route_node_create(route_table_delegate_t
*delegate
,
91 struct route_table
*table
)
93 struct route_node
*node
;
94 node
= XCALLOC(MTYPE_ISIS_EXT_ROUTE
, sizeof(*node
));
98 static void isis_redist_route_node_destroy(route_table_delegate_t
*delegate
,
99 struct route_table
*table
,
100 struct route_node
*node
)
103 XFREE(MTYPE_ISIS_EXT_INFO
, node
->info
);
104 XFREE(MTYPE_ISIS_EXT_ROUTE
, node
);
107 static route_table_delegate_t isis_redist_rt_delegate
= {
108 .create_node
= isis_redist_route_node_create
,
109 .destroy_node
= isis_redist_route_node_destroy
};
111 /* Install external reachability information into a
112 * specific area for a specific level.
113 * Schedule an lsp regenerate if necessary */
114 static void isis_redist_install(struct isis_area
*area
, int level
,
115 struct prefix
*p
, struct isis_ext_info
*info
)
117 int family
= p
->family
;
118 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
119 struct route_node
*er_node
;
123 "%s: External reachability table of area %s"
124 " is not initialized.",
125 __func__
, area
->area_tag
);
129 er_node
= route_node_get(er_table
, p
);
131 route_unlock_node(er_node
);
133 /* Don't update/reschedule lsp generation if nothing changed. */
134 if (!memcmp(er_node
->info
, info
, sizeof(*info
)))
137 er_node
->info
= XMALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(*info
));
140 memcpy(er_node
->info
, info
, sizeof(*info
));
141 lsp_regenerate_schedule(area
, level
, 0);
144 /* Remove external reachability information from a
145 * specific area for a specific level.
146 * Schedule an lsp regenerate if necessary. */
147 static void isis_redist_uninstall(struct isis_area
*area
, int level
,
150 int family
= p
->family
;
151 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
152 struct route_node
*er_node
;
156 "%s: External reachability table of area %s"
157 " is not initialized.",
158 __func__
, area
->area_tag
);
162 er_node
= route_node_lookup(er_table
, p
);
166 route_unlock_node(er_node
);
171 XFREE(MTYPE_ISIS_EXT_INFO
, er_node
->info
);
172 route_unlock_node(er_node
);
173 lsp_regenerate_schedule(area
, level
, 0);
176 /* Update external reachability info of area for a given level
177 * and prefix, using the given redistribution settings. */
178 static void isis_redist_update_ext_reach(struct isis_area
*area
, int level
,
179 struct isis_redist
*redist
,
181 struct isis_ext_info
*info
)
183 struct isis_ext_info area_info
;
184 route_map_result_t map_ret
;
186 memcpy(&area_info
, info
, sizeof(area_info
));
187 if (redist
->metric
!= 0xffffffff)
188 area_info
.metric
= redist
->metric
;
190 if (redist
->map_name
) {
192 route_map_apply(redist
->map
, p
, RMAP_ISIS
, &area_info
);
193 if (map_ret
== RMAP_DENYMATCH
)
194 area_info
.distance
= 255;
197 /* Allow synthesized default routes only on always orignate */
198 if (area_info
.origin
== DEFAULT_ROUTE
199 && redist
->redist
!= DEFAULT_ORIGINATE_ALWAYS
)
200 area_info
.distance
= 255;
202 if (area_info
.distance
< 255)
203 isis_redist_install(area
, level
, p
, &area_info
);
205 isis_redist_uninstall(area
, level
, p
);
208 static void isis_redist_ensure_default(struct isis
*isis
, int family
)
211 struct route_table
*ei_table
= get_ext_info(isis
, family
);
212 struct route_node
*ei_node
;
213 struct isis_ext_info
*info
;
215 if (family
== AF_INET
) {
218 memset(&p
.u
.prefix4
, 0, sizeof(p
.u
.prefix4
));
219 } else if (family
== AF_INET6
) {
222 memset(&p
.u
.prefix6
, 0, sizeof(p
.u
.prefix6
));
224 assert(!"Unknown family!");
226 ei_node
= route_node_get(ei_table
, &p
);
228 route_unlock_node(ei_node
);
233 XCALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(struct isis_ext_info
));
235 info
= ei_node
->info
;
236 info
->origin
= DEFAULT_ROUTE
;
237 info
->distance
= 254;
238 info
->metric
= MAX_WIDE_PATH_METRIC
;
241 /* Handle notification about route being added */
242 void isis_redist_add(int type
, struct prefix
*p
, uint8_t distance
,
245 int family
= p
->family
;
246 struct route_table
*ei_table
= get_ext_info(isis
, family
);
247 struct route_node
*ei_node
;
248 struct isis_ext_info
*info
;
249 struct listnode
*node
;
250 struct isis_area
*area
;
252 struct isis_redist
*redist
;
254 char debug_buf
[BUFSIZ
];
255 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
257 zlog_debug("%s: New route %s from %s: distance %d.", __func__
,
258 debug_buf
, zebra_route_string(type
), distance
);
261 zlog_warn("%s: External information table not initialized.",
266 ei_node
= route_node_get(ei_table
, p
);
268 route_unlock_node(ei_node
);
270 ei_node
->info
= XCALLOC(MTYPE_ISIS_EXT_INFO
,
271 sizeof(struct isis_ext_info
));
273 info
= ei_node
->info
;
275 info
->distance
= distance
;
276 info
->metric
= metric
;
278 if (is_default_prefix(p
))
279 type
= DEFAULT_ROUTE
;
281 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
282 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
283 redist
= get_redist_settings(area
, family
, type
, level
);
287 isis_redist_update_ext_reach(area
, level
, redist
, p
,
292 void isis_redist_delete(int type
, struct prefix
*p
)
294 int family
= p
->family
;
295 struct route_table
*ei_table
= get_ext_info(isis
, family
);
296 struct route_node
*ei_node
;
297 struct listnode
*node
;
298 struct isis_area
*area
;
300 struct isis_redist
*redist
;
302 char debug_buf
[BUFSIZ
];
303 prefix2str(p
, debug_buf
, sizeof(debug_buf
));
305 zlog_debug("%s: Removing route %s from %s.", __func__
, debug_buf
,
306 zebra_route_string(type
));
308 if (is_default_prefix(p
)) {
309 /* Don't remove default route but add synthetic route for use
310 * by "default-information originate always". Areas without the
311 * "always" setting will ignore routes with origin
313 isis_redist_add(DEFAULT_ROUTE
, p
, 254, MAX_WIDE_PATH_METRIC
);
318 zlog_warn("%s: External information table not initialized.",
323 ei_node
= route_node_lookup(ei_table
, p
);
324 if (!ei_node
|| !ei_node
->info
) {
326 prefix2str(p
, buf
, sizeof(buf
));
328 "%s: Got a delete for %s route %s, but that route"
330 __func__
, zebra_route_string(type
), buf
);
332 route_unlock_node(ei_node
);
335 route_unlock_node(ei_node
);
337 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
338 for (level
= 1; level
< ISIS_LEVELS
; level
++) {
339 redist
= get_redist_settings(area
, family
, type
, level
);
343 isis_redist_uninstall(area
, level
, p
);
346 XFREE(MTYPE_ISIS_EXT_INFO
, ei_node
->info
);
347 route_unlock_node(ei_node
);
350 static void isis_redist_routemap_set(struct isis_redist
*redist
,
351 const char *routemap
)
353 if (redist
->map_name
) {
354 XFREE(MTYPE_ISIS
, redist
->map_name
);
358 if (routemap
&& strlen(routemap
)) {
359 redist
->map_name
= XSTRDUP(MTYPE_ISIS
, routemap
);
360 redist
->map
= route_map_lookup_by_name(routemap
);
364 static void isis_redist_update_zebra_subscriptions(struct isis
*isis
)
366 struct listnode
*node
;
367 struct isis_area
*area
;
372 char do_subscribe
[REDIST_PROTOCOL_COUNT
][ZEBRA_ROUTE_MAX
+ 1];
374 memset(do_subscribe
, 0, sizeof(do_subscribe
));
376 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
377 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
378 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++)
379 for (level
= 0; level
< ISIS_LEVELS
; level
++)
380 if (area
->redist_settings
[protocol
]
383 do_subscribe
[protocol
][type
] =
386 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
387 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
388 /* This field is actually controlling transmission of
390 * routes to Zebra and has nothing to do with
393 if (type
== ZEBRA_ROUTE_ISIS
)
396 afi_t afi
= afi_for_redist_protocol(protocol
);
398 if (do_subscribe
[protocol
][type
])
399 isis_zebra_redistribute_set(afi
, type
);
401 isis_zebra_redistribute_unset(afi
, type
);
405 static void isis_redist_set(struct isis_area
*area
, int level
, int family
,
406 int type
, uint32_t metric
, const char *routemap
,
409 int protocol
= redist_protocol(family
);
410 struct isis_redist
*redist
=
411 get_redist_settings(area
, family
, type
, level
);
413 struct route_table
*ei_table
;
414 struct route_node
*rn
;
415 struct isis_ext_info
*info
;
417 redist
->redist
= (type
== DEFAULT_ROUTE
) ? originate_type
: 1;
418 redist
->metric
= metric
;
419 isis_redist_routemap_set(redist
, routemap
);
421 if (!area
->ext_reach
[protocol
][level
- 1]) {
422 area
->ext_reach
[protocol
][level
- 1] =
423 route_table_init_with_delegate(
424 &isis_redist_rt_delegate
);
427 for (i
= 0; i
< REDIST_PROTOCOL_COUNT
; i
++)
428 if (!area
->isis
->ext_info
[i
]) {
429 area
->isis
->ext_info
[i
] =
430 route_table_init_with_delegate(
431 &isis_redist_rt_delegate
);
434 isis_redist_update_zebra_subscriptions(area
->isis
);
436 if (type
== DEFAULT_ROUTE
&& originate_type
== DEFAULT_ORIGINATE_ALWAYS
)
437 isis_redist_ensure_default(area
->isis
, family
);
439 ei_table
= get_ext_info(area
->isis
, family
);
440 for (rn
= route_top(ei_table
); rn
; rn
= route_next(rn
)) {
445 if (type
== DEFAULT_ROUTE
) {
446 if (!is_default_prefix(&rn
->p
))
449 if (info
->origin
!= type
)
453 isis_redist_update_ext_reach(area
, level
, redist
, &rn
->p
, info
);
457 static void isis_redist_unset(struct isis_area
*area
, int level
, int family
,
460 struct isis_redist
*redist
=
461 get_redist_settings(area
, family
, type
, level
);
462 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
463 struct route_node
*rn
;
464 struct isis_ext_info
*info
;
471 zlog_warn("%s: External reachability table uninitialized.",
476 for (rn
= route_top(er_table
); rn
; rn
= route_next(rn
)) {
481 if (type
== DEFAULT_ROUTE
) {
482 if (!is_default_prefix(&rn
->p
))
485 if (info
->origin
!= type
)
489 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
490 route_unlock_node(rn
);
493 lsp_regenerate_schedule(area
, level
, 0);
494 isis_redist_update_zebra_subscriptions(area
->isis
);
497 void isis_redist_area_finish(struct isis_area
*area
)
503 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
504 for (level
= 0; level
< ISIS_LEVELS
; level
++) {
505 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
506 struct isis_redist
*redist
;
508 redist
= &area
->redist_settings
[protocol
][type
]
511 if (redist
->map_name
)
512 XFREE(MTYPE_ISIS
, redist
->map_name
);
514 route_table_finish(area
->ext_reach
[protocol
][level
]);
517 isis_redist_update_zebra_subscriptions(area
->isis
);
520 DEFUN (isis_redistribute
,
521 isis_redistribute_cmd
,
522 "redistribute <ipv4|ipv6> " FRR_REDIST_STR_ISISD
" <level-1|level-2> [<metric (0-16777215)|route-map WORD>]",
524 "Redistribute IPv4 routes\n"
525 "Redistribute IPv6 routes\n"
526 FRR_REDIST_HELP_STR_ISISD
527 "Redistribute into level-1\n"
528 "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
= 4;
538 VTY_DECLVAR_CONTEXT(isis_area
, area
);
543 unsigned long metric
;
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
;
558 if (!strcmp("level-1", argv
[idx_level
]->arg
))
560 else if (!strcmp("level-2", argv
[idx_level
]->arg
))
563 return CMD_WARNING_CONFIG_FAILED
;
565 if ((area
->is_type
& level
) != level
) {
566 vty_out(vty
, "Node is not a level-%d IS\n", level
);
567 return CMD_WARNING_CONFIG_FAILED
;
573 if (argc
> idx_metric_rmap
+ 1) {
574 if (argv
[idx_metric_rmap
+ 1]->arg
[0] == '\0')
575 return CMD_WARNING_CONFIG_FAILED
;
577 if (strmatch(argv
[idx_metric_rmap
]->text
, "metric")) {
579 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, &endp
,
583 return CMD_WARNING_CONFIG_FAILED
;
585 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
589 isis_redist_set(area
, level
, family
, type
, metric
, routemap
, 0);
593 DEFUN (no_isis_redistribute
,
594 no_isis_redistribute_cmd
,
595 "no redistribute <ipv4|ipv6> " FRR_REDIST_STR_ISISD
" <level-1|level-2>",
598 "Redistribute IPv4 routes\n"
599 "Redistribute IPv6 routes\n"
600 FRR_REDIST_HELP_STR_ISISD
601 "Redistribute into level-1\n"
602 "Redistribute into level-2\n")
605 int idx_protocol
= 3;
607 VTY_DECLVAR_CONTEXT(isis_area
, area
);
613 family
= str2family(argv
[idx_afi
]->arg
);
615 return CMD_WARNING_CONFIG_FAILED
;
617 afi
= family2afi(family
);
619 return CMD_WARNING_CONFIG_FAILED
;
621 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
623 return CMD_WARNING_CONFIG_FAILED
;
625 level
= strmatch("level-1", argv
[idx_level
]->text
) ? 1 : 2;
627 isis_redist_unset(area
, level
, family
, type
);
631 DEFUN (isis_default_originate
,
632 isis_default_originate_cmd
,
633 "default-information originate <ipv4|ipv6> <level-1|level-2> [always] [<metric (0-16777215)|route-map WORD>]",
634 "Control distribution of default information\n"
635 "Distribute a default route\n"
636 "Distribute default route for IPv4\n"
637 "Distribute default route for IPv6\n"
638 "Distribute default route into level-1\n"
639 "Distribute default route into level-2\n"
640 "Always advertise default route\n"
641 "Metric for default route\n"
642 "ISIS default metric\n"
643 "Route map reference\n"
644 "Pointer to route-map entries\n")
649 int idx_metric_rmap
= 4;
650 VTY_DECLVAR_CONTEXT(isis_area
, area
);
652 int originate_type
= DEFAULT_ORIGINATE
;
654 unsigned long metric
= 0xffffffff;
655 const char *routemap
= NULL
;
657 family
= str2family(argv
[idx_afi
]->text
);
659 return CMD_WARNING_CONFIG_FAILED
;
661 level
= strmatch("level-1", argv
[idx_level
]->text
) ? 1 : 2;
663 if ((area
->is_type
& level
) != level
) {
664 vty_out(vty
, "Node is not a level-%d IS\n", level
);
665 return CMD_WARNING_CONFIG_FAILED
;
668 if (argc
> idx_always
&& strmatch(argv
[idx_always
]->text
, "always")) {
669 originate_type
= DEFAULT_ORIGINATE_ALWAYS
;
673 if (argc
> idx_metric_rmap
) {
674 if (strmatch(argv
[idx_metric_rmap
]->text
, "metric"))
675 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
,
678 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
681 if (family
== AF_INET6
&& originate_type
!= DEFAULT_ORIGINATE_ALWAYS
) {
683 "Zebra doesn't implement default-originate for IPv6 yet\n");
685 "so use with care or use default-originate always.\n");
688 isis_redist_set(area
, level
, family
, DEFAULT_ROUTE
, metric
, routemap
,
693 DEFUN (no_isis_default_originate
,
694 no_isis_default_originate_cmd
,
695 "no default-information originate <ipv4|ipv6> <level-1|level-2>",
697 "Control distribution of default information\n"
698 "Distribute a default route\n"
699 "Distribute default route for IPv4\n"
700 "Distribute default route for IPv6\n"
701 "Distribute default route into level-1\n"
702 "Distribute default route into level-2\n")
706 VTY_DECLVAR_CONTEXT(isis_area
, area
);
710 family
= str2family(argv
[idx_afi
]->text
);
712 return CMD_WARNING_CONFIG_FAILED
;
714 if (strmatch("level-1", argv
[idx_level
]->text
))
716 else if (strmatch("level-2", argv
[idx_level
]->text
))
719 return CMD_WARNING_CONFIG_FAILED
;
721 isis_redist_unset(area
, level
, family
, DEFAULT_ROUTE
);
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
== ZEBRA_ROUTE_ISIS
)
745 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
746 redist
= get_redist_settings(area
, family
, type
, level
);
749 vty_out(vty
, " redistribute %s %s level-%d", family_str
,
750 zebra_route_string(type
), level
);
751 if (redist
->metric
!= 0xffffffff)
752 vty_out(vty
, " metric %u", redist
->metric
);
753 if (redist
->map_name
)
754 vty_out(vty
, " route-map %s", redist
->map_name
);
760 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
762 get_redist_settings(area
, family
, DEFAULT_ROUTE
, level
);
765 vty_out(vty
, " default-information originate %s level-%d",
767 if (redist
->redist
== DEFAULT_ORIGINATE_ALWAYS
)
768 vty_out(vty
, " always");
769 if (redist
->metric
!= 0xffffffff)
770 vty_out(vty
, " metric %u", redist
->metric
);
771 if (redist
->map_name
)
772 vty_out(vty
, " route-map %s", redist
->map_name
);
780 void isis_redist_init(void)
782 install_element(ISIS_NODE
, &isis_redistribute_cmd
);
783 install_element(ISIS_NODE
, &no_isis_redistribute_cmd
);
784 install_element(ISIS_NODE
, &isis_default_originate_cmd
);
785 install_element(ISIS_NODE
, &no_isis_default_originate_cmd
);