]> git.proxmox.com Git - mirror_frr.git/blame - lib/nexthop.c
Merge pull request #5895 from patrasar/2404618
[mirror_frr.git] / lib / nexthop.c
CommitLineData
fb018d25
DS
1/* A generic nexthop structure
2 * Copyright (C) 2013 Cumulus Networks, Inc.
3 *
a399694f 4 * This file is part of Quagga.
fb018d25 5 *
a399694f 6 * Quagga is free software; you can redistribute it and/or modify it
fb018d25
DS
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
a399694f 11 * Quagga is distributed in the hope that it will be useful, but
fb018d25
DS
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
896014f4
DL
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
fb018d25
DS
19 */
20#include <zebra.h>
21
22#include "prefix.h"
23#include "table.h"
24#include "memory.h"
fb018d25 25#include "command.h"
fb018d25
DS
26#include "log.h"
27#include "sockunion.h"
28#include "linklist.h"
fb018d25
DS
29#include "prefix.h"
30#include "nexthop.h"
40c7bdb0 31#include "mpls.h"
d36d0d57 32#include "jhash.h"
d52ec572 33#include "printfrr.h"
f3323df2 34#include "vrf.h"
77bf9504 35#include "nexthop_group.h"
fb018d25 36
d62a17ae 37DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop")
38DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label")
4a1ab8e4 39
a5a2d802
SW
40static int _nexthop_labels_cmp(const struct nexthop *nh1,
41 const struct nexthop *nh2)
ebc403dd
SW
42{
43 const struct mpls_label_stack *nhl1 = NULL;
44 const struct mpls_label_stack *nhl2 = NULL;
45
46 nhl1 = nh1->nh_label;
47 nhl2 = nh2->nh_label;
48
49 /* No labels is a match */
50 if (!nhl1 && !nhl2)
51 return 0;
52
53 if (nhl1 && !nhl2)
54 return 1;
55
56 if (nhl2 && !nhl1)
57 return -1;
58
59 if (nhl1->num_labels > nhl2->num_labels)
60 return 1;
61
62 if (nhl1->num_labels < nhl2->num_labels)
63 return -1;
64
65 return memcmp(nhl1->label, nhl2->label, nhl1->num_labels);
66}
67
3c6e0bd4
SW
68int nexthop_g_addr_cmp(enum nexthop_types_t type, const union g_addr *addr1,
69 const union g_addr *addr2)
24cfec84
SW
70{
71 int ret = 0;
72
73 switch (type) {
74 case NEXTHOP_TYPE_IPV4:
75 case NEXTHOP_TYPE_IPV4_IFINDEX:
76 ret = IPV4_ADDR_CMP(&addr1->ipv4, &addr2->ipv4);
77 break;
78 case NEXTHOP_TYPE_IPV6:
79 case NEXTHOP_TYPE_IPV6_IFINDEX:
80 ret = IPV6_ADDR_CMP(&addr1->ipv6, &addr2->ipv6);
81 break;
82 case NEXTHOP_TYPE_IFINDEX:
83 case NEXTHOP_TYPE_BLACKHOLE:
84 /* No addr here */
85 break;
86 }
87
88 return ret;
89}
90
a5a2d802
SW
91static int _nexthop_gateway_cmp(const struct nexthop *nh1,
92 const struct nexthop *nh2)
24cfec84 93{
3c6e0bd4 94 return nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);
24cfec84
SW
95}
96
a5a2d802
SW
97static int _nexthop_source_cmp(const struct nexthop *nh1,
98 const struct nexthop *nh2)
24cfec84 99{
3c6e0bd4 100 return nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src);
24cfec84
SW
101}
102
a5a2d802
SW
103static int _nexthop_cmp_no_labels(const struct nexthop *next1,
104 const struct nexthop *next2)
776c3e90 105{
ff0e16da 106 int ret = 0;
776c3e90
DS
107
108 if (next1->vrf_id < next2->vrf_id)
109 return -1;
110
111 if (next1->vrf_id > next2->vrf_id)
112 return 1;
113
114 if (next1->type < next2->type)
115 return -1;
116
117 if (next1->type > next2->type)
118 return 1;
119
bd054c1a
DS
120 if (next1->weight < next2->weight)
121 return -1;
122
123 if (next1->weight > next2->weight)
124 return 1;
125
ff0e16da 126 switch (next1->type) {
776c3e90 127 case NEXTHOP_TYPE_IPV4:
776c3e90 128 case NEXTHOP_TYPE_IPV6:
a5a2d802
SW
129 ret = _nexthop_gateway_cmp(next1, next2);
130 if (ret != 0)
776c3e90
DS
131 return ret;
132 break;
133 case NEXTHOP_TYPE_IPV4_IFINDEX:
134 case NEXTHOP_TYPE_IPV6_IFINDEX:
a5a2d802
SW
135 ret = _nexthop_gateway_cmp(next1, next2);
136 if (ret != 0)
776c3e90
DS
137 return ret;
138 /* Intentional Fall-Through */
139 case NEXTHOP_TYPE_IFINDEX:
140 if (next1->ifindex < next2->ifindex)
141 return -1;
142
143 if (next1->ifindex > next2->ifindex)
144 return 1;
145 break;
146 case NEXTHOP_TYPE_BLACKHOLE:
147 if (next1->bh_type < next2->bh_type)
148 return -1;
149
150 if (next1->bh_type > next2->bh_type)
151 return 1;
152 break;
153 }
154
31f937fb
SM
155 if (next1->srte_color < next2->srte_color)
156 return -1;
157 if (next1->srte_color > next2->srte_color)
158 return 1;
159
a5a2d802 160 ret = _nexthop_source_cmp(next1, next2);
defd2ea4
MS
161 if (ret != 0)
162 goto done;
163
474aebd9
MS
164 if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
165 !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
166 return 0;
167
defd2ea4
MS
168 if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
169 CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
170 return -1;
171
172 if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
173 !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
174 return 1;
a5a2d802 175
474aebd9
MS
176 if (next1->backup_num == 0 && next2->backup_num == 0)
177 goto done;
178
179 if (next1->backup_num < next2->backup_num)
defd2ea4
MS
180 return -1;
181
474aebd9 182 if (next1->backup_num > next2->backup_num)
defd2ea4
MS
183 return 1;
184
474aebd9
MS
185 ret = memcmp(next1->backup_idx,
186 next2->backup_idx, next1->backup_num);
187
defd2ea4 188done:
a5a2d802
SW
189 return ret;
190}
191
192int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2)
193{
194 int ret = 0;
195
196 ret = _nexthop_cmp_no_labels(next1, next2);
197 if (ret != 0)
ff0e16da
SW
198 return ret;
199
a5a2d802
SW
200 ret = _nexthop_labels_cmp(next1, next2);
201
776c3e90
DS
202 return ret;
203}
204
fb018d25
DS
205/*
206 * nexthop_type_to_str
207 */
d62a17ae 208const char *nexthop_type_to_str(enum nexthop_types_t nh_type)
fb018d25 209{
2b64873d 210 static const char *const desc[] = {
d62a17ae 211 "none", "Directly connected",
212 "IPv4 nexthop", "IPv4 nexthop with ifindex",
213 "IPv6 nexthop", "IPv6 nexthop with ifindex",
214 "Null0 nexthop",
215 };
216
217 return desc[nh_type];
fb018d25 218}
a399694f 219
a64448ba
DS
220/*
221 * Check if the labels match for the 2 nexthops specified.
222 */
89dc3160 223bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2)
a64448ba 224{
a5a2d802 225 if (_nexthop_labels_cmp(nh1, nh2) != 0)
89dc3160 226 return false;
a64448ba 227
89dc3160 228 return true;
a64448ba
DS
229}
230
d62a17ae 231struct nexthop *nexthop_new(void)
a399694f 232{
e28492ae
SW
233 struct nexthop *nh;
234
235 nh = XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop));
236
237 /*
238 * Default the weight to 1 here for all nexthops.
239 * The linux kernel does some weird stuff with adding +1 to
240 * all nexthop weights it gets over netlink.
241 * To handle this, just default everything to 1 right from
defd2ea4 242 * from the beginning so we don't have to special case
e28492ae
SW
243 * default weights in the linux netlink code.
244 *
245 * 1 should be a valid on all platforms anyway.
246 */
247 nh->weight = 1;
248
249 return nh;
a399694f
DS
250}
251
a399694f 252/* Free nexthop. */
d62a17ae 253void nexthop_free(struct nexthop *nexthop)
a399694f 254{
d62a17ae 255 nexthop_del_labels(nexthop);
256 if (nexthop->resolved)
257 nexthops_free(nexthop->resolved);
258 XFREE(MTYPE_NEXTHOP, nexthop);
a399694f
DS
259}
260
261/* Frees a list of nexthops */
d62a17ae 262void nexthops_free(struct nexthop *nexthop)
a399694f 263{
d62a17ae 264 struct nexthop *nh, *next;
a399694f 265
d62a17ae 266 for (nh = nexthop; nh; nh = next) {
267 next = nh->next;
268 nexthop_free(nh);
269 }
a399694f 270}
80c2442a 271
31919191
DS
272bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2)
273{
274 if (nh1 && !nh2)
275 return false;
276
277 if (!nh1 && nh2)
278 return false;
279
280 if (nh1 == nh2)
281 return true;
282
2ed74b93 283 if (nexthop_cmp(nh1, nh2) != 0)
31919191
DS
284 return false;
285
2ed74b93 286 return true;
31919191
DS
287}
288
a5a2d802
SW
289bool nexthop_same_no_labels(const struct nexthop *nh1,
290 const struct nexthop *nh2)
291{
292 if (nh1 && !nh2)
293 return false;
294
295 if (!nh1 && nh2)
296 return false;
297
298 if (nh1 == nh2)
299 return true;
300
301 if (_nexthop_cmp_no_labels(nh1, nh2) != 0)
302 return false;
303
304 return true;
305}
306
f3323df2
MS
307/*
308 * Allocate a new nexthop object and initialize it from various args.
309 */
310struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
311{
312 struct nexthop *nexthop;
313
314 nexthop = nexthop_new();
315 nexthop->type = NEXTHOP_TYPE_IFINDEX;
316 nexthop->ifindex = ifindex;
317 nexthop->vrf_id = vrf_id;
318
319 return nexthop;
320}
321
322struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4,
323 const struct in_addr *src,
324 vrf_id_t vrf_id)
325{
326 struct nexthop *nexthop;
327
328 nexthop = nexthop_new();
329 nexthop->type = NEXTHOP_TYPE_IPV4;
330 nexthop->vrf_id = vrf_id;
331 nexthop->gate.ipv4 = *ipv4;
332 if (src)
333 nexthop->src.ipv4 = *src;
334
335 return nexthop;
336}
337
338struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4,
339 const struct in_addr *src,
340 ifindex_t ifindex, vrf_id_t vrf_id)
341{
342 struct nexthop *nexthop;
343
344 nexthop = nexthop_new();
345 nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
346 nexthop->vrf_id = vrf_id;
347 nexthop->gate.ipv4 = *ipv4;
348 if (src)
349 nexthop->src.ipv4 = *src;
350 nexthop->ifindex = ifindex;
351
352 return nexthop;
353}
354
355struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6,
356 vrf_id_t vrf_id)
357{
358 struct nexthop *nexthop;
359
360 nexthop = nexthop_new();
361 nexthop->vrf_id = vrf_id;
362 nexthop->type = NEXTHOP_TYPE_IPV6;
363 nexthop->gate.ipv6 = *ipv6;
364
365 return nexthop;
366}
367
368struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6,
369 ifindex_t ifindex, vrf_id_t vrf_id)
370{
371 struct nexthop *nexthop;
372
373 nexthop = nexthop_new();
374 nexthop->vrf_id = vrf_id;
375 nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
376 nexthop->gate.ipv6 = *ipv6;
377 nexthop->ifindex = ifindex;
378
379 return nexthop;
380}
381
382struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type)
383{
384 struct nexthop *nexthop;
385
386 nexthop = nexthop_new();
387 nexthop->vrf_id = VRF_DEFAULT;
388 nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
389 nexthop->bh_type = bh_type;
390
391 return nexthop;
392}
393
40c7bdb0 394/* Update nexthop with label information. */
e4a1ec74
MS
395void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t ltype,
396 uint8_t num_labels, const mpls_label_t *labels)
40c7bdb0 397{
8ecdb26e 398 struct mpls_label_stack *nh_label;
d62a17ae 399 int i;
400
054af19a
MS
401 if (num_labels == 0)
402 return;
403
e4a1ec74
MS
404 /* Enforce limit on label stack size */
405 if (num_labels > MPLS_MAX_LABELS)
406 num_labels = MPLS_MAX_LABELS;
407
408 nexthop->nh_label_type = ltype;
409
d62a17ae 410 nh_label = XCALLOC(MTYPE_NH_LABEL,
8ecdb26e 411 sizeof(struct mpls_label_stack)
d62a17ae 412 + num_labels * sizeof(mpls_label_t));
413 nh_label->num_labels = num_labels;
414 for (i = 0; i < num_labels; i++)
e4a1ec74 415 nh_label->label[i] = *(labels + i);
d62a17ae 416 nexthop->nh_label = nh_label;
40c7bdb0 417}
418
419/* Free label information of nexthop, if present. */
d62a17ae 420void nexthop_del_labels(struct nexthop *nexthop)
40c7bdb0 421{
fe86c6b3
QY
422 XFREE(MTYPE_NH_LABEL, nexthop->nh_label);
423 nexthop->nh_label_type = ZEBRA_LSP_NONE;
40c7bdb0 424}
425
d36d0d57 426const char *nexthop2str(const struct nexthop *nexthop, char *str, int size)
80c2442a 427{
d62a17ae 428 switch (nexthop->type) {
429 case NEXTHOP_TYPE_IFINDEX:
430 snprintf(str, size, "if %u", nexthop->ifindex);
431 break;
432 case NEXTHOP_TYPE_IPV4:
d62a17ae 433 case NEXTHOP_TYPE_IPV4_IFINDEX:
434 snprintf(str, size, "%s if %u", inet_ntoa(nexthop->gate.ipv4),
435 nexthop->ifindex);
436 break;
437 case NEXTHOP_TYPE_IPV6:
d62a17ae 438 case NEXTHOP_TYPE_IPV6_IFINDEX:
439 snprintf(str, size, "%s if %u", inet6_ntoa(nexthop->gate.ipv6),
440 nexthop->ifindex);
441 break;
442 case NEXTHOP_TYPE_BLACKHOLE:
443 snprintf(str, size, "blackhole");
444 break;
445 default:
446 snprintf(str, size, "unknown");
447 break;
448 }
449
450 return str;
80c2442a 451}
9fb47c05
CF
452
453/*
454 * Iteration step for ALL_NEXTHOPS macro:
455 * This is the tricky part. Check if `nexthop' has
456 * NEXTHOP_FLAG_RECURSIVE set. If yes, this implies that `nexthop' has
457 * at least one nexthop attached to `nexthop->resolved', which will be
458 * the next one.
459 *
460 * If NEXTHOP_FLAG_RECURSIVE is not set, `nexthop' will progress in its
461 * current chain. In case its current chain end is reached, it will move
462 * upwards in the recursion levels and progress there. Whenever a step
463 * forward in a chain is done, recursion will be checked again.
464 * In a nustshell, it's equivalent to a pre-traversal order assuming that
465 * left branch is 'resolved' and right branch is 'next':
466 * https://en.wikipedia.org/wiki/Tree_traversal#/media/File:Sorted_binary_tree_preorder.svg
467 */
17c25e03 468struct nexthop *nexthop_next(const struct nexthop *nexthop)
9fb47c05 469{
d62a17ae 470 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
471 return nexthop->resolved;
9fb47c05 472
d62a17ae 473 if (nexthop->next)
474 return nexthop->next;
9fb47c05 475
d62a17ae 476 for (struct nexthop *par = nexthop->rparent; par; par = par->rparent)
477 if (par->next)
478 return par->next;
9fb47c05 479
d62a17ae 480 return NULL;
9fb47c05 481}
e3054ee9 482
986a6617 483/* Return the next nexthop in the tree that is resolved and active */
17c25e03 484struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop)
986a6617
SW
485{
486 struct nexthop *next = nexthop_next(nexthop);
487
488 while (next
489 && (CHECK_FLAG(next->flags, NEXTHOP_FLAG_RECURSIVE)
490 || !CHECK_FLAG(next->flags, NEXTHOP_FLAG_ACTIVE)))
491 next = nexthop_next(next);
492
493 return next;
494}
495
850c85b9 496unsigned int nexthop_level(const struct nexthop *nexthop)
e3054ee9 497{
d62a17ae 498 unsigned int rv = 0;
e3054ee9 499
850c85b9
MS
500 for (const struct nexthop *par = nexthop->rparent;
501 par; par = par->rparent)
d62a17ae 502 rv++;
e3054ee9 503
d62a17ae 504 return rv;
e3054ee9 505}
d36d0d57 506
73a38187
SW
507/* Only hash word-sized things, let cmp do the rest. */
508uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
d36d0d57 509{
1b1fe1c4 510 uint32_t key = 0x45afe398;
474aebd9 511 int i;
d36d0d57 512
1b1fe1c4
SW
513 key = jhash_3words(nexthop->type, nexthop->vrf_id,
514 nexthop->nh_label_type, key);
a15e669c 515
1b1fe1c4
SW
516 if (nexthop->nh_label) {
517 int labels = nexthop->nh_label->num_labels;
474aebd9
MS
518
519 i = 0;
1b1fe1c4
SW
520
521 while (labels >= 3) {
522 key = jhash_3words(nexthop->nh_label->label[i],
523 nexthop->nh_label->label[i + 1],
524 nexthop->nh_label->label[i + 2],
525 key);
526 labels -= 3;
527 i += 3;
528 }
529
530 if (labels >= 2) {
531 key = jhash_2words(nexthop->nh_label->label[i],
532 nexthop->nh_label->label[i + 1],
533 key);
534 labels -= 2;
535 i += 2;
536 }
537
538 if (labels >= 1)
539 key = jhash_1word(nexthop->nh_label->label[i], key);
540 }
541
474aebd9
MS
542 key = jhash_2words(nexthop->ifindex,
543 CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK),
cba6a409 544 key);
a7df4ccf 545
474aebd9
MS
546 /* Include backup nexthops, if present */
547 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
548 int backups = nexthop->backup_num;
549
550 i = 0;
551
552 while (backups >= 3) {
553 key = jhash_3words(nexthop->backup_idx[i],
554 nexthop->backup_idx[i + 1],
555 nexthop->backup_idx[i + 2], key);
556 backups -= 3;
557 i += 3;
558 }
559
560 while (backups >= 2) {
561 key = jhash_2words(nexthop->backup_idx[i],
562 nexthop->backup_idx[i + 1], key);
563 backups -= 2;
564 i += 2;
565 }
566
567 if (backups >= 1)
568 key = jhash_1word(nexthop->backup_idx[i], key);
569 }
570
d36d0d57
QY
571 return key;
572}
d52ec572 573
73a38187
SW
574
575#define GATE_SIZE 4 /* Number of uint32_t words in struct g_addr */
576
577/* For a more granular hash */
578uint32_t nexthop_hash(const struct nexthop *nexthop)
579{
580 uint32_t gate_src_rmap_raw[GATE_SIZE * 3] = {};
581 /* Get all the quick stuff */
582 uint32_t key = nexthop_hash_quick(nexthop);
583
584 assert(((sizeof(nexthop->gate) + sizeof(nexthop->src)
585 + sizeof(nexthop->rmap_src))
586 / 3)
587 == (GATE_SIZE * sizeof(uint32_t)));
588
589 memcpy(gate_src_rmap_raw, &nexthop->gate, GATE_SIZE);
590 memcpy(gate_src_rmap_raw + GATE_SIZE, &nexthop->src, GATE_SIZE);
591 memcpy(gate_src_rmap_raw + (2 * GATE_SIZE), &nexthop->rmap_src,
592 GATE_SIZE);
593
594 key = jhash2(gate_src_rmap_raw, (GATE_SIZE * 3), key);
595
596 return key;
597}
598
77bf9504
SW
599void nexthop_copy_no_recurse(struct nexthop *copy,
600 const struct nexthop *nexthop,
601 struct nexthop *rparent)
e7addf02
SW
602{
603 copy->vrf_id = nexthop->vrf_id;
604 copy->ifindex = nexthop->ifindex;
605 copy->type = nexthop->type;
606 copy->flags = nexthop->flags;
df7fb580 607 copy->weight = nexthop->weight;
474aebd9
MS
608
609 assert(nexthop->backup_num < NEXTHOP_MAX_BACKUPS);
610 copy->backup_num = nexthop->backup_num;
611 if (copy->backup_num > 0)
612 memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num);
613
31f937fb 614 copy->srte_color = nexthop->srte_color;
e7addf02
SW
615 memcpy(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
616 memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
617 memcpy(&copy->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
618 copy->rparent = rparent;
619 if (nexthop->nh_label)
620 nexthop_add_labels(copy, nexthop->nh_label_type,
621 nexthop->nh_label->num_labels,
622 &nexthop->nh_label->label[0]);
623}
624
77bf9504
SW
625void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
626 struct nexthop *rparent)
627{
628 nexthop_copy_no_recurse(copy, nexthop, rparent);
629
630 /* Bit of a special case here, we need to handle the case
0cac0cf4 631 * of a nexthop resolving to a group. Hence, we need to
77bf9504
SW
632 * use a nexthop_group API.
633 */
634 if (CHECK_FLAG(copy->flags, NEXTHOP_FLAG_RECURSIVE))
635 copy_nexthops(&copy->resolved, nexthop->resolved, copy);
636}
637
638struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop,
639 struct nexthop *rparent)
640{
641 struct nexthop *new = nexthop_new();
642
643 nexthop_copy_no_recurse(new, nexthop, rparent);
644 return new;
645}
646
504d0a40
SW
647struct nexthop *nexthop_dup(const struct nexthop *nexthop,
648 struct nexthop *rparent)
649{
650 struct nexthop *new = nexthop_new();
651
652 nexthop_copy(new, nexthop, rparent);
653 return new;
654}
655
0cac0cf4
MS
656/*
657 * Parse one or more backup index values, as comma-separated numbers,
658 * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS
659 * in size. Mails back the number of values converted, and returns 0 on
660 * success, <0 if an error in parsing.
661 */
662int nexthop_str2backups(const char *str, int *num_backups,
663 uint8_t *backups)
664{
665 char *ostr; /* copy of string (start) */
666 char *lstr; /* working copy of string */
667 char *nump; /* pointer to next segment */
668 char *endp; /* end pointer */
669 int i, ret;
670 uint8_t tmp[NEXTHOP_MAX_BACKUPS];
671 uint32_t lval;
672
673 /* Copy incoming string; the parse is destructive */
674 lstr = ostr = XSTRDUP(MTYPE_TMP, str);
675 *num_backups = 0;
676 ret = 0;
677
678 for (i = 0; i < NEXTHOP_MAX_BACKUPS && lstr; i++) {
679 nump = strsep(&lstr, ",");
680 lval = strtoul(nump, &endp, 10);
681
682 /* Format check */
683 if (*endp != '\0') {
684 ret = -1;
685 break;
686 }
687
688 /* Empty value */
689 if (endp == nump) {
690 ret = -1;
691 break;
692 }
693
694 /* Limit to one octet */
695 if (lval > 255) {
696 ret = -1;
697 break;
698 }
699
700 tmp[i] = lval;
701 }
702
703 /* Excess values */
704 if (ret == 0 && i == NEXTHOP_MAX_BACKUPS && lstr)
705 ret = -1;
706
707 if (ret == 0) {
708 *num_backups = i;
709 memcpy(backups, tmp, i);
710 }
711
712 XFREE(MTYPE_TMP, ostr);
713
714 return ret;
715}
716
d52ec572
DL
717/*
718 * nexthop printing variants:
719 * %pNHvv
720 * via 1.2.3.4
721 * via 1.2.3.4, eth0
722 * is directly connected, eth0
723 * unreachable (blackhole)
724 * %pNHv
725 * 1.2.3.4
726 * 1.2.3.4, via eth0
727 * directly connected, eth0
728 * unreachable (blackhole)
729 * %pNHs
730 * nexthop2str()
731 */
732printfrr_ext_autoreg_p("NH", printfrr_nh)
733static ssize_t printfrr_nh(char *buf, size_t bsz, const char *fmt,
734 int prec, const void *ptr)
735{
736 const struct nexthop *nexthop = ptr;
737 struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
738 bool do_ifi = false;
739 const char *s, *v_is = "", *v_via = "", *v_viaif = "via ";
740 ssize_t ret = 3;
741
742 switch (fmt[2]) {
743 case 'v':
744 if (fmt[3] == 'v') {
745 v_is = "is ";
746 v_via = "via ";
747 v_viaif = "";
748 ret++;
749 }
750
751 switch (nexthop->type) {
752 case NEXTHOP_TYPE_IPV4:
753 case NEXTHOP_TYPE_IPV4_IFINDEX:
754 bprintfrr(&fb, "%s%pI4", v_via, &nexthop->gate.ipv4);
755 do_ifi = true;
756 break;
757 case NEXTHOP_TYPE_IPV6:
758 case NEXTHOP_TYPE_IPV6_IFINDEX:
759 bprintfrr(&fb, "%s%pI6", v_via, &nexthop->gate.ipv6);
760 do_ifi = true;
761 break;
762 case NEXTHOP_TYPE_IFINDEX:
763 bprintfrr(&fb, "%sdirectly connected, %s", v_is,
764 ifindex2ifname(nexthop->ifindex,
765 nexthop->vrf_id));
766 break;
767 case NEXTHOP_TYPE_BLACKHOLE:
768 switch (nexthop->bh_type) {
769 case BLACKHOLE_REJECT:
770 s = " (ICMP unreachable)";
771 break;
772 case BLACKHOLE_ADMINPROHIB:
773 s = " (ICMP admin-prohibited)";
774 break;
775 case BLACKHOLE_NULL:
776 s = " (blackhole)";
777 break;
778 default:
779 s = "";
780 break;
781 }
782 bprintfrr(&fb, "unreachable%s", s);
783 break;
784 default:
785 break;
786 }
787 if (do_ifi && nexthop->ifindex)
788 bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname(
789 nexthop->ifindex,
790 nexthop->vrf_id));
791
792 *fb.pos = '\0';
793 return ret;
794 case 's':
795 nexthop2str(nexthop, buf, bsz);
796 return 3;
797 }
798 return 0;
799}