diff options
Diffstat (limited to 'ospf6d/ospf6_abr.c')
-rw-r--r-- | ospf6d/ospf6_abr.c | 655 |
1 files changed, 655 insertions, 0 deletions
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c new file mode 100644 index 000000000..864e0c23a --- /dev/null +++ b/ospf6d/ospf6_abr.c @@ -0,0 +1,655 @@ +/* + * Copyright (C) 2001 Yasuhiro Ohara + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "ospf6d.h" + +#include "ospf6_dump.h" +#include "ospf6_abr.h" + +static int abr_index; +#define IS_OSPF6_DUMP_ABR (ospf6_dump_is_on (abr_index)) + +#define ADD 0 +#define CHANGE 1 +#define REMOVE 2 + +/* Inter-Area-Prefix-LSA Calculation */ + +static struct ospf6_route_req * +ospf6_abr_entry_lookup (struct ospf6_route_req *abr_entry, + u_int32_t router_id, struct ospf6_area *area) +{ + struct prefix_ls abr_id; + char router_string[32]; + + inet_ntop (AF_INET, &router_id, router_string, sizeof (router_string)); + + //zlog_info ("ABR: Finding router %s in area %s", router_string, area->str); + + memset (&abr_id, 0, sizeof (abr_id)); + abr_id.family = AF_UNSPEC; + abr_id.prefixlen = 64; /* xxx */ + abr_id.id.s_addr = htonl (0); + abr_id.adv_router.s_addr = router_id; + + ospf6_route_lookup (abr_entry, (struct prefix *) &abr_id, + area->table_topology); + + if (ospf6_route_end (abr_entry)) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Router %s not found in area %s", + router_string, area->str); + return NULL; + } + + if (abr_entry->path.area_id != area->area_id) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: ABR area id mismatch"); + return NULL; + } + + if (! CHECK_FLAG (abr_entry->path.router_bits, OSPF6_ROUTER_LSA_BIT_B)) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: ABR entry's B bit off"); + return NULL; + } + + return abr_entry; +} + +static int +ospf6_abr_prefix_lsa_to_route (struct ospf6_lsa *lsa, + struct ospf6_route_req *request) +{ + struct ospf6_inter_area_prefix_lsa *iep; + struct ospf6_route_req abr_entry; + + if (lsa->header->type != htons (OSPF6_LSA_TYPE_INTER_PREFIX)) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: LSA type mismatch"); + return -1; + } + + if (IS_LSA_MAXAGE (lsa)) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: LSA MaxAge"); + return -1; + } + + if (! ospf6_abr_entry_lookup (&abr_entry, lsa->header->adv_router, + (struct ospf6_area *) lsa->scope)) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: ABR check failed"); + return -1; + } + + iep = OSPF6_LSA_HEADER_END (lsa->header); + + memset (request, 0, sizeof (struct ospf6_route_req)); + request->route.type = OSPF6_DEST_TYPE_NETWORK; + request->route.prefix.family = AF_INET6; + request->route.prefix.prefixlen = iep->prefix.prefix_length; + ospf6_prefix_in6_addr (&iep->prefix, &request->route.prefix.u.prefix6); + + request->path.cost = abr_entry.path.cost + + (ntohl (iep->metric) & ntohl (0x000fffff)); + request->path.type = OSPF6_PATH_TYPE_INTER; + request->path.origin.type = lsa->header->type; + request->path.origin.id = lsa->header->id; + request->path.origin.adv_router = lsa->header->adv_router; + memcpy (&request->nexthop.address, &abr_entry.nexthop.address, + sizeof (request->nexthop.address)); + request->nexthop.ifindex = abr_entry.nexthop.ifindex; + + return 0; +} + +void +ospf6_abr_prefix_lsa_add (struct ospf6_lsa *lsa) +{ + struct ospf6_route_req request; + int ret; + + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Calculate %s", lsa->str); + + ret = ospf6_abr_prefix_lsa_to_route (lsa, &request); + if (ret < 0) + return; + + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Inter Area Route add for %s", lsa->str); + + ospf6_route_add (&request, ospf6->route_table); +} + +void +ospf6_abr_prefix_lsa_remove (struct ospf6_lsa *lsa) +{ + struct ospf6_inter_area_prefix_lsa *iep; + struct prefix_ipv6 prefix6; + struct ospf6_route_req request; + + iep = OSPF6_LSA_HEADER_END (lsa->header); + + prefix6.family = AF_INET6; + prefix6.prefixlen = iep->prefix.prefix_length; + ospf6_prefix_in6_addr (&iep->prefix, &prefix6.prefix); + + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Inter Area Route remove for %s", lsa->str); + + for (ospf6_route_lookup (&request, (struct prefix *) &prefix6, + ospf6->route_table); + ! ospf6_route_end (&request); + ospf6_route_next (&request)) + { + if (memcmp (&prefix6, &request.route.prefix, sizeof (prefix6))) + break; + if (request.path.origin.type != htons (OSPF6_LSA_TYPE_INTER_PREFIX) || + request.path.origin.adv_router != lsa->header->adv_router || + request.path.origin.id != lsa->header->id) + continue; + + ospf6_route_remove (&request, ospf6->route_table); + } +} + +static int +ospf6_abr_router_lsa_to_route (struct ospf6_lsa *lsa, + struct ospf6_route_req *request) +{ + struct ospf6_inter_area_router_lsa *ier; + struct ospf6_route_req abr_entry; + + if (lsa->header->type != htons (OSPF6_LSA_TYPE_INTER_ROUTER)) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: LSA type mismatch"); + return -1; + } + + if (IS_LSA_MAXAGE (lsa)) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: LSA MaxAge"); + return -1; + } + + if (! ospf6_abr_entry_lookup (&abr_entry, lsa->header->adv_router, + (struct ospf6_area *) lsa->scope)) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Advertising router check failed"); + return -1; + } + + ier = OSPF6_LSA_HEADER_END (lsa->header); + + memset (request, 0, sizeof (struct ospf6_route_req)); + request->route.type = OSPF6_DEST_TYPE_ROUTER; + request->route.prefix.family = AF_UNSPEC; + request->route.prefix.prefixlen = 64; /* XXX */ + ((struct prefix_ls *) &request->route.prefix)->adv_router.s_addr + = ier->router_id; + + request->path.cost = abr_entry.path.cost + + (ntohl (ier->metric & htonl (0x000fffff))); + request->path.type = OSPF6_PATH_TYPE_INTER; + request->path.origin.type = lsa->header->type; + request->path.origin.id = lsa->header->id; + request->path.origin.adv_router = lsa->header->adv_router; + SET_FLAG (request->path.router_bits, OSPF6_ROUTER_LSA_BIT_E); + request->path.capability[0] = ier->options[0]; + request->path.capability[1] = ier->options[1]; + request->path.capability[2] = ier->options[2]; + + memcpy (&request->nexthop.address, &abr_entry.nexthop.address, + sizeof (request->nexthop.address)); + request->nexthop.ifindex = abr_entry.nexthop.ifindex; + + return 0; +} + +void +ospf6_abr_router_lsa_add (struct ospf6_lsa *lsa) +{ + struct ospf6_route_req request; + int ret; + + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Calculate %s", lsa->str); + + ret = ospf6_abr_router_lsa_to_route (lsa, &request); + if (ret < 0) + return; + + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Inter Area Router add for %s", lsa->str); + + ospf6_route_add (&request, ospf6->topology_table); +} + +void +ospf6_abr_router_lsa_remove (struct ospf6_lsa *lsa) +{ + struct ospf6_inter_area_router_lsa *ier; + struct prefix_ls prefix_ls; + struct ospf6_route_req request; + + ier = OSPF6_LSA_HEADER_END (lsa->header); + + memset (&prefix_ls, 0, sizeof (prefix_ls)); + prefix_ls.family = AF_INET6; + prefix_ls.prefixlen = 64; /* XXX */ + prefix_ls.adv_router.s_addr = ier->router_id; + + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Inter Area Route remove for %s", lsa->str); + + for (ospf6_route_lookup (&request, (struct prefix *) &prefix_ls, + ospf6->route_table); + ! ospf6_route_end (&request); + ospf6_route_next (&request)) + { + if (memcmp (&prefix_ls, &request.route.prefix, sizeof (prefix_ls))) + break; + if (request.path.origin.type != htons (OSPF6_LSA_TYPE_INTER_ROUTER) || + request.path.origin.adv_router != lsa->header->adv_router || + request.path.origin.id != lsa->header->id) + continue; + + ospf6_route_remove (&request, ospf6->route_table); + } +} + + +void +ospf6_abr_abr_entry_add (struct ospf6_route_req *abr_entry) +{ + struct ospf6_lsdb_node node; + struct prefix_ls *abr_id; + struct ospf6_route_req request; + struct ospf6_area *area; + + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: New Area Border Router found"); + + area = ospf6_area_lookup (abr_entry->path.area_id, ospf6); + if (! area) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Can't find associated area"); + return; + } + + abr_id = (struct prefix_ls *) &abr_entry->route.prefix; + if (! ospf6_abr_entry_lookup (&request, abr_id->adv_router.s_addr, area)) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: back check failed"); + return; + } + + /* for each inter-prefix LSA this ABR originated */ + for (ospf6_lsdb_type_router (&node, htons (OSPF6_LSA_TYPE_INTER_PREFIX), + abr_id->adv_router.s_addr, area->lsdb); + ! ospf6_lsdb_is_end (&node); + ospf6_lsdb_next (&node)) + ospf6_abr_prefix_lsa_add (node.lsa); + + /* for each inter-router LSA this ABR originated */ + for (ospf6_lsdb_type_router (&node, htons (OSPF6_LSA_TYPE_INTER_ROUTER), + abr_id->adv_router.s_addr, area->lsdb); + ! ospf6_lsdb_is_end (&node); + ospf6_lsdb_next (&node)) + ospf6_abr_router_lsa_add (node.lsa); +} + +void +ospf6_abr_abr_entry_remove (struct ospf6_route_req *abr_entry) +{ + struct ospf6_lsdb_node node; + struct prefix_ls *abr_id; + struct ospf6_area *area; + + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Area Border Router removed"); + + abr_id = (struct prefix_ls *) &abr_entry->route.prefix; + + area = ospf6_area_lookup (abr_entry->path.area_id, ospf6); + if (! area) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Can't find associated area"); + return; + } + + /* for each inter-prefix LSA this ABR originated */ + for (ospf6_lsdb_type_router (&node, htons (OSPF6_LSA_TYPE_INTER_PREFIX), + abr_id->adv_router.s_addr, area->lsdb); + ! ospf6_lsdb_is_end (&node); + ospf6_lsdb_next (&node)) + ospf6_abr_prefix_lsa_remove (node.lsa); + + /* for each inter-router LSA this ABR originated */ + for (ospf6_lsdb_type_router (&node, htons (OSPF6_LSA_TYPE_INTER_ROUTER), + abr_id->adv_router.s_addr, area->lsdb); + ! ospf6_lsdb_is_end (&node); + ospf6_lsdb_next (&node)) + ospf6_abr_router_lsa_remove (node.lsa); +} + +/* Inter-Area-Prefix-LSA Origination */ + +static void +ospf6_abr_prefix_lsa_update_add (struct ospf6_route_req *request, + struct ospf6_area *area) +{ + char buffer [MAXLSASIZE]; + u_int16_t size; + struct ospf6_inter_area_prefix_lsa *iep; + char *p; + + if (IS_OSPF6_DUMP_ABR) + zlog_info ("Update Inter-Prefix for %s: ID: %lu", + area->str, (u_long) ntohl (request->route_id)); + + /* prepare buffer */ + memset (buffer, 0, sizeof (buffer)); + size = sizeof (struct ospf6_inter_area_prefix_lsa); + iep = (struct ospf6_inter_area_prefix_lsa *) buffer; + p = (char *) (iep + 1); + + /* prefixlen */ + iep->prefix.prefix_length = request->route.prefix.prefixlen; + + /* PrefixOptions */ + iep->prefix.prefix_options = request->path.prefix_options; + + /* set Prefix */ + memcpy (p, &request->route.prefix.u.prefix6, + OSPF6_PREFIX_SPACE (request->route.prefix.prefixlen)); + ospf6_prefix_apply_mask (&iep->prefix); + size += OSPF6_PREFIX_SPACE (request->route.prefix.prefixlen); + + ospf6_lsa_originate (htons (OSPF6_LSA_TYPE_INTER_PREFIX), + htonl (request->route_id), ospf6->router_id, + (char *) iep, size, area); +} + +static void +ospf6_abr_prefix_lsa_update_remove (struct ospf6_route_req *request, + struct ospf6_area *area) +{ + struct ospf6_lsa *lsa; + lsa = ospf6_lsdb_lookup_lsdb (htons (OSPF6_LSA_TYPE_INTER_PREFIX), + htonl (request->route_id), + ospf6->router_id, area->lsdb); + if (lsa) + ospf6_lsa_premature_aging (lsa); +} + +static void +ospf6_abr_prefix_lsa_update (int type, struct ospf6_route_req *request) +{ + struct ospf6_route_req route, target; + listnode node; + struct ospf6_area *area; + struct ospf6_interface *o6i; + + if (request->route.type != OSPF6_DEST_TYPE_NETWORK) + return; + + /* assert this is best path; if not, return */ + ospf6_route_lookup (&route, &request->route.prefix, request->table); + if (memcmp (&route.path, &request->path, sizeof (route.path))) + return; + + if (target.path.cost >= LS_INFINITY || + target.path.cost_e2 >= LS_INFINITY) + { + if (IS_OSPF6_DUMP_ABR) + zlog_info ("ABR: Exceeds LS Infinity, ignore"); + return; + } + + ospf6_route_lookup (&target, &request->route.prefix, request->table); + if (type == REMOVE) + { + ospf6_route_next (&route); + if (! memcmp (&route.route, &request->route, sizeof (route.route))) + { + type = ADD; + ospf6_route_next (&target); + } + } + + for (node = listhead (ospf6->area_list); node; nextnode (node)) + { + area = getdata (node); + + if (target.path.area_id == area->area_id) + continue; + + o6i = ospf6_interface_lookup_by_index (target.nexthop.ifindex); + if (o6i && o6i->area && o6i->area->area_id == area->area_id) + { + zlog_info ("ABR: Logical equivalent of split horizon, skip for %s", + area->str); + continue; + } + + if (area->area_id == ntohs (0) && /* Backbone */ + target.path.type != OSPF6_PATH_TYPE_INTRA) + continue; + + /* XXX, stub area check */ + + /* XXX, aggregate */ + /* if either the area of the route or the area trying to + advertise is backbone, do not aggregate */ + + if (type == ADD) + ospf6_abr_prefix_lsa_update_add (&target, area); + else + ospf6_abr_prefix_lsa_update_remove (&target, area); + } +} + +void +ospf6_abr_route_add (struct ospf6_route_req *request) +{ + ospf6_abr_prefix_lsa_update (ADD, request); +} + +void +ospf6_abr_route_remove (struct ospf6_route_req *request) +{ + ospf6_abr_prefix_lsa_update (REMOVE, request); +} + +int +ospf6_abr_prefix_lsa_refresh (void *data) +{ + struct ospf6_lsa *lsa = data; + struct ospf6_inter_area_prefix_lsa *ier; + struct prefix_ipv6 prefix6; + struct ospf6_route_req route; + + ier = OSPF6_LSA_HEADER_END (lsa->header); + memset (&prefix6, 0, sizeof (prefix6)); + prefix6.family = AF_INET6; + prefix6.prefixlen = ier->prefix.prefix_length; + ospf6_prefix_in6_addr (&ier->prefix, &prefix6.prefix); + + ospf6_route_lookup (&route, (struct prefix *) &prefix6, + ospf6->route_table); + assert (! ospf6_route_end (&route)); + + ospf6_abr_prefix_lsa_update (ADD, &route); + return 0; +} + +int +ospf6_abr_prefix_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) +{ + struct ospf6_inter_area_prefix_lsa *ier; + char prefix[128]; + + assert (lsa->header); + ier = OSPF6_LSA_HEADER_END (lsa->header); + + ospf6_prefix_string (&ier->prefix, prefix, sizeof (prefix)); + + vty_out (vty, " Metric: %d%s", + ntohl (ier->metric & htonl (0x000fffff)), VTY_NEWLINE); + vty_out (vty, " Prefix: %s%s", prefix, VTY_NEWLINE); + + return 0; +} + +int +ospf6_abr_prefix_lsa_hook_add (void *data) +{ + struct ospf6_lsa *lsa = data; + ospf6_abr_prefix_lsa_add (lsa); + return 0; +} + +int +ospf6_abr_prefix_lsa_hook_remove (void *data) +{ + struct ospf6_lsa *lsa = data; + ospf6_abr_prefix_lsa_remove (lsa); + return 0; +} + +void +ospf6_abr_database_hook_inter_prefix (struct ospf6_lsa *old, + struct ospf6_lsa *new) +{ + if (old) + ospf6_abr_prefix_lsa_hook_remove (old); + if (new && ! IS_LSA_MAXAGE (new)) + ospf6_abr_prefix_lsa_hook_add (new); +} + +void +ospf6_abr_register_inter_prefix () +{ + struct ospf6_lsa_slot slot; + + memset (&slot, 0, sizeof (slot)); + slot.type = htons (OSPF6_LSA_TYPE_INTER_PREFIX); + slot.name = "Inter-Prefix"; + slot.func_show = ospf6_abr_prefix_lsa_show; + slot.func_refresh = ospf6_abr_prefix_lsa_refresh; + ospf6_lsa_slot_register (&slot); + + ospf6_lsdb_hook[OSPF6_LSA_TYPE_INTER_PREFIX & OSPF6_LSTYPE_CODE_MASK].hook = + ospf6_abr_database_hook_inter_prefix; +} + +int +ospf6_abr_router_lsa_hook_add (void *data) +{ + struct ospf6_lsa *lsa = data; + ospf6_abr_router_lsa_add (lsa); + return 0; +} + +int +ospf6_abr_router_lsa_hook_remove (void *data) +{ + struct ospf6_lsa *lsa = data; + ospf6_abr_router_lsa_remove (lsa); + return 0; +} + +int +ospf6_abr_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) +{ + return 0; +} + +int +ospf6_abr_router_lsa_refresh (void *data) +{ + return 0; +} + +void +ospf6_abr_database_hook_inter_router (struct ospf6_lsa *old, + struct ospf6_lsa *new) +{ + if (old) + ospf6_abr_router_lsa_hook_remove (old); + if (new && ! IS_LSA_MAXAGE (new)) + ospf6_abr_router_lsa_hook_add (new); +} + +void +ospf6_abr_register_inter_router () +{ + struct ospf6_lsa_slot slot; + + memset (&slot, 0, sizeof (slot)); + slot.type = htons (OSPF6_LSA_TYPE_INTER_ROUTER); + slot.name = "Inter-Router"; + slot.func_show = ospf6_abr_router_lsa_show; + slot.func_refresh = ospf6_abr_router_lsa_refresh; + ospf6_lsa_slot_register (&slot); + + ospf6_lsdb_hook[OSPF6_LSA_TYPE_INTER_ROUTER & OSPF6_LSTYPE_CODE_MASK].hook = + ospf6_abr_database_hook_inter_router; +} + +void +ospf6_abr_inter_route_calculation (struct ospf6_area *area) +{ + struct ospf6_lsdb_node node; + + /* for each inter-prefix LSA */ + for (ospf6_lsdb_type (&node, htons (OSPF6_LSA_TYPE_INTER_PREFIX), + area->lsdb); + ! ospf6_lsdb_is_end (&node); + ospf6_lsdb_next (&node)) + ospf6_abr_prefix_lsa_add (node.lsa); +} + +void +ospf6_abr_init () +{ + abr_index = ospf6_dump_install ("abr", "Area Border Router Function\n"); + + ospf6_abr_register_inter_prefix (); + ospf6_abr_register_inter_router (); +} + + |