]> git.proxmox.com Git - mirror_frr.git/blame - pbrd/pbr_nht.c
pbrd: fix detection of inconsistent nexthop groups
[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>
24#include <nexthop_group.h>
25#include <hash.h>
26#include <jhash.h>
27#include <vty.h>
28#include <zclient.h>
b13e5ad6 29#include <debug.h>
e5c83d9b
DS
30
31#include "pbrd/pbr_nht.h"
32#include "pbrd/pbr_map.h"
e5c83d9b
DS
33#include "pbrd/pbr_zebra.h"
34#include "pbrd/pbr_memory.h"
35#include "pbrd/pbr_debug.h"
36
37DEFINE_MTYPE_STATIC(PBRD, PBR_NHG, "PBR Nexthop Groups")
38
39static struct hash *pbr_nhg_hash;
b13e5ad6 40static struct hash *pbr_nhrc_hash;
e5c83d9b
DS
41
42static uint32_t pbr_nhg_low_table;
43static uint32_t pbr_nhg_high_table;
44static uint32_t pbr_nhg_low_rule;
45static uint32_t pbr_nhg_high_rule;
46static bool nhg_tableid[65535];
47
b13e5ad6
DS
48static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
49 struct nexthop_group nhg);
ff9799c3
DS
50static void
51pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
52 struct nexthop_group nhg,
aafac994 53 enum nexthop_types_t nh_type);
b13e5ad6
DS
54
55/*
56 * Nexthop refcount.
57 */
58struct nhrc {
59 struct nexthop nexthop;
60 unsigned int refcount;
61};
62
63/* Hash functions for pbr_nhrc_hash ---------------------------------------- */
64
65static void *pbr_nhrc_hash_alloc(void *p)
66{
67 struct nhrc *nhrc = XCALLOC(MTYPE_PBR_NHG, sizeof(struct nhrc));
68 nhrc->nexthop = *(struct nexthop *)p;
69 return nhrc;
70}
71
74df8d6d 72static bool pbr_nhrc_hash_equal(const void *arg1, const void *arg2)
b13e5ad6
DS
73{
74 const struct nexthop *nh1, *nh2;
75
76 nh1 = arg1;
77 nh2 = arg2;
78
79 return nexthop_same(nh1, nh2);
80}
81
82/* ------------------------------------------------------------------------- */
83
e5c83d9b
DS
84static void *pbr_nh_alloc(void *p)
85{
86 struct pbr_nexthop_cache *new;
87 struct pbr_nexthop_cache *pnhc = (struct pbr_nexthop_cache *)p;
b13e5ad6 88 struct nhrc *nhrc;
e5c83d9b
DS
89
90 new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new));
b13e5ad6
DS
91 nhrc = hash_get(pbr_nhrc_hash, pnhc->nexthop, pbr_nhrc_hash_alloc);
92 new->nexthop = &nhrc->nexthop;
93
94 /* Decremented again in pbr_nh_delete */
95 ++nhrc->refcount;
e5c83d9b
DS
96
97 DEBUGD(&pbr_dbg_nht, "%s: Sending nexthop to Zebra",
98 __PRETTY_FUNCTION__);
99
b13e5ad6 100 pbr_send_rnh(new->nexthop, true);
e5c83d9b
DS
101
102 new->valid = false;
103 return new;
104}
105
106static void pbr_nh_delete(struct pbr_nexthop_cache **pnhc)
107{
b13e5ad6
DS
108 struct nhrc *nhrc;
109
110 nhrc = hash_lookup(pbr_nhrc_hash, (*pnhc)->nexthop);
111
112 if (nhrc)
113 --nhrc->refcount;
114 if (!nhrc || nhrc->refcount == 0) {
115 DEBUGD(&pbr_dbg_nht, "%s: Removing nexthop from Zebra",
116 __PRETTY_FUNCTION__);
117 pbr_send_rnh((*pnhc)->nexthop, false);
118 }
119 if (nhrc && nhrc->refcount == 0) {
120 hash_release(pbr_nhrc_hash, nhrc);
121 XFREE(MTYPE_PBR_NHG, nhrc);
122 }
e5c83d9b
DS
123
124 XFREE(MTYPE_PBR_NHG, *pnhc);
125}
126
b13e5ad6
DS
127static void pbr_nh_delete_iterate(struct hash_backet *b, void *p)
128{
129 pbr_nh_delete((struct pbr_nexthop_cache **)&b->data);
130}
131
e5c83d9b
DS
132static uint32_t pbr_nh_hash_key(void *arg)
133{
134 uint32_t key;
135 struct pbr_nexthop_cache *pbrnc = (struct pbr_nexthop_cache *)arg;
136
b13e5ad6 137 key = nexthop_hash(pbrnc->nexthop);
e5c83d9b
DS
138
139 return key;
140}
141
74df8d6d 142static bool pbr_nh_hash_equal(const void *arg1, const void *arg2)
e5c83d9b
DS
143{
144 const struct pbr_nexthop_cache *pbrnc1 =
145 (const struct pbr_nexthop_cache *)arg1;
146 const struct pbr_nexthop_cache *pbrnc2 =
147 (const struct pbr_nexthop_cache *)arg2;
148
b13e5ad6 149 if (pbrnc1->nexthop->vrf_id != pbrnc2->nexthop->vrf_id)
74df8d6d 150 return false;
e5c83d9b 151
b13e5ad6 152 if (pbrnc1->nexthop->ifindex != pbrnc2->nexthop->ifindex)
74df8d6d 153 return false;
e5c83d9b 154
b13e5ad6 155 if (pbrnc1->nexthop->type != pbrnc2->nexthop->type)
74df8d6d 156 return false;
e5c83d9b 157
b13e5ad6 158 switch (pbrnc1->nexthop->type) {
e5c83d9b 159 case NEXTHOP_TYPE_IFINDEX:
74df8d6d 160 return true;
e5c83d9b
DS
161 case NEXTHOP_TYPE_IPV4_IFINDEX:
162 case NEXTHOP_TYPE_IPV4:
b13e5ad6
DS
163 return pbrnc1->nexthop->gate.ipv4.s_addr
164 == pbrnc2->nexthop->gate.ipv4.s_addr;
e5c83d9b
DS
165 case NEXTHOP_TYPE_IPV6_IFINDEX:
166 case NEXTHOP_TYPE_IPV6:
74df8d6d
DS
167 return !!memcmp(&pbrnc1->nexthop->gate.ipv6,
168 &pbrnc2->nexthop->gate.ipv6, 16);
e5c83d9b 169 case NEXTHOP_TYPE_BLACKHOLE:
b13e5ad6 170 return pbrnc1->nexthop->bh_type == pbrnc2->nexthop->bh_type;
e5c83d9b
DS
171 }
172
173 /*
174 * We should not get here
175 */
74df8d6d 176 return false;
e5c83d9b
DS
177}
178
b13e5ad6
DS
179static void pbr_nhgc_delete(struct pbr_nexthop_group_cache *p)
180{
181 hash_iterate(p->nhh, pbr_nh_delete_iterate, NULL);
182 hash_free(p->nhh);
183 XFREE(MTYPE_PBR_NHG, p);
184}
185
186static void *pbr_nhgc_alloc(void *p)
187{
188 struct pbr_nexthop_group_cache *new;
189 struct pbr_nexthop_group_cache *pnhgc =
190 (struct pbr_nexthop_group_cache *)p;
191
192 new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new));
193
194 strcpy(new->name, pnhgc->name);
a4044dc1 195 new->table_id = pbr_nht_get_next_tableid(false);
b13e5ad6
DS
196
197 DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u",
198 __PRETTY_FUNCTION__, new->name, new->table_id);
199
200 new->nhh = hash_create_size(8, pbr_nh_hash_key, pbr_nh_hash_equal,
201 "PBR NH Cache Hash");
202 return new;
203}
204
205
e5c83d9b
DS
206void pbr_nhgroup_add_cb(const char *name)
207{
b13e5ad6
DS
208 struct pbr_nexthop_group_cache *pnhgc;
209 struct nexthop_group_cmd *nhgc;
e5c83d9b 210
b13e5ad6 211 nhgc = nhgc_find(name);
68a63f60
QY
212
213 if (!nhgc) {
214 DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s\n",
215 __PRETTY_FUNCTION__, name);
216 return;
217 }
218
b13e5ad6 219 pnhgc = pbr_nht_add_group(name);
e5c83d9b 220
a4044dc1
QY
221 if (!pnhgc)
222 return;
223
b13e5ad6 224 DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __PRETTY_FUNCTION__,
e5c83d9b 225 name);
b13e5ad6
DS
226
227 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
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'",
243 __PRETTY_FUNCTION__, nhgc->name);
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",
262 __PRETTY_FUNCTION__, debugstr, nhgc->name);
263 }
264
265 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
266 pbr_map_check_nh_group_change(nhgc->name);
e5c83d9b
DS
267}
268
b13e5ad6 269void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc,
e5c83d9b
DS
270 const struct nexthop *nhop)
271{
b13e5ad6 272 char debugstr[256];
3e300703 273 struct pbr_nexthop_group_cache pnhgc_find = {};
b13e5ad6 274 struct pbr_nexthop_group_cache *pnhgc;
3e300703 275 struct pbr_nexthop_cache pnhc_find = {};
b13e5ad6 276 struct pbr_nexthop_cache *pnhc;
aafac994 277 enum nexthop_types_t nh_type = nhop->type;
b13e5ad6
DS
278
279 /* find pnhgc by name */
280 strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
a4044dc1 281 pnhgc = hash_lookup(pbr_nhg_hash, &pnhgc_find);
b13e5ad6
DS
282
283 /* delete pnhc from pnhgc->nhh */
284 pnhc_find.nexthop = (struct nexthop *)nhop;
285 pnhc = hash_release(pnhgc->nhh, &pnhc_find);
286
287 /* delete pnhc */
288 pbr_nh_delete(&pnhc);
e5c83d9b 289
b13e5ad6
DS
290 if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) {
291 nexthop2str(nhop, debugstr, sizeof(debugstr));
292 DEBUGD(&pbr_dbg_nht, "%s: Removed %s from nexthop-group %s",
293 __PRETTY_FUNCTION__, debugstr, nhgc->name);
294 }
e5c83d9b 295
ff9799c3
DS
296 if (pnhgc->nhh->count)
297 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
298 else
aafac994 299 pbr_nht_uninstall_nexthop_group(pnhgc, nhgc->nhg, nh_type);
ff9799c3 300
b13e5ad6 301 pbr_map_check_nh_group_change(nhgc->name);
e5c83d9b
DS
302}
303
304void pbr_nhgroup_delete_cb(const char *name)
305{
b13e5ad6 306 DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s",
e5c83d9b 307 __PRETTY_FUNCTION__, name);
b13e5ad6 308
ff9799c3
DS
309 /* delete group from all pbrms's */
310 pbr_nht_delete_group(name);
311
b13e5ad6 312 pbr_map_check_nh_group_change(name);
e5c83d9b
DS
313}
314
315#if 0
316static struct pbr_nexthop_cache *pbr_nht_lookup_nexthop(struct nexthop *nexthop)
317{
318 return NULL;
319}
320#endif
321
322static void pbr_nht_find_nhg_from_table_install(struct hash_backet *b,
323 void *data)
324{
325 struct pbr_nexthop_group_cache *pnhgc =
326 (struct pbr_nexthop_group_cache *)b->data;
327 uint32_t *table_id = (uint32_t *)data;
328
329 if (pnhgc->table_id == *table_id) {
330 DEBUGD(&pbr_dbg_nht, "%s: Table ID (%u) matches %s",
331 __PRETTY_FUNCTION__, *table_id, pnhgc->name);
2fb7892e
DS
332
333 /*
334 * If the table has been re-handled by zebra
335 * and we are already installed no need to do
336 * anything here.
337 */
338 if (!pnhgc->installed) {
339 pnhgc->installed = true;
340 pbr_map_schedule_policy_from_nhg(pnhgc->name);
341 }
e5c83d9b
DS
342 }
343}
344
345void pbr_nht_route_installed_for_table(uint32_t table_id)
346{
347 hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_install,
348 &table_id);
349}
350
351static void pbr_nht_find_nhg_from_table_remove(struct hash_backet *b,
352 void *data)
353{
354 ;
355}
356
357void pbr_nht_route_removed_for_table(uint32_t table_id)
358{
359 hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_remove,
360 &table_id);
361}
362
363/*
364 * Loop through all nexthops in a nexthop group to check that they are all the
365 * same. If they are not all the same, log this peculiarity.
366 *
367 * nhg
368 * The nexthop group to check
369 *
370 * Returns:
371 * - AFI of last nexthop in the group
372 * - AFI_MAX on error
373 */
ff9799c3 374static afi_t pbr_nht_which_afi(struct nexthop_group nhg,
aafac994 375 enum nexthop_types_t nh_type)
e5c83d9b
DS
376{
377 struct nexthop *nexthop;
378 afi_t install_afi = AFI_MAX;
379 bool v6, v4, bh;
d3765386 380
268c24ee
RW
381 if (nh_type) {
382 switch (nh_type) {
383 case NEXTHOP_TYPE_IPV4:
384 case NEXTHOP_TYPE_IPV4_IFINDEX:
385 return AFI_IP;
386 case NEXTHOP_TYPE_IPV6:
387 case NEXTHOP_TYPE_IPV6_IFINDEX:
388 return AFI_IP6;
389 case NEXTHOP_TYPE_IFINDEX:
390 case NEXTHOP_TYPE_BLACKHOLE:
391 return AFI_MAX;
392 }
393 }
394
e5c83d9b
DS
395 v6 = v4 = bh = false;
396
268c24ee
RW
397 for (ALL_NEXTHOPS(nhg, nexthop)) {
398 nh_type = nexthop->type;
399
400 switch (nh_type) {
401 case NEXTHOP_TYPE_IFINDEX:
402 break;
403 case NEXTHOP_TYPE_IPV4:
404 case NEXTHOP_TYPE_IPV4_IFINDEX:
405 v6 = true;
406 install_afi = AFI_IP;
407 break;
408 case NEXTHOP_TYPE_IPV6:
409 case NEXTHOP_TYPE_IPV6_IFINDEX:
410 v4 = true;
411 install_afi = AFI_IP6;
412 break;
413 case NEXTHOP_TYPE_BLACKHOLE:
414 bh = true;
e5c83d9b
DS
415 break;
416 }
417 }
418
268c24ee
RW
419 /* Interface and/or blackhole nexthops only. */
420 if (!v4 && !v6)
ff9799c3 421 install_afi = AFI_MAX;
ff9799c3 422
e5c83d9b
DS
423 if (!bh && v6 && v4)
424 DEBUGD(&pbr_dbg_nht,
425 "%s: Saw both V6 and V4 nexthops...using %s",
426 __PRETTY_FUNCTION__, afi2str(install_afi));
427 if (bh && (v6 || v4))
428 DEBUGD(&pbr_dbg_nht,
429 "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.",
430 __PRETTY_FUNCTION__, v4 ? "v4" : "",
431 (v4 && v6) ? " and " : "", v6 ? "v6" : "");
432
433 return install_afi;
434}
435
436static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
437 struct nexthop_group nhg)
438{
439 afi_t install_afi;
aafac994 440 enum nexthop_types_t nh_type = 0;
e5c83d9b 441
aafac994 442 install_afi = pbr_nht_which_afi(nhg, nh_type);
e5c83d9b 443
e5c83d9b
DS
444 route_add(pnhgc, nhg, install_afi);
445}
446
447static void
448pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
ff9799c3 449 struct nexthop_group nhg,
aafac994 450 enum nexthop_types_t nh_type)
e5c83d9b
DS
451{
452 afi_t install_afi;
453
aafac994 454 install_afi = pbr_nht_which_afi(nhg, nh_type);
e5c83d9b
DS
455
456 pnhgc->installed = false;
457 pnhgc->valid = false;
458 route_delete(pnhgc, install_afi);
459}
460
461void pbr_nht_change_group(const char *name)
462{
463 struct nexthop_group_cmd *nhgc;
464 struct pbr_nexthop_group_cache *pnhgc;
465 struct pbr_nexthop_group_cache find;
466 struct nexthop *nhop;
467
468 nhgc = nhgc_find(name);
469 if (!nhgc)
470 return;
471
472 memset(&find, 0, sizeof(find));
6612590d 473 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
474 pnhgc = hash_lookup(pbr_nhg_hash, &find);
475
476 if (!pnhgc) {
477 DEBUGD(&pbr_dbg_nht,
478 "%s: Could not find nexthop-group cache w/ name '%s'",
479 __PRETTY_FUNCTION__, name);
480 return;
481 }
482
483 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
484 struct pbr_nexthop_cache lookup;
485 struct pbr_nexthop_cache *pnhc;
486
b13e5ad6 487 lookup.nexthop = nhop;
e5c83d9b
DS
488 pnhc = hash_lookup(pnhgc->nhh, &lookup);
489 if (!pnhc) {
490 pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
491 pnhc->parent = pnhgc;
492 }
493 }
494 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
495}
496
497char *pbr_nht_nexthop_make_name(char *name, size_t l,
498 uint32_t seqno, char *buffer)
499{
500 snprintf(buffer, l, "%s%u", name, seqno);
501 return buffer;
502}
503
b13e5ad6 504void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms)
e5c83d9b
DS
505{
506 struct pbr_nexthop_group_cache *pnhgc;
507 struct pbr_nexthop_group_cache find;
508 struct pbr_nexthop_cache *pnhc;
e5c83d9b
DS
509 struct pbr_nexthop_cache lookup;
510
e5c83d9b 511 memset(&find, 0, sizeof(find));
06210d1f 512 pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
e5c83d9b 513 pbrms->seqno, find.name);
a4044dc1
QY
514
515 if (!pbr_nht_get_next_tableid(true)) {
516 zlog_warn(
517 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
518 __PRETTY_FUNCTION__, find.name);
519 return;
520 }
521
e5c83d9b
DS
522 if (!pbrms->internal_nhg_name)
523 pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name);
524
525 pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc);
526
b13e5ad6 527 lookup.nexthop = pbrms->nhg->nexthop;
e5c83d9b
DS
528 pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
529 pnhc->parent = pnhgc;
530 pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg);
531}
532
b13e5ad6 533void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
e5c83d9b
DS
534{
535 struct pbr_nexthop_group_cache *pnhgc;
536 struct pbr_nexthop_group_cache find;
537 struct pbr_nexthop_cache *pnhc;
538 struct pbr_nexthop_cache lup;
b13e5ad6
DS
539 struct pbr_map *pbrm = pbrms->parent;
540 struct listnode *node;
541 struct pbr_map_interface *pmi;
e5c83d9b 542 struct nexthop *nh;
aafac994 543 enum nexthop_types_t nh_type = 0;
e5c83d9b 544
b13e5ad6
DS
545 if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) {
546 for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi))
547 pbr_send_pbr_map(pbrms, pmi, false);
548 }
549
550 pbrm->valid = false;
551 pbrms->nhs_installed = false;
b13e5ad6 552 pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
e5c83d9b
DS
553
554 memset(&find, 0, sizeof(find));
6612590d 555 snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name);
e5c83d9b
DS
556 pnhgc = hash_lookup(pbr_nhg_hash, &find);
557
558 nh = pbrms->nhg->nexthop;
aafac994 559 nh_type = nh->type;
b13e5ad6 560 lup.nexthop = nh;
e5c83d9b
DS
561 pnhc = hash_lookup(pnhgc->nhh, &lup);
562 pnhc->parent = NULL;
563 hash_release(pnhgc->nhh, pnhc);
564 pbr_nh_delete(&pnhc);
aafac994 565 pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_type);
e5c83d9b
DS
566
567 hash_release(pbr_nhg_hash, pnhgc);
568
569 nexthop_del(pbrms->nhg, nh);
570 nexthop_free(nh);
571 nexthop_group_delete(&pbrms->nhg);
572 XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
573}
574
b13e5ad6 575struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name)
e5c83d9b
DS
576{
577 struct nexthop *nhop;
578 struct nexthop_group_cmd *nhgc;
579 struct pbr_nexthop_group_cache *pnhgc;
580 struct pbr_nexthop_group_cache lookup;
581
a4044dc1
QY
582 if (!pbr_nht_get_next_tableid(true)) {
583 zlog_warn(
584 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
585 __PRETTY_FUNCTION__, name);
586 return NULL;
587 }
588
e5c83d9b
DS
589 nhgc = nhgc_find(name);
590
591 if (!nhgc) {
a4044dc1
QY
592 DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s\n",
593 __PRETTY_FUNCTION__, name);
b13e5ad6 594 return NULL;
e5c83d9b
DS
595 }
596
6612590d 597 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
e5c83d9b
DS
598 pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc);
599 DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __PRETTY_FUNCTION__,
600 pnhgc);
601
602 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
7fe96307 603 struct pbr_nexthop_cache lookupc;
e5c83d9b
DS
604 struct pbr_nexthop_cache *pnhc;
605
7fe96307
A
606 lookupc.nexthop = nhop;
607 pnhc = hash_lookup(pnhgc->nhh, &lookupc);
e5c83d9b 608 if (!pnhc) {
7fe96307 609 pnhc = hash_get(pnhgc->nhh, &lookupc, pbr_nh_alloc);
e5c83d9b
DS
610 pnhc->parent = pnhgc;
611 }
612 }
b13e5ad6
DS
613
614 return pnhgc;
e5c83d9b
DS
615}
616
617void pbr_nht_delete_group(const char *name)
618{
619 struct pbr_map_sequence *pbrms;
620 struct listnode *snode;
621 struct pbr_map *pbrm;
b13e5ad6
DS
622 struct pbr_nexthop_group_cache pnhgc_find;
623 struct pbr_nexthop_group_cache *pnhgc;
e5c83d9b
DS
624
625 RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
626 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) {
627 if (pbrms->nhgrp_name
b13e5ad6 628 && strmatch(pbrms->nhgrp_name, name)) {
e5c83d9b 629 pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
b13e5ad6
DS
630 nexthop_group_delete(&pbrms->nhg);
631 pbrms->nhg = NULL;
632 pbrms->internal_nhg_name = NULL;
e5c83d9b
DS
633 pbrm->valid = false;
634 }
635 }
636 }
b13e5ad6
DS
637
638 strlcpy(pnhgc_find.name, name, sizeof(pnhgc_find.name));
639 pnhgc = hash_release(pbr_nhg_hash, &pnhgc_find);
640 pbr_nhgc_delete(pnhgc);
e5c83d9b
DS
641}
642
643bool pbr_nht_nexthop_valid(struct nexthop_group *nhg)
644{
645 DEBUGD(&pbr_dbg_nht, "%s: %p", __PRETTY_FUNCTION__, nhg);
646 return true;
647}
648
649bool pbr_nht_nexthop_group_valid(const char *name)
650{
651 struct pbr_nexthop_group_cache *pnhgc;
652 struct pbr_nexthop_group_cache lookup;
653
654 DEBUGD(&pbr_dbg_nht, "%s: %s", __PRETTY_FUNCTION__, name);
655
6612590d 656 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
e5c83d9b
DS
657 pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL);
658 if (!pnhgc)
659 return false;
660 DEBUGD(&pbr_dbg_nht, "%s: \t%d %d", __PRETTY_FUNCTION__, pnhgc->valid,
661 pnhgc->installed);
662 if (pnhgc->valid && pnhgc->installed)
663 return true;
664
665 return false;
666}
667
668struct pbr_nht_individual {
669 struct zapi_route *nhr;
670
671 uint32_t valid;
672};
673
674static void pbr_nht_individual_nexthop_update_lookup(struct hash_backet *b,
675 void *data)
676{
677 struct pbr_nexthop_cache *pnhc = b->data;
678 struct pbr_nht_individual *pnhi = data;
679 char buf[PREFIX_STRLEN];
680 bool old_valid;
681
682 old_valid = pnhc->valid;
683
684 switch (pnhi->nhr->prefix.family) {
685 case AF_INET:
b13e5ad6 686 if (pnhc->nexthop->gate.ipv4.s_addr
e5c83d9b
DS
687 == pnhi->nhr->prefix.u.prefix4.s_addr)
688 pnhc->valid = !!pnhi->nhr->nexthop_num;
689 break;
690 case AF_INET6:
b13e5ad6
DS
691 if (memcmp(&pnhc->nexthop->gate.ipv6,
692 &pnhi->nhr->prefix.u.prefix6, 16)
693 == 0)
e5c83d9b
DS
694 pnhc->valid = !!pnhi->nhr->nexthop_num;
695 break;
696 }
697
698 DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d",
699 prefix2str(&pnhi->nhr->prefix, buf, sizeof(buf)), old_valid,
700 pnhc->valid);
701
e5c83d9b
DS
702 if (pnhc->valid)
703 pnhi->valid += 1;
704}
705
706static void pbr_nht_nexthop_update_lookup(struct hash_backet *b, void *data)
707{
708 struct pbr_nexthop_group_cache *pnhgc = b->data;
709 struct pbr_nht_individual pnhi;
b13e5ad6
DS
710 bool old_valid;
711
712 old_valid = pnhgc->valid;
e5c83d9b
DS
713
714 pnhi.nhr = (struct zapi_route *)data;
715 pnhi.valid = 0;
716 hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_update_lookup,
717 &pnhi);
718
719 /*
720 * If any of the specified nexthops are valid we are valid
721 */
722 pnhgc->valid = !!pnhi.valid;
b13e5ad6
DS
723
724 if (old_valid != pnhgc->valid)
725 pbr_map_check_nh_group_change(pnhgc->name);
e5c83d9b
DS
726}
727
728void pbr_nht_nexthop_update(struct zapi_route *nhr)
729{
730 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr);
731}
732
733static uint32_t pbr_nhg_hash_key(void *arg)
734{
735 struct pbr_nexthop_group_cache *nhgc =
736 (struct pbr_nexthop_group_cache *)arg;
737
738 return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96);
739}
740
74df8d6d 741static bool pbr_nhg_hash_equal(const void *arg1, const void *arg2)
e5c83d9b
DS
742{
743 const struct pbr_nexthop_group_cache *nhgc1 =
744 (const struct pbr_nexthop_group_cache *)arg1;
745 const struct pbr_nexthop_group_cache *nhgc2 =
746 (const struct pbr_nexthop_group_cache *)arg2;
747
748 return !strcmp(nhgc1->name, nhgc2->name);
749}
750
a4044dc1 751uint32_t pbr_nht_get_next_tableid(bool peek)
e5c83d9b
DS
752{
753 uint32_t i;
754 bool found = false;
755
756 for (i = pbr_nhg_low_table; i <= pbr_nhg_high_table; i++) {
757 if (nhg_tableid[i] == false) {
758 found = true;
759 break;
760 }
761 }
762
763 if (found) {
a4044dc1 764 nhg_tableid[i] = !peek;
e5c83d9b
DS
765 return i;
766 } else
767 return 0;
768}
769
770void pbr_nht_set_tableid_range(uint32_t low, uint32_t high)
771{
772 pbr_nhg_low_table = low;
773 pbr_nhg_high_table = high;
774}
775
776void pbr_nht_write_table_range(struct vty *vty)
777{
778 if (pbr_nhg_low_table != PBR_NHT_DEFAULT_LOW_TABLEID
779 || pbr_nhg_high_table != PBR_NHT_DEFAULT_HIGH_TABLEID) {
780 vty_out(vty, "pbr table range %u %u\n", pbr_nhg_low_table,
781 pbr_nhg_high_table);
782 }
783}
784
785uint32_t pbr_nht_get_next_rule(uint32_t seqno)
786{
787 return seqno + pbr_nhg_low_rule - 1;
788}
789void pbr_nht_set_rule_range(uint32_t low, uint32_t high)
790{
791 pbr_nhg_low_rule = low;
792 pbr_nhg_high_rule = high;
793}
794
795void pbr_nht_write_rule_range(struct vty *vty)
796{
797 if (pbr_nhg_low_rule != PBR_NHT_DEFAULT_LOW_RULE
798 || pbr_nhg_high_rule != PBR_NHT_DEFAULT_HIGH_RULE) {
799 vty_out(vty, "pbr rule range %u %u\n", pbr_nhg_low_rule,
800 pbr_nhg_high_rule);
801 }
802}
803
804uint32_t pbr_nht_get_table(const char *name)
805{
806 struct pbr_nexthop_group_cache find;
807 struct pbr_nexthop_group_cache *pnhgc;
808
809 memset(&find, 0, sizeof(find));
6612590d 810 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
811 pnhgc = hash_lookup(pbr_nhg_hash, &find);
812
813 if (!pnhgc) {
814 DEBUGD(&pbr_dbg_nht,
815 "%s: Could not find nexthop-group cache w/ name '%s'",
816 __PRETTY_FUNCTION__, name);
817 return 5000;
818 }
819
820 return pnhgc->table_id;
821}
822
823bool pbr_nht_get_installed(const char *name)
824{
825 struct pbr_nexthop_group_cache find;
826 struct pbr_nexthop_group_cache *pnhgc;
827
828 memset(&find, 0, sizeof(find));
6612590d 829 snprintf(find.name, sizeof(find.name), "%s", name);
e5c83d9b
DS
830
831 pnhgc = hash_lookup(pbr_nhg_hash, &find);
832
d3765386 833 if (!pnhgc)
e5c83d9b 834 return false;
e5c83d9b
DS
835
836 return pnhgc->installed;
837}
838
839static void pbr_nht_show_nhg_nexthops(struct hash_backet *b, void *data)
840{
841 struct pbr_nexthop_cache *pnhc = b->data;
842 struct vty *vty = data;
843
57cdafc4 844 vty_out(vty, "\tValid: %d ", pnhc->valid);
b13e5ad6 845 nexthop_group_write_nexthop(vty, pnhc->nexthop);
e5c83d9b
DS
846}
847
848struct pbr_nht_show {
849 struct vty *vty;
850 const char *name;
851};
852
853static void pbr_nht_show_nhg(struct hash_backet *b, void *data)
854{
855 struct pbr_nexthop_group_cache *pnhgc = b->data;
856 struct pbr_nht_show *pns = data;
857 struct vty *vty;
858
859 if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
860 return;
861
862 vty = pns->vty;
863 vty_out(vty, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
864 pnhgc->name, pnhgc->table_id, pnhgc->valid, pnhgc->installed);
865
866 hash_iterate(pnhgc->nhh, pbr_nht_show_nhg_nexthops, vty);
867}
868
869void pbr_nht_show_nexthop_group(struct vty *vty, const char *name)
870{
871 struct pbr_nht_show pns;
872
873 pns.vty = vty;
874 pns.name = name;
875
876 hash_iterate(pbr_nhg_hash, pbr_nht_show_nhg, &pns);
877}
878
879void pbr_nht_init(void)
880{
881 pbr_nhg_hash = hash_create_size(
882 16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash");
b13e5ad6
DS
883 pbr_nhrc_hash =
884 hash_create_size(16, (unsigned int (*)(void *))nexthop_hash,
885 pbr_nhrc_hash_equal, "PBR NH Hash");
e5c83d9b
DS
886
887 pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID;
888 pbr_nhg_high_table = PBR_NHT_DEFAULT_HIGH_TABLEID;
889 pbr_nhg_low_rule = PBR_NHT_DEFAULT_LOW_RULE;
890 pbr_nhg_high_rule = PBR_NHT_DEFAULT_HIGH_RULE;
891 memset(&nhg_tableid, 0, 65535 * sizeof(uint8_t));
892}