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