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