]> git.proxmox.com Git - mirror_frr.git/blame - pbrd/pbr_nht.c
Merge pull request #8616 from donaldsharp/pim_ordering
[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;
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) {
1d5453d6 216 DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s",
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
09813729
SW
323static void
324pbr_nht_find_nhg_from_table_update(struct pbr_nexthop_group_cache *pnhgc,
325 uint32_t table_id, bool installed)
326{
327 if (pnhgc->table_id == table_id) {
328 DEBUGD(&pbr_dbg_nht, "%s: %s: Table ID (%u) matches %s",
329 __func__, (installed ? "install" : "remove"), table_id,
330 pnhgc->name);
331
332 pnhgc->installed = installed;
333 pnhgc->valid = installed;
334 pbr_map_schedule_policy_from_nhg(pnhgc->name, pnhgc->installed);
335 }
336}
337
e3b78da8 338static void pbr_nht_find_nhg_from_table_install(struct hash_bucket *b,
e5c83d9b
DS
339 void *data)
340{
341 struct pbr_nexthop_group_cache *pnhgc =
342 (struct pbr_nexthop_group_cache *)b->data;
09813729
SW
343 uint32_t table_id = *(uint32_t *)data;
344
345 pbr_nht_find_nhg_from_table_update(pnhgc, table_id, true);
e5c83d9b
DS
346}
347
348void pbr_nht_route_installed_for_table(uint32_t table_id)
349{
350 hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_install,
351 &table_id);
352}
353
e3b78da8 354static void pbr_nht_find_nhg_from_table_remove(struct hash_bucket *b,
e5c83d9b
DS
355 void *data)
356{
09813729
SW
357 struct pbr_nexthop_group_cache *pnhgc =
358 (struct pbr_nexthop_group_cache *)b->data;
359 uint32_t table_id = *(uint32_t *)data;
360
361 pbr_nht_find_nhg_from_table_update(pnhgc, table_id, false);
e5c83d9b
DS
362}
363
364void pbr_nht_route_removed_for_table(uint32_t table_id)
365{
366 hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_remove,
367 &table_id);
368}
369
370/*
371 * Loop through all nexthops in a nexthop group to check that they are all the
372 * same. If they are not all the same, log this peculiarity.
373 *
374 * nhg
375 * The nexthop group to check
376 *
377 * Returns:
378 * - AFI of last nexthop in the group
379 * - AFI_MAX on error
380 */
ff9799c3 381static afi_t pbr_nht_which_afi(struct nexthop_group nhg,
aafac994 382 enum nexthop_types_t nh_type)
e5c83d9b
DS
383{
384 struct nexthop *nexthop;
385 afi_t install_afi = AFI_MAX;
386 bool v6, v4, bh;
d3765386 387
268c24ee
RW
388 if (nh_type) {
389 switch (nh_type) {
390 case NEXTHOP_TYPE_IPV4:
391 case NEXTHOP_TYPE_IPV4_IFINDEX:
392 return AFI_IP;
393 case NEXTHOP_TYPE_IPV6:
394 case NEXTHOP_TYPE_IPV6_IFINDEX:
395 return AFI_IP6;
396 case NEXTHOP_TYPE_IFINDEX:
397 case NEXTHOP_TYPE_BLACKHOLE:
398 return AFI_MAX;
399 }
400 }
401
e5c83d9b
DS
402 v6 = v4 = bh = false;
403
268c24ee
RW
404 for (ALL_NEXTHOPS(nhg, nexthop)) {
405 nh_type = nexthop->type;
406
407 switch (nh_type) {
408 case NEXTHOP_TYPE_IFINDEX:
409 break;
410 case NEXTHOP_TYPE_IPV4:
411 case NEXTHOP_TYPE_IPV4_IFINDEX:
412 v6 = true;
413 install_afi = AFI_IP;
414 break;
415 case NEXTHOP_TYPE_IPV6:
416 case NEXTHOP_TYPE_IPV6_IFINDEX:
417 v4 = true;
418 install_afi = AFI_IP6;
419 break;
420 case NEXTHOP_TYPE_BLACKHOLE:
421 bh = true;
e5c83d9b
DS
422 break;
423 }
424 }
425
268c24ee
RW
426 /* Interface and/or blackhole nexthops only. */
427 if (!v4 && !v6)
ff9799c3 428 install_afi = AFI_MAX;
ff9799c3 429
e5c83d9b
DS
430 if (!bh && v6 && v4)
431 DEBUGD(&pbr_dbg_nht,
5e81f5dd
DS
432 "%s: Saw both V6 and V4 nexthops...using %s", __func__,
433 afi2str(install_afi));
e5c83d9b
DS
434 if (bh && (v6 || v4))
435 DEBUGD(&pbr_dbg_nht,
436 "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.",
5e81f5dd
DS
437 __func__, v4 ? "v4" : "", (v4 && v6) ? " and " : "",
438 v6 ? "v6" : "");
e5c83d9b
DS
439
440 return install_afi;
441}
442
443static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
444 struct nexthop_group nhg)
445{
446 afi_t install_afi;
aafac994 447 enum nexthop_types_t nh_type = 0;
e5c83d9b 448
aafac994 449 install_afi = pbr_nht_which_afi(nhg, nh_type);
e5c83d9b 450
e5c83d9b
DS
451 route_add(pnhgc, nhg, install_afi);
452}
453
454static void
455pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
ff9799c3 456 struct nexthop_group nhg,
aafac994 457 enum nexthop_types_t nh_type)
e5c83d9b
DS
458{
459 afi_t install_afi;
460
aafac994 461 install_afi = pbr_nht_which_afi(nhg, nh_type);
e5c83d9b
DS
462
463 pnhgc->installed = false;
464 pnhgc->valid = false;
465 route_delete(pnhgc, install_afi);
466}
467
468void pbr_nht_change_group(const char *name)
469{
470 struct nexthop_group_cmd *nhgc;
471 struct pbr_nexthop_group_cache *pnhgc;
472 struct pbr_nexthop_group_cache find;
473 struct nexthop *nhop;
474
475 nhgc = nhgc_find(name);
476 if (!nhgc)
477 return;
478
479 memset(&find, 0, sizeof(find));
6612590d 480 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
481 pnhgc = hash_lookup(pbr_nhg_hash, &find);
482
483 if (!pnhgc) {
484 DEBUGD(&pbr_dbg_nht,
485 "%s: Could not find nexthop-group cache w/ name '%s'",
5e81f5dd 486 __func__, name);
e5c83d9b
DS
487 return;
488 }
489
490 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
491 struct pbr_nexthop_cache lookup;
492 struct pbr_nexthop_cache *pnhc;
493
734bf907 494 lookup.nexthop = *nhop;
e5c83d9b
DS
495 pnhc = hash_lookup(pnhgc->nhh, &lookup);
496 if (!pnhc) {
497 pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
498 pnhc->parent = pnhgc;
499 }
500 }
501 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
502}
503
504char *pbr_nht_nexthop_make_name(char *name, size_t l,
505 uint32_t seqno, char *buffer)
506{
507 snprintf(buffer, l, "%s%u", name, seqno);
508 return buffer;
509}
510
f143cffa
SW
511void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms,
512 const struct nexthop *nhop)
e5c83d9b
DS
513{
514 struct pbr_nexthop_group_cache *pnhgc;
515 struct pbr_nexthop_group_cache find;
516 struct pbr_nexthop_cache *pnhc;
e5c83d9b 517 struct pbr_nexthop_cache lookup;
f143cffa
SW
518 struct nexthop *nh;
519 char buf[PBR_NHC_NAMELEN];
520
521 pbrms->nhg = nexthop_group_new();
522 pbrms->internal_nhg_name = XSTRDUP(
523 MTYPE_TMP,
524 pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
525 pbrms->seqno, buf));
526
527 nh = nexthop_new();
528 memcpy(nh, nhop, sizeof(*nh));
529
530 nexthop_group_add_sorted(pbrms->nhg, nh);
e5c83d9b 531
e5c83d9b 532 memset(&find, 0, sizeof(find));
06210d1f 533 pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
e5c83d9b 534 pbrms->seqno, find.name);
a4044dc1
QY
535
536 if (!pbr_nht_get_next_tableid(true)) {
537 zlog_warn(
538 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
15569c58 539 __func__, find.name);
a4044dc1
QY
540 return;
541 }
542
e5c83d9b
DS
543 if (!pbrms->internal_nhg_name)
544 pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name);
545
546 pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc);
547
734bf907 548 lookup.nexthop = *pbrms->nhg->nexthop;
e5c83d9b
DS
549 pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
550 pnhc->parent = pnhgc;
fcf29c69
DS
551 if (nhop->vrf_id != VRF_DEFAULT) {
552 struct vrf *vrf = vrf_lookup_by_id(nhop->vrf_id);
553
554 if (vrf)
555 strlcpy(pnhc->vrf_name, vrf->name,
556 sizeof(pnhc->vrf_name));
557 }
7cbdabff
DS
558
559 if (nhop->ifindex != 0) {
560 struct interface *ifp =
561 if_lookup_by_index(nhop->ifindex, nhop->vrf_id);
562
563 if (ifp)
564 strlcpy(pnhc->intf_name, ifp->name,
565 sizeof(pnhc->intf_name));
566 }
e5c83d9b
DS
567 pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg);
568}
569
f143cffa 570static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence *pbrms)
e5c83d9b
DS
571{
572 struct pbr_nexthop_group_cache *pnhgc;
573 struct pbr_nexthop_group_cache find;
574 struct pbr_nexthop_cache *pnhc;
575 struct pbr_nexthop_cache lup;
e5c83d9b 576 struct nexthop *nh;
aafac994 577 enum nexthop_types_t nh_type = 0;
e5c83d9b 578
e5c83d9b 579 memset(&find, 0, sizeof(find));
6612590d 580 snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name);
e5c83d9b
DS
581 pnhgc = hash_lookup(pbr_nhg_hash, &find);
582
583 nh = pbrms->nhg->nexthop;
aafac994 584 nh_type = nh->type;
734bf907 585 lup.nexthop = *nh;
e5c83d9b
DS
586 pnhc = hash_lookup(pnhgc->nhh, &lup);
587 pnhc->parent = NULL;
588 hash_release(pnhgc->nhh, pnhc);
589 pbr_nh_delete(&pnhc);
aafac994 590 pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_type);
e5c83d9b
DS
591
592 hash_release(pbr_nhg_hash, pnhgc);
1f375577 593 pbr_nhgc_delete(pnhgc);
e5c83d9b 594
e5c83d9b
DS
595 nexthop_group_delete(&pbrms->nhg);
596 XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
597}
598
f143cffa
SW
599void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
600{
601 pbr_map_delete_nexthops(pbrms);
602
603 pbr_nht_release_individual_nexthop(pbrms);
604}
605
b13e5ad6 606struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name)
e5c83d9b
DS
607{
608 struct nexthop *nhop;
609 struct nexthop_group_cmd *nhgc;
610 struct pbr_nexthop_group_cache *pnhgc;
611 struct pbr_nexthop_group_cache lookup;
612
a4044dc1
QY
613 if (!pbr_nht_get_next_tableid(true)) {
614 zlog_warn(
615 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
5e81f5dd 616 __func__, name);
a4044dc1
QY
617 return NULL;
618 }
619
e5c83d9b
DS
620 nhgc = nhgc_find(name);
621
622 if (!nhgc) {
1d5453d6 623 DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s",
5e81f5dd 624 __func__, name);
b13e5ad6 625 return NULL;
e5c83d9b
DS
626 }
627
6612590d 628 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
e5c83d9b 629 pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc);
5e81f5dd 630 DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __func__, pnhgc);
e5c83d9b
DS
631
632 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
7fe96307 633 struct pbr_nexthop_cache lookupc;
e5c83d9b
DS
634 struct pbr_nexthop_cache *pnhc;
635
734bf907 636 lookupc.nexthop = *nhop;
7fe96307 637 pnhc = hash_lookup(pnhgc->nhh, &lookupc);
e5c83d9b 638 if (!pnhc) {
7fe96307 639 pnhc = hash_get(pnhgc->nhh, &lookupc, pbr_nh_alloc);
e5c83d9b
DS
640 pnhc->parent = pnhgc;
641 }
642 }
b13e5ad6
DS
643
644 return pnhgc;
e5c83d9b
DS
645}
646
647void pbr_nht_delete_group(const char *name)
648{
649 struct pbr_map_sequence *pbrms;
650 struct listnode *snode;
651 struct pbr_map *pbrm;
b13e5ad6
DS
652 struct pbr_nexthop_group_cache pnhgc_find;
653 struct pbr_nexthop_group_cache *pnhgc;
e5c83d9b
DS
654
655 RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
656 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) {
657 if (pbrms->nhgrp_name
b13e5ad6 658 && strmatch(pbrms->nhgrp_name, name)) {
e5c83d9b 659 pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
b13e5ad6
DS
660 pbrms->nhg = NULL;
661 pbrms->internal_nhg_name = NULL;
e5c83d9b
DS
662 pbrm->valid = false;
663 }
664 }
665 }
b13e5ad6
DS
666
667 strlcpy(pnhgc_find.name, name, sizeof(pnhgc_find.name));
668 pnhgc = hash_release(pbr_nhg_hash, &pnhgc_find);
669 pbr_nhgc_delete(pnhgc);
e5c83d9b
DS
670}
671
672bool pbr_nht_nexthop_valid(struct nexthop_group *nhg)
673{
15569c58 674 DEBUGD(&pbr_dbg_nht, "%s: %p", __func__, nhg);
e5c83d9b
DS
675 return true;
676}
677
678bool pbr_nht_nexthop_group_valid(const char *name)
679{
680 struct pbr_nexthop_group_cache *pnhgc;
681 struct pbr_nexthop_group_cache lookup;
682
15569c58 683 DEBUGD(&pbr_dbg_nht, "%s: %s", __func__, name);
e5c83d9b 684
6612590d 685 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
e5c83d9b
DS
686 pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL);
687 if (!pnhgc)
688 return false;
1d5453d6 689 DEBUGD(&pbr_dbg_nht, "%s: %d %d", __func__, pnhgc->valid,
e5c83d9b
DS
690 pnhgc->installed);
691 if (pnhgc->valid && pnhgc->installed)
692 return true;
693
694 return false;
695}
696
697struct pbr_nht_individual {
698 struct zapi_route *nhr;
a106a408 699 struct interface *ifp;
fcf29c69
DS
700 struct pbr_vrf *pbr_vrf;
701 struct pbr_nexthop_cache *pnhc;
9d961247 702 vrf_id_t old_vrf_id;
e5c83d9b 703
4bf97ce1 704 bool valid;
fcf29c69
DS
705
706 bool nhr_matched;
e5c83d9b
DS
707};
708
8babeb1a
SW
709static bool
710pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache *pnhc,
c59f754d 711 struct pbr_nht_individual *pnhi)
e5c83d9b 712{
8babeb1a 713 bool is_valid = pnhc->valid;
e5c83d9b 714
89527add
DS
715 /*
716 * If we have an interface down event, let's note that
717 * it is happening and find all the nexthops that depend
718 * on that interface. As that if we have an interface
719 * flapping fast enough it means that zebra might turn
720 * those nexthop tracking events into a no-update
721 * So let's search and do the right thing on the
722 * interface event.
723 */
3558b8b8 724 if (!pnhi->nhr) {
89527add
DS
725 switch (pnhc->nexthop.type) {
726 case NEXTHOP_TYPE_BLACKHOLE:
e6b00e3f
SW
727 case NEXTHOP_TYPE_IPV4:
728 case NEXTHOP_TYPE_IPV6:
729 goto done;
89527add
DS
730 case NEXTHOP_TYPE_IFINDEX:
731 case NEXTHOP_TYPE_IPV4_IFINDEX:
732 case NEXTHOP_TYPE_IPV6_IFINDEX:
e6b00e3f
SW
733 if (pnhc->nexthop.ifindex == pnhi->ifp->ifindex)
734 is_valid = if_is_up(pnhi->ifp);
735 goto done;
89527add
DS
736 }
737
8babeb1a 738 goto done;
89527add 739 }
e5c83d9b 740
3558b8b8
IR
741 switch (pnhi->nhr->prefix.family) {
742 case AF_INET:
743 if (pnhc->nexthop.gate.ipv4.s_addr
744 != pnhi->nhr->prefix.u.prefix4.s_addr)
745 goto done; /* Unrelated change */
746 break;
747 case AF_INET6:
748 if (memcmp(&pnhc->nexthop.gate.ipv6,
749 &pnhi->nhr->prefix.u.prefix6, 16)
750 != 0)
751 goto done; /* Unrelated change */
752 break;
8babeb1a
SW
753 }
754
c59f754d 755 pnhi->nhr_matched = true;
8babeb1a
SW
756 if (!pnhi->nhr->nexthop_num) {
757 is_valid = false;
758 goto done;
759 }
760
734bf907
DS
761 if (pnhc->nexthop.type == NEXTHOP_TYPE_IPV4_IFINDEX
762 || pnhc->nexthop.type == NEXTHOP_TYPE_IPV6_IFINDEX) {
8babeb1a
SW
763
764 /* GATEWAY_IFINDEX type shouldn't resolve to group */
765 if (pnhi->nhr->nexthop_num > 1) {
766 is_valid = false;
767 goto done;
768 }
769
770 /* If whatever we resolved to wasn't on the interface we
771 * specified. (i.e. not a connected route), its invalid.
772 */
734bf907 773 if (pnhi->nhr->nexthops[0].ifindex != pnhc->nexthop.ifindex) {
8babeb1a
SW
774 is_valid = false;
775 goto done;
776 }
777 }
778
779 is_valid = true;
780
781done:
782 pnhc->valid = is_valid;
783
784 return pnhc->valid;
785}
786
c59f754d
WC
787static bool
788pbr_nht_individual_nexthop_interface_update(struct pbr_nexthop_cache *pnhc,
789 struct pbr_nht_individual *pnhi)
8babeb1a
SW
790{
791 bool is_valid = pnhc->valid;
792
793 if (!pnhi->ifp) /* It doesn't care about non-interface updates */
794 goto done;
795
734bf907 796 if (pnhc->nexthop.ifindex
8babeb1a
SW
797 != pnhi->ifp->ifindex) /* Un-related interface */
798 goto done;
799
c59f754d 800 pnhi->nhr_matched = true;
8babeb1a
SW
801 is_valid = !!if_is_up(pnhi->ifp);
802
803done:
804 pnhc->valid = is_valid;
805
806 return pnhc->valid;
807}
808
809/* Given this update either from interface or nexthop tracking, re-validate this
810 * nexthop.
811 *
812 * If the update is un-related, the subroutines shoud just return their cached
813 * valid state.
814 */
c59f754d
WC
815static void pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache *pnhc,
816 struct pbr_nht_individual *pnhi)
8babeb1a
SW
817{
818 assert(pnhi->nhr || pnhi->ifp); /* Either nexthop or interface update */
819
734bf907 820 switch (pnhc->nexthop.type) {
8babeb1a
SW
821 case NEXTHOP_TYPE_IFINDEX:
822 pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
823 break;
cb254f41 824 case NEXTHOP_TYPE_IPV6_IFINDEX:
734bf907 825 if (IN6_IS_ADDR_LINKLOCAL(&pnhc->nexthop.gate.ipv6)) {
cb254f41
SW
826 pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
827 break;
828 }
829 /* Intentional fall thru */
830 case NEXTHOP_TYPE_IPV4_IFINDEX:
8babeb1a
SW
831 case NEXTHOP_TYPE_IPV4:
832 case NEXTHOP_TYPE_IPV6:
8babeb1a
SW
833 pbr_nht_individual_nexthop_gw_update(pnhc, pnhi);
834 break;
835 case NEXTHOP_TYPE_BLACKHOLE:
836 pnhc->valid = true;
e5c83d9b
DS
837 break;
838 }
8babeb1a
SW
839}
840
841static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket *b,
842 void *data)
843{
844 struct pbr_nexthop_cache *pnhc = b->data;
845 struct pbr_nht_individual *pnhi = data;
8babeb1a
SW
846 bool old_valid;
847
848 old_valid = pnhc->valid;
849
850 pbr_nht_individual_nexthop_update(pnhc, pnhi);
e5c83d9b 851
1d5453d6 852 DEBUGD(&pbr_dbg_nht, " Found %pFX: old: %d new: %d",
2dbe669b 853 &pnhi->nhr->prefix, old_valid, pnhc->valid);
e5c83d9b 854
e5c83d9b 855 if (pnhc->valid)
4bf97ce1 856 pnhi->valid = true;
e5c83d9b
DS
857}
858
b822b93a
SW
859static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket *b,
860 void *data)
861{
862 struct pbr_nexthop_cache *pnhc = b->data;
863 struct nexthop_group *nhg = data;
864 struct nexthop *nh = NULL;
865
734bf907 866 copy_nexthops(&nh, &pnhc->nexthop, NULL);
b822b93a 867
50d89650 868 _nexthop_add(&nhg->nexthop, nh);
b822b93a
SW
869}
870
871static void
872pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group *nhg,
873 struct pbr_nexthop_group_cache *pnhgc)
874{
875 hash_iterate(pnhgc->nhh, pbr_nexthop_group_cache_iterate_to_group, nhg);
876}
877
e3b78da8 878static void pbr_nht_nexthop_update_lookup(struct hash_bucket *b, void *data)
e5c83d9b
DS
879{
880 struct pbr_nexthop_group_cache *pnhgc = b->data;
8babeb1a 881 struct pbr_nht_individual pnhi = {};
b822b93a 882 struct nexthop_group nhg = {};
b13e5ad6
DS
883 bool old_valid;
884
885 old_valid = pnhgc->valid;
e5c83d9b
DS
886
887 pnhi.nhr = (struct zapi_route *)data;
4bf97ce1 888 pnhi.valid = false;
c59f754d 889 pnhi.nhr_matched = false;
e5c83d9b
DS
890 hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_update_lookup,
891 &pnhi);
892
c59f754d
WC
893 if (!pnhi.nhr_matched)
894 return;
895
e5c83d9b
DS
896 /*
897 * If any of the specified nexthops are valid we are valid
898 */
899 pnhgc->valid = !!pnhi.valid;
b13e5ad6 900
6db1188f
SW
901 pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc);
902
903 if (pnhgc->valid)
b822b93a 904 pbr_nht_install_nexthop_group(pnhgc, nhg);
6db1188f
SW
905 else
906 pbr_nht_uninstall_nexthop_group(pnhgc, nhg, 0);
907
908 /* Don't need copied nexthops anymore */
909 nexthops_free(nhg.nexthop);
b822b93a 910
b13e5ad6
DS
911 if (old_valid != pnhgc->valid)
912 pbr_map_check_nh_group_change(pnhgc->name);
e5c83d9b
DS
913}
914
915void pbr_nht_nexthop_update(struct zapi_route *nhr)
916{
917 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr);
918}
919
9d961247
DS
920struct nhrc_vrf_info {
921 struct pbr_vrf *pbr_vrf;
922 uint32_t old_vrf_id;
923 struct nhrc *nhrc;
924};
925
926static int pbr_nht_nhrc_vrf_change(struct hash_bucket *b, void *data)
927{
928 struct nhrc *nhrc = b->data;
929 struct nhrc_vrf_info *nhrcvi = data;
930
931 if (nhrc->nexthop.vrf_id == nhrcvi->old_vrf_id) {
932 nhrcvi->nhrc = nhrc;
933 return HASHWALK_ABORT;
934 }
935
936 return HASHWALK_CONTINUE;
937}
938
939static int pbr_nht_individual_nexthop_vrf_handle(struct hash_bucket *b,
940 void *data)
fcf29c69
DS
941{
942 struct pbr_nexthop_cache *pnhc = b->data;
943 struct pbr_nht_individual *pnhi = data;
944
9d961247
DS
945 if (pnhc->looked_at == true)
946 return HASHWALK_CONTINUE;
947
734bf907 948 if (pnhc->nexthop.vrf_id == VRF_DEFAULT)
9d961247 949 return HASHWALK_CONTINUE;
fcf29c69 950
9d961247
DS
951 if (strncmp(pnhc->vrf_name, pbr_vrf_name(pnhi->pbr_vrf),
952 sizeof(pnhc->vrf_name))
953 == 0) {
fcf29c69 954 pnhi->pnhc = pnhc;
9d961247 955
734bf907 956 if (pnhc->nexthop.vrf_id != pbr_vrf_id(pnhi->pbr_vrf)) {
9d961247
DS
957 struct nhrc_vrf_info nhrcvi;
958
959 memset(&nhrcvi, 0, sizeof(nhrcvi));
960 nhrcvi.pbr_vrf = pnhi->pbr_vrf;
734bf907 961 nhrcvi.old_vrf_id = pnhc->nexthop.vrf_id;
9d961247
DS
962
963 pnhi->nhr_matched = true;
734bf907 964 pnhi->old_vrf_id = pnhc->nexthop.vrf_id;
9d961247
DS
965
966 do {
967 nhrcvi.nhrc = NULL;
968 hash_walk(pbr_nhrc_hash,
969 pbr_nht_nhrc_vrf_change, &nhrcvi);
970 if (nhrcvi.nhrc) {
971 hash_release(pbr_nhrc_hash,
972 nhrcvi.nhrc);
973 nhrcvi.nhrc->nexthop.vrf_id =
974 pbr_vrf_id(pnhi->pbr_vrf);
975 hash_get(pbr_nhrc_hash, nhrcvi.nhrc,
976 hash_alloc_intern);
977 pbr_send_rnh(&nhrcvi.nhrc->nexthop, true);
978 }
979 } while (nhrcvi.nhrc);
980 }
981
982 pnhc->looked_at = true;
983 return HASHWALK_ABORT;
fcf29c69 984 }
9d961247
DS
985
986 return HASHWALK_CONTINUE;
987}
988
989static void pbr_nht_clear_looked_at(struct hash_bucket *b, void *data)
990{
991 struct pbr_nexthop_cache *pnhc = b->data;
992
993 pnhc->looked_at = false;
fcf29c69
DS
994}
995
996static void pbr_nht_nexthop_vrf_handle(struct hash_bucket *b, void *data)
997{
998 struct pbr_nexthop_group_cache *pnhgc = b->data;
999 struct pbr_vrf *pbr_vrf = data;
1000 struct pbr_nht_individual pnhi = {};
fcf29c69 1001
9d961247
DS
1002 hash_iterate(pnhgc->nhh, pbr_nht_clear_looked_at, NULL);
1003 memset(&pnhi, 0, sizeof(pnhi));
1004 pnhi.pbr_vrf = pbr_vrf;
fcf29c69 1005 do {
9d961247
DS
1006 struct pbr_nexthop_cache *pnhc;
1007
1008 pnhi.pnhc = NULL;
1009 hash_walk(pnhgc->nhh, pbr_nht_individual_nexthop_vrf_handle,
1010 &pnhi);
fcf29c69
DS
1011
1012 if (!pnhi.pnhc)
1013 continue;
1014
9d961247 1015 pnhc = pnhi.pnhc;
734bf907 1016 pnhc->nexthop.vrf_id = pnhi.old_vrf_id;
fcf29c69 1017 pnhi.pnhc = hash_release(pnhgc->nhh, pnhi.pnhc);
9d961247 1018 if (pnhi.pnhc) {
734bf907 1019 pnhi.pnhc->nexthop.vrf_id = pbr_vrf_id(pbr_vrf);
fcf29c69 1020
9d961247
DS
1021 hash_get(pnhgc->nhh, pnhi.pnhc, hash_alloc_intern);
1022 } else
734bf907 1023 pnhc->nexthop.vrf_id = pbr_vrf_id(pbr_vrf);
fcf29c69
DS
1024
1025 pbr_map_check_vrf_nh_group_change(pnhgc->name, pbr_vrf,
9d961247 1026 pnhi.old_vrf_id);
fcf29c69
DS
1027 } while (pnhi.pnhc);
1028}
1029
1030void pbr_nht_vrf_update(struct pbr_vrf *pbr_vrf)
1031{
1032 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_vrf_handle, pbr_vrf);
1033}
1034
7cbdabff
DS
1035static void pbr_nht_individual_nexthop_interface_handle(struct hash_bucket *b,
1036 void *data)
1037{
1038 struct pbr_nexthop_cache *pnhc = b->data;
1039 struct pbr_nht_individual *pnhi = data;
1040
734bf907 1041 if (pnhc->nexthop.ifindex == 0)
7cbdabff
DS
1042 return;
1043
1044 if ((strncmp(pnhc->intf_name, pnhi->ifp->name, sizeof(pnhc->intf_name))
1045 == 0)
734bf907 1046 && pnhc->nexthop.ifindex != pnhi->ifp->ifindex)
7cbdabff
DS
1047 pnhi->pnhc = pnhc;
1048}
1049
1050static void pbr_nht_nexthop_interface_handle(struct hash_bucket *b, void *data)
1051{
1052 struct pbr_nexthop_group_cache *pnhgc = b->data;
1053 struct interface *ifp = data;
1054 struct pbr_nht_individual pnhi = {};
1055 struct nhrc *nhrc;
1056 uint32_t old_ifindex;
1057
1058 do {
1059 memset(&pnhi, 0, sizeof(pnhi));
1060 pnhi.ifp = ifp;
1061 hash_iterate(pnhgc->nhh,
1062 pbr_nht_individual_nexthop_interface_handle,
1063 &pnhi);
1064
1065 if (!pnhi.pnhc)
1066 continue;
1067
1068 pnhi.pnhc = hash_release(pnhgc->nhh, pnhi.pnhc);
734bf907 1069 old_ifindex = pnhi.pnhc->nexthop.ifindex;
7cbdabff 1070
734bf907 1071 nhrc = hash_lookup(pbr_nhrc_hash, &pnhi.pnhc->nexthop);
7cbdabff
DS
1072 if (nhrc) {
1073 hash_release(pbr_nhrc_hash, nhrc);
1074 nhrc->nexthop.ifindex = ifp->ifindex;
1075 hash_get(pbr_nhrc_hash, nhrc, hash_alloc_intern);
1076 }
734bf907 1077 pnhi.pnhc->nexthop.ifindex = ifp->ifindex;
7cbdabff
DS
1078
1079 hash_get(pnhgc->nhh, pnhi.pnhc, hash_alloc_intern);
1080
1081 pbr_map_check_interface_nh_group_change(pnhgc->name, ifp,
1082 old_ifindex);
1083 } while (pnhi.pnhc);
1084}
1085
1086void pbr_nht_interface_update(struct interface *ifp)
1087{
1088 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_handle, ifp);
1089}
1090
a106a408 1091static void
7f5818fb 1092pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket *b,
a106a408
RW
1093 void *data)
1094{
1095 struct pbr_nexthop_cache *pnhc = b->data;
1096 struct pbr_nht_individual *pnhi = data;
1097 bool old_valid;
1098
1099 old_valid = pnhc->valid;
1100
8babeb1a 1101 pbr_nht_individual_nexthop_update(pnhc, pnhi);
a106a408 1102
1d5453d6 1103 DEBUGD(&pbr_dbg_nht, " Found %s: old: %d new: %d", pnhi->ifp->name,
a106a408
RW
1104 old_valid, pnhc->valid);
1105
1106 if (pnhc->valid)
4bf97ce1 1107 pnhi->valid = true;
a106a408
RW
1108}
1109
7f5818fb 1110static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket *b,
a106a408
RW
1111 void *data)
1112{
1113 struct pbr_nexthop_group_cache *pnhgc = b->data;
8babeb1a 1114 struct pbr_nht_individual pnhi = {};
89527add 1115 struct nexthop_group nhg = {};
a106a408
RW
1116 bool old_valid;
1117
1118 old_valid = pnhgc->valid;
1119
1120 pnhi.ifp = data;
4bf97ce1 1121 pnhi.valid = false;
a106a408
RW
1122 hash_iterate(pnhgc->nhh,
1123 pbr_nht_individual_nexthop_interface_update_lookup, &pnhi);
1124
1125 /*
1126 * If any of the specified nexthops are valid we are valid
1127 */
4bf97ce1 1128 pnhgc->valid = pnhi.valid;
a106a408 1129
89527add
DS
1130 pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc);
1131
1132 if (pnhgc->valid)
1133 pbr_nht_install_nexthop_group(pnhgc, nhg);
1134 else
1135 pbr_nht_uninstall_nexthop_group(pnhgc, nhg, 0);
1136
1137 nexthops_free(nhg.nexthop);
1138
a106a408
RW
1139 if (old_valid != pnhgc->valid)
1140 pbr_map_check_nh_group_change(pnhgc->name);
1141}
1142
1143void pbr_nht_nexthop_interface_update(struct interface *ifp)
1144{
1145 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_update_lookup,
1146 ifp);
1147}
1148
d8b87afe 1149static uint32_t pbr_nhg_hash_key(const void *arg)
e5c83d9b 1150{
d8b87afe 1151 const struct pbr_nexthop_group_cache *nhgc = arg;
e5c83d9b
DS
1152
1153 return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96);
1154}
1155
74df8d6d 1156static bool pbr_nhg_hash_equal(const void *arg1, const void *arg2)
e5c83d9b
DS
1157{
1158 const struct pbr_nexthop_group_cache *nhgc1 =
1159 (const struct pbr_nexthop_group_cache *)arg1;
1160 const struct pbr_nexthop_group_cache *nhgc2 =
1161 (const struct pbr_nexthop_group_cache *)arg2;
1162
1163 return !strcmp(nhgc1->name, nhgc2->name);
1164}
1165
a4044dc1 1166uint32_t pbr_nht_get_next_tableid(bool peek)
e5c83d9b
DS
1167{
1168 uint32_t i;
1169 bool found = false;
1170
1171 for (i = pbr_nhg_low_table; i <= pbr_nhg_high_table; i++) {
d8729f8c 1172 if (!nhg_tableid[i]) {
e5c83d9b
DS
1173 found = true;
1174 break;
1175 }
1176 }
1177
1178 if (found) {
a4044dc1 1179 nhg_tableid[i] = !peek;
e5c83d9b
DS
1180 return i;
1181 } else
1182 return 0;
1183}
1184
1185void pbr_nht_set_tableid_range(uint32_t low, uint32_t high)
1186{
1187 pbr_nhg_low_table = low;
1188 pbr_nhg_high_table = high;
1189}
1190
1191void pbr_nht_write_table_range(struct vty *vty)
1192{
1193 if (pbr_nhg_low_table != PBR_NHT_DEFAULT_LOW_TABLEID
1194 || pbr_nhg_high_table != PBR_NHT_DEFAULT_HIGH_TABLEID) {
1195 vty_out(vty, "pbr table range %u %u\n", pbr_nhg_low_table,
1196 pbr_nhg_high_table);
1197 }
1198}
1199
1200uint32_t pbr_nht_get_next_rule(uint32_t seqno)
1201{
1202 return seqno + pbr_nhg_low_rule - 1;
1203}
1204void pbr_nht_set_rule_range(uint32_t low, uint32_t high)
1205{
1206 pbr_nhg_low_rule = low;
1207 pbr_nhg_high_rule = high;
1208}
1209
1210void pbr_nht_write_rule_range(struct vty *vty)
1211{
1212 if (pbr_nhg_low_rule != PBR_NHT_DEFAULT_LOW_RULE
1213 || pbr_nhg_high_rule != PBR_NHT_DEFAULT_HIGH_RULE) {
1214 vty_out(vty, "pbr rule range %u %u\n", pbr_nhg_low_rule,
1215 pbr_nhg_high_rule);
1216 }
1217}
1218
1219uint32_t pbr_nht_get_table(const char *name)
1220{
1221 struct pbr_nexthop_group_cache find;
1222 struct pbr_nexthop_group_cache *pnhgc;
1223
1224 memset(&find, 0, sizeof(find));
6612590d 1225 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
1226 pnhgc = hash_lookup(pbr_nhg_hash, &find);
1227
1228 if (!pnhgc) {
1229 DEBUGD(&pbr_dbg_nht,
1230 "%s: Could not find nexthop-group cache w/ name '%s'",
15569c58 1231 __func__, name);
e5c83d9b
DS
1232 return 5000;
1233 }
1234
1235 return pnhgc->table_id;
1236}
1237
1238bool pbr_nht_get_installed(const char *name)
1239{
1240 struct pbr_nexthop_group_cache find;
1241 struct pbr_nexthop_group_cache *pnhgc;
1242
1243 memset(&find, 0, sizeof(find));
6612590d 1244 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
1245
1246 pnhgc = hash_lookup(pbr_nhg_hash, &find);
1247
d3765386 1248 if (!pnhgc)
e5c83d9b 1249 return false;
e5c83d9b
DS
1250
1251 return pnhgc->installed;
1252}
1253
e3b78da8 1254static void pbr_nht_show_nhg_nexthops(struct hash_bucket *b, void *data)
e5c83d9b
DS
1255{
1256 struct pbr_nexthop_cache *pnhc = b->data;
1257 struct vty *vty = data;
1258
57cdafc4 1259 vty_out(vty, "\tValid: %d ", pnhc->valid);
734bf907 1260 nexthop_group_write_nexthop(vty, &pnhc->nexthop);
e5c83d9b
DS
1261}
1262
010dd8ed
WC
1263static void pbr_nht_json_nhg_nexthops(struct hash_bucket *b, void *data)
1264{
1265 struct pbr_nexthop_cache *pnhc = b->data;
1266 json_object *all_hops = data;
1267 json_object *this_hop;
1268
1269 this_hop = json_object_new_object();
734bf907 1270 nexthop_group_json_nexthop(this_hop, &pnhc->nexthop);
3e81618d 1271 json_object_boolean_add(this_hop, "valid", pnhc->valid);
010dd8ed
WC
1272
1273 json_object_array_add(all_hops, this_hop);
1274}
1275
e5c83d9b
DS
1276struct pbr_nht_show {
1277 struct vty *vty;
010dd8ed 1278 json_object *json;
e5c83d9b
DS
1279 const char *name;
1280};
1281
e3b78da8 1282static void pbr_nht_show_nhg(struct hash_bucket *b, void *data)
e5c83d9b
DS
1283{
1284 struct pbr_nexthop_group_cache *pnhgc = b->data;
1285 struct pbr_nht_show *pns = data;
1286 struct vty *vty;
1287
1288 if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
1289 return;
1290
1291 vty = pns->vty;
1292 vty_out(vty, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
1293 pnhgc->name, pnhgc->table_id, pnhgc->valid, pnhgc->installed);
1294
1295 hash_iterate(pnhgc->nhh, pbr_nht_show_nhg_nexthops, vty);
1296}
1297
010dd8ed
WC
1298static void pbr_nht_json_nhg(struct hash_bucket *b, void *data)
1299{
1300 struct pbr_nexthop_group_cache *pnhgc = b->data;
1301 struct pbr_nht_show *pns = data;
1302 json_object *j, *this_group, *group_hops;
1303
1304 if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
1305 return;
1306
1307 j = pns->json;
1308 this_group = json_object_new_object();
1309
1310 if (!j || !this_group)
1311 return;
1312
010dd8ed 1313 json_object_int_add(this_group, "id", pnhgc->table_id);
81c0078e 1314 json_object_string_add(this_group, "name", pnhgc->name);
3e81618d
WC
1315 json_object_boolean_add(this_group, "valid", pnhgc->valid);
1316 json_object_boolean_add(this_group, "installed", pnhgc->installed);
010dd8ed
WC
1317
1318 group_hops = json_object_new_array();
1319
1320 if (group_hops) {
1321 hash_iterate(pnhgc->nhh, pbr_nht_json_nhg_nexthops, group_hops);
1322 json_object_object_add(this_group, "nexthops", group_hops);
1323 }
1324
dadba1a2 1325 json_object_array_add(j, this_group);
010dd8ed
WC
1326}
1327
e5c83d9b
DS
1328void pbr_nht_show_nexthop_group(struct vty *vty, const char *name)
1329{
1330 struct pbr_nht_show pns;
1331
1332 pns.vty = vty;
1333 pns.name = name;
1334
1335 hash_iterate(pbr_nhg_hash, pbr_nht_show_nhg, &pns);
1336}
1337
010dd8ed
WC
1338void pbr_nht_json_nexthop_group(json_object *j, const char *name)
1339{
1340 struct pbr_nht_show pns;
1341
1342 pns.name = name;
1343 pns.json = j;
1344
1345 hash_iterate(pbr_nhg_hash, pbr_nht_json_nhg, &pns);
1346}
1347
e5c83d9b
DS
1348void pbr_nht_init(void)
1349{
1350 pbr_nhg_hash = hash_create_size(
1351 16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash");
b13e5ad6 1352 pbr_nhrc_hash =
d8b87afe 1353 hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash,
b13e5ad6 1354 pbr_nhrc_hash_equal, "PBR NH Hash");
e5c83d9b
DS
1355
1356 pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID;
1357 pbr_nhg_high_table = PBR_NHT_DEFAULT_HIGH_TABLEID;
1358 pbr_nhg_low_rule = PBR_NHT_DEFAULT_LOW_RULE;
1359 pbr_nhg_high_rule = PBR_NHT_DEFAULT_HIGH_RULE;
1360 memset(&nhg_tableid, 0, 65535 * sizeof(uint8_t));
1361}