]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
fb018d25 DS |
2 | /* A generic nexthop structure |
3 | * Copyright (C) 2013 Cumulus Networks, Inc. | |
fb018d25 DS |
4 | */ |
5 | #include <zebra.h> | |
6 | ||
7 | #include "prefix.h" | |
8 | #include "table.h" | |
9 | #include "memory.h" | |
fb018d25 | 10 | #include "command.h" |
fb018d25 DS |
11 | #include "log.h" |
12 | #include "sockunion.h" | |
13 | #include "linklist.h" | |
fb018d25 DS |
14 | #include "prefix.h" |
15 | #include "nexthop.h" | |
40c7bdb0 | 16 | #include "mpls.h" |
d36d0d57 | 17 | #include "jhash.h" |
d52ec572 | 18 | #include "printfrr.h" |
f3323df2 | 19 | #include "vrf.h" |
77bf9504 | 20 | #include "nexthop_group.h" |
fb018d25 | 21 | |
bf8d3d6a DL |
22 | DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop"); |
23 | DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label"); | |
eab0f8f0 | 24 | DEFINE_MTYPE_STATIC(LIB, NH_SRV6, "Nexthop srv6"); |
4a1ab8e4 | 25 | |
a5a2d802 SW |
26 | static int _nexthop_labels_cmp(const struct nexthop *nh1, |
27 | const struct nexthop *nh2) | |
ebc403dd SW |
28 | { |
29 | const struct mpls_label_stack *nhl1 = NULL; | |
30 | const struct mpls_label_stack *nhl2 = NULL; | |
31 | ||
32 | nhl1 = nh1->nh_label; | |
33 | nhl2 = nh2->nh_label; | |
34 | ||
35 | /* No labels is a match */ | |
36 | if (!nhl1 && !nhl2) | |
37 | return 0; | |
38 | ||
39 | if (nhl1 && !nhl2) | |
40 | return 1; | |
41 | ||
42 | if (nhl2 && !nhl1) | |
43 | return -1; | |
44 | ||
45 | if (nhl1->num_labels > nhl2->num_labels) | |
46 | return 1; | |
47 | ||
48 | if (nhl1->num_labels < nhl2->num_labels) | |
49 | return -1; | |
50 | ||
26dddc01 MS |
51 | return memcmp(nhl1->label, nhl2->label, |
52 | (nhl1->num_labels * sizeof(mpls_label_t))); | |
ebc403dd SW |
53 | } |
54 | ||
eab0f8f0 HS |
55 | static int _nexthop_srv6_cmp(const struct nexthop *nh1, |
56 | const struct nexthop *nh2) | |
cb7775a9 | 57 | { |
eab0f8f0 | 58 | int ret = 0; |
cb7775a9 | 59 | |
eab0f8f0 | 60 | if (!nh1->nh_srv6 && !nh2->nh_srv6) |
cb7775a9 HS |
61 | return 0; |
62 | ||
eab0f8f0 | 63 | if (nh1->nh_srv6 && !nh2->nh_srv6) |
cb7775a9 HS |
64 | return 1; |
65 | ||
eab0f8f0 | 66 | if (!nh1->nh_srv6 && nh2->nh_srv6) |
cb7775a9 HS |
67 | return -1; |
68 | ||
eab0f8f0 | 69 | if (nh1->nh_srv6->seg6local_action > nh2->nh_srv6->seg6local_action) |
2aa01034 HS |
70 | return 1; |
71 | ||
eab0f8f0 | 72 | if (nh2->nh_srv6->seg6local_action < nh1->nh_srv6->seg6local_action) |
2aa01034 HS |
73 | return -1; |
74 | ||
eab0f8f0 HS |
75 | ret = memcmp(&nh1->nh_srv6->seg6local_ctx, |
76 | &nh2->nh_srv6->seg6local_ctx, | |
77 | sizeof(struct seg6local_context)); | |
78 | if (ret != 0) | |
79 | return ret; | |
80 | ||
81 | ret = memcmp(&nh1->nh_srv6->seg6_segs, | |
82 | &nh2->nh_srv6->seg6_segs, | |
83 | sizeof(struct in6_addr)); | |
84 | ||
85 | return ret; | |
2aa01034 HS |
86 | } |
87 | ||
3c6e0bd4 SW |
88 | int nexthop_g_addr_cmp(enum nexthop_types_t type, const union g_addr *addr1, |
89 | const union g_addr *addr2) | |
24cfec84 SW |
90 | { |
91 | int ret = 0; | |
92 | ||
93 | switch (type) { | |
94 | case NEXTHOP_TYPE_IPV4: | |
95 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
96 | ret = IPV4_ADDR_CMP(&addr1->ipv4, &addr2->ipv4); | |
97 | break; | |
98 | case NEXTHOP_TYPE_IPV6: | |
99 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
100 | ret = IPV6_ADDR_CMP(&addr1->ipv6, &addr2->ipv6); | |
101 | break; | |
102 | case NEXTHOP_TYPE_IFINDEX: | |
103 | case NEXTHOP_TYPE_BLACKHOLE: | |
104 | /* No addr here */ | |
105 | break; | |
106 | } | |
107 | ||
108 | return ret; | |
109 | } | |
110 | ||
a5a2d802 SW |
111 | static int _nexthop_gateway_cmp(const struct nexthop *nh1, |
112 | const struct nexthop *nh2) | |
24cfec84 | 113 | { |
3c6e0bd4 | 114 | return nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate); |
24cfec84 SW |
115 | } |
116 | ||
a5a2d802 SW |
117 | static int _nexthop_source_cmp(const struct nexthop *nh1, |
118 | const struct nexthop *nh2) | |
24cfec84 | 119 | { |
3c6e0bd4 | 120 | return nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src); |
24cfec84 SW |
121 | } |
122 | ||
a5a2d802 SW |
123 | static int _nexthop_cmp_no_labels(const struct nexthop *next1, |
124 | const struct nexthop *next2) | |
776c3e90 | 125 | { |
ff0e16da | 126 | int ret = 0; |
776c3e90 DS |
127 | |
128 | if (next1->vrf_id < next2->vrf_id) | |
129 | return -1; | |
130 | ||
131 | if (next1->vrf_id > next2->vrf_id) | |
132 | return 1; | |
133 | ||
134 | if (next1->type < next2->type) | |
135 | return -1; | |
136 | ||
137 | if (next1->type > next2->type) | |
138 | return 1; | |
139 | ||
bd054c1a DS |
140 | if (next1->weight < next2->weight) |
141 | return -1; | |
142 | ||
143 | if (next1->weight > next2->weight) | |
144 | return 1; | |
145 | ||
ff0e16da | 146 | switch (next1->type) { |
776c3e90 | 147 | case NEXTHOP_TYPE_IPV4: |
776c3e90 | 148 | case NEXTHOP_TYPE_IPV6: |
a5a2d802 SW |
149 | ret = _nexthop_gateway_cmp(next1, next2); |
150 | if (ret != 0) | |
776c3e90 DS |
151 | return ret; |
152 | break; | |
153 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
154 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
a5a2d802 SW |
155 | ret = _nexthop_gateway_cmp(next1, next2); |
156 | if (ret != 0) | |
776c3e90 DS |
157 | return ret; |
158 | /* Intentional Fall-Through */ | |
159 | case NEXTHOP_TYPE_IFINDEX: | |
160 | if (next1->ifindex < next2->ifindex) | |
161 | return -1; | |
162 | ||
163 | if (next1->ifindex > next2->ifindex) | |
164 | return 1; | |
165 | break; | |
166 | case NEXTHOP_TYPE_BLACKHOLE: | |
167 | if (next1->bh_type < next2->bh_type) | |
168 | return -1; | |
169 | ||
170 | if (next1->bh_type > next2->bh_type) | |
171 | return 1; | |
172 | break; | |
173 | } | |
174 | ||
31f937fb SM |
175 | if (next1->srte_color < next2->srte_color) |
176 | return -1; | |
177 | if (next1->srte_color > next2->srte_color) | |
178 | return 1; | |
179 | ||
a5a2d802 | 180 | ret = _nexthop_source_cmp(next1, next2); |
defd2ea4 MS |
181 | if (ret != 0) |
182 | goto done; | |
183 | ||
474aebd9 MS |
184 | if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && |
185 | !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) | |
186 | return 0; | |
187 | ||
defd2ea4 MS |
188 | if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && |
189 | CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) | |
190 | return -1; | |
191 | ||
192 | if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && | |
193 | !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) | |
194 | return 1; | |
a5a2d802 | 195 | |
474aebd9 MS |
196 | if (next1->backup_num == 0 && next2->backup_num == 0) |
197 | goto done; | |
198 | ||
199 | if (next1->backup_num < next2->backup_num) | |
defd2ea4 MS |
200 | return -1; |
201 | ||
474aebd9 | 202 | if (next1->backup_num > next2->backup_num) |
defd2ea4 MS |
203 | return 1; |
204 | ||
474aebd9 MS |
205 | ret = memcmp(next1->backup_idx, |
206 | next2->backup_idx, next1->backup_num); | |
207 | ||
defd2ea4 | 208 | done: |
a5a2d802 SW |
209 | return ret; |
210 | } | |
211 | ||
212 | int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2) | |
213 | { | |
214 | int ret = 0; | |
215 | ||
216 | ret = _nexthop_cmp_no_labels(next1, next2); | |
217 | if (ret != 0) | |
ff0e16da SW |
218 | return ret; |
219 | ||
a5a2d802 | 220 | ret = _nexthop_labels_cmp(next1, next2); |
cb7775a9 HS |
221 | if (ret != 0) |
222 | return ret; | |
223 | ||
eab0f8f0 | 224 | ret = _nexthop_srv6_cmp(next1, next2); |
a5a2d802 | 225 | |
776c3e90 DS |
226 | return ret; |
227 | } | |
228 | ||
338ec3b8 MS |
229 | /* |
230 | * More-limited comparison function used to detect duplicate | |
231 | * nexthops. This is used in places where we don't need the full | |
232 | * comparison of 'nexthop_cmp()'. | |
233 | */ | |
234 | int nexthop_cmp_basic(const struct nexthop *nh1, | |
235 | const struct nexthop *nh2) | |
236 | { | |
237 | int ret = 0; | |
238 | const struct mpls_label_stack *nhl1 = NULL; | |
239 | const struct mpls_label_stack *nhl2 = NULL; | |
240 | ||
241 | if (nh1 == NULL && nh2 == NULL) | |
242 | return 0; | |
243 | ||
244 | if (nh1 && !nh2) | |
245 | return 1; | |
246 | ||
247 | if (!nh1 && nh2) | |
248 | return -1; | |
249 | ||
250 | if (nh1->vrf_id < nh2->vrf_id) | |
251 | return -1; | |
252 | ||
253 | if (nh1->vrf_id > nh2->vrf_id) | |
254 | return 1; | |
255 | ||
256 | if (nh1->type < nh2->type) | |
257 | return -1; | |
258 | ||
259 | if (nh1->type > nh2->type) | |
260 | return 1; | |
261 | ||
262 | if (nh1->weight < nh2->weight) | |
263 | return -1; | |
264 | ||
265 | if (nh1->weight > nh2->weight) | |
266 | return 1; | |
267 | ||
268 | switch (nh1->type) { | |
269 | case NEXTHOP_TYPE_IPV4: | |
270 | case NEXTHOP_TYPE_IPV6: | |
271 | ret = nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate); | |
272 | if (ret != 0) | |
273 | return ret; | |
274 | break; | |
275 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
276 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
277 | ret = nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate); | |
278 | if (ret != 0) | |
279 | return ret; | |
280 | /* Intentional Fall-Through */ | |
281 | case NEXTHOP_TYPE_IFINDEX: | |
282 | if (nh1->ifindex < nh2->ifindex) | |
283 | return -1; | |
284 | ||
285 | if (nh1->ifindex > nh2->ifindex) | |
286 | return 1; | |
287 | break; | |
288 | case NEXTHOP_TYPE_BLACKHOLE: | |
289 | if (nh1->bh_type < nh2->bh_type) | |
290 | return -1; | |
291 | ||
292 | if (nh1->bh_type > nh2->bh_type) | |
293 | return 1; | |
294 | break; | |
295 | } | |
296 | ||
297 | /* Compare source addr */ | |
298 | ret = nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src); | |
299 | if (ret != 0) | |
300 | goto done; | |
301 | ||
302 | nhl1 = nh1->nh_label; | |
303 | nhl2 = nh2->nh_label; | |
304 | ||
305 | /* No labels is a match */ | |
306 | if (!nhl1 && !nhl2) | |
307 | return 0; | |
308 | ||
309 | if (nhl1 && !nhl2) | |
310 | return 1; | |
311 | ||
312 | if (nhl2 && !nhl1) | |
313 | return -1; | |
314 | ||
315 | if (nhl1->num_labels > nhl2->num_labels) | |
316 | return 1; | |
317 | ||
318 | if (nhl1->num_labels < nhl2->num_labels) | |
319 | return -1; | |
320 | ||
321 | ret = memcmp(nhl1->label, nhl2->label, | |
322 | (nhl1->num_labels * sizeof(mpls_label_t))); | |
323 | ||
324 | done: | |
325 | return ret; | |
326 | } | |
327 | ||
fb018d25 DS |
328 | /* |
329 | * nexthop_type_to_str | |
330 | */ | |
d62a17ae | 331 | const char *nexthop_type_to_str(enum nexthop_types_t nh_type) |
fb018d25 | 332 | { |
2b64873d | 333 | static const char *const desc[] = { |
d62a17ae | 334 | "none", "Directly connected", |
335 | "IPv4 nexthop", "IPv4 nexthop with ifindex", | |
336 | "IPv6 nexthop", "IPv6 nexthop with ifindex", | |
337 | "Null0 nexthop", | |
338 | }; | |
339 | ||
340 | return desc[nh_type]; | |
fb018d25 | 341 | } |
a399694f | 342 | |
a64448ba DS |
343 | /* |
344 | * Check if the labels match for the 2 nexthops specified. | |
345 | */ | |
89dc3160 | 346 | bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2) |
a64448ba | 347 | { |
a5a2d802 | 348 | if (_nexthop_labels_cmp(nh1, nh2) != 0) |
89dc3160 | 349 | return false; |
a64448ba | 350 | |
89dc3160 | 351 | return true; |
a64448ba DS |
352 | } |
353 | ||
d62a17ae | 354 | struct nexthop *nexthop_new(void) |
a399694f | 355 | { |
e28492ae SW |
356 | struct nexthop *nh; |
357 | ||
358 | nh = XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop)); | |
359 | ||
360 | /* | |
361 | * Default the weight to 1 here for all nexthops. | |
362 | * The linux kernel does some weird stuff with adding +1 to | |
363 | * all nexthop weights it gets over netlink. | |
364 | * To handle this, just default everything to 1 right from | |
defd2ea4 | 365 | * from the beginning so we don't have to special case |
e28492ae SW |
366 | * default weights in the linux netlink code. |
367 | * | |
368 | * 1 should be a valid on all platforms anyway. | |
369 | */ | |
370 | nh->weight = 1; | |
371 | ||
372 | return nh; | |
a399694f DS |
373 | } |
374 | ||
a399694f | 375 | /* Free nexthop. */ |
d62a17ae | 376 | void nexthop_free(struct nexthop *nexthop) |
a399694f | 377 | { |
d62a17ae | 378 | nexthop_del_labels(nexthop); |
eab0f8f0 HS |
379 | nexthop_del_srv6_seg6local(nexthop); |
380 | nexthop_del_srv6_seg6(nexthop); | |
d62a17ae | 381 | if (nexthop->resolved) |
382 | nexthops_free(nexthop->resolved); | |
383 | XFREE(MTYPE_NEXTHOP, nexthop); | |
a399694f DS |
384 | } |
385 | ||
386 | /* Frees a list of nexthops */ | |
d62a17ae | 387 | void nexthops_free(struct nexthop *nexthop) |
a399694f | 388 | { |
d62a17ae | 389 | struct nexthop *nh, *next; |
a399694f | 390 | |
d62a17ae | 391 | for (nh = nexthop; nh; nh = next) { |
392 | next = nh->next; | |
393 | nexthop_free(nh); | |
394 | } | |
a399694f | 395 | } |
80c2442a | 396 | |
31919191 DS |
397 | bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2) |
398 | { | |
399 | if (nh1 && !nh2) | |
400 | return false; | |
401 | ||
402 | if (!nh1 && nh2) | |
403 | return false; | |
404 | ||
405 | if (nh1 == nh2) | |
406 | return true; | |
407 | ||
2ed74b93 | 408 | if (nexthop_cmp(nh1, nh2) != 0) |
31919191 DS |
409 | return false; |
410 | ||
2ed74b93 | 411 | return true; |
31919191 DS |
412 | } |
413 | ||
a5a2d802 SW |
414 | bool nexthop_same_no_labels(const struct nexthop *nh1, |
415 | const struct nexthop *nh2) | |
416 | { | |
417 | if (nh1 && !nh2) | |
418 | return false; | |
419 | ||
420 | if (!nh1 && nh2) | |
421 | return false; | |
422 | ||
423 | if (nh1 == nh2) | |
424 | return true; | |
425 | ||
426 | if (_nexthop_cmp_no_labels(nh1, nh2) != 0) | |
427 | return false; | |
428 | ||
429 | return true; | |
430 | } | |
431 | ||
f3323df2 MS |
432 | /* |
433 | * Allocate a new nexthop object and initialize it from various args. | |
434 | */ | |
435 | struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id) | |
436 | { | |
437 | struct nexthop *nexthop; | |
438 | ||
439 | nexthop = nexthop_new(); | |
440 | nexthop->type = NEXTHOP_TYPE_IFINDEX; | |
441 | nexthop->ifindex = ifindex; | |
442 | nexthop->vrf_id = vrf_id; | |
443 | ||
444 | return nexthop; | |
445 | } | |
446 | ||
447 | struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4, | |
448 | const struct in_addr *src, | |
449 | vrf_id_t vrf_id) | |
450 | { | |
451 | struct nexthop *nexthop; | |
452 | ||
453 | nexthop = nexthop_new(); | |
454 | nexthop->type = NEXTHOP_TYPE_IPV4; | |
455 | nexthop->vrf_id = vrf_id; | |
456 | nexthop->gate.ipv4 = *ipv4; | |
457 | if (src) | |
458 | nexthop->src.ipv4 = *src; | |
459 | ||
460 | return nexthop; | |
461 | } | |
462 | ||
463 | struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4, | |
464 | const struct in_addr *src, | |
465 | ifindex_t ifindex, vrf_id_t vrf_id) | |
466 | { | |
467 | struct nexthop *nexthop; | |
468 | ||
469 | nexthop = nexthop_new(); | |
470 | nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX; | |
471 | nexthop->vrf_id = vrf_id; | |
472 | nexthop->gate.ipv4 = *ipv4; | |
473 | if (src) | |
474 | nexthop->src.ipv4 = *src; | |
475 | nexthop->ifindex = ifindex; | |
476 | ||
477 | return nexthop; | |
478 | } | |
479 | ||
480 | struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6, | |
481 | vrf_id_t vrf_id) | |
482 | { | |
483 | struct nexthop *nexthop; | |
484 | ||
485 | nexthop = nexthop_new(); | |
486 | nexthop->vrf_id = vrf_id; | |
487 | nexthop->type = NEXTHOP_TYPE_IPV6; | |
488 | nexthop->gate.ipv6 = *ipv6; | |
489 | ||
490 | return nexthop; | |
491 | } | |
492 | ||
493 | struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6, | |
494 | ifindex_t ifindex, vrf_id_t vrf_id) | |
495 | { | |
496 | struct nexthop *nexthop; | |
497 | ||
498 | nexthop = nexthop_new(); | |
499 | nexthop->vrf_id = vrf_id; | |
500 | nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX; | |
501 | nexthop->gate.ipv6 = *ipv6; | |
502 | nexthop->ifindex = ifindex; | |
503 | ||
504 | return nexthop; | |
505 | } | |
506 | ||
0789eb69 KM |
507 | struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type, |
508 | vrf_id_t nh_vrf_id) | |
f3323df2 MS |
509 | { |
510 | struct nexthop *nexthop; | |
511 | ||
512 | nexthop = nexthop_new(); | |
0789eb69 | 513 | nexthop->vrf_id = nh_vrf_id; |
f3323df2 MS |
514 | nexthop->type = NEXTHOP_TYPE_BLACKHOLE; |
515 | nexthop->bh_type = bh_type; | |
516 | ||
517 | return nexthop; | |
518 | } | |
519 | ||
40c7bdb0 | 520 | /* Update nexthop with label information. */ |
e4a1ec74 MS |
521 | void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t ltype, |
522 | uint8_t num_labels, const mpls_label_t *labels) | |
40c7bdb0 | 523 | { |
8ecdb26e | 524 | struct mpls_label_stack *nh_label; |
d62a17ae | 525 | int i; |
526 | ||
054af19a MS |
527 | if (num_labels == 0) |
528 | return; | |
529 | ||
e4a1ec74 MS |
530 | /* Enforce limit on label stack size */ |
531 | if (num_labels > MPLS_MAX_LABELS) | |
532 | num_labels = MPLS_MAX_LABELS; | |
533 | ||
534 | nexthop->nh_label_type = ltype; | |
535 | ||
d62a17ae | 536 | nh_label = XCALLOC(MTYPE_NH_LABEL, |
8ecdb26e | 537 | sizeof(struct mpls_label_stack) |
d62a17ae | 538 | + num_labels * sizeof(mpls_label_t)); |
539 | nh_label->num_labels = num_labels; | |
540 | for (i = 0; i < num_labels; i++) | |
e4a1ec74 | 541 | nh_label->label[i] = *(labels + i); |
d62a17ae | 542 | nexthop->nh_label = nh_label; |
40c7bdb0 | 543 | } |
544 | ||
545 | /* Free label information of nexthop, if present. */ | |
d62a17ae | 546 | void nexthop_del_labels(struct nexthop *nexthop) |
40c7bdb0 | 547 | { |
fe86c6b3 QY |
548 | XFREE(MTYPE_NH_LABEL, nexthop->nh_label); |
549 | nexthop->nh_label_type = ZEBRA_LSP_NONE; | |
40c7bdb0 | 550 | } |
551 | ||
eab0f8f0 HS |
552 | void nexthop_add_srv6_seg6local(struct nexthop *nexthop, uint32_t action, |
553 | const struct seg6local_context *ctx) | |
cb7775a9 | 554 | { |
cb7775a9 HS |
555 | if (action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) |
556 | return; | |
557 | ||
eab0f8f0 HS |
558 | if (!nexthop->nh_srv6) |
559 | nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6, | |
560 | sizeof(struct nexthop_srv6)); | |
561 | ||
562 | nexthop->nh_srv6->seg6local_action = action; | |
563 | nexthop->nh_srv6->seg6local_ctx = *ctx; | |
cb7775a9 HS |
564 | } |
565 | ||
eab0f8f0 | 566 | void nexthop_del_srv6_seg6local(struct nexthop *nexthop) |
cb7775a9 | 567 | { |
eab0f8f0 HS |
568 | if (!nexthop->nh_srv6) |
569 | return; | |
570 | ||
571 | nexthop->nh_srv6->seg6local_action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; | |
572 | ||
573 | if (sid_zero(&nexthop->nh_srv6->seg6_segs)) | |
574 | XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6); | |
cb7775a9 HS |
575 | } |
576 | ||
eab0f8f0 HS |
577 | void nexthop_add_srv6_seg6(struct nexthop *nexthop, |
578 | const struct in6_addr *segs) | |
2aa01034 | 579 | { |
eab0f8f0 HS |
580 | if (!segs) |
581 | return; | |
582 | ||
583 | if (!nexthop->nh_srv6) | |
584 | nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6, | |
585 | sizeof(struct nexthop_srv6)); | |
2aa01034 | 586 | |
eab0f8f0 | 587 | nexthop->nh_srv6->seg6_segs = *segs; |
2aa01034 HS |
588 | } |
589 | ||
eab0f8f0 | 590 | void nexthop_del_srv6_seg6(struct nexthop *nexthop) |
2aa01034 | 591 | { |
eab0f8f0 HS |
592 | if (!nexthop->nh_srv6) |
593 | return; | |
594 | ||
595 | memset(&nexthop->nh_srv6->seg6_segs, 0, | |
596 | sizeof(nexthop->nh_srv6->seg6_segs)); | |
597 | ||
598 | if (nexthop->nh_srv6->seg6local_action == | |
599 | ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) | |
600 | XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6); | |
2aa01034 HS |
601 | } |
602 | ||
d36d0d57 | 603 | const char *nexthop2str(const struct nexthop *nexthop, char *str, int size) |
80c2442a | 604 | { |
d62a17ae | 605 | switch (nexthop->type) { |
606 | case NEXTHOP_TYPE_IFINDEX: | |
607 | snprintf(str, size, "if %u", nexthop->ifindex); | |
608 | break; | |
609 | case NEXTHOP_TYPE_IPV4: | |
d62a17ae | 610 | case NEXTHOP_TYPE_IPV4_IFINDEX: |
af3b34f6 DA |
611 | snprintfrr(str, size, "%pI4 if %u", &nexthop->gate.ipv4, |
612 | nexthop->ifindex); | |
d62a17ae | 613 | break; |
614 | case NEXTHOP_TYPE_IPV6: | |
d62a17ae | 615 | case NEXTHOP_TYPE_IPV6_IFINDEX: |
af3b34f6 DA |
616 | snprintfrr(str, size, "%pI6 if %u", &nexthop->gate.ipv6, |
617 | nexthop->ifindex); | |
d62a17ae | 618 | break; |
619 | case NEXTHOP_TYPE_BLACKHOLE: | |
620 | snprintf(str, size, "blackhole"); | |
621 | break; | |
d62a17ae | 622 | } |
623 | ||
624 | return str; | |
80c2442a | 625 | } |
9fb47c05 CF |
626 | |
627 | /* | |
628 | * Iteration step for ALL_NEXTHOPS macro: | |
629 | * This is the tricky part. Check if `nexthop' has | |
630 | * NEXTHOP_FLAG_RECURSIVE set. If yes, this implies that `nexthop' has | |
631 | * at least one nexthop attached to `nexthop->resolved', which will be | |
632 | * the next one. | |
633 | * | |
634 | * If NEXTHOP_FLAG_RECURSIVE is not set, `nexthop' will progress in its | |
635 | * current chain. In case its current chain end is reached, it will move | |
636 | * upwards in the recursion levels and progress there. Whenever a step | |
637 | * forward in a chain is done, recursion will be checked again. | |
638 | * In a nustshell, it's equivalent to a pre-traversal order assuming that | |
639 | * left branch is 'resolved' and right branch is 'next': | |
640 | * https://en.wikipedia.org/wiki/Tree_traversal#/media/File:Sorted_binary_tree_preorder.svg | |
641 | */ | |
17c25e03 | 642 | struct nexthop *nexthop_next(const struct nexthop *nexthop) |
9fb47c05 | 643 | { |
d62a17ae | 644 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) |
645 | return nexthop->resolved; | |
9fb47c05 | 646 | |
d62a17ae | 647 | if (nexthop->next) |
648 | return nexthop->next; | |
9fb47c05 | 649 | |
d62a17ae | 650 | for (struct nexthop *par = nexthop->rparent; par; par = par->rparent) |
651 | if (par->next) | |
652 | return par->next; | |
9fb47c05 | 653 | |
d62a17ae | 654 | return NULL; |
9fb47c05 | 655 | } |
e3054ee9 | 656 | |
986a6617 | 657 | /* Return the next nexthop in the tree that is resolved and active */ |
17c25e03 | 658 | struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop) |
986a6617 SW |
659 | { |
660 | struct nexthop *next = nexthop_next(nexthop); | |
661 | ||
662 | while (next | |
663 | && (CHECK_FLAG(next->flags, NEXTHOP_FLAG_RECURSIVE) | |
664 | || !CHECK_FLAG(next->flags, NEXTHOP_FLAG_ACTIVE))) | |
665 | next = nexthop_next(next); | |
666 | ||
667 | return next; | |
668 | } | |
669 | ||
850c85b9 | 670 | unsigned int nexthop_level(const struct nexthop *nexthop) |
e3054ee9 | 671 | { |
d62a17ae | 672 | unsigned int rv = 0; |
e3054ee9 | 673 | |
850c85b9 MS |
674 | for (const struct nexthop *par = nexthop->rparent; |
675 | par; par = par->rparent) | |
d62a17ae | 676 | rv++; |
e3054ee9 | 677 | |
d62a17ae | 678 | return rv; |
e3054ee9 | 679 | } |
d36d0d57 | 680 | |
73a38187 SW |
681 | /* Only hash word-sized things, let cmp do the rest. */ |
682 | uint32_t nexthop_hash_quick(const struct nexthop *nexthop) | |
d36d0d57 | 683 | { |
1b1fe1c4 | 684 | uint32_t key = 0x45afe398; |
474aebd9 | 685 | int i; |
d36d0d57 | 686 | |
1b1fe1c4 SW |
687 | key = jhash_3words(nexthop->type, nexthop->vrf_id, |
688 | nexthop->nh_label_type, key); | |
a15e669c | 689 | |
1b1fe1c4 SW |
690 | if (nexthop->nh_label) { |
691 | int labels = nexthop->nh_label->num_labels; | |
474aebd9 MS |
692 | |
693 | i = 0; | |
1b1fe1c4 SW |
694 | |
695 | while (labels >= 3) { | |
696 | key = jhash_3words(nexthop->nh_label->label[i], | |
697 | nexthop->nh_label->label[i + 1], | |
698 | nexthop->nh_label->label[i + 2], | |
699 | key); | |
700 | labels -= 3; | |
701 | i += 3; | |
702 | } | |
703 | ||
704 | if (labels >= 2) { | |
705 | key = jhash_2words(nexthop->nh_label->label[i], | |
706 | nexthop->nh_label->label[i + 1], | |
707 | key); | |
708 | labels -= 2; | |
709 | i += 2; | |
710 | } | |
711 | ||
712 | if (labels >= 1) | |
713 | key = jhash_1word(nexthop->nh_label->label[i], key); | |
714 | } | |
715 | ||
474aebd9 MS |
716 | key = jhash_2words(nexthop->ifindex, |
717 | CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), | |
cba6a409 | 718 | key); |
a7df4ccf | 719 | |
474aebd9 MS |
720 | /* Include backup nexthops, if present */ |
721 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { | |
722 | int backups = nexthop->backup_num; | |
723 | ||
724 | i = 0; | |
725 | ||
726 | while (backups >= 3) { | |
727 | key = jhash_3words(nexthop->backup_idx[i], | |
728 | nexthop->backup_idx[i + 1], | |
729 | nexthop->backup_idx[i + 2], key); | |
730 | backups -= 3; | |
731 | i += 3; | |
732 | } | |
733 | ||
734 | while (backups >= 2) { | |
735 | key = jhash_2words(nexthop->backup_idx[i], | |
736 | nexthop->backup_idx[i + 1], key); | |
737 | backups -= 2; | |
738 | i += 2; | |
739 | } | |
740 | ||
741 | if (backups >= 1) | |
742 | key = jhash_1word(nexthop->backup_idx[i], key); | |
743 | } | |
744 | ||
eab0f8f0 HS |
745 | if (nexthop->nh_srv6) { |
746 | key = jhash_1word(nexthop->nh_srv6->seg6local_action, key); | |
747 | key = jhash(&nexthop->nh_srv6->seg6local_ctx, | |
748 | sizeof(nexthop->nh_srv6->seg6local_ctx), key); | |
749 | key = jhash(&nexthop->nh_srv6->seg6_segs, | |
750 | sizeof(nexthop->nh_srv6->seg6_segs), key); | |
cb7775a9 HS |
751 | } |
752 | ||
d36d0d57 QY |
753 | return key; |
754 | } | |
d52ec572 | 755 | |
73a38187 SW |
756 | |
757 | #define GATE_SIZE 4 /* Number of uint32_t words in struct g_addr */ | |
758 | ||
759 | /* For a more granular hash */ | |
760 | uint32_t nexthop_hash(const struct nexthop *nexthop) | |
761 | { | |
762 | uint32_t gate_src_rmap_raw[GATE_SIZE * 3] = {}; | |
763 | /* Get all the quick stuff */ | |
764 | uint32_t key = nexthop_hash_quick(nexthop); | |
765 | ||
766 | assert(((sizeof(nexthop->gate) + sizeof(nexthop->src) | |
767 | + sizeof(nexthop->rmap_src)) | |
768 | / 3) | |
769 | == (GATE_SIZE * sizeof(uint32_t))); | |
770 | ||
771 | memcpy(gate_src_rmap_raw, &nexthop->gate, GATE_SIZE); | |
772 | memcpy(gate_src_rmap_raw + GATE_SIZE, &nexthop->src, GATE_SIZE); | |
773 | memcpy(gate_src_rmap_raw + (2 * GATE_SIZE), &nexthop->rmap_src, | |
774 | GATE_SIZE); | |
775 | ||
776 | key = jhash2(gate_src_rmap_raw, (GATE_SIZE * 3), key); | |
777 | ||
778 | return key; | |
779 | } | |
780 | ||
77bf9504 SW |
781 | void nexthop_copy_no_recurse(struct nexthop *copy, |
782 | const struct nexthop *nexthop, | |
783 | struct nexthop *rparent) | |
e7addf02 SW |
784 | { |
785 | copy->vrf_id = nexthop->vrf_id; | |
786 | copy->ifindex = nexthop->ifindex; | |
787 | copy->type = nexthop->type; | |
788 | copy->flags = nexthop->flags; | |
df7fb580 | 789 | copy->weight = nexthop->weight; |
474aebd9 MS |
790 | |
791 | assert(nexthop->backup_num < NEXTHOP_MAX_BACKUPS); | |
792 | copy->backup_num = nexthop->backup_num; | |
793 | if (copy->backup_num > 0) | |
794 | memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num); | |
795 | ||
31f937fb | 796 | copy->srte_color = nexthop->srte_color; |
e7addf02 SW |
797 | memcpy(©->gate, &nexthop->gate, sizeof(nexthop->gate)); |
798 | memcpy(©->src, &nexthop->src, sizeof(nexthop->src)); | |
799 | memcpy(©->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src)); | |
800 | copy->rparent = rparent; | |
801 | if (nexthop->nh_label) | |
802 | nexthop_add_labels(copy, nexthop->nh_label_type, | |
803 | nexthop->nh_label->num_labels, | |
804 | &nexthop->nh_label->label[0]); | |
cb7775a9 | 805 | |
eab0f8f0 HS |
806 | if (nexthop->nh_srv6) { |
807 | if (nexthop->nh_srv6->seg6local_action != | |
808 | ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) | |
809 | nexthop_add_srv6_seg6local(copy, | |
810 | nexthop->nh_srv6->seg6local_action, | |
811 | &nexthop->nh_srv6->seg6local_ctx); | |
812 | if (!sid_zero(&nexthop->nh_srv6->seg6_segs)) | |
813 | nexthop_add_srv6_seg6(copy, | |
814 | &nexthop->nh_srv6->seg6_segs); | |
815 | } | |
e7addf02 SW |
816 | } |
817 | ||
77bf9504 SW |
818 | void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop, |
819 | struct nexthop *rparent) | |
820 | { | |
821 | nexthop_copy_no_recurse(copy, nexthop, rparent); | |
822 | ||
823 | /* Bit of a special case here, we need to handle the case | |
0cac0cf4 | 824 | * of a nexthop resolving to a group. Hence, we need to |
77bf9504 SW |
825 | * use a nexthop_group API. |
826 | */ | |
827 | if (CHECK_FLAG(copy->flags, NEXTHOP_FLAG_RECURSIVE)) | |
828 | copy_nexthops(©->resolved, nexthop->resolved, copy); | |
829 | } | |
830 | ||
831 | struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop, | |
832 | struct nexthop *rparent) | |
833 | { | |
834 | struct nexthop *new = nexthop_new(); | |
835 | ||
836 | nexthop_copy_no_recurse(new, nexthop, rparent); | |
837 | return new; | |
838 | } | |
839 | ||
504d0a40 SW |
840 | struct nexthop *nexthop_dup(const struct nexthop *nexthop, |
841 | struct nexthop *rparent) | |
842 | { | |
843 | struct nexthop *new = nexthop_new(); | |
844 | ||
845 | nexthop_copy(new, nexthop, rparent); | |
846 | return new; | |
847 | } | |
848 | ||
0cac0cf4 MS |
849 | /* |
850 | * Parse one or more backup index values, as comma-separated numbers, | |
851 | * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS | |
852 | * in size. Mails back the number of values converted, and returns 0 on | |
853 | * success, <0 if an error in parsing. | |
854 | */ | |
855 | int nexthop_str2backups(const char *str, int *num_backups, | |
856 | uint8_t *backups) | |
857 | { | |
858 | char *ostr; /* copy of string (start) */ | |
859 | char *lstr; /* working copy of string */ | |
860 | char *nump; /* pointer to next segment */ | |
861 | char *endp; /* end pointer */ | |
862 | int i, ret; | |
863 | uint8_t tmp[NEXTHOP_MAX_BACKUPS]; | |
864 | uint32_t lval; | |
865 | ||
866 | /* Copy incoming string; the parse is destructive */ | |
867 | lstr = ostr = XSTRDUP(MTYPE_TMP, str); | |
868 | *num_backups = 0; | |
869 | ret = 0; | |
870 | ||
871 | for (i = 0; i < NEXTHOP_MAX_BACKUPS && lstr; i++) { | |
872 | nump = strsep(&lstr, ","); | |
873 | lval = strtoul(nump, &endp, 10); | |
874 | ||
875 | /* Format check */ | |
876 | if (*endp != '\0') { | |
877 | ret = -1; | |
878 | break; | |
879 | } | |
880 | ||
881 | /* Empty value */ | |
882 | if (endp == nump) { | |
883 | ret = -1; | |
884 | break; | |
885 | } | |
886 | ||
887 | /* Limit to one octet */ | |
888 | if (lval > 255) { | |
889 | ret = -1; | |
890 | break; | |
891 | } | |
892 | ||
893 | tmp[i] = lval; | |
894 | } | |
895 | ||
896 | /* Excess values */ | |
897 | if (ret == 0 && i == NEXTHOP_MAX_BACKUPS && lstr) | |
898 | ret = -1; | |
899 | ||
900 | if (ret == 0) { | |
901 | *num_backups = i; | |
902 | memcpy(backups, tmp, i); | |
903 | } | |
904 | ||
905 | XFREE(MTYPE_TMP, ostr); | |
906 | ||
907 | return ret; | |
908 | } | |
909 | ||
f7a410a7 DS |
910 | ssize_t printfrr_nhs(struct fbuf *buf, const struct nexthop *nexthop) |
911 | { | |
912 | ssize_t ret = 0; | |
913 | ||
914 | if (!nexthop) | |
915 | return bputs(buf, "(null)"); | |
916 | ||
917 | switch (nexthop->type) { | |
918 | case NEXTHOP_TYPE_IFINDEX: | |
919 | ret += bprintfrr(buf, "if %u", nexthop->ifindex); | |
920 | break; | |
921 | case NEXTHOP_TYPE_IPV4: | |
922 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
923 | ret += bprintfrr(buf, "%pI4 if %u", &nexthop->gate.ipv4, | |
924 | nexthop->ifindex); | |
925 | break; | |
926 | case NEXTHOP_TYPE_IPV6: | |
927 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
928 | ret += bprintfrr(buf, "%pI6 if %u", &nexthop->gate.ipv6, | |
929 | nexthop->ifindex); | |
930 | break; | |
931 | case NEXTHOP_TYPE_BLACKHOLE: | |
932 | ret += bputs(buf, "blackhole"); | |
933 | break; | |
934 | } | |
935 | return ret; | |
936 | } | |
937 | ||
d52ec572 DL |
938 | /* |
939 | * nexthop printing variants: | |
940 | * %pNHvv | |
941 | * via 1.2.3.4 | |
942 | * via 1.2.3.4, eth0 | |
943 | * is directly connected, eth0 | |
944 | * unreachable (blackhole) | |
945 | * %pNHv | |
946 | * 1.2.3.4 | |
947 | * 1.2.3.4, via eth0 | |
948 | * directly connected, eth0 | |
949 | * unreachable (blackhole) | |
950 | * %pNHs | |
951 | * nexthop2str() | |
016cfe70 PZ |
952 | * %pNHcg |
953 | * 1.2.3.4 | |
954 | * (0-length if no IP address present) | |
955 | * %pNHci | |
956 | * eth0 | |
957 | * (0-length if no interface present) | |
d52ec572 | 958 | */ |
54929fd3 | 959 | printfrr_ext_autoreg_p("NH", printfrr_nh); |
3ea79430 DL |
960 | static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, |
961 | const void *ptr) | |
d52ec572 DL |
962 | { |
963 | const struct nexthop *nexthop = ptr; | |
d52ec572 | 964 | bool do_ifi = false; |
212e04e5 DL |
965 | const char *v_is = "", *v_via = "", *v_viaif = "via "; |
966 | ssize_t ret = 0; | |
d52ec572 | 967 | |
3ea79430 | 968 | switch (*ea->fmt) { |
d52ec572 | 969 | case 'v': |
3ea79430 DL |
970 | ea->fmt++; |
971 | if (*ea->fmt == 'v') { | |
d52ec572 DL |
972 | v_is = "is "; |
973 | v_via = "via "; | |
974 | v_viaif = ""; | |
3ea79430 | 975 | ea->fmt++; |
d52ec572 DL |
976 | } |
977 | ||
212e04e5 | 978 | if (!nexthop) |
eba599a3 | 979 | return bputs(buf, "(null)"); |
212e04e5 | 980 | |
d52ec572 DL |
981 | switch (nexthop->type) { |
982 | case NEXTHOP_TYPE_IPV4: | |
983 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
212e04e5 DL |
984 | ret += bprintfrr(buf, "%s%pI4", v_via, |
985 | &nexthop->gate.ipv4); | |
d52ec572 DL |
986 | do_ifi = true; |
987 | break; | |
988 | case NEXTHOP_TYPE_IPV6: | |
989 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
212e04e5 DL |
990 | ret += bprintfrr(buf, "%s%pI6", v_via, |
991 | &nexthop->gate.ipv6); | |
d52ec572 DL |
992 | do_ifi = true; |
993 | break; | |
994 | case NEXTHOP_TYPE_IFINDEX: | |
212e04e5 DL |
995 | ret += bprintfrr(buf, "%sdirectly connected, %s", v_is, |
996 | ifindex2ifname(nexthop->ifindex, | |
997 | nexthop->vrf_id)); | |
d52ec572 DL |
998 | break; |
999 | case NEXTHOP_TYPE_BLACKHOLE: | |
212e04e5 DL |
1000 | ret += bputs(buf, "unreachable"); |
1001 | ||
d52ec572 DL |
1002 | switch (nexthop->bh_type) { |
1003 | case BLACKHOLE_REJECT: | |
212e04e5 | 1004 | ret += bputs(buf, " (ICMP unreachable)"); |
d52ec572 DL |
1005 | break; |
1006 | case BLACKHOLE_ADMINPROHIB: | |
212e04e5 | 1007 | ret += bputs(buf, " (ICMP admin-prohibited)"); |
d52ec572 DL |
1008 | break; |
1009 | case BLACKHOLE_NULL: | |
212e04e5 | 1010 | ret += bputs(buf, " (blackhole)"); |
d52ec572 | 1011 | break; |
21ecdb1f | 1012 | case BLACKHOLE_UNSPEC: |
d52ec572 DL |
1013 | break; |
1014 | } | |
d52ec572 | 1015 | break; |
d52ec572 DL |
1016 | } |
1017 | if (do_ifi && nexthop->ifindex) | |
212e04e5 DL |
1018 | ret += bprintfrr(buf, ", %s%s", v_viaif, |
1019 | ifindex2ifname(nexthop->ifindex, | |
1020 | nexthop->vrf_id)); | |
d52ec572 | 1021 | |
d52ec572 DL |
1022 | return ret; |
1023 | case 's': | |
3ea79430 | 1024 | ea->fmt++; |
212e04e5 | 1025 | |
f7a410a7 | 1026 | ret += printfrr_nhs(buf, nexthop); |
212e04e5 | 1027 | return ret; |
016cfe70 PZ |
1028 | case 'c': |
1029 | ea->fmt++; | |
1030 | if (*ea->fmt == 'g') { | |
1031 | ea->fmt++; | |
1032 | if (!nexthop) | |
1033 | return bputs(buf, "(null)"); | |
1034 | switch (nexthop->type) { | |
1035 | case NEXTHOP_TYPE_IPV4: | |
1036 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
1037 | ret += bprintfrr(buf, "%pI4", | |
1038 | &nexthop->gate.ipv4); | |
1039 | break; | |
1040 | case NEXTHOP_TYPE_IPV6: | |
1041 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1042 | ret += bprintfrr(buf, "%pI6", | |
1043 | &nexthop->gate.ipv6); | |
1044 | break; | |
1045 | case NEXTHOP_TYPE_IFINDEX: | |
1046 | case NEXTHOP_TYPE_BLACKHOLE: | |
1047 | break; | |
1048 | } | |
1049 | } else if (*ea->fmt == 'i') { | |
1050 | ea->fmt++; | |
1051 | if (!nexthop) | |
1052 | return bputs(buf, "(null)"); | |
1053 | switch (nexthop->type) { | |
1054 | case NEXTHOP_TYPE_IFINDEX: | |
1055 | ret += bprintfrr( | |
1056 | buf, "%s", | |
1057 | ifindex2ifname(nexthop->ifindex, | |
1058 | nexthop->vrf_id)); | |
1059 | break; | |
1060 | case NEXTHOP_TYPE_IPV4: | |
1061 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
1062 | case NEXTHOP_TYPE_IPV6: | |
1063 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1064 | if (nexthop->ifindex) | |
1065 | ret += bprintfrr( | |
1066 | buf, "%s", | |
1067 | ifindex2ifname( | |
1068 | nexthop->ifindex, | |
1069 | nexthop->vrf_id)); | |
1070 | break; | |
1071 | case NEXTHOP_TYPE_BLACKHOLE: | |
1072 | break; | |
1073 | } | |
1074 | } | |
1075 | return ret; | |
d52ec572 | 1076 | } |
212e04e5 | 1077 | return -1; |
d52ec572 | 1078 | } |
5bb87732 AR |
1079 | |
1080 | bool nexthop_is_ifindex_type(const struct nexthop *nh) | |
1081 | { | |
1082 | if (nh->type == NEXTHOP_TYPE_IFINDEX || | |
1083 | nh->type == NEXTHOP_TYPE_IPV4_IFINDEX || | |
1084 | nh->type == NEXTHOP_TYPE_IPV6_IFINDEX) | |
1085 | return true; | |
1086 | return false; | |
1087 | } |