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
32 #include "srcdest_table.h"
34 #include "isisd/isis_constants.h"
35 #include "isisd/isis_common.h"
36 #include "isisd/isis_flags.h"
37 #include "isisd/isis_misc.h"
38 #include "isisd/isis_circuit.h"
39 #include "isisd/isisd.h"
40 #include "isisd/isis_lsp.h"
41 #include "isisd/isis_route.h"
42 #include "isisd/isis_zebra.h"
44 DEFINE_MTYPE_STATIC(ISISD
, ISIS_EXT_ROUTE
, "ISIS redistributed route");
45 DEFINE_MTYPE_STATIC(ISISD
, ISIS_EXT_INFO
, "ISIS redistributed route info");
46 DEFINE_MTYPE_STATIC(ISISD
, ISIS_RMAP_NAME
, "ISIS redistribute route-map name");
48 static int redist_protocol(int family
)
50 if (family
== AF_INET
)
52 if (family
== AF_INET6
)
55 assert(!"Unsupported address family!");
59 afi_t
afi_for_redist_protocol(int protocol
)
66 assert(!"Unknown redist protocol!");
70 static struct route_table
*get_ext_info(struct isis
*i
, int family
)
72 int protocol
= redist_protocol(family
);
74 return i
->ext_info
[protocol
];
77 static struct isis_redist
*get_redist_settings(struct isis_area
*area
,
78 int family
, int type
, int level
)
80 int protocol
= redist_protocol(family
);
82 return &area
->redist_settings
[protocol
][type
][level
- 1];
85 struct route_table
*get_ext_reach(struct isis_area
*area
, int family
, int level
)
87 int protocol
= redist_protocol(family
);
89 return area
->ext_reach
[protocol
][level
- 1];
92 /* Install external reachability information into a
93 * specific area for a specific level.
94 * Schedule an lsp regenerate if necessary */
95 static void isis_redist_install(struct isis_area
*area
, int level
,
96 const struct prefix
*p
,
97 const struct prefix_ipv6
*src_p
,
98 struct isis_ext_info
*info
)
100 int family
= p
->family
;
101 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
102 struct route_node
*er_node
;
106 "%s: External reachability table of area %s is not initialized.",
107 __func__
, area
->area_tag
);
111 er_node
= srcdest_rnode_get(er_table
, p
, src_p
);
113 route_unlock_node(er_node
);
115 /* Don't update/reschedule lsp generation if nothing changed. */
116 if (!memcmp(er_node
->info
, info
, sizeof(*info
)))
119 er_node
->info
= XMALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(*info
));
122 memcpy(er_node
->info
, info
, sizeof(*info
));
123 lsp_regenerate_schedule(area
, level
, 0);
126 /* Remove external reachability information from a
127 * specific area for a specific level.
128 * Schedule an lsp regenerate if necessary. */
129 static void isis_redist_uninstall(struct isis_area
*area
, int level
,
130 const struct prefix
*p
,
131 const struct prefix_ipv6
*src_p
)
133 int family
= p
->family
;
134 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
135 struct route_node
*er_node
;
139 "%s: External reachability table of area %s 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
) {
173 map_ret
= route_map_apply(redist
->map
, p
, &area_info
);
174 if (map_ret
== RMAP_DENYMATCH
)
175 area_info
.distance
= 255;
178 /* Allow synthesized default routes only on always orignate */
179 if (area_info
.origin
== DEFAULT_ROUTE
180 && redist
->redist
!= DEFAULT_ORIGINATE_ALWAYS
)
181 area_info
.distance
= 255;
183 if (area_info
.distance
< 255)
184 isis_redist_install(area
, level
, p
, src_p
, &area_info
);
186 isis_redist_uninstall(area
, level
, p
, src_p
);
189 static void isis_redist_ensure_default(struct isis
*isis
, int family
)
192 struct route_table
*ei_table
= get_ext_info(isis
, family
);
193 struct route_node
*ei_node
;
194 struct isis_ext_info
*info
;
196 if (family
== AF_INET
) {
199 memset(&p
.u
.prefix4
, 0, sizeof(p
.u
.prefix4
));
200 } else if (family
== AF_INET6
) {
203 memset(&p
.u
.prefix6
, 0, sizeof(p
.u
.prefix6
));
205 assert(!"Unknown family!");
207 ei_node
= srcdest_rnode_get(ei_table
, &p
, NULL
);
209 route_unlock_node(ei_node
);
214 XCALLOC(MTYPE_ISIS_EXT_INFO
, sizeof(struct isis_ext_info
));
216 info
= ei_node
->info
;
217 info
->origin
= DEFAULT_ROUTE
;
218 info
->distance
= 254;
219 info
->metric
= MAX_WIDE_PATH_METRIC
;
222 /* Handle notification about route being added */
223 void isis_redist_add(struct isis
*isis
, int type
, struct prefix
*p
,
224 struct prefix_ipv6
*src_p
, uint8_t distance
,
225 uint32_t metric
, const route_tag_t tag
)
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 zlog_debug("%s: New route %pFX from %s: distance %d.", __func__
, p
,
237 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
;
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 zlog_debug("%s: Removing route %pFX from %s.", __func__
, p
,
286 zebra_route_string(type
));
288 if (is_default_prefix(p
)
289 && (!src_p
|| !src_p
->prefixlen
)) {
290 /* Don't remove default route but add synthetic route for use
291 * by "default-information originate always". Areas without the
292 * "always" setting will ignore routes with origin
294 isis_redist_add(isis
, DEFAULT_ROUTE
, p
, NULL
, 254,
295 MAX_WIDE_PATH_METRIC
, 0);
300 zlog_warn("%s: External information table not initialized.",
305 ei_node
= srcdest_rnode_lookup(ei_table
, p
, src_p
);
306 if (!ei_node
|| !ei_node
->info
) {
308 "%s: Got a delete for %s route %pFX, but that route was never added.",
309 __func__
, zebra_route_string(type
), p
);
311 route_unlock_node(ei_node
);
314 route_unlock_node(ei_node
);
316 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
))
317 for (level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
318 redist
= get_redist_settings(area
, family
, type
, level
);
322 isis_redist_uninstall(area
, level
, p
, src_p
);
325 XFREE(MTYPE_ISIS_EXT_INFO
, ei_node
->info
);
326 route_unlock_node(ei_node
);
329 static void isis_redist_routemap_set(struct isis_redist
*redist
,
330 const char *routemap
)
332 if (redist
->map_name
) {
333 XFREE(MTYPE_ISIS_RMAP_NAME
, redist
->map_name
);
334 route_map_counter_decrement(redist
->map
);
338 if (routemap
&& strlen(routemap
)) {
339 redist
->map_name
= XSTRDUP(MTYPE_ISIS_RMAP_NAME
, routemap
);
340 redist
->map
= route_map_lookup_by_name(routemap
);
341 route_map_counter_increment(redist
->map
);
345 static void isis_redist_update_zebra_subscriptions(struct isis
*isis
)
347 struct listnode
*node
;
348 struct isis_area
*area
;
353 if (isis
->vrf_id
== VRF_UNKNOWN
)
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
]
368 do_subscribe
[protocol
][type
] =
371 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
372 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
373 /* This field is actually controlling transmission of
375 * routes to Zebra and has nothing to do with
378 if (type
== PROTO_TYPE
)
381 afi_t afi
= afi_for_redist_protocol(protocol
);
383 if (do_subscribe
[protocol
][type
])
384 isis_zebra_redistribute_set(afi
, type
,
387 isis_zebra_redistribute_unset(afi
, type
,
392 void isis_redist_free(struct isis
*isis
)
394 struct route_node
*rn
;
397 for (i
= 0; i
< REDIST_PROTOCOL_COUNT
; i
++) {
398 if (!isis
->ext_info
[i
])
401 for (rn
= route_top(isis
->ext_info
[i
]); rn
;
402 rn
= srcdest_route_next(rn
)) {
404 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
407 route_table_finish(isis
->ext_info
[i
]);
408 isis
->ext_info
[i
] = NULL
;
412 void isis_redist_set(struct isis_area
*area
, int level
, int family
, int type
,
413 uint32_t metric
, const char *routemap
, int originate_type
)
415 int protocol
= redist_protocol(family
);
416 struct isis_redist
*redist
=
417 get_redist_settings(area
, family
, type
, level
);
419 struct route_table
*ei_table
;
420 struct route_node
*rn
;
421 struct isis_ext_info
*info
;
423 redist
->redist
= (type
== DEFAULT_ROUTE
) ? originate_type
: 1;
424 redist
->metric
= metric
;
425 isis_redist_routemap_set(redist
, routemap
);
427 if (!area
->ext_reach
[protocol
][level
- 1]) {
428 area
->ext_reach
[protocol
][level
- 1] = srcdest_table_init();
431 for (i
= 0; i
< REDIST_PROTOCOL_COUNT
; i
++) {
432 if (!area
->isis
->ext_info
[i
]) {
433 area
->isis
->ext_info
[i
] = srcdest_table_init();
437 isis_redist_update_zebra_subscriptions(area
->isis
);
439 if (type
== DEFAULT_ROUTE
&& originate_type
== DEFAULT_ORIGINATE_ALWAYS
)
440 isis_redist_ensure_default(area
->isis
, family
);
442 ei_table
= get_ext_info(area
->isis
, family
);
443 for (rn
= route_top(ei_table
); rn
; rn
= srcdest_route_next(rn
)) {
448 const struct prefix
*p
, *src_p
;
450 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
452 if (type
== DEFAULT_ROUTE
) {
453 if (!is_default_prefix(p
)
454 || (src_p
&& src_p
->prefixlen
)) {
458 if (info
->origin
!= type
)
462 isis_redist_update_ext_reach(area
, level
, redist
, p
,
463 (const struct prefix_ipv6
*)src_p
,
468 void isis_redist_unset(struct isis_area
*area
, int level
, int family
, int type
)
470 struct isis_redist
*redist
=
471 get_redist_settings(area
, family
, type
, level
);
472 struct route_table
*er_table
= get_ext_reach(area
, family
, level
);
473 struct route_node
*rn
;
474 struct isis_ext_info
*info
;
481 zlog_warn("%s: External reachability table uninitialized.",
486 for (rn
= route_top(er_table
); rn
; rn
= srcdest_route_next(rn
)) {
491 const struct prefix
*p
, *src_p
;
492 srcdest_rnode_prefixes(rn
, &p
, &src_p
);
494 if (type
== DEFAULT_ROUTE
) {
495 if (!is_default_prefix(p
)
496 || (src_p
&& src_p
->prefixlen
)) {
500 if (info
->origin
!= type
)
504 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
505 route_unlock_node(rn
);
508 lsp_regenerate_schedule(area
, level
, 0);
509 isis_redist_update_zebra_subscriptions(area
->isis
);
512 void isis_redist_area_finish(struct isis_area
*area
)
514 struct route_node
*rn
;
519 for (protocol
= 0; protocol
< REDIST_PROTOCOL_COUNT
; protocol
++)
520 for (level
= 0; level
< ISIS_LEVELS
; level
++) {
521 for (type
= 0; type
< ZEBRA_ROUTE_MAX
+ 1; type
++) {
522 struct isis_redist
*redist
;
524 redist
= &area
->redist_settings
[protocol
][type
]
527 XFREE(MTYPE_ISIS_RMAP_NAME
, redist
->map_name
);
529 if (!area
->ext_reach
[protocol
][level
])
531 for (rn
= route_top(area
->ext_reach
[protocol
][level
]);
532 rn
; rn
= srcdest_route_next(rn
)) {
534 XFREE(MTYPE_ISIS_EXT_INFO
, rn
->info
);
536 route_table_finish(area
->ext_reach
[protocol
][level
]);
537 area
->ext_reach
[protocol
][level
] = NULL
;
540 isis_redist_update_zebra_subscriptions(area
->isis
);
544 DEFUN (isis_redistribute
,
545 isis_redistribute_cmd
,
546 "redistribute <ipv4|ipv6> " PROTO_REDIST_STR
547 " [{metric (0-16777215)|route-map WORD}]",
549 "Redistribute IPv4 routes\n"
550 "Redistribute IPv6 routes\n"
552 "Metric for redistributed routes\n"
553 "ISIS default metric\n"
554 "Route map reference\n"
555 "Pointer to route-map entries\n")
558 int idx_protocol
= 2;
559 int idx_metric_rmap
= 1;
560 VTY_DECLVAR_CONTEXT(isis_area
, area
);
565 unsigned long metric
= 0;
566 const char *routemap
= NULL
;
568 family
= str2family(argv
[idx_afi
]->text
);
570 return CMD_WARNING_CONFIG_FAILED
;
572 afi
= family2afi(family
);
574 return CMD_WARNING_CONFIG_FAILED
;
576 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
578 return CMD_WARNING_CONFIG_FAILED
;
582 if ((area
->is_type
& level
) != level
) {
583 vty_out(vty
, "Node is not a level-%d IS\n", level
);
584 return CMD_WARNING_CONFIG_FAILED
;
587 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
588 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
592 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
593 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
596 isis_redist_set(area
, level
, family
, type
, metric
, routemap
, 0);
600 DEFUN (no_isis_redistribute
,
601 no_isis_redistribute_cmd
,
602 "no redistribute <ipv4|ipv6> " PROTO_REDIST_STR
,
605 "Redistribute IPv4 routes\n"
606 "Redistribute IPv6 routes\n"
610 int idx_protocol
= 3;
611 VTY_DECLVAR_CONTEXT(isis_area
, area
);
617 family
= str2family(argv
[idx_afi
]->arg
);
619 return CMD_WARNING_CONFIG_FAILED
;
621 afi
= family2afi(family
);
623 return CMD_WARNING_CONFIG_FAILED
;
625 type
= proto_redistnum(afi
, argv
[idx_protocol
]->text
);
627 return CMD_WARNING_CONFIG_FAILED
;
631 isis_redist_unset(area
, level
, family
, type
);
635 DEFUN (isis_default_originate
,
636 isis_default_originate_cmd
,
637 "default-information originate <ipv4|ipv6> [always] [{metric (0-16777215)|route-map WORD}]",
638 "Control distribution of default information\n"
639 "Distribute a default route\n"
640 "Distribute default route for IPv4\n"
641 "Distribute default route for IPv6\n"
642 "Always advertise default route\n"
643 "Metric for default route\n"
644 "ISIS default metric\n"
645 "Route map reference\n"
646 "Pointer to route-map entries\n")
649 int idx_always
= fabricd
? 3 : 4;
650 int idx_metric_rmap
= 1;
651 VTY_DECLVAR_CONTEXT(isis_area
, area
);
653 int originate_type
= DEFAULT_ORIGINATE
;
655 unsigned long metric
= 0;
656 const char *routemap
= NULL
;
658 family
= str2family(argv
[idx_afi
]->text
);
660 return CMD_WARNING_CONFIG_FAILED
;
664 if ((area
->is_type
& level
) != level
) {
665 vty_out(vty
, "Node is not a level-%d IS\n", level
);
666 return CMD_WARNING_CONFIG_FAILED
;
669 if (argc
> idx_always
&& strmatch(argv
[idx_always
]->text
, "always")) {
670 originate_type
= DEFAULT_ORIGINATE_ALWAYS
;
674 if (argv_find(argv
, argc
, "metric", &idx_metric_rmap
)) {
675 metric
= strtoul(argv
[idx_metric_rmap
+ 1]->arg
, NULL
, 10);
679 if (argv_find(argv
, argc
, "route-map", &idx_metric_rmap
)) {
680 routemap
= argv
[idx_metric_rmap
+ 1]->arg
;
683 if (family
== AF_INET6
&& originate_type
!= DEFAULT_ORIGINATE_ALWAYS
) {
685 "Zebra doesn't implement default-originate for IPv6 yet\n");
687 "so use with care or use default-originate always.\n");
690 isis_redist_set(area
, level
, family
, DEFAULT_ROUTE
, metric
, routemap
,
695 DEFUN (no_isis_default_originate
,
696 no_isis_default_originate_cmd
,
697 "no default-information originate <ipv4|ipv6>",
699 "Control distribution of default information\n"
700 "Distribute a default route\n"
701 "Distribute default route for IPv4\n"
702 "Distribute default route for IPv6\n")
705 VTY_DECLVAR_CONTEXT(isis_area
, area
);
709 family
= str2family(argv
[idx_afi
]->text
);
711 return CMD_WARNING_CONFIG_FAILED
;
715 isis_redist_unset(area
, level
, family
, DEFAULT_ROUTE
);
718 #endif /* ifdef FABRICD */
720 int isis_redist_config_write(struct vty
*vty
, struct isis_area
*area
,
726 struct isis_redist
*redist
;
727 const char *family_str
;
729 if (family
== AF_INET
)
731 else if (family
== AF_INET6
)
736 for (type
= 0; type
< ZEBRA_ROUTE_MAX
; type
++) {
737 if (type
== PROTO_TYPE
)
740 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
741 redist
= get_redist_settings(area
, family
, type
, level
);
744 vty_out(vty
, " redistribute %s %s", family_str
,
745 zebra_route_string(type
));
747 vty_out(vty
, " level-%d", level
);
749 vty_out(vty
, " metric %u", redist
->metric
);
750 if (redist
->map_name
)
751 vty_out(vty
, " route-map %s", redist
->map_name
);
757 for (level
= 1; level
<= ISIS_LEVELS
; level
++) {
759 get_redist_settings(area
, family
, DEFAULT_ROUTE
, level
);
762 vty_out(vty
, " default-information originate %s",
765 vty_out(vty
, " level-%d", level
);
766 if (redist
->redist
== DEFAULT_ORIGINATE_ALWAYS
)
767 vty_out(vty
, " always");
769 vty_out(vty
, " metric %u", redist
->metric
);
770 if (redist
->map_name
)
771 vty_out(vty
, " route-map %s", redist
->map_name
);
779 void isis_redist_init(void)
782 install_element(ROUTER_NODE
, &isis_redistribute_cmd
);
783 install_element(ROUTER_NODE
, &no_isis_redistribute_cmd
);
785 install_element(ROUTER_NODE
, &isis_default_originate_cmd
);
786 install_element(ROUTER_NODE
, &no_isis_default_originate_cmd
);
787 #endif /* ifdef FABRICD */