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