]> git.proxmox.com Git - mirror_frr.git/blame - zebra/zebra_nhg.c
zebra: improve route debugging and add support for backups
[mirror_frr.git] / zebra / zebra_nhg.c
CommitLineData
ad28e79a
SW
1/* Zebra Nexthop Group Code.
2 * Copyright (C) 2019 Cumulus Networks, Inc.
3 * Donald Sharp
4 * Stephen Worley
5 *
6 * This file is part of FRR.
7 *
8 * FRR is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * FRR is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with FRR; see the file COPYING. If not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 * 02111-1307, USA.
22 */
23#include <zebra.h>
24
25#include "lib/nexthop.h"
50d89650 26#include "lib/nexthop_group_private.h"
ad28e79a 27#include "lib/routemap.h"
b43434ad 28#include "lib/mpls.h"
69171da2 29#include "lib/jhash.h"
51d80884 30#include "lib/debug.h"
ad28e79a
SW
31
32#include "zebra/connected.h"
33#include "zebra/debug.h"
34#include "zebra/zebra_router.h"
5948f013 35#include "zebra/zebra_nhg_private.h"
ad28e79a
SW
36#include "zebra/zebra_rnh.h"
37#include "zebra/zebra_routemap.h"
51d80884
SW
38#include "zebra/zebra_memory.h"
39#include "zebra/zserv.h"
ad28e79a 40#include "zebra/rt.h"
d9f5b2f5 41#include "zebra_errors.h"
0c8215cb 42#include "zebra_dplane.h"
fe593b78 43#include "zebra/interface.h"
d9f5b2f5 44
51d80884 45DEFINE_MTYPE_STATIC(ZEBRA, NHG, "Nexthop Group Entry");
a15d4c00 46DEFINE_MTYPE_STATIC(ZEBRA, NHG_CONNECTED, "Nexthop Group Connected");
e22e8001 47DEFINE_MTYPE_STATIC(ZEBRA, NHG_CTX, "Nexthop Group Context");
0c8215cb 48
38e40db1
SW
49/* id counter to keep in sync with kernel */
50uint32_t id_counter;
51
7c99d51b
MS
52/* */
53static bool g_nexthops_enabled = true;
54
cb86eba3
MS
55static struct nhg_hash_entry *depends_find(const struct nexthop *nh,
56 afi_t afi);
37c6708b 57static void depends_add(struct nhg_connected_tree_head *head,
5657e7e9 58 struct nhg_hash_entry *depend);
38e40db1
SW
59static struct nhg_hash_entry *
60depends_find_add(struct nhg_connected_tree_head *head, struct nexthop *nh,
61 afi_t afi);
62static struct nhg_hash_entry *
63depends_find_id_add(struct nhg_connected_tree_head *head, uint32_t id);
37c6708b 64static void depends_decrement_free(struct nhg_connected_tree_head *head);
0c8215cb 65
1d48702e
MS
66static struct nhg_backup_info *
67nhg_backup_copy(const struct nhg_backup_info *orig);
68
e22e8001 69
5948f013 70static void nhg_connected_free(struct nhg_connected *dep)
0c8215cb 71{
a15d4c00 72 XFREE(MTYPE_NHG_CONNECTED, dep);
0c8215cb
SW
73}
74
5948f013 75static struct nhg_connected *nhg_connected_new(struct nhg_hash_entry *nhe)
0c8215cb 76{
a15d4c00 77 struct nhg_connected *new = NULL;
0c8215cb 78
a15d4c00 79 new = XCALLOC(MTYPE_NHG_CONNECTED, sizeof(struct nhg_connected));
0c8215cb
SW
80 new->nhe = nhe;
81
82 return new;
83}
84
37c6708b 85void nhg_connected_tree_free(struct nhg_connected_tree_head *head)
0c8215cb 86{
a15d4c00 87 struct nhg_connected *rb_node_dep = NULL;
0c8215cb 88
37c6708b 89 if (!nhg_connected_tree_is_empty(head)) {
fec211ad 90 frr_each_safe(nhg_connected_tree, head, rb_node_dep) {
37c6708b 91 nhg_connected_tree_del(head, rb_node_dep);
fe593b78
SW
92 nhg_connected_free(rb_node_dep);
93 }
0c8215cb 94 }
0c8215cb
SW
95}
96
37c6708b 97bool nhg_connected_tree_is_empty(const struct nhg_connected_tree_head *head)
0c8215cb 98{
fec211ad 99 return nhg_connected_tree_count(head) ? false : true;
0c8215cb
SW
100}
101
98cda54a 102struct nhg_connected *
37c6708b 103nhg_connected_tree_root(struct nhg_connected_tree_head *head)
98cda54a 104{
37c6708b 105 return nhg_connected_tree_first(head);
98cda54a
SW
106}
107
5bf15faa
SW
108struct nhg_hash_entry *
109nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head,
110 struct nhg_hash_entry *depend)
0c8215cb 111{
a15d4c00 112 struct nhg_connected lookup = {};
085304dc 113 struct nhg_connected *remove = NULL;
5bf15faa 114 struct nhg_hash_entry *removed_nhe;
0c8215cb
SW
115
116 lookup.nhe = depend;
3119f6a1 117
085304dc 118 /* Lookup to find the element, then remove it */
37c6708b 119 remove = nhg_connected_tree_find(head, &lookup);
085304dc 120 if (remove)
5bf15faa
SW
121 /* Re-returning here just in case this API changes..
122 * the _del list api's are a bit undefined at the moment.
123 *
124 * So hopefully returning here will make it fail if the api
125 * changes to something different than currently expected.
126 */
127 remove = nhg_connected_tree_del(head, remove);
128
129 /* If the entry was sucessfully removed, free the 'connected` struct */
130 if (remove) {
131 removed_nhe = remove->nhe;
085304dc 132 nhg_connected_free(remove);
5bf15faa
SW
133 return removed_nhe;
134 }
135
136 return NULL;
3119f6a1
SW
137}
138
5bf15faa
SW
139/* Assuming UNIQUE RB tree. If this changes, assumptions here about
140 * insertion need to change.
141 */
142struct nhg_hash_entry *
143nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head,
144 struct nhg_hash_entry *depend)
3119f6a1 145{
a15d4c00 146 struct nhg_connected *new = NULL;
0c8215cb 147
a15d4c00 148 new = nhg_connected_new(depend);
0c8215cb 149
5bf15faa
SW
150 /* On success, NULL will be returned from the
151 * RB code.
152 */
153 if (new && (nhg_connected_tree_add(head, new) == NULL))
154 return NULL;
155
156 /* If it wasn't successful, it must be a duplicate. We enforce the
157 * unique property for the `nhg_connected` tree.
158 */
159 nhg_connected_free(new);
160
161 return depend;
3119f6a1
SW
162}
163
37c6708b
SW
164static void
165nhg_connected_tree_decrement_ref(struct nhg_connected_tree_head *head)
32e29e79
SW
166{
167 struct nhg_connected *rb_node_dep = NULL;
32e29e79 168
fec211ad 169 frr_each_safe(nhg_connected_tree, head, rb_node_dep) {
32e29e79
SW
170 zebra_nhg_decrement_ref(rb_node_dep->nhe);
171 }
172}
173
37c6708b
SW
174static void
175nhg_connected_tree_increment_ref(struct nhg_connected_tree_head *head)
32e29e79
SW
176{
177 struct nhg_connected *rb_node_dep = NULL;
178
fec211ad 179 frr_each(nhg_connected_tree, head, rb_node_dep) {
32e29e79
SW
180 zebra_nhg_increment_ref(rb_node_dep->nhe);
181 }
182}
183
98cda54a
SW
184struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe)
185{
186 if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE)
187 && !zebra_nhg_depends_is_empty(nhe)) {
37c6708b 188 nhe = nhg_connected_tree_root(&nhe->nhg_depends)->nhe;
98cda54a
SW
189 return zebra_nhg_resolve(nhe);
190 }
191
192 return nhe;
193}
194
fe593b78 195unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe)
a15d4c00 196{
37c6708b 197 return nhg_connected_tree_count(&nhe->nhg_depends);
a15d4c00
SW
198}
199
fe593b78 200bool zebra_nhg_depends_is_empty(const struct nhg_hash_entry *nhe)
a15d4c00 201{
37c6708b 202 return nhg_connected_tree_is_empty(&nhe->nhg_depends);
a15d4c00
SW
203}
204
5948f013
SW
205static void zebra_nhg_depends_del(struct nhg_hash_entry *from,
206 struct nhg_hash_entry *depend)
3119f6a1 207{
37c6708b 208 nhg_connected_tree_del_nhe(&from->nhg_depends, depend);
3119f6a1
SW
209}
210
5948f013 211static void zebra_nhg_depends_init(struct nhg_hash_entry *nhe)
148a0103 212{
37c6708b 213 nhg_connected_tree_init(&nhe->nhg_depends);
148a0103
SW
214}
215
fe593b78
SW
216unsigned int zebra_nhg_dependents_count(const struct nhg_hash_entry *nhe)
217{
37c6708b 218 return nhg_connected_tree_count(&nhe->nhg_dependents);
fe593b78
SW
219}
220
5948f013 221
fe593b78
SW
222bool zebra_nhg_dependents_is_empty(const struct nhg_hash_entry *nhe)
223{
37c6708b 224 return nhg_connected_tree_is_empty(&nhe->nhg_dependents);
fe593b78
SW
225}
226
5948f013
SW
227static void zebra_nhg_dependents_del(struct nhg_hash_entry *from,
228 struct nhg_hash_entry *dependent)
fe593b78 229{
37c6708b 230 nhg_connected_tree_del_nhe(&from->nhg_dependents, dependent);
fe593b78
SW
231}
232
5948f013
SW
233static void zebra_nhg_dependents_add(struct nhg_hash_entry *to,
234 struct nhg_hash_entry *dependent)
fe593b78 235{
37c6708b 236 nhg_connected_tree_add_nhe(&to->nhg_dependents, dependent);
fe593b78
SW
237}
238
5948f013 239static void zebra_nhg_dependents_init(struct nhg_hash_entry *nhe)
fe593b78 240{
37c6708b 241 nhg_connected_tree_init(&nhe->nhg_dependents);
fe593b78
SW
242}
243
21615102
SW
244/* Release this nhe from anything depending on it */
245static void zebra_nhg_dependents_release(struct nhg_hash_entry *nhe)
246{
80286aa5 247 struct nhg_connected *rb_node_dep = NULL;
21615102 248
80286aa5
SW
249 frr_each_safe(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep) {
250 zebra_nhg_depends_del(rb_node_dep->nhe, nhe);
251 /* recheck validity of the dependent */
252 zebra_nhg_check_valid(rb_node_dep->nhe);
21615102
SW
253 }
254}
255
5948f013
SW
256/* Release this nhe from anything that it depends on */
257static void zebra_nhg_depends_release(struct nhg_hash_entry *nhe)
258{
259 if (!zebra_nhg_depends_is_empty(nhe)) {
260 struct nhg_connected *rb_node_dep = NULL;
261
262 frr_each_safe(nhg_connected_tree, &nhe->nhg_depends,
263 rb_node_dep) {
264 zebra_nhg_dependents_del(rb_node_dep->nhe, nhe);
265 }
266 }
267}
268
269
d9f5b2f5
SW
270struct nhg_hash_entry *zebra_nhg_lookup_id(uint32_t id)
271{
0c8215cb 272 struct nhg_hash_entry lookup = {};
d9f5b2f5
SW
273
274 lookup.id = id;
275 return hash_lookup(zrouter.nhgs_id, &lookup);
276}
277
5948f013 278static int zebra_nhg_insert_id(struct nhg_hash_entry *nhe)
d9f5b2f5
SW
279{
280 if (hash_lookup(zrouter.nhgs_id, nhe)) {
281 flog_err(
282 EC_ZEBRA_NHG_TABLE_INSERT_FAILED,
283 "Failed inserting NHG id=%u into the ID hash table, entry already exists",
284 nhe->id);
285 return -1;
286 }
287
288 hash_get(zrouter.nhgs_id, nhe, hash_alloc_intern);
289
290 return 0;
291}
ad28e79a 292
5948f013
SW
293static void zebra_nhg_set_if(struct nhg_hash_entry *nhe, struct interface *ifp)
294{
295 nhe->ifp = ifp;
296 if_nhg_dependents_add(ifp, nhe);
297}
298
38e40db1
SW
299static void
300zebra_nhg_connect_depends(struct nhg_hash_entry *nhe,
301 struct nhg_connected_tree_head nhg_depends)
4e49c8b8 302{
a15d4c00
SW
303 struct nhg_connected *rb_node_dep = NULL;
304
38e40db1
SW
305 /* This has been allocated higher above in the stack. Could probably
306 * re-allocate and free the old stuff but just using the same memory
307 * for now. Otherwise, their might be a time trade-off for repeated
308 * alloc/frees as startup.
309 */
310 nhe->nhg_depends = nhg_depends;
4e49c8b8 311
a15d4c00 312 /* Attach backpointer to anything that it depends on */
fe593b78 313 zebra_nhg_dependents_init(nhe);
a15d4c00 314 if (!zebra_nhg_depends_is_empty(nhe)) {
fec211ad 315 frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
a15d4c00
SW
316 zebra_nhg_dependents_add(rb_node_dep->nhe, nhe);
317 }
318 }
4e49c8b8 319
7b683a96 320 /* Add the ifp now if its not a group or recursive and has ifindex */
c415d895
MS
321 if (zebra_nhg_depends_is_empty(nhe) && nhe->nhg.nexthop
322 && nhe->nhg.nexthop->ifindex) {
7b683a96
SW
323 struct interface *ifp = NULL;
324
c415d895
MS
325 ifp = if_lookup_by_index(nhe->nhg.nexthop->ifindex,
326 nhe->nhg.nexthop->vrf_id);
7f1abf79
SW
327 if (ifp)
328 zebra_nhg_set_if(nhe, ifp);
329 else
330 flog_err(
331 EC_ZEBRA_IF_LOOKUP_FAILED,
332 "Zebra failed to lookup an interface with ifindex=%d in vrf=%u for NHE id=%u",
c415d895
MS
333 nhe->nhg.nexthop->ifindex,
334 nhe->nhg.nexthop->vrf_id, nhe->id);
7b683a96 335 }
38e40db1
SW
336}
337
0eb97b86 338struct nhg_hash_entry *zebra_nhg_alloc(void)
38e40db1
SW
339{
340 struct nhg_hash_entry *nhe;
341
342 nhe = XCALLOC(MTYPE_NHG, sizeof(struct nhg_hash_entry));
343
0eb97b86
MS
344 return nhe;
345}
346
1d48702e 347static struct nhg_hash_entry *zebra_nhg_copy(const struct nhg_hash_entry *orig,
0eb97b86
MS
348 uint32_t id)
349{
350 struct nhg_hash_entry *nhe;
351
352 nhe = zebra_nhg_alloc();
353
38e40db1
SW
354 nhe->id = id;
355
1d48702e 356 nexthop_group_copy(&(nhe->nhg), &(orig->nhg));
38e40db1 357
1d48702e
MS
358 nhe->vrf_id = orig->vrf_id;
359 nhe->afi = orig->afi;
360 nhe->type = orig->type ? orig->type : ZEBRA_ROUTE_NHG;
38e40db1
SW
361 nhe->refcnt = 0;
362 nhe->dplane_ref = zebra_router_get_next_sequence();
363
1d48702e
MS
364 /* Copy backup info also, if present */
365 if (orig->backup_info)
366 nhe->backup_info = nhg_backup_copy(orig->backup_info);
367
38e40db1
SW
368 return nhe;
369}
370
371/* Allocation via hash handler */
372static void *zebra_nhg_hash_alloc(void *arg)
373{
374 struct nhg_hash_entry *nhe = NULL;
375 struct nhg_hash_entry *copy = arg;
7b683a96 376
38e40db1 377 nhe = zebra_nhg_copy(copy, copy->id);
9ef49038
SW
378
379 /* Mark duplicate nexthops in a group at creation time. */
c415d895 380 nexthop_group_mark_duplicates(&(nhe->nhg));
9ef49038 381
38e40db1 382 zebra_nhg_connect_depends(nhe, copy->nhg_depends);
d9f5b2f5
SW
383 zebra_nhg_insert_id(nhe);
384
4e49c8b8
DS
385 return nhe;
386}
387
4e49c8b8
DS
388uint32_t zebra_nhg_hash_key(const void *arg)
389{
390 const struct nhg_hash_entry *nhe = arg;
1d48702e
MS
391 uint32_t val, key = 0x5a351234;
392
393 val = nexthop_group_hash(&(nhe->nhg));
394 if (nhe->backup_info) {
395 val = jhash_2words(val,
396 nexthop_group_hash(
397 &(nhe->backup_info->nhe->nhg)),
398 key);
399 }
d9f5b2f5 400
1d48702e 401 key = jhash_3words(nhe->vrf_id, nhe->afi, val, key);
d9f5b2f5 402
d9f5b2f5 403 return key;
4e49c8b8
DS
404}
405
a95b8020
SW
406uint32_t zebra_nhg_id_key(const void *arg)
407{
408 const struct nhg_hash_entry *nhe = arg;
409
410 return nhe->id;
411}
412
1d48702e
MS
413/* Helper with common nhg/nhe nexthop comparison logic */
414static bool nhg_compare_nexthops(const struct nexthop *nh1,
415 const struct nexthop *nh2)
416{
417 if (nh1 && !nh2)
418 return false;
419
420 if (!nh1 && nh2)
421 return false;
422
423 /*
424 * We have to check the active flag of each individual one,
425 * not just the overall active_num. This solves the special case
426 * issue of a route with a nexthop group with one nexthop
427 * resolving to itself and thus marking it inactive. If we
428 * have two different routes each wanting to mark a different
429 * nexthop inactive, they need to hash to two different groups.
430 *
431 * If we just hashed on num_active, they would hash the same
432 * which is incorrect.
433 *
434 * ex)
435 * 1.1.1.0/24
436 * -> 1.1.1.1 dummy1 (inactive)
437 * -> 1.1.2.1 dummy2
438 *
439 * 1.1.2.0/24
440 * -> 1.1.1.1 dummy1
441 * -> 1.1.2.1 dummy2 (inactive)
442 *
443 * Without checking each individual one, they would hash to
444 * the same group and both have 1.1.1.1 dummy1 marked inactive.
445 *
446 */
447 if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_ACTIVE)
448 != CHECK_FLAG(nh2->flags, NEXTHOP_FLAG_ACTIVE))
449 return false;
450
451 if (!nexthop_same(nh1, nh2))
452 return false;
453
454 return true;
455}
456
4e49c8b8
DS
457bool zebra_nhg_hash_equal(const void *arg1, const void *arg2)
458{
459 const struct nhg_hash_entry *nhe1 = arg1;
460 const struct nhg_hash_entry *nhe2 = arg2;
148813c2
SW
461 struct nexthop *nexthop1;
462 struct nexthop *nexthop2;
4e49c8b8 463
98cda54a
SW
464 /* No matter what if they equal IDs, assume equal */
465 if (nhe1->id && nhe2->id && (nhe1->id == nhe2->id))
466 return true;
467
4e49c8b8
DS
468 if (nhe1->vrf_id != nhe2->vrf_id)
469 return false;
470
77b76fc9
SW
471 if (nhe1->afi != nhe2->afi)
472 return false;
473
1d48702e 474 /* Nexthops should be in-order, so we simply compare them in-place */
c415d895 475 for (nexthop1 = nhe1->nhg.nexthop, nexthop2 = nhe2->nhg.nexthop;
148813c2
SW
476 nexthop1 || nexthop2;
477 nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) {
148813c2 478
1d48702e 479 if (!nhg_compare_nexthops(nexthop1, nexthop2))
148813c2 480 return false;
1d48702e 481 }
148813c2 482
1d48702e
MS
483 /* If there's no backup info, comparison is done. */
484 if ((nhe1->backup_info == NULL) && (nhe2->backup_info == NULL))
485 return true;
486
487 /* Compare backup info also - test the easy things first */
488 if (nhe1->backup_info && (nhe2->backup_info == NULL))
489 return false;
490 if (nhe2->backup_info && (nhe1->backup_info == NULL))
491 return false;
492
493 /* Compare number of backups before actually comparing any */
494 for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop,
495 nexthop2 = nhe2->backup_info->nhe->nhg.nexthop;
496 nexthop1 && nexthop2;
497 nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) {
498 ;
499 }
500
501 /* Did we find the end of one list before the other? */
502 if (nexthop1 || nexthop2)
503 return false;
504
505 /* Have to compare the backup nexthops */
506 for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop,
507 nexthop2 = nhe2->backup_info->nhe->nhg.nexthop;
508 nexthop1 || nexthop2;
509 nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) {
148813c2 510
1d48702e 511 if (!nhg_compare_nexthops(nexthop1, nexthop2))
148813c2
SW
512 return false;
513 }
e4ac313b 514
4e49c8b8
DS
515 return true;
516}
517
d9f5b2f5 518bool zebra_nhg_hash_id_equal(const void *arg1, const void *arg2)
4e49c8b8 519{
d9f5b2f5
SW
520 const struct nhg_hash_entry *nhe1 = arg1;
521 const struct nhg_hash_entry *nhe2 = arg2;
4e49c8b8 522
d9f5b2f5
SW
523 return nhe1->id == nhe2->id;
524}
4e49c8b8 525
1b366e63
SW
526static int zebra_nhg_process_grp(struct nexthop_group *nhg,
527 struct nhg_connected_tree_head *depends,
528 struct nh_grp *grp, uint8_t count)
e22e8001 529{
37c6708b 530 nhg_connected_tree_init(depends);
e22e8001
SW
531
532 for (int i = 0; i < count; i++) {
533 struct nhg_hash_entry *depend = NULL;
534 /* We do not care about nexthop_grp.weight at
535 * this time. But we should figure out
536 * how to adapt this to our code in
537 * the future.
538 */
38e40db1 539 depend = depends_find_id_add(depends, grp[i].id);
e22e8001 540
38e40db1 541 if (!depend) {
e22e8001
SW
542 flog_err(
543 EC_ZEBRA_NHG_SYNC,
544 "Received Nexthop Group from the kernel with a dependent Nexthop ID (%u) which we do not have in our table",
545 grp[i].id);
1b366e63 546 return -1;
e22e8001 547 }
38e40db1
SW
548
549 /*
550 * If this is a nexthop with its own group
551 * dependencies, add them as well. Not sure its
552 * even possible to have a group within a group
553 * in the kernel.
554 */
555
c415d895 556 copy_nexthops(&nhg->nexthop, depend->nhg.nexthop, NULL);
e22e8001 557 }
1b366e63
SW
558
559 return 0;
e22e8001
SW
560}
561
6384cbcb
SW
562static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends,
563 struct nexthop *nh, afi_t afi)
564{
565 struct nhg_hash_entry *depend = NULL;
566 struct nexthop_group resolved_ng = {};
567
1d049aba 568 resolved_ng.nexthop = nh;
6384cbcb
SW
569
570 depend = zebra_nhg_rib_find(0, &resolved_ng, afi);
a7e1b02d
SW
571
572 if (depend)
573 depends_add(nhg_depends, depend);
6384cbcb 574}
e22e8001 575
4505578b
SW
576static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id,
577 struct nexthop_group *nhg,
37c6708b 578 struct nhg_connected_tree_head *nhg_depends,
38e40db1 579 vrf_id_t vrf_id, afi_t afi, int type)
a95b8020 580{
0c8215cb 581 struct nhg_hash_entry lookup = {};
5bd81e4c 582
e22e8001 583 uint32_t old_id_counter = id_counter;
5bd81e4c 584
4505578b 585 bool created = false;
6384cbcb 586 bool recursive = false;
4505578b 587
1d48702e
MS
588 if (IS_ZEBRA_DEBUG_NHG_DETAIL)
589 zlog_debug("%s: id %u, nhg %p, vrf %d, type %d, depends %p",
590 __func__, id, nhg, vrf_id, type,
591 nhg_depends);
592
38e40db1
SW
593 /*
594 * If it has an id at this point, we must have gotten it from the kernel
595 */
596 lookup.id = id ? id : ++id_counter;
a95b8020 597
9a1588c4 598 lookup.type = type ? type : ZEBRA_ROUTE_NHG;
c415d895 599 lookup.nhg = *nhg;
e22e8001 600
88cafda7 601 lookup.vrf_id = vrf_id;
c415d895 602 if (lookup.nhg.nexthop->next) {
6384cbcb
SW
603 /* Groups can have all vrfs and AF's in them */
604 lookup.afi = AFI_UNSPEC;
6384cbcb 605 } else {
c415d895 606 switch (lookup.nhg.nexthop->type) {
4d21c7c0
SW
607 case (NEXTHOP_TYPE_IFINDEX):
608 case (NEXTHOP_TYPE_BLACKHOLE):
609 /*
610 * This switch case handles setting the afi different
611 * for ipv4/v6 routes. Ifindex/blackhole nexthop
612 * objects cannot be ambiguous, they must be Address
613 * Family specific. If we get here, we will either use
614 * the AF of the route, or the one we got passed from
615 * here from the kernel.
616 */
617 lookup.afi = afi;
618 break;
619 case (NEXTHOP_TYPE_IPV4_IFINDEX):
620 case (NEXTHOP_TYPE_IPV4):
621 lookup.afi = AFI_IP;
622 break;
623 case (NEXTHOP_TYPE_IPV6_IFINDEX):
624 case (NEXTHOP_TYPE_IPV6):
625 lookup.afi = AFI_IP6;
626 break;
627 }
6384cbcb 628 }
a95b8020 629
b599cd2a 630 if (id)
4505578b 631 (*nhe) = zebra_nhg_lookup_id(id);
b599cd2a 632 else
4505578b 633 (*nhe) = hash_lookup(zrouter.nhgs, &lookup);
d9f5b2f5 634
5bd81e4c 635 /* If it found an nhe in our tables, this new ID is unused */
4505578b 636 if (*nhe)
5bd81e4c
SW
637 id_counter = old_id_counter;
638
4505578b 639 if (!(*nhe)) {
6384cbcb
SW
640 /* Only hash/lookup the depends if the first lookup
641 * fails to find something. This should hopefully save a
642 * lot of cycles for larger ecmp sizes.
643 */
644 if (nhg_depends)
645 /* If you don't want to hash on each nexthop in the
646 * nexthop group struct you can pass the depends
647 * directly. Kernel-side we do this since it just looks
648 * them up via IDs.
649 */
650 lookup.nhg_depends = *nhg_depends;
651 else {
652 if (nhg->nexthop->next) {
5948f013 653 zebra_nhg_depends_init(&lookup);
6384cbcb
SW
654
655 /* If its a group, create a dependency tree */
656 struct nexthop *nh = NULL;
657
658 for (nh = nhg->nexthop; nh; nh = nh->next)
659 depends_find_add(&lookup.nhg_depends,
660 nh, afi);
661 } else if (CHECK_FLAG(nhg->nexthop->flags,
662 NEXTHOP_FLAG_RECURSIVE)) {
5948f013 663 zebra_nhg_depends_init(&lookup);
6384cbcb
SW
664 handle_recursive_depend(&lookup.nhg_depends,
665 nhg->nexthop->resolved,
666 afi);
667 recursive = true;
668 }
669 }
670
38e40db1 671 (*nhe) = hash_get(zrouter.nhgs, &lookup, zebra_nhg_hash_alloc);
4505578b 672 created = true;
d9f5b2f5 673
6384cbcb
SW
674 if (recursive)
675 SET_FLAG((*nhe)->flags, NEXTHOP_GROUP_RECURSIVE);
676 }
4505578b 677 return created;
a95b8020
SW
678}
679
e22e8001 680/* Find/create a single nexthop */
6384cbcb
SW
681static struct nhg_hash_entry *
682zebra_nhg_find_nexthop(uint32_t id, struct nexthop *nh, afi_t afi, int type)
3057df51 683{
6384cbcb 684 struct nhg_hash_entry *nhe = NULL;
e22e8001 685 struct nexthop_group nhg = {};
88cafda7 686 vrf_id_t vrf_id = !vrf_is_backend_netns() ? VRF_DEFAULT : nh->vrf_id;
e22e8001 687
0eb97b86 688 nexthop_group_add_sorted(&nhg, nh);
e22e8001 689
88cafda7 690 zebra_nhg_find(&nhe, id, &nhg, NULL, vrf_id, afi, type);
8a507796 691
6384cbcb 692 return nhe;
e22e8001
SW
693}
694
10200d40
SW
695static uint32_t nhg_ctx_get_id(const struct nhg_ctx *ctx)
696{
697 return ctx->id;
698}
699
1b366e63 700static void nhg_ctx_set_status(struct nhg_ctx *ctx, enum nhg_ctx_status status)
e22e8001
SW
701{
702 ctx->status = status;
703}
704
1b366e63 705static enum nhg_ctx_status nhg_ctx_get_status(const struct nhg_ctx *ctx)
e22e8001
SW
706{
707 return ctx->status;
708}
709
710static void nhg_ctx_set_op(struct nhg_ctx *ctx, enum nhg_ctx_op_e op)
711{
712 ctx->op = op;
713}
714
715static enum nhg_ctx_op_e nhg_ctx_get_op(const struct nhg_ctx *ctx)
716{
717 return ctx->op;
718}
719
10200d40
SW
720static vrf_id_t nhg_ctx_get_vrf_id(const struct nhg_ctx *ctx)
721{
722 return ctx->vrf_id;
723}
724
725static int nhg_ctx_get_type(const struct nhg_ctx *ctx)
726{
727 return ctx->type;
728}
729
730static int nhg_ctx_get_afi(const struct nhg_ctx *ctx)
731{
732 return ctx->afi;
733}
734
735static struct nexthop *nhg_ctx_get_nh(struct nhg_ctx *ctx)
736{
737 return &ctx->u.nh;
738}
739
740static uint8_t nhg_ctx_get_count(const struct nhg_ctx *ctx)
741{
742 return ctx->count;
743}
744
745static struct nh_grp *nhg_ctx_get_grp(struct nhg_ctx *ctx)
746{
747 return ctx->u.grp;
748}
749
99e7ab12 750static struct nhg_ctx *nhg_ctx_new(void)
7c6d5f25 751{
99e7ab12 752 struct nhg_ctx *new;
7c6d5f25
SW
753
754 new = XCALLOC(MTYPE_NHG_CTX, sizeof(struct nhg_ctx));
755
756 return new;
757}
758
759static void nhg_ctx_free(struct nhg_ctx **ctx)
760{
761 struct nexthop *nh;
762
763 if (ctx == NULL)
764 return;
765
766 assert((*ctx) != NULL);
767
768 if (nhg_ctx_get_count(*ctx))
769 goto done;
770
771 nh = nhg_ctx_get_nh(*ctx);
772
773 nexthop_del_labels(nh);
774
775done:
776 XFREE(MTYPE_NHG_CTX, *ctx);
7c6d5f25
SW
777}
778
81505946
SW
779static struct nhg_ctx *nhg_ctx_init(uint32_t id, struct nexthop *nh,
780 struct nh_grp *grp, vrf_id_t vrf_id,
781 afi_t afi, int type, uint8_t count)
782{
783 struct nhg_ctx *ctx = NULL;
784
785 ctx = nhg_ctx_new();
786
787 ctx->id = id;
788 ctx->vrf_id = vrf_id;
789 ctx->afi = afi;
790 ctx->type = type;
791 ctx->count = count;
792
793 if (count)
794 /* Copy over the array */
795 memcpy(&ctx->u.grp, grp, count * sizeof(struct nh_grp));
796 else if (nh)
797 ctx->u.nh = *nh;
798
799 return ctx;
800}
801
c1da832a 802static bool zebra_nhg_contains_unhashable(struct nhg_hash_entry *nhe)
38e40db1
SW
803{
804 struct nhg_connected *rb_node_dep = NULL;
805
fec211ad 806 frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
38e40db1 807 if (CHECK_FLAG(rb_node_dep->nhe->flags,
c1da832a 808 NEXTHOP_GROUP_UNHASHABLE))
38e40db1
SW
809 return true;
810 }
811
812 return false;
813}
814
c1da832a 815static void zebra_nhg_set_unhashable(struct nhg_hash_entry *nhe)
38e40db1 816{
c1da832a 817 SET_FLAG(nhe->flags, NEXTHOP_GROUP_UNHASHABLE);
38e40db1
SW
818 SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
819
c1da832a
SW
820 flog_warn(
821 EC_ZEBRA_DUPLICATE_NHG_MESSAGE,
822 "Nexthop Group with ID (%d) is a duplicate, therefore unhashable, ignoring",
823 nhe->id);
38e40db1
SW
824}
825
80286aa5
SW
826static void zebra_nhg_set_valid(struct nhg_hash_entry *nhe)
827{
828 struct nhg_connected *rb_node_dep;
829
830 SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
831
832 frr_each(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep)
833 zebra_nhg_set_valid(rb_node_dep->nhe);
834}
835
836static void zebra_nhg_set_invalid(struct nhg_hash_entry *nhe)
837{
838 struct nhg_connected *rb_node_dep;
839
840 UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
841
842 /* Update validity of nexthops depending on it */
843 frr_each(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep)
844 zebra_nhg_check_valid(rb_node_dep->nhe);
845}
846
847void zebra_nhg_check_valid(struct nhg_hash_entry *nhe)
848{
849 struct nhg_connected *rb_node_dep = NULL;
850 bool valid = false;
851
852 /* If anthing else in the group is valid, the group is valid */
853 frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
854 if (CHECK_FLAG(rb_node_dep->nhe->flags, NEXTHOP_GROUP_VALID)) {
855 valid = true;
856 goto done;
857 }
858 }
859
860done:
861 if (valid)
862 zebra_nhg_set_valid(nhe);
863 else
864 zebra_nhg_set_invalid(nhe);
865}
866
867
177e711d 868static void zebra_nhg_release(struct nhg_hash_entry *nhe)
9a1588c4
SW
869{
870 /* Remove it from any lists it may be on */
871 zebra_nhg_depends_release(nhe);
872 zebra_nhg_dependents_release(nhe);
873 if (nhe->ifp)
874 if_nhg_dependents_del(nhe->ifp, nhe);
875
876 /*
c1da832a 877 * If its unhashable, we didn't store it here and have to be
9a1588c4
SW
878 * sure we don't clear one thats actually being used.
879 */
c1da832a 880 if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_UNHASHABLE))
9a1588c4 881 hash_release(zrouter.nhgs, nhe);
9a1588c4 882
9a1588c4
SW
883 hash_release(zrouter.nhgs_id, nhe);
884}
885
9a1588c4
SW
886static void zebra_nhg_handle_uninstall(struct nhg_hash_entry *nhe)
887{
177e711d 888 zebra_nhg_release(nhe);
9a1588c4
SW
889 zebra_nhg_free(nhe);
890}
891
80286aa5
SW
892static void zebra_nhg_handle_install(struct nhg_hash_entry *nhe)
893{
894 /* Update validity of groups depending on it */
895 struct nhg_connected *rb_node_dep;
896
897 frr_each_safe(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep)
898 zebra_nhg_set_valid(rb_node_dep->nhe);
899}
900
9a1588c4
SW
901/*
902 * The kernel/other program has changed the state of a nexthop object we are
903 * using.
904 */
905static void zebra_nhg_handle_kernel_state_change(struct nhg_hash_entry *nhe,
906 bool is_delete)
907{
908 if (nhe->refcnt) {
909 flog_err(
910 EC_ZEBRA_NHG_SYNC,
911 "Kernel %s a nexthop group with ID (%u) that we are still using for a route, sending it back down",
912 (is_delete ? "deleted" : "updated"), nhe->id);
913
914 UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
915 zebra_nhg_install_kernel(nhe);
177e711d 916 } else
9a1588c4 917 zebra_nhg_handle_uninstall(nhe);
9a1588c4
SW
918}
919
e22e8001
SW
920static int nhg_ctx_process_new(struct nhg_ctx *ctx)
921{
922 struct nexthop_group *nhg = NULL;
37c6708b 923 struct nhg_connected_tree_head nhg_depends = {};
9a1588c4 924 struct nhg_hash_entry *lookup = NULL;
3057df51
SW
925 struct nhg_hash_entry *nhe = NULL;
926
10200d40
SW
927 uint32_t id = nhg_ctx_get_id(ctx);
928 uint8_t count = nhg_ctx_get_count(ctx);
929 vrf_id_t vrf_id = nhg_ctx_get_vrf_id(ctx);
930 int type = nhg_ctx_get_type(ctx);
931 afi_t afi = nhg_ctx_get_afi(ctx);
932
933 lookup = zebra_nhg_lookup_id(id);
9a1588c4
SW
934
935 if (lookup) {
936 /* This is already present in our table, hence an update
937 * that we did not initate.
938 */
939 zebra_nhg_handle_kernel_state_change(lookup, false);
940 return 0;
941 }
942
10200d40 943 if (nhg_ctx_get_count(ctx)) {
e22e8001 944 nhg = nexthop_group_new();
1b366e63
SW
945 if (zebra_nhg_process_grp(nhg, &nhg_depends,
946 nhg_ctx_get_grp(ctx), count)) {
947 depends_decrement_free(&nhg_depends);
d3a35138 948 nexthop_group_delete(&nhg);
fec211ad 949 return -ENOENT;
1b366e63
SW
950 }
951
10200d40
SW
952 if (!zebra_nhg_find(&nhe, id, nhg, &nhg_depends, vrf_id, type,
953 afi))
38e40db1 954 depends_decrement_free(&nhg_depends);
4505578b 955
e22e8001 956 /* These got copied over in zebra_nhg_alloc() */
d3a35138 957 nexthop_group_delete(&nhg);
38e40db1 958 } else
10200d40
SW
959 nhe = zebra_nhg_find_nexthop(id, nhg_ctx_get_nh(ctx), afi,
960 type);
e22e8001
SW
961
962 if (nhe) {
10200d40 963 if (id != nhe->id) {
38e40db1
SW
964 struct nhg_hash_entry *kernel_nhe = NULL;
965
e22e8001 966 /* Duplicate but with different ID from
2d3c57e6
SW
967 * the kernel
968 */
e22e8001
SW
969
970 /* The kernel allows duplicate nexthops
971 * as long as they have different IDs.
972 * We are ignoring those to prevent
973 * syncing problems with the kernel
974 * changes.
38e40db1
SW
975 *
976 * We maintain them *ONLY* in the ID hash table to
c1da832a
SW
977 * track them and set the flag to indicated
978 * their attributes are unhashable.
38e40db1
SW
979 */
980
10200d40 981 kernel_nhe = zebra_nhg_copy(nhe, id);
38e40db1 982 zebra_nhg_insert_id(kernel_nhe);
c1da832a
SW
983 zebra_nhg_set_unhashable(kernel_nhe);
984 } else if (zebra_nhg_contains_unhashable(nhe)) {
985 /* The group we got contains an unhashable/duplicated
986 * depend, so lets mark this group as unhashable as well
987 * and release it from the non-ID hash.
e22e8001 988 */
38e40db1 989 hash_release(zrouter.nhgs, nhe);
c1da832a 990 zebra_nhg_set_unhashable(nhe);
38e40db1 991 } else {
e22e8001 992 /* It actually created a new nhe */
38e40db1
SW
993 SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
994 SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
e22e8001
SW
995 }
996 } else {
997 flog_err(
998 EC_ZEBRA_TABLE_LOOKUP_FAILED,
999 "Zebra failed to find or create a nexthop hash entry for ID (%u)",
10200d40 1000 id);
e22e8001
SW
1001 return -1;
1002 }
1003
1004 return 0;
1005}
1006
9a1588c4
SW
1007static int nhg_ctx_process_del(struct nhg_ctx *ctx)
1008{
1009 struct nhg_hash_entry *nhe = NULL;
10200d40 1010 uint32_t id = nhg_ctx_get_id(ctx);
9a1588c4 1011
10200d40 1012 nhe = zebra_nhg_lookup_id(id);
9a1588c4
SW
1013
1014 if (!nhe) {
1015 flog_warn(
1016 EC_ZEBRA_BAD_NHG_MESSAGE,
1017 "Kernel delete message received for nexthop group ID (%u) that we do not have in our ID table",
10200d40 1018 id);
81505946 1019 return -1;
9a1588c4
SW
1020 }
1021
1022 zebra_nhg_handle_kernel_state_change(nhe, true);
1023
1024 return 0;
1025}
1026
7c6d5f25 1027static void nhg_ctx_fini(struct nhg_ctx **ctx)
e22e8001
SW
1028{
1029 /*
1030 * Just freeing for now, maybe do something more in the future
1031 * based on flag.
1032 */
1033
7134ba70 1034 nhg_ctx_free(ctx);
e22e8001
SW
1035}
1036
1b366e63
SW
1037static int queue_add(struct nhg_ctx *ctx)
1038{
1039 /* If its queued or already processed do nothing */
1040 if (nhg_ctx_get_status(ctx) == NHG_CTX_QUEUED)
1041 return 0;
1042
1043 if (rib_queue_nhg_add(ctx)) {
1044 nhg_ctx_set_status(ctx, NHG_CTX_FAILURE);
1045 return -1;
1046 }
1047
1048 nhg_ctx_set_status(ctx, NHG_CTX_QUEUED);
1049
1050 return 0;
1051}
1052
e22e8001
SW
1053int nhg_ctx_process(struct nhg_ctx *ctx)
1054{
1055 int ret = 0;
1056
1057 switch (nhg_ctx_get_op(ctx)) {
1058 case NHG_CTX_OP_NEW:
1059 ret = nhg_ctx_process_new(ctx);
fec211ad 1060 if (nhg_ctx_get_count(ctx) && ret == -ENOENT
1b366e63 1061 && nhg_ctx_get_status(ctx) != NHG_CTX_REQUEUED) {
e1292378
SW
1062 /**
1063 * We have entered a situation where we are
1064 * processing a group from the kernel
1065 * that has a contained nexthop which
1066 * we have not yet processed.
1b366e63 1067 *
e1292378
SW
1068 * Re-enqueue this ctx to be handled exactly one
1069 * more time (indicated by the flag).
1070 *
1071 * By the time we get back to it, we
1072 * should have processed its depends.
1b366e63
SW
1073 */
1074 nhg_ctx_set_status(ctx, NHG_CTX_NONE);
1075 if (queue_add(ctx) == 0) {
1076 nhg_ctx_set_status(ctx, NHG_CTX_REQUEUED);
1077 return 0;
1078 }
1079 }
e22e8001
SW
1080 break;
1081 case NHG_CTX_OP_DEL:
9a1588c4 1082 ret = nhg_ctx_process_del(ctx);
e22e8001
SW
1083 case NHG_CTX_OP_NONE:
1084 break;
1085 }
1086
1087 nhg_ctx_set_status(ctx, (ret ? NHG_CTX_FAILURE : NHG_CTX_SUCCESS));
1088
7c6d5f25 1089 nhg_ctx_fini(&ctx);
e22e8001
SW
1090
1091 return ret;
1092}
3057df51 1093
e22e8001
SW
1094/* Kernel-side, you either get a single new nexthop or a array of ID's */
1095int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, struct nh_grp *grp,
38e40db1
SW
1096 uint8_t count, vrf_id_t vrf_id, afi_t afi, int type,
1097 int startup)
e22e8001 1098{
e22e8001
SW
1099 struct nhg_ctx *ctx = NULL;
1100
38e40db1
SW
1101 if (id > id_counter)
1102 /* Increase our counter so we don't try to create
1103 * an ID that already exists
1104 */
1105 id_counter = id;
1106
81505946 1107 ctx = nhg_ctx_init(id, nh, grp, vrf_id, afi, type, count);
e22e8001
SW
1108 nhg_ctx_set_op(ctx, NHG_CTX_OP_NEW);
1109
38e40db1
SW
1110 /* Under statup conditions, we need to handle them immediately
1111 * like we do for routes. Otherwise, we are going to get a route
1112 * with a nhe_id that we have not handled.
1113 */
1114 if (startup)
1115 return nhg_ctx_process(ctx);
1116
e22e8001 1117 if (queue_add(ctx)) {
7c6d5f25 1118 nhg_ctx_fini(&ctx);
e22e8001
SW
1119 return -1;
1120 }
1121
1122 return 0;
1123}
1124
9a1588c4 1125/* Kernel-side, received delete message */
88cafda7 1126int zebra_nhg_kernel_del(uint32_t id, vrf_id_t vrf_id)
9a1588c4
SW
1127{
1128 struct nhg_ctx *ctx = NULL;
1129
88cafda7 1130 ctx = nhg_ctx_init(id, NULL, NULL, vrf_id, 0, 0, 0);
9a1588c4
SW
1131
1132 nhg_ctx_set_op(ctx, NHG_CTX_OP_DEL);
1133
1134 if (queue_add(ctx)) {
7c6d5f25 1135 nhg_ctx_fini(&ctx);
9a1588c4
SW
1136 return -1;
1137 }
1138
1139 return 0;
1140}
1141
5657e7e9 1142/* Some dependency helper functions */
0fff714e
SW
1143static struct nhg_hash_entry *depends_find_recursive(const struct nexthop *nh,
1144 afi_t afi)
98cda54a 1145{
0fff714e
SW
1146 struct nhg_hash_entry *nhe;
1147 struct nexthop *lookup = NULL;
98cda54a 1148
77bf9504 1149 lookup = nexthop_dup(nh, NULL);
0fff714e
SW
1150
1151 nhe = zebra_nhg_find_nexthop(0, lookup, afi, 0);
1152
1153 nexthops_free(lookup);
1154
1155 return nhe;
1156}
1157
1158static struct nhg_hash_entry *depends_find_singleton(const struct nexthop *nh,
1159 afi_t afi)
1160{
1161 struct nhg_hash_entry *nhe;
1162 struct nexthop lookup = {};
606fa9e5 1163
cb86eba3
MS
1164 /* Capture a snapshot of this single nh; it might be part of a list,
1165 * so we need to make a standalone copy.
1166 */
77bf9504 1167 nexthop_copy_no_recurse(&lookup, nh, NULL);
8a507796 1168
cb86eba3 1169 nhe = zebra_nhg_find_nexthop(0, &lookup, afi, 0);
8a507796 1170
cb86eba3
MS
1171 /* The copy may have allocated labels; free them if necessary. */
1172 nexthop_del_labels(&lookup);
4505578b 1173
0fff714e
SW
1174 return nhe;
1175}
1176
1177static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi)
1178{
1179 struct nhg_hash_entry *nhe = NULL;
1180
1181 if (!nh)
1182 goto done;
1183
1184 /* We are separating these functions out to increase handling speed
1185 * in the non-recursive case (by not alloc/freeing)
1186 */
1187 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE))
1188 nhe = depends_find_recursive(nh, afi);
1189 else
1190 nhe = depends_find_singleton(nh, afi);
1191
606fa9e5 1192done:
4505578b 1193 return nhe;
98cda54a
SW
1194}
1195
37c6708b 1196static void depends_add(struct nhg_connected_tree_head *head,
5657e7e9
SW
1197 struct nhg_hash_entry *depend)
1198{
5bf15faa
SW
1199 /* If NULL is returned, it was successfully added and
1200 * needs to have its refcnt incremented.
1201 *
1202 * Else the NHE is already present in the tree and doesn't
1203 * need to increment the refcnt.
1204 */
1205 if (nhg_connected_tree_add_nhe(head, depend) == NULL)
1206 zebra_nhg_increment_ref(depend);
5657e7e9
SW
1207}
1208
38e40db1
SW
1209static struct nhg_hash_entry *
1210depends_find_add(struct nhg_connected_tree_head *head, struct nexthop *nh,
1211 afi_t afi)
5657e7e9
SW
1212{
1213 struct nhg_hash_entry *depend = NULL;
1214
1215 depend = depends_find(nh, afi);
1b366e63 1216
1d48702e
MS
1217 if (IS_ZEBRA_DEBUG_NHG_DETAIL)
1218 zlog_debug("%s: nh %pNHv => %p",
1219 __func__, nh, depend);
1220
1b366e63
SW
1221 if (depend)
1222 depends_add(head, depend);
38e40db1
SW
1223
1224 return depend;
1225}
1226
1227static struct nhg_hash_entry *
1228depends_find_id_add(struct nhg_connected_tree_head *head, uint32_t id)
1229{
1230 struct nhg_hash_entry *depend = NULL;
1231
1232 depend = zebra_nhg_lookup_id(id);
1b366e63
SW
1233
1234 if (depend)
1235 depends_add(head, depend);
38e40db1
SW
1236
1237 return depend;
5657e7e9
SW
1238}
1239
37c6708b 1240static void depends_decrement_free(struct nhg_connected_tree_head *head)
5657e7e9 1241{
37c6708b
SW
1242 nhg_connected_tree_decrement_ref(head);
1243 nhg_connected_tree_free(head);
5657e7e9
SW
1244}
1245
e22e8001 1246/* Rib-side, you get a nexthop group struct */
7f997721
SW
1247struct nhg_hash_entry *
1248zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi)
e22e8001
SW
1249{
1250 struct nhg_hash_entry *nhe = NULL;
88cafda7
DS
1251 vrf_id_t vrf_id;
1252
1253 /*
1254 * CLANG SA is complaining that nexthop may be NULL
1255 * Make it happy but this is ridonc
1256 */
1257 assert(nhg->nexthop);
1258 vrf_id = !vrf_is_backend_netns() ? VRF_DEFAULT : nhg->nexthop->vrf_id;
98cda54a 1259
88cafda7 1260 zebra_nhg_find(&nhe, id, nhg, NULL, vrf_id, rt_afi, 0);
4505578b 1261
3057df51
SW
1262 return nhe;
1263}
1264
1d48702e
MS
1265/*
1266 * Allocate backup nexthop info object. Typically these are embedded in
1267 * nhg_hash_entry objects.
1268 */
1269struct nhg_backup_info *zebra_nhg_backup_alloc(void)
1270{
1271 struct nhg_backup_info *p;
1272
1273 p = XCALLOC(MTYPE_NHG, sizeof(struct nhg_backup_info));
1274
1275 p->nhe = zebra_nhg_alloc();
1276
1277 /* Identify the embedded group used to hold the list of backups */
1278 SET_FLAG(p->nhe->flags, NEXTHOP_GROUP_BACKUP);
1279
1280 return p;
1281}
1282
1283/*
1284 * Free backup nexthop info object, deal with any embedded allocations
1285 */
1286void zebra_nhg_backup_free(struct nhg_backup_info **p)
1287{
1288 if (p && *p) {
1289 if ((*p)->nhe)
1290 zebra_nhg_free((*p)->nhe);
1291
1292 XFREE(MTYPE_NHG, (*p));
1293 }
1294}
1295
1296/* Accessor for backup nexthop info */
1297struct nhg_hash_entry *zebra_nhg_get_backup_nhe(struct nhg_hash_entry *nhe)
1298{
1299 struct nhg_hash_entry *p = NULL;
1300
1301 if (nhe) {
1302 if (nhe->backup_info)
1303 p = nhe->backup_info->nhe;
1304 }
1305
1306 return p;
1307}
1308
1309/* Accessor for backup nexthop group */
1310struct nexthop_group *zebra_nhg_get_backup_nhg(struct nhg_hash_entry *nhe)
1311{
1312 struct nexthop_group *p = NULL;
1313
1314 if (nhe) {
1315 if (nhe->backup_info && nhe->backup_info->nhe)
1316 p = &(nhe->backup_info->nhe->nhg);
1317 }
1318
1319 return p;
1320}
1321
1322/*
1323 * Helper to return a copy of a backup_info - note that this is a shallow
1324 * copy, meant to be used when creating a new nhe from info passed in with
1325 * a route e.g.
1326 */
1327static struct nhg_backup_info *
1328nhg_backup_copy(const struct nhg_backup_info *orig)
1329{
1330 struct nhg_backup_info *b;
1331
1332 b = zebra_nhg_backup_alloc();
1333
1334 /* Copy list of nexthops */
1335 nexthop_group_copy(&(b->nhe->nhg), &(orig->nhe->nhg));
1336
1337 return b;
1338}
1339
5948f013 1340static void zebra_nhg_free_members(struct nhg_hash_entry *nhe)
b599cd2a 1341{
c415d895
MS
1342 nexthops_free(nhe->nhg.nexthop);
1343
1d48702e
MS
1344 zebra_nhg_backup_free(&nhe->backup_info);
1345
58396544 1346 /* Decrement to remove connection ref */
37c6708b
SW
1347 nhg_connected_tree_decrement_ref(&nhe->nhg_depends);
1348 nhg_connected_tree_free(&nhe->nhg_depends);
1349 nhg_connected_tree_free(&nhe->nhg_dependents);
b599cd2a
SW
1350}
1351
0eb97b86 1352void zebra_nhg_free(struct nhg_hash_entry *nhe)
a95b8020 1353{
38e40db1
SW
1354 if (nhe->refcnt)
1355 zlog_debug("nhe_id=%u hash refcnt=%d", nhe->id, nhe->refcnt);
1356
8e401b25 1357 zebra_nhg_free_members(nhe);
51d80884
SW
1358
1359 XFREE(MTYPE_NHG, nhe);
a95b8020
SW
1360}
1361
0eb97b86
MS
1362void zebra_nhg_hash_free(void *p)
1363{
1364 zebra_nhg_free((struct nhg_hash_entry *)p);
1365}
1366
d9f5b2f5
SW
1367void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe)
1368{
e22e8001
SW
1369 nhe->refcnt--;
1370
32e29e79 1371 if (!zebra_nhg_depends_is_empty(nhe))
37c6708b 1372 nhg_connected_tree_decrement_ref(&nhe->nhg_depends);
f54ef6a5 1373
38e40db1 1374 if (ZEBRA_NHG_CREATED(nhe) && nhe->refcnt <= 0)
cb50cbc9 1375 zebra_nhg_uninstall_kernel(nhe);
7fd392cc
SW
1376}
1377
7fd392cc
SW
1378void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe)
1379{
e22e8001
SW
1380 nhe->refcnt++;
1381
32e29e79 1382 if (!zebra_nhg_depends_is_empty(nhe))
37c6708b 1383 nhg_connected_tree_increment_ref(&nhe->nhg_depends);
e22e8001 1384}
d9f5b2f5 1385
ad28e79a
SW
1386static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
1387 struct nexthop *nexthop)
1388{
1389 struct nexthop *resolved_hop;
b43434ad
SW
1390 uint8_t num_labels = 0;
1391 mpls_label_t labels[MPLS_MAX_LABELS];
1392 enum lsp_types_t label_type = ZEBRA_LSP_NONE;
1393 int i = 0;
ad28e79a
SW
1394
1395 resolved_hop = nexthop_new();
1396 SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
1397
1398 resolved_hop->vrf_id = nexthop->vrf_id;
1399 switch (newhop->type) {
1400 case NEXTHOP_TYPE_IPV4:
1401 case NEXTHOP_TYPE_IPV4_IFINDEX:
1402 /* If the resolving route specifies a gateway, use it */
1403 resolved_hop->type = newhop->type;
1404 resolved_hop->gate.ipv4 = newhop->gate.ipv4;
1405
1406 if (newhop->ifindex) {
1407 resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
1408 resolved_hop->ifindex = newhop->ifindex;
1409 }
1410 break;
1411 case NEXTHOP_TYPE_IPV6:
1412 case NEXTHOP_TYPE_IPV6_IFINDEX:
1413 resolved_hop->type = newhop->type;
1414 resolved_hop->gate.ipv6 = newhop->gate.ipv6;
1415
1416 if (newhop->ifindex) {
1417 resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
1418 resolved_hop->ifindex = newhop->ifindex;
1419 }
1420 break;
1421 case NEXTHOP_TYPE_IFINDEX:
1422 /* If the resolving route is an interface route,
1423 * it means the gateway we are looking up is connected
1424 * to that interface. (The actual network is _not_ onlink).
1425 * Therefore, the resolved route should have the original
1426 * gateway as nexthop as it is directly connected.
1427 *
1428 * On Linux, we have to set the onlink netlink flag because
1429 * otherwise, the kernel won't accept the route.
1430 */
1431 resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
1432 if (afi == AFI_IP) {
1433 resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
1434 resolved_hop->gate.ipv4 = nexthop->gate.ipv4;
1435 } else if (afi == AFI_IP6) {
1436 resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
1437 resolved_hop->gate.ipv6 = nexthop->gate.ipv6;
1438 }
1439 resolved_hop->ifindex = newhop->ifindex;
1440 break;
1441 case NEXTHOP_TYPE_BLACKHOLE:
1442 resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE;
2dc359a6 1443 resolved_hop->bh_type = newhop->bh_type;
ad28e79a
SW
1444 break;
1445 }
1446
1447 if (newhop->flags & NEXTHOP_FLAG_ONLINK)
1448 resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
1449
b43434ad
SW
1450 /* Copy labels of the resolved route and the parent resolving to it */
1451 if (newhop->nh_label) {
1452 for (i = 0; i < newhop->nh_label->num_labels; i++)
1453 labels[num_labels++] = newhop->nh_label->label[i];
1454 label_type = newhop->nh_label_type;
1455 }
1456
1457 if (nexthop->nh_label) {
1458 for (i = 0; i < nexthop->nh_label->num_labels; i++)
1459 labels[num_labels++] = nexthop->nh_label->label[i];
1460
1461 /* If the parent has labels, use its type */
1462 label_type = nexthop->nh_label_type;
1463 }
1464
1465 if (num_labels)
1466 nexthop_add_labels(resolved_hop, label_type, num_labels,
1467 labels);
ad28e79a
SW
1468
1469 resolved_hop->rparent = nexthop;
50d89650 1470 _nexthop_add(&nexthop->resolved, resolved_hop);
ad28e79a
SW
1471}
1472
6913cb1b
SW
1473/* Checks if nexthop we are trying to resolve to is valid */
1474static bool nexthop_valid_resolve(const struct nexthop *nexthop,
1475 const struct nexthop *resolved)
1476{
1477 /* Can't resolve to a recursive nexthop */
1478 if (CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_RECURSIVE))
1479 return false;
1480
1481 switch (nexthop->type) {
1482 case NEXTHOP_TYPE_IPV4_IFINDEX:
1483 case NEXTHOP_TYPE_IPV6_IFINDEX:
1484 /* If the nexthop we are resolving to does not match the
1485 * ifindex for the nexthop the route wanted, its not valid.
1486 */
1487 if (nexthop->ifindex != resolved->ifindex)
1488 return false;
1489 break;
1490 case NEXTHOP_TYPE_IPV4:
1491 case NEXTHOP_TYPE_IPV6:
1492 case NEXTHOP_TYPE_IFINDEX:
1493 case NEXTHOP_TYPE_BLACKHOLE:
1494 break;
1495 }
1496
1497 return true;
1498}
1499
ad28e79a
SW
1500/*
1501 * Given a nexthop we need to properly recursively resolve
1502 * the route. As such, do a table lookup to find and match
98cda54a
SW
1503 * if at all possible. Set the nexthop->ifindex and resolved_id
1504 * as appropriate
ad28e79a
SW
1505 */
1506static int nexthop_active(afi_t afi, struct route_entry *re,
8a507796 1507 struct nexthop *nexthop, struct route_node *top)
ad28e79a
SW
1508{
1509 struct prefix p;
1510 struct route_table *table;
1511 struct route_node *rn;
1512 struct route_entry *match = NULL;
1513 int resolved;
1514 struct nexthop *newhop;
1515 struct interface *ifp;
1516 rib_dest_t *dest;
5a0bdc78 1517 struct zebra_vrf *zvrf;
ad28e79a
SW
1518
1519 if ((nexthop->type == NEXTHOP_TYPE_IPV4)
1520 || nexthop->type == NEXTHOP_TYPE_IPV6)
1521 nexthop->ifindex = 0;
1522
8a507796 1523
ad28e79a
SW
1524 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
1525 nexthops_free(nexthop->resolved);
1526 nexthop->resolved = NULL;
1527 re->nexthop_mtu = 0;
1528
1529 /*
a8c427ee 1530 * If the kernel has sent us a NEW route, then
ad28e79a 1531 * by golly gee whiz it's a good route.
a8c427ee
SW
1532 *
1533 * If its an already INSTALLED route we have already handled, then the
1534 * kernel route's nexthop might have became unreachable
1535 * and we have to handle that.
ad28e79a 1536 */
a8c427ee
SW
1537 if (!CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)
1538 && (re->type == ZEBRA_ROUTE_KERNEL
1539 || re->type == ZEBRA_ROUTE_SYSTEM))
ad28e79a
SW
1540 return 1;
1541
1542 /*
1543 * Check to see if we should trust the passed in information
1544 * for UNNUMBERED interfaces as that we won't find the GW
1545 * address in the routing table.
1546 * This check should suffice to handle IPv4 or IPv6 routes
1547 * sourced from EVPN routes which are installed with the
1548 * next hop as the remote VTEP IP.
1549 */
1550 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) {
1551 ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
1552 if (!ifp) {
1553 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
1554 zlog_debug(
d6951e5e 1555 " %s: Onlink and interface: %u[%u] does not exist",
5e81f5dd 1556 __func__, nexthop->ifindex,
ad28e79a
SW
1557 nexthop->vrf_id);
1558 return 0;
1559 }
1560 if (connected_is_unnumbered(ifp)) {
1561 if (if_is_operative(ifp))
1562 return 1;
2d3c57e6
SW
1563
1564 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
1565 zlog_debug(
d6951e5e 1566 " %s: Onlink and interface %s is not operative",
5e81f5dd 1567 __func__, ifp->name);
2d3c57e6 1568 return 0;
ad28e79a
SW
1569 }
1570 if (!if_is_operative(ifp)) {
1571 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
1572 zlog_debug(
d6951e5e 1573 " %s: Interface %s is not unnumbered",
5e81f5dd 1574 __func__, ifp->name);
ad28e79a
SW
1575 return 0;
1576 }
1577 }
1578
4dcc2276
DS
1579 if ((top->p.family == AF_INET && top->p.prefixlen == 32
1580 && nexthop->gate.ipv4.s_addr == top->p.u.prefix4.s_addr)
1581 || (top->p.family == AF_INET6 && top->p.prefixlen == 128
1582 && memcmp(&nexthop->gate.ipv6, &top->p.u.prefix6, 16) == 0)) {
1583 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
1584 zlog_debug(
d6951e5e 1585 " :%s: Attempting to install a max prefixlength route through itself",
5e81f5dd 1586 __func__);
4dcc2276
DS
1587 return 0;
1588 }
1589
ad28e79a
SW
1590 /* Make lookup prefix. */
1591 memset(&p, 0, sizeof(struct prefix));
1592 switch (afi) {
1593 case AFI_IP:
1594 p.family = AF_INET;
1595 p.prefixlen = IPV4_MAX_PREFIXLEN;
1596 p.u.prefix4 = nexthop->gate.ipv4;
1597 break;
1598 case AFI_IP6:
1599 p.family = AF_INET6;
1600 p.prefixlen = IPV6_MAX_PREFIXLEN;
1601 p.u.prefix6 = nexthop->gate.ipv6;
1602 break;
1603 default:
1604 assert(afi != AFI_IP && afi != AFI_IP6);
1605 break;
1606 }
1607 /* Lookup table. */
1608 table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id);
5a0bdc78
PG
1609 /* get zvrf */
1610 zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
1611 if (!table || !zvrf) {
ad28e79a 1612 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
d6951e5e 1613 zlog_debug(" %s: Table not found", __func__);
ad28e79a
SW
1614 return 0;
1615 }
1616
1617 rn = route_node_match(table, (struct prefix *)&p);
1618 while (rn) {
1619 route_unlock_node(rn);
1620
1621 /* Lookup should halt if we've matched against ourselves ('top',
1622 * if specified) - i.e., we cannot have a nexthop NH1 is
1623 * resolved by a route NH1. The exception is if the route is a
1624 * host route.
1625 */
92756825 1626 if (rn == top)
ad28e79a
SW
1627 if (((afi == AFI_IP) && (rn->p.prefixlen != 32))
1628 || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) {
1629 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
1630 zlog_debug(
d6951e5e 1631 " %s: Matched against ourself and prefix length is not max bit length",
5e81f5dd 1632 __func__);
ad28e79a
SW
1633 return 0;
1634 }
1635
1636 /* Pick up selected route. */
1637 /* However, do not resolve over default route unless explicitly
2d3c57e6
SW
1638 * allowed.
1639 */
ad28e79a 1640 if (is_default_prefix(&rn->p)
5a0bdc78 1641 && !rnh_resolve_via_default(zvrf, p.family)) {
ad28e79a
SW
1642 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
1643 zlog_debug(
d6951e5e 1644 " :%s: Resolved against default route",
5e81f5dd 1645 __func__);
ad28e79a
SW
1646 return 0;
1647 }
1648
1649 dest = rib_dest_from_rnode(rn);
1650 if (dest && dest->selected_fib
1651 && !CHECK_FLAG(dest->selected_fib->status,
1652 ROUTE_ENTRY_REMOVED)
1653 && dest->selected_fib->type != ZEBRA_ROUTE_TABLE)
1654 match = dest->selected_fib;
1655
1656 /* If there is no selected route or matched route is EGP, go up
2d3c57e6
SW
1657 * tree.
1658 */
ad28e79a
SW
1659 if (!match) {
1660 do {
1661 rn = rn->parent;
1662 } while (rn && rn->info == NULL);
1663 if (rn)
1664 route_lock_node(rn);
1665
1666 continue;
1667 }
1668
1669 if (match->type == ZEBRA_ROUTE_CONNECT) {
1670 /* Directly point connected route. */
c415d895 1671 newhop = match->nhe->nhg.nexthop;
ad28e79a
SW
1672 if (newhop) {
1673 if (nexthop->type == NEXTHOP_TYPE_IPV4
1674 || nexthop->type == NEXTHOP_TYPE_IPV6)
1675 nexthop->ifindex = newhop->ifindex;
1676 }
1677 return 1;
1678 } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
1679 resolved = 0;
c415d895 1680 for (ALL_NEXTHOPS(match->nhe->nhg, newhop)) {
ad28e79a
SW
1681 if (!CHECK_FLAG(match->status,
1682 ROUTE_ENTRY_INSTALLED))
1683 continue;
6913cb1b 1684 if (!nexthop_valid_resolve(nexthop, newhop))
ad28e79a
SW
1685 continue;
1686
1687 SET_FLAG(nexthop->flags,
1688 NEXTHOP_FLAG_RECURSIVE);
ad28e79a
SW
1689 nexthop_set_resolved(afi, newhop, nexthop);
1690 resolved = 1;
1691 }
8a507796 1692 if (resolved)
ad28e79a 1693 re->nexthop_mtu = match->mtu;
8a507796 1694
ad28e79a 1695 if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
d6951e5e
DL
1696 zlog_debug(
1697 " %s: Recursion failed to find",
1698 __func__);
ad28e79a
SW
1699 return resolved;
1700 } else if (re->type == ZEBRA_ROUTE_STATIC) {
1701 resolved = 0;
c415d895 1702 for (ALL_NEXTHOPS(match->nhe->nhg, newhop)) {
ad28e79a
SW
1703 if (!CHECK_FLAG(match->status,
1704 ROUTE_ENTRY_INSTALLED))
1705 continue;
6913cb1b 1706 if (!nexthop_valid_resolve(nexthop, newhop))
ad28e79a
SW
1707 continue;
1708
1709 SET_FLAG(nexthop->flags,
1710 NEXTHOP_FLAG_RECURSIVE);
1711 nexthop_set_resolved(afi, newhop, nexthop);
1712 resolved = 1;
1713 }
8a507796 1714 if (resolved)
ad28e79a 1715 re->nexthop_mtu = match->mtu;
8a507796 1716
ad28e79a
SW
1717 if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
1718 zlog_debug(
d6951e5e 1719 " %s: Static route unable to resolve",
5e81f5dd 1720 __func__);
ad28e79a
SW
1721 return resolved;
1722 } else {
1723 if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
1724 zlog_debug(
d6951e5e 1725 " %s: Route Type %s has not turned on recursion",
5e81f5dd 1726 __func__, zebra_route_string(re->type));
ad28e79a
SW
1727 if (re->type == ZEBRA_ROUTE_BGP
1728 && !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP))
1729 zlog_debug(
d6951e5e 1730 " EBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\"");
ad28e79a
SW
1731 }
1732 return 0;
1733 }
1734 }
1735 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
d6951e5e
DL
1736 zlog_debug(" %s: Nexthop did not lookup in table",
1737 __func__);
ad28e79a
SW
1738 return 0;
1739}
1740
1741/* This function verifies reachability of one given nexthop, which can be
1742 * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
1743 * in nexthop->flags field. The nexthop->ifindex will be updated
1744 * appropriately as well. An existing route map can turn
1745 * (otherwise active) nexthop into inactive, but not vice versa.
1746 *
98cda54a
SW
1747 * If it finds a nexthop recursivedly, set the resolved_id
1748 * to match that nexthop's nhg_hash_entry ID;
1749 *
ad28e79a
SW
1750 * The return value is the final value of 'ACTIVE' flag.
1751 */
1752static unsigned nexthop_active_check(struct route_node *rn,
1753 struct route_entry *re,
8a507796 1754 struct nexthop *nexthop)
ad28e79a
SW
1755{
1756 struct interface *ifp;
b68885f9 1757 route_map_result_t ret = RMAP_PERMITMATCH;
ad28e79a
SW
1758 int family;
1759 char buf[SRCDEST2STR_BUFFER];
1760 const struct prefix *p, *src_p;
1761 struct zebra_vrf *zvrf;
1762
1763 srcdest_rnode_prefixes(rn, &p, &src_p);
1764
1765 if (rn->p.family == AF_INET)
1766 family = AFI_IP;
1767 else if (rn->p.family == AF_INET6)
1768 family = AFI_IP6;
1769 else
1770 family = 0;
1771 switch (nexthop->type) {
1772 case NEXTHOP_TYPE_IFINDEX:
1773 ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
fc8a02c4
SW
1774 /*
1775 * If the interface exists and its operative or its a kernel
1776 * route and interface is up, its active. We trust kernel routes
1777 * to be good.
1778 */
1779 if (ifp
1780 && (if_is_operative(ifp)
1781 || (if_is_up(ifp)
1782 && (re->type == ZEBRA_ROUTE_KERNEL
1783 || re->type == ZEBRA_ROUTE_SYSTEM))))
ad28e79a
SW
1784 SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1785 else
1786 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1787 break;
1788 case NEXTHOP_TYPE_IPV4:
1789 case NEXTHOP_TYPE_IPV4_IFINDEX:
1790 family = AFI_IP;
8a507796 1791 if (nexthop_active(AFI_IP, re, nexthop, rn))
ad28e79a
SW
1792 SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1793 else
1794 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1795 break;
1796 case NEXTHOP_TYPE_IPV6:
1797 family = AFI_IP6;
8a507796 1798 if (nexthop_active(AFI_IP6, re, nexthop, rn))
ad28e79a
SW
1799 SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1800 else
1801 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1802 break;
1803 case NEXTHOP_TYPE_IPV6_IFINDEX:
1804 /* RFC 5549, v4 prefix with v6 NH */
1805 if (rn->p.family != AF_INET)
1806 family = AFI_IP6;
1807 if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
1808 ifp = if_lookup_by_index(nexthop->ifindex,
1809 nexthop->vrf_id);
1810 if (ifp && if_is_operative(ifp))
1811 SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1812 else
1813 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1814 } else {
8a507796 1815 if (nexthop_active(AFI_IP6, re, nexthop, rn))
ad28e79a
SW
1816 SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1817 else
1818 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1819 }
1820 break;
1821 case NEXTHOP_TYPE_BLACKHOLE:
1822 SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1823 break;
1824 default:
1825 break;
1826 }
1827 if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
1828 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
d6951e5e
DL
1829 zlog_debug(
1830 " %s: Unable to find a active nexthop",
1831 __func__);
ad28e79a
SW
1832 return 0;
1833 }
1834
1835 /* XXX: What exactly do those checks do? Do we support
1836 * e.g. IPv4 routes with IPv6 nexthops or vice versa?
1837 */
1838 if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET)
1839 || (family == AFI_IP6 && p->family != AF_INET6))
1840 return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1841
1842 /* The original code didn't determine the family correctly
1843 * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi
1844 * from the rib_table_info in those cases.
1845 * Possibly it may be better to use only the rib_table_info
1846 * in every case.
1847 */
1848 if (!family) {
1849 rib_table_info_t *info;
1850
1851 info = srcdest_rnode_table_info(rn);
1852 family = info->afi;
1853 }
1854
1855 memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr));
1856
1857 zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
1858 if (!zvrf) {
1859 if (IS_ZEBRA_DEBUG_RIB_DETAILED)
d6951e5e 1860 zlog_debug(" %s: zvrf is NULL", __func__);
ad28e79a
SW
1861 return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1862 }
1863
1864 /* It'll get set if required inside */
1865 ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop,
1866 zvrf, re->tag);
1867 if (ret == RMAP_DENYMATCH) {
1868 if (IS_ZEBRA_DEBUG_RIB) {
1869 srcdest_rnode2str(rn, buf, sizeof(buf));
1870 zlog_debug(
1871 "%u:%s: Filtering out with NH out %s due to route map",
1872 re->vrf_id, buf,
1873 ifindex2ifname(nexthop->ifindex,
1874 nexthop->vrf_id));
1875 }
1876 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1877 }
1878 return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1879}
1880
086e4e02
SW
1881/* Helper function called after resolution to walk nhg rb trees
1882 * and toggle the NEXTHOP_GROUP_VALID flag if the nexthop
1883 * is active on singleton NHEs.
1884 */
1885static bool zebra_nhg_set_valid_if_active(struct nhg_hash_entry *nhe)
1886{
1887 struct nhg_connected *rb_node_dep = NULL;
1888 bool valid = false;
1889
1890 if (!zebra_nhg_depends_is_empty(nhe)) {
1891 /* Is at least one depend valid? */
1892 frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
1893 if (zebra_nhg_set_valid_if_active(rb_node_dep->nhe))
1894 valid = true;
1895 }
1896
1897 goto done;
1898 }
1899
1900 /* should be fully resolved singleton at this point */
1901 if (CHECK_FLAG(nhe->nhg.nexthop->flags, NEXTHOP_FLAG_ACTIVE))
1902 valid = true;
1903
1904done:
1905 if (valid)
1906 SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
1907
1908 return valid;
1909}
1910
ad28e79a
SW
1911/*
1912 * Iterate over all nexthops of the given RIB entry and refresh their
9a0d4dd3
DS
1913 * ACTIVE flag. If any nexthop is found to toggle the ACTIVE flag,
1914 * the whole re structure is flagged with ROUTE_ENTRY_CHANGED.
ad28e79a
SW
1915 *
1916 * Return value is the new number of active nexthops.
1917 */
1918int nexthop_active_update(struct route_node *rn, struct route_entry *re)
1919{
98cda54a 1920 struct nexthop_group new_grp = {};
ad28e79a
SW
1921 struct nexthop *nexthop;
1922 union g_addr prev_src;
1923 unsigned int prev_active, new_active;
1924 ifindex_t prev_index;
9a0d4dd3 1925 uint8_t curr_active = 0;
e22e8001 1926
98cda54a 1927 afi_t rt_afi = family2afi(rn->p.family);
e22e8001 1928
98cda54a 1929 UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
e22e8001 1930
98cda54a 1931 /* Copy over the nexthops in current state */
c415d895 1932 nexthop_group_copy(&new_grp, &(re->nhe->nhg));
ad28e79a 1933
98cda54a 1934 for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) {
ad28e79a 1935
ad28e79a
SW
1936 /* No protocol daemon provides src and so we're skipping
1937 * tracking it */
1938 prev_src = nexthop->rmap_src;
1939 prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
1940 prev_index = nexthop->ifindex;
1941 /*
1942 * We need to respect the multipath_num here
1943 * as that what we should be able to install from
1944 * a multipath perpsective should not be a data plane
1945 * decision point.
1946 */
98cda54a 1947 new_active =
8a507796 1948 nexthop_active_check(rn, re, nexthop);
98cda54a 1949
08de78b8 1950 if (new_active && curr_active >= zrouter.multipath_num) {
4c55b5ff
SW
1951 struct nexthop *nh;
1952
1953 /* Set it and its resolved nexthop as inactive. */
1954 for (nh = nexthop; nh; nh = nh->resolved)
1955 UNSET_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE);
1956
ad28e79a
SW
1957 new_active = 0;
1958 }
9a0d4dd3 1959
df31a989 1960 if (new_active)
9a0d4dd3
DS
1961 curr_active++;
1962
ad28e79a
SW
1963 /* Don't allow src setting on IPv6 addr for now */
1964 if (prev_active != new_active || prev_index != nexthop->ifindex
1965 || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX
1966 && nexthop->type < NEXTHOP_TYPE_IPV6)
1967 && prev_src.ipv4.s_addr
1968 != nexthop->rmap_src.ipv4.s_addr)
1969 || ((nexthop->type >= NEXTHOP_TYPE_IPV6
1970 && nexthop->type < NEXTHOP_TYPE_BLACKHOLE)
1971 && !(IPV6_ADDR_SAME(&prev_src.ipv6,
1972 &nexthop->rmap_src.ipv6)))
8a507796 1973 || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED))
ad28e79a 1974 SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
ad28e79a
SW
1975 }
1976
8a507796 1977 if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED)) {
98cda54a 1978 struct nhg_hash_entry *new_nhe = NULL;
98cda54a 1979
7f997721 1980 new_nhe = zebra_nhg_rib_find(0, &new_grp, rt_afi);
98cda54a 1981
5463ce26 1982 route_entry_update_nhe(re, new_nhe);
e22e8001
SW
1983 }
1984
086e4e02
SW
1985 /* Walk the NHE depends tree and toggle NEXTHOP_GROUP_VALID
1986 * flag where appropriate.
1987 */
715e5c70 1988 if (curr_active)
086e4e02 1989 zebra_nhg_set_valid_if_active(re->nhe);
98cda54a
SW
1990
1991 /*
1992 * Do not need these nexthops anymore since they
1993 * were either copied over into an nhe or not
1994 * used at all.
1995 */
1996 nexthops_free(new_grp.nexthop);
9a0d4dd3 1997 return curr_active;
ad28e79a 1998}
5be96a2d 1999
497ff579
SW
2000/* Recursively construct a grp array of fully resolved IDs.
2001 *
2002 * This function allows us to account for groups within groups,
2003 * by converting them into a flat array of IDs.
2004 *
2005 * nh_grp is modified at every level of recursion to append
2006 * to it the next unique, fully resolved ID from the entire tree.
2007 *
2008 *
2009 * Note:
2010 * I'm pretty sure we only allow ONE level of group within group currently.
2011 * But making this recursive just in case that ever changes.
2012 */
2013static uint8_t zebra_nhg_nhe2grp_internal(struct nh_grp *grp,
2014 uint8_t curr_index,
2015 struct nhg_hash_entry *nhe,
2016 int max_num)
98cda54a
SW
2017{
2018 struct nhg_connected *rb_node_dep = NULL;
2019 struct nhg_hash_entry *depend = NULL;
497ff579 2020 uint8_t i = curr_index;
98cda54a 2021
fec211ad 2022 frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
8dbc800f
SW
2023 bool duplicate = false;
2024
497ff579
SW
2025 if (i >= max_num)
2026 goto done;
2027
98cda54a
SW
2028 depend = rb_node_dep->nhe;
2029
2030 /*
2031 * If its recursive, use its resolved nhe in the group
2032 */
2033 if (CHECK_FLAG(depend->flags, NEXTHOP_GROUP_RECURSIVE)) {
2034 depend = zebra_nhg_resolve(depend);
2035 if (!depend) {
2036 flog_err(
2037 EC_ZEBRA_NHG_FIB_UPDATE,
df31a989
SW
2038 "Failed to recursively resolve Nexthop Hash Entry in the group id=%u",
2039 nhe->id);
98cda54a
SW
2040 continue;
2041 }
2042 }
2043
497ff579
SW
2044 if (!zebra_nhg_depends_is_empty(depend)) {
2045 /* This is a group within a group */
2046 i = zebra_nhg_nhe2grp_internal(grp, i, depend, max_num);
2047 } else {
086e4e02
SW
2048 if (!CHECK_FLAG(depend->flags, NEXTHOP_GROUP_VALID)) {
2049 if (IS_ZEBRA_DEBUG_RIB_DETAILED
2050 || IS_ZEBRA_DEBUG_NHG)
2051 zlog_debug(
2052 "%s: Nexthop ID (%u) not valid, not appending to dataplane install group",
2053 __func__, depend->id);
2054 continue;
2055 }
2056
1866b3af
SW
2057 /* If the nexthop not installed/queued for install don't
2058 * put in the ID array.
2059 */
2060 if (!(CHECK_FLAG(depend->flags, NEXTHOP_GROUP_INSTALLED)
2061 || CHECK_FLAG(depend->flags,
2062 NEXTHOP_GROUP_QUEUED))) {
2063 if (IS_ZEBRA_DEBUG_RIB_DETAILED
2064 || IS_ZEBRA_DEBUG_NHG)
2065 zlog_debug(
2066 "%s: Nexthop ID (%u) not installed or queued for install, not appending to dataplane install group",
2067 __func__, depend->id);
2068 continue;
2069 }
2070
b1c3f7ef 2071 /* Check for duplicate IDs, ignore if found. */
497ff579 2072 for (int j = 0; j < i; j++) {
d43122b5 2073 if (depend->id == grp[j].id) {
497ff579 2074 duplicate = true;
d43122b5
SW
2075 break;
2076 }
497ff579 2077 }
8dbc800f 2078
b1c3f7ef
SW
2079 if (duplicate) {
2080 if (IS_ZEBRA_DEBUG_RIB_DETAILED
2081 || IS_ZEBRA_DEBUG_NHG)
2082 zlog_debug(
2083 "%s: Nexthop ID (%u) is duplicate, not appending to dataplane install group",
2084 __func__, depend->id);
2085 continue;
497ff579 2086 }
b1c3f7ef
SW
2087
2088 grp[i].id = depend->id;
2089 grp[i].weight = depend->nhg.nexthop->weight;
2090 i++;
8dbc800f 2091 }
98cda54a 2092 }
8dbc800f
SW
2093
2094done:
98cda54a
SW
2095 return i;
2096}
2097
497ff579
SW
2098/* Convert a nhe into a group array */
2099uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe,
2100 int max_num)
2101{
2102 /* Call into the recursive function */
2103 return zebra_nhg_nhe2grp_internal(grp, 0, nhe, max_num);
2104}
2105
5be96a2d
SW
2106void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe)
2107{
f429bd1b
SW
2108 struct nhg_connected *rb_node_dep = NULL;
2109
2110 /* Resolve it first */
2111 nhe = zebra_nhg_resolve(nhe);
2112
2113 /* Make sure all depends are installed/queued */
fec211ad 2114 frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
f429bd1b
SW
2115 zebra_nhg_install_kernel(rb_node_dep->nhe);
2116 }
2117
086e4e02
SW
2118 if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)
2119 && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)
e22e8001 2120 && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)) {
724583ed
SW
2121 /* Change its type to us since we are installing it */
2122 nhe->type = ZEBRA_ROUTE_NHG;
2123
147bad16 2124 int ret = dplane_nexthop_add(nhe);
2d3c57e6 2125
147bad16
SW
2126 switch (ret) {
2127 case ZEBRA_DPLANE_REQUEST_QUEUED:
2128 SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
2129 break;
2130 case ZEBRA_DPLANE_REQUEST_FAILURE:
2131 flog_err(
2132 EC_ZEBRA_DP_INSTALL_FAIL,
2133 "Failed to install Nexthop ID (%u) into the kernel",
2134 nhe->id);
2135 break;
2136 case ZEBRA_DPLANE_REQUEST_SUCCESS:
2137 SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
80286aa5 2138 zebra_nhg_handle_install(nhe);
147bad16
SW
2139 break;
2140 }
2141 }
2142}
2143
147bad16
SW
2144void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe)
2145{
2146 if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)) {
2147 int ret = dplane_nexthop_delete(nhe);
2d3c57e6 2148
147bad16
SW
2149 switch (ret) {
2150 case ZEBRA_DPLANE_REQUEST_QUEUED:
2151 SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
2152 break;
2153 case ZEBRA_DPLANE_REQUEST_FAILURE:
2154 flog_err(
2155 EC_ZEBRA_DP_DELETE_FAIL,
2156 "Failed to uninstall Nexthop ID (%u) from the kernel",
2157 nhe->id);
2158 break;
2159 case ZEBRA_DPLANE_REQUEST_SUCCESS:
2160 UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
2161 break;
2162 }
177e711d
SW
2163 }
2164
2165 zebra_nhg_handle_uninstall(nhe);
147bad16
SW
2166}
2167
5f3c9e52
SW
2168void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
2169{
2170 enum dplane_op_e op;
2171 enum zebra_dplane_result status;
2172 uint32_t id = 0;
2173 struct nhg_hash_entry *nhe = NULL;
2174
2175 op = dplane_ctx_get_op(ctx);
2176 status = dplane_ctx_get_status(ctx);
2177
0c8215cb 2178 id = dplane_ctx_get_nhe_id(ctx);
e22e8001 2179
177e711d
SW
2180 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2181 zlog_debug(
2182 "Nexthop dplane ctx %p, op %s, nexthop ID (%u), result %s",
2183 ctx, dplane_op2str(op), id, dplane_res2str(status));
5f3c9e52 2184
177e711d
SW
2185 switch (op) {
2186 case DPLANE_OP_NH_DELETE:
2187 if (status != ZEBRA_DPLANE_REQUEST_SUCCESS)
2188 flog_err(
2189 EC_ZEBRA_DP_DELETE_FAIL,
2190 "Failed to uninstall Nexthop ID (%u) from the kernel",
2191 id);
2192 /* We already free'd the data, nothing to do */
2193 break;
2194 case DPLANE_OP_NH_INSTALL:
2195 case DPLANE_OP_NH_UPDATE:
2196 nhe = zebra_nhg_lookup_id(id);
2197
2198 if (!nhe) {
2199 flog_err(
2200 EC_ZEBRA_NHG_SYNC,
2201 "%s operation preformed on Nexthop ID (%u) in the kernel, that we no longer have in our table",
2202 dplane_op2str(op), id);
5f3c9e52
SW
2203 break;
2204 }
177e711d
SW
2205
2206 UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
2207 if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
2208 SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
2209 SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
80286aa5 2210 zebra_nhg_handle_install(nhe);
177e711d
SW
2211 } else
2212 flog_err(
2213 EC_ZEBRA_DP_INSTALL_FAIL,
2214 "Failed to install Nexthop ID (%u) into the kernel",
2215 nhe->id);
2216 break;
2217 case DPLANE_OP_ROUTE_INSTALL:
2218 case DPLANE_OP_ROUTE_UPDATE:
2219 case DPLANE_OP_ROUTE_DELETE:
2220 case DPLANE_OP_ROUTE_NOTIFY:
2221 case DPLANE_OP_LSP_INSTALL:
2222 case DPLANE_OP_LSP_UPDATE:
2223 case DPLANE_OP_LSP_DELETE:
2224 case DPLANE_OP_LSP_NOTIFY:
2225 case DPLANE_OP_PW_INSTALL:
2226 case DPLANE_OP_PW_UNINSTALL:
2227 case DPLANE_OP_SYS_ROUTE_ADD:
2228 case DPLANE_OP_SYS_ROUTE_DELETE:
2229 case DPLANE_OP_ADDR_INSTALL:
2230 case DPLANE_OP_ADDR_UNINSTALL:
2231 case DPLANE_OP_MAC_INSTALL:
2232 case DPLANE_OP_MAC_DELETE:
2233 case DPLANE_OP_NEIGH_INSTALL:
2234 case DPLANE_OP_NEIGH_UPDATE:
2235 case DPLANE_OP_NEIGH_DELETE:
2236 case DPLANE_OP_VTEP_ADD:
2237 case DPLANE_OP_VTEP_DELETE:
2238 case DPLANE_OP_NONE:
2239 break;
2240 }
71593b3f
SW
2241
2242 dplane_ctx_fini(&ctx);
5be96a2d
SW
2243}
2244
38e40db1
SW
2245static void zebra_nhg_sweep_entry(struct hash_bucket *bucket, void *arg)
2246{
2247 struct nhg_hash_entry *nhe = NULL;
2248
2249 nhe = (struct nhg_hash_entry *)bucket->data;
2250
2251 /* If its being ref'd, just let it be uninstalled via a route removal */
2252 if (ZEBRA_NHG_CREATED(nhe) && nhe->refcnt <= 0)
2253 zebra_nhg_uninstall_kernel(nhe);
2254}
2255
2256void zebra_nhg_sweep_table(struct hash *hash)
2257{
2258 hash_iterate(hash, zebra_nhg_sweep_entry, NULL);
2259}
7c99d51b
MS
2260
2261/* Global control to disable use of kernel nexthops, if available. We can't
2262 * force the kernel to support nexthop ids, of course, but we can disable
2263 * zebra's use of them, for testing e.g. By default, if the kernel supports
2264 * nexthop ids, zebra uses them.
2265 */
2266void zebra_nhg_enable_kernel_nexthops(bool set)
2267{
2268 g_nexthops_enabled = set;
2269}
2270
2271bool zebra_nhg_kernel_nexthops_enabled(void)
2272{
2273 return g_nexthops_enabled;
2274}