diff options
Diffstat (limited to 'isisd/isis_route.c')
-rw-r--r-- | isisd/isis_route.c | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/isisd/isis_route.c b/isisd/isis_route.c new file mode 100644 index 000000000..74231adc6 --- /dev/null +++ b/isisd/isis_route.c @@ -0,0 +1,615 @@ +/* + * IS-IS Rout(e)ing protocol - isis_route.c + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * based on ../ospf6d/ospf6_route.[ch] + * by Yasuhiro Ohara + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public Licenseas published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <zebra.h> +#include <net/ethernet.h> + +#include "thread.h" +#include "linklist.h" +#include "vty.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "hash.h" +#include "if.h" +#include "table.h" + +#include "isis_constants.h" +#include "isis_common.h" +#include "dict.h" +#include "isisd.h" +#include "isis_misc.h" +#include "isis_adjacency.h" +#include "isis_circuit.h" +#include "isis_tlv.h" +#include "isis_pdu.h" +#include "isis_lsp.h" +#include "isis_spf.h" +#include "isis_route.h" +#include "isis_zebra.h" + +extern struct isis *isis; +extern struct thread_master *master; + +struct isis_nexthop * +isis_nexthop_create (struct in_addr *ip, unsigned int ifindex) + +{ + struct listnode *node; + struct isis_nexthop *nexthop; + + for (node = listhead (isis->nexthops); node; nextnode (node)) { + nexthop = getdata (node); + if (nexthop->ifindex != ifindex) + continue; + if (ip && memcmp (&nexthop->ip, ip, sizeof (struct in_addr)) != 0) + continue; + + nexthop->lock++; + return nexthop; + } + + nexthop = XMALLOC (MTYPE_ISIS_NEXTHOP, sizeof (struct isis_nexthop)); + if (!nexthop) { + zlog_err ("ISIS-Rte: isis_nexthop_create: out of memory!"); + } + + memset (nexthop, 0, sizeof (struct isis_nexthop)); + nexthop->ifindex = ifindex; + memcpy (&nexthop->ip, ip, sizeof (struct in_addr)); + listnode_add (isis->nexthops, nexthop); + nexthop->lock++; + + return nexthop; +} + + +void +isis_nexthop_delete (struct isis_nexthop *nexthop) +{ + nexthop->lock--; + if (nexthop->lock == 0) { + listnode_delete (isis->nexthops, nexthop); + XFREE (MTYPE_ISIS_NEXTHOP, nexthop); + } + + return; +} + +int +nexthoplookup (struct list *nexthops, struct in_addr *ip, + unsigned int ifindex) +{ + struct listnode *node; + struct isis_nexthop *nh; + + for (node = listhead (nexthops); node; nextnode (node)) { + nh = getdata (node); + if (!(memcmp (ip, &nh->ip, sizeof (struct in_addr))) && + ifindex == nh->ifindex) + return 1; + } + + return 0; +} + +void +nexthop_print (struct isis_nexthop *nh) +{ + u_char buf[BUFSIZ]; + + inet_ntop (AF_INET, &nh->ip, buf, BUFSIZ); + + zlog_info (" %s %u", buf, nh->ifindex); +} + +void +nexthops_print (struct list *nhs) +{ + struct listnode *node; + + for (node = listhead(nhs); node; nextnode (node)) + nexthop_print (getdata (node)); +} + +#ifdef HAVE_IPV6 + +struct isis_nexthop6 * +isis_nexthop6_new (struct in6_addr *ip6, unsigned int ifindex) +{ + + struct isis_nexthop6 *nexthop6; + + nexthop6 = XMALLOC (MTYPE_ISIS_NEXTHOP6, sizeof (struct isis_nexthop6)); + if (!nexthop6) { + zlog_err ("ISIS-Rte: isis_nexthop_create6: out of memory!"); + } + + memset (nexthop6, 0, sizeof (struct isis_nexthop6)); + nexthop6->ifindex = ifindex; + memcpy (&nexthop6->ip6, ip6, sizeof (struct in6_addr)); + nexthop6->lock++; + + return nexthop6; +} + +struct isis_nexthop6 * +isis_nexthop6_create (struct in6_addr *ip6, unsigned int ifindex) + +{ + struct listnode *node; + struct isis_nexthop6 *nexthop6; + + for (node = listhead (isis->nexthops6); node; nextnode (node)) { + nexthop6 = getdata (node); + if (nexthop6->ifindex != ifindex) + continue; + if (ip6 && memcmp (&nexthop6->ip6, ip6, sizeof (struct in6_addr)) != 0) + continue; + + nexthop6->lock++; + return nexthop6; + } + + nexthop6 = isis_nexthop6_new (ip6, ifindex); + + return nexthop6; +} + + +void +isis_nexthop6_delete (struct isis_nexthop6 *nexthop6) +{ + + nexthop6->lock--; + if (nexthop6->lock == 0) { + listnode_delete (isis->nexthops6, nexthop6); + XFREE (MTYPE_ISIS_NEXTHOP6, nexthop6); + } + + return; +} + +int +nexthop6lookup (struct list *nexthops6, struct in6_addr *ip6, + unsigned int ifindex) +{ + struct listnode *node; + struct isis_nexthop6 *nh6; + + for (node = listhead (nexthops6); node; nextnode (node)) { + nh6 = getdata (node); + if (!(memcmp (ip6, &nh6->ip6, sizeof (struct in6_addr))) && + ifindex == nh6->ifindex) + return 1; + } + + return 0; +} + +void +nexthop6_print (struct isis_nexthop6 *nh6) +{ + u_char buf[BUFSIZ]; + + inet_ntop (AF_INET6, &nh6->ip6, buf, BUFSIZ); + + zlog_info (" %s %u", buf, nh6->ifindex); +} + +void +nexthops6_print (struct list *nhs6) +{ + struct listnode *node; + + for (node = listhead(nhs6); node; nextnode (node)) + nexthop6_print (getdata (node)); +} + + +#endif /* HAVE_IPV6 */ + +void +adjinfo2nexthop (struct list *nexthops, struct isis_adjacency *adj) +{ + struct isis_nexthop *nh; + struct listnode *node; + struct in_addr *ipv4_addr; + + if (adj->ipv4_addrs == NULL) + return; + for (node = listhead (adj->ipv4_addrs); node; nextnode (node)) { + ipv4_addr = getdata (node); + if (!nexthoplookup (nexthops, ipv4_addr, + adj->circuit->interface->ifindex)) { + nh = isis_nexthop_create (ipv4_addr, + adj->circuit->interface->ifindex); + listnode_add (nexthops, nh); + } + } +} + +#ifdef HAVE_IPV6 +void +adjinfo2nexthop6 (struct list *nexthops6, struct isis_adjacency *adj) +{ + struct listnode *node; + struct in6_addr *ipv6_addr; + struct isis_nexthop6 *nh6; + + if (!adj->ipv6_addrs) + return; + + for (node = listhead (adj->ipv6_addrs); node; nextnode (node)) { + ipv6_addr = getdata (node); + if (!nexthop6lookup (nexthops6, ipv6_addr, + adj->circuit->interface->ifindex)) { + nh6 = isis_nexthop6_create (ipv6_addr, + adj->circuit->interface->ifindex); + listnode_add (nexthops6, nh6); + } + } +} +#endif /* HAVE_IPV6 */ + +struct isis_route_info * +isis_route_info_new (uint32_t cost, uint32_t depth, u_char family, + struct list *adjacencies) +{ + struct isis_route_info *rinfo; + struct isis_adjacency *adj; + struct listnode *node; + + rinfo = XMALLOC (MTYPE_ISIS_ROUTE_INFO, sizeof (struct isis_route_info)); + if (!rinfo) { + zlog_err ("ISIS-Rte: isis_route_info_new: out of memory!"); + return NULL; + } + memset (rinfo, 0, sizeof (struct isis_route_info)); + + if (family == AF_INET) { + rinfo->nexthops = list_new (); + for (node = listhead (adjacencies); node; nextnode (node)) { + adj = getdata (node); + adjinfo2nexthop (rinfo->nexthops, adj); + } + } +#ifdef HAVE_IPV6 + if (family == AF_INET6) { + rinfo->nexthops6 = list_new (); + for (node = listhead (adjacencies); node; nextnode (node)) { + adj =getdata (node); + adjinfo2nexthop6 (rinfo->nexthops6, adj); + } + } + +#endif /* HAVE_IPV6 */ + + rinfo->cost = cost; + rinfo->depth = depth; + + return rinfo; +} + + +void +isis_route_info_delete (struct isis_route_info *route_info) +{ + + if (route_info->nexthops) { + route_info->nexthops->del = (void *)isis_nexthop_delete; + list_delete (route_info->nexthops); + } + +#ifdef HAVE_IPV6 + if (route_info->nexthops6) { + route_info->nexthops6->del = (void *)isis_nexthop6_delete; + list_delete (route_info->nexthops6); + } +#endif /* HAVE_IPV6 */ + + XFREE (MTYPE_ISIS_ROUTE_INFO, route_info); +} + +int +isis_route_info_same_attrib (struct isis_route_info *new, + struct isis_route_info *old) +{ + if (new->cost != old->cost) + return 0; + if (new->depth != old->depth) + return 0; + + return 1; +} + +int +isis_route_info_same (struct isis_route_info *new, struct isis_route_info *old, + u_char family) +{ + struct listnode *node; + struct isis_nexthop *nexthop; +#ifdef HAVE_IPV6 + struct isis_nexthop6 *nexthop6; +#endif /* HAVE_IPV6 */ + if (!isis_route_info_same_attrib (new, old)) + return 0; + + if (family == AF_INET) { + for (node = listhead (new->nexthops); node; nextnode (node)) { + nexthop = (struct isis_nexthop *) getdata (node); + if (nexthoplookup (old->nexthops, &nexthop->ip, nexthop->ifindex) == 0) + return 0; + } + + for (node = listhead (old->nexthops); node; nextnode (node)) { + nexthop = (struct isis_nexthop *) getdata (node); + if (nexthoplookup (new->nexthops, &nexthop->ip, nexthop->ifindex) == 0) + return 0; + } + } +#ifdef HAVE_IPV6 + else if (family == AF_INET6) { + for (node = listhead (new->nexthops6); node; nextnode (node)) { + nexthop6 = (struct isis_nexthop6 *) getdata (node); + if (nexthop6lookup (old->nexthops6, &nexthop6->ip6, + nexthop6->ifindex) == 0) + return 0; + } + + for (node = listhead (old->nexthops6); node; nextnode (node)) { + nexthop6 = (struct isis_nexthop6 *) getdata (node); + if (nexthop6lookup (new->nexthops6, &nexthop6->ip6, + nexthop6->ifindex) == 0) + return 0; + } + } +#endif /* HAVE_IPV6 */ + + return 1; +} + + +void +isis_nexthops_merge (struct list *new, struct list *old) +{ + struct listnode *node; + struct isis_nexthop *nexthop; + + for (node = listhead (new); node; nextnode (node)) { + nexthop = (struct isis_nexthop *) getdata (node); + if (nexthoplookup (old, &nexthop->ip, nexthop->ifindex)) + continue; + listnode_add (old, nexthop); + nexthop->lock++; + } +} + + +#ifdef HAVE_IPV6 +void +isis_nexthops6_merge (struct list *new, struct list *old) +{ + struct listnode *node; + struct isis_nexthop6 *nexthop6; + + for (node = listhead (new); node; nextnode (node)) { + nexthop6 = (struct isis_nexthop6 *) getdata (node); + if (nexthop6lookup (old, &nexthop6->ip6, nexthop6->ifindex)) + continue; + listnode_add (old, nexthop6); + nexthop6->lock++; + } +} +#endif /* HAVE_IPV6 */ + +void +isis_route_info_merge (struct isis_route_info *new, + struct isis_route_info *old, u_char family) +{ + + if (family == AF_INET) + isis_nexthops_merge (new->nexthops, old->nexthops); +#ifdef HAVE_IPV6 + else if (family == AF_INET6) + isis_nexthops6_merge (new->nexthops6, old->nexthops6); +#endif /* HAVE_IPV6 */ + + return; +} + + +int +isis_route_info_prefer_new (struct isis_route_info *new, + struct isis_route_info *old) +{ + + if (!CHECK_FLAG (old->flag, ISIS_ROUTE_FLAG_ACTIVE)) + return 1; + + if (new->cost < old->cost) + return 1; + + return 0; +} + + +struct isis_route_info * +isis_route_create (struct prefix *prefix, u_int32_t cost, u_int32_t depth, + struct list *adjacencies, struct isis_area *area) +{ + struct route_node *route_node; + struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL; + u_char buff[BUFSIZ]; + u_char family; + + family = prefix->family; + /* for debugs */ + prefix2str (prefix, buff, BUFSIZ); + + rinfo_new = isis_route_info_new (cost, depth, family, adjacencies); + if (!rinfo_new) { + zlog_err ("ISIS-Rte (%s): isis_route_create: out of memory!", + area->area_tag); + return NULL; + } + + if (family == AF_INET) + route_node = route_node_get (area->route_table, prefix); +#ifdef HAVE_IPV6 + else if (family == AF_INET6) + route_node = route_node_get (area->route_table6, prefix); +#endif /* HAVE_IPV6 */ + else + return NULL; + rinfo_old = route_node->info; + if (!rinfo_old) { + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_info ("ISIS-Rte (%s) route created: %s", area->area_tag, buff); + SET_FLAG(rinfo_new->flag, ISIS_ROUTE_FLAG_ACTIVE); + route_node->info = rinfo_new; + return rinfo_new; + } + + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_info ("ISIS-Rte (%s) route already exists: %s", area->area_tag, buff); + + if (isis_route_info_same (rinfo_new, rinfo_old, family)) { + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_info ("ISIS-Rte (%s) route unchanged: %s", area->area_tag, buff); + isis_route_info_delete (rinfo_new); + route_info = rinfo_old; + } else if (isis_route_info_same_attrib (rinfo_new, rinfo_old)) { + /* merge the nexthop lists */ + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_info ("ISIS-Rte (%s) route changed (same attribs): %s", + area->area_tag, buff); +#ifdef EXTREME_DEBUG + zlog_info ("Old nexthops"); + nexthops6_print (rinfo_old->nexthops6); + zlog_info ("New nexthops"); + nexthops6_print (rinfo_new->nexthops6); +#endif /* EXTREME_DEBUG */ + isis_route_info_merge (rinfo_new, rinfo_old, family); + isis_route_info_delete (rinfo_new); + route_info = rinfo_old; + } else { + if (isis_route_info_prefer_new (rinfo_new, rinfo_old)) { + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_info ("ISIS-Rte (%s) route changed: %s", area->area_tag, buff); + isis_route_info_delete (rinfo_old); + route_info = rinfo_new; + } else { + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_info ("ISIS-Rte (%s) route rejected: %s", area->area_tag, buff); + isis_route_info_delete (rinfo_new); + route_info = rinfo_old; + } + } + + SET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ACTIVE); + route_node->info = route_info; + + return route_info; +} + +void +isis_route_delete (struct prefix *prefix, struct route_table *table) +{ + struct route_node *rode; + struct isis_route_info *rinfo; + char buff[BUFSIZ]; + + /* for log */ + prefix2str (prefix, buff, BUFSIZ); + + + rode = route_node_get (table, prefix); + rinfo = rode->info; + + if (rinfo == NULL) { + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_info ("ISIS-Rte: tried to delete non-existant route %s", buff); + return; + } + + if (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC)) { + UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE); + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_info ("ISIS-Rte: route delete %s", buff); + isis_zebra_route_update (prefix, rinfo); + } + isis_route_info_delete (rinfo); + rode->info = NULL; + + return; +} + +int +isis_route_validate (struct thread *thread) +{ + struct isis_area *area; + struct route_table *table; + struct route_node *rode; + struct isis_route_info *rinfo; + u_char buff[BUFSIZ]; +#ifdef HAVE_IPV6 + int v6done = 0; +#endif + area = THREAD_ARG (thread); + table = area->route_table; +#ifdef HAVE_IPV6 + again: +#endif + for (rode = route_top (table); rode; rode = route_next (rode)) { + if (rode->info == NULL) + continue; + rinfo = rode->info; + + if (isis->debugs & DEBUG_RTE_EVENTS) { + prefix2str (&rode->p, buff, BUFSIZ); + zlog_info ("ISIS-Rte (%s): route validate: %s %s %s", + area->area_tag, + (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC) ? + "sync'ed": "nosync"), + (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE) ? + "active": "inactive"), buff); + } + + isis_zebra_route_update (&rode->p, rinfo); + if (!CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE)) + isis_route_delete (&rode->p, area->route_table); + } +#ifdef HAVE_IPV6 + if (v6done) + return ISIS_OK; + table = area->route_table6; + v6done = 1; + goto again; +#endif + + return ISIS_OK; +} |