3 * Copyright (C) 2018 Cumulus Networks, Inc.
6 * FRR 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
11 * FRR 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.
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
24 #include "nexthop_group.h"
25 #include "nexthop_group_private.h"
32 #include "pbrd/pbr_nht.h"
33 #include "pbrd/pbr_map.h"
34 #include "pbrd/pbr_zebra.h"
35 #include "pbrd/pbr_memory.h"
36 #include "pbrd/pbr_debug.h"
38 DEFINE_MTYPE_STATIC(PBRD
, PBR_NHG
, "PBR Nexthop Groups");
40 struct hash
*pbr_nhg_hash
;
41 static struct hash
*pbr_nhrc_hash
;
43 static uint32_t pbr_nhg_low_table
;
44 static uint32_t pbr_nhg_high_table
;
45 static uint32_t pbr_nhg_low_rule
;
46 static uint32_t pbr_nhg_high_rule
;
47 static bool nhg_tableid
[65535];
49 static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
50 struct nexthop_group nhg
);
52 pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
53 struct nexthop_group nhg
,
54 enum nexthop_types_t nh_type
);
60 struct nexthop nexthop
;
61 unsigned int refcount
;
64 /* Hash functions for pbr_nhrc_hash ---------------------------------------- */
66 static void *pbr_nhrc_hash_alloc(void *p
)
68 struct nhrc
*nhrc
= XCALLOC(MTYPE_PBR_NHG
, sizeof(struct nhrc
));
69 nhrc
->nexthop
= *(struct nexthop
*)p
;
70 nhrc
->nexthop
.next
= NULL
;
71 nhrc
->nexthop
.prev
= NULL
;
75 static bool pbr_nhrc_hash_equal(const void *arg1
, const void *arg2
)
77 const struct nexthop
*nh1
, *nh2
;
82 return nexthop_same(nh1
, nh2
);
85 /* ------------------------------------------------------------------------- */
87 static void *pbr_nh_alloc(void *p
)
89 struct pbr_nexthop_cache
*new;
90 struct pbr_nexthop_cache
*pnhc
= (struct pbr_nexthop_cache
*)p
;
93 new = XCALLOC(MTYPE_PBR_NHG
, sizeof(*new));
94 nhrc
= hash_get(pbr_nhrc_hash
, &pnhc
->nexthop
, pbr_nhrc_hash_alloc
);
95 new->nexthop
= nhrc
->nexthop
;
97 /* Decremented again in pbr_nh_delete */
100 DEBUGD(&pbr_dbg_nht
, "%s: Sending nexthop to Zebra", __func__
);
102 pbr_send_rnh(&new->nexthop
, true);
108 static void pbr_nh_delete(struct pbr_nexthop_cache
**pnhc
)
112 nhrc
= hash_lookup(pbr_nhrc_hash
, &((*pnhc
)->nexthop
));
116 if (!nhrc
|| nhrc
->refcount
== 0) {
117 DEBUGD(&pbr_dbg_nht
, "%s: Removing nexthop from Zebra",
119 pbr_send_rnh(&((*pnhc
)->nexthop
), false);
121 if (nhrc
&& nhrc
->refcount
== 0) {
122 hash_release(pbr_nhrc_hash
, nhrc
);
123 XFREE(MTYPE_PBR_NHG
, nhrc
);
126 XFREE(MTYPE_PBR_NHG
, *pnhc
);
129 static void pbr_nh_delete_iterate(struct hash_bucket
*b
, void *p
)
131 pbr_nh_delete((struct pbr_nexthop_cache
**)&b
->data
);
134 static uint32_t pbr_nh_hash_key(const void *arg
)
137 const struct pbr_nexthop_cache
*pbrnc
= arg
;
139 key
= nexthop_hash(&pbrnc
->nexthop
);
144 static bool pbr_nh_hash_equal(const void *arg1
, const void *arg2
)
146 const struct pbr_nexthop_cache
*pbrnc1
=
147 (const struct pbr_nexthop_cache
*)arg1
;
148 const struct pbr_nexthop_cache
*pbrnc2
=
149 (const struct pbr_nexthop_cache
*)arg2
;
151 if (pbrnc1
->nexthop
.vrf_id
!= pbrnc2
->nexthop
.vrf_id
)
154 if (pbrnc1
->nexthop
.ifindex
!= pbrnc2
->nexthop
.ifindex
)
157 if (pbrnc1
->nexthop
.type
!= pbrnc2
->nexthop
.type
)
160 switch (pbrnc1
->nexthop
.type
) {
161 case NEXTHOP_TYPE_IFINDEX
:
162 return pbrnc1
->nexthop
.ifindex
== pbrnc2
->nexthop
.ifindex
;
163 case NEXTHOP_TYPE_IPV4_IFINDEX
:
164 case NEXTHOP_TYPE_IPV4
:
165 return pbrnc1
->nexthop
.gate
.ipv4
.s_addr
166 == pbrnc2
->nexthop
.gate
.ipv4
.s_addr
;
167 case NEXTHOP_TYPE_IPV6_IFINDEX
:
168 case NEXTHOP_TYPE_IPV6
:
169 return !memcmp(&pbrnc1
->nexthop
.gate
.ipv6
,
170 &pbrnc2
->nexthop
.gate
.ipv6
, 16);
171 case NEXTHOP_TYPE_BLACKHOLE
:
172 return pbrnc1
->nexthop
.bh_type
== pbrnc2
->nexthop
.bh_type
;
176 * We should not get here
181 static void pbr_nhgc_delete(struct pbr_nexthop_group_cache
*p
)
183 hash_iterate(p
->nhh
, pbr_nh_delete_iterate
, NULL
);
185 XFREE(MTYPE_PBR_NHG
, p
);
188 static void *pbr_nhgc_alloc(void *p
)
190 struct pbr_nexthop_group_cache
*new;
191 struct pbr_nexthop_group_cache
*pnhgc
=
192 (struct pbr_nexthop_group_cache
*)p
;
194 new = XCALLOC(MTYPE_PBR_NHG
, sizeof(*new));
196 strlcpy(new->name
, pnhgc
->name
, sizeof(pnhgc
->name
));
197 new->table_id
= pbr_nht_get_next_tableid(false);
199 DEBUGD(&pbr_dbg_nht
, "%s: NHT: %s assigned Table ID: %u", __func__
,
200 new->name
, new->table_id
);
202 new->nhh
= hash_create_size(8, pbr_nh_hash_key
, pbr_nh_hash_equal
,
203 "PBR NH Cache Hash");
208 void pbr_nhgroup_add_cb(const char *name
)
210 struct pbr_nexthop_group_cache
*pnhgc
;
211 struct nexthop_group_cmd
*nhgc
;
213 nhgc
= nhgc_find(name
);
216 DEBUGD(&pbr_dbg_nht
, "%s: Could not find nhgc with name: %s",
221 pnhgc
= pbr_nht_add_group(name
);
226 DEBUGD(&pbr_dbg_nht
, "%s: Added nexthop-group %s", __func__
, name
);
228 pbr_map_check_nh_group_change(name
);
231 void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd
*nhgc
,
232 const struct nexthop
*nhop
)
235 struct pbr_nexthop_group_cache pnhgc_find
= {};
236 struct pbr_nexthop_group_cache
*pnhgc
;
237 struct pbr_nexthop_cache pnhc_find
= {};
238 struct pbr_nexthop_cache
*pnhc
;
240 if (!pbr_nht_get_next_tableid(true)) {
242 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
243 __func__
, nhgc
->name
);
247 /* find pnhgc by name */
248 strlcpy(pnhgc_find
.name
, nhgc
->name
, sizeof(pnhgc_find
.name
));
249 pnhgc
= hash_get(pbr_nhg_hash
, &pnhgc_find
, pbr_nhgc_alloc
);
251 /* create & insert new pnhc into pnhgc->nhh */
252 pnhc_find
.nexthop
= *nhop
;
253 pnhc
= hash_get(pnhgc
->nhh
, &pnhc_find
, pbr_nh_alloc
);
255 /* set parent pnhgc */
256 pnhc
->parent
= pnhgc
;
258 if (DEBUG_MODE_CHECK(&pbr_dbg_nht
, DEBUG_MODE_ALL
)) {
259 nexthop2str(nhop
, debugstr
, sizeof(debugstr
));
260 DEBUGD(&pbr_dbg_nht
, "%s: Added %s to nexthop-group %s",
261 __func__
, debugstr
, nhgc
->name
);
264 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
265 pbr_map_check_nh_group_change(nhgc
->name
);
267 if (nhop
->type
== NEXTHOP_TYPE_IFINDEX
268 || (nhop
->type
== NEXTHOP_TYPE_IPV6_IFINDEX
269 && IN6_IS_ADDR_LINKLOCAL(&nhop
->gate
.ipv6
))) {
270 struct interface
*ifp
;
272 ifp
= if_lookup_by_index(nhop
->ifindex
, nhop
->vrf_id
);
274 pbr_nht_nexthop_interface_update(ifp
);
278 void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd
*nhgc
,
279 const struct nexthop
*nhop
)
282 struct pbr_nexthop_group_cache pnhgc_find
= {};
283 struct pbr_nexthop_group_cache
*pnhgc
;
284 struct pbr_nexthop_cache pnhc_find
= {};
285 struct pbr_nexthop_cache
*pnhc
;
286 enum nexthop_types_t nh_type
= nhop
->type
;
288 /* find pnhgc by name */
289 strlcpy(pnhgc_find
.name
, nhgc
->name
, sizeof(pnhgc_find
.name
));
290 pnhgc
= hash_lookup(pbr_nhg_hash
, &pnhgc_find
);
292 /* delete pnhc from pnhgc->nhh */
293 pnhc_find
.nexthop
= *nhop
;
294 pnhc
= hash_release(pnhgc
->nhh
, &pnhc_find
);
297 pbr_nh_delete(&pnhc
);
299 if (DEBUG_MODE_CHECK(&pbr_dbg_nht
, DEBUG_MODE_ALL
)) {
300 nexthop2str(nhop
, debugstr
, sizeof(debugstr
));
301 DEBUGD(&pbr_dbg_nht
, "%s: Removed %s from nexthop-group %s",
302 __func__
, debugstr
, nhgc
->name
);
305 if (pnhgc
->nhh
->count
)
306 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
308 pbr_nht_uninstall_nexthop_group(pnhgc
, nhgc
->nhg
, nh_type
);
310 pbr_map_check_nh_group_change(nhgc
->name
);
313 void pbr_nhgroup_delete_cb(const char *name
)
315 DEBUGD(&pbr_dbg_nht
, "%s: Removed nexthop-group %s", __func__
, name
);
317 /* delete group from all pbrms's */
318 pbr_nht_delete_group(name
);
320 pbr_map_check_nh_group_change(name
);
324 pbr_nht_find_nhg_from_table_update(struct pbr_nexthop_group_cache
*pnhgc
,
325 uint32_t table_id
, bool installed
)
327 if (pnhgc
->table_id
== table_id
) {
328 DEBUGD(&pbr_dbg_nht
, "%s: %s: Table ID (%u) matches %s",
329 __func__
, (installed
? "install" : "remove"), table_id
,
332 pnhgc
->installed
= installed
;
333 pnhgc
->valid
= installed
;
334 pbr_map_schedule_policy_from_nhg(pnhgc
->name
, pnhgc
->installed
);
338 static void pbr_nht_find_nhg_from_table_install(struct hash_bucket
*b
,
341 struct pbr_nexthop_group_cache
*pnhgc
=
342 (struct pbr_nexthop_group_cache
*)b
->data
;
343 uint32_t table_id
= *(uint32_t *)data
;
345 pbr_nht_find_nhg_from_table_update(pnhgc
, table_id
, true);
348 void pbr_nht_route_installed_for_table(uint32_t table_id
)
350 hash_iterate(pbr_nhg_hash
, pbr_nht_find_nhg_from_table_install
,
354 static void pbr_nht_find_nhg_from_table_remove(struct hash_bucket
*b
,
357 struct pbr_nexthop_group_cache
*pnhgc
=
358 (struct pbr_nexthop_group_cache
*)b
->data
;
359 uint32_t table_id
= *(uint32_t *)data
;
361 pbr_nht_find_nhg_from_table_update(pnhgc
, table_id
, false);
364 void pbr_nht_route_removed_for_table(uint32_t table_id
)
366 hash_iterate(pbr_nhg_hash
, pbr_nht_find_nhg_from_table_remove
,
371 * Loop through all nexthops in a nexthop group to check that they are all the
372 * same. If they are not all the same, log this peculiarity.
375 * The nexthop group to check
378 * - AFI of last nexthop in the group
381 static afi_t
pbr_nht_which_afi(struct nexthop_group nhg
,
382 enum nexthop_types_t nh_type
)
384 struct nexthop
*nexthop
;
385 afi_t install_afi
= AFI_MAX
;
390 case NEXTHOP_TYPE_IPV4
:
391 case NEXTHOP_TYPE_IPV4_IFINDEX
:
393 case NEXTHOP_TYPE_IPV6
:
394 case NEXTHOP_TYPE_IPV6_IFINDEX
:
396 case NEXTHOP_TYPE_IFINDEX
:
397 case NEXTHOP_TYPE_BLACKHOLE
:
402 v6
= v4
= bh
= false;
404 for (ALL_NEXTHOPS(nhg
, nexthop
)) {
405 nh_type
= nexthop
->type
;
408 case NEXTHOP_TYPE_IFINDEX
:
410 case NEXTHOP_TYPE_IPV4
:
411 case NEXTHOP_TYPE_IPV4_IFINDEX
:
413 install_afi
= AFI_IP
;
415 case NEXTHOP_TYPE_IPV6
:
416 case NEXTHOP_TYPE_IPV6_IFINDEX
:
418 install_afi
= AFI_IP6
;
420 case NEXTHOP_TYPE_BLACKHOLE
:
426 /* Interface and/or blackhole nexthops only. */
428 install_afi
= AFI_MAX
;
432 "%s: Saw both V6 and V4 nexthops...using %s", __func__
,
433 afi2str(install_afi
));
434 if (bh
&& (v6
|| v4
))
436 "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.",
437 __func__
, v4
? "v4" : "", (v4
&& v6
) ? " and " : "",
443 static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
444 struct nexthop_group nhg
)
447 enum nexthop_types_t nh_type
= 0;
449 install_afi
= pbr_nht_which_afi(nhg
, nh_type
);
451 route_add(pnhgc
, nhg
, install_afi
);
455 pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
456 struct nexthop_group nhg
,
457 enum nexthop_types_t nh_type
)
461 install_afi
= pbr_nht_which_afi(nhg
, nh_type
);
463 pnhgc
->installed
= false;
464 pnhgc
->valid
= false;
465 route_delete(pnhgc
, install_afi
);
468 void pbr_nht_change_group(const char *name
)
470 struct nexthop_group_cmd
*nhgc
;
471 struct pbr_nexthop_group_cache
*pnhgc
;
472 struct pbr_nexthop_group_cache find
;
473 struct nexthop
*nhop
;
475 nhgc
= nhgc_find(name
);
479 memset(&find
, 0, sizeof(find
));
480 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
481 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
485 "%s: Could not find nexthop-group cache w/ name '%s'",
490 for (ALL_NEXTHOPS(nhgc
->nhg
, nhop
)) {
491 struct pbr_nexthop_cache lookup
;
492 struct pbr_nexthop_cache
*pnhc
;
494 lookup
.nexthop
= *nhop
;
495 pnhc
= hash_lookup(pnhgc
->nhh
, &lookup
);
497 pnhc
= hash_get(pnhgc
->nhh
, &lookup
, pbr_nh_alloc
);
498 pnhc
->parent
= pnhgc
;
501 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
504 char *pbr_nht_nexthop_make_name(char *name
, size_t l
,
505 uint32_t seqno
, char *buffer
)
507 snprintf(buffer
, l
, "%s%u", name
, seqno
);
511 void pbr_nht_add_individual_nexthop(struct pbr_map_sequence
*pbrms
,
512 const struct nexthop
*nhop
)
514 struct pbr_nexthop_group_cache
*pnhgc
;
515 struct pbr_nexthop_group_cache find
;
516 struct pbr_nexthop_cache
*pnhc
;
517 struct pbr_nexthop_cache lookup
;
519 char buf
[PBR_NHC_NAMELEN
];
521 pbrms
->nhg
= nexthop_group_new();
522 pbrms
->internal_nhg_name
= XSTRDUP(
524 pbr_nht_nexthop_make_name(pbrms
->parent
->name
, PBR_NHC_NAMELEN
,
528 memcpy(nh
, nhop
, sizeof(*nh
));
530 nexthop_group_add_sorted(pbrms
->nhg
, nh
);
532 memset(&find
, 0, sizeof(find
));
533 pbr_nht_nexthop_make_name(pbrms
->parent
->name
, PBR_NHC_NAMELEN
,
534 pbrms
->seqno
, find
.name
);
536 if (!pbr_nht_get_next_tableid(true)) {
538 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
539 __func__
, find
.name
);
543 if (!pbrms
->internal_nhg_name
)
544 pbrms
->internal_nhg_name
= XSTRDUP(MTYPE_TMP
, find
.name
);
546 pnhgc
= hash_get(pbr_nhg_hash
, &find
, pbr_nhgc_alloc
);
548 lookup
.nexthop
= *pbrms
->nhg
->nexthop
;
549 pnhc
= hash_get(pnhgc
->nhh
, &lookup
, pbr_nh_alloc
);
550 pnhc
->parent
= pnhgc
;
551 if (nhop
->vrf_id
!= VRF_DEFAULT
) {
552 struct vrf
*vrf
= vrf_lookup_by_id(nhop
->vrf_id
);
555 strlcpy(pnhc
->vrf_name
, vrf
->name
,
556 sizeof(pnhc
->vrf_name
));
559 if (nhop
->ifindex
!= 0) {
560 struct interface
*ifp
=
561 if_lookup_by_index(nhop
->ifindex
, nhop
->vrf_id
);
564 strlcpy(pnhc
->intf_name
, ifp
->name
,
565 sizeof(pnhc
->intf_name
));
567 pbr_nht_install_nexthop_group(pnhgc
, *pbrms
->nhg
);
570 static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence
*pbrms
)
572 struct pbr_nexthop_group_cache
*pnhgc
;
573 struct pbr_nexthop_group_cache find
;
574 struct pbr_nexthop_cache
*pnhc
;
575 struct pbr_nexthop_cache lup
;
577 enum nexthop_types_t nh_type
= 0;
579 memset(&find
, 0, sizeof(find
));
580 snprintf(find
.name
, sizeof(find
.name
), "%s", pbrms
->internal_nhg_name
);
581 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
583 nh
= pbrms
->nhg
->nexthop
;
586 pnhc
= hash_lookup(pnhgc
->nhh
, &lup
);
588 hash_release(pnhgc
->nhh
, pnhc
);
589 pbr_nh_delete(&pnhc
);
590 pbr_nht_uninstall_nexthop_group(pnhgc
, *pbrms
->nhg
, nh_type
);
592 hash_release(pbr_nhg_hash
, pnhgc
);
593 pbr_nhgc_delete(pnhgc
);
595 nexthop_group_delete(&pbrms
->nhg
);
596 XFREE(MTYPE_TMP
, pbrms
->internal_nhg_name
);
599 void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence
*pbrms
)
601 pbr_map_delete_nexthops(pbrms
);
603 pbr_nht_release_individual_nexthop(pbrms
);
606 struct pbr_nexthop_group_cache
*pbr_nht_add_group(const char *name
)
608 struct nexthop
*nhop
;
609 struct nexthop_group_cmd
*nhgc
;
610 struct pbr_nexthop_group_cache
*pnhgc
;
611 struct pbr_nexthop_group_cache lookup
;
613 if (!pbr_nht_get_next_tableid(true)) {
615 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
620 nhgc
= nhgc_find(name
);
623 DEBUGD(&pbr_dbg_nht
, "%s: Could not find nhgc with name: %s",
628 snprintf(lookup
.name
, sizeof(lookup
.name
), "%s", name
);
629 pnhgc
= hash_get(pbr_nhg_hash
, &lookup
, pbr_nhgc_alloc
);
630 DEBUGD(&pbr_dbg_nht
, "%s: Retrieved NHGC @ %p", __func__
, pnhgc
);
632 for (ALL_NEXTHOPS(nhgc
->nhg
, nhop
)) {
633 struct pbr_nexthop_cache lookupc
;
634 struct pbr_nexthop_cache
*pnhc
;
636 lookupc
.nexthop
= *nhop
;
637 pnhc
= hash_lookup(pnhgc
->nhh
, &lookupc
);
639 pnhc
= hash_get(pnhgc
->nhh
, &lookupc
, pbr_nh_alloc
);
640 pnhc
->parent
= pnhgc
;
647 void pbr_nht_delete_group(const char *name
)
649 struct pbr_map_sequence
*pbrms
;
650 struct listnode
*snode
;
651 struct pbr_map
*pbrm
;
652 struct pbr_nexthop_group_cache pnhgc_find
;
653 struct pbr_nexthop_group_cache
*pnhgc
;
655 RB_FOREACH (pbrm
, pbr_map_entry_head
, &pbr_maps
) {
656 for (ALL_LIST_ELEMENTS_RO(pbrm
->seqnumbers
, snode
, pbrms
)) {
657 if (pbrms
->nhgrp_name
658 && strmatch(pbrms
->nhgrp_name
, name
)) {
659 pbrms
->reason
|= PBR_MAP_INVALID_NO_NEXTHOPS
;
661 pbrms
->internal_nhg_name
= NULL
;
667 strlcpy(pnhgc_find
.name
, name
, sizeof(pnhgc_find
.name
));
668 pnhgc
= hash_release(pbr_nhg_hash
, &pnhgc_find
);
669 pbr_nhgc_delete(pnhgc
);
672 bool pbr_nht_nexthop_valid(struct nexthop_group
*nhg
)
674 DEBUGD(&pbr_dbg_nht
, "%s: %p", __func__
, nhg
);
678 bool pbr_nht_nexthop_group_valid(const char *name
)
680 struct pbr_nexthop_group_cache
*pnhgc
;
681 struct pbr_nexthop_group_cache lookup
;
683 DEBUGD(&pbr_dbg_nht
, "%s: %s", __func__
, name
);
685 snprintf(lookup
.name
, sizeof(lookup
.name
), "%s", name
);
686 pnhgc
= hash_get(pbr_nhg_hash
, &lookup
, NULL
);
689 DEBUGD(&pbr_dbg_nht
, "%s: %d %d", __func__
, pnhgc
->valid
,
691 if (pnhgc
->valid
&& pnhgc
->installed
)
697 struct pbr_nht_individual
{
698 struct zapi_route
*nhr
;
699 struct interface
*ifp
;
700 struct pbr_vrf
*pbr_vrf
;
701 struct pbr_nexthop_cache
*pnhc
;
710 pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache
*pnhc
,
711 struct pbr_nht_individual
*pnhi
)
713 bool is_valid
= pnhc
->valid
;
716 * If we have an interface down event, let's note that
717 * it is happening and find all the nexthops that depend
718 * on that interface. As that if we have an interface
719 * flapping fast enough it means that zebra might turn
720 * those nexthop tracking events into a no-update
721 * So let's search and do the right thing on the
724 if (!pnhi
->nhr
&& pnhi
->ifp
) {
725 switch (pnhc
->nexthop
.type
) {
726 case NEXTHOP_TYPE_BLACKHOLE
:
727 case NEXTHOP_TYPE_IPV4
:
728 case NEXTHOP_TYPE_IPV6
:
730 case NEXTHOP_TYPE_IFINDEX
:
731 case NEXTHOP_TYPE_IPV4_IFINDEX
:
732 case NEXTHOP_TYPE_IPV6_IFINDEX
:
733 if (pnhc
->nexthop
.ifindex
== pnhi
->ifp
->ifindex
)
734 is_valid
= if_is_up(pnhi
->ifp
);
742 switch (pnhi
->nhr
->prefix
.family
) {
744 if (pnhc
->nexthop
.gate
.ipv4
.s_addr
745 != pnhi
->nhr
->prefix
.u
.prefix4
.s_addr
)
746 goto done
; /* Unrelated change */
749 if (memcmp(&pnhc
->nexthop
.gate
.ipv6
,
750 &pnhi
->nhr
->prefix
.u
.prefix6
, 16)
752 goto done
; /* Unrelated change */
757 pnhi
->nhr_matched
= true;
758 if (!pnhi
->nhr
->nexthop_num
) {
763 if (pnhc
->nexthop
.type
== NEXTHOP_TYPE_IPV4_IFINDEX
764 || pnhc
->nexthop
.type
== NEXTHOP_TYPE_IPV6_IFINDEX
) {
766 /* GATEWAY_IFINDEX type shouldn't resolve to group */
767 if (pnhi
->nhr
->nexthop_num
> 1) {
772 /* If whatever we resolved to wasn't on the interface we
773 * specified. (i.e. not a connected route), its invalid.
775 if (pnhi
->nhr
->nexthops
[0].ifindex
!= pnhc
->nexthop
.ifindex
) {
784 pnhc
->valid
= is_valid
;
790 pbr_nht_individual_nexthop_interface_update(struct pbr_nexthop_cache
*pnhc
,
791 struct pbr_nht_individual
*pnhi
)
793 bool is_valid
= pnhc
->valid
;
795 if (!pnhi
->ifp
) /* It doesn't care about non-interface updates */
798 if (pnhc
->nexthop
.ifindex
799 != pnhi
->ifp
->ifindex
) /* Un-related interface */
802 pnhi
->nhr_matched
= true;
803 is_valid
= !!if_is_up(pnhi
->ifp
);
806 pnhc
->valid
= is_valid
;
811 /* Given this update either from interface or nexthop tracking, re-validate this
814 * If the update is un-related, the subroutines shoud just return their cached
817 static void pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache
*pnhc
,
818 struct pbr_nht_individual
*pnhi
)
820 assert(pnhi
->nhr
|| pnhi
->ifp
); /* Either nexthop or interface update */
822 switch (pnhc
->nexthop
.type
) {
823 case NEXTHOP_TYPE_IFINDEX
:
824 pbr_nht_individual_nexthop_interface_update(pnhc
, pnhi
);
826 case NEXTHOP_TYPE_IPV6_IFINDEX
:
827 if (IN6_IS_ADDR_LINKLOCAL(&pnhc
->nexthop
.gate
.ipv6
)) {
828 pbr_nht_individual_nexthop_interface_update(pnhc
, pnhi
);
831 /* Intentional fall thru */
832 case NEXTHOP_TYPE_IPV4_IFINDEX
:
833 case NEXTHOP_TYPE_IPV4
:
834 case NEXTHOP_TYPE_IPV6
:
835 pbr_nht_individual_nexthop_gw_update(pnhc
, pnhi
);
837 case NEXTHOP_TYPE_BLACKHOLE
:
843 static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket
*b
,
846 struct pbr_nexthop_cache
*pnhc
= b
->data
;
847 struct pbr_nht_individual
*pnhi
= data
;
850 old_valid
= pnhc
->valid
;
852 pbr_nht_individual_nexthop_update(pnhc
, pnhi
);
854 DEBUGD(&pbr_dbg_nht
, " Found %pFX: old: %d new: %d",
855 &pnhi
->nhr
->prefix
, old_valid
, pnhc
->valid
);
861 static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket
*b
,
864 struct pbr_nexthop_cache
*pnhc
= b
->data
;
865 struct nexthop_group
*nhg
= data
;
866 struct nexthop
*nh
= NULL
;
868 copy_nexthops(&nh
, &pnhc
->nexthop
, NULL
);
870 _nexthop_add(&nhg
->nexthop
, nh
);
874 pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group
*nhg
,
875 struct pbr_nexthop_group_cache
*pnhgc
)
877 hash_iterate(pnhgc
->nhh
, pbr_nexthop_group_cache_iterate_to_group
, nhg
);
880 static void pbr_nht_nexthop_update_lookup(struct hash_bucket
*b
, void *data
)
882 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
883 struct pbr_nht_individual pnhi
= {};
884 struct nexthop_group nhg
= {};
887 old_valid
= pnhgc
->valid
;
889 pnhi
.nhr
= (struct zapi_route
*)data
;
891 pnhi
.nhr_matched
= false;
892 hash_iterate(pnhgc
->nhh
, pbr_nht_individual_nexthop_update_lookup
,
895 if (!pnhi
.nhr_matched
)
899 * If any of the specified nexthops are valid we are valid
901 pnhgc
->valid
= !!pnhi
.valid
;
903 pbr_nexthop_group_cache_to_nexthop_group(&nhg
, pnhgc
);
906 pbr_nht_install_nexthop_group(pnhgc
, nhg
);
908 pbr_nht_uninstall_nexthop_group(pnhgc
, nhg
, 0);
910 /* Don't need copied nexthops anymore */
911 nexthops_free(nhg
.nexthop
);
913 if (old_valid
!= pnhgc
->valid
)
914 pbr_map_check_nh_group_change(pnhgc
->name
);
917 void pbr_nht_nexthop_update(struct zapi_route
*nhr
)
919 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_update_lookup
, nhr
);
922 struct nhrc_vrf_info
{
923 struct pbr_vrf
*pbr_vrf
;
928 static int pbr_nht_nhrc_vrf_change(struct hash_bucket
*b
, void *data
)
930 struct nhrc
*nhrc
= b
->data
;
931 struct nhrc_vrf_info
*nhrcvi
= data
;
933 if (nhrc
->nexthop
.vrf_id
== nhrcvi
->old_vrf_id
) {
935 return HASHWALK_ABORT
;
938 return HASHWALK_CONTINUE
;
941 static int pbr_nht_individual_nexthop_vrf_handle(struct hash_bucket
*b
,
944 struct pbr_nexthop_cache
*pnhc
= b
->data
;
945 struct pbr_nht_individual
*pnhi
= data
;
947 if (pnhc
->looked_at
== true)
948 return HASHWALK_CONTINUE
;
950 if (pnhc
->nexthop
.vrf_id
== VRF_DEFAULT
)
951 return HASHWALK_CONTINUE
;
953 if (strncmp(pnhc
->vrf_name
, pbr_vrf_name(pnhi
->pbr_vrf
),
954 sizeof(pnhc
->vrf_name
))
958 if (pnhc
->nexthop
.vrf_id
!= pbr_vrf_id(pnhi
->pbr_vrf
)) {
959 struct nhrc_vrf_info nhrcvi
;
961 memset(&nhrcvi
, 0, sizeof(nhrcvi
));
962 nhrcvi
.pbr_vrf
= pnhi
->pbr_vrf
;
963 nhrcvi
.old_vrf_id
= pnhc
->nexthop
.vrf_id
;
965 pnhi
->nhr_matched
= true;
966 pnhi
->old_vrf_id
= pnhc
->nexthop
.vrf_id
;
970 hash_walk(pbr_nhrc_hash
,
971 pbr_nht_nhrc_vrf_change
, &nhrcvi
);
973 hash_release(pbr_nhrc_hash
,
975 nhrcvi
.nhrc
->nexthop
.vrf_id
=
976 pbr_vrf_id(pnhi
->pbr_vrf
);
977 hash_get(pbr_nhrc_hash
, nhrcvi
.nhrc
,
979 pbr_send_rnh(&nhrcvi
.nhrc
->nexthop
, true);
981 } while (nhrcvi
.nhrc
);
984 pnhc
->looked_at
= true;
985 return HASHWALK_ABORT
;
988 return HASHWALK_CONTINUE
;
991 static void pbr_nht_clear_looked_at(struct hash_bucket
*b
, void *data
)
993 struct pbr_nexthop_cache
*pnhc
= b
->data
;
995 pnhc
->looked_at
= false;
998 static void pbr_nht_nexthop_vrf_handle(struct hash_bucket
*b
, void *data
)
1000 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1001 struct pbr_vrf
*pbr_vrf
= data
;
1002 struct pbr_nht_individual pnhi
= {};
1004 hash_iterate(pnhgc
->nhh
, pbr_nht_clear_looked_at
, NULL
);
1005 memset(&pnhi
, 0, sizeof(pnhi
));
1006 pnhi
.pbr_vrf
= pbr_vrf
;
1008 struct pbr_nexthop_cache
*pnhc
;
1011 hash_walk(pnhgc
->nhh
, pbr_nht_individual_nexthop_vrf_handle
,
1018 pnhc
->nexthop
.vrf_id
= pnhi
.old_vrf_id
;
1019 pnhi
.pnhc
= hash_release(pnhgc
->nhh
, pnhi
.pnhc
);
1021 pnhi
.pnhc
->nexthop
.vrf_id
= pbr_vrf_id(pbr_vrf
);
1023 hash_get(pnhgc
->nhh
, pnhi
.pnhc
, hash_alloc_intern
);
1025 pnhc
->nexthop
.vrf_id
= pbr_vrf_id(pbr_vrf
);
1027 pbr_map_check_vrf_nh_group_change(pnhgc
->name
, pbr_vrf
,
1029 } while (pnhi
.pnhc
);
1032 void pbr_nht_vrf_update(struct pbr_vrf
*pbr_vrf
)
1034 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_vrf_handle
, pbr_vrf
);
1037 static void pbr_nht_individual_nexthop_interface_handle(struct hash_bucket
*b
,
1040 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1041 struct pbr_nht_individual
*pnhi
= data
;
1043 if (pnhc
->nexthop
.ifindex
== 0)
1046 if ((strncmp(pnhc
->intf_name
, pnhi
->ifp
->name
, sizeof(pnhc
->intf_name
))
1048 && pnhc
->nexthop
.ifindex
!= pnhi
->ifp
->ifindex
)
1052 static void pbr_nht_nexthop_interface_handle(struct hash_bucket
*b
, void *data
)
1054 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1055 struct interface
*ifp
= data
;
1056 struct pbr_nht_individual pnhi
= {};
1058 uint32_t old_ifindex
;
1061 memset(&pnhi
, 0, sizeof(pnhi
));
1063 hash_iterate(pnhgc
->nhh
,
1064 pbr_nht_individual_nexthop_interface_handle
,
1070 pnhi
.pnhc
= hash_release(pnhgc
->nhh
, pnhi
.pnhc
);
1071 old_ifindex
= pnhi
.pnhc
->nexthop
.ifindex
;
1073 nhrc
= hash_lookup(pbr_nhrc_hash
, &pnhi
.pnhc
->nexthop
);
1075 hash_release(pbr_nhrc_hash
, nhrc
);
1076 nhrc
->nexthop
.ifindex
= ifp
->ifindex
;
1077 hash_get(pbr_nhrc_hash
, nhrc
, hash_alloc_intern
);
1079 pnhi
.pnhc
->nexthop
.ifindex
= ifp
->ifindex
;
1081 hash_get(pnhgc
->nhh
, pnhi
.pnhc
, hash_alloc_intern
);
1083 pbr_map_check_interface_nh_group_change(pnhgc
->name
, ifp
,
1085 } while (pnhi
.pnhc
);
1088 void pbr_nht_interface_update(struct interface
*ifp
)
1090 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_interface_handle
, ifp
);
1094 pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket
*b
,
1097 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1098 struct pbr_nht_individual
*pnhi
= data
;
1101 old_valid
= pnhc
->valid
;
1103 pbr_nht_individual_nexthop_update(pnhc
, pnhi
);
1105 DEBUGD(&pbr_dbg_nht
, " Found %s: old: %d new: %d", pnhi
->ifp
->name
,
1106 old_valid
, pnhc
->valid
);
1112 static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket
*b
,
1115 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1116 struct pbr_nht_individual pnhi
= {};
1117 struct nexthop_group nhg
= {};
1120 old_valid
= pnhgc
->valid
;
1124 hash_iterate(pnhgc
->nhh
,
1125 pbr_nht_individual_nexthop_interface_update_lookup
, &pnhi
);
1128 * If any of the specified nexthops are valid we are valid
1130 pnhgc
->valid
= pnhi
.valid
;
1132 pbr_nexthop_group_cache_to_nexthop_group(&nhg
, pnhgc
);
1135 pbr_nht_install_nexthop_group(pnhgc
, nhg
);
1137 pbr_nht_uninstall_nexthop_group(pnhgc
, nhg
, 0);
1139 nexthops_free(nhg
.nexthop
);
1141 if (old_valid
!= pnhgc
->valid
)
1142 pbr_map_check_nh_group_change(pnhgc
->name
);
1145 void pbr_nht_nexthop_interface_update(struct interface
*ifp
)
1147 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_interface_update_lookup
,
1151 static uint32_t pbr_nhg_hash_key(const void *arg
)
1153 const struct pbr_nexthop_group_cache
*nhgc
= arg
;
1155 return jhash(&nhgc
->name
, strlen(nhgc
->name
), 0x52c34a96);
1158 static bool pbr_nhg_hash_equal(const void *arg1
, const void *arg2
)
1160 const struct pbr_nexthop_group_cache
*nhgc1
=
1161 (const struct pbr_nexthop_group_cache
*)arg1
;
1162 const struct pbr_nexthop_group_cache
*nhgc2
=
1163 (const struct pbr_nexthop_group_cache
*)arg2
;
1165 return !strcmp(nhgc1
->name
, nhgc2
->name
);
1168 uint32_t pbr_nht_get_next_tableid(bool peek
)
1173 for (i
= pbr_nhg_low_table
; i
<= pbr_nhg_high_table
; i
++) {
1174 if (!nhg_tableid
[i
]) {
1181 nhg_tableid
[i
] = !peek
;
1187 void pbr_nht_set_tableid_range(uint32_t low
, uint32_t high
)
1189 pbr_nhg_low_table
= low
;
1190 pbr_nhg_high_table
= high
;
1193 void pbr_nht_write_table_range(struct vty
*vty
)
1195 if (pbr_nhg_low_table
!= PBR_NHT_DEFAULT_LOW_TABLEID
1196 || pbr_nhg_high_table
!= PBR_NHT_DEFAULT_HIGH_TABLEID
) {
1197 vty_out(vty
, "pbr table range %u %u\n", pbr_nhg_low_table
,
1198 pbr_nhg_high_table
);
1202 uint32_t pbr_nht_get_next_rule(uint32_t seqno
)
1204 return seqno
+ pbr_nhg_low_rule
- 1;
1206 void pbr_nht_set_rule_range(uint32_t low
, uint32_t high
)
1208 pbr_nhg_low_rule
= low
;
1209 pbr_nhg_high_rule
= high
;
1212 void pbr_nht_write_rule_range(struct vty
*vty
)
1214 if (pbr_nhg_low_rule
!= PBR_NHT_DEFAULT_LOW_RULE
1215 || pbr_nhg_high_rule
!= PBR_NHT_DEFAULT_HIGH_RULE
) {
1216 vty_out(vty
, "pbr rule range %u %u\n", pbr_nhg_low_rule
,
1221 uint32_t pbr_nht_get_table(const char *name
)
1223 struct pbr_nexthop_group_cache find
;
1224 struct pbr_nexthop_group_cache
*pnhgc
;
1226 memset(&find
, 0, sizeof(find
));
1227 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
1228 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
1231 DEBUGD(&pbr_dbg_nht
,
1232 "%s: Could not find nexthop-group cache w/ name '%s'",
1237 return pnhgc
->table_id
;
1240 bool pbr_nht_get_installed(const char *name
)
1242 struct pbr_nexthop_group_cache find
;
1243 struct pbr_nexthop_group_cache
*pnhgc
;
1245 memset(&find
, 0, sizeof(find
));
1246 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
1248 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
1253 return pnhgc
->installed
;
1256 static void pbr_nht_show_nhg_nexthops(struct hash_bucket
*b
, void *data
)
1258 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1259 struct vty
*vty
= data
;
1261 vty_out(vty
, "\tValid: %d ", pnhc
->valid
);
1262 nexthop_group_write_nexthop(vty
, &pnhc
->nexthop
);
1265 static void pbr_nht_json_nhg_nexthops(struct hash_bucket
*b
, void *data
)
1267 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1268 json_object
*all_hops
= data
;
1269 json_object
*this_hop
;
1271 this_hop
= json_object_new_object();
1272 nexthop_group_json_nexthop(this_hop
, &pnhc
->nexthop
);
1273 json_object_boolean_add(this_hop
, "valid", pnhc
->valid
);
1275 json_object_array_add(all_hops
, this_hop
);
1278 struct pbr_nht_show
{
1284 static void pbr_nht_show_nhg(struct hash_bucket
*b
, void *data
)
1286 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1287 struct pbr_nht_show
*pns
= data
;
1290 if (pns
->name
&& strcmp(pns
->name
, pnhgc
->name
) != 0)
1294 vty_out(vty
, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
1295 pnhgc
->name
, pnhgc
->table_id
, pnhgc
->valid
, pnhgc
->installed
);
1297 hash_iterate(pnhgc
->nhh
, pbr_nht_show_nhg_nexthops
, vty
);
1300 static void pbr_nht_json_nhg(struct hash_bucket
*b
, void *data
)
1302 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1303 struct pbr_nht_show
*pns
= data
;
1304 json_object
*j
, *this_group
, *group_hops
;
1306 if (pns
->name
&& strcmp(pns
->name
, pnhgc
->name
) != 0)
1310 this_group
= json_object_new_object();
1312 if (!j
|| !this_group
)
1315 json_object_int_add(this_group
, "id", pnhgc
->table_id
);
1316 json_object_string_add(this_group
, "name", pnhgc
->name
);
1317 json_object_boolean_add(this_group
, "valid", pnhgc
->valid
);
1318 json_object_boolean_add(this_group
, "installed", pnhgc
->installed
);
1320 group_hops
= json_object_new_array();
1323 hash_iterate(pnhgc
->nhh
, pbr_nht_json_nhg_nexthops
, group_hops
);
1324 json_object_object_add(this_group
, "nexthops", group_hops
);
1327 json_object_array_add(j
, this_group
);
1330 void pbr_nht_show_nexthop_group(struct vty
*vty
, const char *name
)
1332 struct pbr_nht_show pns
;
1337 hash_iterate(pbr_nhg_hash
, pbr_nht_show_nhg
, &pns
);
1340 void pbr_nht_json_nexthop_group(json_object
*j
, const char *name
)
1342 struct pbr_nht_show pns
;
1347 hash_iterate(pbr_nhg_hash
, pbr_nht_json_nhg
, &pns
);
1350 void pbr_nht_init(void)
1352 pbr_nhg_hash
= hash_create_size(
1353 16, pbr_nhg_hash_key
, pbr_nhg_hash_equal
, "PBR NHG Cache Hash");
1355 hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash
,
1356 pbr_nhrc_hash_equal
, "PBR NH Hash");
1358 pbr_nhg_low_table
= PBR_NHT_DEFAULT_LOW_TABLEID
;
1359 pbr_nhg_high_table
= PBR_NHT_DEFAULT_HIGH_TABLEID
;
1360 pbr_nhg_low_rule
= PBR_NHT_DEFAULT_LOW_RULE
;
1361 pbr_nhg_high_rule
= PBR_NHT_DEFAULT_HIGH_RULE
;
1362 memset(&nhg_tableid
, 0, 65535 * sizeof(uint8_t));