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