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