]> git.proxmox.com Git - mirror_frr.git/blame - lib/nexthop_group.c
Merge pull request #6167 from qlyoung/fix-cluster-list-uaf
[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
f17f2c5d
SW
150static bool
151nexthop_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 */
173bool 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 */
197bool 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
218struct nexthop_group *nexthop_group_new(void)
219{
220 return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
221}
222
c13bfa74
MS
223void 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 230void 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 243void _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 */
257static 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
287void 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 310void _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 */
336void 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 367void 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 379uint32_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
394uint32_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
405void 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
423static 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 441struct 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
450static 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
464static 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
478static 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
497static 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
511static 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
534static 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
548DEFINE_QOBJ_TYPE(nexthop_group_cmd)
549
868ee86c 550DEFUN_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 563DEFUN_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
578DEFPY(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
590DEFPY(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
603static 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
629static 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
663static 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 */
741static 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 749DEFPY(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 862static struct cmd_node nexthop_group_node = {
dba32923
DS
863 NH_GROUP_NODE,
864 "%s(config-nh-group)# ",
865 1
866};
867
1b7bce04
DS
868void 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
922static 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
950static 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
975void 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
1009void 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
1042void 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
1115static 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
1124static const struct cmd_variable_handler nhg_name_handlers[] = {
1125 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1126 {.completions = NULL}};
1127
31919191
DS
1128void 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}