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_modify_cb(const struct nexthop_group_cmd
*nhgc
)
236 void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd
*nhgc
,
237 const struct nexthop
*nhop
)
240 struct pbr_nexthop_group_cache pnhgc_find
= {};
241 struct pbr_nexthop_group_cache
*pnhgc
;
242 struct pbr_nexthop_cache pnhc_find
= {};
243 struct pbr_nexthop_cache
*pnhc
;
245 /* find pnhgc by name */
246 strlcpy(pnhgc_find
.name
, nhgc
->name
, sizeof(pnhgc_find
.name
));
247 pnhgc
= hash_lookup(pbr_nhg_hash
, &pnhgc_find
);
250 /* Check if configured table range is exhausted */
251 if (!pbr_nht_has_unallocated_table()) {
253 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
254 __func__
, nhgc
->name
);
258 /* No nhgc but range not exhausted? Then alloc it */
259 pnhgc
= hash_get(pbr_nhg_hash
, &pnhgc_find
, pbr_nhgc_alloc
);
262 /* create & insert new pnhc into pnhgc->nhh */
263 pnhc_find
.nexthop
= *nhop
;
264 pnhc
= hash_get(pnhgc
->nhh
, &pnhc_find
, pbr_nh_alloc
);
266 /* set parent pnhgc */
267 pnhc
->parent
= pnhgc
;
269 if (DEBUG_MODE_CHECK(&pbr_dbg_nht
, DEBUG_MODE_ALL
)) {
270 nexthop2str(nhop
, debugstr
, sizeof(debugstr
));
271 DEBUGD(&pbr_dbg_nht
, "%s: Added %s to nexthop-group %s",
272 __func__
, debugstr
, nhgc
->name
);
275 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
276 pbr_map_check_nh_group_change(nhgc
->name
);
278 if (nhop
->type
== NEXTHOP_TYPE_IFINDEX
279 || (nhop
->type
== NEXTHOP_TYPE_IPV6_IFINDEX
280 && IN6_IS_ADDR_LINKLOCAL(&nhop
->gate
.ipv6
))) {
281 struct interface
*ifp
;
283 ifp
= if_lookup_by_index(nhop
->ifindex
, nhop
->vrf_id
);
285 pbr_nht_nexthop_interface_update(ifp
);
289 void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd
*nhgc
,
290 const struct nexthop
*nhop
)
293 struct pbr_nexthop_group_cache pnhgc_find
= {};
294 struct pbr_nexthop_group_cache
*pnhgc
;
295 struct pbr_nexthop_cache pnhc_find
= {};
296 struct pbr_nexthop_cache
*pnhc
;
297 enum nexthop_types_t nh_type
= nhop
->type
;
299 /* find pnhgc by name */
300 strlcpy(pnhgc_find
.name
, nhgc
->name
, sizeof(pnhgc_find
.name
));
301 pnhgc
= hash_lookup(pbr_nhg_hash
, &pnhgc_find
);
304 * Ignore deletions of nhg we did not / could not allocate nhgc for
305 * Occurs when PBR table range is full but new nhg keep coming in
310 /* delete pnhc from pnhgc->nhh */
311 pnhc_find
.nexthop
= *nhop
;
312 pnhc
= hash_release(pnhgc
->nhh
, &pnhc_find
);
315 pbr_nh_delete(&pnhc
);
317 if (DEBUG_MODE_CHECK(&pbr_dbg_nht
, DEBUG_MODE_ALL
)) {
318 nexthop2str(nhop
, debugstr
, sizeof(debugstr
));
319 DEBUGD(&pbr_dbg_nht
, "%s: Removed %s from nexthop-group %s",
320 __func__
, debugstr
, nhgc
->name
);
323 if (pnhgc
->nhh
->count
)
324 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
326 pbr_nht_uninstall_nexthop_group(pnhgc
, nhgc
->nhg
, nh_type
);
328 pbr_map_check_nh_group_change(nhgc
->name
);
331 void pbr_nhgroup_delete_cb(const char *name
)
333 DEBUGD(&pbr_dbg_nht
, "%s: Removed nexthop-group %s", __func__
, name
);
335 /* delete group from all pbrms's */
336 pbr_nht_delete_group(name
);
338 pbr_map_check_nh_group_change(name
);
342 pbr_nht_find_nhg_from_table_update(struct pbr_nexthop_group_cache
*pnhgc
,
343 uint32_t table_id
, bool installed
)
345 if (pnhgc
->table_id
== table_id
) {
346 DEBUGD(&pbr_dbg_nht
, "%s: %s: Table ID (%u) matches %s",
347 __func__
, (installed
? "install" : "remove"), table_id
,
350 pnhgc
->installed
= installed
;
351 pnhgc
->valid
= installed
;
352 pbr_map_schedule_policy_from_nhg(pnhgc
->name
, pnhgc
->installed
);
356 static void pbr_nht_find_nhg_from_table_install(struct hash_bucket
*b
,
359 struct pbr_nexthop_group_cache
*pnhgc
=
360 (struct pbr_nexthop_group_cache
*)b
->data
;
361 uint32_t table_id
= *(uint32_t *)data
;
363 pbr_nht_find_nhg_from_table_update(pnhgc
, table_id
, true);
366 void pbr_nht_route_installed_for_table(uint32_t table_id
)
368 hash_iterate(pbr_nhg_hash
, pbr_nht_find_nhg_from_table_install
,
372 static void pbr_nht_find_nhg_from_table_remove(struct hash_bucket
*b
,
375 struct pbr_nexthop_group_cache
*pnhgc
=
376 (struct pbr_nexthop_group_cache
*)b
->data
;
377 uint32_t table_id
= *(uint32_t *)data
;
379 pbr_nht_find_nhg_from_table_update(pnhgc
, table_id
, false);
382 void pbr_nht_route_removed_for_table(uint32_t table_id
)
384 hash_iterate(pbr_nhg_hash
, pbr_nht_find_nhg_from_table_remove
,
389 * Loop through all nexthops in a nexthop group to check that they are all the
390 * same. If they are not all the same, log this peculiarity.
393 * The nexthop group to check
396 * - AFI of last nexthop in the group
399 static afi_t
pbr_nht_which_afi(struct nexthop_group nhg
,
400 enum nexthop_types_t nh_type
)
402 struct nexthop
*nexthop
;
403 afi_t install_afi
= AFI_MAX
;
408 case NEXTHOP_TYPE_IPV4
:
409 case NEXTHOP_TYPE_IPV4_IFINDEX
:
411 case NEXTHOP_TYPE_IPV6
:
412 case NEXTHOP_TYPE_IPV6_IFINDEX
:
414 case NEXTHOP_TYPE_IFINDEX
:
415 case NEXTHOP_TYPE_BLACKHOLE
:
420 v6
= v4
= bh
= false;
422 for (ALL_NEXTHOPS(nhg
, nexthop
)) {
423 nh_type
= nexthop
->type
;
426 case NEXTHOP_TYPE_IFINDEX
:
428 case NEXTHOP_TYPE_IPV4
:
429 case NEXTHOP_TYPE_IPV4_IFINDEX
:
431 install_afi
= AFI_IP
;
433 case NEXTHOP_TYPE_IPV6
:
434 case NEXTHOP_TYPE_IPV6_IFINDEX
:
436 install_afi
= AFI_IP6
;
438 case NEXTHOP_TYPE_BLACKHOLE
:
444 /* Interface and/or blackhole nexthops only. */
446 install_afi
= AFI_MAX
;
450 "%s: Saw both V6 and V4 nexthops...using %s", __func__
,
451 afi2str(install_afi
));
452 if (bh
&& (v6
|| v4
))
454 "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.",
455 __func__
, v4
? "v4" : "", (v4
&& v6
) ? " and " : "",
461 static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
462 struct nexthop_group nhg
)
465 enum nexthop_types_t nh_type
= 0;
467 install_afi
= pbr_nht_which_afi(nhg
, nh_type
);
469 route_add(pnhgc
, nhg
, install_afi
);
473 pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
474 struct nexthop_group nhg
,
475 enum nexthop_types_t nh_type
)
479 install_afi
= pbr_nht_which_afi(nhg
, nh_type
);
481 pnhgc
->installed
= false;
482 pnhgc
->valid
= false;
483 route_delete(pnhgc
, install_afi
);
486 void pbr_nht_change_group(const char *name
)
488 struct nexthop_group_cmd
*nhgc
;
489 struct pbr_nexthop_group_cache
*pnhgc
;
490 struct pbr_nexthop_group_cache find
;
491 struct nexthop
*nhop
;
493 nhgc
= nhgc_find(name
);
497 memset(&find
, 0, sizeof(find
));
498 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
499 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
503 "%s: Could not find nexthop-group cache w/ name '%s'",
508 for (ALL_NEXTHOPS(nhgc
->nhg
, nhop
)) {
509 struct pbr_nexthop_cache lookup
;
510 struct pbr_nexthop_cache
*pnhc
;
512 lookup
.nexthop
= *nhop
;
513 pnhc
= hash_lookup(pnhgc
->nhh
, &lookup
);
515 pnhc
= hash_get(pnhgc
->nhh
, &lookup
, pbr_nh_alloc
);
516 pnhc
->parent
= pnhgc
;
519 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
522 char *pbr_nht_nexthop_make_name(char *name
, size_t l
,
523 uint32_t seqno
, char *buffer
)
525 snprintf(buffer
, l
, "%s%u", name
, seqno
);
529 /* Set data derived from nhg in pbrms */
530 void pbr_nht_set_seq_nhg_data(struct pbr_map_sequence
*pbrms
,
531 const struct nexthop_group_cmd
*nhgc
)
533 const struct nexthop_group
*nhg
;
542 switch (nhg
->nexthop
->type
) {
543 case NEXTHOP_TYPE_IPV6
:
544 case NEXTHOP_TYPE_IPV6_IFINDEX
:
545 pbrms
->family
= AF_INET6
;
547 case NEXTHOP_TYPE_IPV4
:
548 case NEXTHOP_TYPE_IPV4_IFINDEX
:
549 pbrms
->family
= AF_INET
;
550 case NEXTHOP_TYPE_IFINDEX
:
551 case NEXTHOP_TYPE_BLACKHOLE
:
556 /* Configure a routemap sequence to use a given nexthop group */
557 void pbr_nht_set_seq_nhg(struct pbr_map_sequence
*pbrms
, const char *name
)
559 struct nexthop_group_cmd
*nhgc
;
564 pbrms
->nhgrp_name
= XSTRDUP(MTYPE_TMP
, name
);
566 nhgc
= nhgc_find(name
);
570 pbr_nht_set_seq_nhg_data(pbrms
, nhgc
);
573 void pbr_nht_add_individual_nexthop(struct pbr_map_sequence
*pbrms
,
574 const struct nexthop
*nhop
)
576 struct pbr_nexthop_group_cache
*pnhgc
;
577 struct pbr_nexthop_group_cache find
;
578 struct pbr_nexthop_cache
*pnhc
;
579 struct pbr_nexthop_cache lookup
;
581 char buf
[PBR_NHC_NAMELEN
];
583 pbrms
->nhg
= nexthop_group_new();
584 pbrms
->internal_nhg_name
= XSTRDUP(
586 pbr_nht_nexthop_make_name(pbrms
->parent
->name
, PBR_NHC_NAMELEN
,
590 memcpy(nh
, nhop
, sizeof(*nh
));
592 nexthop_group_add_sorted(pbrms
->nhg
, nh
);
594 memset(&find
, 0, sizeof(find
));
595 pbr_nht_nexthop_make_name(pbrms
->parent
->name
, PBR_NHC_NAMELEN
,
596 pbrms
->seqno
, find
.name
);
598 if (!pbr_nht_has_unallocated_table()) {
600 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
601 __func__
, find
.name
);
605 if (!pbrms
->internal_nhg_name
)
606 pbrms
->internal_nhg_name
= XSTRDUP(MTYPE_TMP
, find
.name
);
608 pnhgc
= hash_get(pbr_nhg_hash
, &find
, pbr_nhgc_alloc
);
610 lookup
.nexthop
= *pbrms
->nhg
->nexthop
;
611 pnhc
= hash_get(pnhgc
->nhh
, &lookup
, pbr_nh_alloc
);
612 pnhc
->parent
= pnhgc
;
613 if (nhop
->vrf_id
!= VRF_DEFAULT
) {
614 struct vrf
*vrf
= vrf_lookup_by_id(nhop
->vrf_id
);
617 strlcpy(pnhc
->vrf_name
, vrf
->name
,
618 sizeof(pnhc
->vrf_name
));
621 if (nhop
->ifindex
!= 0) {
622 struct interface
*ifp
=
623 if_lookup_by_index(nhop
->ifindex
, nhop
->vrf_id
);
626 strlcpy(pnhc
->intf_name
, ifp
->name
,
627 sizeof(pnhc
->intf_name
));
629 pbr_nht_install_nexthop_group(pnhgc
, *pbrms
->nhg
);
632 static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence
*pbrms
)
634 struct pbr_nexthop_group_cache
*pnhgc
;
635 struct pbr_nexthop_group_cache find
;
636 struct pbr_nexthop_cache
*pnhc
;
637 struct pbr_nexthop_cache lup
;
639 enum nexthop_types_t nh_type
= 0;
641 memset(&find
, 0, sizeof(find
));
642 snprintf(find
.name
, sizeof(find
.name
), "%s", pbrms
->internal_nhg_name
);
643 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
645 nh
= pbrms
->nhg
->nexthop
;
648 pnhc
= hash_lookup(pnhgc
->nhh
, &lup
);
650 hash_release(pnhgc
->nhh
, pnhc
);
651 pbr_nh_delete(&pnhc
);
652 pbr_nht_uninstall_nexthop_group(pnhgc
, *pbrms
->nhg
, nh_type
);
654 hash_release(pbr_nhg_hash
, pnhgc
);
655 pbr_nhgc_delete(pnhgc
);
657 nexthop_group_delete(&pbrms
->nhg
);
658 XFREE(MTYPE_TMP
, pbrms
->internal_nhg_name
);
661 void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence
*pbrms
)
663 pbr_map_delete_nexthops(pbrms
);
665 pbr_nht_release_individual_nexthop(pbrms
);
668 struct pbr_nexthop_group_cache
*pbr_nht_add_group(const char *name
)
670 struct nexthop
*nhop
;
671 struct nexthop_group_cmd
*nhgc
;
672 struct pbr_nexthop_group_cache
*pnhgc
;
673 struct pbr_nexthop_group_cache lookup
;
675 if (!pbr_nht_has_unallocated_table()) {
677 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
682 nhgc
= nhgc_find(name
);
685 DEBUGD(&pbr_dbg_nht
, "%s: Could not find nhgc with name: %s",
690 snprintf(lookup
.name
, sizeof(lookup
.name
), "%s", name
);
691 pnhgc
= hash_get(pbr_nhg_hash
, &lookup
, pbr_nhgc_alloc
);
692 DEBUGD(&pbr_dbg_nht
, "%s: Retrieved NHGC @ %p", __func__
, pnhgc
);
694 for (ALL_NEXTHOPS(nhgc
->nhg
, nhop
)) {
695 struct pbr_nexthop_cache lookupc
;
696 struct pbr_nexthop_cache
*pnhc
;
698 lookupc
.nexthop
= *nhop
;
699 pnhc
= hash_lookup(pnhgc
->nhh
, &lookupc
);
701 pnhc
= hash_get(pnhgc
->nhh
, &lookupc
, pbr_nh_alloc
);
702 pnhc
->parent
= pnhgc
;
709 void pbr_nht_delete_group(const char *name
)
711 struct pbr_map_sequence
*pbrms
;
712 struct listnode
*snode
;
713 struct pbr_map
*pbrm
;
714 struct pbr_nexthop_group_cache pnhgc_find
;
715 struct pbr_nexthop_group_cache
*pnhgc
;
717 RB_FOREACH (pbrm
, pbr_map_entry_head
, &pbr_maps
) {
718 for (ALL_LIST_ELEMENTS_RO(pbrm
->seqnumbers
, snode
, pbrms
)) {
719 if (pbrms
->nhgrp_name
720 && strmatch(pbrms
->nhgrp_name
, name
)) {
721 pbrms
->reason
|= PBR_MAP_INVALID_NO_NEXTHOPS
;
723 pbrms
->internal_nhg_name
= NULL
;
729 strlcpy(pnhgc_find
.name
, name
, sizeof(pnhgc_find
.name
));
730 pnhgc
= hash_release(pbr_nhg_hash
, &pnhgc_find
);
733 * Ignore deletions of nh we did not / could not allocate nhgc for
734 * Occurs when PBR table range is full but new nhg keep coming in
739 /* Remove and recalculate the next table id */
740 hash_release(pbr_nhg_allocated_id_hash
, pnhgc
);
741 pbr_nht_update_next_unallocated_table_id();
743 pbr_nhgc_delete(pnhgc
);
746 bool pbr_nht_nexthop_valid(struct nexthop_group
*nhg
)
748 DEBUGD(&pbr_dbg_nht
, "%s: %p", __func__
, nhg
);
752 bool pbr_nht_nexthop_group_valid(const char *name
)
754 struct pbr_nexthop_group_cache
*pnhgc
;
755 struct pbr_nexthop_group_cache lookup
;
757 DEBUGD(&pbr_dbg_nht
, "%s: %s", __func__
, name
);
759 snprintf(lookup
.name
, sizeof(lookup
.name
), "%s", name
);
760 pnhgc
= hash_get(pbr_nhg_hash
, &lookup
, NULL
);
763 DEBUGD(&pbr_dbg_nht
, "%s: %d %d", __func__
, pnhgc
->valid
,
765 if (pnhgc
->valid
&& pnhgc
->installed
)
771 struct pbr_nht_individual
{
772 struct zapi_route
*nhr
;
773 struct interface
*ifp
;
774 struct pbr_vrf
*pbr_vrf
;
775 struct pbr_nexthop_cache
*pnhc
;
784 pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache
*pnhc
,
785 struct pbr_nht_individual
*pnhi
)
787 bool is_valid
= pnhc
->valid
;
790 * If we have an interface down event, let's note that
791 * it is happening and find all the nexthops that depend
792 * on that interface. As that if we have an interface
793 * flapping fast enough it means that zebra might turn
794 * those nexthop tracking events into a no-update
795 * So let's search and do the right thing on the
799 switch (pnhc
->nexthop
.type
) {
800 case NEXTHOP_TYPE_BLACKHOLE
:
801 case NEXTHOP_TYPE_IPV4
:
802 case NEXTHOP_TYPE_IPV6
:
804 case NEXTHOP_TYPE_IFINDEX
:
805 case NEXTHOP_TYPE_IPV4_IFINDEX
:
806 case NEXTHOP_TYPE_IPV6_IFINDEX
:
807 if (pnhc
->nexthop
.ifindex
== pnhi
->ifp
->ifindex
)
808 is_valid
= if_is_up(pnhi
->ifp
);
815 switch (pnhi
->nhr
->prefix
.family
) {
817 if (pnhc
->nexthop
.gate
.ipv4
.s_addr
818 != pnhi
->nhr
->prefix
.u
.prefix4
.s_addr
)
819 goto done
; /* Unrelated change */
822 if (memcmp(&pnhc
->nexthop
.gate
.ipv6
,
823 &pnhi
->nhr
->prefix
.u
.prefix6
, 16)
825 goto done
; /* Unrelated change */
829 pnhi
->nhr_matched
= true;
830 if (!pnhi
->nhr
->nexthop_num
) {
835 if (pnhc
->nexthop
.type
== NEXTHOP_TYPE_IPV4_IFINDEX
836 || pnhc
->nexthop
.type
== NEXTHOP_TYPE_IPV6_IFINDEX
) {
838 /* GATEWAY_IFINDEX type shouldn't resolve to group */
839 if (pnhi
->nhr
->nexthop_num
> 1) {
844 /* If whatever we resolved to wasn't on the interface we
845 * specified. (i.e. not a connected route), its invalid.
847 if (pnhi
->nhr
->nexthops
[0].ifindex
!= pnhc
->nexthop
.ifindex
) {
856 pnhc
->valid
= is_valid
;
862 pbr_nht_individual_nexthop_interface_update(struct pbr_nexthop_cache
*pnhc
,
863 struct pbr_nht_individual
*pnhi
)
865 bool is_valid
= pnhc
->valid
;
867 if (!pnhi
->ifp
) /* It doesn't care about non-interface updates */
870 if (pnhc
->nexthop
.ifindex
871 != pnhi
->ifp
->ifindex
) /* Un-related interface */
874 pnhi
->nhr_matched
= true;
875 is_valid
= !!if_is_up(pnhi
->ifp
);
878 pnhc
->valid
= is_valid
;
883 /* Given this update either from interface or nexthop tracking, re-validate this
886 * If the update is un-related, the subroutines shoud just return their cached
889 static void pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache
*pnhc
,
890 struct pbr_nht_individual
*pnhi
)
892 assert(pnhi
->nhr
|| pnhi
->ifp
); /* Either nexthop or interface update */
894 switch (pnhc
->nexthop
.type
) {
895 case NEXTHOP_TYPE_IFINDEX
:
896 pbr_nht_individual_nexthop_interface_update(pnhc
, pnhi
);
898 case NEXTHOP_TYPE_IPV6_IFINDEX
:
899 if (IN6_IS_ADDR_LINKLOCAL(&pnhc
->nexthop
.gate
.ipv6
)) {
900 pbr_nht_individual_nexthop_interface_update(pnhc
, pnhi
);
903 /* Intentional fall thru */
904 case NEXTHOP_TYPE_IPV4_IFINDEX
:
905 case NEXTHOP_TYPE_IPV4
:
906 case NEXTHOP_TYPE_IPV6
:
907 pbr_nht_individual_nexthop_gw_update(pnhc
, pnhi
);
909 case NEXTHOP_TYPE_BLACKHOLE
:
915 static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket
*b
,
918 struct pbr_nexthop_cache
*pnhc
= b
->data
;
919 struct pbr_nht_individual
*pnhi
= data
;
922 old_valid
= pnhc
->valid
;
924 pbr_nht_individual_nexthop_update(pnhc
, pnhi
);
926 DEBUGD(&pbr_dbg_nht
, " Found %pFX: old: %d new: %d",
927 &pnhi
->nhr
->prefix
, old_valid
, pnhc
->valid
);
933 static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket
*b
,
936 struct pbr_nexthop_cache
*pnhc
= b
->data
;
937 struct nexthop_group
*nhg
= data
;
938 struct nexthop
*nh
= NULL
;
940 copy_nexthops(&nh
, &pnhc
->nexthop
, NULL
);
942 _nexthop_add(&nhg
->nexthop
, nh
);
946 pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group
*nhg
,
947 struct pbr_nexthop_group_cache
*pnhgc
)
949 hash_iterate(pnhgc
->nhh
, pbr_nexthop_group_cache_iterate_to_group
, nhg
);
952 static void pbr_nht_nexthop_update_lookup(struct hash_bucket
*b
, void *data
)
954 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
955 struct pbr_nht_individual pnhi
= {};
956 struct nexthop_group nhg
= {};
959 old_valid
= pnhgc
->valid
;
961 pnhi
.nhr
= (struct zapi_route
*)data
;
963 pnhi
.nhr_matched
= false;
964 hash_iterate(pnhgc
->nhh
, pbr_nht_individual_nexthop_update_lookup
,
967 if (!pnhi
.nhr_matched
)
971 * If any of the specified nexthops are valid we are valid
973 pnhgc
->valid
= !!pnhi
.valid
;
975 pbr_nexthop_group_cache_to_nexthop_group(&nhg
, pnhgc
);
978 pbr_nht_install_nexthop_group(pnhgc
, nhg
);
980 pbr_nht_uninstall_nexthop_group(pnhgc
, nhg
, 0);
982 /* Don't need copied nexthops anymore */
983 nexthops_free(nhg
.nexthop
);
985 if (old_valid
!= pnhgc
->valid
)
986 pbr_map_check_nh_group_change(pnhgc
->name
);
989 void pbr_nht_nexthop_update(struct zapi_route
*nhr
)
991 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_update_lookup
, nhr
);
994 struct nhrc_vrf_info
{
995 struct pbr_vrf
*pbr_vrf
;
1000 static int pbr_nht_nhrc_vrf_change(struct hash_bucket
*b
, void *data
)
1002 struct nhrc
*nhrc
= b
->data
;
1003 struct nhrc_vrf_info
*nhrcvi
= data
;
1005 if (nhrc
->nexthop
.vrf_id
== nhrcvi
->old_vrf_id
) {
1006 nhrcvi
->nhrc
= nhrc
;
1007 return HASHWALK_ABORT
;
1010 return HASHWALK_CONTINUE
;
1013 static int pbr_nht_individual_nexthop_vrf_handle(struct hash_bucket
*b
,
1016 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1017 struct pbr_nht_individual
*pnhi
= data
;
1019 if (pnhc
->looked_at
== true)
1020 return HASHWALK_CONTINUE
;
1022 if (pnhc
->nexthop
.vrf_id
== VRF_DEFAULT
)
1023 return HASHWALK_CONTINUE
;
1025 if (strncmp(pnhc
->vrf_name
, pbr_vrf_name(pnhi
->pbr_vrf
),
1026 sizeof(pnhc
->vrf_name
))
1030 if (pnhc
->nexthop
.vrf_id
!= pbr_vrf_id(pnhi
->pbr_vrf
)) {
1031 struct nhrc_vrf_info nhrcvi
;
1033 memset(&nhrcvi
, 0, sizeof(nhrcvi
));
1034 nhrcvi
.pbr_vrf
= pnhi
->pbr_vrf
;
1035 nhrcvi
.old_vrf_id
= pnhc
->nexthop
.vrf_id
;
1037 pnhi
->nhr_matched
= true;
1038 pnhi
->old_vrf_id
= pnhc
->nexthop
.vrf_id
;
1042 hash_walk(pbr_nhrc_hash
,
1043 pbr_nht_nhrc_vrf_change
, &nhrcvi
);
1045 hash_release(pbr_nhrc_hash
,
1047 nhrcvi
.nhrc
->nexthop
.vrf_id
=
1048 pbr_vrf_id(pnhi
->pbr_vrf
);
1049 (void)hash_get(pbr_nhrc_hash
,
1052 pbr_send_rnh(&nhrcvi
.nhrc
->nexthop
, true);
1054 } while (nhrcvi
.nhrc
);
1057 pnhc
->looked_at
= true;
1058 return HASHWALK_ABORT
;
1061 return HASHWALK_CONTINUE
;
1064 static void pbr_nht_clear_looked_at(struct hash_bucket
*b
, void *data
)
1066 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1068 pnhc
->looked_at
= false;
1071 static void pbr_nht_nexthop_vrf_handle(struct hash_bucket
*b
, void *data
)
1073 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1074 struct pbr_vrf
*pbr_vrf
= data
;
1075 struct pbr_nht_individual pnhi
= {};
1077 hash_iterate(pnhgc
->nhh
, pbr_nht_clear_looked_at
, NULL
);
1078 memset(&pnhi
, 0, sizeof(pnhi
));
1079 pnhi
.pbr_vrf
= pbr_vrf
;
1081 struct pbr_nexthop_cache
*pnhc
;
1084 hash_walk(pnhgc
->nhh
, pbr_nht_individual_nexthop_vrf_handle
,
1091 pnhc
->nexthop
.vrf_id
= pnhi
.old_vrf_id
;
1092 pnhi
.pnhc
= hash_release(pnhgc
->nhh
, pnhi
.pnhc
);
1094 pnhi
.pnhc
->nexthop
.vrf_id
= pbr_vrf_id(pbr_vrf
);
1096 (void)hash_get(pnhgc
->nhh
, pnhi
.pnhc
,
1099 pnhc
->nexthop
.vrf_id
= pbr_vrf_id(pbr_vrf
);
1101 pbr_map_check_vrf_nh_group_change(pnhgc
->name
, pbr_vrf
,
1103 } while (pnhi
.pnhc
);
1106 void pbr_nht_vrf_update(struct pbr_vrf
*pbr_vrf
)
1108 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_vrf_handle
, pbr_vrf
);
1111 static void pbr_nht_individual_nexthop_interface_handle(struct hash_bucket
*b
,
1114 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1115 struct pbr_nht_individual
*pnhi
= data
;
1117 if (pnhc
->nexthop
.ifindex
== 0)
1120 if ((strncmp(pnhc
->intf_name
, pnhi
->ifp
->name
, sizeof(pnhc
->intf_name
))
1122 && pnhc
->nexthop
.ifindex
!= pnhi
->ifp
->ifindex
)
1126 static void pbr_nht_nexthop_interface_handle(struct hash_bucket
*b
, void *data
)
1128 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1129 struct interface
*ifp
= data
;
1130 struct pbr_nht_individual pnhi
= {};
1132 uint32_t old_ifindex
;
1135 memset(&pnhi
, 0, sizeof(pnhi
));
1137 hash_iterate(pnhgc
->nhh
,
1138 pbr_nht_individual_nexthop_interface_handle
,
1144 pnhi
.pnhc
= hash_release(pnhgc
->nhh
, pnhi
.pnhc
);
1145 old_ifindex
= pnhi
.pnhc
->nexthop
.ifindex
;
1147 nhrc
= hash_lookup(pbr_nhrc_hash
, &pnhi
.pnhc
->nexthop
);
1149 hash_release(pbr_nhrc_hash
, nhrc
);
1150 nhrc
->nexthop
.ifindex
= ifp
->ifindex
;
1151 (void)hash_get(pbr_nhrc_hash
, nhrc
, hash_alloc_intern
);
1153 pnhi
.pnhc
->nexthop
.ifindex
= ifp
->ifindex
;
1155 (void)hash_get(pnhgc
->nhh
, pnhi
.pnhc
, hash_alloc_intern
);
1157 pbr_map_check_interface_nh_group_change(pnhgc
->name
, ifp
,
1159 } while (pnhi
.pnhc
);
1162 void pbr_nht_interface_update(struct interface
*ifp
)
1164 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_interface_handle
, ifp
);
1168 pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket
*b
,
1171 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1172 struct pbr_nht_individual
*pnhi
= data
;
1175 old_valid
= pnhc
->valid
;
1177 pbr_nht_individual_nexthop_update(pnhc
, pnhi
);
1179 DEBUGD(&pbr_dbg_nht
, " Found %s: old: %d new: %d", pnhi
->ifp
->name
,
1180 old_valid
, pnhc
->valid
);
1186 static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket
*b
,
1189 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1190 struct pbr_nht_individual pnhi
= {};
1191 struct nexthop_group nhg
= {};
1194 old_valid
= pnhgc
->valid
;
1198 hash_iterate(pnhgc
->nhh
,
1199 pbr_nht_individual_nexthop_interface_update_lookup
, &pnhi
);
1202 * If any of the specified nexthops are valid we are valid
1204 pnhgc
->valid
= pnhi
.valid
;
1206 pbr_nexthop_group_cache_to_nexthop_group(&nhg
, pnhgc
);
1209 pbr_nht_install_nexthop_group(pnhgc
, nhg
);
1211 pbr_nht_uninstall_nexthop_group(pnhgc
, nhg
, 0);
1213 nexthops_free(nhg
.nexthop
);
1215 if (old_valid
!= pnhgc
->valid
)
1216 pbr_map_check_nh_group_change(pnhgc
->name
);
1219 void pbr_nht_nexthop_interface_update(struct interface
*ifp
)
1221 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_interface_update_lookup
,
1225 static bool pbr_nhg_allocated_id_hash_equal(const void *arg1
, const void *arg2
)
1227 const struct pbr_nexthop_group_cache
*left
, *right
;
1229 left
= (const struct pbr_nexthop_group_cache
*)arg1
;
1230 right
= (const struct pbr_nexthop_group_cache
*)arg2
;
1232 return left
->table_id
== right
->table_id
;
1235 static uint32_t pbr_nhg_allocated_id_hash_key(const void *arg
)
1237 const struct pbr_nexthop_group_cache
*nhgc
= arg
;
1239 /* table_id makes elements in this hash unique */
1240 return nhgc
->table_id
;
1243 static uint32_t pbr_nhg_hash_key(const void *arg
)
1245 const struct pbr_nexthop_group_cache
*nhgc
= arg
;
1247 return jhash(&nhgc
->name
, strlen(nhgc
->name
), 0x52c34a96);
1250 static bool pbr_nhg_hash_equal(const void *arg1
, const void *arg2
)
1252 const struct pbr_nexthop_group_cache
*nhgc1
=
1253 (const struct pbr_nexthop_group_cache
*)arg1
;
1254 const struct pbr_nexthop_group_cache
*nhgc2
=
1255 (const struct pbr_nexthop_group_cache
*)arg2
;
1257 return !strcmp(nhgc1
->name
, nhgc2
->name
);
1260 uint32_t pbr_nht_find_next_unallocated_table_id(void)
1262 struct pbr_nexthop_group_cache iter
;
1265 * Find the smallest unallocated table id
1266 * This can be non-trivial considering nhg removals / shifting upper &
1267 * lower bounds, so start at the lowest in the range and continue until
1268 * an unallocated space is found
1270 for (iter
.table_id
= pbr_nhg_low_table
;
1271 iter
.table_id
< pbr_nhg_high_table
; ++iter
.table_id
)
1272 if (!hash_lookup(pbr_nhg_allocated_id_hash
, &iter
))
1273 return iter
.table_id
;
1275 /* Configured range is full, cannot install anywhere */
1279 bool pbr_nht_has_unallocated_table(void)
1281 return !!pbr_next_unallocated_table_id
;
1284 void pbr_nht_update_next_unallocated_table_id(void)
1286 pbr_next_unallocated_table_id
=
1287 pbr_nht_find_next_unallocated_table_id();
1290 uint32_t pbr_nht_reserve_next_table_id(struct pbr_nexthop_group_cache
*nhgc
)
1292 /* Nothing to reserve if all tables in range already used */
1293 if (!pbr_next_unallocated_table_id
)
1296 /* Reserve this table id */
1297 nhgc
->table_id
= pbr_next_unallocated_table_id
;
1299 /* Mark table id as allocated in id-indexed hash */
1300 (void)hash_get(pbr_nhg_allocated_id_hash
, nhgc
, hash_alloc_intern
);
1302 /* Pre-compute the next unallocated table id */
1303 pbr_nht_update_next_unallocated_table_id();
1305 /* Present caller with reserved table id */
1306 return nhgc
->table_id
;
1309 void pbr_nht_set_tableid_range(uint32_t low
, uint32_t high
)
1311 pbr_nhg_low_table
= low
;
1312 pbr_nhg_high_table
= high
;
1314 /* Re-compute next unallocated id within new range */
1315 pbr_nht_update_next_unallocated_table_id();
1318 void pbr_nht_write_table_range(struct vty
*vty
)
1320 if (pbr_nhg_low_table
!= PBR_NHT_DEFAULT_LOW_TABLEID
1321 || pbr_nhg_high_table
!= PBR_NHT_DEFAULT_HIGH_TABLEID
) {
1322 vty_out(vty
, "pbr table range %u %u\n", pbr_nhg_low_table
,
1323 pbr_nhg_high_table
);
1327 uint32_t pbr_nht_get_next_rule(uint32_t seqno
)
1329 return seqno
+ pbr_nhg_low_rule
- 1;
1331 void pbr_nht_set_rule_range(uint32_t low
, uint32_t high
)
1333 pbr_nhg_low_rule
= low
;
1334 pbr_nhg_high_rule
= high
;
1337 void pbr_nht_write_rule_range(struct vty
*vty
)
1339 if (pbr_nhg_low_rule
!= PBR_NHT_DEFAULT_LOW_RULE
1340 || pbr_nhg_high_rule
!= PBR_NHT_DEFAULT_HIGH_RULE
) {
1341 vty_out(vty
, "pbr rule range %u %u\n", pbr_nhg_low_rule
,
1346 uint32_t pbr_nht_get_table(const char *name
)
1348 struct pbr_nexthop_group_cache find
;
1349 struct pbr_nexthop_group_cache
*pnhgc
;
1351 memset(&find
, 0, sizeof(find
));
1352 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
1353 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
1356 DEBUGD(&pbr_dbg_nht
,
1357 "%s: Could not find nexthop-group cache w/ name '%s'",
1362 return pnhgc
->table_id
;
1365 bool pbr_nht_get_installed(const char *name
)
1367 struct pbr_nexthop_group_cache find
;
1368 struct pbr_nexthop_group_cache
*pnhgc
;
1370 memset(&find
, 0, sizeof(find
));
1371 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
1373 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
1378 return pnhgc
->installed
;
1381 static void pbr_nht_show_nhg_nexthops(struct hash_bucket
*b
, void *data
)
1383 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1384 struct vty
*vty
= data
;
1386 vty_out(vty
, "\tValid: %d ", pnhc
->valid
);
1387 nexthop_group_write_nexthop(vty
, &pnhc
->nexthop
);
1390 static void pbr_nht_json_nhg_nexthops(struct hash_bucket
*b
, void *data
)
1392 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1393 json_object
*all_hops
= data
;
1394 json_object
*this_hop
;
1396 this_hop
= json_object_new_object();
1397 nexthop_group_json_nexthop(this_hop
, &pnhc
->nexthop
);
1398 json_object_boolean_add(this_hop
, "valid", pnhc
->valid
);
1400 json_object_array_add(all_hops
, this_hop
);
1403 struct pbr_nht_show
{
1409 static void pbr_nht_show_nhg(struct hash_bucket
*b
, void *data
)
1411 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1412 struct pbr_nht_show
*pns
= data
;
1415 if (pns
->name
&& strcmp(pns
->name
, pnhgc
->name
) != 0)
1419 vty_out(vty
, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
1420 pnhgc
->name
, pnhgc
->table_id
, pnhgc
->valid
, pnhgc
->installed
);
1422 hash_iterate(pnhgc
->nhh
, pbr_nht_show_nhg_nexthops
, vty
);
1425 static void pbr_nht_json_nhg(struct hash_bucket
*b
, void *data
)
1427 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1428 struct pbr_nht_show
*pns
= data
;
1429 json_object
*j
, *this_group
, *group_hops
;
1431 if (pns
->name
&& strcmp(pns
->name
, pnhgc
->name
) != 0)
1435 this_group
= json_object_new_object();
1437 if (!j
|| !this_group
)
1440 json_object_int_add(this_group
, "id", pnhgc
->table_id
);
1441 json_object_string_add(this_group
, "name", pnhgc
->name
);
1442 json_object_boolean_add(this_group
, "valid", pnhgc
->valid
);
1443 json_object_boolean_add(this_group
, "installed", pnhgc
->installed
);
1445 group_hops
= json_object_new_array();
1448 hash_iterate(pnhgc
->nhh
, pbr_nht_json_nhg_nexthops
, group_hops
);
1449 json_object_object_add(this_group
, "nexthops", group_hops
);
1452 json_object_array_add(j
, this_group
);
1455 void pbr_nht_show_nexthop_group(struct vty
*vty
, const char *name
)
1457 struct pbr_nht_show pns
;
1462 hash_iterate(pbr_nhg_hash
, pbr_nht_show_nhg
, &pns
);
1465 void pbr_nht_json_nexthop_group(json_object
*j
, const char *name
)
1467 struct pbr_nht_show pns
;
1472 hash_iterate(pbr_nhg_hash
, pbr_nht_json_nhg
, &pns
);
1475 void pbr_nht_init(void)
1477 pbr_nhg_hash
= hash_create_size(
1478 16, pbr_nhg_hash_key
, pbr_nhg_hash_equal
, "PBR NHG Cache Hash");
1480 hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash
,
1481 pbr_nhrc_hash_equal
, "PBR NH Hash");
1482 pbr_nhg_allocated_id_hash
= hash_create_size(
1483 16, pbr_nhg_allocated_id_hash_key
,
1484 pbr_nhg_allocated_id_hash_equal
, "PBR Allocated Table Hash");
1486 pbr_nhg_low_table
= PBR_NHT_DEFAULT_LOW_TABLEID
;
1487 pbr_nhg_high_table
= PBR_NHT_DEFAULT_HIGH_TABLEID
;
1488 pbr_nhg_low_rule
= PBR_NHT_DEFAULT_LOW_RULE
;
1489 pbr_nhg_high_rule
= PBR_NHT_DEFAULT_HIGH_RULE
;
1491 /* First unallocated table is lowest in range on init */
1492 pbr_next_unallocated_table_id
= PBR_NHT_DEFAULT_LOW_TABLEID
;