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