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