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