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