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