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