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
;
42 static struct hash
*pbr_nhg_allocated_id_hash
;
44 static uint32_t pbr_nhg_low_table
;
45 static uint32_t pbr_nhg_high_table
;
46 static uint32_t pbr_next_unallocated_table_id
;
47 static uint32_t pbr_nhg_low_rule
;
48 static uint32_t pbr_nhg_high_rule
;
50 static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
51 struct nexthop_group nhg
);
53 pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
54 struct nexthop_group nhg
,
55 enum nexthop_types_t nh_type
);
61 struct nexthop nexthop
;
62 unsigned int refcount
;
65 /* Hash functions for pbr_nhrc_hash ---------------------------------------- */
67 static void *pbr_nhrc_hash_alloc(void *p
)
69 struct nhrc
*nhrc
= XCALLOC(MTYPE_PBR_NHG
, sizeof(struct nhrc
));
70 nhrc
->nexthop
= *(struct nexthop
*)p
;
71 nhrc
->nexthop
.next
= NULL
;
72 nhrc
->nexthop
.prev
= NULL
;
76 static bool pbr_nhrc_hash_equal(const void *arg1
, const void *arg2
)
78 const struct nexthop
*nh1
, *nh2
;
83 return nexthop_same(nh1
, nh2
);
86 /* ------------------------------------------------------------------------- */
88 static void *pbr_nh_alloc(void *p
)
90 struct pbr_nexthop_cache
*new;
91 struct pbr_nexthop_cache
*pnhc
= (struct pbr_nexthop_cache
*)p
;
94 new = XCALLOC(MTYPE_PBR_NHG
, sizeof(*new));
95 nhrc
= hash_get(pbr_nhrc_hash
, &pnhc
->nexthop
, pbr_nhrc_hash_alloc
);
96 new->nexthop
= nhrc
->nexthop
;
98 /* Decremented again in pbr_nh_delete */
101 DEBUGD(&pbr_dbg_nht
, "%s: Sending nexthop to Zebra", __func__
);
103 pbr_send_rnh(&new->nexthop
, true);
109 static void pbr_nh_delete(struct pbr_nexthop_cache
**pnhc
)
113 nhrc
= hash_lookup(pbr_nhrc_hash
, &((*pnhc
)->nexthop
));
117 if (!nhrc
|| nhrc
->refcount
== 0) {
118 DEBUGD(&pbr_dbg_nht
, "%s: Removing nexthop from Zebra",
120 pbr_send_rnh(&((*pnhc
)->nexthop
), false);
122 if (nhrc
&& nhrc
->refcount
== 0) {
123 hash_release(pbr_nhrc_hash
, nhrc
);
124 XFREE(MTYPE_PBR_NHG
, nhrc
);
127 XFREE(MTYPE_PBR_NHG
, *pnhc
);
130 static void pbr_nh_delete_iterate(struct hash_bucket
*b
, void *p
)
132 pbr_nh_delete((struct pbr_nexthop_cache
**)&b
->data
);
135 static uint32_t pbr_nh_hash_key(const void *arg
)
138 const struct pbr_nexthop_cache
*pbrnc
= arg
;
140 key
= nexthop_hash(&pbrnc
->nexthop
);
145 static bool pbr_nh_hash_equal(const void *arg1
, const void *arg2
)
147 const struct pbr_nexthop_cache
*pbrnc1
=
148 (const struct pbr_nexthop_cache
*)arg1
;
149 const struct pbr_nexthop_cache
*pbrnc2
=
150 (const struct pbr_nexthop_cache
*)arg2
;
152 if (pbrnc1
->nexthop
.vrf_id
!= pbrnc2
->nexthop
.vrf_id
)
155 if (pbrnc1
->nexthop
.ifindex
!= pbrnc2
->nexthop
.ifindex
)
158 if (pbrnc1
->nexthop
.type
!= pbrnc2
->nexthop
.type
)
161 switch (pbrnc1
->nexthop
.type
) {
162 case NEXTHOP_TYPE_IFINDEX
:
163 return pbrnc1
->nexthop
.ifindex
== pbrnc2
->nexthop
.ifindex
;
164 case NEXTHOP_TYPE_IPV4_IFINDEX
:
165 case NEXTHOP_TYPE_IPV4
:
166 return pbrnc1
->nexthop
.gate
.ipv4
.s_addr
167 == pbrnc2
->nexthop
.gate
.ipv4
.s_addr
;
168 case NEXTHOP_TYPE_IPV6_IFINDEX
:
169 case NEXTHOP_TYPE_IPV6
:
170 return !memcmp(&pbrnc1
->nexthop
.gate
.ipv6
,
171 &pbrnc2
->nexthop
.gate
.ipv6
, 16);
172 case NEXTHOP_TYPE_BLACKHOLE
:
173 return pbrnc1
->nexthop
.bh_type
== pbrnc2
->nexthop
.bh_type
;
177 * We should not get here
182 static void pbr_nhgc_delete(struct pbr_nexthop_group_cache
*p
)
184 hash_iterate(p
->nhh
, pbr_nh_delete_iterate
, NULL
);
186 XFREE(MTYPE_PBR_NHG
, p
);
189 static void *pbr_nhgc_alloc(void *p
)
191 struct pbr_nexthop_group_cache
*new;
192 struct pbr_nexthop_group_cache
*pnhgc
=
193 (struct pbr_nexthop_group_cache
*)p
;
195 new = XCALLOC(MTYPE_PBR_NHG
, sizeof(*new));
197 strlcpy(new->name
, pnhgc
->name
, sizeof(pnhgc
->name
));
198 pbr_nht_reserve_next_table_id(new);
200 DEBUGD(&pbr_dbg_nht
, "%s: NHT: %s assigned Table ID: %u", __func__
,
201 new->name
, new->table_id
);
203 new->nhh
= hash_create_size(8, pbr_nh_hash_key
, pbr_nh_hash_equal
,
204 "PBR NH Cache Hash");
209 void pbr_nhgroup_add_cb(const char *name
)
211 struct pbr_nexthop_group_cache
*pnhgc
;
212 struct nexthop_group_cmd
*nhgc
;
214 nhgc
= nhgc_find(name
);
217 DEBUGD(&pbr_dbg_nht
, "%s: Could not find nhgc with name: %s",
222 pnhgc
= pbr_nht_add_group(name
);
227 DEBUGD(&pbr_dbg_nht
, "%s: Added nexthop-group %s", __func__
, name
);
229 pbr_map_check_nh_group_change(name
);
232 void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd
*nhgc
,
233 const struct nexthop
*nhop
)
236 struct pbr_nexthop_group_cache pnhgc_find
= {};
237 struct pbr_nexthop_group_cache
*pnhgc
;
238 struct pbr_nexthop_cache pnhc_find
= {};
239 struct pbr_nexthop_cache
*pnhc
;
241 /* find pnhgc by name */
242 strlcpy(pnhgc_find
.name
, nhgc
->name
, sizeof(pnhgc_find
.name
));
243 pnhgc
= hash_lookup(pbr_nhg_hash
, &pnhgc_find
);
246 /* Check if configured table range is exhausted */
247 if (!pbr_nht_has_unallocated_table()) {
249 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
250 __func__
, nhgc
->name
);
254 /* No nhgc but range not exhausted? Then alloc it */
255 pnhgc
= hash_get(pbr_nhg_hash
, &pnhgc_find
, pbr_nhgc_alloc
);
258 /* create & insert new pnhc into pnhgc->nhh */
259 pnhc_find
.nexthop
= *nhop
;
260 pnhc
= hash_get(pnhgc
->nhh
, &pnhc_find
, pbr_nh_alloc
);
262 /* set parent pnhgc */
263 pnhc
->parent
= pnhgc
;
265 if (DEBUG_MODE_CHECK(&pbr_dbg_nht
, DEBUG_MODE_ALL
)) {
266 nexthop2str(nhop
, debugstr
, sizeof(debugstr
));
267 DEBUGD(&pbr_dbg_nht
, "%s: Added %s to nexthop-group %s",
268 __func__
, debugstr
, nhgc
->name
);
271 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
272 pbr_map_check_nh_group_change(nhgc
->name
);
274 if (nhop
->type
== NEXTHOP_TYPE_IFINDEX
275 || (nhop
->type
== NEXTHOP_TYPE_IPV6_IFINDEX
276 && IN6_IS_ADDR_LINKLOCAL(&nhop
->gate
.ipv6
))) {
277 struct interface
*ifp
;
279 ifp
= if_lookup_by_index(nhop
->ifindex
, nhop
->vrf_id
);
281 pbr_nht_nexthop_interface_update(ifp
);
285 void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd
*nhgc
,
286 const struct nexthop
*nhop
)
289 struct pbr_nexthop_group_cache pnhgc_find
= {};
290 struct pbr_nexthop_group_cache
*pnhgc
;
291 struct pbr_nexthop_cache pnhc_find
= {};
292 struct pbr_nexthop_cache
*pnhc
;
293 enum nexthop_types_t nh_type
= nhop
->type
;
295 /* find pnhgc by name */
296 strlcpy(pnhgc_find
.name
, nhgc
->name
, sizeof(pnhgc_find
.name
));
297 pnhgc
= hash_lookup(pbr_nhg_hash
, &pnhgc_find
);
300 * Ignore deletions of nhg we did not / could not allocate nhgc for
301 * Occurs when PBR table range is full but new nhg keep coming in
306 /* delete pnhc from pnhgc->nhh */
307 pnhc_find
.nexthop
= *nhop
;
308 pnhc
= hash_release(pnhgc
->nhh
, &pnhc_find
);
311 pbr_nh_delete(&pnhc
);
313 if (DEBUG_MODE_CHECK(&pbr_dbg_nht
, DEBUG_MODE_ALL
)) {
314 nexthop2str(nhop
, debugstr
, sizeof(debugstr
));
315 DEBUGD(&pbr_dbg_nht
, "%s: Removed %s from nexthop-group %s",
316 __func__
, debugstr
, nhgc
->name
);
319 if (pnhgc
->nhh
->count
)
320 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
322 pbr_nht_uninstall_nexthop_group(pnhgc
, nhgc
->nhg
, nh_type
);
324 pbr_map_check_nh_group_change(nhgc
->name
);
327 void pbr_nhgroup_delete_cb(const char *name
)
329 DEBUGD(&pbr_dbg_nht
, "%s: Removed nexthop-group %s", __func__
, name
);
331 /* delete group from all pbrms's */
332 pbr_nht_delete_group(name
);
334 pbr_map_check_nh_group_change(name
);
338 pbr_nht_find_nhg_from_table_update(struct pbr_nexthop_group_cache
*pnhgc
,
339 uint32_t table_id
, bool installed
)
341 if (pnhgc
->table_id
== table_id
) {
342 DEBUGD(&pbr_dbg_nht
, "%s: %s: Table ID (%u) matches %s",
343 __func__
, (installed
? "install" : "remove"), table_id
,
346 pnhgc
->installed
= installed
;
347 pnhgc
->valid
= installed
;
348 pbr_map_schedule_policy_from_nhg(pnhgc
->name
, pnhgc
->installed
);
352 static void pbr_nht_find_nhg_from_table_install(struct hash_bucket
*b
,
355 struct pbr_nexthop_group_cache
*pnhgc
=
356 (struct pbr_nexthop_group_cache
*)b
->data
;
357 uint32_t table_id
= *(uint32_t *)data
;
359 pbr_nht_find_nhg_from_table_update(pnhgc
, table_id
, true);
362 void pbr_nht_route_installed_for_table(uint32_t table_id
)
364 hash_iterate(pbr_nhg_hash
, pbr_nht_find_nhg_from_table_install
,
368 static void pbr_nht_find_nhg_from_table_remove(struct hash_bucket
*b
,
371 struct pbr_nexthop_group_cache
*pnhgc
=
372 (struct pbr_nexthop_group_cache
*)b
->data
;
373 uint32_t table_id
= *(uint32_t *)data
;
375 pbr_nht_find_nhg_from_table_update(pnhgc
, table_id
, false);
378 void pbr_nht_route_removed_for_table(uint32_t table_id
)
380 hash_iterate(pbr_nhg_hash
, pbr_nht_find_nhg_from_table_remove
,
385 * Loop through all nexthops in a nexthop group to check that they are all the
386 * same. If they are not all the same, log this peculiarity.
389 * The nexthop group to check
392 * - AFI of last nexthop in the group
395 static afi_t
pbr_nht_which_afi(struct nexthop_group nhg
,
396 enum nexthop_types_t nh_type
)
398 struct nexthop
*nexthop
;
399 afi_t install_afi
= AFI_MAX
;
404 case NEXTHOP_TYPE_IPV4
:
405 case NEXTHOP_TYPE_IPV4_IFINDEX
:
407 case NEXTHOP_TYPE_IPV6
:
408 case NEXTHOP_TYPE_IPV6_IFINDEX
:
410 case NEXTHOP_TYPE_IFINDEX
:
411 case NEXTHOP_TYPE_BLACKHOLE
:
416 v6
= v4
= bh
= false;
418 for (ALL_NEXTHOPS(nhg
, nexthop
)) {
419 nh_type
= nexthop
->type
;
422 case NEXTHOP_TYPE_IFINDEX
:
424 case NEXTHOP_TYPE_IPV4
:
425 case NEXTHOP_TYPE_IPV4_IFINDEX
:
427 install_afi
= AFI_IP
;
429 case NEXTHOP_TYPE_IPV6
:
430 case NEXTHOP_TYPE_IPV6_IFINDEX
:
432 install_afi
= AFI_IP6
;
434 case NEXTHOP_TYPE_BLACKHOLE
:
440 /* Interface and/or blackhole nexthops only. */
442 install_afi
= AFI_MAX
;
446 "%s: Saw both V6 and V4 nexthops...using %s", __func__
,
447 afi2str(install_afi
));
448 if (bh
&& (v6
|| v4
))
450 "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.",
451 __func__
, v4
? "v4" : "", (v4
&& v6
) ? " and " : "",
457 static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
458 struct nexthop_group nhg
)
461 enum nexthop_types_t nh_type
= 0;
463 install_afi
= pbr_nht_which_afi(nhg
, nh_type
);
465 route_add(pnhgc
, nhg
, install_afi
);
469 pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
470 struct nexthop_group nhg
,
471 enum nexthop_types_t nh_type
)
475 install_afi
= pbr_nht_which_afi(nhg
, nh_type
);
477 pnhgc
->installed
= false;
478 pnhgc
->valid
= false;
479 route_delete(pnhgc
, install_afi
);
482 void pbr_nht_change_group(const char *name
)
484 struct nexthop_group_cmd
*nhgc
;
485 struct pbr_nexthop_group_cache
*pnhgc
;
486 struct pbr_nexthop_group_cache find
;
487 struct nexthop
*nhop
;
489 nhgc
= nhgc_find(name
);
493 memset(&find
, 0, sizeof(find
));
494 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
495 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
499 "%s: Could not find nexthop-group cache w/ name '%s'",
504 for (ALL_NEXTHOPS(nhgc
->nhg
, nhop
)) {
505 struct pbr_nexthop_cache lookup
;
506 struct pbr_nexthop_cache
*pnhc
;
508 lookup
.nexthop
= *nhop
;
509 pnhc
= hash_lookup(pnhgc
->nhh
, &lookup
);
511 pnhc
= hash_get(pnhgc
->nhh
, &lookup
, pbr_nh_alloc
);
512 pnhc
->parent
= pnhgc
;
515 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
518 char *pbr_nht_nexthop_make_name(char *name
, size_t l
,
519 uint32_t seqno
, char *buffer
)
521 snprintf(buffer
, l
, "%s%u", name
, seqno
);
525 /* Set data derived from nhg in pbrms */
526 void pbr_nht_set_seq_nhg_data(struct pbr_map_sequence
*pbrms
,
527 const struct nexthop_group_cmd
*nhgc
)
529 const struct nexthop_group
*nhg
;
538 switch (nhg
->nexthop
->type
) {
539 case NEXTHOP_TYPE_IPV6
:
540 case NEXTHOP_TYPE_IPV6_IFINDEX
:
541 pbrms
->family
= AF_INET6
;
543 case NEXTHOP_TYPE_IPV4
:
544 case NEXTHOP_TYPE_IPV4_IFINDEX
:
545 pbrms
->family
= AF_INET
;
551 /* Configure a routemap sequence to use a given nexthop group */
552 void pbr_nht_set_seq_nhg(struct pbr_map_sequence
*pbrms
, const char *name
)
554 struct nexthop_group_cmd
*nhgc
;
559 pbrms
->nhgrp_name
= XSTRDUP(MTYPE_TMP
, name
);
561 nhgc
= nhgc_find(name
);
565 pbr_nht_set_seq_nhg_data(pbrms
, nhgc
);
568 void pbr_nht_add_individual_nexthop(struct pbr_map_sequence
*pbrms
,
569 const struct nexthop
*nhop
)
571 struct pbr_nexthop_group_cache
*pnhgc
;
572 struct pbr_nexthop_group_cache find
;
573 struct pbr_nexthop_cache
*pnhc
;
574 struct pbr_nexthop_cache lookup
;
576 char buf
[PBR_NHC_NAMELEN
];
578 pbrms
->nhg
= nexthop_group_new();
579 pbrms
->internal_nhg_name
= XSTRDUP(
581 pbr_nht_nexthop_make_name(pbrms
->parent
->name
, PBR_NHC_NAMELEN
,
585 memcpy(nh
, nhop
, sizeof(*nh
));
587 nexthop_group_add_sorted(pbrms
->nhg
, nh
);
589 memset(&find
, 0, sizeof(find
));
590 pbr_nht_nexthop_make_name(pbrms
->parent
->name
, PBR_NHC_NAMELEN
,
591 pbrms
->seqno
, find
.name
);
593 if (!pbr_nht_has_unallocated_table()) {
595 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
596 __func__
, find
.name
);
600 if (!pbrms
->internal_nhg_name
)
601 pbrms
->internal_nhg_name
= XSTRDUP(MTYPE_TMP
, find
.name
);
603 pnhgc
= hash_get(pbr_nhg_hash
, &find
, pbr_nhgc_alloc
);
605 lookup
.nexthop
= *pbrms
->nhg
->nexthop
;
606 pnhc
= hash_get(pnhgc
->nhh
, &lookup
, pbr_nh_alloc
);
607 pnhc
->parent
= pnhgc
;
608 if (nhop
->vrf_id
!= VRF_DEFAULT
) {
609 struct vrf
*vrf
= vrf_lookup_by_id(nhop
->vrf_id
);
612 strlcpy(pnhc
->vrf_name
, vrf
->name
,
613 sizeof(pnhc
->vrf_name
));
616 if (nhop
->ifindex
!= 0) {
617 struct interface
*ifp
=
618 if_lookup_by_index(nhop
->ifindex
, nhop
->vrf_id
);
621 strlcpy(pnhc
->intf_name
, ifp
->name
,
622 sizeof(pnhc
->intf_name
));
624 pbr_nht_install_nexthop_group(pnhgc
, *pbrms
->nhg
);
627 static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence
*pbrms
)
629 struct pbr_nexthop_group_cache
*pnhgc
;
630 struct pbr_nexthop_group_cache find
;
631 struct pbr_nexthop_cache
*pnhc
;
632 struct pbr_nexthop_cache lup
;
634 enum nexthop_types_t nh_type
= 0;
636 memset(&find
, 0, sizeof(find
));
637 snprintf(find
.name
, sizeof(find
.name
), "%s", pbrms
->internal_nhg_name
);
638 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
640 nh
= pbrms
->nhg
->nexthop
;
643 pnhc
= hash_lookup(pnhgc
->nhh
, &lup
);
645 hash_release(pnhgc
->nhh
, pnhc
);
646 pbr_nh_delete(&pnhc
);
647 pbr_nht_uninstall_nexthop_group(pnhgc
, *pbrms
->nhg
, nh_type
);
649 hash_release(pbr_nhg_hash
, pnhgc
);
650 pbr_nhgc_delete(pnhgc
);
652 nexthop_group_delete(&pbrms
->nhg
);
653 XFREE(MTYPE_TMP
, pbrms
->internal_nhg_name
);
656 void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence
*pbrms
)
658 pbr_map_delete_nexthops(pbrms
);
660 pbr_nht_release_individual_nexthop(pbrms
);
663 struct pbr_nexthop_group_cache
*pbr_nht_add_group(const char *name
)
665 struct nexthop
*nhop
;
666 struct nexthop_group_cmd
*nhgc
;
667 struct pbr_nexthop_group_cache
*pnhgc
;
668 struct pbr_nexthop_group_cache lookup
;
670 if (!pbr_nht_has_unallocated_table()) {
672 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
677 nhgc
= nhgc_find(name
);
680 DEBUGD(&pbr_dbg_nht
, "%s: Could not find nhgc with name: %s",
685 snprintf(lookup
.name
, sizeof(lookup
.name
), "%s", name
);
686 pnhgc
= hash_get(pbr_nhg_hash
, &lookup
, pbr_nhgc_alloc
);
687 DEBUGD(&pbr_dbg_nht
, "%s: Retrieved NHGC @ %p", __func__
, pnhgc
);
689 for (ALL_NEXTHOPS(nhgc
->nhg
, nhop
)) {
690 struct pbr_nexthop_cache lookupc
;
691 struct pbr_nexthop_cache
*pnhc
;
693 lookupc
.nexthop
= *nhop
;
694 pnhc
= hash_lookup(pnhgc
->nhh
, &lookupc
);
696 pnhc
= hash_get(pnhgc
->nhh
, &lookupc
, pbr_nh_alloc
);
697 pnhc
->parent
= pnhgc
;
704 void pbr_nht_delete_group(const char *name
)
706 struct pbr_map_sequence
*pbrms
;
707 struct listnode
*snode
;
708 struct pbr_map
*pbrm
;
709 struct pbr_nexthop_group_cache pnhgc_find
;
710 struct pbr_nexthop_group_cache
*pnhgc
;
712 RB_FOREACH (pbrm
, pbr_map_entry_head
, &pbr_maps
) {
713 for (ALL_LIST_ELEMENTS_RO(pbrm
->seqnumbers
, snode
, pbrms
)) {
714 if (pbrms
->nhgrp_name
715 && strmatch(pbrms
->nhgrp_name
, name
)) {
716 pbrms
->reason
|= PBR_MAP_INVALID_NO_NEXTHOPS
;
718 pbrms
->internal_nhg_name
= NULL
;
724 strlcpy(pnhgc_find
.name
, name
, sizeof(pnhgc_find
.name
));
725 pnhgc
= hash_release(pbr_nhg_hash
, &pnhgc_find
);
728 * Ignore deletions of nh we did not / could not allocate nhgc for
729 * Occurs when PBR table range is full but new nhg keep coming in
734 /* Remove and recalculate the next table id */
735 hash_release(pbr_nhg_allocated_id_hash
, pnhgc
);
736 pbr_nht_update_next_unallocated_table_id();
738 pbr_nhgc_delete(pnhgc
);
741 bool pbr_nht_nexthop_valid(struct nexthop_group
*nhg
)
743 DEBUGD(&pbr_dbg_nht
, "%s: %p", __func__
, nhg
);
747 bool pbr_nht_nexthop_group_valid(const char *name
)
749 struct pbr_nexthop_group_cache
*pnhgc
;
750 struct pbr_nexthop_group_cache lookup
;
752 DEBUGD(&pbr_dbg_nht
, "%s: %s", __func__
, name
);
754 snprintf(lookup
.name
, sizeof(lookup
.name
), "%s", name
);
755 pnhgc
= hash_get(pbr_nhg_hash
, &lookup
, NULL
);
758 DEBUGD(&pbr_dbg_nht
, "%s: %d %d", __func__
, pnhgc
->valid
,
760 if (pnhgc
->valid
&& pnhgc
->installed
)
766 struct pbr_nht_individual
{
767 struct zapi_route
*nhr
;
768 struct interface
*ifp
;
769 struct pbr_vrf
*pbr_vrf
;
770 struct pbr_nexthop_cache
*pnhc
;
779 pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache
*pnhc
,
780 struct pbr_nht_individual
*pnhi
)
782 bool is_valid
= pnhc
->valid
;
785 * If we have an interface down event, let's note that
786 * it is happening and find all the nexthops that depend
787 * on that interface. As that if we have an interface
788 * flapping fast enough it means that zebra might turn
789 * those nexthop tracking events into a no-update
790 * So let's search and do the right thing on the
794 switch (pnhc
->nexthop
.type
) {
795 case NEXTHOP_TYPE_BLACKHOLE
:
796 case NEXTHOP_TYPE_IPV4
:
797 case NEXTHOP_TYPE_IPV6
:
799 case NEXTHOP_TYPE_IFINDEX
:
800 case NEXTHOP_TYPE_IPV4_IFINDEX
:
801 case NEXTHOP_TYPE_IPV6_IFINDEX
:
802 if (pnhc
->nexthop
.ifindex
== pnhi
->ifp
->ifindex
)
803 is_valid
= if_is_up(pnhi
->ifp
);
810 switch (pnhi
->nhr
->prefix
.family
) {
812 if (pnhc
->nexthop
.gate
.ipv4
.s_addr
813 != pnhi
->nhr
->prefix
.u
.prefix4
.s_addr
)
814 goto done
; /* Unrelated change */
817 if (memcmp(&pnhc
->nexthop
.gate
.ipv6
,
818 &pnhi
->nhr
->prefix
.u
.prefix6
, 16)
820 goto done
; /* Unrelated change */
824 pnhi
->nhr_matched
= true;
825 if (!pnhi
->nhr
->nexthop_num
) {
830 if (pnhc
->nexthop
.type
== NEXTHOP_TYPE_IPV4_IFINDEX
831 || pnhc
->nexthop
.type
== NEXTHOP_TYPE_IPV6_IFINDEX
) {
833 /* GATEWAY_IFINDEX type shouldn't resolve to group */
834 if (pnhi
->nhr
->nexthop_num
> 1) {
839 /* If whatever we resolved to wasn't on the interface we
840 * specified. (i.e. not a connected route), its invalid.
842 if (pnhi
->nhr
->nexthops
[0].ifindex
!= pnhc
->nexthop
.ifindex
) {
851 pnhc
->valid
= is_valid
;
857 pbr_nht_individual_nexthop_interface_update(struct pbr_nexthop_cache
*pnhc
,
858 struct pbr_nht_individual
*pnhi
)
860 bool is_valid
= pnhc
->valid
;
862 if (!pnhi
->ifp
) /* It doesn't care about non-interface updates */
865 if (pnhc
->nexthop
.ifindex
866 != pnhi
->ifp
->ifindex
) /* Un-related interface */
869 pnhi
->nhr_matched
= true;
870 is_valid
= !!if_is_up(pnhi
->ifp
);
873 pnhc
->valid
= is_valid
;
878 /* Given this update either from interface or nexthop tracking, re-validate this
881 * If the update is un-related, the subroutines shoud just return their cached
884 static void pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache
*pnhc
,
885 struct pbr_nht_individual
*pnhi
)
887 assert(pnhi
->nhr
|| pnhi
->ifp
); /* Either nexthop or interface update */
889 switch (pnhc
->nexthop
.type
) {
890 case NEXTHOP_TYPE_IFINDEX
:
891 pbr_nht_individual_nexthop_interface_update(pnhc
, pnhi
);
893 case NEXTHOP_TYPE_IPV6_IFINDEX
:
894 if (IN6_IS_ADDR_LINKLOCAL(&pnhc
->nexthop
.gate
.ipv6
)) {
895 pbr_nht_individual_nexthop_interface_update(pnhc
, pnhi
);
898 /* Intentional fall thru */
899 case NEXTHOP_TYPE_IPV4_IFINDEX
:
900 case NEXTHOP_TYPE_IPV4
:
901 case NEXTHOP_TYPE_IPV6
:
902 pbr_nht_individual_nexthop_gw_update(pnhc
, pnhi
);
904 case NEXTHOP_TYPE_BLACKHOLE
:
910 static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket
*b
,
913 struct pbr_nexthop_cache
*pnhc
= b
->data
;
914 struct pbr_nht_individual
*pnhi
= data
;
917 old_valid
= pnhc
->valid
;
919 pbr_nht_individual_nexthop_update(pnhc
, pnhi
);
921 DEBUGD(&pbr_dbg_nht
, " Found %pFX: old: %d new: %d",
922 &pnhi
->nhr
->prefix
, old_valid
, pnhc
->valid
);
928 static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket
*b
,
931 struct pbr_nexthop_cache
*pnhc
= b
->data
;
932 struct nexthop_group
*nhg
= data
;
933 struct nexthop
*nh
= NULL
;
935 copy_nexthops(&nh
, &pnhc
->nexthop
, NULL
);
937 _nexthop_add(&nhg
->nexthop
, nh
);
941 pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group
*nhg
,
942 struct pbr_nexthop_group_cache
*pnhgc
)
944 hash_iterate(pnhgc
->nhh
, pbr_nexthop_group_cache_iterate_to_group
, nhg
);
947 static void pbr_nht_nexthop_update_lookup(struct hash_bucket
*b
, void *data
)
949 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
950 struct pbr_nht_individual pnhi
= {};
951 struct nexthop_group nhg
= {};
954 old_valid
= pnhgc
->valid
;
956 pnhi
.nhr
= (struct zapi_route
*)data
;
958 pnhi
.nhr_matched
= false;
959 hash_iterate(pnhgc
->nhh
, pbr_nht_individual_nexthop_update_lookup
,
962 if (!pnhi
.nhr_matched
)
966 * If any of the specified nexthops are valid we are valid
968 pnhgc
->valid
= !!pnhi
.valid
;
970 pbr_nexthop_group_cache_to_nexthop_group(&nhg
, pnhgc
);
973 pbr_nht_install_nexthop_group(pnhgc
, nhg
);
975 pbr_nht_uninstall_nexthop_group(pnhgc
, nhg
, 0);
977 /* Don't need copied nexthops anymore */
978 nexthops_free(nhg
.nexthop
);
980 if (old_valid
!= pnhgc
->valid
)
981 pbr_map_check_nh_group_change(pnhgc
->name
);
984 void pbr_nht_nexthop_update(struct zapi_route
*nhr
)
986 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_update_lookup
, nhr
);
989 struct nhrc_vrf_info
{
990 struct pbr_vrf
*pbr_vrf
;
995 static int pbr_nht_nhrc_vrf_change(struct hash_bucket
*b
, void *data
)
997 struct nhrc
*nhrc
= b
->data
;
998 struct nhrc_vrf_info
*nhrcvi
= data
;
1000 if (nhrc
->nexthop
.vrf_id
== nhrcvi
->old_vrf_id
) {
1001 nhrcvi
->nhrc
= nhrc
;
1002 return HASHWALK_ABORT
;
1005 return HASHWALK_CONTINUE
;
1008 static int pbr_nht_individual_nexthop_vrf_handle(struct hash_bucket
*b
,
1011 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1012 struct pbr_nht_individual
*pnhi
= data
;
1014 if (pnhc
->looked_at
== true)
1015 return HASHWALK_CONTINUE
;
1017 if (pnhc
->nexthop
.vrf_id
== VRF_DEFAULT
)
1018 return HASHWALK_CONTINUE
;
1020 if (strncmp(pnhc
->vrf_name
, pbr_vrf_name(pnhi
->pbr_vrf
),
1021 sizeof(pnhc
->vrf_name
))
1025 if (pnhc
->nexthop
.vrf_id
!= pbr_vrf_id(pnhi
->pbr_vrf
)) {
1026 struct nhrc_vrf_info nhrcvi
;
1028 memset(&nhrcvi
, 0, sizeof(nhrcvi
));
1029 nhrcvi
.pbr_vrf
= pnhi
->pbr_vrf
;
1030 nhrcvi
.old_vrf_id
= pnhc
->nexthop
.vrf_id
;
1032 pnhi
->nhr_matched
= true;
1033 pnhi
->old_vrf_id
= pnhc
->nexthop
.vrf_id
;
1037 hash_walk(pbr_nhrc_hash
,
1038 pbr_nht_nhrc_vrf_change
, &nhrcvi
);
1040 hash_release(pbr_nhrc_hash
,
1042 nhrcvi
.nhrc
->nexthop
.vrf_id
=
1043 pbr_vrf_id(pnhi
->pbr_vrf
);
1044 (void)hash_get(pbr_nhrc_hash
,
1047 pbr_send_rnh(&nhrcvi
.nhrc
->nexthop
, true);
1049 } while (nhrcvi
.nhrc
);
1052 pnhc
->looked_at
= true;
1053 return HASHWALK_ABORT
;
1056 return HASHWALK_CONTINUE
;
1059 static void pbr_nht_clear_looked_at(struct hash_bucket
*b
, void *data
)
1061 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1063 pnhc
->looked_at
= false;
1066 static void pbr_nht_nexthop_vrf_handle(struct hash_bucket
*b
, void *data
)
1068 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1069 struct pbr_vrf
*pbr_vrf
= data
;
1070 struct pbr_nht_individual pnhi
= {};
1072 hash_iterate(pnhgc
->nhh
, pbr_nht_clear_looked_at
, NULL
);
1073 memset(&pnhi
, 0, sizeof(pnhi
));
1074 pnhi
.pbr_vrf
= pbr_vrf
;
1076 struct pbr_nexthop_cache
*pnhc
;
1079 hash_walk(pnhgc
->nhh
, pbr_nht_individual_nexthop_vrf_handle
,
1086 pnhc
->nexthop
.vrf_id
= pnhi
.old_vrf_id
;
1087 pnhi
.pnhc
= hash_release(pnhgc
->nhh
, pnhi
.pnhc
);
1089 pnhi
.pnhc
->nexthop
.vrf_id
= pbr_vrf_id(pbr_vrf
);
1091 (void)hash_get(pnhgc
->nhh
, pnhi
.pnhc
,
1094 pnhc
->nexthop
.vrf_id
= pbr_vrf_id(pbr_vrf
);
1096 pbr_map_check_vrf_nh_group_change(pnhgc
->name
, pbr_vrf
,
1098 } while (pnhi
.pnhc
);
1101 void pbr_nht_vrf_update(struct pbr_vrf
*pbr_vrf
)
1103 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_vrf_handle
, pbr_vrf
);
1106 static void pbr_nht_individual_nexthop_interface_handle(struct hash_bucket
*b
,
1109 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1110 struct pbr_nht_individual
*pnhi
= data
;
1112 if (pnhc
->nexthop
.ifindex
== 0)
1115 if ((strncmp(pnhc
->intf_name
, pnhi
->ifp
->name
, sizeof(pnhc
->intf_name
))
1117 && pnhc
->nexthop
.ifindex
!= pnhi
->ifp
->ifindex
)
1121 static void pbr_nht_nexthop_interface_handle(struct hash_bucket
*b
, void *data
)
1123 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1124 struct interface
*ifp
= data
;
1125 struct pbr_nht_individual pnhi
= {};
1127 uint32_t old_ifindex
;
1130 memset(&pnhi
, 0, sizeof(pnhi
));
1132 hash_iterate(pnhgc
->nhh
,
1133 pbr_nht_individual_nexthop_interface_handle
,
1139 pnhi
.pnhc
= hash_release(pnhgc
->nhh
, pnhi
.pnhc
);
1140 old_ifindex
= pnhi
.pnhc
->nexthop
.ifindex
;
1142 nhrc
= hash_lookup(pbr_nhrc_hash
, &pnhi
.pnhc
->nexthop
);
1144 hash_release(pbr_nhrc_hash
, nhrc
);
1145 nhrc
->nexthop
.ifindex
= ifp
->ifindex
;
1146 (void)hash_get(pbr_nhrc_hash
, nhrc
, hash_alloc_intern
);
1148 pnhi
.pnhc
->nexthop
.ifindex
= ifp
->ifindex
;
1150 (void)hash_get(pnhgc
->nhh
, pnhi
.pnhc
, hash_alloc_intern
);
1152 pbr_map_check_interface_nh_group_change(pnhgc
->name
, ifp
,
1154 } while (pnhi
.pnhc
);
1157 void pbr_nht_interface_update(struct interface
*ifp
)
1159 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_interface_handle
, ifp
);
1163 pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket
*b
,
1166 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1167 struct pbr_nht_individual
*pnhi
= data
;
1170 old_valid
= pnhc
->valid
;
1172 pbr_nht_individual_nexthop_update(pnhc
, pnhi
);
1174 DEBUGD(&pbr_dbg_nht
, " Found %s: old: %d new: %d", pnhi
->ifp
->name
,
1175 old_valid
, pnhc
->valid
);
1181 static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket
*b
,
1184 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1185 struct pbr_nht_individual pnhi
= {};
1186 struct nexthop_group nhg
= {};
1189 old_valid
= pnhgc
->valid
;
1193 hash_iterate(pnhgc
->nhh
,
1194 pbr_nht_individual_nexthop_interface_update_lookup
, &pnhi
);
1197 * If any of the specified nexthops are valid we are valid
1199 pnhgc
->valid
= pnhi
.valid
;
1201 pbr_nexthop_group_cache_to_nexthop_group(&nhg
, pnhgc
);
1204 pbr_nht_install_nexthop_group(pnhgc
, nhg
);
1206 pbr_nht_uninstall_nexthop_group(pnhgc
, nhg
, 0);
1208 nexthops_free(nhg
.nexthop
);
1210 if (old_valid
!= pnhgc
->valid
)
1211 pbr_map_check_nh_group_change(pnhgc
->name
);
1214 void pbr_nht_nexthop_interface_update(struct interface
*ifp
)
1216 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_interface_update_lookup
,
1220 static bool pbr_nhg_allocated_id_hash_equal(const void *arg1
, const void *arg2
)
1222 const struct pbr_nexthop_group_cache
*left
, *right
;
1224 left
= (const struct pbr_nexthop_group_cache
*)arg1
;
1225 right
= (const struct pbr_nexthop_group_cache
*)arg2
;
1227 return left
->table_id
== right
->table_id
;
1230 static uint32_t pbr_nhg_allocated_id_hash_key(const void *arg
)
1232 const struct pbr_nexthop_group_cache
*nhgc
= arg
;
1234 /* table_id makes elements in this hash unique */
1235 return nhgc
->table_id
;
1238 static uint32_t pbr_nhg_hash_key(const void *arg
)
1240 const struct pbr_nexthop_group_cache
*nhgc
= arg
;
1242 return jhash(&nhgc
->name
, strlen(nhgc
->name
), 0x52c34a96);
1245 static bool pbr_nhg_hash_equal(const void *arg1
, const void *arg2
)
1247 const struct pbr_nexthop_group_cache
*nhgc1
=
1248 (const struct pbr_nexthop_group_cache
*)arg1
;
1249 const struct pbr_nexthop_group_cache
*nhgc2
=
1250 (const struct pbr_nexthop_group_cache
*)arg2
;
1252 return !strcmp(nhgc1
->name
, nhgc2
->name
);
1255 uint32_t pbr_nht_find_next_unallocated_table_id(void)
1257 struct pbr_nexthop_group_cache iter
;
1260 * Find the smallest unallocated table id
1261 * This can be non-trivial considering nhg removals / shifting upper &
1262 * lower bounds, so start at the lowest in the range and continue until
1263 * an unallocated space is found
1265 for (iter
.table_id
= pbr_nhg_low_table
;
1266 iter
.table_id
< pbr_nhg_high_table
; ++iter
.table_id
)
1267 if (!hash_lookup(pbr_nhg_allocated_id_hash
, &iter
))
1268 return iter
.table_id
;
1270 /* Configured range is full, cannot install anywhere */
1274 bool pbr_nht_has_unallocated_table(void)
1276 return !!pbr_next_unallocated_table_id
;
1279 void pbr_nht_update_next_unallocated_table_id(void)
1281 pbr_next_unallocated_table_id
=
1282 pbr_nht_find_next_unallocated_table_id();
1285 uint32_t pbr_nht_reserve_next_table_id(struct pbr_nexthop_group_cache
*nhgc
)
1287 /* Nothing to reserve if all tables in range already used */
1288 if (!pbr_next_unallocated_table_id
)
1291 /* Reserve this table id */
1292 nhgc
->table_id
= pbr_next_unallocated_table_id
;
1294 /* Mark table id as allocated in id-indexed hash */
1295 (void)hash_get(pbr_nhg_allocated_id_hash
, nhgc
, hash_alloc_intern
);
1297 /* Pre-compute the next unallocated table id */
1298 pbr_nht_update_next_unallocated_table_id();
1300 /* Present caller with reserved table id */
1301 return nhgc
->table_id
;
1304 void pbr_nht_set_tableid_range(uint32_t low
, uint32_t high
)
1306 pbr_nhg_low_table
= low
;
1307 pbr_nhg_high_table
= high
;
1309 /* Re-compute next unallocated id within new range */
1310 pbr_nht_update_next_unallocated_table_id();
1313 void pbr_nht_write_table_range(struct vty
*vty
)
1315 if (pbr_nhg_low_table
!= PBR_NHT_DEFAULT_LOW_TABLEID
1316 || pbr_nhg_high_table
!= PBR_NHT_DEFAULT_HIGH_TABLEID
) {
1317 vty_out(vty
, "pbr table range %u %u\n", pbr_nhg_low_table
,
1318 pbr_nhg_high_table
);
1322 uint32_t pbr_nht_get_next_rule(uint32_t seqno
)
1324 return seqno
+ pbr_nhg_low_rule
- 1;
1326 void pbr_nht_set_rule_range(uint32_t low
, uint32_t high
)
1328 pbr_nhg_low_rule
= low
;
1329 pbr_nhg_high_rule
= high
;
1332 void pbr_nht_write_rule_range(struct vty
*vty
)
1334 if (pbr_nhg_low_rule
!= PBR_NHT_DEFAULT_LOW_RULE
1335 || pbr_nhg_high_rule
!= PBR_NHT_DEFAULT_HIGH_RULE
) {
1336 vty_out(vty
, "pbr rule range %u %u\n", pbr_nhg_low_rule
,
1341 uint32_t pbr_nht_get_table(const char *name
)
1343 struct pbr_nexthop_group_cache find
;
1344 struct pbr_nexthop_group_cache
*pnhgc
;
1346 memset(&find
, 0, sizeof(find
));
1347 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
1348 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
1351 DEBUGD(&pbr_dbg_nht
,
1352 "%s: Could not find nexthop-group cache w/ name '%s'",
1357 return pnhgc
->table_id
;
1360 bool pbr_nht_get_installed(const char *name
)
1362 struct pbr_nexthop_group_cache find
;
1363 struct pbr_nexthop_group_cache
*pnhgc
;
1365 memset(&find
, 0, sizeof(find
));
1366 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
1368 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
1373 return pnhgc
->installed
;
1376 static void pbr_nht_show_nhg_nexthops(struct hash_bucket
*b
, void *data
)
1378 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1379 struct vty
*vty
= data
;
1381 vty_out(vty
, "\tValid: %d ", pnhc
->valid
);
1382 nexthop_group_write_nexthop(vty
, &pnhc
->nexthop
);
1385 static void pbr_nht_json_nhg_nexthops(struct hash_bucket
*b
, void *data
)
1387 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1388 json_object
*all_hops
= data
;
1389 json_object
*this_hop
;
1391 this_hop
= json_object_new_object();
1392 nexthop_group_json_nexthop(this_hop
, &pnhc
->nexthop
);
1393 json_object_boolean_add(this_hop
, "valid", pnhc
->valid
);
1395 json_object_array_add(all_hops
, this_hop
);
1398 struct pbr_nht_show
{
1404 static void pbr_nht_show_nhg(struct hash_bucket
*b
, void *data
)
1406 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1407 struct pbr_nht_show
*pns
= data
;
1410 if (pns
->name
&& strcmp(pns
->name
, pnhgc
->name
) != 0)
1414 vty_out(vty
, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
1415 pnhgc
->name
, pnhgc
->table_id
, pnhgc
->valid
, pnhgc
->installed
);
1417 hash_iterate(pnhgc
->nhh
, pbr_nht_show_nhg_nexthops
, vty
);
1420 static void pbr_nht_json_nhg(struct hash_bucket
*b
, void *data
)
1422 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1423 struct pbr_nht_show
*pns
= data
;
1424 json_object
*j
, *this_group
, *group_hops
;
1426 if (pns
->name
&& strcmp(pns
->name
, pnhgc
->name
) != 0)
1430 this_group
= json_object_new_object();
1432 if (!j
|| !this_group
)
1435 json_object_int_add(this_group
, "id", pnhgc
->table_id
);
1436 json_object_string_add(this_group
, "name", pnhgc
->name
);
1437 json_object_boolean_add(this_group
, "valid", pnhgc
->valid
);
1438 json_object_boolean_add(this_group
, "installed", pnhgc
->installed
);
1440 group_hops
= json_object_new_array();
1443 hash_iterate(pnhgc
->nhh
, pbr_nht_json_nhg_nexthops
, group_hops
);
1444 json_object_object_add(this_group
, "nexthops", group_hops
);
1447 json_object_array_add(j
, this_group
);
1450 void pbr_nht_show_nexthop_group(struct vty
*vty
, const char *name
)
1452 struct pbr_nht_show pns
;
1457 hash_iterate(pbr_nhg_hash
, pbr_nht_show_nhg
, &pns
);
1460 void pbr_nht_json_nexthop_group(json_object
*j
, const char *name
)
1462 struct pbr_nht_show pns
;
1467 hash_iterate(pbr_nhg_hash
, pbr_nht_json_nhg
, &pns
);
1470 void pbr_nht_init(void)
1472 pbr_nhg_hash
= hash_create_size(
1473 16, pbr_nhg_hash_key
, pbr_nhg_hash_equal
, "PBR NHG Cache Hash");
1475 hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash
,
1476 pbr_nhrc_hash_equal
, "PBR NH Hash");
1477 pbr_nhg_allocated_id_hash
= hash_create_size(
1478 16, pbr_nhg_allocated_id_hash_key
,
1479 pbr_nhg_allocated_id_hash_equal
, "PBR Allocated Table Hash");
1481 pbr_nhg_low_table
= PBR_NHT_DEFAULT_LOW_TABLEID
;
1482 pbr_nhg_high_table
= PBR_NHT_DEFAULT_HIGH_TABLEID
;
1483 pbr_nhg_low_rule
= PBR_NHT_DEFAULT_LOW_RULE
;
1484 pbr_nhg_high_rule
= PBR_NHT_DEFAULT_HIGH_RULE
;
1486 /* First unallocated table is lowest in range on init */
1487 pbr_next_unallocated_table_id
= PBR_NHT_DEFAULT_LOW_TABLEID
;