+ note the meta-queue in NEWS
[jleu-quagga.git] / isisd / isis_route.c
blob1286486cec6b95bcca8e22f35152d86088e6a456
1 /*
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]
8 * by Yasuhiro Ohara
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)
13 * any later version.
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
18 * more details.
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.
25 #include <zebra.h>
27 #include "thread.h"
28 #include "linklist.h"
29 #include "vty.h"
30 #include "log.h"
31 #include "memory.h"
32 #include "prefix.h"
33 #include "hash.h"
34 #include "if.h"
35 #include "table.h"
37 #include "isis_constants.h"
38 #include "isis_common.h"
39 #include "dict.h"
40 #include "isisd.h"
41 #include "isis_misc.h"
42 #include "isis_adjacency.h"
43 #include "isis_circuit.h"
44 #include "isis_tlv.h"
45 #include "isis_pdu.h"
46 #include "isis_lsp.h"
47 #include "isis_spf.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)
63 continue;
64 if (ip && memcmp (&nexthop->ip, ip, sizeof (struct in_addr)) != 0)
65 continue;
67 nexthop->lock++;
68 return nexthop;
71 nexthop = XCALLOC (MTYPE_ISIS_NEXTHOP, sizeof (struct isis_nexthop));
72 if (!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);
80 nexthop->lock++;
82 return nexthop;
85 static void
86 isis_nexthop_delete (struct isis_nexthop *nexthop)
88 nexthop->lock--;
89 if (nexthop->lock == 0)
91 listnode_delete (isis->nexthops, nexthop);
92 XFREE (MTYPE_ISIS_NEXTHOP, nexthop);
95 return;
98 static int
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)
109 return 1;
112 return 0;
115 #ifdef EXTREME_DEBUG
116 static void
117 nexthop_print (struct isis_nexthop *nh)
119 u_char buf[BUFSIZ];
121 inet_ntop (AF_INET, &nh->ip, (char *) buf, BUFSIZ);
123 zlog_debug (" %s %u", buf, nh->ifindex);
126 static void
127 nexthops_print (struct list *nhs)
129 struct listnode *node;
130 struct isis_nexthop *nh;
132 for (ALL_LIST_ELEMENTS_RO (nhs, node, nh))
133 nexthop_print (nh);
135 #endif /* EXTREME_DEBUG */
137 #ifdef HAVE_IPV6
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));
144 if (!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));
151 nexthop6->lock++;
153 return nexthop6;
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)
165 continue;
166 if (ip6 && memcmp (&nexthop6->ip6, ip6, sizeof (struct in6_addr)) != 0)
167 continue;
169 nexthop6->lock++;
170 return nexthop6;
173 nexthop6 = isis_nexthop6_new (ip6, ifindex);
175 return nexthop6;
178 static void
179 isis_nexthop6_delete (struct isis_nexthop6 *nexthop6)
182 nexthop6->lock--;
183 if (nexthop6->lock == 0)
185 listnode_delete (isis->nexthops6, nexthop6);
186 XFREE (MTYPE_ISIS_NEXTHOP6, nexthop6);
189 return;
192 static int
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)
203 return 1;
206 return 0;
209 #ifdef EXTREME_DEBUG
210 static void
211 nexthop6_print (struct isis_nexthop6 *nh6)
213 u_char buf[BUFSIZ];
215 inet_ntop (AF_INET6, &nh6->ip6, (char *) buf, BUFSIZ);
217 zlog_debug (" %s %u", buf, nh6->ifindex);
220 static void
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 */
232 static void
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)
240 return;
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);
254 #ifdef HAVE_IPV6
255 static void
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)
263 return;
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));
287 if (!rinfo)
289 zlog_err ("ISIS-Rte: isis_route_info_new: out of memory!");
290 return NULL;
293 if (family == AF_INET)
295 rinfo->nexthops = list_new ();
296 for (ALL_LIST_ELEMENTS_RO (adjacencies, node, adj))
297 adjinfo2nexthop (rinfo->nexthops, adj);
299 #ifdef HAVE_IPV6
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 */
309 rinfo->cost = cost;
310 rinfo->depth = depth;
312 return rinfo;
315 static void
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);
324 #ifdef HAVE_IPV6
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);
335 static int
336 isis_route_info_same_attrib (struct isis_route_info *new,
337 struct isis_route_info *old)
339 if (new->cost != old->cost)
340 return 0;
341 if (new->depth != old->depth)
342 return 0;
344 return 1;
347 static int
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;
353 #ifdef HAVE_IPV6
354 struct isis_nexthop6 *nexthop6;
355 #endif /* HAVE_IPV6 */
356 if (!isis_route_info_same_attrib (new, old))
357 return 0;
359 if (family == AF_INET)
361 for (ALL_LIST_ELEMENTS_RO (new->nexthops, node, nexthop))
362 if (nexthoplookup (old->nexthops, &nexthop->ip, nexthop->ifindex)
363 == 0)
364 return 0;
366 for (ALL_LIST_ELEMENTS_RO (old->nexthops, node, nexthop))
367 if (nexthoplookup (new->nexthops, &nexthop->ip, nexthop->ifindex)
368 == 0)
369 return 0;
371 #ifdef HAVE_IPV6
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)
377 return 0;
379 for (ALL_LIST_ELEMENTS_RO (old->nexthops6, node, nexthop6))
380 if (nexthop6lookup (new->nexthops6, &nexthop6->ip6,
381 nexthop6->ifindex) == 0)
382 return 0;
384 #endif /* HAVE_IPV6 */
386 return 1;
389 static void
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))
398 continue;
399 listnode_add (old, nexthop);
400 nexthop->lock++;
404 #ifdef HAVE_IPV6
405 static void
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))
414 continue;
415 listnode_add (old, nexthop6);
416 nexthop6->lock++;
419 #endif /* HAVE_IPV6 */
421 static void
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);
427 #ifdef HAVE_IPV6
428 else if (family == AF_INET6)
429 isis_nexthops6_merge (new->nexthops6, old->nexthops6);
430 #endif /* HAVE_IPV6 */
432 return;
435 static int
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))
440 return 1;
442 if (new->cost < old->cost)
443 return 1;
445 return 0;
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,
451 int level)
453 struct route_node *route_node;
454 struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL;
455 u_char buff[BUFSIZ];
456 u_char family;
458 family = prefix->family;
459 /* for debugs */
460 prefix2str (prefix, (char *) buff, BUFSIZ);
462 rinfo_new = isis_route_info_new (cost, depth, family, adjacencies);
463 if (!rinfo_new)
465 zlog_err ("ISIS-Rte (%s): isis_route_create: out of memory!",
466 area->area_tag);
467 return NULL;
470 if (family == AF_INET)
471 route_node = route_node_get (area->route_table[level - 1], prefix);
472 #ifdef HAVE_IPV6
473 else if (family == AF_INET6)
474 route_node = route_node_get (area->route_table6[level - 1], prefix);
475 #endif /* HAVE_IPV6 */
476 else
477 return NULL;
478 rinfo_old = route_node->info;
479 if (!rinfo_old)
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;
485 return rinfo_new;
488 if (isis->debugs & DEBUG_RTE_EVENTS)
489 zlog_debug ("ISIS-Rte (%s) route already exists: %s", area->area_tag,
490 buff);
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);
505 #ifdef EXTREME_DEBUG
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);
526 else
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,
532 buff);
533 isis_route_info_delete (rinfo_old);
534 route_info = rinfo_new;
536 else
538 if (isis->debugs & DEBUG_RTE_EVENTS)
539 zlog_debug ("ISIS-Rte (%s) route rejected: %s", area->area_tag,
540 buff);
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;
549 return route_info;
552 static void
553 isis_route_delete (struct prefix *prefix, struct route_table *table)
555 struct route_node *rode;
556 struct isis_route_info *rinfo;
557 char buff[BUFSIZ];
559 /* for log */
560 prefix2str (prefix, buff, BUFSIZ);
563 rode = route_node_get (table, prefix);
564 rinfo = rode->info;
566 if (rinfo == NULL)
568 if (isis->debugs & DEBUG_RTE_EVENTS)
569 zlog_debug ("ISIS-Rte: tried to delete non-existant route %s", buff);
570 return;
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);
581 rode->info = NULL;
583 return;
586 /* Validating routes in particular table. */
587 static void
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;
592 u_char buff[BUFSIZ];
594 for (rnode = route_top (table); rnode; rnode = route_next (rnode))
596 if (rnode->info == NULL)
597 continue;
598 rinfo = rnode->info;
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",
604 area->area_tag,
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);
619 continue;
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)
629 drnode->info = NULL;
630 drnode = route_node_get (area->route_table[1], &rnode->p);
631 if (drnode->info == rnode->info)
632 drnode->info = NULL;
635 #ifdef HAVE_IPV6
636 if (rnode->p.family == AF_INET6)
638 drnode = route_node_get (area->route_table6[0], &rnode->p);
639 if (drnode->info == rnode->info)
640 drnode->info = NULL;
641 drnode = route_node_get (area->route_table6[1], &rnode->p);
642 if (drnode->info == rnode->info)
643 drnode->info = NULL;
645 #endif
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? */
662 static void
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];
673 #ifdef HAVE_IPV6
674 else if (family == AF_INET6)
675 table = area->route_table6[0];
676 #endif
678 for (rnode = route_top (table); rnode; rnode = route_next (rnode))
680 if (rnode->info == NULL)
681 continue;
682 mrnode = route_node_get (merge, &rnode->p);
683 mrnode->info = rnode->info;
686 if (family == AF_INET)
687 table = area->route_table[1];
688 #ifdef HAVE_IPV6
689 else if (family == AF_INET6)
690 table = area->route_table6[1];
691 #endif
693 for (rnode = route_top (table); rnode; rnode = route_next (rnode))
695 if (rnode->info == NULL)
696 continue;
697 mrnode = route_node_get (merge, &rnode->p);
698 if (mrnode->info != NULL)
699 continue;
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]);
719 goto validate_ipv6;
721 if (area->is_type == IS_LEVEL_2)
723 isis_route_validate_table (area, area->route_table[1]);
724 goto validate_ipv6;
727 isis_route_validate_merge (area, AF_INET);
729 validate_ipv6:
730 #ifdef HAVE_IPV6
731 if (area->is_type == IS_LEVEL_1)
733 isis_route_validate_table (area, area->route_table6[0]);
734 return ISIS_OK;
736 if (area->is_type == IS_LEVEL_2)
738 isis_route_validate_table (area, area->route_table6[1]);
739 return ISIS_OK;
742 isis_route_validate_merge (area, AF_INET6);
743 #endif
745 return ISIS_OK;