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