]> git.proxmox.com Git - mirror_frr.git/blame - pbrd/pbr_nht.c
pbrd: nexthop_group delete cb don't free pbr->nhg
[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
DS
99
100 DEBUGD(&pbr_dbg_nht, "%s: Sending nexthop to Zebra",
101 __PRETTY_FUNCTION__);
102
b13e5ad6 103 pbr_send_rnh(new->nexthop, true);
e5c83d9b
DS
104
105 new->valid = false;
106 return new;
107}
108
109static void pbr_nh_delete(struct pbr_nexthop_cache **pnhc)
110{
b13e5ad6
DS
111 struct nhrc *nhrc;
112
113 nhrc = hash_lookup(pbr_nhrc_hash, (*pnhc)->nexthop);
114
115 if (nhrc)
116 --nhrc->refcount;
117 if (!nhrc || nhrc->refcount == 0) {
118 DEBUGD(&pbr_dbg_nht, "%s: Removing nexthop from Zebra",
119 __PRETTY_FUNCTION__);
120 pbr_send_rnh((*pnhc)->nexthop, false);
121 }
122 if (nhrc && nhrc->refcount == 0) {
123 hash_release(pbr_nhrc_hash, nhrc);
124 XFREE(MTYPE_PBR_NHG, nhrc);
125 }
e5c83d9b
DS
126
127 XFREE(MTYPE_PBR_NHG, *pnhc);
128}
129
e3b78da8 130static void pbr_nh_delete_iterate(struct hash_bucket *b, void *p)
b13e5ad6
DS
131{
132 pbr_nh_delete((struct pbr_nexthop_cache **)&b->data);
133}
134
d8b87afe 135static uint32_t pbr_nh_hash_key(const void *arg)
e5c83d9b
DS
136{
137 uint32_t key;
d8b87afe 138 const struct pbr_nexthop_cache *pbrnc = arg;
e5c83d9b 139
b13e5ad6 140 key = nexthop_hash(pbrnc->nexthop);
e5c83d9b
DS
141
142 return key;
143}
144
74df8d6d 145static bool pbr_nh_hash_equal(const void *arg1, const void *arg2)
e5c83d9b
DS
146{
147 const struct pbr_nexthop_cache *pbrnc1 =
148 (const struct pbr_nexthop_cache *)arg1;
149 const struct pbr_nexthop_cache *pbrnc2 =
150 (const struct pbr_nexthop_cache *)arg2;
151
b13e5ad6 152 if (pbrnc1->nexthop->vrf_id != pbrnc2->nexthop->vrf_id)
74df8d6d 153 return false;
e5c83d9b 154
b13e5ad6 155 if (pbrnc1->nexthop->ifindex != pbrnc2->nexthop->ifindex)
74df8d6d 156 return false;
e5c83d9b 157
b13e5ad6 158 if (pbrnc1->nexthop->type != pbrnc2->nexthop->type)
74df8d6d 159 return false;
e5c83d9b 160
b13e5ad6 161 switch (pbrnc1->nexthop->type) {
e5c83d9b 162 case NEXTHOP_TYPE_IFINDEX:
a106a408 163 return pbrnc1->nexthop->ifindex == pbrnc2->nexthop->ifindex;
e5c83d9b
DS
164 case NEXTHOP_TYPE_IPV4_IFINDEX:
165 case NEXTHOP_TYPE_IPV4:
b13e5ad6
DS
166 return pbrnc1->nexthop->gate.ipv4.s_addr
167 == pbrnc2->nexthop->gate.ipv4.s_addr;
e5c83d9b
DS
168 case NEXTHOP_TYPE_IPV6_IFINDEX:
169 case NEXTHOP_TYPE_IPV6:
f24f3450
RW
170 return !memcmp(&pbrnc1->nexthop->gate.ipv6,
171 &pbrnc2->nexthop->gate.ipv6, 16);
e5c83d9b 172 case NEXTHOP_TYPE_BLACKHOLE:
b13e5ad6 173 return pbrnc1->nexthop->bh_type == pbrnc2->nexthop->bh_type;
e5c83d9b
DS
174 }
175
176 /*
177 * We should not get here
178 */
74df8d6d 179 return false;
e5c83d9b
DS
180}
181
b13e5ad6
DS
182static void pbr_nhgc_delete(struct pbr_nexthop_group_cache *p)
183{
184 hash_iterate(p->nhh, pbr_nh_delete_iterate, NULL);
185 hash_free(p->nhh);
186 XFREE(MTYPE_PBR_NHG, p);
187}
188
189static void *pbr_nhgc_alloc(void *p)
190{
191 struct pbr_nexthop_group_cache *new;
192 struct pbr_nexthop_group_cache *pnhgc =
193 (struct pbr_nexthop_group_cache *)p;
194
195 new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new));
196
65b88efa 197 strlcpy(new->name, pnhgc->name, sizeof(pnhgc->name));
a4044dc1 198 new->table_id = pbr_nht_get_next_tableid(false);
b13e5ad6
DS
199
200 DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u",
201 __PRETTY_FUNCTION__, new->name, new->table_id);
202
203 new->nhh = hash_create_size(8, pbr_nh_hash_key, pbr_nh_hash_equal,
204 "PBR NH Cache Hash");
205 return new;
206}
207
208
e5c83d9b
DS
209void pbr_nhgroup_add_cb(const char *name)
210{
b13e5ad6
DS
211 struct pbr_nexthop_group_cache *pnhgc;
212 struct nexthop_group_cmd *nhgc;
e5c83d9b 213
b13e5ad6 214 nhgc = nhgc_find(name);
68a63f60
QY
215
216 if (!nhgc) {
217 DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s\n",
218 __PRETTY_FUNCTION__, name);
219 return;
220 }
221
b13e5ad6 222 pnhgc = pbr_nht_add_group(name);
e5c83d9b 223
a4044dc1
QY
224 if (!pnhgc)
225 return;
226
b13e5ad6 227 DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __PRETTY_FUNCTION__,
e5c83d9b 228 name);
b13e5ad6 229
b13e5ad6 230 pbr_map_check_nh_group_change(name);
e5c83d9b
DS
231}
232
b13e5ad6 233void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc,
e5c83d9b
DS
234 const struct nexthop *nhop)
235{
b13e5ad6 236 char debugstr[256];
3e300703 237 struct pbr_nexthop_group_cache pnhgc_find = {};
b13e5ad6 238 struct pbr_nexthop_group_cache *pnhgc;
3e300703 239 struct pbr_nexthop_cache pnhc_find = {};
b13e5ad6
DS
240 struct pbr_nexthop_cache *pnhc;
241
a4044dc1
QY
242 if (!pbr_nht_get_next_tableid(true)) {
243 zlog_warn(
244 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
245 __PRETTY_FUNCTION__, nhgc->name);
246 return;
247 }
248
b13e5ad6
DS
249 /* find pnhgc by name */
250 strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
251 pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc);
e5c83d9b 252
b13e5ad6
DS
253 /* create & insert new pnhc into pnhgc->nhh */
254 pnhc_find.nexthop = (struct nexthop *)nhop;
255 pnhc = hash_get(pnhgc->nhh, &pnhc_find, pbr_nh_alloc);
256 pnhc_find.nexthop = NULL;
257
258 /* set parent pnhgc */
259 pnhc->parent = pnhgc;
e5c83d9b 260
b13e5ad6
DS
261 if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) {
262 nexthop2str(nhop, debugstr, sizeof(debugstr));
263 DEBUGD(&pbr_dbg_nht, "%s: Added %s to nexthop-group %s",
264 __PRETTY_FUNCTION__, debugstr, nhgc->name);
265 }
266
267 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
268 pbr_map_check_nh_group_change(nhgc->name);
a106a408 269
cb254f41
SW
270 if (nhop->type == NEXTHOP_TYPE_IFINDEX
271 || (nhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
272 && IN6_IS_ADDR_LINKLOCAL(&nhop->gate.ipv6))) {
a106a408
RW
273 struct interface *ifp;
274
275 ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id);
276 if (ifp)
277 pbr_nht_nexthop_interface_update(ifp);
278 }
e5c83d9b
DS
279}
280
b13e5ad6 281void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc,
e5c83d9b
DS
282 const struct nexthop *nhop)
283{
b13e5ad6 284 char debugstr[256];
3e300703 285 struct pbr_nexthop_group_cache pnhgc_find = {};
b13e5ad6 286 struct pbr_nexthop_group_cache *pnhgc;
3e300703 287 struct pbr_nexthop_cache pnhc_find = {};
b13e5ad6 288 struct pbr_nexthop_cache *pnhc;
aafac994 289 enum nexthop_types_t nh_type = nhop->type;
b13e5ad6
DS
290
291 /* find pnhgc by name */
292 strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
a4044dc1 293 pnhgc = hash_lookup(pbr_nhg_hash, &pnhgc_find);
b13e5ad6
DS
294
295 /* delete pnhc from pnhgc->nhh */
296 pnhc_find.nexthop = (struct nexthop *)nhop;
297 pnhc = hash_release(pnhgc->nhh, &pnhc_find);
298
299 /* delete pnhc */
300 pbr_nh_delete(&pnhc);
e5c83d9b 301
b13e5ad6
DS
302 if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) {
303 nexthop2str(nhop, debugstr, sizeof(debugstr));
304 DEBUGD(&pbr_dbg_nht, "%s: Removed %s from nexthop-group %s",
305 __PRETTY_FUNCTION__, debugstr, nhgc->name);
306 }
e5c83d9b 307
ff9799c3
DS
308 if (pnhgc->nhh->count)
309 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
310 else
aafac994 311 pbr_nht_uninstall_nexthop_group(pnhgc, nhgc->nhg, nh_type);
ff9799c3 312
b13e5ad6 313 pbr_map_check_nh_group_change(nhgc->name);
e5c83d9b
DS
314}
315
316void pbr_nhgroup_delete_cb(const char *name)
317{
b13e5ad6 318 DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s",
e5c83d9b 319 __PRETTY_FUNCTION__, name);
b13e5ad6 320
ff9799c3
DS
321 /* delete group from all pbrms's */
322 pbr_nht_delete_group(name);
323
b13e5ad6 324 pbr_map_check_nh_group_change(name);
e5c83d9b
DS
325}
326
327#if 0
328static struct pbr_nexthop_cache *pbr_nht_lookup_nexthop(struct nexthop *nexthop)
329{
330 return NULL;
331}
332#endif
333
e3b78da8 334static void pbr_nht_find_nhg_from_table_install(struct hash_bucket *b,
e5c83d9b
DS
335 void *data)
336{
337 struct pbr_nexthop_group_cache *pnhgc =
338 (struct pbr_nexthop_group_cache *)b->data;
339 uint32_t *table_id = (uint32_t *)data;
340
341 if (pnhgc->table_id == *table_id) {
342 DEBUGD(&pbr_dbg_nht, "%s: Table ID (%u) matches %s",
343 __PRETTY_FUNCTION__, *table_id, pnhgc->name);
2fb7892e
DS
344
345 /*
346 * If the table has been re-handled by zebra
347 * and we are already installed no need to do
348 * anything here.
349 */
350 if (!pnhgc->installed) {
351 pnhgc->installed = true;
352 pbr_map_schedule_policy_from_nhg(pnhgc->name);
353 }
e5c83d9b
DS
354 }
355}
356
357void pbr_nht_route_installed_for_table(uint32_t table_id)
358{
359 hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_install,
360 &table_id);
361}
362
e3b78da8 363static void pbr_nht_find_nhg_from_table_remove(struct hash_bucket *b,
e5c83d9b
DS
364 void *data)
365{
366 ;
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,
437 "%s: Saw both V6 and V4 nexthops...using %s",
438 __PRETTY_FUNCTION__, afi2str(install_afi));
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.",
442 __PRETTY_FUNCTION__, v4 ? "v4" : "",
443 (v4 && v6) ? " and " : "", v6 ? "v6" : "");
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'",
491 __PRETTY_FUNCTION__, name);
492 return;
493 }
494
495 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
496 struct pbr_nexthop_cache lookup;
497 struct pbr_nexthop_cache *pnhc;
498
b13e5ad6 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
b13e5ad6 516void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms)
e5c83d9b
DS
517{
518 struct pbr_nexthop_group_cache *pnhgc;
519 struct pbr_nexthop_group_cache find;
520 struct pbr_nexthop_cache *pnhc;
e5c83d9b
DS
521 struct pbr_nexthop_cache lookup;
522
e5c83d9b 523 memset(&find, 0, sizeof(find));
06210d1f 524 pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
e5c83d9b 525 pbrms->seqno, find.name);
a4044dc1
QY
526
527 if (!pbr_nht_get_next_tableid(true)) {
528 zlog_warn(
529 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
530 __PRETTY_FUNCTION__, find.name);
531 return;
532 }
533
e5c83d9b
DS
534 if (!pbrms->internal_nhg_name)
535 pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name);
536
537 pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc);
538
b13e5ad6 539 lookup.nexthop = pbrms->nhg->nexthop;
e5c83d9b
DS
540 pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
541 pnhc->parent = pnhgc;
542 pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg);
543}
544
b13e5ad6 545void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
e5c83d9b
DS
546{
547 struct pbr_nexthop_group_cache *pnhgc;
548 struct pbr_nexthop_group_cache find;
549 struct pbr_nexthop_cache *pnhc;
550 struct pbr_nexthop_cache lup;
b13e5ad6
DS
551 struct pbr_map *pbrm = pbrms->parent;
552 struct listnode *node;
553 struct pbr_map_interface *pmi;
e5c83d9b 554 struct nexthop *nh;
aafac994 555 enum nexthop_types_t nh_type = 0;
e5c83d9b 556
b13e5ad6
DS
557 if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) {
558 for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi))
559 pbr_send_pbr_map(pbrms, pmi, false);
560 }
561
562 pbrm->valid = false;
563 pbrms->nhs_installed = false;
b13e5ad6 564 pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
e5c83d9b
DS
565
566 memset(&find, 0, sizeof(find));
6612590d 567 snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name);
e5c83d9b
DS
568 pnhgc = hash_lookup(pbr_nhg_hash, &find);
569
570 nh = pbrms->nhg->nexthop;
aafac994 571 nh_type = nh->type;
b13e5ad6 572 lup.nexthop = nh;
e5c83d9b
DS
573 pnhc = hash_lookup(pnhgc->nhh, &lup);
574 pnhc->parent = NULL;
575 hash_release(pnhgc->nhh, pnhc);
576 pbr_nh_delete(&pnhc);
aafac994 577 pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_type);
e5c83d9b
DS
578
579 hash_release(pbr_nhg_hash, pnhgc);
580
50d89650 581 _nexthop_del(pbrms->nhg, nh);
e5c83d9b
DS
582 nexthop_free(nh);
583 nexthop_group_delete(&pbrms->nhg);
584 XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
585}
586
b13e5ad6 587struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name)
e5c83d9b
DS
588{
589 struct nexthop *nhop;
590 struct nexthop_group_cmd *nhgc;
591 struct pbr_nexthop_group_cache *pnhgc;
592 struct pbr_nexthop_group_cache lookup;
593
a4044dc1
QY
594 if (!pbr_nht_get_next_tableid(true)) {
595 zlog_warn(
596 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
597 __PRETTY_FUNCTION__, name);
598 return NULL;
599 }
600
e5c83d9b
DS
601 nhgc = nhgc_find(name);
602
603 if (!nhgc) {
a4044dc1
QY
604 DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s\n",
605 __PRETTY_FUNCTION__, name);
b13e5ad6 606 return NULL;
e5c83d9b
DS
607 }
608
6612590d 609 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
e5c83d9b
DS
610 pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc);
611 DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __PRETTY_FUNCTION__,
612 pnhgc);
613
614 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
7fe96307 615 struct pbr_nexthop_cache lookupc;
e5c83d9b
DS
616 struct pbr_nexthop_cache *pnhc;
617
7fe96307
A
618 lookupc.nexthop = nhop;
619 pnhc = hash_lookup(pnhgc->nhh, &lookupc);
e5c83d9b 620 if (!pnhc) {
7fe96307 621 pnhc = hash_get(pnhgc->nhh, &lookupc, pbr_nh_alloc);
e5c83d9b
DS
622 pnhc->parent = pnhgc;
623 }
624 }
b13e5ad6
DS
625
626 return pnhgc;
e5c83d9b
DS
627}
628
629void pbr_nht_delete_group(const char *name)
630{
631 struct pbr_map_sequence *pbrms;
632 struct listnode *snode;
633 struct pbr_map *pbrm;
b13e5ad6
DS
634 struct pbr_nexthop_group_cache pnhgc_find;
635 struct pbr_nexthop_group_cache *pnhgc;
e5c83d9b
DS
636
637 RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
638 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) {
639 if (pbrms->nhgrp_name
b13e5ad6 640 && strmatch(pbrms->nhgrp_name, name)) {
e5c83d9b 641 pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
b13e5ad6
DS
642 pbrms->nhg = NULL;
643 pbrms->internal_nhg_name = NULL;
e5c83d9b
DS
644 pbrm->valid = false;
645 }
646 }
647 }
b13e5ad6
DS
648
649 strlcpy(pnhgc_find.name, name, sizeof(pnhgc_find.name));
650 pnhgc = hash_release(pbr_nhg_hash, &pnhgc_find);
651 pbr_nhgc_delete(pnhgc);
e5c83d9b
DS
652}
653
654bool pbr_nht_nexthop_valid(struct nexthop_group *nhg)
655{
656 DEBUGD(&pbr_dbg_nht, "%s: %p", __PRETTY_FUNCTION__, nhg);
657 return true;
658}
659
660bool pbr_nht_nexthop_group_valid(const char *name)
661{
662 struct pbr_nexthop_group_cache *pnhgc;
663 struct pbr_nexthop_group_cache lookup;
664
665 DEBUGD(&pbr_dbg_nht, "%s: %s", __PRETTY_FUNCTION__, name);
666
6612590d 667 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
e5c83d9b
DS
668 pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL);
669 if (!pnhgc)
670 return false;
671 DEBUGD(&pbr_dbg_nht, "%s: \t%d %d", __PRETTY_FUNCTION__, pnhgc->valid,
672 pnhgc->installed);
673 if (pnhgc->valid && pnhgc->installed)
674 return true;
675
676 return false;
677}
678
679struct pbr_nht_individual {
680 struct zapi_route *nhr;
a106a408 681 struct interface *ifp;
e5c83d9b
DS
682
683 uint32_t valid;
684};
685
8babeb1a
SW
686static bool
687pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache *pnhc,
688 const struct pbr_nht_individual *pnhi)
e5c83d9b 689{
8babeb1a 690 bool is_valid = pnhc->valid;
e5c83d9b 691
8babeb1a
SW
692 if (!pnhi->nhr) /* It doesn't care about non-nexthop updates */
693 goto done;
e5c83d9b
DS
694
695 switch (pnhi->nhr->prefix.family) {
696 case AF_INET:
b13e5ad6 697 if (pnhc->nexthop->gate.ipv4.s_addr
8babeb1a
SW
698 != pnhi->nhr->prefix.u.prefix4.s_addr)
699 goto done; /* Unrelated change */
e5c83d9b
DS
700 break;
701 case AF_INET6:
b13e5ad6
DS
702 if (memcmp(&pnhc->nexthop->gate.ipv6,
703 &pnhi->nhr->prefix.u.prefix6, 16)
8babeb1a
SW
704 != 0)
705 goto done; /* Unrelated change */
706 break;
707 }
708
709 if (!pnhi->nhr->nexthop_num) {
710 is_valid = false;
711 goto done;
712 }
713
714 if (pnhc->nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
4550d5df 715 || pnhc->nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
8babeb1a
SW
716
717 /* GATEWAY_IFINDEX type shouldn't resolve to group */
718 if (pnhi->nhr->nexthop_num > 1) {
719 is_valid = false;
720 goto done;
721 }
722
723 /* If whatever we resolved to wasn't on the interface we
724 * specified. (i.e. not a connected route), its invalid.
725 */
726 if (pnhi->nhr->nexthops[0].ifindex != pnhc->nexthop->ifindex) {
727 is_valid = false;
728 goto done;
729 }
730 }
731
732 is_valid = true;
733
734done:
735 pnhc->valid = is_valid;
736
737 return pnhc->valid;
738}
739
740static bool pbr_nht_individual_nexthop_interface_update(
741 struct pbr_nexthop_cache *pnhc, const struct pbr_nht_individual *pnhi)
742{
743 bool is_valid = pnhc->valid;
744
745 if (!pnhi->ifp) /* It doesn't care about non-interface updates */
746 goto done;
747
748 if (pnhc->nexthop->ifindex
749 != pnhi->ifp->ifindex) /* Un-related interface */
750 goto done;
751
752 is_valid = !!if_is_up(pnhi->ifp);
753
754done:
755 pnhc->valid = is_valid;
756
757 return pnhc->valid;
758}
759
760/* Given this update either from interface or nexthop tracking, re-validate this
761 * nexthop.
762 *
763 * If the update is un-related, the subroutines shoud just return their cached
764 * valid state.
765 */
766static void
767pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache *pnhc,
768 const struct pbr_nht_individual *pnhi)
769{
770 assert(pnhi->nhr || pnhi->ifp); /* Either nexthop or interface update */
771
772 switch (pnhc->nexthop->type) {
773 case NEXTHOP_TYPE_IFINDEX:
774 pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
775 break;
cb254f41
SW
776 case NEXTHOP_TYPE_IPV6_IFINDEX:
777 if (IN6_IS_ADDR_LINKLOCAL(&pnhc->nexthop->gate.ipv6)) {
778 pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
779 break;
780 }
781 /* Intentional fall thru */
782 case NEXTHOP_TYPE_IPV4_IFINDEX:
8babeb1a
SW
783 case NEXTHOP_TYPE_IPV4:
784 case NEXTHOP_TYPE_IPV6:
8babeb1a
SW
785 pbr_nht_individual_nexthop_gw_update(pnhc, pnhi);
786 break;
787 case NEXTHOP_TYPE_BLACKHOLE:
788 pnhc->valid = true;
e5c83d9b
DS
789 break;
790 }
8babeb1a
SW
791}
792
793static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket *b,
794 void *data)
795{
796 struct pbr_nexthop_cache *pnhc = b->data;
797 struct pbr_nht_individual *pnhi = data;
798 char buf[PREFIX_STRLEN];
799 bool old_valid;
800
801 old_valid = pnhc->valid;
802
803 pbr_nht_individual_nexthop_update(pnhc, pnhi);
e5c83d9b
DS
804
805 DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d",
806 prefix2str(&pnhi->nhr->prefix, buf, sizeof(buf)), old_valid,
807 pnhc->valid);
808
e5c83d9b
DS
809 if (pnhc->valid)
810 pnhi->valid += 1;
811}
812
b822b93a
SW
813static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket *b,
814 void *data)
815{
816 struct pbr_nexthop_cache *pnhc = b->data;
817 struct nexthop_group *nhg = data;
818 struct nexthop *nh = NULL;
819
820 copy_nexthops(&nh, pnhc->nexthop, NULL);
821
50d89650 822 _nexthop_add(&nhg->nexthop, nh);
b822b93a
SW
823}
824
825static void
826pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group *nhg,
827 struct pbr_nexthop_group_cache *pnhgc)
828{
829 hash_iterate(pnhgc->nhh, pbr_nexthop_group_cache_iterate_to_group, nhg);
830}
831
e3b78da8 832static void pbr_nht_nexthop_update_lookup(struct hash_bucket *b, void *data)
e5c83d9b
DS
833{
834 struct pbr_nexthop_group_cache *pnhgc = b->data;
8babeb1a 835 struct pbr_nht_individual pnhi = {};
b822b93a 836 struct nexthop_group nhg = {};
b13e5ad6
DS
837 bool old_valid;
838
839 old_valid = pnhgc->valid;
e5c83d9b
DS
840
841 pnhi.nhr = (struct zapi_route *)data;
842 pnhi.valid = 0;
843 hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_update_lookup,
844 &pnhi);
845
846 /*
847 * If any of the specified nexthops are valid we are valid
848 */
849 pnhgc->valid = !!pnhi.valid;
b13e5ad6 850
b822b93a
SW
851 if (pnhgc->valid) {
852 pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc);
853 pbr_nht_install_nexthop_group(pnhgc, nhg);
854 /* Don't need copied nexthops anymore */
855 nexthops_free(nhg.nexthop);
856 }
857
b13e5ad6
DS
858 if (old_valid != pnhgc->valid)
859 pbr_map_check_nh_group_change(pnhgc->name);
e5c83d9b
DS
860}
861
862void pbr_nht_nexthop_update(struct zapi_route *nhr)
863{
864 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr);
865}
866
a106a408
RW
867static void
868pbr_nht_individual_nexthop_interface_update_lookup(struct hash_backet *b,
869 void *data)
870{
871 struct pbr_nexthop_cache *pnhc = b->data;
872 struct pbr_nht_individual *pnhi = data;
873 bool old_valid;
874
875 old_valid = pnhc->valid;
876
8babeb1a 877 pbr_nht_individual_nexthop_update(pnhc, pnhi);
a106a408
RW
878
879 DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d", pnhi->ifp->name,
880 old_valid, pnhc->valid);
881
882 if (pnhc->valid)
883 pnhi->valid += 1;
884}
885
886static void pbr_nht_nexthop_interface_update_lookup(struct hash_backet *b,
887 void *data)
888{
889 struct pbr_nexthop_group_cache *pnhgc = b->data;
8babeb1a 890 struct pbr_nht_individual pnhi = {};
a106a408
RW
891 bool old_valid;
892
893 old_valid = pnhgc->valid;
894
895 pnhi.ifp = data;
896 pnhi.valid = 0;
897 hash_iterate(pnhgc->nhh,
898 pbr_nht_individual_nexthop_interface_update_lookup, &pnhi);
899
900 /*
901 * If any of the specified nexthops are valid we are valid
902 */
903 pnhgc->valid = !!pnhi.valid;
904
905 if (old_valid != pnhgc->valid)
906 pbr_map_check_nh_group_change(pnhgc->name);
907}
908
909void pbr_nht_nexthop_interface_update(struct interface *ifp)
910{
911 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_update_lookup,
912 ifp);
913}
914
d8b87afe 915static uint32_t pbr_nhg_hash_key(const void *arg)
e5c83d9b 916{
d8b87afe 917 const struct pbr_nexthop_group_cache *nhgc = arg;
e5c83d9b
DS
918
919 return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96);
920}
921
74df8d6d 922static bool pbr_nhg_hash_equal(const void *arg1, const void *arg2)
e5c83d9b
DS
923{
924 const struct pbr_nexthop_group_cache *nhgc1 =
925 (const struct pbr_nexthop_group_cache *)arg1;
926 const struct pbr_nexthop_group_cache *nhgc2 =
927 (const struct pbr_nexthop_group_cache *)arg2;
928
929 return !strcmp(nhgc1->name, nhgc2->name);
930}
931
a4044dc1 932uint32_t pbr_nht_get_next_tableid(bool peek)
e5c83d9b
DS
933{
934 uint32_t i;
935 bool found = false;
936
937 for (i = pbr_nhg_low_table; i <= pbr_nhg_high_table; i++) {
d8729f8c 938 if (!nhg_tableid[i]) {
e5c83d9b
DS
939 found = true;
940 break;
941 }
942 }
943
944 if (found) {
a4044dc1 945 nhg_tableid[i] = !peek;
e5c83d9b
DS
946 return i;
947 } else
948 return 0;
949}
950
951void pbr_nht_set_tableid_range(uint32_t low, uint32_t high)
952{
953 pbr_nhg_low_table = low;
954 pbr_nhg_high_table = high;
955}
956
957void pbr_nht_write_table_range(struct vty *vty)
958{
959 if (pbr_nhg_low_table != PBR_NHT_DEFAULT_LOW_TABLEID
960 || pbr_nhg_high_table != PBR_NHT_DEFAULT_HIGH_TABLEID) {
961 vty_out(vty, "pbr table range %u %u\n", pbr_nhg_low_table,
962 pbr_nhg_high_table);
963 }
964}
965
966uint32_t pbr_nht_get_next_rule(uint32_t seqno)
967{
968 return seqno + pbr_nhg_low_rule - 1;
969}
970void pbr_nht_set_rule_range(uint32_t low, uint32_t high)
971{
972 pbr_nhg_low_rule = low;
973 pbr_nhg_high_rule = high;
974}
975
976void pbr_nht_write_rule_range(struct vty *vty)
977{
978 if (pbr_nhg_low_rule != PBR_NHT_DEFAULT_LOW_RULE
979 || pbr_nhg_high_rule != PBR_NHT_DEFAULT_HIGH_RULE) {
980 vty_out(vty, "pbr rule range %u %u\n", pbr_nhg_low_rule,
981 pbr_nhg_high_rule);
982 }
983}
984
985uint32_t pbr_nht_get_table(const char *name)
986{
987 struct pbr_nexthop_group_cache find;
988 struct pbr_nexthop_group_cache *pnhgc;
989
990 memset(&find, 0, sizeof(find));
6612590d 991 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
992 pnhgc = hash_lookup(pbr_nhg_hash, &find);
993
994 if (!pnhgc) {
995 DEBUGD(&pbr_dbg_nht,
996 "%s: Could not find nexthop-group cache w/ name '%s'",
997 __PRETTY_FUNCTION__, name);
998 return 5000;
999 }
1000
1001 return pnhgc->table_id;
1002}
1003
1004bool pbr_nht_get_installed(const char *name)
1005{
1006 struct pbr_nexthop_group_cache find;
1007 struct pbr_nexthop_group_cache *pnhgc;
1008
1009 memset(&find, 0, sizeof(find));
6612590d 1010 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
1011
1012 pnhgc = hash_lookup(pbr_nhg_hash, &find);
1013
d3765386 1014 if (!pnhgc)
e5c83d9b 1015 return false;
e5c83d9b
DS
1016
1017 return pnhgc->installed;
1018}
1019
e3b78da8 1020static void pbr_nht_show_nhg_nexthops(struct hash_bucket *b, void *data)
e5c83d9b
DS
1021{
1022 struct pbr_nexthop_cache *pnhc = b->data;
1023 struct vty *vty = data;
1024
57cdafc4 1025 vty_out(vty, "\tValid: %d ", pnhc->valid);
b13e5ad6 1026 nexthop_group_write_nexthop(vty, pnhc->nexthop);
e5c83d9b
DS
1027}
1028
1029struct pbr_nht_show {
1030 struct vty *vty;
1031 const char *name;
1032};
1033
e3b78da8 1034static void pbr_nht_show_nhg(struct hash_bucket *b, void *data)
e5c83d9b
DS
1035{
1036 struct pbr_nexthop_group_cache *pnhgc = b->data;
1037 struct pbr_nht_show *pns = data;
1038 struct vty *vty;
1039
1040 if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
1041 return;
1042
1043 vty = pns->vty;
1044 vty_out(vty, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
1045 pnhgc->name, pnhgc->table_id, pnhgc->valid, pnhgc->installed);
1046
1047 hash_iterate(pnhgc->nhh, pbr_nht_show_nhg_nexthops, vty);
1048}
1049
1050void pbr_nht_show_nexthop_group(struct vty *vty, const char *name)
1051{
1052 struct pbr_nht_show pns;
1053
1054 pns.vty = vty;
1055 pns.name = name;
1056
1057 hash_iterate(pbr_nhg_hash, pbr_nht_show_nhg, &pns);
1058}
1059
1060void pbr_nht_init(void)
1061{
1062 pbr_nhg_hash = hash_create_size(
1063 16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash");
b13e5ad6 1064 pbr_nhrc_hash =
d8b87afe 1065 hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash,
b13e5ad6 1066 pbr_nhrc_hash_equal, "PBR NH Hash");
e5c83d9b
DS
1067
1068 pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID;
1069 pbr_nhg_high_table = PBR_NHT_DEFAULT_HIGH_TABLEID;
1070 pbr_nhg_low_rule = PBR_NHT_DEFAULT_LOW_RULE;
1071 pbr_nhg_high_rule = PBR_NHT_DEFAULT_HIGH_RULE;
1072 memset(&nhg_tableid, 0, 65535 * sizeof(uint8_t));
1073}