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