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