]> git.proxmox.com Git - mirror_frr.git/blame - pbrd/pbr_nht.c
pbrd: free nexthop_group name on `no set nexthop-group`
[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
40static struct 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));
b13e5ad6
DS
94 nhrc = hash_get(pbr_nhrc_hash, pnhc->nexthop, pbr_nhrc_hash_alloc);
95 new->nexthop = &nhrc->nexthop;
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
b13e5ad6 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
112 nhrc = hash_lookup(pbr_nhrc_hash, (*pnhc)->nexthop);
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__);
b13e5ad6
DS
119 pbr_send_rnh((*pnhc)->nexthop, false);
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
b13e5ad6 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
b13e5ad6 151 if (pbrnc1->nexthop->vrf_id != pbrnc2->nexthop->vrf_id)
74df8d6d 152 return false;
e5c83d9b 153
b13e5ad6 154 if (pbrnc1->nexthop->ifindex != pbrnc2->nexthop->ifindex)
74df8d6d 155 return false;
e5c83d9b 156
b13e5ad6 157 if (pbrnc1->nexthop->type != pbrnc2->nexthop->type)
74df8d6d 158 return false;
e5c83d9b 159
b13e5ad6 160 switch (pbrnc1->nexthop->type) {
e5c83d9b 161 case NEXTHOP_TYPE_IFINDEX:
a106a408 162 return pbrnc1->nexthop->ifindex == pbrnc2->nexthop->ifindex;
e5c83d9b
DS
163 case NEXTHOP_TYPE_IPV4_IFINDEX:
164 case NEXTHOP_TYPE_IPV4:
b13e5ad6
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:
f24f3450
RW
169 return !memcmp(&pbrnc1->nexthop->gate.ipv6,
170 &pbrnc2->nexthop->gate.ipv6, 16);
e5c83d9b 171 case NEXTHOP_TYPE_BLACKHOLE:
b13e5ad6 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
DS
251 /* create & insert new pnhc into pnhgc->nhh */
252 pnhc_find.nexthop = (struct nexthop *)nhop;
253 pnhc = hash_get(pnhgc->nhh, &pnhc_find, pbr_nh_alloc);
254 pnhc_find.nexthop = NULL;
255
256 /* set parent pnhgc */
257 pnhc->parent = pnhgc;
e5c83d9b 258
b13e5ad6
DS
259 if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) {
260 nexthop2str(nhop, debugstr, sizeof(debugstr));
261 DEBUGD(&pbr_dbg_nht, "%s: Added %s to nexthop-group %s",
15569c58 262 __func__, debugstr, nhgc->name);
b13e5ad6
DS
263 }
264
265 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
266 pbr_map_check_nh_group_change(nhgc->name);
a106a408 267
cb254f41
SW
268 if (nhop->type == NEXTHOP_TYPE_IFINDEX
269 || (nhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
270 && IN6_IS_ADDR_LINKLOCAL(&nhop->gate.ipv6))) {
a106a408
RW
271 struct interface *ifp;
272
273 ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id);
274 if (ifp)
275 pbr_nht_nexthop_interface_update(ifp);
276 }
e5c83d9b
DS
277}
278
b13e5ad6 279void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc,
e5c83d9b
DS
280 const struct nexthop *nhop)
281{
b13e5ad6 282 char debugstr[256];
3e300703 283 struct pbr_nexthop_group_cache pnhgc_find = {};
b13e5ad6 284 struct pbr_nexthop_group_cache *pnhgc;
3e300703 285 struct pbr_nexthop_cache pnhc_find = {};
b13e5ad6 286 struct pbr_nexthop_cache *pnhc;
aafac994 287 enum nexthop_types_t nh_type = nhop->type;
b13e5ad6
DS
288
289 /* find pnhgc by name */
290 strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
a4044dc1 291 pnhgc = hash_lookup(pbr_nhg_hash, &pnhgc_find);
b13e5ad6
DS
292
293 /* delete pnhc from pnhgc->nhh */
294 pnhc_find.nexthop = (struct nexthop *)nhop;
295 pnhc = hash_release(pnhgc->nhh, &pnhc_find);
296
297 /* delete pnhc */
298 pbr_nh_delete(&pnhc);
e5c83d9b 299
b13e5ad6
DS
300 if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) {
301 nexthop2str(nhop, debugstr, sizeof(debugstr));
302 DEBUGD(&pbr_dbg_nht, "%s: Removed %s from nexthop-group %s",
15569c58 303 __func__, debugstr, nhgc->name);
b13e5ad6 304 }
e5c83d9b 305
ff9799c3
DS
306 if (pnhgc->nhh->count)
307 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
308 else
aafac994 309 pbr_nht_uninstall_nexthop_group(pnhgc, nhgc->nhg, nh_type);
ff9799c3 310
b13e5ad6 311 pbr_map_check_nh_group_change(nhgc->name);
e5c83d9b
DS
312}
313
314void pbr_nhgroup_delete_cb(const char *name)
315{
15569c58 316 DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s", __func__, name);
b13e5ad6 317
ff9799c3
DS
318 /* delete group from all pbrms's */
319 pbr_nht_delete_group(name);
320
b13e5ad6 321 pbr_map_check_nh_group_change(name);
e5c83d9b
DS
322}
323
324#if 0
325static struct pbr_nexthop_cache *pbr_nht_lookup_nexthop(struct nexthop *nexthop)
326{
327 return NULL;
328}
329#endif
330
e3b78da8 331static void pbr_nht_find_nhg_from_table_install(struct hash_bucket *b,
e5c83d9b
DS
332 void *data)
333{
334 struct pbr_nexthop_group_cache *pnhgc =
335 (struct pbr_nexthop_group_cache *)b->data;
336 uint32_t *table_id = (uint32_t *)data;
337
338 if (pnhgc->table_id == *table_id) {
15569c58
DA
339 DEBUGD(&pbr_dbg_nht, "%s: Table ID (%u) matches %s", __func__,
340 *table_id, pnhgc->name);
2fb7892e
DS
341
342 /*
343 * If the table has been re-handled by zebra
344 * and we are already installed no need to do
345 * anything here.
346 */
347 if (!pnhgc->installed) {
348 pnhgc->installed = true;
349 pbr_map_schedule_policy_from_nhg(pnhgc->name);
350 }
e5c83d9b
DS
351 }
352}
353
354void pbr_nht_route_installed_for_table(uint32_t table_id)
355{
356 hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_install,
357 &table_id);
358}
359
e3b78da8 360static void pbr_nht_find_nhg_from_table_remove(struct hash_bucket *b,
e5c83d9b
DS
361 void *data)
362{
363 ;
364}
365
366void pbr_nht_route_removed_for_table(uint32_t table_id)
367{
368 hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_remove,
369 &table_id);
370}
371
372/*
373 * Loop through all nexthops in a nexthop group to check that they are all the
374 * same. If they are not all the same, log this peculiarity.
375 *
376 * nhg
377 * The nexthop group to check
378 *
379 * Returns:
380 * - AFI of last nexthop in the group
381 * - AFI_MAX on error
382 */
ff9799c3 383static afi_t pbr_nht_which_afi(struct nexthop_group nhg,
aafac994 384 enum nexthop_types_t nh_type)
e5c83d9b
DS
385{
386 struct nexthop *nexthop;
387 afi_t install_afi = AFI_MAX;
388 bool v6, v4, bh;
d3765386 389
268c24ee
RW
390 if (nh_type) {
391 switch (nh_type) {
392 case NEXTHOP_TYPE_IPV4:
393 case NEXTHOP_TYPE_IPV4_IFINDEX:
394 return AFI_IP;
395 case NEXTHOP_TYPE_IPV6:
396 case NEXTHOP_TYPE_IPV6_IFINDEX:
397 return AFI_IP6;
398 case NEXTHOP_TYPE_IFINDEX:
399 case NEXTHOP_TYPE_BLACKHOLE:
400 return AFI_MAX;
401 }
402 }
403
e5c83d9b
DS
404 v6 = v4 = bh = false;
405
268c24ee
RW
406 for (ALL_NEXTHOPS(nhg, nexthop)) {
407 nh_type = nexthop->type;
408
409 switch (nh_type) {
410 case NEXTHOP_TYPE_IFINDEX:
411 break;
412 case NEXTHOP_TYPE_IPV4:
413 case NEXTHOP_TYPE_IPV4_IFINDEX:
414 v6 = true;
415 install_afi = AFI_IP;
416 break;
417 case NEXTHOP_TYPE_IPV6:
418 case NEXTHOP_TYPE_IPV6_IFINDEX:
419 v4 = true;
420 install_afi = AFI_IP6;
421 break;
422 case NEXTHOP_TYPE_BLACKHOLE:
423 bh = true;
e5c83d9b
DS
424 break;
425 }
426 }
427
268c24ee
RW
428 /* Interface and/or blackhole nexthops only. */
429 if (!v4 && !v6)
ff9799c3 430 install_afi = AFI_MAX;
ff9799c3 431
e5c83d9b
DS
432 if (!bh && v6 && v4)
433 DEBUGD(&pbr_dbg_nht,
5e81f5dd
DS
434 "%s: Saw both V6 and V4 nexthops...using %s", __func__,
435 afi2str(install_afi));
e5c83d9b
DS
436 if (bh && (v6 || v4))
437 DEBUGD(&pbr_dbg_nht,
438 "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.",
5e81f5dd
DS
439 __func__, v4 ? "v4" : "", (v4 && v6) ? " and " : "",
440 v6 ? "v6" : "");
e5c83d9b
DS
441
442 return install_afi;
443}
444
445static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
446 struct nexthop_group nhg)
447{
448 afi_t install_afi;
aafac994 449 enum nexthop_types_t nh_type = 0;
e5c83d9b 450
aafac994 451 install_afi = pbr_nht_which_afi(nhg, nh_type);
e5c83d9b 452
e5c83d9b
DS
453 route_add(pnhgc, nhg, install_afi);
454}
455
456static void
457pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
ff9799c3 458 struct nexthop_group nhg,
aafac994 459 enum nexthop_types_t nh_type)
e5c83d9b
DS
460{
461 afi_t install_afi;
462
aafac994 463 install_afi = pbr_nht_which_afi(nhg, nh_type);
e5c83d9b
DS
464
465 pnhgc->installed = false;
466 pnhgc->valid = false;
467 route_delete(pnhgc, install_afi);
468}
469
470void pbr_nht_change_group(const char *name)
471{
472 struct nexthop_group_cmd *nhgc;
473 struct pbr_nexthop_group_cache *pnhgc;
474 struct pbr_nexthop_group_cache find;
475 struct nexthop *nhop;
476
477 nhgc = nhgc_find(name);
478 if (!nhgc)
479 return;
480
481 memset(&find, 0, sizeof(find));
6612590d 482 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
483 pnhgc = hash_lookup(pbr_nhg_hash, &find);
484
485 if (!pnhgc) {
486 DEBUGD(&pbr_dbg_nht,
487 "%s: Could not find nexthop-group cache w/ name '%s'",
5e81f5dd 488 __func__, name);
e5c83d9b
DS
489 return;
490 }
491
492 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
493 struct pbr_nexthop_cache lookup;
494 struct pbr_nexthop_cache *pnhc;
495
b13e5ad6 496 lookup.nexthop = nhop;
e5c83d9b
DS
497 pnhc = hash_lookup(pnhgc->nhh, &lookup);
498 if (!pnhc) {
499 pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
500 pnhc->parent = pnhgc;
501 }
502 }
503 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
504}
505
506char *pbr_nht_nexthop_make_name(char *name, size_t l,
507 uint32_t seqno, char *buffer)
508{
509 snprintf(buffer, l, "%s%u", name, seqno);
510 return buffer;
511}
512
b13e5ad6 513void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms)
e5c83d9b
DS
514{
515 struct pbr_nexthop_group_cache *pnhgc;
516 struct pbr_nexthop_group_cache find;
517 struct pbr_nexthop_cache *pnhc;
e5c83d9b
DS
518 struct pbr_nexthop_cache lookup;
519
e5c83d9b 520 memset(&find, 0, sizeof(find));
06210d1f 521 pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
e5c83d9b 522 pbrms->seqno, find.name);
a4044dc1
QY
523
524 if (!pbr_nht_get_next_tableid(true)) {
525 zlog_warn(
526 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
15569c58 527 __func__, find.name);
a4044dc1
QY
528 return;
529 }
530
e5c83d9b
DS
531 if (!pbrms->internal_nhg_name)
532 pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name);
533
534 pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc);
535
b13e5ad6 536 lookup.nexthop = pbrms->nhg->nexthop;
e5c83d9b
DS
537 pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
538 pnhc->parent = pnhgc;
539 pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg);
540}
541
b13e5ad6 542void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
e5c83d9b
DS
543{
544 struct pbr_nexthop_group_cache *pnhgc;
545 struct pbr_nexthop_group_cache find;
546 struct pbr_nexthop_cache *pnhc;
547 struct pbr_nexthop_cache lup;
e5c83d9b 548 struct nexthop *nh;
aafac994 549 enum nexthop_types_t nh_type = 0;
e5c83d9b 550
be3b67b5 551 pbr_map_delete_nexthops(pbrms);
e5c83d9b
DS
552
553 memset(&find, 0, sizeof(find));
6612590d 554 snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name);
e5c83d9b
DS
555 pnhgc = hash_lookup(pbr_nhg_hash, &find);
556
557 nh = pbrms->nhg->nexthop;
aafac994 558 nh_type = nh->type;
b13e5ad6 559 lup.nexthop = nh;
e5c83d9b
DS
560 pnhc = hash_lookup(pnhgc->nhh, &lup);
561 pnhc->parent = NULL;
562 hash_release(pnhgc->nhh, pnhc);
563 pbr_nh_delete(&pnhc);
aafac994 564 pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_type);
e5c83d9b
DS
565
566 hash_release(pbr_nhg_hash, pnhgc);
1f375577 567 pbr_nhgc_delete(pnhgc);
e5c83d9b 568
e5c83d9b
DS
569 nexthop_group_delete(&pbrms->nhg);
570 XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
571}
572
b13e5ad6 573struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name)
e5c83d9b
DS
574{
575 struct nexthop *nhop;
576 struct nexthop_group_cmd *nhgc;
577 struct pbr_nexthop_group_cache *pnhgc;
578 struct pbr_nexthop_group_cache lookup;
579
a4044dc1
QY
580 if (!pbr_nht_get_next_tableid(true)) {
581 zlog_warn(
582 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
5e81f5dd 583 __func__, name);
a4044dc1
QY
584 return NULL;
585 }
586
e5c83d9b
DS
587 nhgc = nhgc_find(name);
588
589 if (!nhgc) {
a4044dc1 590 DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s\n",
5e81f5dd 591 __func__, name);
b13e5ad6 592 return NULL;
e5c83d9b
DS
593 }
594
6612590d 595 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
e5c83d9b 596 pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc);
5e81f5dd 597 DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __func__, pnhgc);
e5c83d9b
DS
598
599 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
7fe96307 600 struct pbr_nexthop_cache lookupc;
e5c83d9b
DS
601 struct pbr_nexthop_cache *pnhc;
602
7fe96307
A
603 lookupc.nexthop = nhop;
604 pnhc = hash_lookup(pnhgc->nhh, &lookupc);
e5c83d9b 605 if (!pnhc) {
7fe96307 606 pnhc = hash_get(pnhgc->nhh, &lookupc, pbr_nh_alloc);
e5c83d9b
DS
607 pnhc->parent = pnhgc;
608 }
609 }
b13e5ad6
DS
610
611 return pnhgc;
e5c83d9b
DS
612}
613
614void pbr_nht_delete_group(const char *name)
615{
616 struct pbr_map_sequence *pbrms;
617 struct listnode *snode;
618 struct pbr_map *pbrm;
b13e5ad6
DS
619 struct pbr_nexthop_group_cache pnhgc_find;
620 struct pbr_nexthop_group_cache *pnhgc;
e5c83d9b
DS
621
622 RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
623 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) {
624 if (pbrms->nhgrp_name
b13e5ad6 625 && strmatch(pbrms->nhgrp_name, name)) {
e5c83d9b 626 pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
b13e5ad6
DS
627 pbrms->nhg = NULL;
628 pbrms->internal_nhg_name = NULL;
e5c83d9b
DS
629 pbrm->valid = false;
630 }
631 }
632 }
b13e5ad6
DS
633
634 strlcpy(pnhgc_find.name, name, sizeof(pnhgc_find.name));
635 pnhgc = hash_release(pbr_nhg_hash, &pnhgc_find);
636 pbr_nhgc_delete(pnhgc);
e5c83d9b
DS
637}
638
639bool pbr_nht_nexthop_valid(struct nexthop_group *nhg)
640{
15569c58 641 DEBUGD(&pbr_dbg_nht, "%s: %p", __func__, nhg);
e5c83d9b
DS
642 return true;
643}
644
645bool pbr_nht_nexthop_group_valid(const char *name)
646{
647 struct pbr_nexthop_group_cache *pnhgc;
648 struct pbr_nexthop_group_cache lookup;
649
15569c58 650 DEBUGD(&pbr_dbg_nht, "%s: %s", __func__, name);
e5c83d9b 651
6612590d 652 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
e5c83d9b
DS
653 pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL);
654 if (!pnhgc)
655 return false;
15569c58 656 DEBUGD(&pbr_dbg_nht, "%s: \t%d %d", __func__, pnhgc->valid,
e5c83d9b
DS
657 pnhgc->installed);
658 if (pnhgc->valid && pnhgc->installed)
659 return true;
660
661 return false;
662}
663
664struct pbr_nht_individual {
665 struct zapi_route *nhr;
a106a408 666 struct interface *ifp;
e5c83d9b
DS
667
668 uint32_t valid;
669};
670
8babeb1a
SW
671static bool
672pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache *pnhc,
673 const struct pbr_nht_individual *pnhi)
e5c83d9b 674{
8babeb1a 675 bool is_valid = pnhc->valid;
e5c83d9b 676
8babeb1a
SW
677 if (!pnhi->nhr) /* It doesn't care about non-nexthop updates */
678 goto done;
e5c83d9b
DS
679
680 switch (pnhi->nhr->prefix.family) {
681 case AF_INET:
b13e5ad6 682 if (pnhc->nexthop->gate.ipv4.s_addr
8babeb1a
SW
683 != pnhi->nhr->prefix.u.prefix4.s_addr)
684 goto done; /* Unrelated change */
e5c83d9b
DS
685 break;
686 case AF_INET6:
b13e5ad6
DS
687 if (memcmp(&pnhc->nexthop->gate.ipv6,
688 &pnhi->nhr->prefix.u.prefix6, 16)
8babeb1a
SW
689 != 0)
690 goto done; /* Unrelated change */
691 break;
692 }
693
694 if (!pnhi->nhr->nexthop_num) {
695 is_valid = false;
696 goto done;
697 }
698
699 if (pnhc->nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
4550d5df 700 || pnhc->nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
8babeb1a
SW
701
702 /* GATEWAY_IFINDEX type shouldn't resolve to group */
703 if (pnhi->nhr->nexthop_num > 1) {
704 is_valid = false;
705 goto done;
706 }
707
708 /* If whatever we resolved to wasn't on the interface we
709 * specified. (i.e. not a connected route), its invalid.
710 */
711 if (pnhi->nhr->nexthops[0].ifindex != pnhc->nexthop->ifindex) {
712 is_valid = false;
713 goto done;
714 }
715 }
716
717 is_valid = true;
718
719done:
720 pnhc->valid = is_valid;
721
722 return pnhc->valid;
723}
724
725static bool pbr_nht_individual_nexthop_interface_update(
726 struct pbr_nexthop_cache *pnhc, const struct pbr_nht_individual *pnhi)
727{
728 bool is_valid = pnhc->valid;
729
730 if (!pnhi->ifp) /* It doesn't care about non-interface updates */
731 goto done;
732
733 if (pnhc->nexthop->ifindex
734 != pnhi->ifp->ifindex) /* Un-related interface */
735 goto done;
736
737 is_valid = !!if_is_up(pnhi->ifp);
738
739done:
740 pnhc->valid = is_valid;
741
742 return pnhc->valid;
743}
744
745/* Given this update either from interface or nexthop tracking, re-validate this
746 * nexthop.
747 *
748 * If the update is un-related, the subroutines shoud just return their cached
749 * valid state.
750 */
751static void
752pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache *pnhc,
753 const struct pbr_nht_individual *pnhi)
754{
755 assert(pnhi->nhr || pnhi->ifp); /* Either nexthop or interface update */
756
757 switch (pnhc->nexthop->type) {
758 case NEXTHOP_TYPE_IFINDEX:
759 pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
760 break;
cb254f41
SW
761 case NEXTHOP_TYPE_IPV6_IFINDEX:
762 if (IN6_IS_ADDR_LINKLOCAL(&pnhc->nexthop->gate.ipv6)) {
763 pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
764 break;
765 }
766 /* Intentional fall thru */
767 case NEXTHOP_TYPE_IPV4_IFINDEX:
8babeb1a
SW
768 case NEXTHOP_TYPE_IPV4:
769 case NEXTHOP_TYPE_IPV6:
8babeb1a
SW
770 pbr_nht_individual_nexthop_gw_update(pnhc, pnhi);
771 break;
772 case NEXTHOP_TYPE_BLACKHOLE:
773 pnhc->valid = true;
e5c83d9b
DS
774 break;
775 }
8babeb1a
SW
776}
777
778static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket *b,
779 void *data)
780{
781 struct pbr_nexthop_cache *pnhc = b->data;
782 struct pbr_nht_individual *pnhi = data;
783 char buf[PREFIX_STRLEN];
784 bool old_valid;
785
786 old_valid = pnhc->valid;
787
788 pbr_nht_individual_nexthop_update(pnhc, pnhi);
e5c83d9b
DS
789
790 DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d",
791 prefix2str(&pnhi->nhr->prefix, buf, sizeof(buf)), old_valid,
792 pnhc->valid);
793
e5c83d9b
DS
794 if (pnhc->valid)
795 pnhi->valid += 1;
796}
797
b822b93a
SW
798static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket *b,
799 void *data)
800{
801 struct pbr_nexthop_cache *pnhc = b->data;
802 struct nexthop_group *nhg = data;
803 struct nexthop *nh = NULL;
804
805 copy_nexthops(&nh, pnhc->nexthop, NULL);
806
50d89650 807 _nexthop_add(&nhg->nexthop, nh);
b822b93a
SW
808}
809
810static void
811pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group *nhg,
812 struct pbr_nexthop_group_cache *pnhgc)
813{
814 hash_iterate(pnhgc->nhh, pbr_nexthop_group_cache_iterate_to_group, nhg);
815}
816
e3b78da8 817static void pbr_nht_nexthop_update_lookup(struct hash_bucket *b, void *data)
e5c83d9b
DS
818{
819 struct pbr_nexthop_group_cache *pnhgc = b->data;
8babeb1a 820 struct pbr_nht_individual pnhi = {};
b822b93a 821 struct nexthop_group nhg = {};
b13e5ad6
DS
822 bool old_valid;
823
824 old_valid = pnhgc->valid;
e5c83d9b
DS
825
826 pnhi.nhr = (struct zapi_route *)data;
827 pnhi.valid = 0;
828 hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_update_lookup,
829 &pnhi);
830
831 /*
832 * If any of the specified nexthops are valid we are valid
833 */
834 pnhgc->valid = !!pnhi.valid;
b13e5ad6 835
b822b93a
SW
836 if (pnhgc->valid) {
837 pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc);
838 pbr_nht_install_nexthop_group(pnhgc, nhg);
839 /* Don't need copied nexthops anymore */
840 nexthops_free(nhg.nexthop);
841 }
842
b13e5ad6
DS
843 if (old_valid != pnhgc->valid)
844 pbr_map_check_nh_group_change(pnhgc->name);
e5c83d9b
DS
845}
846
847void pbr_nht_nexthop_update(struct zapi_route *nhr)
848{
849 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr);
850}
851
a106a408 852static void
7f5818fb 853pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket *b,
a106a408
RW
854 void *data)
855{
856 struct pbr_nexthop_cache *pnhc = b->data;
857 struct pbr_nht_individual *pnhi = data;
858 bool old_valid;
859
860 old_valid = pnhc->valid;
861
8babeb1a 862 pbr_nht_individual_nexthop_update(pnhc, pnhi);
a106a408
RW
863
864 DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d", pnhi->ifp->name,
865 old_valid, pnhc->valid);
866
867 if (pnhc->valid)
868 pnhi->valid += 1;
869}
870
7f5818fb 871static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket *b,
a106a408
RW
872 void *data)
873{
874 struct pbr_nexthop_group_cache *pnhgc = b->data;
8babeb1a 875 struct pbr_nht_individual pnhi = {};
a106a408
RW
876 bool old_valid;
877
878 old_valid = pnhgc->valid;
879
880 pnhi.ifp = data;
881 pnhi.valid = 0;
882 hash_iterate(pnhgc->nhh,
883 pbr_nht_individual_nexthop_interface_update_lookup, &pnhi);
884
885 /*
886 * If any of the specified nexthops are valid we are valid
887 */
888 pnhgc->valid = !!pnhi.valid;
889
890 if (old_valid != pnhgc->valid)
891 pbr_map_check_nh_group_change(pnhgc->name);
892}
893
894void pbr_nht_nexthop_interface_update(struct interface *ifp)
895{
896 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_update_lookup,
897 ifp);
898}
899
d8b87afe 900static uint32_t pbr_nhg_hash_key(const void *arg)
e5c83d9b 901{
d8b87afe 902 const struct pbr_nexthop_group_cache *nhgc = arg;
e5c83d9b
DS
903
904 return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96);
905}
906
74df8d6d 907static bool pbr_nhg_hash_equal(const void *arg1, const void *arg2)
e5c83d9b
DS
908{
909 const struct pbr_nexthop_group_cache *nhgc1 =
910 (const struct pbr_nexthop_group_cache *)arg1;
911 const struct pbr_nexthop_group_cache *nhgc2 =
912 (const struct pbr_nexthop_group_cache *)arg2;
913
914 return !strcmp(nhgc1->name, nhgc2->name);
915}
916
a4044dc1 917uint32_t pbr_nht_get_next_tableid(bool peek)
e5c83d9b
DS
918{
919 uint32_t i;
920 bool found = false;
921
922 for (i = pbr_nhg_low_table; i <= pbr_nhg_high_table; i++) {
d8729f8c 923 if (!nhg_tableid[i]) {
e5c83d9b
DS
924 found = true;
925 break;
926 }
927 }
928
929 if (found) {
a4044dc1 930 nhg_tableid[i] = !peek;
e5c83d9b
DS
931 return i;
932 } else
933 return 0;
934}
935
936void pbr_nht_set_tableid_range(uint32_t low, uint32_t high)
937{
938 pbr_nhg_low_table = low;
939 pbr_nhg_high_table = high;
940}
941
942void pbr_nht_write_table_range(struct vty *vty)
943{
944 if (pbr_nhg_low_table != PBR_NHT_DEFAULT_LOW_TABLEID
945 || pbr_nhg_high_table != PBR_NHT_DEFAULT_HIGH_TABLEID) {
946 vty_out(vty, "pbr table range %u %u\n", pbr_nhg_low_table,
947 pbr_nhg_high_table);
948 }
949}
950
951uint32_t pbr_nht_get_next_rule(uint32_t seqno)
952{
953 return seqno + pbr_nhg_low_rule - 1;
954}
955void pbr_nht_set_rule_range(uint32_t low, uint32_t high)
956{
957 pbr_nhg_low_rule = low;
958 pbr_nhg_high_rule = high;
959}
960
961void pbr_nht_write_rule_range(struct vty *vty)
962{
963 if (pbr_nhg_low_rule != PBR_NHT_DEFAULT_LOW_RULE
964 || pbr_nhg_high_rule != PBR_NHT_DEFAULT_HIGH_RULE) {
965 vty_out(vty, "pbr rule range %u %u\n", pbr_nhg_low_rule,
966 pbr_nhg_high_rule);
967 }
968}
969
970uint32_t pbr_nht_get_table(const char *name)
971{
972 struct pbr_nexthop_group_cache find;
973 struct pbr_nexthop_group_cache *pnhgc;
974
975 memset(&find, 0, sizeof(find));
6612590d 976 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
977 pnhgc = hash_lookup(pbr_nhg_hash, &find);
978
979 if (!pnhgc) {
980 DEBUGD(&pbr_dbg_nht,
981 "%s: Could not find nexthop-group cache w/ name '%s'",
15569c58 982 __func__, name);
e5c83d9b
DS
983 return 5000;
984 }
985
986 return pnhgc->table_id;
987}
988
989bool pbr_nht_get_installed(const char *name)
990{
991 struct pbr_nexthop_group_cache find;
992 struct pbr_nexthop_group_cache *pnhgc;
993
994 memset(&find, 0, sizeof(find));
6612590d 995 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
996
997 pnhgc = hash_lookup(pbr_nhg_hash, &find);
998
d3765386 999 if (!pnhgc)
e5c83d9b 1000 return false;
e5c83d9b
DS
1001
1002 return pnhgc->installed;
1003}
1004
e3b78da8 1005static void pbr_nht_show_nhg_nexthops(struct hash_bucket *b, void *data)
e5c83d9b
DS
1006{
1007 struct pbr_nexthop_cache *pnhc = b->data;
1008 struct vty *vty = data;
1009
57cdafc4 1010 vty_out(vty, "\tValid: %d ", pnhc->valid);
b13e5ad6 1011 nexthop_group_write_nexthop(vty, pnhc->nexthop);
e5c83d9b
DS
1012}
1013
1014struct pbr_nht_show {
1015 struct vty *vty;
1016 const char *name;
1017};
1018
e3b78da8 1019static void pbr_nht_show_nhg(struct hash_bucket *b, void *data)
e5c83d9b
DS
1020{
1021 struct pbr_nexthop_group_cache *pnhgc = b->data;
1022 struct pbr_nht_show *pns = data;
1023 struct vty *vty;
1024
1025 if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
1026 return;
1027
1028 vty = pns->vty;
1029 vty_out(vty, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
1030 pnhgc->name, pnhgc->table_id, pnhgc->valid, pnhgc->installed);
1031
1032 hash_iterate(pnhgc->nhh, pbr_nht_show_nhg_nexthops, vty);
1033}
1034
1035void pbr_nht_show_nexthop_group(struct vty *vty, const char *name)
1036{
1037 struct pbr_nht_show pns;
1038
1039 pns.vty = vty;
1040 pns.name = name;
1041
1042 hash_iterate(pbr_nhg_hash, pbr_nht_show_nhg, &pns);
1043}
1044
1045void pbr_nht_init(void)
1046{
1047 pbr_nhg_hash = hash_create_size(
1048 16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash");
b13e5ad6 1049 pbr_nhrc_hash =
d8b87afe 1050 hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash,
b13e5ad6 1051 pbr_nhrc_hash_equal, "PBR NH Hash");
e5c83d9b
DS
1052
1053 pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID;
1054 pbr_nhg_high_table = PBR_NHT_DEFAULT_HIGH_TABLEID;
1055 pbr_nhg_low_rule = PBR_NHT_DEFAULT_LOW_RULE;
1056 pbr_nhg_high_rule = PBR_NHT_DEFAULT_HIGH_RULE;
1057 memset(&nhg_tableid, 0, 65535 * sizeof(uint8_t));
1058}