]>
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" |
a815b788 | 49 | #include "zebra/interface.h" |
4a1ab8e4 | 50 | #include "zebra/zebra_memory.h" |
43e52561 | 51 | #include "zebra/zebra_errors.h" |
fb018d25 | 52 | |
d62a17ae | 53 | static void free_state(vrf_id_t vrf_id, struct route_entry *re, |
54 | struct route_node *rn); | |
f0f77c9a | 55 | static void copy_state(struct rnh *rnh, struct route_entry *re, |
078430f6 | 56 | struct route_node *rn); |
d62a17ae | 57 | #define lookup_rnh_table(v, f) \ |
58 | ({ \ | |
59 | struct zebra_vrf *zvrf; \ | |
60 | struct route_table *t = NULL; \ | |
61 | zvrf = zebra_vrf_lookup_by_id(v); \ | |
62 | if (zvrf) \ | |
63 | t = zvrf->rnh_table[family2afi(f)]; \ | |
64 | t; \ | |
65 | }) | |
b72ede27 | 66 | |
f0f77c9a | 67 | static int compare_state(struct route_entry *r1, struct route_entry *r2); |
7076bb2f | 68 | static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, |
d62a17ae | 69 | vrf_id_t vrf_id); |
fb018d25 | 70 | static void print_rnh(struct route_node *rn, struct vty *vty); |
453844ab | 71 | static int zebra_client_cleanup_rnh(struct zserv *client); |
fb018d25 | 72 | |
a50b580a DS |
73 | int zebra_rnh_ip_default_route = 0; |
74 | int zebra_rnh_ipv6_default_route = 0; | |
75 | ||
453844ab QY |
76 | void zebra_rnh_init(void) |
77 | { | |
21ccc0cf | 78 | hook_register(zserv_client_close, zebra_client_cleanup_rnh); |
453844ab QY |
79 | } |
80 | ||
b72ede27 | 81 | static inline struct route_table *get_rnh_table(vrf_id_t vrfid, int family, |
078430f6 DS |
82 | rnh_type_t type) |
83 | { | |
d62a17ae | 84 | struct zebra_vrf *zvrf; |
85 | struct route_table *t = NULL; | |
86 | ||
87 | zvrf = zebra_vrf_lookup_by_id(vrfid); | |
88 | if (zvrf) | |
89 | switch (type) { | |
90 | case RNH_NEXTHOP_TYPE: | |
91 | t = zvrf->rnh_table[family2afi(family)]; | |
92 | break; | |
93 | case RNH_IMPORT_CHECK_TYPE: | |
94 | t = zvrf->import_check_table[family2afi(family)]; | |
95 | break; | |
96 | } | |
97 | ||
98 | return t; | |
078430f6 DS |
99 | } |
100 | ||
d62a17ae | 101 | char *rnh_str(struct rnh *rnh, char *buf, int size) |
fb018d25 | 102 | { |
d62a17ae | 103 | prefix2str(&(rnh->node->p), buf, size); |
104 | return buf; | |
fb018d25 DS |
105 | } |
106 | ||
1d30d1f4 DS |
107 | struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type, |
108 | bool *exists) | |
fb018d25 | 109 | { |
d62a17ae | 110 | struct route_table *table; |
111 | struct route_node *rn; | |
112 | struct rnh *rnh = NULL; | |
113 | char buf[PREFIX2STR_BUFFER]; | |
114 | ||
115 | if (IS_ZEBRA_DEBUG_NHT) { | |
116 | prefix2str(p, buf, sizeof(buf)); | |
117 | zlog_debug("%u: Add RNH %s type %d", vrfid, buf, type); | |
118 | } | |
119 | table = get_rnh_table(vrfid, PREFIX_FAMILY(p), type); | |
120 | if (!table) { | |
121 | prefix2str(p, buf, sizeof(buf)); | |
e914ccbe | 122 | flog_warn(EC_ZEBRA_RNH_NO_TABLE, |
9df414fe | 123 | "%u: Add RNH %s type %d - table not found", vrfid, |
d62a17ae | 124 | buf, type); |
1d30d1f4 | 125 | exists = false; |
d62a17ae | 126 | return NULL; |
127 | } | |
128 | ||
129 | /* Make it sure prefixlen is applied to the prefix. */ | |
130 | apply_mask(p); | |
131 | ||
132 | /* Lookup (or add) route node.*/ | |
133 | rn = route_node_get(table, p); | |
134 | ||
135 | if (!rn->info) { | |
136 | rnh = XCALLOC(MTYPE_RNH, sizeof(struct rnh)); | |
137 | rnh->client_list = list_new(); | |
138 | rnh->vrf_id = vrfid; | |
731a75fe | 139 | rnh->zebra_pseudowire_list = list_new(); |
d62a17ae | 140 | route_lock_node(rn); |
141 | rn->info = rnh; | |
142 | rnh->node = rn; | |
1d30d1f4 DS |
143 | *exists = false; |
144 | } else | |
145 | *exists = true; | |
d62a17ae | 146 | |
147 | route_unlock_node(rn); | |
148 | return (rn->info); | |
fb018d25 DS |
149 | } |
150 | ||
d62a17ae | 151 | struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type) |
fb018d25 | 152 | { |
d62a17ae | 153 | struct route_table *table; |
154 | struct route_node *rn; | |
fb018d25 | 155 | |
d62a17ae | 156 | table = get_rnh_table(vrfid, PREFIX_FAMILY(p), type); |
157 | if (!table) | |
158 | return NULL; | |
fb018d25 | 159 | |
d62a17ae | 160 | /* Make it sure prefixlen is applied to the prefix. */ |
161 | apply_mask(p); | |
fb018d25 | 162 | |
d62a17ae | 163 | /* Lookup route node.*/ |
164 | rn = route_node_lookup(table, p); | |
165 | if (!rn) | |
166 | return NULL; | |
fb018d25 | 167 | |
d62a17ae | 168 | route_unlock_node(rn); |
169 | return (rn->info); | |
fb018d25 DS |
170 | } |
171 | ||
d62a17ae | 172 | void zebra_free_rnh(struct rnh *rnh) |
5a8dfcd8 | 173 | { |
d62a17ae | 174 | rnh->flags |= ZEBRA_NHT_DELETED; |
6a154c88 DL |
175 | list_delete(&rnh->client_list); |
176 | list_delete(&rnh->zebra_pseudowire_list); | |
d62a17ae | 177 | free_state(rnh->vrf_id, rnh->state, rnh->node); |
178 | XFREE(MTYPE_RNH, rnh); | |
5a8dfcd8 RW |
179 | } |
180 | ||
d62a17ae | 181 | void zebra_delete_rnh(struct rnh *rnh, rnh_type_t type) |
fb018d25 | 182 | { |
d62a17ae | 183 | struct route_node *rn; |
fb018d25 | 184 | |
d62a17ae | 185 | if (!rnh || (rnh->flags & ZEBRA_NHT_DELETED) || !(rn = rnh->node)) |
186 | return; | |
fb018d25 | 187 | |
d62a17ae | 188 | if (IS_ZEBRA_DEBUG_NHT) { |
189 | char buf[PREFIX2STR_BUFFER]; | |
190 | zlog_debug("%u: Del RNH %s type %d", rnh->vrf_id, | |
191 | rnh_str(rnh, buf, sizeof(buf)), type); | |
192 | } | |
fb018d25 | 193 | |
d62a17ae | 194 | zebra_free_rnh(rnh); |
195 | rn->info = NULL; | |
196 | route_unlock_node(rn); | |
fb018d25 DS |
197 | } |
198 | ||
1d30d1f4 DS |
199 | /* |
200 | * This code will send to the registering client | |
201 | * the looked up rnh. | |
202 | * For a rnh that was created, there is no data | |
203 | * so it will send an empty nexthop group | |
204 | * If rnh exists then we know it has been evaluated | |
205 | * and as such it will have a resolved rnh. | |
206 | */ | |
d62a17ae | 207 | void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, |
208 | rnh_type_t type, vrf_id_t vrf_id) | |
fb018d25 | 209 | { |
d62a17ae | 210 | if (IS_ZEBRA_DEBUG_NHT) { |
211 | char buf[PREFIX2STR_BUFFER]; | |
212 | zlog_debug("%u: Client %s registers for RNH %s type %d", vrf_id, | |
213 | zebra_route_string(client->proto), | |
214 | rnh_str(rnh, buf, sizeof(buf)), type); | |
215 | } | |
81446366 | 216 | if (!listnode_lookup(rnh->client_list, client)) |
d62a17ae | 217 | listnode_add(rnh->client_list, client); |
81446366 DS |
218 | |
219 | /* | |
220 | * We always need to respond with known information, | |
221 | * currently multiple daemons expect this behavior | |
222 | */ | |
223 | send_client(rnh, client, type, vrf_id); | |
fb018d25 DS |
224 | } |
225 | ||
d62a17ae | 226 | void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, |
227 | rnh_type_t type) | |
fb018d25 | 228 | { |
d62a17ae | 229 | if (IS_ZEBRA_DEBUG_NHT) { |
230 | char buf[PREFIX2STR_BUFFER]; | |
231 | zlog_debug("Client %s unregisters for RNH %s type %d", | |
232 | zebra_route_string(client->proto), | |
233 | rnh_str(rnh, buf, sizeof(buf)), type); | |
234 | } | |
235 | listnode_delete(rnh->client_list, client); | |
236 | if (list_isempty(rnh->client_list) | |
731a75fe | 237 | && list_isempty(rnh->zebra_pseudowire_list)) |
d62a17ae | 238 | zebra_delete_rnh(rnh, type); |
6e26278c DS |
239 | } |
240 | ||
731a75fe RW |
241 | /* XXX move this utility function elsewhere? */ |
242 | static void addr2hostprefix(int af, const union g_addr *addr, | |
243 | struct prefix *prefix) | |
244 | { | |
245 | switch (af) { | |
246 | case AF_INET: | |
247 | prefix->family = AF_INET; | |
248 | prefix->prefixlen = IPV4_MAX_BITLEN; | |
249 | prefix->u.prefix4 = addr->ipv4; | |
250 | break; | |
251 | case AF_INET6: | |
252 | prefix->family = AF_INET6; | |
253 | prefix->prefixlen = IPV6_MAX_BITLEN; | |
254 | prefix->u.prefix6 = addr->ipv6; | |
255 | break; | |
256 | default: | |
c31a793b | 257 | memset(prefix, 0, sizeof(*prefix)); |
9df414fe | 258 | zlog_debug("%s: unknown address family %d", __func__, af); |
731a75fe RW |
259 | break; |
260 | } | |
261 | } | |
262 | ||
263 | void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw) | |
264 | { | |
265 | struct prefix nh; | |
266 | struct rnh *rnh; | |
1d30d1f4 | 267 | bool exists; |
6d53d7b1 | 268 | struct zebra_vrf *zvrf; |
269 | ||
270 | zvrf = vrf_info_lookup(vrf_id); | |
271 | if (!zvrf) | |
272 | return; | |
731a75fe RW |
273 | |
274 | addr2hostprefix(pw->af, &pw->nexthop, &nh); | |
1d30d1f4 | 275 | rnh = zebra_add_rnh(&nh, vrf_id, RNH_NEXTHOP_TYPE, &exists); |
731a75fe RW |
276 | if (rnh && !listnode_lookup(rnh->zebra_pseudowire_list, pw)) { |
277 | listnode_add(rnh->zebra_pseudowire_list, pw); | |
278 | pw->rnh = rnh; | |
6d53d7b1 | 279 | zebra_evaluate_rnh(zvrf, pw->af, 1, RNH_NEXTHOP_TYPE, &nh); |
731a75fe RW |
280 | } |
281 | } | |
282 | ||
283 | void zebra_deregister_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw) | |
284 | { | |
285 | struct rnh *rnh; | |
286 | ||
287 | rnh = pw->rnh; | |
288 | if (!rnh) | |
289 | return; | |
290 | ||
291 | listnode_delete(rnh->zebra_pseudowire_list, pw); | |
292 | pw->rnh = NULL; | |
293 | ||
294 | if (list_isempty(rnh->client_list) | |
731a75fe RW |
295 | && list_isempty(rnh->zebra_pseudowire_list)) |
296 | zebra_delete_rnh(rnh, RNH_NEXTHOP_TYPE); | |
297 | } | |
298 | ||
d50b5bdd | 299 | /* Apply the NHT route-map for a client to the route (and nexthops) |
300 | * resolving a NH. | |
301 | */ | |
6d53d7b1 | 302 | static int zebra_rnh_apply_nht_rmap(int family, struct zebra_vrf *zvrf, |
303 | struct route_node *prn, | |
d62a17ae | 304 | struct route_entry *re, int proto) |
6e26278c | 305 | { |
d62a17ae | 306 | int at_least_one = 0; |
307 | int rmap_family; /* Route map has diff AF family enum */ | |
308 | struct nexthop *nexthop; | |
309 | int ret; | |
310 | ||
311 | rmap_family = (family == AF_INET) ? AFI_IP : AFI_IP6; | |
312 | ||
313 | if (prn && re) { | |
7ee30f28 DS |
314 | for (nexthop = re->ng.nexthop; nexthop; |
315 | nexthop = nexthop->next) { | |
ac6eebce | 316 | ret = zebra_nht_route_map_check( |
317 | rmap_family, proto, &prn->p, zvrf, re, nexthop); | |
d62a17ae | 318 | if (ret != RMAP_DENYMATCH) { |
319 | SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
320 | at_least_one++; /* at least one valid NH */ | |
321 | } else { | |
322 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
323 | } | |
324 | } | |
6e26278c | 325 | } |
d62a17ae | 326 | return (at_least_one); |
6e26278c DS |
327 | } |
328 | ||
d50b5bdd | 329 | /* |
fd7fd9e5 DS |
330 | * Determine appropriate route (RE entry) resolving a tracked BGP route |
331 | * for BGP route for import. | |
d50b5bdd | 332 | */ |
996c9314 | 333 | static struct route_entry * |
6d53d7b1 | 334 | zebra_rnh_resolve_import_entry(struct zebra_vrf *zvrf, int family, |
996c9314 LB |
335 | struct route_node *nrn, struct rnh *rnh, |
336 | struct route_node **prn) | |
fb018d25 | 337 | { |
d62a17ae | 338 | struct route_table *route_table; |
339 | struct route_node *rn; | |
340 | struct route_entry *re; | |
341 | ||
342 | *prn = NULL; | |
343 | ||
6d53d7b1 | 344 | route_table = zvrf->table[family2afi(family)][SAFI_UNICAST]; |
d62a17ae | 345 | if (!route_table) // unexpected |
346 | return NULL; | |
347 | ||
348 | rn = route_node_match(route_table, &nrn->p); | |
349 | if (!rn) | |
350 | return NULL; | |
351 | ||
fd7fd9e5 DS |
352 | /* Unlock route node - we don't need to lock when walking the tree. */ |
353 | route_unlock_node(rn); | |
354 | ||
996c9314 LB |
355 | if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_EXACT_MATCH) |
356 | && !prefix_same(&nrn->p, &rn->p)) | |
fd7fd9e5 | 357 | return NULL; |
d62a17ae | 358 | |
fd7fd9e5 | 359 | /* Identify appropriate route entry. */ |
996c9314 LB |
360 | RNODE_FOREACH_RE (rn, re) { |
361 | if (!CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED) | |
362 | && CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) | |
363 | && (re->type != ZEBRA_ROUTE_BGP)) | |
fd7fd9e5 | 364 | break; |
d62a17ae | 365 | } |
366 | ||
d62a17ae | 367 | if (re) |
368 | *prn = rn; | |
369 | return re; | |
d50b5bdd | 370 | } |
371 | ||
372 | /* | |
373 | * See if a tracked route entry for import (by BGP) has undergone any | |
374 | * change, and if so, notify the client. | |
375 | */ | |
d62a17ae | 376 | static void zebra_rnh_eval_import_check_entry(vrf_id_t vrfid, int family, |
377 | int force, struct route_node *nrn, | |
378 | struct rnh *rnh, | |
379 | struct route_entry *re) | |
d50b5bdd | 380 | { |
d62a17ae | 381 | int state_changed = 0; |
382 | struct zserv *client; | |
383 | char bufn[INET6_ADDRSTRLEN]; | |
384 | struct listnode *node; | |
385 | struct nexthop *nexthop; | |
386 | ||
387 | if (re && (rnh->state == NULL)) { | |
7ee30f28 | 388 | for (ALL_NEXTHOPS(re->ng, nexthop)) |
d62a17ae | 389 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { |
390 | state_changed = 1; | |
391 | break; | |
392 | } | |
393 | } else if (!re && (rnh->state != NULL)) | |
394 | state_changed = 1; | |
395 | ||
396 | if (compare_state(re, rnh->state)) | |
397 | copy_state(rnh, re, nrn); | |
398 | ||
399 | if (state_changed || force) { | |
400 | if (IS_ZEBRA_DEBUG_NHT) { | |
401 | prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN); | |
402 | zlog_debug("%u:%s: Route import check %s %s\n", vrfid, | |
403 | bufn, rnh->state ? "passed" : "failed", | |
404 | state_changed ? "(state changed)" : ""); | |
405 | } | |
406 | /* state changed, notify clients */ | |
407 | for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { | |
408 | send_client(rnh, client, RNH_IMPORT_CHECK_TYPE, vrfid); | |
409 | } | |
410 | } | |
d50b5bdd | 411 | } |
fb018d25 | 412 | |
d50b5bdd | 413 | /* |
414 | * Notify clients registered for this nexthop about a change. | |
415 | */ | |
6d53d7b1 | 416 | static void zebra_rnh_notify_protocol_clients( |
417 | struct zebra_vrf *zvrf, int family, struct route_node *nrn, | |
418 | struct rnh *rnh, struct route_node *prn, struct route_entry *re) | |
d50b5bdd | 419 | { |
d62a17ae | 420 | struct listnode *node; |
421 | struct zserv *client; | |
422 | char bufn[INET6_ADDRSTRLEN]; | |
423 | char bufp[INET6_ADDRSTRLEN]; | |
424 | int num_resolving_nh; | |
425 | ||
426 | if (IS_ZEBRA_DEBUG_NHT) { | |
427 | prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN); | |
428 | if (prn && re) { | |
429 | prefix2str(&prn->p, bufp, INET6_ADDRSTRLEN); | |
6d53d7b1 | 430 | zlog_debug("%u:%s: NH resolved over route %s", |
431 | zvrf->vrf->vrf_id, bufn, bufp); | |
d62a17ae | 432 | } else |
6d53d7b1 | 433 | zlog_debug("%u:%s: NH has become unresolved", |
434 | zvrf->vrf->vrf_id, bufn); | |
d62a17ae | 435 | } |
436 | ||
437 | for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { | |
438 | if (prn && re) { | |
439 | /* Apply route-map for this client to route resolving | |
440 | * this | |
441 | * nexthop to see if it is filtered or not. | |
442 | */ | |
443 | num_resolving_nh = zebra_rnh_apply_nht_rmap( | |
6d53d7b1 | 444 | family, zvrf, prn, re, client->proto); |
d62a17ae | 445 | if (num_resolving_nh) |
446 | rnh->filtered[client->proto] = 0; | |
447 | else | |
448 | rnh->filtered[client->proto] = 1; | |
449 | ||
450 | if (IS_ZEBRA_DEBUG_NHT) | |
451 | zlog_debug( | |
452 | "%u:%s: Notifying client %s about NH %s", | |
6d53d7b1 | 453 | zvrf->vrf->vrf_id, bufn, |
d62a17ae | 454 | zebra_route_string(client->proto), |
455 | num_resolving_nh | |
456 | ? "" | |
457 | : "(filtered by route-map)"); | |
458 | } else { | |
459 | rnh->filtered[client->proto] = 0; | |
460 | if (IS_ZEBRA_DEBUG_NHT) | |
461 | zlog_debug( | |
462 | "%u:%s: Notifying client %s about NH (unreachable)", | |
6d53d7b1 | 463 | zvrf->vrf->vrf_id, bufn, |
d62a17ae | 464 | zebra_route_string(client->proto)); |
465 | } | |
466 | ||
6d53d7b1 | 467 | send_client(rnh, client, RNH_NEXTHOP_TYPE, zvrf->vrf->vrf_id); |
d62a17ae | 468 | } |
d50b5bdd | 469 | } |
078430f6 | 470 | |
d61d5d88 DS |
471 | static void zebra_rnh_process_pbr_tables(int family, |
472 | struct route_node *nrn, | |
473 | struct rnh *rnh, | |
474 | struct route_node *prn, | |
475 | struct route_entry *re) | |
476 | { | |
89272910 | 477 | struct zebra_router_table *zrt; |
d61d5d88 DS |
478 | struct route_entry *o_re; |
479 | struct route_node *o_rn; | |
480 | struct listnode *node; | |
481 | struct zserv *client; | |
d61d5d88 DS |
482 | afi_t afi = AFI_IP; |
483 | ||
484 | if (family == AF_INET6) | |
485 | afi = AFI_IP6; | |
486 | ||
487 | /* | |
488 | * We are only concerned about nexthops that change for | |
489 | * anyone using PBR | |
490 | */ | |
491 | for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { | |
492 | if (client->proto == ZEBRA_ROUTE_PBR) | |
493 | break; | |
494 | } | |
495 | ||
496 | if (!client) | |
497 | return; | |
498 | ||
89272910 DS |
499 | RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables) { |
500 | if (afi != zrt->afi) | |
d61d5d88 DS |
501 | continue; |
502 | ||
89272910 DS |
503 | for (o_rn = route_top(zrt->table); o_rn; |
504 | o_rn = srcdest_route_next(o_rn)) { | |
d61d5d88 DS |
505 | RNODE_FOREACH_RE (o_rn, o_re) { |
506 | if (o_re->type == ZEBRA_ROUTE_PBR) | |
507 | break; | |
508 | ||
509 | } | |
510 | ||
511 | /* | |
512 | * If we have a PBR route and a nexthop changes | |
513 | * just rethink it. Yes this is a hammer, but | |
514 | * a small one | |
515 | */ | |
4d96fd9f DS |
516 | if (o_re) { |
517 | SET_FLAG(o_re->status, ROUTE_ENTRY_CHANGED); | |
d61d5d88 | 518 | rib_queue_add(o_rn); |
4d96fd9f | 519 | } |
d61d5d88 DS |
520 | } |
521 | } | |
522 | } | |
523 | ||
4dfd7a02 MS |
524 | /* |
525 | * Utility to determine whether a candidate nexthop is useable. We make this | |
526 | * check in a couple of places, so this is a single home for the logic we | |
527 | * use. | |
528 | */ | |
529 | static bool rnh_nexthop_valid(const struct nexthop *nh) | |
530 | { | |
72366a6e | 531 | return (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_FIB) |
4dfd7a02 MS |
532 | && CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)); |
533 | } | |
534 | ||
fd7fd9e5 DS |
535 | /* |
536 | * Determine appropriate route (route entry) resolving a tracked | |
537 | * nexthop. | |
538 | */ | |
996c9314 | 539 | static struct route_entry * |
6d53d7b1 | 540 | zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, int family, |
996c9314 LB |
541 | struct route_node *nrn, struct rnh *rnh, |
542 | struct route_node **prn) | |
fd7fd9e5 DS |
543 | { |
544 | struct route_table *route_table; | |
545 | struct route_node *rn; | |
546 | struct route_entry *re; | |
f183e380 | 547 | struct nexthop *nexthop; |
fd7fd9e5 DS |
548 | |
549 | *prn = NULL; | |
550 | ||
6d53d7b1 | 551 | route_table = zvrf->table[family2afi(family)][SAFI_UNICAST]; |
fd7fd9e5 DS |
552 | if (!route_table) |
553 | return NULL; | |
554 | ||
555 | rn = route_node_match(route_table, &nrn->p); | |
556 | if (!rn) | |
557 | return NULL; | |
558 | ||
559 | /* Unlock route node - we don't need to lock when walking the tree. */ | |
560 | route_unlock_node(rn); | |
561 | ||
562 | /* While resolving nexthops, we may need to walk up the tree from the | |
563 | * most-specific match. Do similar logic as in zebra_rib.c | |
564 | */ | |
565 | while (rn) { | |
566 | /* Do not resolve over default route unless allowed && | |
567 | * match route to be exact if so specified | |
568 | */ | |
996c9314 LB |
569 | if (is_default_prefix(&rn->p) |
570 | && !rnh_resolve_via_default(rn->p.family)) | |
fd7fd9e5 DS |
571 | return NULL; |
572 | ||
573 | /* Identify appropriate route entry. */ | |
996c9314 | 574 | RNODE_FOREACH_RE (rn, re) { |
fd7fd9e5 DS |
575 | if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) |
576 | continue; | |
577 | if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) | |
578 | continue; | |
579 | ||
f183e380 MS |
580 | /* Just being SELECTED isn't quite enough - must |
581 | * have an installed nexthop to be useful. | |
582 | */ | |
72366a6e | 583 | for (ALL_NEXTHOPS(re->ng, nexthop)) { |
4dfd7a02 | 584 | if (rnh_nexthop_valid(nexthop)) |
f183e380 | 585 | break; |
4dfd7a02 | 586 | } |
f183e380 MS |
587 | |
588 | if (nexthop == NULL) | |
589 | continue; | |
590 | ||
fd7fd9e5 DS |
591 | if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)) { |
592 | if ((re->type == ZEBRA_ROUTE_CONNECT) | |
593 | || (re->type == ZEBRA_ROUTE_STATIC)) | |
594 | break; | |
595 | if (re->type == ZEBRA_ROUTE_NHRP) { | |
fd7fd9e5 | 596 | |
7ee30f28 | 597 | for (nexthop = re->ng.nexthop; nexthop; |
fd7fd9e5 DS |
598 | nexthop = nexthop->next) |
599 | if (nexthop->type | |
996c9314 | 600 | == NEXTHOP_TYPE_IFINDEX) |
fd7fd9e5 DS |
601 | break; |
602 | if (nexthop) | |
603 | break; | |
604 | } | |
605 | } else | |
606 | break; | |
607 | } | |
608 | ||
609 | /* Route entry found, we're done; else, walk up the tree. */ | |
610 | if (re) { | |
611 | *prn = rn; | |
612 | return re; | |
613 | } | |
614 | ||
615 | if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)) | |
616 | rn = rn->parent; | |
617 | else | |
618 | return NULL; | |
619 | } | |
620 | ||
621 | return NULL; | |
622 | } | |
623 | ||
731a75fe RW |
624 | static void zebra_rnh_process_pseudowires(vrf_id_t vrfid, struct rnh *rnh) |
625 | { | |
626 | struct zebra_pw *pw; | |
627 | struct listnode *node; | |
628 | ||
629 | for (ALL_LIST_ELEMENTS_RO(rnh->zebra_pseudowire_list, node, pw)) | |
630 | zebra_pw_update(pw); | |
631 | } | |
632 | ||
d50b5bdd | 633 | /* |
634 | * See if a tracked nexthop entry has undergone any change, and if so, | |
635 | * take appropriate action; this involves notifying any clients and/or | |
636 | * scheduling dependent static routes for processing. | |
637 | */ | |
6d53d7b1 | 638 | static void zebra_rnh_eval_nexthop_entry(struct zebra_vrf *zvrf, int family, |
639 | int force, struct route_node *nrn, | |
d62a17ae | 640 | struct rnh *rnh, |
641 | struct route_node *prn, | |
642 | struct route_entry *re) | |
d50b5bdd | 643 | { |
d62a17ae | 644 | int state_changed = 0; |
645 | ||
646 | /* If we're resolving over a different route, resolution has changed or | |
647 | * the resolving route has some change (e.g., metric), there is a state | |
648 | * change. | |
649 | */ | |
650 | if (!prefix_same(&rnh->resolved_route, &prn->p)) { | |
651 | if (prn) | |
652 | prefix_copy(&rnh->resolved_route, &prn->p); | |
653 | else | |
654 | memset(&rnh->resolved_route, 0, sizeof(struct prefix)); | |
655 | ||
656 | copy_state(rnh, re, nrn); | |
657 | state_changed = 1; | |
658 | } else if (compare_state(re, rnh->state)) { | |
659 | copy_state(rnh, re, nrn); | |
660 | state_changed = 1; | |
661 | } | |
662 | ||
663 | if (state_changed || force) { | |
664 | /* NOTE: Use the "copy" of resolving route stored in 'rnh' i.e., | |
665 | * rnh->state. | |
666 | */ | |
667 | /* Notify registered protocol clients. */ | |
6d53d7b1 | 668 | zebra_rnh_notify_protocol_clients(zvrf, family, nrn, rnh, prn, |
d62a17ae | 669 | rnh->state); |
670 | ||
6d53d7b1 | 671 | zebra_rnh_process_pbr_tables(family, nrn, rnh, prn, rnh->state); |
d61d5d88 | 672 | |
731a75fe | 673 | /* Process pseudowires attached to this nexthop */ |
6d53d7b1 | 674 | zebra_rnh_process_pseudowires(zvrf->vrf->vrf_id, rnh); |
d62a17ae | 675 | } |
d50b5bdd | 676 | } |
6e26278c | 677 | |
d50b5bdd | 678 | /* Evaluate one tracked entry */ |
6d53d7b1 | 679 | static void zebra_rnh_evaluate_entry(struct zebra_vrf *zvrf, int family, |
680 | int force, rnh_type_t type, | |
681 | struct route_node *nrn) | |
d50b5bdd | 682 | { |
d62a17ae | 683 | struct rnh *rnh; |
684 | struct route_entry *re; | |
685 | struct route_node *prn; | |
686 | char bufn[INET6_ADDRSTRLEN]; | |
687 | ||
688 | if (IS_ZEBRA_DEBUG_NHT) { | |
689 | prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN); | |
6d53d7b1 | 690 | zlog_debug("%u:%s: Evaluate RNH, type %d %s", zvrf->vrf->vrf_id, |
691 | bufn, type, force ? "(force)" : ""); | |
d62a17ae | 692 | } |
693 | ||
694 | rnh = nrn->info; | |
695 | ||
696 | /* Identify route entry (RE) resolving this tracked entry. */ | |
fd7fd9e5 | 697 | if (type == RNH_IMPORT_CHECK_TYPE) |
6d53d7b1 | 698 | re = zebra_rnh_resolve_import_entry(zvrf, family, nrn, rnh, |
996c9314 | 699 | &prn); |
fd7fd9e5 | 700 | else |
6d53d7b1 | 701 | re = zebra_rnh_resolve_nexthop_entry(zvrf, family, nrn, rnh, |
fd7fd9e5 | 702 | &prn); |
d62a17ae | 703 | |
704 | /* If the entry cannot be resolved and that is also the existing state, | |
705 | * there is nothing further to do. | |
706 | */ | |
707 | if (!re && rnh->state == NULL && !force) | |
708 | return; | |
709 | ||
710 | /* Process based on type of entry. */ | |
711 | if (type == RNH_IMPORT_CHECK_TYPE) | |
6d53d7b1 | 712 | zebra_rnh_eval_import_check_entry(zvrf->vrf->vrf_id, family, |
713 | force, nrn, rnh, re); | |
d62a17ae | 714 | else |
6d53d7b1 | 715 | zebra_rnh_eval_nexthop_entry(zvrf, family, force, nrn, rnh, prn, |
716 | re); | |
d50b5bdd | 717 | } |
718 | ||
8ce5e6a3 | 719 | /* |
f0f77c9a DS |
720 | * Clear the ROUTE_ENTRY_NEXTHOPS_CHANGED flag |
721 | * from the re entries. | |
8ce5e6a3 DS |
722 | * |
723 | * Please note we are doing this *after* we have | |
724 | * notified the world about each nexthop as that | |
f0f77c9a | 725 | * we can have a situation where one re entry |
8ce5e6a3 DS |
726 | * covers multiple nexthops we are interested in. |
727 | */ | |
6d53d7b1 | 728 | static void zebra_rnh_clear_nhc_flag(struct zebra_vrf *zvrf, int family, |
d62a17ae | 729 | rnh_type_t type, struct route_node *nrn) |
8ce5e6a3 | 730 | { |
d62a17ae | 731 | struct rnh *rnh; |
732 | struct route_entry *re; | |
733 | struct route_node *prn; | |
8ce5e6a3 | 734 | |
d62a17ae | 735 | rnh = nrn->info; |
8ce5e6a3 | 736 | |
fd7fd9e5 DS |
737 | /* Identify route entry (RIB) resolving this tracked entry. */ |
738 | if (type == RNH_IMPORT_CHECK_TYPE) | |
6d53d7b1 | 739 | re = zebra_rnh_resolve_import_entry(zvrf, family, nrn, rnh, |
996c9314 | 740 | &prn); |
fd7fd9e5 | 741 | else |
6d53d7b1 | 742 | re = zebra_rnh_resolve_nexthop_entry(zvrf, family, nrn, rnh, |
fd7fd9e5 | 743 | &prn); |
8ce5e6a3 | 744 | |
332ad713 | 745 | if (re) { |
d62a17ae | 746 | UNSET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED); |
332ad713 RW |
747 | UNSET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); |
748 | } | |
8ce5e6a3 | 749 | } |
d50b5bdd | 750 | |
751 | /* Evaluate all tracked entries (nexthops or routes for import into BGP) | |
752 | * of a particular VRF and address-family or a specific prefix. | |
753 | */ | |
6d53d7b1 | 754 | void zebra_evaluate_rnh(struct zebra_vrf *zvrf, int family, int force, |
755 | rnh_type_t type, struct prefix *p) | |
d50b5bdd | 756 | { |
d62a17ae | 757 | struct route_table *rnh_table; |
758 | struct route_node *nrn; | |
759 | ||
6d53d7b1 | 760 | rnh_table = get_rnh_table(zvrf->vrf->vrf_id, family, type); |
d62a17ae | 761 | if (!rnh_table) // unexpected |
762 | return; | |
763 | ||
764 | if (p) { | |
765 | /* Evaluating a specific entry, make sure it exists. */ | |
766 | nrn = route_node_lookup(rnh_table, p); | |
767 | if (nrn && nrn->info) | |
6d53d7b1 | 768 | zebra_rnh_evaluate_entry(zvrf, family, force, type, |
d62a17ae | 769 | nrn); |
770 | ||
771 | if (nrn) | |
772 | route_unlock_node(nrn); | |
773 | } else { | |
774 | /* Evaluate entire table. */ | |
775 | nrn = route_top(rnh_table); | |
776 | while (nrn) { | |
777 | if (nrn->info) | |
6d53d7b1 | 778 | zebra_rnh_evaluate_entry(zvrf, family, force, |
d62a17ae | 779 | type, nrn); |
780 | nrn = route_next(nrn); /* this will also unlock nrn */ | |
781 | } | |
782 | nrn = route_top(rnh_table); | |
783 | while (nrn) { | |
784 | if (nrn->info) | |
6d53d7b1 | 785 | zebra_rnh_clear_nhc_flag(zvrf, family, type, |
d62a17ae | 786 | nrn); |
787 | nrn = route_next(nrn); /* this will also unlock nrn */ | |
788 | } | |
789 | } | |
fb018d25 DS |
790 | } |
791 | ||
d62a17ae | 792 | void zebra_print_rnh_table(vrf_id_t vrfid, int af, struct vty *vty, |
793 | rnh_type_t type) | |
fb018d25 | 794 | { |
d62a17ae | 795 | struct route_table *table; |
796 | struct route_node *rn; | |
797 | ||
798 | table = get_rnh_table(vrfid, af, type); | |
799 | if (!table) { | |
800 | zlog_debug("print_rnhs: rnh table not found\n"); | |
801 | return; | |
802 | } | |
803 | ||
804 | for (rn = route_top(table); rn; rn = route_next(rn)) | |
805 | if (rn->info) | |
806 | print_rnh(rn, vty); | |
fb018d25 DS |
807 | } |
808 | ||
fb018d25 | 809 | /** |
f0f77c9a | 810 | * free_state - free up the re structure associated with the rnh. |
fb018d25 | 811 | */ |
d62a17ae | 812 | static void free_state(vrf_id_t vrf_id, struct route_entry *re, |
813 | struct route_node *rn) | |
fb018d25 | 814 | { |
fb018d25 | 815 | |
d62a17ae | 816 | if (!re) |
817 | return; | |
fb018d25 | 818 | |
d62a17ae | 819 | /* free RE and nexthops */ |
7ee30f28 | 820 | nexthops_free(re->ng.nexthop); |
d62a17ae | 821 | XFREE(MTYPE_RE, re); |
fb018d25 DS |
822 | } |
823 | ||
d62a17ae | 824 | static void copy_state(struct rnh *rnh, struct route_entry *re, |
825 | struct route_node *rn) | |
fb018d25 | 826 | { |
d62a17ae | 827 | struct route_entry *state; |
fb018d25 | 828 | |
d62a17ae | 829 | if (rnh->state) { |
830 | free_state(rnh->vrf_id, rnh->state, rn); | |
831 | rnh->state = NULL; | |
832 | } | |
fb018d25 | 833 | |
d62a17ae | 834 | if (!re) |
835 | return; | |
fb018d25 | 836 | |
d62a17ae | 837 | state = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); |
838 | state->type = re->type; | |
7733c6c4 | 839 | state->distance = re->distance; |
d62a17ae | 840 | state->metric = re->metric; |
8f43b4d8 | 841 | state->vrf_id = re->vrf_id; |
fb018d25 | 842 | |
7ee30f28 | 843 | route_entry_copy_nexthops(state, re->ng.nexthop); |
d62a17ae | 844 | rnh->state = state; |
fb018d25 DS |
845 | } |
846 | ||
d62a17ae | 847 | static int compare_state(struct route_entry *r1, struct route_entry *r2) |
fb018d25 | 848 | { |
fb018d25 | 849 | |
d62a17ae | 850 | if (!r1 && !r2) |
851 | return 0; | |
fb018d25 | 852 | |
d62a17ae | 853 | if ((!r1 && r2) || (r1 && !r2)) |
854 | return 1; | |
fb018d25 | 855 | |
7733c6c4 JB |
856 | if (r1->distance != r2->distance) |
857 | return 1; | |
858 | ||
d62a17ae | 859 | if (r1->metric != r2->metric) |
860 | return 1; | |
fb018d25 | 861 | |
d62a17ae | 862 | if (r1->nexthop_num != r2->nexthop_num) |
863 | return 1; | |
fb018d25 | 864 | |
332ad713 RW |
865 | if (CHECK_FLAG(r1->status, ROUTE_ENTRY_NEXTHOPS_CHANGED) |
866 | || CHECK_FLAG(r1->status, ROUTE_ENTRY_LABELS_CHANGED)) | |
d62a17ae | 867 | return 1; |
fb018d25 | 868 | |
d62a17ae | 869 | return 0; |
fb018d25 DS |
870 | } |
871 | ||
d62a17ae | 872 | static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, |
873 | vrf_id_t vrf_id) | |
fb018d25 | 874 | { |
d62a17ae | 875 | struct stream *s; |
876 | struct route_entry *re; | |
877 | unsigned long nump; | |
d7c0a89a | 878 | uint8_t num; |
0acf4df0 | 879 | struct nexthop *nh; |
d62a17ae | 880 | struct route_node *rn; |
881 | int cmd = (type == RNH_IMPORT_CHECK_TYPE) ? ZEBRA_IMPORT_CHECK_UPDATE | |
882 | : ZEBRA_NEXTHOP_UPDATE; | |
883 | ||
884 | rn = rnh->node; | |
885 | re = rnh->state; | |
886 | ||
887 | /* Get output stream. */ | |
1002497a | 888 | s = stream_new(ZEBRA_MAX_PACKET_SIZ); |
d62a17ae | 889 | |
7cf15b25 | 890 | zclient_create_header(s, cmd, vrf_id); |
d62a17ae | 891 | |
892 | stream_putw(s, rn->p.family); | |
893 | switch (rn->p.family) { | |
894 | case AF_INET: | |
895 | stream_putc(s, rn->p.prefixlen); | |
896 | stream_put_in_addr(s, &rn->p.u.prefix4); | |
fb018d25 | 897 | break; |
d62a17ae | 898 | case AF_INET6: |
899 | stream_putc(s, rn->p.prefixlen); | |
900 | stream_put(s, &rn->p.u.prefix6, IPV6_MAX_BYTELEN); | |
fb018d25 | 901 | break; |
d62a17ae | 902 | default: |
e914ccbe | 903 | flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY, |
1c50c1c0 QY |
904 | "%s: Unknown family (%d) notification attempted\n", |
905 | __FUNCTION__, rn->p.family); | |
fb018d25 | 906 | break; |
d62a17ae | 907 | } |
908 | if (re) { | |
05dd5aaf DS |
909 | stream_putc(s, re->type); |
910 | stream_putw(s, re->instance); | |
d62a17ae | 911 | stream_putc(s, re->distance); |
912 | stream_putl(s, re->metric); | |
913 | num = 0; | |
914 | nump = stream_get_endp(s); | |
915 | stream_putc(s, 0); | |
72366a6e | 916 | for (ALL_NEXTHOPS(re->ng, nh)) |
4dfd7a02 | 917 | if (rnh_nexthop_valid(nh)) { |
b6c9de3b | 918 | stream_putl(s, nh->vrf_id); |
0acf4df0 DS |
919 | stream_putc(s, nh->type); |
920 | switch (nh->type) { | |
d62a17ae | 921 | case NEXTHOP_TYPE_IPV4: |
aab09c10 | 922 | case NEXTHOP_TYPE_IPV4_IFINDEX: |
0acf4df0 DS |
923 | stream_put_in_addr(s, &nh->gate.ipv4); |
924 | stream_putl(s, nh->ifindex); | |
d62a17ae | 925 | break; |
926 | case NEXTHOP_TYPE_IFINDEX: | |
0acf4df0 | 927 | stream_putl(s, nh->ifindex); |
d62a17ae | 928 | break; |
d62a17ae | 929 | case NEXTHOP_TYPE_IPV6: |
d62a17ae | 930 | case NEXTHOP_TYPE_IPV6_IFINDEX: |
0acf4df0 DS |
931 | stream_put(s, &nh->gate.ipv6, 16); |
932 | stream_putl(s, nh->ifindex); | |
d62a17ae | 933 | break; |
934 | default: | |
935 | /* do nothing */ | |
936 | break; | |
937 | } | |
0acf4df0 DS |
938 | if (nh->nh_label) { |
939 | stream_putc(s, | |
940 | nh->nh_label->num_labels); | |
941 | if (nh->nh_label->num_labels) | |
942 | stream_put( | |
943 | s, | |
944 | &nh->nh_label->label[0], | |
945 | nh->nh_label->num_labels | |
946 | * sizeof(mpls_label_t)); | |
947 | } else | |
948 | stream_putc(s, 0); | |
d62a17ae | 949 | num++; |
950 | } | |
951 | stream_putc_at(s, nump, num); | |
952 | } else { | |
05dd5aaf DS |
953 | stream_putc(s, 0); // type |
954 | stream_putw(s, 0); // instance | |
d62a17ae | 955 | stream_putc(s, 0); // distance |
956 | stream_putl(s, 0); // metric | |
957 | stream_putc(s, 0); // nexthops | |
958 | } | |
959 | stream_putw_at(s, 0, stream_get_endp(s)); | |
960 | ||
961 | client->nh_last_upd_time = monotime(NULL); | |
962 | client->last_write_cmd = cmd; | |
21ccc0cf | 963 | return zserv_send_message(client, s); |
fb018d25 DS |
964 | } |
965 | ||
d62a17ae | 966 | static void print_nh(struct nexthop *nexthop, struct vty *vty) |
fb018d25 | 967 | { |
d62a17ae | 968 | char buf[BUFSIZ]; |
969 | struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); | |
970 | ||
971 | switch (nexthop->type) { | |
972 | case NEXTHOP_TYPE_IPV4: | |
973 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
974 | vty_out(vty, " via %s", inet_ntoa(nexthop->gate.ipv4)); | |
975 | if (nexthop->ifindex) | |
976 | vty_out(vty, ", %s", | |
977 | ifindex2ifname_per_ns(zns, nexthop->ifindex)); | |
978 | break; | |
979 | case NEXTHOP_TYPE_IPV6: | |
980 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
981 | vty_out(vty, " %s", | |
982 | inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); | |
983 | if (nexthop->ifindex) | |
984 | vty_out(vty, ", via %s", | |
985 | ifindex2ifname_per_ns(zns, nexthop->ifindex)); | |
986 | break; | |
987 | case NEXTHOP_TYPE_IFINDEX: | |
988 | vty_out(vty, " is directly connected, %s", | |
989 | ifindex2ifname_per_ns(zns, nexthop->ifindex)); | |
990 | break; | |
991 | case NEXTHOP_TYPE_BLACKHOLE: | |
992 | vty_out(vty, " is directly connected, Null0"); | |
993 | break; | |
994 | default: | |
995 | break; | |
996 | } | |
997 | vty_out(vty, "\n"); | |
fb018d25 DS |
998 | } |
999 | ||
d62a17ae | 1000 | static void print_rnh(struct route_node *rn, struct vty *vty) |
fb018d25 | 1001 | { |
d62a17ae | 1002 | struct rnh *rnh; |
1003 | struct nexthop *nexthop; | |
1004 | struct listnode *node; | |
1005 | struct zserv *client; | |
1006 | char buf[BUFSIZ]; | |
1007 | ||
1008 | rnh = rn->info; | |
1009 | vty_out(vty, "%s%s\n", | |
1010 | inet_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ), | |
1011 | CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED) ? "(Connected)" | |
1012 | : ""); | |
1013 | if (rnh->state) { | |
1014 | vty_out(vty, " resolved via %s\n", | |
1015 | zebra_route_string(rnh->state->type)); | |
7ee30f28 | 1016 | for (nexthop = rnh->state->ng.nexthop; nexthop; |
d62a17ae | 1017 | nexthop = nexthop->next) |
1018 | print_nh(nexthop, vty); | |
1019 | } else | |
1020 | vty_out(vty, " unresolved%s\n", | |
1021 | CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED) | |
1022 | ? "(Connected)" | |
1023 | : ""); | |
1024 | ||
1025 | vty_out(vty, " Client list:"); | |
1026 | for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) | |
1027 | vty_out(vty, " %s(fd %d)%s", zebra_route_string(client->proto), | |
1028 | client->sock, | |
1029 | rnh->filtered[client->proto] ? "(filtered)" : ""); | |
731a75fe RW |
1030 | if (!list_isempty(rnh->zebra_pseudowire_list)) |
1031 | vty_out(vty, " zebra[pseudowires]"); | |
d62a17ae | 1032 | vty_out(vty, "\n"); |
fb018d25 | 1033 | } |
bf094f69 QY |
1034 | |
1035 | static int zebra_cleanup_rnh_client(vrf_id_t vrf_id, int family, | |
1036 | struct zserv *client, rnh_type_t type) | |
1037 | { | |
1038 | struct route_table *ntable; | |
1039 | struct route_node *nrn; | |
1040 | struct rnh *rnh; | |
1041 | ||
1042 | if (IS_ZEBRA_DEBUG_NHT) | |
1043 | zlog_debug("%u: Client %s RNH cleanup for family %d type %d", | |
1044 | vrf_id, zebra_route_string(client->proto), family, | |
1045 | type); | |
1046 | ||
1047 | ntable = get_rnh_table(vrf_id, family, type); | |
1048 | if (!ntable) { | |
1049 | zlog_debug("cleanup_rnh_client: rnh table not found\n"); | |
1050 | return -1; | |
1051 | } | |
1052 | ||
1053 | for (nrn = route_top(ntable); nrn; nrn = route_next(nrn)) { | |
1054 | if (!nrn->info) | |
1055 | continue; | |
1056 | ||
1057 | rnh = nrn->info; | |
1058 | zebra_remove_rnh_client(rnh, client, type); | |
1059 | } | |
1060 | return 1; | |
1061 | } | |
1062 | ||
1063 | /* Cleanup registered nexthops (across VRFs) upon client disconnect. */ | |
453844ab | 1064 | static int zebra_client_cleanup_rnh(struct zserv *client) |
bf094f69 QY |
1065 | { |
1066 | struct vrf *vrf; | |
1067 | struct zebra_vrf *zvrf; | |
1068 | ||
1069 | RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { | |
8b1766b1 QY |
1070 | zvrf = vrf->info; |
1071 | if (zvrf) { | |
bf094f69 QY |
1072 | zebra_cleanup_rnh_client(zvrf_id(zvrf), AF_INET, client, |
1073 | RNH_NEXTHOP_TYPE); | |
1074 | zebra_cleanup_rnh_client(zvrf_id(zvrf), AF_INET6, | |
1075 | client, RNH_NEXTHOP_TYPE); | |
1076 | zebra_cleanup_rnh_client(zvrf_id(zvrf), AF_INET, client, | |
1077 | RNH_IMPORT_CHECK_TYPE); | |
1078 | zebra_cleanup_rnh_client(zvrf_id(zvrf), AF_INET6, | |
1079 | client, RNH_IMPORT_CHECK_TYPE); | |
1080 | if (client->proto == ZEBRA_ROUTE_LDP) { | |
1081 | hash_iterate(zvrf->lsp_table, | |
1082 | mpls_ldp_lsp_uninstall_all, | |
1083 | zvrf->lsp_table); | |
1084 | mpls_ldp_ftn_uninstall_all(zvrf, AFI_IP); | |
1085 | mpls_ldp_ftn_uninstall_all(zvrf, AFI_IP6); | |
1086 | } | |
1087 | } | |
1088 | } | |
453844ab QY |
1089 | |
1090 | return 0; | |
bf094f69 | 1091 | } |