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