/* * Copyright (C) 1999 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" char * dtype_name[OSPF6_DEST_TYPE_MAX] = { "Unknown", "Router", "Network", "Discard" }; #define DTYPE_NAME(x) \ (0 < (x) && (x) < sizeof (dtype_name) ? \ dtype_name[(x)] : dtype_name[0]) char * dtype_abname[OSPF6_DEST_TYPE_MAX] = { "?", "R", "N", "D" }; #define DTYPE_ABNAME(x) \ (0 < (x) && (x) < sizeof (dtype_abname) ? \ dtype_abname[(x)] : dtype_abname[0]) char * ptype_name[OSPF6_PATH_TYPE_MAX] = { "Unknown", "Intra", "Inter", "External-1", "External-2", "System", "Kernel", "Connect", "Static", "RIP", "RIPng", "OSPF", "OSPF6", "BGP" }; #define PTYPE_NAME(x) \ (0 < (x) && (x) < sizeof (ptype_name) ? \ ptype_name[(x)] : ptype_name[0]) char * ptype_abname[OSPF6_PATH_TYPE_MAX] = { "??", "Ia", "Ie", "E1", "E2", "-X", "-K", "-C", "-S", "-R", "-R", "-O", "-O", "-B" }; #define PTYPE_ABNAME(x) \ (0 < (x) && (x) < sizeof (ptype_abname) ? \ ptype_abname[(x)] : ptype_abname[0]) int ospf6_path_cmp (void *arg1, void *arg2) { struct ospf6_path_node *pn1 = arg1; struct ospf6_path_node *pn2 = arg2; struct ospf6_path *p1 = &pn1->path; struct ospf6_path *p2 = &pn2->path; if (p1->type < p2->type) return -1; else if (p1->type > p2->type) return 1; if (p1->type == OSPF6_PATH_TYPE_EXTERNAL2) { if (p1->cost_e2 < p2->cost_e2) return -1; else if (p1->cost_e2 > p2->cost_e2) return 1; } if (p1->cost < p2->cost) return -1; else if (p1->cost > p2->cost) return 1; /* if from the same source, recognize as identical (and treat this as update) */ if (! memcmp (&p1->origin, &p2->origin, sizeof (struct ls_origin)) && p1->area_id == p2->area_id) return 0; /* else, always prefer left */ return -1; } int ospf6_nexthop_cmp (void *arg1, void *arg2) { int i, ret = 0; struct ospf6_nexthop_node *nn1 = arg1; struct ospf6_nexthop_node *nn2 = arg2; struct ospf6_nexthop *n1 = &nn1->nexthop; struct ospf6_nexthop *n2 = &nn2->nexthop; if (memcmp (n1, n2, sizeof (struct ospf6_nexthop)) == 0) return 0; for (i = 0; i < sizeof (struct in6_addr); i++) { if (nn1->nexthop.address.s6_addr[i] != nn2->nexthop.address.s6_addr[i]) { ret = nn1->nexthop.address.s6_addr[i] - nn2->nexthop.address.s6_addr[i]; break; } } if (ret == 0) ret = -1; return ret; } static void ospf6_route_request (struct ospf6_route_req *request, struct ospf6_route_node *rn, struct ospf6_path_node *pn, struct ospf6_nexthop_node *nn) { assert (request); assert (rn && pn && nn); request->route_node = rn->route_node; linklist_head (rn->path_list, &request->path_lnode); while (request->path_lnode.data != pn) { //assert (! linklist_end (&request->path_lnode)); if (linklist_end (&request->path_lnode)) { struct linklist_node node; zlog_info ("rn: %p, pn: %p", rn, pn); zlog_info ("origin: %hx %x %x bits: %x opt: %x%x%x popt: %x area: %x type: %d cost %d %d %d", pn->path.origin.type, pn->path.origin.id, pn->path.origin.adv_router, (int)pn->path.router_bits, (int)pn->path.capability[0], (int)pn->path.capability[1], (int)pn->path.capability[2], (int)pn->path.prefix_options, pn->path.area_id, pn->path.type, pn->path.metric_type, pn->path.cost, pn->path.cost_e2); for (linklist_head (rn->path_list, &node); ! linklist_end (&node); linklist_next (&node)) { struct ospf6_path_node *pn2 = node.data; zlog_info (" %p: path data with pn(%p): %s", pn2, pn, (memcmp (&pn->path, &pn2->path, sizeof (struct ospf6_path)) ? "different" : "same")); zlog_info (" origin: %hx %x %x bits: %x opt: %x%x%x popt: %x area: %x type: %d cost %d %d %d", pn2->path.origin.type, pn2->path.origin.id, pn2->path.origin.adv_router, (int)pn2->path.router_bits, (int)pn2->path.capability[0], (int)pn2->path.capability[1], (int)pn2->path.capability[2], (int)pn2->path.prefix_options, pn2->path.area_id, pn2->path.type, pn2->path.metric_type, pn2->path.cost, pn2->path.cost_e2); if (! memcmp (&pn->path, &pn2->path, sizeof (struct ospf6_path))) { pn = pn2; request->nexthop_lnode.data = pn2; } } break; } linklist_next (&request->path_lnode); } assert (request->path_lnode.data == pn); linklist_head (pn->nexthop_list, &request->nexthop_lnode); while (request->nexthop_lnode.data != nn) { assert (! linklist_end (&request->nexthop_lnode)); linklist_next (&request->nexthop_lnode); } assert (request->nexthop_lnode.data == nn); request->table = rn->table; request->count = rn->count; request->route_id = rn->route_id; memcpy (&request->route, &rn->route, sizeof (struct ospf6_route)); memcpy (&request->path, &pn->path, sizeof (struct ospf6_path)); memcpy (&request->nexthop, &nn->nexthop, sizeof (struct ospf6_nexthop)); } int ospf6_route_count (struct ospf6_route_req *request) { return request->count; } int ospf6_route_lookup (struct ospf6_route_req *request, struct prefix *prefix, struct ospf6_route_table *table) { struct route_node *node; struct ospf6_route_node *rn = NULL; struct ospf6_path_node *pn = NULL; struct ospf6_nexthop_node *nn = NULL; struct linklist_node lnode; if (request) memset ((void *) request, 0, sizeof (struct ospf6_route_req)); node = route_node_lookup (table->table, prefix); if (! node) return 0; rn = (struct ospf6_route_node *) node->info; if (! rn) return 0; if (request) { linklist_head (rn->path_list, &lnode); pn = lnode.data; linklist_head (pn->nexthop_list, &lnode); nn = lnode.data; ospf6_route_request (request, rn, pn, nn); } return 1; } void ospf6_route_head (struct ospf6_route_req *request, struct ospf6_route_table *table) { struct route_node *node; struct ospf6_route_node *rn = NULL; struct ospf6_path_node *pn = NULL; struct ospf6_nexthop_node *nn = NULL; struct linklist_node lnode; if (request) memset (request, 0, sizeof (struct ospf6_route_req)); node = route_top (table->table); if (! node) return; while (node && node->info == NULL) node = route_next (node); if (! node) return; rn = (struct ospf6_route_node *) node->info; linklist_head (rn->path_list, &lnode); pn = lnode.data; linklist_head (pn->nexthop_list, &lnode); nn = lnode.data; ospf6_route_request (request, rn, pn, nn); } int ospf6_route_end (struct ospf6_route_req *request) { if (request->route_node == NULL && linklist_end (&request->path_lnode) && linklist_end (&request->nexthop_lnode) && request->nexthop.ifindex == 0 && IN6_IS_ADDR_UNSPECIFIED (&request->nexthop.address)) return 1; return 0; } void ospf6_route_next (struct ospf6_route_req *request) { struct ospf6_route_node *route_node = NULL; struct ospf6_path_node *path_node = NULL; struct ospf6_nexthop_node *nexthop_node = NULL; linklist_next (&request->nexthop_lnode); if (linklist_end (&request->nexthop_lnode)) { linklist_next (&request->path_lnode); if (linklist_end (&request->path_lnode)) { request->route_node = route_next (request->route_node); while (request->route_node && request->route_node->info == NULL) request->route_node = route_next (request->route_node); if (request->route_node) { route_node = request->route_node->info; if (route_node) linklist_head (route_node->path_list, &request->path_lnode); } } path_node = request->path_lnode.data; if (path_node) linklist_head (path_node->nexthop_list, &request->nexthop_lnode); } nexthop_node = request->nexthop_lnode.data; if (nexthop_node == NULL) { assert (path_node == NULL); assert (route_node == NULL); memset (&request->route, 0, sizeof (struct ospf6_route)); memset (&request->path, 0, sizeof (struct ospf6_path)); memset (&request->nexthop, 0, sizeof (struct ospf6_nexthop)); } else { path_node = request->path_lnode.data; route_node = request->route_node->info; assert (path_node != NULL); assert (route_node != NULL); memcpy (&request->route, &route_node->route, sizeof (struct ospf6_route)); memcpy (&request->path, &path_node->path, sizeof (struct ospf6_path)); memcpy (&request->nexthop, &nexthop_node->nexthop, sizeof (struct ospf6_nexthop)); } } #define ADD 0 #define CHANGE 1 #define REMOVE 2 void ospf6_route_hook_call (int type, struct ospf6_route_req *request, struct ospf6_route_table *table) { struct linklist_node node; void (*func) (struct ospf6_route_req *); for (linklist_head (table->hook_list[type], &node); ! linklist_end (&node); linklist_next (&node)) { func = node.data; (*func) (request); } } void ospf6_route_hook_register (void (*add) (struct ospf6_route_req *), void (*change) (struct ospf6_route_req *), void (*remove) (struct ospf6_route_req *), struct ospf6_route_table *table) { linklist_add (add, table->hook_list[ADD]); linklist_add (change, table->hook_list[CHANGE]); linklist_add (remove, table->hook_list[REMOVE]); } void ospf6_route_hook_unregister (void (*add) (struct ospf6_route_req *), void (*change) (struct ospf6_route_req *), void (*remove) (struct ospf6_route_req *), struct ospf6_route_table *table) { linklist_remove (add, table->hook_list[ADD]); linklist_remove (change, table->hook_list[CHANGE]); linklist_remove (remove, table->hook_list[REMOVE]); } int prefix_ls2str (struct prefix *p, char *str, int size) { char id[BUFSIZ], adv_router[BUFSIZ]; struct prefix_ls *pl = (struct prefix_ls *) p; inet_ntop (AF_INET, &pl->id, id, BUFSIZ); inet_ntop (AF_INET, &pl->adv_router, adv_router, BUFSIZ); snprintf (str, size, "%s-%s", adv_router, id); return 0; } void ospf6_route_log_request (char *what, char *where, struct ospf6_route_req *request) { char prefix[64]; char area_id[16]; char type[16], id[16], adv[16]; char address[64], ifname[IFNAMSIZ]; if (request->route.prefix.family != AF_INET && request->route.prefix.family != AF_INET6) prefix_ls2str (&request->route.prefix, prefix, sizeof (prefix)); else prefix2str (&request->route.prefix, prefix, sizeof (prefix)); inet_ntop (AF_INET, &request->path.area_id, area_id, sizeof (area_id)); ospf6_lsa_type_string (request->path.origin.type, type, sizeof (type)); inet_ntop (AF_INET, &request->path.origin.id, id, sizeof (id)); inet_ntop (AF_INET, &request->path.origin.adv_router, adv, sizeof (adv)); inet_ntop (AF_INET6, &request->nexthop.address, address, sizeof (address)); zlog_info ("ROUTE: %s %s %s %s %s", what, DTYPE_ABNAME (request->route.type), prefix, ((strcmp ("Add", what) == 0) ? "to" : "from"), where); zlog_info ("ROUTE: Area: %s type: %s cost: %lu (E2: %lu)", area_id, PTYPE_NAME (request->path.type), (u_long) request->path.cost, (u_long) request->path.cost_e2); zlog_info ("ROUTE: Origin: Type: %s", type); zlog_info ("ROUTE: Origin: Id: %s Adv: %s", id, adv); zlog_info ("ROUTE: Nexthop: %s", address); zlog_info ("ROUTE: Nexthop: Ifindex: %u (%s)", request->nexthop.ifindex, if_indextoname (request->nexthop.ifindex, ifname)); } struct ospf6_path_node * ospf6_route_find_path_node (struct ospf6_route_req *request, struct ospf6_route_node *rn) { struct linklist_node node; for (linklist_head (rn->path_list, &node); ! linklist_end (&node); linklist_next (&node)) { struct ospf6_path_node *path_node = node.data; if (path_node->path.area_id == request->path.area_id && path_node->path.origin.type == request->path.origin.type && path_node->path.origin.id == request->path.origin.id && path_node->path.origin.adv_router == request->path.origin.adv_router) return path_node; } #if 0 zlog_info ("req path : area: %#x origin: type: %d, id: %d, adv_router: %#x", request->path.area_id, request->path.origin.type, request->path.origin.id, request->path.origin.adv_router); for (linklist_head (rn->path_list, &node); ! linklist_end (&node); linklist_next (&node)) { struct ospf6_path_node *path_node = node.data; zlog_info (" path : area: %#x origin: type: %d, id: %d, adv_router: %#x", path_node->path.area_id, path_node->path.origin.type, path_node->path.origin.id, path_node->path.origin.adv_router); } #endif return NULL; } struct ospf6_nexthop_node * ospf6_route_find_nexthop_node (struct ospf6_route_req *request, struct ospf6_path_node *pn) { struct linklist_node node; for (linklist_head (pn->nexthop_list, &node); ! linklist_end (&node); linklist_next (&node)) { struct ospf6_nexthop_node *nexthop_node = node.data; if (! memcmp (&nexthop_node->nexthop, &request->nexthop, sizeof (struct ospf6_nexthop))) return nexthop_node; } return NULL; } void ospf6_route_add (struct ospf6_route_req *request, struct ospf6_route_table *table) { struct ospf6_route_node *rn; struct ospf6_path_node *pn; struct ospf6_nexthop_node *nn; struct route_node *route_node; struct ospf6_route_req route; int route_change = 0; int path_change = 0; int nexthop_change = 0; /* find the requested route */ route_node = route_node_get (table->table, &request->route.prefix); rn = (struct ospf6_route_node *) route_node->info; if (rn) { if (memcmp (&rn->route, &request->route, sizeof (struct ospf6_route))) { memcpy (&rn->route, &request->route, sizeof (struct ospf6_route)); route_change++; } } else { rn = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_route_node)); rn->table = table; rn->route_node = route_node; rn->route_id = table->route_id++; rn->path_list = linklist_create (); rn->path_list->cmp = ospf6_path_cmp; memcpy (&rn->route, &request->route, sizeof (struct ospf6_route)); route_node->info = rn; } /* find the same path */ pn = ospf6_route_find_path_node (request, rn); if (pn) { if (memcmp (&pn->path, &request->path, sizeof (struct ospf6_path))) { memcpy (&pn->path, &request->path, sizeof (struct ospf6_path)); path_change++; } } else { pn = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_path_node)); pn->route_node = rn; pn->nexthop_list = linklist_create (); pn->nexthop_list->cmp = ospf6_nexthop_cmp; memcpy (&pn->path, &request->path, sizeof (struct ospf6_path)); linklist_add (pn, rn->path_list); } /* find the same nexthop */ nn = ospf6_route_find_nexthop_node (request, pn); if (nn) { if (memcmp (&nn->nexthop, &request->nexthop, sizeof (struct ospf6_nexthop))) { memcpy (&nn->nexthop, &request->nexthop, sizeof (struct ospf6_nexthop)); nexthop_change++; gettimeofday (&nn->installed, (struct timezone *) NULL); } } else { nn = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_nexthop_node)); nn->path_node = pn; memcpy (&nn->nexthop, &request->nexthop, sizeof (struct ospf6_nexthop)); linklist_add (nn, pn->nexthop_list); rn->count++; gettimeofday (&nn->installed, (struct timezone *) NULL); } SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ADD); if (route_change) SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ROUTE_CHANGE); if (path_change) SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_PATH_CHANGE); if (nexthop_change) SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE); if (table->freeze) return; if (IS_OSPF6_DUMP_ROUTE) { ospf6_route_log_request ("Add", table->name, request); if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ROUTE_CHANGE)) zlog_info ("ROUTE: route attribute change"); if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_PATH_CHANGE)) zlog_info ("ROUTE: path attribute change"); if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE)) zlog_info ("ROUTE: nexthop attribute change"); } if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ROUTE_CHANGE) || CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_PATH_CHANGE)) SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE); /* Call hooks */ ospf6_route_request (&route, rn, pn, nn); if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ADD)) ospf6_route_hook_call (ADD, &route, table); else if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE)) ospf6_route_hook_call (CHANGE, &route, table); if (table->hook_add && CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ADD)) (*table->hook_add) (&route); else if (table->hook_change && CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE)) (*table->hook_change) (&route); /* clear flag */ nn->flag = 0; } void ospf6_route_remove (struct ospf6_route_req *request, struct ospf6_route_table *table) { struct ospf6_route_node *rn; struct ospf6_path_node *pn; struct ospf6_nexthop_node *nn; struct route_node *route_node; struct ospf6_route_req route; /* find the requested route */ route_node = route_node_get (table->table, &request->route.prefix); rn = (struct ospf6_route_node *) route_node->info; if (! rn) { if (IS_OSPF6_DUMP_ROUTE) { ospf6_route_log_request ("Remove", table->name, request); zlog_info ("ROUTE: Can't remove: No such route"); } return; } pn = ospf6_route_find_path_node (request, rn); if (! pn) { if (IS_OSPF6_DUMP_ROUTE) { ospf6_route_log_request ("Remove", table->name, request); zlog_info ("ROUTE: Can't remove: No such path"); } return; } if (pn->path.area_id != request->path.area_id || pn->path.origin.type != request->path.origin.type || pn->path.origin.id != request->path.origin.id || pn->path.origin.adv_router != request->path.origin.adv_router) { if (IS_OSPF6_DUMP_ROUTE) { ospf6_route_log_request ("Remove", table->name, request); zlog_info ("ROUTE: Can't remove: Path differ"); { char *s, *e, *c; char line[512], *p; p = line; s = (char *) &pn->path; e = s + sizeof (struct ospf6_path); for (c = s; c < e; c++) { if ((c - s) % 4 == 0) snprintf (p++, line + sizeof (line) - p, " "); snprintf (p, line + sizeof (line) - p, "%02x", *c); p += 2; } zlog_info ("ROUTE: path: %s", line); p = line; s = (char *) &request->path; e = s + sizeof (struct ospf6_path); for (c = s; c < e; c++) { if ((c - s) % 4 == 0) snprintf (p++, line + sizeof (line) - p, " "); snprintf (p, line + sizeof (line) - p, "%02x", *c); p += 2; } zlog_info ("ROUTE: req : %s", line); } } return; } nn = ospf6_route_find_nexthop_node (request, pn); if (! nn) { if (IS_OSPF6_DUMP_ROUTE) { ospf6_route_log_request ("Remove", table->name, request); zlog_info ("ROUTE: Can't remove: No such nexthop"); } return; } if (memcmp (&nn->nexthop, &request->nexthop, sizeof (struct ospf6_nexthop))) { if (IS_OSPF6_DUMP_ROUTE) { ospf6_route_log_request ("Remove", table->name, request); zlog_info ("ROUTE: Can't remove: Nexthop differ"); } return; } SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_REMOVE); if (table->freeze) return; if (IS_OSPF6_DUMP_ROUTE) ospf6_route_log_request ("Remove", table->name, request); ospf6_route_request (&route, rn, pn, nn); ospf6_route_hook_call (REMOVE, &route, table); if (table->hook_remove) (*table->hook_remove) (&route); /* clear flag */ nn->flag = 0; /* remove nexthop */ linklist_remove (nn, pn->nexthop_list); rn->count--; XFREE (MTYPE_OSPF6_ROUTE, nn); /* remove path if there's no nexthop for the path */ if (pn->nexthop_list->count != 0) return; linklist_remove (pn, rn->path_list); linklist_delete (pn->nexthop_list); XFREE (MTYPE_OSPF6_ROUTE, pn); /* remove route if there's no path for the route */ if (rn->path_list->count != 0) return; route_node->info = NULL; linklist_delete (rn->path_list); XFREE (MTYPE_OSPF6_ROUTE, rn); } void ospf6_route_remove_all (struct ospf6_route_table *table) { struct ospf6_route_req request; for (ospf6_route_head (&request, table); ! ospf6_route_end (&request); ospf6_route_next (&request)) ospf6_route_remove (&request, table); } struct ospf6_route_table * ospf6_route_table_create (char *name) { int i; struct ospf6_route_table *new; new = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_route_table)); snprintf (new->name, sizeof (new->name), "%s", name); new->table = route_table_init (); for (i = 0; i < 3; i++) new->hook_list[i] = linklist_create (); return new; } void ospf6_route_table_delete (struct ospf6_route_table *table) { int i; ospf6_route_remove_all (table); route_table_finish (table->table); for (i = 0; i < 3; i++) linklist_delete (table->hook_list[i]); XFREE (MTYPE_OSPF6_ROUTE, table); } void ospf6_route_table_freeze (struct ospf6_route_table *route_table) { if (IS_OSPF6_DUMP_ROUTE) zlog_info ("ROUTE: Table freeze: %s", route_table->name); assert (route_table->freeze == 0); route_table->freeze = 1; } void ospf6_route_table_thaw (struct ospf6_route_table *route_table) { struct route_node *node; struct linklist_node pnode; struct linklist_node nnode; struct ospf6_route_node *rn; struct ospf6_path_node *pn; struct ospf6_nexthop_node *nn; struct ospf6_route_req request; if (IS_OSPF6_DUMP_ROUTE) zlog_info ("ROUTE: Table thaw: %s", route_table->name); assert (route_table->freeze == 1); route_table->freeze = 0; for (node = route_top (route_table->table); node; node = route_next (node)) { rn = node->info; if (! rn) continue; for (linklist_head (rn->path_list, &pnode); ! linklist_end (&pnode); linklist_next (&pnode)) { pn = pnode.data; for (linklist_head (pn->nexthop_list, &nnode); ! linklist_end (&nnode); linklist_next (&nnode)) { nn = nnode.data; /* if the add and remove flag set without change flag, do nothing with this route */ if (! CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE) && CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ADD) && CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_REMOVE)) { nn->flag = 0; continue; } memset (&request, 0, sizeof (request)); memcpy (&request.route, &rn->route, sizeof (rn->route)); memcpy (&request.path, &pn->path, sizeof (pn->path)); memcpy (&request.nexthop, &nn->nexthop, sizeof (nn->nexthop)); if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ADD) || CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE)) ospf6_route_add (&request, route_table); else if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_REMOVE)) ospf6_route_remove (&request, route_table); } } } } /* VTY commands */ void ospf6_route_show (struct vty *vty, struct ospf6_route_node *rn) { struct linklist_node pnode; struct linklist_node nnode; struct ospf6_path_node *pn; struct ospf6_nexthop_node *nn; struct timeval now, res; char duration[16]; u_int pc = 0; u_int nc = 0; #define HEAD (pc == 0 && nc == 0) char prefix[64], nexthop[64], ifname[IFNAMSIZ]; gettimeofday (&now, (struct timezone *) NULL); /* destination */ if (rn->route.prefix.family == AF_INET || rn->route.prefix.family == AF_INET6) prefix2str (&rn->route.prefix, prefix, sizeof (prefix)); else prefix_ls2str (&rn->route.prefix, prefix, sizeof (prefix)); for (linklist_head (rn->path_list, &pnode); ! linklist_end (&pnode); linklist_next (&pnode)) { pn = pnode.data; for (linklist_head (pn->nexthop_list, &nnode); ! linklist_end (&nnode); linklist_next (&nnode)) { nn = nnode.data; inet_ntop (AF_INET6, &nn->nexthop.address, nexthop, sizeof (nexthop)); if (! if_indextoname (nn->nexthop.ifindex, ifname)) snprintf (ifname, sizeof (ifname), "%d", nn->nexthop.ifindex); ospf6_timeval_sub (&now, &nn->installed, &res); ospf6_timeval_string_summary (&res, duration, sizeof (duration)); vty_out (vty, "%c%1s %2s %-30s %-25s %6s %s%s", (HEAD ? '*' : ' '), DTYPE_ABNAME (rn->route.type), PTYPE_ABNAME (pn->path.type), prefix, nexthop, ifname, duration, VTY_NEWLINE); nc++; } pc++; } } void ospf6_route_show_detail (struct vty *vty, struct ospf6_route_node *rn) { struct linklist_node pnode; struct linklist_node nnode; struct ospf6_path_node *pn; struct ospf6_nexthop_node *nn; u_int pc = 0; u_int nc = 0; char prefix[64], nexthop[64], ifname[IFNAMSIZ]; char area_id[16], type[16], id[16], adv[16]; char capa[64]; /* destination */ if (rn->route.prefix.family == AF_INET || rn->route.prefix.family == AF_INET6) prefix2str (&rn->route.prefix, prefix, sizeof (prefix)); else prefix_ls2str (&rn->route.prefix, prefix, sizeof (prefix)); vty_out (vty, "%s%s%s", VTY_NEWLINE, prefix, VTY_NEWLINE); vty_out (vty, " Destination Type: %s%s", DTYPE_NAME (rn->route.type), VTY_NEWLINE); for (linklist_head (rn->path_list, &pnode); ! linklist_end (&pnode); linklist_next (&pnode)) { pn = pnode.data; inet_ntop (AF_INET, &pn->path.area_id, area_id, sizeof (area_id)); ospf6_lsa_type_string (pn->path.origin.type, type, sizeof (type)); inet_ntop (AF_INET, &pn->path.origin.id, id, sizeof (id)); inet_ntop (AF_INET, &pn->path.origin.adv_router, adv, sizeof (adv)); ospf6_options_string (pn->path.capability, capa, sizeof (capa)); vty_out (vty, " Path:%s", VTY_NEWLINE); vty_out (vty, " Associated Area: %s%s", area_id, VTY_NEWLINE); vty_out (vty, " LS Origin: %s ID: %s Adv: %s%s", type, id, adv, VTY_NEWLINE); vty_out (vty, " Path Type: %s%s", PTYPE_NAME (pn->path.type), VTY_NEWLINE); vty_out (vty, " Metric Type: %d%s", pn->path.metric_type, VTY_NEWLINE); vty_out (vty, " Cost: Type-1: %lu Type-2: %lu%s", (u_long) pn->path.cost, (u_long) pn->path.cost_e2, VTY_NEWLINE); vty_out (vty, " Router Bits: %s|%s|%s|%s%s", (CHECK_FLAG (pn->path.router_bits, OSPF6_ROUTER_LSA_BIT_W) ? "W" : "-"), (CHECK_FLAG (pn->path.router_bits, OSPF6_ROUTER_LSA_BIT_V) ? "V" : "-"), (CHECK_FLAG (pn->path.router_bits, OSPF6_ROUTER_LSA_BIT_E) ? "E" : "-"), (CHECK_FLAG (pn->path.router_bits, OSPF6_ROUTER_LSA_BIT_B) ? "B" : "-"), VTY_NEWLINE); vty_out (vty, " Optional Capabilities: %s%s", capa, VTY_NEWLINE); vty_out (vty, " Prefix Options: %s%s", "xxx", VTY_NEWLINE); vty_out (vty, " Next Hops:%s", VTY_NEWLINE); for (linklist_head (pn->nexthop_list, &nnode); ! linklist_end (&nnode); linklist_next (&nnode)) { nn = nnode.data; inet_ntop (AF_INET6, &nn->nexthop.address, nexthop, sizeof (nexthop)); if (! if_indextoname (nn->nexthop.ifindex, ifname)) snprintf (ifname, sizeof (ifname), "%d", nn->nexthop.ifindex); vty_out (vty, " %c%s%%%s%s", (HEAD ? '*' : ' '), nexthop, ifname, VTY_NEWLINE); nc++; } pc++; } vty_out (vty, "%s", VTY_NEWLINE); } int ospf6_route_table_show (struct vty *vty, int argc, char **argv, struct ospf6_route_table *table) { int i, ret; unsigned long ret_ul; char *endptr; struct prefix prefix; int detail = 0; int arg_ipv6 = 0; int arg_ipv4 = 0; int arg_digit = 0; struct prefix_ipv6 *p6 = (struct prefix_ipv6 *) &prefix; struct prefix_ls *pl = (struct prefix_ls *) &prefix; struct route_node *node; u_int route_count = 0; u_int path_count = 0; u_int route_redundant = 0; memset (&prefix, 0, sizeof (struct prefix)); for (i = 0; i < argc; i++) { if (! strcmp (argv[i], "detail")) { detail++; break; } if (! arg_ipv6 && ! arg_ipv4 && ! arg_digit) { if ((ret = inet_pton (AF_INET6, argv[i], &p6->prefix)) == 1) { p6->family = AF_INET6; p6->prefixlen = 128; arg_ipv6++; continue; } else if ((ret = inet_pton (AF_INET, argv[i], &pl->adv_router)) == 1) { pl->family = AF_UNSPEC; pl->prefixlen = 64; /* xxx */ arg_ipv4++; continue; } else { ret_ul = strtoul (argv[i], &endptr, 10); if (*endptr == '\0') { pl->adv_router.s_addr = htonl (ret_ul); pl->family = AF_UNSPEC; pl->prefixlen = 64; /* xxx */ arg_digit++; continue; } else { vty_out (vty, "Malformed argument: %s%s", argv[i], VTY_NEWLINE); return CMD_SUCCESS; } } } if (arg_ipv4 || arg_digit) { if ((ret = inet_pton (AF_INET, argv[i], &pl->id)) == 1) { arg_ipv4++; } else { ret_ul = strtoul (argv[i], &endptr, 10); if (*endptr == '\0') { pl->id.s_addr = htonl (ret_ul); arg_digit++; } else { vty_out (vty, "Malformed argument: %s%s", argv[i], VTY_NEWLINE); return CMD_SUCCESS; } } } } if (arg_ipv4 || arg_ipv6 || arg_digit) { node = route_node_match (table->table, &prefix); if (node && node->info) ospf6_route_show_detail (vty, node->info); return CMD_SUCCESS; } if (! detail) { vty_out (vty, "%s%c%1s %2s %-30s %-25s %6s%s", VTY_NEWLINE, ' ', " ", " ", "Destination", "Gateway", "I/F", VTY_NEWLINE); vty_out (vty, "---------------------------%s", VTY_NEWLINE); } for (node = route_top (table->table); node; node = route_next (node)) { struct ospf6_route_node *route = node->info; if (! route) continue; if (detail) ospf6_route_show_detail (vty, route); else ospf6_route_show (vty, route); route_count++; path_count += route->path_list->count; if (route->path_list->count > 1) route_redundant++; } vty_out (vty, "===========%s", VTY_NEWLINE); vty_out (vty, "Route: %d Path: %d Redundant: %d%s", route_count, path_count, route_redundant, VTY_NEWLINE); return CMD_SUCCESS; }