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