]> git.proxmox.com Git - mirror_frr.git/blame - pbrd/pbr_nht.c
pim6d: Initialise VRF related stuffs in Northbound
[mirror_frr.git] / pbrd / pbr_nht.c
CommitLineData
e5c83d9b
DS
1/*
2 * PBR-nht Code
3 * Copyright (C) 2018 Cumulus Networks, Inc.
4 * Donald Sharp
5 *
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
9 * later version.
10 *
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.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20#include <zebra.h>
21
22#include <log.h>
23#include <nexthop.h>
50d89650
SW
24#include "nexthop_group.h"
25#include "nexthop_group_private.h"
e5c83d9b
DS
26#include <hash.h>
27#include <jhash.h>
28#include <vty.h>
29#include <zclient.h>
b13e5ad6 30#include <debug.h>
e5c83d9b
DS
31
32#include "pbrd/pbr_nht.h"
33#include "pbrd/pbr_map.h"
e5c83d9b
DS
34#include "pbrd/pbr_zebra.h"
35#include "pbrd/pbr_memory.h"
36#include "pbrd/pbr_debug.h"
37
bf8d3d6a 38DEFINE_MTYPE_STATIC(PBRD, PBR_NHG, "PBR Nexthop Groups");
e5c83d9b 39
fcf29c69 40struct hash *pbr_nhg_hash;
b13e5ad6 41static struct hash *pbr_nhrc_hash;
389571aa 42static struct hash *pbr_nhg_allocated_id_hash;
e5c83d9b
DS
43
44static uint32_t pbr_nhg_low_table;
45static uint32_t pbr_nhg_high_table;
389571aa 46static uint32_t pbr_next_unallocated_table_id;
e5c83d9b
DS
47static uint32_t pbr_nhg_low_rule;
48static uint32_t pbr_nhg_high_rule;
e5c83d9b 49
b13e5ad6
DS
50static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
51 struct nexthop_group nhg);
ff9799c3
DS
52static void
53pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
54 struct nexthop_group nhg,
aafac994 55 enum nexthop_types_t nh_type);
b13e5ad6
DS
56
57/*
58 * Nexthop refcount.
59 */
60struct nhrc {
61 struct nexthop nexthop;
62 unsigned int refcount;
63};
64
65/* Hash functions for pbr_nhrc_hash ---------------------------------------- */
66
67static void *pbr_nhrc_hash_alloc(void *p)
68{
69 struct nhrc *nhrc = XCALLOC(MTYPE_PBR_NHG, sizeof(struct nhrc));
70 nhrc->nexthop = *(struct nexthop *)p;
ad9255f8
SW
71 nhrc->nexthop.next = NULL;
72 nhrc->nexthop.prev = NULL;
b13e5ad6
DS
73 return nhrc;
74}
75
74df8d6d 76static bool pbr_nhrc_hash_equal(const void *arg1, const void *arg2)
b13e5ad6
DS
77{
78 const struct nexthop *nh1, *nh2;
79
80 nh1 = arg1;
81 nh2 = arg2;
82
83 return nexthop_same(nh1, nh2);
84}
85
86/* ------------------------------------------------------------------------- */
87
e5c83d9b
DS
88static void *pbr_nh_alloc(void *p)
89{
90 struct pbr_nexthop_cache *new;
91 struct pbr_nexthop_cache *pnhc = (struct pbr_nexthop_cache *)p;
b13e5ad6 92 struct nhrc *nhrc;
e5c83d9b
DS
93
94 new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new));
734bf907
DS
95 nhrc = hash_get(pbr_nhrc_hash, &pnhc->nexthop, pbr_nhrc_hash_alloc);
96 new->nexthop = nhrc->nexthop;
b13e5ad6
DS
97
98 /* Decremented again in pbr_nh_delete */
99 ++nhrc->refcount;
e5c83d9b 100
15569c58 101 DEBUGD(&pbr_dbg_nht, "%s: Sending nexthop to Zebra", __func__);
e5c83d9b 102
734bf907 103 pbr_send_rnh(&new->nexthop, true);
e5c83d9b
DS
104
105 new->valid = false;
106 return new;
107}
108
109static void pbr_nh_delete(struct pbr_nexthop_cache **pnhc)
110{
b13e5ad6
DS
111 struct nhrc *nhrc;
112
734bf907 113 nhrc = hash_lookup(pbr_nhrc_hash, &((*pnhc)->nexthop));
b13e5ad6
DS
114
115 if (nhrc)
116 --nhrc->refcount;
117 if (!nhrc || nhrc->refcount == 0) {
118 DEBUGD(&pbr_dbg_nht, "%s: Removing nexthop from Zebra",
15569c58 119 __func__);
734bf907 120 pbr_send_rnh(&((*pnhc)->nexthop), false);
b13e5ad6
DS
121 }
122 if (nhrc && nhrc->refcount == 0) {
123 hash_release(pbr_nhrc_hash, nhrc);
124 XFREE(MTYPE_PBR_NHG, nhrc);
125 }
e5c83d9b
DS
126
127 XFREE(MTYPE_PBR_NHG, *pnhc);
128}
129
e3b78da8 130static void pbr_nh_delete_iterate(struct hash_bucket *b, void *p)
b13e5ad6
DS
131{
132 pbr_nh_delete((struct pbr_nexthop_cache **)&b->data);
133}
134
d8b87afe 135static uint32_t pbr_nh_hash_key(const void *arg)
e5c83d9b
DS
136{
137 uint32_t key;
d8b87afe 138 const struct pbr_nexthop_cache *pbrnc = arg;
e5c83d9b 139
734bf907 140 key = nexthop_hash(&pbrnc->nexthop);
e5c83d9b
DS
141
142 return key;
143}
144
74df8d6d 145static bool pbr_nh_hash_equal(const void *arg1, const void *arg2)
e5c83d9b
DS
146{
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;
151
734bf907 152 if (pbrnc1->nexthop.vrf_id != pbrnc2->nexthop.vrf_id)
74df8d6d 153 return false;
e5c83d9b 154
734bf907 155 if (pbrnc1->nexthop.ifindex != pbrnc2->nexthop.ifindex)
74df8d6d 156 return false;
e5c83d9b 157
734bf907 158 if (pbrnc1->nexthop.type != pbrnc2->nexthop.type)
74df8d6d 159 return false;
e5c83d9b 160
734bf907 161 switch (pbrnc1->nexthop.type) {
e5c83d9b 162 case NEXTHOP_TYPE_IFINDEX:
734bf907 163 return pbrnc1->nexthop.ifindex == pbrnc2->nexthop.ifindex;
e5c83d9b
DS
164 case NEXTHOP_TYPE_IPV4_IFINDEX:
165 case NEXTHOP_TYPE_IPV4:
734bf907
DS
166 return pbrnc1->nexthop.gate.ipv4.s_addr
167 == pbrnc2->nexthop.gate.ipv4.s_addr;
e5c83d9b
DS
168 case NEXTHOP_TYPE_IPV6_IFINDEX:
169 case NEXTHOP_TYPE_IPV6:
734bf907
DS
170 return !memcmp(&pbrnc1->nexthop.gate.ipv6,
171 &pbrnc2->nexthop.gate.ipv6, 16);
e5c83d9b 172 case NEXTHOP_TYPE_BLACKHOLE:
734bf907 173 return pbrnc1->nexthop.bh_type == pbrnc2->nexthop.bh_type;
e5c83d9b
DS
174 }
175
176 /*
177 * We should not get here
178 */
74df8d6d 179 return false;
e5c83d9b
DS
180}
181
b13e5ad6
DS
182static void pbr_nhgc_delete(struct pbr_nexthop_group_cache *p)
183{
184 hash_iterate(p->nhh, pbr_nh_delete_iterate, NULL);
185 hash_free(p->nhh);
186 XFREE(MTYPE_PBR_NHG, p);
187}
188
189static void *pbr_nhgc_alloc(void *p)
190{
191 struct pbr_nexthop_group_cache *new;
192 struct pbr_nexthop_group_cache *pnhgc =
193 (struct pbr_nexthop_group_cache *)p;
194
195 new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new));
196
65b88efa 197 strlcpy(new->name, pnhgc->name, sizeof(pnhgc->name));
389571aa 198 pbr_nht_reserve_next_table_id(new);
b13e5ad6 199
15569c58
DA
200 DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u", __func__,
201 new->name, new->table_id);
b13e5ad6
DS
202
203 new->nhh = hash_create_size(8, pbr_nh_hash_key, pbr_nh_hash_equal,
204 "PBR NH Cache Hash");
205 return new;
206}
207
208
e5c83d9b
DS
209void pbr_nhgroup_add_cb(const char *name)
210{
b13e5ad6
DS
211 struct pbr_nexthop_group_cache *pnhgc;
212 struct nexthop_group_cmd *nhgc;
e5c83d9b 213
b13e5ad6 214 nhgc = nhgc_find(name);
68a63f60
QY
215
216 if (!nhgc) {
1d5453d6 217 DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s",
15569c58 218 __func__, name);
68a63f60
QY
219 return;
220 }
221
b13e5ad6 222 pnhgc = pbr_nht_add_group(name);
e5c83d9b 223
a4044dc1
QY
224 if (!pnhgc)
225 return;
226
15569c58 227 DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __func__, name);
b13e5ad6 228
b13e5ad6 229 pbr_map_check_nh_group_change(name);
e5c83d9b
DS
230}
231
b13e5ad6 232void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc,
e5c83d9b
DS
233 const struct nexthop *nhop)
234{
b13e5ad6 235 char debugstr[256];
3e300703 236 struct pbr_nexthop_group_cache pnhgc_find = {};
b13e5ad6 237 struct pbr_nexthop_group_cache *pnhgc;
3e300703 238 struct pbr_nexthop_cache pnhc_find = {};
b13e5ad6
DS
239 struct pbr_nexthop_cache *pnhc;
240
241 /* find pnhgc by name */
242 strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
64f37745
WC
243 pnhgc = hash_lookup(pbr_nhg_hash, &pnhgc_find);
244
245 if (!pnhgc) {
246 /* Check if configured table range is exhausted */
247 if (!pbr_nht_has_unallocated_table()) {
248 zlog_warn(
249 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
250 __func__, nhgc->name);
251 return;
252 }
253
254 /* No nhgc but range not exhausted? Then alloc it */
255 pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc);
256 }
e5c83d9b 257
b13e5ad6 258 /* create & insert new pnhc into pnhgc->nhh */
734bf907 259 pnhc_find.nexthop = *nhop;
b13e5ad6 260 pnhc = hash_get(pnhgc->nhh, &pnhc_find, pbr_nh_alloc);
b13e5ad6
DS
261
262 /* set parent pnhgc */
263 pnhc->parent = pnhgc;
e5c83d9b 264
b13e5ad6
DS
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",
15569c58 268 __func__, debugstr, nhgc->name);
b13e5ad6
DS
269 }
270
271 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
272 pbr_map_check_nh_group_change(nhgc->name);
a106a408 273
cb254f41
SW
274 if (nhop->type == NEXTHOP_TYPE_IFINDEX
275 || (nhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
276 && IN6_IS_ADDR_LINKLOCAL(&nhop->gate.ipv6))) {
a106a408
RW
277 struct interface *ifp;
278
279 ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id);
280 if (ifp)
281 pbr_nht_nexthop_interface_update(ifp);
282 }
e5c83d9b
DS
283}
284
b13e5ad6 285void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc,
e5c83d9b
DS
286 const struct nexthop *nhop)
287{
b13e5ad6 288 char debugstr[256];
3e300703 289 struct pbr_nexthop_group_cache pnhgc_find = {};
b13e5ad6 290 struct pbr_nexthop_group_cache *pnhgc;
3e300703 291 struct pbr_nexthop_cache pnhc_find = {};
b13e5ad6 292 struct pbr_nexthop_cache *pnhc;
aafac994 293 enum nexthop_types_t nh_type = nhop->type;
b13e5ad6
DS
294
295 /* find pnhgc by name */
296 strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
a4044dc1 297 pnhgc = hash_lookup(pbr_nhg_hash, &pnhgc_find);
b13e5ad6 298
389571aa
WC
299 /*
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
302 */
303 if (!pnhgc)
304 return;
305
b13e5ad6 306 /* delete pnhc from pnhgc->nhh */
734bf907 307 pnhc_find.nexthop = *nhop;
b13e5ad6
DS
308 pnhc = hash_release(pnhgc->nhh, &pnhc_find);
309
310 /* delete pnhc */
311 pbr_nh_delete(&pnhc);
e5c83d9b 312
b13e5ad6
DS
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",
15569c58 316 __func__, debugstr, nhgc->name);
b13e5ad6 317 }
e5c83d9b 318
ff9799c3
DS
319 if (pnhgc->nhh->count)
320 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
321 else
aafac994 322 pbr_nht_uninstall_nexthop_group(pnhgc, nhgc->nhg, nh_type);
ff9799c3 323
b13e5ad6 324 pbr_map_check_nh_group_change(nhgc->name);
e5c83d9b
DS
325}
326
327void pbr_nhgroup_delete_cb(const char *name)
328{
15569c58 329 DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s", __func__, name);
b13e5ad6 330
ff9799c3
DS
331 /* delete group from all pbrms's */
332 pbr_nht_delete_group(name);
333
b13e5ad6 334 pbr_map_check_nh_group_change(name);
e5c83d9b
DS
335}
336
09813729
SW
337static void
338pbr_nht_find_nhg_from_table_update(struct pbr_nexthop_group_cache *pnhgc,
339 uint32_t table_id, bool installed)
340{
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,
344 pnhgc->name);
345
346 pnhgc->installed = installed;
347 pnhgc->valid = installed;
348 pbr_map_schedule_policy_from_nhg(pnhgc->name, pnhgc->installed);
349 }
350}
351
e3b78da8 352static void pbr_nht_find_nhg_from_table_install(struct hash_bucket *b,
e5c83d9b
DS
353 void *data)
354{
355 struct pbr_nexthop_group_cache *pnhgc =
356 (struct pbr_nexthop_group_cache *)b->data;
09813729
SW
357 uint32_t table_id = *(uint32_t *)data;
358
359 pbr_nht_find_nhg_from_table_update(pnhgc, table_id, true);
e5c83d9b
DS
360}
361
362void pbr_nht_route_installed_for_table(uint32_t table_id)
363{
364 hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_install,
365 &table_id);
366}
367
e3b78da8 368static void pbr_nht_find_nhg_from_table_remove(struct hash_bucket *b,
e5c83d9b
DS
369 void *data)
370{
09813729
SW
371 struct pbr_nexthop_group_cache *pnhgc =
372 (struct pbr_nexthop_group_cache *)b->data;
373 uint32_t table_id = *(uint32_t *)data;
374
375 pbr_nht_find_nhg_from_table_update(pnhgc, table_id, false);
e5c83d9b
DS
376}
377
378void pbr_nht_route_removed_for_table(uint32_t table_id)
379{
380 hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_remove,
381 &table_id);
382}
383
384/*
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.
387 *
388 * nhg
389 * The nexthop group to check
390 *
391 * Returns:
392 * - AFI of last nexthop in the group
393 * - AFI_MAX on error
394 */
ff9799c3 395static afi_t pbr_nht_which_afi(struct nexthop_group nhg,
aafac994 396 enum nexthop_types_t nh_type)
e5c83d9b
DS
397{
398 struct nexthop *nexthop;
399 afi_t install_afi = AFI_MAX;
400 bool v6, v4, bh;
d3765386 401
268c24ee
RW
402 if (nh_type) {
403 switch (nh_type) {
404 case NEXTHOP_TYPE_IPV4:
405 case NEXTHOP_TYPE_IPV4_IFINDEX:
406 return AFI_IP;
407 case NEXTHOP_TYPE_IPV6:
408 case NEXTHOP_TYPE_IPV6_IFINDEX:
409 return AFI_IP6;
410 case NEXTHOP_TYPE_IFINDEX:
411 case NEXTHOP_TYPE_BLACKHOLE:
412 return AFI_MAX;
413 }
414 }
415
e5c83d9b
DS
416 v6 = v4 = bh = false;
417
268c24ee
RW
418 for (ALL_NEXTHOPS(nhg, nexthop)) {
419 nh_type = nexthop->type;
420
421 switch (nh_type) {
422 case NEXTHOP_TYPE_IFINDEX:
423 break;
424 case NEXTHOP_TYPE_IPV4:
425 case NEXTHOP_TYPE_IPV4_IFINDEX:
426 v6 = true;
427 install_afi = AFI_IP;
428 break;
429 case NEXTHOP_TYPE_IPV6:
430 case NEXTHOP_TYPE_IPV6_IFINDEX:
431 v4 = true;
432 install_afi = AFI_IP6;
433 break;
434 case NEXTHOP_TYPE_BLACKHOLE:
435 bh = true;
e5c83d9b
DS
436 break;
437 }
438 }
439
268c24ee
RW
440 /* Interface and/or blackhole nexthops only. */
441 if (!v4 && !v6)
ff9799c3 442 install_afi = AFI_MAX;
ff9799c3 443
e5c83d9b
DS
444 if (!bh && v6 && v4)
445 DEBUGD(&pbr_dbg_nht,
5e81f5dd
DS
446 "%s: Saw both V6 and V4 nexthops...using %s", __func__,
447 afi2str(install_afi));
e5c83d9b
DS
448 if (bh && (v6 || v4))
449 DEBUGD(&pbr_dbg_nht,
450 "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.",
5e81f5dd
DS
451 __func__, v4 ? "v4" : "", (v4 && v6) ? " and " : "",
452 v6 ? "v6" : "");
e5c83d9b
DS
453
454 return install_afi;
455}
456
457static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
458 struct nexthop_group nhg)
459{
460 afi_t install_afi;
aafac994 461 enum nexthop_types_t nh_type = 0;
e5c83d9b 462
aafac994 463 install_afi = pbr_nht_which_afi(nhg, nh_type);
e5c83d9b 464
e5c83d9b
DS
465 route_add(pnhgc, nhg, install_afi);
466}
467
468static void
469pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
ff9799c3 470 struct nexthop_group nhg,
aafac994 471 enum nexthop_types_t nh_type)
e5c83d9b
DS
472{
473 afi_t install_afi;
474
aafac994 475 install_afi = pbr_nht_which_afi(nhg, nh_type);
e5c83d9b
DS
476
477 pnhgc->installed = false;
478 pnhgc->valid = false;
479 route_delete(pnhgc, install_afi);
480}
481
482void pbr_nht_change_group(const char *name)
483{
484 struct nexthop_group_cmd *nhgc;
485 struct pbr_nexthop_group_cache *pnhgc;
486 struct pbr_nexthop_group_cache find;
487 struct nexthop *nhop;
488
489 nhgc = nhgc_find(name);
490 if (!nhgc)
491 return;
492
493 memset(&find, 0, sizeof(find));
6612590d 494 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
495 pnhgc = hash_lookup(pbr_nhg_hash, &find);
496
497 if (!pnhgc) {
498 DEBUGD(&pbr_dbg_nht,
499 "%s: Could not find nexthop-group cache w/ name '%s'",
5e81f5dd 500 __func__, name);
e5c83d9b
DS
501 return;
502 }
503
504 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
505 struct pbr_nexthop_cache lookup;
506 struct pbr_nexthop_cache *pnhc;
507
734bf907 508 lookup.nexthop = *nhop;
e5c83d9b
DS
509 pnhc = hash_lookup(pnhgc->nhh, &lookup);
510 if (!pnhc) {
511 pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
512 pnhc->parent = pnhgc;
513 }
514 }
515 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
516}
517
518char *pbr_nht_nexthop_make_name(char *name, size_t l,
519 uint32_t seqno, char *buffer)
520{
521 snprintf(buffer, l, "%s%u", name, seqno);
522 return buffer;
523}
524
9a7ea213
SW
525/* Set data derived from nhg in pbrms */
526void pbr_nht_set_seq_nhg_data(struct pbr_map_sequence *pbrms,
527 const struct nexthop_group_cmd *nhgc)
528{
529 const struct nexthop_group *nhg;
530
531 if (!nhgc)
532 return;
533
534 nhg = &nhgc->nhg;
535 if (!nhg->nexthop)
536 return;
537
538 switch (nhg->nexthop->type) {
539 case NEXTHOP_TYPE_IPV6:
540 case NEXTHOP_TYPE_IPV6_IFINDEX:
541 pbrms->family = AF_INET6;
542 break;
543 case NEXTHOP_TYPE_IPV4:
544 case NEXTHOP_TYPE_IPV4_IFINDEX:
545 pbrms->family = AF_INET;
546 default:
547 break;
548 }
549}
550
551/* Configure a routemap sequence to use a given nexthop group */
552void pbr_nht_set_seq_nhg(struct pbr_map_sequence *pbrms, const char *name)
553{
554 struct nexthop_group_cmd *nhgc;
555
556 if (!name)
557 return;
558
559 pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name);
560
561 nhgc = nhgc_find(name);
562 if (!nhgc)
563 return;
564
565 pbr_nht_set_seq_nhg_data(pbrms, nhgc);
566}
567
f143cffa
SW
568void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms,
569 const struct nexthop *nhop)
e5c83d9b
DS
570{
571 struct pbr_nexthop_group_cache *pnhgc;
572 struct pbr_nexthop_group_cache find;
573 struct pbr_nexthop_cache *pnhc;
e5c83d9b 574 struct pbr_nexthop_cache lookup;
f143cffa
SW
575 struct nexthop *nh;
576 char buf[PBR_NHC_NAMELEN];
577
578 pbrms->nhg = nexthop_group_new();
579 pbrms->internal_nhg_name = XSTRDUP(
580 MTYPE_TMP,
581 pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
582 pbrms->seqno, buf));
583
584 nh = nexthop_new();
585 memcpy(nh, nhop, sizeof(*nh));
586
587 nexthop_group_add_sorted(pbrms->nhg, nh);
e5c83d9b 588
e5c83d9b 589 memset(&find, 0, sizeof(find));
06210d1f 590 pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
e5c83d9b 591 pbrms->seqno, find.name);
a4044dc1 592
389571aa 593 if (!pbr_nht_has_unallocated_table()) {
a4044dc1
QY
594 zlog_warn(
595 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
15569c58 596 __func__, find.name);
a4044dc1
QY
597 return;
598 }
599
e5c83d9b
DS
600 if (!pbrms->internal_nhg_name)
601 pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name);
602
603 pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc);
604
734bf907 605 lookup.nexthop = *pbrms->nhg->nexthop;
e5c83d9b
DS
606 pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
607 pnhc->parent = pnhgc;
fcf29c69
DS
608 if (nhop->vrf_id != VRF_DEFAULT) {
609 struct vrf *vrf = vrf_lookup_by_id(nhop->vrf_id);
610
611 if (vrf)
612 strlcpy(pnhc->vrf_name, vrf->name,
613 sizeof(pnhc->vrf_name));
614 }
7cbdabff
DS
615
616 if (nhop->ifindex != 0) {
617 struct interface *ifp =
618 if_lookup_by_index(nhop->ifindex, nhop->vrf_id);
619
620 if (ifp)
621 strlcpy(pnhc->intf_name, ifp->name,
622 sizeof(pnhc->intf_name));
623 }
e5c83d9b
DS
624 pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg);
625}
626
f143cffa 627static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence *pbrms)
e5c83d9b
DS
628{
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;
e5c83d9b 633 struct nexthop *nh;
aafac994 634 enum nexthop_types_t nh_type = 0;
e5c83d9b 635
e5c83d9b 636 memset(&find, 0, sizeof(find));
6612590d 637 snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name);
e5c83d9b
DS
638 pnhgc = hash_lookup(pbr_nhg_hash, &find);
639
640 nh = pbrms->nhg->nexthop;
aafac994 641 nh_type = nh->type;
734bf907 642 lup.nexthop = *nh;
e5c83d9b
DS
643 pnhc = hash_lookup(pnhgc->nhh, &lup);
644 pnhc->parent = NULL;
645 hash_release(pnhgc->nhh, pnhc);
646 pbr_nh_delete(&pnhc);
aafac994 647 pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_type);
e5c83d9b
DS
648
649 hash_release(pbr_nhg_hash, pnhgc);
1f375577 650 pbr_nhgc_delete(pnhgc);
e5c83d9b 651
e5c83d9b
DS
652 nexthop_group_delete(&pbrms->nhg);
653 XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
654}
655
f143cffa
SW
656void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
657{
658 pbr_map_delete_nexthops(pbrms);
659
660 pbr_nht_release_individual_nexthop(pbrms);
661}
662
b13e5ad6 663struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name)
e5c83d9b
DS
664{
665 struct nexthop *nhop;
666 struct nexthop_group_cmd *nhgc;
667 struct pbr_nexthop_group_cache *pnhgc;
668 struct pbr_nexthop_group_cache lookup;
669
389571aa 670 if (!pbr_nht_has_unallocated_table()) {
a4044dc1
QY
671 zlog_warn(
672 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
5e81f5dd 673 __func__, name);
a4044dc1
QY
674 return NULL;
675 }
676
e5c83d9b
DS
677 nhgc = nhgc_find(name);
678
679 if (!nhgc) {
1d5453d6 680 DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s",
5e81f5dd 681 __func__, name);
b13e5ad6 682 return NULL;
e5c83d9b
DS
683 }
684
6612590d 685 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
e5c83d9b 686 pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc);
5e81f5dd 687 DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __func__, pnhgc);
e5c83d9b
DS
688
689 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
7fe96307 690 struct pbr_nexthop_cache lookupc;
e5c83d9b
DS
691 struct pbr_nexthop_cache *pnhc;
692
734bf907 693 lookupc.nexthop = *nhop;
7fe96307 694 pnhc = hash_lookup(pnhgc->nhh, &lookupc);
e5c83d9b 695 if (!pnhc) {
7fe96307 696 pnhc = hash_get(pnhgc->nhh, &lookupc, pbr_nh_alloc);
e5c83d9b
DS
697 pnhc->parent = pnhgc;
698 }
699 }
b13e5ad6
DS
700
701 return pnhgc;
e5c83d9b
DS
702}
703
704void pbr_nht_delete_group(const char *name)
705{
706 struct pbr_map_sequence *pbrms;
707 struct listnode *snode;
708 struct pbr_map *pbrm;
b13e5ad6
DS
709 struct pbr_nexthop_group_cache pnhgc_find;
710 struct pbr_nexthop_group_cache *pnhgc;
e5c83d9b
DS
711
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
b13e5ad6 715 && strmatch(pbrms->nhgrp_name, name)) {
e5c83d9b 716 pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
b13e5ad6
DS
717 pbrms->nhg = NULL;
718 pbrms->internal_nhg_name = NULL;
e5c83d9b
DS
719 pbrm->valid = false;
720 }
721 }
722 }
b13e5ad6
DS
723
724 strlcpy(pnhgc_find.name, name, sizeof(pnhgc_find.name));
725 pnhgc = hash_release(pbr_nhg_hash, &pnhgc_find);
389571aa
WC
726
727 /*
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
730 */
731 if (!pnhgc)
732 return;
733
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();
737
b13e5ad6 738 pbr_nhgc_delete(pnhgc);
e5c83d9b
DS
739}
740
741bool pbr_nht_nexthop_valid(struct nexthop_group *nhg)
742{
15569c58 743 DEBUGD(&pbr_dbg_nht, "%s: %p", __func__, nhg);
e5c83d9b
DS
744 return true;
745}
746
747bool pbr_nht_nexthop_group_valid(const char *name)
748{
749 struct pbr_nexthop_group_cache *pnhgc;
750 struct pbr_nexthop_group_cache lookup;
751
15569c58 752 DEBUGD(&pbr_dbg_nht, "%s: %s", __func__, name);
e5c83d9b 753
6612590d 754 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
e5c83d9b
DS
755 pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL);
756 if (!pnhgc)
757 return false;
1d5453d6 758 DEBUGD(&pbr_dbg_nht, "%s: %d %d", __func__, pnhgc->valid,
e5c83d9b
DS
759 pnhgc->installed);
760 if (pnhgc->valid && pnhgc->installed)
761 return true;
762
763 return false;
764}
765
766struct pbr_nht_individual {
767 struct zapi_route *nhr;
a106a408 768 struct interface *ifp;
fcf29c69
DS
769 struct pbr_vrf *pbr_vrf;
770 struct pbr_nexthop_cache *pnhc;
9d961247 771 vrf_id_t old_vrf_id;
e5c83d9b 772
4bf97ce1 773 bool valid;
fcf29c69
DS
774
775 bool nhr_matched;
e5c83d9b
DS
776};
777
8babeb1a
SW
778static bool
779pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache *pnhc,
c59f754d 780 struct pbr_nht_individual *pnhi)
e5c83d9b 781{
8babeb1a 782 bool is_valid = pnhc->valid;
e5c83d9b 783
89527add
DS
784 /*
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
791 * interface event.
792 */
3558b8b8 793 if (!pnhi->nhr) {
89527add
DS
794 switch (pnhc->nexthop.type) {
795 case NEXTHOP_TYPE_BLACKHOLE:
e6b00e3f
SW
796 case NEXTHOP_TYPE_IPV4:
797 case NEXTHOP_TYPE_IPV6:
798 goto done;
89527add
DS
799 case NEXTHOP_TYPE_IFINDEX:
800 case NEXTHOP_TYPE_IPV4_IFINDEX:
801 case NEXTHOP_TYPE_IPV6_IFINDEX:
e6b00e3f
SW
802 if (pnhc->nexthop.ifindex == pnhi->ifp->ifindex)
803 is_valid = if_is_up(pnhi->ifp);
804 goto done;
89527add
DS
805 }
806
8babeb1a 807 goto done;
89527add 808 }
e5c83d9b 809
3558b8b8
IR
810 switch (pnhi->nhr->prefix.family) {
811 case AF_INET:
812 if (pnhc->nexthop.gate.ipv4.s_addr
813 != pnhi->nhr->prefix.u.prefix4.s_addr)
814 goto done; /* Unrelated change */
815 break;
816 case AF_INET6:
817 if (memcmp(&pnhc->nexthop.gate.ipv6,
818 &pnhi->nhr->prefix.u.prefix6, 16)
819 != 0)
820 goto done; /* Unrelated change */
821 break;
8babeb1a
SW
822 }
823
c59f754d 824 pnhi->nhr_matched = true;
8babeb1a
SW
825 if (!pnhi->nhr->nexthop_num) {
826 is_valid = false;
827 goto done;
828 }
829
734bf907
DS
830 if (pnhc->nexthop.type == NEXTHOP_TYPE_IPV4_IFINDEX
831 || pnhc->nexthop.type == NEXTHOP_TYPE_IPV6_IFINDEX) {
8babeb1a
SW
832
833 /* GATEWAY_IFINDEX type shouldn't resolve to group */
834 if (pnhi->nhr->nexthop_num > 1) {
835 is_valid = false;
836 goto done;
837 }
838
839 /* If whatever we resolved to wasn't on the interface we
840 * specified. (i.e. not a connected route), its invalid.
841 */
734bf907 842 if (pnhi->nhr->nexthops[0].ifindex != pnhc->nexthop.ifindex) {
8babeb1a
SW
843 is_valid = false;
844 goto done;
845 }
846 }
847
848 is_valid = true;
849
850done:
851 pnhc->valid = is_valid;
852
853 return pnhc->valid;
854}
855
c59f754d
WC
856static bool
857pbr_nht_individual_nexthop_interface_update(struct pbr_nexthop_cache *pnhc,
858 struct pbr_nht_individual *pnhi)
8babeb1a
SW
859{
860 bool is_valid = pnhc->valid;
861
862 if (!pnhi->ifp) /* It doesn't care about non-interface updates */
863 goto done;
864
734bf907 865 if (pnhc->nexthop.ifindex
8babeb1a
SW
866 != pnhi->ifp->ifindex) /* Un-related interface */
867 goto done;
868
c59f754d 869 pnhi->nhr_matched = true;
8babeb1a
SW
870 is_valid = !!if_is_up(pnhi->ifp);
871
872done:
873 pnhc->valid = is_valid;
874
875 return pnhc->valid;
876}
877
878/* Given this update either from interface or nexthop tracking, re-validate this
879 * nexthop.
880 *
881 * If the update is un-related, the subroutines shoud just return their cached
882 * valid state.
883 */
c59f754d
WC
884static void pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache *pnhc,
885 struct pbr_nht_individual *pnhi)
8babeb1a
SW
886{
887 assert(pnhi->nhr || pnhi->ifp); /* Either nexthop or interface update */
888
734bf907 889 switch (pnhc->nexthop.type) {
8babeb1a
SW
890 case NEXTHOP_TYPE_IFINDEX:
891 pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
892 break;
cb254f41 893 case NEXTHOP_TYPE_IPV6_IFINDEX:
734bf907 894 if (IN6_IS_ADDR_LINKLOCAL(&pnhc->nexthop.gate.ipv6)) {
cb254f41
SW
895 pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
896 break;
897 }
898 /* Intentional fall thru */
899 case NEXTHOP_TYPE_IPV4_IFINDEX:
8babeb1a
SW
900 case NEXTHOP_TYPE_IPV4:
901 case NEXTHOP_TYPE_IPV6:
8babeb1a
SW
902 pbr_nht_individual_nexthop_gw_update(pnhc, pnhi);
903 break;
904 case NEXTHOP_TYPE_BLACKHOLE:
905 pnhc->valid = true;
e5c83d9b
DS
906 break;
907 }
8babeb1a
SW
908}
909
910static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket *b,
911 void *data)
912{
913 struct pbr_nexthop_cache *pnhc = b->data;
914 struct pbr_nht_individual *pnhi = data;
8babeb1a
SW
915 bool old_valid;
916
917 old_valid = pnhc->valid;
918
919 pbr_nht_individual_nexthop_update(pnhc, pnhi);
e5c83d9b 920
1d5453d6 921 DEBUGD(&pbr_dbg_nht, " Found %pFX: old: %d new: %d",
2dbe669b 922 &pnhi->nhr->prefix, old_valid, pnhc->valid);
e5c83d9b 923
e5c83d9b 924 if (pnhc->valid)
4bf97ce1 925 pnhi->valid = true;
e5c83d9b
DS
926}
927
b822b93a
SW
928static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket *b,
929 void *data)
930{
931 struct pbr_nexthop_cache *pnhc = b->data;
932 struct nexthop_group *nhg = data;
933 struct nexthop *nh = NULL;
934
734bf907 935 copy_nexthops(&nh, &pnhc->nexthop, NULL);
b822b93a 936
50d89650 937 _nexthop_add(&nhg->nexthop, nh);
b822b93a
SW
938}
939
940static void
941pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group *nhg,
942 struct pbr_nexthop_group_cache *pnhgc)
943{
944 hash_iterate(pnhgc->nhh, pbr_nexthop_group_cache_iterate_to_group, nhg);
945}
946
e3b78da8 947static void pbr_nht_nexthop_update_lookup(struct hash_bucket *b, void *data)
e5c83d9b
DS
948{
949 struct pbr_nexthop_group_cache *pnhgc = b->data;
8babeb1a 950 struct pbr_nht_individual pnhi = {};
b822b93a 951 struct nexthop_group nhg = {};
b13e5ad6
DS
952 bool old_valid;
953
954 old_valid = pnhgc->valid;
e5c83d9b
DS
955
956 pnhi.nhr = (struct zapi_route *)data;
4bf97ce1 957 pnhi.valid = false;
c59f754d 958 pnhi.nhr_matched = false;
e5c83d9b
DS
959 hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_update_lookup,
960 &pnhi);
961
c59f754d
WC
962 if (!pnhi.nhr_matched)
963 return;
964
e5c83d9b
DS
965 /*
966 * If any of the specified nexthops are valid we are valid
967 */
968 pnhgc->valid = !!pnhi.valid;
b13e5ad6 969
6db1188f
SW
970 pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc);
971
972 if (pnhgc->valid)
b822b93a 973 pbr_nht_install_nexthop_group(pnhgc, nhg);
6db1188f
SW
974 else
975 pbr_nht_uninstall_nexthop_group(pnhgc, nhg, 0);
976
977 /* Don't need copied nexthops anymore */
978 nexthops_free(nhg.nexthop);
b822b93a 979
b13e5ad6
DS
980 if (old_valid != pnhgc->valid)
981 pbr_map_check_nh_group_change(pnhgc->name);
e5c83d9b
DS
982}
983
984void pbr_nht_nexthop_update(struct zapi_route *nhr)
985{
986 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr);
987}
988
9d961247
DS
989struct nhrc_vrf_info {
990 struct pbr_vrf *pbr_vrf;
991 uint32_t old_vrf_id;
992 struct nhrc *nhrc;
993};
994
995static int pbr_nht_nhrc_vrf_change(struct hash_bucket *b, void *data)
996{
997 struct nhrc *nhrc = b->data;
998 struct nhrc_vrf_info *nhrcvi = data;
999
1000 if (nhrc->nexthop.vrf_id == nhrcvi->old_vrf_id) {
1001 nhrcvi->nhrc = nhrc;
1002 return HASHWALK_ABORT;
1003 }
1004
1005 return HASHWALK_CONTINUE;
1006}
1007
1008static int pbr_nht_individual_nexthop_vrf_handle(struct hash_bucket *b,
1009 void *data)
fcf29c69
DS
1010{
1011 struct pbr_nexthop_cache *pnhc = b->data;
1012 struct pbr_nht_individual *pnhi = data;
1013
9d961247
DS
1014 if (pnhc->looked_at == true)
1015 return HASHWALK_CONTINUE;
1016
734bf907 1017 if (pnhc->nexthop.vrf_id == VRF_DEFAULT)
9d961247 1018 return HASHWALK_CONTINUE;
fcf29c69 1019
9d961247
DS
1020 if (strncmp(pnhc->vrf_name, pbr_vrf_name(pnhi->pbr_vrf),
1021 sizeof(pnhc->vrf_name))
1022 == 0) {
fcf29c69 1023 pnhi->pnhc = pnhc;
9d961247 1024
734bf907 1025 if (pnhc->nexthop.vrf_id != pbr_vrf_id(pnhi->pbr_vrf)) {
9d961247
DS
1026 struct nhrc_vrf_info nhrcvi;
1027
1028 memset(&nhrcvi, 0, sizeof(nhrcvi));
1029 nhrcvi.pbr_vrf = pnhi->pbr_vrf;
734bf907 1030 nhrcvi.old_vrf_id = pnhc->nexthop.vrf_id;
9d961247
DS
1031
1032 pnhi->nhr_matched = true;
734bf907 1033 pnhi->old_vrf_id = pnhc->nexthop.vrf_id;
9d961247
DS
1034
1035 do {
1036 nhrcvi.nhrc = NULL;
1037 hash_walk(pbr_nhrc_hash,
1038 pbr_nht_nhrc_vrf_change, &nhrcvi);
1039 if (nhrcvi.nhrc) {
1040 hash_release(pbr_nhrc_hash,
1041 nhrcvi.nhrc);
1042 nhrcvi.nhrc->nexthop.vrf_id =
1043 pbr_vrf_id(pnhi->pbr_vrf);
1044 hash_get(pbr_nhrc_hash, nhrcvi.nhrc,
1045 hash_alloc_intern);
1046 pbr_send_rnh(&nhrcvi.nhrc->nexthop, true);
1047 }
1048 } while (nhrcvi.nhrc);
1049 }
1050
1051 pnhc->looked_at = true;
1052 return HASHWALK_ABORT;
fcf29c69 1053 }
9d961247
DS
1054
1055 return HASHWALK_CONTINUE;
1056}
1057
1058static void pbr_nht_clear_looked_at(struct hash_bucket *b, void *data)
1059{
1060 struct pbr_nexthop_cache *pnhc = b->data;
1061
1062 pnhc->looked_at = false;
fcf29c69
DS
1063}
1064
1065static void pbr_nht_nexthop_vrf_handle(struct hash_bucket *b, void *data)
1066{
1067 struct pbr_nexthop_group_cache *pnhgc = b->data;
1068 struct pbr_vrf *pbr_vrf = data;
1069 struct pbr_nht_individual pnhi = {};
fcf29c69 1070
9d961247
DS
1071 hash_iterate(pnhgc->nhh, pbr_nht_clear_looked_at, NULL);
1072 memset(&pnhi, 0, sizeof(pnhi));
1073 pnhi.pbr_vrf = pbr_vrf;
fcf29c69 1074 do {
9d961247
DS
1075 struct pbr_nexthop_cache *pnhc;
1076
1077 pnhi.pnhc = NULL;
1078 hash_walk(pnhgc->nhh, pbr_nht_individual_nexthop_vrf_handle,
1079 &pnhi);
fcf29c69
DS
1080
1081 if (!pnhi.pnhc)
1082 continue;
1083
9d961247 1084 pnhc = pnhi.pnhc;
734bf907 1085 pnhc->nexthop.vrf_id = pnhi.old_vrf_id;
fcf29c69 1086 pnhi.pnhc = hash_release(pnhgc->nhh, pnhi.pnhc);
9d961247 1087 if (pnhi.pnhc) {
734bf907 1088 pnhi.pnhc->nexthop.vrf_id = pbr_vrf_id(pbr_vrf);
fcf29c69 1089
9d961247
DS
1090 hash_get(pnhgc->nhh, pnhi.pnhc, hash_alloc_intern);
1091 } else
734bf907 1092 pnhc->nexthop.vrf_id = pbr_vrf_id(pbr_vrf);
fcf29c69
DS
1093
1094 pbr_map_check_vrf_nh_group_change(pnhgc->name, pbr_vrf,
9d961247 1095 pnhi.old_vrf_id);
fcf29c69
DS
1096 } while (pnhi.pnhc);
1097}
1098
1099void pbr_nht_vrf_update(struct pbr_vrf *pbr_vrf)
1100{
1101 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_vrf_handle, pbr_vrf);
1102}
1103
7cbdabff
DS
1104static void pbr_nht_individual_nexthop_interface_handle(struct hash_bucket *b,
1105 void *data)
1106{
1107 struct pbr_nexthop_cache *pnhc = b->data;
1108 struct pbr_nht_individual *pnhi = data;
1109
734bf907 1110 if (pnhc->nexthop.ifindex == 0)
7cbdabff
DS
1111 return;
1112
1113 if ((strncmp(pnhc->intf_name, pnhi->ifp->name, sizeof(pnhc->intf_name))
1114 == 0)
734bf907 1115 && pnhc->nexthop.ifindex != pnhi->ifp->ifindex)
7cbdabff
DS
1116 pnhi->pnhc = pnhc;
1117}
1118
1119static void pbr_nht_nexthop_interface_handle(struct hash_bucket *b, void *data)
1120{
1121 struct pbr_nexthop_group_cache *pnhgc = b->data;
1122 struct interface *ifp = data;
1123 struct pbr_nht_individual pnhi = {};
1124 struct nhrc *nhrc;
1125 uint32_t old_ifindex;
1126
1127 do {
1128 memset(&pnhi, 0, sizeof(pnhi));
1129 pnhi.ifp = ifp;
1130 hash_iterate(pnhgc->nhh,
1131 pbr_nht_individual_nexthop_interface_handle,
1132 &pnhi);
1133
1134 if (!pnhi.pnhc)
1135 continue;
1136
1137 pnhi.pnhc = hash_release(pnhgc->nhh, pnhi.pnhc);
734bf907 1138 old_ifindex = pnhi.pnhc->nexthop.ifindex;
7cbdabff 1139
734bf907 1140 nhrc = hash_lookup(pbr_nhrc_hash, &pnhi.pnhc->nexthop);
7cbdabff
DS
1141 if (nhrc) {
1142 hash_release(pbr_nhrc_hash, nhrc);
1143 nhrc->nexthop.ifindex = ifp->ifindex;
1144 hash_get(pbr_nhrc_hash, nhrc, hash_alloc_intern);
1145 }
734bf907 1146 pnhi.pnhc->nexthop.ifindex = ifp->ifindex;
7cbdabff
DS
1147
1148 hash_get(pnhgc->nhh, pnhi.pnhc, hash_alloc_intern);
1149
1150 pbr_map_check_interface_nh_group_change(pnhgc->name, ifp,
1151 old_ifindex);
1152 } while (pnhi.pnhc);
1153}
1154
1155void pbr_nht_interface_update(struct interface *ifp)
1156{
1157 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_handle, ifp);
1158}
1159
a106a408 1160static void
7f5818fb 1161pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket *b,
a106a408
RW
1162 void *data)
1163{
1164 struct pbr_nexthop_cache *pnhc = b->data;
1165 struct pbr_nht_individual *pnhi = data;
1166 bool old_valid;
1167
1168 old_valid = pnhc->valid;
1169
8babeb1a 1170 pbr_nht_individual_nexthop_update(pnhc, pnhi);
a106a408 1171
1d5453d6 1172 DEBUGD(&pbr_dbg_nht, " Found %s: old: %d new: %d", pnhi->ifp->name,
a106a408
RW
1173 old_valid, pnhc->valid);
1174
1175 if (pnhc->valid)
4bf97ce1 1176 pnhi->valid = true;
a106a408
RW
1177}
1178
7f5818fb 1179static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket *b,
a106a408
RW
1180 void *data)
1181{
1182 struct pbr_nexthop_group_cache *pnhgc = b->data;
8babeb1a 1183 struct pbr_nht_individual pnhi = {};
89527add 1184 struct nexthop_group nhg = {};
a106a408
RW
1185 bool old_valid;
1186
1187 old_valid = pnhgc->valid;
1188
1189 pnhi.ifp = data;
4bf97ce1 1190 pnhi.valid = false;
a106a408
RW
1191 hash_iterate(pnhgc->nhh,
1192 pbr_nht_individual_nexthop_interface_update_lookup, &pnhi);
1193
1194 /*
1195 * If any of the specified nexthops are valid we are valid
1196 */
4bf97ce1 1197 pnhgc->valid = pnhi.valid;
a106a408 1198
89527add
DS
1199 pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc);
1200
1201 if (pnhgc->valid)
1202 pbr_nht_install_nexthop_group(pnhgc, nhg);
1203 else
1204 pbr_nht_uninstall_nexthop_group(pnhgc, nhg, 0);
1205
1206 nexthops_free(nhg.nexthop);
1207
a106a408
RW
1208 if (old_valid != pnhgc->valid)
1209 pbr_map_check_nh_group_change(pnhgc->name);
1210}
1211
1212void pbr_nht_nexthop_interface_update(struct interface *ifp)
1213{
1214 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_update_lookup,
1215 ifp);
1216}
1217
389571aa
WC
1218static bool pbr_nhg_allocated_id_hash_equal(const void *arg1, const void *arg2)
1219{
1220 const struct pbr_nexthop_group_cache *left, *right;
1221
1222 left = (const struct pbr_nexthop_group_cache *)arg1;
1223 right = (const struct pbr_nexthop_group_cache *)arg2;
1224
1225 return left->table_id == right->table_id;
1226}
1227
1228static uint32_t pbr_nhg_allocated_id_hash_key(const void *arg)
1229{
1230 const struct pbr_nexthop_group_cache *nhgc = arg;
1231
1232 /* table_id makes elements in this hash unique */
1233 return nhgc->table_id;
1234}
1235
d8b87afe 1236static uint32_t pbr_nhg_hash_key(const void *arg)
e5c83d9b 1237{
d8b87afe 1238 const struct pbr_nexthop_group_cache *nhgc = arg;
e5c83d9b
DS
1239
1240 return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96);
1241}
1242
74df8d6d 1243static bool pbr_nhg_hash_equal(const void *arg1, const void *arg2)
e5c83d9b
DS
1244{
1245 const struct pbr_nexthop_group_cache *nhgc1 =
1246 (const struct pbr_nexthop_group_cache *)arg1;
1247 const struct pbr_nexthop_group_cache *nhgc2 =
1248 (const struct pbr_nexthop_group_cache *)arg2;
1249
1250 return !strcmp(nhgc1->name, nhgc2->name);
1251}
1252
389571aa 1253uint32_t pbr_nht_find_next_unallocated_table_id(void)
e5c83d9b 1254{
389571aa 1255 struct pbr_nexthop_group_cache iter;
e5c83d9b 1256
389571aa
WC
1257 /*
1258 * Find the smallest unallocated table id
1259 * This can be non-trivial considering nhg removals / shifting upper &
1260 * lower bounds, so start at the lowest in the range and continue until
1261 * an unallocated space is found
1262 */
1263 for (iter.table_id = pbr_nhg_low_table;
1264 iter.table_id < pbr_nhg_high_table; ++iter.table_id)
1265 if (!hash_lookup(pbr_nhg_allocated_id_hash, &iter))
1266 return iter.table_id;
1267
1268 /* Configured range is full, cannot install anywhere */
1269 return 0;
1270}
e5c83d9b 1271
389571aa
WC
1272bool pbr_nht_has_unallocated_table(void)
1273{
1274 return !!pbr_next_unallocated_table_id;
1275}
e5c83d9b 1276
389571aa
WC
1277void pbr_nht_update_next_unallocated_table_id(void)
1278{
1279 pbr_next_unallocated_table_id =
1280 pbr_nht_find_next_unallocated_table_id();
1281}
1282
1283uint32_t pbr_nht_reserve_next_table_id(struct pbr_nexthop_group_cache *nhgc)
1284{
1285 /* Nothing to reserve if all tables in range already used */
1286 if (!pbr_next_unallocated_table_id)
e5c83d9b 1287 return 0;
389571aa
WC
1288
1289 /* Reserve this table id */
1290 nhgc->table_id = pbr_next_unallocated_table_id;
1291
1292 /* Mark table id as allocated in id-indexed hash */
1293 hash_get(pbr_nhg_allocated_id_hash, nhgc, hash_alloc_intern);
1294
1295 /* Pre-compute the next unallocated table id */
1296 pbr_nht_update_next_unallocated_table_id();
1297
1298 /* Present caller with reserved table id */
1299 return nhgc->table_id;
e5c83d9b
DS
1300}
1301
1302void pbr_nht_set_tableid_range(uint32_t low, uint32_t high)
1303{
1304 pbr_nhg_low_table = low;
1305 pbr_nhg_high_table = high;
389571aa
WC
1306
1307 /* Re-compute next unallocated id within new range */
1308 pbr_nht_update_next_unallocated_table_id();
e5c83d9b
DS
1309}
1310
1311void pbr_nht_write_table_range(struct vty *vty)
1312{
1313 if (pbr_nhg_low_table != PBR_NHT_DEFAULT_LOW_TABLEID
1314 || pbr_nhg_high_table != PBR_NHT_DEFAULT_HIGH_TABLEID) {
1315 vty_out(vty, "pbr table range %u %u\n", pbr_nhg_low_table,
1316 pbr_nhg_high_table);
1317 }
1318}
1319
1320uint32_t pbr_nht_get_next_rule(uint32_t seqno)
1321{
1322 return seqno + pbr_nhg_low_rule - 1;
1323}
1324void pbr_nht_set_rule_range(uint32_t low, uint32_t high)
1325{
1326 pbr_nhg_low_rule = low;
1327 pbr_nhg_high_rule = high;
1328}
1329
1330void pbr_nht_write_rule_range(struct vty *vty)
1331{
1332 if (pbr_nhg_low_rule != PBR_NHT_DEFAULT_LOW_RULE
1333 || pbr_nhg_high_rule != PBR_NHT_DEFAULT_HIGH_RULE) {
1334 vty_out(vty, "pbr rule range %u %u\n", pbr_nhg_low_rule,
1335 pbr_nhg_high_rule);
1336 }
1337}
1338
1339uint32_t pbr_nht_get_table(const char *name)
1340{
1341 struct pbr_nexthop_group_cache find;
1342 struct pbr_nexthop_group_cache *pnhgc;
1343
1344 memset(&find, 0, sizeof(find));
6612590d 1345 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
1346 pnhgc = hash_lookup(pbr_nhg_hash, &find);
1347
1348 if (!pnhgc) {
1349 DEBUGD(&pbr_dbg_nht,
1350 "%s: Could not find nexthop-group cache w/ name '%s'",
15569c58 1351 __func__, name);
e5c83d9b
DS
1352 return 5000;
1353 }
1354
1355 return pnhgc->table_id;
1356}
1357
1358bool pbr_nht_get_installed(const char *name)
1359{
1360 struct pbr_nexthop_group_cache find;
1361 struct pbr_nexthop_group_cache *pnhgc;
1362
1363 memset(&find, 0, sizeof(find));
6612590d 1364 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
1365
1366 pnhgc = hash_lookup(pbr_nhg_hash, &find);
1367
d3765386 1368 if (!pnhgc)
e5c83d9b 1369 return false;
e5c83d9b
DS
1370
1371 return pnhgc->installed;
1372}
1373
e3b78da8 1374static void pbr_nht_show_nhg_nexthops(struct hash_bucket *b, void *data)
e5c83d9b
DS
1375{
1376 struct pbr_nexthop_cache *pnhc = b->data;
1377 struct vty *vty = data;
1378
57cdafc4 1379 vty_out(vty, "\tValid: %d ", pnhc->valid);
734bf907 1380 nexthop_group_write_nexthop(vty, &pnhc->nexthop);
e5c83d9b
DS
1381}
1382
010dd8ed
WC
1383static void pbr_nht_json_nhg_nexthops(struct hash_bucket *b, void *data)
1384{
1385 struct pbr_nexthop_cache *pnhc = b->data;
1386 json_object *all_hops = data;
1387 json_object *this_hop;
1388
1389 this_hop = json_object_new_object();
734bf907 1390 nexthop_group_json_nexthop(this_hop, &pnhc->nexthop);
3e81618d 1391 json_object_boolean_add(this_hop, "valid", pnhc->valid);
010dd8ed
WC
1392
1393 json_object_array_add(all_hops, this_hop);
1394}
1395
e5c83d9b
DS
1396struct pbr_nht_show {
1397 struct vty *vty;
010dd8ed 1398 json_object *json;
e5c83d9b
DS
1399 const char *name;
1400};
1401
e3b78da8 1402static void pbr_nht_show_nhg(struct hash_bucket *b, void *data)
e5c83d9b
DS
1403{
1404 struct pbr_nexthop_group_cache *pnhgc = b->data;
1405 struct pbr_nht_show *pns = data;
1406 struct vty *vty;
1407
1408 if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
1409 return;
1410
1411 vty = pns->vty;
1412 vty_out(vty, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
1413 pnhgc->name, pnhgc->table_id, pnhgc->valid, pnhgc->installed);
1414
1415 hash_iterate(pnhgc->nhh, pbr_nht_show_nhg_nexthops, vty);
1416}
1417
010dd8ed
WC
1418static void pbr_nht_json_nhg(struct hash_bucket *b, void *data)
1419{
1420 struct pbr_nexthop_group_cache *pnhgc = b->data;
1421 struct pbr_nht_show *pns = data;
1422 json_object *j, *this_group, *group_hops;
1423
1424 if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
1425 return;
1426
1427 j = pns->json;
1428 this_group = json_object_new_object();
1429
1430 if (!j || !this_group)
1431 return;
1432
010dd8ed 1433 json_object_int_add(this_group, "id", pnhgc->table_id);
81c0078e 1434 json_object_string_add(this_group, "name", pnhgc->name);
3e81618d
WC
1435 json_object_boolean_add(this_group, "valid", pnhgc->valid);
1436 json_object_boolean_add(this_group, "installed", pnhgc->installed);
010dd8ed
WC
1437
1438 group_hops = json_object_new_array();
1439
1440 if (group_hops) {
1441 hash_iterate(pnhgc->nhh, pbr_nht_json_nhg_nexthops, group_hops);
1442 json_object_object_add(this_group, "nexthops", group_hops);
1443 }
1444
dadba1a2 1445 json_object_array_add(j, this_group);
010dd8ed
WC
1446}
1447
e5c83d9b
DS
1448void pbr_nht_show_nexthop_group(struct vty *vty, const char *name)
1449{
1450 struct pbr_nht_show pns;
1451
1452 pns.vty = vty;
1453 pns.name = name;
1454
1455 hash_iterate(pbr_nhg_hash, pbr_nht_show_nhg, &pns);
1456}
1457
010dd8ed
WC
1458void pbr_nht_json_nexthop_group(json_object *j, const char *name)
1459{
1460 struct pbr_nht_show pns;
1461
1462 pns.name = name;
1463 pns.json = j;
1464
1465 hash_iterate(pbr_nhg_hash, pbr_nht_json_nhg, &pns);
1466}
1467
e5c83d9b
DS
1468void pbr_nht_init(void)
1469{
1470 pbr_nhg_hash = hash_create_size(
1471 16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash");
b13e5ad6 1472 pbr_nhrc_hash =
d8b87afe 1473 hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash,
b13e5ad6 1474 pbr_nhrc_hash_equal, "PBR NH Hash");
389571aa
WC
1475 pbr_nhg_allocated_id_hash = hash_create_size(
1476 16, pbr_nhg_allocated_id_hash_key,
1477 pbr_nhg_allocated_id_hash_equal, "PBR Allocated Table Hash");
e5c83d9b
DS
1478
1479 pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID;
1480 pbr_nhg_high_table = PBR_NHT_DEFAULT_HIGH_TABLEID;
1481 pbr_nhg_low_rule = PBR_NHT_DEFAULT_LOW_RULE;
1482 pbr_nhg_high_rule = PBR_NHT_DEFAULT_HIGH_RULE;
389571aa
WC
1483
1484 /* First unallocated table is lowest in range on init */
1485 pbr_next_unallocated_table_id = PBR_NHT_DEFAULT_LOW_TABLEID;
e5c83d9b 1486}