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