Line data Source code
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 :
22 : #include <vrf.h>
23 : #include <sockunion.h>
24 : #include <nexthop.h>
25 : #include <nexthop_group.h>
26 : #include <nexthop_group_private.h>
27 : #include <vty.h>
28 : #include <command.h>
29 : #include <jhash.h>
30 :
31 : #include "lib/nexthop_group_clippy.c"
32 :
33 11 : DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group");
34 :
35 : /*
36 : * Internal struct used to hold nhg config strings
37 : */
38 : struct nexthop_hold {
39 : char *nhvrf_name;
40 : union sockunion *addr;
41 : char *intf;
42 : bool onlink;
43 : char *labels;
44 : uint32_t weight;
45 : char *backup_str;
46 : };
47 :
48 : struct nexthop_group_hooks {
49 : void (*new)(const char *name);
50 : void (*modify)(const struct nexthop_group_cmd *nhgc);
51 : void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
52 : const struct nexthop *nhop);
53 : void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
54 : const struct nexthop *nhop);
55 : void (*delete)(const char *name);
56 : };
57 :
58 : static struct nexthop_group_hooks nhg_hooks;
59 :
60 : static inline int
61 : nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
62 : const struct nexthop_group_cmd *nhgc2);
63 0 : RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry,
64 : nexthop_group_cmd_compare)
65 :
66 : static struct nhgc_entry_head nhgc_entries;
67 :
68 : static inline int
69 0 : nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
70 : const struct nexthop_group_cmd *nhgc2)
71 : {
72 0 : return strcmp(nhgc1->name, nhgc2->name);
73 : }
74 :
75 16 : static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg)
76 : {
77 16 : struct nexthop *nexthop = nhg->nexthop;
78 :
79 16 : while (nexthop && nexthop->next)
80 : nexthop = nexthop->next;
81 :
82 16 : return nexthop;
83 : }
84 :
85 16 : uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)
86 : {
87 16 : struct nexthop *nhop;
88 16 : uint8_t num = 0;
89 :
90 32 : for (ALL_NEXTHOPS_PTR(nhg, nhop))
91 16 : num++;
92 :
93 16 : return num;
94 : }
95 :
96 18 : uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
97 : {
98 18 : struct nexthop *nhop;
99 18 : uint8_t num = 0;
100 :
101 36 : for (nhop = nhg->nexthop; nhop; nhop = nhop->next)
102 18 : num++;
103 :
104 18 : return num;
105 : }
106 :
107 33 : uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
108 : {
109 33 : struct nexthop *nhop;
110 33 : uint8_t num = 0;
111 :
112 66 : for (ALL_NEXTHOPS_PTR(nhg, nhop)) {
113 33 : if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
114 17 : num++;
115 : }
116 :
117 33 : return num;
118 : }
119 :
120 : uint8_t
121 0 : nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
122 : {
123 0 : struct nexthop *nhop;
124 0 : uint8_t num = 0;
125 :
126 0 : for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
127 0 : if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
128 0 : num++;
129 : }
130 :
131 0 : return num;
132 : }
133 :
134 0 : struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
135 : const struct nexthop *nh)
136 : {
137 0 : struct nexthop *nexthop;
138 :
139 0 : for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
140 0 : if (nexthop_same(nh, nexthop))
141 0 : return nexthop;
142 : }
143 :
144 : return NULL;
145 : }
146 :
147 : /*
148 : * Helper that locates a nexthop in an nhg config list. Note that
149 : * this uses a specific matching / equality rule that's different from
150 : * the complete match performed by 'nexthop_same()'.
151 : */
152 0 : static struct nexthop *nhg_nh_find(const struct nexthop_group *nhg,
153 : const struct nexthop *nh)
154 : {
155 0 : struct nexthop *nexthop;
156 0 : int ret;
157 :
158 : /* We compare: vrf, gateway, and interface */
159 :
160 0 : for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
161 :
162 : /* Compare vrf and type */
163 0 : if (nexthop->vrf_id != nh->vrf_id)
164 0 : continue;
165 0 : if (nexthop->type != nh->type)
166 0 : continue;
167 :
168 : /* Compare gateway */
169 0 : switch (nexthop->type) {
170 0 : case NEXTHOP_TYPE_IPV4:
171 : case NEXTHOP_TYPE_IPV6:
172 0 : ret = nexthop_g_addr_cmp(nexthop->type,
173 0 : &nexthop->gate, &nh->gate);
174 0 : if (ret != 0)
175 0 : continue;
176 : break;
177 0 : case NEXTHOP_TYPE_IPV4_IFINDEX:
178 : case NEXTHOP_TYPE_IPV6_IFINDEX:
179 0 : ret = nexthop_g_addr_cmp(nexthop->type,
180 0 : &nexthop->gate, &nh->gate);
181 0 : if (ret != 0)
182 0 : continue;
183 : /* Intentional Fall-Through */
184 : case NEXTHOP_TYPE_IFINDEX:
185 0 : if (nexthop->ifindex != nh->ifindex)
186 0 : continue;
187 : break;
188 0 : case NEXTHOP_TYPE_BLACKHOLE:
189 0 : if (nexthop->bh_type != nh->bh_type)
190 0 : continue;
191 : break;
192 : }
193 :
194 : return nexthop;
195 : }
196 :
197 : return NULL;
198 : }
199 :
200 : static bool
201 9 : nexthop_group_equal_common(const struct nexthop_group *nhg1,
202 : const struct nexthop_group *nhg2,
203 : uint8_t (*nexthop_group_nexthop_num_func)(
204 : const struct nexthop_group *nhg))
205 : {
206 9 : if (nhg1 && !nhg2)
207 : return false;
208 :
209 9 : if (!nhg1 && nhg2)
210 : return false;
211 :
212 9 : if (nhg1 == nhg2)
213 : return true;
214 :
215 18 : if (nexthop_group_nexthop_num_func(nhg1)
216 9 : != nexthop_group_nexthop_num_func(nhg2))
217 : return false;
218 :
219 : return true;
220 : }
221 :
222 : /* This assumes ordered */
223 9 : bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1,
224 : const struct nexthop_group *nhg2)
225 : {
226 9 : struct nexthop *nh1 = NULL;
227 9 : struct nexthop *nh2 = NULL;
228 :
229 9 : if (!nexthop_group_equal_common(nhg1, nhg2,
230 : &nexthop_group_nexthop_num_no_recurse))
231 : return false;
232 :
233 17 : for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
234 8 : nh1 = nh1->next, nh2 = nh2->next) {
235 9 : if (nh1 && !nh2)
236 : return false;
237 9 : if (!nh1 && nh2)
238 : return false;
239 9 : if (!nexthop_same(nh1, nh2))
240 : return false;
241 : }
242 :
243 : return true;
244 : }
245 :
246 : /* This assumes ordered */
247 0 : bool nexthop_group_equal(const struct nexthop_group *nhg1,
248 : const struct nexthop_group *nhg2)
249 : {
250 0 : struct nexthop *nh1 = NULL;
251 0 : struct nexthop *nh2 = NULL;
252 :
253 0 : if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num))
254 : return false;
255 :
256 0 : for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
257 0 : nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) {
258 0 : if (nh1 && !nh2)
259 : return false;
260 0 : if (!nh1 && nh2)
261 : return false;
262 0 : if (!nexthop_same(nh1, nh2))
263 : return false;
264 : }
265 :
266 : return true;
267 : }
268 0 : struct nexthop_group *nexthop_group_new(void)
269 : {
270 0 : return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
271 : }
272 :
273 55 : void nexthop_group_copy(struct nexthop_group *to,
274 : const struct nexthop_group *from)
275 : {
276 55 : to->nhgr = from->nhgr;
277 : /* Copy everything, including recursive info */
278 55 : copy_nexthops(&to->nexthop, from->nexthop, NULL);
279 55 : }
280 :
281 0 : void nexthop_group_delete(struct nexthop_group **nhg)
282 : {
283 : /* OK to call with NULL group */
284 0 : if ((*nhg) == NULL)
285 : return;
286 :
287 0 : if ((*nhg)->nexthop)
288 0 : nexthops_free((*nhg)->nexthop);
289 :
290 0 : XFREE(MTYPE_NEXTHOP_GROUP, *nhg);
291 : }
292 :
293 : /* Add nexthop to the end of a nexthop list. */
294 70 : void _nexthop_add(struct nexthop **target, struct nexthop *nexthop)
295 : {
296 70 : struct nexthop *last;
297 :
298 70 : for (last = *target; last && last->next; last = last->next)
299 : ;
300 70 : if (last)
301 0 : last->next = nexthop;
302 : else
303 70 : *target = nexthop;
304 70 : nexthop->prev = last;
305 70 : }
306 :
307 : /* Add nexthop to sorted list of nexthops */
308 16 : static void _nexthop_add_sorted(struct nexthop **head,
309 : struct nexthop *nexthop)
310 : {
311 16 : struct nexthop *position, *prev;
312 :
313 16 : assert(!nexthop->next);
314 :
315 16 : for (position = *head, prev = NULL; position;
316 0 : prev = position, position = position->next) {
317 0 : if (nexthop_cmp(position, nexthop) > 0) {
318 0 : nexthop->next = position;
319 0 : nexthop->prev = prev;
320 :
321 0 : if (nexthop->prev)
322 0 : nexthop->prev->next = nexthop;
323 : else
324 0 : *head = nexthop;
325 :
326 0 : position->prev = nexthop;
327 0 : return;
328 : }
329 : }
330 :
331 16 : nexthop->prev = prev;
332 16 : if (prev)
333 0 : prev->next = nexthop;
334 : else
335 16 : *head = nexthop;
336 : }
337 :
338 16 : void nexthop_group_add_sorted(struct nexthop_group *nhg,
339 : struct nexthop *nexthop)
340 : {
341 16 : struct nexthop *tail;
342 :
343 16 : assert(!nexthop->next);
344 :
345 : /* Try to just append to the end first;
346 : * trust the list is already sorted
347 : */
348 16 : tail = nexthop_group_tail(nhg);
349 :
350 16 : if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
351 0 : tail->next = nexthop;
352 0 : nexthop->prev = tail;
353 :
354 0 : return;
355 : }
356 :
357 16 : _nexthop_add_sorted(&nhg->nexthop, nexthop);
358 : }
359 :
360 : /* Delete nexthop from a nexthop list. */
361 0 : void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh)
362 : {
363 0 : struct nexthop *nexthop;
364 :
365 0 : for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
366 0 : if (nexthop_same(nh, nexthop))
367 : break;
368 : }
369 :
370 0 : assert(nexthop);
371 :
372 0 : if (nexthop->prev)
373 0 : nexthop->prev->next = nexthop->next;
374 : else
375 0 : nhg->nexthop = nexthop->next;
376 :
377 0 : if (nexthop->next)
378 0 : nexthop->next->prev = nexthop->prev;
379 :
380 0 : nh->prev = NULL;
381 0 : nh->next = NULL;
382 0 : }
383 :
384 : /* Unlink a nexthop from the list it's on, unconditionally */
385 0 : static void nexthop_unlink(struct nexthop_group *nhg, struct nexthop *nexthop)
386 : {
387 :
388 0 : if (nexthop->prev)
389 0 : nexthop->prev->next = nexthop->next;
390 : else {
391 0 : assert(nhg->nexthop == nexthop);
392 0 : assert(nexthop->prev == NULL);
393 0 : nhg->nexthop = nexthop->next;
394 : }
395 :
396 0 : if (nexthop->next)
397 0 : nexthop->next->prev = nexthop->prev;
398 :
399 0 : nexthop->prev = NULL;
400 0 : nexthop->next = NULL;
401 0 : }
402 :
403 : /*
404 : * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
405 : */
406 0 : void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg,
407 : const struct nexthop *nh)
408 : {
409 0 : struct nexthop *nexthop, *tail;
410 0 : const struct nexthop *nh1;
411 :
412 : /* We'll try to append to the end of the new list;
413 : * if the original list in nh is already sorted, this eliminates
414 : * lots of comparison operations.
415 : */
416 0 : tail = nexthop_group_tail(nhg);
417 :
418 0 : for (nh1 = nh; nh1; nh1 = nh1->next) {
419 0 : nexthop = nexthop_dup(nh1, NULL);
420 :
421 0 : if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
422 0 : tail->next = nexthop;
423 0 : nexthop->prev = tail;
424 :
425 0 : tail = nexthop;
426 0 : continue;
427 : }
428 :
429 0 : _nexthop_add_sorted(&nhg->nexthop, nexthop);
430 :
431 0 : if (tail == NULL)
432 0 : tail = nexthop;
433 : }
434 0 : }
435 :
436 : /* Copy a list of nexthops, no effort made to sort or order them. */
437 70 : void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
438 : struct nexthop *rparent)
439 : {
440 70 : struct nexthop *nexthop;
441 70 : const struct nexthop *nh1;
442 :
443 140 : for (nh1 = nh; nh1; nh1 = nh1->next) {
444 70 : nexthop = nexthop_dup(nh1, rparent);
445 70 : _nexthop_add(tnh, nexthop);
446 : }
447 70 : }
448 :
449 0 : uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg)
450 : {
451 0 : struct nexthop *nh;
452 0 : uint32_t key = 0;
453 :
454 : /*
455 : * We are not interested in hashing over any recursively
456 : * resolved nexthops
457 : */
458 0 : for (nh = nhg->nexthop; nh; nh = nh->next)
459 0 : key = jhash_1word(nexthop_hash(nh), key);
460 :
461 0 : return key;
462 : }
463 :
464 57 : uint32_t nexthop_group_hash(const struct nexthop_group *nhg)
465 : {
466 57 : struct nexthop *nh;
467 57 : uint32_t key = 0;
468 :
469 114 : for (ALL_NEXTHOPS_PTR(nhg, nh))
470 57 : key = jhash_1word(nexthop_hash(nh), key);
471 :
472 57 : return key;
473 : }
474 :
475 13 : void nexthop_group_mark_duplicates(struct nexthop_group *nhg)
476 : {
477 13 : struct nexthop *nexthop, *prev;
478 :
479 26 : for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
480 13 : UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE);
481 13 : for (ALL_NEXTHOPS_PTR(nhg, prev)) {
482 13 : if (prev == nexthop)
483 : break;
484 0 : if (nexthop_same(nexthop, prev)) {
485 0 : SET_FLAG(nexthop->flags,
486 : NEXTHOP_FLAG_DUPLICATE);
487 0 : break;
488 : }
489 : }
490 : }
491 13 : }
492 :
493 0 : static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc)
494 : {
495 0 : struct nexthop *nexthop;
496 :
497 0 : nexthop = nhgc->nhg.nexthop;
498 0 : while (nexthop) {
499 0 : struct nexthop *next = nexthop_next(nexthop);
500 :
501 0 : _nexthop_del(&nhgc->nhg, nexthop);
502 0 : if (nhg_hooks.del_nexthop)
503 0 : nhg_hooks.del_nexthop(nhgc, nexthop);
504 :
505 0 : nexthop_free(nexthop);
506 :
507 0 : nexthop = next;
508 : }
509 0 : }
510 :
511 0 : struct nexthop_group_cmd *nhgc_find(const char *name)
512 : {
513 0 : struct nexthop_group_cmd find;
514 :
515 0 : strlcpy(find.name, name, sizeof(find.name));
516 :
517 0 : return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
518 : }
519 :
520 0 : static int nhgc_cmp_helper(const char *a, const char *b)
521 : {
522 0 : if (!a && !b)
523 : return 0;
524 :
525 0 : if (a && !b)
526 : return -1;
527 :
528 0 : if (!a && b)
529 : return 1;
530 :
531 0 : return strcmp(a, b);
532 : }
533 :
534 0 : static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b)
535 : {
536 0 : if (!a && !b)
537 : return 0;
538 :
539 0 : if (a && !b)
540 : return -1;
541 :
542 0 : if (!a && b)
543 : return 1;
544 :
545 0 : return sockunion_cmp(a, b);
546 : }
547 :
548 0 : static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
549 : {
550 0 : int ret;
551 :
552 0 : ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr);
553 0 : if (ret)
554 : return ret;
555 :
556 0 : ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
557 0 : if (ret)
558 : return ret;
559 :
560 0 : ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
561 0 : if (ret)
562 : return ret;
563 :
564 0 : ret = ((int)nh2->onlink) - ((int)nh1->onlink);
565 0 : if (ret)
566 : return ret;
567 :
568 0 : return nhgc_cmp_helper(nh1->labels, nh2->labels);
569 : }
570 :
571 0 : static void nhgl_delete(struct nexthop_hold *nh)
572 : {
573 0 : XFREE(MTYPE_TMP, nh->intf);
574 :
575 0 : XFREE(MTYPE_TMP, nh->nhvrf_name);
576 :
577 0 : if (nh->addr)
578 0 : sockunion_free(nh->addr);
579 :
580 0 : XFREE(MTYPE_TMP, nh->labels);
581 :
582 0 : XFREE(MTYPE_TMP, nh);
583 0 : }
584 :
585 0 : static struct nexthop_group_cmd *nhgc_get(const char *name)
586 : {
587 0 : struct nexthop_group_cmd *nhgc;
588 :
589 0 : nhgc = nhgc_find(name);
590 0 : if (!nhgc) {
591 0 : nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc));
592 0 : strlcpy(nhgc->name, name, sizeof(nhgc->name));
593 :
594 0 : QOBJ_REG(nhgc, nexthop_group_cmd);
595 0 : RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
596 :
597 0 : nhgc->nhg_list = list_new();
598 0 : nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
599 0 : nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
600 :
601 0 : if (nhg_hooks.new)
602 0 : nhg_hooks.new(name);
603 : }
604 :
605 0 : return nhgc;
606 : }
607 :
608 0 : static void nhgc_delete(struct nexthop_group_cmd *nhgc)
609 : {
610 0 : nhgc_delete_nexthops(nhgc);
611 :
612 0 : if (nhg_hooks.delete)
613 0 : nhg_hooks.delete(nhgc->name);
614 :
615 0 : RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
616 :
617 0 : list_delete(&nhgc->nhg_list);
618 :
619 0 : QOBJ_UNREG(nhgc);
620 0 : XFREE(MTYPE_TMP, nhgc);
621 0 : }
622 :
623 : DEFINE_QOBJ_TYPE(nexthop_group_cmd);
624 :
625 0 : DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME",
626 : "Enter into the nexthop-group submode\n"
627 : "Specify the NAME of the nexthop-group\n")
628 : {
629 0 : const char *nhg_name = argv[1]->arg;
630 0 : struct nexthop_group_cmd *nhgc = NULL;
631 :
632 0 : nhgc = nhgc_get(nhg_name);
633 0 : VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
634 :
635 0 : return CMD_SUCCESS;
636 : }
637 :
638 0 : DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
639 : NO_STR
640 : "Delete the nexthop-group\n"
641 : "Specify the NAME of the nexthop-group\n")
642 : {
643 0 : const char *nhg_name = argv[2]->arg;
644 0 : struct nexthop_group_cmd *nhgc = NULL;
645 :
646 0 : nhgc = nhgc_find(nhg_name);
647 0 : if (nhgc)
648 0 : nhgc_delete(nhgc);
649 :
650 0 : return CMD_SUCCESS;
651 : }
652 :
653 0 : DEFPY(nexthop_group_backup, nexthop_group_backup_cmd,
654 : "backup-group WORD$name",
655 : "Specify a group name containing backup nexthops\n"
656 : "The name of the backup group\n")
657 : {
658 0 : VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
659 :
660 0 : strlcpy(nhgc->backup_list_name, name, sizeof(nhgc->backup_list_name));
661 :
662 0 : return CMD_SUCCESS;
663 : }
664 :
665 0 : DEFPY(no_nexthop_group_backup, no_nexthop_group_backup_cmd,
666 : "no backup-group [WORD$name]",
667 : NO_STR
668 : "Clear group name containing backup nexthops\n"
669 : "The name of the backup group\n")
670 : {
671 0 : VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
672 :
673 0 : nhgc->backup_list_name[0] = 0;
674 :
675 0 : return CMD_SUCCESS;
676 : }
677 :
678 0 : DEFPY(nexthop_group_resilience,
679 : nexthop_group_resilience_cmd,
680 : "resilient buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)",
681 : "A resilient Nexthop Group\n"
682 : "Buckets in the Hash for this Group\n"
683 : "Number of buckets\n"
684 : "The Idle timer for this Resilient Nexthop Group in seconds\n"
685 : "Number of seconds of Idle time\n"
686 : "The length of time that the Nexthop Group can be unbalanced\n"
687 : "Number of seconds of Unbalanced time\n")
688 : {
689 0 : VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
690 :
691 0 : nhgc->nhg.nhgr.buckets = buckets;
692 0 : nhgc->nhg.nhgr.idle_timer = idle_timer;
693 0 : nhgc->nhg.nhgr.unbalanced_timer = unbalanced_timer;
694 :
695 0 : if (nhg_hooks.modify)
696 0 : nhg_hooks.modify(nhgc);
697 :
698 : return CMD_SUCCESS;
699 : }
700 :
701 0 : DEFPY(no_nexthop_group_resilience,
702 : no_nexthop_group_resilience_cmd,
703 : "no resilient [buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)]",
704 : NO_STR
705 : "A resilient Nexthop Group\n"
706 : "Buckets in the Hash for this Group\n"
707 : "Number of buckets\n"
708 : "The Idle timer for this Resilient Nexthop Group in seconds\n"
709 : "Number of seconds of Idle time\n"
710 : "The length of time that the Nexthop Group can be unbalanced\n"
711 : "Number of seconds of Unbalanced time\n")
712 : {
713 0 : VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
714 :
715 0 : nhgc->nhg.nhgr.buckets = 0;
716 0 : nhgc->nhg.nhgr.idle_timer = 0;
717 0 : nhgc->nhg.nhgr.unbalanced_timer = 0;
718 :
719 0 : return CMD_SUCCESS;
720 : }
721 :
722 0 : static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
723 : const char *nhvrf_name,
724 : const union sockunion *addr,
725 : const char *intf, bool onlink,
726 : const char *labels, const uint32_t weight,
727 : const char *backup_str)
728 : {
729 0 : struct nexthop_hold *nh;
730 :
731 0 : nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
732 :
733 0 : if (nhvrf_name)
734 0 : nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
735 0 : if (intf)
736 0 : nh->intf = XSTRDUP(MTYPE_TMP, intf);
737 0 : if (addr)
738 0 : nh->addr = sockunion_dup(addr);
739 0 : if (labels)
740 0 : nh->labels = XSTRDUP(MTYPE_TMP, labels);
741 :
742 0 : nh->onlink = onlink;
743 :
744 0 : nh->weight = weight;
745 :
746 0 : if (backup_str)
747 0 : nh->backup_str = XSTRDUP(MTYPE_TMP, backup_str);
748 :
749 0 : listnode_add_sort(nhgc->nhg_list, nh);
750 0 : }
751 :
752 : /*
753 : * Remove config info about a nexthop from group 'nhgc'. Note that we
754 : * use only a subset of the available attributes here to determine
755 : * a 'match'.
756 : * Note that this doesn't change the list of nexthops, only the config
757 : * information.
758 : */
759 0 : static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
760 : const char *nhvrf_name,
761 : const union sockunion *addr,
762 : const char *intf)
763 : {
764 0 : struct nexthop_hold *nh;
765 0 : struct listnode *node;
766 :
767 0 : for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
768 0 : if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
769 0 : && nhgc_addr_cmp_helper(addr, nh->addr) == 0
770 0 : && nhgc_cmp_helper(intf, nh->intf) == 0)
771 : break;
772 : }
773 :
774 : /*
775 : * Something has gone seriously wrong, fail gracefully
776 : */
777 0 : if (!nh)
778 : return;
779 :
780 0 : list_delete_node(nhgc->nhg_list, node);
781 0 : nhgl_delete(nh);
782 : }
783 :
784 : /*
785 : * Parse the config strings we support for a single nexthop. This gets used
786 : * in a couple of different ways, and we distinguish between transient
787 : * failures - such as a still-unprocessed interface - and fatal errors
788 : * from label-string parsing.
789 : */
790 0 : static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
791 : const union sockunion *addr,
792 : const char *intf, bool onlink,
793 : const char *name, const char *labels,
794 : int *lbl_ret, uint32_t weight,
795 : const char *backup_str)
796 : {
797 0 : int ret = 0;
798 0 : struct vrf *vrf;
799 0 : int num;
800 :
801 0 : memset(nhop, 0, sizeof(*nhop));
802 :
803 0 : if (name)
804 0 : vrf = vrf_lookup_by_name(name);
805 : else
806 0 : vrf = vrf_lookup_by_id(VRF_DEFAULT);
807 :
808 0 : if (!vrf)
809 : return false;
810 :
811 0 : nhop->vrf_id = vrf->vrf_id;
812 :
813 0 : if (intf) {
814 0 : nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
815 0 : if (nhop->ifindex == IFINDEX_INTERNAL)
816 : return false;
817 : }
818 :
819 0 : if (onlink)
820 0 : SET_FLAG(nhop->flags, NEXTHOP_FLAG_ONLINK);
821 :
822 0 : if (addr) {
823 0 : if (addr->sa.sa_family == AF_INET) {
824 0 : nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
825 0 : if (intf)
826 0 : nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
827 : else
828 0 : nhop->type = NEXTHOP_TYPE_IPV4;
829 : } else {
830 0 : nhop->gate.ipv6 = addr->sin6.sin6_addr;
831 0 : if (intf)
832 0 : nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
833 : else
834 0 : nhop->type = NEXTHOP_TYPE_IPV6;
835 : }
836 : } else
837 0 : nhop->type = NEXTHOP_TYPE_IFINDEX;
838 :
839 0 : if (labels) {
840 0 : uint8_t num = 0;
841 0 : mpls_label_t larray[MPLS_MAX_LABELS];
842 :
843 0 : ret = mpls_str2label(labels, &num, larray);
844 :
845 : /* Return label parse result */
846 0 : if (lbl_ret)
847 0 : *lbl_ret = ret;
848 :
849 0 : if (ret < 0)
850 0 : return false;
851 0 : else if (num > 0)
852 0 : nexthop_add_labels(nhop, ZEBRA_LSP_NONE,
853 : num, larray);
854 : }
855 :
856 0 : nhop->weight = weight;
857 :
858 0 : if (backup_str) {
859 : /* Parse backup indexes */
860 0 : ret = nexthop_str2backups(backup_str,
861 0 : &num, nhop->backup_idx);
862 0 : if (ret == 0) {
863 0 : SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
864 0 : nhop->backup_num = num;
865 : } else
866 : return false;
867 : }
868 :
869 : return true;
870 : }
871 :
872 : /*
873 : * Wrapper to parse the strings in a 'nexthop_hold'
874 : */
875 0 : static bool nexthop_group_parse_nhh(struct nexthop *nhop,
876 : const struct nexthop_hold *nhh)
877 : {
878 0 : return (nexthop_group_parse_nexthop(
879 0 : nhop, nhh->addr, nhh->intf, nhh->onlink, nhh->nhvrf_name,
880 0 : nhh->labels, NULL, nhh->weight, nhh->backup_str));
881 : }
882 :
883 0 : DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
884 : "[no] nexthop\
885 : <\
886 : <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
887 : |INTERFACE$intf\
888 : >\
889 : [{ \
890 : nexthop-vrf NAME$vrf_name \
891 : |label WORD \
892 : |weight (1-255) \
893 : |backup-idx WORD \
894 : }]",
895 : NO_STR
896 : "Specify one of the nexthops in this ECMP group\n"
897 : "v4 Address\n"
898 : "v6 Address\n"
899 : "Interface to use\n"
900 : "Treat nexthop as directly attached to the interface\n"
901 : "Interface to use\n"
902 : "If the nexthop is in a different vrf tell us\n"
903 : "The nexthop-vrf Name\n"
904 : "Specify label(s) for this nexthop\n"
905 : "One or more labels in the range (16-1048575) separated by '/'\n"
906 : "Weight to be used by the nexthop for purposes of ECMP\n"
907 : "Weight value to be used\n"
908 : "Specify backup nexthop indexes in another group\n"
909 : "One or more indexes in the range (0-254) separated by ','\n")
910 : {
911 0 : VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
912 0 : struct nexthop nhop;
913 0 : struct nexthop *nh;
914 0 : int lbl_ret = 0;
915 0 : bool legal;
916 0 : int num;
917 0 : uint8_t backups[NEXTHOP_MAX_BACKUPS];
918 0 : bool yes = !no;
919 :
920 : /* Pre-parse backup string to validate */
921 0 : if (backup_idx) {
922 0 : lbl_ret = nexthop_str2backups(backup_idx, &num, backups);
923 0 : if (lbl_ret < 0) {
924 0 : vty_out(vty, "%% Invalid backups\n");
925 0 : return CMD_WARNING_CONFIG_FAILED;
926 : }
927 : }
928 :
929 0 : legal = nexthop_group_parse_nexthop(&nhop, addr, intf, !!onlink,
930 : vrf_name, label, &lbl_ret, weight,
931 : backup_idx);
932 :
933 0 : if (nhop.type == NEXTHOP_TYPE_IPV6
934 0 : && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
935 0 : vty_out(vty,
936 : "Specified a v6 LL with no interface, rejecting\n");
937 0 : return CMD_WARNING_CONFIG_FAILED;
938 : }
939 :
940 : /* Handle label-string errors */
941 0 : if (!legal && lbl_ret < 0) {
942 0 : switch (lbl_ret) {
943 0 : case -1:
944 0 : vty_out(vty, "%% Malformed label(s)\n");
945 0 : break;
946 0 : case -2:
947 0 : vty_out(vty,
948 : "%% Cannot use reserved label(s) (%d-%d)\n",
949 : MPLS_LABEL_RESERVED_MIN,
950 : MPLS_LABEL_RESERVED_MAX);
951 0 : break;
952 0 : case -3:
953 0 : vty_out(vty,
954 : "%% Too many labels. Enter %d or fewer\n",
955 : MPLS_MAX_LABELS);
956 0 : break;
957 : }
958 0 : return CMD_WARNING_CONFIG_FAILED;
959 : }
960 :
961 : /* Look for an existing nexthop in the config. Note that the test
962 : * here tests only some attributes - it's not a complete comparison.
963 : * Note that we've got two kinds of objects to manage: 'nexthop_hold'
964 : * that represent config that may or may not be valid (yet), and
965 : * actual nexthops that have been validated and parsed.
966 : */
967 0 : nh = nhg_nh_find(&nhgc->nhg, &nhop);
968 :
969 : /* Always attempt to remove old config info. */
970 0 : nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
971 :
972 : /* Remove any existing nexthop, for delete and replace cases. */
973 0 : if (nh) {
974 0 : nexthop_unlink(&nhgc->nhg, nh);
975 :
976 0 : if (nhg_hooks.del_nexthop)
977 0 : nhg_hooks.del_nexthop(nhgc, nh);
978 :
979 0 : nexthop_free(nh);
980 : }
981 0 : if (yes) {
982 : /* Add/replace case: capture nexthop if valid, and capture
983 : * config info always.
984 : */
985 0 : if (legal) {
986 0 : nh = nexthop_new();
987 :
988 0 : memcpy(nh, &nhop, sizeof(nhop));
989 0 : _nexthop_add(&nhgc->nhg.nexthop, nh);
990 : }
991 :
992 : /* Save config always */
993 0 : nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, !!onlink,
994 : label, weight, backup_idx);
995 :
996 0 : if (legal && nhg_hooks.add_nexthop)
997 0 : nhg_hooks.add_nexthop(nhgc, nh);
998 : }
999 :
1000 : return CMD_SUCCESS;
1001 : }
1002 :
1003 : static int nexthop_group_write(struct vty *vty);
1004 : static struct cmd_node nexthop_group_node = {
1005 : .name = "nexthop-group",
1006 : .node = NH_GROUP_NODE,
1007 : .parent_node = CONFIG_NODE,
1008 : .prompt = "%s(config-nh-group)# ",
1009 : .config_write = nexthop_group_write,
1010 : };
1011 :
1012 0 : void nexthop_group_write_nexthop_simple(struct vty *vty,
1013 : const struct nexthop *nh,
1014 : char *altifname)
1015 : {
1016 0 : char *ifname;
1017 :
1018 0 : vty_out(vty, "nexthop ");
1019 :
1020 0 : if (altifname)
1021 : ifname = altifname;
1022 : else
1023 0 : ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id);
1024 :
1025 0 : switch (nh->type) {
1026 0 : case NEXTHOP_TYPE_IFINDEX:
1027 0 : vty_out(vty, "%s", ifname);
1028 0 : break;
1029 0 : case NEXTHOP_TYPE_IPV4:
1030 0 : vty_out(vty, "%pI4", &nh->gate.ipv4);
1031 0 : break;
1032 0 : case NEXTHOP_TYPE_IPV4_IFINDEX:
1033 0 : vty_out(vty, "%pI4 %s", &nh->gate.ipv4, ifname);
1034 0 : break;
1035 0 : case NEXTHOP_TYPE_IPV6:
1036 0 : vty_out(vty, "%pI6", &nh->gate.ipv6);
1037 0 : break;
1038 0 : case NEXTHOP_TYPE_IPV6_IFINDEX:
1039 0 : vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname);
1040 0 : break;
1041 : case NEXTHOP_TYPE_BLACKHOLE:
1042 : break;
1043 : }
1044 0 : }
1045 :
1046 0 : void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
1047 : {
1048 0 : struct vrf *vrf;
1049 0 : int i;
1050 :
1051 0 : nexthop_group_write_nexthop_simple(vty, nh, NULL);
1052 :
1053 0 : if (nh->vrf_id != VRF_DEFAULT) {
1054 0 : vrf = vrf_lookup_by_id(nh->vrf_id);
1055 0 : vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf));
1056 : }
1057 :
1058 0 : if (nh->nh_label && nh->nh_label->num_labels > 0) {
1059 0 : char buf[200];
1060 :
1061 0 : mpls_label2str(nh->nh_label->num_labels,
1062 0 : nh->nh_label->label,
1063 : buf, sizeof(buf), 0);
1064 0 : vty_out(vty, " label %s", buf);
1065 : }
1066 :
1067 0 : if (nh->weight)
1068 0 : vty_out(vty, " weight %u", nh->weight);
1069 :
1070 0 : if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1071 0 : vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
1072 :
1073 0 : for (i = 1; i < nh->backup_num; i++)
1074 0 : vty_out(vty, ",%d", nh->backup_idx[i]);
1075 : }
1076 :
1077 0 : vty_out(vty, "\n");
1078 0 : }
1079 :
1080 0 : void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
1081 : {
1082 0 : struct vrf *vrf;
1083 0 : json_object *json_backups = NULL;
1084 0 : int i;
1085 :
1086 0 : switch (nh->type) {
1087 0 : case NEXTHOP_TYPE_IFINDEX:
1088 0 : json_object_string_add(j, "nexthop",
1089 0 : ifindex2ifname(nh->ifindex, nh->vrf_id));
1090 0 : break;
1091 0 : case NEXTHOP_TYPE_IPV4:
1092 0 : json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
1093 0 : break;
1094 0 : case NEXTHOP_TYPE_IPV4_IFINDEX:
1095 0 : json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
1096 0 : json_object_string_add(j, "vrfId",
1097 0 : ifindex2ifname(nh->ifindex, nh->vrf_id));
1098 0 : break;
1099 0 : case NEXTHOP_TYPE_IPV6:
1100 0 : json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
1101 0 : break;
1102 0 : case NEXTHOP_TYPE_IPV6_IFINDEX:
1103 0 : json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
1104 0 : json_object_string_add(j, "vrfId",
1105 0 : ifindex2ifname(nh->ifindex, nh->vrf_id));
1106 0 : break;
1107 : case NEXTHOP_TYPE_BLACKHOLE:
1108 : break;
1109 : }
1110 :
1111 0 : if (nh->vrf_id != VRF_DEFAULT) {
1112 0 : vrf = vrf_lookup_by_id(nh->vrf_id);
1113 0 : json_object_string_add(j, "targetVrf", vrf->name);
1114 : }
1115 :
1116 0 : if (nh->nh_label && nh->nh_label->num_labels > 0) {
1117 0 : char buf[200];
1118 :
1119 0 : mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label,
1120 : buf, sizeof(buf), 0);
1121 0 : json_object_string_add(j, "label", buf);
1122 : }
1123 :
1124 0 : if (nh->weight)
1125 0 : json_object_int_add(j, "weight", nh->weight);
1126 :
1127 0 : if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1128 0 : json_backups = json_object_new_array();
1129 0 : for (i = 0; i < nh->backup_num; i++)
1130 0 : json_object_array_add(
1131 : json_backups,
1132 0 : json_object_new_int(nh->backup_idx[i]));
1133 :
1134 0 : json_object_object_add(j, "backupIdx", json_backups);
1135 : }
1136 0 : }
1137 :
1138 0 : static void nexthop_group_write_nexthop_internal(struct vty *vty,
1139 : const struct nexthop_hold *nh)
1140 : {
1141 0 : vty_out(vty, "nexthop");
1142 :
1143 0 : if (nh->addr)
1144 0 : vty_out(vty, " %pSU", nh->addr);
1145 :
1146 0 : if (nh->intf)
1147 0 : vty_out(vty, " %s", nh->intf);
1148 :
1149 0 : if (nh->onlink)
1150 0 : vty_out(vty, " onlink");
1151 :
1152 0 : if (nh->nhvrf_name)
1153 0 : vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
1154 :
1155 0 : if (nh->labels)
1156 0 : vty_out(vty, " label %s", nh->labels);
1157 :
1158 0 : if (nh->weight)
1159 0 : vty_out(vty, " weight %u", nh->weight);
1160 :
1161 0 : if (nh->backup_str)
1162 0 : vty_out(vty, " backup-idx %s", nh->backup_str);
1163 :
1164 0 : vty_out(vty, "\n");
1165 0 : }
1166 :
1167 0 : static int nexthop_group_write(struct vty *vty)
1168 : {
1169 0 : struct nexthop_group_cmd *nhgc;
1170 0 : struct nexthop_hold *nh;
1171 :
1172 0 : RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1173 0 : struct listnode *node;
1174 :
1175 0 : vty_out(vty, "nexthop-group %s\n", nhgc->name);
1176 :
1177 0 : if (nhgc->nhg.nhgr.buckets)
1178 0 : vty_out(vty,
1179 : " resilient buckets %u idle-timer %u unbalanced-timer %u\n",
1180 : nhgc->nhg.nhgr.buckets,
1181 : nhgc->nhg.nhgr.idle_timer,
1182 : nhgc->nhg.nhgr.unbalanced_timer);
1183 :
1184 0 : if (nhgc->backup_list_name[0])
1185 0 : vty_out(vty, " backup-group %s\n",
1186 0 : nhgc->backup_list_name);
1187 :
1188 0 : for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
1189 0 : vty_out(vty, " ");
1190 0 : nexthop_group_write_nexthop_internal(vty, nh);
1191 : }
1192 :
1193 0 : vty_out(vty, "exit\n");
1194 0 : vty_out(vty, "!\n");
1195 : }
1196 :
1197 0 : return 1;
1198 : }
1199 :
1200 3 : void nexthop_group_enable_vrf(struct vrf *vrf)
1201 : {
1202 3 : struct nexthop_group_cmd *nhgc;
1203 3 : struct nexthop_hold *nhh;
1204 :
1205 6 : RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1206 0 : struct listnode *node;
1207 :
1208 0 : for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1209 0 : struct nexthop nhop;
1210 0 : struct nexthop *nh;
1211 :
1212 0 : if (!nexthop_group_parse_nhh(&nhop, nhh))
1213 0 : continue;
1214 :
1215 0 : nh = nexthop_exists(&nhgc->nhg, &nhop);
1216 :
1217 0 : if (nh)
1218 0 : continue;
1219 :
1220 0 : if (nhop.vrf_id != vrf->vrf_id)
1221 0 : continue;
1222 :
1223 0 : nh = nexthop_new();
1224 :
1225 0 : memcpy(nh, &nhop, sizeof(nhop));
1226 0 : _nexthop_add(&nhgc->nhg.nexthop, nh);
1227 :
1228 0 : if (nhg_hooks.add_nexthop)
1229 0 : nhg_hooks.add_nexthop(nhgc, nh);
1230 : }
1231 : }
1232 3 : }
1233 :
1234 3 : void nexthop_group_disable_vrf(struct vrf *vrf)
1235 : {
1236 3 : struct nexthop_group_cmd *nhgc;
1237 3 : struct nexthop_hold *nhh;
1238 :
1239 6 : RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1240 0 : struct listnode *node;
1241 :
1242 0 : for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1243 0 : struct nexthop nhop;
1244 0 : struct nexthop *nh;
1245 :
1246 0 : if (!nexthop_group_parse_nhh(&nhop, nhh))
1247 0 : continue;
1248 :
1249 0 : nh = nexthop_exists(&nhgc->nhg, &nhop);
1250 :
1251 0 : if (!nh)
1252 0 : continue;
1253 :
1254 0 : if (nh->vrf_id != vrf->vrf_id)
1255 0 : continue;
1256 :
1257 0 : _nexthop_del(&nhgc->nhg, nh);
1258 :
1259 0 : if (nhg_hooks.del_nexthop)
1260 0 : nhg_hooks.del_nexthop(nhgc, nh);
1261 :
1262 0 : nexthop_free(nh);
1263 : }
1264 : }
1265 3 : }
1266 :
1267 22 : void nexthop_group_interface_state_change(struct interface *ifp,
1268 : ifindex_t oldifindex)
1269 : {
1270 22 : struct nexthop_group_cmd *nhgc;
1271 22 : struct nexthop_hold *nhh;
1272 :
1273 44 : RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1274 0 : struct listnode *node;
1275 0 : struct nexthop *nh;
1276 :
1277 0 : if (if_is_up(ifp)) {
1278 0 : for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1279 0 : struct nexthop nhop;
1280 :
1281 0 : if (!nexthop_group_parse_nhh(&nhop, nhh))
1282 0 : continue;
1283 :
1284 0 : switch (nhop.type) {
1285 0 : case NEXTHOP_TYPE_IPV4:
1286 : case NEXTHOP_TYPE_IPV6:
1287 : case NEXTHOP_TYPE_BLACKHOLE:
1288 0 : continue;
1289 : case NEXTHOP_TYPE_IFINDEX:
1290 : case NEXTHOP_TYPE_IPV4_IFINDEX:
1291 : case NEXTHOP_TYPE_IPV6_IFINDEX:
1292 : break;
1293 : }
1294 0 : nh = nexthop_exists(&nhgc->nhg, &nhop);
1295 :
1296 0 : if (nh)
1297 0 : continue;
1298 :
1299 0 : if (ifp->ifindex != nhop.ifindex)
1300 0 : continue;
1301 :
1302 0 : nh = nexthop_new();
1303 :
1304 0 : memcpy(nh, &nhop, sizeof(nhop));
1305 0 : _nexthop_add(&nhgc->nhg.nexthop, nh);
1306 :
1307 0 : if (nhg_hooks.add_nexthop)
1308 0 : nhg_hooks.add_nexthop(nhgc, nh);
1309 : }
1310 : } else {
1311 0 : struct nexthop *next_nh;
1312 :
1313 0 : for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
1314 0 : next_nh = nh->next;
1315 0 : switch (nh->type) {
1316 0 : case NEXTHOP_TYPE_IPV4:
1317 : case NEXTHOP_TYPE_IPV6:
1318 : case NEXTHOP_TYPE_BLACKHOLE:
1319 0 : continue;
1320 : case NEXTHOP_TYPE_IFINDEX:
1321 : case NEXTHOP_TYPE_IPV4_IFINDEX:
1322 : case NEXTHOP_TYPE_IPV6_IFINDEX:
1323 : break;
1324 : }
1325 :
1326 0 : if (oldifindex != nh->ifindex)
1327 0 : continue;
1328 :
1329 0 : _nexthop_del(&nhgc->nhg, nh);
1330 :
1331 0 : if (nhg_hooks.del_nexthop)
1332 0 : nhg_hooks.del_nexthop(nhgc, nh);
1333 :
1334 0 : nexthop_free(nh);
1335 : }
1336 : }
1337 : }
1338 22 : }
1339 :
1340 0 : static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
1341 : {
1342 0 : struct nexthop_group_cmd *nhgc;
1343 :
1344 0 : RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1345 0 : vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
1346 : }
1347 0 : }
1348 :
1349 : static const struct cmd_variable_handler nhg_name_handlers[] = {
1350 : {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1351 : {.completions = NULL}};
1352 :
1353 0 : void nexthop_group_init(void (*new)(const char *name),
1354 : void (*modify)(const struct nexthop_group_cmd *nhgc),
1355 : void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
1356 : const struct nexthop *nhop),
1357 : void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
1358 : const struct nexthop *nhop),
1359 : void (*delete)(const char *name))
1360 : {
1361 0 : RB_INIT(nhgc_entry_head, &nhgc_entries);
1362 :
1363 0 : cmd_variable_handler_register(nhg_name_handlers);
1364 :
1365 0 : install_node(&nexthop_group_node);
1366 0 : install_element(CONFIG_NODE, &nexthop_group_cmd);
1367 0 : install_element(CONFIG_NODE, &no_nexthop_group_cmd);
1368 :
1369 0 : install_default(NH_GROUP_NODE);
1370 0 : install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
1371 0 : install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
1372 0 : install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
1373 :
1374 0 : install_element(NH_GROUP_NODE, &nexthop_group_resilience_cmd);
1375 0 : install_element(NH_GROUP_NODE, &no_nexthop_group_resilience_cmd);
1376 :
1377 0 : memset(&nhg_hooks, 0, sizeof(nhg_hooks));
1378 :
1379 0 : if (new)
1380 0 : nhg_hooks.new = new;
1381 0 : if (modify)
1382 0 : nhg_hooks.modify = modify;
1383 0 : if (add_nexthop)
1384 0 : nhg_hooks.add_nexthop = add_nexthop;
1385 0 : if (del_nexthop)
1386 0 : nhg_hooks.del_nexthop = del_nexthop;
1387 0 : if (delete)
1388 0 : nhg_hooks.delete = delete;
1389 0 : }
|