]>
Commit | Line | Data |
---|---|---|
ad28e79a SW |
1 | /* Zebra Nexthop Group Code. |
2 | * Copyright (C) 2019 Cumulus Networks, Inc. | |
3 | * Donald Sharp | |
4 | * Stephen Worley | |
5 | * | |
6 | * This file is part of FRR. | |
7 | * | |
8 | * FRR is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2, or (at your option) any | |
11 | * later version. | |
12 | * | |
13 | * FRR is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with FRR; see the file COPYING. If not, write to the Free | |
20 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
21 | * 02111-1307, USA. | |
22 | */ | |
23 | #include <zebra.h> | |
24 | ||
25 | #include "lib/nexthop.h" | |
50d89650 | 26 | #include "lib/nexthop_group_private.h" |
ad28e79a | 27 | #include "lib/routemap.h" |
b43434ad | 28 | #include "lib/mpls.h" |
69171da2 | 29 | #include "lib/jhash.h" |
51d80884 | 30 | #include "lib/debug.h" |
ad28e79a SW |
31 | |
32 | #include "zebra/connected.h" | |
33 | #include "zebra/debug.h" | |
34 | #include "zebra/zebra_router.h" | |
35 | #include "zebra/zebra_nhg.h" | |
36 | #include "zebra/zebra_rnh.h" | |
37 | #include "zebra/zebra_routemap.h" | |
51d80884 SW |
38 | #include "zebra/zebra_memory.h" |
39 | #include "zebra/zserv.h" | |
ad28e79a | 40 | #include "zebra/rt.h" |
d9f5b2f5 | 41 | #include "zebra_errors.h" |
0c8215cb | 42 | #include "zebra_dplane.h" |
fe593b78 | 43 | #include "zebra/interface.h" |
d9f5b2f5 | 44 | |
51d80884 | 45 | DEFINE_MTYPE_STATIC(ZEBRA, NHG, "Nexthop Group Entry"); |
a15d4c00 | 46 | DEFINE_MTYPE_STATIC(ZEBRA, NHG_CONNECTED, "Nexthop Group Connected"); |
e22e8001 | 47 | DEFINE_MTYPE_STATIC(ZEBRA, NHG_CTX, "Nexthop Group Context"); |
0c8215cb | 48 | |
fe593b78 SW |
49 | static int nhg_connected_cmp(const struct nhg_connected *dep1, |
50 | const struct nhg_connected *dep2); | |
0c8215cb | 51 | |
fe593b78 | 52 | RB_GENERATE(nhg_connected_head, nhg_connected, nhg_entry, nhg_connected_cmp); |
0c8215cb | 53 | |
e22e8001 | 54 | |
fe593b78 | 55 | void nhg_connected_free(struct nhg_connected *dep) |
0c8215cb | 56 | { |
a15d4c00 | 57 | XFREE(MTYPE_NHG_CONNECTED, dep); |
0c8215cb SW |
58 | } |
59 | ||
fe593b78 | 60 | struct nhg_connected *nhg_connected_new(struct nhg_hash_entry *nhe) |
0c8215cb | 61 | { |
a15d4c00 | 62 | struct nhg_connected *new = NULL; |
0c8215cb | 63 | |
a15d4c00 | 64 | new = XCALLOC(MTYPE_NHG_CONNECTED, sizeof(struct nhg_connected)); |
0c8215cb SW |
65 | new->nhe = nhe; |
66 | ||
67 | return new; | |
68 | } | |
69 | ||
fe593b78 SW |
70 | void nhg_connected_head_init(struct nhg_connected_head *head) |
71 | { | |
72 | RB_INIT(nhg_connected_head, head); | |
73 | } | |
74 | ||
75 | void nhg_connected_head_free(struct nhg_connected_head *head) | |
0c8215cb | 76 | { |
a15d4c00 | 77 | struct nhg_connected *rb_node_dep = NULL; |
fe593b78 | 78 | struct nhg_connected *tmp = NULL; |
0c8215cb | 79 | |
fe593b78 SW |
80 | if (!nhg_connected_head_is_empty(head)) { |
81 | RB_FOREACH_SAFE (rb_node_dep, nhg_connected_head, head, tmp) { | |
82 | RB_REMOVE(nhg_connected_head, head, rb_node_dep); | |
83 | nhg_connected_free(rb_node_dep); | |
84 | } | |
0c8215cb | 85 | } |
0c8215cb SW |
86 | } |
87 | ||
fe593b78 | 88 | unsigned int nhg_connected_head_count(const struct nhg_connected_head *head) |
0c8215cb | 89 | { |
fe593b78 SW |
90 | struct nhg_connected *rb_node_dep = NULL; |
91 | unsigned int i = 0; | |
0c8215cb | 92 | |
fe593b78 SW |
93 | RB_FOREACH (rb_node_dep, nhg_connected_head, head) { |
94 | i++; | |
95 | } | |
96 | return i; | |
0c8215cb | 97 | } |
3119f6a1 | 98 | |
fe593b78 | 99 | bool nhg_connected_head_is_empty(const struct nhg_connected_head *head) |
3119f6a1 | 100 | { |
fe593b78 | 101 | return RB_EMPTY(nhg_connected_head, head); |
0c8215cb SW |
102 | } |
103 | ||
98cda54a SW |
104 | struct nhg_connected * |
105 | nhg_connected_head_root(const struct nhg_connected_head *head) | |
106 | { | |
107 | return RB_ROOT(nhg_connected_head, head); | |
108 | } | |
109 | ||
fe593b78 SW |
110 | void nhg_connected_head_del(struct nhg_connected_head *head, |
111 | struct nhg_hash_entry *depend) | |
0c8215cb | 112 | { |
a15d4c00 | 113 | struct nhg_connected lookup = {}; |
085304dc | 114 | struct nhg_connected *remove = NULL; |
0c8215cb SW |
115 | |
116 | lookup.nhe = depend; | |
3119f6a1 | 117 | |
085304dc SW |
118 | /* Lookup to find the element, then remove it */ |
119 | remove = RB_FIND(nhg_connected_head, head, &lookup); | |
120 | remove = RB_REMOVE(nhg_connected_head, head, remove); | |
3119f6a1 | 121 | |
085304dc SW |
122 | if (remove) |
123 | nhg_connected_free(remove); | |
3119f6a1 SW |
124 | } |
125 | ||
fe593b78 SW |
126 | void nhg_connected_head_add(struct nhg_connected_head *head, |
127 | struct nhg_hash_entry *depend) | |
3119f6a1 | 128 | { |
a15d4c00 | 129 | struct nhg_connected *new = NULL; |
0c8215cb | 130 | |
a15d4c00 | 131 | new = nhg_connected_new(depend); |
0c8215cb | 132 | |
18cf7f15 SW |
133 | if (new) |
134 | RB_INSERT(nhg_connected_head, head, new); | |
3119f6a1 SW |
135 | } |
136 | ||
98cda54a SW |
137 | struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe) |
138 | { | |
139 | if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE) | |
140 | && !zebra_nhg_depends_is_empty(nhe)) { | |
141 | nhe = nhg_connected_head_root(&nhe->nhg_depends)->nhe; | |
142 | return zebra_nhg_resolve(nhe); | |
143 | } | |
144 | ||
145 | return nhe; | |
146 | } | |
147 | ||
148 | uint32_t zebra_nhg_get_resolved_id(uint32_t id) | |
149 | { | |
150 | struct nhg_hash_entry *nhe = NULL; | |
151 | ||
152 | nhe = zebra_nhg_lookup_id(id); | |
153 | ||
154 | if (!nhe) { | |
155 | flog_err( | |
156 | EC_ZEBRA_TABLE_LOOKUP_FAILED, | |
157 | "Zebra failed to lookup a resolved nexthop hash entry id=%u", | |
158 | id); | |
159 | return id; | |
160 | } | |
161 | ||
162 | if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE)) | |
163 | nhe = zebra_nhg_resolve(nhe); | |
164 | ||
165 | return nhe->id; | |
166 | } | |
167 | ||
fe593b78 | 168 | unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe) |
a15d4c00 | 169 | { |
fe593b78 | 170 | return nhg_connected_head_count(&nhe->nhg_depends); |
a15d4c00 SW |
171 | } |
172 | ||
fe593b78 | 173 | bool zebra_nhg_depends_is_empty(const struct nhg_hash_entry *nhe) |
a15d4c00 | 174 | { |
fe593b78 | 175 | return nhg_connected_head_is_empty(&nhe->nhg_depends); |
a15d4c00 SW |
176 | } |
177 | ||
0c8215cb SW |
178 | void zebra_nhg_depends_del(struct nhg_hash_entry *from, |
179 | struct nhg_hash_entry *depend) | |
3119f6a1 | 180 | { |
fe593b78 | 181 | nhg_connected_head_del(&from->nhg_depends, depend); |
3119f6a1 SW |
182 | } |
183 | ||
0c8215cb SW |
184 | void zebra_nhg_depends_add(struct nhg_hash_entry *to, |
185 | struct nhg_hash_entry *depend) | |
3119f6a1 | 186 | { |
fe593b78 | 187 | nhg_connected_head_add(&to->nhg_depends, depend); |
d6e0094f SW |
188 | } |
189 | ||
0c8215cb | 190 | void zebra_nhg_depends_init(struct nhg_hash_entry *nhe) |
148a0103 | 191 | { |
fe593b78 | 192 | nhg_connected_head_init(&nhe->nhg_depends); |
148a0103 SW |
193 | } |
194 | ||
21615102 SW |
195 | /* Release this nhe from anything that it depends on */ |
196 | static void zebra_nhg_depends_release(struct nhg_hash_entry *nhe) | |
197 | { | |
198 | if (!zebra_nhg_depends_is_empty(nhe)) { | |
199 | struct nhg_connected *rb_node_dep = NULL; | |
200 | struct nhg_connected *tmp = NULL; | |
201 | ||
202 | RB_FOREACH_SAFE (rb_node_dep, nhg_connected_head, | |
203 | &nhe->nhg_depends, tmp) { | |
204 | zebra_nhg_dependents_del(rb_node_dep->nhe, nhe); | |
205 | } | |
206 | } | |
207 | } | |
208 | ||
fe593b78 SW |
209 | unsigned int zebra_nhg_dependents_count(const struct nhg_hash_entry *nhe) |
210 | { | |
211 | return nhg_connected_head_count(&nhe->nhg_dependents); | |
212 | } | |
213 | ||
214 | bool zebra_nhg_dependents_is_empty(const struct nhg_hash_entry *nhe) | |
215 | { | |
216 | return nhg_connected_head_is_empty(&nhe->nhg_dependents); | |
217 | } | |
218 | ||
fe593b78 SW |
219 | void zebra_nhg_dependents_del(struct nhg_hash_entry *from, |
220 | struct nhg_hash_entry *dependent) | |
221 | { | |
222 | nhg_connected_head_del(&from->nhg_dependents, dependent); | |
223 | } | |
224 | ||
fe593b78 SW |
225 | void zebra_nhg_dependents_add(struct nhg_hash_entry *to, |
226 | struct nhg_hash_entry *dependent) | |
227 | { | |
228 | nhg_connected_head_add(&to->nhg_dependents, dependent); | |
229 | } | |
230 | ||
fe593b78 SW |
231 | void zebra_nhg_dependents_init(struct nhg_hash_entry *nhe) |
232 | { | |
233 | nhg_connected_head_init(&nhe->nhg_dependents); | |
234 | } | |
235 | ||
21615102 SW |
236 | /* Release this nhe from anything depending on it */ |
237 | static void zebra_nhg_dependents_release(struct nhg_hash_entry *nhe) | |
238 | { | |
239 | if (!zebra_nhg_dependents_is_empty(nhe)) { | |
240 | struct nhg_connected *rb_node_dep = NULL; | |
241 | struct nhg_connected *tmp = NULL; | |
242 | ||
243 | RB_FOREACH_SAFE (rb_node_dep, nhg_connected_head, | |
244 | &nhe->nhg_dependents, tmp) { | |
245 | zebra_nhg_depends_del(rb_node_dep->nhe, nhe); | |
246 | } | |
247 | } | |
248 | } | |
249 | ||
d9f5b2f5 SW |
250 | struct nhg_hash_entry *zebra_nhg_lookup_id(uint32_t id) |
251 | { | |
0c8215cb | 252 | struct nhg_hash_entry lookup = {}; |
d9f5b2f5 SW |
253 | |
254 | lookup.id = id; | |
255 | return hash_lookup(zrouter.nhgs_id, &lookup); | |
256 | } | |
257 | ||
d9f5b2f5 SW |
258 | int zebra_nhg_insert_id(struct nhg_hash_entry *nhe) |
259 | { | |
260 | if (hash_lookup(zrouter.nhgs_id, nhe)) { | |
261 | flog_err( | |
262 | EC_ZEBRA_NHG_TABLE_INSERT_FAILED, | |
263 | "Failed inserting NHG id=%u into the ID hash table, entry already exists", | |
264 | nhe->id); | |
265 | return -1; | |
266 | } | |
267 | ||
268 | hash_get(zrouter.nhgs_id, nhe, hash_alloc_intern); | |
269 | ||
270 | return 0; | |
271 | } | |
ad28e79a | 272 | |
4e49c8b8 DS |
273 | |
274 | static void *zebra_nhg_alloc(void *arg) | |
275 | { | |
276 | struct nhg_hash_entry *nhe; | |
277 | struct nhg_hash_entry *copy = arg; | |
a15d4c00 SW |
278 | struct nhg_connected *rb_node_dep = NULL; |
279 | ||
51d80884 | 280 | nhe = XCALLOC(MTYPE_NHG, sizeof(struct nhg_hash_entry)); |
d9f5b2f5 | 281 | |
5bd81e4c | 282 | nhe->id = copy->id; |
0c8215cb | 283 | nhe->nhg_depends = copy->nhg_depends; |
8e401b25 | 284 | |
2d6cd1f0 SW |
285 | nhe->nhg = nexthop_group_new(); |
286 | nexthop_group_copy(nhe->nhg, copy->nhg); | |
3119f6a1 | 287 | |
4e49c8b8 | 288 | nhe->vrf_id = copy->vrf_id; |
77b76fc9 | 289 | nhe->afi = copy->afi; |
4e49c8b8 | 290 | nhe->refcnt = 0; |
9ed6c34a | 291 | nhe->is_kernel_nh = copy->is_kernel_nh; |
4e49c8b8 | 292 | nhe->dplane_ref = zebra_router_get_next_sequence(); |
4e49c8b8 | 293 | |
a15d4c00 | 294 | /* Attach backpointer to anything that it depends on */ |
fe593b78 | 295 | zebra_nhg_dependents_init(nhe); |
a15d4c00 SW |
296 | if (!zebra_nhg_depends_is_empty(nhe)) { |
297 | RB_FOREACH (rb_node_dep, nhg_connected_head, | |
298 | &nhe->nhg_depends) { | |
299 | zebra_nhg_dependents_add(rb_node_dep->nhe, nhe); | |
300 | } | |
301 | } | |
4e49c8b8 | 302 | |
7b683a96 | 303 | /* Add the ifp now if its not a group or recursive and has ifindex */ |
a6e6a6d8 SW |
304 | if (zebra_nhg_depends_is_empty(nhe) && nhe->nhg->nexthop |
305 | && nhe->nhg->nexthop->ifindex) { | |
7b683a96 SW |
306 | struct interface *ifp = NULL; |
307 | ||
a6e6a6d8 SW |
308 | ifp = if_lookup_by_index(nhe->nhg->nexthop->ifindex, |
309 | nhe->vrf_id); | |
7f1abf79 SW |
310 | if (ifp) |
311 | zebra_nhg_set_if(nhe, ifp); | |
312 | else | |
313 | flog_err( | |
314 | EC_ZEBRA_IF_LOOKUP_FAILED, | |
315 | "Zebra failed to lookup an interface with ifindex=%d in vrf=%u for NHE id=%u", | |
316 | nhe->nhg->nexthop->ifindex, nhe->vrf_id, | |
317 | nhe->id); | |
7b683a96 SW |
318 | } |
319 | ||
d9f5b2f5 SW |
320 | /* Add to id table as well */ |
321 | zebra_nhg_insert_id(nhe); | |
322 | ||
4e49c8b8 DS |
323 | return nhe; |
324 | } | |
325 | ||
4e49c8b8 DS |
326 | uint32_t zebra_nhg_hash_key(const void *arg) |
327 | { | |
328 | const struct nhg_hash_entry *nhe = arg; | |
d9f5b2f5 | 329 | |
7286ac02 | 330 | uint32_t key = 0x5a351234; |
4e49c8b8 | 331 | |
77b76fc9 | 332 | key = jhash_2words(nhe->vrf_id, nhe->afi, key); |
4e49c8b8 | 333 | |
0c8215cb | 334 | key = jhash_1word(nexthop_group_hash(nhe->nhg), key); |
d9f5b2f5 | 335 | |
d9f5b2f5 | 336 | return key; |
4e49c8b8 DS |
337 | } |
338 | ||
a95b8020 SW |
339 | uint32_t zebra_nhg_id_key(const void *arg) |
340 | { | |
341 | const struct nhg_hash_entry *nhe = arg; | |
342 | ||
343 | return nhe->id; | |
344 | } | |
345 | ||
4e49c8b8 DS |
346 | bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) |
347 | { | |
348 | const struct nhg_hash_entry *nhe1 = arg1; | |
349 | const struct nhg_hash_entry *nhe2 = arg2; | |
4e49c8b8 | 350 | |
98cda54a SW |
351 | /* No matter what if they equal IDs, assume equal */ |
352 | if (nhe1->id && nhe2->id && (nhe1->id == nhe2->id)) | |
353 | return true; | |
354 | ||
4e49c8b8 DS |
355 | if (nhe1->vrf_id != nhe2->vrf_id) |
356 | return false; | |
357 | ||
77b76fc9 SW |
358 | if (nhe1->afi != nhe2->afi) |
359 | return false; | |
360 | ||
7192bb23 | 361 | if (!nexthop_group_equal(nhe1->nhg, nhe2->nhg)) |
20822f9d | 362 | return false; |
4e49c8b8 | 363 | |
98cda54a SW |
364 | if (nexthop_group_active_nexthop_num_no_recurse(nhe1->nhg) |
365 | != nexthop_group_active_nexthop_num_no_recurse(nhe2->nhg)) | |
366 | return false; | |
367 | ||
4e49c8b8 DS |
368 | return true; |
369 | } | |
370 | ||
d9f5b2f5 | 371 | bool zebra_nhg_hash_id_equal(const void *arg1, const void *arg2) |
4e49c8b8 | 372 | { |
d9f5b2f5 SW |
373 | const struct nhg_hash_entry *nhe1 = arg1; |
374 | const struct nhg_hash_entry *nhe2 = arg2; | |
4e49c8b8 | 375 | |
d9f5b2f5 SW |
376 | return nhe1->id == nhe2->id; |
377 | } | |
4e49c8b8 | 378 | |
3082f99c SW |
379 | static int nhg_connected_cmp(const struct nhg_connected *con1, |
380 | const struct nhg_connected *con2) | |
0c8215cb | 381 | { |
3082f99c | 382 | return (con1->nhe->id - con2->nhe->id); |
0c8215cb SW |
383 | } |
384 | ||
e22e8001 SW |
385 | static void zebra_nhg_process_grp(struct nexthop_group *nhg, |
386 | struct nhg_connected_head *depends, | |
387 | struct nh_grp *grp, uint8_t count) | |
388 | { | |
389 | nhg_connected_head_init(depends); | |
390 | ||
391 | for (int i = 0; i < count; i++) { | |
392 | struct nhg_hash_entry *depend = NULL; | |
393 | /* We do not care about nexthop_grp.weight at | |
394 | * this time. But we should figure out | |
395 | * how to adapt this to our code in | |
396 | * the future. | |
397 | */ | |
398 | depend = zebra_nhg_lookup_id(grp[i].id); | |
399 | if (depend) { | |
400 | nhg_connected_head_add(depends, depend); | |
401 | /* | |
402 | * If this is a nexthop with its own group | |
403 | * dependencies, add them as well. Not sure its | |
404 | * even possible to have a group within a group | |
405 | * in the kernel. | |
406 | */ | |
407 | ||
408 | copy_nexthops(&nhg->nexthop, depend->nhg->nexthop, | |
409 | NULL); | |
410 | } else { | |
411 | flog_err( | |
412 | EC_ZEBRA_NHG_SYNC, | |
413 | "Received Nexthop Group from the kernel with a dependent Nexthop ID (%u) which we do not have in our table", | |
414 | grp[i].id); | |
415 | } | |
416 | } | |
417 | } | |
418 | ||
419 | ||
4505578b SW |
420 | static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id, |
421 | struct nexthop_group *nhg, | |
422 | struct nhg_connected_head *nhg_depends, | |
423 | vrf_id_t vrf_id, afi_t afi, bool is_kernel_nh) | |
a95b8020 | 424 | { |
5bd81e4c SW |
425 | /* id counter to keep in sync with kernel */ |
426 | static uint32_t id_counter = 0; | |
427 | ||
0c8215cb | 428 | struct nhg_hash_entry lookup = {}; |
5bd81e4c | 429 | |
e22e8001 | 430 | uint32_t old_id_counter = id_counter; |
5bd81e4c | 431 | |
4505578b SW |
432 | bool created = false; |
433 | ||
e22e8001 SW |
434 | if (id > id_counter) { |
435 | /* Increase our counter so we don't try to create | |
436 | * an ID that already exists | |
437 | */ | |
438 | id_counter = id; | |
5bd81e4c | 439 | lookup.id = id; |
e22e8001 | 440 | } else |
5bd81e4c | 441 | lookup.id = ++id_counter; |
a95b8020 | 442 | |
77b76fc9 | 443 | lookup.afi = afi; |
e22e8001 | 444 | lookup.vrf_id = vrf_id; |
9ed6c34a | 445 | lookup.is_kernel_nh = is_kernel_nh; |
e22e8001 SW |
446 | lookup.nhg = nhg; |
447 | ||
448 | if (nhg_depends) | |
449 | lookup.nhg_depends = *nhg_depends; | |
a95b8020 | 450 | |
b599cd2a | 451 | if (id) |
4505578b | 452 | (*nhe) = zebra_nhg_lookup_id(id); |
b599cd2a | 453 | else |
4505578b | 454 | (*nhe) = hash_lookup(zrouter.nhgs, &lookup); |
d9f5b2f5 | 455 | |
5bd81e4c | 456 | /* If it found an nhe in our tables, this new ID is unused */ |
4505578b | 457 | if (*nhe) |
5bd81e4c SW |
458 | id_counter = old_id_counter; |
459 | ||
4505578b SW |
460 | if (!(*nhe)) { |
461 | (*nhe) = hash_get(zrouter.nhgs, &lookup, zebra_nhg_alloc); | |
462 | created = true; | |
463 | } | |
d9f5b2f5 | 464 | |
4505578b | 465 | return created; |
a95b8020 SW |
466 | } |
467 | ||
e22e8001 | 468 | /* Find/create a single nexthop */ |
4505578b SW |
469 | static bool zebra_nhg_find_nexthop(struct nhg_hash_entry **nhe, uint32_t id, |
470 | struct nexthop *nh, afi_t afi, | |
471 | bool is_kernel_nh) | |
3057df51 | 472 | { |
e22e8001 SW |
473 | struct nexthop_group nhg = {}; |
474 | ||
475 | _nexthop_group_add_sorted(&nhg, nh); | |
476 | ||
4505578b SW |
477 | return zebra_nhg_find(nhe, id, &nhg, NULL, nh->vrf_id, afi, |
478 | is_kernel_nh); | |
e22e8001 SW |
479 | } |
480 | ||
481 | static struct nhg_ctx *nhg_ctx_new() | |
482 | { | |
483 | struct nhg_ctx *new = NULL; | |
484 | ||
485 | new = XCALLOC(MTYPE_NHG_CTX, sizeof(struct nhg_ctx)); | |
486 | ||
487 | return new; | |
488 | } | |
489 | ||
490 | static void nhg_ctx_free(struct nhg_ctx *ctx) | |
491 | { | |
492 | XFREE(MTYPE_NHG_CTX, ctx); | |
493 | } | |
494 | ||
495 | static void nhg_ctx_set_status(struct nhg_ctx *ctx, enum nhg_ctx_result status) | |
496 | { | |
497 | ctx->status = status; | |
498 | } | |
499 | ||
500 | static enum nhg_ctx_result nhg_ctx_get_status(const struct nhg_ctx *ctx) | |
501 | { | |
502 | return ctx->status; | |
503 | } | |
504 | ||
505 | static void nhg_ctx_set_op(struct nhg_ctx *ctx, enum nhg_ctx_op_e op) | |
506 | { | |
507 | ctx->op = op; | |
508 | } | |
509 | ||
510 | static enum nhg_ctx_op_e nhg_ctx_get_op(const struct nhg_ctx *ctx) | |
511 | { | |
512 | return ctx->op; | |
513 | } | |
514 | ||
515 | static int nhg_ctx_process_new(struct nhg_ctx *ctx) | |
516 | { | |
517 | struct nexthop_group *nhg = NULL; | |
518 | struct nhg_connected_head nhg_depends = {}; | |
3057df51 SW |
519 | struct nhg_hash_entry *nhe = NULL; |
520 | ||
e22e8001 SW |
521 | if (ctx->count) { |
522 | nhg = nexthop_group_new(); | |
523 | zebra_nhg_process_grp(nhg, &nhg_depends, ctx->u.grp, | |
524 | ctx->count); | |
4505578b SW |
525 | if (!zebra_nhg_find(&nhe, ctx->id, nhg, &nhg_depends, |
526 | ctx->vrf_id, ctx->afi, true)) | |
527 | nhg_connected_head_free(&nhg_depends); | |
528 | ||
e22e8001 SW |
529 | /* These got copied over in zebra_nhg_alloc() */ |
530 | nexthop_group_free_delete(&nhg); | |
4505578b SW |
531 | } else if (!zebra_nhg_find_nexthop(&nhe, ctx->id, &ctx->u.nh, ctx->afi, |
532 | ctx->is_kernel_nh)) | |
533 | nhg_connected_head_free(&nhg_depends); | |
e22e8001 SW |
534 | |
535 | if (nhe) { | |
536 | if (ctx->id != nhe->id) | |
537 | /* Duplicate but with different ID from | |
2d3c57e6 SW |
538 | * the kernel |
539 | */ | |
e22e8001 SW |
540 | |
541 | /* The kernel allows duplicate nexthops | |
542 | * as long as they have different IDs. | |
543 | * We are ignoring those to prevent | |
544 | * syncing problems with the kernel | |
545 | * changes. | |
546 | */ | |
547 | flog_warn( | |
548 | EC_ZEBRA_DUPLICATE_NHG_MESSAGE, | |
549 | "Nexthop Group with ID (%d) is a duplicate, ignoring", | |
550 | ctx->id); | |
551 | else { | |
552 | /* It actually created a new nhe */ | |
553 | if (nhe->is_kernel_nh) { | |
554 | SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); | |
555 | SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); | |
556 | } | |
557 | } | |
558 | } else { | |
559 | flog_err( | |
560 | EC_ZEBRA_TABLE_LOOKUP_FAILED, | |
561 | "Zebra failed to find or create a nexthop hash entry for ID (%u)", | |
562 | ctx->id); | |
563 | return -1; | |
564 | } | |
565 | ||
566 | return 0; | |
567 | } | |
568 | ||
569 | static void nhg_ctx_process_finish(struct nhg_ctx *ctx) | |
570 | { | |
571 | /* | |
572 | * Just freeing for now, maybe do something more in the future | |
573 | * based on flag. | |
574 | */ | |
575 | ||
576 | if (ctx) | |
577 | nhg_ctx_free(ctx); | |
578 | } | |
579 | ||
580 | int nhg_ctx_process(struct nhg_ctx *ctx) | |
581 | { | |
582 | int ret = 0; | |
583 | ||
584 | switch (nhg_ctx_get_op(ctx)) { | |
585 | case NHG_CTX_OP_NEW: | |
586 | ret = nhg_ctx_process_new(ctx); | |
587 | break; | |
588 | case NHG_CTX_OP_DEL: | |
589 | case NHG_CTX_OP_NONE: | |
590 | break; | |
591 | } | |
592 | ||
593 | nhg_ctx_set_status(ctx, (ret ? NHG_CTX_FAILURE : NHG_CTX_SUCCESS)); | |
594 | ||
595 | nhg_ctx_process_finish(ctx); | |
596 | ||
597 | return ret; | |
598 | } | |
3057df51 | 599 | |
e22e8001 SW |
600 | static int queue_add(struct nhg_ctx *ctx) |
601 | { | |
602 | /* If its queued or already processed do nothing */ | |
603 | if (nhg_ctx_get_status(ctx)) | |
604 | return 0; | |
605 | ||
606 | if (rib_queue_nhg_add(ctx)) { | |
607 | nhg_ctx_set_status(ctx, NHG_CTX_FAILURE); | |
608 | return -1; | |
609 | } | |
610 | ||
611 | nhg_ctx_set_status(ctx, NHG_CTX_QUEUED); | |
612 | ||
613 | return 0; | |
614 | } | |
615 | ||
616 | /* Kernel-side, you either get a single new nexthop or a array of ID's */ | |
617 | int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, struct nh_grp *grp, | |
618 | uint8_t count, vrf_id_t vrf_id, afi_t afi) | |
619 | { | |
620 | // TODO: Can probably put table lookup | |
621 | // here before queueing? And if deleted, re-send to kernel? | |
622 | // ... Well, if changing the flags it probably needs to be queued | |
623 | // still... | |
3057df51 | 624 | |
e22e8001 SW |
625 | struct nhg_ctx *ctx = NULL; |
626 | ||
627 | ctx = nhg_ctx_new(); | |
628 | ||
629 | ctx->id = id; | |
630 | ctx->vrf_id = vrf_id; | |
631 | ctx->afi = afi; | |
632 | ctx->is_kernel_nh = true; | |
633 | ctx->count = count; | |
634 | ||
635 | if (count) | |
636 | /* Copy over the array */ | |
637 | memcpy(&ctx->u.grp, grp, count * sizeof(struct nh_grp)); | |
638 | else | |
639 | ctx->u.nh = *nh; | |
640 | ||
641 | nhg_ctx_set_op(ctx, NHG_CTX_OP_NEW); | |
642 | ||
643 | if (queue_add(ctx)) { | |
644 | nhg_ctx_process_finish(ctx); | |
645 | return -1; | |
646 | } | |
647 | ||
648 | return 0; | |
649 | } | |
650 | ||
98cda54a SW |
651 | static struct nhg_hash_entry *depends_find(struct nexthop *nh, afi_t afi) |
652 | { | |
653 | struct nexthop lookup = {0}; | |
4505578b | 654 | struct nhg_hash_entry *nhe = NULL; |
98cda54a SW |
655 | |
656 | lookup = *nh; | |
657 | /* Clear it, in case its a group */ | |
658 | lookup.next = NULL; | |
659 | lookup.prev = NULL; | |
4505578b SW |
660 | zebra_nhg_find_nexthop(&nhe, 0, &lookup, afi, false); |
661 | ||
662 | return nhe; | |
98cda54a SW |
663 | } |
664 | ||
e22e8001 SW |
665 | /* Rib-side, you get a nexthop group struct */ |
666 | struct nhg_hash_entry *zebra_nhg_rib_find(uint32_t id, | |
667 | struct nexthop_group *nhg, | |
668 | vrf_id_t rt_vrf_id, afi_t rt_afi) | |
669 | { | |
670 | struct nhg_hash_entry *nhe = NULL; | |
98cda54a | 671 | struct nhg_hash_entry *depend = NULL; |
e22e8001 | 672 | struct nhg_connected_head nhg_depends = {}; |
98cda54a | 673 | |
2d3c57e6 | 674 | /* Defualt the nhe to the afi and vrf of the route */ |
e22e8001 SW |
675 | afi_t nhg_afi = rt_afi; |
676 | vrf_id_t nhg_vrf_id = rt_vrf_id; | |
677 | ||
98cda54a SW |
678 | if (!nhg) { |
679 | flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED, | |
2d3c57e6 | 680 | "No nexthop passed to %s", __func__); |
98cda54a SW |
681 | return NULL; |
682 | } | |
e22e8001 | 683 | |
98cda54a | 684 | if (nhg->nexthop->next) { |
e22e8001 SW |
685 | nhg_connected_head_init(&nhg_depends); |
686 | ||
98cda54a SW |
687 | /* If its a group, create a dependency tree */ |
688 | struct nexthop *nh = NULL; | |
689 | ||
690 | for (nh = nhg->nexthop; nh; nh = nh->next) { | |
691 | depend = depends_find(nh, rt_afi); | |
e22e8001 SW |
692 | nhg_connected_head_add(&nhg_depends, depend); |
693 | } | |
694 | ||
695 | /* change the afi/vrf_id since its a group */ | |
696 | nhg_afi = AFI_UNSPEC; | |
697 | nhg_vrf_id = 0; | |
4505578b SW |
698 | } else { |
699 | /* | |
700 | * If the vrf_id on the nexthop does not match | |
701 | * the route one, use it instead. | |
702 | */ | |
703 | vrf_id_t nh_vrf_id = nhg->nexthop->vrf_id; | |
704 | ||
705 | if (nh_vrf_id && nh_vrf_id != rt_vrf_id) | |
706 | nhg_vrf_id = nh_vrf_id; | |
e22e8001 SW |
707 | } |
708 | ||
4505578b SW |
709 | if (!zebra_nhg_find(&nhe, id, nhg, &nhg_depends, nhg_vrf_id, nhg_afi, |
710 | false)) | |
711 | nhg_connected_head_free(&nhg_depends); | |
712 | ||
3057df51 SW |
713 | return nhe; |
714 | } | |
715 | ||
b599cd2a SW |
716 | void zebra_nhg_free_members(struct nhg_hash_entry *nhe) |
717 | { | |
ddaee0c7 SW |
718 | nexthop_group_free_delete(&nhe->nhg); |
719 | nhg_connected_head_free(&nhe->nhg_depends); | |
fe593b78 | 720 | nhg_connected_head_free(&nhe->nhg_dependents); |
b599cd2a SW |
721 | } |
722 | ||
d9f5b2f5 | 723 | void zebra_nhg_free(void *arg) |
a95b8020 | 724 | { |
d9f5b2f5 | 725 | struct nhg_hash_entry *nhe = NULL; |
a95b8020 | 726 | |
d9f5b2f5 | 727 | nhe = (struct nhg_hash_entry *)arg; |
a95b8020 | 728 | |
8e401b25 | 729 | zebra_nhg_free_members(nhe); |
51d80884 SW |
730 | |
731 | XFREE(MTYPE_NHG, nhe); | |
a95b8020 SW |
732 | } |
733 | ||
e22e8001 | 734 | static void zebra_nhg_release(struct nhg_hash_entry *nhe) |
4e49c8b8 | 735 | { |
6ccc2b28 SW |
736 | zlog_debug("Releasing nexthop group with ID (%u)", nhe->id); |
737 | ||
738 | /* Remove it from any lists it may be on */ | |
739 | zebra_nhg_depends_release(nhe); | |
740 | zebra_nhg_dependents_release(nhe); | |
741 | if (nhe->ifp) | |
742 | if_nhg_dependents_del(nhe->ifp, nhe); | |
743 | ||
744 | hash_release(zrouter.nhgs, nhe); | |
745 | hash_release(zrouter.nhgs_id, nhe); | |
e22e8001 | 746 | |
6ccc2b28 | 747 | zebra_nhg_free(nhe); |
7512f617 | 748 | } |
4e49c8b8 | 749 | |
d9f5b2f5 SW |
750 | void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe) |
751 | { | |
e22e8001 SW |
752 | nhe->refcnt--; |
753 | ||
0c8215cb | 754 | if (!zebra_nhg_depends_is_empty(nhe)) { |
a15d4c00 | 755 | struct nhg_connected *rb_node_dep = NULL; |
98cda54a | 756 | struct nhg_connected *tmp = NULL; |
f54ef6a5 | 757 | |
98cda54a SW |
758 | RB_FOREACH_SAFE (rb_node_dep, nhg_connected_head, |
759 | &nhe->nhg_depends, tmp) { | |
0c8215cb | 760 | zebra_nhg_decrement_ref(rb_node_dep->nhe); |
f54ef6a5 SW |
761 | } |
762 | } | |
763 | ||
e22e8001 | 764 | if (!nhe->is_kernel_nh && nhe->refcnt <= 0) |
cb50cbc9 | 765 | zebra_nhg_uninstall_kernel(nhe); |
7fd392cc SW |
766 | } |
767 | ||
7fd392cc SW |
768 | void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe) |
769 | { | |
e22e8001 SW |
770 | nhe->refcnt++; |
771 | ||
0c8215cb | 772 | if (!zebra_nhg_depends_is_empty(nhe)) { |
a15d4c00 | 773 | struct nhg_connected *rb_node_dep = NULL; |
7fd392cc | 774 | |
a15d4c00 SW |
775 | RB_FOREACH (rb_node_dep, nhg_connected_head, |
776 | &nhe->nhg_depends) { | |
0c8215cb | 777 | zebra_nhg_increment_ref(rb_node_dep->nhe); |
7fd392cc SW |
778 | } |
779 | } | |
e22e8001 | 780 | } |
d9f5b2f5 | 781 | |
fe593b78 SW |
782 | void zebra_nhg_set_invalid(struct nhg_hash_entry *nhe) |
783 | { | |
055a3fa6 SW |
784 | if (!zebra_nhg_depends_is_empty(nhe) |
785 | && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE)) { | |
786 | struct nhg_connected *rb_node_dep = NULL; | |
787 | ||
788 | /* If anthing else in the group is valid, the group is valid */ | |
789 | RB_FOREACH (rb_node_dep, nhg_connected_head, | |
790 | &nhe->nhg_dependents) { | |
791 | if (CHECK_FLAG(rb_node_dep->nhe->flags, | |
792 | NEXTHOP_GROUP_VALID)) | |
793 | return; | |
794 | } | |
795 | } | |
796 | ||
e22e8001 SW |
797 | UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); |
798 | /* Assuming uninstalled as well here */ | |
799 | UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); | |
fe593b78 SW |
800 | |
801 | if (!zebra_nhg_dependents_is_empty(nhe)) { | |
802 | struct nhg_connected *rb_node_dep = NULL; | |
803 | ||
804 | RB_FOREACH (rb_node_dep, nhg_connected_head, | |
805 | &nhe->nhg_dependents) { | |
806 | zebra_nhg_set_invalid(rb_node_dep->nhe); | |
807 | } | |
808 | } | |
fe593b78 SW |
809 | } |
810 | ||
811 | void zebra_nhg_set_if(struct nhg_hash_entry *nhe, struct interface *ifp) | |
812 | { | |
813 | nhe->ifp = ifp; | |
814 | if_nhg_dependents_add(ifp, nhe); | |
815 | } | |
816 | ||
ad28e79a SW |
817 | static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, |
818 | struct nexthop *nexthop) | |
819 | { | |
820 | struct nexthop *resolved_hop; | |
b43434ad SW |
821 | uint8_t num_labels = 0; |
822 | mpls_label_t labels[MPLS_MAX_LABELS]; | |
823 | enum lsp_types_t label_type = ZEBRA_LSP_NONE; | |
824 | int i = 0; | |
ad28e79a SW |
825 | |
826 | resolved_hop = nexthop_new(); | |
827 | SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); | |
828 | ||
829 | resolved_hop->vrf_id = nexthop->vrf_id; | |
830 | switch (newhop->type) { | |
831 | case NEXTHOP_TYPE_IPV4: | |
832 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
833 | /* If the resolving route specifies a gateway, use it */ | |
834 | resolved_hop->type = newhop->type; | |
835 | resolved_hop->gate.ipv4 = newhop->gate.ipv4; | |
836 | ||
837 | if (newhop->ifindex) { | |
838 | resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; | |
839 | resolved_hop->ifindex = newhop->ifindex; | |
840 | } | |
841 | break; | |
842 | case NEXTHOP_TYPE_IPV6: | |
843 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
844 | resolved_hop->type = newhop->type; | |
845 | resolved_hop->gate.ipv6 = newhop->gate.ipv6; | |
846 | ||
847 | if (newhop->ifindex) { | |
848 | resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; | |
849 | resolved_hop->ifindex = newhop->ifindex; | |
850 | } | |
851 | break; | |
852 | case NEXTHOP_TYPE_IFINDEX: | |
853 | /* If the resolving route is an interface route, | |
854 | * it means the gateway we are looking up is connected | |
855 | * to that interface. (The actual network is _not_ onlink). | |
856 | * Therefore, the resolved route should have the original | |
857 | * gateway as nexthop as it is directly connected. | |
858 | * | |
859 | * On Linux, we have to set the onlink netlink flag because | |
860 | * otherwise, the kernel won't accept the route. | |
861 | */ | |
862 | resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; | |
863 | if (afi == AFI_IP) { | |
864 | resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; | |
865 | resolved_hop->gate.ipv4 = nexthop->gate.ipv4; | |
866 | } else if (afi == AFI_IP6) { | |
867 | resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; | |
868 | resolved_hop->gate.ipv6 = nexthop->gate.ipv6; | |
869 | } | |
870 | resolved_hop->ifindex = newhop->ifindex; | |
871 | break; | |
872 | case NEXTHOP_TYPE_BLACKHOLE: | |
873 | resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE; | |
2dc359a6 | 874 | resolved_hop->bh_type = newhop->bh_type; |
ad28e79a SW |
875 | break; |
876 | } | |
877 | ||
878 | if (newhop->flags & NEXTHOP_FLAG_ONLINK) | |
879 | resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; | |
880 | ||
b43434ad SW |
881 | /* Copy labels of the resolved route and the parent resolving to it */ |
882 | if (newhop->nh_label) { | |
883 | for (i = 0; i < newhop->nh_label->num_labels; i++) | |
884 | labels[num_labels++] = newhop->nh_label->label[i]; | |
885 | label_type = newhop->nh_label_type; | |
886 | } | |
887 | ||
888 | if (nexthop->nh_label) { | |
889 | for (i = 0; i < nexthop->nh_label->num_labels; i++) | |
890 | labels[num_labels++] = nexthop->nh_label->label[i]; | |
891 | ||
892 | /* If the parent has labels, use its type */ | |
893 | label_type = nexthop->nh_label_type; | |
894 | } | |
895 | ||
896 | if (num_labels) | |
897 | nexthop_add_labels(resolved_hop, label_type, num_labels, | |
898 | labels); | |
ad28e79a SW |
899 | |
900 | resolved_hop->rparent = nexthop; | |
50d89650 | 901 | _nexthop_add(&nexthop->resolved, resolved_hop); |
ad28e79a SW |
902 | } |
903 | ||
6913cb1b SW |
904 | /* Checks if nexthop we are trying to resolve to is valid */ |
905 | static bool nexthop_valid_resolve(const struct nexthop *nexthop, | |
906 | const struct nexthop *resolved) | |
907 | { | |
908 | /* Can't resolve to a recursive nexthop */ | |
909 | if (CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_RECURSIVE)) | |
910 | return false; | |
911 | ||
912 | switch (nexthop->type) { | |
913 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
914 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
915 | /* If the nexthop we are resolving to does not match the | |
916 | * ifindex for the nexthop the route wanted, its not valid. | |
917 | */ | |
918 | if (nexthop->ifindex != resolved->ifindex) | |
919 | return false; | |
920 | break; | |
921 | case NEXTHOP_TYPE_IPV4: | |
922 | case NEXTHOP_TYPE_IPV6: | |
923 | case NEXTHOP_TYPE_IFINDEX: | |
924 | case NEXTHOP_TYPE_BLACKHOLE: | |
925 | break; | |
926 | } | |
927 | ||
928 | return true; | |
929 | } | |
930 | ||
ad28e79a SW |
931 | /* |
932 | * Given a nexthop we need to properly recursively resolve | |
933 | * the route. As such, do a table lookup to find and match | |
98cda54a SW |
934 | * if at all possible. Set the nexthop->ifindex and resolved_id |
935 | * as appropriate | |
ad28e79a SW |
936 | */ |
937 | static int nexthop_active(afi_t afi, struct route_entry *re, | |
98cda54a SW |
938 | struct nexthop *nexthop, struct route_node *top, |
939 | uint32_t *resolved_id) | |
ad28e79a SW |
940 | { |
941 | struct prefix p; | |
942 | struct route_table *table; | |
943 | struct route_node *rn; | |
944 | struct route_entry *match = NULL; | |
945 | int resolved; | |
946 | struct nexthop *newhop; | |
947 | struct interface *ifp; | |
948 | rib_dest_t *dest; | |
5a0bdc78 | 949 | struct zebra_vrf *zvrf; |
ad28e79a SW |
950 | |
951 | if ((nexthop->type == NEXTHOP_TYPE_IPV4) | |
952 | || nexthop->type == NEXTHOP_TYPE_IPV6) | |
953 | nexthop->ifindex = 0; | |
954 | ||
955 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); | |
956 | nexthops_free(nexthop->resolved); | |
957 | nexthop->resolved = NULL; | |
958 | re->nexthop_mtu = 0; | |
959 | ||
960 | /* | |
a8c427ee | 961 | * If the kernel has sent us a NEW route, then |
ad28e79a | 962 | * by golly gee whiz it's a good route. |
a8c427ee SW |
963 | * |
964 | * If its an already INSTALLED route we have already handled, then the | |
965 | * kernel route's nexthop might have became unreachable | |
966 | * and we have to handle that. | |
ad28e79a | 967 | */ |
a8c427ee SW |
968 | if (!CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED) |
969 | && (re->type == ZEBRA_ROUTE_KERNEL | |
970 | || re->type == ZEBRA_ROUTE_SYSTEM)) | |
ad28e79a SW |
971 | return 1; |
972 | ||
973 | /* | |
974 | * Check to see if we should trust the passed in information | |
975 | * for UNNUMBERED interfaces as that we won't find the GW | |
976 | * address in the routing table. | |
977 | * This check should suffice to handle IPv4 or IPv6 routes | |
978 | * sourced from EVPN routes which are installed with the | |
979 | * next hop as the remote VTEP IP. | |
980 | */ | |
981 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) { | |
982 | ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); | |
983 | if (!ifp) { | |
984 | if (IS_ZEBRA_DEBUG_RIB_DETAILED) | |
985 | zlog_debug( | |
986 | "\t%s: Onlink and interface: %u[%u] does not exist", | |
987 | __PRETTY_FUNCTION__, nexthop->ifindex, | |
988 | nexthop->vrf_id); | |
989 | return 0; | |
990 | } | |
991 | if (connected_is_unnumbered(ifp)) { | |
992 | if (if_is_operative(ifp)) | |
993 | return 1; | |
2d3c57e6 SW |
994 | |
995 | if (IS_ZEBRA_DEBUG_RIB_DETAILED) | |
996 | zlog_debug( | |
997 | "\t%s: Onlink and interface %s is not operative", | |
998 | __PRETTY_FUNCTION__, ifp->name); | |
999 | return 0; | |
ad28e79a SW |
1000 | } |
1001 | if (!if_is_operative(ifp)) { | |
1002 | if (IS_ZEBRA_DEBUG_RIB_DETAILED) | |
1003 | zlog_debug( | |
1004 | "\t%s: Interface %s is not unnumbered", | |
1005 | __PRETTY_FUNCTION__, ifp->name); | |
1006 | return 0; | |
1007 | } | |
1008 | } | |
1009 | ||
1010 | /* Make lookup prefix. */ | |
1011 | memset(&p, 0, sizeof(struct prefix)); | |
1012 | switch (afi) { | |
1013 | case AFI_IP: | |
1014 | p.family = AF_INET; | |
1015 | p.prefixlen = IPV4_MAX_PREFIXLEN; | |
1016 | p.u.prefix4 = nexthop->gate.ipv4; | |
1017 | break; | |
1018 | case AFI_IP6: | |
1019 | p.family = AF_INET6; | |
1020 | p.prefixlen = IPV6_MAX_PREFIXLEN; | |
1021 | p.u.prefix6 = nexthop->gate.ipv6; | |
1022 | break; | |
1023 | default: | |
1024 | assert(afi != AFI_IP && afi != AFI_IP6); | |
1025 | break; | |
1026 | } | |
1027 | /* Lookup table. */ | |
1028 | table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id); | |
5a0bdc78 PG |
1029 | /* get zvrf */ |
1030 | zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); | |
1031 | if (!table || !zvrf) { | |
ad28e79a SW |
1032 | if (IS_ZEBRA_DEBUG_RIB_DETAILED) |
1033 | zlog_debug("\t%s: Table not found", | |
1034 | __PRETTY_FUNCTION__); | |
1035 | return 0; | |
1036 | } | |
1037 | ||
1038 | rn = route_node_match(table, (struct prefix *)&p); | |
1039 | while (rn) { | |
1040 | route_unlock_node(rn); | |
1041 | ||
1042 | /* Lookup should halt if we've matched against ourselves ('top', | |
1043 | * if specified) - i.e., we cannot have a nexthop NH1 is | |
1044 | * resolved by a route NH1. The exception is if the route is a | |
1045 | * host route. | |
1046 | */ | |
1047 | if (top && rn == top) | |
1048 | if (((afi == AFI_IP) && (rn->p.prefixlen != 32)) | |
1049 | || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) { | |
1050 | if (IS_ZEBRA_DEBUG_RIB_DETAILED) | |
1051 | zlog_debug( | |
1052 | "\t%s: Matched against ourself and prefix length is not max bit length", | |
1053 | __PRETTY_FUNCTION__); | |
1054 | return 0; | |
1055 | } | |
1056 | ||
1057 | /* Pick up selected route. */ | |
1058 | /* However, do not resolve over default route unless explicitly | |
2d3c57e6 SW |
1059 | * allowed. |
1060 | */ | |
ad28e79a | 1061 | if (is_default_prefix(&rn->p) |
5a0bdc78 | 1062 | && !rnh_resolve_via_default(zvrf, p.family)) { |
ad28e79a SW |
1063 | if (IS_ZEBRA_DEBUG_RIB_DETAILED) |
1064 | zlog_debug( | |
1065 | "\t:%s: Resolved against default route", | |
1066 | __PRETTY_FUNCTION__); | |
1067 | return 0; | |
1068 | } | |
1069 | ||
1070 | dest = rib_dest_from_rnode(rn); | |
1071 | if (dest && dest->selected_fib | |
1072 | && !CHECK_FLAG(dest->selected_fib->status, | |
1073 | ROUTE_ENTRY_REMOVED) | |
1074 | && dest->selected_fib->type != ZEBRA_ROUTE_TABLE) | |
1075 | match = dest->selected_fib; | |
1076 | ||
1077 | /* If there is no selected route or matched route is EGP, go up | |
2d3c57e6 SW |
1078 | * tree. |
1079 | */ | |
ad28e79a SW |
1080 | if (!match) { |
1081 | do { | |
1082 | rn = rn->parent; | |
1083 | } while (rn && rn->info == NULL); | |
1084 | if (rn) | |
1085 | route_lock_node(rn); | |
1086 | ||
1087 | continue; | |
1088 | } | |
1089 | ||
1090 | if (match->type == ZEBRA_ROUTE_CONNECT) { | |
1091 | /* Directly point connected route. */ | |
6b468511 | 1092 | newhop = match->ng->nexthop; |
ad28e79a SW |
1093 | if (newhop) { |
1094 | if (nexthop->type == NEXTHOP_TYPE_IPV4 | |
1095 | || nexthop->type == NEXTHOP_TYPE_IPV6) | |
1096 | nexthop->ifindex = newhop->ifindex; | |
1097 | } | |
1098 | return 1; | |
1099 | } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { | |
1100 | resolved = 0; | |
6b468511 | 1101 | for (ALL_NEXTHOPS_PTR(match->ng, newhop)) { |
ad28e79a SW |
1102 | if (!CHECK_FLAG(match->status, |
1103 | ROUTE_ENTRY_INSTALLED)) | |
1104 | continue; | |
6913cb1b | 1105 | if (!nexthop_valid_resolve(nexthop, newhop)) |
ad28e79a SW |
1106 | continue; |
1107 | ||
1108 | SET_FLAG(nexthop->flags, | |
1109 | NEXTHOP_FLAG_RECURSIVE); | |
ad28e79a SW |
1110 | nexthop_set_resolved(afi, newhop, nexthop); |
1111 | resolved = 1; | |
1112 | } | |
98cda54a | 1113 | if (resolved) { |
ad28e79a | 1114 | re->nexthop_mtu = match->mtu; |
98cda54a SW |
1115 | *resolved_id = match->nhe_id; |
1116 | } | |
ad28e79a SW |
1117 | if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) |
1118 | zlog_debug("\t%s: Recursion failed to find", | |
1119 | __PRETTY_FUNCTION__); | |
1120 | return resolved; | |
1121 | } else if (re->type == ZEBRA_ROUTE_STATIC) { | |
1122 | resolved = 0; | |
6b468511 | 1123 | for (ALL_NEXTHOPS_PTR(match->ng, newhop)) { |
ad28e79a SW |
1124 | if (!CHECK_FLAG(match->status, |
1125 | ROUTE_ENTRY_INSTALLED)) | |
1126 | continue; | |
6913cb1b | 1127 | if (!nexthop_valid_resolve(nexthop, newhop)) |
ad28e79a SW |
1128 | continue; |
1129 | ||
1130 | SET_FLAG(nexthop->flags, | |
1131 | NEXTHOP_FLAG_RECURSIVE); | |
1132 | nexthop_set_resolved(afi, newhop, nexthop); | |
1133 | resolved = 1; | |
1134 | } | |
98cda54a | 1135 | if (resolved) { |
ad28e79a | 1136 | re->nexthop_mtu = match->mtu; |
98cda54a SW |
1137 | *resolved_id = match->nhe_id; |
1138 | } | |
ad28e79a SW |
1139 | if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) |
1140 | zlog_debug( | |
1141 | "\t%s: Static route unable to resolve", | |
1142 | __PRETTY_FUNCTION__); | |
1143 | return resolved; | |
1144 | } else { | |
1145 | if (IS_ZEBRA_DEBUG_RIB_DETAILED) { | |
1146 | zlog_debug( | |
1147 | "\t%s: Route Type %s has not turned on recursion", | |
1148 | __PRETTY_FUNCTION__, | |
1149 | zebra_route_string(re->type)); | |
1150 | if (re->type == ZEBRA_ROUTE_BGP | |
1151 | && !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP)) | |
1152 | zlog_debug( | |
1153 | "\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\""); | |
1154 | } | |
1155 | return 0; | |
1156 | } | |
1157 | } | |
1158 | if (IS_ZEBRA_DEBUG_RIB_DETAILED) | |
1159 | zlog_debug("\t%s: Nexthop did not lookup in table", | |
1160 | __PRETTY_FUNCTION__); | |
1161 | return 0; | |
1162 | } | |
1163 | ||
1164 | /* This function verifies reachability of one given nexthop, which can be | |
1165 | * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored | |
1166 | * in nexthop->flags field. The nexthop->ifindex will be updated | |
1167 | * appropriately as well. An existing route map can turn | |
1168 | * (otherwise active) nexthop into inactive, but not vice versa. | |
1169 | * | |
98cda54a SW |
1170 | * If it finds a nexthop recursivedly, set the resolved_id |
1171 | * to match that nexthop's nhg_hash_entry ID; | |
1172 | * | |
ad28e79a SW |
1173 | * The return value is the final value of 'ACTIVE' flag. |
1174 | */ | |
1175 | static unsigned nexthop_active_check(struct route_node *rn, | |
1176 | struct route_entry *re, | |
98cda54a SW |
1177 | struct nexthop *nexthop, |
1178 | uint32_t *resolved_id) | |
ad28e79a SW |
1179 | { |
1180 | struct interface *ifp; | |
b68885f9 | 1181 | route_map_result_t ret = RMAP_PERMITMATCH; |
ad28e79a SW |
1182 | int family; |
1183 | char buf[SRCDEST2STR_BUFFER]; | |
1184 | const struct prefix *p, *src_p; | |
1185 | struct zebra_vrf *zvrf; | |
1186 | ||
1187 | srcdest_rnode_prefixes(rn, &p, &src_p); | |
1188 | ||
1189 | if (rn->p.family == AF_INET) | |
1190 | family = AFI_IP; | |
1191 | else if (rn->p.family == AF_INET6) | |
1192 | family = AFI_IP6; | |
1193 | else | |
1194 | family = 0; | |
1195 | switch (nexthop->type) { | |
1196 | case NEXTHOP_TYPE_IFINDEX: | |
1197 | ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); | |
1198 | if (ifp && if_is_operative(ifp)) | |
1199 | SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1200 | else | |
1201 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1202 | break; | |
1203 | case NEXTHOP_TYPE_IPV4: | |
1204 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
1205 | family = AFI_IP; | |
98cda54a | 1206 | if (nexthop_active(AFI_IP, re, nexthop, rn, resolved_id)) |
ad28e79a SW |
1207 | SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); |
1208 | else | |
1209 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1210 | break; | |
1211 | case NEXTHOP_TYPE_IPV6: | |
1212 | family = AFI_IP6; | |
98cda54a | 1213 | if (nexthop_active(AFI_IP6, re, nexthop, rn, resolved_id)) |
ad28e79a SW |
1214 | SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); |
1215 | else | |
1216 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1217 | break; | |
1218 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1219 | /* RFC 5549, v4 prefix with v6 NH */ | |
1220 | if (rn->p.family != AF_INET) | |
1221 | family = AFI_IP6; | |
1222 | if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) { | |
1223 | ifp = if_lookup_by_index(nexthop->ifindex, | |
1224 | nexthop->vrf_id); | |
1225 | if (ifp && if_is_operative(ifp)) | |
1226 | SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1227 | else | |
1228 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1229 | } else { | |
98cda54a SW |
1230 | if (nexthop_active(AFI_IP6, re, nexthop, rn, |
1231 | resolved_id)) | |
ad28e79a SW |
1232 | SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); |
1233 | else | |
1234 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1235 | } | |
1236 | break; | |
1237 | case NEXTHOP_TYPE_BLACKHOLE: | |
1238 | SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1239 | break; | |
1240 | default: | |
1241 | break; | |
1242 | } | |
1243 | if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { | |
1244 | if (IS_ZEBRA_DEBUG_RIB_DETAILED) | |
1245 | zlog_debug("\t%s: Unable to find a active nexthop", | |
1246 | __PRETTY_FUNCTION__); | |
1247 | return 0; | |
1248 | } | |
1249 | ||
1250 | /* XXX: What exactly do those checks do? Do we support | |
1251 | * e.g. IPv4 routes with IPv6 nexthops or vice versa? | |
1252 | */ | |
1253 | if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET) | |
1254 | || (family == AFI_IP6 && p->family != AF_INET6)) | |
1255 | return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1256 | ||
1257 | /* The original code didn't determine the family correctly | |
1258 | * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi | |
1259 | * from the rib_table_info in those cases. | |
1260 | * Possibly it may be better to use only the rib_table_info | |
1261 | * in every case. | |
1262 | */ | |
1263 | if (!family) { | |
1264 | rib_table_info_t *info; | |
1265 | ||
1266 | info = srcdest_rnode_table_info(rn); | |
1267 | family = info->afi; | |
1268 | } | |
1269 | ||
1270 | memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr)); | |
1271 | ||
1272 | zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); | |
1273 | if (!zvrf) { | |
1274 | if (IS_ZEBRA_DEBUG_RIB_DETAILED) | |
1275 | zlog_debug("\t%s: zvrf is NULL", __PRETTY_FUNCTION__); | |
1276 | return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1277 | } | |
1278 | ||
1279 | /* It'll get set if required inside */ | |
1280 | ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop, | |
1281 | zvrf, re->tag); | |
1282 | if (ret == RMAP_DENYMATCH) { | |
1283 | if (IS_ZEBRA_DEBUG_RIB) { | |
1284 | srcdest_rnode2str(rn, buf, sizeof(buf)); | |
1285 | zlog_debug( | |
1286 | "%u:%s: Filtering out with NH out %s due to route map", | |
1287 | re->vrf_id, buf, | |
1288 | ifindex2ifname(nexthop->ifindex, | |
1289 | nexthop->vrf_id)); | |
1290 | } | |
1291 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1292 | } | |
1293 | return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1294 | } | |
1295 | ||
1296 | /* | |
1297 | * Iterate over all nexthops of the given RIB entry and refresh their | |
9a0d4dd3 DS |
1298 | * ACTIVE flag. If any nexthop is found to toggle the ACTIVE flag, |
1299 | * the whole re structure is flagged with ROUTE_ENTRY_CHANGED. | |
ad28e79a SW |
1300 | * |
1301 | * Return value is the new number of active nexthops. | |
1302 | */ | |
1303 | int nexthop_active_update(struct route_node *rn, struct route_entry *re) | |
1304 | { | |
98cda54a | 1305 | struct nexthop_group new_grp = {}; |
ad28e79a SW |
1306 | struct nexthop *nexthop; |
1307 | union g_addr prev_src; | |
1308 | unsigned int prev_active, new_active; | |
1309 | ifindex_t prev_index; | |
9a0d4dd3 | 1310 | uint8_t curr_active = 0; |
e22e8001 | 1311 | |
98cda54a | 1312 | afi_t rt_afi = family2afi(rn->p.family); |
e22e8001 | 1313 | |
98cda54a | 1314 | UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); |
e22e8001 | 1315 | |
98cda54a SW |
1316 | /* Copy over the nexthops in current state */ |
1317 | nexthop_group_copy(&new_grp, re->ng); | |
ad28e79a | 1318 | |
98cda54a SW |
1319 | for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) { |
1320 | struct nhg_hash_entry *nhe = NULL; | |
1321 | uint32_t resolved_id = 0; | |
ad28e79a | 1322 | |
ad28e79a SW |
1323 | /* No protocol daemon provides src and so we're skipping |
1324 | * tracking it */ | |
1325 | prev_src = nexthop->rmap_src; | |
1326 | prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
1327 | prev_index = nexthop->ifindex; | |
1328 | /* | |
1329 | * We need to respect the multipath_num here | |
1330 | * as that what we should be able to install from | |
1331 | * a multipath perpsective should not be a data plane | |
1332 | * decision point. | |
1333 | */ | |
98cda54a SW |
1334 | new_active = |
1335 | nexthop_active_check(rn, re, nexthop, &resolved_id); | |
1336 | ||
1337 | /* | |
1338 | * Create the individual nexthop hash entries | |
1339 | * for the nexthops in the group | |
1340 | */ | |
1341 | ||
1342 | nhe = depends_find(nexthop, rt_afi); | |
1343 | ||
1344 | if (nhe && resolved_id) { | |
1345 | struct nhg_hash_entry *old_resolved = NULL; | |
1346 | struct nhg_hash_entry *new_resolved = NULL; | |
1347 | ||
1348 | /* If this was already resolved, get its resolved nhe */ | |
1349 | if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE)) | |
1350 | old_resolved = zebra_nhg_resolve(nhe); | |
1351 | ||
1352 | /* | |
1353 | * We are going to do what is done in nexthop_active | |
1354 | * and clear whatever resolved nexthop may already be | |
1355 | * there. | |
1356 | */ | |
1357 | ||
1358 | zebra_nhg_depends_release(nhe); | |
1359 | nhg_connected_head_free(&nhe->nhg_depends); | |
1360 | ||
1361 | new_resolved = zebra_nhg_lookup_id(resolved_id); | |
1362 | ||
1363 | if (new_resolved) { | |
1364 | /* Add new resolved */ | |
1365 | zebra_nhg_depends_add(nhe, new_resolved); | |
1366 | zebra_nhg_dependents_add(new_resolved, nhe); | |
9834bb52 SW |
1367 | |
1368 | if (old_resolved && new_resolved->id != old_resolved->id) { | |
1369 | new_resolved->refcnt+=nhe->refcnt; | |
1370 | old_resolved->refcnt-=nhe->refcnt; | |
1371 | } else if (!old_resolved) | |
1372 | zebra_nhg_increment_ref(new_resolved); | |
98cda54a SW |
1373 | |
1374 | SET_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE); | |
1375 | } else | |
1376 | flog_err( | |
1377 | EC_ZEBRA_TABLE_LOOKUP_FAILED, | |
1378 | "Zebra failed to lookup a resolved nexthop hash entry id=%u", | |
1379 | resolved_id); | |
1380 | } | |
1381 | ||
ad28e79a | 1382 | if (new_active |
98cda54a | 1383 | && nexthop_group_active_nexthop_num(&new_grp) |
9a0d4dd3 | 1384 | >= zrouter.multipath_num) { |
ad28e79a SW |
1385 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); |
1386 | new_active = 0; | |
1387 | } | |
9a0d4dd3 | 1388 | |
98cda54a | 1389 | if (nhe && new_active) { |
9a0d4dd3 DS |
1390 | curr_active++; |
1391 | ||
98cda54a SW |
1392 | SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); |
1393 | if (!nhe->is_kernel_nh | |
1394 | && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE)) | |
1395 | zebra_nhg_install_kernel(nhe); | |
1396 | } | |
1397 | ||
ad28e79a SW |
1398 | /* Don't allow src setting on IPv6 addr for now */ |
1399 | if (prev_active != new_active || prev_index != nexthop->ifindex | |
1400 | || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX | |
1401 | && nexthop->type < NEXTHOP_TYPE_IPV6) | |
1402 | && prev_src.ipv4.s_addr | |
1403 | != nexthop->rmap_src.ipv4.s_addr) | |
1404 | || ((nexthop->type >= NEXTHOP_TYPE_IPV6 | |
1405 | && nexthop->type < NEXTHOP_TYPE_BLACKHOLE) | |
1406 | && !(IPV6_ADDR_SAME(&prev_src.ipv6, | |
1407 | &nexthop->rmap_src.ipv6))) | |
42fc558e | 1408 | || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) |
ad28e79a | 1409 | SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); |
ad28e79a SW |
1410 | } |
1411 | ||
98cda54a SW |
1412 | if (CHECK_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED)) { |
1413 | struct nhg_hash_entry *new_nhe = NULL; | |
1414 | // TODO: Add proto type here | |
1415 | ||
1416 | new_nhe = zebra_nhg_rib_find(0, &new_grp, re->vrf_id, rt_afi); | |
1417 | ||
144a1b34 | 1418 | zebra_nhg_re_update_ref(re, new_nhe); |
e22e8001 SW |
1419 | } |
1420 | ||
98cda54a SW |
1421 | if (curr_active) { |
1422 | struct nhg_hash_entry *nhe = NULL; | |
1423 | ||
1424 | nhe = zebra_nhg_lookup_id(re->nhe_id); | |
1425 | ||
1426 | if (nhe) { | |
1427 | SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); | |
1428 | if (!nhe->is_kernel_nh | |
1429 | && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE)) | |
1430 | zebra_nhg_install_kernel(nhe); | |
1431 | } else | |
1432 | flog_err( | |
1433 | EC_ZEBRA_TABLE_LOOKUP_FAILED, | |
1434 | "Active update on NHE id=%u that we do not have in our tables", | |
1435 | re->nhe_id); | |
1436 | } | |
1437 | ||
1438 | /* | |
1439 | * Do not need these nexthops anymore since they | |
1440 | * were either copied over into an nhe or not | |
1441 | * used at all. | |
1442 | */ | |
1443 | nexthops_free(new_grp.nexthop); | |
9a0d4dd3 | 1444 | return curr_active; |
ad28e79a | 1445 | } |
5be96a2d | 1446 | |
144a1b34 SW |
1447 | int zebra_nhg_re_update_ref(struct route_entry *re, struct nhg_hash_entry *new) |
1448 | { | |
1449 | struct nhg_hash_entry *old = NULL; | |
139ddad8 | 1450 | int ret = 0; |
144a1b34 | 1451 | |
139ddad8 SW |
1452 | if (new == NULL) { |
1453 | re->ng = NULL; | |
1454 | goto done; | |
1455 | } | |
144a1b34 SW |
1456 | |
1457 | if (re->nhe_id != new->id) { | |
1458 | old = zebra_nhg_lookup_id(re->nhe_id); | |
1459 | ||
1460 | re->ng = new->nhg; | |
1461 | re->nhe_id = new->id; | |
1462 | ||
1463 | zebra_nhg_increment_ref(new); | |
1464 | if (old) | |
1465 | zebra_nhg_decrement_ref(old); | |
1466 | } | |
1467 | ||
139ddad8 SW |
1468 | done: |
1469 | return ret; | |
144a1b34 SW |
1470 | } |
1471 | ||
98cda54a SW |
1472 | /* Convert a nhe into a group array */ |
1473 | uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe) | |
1474 | { | |
1475 | struct nhg_connected *rb_node_dep = NULL; | |
1476 | struct nhg_hash_entry *depend = NULL; | |
1477 | uint8_t i = 0; | |
1478 | ||
1479 | RB_FOREACH (rb_node_dep, nhg_connected_head, &nhe->nhg_depends) { | |
1480 | depend = rb_node_dep->nhe; | |
1481 | ||
1482 | /* | |
1483 | * If its recursive, use its resolved nhe in the group | |
1484 | */ | |
1485 | if (CHECK_FLAG(depend->flags, NEXTHOP_GROUP_RECURSIVE)) { | |
1486 | depend = zebra_nhg_resolve(depend); | |
1487 | if (!depend) { | |
1488 | flog_err( | |
1489 | EC_ZEBRA_NHG_FIB_UPDATE, | |
1490 | "Failed to recursively resolve Nexthop Hash Entry id=%u in the group id=%u", | |
1491 | depend->id, nhe->id); | |
1492 | continue; | |
1493 | } | |
1494 | } | |
1495 | ||
1496 | grp[i].id = depend->id; | |
1497 | /* We aren't using weights for anything right now */ | |
1498 | grp[i].weight = 0; | |
1499 | i++; | |
1500 | } | |
1501 | return i; | |
1502 | } | |
1503 | ||
5be96a2d SW |
1504 | void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe) |
1505 | { | |
e22e8001 SW |
1506 | if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) |
1507 | && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)) { | |
147bad16 SW |
1508 | nhe->is_kernel_nh = false; |
1509 | int ret = dplane_nexthop_add(nhe); | |
2d3c57e6 | 1510 | |
147bad16 SW |
1511 | switch (ret) { |
1512 | case ZEBRA_DPLANE_REQUEST_QUEUED: | |
1513 | SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED); | |
1514 | break; | |
1515 | case ZEBRA_DPLANE_REQUEST_FAILURE: | |
1516 | flog_err( | |
1517 | EC_ZEBRA_DP_INSTALL_FAIL, | |
1518 | "Failed to install Nexthop ID (%u) into the kernel", | |
1519 | nhe->id); | |
1520 | break; | |
1521 | case ZEBRA_DPLANE_REQUEST_SUCCESS: | |
1522 | SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); | |
1523 | break; | |
1524 | } | |
1525 | } | |
1526 | } | |
1527 | ||
147bad16 SW |
1528 | void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe) |
1529 | { | |
1530 | if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)) { | |
1531 | int ret = dplane_nexthop_delete(nhe); | |
2d3c57e6 | 1532 | |
147bad16 SW |
1533 | switch (ret) { |
1534 | case ZEBRA_DPLANE_REQUEST_QUEUED: | |
1535 | SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED); | |
1536 | break; | |
1537 | case ZEBRA_DPLANE_REQUEST_FAILURE: | |
1538 | flog_err( | |
1539 | EC_ZEBRA_DP_DELETE_FAIL, | |
1540 | "Failed to uninstall Nexthop ID (%u) from the kernel", | |
1541 | nhe->id); | |
1542 | break; | |
1543 | case ZEBRA_DPLANE_REQUEST_SUCCESS: | |
1544 | UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); | |
e22e8001 | 1545 | zebra_nhg_release(nhe); |
147bad16 SW |
1546 | break; |
1547 | } | |
e22e8001 SW |
1548 | } else |
1549 | zebra_nhg_release(nhe); | |
147bad16 SW |
1550 | } |
1551 | ||
3e0372d2 SW |
1552 | static void zebra_nhg_uninstall_created(struct hash_bucket *bucket, void *arg) |
1553 | { | |
1554 | struct nhg_hash_entry *nhe = NULL; | |
1555 | ||
1556 | nhe = (struct nhg_hash_entry *)bucket->data; | |
1557 | ||
1558 | if (nhe && !nhe->is_kernel_nh) | |
1559 | zebra_nhg_uninstall_kernel(nhe); | |
1560 | } | |
1561 | ||
3e0372d2 SW |
1562 | void zebra_nhg_cleanup_tables(void) |
1563 | { | |
5155d86c SW |
1564 | // TODO: These should only be uninstalled via route cleanup |
1565 | // path? | |
1566 | return; | |
3e0372d2 SW |
1567 | hash_iterate(zrouter.nhgs, zebra_nhg_uninstall_created, NULL); |
1568 | } | |
1569 | ||
5f3c9e52 SW |
1570 | void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) |
1571 | { | |
1572 | enum dplane_op_e op; | |
1573 | enum zebra_dplane_result status; | |
1574 | uint32_t id = 0; | |
1575 | struct nhg_hash_entry *nhe = NULL; | |
1576 | ||
1577 | op = dplane_ctx_get_op(ctx); | |
1578 | status = dplane_ctx_get_status(ctx); | |
1579 | ||
0c8215cb | 1580 | id = dplane_ctx_get_nhe_id(ctx); |
e22e8001 | 1581 | |
5f3c9e52 SW |
1582 | nhe = zebra_nhg_lookup_id(id); |
1583 | ||
1584 | if (nhe) { | |
7512f617 | 1585 | UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED); |
5f3c9e52 SW |
1586 | if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) |
1587 | zlog_debug( | |
1588 | "Nexthop dplane ctx %p, op %s, nexthop ID (%u), result %s", | |
1589 | ctx, dplane_op2str(op), nhe->id, | |
1590 | dplane_res2str(status)); | |
1591 | ||
1592 | switch (op) { | |
1593 | case DPLANE_OP_NH_DELETE: | |
1594 | if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { | |
1595 | UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); | |
e22e8001 | 1596 | zebra_nhg_release(nhe); |
5f3c9e52 SW |
1597 | } else { |
1598 | flog_err( | |
1599 | EC_ZEBRA_DP_DELETE_FAIL, | |
1600 | "Failed to uninstall Nexthop ID (%u) from the kernel", | |
1601 | nhe->id); | |
1602 | } | |
1603 | break; | |
1604 | case DPLANE_OP_NH_INSTALL: | |
1605 | case DPLANE_OP_NH_UPDATE: | |
1606 | if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { | |
1607 | SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); | |
1608 | } else { | |
1609 | flog_err( | |
1610 | EC_ZEBRA_DP_INSTALL_FAIL, | |
1611 | "Failed to install Nexthop ID (%u) into the kernel", | |
1612 | nhe->id); | |
1613 | UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); | |
1614 | } | |
5f3c9e52 SW |
1615 | break; |
1616 | case DPLANE_OP_ROUTE_INSTALL: | |
1617 | case DPLANE_OP_ROUTE_UPDATE: | |
1618 | case DPLANE_OP_ROUTE_DELETE: | |
1619 | case DPLANE_OP_ROUTE_NOTIFY: | |
1620 | case DPLANE_OP_LSP_INSTALL: | |
1621 | case DPLANE_OP_LSP_UPDATE: | |
1622 | case DPLANE_OP_LSP_DELETE: | |
1623 | case DPLANE_OP_LSP_NOTIFY: | |
1624 | case DPLANE_OP_PW_INSTALL: | |
1625 | case DPLANE_OP_PW_UNINSTALL: | |
1626 | case DPLANE_OP_SYS_ROUTE_ADD: | |
1627 | case DPLANE_OP_SYS_ROUTE_DELETE: | |
1628 | case DPLANE_OP_ADDR_INSTALL: | |
1629 | case DPLANE_OP_ADDR_UNINSTALL: | |
1630 | case DPLANE_OP_MAC_INSTALL: | |
1631 | case DPLANE_OP_MAC_DELETE: | |
1632 | case DPLANE_OP_NONE: | |
1633 | break; | |
1634 | } | |
71593b3f | 1635 | } else |
5f3c9e52 SW |
1636 | flog_err( |
1637 | EC_ZEBRA_NHG_SYNC, | |
1638 | "%s operation preformed on Nexthop ID (%u) in the kernel, that we no longer have in our table", | |
1639 | dplane_op2str(op), id); | |
71593b3f SW |
1640 | |
1641 | dplane_ctx_fini(&ctx); | |
5be96a2d SW |
1642 | } |
1643 |