]> git.proxmox.com Git - mirror_frr.git/blob - zebra/zebra_rnh.c
lib: msg: refactor common connection code from mgmtd
[mirror_frr.git] / zebra / zebra_rnh.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Zebra next hop tracking code
3 * Copyright (C) 2013 Cumulus Networks, Inc.
4 */
5
6 #include <zebra.h>
7
8 #include "prefix.h"
9 #include "table.h"
10 #include "memory.h"
11 #include "command.h"
12 #include "if.h"
13 #include "log.h"
14 #include "sockunion.h"
15 #include "linklist.h"
16 #include "frrevent.h"
17 #include "workqueue.h"
18 #include "prefix.h"
19 #include "routemap.h"
20 #include "stream.h"
21 #include "nexthop.h"
22 #include "vrf.h"
23
24 #include "zebra/zebra_router.h"
25 #include "zebra/rib.h"
26 #include "zebra/rt.h"
27 #include "zebra/zserv.h"
28 #include "zebra/zebra_ns.h"
29 #include "zebra/zebra_vrf.h"
30 #include "zebra/redistribute.h"
31 #include "zebra/debug.h"
32 #include "zebra/zebra_rnh.h"
33 #include "zebra/zebra_routemap.h"
34 #include "zebra/zebra_srte.h"
35 #include "zebra/interface.h"
36 #include "zebra/zebra_errors.h"
37
38 DEFINE_MTYPE_STATIC(ZEBRA, RNH, "Nexthop tracking object");
39
40 /* UI controls whether to notify about changes that only involve backup
41 * nexthops. Default is to notify all changes.
42 */
43 static bool rnh_hide_backups;
44
45 static void free_state(vrf_id_t vrf_id, struct route_entry *re,
46 struct route_node *rn);
47 static void copy_state(struct rnh *rnh, const struct route_entry *re,
48 struct route_node *rn);
49 static bool compare_state(struct route_entry *r1, struct route_entry *r2);
50 static void print_rnh(struct route_node *rn, struct vty *vty,
51 json_object *json);
52 static int zebra_client_cleanup_rnh(struct zserv *client);
53
54 void zebra_rnh_init(void)
55 {
56 hook_register(zserv_client_close, zebra_client_cleanup_rnh);
57 }
58
59 static inline struct route_table *get_rnh_table(vrf_id_t vrfid, afi_t afi,
60 safi_t safi)
61 {
62 struct zebra_vrf *zvrf;
63 struct route_table *t = NULL;
64
65 zvrf = zebra_vrf_lookup_by_id(vrfid);
66 if (zvrf) {
67 if (safi == SAFI_UNICAST)
68 t = zvrf->rnh_table[afi];
69 else if (safi == SAFI_MULTICAST)
70 t = zvrf->rnh_table_multicast[afi];
71 }
72
73 return t;
74 }
75
76 static void zebra_rnh_remove_from_routing_table(struct rnh *rnh)
77 {
78 struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(rnh->vrf_id);
79 struct route_table *table = zvrf->table[rnh->afi][rnh->safi];
80 struct route_node *rn;
81 rib_dest_t *dest;
82
83 if (!table)
84 return;
85
86 rn = route_node_match(table, &rnh->resolved_route);
87 if (!rn)
88 return;
89
90 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
91 zlog_debug("%s: %s(%u):%pRN removed from tracking on %pRN",
92 __func__, VRF_LOGNAME(zvrf->vrf), rnh->vrf_id,
93 rnh->node, rn);
94
95 dest = rib_dest_from_rnode(rn);
96 rnh_list_del(&dest->nht, rnh);
97 route_unlock_node(rn);
98 }
99
100 static void zebra_rnh_store_in_routing_table(struct rnh *rnh)
101 {
102 struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(rnh->vrf_id);
103 struct route_table *table = zvrf->table[rnh->afi][rnh->safi];
104 struct route_node *rn;
105 rib_dest_t *dest;
106
107 rn = route_node_match(table, &rnh->resolved_route);
108 if (!rn)
109 return;
110
111 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
112 zlog_debug("%s: %s(%u):%pRN added for tracking on %pRN",
113 __func__, VRF_LOGNAME(zvrf->vrf), rnh->vrf_id,
114 rnh->node, rn);
115
116 dest = rib_dest_from_rnode(rn);
117 rnh_list_add_tail(&dest->nht, rnh);
118 route_unlock_node(rn);
119 }
120
121 struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, safi_t safi,
122 bool *exists)
123 {
124 struct route_table *table;
125 struct route_node *rn;
126 struct rnh *rnh = NULL;
127 afi_t afi = family2afi(p->family);
128
129 if (IS_ZEBRA_DEBUG_NHT) {
130 struct vrf *vrf = vrf_lookup_by_id(vrfid);
131
132 zlog_debug("%s(%u): Add RNH %pFX for safi: %u",
133 VRF_LOGNAME(vrf), vrfid, p, safi);
134 }
135
136 table = get_rnh_table(vrfid, afi, safi);
137 if (!table) {
138 struct vrf *vrf = vrf_lookup_by_id(vrfid);
139
140 flog_warn(EC_ZEBRA_RNH_NO_TABLE,
141 "%s(%u): Add RNH %pFX - table not found",
142 VRF_LOGNAME(vrf), vrfid, p);
143 *exists = false;
144 return NULL;
145 }
146
147 /* Make it sure prefixlen is applied to the prefix. */
148 apply_mask(p);
149
150 /* Lookup (or add) route node.*/
151 rn = route_node_get(table, p);
152
153 if (!rn->info) {
154 rnh = XCALLOC(MTYPE_RNH, sizeof(struct rnh));
155
156 /*
157 * The resolved route is already 0.0.0.0/0 or
158 * 0::0/0 due to the calloc right above, but
159 * we should set the family so that future
160 * comparisons can just be done
161 */
162 rnh->resolved_route.family = p->family;
163 rnh->client_list = list_new();
164 rnh->vrf_id = vrfid;
165 rnh->seqno = 0;
166 rnh->afi = afi;
167 rnh->safi = safi;
168 rnh->zebra_pseudowire_list = list_new();
169 route_lock_node(rn);
170 rn->info = rnh;
171 rnh->node = rn;
172 *exists = false;
173
174 zebra_rnh_store_in_routing_table(rnh);
175 } else
176 *exists = true;
177
178 route_unlock_node(rn);
179 return (rn->info);
180 }
181
182 struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, safi_t safi)
183 {
184 struct route_table *table;
185 struct route_node *rn;
186
187 table = get_rnh_table(vrfid, family2afi(PREFIX_FAMILY(p)), safi);
188 if (!table)
189 return NULL;
190
191 /* Make it sure prefixlen is applied to the prefix. */
192 apply_mask(p);
193
194 /* Lookup route node.*/
195 rn = route_node_lookup(table, p);
196 if (!rn)
197 return NULL;
198
199 route_unlock_node(rn);
200 return (rn->info);
201 }
202
203 void zebra_free_rnh(struct rnh *rnh)
204 {
205 struct zebra_vrf *zvrf;
206 struct route_table *table;
207
208 zebra_rnh_remove_from_routing_table(rnh);
209 rnh->flags |= ZEBRA_NHT_DELETED;
210 list_delete(&rnh->client_list);
211 list_delete(&rnh->zebra_pseudowire_list);
212
213 zvrf = zebra_vrf_lookup_by_id(rnh->vrf_id);
214 table = zvrf->table[family2afi(rnh->resolved_route.family)][rnh->safi];
215
216 if (table) {
217 struct route_node *rern;
218
219 rern = route_node_match(table, &rnh->resolved_route);
220 if (rern) {
221 rib_dest_t *dest;
222
223 route_unlock_node(rern);
224
225 dest = rib_dest_from_rnode(rern);
226 rnh_list_del(&dest->nht, rnh);
227 }
228 }
229 free_state(rnh->vrf_id, rnh->state, rnh->node);
230 XFREE(MTYPE_RNH, rnh);
231 }
232
233 static void zebra_delete_rnh(struct rnh *rnh)
234 {
235 struct route_node *rn;
236
237 if (!list_isempty(rnh->client_list)
238 || !list_isempty(rnh->zebra_pseudowire_list))
239 return;
240
241 if ((rnh->flags & ZEBRA_NHT_DELETED) || !(rn = rnh->node))
242 return;
243
244 if (IS_ZEBRA_DEBUG_NHT) {
245 struct vrf *vrf = vrf_lookup_by_id(rnh->vrf_id);
246
247 zlog_debug("%s(%u): Del RNH %pRN", VRF_LOGNAME(vrf),
248 rnh->vrf_id, rnh->node);
249 }
250
251 zebra_free_rnh(rnh);
252 rn->info = NULL;
253 route_unlock_node(rn);
254 }
255
256 /*
257 * This code will send to the registering client
258 * the looked up rnh.
259 * For a rnh that was created, there is no data
260 * so it will send an empty nexthop group
261 * If rnh exists then we know it has been evaluated
262 * and as such it will have a resolved rnh.
263 */
264 void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client,
265 vrf_id_t vrf_id)
266 {
267 if (IS_ZEBRA_DEBUG_NHT) {
268 struct vrf *vrf = vrf_lookup_by_id(vrf_id);
269
270 zlog_debug("%s(%u): Client %s registers for RNH %pRN",
271 VRF_LOGNAME(vrf), vrf_id,
272 zebra_route_string(client->proto), rnh->node);
273 }
274 if (!listnode_lookup(rnh->client_list, client))
275 listnode_add(rnh->client_list, client);
276
277 /*
278 * We always need to respond with known information,
279 * currently multiple daemons expect this behavior
280 */
281 zebra_send_rnh_update(rnh, client, vrf_id, 0);
282 }
283
284 void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client)
285 {
286 if (IS_ZEBRA_DEBUG_NHT) {
287 struct vrf *vrf = vrf_lookup_by_id(rnh->vrf_id);
288
289 zlog_debug("Client %s unregisters for RNH %s(%u)%pRN",
290 zebra_route_string(client->proto), VRF_LOGNAME(vrf),
291 vrf->vrf_id, rnh->node);
292 }
293 listnode_delete(rnh->client_list, client);
294 zebra_delete_rnh(rnh);
295 }
296
297 /* XXX move this utility function elsewhere? */
298 static void addr2hostprefix(int af, const union g_addr *addr,
299 struct prefix *prefix)
300 {
301 switch (af) {
302 case AF_INET:
303 prefix->family = AF_INET;
304 prefix->prefixlen = IPV4_MAX_BITLEN;
305 prefix->u.prefix4 = addr->ipv4;
306 break;
307 case AF_INET6:
308 prefix->family = AF_INET6;
309 prefix->prefixlen = IPV6_MAX_BITLEN;
310 prefix->u.prefix6 = addr->ipv6;
311 break;
312 default:
313 memset(prefix, 0, sizeof(*prefix));
314 zlog_warn("%s: unknown address family %d", __func__, af);
315 break;
316 }
317 }
318
319 void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw,
320 bool *nht_exists)
321 {
322 struct prefix nh;
323 struct rnh *rnh;
324 bool exists;
325 struct zebra_vrf *zvrf;
326
327 *nht_exists = false;
328
329 zvrf = zebra_vrf_lookup_by_id(vrf_id);
330 if (!zvrf)
331 return;
332
333 addr2hostprefix(pw->af, &pw->nexthop, &nh);
334 rnh = zebra_add_rnh(&nh, vrf_id, SAFI_UNICAST, &exists);
335 if (!rnh)
336 return;
337
338 if (!listnode_lookup(rnh->zebra_pseudowire_list, pw)) {
339 listnode_add(rnh->zebra_pseudowire_list, pw);
340 pw->rnh = rnh;
341 zebra_evaluate_rnh(zvrf, family2afi(pw->af), 1, &nh,
342 SAFI_UNICAST);
343 } else
344 *nht_exists = true;
345 }
346
347 void zebra_deregister_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw)
348 {
349 struct rnh *rnh;
350
351 rnh = pw->rnh;
352 if (!rnh)
353 return;
354
355 listnode_delete(rnh->zebra_pseudowire_list, pw);
356 pw->rnh = NULL;
357
358 zebra_delete_rnh(rnh);
359 }
360
361 /* Clear the NEXTHOP_FLAG_RNH_FILTERED flags on all nexthops
362 */
363 static void zebra_rnh_clear_nexthop_rnh_filters(struct route_entry *re)
364 {
365 struct nexthop *nexthop;
366
367 if (re) {
368 for (nexthop = re->nhe->nhg.nexthop; nexthop;
369 nexthop = nexthop->next) {
370 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RNH_FILTERED);
371 }
372 }
373 }
374
375 /* Apply the NHT route-map for a client to the route (and nexthops)
376 * resolving a NH.
377 */
378 static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf,
379 struct route_node *prn,
380 struct route_entry *re, int proto)
381 {
382 int at_least_one = 0;
383 struct nexthop *nexthop;
384 route_map_result_t ret;
385
386 if (prn && re) {
387 for (nexthop = re->nhe->nhg.nexthop; nexthop;
388 nexthop = nexthop->next) {
389 ret = zebra_nht_route_map_check(
390 afi, proto, &prn->p, zvrf, re, nexthop);
391 if (ret != RMAP_DENYMATCH)
392 at_least_one++; /* at least one valid NH */
393 else {
394 SET_FLAG(nexthop->flags,
395 NEXTHOP_FLAG_RNH_FILTERED);
396 }
397 }
398 }
399 return (at_least_one);
400 }
401
402 /*
403 * Notify clients registered for this nexthop about a change.
404 */
405 static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi,
406 struct route_node *nrn,
407 struct rnh *rnh,
408 struct route_node *prn,
409 struct route_entry *re)
410 {
411 struct listnode *node;
412 struct zserv *client;
413 int num_resolving_nh;
414
415 if (IS_ZEBRA_DEBUG_NHT) {
416 if (prn && re) {
417 zlog_debug("%s(%u):%pRN: NH resolved over route %pRN",
418 VRF_LOGNAME(zvrf->vrf), zvrf->vrf->vrf_id,
419 nrn, prn);
420 } else
421 zlog_debug("%s(%u):%pRN: NH has become unresolved",
422 VRF_LOGNAME(zvrf->vrf), zvrf->vrf->vrf_id,
423 nrn);
424 }
425
426 for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
427 if (prn && re) {
428 /* Apply route-map for this client to route resolving
429 * this
430 * nexthop to see if it is filtered or not.
431 */
432 zebra_rnh_clear_nexthop_rnh_filters(re);
433 num_resolving_nh = zebra_rnh_apply_nht_rmap(
434 afi, zvrf, prn, re, client->proto);
435 if (num_resolving_nh)
436 rnh->filtered[client->proto] = 0;
437 else
438 rnh->filtered[client->proto] = 1;
439
440 if (IS_ZEBRA_DEBUG_NHT)
441 zlog_debug(
442 "%s(%u):%pRN: Notifying client %s about NH %s",
443 VRF_LOGNAME(zvrf->vrf),
444 zvrf->vrf->vrf_id, nrn,
445 zebra_route_string(client->proto),
446 num_resolving_nh
447 ? ""
448 : "(filtered by route-map)");
449 } else {
450 rnh->filtered[client->proto] = 0;
451 if (IS_ZEBRA_DEBUG_NHT)
452 zlog_debug(
453 "%s(%u):%pRN: Notifying client %s about NH (unreachable)",
454 VRF_LOGNAME(zvrf->vrf),
455 zvrf->vrf->vrf_id, nrn,
456 zebra_route_string(client->proto));
457 }
458
459 zebra_send_rnh_update(rnh, client, zvrf->vrf->vrf_id, 0);
460 }
461
462 if (re)
463 zebra_rnh_clear_nexthop_rnh_filters(re);
464 }
465
466 /*
467 * Utility to determine whether a candidate nexthop is useable. We make this
468 * check in a couple of places, so this is a single home for the logic we
469 * use.
470 */
471
472 static const int RNH_INVALID_NH_FLAGS = (NEXTHOP_FLAG_RECURSIVE |
473 NEXTHOP_FLAG_DUPLICATE |
474 NEXTHOP_FLAG_RNH_FILTERED);
475
476 bool rnh_nexthop_valid(const struct route_entry *re, const struct nexthop *nh)
477 {
478 return (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)
479 && CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)
480 && !CHECK_FLAG(nh->flags, RNH_INVALID_NH_FLAGS));
481 }
482
483 /*
484 * Determine whether an re's nexthops are valid for tracking.
485 */
486 static bool rnh_check_re_nexthops(const struct route_entry *re,
487 const struct rnh *rnh)
488 {
489 bool ret = false;
490 const struct nexthop *nexthop = NULL;
491
492 /* Check route's nexthops */
493 for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) {
494 if (rnh_nexthop_valid(re, nexthop))
495 break;
496 }
497
498 /* Check backup nexthops, if any. */
499 if (nexthop == NULL && re->nhe->backup_info &&
500 re->nhe->backup_info->nhe) {
501 for (ALL_NEXTHOPS(re->nhe->backup_info->nhe->nhg, nexthop)) {
502 if (rnh_nexthop_valid(re, nexthop))
503 break;
504 }
505 }
506
507 if (nexthop == NULL) {
508 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
509 zlog_debug(
510 " Route Entry %s no nexthops",
511 zebra_route_string(re->type));
512
513 goto done;
514 }
515
516 /* Some special checks if registration asked for them. */
517 if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)) {
518 if ((re->type == ZEBRA_ROUTE_CONNECT)
519 || (re->type == ZEBRA_ROUTE_STATIC))
520 ret = true;
521 if (re->type == ZEBRA_ROUTE_NHRP) {
522
523 for (nexthop = re->nhe->nhg.nexthop;
524 nexthop;
525 nexthop = nexthop->next)
526 if (nexthop->type == NEXTHOP_TYPE_IFINDEX)
527 break;
528 if (nexthop)
529 ret = true;
530 }
531 } else {
532 ret = true;
533 }
534
535 done:
536 return ret;
537 }
538
539 /*
540 * Determine appropriate route (route entry) resolving a tracked
541 * nexthop.
542 */
543 static struct route_entry *
544 zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi,
545 struct route_node *nrn, const struct rnh *rnh,
546 struct route_node **prn)
547 {
548 struct route_table *route_table;
549 struct route_node *rn;
550 struct route_entry *re;
551
552 *prn = NULL;
553
554 route_table = zvrf->table[afi][rnh->safi];
555 if (!route_table)
556 return NULL;
557
558 rn = route_node_match(route_table, &nrn->p);
559 if (!rn)
560 return NULL;
561
562 /* Unlock route node - we don't need to lock when walking the tree. */
563 route_unlock_node(rn);
564
565 /* While resolving nexthops, we may need to walk up the tree from the
566 * most-specific match. Do similar logic as in zebra_rib.c
567 */
568 while (rn) {
569 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
570 zlog_debug("%s: %s(%u):%pRN Possible Match to %pRN",
571 __func__, VRF_LOGNAME(zvrf->vrf),
572 rnh->vrf_id, rnh->node, rn);
573
574 /* Do not resolve over default route unless allowed &&
575 * match route to be exact if so specified
576 */
577 if (is_default_prefix(&rn->p)
578 && (!CHECK_FLAG(rnh->flags, ZEBRA_NHT_RESOLVE_VIA_DEFAULT)
579 && !rnh_resolve_via_default(zvrf, rn->p.family))) {
580 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
581 zlog_debug(
582 " Not allowed to resolve through default prefix: rnh->resolve_via_default: %u",
583 CHECK_FLAG(
584 rnh->flags,
585 ZEBRA_NHT_RESOLVE_VIA_DEFAULT));
586 return NULL;
587 }
588
589 /* Identify appropriate route entry. */
590 RNODE_FOREACH_RE (rn, re) {
591 if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) {
592 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
593 zlog_debug(
594 " Route Entry %s removed",
595 zebra_route_string(re->type));
596 continue;
597 }
598 if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) &&
599 !CHECK_FLAG(re->flags, ZEBRA_FLAG_FIB_OVERRIDE)) {
600 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
601 zlog_debug(
602 " Route Entry %s !selected",
603 zebra_route_string(re->type));
604 continue;
605 }
606
607 if (CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED)) {
608 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
609 zlog_debug(
610 " Route Entry %s queued",
611 zebra_route_string(re->type));
612 continue;
613 }
614
615 /* Just being SELECTED isn't quite enough - must
616 * have an installed nexthop to be useful.
617 */
618 if (rnh_check_re_nexthops(re, rnh))
619 break;
620 }
621
622 /* Route entry found, we're done; else, walk up the tree. */
623 if (re) {
624 *prn = rn;
625 return re;
626 } else {
627 /* Resolve the nexthop recursively by finding matching
628 * route with lower prefix length
629 */
630 rn = rn->parent;
631 }
632 }
633
634 return NULL;
635 }
636
637 static void zebra_rnh_process_pseudowires(vrf_id_t vrfid, struct rnh *rnh)
638 {
639 struct zebra_pw *pw;
640 struct listnode *node;
641
642 for (ALL_LIST_ELEMENTS_RO(rnh->zebra_pseudowire_list, node, pw))
643 zebra_pw_update(pw);
644 }
645
646 /*
647 * See if a tracked nexthop entry has undergone any change, and if so,
648 * take appropriate action; this involves notifying any clients and/or
649 * scheduling dependent static routes for processing.
650 */
651 static void zebra_rnh_eval_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi,
652 int force, struct route_node *nrn,
653 struct rnh *rnh,
654 struct route_node *prn,
655 struct route_entry *re)
656 {
657 int state_changed = 0;
658
659 /* If we're resolving over a different route, resolution has changed or
660 * the resolving route has some change (e.g., metric), there is a state
661 * change.
662 */
663 zebra_rnh_remove_from_routing_table(rnh);
664 if (!prefix_same(&rnh->resolved_route, prn ? &prn->p : NULL)) {
665 if (prn)
666 prefix_copy(&rnh->resolved_route, &prn->p);
667 else {
668 /*
669 * Just quickly store the family of the resolved
670 * route so that we can reset it in a second here
671 */
672 int family = rnh->resolved_route.family;
673
674 memset(&rnh->resolved_route, 0, sizeof(struct prefix));
675 rnh->resolved_route.family = family;
676 }
677
678 copy_state(rnh, re, nrn);
679 state_changed = 1;
680 } else if (compare_state(re, rnh->state)) {
681 copy_state(rnh, re, nrn);
682 state_changed = 1;
683 }
684 zebra_rnh_store_in_routing_table(rnh);
685
686 if (state_changed || force) {
687 /* NOTE: Use the "copy" of resolving route stored in 'rnh' i.e.,
688 * rnh->state.
689 */
690 /* Notify registered protocol clients. */
691 zebra_rnh_notify_protocol_clients(zvrf, afi, nrn, rnh, prn,
692 rnh->state);
693
694 /* Process pseudowires attached to this nexthop */
695 zebra_rnh_process_pseudowires(zvrf->vrf->vrf_id, rnh);
696 }
697 }
698
699 /* Evaluate one tracked entry */
700 static void zebra_rnh_evaluate_entry(struct zebra_vrf *zvrf, afi_t afi,
701 int force, struct route_node *nrn)
702 {
703 struct rnh *rnh;
704 struct route_entry *re;
705 struct route_node *prn;
706
707 if (IS_ZEBRA_DEBUG_NHT) {
708 zlog_debug("%s(%u):%pRN: Evaluate RNH, %s",
709 VRF_LOGNAME(zvrf->vrf), zvrf->vrf->vrf_id, nrn,
710 force ? "(force)" : "");
711 }
712
713 rnh = nrn->info;
714
715 /* Identify route entry (RE) resolving this tracked entry. */
716 re = zebra_rnh_resolve_nexthop_entry(zvrf, afi, nrn, rnh, &prn);
717
718 /* If the entry cannot be resolved and that is also the existing state,
719 * there is nothing further to do.
720 */
721 if (!re && rnh->state == NULL && !force)
722 return;
723
724 /* Process based on type of entry. */
725 zebra_rnh_eval_nexthop_entry(zvrf, afi, force, nrn, rnh, prn, re);
726 }
727
728 /*
729 * Clear the ROUTE_ENTRY_NEXTHOPS_CHANGED flag
730 * from the re entries.
731 *
732 * Please note we are doing this *after* we have
733 * notified the world about each nexthop as that
734 * we can have a situation where one re entry
735 * covers multiple nexthops we are interested in.
736 */
737 static void zebra_rnh_clear_nhc_flag(struct zebra_vrf *zvrf, afi_t afi,
738 struct route_node *nrn)
739 {
740 struct rnh *rnh;
741 struct route_entry *re;
742 struct route_node *prn;
743
744 rnh = nrn->info;
745
746 /* Identify route entry (RIB) resolving this tracked entry. */
747 re = zebra_rnh_resolve_nexthop_entry(zvrf, afi, nrn, rnh, &prn);
748
749 if (re)
750 UNSET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED);
751 }
752
753 /* Evaluate all tracked entries (nexthops or routes for import into BGP)
754 * of a particular VRF and address-family or a specific prefix.
755 */
756 void zebra_evaluate_rnh(struct zebra_vrf *zvrf, afi_t afi, int force,
757 const struct prefix *p, safi_t safi)
758 {
759 struct route_table *rnh_table;
760 struct route_node *nrn;
761
762 rnh_table = get_rnh_table(zvrf->vrf->vrf_id, afi, safi);
763 if (!rnh_table) // unexpected
764 return;
765
766 if (p) {
767 /* Evaluating a specific entry, make sure it exists. */
768 nrn = route_node_lookup(rnh_table, p);
769 if (nrn && nrn->info)
770 zebra_rnh_evaluate_entry(zvrf, afi, force, nrn);
771
772 if (nrn)
773 route_unlock_node(nrn);
774 } else {
775 /* Evaluate entire table. */
776 nrn = route_top(rnh_table);
777 while (nrn) {
778 if (nrn->info)
779 zebra_rnh_evaluate_entry(zvrf, afi, force, nrn);
780 nrn = route_next(nrn); /* this will also unlock nrn */
781 }
782 nrn = route_top(rnh_table);
783 while (nrn) {
784 if (nrn->info)
785 zebra_rnh_clear_nhc_flag(zvrf, afi, nrn);
786 nrn = route_next(nrn); /* this will also unlock nrn */
787 }
788 }
789 }
790
791 void zebra_print_rnh_table(vrf_id_t vrfid, afi_t afi, safi_t safi,
792 struct vty *vty, const struct prefix *p,
793 json_object *json)
794 {
795 struct route_table *table;
796 struct route_node *rn;
797
798 table = get_rnh_table(vrfid, afi, safi);
799 if (!table) {
800 if (IS_ZEBRA_DEBUG_NHT)
801 zlog_debug("print_rnhs: rnh table not found");
802 return;
803 }
804
805 for (rn = route_top(table); rn; rn = route_next(rn)) {
806 if (p && !prefix_match(&rn->p, p))
807 continue;
808
809 if (rn->info)
810 print_rnh(rn, vty, json);
811 }
812 }
813
814 /**
815 * free_state - free up the re structure associated with the rnh.
816 */
817 static void free_state(vrf_id_t vrf_id, struct route_entry *re,
818 struct route_node *rn)
819 {
820 if (!re)
821 return;
822
823 /* free RE and nexthops */
824 zebra_nhg_free(re->nhe);
825 XFREE(MTYPE_RE, re);
826 }
827
828 static void copy_state(struct rnh *rnh, const struct route_entry *re,
829 struct route_node *rn)
830 {
831 struct route_entry *state;
832
833 if (rnh->state) {
834 free_state(rnh->vrf_id, rnh->state, rn);
835 rnh->state = NULL;
836 }
837
838 if (!re)
839 return;
840
841 state = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
842 state->type = re->type;
843 state->distance = re->distance;
844 state->metric = re->metric;
845 state->vrf_id = re->vrf_id;
846 state->status = re->status;
847
848 state->nhe = zebra_nhe_copy(re->nhe, 0);
849
850 /* Copy the 'fib' nexthops also, if present - we want to capture
851 * the true installed nexthops.
852 */
853 if (re->fib_ng.nexthop)
854 nexthop_group_copy(&state->fib_ng, &re->fib_ng);
855 if (re->fib_backup_ng.nexthop)
856 nexthop_group_copy(&state->fib_backup_ng, &re->fib_backup_ng);
857
858 rnh->state = state;
859 }
860
861 /*
862 * Locate the next primary nexthop, used when comparing current rnh info with
863 * an updated route.
864 */
865 static struct nexthop *next_valid_primary_nh(struct route_entry *re,
866 struct nexthop *nh)
867 {
868 struct nexthop_group *nhg;
869 struct nexthop *bnh;
870 int i, idx;
871 bool default_path = true;
872
873 /* Fib backup ng present: some backups are installed,
874 * and we're configured for special handling if there are backups.
875 */
876 if (rnh_hide_backups && (re->fib_backup_ng.nexthop != NULL))
877 default_path = false;
878
879 /* Default path: no special handling, just using the 'installed'
880 * primary nexthops and the common validity test.
881 */
882 if (default_path) {
883 if (nh == NULL) {
884 nhg = rib_get_fib_nhg(re);
885 nh = nhg->nexthop;
886 } else
887 nh = nexthop_next(nh);
888
889 while (nh) {
890 if (rnh_nexthop_valid(re, nh))
891 break;
892 else
893 nh = nexthop_next(nh);
894 }
895
896 return nh;
897 }
898
899 /* Hide backup activation/switchover events.
900 *
901 * If we've had a switchover, an inactive primary won't be in
902 * the fib list at all - the 'fib' list could even be empty
903 * in the case where no primary is installed. But we want to consider
904 * those primaries "valid" if they have an activated backup nh.
905 *
906 * The logic is something like:
907 * if (!fib_nhg)
908 * // then all primaries are installed
909 * else
910 * for each primary in re nhg
911 * if in fib_nhg
912 * primary is installed
913 * else if a backup is installed
914 * primary counts as installed
915 * else
916 * primary !installed
917 */
918
919 /* Start with the first primary */
920 if (nh == NULL)
921 nh = re->nhe->nhg.nexthop;
922 else
923 nh = nexthop_next(nh);
924
925 while (nh) {
926
927 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
928 zlog_debug("%s: checking primary NH %pNHv",
929 __func__, nh);
930
931 /* If this nexthop is in the fib list, it's installed */
932 nhg = rib_get_fib_nhg(re);
933
934 for (bnh = nhg->nexthop; bnh; bnh = nexthop_next(bnh)) {
935 if (nexthop_cmp(nh, bnh) == 0)
936 break;
937 }
938
939 if (bnh != NULL) {
940 /* Found the match */
941 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
942 zlog_debug("%s: NH in fib list", __func__);
943 break;
944 }
945
946 /* Else if this nexthop's backup is installed, it counts */
947 nhg = rib_get_fib_backup_nhg(re);
948 bnh = nhg->nexthop;
949
950 for (idx = 0; bnh != NULL; idx++) {
951 /* If we find an active backup nh for this
952 * primary, we're done;
953 */
954 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
955 zlog_debug("%s: checking backup %pNHv [%d]",
956 __func__, bnh, idx);
957
958 if (!CHECK_FLAG(bnh->flags, NEXTHOP_FLAG_ACTIVE))
959 continue;
960
961 for (i = 0; i < nh->backup_num; i++) {
962 /* Found a matching activated backup nh */
963 if (nh->backup_idx[i] == idx) {
964 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
965 zlog_debug("%s: backup %d activated",
966 __func__, i);
967
968 goto done;
969 }
970 }
971
972 /* Note that we're not recursing here if the
973 * backups are recursive: the primary's index is
974 * only valid in the top-level backup list.
975 */
976 bnh = bnh->next;
977 }
978
979 /* Try the next primary nexthop */
980 nh = nexthop_next(nh);
981 }
982
983 done:
984
985 return nh;
986 }
987
988 /*
989 * Compare two route_entries' nexthops. Account for backup nexthops
990 * and for the 'fib' nexthop lists, if present.
991 */
992 static bool compare_valid_nexthops(struct route_entry *r1,
993 struct route_entry *r2)
994 {
995 bool matched_p = false;
996 struct nexthop_group *nhg1, *nhg2;
997 struct nexthop *nh1, *nh2;
998
999 /* Start with the primary nexthops */
1000
1001 nh1 = next_valid_primary_nh(r1, NULL);
1002 nh2 = next_valid_primary_nh(r2, NULL);
1003
1004 while (1) {
1005 /* Find any differences in the nexthop lists */
1006
1007 if (nh1 && nh2) {
1008 /* Any difference is a no-match */
1009 if (nexthop_cmp(nh1, nh2) != 0) {
1010 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
1011 zlog_debug("%s: nh1: %pNHv, nh2: %pNHv differ",
1012 __func__, nh1, nh2);
1013 goto done;
1014 }
1015
1016 } else if (nh1 || nh2) {
1017 /* One list has more valid nexthops than the other */
1018 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
1019 zlog_debug("%s: nh1 %s, nh2 %s", __func__,
1020 nh1 ? "non-NULL" : "NULL",
1021 nh2 ? "non-NULL" : "NULL");
1022 goto done;
1023 } else
1024 break; /* Done with both lists */
1025
1026 nh1 = next_valid_primary_nh(r1, nh1);
1027 nh2 = next_valid_primary_nh(r2, nh2);
1028 }
1029
1030 /* If configured, don't compare installed backup state - we've
1031 * accounted for that with the primaries above.
1032 *
1033 * But we do want to compare the routes' backup info,
1034 * in case the owning route has changed the backups -
1035 * that change we do want to report.
1036 */
1037 if (rnh_hide_backups) {
1038 uint32_t hash1 = 0, hash2 = 0;
1039
1040 if (r1->nhe->backup_info)
1041 hash1 = nexthop_group_hash(
1042 &r1->nhe->backup_info->nhe->nhg);
1043
1044 if (r2->nhe->backup_info)
1045 hash2 = nexthop_group_hash(
1046 &r2->nhe->backup_info->nhe->nhg);
1047
1048 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
1049 zlog_debug("%s: backup hash1 %#x, hash2 %#x",
1050 __func__, hash1, hash2);
1051
1052 if (hash1 != hash2)
1053 goto done;
1054 else
1055 goto finished;
1056 }
1057
1058 /* The test for the backups is slightly different: the only installed
1059 * backups will be in the 'fib' list.
1060 */
1061 nhg1 = rib_get_fib_backup_nhg(r1);
1062 nhg2 = rib_get_fib_backup_nhg(r2);
1063
1064 nh1 = nhg1->nexthop;
1065 nh2 = nhg2->nexthop;
1066
1067 while (1) {
1068 /* Find each backup list's next valid nexthop */
1069 while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1))
1070 nh1 = nexthop_next(nh1);
1071
1072 while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2))
1073 nh2 = nexthop_next(nh2);
1074
1075 if (nh1 && nh2) {
1076 /* Any difference is a no-match */
1077 if (nexthop_cmp(nh1, nh2) != 0) {
1078 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
1079 zlog_debug("%s: backup nh1: %pNHv, nh2: %pNHv differ",
1080 __func__, nh1, nh2);
1081 goto done;
1082 }
1083
1084 nh1 = nexthop_next(nh1);
1085 nh2 = nexthop_next(nh2);
1086 } else if (nh1 || nh2) {
1087 /* One list has more valid nexthops than the other */
1088 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
1089 zlog_debug("%s: backup nh1 %s, nh2 %s",
1090 __func__,
1091 nh1 ? "non-NULL" : "NULL",
1092 nh2 ? "non-NULL" : "NULL");
1093 goto done;
1094 } else
1095 break; /* Done with both lists */
1096 }
1097
1098 finished:
1099
1100 /* Well, it's a match */
1101 matched_p = true;
1102
1103 done:
1104
1105 if (IS_ZEBRA_DEBUG_NHT_DETAILED)
1106 zlog_debug("%s: %smatched",
1107 __func__, (matched_p ? "" : "NOT "));
1108
1109 return matched_p;
1110 }
1111
1112 /* Returns 'false' if no difference. */
1113 static bool compare_state(struct route_entry *r1,
1114 struct route_entry *r2)
1115 {
1116 if (!r1 && !r2)
1117 return false;
1118
1119 if ((!r1 && r2) || (r1 && !r2))
1120 return true;
1121
1122 if (r1->distance != r2->distance)
1123 return true;
1124
1125 if (r1->metric != r2->metric)
1126 return true;
1127
1128 if (!compare_valid_nexthops(r1, r2))
1129 return true;
1130
1131 return false;
1132 }
1133
1134 int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client,
1135 vrf_id_t vrf_id, uint32_t srte_color)
1136 {
1137 struct stream *s = NULL;
1138 struct route_entry *re;
1139 unsigned long nump;
1140 uint8_t num;
1141 struct nexthop *nh;
1142 struct route_node *rn;
1143 int ret;
1144 uint32_t message = 0;
1145
1146 rn = rnh->node;
1147 re = rnh->state;
1148
1149 /* Get output stream. */
1150 s = stream_new(ZEBRA_MAX_PACKET_SIZ);
1151
1152 zclient_create_header(s, ZEBRA_NEXTHOP_UPDATE, vrf_id);
1153
1154 /* Message flags. */
1155 if (srte_color)
1156 SET_FLAG(message, ZAPI_MESSAGE_SRTE);
1157 stream_putl(s, message);
1158
1159 /*
1160 * Put what we were told to match against
1161 */
1162 stream_putw(s, rnh->safi);
1163 stream_putw(s, rn->p.family);
1164 stream_putc(s, rn->p.prefixlen);
1165 switch (rn->p.family) {
1166 case AF_INET:
1167 stream_put_in_addr(s, &rn->p.u.prefix4);
1168 break;
1169 case AF_INET6:
1170 stream_put(s, &rn->p.u.prefix6, IPV6_MAX_BYTELEN);
1171 break;
1172 default:
1173 flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY,
1174 "%s: Unknown family (%d) notification attempted",
1175 __func__, rn->p.family);
1176 goto failure;
1177 }
1178
1179 /*
1180 * What we matched against
1181 */
1182 stream_putw(s, rnh->resolved_route.family);
1183 stream_putc(s, rnh->resolved_route.prefixlen);
1184 switch (rnh->resolved_route.family) {
1185 case AF_INET:
1186 stream_put_in_addr(s, &rnh->resolved_route.u.prefix4);
1187 break;
1188 case AF_INET6:
1189 stream_put(s, &rnh->resolved_route.u.prefix6, IPV6_MAX_BYTELEN);
1190 break;
1191 default:
1192 flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY,
1193 "%s: Unknown family (%d) notification attempted",
1194 __func__, rn->p.family);
1195 goto failure;
1196 }
1197
1198 if (srte_color)
1199 stream_putl(s, srte_color);
1200
1201 if (re) {
1202 struct zapi_nexthop znh;
1203 struct nexthop_group *nhg;
1204
1205 stream_putc(s, re->type);
1206 stream_putw(s, re->instance);
1207 stream_putc(s, re->distance);
1208 stream_putl(s, re->metric);
1209 num = 0;
1210 nump = stream_get_endp(s);
1211 stream_putc(s, 0);
1212
1213 nhg = rib_get_fib_nhg(re);
1214 for (ALL_NEXTHOPS_PTR(nhg, nh))
1215 if (rnh_nexthop_valid(re, nh)) {
1216 zapi_nexthop_from_nexthop(&znh, nh);
1217 ret = zapi_nexthop_encode(s, &znh, 0, message);
1218 if (ret < 0)
1219 goto failure;
1220
1221 num++;
1222 }
1223
1224 nhg = rib_get_fib_backup_nhg(re);
1225 if (nhg) {
1226 for (ALL_NEXTHOPS_PTR(nhg, nh))
1227 if (rnh_nexthop_valid(re, nh)) {
1228 zapi_nexthop_from_nexthop(&znh, nh);
1229 ret = zapi_nexthop_encode(
1230 s, &znh, 0 /* flags */,
1231 0 /* message */);
1232 if (ret < 0)
1233 goto failure;
1234
1235 num++;
1236 }
1237 }
1238
1239 stream_putc_at(s, nump, num);
1240 } else {
1241 stream_putc(s, 0); // type
1242 stream_putw(s, 0); // instance
1243 stream_putc(s, 0); // distance
1244 stream_putl(s, 0); // metric
1245 stream_putc(s, 0); // nexthops
1246 }
1247 stream_putw_at(s, 0, stream_get_endp(s));
1248
1249 client->nh_last_upd_time = monotime(NULL);
1250 return zserv_send_message(client, s);
1251
1252 failure:
1253
1254 stream_free(s);
1255 return -1;
1256 }
1257
1258
1259 /*
1260 * Render a nexthop into a json object; the caller allocates and owns
1261 * the json object memory.
1262 */
1263 void show_nexthop_json_helper(json_object *json_nexthop,
1264 const struct nexthop *nexthop,
1265 const struct route_entry *re)
1266 {
1267 json_object *json_labels = NULL;
1268 json_object *json_backups = NULL;
1269 json_object *json_seg6local = NULL;
1270 json_object *json_seg6 = NULL;
1271 int i;
1272
1273 json_object_int_add(json_nexthop, "flags", nexthop->flags);
1274
1275 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
1276 json_object_boolean_true_add(json_nexthop, "duplicate");
1277
1278 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
1279 json_object_boolean_true_add(json_nexthop, "fib");
1280
1281 switch (nexthop->type) {
1282 case NEXTHOP_TYPE_IPV4:
1283 case NEXTHOP_TYPE_IPV4_IFINDEX:
1284 json_object_string_addf(json_nexthop, "ip", "%pI4",
1285 &nexthop->gate.ipv4);
1286 json_object_string_add(json_nexthop, "afi", "ipv4");
1287
1288 if (nexthop->ifindex) {
1289 json_object_int_add(json_nexthop, "interfaceIndex",
1290 nexthop->ifindex);
1291 json_object_string_add(json_nexthop, "interfaceName",
1292 ifindex2ifname(nexthop->ifindex,
1293 nexthop->vrf_id));
1294 }
1295 break;
1296 case NEXTHOP_TYPE_IPV6:
1297 case NEXTHOP_TYPE_IPV6_IFINDEX:
1298 json_object_string_addf(json_nexthop, "ip", "%pI6",
1299 &nexthop->gate.ipv6);
1300 json_object_string_add(json_nexthop, "afi", "ipv6");
1301
1302 if (nexthop->ifindex) {
1303 json_object_int_add(json_nexthop, "interfaceIndex",
1304 nexthop->ifindex);
1305 json_object_string_add(json_nexthop, "interfaceName",
1306 ifindex2ifname(nexthop->ifindex,
1307 nexthop->vrf_id));
1308 }
1309 break;
1310
1311 case NEXTHOP_TYPE_IFINDEX:
1312 json_object_boolean_true_add(json_nexthop, "directlyConnected");
1313 json_object_int_add(json_nexthop, "interfaceIndex",
1314 nexthop->ifindex);
1315 json_object_string_add(
1316 json_nexthop, "interfaceName",
1317 ifindex2ifname(nexthop->ifindex, nexthop->vrf_id));
1318 break;
1319 case NEXTHOP_TYPE_BLACKHOLE:
1320 json_object_boolean_true_add(json_nexthop, "unreachable");
1321 switch (nexthop->bh_type) {
1322 case BLACKHOLE_REJECT:
1323 json_object_boolean_true_add(json_nexthop, "reject");
1324 break;
1325 case BLACKHOLE_ADMINPROHIB:
1326 json_object_boolean_true_add(json_nexthop,
1327 "adminProhibited");
1328 break;
1329 case BLACKHOLE_NULL:
1330 json_object_boolean_true_add(json_nexthop, "blackhole");
1331 break;
1332 case BLACKHOLE_UNSPEC:
1333 break;
1334 }
1335 break;
1336 }
1337
1338 /* This nexthop is a resolver for the parent nexthop.
1339 * Set resolver flag for better clarity and delimiter
1340 * in flat list of nexthops in json.
1341 */
1342 if (nexthop->rparent)
1343 json_object_boolean_true_add(json_nexthop, "resolver");
1344
1345 if ((re == NULL || (nexthop->vrf_id != re->vrf_id)))
1346 json_object_string_add(json_nexthop, "vrf",
1347 vrf_id_to_name(nexthop->vrf_id));
1348
1349 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
1350 json_object_boolean_true_add(json_nexthop, "duplicate");
1351
1352 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
1353 json_object_boolean_true_add(json_nexthop, "active");
1354
1355 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
1356 json_object_boolean_true_add(json_nexthop, "onLink");
1357
1358 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN))
1359 json_object_boolean_true_add(json_nexthop, "linkDown");
1360
1361 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
1362 json_object_boolean_true_add(json_nexthop, "recursive");
1363
1364 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1365 json_backups = json_object_new_array();
1366 for (i = 0; i < nexthop->backup_num; i++) {
1367 json_object_array_add(
1368 json_backups,
1369 json_object_new_int(nexthop->backup_idx[i]));
1370 }
1371
1372 json_object_object_add(json_nexthop, "backupIndex",
1373 json_backups);
1374 }
1375
1376 switch (nexthop->type) {
1377 case NEXTHOP_TYPE_IPV4:
1378 case NEXTHOP_TYPE_IPV4_IFINDEX:
1379 if (nexthop->src.ipv4.s_addr)
1380 json_object_string_addf(json_nexthop, "source", "%pI4",
1381 &nexthop->src.ipv4);
1382 break;
1383 case NEXTHOP_TYPE_IPV6:
1384 case NEXTHOP_TYPE_IPV6_IFINDEX:
1385 if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any))
1386 json_object_string_addf(json_nexthop, "source", "%pI6",
1387 &nexthop->src.ipv6);
1388 break;
1389 case NEXTHOP_TYPE_IFINDEX:
1390 case NEXTHOP_TYPE_BLACKHOLE:
1391 break;
1392 }
1393
1394 if (nexthop->nh_label && nexthop->nh_label->num_labels) {
1395 json_labels = json_object_new_array();
1396
1397 for (int label_index = 0;
1398 label_index < nexthop->nh_label->num_labels; label_index++)
1399 json_object_array_add(
1400 json_labels,
1401 json_object_new_int((
1402 (nexthop->nh_label_type ==
1403 ZEBRA_LSP_EVPN)
1404 ? label2vni(
1405 &nexthop->nh_label->label
1406 [label_index])
1407 : nexthop->nh_label->label
1408 [label_index])));
1409
1410 json_object_object_add(json_nexthop, "labels", json_labels);
1411 }
1412
1413 if (nexthop->weight)
1414 json_object_int_add(json_nexthop, "weight", nexthop->weight);
1415
1416 if (nexthop->srte_color)
1417 json_object_int_add(json_nexthop, "srteColor",
1418 nexthop->srte_color);
1419
1420 if (nexthop->nh_srv6) {
1421 json_seg6local = json_object_new_object();
1422 json_object_string_add(
1423 json_seg6local, "action",
1424 seg6local_action2str(
1425 nexthop->nh_srv6->seg6local_action));
1426 json_object_object_add(json_nexthop, "seg6local",
1427 json_seg6local);
1428
1429 json_seg6 = json_object_new_object();
1430 json_object_string_addf(json_seg6, "segs", "%pI6",
1431 &nexthop->nh_srv6->seg6_segs);
1432 json_object_object_add(json_nexthop, "seg6", json_seg6);
1433 }
1434 }
1435
1436 /*
1437 * Helper for nexthop output, used in the 'show ip route' path
1438 */
1439 void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re,
1440 const struct nexthop *nexthop)
1441 {
1442 char buf[MPLS_LABEL_STRLEN];
1443 int i;
1444
1445 switch (nexthop->type) {
1446 case NEXTHOP_TYPE_IPV4:
1447 case NEXTHOP_TYPE_IPV4_IFINDEX:
1448 vty_out(vty, " via %pI4", &nexthop->gate.ipv4);
1449 if (nexthop->ifindex)
1450 vty_out(vty, ", %s",
1451 ifindex2ifname(nexthop->ifindex,
1452 nexthop->vrf_id));
1453 break;
1454 case NEXTHOP_TYPE_IPV6:
1455 case NEXTHOP_TYPE_IPV6_IFINDEX:
1456 vty_out(vty, " via %s",
1457 inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf,
1458 sizeof(buf)));
1459 if (nexthop->ifindex)
1460 vty_out(vty, ", %s",
1461 ifindex2ifname(nexthop->ifindex,
1462 nexthop->vrf_id));
1463 break;
1464
1465 case NEXTHOP_TYPE_IFINDEX:
1466 vty_out(vty, " is directly connected, %s",
1467 ifindex2ifname(nexthop->ifindex, nexthop->vrf_id));
1468 break;
1469 case NEXTHOP_TYPE_BLACKHOLE:
1470 vty_out(vty, " unreachable");
1471 switch (nexthop->bh_type) {
1472 case BLACKHOLE_REJECT:
1473 vty_out(vty, " (ICMP unreachable)");
1474 break;
1475 case BLACKHOLE_ADMINPROHIB:
1476 vty_out(vty, " (ICMP admin-prohibited)");
1477 break;
1478 case BLACKHOLE_NULL:
1479 vty_out(vty, " (blackhole)");
1480 break;
1481 case BLACKHOLE_UNSPEC:
1482 break;
1483 }
1484 break;
1485 }
1486
1487 if ((re == NULL || (nexthop->vrf_id != re->vrf_id)))
1488 vty_out(vty, " (vrf %s)", vrf_id_to_name(nexthop->vrf_id));
1489
1490 if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
1491 vty_out(vty, " inactive");
1492
1493 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
1494 vty_out(vty, " onlink");
1495
1496 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN))
1497 vty_out(vty, " linkdown");
1498
1499 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
1500 vty_out(vty, " (recursive)");
1501
1502 switch (nexthop->type) {
1503 case NEXTHOP_TYPE_IPV4:
1504 case NEXTHOP_TYPE_IPV4_IFINDEX:
1505 if (nexthop->src.ipv4.s_addr) {
1506 vty_out(vty, ", src %pI4", &nexthop->src.ipv4);
1507 /* SR-TE information */
1508 if (nexthop->srte_color)
1509 vty_out(vty, ", SR-TE color %u",
1510 nexthop->srte_color);
1511 }
1512 break;
1513 case NEXTHOP_TYPE_IPV6:
1514 case NEXTHOP_TYPE_IPV6_IFINDEX:
1515 if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any))
1516 vty_out(vty, ", src %pI6", &nexthop->src.ipv6);
1517 break;
1518 case NEXTHOP_TYPE_IFINDEX:
1519 case NEXTHOP_TYPE_BLACKHOLE:
1520 break;
1521 }
1522
1523 /* Label information */
1524 if (nexthop->nh_label && nexthop->nh_label->num_labels) {
1525 vty_out(vty, ", label %s",
1526 mpls_label2str(nexthop->nh_label->num_labels,
1527 nexthop->nh_label->label, buf,
1528 sizeof(buf), nexthop->nh_label_type, 1));
1529 }
1530
1531 if (nexthop->nh_srv6) {
1532 seg6local_context2str(buf, sizeof(buf),
1533 &nexthop->nh_srv6->seg6local_ctx,
1534 nexthop->nh_srv6->seg6local_action);
1535 if (nexthop->nh_srv6->seg6local_action !=
1536 ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
1537 vty_out(vty, ", seg6local %s %s",
1538 seg6local_action2str(
1539 nexthop->nh_srv6->seg6local_action),
1540 buf);
1541 if (IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs, &in6addr_any))
1542 vty_out(vty, ", seg6 %pI6",
1543 &nexthop->nh_srv6->seg6_segs);
1544 }
1545
1546 if (nexthop->weight)
1547 vty_out(vty, ", weight %u", nexthop->weight);
1548
1549 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1550 vty_out(vty, ", backup %d", nexthop->backup_idx[0]);
1551
1552 for (i = 1; i < nexthop->backup_num; i++)
1553 vty_out(vty, ",%d", nexthop->backup_idx[i]);
1554 }
1555 }
1556
1557 static void print_rnh(struct route_node *rn, struct vty *vty, json_object *json)
1558 {
1559 struct rnh *rnh;
1560 struct nexthop *nexthop;
1561 struct listnode *node;
1562 struct zserv *client;
1563 char buf[BUFSIZ];
1564 json_object *json_nht = NULL;
1565 json_object *json_client_array = NULL;
1566 json_object *json_client = NULL;
1567 json_object *json_nexthop_array = NULL;
1568 json_object *json_nexthop = NULL;
1569
1570 rnh = rn->info;
1571
1572 if (json) {
1573 json_nht = json_object_new_object();
1574 json_nexthop_array = json_object_new_array();
1575 json_client_array = json_object_new_array();
1576
1577 json_object_object_add(
1578 json,
1579 inet_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ),
1580 json_nht);
1581 json_object_boolean_add(
1582 json_nht, "nhtConnected",
1583 CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED));
1584 json_object_object_add(json_nht, "clientList",
1585 json_client_array);
1586 json_object_object_add(json_nht, "nexthops",
1587 json_nexthop_array);
1588 } else {
1589 vty_out(vty, "%s%s\n",
1590 inet_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ),
1591 CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)
1592 ? "(Connected)"
1593 : "");
1594 }
1595
1596 if (rnh->state) {
1597 if (json)
1598 json_object_string_add(
1599 json_nht, "resolvedProtocol",
1600 zebra_route_string(rnh->state->type));
1601 else
1602 vty_out(vty, " resolved via %s\n",
1603 zebra_route_string(rnh->state->type));
1604
1605 for (nexthop = rnh->state->nhe->nhg.nexthop; nexthop;
1606 nexthop = nexthop->next) {
1607 if (json) {
1608 json_nexthop = json_object_new_object();
1609 json_object_array_add(json_nexthop_array,
1610 json_nexthop);
1611 show_nexthop_json_helper(json_nexthop, nexthop,
1612 NULL);
1613 } else {
1614 show_route_nexthop_helper(vty, NULL, nexthop);
1615 vty_out(vty, "\n");
1616 }
1617 }
1618 } else {
1619 if (json)
1620 json_object_boolean_add(
1621 json_nht, "unresolved",
1622 CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED));
1623 else
1624 vty_out(vty, " unresolved%s\n",
1625 CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)
1626 ? "(Connected)"
1627 : "");
1628 }
1629
1630 if (!json)
1631 vty_out(vty, " Client list:");
1632
1633 for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
1634 if (json) {
1635 json_client = json_object_new_object();
1636 json_object_array_add(json_client_array, json_client);
1637
1638 json_object_string_add(
1639 json_client, "protocol",
1640 zebra_route_string(client->proto));
1641 json_object_int_add(json_client, "socket",
1642 client->sock);
1643 json_object_string_add(json_client, "protocolFiltered",
1644 (rnh->filtered[client->proto]
1645 ? "(filtered)"
1646 : "none"));
1647 } else {
1648 vty_out(vty, " %s(fd %d)%s",
1649 zebra_route_string(client->proto), client->sock,
1650 rnh->filtered[client->proto] ? "(filtered)"
1651 : "");
1652 }
1653 }
1654
1655 if (!list_isempty(rnh->zebra_pseudowire_list)) {
1656 if (json)
1657 json_object_boolean_true_add(json_nht,
1658 "zebraPseudowires");
1659 else
1660 vty_out(vty, " zebra[pseudowires]");
1661 }
1662
1663 if (!json)
1664 vty_out(vty, "\n");
1665 }
1666
1667 static int zebra_cleanup_rnh_client(vrf_id_t vrf_id, afi_t afi, safi_t safi,
1668 struct zserv *client)
1669 {
1670 struct route_table *ntable;
1671 struct route_node *nrn;
1672 struct rnh *rnh;
1673
1674 if (IS_ZEBRA_DEBUG_NHT) {
1675 struct vrf *vrf = vrf_lookup_by_id(vrf_id);
1676
1677 zlog_debug("%s(%u): Client %s RNH cleanup for family %s",
1678 VRF_LOGNAME(vrf), vrf_id,
1679 zebra_route_string(client->proto), afi2str(afi));
1680 }
1681
1682 ntable = get_rnh_table(vrf_id, afi, safi);
1683 if (!ntable) {
1684 zlog_debug("cleanup_rnh_client: rnh table not found");
1685 return -1;
1686 }
1687
1688 for (nrn = route_top(ntable); nrn; nrn = route_next(nrn)) {
1689 if (!nrn->info)
1690 continue;
1691
1692 rnh = nrn->info;
1693 zebra_remove_rnh_client(rnh, client);
1694 }
1695 return 1;
1696 }
1697
1698 /* Cleanup registered nexthops (across VRFs) upon client disconnect. */
1699 static int zebra_client_cleanup_rnh(struct zserv *client)
1700 {
1701 struct vrf *vrf;
1702 struct zebra_vrf *zvrf;
1703
1704 RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
1705 zvrf = vrf->info;
1706 if (zvrf) {
1707 zebra_cleanup_rnh_client(zvrf_id(zvrf), AFI_IP,
1708 SAFI_UNICAST, client);
1709 zebra_cleanup_rnh_client(zvrf_id(zvrf), AFI_IP,
1710 SAFI_MULTICAST, client);
1711 zebra_cleanup_rnh_client(zvrf_id(zvrf), AFI_IP6,
1712 SAFI_UNICAST, client);
1713 zebra_cleanup_rnh_client(zvrf_id(zvrf), AFI_IP6,
1714 SAFI_MULTICAST, client);
1715 }
1716 }
1717
1718 return 0;
1719 }
1720
1721 int rnh_resolve_via_default(struct zebra_vrf *zvrf, int family)
1722 {
1723 if (((family == AF_INET) && zvrf->zebra_rnh_ip_default_route)
1724 || ((family == AF_INET6) && zvrf->zebra_rnh_ipv6_default_route))
1725 return 1;
1726 else
1727 return 0;
1728 }
1729
1730 /*
1731 * UI control to avoid notifications if backup nexthop status changes
1732 */
1733 void rnh_set_hide_backups(bool hide_p)
1734 {
1735 rnh_hide_backups = hide_p;
1736 }
1737
1738 bool rnh_get_hide_backups(void)
1739 {
1740 return rnh_hide_backups;
1741 }