2 * IS-IS Rout(e)ing protocol - isis_route.c
3 * Copyright (C) 2001,2002 Sampo Saaristo
4 * Tampere University of Technology
5 * Institute of Communications Engineering
7 * based on ../ospf6d/ospf6_route.[ch]
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public Licenseas published by the Free
12 * Software Foundation; either version 2 of the License, or (at your option)
15 * This program is distributed in the hope that it will be useful,but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
20 * You should have received a copy of the GNU General Public License along
21 * with this program; see the file COPYING; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 #include "lib_errors.h"
37 #include "srcdest_table.h"
39 #include "isis_constants.h"
40 #include "isis_common.h"
41 #include "isis_flags.h"
43 #include "isis_misc.h"
44 #include "isis_adjacency.h"
45 #include "isis_circuit.h"
49 #include "isis_route.h"
50 #include "isis_zebra.h"
52 DEFINE_HOOK(isis_route_update_hook
,
53 (struct isis_area
* area
, struct prefix
*prefix
,
54 struct isis_route_info
*route_info
),
55 (area
, prefix
, route_info
))
57 static struct isis_nexthop
*nexthoplookup(struct list
*nexthops
, int family
,
58 union g_addr
*ip
, ifindex_t ifindex
);
59 static void isis_route_update(struct isis_area
*area
, struct prefix
*prefix
,
60 struct prefix_ipv6
*src_p
,
61 struct isis_route_info
*route_info
);
63 static struct isis_nexthop
*isis_nexthop_create(int family
, union g_addr
*ip
,
66 struct isis_nexthop
*nexthop
;
68 nexthop
= nexthoplookup(isis
->nexthops
, family
, ip
, ifindex
);
74 nexthop
= XCALLOC(MTYPE_ISIS_NEXTHOP
, sizeof(struct isis_nexthop
));
76 nexthop
->family
= family
;
77 nexthop
->ifindex
= ifindex
;
79 listnode_add(isis
->nexthops
, nexthop
);
85 static void isis_nexthop_delete(struct isis_nexthop
*nexthop
)
88 if (nexthop
->lock
== 0) {
89 listnode_delete(isis
->nexthops
, nexthop
);
90 XFREE(MTYPE_ISIS_NEXTHOP
, nexthop
);
96 static struct isis_nexthop
*nexthoplookup(struct list
*nexthops
, int family
,
97 union g_addr
*ip
, ifindex_t ifindex
)
99 struct listnode
*node
;
100 struct isis_nexthop
*nh
;
102 for (ALL_LIST_ELEMENTS_RO(nexthops
, node
, nh
)) {
103 if (nh
->family
!= family
)
105 if (nh
->ifindex
!= ifindex
)
110 if (IPV4_ADDR_CMP(&nh
->ip
.ipv4
, &ip
->ipv4
))
114 if (IPV6_ADDR_CMP(&nh
->ip
.ipv6
, &ip
->ipv6
))
118 flog_err(EC_LIB_DEVELOPMENT
,
119 "%s: unknown address family [%d]", __func__
,
130 static void adjinfo2nexthop(int family
, struct list
*nexthops
,
131 struct isis_adjacency
*adj
)
133 struct isis_nexthop
*nh
;
134 union g_addr ip
= {};
138 for (unsigned int i
= 0; i
< adj
->ipv4_address_count
; i
++) {
139 ip
.ipv4
= adj
->ipv4_addresses
[i
];
141 if (!nexthoplookup(nexthops
, AF_INET
, &ip
,
142 adj
->circuit
->interface
->ifindex
)) {
143 nh
= isis_nexthop_create(
145 adj
->circuit
->interface
->ifindex
);
146 listnode_add(nexthops
, nh
);
152 for (unsigned int i
= 0; i
< adj
->ipv6_address_count
; i
++) {
153 ip
.ipv6
= adj
->ipv6_addresses
[i
];
155 if (!nexthoplookup(nexthops
, AF_INET6
, &ip
,
156 adj
->circuit
->interface
->ifindex
)) {
157 nh
= isis_nexthop_create(
159 adj
->circuit
->interface
->ifindex
);
160 listnode_add(nexthops
, nh
);
166 flog_err(EC_LIB_DEVELOPMENT
, "%s: unknown address family [%d]",
172 static struct isis_route_info
*isis_route_info_new(struct prefix
*prefix
,
173 struct prefix_ipv6
*src_p
,
176 struct list
*adjacencies
)
178 struct isis_route_info
*rinfo
;
179 struct isis_adjacency
*adj
;
180 struct listnode
*node
;
182 rinfo
= XCALLOC(MTYPE_ISIS_ROUTE_INFO
, sizeof(struct isis_route_info
));
184 rinfo
->nexthops
= list_new();
185 for (ALL_LIST_ELEMENTS_RO(adjacencies
, node
, adj
)) {
186 /* check for force resync this route */
187 if (CHECK_FLAG(adj
->circuit
->flags
,
188 ISIS_CIRCUIT_FLAPPED_AFTER_SPF
))
189 SET_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ZEBRA_RESYNC
);
191 /* update neighbor router address */
192 switch (prefix
->family
) {
194 if (depth
== 2 && prefix
->prefixlen
== 32)
195 adj
->router_address
= prefix
->u
.prefix4
;
198 if (depth
== 2 && prefix
->prefixlen
== 128
199 && (!src_p
|| !src_p
->prefixlen
)) {
200 adj
->router_address6
= prefix
->u
.prefix6
;
204 flog_err(EC_LIB_DEVELOPMENT
,
205 "%s: unknown address family [%d]", __func__
,
209 adjinfo2nexthop(prefix
->family
, rinfo
->nexthops
, adj
);
213 rinfo
->depth
= depth
;
218 static void isis_route_info_delete(struct isis_route_info
*route_info
)
220 if (route_info
->nexthops
) {
221 route_info
->nexthops
->del
=
222 (void (*)(void *))isis_nexthop_delete
;
223 list_delete(&route_info
->nexthops
);
226 XFREE(MTYPE_ISIS_ROUTE_INFO
, route_info
);
229 static int isis_route_info_same_attrib(struct isis_route_info
*new,
230 struct isis_route_info
*old
)
232 if (new->cost
!= old
->cost
)
234 if (new->depth
!= old
->depth
)
240 static int isis_route_info_same(struct isis_route_info
*new,
241 struct isis_route_info
*old
, uint8_t family
)
243 struct listnode
*node
;
244 struct isis_nexthop
*nexthop
;
246 if (!CHECK_FLAG(old
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
))
249 if (CHECK_FLAG(new->flag
, ISIS_ROUTE_FLAG_ZEBRA_RESYNC
))
252 if (!isis_route_info_same_attrib(new, old
))
255 for (ALL_LIST_ELEMENTS_RO(new->nexthops
, node
, nexthop
))
256 if (!nexthoplookup(old
->nexthops
, nexthop
->family
, &nexthop
->ip
,
260 for (ALL_LIST_ELEMENTS_RO(old
->nexthops
, node
, nexthop
))
261 if (!nexthoplookup(new->nexthops
, nexthop
->family
, &nexthop
->ip
,
268 struct isis_route_info
*isis_route_create(struct prefix
*prefix
,
269 struct prefix_ipv6
*src_p
,
272 struct list
*adjacencies
,
273 struct isis_area
*area
,
274 struct route_table
*table
)
276 struct route_node
*route_node
;
277 struct isis_route_info
*rinfo_new
, *rinfo_old
, *route_info
= NULL
;
278 char buff
[PREFIX2STR_BUFFER
];
281 family
= prefix
->family
;
283 prefix2str(prefix
, buff
, sizeof(buff
));
288 rinfo_new
= isis_route_info_new(prefix
, src_p
, cost
,
290 route_node
= srcdest_rnode_get(table
, prefix
, src_p
);
292 rinfo_old
= route_node
->info
;
294 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
295 zlog_debug("ISIS-Rte (%s) route created: %s",
296 area
->area_tag
, buff
);
297 route_info
= rinfo_new
;
298 UNSET_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
300 route_unlock_node(route_node
);
301 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
302 zlog_debug("ISIS-Rte (%s) route already exists: %s",
303 area
->area_tag
, buff
);
304 if (isis_route_info_same(rinfo_new
, rinfo_old
, family
)) {
305 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
306 zlog_debug("ISIS-Rte (%s) route unchanged: %s",
307 area
->area_tag
, buff
);
308 isis_route_info_delete(rinfo_new
);
309 route_info
= rinfo_old
;
311 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
312 zlog_debug("ISIS-Rte (%s) route changed: %s",
313 area
->area_tag
, buff
);
314 isis_route_info_delete(rinfo_old
);
315 route_info
= rinfo_new
;
316 UNSET_FLAG(route_info
->flag
,
317 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
321 SET_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ACTIVE
);
322 route_node
->info
= route_info
;
327 static void isis_route_delete(struct isis_area
*area
, struct route_node
*rode
,
328 struct route_table
*table
)
330 struct isis_route_info
*rinfo
;
331 char buff
[SRCDEST2STR_BUFFER
];
332 struct prefix
*prefix
;
333 struct prefix_ipv6
*src_p
;
336 srcdest_rnode2str(rode
, buff
, sizeof(buff
));
338 srcdest_rnode_prefixes(rode
, (const struct prefix
**)&prefix
,
339 (const struct prefix
**)&src_p
);
343 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
345 "ISIS-Rte: tried to delete non-existant route %s",
350 if (CHECK_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
)) {
351 UNSET_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ACTIVE
);
352 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
353 zlog_debug("ISIS-Rte: route delete %s", buff
);
354 isis_route_update(area
, prefix
, src_p
, rinfo
);
356 isis_route_info_delete(rinfo
);
358 route_unlock_node(rode
);
361 static void isis_route_update(struct isis_area
*area
, struct prefix
*prefix
,
362 struct prefix_ipv6
*src_p
,
363 struct isis_route_info
*route_info
)
365 if (CHECK_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ACTIVE
)) {
366 if (CHECK_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
))
369 isis_zebra_route_add_route(prefix
, src_p
, route_info
);
370 hook_call(isis_route_update_hook
, area
, prefix
, route_info
);
372 SET_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
373 UNSET_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_RESYNC
);
375 if (!CHECK_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
))
378 isis_zebra_route_del_route(prefix
, src_p
, route_info
);
379 hook_call(isis_route_update_hook
, area
, prefix
, route_info
);
381 UNSET_FLAG(route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
385 static void _isis_route_verify_table(struct isis_area
*area
,
386 struct route_table
*table
,
387 struct route_table
**tables
)
389 struct route_node
*rnode
, *drnode
;
390 struct isis_route_info
*rinfo
;
391 char buff
[SRCDEST2STR_BUFFER
];
393 for (rnode
= route_top(table
); rnode
;
394 rnode
= srcdest_route_next(rnode
)) {
395 if (rnode
->info
== NULL
)
399 struct prefix
*dst_p
;
400 struct prefix_ipv6
*src_p
;
402 srcdest_rnode_prefixes(rnode
,
403 (const struct prefix
**)&dst_p
,
404 (const struct prefix
**)&src_p
);
406 if (isis
->debugs
& DEBUG_RTE_EVENTS
) {
407 srcdest2str(dst_p
, src_p
, buff
, sizeof(buff
));
409 "ISIS-Rte (%s): route validate: %s %s %s %s",
411 (CHECK_FLAG(rinfo
->flag
,
412 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
)
415 (CHECK_FLAG(rinfo
->flag
,
416 ISIS_ROUTE_FLAG_ZEBRA_RESYNC
)
419 (CHECK_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ACTIVE
)
425 isis_route_update(area
, dst_p
, src_p
, rinfo
);
427 if (CHECK_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ACTIVE
))
430 /* Area is either L1 or L2 => we use level route tables
432 * validating => no problems with deleting routes. */
434 isis_route_delete(area
, rnode
, table
);
438 /* If area is L1L2, we work with merge table and
440 * delete node from level tables as well before deleting
442 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
443 drnode
= srcdest_rnode_lookup(tables
[level
- 1],
448 route_unlock_node(drnode
);
450 if (drnode
->info
!= rnode
->info
)
454 route_unlock_node(drnode
);
457 isis_route_delete(area
, rnode
, table
);
461 void isis_route_verify_table(struct isis_area
*area
, struct route_table
*table
)
463 _isis_route_verify_table(area
, table
, NULL
);
466 /* Function to validate route tables for L1L2 areas. In this case we can't use
467 * level route tables directly, we have to merge them at first. L1 routes are
468 * preferred over the L2 ones.
470 * Merge algorithm is trivial (at least for now). All L1 paths are copied into
471 * merge table at first, then L2 paths are added if L1 path for same prefix
472 * doesn't already exists there.
474 * FIXME: Is it right place to do it at all? Maybe we should push both levels
475 * to the RIB with different zebra route types and let RIB handle this? */
476 void isis_route_verify_merge(struct isis_area
*area
,
477 struct route_table
*level1_table
,
478 struct route_table
*level2_table
)
480 struct route_table
*tables
[] = { level1_table
, level2_table
};
481 struct route_table
*merge
;
482 struct route_node
*rnode
, *mrnode
;
484 merge
= srcdest_table_init();
486 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
487 for (rnode
= route_top(tables
[level
- 1]); rnode
;
488 rnode
= srcdest_route_next(rnode
)) {
489 struct isis_route_info
*rinfo
= rnode
->info
;
493 struct prefix
*prefix
;
494 struct prefix_ipv6
*src_p
;
496 srcdest_rnode_prefixes(rnode
,
497 (const struct prefix
**)&prefix
,
498 (const struct prefix
**)&src_p
);
499 mrnode
= srcdest_rnode_get(merge
, prefix
, src_p
);
500 struct isis_route_info
*mrinfo
= mrnode
->info
;
502 route_unlock_node(mrnode
);
503 if (CHECK_FLAG(mrinfo
->flag
,
504 ISIS_ROUTE_FLAG_ACTIVE
)) {
505 /* Clear the ZEBRA_SYNCED flag on the
506 * L2 route when L1 wins, otherwise L2
507 * won't get reinstalled when L1
512 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
515 } else if (CHECK_FLAG(rinfo
->flag
,
516 ISIS_ROUTE_FLAG_ACTIVE
)) {
517 /* Clear the ZEBRA_SYNCED flag on the L1
518 * route when L2 wins, otherwise L1
519 * won't get reinstalled when it
524 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
529 ISIS_ROUTE_FLAG_ZEBRA_SYNCED
)) {
533 mrnode
->info
= rnode
->info
;
537 _isis_route_verify_table(area
, merge
, tables
);
538 route_table_finish(merge
);
541 void isis_route_invalidate_table(struct isis_area
*area
,
542 struct route_table
*table
)
544 struct route_node
*rode
;
545 struct isis_route_info
*rinfo
;
546 for (rode
= route_top(table
); rode
; rode
= srcdest_route_next(rode
)) {
547 if (rode
->info
== NULL
)
551 UNSET_FLAG(rinfo
->flag
, ISIS_ROUTE_FLAG_ACTIVE
);