]> git.proxmox.com Git - mirror_frr.git/blame - lib/nexthop.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / lib / nexthop.c
CommitLineData
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
22DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop");
23DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label");
eab0f8f0 24DEFINE_MTYPE_STATIC(LIB, NH_SRV6, "Nexthop srv6");
4a1ab8e4 25
a5a2d802
SW
26static 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
55static 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
88int 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
111static 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
117static 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
123static 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 208done:
a5a2d802
SW
209 return ret;
210}
211
212int 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 */
234int 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
324done:
325 return ret;
326}
327
fb018d25
DS
328/*
329 * nexthop_type_to_str
330 */
d62a17ae 331const 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 346bool 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 354struct 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 376void 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 387void 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
397bool 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
414bool 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 */
435struct 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
447struct 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
463struct 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
480struct 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
493struct 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
507struct 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
521void 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 546void 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
552void 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 566void 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
577void 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 590void 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 603const 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 642struct 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 658struct 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 670unsigned 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. */
682uint32_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 */
760uint32_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
781void 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(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
798 memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
799 memcpy(&copy->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
818void 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(&copy->resolved, nexthop->resolved, copy);
829}
830
831struct 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
840struct 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 */
855int 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
910ssize_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 959printfrr_ext_autoreg_p("NH", printfrr_nh);
3ea79430
DL
960static 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
1080bool 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}