]> git.proxmox.com Git - mirror_frr.git/blame - lib/nexthop_group.c
zebra: include installed backup nexthops in kernel update
[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 616
20953065 617 QOBJ_UNREG(nhgc);
c57bd6bb 618 XFREE(MTYPE_TMP, nhgc);
31919191
DS
619}
620
621DEFINE_QOBJ_TYPE(nexthop_group_cmd)
622
868ee86c 623DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME",
31919191
DS
624 "Enter into the nexthop-group submode\n"
625 "Specify the NAME of the nexthop-group\n")
626{
627 const char *nhg_name = argv[1]->arg;
628 struct nexthop_group_cmd *nhgc = NULL;
629
630 nhgc = nhgc_get(nhg_name);
631 VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
632
633 return CMD_SUCCESS;
634}
635
868ee86c 636DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
31919191
DS
637 NO_STR
638 "Delete the nexthop-group\n"
639 "Specify the NAME of the nexthop-group\n")
640{
641 const char *nhg_name = argv[2]->arg;
642 struct nexthop_group_cmd *nhgc = NULL;
643
644 nhgc = nhgc_find(nhg_name);
645 if (nhgc)
646 nhgc_delete(nhgc);
647
648 return CMD_SUCCESS;
649}
650
0a8881b4
MS
651DEFPY(nexthop_group_backup, nexthop_group_backup_cmd,
652 "backup-group WORD$name",
653 "Specify a group name containing backup nexthops\n"
654 "The name of the backup group\n")
655{
656 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
657
658 strlcpy(nhgc->backup_list_name, name, sizeof(nhgc->backup_list_name));
659
660 return CMD_SUCCESS;
661}
662
663DEFPY(no_nexthop_group_backup, no_nexthop_group_backup_cmd,
664 "no backup-group [WORD$name]",
665 NO_STR
666 "Clear group name containing backup nexthops\n"
667 "The name of the backup group\n")
668{
669 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
670
671 nhgc->backup_list_name[0] = 0;
672
673 return CMD_SUCCESS;
674}
675
c57bd6bb
DS
676static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
677 const char *nhvrf_name,
678 const union sockunion *addr,
597371a6 679 const char *intf, const char *labels,
0a8881b4 680 const uint32_t weight, int backup_idx)
c57bd6bb
DS
681{
682 struct nexthop_hold *nh;
683
684 nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
685
686 if (nhvrf_name)
687 nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
688 if (intf)
689 nh->intf = XSTRDUP(MTYPE_TMP, intf);
1c869b64
RW
690 if (addr)
691 nh->addr = sockunion_dup(addr);
db2fede9
MS
692 if (labels)
693 nh->labels = XSTRDUP(MTYPE_TMP, labels);
c57bd6bb 694
597371a6
DS
695 nh->weight = weight;
696
0a8881b4
MS
697 nh->backup_idx = backup_idx;
698
c57bd6bb
DS
699 listnode_add_sort(nhgc->nhg_list, nh);
700}
701
4fda105e
MS
702/*
703 * Remove config info about a nexthop from group 'nhgc'. Note that we
704 * use only a subset of the available attributes here to determine
705 * a 'match'.
706 * Note that this doesn't change the list of nexthops, only the config
707 * information.
708 */
c57bd6bb
DS
709static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
710 const char *nhvrf_name,
711 const union sockunion *addr,
4fda105e 712 const char *intf)
c57bd6bb
DS
713{
714 struct nexthop_hold *nh;
715 struct listnode *node;
716
717 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
597371a6
DS
718 if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
719 && nhgc_addr_cmp_helper(addr, nh->addr) == 0
4fda105e 720 && nhgc_cmp_helper(intf, nh->intf) == 0)
c57bd6bb
DS
721 break;
722 }
723
724 /*
725 * Something has gone seriously wrong, fail gracefully
726 */
727 if (!nh)
728 return;
729
730 list_delete_node(nhgc->nhg_list, node);
e5a501c2 731 nhgl_delete(nh);
c57bd6bb
DS
732}
733
db2fede9
MS
734/*
735 * Parse the config strings we support for a single nexthop. This gets used
736 * in a couple of different ways, and we distinguish between transient
737 * failures - such as a still-unprocessed interface - and fatal errors
738 * from label-string parsing.
739 */
98cbbaea
DS
740static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
741 const union sockunion *addr,
db2fede9 742 const char *intf, const char *name,
597371a6 743 const char *labels, int *lbl_ret,
0a8881b4 744 uint32_t weight, int backup_idx)
31919191 745{
db2fede9 746 int ret = 0;
31919191 747 struct vrf *vrf;
98cbbaea
DS
748
749 memset(nhop, 0, sizeof(*nhop));
31919191
DS
750
751 if (name)
752 vrf = vrf_lookup_by_name(name);
753 else
754 vrf = vrf_lookup_by_id(VRF_DEFAULT);
755
98cbbaea
DS
756 if (!vrf)
757 return false;
31919191 758
98cbbaea 759 nhop->vrf_id = vrf->vrf_id;
31919191 760
1c869b64
RW
761 if (intf) {
762 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
763 if (nhop->ifindex == IFINDEX_INTERNAL)
764 return false;
98cbbaea
DS
765 }
766
1c869b64
RW
767 if (addr) {
768 if (addr->sa.sa_family == AF_INET) {
769 nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
770 if (intf)
771 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
772 else
773 nhop->type = NEXTHOP_TYPE_IPV4;
774 } else {
775 nhop->gate.ipv6 = addr->sin6.sin6_addr;
776 if (intf)
777 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
778 else
779 nhop->type = NEXTHOP_TYPE_IPV6;
780 }
781 } else
782 nhop->type = NEXTHOP_TYPE_IFINDEX;
783
db2fede9
MS
784 if (labels) {
785 uint8_t num = 0;
786 mpls_label_t larray[MPLS_MAX_LABELS];
787
788 ret = mpls_str2label(labels, &num, larray);
789
790 /* Return label parse result */
791 if (lbl_ret)
792 *lbl_ret = ret;
793
794 if (ret < 0)
795 return false;
796 else if (num > 0)
797 nexthop_add_labels(nhop, ZEBRA_LSP_NONE,
798 num, larray);
799 }
800
597371a6
DS
801 nhop->weight = weight;
802
0a8881b4
MS
803 if (backup_idx != NHH_BACKUP_IDX_INVALID) {
804 /* Validate index value */
805 if (backup_idx > NEXTHOP_BACKUP_IDX_MAX)
806 return false;
807
808 SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
809 nhop->backup_idx = backup_idx;
810 }
811
98cbbaea
DS
812 return true;
813}
814
db2fede9
MS
815/*
816 * Wrapper to parse the strings in a 'nexthop_hold'
817 */
818static bool nexthop_group_parse_nhh(struct nexthop *nhop,
819 const struct nexthop_hold *nhh)
820{
597371a6
DS
821 return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf,
822 nhh->nhvrf_name, nhh->labels, NULL,
0a8881b4 823 nhh->weight, nhh->backup_idx));
db2fede9
MS
824}
825
98cbbaea 826DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
1c869b64
RW
827 "[no] nexthop\
828 <\
829 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
830 |INTERFACE$intf\
831 >\
db2fede9
MS
832 [{ \
833 nexthop-vrf NAME$vrf_name \
834 |label WORD \
597371a6 835 |weight (1-255) \
0a8881b4 836 |backup-idx$bi_str (0-254)$idx \
db2fede9 837 }]",
98cbbaea
DS
838 NO_STR
839 "Specify one of the nexthops in this ECMP group\n"
840 "v4 Address\n"
841 "v6 Address\n"
842 "Interface to use\n"
1c869b64 843 "Interface to use\n"
98cbbaea 844 "If the nexthop is in a different vrf tell us\n"
db2fede9
MS
845 "The nexthop-vrf Name\n"
846 "Specify label(s) for this nexthop\n"
597371a6
DS
847 "One or more labels in the range (16-1048575) separated by '/'\n"
848 "Weight to be used by the nexthop for purposes of ECMP\n"
0a8881b4
MS
849 "Weight value to be used\n"
850 "Backup nexthop index in another group\n"
851 "Nexthop index value\n")
98cbbaea
DS
852{
853 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
854 struct nexthop nhop;
855 struct nexthop *nh;
db2fede9 856 int lbl_ret = 0;
98cbbaea 857 bool legal;
0a8881b4 858 int backup_idx = idx;
4fda105e 859 bool yes = !no;
0a8881b4
MS
860
861 if (bi_str == NULL)
862 backup_idx = NHH_BACKUP_IDX_INVALID;
98cbbaea 863
597371a6 864 legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label,
0a8881b4 865 &lbl_ret, weight, backup_idx);
98cbbaea
DS
866
867 if (nhop.type == NEXTHOP_TYPE_IPV6
868 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
869 vty_out(vty,
870 "Specified a v6 LL with no interface, rejecting\n");
871 return CMD_WARNING_CONFIG_FAILED;
31919191
DS
872 }
873
db2fede9
MS
874 /* Handle label-string errors */
875 if (!legal && lbl_ret < 0) {
876 switch (lbl_ret) {
877 case -1:
878 vty_out(vty, "%% Malformed label(s)\n");
879 break;
880 case -2:
881 vty_out(vty,
882 "%% Cannot use reserved label(s) (%d-%d)\n",
883 MPLS_LABEL_RESERVED_MIN,
884 MPLS_LABEL_RESERVED_MAX);
885 break;
886 case -3:
887 vty_out(vty,
888 "%% Too many labels. Enter %d or fewer\n",
889 MPLS_MAX_LABELS);
890 break;
891 }
892 return CMD_WARNING_CONFIG_FAILED;
893 }
894
4fda105e
MS
895 /* Look for an existing nexthop in the config. Note that the test
896 * here tests only some attributes - it's not a complete comparison.
897 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
898 * that represent config that may or may not be valid (yet), and
899 * actual nexthops that have been validated and parsed.
900 */
901 nh = nhg_nh_find(&nhgc->nhg, &nhop);
31919191 902
4fda105e
MS
903 /* Always attempt to remove old config info. */
904 nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
0a8881b4 905
4fda105e
MS
906 /* Remove any existing nexthop, for delete and replace cases. */
907 if (nh) {
908 nexthop_unlink(&nhgc->nhg, nh);
31919191 909
4fda105e
MS
910 if (nhg_hooks.del_nexthop)
911 nhg_hooks.del_nexthop(nhgc, nh);
31919191 912
4fda105e 913 nexthop_free(nh);
0a8881b4 914 }
4fda105e
MS
915 if (yes) {
916 /* Add/replace case: capture nexthop if valid, and capture
917 * config info always.
918 */
98cbbaea
DS
919 if (legal) {
920 nh = nexthop_new();
31919191 921
98cbbaea 922 memcpy(nh, &nhop, sizeof(nhop));
50d89650 923 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea 924 }
31919191 925
0a8881b4 926 /* Save config always */
597371a6 927 nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label,
0a8881b4 928 weight, backup_idx);
c57bd6bb 929
98cbbaea 930 if (legal && nhg_hooks.add_nexthop)
31919191
DS
931 nhg_hooks.add_nexthop(nhgc, nh);
932 }
933
dba32923
DS
934 return CMD_SUCCESS;
935}
936
612c2c15 937static int nexthop_group_write(struct vty *vty);
1b3e9a21 938static struct cmd_node nexthop_group_node = {
f4b8291f 939 .name = "nexthop-group",
62b346ee 940 .node = NH_GROUP_NODE,
24389580 941 .parent_node = CONFIG_NODE,
62b346ee 942 .prompt = "%s(config-nh-group)# ",
612c2c15 943 .config_write = nexthop_group_write,
dba32923
DS
944};
945
1b7bce04
DS
946void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
947{
948 char buf[100];
949 struct vrf *vrf;
950
57cdafc4 951 vty_out(vty, "nexthop ");
1b7bce04
DS
952
953 switch (nh->type) {
954 case NEXTHOP_TYPE_IFINDEX:
955 vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id));
956 break;
957 case NEXTHOP_TYPE_IPV4:
958 vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4));
959 break;
960 case NEXTHOP_TYPE_IPV4_IFINDEX:
961 vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4),
962 ifindex2ifname(nh->ifindex, nh->vrf_id));
963 break;
964 case NEXTHOP_TYPE_IPV6:
965 vty_out(vty, "%s",
966 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
967 break;
968 case NEXTHOP_TYPE_IPV6_IFINDEX:
969 vty_out(vty, "%s %s",
970 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)),
971 ifindex2ifname(nh->ifindex, nh->vrf_id));
972 break;
973 case NEXTHOP_TYPE_BLACKHOLE:
974 break;
975 }
976
977 if (nh->vrf_id != VRF_DEFAULT) {
978 vrf = vrf_lookup_by_id(nh->vrf_id);
979 vty_out(vty, " nexthop-vrf %s", vrf->name);
980 }
db2fede9
MS
981
982 if (nh->nh_label && nh->nh_label->num_labels > 0) {
983 char buf[200];
984
985 mpls_label2str(nh->nh_label->num_labels,
986 nh->nh_label->label,
987 buf, sizeof(buf), 0);
988 vty_out(vty, " label %s", buf);
989 }
990
597371a6
DS
991 if (nh->weight)
992 vty_out(vty, " weight %u", nh->weight);
993
0a8881b4
MS
994 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP))
995 vty_out(vty, " backup-idx %d", nh->backup_idx);
996
1b7bce04
DS
997 vty_out(vty, "\n");
998}
999
010dd8ed
WC
1000void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh)
1001{
1002 char buf[100];
1003 struct vrf *vrf;
1004
1005 switch (nh->type) {
1006 case NEXTHOP_TYPE_IFINDEX:
3e81618d 1007 json_object_string_add(j, "nexthop",
010dd8ed
WC
1008 ifindex2ifname(nh->ifindex, nh->vrf_id));
1009 break;
1010 case NEXTHOP_TYPE_IPV4:
3e81618d 1011 json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4));
010dd8ed
WC
1012 break;
1013 case NEXTHOP_TYPE_IPV4_IFINDEX:
3e81618d 1014 json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4));
010dd8ed
WC
1015 json_object_string_add(j, "vrfId",
1016 ifindex2ifname(nh->ifindex, nh->vrf_id));
1017 break;
1018 case NEXTHOP_TYPE_IPV6:
1019 json_object_string_add(
3e81618d 1020 j, "nexthop",
010dd8ed
WC
1021 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
1022 break;
1023 case NEXTHOP_TYPE_IPV6_IFINDEX:
1024 json_object_string_add(
3e81618d 1025 j, "nexthop",
010dd8ed
WC
1026 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
1027 json_object_string_add(j, "vrfId",
1028 ifindex2ifname(nh->ifindex, nh->vrf_id));
1029 break;
1030 case NEXTHOP_TYPE_BLACKHOLE:
1031 break;
1032 }
1033
1034 if (nh->vrf_id != VRF_DEFAULT) {
1035 vrf = vrf_lookup_by_id(nh->vrf_id);
81c0078e 1036 json_object_string_add(j, "targetVrf", vrf->name);
010dd8ed
WC
1037 }
1038
1039 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1040 char buf[200];
1041
1042 mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label,
1043 buf, sizeof(buf), 0);
1044 json_object_string_add(j, "label", buf);
1045 }
1046
1047 if (nh->weight)
1048 json_object_int_add(j, "weight", nh->weight);
1049
1050 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP))
1051 json_object_int_add(j, "backupIdx", nh->backup_idx);
1052}
1053
c57bd6bb
DS
1054static void nexthop_group_write_nexthop_internal(struct vty *vty,
1055 struct nexthop_hold *nh)
1056{
1057 char buf[100];
1058
1c869b64 1059 vty_out(vty, "nexthop");
c57bd6bb 1060
1c869b64
RW
1061 if (nh->addr)
1062 vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf)));
c57bd6bb
DS
1063
1064 if (nh->intf)
1065 vty_out(vty, " %s", nh->intf);
1066
1067 if (nh->nhvrf_name)
1068 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
1069
db2fede9
MS
1070 if (nh->labels)
1071 vty_out(vty, " label %s", nh->labels);
1072
597371a6
DS
1073 if (nh->weight)
1074 vty_out(vty, " weight %u", nh->weight);
1075
0a8881b4
MS
1076 if (nh->backup_idx != NHH_BACKUP_IDX_INVALID)
1077 vty_out(vty, " backup-idx %d", nh->backup_idx);
1078
c57bd6bb
DS
1079 vty_out(vty, "\n");
1080}
1081
dba32923
DS
1082static int nexthop_group_write(struct vty *vty)
1083{
31919191 1084 struct nexthop_group_cmd *nhgc;
c57bd6bb 1085 struct nexthop_hold *nh;
31919191
DS
1086
1087 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
c57bd6bb
DS
1088 struct listnode *node;
1089
31919191
DS
1090 vty_out(vty, "nexthop-group %s\n", nhgc->name);
1091
0a8881b4
MS
1092 if (nhgc->backup_list_name[0])
1093 vty_out(vty, " backup-group %s\n",
1094 nhgc->backup_list_name);
1095
c57bd6bb 1096 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
7dce96f0 1097 vty_out(vty, " ");
c57bd6bb 1098 nexthop_group_write_nexthop_internal(vty, nh);
811f859f 1099 }
31919191 1100
31919191
DS
1101 vty_out(vty, "!\n");
1102 }
dba32923
DS
1103
1104 return 1;
1105}
1106
98cbbaea
DS
1107void nexthop_group_enable_vrf(struct vrf *vrf)
1108{
1109 struct nexthop_group_cmd *nhgc;
1110 struct nexthop_hold *nhh;
1111
1112 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1113 struct listnode *node;
1114
1115 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1116 struct nexthop nhop;
1117 struct nexthop *nh;
1118
db2fede9 1119 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1120 continue;
1121
1122 nh = nexthop_exists(&nhgc->nhg, &nhop);
1123
1124 if (nh)
1125 continue;
1126
1127 if (nhop.vrf_id != vrf->vrf_id)
1128 continue;
1129
1130 nh = nexthop_new();
1131
1132 memcpy(nh, &nhop, sizeof(nhop));
50d89650 1133 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea
DS
1134
1135 if (nhg_hooks.add_nexthop)
1136 nhg_hooks.add_nexthop(nhgc, nh);
1137 }
1138 }
1139}
1140
1141void nexthop_group_disable_vrf(struct vrf *vrf)
1142{
1143 struct nexthop_group_cmd *nhgc;
1144 struct nexthop_hold *nhh;
1145
1146 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1147 struct listnode *node;
1148
1149 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1150 struct nexthop nhop;
1151 struct nexthop *nh;
1152
db2fede9 1153 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1154 continue;
1155
1156 nh = nexthop_exists(&nhgc->nhg, &nhop);
1157
1158 if (!nh)
1159 continue;
1160
1161 if (nh->vrf_id != vrf->vrf_id)
1162 continue;
1163
50d89650 1164 _nexthop_del(&nhgc->nhg, nh);
98cbbaea
DS
1165
1166 if (nhg_hooks.del_nexthop)
1167 nhg_hooks.del_nexthop(nhgc, nh);
1168
1169 nexthop_free(nh);
1170 }
1171 }
1172}
1173
1174void nexthop_group_interface_state_change(struct interface *ifp,
1175 ifindex_t oldifindex)
1176{
1177 struct nexthop_group_cmd *nhgc;
1178 struct nexthop_hold *nhh;
1179
1180 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1181 struct listnode *node;
1182 struct nexthop *nh;
1183
1184 if (if_is_up(ifp)) {
1185 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1186 struct nexthop nhop;
1187
db2fede9 1188 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1189 continue;
1190
1191 switch (nhop.type) {
1192 case NEXTHOP_TYPE_IPV4:
1193 case NEXTHOP_TYPE_IPV6:
1194 case NEXTHOP_TYPE_BLACKHOLE:
1195 continue;
1196 case NEXTHOP_TYPE_IFINDEX:
1197 case NEXTHOP_TYPE_IPV4_IFINDEX:
1198 case NEXTHOP_TYPE_IPV6_IFINDEX:
1199 break;
1200 }
1201 nh = nexthop_exists(&nhgc->nhg, &nhop);
1202
1203 if (nh)
1204 continue;
1205
1206 if (ifp->ifindex != nhop.ifindex)
1207 continue;
1208
1209 nh = nexthop_new();
1210
1211 memcpy(nh, &nhop, sizeof(nhop));
50d89650 1212 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea
DS
1213
1214 if (nhg_hooks.add_nexthop)
1215 nhg_hooks.add_nexthop(nhgc, nh);
1216 }
1217 } else {
1218 struct nexthop *next_nh;
1219
1220 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
1221 next_nh = nh->next;
1222 switch (nh->type) {
1223 case NEXTHOP_TYPE_IPV4:
1224 case NEXTHOP_TYPE_IPV6:
1225 case NEXTHOP_TYPE_BLACKHOLE:
1226 continue;
1227 case NEXTHOP_TYPE_IFINDEX:
1228 case NEXTHOP_TYPE_IPV4_IFINDEX:
1229 case NEXTHOP_TYPE_IPV6_IFINDEX:
1230 break;
1231 }
1232
1233 if (oldifindex != nh->ifindex)
1234 continue;
1235
50d89650 1236 _nexthop_del(&nhgc->nhg, nh);
98cbbaea
DS
1237
1238 if (nhg_hooks.del_nexthop)
1239 nhg_hooks.del_nexthop(nhgc, nh);
1240
1241 nexthop_free(nh);
1242 }
1243 }
1244 }
1245}
1246
868ee86c
DS
1247static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
1248{
1249 struct nexthop_group_cmd *nhgc;
1250
1251 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1252 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
1253 }
1254}
1255
1256static const struct cmd_variable_handler nhg_name_handlers[] = {
1257 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1258 {.completions = NULL}};
1259
31919191
DS
1260void nexthop_group_init(void (*new)(const char *name),
1261 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
1262 const struct nexthop *nhop),
1263 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
1264 const struct nexthop *nhop),
1265 void (*delete)(const char *name))
dba32923 1266{
31919191
DS
1267 RB_INIT(nhgc_entry_head, &nhgc_entries);
1268
868ee86c
DS
1269 cmd_variable_handler_register(nhg_name_handlers);
1270
612c2c15 1271 install_node(&nexthop_group_node);
dba32923 1272 install_element(CONFIG_NODE, &nexthop_group_cmd);
31919191
DS
1273 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
1274
1275 install_default(NH_GROUP_NODE);
0a8881b4
MS
1276 install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
1277 install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
31919191
DS
1278 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
1279
1280 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
1281
1282 if (new)
1283 nhg_hooks.new = new;
1284 if (add_nexthop)
1285 nhg_hooks.add_nexthop = add_nexthop;
1286 if (del_nexthop)
1287 nhg_hooks.del_nexthop = del_nexthop;
1288 if (delete)
1289 nhg_hooks.delete = delete;
dba32923 1290}