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