]>
Commit | Line | Data |
---|---|---|
f3ccedaa CF |
1 | /* |
2 | * IS-IS Rout(e)ing protocol - isis_redist.c | |
3 | * | |
4 | * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org> | |
5 | * | |
d62a17ae | 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) | |
f3ccedaa CF |
9 | * any later version. |
10 | * | |
d62a17ae | 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 | |
f3ccedaa | 14 | * more details. |
896014f4 DL |
15 | * |
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 | |
f3ccedaa CF |
19 | */ |
20 | ||
21 | #include <zebra.h> | |
22 | ||
23 | #include "command.h" | |
24 | #include "if.h" | |
25 | #include "linklist.h" | |
26 | #include "memory.h" | |
4a1ab8e4 | 27 | #include "isis_memory.h" |
f3ccedaa CF |
28 | #include "prefix.h" |
29 | #include "routemap.h" | |
30 | #include "stream.h" | |
31 | #include "table.h" | |
32 | #include "vty.h" | |
33 | ||
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" | |
f3ccedaa CF |
40 | #include "isisd/isisd.h" |
41 | #include "isisd/isis_lsp.h" | |
42 | #include "isisd/isis_route.h" | |
43 | #include "isisd/isis_zebra.h" | |
44 | ||
d62a17ae | 45 | static int redist_protocol(int family) |
f3ccedaa | 46 | { |
d62a17ae | 47 | if (family == AF_INET) |
48 | return 0; | |
49 | if (family == AF_INET6) | |
50 | return 1; | |
f3ccedaa | 51 | |
d62a17ae | 52 | assert(!"Unsupported address family!"); |
53 | return 0; | |
f3ccedaa CF |
54 | } |
55 | ||
d62a17ae | 56 | static afi_t afi_for_redist_protocol(int protocol) |
113b1946 | 57 | { |
d62a17ae | 58 | if (protocol == 0) |
59 | return AFI_IP; | |
60 | if (protocol == 1) | |
61 | return AFI_IP6; | |
113b1946 | 62 | |
d62a17ae | 63 | assert(!"Unknown redist protocol!"); |
64 | return AFI_IP; | |
113b1946 CF |
65 | } |
66 | ||
d62a17ae | 67 | static int is_default(struct prefix *p) |
f3ccedaa | 68 | { |
d62a17ae | 69 | if (p->family == AF_INET) |
70 | if (p->u.prefix4.s_addr == 0 && p->prefixlen == 0) | |
71 | return 1; | |
72 | if (p->family == AF_INET6) | |
73 | if (IN6_IS_ADDR_UNSPECIFIED(&p->u.prefix6) && p->prefixlen == 0) | |
74 | return 1; | |
75 | return 0; | |
f3ccedaa CF |
76 | } |
77 | ||
d62a17ae | 78 | static struct route_table *get_ext_info(struct isis *i, int family) |
f3ccedaa | 79 | { |
d62a17ae | 80 | int protocol = redist_protocol(family); |
f3ccedaa | 81 | |
d62a17ae | 82 | return i->ext_info[protocol]; |
f3ccedaa CF |
83 | } |
84 | ||
d62a17ae | 85 | static struct isis_redist *get_redist_settings(struct isis_area *area, |
86 | int family, int type, int level) | |
f3ccedaa | 87 | { |
d62a17ae | 88 | int protocol = redist_protocol(family); |
f3ccedaa | 89 | |
d62a17ae | 90 | return &area->redist_settings[protocol][type][level - 1]; |
f3ccedaa CF |
91 | } |
92 | ||
d62a17ae | 93 | struct route_table *get_ext_reach(struct isis_area *area, int family, int level) |
f3ccedaa | 94 | { |
d62a17ae | 95 | int protocol = redist_protocol(family); |
f3ccedaa | 96 | |
d62a17ae | 97 | return area->ext_reach[protocol][level - 1]; |
f3ccedaa CF |
98 | } |
99 | ||
100 | static struct route_node * | |
101 | isis_redist_route_node_create(route_table_delegate_t *delegate, | |
d62a17ae | 102 | struct route_table *table) |
f3ccedaa | 103 | { |
d62a17ae | 104 | struct route_node *node; |
105 | node = XCALLOC(MTYPE_ISIS_EXT_ROUTE, sizeof(*node)); | |
106 | return node; | |
f3ccedaa CF |
107 | } |
108 | ||
d62a17ae | 109 | static void isis_redist_route_node_destroy(route_table_delegate_t *delegate, |
110 | struct route_table *table, | |
111 | struct route_node *node) | |
f3ccedaa | 112 | { |
d62a17ae | 113 | if (node->info) |
114 | XFREE(MTYPE_ISIS_EXT_INFO, node->info); | |
115 | XFREE(MTYPE_ISIS_EXT_ROUTE, node); | |
f3ccedaa CF |
116 | } |
117 | ||
118 | static route_table_delegate_t isis_redist_rt_delegate = { | |
d62a17ae | 119 | .create_node = isis_redist_route_node_create, |
120 | .destroy_node = isis_redist_route_node_destroy}; | |
f3ccedaa CF |
121 | |
122 | /* Install external reachability information into a | |
123 | * specific area for a specific level. | |
124 | * Schedule an lsp regenerate if necessary */ | |
d62a17ae | 125 | static void isis_redist_install(struct isis_area *area, int level, |
126 | struct prefix *p, struct isis_ext_info *info) | |
f3ccedaa | 127 | { |
d62a17ae | 128 | int family = p->family; |
129 | struct route_table *er_table = get_ext_reach(area, family, level); | |
130 | struct route_node *er_node; | |
131 | ||
132 | if (!er_table) { | |
133 | zlog_warn( | |
134 | "%s: External reachability table of area %s" | |
135 | " is not initialized.", | |
136 | __func__, area->area_tag); | |
137 | return; | |
138 | } | |
139 | ||
140 | er_node = route_node_get(er_table, p); | |
141 | if (er_node->info) { | |
142 | route_unlock_node(er_node); | |
143 | ||
144 | /* Don't update/reschedule lsp generation if nothing changed. */ | |
145 | if (!memcmp(er_node->info, info, sizeof(*info))) | |
146 | return; | |
147 | } else { | |
148 | er_node->info = XMALLOC(MTYPE_ISIS_EXT_INFO, sizeof(*info)); | |
149 | } | |
150 | ||
151 | memcpy(er_node->info, info, sizeof(*info)); | |
152 | lsp_regenerate_schedule(area, level, 0); | |
f3ccedaa CF |
153 | } |
154 | ||
155 | /* Remove external reachability information from a | |
156 | * specific area for a specific level. | |
157 | * Schedule an lsp regenerate if necessary. */ | |
d62a17ae | 158 | static void isis_redist_uninstall(struct isis_area *area, int level, |
159 | struct prefix *p) | |
f3ccedaa | 160 | { |
d62a17ae | 161 | int family = p->family; |
162 | struct route_table *er_table = get_ext_reach(area, family, level); | |
163 | struct route_node *er_node; | |
164 | ||
165 | if (!er_table) { | |
166 | zlog_warn( | |
167 | "%s: External reachability table of area %s" | |
168 | " is not initialized.", | |
169 | __func__, area->area_tag); | |
170 | return; | |
171 | } | |
172 | ||
173 | er_node = route_node_lookup(er_table, p); | |
174 | if (!er_node) | |
175 | return; | |
176 | else | |
177 | route_unlock_node(er_node); | |
178 | ||
179 | if (!er_node->info) | |
180 | return; | |
181 | ||
182 | XFREE(MTYPE_ISIS_EXT_INFO, er_node->info); | |
183 | route_unlock_node(er_node); | |
184 | lsp_regenerate_schedule(area, level, 0); | |
f3ccedaa CF |
185 | } |
186 | ||
187 | /* Update external reachability info of area for a given level | |
188 | * and prefix, using the given redistribution settings. */ | |
d62a17ae | 189 | static void isis_redist_update_ext_reach(struct isis_area *area, int level, |
190 | struct isis_redist *redist, | |
191 | struct prefix *p, | |
192 | struct isis_ext_info *info) | |
f3ccedaa | 193 | { |
d62a17ae | 194 | struct isis_ext_info area_info; |
195 | route_map_result_t map_ret; | |
196 | ||
197 | memcpy(&area_info, info, sizeof(area_info)); | |
198 | if (redist->metric != 0xffffffff) | |
199 | area_info.metric = redist->metric; | |
200 | ||
201 | if (redist->map_name) { | |
202 | map_ret = | |
203 | route_map_apply(redist->map, p, RMAP_ISIS, &area_info); | |
204 | if (map_ret == RMAP_DENYMATCH) | |
205 | area_info.distance = 255; | |
206 | } | |
207 | ||
208 | /* Allow synthesized default routes only on always orignate */ | |
209 | if (area_info.origin == DEFAULT_ROUTE | |
210 | && redist->redist != DEFAULT_ORIGINATE_ALWAYS) | |
211 | area_info.distance = 255; | |
212 | ||
213 | if (area_info.distance < 255) | |
214 | isis_redist_install(area, level, p, &area_info); | |
215 | else | |
216 | isis_redist_uninstall(area, level, p); | |
f3ccedaa CF |
217 | } |
218 | ||
d62a17ae | 219 | static void isis_redist_ensure_default(struct isis *isis, int family) |
f3ccedaa | 220 | { |
d62a17ae | 221 | struct prefix p; |
222 | struct route_table *ei_table = get_ext_info(isis, family); | |
223 | struct route_node *ei_node; | |
224 | struct isis_ext_info *info; | |
225 | ||
226 | if (family == AF_INET) { | |
227 | p.family = AF_INET; | |
228 | p.prefixlen = 0; | |
229 | memset(&p.u.prefix4, 0, sizeof(p.u.prefix4)); | |
230 | } else if (family == AF_INET6) { | |
231 | p.family = AF_INET6; | |
232 | p.prefixlen = 0; | |
233 | memset(&p.u.prefix6, 0, sizeof(p.u.prefix6)); | |
234 | } else | |
235 | assert(!"Unknown family!"); | |
236 | ||
237 | ei_node = route_node_get(ei_table, &p); | |
238 | if (ei_node->info) { | |
239 | route_unlock_node(ei_node); | |
240 | return; | |
241 | } | |
242 | ||
243 | ei_node->info = | |
244 | XCALLOC(MTYPE_ISIS_EXT_INFO, sizeof(struct isis_ext_info)); | |
245 | ||
246 | info = ei_node->info; | |
247 | info->origin = DEFAULT_ROUTE; | |
248 | info->distance = 254; | |
249 | info->metric = MAX_WIDE_PATH_METRIC; | |
f3ccedaa CF |
250 | } |
251 | ||
252 | /* Handle notification about route being added */ | |
d62a17ae | 253 | void isis_redist_add(int type, struct prefix *p, u_char distance, |
254 | uint32_t metric) | |
f3ccedaa | 255 | { |
d62a17ae | 256 | int family = p->family; |
257 | struct route_table *ei_table = get_ext_info(isis, family); | |
258 | struct route_node *ei_node; | |
259 | struct isis_ext_info *info; | |
260 | struct listnode *node; | |
261 | struct isis_area *area; | |
262 | int level; | |
263 | struct isis_redist *redist; | |
264 | ||
265 | char debug_buf[BUFSIZ]; | |
266 | prefix2str(p, debug_buf, sizeof(debug_buf)); | |
267 | ||
268 | zlog_debug("%s: New route %s from %s.", __func__, debug_buf, | |
269 | zebra_route_string(type)); | |
270 | ||
271 | if (!ei_table) { | |
272 | zlog_warn("%s: External information table not initialized.", | |
273 | __func__); | |
274 | return; | |
275 | } | |
276 | ||
277 | ei_node = route_node_get(ei_table, p); | |
278 | if (ei_node->info) | |
279 | route_unlock_node(ei_node); | |
280 | else | |
281 | ei_node->info = XCALLOC(MTYPE_ISIS_EXT_INFO, | |
282 | sizeof(struct isis_ext_info)); | |
283 | ||
284 | info = ei_node->info; | |
285 | info->origin = type; | |
286 | info->distance = distance; | |
287 | info->metric = metric; | |
288 | ||
289 | if (is_default(p)) | |
290 | type = DEFAULT_ROUTE; | |
291 | ||
292 | for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) | |
293 | for (level = 1; level <= ISIS_LEVELS; level++) { | |
294 | redist = get_redist_settings(area, family, type, level); | |
295 | if (!redist->redist) | |
296 | continue; | |
297 | ||
298 | isis_redist_update_ext_reach(area, level, redist, p, | |
299 | info); | |
300 | } | |
f3ccedaa CF |
301 | } |
302 | ||
d62a17ae | 303 | void isis_redist_delete(int type, struct prefix *p) |
f3ccedaa | 304 | { |
d62a17ae | 305 | int family = p->family; |
306 | struct route_table *ei_table = get_ext_info(isis, family); | |
307 | struct route_node *ei_node; | |
308 | struct listnode *node; | |
309 | struct isis_area *area; | |
310 | int level; | |
311 | struct isis_redist *redist; | |
312 | ||
313 | char debug_buf[BUFSIZ]; | |
314 | prefix2str(p, debug_buf, sizeof(debug_buf)); | |
315 | ||
316 | zlog_debug("%s: Removing route %s from %s.", __func__, debug_buf, | |
317 | zebra_route_string(type)); | |
318 | ||
319 | if (is_default(p)) { | |
320 | /* Don't remove default route but add synthetic route for use | |
321 | * by "default-information originate always". Areas without the | |
322 | * "always" setting will ignore routes with origin | |
323 | * DEFAULT_ROUTE. */ | |
324 | isis_redist_add(DEFAULT_ROUTE, p, 254, MAX_WIDE_PATH_METRIC); | |
325 | return; | |
326 | } | |
327 | ||
328 | if (!ei_table) { | |
329 | zlog_warn("%s: External information table not initialized.", | |
330 | __func__); | |
331 | return; | |
332 | } | |
333 | ||
334 | ei_node = route_node_lookup(ei_table, p); | |
335 | if (!ei_node || !ei_node->info) { | |
336 | char buf[BUFSIZ]; | |
337 | prefix2str(p, buf, sizeof(buf)); | |
338 | zlog_warn( | |
339 | "%s: Got a delete for %s route %s, but that route" | |
340 | " was never added.", | |
341 | __func__, zebra_route_string(type), buf); | |
342 | if (ei_node) | |
343 | route_unlock_node(ei_node); | |
344 | return; | |
345 | } | |
346 | route_unlock_node(ei_node); | |
347 | ||
348 | for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) | |
349 | for (level = 1; level < ISIS_LEVELS; level++) { | |
350 | redist = get_redist_settings(area, family, type, level); | |
351 | if (!redist->redist) | |
352 | continue; | |
353 | ||
354 | isis_redist_uninstall(area, level, p); | |
355 | } | |
356 | ||
357 | XFREE(MTYPE_ISIS_EXT_INFO, ei_node->info); | |
358 | route_unlock_node(ei_node); | |
f3ccedaa CF |
359 | } |
360 | ||
d62a17ae | 361 | static void isis_redist_routemap_set(struct isis_redist *redist, |
362 | const char *routemap) | |
f3ccedaa | 363 | { |
d62a17ae | 364 | if (redist->map_name) { |
365 | XFREE(MTYPE_ISIS, redist->map_name); | |
366 | redist->map = NULL; | |
367 | } | |
368 | ||
369 | if (routemap && strlen(routemap)) { | |
370 | redist->map_name = XSTRDUP(MTYPE_ISIS, routemap); | |
371 | redist->map = route_map_lookup_by_name(routemap); | |
372 | } | |
f3ccedaa CF |
373 | } |
374 | ||
d62a17ae | 375 | static void isis_redist_update_zebra_subscriptions(struct isis *isis) |
f3ccedaa | 376 | { |
d62a17ae | 377 | struct listnode *node; |
378 | struct isis_area *area; | |
379 | int type; | |
380 | int level; | |
381 | int protocol; | |
382 | ||
383 | char do_subscribe[REDIST_PROTOCOL_COUNT][ZEBRA_ROUTE_MAX + 1]; | |
384 | ||
385 | memset(do_subscribe, 0, sizeof(do_subscribe)); | |
386 | ||
387 | for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) | |
388 | for (protocol = 0; protocol < REDIST_PROTOCOL_COUNT; protocol++) | |
389 | for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++) | |
390 | for (level = 0; level < ISIS_LEVELS; level++) | |
9d303b37 DL |
391 | if (area->redist_settings[protocol] |
392 | [type] | |
393 | [level].redist) | |
d62a17ae | 394 | do_subscribe[protocol][type] = |
395 | 1; | |
396 | ||
397 | for (protocol = 0; protocol < REDIST_PROTOCOL_COUNT; protocol++) | |
398 | for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++) { | |
399 | /* This field is actually controlling transmission of | |
400 | * the IS-IS | |
401 | * routes to Zebra and has nothing to do with | |
402 | * redistribution, | |
403 | * so skip it. */ | |
404 | if (type == ZEBRA_ROUTE_ISIS) | |
405 | continue; | |
406 | ||
407 | afi_t afi = afi_for_redist_protocol(protocol); | |
408 | ||
409 | if (do_subscribe[protocol][type]) | |
410 | isis_zebra_redistribute_set(afi, type); | |
411 | else | |
412 | isis_zebra_redistribute_unset(afi, type); | |
413 | } | |
f3ccedaa CF |
414 | } |
415 | ||
d62a17ae | 416 | static void isis_redist_set(struct isis_area *area, int level, int family, |
417 | int type, uint32_t metric, const char *routemap, | |
418 | int originate_type) | |
f3ccedaa | 419 | { |
d62a17ae | 420 | int protocol = redist_protocol(family); |
421 | struct isis_redist *redist = | |
422 | get_redist_settings(area, family, type, level); | |
423 | int i; | |
424 | struct route_table *ei_table; | |
425 | struct route_node *rn; | |
426 | struct isis_ext_info *info; | |
427 | ||
428 | redist->redist = (type == DEFAULT_ROUTE) ? originate_type : 1; | |
429 | redist->metric = metric; | |
430 | isis_redist_routemap_set(redist, routemap); | |
431 | ||
432 | if (!area->ext_reach[protocol][level - 1]) { | |
433 | area->ext_reach[protocol][level - 1] = | |
434 | route_table_init_with_delegate( | |
435 | &isis_redist_rt_delegate); | |
436 | } | |
437 | ||
438 | for (i = 0; i < REDIST_PROTOCOL_COUNT; i++) | |
439 | if (!area->isis->ext_info[i]) { | |
440 | area->isis->ext_info[i] = | |
441 | route_table_init_with_delegate( | |
442 | &isis_redist_rt_delegate); | |
443 | } | |
444 | ||
445 | isis_redist_update_zebra_subscriptions(area->isis); | |
446 | ||
447 | if (type == DEFAULT_ROUTE && originate_type == DEFAULT_ORIGINATE_ALWAYS) | |
448 | isis_redist_ensure_default(area->isis, family); | |
449 | ||
450 | ei_table = get_ext_info(area->isis, family); | |
451 | for (rn = route_top(ei_table); rn; rn = route_next(rn)) { | |
452 | if (!rn->info) | |
453 | continue; | |
454 | info = rn->info; | |
455 | ||
456 | if (type == DEFAULT_ROUTE) { | |
457 | if (!is_default(&rn->p)) | |
458 | continue; | |
459 | } else { | |
460 | if (info->origin != type) | |
461 | continue; | |
462 | } | |
463 | ||
464 | isis_redist_update_ext_reach(area, level, redist, &rn->p, info); | |
465 | } | |
f3ccedaa CF |
466 | } |
467 | ||
d62a17ae | 468 | static void isis_redist_unset(struct isis_area *area, int level, int family, |
469 | int type) | |
f3ccedaa | 470 | { |
d62a17ae | 471 | struct isis_redist *redist = |
472 | get_redist_settings(area, family, type, level); | |
473 | struct route_table *er_table = get_ext_reach(area, family, level); | |
474 | struct route_node *rn; | |
475 | struct isis_ext_info *info; | |
476 | ||
477 | if (!redist->redist) | |
478 | return; | |
479 | ||
480 | redist->redist = 0; | |
481 | if (!er_table) { | |
482 | zlog_warn("%s: External reachability table uninitialized.", | |
483 | __func__); | |
484 | return; | |
485 | } | |
486 | ||
487 | for (rn = route_top(er_table); rn; rn = route_next(rn)) { | |
488 | if (!rn->info) | |
489 | continue; | |
490 | info = rn->info; | |
491 | ||
492 | if (type == DEFAULT_ROUTE) { | |
493 | if (!is_default(&rn->p)) | |
494 | continue; | |
495 | } else { | |
496 | if (info->origin != type) | |
497 | continue; | |
498 | } | |
499 | ||
500 | XFREE(MTYPE_ISIS_EXT_INFO, rn->info); | |
501 | route_unlock_node(rn); | |
502 | } | |
503 | ||
504 | lsp_regenerate_schedule(area, level, 0); | |
505 | isis_redist_update_zebra_subscriptions(area->isis); | |
f3ccedaa CF |
506 | } |
507 | ||
d62a17ae | 508 | void isis_redist_area_finish(struct isis_area *area) |
f3ccedaa | 509 | { |
d62a17ae | 510 | int protocol; |
511 | int level; | |
512 | int type; | |
513 | ||
514 | for (protocol = 0; protocol < REDIST_PROTOCOL_COUNT; protocol++) | |
515 | for (level = 0; level < ISIS_LEVELS; level++) { | |
516 | for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++) { | |
517 | struct isis_redist *redist; | |
518 | ||
519 | redist = &area->redist_settings[protocol][type] | |
520 | [level]; | |
521 | redist->redist = 0; | |
522 | if (redist->map_name) | |
523 | XFREE(MTYPE_ISIS, redist->map_name); | |
524 | } | |
525 | route_table_finish(area->ext_reach[protocol][level]); | |
526 | } | |
527 | ||
528 | isis_redist_update_zebra_subscriptions(area->isis); | |
f3ccedaa CF |
529 | } |
530 | ||
49d41a26 DS |
531 | DEFUN (isis_redistribute, |
532 | isis_redistribute_cmd, | |
cd35442a | 533 | "redistribute <ipv4|ipv6> " FRR_REDIST_STR_ISISD " <level-1|level-2> [<metric (0-16777215)|route-map WORD>]", |
49d41a26 DS |
534 | REDIST_STR |
535 | "Redistribute IPv4 routes\n" | |
536 | "Redistribute IPv6 routes\n" | |
ab0181ee | 537 | FRR_REDIST_HELP_STR_ISISD |
49d41a26 DS |
538 | "Redistribute into level-1\n" |
539 | "Redistribute into level-2\n" | |
540 | "Metric for redistributed routes\n" | |
541 | "ISIS default metric\n" | |
542 | "Route map reference\n" | |
543 | "Pointer to route-map entries\n") | |
f3ccedaa | 544 | { |
d62a17ae | 545 | int idx_afi = 1; |
546 | int idx_protocol = 2; | |
547 | int idx_level = 3; | |
548 | int idx_metric_rmap = 4; | |
549 | VTY_DECLVAR_CONTEXT(isis_area, area); | |
550 | int family; | |
551 | int afi; | |
552 | int type; | |
553 | int level; | |
554 | unsigned long metric; | |
555 | const char *routemap = NULL; | |
556 | ||
557 | family = str2family(argv[idx_afi]->text); | |
558 | if (family < 0) | |
559 | return CMD_WARNING_CONFIG_FAILED; | |
560 | ||
561 | afi = family2afi(family); | |
562 | if (!afi) | |
563 | return CMD_WARNING_CONFIG_FAILED; | |
564 | ||
565 | type = proto_redistnum(afi, argv[idx_protocol]->text); | |
566 | if (type < 0) | |
567 | return CMD_WARNING_CONFIG_FAILED; | |
568 | ||
569 | if (!strcmp("level-1", argv[idx_level]->arg)) | |
570 | level = 1; | |
571 | else if (!strcmp("level-2", argv[idx_level]->arg)) | |
572 | level = 2; | |
573 | else | |
574 | return CMD_WARNING_CONFIG_FAILED; | |
575 | ||
576 | if ((area->is_type & level) != level) { | |
577 | vty_out(vty, "Node is not a level-%d IS\n", level); | |
578 | return CMD_WARNING_CONFIG_FAILED; | |
579 | } | |
580 | ||
581 | metric = 0xffffffff; | |
582 | routemap = NULL; | |
583 | ||
584 | if (argc > idx_metric_rmap + 1) { | |
585 | if (argv[idx_metric_rmap + 1]->arg[0] == '\0') | |
586 | return CMD_WARNING_CONFIG_FAILED; | |
587 | ||
588 | if (strmatch(argv[idx_metric_rmap]->text, "metric")) { | |
589 | char *endp; | |
590 | metric = strtoul(argv[idx_metric_rmap + 1]->arg, &endp, | |
591 | 10); | |
592 | ||
593 | if (*endp != '\0') | |
594 | return CMD_WARNING_CONFIG_FAILED; | |
595 | } else { | |
596 | routemap = argv[idx_metric_rmap + 1]->arg; | |
597 | } | |
598 | } | |
599 | ||
600 | isis_redist_set(area, level, family, type, metric, routemap, 0); | |
601 | return 0; | |
f3ccedaa CF |
602 | } |
603 | ||
49d41a26 DS |
604 | DEFUN (no_isis_redistribute, |
605 | no_isis_redistribute_cmd, | |
cd35442a | 606 | "no redistribute <ipv4|ipv6> " FRR_REDIST_STR_ISISD " <level-1|level-2>", |
49d41a26 DS |
607 | NO_STR |
608 | REDIST_STR | |
609 | "Redistribute IPv4 routes\n" | |
610 | "Redistribute IPv6 routes\n" | |
ab0181ee | 611 | FRR_REDIST_HELP_STR_ISISD |
49d41a26 DS |
612 | "Redistribute into level-1\n" |
613 | "Redistribute into level-2\n") | |
f3ccedaa | 614 | { |
d62a17ae | 615 | int idx_afi = 2; |
616 | int idx_protocol = 3; | |
617 | int idx_level = 4; | |
618 | VTY_DECLVAR_CONTEXT(isis_area, area); | |
619 | int type; | |
620 | int level; | |
621 | int family; | |
622 | int afi; | |
623 | ||
624 | family = str2family(argv[idx_afi]->arg); | |
625 | if (family < 0) | |
626 | return CMD_WARNING_CONFIG_FAILED; | |
627 | ||
628 | afi = family2afi(family); | |
629 | if (!afi) | |
630 | return CMD_WARNING_CONFIG_FAILED; | |
631 | ||
632 | type = proto_redistnum(afi, argv[idx_protocol]->text); | |
633 | if (type < 0) | |
634 | return CMD_WARNING_CONFIG_FAILED; | |
635 | ||
636 | level = strmatch("level-1", argv[idx_level]->text) ? 1 : 2; | |
637 | ||
638 | isis_redist_unset(area, level, family, type); | |
639 | return 0; | |
f3ccedaa CF |
640 | } |
641 | ||
49d41a26 DS |
642 | DEFUN (isis_default_originate, |
643 | isis_default_originate_cmd, | |
cd35442a | 644 | "default-information originate <ipv4|ipv6> <level-1|level-2> [always] [<metric (0-16777215)|route-map WORD>]", |
49d41a26 DS |
645 | "Control distribution of default information\n" |
646 | "Distribute a default route\n" | |
647 | "Distribute default route for IPv4\n" | |
648 | "Distribute default route for IPv6\n" | |
649 | "Distribute default route into level-1\n" | |
650 | "Distribute default route into level-2\n" | |
651 | "Always advertise default route\n" | |
652 | "Metric for default route\n" | |
653 | "ISIS default metric\n" | |
654 | "Route map reference\n" | |
655 | "Pointer to route-map entries\n") | |
f3ccedaa | 656 | { |
d62a17ae | 657 | int idx_afi = 2; |
658 | int idx_level = 3; | |
659 | int idx_always = 4; | |
660 | int idx_metric_rmap = 4; | |
661 | VTY_DECLVAR_CONTEXT(isis_area, area); | |
662 | int family; | |
663 | int originate_type = DEFAULT_ORIGINATE; | |
664 | int level; | |
665 | unsigned long metric = 0xffffffff; | |
666 | const char *routemap = NULL; | |
667 | ||
668 | family = str2family(argv[idx_afi]->text); | |
669 | if (family < 0) | |
670 | return CMD_WARNING_CONFIG_FAILED; | |
671 | ||
672 | level = strmatch("level-1", argv[idx_level]->text) ? 1 : 2; | |
673 | ||
674 | if ((area->is_type & level) != level) { | |
675 | vty_out(vty, "Node is not a level-%d IS\n", level); | |
676 | return CMD_WARNING_CONFIG_FAILED; | |
677 | } | |
678 | ||
679 | if (argc > idx_always && strmatch(argv[idx_always]->text, "always")) { | |
680 | originate_type = DEFAULT_ORIGINATE_ALWAYS; | |
681 | idx_metric_rmap++; | |
682 | } | |
683 | ||
684 | if (argc > idx_metric_rmap) { | |
685 | if (strmatch(argv[idx_metric_rmap]->text, "metric")) | |
686 | metric = strtoul(argv[idx_metric_rmap + 1]->arg, NULL, | |
687 | 10); | |
688 | else | |
689 | routemap = argv[idx_metric_rmap + 1]->arg; | |
690 | } | |
691 | ||
692 | if (family == AF_INET6 && originate_type != DEFAULT_ORIGINATE_ALWAYS) { | |
693 | vty_out(vty, | |
694 | "Zebra doesn't implement default-originate for IPv6 yet\n"); | |
695 | vty_out(vty, | |
696 | "so use with care or use default-originate always.\n"); | |
697 | } | |
698 | ||
699 | isis_redist_set(area, level, family, DEFAULT_ROUTE, metric, routemap, | |
700 | originate_type); | |
701 | return 0; | |
f3ccedaa CF |
702 | } |
703 | ||
49d41a26 DS |
704 | DEFUN (no_isis_default_originate, |
705 | no_isis_default_originate_cmd, | |
6147e2c6 | 706 | "no default-information originate <ipv4|ipv6> <level-1|level-2>", |
49d41a26 DS |
707 | NO_STR |
708 | "Control distribution of default information\n" | |
709 | "Distribute a default route\n" | |
710 | "Distribute default route for IPv4\n" | |
711 | "Distribute default route for IPv6\n" | |
712 | "Distribute default route into level-1\n" | |
713 | "Distribute default route into level-2\n") | |
f3ccedaa | 714 | { |
d62a17ae | 715 | int idx_afi = 3; |
716 | int idx_level = 4; | |
717 | VTY_DECLVAR_CONTEXT(isis_area, area); | |
718 | int family; | |
719 | int level; | |
720 | ||
721 | family = str2family(argv[idx_afi]->text); | |
722 | if (family < 0) | |
723 | return CMD_WARNING_CONFIG_FAILED; | |
724 | ||
725 | if (strmatch("level-1", argv[idx_level]->text)) | |
726 | level = 1; | |
727 | else if (strmatch("level-2", argv[idx_level]->text)) | |
728 | level = 2; | |
729 | else | |
730 | return CMD_WARNING_CONFIG_FAILED; | |
731 | ||
732 | isis_redist_unset(area, level, family, DEFAULT_ROUTE); | |
733 | return 0; | |
f3ccedaa CF |
734 | } |
735 | ||
d62a17ae | 736 | int isis_redist_config_write(struct vty *vty, struct isis_area *area, |
737 | int family) | |
f3ccedaa | 738 | { |
d62a17ae | 739 | int type; |
740 | int level; | |
741 | int write = 0; | |
742 | struct isis_redist *redist; | |
743 | const char *family_str; | |
744 | ||
745 | if (family == AF_INET) | |
746 | family_str = "ipv4"; | |
747 | else if (family == AF_INET6) | |
748 | family_str = "ipv6"; | |
749 | else | |
750 | return 0; | |
751 | ||
752 | for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { | |
753 | if (type == ZEBRA_ROUTE_ISIS) | |
754 | continue; | |
755 | ||
756 | for (level = 1; level <= ISIS_LEVELS; level++) { | |
757 | redist = get_redist_settings(area, family, type, level); | |
758 | if (!redist->redist) | |
759 | continue; | |
760 | vty_out(vty, " redistribute %s %s level-%d", family_str, | |
761 | zebra_route_string(type), level); | |
762 | if (redist->metric != 0xffffffff) | |
763 | vty_out(vty, " metric %u", redist->metric); | |
764 | if (redist->map_name) | |
765 | vty_out(vty, " route-map %s", redist->map_name); | |
766 | vty_out(vty, "\n"); | |
767 | write++; | |
768 | } | |
769 | } | |
770 | ||
771 | for (level = 1; level <= ISIS_LEVELS; level++) { | |
772 | redist = | |
773 | get_redist_settings(area, family, DEFAULT_ROUTE, level); | |
774 | if (!redist->redist) | |
775 | continue; | |
776 | vty_out(vty, " default-information originate %s level-%d", | |
777 | family_str, level); | |
778 | if (redist->redist == DEFAULT_ORIGINATE_ALWAYS) | |
779 | vty_out(vty, " always"); | |
780 | if (redist->metric != 0xffffffff) | |
781 | vty_out(vty, " metric %u", redist->metric); | |
782 | if (redist->map_name) | |
783 | vty_out(vty, " route-map %s", redist->map_name); | |
784 | vty_out(vty, "\n"); | |
785 | write++; | |
786 | } | |
787 | ||
788 | return write; | |
f3ccedaa CF |
789 | } |
790 | ||
d62a17ae | 791 | void isis_redist_init(void) |
f3ccedaa | 792 | { |
d62a17ae | 793 | install_element(ISIS_NODE, &isis_redistribute_cmd); |
794 | install_element(ISIS_NODE, &no_isis_redistribute_cmd); | |
795 | install_element(ISIS_NODE, &isis_default_originate_cmd); | |
796 | install_element(ISIS_NODE, &no_isis_default_originate_cmd); | |
f3ccedaa | 797 | } |