]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
eb5d44eb | 2 | /* |
3 | * IS-IS Rout(e)ing protocol - isis_route.c | |
4 | * Copyright (C) 2001,2002 Sampo Saaristo | |
d62a17ae | 5 | * Tampere University of Technology |
eb5d44eb | 6 | * Institute of Communications Engineering |
7 | * | |
8 | * based on ../ospf6d/ospf6_route.[ch] | |
9 | * by Yasuhiro Ohara | |
eb5d44eb | 10 | */ |
11 | ||
eb5d44eb | 12 | #include <zebra.h> |
eb5d44eb | 13 | |
24a58196 | 14 | #include "frrevent.h" |
eb5d44eb | 15 | #include "linklist.h" |
16 | #include "vty.h" | |
17 | #include "log.h" | |
363be4dd | 18 | #include "lib_errors.h" |
eb5d44eb | 19 | #include "memory.h" |
20 | #include "prefix.h" | |
21 | #include "hash.h" | |
22 | #include "if.h" | |
23 | #include "table.h" | |
321c1bbb | 24 | #include "srcdest_table.h" |
eb5d44eb | 25 | |
26 | #include "isis_constants.h" | |
27 | #include "isis_common.h" | |
3f045a08 | 28 | #include "isis_flags.h" |
eb5d44eb | 29 | #include "isisd.h" |
30 | #include "isis_misc.h" | |
31 | #include "isis_adjacency.h" | |
32 | #include "isis_circuit.h" | |
eb5d44eb | 33 | #include "isis_pdu.h" |
34 | #include "isis_lsp.h" | |
35 | #include "isis_spf.h" | |
7b36d36e | 36 | #include "isis_spf_private.h" |
eb5d44eb | 37 | #include "isis_route.h" |
38 | #include "isis_zebra.h" | |
80ef2e89 | 39 | #include "isis_flex_algo.h" |
eb5d44eb | 40 | |
66b9a381 DL |
41 | DEFINE_MTYPE_STATIC(ISISD, ISIS_NEXTHOP, "ISIS nexthop"); |
42 | DEFINE_MTYPE_STATIC(ISISD, ISIS_ROUTE_INFO, "ISIS route info"); | |
7153c3ca HS |
43 | DEFINE_MTYPE_STATIC(ISISD, ISIS_ROUTE_TABLE_INFO, "ISIS route table info"); |
44 | ||
66b9a381 | 45 | |
6cf38339 RW |
46 | DEFINE_HOOK(isis_route_update_hook, |
47 | (struct isis_area * area, struct prefix *prefix, | |
48 | struct isis_route_info *route_info), | |
8451921b | 49 | (area, prefix, route_info)); |
6cf38339 | 50 | |
363be4dd RW |
51 | static struct isis_nexthop *nexthoplookup(struct list *nexthops, int family, |
52 | union g_addr *ip, ifindex_t ifindex); | |
6cf38339 RW |
53 | static void isis_route_update(struct isis_area *area, struct prefix *prefix, |
54 | struct prefix_ipv6 *src_p, | |
0a5f3f4f | 55 | struct isis_route_info *route_info); |
9e1194c2 | 56 | |
7153c3ca HS |
57 | static struct mpls_label_stack * |
58 | label_stack_dup(const struct mpls_label_stack *const orig) | |
59 | { | |
60 | struct mpls_label_stack *copy; | |
61 | int array_size; | |
62 | ||
63 | if (orig == NULL) | |
64 | return NULL; | |
65 | ||
66 | array_size = orig->num_labels * sizeof(mpls_label_t); | |
67 | copy = XCALLOC(MTYPE_ISIS_NEXTHOP_LABELS, | |
68 | sizeof(struct mpls_label_stack) + array_size); | |
69 | copy->num_labels = orig->num_labels; | |
70 | memcpy(copy->label, orig->label, array_size); | |
71 | return copy; | |
72 | } | |
73 | ||
74 | static struct isis_nexthop * | |
75 | isis_nexthop_create(int family, const union g_addr *const ip, ifindex_t ifindex) | |
eb5d44eb | 76 | { |
d62a17ae | 77 | struct isis_nexthop *nexthop; |
f390d2c7 | 78 | |
d62a17ae | 79 | nexthop = XCALLOC(MTYPE_ISIS_NEXTHOP, sizeof(struct isis_nexthop)); |
f390d2c7 | 80 | |
363be4dd | 81 | nexthop->family = family; |
d62a17ae | 82 | nexthop->ifindex = ifindex; |
363be4dd | 83 | nexthop->ip = *ip; |
eb5d44eb | 84 | |
d62a17ae | 85 | return nexthop; |
eb5d44eb | 86 | } |
87 | ||
7153c3ca HS |
88 | static struct isis_nexthop * |
89 | isis_nexthop_dup(const struct isis_nexthop *const orig) | |
90 | { | |
91 | struct isis_nexthop *nexthop; | |
92 | ||
93 | nexthop = isis_nexthop_create(orig->family, &orig->ip, orig->ifindex); | |
94 | memcpy(nexthop->sysid, orig->sysid, ISIS_SYS_ID_LEN); | |
95 | nexthop->sr = orig->sr; | |
96 | nexthop->label_stack = label_stack_dup(orig->label_stack); | |
97 | ||
98 | return nexthop; | |
99 | } | |
100 | ||
c951ee6e | 101 | void isis_nexthop_delete(struct isis_nexthop *nexthop) |
eb5d44eb | 102 | { |
c951ee6e | 103 | XFREE(MTYPE_ISIS_NEXTHOP_LABELS, nexthop->label_stack); |
885e2413 | 104 | XFREE(MTYPE_ISIS_NEXTHOP, nexthop); |
eb5d44eb | 105 | } |
106 | ||
7153c3ca HS |
107 | static struct list *isis_nexthop_list_dup(const struct list *orig) |
108 | { | |
109 | struct list *copy; | |
110 | struct listnode *node; | |
111 | struct isis_nexthop *nh; | |
112 | struct isis_nexthop *nhcopy; | |
113 | ||
114 | copy = list_new(); | |
115 | for (ALL_LIST_ELEMENTS_RO(orig, node, nh)) { | |
116 | nhcopy = isis_nexthop_dup(nh); | |
117 | listnode_add(copy, nhcopy); | |
118 | } | |
119 | return copy; | |
120 | } | |
121 | ||
363be4dd RW |
122 | static struct isis_nexthop *nexthoplookup(struct list *nexthops, int family, |
123 | union g_addr *ip, ifindex_t ifindex) | |
eb5d44eb | 124 | { |
d62a17ae | 125 | struct listnode *node; |
126 | struct isis_nexthop *nh; | |
eb5d44eb | 127 | |
d62a17ae | 128 | for (ALL_LIST_ELEMENTS_RO(nexthops, node, nh)) { |
74b95643 DA |
129 | if (nh->ifindex != ifindex) |
130 | continue; | |
a11742dc | 131 | |
5389c4f7 LS |
132 | /* if the IP is unspecified, return the first nexthop found on |
133 | * the interface | |
134 | */ | |
135 | if (!ip) | |
136 | return nh; | |
137 | ||
138 | if (nh->family != family) | |
139 | continue; | |
140 | ||
363be4dd RW |
141 | switch (family) { |
142 | case AF_INET: | |
143 | if (IPV4_ADDR_CMP(&nh->ip.ipv4, &ip->ipv4)) | |
144 | continue; | |
145 | break; | |
146 | case AF_INET6: | |
147 | if (IPV6_ADDR_CMP(&nh->ip.ipv6, &ip->ipv6)) | |
148 | continue; | |
149 | break; | |
150 | default: | |
151 | flog_err(EC_LIB_DEVELOPMENT, | |
152 | "%s: unknown address family [%d]", __func__, | |
153 | family); | |
154 | exit(1); | |
155 | } | |
eb5d44eb | 156 | |
363be4dd | 157 | return nh; |
d62a17ae | 158 | } |
eb5d44eb | 159 | |
9e1194c2 | 160 | return NULL; |
eb5d44eb | 161 | } |
162 | ||
c951ee6e | 163 | void adjinfo2nexthop(int family, struct list *nexthops, |
d47d6089 | 164 | struct isis_adjacency *adj, struct isis_sr_psid_info *sr, |
c951ee6e | 165 | struct mpls_label_stack *label_stack) |
eb5d44eb | 166 | { |
d62a17ae | 167 | struct isis_nexthop *nh; |
363be4dd RW |
168 | union g_addr ip = {}; |
169 | ||
170 | switch (family) { | |
171 | case AF_INET: | |
172 | for (unsigned int i = 0; i < adj->ipv4_address_count; i++) { | |
173 | ip.ipv4 = adj->ipv4_addresses[i]; | |
174 | ||
175 | if (!nexthoplookup(nexthops, AF_INET, &ip, | |
176 | adj->circuit->interface->ifindex)) { | |
177 | nh = isis_nexthop_create( | |
178 | AF_INET, &ip, | |
179 | adj->circuit->interface->ifindex); | |
26f6acaf | 180 | memcpy(nh->sysid, adj->sysid, sizeof(nh->sysid)); |
d47d6089 RW |
181 | if (sr) |
182 | nh->sr = *sr; | |
c951ee6e | 183 | nh->label_stack = label_stack; |
363be4dd RW |
184 | listnode_add(nexthops, nh); |
185 | break; | |
186 | } | |
d62a17ae | 187 | } |
363be4dd RW |
188 | break; |
189 | case AF_INET6: | |
173f8887 OD |
190 | for (unsigned int i = 0; i < adj->ll_ipv6_count; i++) { |
191 | ip.ipv6 = adj->ll_ipv6_addrs[i]; | |
363be4dd RW |
192 | |
193 | if (!nexthoplookup(nexthops, AF_INET6, &ip, | |
194 | adj->circuit->interface->ifindex)) { | |
195 | nh = isis_nexthop_create( | |
196 | AF_INET6, &ip, | |
197 | adj->circuit->interface->ifindex); | |
26f6acaf | 198 | memcpy(nh->sysid, adj->sysid, sizeof(nh->sysid)); |
d47d6089 RW |
199 | if (sr) |
200 | nh->sr = *sr; | |
c951ee6e | 201 | nh->label_stack = label_stack; |
363be4dd RW |
202 | listnode_add(nexthops, nh); |
203 | break; | |
204 | } | |
d62a17ae | 205 | } |
363be4dd RW |
206 | break; |
207 | default: | |
208 | flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address family [%d]", | |
209 | __func__, family); | |
210 | exit(1); | |
f390d2c7 | 211 | } |
eb5d44eb | 212 | } |
eb5d44eb | 213 | |
52a7c25e | 214 | static void isis_route_add_dummy_nexthops(struct isis_route_info *rinfo, |
c951ee6e | 215 | const uint8_t *sysid, |
d47d6089 | 216 | struct isis_sr_psid_info *sr, |
c951ee6e | 217 | struct mpls_label_stack *label_stack) |
52a7c25e RW |
218 | { |
219 | struct isis_nexthop *nh; | |
220 | ||
221 | nh = XCALLOC(MTYPE_ISIS_NEXTHOP, sizeof(struct isis_nexthop)); | |
222 | memcpy(nh->sysid, sysid, sizeof(nh->sysid)); | |
d47d6089 | 223 | nh->sr = *sr; |
c951ee6e | 224 | nh->label_stack = label_stack; |
52a7c25e RW |
225 | listnode_add(rinfo->nexthops, nh); |
226 | } | |
227 | ||
d47d6089 RW |
228 | static struct isis_route_info * |
229 | isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p, | |
230 | uint32_t cost, uint32_t depth, struct isis_sr_psid_info *sr, | |
e886416f | 231 | struct list *adjacencies, bool allow_ecmp) |
eb5d44eb | 232 | { |
d62a17ae | 233 | struct isis_route_info *rinfo; |
7b36d36e | 234 | struct isis_vertex_adj *vadj; |
d62a17ae | 235 | struct listnode *node; |
236 | ||
237 | rinfo = XCALLOC(MTYPE_ISIS_ROUTE_INFO, sizeof(struct isis_route_info)); | |
238 | ||
363be4dd | 239 | rinfo->nexthops = list_new(); |
7b36d36e RW |
240 | for (ALL_LIST_ELEMENTS_RO(adjacencies, node, vadj)) { |
241 | struct isis_spf_adj *sadj = vadj->sadj; | |
242 | struct isis_adjacency *adj = sadj->adj; | |
d47d6089 | 243 | struct isis_sr_psid_info *sr = &vadj->sr; |
c951ee6e | 244 | struct mpls_label_stack *label_stack = vadj->label_stack; |
7b36d36e | 245 | |
52a7c25e RW |
246 | /* |
247 | * Create dummy nexthops when running SPF on a testing | |
248 | * environment. | |
249 | */ | |
250 | if (CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) { | |
d47d6089 | 251 | isis_route_add_dummy_nexthops(rinfo, sadj->id, sr, |
c951ee6e | 252 | label_stack); |
e886416f RW |
253 | if (!allow_ecmp) |
254 | break; | |
52a7c25e RW |
255 | continue; |
256 | } | |
257 | ||
363be4dd RW |
258 | /* check for force resync this route */ |
259 | if (CHECK_FLAG(adj->circuit->flags, | |
260 | ISIS_CIRCUIT_FLAPPED_AFTER_SPF)) | |
261 | SET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); | |
262 | ||
263 | /* update neighbor router address */ | |
264 | switch (prefix->family) { | |
265 | case AF_INET: | |
12256b84 | 266 | if (depth == 2 && prefix->prefixlen == IPV4_MAX_BITLEN) |
d62a17ae | 267 | adj->router_address = prefix->u.prefix4; |
363be4dd RW |
268 | break; |
269 | case AF_INET6: | |
13ccce6e | 270 | if (depth == 2 && prefix->prefixlen == IPV6_MAX_BITLEN |
321c1bbb | 271 | && (!src_p || !src_p->prefixlen)) { |
d62a17ae | 272 | adj->router_address6 = prefix->u.prefix6; |
321c1bbb | 273 | } |
363be4dd RW |
274 | break; |
275 | default: | |
276 | flog_err(EC_LIB_DEVELOPMENT, | |
277 | "%s: unknown address family [%d]", __func__, | |
278 | prefix->family); | |
279 | exit(1); | |
d62a17ae | 280 | } |
d47d6089 | 281 | adjinfo2nexthop(prefix->family, rinfo->nexthops, adj, sr, |
c951ee6e | 282 | label_stack); |
e886416f RW |
283 | if (!allow_ecmp) |
284 | break; | |
d62a17ae | 285 | } |
286 | ||
287 | rinfo->cost = cost; | |
288 | rinfo->depth = depth; | |
7153c3ca HS |
289 | rinfo->sr_algo[sr->algorithm] = *sr; |
290 | rinfo->sr_algo[sr->algorithm].nexthops = rinfo->nexthops; | |
291 | rinfo->sr_algo[sr->algorithm].nexthops_backup = | |
bdaafbf8 | 292 | rinfo->backup ? rinfo->backup->nexthops : NULL; |
d62a17ae | 293 | |
294 | return rinfo; | |
eb5d44eb | 295 | } |
296 | ||
d62a17ae | 297 | static void isis_route_info_delete(struct isis_route_info *route_info) |
eb5d44eb | 298 | { |
7153c3ca HS |
299 | for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { |
300 | if (!route_info->sr_algo[i].present) | |
301 | continue; | |
302 | ||
303 | if (route_info->sr_algo[i].nexthops == route_info->nexthops) | |
304 | continue; | |
305 | ||
306 | route_info->sr_algo[i].nexthops->del = | |
307 | (void (*)(void *))isis_nexthop_delete; | |
308 | list_delete(&route_info->sr_algo[i].nexthops); | |
309 | } | |
310 | ||
d62a17ae | 311 | if (route_info->nexthops) { |
312 | route_info->nexthops->del = | |
313 | (void (*)(void *))isis_nexthop_delete; | |
6a154c88 | 314 | list_delete(&route_info->nexthops); |
d62a17ae | 315 | } |
316 | ||
d62a17ae | 317 | XFREE(MTYPE_ISIS_ROUTE_INFO, route_info); |
eb5d44eb | 318 | } |
319 | ||
c951ee6e RW |
320 | void isis_route_node_cleanup(struct route_table *table, struct route_node *node) |
321 | { | |
322 | if (node->info) | |
323 | isis_route_info_delete(node->info); | |
324 | } | |
325 | ||
7153c3ca HS |
326 | struct isis_route_table_info *isis_route_table_info_alloc(uint8_t algorithm) |
327 | { | |
328 | struct isis_route_table_info *info; | |
329 | ||
330 | info = XCALLOC(MTYPE_ISIS_ROUTE_TABLE_INFO, sizeof(*info)); | |
331 | info->algorithm = algorithm; | |
332 | return info; | |
333 | } | |
334 | ||
335 | void isis_route_table_info_free(void *info) | |
336 | { | |
337 | XFREE(MTYPE_ISIS_ROUTE_TABLE_INFO, info); | |
338 | } | |
339 | ||
340 | uint8_t isis_route_table_algorithm(const struct route_table *table) | |
341 | { | |
342 | const struct isis_route_table_info *info = table->info; | |
343 | ||
344 | return info ? info->algorithm : 0; | |
345 | } | |
346 | ||
d47d6089 RW |
347 | static bool isis_sr_psid_info_same(struct isis_sr_psid_info *new, |
348 | struct isis_sr_psid_info *old) | |
349 | { | |
350 | if (new->present != old->present) | |
351 | return false; | |
352 | ||
353 | if (new->label != old->label) | |
354 | return false; | |
355 | ||
356 | if (new->sid.flags != old->sid.flags | |
357 | || new->sid.value != old->sid.value) | |
358 | return false; | |
359 | ||
7f8dddf4 HS |
360 | if (new->sid.algorithm != old->sid.algorithm) |
361 | return false; | |
362 | ||
d47d6089 RW |
363 | return true; |
364 | } | |
365 | ||
16fe8cff RW |
366 | static bool isis_label_stack_same(struct mpls_label_stack *new, |
367 | struct mpls_label_stack *old) | |
368 | { | |
369 | if (!new && !old) | |
370 | return true; | |
371 | if (!new || !old) | |
372 | return false; | |
373 | if (new->num_labels != old->num_labels) | |
374 | return false; | |
375 | if (memcmp(&new->label, &old->label, | |
376 | sizeof(mpls_label_t) * new->num_labels)) | |
377 | return false; | |
378 | ||
379 | return true; | |
380 | } | |
381 | ||
d62a17ae | 382 | static int isis_route_info_same(struct isis_route_info *new, |
1b2b2677 EDP |
383 | struct isis_route_info *old, char *buf, |
384 | size_t buf_size) | |
eb5d44eb | 385 | { |
d62a17ae | 386 | struct listnode *node; |
d47d6089 | 387 | struct isis_nexthop *new_nh, *old_nh; |
d62a17ae | 388 | |
1b2b2677 EDP |
389 | if (new->cost != old->cost) { |
390 | if (buf) | |
391 | snprintf(buf, buf_size, "cost (old: %u, new: %u)", | |
392 | old->cost, new->cost); | |
d62a17ae | 393 | return 0; |
1b2b2677 | 394 | } |
d62a17ae | 395 | |
1b2b2677 EDP |
396 | if (new->depth != old->depth) { |
397 | if (buf) | |
398 | snprintf(buf, buf_size, "depth (old: %u, new: %u)", | |
399 | old->depth, new->depth); | |
d62a17ae | 400 | return 0; |
1b2b2677 | 401 | } |
d62a17ae | 402 | |
7153c3ca HS |
403 | for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { |
404 | struct isis_sr_psid_info new_sr_algo; | |
405 | struct isis_sr_psid_info old_sr_algo; | |
406 | ||
407 | new_sr_algo = new->sr_algo[i]; | |
408 | old_sr_algo = old->sr_algo[i]; | |
409 | ||
410 | if (!isis_sr_psid_info_same(&new_sr_algo, &old_sr_algo)) { | |
411 | if (buf) | |
412 | snprintf( | |
413 | buf, buf_size, | |
414 | "SR input label algo-%u (old: %s, new: %s)", | |
415 | i, old_sr_algo.present ? "yes" : "no", | |
416 | new_sr_algo.present ? "yes" : "no"); | |
417 | return 0; | |
418 | } | |
d47d6089 RW |
419 | } |
420 | ||
1b2b2677 EDP |
421 | if (new->nexthops->count != old->nexthops->count) { |
422 | if (buf) | |
423 | snprintf(buf, buf_size, "nhops num (old: %u, new: %u)", | |
424 | old->nexthops->count, new->nexthops->count); | |
d62a17ae | 425 | return 0; |
1b2b2677 | 426 | } |
d62a17ae | 427 | |
d47d6089 RW |
428 | for (ALL_LIST_ELEMENTS_RO(new->nexthops, node, new_nh)) { |
429 | old_nh = nexthoplookup(old->nexthops, new_nh->family, | |
430 | &new_nh->ip, new_nh->ifindex); | |
431 | if (!old_nh) { | |
1b2b2677 EDP |
432 | if (buf) |
433 | snprintf(buf, buf_size, | |
434 | "new nhop"); /* TODO: print nhop */ | |
363be4dd | 435 | return 0; |
1b2b2677 | 436 | } |
d47d6089 RW |
437 | if (!isis_sr_psid_info_same(&new_nh->sr, &old_nh->sr)) { |
438 | if (buf) | |
439 | snprintf(buf, buf_size, "nhop SR label"); | |
440 | return 0; | |
441 | } | |
16fe8cff RW |
442 | if (!isis_label_stack_same(new_nh->label_stack, |
443 | old_nh->label_stack)) { | |
444 | if (buf) | |
445 | snprintf(buf, buf_size, "nhop label stack"); | |
446 | return 0; | |
447 | } | |
1b2b2677 | 448 | } |
363be4dd | 449 | |
1b2b2677 EDP |
450 | /* only the resync flag needs to be checked */ |
451 | if (CHECK_FLAG(new->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC) | |
452 | != CHECK_FLAG(old->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC)) { | |
453 | if (buf) | |
454 | snprintf(buf, buf_size, "resync flag"); | |
455 | return 0; | |
456 | } | |
d62a17ae | 457 | |
458 | return 1; | |
eb5d44eb | 459 | } |
460 | ||
d47d6089 RW |
461 | struct isis_route_info * |
462 | isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p, | |
463 | uint32_t cost, uint32_t depth, struct isis_sr_psid_info *sr, | |
e886416f RW |
464 | struct list *adjacencies, bool allow_ecmp, |
465 | struct isis_area *area, struct route_table *table) | |
eb5d44eb | 466 | { |
d62a17ae | 467 | struct route_node *route_node; |
468 | struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL; | |
1b2b2677 | 469 | char change_buf[64]; |
d62a17ae | 470 | |
3dace42d | 471 | if (!table) |
d62a17ae | 472 | return NULL; |
3dace42d | 473 | |
d47d6089 | 474 | rinfo_new = isis_route_info_new(prefix, src_p, cost, depth, sr, |
e886416f | 475 | adjacencies, allow_ecmp); |
321c1bbb | 476 | route_node = srcdest_rnode_get(table, prefix, src_p); |
d62a17ae | 477 | |
478 | rinfo_old = route_node->info; | |
479 | if (!rinfo_old) { | |
e740f9c1 | 480 | if (IS_DEBUG_RTE_EVENTS) |
2dbe669b DA |
481 | zlog_debug("ISIS-Rte (%s) route created: %pFX", |
482 | area->area_tag, prefix); | |
d62a17ae | 483 | route_info = rinfo_new; |
484 | UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); | |
485 | } else { | |
bcd9fd50 | 486 | route_unlock_node(route_node); |
1b2b2677 | 487 | #ifdef EXTREME_DEBUG |
e740f9c1 | 488 | if (IS_DEBUG_RTE_EVENTS) |
2dbe669b DA |
489 | zlog_debug("ISIS-Rte (%s) route already exists: %pFX", |
490 | area->area_tag, prefix); | |
1b2b2677 EDP |
491 | #endif /* EXTREME_DEBUG */ |
492 | if (isis_route_info_same(rinfo_new, rinfo_old, change_buf, | |
493 | sizeof(change_buf))) { | |
494 | #ifdef EXTREME_DEBUG | |
e740f9c1 | 495 | if (IS_DEBUG_RTE_EVENTS) |
2dbe669b DA |
496 | zlog_debug( |
497 | "ISIS-Rte (%s) route unchanged: %pFX", | |
498 | area->area_tag, prefix); | |
1b2b2677 | 499 | #endif /* EXTREME_DEBUG */ |
d62a17ae | 500 | isis_route_info_delete(rinfo_new); |
501 | route_info = rinfo_old; | |
502 | } else { | |
e740f9c1 | 503 | if (IS_DEBUG_RTE_EVENTS) |
1b2b2677 | 504 | zlog_debug( |
2dbe669b DA |
505 | "ISIS-Rte (%s): route changed: %pFX, change: %s", |
506 | area->area_tag, prefix, change_buf); | |
7153c3ca HS |
507 | for (int i = 0; i < SR_ALGORITHM_COUNT; i++) |
508 | rinfo_new->sr_algo_previous[i] = | |
509 | rinfo_old->sr_algo[i]; | |
d62a17ae | 510 | isis_route_info_delete(rinfo_old); |
511 | route_info = rinfo_new; | |
512 | UNSET_FLAG(route_info->flag, | |
513 | ISIS_ROUTE_FLAG_ZEBRA_SYNCED); | |
514 | } | |
515 | } | |
516 | ||
517 | SET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE); | |
518 | route_node->info = route_info; | |
519 | ||
520 | return route_info; | |
eb5d44eb | 521 | } |
522 | ||
16fe8cff RW |
523 | void isis_route_delete(struct isis_area *area, struct route_node *rode, |
524 | struct route_table *table) | |
eb5d44eb | 525 | { |
d62a17ae | 526 | struct isis_route_info *rinfo; |
321c1bbb | 527 | char buff[SRCDEST2STR_BUFFER]; |
bcd9fd50 CF |
528 | struct prefix *prefix; |
529 | struct prefix_ipv6 *src_p; | |
d62a17ae | 530 | |
531 | /* for log */ | |
bcd9fd50 | 532 | srcdest_rnode2str(rode, buff, sizeof(buff)); |
d62a17ae | 533 | |
bcd9fd50 CF |
534 | srcdest_rnode_prefixes(rode, (const struct prefix **)&prefix, |
535 | (const struct prefix **)&src_p); | |
d62a17ae | 536 | |
bcd9fd50 | 537 | rinfo = rode->info; |
d62a17ae | 538 | if (rinfo == NULL) { |
e740f9c1 | 539 | if (IS_DEBUG_RTE_EVENTS) |
d62a17ae | 540 | zlog_debug( |
b97047ed | 541 | "ISIS-Rte: tried to delete non-existent route %s", |
d62a17ae | 542 | buff); |
543 | return; | |
544 | } | |
545 | ||
546 | if (CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) { | |
547 | UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE); | |
e740f9c1 | 548 | if (IS_DEBUG_RTE_EVENTS) |
d62a17ae | 549 | zlog_debug("ISIS-Rte: route delete %s", buff); |
6cf38339 | 550 | isis_route_update(area, prefix, src_p, rinfo); |
d62a17ae | 551 | } |
552 | isis_route_info_delete(rinfo); | |
553 | rode->info = NULL; | |
bcd9fd50 | 554 | route_unlock_node(rode); |
eb5d44eb | 555 | } |
556 | ||
8a65b22d LS |
557 | static void isis_route_remove_previous_sid(struct isis_area *area, |
558 | struct prefix *prefix, | |
559 | struct isis_route_info *route_info) | |
560 | { | |
561 | /* | |
562 | * Explicitly uninstall previous Prefix-SID label if it has | |
563 | * changed or was removed. | |
564 | */ | |
7153c3ca HS |
565 | for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { |
566 | if (route_info->sr_algo_previous[i].present && | |
567 | (!route_info->sr_algo[i].present || | |
568 | route_info->sr_algo_previous[i].label != | |
569 | route_info->sr_algo[i].label)) | |
570 | isis_zebra_prefix_sid_uninstall( | |
571 | area, prefix, route_info, | |
572 | &route_info->sr_algo_previous[i]); | |
573 | } | |
574 | } | |
575 | ||
576 | static void set_merge_route_info_sr_algo(struct isis_route_info *mrinfo, | |
577 | struct isis_route_info *rinfo) | |
578 | { | |
579 | for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { | |
580 | if (rinfo->sr_algo[i].present) { | |
581 | assert(i == rinfo->sr_algo[i].algorithm); | |
582 | assert(rinfo->nexthops); | |
583 | assert(rinfo->backup ? rinfo->backup->nexthops != NULL | |
584 | : true); | |
585 | ||
586 | if (mrinfo->sr_algo[i].nexthops != NULL && | |
587 | mrinfo->sr_algo[i].nexthops != mrinfo->nexthops) { | |
588 | mrinfo->sr_algo[i].nexthops->del = | |
589 | (void (*)(void *))isis_nexthop_delete; | |
590 | list_delete(&mrinfo->sr_algo[i].nexthops); | |
591 | } | |
592 | ||
593 | mrinfo->sr_algo[i] = rinfo->sr_algo[i]; | |
594 | mrinfo->sr_algo[i].nexthops = isis_nexthop_list_dup( | |
595 | rinfo->sr_algo[i].nexthops); | |
596 | } | |
597 | } | |
598 | ||
599 | UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); | |
600 | UNSET_FLAG(mrinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); | |
8a65b22d LS |
601 | } |
602 | ||
6cf38339 RW |
603 | static void isis_route_update(struct isis_area *area, struct prefix *prefix, |
604 | struct prefix_ipv6 *src_p, | |
0a5f3f4f RW |
605 | struct isis_route_info *route_info) |
606 | { | |
41c2bf85 LS |
607 | if (area == NULL) |
608 | return; | |
609 | ||
0a5f3f4f RW |
610 | if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) { |
611 | if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) | |
612 | return; | |
613 | ||
8a65b22d | 614 | isis_route_remove_previous_sid(area, prefix, route_info); |
d47d6089 RW |
615 | |
616 | /* Install route. */ | |
41c2bf85 LS |
617 | isis_zebra_route_add_route(area->isis, prefix, src_p, |
618 | route_info); | |
7153c3ca HS |
619 | |
620 | for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { | |
621 | struct isis_sr_psid_info sr_algo; | |
622 | ||
623 | sr_algo = route_info->sr_algo[i]; | |
624 | ||
625 | /* | |
626 | * Install/reinstall Prefix-SID label. | |
627 | */ | |
628 | if (sr_algo.present) | |
629 | isis_zebra_prefix_sid_install(area, prefix, | |
630 | &sr_algo); | |
631 | ||
632 | hook_call(isis_route_update_hook, area, prefix, | |
633 | route_info); | |
634 | } | |
bdaafbf8 | 635 | |
6cf38339 | 636 | hook_call(isis_route_update_hook, area, prefix, route_info); |
0a5f3f4f RW |
637 | |
638 | SET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); | |
639 | UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); | |
640 | } else { | |
d47d6089 | 641 | /* Uninstall Prefix-SID label. */ |
7153c3ca HS |
642 | for (int i = 0; i < SR_ALGORITHM_COUNT; i++) |
643 | if (route_info->sr_algo[i].present) | |
644 | isis_zebra_prefix_sid_uninstall( | |
645 | area, prefix, route_info, | |
646 | &route_info->sr_algo[i]); | |
647 | ||
d47d6089 | 648 | /* Uninstall route. */ |
41c2bf85 LS |
649 | isis_zebra_route_del_route(area->isis, prefix, src_p, |
650 | route_info); | |
6cf38339 | 651 | hook_call(isis_route_update_hook, area, prefix, route_info); |
0a5f3f4f RW |
652 | |
653 | UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); | |
654 | } | |
655 | } | |
656 | ||
3dace42d CF |
657 | static void _isis_route_verify_table(struct isis_area *area, |
658 | struct route_table *table, | |
c951ee6e | 659 | struct route_table *table_backup, |
3dace42d | 660 | struct route_table **tables) |
eb5d44eb | 661 | { |
d62a17ae | 662 | struct route_node *rnode, *drnode; |
663 | struct isis_route_info *rinfo; | |
1b2b2677 | 664 | #ifdef EXTREME_DEBUG |
321c1bbb | 665 | char buff[SRCDEST2STR_BUFFER]; |
1b2b2677 | 666 | #endif /* EXTREME_DEBUG */ |
7153c3ca | 667 | uint8_t algorithm = isis_route_table_algorithm(table); |
d62a17ae | 668 | |
321c1bbb CF |
669 | for (rnode = route_top(table); rnode; |
670 | rnode = srcdest_route_next(rnode)) { | |
d62a17ae | 671 | if (rnode->info == NULL) |
672 | continue; | |
673 | rinfo = rnode->info; | |
674 | ||
321c1bbb CF |
675 | struct prefix *dst_p; |
676 | struct prefix_ipv6 *src_p; | |
677 | ||
678 | srcdest_rnode_prefixes(rnode, | |
679 | (const struct prefix **)&dst_p, | |
680 | (const struct prefix **)&src_p); | |
681 | ||
c951ee6e RW |
682 | /* Link primary route to backup route. */ |
683 | if (table_backup) { | |
684 | struct route_node *rnode_bck; | |
685 | ||
686 | rnode_bck = srcdest_rnode_lookup(table_backup, dst_p, | |
687 | src_p); | |
688 | if (rnode_bck) { | |
689 | rinfo->backup = rnode_bck->info; | |
7153c3ca | 690 | rinfo->sr_algo[algorithm].nexthops_backup = |
bdaafbf8 | 691 | rinfo->backup->nexthops; |
c951ee6e RW |
692 | UNSET_FLAG(rinfo->flag, |
693 | ISIS_ROUTE_FLAG_ZEBRA_SYNCED); | |
16fe8cff RW |
694 | } else if (rinfo->backup) { |
695 | rinfo->backup = NULL; | |
7153c3ca HS |
696 | rinfo->sr_algo[algorithm].nexthops_backup = |
697 | NULL; | |
16fe8cff RW |
698 | UNSET_FLAG(rinfo->flag, |
699 | ISIS_ROUTE_FLAG_ZEBRA_SYNCED); | |
c951ee6e RW |
700 | } |
701 | } | |
702 | ||
1b2b2677 | 703 | #ifdef EXTREME_DEBUG |
e740f9c1 | 704 | if (IS_DEBUG_RTE_EVENTS) { |
321c1bbb | 705 | srcdest2str(dst_p, src_p, buff, sizeof(buff)); |
d62a17ae | 706 | zlog_debug( |
707 | "ISIS-Rte (%s): route validate: %s %s %s %s", | |
708 | area->area_tag, | |
709 | (CHECK_FLAG(rinfo->flag, | |
710 | ISIS_ROUTE_FLAG_ZEBRA_SYNCED) | |
711 | ? "synced" | |
712 | : "not-synced"), | |
713 | (CHECK_FLAG(rinfo->flag, | |
714 | ISIS_ROUTE_FLAG_ZEBRA_RESYNC) | |
715 | ? "resync" | |
716 | : "not-resync"), | |
717 | (CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE) | |
718 | ? "active" | |
719 | : "inactive"), | |
720 | buff); | |
721 | } | |
1b2b2677 | 722 | #endif /* EXTREME_DEBUG */ |
d62a17ae | 723 | |
6cf38339 | 724 | isis_route_update(area, dst_p, src_p, rinfo); |
bcd9fd50 CF |
725 | |
726 | if (CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE)) | |
727 | continue; | |
728 | ||
80ef2e89 | 729 | /* In case the verify is not for a merge, we use a single table |
bcd9fd50 CF |
730 | * directly for |
731 | * validating => no problems with deleting routes. */ | |
732 | if (!tables) { | |
6cf38339 | 733 | isis_route_delete(area, rnode, table); |
bcd9fd50 CF |
734 | continue; |
735 | } | |
736 | ||
80ef2e89 HS |
737 | /* If we work on a merged table, |
738 | * therefore we must | |
739 | * delete node from each table as well before deleting | |
bcd9fd50 | 740 | * route info. */ |
80ef2e89 HS |
741 | for (int i = 0; tables[i]; i++) { |
742 | drnode = srcdest_rnode_lookup(tables[i], dst_p, src_p); | |
bcd9fd50 | 743 | if (!drnode) |
d62a17ae | 744 | continue; |
3dace42d | 745 | |
bcd9fd50 | 746 | route_unlock_node(drnode); |
d62a17ae | 747 | |
bcd9fd50 CF |
748 | if (drnode->info != rnode->info) |
749 | continue; | |
750 | ||
751 | drnode->info = NULL; | |
752 | route_unlock_node(drnode); | |
d62a17ae | 753 | } |
bcd9fd50 | 754 | |
6cf38339 | 755 | isis_route_delete(area, rnode, table); |
f390d2c7 | 756 | } |
fac1f7cc | 757 | } |
758 | ||
80ef2e89 HS |
759 | static void _isis_route_verify_merge(struct isis_area *area, |
760 | struct route_table **tables, | |
761 | struct route_table **tables_backup, | |
762 | int tree); | |
763 | ||
c951ee6e | 764 | void isis_route_verify_table(struct isis_area *area, struct route_table *table, |
80ef2e89 | 765 | struct route_table *table_backup, int tree) |
3dace42d | 766 | { |
80ef2e89 HS |
767 | struct route_table *tables[SR_ALGORITHM_COUNT] = {table}; |
768 | struct route_table *tables_backup[SR_ALGORITHM_COUNT] = {table_backup}; | |
769 | #ifndef FABRICD | |
770 | int tables_next = 1; | |
771 | int level = area->is_type == IS_LEVEL_1 ? ISIS_LEVEL1 : ISIS_LEVEL2; | |
772 | struct listnode *node; | |
773 | struct flex_algo *fa; | |
774 | struct isis_flex_algo_data *data; | |
775 | ||
776 | for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, fa)) { | |
777 | data = fa->data; | |
778 | tables[tables_next] = | |
779 | data->spftree[tree][level - 1]->route_table; | |
780 | tables_backup[tables_next] = | |
781 | data->spftree[tree][level - 1]->route_table_backup; | |
782 | _isis_route_verify_table(area, tables[tables_next], | |
783 | tables_backup[tables_next], NULL); | |
784 | tables_next++; | |
785 | } | |
786 | #endif /* ifndef FABRICD */ | |
787 | ||
788 | _isis_route_verify_merge(area, tables, tables_backup, tree); | |
3dace42d CF |
789 | } |
790 | ||
fac1f7cc | 791 | /* Function to validate route tables for L1L2 areas. In this case we can't use |
792 | * level route tables directly, we have to merge them at first. L1 routes are | |
793 | * preferred over the L2 ones. | |
794 | * | |
795 | * Merge algorithm is trivial (at least for now). All L1 paths are copied into | |
796 | * merge table at first, then L2 paths are added if L1 path for same prefix | |
797 | * doesn't already exists there. | |
798 | * | |
799 | * FIXME: Is it right place to do it at all? Maybe we should push both levels | |
800 | * to the RIB with different zebra route types and let RIB handle this? */ | |
3dace42d CF |
801 | void isis_route_verify_merge(struct isis_area *area, |
802 | struct route_table *level1_table, | |
c951ee6e RW |
803 | struct route_table *level1_table_backup, |
804 | struct route_table *level2_table, | |
80ef2e89 | 805 | struct route_table *level2_table_backup, int tree) |
fac1f7cc | 806 | { |
80ef2e89 | 807 | struct route_table *tables[] = {level1_table, level2_table, NULL}; |
c951ee6e | 808 | struct route_table *tables_backup[] = {level1_table_backup, |
80ef2e89 HS |
809 | level2_table_backup, NULL}; |
810 | _isis_route_verify_merge(area, tables, tables_backup, tree); | |
811 | } | |
812 | ||
813 | static void _isis_route_verify_merge(struct isis_area *area, | |
814 | struct route_table **tables, | |
815 | struct route_table **tables_backup, | |
816 | int tree) | |
817 | { | |
d62a17ae | 818 | struct route_table *merge; |
819 | struct route_node *rnode, *mrnode; | |
820 | ||
321c1bbb | 821 | merge = srcdest_table_init(); |
d62a17ae | 822 | |
80ef2e89 HS |
823 | for (int i = 0; tables[i]; i++) { |
824 | uint8_t algorithm = isis_route_table_algorithm(tables[i]); | |
825 | for (rnode = route_top(tables[i]); rnode; | |
321c1bbb | 826 | rnode = srcdest_route_next(rnode)) { |
26b0598f | 827 | struct isis_route_info *rinfo = rnode->info; |
c951ee6e RW |
828 | struct route_node *rnode_bck; |
829 | ||
26b0598f | 830 | if (!rinfo) |
3dace42d | 831 | continue; |
26b0598f | 832 | |
321c1bbb CF |
833 | struct prefix *prefix; |
834 | struct prefix_ipv6 *src_p; | |
835 | ||
836 | srcdest_rnode_prefixes(rnode, | |
837 | (const struct prefix **)&prefix, | |
838 | (const struct prefix **)&src_p); | |
c951ee6e RW |
839 | |
840 | /* Link primary route to backup route. */ | |
80ef2e89 HS |
841 | rnode_bck = srcdest_rnode_lookup(tables_backup[i], |
842 | prefix, src_p); | |
c951ee6e RW |
843 | if (rnode_bck) { |
844 | rinfo->backup = rnode_bck->info; | |
7153c3ca | 845 | rinfo->sr_algo[algorithm].nexthops_backup = |
bdaafbf8 | 846 | rinfo->backup->nexthops; |
c951ee6e RW |
847 | UNSET_FLAG(rinfo->flag, |
848 | ISIS_ROUTE_FLAG_ZEBRA_SYNCED); | |
16fe8cff RW |
849 | } else if (rinfo->backup) { |
850 | rinfo->backup = NULL; | |
7153c3ca HS |
851 | rinfo->sr_algo[algorithm].nexthops_backup = |
852 | NULL; | |
16fe8cff RW |
853 | UNSET_FLAG(rinfo->flag, |
854 | ISIS_ROUTE_FLAG_ZEBRA_SYNCED); | |
c951ee6e RW |
855 | } |
856 | ||
321c1bbb | 857 | mrnode = srcdest_rnode_get(merge, prefix, src_p); |
26b0598f CF |
858 | struct isis_route_info *mrinfo = mrnode->info; |
859 | if (mrinfo) { | |
3dace42d | 860 | route_unlock_node(mrnode); |
7153c3ca HS |
861 | set_merge_route_info_sr_algo(mrinfo, rinfo); |
862 | ||
26b0598f CF |
863 | if (CHECK_FLAG(mrinfo->flag, |
864 | ISIS_ROUTE_FLAG_ACTIVE)) { | |
865 | /* Clear the ZEBRA_SYNCED flag on the | |
866 | * L2 route when L1 wins, otherwise L2 | |
867 | * won't get reinstalled when L1 | |
868 | * disappears. | |
869 | */ | |
870 | UNSET_FLAG( | |
871 | rinfo->flag, | |
872 | ISIS_ROUTE_FLAG_ZEBRA_SYNCED | |
873 | ); | |
874 | continue; | |
688ea1cb EDP |
875 | } else if (CHECK_FLAG(rinfo->flag, |
876 | ISIS_ROUTE_FLAG_ACTIVE)) { | |
26b0598f CF |
877 | /* Clear the ZEBRA_SYNCED flag on the L1 |
878 | * route when L2 wins, otherwise L1 | |
879 | * won't get reinstalled when it | |
880 | * reappears. | |
881 | */ | |
882 | UNSET_FLAG( | |
883 | mrinfo->flag, | |
884 | ISIS_ROUTE_FLAG_ZEBRA_SYNCED | |
885 | ); | |
688ea1cb EDP |
886 | } else if ( |
887 | CHECK_FLAG( | |
888 | mrinfo->flag, | |
889 | ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) { | |
890 | continue; | |
26b0598f | 891 | } |
224eadd9 EK |
892 | } else { |
893 | mrnode->info = rnode->info; | |
3dace42d | 894 | } |
3dace42d | 895 | } |
d62a17ae | 896 | } |
897 | ||
c951ee6e | 898 | _isis_route_verify_table(area, merge, NULL, tables); |
d62a17ae | 899 | route_table_finish(merge); |
fac1f7cc | 900 | } |
901 | ||
d62a17ae | 902 | void isis_route_invalidate_table(struct isis_area *area, |
903 | struct route_table *table) | |
3f045a08 | 904 | { |
d62a17ae | 905 | struct route_node *rode; |
906 | struct isis_route_info *rinfo; | |
7153c3ca | 907 | uint8_t algorithm = isis_route_table_algorithm(table); |
321c1bbb | 908 | for (rode = route_top(table); rode; rode = srcdest_route_next(rode)) { |
d62a17ae | 909 | if (rode->info == NULL) |
910 | continue; | |
911 | rinfo = rode->info; | |
912 | ||
c951ee6e RW |
913 | if (rinfo->backup) { |
914 | rinfo->backup = NULL; | |
7153c3ca | 915 | rinfo->sr_algo[algorithm].nexthops_backup = NULL; |
c951ee6e RW |
916 | /* |
917 | * For now, always force routes that have backup | |
918 | * nexthops to be reinstalled. | |
919 | */ | |
920 | UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); | |
921 | } | |
d62a17ae | 922 | UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE); |
923 | } | |
3f045a08 | 924 | } |
694fa867 LS |
925 | |
926 | void isis_route_switchover_nexthop(struct isis_area *area, | |
927 | struct route_table *table, int family, | |
928 | union g_addr *nexthop_addr, | |
929 | ifindex_t ifindex) | |
930 | { | |
931 | const char *ifname = NULL, *vrfname = NULL; | |
932 | struct isis_route_info *rinfo; | |
933 | struct prefix_ipv6 *src_p; | |
934 | struct route_node *rnode; | |
935 | vrf_id_t vrf_id; | |
936 | struct prefix *prefix; | |
937 | ||
938 | if (IS_DEBUG_EVENTS) { | |
939 | if (area && area->isis) { | |
940 | vrf_id = area->isis->vrf_id; | |
941 | vrfname = vrf_id_to_name(vrf_id); | |
942 | ifname = ifindex2ifname(ifindex, vrf_id); | |
943 | } | |
944 | zlog_debug("%s: initiating fast-reroute %s on VRF %s iface %s", | |
945 | __func__, family2str(family), vrfname ? vrfname : "", | |
946 | ifname ? ifname : ""); | |
947 | } | |
948 | ||
949 | for (rnode = route_top(table); rnode; | |
950 | rnode = srcdest_route_next(rnode)) { | |
951 | if (!rnode->info) | |
952 | continue; | |
953 | rinfo = rnode->info; | |
954 | ||
955 | if (!rinfo->backup) | |
956 | continue; | |
957 | ||
958 | if (!nexthoplookup(rinfo->nexthops, family, nexthop_addr, | |
959 | ifindex)) | |
960 | continue; | |
961 | ||
962 | srcdest_rnode_prefixes(rnode, (const struct prefix **)&prefix, | |
963 | (const struct prefix **)&src_p); | |
964 | ||
965 | /* Switchover route. */ | |
8a65b22d | 966 | isis_route_remove_previous_sid(area, prefix, rinfo); |
694fa867 LS |
967 | UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); |
968 | isis_route_update(area, prefix, src_p, rinfo->backup); | |
969 | ||
970 | isis_route_info_delete(rinfo); | |
971 | ||
972 | rnode->info = NULL; | |
973 | route_unlock_node(rnode); | |
974 | } | |
975 | } |