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 static 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\n",
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
= (struct nexthop
*)nhop
;
253 pnhc
= hash_get(pnhgc
->nhh
, &pnhc_find
, pbr_nh_alloc
);
254 pnhc_find
.nexthop
= NULL
;
256 /* set parent pnhgc */
257 pnhc
->parent
= pnhgc
;
259 if (DEBUG_MODE_CHECK(&pbr_dbg_nht
, DEBUG_MODE_ALL
)) {
260 nexthop2str(nhop
, debugstr
, sizeof(debugstr
));
261 DEBUGD(&pbr_dbg_nht
, "%s: Added %s to nexthop-group %s",
262 __func__
, debugstr
, nhgc
->name
);
265 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
266 pbr_map_check_nh_group_change(nhgc
->name
);
268 if (nhop
->type
== NEXTHOP_TYPE_IFINDEX
269 || (nhop
->type
== NEXTHOP_TYPE_IPV6_IFINDEX
270 && IN6_IS_ADDR_LINKLOCAL(&nhop
->gate
.ipv6
))) {
271 struct interface
*ifp
;
273 ifp
= if_lookup_by_index(nhop
->ifindex
, nhop
->vrf_id
);
275 pbr_nht_nexthop_interface_update(ifp
);
279 void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd
*nhgc
,
280 const struct nexthop
*nhop
)
283 struct pbr_nexthop_group_cache pnhgc_find
= {};
284 struct pbr_nexthop_group_cache
*pnhgc
;
285 struct pbr_nexthop_cache pnhc_find
= {};
286 struct pbr_nexthop_cache
*pnhc
;
287 enum nexthop_types_t nh_type
= nhop
->type
;
289 /* find pnhgc by name */
290 strlcpy(pnhgc_find
.name
, nhgc
->name
, sizeof(pnhgc_find
.name
));
291 pnhgc
= hash_lookup(pbr_nhg_hash
, &pnhgc_find
);
293 /* delete pnhc from pnhgc->nhh */
294 pnhc_find
.nexthop
= (struct nexthop
*)nhop
;
295 pnhc
= hash_release(pnhgc
->nhh
, &pnhc_find
);
298 pbr_nh_delete(&pnhc
);
300 if (DEBUG_MODE_CHECK(&pbr_dbg_nht
, DEBUG_MODE_ALL
)) {
301 nexthop2str(nhop
, debugstr
, sizeof(debugstr
));
302 DEBUGD(&pbr_dbg_nht
, "%s: Removed %s from nexthop-group %s",
303 __func__
, debugstr
, nhgc
->name
);
306 if (pnhgc
->nhh
->count
)
307 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
309 pbr_nht_uninstall_nexthop_group(pnhgc
, nhgc
->nhg
, nh_type
);
311 pbr_map_check_nh_group_change(nhgc
->name
);
314 void pbr_nhgroup_delete_cb(const char *name
)
316 DEBUGD(&pbr_dbg_nht
, "%s: Removed nexthop-group %s", __func__
, name
);
318 /* delete group from all pbrms's */
319 pbr_nht_delete_group(name
);
321 pbr_map_check_nh_group_change(name
);
325 static struct pbr_nexthop_cache
*pbr_nht_lookup_nexthop(struct nexthop
*nexthop
)
331 static void pbr_nht_find_nhg_from_table_install(struct hash_bucket
*b
,
334 struct pbr_nexthop_group_cache
*pnhgc
=
335 (struct pbr_nexthop_group_cache
*)b
->data
;
336 uint32_t *table_id
= (uint32_t *)data
;
338 if (pnhgc
->table_id
== *table_id
) {
339 DEBUGD(&pbr_dbg_nht
, "%s: Table ID (%u) matches %s", __func__
,
340 *table_id
, pnhgc
->name
);
343 * If the table has been re-handled by zebra
344 * and we are already installed no need to do
347 if (!pnhgc
->installed
) {
348 pnhgc
->installed
= true;
349 pbr_map_schedule_policy_from_nhg(pnhgc
->name
);
354 void pbr_nht_route_installed_for_table(uint32_t table_id
)
356 hash_iterate(pbr_nhg_hash
, pbr_nht_find_nhg_from_table_install
,
360 static void pbr_nht_find_nhg_from_table_remove(struct hash_bucket
*b
,
366 void pbr_nht_route_removed_for_table(uint32_t table_id
)
368 hash_iterate(pbr_nhg_hash
, pbr_nht_find_nhg_from_table_remove
,
373 * Loop through all nexthops in a nexthop group to check that they are all the
374 * same. If they are not all the same, log this peculiarity.
377 * The nexthop group to check
380 * - AFI of last nexthop in the group
383 static afi_t
pbr_nht_which_afi(struct nexthop_group nhg
,
384 enum nexthop_types_t nh_type
)
386 struct nexthop
*nexthop
;
387 afi_t install_afi
= AFI_MAX
;
392 case NEXTHOP_TYPE_IPV4
:
393 case NEXTHOP_TYPE_IPV4_IFINDEX
:
395 case NEXTHOP_TYPE_IPV6
:
396 case NEXTHOP_TYPE_IPV6_IFINDEX
:
398 case NEXTHOP_TYPE_IFINDEX
:
399 case NEXTHOP_TYPE_BLACKHOLE
:
404 v6
= v4
= bh
= false;
406 for (ALL_NEXTHOPS(nhg
, nexthop
)) {
407 nh_type
= nexthop
->type
;
410 case NEXTHOP_TYPE_IFINDEX
:
412 case NEXTHOP_TYPE_IPV4
:
413 case NEXTHOP_TYPE_IPV4_IFINDEX
:
415 install_afi
= AFI_IP
;
417 case NEXTHOP_TYPE_IPV6
:
418 case NEXTHOP_TYPE_IPV6_IFINDEX
:
420 install_afi
= AFI_IP6
;
422 case NEXTHOP_TYPE_BLACKHOLE
:
428 /* Interface and/or blackhole nexthops only. */
430 install_afi
= AFI_MAX
;
434 "%s: Saw both V6 and V4 nexthops...using %s", __func__
,
435 afi2str(install_afi
));
436 if (bh
&& (v6
|| v4
))
438 "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.",
439 __func__
, v4
? "v4" : "", (v4
&& v6
) ? " and " : "",
445 static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
446 struct nexthop_group nhg
)
449 enum nexthop_types_t nh_type
= 0;
451 install_afi
= pbr_nht_which_afi(nhg
, nh_type
);
453 route_add(pnhgc
, nhg
, install_afi
);
457 pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache
*pnhgc
,
458 struct nexthop_group nhg
,
459 enum nexthop_types_t nh_type
)
463 install_afi
= pbr_nht_which_afi(nhg
, nh_type
);
465 pnhgc
->installed
= false;
466 pnhgc
->valid
= false;
467 route_delete(pnhgc
, install_afi
);
470 void pbr_nht_change_group(const char *name
)
472 struct nexthop_group_cmd
*nhgc
;
473 struct pbr_nexthop_group_cache
*pnhgc
;
474 struct pbr_nexthop_group_cache find
;
475 struct nexthop
*nhop
;
477 nhgc
= nhgc_find(name
);
481 memset(&find
, 0, sizeof(find
));
482 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
483 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
487 "%s: Could not find nexthop-group cache w/ name '%s'",
492 for (ALL_NEXTHOPS(nhgc
->nhg
, nhop
)) {
493 struct pbr_nexthop_cache lookup
;
494 struct pbr_nexthop_cache
*pnhc
;
496 lookup
.nexthop
= nhop
;
497 pnhc
= hash_lookup(pnhgc
->nhh
, &lookup
);
499 pnhc
= hash_get(pnhgc
->nhh
, &lookup
, pbr_nh_alloc
);
500 pnhc
->parent
= pnhgc
;
503 pbr_nht_install_nexthop_group(pnhgc
, nhgc
->nhg
);
506 char *pbr_nht_nexthop_make_name(char *name
, size_t l
,
507 uint32_t seqno
, char *buffer
)
509 snprintf(buffer
, l
, "%s%u", name
, seqno
);
513 void pbr_nht_add_individual_nexthop(struct pbr_map_sequence
*pbrms
,
514 const struct nexthop
*nhop
)
516 struct pbr_nexthop_group_cache
*pnhgc
;
517 struct pbr_nexthop_group_cache find
;
518 struct pbr_nexthop_cache
*pnhc
;
519 struct pbr_nexthop_cache lookup
;
521 char buf
[PBR_NHC_NAMELEN
];
523 pbrms
->nhg
= nexthop_group_new();
524 pbrms
->internal_nhg_name
= XSTRDUP(
526 pbr_nht_nexthop_make_name(pbrms
->parent
->name
, PBR_NHC_NAMELEN
,
530 memcpy(nh
, nhop
, sizeof(*nh
));
532 nexthop_group_add_sorted(pbrms
->nhg
, nh
);
534 memset(&find
, 0, sizeof(find
));
535 pbr_nht_nexthop_make_name(pbrms
->parent
->name
, PBR_NHC_NAMELEN
,
536 pbrms
->seqno
, find
.name
);
538 if (!pbr_nht_get_next_tableid(true)) {
540 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
541 __func__
, find
.name
);
545 if (!pbrms
->internal_nhg_name
)
546 pbrms
->internal_nhg_name
= XSTRDUP(MTYPE_TMP
, find
.name
);
548 pnhgc
= hash_get(pbr_nhg_hash
, &find
, pbr_nhgc_alloc
);
550 lookup
.nexthop
= pbrms
->nhg
->nexthop
;
551 pnhc
= hash_get(pnhgc
->nhh
, &lookup
, pbr_nh_alloc
);
552 pnhc
->parent
= pnhgc
;
553 pbr_nht_install_nexthop_group(pnhgc
, *pbrms
->nhg
);
556 static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence
*pbrms
)
558 struct pbr_nexthop_group_cache
*pnhgc
;
559 struct pbr_nexthop_group_cache find
;
560 struct pbr_nexthop_cache
*pnhc
;
561 struct pbr_nexthop_cache lup
;
563 enum nexthop_types_t nh_type
= 0;
565 memset(&find
, 0, sizeof(find
));
566 snprintf(find
.name
, sizeof(find
.name
), "%s", pbrms
->internal_nhg_name
);
567 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
569 nh
= pbrms
->nhg
->nexthop
;
572 pnhc
= hash_lookup(pnhgc
->nhh
, &lup
);
574 hash_release(pnhgc
->nhh
, pnhc
);
575 pbr_nh_delete(&pnhc
);
576 pbr_nht_uninstall_nexthop_group(pnhgc
, *pbrms
->nhg
, nh_type
);
578 hash_release(pbr_nhg_hash
, pnhgc
);
579 pbr_nhgc_delete(pnhgc
);
581 nexthop_group_delete(&pbrms
->nhg
);
582 XFREE(MTYPE_TMP
, pbrms
->internal_nhg_name
);
585 void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence
*pbrms
)
587 pbr_map_delete_nexthops(pbrms
);
589 pbr_nht_release_individual_nexthop(pbrms
);
592 struct pbr_nexthop_group_cache
*pbr_nht_add_group(const char *name
)
594 struct nexthop
*nhop
;
595 struct nexthop_group_cmd
*nhgc
;
596 struct pbr_nexthop_group_cache
*pnhgc
;
597 struct pbr_nexthop_group_cache lookup
;
599 if (!pbr_nht_get_next_tableid(true)) {
601 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
606 nhgc
= nhgc_find(name
);
609 DEBUGD(&pbr_dbg_nht
, "%s: Could not find nhgc with name: %s\n",
614 snprintf(lookup
.name
, sizeof(lookup
.name
), "%s", name
);
615 pnhgc
= hash_get(pbr_nhg_hash
, &lookup
, pbr_nhgc_alloc
);
616 DEBUGD(&pbr_dbg_nht
, "%s: Retrieved NHGC @ %p", __func__
, pnhgc
);
618 for (ALL_NEXTHOPS(nhgc
->nhg
, nhop
)) {
619 struct pbr_nexthop_cache lookupc
;
620 struct pbr_nexthop_cache
*pnhc
;
622 lookupc
.nexthop
= nhop
;
623 pnhc
= hash_lookup(pnhgc
->nhh
, &lookupc
);
625 pnhc
= hash_get(pnhgc
->nhh
, &lookupc
, pbr_nh_alloc
);
626 pnhc
->parent
= pnhgc
;
633 void pbr_nht_delete_group(const char *name
)
635 struct pbr_map_sequence
*pbrms
;
636 struct listnode
*snode
;
637 struct pbr_map
*pbrm
;
638 struct pbr_nexthop_group_cache pnhgc_find
;
639 struct pbr_nexthop_group_cache
*pnhgc
;
641 RB_FOREACH (pbrm
, pbr_map_entry_head
, &pbr_maps
) {
642 for (ALL_LIST_ELEMENTS_RO(pbrm
->seqnumbers
, snode
, pbrms
)) {
643 if (pbrms
->nhgrp_name
644 && strmatch(pbrms
->nhgrp_name
, name
)) {
645 pbrms
->reason
|= PBR_MAP_INVALID_NO_NEXTHOPS
;
647 pbrms
->internal_nhg_name
= NULL
;
653 strlcpy(pnhgc_find
.name
, name
, sizeof(pnhgc_find
.name
));
654 pnhgc
= hash_release(pbr_nhg_hash
, &pnhgc_find
);
655 pbr_nhgc_delete(pnhgc
);
658 bool pbr_nht_nexthop_valid(struct nexthop_group
*nhg
)
660 DEBUGD(&pbr_dbg_nht
, "%s: %p", __func__
, nhg
);
664 bool pbr_nht_nexthop_group_valid(const char *name
)
666 struct pbr_nexthop_group_cache
*pnhgc
;
667 struct pbr_nexthop_group_cache lookup
;
669 DEBUGD(&pbr_dbg_nht
, "%s: %s", __func__
, name
);
671 snprintf(lookup
.name
, sizeof(lookup
.name
), "%s", name
);
672 pnhgc
= hash_get(pbr_nhg_hash
, &lookup
, NULL
);
675 DEBUGD(&pbr_dbg_nht
, "%s: \t%d %d", __func__
, pnhgc
->valid
,
677 if (pnhgc
->valid
&& pnhgc
->installed
)
683 struct pbr_nht_individual
{
684 struct zapi_route
*nhr
;
685 struct interface
*ifp
;
691 pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache
*pnhc
,
692 const struct pbr_nht_individual
*pnhi
)
694 bool is_valid
= pnhc
->valid
;
696 if (!pnhi
->nhr
) /* It doesn't care about non-nexthop updates */
699 switch (pnhi
->nhr
->prefix
.family
) {
701 if (pnhc
->nexthop
->gate
.ipv4
.s_addr
702 != pnhi
->nhr
->prefix
.u
.prefix4
.s_addr
)
703 goto done
; /* Unrelated change */
706 if (memcmp(&pnhc
->nexthop
->gate
.ipv6
,
707 &pnhi
->nhr
->prefix
.u
.prefix6
, 16)
709 goto done
; /* Unrelated change */
713 if (!pnhi
->nhr
->nexthop_num
) {
718 if (pnhc
->nexthop
->type
== NEXTHOP_TYPE_IPV4_IFINDEX
719 || pnhc
->nexthop
->type
== NEXTHOP_TYPE_IPV6_IFINDEX
) {
721 /* GATEWAY_IFINDEX type shouldn't resolve to group */
722 if (pnhi
->nhr
->nexthop_num
> 1) {
727 /* If whatever we resolved to wasn't on the interface we
728 * specified. (i.e. not a connected route), its invalid.
730 if (pnhi
->nhr
->nexthops
[0].ifindex
!= pnhc
->nexthop
->ifindex
) {
739 pnhc
->valid
= is_valid
;
744 static bool pbr_nht_individual_nexthop_interface_update(
745 struct pbr_nexthop_cache
*pnhc
, const struct pbr_nht_individual
*pnhi
)
747 bool is_valid
= pnhc
->valid
;
749 if (!pnhi
->ifp
) /* It doesn't care about non-interface updates */
752 if (pnhc
->nexthop
->ifindex
753 != pnhi
->ifp
->ifindex
) /* Un-related interface */
756 is_valid
= !!if_is_up(pnhi
->ifp
);
759 pnhc
->valid
= is_valid
;
764 /* Given this update either from interface or nexthop tracking, re-validate this
767 * If the update is un-related, the subroutines shoud just return their cached
771 pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache
*pnhc
,
772 const struct pbr_nht_individual
*pnhi
)
774 assert(pnhi
->nhr
|| pnhi
->ifp
); /* Either nexthop or interface update */
776 switch (pnhc
->nexthop
->type
) {
777 case NEXTHOP_TYPE_IFINDEX
:
778 pbr_nht_individual_nexthop_interface_update(pnhc
, pnhi
);
780 case NEXTHOP_TYPE_IPV6_IFINDEX
:
781 if (IN6_IS_ADDR_LINKLOCAL(&pnhc
->nexthop
->gate
.ipv6
)) {
782 pbr_nht_individual_nexthop_interface_update(pnhc
, pnhi
);
785 /* Intentional fall thru */
786 case NEXTHOP_TYPE_IPV4_IFINDEX
:
787 case NEXTHOP_TYPE_IPV4
:
788 case NEXTHOP_TYPE_IPV6
:
789 pbr_nht_individual_nexthop_gw_update(pnhc
, pnhi
);
791 case NEXTHOP_TYPE_BLACKHOLE
:
797 static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket
*b
,
800 struct pbr_nexthop_cache
*pnhc
= b
->data
;
801 struct pbr_nht_individual
*pnhi
= data
;
802 char buf
[PREFIX_STRLEN
];
805 old_valid
= pnhc
->valid
;
807 pbr_nht_individual_nexthop_update(pnhc
, pnhi
);
809 DEBUGD(&pbr_dbg_nht
, "\tFound %s: old: %d new: %d",
810 prefix2str(&pnhi
->nhr
->prefix
, buf
, sizeof(buf
)), old_valid
,
817 static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket
*b
,
820 struct pbr_nexthop_cache
*pnhc
= b
->data
;
821 struct nexthop_group
*nhg
= data
;
822 struct nexthop
*nh
= NULL
;
824 copy_nexthops(&nh
, pnhc
->nexthop
, NULL
);
826 _nexthop_add(&nhg
->nexthop
, nh
);
830 pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group
*nhg
,
831 struct pbr_nexthop_group_cache
*pnhgc
)
833 hash_iterate(pnhgc
->nhh
, pbr_nexthop_group_cache_iterate_to_group
, nhg
);
836 static void pbr_nht_nexthop_update_lookup(struct hash_bucket
*b
, void *data
)
838 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
839 struct pbr_nht_individual pnhi
= {};
840 struct nexthop_group nhg
= {};
843 old_valid
= pnhgc
->valid
;
845 pnhi
.nhr
= (struct zapi_route
*)data
;
847 hash_iterate(pnhgc
->nhh
, pbr_nht_individual_nexthop_update_lookup
,
851 * If any of the specified nexthops are valid we are valid
853 pnhgc
->valid
= !!pnhi
.valid
;
856 pbr_nexthop_group_cache_to_nexthop_group(&nhg
, pnhgc
);
857 pbr_nht_install_nexthop_group(pnhgc
, nhg
);
858 /* Don't need copied nexthops anymore */
859 nexthops_free(nhg
.nexthop
);
862 if (old_valid
!= pnhgc
->valid
)
863 pbr_map_check_nh_group_change(pnhgc
->name
);
866 void pbr_nht_nexthop_update(struct zapi_route
*nhr
)
868 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_update_lookup
, nhr
);
872 pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket
*b
,
875 struct pbr_nexthop_cache
*pnhc
= b
->data
;
876 struct pbr_nht_individual
*pnhi
= data
;
879 old_valid
= pnhc
->valid
;
881 pbr_nht_individual_nexthop_update(pnhc
, pnhi
);
883 DEBUGD(&pbr_dbg_nht
, "\tFound %s: old: %d new: %d", pnhi
->ifp
->name
,
884 old_valid
, pnhc
->valid
);
890 static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket
*b
,
893 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
894 struct pbr_nht_individual pnhi
= {};
897 old_valid
= pnhgc
->valid
;
901 hash_iterate(pnhgc
->nhh
,
902 pbr_nht_individual_nexthop_interface_update_lookup
, &pnhi
);
905 * If any of the specified nexthops are valid we are valid
907 pnhgc
->valid
= !!pnhi
.valid
;
909 if (old_valid
!= pnhgc
->valid
)
910 pbr_map_check_nh_group_change(pnhgc
->name
);
913 void pbr_nht_nexthop_interface_update(struct interface
*ifp
)
915 hash_iterate(pbr_nhg_hash
, pbr_nht_nexthop_interface_update_lookup
,
919 static uint32_t pbr_nhg_hash_key(const void *arg
)
921 const struct pbr_nexthop_group_cache
*nhgc
= arg
;
923 return jhash(&nhgc
->name
, strlen(nhgc
->name
), 0x52c34a96);
926 static bool pbr_nhg_hash_equal(const void *arg1
, const void *arg2
)
928 const struct pbr_nexthop_group_cache
*nhgc1
=
929 (const struct pbr_nexthop_group_cache
*)arg1
;
930 const struct pbr_nexthop_group_cache
*nhgc2
=
931 (const struct pbr_nexthop_group_cache
*)arg2
;
933 return !strcmp(nhgc1
->name
, nhgc2
->name
);
936 uint32_t pbr_nht_get_next_tableid(bool peek
)
941 for (i
= pbr_nhg_low_table
; i
<= pbr_nhg_high_table
; i
++) {
942 if (!nhg_tableid
[i
]) {
949 nhg_tableid
[i
] = !peek
;
955 void pbr_nht_set_tableid_range(uint32_t low
, uint32_t high
)
957 pbr_nhg_low_table
= low
;
958 pbr_nhg_high_table
= high
;
961 void pbr_nht_write_table_range(struct vty
*vty
)
963 if (pbr_nhg_low_table
!= PBR_NHT_DEFAULT_LOW_TABLEID
964 || pbr_nhg_high_table
!= PBR_NHT_DEFAULT_HIGH_TABLEID
) {
965 vty_out(vty
, "pbr table range %u %u\n", pbr_nhg_low_table
,
970 uint32_t pbr_nht_get_next_rule(uint32_t seqno
)
972 return seqno
+ pbr_nhg_low_rule
- 1;
974 void pbr_nht_set_rule_range(uint32_t low
, uint32_t high
)
976 pbr_nhg_low_rule
= low
;
977 pbr_nhg_high_rule
= high
;
980 void pbr_nht_write_rule_range(struct vty
*vty
)
982 if (pbr_nhg_low_rule
!= PBR_NHT_DEFAULT_LOW_RULE
983 || pbr_nhg_high_rule
!= PBR_NHT_DEFAULT_HIGH_RULE
) {
984 vty_out(vty
, "pbr rule range %u %u\n", pbr_nhg_low_rule
,
989 uint32_t pbr_nht_get_table(const char *name
)
991 struct pbr_nexthop_group_cache find
;
992 struct pbr_nexthop_group_cache
*pnhgc
;
994 memset(&find
, 0, sizeof(find
));
995 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
996 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
1000 "%s: Could not find nexthop-group cache w/ name '%s'",
1005 return pnhgc
->table_id
;
1008 bool pbr_nht_get_installed(const char *name
)
1010 struct pbr_nexthop_group_cache find
;
1011 struct pbr_nexthop_group_cache
*pnhgc
;
1013 memset(&find
, 0, sizeof(find
));
1014 snprintf(find
.name
, sizeof(find
.name
), "%s", name
);
1016 pnhgc
= hash_lookup(pbr_nhg_hash
, &find
);
1021 return pnhgc
->installed
;
1024 static void pbr_nht_show_nhg_nexthops(struct hash_bucket
*b
, void *data
)
1026 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1027 struct vty
*vty
= data
;
1029 vty_out(vty
, "\tValid: %d ", pnhc
->valid
);
1030 nexthop_group_write_nexthop(vty
, pnhc
->nexthop
);
1033 static void pbr_nht_json_nhg_nexthops(struct hash_bucket
*b
, void *data
)
1035 struct pbr_nexthop_cache
*pnhc
= b
->data
;
1036 json_object
*all_hops
= data
;
1037 json_object
*this_hop
;
1039 this_hop
= json_object_new_object();
1040 nexthop_group_json_nexthop(this_hop
, pnhc
->nexthop
);
1041 json_object_boolean_add(this_hop
, "valid", pnhc
->valid
);
1043 json_object_array_add(all_hops
, this_hop
);
1046 struct pbr_nht_show
{
1052 static void pbr_nht_show_nhg(struct hash_bucket
*b
, void *data
)
1054 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1055 struct pbr_nht_show
*pns
= data
;
1058 if (pns
->name
&& strcmp(pns
->name
, pnhgc
->name
) != 0)
1062 vty_out(vty
, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
1063 pnhgc
->name
, pnhgc
->table_id
, pnhgc
->valid
, pnhgc
->installed
);
1065 hash_iterate(pnhgc
->nhh
, pbr_nht_show_nhg_nexthops
, vty
);
1068 static void pbr_nht_json_nhg(struct hash_bucket
*b
, void *data
)
1070 struct pbr_nexthop_group_cache
*pnhgc
= b
->data
;
1071 struct pbr_nht_show
*pns
= data
;
1072 json_object
*j
, *this_group
, *group_hops
;
1074 if (pns
->name
&& strcmp(pns
->name
, pnhgc
->name
) != 0)
1078 this_group
= json_object_new_object();
1080 if (!j
|| !this_group
)
1083 json_object_int_add(this_group
, "id", pnhgc
->table_id
);
1084 json_object_string_add(this_group
, "name", pnhgc
->name
);
1085 json_object_boolean_add(this_group
, "valid", pnhgc
->valid
);
1086 json_object_boolean_add(this_group
, "installed", pnhgc
->installed
);
1088 group_hops
= json_object_new_array();
1091 hash_iterate(pnhgc
->nhh
, pbr_nht_json_nhg_nexthops
, group_hops
);
1092 json_object_object_add(this_group
, "nexthops", group_hops
);
1095 json_object_array_add(j
, this_group
);
1098 void pbr_nht_show_nexthop_group(struct vty
*vty
, const char *name
)
1100 struct pbr_nht_show pns
;
1105 hash_iterate(pbr_nhg_hash
, pbr_nht_show_nhg
, &pns
);
1108 void pbr_nht_json_nexthop_group(json_object
*j
, const char *name
)
1110 struct pbr_nht_show pns
;
1115 hash_iterate(pbr_nhg_hash
, pbr_nht_json_nhg
, &pns
);
1118 void pbr_nht_init(void)
1120 pbr_nhg_hash
= hash_create_size(
1121 16, pbr_nhg_hash_key
, pbr_nhg_hash_equal
, "PBR NHG Cache Hash");
1123 hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash
,
1124 pbr_nhrc_hash_equal
, "PBR NH Hash");
1126 pbr_nhg_low_table
= PBR_NHT_DEFAULT_LOW_TABLEID
;
1127 pbr_nhg_high_table
= PBR_NHT_DEFAULT_HIGH_TABLEID
;
1128 pbr_nhg_low_rule
= PBR_NHT_DEFAULT_LOW_RULE
;
1129 pbr_nhg_high_rule
= PBR_NHT_DEFAULT_HIGH_RULE
;
1130 memset(&nhg_tableid
, 0, 65535 * sizeof(uint8_t));