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