]> git.proxmox.com Git - mirror_frr.git/blob - lib/nexthop.c
*: auto-convert to SPDX License IDs
[mirror_frr.git] / lib / nexthop.c
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 {
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
51 return memcmp(nhl1->label, nhl2->label,
52 (nhl1->num_labels * sizeof(mpls_label_t)));
53 }
54
55 static int _nexthop_srv6_cmp(const struct nexthop *nh1,
56 const struct nexthop *nh2)
57 {
58 int ret = 0;
59
60 if (!nh1->nh_srv6 && !nh2->nh_srv6)
61 return 0;
62
63 if (nh1->nh_srv6 && !nh2->nh_srv6)
64 return 1;
65
66 if (!nh1->nh_srv6 && nh2->nh_srv6)
67 return -1;
68
69 if (nh1->nh_srv6->seg6local_action > nh2->nh_srv6->seg6local_action)
70 return 1;
71
72 if (nh2->nh_srv6->seg6local_action < nh1->nh_srv6->seg6local_action)
73 return -1;
74
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;
86 }
87
88 int nexthop_g_addr_cmp(enum nexthop_types_t type, const union g_addr *addr1,
89 const union g_addr *addr2)
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
111 static int _nexthop_gateway_cmp(const struct nexthop *nh1,
112 const struct nexthop *nh2)
113 {
114 return nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);
115 }
116
117 static int _nexthop_source_cmp(const struct nexthop *nh1,
118 const struct nexthop *nh2)
119 {
120 return nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src);
121 }
122
123 static int _nexthop_cmp_no_labels(const struct nexthop *next1,
124 const struct nexthop *next2)
125 {
126 int ret = 0;
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
140 if (next1->weight < next2->weight)
141 return -1;
142
143 if (next1->weight > next2->weight)
144 return 1;
145
146 switch (next1->type) {
147 case NEXTHOP_TYPE_IPV4:
148 case NEXTHOP_TYPE_IPV6:
149 ret = _nexthop_gateway_cmp(next1, next2);
150 if (ret != 0)
151 return ret;
152 break;
153 case NEXTHOP_TYPE_IPV4_IFINDEX:
154 case NEXTHOP_TYPE_IPV6_IFINDEX:
155 ret = _nexthop_gateway_cmp(next1, next2);
156 if (ret != 0)
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
175 if (next1->srte_color < next2->srte_color)
176 return -1;
177 if (next1->srte_color > next2->srte_color)
178 return 1;
179
180 ret = _nexthop_source_cmp(next1, next2);
181 if (ret != 0)
182 goto done;
183
184 if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
185 !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
186 return 0;
187
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;
195
196 if (next1->backup_num == 0 && next2->backup_num == 0)
197 goto done;
198
199 if (next1->backup_num < next2->backup_num)
200 return -1;
201
202 if (next1->backup_num > next2->backup_num)
203 return 1;
204
205 ret = memcmp(next1->backup_idx,
206 next2->backup_idx, next1->backup_num);
207
208 done:
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)
218 return ret;
219
220 ret = _nexthop_labels_cmp(next1, next2);
221 if (ret != 0)
222 return ret;
223
224 ret = _nexthop_srv6_cmp(next1, next2);
225
226 return ret;
227 }
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 {
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
328 /*
329 * nexthop_type_to_str
330 */
331 const char *nexthop_type_to_str(enum nexthop_types_t nh_type)
332 {
333 static const char *const desc[] = {
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];
341 }
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 {
348 if (_nexthop_labels_cmp(nh1, nh2) != 0)
349 return false;
350
351 return true;
352 }
353
354 struct nexthop *nexthop_new(void)
355 {
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
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 nh->weight = 1;
371
372 return nh;
373 }
374
375 /* Free nexthop. */
376 void nexthop_free(struct nexthop *nexthop)
377 {
378 nexthop_del_labels(nexthop);
379 nexthop_del_srv6_seg6local(nexthop);
380 nexthop_del_srv6_seg6(nexthop);
381 if (nexthop->resolved)
382 nexthops_free(nexthop->resolved);
383 XFREE(MTYPE_NEXTHOP, nexthop);
384 }
385
386 /* Frees a list of nexthops */
387 void nexthops_free(struct nexthop *nexthop)
388 {
389 struct nexthop *nh, *next;
390
391 for (nh = nexthop; nh; nh = next) {
392 next = nh->next;
393 nexthop_free(nh);
394 }
395 }
396
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
408 if (nexthop_cmp(nh1, nh2) != 0)
409 return false;
410
411 return true;
412 }
413
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
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
507 struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type,
508 vrf_id_t nh_vrf_id)
509 {
510 struct nexthop *nexthop;
511
512 nexthop = nexthop_new();
513 nexthop->vrf_id = nh_vrf_id;
514 nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
515 nexthop->bh_type = bh_type;
516
517 return nexthop;
518 }
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 {
524 struct mpls_label_stack *nh_label;
525 int i;
526
527 if (num_labels == 0)
528 return;
529
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
536 nh_label = XCALLOC(MTYPE_NH_LABEL,
537 sizeof(struct mpls_label_stack)
538 + num_labels * sizeof(mpls_label_t));
539 nh_label->num_labels = num_labels;
540 for (i = 0; i < num_labels; i++)
541 nh_label->label[i] = *(labels + i);
542 nexthop->nh_label = nh_label;
543 }
544
545 /* Free label information of nexthop, if present. */
546 void nexthop_del_labels(struct nexthop *nexthop)
547 {
548 XFREE(MTYPE_NH_LABEL, nexthop->nh_label);
549 nexthop->nh_label_type = ZEBRA_LSP_NONE;
550 }
551
552 void nexthop_add_srv6_seg6local(struct nexthop *nexthop, uint32_t action,
553 const struct seg6local_context *ctx)
554 {
555 if (action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
556 return;
557
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;
564 }
565
566 void nexthop_del_srv6_seg6local(struct nexthop *nexthop)
567 {
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);
575 }
576
577 void nexthop_add_srv6_seg6(struct nexthop *nexthop,
578 const struct in6_addr *segs)
579 {
580 if (!segs)
581 return;
582
583 if (!nexthop->nh_srv6)
584 nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6,
585 sizeof(struct nexthop_srv6));
586
587 nexthop->nh_srv6->seg6_segs = *segs;
588 }
589
590 void nexthop_del_srv6_seg6(struct nexthop *nexthop)
591 {
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);
601 }
602
603 const char *nexthop2str(const struct nexthop *nexthop, char *str, int size)
604 {
605 switch (nexthop->type) {
606 case NEXTHOP_TYPE_IFINDEX:
607 snprintf(str, size, "if %u", nexthop->ifindex);
608 break;
609 case NEXTHOP_TYPE_IPV4:
610 case NEXTHOP_TYPE_IPV4_IFINDEX:
611 snprintfrr(str, size, "%pI4 if %u", &nexthop->gate.ipv4,
612 nexthop->ifindex);
613 break;
614 case NEXTHOP_TYPE_IPV6:
615 case NEXTHOP_TYPE_IPV6_IFINDEX:
616 snprintfrr(str, size, "%pI6 if %u", &nexthop->gate.ipv6,
617 nexthop->ifindex);
618 break;
619 case NEXTHOP_TYPE_BLACKHOLE:
620 snprintf(str, size, "blackhole");
621 break;
622 }
623
624 return str;
625 }
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 {
644 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
645 return nexthop->resolved;
646
647 if (nexthop->next)
648 return nexthop->next;
649
650 for (struct nexthop *par = nexthop->rparent; par; par = par->rparent)
651 if (par->next)
652 return par->next;
653
654 return NULL;
655 }
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 {
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
670 unsigned int nexthop_level(const struct nexthop *nexthop)
671 {
672 unsigned int rv = 0;
673
674 for (const struct nexthop *par = nexthop->rparent;
675 par; par = par->rparent)
676 rv++;
677
678 return rv;
679 }
680
681 /* Only hash word-sized things, let cmp do the rest. */
682 uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
683 {
684 uint32_t key = 0x45afe398;
685 int i;
686
687 key = jhash_3words(nexthop->type, nexthop->vrf_id,
688 nexthop->nh_label_type, key);
689
690 if (nexthop->nh_label) {
691 int labels = nexthop->nh_label->num_labels;
692
693 i = 0;
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
716 key = jhash_2words(nexthop->ifindex,
717 CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK),
718 key);
719
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
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);
751 }
752
753 return key;
754 }
755
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
781 void nexthop_copy_no_recurse(struct nexthop *copy,
782 const struct nexthop *nexthop,
783 struct nexthop *rparent)
784 {
785 copy->vrf_id = nexthop->vrf_id;
786 copy->ifindex = nexthop->ifindex;
787 copy->type = nexthop->type;
788 copy->flags = nexthop->flags;
789 copy->weight = nexthop->weight;
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
796 copy->srte_color = nexthop->srte_color;
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]);
805
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 }
816 }
817
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
824 * of a nexthop resolving to a group. Hence, we need to
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
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
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
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
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
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()
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)
958 */
959 printfrr_ext_autoreg_p("NH", printfrr_nh);
960 static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea,
961 const void *ptr)
962 {
963 const struct nexthop *nexthop = ptr;
964 bool do_ifi = false;
965 const char *v_is = "", *v_via = "", *v_viaif = "via ";
966 ssize_t ret = 0;
967
968 switch (*ea->fmt) {
969 case 'v':
970 ea->fmt++;
971 if (*ea->fmt == 'v') {
972 v_is = "is ";
973 v_via = "via ";
974 v_viaif = "";
975 ea->fmt++;
976 }
977
978 if (!nexthop)
979 return bputs(buf, "(null)");
980
981 switch (nexthop->type) {
982 case NEXTHOP_TYPE_IPV4:
983 case NEXTHOP_TYPE_IPV4_IFINDEX:
984 ret += bprintfrr(buf, "%s%pI4", v_via,
985 &nexthop->gate.ipv4);
986 do_ifi = true;
987 break;
988 case NEXTHOP_TYPE_IPV6:
989 case NEXTHOP_TYPE_IPV6_IFINDEX:
990 ret += bprintfrr(buf, "%s%pI6", v_via,
991 &nexthop->gate.ipv6);
992 do_ifi = true;
993 break;
994 case NEXTHOP_TYPE_IFINDEX:
995 ret += bprintfrr(buf, "%sdirectly connected, %s", v_is,
996 ifindex2ifname(nexthop->ifindex,
997 nexthop->vrf_id));
998 break;
999 case NEXTHOP_TYPE_BLACKHOLE:
1000 ret += bputs(buf, "unreachable");
1001
1002 switch (nexthop->bh_type) {
1003 case BLACKHOLE_REJECT:
1004 ret += bputs(buf, " (ICMP unreachable)");
1005 break;
1006 case BLACKHOLE_ADMINPROHIB:
1007 ret += bputs(buf, " (ICMP admin-prohibited)");
1008 break;
1009 case BLACKHOLE_NULL:
1010 ret += bputs(buf, " (blackhole)");
1011 break;
1012 case BLACKHOLE_UNSPEC:
1013 break;
1014 }
1015 break;
1016 }
1017 if (do_ifi && nexthop->ifindex)
1018 ret += bprintfrr(buf, ", %s%s", v_viaif,
1019 ifindex2ifname(nexthop->ifindex,
1020 nexthop->vrf_id));
1021
1022 return ret;
1023 case 's':
1024 ea->fmt++;
1025
1026 ret += printfrr_nhs(buf, nexthop);
1027 return ret;
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;
1076 }
1077 return -1;
1078 }