]> git.proxmox.com Git - mirror_frr.git/blob - lib/nexthop_group.c
lib: reduce exported var symbols
[mirror_frr.git] / lib / nexthop_group.c
1 /*
2 * Nexthop Group structure definition.
3 * Copyright (C) 2018 Cumulus Networks, Inc.
4 * Donald Sharp
5 *
6 * This program 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 Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * 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 <vrf.h>
23 #include <sockunion.h>
24 #include <nexthop.h>
25 #include <nexthop_group.h>
26 #include <vty.h>
27 #include <command.h>
28
29 #ifndef VTYSH_EXTRACT_PL
30 #include "lib/nexthop_group_clippy.c"
31 #endif
32
33 DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group")
34
35 struct nexthop_group_hooks {
36 void (*new)(const char *name);
37 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
38 const struct nexthop *nhop);
39 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
40 const struct nexthop *nhop);
41 void (*delete)(const char *name);
42 };
43
44 static struct nexthop_group_hooks nhg_hooks;
45
46 static inline int
47 nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
48 const struct nexthop_group_cmd *nhgc2);
49 RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry,
50 nexthop_group_cmd_compare)
51
52 static struct nhgc_entry_head nhgc_entries;
53
54 static inline int
55 nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
56 const struct nexthop_group_cmd *nhgc2)
57 {
58 return strcmp(nhgc1->name, nhgc2->name);
59 }
60
61 struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh)
62 {
63 struct nexthop *nexthop;
64
65 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
66 if (nexthop_same(nh, nexthop))
67 return nexthop;
68 }
69
70 return NULL;
71 }
72
73 struct nexthop_group *nexthop_group_new(void)
74 {
75 return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
76 }
77
78 void nexthop_group_delete(struct nexthop_group **nhg)
79 {
80 XFREE(MTYPE_NEXTHOP_GROUP, *nhg);
81 }
82
83 /* Add nexthop to the end of a nexthop list. */
84 void nexthop_add(struct nexthop **target, struct nexthop *nexthop)
85 {
86 struct nexthop *last;
87
88 for (last = *target; last && last->next; last = last->next)
89 ;
90 if (last)
91 last->next = nexthop;
92 else
93 *target = nexthop;
94 nexthop->prev = last;
95 }
96
97 /* Delete nexthop from a nexthop list. */
98 void nexthop_del(struct nexthop_group *nhg, struct nexthop *nh)
99 {
100 struct nexthop *nexthop;
101
102 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
103 if (nexthop_same(nh, nexthop))
104 break;
105 }
106
107 assert(nexthop);
108
109 if (nexthop->prev)
110 nexthop->prev->next = nexthop->next;
111 else
112 nhg->nexthop = nexthop->next;
113
114 if (nexthop->next)
115 nexthop->next->prev = nexthop->prev;
116
117 nh->prev = NULL;
118 nh->next = NULL;
119 }
120
121 void copy_nexthops(struct nexthop **tnh, struct nexthop *nh,
122 struct nexthop *rparent)
123 {
124 struct nexthop *nexthop;
125 struct nexthop *nh1;
126
127 for (nh1 = nh; nh1; nh1 = nh1->next) {
128 nexthop = nexthop_new();
129 nexthop->vrf_id = nh1->vrf_id;
130 nexthop->ifindex = nh1->ifindex;
131 nexthop->type = nh1->type;
132 nexthop->flags = nh1->flags;
133 memcpy(&nexthop->gate, &nh1->gate, sizeof(nh1->gate));
134 memcpy(&nexthop->src, &nh1->src, sizeof(nh1->src));
135 memcpy(&nexthop->rmap_src, &nh1->rmap_src,
136 sizeof(nh1->rmap_src));
137 nexthop->rparent = rparent;
138 if (nh1->nh_label)
139 nexthop_add_labels(nexthop, nh1->nh_label_type,
140 nh1->nh_label->num_labels,
141 &nh1->nh_label->label[0]);
142 nexthop_add(tnh, nexthop);
143
144 if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE))
145 copy_nexthops(&nexthop->resolved, nh1->resolved,
146 nexthop);
147 }
148 }
149
150 static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc)
151 {
152 struct nexthop *nexthop;
153
154 nexthop = nhgc->nhg.nexthop;
155 while (nexthop) {
156 struct nexthop *next = nexthop_next(nexthop);
157
158 nexthop_del(&nhgc->nhg, nexthop);
159 if (nhg_hooks.del_nexthop)
160 nhg_hooks.del_nexthop(nhgc, nexthop);
161
162 nexthop_free(nexthop);
163
164 nexthop = next;
165 }
166 }
167
168 struct nexthop_group_cmd *nhgc_find(const char *name)
169 {
170 struct nexthop_group_cmd find;
171
172 strlcpy(find.name, name, sizeof(find.name));
173
174 return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
175 }
176
177 static int nhgc_cmp_helper(const char *a, const char *b)
178 {
179 if (!a && !b)
180 return 0;
181
182 if (a && !b)
183 return -1;
184
185 if (!a && b)
186 return 1;
187
188 return strcmp(a, b);
189 }
190
191 static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b)
192 {
193 if (!a && !b)
194 return 0;
195
196 if (a && !b)
197 return -1;
198
199 if (!a && b)
200 return 1;
201
202 return sockunion_cmp(a, b);
203 }
204
205 static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
206 {
207 int ret;
208
209 ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr);
210 if (ret)
211 return ret;
212
213 ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
214 if (ret)
215 return ret;
216
217 return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
218 }
219
220 static void nhgl_delete(struct nexthop_hold *nh)
221 {
222 XFREE(MTYPE_TMP, nh->intf);
223
224 XFREE(MTYPE_TMP, nh->nhvrf_name);
225
226 if (nh->addr)
227 sockunion_free(nh->addr);
228
229 XFREE(MTYPE_TMP, nh);
230 }
231
232 static struct nexthop_group_cmd *nhgc_get(const char *name)
233 {
234 struct nexthop_group_cmd *nhgc;
235
236 nhgc = nhgc_find(name);
237 if (!nhgc) {
238 nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc));
239 strlcpy(nhgc->name, name, sizeof(nhgc->name));
240
241 QOBJ_REG(nhgc, nexthop_group_cmd);
242 RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
243
244 nhgc->nhg_list = list_new();
245 nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
246 nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
247
248 if (nhg_hooks.new)
249 nhg_hooks.new(name);
250 }
251
252 return nhgc;
253 }
254
255 static void nhgc_delete(struct nexthop_group_cmd *nhgc)
256 {
257 nhgc_delete_nexthops(nhgc);
258
259 if (nhg_hooks.delete)
260 nhg_hooks.delete(nhgc->name);
261
262 RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
263
264 list_delete(&nhgc->nhg_list);
265
266 XFREE(MTYPE_TMP, nhgc);
267 }
268
269 DEFINE_QOBJ_TYPE(nexthop_group_cmd)
270
271 DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NAME",
272 "Enter into the nexthop-group submode\n"
273 "Specify the NAME of the nexthop-group\n")
274 {
275 const char *nhg_name = argv[1]->arg;
276 struct nexthop_group_cmd *nhgc = NULL;
277
278 nhgc = nhgc_get(nhg_name);
279 VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
280
281 return CMD_SUCCESS;
282 }
283
284 DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NAME",
285 NO_STR
286 "Delete the nexthop-group\n"
287 "Specify the NAME of the nexthop-group\n")
288 {
289 const char *nhg_name = argv[2]->arg;
290 struct nexthop_group_cmd *nhgc = NULL;
291
292 nhgc = nhgc_find(nhg_name);
293 if (nhgc)
294 nhgc_delete(nhgc);
295
296 return CMD_SUCCESS;
297 }
298
299 static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
300 const char *nhvrf_name,
301 const union sockunion *addr,
302 const char *intf)
303 {
304 struct nexthop_hold *nh;
305
306 nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
307
308 if (nhvrf_name)
309 nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
310 if (intf)
311 nh->intf = XSTRDUP(MTYPE_TMP, intf);
312 if (addr)
313 nh->addr = sockunion_dup(addr);
314
315 listnode_add_sort(nhgc->nhg_list, nh);
316 }
317
318 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
319 const char *nhvrf_name,
320 const union sockunion *addr,
321 const char *intf)
322 {
323 struct nexthop_hold *nh;
324 struct listnode *node;
325
326 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
327 if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 &&
328 nhgc_addr_cmp_helper(addr, nh->addr) == 0 &&
329 nhgc_cmp_helper(intf, nh->intf) == 0)
330 break;
331 }
332
333 /*
334 * Something has gone seriously wrong, fail gracefully
335 */
336 if (!nh)
337 return;
338
339 list_delete_node(nhgc->nhg_list, node);
340 nhgl_delete(nh);
341 }
342
343 static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
344 const union sockunion *addr,
345 const char *intf, const char *name)
346 {
347 struct vrf *vrf;
348
349 memset(nhop, 0, sizeof(*nhop));
350
351 if (name)
352 vrf = vrf_lookup_by_name(name);
353 else
354 vrf = vrf_lookup_by_id(VRF_DEFAULT);
355
356 if (!vrf)
357 return false;
358
359 nhop->vrf_id = vrf->vrf_id;
360
361 if (intf) {
362 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
363 if (nhop->ifindex == IFINDEX_INTERNAL)
364 return false;
365 }
366
367 if (addr) {
368 if (addr->sa.sa_family == AF_INET) {
369 nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
370 if (intf)
371 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
372 else
373 nhop->type = NEXTHOP_TYPE_IPV4;
374 } else {
375 nhop->gate.ipv6 = addr->sin6.sin6_addr;
376 if (intf)
377 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
378 else
379 nhop->type = NEXTHOP_TYPE_IPV6;
380 }
381 } else
382 nhop->type = NEXTHOP_TYPE_IFINDEX;
383
384 return true;
385 }
386
387 DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
388 "[no] nexthop\
389 <\
390 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
391 |INTERFACE$intf\
392 >\
393 [nexthop-vrf NAME$name]",
394 NO_STR
395 "Specify one of the nexthops in this ECMP group\n"
396 "v4 Address\n"
397 "v6 Address\n"
398 "Interface to use\n"
399 "Interface to use\n"
400 "If the nexthop is in a different vrf tell us\n"
401 "The nexthop-vrf Name\n")
402 {
403 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
404 struct nexthop nhop;
405 struct nexthop *nh;
406 bool legal;
407
408 legal = nexthop_group_parse_nexthop(&nhop, addr, intf, name);
409
410 if (nhop.type == NEXTHOP_TYPE_IPV6
411 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
412 vty_out(vty,
413 "Specified a v6 LL with no interface, rejecting\n");
414 return CMD_WARNING_CONFIG_FAILED;
415 }
416
417 nh = nexthop_exists(&nhgc->nhg, &nhop);
418
419 if (no) {
420 nexthop_group_unsave_nhop(nhgc, name, addr, intf);
421 if (nh) {
422 nexthop_del(&nhgc->nhg, nh);
423
424 if (nhg_hooks.del_nexthop)
425 nhg_hooks.del_nexthop(nhgc, nh);
426
427 nexthop_free(nh);
428 }
429 } else if (!nh) {
430 /* must be adding new nexthop since !no and !nexthop_exists */
431 if (legal) {
432 nh = nexthop_new();
433
434 memcpy(nh, &nhop, sizeof(nhop));
435 nexthop_add(&nhgc->nhg.nexthop, nh);
436 }
437
438 nexthop_group_save_nhop(nhgc, name, addr, intf);
439
440 if (legal && nhg_hooks.add_nexthop)
441 nhg_hooks.add_nexthop(nhgc, nh);
442 }
443
444 return CMD_SUCCESS;
445 }
446
447 struct cmd_node nexthop_group_node = {
448 NH_GROUP_NODE,
449 "%s(config-nh-group)# ",
450 1
451 };
452
453 void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
454 {
455 char buf[100];
456 struct vrf *vrf;
457
458 vty_out(vty, "nexthop ");
459
460 switch (nh->type) {
461 case NEXTHOP_TYPE_IFINDEX:
462 vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id));
463 break;
464 case NEXTHOP_TYPE_IPV4:
465 vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4));
466 break;
467 case NEXTHOP_TYPE_IPV4_IFINDEX:
468 vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4),
469 ifindex2ifname(nh->ifindex, nh->vrf_id));
470 break;
471 case NEXTHOP_TYPE_IPV6:
472 vty_out(vty, "%s",
473 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
474 break;
475 case NEXTHOP_TYPE_IPV6_IFINDEX:
476 vty_out(vty, "%s %s",
477 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)),
478 ifindex2ifname(nh->ifindex, nh->vrf_id));
479 break;
480 case NEXTHOP_TYPE_BLACKHOLE:
481 break;
482 }
483
484 if (nh->vrf_id != VRF_DEFAULT) {
485 vrf = vrf_lookup_by_id(nh->vrf_id);
486 vty_out(vty, " nexthop-vrf %s", vrf->name);
487 }
488 vty_out(vty, "\n");
489 }
490
491 static void nexthop_group_write_nexthop_internal(struct vty *vty,
492 struct nexthop_hold *nh)
493 {
494 char buf[100];
495
496 vty_out(vty, "nexthop");
497
498 if (nh->addr)
499 vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf)));
500
501 if (nh->intf)
502 vty_out(vty, " %s", nh->intf);
503
504 if (nh->nhvrf_name)
505 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
506
507 vty_out(vty, "\n");
508 }
509
510 static int nexthop_group_write(struct vty *vty)
511 {
512 struct nexthop_group_cmd *nhgc;
513 struct nexthop_hold *nh;
514
515 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
516 struct listnode *node;
517
518 vty_out(vty, "nexthop-group %s\n", nhgc->name);
519
520 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
521 vty_out(vty, " ");
522 nexthop_group_write_nexthop_internal(vty, nh);
523 }
524
525 vty_out(vty, "!\n");
526 }
527
528 return 1;
529 }
530
531 void nexthop_group_enable_vrf(struct vrf *vrf)
532 {
533 struct nexthop_group_cmd *nhgc;
534 struct nexthop_hold *nhh;
535
536 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
537 struct listnode *node;
538
539 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
540 struct nexthop nhop;
541 struct nexthop *nh;
542
543 if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
544 nhh->intf,
545 nhh->nhvrf_name))
546 continue;
547
548 nh = nexthop_exists(&nhgc->nhg, &nhop);
549
550 if (nh)
551 continue;
552
553 if (nhop.vrf_id != vrf->vrf_id)
554 continue;
555
556 nh = nexthop_new();
557
558 memcpy(nh, &nhop, sizeof(nhop));
559 nexthop_add(&nhgc->nhg.nexthop, nh);
560
561 if (nhg_hooks.add_nexthop)
562 nhg_hooks.add_nexthop(nhgc, nh);
563 }
564 }
565 }
566
567 void nexthop_group_disable_vrf(struct vrf *vrf)
568 {
569 struct nexthop_group_cmd *nhgc;
570 struct nexthop_hold *nhh;
571
572 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
573 struct listnode *node;
574
575 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
576 struct nexthop nhop;
577 struct nexthop *nh;
578
579 if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
580 nhh->intf,
581 nhh->nhvrf_name))
582 continue;
583
584 nh = nexthop_exists(&nhgc->nhg, &nhop);
585
586 if (!nh)
587 continue;
588
589 if (nh->vrf_id != vrf->vrf_id)
590 continue;
591
592 nexthop_del(&nhgc->nhg, nh);
593
594 if (nhg_hooks.del_nexthop)
595 nhg_hooks.del_nexthop(nhgc, nh);
596
597 nexthop_free(nh);
598 }
599 }
600 }
601
602 void nexthop_group_interface_state_change(struct interface *ifp,
603 ifindex_t oldifindex)
604 {
605 struct nexthop_group_cmd *nhgc;
606 struct nexthop_hold *nhh;
607
608 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
609 struct listnode *node;
610 struct nexthop *nh;
611
612 if (if_is_up(ifp)) {
613 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
614 struct nexthop nhop;
615
616 if (!nexthop_group_parse_nexthop(
617 &nhop, nhh->addr, nhh->intf,
618 nhh->nhvrf_name))
619 continue;
620
621 switch (nhop.type) {
622 case NEXTHOP_TYPE_IPV4:
623 case NEXTHOP_TYPE_IPV6:
624 case NEXTHOP_TYPE_BLACKHOLE:
625 continue;
626 case NEXTHOP_TYPE_IFINDEX:
627 case NEXTHOP_TYPE_IPV4_IFINDEX:
628 case NEXTHOP_TYPE_IPV6_IFINDEX:
629 break;
630 }
631 nh = nexthop_exists(&nhgc->nhg, &nhop);
632
633 if (nh)
634 continue;
635
636 if (ifp->ifindex != nhop.ifindex)
637 continue;
638
639 nh = nexthop_new();
640
641 memcpy(nh, &nhop, sizeof(nhop));
642 nexthop_add(&nhgc->nhg.nexthop, nh);
643
644 if (nhg_hooks.add_nexthop)
645 nhg_hooks.add_nexthop(nhgc, nh);
646 }
647 } else {
648 struct nexthop *next_nh;
649
650 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
651 next_nh = nh->next;
652 switch (nh->type) {
653 case NEXTHOP_TYPE_IPV4:
654 case NEXTHOP_TYPE_IPV6:
655 case NEXTHOP_TYPE_BLACKHOLE:
656 continue;
657 case NEXTHOP_TYPE_IFINDEX:
658 case NEXTHOP_TYPE_IPV4_IFINDEX:
659 case NEXTHOP_TYPE_IPV6_IFINDEX:
660 break;
661 }
662
663 if (oldifindex != nh->ifindex)
664 continue;
665
666 nexthop_del(&nhgc->nhg, nh);
667
668 if (nhg_hooks.del_nexthop)
669 nhg_hooks.del_nexthop(nhgc, nh);
670
671 nexthop_free(nh);
672 }
673 }
674 }
675 }
676
677 void nexthop_group_init(void (*new)(const char *name),
678 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
679 const struct nexthop *nhop),
680 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
681 const struct nexthop *nhop),
682 void (*delete)(const char *name))
683 {
684 RB_INIT(nhgc_entry_head, &nhgc_entries);
685
686 install_node(&nexthop_group_node, nexthop_group_write);
687 install_element(CONFIG_NODE, &nexthop_group_cmd);
688 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
689
690 install_default(NH_GROUP_NODE);
691 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
692
693 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
694
695 if (new)
696 nhg_hooks.new = new;
697 if (add_nexthop)
698 nhg_hooks.add_nexthop = add_nexthop;
699 if (del_nexthop)
700 nhg_hooks.del_nexthop = del_nexthop;
701 if (delete)
702 nhg_hooks.delete = delete;
703 }