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