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