/* * 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" #include "ospf6_damp.h" /* global ospf6d variable */ int ospf6_sock; list iflist; list nexthoplist = NULL; struct sockaddr_in6 allspfrouters6; struct sockaddr_in6 alldrouters6; char *recent_reason; /* set by ospf6_lsa_check_recent () */ int proctitle_mode = 0; char ospf6_daemon_version[] = OSPF6_DAEMON_VERSION; #define TIMER_SEC_MICRO 1000000 void ospf6_timeval_sub (const struct timeval *t1, const struct timeval *t2, struct timeval *result) { long usec, movedown = 0; if (t1->tv_sec < t2->tv_sec || (t1->tv_sec == t2->tv_sec && t1->tv_usec < t2->tv_usec)) { result->tv_sec = 0; result->tv_usec = 0; return; } if (t1->tv_usec < t2->tv_usec) { usec = t1->tv_usec + TIMER_SEC_MICRO; movedown++; } else usec = t1->tv_usec; result->tv_usec = usec - t2->tv_usec; result->tv_sec = t1->tv_sec - t2->tv_sec - movedown; } void ospf6_timeval_div (const struct timeval *t1, u_int by, struct timeval *result) { long movedown; if (by == 0) { result->tv_sec = 0; result->tv_usec = 0; return; } movedown = t1->tv_sec % by; result->tv_sec = t1->tv_sec / by; result->tv_usec = (t1->tv_usec + movedown * TIMER_SEC_MICRO) / by; } void ospf6_timeval_decode (const struct timeval *t, long *dayp, long *hourp, long *minp, long *secp, long *msecp, long *usecp) { long day, hour, min, sec, msec, usec, left; left = t->tv_sec; day = left / 86400; left -= day * 86400; hour = left / 3600; left -= hour * 3600; min = left / 60; left -= min * 60; sec = left; left = t->tv_usec; msec = left / 1000; left -= msec * 1000; usec = left; if (dayp) *dayp = day; if (hourp) *hourp = hour; if (minp) *minp = min; if (secp) *secp = sec; if (msecp) *msecp = msec; if (usecp) *usecp = usec; } void ospf6_timeval_string (struct timeval *tv, char *buf, int size) { char days[16], hours[16], mins[16], secs[16], msecs[16], usecs[16]; long day, hour, min, sec, msec, usec; ospf6_timeval_decode (tv, &day, &hour, &min, &sec, &msec, &usec); snprintf (days, sizeof (days), "%ld days ", day); snprintf (hours, sizeof (hours), "%ld hours ", hour); snprintf (mins, sizeof (mins), "%ld mins ", min); snprintf (secs, sizeof (secs), "%ld secs ", sec); snprintf (msecs, sizeof (msecs), "%ld msecs ", msec); snprintf (usecs, sizeof (usecs), "%ld usecs ", usec); snprintf (buf, size, "%s%s%s%s%s%s", (day ? days : ""), (hour ? hours : ""), (min ? mins : ""), (sec ? secs : ""), (msec ? msecs : ""), (usec ? usecs : "")); } void ospf6_timeval_string_summary (struct timeval *tv, char *buf, int size) { char days[16], hours[16], mins[16], secs[16], msecs[16], usecs[16]; long day, hour, min, sec, msec, usec; ospf6_timeval_decode (tv, &day, &hour, &min, &sec, &msec, &usec); snprintf (days, sizeof (days), "%02ldd", day); snprintf (hours, sizeof (hours), "%ldh", hour); snprintf (mins, sizeof (mins), "%ldm", min); snprintf (secs, sizeof (secs), "%lds", sec); snprintf (msecs, sizeof (msecs), "%ldms", msec); snprintf (usecs, sizeof (usecs), "%ldus", usec); snprintf (buf, size, "%s%02ld:%02ld:%02ld", (day ? days : ""), hour, min, sec); } /* foreach function */ void ospf6_count_state (void *arg, int val, void *obj) { int *count = (int *) arg; u_char state = val; struct ospf6_neighbor *nei = (struct ospf6_neighbor *) obj; if (nei->state == state) (*count)++; } /* VTY commands. */ DEFUN (reload, reload_cmd, "reload", "Reloads\n") { extern void _reload (); _reload (); return CMD_SUCCESS; } DEFUN (garbage_collection, garbage_collection_cmd, "ipv6 ospf6 garbage collect", IPV6_STR OSPF6_STR "garbage collection by hand\n" "Remove Maxages if possible and recalculate routes\n") { ospf6_maxage_remover (); #if 0 ospf6_route_calculation_schedule (); #endif return CMD_SUCCESS; } /* Show version. */ DEFUN (show_version_ospf6, show_version_ospf6_cmd, "show version ospf6", SHOW_STR "Displays ospf6d version\n") { vty_out (vty, "Zebra OSPF6d Version: %s%s", ospf6_daemon_version, VTY_NEWLINE); return CMD_SUCCESS; } /* start ospf6 */ DEFUN (router_ospf6, router_ospf6_cmd, "router ospf6", OSPF6_ROUTER_STR OSPF6_STR) { if (ospf6 == NULL) ospf6_start (); /* set current ospf point. */ vty->node = OSPF6_NODE; vty->index = ospf6; return CMD_SUCCESS; } /* stop ospf6 */ DEFUN (no_router_ospf6, no_router_ospf6_cmd, "no router ospf6", NO_STR OSPF6_ROUTER_STR) { if (!ospf6) vty_out (vty, "OSPFv3 is not running%s", VTY_NEWLINE); else ospf6_stop (); /* return to config node . */ vty->node = CONFIG_NODE; vty->index = NULL; return CMD_SUCCESS; } /* show top level structures */ DEFUN (show_ipv6_ospf6, show_ipv6_ospf6_cmd, "show ipv6 ospf6", SHOW_STR IP6_STR OSPF6_STR) { OSPF6_CMD_CHECK_RUNNING (); ospf6_show (vty); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_nexthoplist, show_ipv6_ospf6_nexthoplist_cmd, "show ipv6 ospf6 nexthop-list", SHOW_STR IP6_STR OSPF6_STR "List of nexthop\n") { #if 0 listnode i; struct ospf6_nexthop *nh; char buf[128]; for (i = listhead (nexthoplist); i; nextnode (i)) { nh = (struct ospf6_nexthop *) getdata (i); nexthop_str (nh, buf, sizeof (buf)); vty_out (vty, "%s%s", buf, VTY_NEWLINE); } #endif return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_statistics, show_ipv6_ospf6_statistics_cmd, "show ipv6 ospf6 statistics", SHOW_STR IP6_STR OSPF6_STR "Statistics\n") { OSPF6_CMD_CHECK_RUNNING (); ospf6_statistics_show (vty, ospf6); return CMD_SUCCESS; } /* change Router_ID commands. */ DEFUN (ospf6_router_id, ospf6_router_id_cmd, "router-id ROUTER_ID", "Configure ospf Router-ID.\n" V4NOTATION_STR) { int ret; u_int32_t router_id; ret = inet_pton (AF_INET, argv[0], &router_id); if (!ret) { vty_out (vty, "malformed ospf router identifier%s", VTY_NEWLINE); vty_out (vty, "%s", VTY_NEWLINE); return CMD_WARNING; } if (IS_OSPF6_DUMP_CONFIG) zlog_info ("CONFIG: router-id %s", argv[0]); ospf6->router_id = router_id; return CMD_SUCCESS; } int ospf6_interface_bind_area (struct vty *vty, char *if_name, char *area_name, char *plist_name, int passive) { struct interface *ifp; struct ospf6_interface *o6i; struct ospf6_area *o6a; u_int32_t area_id; /* find/create ospf6 interface */ ifp = if_get_by_name (if_name); o6i = (struct ospf6_interface *) ifp->info; if (! o6i) o6i = ospf6_interface_create (ifp); /* parse Area-ID */ if (inet_pton (AF_INET, area_name, &area_id) != 1) { vty_out (vty, "Invalid Area-ID: %s%s", area_name, VTY_NEWLINE); return CMD_ERR_AMBIGUOUS; } /* find/create ospf6 area */ o6a = ospf6_area_lookup (area_id, ospf6); if (!o6a) { o6a = ospf6_area_create (area_id); o6a->ospf6 = ospf6; listnode_add (ospf6->area_list, o6a); } if (o6i->area) { if (o6i->area != o6a) { vty_out (vty, "Aready attached to area %s%s", o6i->area->str, VTY_NEWLINE); return CMD_ERR_NOTHING_TODO; } } else { listnode_add (o6a->if_list, o6i); o6i->area = o6a; } /* prefix-list name */ if (plist_name) { if (o6i->plist_name) XFREE (MTYPE_PREFIX_LIST_STR, o6i->plist_name); o6i->plist_name = XSTRDUP (MTYPE_PREFIX_LIST_STR, plist_name); } else { if (o6i->plist_name) XFREE (MTYPE_PREFIX_LIST_STR, o6i->plist_name); o6i->plist_name = NULL; } if (passive) { listnode node; struct ospf6_neighbor *o6n; SET_FLAG (o6i->flag, OSPF6_INTERFACE_FLAG_PASSIVE); if (o6i->thread_send_hello) { thread_cancel (o6i->thread_send_hello); o6i->thread_send_hello = (struct thread *) NULL; } for (node = listhead (o6i->neighbor_list); node; nextnode (node)) { o6n = getdata (node); if (o6n->inactivity_timer) thread_cancel (o6n->inactivity_timer); thread_execute (master, inactivity_timer, o6n, 0); } } else { UNSET_FLAG (o6i->flag, OSPF6_INTERFACE_FLAG_PASSIVE); if (o6i->thread_send_hello == NULL) thread_add_event (master, ospf6_send_hello, o6i, 0); } /* enable I/F if it's not enabled still */ if (! ospf6_interface_is_enabled (o6i->interface->ifindex)) thread_add_event (master, interface_up, o6i, 0); else CALL_FOREACH_LSA_HOOK (hook_interface, hook_change, o6i); CALL_CHANGE_HOOK (&interface_hook, o6i); return CMD_SUCCESS; } DEFUN (ospf6_interface_area_plist, ospf6_interface_area_plist_cmd, "interface IFNAME area A.B.C.D prefix-list WORD", "Enable routing on an IPv6 interface\n" IFNAME_STR "Set the OSPF6 area ID\n" "OSPF6 area ID in IPv4 address notation\n" OSPF6_PREFIX_LIST_STR "IPv6 prefix-list name\n" ) { if (IS_OSPF6_DUMP_CONFIG) zlog_info ("CONFIG: interface %s area %s prefix-list %s", argv[0], argv[1], argv[2]); return ospf6_interface_bind_area (vty, argv[0], argv[1], argv[2], 0); } DEFUN (ospf6_interface_area_plist_passive, ospf6_interface_area_plist_passive_cmd, "interface IFNAME area A.B.C.D prefix-list WORD passive", "Enable routing on an IPv6 interface\n" IFNAME_STR "Set the OSPF6 area ID\n" "OSPF6 area ID in IPv4 address notation\n" OSPF6_PREFIX_LIST_STR "IPv6 prefix-list name\n" "IPv6 prefix-list name\n" OSPF6_PASSIVE_STR ) { if (IS_OSPF6_DUMP_CONFIG) zlog_info ("CONFIG: interface %s area %s prefix-list %s passive", argv[0], argv[1], argv[2]); return ospf6_interface_bind_area (vty, argv[0], argv[1], argv[2], 1); } DEFUN (ospf6_interface_area, ospf6_interface_area_cmd, "interface IFNAME area A.B.C.D", "Enable routing on an IPv6 interface\n" IFNAME_STR "Set the OSPF6 area ID\n" "OSPF6 area ID in IPv4 address notation\n" ) { struct interface *ifp; struct ospf6_interface *o6i; int passive; char *plist_name; if (IS_OSPF6_DUMP_CONFIG) zlog_info ("CONFIG: interface %s area %s", argv[0], argv[1]); ifp = if_get_by_name (argv[0]); o6i = (struct ospf6_interface *) ifp->info; if (o6i) { passive = CHECK_FLAG (o6i->flag, OSPF6_INTERFACE_FLAG_PASSIVE); plist_name = o6i->plist_name; } else { passive = 0; plist_name = NULL; } return ospf6_interface_bind_area (vty, argv[0], argv[1], plist_name, passive); } DEFUN (ospf6_interface_area_passive, ospf6_interface_area_passive_cmd, "interface IFNAME area A.B.C.D passive", "Enable routing on an IPv6 interface\n" IFNAME_STR "Set the OSPF6 area ID\n" "OSPF6 area ID in IPv4 address notation\n" OSPF6_PASSIVE_STR ) { if (IS_OSPF6_DUMP_CONFIG) zlog_info ("CONFIG: interface %s area %s passive", argv[0], argv[1]); return ospf6_interface_bind_area (vty, argv[0], argv[1], NULL, 1); } DEFUN (no_ospf6_interface_area, no_ospf6_interface_area_cmd, "no interface IFNAME area A.B.C.D", NO_STR "Disable routing on an IPv6 interface\n" IFNAME_STR) { struct interface *ifp; struct ospf6_interface *o6i; struct ospf6 *o6; u_int32_t area_id; o6 = (struct ospf6 *) vty->index; ifp = if_lookup_by_name (argv[0]); if (!ifp) return CMD_ERR_NO_MATCH; o6i = (struct ospf6_interface *) ifp->info; if (!o6i) return CMD_SUCCESS; /* parse Area-ID */ if (inet_pton (AF_INET, argv[1], &area_id) != 1) { vty_out (vty, "Invalid Area-ID: %s%s", argv[1], VTY_NEWLINE); return CMD_ERR_AMBIGUOUS; } if (o6i->area->area_id != area_id) { vty_out (vty, "Wrong Area-ID: %s aready attached to area %s%s", o6i->interface->name, o6i->area->str, VTY_NEWLINE); return CMD_ERR_NOTHING_TODO; } if (o6i->area) thread_execute (master, interface_down, o6i, 0); listnode_delete (o6i->area->if_list, o6i); o6i->area = (struct ospf6_area *) NULL; return CMD_SUCCESS; } DEFUN (ospf6_area_range, ospf6_area_range_cmd, "area A.B.C.D range X:X::X:X/M", "OSPFv3 area parameters\n" "OSPFv3 area ID in IPv4 address format\n" "Summarize routes matching address/mask (border routers only)\n" "IPv6 address range\n") { struct ospf6 *o6; struct ospf6_area *o6a; u_int32_t area_id; int ret; o6 = (struct ospf6 *) vty->index; inet_pton (AF_INET, argv[0], &area_id); o6a = ospf6_area_lookup (area_id, o6); if (! o6a) { vty_out (vty, "No such area%s", VTY_NEWLINE); return CMD_ERR_NO_MATCH; } ret = str2prefix_ipv6 (argv[1], &o6a->area_range); if (ret <= 0) { vty_out (vty, "Malformed IPv6 address%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN (ospf6_passive_interface, ospf6_passive_interface_cmd, "passive-interface IFNAME", OSPF6_PASSIVE_STR IFNAME_STR) { struct interface *ifp; struct ospf6_interface *o6i; ifp = if_get_by_name (argv[0]); if (ifp->info) o6i = (struct ospf6_interface *) ifp->info; else o6i = ospf6_interface_create (ifp); SET_FLAG (o6i->flag, OSPF6_INTERFACE_FLAG_PASSIVE); if (o6i->thread_send_hello) { thread_cancel (o6i->thread_send_hello); o6i->thread_send_hello = (struct thread *) NULL; } return CMD_SUCCESS; } DEFUN (no_ospf6_passive_interface, no_ospf6_passive_interface_cmd, "no passive-interface IFNAME", NO_STR OSPF6_PASSIVE_STR IFNAME_STR) { struct interface *ifp; struct ospf6_interface *o6i; ifp = if_lookup_by_name (argv[0]); if (! ifp) return CMD_ERR_NO_MATCH; o6i = (struct ospf6_interface *) ifp->info; UNSET_FLAG (o6i->flag, OSPF6_INTERFACE_FLAG_PASSIVE); if (o6i->thread_send_hello == NULL) thread_add_event (master, ospf6_send_hello, o6i, 0); return CMD_SUCCESS; } #ifdef HAVE_SETPROCTITLE extern int _argc; extern char **_argv; DEFUN (set_proctitle, set_proctitle_cmd, "set proctitle (version|normal|none)", "Set command\n" "Process title\n" "Version information\n" "Normal command-line options\n" "Just program name\n") { int i; char buf[64], tmp[64]; if (strncmp (argv[0], "v", 1) == 0) { proctitle_mode = 1; setproctitle ("%s Zebra: %s", OSPF6_DAEMON_VERSION, QUAGGA_VERSION); } else if (strncmp (argv[0], "nor", 3) == 0) { proctitle_mode = 0; memset (tmp, 0, sizeof (tmp)); memset (buf, 0, sizeof (buf)); for (i = 0; i < _argc; i++) { snprintf (buf, sizeof (buf), "%s%s ", tmp, _argv[i]); memcpy (&tmp, &buf, sizeof (tmp)); } setproctitle (buf); } else if (strncmp (argv[0], "non", 3) == 0) { proctitle_mode = -1; setproctitle (NULL); } else return CMD_ERR_NO_MATCH; return CMD_SUCCESS; } #endif /* HAVE_SETPROCTITLE */ /* OSPF configuration write function. */ int ospf6_config_write (struct vty *vty) { listnode j, k; char buf[64]; struct ospf6_area *area; struct ospf6_interface *o6i; if (proctitle_mode == 1) vty_out (vty, "set proctitle version%s", VTY_NEWLINE); else if (proctitle_mode == -1) vty_out (vty, "set proctitle none%s", VTY_NEWLINE); vty_out (vty, "!%s", VTY_NEWLINE); if (! ospf6) return 0; /* OSPFv6 configuration. */ if (!ospf6) return CMD_SUCCESS; inet_ntop (AF_INET, &ospf6->router_id, buf, sizeof (buf)); vty_out (vty, "router ospf6%s", VTY_NEWLINE); vty_out (vty, " router-id %s%s", buf, VTY_NEWLINE); ospf6_redistribute_config_write (vty); ospf6_damp_config_write (vty); for (j = listhead (ospf6->area_list); j; nextnode (j)) { area = (struct ospf6_area *)getdata (j); for (k = listhead (area->if_list); k; nextnode (k)) { o6i = (struct ospf6_interface *) getdata (k); vty_out (vty, " interface %s area %s%s", o6i->interface->name, area->str, VTY_NEWLINE); } } vty_out (vty, "!%s", VTY_NEWLINE); return 0; } /* OSPF6 node structure. */ struct cmd_node ospf6_node = { OSPF6_NODE, "%s(config-ospf6)# ", vtysh: 1 }; /* Install ospf related commands. */ void ospf6_init () { /* Install ospf6 top node. */ install_node (&ospf6_node, ospf6_config_write); install_element (VIEW_NODE, &show_ipv6_ospf6_cmd); install_element (VIEW_NODE, &show_version_ospf6_cmd); install_element (ENABLE_NODE, &show_ipv6_ospf6_cmd); install_element (ENABLE_NODE, &show_version_ospf6_cmd); install_element (ENABLE_NODE, &reload_cmd); install_element (CONFIG_NODE, &router_ospf6_cmd); install_element (CONFIG_NODE, &interface_cmd); install_element (CONFIG_NODE, &no_interface_cmd); #ifdef OSPF6_STATISTICS install_element (VIEW_NODE, &show_ipv6_ospf6_statistics_cmd); install_element (ENABLE_NODE, &show_ipv6_ospf6_statistics_cmd); #endif /* OSPF6_STATISTICS */ #ifdef OSPF6_GARBAGE_COLLECT install_element (ENABLE_NODE, &garbage_collection_cmd); #endif /* OSPF6_GARBAGE_COLLECT */ #ifdef HAVE_SETPROCTITLE install_element (CONFIG_NODE, &set_proctitle_cmd); #endif /* HAVE_SETPROCTITLE */ install_default (OSPF6_NODE); install_element (OSPF6_NODE, &ospf6_router_id_cmd); install_element (OSPF6_NODE, &ospf6_interface_area_cmd); install_element (OSPF6_NODE, &ospf6_interface_area_passive_cmd); install_element (OSPF6_NODE, &ospf6_interface_area_plist_cmd); install_element (OSPF6_NODE, &ospf6_interface_area_plist_passive_cmd); install_element (OSPF6_NODE, &no_ospf6_interface_area_cmd); install_element (OSPF6_NODE, &ospf6_passive_interface_cmd); install_element (OSPF6_NODE, &no_ospf6_passive_interface_cmd); install_element (OSPF6_NODE, &ospf6_area_range_cmd); /* Make empty list of top list. */ if_init (); /* Install access list */ access_list_init (); /* Install prefix list */ prefix_list_init (); ospf6_dump_init (); #ifdef HAVE_OSPF6_DAMP ospf6_damp_init (); #endif /*HAVE_OSPF6_DAMP*/ ospf6_hook_init (); ospf6_lsa_init (); ospf6_top_init (); ospf6_area_init (); ospf6_interface_init (); ospf6_neighbor_init (); ospf6_zebra_init (); ospf6_routemap_init (); ospf6_lsdb_init (); ospf6_spf_init (); ospf6_intra_init (); ospf6_abr_init (); ospf6_asbr_init (); } void ospf6_terminate () { /* stop ospf6 */ ospf6_stop (); /* log */ zlog (NULL, LOG_INFO, "OSPF6d terminated"); } void ospf6_maxage_remover () { #if 0 if (IS_OSPF6_DUMP_LSDB) zlog_info ("MaxAge Remover"); #endif ospf6_top_schedule_maxage_remover (NULL, 0, ospf6); (*ospf6->foreach_area) (ospf6, NULL, 0, ospf6_area_schedule_maxage_remover); (*ospf6->foreach_if) (ospf6, NULL, 0, ospf6_interface_schedule_maxage_remover); } void * ospf6_lsa_get_scope (u_int16_t type, struct ospf6_interface *o6i) { if (OSPF6_LSA_IS_SCOPE_LINKLOCAL (ntohs (type))) return o6i; else if (OSPF6_LSA_IS_SCOPE_AREA (ntohs (type))) return o6i->area; else if (OSPF6_LSA_IS_SCOPE_AS (ntohs (type))) return o6i->area->ospf6; else return NULL; }