]> git.proxmox.com Git - mirror_frr.git/blob - pbrd/pbr_nht.c
Merge pull request #6054 from sarav511/dr2ndr
[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 static 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\n",
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 = (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 __func__, 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 || (nhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
270 && IN6_IS_ADDR_LINKLOCAL(&nhop->gate.ipv6))) {
271 struct interface *ifp;
272
273 ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id);
274 if (ifp)
275 pbr_nht_nexthop_interface_update(ifp);
276 }
277 }
278
279 void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc,
280 const struct nexthop *nhop)
281 {
282 char debugstr[256];
283 struct pbr_nexthop_group_cache pnhgc_find = {};
284 struct pbr_nexthop_group_cache *pnhgc;
285 struct pbr_nexthop_cache pnhc_find = {};
286 struct pbr_nexthop_cache *pnhc;
287 enum nexthop_types_t nh_type = nhop->type;
288
289 /* find pnhgc by name */
290 strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
291 pnhgc = hash_lookup(pbr_nhg_hash, &pnhgc_find);
292
293 /* delete pnhc from pnhgc->nhh */
294 pnhc_find.nexthop = (struct nexthop *)nhop;
295 pnhc = hash_release(pnhgc->nhh, &pnhc_find);
296
297 /* delete pnhc */
298 pbr_nh_delete(&pnhc);
299
300 if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) {
301 nexthop2str(nhop, debugstr, sizeof(debugstr));
302 DEBUGD(&pbr_dbg_nht, "%s: Removed %s from nexthop-group %s",
303 __func__, debugstr, nhgc->name);
304 }
305
306 if (pnhgc->nhh->count)
307 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
308 else
309 pbr_nht_uninstall_nexthop_group(pnhgc, nhgc->nhg, nh_type);
310
311 pbr_map_check_nh_group_change(nhgc->name);
312 }
313
314 void pbr_nhgroup_delete_cb(const char *name)
315 {
316 DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s", __func__, name);
317
318 /* delete group from all pbrms's */
319 pbr_nht_delete_group(name);
320
321 pbr_map_check_nh_group_change(name);
322 }
323
324 #if 0
325 static struct pbr_nexthop_cache *pbr_nht_lookup_nexthop(struct nexthop *nexthop)
326 {
327 return NULL;
328 }
329 #endif
330
331 static void pbr_nht_find_nhg_from_table_install(struct hash_bucket *b,
332 void *data)
333 {
334 struct pbr_nexthop_group_cache *pnhgc =
335 (struct pbr_nexthop_group_cache *)b->data;
336 uint32_t *table_id = (uint32_t *)data;
337
338 if (pnhgc->table_id == *table_id) {
339 DEBUGD(&pbr_dbg_nht, "%s: Table ID (%u) matches %s", __func__,
340 *table_id, pnhgc->name);
341
342 /*
343 * If the table has been re-handled by zebra
344 * and we are already installed no need to do
345 * anything here.
346 */
347 if (!pnhgc->installed) {
348 pnhgc->installed = true;
349 pbr_map_schedule_policy_from_nhg(pnhgc->name);
350 }
351 }
352 }
353
354 void pbr_nht_route_installed_for_table(uint32_t table_id)
355 {
356 hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_install,
357 &table_id);
358 }
359
360 static void pbr_nht_find_nhg_from_table_remove(struct hash_bucket *b,
361 void *data)
362 {
363 ;
364 }
365
366 void pbr_nht_route_removed_for_table(uint32_t table_id)
367 {
368 hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_remove,
369 &table_id);
370 }
371
372 /*
373 * Loop through all nexthops in a nexthop group to check that they are all the
374 * same. If they are not all the same, log this peculiarity.
375 *
376 * nhg
377 * The nexthop group to check
378 *
379 * Returns:
380 * - AFI of last nexthop in the group
381 * - AFI_MAX on error
382 */
383 static afi_t pbr_nht_which_afi(struct nexthop_group nhg,
384 enum nexthop_types_t nh_type)
385 {
386 struct nexthop *nexthop;
387 afi_t install_afi = AFI_MAX;
388 bool v6, v4, bh;
389
390 if (nh_type) {
391 switch (nh_type) {
392 case NEXTHOP_TYPE_IPV4:
393 case NEXTHOP_TYPE_IPV4_IFINDEX:
394 return AFI_IP;
395 case NEXTHOP_TYPE_IPV6:
396 case NEXTHOP_TYPE_IPV6_IFINDEX:
397 return AFI_IP6;
398 case NEXTHOP_TYPE_IFINDEX:
399 case NEXTHOP_TYPE_BLACKHOLE:
400 return AFI_MAX;
401 }
402 }
403
404 v6 = v4 = bh = false;
405
406 for (ALL_NEXTHOPS(nhg, nexthop)) {
407 nh_type = nexthop->type;
408
409 switch (nh_type) {
410 case NEXTHOP_TYPE_IFINDEX:
411 break;
412 case NEXTHOP_TYPE_IPV4:
413 case NEXTHOP_TYPE_IPV4_IFINDEX:
414 v6 = true;
415 install_afi = AFI_IP;
416 break;
417 case NEXTHOP_TYPE_IPV6:
418 case NEXTHOP_TYPE_IPV6_IFINDEX:
419 v4 = true;
420 install_afi = AFI_IP6;
421 break;
422 case NEXTHOP_TYPE_BLACKHOLE:
423 bh = true;
424 break;
425 }
426 }
427
428 /* Interface and/or blackhole nexthops only. */
429 if (!v4 && !v6)
430 install_afi = AFI_MAX;
431
432 if (!bh && v6 && v4)
433 DEBUGD(&pbr_dbg_nht,
434 "%s: Saw both V6 and V4 nexthops...using %s", __func__,
435 afi2str(install_afi));
436 if (bh && (v6 || v4))
437 DEBUGD(&pbr_dbg_nht,
438 "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.",
439 __func__, v4 ? "v4" : "", (v4 && v6) ? " and " : "",
440 v6 ? "v6" : "");
441
442 return install_afi;
443 }
444
445 static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
446 struct nexthop_group nhg)
447 {
448 afi_t install_afi;
449 enum nexthop_types_t nh_type = 0;
450
451 install_afi = pbr_nht_which_afi(nhg, nh_type);
452
453 route_add(pnhgc, nhg, install_afi);
454 }
455
456 static void
457 pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
458 struct nexthop_group nhg,
459 enum nexthop_types_t nh_type)
460 {
461 afi_t install_afi;
462
463 install_afi = pbr_nht_which_afi(nhg, nh_type);
464
465 pnhgc->installed = false;
466 pnhgc->valid = false;
467 route_delete(pnhgc, install_afi);
468 }
469
470 void pbr_nht_change_group(const char *name)
471 {
472 struct nexthop_group_cmd *nhgc;
473 struct pbr_nexthop_group_cache *pnhgc;
474 struct pbr_nexthop_group_cache find;
475 struct nexthop *nhop;
476
477 nhgc = nhgc_find(name);
478 if (!nhgc)
479 return;
480
481 memset(&find, 0, sizeof(find));
482 snprintf(find.name, sizeof(find.name), "%s", name);
483 pnhgc = hash_lookup(pbr_nhg_hash, &find);
484
485 if (!pnhgc) {
486 DEBUGD(&pbr_dbg_nht,
487 "%s: Could not find nexthop-group cache w/ name '%s'",
488 __func__, name);
489 return;
490 }
491
492 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
493 struct pbr_nexthop_cache lookup;
494 struct pbr_nexthop_cache *pnhc;
495
496 lookup.nexthop = nhop;
497 pnhc = hash_lookup(pnhgc->nhh, &lookup);
498 if (!pnhc) {
499 pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
500 pnhc->parent = pnhgc;
501 }
502 }
503 pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
504 }
505
506 char *pbr_nht_nexthop_make_name(char *name, size_t l,
507 uint32_t seqno, char *buffer)
508 {
509 snprintf(buffer, l, "%s%u", name, seqno);
510 return buffer;
511 }
512
513 void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms,
514 const struct nexthop *nhop)
515 {
516 struct pbr_nexthop_group_cache *pnhgc;
517 struct pbr_nexthop_group_cache find;
518 struct pbr_nexthop_cache *pnhc;
519 struct pbr_nexthop_cache lookup;
520 struct nexthop *nh;
521 char buf[PBR_NHC_NAMELEN];
522
523 pbrms->nhg = nexthop_group_new();
524 pbrms->internal_nhg_name = XSTRDUP(
525 MTYPE_TMP,
526 pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
527 pbrms->seqno, buf));
528
529 nh = nexthop_new();
530 memcpy(nh, nhop, sizeof(*nh));
531
532 nexthop_group_add_sorted(pbrms->nhg, nh);
533
534 memset(&find, 0, sizeof(find));
535 pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
536 pbrms->seqno, find.name);
537
538 if (!pbr_nht_get_next_tableid(true)) {
539 zlog_warn(
540 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
541 __func__, find.name);
542 return;
543 }
544
545 if (!pbrms->internal_nhg_name)
546 pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name);
547
548 pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc);
549
550 lookup.nexthop = pbrms->nhg->nexthop;
551 pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
552 pnhc->parent = pnhgc;
553 pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg);
554 }
555
556 static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence *pbrms)
557 {
558 struct pbr_nexthop_group_cache *pnhgc;
559 struct pbr_nexthop_group_cache find;
560 struct pbr_nexthop_cache *pnhc;
561 struct pbr_nexthop_cache lup;
562 struct nexthop *nh;
563 enum nexthop_types_t nh_type = 0;
564
565 memset(&find, 0, sizeof(find));
566 snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name);
567 pnhgc = hash_lookup(pbr_nhg_hash, &find);
568
569 nh = pbrms->nhg->nexthop;
570 nh_type = nh->type;
571 lup.nexthop = nh;
572 pnhc = hash_lookup(pnhgc->nhh, &lup);
573 pnhc->parent = NULL;
574 hash_release(pnhgc->nhh, pnhc);
575 pbr_nh_delete(&pnhc);
576 pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_type);
577
578 hash_release(pbr_nhg_hash, pnhgc);
579 pbr_nhgc_delete(pnhgc);
580
581 nexthop_group_delete(&pbrms->nhg);
582 XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
583 }
584
585 void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
586 {
587 pbr_map_delete_nexthops(pbrms);
588
589 pbr_nht_release_individual_nexthop(pbrms);
590 }
591
592 struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name)
593 {
594 struct nexthop *nhop;
595 struct nexthop_group_cmd *nhgc;
596 struct pbr_nexthop_group_cache *pnhgc;
597 struct pbr_nexthop_group_cache lookup;
598
599 if (!pbr_nht_get_next_tableid(true)) {
600 zlog_warn(
601 "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'",
602 __func__, name);
603 return NULL;
604 }
605
606 nhgc = nhgc_find(name);
607
608 if (!nhgc) {
609 DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s\n",
610 __func__, name);
611 return NULL;
612 }
613
614 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
615 pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc);
616 DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __func__, pnhgc);
617
618 for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
619 struct pbr_nexthop_cache lookupc;
620 struct pbr_nexthop_cache *pnhc;
621
622 lookupc.nexthop = nhop;
623 pnhc = hash_lookup(pnhgc->nhh, &lookupc);
624 if (!pnhc) {
625 pnhc = hash_get(pnhgc->nhh, &lookupc, pbr_nh_alloc);
626 pnhc->parent = pnhgc;
627 }
628 }
629
630 return pnhgc;
631 }
632
633 void pbr_nht_delete_group(const char *name)
634 {
635 struct pbr_map_sequence *pbrms;
636 struct listnode *snode;
637 struct pbr_map *pbrm;
638 struct pbr_nexthop_group_cache pnhgc_find;
639 struct pbr_nexthop_group_cache *pnhgc;
640
641 RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
642 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) {
643 if (pbrms->nhgrp_name
644 && strmatch(pbrms->nhgrp_name, name)) {
645 pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
646 pbrms->nhg = NULL;
647 pbrms->internal_nhg_name = NULL;
648 pbrm->valid = false;
649 }
650 }
651 }
652
653 strlcpy(pnhgc_find.name, name, sizeof(pnhgc_find.name));
654 pnhgc = hash_release(pbr_nhg_hash, &pnhgc_find);
655 pbr_nhgc_delete(pnhgc);
656 }
657
658 bool pbr_nht_nexthop_valid(struct nexthop_group *nhg)
659 {
660 DEBUGD(&pbr_dbg_nht, "%s: %p", __func__, nhg);
661 return true;
662 }
663
664 bool pbr_nht_nexthop_group_valid(const char *name)
665 {
666 struct pbr_nexthop_group_cache *pnhgc;
667 struct pbr_nexthop_group_cache lookup;
668
669 DEBUGD(&pbr_dbg_nht, "%s: %s", __func__, name);
670
671 snprintf(lookup.name, sizeof(lookup.name), "%s", name);
672 pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL);
673 if (!pnhgc)
674 return false;
675 DEBUGD(&pbr_dbg_nht, "%s: \t%d %d", __func__, pnhgc->valid,
676 pnhgc->installed);
677 if (pnhgc->valid && pnhgc->installed)
678 return true;
679
680 return false;
681 }
682
683 struct pbr_nht_individual {
684 struct zapi_route *nhr;
685 struct interface *ifp;
686
687 uint32_t valid;
688 };
689
690 static bool
691 pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache *pnhc,
692 const struct pbr_nht_individual *pnhi)
693 {
694 bool is_valid = pnhc->valid;
695
696 if (!pnhi->nhr) /* It doesn't care about non-nexthop updates */
697 goto done;
698
699 switch (pnhi->nhr->prefix.family) {
700 case AF_INET:
701 if (pnhc->nexthop->gate.ipv4.s_addr
702 != pnhi->nhr->prefix.u.prefix4.s_addr)
703 goto done; /* Unrelated change */
704 break;
705 case AF_INET6:
706 if (memcmp(&pnhc->nexthop->gate.ipv6,
707 &pnhi->nhr->prefix.u.prefix6, 16)
708 != 0)
709 goto done; /* Unrelated change */
710 break;
711 }
712
713 if (!pnhi->nhr->nexthop_num) {
714 is_valid = false;
715 goto done;
716 }
717
718 if (pnhc->nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
719 || pnhc->nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
720
721 /* GATEWAY_IFINDEX type shouldn't resolve to group */
722 if (pnhi->nhr->nexthop_num > 1) {
723 is_valid = false;
724 goto done;
725 }
726
727 /* If whatever we resolved to wasn't on the interface we
728 * specified. (i.e. not a connected route), its invalid.
729 */
730 if (pnhi->nhr->nexthops[0].ifindex != pnhc->nexthop->ifindex) {
731 is_valid = false;
732 goto done;
733 }
734 }
735
736 is_valid = true;
737
738 done:
739 pnhc->valid = is_valid;
740
741 return pnhc->valid;
742 }
743
744 static bool pbr_nht_individual_nexthop_interface_update(
745 struct pbr_nexthop_cache *pnhc, const struct pbr_nht_individual *pnhi)
746 {
747 bool is_valid = pnhc->valid;
748
749 if (!pnhi->ifp) /* It doesn't care about non-interface updates */
750 goto done;
751
752 if (pnhc->nexthop->ifindex
753 != pnhi->ifp->ifindex) /* Un-related interface */
754 goto done;
755
756 is_valid = !!if_is_up(pnhi->ifp);
757
758 done:
759 pnhc->valid = is_valid;
760
761 return pnhc->valid;
762 }
763
764 /* Given this update either from interface or nexthop tracking, re-validate this
765 * nexthop.
766 *
767 * If the update is un-related, the subroutines shoud just return their cached
768 * valid state.
769 */
770 static void
771 pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache *pnhc,
772 const struct pbr_nht_individual *pnhi)
773 {
774 assert(pnhi->nhr || pnhi->ifp); /* Either nexthop or interface update */
775
776 switch (pnhc->nexthop->type) {
777 case NEXTHOP_TYPE_IFINDEX:
778 pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
779 break;
780 case NEXTHOP_TYPE_IPV6_IFINDEX:
781 if (IN6_IS_ADDR_LINKLOCAL(&pnhc->nexthop->gate.ipv6)) {
782 pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
783 break;
784 }
785 /* Intentional fall thru */
786 case NEXTHOP_TYPE_IPV4_IFINDEX:
787 case NEXTHOP_TYPE_IPV4:
788 case NEXTHOP_TYPE_IPV6:
789 pbr_nht_individual_nexthop_gw_update(pnhc, pnhi);
790 break;
791 case NEXTHOP_TYPE_BLACKHOLE:
792 pnhc->valid = true;
793 break;
794 }
795 }
796
797 static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket *b,
798 void *data)
799 {
800 struct pbr_nexthop_cache *pnhc = b->data;
801 struct pbr_nht_individual *pnhi = data;
802 char buf[PREFIX_STRLEN];
803 bool old_valid;
804
805 old_valid = pnhc->valid;
806
807 pbr_nht_individual_nexthop_update(pnhc, pnhi);
808
809 DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d",
810 prefix2str(&pnhi->nhr->prefix, buf, sizeof(buf)), old_valid,
811 pnhc->valid);
812
813 if (pnhc->valid)
814 pnhi->valid += 1;
815 }
816
817 static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket *b,
818 void *data)
819 {
820 struct pbr_nexthop_cache *pnhc = b->data;
821 struct nexthop_group *nhg = data;
822 struct nexthop *nh = NULL;
823
824 copy_nexthops(&nh, pnhc->nexthop, NULL);
825
826 _nexthop_add(&nhg->nexthop, nh);
827 }
828
829 static void
830 pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group *nhg,
831 struct pbr_nexthop_group_cache *pnhgc)
832 {
833 hash_iterate(pnhgc->nhh, pbr_nexthop_group_cache_iterate_to_group, nhg);
834 }
835
836 static void pbr_nht_nexthop_update_lookup(struct hash_bucket *b, void *data)
837 {
838 struct pbr_nexthop_group_cache *pnhgc = b->data;
839 struct pbr_nht_individual pnhi = {};
840 struct nexthop_group nhg = {};
841 bool old_valid;
842
843 old_valid = pnhgc->valid;
844
845 pnhi.nhr = (struct zapi_route *)data;
846 pnhi.valid = 0;
847 hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_update_lookup,
848 &pnhi);
849
850 /*
851 * If any of the specified nexthops are valid we are valid
852 */
853 pnhgc->valid = !!pnhi.valid;
854
855 if (pnhgc->valid) {
856 pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc);
857 pbr_nht_install_nexthop_group(pnhgc, nhg);
858 /* Don't need copied nexthops anymore */
859 nexthops_free(nhg.nexthop);
860 }
861
862 if (old_valid != pnhgc->valid)
863 pbr_map_check_nh_group_change(pnhgc->name);
864 }
865
866 void pbr_nht_nexthop_update(struct zapi_route *nhr)
867 {
868 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr);
869 }
870
871 static void
872 pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket *b,
873 void *data)
874 {
875 struct pbr_nexthop_cache *pnhc = b->data;
876 struct pbr_nht_individual *pnhi = data;
877 bool old_valid;
878
879 old_valid = pnhc->valid;
880
881 pbr_nht_individual_nexthop_update(pnhc, pnhi);
882
883 DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d", pnhi->ifp->name,
884 old_valid, pnhc->valid);
885
886 if (pnhc->valid)
887 pnhi->valid += 1;
888 }
889
890 static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket *b,
891 void *data)
892 {
893 struct pbr_nexthop_group_cache *pnhgc = b->data;
894 struct pbr_nht_individual pnhi = {};
895 bool old_valid;
896
897 old_valid = pnhgc->valid;
898
899 pnhi.ifp = data;
900 pnhi.valid = 0;
901 hash_iterate(pnhgc->nhh,
902 pbr_nht_individual_nexthop_interface_update_lookup, &pnhi);
903
904 /*
905 * If any of the specified nexthops are valid we are valid
906 */
907 pnhgc->valid = !!pnhi.valid;
908
909 if (old_valid != pnhgc->valid)
910 pbr_map_check_nh_group_change(pnhgc->name);
911 }
912
913 void pbr_nht_nexthop_interface_update(struct interface *ifp)
914 {
915 hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_update_lookup,
916 ifp);
917 }
918
919 static uint32_t pbr_nhg_hash_key(const void *arg)
920 {
921 const struct pbr_nexthop_group_cache *nhgc = arg;
922
923 return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96);
924 }
925
926 static bool pbr_nhg_hash_equal(const void *arg1, const void *arg2)
927 {
928 const struct pbr_nexthop_group_cache *nhgc1 =
929 (const struct pbr_nexthop_group_cache *)arg1;
930 const struct pbr_nexthop_group_cache *nhgc2 =
931 (const struct pbr_nexthop_group_cache *)arg2;
932
933 return !strcmp(nhgc1->name, nhgc2->name);
934 }
935
936 uint32_t pbr_nht_get_next_tableid(bool peek)
937 {
938 uint32_t i;
939 bool found = false;
940
941 for (i = pbr_nhg_low_table; i <= pbr_nhg_high_table; i++) {
942 if (!nhg_tableid[i]) {
943 found = true;
944 break;
945 }
946 }
947
948 if (found) {
949 nhg_tableid[i] = !peek;
950 return i;
951 } else
952 return 0;
953 }
954
955 void pbr_nht_set_tableid_range(uint32_t low, uint32_t high)
956 {
957 pbr_nhg_low_table = low;
958 pbr_nhg_high_table = high;
959 }
960
961 void pbr_nht_write_table_range(struct vty *vty)
962 {
963 if (pbr_nhg_low_table != PBR_NHT_DEFAULT_LOW_TABLEID
964 || pbr_nhg_high_table != PBR_NHT_DEFAULT_HIGH_TABLEID) {
965 vty_out(vty, "pbr table range %u %u\n", pbr_nhg_low_table,
966 pbr_nhg_high_table);
967 }
968 }
969
970 uint32_t pbr_nht_get_next_rule(uint32_t seqno)
971 {
972 return seqno + pbr_nhg_low_rule - 1;
973 }
974 void pbr_nht_set_rule_range(uint32_t low, uint32_t high)
975 {
976 pbr_nhg_low_rule = low;
977 pbr_nhg_high_rule = high;
978 }
979
980 void pbr_nht_write_rule_range(struct vty *vty)
981 {
982 if (pbr_nhg_low_rule != PBR_NHT_DEFAULT_LOW_RULE
983 || pbr_nhg_high_rule != PBR_NHT_DEFAULT_HIGH_RULE) {
984 vty_out(vty, "pbr rule range %u %u\n", pbr_nhg_low_rule,
985 pbr_nhg_high_rule);
986 }
987 }
988
989 uint32_t pbr_nht_get_table(const char *name)
990 {
991 struct pbr_nexthop_group_cache find;
992 struct pbr_nexthop_group_cache *pnhgc;
993
994 memset(&find, 0, sizeof(find));
995 snprintf(find.name, sizeof(find.name), "%s", name);
996 pnhgc = hash_lookup(pbr_nhg_hash, &find);
997
998 if (!pnhgc) {
999 DEBUGD(&pbr_dbg_nht,
1000 "%s: Could not find nexthop-group cache w/ name '%s'",
1001 __func__, name);
1002 return 5000;
1003 }
1004
1005 return pnhgc->table_id;
1006 }
1007
1008 bool pbr_nht_get_installed(const char *name)
1009 {
1010 struct pbr_nexthop_group_cache find;
1011 struct pbr_nexthop_group_cache *pnhgc;
1012
1013 memset(&find, 0, sizeof(find));
1014 snprintf(find.name, sizeof(find.name), "%s", name);
1015
1016 pnhgc = hash_lookup(pbr_nhg_hash, &find);
1017
1018 if (!pnhgc)
1019 return false;
1020
1021 return pnhgc->installed;
1022 }
1023
1024 static void pbr_nht_show_nhg_nexthops(struct hash_bucket *b, void *data)
1025 {
1026 struct pbr_nexthop_cache *pnhc = b->data;
1027 struct vty *vty = data;
1028
1029 vty_out(vty, "\tValid: %d ", pnhc->valid);
1030 nexthop_group_write_nexthop(vty, pnhc->nexthop);
1031 }
1032
1033 static void pbr_nht_json_nhg_nexthops(struct hash_bucket *b, void *data)
1034 {
1035 struct pbr_nexthop_cache *pnhc = b->data;
1036 json_object *all_hops = data;
1037 json_object *this_hop;
1038
1039 this_hop = json_object_new_object();
1040 nexthop_group_json_nexthop(this_hop, pnhc->nexthop);
1041 json_object_boolean_add(this_hop, "valid", pnhc->valid);
1042
1043 json_object_array_add(all_hops, this_hop);
1044 }
1045
1046 struct pbr_nht_show {
1047 struct vty *vty;
1048 json_object *json;
1049 const char *name;
1050 };
1051
1052 static void pbr_nht_show_nhg(struct hash_bucket *b, void *data)
1053 {
1054 struct pbr_nexthop_group_cache *pnhgc = b->data;
1055 struct pbr_nht_show *pns = data;
1056 struct vty *vty;
1057
1058 if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
1059 return;
1060
1061 vty = pns->vty;
1062 vty_out(vty, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
1063 pnhgc->name, pnhgc->table_id, pnhgc->valid, pnhgc->installed);
1064
1065 hash_iterate(pnhgc->nhh, pbr_nht_show_nhg_nexthops, vty);
1066 }
1067
1068 static void pbr_nht_json_nhg(struct hash_bucket *b, void *data)
1069 {
1070 struct pbr_nexthop_group_cache *pnhgc = b->data;
1071 struct pbr_nht_show *pns = data;
1072 json_object *j, *this_group, *group_hops;
1073
1074 if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
1075 return;
1076
1077 j = pns->json;
1078 this_group = json_object_new_object();
1079
1080 if (!j || !this_group)
1081 return;
1082
1083 json_object_int_add(this_group, "id", pnhgc->table_id);
1084 json_object_string_add(this_group, "name", pnhgc->name);
1085 json_object_boolean_add(this_group, "valid", pnhgc->valid);
1086 json_object_boolean_add(this_group, "installed", pnhgc->installed);
1087
1088 group_hops = json_object_new_array();
1089
1090 if (group_hops) {
1091 hash_iterate(pnhgc->nhh, pbr_nht_json_nhg_nexthops, group_hops);
1092 json_object_object_add(this_group, "nexthops", group_hops);
1093 }
1094
1095 json_object_array_add(j, this_group);
1096 }
1097
1098 void pbr_nht_show_nexthop_group(struct vty *vty, const char *name)
1099 {
1100 struct pbr_nht_show pns;
1101
1102 pns.vty = vty;
1103 pns.name = name;
1104
1105 hash_iterate(pbr_nhg_hash, pbr_nht_show_nhg, &pns);
1106 }
1107
1108 void pbr_nht_json_nexthop_group(json_object *j, const char *name)
1109 {
1110 struct pbr_nht_show pns;
1111
1112 pns.name = name;
1113 pns.json = j;
1114
1115 hash_iterate(pbr_nhg_hash, pbr_nht_json_nhg, &pns);
1116 }
1117
1118 void pbr_nht_init(void)
1119 {
1120 pbr_nhg_hash = hash_create_size(
1121 16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash");
1122 pbr_nhrc_hash =
1123 hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash,
1124 pbr_nhrc_hash_equal, "PBR NH Hash");
1125
1126 pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID;
1127 pbr_nhg_high_table = PBR_NHT_DEFAULT_HIGH_TABLEID;
1128 pbr_nhg_low_rule = PBR_NHT_DEFAULT_LOW_RULE;
1129 pbr_nhg_high_rule = PBR_NHT_DEFAULT_HIGH_RULE;
1130 memset(&nhg_tableid, 0, 65535 * sizeof(uint8_t));
1131 }