]> git.proxmox.com Git - mirror_frr.git/blob - lib/nexthop.c
lib, zebra: Allow for installation of a weighted nexthop
[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 switch (next1->type) {
122 case NEXTHOP_TYPE_IPV4:
123 case NEXTHOP_TYPE_IPV6:
124 ret = _nexthop_gateway_cmp(next1, next2);
125 if (ret != 0)
126 return ret;
127 break;
128 case NEXTHOP_TYPE_IPV4_IFINDEX:
129 case NEXTHOP_TYPE_IPV6_IFINDEX:
130 ret = _nexthop_gateway_cmp(next1, next2);
131 if (ret != 0)
132 return ret;
133 /* Intentional Fall-Through */
134 case NEXTHOP_TYPE_IFINDEX:
135 if (next1->ifindex < next2->ifindex)
136 return -1;
137
138 if (next1->ifindex > next2->ifindex)
139 return 1;
140 break;
141 case NEXTHOP_TYPE_BLACKHOLE:
142 if (next1->bh_type < next2->bh_type)
143 return -1;
144
145 if (next1->bh_type > next2->bh_type)
146 return 1;
147 break;
148 }
149
150 ret = _nexthop_source_cmp(next1, next2);
151
152 return ret;
153 }
154
155 int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2)
156 {
157 int ret = 0;
158
159 ret = _nexthop_cmp_no_labels(next1, next2);
160 if (ret != 0)
161 return ret;
162
163 ret = _nexthop_labels_cmp(next1, next2);
164
165 return ret;
166 }
167
168 int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2)
169 {
170 int type1 = NEXTHOP_FIRSTHOPTYPE(next1->type);
171 int type2 = NEXTHOP_FIRSTHOPTYPE(next2->type);
172
173 if (type1 != type2)
174 return 0;
175 switch (type1) {
176 case NEXTHOP_TYPE_IPV4_IFINDEX:
177 if (!IPV4_ADDR_SAME(&next1->gate.ipv4, &next2->gate.ipv4))
178 return 0;
179 if (next1->ifindex != next2->ifindex)
180 return 0;
181 break;
182 case NEXTHOP_TYPE_IFINDEX:
183 if (next1->ifindex != next2->ifindex)
184 return 0;
185 break;
186 case NEXTHOP_TYPE_IPV6_IFINDEX:
187 if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6))
188 return 0;
189 if (next1->ifindex != next2->ifindex)
190 return 0;
191 break;
192 default:
193 /* do nothing */
194 break;
195 }
196 return 1;
197 }
198
199 /*
200 * nexthop_type_to_str
201 */
202 const char *nexthop_type_to_str(enum nexthop_types_t nh_type)
203 {
204 static const char *const desc[] = {
205 "none", "Directly connected",
206 "IPv4 nexthop", "IPv4 nexthop with ifindex",
207 "IPv6 nexthop", "IPv6 nexthop with ifindex",
208 "Null0 nexthop",
209 };
210
211 return desc[nh_type];
212 }
213
214 /*
215 * Check if the labels match for the 2 nexthops specified.
216 */
217 bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2)
218 {
219 if (_nexthop_labels_cmp(nh1, nh2) != 0)
220 return false;
221
222 return true;
223 }
224
225 struct nexthop *nexthop_new(void)
226 {
227 return XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop));
228 }
229
230 /* Free nexthop. */
231 void nexthop_free(struct nexthop *nexthop)
232 {
233 nexthop_del_labels(nexthop);
234 if (nexthop->resolved)
235 nexthops_free(nexthop->resolved);
236 XFREE(MTYPE_NEXTHOP, nexthop);
237 }
238
239 /* Frees a list of nexthops */
240 void nexthops_free(struct nexthop *nexthop)
241 {
242 struct nexthop *nh, *next;
243
244 for (nh = nexthop; nh; nh = next) {
245 next = nh->next;
246 nexthop_free(nh);
247 }
248 }
249
250 bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2)
251 {
252 if (nh1 && !nh2)
253 return false;
254
255 if (!nh1 && nh2)
256 return false;
257
258 if (nh1 == nh2)
259 return true;
260
261 if (nexthop_cmp(nh1, nh2) != 0)
262 return false;
263
264 return true;
265 }
266
267 bool nexthop_same_no_labels(const struct nexthop *nh1,
268 const struct nexthop *nh2)
269 {
270 if (nh1 && !nh2)
271 return false;
272
273 if (!nh1 && nh2)
274 return false;
275
276 if (nh1 == nh2)
277 return true;
278
279 if (_nexthop_cmp_no_labels(nh1, nh2) != 0)
280 return false;
281
282 return true;
283 }
284
285 /*
286 * Allocate a new nexthop object and initialize it from various args.
287 */
288 struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
289 {
290 struct nexthop *nexthop;
291
292 nexthop = nexthop_new();
293 nexthop->type = NEXTHOP_TYPE_IFINDEX;
294 nexthop->ifindex = ifindex;
295 nexthop->vrf_id = vrf_id;
296
297 return nexthop;
298 }
299
300 struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4,
301 const struct in_addr *src,
302 vrf_id_t vrf_id)
303 {
304 struct nexthop *nexthop;
305
306 nexthop = nexthop_new();
307 nexthop->type = NEXTHOP_TYPE_IPV4;
308 nexthop->vrf_id = vrf_id;
309 nexthop->gate.ipv4 = *ipv4;
310 if (src)
311 nexthop->src.ipv4 = *src;
312
313 return nexthop;
314 }
315
316 struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4,
317 const struct in_addr *src,
318 ifindex_t ifindex, vrf_id_t vrf_id)
319 {
320 struct nexthop *nexthop;
321
322 nexthop = nexthop_new();
323 nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
324 nexthop->vrf_id = vrf_id;
325 nexthop->gate.ipv4 = *ipv4;
326 if (src)
327 nexthop->src.ipv4 = *src;
328 nexthop->ifindex = ifindex;
329
330 return nexthop;
331 }
332
333 struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6,
334 vrf_id_t vrf_id)
335 {
336 struct nexthop *nexthop;
337
338 nexthop = nexthop_new();
339 nexthop->vrf_id = vrf_id;
340 nexthop->type = NEXTHOP_TYPE_IPV6;
341 nexthop->gate.ipv6 = *ipv6;
342
343 return nexthop;
344 }
345
346 struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6,
347 ifindex_t ifindex, vrf_id_t vrf_id)
348 {
349 struct nexthop *nexthop;
350
351 nexthop = nexthop_new();
352 nexthop->vrf_id = vrf_id;
353 nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
354 nexthop->gate.ipv6 = *ipv6;
355 nexthop->ifindex = ifindex;
356
357 return nexthop;
358 }
359
360 struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type)
361 {
362 struct nexthop *nexthop;
363
364 nexthop = nexthop_new();
365 nexthop->vrf_id = VRF_DEFAULT;
366 nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
367 nexthop->bh_type = bh_type;
368
369 return nexthop;
370 }
371
372 /* Update nexthop with label information. */
373 void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type,
374 uint8_t num_labels, mpls_label_t *label)
375 {
376 struct mpls_label_stack *nh_label;
377 int i;
378
379 if (num_labels == 0)
380 return;
381
382 nexthop->nh_label_type = type;
383 nh_label = XCALLOC(MTYPE_NH_LABEL,
384 sizeof(struct mpls_label_stack)
385 + num_labels * sizeof(mpls_label_t));
386 nh_label->num_labels = num_labels;
387 for (i = 0; i < num_labels; i++)
388 nh_label->label[i] = *(label + i);
389 nexthop->nh_label = nh_label;
390 }
391
392 /* Free label information of nexthop, if present. */
393 void nexthop_del_labels(struct nexthop *nexthop)
394 {
395 if (nexthop->nh_label) {
396 XFREE(MTYPE_NH_LABEL, nexthop->nh_label);
397 nexthop->nh_label_type = ZEBRA_LSP_NONE;
398 }
399 }
400
401 const char *nexthop2str(const struct nexthop *nexthop, char *str, int size)
402 {
403 switch (nexthop->type) {
404 case NEXTHOP_TYPE_IFINDEX:
405 snprintf(str, size, "if %u", nexthop->ifindex);
406 break;
407 case NEXTHOP_TYPE_IPV4:
408 case NEXTHOP_TYPE_IPV4_IFINDEX:
409 snprintf(str, size, "%s if %u", inet_ntoa(nexthop->gate.ipv4),
410 nexthop->ifindex);
411 break;
412 case NEXTHOP_TYPE_IPV6:
413 case NEXTHOP_TYPE_IPV6_IFINDEX:
414 snprintf(str, size, "%s if %u", inet6_ntoa(nexthop->gate.ipv6),
415 nexthop->ifindex);
416 break;
417 case NEXTHOP_TYPE_BLACKHOLE:
418 snprintf(str, size, "blackhole");
419 break;
420 default:
421 snprintf(str, size, "unknown");
422 break;
423 }
424
425 return str;
426 }
427
428 /*
429 * Iteration step for ALL_NEXTHOPS macro:
430 * This is the tricky part. Check if `nexthop' has
431 * NEXTHOP_FLAG_RECURSIVE set. If yes, this implies that `nexthop' has
432 * at least one nexthop attached to `nexthop->resolved', which will be
433 * the next one.
434 *
435 * If NEXTHOP_FLAG_RECURSIVE is not set, `nexthop' will progress in its
436 * current chain. In case its current chain end is reached, it will move
437 * upwards in the recursion levels and progress there. Whenever a step
438 * forward in a chain is done, recursion will be checked again.
439 * In a nustshell, it's equivalent to a pre-traversal order assuming that
440 * left branch is 'resolved' and right branch is 'next':
441 * https://en.wikipedia.org/wiki/Tree_traversal#/media/File:Sorted_binary_tree_preorder.svg
442 */
443 struct nexthop *nexthop_next(const struct nexthop *nexthop)
444 {
445 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
446 return nexthop->resolved;
447
448 if (nexthop->next)
449 return nexthop->next;
450
451 for (struct nexthop *par = nexthop->rparent; par; par = par->rparent)
452 if (par->next)
453 return par->next;
454
455 return NULL;
456 }
457
458 /* Return the next nexthop in the tree that is resolved and active */
459 struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop)
460 {
461 struct nexthop *next = nexthop_next(nexthop);
462
463 while (next
464 && (CHECK_FLAG(next->flags, NEXTHOP_FLAG_RECURSIVE)
465 || !CHECK_FLAG(next->flags, NEXTHOP_FLAG_ACTIVE)))
466 next = nexthop_next(next);
467
468 return next;
469 }
470
471 unsigned int nexthop_level(struct nexthop *nexthop)
472 {
473 unsigned int rv = 0;
474
475 for (struct nexthop *par = nexthop->rparent; par; par = par->rparent)
476 rv++;
477
478 return rv;
479 }
480
481 /* Only hash word-sized things, let cmp do the rest. */
482 uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
483 {
484 uint32_t key = 0x45afe398;
485
486 key = jhash_3words(nexthop->type, nexthop->vrf_id,
487 nexthop->nh_label_type, key);
488
489 if (nexthop->nh_label) {
490 int labels = nexthop->nh_label->num_labels;
491 int i = 0;
492
493 while (labels >= 3) {
494 key = jhash_3words(nexthop->nh_label->label[i],
495 nexthop->nh_label->label[i + 1],
496 nexthop->nh_label->label[i + 2],
497 key);
498 labels -= 3;
499 i += 3;
500 }
501
502 if (labels >= 2) {
503 key = jhash_2words(nexthop->nh_label->label[i],
504 nexthop->nh_label->label[i + 1],
505 key);
506 labels -= 2;
507 i += 2;
508 }
509
510 if (labels >= 1)
511 key = jhash_1word(nexthop->nh_label->label[i], key);
512 }
513
514 key = jhash_2words(nexthop->ifindex,
515 CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK),
516 key);
517
518 return key;
519 }
520
521
522 #define GATE_SIZE 4 /* Number of uint32_t words in struct g_addr */
523
524 /* For a more granular hash */
525 uint32_t nexthop_hash(const struct nexthop *nexthop)
526 {
527 uint32_t gate_src_rmap_raw[GATE_SIZE * 3] = {};
528 /* Get all the quick stuff */
529 uint32_t key = nexthop_hash_quick(nexthop);
530
531 assert(((sizeof(nexthop->gate) + sizeof(nexthop->src)
532 + sizeof(nexthop->rmap_src))
533 / 3)
534 == (GATE_SIZE * sizeof(uint32_t)));
535
536 memcpy(gate_src_rmap_raw, &nexthop->gate, GATE_SIZE);
537 memcpy(gate_src_rmap_raw + GATE_SIZE, &nexthop->src, GATE_SIZE);
538 memcpy(gate_src_rmap_raw + (2 * GATE_SIZE), &nexthop->rmap_src,
539 GATE_SIZE);
540
541 key = jhash2(gate_src_rmap_raw, (GATE_SIZE * 3), key);
542
543 return key;
544 }
545
546 void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
547 struct nexthop *rparent)
548 {
549 copy->vrf_id = nexthop->vrf_id;
550 copy->ifindex = nexthop->ifindex;
551 copy->type = nexthop->type;
552 copy->flags = nexthop->flags;
553 copy->weight = nexthop->weight;
554 memcpy(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
555 memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
556 memcpy(&copy->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
557 copy->rparent = rparent;
558 if (nexthop->nh_label)
559 nexthop_add_labels(copy, nexthop->nh_label_type,
560 nexthop->nh_label->num_labels,
561 &nexthop->nh_label->label[0]);
562 }
563
564 struct nexthop *nexthop_dup(const struct nexthop *nexthop,
565 struct nexthop *rparent)
566 {
567 struct nexthop *new = nexthop_new();
568
569 nexthop_copy(new, nexthop, rparent);
570 return new;
571 }
572
573 /*
574 * nexthop printing variants:
575 * %pNHvv
576 * via 1.2.3.4
577 * via 1.2.3.4, eth0
578 * is directly connected, eth0
579 * unreachable (blackhole)
580 * %pNHv
581 * 1.2.3.4
582 * 1.2.3.4, via eth0
583 * directly connected, eth0
584 * unreachable (blackhole)
585 * %pNHs
586 * nexthop2str()
587 */
588 printfrr_ext_autoreg_p("NH", printfrr_nh)
589 static ssize_t printfrr_nh(char *buf, size_t bsz, const char *fmt,
590 int prec, const void *ptr)
591 {
592 const struct nexthop *nexthop = ptr;
593 struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
594 bool do_ifi = false;
595 const char *s, *v_is = "", *v_via = "", *v_viaif = "via ";
596 ssize_t ret = 3;
597
598 switch (fmt[2]) {
599 case 'v':
600 if (fmt[3] == 'v') {
601 v_is = "is ";
602 v_via = "via ";
603 v_viaif = "";
604 ret++;
605 }
606
607 switch (nexthop->type) {
608 case NEXTHOP_TYPE_IPV4:
609 case NEXTHOP_TYPE_IPV4_IFINDEX:
610 bprintfrr(&fb, "%s%pI4", v_via, &nexthop->gate.ipv4);
611 do_ifi = true;
612 break;
613 case NEXTHOP_TYPE_IPV6:
614 case NEXTHOP_TYPE_IPV6_IFINDEX:
615 bprintfrr(&fb, "%s%pI6", v_via, &nexthop->gate.ipv6);
616 do_ifi = true;
617 break;
618 case NEXTHOP_TYPE_IFINDEX:
619 bprintfrr(&fb, "%sdirectly connected, %s", v_is,
620 ifindex2ifname(nexthop->ifindex,
621 nexthop->vrf_id));
622 break;
623 case NEXTHOP_TYPE_BLACKHOLE:
624 switch (nexthop->bh_type) {
625 case BLACKHOLE_REJECT:
626 s = " (ICMP unreachable)";
627 break;
628 case BLACKHOLE_ADMINPROHIB:
629 s = " (ICMP admin-prohibited)";
630 break;
631 case BLACKHOLE_NULL:
632 s = " (blackhole)";
633 break;
634 default:
635 s = "";
636 break;
637 }
638 bprintfrr(&fb, "unreachable%s", s);
639 break;
640 default:
641 break;
642 }
643 if (do_ifi && nexthop->ifindex)
644 bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname(
645 nexthop->ifindex,
646 nexthop->vrf_id));
647
648 *fb.pos = '\0';
649 return ret;
650 case 's':
651 nexthop2str(nexthop, buf, bsz);
652 return 3;
653 }
654 return 0;
655 }