]> git.proxmox.com Git - mirror_frr.git/blob - lib/nexthop.c
Merge pull request #5427 from liam-mcb/igmp-join-any
[mirror_frr.git] / lib / nexthop.c
1 /* A generic nexthop structure
2 * Copyright (C) 2013 Cumulus Networks, Inc.
3 *
4 * This file is part of Quagga.
5 *
6 * Quagga is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * Quagga is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 #include <zebra.h>
21
22 #include "prefix.h"
23 #include "table.h"
24 #include "memory.h"
25 #include "command.h"
26 #include "if.h"
27 #include "log.h"
28 #include "sockunion.h"
29 #include "linklist.h"
30 #include "thread.h"
31 #include "prefix.h"
32 #include "nexthop.h"
33 #include "mpls.h"
34 #include "jhash.h"
35 #include "printfrr.h"
36 #include "vrf.h"
37
38 DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop")
39 DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label")
40
41 static int _nexthop_labels_cmp(const struct nexthop *nh1,
42 const struct nexthop *nh2)
43 {
44 const struct mpls_label_stack *nhl1 = NULL;
45 const struct mpls_label_stack *nhl2 = NULL;
46
47 nhl1 = nh1->nh_label;
48 nhl2 = nh2->nh_label;
49
50 /* No labels is a match */
51 if (!nhl1 && !nhl2)
52 return 0;
53
54 if (nhl1 && !nhl2)
55 return 1;
56
57 if (nhl2 && !nhl1)
58 return -1;
59
60 if (nhl1->num_labels > nhl2->num_labels)
61 return 1;
62
63 if (nhl1->num_labels < nhl2->num_labels)
64 return -1;
65
66 return memcmp(nhl1->label, nhl2->label, nhl1->num_labels);
67 }
68
69 int nexthop_g_addr_cmp(enum nexthop_types_t type, const union g_addr *addr1,
70 const union g_addr *addr2)
71 {
72 int ret = 0;
73
74 switch (type) {
75 case NEXTHOP_TYPE_IPV4:
76 case NEXTHOP_TYPE_IPV4_IFINDEX:
77 ret = IPV4_ADDR_CMP(&addr1->ipv4, &addr2->ipv4);
78 break;
79 case NEXTHOP_TYPE_IPV6:
80 case NEXTHOP_TYPE_IPV6_IFINDEX:
81 ret = IPV6_ADDR_CMP(&addr1->ipv6, &addr2->ipv6);
82 break;
83 case NEXTHOP_TYPE_IFINDEX:
84 case NEXTHOP_TYPE_BLACKHOLE:
85 /* No addr here */
86 break;
87 }
88
89 return ret;
90 }
91
92 static int _nexthop_gateway_cmp(const struct nexthop *nh1,
93 const struct nexthop *nh2)
94 {
95 return nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);
96 }
97
98 static int _nexthop_source_cmp(const struct nexthop *nh1,
99 const struct nexthop *nh2)
100 {
101 return nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src);
102 }
103
104 static int _nexthop_cmp_no_labels(const struct nexthop *next1,
105 const struct nexthop *next2)
106 {
107 int ret = 0;
108
109 if (next1->vrf_id < next2->vrf_id)
110 return -1;
111
112 if (next1->vrf_id > next2->vrf_id)
113 return 1;
114
115 if (next1->type < next2->type)
116 return -1;
117
118 if (next1->type > next2->type)
119 return 1;
120
121 if (next1->weight < next2->weight)
122 return -1;
123
124 if (next1->weight > next2->weight)
125 return 1;
126
127 switch (next1->type) {
128 case NEXTHOP_TYPE_IPV4:
129 case NEXTHOP_TYPE_IPV6:
130 ret = _nexthop_gateway_cmp(next1, next2);
131 if (ret != 0)
132 return ret;
133 break;
134 case NEXTHOP_TYPE_IPV4_IFINDEX:
135 case NEXTHOP_TYPE_IPV6_IFINDEX:
136 ret = _nexthop_gateway_cmp(next1, next2);
137 if (ret != 0)
138 return ret;
139 /* Intentional Fall-Through */
140 case NEXTHOP_TYPE_IFINDEX:
141 if (next1->ifindex < next2->ifindex)
142 return -1;
143
144 if (next1->ifindex > next2->ifindex)
145 return 1;
146 break;
147 case NEXTHOP_TYPE_BLACKHOLE:
148 if (next1->bh_type < next2->bh_type)
149 return -1;
150
151 if (next1->bh_type > next2->bh_type)
152 return 1;
153 break;
154 }
155
156 ret = _nexthop_source_cmp(next1, next2);
157
158 return ret;
159 }
160
161 int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2)
162 {
163 int ret = 0;
164
165 ret = _nexthop_cmp_no_labels(next1, next2);
166 if (ret != 0)
167 return ret;
168
169 ret = _nexthop_labels_cmp(next1, next2);
170
171 return ret;
172 }
173
174 int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2)
175 {
176 int type1 = NEXTHOP_FIRSTHOPTYPE(next1->type);
177 int type2 = NEXTHOP_FIRSTHOPTYPE(next2->type);
178
179 if (type1 != type2)
180 return 0;
181 switch (type1) {
182 case NEXTHOP_TYPE_IPV4_IFINDEX:
183 if (!IPV4_ADDR_SAME(&next1->gate.ipv4, &next2->gate.ipv4))
184 return 0;
185 if (next1->ifindex != next2->ifindex)
186 return 0;
187 break;
188 case NEXTHOP_TYPE_IFINDEX:
189 if (next1->ifindex != next2->ifindex)
190 return 0;
191 break;
192 case NEXTHOP_TYPE_IPV6_IFINDEX:
193 if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6))
194 return 0;
195 if (next1->ifindex != next2->ifindex)
196 return 0;
197 break;
198 default:
199 /* do nothing */
200 break;
201 }
202 return 1;
203 }
204
205 /*
206 * nexthop_type_to_str
207 */
208 const char *nexthop_type_to_str(enum nexthop_types_t nh_type)
209 {
210 static const char *const desc[] = {
211 "none", "Directly connected",
212 "IPv4 nexthop", "IPv4 nexthop with ifindex",
213 "IPv6 nexthop", "IPv6 nexthop with ifindex",
214 "Null0 nexthop",
215 };
216
217 return desc[nh_type];
218 }
219
220 /*
221 * Check if the labels match for the 2 nexthops specified.
222 */
223 bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2)
224 {
225 if (_nexthop_labels_cmp(nh1, nh2) != 0)
226 return false;
227
228 return true;
229 }
230
231 struct nexthop *nexthop_new(void)
232 {
233 return XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop));
234 }
235
236 /* Free nexthop. */
237 void nexthop_free(struct nexthop *nexthop)
238 {
239 nexthop_del_labels(nexthop);
240 if (nexthop->resolved)
241 nexthops_free(nexthop->resolved);
242 XFREE(MTYPE_NEXTHOP, nexthop);
243 }
244
245 /* Frees a list of nexthops */
246 void nexthops_free(struct nexthop *nexthop)
247 {
248 struct nexthop *nh, *next;
249
250 for (nh = nexthop; nh; nh = next) {
251 next = nh->next;
252 nexthop_free(nh);
253 }
254 }
255
256 bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2)
257 {
258 if (nh1 && !nh2)
259 return false;
260
261 if (!nh1 && nh2)
262 return false;
263
264 if (nh1 == nh2)
265 return true;
266
267 if (nexthop_cmp(nh1, nh2) != 0)
268 return false;
269
270 return true;
271 }
272
273 bool nexthop_same_no_labels(const struct nexthop *nh1,
274 const struct nexthop *nh2)
275 {
276 if (nh1 && !nh2)
277 return false;
278
279 if (!nh1 && nh2)
280 return false;
281
282 if (nh1 == nh2)
283 return true;
284
285 if (_nexthop_cmp_no_labels(nh1, nh2) != 0)
286 return false;
287
288 return true;
289 }
290
291 /*
292 * Allocate a new nexthop object and initialize it from various args.
293 */
294 struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
295 {
296 struct nexthop *nexthop;
297
298 nexthop = nexthop_new();
299 nexthop->type = NEXTHOP_TYPE_IFINDEX;
300 nexthop->ifindex = ifindex;
301 nexthop->vrf_id = vrf_id;
302
303 return nexthop;
304 }
305
306 struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4,
307 const struct in_addr *src,
308 vrf_id_t vrf_id)
309 {
310 struct nexthop *nexthop;
311
312 nexthop = nexthop_new();
313 nexthop->type = NEXTHOP_TYPE_IPV4;
314 nexthop->vrf_id = vrf_id;
315 nexthop->gate.ipv4 = *ipv4;
316 if (src)
317 nexthop->src.ipv4 = *src;
318
319 return nexthop;
320 }
321
322 struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4,
323 const struct in_addr *src,
324 ifindex_t ifindex, vrf_id_t vrf_id)
325 {
326 struct nexthop *nexthop;
327
328 nexthop = nexthop_new();
329 nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
330 nexthop->vrf_id = vrf_id;
331 nexthop->gate.ipv4 = *ipv4;
332 if (src)
333 nexthop->src.ipv4 = *src;
334 nexthop->ifindex = ifindex;
335
336 return nexthop;
337 }
338
339 struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6,
340 vrf_id_t vrf_id)
341 {
342 struct nexthop *nexthop;
343
344 nexthop = nexthop_new();
345 nexthop->vrf_id = vrf_id;
346 nexthop->type = NEXTHOP_TYPE_IPV6;
347 nexthop->gate.ipv6 = *ipv6;
348
349 return nexthop;
350 }
351
352 struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6,
353 ifindex_t ifindex, vrf_id_t vrf_id)
354 {
355 struct nexthop *nexthop;
356
357 nexthop = nexthop_new();
358 nexthop->vrf_id = vrf_id;
359 nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
360 nexthop->gate.ipv6 = *ipv6;
361 nexthop->ifindex = ifindex;
362
363 return nexthop;
364 }
365
366 struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type)
367 {
368 struct nexthop *nexthop;
369
370 nexthop = nexthop_new();
371 nexthop->vrf_id = VRF_DEFAULT;
372 nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
373 nexthop->bh_type = bh_type;
374
375 return nexthop;
376 }
377
378 /* Update nexthop with label information. */
379 void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type,
380 uint8_t num_labels, mpls_label_t *label)
381 {
382 struct mpls_label_stack *nh_label;
383 int i;
384
385 if (num_labels == 0)
386 return;
387
388 nexthop->nh_label_type = type;
389 nh_label = XCALLOC(MTYPE_NH_LABEL,
390 sizeof(struct mpls_label_stack)
391 + num_labels * sizeof(mpls_label_t));
392 nh_label->num_labels = num_labels;
393 for (i = 0; i < num_labels; i++)
394 nh_label->label[i] = *(label + i);
395 nexthop->nh_label = nh_label;
396 }
397
398 /* Free label information of nexthop, if present. */
399 void nexthop_del_labels(struct nexthop *nexthop)
400 {
401 if (nexthop->nh_label) {
402 XFREE(MTYPE_NH_LABEL, nexthop->nh_label);
403 nexthop->nh_label_type = ZEBRA_LSP_NONE;
404 }
405 }
406
407 const char *nexthop2str(const struct nexthop *nexthop, char *str, int size)
408 {
409 switch (nexthop->type) {
410 case NEXTHOP_TYPE_IFINDEX:
411 snprintf(str, size, "if %u", nexthop->ifindex);
412 break;
413 case NEXTHOP_TYPE_IPV4:
414 case NEXTHOP_TYPE_IPV4_IFINDEX:
415 snprintf(str, size, "%s if %u", inet_ntoa(nexthop->gate.ipv4),
416 nexthop->ifindex);
417 break;
418 case NEXTHOP_TYPE_IPV6:
419 case NEXTHOP_TYPE_IPV6_IFINDEX:
420 snprintf(str, size, "%s if %u", inet6_ntoa(nexthop->gate.ipv6),
421 nexthop->ifindex);
422 break;
423 case NEXTHOP_TYPE_BLACKHOLE:
424 snprintf(str, size, "blackhole");
425 break;
426 default:
427 snprintf(str, size, "unknown");
428 break;
429 }
430
431 return str;
432 }
433
434 /*
435 * Iteration step for ALL_NEXTHOPS macro:
436 * This is the tricky part. Check if `nexthop' has
437 * NEXTHOP_FLAG_RECURSIVE set. If yes, this implies that `nexthop' has
438 * at least one nexthop attached to `nexthop->resolved', which will be
439 * the next one.
440 *
441 * If NEXTHOP_FLAG_RECURSIVE is not set, `nexthop' will progress in its
442 * current chain. In case its current chain end is reached, it will move
443 * upwards in the recursion levels and progress there. Whenever a step
444 * forward in a chain is done, recursion will be checked again.
445 * In a nustshell, it's equivalent to a pre-traversal order assuming that
446 * left branch is 'resolved' and right branch is 'next':
447 * https://en.wikipedia.org/wiki/Tree_traversal#/media/File:Sorted_binary_tree_preorder.svg
448 */
449 struct nexthop *nexthop_next(const struct nexthop *nexthop)
450 {
451 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
452 return nexthop->resolved;
453
454 if (nexthop->next)
455 return nexthop->next;
456
457 for (struct nexthop *par = nexthop->rparent; par; par = par->rparent)
458 if (par->next)
459 return par->next;
460
461 return NULL;
462 }
463
464 /* Return the next nexthop in the tree that is resolved and active */
465 struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop)
466 {
467 struct nexthop *next = nexthop_next(nexthop);
468
469 while (next
470 && (CHECK_FLAG(next->flags, NEXTHOP_FLAG_RECURSIVE)
471 || !CHECK_FLAG(next->flags, NEXTHOP_FLAG_ACTIVE)))
472 next = nexthop_next(next);
473
474 return next;
475 }
476
477 unsigned int nexthop_level(struct nexthop *nexthop)
478 {
479 unsigned int rv = 0;
480
481 for (struct nexthop *par = nexthop->rparent; par; par = par->rparent)
482 rv++;
483
484 return rv;
485 }
486
487 /* Only hash word-sized things, let cmp do the rest. */
488 uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
489 {
490 uint32_t key = 0x45afe398;
491
492 key = jhash_3words(nexthop->type, nexthop->vrf_id,
493 nexthop->nh_label_type, key);
494
495 if (nexthop->nh_label) {
496 int labels = nexthop->nh_label->num_labels;
497 int i = 0;
498
499 while (labels >= 3) {
500 key = jhash_3words(nexthop->nh_label->label[i],
501 nexthop->nh_label->label[i + 1],
502 nexthop->nh_label->label[i + 2],
503 key);
504 labels -= 3;
505 i += 3;
506 }
507
508 if (labels >= 2) {
509 key = jhash_2words(nexthop->nh_label->label[i],
510 nexthop->nh_label->label[i + 1],
511 key);
512 labels -= 2;
513 i += 2;
514 }
515
516 if (labels >= 1)
517 key = jhash_1word(nexthop->nh_label->label[i], key);
518 }
519
520 key = jhash_2words(nexthop->ifindex,
521 CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK),
522 key);
523
524 return key;
525 }
526
527
528 #define GATE_SIZE 4 /* Number of uint32_t words in struct g_addr */
529
530 /* For a more granular hash */
531 uint32_t nexthop_hash(const struct nexthop *nexthop)
532 {
533 uint32_t gate_src_rmap_raw[GATE_SIZE * 3] = {};
534 /* Get all the quick stuff */
535 uint32_t key = nexthop_hash_quick(nexthop);
536
537 assert(((sizeof(nexthop->gate) + sizeof(nexthop->src)
538 + sizeof(nexthop->rmap_src))
539 / 3)
540 == (GATE_SIZE * sizeof(uint32_t)));
541
542 memcpy(gate_src_rmap_raw, &nexthop->gate, GATE_SIZE);
543 memcpy(gate_src_rmap_raw + GATE_SIZE, &nexthop->src, GATE_SIZE);
544 memcpy(gate_src_rmap_raw + (2 * GATE_SIZE), &nexthop->rmap_src,
545 GATE_SIZE);
546
547 key = jhash2(gate_src_rmap_raw, (GATE_SIZE * 3), key);
548
549 return key;
550 }
551
552 void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
553 struct nexthop *rparent)
554 {
555 copy->vrf_id = nexthop->vrf_id;
556 copy->ifindex = nexthop->ifindex;
557 copy->type = nexthop->type;
558 copy->flags = nexthop->flags;
559 copy->weight = nexthop->weight;
560 memcpy(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
561 memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
562 memcpy(&copy->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
563 copy->rparent = rparent;
564 if (nexthop->nh_label)
565 nexthop_add_labels(copy, nexthop->nh_label_type,
566 nexthop->nh_label->num_labels,
567 &nexthop->nh_label->label[0]);
568 }
569
570 struct nexthop *nexthop_dup(const struct nexthop *nexthop,
571 struct nexthop *rparent)
572 {
573 struct nexthop *new = nexthop_new();
574
575 nexthop_copy(new, nexthop, rparent);
576 return new;
577 }
578
579 /*
580 * nexthop printing variants:
581 * %pNHvv
582 * via 1.2.3.4
583 * via 1.2.3.4, eth0
584 * is directly connected, eth0
585 * unreachable (blackhole)
586 * %pNHv
587 * 1.2.3.4
588 * 1.2.3.4, via eth0
589 * directly connected, eth0
590 * unreachable (blackhole)
591 * %pNHs
592 * nexthop2str()
593 */
594 printfrr_ext_autoreg_p("NH", printfrr_nh)
595 static ssize_t printfrr_nh(char *buf, size_t bsz, const char *fmt,
596 int prec, const void *ptr)
597 {
598 const struct nexthop *nexthop = ptr;
599 struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
600 bool do_ifi = false;
601 const char *s, *v_is = "", *v_via = "", *v_viaif = "via ";
602 ssize_t ret = 3;
603
604 switch (fmt[2]) {
605 case 'v':
606 if (fmt[3] == 'v') {
607 v_is = "is ";
608 v_via = "via ";
609 v_viaif = "";
610 ret++;
611 }
612
613 switch (nexthop->type) {
614 case NEXTHOP_TYPE_IPV4:
615 case NEXTHOP_TYPE_IPV4_IFINDEX:
616 bprintfrr(&fb, "%s%pI4", v_via, &nexthop->gate.ipv4);
617 do_ifi = true;
618 break;
619 case NEXTHOP_TYPE_IPV6:
620 case NEXTHOP_TYPE_IPV6_IFINDEX:
621 bprintfrr(&fb, "%s%pI6", v_via, &nexthop->gate.ipv6);
622 do_ifi = true;
623 break;
624 case NEXTHOP_TYPE_IFINDEX:
625 bprintfrr(&fb, "%sdirectly connected, %s", v_is,
626 ifindex2ifname(nexthop->ifindex,
627 nexthop->vrf_id));
628 break;
629 case NEXTHOP_TYPE_BLACKHOLE:
630 switch (nexthop->bh_type) {
631 case BLACKHOLE_REJECT:
632 s = " (ICMP unreachable)";
633 break;
634 case BLACKHOLE_ADMINPROHIB:
635 s = " (ICMP admin-prohibited)";
636 break;
637 case BLACKHOLE_NULL:
638 s = " (blackhole)";
639 break;
640 default:
641 s = "";
642 break;
643 }
644 bprintfrr(&fb, "unreachable%s", s);
645 break;
646 default:
647 break;
648 }
649 if (do_ifi && nexthop->ifindex)
650 bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname(
651 nexthop->ifindex,
652 nexthop->vrf_id));
653
654 *fb.pos = '\0';
655 return ret;
656 case 's':
657 nexthop2str(nexthop, buf, bsz);
658 return 3;
659 }
660 return 0;
661 }