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