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; if not, write to the Free Software Foundation, Inc.,
22 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
37 #include "isis_constants.h"
38 #include "isis_common.h"
41 #include "isis_misc.h"
42 #include "isis_adjacency.h"
43 #include "isis_circuit.h"
48 #include "isis_route.h"
49 #include "isis_zebra.h"
51 extern struct isis
*isis
;
52 extern struct thread_master
*master
;
54 static struct isis_nexthop
*
55 isis_nexthop_create (struct in_addr
*ip
, unsigned int ifindex
)
57 struct listnode
*node
;
58 struct isis_nexthop
*nexthop
;
60 for (ALL_LIST_ELEMENTS_RO (isis
->nexthops
, node
, nexthop
))
62 if (nexthop
->ifindex
!= ifindex
)
64 if (ip
&& memcmp (&nexthop
->ip
, ip
, sizeof (struct in_addr
)) != 0)
71 nexthop
= XCALLOC (MTYPE_ISIS_NEXTHOP
, sizeof (struct isis_nexthop
));
74 zlog_err ("ISIS-Rte: isis_nexthop_create: out of memory!");
77 nexthop
->ifindex
= ifindex
;
78 memcpy (&nexthop
->ip
, ip
, sizeof (struct in_addr
));
79 listnode_add (isis
->nexthops
, nexthop
);
86 isis_nexthop_delete (struct isis_nexthop
*nexthop
)
89 if (nexthop
->lock
== 0)
91 listnode_delete (isis
->nexthops
, nexthop
);
92 XFREE (MTYPE_ISIS_NEXTHOP
, nexthop
);
99 nexthoplookup (struct list
*nexthops
, struct in_addr
*ip
,
100 unsigned int ifindex
)
102 struct listnode
*node
;
103 struct isis_nexthop
*nh
;
105 for (ALL_LIST_ELEMENTS_RO (nexthops
, node
, nh
))
107 if (!(memcmp (ip
, &nh
->ip
, sizeof (struct in_addr
))) &&
108 ifindex
== nh
->ifindex
)
117 nexthop_print (struct isis_nexthop
*nh
)
121 inet_ntop (AF_INET
, &nh
->ip
, (char *) buf
, BUFSIZ
);
123 zlog_debug (" %s %u", buf
, nh
->ifindex
);
127 nexthops_print (struct list
*nhs
)
129 struct listnode
*node
;
130 struct isis_nexthop
*nh
;
132 for (ALL_LIST_ELEMENTS_RO (nhs
, node
, nh
))
135 #endif /* EXTREME_DEBUG */
138 static struct isis_nexthop6
*
139 isis_nexthop6_new (struct in6_addr
*ip6
, unsigned int ifindex
)
141 struct isis_nexthop6
*nexthop6
;
143 nexthop6
= XCALLOC (MTYPE_ISIS_NEXTHOP6
, sizeof (struct isis_nexthop6
));
146 zlog_err ("ISIS-Rte: isis_nexthop_create6: out of memory!");
149 nexthop6
->ifindex
= ifindex
;
150 memcpy (&nexthop6
->ip6
, ip6
, sizeof (struct in6_addr
));
156 static struct isis_nexthop6
*
157 isis_nexthop6_create (struct in6_addr
*ip6
, unsigned int ifindex
)
159 struct listnode
*node
;
160 struct isis_nexthop6
*nexthop6
;
162 for (ALL_LIST_ELEMENTS_RO (isis
->nexthops6
, node
, nexthop6
))
164 if (nexthop6
->ifindex
!= ifindex
)
166 if (ip6
&& memcmp (&nexthop6
->ip6
, ip6
, sizeof (struct in6_addr
)) != 0)
173 nexthop6
= isis_nexthop6_new (ip6
, ifindex
);
179 isis_nexthop6_delete (struct isis_nexthop6
*nexthop6
)
183 if (nexthop6
->lock
== 0)
185 listnode_delete (isis
->nexthops6
, nexthop6
);
186 XFREE (MTYPE_ISIS_NEXTHOP6
, nexthop6
);
193 nexthop6lookup (struct list
*nexthops6
, struct in6_addr
*ip6
,
194 unsigned int ifindex
)
196 struct listnode
*node
;
197 struct isis_nexthop6
*nh6
;
199 for (ALL_LIST_ELEMENTS_RO (nexthops6
, node
, nh6
))
201 if (!(memcmp (ip6
, &nh6
->ip6
, sizeof (struct in6_addr
))) &&
202 ifindex
== nh6
->ifindex
)
211 nexthop6_print (struct isis_nexthop6
*nh6
)
215 inet_ntop (AF_INET6
, &nh6
->ip6
, (char *) buf
, BUFSIZ
);
217 zlog_debug (" %s %u", buf
, nh6
->ifindex
);
221 nexthops6_print (struct list
*nhs6
)
223 struct listnode
*node
;
224 struct isis_nexthop6
*nh6
;
226 for (ALL_LIST_ELEMENTS_RO (nhs6
, node
, nh6
))
227 nexthop6_print (nh6
);
229 #endif /* EXTREME_DEBUG */
230 #endif /* HAVE_IPV6 */
233 adjinfo2nexthop (struct list
*nexthops
, struct isis_adjacency
*adj
)
235 struct isis_nexthop
*nh
;
236 struct listnode
*node
;
237 struct in_addr
*ipv4_addr
;
239 if (adj
->ipv4_addrs
== NULL
)
242 for (ALL_LIST_ELEMENTS_RO (adj
->ipv4_addrs
, node
, ipv4_addr
))
244 if (!nexthoplookup (nexthops
, ipv4_addr
,
245 adj
->circuit
->interface
->ifindex
))
247 nh
= isis_nexthop_create (ipv4_addr
,
248 adj
->circuit
->interface
->ifindex
);
249 listnode_add (nexthops
, nh
);
256 adjinfo2nexthop6 (struct list
*nexthops6
, struct isis_adjacency
*adj
)
258 struct listnode
*node
;
259 struct in6_addr
*ipv6_addr
;
260 struct isis_nexthop6
*nh6
;
262 if (!adj
->ipv6_addrs
)
265 for (ALL_LIST_ELEMENTS_RO (adj
->ipv6_addrs
, node
, ipv6_addr
))
267 if (!nexthop6lookup (nexthops6
, ipv6_addr
,
268 adj
->circuit
->interface
->ifindex
))
270 nh6
= isis_nexthop6_create (ipv6_addr
,
271 adj
->circuit
->interface
->ifindex
);
272 listnode_add (nexthops6
, nh6
);
276 #endif /* HAVE_IPV6 */
278 static struct isis_route_info
*
279 isis_route_info_new (uint32_t cost
, uint32_t depth
, u_char family
,
280 struct list
*adjacencies
)
282 struct isis_route_info
*rinfo
;
283 struct isis_adjacency
*adj
;
284 struct listnode
*node
;
286 rinfo
= XCALLOC (MTYPE_ISIS_ROUTE_INFO
, sizeof (struct isis_route_info
));
289 zlog_err ("ISIS-Rte: isis_route_info_new: out of memory!");
293 if (family
== AF_INET
)
295 rinfo
->nexthops
= list_new ();
296 for (ALL_LIST_ELEMENTS_RO (adjacencies
, node
, adj
))
297 adjinfo2nexthop (rinfo
->nexthops
, adj
);
300 if (family
== AF_INET6
)
302 rinfo
->nexthops6
= list_new ();
303 for (ALL_LIST_ELEMENTS_RO (adjacencies
, node
, adj
))
304 adjinfo2nexthop6 (rinfo
->nexthops6
, adj
);
307 #endif /* HAVE_IPV6 */
310 rinfo
->depth
= depth
;
316 isis_route_info_delete (struct isis_route_info
*route_info
)
318 if (route_info
->nexthops
)
320 route_info
->nexthops
->del
= (void (*)(void *)) isis_nexthop_delete
;
321 list_delete (route_info
->nexthops
);
325 if (route_info
->nexthops6
)
327 route_info
->nexthops6
->del
= (void (*)(void *)) isis_nexthop6_delete
;
328 list_delete (route_info
->nexthops6
);
330 #endif /* HAVE_IPV6 */
332 XFREE (MTYPE_ISIS_ROUTE_INFO
, route_info
);
336 isis_route_info_same_attrib (struct isis_route_info
*new,
337 struct isis_route_info
*old
)
339 if (new->cost
!= old
->cost
)
341 if (new->depth
!= old
->depth
)
348 isis_route_info_same (struct isis_route_info
*new,
349 struct isis_route_info
*old
, u_char family
)
351 struct listnode
*node
;
352 struct isis_nexthop
*nexthop
;
354 struct isis_nexthop6
*nexthop6
;
355 #endif /* HAVE_IPV6 */
356 if (!isis_route_info_same_attrib (new, old
))
359 if (family
== AF_INET
)
361 for (ALL_LIST_ELEMENTS_RO (new->nexthops
, node
, nexthop
))
362 if (nexthoplookup (old
->nexthops
, &nexthop
->ip
, nexthop
->ifindex
)
366 for (ALL_LIST_ELEMENTS_RO (old
->nexthops
, node
, nexthop
))
367 if (nexthoplookup (new->nexthops
, &nexthop
->ip
, nexthop
->ifindex
)
372 else if (family
== AF_INET6
)
374 for (ALL_LIST_ELEMENTS_RO (new->nexthops6
, node
, nexthop6
))
375 if (nexthop6lookup (old
->nexthops6
, &nexthop6
->ip6
,
376 nexthop6
->ifindex
) == 0)
379 for (ALL_LIST_ELEMENTS_RO (old
->nexthops6
, node
, nexthop6
))
380 if (nexthop6lookup (new->nexthops6
, &nexthop6
->ip6
,
381 nexthop6
->ifindex
) == 0)
384 #endif /* HAVE_IPV6 */
390 isis_nexthops_merge (struct list
*new, struct list
*old
)
392 struct listnode
*node
;
393 struct isis_nexthop
*nexthop
;
395 for (ALL_LIST_ELEMENTS_RO (new, node
, nexthop
))
397 if (nexthoplookup (old
, &nexthop
->ip
, nexthop
->ifindex
))
399 listnode_add (old
, nexthop
);
406 isis_nexthops6_merge (struct list
*new, struct list
*old
)
408 struct listnode
*node
;
409 struct isis_nexthop6
*nexthop6
;
411 for (ALL_LIST_ELEMENTS_RO (new, node
, nexthop6
))
413 if (nexthop6lookup (old
, &nexthop6
->ip6
, nexthop6
->ifindex
))
415 listnode_add (old
, nexthop6
);
419 #endif /* HAVE_IPV6 */
422 isis_route_info_merge (struct isis_route_info
*new,
423 struct isis_route_info
*old
, u_char family
)
425 if (family
== AF_INET
)
426 isis_nexthops_merge (new->nexthops
, old
->nexthops
);
428 else if (family
== AF_INET6
)
429 isis_nexthops6_merge (new->nexthops6
, old
->nexthops6
);
430 #endif /* HAVE_IPV6 */
436 isis_route_info_prefer_new (struct isis_route_info
*new,
437 struct isis_route_info
*old
)
439 if (!CHECK_FLAG (old
->flag
, ISIS_ROUTE_FLAG_ACTIVE
))
442 if (new->cost
< old
->cost
)
448 struct isis_route_info
*
449 isis_route_create (struct prefix
*prefix
, u_int32_t cost
, u_int32_t depth
,
450 struct list
*adjacencies
, struct isis_area
*area
,
453 struct route_node
*route_node
;
454 struct isis_route_info
*rinfo_new
, *rinfo_old
, *route_info
= NULL
;
458 family
= prefix
->family
;
460 prefix2str (prefix
, (char *) buff
, BUFSIZ
);
462 rinfo_new
= isis_route_info_new (cost
, depth
, family
, adjacencies
);
465 zlog_err ("ISIS-Rte (%s): isis_route_create: out of memory!",
470 if (family
== AF_INET
)
471 route_node
= route_node_get (area
->route_table
[level
- 1], prefix
);
473 else if (family
== AF_INET6
)
474 route_node
= route_node_get (area
->route_table6
[level
- 1], prefix
);
475 #endif /* HAVE_IPV6 */
478 rinfo_old
= route_node
->info
;
481 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
482 zlog_debug ("ISIS-Rte (%s) route created: %s", area
->area_tag
, buff
);
483 SET_FLAG (rinfo_new
->flag
, ISIS_ROUTE_FLAG_ACTIVE
);
484 route_node
->info
= rinfo_new
;
488 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
489 zlog_debug ("ISIS-Rte (%s) route already exists: %s", area
->area_tag
,
492 if (isis_route_info_same (rinfo_new
, rinfo_old
, family
))
494 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
495 zlog_debug ("ISIS-Rte (%s) route unchanged: %s", area
->area_tag
, buff
);
496 isis_route_info_delete (rinfo_new
);
497 route_info
= rinfo_old
;
499 else if (isis_route_info_same_attrib (rinfo_new
, rinfo_old
))
501 /* merge the nexthop lists */
502 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
503 zlog_debug ("ISIS-Rte (%s) route changed (same attribs): %s",
504 area
->area_tag
, buff
);
506 if (family
== AF_INET
)
508 zlog_debug ("Old nexthops");
509 nexthops_print (rinfo_old
->nexthops
);
510 zlog_debug ("New nexthops");
511 nexthops_print (rinfo_new
->nexthops
);
513 else if (family
== AF_INET6
)
515 zlog_debug ("Old nexthops");
516 nexthops6_print (rinfo_old
->nexthops6
);
517 zlog_debug ("New nexthops");
518 nexthops6_print (rinfo_new
->nexthops6
);
520 #endif /* EXTREME_DEBUG */
521 isis_route_info_merge (rinfo_new
, rinfo_old
, family
);
522 isis_route_info_delete (rinfo_new
);
523 route_info
= rinfo_old
;
524 UNSET_FLAG (route_info
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNC
);
528 if (isis_route_info_prefer_new (rinfo_new
, rinfo_old
))
530 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
531 zlog_debug ("ISIS-Rte (%s) route changed: %s", area
->area_tag
,
533 isis_route_info_delete (rinfo_old
);
534 route_info
= rinfo_new
;
538 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
539 zlog_debug ("ISIS-Rte (%s) route rejected: %s", area
->area_tag
,
541 isis_route_info_delete (rinfo_new
);
542 route_info
= rinfo_old
;
546 SET_FLAG (route_info
->flag
, ISIS_ROUTE_FLAG_ACTIVE
);
547 route_node
->info
= route_info
;
553 isis_route_delete (struct prefix
*prefix
, struct route_table
*table
)
555 struct route_node
*rode
;
556 struct isis_route_info
*rinfo
;
560 prefix2str (prefix
, buff
, BUFSIZ
);
563 rode
= route_node_get (table
, prefix
);
568 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
569 zlog_debug ("ISIS-Rte: tried to delete non-existant route %s", buff
);
573 if (CHECK_FLAG (rinfo
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNC
))
575 UNSET_FLAG (rinfo
->flag
, ISIS_ROUTE_FLAG_ACTIVE
);
576 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
577 zlog_debug ("ISIS-Rte: route delete %s", buff
);
578 isis_zebra_route_update (prefix
, rinfo
);
580 isis_route_info_delete (rinfo
);
586 /* Validating routes in particular table. */
588 isis_route_validate_table (struct isis_area
*area
, struct route_table
*table
)
590 struct route_node
*rnode
, *drnode
;
591 struct isis_route_info
*rinfo
;
594 for (rnode
= route_top (table
); rnode
; rnode
= route_next (rnode
))
596 if (rnode
->info
== NULL
)
600 if (isis
->debugs
& DEBUG_RTE_EVENTS
)
602 prefix2str (&rnode
->p
, (char *) buff
, BUFSIZ
);
603 zlog_debug ("ISIS-Rte (%s): route validate: %s %s %s",
605 (CHECK_FLAG (rinfo
->flag
, ISIS_ROUTE_FLAG_ZEBRA_SYNC
) ?
606 "sync'ed" : "nosync"),
607 (CHECK_FLAG (rinfo
->flag
, ISIS_ROUTE_FLAG_ACTIVE
) ?
608 "active" : "inactive"), buff
);
611 isis_zebra_route_update (&rnode
->p
, rinfo
);
612 if (!CHECK_FLAG (rinfo
->flag
, ISIS_ROUTE_FLAG_ACTIVE
))
614 /* Area is either L1 or L2 => we use level route tables directly for
615 * validating => no problems with deleting routes. */
616 if (area
->is_type
!= IS_LEVEL_1_AND_2
)
618 isis_route_delete (&rnode
->p
, table
);
621 /* If area is L1L2, we work with merge table and therefore must
622 * delete node from level tables as well before deleting route info.
623 * FIXME: Is it performance problem? There has to be the better way.
624 * Like not to deal with it here at all (see the next comment)? */
625 if (rnode
->p
.family
== AF_INET
)
627 drnode
= route_node_get (area
->route_table
[0], &rnode
->p
);
628 if (drnode
->info
== rnode
->info
)
630 drnode
= route_node_get (area
->route_table
[1], &rnode
->p
);
631 if (drnode
->info
== rnode
->info
)
636 if (rnode
->p
.family
== AF_INET6
)
638 drnode
= route_node_get (area
->route_table6
[0], &rnode
->p
);
639 if (drnode
->info
== rnode
->info
)
641 drnode
= route_node_get (area
->route_table6
[1], &rnode
->p
);
642 if (drnode
->info
== rnode
->info
)
647 isis_route_delete (&rnode
->p
, table
);
652 /* Function to validate route tables for L1L2 areas. In this case we can't use
653 * level route tables directly, we have to merge them at first. L1 routes are
654 * preferred over the L2 ones.
656 * Merge algorithm is trivial (at least for now). All L1 paths are copied into
657 * merge table at first, then L2 paths are added if L1 path for same prefix
658 * doesn't already exists there.
660 * FIXME: Is it right place to do it at all? Maybe we should push both levels
661 * to the RIB with different zebra route types and let RIB handle this? */
663 isis_route_validate_merge (struct isis_area
*area
, int family
)
665 struct route_table
*table
= NULL
;
666 struct route_table
*merge
;
667 struct route_node
*rnode
, *mrnode
;
669 merge
= route_table_init ();
671 if (family
== AF_INET
)
672 table
= area
->route_table
[0];
674 else if (family
== AF_INET6
)
675 table
= area
->route_table6
[0];
678 for (rnode
= route_top (table
); rnode
; rnode
= route_next (rnode
))
680 if (rnode
->info
== NULL
)
682 mrnode
= route_node_get (merge
, &rnode
->p
);
683 mrnode
->info
= rnode
->info
;
686 if (family
== AF_INET
)
687 table
= area
->route_table
[1];
689 else if (family
== AF_INET6
)
690 table
= area
->route_table6
[1];
693 for (rnode
= route_top (table
); rnode
; rnode
= route_next (rnode
))
695 if (rnode
->info
== NULL
)
697 mrnode
= route_node_get (merge
, &rnode
->p
);
698 if (mrnode
->info
!= NULL
)
700 mrnode
->info
= rnode
->info
;
703 isis_route_validate_table (area
, merge
);
704 route_table_finish (merge
);
707 /* Walk through route tables and propagate necessary changes into RIB. In case
708 * of L1L2 area, level tables have to be merged at first. */
710 isis_route_validate (struct thread
*thread
)
712 struct isis_area
*area
;
714 area
= THREAD_ARG (thread
);
716 if (area
->is_type
== IS_LEVEL_1
)
718 isis_route_validate_table (area
, area
->route_table
[0]);
721 if (area
->is_type
== IS_LEVEL_2
)
723 isis_route_validate_table (area
, area
->route_table
[1]);
727 isis_route_validate_merge (area
, AF_INET
);
731 if (area
->is_type
== IS_LEVEL_1
)
733 isis_route_validate_table (area
, area
->route_table6
[0]);
736 if (area
->is_type
== IS_LEVEL_2
)
738 isis_route_validate_table (area
, area
->route_table6
[1]);
742 isis_route_validate_merge (area
, AF_INET6
);