]>
Commit | Line | Data |
---|---|---|
7ee30f28 DS |
1 | /* |
2 | * Nexthop Group structure definition. | |
3 | * Copyright (C) 2018 Cumulus Networks, Inc. | |
4 | * Donald Sharp | |
5 | * | |
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) | |
9 | * any later version. | |
10 | * | |
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 | |
14 | * more details. | |
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 | |
19 | */ | |
20 | #include <zebra.h> | |
21 | ||
31919191 | 22 | #include <vrf.h> |
c57bd6bb | 23 | #include <sockunion.h> |
7ee30f28 DS |
24 | #include <nexthop.h> |
25 | #include <nexthop_group.h> | |
50d89650 | 26 | #include <nexthop_group_private.h> |
dba32923 DS |
27 | #include <vty.h> |
28 | #include <command.h> | |
1b1fe1c4 | 29 | #include <jhash.h> |
dba32923 DS |
30 | |
31 | #ifndef VTYSH_EXTRACT_PL | |
32 | #include "lib/nexthop_group_clippy.c" | |
33 | #endif | |
7ee30f28 | 34 | |
31919191 DS |
35 | DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group") |
36 | ||
db2fede9 MS |
37 | /* |
38 | * Internal struct used to hold nhg config strings | |
39 | */ | |
40 | struct nexthop_hold { | |
41 | char *nhvrf_name; | |
42 | union sockunion *addr; | |
43 | char *intf; | |
44 | char *labels; | |
597371a6 | 45 | uint32_t weight; |
0a8881b4 | 46 | int backup_idx; /* Index of backup nexthop, if >= 0 */ |
db2fede9 MS |
47 | }; |
48 | ||
0a8881b4 MS |
49 | /* Invalid/unset value for nexthop_hold's backup_idx */ |
50 | #define NHH_BACKUP_IDX_INVALID -1 | |
51 | ||
31919191 DS |
52 | struct nexthop_group_hooks { |
53 | void (*new)(const char *name); | |
54 | void (*add_nexthop)(const struct nexthop_group_cmd *nhg, | |
55 | const struct nexthop *nhop); | |
56 | void (*del_nexthop)(const struct nexthop_group_cmd *nhg, | |
57 | const struct nexthop *nhop); | |
58 | void (*delete)(const char *name); | |
59 | }; | |
60 | ||
61 | static struct nexthop_group_hooks nhg_hooks; | |
62 | ||
63 | static inline int | |
64 | nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, | |
65 | const struct nexthop_group_cmd *nhgc2); | |
66 | RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry, | |
67 | nexthop_group_cmd_compare) | |
68 | ||
c17faa4b | 69 | static struct nhgc_entry_head nhgc_entries; |
31919191 DS |
70 | |
71 | static inline int | |
72 | nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, | |
73 | const struct nexthop_group_cmd *nhgc2) | |
74 | { | |
75 | return strcmp(nhgc1->name, nhgc2->name); | |
76 | } | |
77 | ||
8c15fa95 SW |
78 | static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg) |
79 | { | |
80 | struct nexthop *nexthop = nhg->nexthop; | |
81 | ||
82 | while (nexthop && nexthop->next) | |
83 | nexthop = nexthop->next; | |
84 | ||
85 | return nexthop; | |
86 | } | |
87 | ||
454192f4 DS |
88 | uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg) |
89 | { | |
90 | struct nexthop *nhop; | |
91 | uint8_t num = 0; | |
92 | ||
93 | for (ALL_NEXTHOPS_PTR(nhg, nhop)) | |
94 | num++; | |
95 | ||
96 | return num; | |
97 | } | |
98 | ||
98cda54a SW |
99 | uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg) |
100 | { | |
101 | struct nexthop *nhop; | |
102 | uint8_t num = 0; | |
103 | ||
104 | for (nhop = nhg->nexthop; nhop; nhop = nhop->next) | |
105 | num++; | |
106 | ||
107 | return num; | |
108 | } | |
109 | ||
454192f4 DS |
110 | uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg) |
111 | { | |
112 | struct nexthop *nhop; | |
113 | uint8_t num = 0; | |
114 | ||
115 | for (ALL_NEXTHOPS_PTR(nhg, nhop)) { | |
116 | if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE)) | |
117 | num++; | |
118 | } | |
119 | ||
120 | return num; | |
121 | } | |
122 | ||
98cda54a SW |
123 | uint8_t |
124 | nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg) | |
125 | { | |
126 | struct nexthop *nhop; | |
127 | uint8_t num = 0; | |
128 | ||
129 | for (nhop = nhg->nexthop; nhop; nhop = nhop->next) { | |
130 | if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE)) | |
131 | num++; | |
132 | } | |
133 | ||
134 | return num; | |
135 | } | |
136 | ||
8f8d9845 SW |
137 | struct nexthop *nexthop_exists(const struct nexthop_group *nhg, |
138 | const struct nexthop *nh) | |
31919191 DS |
139 | { |
140 | struct nexthop *nexthop; | |
141 | ||
142 | for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { | |
143 | if (nexthop_same(nh, nexthop)) | |
144 | return nexthop; | |
145 | } | |
146 | ||
147 | return NULL; | |
148 | } | |
149 | ||
f17f2c5d SW |
150 | static bool |
151 | nexthop_group_equal_common(const struct nexthop_group *nhg1, | |
152 | const struct nexthop_group *nhg2, | |
153 | uint8_t (*nexthop_group_nexthop_num_func)( | |
154 | const struct nexthop_group *nhg)) | |
8f8d9845 | 155 | { |
8f8d9845 SW |
156 | if (nhg1 && !nhg2) |
157 | return false; | |
158 | ||
9c387098 | 159 | if (!nhg1 && nhg2) |
8f8d9845 SW |
160 | return false; |
161 | ||
dd9546e1 SW |
162 | if (nhg1 == nhg2) |
163 | return true; | |
164 | ||
f17f2c5d SW |
165 | if (nexthop_group_nexthop_num_func(nhg1) |
166 | != nexthop_group_nexthop_num_func(nhg2)) | |
167 | return false; | |
168 | ||
169 | return true; | |
170 | } | |
171 | ||
172 | /* This assumes ordered */ | |
173 | bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1, | |
174 | const struct nexthop_group *nhg2) | |
175 | { | |
176 | struct nexthop *nh1 = NULL; | |
177 | struct nexthop *nh2 = NULL; | |
178 | ||
179 | if (!nexthop_group_equal_common(nhg1, nhg2, | |
180 | &nexthop_group_nexthop_num_no_recurse)) | |
8f8d9845 SW |
181 | return false; |
182 | ||
2171b19c | 183 | for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2; |
12ec584d | 184 | nh1 = nh1->next, nh2 = nh2->next) { |
dd9546e1 SW |
185 | if (nh1 && !nh2) |
186 | return false; | |
187 | if (!nh1 && nh2) | |
188 | return false; | |
12ec584d | 189 | if (!nexthop_same(nh1, nh2)) |
8f8d9845 SW |
190 | return false; |
191 | } | |
192 | ||
193 | return true; | |
194 | } | |
195 | ||
2171b19c SW |
196 | /* This assumes ordered */ |
197 | bool nexthop_group_equal(const struct nexthop_group *nhg1, | |
198 | const struct nexthop_group *nhg2) | |
199 | { | |
200 | struct nexthop *nh1 = NULL; | |
201 | struct nexthop *nh2 = NULL; | |
202 | ||
f17f2c5d | 203 | if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num)) |
2171b19c SW |
204 | return false; |
205 | ||
206 | for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2; | |
207 | nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) { | |
dd9546e1 SW |
208 | if (nh1 && !nh2) |
209 | return false; | |
210 | if (!nh1 && nh2) | |
211 | return false; | |
2171b19c SW |
212 | if (!nexthop_same(nh1, nh2)) |
213 | return false; | |
214 | } | |
215 | ||
216 | return true; | |
217 | } | |
31919191 DS |
218 | struct nexthop_group *nexthop_group_new(void) |
219 | { | |
220 | return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group)); | |
221 | } | |
222 | ||
c13bfa74 MS |
223 | void nexthop_group_copy(struct nexthop_group *to, |
224 | const struct nexthop_group *from) | |
6c8b51e1 DS |
225 | { |
226 | /* Copy everything, including recursive info */ | |
227 | copy_nexthops(&to->nexthop, from->nexthop, NULL); | |
228 | } | |
229 | ||
31919191 | 230 | void nexthop_group_delete(struct nexthop_group **nhg) |
0c8215cb | 231 | { |
e4a1ec74 MS |
232 | /* OK to call with NULL group */ |
233 | if ((*nhg) == NULL) | |
234 | return; | |
235 | ||
0c8215cb SW |
236 | if ((*nhg)->nexthop) |
237 | nexthops_free((*nhg)->nexthop); | |
d3a35138 SW |
238 | |
239 | XFREE(MTYPE_NEXTHOP_GROUP, *nhg); | |
0c8215cb SW |
240 | } |
241 | ||
7ee30f28 | 242 | /* Add nexthop to the end of a nexthop list. */ |
50d89650 | 243 | void _nexthop_add(struct nexthop **target, struct nexthop *nexthop) |
7ee30f28 DS |
244 | { |
245 | struct nexthop *last; | |
246 | ||
247 | for (last = *target; last && last->next; last = last->next) | |
248 | ; | |
249 | if (last) | |
250 | last->next = nexthop; | |
251 | else | |
252 | *target = nexthop; | |
253 | nexthop->prev = last; | |
254 | } | |
255 | ||
e1f3a8eb MS |
256 | /* Add nexthop to sorted list of nexthops */ |
257 | static void _nexthop_add_sorted(struct nexthop **head, | |
258 | struct nexthop *nexthop) | |
6c8b51e1 | 259 | { |
e1f3a8eb | 260 | struct nexthop *position, *prev; |
8c15fa95 | 261 | |
89ca64c9 | 262 | assert(!nexthop->next); |
6c8b51e1 | 263 | |
e1f3a8eb | 264 | for (position = *head, prev = NULL; position; |
6c8b51e1 DS |
265 | prev = position, position = position->next) { |
266 | if (nexthop_cmp(position, nexthop) > 0) { | |
267 | nexthop->next = position; | |
268 | nexthop->prev = prev; | |
269 | ||
270 | if (nexthop->prev) | |
271 | nexthop->prev->next = nexthop; | |
272 | else | |
e1f3a8eb | 273 | *head = nexthop; |
6c8b51e1 DS |
274 | |
275 | position->prev = nexthop; | |
276 | return; | |
277 | } | |
278 | } | |
279 | ||
280 | nexthop->prev = prev; | |
281 | if (prev) | |
282 | prev->next = nexthop; | |
283 | else | |
e1f3a8eb MS |
284 | *head = nexthop; |
285 | } | |
286 | ||
287 | void nexthop_group_add_sorted(struct nexthop_group *nhg, | |
288 | struct nexthop *nexthop) | |
289 | { | |
290 | struct nexthop *tail; | |
291 | ||
89ca64c9 SW |
292 | assert(!nexthop->next); |
293 | ||
e1f3a8eb MS |
294 | /* Try to just append to the end first; |
295 | * trust the list is already sorted | |
296 | */ | |
297 | tail = nexthop_group_tail(nhg); | |
298 | ||
299 | if (tail && (nexthop_cmp(tail, nexthop) < 0)) { | |
300 | tail->next = nexthop; | |
301 | nexthop->prev = tail; | |
302 | ||
303 | return; | |
304 | } | |
305 | ||
306 | _nexthop_add_sorted(&nhg->nexthop, nexthop); | |
6c8b51e1 DS |
307 | } |
308 | ||
31919191 | 309 | /* Delete nexthop from a nexthop list. */ |
50d89650 | 310 | void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh) |
31919191 DS |
311 | { |
312 | struct nexthop *nexthop; | |
313 | ||
314 | for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { | |
315 | if (nexthop_same(nh, nexthop)) | |
316 | break; | |
317 | } | |
318 | ||
319 | assert(nexthop); | |
320 | ||
321 | if (nexthop->prev) | |
322 | nexthop->prev->next = nexthop->next; | |
323 | else | |
324 | nhg->nexthop = nexthop->next; | |
325 | ||
326 | if (nexthop->next) | |
327 | nexthop->next->prev = nexthop->prev; | |
ebee2bc4 DS |
328 | |
329 | nh->prev = NULL; | |
330 | nh->next = NULL; | |
31919191 DS |
331 | } |
332 | ||
e1f3a8eb MS |
333 | /* |
334 | * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order | |
335 | */ | |
336 | void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg, | |
337 | const struct nexthop *nh) | |
338 | { | |
339 | struct nexthop *nexthop, *tail; | |
340 | const struct nexthop *nh1; | |
341 | ||
342 | /* We'll try to append to the end of the new list; | |
343 | * if the original list in nh is already sorted, this eliminates | |
344 | * lots of comparison operations. | |
345 | */ | |
346 | tail = nexthop_group_tail(nhg); | |
347 | ||
348 | for (nh1 = nh; nh1; nh1 = nh1->next) { | |
349 | nexthop = nexthop_dup(nh1, NULL); | |
350 | ||
351 | if (tail && (nexthop_cmp(tail, nexthop) < 0)) { | |
352 | tail->next = nexthop; | |
353 | nexthop->prev = tail; | |
354 | ||
355 | tail = nexthop; | |
356 | continue; | |
357 | } | |
358 | ||
359 | _nexthop_add_sorted(&nhg->nexthop, nexthop); | |
360 | ||
361 | if (tail == NULL) | |
362 | tail = nexthop; | |
363 | } | |
364 | } | |
365 | ||
366 | /* Copy a list of nexthops, no effort made to sort or order them. */ | |
deff170e | 367 | void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh, |
7ee30f28 DS |
368 | struct nexthop *rparent) |
369 | { | |
370 | struct nexthop *nexthop; | |
deff170e | 371 | const struct nexthop *nh1; |
7ee30f28 DS |
372 | |
373 | for (nh1 = nh; nh1; nh1 = nh1->next) { | |
504d0a40 | 374 | nexthop = nexthop_dup(nh1, rparent); |
50d89650 | 375 | _nexthop_add(tnh, nexthop); |
7ee30f28 DS |
376 | } |
377 | } | |
dba32923 | 378 | |
2f000944 | 379 | uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg) |
1b1fe1c4 SW |
380 | { |
381 | struct nexthop *nh; | |
382 | uint32_t key = 0; | |
383 | ||
384 | /* | |
385 | * We are not interested in hashing over any recursively | |
386 | * resolved nexthops | |
387 | */ | |
388 | for (nh = nhg->nexthop; nh; nh = nh->next) | |
389 | key = jhash_1word(nexthop_hash(nh), key); | |
390 | ||
391 | return key; | |
2f000944 SW |
392 | } |
393 | ||
394 | uint32_t nexthop_group_hash(const struct nexthop_group *nhg) | |
395 | { | |
396 | struct nexthop *nh; | |
397 | uint32_t key = 0; | |
398 | ||
399 | for (ALL_NEXTHOPS_PTR(nhg, nh)) | |
400 | key = jhash_1word(nexthop_hash(nh), key); | |
401 | ||
402 | return key; | |
1b1fe1c4 SW |
403 | } |
404 | ||
9ef49038 SW |
405 | void nexthop_group_mark_duplicates(struct nexthop_group *nhg) |
406 | { | |
407 | struct nexthop *nexthop, *prev; | |
408 | ||
409 | for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { | |
410 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE); | |
411 | for (ALL_NEXTHOPS_PTR(nhg, prev)) { | |
412 | if (prev == nexthop) | |
413 | break; | |
414 | if (nexthop_same_firsthop(nexthop, prev)) { | |
415 | SET_FLAG(nexthop->flags, | |
416 | NEXTHOP_FLAG_DUPLICATE); | |
417 | break; | |
418 | } | |
419 | } | |
420 | } | |
421 | } | |
422 | ||
31919191 DS |
423 | static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc) |
424 | { | |
425 | struct nexthop *nexthop; | |
426 | ||
427 | nexthop = nhgc->nhg.nexthop; | |
428 | while (nexthop) { | |
429 | struct nexthop *next = nexthop_next(nexthop); | |
430 | ||
50d89650 | 431 | _nexthop_del(&nhgc->nhg, nexthop); |
31919191 DS |
432 | if (nhg_hooks.del_nexthop) |
433 | nhg_hooks.del_nexthop(nhgc, nexthop); | |
434 | ||
435 | nexthop_free(nexthop); | |
436 | ||
437 | nexthop = next; | |
438 | } | |
439 | } | |
440 | ||
d604266c | 441 | struct nexthop_group_cmd *nhgc_find(const char *name) |
31919191 DS |
442 | { |
443 | struct nexthop_group_cmd find; | |
444 | ||
445 | strlcpy(find.name, name, sizeof(find.name)); | |
446 | ||
447 | return RB_FIND(nhgc_entry_head, &nhgc_entries, &find); | |
448 | } | |
449 | ||
c57bd6bb DS |
450 | static int nhgc_cmp_helper(const char *a, const char *b) |
451 | { | |
452 | if (!a && !b) | |
453 | return 0; | |
454 | ||
455 | if (a && !b) | |
456 | return -1; | |
457 | ||
458 | if (!a && b) | |
459 | return 1; | |
460 | ||
461 | return strcmp(a, b); | |
462 | } | |
463 | ||
1c869b64 RW |
464 | static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b) |
465 | { | |
466 | if (!a && !b) | |
467 | return 0; | |
468 | ||
469 | if (a && !b) | |
470 | return -1; | |
471 | ||
472 | if (!a && b) | |
473 | return 1; | |
474 | ||
475 | return sockunion_cmp(a, b); | |
476 | } | |
477 | ||
c57bd6bb DS |
478 | static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2) |
479 | { | |
480 | int ret; | |
481 | ||
1c869b64 | 482 | ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr); |
c57bd6bb DS |
483 | if (ret) |
484 | return ret; | |
485 | ||
486 | ret = nhgc_cmp_helper(nh1->intf, nh2->intf); | |
487 | if (ret) | |
488 | return ret; | |
489 | ||
db2fede9 MS |
490 | ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name); |
491 | if (ret) | |
492 | return ret; | |
493 | ||
494 | return nhgc_cmp_helper(nh1->labels, nh2->labels); | |
c57bd6bb DS |
495 | } |
496 | ||
497 | static void nhgl_delete(struct nexthop_hold *nh) | |
498 | { | |
0a22ddfb | 499 | XFREE(MTYPE_TMP, nh->intf); |
c57bd6bb | 500 | |
0a22ddfb | 501 | XFREE(MTYPE_TMP, nh->nhvrf_name); |
c57bd6bb | 502 | |
1c869b64 RW |
503 | if (nh->addr) |
504 | sockunion_free(nh->addr); | |
b43bb64f | 505 | |
db2fede9 MS |
506 | XFREE(MTYPE_TMP, nh->labels); |
507 | ||
c57bd6bb DS |
508 | XFREE(MTYPE_TMP, nh); |
509 | } | |
510 | ||
31919191 DS |
511 | static struct nexthop_group_cmd *nhgc_get(const char *name) |
512 | { | |
513 | struct nexthop_group_cmd *nhgc; | |
514 | ||
515 | nhgc = nhgc_find(name); | |
516 | if (!nhgc) { | |
517 | nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc)); | |
518 | strlcpy(nhgc->name, name, sizeof(nhgc->name)); | |
519 | ||
520 | QOBJ_REG(nhgc, nexthop_group_cmd); | |
521 | RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc); | |
522 | ||
c57bd6bb DS |
523 | nhgc->nhg_list = list_new(); |
524 | nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp; | |
525 | nhgc->nhg_list->del = (void (*)(void *))nhgl_delete; | |
526 | ||
31919191 DS |
527 | if (nhg_hooks.new) |
528 | nhg_hooks.new(name); | |
529 | } | |
530 | ||
531 | return nhgc; | |
532 | } | |
533 | ||
534 | static void nhgc_delete(struct nexthop_group_cmd *nhgc) | |
dba32923 | 535 | { |
31919191 DS |
536 | nhgc_delete_nexthops(nhgc); |
537 | ||
538 | if (nhg_hooks.delete) | |
539 | nhg_hooks.delete(nhgc->name); | |
540 | ||
541 | RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc); | |
c57bd6bb | 542 | |
6a154c88 | 543 | list_delete(&nhgc->nhg_list); |
c57bd6bb DS |
544 | |
545 | XFREE(MTYPE_TMP, nhgc); | |
31919191 DS |
546 | } |
547 | ||
548 | DEFINE_QOBJ_TYPE(nexthop_group_cmd) | |
549 | ||
868ee86c | 550 | DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME", |
31919191 DS |
551 | "Enter into the nexthop-group submode\n" |
552 | "Specify the NAME of the nexthop-group\n") | |
553 | { | |
554 | const char *nhg_name = argv[1]->arg; | |
555 | struct nexthop_group_cmd *nhgc = NULL; | |
556 | ||
557 | nhgc = nhgc_get(nhg_name); | |
558 | VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc); | |
559 | ||
560 | return CMD_SUCCESS; | |
561 | } | |
562 | ||
868ee86c | 563 | DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME", |
31919191 DS |
564 | NO_STR |
565 | "Delete the nexthop-group\n" | |
566 | "Specify the NAME of the nexthop-group\n") | |
567 | { | |
568 | const char *nhg_name = argv[2]->arg; | |
569 | struct nexthop_group_cmd *nhgc = NULL; | |
570 | ||
571 | nhgc = nhgc_find(nhg_name); | |
572 | if (nhgc) | |
573 | nhgc_delete(nhgc); | |
574 | ||
575 | return CMD_SUCCESS; | |
576 | } | |
577 | ||
0a8881b4 MS |
578 | DEFPY(nexthop_group_backup, nexthop_group_backup_cmd, |
579 | "backup-group WORD$name", | |
580 | "Specify a group name containing backup nexthops\n" | |
581 | "The name of the backup group\n") | |
582 | { | |
583 | VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); | |
584 | ||
585 | strlcpy(nhgc->backup_list_name, name, sizeof(nhgc->backup_list_name)); | |
586 | ||
587 | return CMD_SUCCESS; | |
588 | } | |
589 | ||
590 | DEFPY(no_nexthop_group_backup, no_nexthop_group_backup_cmd, | |
591 | "no backup-group [WORD$name]", | |
592 | NO_STR | |
593 | "Clear group name containing backup nexthops\n" | |
594 | "The name of the backup group\n") | |
595 | { | |
596 | VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); | |
597 | ||
598 | nhgc->backup_list_name[0] = 0; | |
599 | ||
600 | return CMD_SUCCESS; | |
601 | } | |
602 | ||
c57bd6bb DS |
603 | static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, |
604 | const char *nhvrf_name, | |
605 | const union sockunion *addr, | |
597371a6 | 606 | const char *intf, const char *labels, |
0a8881b4 | 607 | const uint32_t weight, int backup_idx) |
c57bd6bb DS |
608 | { |
609 | struct nexthop_hold *nh; | |
610 | ||
611 | nh = XCALLOC(MTYPE_TMP, sizeof(*nh)); | |
612 | ||
613 | if (nhvrf_name) | |
614 | nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name); | |
615 | if (intf) | |
616 | nh->intf = XSTRDUP(MTYPE_TMP, intf); | |
1c869b64 RW |
617 | if (addr) |
618 | nh->addr = sockunion_dup(addr); | |
db2fede9 MS |
619 | if (labels) |
620 | nh->labels = XSTRDUP(MTYPE_TMP, labels); | |
c57bd6bb | 621 | |
597371a6 DS |
622 | nh->weight = weight; |
623 | ||
0a8881b4 MS |
624 | nh->backup_idx = backup_idx; |
625 | ||
c57bd6bb DS |
626 | listnode_add_sort(nhgc->nhg_list, nh); |
627 | } | |
628 | ||
629 | static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, | |
630 | const char *nhvrf_name, | |
631 | const union sockunion *addr, | |
597371a6 DS |
632 | const char *intf, const char *labels, |
633 | const uint32_t weight) | |
c57bd6bb DS |
634 | { |
635 | struct nexthop_hold *nh; | |
636 | struct listnode *node; | |
637 | ||
638 | for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) { | |
597371a6 DS |
639 | if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 |
640 | && nhgc_addr_cmp_helper(addr, nh->addr) == 0 | |
641 | && nhgc_cmp_helper(intf, nh->intf) == 0 | |
642 | && nhgc_cmp_helper(labels, nh->labels) == 0 | |
643 | && weight == nh->weight) | |
c57bd6bb DS |
644 | break; |
645 | } | |
646 | ||
647 | /* | |
648 | * Something has gone seriously wrong, fail gracefully | |
649 | */ | |
650 | if (!nh) | |
651 | return; | |
652 | ||
653 | list_delete_node(nhgc->nhg_list, node); | |
e5a501c2 | 654 | nhgl_delete(nh); |
c57bd6bb DS |
655 | } |
656 | ||
db2fede9 MS |
657 | /* |
658 | * Parse the config strings we support for a single nexthop. This gets used | |
659 | * in a couple of different ways, and we distinguish between transient | |
660 | * failures - such as a still-unprocessed interface - and fatal errors | |
661 | * from label-string parsing. | |
662 | */ | |
98cbbaea DS |
663 | static bool nexthop_group_parse_nexthop(struct nexthop *nhop, |
664 | const union sockunion *addr, | |
db2fede9 | 665 | const char *intf, const char *name, |
597371a6 | 666 | const char *labels, int *lbl_ret, |
0a8881b4 | 667 | uint32_t weight, int backup_idx) |
31919191 | 668 | { |
db2fede9 | 669 | int ret = 0; |
31919191 | 670 | struct vrf *vrf; |
98cbbaea DS |
671 | |
672 | memset(nhop, 0, sizeof(*nhop)); | |
31919191 DS |
673 | |
674 | if (name) | |
675 | vrf = vrf_lookup_by_name(name); | |
676 | else | |
677 | vrf = vrf_lookup_by_id(VRF_DEFAULT); | |
678 | ||
98cbbaea DS |
679 | if (!vrf) |
680 | return false; | |
31919191 | 681 | |
98cbbaea | 682 | nhop->vrf_id = vrf->vrf_id; |
31919191 | 683 | |
1c869b64 RW |
684 | if (intf) { |
685 | nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id); | |
686 | if (nhop->ifindex == IFINDEX_INTERNAL) | |
687 | return false; | |
98cbbaea DS |
688 | } |
689 | ||
1c869b64 RW |
690 | if (addr) { |
691 | if (addr->sa.sa_family == AF_INET) { | |
692 | nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr; | |
693 | if (intf) | |
694 | nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX; | |
695 | else | |
696 | nhop->type = NEXTHOP_TYPE_IPV4; | |
697 | } else { | |
698 | nhop->gate.ipv6 = addr->sin6.sin6_addr; | |
699 | if (intf) | |
700 | nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX; | |
701 | else | |
702 | nhop->type = NEXTHOP_TYPE_IPV6; | |
703 | } | |
704 | } else | |
705 | nhop->type = NEXTHOP_TYPE_IFINDEX; | |
706 | ||
db2fede9 MS |
707 | if (labels) { |
708 | uint8_t num = 0; | |
709 | mpls_label_t larray[MPLS_MAX_LABELS]; | |
710 | ||
711 | ret = mpls_str2label(labels, &num, larray); | |
712 | ||
713 | /* Return label parse result */ | |
714 | if (lbl_ret) | |
715 | *lbl_ret = ret; | |
716 | ||
717 | if (ret < 0) | |
718 | return false; | |
719 | else if (num > 0) | |
720 | nexthop_add_labels(nhop, ZEBRA_LSP_NONE, | |
721 | num, larray); | |
722 | } | |
723 | ||
597371a6 DS |
724 | nhop->weight = weight; |
725 | ||
0a8881b4 MS |
726 | if (backup_idx != NHH_BACKUP_IDX_INVALID) { |
727 | /* Validate index value */ | |
728 | if (backup_idx > NEXTHOP_BACKUP_IDX_MAX) | |
729 | return false; | |
730 | ||
731 | SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP); | |
732 | nhop->backup_idx = backup_idx; | |
733 | } | |
734 | ||
98cbbaea DS |
735 | return true; |
736 | } | |
737 | ||
db2fede9 MS |
738 | /* |
739 | * Wrapper to parse the strings in a 'nexthop_hold' | |
740 | */ | |
741 | static bool nexthop_group_parse_nhh(struct nexthop *nhop, | |
742 | const struct nexthop_hold *nhh) | |
743 | { | |
597371a6 DS |
744 | return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf, |
745 | nhh->nhvrf_name, nhh->labels, NULL, | |
0a8881b4 | 746 | nhh->weight, nhh->backup_idx)); |
db2fede9 MS |
747 | } |
748 | ||
98cbbaea | 749 | DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, |
1c869b64 RW |
750 | "[no] nexthop\ |
751 | <\ | |
752 | <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\ | |
753 | |INTERFACE$intf\ | |
754 | >\ | |
db2fede9 MS |
755 | [{ \ |
756 | nexthop-vrf NAME$vrf_name \ | |
757 | |label WORD \ | |
597371a6 | 758 | |weight (1-255) \ |
0a8881b4 | 759 | |backup-idx$bi_str (0-254)$idx \ |
db2fede9 | 760 | }]", |
98cbbaea DS |
761 | NO_STR |
762 | "Specify one of the nexthops in this ECMP group\n" | |
763 | "v4 Address\n" | |
764 | "v6 Address\n" | |
765 | "Interface to use\n" | |
1c869b64 | 766 | "Interface to use\n" |
98cbbaea | 767 | "If the nexthop is in a different vrf tell us\n" |
db2fede9 MS |
768 | "The nexthop-vrf Name\n" |
769 | "Specify label(s) for this nexthop\n" | |
597371a6 DS |
770 | "One or more labels in the range (16-1048575) separated by '/'\n" |
771 | "Weight to be used by the nexthop for purposes of ECMP\n" | |
0a8881b4 MS |
772 | "Weight value to be used\n" |
773 | "Backup nexthop index in another group\n" | |
774 | "Nexthop index value\n") | |
98cbbaea DS |
775 | { |
776 | VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); | |
777 | struct nexthop nhop; | |
778 | struct nexthop *nh; | |
db2fede9 | 779 | int lbl_ret = 0; |
98cbbaea | 780 | bool legal; |
0a8881b4 MS |
781 | int backup_idx = idx; |
782 | bool add_update = false; | |
783 | ||
784 | if (bi_str == NULL) | |
785 | backup_idx = NHH_BACKUP_IDX_INVALID; | |
98cbbaea | 786 | |
597371a6 | 787 | legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label, |
0a8881b4 | 788 | &lbl_ret, weight, backup_idx); |
98cbbaea DS |
789 | |
790 | if (nhop.type == NEXTHOP_TYPE_IPV6 | |
791 | && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) { | |
792 | vty_out(vty, | |
793 | "Specified a v6 LL with no interface, rejecting\n"); | |
794 | return CMD_WARNING_CONFIG_FAILED; | |
31919191 DS |
795 | } |
796 | ||
db2fede9 MS |
797 | /* Handle label-string errors */ |
798 | if (!legal && lbl_ret < 0) { | |
799 | switch (lbl_ret) { | |
800 | case -1: | |
801 | vty_out(vty, "%% Malformed label(s)\n"); | |
802 | break; | |
803 | case -2: | |
804 | vty_out(vty, | |
805 | "%% Cannot use reserved label(s) (%d-%d)\n", | |
806 | MPLS_LABEL_RESERVED_MIN, | |
807 | MPLS_LABEL_RESERVED_MAX); | |
808 | break; | |
809 | case -3: | |
810 | vty_out(vty, | |
811 | "%% Too many labels. Enter %d or fewer\n", | |
812 | MPLS_MAX_LABELS); | |
813 | break; | |
814 | } | |
815 | return CMD_WARNING_CONFIG_FAILED; | |
816 | } | |
817 | ||
31919191 DS |
818 | nh = nexthop_exists(&nhgc->nhg, &nhop); |
819 | ||
0a8881b4 MS |
820 | if (no || nh) { |
821 | /* Remove or replace cases */ | |
822 | ||
823 | /* Remove existing config */ | |
597371a6 DS |
824 | nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label, |
825 | weight); | |
31919191 | 826 | if (nh) { |
0a8881b4 | 827 | /* Remove nexthop object */ |
50d89650 | 828 | _nexthop_del(&nhgc->nhg, nh); |
31919191 DS |
829 | |
830 | if (nhg_hooks.del_nexthop) | |
831 | nhg_hooks.del_nexthop(nhgc, nh); | |
832 | ||
833 | nexthop_free(nh); | |
0a8881b4 | 834 | nh = NULL; |
31919191 | 835 | } |
0a8881b4 MS |
836 | } |
837 | ||
838 | add_update = !no; | |
839 | ||
840 | if (add_update) { | |
841 | /* Add or replace cases */ | |
842 | ||
843 | /* If valid config, add nexthop object */ | |
98cbbaea DS |
844 | if (legal) { |
845 | nh = nexthop_new(); | |
31919191 | 846 | |
98cbbaea | 847 | memcpy(nh, &nhop, sizeof(nhop)); |
50d89650 | 848 | _nexthop_add(&nhgc->nhg.nexthop, nh); |
98cbbaea | 849 | } |
31919191 | 850 | |
0a8881b4 | 851 | /* Save config always */ |
597371a6 | 852 | nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label, |
0a8881b4 | 853 | weight, backup_idx); |
c57bd6bb | 854 | |
98cbbaea | 855 | if (legal && nhg_hooks.add_nexthop) |
31919191 DS |
856 | nhg_hooks.add_nexthop(nhgc, nh); |
857 | } | |
858 | ||
dba32923 DS |
859 | return CMD_SUCCESS; |
860 | } | |
861 | ||
1b3e9a21 | 862 | static struct cmd_node nexthop_group_node = { |
dba32923 DS |
863 | NH_GROUP_NODE, |
864 | "%s(config-nh-group)# ", | |
865 | 1 | |
866 | }; | |
867 | ||
1b7bce04 DS |
868 | void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) |
869 | { | |
870 | char buf[100]; | |
871 | struct vrf *vrf; | |
872 | ||
57cdafc4 | 873 | vty_out(vty, "nexthop "); |
1b7bce04 DS |
874 | |
875 | switch (nh->type) { | |
876 | case NEXTHOP_TYPE_IFINDEX: | |
877 | vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id)); | |
878 | break; | |
879 | case NEXTHOP_TYPE_IPV4: | |
880 | vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4)); | |
881 | break; | |
882 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
883 | vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), | |
884 | ifindex2ifname(nh->ifindex, nh->vrf_id)); | |
885 | break; | |
886 | case NEXTHOP_TYPE_IPV6: | |
887 | vty_out(vty, "%s", | |
888 | inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); | |
889 | break; | |
890 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
891 | vty_out(vty, "%s %s", | |
892 | inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)), | |
893 | ifindex2ifname(nh->ifindex, nh->vrf_id)); | |
894 | break; | |
895 | case NEXTHOP_TYPE_BLACKHOLE: | |
896 | break; | |
897 | } | |
898 | ||
899 | if (nh->vrf_id != VRF_DEFAULT) { | |
900 | vrf = vrf_lookup_by_id(nh->vrf_id); | |
901 | vty_out(vty, " nexthop-vrf %s", vrf->name); | |
902 | } | |
db2fede9 MS |
903 | |
904 | if (nh->nh_label && nh->nh_label->num_labels > 0) { | |
905 | char buf[200]; | |
906 | ||
907 | mpls_label2str(nh->nh_label->num_labels, | |
908 | nh->nh_label->label, | |
909 | buf, sizeof(buf), 0); | |
910 | vty_out(vty, " label %s", buf); | |
911 | } | |
912 | ||
597371a6 DS |
913 | if (nh->weight) |
914 | vty_out(vty, " weight %u", nh->weight); | |
915 | ||
0a8881b4 MS |
916 | if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) |
917 | vty_out(vty, " backup-idx %d", nh->backup_idx); | |
918 | ||
1b7bce04 DS |
919 | vty_out(vty, "\n"); |
920 | } | |
921 | ||
c57bd6bb DS |
922 | static void nexthop_group_write_nexthop_internal(struct vty *vty, |
923 | struct nexthop_hold *nh) | |
924 | { | |
925 | char buf[100]; | |
926 | ||
1c869b64 | 927 | vty_out(vty, "nexthop"); |
c57bd6bb | 928 | |
1c869b64 RW |
929 | if (nh->addr) |
930 | vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf))); | |
c57bd6bb DS |
931 | |
932 | if (nh->intf) | |
933 | vty_out(vty, " %s", nh->intf); | |
934 | ||
935 | if (nh->nhvrf_name) | |
936 | vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name); | |
937 | ||
db2fede9 MS |
938 | if (nh->labels) |
939 | vty_out(vty, " label %s", nh->labels); | |
940 | ||
597371a6 DS |
941 | if (nh->weight) |
942 | vty_out(vty, " weight %u", nh->weight); | |
943 | ||
0a8881b4 MS |
944 | if (nh->backup_idx != NHH_BACKUP_IDX_INVALID) |
945 | vty_out(vty, " backup-idx %d", nh->backup_idx); | |
946 | ||
c57bd6bb DS |
947 | vty_out(vty, "\n"); |
948 | } | |
949 | ||
dba32923 DS |
950 | static int nexthop_group_write(struct vty *vty) |
951 | { | |
31919191 | 952 | struct nexthop_group_cmd *nhgc; |
c57bd6bb | 953 | struct nexthop_hold *nh; |
31919191 DS |
954 | |
955 | RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { | |
c57bd6bb DS |
956 | struct listnode *node; |
957 | ||
31919191 DS |
958 | vty_out(vty, "nexthop-group %s\n", nhgc->name); |
959 | ||
0a8881b4 MS |
960 | if (nhgc->backup_list_name[0]) |
961 | vty_out(vty, " backup-group %s\n", | |
962 | nhgc->backup_list_name); | |
963 | ||
c57bd6bb | 964 | for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) { |
7dce96f0 | 965 | vty_out(vty, " "); |
c57bd6bb | 966 | nexthop_group_write_nexthop_internal(vty, nh); |
811f859f | 967 | } |
31919191 | 968 | |
31919191 DS |
969 | vty_out(vty, "!\n"); |
970 | } | |
dba32923 DS |
971 | |
972 | return 1; | |
973 | } | |
974 | ||
98cbbaea DS |
975 | void nexthop_group_enable_vrf(struct vrf *vrf) |
976 | { | |
977 | struct nexthop_group_cmd *nhgc; | |
978 | struct nexthop_hold *nhh; | |
979 | ||
980 | RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { | |
981 | struct listnode *node; | |
982 | ||
983 | for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { | |
984 | struct nexthop nhop; | |
985 | struct nexthop *nh; | |
986 | ||
db2fede9 | 987 | if (!nexthop_group_parse_nhh(&nhop, nhh)) |
98cbbaea DS |
988 | continue; |
989 | ||
990 | nh = nexthop_exists(&nhgc->nhg, &nhop); | |
991 | ||
992 | if (nh) | |
993 | continue; | |
994 | ||
995 | if (nhop.vrf_id != vrf->vrf_id) | |
996 | continue; | |
997 | ||
998 | nh = nexthop_new(); | |
999 | ||
1000 | memcpy(nh, &nhop, sizeof(nhop)); | |
50d89650 | 1001 | _nexthop_add(&nhgc->nhg.nexthop, nh); |
98cbbaea DS |
1002 | |
1003 | if (nhg_hooks.add_nexthop) | |
1004 | nhg_hooks.add_nexthop(nhgc, nh); | |
1005 | } | |
1006 | } | |
1007 | } | |
1008 | ||
1009 | void nexthop_group_disable_vrf(struct vrf *vrf) | |
1010 | { | |
1011 | struct nexthop_group_cmd *nhgc; | |
1012 | struct nexthop_hold *nhh; | |
1013 | ||
1014 | RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { | |
1015 | struct listnode *node; | |
1016 | ||
1017 | for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { | |
1018 | struct nexthop nhop; | |
1019 | struct nexthop *nh; | |
1020 | ||
db2fede9 | 1021 | if (!nexthop_group_parse_nhh(&nhop, nhh)) |
98cbbaea DS |
1022 | continue; |
1023 | ||
1024 | nh = nexthop_exists(&nhgc->nhg, &nhop); | |
1025 | ||
1026 | if (!nh) | |
1027 | continue; | |
1028 | ||
1029 | if (nh->vrf_id != vrf->vrf_id) | |
1030 | continue; | |
1031 | ||
50d89650 | 1032 | _nexthop_del(&nhgc->nhg, nh); |
98cbbaea DS |
1033 | |
1034 | if (nhg_hooks.del_nexthop) | |
1035 | nhg_hooks.del_nexthop(nhgc, nh); | |
1036 | ||
1037 | nexthop_free(nh); | |
1038 | } | |
1039 | } | |
1040 | } | |
1041 | ||
1042 | void nexthop_group_interface_state_change(struct interface *ifp, | |
1043 | ifindex_t oldifindex) | |
1044 | { | |
1045 | struct nexthop_group_cmd *nhgc; | |
1046 | struct nexthop_hold *nhh; | |
1047 | ||
1048 | RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { | |
1049 | struct listnode *node; | |
1050 | struct nexthop *nh; | |
1051 | ||
1052 | if (if_is_up(ifp)) { | |
1053 | for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { | |
1054 | struct nexthop nhop; | |
1055 | ||
db2fede9 | 1056 | if (!nexthop_group_parse_nhh(&nhop, nhh)) |
98cbbaea DS |
1057 | continue; |
1058 | ||
1059 | switch (nhop.type) { | |
1060 | case NEXTHOP_TYPE_IPV4: | |
1061 | case NEXTHOP_TYPE_IPV6: | |
1062 | case NEXTHOP_TYPE_BLACKHOLE: | |
1063 | continue; | |
1064 | case NEXTHOP_TYPE_IFINDEX: | |
1065 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
1066 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1067 | break; | |
1068 | } | |
1069 | nh = nexthop_exists(&nhgc->nhg, &nhop); | |
1070 | ||
1071 | if (nh) | |
1072 | continue; | |
1073 | ||
1074 | if (ifp->ifindex != nhop.ifindex) | |
1075 | continue; | |
1076 | ||
1077 | nh = nexthop_new(); | |
1078 | ||
1079 | memcpy(nh, &nhop, sizeof(nhop)); | |
50d89650 | 1080 | _nexthop_add(&nhgc->nhg.nexthop, nh); |
98cbbaea DS |
1081 | |
1082 | if (nhg_hooks.add_nexthop) | |
1083 | nhg_hooks.add_nexthop(nhgc, nh); | |
1084 | } | |
1085 | } else { | |
1086 | struct nexthop *next_nh; | |
1087 | ||
1088 | for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) { | |
1089 | next_nh = nh->next; | |
1090 | switch (nh->type) { | |
1091 | case NEXTHOP_TYPE_IPV4: | |
1092 | case NEXTHOP_TYPE_IPV6: | |
1093 | case NEXTHOP_TYPE_BLACKHOLE: | |
1094 | continue; | |
1095 | case NEXTHOP_TYPE_IFINDEX: | |
1096 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
1097 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1098 | break; | |
1099 | } | |
1100 | ||
1101 | if (oldifindex != nh->ifindex) | |
1102 | continue; | |
1103 | ||
50d89650 | 1104 | _nexthop_del(&nhgc->nhg, nh); |
98cbbaea DS |
1105 | |
1106 | if (nhg_hooks.del_nexthop) | |
1107 | nhg_hooks.del_nexthop(nhgc, nh); | |
1108 | ||
1109 | nexthop_free(nh); | |
1110 | } | |
1111 | } | |
1112 | } | |
1113 | } | |
1114 | ||
868ee86c DS |
1115 | static void nhg_name_autocomplete(vector comps, struct cmd_token *token) |
1116 | { | |
1117 | struct nexthop_group_cmd *nhgc; | |
1118 | ||
1119 | RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { | |
1120 | vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name)); | |
1121 | } | |
1122 | } | |
1123 | ||
1124 | static const struct cmd_variable_handler nhg_name_handlers[] = { | |
1125 | {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete}, | |
1126 | {.completions = NULL}}; | |
1127 | ||
31919191 DS |
1128 | void nexthop_group_init(void (*new)(const char *name), |
1129 | void (*add_nexthop)(const struct nexthop_group_cmd *nhg, | |
1130 | const struct nexthop *nhop), | |
1131 | void (*del_nexthop)(const struct nexthop_group_cmd *nhg, | |
1132 | const struct nexthop *nhop), | |
1133 | void (*delete)(const char *name)) | |
dba32923 | 1134 | { |
31919191 DS |
1135 | RB_INIT(nhgc_entry_head, &nhgc_entries); |
1136 | ||
868ee86c DS |
1137 | cmd_variable_handler_register(nhg_name_handlers); |
1138 | ||
dba32923 DS |
1139 | install_node(&nexthop_group_node, nexthop_group_write); |
1140 | install_element(CONFIG_NODE, &nexthop_group_cmd); | |
31919191 DS |
1141 | install_element(CONFIG_NODE, &no_nexthop_group_cmd); |
1142 | ||
1143 | install_default(NH_GROUP_NODE); | |
0a8881b4 MS |
1144 | install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd); |
1145 | install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd); | |
31919191 DS |
1146 | install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd); |
1147 | ||
1148 | memset(&nhg_hooks, 0, sizeof(nhg_hooks)); | |
1149 | ||
1150 | if (new) | |
1151 | nhg_hooks.new = new; | |
1152 | if (add_nexthop) | |
1153 | nhg_hooks.add_nexthop = add_nexthop; | |
1154 | if (del_nexthop) | |
1155 | nhg_hooks.del_nexthop = del_nexthop; | |
1156 | if (delete) | |
1157 | nhg_hooks.delete = delete; | |
dba32923 | 1158 | } |