diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/.cvsignore | 4 | ||||
-rw-r--r-- | lib/ChangeLog | 1926 | ||||
-rw-r--r-- | lib/Makefile.am | 29 | ||||
-rw-r--r-- | lib/Makefile.in | 469 | ||||
-rw-r--r-- | lib/buffer.c | 568 | ||||
-rw-r--r-- | lib/buffer.h | 77 | ||||
-rw-r--r-- | lib/checksum.c | 47 | ||||
-rw-r--r-- | lib/command.c | 2981 | ||||
-rw-r--r-- | lib/command.h | 308 | ||||
-rw-r--r-- | lib/daemon.c | 80 | ||||
-rw-r--r-- | lib/distribute.c | 709 | ||||
-rw-r--r-- | lib/distribute.h | 57 | ||||
-rw-r--r-- | lib/filter.c | 2058 | ||||
-rw-r--r-- | lib/filter.h | 67 | ||||
-rw-r--r-- | lib/getopt.c | 1054 | ||||
-rw-r--r-- | lib/getopt.h | 133 | ||||
-rw-r--r-- | lib/getopt1.c | 190 | ||||
-rw-r--r-- | lib/hash.c | 182 | ||||
-rw-r--r-- | lib/hash.h | 71 | ||||
-rw-r--r-- | lib/if.c | 713 | ||||
-rw-r--r-- | lib/if.h | 222 | ||||
-rw-r--r-- | lib/if_rmap.c | 305 | ||||
-rw-r--r-- | lib/if_rmap.h | 47 | ||||
-rw-r--r-- | lib/keychain.c | 1001 | ||||
-rw-r--r-- | lib/keychain.h | 56 | ||||
-rw-r--r-- | lib/linklist.c | 312 | ||||
-rw-r--r-- | lib/linklist.h | 101 | ||||
-rw-r--r-- | lib/log.c | 483 | ||||
-rw-r--r-- | lib/log.h | 128 | ||||
-rw-r--r-- | lib/md5-gnu.h | 156 | ||||
-rw-r--r-- | lib/md5.c | 447 | ||||
-rw-r--r-- | lib/memory.c | 493 | ||||
-rw-r--r-- | lib/memory.h | 245 | ||||
-rw-r--r-- | lib/network.c | 71 | ||||
-rw-r--r-- | lib/network.h | 29 | ||||
-rw-r--r-- | lib/pid_output.c | 77 | ||||
-rw-r--r-- | lib/plist.c | 2881 | ||||
-rw-r--r-- | lib/plist.h | 78 | ||||
-rw-r--r-- | lib/prefix.c | 696 | ||||
-rw-r--r-- | lib/prefix.h | 161 | ||||
-rw-r--r-- | lib/print_version.c | 31 | ||||
-rw-r--r-- | lib/regex-gnu.h | 542 | ||||
-rw-r--r-- | lib/regex.c | 5891 | ||||
-rw-r--r-- | lib/routemap.c | 1077 | ||||
-rw-r--r-- | lib/routemap.h | 194 | ||||
-rw-r--r-- | lib/smux.c | 1501 | ||||
-rw-r--r-- | lib/smux.h | 159 | ||||
-rw-r--r-- | lib/sockopt.c | 199 | ||||
-rw-r--r-- | lib/sockopt.h | 41 | ||||
-rw-r--r-- | lib/sockunion.c | 756 | ||||
-rw-r--r-- | lib/sockunion.h | 128 | ||||
-rw-r--r-- | lib/str.c | 62 | ||||
-rw-r--r-- | lib/str.h | 24 | ||||
-rw-r--r-- | lib/stream.c | 479 | ||||
-rw-r--r-- | lib/stream.h | 113 | ||||
-rw-r--r-- | lib/table.c | 503 | ||||
-rw-r--r-- | lib/table.h | 74 | ||||
-rw-r--r-- | lib/tcpfilter.c | 69 | ||||
-rw-r--r-- | lib/tcpfilter.h | 21 | ||||
-rw-r--r-- | lib/thread.c | 668 | ||||
-rw-r--r-- | lib/thread.h | 139 | ||||
-rw-r--r-- | lib/vector.c | 189 | ||||
-rw-r--r-- | lib/vector.h | 58 | ||||
-rw-r--r-- | lib/version.h | 39 | ||||
-rw-r--r-- | lib/vty.c | 2792 | ||||
-rw-r--r-- | lib/vty.h | 205 | ||||
-rw-r--r-- | lib/zclient.c | 901 | ||||
-rw-r--r-- | lib/zclient.h | 164 | ||||
-rw-r--r-- | lib/zebra.h | 312 |
69 files changed, 37043 insertions, 0 deletions
diff --git a/lib/.cvsignore b/lib/.cvsignore new file mode 100644 index 00000000..051b8e4f --- /dev/null +++ b/lib/.cvsignore @@ -0,0 +1,4 @@ +Makefile +*.o +version.c +.deps diff --git a/lib/ChangeLog b/lib/ChangeLog new file mode 100644 index 00000000..b4d0ae12 --- /dev/null +++ b/lib/ChangeLog @@ -0,0 +1,1926 @@ +2002-09-28 Yasuhiro Ohara <yasu@sfc.wide.ad.jp> + + * vty.c (vty_flush): One line more on vty. + +2002-09-27 Kunihiro Ishiguro <kunihiro@ipinfusion.com> + + * vector.c (vector_lookup): Add new function. + +2002-08-19 Kunihiro Ishiguro <kunihiro@ipinfusion.com> + + * thread.c (timeval_adjust): Fix unconditional crush due to + FreeBSD's select() system call timeval value check. + +2002-07-07 Kunihiro Ishiguro <kunihiro@ipinfusion.com> + + * zebra-0.93 released. + +2002-06-21 Kunihiro Ishiguro <kunihiro@ipinfusion.com> + + * if.c (ifc_pointopoint): Add ifc_pointopoint() accoding to Frank + van Maarseveen's suggestion. + +2002-06-18 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c: Change bcopy() to memcpy(). + +2001-12-12 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (config_password): Fix host.password clear bug. + Reported by Wang Jian <lark@linux.net.cn>. + +2001-08-29 Kunihiro Ishiguro <kunihiro@ipinfusion.com> + + * thread.c (thread_should_yield): New function to check thread + should yeild it's execution to other thread. Suggested by: Rick + Payne <rickp@ayrnetworks.com> + +2001-08-20 Kunihiro Ishiguro <kunihiro@ipinfusion.com> + + * thread.c (thread_timer_cmp): Rewrite function. + + * hash.c: Add hash_get(). Change hash_pull() to hash_release(). + +2001-08-19 Kunihiro Ishiguro <kunihiro@ipinfusion.com> + + * zebra-0.92a released. + +2001-08-15 Kunihiro Ishiguro <kunihiro@ipinfusion.com> + + * zebra-0.92 released. + +2001-08-12 Akihiro Mizutani <mizutani@dml.com> + + * prefix.c (netmask_str2prefix_str): Convert "1.1.0.0 255.255.0.0" + string to "1.1.0.0/16". + +2001-08-10 Kunihiro Ishiguro <kunihiro@zebra.org> + + * filter.c (access_list_lookup): access_list_lookup's first + argument is changed from address family to AFI. + + * plist.c: (prefix_list_lookup): Likewise. + +2001-07-27 Akihiro Mizutani <mizutani@dml.com> + + * plist.c: ge and le display order is changed. Old compatible + rule (len <= ge-value <= le-value) is removed. + +2001-07-08 Kunihiro Ishiguro <kunihiro@zebra.org> + + * prefix.h: Temporary fix for alignment of prefix problem. + +2001-06-21 Kunihiro Ishiguro <kunihiro@zebra.org> + + * prefix.h (struct prefix): Remove safi and padding field. + (struct prefix_ipv4): Likewise. + (struct prefix_ipv6): Likewise. + (struct prefix_ls): Likewise. + (struct prefix_rd): Likewise. + + * command.h (enum node_type): Preparation for BGP new config. + + * vty.c (vty_end_config): Likewise. + +2001-06-17 Kunihiro Ishiguro <kunihiro@zebra.org> + + * routemap.c (route_map_rule_delete): Call func_free when + route-map rule is deleted. + +2001-06-14 "Akihiro Mizutani" <mizutani@dml.com> + + * routemap.c (route_map_index_lookup): Prevent to use deny and + permit for same route-map sequence. + +2001-04-12 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_read_config): Fix warning. + +2001-03-08 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (IPV6_PREFIX_STR): Add '.' and '%' for IPv6 address + strings. + +2001-03-07 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra.h (_XPG4_2): Define _XPG4_2 and __EXTENSIONS__ for + CMSG_FIRSTHDR. + +2001-03-07 Michael Rozhavsky <mrozhavsky@opticalaccess.com> + + * zebra.h (struct in_pktinfo): structure in_pktinfo declaration. + +2001-02-19 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.c (memory_list_lib): Add MTYPE_NEXTHOP for "show memory + lib" member. + +2001-02-13 Matthew Grant <grantma@anathoth.gen.nz> + + * vty.c (vty_read_config): Revert check of integrate_default when + VTYSH is defined. + +2001-02-13 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_read_config): Do not check integrate_default. That + should be used only by vtysh. + +2001-02-08 Matthew Grant <grantma@anathoth.gen.nz> + + * vty.c (vty_serv_un): Set umask 0077. + (vty_read_config): Stat for vtysh Zebra.conf, if found startup and + wait for boot configuration. + + * if.c (if_lookup_address): Make it smart implementation. + + * sockopt.c (setsockopt_multicast_ipv4): Set up a multicast socket + options for IPv4 This is here so that people only have to do their + OS multicast mess in one place rather than all through zebra, + ospfd, and ripd . + +2001-02-04 Akihiro Mizutani <mizutani@dml.com> + + * plist.c (vty_prefix_list_install): Even when argument is + invalid, new memory is allocated. Now memory allocation is done + after argument check. + +2001-02-01 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra-0.91 is released. + +2001-01-31 Akihiro Mizutani <mizutani@dml.com> + + * vty.c (vty_login): Add vty login command. + +2001-01-31 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_reset): Close accept socket. + +2001-01-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): MTYPE_ATTR_TRANSIT is added for unknown transit + attribute. + +2001-01-22 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zclient.c (zebra_interface_address_add_read): Fetch interface + address flag. + (zebra_interface_address_delete_read): Likewise. + +2001-01-16 Kunihiro Ishiguro <kunihiro@zebra.org> + + * table.c (route_node_match_ipv4): Utility function for IPv4 + address lookup. + (route_node_match_ipv6): Utility function for IPv4 address lookup. + +2001-01-15 Kunihiro Ishiguro <kunihiro@zebra.org> + + * if.c: Delete RIP_API part until new implementation comes out. + +2001-01-13 Kunihiro Ishiguro <kunihiro@zebra.org> + + * hash.h (struct Hash): Rename alloc to count. Change type to + unsigned long. + + * stream.c (stream_getc_from): New function. + (stream_getw_from): Likewise. + + * zebra.h (ZEBRA_FLAG_STATIC): Add new flag for persistent route. + +2001-01-11 Kunihiro Ishiguro <kunihiro@zebra.org> + + * flap.c: File is removed. + + * flap.c: Likewise. + + * roken.h: Likewise. + + * buffer.c (buffer_new): Remove type option to buffer_new(). + +2001-01-10 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zclient.c (zapi_ipv4_delete): Remove OLD_RIB part. + +2001-01-09 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra-0.90 is released. + + * command.c: Update Copyright year. + +2001-01-09 Matthew Grant <grantma@anathoth.gen.nz> + + * if.c (if_create): Register connected_free() function for + deletion. + (if_delete): Free connected information when the interface is + deleted. + (if_lookup_by_index): Fix argument type from int to unsigned int. + (connected_add): Keep list in order if old info found, essential + for repeatable operation in some daemons. + +2001-01-09 endo@suri.co.jp (Masahiko Endo) + + * vty.c (vty_flush): When vty->statis is VTY_CLOSE do not add vty + read thread. + +2001-01-08 Kunihiro Ishiguro <kunihiro@zebra.org> + + * filter.c (access_list_delete): Access-list name is not freed. + + * plist.c (prefix_list_delete): Prefix-list name is not freed. + +2000-12-29 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zclient.c (zclient_start): Change to use UNIX domain + socket for zebra communication. + + * vector.c (vector_init): vector_alloc and vector_data_alloc is + removed. All memory allocation count should be maintained by + XMALLOC and XFREE macros. + +2000-12-28 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra.h (ZEBRA_NEXTHOP_IFINDEX): Define ZEBRA_NEXTHOP_* values. + +2000-12-27 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra.h (ZEBRA_ERR_RTEXIST): Make zebra error code to negative + value. + +2000-12-25 "Wataru Uno" <wataru@po.ntts.co.jp> + + * vty.c (vtysh_read): Don't allocate new buffer because buffer is + allocated in vty_new (). + +2000-12-14 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): Add MTYPE_AS_FILTER_STR. + + * command.c (config_write_terminal): Display "end" at the end of + configuration. + + * plist.c (vty_prefix_list_install): Use AF_INET to determine + lenum length. + +2000-12-13 "Wataru Uno" <wataru@po.ntts.co.jp> + + * buffer.c (buffer_flush_vty): If IOV_MAX defined in the System, + then all lines write by IOV_MAX. + +2000-12-12 Michael Rozhavsky <mrozhavsky@opticalaccess.com> + + * command.c (config_write_file): Robust method for writing + configuration file and recover from backing up config file. + +2000-11-29 Kunihiro Ishiguro <kunihiro@zebra.org> + + * smux.c (smux_connect): More fail check. + (smux_trap): When SMUX connection is not established, do nothing. + +2000-11-28 Gleb Natapov <gleb@nbase.co.il> + + * thread.c (thread_fetch): Execut event list first. Old event + list is renamed to ready list. With this change, event thread is + executed before any other thread. + + * thread.h (struct thread_master): Add ready list. + +2000-11-28 Kunihiro Ishiguro <kunihiro@zebra.org> + + * linklist.c (listnode_add_after): Add node right after the + listnode pointer. + +2000-11-27 Kunihiro Ishiguro <kunihiro@zebra.org> + + * smux.h: Pass struct variable to WriteMethod. + +2000-11-25 Frank van Maarseveen <F.vanMaarseveen@inter.NL.net> + + * if.c (if_lookup_address): When looking up interface with IP + address, Sometimes multiple interfaces will match. Now PtP + interfaces prevail in such a case which seem the right thing to + do: There will probably also be host routes which usually prevail + over network routes. + +2000-11-25 Kunihiro Ishiguro <kunihiro@zebra.org> + + * smux.c (smux_trap): SMUX trap implementation. + +2000-11-19 Akihiro Mizutani <mizutani@dml.com> + + * plist.c: Add automatic conversion function of an old rule. + ex.) 10.0.0.0/8 ge 8 -> 10.0.0.0/8 le 32 + +2000-11-16 Yon Uriarte <ukl2@rz.uni-karlsruhe.de> + + * zclient.c (zebra_interface_add_read): Read hardware address when + hw_addr_len is greater than 0. + +2000-11-15 Akihiro Mizutani <mizutani@dml.com> + + * plist.c: The rule of "len <= ge-value <= le-value" + was changed to "len < ge-value <= le-value". + +2000-11-09 Yasuhiro Ohara <yasu@sfc.wide.ad.jp> + + * memory.[ch]: Added #define and functions for ospf6d. + + * log.[ch]: some platform says that the data of used va_list + is undefined. Changed to hold list of va_list for each + vsnprintf. + +2000-11-07 Rick Payne <rickp@rossfell.co.uk> + + * memory.h (enum): Add MTYPE_COMMUNITY_REGEXP. + +2000-11-06 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (config_exit): Fix bug of missing break after case + BGP_VPNV4_NODE. + +2000-10-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vector.c (vector_unset): Check i is not nevative. + +2000-10-24 Arkadiusz Miskiewicz <misiek@pld.org.pl> + + * smux.c (smux_sock): Set terminating '\0'. Check address family. + + * vty.c (vty_serv_sock_addrinfo): Set terminating '\0'. Use + gai_strerror. Check address family. + +2000-10-23 Jochen Friedrich <jochen@scram.de> + + * smux.c: Use linklist rather than vector. + (smux_getnext): A SMUX subagent has to behave as if it manages the + whole SNMP MIB tree itself. It's the duty of the master agent to + collect the best answer and return it to the manager. See RFC 1227 + chapter 3.1.6 for the glory details :-). ucd-snmp really behaves + bad here as it actually might ask multiple times for the same + GETNEXT request as it throws away the answer when it expects it in + a different subtree and might come back later with the very same + request. + +2000-10-23 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (cmd_init): Log related command are only installed for + terminal mode. + +2000-10-21 Kunihiro Ishiguro <kunihiro@zebra.org> + + * Makefile.am (libzebra_a_SOURCES): Remove duplicated buffer.c. + + * zebra.h: Remove #warn directive. + +2000-10-20 Kunihiro Ishiguro <kunihiro@zebra.org> + + * keychain.c (keychain_init): Register "key chain" command to + KEYCHAIN_NODE and KEYCHAIN_KEY_NODE. + + * vty.c (vty_end_config): Fix missing vty_cinfig_unlock for other + CONFIG_NODE. + + * command.c (config_end): Likewise. + + * keychain.c (keychain_get): Key is sorted by it's identifier + value. + +2000-10-19 Kunihiro Ishiguro <kunihiro@zebra.org> + + * linklist.c (list_delete_all_node): Call delete function if it is + defined. + + * command.c (cmd_execute_command_strict): Add modification for + vtysh. + (cmd_execute_command_strict): Remove first argument cmdvec because + it is global varibale in command.c. + +2000-10-18 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (cmd_init): Install + copy_runningconfig_startupconfig_cmd only in terminal mode. + + * linklist.c (list_delete_node): Simplify the function. + (listnode_lookup): Renamed from list_lookup_node. + +2000-10-17 Kunihiro Ishiguro <kunihiro@zebra.org> + + * stream.h: Undef stream_read and stream_write without + parenthesis. + + * newlist.c: File removed. + + * newlist.h: Likewise. + + * linklist.c (list_new): Remove list_init(). To allocate new + linked list, please use list_new(). + (listnode_add): Remove list_add_node(). To add new node to linked + list, please use listnode_add(). + (list_delete_by_val): Revemove fucntion. + +2000-10-16 Nobuaki Tanaka <nobby@po.ntts.co.jp> + + * table.c (route_table_free): Reimplement route_table_free(). + +2000-10-11 Kunihiro Ishiguro <kunihiro@zebra.org> + + * keychain.c (keychain_get): Register key_delete_func to key + list's delete function. Use linklist.c instead of newlist.c. + +2000-10-04 Akihiro Mizutani <mizutani@dml.com> + + * filter.c (access_list_remark): Add access-list's remark command. + (no_access_list): "no access-list 100 permit any" error message + bug is fixed. + +2000-10-03 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): Add MTYPE_SOCKUNION. + +2000-10-02 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra-0.89 is released. + +2000-10-01 Kunihiro Ishiguro <kunihiro@zebra.org> + + * linklist.c (list_add_node_head): Delete unused function. + (list_add_node_tail): Likewise. + +2000-09-26 Kunihiro Ishiguro <kunihiro@zebra.org> + + * stream.c (stream_read_unblock): Add new function for unblocking + read. + +2000-09-26 Jochen Friedrich <jochen@nwe.de> + + * smux.c (smux_register): Fix bug of can't register more than one + MIB with SMUX. + +2000-09-26 Makoto Otsuka <otsuka@inl.ntts.co.jp> + + * vty.c (vty_close): Fix memory leak of sb_buffer. + (vty_new): Likewise. + +2000-09-21 steve@Watt.COM (Steve Watt) + + * log.h: Do not declare zlog_priority[0] variable. + +2000-09-12 Kunihiro Ishiguro <kunihiro@zebra.org> + + * linklist.h (struct _list ): Add member cmp for compare function. + (struct _list ): Member up is deleted + +2000-09-12 David Lipovkov <dlipovkov@OpticalAccess.com> + + * if.c: Include RIP_API header when RIP API is enabled. + +2000-09-10 Kunihiro Ishiguro <kunihiro@zebra.org> + + * prefix.c (prefix_free): Siplify prefix_free(). + + * keychain.c (key_match_for_accept): strncmp check bug is fixed. + +2000-09-07 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra.h: Merge roken.h into zebra.h. + +2000-09-05 Akihiro Mizutani <mizutani@dml.com> + + * routemap.c (route_map_init_vty): Install route-map command to + RMAP_NODE. + +2000-08-22 Kunihiro Ishiguro <kunihiro@zebra.org> + + * thread.c (thread_get_id): Remove pthread related garbage. + + * command.h (struct host): Likewise. + + * zebra.h: Likewise. + +2000-08-20 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (node_type ): Add AAA node for authentication. + + * vty.c (vty_close): Do not close stdout. + +2000-08-18 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_init_vtysh): Added for vtysh. + + * distribute.c (districute_list_prefix_all): Interface independent + filter can be set. + (distribute_list_all): Likewise. + (config_show_distribute): Display current distribute-list status + for "show ip protocols". + +2000-08-18 Akihiro Mizutani <mizutani@dml.com> + + * command.c (config_terminal_no_length): no terminal monitor -> + terminal no monitor + (cmd_init): Do not install service_terminal_length_cmd into + ENABLE_NODE. + + * vty.c (terminal_no_monitor): no terminal length -> terminal no + length. + +2000-08-17 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra-0.88 is released. + +2000-08-17 Magnus Ahltorp <ahltorp@nada.kth.se> + + * vty.h (struct vty ): Add iac_sb_in_progress and sb_buffer for + better IAC handling. + + * vty.c (vty_telnet_option): Change telnet option handling. + +2000-08-15 Gleb Natapov <gleb@nbase.co.il> + + * zclient.c (zclient_redistribute_unset): New function added. + +2000-08-14 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zclient.c (zebra_interface_add_read): Change ifindex restore + size from two octet to four. + (zebra_interface_state_read): Likewise. + (zebra_interface_address_add_read): Likewise. + +2000-08-13 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_event): Use vector_set_index() instead of + vector_set(). + +2000-08-07 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra.h (ZEBRA_XXX_DISTANCE_DEFAULT): Define Default + Administrative Distance of each protocol. + +2000-08-07 Matthew Grant <grantma@anathoth.gen.nz> + + * if.h (struct interface ): Add new member bandwidth to struct + interface. + + * zclient.c (zebra_interface_add_read): Fetch bandwidth value. + (zebra_interface_state_read): Likewise. + +2000-08-07 Gleb Natapov <gleb@nbase.co.il> + + * routemap.c (route_map_event_hook): New hook route_map_event_hook + is added. This hook is called when route-map is changed. The + parameters passed to the hook are 'event' and 'route-map name' + + * routemap.h: Add prototype for route_map_event_hook(). + +2000-08-06 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zclient.c (zebra_ipv4_route): zebra_ipv4_route(), + zebra_ipv4_add(), zebra_ipv4_delete() are removed. + + * routemap.c (route_map_empty): Add new function. + (route_map_delete): Use route_map_index_delete() instead of + route_map_index_free(). + (route_map_index_free): Function removed. + +2000-08-06 Gleb Natapov <gleb@nbase.co.il> + + * routemap.c (route_map_index_delete): Add check for route-map is + empty or not. + +2000-08-03 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zclient.c (zebra_ipv4_add): Change socket arguemnt with struct + zclient. + +2000-08-02 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zclient.h (struct zebra): Add obuf for output buffer. + + * if.c: Remove #ifdef NRL enclosing if_nametoindex() and + if_indextoname(). + +2000-08-02 David Lipovkov <davidl@nbase.co.il> + + * if.h (IF_PSEUDO_UNSET): IF_PSEUDO related macro added. + (IF_UNKNOWN_SET): IF_UNKNOWN related macro deleted. + + * if.c (interface_pseudo): Add "pseudo" command to interface node. + (no_interface_pseudo): Add "no pseudo" command to interface node. + + * zclient.c (zebra_interface_add_read): Set pseudo flag when it is + send from zebra. + +2000-08-01 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra.h (ZEBRA_IPV4_NEXTHOP_LOOKUP): Add new message. + (ZEBRA_IPV6_NEXTHOP_LOOKUP): Likewise. + + * vty.c (vty_serv_un): Use AF_UNIX for backward compatibility. + +2000-07-31 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c: Use vector for VTY server thread listing instead of + single value. + +2000-07-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * keychain.c (no_key_chain): "no key chain WORD" command is added. + +2000-07-29 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (config_from_file): If command fail in + KEYCHAIN_KEY_NODE, down to KEYCHAIN_NODE. + + * vty.h (struct vty ): Add index_sub member. + +2000-07-27 Akihiro Mizutani <mizutani@dml.com> + + * if.c: Help strings updates. + +2000-07-11 Akihiro Mizutani <mizutani@dml.com> + + * command.c (no_config_enable_password): Add "no enable password" + command. + (config_write_host): Display password string. + + * routemap.c (route_map_delete_match): Add support for delete + match without argument. + (route_map_delete_set): Likewise. + +2000-07-09 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (node_type ): Change KEYCHAIN_NODE and + KEYCHAIN_KEY_NODE place just before INTERFACE_NODE. + +2000-07-09 Jochen Friedrich <jochen@scram.de> + + * smux.c (config_write_smux): Fixes the option to override OID and + password for SMUX. + +2000-07-09 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (node_type ): Add SMUX_NODE for SMUX configuration. + +2000-07-09 Toshiaki Takada <takada@zebra.org> + + * command.c: Sort descvec command's help. + + * vty.c (vty_describe_command): Display '<cr>' at the end of + descriptions. + +2000-07-05 Toshiaki Takada <takada@zebra.org> + + * command.c (cmd_ipv6_match), (cmd_ipv6_prefix_match): Fix bug + treatment of double colon. + +2000-07-04 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zclient.h: Add zclient_redistribute_default_{set,unset}(). + + * keychain.c: New file for authentication key management. + * keychain.h: Likewise. + + * tcpfilter.c: New file for TCP/UDP base filtering using ipfw or + ipchains. + * tcpfilter.h: Likewise. + + * flap.h: New file for route flap dampening. + * flap.c: Likewise. + +2000-07-04 Toshiaki Takada <takada@zebra.org> + + * filter.c (struct filter): Add exact flag. + (access_list): Add exact-match command. + (ipv6_access_list): Add exact-match command. + +2000-07-03 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra.h (ZEBRA_REDISTRIBUTE_DEFAULT_ADD): New message for + request default route. + +2000-07-01 Hideaki YOSHIFUJI ($B5HF#1QL@(B) <yoshfuji@ecei.tohoku.ac.jp> + + * smux.c: Add IPv6 smux connection code. + +2000-06-15 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_complete_command): To cooperate readline library, + returned string is newly allocated. So some match function case + need, free of memory. + +2000-06-12 Akihiro Mizutani <mizutani@dml.com> + + * distribute.c: Fix help strings. + +2000-06-11 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (cmd_complete_command): Add check for vector_slot + (vline, index) is not NULL when calculating lcd. + (cmd_entry_function): First check variable arguemnt to prevent it + from completion. + +2000-06-10 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.h (struct vty ): Add output_count member for displaying + output route count. Remove arugment arg from output_func because + the value is passed by vty argument. Change output to output_rn. + Add output_clean function pointer member. Add output_type member. + +2000-06-10 Toshiaki Takada <takada@zebra.org> + + * command.c (show_startup_config): Add "show startup-config" + command. + +2000-06-06 Akihiro Mizutani <mizutani@dml.com> + + * filter.c: Fix help strings. + +2000-06-05 Kunihiro Ishiguro <kunihiro@zebra.org> + + * prefix.h (struct prefix_rd): New prefix structure for routing + distinguisher. + (struct prefix): Add padding to every prefix structure. + + + * routemap.c (route_map_add_match): When completely same match + statement exists, don't duplicate it. + +2000-06-05 Akihiro Mizutani <mizutani@dml.com> + + * routemap.c: Change NAME to WORD. + + * plist.c: Fix help strings. + +2000-06-02 Akihiro Mizutani <mizutani@dml.com> + + * routemap.c: Fix route-map help strings. + +2000-06-01 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (cmd_filter_by_completion): Fix CMD_VARARG treatment + to filter other non vararg commands. + + * routemap.c (route_map_init_vty): Use install_default() for + install common commands into route-map node.. + +2000-06-01 Akihiro Mizutani <mizutani@dml.com> + + * command.h (OSPF_STR): Macro added. + +2000-05-31 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (cmd_complete_command): LCD completion must not modify + installed command string. + + * plist.c (ipv6_prefix_list): Fix wrong syntax definition. Change + X:X::X:X to X:X::X:X/M. + +2000-05-31 Toshiaki Takada <takada@zebra.org> + + * vty.c (show_history): New defun added. + +2000-05-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (CMD_COMPLETE_LIST_MATCH): New define for completion + list. CMD_COMPLETE_MATCH is used for LCD completion. + + * vty.c (vty_complete_command): Matched string's LCD is completed. + + * command.c (cmd_lcd): New function for calculate LCD of matched + strings. + +2000-05-26 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (install_default): config_write_terminal_cmd, + config_write_file_cmd, config_write_memory_cmd are added to + default node. + + * memory.c (memory_init): Divide show memory command into each + sort. + + * command.c (cmd_init): config_write_terminal_cmd, + config_write_file_cmd, config_write_memory_cmd are added to + CONFIG_NODE. + + * routemap.c (route_map_index_free): New function. + (no_route_map_all): New DEFUN for "no route-map NAME". + + * filter.c (no_access_list_all): New DEFUN for delete access-list + with NAME. + (no_ipv6_access_list_all): Likewise. + +2000-05-23 Kunihiro Ishiguro <kunihiro@zebra.org> + + * plist.c: Change IPV6_PREFIX to X:X::X:X. When "any" is + specified, user can not use "ge" and "le" statement. + +2000-05-22 Thomas Molkenbur <tmo@datus.datus.com> + + * routemap.c (route_map_add_set): Fix bug of next pointer missing. + + * table.c (route_table_free): Like wise. + +2000-05-22 Toshiaki Takada <takada@zebra.org> + + * vty.c (vty_stop_input): Set history pointer to the latest one. + + * vty.c (vty_hist_add): Do not add command line history when input + is as same as previous one. + +2000-05-14 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): Add MTYPE_ECOMMUNITY and MTYPE_ECOMMUNITY_VAL. + +2000-05-13 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (node_type ): Add BGP_VPNV4_NODE. + +2000-05-08 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vtysh_accept): Add cast of struct sockaddr * to bind + argument. Reported by: Vesselin Mladenov <mladenov@netbg.com>. + + * filter.c (ipv6_access_list): Add IPv6 prefix example instead of + IPv4 example. Reported by: Love <lha@s3.kth.se>. + + * command.c (cmd_complete_command): Make it sure last element of + matchvec is NULL. This fix problem which cause crush in + vty_complete_command(). Reported by: JINMEI Tatuya + <jinmei@isl.rdc.toshiba.co.jp>. + +2000-04-28 Love <lha@s3.kth.se> + + * prefix.h (struct prefix): Add padding. + +2000-04-28 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (show_version): Update copyright year. + +2000-04-27 Kunihiro Ishiguro <kunihiro@zebra.org> + + * routemap.c (route_map_apply): When map is NULL, return deny. + +2000-04-26 Kunihiro Ishiguro <kunihiro@zebra.org> + + * filter.c (access_list_apply): When access is NULL, return deny. + + * plist.c (prefix_list_apply): When plist is NULL, return deny. + +2000-04-23 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (node_type ): Change RDISC_NODE to IRDP_NODE. + +2000-04-18 Toshiaki Takada <takada@zebra.org> + + * filter.[ch] (access_list_add_hook), (access_list_delete_hook): + Add argument for hook function to give struct access_list *. + +2000-04-17 Kunihiro Ishiguro <kunihiro@zebra.org> + + * plist.c (prefix_list_entry_match): In case of le nor ge is + specified, exact match is performed. + (prefix_list_entry_match): Add any entry matching check. + +2000-04-09 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (exec_timeout): Separate timeout setting to minutes and + seconds. + (no_exec_timeout): Add "no exec-timeout" command. + + * vty.h (VTY_TIMEOUT_DEFAULT): Change default value from 300 to + 600. + +2000-03-31 Jochen Friedrich <jochen@scram.de> + + * smux.h (SMUX_CLOSE): The SMUX_CLOSE PDU is implicit integer, so + it is a primitive encoding and not constructed. + +2000-03-28 Toshiaki Takada <takada@zebra.org> + + * memory.[ch] (enum): Add MTYPE_OSPF_EXTERNAL_INFO. + +2000-03-26 Love <lha@s3.kth.se> + + * zclient.c (zclient_read): Add nbytes size check for + ZEBRA_HEADER_SIZE. Check return value of steam_read (). + +2000-03-26 Rick Payne <rickp@rossfell.co.uk> + + * routemap.c: Add flexible route-map commands such as on-match + next, on-match goto N. + + * routemap.h: Likewise + +2000-03-23 Adrian Bool <aid@u.net.uk> + + * command.c (config_log_trap): Add new command "log trap + PRIORITY". + +2000-03-14 Toshiaki Takada <takada@zebra.org> + + * memory.c (struct memory_list): Add Link List and Link Node + to view. + + * memory.h (enum): Remove MTYPE_OSPF_EXTERNAL_ROUTE. + +2000-01-20 Hideto Yamakawa <hideto.yamakawa@soliton.co.jp> + + * str.c (snprintf): Fix bug of calling sprintf instead of + vsprintf. + +2000-01-16 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): Add MTYPE_RIP_PEER. + +2000-01-15 Toshiaki Takada <takada@zebra.org> + + * memory.h (enum): Add MTYPE_OSPF_CRYPT_KEY. + +2000-01-15 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (node_type ): Add MASC_NODE for masc. + +2000-01-09 Wang Jianliang <wangjl@soim.net> + + * routemap.c (route_map_index_add): When route_map_index is not + empty and insert new item at the head, it can cause core dump. + Fix "if (index == map->head)" to "if (point == map->head). + (route_map_add_set): If there is an old set command, override old + set command with new one. + (route_map_index_delete): Use while() instead of for for() for + logical correctness. + +1999-12-26 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): Add MTYPE_BGP_STATIC. + +1999-12-23 Alex Zinin <zinin@amt.ru> + * zebra.h, zclient.*: dynamic int up/down message + support + +1999-12-10 Kunihiro Ishiguro <kunihiro@zebra.org> + + * thread.c (thread_cancel_event): Add a function for clean up + events. + +1999-12-09 Kunihiro Ishiguro <kunihiro@zebra.org> + + * dropline.c: Delete file. + dropline.h: Linewise. + +1999-12-14 Kunihiro Ishiguro <kunihiro@zebra.org> + + * filter.c (access_list_filter_delete): Wrong pointer + access->master was pointed out after access is freed. I store + master value at the beginning of the function. + +1999-12-08 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (exec_timeout): Change of VTY timeout affect to current + VTY connection. + (vty_accept): Instead of immediate exit() return -1. + +1999-12-07 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_configure_lock): Configuration lock function added. + Only one VTY can use CONFI_NODE at the same time. + + * log.c: Delete zvlog_* functions. Now zlog_* does the same + thing. + + * log.c (log_init): Function removed. + (log_close): Likewise. + (log_flush): Likewise. + (log_open): Likewise. + + * vty.c (terminal_monitor): Add new command. + (no_terminal_monitor): Likewise. + + * log.c (old_log): Function removed. + (old_log2): Likewise. + (old_log_warn): Likewise. + +1999-12-04 Toshiaki Takada <takada@zebra.org> + + * command.c (cmd_ipv6_match): New function added. + (cmd_ipv6_prefix_match): Likewise. + +1999-12-04 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (cmd_ipv6_match): + + * table.c: Delete #ifdef HAVE_MBGPV4. + + * prefix.h (struct prefix): Add safi member. + (struct prefix_ipv4): Likewise. + (struct prefix_ipv6): Likewise. + +1999-12-04 Rumen Svobodnikov <rumen@linux.tu-varna.acad.bg> + + * memory.c (struct mstat): Revert to support MEMORY_LOG. + +1999-11-25 Kunihiro Ishiguro <kunihiro@zebra.org> + + * version.h: Bump up to 0.81c for testing new kernel codes. + +1999-11-21 Kunihiro Ishiguro <kunihiro@zebra.org> + + * thread.h (struct thread): Pthread support is disabled all + platform. + +1999-11-21 Michael Handler <handler@sub-rosa.com> + + * Include <limits.h> and <strings.h> under SUNOS_5. + +1999-11-21 Kunihiro Ishiguro <kunihiro@zebra.org> + + * sockunion.c (in6addr_cmp): Enclosed by #define HAVE_IPV6 +1999-11-13 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (node_type ): Add BGP_IPV4_NODE and BGP_IPV6_NODE. + +1999-11-12 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (disable): Add `disable' command. + +1999-11-09 Kunihiro Ishiguro <kunihiro@zebra.org> + + * plist.c (vty_prefix_list_install): Add any check. + +1999-11-04 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (node_type ): Add DUMP_NODE. + +1999-11-03 Kunihiro Ishiguro <kunihiro@zebra.org> + + * smux.c: Change default SMUX oid to compatible with gated. + +1999-10-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * if_rmap.c: New file added. + + * if_rmap.h: New file added. + +1999-10-29 Alex Zinin <zinin@amt.ru> + + * hash.c: add hash_free() function + +1999-10-25 Kunihiro Ishiguro <kunihiro@zebra.org> + + * hash.c (hash_clean): Add clean function. + + * plist.c (prefix_list_reset): Add reset function. + + * filter.c (access_list_reset): Add reset function. + +1999-10-17 Kunihiro Ishiguro <kunihiro@zebra.org> + + * client.c: Merged with zclient.c. + * client.h: Merged with zclient.h. + +1999-10-15 Jordan Mendelson <jordy@wserv.com> + + * md5.c: Imported from GNU C Library. + * md5-gnu.h: Likewise. + +1999-10-15 Jochen Friedrich <jochen@scram.de> + + * smux.c (smux_getresp_send): SMUX_GETRSP codes improvement. + +1999-10-06 Kunihiro Ishiguro <kunihiro@zebra.org> + + * smux.h: New file added. + + * snmp.c: Rename to smux.c. + +1999-10-02 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (cmd_execute_command_strict): Filter ambious commands. + (cmd_filter_by_string): Change to return enum match_type. + +1999-10-01 Toshiaki Takada <takada@zebra.org> + + * vty.c (vty_describe_fold): New function which does VTY + description line fold. + * vty.c (vty_describe_command): Set description column. + +1999-09-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * plist.c (prefix_list_init_ipv4): VTY user interface is improved. + +1999-09-26 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (cmd_filter_by_string): Fix bug of CMD_IPV4 and + CMD_IPV4_PREFIX check. Both return type must be exact_match. + +1999-09-24 Toshiaki Takada <takada@zebra.org> + + * command.c (cmd_filter_by_completion), + (is_cmd_ambiguous): Check IPv4 address, IPv4 prefix and range + parameter matches range. + +1999-09-22 Kunihiro Ishiguro <kunihiro@zebra.org> + + * routemap.c (route_map_apply): Returm RM_DENYMATCH when no match + is performed. + +1999-09-21 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_read): Control-C stop VTY_MORE mode. + +1999-09-20 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (node_type ): Add ACCESS_IPV6_NODE and + PREFIX_IPV6_NODE. + + * distribute.h: New file added. + + * command.h (node_type ): Delete DISTRIBUTE_NODE. + +1999-09-18 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_terminate_all): New function added for reload + support. + +1999-09-06 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): Add new type MTYPE_OSPF_EXTERNAL_ROUTE. + +1999-08-31 Janos Farkas <chexum@shadow.banki.hu> + + * vty.c (vty_read): Handle also 0x7f (alt-backspace), just like + esc-ctrl-h (delete word backwards). + +1999-08-24 Kunihiro Ishiguro <kunihiro@zebra.org> + + * if.h: Add if_nametoindex for NRL. + +1999-08-23 Kunihiro Ishiguro <kunihiro@zebra.org> + + * if.c (if_create): New function. + +1999-08-22 Kunihiro Ishiguro <kunihiro@zebra.org> + + * snmp.c: New file. + +1999-08-21 Kunihiro Ishiguro <kunihiro@zebra.org> + + * stream.c (stream_put): stream_memcpy () is changed to stream_put + (). stream_get () is added. + +1999-08-18 Toshiaki Takada <takada@zebra.org> + + * memory.h (enum): Add MTYPE_OSPF_LSA_DATA. + +1999-08-18 Yasuhiro Ohara <yasu@sfc.wide.ad.jp> + + * table.c (route_table_finish): add function frees table. + +1999-08-12 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): Add MTYPE_RTADV_PREFIX. + +1999-08-11 Kunihiro Ishiguro <kunihiro@zebra.org> + + * if.h (struct interface ): hw_address, hw_address_len added. + +1999-08-10 Kunihiro Ishiguro <kunihiro@zebra.org> + + * if.h (struct interface ): Change structure member if_data to + info, index to ifindex. + +1999-08-08 Rick Payne <rickp@rossfell.co.uk> + + * routemap.c: Multi protocol route-map modification. + + * routemap.c (route_map_apply): Route match process bug is fixed. + +1999-08-05 Kunihiro Ishiguro <kunihiro@zebra.org> + + * thread.c (thread_fetch): When signal comes, goto retry point. + +1999-08-04 Kunihiro Ishiguro <kunihiro@zebra.org> + + * Makefile.am: Add sockopt.c and sockopt.h + * sockopt.c: New file. + * sockopt.h: New file. + +1999-08-02 Kunihiro Ishiguro <kunihiro@zebra.org> + + * version.h (ZEBRA_VERSION): Release zebra-0.75 + +1999-08-01 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): Add MTYPE_RIPNG_AGGREGATE. + +1999-07-31 Kunihiro Ishiguro <kunihiro@zebra.org> + + * sockunion.h: Add sockunion_getpeername (). + +1999-07-27 Kunihiro Ishiguro <kunihiro@zebra.org> + + * version.h: Release zebra-0.74 + +1999-07-26 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (struct host): Delete lines from struct host. Add + lines to struct vty. + + * command.c: Delete `lines LINES'. Terminal display line settings + should be done by `terminal length' command. + +1999-07-24 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): MTYPE_OSPF_PATH are added. + +1999-07-22 Toshiaki Takada <takada@zebra.org> + + * memory.h (enum): MTYPE_OSPF_NEXTHOP is added. + +1999-07-21 Toshiaki Takada <takada@zebra.org> + + * linklist.c (list_add_node_prev), (list_add_node_next), + (list_add_list): New function added. + + * table.c (route_table_free): New function added. + +1999-07-21 Kunihiro Ishiguro <kunihiro@zebra.org> + + * plist.c (config_write_prefix): Set write flag when configuration + is written. + +1999-07-15 Yasuhiro Ohara <yasu@sfc.wide.ad.jp> + + * prefix.c : prefix_cmp() added. change apply_mask() to + apply_mask_ipv4(), and new apply_mask() added. + +1999-07-14 Yasuhiro Ohara <yasu@sfc.wide.ad.jp> + + * prefix.c (prefix2str): append prefixlen. + +1999-07-13 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (config_terminal): Change "config terminal" to + "configure terminal". Reported by Georg Hitsch + <georg@atnet.at>. + (config_terminal_length): `terminal length <0-512>' is added. At + this moment this command is only usef for vty interface. + Suggested by Georg Hitsch <georg@atnet.at>. + +1999-07-12 Kunihiro Ishiguro <kunihiro@zebra.org> + + * routemap.c (rulecmp): Add wrapper function of strcmp. + +1999-07-08 Rick Payne <rickp@rossfell.co.uk> + + * sockunion.c (inet_aton): Fix bug of inet_aton. + +1999-07-08 Kunihiro Ishiguro <kunihiro@zebra.org> + + * version.h (ZEBRA_VERSION): Start zebra-0.73 + +1999-07-06 Kunihiro Ishiguro <kunihiro@zebra.org> + + * version.h: Bump up to 0.72. + +1999-07-05 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (install_default): New function for install default + commands to the node. + + * memory.h (enum): MTYPE_NEXTHOP is added. + +1999-07-01 <kunihiro@zebra.org> + + * command.c (no_banner_motd): `no banner motd' command added. + +1999-06-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * regex.c: Update to glibc-2.1.1's posix/regex.c + + * regex-gnu.h: Update to glibc-2.1.1's posix/regex.h + + * prefix.h (IPV4_ADDR_SAME): Macro added. + (IPV6_ADDR_SAME): Likewise. + +1999-06-29 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): Add MTYPE_OSPF_VERTEX + + * version.h: Bump up to 0.71. + + * vty.c (vty_serv_sock_addrinfo): Use addrinfo function to bind + VTY socket when IPv6 is enabled. + +1999-06-28 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_serv_sock): Change vty_serv_sock determine which + address family to bind. + + * command.c: Add quit command. + +1999-06-26 NOGUCHI kay <kay@dti.ad.jp> + + * vty.c (vty_read_config): Fix bug of configuration file path + detection. + +1999-06-25 Kunihiro Ishiguro <kunihiro@zebra.org> + + * version.h: Bump up to 0.70. + +1999-06-17 Kunihiro Ishiguro <kunihiro@zebra.org> + + * buffer.h (GETL): Remove GETL macro. + + * version.h: Bump up to 0.69. + +1999-06-14 Kunihiro Ishiguro <kunihiro@zebra.org> + + * if.c (connected_add): Commented out connected_log. + +1999-06-13 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (struct cmd_element ): strvec and descvec is combined + into newstrvec. + + * command.c (desc_make): Function removed. + (desc_next): Function removed. + + * command.h (struct cmd_element ): docvec is removed from struct + cmd_element. + +1999-06-12 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (cmd_execute_command): Remove command NULL check. + + * command.h (struct cmd_element ): Add newstrvec entry to struct + cmd_element. + (DEFUN2): DEFUN2 macro is removed. DEFUN is extended to support + (a|b|c) statement. + (DESC): DESC macro is removed. + + * vty.c (vty_complete_command): When return value is + CMD_ERR_NO_MATCH, don't display error message. + +1999-06-08 Kunihiro Ishiguro <kunihiro@zebra.org> + + * table.c (route_next_until): New function. + + * version.h: Bump up to 0.68. + +1999-06-06 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_close): Free vty->buf when vty is closed. + + * memory.h (enum): Add MTYPE_COMMUNITY_ENTRY and + MTYPE_COMMUNITY_LIST. + + * vty.h (struct vty ): Change buf from static length buffer to + variable length buffer. + + * vty.c (vty_ensure): New function added. + +1999-06-04 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (node_type ): Add COMMUNITY_LIST_NODE. + + * command.c (config_enable_password): Freeing host.enable bug is + fixed. + (config_enable_password): Add argc count check. + +1999-05-31 Kunihiro Ishiguro <kunihiro@zebra.org> + + * version.h: Bump up to 0.67. + +1999-05-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (zencrypt): New function for encrypt password. + + * command.h (struct host): Add password_encrypt and + enable_encrypt. + +1999-05-30 Jochen Friedrich <jochen@scram.de> + + * command.h (struct host): New member encrypt is added for + encrypted password. + +1999-05-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c: Remove all_digit_check function. Instead use all_digit. + + * prefix.c (all_digit): New function for checking string is made + from digit character. + +1999-05-25 Kunihiro Ishiguro <kunihiro@zebra.org> + + * Makefile.am (libzebra_a_SOURCES): Add zclient.c. + (noinst_HEADERS): Add zclient.h + + * zclient.[ch]: New file for zebra client routine. + + * memory.h (enum): Add MTYPE_ZEBRA. + +1999-05-19 Kunihiro Ishiguro <kunihiro@zebra.org> + + * version.h (ZEBRA_VERSION): Update to 0.66. + +1999-05-15 Kunihiro Ishiguro <kunihiro@zebra.org> + + * buffer.h (GETC,GETW): Macro deleted. + +1999-05-15 Carlos Alberto Barcenilla <barce@frlp.utn.edu.ar> + + * prefix.h (IPV4_NET0, IPV4_NET127): Macro added. + +1999-05-15 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (service_advanced_vty): New command added. + (no_service_advanced_vty): Likewise. + +1999-05-14 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_auth): If advanced flag is set and enable password is + not set, directly login to the ENABLE_NODE. This feature is + originally designed and implemented by Stephen R. van den Berg + <srb@cuci.nl>. + + * command.h (host): Add advanced flag to struct host for advanced + vty terminal interface. + + * version.h (ZEBRA_VERSION): Update to 0.65 for next beta release. + +1999-05-14 Stephen R. van den Berg <srb@cuci.nl> + + * command.h (node_type ): Add TABLE_NODE. + + * vty.c (vty_telnet_option): Check host.lines value. + + * command.c (config_lines): DEFUN for 'lines LINES' command. + + * zebra.h: Include <sys/utsname.h> for uname(). + (RT_TABLE_MAIN): Defined as 0 if OS does not support multiple + routing table. + + * vty.c (vty_auth): Directly login to the ENABLE_NODE when enable + password is not set. + (vty_prompt): Get machine's hostname when hostname is not set. + +1999-05-11 James Willard <james@whispering.org> + + * command.c (config_exit): Close connection when `exit' command is + executed at ENABLE_NODE. + +1999-05-10 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_stop_input): `C-c' key change node to ENABLE_NODE. + + * command.c (cmd_execute_command_strict): Matched command size + check added. + (cmd_make_desc_line): New function for DEFUN2. + + * command.h (struct cmd_element ): Add descsize. + +1999-05-09 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (struct cmd_element ): Remame descvec to docvec. + (struct cmd_element ): Add descvec for new description system. + + * command.c (desc_make): Check cmd->descvec. + +1999-05-06 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): Add MTYPE_CLUSTER, MTYPE_CLUSTER_VAL. + +1999-05-05 Kunihiro Ishiguro <kunihiro@zebra.org> + + * version.h (ZEBRA_VERSION): Bump up to 0.64 for next beta + release. + +1999-05-04 Yasuhiro Ohara <yasu@sfc.wide.ad.jp> + + * linklist.c (list_delete_all_node): bug fix. + previous code loses current position when node + is deleted. + +1999-05-03 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (DESC): Macro added. + (struct cmd_element2): Delete struct cmd_element2. + + * plist.c (prefix_list): Sequential number option check is added. + +1999-05-02 Yasuhiro Ohara <yasu@sfc.wide.ad.jp> + + * log.c (zvlog_{debug,info,notice,warn,err}): have been + added. now we can log both console and file, but still + need some fix about config write. + +1999-05-02 Kunihiro Ishiguro <kunihiro@zebra.org> + + * log.c (zvlog_debug): Fix yasu's change. + +1999-05-01 Kunihiro Ishiguro <kunihiro@zebra.org> + + * plist.c (prefix_list): Fix typo. + +1999-04-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * Set version to 0.63 for first beta package. + +1999-04-27 Carlos Barcenilla <barce@frlp.utn.edu.ar> + + * prefix.c (str2prefix_ipv4): Fix prefix length check. + (str2prefix_ipv6): Likewise. + +1999-04-25 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): Add MTPYE_PREFIX_LIST and + MTYPE_PREFIX_LIST_ENTRY. + + * command.h (node_type ): Add PREFIX_NODE. + +1999-04-25 Carlos Barcenilla <barce@frlp.utn.edu.ar> + + * command.c: ALIAS (config_write_memory_cmd) and ALIAS + (copy_runningconfig_startupconfig_cmd) is added. + + * table.c (route_node_lookup): Unused match variable deletion. + +1999-04-24 Kunihiro Ishiguro <kunihiro@zebra.org> + + * Makefile.am (libzebra_a_SOURCES): plist.c added. + (noinst_HEADERS): plist.h added. + + * plist.c, plist.h: New file added. + + * memory.h (enum): Rename MTYPE_AS_PASN to MTYPE_AS_STR. + * memory.c: Likewise. + +1999-04-19 Carlos Alberto Barcenilla <barce@frlp.utn.edu.ar> + + * command.c (show_version): `show version' command added. + +1999-04-19 Kunihiro Ishiguro <kunihiro@zebra.org> + + * prefix.c (str2prefix_ipv6): Prefix length overflow check. + +1999-04-19 Carlos Alberto Barcenilla <barce@frlp.utn.edu.ar> + + * prefix.c (str2prefix_ipv4): Prefix length overflow check. + +1999-04-19 Alex Bligh <amb@gxn.net> + + * prefix.c (sockunion2hostprefix): Function added. + (sockunion2prefix): Address family was not set. Now it is set. + + * vty.c: VTY access-class command is added. + +1999-04-18 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.c: Change xmalloc to zmalloc. xcalloc, xrealloc, xfree, + xstrdup are likewise. + +1999-04-18 Yasuhiro Ohara <yasu@sfc.wide.ad.jp> + + * thread.c: Add thread_execute for other routing daemon. + OSPF tasks need to be generated by "sheduled" and "executed". + +1999-04-13 Kunihiro Ishiguro <kunihiro@zebra.org> + + * buffer.c: Rewrite buffer_write and buffer_flush related + functions for fixing bugs. Reason of the problem and fix is + suggested by Alex Bligh <amb@gxn.net>. + +1999-04-12 Alex Bligh <amb@gxn.net> + + * command.c (cmd_entry_function_descr): Added for variable + argument help display. + +1999-04-07 Kunihiro Ishiguro <kunihiro@zebra.org> + + * regex.c, regex-gnu.h: Imported from GNU sed-3.02 distribution. + +1999-03-24 Kunihiro Ishiguro <kunihiro@zebra.org> + + * stream.c: stream_fifo_free bug is fixed. + +1999-03-19 Toshiaki Takada <takada@zebra.org> + + * stream.c (stream_strncpy): Added for getting any length bytes + from stream. + +1999-03-16 Kunihiro Ishiguro <kunihiro@zebra.org> + + * version.h (ZEBRA_BUG_ADDRESS): New macro added. + +1999-03-14 Kunihiro Ishiguro <kunihiro@zebra.org> + + * buffer.c (buffer_flush_window): If ep is same as buffer's size + length and lp is overrun one octet. + +1999-03-13 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.h: add VTY's timeout function. + +1999-03-05 <kunihiro@zebra.org> + + * command.h (node_type ): Add OSPF6_node. + +1999-03-04 Kunihiro Ishiguro <kunihiro@zebra.org> + + * zebra.h: Check HAVE_SYS_SELECT_H when include <sys/select.h> + +1999-03-03 Jeroen Ruigrok/Asmodai <asmodai@wxs.nl> + + * zebra.h: Include <net/if_var.h> if it exists. + +1999-03-02 Kunihiro Ishiguro <kunihiro@zebra.org> + + * getopt.[ch],getopt1.c: Sync with glibc-2.1. + + * log.c (zlog): Tempolary ZLOG_STDOUT feature added. + + * command.h: Include vector.h and vty.h + +1999-02-25 Kunihiro Ishiguro <kunihiro@zebra.org> + + * routemap.h (struct route_map_rule_cmd): Add prefix arguemnt. + + * routemap.c (route_map_apply_index): Add prefix argument. + (route_map_apply): Likewise. + + * memory.h (enum): Add MTYPE_ROUTE_MAP_COMPILED. + + * stream.c: Add stream_fifo related functions. + +1999-02-24 Kunihiro Ishiguro <kunihiro@zebra.org> + + * daemon.c: Return integer value. File descriptor close is added. + + * memory.h (enum): add MTYPE_OSPF_LSA. + +1999-02-23 Kunihiro Ishiguro <kunihiro@zebra.org> + + * rsh.c: Remove empty file. + +1999-02-22 <kunihiro@zebra.org> + + * routemap.c: Add add/delete hook to route_map_master. + +1999-02-19 Peter Galbavy <Peter.Galbavy@knowledge.com> + + * str.[ch] added to supply wrappers for snprintf(), strlcat() and + strlcpy on system without these. + +1999-02-18 Peter Galbavy <Peter.Galbavy@knowledge.com> + + * syslog support added + +1999-02-02 Kunihiro Ishiguro <kunihiro@zebra.org> + + * filter.c (access_list_add_hook): added for hook function management. + * filter.c (access_list_delete_hook): Likewise. + +1999-01-19 Kunihiro Ishiguro <kunihiro@zebra.org> + + * stream.c: New file. + * stream.h: New file. + * Divide stream related fucntions from buffer.[ch] into stream.[ch]. + +1999-01-14 Kunihiro Ishiguro <kunihiro@zebra.org> + + * memory.h (enum): add MTYPE_STREAM, MTYPE_STREAM_DATA + + * buffer.c (stream_new): Set MTYPE_STREAM to XMALLOC argument. + +1998-12-23 Kunihiro Ishiguro <kunihiro@zebra.org> + + * routemap.c: route_map_index_delete() added. + +1998-12-22 Kunihiro Ishiguro <kunihiro@zebra.org> + + * buffer.c (buffer_empty): check cp instead of sp. + +1998-12-17 Kunihiro Ishiguro <kunihiro@zebra.org> + + * radix.[ch]: Deleted. + +1998-12-15 Magnus Ahltorp <map@stacken.kth.se> + + * buffer.c: Prototype fixes. + * prefix.c: Likewise. + * sockunion.c: Likewise. + * sockunion.h: Likewise. + +1998-12-14 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_read): DELETE key works as vty_delete_char. + +1998-12-13 Kunihiro Ishiguro <kunihiro@zebra.org> + + * log.c (time_print): chane %y to %Y. + +1998-12-10 Kunihiro Ishiguro <kunihiro@zebra.org> + + * distribute.c: new file. + +1998-12-09 Kunihiro Ishiguro <kunihiro@zebra.org> + + * filter.c: Remove all of struct prefix_{ipv4,ipv6} and add + complete support of IPv6 access list. + + * command.c (config_write_element): function delete. + (config_write_host): function add. password and enable password + isn't printed to vty interface. + +1998-12-08 Kunihiro Ishiguro <kunihiro@zebra.org> + + * filter.c: Change prefix_ipv4 to prefix and add support of + prefix_ipv6 filtering. + +1998-12-07 Kunihiro Ishiguro <kunihiro@zebra.org> + + * Makefile.am (INCLUDES): add @INCLUDES@ for Linux IPv6 inet6-apps + header includes. + +1998-12-05 Kunihiro Ishiguro <kunihiro@zebra.org> + + * log.c (log_flush): fix function name typo. + +1998-12-04 Yasuhiro Ohara <yasu@sfc.wide.ad.jp> + + * memory.h: OSPF memory type is added. + +1998-11-15 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (sort_node): add sort_node() for pretty printing of + command on vty interface. + (config_password): delete the restriction of charaster of password + string. + +1998-09-05 Kunihiro Ishiguro <kunihiro@debian.zebra.org> + + * prefix.c (prefix_ipv4_any): add prefix_ipv4_any(). + +1998-08-25 Kunihiro Ishiguro <kunihiro@zebra.org> + + * network.h: New file. + +1998-08-24 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_will_echo): function name change from vty_off_echo. + +1998-08-18 Kunihiro Ishiguro <kunihiro@zebra.org> + + * buffer.h: add PUTC,PUTW,PUTL macros. + +1998-07-22 Kunihiro Ishiguro <kunihiro@zebra.org> + + * route.[ch]: renamed to prefix.[ch] + +1998-06-09 Kunihiro Ishiguro <kunihiro@zebra.org> + + * prefix_in, prefix_in6 is replaced by prefix_ipv4, prefix_ipv6. + + * Makefile.am: @INCLUDES@ is deleted from INCLUDES. + +1998-06-07 Kunihiro Ishiguro <kunihiro@zebra.org> + + * host.[ch]: merged with command.[ch] + +1998-05-08 Kunihiro Ishiguro <kunihiro@zebra.org> + + * Makefile.am (libzebra_a_SOURCES): add route.c to libzebra_a_SOURCES. + +1998-05-07 Kunihiro Ishiguro <kunihiro@zebra.org> + + * route.c (str2prefix): str2prefix () is gone. + +1998-05-03 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_read_config): change CONDIR to SYSCONFDIR. + + * .cvsignore: add file. + + * memory.c (xerror): add arguent `type' and `size'. + + * socket.c: deleted. + +1998-05-02 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vector.c: malloc,free,realloc -> XMALLOC,XFREE,XREALLOC. + * linklist.c: same as above. + +1998-04-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * filter.[ch]: added. + +1998-04-01 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (config_who): return CMD_SUCCESS + +1998-04-01 Jochen Friedrich <jochen@scram.de> + + * table.c (route_dump_node): route_dump_node is IPv6 specific + function so move #ifdef to the end of route_dump_node (). + +1998-03-05 "Hannes R. Boehm" <hannes@boehm.org> + + * if.c: DEFUN(interface_desc) added. + +1998-03-05 Kunihiro Ishiguro <kunihiro@zebra.org> + + * if.c: separated from ripd/rip_interface.c + +1998-03-04 Kunihiro Ishiguro <kunihiro@zebra.org> + + * thread.[ch] : added. + +1998-02-14 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_delete_char): fix size bug. + (vty_backward_pure_word): function added. + (vty_read): ESC + 'f' perform vty_forward_word. + (vty_read): ESC + 'b' perform vty_backward_word. + +1998-02-11 Kunihiro Ishiguro <kunihiro@zebra.org> + + * radix.c (radix_lookup_rt): add mask check. + (radix_delete_duproute): add mask check. + +1998-02-10 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (config_write_file): fix vty -> file_vty. + +1998-02-06 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (cmd_filter_ambiguous): add complex type treatment. + +1998-02-05 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c (vty_time_print): function added. + (vty_complete_command): now [...] element isn't shown by completion. + +1998-01-26 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c : change from cmd_install_node() to install_node(). + +1998-01-16 Kunihiro Ishiguro <kunihiro@zebra.org> + + * route.[ch]: struct rt{} is replaced by struct prefix{}. + +1998-01-06 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.c (cmd_execute_command): check command length. + + * timer.c (zebra_timer_set): add zebra_timer_set. + +1998-01-05 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.h (node_type ): add ZEBRA_NODE. + + * command.c (config_exit): add RIP_NODE. + (config_write_file): add RIP_NODE. + +1998-01-04 Kunihiro Ishiguro <kunihiro@zebra.org> + + * print_version.c (print_version): Now Copyright is 1996-1998. + + * sockunion.c (sockunion_log): moved from ../zebra/route.c + +1997-12-30 Kunihiro Ishiguro <kunihiro@zebra.org> + + * host.c (config_logfile): change 'log PATH' to 'logfile PATH'. + + * sockunion.c (sockunion_sameprefix): add same prefix for + sockunion. + +1997-12-29 Kunihiro Ishiguro <kunihiro@zebra.org> + + * radix.[ch] : are moved from ../zebra directroy. + + * command.c (config_from_file): if command execution failed down + level to CONFIG_NODE. + + * host.c: config_log function which enable 'log FILENAME' command. + +1997-12-23 Kunihiro Ishiguro <kunihiro@zebra.org> + + * vty.c: add vty_transpose_chars (). Now you can use '^T' to + transpose character. + + * command.c: cmd_cmdsize add, this is useful to check incomplete + command. + +1997-12-07 Kunihiro Ishiguro <kunihiro@zebra.org> + + * fd.h: add family for address family + +1997-12-06 Kunihiro Ishiguro <kunihiro@zebra.org> + + * command.o + * vty.o + * host.o is moved from ../zebra + +1997-08-14 Kunihiro Ishiguro <kunihiro@zebra.org> + + * make library directory. + diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 00000000..81f1e41e --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,29 @@ +## Process this file with automake to produce Makefile.in. + +INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" + +noinst_LIBRARIES = libzebra.a + +libzebra_a_SOURCES = \ + version.c network.c pid_output.c getopt.c getopt1.c daemon.c \ + print_version.c checksum.c vector.c linklist.c vty.c command.c \ + sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \ + filter.c routemap.c distribute.c stream.c str.c log.c plist.c \ + zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c + +libzebra_a_DEPENDENCIES = @LIB_REGEX@ + +libzebra_a_LIBADD = @LIB_REGEX@ + +noinst_HEADERS = \ + buffer.h command.h filter.h getopt.h hash.h if.h linklist.h log.h \ + memory.h network.h prefix.h routemap.h distribute.h sockunion.h \ + str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \ + plist.h zclient.h sockopt.h smux.h md5-gnu.h if_rmap.h keychain.h + +EXTRA_DIST = regex.c regex-gnu.h + +version.c: Makefile + echo '' >version.c + echo 'char *host_name = "$(host_alias)";' >>version.c diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 00000000..d821f238 --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,469 @@ +# Makefile.in generated by automake 1.7 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_triplet = @host@ +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BGPD = @BGPD@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CURSES = @CURSES@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +IF_METHOD = @IF_METHOD@ +IF_PROC = @IF_PROC@ + +INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPFORWARD = @IPFORWARD@ +KERNEL_METHOD = @KERNEL_METHOD@ +LDFLAGS = @LDFLAGS@ +LIBPAM = @LIBPAM@ +LIBS = @LIBS@ +LIB_IPV6 = @LIB_IPV6@ +LIB_REGEX = @LIB_REGEX@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MULTIPATH_NUM = @MULTIPATH_NUM@ +OBJEXT = @OBJEXT@ +OSPF6D = @OSPF6D@ +OSPFD = @OSPFD@ +OTHER_METHOD = @OTHER_METHOD@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RIPD = @RIPD@ +RIPNGD = @RIPNGD@ +RTREAD_METHOD = @RTREAD_METHOD@ +RT_METHOD = @RT_METHOD@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +VTYSH = @VTYSH@ +ZEBRA = @ZEBRA@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__include = @am__include@ +am__quote = @am__quote@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ + +noinst_LIBRARIES = libzebra.a + +libzebra_a_SOURCES = \ + version.c network.c pid_output.c getopt.c getopt1.c daemon.c \ + print_version.c checksum.c vector.c linklist.c vty.c command.c \ + sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \ + filter.c routemap.c distribute.c stream.c str.c log.c plist.c \ + zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c + + +libzebra_a_DEPENDENCIES = @LIB_REGEX@ + +libzebra_a_LIBADD = @LIB_REGEX@ + +noinst_HEADERS = \ + buffer.h command.h filter.h getopt.h hash.h if.h linklist.h log.h \ + memory.h network.h prefix.h routemap.h distribute.h sockunion.h \ + str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \ + plist.h zclient.h sockopt.h smux.h md5-gnu.h if_rmap.h keychain.h + + +EXTRA_DIST = regex.c regex-gnu.h +subdir = lib +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + +libzebra_a_AR = $(AR) cru +am_libzebra_a_OBJECTS = version.$(OBJEXT) network.$(OBJEXT) \ + pid_output.$(OBJEXT) getopt.$(OBJEXT) getopt1.$(OBJEXT) \ + daemon.$(OBJEXT) print_version.$(OBJEXT) checksum.$(OBJEXT) \ + vector.$(OBJEXT) linklist.$(OBJEXT) vty.$(OBJEXT) \ + command.$(OBJEXT) sockunion.$(OBJEXT) prefix.$(OBJEXT) \ + thread.$(OBJEXT) if.$(OBJEXT) memory.$(OBJEXT) buffer.$(OBJEXT) \ + table.$(OBJEXT) hash.$(OBJEXT) filter.$(OBJEXT) \ + routemap.$(OBJEXT) distribute.$(OBJEXT) stream.$(OBJEXT) \ + str.$(OBJEXT) log.$(OBJEXT) plist.$(OBJEXT) zclient.$(OBJEXT) \ + sockopt.$(OBJEXT) smux.$(OBJEXT) md5.$(OBJEXT) \ + if_rmap.$(OBJEXT) keychain.$(OBJEXT) +libzebra_a_OBJECTS = $(am_libzebra_a_OBJECTS) + +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/buffer.Po ./$(DEPDIR)/checksum.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/command.Po ./$(DEPDIR)/daemon.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/distribute.Po ./$(DEPDIR)/filter.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/getopt.Po ./$(DEPDIR)/getopt1.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/hash.Po ./$(DEPDIR)/if.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/if_rmap.Po ./$(DEPDIR)/keychain.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/linklist.Po ./$(DEPDIR)/log.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/md5.Po ./$(DEPDIR)/memory.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/network.Po ./$(DEPDIR)/pid_output.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/plist.Po ./$(DEPDIR)/prefix.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/print_version.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/routemap.Po ./$(DEPDIR)/smux.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/sockopt.Po ./$(DEPDIR)/sockunion.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/str.Po ./$(DEPDIR)/stream.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/table.Po ./$(DEPDIR)/thread.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/vector.Po ./$(DEPDIR)/version.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/vty.Po ./$(DEPDIR)/zclient.Po +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libzebra_a_SOURCES) +HEADERS = $(noinst_HEADERS) + +DIST_COMMON = $(noinst_HEADERS) ChangeLog Makefile.am Makefile.in +SOURCES = $(libzebra_a_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign lib/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libzebra.a: $(libzebra_a_OBJECTS) $(libzebra_a_DEPENDENCIES) + -rm -f libzebra.a + $(libzebra_a_AR) libzebra.a $(libzebra_a_OBJECTS) $(libzebra_a_LIBADD) + $(RANLIB) libzebra.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/checksum.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/distribute.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/if.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/if_rmap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keychain.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linklist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/network.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pid_output.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print_version.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/routemap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smux.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sockopt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sockunion.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/table.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vector.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vty.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zclient.Po@am__quote@ + +distclean-depend: + -rm -rf ./$(DEPDIR) + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" \ +@am__fastdepCC_TRUE@ -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$<; \ +@am__fastdepCC_TRUE@ then mv "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; \ +@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; \ +@am__fastdepCC_TRUE@ fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `test -f '$<' || echo '$(srcdir)/'`$< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" \ +@am__fastdepCC_TRUE@ -c -o $@ `if test -f '$<'; then $(CYGPATH_W) '$<'; else $(CYGPATH_W) '$(srcdir)/$<'`; \ +@am__fastdepCC_TRUE@ then mv "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; \ +@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; \ +@am__fastdepCC_TRUE@ fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `if test -f '$<'; then $(CYGPATH_W) '$<'; else $(CYGPATH_W) '$(srcdir)/$<'` +uninstall-info-am: + +ETAGS = etags +ETAGSFLAGS = + +CTAGS = ctags +CTAGSFLAGS = + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique + +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) $(HEADERS) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES ctags distclean distclean-compile \ + distclean-depend distclean-generic distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-info-am + + +version.c: Makefile + echo '' >version.c + echo 'char *host_name = "$(host_alias)";' >>version.c +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/buffer.c b/lib/buffer.c new file mode 100644 index 00000000..de51ee3e --- /dev/null +++ b/lib/buffer.c @@ -0,0 +1,568 @@ +/* + * Buffering of output and input. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "memory.h" +#include "buffer.h" + +/* Make buffer data. */ +struct buffer_data * +buffer_data_new (size_t size) +{ + struct buffer_data *d; + + d = XMALLOC (MTYPE_BUFFER_DATA, sizeof (struct buffer_data)); + memset (d, 0, sizeof (struct buffer_data)); + d->data = XMALLOC (MTYPE_BUFFER_DATA, size); + + return d; +} + +void +buffer_data_free (struct buffer_data *d) +{ + if (d->data) + XFREE (MTYPE_BUFFER_DATA, d->data); + XFREE (MTYPE_BUFFER_DATA, d); +} + +/* Make new buffer. */ +struct buffer * +buffer_new (size_t size) +{ + struct buffer *b; + + b = XMALLOC (MTYPE_BUFFER, sizeof (struct buffer)); + memset (b, 0, sizeof (struct buffer)); + + b->size = size; + + return b; +} + +/* Free buffer. */ +void +buffer_free (struct buffer *b) +{ + struct buffer_data *d; + struct buffer_data *next; + + d = b->head; + while (d) + { + next = d->next; + buffer_data_free (d); + d = next; + } + + d = b->unused_head; + while (d) + { + next = d->next; + buffer_data_free (d); + d = next; + } + + XFREE (MTYPE_BUFFER, b); +} + +/* Make string clone. */ +char * +buffer_getstr (struct buffer *b) +{ + return strdup ((char *)b->head->data); +} + +/* Return 1 if buffer is empty. */ +int +buffer_empty (struct buffer *b) +{ + if (b->tail == NULL || b->tail->cp == b->tail->sp) + return 1; + else + return 0; +} + +/* Clear and free all allocated data. */ +void +buffer_reset (struct buffer *b) +{ + struct buffer_data *data; + struct buffer_data *next; + + for (data = b->head; data; data = next) + { + next = data->next; + buffer_data_free (data); + } + b->head = b->tail = NULL; + b->alloc = 0; + b->length = 0; +} + +/* Add buffer_data to the end of buffer. */ +void +buffer_add (struct buffer *b) +{ + struct buffer_data *d; + + d = buffer_data_new (b->size); + + if (b->tail == NULL) + { + d->prev = NULL; + d->next = NULL; + b->head = d; + b->tail = d; + } + else + { + d->prev = b->tail; + d->next = NULL; + + b->tail->next = d; + b->tail = d; + } + + b->alloc++; +} + +/* Write data to buffer. */ +int +buffer_write (struct buffer *b, u_char *ptr, size_t size) +{ + struct buffer_data *data; + + data = b->tail; + b->length += size; + + /* We use even last one byte of data buffer. */ + while (size) + { + /* If there is no data buffer add it. */ + if (data == NULL || data->cp == b->size) + { + buffer_add (b); + data = b->tail; + } + + /* Last data. */ + if (size <= (b->size - data->cp)) + { + memcpy ((data->data + data->cp), ptr, size); + + data->cp += size; + size = 0; + } + else + { + memcpy ((data->data + data->cp), ptr, (b->size - data->cp)); + + size -= (b->size - data->cp); + ptr += (b->size - data->cp); + + data->cp = b->size; + } + } + return 1; +} + +/* Insert character into the buffer. */ +int +buffer_putc (struct buffer *b, u_char c) +{ + buffer_write (b, &c, 1); + return 1; +} + +/* Insert word (2 octets) into ther buffer. */ +int +buffer_putw (struct buffer *b, u_short c) +{ + buffer_write (b, (char *)&c, 2); + return 1; +} + +/* Put string to the buffer. */ +int +buffer_putstr (struct buffer *b, u_char *c) +{ + size_t size; + + size = strlen ((char *)c); + buffer_write (b, c, size); + return 1; +} + +/* Flush specified size to the fd. */ +void +buffer_flush (struct buffer *b, int fd, size_t size) +{ + int iov_index; + struct iovec *iovec; + struct buffer_data *data; + struct buffer_data *out; + struct buffer_data *next; + + iovec = malloc (sizeof (struct iovec) * b->alloc); + iov_index = 0; + + for (data = b->head; data; data = data->next) + { + iovec[iov_index].iov_base = (char *)(data->data + data->sp); + + if (size <= (data->cp - data->sp)) + { + iovec[iov_index++].iov_len = size; + data->sp += size; + if (data->sp == data->cp) + data = data->next; + break; + } + else + { + iovec[iov_index++].iov_len = data->cp - data->sp; + size -= data->cp - data->sp; + data->sp = data->cp; + } + } + + /* Write buffer to the fd. */ + writev (fd, iovec, iov_index); + + /* Free printed buffer data. */ + for (out = b->head; out && out != data; out = next) + { + next = out->next; + if (next) + next->prev = NULL; + else + b->tail = next; + b->head = next; + + buffer_data_free (out); + b->alloc--; + } + + free (iovec); +} + +/* Flush all buffer to the fd. */ +int +buffer_flush_all (struct buffer *b, int fd) +{ + int ret; + struct buffer_data *d; + int iov_index; + struct iovec *iovec; + + if (buffer_empty (b)) + return 0; + + iovec = malloc (sizeof (struct iovec) * b->alloc); + iov_index = 0; + + for (d = b->head; d; d = d->next) + { + iovec[iov_index].iov_base = (char *)(d->data + d->sp); + iovec[iov_index].iov_len = d->cp - d->sp; + iov_index++; + } + ret = writev (fd, iovec, iov_index); + + free (iovec); + + buffer_reset (b); + + return ret; +} + +/* Flush all buffer to the fd. */ +int +buffer_flush_vty_all (struct buffer *b, int fd, int erase_flag, + int no_more_flag) +{ + int nbytes; + int iov_index; + struct iovec *iov; + struct iovec small_iov[3]; + char more[] = " --More-- "; + char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; + struct buffer_data *data; + struct buffer_data *out; + struct buffer_data *next; + + /* For erase and more data add two to b's buffer_data count.*/ + if (b->alloc == 1) + iov = small_iov; + else + iov = XCALLOC (MTYPE_TMP, sizeof (struct iovec) * (b->alloc + 2)); + + data = b->head; + iov_index = 0; + + /* Previously print out is performed. */ + if (erase_flag) + { + iov[iov_index].iov_base = erase; + iov[iov_index].iov_len = sizeof erase; + iov_index++; + } + + /* Output data. */ + for (data = b->head; data; data = data->next) + { + iov[iov_index].iov_base = (char *)(data->data + data->sp); + iov[iov_index].iov_len = data->cp - data->sp; + iov_index++; + } + + /* In case of `more' display need. */ + if (! buffer_empty (b) && !no_more_flag) + { + iov[iov_index].iov_base = more; + iov[iov_index].iov_len = sizeof more; + iov_index++; + } + + /* We use write or writev*/ + nbytes = writev (fd, iov, iov_index); + + /* Error treatment. */ + if (nbytes < 0) + { + if (errno == EINTR) + ; + if (errno == EWOULDBLOCK) + ; + } + + /* Free printed buffer data. */ + for (out = b->head; out && out != data; out = next) + { + next = out->next; + if (next) + next->prev = NULL; + else + b->tail = next; + b->head = next; + + buffer_data_free (out); + b->alloc--; + } + + if (iov != small_iov) + XFREE (MTYPE_TMP, iov); + + return nbytes; +} + +/* Flush buffer to the file descriptor. Mainly used from vty + interface. */ +int +buffer_flush_vty (struct buffer *b, int fd, int size, + int erase_flag, int no_more_flag) +{ + int nbytes; + int iov_index; + struct iovec *iov; + struct iovec small_iov[3]; + char more[] = " --More-- "; + char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; + struct buffer_data *data; + struct buffer_data *out; + struct buffer_data *next; + +#ifdef IOV_MAX + int iov_size; + int total_size; + struct iovec *c_iov; + int c_nbytes; +#endif /* IOV_MAX */ + + /* For erase and more data add two to b's buffer_data count.*/ + if (b->alloc == 1) + iov = small_iov; + else + iov = XCALLOC (MTYPE_TMP, sizeof (struct iovec) * (b->alloc + 2)); + + data = b->head; + iov_index = 0; + + /* Previously print out is performed. */ + if (erase_flag) + { + iov[iov_index].iov_base = erase; + iov[iov_index].iov_len = sizeof erase; + iov_index++; + } + + /* Output data. */ + for (data = b->head; data; data = data->next) + { + iov[iov_index].iov_base = (char *)(data->data + data->sp); + + if (size <= (data->cp - data->sp)) + { + iov[iov_index++].iov_len = size; + data->sp += size; + if (data->sp == data->cp) + data = data->next; + break; + } + else + { + iov[iov_index++].iov_len = data->cp - data->sp; + size -= (data->cp - data->sp); + data->sp = data->cp; + } + } + + /* In case of `more' display need. */ + if (!buffer_empty (b) && !no_more_flag) + { + iov[iov_index].iov_base = more; + iov[iov_index].iov_len = sizeof more; + iov_index++; + } + + /* We use write or writev*/ + +#ifdef IOV_MAX + /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g. + example: Solaris2.6 are defined IOV_MAX size at 16. */ + c_iov = iov; + total_size = iov_index; + nbytes = 0; + + while( total_size > 0 ) + { + /* initialize write vector size at once */ + iov_size = ( total_size > IOV_MAX ) ? IOV_MAX : total_size; + + c_nbytes = writev (fd, c_iov, iov_size ); + + if( c_nbytes < 0 ) + { + if(errno == EINTR) + ; + ; + if(errno == EWOULDBLOCK) + ; + ; + nbytes = c_nbytes; + break; + + } + + nbytes += c_nbytes; + + /* move pointer io-vector */ + c_iov += iov_size; + total_size -= iov_size; + } +#else /* IOV_MAX */ + nbytes = writev (fd, iov, iov_index); + + /* Error treatment. */ + if (nbytes < 0) + { + if (errno == EINTR) + ; + if (errno == EWOULDBLOCK) + ; + } +#endif /* IOV_MAX */ + + /* Free printed buffer data. */ + for (out = b->head; out && out != data; out = next) + { + next = out->next; + if (next) + next->prev = NULL; + else + b->tail = next; + b->head = next; + + buffer_data_free (out); + b->alloc--; + } + + if (iov != small_iov) + XFREE (MTYPE_TMP, iov); + + return nbytes; +} + +/* Calculate size of outputs then flush buffer to the file + descriptor. */ +int +buffer_flush_window (struct buffer *b, int fd, int width, int height, + int erase, int no_more) +{ + unsigned long cp; + unsigned long size; + int lp; + int lineno; + struct buffer_data *data; + + if (height >= 2) + height--; + + /* We have to calculate how many bytes should be written. */ + lp = 0; + lineno = 0; + size = 0; + + for (data = b->head; data; data = data->next) + { + cp = data->sp; + + while (cp < data->cp) + { + if (data->data[cp] == '\n' || lp == width) + { + lineno++; + if (lineno == height) + { + cp++; + size++; + goto flush; + } + lp = 0; + } + cp++; + lp++; + size++; + } + } + + /* Write data to the file descriptor. */ + flush: + + return buffer_flush_vty (b, fd, size, erase, no_more); +} diff --git a/lib/buffer.h b/lib/buffer.h new file mode 100644 index 00000000..7449aa77 --- /dev/null +++ b/lib/buffer.h @@ -0,0 +1,77 @@ +/* + * Buffering to output and input. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_BUFFER_H +#define _ZEBRA_BUFFER_H + +/* Buffer master. */ +struct buffer +{ + /* Data list. */ + struct buffer_data *head; + struct buffer_data *tail; + + /* Current allocated data. */ + unsigned long alloc; + + /* Total length of buffer. */ + unsigned long size; + + /* For allocation. */ + struct buffer_data *unused_head; + struct buffer_data *unused_tail; + + /* Current total length of this buffer. */ + unsigned long length; +}; + +/* Data container. */ +struct buffer_data +{ + struct buffer *parent; + struct buffer_data *next; + struct buffer_data *prev; + + /* Acctual data stream. */ + unsigned char *data; + + /* Current pointer. */ + unsigned long cp; + + /* Start pointer. */ + unsigned long sp; +}; + +/* Buffer prototypes. */ +struct buffer *buffer_new (size_t); +int buffer_write (struct buffer *, u_char *, size_t); +void buffer_free (struct buffer *); +char *buffer_getstr (struct buffer *); +int buffer_putc (struct buffer *, u_char); +int buffer_putstr (struct buffer *, u_char *); +void buffer_reset (struct buffer *); +int buffer_flush_all (struct buffer *, int); +int buffer_flush_vty_all (struct buffer *, int, int, int); +int buffer_flush_window (struct buffer *, int, int, int, int, int); +int buffer_empty (struct buffer *); + +#endif /* _ZEBRA_BUFFER_H */ diff --git a/lib/checksum.c b/lib/checksum.c new file mode 100644 index 00000000..6a29cbac --- /dev/null +++ b/lib/checksum.c @@ -0,0 +1,47 @@ +/* + * Checksum routine for Internet Protocol family headers (C Version). + * + * Refer to "Computing the Internet Checksum" by R. Braden, D. Borman and + * C. Partridge, Computer Communication Review, Vol. 19, No. 2, April 1989, + * pp. 86-101, for additional details on computing this checksum. + */ + +#include <zebra.h> + +int /* return checksum in low-order 16 bits */ +in_cksum(ptr, nbytes) +register u_short *ptr; +register int nbytes; +{ + register long sum; /* assumes long == 32 bits */ + u_short oddbyte; + register u_short answer; /* assumes u_short == 16 bits */ + + /* + * Our algorithm is simple, using a 32-bit accumulator (sum), + * we add sequential 16-bit words to it, and at the end, fold back + * all the carry bits from the top 16 bits into the lower 16 bits. + */ + + sum = 0; + while (nbytes > 1) { + sum += *ptr++; + nbytes -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nbytes == 1) { + oddbyte = 0; /* make sure top half is zero */ + *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */ + sum += oddbyte; + } + + /* + * Add back carry outs from top 16 bits to low 16 bits. + */ + + sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* ones-complement, then truncate to 16 bits */ + return(answer); +} diff --git a/lib/command.c b/lib/command.c new file mode 100644 index 00000000..8cbecce1 --- /dev/null +++ b/lib/command.c @@ -0,0 +1,2981 @@ +/* Command interpreter routine for virtual terminal [aka TeletYpe] + Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + +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 <zebra.h> + +#include "command.h" +#include "memory.h" +#include "log.h" +#include "version.h" + +/* Command vector which includes some level of command lists. Normally + each daemon maintains each own cmdvec. */ +vector cmdvec; + +/* Host information structure. */ +struct host host; + +/* Default motd string. */ +char *default_motd = +"\r\n\ +Hello, this is zebra (version " ZEBRA_VERSION ").\r\n\ +Copyright 1996-2002 Kunihiro Ishiguro.\r\n\ +\r\n"; + +/* Standard command node structures. */ +struct cmd_node auth_node = +{ + AUTH_NODE, + "Password: ", +}; + +struct cmd_node view_node = +{ + VIEW_NODE, + "%s> ", +}; + +struct cmd_node auth_enable_node = +{ + AUTH_ENABLE_NODE, + "Password: ", +}; + +struct cmd_node enable_node = +{ + ENABLE_NODE, + "%s# ", +}; + +struct cmd_node config_node = +{ + CONFIG_NODE, + "%s(config)# ", + 1 +}; + +/* Utility function to concatenate argv argument into a single string + with inserting ' ' character between each argument. */ +char * +argv_concat (char **argv, int argc, int shift) +{ + int i; + int len; + int index; + char *str; + + str = NULL; + index = 0; + + for (i = shift; i < argc; i++) + { + len = strlen (argv[i]); + + if (i == shift) + { + str = XSTRDUP (MTYPE_TMP, argv[i]); + index = len; + } + else + { + str = XREALLOC (MTYPE_TMP, str, (index + len + 2)); + str[index++] = ' '; + memcpy (str + index, argv[i], len); + index += len; + str[index] = '\0'; + } + } + return str; +} + +/* Install top node of command vector. */ +void +install_node (struct cmd_node *node, + int (*func) (struct vty *)) +{ + vector_set_index (cmdvec, node->node, node); + node->func = func; + node->cmd_vector = vector_init (VECTOR_MIN_SIZE); +} + +/* Compare two command's string. Used in sort_node (). */ +int +cmp_node (const void *p, const void *q) +{ + struct cmd_element *a = *(struct cmd_element **)p; + struct cmd_element *b = *(struct cmd_element **)q; + + return strcmp (a->string, b->string); +} + +int +cmp_desc (const void *p, const void *q) +{ + struct desc *a = *(struct desc **)p; + struct desc *b = *(struct desc **)q; + + return strcmp (a->cmd, b->cmd); +} + +/* Sort each node's command element according to command string. */ +void +sort_node () +{ + int i, j; + struct cmd_node *cnode; + vector descvec; + struct cmd_element *cmd_element; + + for (i = 0; i < vector_max (cmdvec); i++) + if ((cnode = vector_slot (cmdvec, i)) != NULL) + { + vector cmd_vector = cnode->cmd_vector; + qsort (cmd_vector->index, cmd_vector->max, sizeof (void *), cmp_node); + + for (j = 0; j < vector_max (cmd_vector); j++) + if ((cmd_element = vector_slot (cmd_vector, j)) != NULL) + { + descvec = vector_slot (cmd_element->strvec, + vector_max (cmd_element->strvec) - 1); + qsort (descvec->index, descvec->max, sizeof (void *), cmp_desc); + } + } +} + +/* Breaking up string into each command piece. I assume given + character is separated by a space character. Return value is a + vector which includes char ** data element. */ +vector +cmd_make_strvec (char *string) +{ + char *cp, *start, *token; + int strlen; + vector strvec; + + if (string == NULL) + return NULL; + + cp = string; + + /* Skip white spaces. */ + while (isspace ((int) *cp) && *cp != '\0') + cp++; + + /* Return if there is only white spaces */ + if (*cp == '\0') + return NULL; + + if (*cp == '!' || *cp == '#') + return NULL; + + /* Prepare return vector. */ + strvec = vector_init (VECTOR_MIN_SIZE); + + /* Copy each command piece and set into vector. */ + while (1) + { + start = cp; + while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && + *cp != '\0') + cp++; + strlen = cp - start; + token = XMALLOC (MTYPE_STRVEC, strlen + 1); + memcpy (token, start, strlen); + *(token + strlen) = '\0'; + vector_set (strvec, token); + + while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') && + *cp != '\0') + cp++; + + if (*cp == '\0') + return strvec; + } +} + +/* Free allocated string vector. */ +void +cmd_free_strvec (vector v) +{ + int i; + char *cp; + + if (!v) + return; + + for (i = 0; i < vector_max (v); i++) + if ((cp = vector_slot (v, i)) != NULL) + XFREE (MTYPE_STRVEC, cp); + + vector_free (v); +} + +/* Fetch next description. Used in cmd_make_descvec(). */ +char * +cmd_desc_str (char **string) +{ + char *cp, *start, *token; + int strlen; + + cp = *string; + + if (cp == NULL) + return NULL; + + /* Skip white spaces. */ + while (isspace ((int) *cp) && *cp != '\0') + cp++; + + /* Return if there is only white spaces */ + if (*cp == '\0') + return NULL; + + start = cp; + + while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') + cp++; + + strlen = cp - start; + token = XMALLOC (MTYPE_STRVEC, strlen + 1); + memcpy (token, start, strlen); + *(token + strlen) = '\0'; + + *string = cp; + + return token; +} + +/* New string vector. */ +vector +cmd_make_descvec (char *string, char *descstr) +{ + int multiple = 0; + char *sp; + char *token; + int len; + char *cp; + char *dp; + vector allvec; + vector strvec = NULL; + struct desc *desc; + + cp = string; + dp = descstr; + + if (cp == NULL) + return NULL; + + allvec = vector_init (VECTOR_MIN_SIZE); + + while (1) + { + while (isspace ((int) *cp) && *cp != '\0') + cp++; + + if (*cp == '(') + { + multiple = 1; + cp++; + } + if (*cp == ')') + { + multiple = 0; + cp++; + } + if (*cp == '|') + { + if (! multiple) + { + fprintf (stderr, "Command parse error!: %s\n", string); + exit (1); + } + cp++; + } + + while (isspace ((int) *cp) && *cp != '\0') + cp++; + + if (*cp == '(') + { + multiple = 1; + cp++; + } + + if (*cp == '\0') + return allvec; + + sp = cp; + + while (! (isspace ((int) *cp) || *cp == '\r' || *cp == '\n' || *cp == ')' || *cp == '|') && *cp != '\0') + cp++; + + len = cp - sp; + + token = XMALLOC (MTYPE_STRVEC, len + 1); + memcpy (token, sp, len); + *(token + len) = '\0'; + + desc = XCALLOC (MTYPE_DESC, sizeof (struct desc)); + desc->cmd = token; + desc->str = cmd_desc_str (&dp); + + if (multiple) + { + if (multiple == 1) + { + strvec = vector_init (VECTOR_MIN_SIZE); + vector_set (allvec, strvec); + } + multiple++; + } + else + { + strvec = vector_init (VECTOR_MIN_SIZE); + vector_set (allvec, strvec); + } + vector_set (strvec, desc); + } +} + +/* Count mandantory string vector size. This is to determine inputed + command has enough command length. */ +int +cmd_cmdsize (vector strvec) +{ + int i; + char *str; + int size = 0; + vector descvec; + + for (i = 0; i < vector_max (strvec); i++) + { + descvec = vector_slot (strvec, i); + + if (vector_max (descvec) == 1) + { + struct desc *desc = vector_slot (descvec, 0); + + str = desc->cmd; + + if (str == NULL || CMD_OPTION (str)) + return size; + else + size++; + } + else + size++; + } + return size; +} + +/* Return prompt character of specified node. */ +char * +cmd_prompt (enum node_type node) +{ + struct cmd_node *cnode; + + cnode = vector_slot (cmdvec, node); + return cnode->prompt; +} + +/* Install a command into a node. */ +void +install_element (enum node_type ntype, struct cmd_element *cmd) +{ + struct cmd_node *cnode; + + cnode = vector_slot (cmdvec, ntype); + + if (cnode == NULL) + { + fprintf (stderr, "Command node %d doesn't exist, please check it\n", + ntype); + exit (1); + } + + vector_set (cnode->cmd_vector, cmd); + + cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc); + cmd->cmdsize = cmd_cmdsize (cmd->strvec); +} + +static unsigned char itoa64[] = +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +void +to64(char *s, long v, int n) +{ + while (--n >= 0) + { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +char *zencrypt (char *passwd) +{ + char salt[6]; + struct timeval tv; + char *crypt (const char *, const char *); + + gettimeofday(&tv,0); + + to64(&salt[0], random(), 3); + to64(&salt[3], tv.tv_usec, 3); + salt[5] = '\0'; + + return crypt (passwd, salt); +} + +/* This function write configuration of this host. */ +int +config_write_host (struct vty *vty) +{ + if (host.name) + vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE); + + if (host.encrypt) + { + if (host.password_encrypt) + vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE); + if (host.enable_encrypt) + vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE); + } + else + { + if (host.password) + vty_out (vty, "password %s%s", host.password, VTY_NEWLINE); + if (host.enable) + vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE); + } + + if (host.logfile) + vty_out (vty, "log file %s%s", host.logfile, VTY_NEWLINE); + + if (host.log_stdout) + vty_out (vty, "log stdout%s", VTY_NEWLINE); + + if (host.log_syslog) + vty_out (vty, "log syslog%s", VTY_NEWLINE); + + if (zlog_default->maskpri != LOG_DEBUG) + vty_out (vty, "log trap %s%s", zlog_priority[zlog_default->maskpri], VTY_NEWLINE); + + if (zlog_default->record_priority == 1) + vty_out (vty, "log record-priority%s", VTY_NEWLINE); + + if (host.advanced) + vty_out (vty, "service advanced-vty%s", VTY_NEWLINE); + + if (host.encrypt) + vty_out (vty, "service password-encryption%s", VTY_NEWLINE); + + if (host.lines >= 0) + vty_out (vty, "service terminal-length %d%s", host.lines, + VTY_NEWLINE); + + if (! host.motd) + vty_out (vty, "no banner motd%s", VTY_NEWLINE); + + return 1; +} + +/* Utility function for getting command vector. */ +vector +cmd_node_vector (vector v, enum node_type ntype) +{ + struct cmd_node *cnode = vector_slot (v, ntype); + return cnode->cmd_vector; +} + +/* Filter command vector by symbol */ +int +cmd_filter_by_symbol (char *command, char *symbol) +{ + int i, lim; + + if (strcmp (symbol, "IPV4_ADDRESS") == 0) + { + i = 0; + lim = strlen (command); + while (i < lim) + { + if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/')) + return 1; + i++; + } + return 0; + } + if (strcmp (symbol, "STRING") == 0) + { + i = 0; + lim = strlen (command); + while (i < lim) + { + if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-')) + return 1; + i++; + } + return 0; + } + if (strcmp (symbol, "IFNAME") == 0) + { + i = 0; + lim = strlen (command); + while (i < lim) + { + if (! isalnum ((int) command[i])) + return 1; + i++; + } + return 0; + } + return 0; +} + +/* Completion match types. */ +enum match_type +{ + no_match, + extend_match, + ipv4_prefix_match, + ipv4_match, + ipv6_prefix_match, + ipv6_match, + range_match, + vararg_match, + partly_match, + exact_match +}; + +enum match_type +cmd_ipv4_match (char *str) +{ + char *sp; + int dots = 0, nums = 0; + char buf[4]; + + if (str == NULL) + return partly_match; + + for (;;) + { + memset (buf, 0, sizeof (buf)); + sp = str; + while (*str != '\0') + { + if (*str == '.') + { + if (dots >= 3) + return no_match; + + if (*(str + 1) == '.') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + if (!isdigit ((int) *str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy (buf, sp, str - sp); + if (atoi (buf) > 255) + return no_match; + + nums++; + + if (*str == '\0') + break; + + str++; + } + + if (nums < 4) + return partly_match; + + return exact_match; +} + +enum match_type +cmd_ipv4_prefix_match (char *str) +{ + char *sp; + int dots = 0; + char buf[4]; + + if (str == NULL) + return partly_match; + + for (;;) + { + memset (buf, 0, sizeof (buf)); + sp = str; + while (*str != '\0' && *str != '/') + { + if (*str == '.') + { + if (dots == 3) + return no_match; + + if (*(str + 1) == '.' || *(str + 1) == '/') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + + if (!isdigit ((int) *str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy (buf, sp, str - sp); + if (atoi (buf) > 255) + return no_match; + + if (dots == 3) + { + if (*str == '/') + { + if (*(str + 1) == '\0') + return partly_match; + + str++; + break; + } + else if (*str == '\0') + return partly_match; + } + + if (*str == '\0') + return partly_match; + + str++; + } + + sp = str; + while (*str != '\0') + { + if (!isdigit ((int) *str)) + return no_match; + + str++; + } + + if (atoi (sp) > 32) + return no_match; + + return exact_match; +} + +#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%" +#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/" +#define STATE_START 1 +#define STATE_COLON 2 +#define STATE_DOUBLE 3 +#define STATE_ADDR 4 +#define STATE_DOT 5 +#define STATE_SLASH 6 +#define STATE_MASK 7 + +enum match_type +cmd_ipv6_match (char *str) +{ + int state = STATE_START; + int colons = 0, nums = 0, double_colon = 0; + char *sp = NULL; + + if (str == NULL) + return partly_match; + + if (strspn (str, IPV6_ADDR_STR) != strlen (str)) + return no_match; + + while (*str != '\0') + { + switch (state) + { + case STATE_START: + if (*str == ':') + { + if (*(str + 1) != ':' && *(str + 1) != '\0') + return no_match; + colons--; + state = STATE_COLON; + } + else + { + sp = str; + state = STATE_ADDR; + } + + continue; + case STATE_COLON: + colons++; + if (*(str + 1) == ':') + state = STATE_DOUBLE; + else + { + sp = str + 1; + state = STATE_ADDR; + } + break; + case STATE_DOUBLE: + if (double_colon) + return no_match; + + if (*(str + 1) == ':') + return no_match; + else + { + if (*(str + 1) != '\0') + colons++; + sp = str + 1; + state = STATE_ADDR; + } + + double_colon++; + nums++; + break; + case STATE_ADDR: + if (*(str + 1) == ':' || *(str + 1) == '\0') + { + if (str - sp > 3) + return no_match; + + nums++; + state = STATE_COLON; + } + if (*(str + 1) == '.') + state = STATE_DOT; + break; + case STATE_DOT: + state = STATE_ADDR; + break; + default: + break; + } + + if (nums > 8) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + +#if 0 + if (nums < 11) + return partly_match; +#endif /* 0 */ + + return exact_match; +} + +enum match_type +cmd_ipv6_prefix_match (char *str) +{ + int state = STATE_START; + int colons = 0, nums = 0, double_colon = 0; + int mask; + char *sp = NULL; + char *endptr = NULL; + + if (str == NULL) + return partly_match; + + if (strspn (str, IPV6_PREFIX_STR) != strlen (str)) + return no_match; + + while (*str != '\0' && state != STATE_MASK) + { + switch (state) + { + case STATE_START: + if (*str == ':') + { + if (*(str + 1) != ':' && *(str + 1) != '\0') + return no_match; + colons--; + state = STATE_COLON; + } + else + { + sp = str; + state = STATE_ADDR; + } + + continue; + case STATE_COLON: + colons++; + if (*(str + 1) == '/') + return no_match; + else if (*(str + 1) == ':') + state = STATE_DOUBLE; + else + { + sp = str + 1; + state = STATE_ADDR; + } + break; + case STATE_DOUBLE: + if (double_colon) + return no_match; + + if (*(str + 1) == ':') + return no_match; + else + { + if (*(str + 1) != '\0' && *(str + 1) != '/') + colons++; + sp = str + 1; + + if (*(str + 1) == '/') + state = STATE_SLASH; + else + state = STATE_ADDR; + } + + double_colon++; + nums += 1; + break; + case STATE_ADDR: + if (*(str + 1) == ':' || *(str + 1) == '.' + || *(str + 1) == '\0' || *(str + 1) == '/') + { + if (str - sp > 3) + return no_match; + + for (; sp <= str; sp++) + if (*sp == '/') + return no_match; + + nums++; + + if (*(str + 1) == ':') + state = STATE_COLON; + else if (*(str + 1) == '.') + state = STATE_DOT; + else if (*(str + 1) == '/') + state = STATE_SLASH; + } + break; + case STATE_DOT: + state = STATE_ADDR; + break; + case STATE_SLASH: + if (*(str + 1) == '\0') + return partly_match; + + state = STATE_MASK; + break; + default: + break; + } + + if (nums > 11) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + + if (state < STATE_MASK) + return partly_match; + + mask = strtol (str, &endptr, 10); + if (*endptr != '\0') + return no_match; + + if (mask < 0 || mask > 128) + return no_match; + +/* I don't know why mask < 13 makes command match partly. + Forgive me to make this comments. I Want to set static default route + because of lack of function to originate default in ospf6d; sorry + yasu + if (mask < 13) + return partly_match; +*/ + + return exact_match; +} + +#define DECIMAL_STRLEN_MAX 10 + +int +cmd_range_match (char *range, char *str) +{ + char *p; + char buf[DECIMAL_STRLEN_MAX + 1]; + char *endptr = NULL; + unsigned long min, max, val; + + if (str == NULL) + return 1; + + val = strtoul (str, &endptr, 10); + if (*endptr != '\0') + return 0; + + range++; + p = strchr (range, '-'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy (buf, range, p - range); + buf[p - range] = '\0'; + min = strtoul (buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + range = p + 1; + p = strchr (range, '>'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy (buf, range, p - range); + buf[p - range] = '\0'; + max = strtoul (buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + if (val < min || val > max) + return 0; + + return 1; +} + +/* Make completion match and return match type flag. */ +enum match_type +cmd_filter_by_completion (char *command, vector v, int index) +{ + int i; + char *str; + struct cmd_element *cmd_element; + enum match_type match_type; + vector descvec; + struct desc *desc; + + match_type = no_match; + + /* If command and cmd_element string does not match set NULL to vector */ + for (i = 0; i < vector_max (v); i++) + if ((cmd_element = vector_slot (v, i)) != NULL) + { + if (index >= vector_max (cmd_element->strvec)) + vector_slot (v, i) = NULL; + else + { + int j; + int matched = 0; + + descvec = vector_slot (cmd_element->strvec, index); + + for (j = 0; j < vector_max (descvec); j++) + { + desc = vector_slot (descvec, j); + str = desc->cmd; + + if (CMD_VARARG (str)) + { + if (match_type < vararg_match) + match_type = vararg_match; + matched++; + } + else if (CMD_RANGE (str)) + { + if (cmd_range_match (str, command)) + { + if (match_type < range_match) + match_type = range_match; + + matched++; + } + } + else if (CMD_IPV6 (str)) + { + if (cmd_ipv6_match (command)) + { + if (match_type < ipv6_match) + match_type = ipv6_match; + + matched++; + } + } + else if (CMD_IPV6_PREFIX (str)) + { + if (cmd_ipv6_prefix_match (command)) + { + if (match_type < ipv6_prefix_match) + match_type = ipv6_prefix_match; + + matched++; + } + } + else if (CMD_IPV4 (str)) + { + if (cmd_ipv4_match (command)) + { + if (match_type < ipv4_match) + match_type = ipv4_match; + + matched++; + } + } + else if (CMD_IPV4_PREFIX (str)) + { + if (cmd_ipv4_prefix_match (command)) + { + if (match_type < ipv4_prefix_match) + match_type = ipv4_prefix_match; + matched++; + } + } + else + /* Check is this point's argument optional ? */ + if (CMD_OPTION (str) || CMD_VARIABLE (str)) + { + if (match_type < extend_match) + match_type = extend_match; + matched++; + } + else if (strncmp (command, str, strlen (command)) == 0) + { + if (strcmp (command, str) == 0) + match_type = exact_match; + else + { + if (match_type < partly_match) + match_type = partly_match; + } + matched++; + } + } + if (! matched) + vector_slot (v, i) = NULL; + } + } + return match_type; +} + +/* Filter vector by command character with index. */ +enum match_type +cmd_filter_by_string (char *command, vector v, int index) +{ + int i; + char *str; + struct cmd_element *cmd_element; + enum match_type match_type; + vector descvec; + struct desc *desc; + + match_type = no_match; + + /* If command and cmd_element string does not match set NULL to vector */ + for (i = 0; i < vector_max (v); i++) + if ((cmd_element = vector_slot (v, i)) != NULL) + { + /* If given index is bigger than max string vector of command, + set NULL*/ + if (index >= vector_max (cmd_element->strvec)) + vector_slot (v, i) = NULL; + else + { + int j; + int matched = 0; + + descvec = vector_slot (cmd_element->strvec, index); + + for (j = 0; j < vector_max (descvec); j++) + { + desc = vector_slot (descvec, j); + str = desc->cmd; + + if (CMD_VARARG (str)) + { + if (match_type < vararg_match) + match_type = vararg_match; + matched++; + } + else if (CMD_RANGE (str)) + { + if (cmd_range_match (str, command)) + { + if (match_type < range_match) + match_type = range_match; + matched++; + } + } + else if (CMD_IPV6 (str)) + { + if (cmd_ipv6_match (command) == exact_match) + { + if (match_type < ipv6_match) + match_type = ipv6_match; + matched++; + } + } + else if (CMD_IPV6_PREFIX (str)) + { + if (cmd_ipv6_prefix_match (command) == exact_match) + { + if (match_type < ipv6_prefix_match) + match_type = ipv6_prefix_match; + matched++; + } + } + else if (CMD_IPV4 (str)) + { + if (cmd_ipv4_match (command) == exact_match) + { + if (match_type < ipv4_match) + match_type = ipv4_match; + matched++; + } + } + else if (CMD_IPV4_PREFIX (str)) + { + if (cmd_ipv4_prefix_match (command) == exact_match) + { + if (match_type < ipv4_prefix_match) + match_type = ipv4_prefix_match; + matched++; + } + } + else if (CMD_OPTION (str) || CMD_VARIABLE (str)) + { + if (match_type < extend_match) + match_type = extend_match; + matched++; + } + else + { + if (strcmp (command, str) == 0) + { + match_type = exact_match; + matched++; + } + } + } + if (! matched) + vector_slot (v, i) = NULL; + } + } + return match_type; +} + +/* Check ambiguous match */ +int +is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) +{ + int i; + int j; + char *str = NULL; + struct cmd_element *cmd_element; + char *matched = NULL; + vector descvec; + struct desc *desc; + + for (i = 0; i < vector_max (v); i++) + if ((cmd_element = vector_slot (v, i)) != NULL) + { + int match = 0; + + descvec = vector_slot (cmd_element->strvec, index); + + for (j = 0; j < vector_max (descvec); j++) + { + enum match_type ret; + + desc = vector_slot (descvec, j); + str = desc->cmd; + + switch (type) + { + case exact_match: + if (! (CMD_OPTION (str) || CMD_VARIABLE (str)) + && strcmp (command, str) == 0) + match++; + break; + case partly_match: + if (! (CMD_OPTION (str) || CMD_VARIABLE (str)) + && strncmp (command, str, strlen (command)) == 0) + { + if (matched && strcmp (matched, str) != 0) + return 1; /* There is ambiguous match. */ + else + matched = str; + match++; + } + break; + case range_match: + if (cmd_range_match (str, command)) + { + if (matched && strcmp (matched, str) != 0) + return 1; + else + matched = str; + match++; + } + break; + case ipv6_match: + if (CMD_IPV6 (str)) + match++; + break; + case ipv6_prefix_match: + if ((ret = cmd_ipv6_prefix_match (command)) != no_match) + { + if (ret == partly_match) + return 2; /* There is incomplete match. */ + + match++; + } + break; + case ipv4_match: + if (CMD_IPV4 (str)) + match++; + break; + case ipv4_prefix_match: + if ((ret = cmd_ipv4_prefix_match (command)) != no_match) + { + if (ret == partly_match) + return 2; /* There is incomplete match. */ + + match++; + } + break; + case extend_match: + if (CMD_OPTION (str) || CMD_VARIABLE (str)) + match++; + break; + case no_match: + default: + break; + } + } + if (! match) + vector_slot (v, i) = NULL; + } + return 0; +} + +/* If src matches dst return dst string, otherwise return NULL */ +char * +cmd_entry_function (char *src, char *dst) +{ + /* Skip variable arguments. */ + if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) || + CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst)) + return NULL; + + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + /* Matched with input string. */ + if (strncmp (src, dst, strlen (src)) == 0) + return dst; + + return NULL; +} + +/* If src matches dst return dst string, otherwise return NULL */ +/* This version will return the dst string always if it is + CMD_VARIABLE for '?' key processing */ +char * +cmd_entry_function_desc (char *src, char *dst) +{ + if (CMD_VARARG (dst)) + return dst; + + if (CMD_RANGE (dst)) + { + if (cmd_range_match (dst, src)) + return dst; + else + return NULL; + } + + if (CMD_IPV6 (dst)) + { + if (cmd_ipv6_match (src)) + return dst; + else + return NULL; + } + + if (CMD_IPV6_PREFIX (dst)) + { + if (cmd_ipv6_prefix_match (src)) + return dst; + else + return NULL; + } + + if (CMD_IPV4 (dst)) + { + if (cmd_ipv4_match (src)) + return dst; + else + return NULL; + } + + if (CMD_IPV4_PREFIX (dst)) + { + if (cmd_ipv4_prefix_match (src)) + return dst; + else + return NULL; + } + + /* Optional or variable commands always match on '?' */ + if (CMD_OPTION (dst) || CMD_VARIABLE (dst)) + return dst; + + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + if (strncmp (src, dst, strlen (src)) == 0) + return dst; + else + return NULL; +} + +/* Check same string element existence. If it isn't there return + 1. */ +int +cmd_unique_string (vector v, char *str) +{ + int i; + char *match; + + for (i = 0; i < vector_max (v); i++) + if ((match = vector_slot (v, i)) != NULL) + if (strcmp (match, str) == 0) + return 0; + return 1; +} + +/* Compare string to description vector. If there is same string + return 1 else return 0. */ +int +desc_unique_string (vector v, char *str) +{ + int i; + struct desc *desc; + + for (i = 0; i < vector_max (v); i++) + if ((desc = vector_slot (v, i)) != NULL) + if (strcmp (desc->cmd, str) == 0) + return 1; + return 0; +} + +/* '?' describe command support. */ +vector +cmd_describe_command (vector vline, struct vty *vty, int *status) +{ + int i; + vector cmd_vector; +#define INIT_MATCHVEC_SIZE 10 + vector matchvec; + struct cmd_element *cmd_element; + int index; + static struct desc desc_cr = { "<cr>", "" }; + + /* Set index. */ + index = vector_max (vline) - 1; + + /* Make copy vector of current node's command vector. */ + cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + + /* Prepare match vector */ + matchvec = vector_init (INIT_MATCHVEC_SIZE); + + /* Filter commands. */ + for (i = 0; i < index; i++) + { + enum match_type match; + char *command; + int ret; + + command = vector_slot (vline, i); + + match = cmd_filter_by_completion (command, cmd_vector, i); + + if (match == vararg_match) + { + struct cmd_element *cmd_element; + vector descvec; + int j, k; + + for (j = 0; j < vector_max (cmd_vector); j++) + if ((cmd_element = vector_slot (cmd_vector, j)) != NULL) + { + descvec = vector_slot (cmd_element->strvec, + vector_max (cmd_element->strvec) - 1); + for (k = 0; k < vector_max (descvec); k++) + { + struct desc *desc = vector_slot (descvec, k); + vector_set (matchvec, desc); + } + } + + vector_set (matchvec, &desc_cr); + + vector_free (cmd_vector); + + return matchvec; + } + + if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) + { + vector_free (cmd_vector); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + else if (ret == 2) + { + vector_free (cmd_vector); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + } + + /* Prepare match vector */ + /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ + + /* Make description vector. */ + for (i = 0; i < vector_max (cmd_vector); i++) + if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) + { + char *string = NULL; + vector strvec = cmd_element->strvec; + + if (index > vector_max (strvec)) + vector_slot (cmd_vector, i) = NULL; + else + { + /* Check is command is completed. */ + if (index == vector_max (strvec)) + { + string = "<cr>"; + if (! desc_unique_string (matchvec, string)) + vector_set (matchvec, &desc_cr); + } + else + { + int j; + vector descvec = vector_slot (strvec, index); + struct desc *desc; + + for (j = 0; j < vector_max (descvec); j++) + { + desc = vector_slot (descvec, j); + string = cmd_entry_function_desc (vector_slot (vline, index), desc->cmd); + if (string) + { + /* Uniqueness check */ + if (! desc_unique_string (matchvec, string)) + vector_set (matchvec, desc); + } + } + } + } + } + vector_free (cmd_vector); + + if (vector_slot (matchvec, 0) == NULL) + { + vector_free (matchvec); + *status= CMD_ERR_NO_MATCH; + } + else + *status = CMD_SUCCESS; + + return matchvec; +} + +/* Check LCD of matched command. */ +int +cmd_lcd (char **matched) +{ + int i; + int j; + int lcd = -1; + char *s1, *s2; + char c1, c2; + + if (matched[0] == NULL || matched[1] == NULL) + return 0; + + for (i = 1; matched[i] != NULL; i++) + { + s1 = matched[i - 1]; + s2 = matched[i]; + + for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) + if (c1 != c2) + break; + + if (lcd < 0) + lcd = j; + else + { + if (lcd > j) + lcd = j; + } + } + return lcd; +} + +/* Command line completion support. */ +char ** +cmd_complete_command (vector vline, struct vty *vty, int *status) +{ + int i; + vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); +#define INIT_MATCHVEC_SIZE 10 + vector matchvec; + struct cmd_element *cmd_element; + int index = vector_max (vline) - 1; + char **match_str; + struct desc *desc; + vector descvec; + char *command; + int lcd; + + /* First, filter by preceeding command string */ + for (i = 0; i < index; i++) + { + enum match_type match; + int ret; + + command = vector_slot (vline, i); + + /* First try completion match, if there is exactly match return 1 */ + match = cmd_filter_by_completion (command, cmd_vector, i); + + /* If there is exact match then filter ambiguous match else check + ambiguousness. */ + if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) + { + vector_free (cmd_vector); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + /* + else if (ret == 2) + { + vector_free (cmd_vector); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + */ + } + + /* Prepare match vector. */ + matchvec = vector_init (INIT_MATCHVEC_SIZE); + + /* Now we got into completion */ + for (i = 0; i < vector_max (cmd_vector); i++) + if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) + { + char *string; + vector strvec = cmd_element->strvec; + + /* Check field length */ + if (index >= vector_max (strvec)) + vector_slot (cmd_vector, i) = NULL; + else + { + int j; + + descvec = vector_slot (strvec, index); + for (j = 0; j < vector_max (descvec); j++) + { + desc = vector_slot (descvec, j); + + if ((string = cmd_entry_function (vector_slot (vline, index), + desc->cmd))) + if (cmd_unique_string (matchvec, string)) + vector_set (matchvec, XSTRDUP (MTYPE_TMP, string)); + } + } + } + + /* We don't need cmd_vector any more. */ + vector_free (cmd_vector); + + /* No matched command */ + if (vector_slot (matchvec, 0) == NULL) + { + vector_free (matchvec); + + /* In case of 'command \t' pattern. Do you need '?' command at + the end of the line. */ + if (vector_slot (vline, index) == '\0') + *status = CMD_ERR_NOTHING_TODO; + else + *status = CMD_ERR_NO_MATCH; + return NULL; + } + + /* Only one matched */ + if (vector_slot (matchvec, 1) == NULL) + { + match_str = (char **) matchvec->index; + vector_only_wrapper_free (matchvec); + *status = CMD_COMPLETE_FULL_MATCH; + return match_str; + } + /* Make it sure last element is NULL. */ + vector_set (matchvec, NULL); + + /* Check LCD of matched strings. */ + if (vector_slot (vline, index) != NULL) + { + lcd = cmd_lcd ((char **) matchvec->index); + + if (lcd) + { + int len = strlen (vector_slot (vline, index)); + + if (len < lcd) + { + char *lcdstr; + + lcdstr = XMALLOC (MTYPE_TMP, lcd + 1); + memcpy (lcdstr, matchvec->index[0], lcd); + lcdstr[lcd] = '\0'; + + /* match_str = (char **) &lcdstr; */ + + /* Free matchvec. */ + for (i = 0; i < vector_max (matchvec); i++) + { + if (vector_slot (matchvec, i)) + XFREE (MTYPE_TMP, vector_slot (matchvec, i)); + } + vector_free (matchvec); + + /* Make new matchvec. */ + matchvec = vector_init (INIT_MATCHVEC_SIZE); + vector_set (matchvec, lcdstr); + match_str = (char **) matchvec->index; + vector_only_wrapper_free (matchvec); + + *status = CMD_COMPLETE_MATCH; + return match_str; + } + } + } + + match_str = (char **) matchvec->index; + vector_only_wrapper_free (matchvec); + *status = CMD_COMPLETE_LIST_MATCH; + return match_str; +} + +/* Execute command by argument vline vector. */ +int +cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd) +{ + int i; + int index; + vector cmd_vector; + struct cmd_element *cmd_element; + struct cmd_element *matched_element; + unsigned int matched_count, incomplete_count; + int argc; + char *argv[CMD_ARGC_MAX]; + enum match_type match = 0; + int varflag; + char *command; + + /* Make copy of command elements. */ + cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + + for (index = 0; index < vector_max (vline); index++) + { + int ret; + + command = vector_slot (vline, index); + + match = cmd_filter_by_completion (command, cmd_vector, index); + + if (match == vararg_match) + break; + + ret = is_cmd_ambiguous (command, cmd_vector, index, match); + + if (ret == 1) + { + vector_free (cmd_vector); + return CMD_ERR_AMBIGUOUS; + } + else if (ret == 2) + { + vector_free (cmd_vector); + return CMD_ERR_NO_MATCH; + } + } + + /* Check matched count. */ + matched_element = NULL; + matched_count = 0; + incomplete_count = 0; + + for (i = 0; i < vector_max (cmd_vector); i++) + if (vector_slot (cmd_vector,i) != NULL) + { + cmd_element = vector_slot (cmd_vector,i); + + if (match == vararg_match || index >= cmd_element->cmdsize) + { + matched_element = cmd_element; +#if 0 + printf ("DEBUG: %s\n", cmd_element->string); +#endif + matched_count++; + } + else + { + incomplete_count++; + } + } + + /* Finish of using cmd_vector. */ + vector_free (cmd_vector); + + /* To execute command, matched_count must be 1.*/ + if (matched_count == 0) + { + if (incomplete_count) + return CMD_ERR_INCOMPLETE; + else + return CMD_ERR_NO_MATCH; + } + + if (matched_count > 1) + return CMD_ERR_AMBIGUOUS; + + /* Argument treatment */ + varflag = 0; + argc = 0; + + for (i = 0; i < vector_max (vline); i++) + { + if (varflag) + argv[argc++] = vector_slot (vline, i); + else + { + vector descvec = vector_slot (matched_element->strvec, i); + + if (vector_max (descvec) == 1) + { + struct desc *desc = vector_slot (descvec, 0); + char *str = desc->cmd; + + if (CMD_VARARG (str)) + varflag = 1; + + if (varflag || CMD_VARIABLE (str) || CMD_OPTION (str)) + argv[argc++] = vector_slot (vline, i); + } + else + argv[argc++] = vector_slot (vline, i); + } + + if (argc >= CMD_ARGC_MAX) + return CMD_ERR_EXEED_ARGC_MAX; + } + + /* For vtysh execution. */ + if (cmd) + *cmd = matched_element; + + if (matched_element->daemon) + return CMD_SUCCESS_DAEMON; + + /* Execute matched command. */ + return (*matched_element->func) (matched_element, vty, argc, argv); +} + +/* Execute command by argument readline. */ +int +cmd_execute_command_strict (vector vline, struct vty *vty, + struct cmd_element **cmd) +{ + int i; + int index; + vector cmd_vector; + struct cmd_element *cmd_element; + struct cmd_element *matched_element; + unsigned int matched_count, incomplete_count; + int argc; + char *argv[CMD_ARGC_MAX]; + int varflag; + enum match_type match = 0; + char *command; + + /* Make copy of command element */ + cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + + for (index = 0; index < vector_max (vline); index++) + { + int ret; + + command = vector_slot (vline, index); + + match = cmd_filter_by_string (vector_slot (vline, index), + cmd_vector, index); + + /* If command meets '.VARARG' then finish matching. */ + if (match == vararg_match) + break; + + ret = is_cmd_ambiguous (command, cmd_vector, index, match); + if (ret == 1) + { + vector_free (cmd_vector); + return CMD_ERR_AMBIGUOUS; + } + if (ret == 2) + { + vector_free (cmd_vector); + return CMD_ERR_NO_MATCH; + } + } + + /* Check matched count. */ + matched_element = NULL; + matched_count = 0; + incomplete_count = 0; + for (i = 0; i < vector_max (cmd_vector); i++) + if (vector_slot (cmd_vector,i) != NULL) + { + cmd_element = vector_slot (cmd_vector,i); + + if (match == vararg_match || index >= cmd_element->cmdsize) + { + matched_element = cmd_element; + matched_count++; + } + else + incomplete_count++; + } + + /* Finish of using cmd_vector. */ + vector_free (cmd_vector); + + /* To execute command, matched_count must be 1.*/ + if (matched_count == 0) + { + if (incomplete_count) + return CMD_ERR_INCOMPLETE; + else + return CMD_ERR_NO_MATCH; + } + + if (matched_count > 1) + return CMD_ERR_AMBIGUOUS; + + /* Argument treatment */ + varflag = 0; + argc = 0; + + for (i = 0; i < vector_max (vline); i++) + { + if (varflag) + argv[argc++] = vector_slot (vline, i); + else + { + vector descvec = vector_slot (matched_element->strvec, i); + + if (vector_max (descvec) == 1) + { + struct desc *desc = vector_slot (descvec, 0); + char *str = desc->cmd; + + if (CMD_VARARG (str)) + varflag = 1; + + if (varflag || CMD_VARIABLE (str) || CMD_OPTION (str)) + argv[argc++] = vector_slot (vline, i); + } + else + argv[argc++] = vector_slot (vline, i); + } + + if (argc >= CMD_ARGC_MAX) + return CMD_ERR_EXEED_ARGC_MAX; + } + + /* For vtysh execution. */ + if (cmd) + *cmd = matched_element; + + if (matched_element->daemon) + return CMD_SUCCESS_DAEMON; + + /* Now execute matched command */ + return (*matched_element->func) (matched_element, vty, argc, argv); +} + +/* Configration make from file. */ +int +config_from_file (struct vty *vty, FILE *fp) +{ + int ret; + vector vline; + + while (fgets (vty->buf, VTY_BUFSIZ, fp)) + { + vline = cmd_make_strvec (vty->buf); + + /* In case of comment line */ + if (vline == NULL) + continue; + /* Execute configuration command : this is strict match */ + ret = cmd_execute_command_strict (vline, vty, NULL); + + /* Try again with setting node to CONFIG_NODE */ + if (ret != CMD_SUCCESS && ret != CMD_WARNING) + { + if (vty->node == KEYCHAIN_KEY_NODE) + { + vty->node = KEYCHAIN_NODE; + + ret = cmd_execute_command_strict (vline, vty, NULL); + + if (ret != CMD_SUCCESS && ret != CMD_WARNING) + { + vty->node = CONFIG_NODE; + ret = cmd_execute_command_strict (vline, vty, NULL); + } + } + else + { + vty->node = CONFIG_NODE; + ret = cmd_execute_command_strict (vline, vty, NULL); + } + } + + cmd_free_strvec (vline); + + if (ret != CMD_SUCCESS && ret != CMD_WARNING) + return ret; + } + return CMD_SUCCESS; +} + +/* Configration from terminal */ +DEFUN (config_terminal, + config_terminal_cmd, + "configure terminal", + "Configuration from vty interface\n" + "Configuration terminal\n") +{ + if (vty_config_lock (vty)) + vty->node = CONFIG_NODE; + else + { + vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +/* Enable command */ +DEFUN (enable, + config_enable_cmd, + "enable", + "Turn on privileged mode command\n") +{ + /* If enable password is NULL, change to ENABLE_NODE */ + if ((host.enable == NULL && host.enable_encrypt == NULL) || + vty->type == VTY_SHELL_SERV) + vty->node = ENABLE_NODE; + else + vty->node = AUTH_ENABLE_NODE; + + return CMD_SUCCESS; +} + +/* Disable command */ +DEFUN (disable, + config_disable_cmd, + "disable", + "Turn off privileged mode command\n") +{ + if (vty->node == ENABLE_NODE) + vty->node = VIEW_NODE; + return CMD_SUCCESS; +} + +/* Down vty node level. */ +DEFUN (config_exit, + config_exit_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + switch (vty->node) + { + case VIEW_NODE: + case ENABLE_NODE: + if (vty_shell (vty)) + exit (0); + else + vty->status = VTY_CLOSE; + break; + case CONFIG_NODE: + vty->node = ENABLE_NODE; + vty_config_unlock (vty); + break; + case INTERFACE_NODE: + case ZEBRA_NODE: + case BGP_NODE: + case RIP_NODE: + case RIPNG_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case KEYCHAIN_NODE: + case MASC_NODE: + case RMAP_NODE: + case VTY_NODE: + vty->node = CONFIG_NODE; + break; + case BGP_VPNV4_NODE: + case BGP_IPV4_NODE: + case BGP_IPV4M_NODE: + case BGP_IPV6_NODE: + vty->node = BGP_NODE; + break; + case KEYCHAIN_KEY_NODE: + vty->node = KEYCHAIN_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* quit is alias of exit. */ +ALIAS (config_exit, + config_quit_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +/* End of configuration. */ +DEFUN (config_end, + config_end_cmd, + "end", + "End current mode and change to enable mode.") +{ + switch (vty->node) + { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case INTERFACE_NODE: + case ZEBRA_NODE: + case RIP_NODE: + case RIPNG_NODE: + case BGP_NODE: + case BGP_VPNV4_NODE: + case BGP_IPV4_NODE: + case BGP_IPV4M_NODE: + case BGP_IPV6_NODE: + case RMAP_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case KEYCHAIN_NODE: + case KEYCHAIN_KEY_NODE: + case MASC_NODE: + case VTY_NODE: + vty_config_unlock (vty); + vty->node = ENABLE_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* Show version. */ +DEFUN (show_version, + show_version_cmd, + "show version", + SHOW_STR + "Displays zebra version\n") +{ + vty_out (vty, "Zebra %s (%s).%s", ZEBRA_VERSION, + host_name, + VTY_NEWLINE); + vty_out (vty, "Copyright 1996-2002, Kunihiro Ishiguro.%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +/* Help display function for all node. */ +DEFUN (config_help, + config_help_cmd, + "help", + "Description of the interactive help system\n") +{ + vty_out (vty, + "Zebra VTY provides advanced help feature. When you need help,%s\ +anytime at the command line please press '?'.%s\ +%s\ +If nothing matches, the help list will be empty and you must backup%s\ + until entering a '?' shows the available options.%s\ +Two styles of help are provided:%s\ +1. Full help is available when you are ready to enter a%s\ +command argument (e.g. 'show ?') and describes each possible%s\ +argument.%s\ +2. Partial help is provided when an abbreviated argument is entered%s\ + and you want to know what arguments match the input%s\ + (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Help display function for all node. */ +DEFUN (config_list, + config_list_cmd, + "list", + "Print command list\n") +{ + int i; + struct cmd_node *cnode = vector_slot (cmdvec, vty->node); + struct cmd_element *cmd; + + for (i = 0; i < vector_max (cnode->cmd_vector); i++) + if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL) + vty_out (vty, " %s%s", cmd->string, + VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Write current configuration into file. */ +DEFUN (config_write_file, + config_write_file_cmd, + "write file", + "Write running configuration to memory, network, or terminal\n" + "Write to configuration file\n") +{ + int i; + int fd; + struct cmd_node *node; + char *config_file; + char *config_file_tmp = NULL; + char *config_file_sav = NULL; + struct vty *file_vty; + + /* Check and see if we are operating under vtysh configuration */ + if (host.config == NULL) + { + vty_out (vty, "Can't save to configuration file, using vtysh.%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get filename. */ + config_file = host.config; + + config_file_sav = malloc (strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1); + strcpy (config_file_sav, config_file); + strcat (config_file_sav, CONF_BACKUP_EXT); + + + config_file_tmp = malloc (strlen (config_file) + 8); + sprintf (config_file_tmp, "%s.XXXXXX", config_file); + + /* Open file to configuration write. */ + fd = mkstemp (config_file_tmp); + if (fd < 0) + { + vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp, + VTY_NEWLINE); + free (config_file_tmp); + free (config_file_sav); + return CMD_WARNING; + } + + /* Make vty for configuration file. */ + file_vty = vty_new (); + file_vty->fd = fd; + file_vty->type = VTY_FILE; + + /* Config file header print. */ + vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! "); + vty_time_print (file_vty, 1); + vty_out (file_vty, "!\n"); + + for (i = 0; i < vector_max (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func) + { + if ((*node->func) (file_vty)) + vty_out (file_vty, "!\n"); + } + vty_close (file_vty); + + if (unlink (config_file_sav) != 0) + if (errno != ENOENT) + { + vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, + VTY_NEWLINE); + free (config_file_sav); + free (config_file_tmp); + unlink (config_file_tmp); + return CMD_WARNING; + } + if (link (config_file, config_file_sav) != 0) + { + vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, + VTY_NEWLINE); + free (config_file_sav); + free (config_file_tmp); + unlink (config_file_tmp); + return CMD_WARNING; + } + sync (); + if (unlink (config_file) != 0) + { + vty_out (vty, "Can't unlink configuration file %s.%s", config_file, + VTY_NEWLINE); + free (config_file_sav); + free (config_file_tmp); + unlink (config_file_tmp); + return CMD_WARNING; + } + if (link (config_file_tmp, config_file) != 0) + { + vty_out (vty, "Can't save configuration file %s.%s", config_file, + VTY_NEWLINE); + free (config_file_sav); + free (config_file_tmp); + unlink (config_file_tmp); + return CMD_WARNING; + } + unlink (config_file_tmp); + sync (); + + free (config_file_sav); + free (config_file_tmp); + vty_out (vty, "Configuration saved to %s%s", config_file, + VTY_NEWLINE); + return CMD_SUCCESS; +} + +ALIAS (config_write_file, + config_write_cmd, + "write", + "Write running configuration to memory, network, or terminal\n") + +ALIAS (config_write_file, + config_write_memory_cmd, + "write memory", + "Write running configuration to memory, network, or terminal\n" + "Write configuration to the file (same as write file)\n") + +ALIAS (config_write_file, + copy_runningconfig_startupconfig_cmd, + "copy running-config startup-config", + "Copy configuration\n" + "Copy running config to... \n" + "Copy running config to startup config (same as write file)\n") + +/* Write current configuration into the terminal. */ +DEFUN (config_write_terminal, + config_write_terminal_cmd, + "write terminal", + "Write running configuration to memory, network, or terminal\n" + "Write to terminal\n") +{ + int i; + struct cmd_node *node; + + if (vty->type == VTY_SHELL_SERV) + { + for (i = 0; i < vector_max (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) + { + if ((*node->func) (vty)) + vty_out (vty, "!%s", VTY_NEWLINE); + } + } + else + { + vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, + VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + + for (i = 0; i < vector_max (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func) + { + if ((*node->func) (vty)) + vty_out (vty, "!%s", VTY_NEWLINE); + } + vty_out (vty, "end%s",VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +/* Write current configuration into the terminal. */ +ALIAS (config_write_terminal, + show_running_config_cmd, + "show running-config", + SHOW_STR + "running configuration\n") + +/* Write startup configuration into the terminal. */ +DEFUN (show_startup_config, + show_startup_config_cmd, + "show startup-config", + SHOW_STR + "Contentes of startup configuration\n") +{ + char buf[BUFSIZ]; + FILE *confp; + + confp = fopen (host.config, "r"); + if (confp == NULL) + { + vty_out (vty, "Can't open configuration file [%s]%s", + host.config, VTY_NEWLINE); + return CMD_WARNING; + } + + while (fgets (buf, BUFSIZ, confp)) + { + char *cp = buf; + + while (*cp != '\r' && *cp != '\n' && *cp != '\0') + cp++; + *cp = '\0'; + + vty_out (vty, "%s%s", buf, VTY_NEWLINE); + } + + fclose (confp); + + return CMD_SUCCESS; +} + +/* Hostname configuration */ +DEFUN (config_hostname, + hostname_cmd, + "hostname WORD", + "Set system's network name\n" + "This system's network name\n") +{ + if (!isalpha((int) *argv[0])) + { + vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.name) + XFREE (0, host.name); + + host.name = strdup (argv[0]); + return CMD_SUCCESS; +} + +DEFUN (config_no_hostname, + no_hostname_cmd, + "no hostname [HOSTNAME]", + NO_STR + "Reset system's network name\n" + "Host name of this router\n") +{ + if (host.name) + XFREE (0, host.name); + host.name = NULL; + return CMD_SUCCESS; +} + +/* VTY interface password set. */ +DEFUN (config_password, password_cmd, + "password (8|) WORD", + "Assign the terminal connection password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" + "The HIDDEN line password string\n") +{ + /* Argument check. */ + if (argc == 0) + { + vty_out (vty, "Please specify password.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 2) + { + if (*argv[0] == '8') + { + if (host.password) + XFREE (0, host.password); + host.password = NULL; + if (host.password_encrypt) + XFREE (0, host.password_encrypt); + host.password_encrypt = XSTRDUP (0, strdup (argv[1])); + return CMD_SUCCESS; + } + else + { + vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (!isalnum ((int) *argv[0])) + { + vty_out (vty, + "Please specify string starting with alphanumeric%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.password) + XFREE (0, host.password); + host.password = NULL; + + if (host.encrypt) + { + if (host.password_encrypt) + XFREE (0, host.password_encrypt); + host.password_encrypt = XSTRDUP (0, zencrypt (argv[0])); + } + else + host.password = XSTRDUP (0, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (config_password, password_text_cmd, + "password LINE", + "Assign the terminal connection password\n" + "The UNENCRYPTED (cleartext) line password\n") + +/* VTY enable password set. */ +DEFUN (config_enable_password, enable_password_cmd, + "enable password (8|) WORD", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" + "The HIDDEN 'enable' password string\n") +{ + /* Argument check. */ + if (argc == 0) + { + vty_out (vty, "Please specify password.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Crypt type is specified. */ + if (argc == 2) + { + if (*argv[0] == '8') + { + if (host.enable) + XFREE (0, host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + XFREE (0, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (0, argv[1]); + + return CMD_SUCCESS; + } + else + { + vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (!isalnum ((int) *argv[0])) + { + vty_out (vty, + "Please specify string starting with alphanumeric%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.enable) + XFREE (0, host.enable); + host.enable = NULL; + + /* Plain password input. */ + if (host.encrypt) + { + if (host.enable_encrypt) + XFREE (0, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (0, zencrypt (argv[0])); + } + else + host.enable = XSTRDUP (0, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (config_enable_password, + enable_password_text_cmd, + "enable password LINE", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "The UNENCRYPTED (cleartext) 'enable' password\n") + +/* VTY enable password delete. */ +DEFUN (no_config_enable_password, no_enable_password_cmd, + "no enable password", + NO_STR + "Modify enable password parameters\n" + "Assign the privileged level password\n") +{ + if (host.enable) + XFREE (0, host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + XFREE (0, host.enable_encrypt); + host.enable_encrypt = NULL; + + return CMD_SUCCESS; +} + +DEFUN (service_password_encrypt, + service_password_encrypt_cmd, + "service password-encryption", + "Set up miscellaneous service\n" + "Enable encrypted passwords\n") +{ + if (host.encrypt) + return CMD_SUCCESS; + + host.encrypt = 1; + + if (host.password) + { + if (host.password_encrypt) + XFREE (0, host.password_encrypt); + host.password_encrypt = XSTRDUP (0, zencrypt (host.password)); + } + if (host.enable) + { + if (host.enable_encrypt) + XFREE (0, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (0, zencrypt (host.enable)); + } + + return CMD_SUCCESS; +} + +DEFUN (no_service_password_encrypt, + no_service_password_encrypt_cmd, + "no service password-encryption", + NO_STR + "Set up miscellaneous service\n" + "Enable encrypted passwords\n") +{ + if (! host.encrypt) + return CMD_SUCCESS; + + host.encrypt = 0; + + if (host.password_encrypt) + XFREE (0, host.password_encrypt); + host.password_encrypt = NULL; + + if (host.enable_encrypt) + XFREE (0, host.enable_encrypt); + host.enable_encrypt = NULL; + + return CMD_SUCCESS; +} + +DEFUN (config_terminal_length, config_terminal_length_cmd, + "terminal length <0-512>", + "Set terminal line parameters\n" + "Set number of lines on a screen\n" + "Number of lines on screen (0 for no pausing)\n") +{ + int lines; + char *endptr = NULL; + + lines = strtol (argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') + { + vty_out (vty, "length is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + vty->lines = lines; + + return CMD_SUCCESS; +} + +DEFUN (config_terminal_no_length, config_terminal_no_length_cmd, + "terminal no length", + "Set terminal line parameters\n" + NO_STR + "Set number of lines on a screen\n") +{ + vty->lines = -1; + return CMD_SUCCESS; +} + +DEFUN (service_terminal_length, service_terminal_length_cmd, + "service terminal-length <0-512>", + "Set up miscellaneous service\n" + "System wide terminal length configuration\n" + "Number of lines of VTY (0 means no line control)\n") +{ + int lines; + char *endptr = NULL; + + lines = strtol (argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') + { + vty_out (vty, "length is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + host.lines = lines; + + return CMD_SUCCESS; +} + +DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, + "no service terminal-length [<0-512>]", + NO_STR + "Set up miscellaneous service\n" + "System wide terminal length configuration\n" + "Number of lines of VTY (0 means no line control)\n") +{ + host.lines = -1; + return CMD_SUCCESS; +} + +DEFUN (config_log_stdout, + config_log_stdout_cmd, + "log stdout", + "Logging control\n" + "Logging goes to stdout\n") +{ + zlog_set_flag (NULL, ZLOG_STDOUT); + host.log_stdout = 1; + return CMD_SUCCESS; +} + +DEFUN (no_config_log_stdout, + no_config_log_stdout_cmd, + "no log stdout", + NO_STR + "Logging control\n" + "Cancel logging to stdout\n") +{ + zlog_reset_flag (NULL, ZLOG_STDOUT); + host.log_stdout = 0; + return CMD_SUCCESS; +} + +DEFUN (config_log_file, + config_log_file_cmd, + "log file FILENAME", + "Logging control\n" + "Logging to file\n" + "Logging filename\n") +{ + int ret; + char *cwd; + char *fullpath; + + /* Path detection. */ + if (! IS_DIRECTORY_SEP (*argv[0])) + { + cwd = getcwd (NULL, MAXPATHLEN); + fullpath = XMALLOC (MTYPE_TMP, + strlen (cwd) + strlen (argv[0]) + 2); + sprintf (fullpath, "%s/%s", cwd, argv[0]); + } + else + fullpath = argv[0]; + + ret = zlog_set_file (NULL, ZLOG_FILE, fullpath); + + if (!ret) + { + vty_out (vty, "can't open logfile %s\n", argv[0]); + return CMD_WARNING; + } + + if (host.logfile) + XFREE (MTYPE_TMP, host.logfile); + + host.logfile = strdup (argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_config_log_file, + no_config_log_file_cmd, + "no log file [FILENAME]", + NO_STR + "Logging control\n" + "Cancel logging to file\n" + "Logging file name\n") +{ + zlog_reset_file (NULL); + + if (host.logfile) + XFREE (MTYPE_TMP, host.logfile); + + host.logfile = NULL; + + return CMD_SUCCESS; +} + +DEFUN (config_log_syslog, + config_log_syslog_cmd, + "log syslog", + "Logging control\n" + "Logging goes to syslog\n") +{ + zlog_set_flag (NULL, ZLOG_SYSLOG); + host.log_syslog = 1; + return CMD_SUCCESS; +} + +DEFUN (no_config_log_syslog, + no_config_log_syslog_cmd, + "no log syslog", + NO_STR + "Logging control\n" + "Cancel logging to syslog\n") +{ + zlog_reset_flag (NULL, ZLOG_SYSLOG); + host.log_syslog = 0; + return CMD_SUCCESS; +} + +DEFUN (config_log_trap, + config_log_trap_cmd, + "log trap (emergencies|alerts|critical|errors|warnings|notifications|informational|debugging)", + "Logging control\n" + "Limit logging to specifed level\n") +{ + int new_level ; + + for ( new_level = 0 ; zlog_priority [new_level] != NULL ; new_level ++ ) + { + if ( strcmp ( argv[0], zlog_priority [new_level] ) == 0 ) + /* found new logging level */ + { + zlog_default->maskpri = new_level; + return CMD_SUCCESS; + } + } + return CMD_ERR_NO_MATCH; +} + +DEFUN (no_config_log_trap, + no_config_log_trap_cmd, + "no log trap", + NO_STR + "Logging control\n" + "Permit all logging information\n") +{ + zlog_default->maskpri = LOG_DEBUG; + return CMD_SUCCESS; +} + +DEFUN (config_log_record_priority, + config_log_record_priority_cmd, + "log record-priority", + "Logging control\n" + "Log the priority of the message within the message\n") +{ + zlog_default->record_priority = 1 ; + return CMD_SUCCESS; +} + +DEFUN (no_config_log_record_priority, + no_config_log_record_priority_cmd, + "no log record-priority", + NO_STR + "Logging control\n" + "Do not log the priority of the message within the message\n") +{ + zlog_default->record_priority = 0 ; + return CMD_SUCCESS; +} + + +DEFUN (banner_motd_default, + banner_motd_default_cmd, + "banner motd default", + "Set banner string\n" + "Strings for motd\n" + "Default string\n") +{ + host.motd = default_motd; + return CMD_SUCCESS; +} + +DEFUN (no_banner_motd, + no_banner_motd_cmd, + "no banner motd", + NO_STR + "Set banner string\n" + "Strings for motd\n") +{ + host.motd = NULL; + return CMD_SUCCESS; +} + +/* Set config filename. Called from vty.c */ +void +host_config_set (char *filename) +{ + host.config = strdup (filename); +} + +void +install_default (enum node_type node) +{ + install_element (node, &config_exit_cmd); + install_element (node, &config_quit_cmd); + install_element (node, &config_end_cmd); + install_element (node, &config_help_cmd); + install_element (node, &config_list_cmd); + + install_element (node, &config_write_terminal_cmd); + install_element (node, &config_write_file_cmd); + install_element (node, &config_write_memory_cmd); + install_element (node, &config_write_cmd); + install_element (node, &show_running_config_cmd); +} + +/* Initialize command interface. Install basic nodes and commands. */ +void +cmd_init (int terminal) +{ + /* Allocate initial top vector of commands. */ + cmdvec = vector_init (VECTOR_MIN_SIZE); + + /* Default host value settings. */ + host.name = NULL; + host.password = NULL; + host.enable = NULL; + host.logfile = NULL; + host.config = NULL; + host.lines = -1; + host.motd = default_motd; + + /* Install top nodes. */ + install_node (&view_node, NULL); + install_node (&enable_node, NULL); + install_node (&auth_node, NULL); + install_node (&auth_enable_node, NULL); + install_node (&config_node, config_write_host); + + /* Each node's basic commands. */ + install_element (VIEW_NODE, &show_version_cmd); + if (terminal) + { + install_element (VIEW_NODE, &config_list_cmd); + install_element (VIEW_NODE, &config_exit_cmd); + install_element (VIEW_NODE, &config_quit_cmd); + install_element (VIEW_NODE, &config_help_cmd); + install_element (VIEW_NODE, &config_enable_cmd); + install_element (VIEW_NODE, &config_terminal_length_cmd); + install_element (VIEW_NODE, &config_terminal_no_length_cmd); + } + + if (terminal) + { + install_default (ENABLE_NODE); + install_element (ENABLE_NODE, &config_disable_cmd); + install_element (ENABLE_NODE, &config_terminal_cmd); + install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); + } + install_element (ENABLE_NODE, &show_startup_config_cmd); + install_element (ENABLE_NODE, &show_version_cmd); + install_element (ENABLE_NODE, &config_terminal_length_cmd); + install_element (ENABLE_NODE, &config_terminal_no_length_cmd); + + if (terminal) + install_default (CONFIG_NODE); + install_element (CONFIG_NODE, &hostname_cmd); + install_element (CONFIG_NODE, &no_hostname_cmd); + install_element (CONFIG_NODE, &password_cmd); + install_element (CONFIG_NODE, &password_text_cmd); + install_element (CONFIG_NODE, &enable_password_cmd); + install_element (CONFIG_NODE, &enable_password_text_cmd); + install_element (CONFIG_NODE, &no_enable_password_cmd); + if (terminal) + { + install_element (CONFIG_NODE, &config_log_stdout_cmd); + install_element (CONFIG_NODE, &no_config_log_stdout_cmd); + install_element (CONFIG_NODE, &config_log_file_cmd); + install_element (CONFIG_NODE, &no_config_log_file_cmd); + install_element (CONFIG_NODE, &config_log_syslog_cmd); + install_element (CONFIG_NODE, &no_config_log_syslog_cmd); + install_element (CONFIG_NODE, &config_log_trap_cmd); + install_element (CONFIG_NODE, &no_config_log_trap_cmd); + install_element (CONFIG_NODE, &config_log_record_priority_cmd); + install_element (CONFIG_NODE, &no_config_log_record_priority_cmd); + install_element (CONFIG_NODE, &service_password_encrypt_cmd); + install_element (CONFIG_NODE, &no_service_password_encrypt_cmd); + install_element (CONFIG_NODE, &banner_motd_default_cmd); + install_element (CONFIG_NODE, &no_banner_motd_cmd); + install_element (CONFIG_NODE, &service_terminal_length_cmd); + install_element (CONFIG_NODE, &no_service_terminal_length_cmd); + } + + srand(time(NULL)); +} diff --git a/lib/command.h b/lib/command.h new file mode 100644 index 00000000..3009b261 --- /dev/null +++ b/lib/command.h @@ -0,0 +1,308 @@ +/* + * Zebra configuration command interface routine + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_COMMAND_H +#define _ZEBRA_COMMAND_H + +#include "vector.h" +#include "vty.h" + +/* Host configuration variable */ +struct host +{ + /* Host name of this router. */ + char *name; + + /* Password for vty interface. */ + char *password; + char *password_encrypt; + + /* Enable password */ + char *enable; + char *enable_encrypt; + + /* System wide terminal lines. */ + int lines; + + /* Log filename. */ + char *logfile; + + /* Log stdout. */ + u_char log_stdout; + + /* Log syslog. */ + u_char log_syslog; + + /* config file name of this host */ + char *config; + + /* Flags for services */ + int advanced; + int encrypt; + + /* Banner configuration. */ + char *motd; +}; + +/* There are some command levels which called from command node. */ +enum node_type +{ + AUTH_NODE, /* Authentication mode of vty interface. */ + VIEW_NODE, /* View node. Default mode of vty interface. */ + AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ + ENABLE_NODE, /* Enable node. */ + CONFIG_NODE, /* Config node. Default mode of config file. */ + DEBUG_NODE, /* Debug node. */ + AAA_NODE, /* AAA node. */ + KEYCHAIN_NODE, /* Key-chain node. */ + KEYCHAIN_KEY_NODE, /* Key-chain key node. */ + INTERFACE_NODE, /* Interface mode node. */ + ZEBRA_NODE, /* zebra connection node. */ + TABLE_NODE, /* rtm_table selection node. */ + RIP_NODE, /* RIP protocol mode node. */ + RIPNG_NODE, /* RIPng protocol mode node. */ + BGP_NODE, /* BGP protocol mode which includes BGP4+ */ + BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ + BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ + BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ + BGP_IPV6_NODE, /* BGP IPv6 address family */ + OSPF_NODE, /* OSPF protocol mode */ + OSPF6_NODE, /* OSPF protocol for IPv6 mode */ + MASC_NODE, /* MASC for multicast. */ + IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ + IP_NODE, /* Static ip route node. */ + ACCESS_NODE, /* Access list node. */ + PREFIX_NODE, /* Prefix list node. */ + ACCESS_IPV6_NODE, /* Access list node. */ + PREFIX_IPV6_NODE, /* Prefix list node. */ + AS_LIST_NODE, /* AS list node. */ + COMMUNITY_LIST_NODE, /* Community list node. */ + RMAP_NODE, /* Route map node. */ + SMUX_NODE, /* SNMP configuration node. */ + DUMP_NODE, /* Packet dump node. */ + FORWARDING_NODE, /* IP forwarding node. */ + VTY_NODE /* Vty node. */ +}; + +/* Node which has some commands and prompt string and configuration + function pointer . */ +struct cmd_node +{ + /* Node index. */ + enum node_type node; + + /* Prompt character at vty interface. */ + char *prompt; + + /* Is this node's configuration goes to vtysh ? */ + int vtysh; + + /* Node's configuration write function */ + int (*func) (struct vty *); + + /* Vector of this node's command list. */ + vector cmd_vector; +}; + +/* Structure of command element. */ +struct cmd_element +{ + char *string; /* Command specification by string. */ + int (*func) (struct cmd_element *, struct vty *, int, char **); + char *doc; /* Documentation of this command. */ + int daemon; /* Daemon to which this command belong. */ + vector strvec; /* Pointing out each description vector. */ + int cmdsize; /* Command index count. */ + char *config; /* Configuration string */ + vector subconfig; /* Sub configuration string */ +}; + +/* Command description structure. */ +struct desc +{ + char *cmd; /* Command string. */ + char *str; /* Command's description. */ +}; + +/* Return value of the commands. */ +#define CMD_SUCCESS 0 +#define CMD_WARNING 1 +#define CMD_ERR_NO_MATCH 2 +#define CMD_ERR_AMBIGUOUS 3 +#define CMD_ERR_INCOMPLETE 4 +#define CMD_ERR_EXEED_ARGC_MAX 5 +#define CMD_ERR_NOTHING_TODO 6 +#define CMD_COMPLETE_FULL_MATCH 7 +#define CMD_COMPLETE_MATCH 8 +#define CMD_COMPLETE_LIST_MATCH 9 +#define CMD_SUCCESS_DAEMON 10 + +/* Argc max counts. */ +#define CMD_ARGC_MAX 25 + +/* Turn off these macros when uisng cpp with extract.pl */ +#ifndef VTYSH_EXTRACT_PL + +/* DEFUN for vty command interafce. Little bit hacky ;-). */ +#define DEFUN(funcname, cmdname, cmdstr, helpstr) \ + int funcname (struct cmd_element *, struct vty *, int, char **); \ + struct cmd_element cmdname = \ + { \ + cmdstr, \ + funcname, \ + helpstr \ + }; \ + int funcname \ + (struct cmd_element *self, struct vty *vty, int argc, char **argv) + +/* DEFUN_NOSH for commands that vtysh should ignore */ +#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \ + DEFUN(funcname, cmdname, cmdstr, helpstr) + +/* DEFSH for vtysh. */ +#define DEFSH(daemon, cmdname, cmdstr, helpstr) \ + struct cmd_element cmdname = \ + { \ + cmdstr, \ + NULL, \ + helpstr, \ + daemon \ + }; \ + +/* DEFUN + DEFSH */ +#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \ + int funcname (struct cmd_element *, struct vty *, int, char **); \ + struct cmd_element cmdname = \ + { \ + cmdstr, \ + funcname, \ + helpstr, \ + daemon \ + }; \ + int funcname \ + (struct cmd_element *self, struct vty *vty, int argc, char **argv) + +/* ALIAS macro which define existing command's alias. */ +#define ALIAS(funcname, cmdname, cmdstr, helpstr) \ + struct cmd_element cmdname = \ + { \ + cmdstr, \ + funcname, \ + helpstr \ + }; + +#endif /* VTYSH_EXTRACT_PL */ + +/* Some macroes */ +#define CMD_OPTION(S) ((S[0]) == '[') +#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<')) +#define CMD_VARARG(S) ((S[0]) == '.') +#define CMD_RANGE(S) ((S[0] == '<')) + +#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0)) +#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0)) +#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0)) +#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0)) + +/* Common descriptions. */ +#define SHOW_STR "Show running system information\n" +#define IP_STR "IP information\n" +#define IPV6_STR "IPv6 information\n" +#define NO_STR "Negate a command or set its defaults\n" +#define CLEAR_STR "Reset functions\n" +#define RIP_STR "RIP information\n" +#define BGP_STR "BGP information\n" +#define OSPF_STR "OSPF information\n" +#define NEIGHBOR_STR "Specify neighbor router\n" +#define DEBUG_STR "Debugging functions (see also 'undebug')\n" +#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n" +#define ROUTER_STR "Enable a routing process\n" +#define AS_STR "AS number\n" +#define MBGP_STR "MBGP information\n" +#define MATCH_STR "Match values from routing table\n" +#define SET_STR "Set values in destination routing protocol\n" +#define OUT_STR "Filter outgoing routing updates\n" +#define IN_STR "Filter incoming routing updates\n" +#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n" +#define OSPF6_NUMBER_STR "Specify by number\n" +#define INTERFACE_STR "Interface infomation\n" +#define IFNAME_STR "Interface name(e.g. ep0)\n" +#define IP6_STR "IPv6 Information\n" +#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n" +#define OSPF6_ROUTER_STR "Enable a routing process\n" +#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n" +#define SECONDS_STR "<1-65535> Seconds\n" +#define ROUTE_STR "Routing Table\n" +#define PREFIX_LIST_STR "Build a prefix list\n" +#define OSPF6_DUMP_TYPE_LIST \ +"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)" + +#define CONF_BACKUP_EXT ".sav" + +/* IPv4 only machine should not accept IPv6 address for peer's IP + address. So we replace VTY command string like below. */ +#ifdef HAVE_IPV6 +#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) " +#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) " +#define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n" +#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nNeighbor tag\n" +#else +#define NEIGHBOR_CMD "neighbor A.B.C.D " +#define NO_NEIGHBOR_CMD "no neighbor A.B.C.D " +#define NEIGHBOR_ADDR_STR "Neighbor address\n" +#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) " +#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) " +#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n" +#endif /* HAVE_IPV6 */ + +/* Prototypes. */ +void install_node (struct cmd_node *, int (*) (struct vty *)); +void install_default (enum node_type); +void install_element (enum node_type, struct cmd_element *); +void sort_node (); + +char *argv_concat (char **, int, int); +vector cmd_make_strvec (char *); +void cmd_free_strvec (vector); +vector cmd_describe_command (); +char **cmd_complete_command (); +char *cmd_prompt (enum node_type); +int config_from_file (struct vty *, FILE *); +int cmd_execute_command (vector, struct vty *, struct cmd_element **); +int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **); +void config_replace_string (struct cmd_element *, char *, ...); +void cmd_init (int); + +/* Export typical functions. */ +extern struct cmd_element config_end_cmd; +extern struct cmd_element config_exit_cmd; +extern struct cmd_element config_quit_cmd; +extern struct cmd_element config_help_cmd; +extern struct cmd_element config_list_cmd; +int config_exit (struct cmd_element *, struct vty *, int, char **); +int config_help (struct cmd_element *, struct vty *, int, char **); +char *host_config_file (); +void host_config_set (char *); + +#endif /* _ZEBRA_COMMAND_H */ diff --git a/lib/daemon.c b/lib/daemon.c new file mode 100644 index 00000000..dfd26b36 --- /dev/null +++ b/lib/daemon.c @@ -0,0 +1,80 @@ +/* + * Daemonize routine + * Copyright (C) 1997, 1999 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#ifndef HAVE_DAEMON + +/* Daemonize myself. */ +int +daemon (int nochdir, int noclose) +{ + pid_t pid; + + pid = fork (); + + /* In case of fork is error. */ + if (pid < 0) + { + perror ("fork"); + return -1; + } + + /* In case of this is parent process. */ + if (pid != 0) + exit (0); + + /* Become session leader and get pid. */ + pid = setsid(); + + if (pid < -1) + { + perror ("setsid"); + return -1; + } + + /* Change directory to root. */ + if (! nochdir) + chdir ("/"); + + /* File descriptor close. */ + if (! noclose) + { + int fd; + + fd = open ("/dev/null", O_RDWR, 0); + if (fd != -1) + { + dup2 (fd, STDIN_FILENO); + dup2 (fd, STDOUT_FILENO); + dup2 (fd, STDERR_FILENO); + if (fd > 2) + close (fd); + } + } + + umask (0027); + + return 0; +} + +#endif /* HAVE_DAEMON */ diff --git a/lib/distribute.c b/lib/distribute.c new file mode 100644 index 00000000..d5893a5b --- /dev/null +++ b/lib/distribute.c @@ -0,0 +1,709 @@ +/* Distribute list functions + * Copyright (C) 1998, 1999 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "hash.h" +#include "if.h" +#include "filter.h" +#include "command.h" +#include "distribute.h" +#include "memory.h" + +/* Hash of distribute list. */ +struct hash *disthash; + +/* Hook functions. */ +void (*distribute_add_hook) (struct distribute *); +void (*distribute_delete_hook) (struct distribute *); + +struct distribute * +distribute_new () +{ + struct distribute *new; + + new = XMALLOC (MTYPE_DISTRIBUTE, sizeof (struct distribute)); + memset (new, 0, sizeof (struct distribute)); + + return new; +} + +/* Free distribute object. */ +void +distribute_free (struct distribute *dist) +{ + if (dist->ifname) + free (dist->ifname); + + if (dist->list[DISTRIBUTE_IN]) + free (dist->list[DISTRIBUTE_IN]); + if (dist->list[DISTRIBUTE_OUT]) + free (dist->list[DISTRIBUTE_OUT]); + + if (dist->prefix[DISTRIBUTE_IN]) + free (dist->prefix[DISTRIBUTE_IN]); + if (dist->prefix[DISTRIBUTE_OUT]) + free (dist->prefix[DISTRIBUTE_OUT]); + + XFREE (MTYPE_DISTRIBUTE, dist); +} + +/* Lookup interface's distribute list. */ +struct distribute * +distribute_lookup (char *ifname) +{ + struct distribute key; + struct distribute *dist; + + key.ifname = ifname; + + dist = hash_lookup (disthash, &key); + + return dist; +} + +void +distribute_list_add_hook (void (*func) (struct distribute *)) +{ + distribute_add_hook = func; +} + +void +distribute_list_delete_hook (void (*func) (struct distribute *)) +{ + distribute_delete_hook = func; +} + +void * +distribute_hash_alloc (struct distribute *arg) +{ + struct distribute *dist; + + dist = distribute_new (); + if (arg->ifname) + dist->ifname = strdup (arg->ifname); + else + dist->ifname = NULL; + return dist; +} + +/* Make new distribute list and push into hash. */ +struct distribute * +distribute_get (char *ifname) +{ + struct distribute key; + + key.ifname = ifname; + + return hash_get (disthash, &key, distribute_hash_alloc); +} + +unsigned int +distribute_hash_make (struct distribute *dist) +{ + unsigned int key; + int i; + + key = 0; + if (dist->ifname) + for (i = 0; i < strlen (dist->ifname); i++) + key += dist->ifname[i]; + + return key; +} + +/* If two distribute-list have same value then return 1 else return + 0. This function is used by hash package. */ +int +distribute_cmp (struct distribute *dist1, struct distribute *dist2) +{ + if (dist1->ifname && dist2->ifname) + if (strcmp (dist1->ifname, dist2->ifname) == 0) + return 1; + if (! dist1->ifname && ! dist2->ifname) + return 1; + return 0; +} + +/* Set access-list name to the distribute list. */ +struct distribute * +distribute_list_set (char *ifname, enum distribute_type type, char *alist_name) +{ + struct distribute *dist; + + dist = distribute_get (ifname); + + if (type == DISTRIBUTE_IN) + { + if (dist->list[DISTRIBUTE_IN]) + free (dist->list[DISTRIBUTE_IN]); + dist->list[DISTRIBUTE_IN] = strdup (alist_name); + } + if (type == DISTRIBUTE_OUT) + { + if (dist->list[DISTRIBUTE_OUT]) + free (dist->list[DISTRIBUTE_OUT]); + dist->list[DISTRIBUTE_OUT] = strdup (alist_name); + } + + /* Apply this distribute-list to the interface. */ + (*distribute_add_hook) (dist); + + return dist; +} + +/* Unset distribute-list. If matched distribute-list exist then + return 1. */ +int +distribute_list_unset (char *ifname, enum distribute_type type, + char *alist_name) +{ + struct distribute *dist; + + dist = distribute_lookup (ifname); + if (!dist) + return 0; + + if (type == DISTRIBUTE_IN) + { + if (!dist->list[DISTRIBUTE_IN]) + return 0; + if (strcmp (dist->list[DISTRIBUTE_IN], alist_name) != 0) + return 0; + + free (dist->list[DISTRIBUTE_IN]); + dist->list[DISTRIBUTE_IN] = NULL; + } + + if (type == DISTRIBUTE_OUT) + { + if (!dist->list[DISTRIBUTE_OUT]) + return 0; + if (strcmp (dist->list[DISTRIBUTE_OUT], alist_name) != 0) + return 0; + + free (dist->list[DISTRIBUTE_OUT]); + dist->list[DISTRIBUTE_OUT] = NULL; + } + + /* Apply this distribute-list to the interface. */ + (*distribute_delete_hook) (dist); + + /* If both out and in is NULL then free distribute list. */ + if (dist->list[DISTRIBUTE_IN] == NULL && + dist->list[DISTRIBUTE_OUT] == NULL && + dist->prefix[DISTRIBUTE_IN] == NULL && + dist->prefix[DISTRIBUTE_OUT] == NULL) + { + hash_release (disthash, dist); + distribute_free (dist); + } + + return 1; +} + +/* Set access-list name to the distribute list. */ +struct distribute * +distribute_list_prefix_set (char *ifname, enum distribute_type type, + char *plist_name) +{ + struct distribute *dist; + + dist = distribute_get (ifname); + + if (type == DISTRIBUTE_IN) + { + if (dist->prefix[DISTRIBUTE_IN]) + free (dist->prefix[DISTRIBUTE_IN]); + dist->prefix[DISTRIBUTE_IN] = strdup (plist_name); + } + if (type == DISTRIBUTE_OUT) + { + if (dist->prefix[DISTRIBUTE_OUT]) + free (dist->prefix[DISTRIBUTE_OUT]); + dist->prefix[DISTRIBUTE_OUT] = strdup (plist_name); + } + + /* Apply this distribute-list to the interface. */ + (*distribute_add_hook) (dist); + + return dist; +} + +/* Unset distribute-list. If matched distribute-list exist then + return 1. */ +int +distribute_list_prefix_unset (char *ifname, enum distribute_type type, + char *plist_name) +{ + struct distribute *dist; + + dist = distribute_lookup (ifname); + if (!dist) + return 0; + + if (type == DISTRIBUTE_IN) + { + if (!dist->prefix[DISTRIBUTE_IN]) + return 0; + if (strcmp (dist->prefix[DISTRIBUTE_IN], plist_name) != 0) + return 0; + + free (dist->prefix[DISTRIBUTE_IN]); + dist->prefix[DISTRIBUTE_IN] = NULL; + } + + if (type == DISTRIBUTE_OUT) + { + if (!dist->prefix[DISTRIBUTE_OUT]) + return 0; + if (strcmp (dist->prefix[DISTRIBUTE_OUT], plist_name) != 0) + return 0; + + free (dist->prefix[DISTRIBUTE_OUT]); + dist->prefix[DISTRIBUTE_OUT] = NULL; + } + + /* Apply this distribute-list to the interface. */ + (*distribute_delete_hook) (dist); + + /* If both out and in is NULL then free distribute list. */ + if (dist->list[DISTRIBUTE_IN] == NULL && + dist->list[DISTRIBUTE_OUT] == NULL && + dist->prefix[DISTRIBUTE_IN] == NULL && + dist->prefix[DISTRIBUTE_OUT] == NULL) + { + hash_release (disthash, dist); + distribute_free (dist); + } + + return 1; +} + +DEFUN (distribute_list_all, + distribute_list_all_cmd, + "distribute-list WORD (in|out)", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + enum distribute_type type; + struct distribute *dist; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + dist = distribute_list_set (NULL, type, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_distribute_list_all, + no_distribute_list_all_cmd, + "no distribute-list WORD (in|out)", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_unset (NULL, type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN (distribute_list, + distribute_list_cmd, + "distribute-list WORD (in|out) WORD", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + enum distribute_type type; + struct distribute *dist; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + dist = distribute_list_set (argv[2], type, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_districute_list, no_distribute_list_cmd, + "no distribute-list WORD (in|out) WORD", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_unset (argv[2], type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN (districute_list_prefix_all, + distribute_list_prefix_all_cmd, + "distribute-list prefix WORD (in|out)", + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + enum distribute_type type; + struct distribute *dist; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + dist = distribute_list_prefix_set (NULL, type, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_districute_list_prefix_all, + no_distribute_list_prefix_all_cmd, + "no distribute-list prefix WORD (in|out)", + NO_STR + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_prefix_unset (NULL, type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN (districute_list_prefix, distribute_list_prefix_cmd, + "distribute-list prefix WORD (in|out) WORD", + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + enum distribute_type type; + struct distribute *dist; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + dist = distribute_list_prefix_set (argv[2], type, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_districute_list_prefix, no_distribute_list_prefix_cmd, + "no distribute-list prefix WORD (in|out) WORD", + NO_STR + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_prefix_unset (argv[2], type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +int +config_show_distribute (struct vty *vty) +{ + int i; + struct hash_backet *mp; + struct distribute *dist; + + /* Output filter configuration. */ + dist = distribute_lookup (NULL); + if (dist && (dist->list[DISTRIBUTE_OUT] || dist->prefix[DISTRIBUTE_OUT])) + { + vty_out (vty, " Outgoing update filter list for all interface is"); + if (dist->list[DISTRIBUTE_OUT]) + vty_out (vty, " %s", dist->list[DISTRIBUTE_OUT]); + if (dist->prefix[DISTRIBUTE_OUT]) + vty_out (vty, "%s (prefix-list) %s", + dist->list[DISTRIBUTE_OUT] ? "," : "", + dist->prefix[DISTRIBUTE_OUT]); + vty_out (vty, "%s", VTY_NEWLINE); + } + else + vty_out (vty, " Outgoing update filter list for all interface is not set%s", VTY_NEWLINE); + + for (i = 0; i < disthash->size; i++) + for (mp = disthash->index[i]; mp; mp = mp->next) + { + dist = mp->data; + if (dist->ifname) + if (dist->list[DISTRIBUTE_OUT] || dist->prefix[DISTRIBUTE_OUT]) + { + vty_out (vty, " %s filtered by", dist->ifname); + if (dist->list[DISTRIBUTE_OUT]) + vty_out (vty, " %s", dist->list[DISTRIBUTE_OUT]); + if (dist->prefix[DISTRIBUTE_OUT]) + vty_out (vty, "%s (prefix-list) %s", + dist->list[DISTRIBUTE_OUT] ? "," : "", + dist->prefix[DISTRIBUTE_OUT]); + vty_out (vty, "%s", VTY_NEWLINE); + } + } + + + /* Input filter configuration. */ + dist = distribute_lookup (NULL); + if (dist && (dist->list[DISTRIBUTE_IN] || dist->prefix[DISTRIBUTE_IN])) + { + vty_out (vty, " Incoming update filter list for all interface is"); + if (dist->list[DISTRIBUTE_IN]) + vty_out (vty, " %s", dist->list[DISTRIBUTE_IN]); + if (dist->prefix[DISTRIBUTE_IN]) + vty_out (vty, "%s (prefix-list) %s", + dist->list[DISTRIBUTE_IN] ? "," : "", + dist->prefix[DISTRIBUTE_IN]); + vty_out (vty, "%s", VTY_NEWLINE); + } + else + vty_out (vty, " Incoming update filter list for all interface is not set%s", VTY_NEWLINE); + + for (i = 0; i < disthash->size; i++) + for (mp = disthash->index[i]; mp; mp = mp->next) + { + dist = mp->data; + if (dist->ifname) + if (dist->list[DISTRIBUTE_IN] || dist->prefix[DISTRIBUTE_IN]) + { + vty_out (vty, " %s filtered by", dist->ifname); + if (dist->list[DISTRIBUTE_IN]) + vty_out (vty, " %s", dist->list[DISTRIBUTE_IN]); + if (dist->prefix[DISTRIBUTE_IN]) + vty_out (vty, "%s (prefix-list) %s", + dist->list[DISTRIBUTE_IN] ? "," : "", + dist->prefix[DISTRIBUTE_IN]); + vty_out (vty, "%s", VTY_NEWLINE); + } + } + return 0; +} + +/* Configuration write function. */ +int +config_write_distribute (struct vty *vty) +{ + int i; + struct hash_backet *mp; + int write = 0; + + for (i = 0; i < disthash->size; i++) + for (mp = disthash->index[i]; mp; mp = mp->next) + { + struct distribute *dist; + + dist = mp->data; + + if (dist->list[DISTRIBUTE_IN]) + { + vty_out (vty, " distribute-list %s in %s%s", + dist->list[DISTRIBUTE_IN], + dist->ifname ? dist->ifname : "", + VTY_NEWLINE); + write++; + } + + if (dist->list[DISTRIBUTE_OUT]) + { + vty_out (vty, " distribute-list %s out %s%s", + + dist->list[DISTRIBUTE_OUT], + dist->ifname ? dist->ifname : "", + VTY_NEWLINE); + write++; + } + + if (dist->prefix[DISTRIBUTE_IN]) + { + vty_out (vty, " distribute-list prefix %s in %s%s", + dist->prefix[DISTRIBUTE_IN], + dist->ifname ? dist->ifname : "", + VTY_NEWLINE); + write++; + } + + if (dist->prefix[DISTRIBUTE_OUT]) + { + vty_out (vty, " distribute-list prefix %s out %s%s", + dist->prefix[DISTRIBUTE_OUT], + dist->ifname ? dist->ifname : "", + VTY_NEWLINE); + write++; + } + } + return write; +} + +/* Clear all distribute list. */ +void +distribute_list_reset () +{ + hash_clean (disthash, (void (*) (void *)) distribute_free); +} + +/* Initialize distribute list related hash. */ +void +distribute_list_init (int node) +{ + disthash = hash_create (distribute_hash_make, distribute_cmp); + + install_element (node, &distribute_list_all_cmd); + install_element (node, &no_distribute_list_all_cmd); + + install_element (node, &distribute_list_cmd); + install_element (node, &no_distribute_list_cmd); + + install_element (node, &distribute_list_prefix_all_cmd); + install_element (node, &no_distribute_list_prefix_all_cmd); + + install_element (node, &distribute_list_prefix_cmd); + install_element (node, &no_distribute_list_prefix_cmd); +} diff --git a/lib/distribute.h b/lib/distribute.h new file mode 100644 index 00000000..330126b9 --- /dev/null +++ b/lib/distribute.h @@ -0,0 +1,57 @@ +/* Distribute list functions header + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_DISTRIBUTE_H +#define _ZEBRA_DISTRIBUTE_H + +/* Disctirubte list types. */ +enum distribute_type +{ + DISTRIBUTE_IN, + DISTRIBUTE_OUT, + DISTRIBUTE_MAX +}; + +struct distribute +{ + /* Name of the interface. */ + char *ifname; + + /* Filter name of `in' and `out' */ + char *list[DISTRIBUTE_MAX]; + + /* prefix-list name of `in' and `out' */ + char *prefix[DISTRIBUTE_MAX]; +}; + +/* Prototypes for distribute-list. */ +void distribute_list_init (int); +void distribute_list_reset (void); +void distribute_list_add_hook (void (*) (struct distribute *)); +void distribute_list_delete_hook (void (*) (struct distribute *)); +struct distribute *distribute_lookup (char *); +int config_write_distribute (struct vty *); +int config_show_distribute (struct vty *); + +enum filter_type distribute_apply_in (struct interface *, struct prefix *); +enum filter_type distribute_apply_out (struct interface *, struct prefix *); + +#endif /* _ZEBRA_DISTRIBUTE_H */ diff --git a/lib/filter.c b/lib/filter.c new file mode 100644 index 00000000..a483ce23 --- /dev/null +++ b/lib/filter.c @@ -0,0 +1,2058 @@ +/* Route filtering function. + * Copyright (C) 1998, 1999 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "prefix.h" +#include "filter.h" +#include "memory.h" +#include "command.h" +#include "sockunion.h" +#include "buffer.h" + +struct filter_cisco +{ + /* Cisco access-list */ + int extended; + struct in_addr addr; + struct in_addr addr_mask; + struct in_addr mask; + struct in_addr mask_mask; +}; + +struct filter_zebra +{ + /* If this filter is "exact" match then this flag is set. */ + int exact; + + /* Prefix information. */ + struct prefix prefix; +}; + +/* Filter element of access list */ +struct filter +{ + /* For doubly linked list. */ + struct filter *next; + struct filter *prev; + + /* Filter type information. */ + enum filter_type type; + + /* Cisco access-list */ + int cisco; + + union + { + struct filter_cisco cfilter; + struct filter_zebra zfilter; + } u; +}; + +/* List of access_list. */ +struct access_list_list +{ + struct access_list *head; + struct access_list *tail; +}; + +/* Master structure of access_list. */ +struct access_master +{ + /* List of access_list which name is number. */ + struct access_list_list num; + + /* List of access_list which name is string. */ + struct access_list_list str; + + /* Hook function which is executed when new access_list is added. */ + void (*add_hook) (); + + /* Hook function which is executed when access_list is deleted. */ + void (*delete_hook) (); +}; + +/* Static structure for IPv4 access_list's master. */ +static struct access_master access_master_ipv4 = +{ + {NULL, NULL}, + {NULL, NULL}, + NULL, + NULL, +}; + +#ifdef HAVE_IPV6 +/* Static structure for IPv6 access_list's master. */ +static struct access_master access_master_ipv6 = +{ + {NULL, NULL}, + {NULL, NULL}, + NULL, + NULL, +}; +#endif /* HAVE_IPV6 */ + +struct access_master * +access_master_get (afi_t afi) +{ + if (afi == AFI_IP) + return &access_master_ipv4; +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + return &access_master_ipv6; +#endif /* HAVE_IPV6 */ + return NULL; +} + +/* Allocate new filter structure. */ +struct filter * +filter_new () +{ + return (struct filter *) XCALLOC (MTYPE_ACCESS_FILTER, + sizeof (struct filter)); +} + +void +filter_free (struct filter *filter) +{ + XFREE (MTYPE_ACCESS_FILTER, filter); +} + +/* Return string of filter_type. */ +static char * +filter_type_str (struct filter *filter) +{ + switch (filter->type) + { + case FILTER_PERMIT: + return "permit"; + break; + case FILTER_DENY: + return "deny"; + break; + case FILTER_DYNAMIC: + return "dynamic"; + break; + default: + return ""; + break; + } +} + +/* If filter match to the prefix then return 1. */ +static int +filter_match_cisco (struct filter *mfilter, struct prefix *p) +{ + struct filter_cisco *filter; + struct in_addr mask; + u_int32_t check_addr; + u_int32_t check_mask; + + filter = &mfilter->u.cfilter; + check_addr = p->u.prefix4.s_addr & ~filter->addr_mask.s_addr; + + if (filter->extended) + { + masklen2ip (p->prefixlen, &mask); + check_mask = mask.s_addr & ~filter->mask_mask.s_addr; + + if (memcmp (&check_addr, &filter->addr.s_addr, 4) == 0 + && memcmp (&check_mask, &filter->mask.s_addr, 4) == 0) + return 1; + } + else if (memcmp (&check_addr, &filter->addr.s_addr, 4) == 0) + return 1; + + return 0; +} + +/* If filter match to the prefix then return 1. */ +static int +filter_match_zebra (struct filter *mfilter, struct prefix *p) +{ + struct filter_zebra *filter; + + filter = &mfilter->u.zfilter; + + if (filter->prefix.family == p->family) + { + if (filter->exact) + { + if (filter->prefix.prefixlen == p->prefixlen) + return prefix_match (&filter->prefix, p); + else + return 0; + } + else + return prefix_match (&filter->prefix, p); + } + else + return 0; +} + +/* Allocate new access list structure. */ +struct access_list * +access_list_new () +{ + return (struct access_list *) XCALLOC (MTYPE_ACCESS_LIST, + sizeof (struct access_list)); +} + +/* Free allocated access_list. */ +void +access_list_free (struct access_list *access) +{ + XFREE (MTYPE_ACCESS_LIST, access); +} + +/* Delete access_list from access_master and free it. */ +void +access_list_delete (struct access_list *access) +{ + struct filter *filter; + struct filter *next; + struct access_list_list *list; + struct access_master *master; + + for (filter = access->head; filter; filter = next) + { + next = filter->next; + filter_free (filter); + } + + master = access->master; + + if (access->type == ACCESS_TYPE_NUMBER) + list = &master->num; + else + list = &master->str; + + if (access->next) + access->next->prev = access->prev; + else + list->tail = access->prev; + + if (access->prev) + access->prev->next = access->next; + else + list->head = access->next; + + if (access->name) + XFREE (MTYPE_ACCESS_LIST_STR, access->name); + + if (access->remark) + XFREE (MTYPE_TMP, access->remark); + + access_list_free (access); +} + +/* Insert new access list to list of access_list. Each acceess_list + is sorted by the name. */ +struct access_list * +access_list_insert (afi_t afi, char *name) +{ + int i; + long number; + struct access_list *access; + struct access_list *point; + struct access_list_list *alist; + struct access_master *master; + + master = access_master_get (afi); + if (master == NULL) + return NULL; + + /* Allocate new access_list and copy given name. */ + access = access_list_new (); + access->name = XSTRDUP (MTYPE_ACCESS_LIST_STR, name); + access->master = master; + + /* If name is made by all digit character. We treat it as + number. */ + for (number = 0, i = 0; i < strlen (name); i++) + { + if (isdigit ((int) name[i])) + number = (number * 10) + (name[i] - '0'); + else + break; + } + + /* In case of name is all digit character */ + if (i == strlen (name)) + { + access->type = ACCESS_TYPE_NUMBER; + + /* Set access_list to number list. */ + alist = &master->num; + + for (point = alist->head; point; point = point->next) + if (atol (point->name) >= number) + break; + } + else + { + access->type = ACCESS_TYPE_STRING; + + /* Set access_list to string list. */ + alist = &master->str; + + /* Set point to insertion point. */ + for (point = alist->head; point; point = point->next) + if (strcmp (point->name, name) >= 0) + break; + } + + /* In case of this is the first element of master. */ + if (alist->head == NULL) + { + alist->head = alist->tail = access; + return access; + } + + /* In case of insertion is made at the tail of access_list. */ + if (point == NULL) + { + access->prev = alist->tail; + alist->tail->next = access; + alist->tail = access; + return access; + } + + /* In case of insertion is made at the head of access_list. */ + if (point == alist->head) + { + access->next = alist->head; + alist->head->prev = access; + alist->head = access; + return access; + } + + /* Insertion is made at middle of the access_list. */ + access->next = point; + access->prev = point->prev; + + if (point->prev) + point->prev->next = access; + point->prev = access; + + return access; +} + +/* Lookup access_list from list of access_list by name. */ +struct access_list * +access_list_lookup (afi_t afi, char *name) +{ + struct access_list *access; + struct access_master *master; + + if (name == NULL) + return NULL; + + master = access_master_get (afi); + if (master == NULL) + return NULL; + + for (access = master->num.head; access; access = access->next) + if (strcmp (access->name, name) == 0) + return access; + + for (access = master->str.head; access; access = access->next) + if (strcmp (access->name, name) == 0) + return access; + + return NULL; +} + +/* Get access list from list of access_list. If there isn't matched + access_list create new one and return it. */ +struct access_list * +access_list_get (afi_t afi, char *name) +{ + struct access_list *access; + + access = access_list_lookup (afi, name); + if (access == NULL) + access = access_list_insert (afi, name); + return access; +} + +/* Apply access list to object (which should be struct prefix *). */ +enum filter_type +access_list_apply (struct access_list *access, void *object) +{ + struct filter *filter; + struct prefix *p; + + p = (struct prefix *) object; + + if (access == NULL) + return FILTER_DENY; + + for (filter = access->head; filter; filter = filter->next) + { + if (filter->cisco) + { + if (filter_match_cisco (filter, p)) + return filter->type; + } + else + { + if (filter_match_zebra (filter, p)) + return filter->type; + } + } + + return FILTER_DENY; +} + +/* Add hook function. */ +void +access_list_add_hook (void (*func) (struct access_list *access)) +{ + access_master_ipv4.add_hook = func; +#ifdef HAVE_IPV6 + access_master_ipv6.add_hook = func; +#endif /* HAVE_IPV6 */ +} + +/* Delete hook function. */ +void +access_list_delete_hook (void (*func) (struct access_list *access)) +{ + access_master_ipv4.delete_hook = func; +#ifdef HAVE_IPV6 + access_master_ipv6.delete_hook = func; +#endif /* HAVE_IPV6 */ +} + +/* Add new filter to the end of specified access_list. */ +void +access_list_filter_add (struct access_list *access, struct filter *filter) +{ + filter->next = NULL; + filter->prev = access->tail; + + if (access->tail) + access->tail->next = filter; + else + access->head = filter; + access->tail = filter; + + /* Run hook function. */ + if (access->master->add_hook) + (*access->master->add_hook) (access); +} + +/* If access_list has no filter then return 1. */ +static int +access_list_empty (struct access_list *access) +{ + if (access->head == NULL && access->tail == NULL) + return 1; + else + return 0; +} + +/* Delete filter from specified access_list. If there is hook + function execute it. */ +void +access_list_filter_delete (struct access_list *access, struct filter *filter) +{ + struct access_master *master; + + master = access->master; + + if (filter->next) + filter->next->prev = filter->prev; + else + access->tail = filter->prev; + + if (filter->prev) + filter->prev->next = filter->next; + else + access->head = filter->next; + + filter_free (filter); + + /* If access_list becomes empty delete it from access_master. */ + if (access_list_empty (access)) + access_list_delete (access); + + /* Run hook function. */ + if (master->delete_hook) + (*master->delete_hook) (access); +} + +/* + deny Specify packets to reject + permit Specify packets to forward + dynamic ? +*/ + +/* + Hostname or A.B.C.D Address to match + any Any source host + host A single host address +*/ + +struct filter * +filter_lookup_cisco (struct access_list *access, struct filter *mnew) +{ + struct filter *mfilter; + struct filter_cisco *filter; + struct filter_cisco *new; + + new = &mnew->u.cfilter; + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + filter = &mfilter->u.cfilter; + + if (filter->extended) + { + if (mfilter->type == mnew->type + && filter->addr.s_addr == new->addr.s_addr + && filter->addr_mask.s_addr == new->addr_mask.s_addr + && filter->mask.s_addr == new->mask.s_addr + && filter->mask_mask.s_addr == new->mask_mask.s_addr) + return mfilter; + } + else + { + if (mfilter->type == mnew->type + && filter->addr.s_addr == new->addr.s_addr + && filter->addr_mask.s_addr == new->addr_mask.s_addr) + return mfilter; + } + } + + return NULL; +} + +struct filter * +filter_lookup_zebra (struct access_list *access, struct filter *mnew) +{ + struct filter *mfilter; + struct filter_zebra *filter; + struct filter_zebra *new; + + new = &mnew->u.zfilter; + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + filter = &mfilter->u.zfilter; + + if (filter->exact == new->exact + && mfilter->type == mnew->type + && prefix_same (&filter->prefix, &new->prefix)) + return mfilter; + } + return NULL; +} + +int +vty_access_list_remark_unset (struct vty *vty, afi_t afi, char *name) +{ + struct access_list *access; + + access = access_list_lookup (afi, name); + if (! access) + { + vty_out (vty, "%% access-list %s doesn't exist%s", name, + VTY_NEWLINE); + return CMD_WARNING; + } + + if (access->remark) + { + XFREE (MTYPE_TMP, access->remark); + access->remark = NULL; + } + + if (access->head == NULL && access->tail == NULL && access->remark == NULL) + access_list_delete (access); + + return CMD_SUCCESS; +} + +int +filter_set_cisco (struct vty *vty, char *name_str, char *type_str, + char *addr_str, char *addr_mask_str, + char *mask_str, char *mask_mask_str, + int extended, int set) +{ + int ret; + enum filter_type type; + struct filter *mfilter; + struct filter_cisco *filter; + struct access_list *access; + struct in_addr addr; + struct in_addr addr_mask; + struct in_addr mask; + struct in_addr mask_mask; + + /* Check of filter type. */ + if (strncmp (type_str, "p", 1) == 0) + type = FILTER_PERMIT; + else if (strncmp (type_str, "d", 1) == 0) + type = FILTER_DENY; + else + { + vty_out (vty, "%% filter type must be permit or deny%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = inet_aton (addr_str, &addr); + if (ret <= 0) + { + vty_out (vty, "%%Inconsistent address and mask%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = inet_aton (addr_mask_str, &addr_mask); + if (ret <= 0) + { + vty_out (vty, "%%Inconsistent address and mask%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (extended) + { + ret = inet_aton (mask_str, &mask); + if (ret <= 0) + { + vty_out (vty, "%%Inconsistent address and mask%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = inet_aton (mask_mask_str, &mask_mask); + if (ret <= 0) + { + vty_out (vty, "%%Inconsistent address and mask%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } + + mfilter = filter_new(); + mfilter->type = type; + mfilter->cisco = 1; + filter = &mfilter->u.cfilter; + filter->extended = extended; + filter->addr.s_addr = addr.s_addr & ~addr_mask.s_addr; + filter->addr_mask.s_addr = addr_mask.s_addr; + + if (extended) + { + filter->mask.s_addr = mask.s_addr & ~mask_mask.s_addr; + filter->mask_mask.s_addr = mask_mask.s_addr; + } + + /* Install new filter to the access_list. */ + access = access_list_get (AFI_IP, name_str); + + if (set) + { + if (filter_lookup_cisco (access, mfilter)) + filter_free (mfilter); + else + access_list_filter_add (access, mfilter); + } + else + { + struct filter *delete_filter; + + delete_filter = filter_lookup_cisco (access, mfilter); + if (delete_filter) + access_list_filter_delete (access, delete_filter); + + filter_free (mfilter); + } + + return CMD_SUCCESS; +} + +/* Standard access-list */ +DEFUN (access_list_standard, + access_list_standard_cmd, + "access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D A.B.C.D", + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Address to match\n" + "Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], argv[3], + NULL, NULL, 0, 1); +} + +DEFUN (access_list_standard_nomask, + access_list_standard_nomask_cmd, + "access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D", + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Address to match\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + NULL, NULL, 0, 1); +} + +DEFUN (access_list_standard_host, + access_list_standard_host_cmd, + "access-list (<1-99>|<1300-1999>) (deny|permit) host A.B.C.D", + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "A single host address\n" + "Address to match\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + NULL, NULL, 0, 1); +} + +DEFUN (access_list_standard_any, + access_list_standard_any_cmd, + "access-list (<1-99>|<1300-1999>) (deny|permit) any", + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any source host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", NULL, NULL, 0, 1); +} + +DEFUN (no_access_list_standard, + no_access_list_standard_cmd, + "no access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Address to match\n" + "Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], argv[3], + NULL, NULL, 0, 0); +} + +DEFUN (no_access_list_standard_nomask, + no_access_list_standard_nomask_cmd, + "no access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Address to match\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + NULL, NULL, 0, 0); +} + +DEFUN (no_access_list_standard_host, + no_access_list_standard_host_cmd, + "no access-list (<1-99>|<1300-1999>) (deny|permit) host A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "A single host address\n" + "Address to match\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + NULL, NULL, 0, 0); +} + +DEFUN (no_access_list_standard_any, + no_access_list_standard_any_cmd, + "no access-list (<1-99>|<1300-1999>) (deny|permit) any", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any source host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", NULL, NULL, 0, 0); +} + +/* Extended access-list */ +DEFUN (access_list_extended, + access_list_extended_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], 1 ,1); +} + +DEFUN (access_list_extended_mask_any, + access_list_extended_mask_any_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D any", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], "0.0.0.0", + "255.255.255.255", 1, 1); +} + +DEFUN (access_list_extended_any_mask, + access_list_extended_any_mask_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip any A.B.C.D A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", argv[2], + argv[3], 1, 1); +} + +DEFUN (access_list_extended_any_any, + access_list_extended_any_any_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip any any", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", "0.0.0.0", + "255.255.255.255", 1, 1); +} + +DEFUN (access_list_extended_mask_host, + access_list_extended_mask_host_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D host A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], argv[4], + "0.0.0.0", 1, 1); +} + +DEFUN (access_list_extended_host_mask, + access_list_extended_host_mask_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D A.B.C.D A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", argv[3], + argv[4], 1, 1); +} + +DEFUN (access_list_extended_host_host, + access_list_extended_host_host_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D host A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", argv[3], + "0.0.0.0", 1, 1); +} + +DEFUN (access_list_extended_any_host, + access_list_extended_any_host_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip any host A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", argv[2], + "0.0.0.0", 1, 1); +} + +DEFUN (access_list_extended_host_any, + access_list_extended_host_any_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D any", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", "0.0.0.0", + "255.255.255.255", 1, 1); +} + +DEFUN (no_access_list_extended, + no_access_list_extended_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], 1, 0); +} + +DEFUN (no_access_list_extended_mask_any, + no_access_list_extended_mask_any_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D any", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], "0.0.0.0", + "255.255.255.255", 1, 0); +} + +DEFUN (no_access_list_extended_any_mask, + no_access_list_extended_any_mask_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any A.B.C.D A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", argv[2], + argv[3], 1, 0); +} + +DEFUN (no_access_list_extended_any_any, + no_access_list_extended_any_any_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any any", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", "0.0.0.0", + "255.255.255.255", 1, 0); +} + +DEFUN (no_access_list_extended_mask_host, + no_access_list_extended_mask_host_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D host A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], argv[4], + "0.0.0.0", 1, 0); +} + +DEFUN (no_access_list_extended_host_mask, + no_access_list_extended_host_mask_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D A.B.C.D A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", argv[3], + argv[4], 1, 0); +} + +DEFUN (no_access_list_extended_host_host, + no_access_list_extended_host_host_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D host A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", argv[3], + "0.0.0.0", 1, 0); +} + +DEFUN (no_access_list_extended_any_host, + no_access_list_extended_any_host_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any host A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", argv[2], + "0.0.0.0", 1, 0); +} + +DEFUN (no_access_list_extended_host_any, + no_access_list_extended_host_any_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D any", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", "0.0.0.0", + "255.255.255.255", 1, 0); +} + +int +filter_set_zebra (struct vty *vty, char *name_str, char *type_str, + afi_t afi, char *prefix_str, int exact, int set) +{ + int ret; + enum filter_type type; + struct filter *mfilter; + struct filter_zebra *filter; + struct access_list *access; + struct prefix p; + + /* Check of filter type. */ + if (strncmp (type_str, "p", 1) == 0) + type = FILTER_PERMIT; + else if (strncmp (type_str, "d", 1) == 0) + type = FILTER_DENY; + else + { + vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check string format of prefix and prefixlen. */ + if (afi == AFI_IP) + { + ret = str2prefix_ipv4 (prefix_str, (struct prefix_ipv4 *)&p); + if (ret <= 0) + { + vty_out (vty, "IP address prefix/prefixlen is malformed%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + { + ret = str2prefix_ipv6 (prefix_str, (struct prefix_ipv6 *) &p); + if (ret <= 0) + { + vty_out (vty, "IPv6 address prefix/prefixlen is malformed%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } +#endif /* HAVE_IPV6 */ + else + return CMD_WARNING; + + mfilter = filter_new (); + mfilter->type = type; + filter = &mfilter->u.zfilter; + prefix_copy (&filter->prefix, &p); + + /* "exact-match" */ + if (exact) + filter->exact = 1; + + /* Install new filter to the access_list. */ + access = access_list_get (afi, name_str); + + if (set) + { + if (filter_lookup_zebra (access, mfilter)) + filter_free (mfilter); + else + access_list_filter_add (access, mfilter); + } + else + { + struct filter *delete_filter; + + delete_filter = filter_lookup_zebra (access, mfilter); + if (delete_filter) + access_list_filter_delete (access, delete_filter); + + filter_free (mfilter); + } + + return CMD_SUCCESS; +} + +/* Zebra access-list */ +DEFUN (access_list, + access_list_cmd, + "access-list WORD (deny|permit) A.B.C.D/M", + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 0, 1); +} + +DEFUN (access_list_exact, + access_list_exact_cmd, + "access-list WORD (deny|permit) A.B.C.D/M exact-match", + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n" + "Exact match of the prefixes\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 1, 1); +} + +DEFUN (access_list_any, + access_list_any_cmd, + "access-list WORD (deny|permit) any", + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, "0.0.0.0/0", 0, 1); +} + +DEFUN (no_access_list, + no_access_list_cmd, + "no access-list WORD (deny|permit) A.B.C.D/M", + NO_STR + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 0, 0); +} + +DEFUN (no_access_list_exact, + no_access_list_exact_cmd, + "no access-list WORD (deny|permit) A.B.C.D/M exact-match", + NO_STR + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n" + "Exact match of the prefixes\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 1, 0); +} + +DEFUN (no_access_list_any, + no_access_list_any_cmd, + "no access-list WORD (deny|permit) any", + NO_STR + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, "0.0.0.0/0", 0, 0); +} + +DEFUN (no_access_list_all, + no_access_list_all_cmd, + "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD)", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP extended access list\n" + "IP standard access list (expanded range)\n" + "IP extended access list (expanded range)\n" + "IP zebra access-list name\n") +{ + struct access_list *access; + struct access_master *master; + + /* Looking up access_list. */ + access = access_list_lookup (AFI_IP, argv[0]); + if (access == NULL) + { + vty_out (vty, "%% access-list %s doesn't exist%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + master = access->master; + + /* Delete all filter from access-list. */ + access_list_delete (access); + + /* Run hook function. */ + if (master->delete_hook) + (*master->delete_hook) (access); + + return CMD_SUCCESS; +} + +DEFUN (access_list_remark, + access_list_remark_cmd, + "access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark .LINE", + "Add an access list entry\n" + "IP standard access list\n" + "IP extended access list\n" + "IP standard access list (expanded range)\n" + "IP extended access list (expanded range)\n" + "IP zebra access-list\n" + "Access list entry comment\n" + "Comment up to 100 characters\n") +{ + struct access_list *access; + struct buffer *b; + int i; + + access = access_list_get (AFI_IP, argv[0]); + + if (access->remark) + { + XFREE (MTYPE_TMP, access->remark); + access->remark = NULL; + } + + /* Below is remark get codes. */ + b = buffer_new (1024); + for (i = 1; i < argc; i++) + { + buffer_putstr (b, (u_char *)argv[i]); + buffer_putc (b, ' '); + } + buffer_putc (b, '\0'); + + access->remark = buffer_getstr (b); + + buffer_free (b); + + return CMD_SUCCESS; +} + +DEFUN (no_access_list_remark, + no_access_list_remark_cmd, + "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP extended access list\n" + "IP standard access list (expanded range)\n" + "IP extended access list (expanded range)\n" + "IP zebra access-list\n" + "Access list entry comment\n") +{ + return vty_access_list_remark_unset (vty, AFI_IP, argv[0]); +} + +ALIAS (no_access_list_remark, + no_access_list_remark_arg_cmd, + "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark .LINE", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP extended access list\n" + "IP standard access list (expanded range)\n" + "IP extended access list (expanded range)\n" + "IP zebra access-list\n" + "Access list entry comment\n" + "Comment up to 100 characters\n") + +#ifdef HAVE_IPV6 +DEFUN (ipv6_access_list, + ipv6_access_list_cmd, + "ipv6 access-list WORD (deny|permit) X:X::X:X/M", + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 3ffe:506::/32\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 0, 1); +} + +DEFUN (ipv6_access_list_exact, + ipv6_access_list_exact_cmd, + "ipv6 access-list WORD (deny|permit) X:X::X:X/M exact-match", + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 3ffe:506::/32\n" + "Exact match of the prefixes\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 1, 1); +} + +DEFUN (ipv6_access_list_any, + ipv6_access_list_any_cmd, + "ipv6 access-list WORD (deny|permit) any", + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any prefixi to match\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, "::/0", 0, 1); +} + +DEFUN (no_ipv6_access_list, + no_ipv6_access_list_cmd, + "no ipv6 access-list WORD (deny|permit) X:X::X:X/M", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 3ffe:506::/32\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 0, 0); +} + +DEFUN (no_ipv6_access_list_exact, + no_ipv6_access_list_exact_cmd, + "no ipv6 access-list WORD (deny|permit) X:X::X:X/M exact-match", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 3ffe:506::/32\n" + "Exact match of the prefixes\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 1, 0); +} + +DEFUN (no_ipv6_access_list_any, + no_ipv6_access_list_any_cmd, + "no ipv6 access-list WORD (deny|permit) any", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any prefixi to match\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, "::/0", 0, 0); +} + + +DEFUN (no_ipv6_access_list_all, + no_ipv6_access_list_all_cmd, + "no ipv6 access-list WORD", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n") +{ + struct access_list *access; + struct access_master *master; + + /* Looking up access_list. */ + access = access_list_lookup (AFI_IP6, argv[0]); + if (access == NULL) + { + vty_out (vty, "%% access-list %s doesn't exist%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + master = access->master; + + /* Delete all filter from access-list. */ + access_list_delete (access); + + /* Run hook function. */ + if (master->delete_hook) + (*master->delete_hook) (access); + + return CMD_SUCCESS; +} + +DEFUN (ipv6_access_list_remark, + ipv6_access_list_remark_cmd, + "ipv6 access-list WORD remark .LINE", + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Access list entry comment\n" + "Comment up to 100 characters\n") +{ + struct access_list *access; + struct buffer *b; + int i; + + access = access_list_get (AFI_IP6, argv[0]); + + if (access->remark) + { + XFREE (MTYPE_TMP, access->remark); + access->remark = NULL; + } + + /* Below is remark get codes. */ + b = buffer_new (1024); + for (i = 1; i < argc; i++) + { + buffer_putstr (b, (u_char *)argv[i]); + buffer_putc (b, ' '); + } + buffer_putc (b, '\0'); + + access->remark = buffer_getstr (b); + + buffer_free (b); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_access_list_remark, + no_ipv6_access_list_remark_cmd, + "no ipv6 access-list WORD remark", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Access list entry comment\n") +{ + return vty_access_list_remark_unset (vty, AFI_IP6, argv[0]); +} + +ALIAS (no_ipv6_access_list_remark, + no_ipv6_access_list_remark_arg_cmd, + "no ipv6 access-list WORD remark .LINE", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Access list entry comment\n" + "Comment up to 100 characters\n") +#endif /* HAVE_IPV6 */ + +void config_write_access_zebra (struct vty *, struct filter *); +void config_write_access_cisco (struct vty *, struct filter *); + +/* show access-list command. */ +int +filter_show (struct vty *vty, char *name, afi_t afi) +{ + struct access_list *access; + struct access_master *master; + struct filter *mfilter; + struct filter_cisco *filter; + int write = 0; + + master = access_master_get (afi); + if (master == NULL) + return 0; + + for (access = master->num.head; access; access = access->next) + { + if (name && strcmp (access->name, name) != 0) + continue; + + write = 1; + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + filter = &mfilter->u.cfilter; + + if (write) + { + vty_out (vty, "%s IP%s access list %s%s", + mfilter->cisco ? + (filter->extended ? "Extended" : "Standard") : "Zebra", + afi == AFI_IP6 ? "v6" : "", + access->name, VTY_NEWLINE); + write = 0; + } + + vty_out (vty, " %s%s", filter_type_str (mfilter), + mfilter->type == FILTER_DENY ? " " : ""); + + if (! mfilter->cisco) + config_write_access_zebra (vty, mfilter); + else if (filter->extended) + config_write_access_cisco (vty, mfilter); + else + { + if (filter->addr_mask.s_addr == 0xffffffff) + vty_out (vty, " any%s", VTY_NEWLINE); + else + { + vty_out (vty, " %s", inet_ntoa (filter->addr)); + if (filter->addr_mask.s_addr != 0) + vty_out (vty, ", wildcard bits %s", inet_ntoa (filter->addr_mask)); + vty_out (vty, "%s", VTY_NEWLINE); + } + } + } + } + + for (access = master->str.head; access; access = access->next) + { + if (name && strcmp (access->name, name) != 0) + continue; + + write = 1; + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + filter = &mfilter->u.cfilter; + + if (write) + { + vty_out (vty, "%s IP%s access list %s%s", + mfilter->cisco ? + (filter->extended ? "Extended" : "Standard") : "Zebra", + afi == AFI_IP6 ? "v6" : "", + access->name, VTY_NEWLINE); + write = 0; + } + + vty_out (vty, " %s%s", filter_type_str (mfilter), + mfilter->type == FILTER_DENY ? " " : ""); + + if (! mfilter->cisco) + config_write_access_zebra (vty, mfilter); + else if (filter->extended) + config_write_access_cisco (vty, mfilter); + else + { + if (filter->addr_mask.s_addr == 0xffffffff) + vty_out (vty, " any%s", VTY_NEWLINE); + else + { + vty_out (vty, " %s", inet_ntoa (filter->addr)); + if (filter->addr_mask.s_addr != 0) + vty_out (vty, ", wildcard bits %s", inet_ntoa (filter->addr_mask)); + vty_out (vty, "%s", VTY_NEWLINE); + } + } + } + } + return CMD_SUCCESS; +} + +DEFUN (show_ip_access_list, + show_ip_access_list_cmd, + "show ip access-list", + SHOW_STR + IP_STR + "List IP access lists\n") +{ + return filter_show (vty, NULL, AFI_IP); +} + +DEFUN (show_ip_access_list_name, + show_ip_access_list_name_cmd, + "show ip access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD)", + SHOW_STR + IP_STR + "List IP access lists\n" + "IP standard access list\n" + "IP extended access list\n" + "IP standard access list (expanded range)\n" + "IP extended access list (expanded range)\n" + "IP zebra access-list\n") +{ + return filter_show (vty, argv[0], AFI_IP); +} + +#ifdef HAVE_IPV6 +DEFUN (show_ipv6_access_list, + show_ipv6_access_list_cmd, + "show ipv6 access-list", + SHOW_STR + IPV6_STR + "List IPv6 access lists\n") +{ + return filter_show (vty, NULL, AFI_IP6); +} + +DEFUN (show_ipv6_access_list_name, + show_ipv6_access_list_name_cmd, + "show ipv6 access-list WORD", + SHOW_STR + IPV6_STR + "List IPv6 access lists\n" + "IPv6 zebra access-list\n") +{ + return filter_show (vty, argv[0], AFI_IP6); +} +#endif /* HAVE_IPV6 */ + +void +config_write_access_cisco (struct vty *vty, struct filter *mfilter) +{ + struct filter_cisco *filter; + + filter = &mfilter->u.cfilter; + + if (filter->extended) + { + vty_out (vty, " ip"); + if (filter->addr_mask.s_addr == 0xffffffff) + vty_out (vty, " any"); + else if (filter->addr_mask.s_addr == 0) + vty_out (vty, " host %s", inet_ntoa (filter->addr)); + else + { + vty_out (vty, " %s", inet_ntoa (filter->addr)); + vty_out (vty, " %s", inet_ntoa (filter->addr_mask)); + } + + if (filter->mask_mask.s_addr == 0xffffffff) + vty_out (vty, " any"); + else if (filter->mask_mask.s_addr == 0) + vty_out (vty, " host %s", inet_ntoa (filter->mask)); + else + { + vty_out (vty, " %s", inet_ntoa (filter->mask)); + vty_out (vty, " %s", inet_ntoa (filter->mask_mask)); + } + vty_out (vty, "%s", VTY_NEWLINE); + } + else + { + if (filter->addr_mask.s_addr == 0xffffffff) + vty_out (vty, " any%s", VTY_NEWLINE); + else + { + vty_out (vty, " %s", inet_ntoa (filter->addr)); + if (filter->addr_mask.s_addr != 0) + vty_out (vty, " %s", inet_ntoa (filter->addr_mask)); + vty_out (vty, "%s", VTY_NEWLINE); + } + } +} + +void +config_write_access_zebra (struct vty *vty, struct filter *mfilter) +{ + struct filter_zebra *filter; + struct prefix *p; + char buf[BUFSIZ]; + + filter = &mfilter->u.zfilter; + p = &filter->prefix; + + if (p->prefixlen == 0 && ! filter->exact) + vty_out (vty, " any"); + else + vty_out (vty, " %s/%d%s", + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen, + filter->exact ? " exact-match" : ""); + + vty_out (vty, "%s", VTY_NEWLINE); +} + +int +config_write_access (struct vty *vty, afi_t afi) +{ + struct access_list *access; + struct access_master *master; + struct filter *mfilter; + int write = 0; + + master = access_master_get (afi); + if (master == NULL) + return 0; + + for (access = master->num.head; access; access = access->next) + { + if (access->remark) + { + vty_out (vty, "%saccess-list %s remark %s%s", + afi == AFI_IP ? "" : "ipv6 ", + access->name, access->remark, + VTY_NEWLINE); + write++; + } + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + vty_out (vty, "%saccess-list %s %s", + afi == AFI_IP ? "" : "ipv6 ", + access->name, + filter_type_str (mfilter)); + + if (mfilter->cisco) + config_write_access_cisco (vty, mfilter); + else + config_write_access_zebra (vty, mfilter); + + write++; + } + } + + for (access = master->str.head; access; access = access->next) + { + if (access->remark) + { + vty_out (vty, "%saccess-list %s remark %s%s", + afi == AFI_IP ? "" : "ipv6 ", + access->name, access->remark, + VTY_NEWLINE); + write++; + } + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + vty_out (vty, "%saccess-list %s %s", + afi == AFI_IP ? "" : "ipv6 ", + access->name, + filter_type_str (mfilter)); + + if (mfilter->cisco) + config_write_access_cisco (vty, mfilter); + else + config_write_access_zebra (vty, mfilter); + + write++; + } + } + return write; +} + +/* Access-list node. */ +struct cmd_node access_node = +{ + ACCESS_NODE, + "", /* Access list has no interface. */ + 1 +}; + +int +config_write_access_ipv4 (struct vty *vty) +{ + return config_write_access (vty, AFI_IP); +} + +void +access_list_reset_ipv4 () +{ + struct access_list *access; + struct access_list *next; + struct access_master *master; + + master = access_master_get (AFI_IP); + if (master == NULL) + return; + + for (access = master->num.head; access; access = next) + { + next = access->next; + access_list_delete (access); + } + for (access = master->str.head; access; access = next) + { + next = access->next; + access_list_delete (access); + } + + assert (master->num.head == NULL); + assert (master->num.tail == NULL); + + assert (master->str.head == NULL); + assert (master->str.tail == NULL); +} + +/* Install vty related command. */ +void +access_list_init_ipv4 () +{ + install_node (&access_node, config_write_access_ipv4); + + install_element (ENABLE_NODE, &show_ip_access_list_cmd); + install_element (ENABLE_NODE, &show_ip_access_list_name_cmd); + + /* Zebra access-list */ + install_element (CONFIG_NODE, &access_list_cmd); + install_element (CONFIG_NODE, &access_list_exact_cmd); + install_element (CONFIG_NODE, &access_list_any_cmd); + install_element (CONFIG_NODE, &no_access_list_cmd); + install_element (CONFIG_NODE, &no_access_list_exact_cmd); + install_element (CONFIG_NODE, &no_access_list_any_cmd); + + /* Standard access-list */ + install_element (CONFIG_NODE, &access_list_standard_cmd); + install_element (CONFIG_NODE, &access_list_standard_nomask_cmd); + install_element (CONFIG_NODE, &access_list_standard_host_cmd); + install_element (CONFIG_NODE, &access_list_standard_any_cmd); + install_element (CONFIG_NODE, &no_access_list_standard_cmd); + install_element (CONFIG_NODE, &no_access_list_standard_nomask_cmd); + install_element (CONFIG_NODE, &no_access_list_standard_host_cmd); + install_element (CONFIG_NODE, &no_access_list_standard_any_cmd); + + /* Extended access-list */ + install_element (CONFIG_NODE, &access_list_extended_cmd); + install_element (CONFIG_NODE, &access_list_extended_any_mask_cmd); + install_element (CONFIG_NODE, &access_list_extended_mask_any_cmd); + install_element (CONFIG_NODE, &access_list_extended_any_any_cmd); + install_element (CONFIG_NODE, &access_list_extended_host_mask_cmd); + install_element (CONFIG_NODE, &access_list_extended_mask_host_cmd); + install_element (CONFIG_NODE, &access_list_extended_host_host_cmd); + install_element (CONFIG_NODE, &access_list_extended_any_host_cmd); + install_element (CONFIG_NODE, &access_list_extended_host_any_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_any_mask_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_mask_any_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_any_any_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_host_mask_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_mask_host_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_host_host_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_any_host_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_host_any_cmd); + + install_element (CONFIG_NODE, &access_list_remark_cmd); + install_element (CONFIG_NODE, &no_access_list_all_cmd); + install_element (CONFIG_NODE, &no_access_list_remark_cmd); + install_element (CONFIG_NODE, &no_access_list_remark_arg_cmd); +} + +#ifdef HAVE_IPV6 +struct cmd_node access_ipv6_node = +{ + ACCESS_IPV6_NODE, + "", + 1 +}; + +int +config_write_access_ipv6 (struct vty *vty) +{ + return config_write_access (vty, AFI_IP6); +} + +void +access_list_reset_ipv6 () +{ + struct access_list *access; + struct access_list *next; + struct access_master *master; + + master = access_master_get (AFI_IP6); + if (master == NULL) + return; + + for (access = master->num.head; access; access = next) + { + next = access->next; + access_list_delete (access); + } + for (access = master->str.head; access; access = next) + { + next = access->next; + access_list_delete (access); + } + + assert (master->num.head == NULL); + assert (master->num.tail == NULL); + + assert (master->str.head == NULL); + assert (master->str.tail == NULL); +} + +void +access_list_init_ipv6 () +{ + install_node (&access_ipv6_node, config_write_access_ipv6); + + install_element (ENABLE_NODE, &show_ipv6_access_list_cmd); + install_element (ENABLE_NODE, &show_ipv6_access_list_name_cmd); + + install_element (CONFIG_NODE, &ipv6_access_list_cmd); + install_element (CONFIG_NODE, &ipv6_access_list_exact_cmd); + install_element (CONFIG_NODE, &ipv6_access_list_any_cmd); + install_element (CONFIG_NODE, &no_ipv6_access_list_exact_cmd); + install_element (CONFIG_NODE, &no_ipv6_access_list_cmd); + install_element (CONFIG_NODE, &no_ipv6_access_list_any_cmd); + + install_element (CONFIG_NODE, &no_ipv6_access_list_all_cmd); + install_element (CONFIG_NODE, &ipv6_access_list_remark_cmd); + install_element (CONFIG_NODE, &no_ipv6_access_list_remark_cmd); + install_element (CONFIG_NODE, &no_ipv6_access_list_remark_arg_cmd); +} +#endif /* HAVE_IPV6 */ + +void +access_list_init () +{ + access_list_init_ipv4 (); +#ifdef HAVE_IPV6 + access_list_init_ipv6(); +#endif /* HAVE_IPV6 */ +} + +void +access_list_reset () +{ + access_list_reset_ipv4 (); +#ifdef HAVE_IPV6 + access_list_reset_ipv6(); +#endif /* HAVE_IPV6 */ +} diff --git a/lib/filter.h b/lib/filter.h new file mode 100644 index 00000000..077ac2fb --- /dev/null +++ b/lib/filter.h @@ -0,0 +1,67 @@ +/* + * Route filtering function. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_FILTER_H +#define _ZEBRA_FILTER_H + +#include "if.h" + +/* Filter type is made by `permit', `deny' and `dynamic'. */ +enum filter_type +{ + FILTER_DENY, + FILTER_PERMIT, + FILTER_DYNAMIC +}; + +enum access_type +{ + ACCESS_TYPE_STRING, + ACCESS_TYPE_NUMBER +}; + +/* Access list */ +struct access_list +{ + char *name; + char *remark; + + struct access_master *master; + + enum access_type type; + + struct access_list *next; + struct access_list *prev; + + struct filter *head; + struct filter *tail; +}; + +/* Prototypes for access-list. */ +void access_list_init (void); +void access_list_reset (void); +void access_list_add_hook (void (*func)(struct access_list *)); +void access_list_delete_hook (void (*func)(struct access_list *)); +struct access_list *access_list_lookup (afi_t, char *); +enum filter_type access_list_apply (struct access_list *, void *); + +#endif /* _ZEBRA_FILTER_H */ diff --git a/lib/getopt.c b/lib/getopt.c new file mode 100644 index 00000000..426b29bf --- /dev/null +++ b/lib/getopt.c @@ -0,0 +1,1054 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98 + Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + This program 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. + + 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. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. + Ditto for AIX 3.2 and <stdlib.h>. */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include <gnu-versions.h> +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include <stdlib.h> +# include <unistd.h> +#endif /* GNU C library. */ + +#ifdef VMS +# include <unixlib.h> +# if HAVE_STRING_H - 0 +# include <string.h> +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include <libintl.h> +# define _(msgid) gettext (msgid) +# else +# define _(msgid) (msgid) +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include <string.h> +# define my_index strchr +#else + +# if HAVE_STRING_H +# include <string.h> +# else +# include <strings.h> +# endif + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +# ifdef text_set_element +text_set_element (__libc_subinit, store_args_and_env); +# endif /* text_set_element */ + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/lib/getopt.h b/lib/getopt.h new file mode 100644 index 00000000..fb30719a --- /dev/null +++ b/lib/getopt.h @@ -0,0 +1,133 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + This program 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. + + 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. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* getopt.h */ diff --git a/lib/getopt1.c b/lib/getopt1.c new file mode 100644 index 00000000..ff257374 --- /dev/null +++ b/lib/getopt1.c @@ -0,0 +1,190 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + This program 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. + + 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. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include <gnu-versions.h> +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include <stdlib.h> +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include <stdio.h> + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/lib/hash.c b/lib/hash.c new file mode 100644 index 00000000..40975079 --- /dev/null +++ b/lib/hash.c @@ -0,0 +1,182 @@ +/* Hash routine. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "hash.h" +#include "memory.h" + +/* Allocate a new hash. */ +struct hash * +hash_create_size (unsigned int size, + unsigned int (*hash_key) (), int (*hash_cmp) ()) +{ + struct hash *hash; + + hash = XMALLOC (MTYPE_HASH, sizeof (struct hash)); + hash->index = XMALLOC (MTYPE_HASH_INDEX, + sizeof (struct hash_backet *) * size); + memset (hash->index, 0, sizeof (struct hash_backet *) * size); + hash->size = size; + hash->hash_key = hash_key; + hash->hash_cmp = hash_cmp; + hash->count = 0; + + return hash; +} + +/* Allocate a new hash with default hash size. */ +struct hash * +hash_create (unsigned int (*hash_key) (), int (*hash_cmp) ()) +{ + return hash_create_size (HASHTABSIZE, hash_key, hash_cmp); +} + +/* Utility function for hash_get(). When this function is specified + as alloc_func, return arugment as it is. This function is used for + intern already allocated value. */ +void * +hash_alloc_intern (void *arg) +{ + return arg; +} + +/* Lookup and return hash backet in hash. If there is no + corresponding hash backet and alloc_func is specified, create new + hash backet. */ +void * +hash_get (struct hash *hash, void *data, void * (*alloc_func) ()) +{ + unsigned int key; + unsigned int index; + void *newdata; + struct hash_backet *backet; + + key = (*hash->hash_key) (data); + index = key % hash->size; + + for (backet = hash->index[index]; backet != NULL; backet = backet->next) + if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) + return backet->data; + + if (alloc_func) + { + newdata = (*alloc_func) (data); + if (newdata == NULL) + return NULL; + + backet = XMALLOC (MTYPE_HASH_BACKET, sizeof (struct hash_backet)); + backet->data = newdata; + backet->key = key; + backet->next = hash->index[index]; + hash->index[index] = backet; + hash->count++; + return backet->data; + } + return NULL; +} + +/* Hash lookup. */ +void * +hash_lookup (struct hash *hash, void *data) +{ + return hash_get (hash, data, NULL); +} + +/* This function release registered value from specified hash. When + release is successfully finished, return the data pointer in the + hash backet. */ +void * +hash_release (struct hash *hash, void *data) +{ + void *ret; + unsigned int key; + unsigned int index; + struct hash_backet *backet; + struct hash_backet *pp; + + key = (*hash->hash_key) (data); + index = key % hash->size; + + for (backet = pp = hash->index[index]; backet; backet = backet->next) + { + if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) + { + if (backet == pp) + hash->index[index] = backet->next; + else + pp->next = backet->next; + + ret = backet->data; + XFREE (MTYPE_HASH_BACKET, backet); + hash->count--; + return ret; + } + pp = backet; + } + return NULL; +} + +/* Iterator function for hash. */ +void +hash_iterate (struct hash *hash, + void (*func) (struct hash_backet *, void *), void *arg) +{ + int i; + struct hash_backet *hb; + + for (i = 0; i < hash->size; i++) + for (hb = hash->index[i]; hb; hb = hb->next) + (*func) (hb, arg); +} + +/* Clean up hash. */ +void +hash_clean (struct hash *hash, void (*free_func) (void *)) +{ + int i; + struct hash_backet *hb; + struct hash_backet *next; + + for (i = 0; i < hash->size; i++) + { + for (hb = hash->index[i]; hb; hb = next) + { + next = hb->next; + + if (free_func) + (*free_func) (hb->data); + + XFREE (MTYPE_HASH_BACKET, hb); + hash->count--; + } + hash->index[i] = NULL; + } +} + +/* Free hash memory. You may call hash_clean before call this + function. */ +void +hash_free (struct hash *hash) +{ + XFREE (MTYPE_HASH_INDEX, hash->index); + XFREE (MTYPE_HASH, hash); +} diff --git a/lib/hash.h b/lib/hash.h new file mode 100644 index 00000000..715e53b5 --- /dev/null +++ b/lib/hash.h @@ -0,0 +1,71 @@ +/* Hash routine. + Copyright (C) 1998 Kunihiro Ishiguro + +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. */ + +#ifndef _ZEBRA_HASH_H +#define _ZEBRA_HASH_H + +/* Default hash table size. */ +#define HASHTABSIZE 1024 + +struct hash_backet +{ + /* Linked list. */ + struct hash_backet *next; + + /* Hash key. */ + unsigned int key; + + /* Data. */ + void *data; +}; + +struct hash +{ + /* Hash backet. */ + struct hash_backet **index; + + /* Hash table size. */ + unsigned int size; + + /* Key make function. */ + unsigned int (*hash_key) (); + + /* Data compare function. */ + int (*hash_cmp) (); + + /* Backet alloc. */ + unsigned long count; +}; + +struct hash *hash_create (unsigned int (*) (), int (*) ()); +struct hash *hash_create_size (unsigned int, unsigned int (*) (), int (*) ()); + +void *hash_get (struct hash *, void *, void * (*) ()); +void *hash_alloc_intern (void *); +void *hash_lookup (struct hash *, void *); +void *hash_release (struct hash *, void *); + +void hash_iterate (struct hash *, + void (*) (struct hash_backet *, void *), void *); + +void hash_clean (struct hash *, void (*) (void *)); +void hash_free (struct hash *); + +#endif /* _ZEBRA_HASH_H */ diff --git a/lib/if.c b/lib/if.c new file mode 100644 index 00000000..bbf22ab1 --- /dev/null +++ b/lib/if.c @@ -0,0 +1,713 @@ +/* + * Interface functions. + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "linklist.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "if.h" +#include "sockunion.h" +#include "prefix.h" +#include "zebra/connected.h" +#include "memory.h" +#include "table.h" +#include "buffer.h" +#include "str.h" +#include "log.h" + +/* Master list of interfaces. */ +struct list *iflist; + +/* One for each program. This structure is needed to store hooks. */ +struct if_master +{ + int (*if_new_hook) (struct interface *); + int (*if_delete_hook) (struct interface *); +} if_master; + +/* Create new interface structure. */ +struct interface * +if_new () +{ + struct interface *ifp; + + ifp = XMALLOC (MTYPE_IF, sizeof (struct interface)); + memset (ifp, 0, sizeof (struct interface)); + return ifp; +} + +struct interface * +if_create () +{ + struct interface *ifp; + + ifp = if_new (); + + listnode_add (iflist, ifp); + ifp->connected = list_new (); + ifp->connected->del = (void (*) (void *)) connected_free; + + if (if_master.if_new_hook) + (*if_master.if_new_hook) (ifp); + + return ifp; +} + +/* Delete and free interface structure. */ +void +if_delete (struct interface *ifp) +{ + listnode_delete (iflist, ifp); + + if (if_master.if_delete_hook) + (*if_master.if_delete_hook) (ifp); + + /* Free connected address list */ + list_delete (ifp->connected); + + XFREE (MTYPE_IF, ifp); +} + +/* Add hook to interface master. */ +void +if_add_hook (int type, int (*func)(struct interface *ifp)) +{ + switch (type) { + case IF_NEW_HOOK: + if_master.if_new_hook = func; + break; + case IF_DELETE_HOOK: + if_master.if_delete_hook = func; + break; + default: + break; + } +} + +/* Interface existance check by index. */ +struct interface * +if_lookup_by_index (unsigned int index) +{ + listnode node; + struct interface *ifp; + + for (node = listhead (iflist); node; nextnode (node)) + { + ifp = getdata (node); + if (ifp->ifindex == index) + return ifp; + } + return NULL; +} + +char * +ifindex2ifname (unsigned int index) +{ + listnode node; + struct interface *ifp; + + for (node = listhead (iflist); node; nextnode (node)) + { + ifp = getdata (node); + if (ifp->ifindex == index) + return ifp->name; + } + return "unknown"; +} + +/* Interface existance check by interface name. */ +struct interface * +if_lookup_by_name (char *name) +{ + listnode node; + struct interface *ifp; + + for (node = listhead (iflist); node; nextnode (node)) + { + ifp = getdata (node); + if (strncmp (name, ifp->name, sizeof ifp->name) == 0) + return ifp; + } + return NULL; +} + +/* Lookup interface by IPv4 address. */ +struct interface * +if_lookup_exact_address (struct in_addr src) +{ + listnode node; + listnode cnode; + struct interface *ifp; + struct prefix *p; + struct connected *c; + + for (node = listhead (iflist); node; nextnode (node)) + { + ifp = getdata (node); + + for (cnode = listhead (ifp->connected); cnode; nextnode (cnode)) + { + c = getdata (cnode); + + p = c->address; + + if (p && p->family == AF_INET) + { + if (IPV4_ADDR_SAME (&p->u.prefix4, &src)) + return ifp; + } + } + } + return NULL; +} + +/* Lookup interface by IPv4 address. */ +struct interface * +if_lookup_address (struct in_addr src) +{ + listnode node; + struct prefix addr; + struct prefix best; + listnode cnode; + struct interface *ifp; + struct prefix *p; + struct connected *c; + struct interface *match; + + /* Zero structures - get rid of rubbish from stack */ + memset(&addr, 0, sizeof(addr)); + memset(&best, 0, sizeof(best)); + + addr.family = AF_INET; + addr.u.prefix4 = src; + addr.prefixlen = IPV4_MAX_BITLEN; + + match = NULL; + + for (node = listhead (iflist); node; nextnode (node)) + { + ifp = getdata (node); + + for (cnode = listhead (ifp->connected); cnode; nextnode (cnode)) + { + c = getdata (cnode); + + if (if_is_pointopoint (ifp)) + { + p = c->address; + + if (p && p->family == AF_INET) + { +#ifdef OLD_RIB /* PTP links are conventionally identified + by the address of the far end - MAG */ + if (IPV4_ADDR_SAME (&p->u.prefix4, &src)) + return ifp; +#endif + p = c->destination; + if (p && IPV4_ADDR_SAME (&p->u.prefix4, &src)) + return ifp; + } + } + else + { + p = c->address; + + if (p->family == AF_INET) + { + if (prefix_match (p, &addr) && p->prefixlen > best.prefixlen) + { + best = *p; + match = ifp; + } + } + } + } + } + return match; +} + +/* Get interface by name if given name interface doesn't exist create + one. */ +struct interface * +if_get_by_name (char *name) +{ + struct interface *ifp; + + ifp = if_lookup_by_name (name); + if (ifp == NULL) + { + ifp = if_create (); + strncpy (ifp->name, name, IFNAMSIZ); + } + return ifp; +} + +/* Does interface up ? */ +int +if_is_up (struct interface *ifp) +{ + return ifp->flags & IFF_UP; +} + +/* Is this loopback interface ? */ +int +if_is_loopback (struct interface *ifp) +{ + return ifp->flags & IFF_LOOPBACK; +} + +/* Does this interface support broadcast ? */ +int +if_is_broadcast (struct interface *ifp) +{ + return ifp->flags & IFF_BROADCAST; +} + +/* Does this interface support broadcast ? */ +int +if_is_pointopoint (struct interface *ifp) +{ + return ifp->flags & IFF_POINTOPOINT; +} + +/* Does this interface support multicast ? */ +int +if_is_multicast (struct interface *ifp) +{ + return ifp->flags & IFF_MULTICAST; +} + +/* Printout flag information into log */ +const char * +if_flag_dump (unsigned long flag) +{ + int separator = 0; + static char logbuf[BUFSIZ]; + +#define IFF_OUT_LOG(X,STR) \ + if ((X) && (flag & (X))) \ + { \ + if (separator) \ + strlcat (logbuf, ",", BUFSIZ); \ + else \ + separator = 1; \ + strlcat (logbuf, STR, BUFSIZ); \ + } + + strlcpy (logbuf, " <", BUFSIZ); + IFF_OUT_LOG (IFF_UP, "UP"); + IFF_OUT_LOG (IFF_BROADCAST, "BROADCAST"); + IFF_OUT_LOG (IFF_DEBUG, "DEBUG"); + IFF_OUT_LOG (IFF_LOOPBACK, "LOOPBACK"); + IFF_OUT_LOG (IFF_POINTOPOINT, "POINTOPOINT"); + IFF_OUT_LOG (IFF_NOTRAILERS, "NOTRAILERS"); + IFF_OUT_LOG (IFF_RUNNING, "RUNNING"); + IFF_OUT_LOG (IFF_NOARP, "NOARP"); + IFF_OUT_LOG (IFF_PROMISC, "PROMISC"); + IFF_OUT_LOG (IFF_ALLMULTI, "ALLMULTI"); + IFF_OUT_LOG (IFF_OACTIVE, "OACTIVE"); + IFF_OUT_LOG (IFF_SIMPLEX, "SIMPLEX"); + IFF_OUT_LOG (IFF_LINK0, "LINK0"); + IFF_OUT_LOG (IFF_LINK1, "LINK1"); + IFF_OUT_LOG (IFF_LINK2, "LINK2"); + IFF_OUT_LOG (IFF_MULTICAST, "MULTICAST"); + + strlcat (logbuf, ">", BUFSIZ); + + return logbuf; +} + +/* For debugging */ +void +if_dump (struct interface *ifp) +{ + listnode node; + + zlog_info ("Interface %s index %d metric %d mtu %d %s", + ifp->name, ifp->ifindex, ifp->metric, ifp->mtu, + if_flag_dump (ifp->flags)); + + for (node = listhead (ifp->connected); node; nextnode (node)) + ; +} + +/* Interface printing for all interface. */ +void +if_dump_all () +{ + listnode node; + + for (node = listhead (iflist); node; nextnode (node)) + if_dump (getdata (node)); +} + +DEFUN (interface_desc, + interface_desc_cmd, + "description .LINE", + "Interface specific description\n" + "Characters describing this interface\n") +{ + int i; + struct interface *ifp; + struct buffer *b; + + if (argc == 0) + return CMD_SUCCESS; + + ifp = vty->index; + if (ifp->desc) + XFREE (0, ifp->desc); + + b = buffer_new (1024); + for (i = 0; i < argc; i++) + { + buffer_putstr (b, (u_char *)argv[i]); + buffer_putc (b, ' '); + } + buffer_putc (b, '\0'); + + ifp->desc = buffer_getstr (b); + buffer_free (b); + + return CMD_SUCCESS; +} + +DEFUN (no_interface_desc, + no_interface_desc_cmd, + "no description", + NO_STR + "Interface specific description\n") +{ + struct interface *ifp; + + ifp = vty->index; + if (ifp->desc) + XFREE (0, ifp->desc); + ifp->desc = NULL; + + return CMD_SUCCESS; +} + + +/* See also wrapper function zebra_interface() in zebra/interface.c */ +DEFUN (interface, + interface_cmd, + "interface IFNAME", + "Select an interface to configure\n" + "Interface's name\n") +{ + struct interface *ifp; + + ifp = if_lookup_by_name (argv[0]); + + if (ifp == NULL) + { + ifp = if_create (); + strncpy (ifp->name, argv[0], INTERFACE_NAMSIZ); + } + vty->index = ifp; + vty->node = INTERFACE_NODE; + + return CMD_SUCCESS; +} + +/* For debug purpose. */ +DEFUN (show_address, + show_address_cmd, + "show address", + SHOW_STR + "address\n") +{ + listnode node; + listnode node2; + struct interface *ifp; + struct connected *ifc; + struct prefix *p; + + for (node = listhead (iflist); node; nextnode (node)) + { + ifp = getdata (node); + + for (node2 = listhead (ifp->connected); node2; nextnode (node2)) + { + ifc = getdata (node2); + p = ifc->address; + + if (p->family == AF_INET) + vty_out (vty, "%s/%d%s", inet_ntoa (p->u.prefix4), p->prefixlen, + VTY_NEWLINE); + } + } + return CMD_SUCCESS; +} + +/* Allocate connected structure. */ +struct connected * +connected_new () +{ + struct connected *new = XMALLOC (MTYPE_CONNECTED, sizeof (struct connected)); + memset (new, 0, sizeof (struct connected)); + return new; +} + +/* Free connected structure. */ +void +connected_free (struct connected *connected) +{ + if (connected->address) + prefix_free (connected->address); + + if (connected->destination) + prefix_free (connected->destination); + + if (connected->label) + free (connected->label); + + XFREE (MTYPE_CONNECTED, connected); +} + +/* Print if_addr structure. */ +void +connected_log (struct connected *connected, char *str) +{ + struct prefix *p; + struct interface *ifp; + char logbuf[BUFSIZ]; + char buf[BUFSIZ]; + + ifp = connected->ifp; + p = connected->address; + + snprintf (logbuf, BUFSIZ, "%s interface %s %s %s/%d ", + str, ifp->name, prefix_family_str (p), + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + p = connected->destination; + if (p) + { + strncat (logbuf, inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + BUFSIZ - strlen(logbuf)); + } + zlog (NULL, LOG_INFO, logbuf); +} + +/* If two connected address has same prefix return 1. */ +int +connected_same_prefix (struct prefix *p1, struct prefix *p2) +{ + if (p1->family == p2->family) + { + if (p1->family == AF_INET && + IPV4_ADDR_SAME (&p1->u.prefix4, &p2->u.prefix4)) + return 1; +#ifdef HAVE_IPV6 + if (p1->family == AF_INET6 && + IPV6_ADDR_SAME (&p1->u.prefix6, &p2->u.prefix6)) + return 1; +#endif /* HAVE_IPV6 */ + } + return 0; +} + +struct connected * +connected_delete_by_prefix (struct interface *ifp, struct prefix *p) +{ + struct listnode *node; + struct listnode *next; + struct connected *ifc; + + /* In case of same prefix come, replace it with new one. */ + for (node = listhead (ifp->connected); node; node = next) + { + ifc = getdata (node); + next = node->next; + + if (connected_same_prefix (ifc->address, p)) + { + listnode_delete (ifp->connected, ifc); + return ifc; + } + } + return NULL; +} + +/* Check the connected information is PtP style or not. */ +int +ifc_pointopoint (struct connected *ifc) +{ + struct prefix *p; + int ptp = 0; + + /* When interface has PtP flag. */ + if (if_is_pointopoint (ifc->ifp)) + return 1; + + /* RFC3021 PtP check. */ + p = ifc->address; + + if (p->family == AF_INET) + ptp = (p->prefixlen >= IPV4_MAX_PREFIXLEN - 1); +#ifdef HAVE_IPV6 + if (p->family == AF_INET6) + ptp = (p->prefixlen >= IPV6_MAX_PREFIXLEN - 1); +#endif /* HAVE_IPV6 */ + + return ptp; +} + +#ifndef HAVE_IF_NAMETOINDEX +unsigned int +if_nametoindex (const char *name) +{ + listnode node; + struct interface *ifp; + + for (node = listhead (iflist); node; nextnode (node)) + { + ifp = getdata (node); + if (strcmp (ifp->name, name) == 0) + return ifp->ifindex; + } + return 0; +} +#endif + +#ifndef HAVE_IF_INDEXTONAME +char * +if_indextoname (unsigned int ifindex, char *name) +{ + listnode node; + struct interface *ifp; + + for (node = listhead (iflist); node; nextnode (node)) + { + ifp = getdata (node); + if (ifp->ifindex == ifindex) + { + memcpy (name, ifp->name, IFNAMSIZ); + return ifp->name; + } + } + return NULL; +} +#endif + +/* Interface looking up by interface's address. */ + +/* Interface's IPv4 address reverse lookup table. */ +struct route_table *ifaddr_ipv4_table; +/* struct route_table *ifaddr_ipv6_table; */ + +void +ifaddr_ipv4_add (struct in_addr *ifaddr, struct interface *ifp) +{ + struct route_node *rn; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = *ifaddr; + + rn = route_node_get (ifaddr_ipv4_table, (struct prefix *) &p); + if (rn) + { + route_unlock_node (rn); + zlog_info ("ifaddr_ipv4_add(): address %s is already added", + inet_ntoa (*ifaddr)); + return; + } + rn->info = ifp; +} + +void +ifaddr_ipv4_delete (struct in_addr *ifaddr, struct interface *ifp) +{ + struct route_node *rn; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = *ifaddr; + + rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p); + if (! rn) + { + zlog_info ("ifaddr_ipv4_delete(): can't find address %s", + inet_ntoa (*ifaddr)); + return; + } + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); +} + +/* Lookup interface by interface's IP address or interface index. */ +struct interface * +ifaddr_ipv4_lookup (struct in_addr *addr, unsigned int ifindex) +{ + struct prefix_ipv4 p; + struct route_node *rn; + struct interface *ifp; + listnode node; + + if (addr) + { + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = *addr; + + rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p); + if (! rn) + return NULL; + + ifp = rn->info; + route_unlock_node (rn); + return ifp; + } + else + { + for (node = listhead (iflist); node; nextnode (node)) + { + ifp = getdata (node); + + if (ifp->ifindex == ifindex) + return ifp; + } + } + return NULL; +} + +/* Initialize interface list. */ +void +if_init () +{ + iflist = list_new (); + ifaddr_ipv4_table = route_table_init (); + + if (iflist) + return; + + memset (&if_master, 0, sizeof if_master); +} diff --git a/lib/if.h b/lib/if.h new file mode 100644 index 00000000..3896d187 --- /dev/null +++ b/lib/if.h @@ -0,0 +1,222 @@ +/* Interface related header. + Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + +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. */ + +#ifndef _ZEBRA_IF_H +#define _ZEBRA_IF_H + +#include "linklist.h" + +/* + Interface name length. + + Linux define value in /usr/include/linux/if.h. + #define IFNAMSIZ 16 + + FreeBSD define value in /usr/include/net/if.h. + #define IFNAMSIZ 16 +*/ + +#define INTERFACE_NAMSIZ 20 +#define INTERFACE_HWADDR_MAX 20 + +/* Internal If indexes start at 0xFFFFFFFF and go down to 1 greater + than this */ +#define IFINDEX_INTERNBASE 0x80000000 + +#ifdef HAVE_PROC_NET_DEV +struct if_stats +{ + unsigned long rx_packets; /* total packets received */ + unsigned long tx_packets; /* total packets transmitted */ + unsigned long rx_bytes; /* total bytes received */ + unsigned long tx_bytes; /* total bytes transmitted */ + unsigned long rx_errors; /* bad packets received */ + unsigned long tx_errors; /* packet transmit problems */ + unsigned long rx_dropped; /* no space in linux buffers */ + unsigned long tx_dropped; /* no space available in linux */ + unsigned long rx_multicast; /* multicast packets received */ + unsigned long rx_compressed; + unsigned long tx_compressed; + unsigned long collisions; + + /* detailed rx_errors: */ + unsigned long rx_length_errors; + unsigned long rx_over_errors; /* receiver ring buff overflow */ + unsigned long rx_crc_errors; /* recved pkt with crc error */ + unsigned long rx_frame_errors; /* recv'd frame alignment error */ + unsigned long rx_fifo_errors; /* recv'r fifo overrun */ + unsigned long rx_missed_errors; /* receiver missed packet */ + /* detailed tx_errors */ + unsigned long tx_aborted_errors; + unsigned long tx_carrier_errors; + unsigned long tx_fifo_errors; + unsigned long tx_heartbeat_errors; + unsigned long tx_window_errors; +}; +#endif /* HAVE_PROC_NET_DEV */ + +/* Interface structure */ +struct interface +{ + /* Interface name. */ + char name[INTERFACE_NAMSIZ + 1]; + + /* Interface index. */ + unsigned int ifindex; + + /* Zebra internal interface status */ + u_char status; +#define ZEBRA_INTERFACE_ACTIVE (1 << 0) +#define ZEBRA_INTERFACE_SUB (1 << 1) + + /* Interface flags. */ + unsigned long flags; + + /* Interface metric */ + int metric; + + /* Interface MTU. */ + int mtu; + + /* Hardware address. */ +#ifdef HAVE_SOCKADDR_DL + struct sockaddr_dl sdl; +#else + unsigned short hw_type; + u_char hw_addr[INTERFACE_HWADDR_MAX]; + int hw_addr_len; +#endif /* HAVE_SOCKADDR_DL */ + + /* interface bandwidth, kbits */ + unsigned int bandwidth; + + /* description of the interface. */ + char *desc; + + /* Distribute list. */ + void *distribute_in; + void *distribute_out; + + /* Connected address list. */ + list connected; + + /* Daemon specific interface data pointer. */ + void *info; + + /* Statistics fileds. */ +#ifdef HAVE_PROC_NET_DEV + struct if_stats stats; +#endif /* HAVE_PROC_NET_DEV */ +#ifdef HAVE_NET_RT_IFLIST + struct if_data stats; +#endif /* HAVE_NET_RT_IFLIST */ +}; + +/* Connected address structure. */ +struct connected +{ + /* Attached interface. */ + struct interface *ifp; + + /* Flags for configuration. */ + u_char conf; +#define ZEBRA_IFC_REAL (1 << 0) +#define ZEBRA_IFC_CONFIGURED (1 << 1) + + /* Flags for connected address. */ + u_char flags; +#define ZEBRA_IFA_SECONDARY (1 << 0) + + /* Address of connected network. */ + struct prefix *address; + struct prefix *destination; + + /* Label for Linux 2.2.X and upper. */ + char *label; +}; + +/* Interface hook sort. */ +#define IF_NEW_HOOK 0 +#define IF_DELETE_HOOK 1 + +/* There are some interface flags which are only supported by some + operating system. */ + +#ifndef IFF_NOTRAILERS +#define IFF_NOTRAILERS 0x0 +#endif /* IFF_NOTRAILERS */ +#ifndef IFF_OACTIVE +#define IFF_OACTIVE 0x0 +#endif /* IFF_OACTIVE */ +#ifndef IFF_SIMPLEX +#define IFF_SIMPLEX 0x0 +#endif /* IFF_SIMPLEX */ +#ifndef IFF_LINK0 +#define IFF_LINK0 0x0 +#endif /* IFF_LINK0 */ +#ifndef IFF_LINK1 +#define IFF_LINK1 0x0 +#endif /* IFF_LINK1 */ +#ifndef IFF_LINK2 +#define IFF_LINK2 0x0 +#endif /* IFF_LINK2 */ + +/* Prototypes. */ +struct interface *if_new (void); +struct interface *if_create (void); +struct interface *if_lookup_by_index (unsigned int); +struct interface *if_lookup_by_name (char *); +struct interface *if_lookup_exact_address (struct in_addr); +struct interface *if_lookup_address (struct in_addr); +struct interface *if_get_by_name (char *); +void if_delete (struct interface *); +int if_is_up (struct interface *); +int if_is_loopback (struct interface *); +int if_is_broadcast (struct interface *); +int if_is_pointopoint (struct interface *); +int if_is_multicast (struct interface *); +void if_add_hook (int, int (*)(struct interface *)); +void if_init (); +void if_dump_all (); +char *ifindex2ifname (unsigned int); + +/* Connected address functions. */ +struct connected *connected_new (); +void connected_free (struct connected *); +void connected_add (struct interface *, struct connected *); +struct connected *connected_delete_by_prefix (struct interface *, struct prefix *); +int ifc_pointopoint (struct connected *); + +#ifndef HAVE_IF_NAMETOINDEX +unsigned int if_nametoindex (const char *); +#endif +#ifndef HAVE_IF_INDEXTONAME +char *if_indextoname (unsigned int, char *); +#endif + +/* Exported variables. */ +extern list iflist; +extern struct cmd_element interface_desc_cmd; +extern struct cmd_element no_interface_desc_cmd; +extern struct cmd_element interface_cmd; +extern struct cmd_element interface_pseudo_cmd; +extern struct cmd_element no_interface_pseudo_cmd; + +#endif /* _ZEBRA_IF_H */ diff --git a/lib/if_rmap.c b/lib/if_rmap.c new file mode 100644 index 00000000..d3031fad --- /dev/null +++ b/lib/if_rmap.c @@ -0,0 +1,305 @@ +/* route-map for interface. + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "hash.h" +#include "command.h" +#include "memory.h" +#include "if.h" +#include "if_rmap.h" + +struct hash *ifrmaphash; + +/* Hook functions. */ +void (*if_rmap_add_hook) (struct if_rmap *) = NULL; +void (*if_rmap_delete_hook) (struct if_rmap *) = NULL; + +struct if_rmap * +if_rmap_new () +{ + struct if_rmap *new; + + new = XCALLOC (MTYPE_IF_RMAP, sizeof (struct if_rmap)); + + return new; +} + +void +if_rmap_free (struct if_rmap *if_rmap) +{ + if (if_rmap->ifname) + free (if_rmap->ifname); + + if (if_rmap->routemap[IF_RMAP_IN]) + free (if_rmap->routemap[IF_RMAP_IN]); + if (if_rmap->routemap[IF_RMAP_OUT]) + free (if_rmap->routemap[IF_RMAP_OUT]); + + XFREE (MTYPE_IF_RMAP, if_rmap); +} + +struct if_rmap * +if_rmap_lookup (char *ifname) +{ + struct if_rmap key; + struct if_rmap *if_rmap; + + key.ifname = ifname; + + if_rmap = hash_lookup (ifrmaphash, &key); + + return if_rmap; +} + +void +if_rmap_hook_add (void (*func) (struct if_rmap *)) +{ + if_rmap_add_hook = func; +} + +void +if_rmap_hook_delete (void (*func) (struct if_rmap *)) +{ + if_rmap_delete_hook = func; +} + +void * +if_rmap_hash_alloc (struct if_rmap *arg) +{ + struct if_rmap *if_rmap; + + if_rmap = if_rmap_new (); + if_rmap->ifname = strdup (arg->ifname); + + return if_rmap; +} + +struct if_rmap * +if_rmap_get (char *ifname) +{ + struct if_rmap key; + + key.ifname = ifname; + + return (struct if_rmap *) hash_get (ifrmaphash, &key, if_rmap_hash_alloc); +} + +unsigned int +if_rmap_hash_make (struct if_rmap *if_rmap) +{ + unsigned int key; + int i; + + key = 0; + for (i = 0; i < strlen (if_rmap->ifname); i++) + key += if_rmap->ifname[i]; + + return key; +} + +int +if_rmap_hash_cmp (struct if_rmap *if_rmap1, struct if_rmap *if_rmap2) +{ + if (strcmp (if_rmap1->ifname, if_rmap2->ifname) == 0) + return 1; + return 0; +} + +struct if_rmap * +if_rmap_set (char *ifname, enum if_rmap_type type, char *routemap_name) +{ + struct if_rmap *if_rmap; + + if_rmap = if_rmap_get (ifname); + + if (type == IF_RMAP_IN) + { + if (if_rmap->routemap[IF_RMAP_IN]) + free (if_rmap->routemap[IF_RMAP_IN]); + if_rmap->routemap[IF_RMAP_IN] = strdup (routemap_name); + } + if (type == IF_RMAP_OUT) + { + if (if_rmap->routemap[IF_RMAP_OUT]) + free (if_rmap->routemap[IF_RMAP_OUT]); + if_rmap->routemap[IF_RMAP_OUT] = strdup (routemap_name); + } + + if (if_rmap_add_hook) + (*if_rmap_add_hook) (if_rmap); + + return if_rmap; +} + +int +if_rmap_unset (char *ifname, enum if_rmap_type type, char *routemap_name) +{ + struct if_rmap *if_rmap; + + if_rmap = if_rmap_lookup (ifname); + if (!if_rmap) + return 0; + + if (type == IF_RMAP_IN) + { + if (!if_rmap->routemap[IF_RMAP_IN]) + return 0; + if (strcmp (if_rmap->routemap[IF_RMAP_IN], routemap_name) != 0) + return 0; + + free (if_rmap->routemap[IF_RMAP_IN]); + if_rmap->routemap[IF_RMAP_IN] = NULL; + } + + if (type == IF_RMAP_OUT) + { + if (!if_rmap->routemap[IF_RMAP_OUT]) + return 0; + if (strcmp (if_rmap->routemap[IF_RMAP_OUT], routemap_name) != 0) + return 0; + + free (if_rmap->routemap[IF_RMAP_OUT]); + if_rmap->routemap[IF_RMAP_OUT] = NULL; + } + + if (if_rmap_delete_hook) + (*if_rmap_delete_hook) (if_rmap); + + if (if_rmap->routemap[IF_RMAP_IN] == NULL && + if_rmap->routemap[IF_RMAP_OUT] == NULL) + { + hash_release (ifrmaphash, if_rmap); + if_rmap_free (if_rmap); + } + + return 1; +} + +DEFUN (if_rmap, + if_rmap_cmd, + "route-map RMAP_NAME (in|out) IFNAME", + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" + "Route map interface name\n") +{ + enum if_rmap_type type; + struct if_rmap *if_rmap; + + if (strncmp (argv[1], "i", 1) == 0) + type = IF_RMAP_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = IF_RMAP_OUT; + else + { + vty_out (vty, "route-map direction must be [in|out]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if_rmap = if_rmap_set (argv[2], type, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_if_rmap, + no_if_rmap_cmd, + "no route-map ROUTEMAP_NAME (in|out) IFNAME", + NO_STR + "Route map unset\n" + "Route map name\n" + "Route map for input filtering\n" + "Route map for output filtering\n" + "Route map interface name\n") +{ + int ret; + enum if_rmap_type type; + + if (strncmp (argv[1], "i", 1) == 0) + type = IF_RMAP_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = IF_RMAP_OUT; + else + { + vty_out (vty, "route-map direction must be [in|out]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = if_rmap_unset (argv[2], type, argv[0]); + if (! ret) + { + vty_out (vty, "route-map doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +/* Configuration write function. */ +int +config_write_if_rmap (struct vty *vty) +{ + int i; + struct hash_backet *mp; + int write = 0; + + for (i = 0; i < ifrmaphash->size; i++) + for (mp = ifrmaphash->index[i]; mp; mp = mp->next) + { + struct if_rmap *if_rmap; + + if_rmap = mp->data; + + if (if_rmap->routemap[IF_RMAP_IN]) + { + vty_out (vty, " route-map %s in %s%s", + if_rmap->routemap[IF_RMAP_IN], + if_rmap->ifname, + VTY_NEWLINE); + write++; + } + + if (if_rmap->routemap[IF_RMAP_OUT]) + { + vty_out (vty, " route-map %s out %s%s", + if_rmap->routemap[IF_RMAP_OUT], + if_rmap->ifname, + VTY_NEWLINE); + write++; + } + } + return write; +} + +void +if_rmap_reset () +{ + hash_clean (ifrmaphash, (void (*) (void *)) if_rmap_free); +} + +void +if_rmap_init (int node) +{ + ifrmaphash = hash_create (if_rmap_hash_make, if_rmap_hash_cmp); + + install_element (node, &if_rmap_cmd); + install_element (node, &no_if_rmap_cmd); +} diff --git a/lib/if_rmap.h b/lib/if_rmap.h new file mode 100644 index 00000000..a9355ab9 --- /dev/null +++ b/lib/if_rmap.h @@ -0,0 +1,47 @@ +/* route-map for interface. + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_IF_RMAP_H +#define _ZEBRA_IF_RMAP_H + +enum if_rmap_type +{ + IF_RMAP_IN, + IF_RMAP_OUT, + IF_RMAP_MAX +}; + +struct if_rmap +{ + /* Name of the interface. */ + char *ifname; + + char *routemap[IF_RMAP_MAX]; +}; + +void if_rmap_init (int); +void if_rmap_reset (void); +void if_rmap_hook_add (void (*) (struct if_rmap *)); +void if_rmap_hook_delete (void (*) (struct if_rmap *)); +struct if_rmap *if_rmap_lookup (char *); +int config_write_if_rmap (struct vty *); + +#endif /* _ZEBRA_IF_RMAP_H */ diff --git a/lib/keychain.c b/lib/keychain.c new file mode 100644 index 00000000..dbf431a4 --- /dev/null +++ b/lib/keychain.c @@ -0,0 +1,1001 @@ +/* key-chain for authentication. + Copyright (C) 2000 Kunihiro Ishiguro + +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 <zebra.h> + +#include "command.h" +#include "memory.h" +#include "linklist.h" +#include "keychain.h" + +/* Master list of key chain. */ +struct list *keychain_list; + +struct keychain * +keychain_new () +{ + struct keychain *new; + new = XMALLOC (MTYPE_KEYCHAIN, sizeof (struct keychain)); + memset (new, 0, sizeof (struct keychain)); + return new; +} + +void +keychain_free (struct keychain *keychain) +{ + XFREE (MTYPE_KEYCHAIN, keychain); +} + +struct key * +key_new () +{ + struct key *new; + new = XMALLOC (MTYPE_KEY, sizeof (struct key)); + memset (new, 0, sizeof (struct key)); + return new; +} + +void +key_free (struct key *key) +{ + XFREE (MTYPE_KEY, key); +} + +struct keychain * +keychain_lookup (char *name) +{ + struct listnode *nn; + struct keychain *keychain; + + if (name == NULL) + return NULL; + + LIST_LOOP (keychain_list, keychain, nn) + { + if (strcmp (keychain->name, name) == 0) + return keychain; + } + return NULL; +} + +int +key_cmp_func (struct key *k1, struct key *k2) +{ + if (k1->index > k2->index) + return 1; + if (k1->index < k2->index) + return -1; + return 0; +} + +void +key_delete_func (struct key *key) +{ + if (key->string) + free (key->string); + key_free (key); +} + +struct keychain * +keychain_get (char *name) +{ + struct keychain *keychain; + + keychain = keychain_lookup (name); + + if (keychain) + return keychain; + + keychain = keychain_new (); + keychain->name = strdup (name); + keychain->key = list_new (); + keychain->key->cmp = (int (*)(void *, void *)) key_cmp_func; + keychain->key->del = (void (*)(void *)) key_delete_func; + listnode_add (keychain_list, keychain); + + return keychain; +} + +void +keychain_delete (struct keychain *keychain) +{ + if (keychain->name) + free (keychain->name); + + list_delete (keychain->key); + listnode_delete (keychain_list, keychain); + keychain_free (keychain); +} + +struct key * +key_lookup (struct keychain *keychain, u_int32_t index) +{ + struct listnode *nn; + struct key *key; + + LIST_LOOP (keychain->key, key, nn) + { + if (key->index == index) + return key; + } + return NULL; +} + +struct key * +key_lookup_for_accept (struct keychain *keychain, u_int32_t index) +{ + struct listnode *nn; + struct key *key; + time_t now; + + now = time (NULL); + + LIST_LOOP (keychain->key, key, nn) + { + if (key->index >= index) + { + if (key->accept.start == 0) + return key; + + if (key->accept.start <= now) + if (key->accept.end >= now || key->accept.end == -1) + return key; + } + } + return NULL; +} + +struct key * +key_match_for_accept (struct keychain *keychain, char *auth_str) +{ + struct listnode *nn; + struct key *key; + time_t now; + + now = time (NULL); + + LIST_LOOP (keychain->key, key, nn) + { + if (key->accept.start == 0 || + (key->accept.start <= now && + (key->accept.end >= now || key->accept.end == -1))) + if (strncmp (key->string, auth_str, 16) == 0) + return key; + } + return NULL; +} + +struct key * +key_lookup_for_send (struct keychain *keychain) +{ + struct listnode *nn; + struct key *key; + time_t now; + + now = time (NULL); + + LIST_LOOP (keychain->key, key, nn) + { + if (key->send.start == 0) + return key; + + if (key->send.start <= now) + if (key->send.end >= now || key->send.end == -1) + return key; + } + return NULL; +} + +struct key * +key_get (struct keychain *keychain, u_int32_t index) +{ + struct key *key; + + key = key_lookup (keychain, index); + + if (key) + return key; + + key = key_new (); + key->index = index; + listnode_add_sort (keychain->key, key); + + return key; +} + +void +key_delete (struct keychain *keychain, struct key *key) +{ + listnode_delete (keychain->key, key); + + if (key->string) + free (key->string); + key_free (key); +} + +DEFUN (key_chain, + key_chain_cmd, + "key chain WORD", + "Authentication key management\n" + "Key-chain management\n" + "Key-chain name\n") +{ + struct keychain *keychain; + + keychain = keychain_get (argv[0]); + vty->index = keychain; + vty->node = KEYCHAIN_NODE; + + return CMD_SUCCESS; +} + +DEFUN (no_key_chain, + no_key_chain_cmd, + "no key chain WORD", + NO_STR + "Authentication key management\n" + "Key-chain management\n" + "Key-chain name\n") +{ + struct keychain *keychain; + + keychain = keychain_lookup (argv[0]); + + if (! keychain) + { + vty_out (vty, "Can't find keychain %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + keychain_delete (keychain); + + return CMD_SUCCESS; +} + +DEFUN (key, + key_cmd, + "key <0-2147483647>", + "Configure a key\n" + "Key identifier number\n") +{ + struct keychain *keychain; + struct key *key; + u_int32_t index; + char *endptr = NULL; + + keychain = vty->index; + + index = strtoul (argv[0], &endptr, 10); + if (index == ULONG_MAX || *endptr != '\0') + { + vty_out (vty, "Key identifier number error%s", VTY_NEWLINE); + return CMD_WARNING; + } + key = key_get (keychain, index); + vty->index_sub = key; + vty->node = KEYCHAIN_KEY_NODE; + + return CMD_SUCCESS; +} + +DEFUN (no_key, + no_key_cmd, + "no key <0-2147483647>", + NO_STR + "Delete a key\n" + "Key identifier number\n") +{ + struct keychain *keychain; + struct key *key; + u_int32_t index; + char *endptr = NULL; + + keychain = vty->index; + + index = strtoul (argv[0], &endptr, 10); + if (index == ULONG_MAX || *endptr != '\0') + { + vty_out (vty, "Key identifier number error%s", VTY_NEWLINE); + return CMD_WARNING; + } + + key = key_lookup (keychain, index); + if (! key) + { + vty_out (vty, "Can't find key %d%s", index, VTY_NEWLINE); + return CMD_WARNING; + } + + key_delete (keychain, key); + + vty->node = KEYCHAIN_NODE; + + return CMD_SUCCESS; +} + +DEFUN (key_string, + key_string_cmd, + "key-string LINE", + "Set key string\n" + "The key\n") +{ + struct key *key; + + key = vty->index_sub; + + if (key->string) + free (key->string); + key->string = strdup (argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_key_string, + no_key_string_cmd, + "no key-string [LINE]", + NO_STR + "Unset key string\n" + "The key\n") +{ + struct key *key; + + key = vty->index_sub; + + if (key->string) + { + free (key->string); + key->string = NULL; + } + + return CMD_SUCCESS; +} + +/* Convert HH:MM:SS MON DAY YEAR to time_t value. -1 is returned when + given string is malformed. */ +time_t +key_str2time(char *time_str, char *day_str, char *month_str, char *year_str) +{ + int i = 0; + char *colon; + struct tm tm; + time_t time; + int sec, min, hour; + int day, month, year; + char *endptr = NULL; + + char *month_name[] = + { + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + NULL + }; + + /* Check hour field of time_str. */ + colon = strchr (time_str, ':'); + if (colon == NULL) + return -1; + *colon = '\0'; + + /* Hour must be between 0 and 23. */ + hour = strtoul (time_str, &endptr, 10); + if (hour == ULONG_MAX || *endptr != '\0' || hour < 0 || hour > 23) + return -1; + + /* Check min field of time_str. */ + time_str = colon + 1; + colon = strchr (time_str, ':'); + if (*time_str == '\0' || colon == NULL) + return -1; + *colon = '\0'; + + /* Min must be between 0 and 59. */ + min = strtoul (time_str, &endptr, 10); + if (min == ULONG_MAX || *endptr != '\0' || min < 0 || min > 59) + return -1; + + /* Check sec field of time_str. */ + time_str = colon + 1; + if (*time_str == '\0') + return -1; + + /* Sec must be between 0 and 59. */ + sec = strtoul (time_str, &endptr, 10); + if (sec == ULONG_MAX || *endptr != '\0' || sec < 0 || sec > 59) + return -1; + + /* Check day_str. Day must be <1-31>. */ + day = strtoul (day_str, &endptr, 10); + if (day == ULONG_MAX || *endptr != '\0' || day < 0 || day > 31) + return -1; + + /* Check month_str. Month must match month_name. */ + month = 0; + if (strlen (month_str) >= 3) + for (i = 0; month_name[i]; i++) + if (strncmp (month_str, month_name[i], strlen (month_str)) == 0) + { + month = i; + break; + } + if (! month_name[i]) + return -1; + + /* Check year_str. Year must be <1993-2035>. */ + year = strtoul (year_str, &endptr, 10); + if (year == ULONG_MAX || *endptr != '\0' || year < 1993 || year > 2035) + return -1; + + memset (&tm, 0, sizeof (struct tm)); + tm.tm_sec = sec; + tm.tm_min = min; + tm.tm_hour = hour; + tm.tm_mon = month; + tm.tm_mday = day; + tm.tm_year = year - 1900; + + time = mktime (&tm); + + return time; +} + +int +key_lifetime_set (struct vty *vty, struct key_range *krange, char *stime_str, + char *sday_str, char *smonth_str, char *syear_str, + char *etime_str, char *eday_str, char *emonth_str, + char *eyear_str) +{ + time_t time_start; + time_t time_end; + + time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str); + if (time_start < 0) + { + vty_out (vty, "Malformed time value%s", VTY_NEWLINE); + return CMD_WARNING; + } + time_end = key_str2time (etime_str, eday_str, emonth_str, eyear_str); + + if (time_end < 0) + { + vty_out (vty, "Malformed time value%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (time_end <= time_start) + { + vty_out (vty, "Expire time is not later than start time%s", VTY_NEWLINE); + return CMD_WARNING; + } + + krange->start = time_start; + krange->end = time_end; + + return CMD_SUCCESS; +} + +int +key_lifetime_duration_set (struct vty *vty, struct key_range *krange, + char *stime_str, char *sday_str, char *smonth_str, + char *syear_str, char *duration_str) +{ + time_t time_start; + u_int32_t duration; + char *endptr = NULL; + + time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str); + if (time_start < 0) + { + vty_out (vty, "Malformed time value%s", VTY_NEWLINE); + return CMD_WARNING; + } + krange->start = time_start; + + duration = strtoul (duration_str, &endptr, 10); + if (duration == ULONG_MAX || *endptr != '\0') + { + vty_out (vty, "Malformed duration%s", VTY_NEWLINE); + return CMD_WARNING; + } + krange->duration = 1; + krange->end = time_start + duration; + + return CMD_SUCCESS; +} + +int +key_lifetime_infinite_set (struct vty *vty, struct key_range *krange, + char *stime_str, char *sday_str, char *smonth_str, + char *syear_str) +{ + time_t time_start; + + time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str); + if (time_start < 0) + { + vty_out (vty, "Malformed time value%s", VTY_NEWLINE); + return CMD_WARNING; + } + krange->start = time_start; + + krange->end = -1; + + return CMD_SUCCESS; +} + +DEFUN (accept_lifetime_day_month_day_month, + accept_lifetime_day_month_day_month_cmd, + "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->accept, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], argv[6], argv[7]); +} + +DEFUN (accept_lifetime_day_month_month_day, + accept_lifetime_day_month_month_day_cmd, + "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->accept, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[6], argv[5], argv[7]); +} + +DEFUN (accept_lifetime_month_day_day_month, + accept_lifetime_month_day_day_month_cmd, + "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->accept, argv[0], argv[2], argv[1], + argv[3], argv[4], argv[5], argv[6], argv[7]); +} + +DEFUN (accept_lifetime_month_day_month_day, + accept_lifetime_month_day_month_day_cmd, + "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->accept, argv[0], argv[2], argv[1], + argv[3], argv[4], argv[6], argv[5], argv[7]); +} + +DEFUN (accept_lifetime_infinite_day_month, + accept_lifetime_infinite_day_month_cmd, + "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> infinite", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Never expires") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_infinite_set (vty, &key->accept, argv[0], argv[1], + argv[2], argv[3]); +} + +DEFUN (accept_lifetime_infinite_month_day, + accept_lifetime_infinite_month_day_cmd, + "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> infinite", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Never expires") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_infinite_set (vty, &key->accept, argv[0], argv[2], + argv[1], argv[3]); +} + +DEFUN (accept_lifetime_duration_day_month, + accept_lifetime_duration_day_month_cmd, + "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> duration <1-2147483646>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[1], + argv[2], argv[3], argv[4]); +} + +DEFUN (accept_lifetime_duration_month_day, + accept_lifetime_duration_month_day_cmd, + "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> duration <1-2147483646>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[2], + argv[1], argv[3], argv[4]); +} + +DEFUN (send_lifetime_day_month_day_month, + send_lifetime_day_month_day_month_cmd, + "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6], argv[7]); +} + +DEFUN (send_lifetime_day_month_month_day, + send_lifetime_day_month_month_day_cmd, + "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3], + argv[4], argv[6], argv[5], argv[7]); +} + +DEFUN (send_lifetime_month_day_day_month, + send_lifetime_month_day_day_month_cmd, + "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3], + argv[4], argv[5], argv[6], argv[7]); +} + +DEFUN (send_lifetime_month_day_month_day, + send_lifetime_month_day_month_day_cmd, + "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3], + argv[4], argv[6], argv[5], argv[7]); +} + +DEFUN (send_lifetime_infinite_day_month, + send_lifetime_infinite_day_month_cmd, + "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> infinite", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Never expires") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_infinite_set (vty, &key->send, argv[0], argv[1], argv[2], + argv[3]); +} + +DEFUN (send_lifetime_infinite_month_day, + send_lifetime_infinite_month_day_cmd, + "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> infinite", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Never expires") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_infinite_set (vty, &key->send, argv[0], argv[2], argv[1], + argv[3]); +} + +DEFUN (send_lifetime_duration_day_month, + send_lifetime_duration_day_month_cmd, + "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> duration <1-2147483646>", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_duration_set (vty, &key->send, argv[0], argv[1], argv[2], + argv[3], argv[4]); +} + +DEFUN (send_lifetime_duration_month_day, + send_lifetime_duration_month_day_cmd, + "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> duration <1-2147483646>", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_duration_set (vty, &key->send, argv[0], argv[2], argv[1], + argv[3], argv[4]); +} + +struct cmd_node keychain_node = +{ + KEYCHAIN_NODE, + "%s(config-keychain)# ", + 1 +}; + +struct cmd_node keychain_key_node = +{ + KEYCHAIN_KEY_NODE, + "%s(config-keychain-key)# ", + 1 +}; + +int +keychain_strftime (char *buf, int bufsiz, time_t *time) +{ + struct tm *tm; + size_t len; + + tm = localtime (time); + + len = strftime (buf, bufsiz, "%T %b %d %Y", tm); + + return len; +} + +int +keychain_config_write (struct vty *vty) +{ + struct keychain *keychain; + struct key *key; + struct listnode *nn; + struct listnode *nm; + char buf[BUFSIZ]; + + LIST_LOOP (keychain_list, keychain, nn) + { + vty_out (vty, "key chain %s%s", keychain->name, VTY_NEWLINE); + + LIST_LOOP (keychain->key, key, nm) + { + vty_out (vty, " key %d%s", key->index, VTY_NEWLINE); + + if (key->string) + vty_out (vty, " key-string %s%s", key->string, VTY_NEWLINE); + + if (key->accept.start) + { + keychain_strftime (buf, BUFSIZ, &key->accept.start); + vty_out (vty, " accept-lifetime %s", buf); + + if (key->accept.end == -1) + vty_out (vty, " infinite"); + else if (key->accept.duration) + vty_out (vty, " duration %ld", + key->accept.end - key->accept.start); + else + { + keychain_strftime (buf, BUFSIZ, &key->accept.end); + vty_out (vty, " %s", buf); + } + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (key->send.start) + { + keychain_strftime (buf, BUFSIZ, &key->send.start); + vty_out (vty, " send-lifetime %s", buf); + + if (key->send.end == -1) + vty_out (vty, " infinite"); + else if (key->send.duration) + vty_out (vty, " duration %ld", key->send.end - key->send.start); + else + { + keychain_strftime (buf, BUFSIZ, &key->send.end); + vty_out (vty, " %s", buf); + } + vty_out (vty, "%s", VTY_NEWLINE); + } + } + vty_out (vty, "!%s", VTY_NEWLINE); + } + + return 0; +} + +void +keychain_init () +{ + keychain_list = list_new (); + + install_node (&keychain_node, keychain_config_write); + install_node (&keychain_key_node, NULL); + + install_default (KEYCHAIN_NODE); + install_default (KEYCHAIN_KEY_NODE); + + install_element (CONFIG_NODE, &key_chain_cmd); + install_element (CONFIG_NODE, &no_key_chain_cmd); + install_element (KEYCHAIN_NODE, &key_cmd); + install_element (KEYCHAIN_NODE, &no_key_cmd); + + install_element (KEYCHAIN_NODE, &key_chain_cmd); + install_element (KEYCHAIN_NODE, &no_key_chain_cmd); + + install_element (KEYCHAIN_KEY_NODE, &key_string_cmd); + install_element (KEYCHAIN_KEY_NODE, &no_key_string_cmd); + + install_element (KEYCHAIN_KEY_NODE, &key_chain_cmd); + install_element (KEYCHAIN_KEY_NODE, &no_key_chain_cmd); + + install_element (KEYCHAIN_KEY_NODE, &key_cmd); + install_element (KEYCHAIN_KEY_NODE, &no_key_cmd); + + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_day_month_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_day_month_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_month_day_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_month_day_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_infinite_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_infinite_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_duration_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_duration_month_day_cmd); + + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_day_month_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_day_month_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_month_day_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_month_day_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_infinite_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_infinite_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_duration_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_duration_month_day_cmd); +} diff --git a/lib/keychain.h b/lib/keychain.h new file mode 100644 index 00000000..0cfa3d52 --- /dev/null +++ b/lib/keychain.h @@ -0,0 +1,56 @@ +/* key-chain for authentication. + * Copyright (C) 2000 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_KEYCHAIN_H +#define _ZEBRA_KEYCHAIN_H + +struct keychain +{ + char *name; + + struct list *key; +}; + +struct key_range +{ + time_t start; + time_t end; + + u_char duration; +}; + +struct key +{ + u_int32_t index; + + char *string; + + struct key_range send; + struct key_range accept; +}; + +void keychain_init (); +struct keychain *keychain_lookup (char *); +struct key *key_lookup_for_accept (struct keychain *, u_int32_t); +struct key *key_match_for_accept (struct keychain *, char *); +struct key *key_lookup_for_send (struct keychain *); + +#endif /* _ZEBRA_KEYCHAIN_H */ diff --git a/lib/linklist.c b/lib/linklist.c new file mode 100644 index 00000000..5a2b6969 --- /dev/null +++ b/lib/linklist.c @@ -0,0 +1,312 @@ +/* Generic linked list routine. + * Copyright (C) 1997, 2000 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "linklist.h" +#include "memory.h" + +/* Allocate new list. */ +struct list * +list_new () +{ + struct list *new; + + new = XMALLOC (MTYPE_LINK_LIST, sizeof (struct list)); + memset (new, 0, sizeof (struct list)); + return new; +} + +/* Free list. */ +void +list_free (struct list *l) +{ + XFREE (MTYPE_LINK_LIST, l); +} + +/* Allocate new listnode. Internal use only. */ +static struct listnode * +listnode_new () +{ + struct listnode *node; + + node = XMALLOC (MTYPE_LINK_NODE, sizeof (struct listnode)); + memset (node, 0, sizeof (struct listnode)); + return node; +} + +/* Free listnode. */ +static void +listnode_free (struct listnode *node) +{ + XFREE (MTYPE_LINK_NODE, node); +} + +/* Add new data to the list. */ +void +listnode_add (struct list *list, void *val) +{ + struct listnode *node; + + node = listnode_new (); + + node->prev = list->tail; + node->data = val; + + if (list->head == NULL) + list->head = node; + else + list->tail->next = node; + list->tail = node; + + list->count++; +} + +/* Add new node with sort function. */ +void +listnode_add_sort (struct list *list, void *val) +{ + struct listnode *n; + struct listnode *new; + + new = listnode_new (); + new->data = val; + + if (list->cmp) + { + for (n = list->head; n; n = n->next) + { + if ((*list->cmp) (val, n->data) < 0) + { + new->next = n; + new->prev = n->prev; + + if (n->prev) + n->prev->next = new; + else + list->head = new; + n->prev = new; + list->count++; + return; + } + } + } + + new->prev = list->tail; + + if (list->tail) + list->tail->next = new; + else + list->head = new; + + list->tail = new; + list->count++; +} + +void +listnode_add_after (struct list *list, struct listnode *pp, void *val) +{ + struct listnode *nn; + + nn = listnode_new (); + nn->data = val; + + if (pp == NULL) + { + if (list->head) + list->head->prev = nn; + else + list->tail = nn; + + nn->next = list->head; + nn->prev = pp; + + list->head = nn; + } + else + { + if (pp->next) + pp->next->prev = nn; + else + list->tail = nn; + + nn->next = pp->next; + nn->prev = pp; + + pp->next = nn; + } +} + + +/* Delete specific date pointer from the list. */ +void +listnode_delete (struct list *list, void *val) +{ + struct listnode *node; + + for (node = list->head; node; node = node->next) + { + if (node->data == val) + { + if (node->prev) + node->prev->next = node->next; + else + list->head = node->next; + + if (node->next) + node->next->prev = node->prev; + else + list->tail = node->prev; + + list->count--; + listnode_free (node); + return; + } + } +} + +/* Return first node's data if it is there. */ +void * +listnode_head (struct list *list) +{ + struct listnode *node; + + node = list->head; + + if (node) + return node->data; + return NULL; +} + +/* Delete all listnode from the list. */ +void +list_delete_all_node (struct list *list) +{ + struct listnode *node; + struct listnode *next; + + for (node = list->head; node; node = next) + { + next = node->next; + if (list->del) + (*list->del) (node->data); + listnode_free (node); + } + list->head = list->tail = NULL; + list->count = 0; +} + +/* Delete all listnode then free list itself. */ +void +list_delete (struct list *list) +{ + struct listnode *node; + struct listnode *next; + + for (node = list->head; node; node = next) + { + next = node->next; + if (list->del) + (*list->del) (node->data); + listnode_free (node); + } + list_free (list); +} + +/* Lookup the node which has given data. */ +struct listnode * +listnode_lookup (struct list *list, void *data) +{ + listnode node; + + for (node = list->head; node; nextnode (node)) + if (data == getdata (node)) + return node; + return NULL; +} + +/* Delete the node from list. For ospfd and ospf6d. */ +void +list_delete_node (list list, listnode node) +{ + if (node->prev) + node->prev->next = node->next; + else + list->head = node->next; + if (node->next) + node->next->prev = node->prev; + else + list->tail = node->prev; + list->count--; + listnode_free (node); +} + +/* ospf_spf.c */ +void +list_add_node_prev (list list, listnode current, void *val) +{ + struct listnode *node; + + node = listnode_new (); + node->next = current; + node->data = val; + + if (current->prev == NULL) + list->head = node; + else + current->prev->next = node; + + node->prev = current->prev; + current->prev = node; + + list->count++; +} + +/* ospf_spf.c */ +void +list_add_node_next (list list, listnode current, void *val) +{ + struct listnode *node; + + node = listnode_new (); + node->prev = current; + node->data = val; + + if (current->next == NULL) + list->tail = node; + else + current->next->prev = node; + + node->next = current->next; + current->next = node; + + list->count++; +} + +/* ospf_spf.c */ +void +list_add_list (struct list *l, struct list *m) +{ + struct listnode *n; + + for (n = listhead (m); n; nextnode (n)) + listnode_add (l, n->data); +} diff --git a/lib/linklist.h b/lib/linklist.h new file mode 100644 index 00000000..a91947c3 --- /dev/null +++ b/lib/linklist.h @@ -0,0 +1,101 @@ +/* Generic linked list + * Copyright (C) 1997, 2000 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_LINKLIST_H +#define _ZEBRA_LINKLIST_H + +typedef struct list *list; +typedef struct listnode *listnode; + +struct listnode +{ + struct listnode *next; + struct listnode *prev; + void *data; +}; + +struct list +{ + struct listnode *head; + struct listnode *tail; + unsigned int count; + int (*cmp) (void *val1, void *val2); + void (*del) (void *val); +}; + +#define nextnode(X) ((X) = (X)->next) +#define listhead(X) ((X)->head) +#define listcount(X) ((X)->count) +#define list_isempty(X) ((X)->head == NULL && (X)->tail == NULL) +#define getdata(X) ((X)->data) + +/* Prototypes. */ +struct list *list_new(); +void list_free (struct list *); + +void listnode_add (struct list *, void *); +void listnode_add_sort (struct list *, void *); +void listnode_add_after (struct list *, struct listnode *, void *); +void listnode_delete (struct list *, void *); +struct listnode *listnode_lookup (struct list *, void *); +void *listnode_head (struct list *); + +void list_delete (struct list *); +void list_delete_all_node (struct list *); + +/* For ospfd and ospf6d. */ +void list_delete_node (list, listnode); + +/* For ospf_spf.c */ +void list_add_node_prev (list, listnode, void *); +void list_add_node_next (list, listnode, void *); +void list_add_list (list, list); + +/* List iteration macro. */ +#define LIST_LOOP(L,V,N) \ + for ((N) = (L)->head; (N); (N) = (N)->next) \ + if (((V) = (N)->data) != NULL) + +/* List node add macro. */ +#define LISTNODE_ADD(L,N) \ + do { \ + (N)->prev = (L)->tail; \ + if ((L)->head == NULL) \ + (L)->head = (N); \ + else \ + (L)->tail->next = (N); \ + (L)->tail = (N); \ + } while (0) + +/* List node delete macro. */ +#define LISTNODE_DELETE(L,N) \ + do { \ + if ((N)->prev) \ + (N)->prev->next = (N)->next; \ + else \ + (L)->head = (N)->next; \ + if ((N)->next) \ + (N)->next->prev = (N)->prev; \ + else \ + (L)->tail = (N)->prev; \ + } while (0) + +#endif /* _ZEBRA_LINKLIST_H */ diff --git a/lib/log.c b/lib/log.c new file mode 100644 index 00000000..9c676428 --- /dev/null +++ b/lib/log.c @@ -0,0 +1,483 @@ +/* Logging of zebra + * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "log.h" +#include "memory.h" +#include "command.h" + +struct zlog *zlog_default = NULL; + +const char *zlog_proto_names[] = +{ + "NONE", + "DEFAULT", + "ZEBRA", + "RIP", + "BGP", + "OSPF", + "RIPNG", + "OSPF6", + "MASC", + NULL, +}; + +const char *zlog_priority[] = +{ + "emergencies", + "alerts", + "critical", + "errors", + "warnings", + "notifications", + "informational", + "debugging", + NULL, +}; + + + +/* For time string format. */ +#define TIME_BUF 27 + +/* Utility routine for current time printing. */ +static void +time_print (FILE *fp) +{ + int ret; + char buf [TIME_BUF]; + time_t clock; + struct tm *tm; + + time (&clock); + tm = localtime (&clock); + + ret = strftime (buf, TIME_BUF, "%Y/%m/%d %H:%M:%S", tm); + if (ret == 0) { + zlog_warn ("strftime error"); + } + + fprintf (fp, "%s ", buf); +} + +/* va_list version of zlog. */ +void +vzlog (struct zlog *zl, int priority, const char *format, va_list *args) +{ + /* If zlog is not specified, use default one. */ + if (zl == NULL) + zl = zlog_default; + + /* When zlog_default is also NULL, use stderr for logging. */ + if (zl == NULL) + { + time_print (stderr); + fprintf (stderr, "%s: ", "unknown"); + vfprintf (stderr, format, args[ZLOG_NOLOG_INDEX]); + fprintf (stderr, "\n"); + fflush (stderr); + + /* In this case we return at here. */ + return; + } + + /* only log this information if it has not been masked out */ + if ( priority > zl->maskpri ) + return ; + + /* Syslog output */ + if (zl->flags & ZLOG_SYSLOG) + vsyslog (priority, format, args[ZLOG_SYSLOG_INDEX]); + + /* File output. */ + if (zl->flags & ZLOG_FILE) + { + time_print (zl->fp); + if (zl->record_priority) fprintf (zl->fp, "%s: ", zlog_priority[priority]); + fprintf (zl->fp, "%s: ", zlog_proto_names[zl->protocol]); + vfprintf (zl->fp, format, args[ZLOG_FILE_INDEX]); + fprintf (zl->fp, "\n"); + fflush (zl->fp); + } + + /* stdout output. */ + if (zl->flags & ZLOG_STDOUT) + { + time_print (stdout); + if (zl->record_priority) fprintf (stdout, "%s: ", zlog_priority[priority]); + fprintf (stdout, "%s: ", zlog_proto_names[zl->protocol]); + vfprintf (stdout, format, args[ZLOG_STDOUT_INDEX]); + fprintf (stdout, "\n"); + fflush (stdout); + } + + /* stderr output. */ + if (zl->flags & ZLOG_STDERR) + { + time_print (stderr); + if (zl->record_priority) fprintf (stderr, "%s: ", zlog_priority[priority]); + fprintf (stderr, "%s: ", zlog_proto_names[zl->protocol]); + vfprintf (stderr, format, args[ZLOG_STDERR_INDEX]); + fprintf (stderr, "\n"); + fflush (stderr); + } + + /* Terminal monitor. */ + vty_log (zlog_proto_names[zl->protocol], format, args[ZLOG_NOLOG_INDEX]); +} + +void +zlog (struct zlog *zl, int priority, const char *format, ...) +{ + va_list args[ZLOG_MAX_INDEX]; + int index; + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_start(args[index], format); + + vzlog (zl, priority, format, args); + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_end (args[index]); +} + +void +zlog_err (const char *format, ...) +{ + va_list args[ZLOG_MAX_INDEX]; + int index; + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_start(args[index], format); + + vzlog (NULL, LOG_ERR, format, args); + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_end (args[index]); +} + +void +zlog_warn (const char *format, ...) +{ + va_list args[ZLOG_MAX_INDEX]; + int index; + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_start(args[index], format); + + vzlog (NULL, LOG_WARNING, format, args); + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_end (args[index]); +} + +void +zlog_info (const char *format, ...) +{ + va_list args[ZLOG_MAX_INDEX]; + int index; + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_start(args[index], format); + + vzlog (NULL, LOG_INFO, format, args); + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_end (args[index]); +} + +void +zlog_notice (const char *format, ...) +{ + va_list args[ZLOG_MAX_INDEX]; + int index; + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_start(args[index], format); + + vzlog (NULL, LOG_NOTICE, format, args); + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_end (args[index]); +} + +void +zlog_debug (const char *format, ...) +{ + va_list args[ZLOG_MAX_INDEX]; + int index; + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_start(args[index], format); + + vzlog (NULL, LOG_DEBUG, format, args); + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_end (args[index]); +} + +void +plog_err (struct zlog *zl, const char *format, ...) +{ + va_list args[ZLOG_MAX_INDEX]; + int index; + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_start(args[index], format); + + vzlog (zl, LOG_ERR, format, args); + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_end (args[index]); +} + +void +plog_warn (struct zlog *zl, const char *format, ...) +{ + va_list args[ZLOG_MAX_INDEX]; + int index; + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_start(args[index], format); + + vzlog (zl, LOG_WARNING, format, args); + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_end (args[index]); +} + +void +plog_info (struct zlog *zl, const char *format, ...) +{ + va_list args[ZLOG_MAX_INDEX]; + int index; + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_start(args[index], format); + + vzlog (zl, LOG_INFO, format, args); + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_end (args[index]); +} + +void +plog_notice (struct zlog *zl, const char *format, ...) +{ + va_list args[ZLOG_MAX_INDEX]; + int index; + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_start(args[index], format); + + vzlog (zl, LOG_NOTICE, format, args); + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_end (args[index]); +} + +void +plog_debug (struct zlog *zl, const char *format, ...) +{ + va_list args[ZLOG_MAX_INDEX]; + int index; + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_start(args[index], format); + + vzlog (zl, LOG_DEBUG, format, args); + + for (index = 0; index < ZLOG_MAX_INDEX; index++) + va_end (args[index]); +} + + +/* Open log stream */ +struct zlog * +openzlog (const char *progname, int flags, zlog_proto_t protocol, + int syslog_flags, int syslog_facility) +{ + struct zlog *zl; + + zl = XMALLOC(MTYPE_ZLOG, sizeof (struct zlog)); + memset (zl, 0, sizeof (struct zlog)); + + zl->ident = progname; + zl->flags = flags; + zl->protocol = protocol; + zl->facility = syslog_facility; + zl->maskpri = LOG_DEBUG; + zl->record_priority = 0; + + openlog (progname, syslog_flags, zl->facility); + + return zl; +} + +void +closezlog (struct zlog *zl) +{ + closelog(); + fclose (zl->fp); + + XFREE (MTYPE_ZLOG, zl); +} + +/* Called from command.c. */ +void +zlog_set_flag (struct zlog *zl, int flags) +{ + if (zl == NULL) + zl = zlog_default; + + zl->flags |= flags; +} + +void +zlog_reset_flag (struct zlog *zl, int flags) +{ + if (zl == NULL) + zl = zlog_default; + + zl->flags &= ~flags; +} + +int +zlog_set_file (struct zlog *zl, int flags, char *filename) +{ + FILE *fp; + + /* There is opend file. */ + zlog_reset_file (zl); + + /* Set default zl. */ + if (zl == NULL) + zl = zlog_default; + + /* Open file. */ + fp = fopen (filename, "a"); + if (fp == NULL) + return 0; + + /* Set flags. */ + zl->filename = strdup (filename); + zl->flags |= ZLOG_FILE; + zl->fp = fp; + + return 1; +} + +/* Reset opend file. */ +int +zlog_reset_file (struct zlog *zl) +{ + if (zl == NULL) + zl = zlog_default; + + zl->flags &= ~ZLOG_FILE; + + if (zl->fp) + fclose (zl->fp); + zl->fp = NULL; + + if (zl->filename) + free (zl->filename); + zl->filename = NULL; + + return 1; +} + +/* Reopen log file. */ +int +zlog_rotate (struct zlog *zl) +{ + FILE *fp; + + if (zl == NULL) + zl = zlog_default; + + if (zl->fp) + fclose (zl->fp); + zl->fp = NULL; + + if (zl->filename) + { + fp = fopen (zl->filename, "a"); + if (fp == NULL) + return -1; + zl->fp = fp; + } + + return 1; +} + +static char *zlog_cwd = NULL; + +void +zlog_save_cwd () +{ + char *cwd; + + cwd = getcwd (NULL, MAXPATHLEN); + + zlog_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1); + strcpy (zlog_cwd, cwd); +} + +char * +zlog_get_cwd () +{ + return zlog_cwd; +} + +void +zlog_free_cwd () +{ + if (zlog_cwd) + XFREE (MTYPE_TMP, zlog_cwd); +} + +/* Message lookup function. */ +char * +lookup (struct message *mes, int key) +{ + struct message *pnt; + + for (pnt = mes; pnt->key != 0; pnt++) + if (pnt->key == key) + return pnt->str; + + return ""; +} + +/* Very old hacky version of message lookup function. Still partly + used in bgpd and ospfd. */ +char * +mes_lookup (struct message *meslist, int max, int index) +{ + if (index < 0 || index >= max) + { + zlog_err ("message index out of bound: %d", max); + return NULL; + } + return meslist[index].str; +} diff --git a/lib/log.h b/lib/log.h new file mode 100644 index 00000000..69919b49 --- /dev/null +++ b/lib/log.h @@ -0,0 +1,128 @@ +/* Zebra logging funcions. + * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_LOG_H +#define _ZEBRA_LOG_H + +#include <syslog.h> + +#define ZLOG_NOLOG 0x00 +#define ZLOG_FILE 0x01 +#define ZLOG_SYSLOG 0x02 +#define ZLOG_STDOUT 0x04 +#define ZLOG_STDERR 0x08 + +#define ZLOG_NOLOG_INDEX 0 +#define ZLOG_FILE_INDEX 1 +#define ZLOG_SYSLOG_INDEX 2 +#define ZLOG_STDOUT_INDEX 3 +#define ZLOG_STDERR_INDEX 4 +#define ZLOG_MAX_INDEX 5 + +typedef enum +{ + ZLOG_NONE, + ZLOG_DEFAULT, + ZLOG_ZEBRA, + ZLOG_RIP, + ZLOG_BGP, + ZLOG_OSPF, + ZLOG_RIPNG, + ZLOG_OSPF6, + ZLOG_MASC +} zlog_proto_t; + +struct zlog +{ + const char *ident; + zlog_proto_t protocol; + int flags; + FILE *fp; + char *filename; + int syslog; + int stat; + int connected; + int maskpri; /* as per syslog setlogmask */ + int priority; /* as per syslog priority */ + int facility; /* as per syslog facility */ + int record_priority; +}; + +/* Message structure. */ +struct message +{ + int key; + char *str; +}; + +/* Default logging strucutre. */ +extern struct zlog *zlog_default; + +/* Open zlog function */ +struct zlog *openzlog (const char *, int, zlog_proto_t, int, int); + +/* Close zlog function. */ +void closezlog (struct zlog *zl); + +/* GCC have printf type attribute check. */ +#ifdef __GNUC__ +#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#else +#define PRINTF_ATTRIBUTE(a,b) +#endif /* __GNUC__ */ + +/* Generic function for zlog. */ +void zlog (struct zlog *zl, int priority, const char *format, ...) PRINTF_ATTRIBUTE(3, 4); + +/* Handy zlog functions. */ +void zlog_err (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +void zlog_warn (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +void zlog_info (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +void zlog_notice (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +void zlog_debug (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); + +/* For bgpd's peer oriented log. */ +void plog_err (struct zlog *, const char *format, ...); +void plog_warn (struct zlog *, const char *format, ...); +void plog_info (struct zlog *, const char *format, ...); +void plog_notice (struct zlog *, const char *format, ...); +void plog_debug (struct zlog *, const char *format, ...); + +/* Set zlog flags. */ +void zlog_set_flag (struct zlog *zl, int flags); +void zlog_reset_flag (struct zlog *zl, int flags); + +/* Set zlog filename. */ +int zlog_set_file (struct zlog *zl, int flags, char *filename); +int zlog_reset_file (struct zlog *zl); + +/* Rotate log. */ +int zlog_rotate (); + +/* For hackey massage lookup and check */ +#define LOOKUP(x, y) mes_lookup(x, x ## _max, y) + +char *lookup (struct message *, int); +char *mes_lookup (struct message *meslist, int max, int index); + +extern const char *zlog_priority[]; + +#endif /* _ZEBRA_LOG_H */ diff --git a/lib/md5-gnu.h b/lib/md5-gnu.h new file mode 100644 index 00000000..dacc1ae7 --- /dev/null +++ b/lib/md5-gnu.h @@ -0,0 +1,156 @@ +/* Declaration of functions and data types used for MD5 sum computing + library functions. + Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _MD5_H +#define _MD5_H 1 + +#include <stdio.h> + +#if defined HAVE_LIMITS_H || _LIBC +# include <limits.h> +#endif + +/* The following contortions are an attempt to use the C preprocessor + to determine an unsigned integral type that is 32 bits wide. An + alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but + doing that would require that the configure script compile and *run* + the resulting executable. Locally running cross-compiled executables + is usually not possible. */ + +#ifdef _LIBC +# include <sys/types.h> +typedef u_int32_t md5_uint32; +#else +# if defined __STDC__ && __STDC__ +# define UINT_MAX_32_BITS 4294967295U +# else +# define UINT_MAX_32_BITS 0xFFFFFFFF +# endif + +/* If UINT_MAX isn't defined, assume it's a 32-bit type. + This should be valid for all systems GNU cares about because + that doesn't include 16-bit systems, and only modern systems + (that certainly have <limits.h>) have 64+-bit integral types. */ + +# ifndef UINT_MAX +# define UINT_MAX UINT_MAX_32_BITS +# endif + +# if UINT_MAX == UINT_MAX_32_BITS + typedef unsigned int md5_uint32; +# else +# if USHRT_MAX == UINT_MAX_32_BITS + typedef unsigned short md5_uint32; +# else +# if ULONG_MAX == UINT_MAX_32_BITS + typedef unsigned long md5_uint32; +# else + /* The following line is intended to evoke an error. + Using #error is not portable enough. */ + "Cannot determine unsigned 32-bit data type." +# endif +# endif +# endif +#endif + +#undef __P +#if defined (__STDC__) && __STDC__ +# define __P(x) x +#else +# define __P(x) () +#endif + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx +{ + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + + md5_uint32 total[2]; + md5_uint32 buflen; + char buffer[128]; +}; + +/* + * The following three functions are build up the low level used in + * the functions `md5_stream' and `md5_buffer'. + */ + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +extern void __md5_init_ctx __P ((struct md5_ctx *ctx)); +extern void md5_init_ctx __P ((struct md5_ctx *ctx)); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void __md5_process_block __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); +extern void md5_process_block __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void __md5_process_bytes __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); +extern void md5_process_bytes __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); + +/* Process the remaining bytes in the buffer and put result from CTX + in first 16 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *__md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf)); +extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf)); + + +/* Put result from CTX in first 16 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *__md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf)); +extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf)); + + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +extern int __md5_stream __P ((FILE *stream, void *resblock)); + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *__md5_buffer __P ((const char *buffer, size_t len, + void *resblock)); +extern void *md5_buffer __P ((const char *buffer, size_t len, + void *resblock)); + +#endif /* md5.h */ diff --git a/lib/md5.c b/lib/md5.c new file mode 100644 index 00000000..2068c46d --- /dev/null +++ b/lib/md5.c @@ -0,0 +1,447 @@ +/* md5.c - Functions to compute MD5 message digest of files or memory blocks + according to the definition of MD5 in RFC 1321 from April 1992. + Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> + +#if STDC_HEADERS || defined _LIBC +# include <stdlib.h> +# include <string.h> +#else +# ifndef HAVE_MEMCPY +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#include "md5-gnu.h" + +#ifdef _LIBC +# include <endian.h> +# if __BYTE_ORDER == __BIG_ENDIAN +# define WORDS_BIGENDIAN 1 +# endif +/* We need to keep the namespace clean so define the MD5 function + protected using leading __ and use weak aliases. */ +# define md5_init_ctx __md5_init_ctx +# define md5_process_block __md5_process_block +# define md5_process_bytes __md5_process_bytes +# define md5_finish_ctx __md5_finish_ctx +# define md5_read_ctx __md5_read_ctx +# define md5_stream __md5_stream +# define md5_buffer __md5_buffer +#endif + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +# define SWAP(n) (n) +#endif + + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void +md5_init_ctx (ctx) + struct md5_ctx *ctx; +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_read_ctx (ctx, resbuf) + const struct md5_ctx *ctx; + void *resbuf; +{ + ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A); + ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B); + ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C); + ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_finish_ctx (ctx, resbuf) + struct md5_ctx *ctx; + void *resbuf; +{ + /* Take yet unprocessed bytes into account. */ + md5_uint32 bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + + /* Process last bytes. */ + md5_process_block (ctx->buffer, bytes + pad + 8, ctx); + + return md5_read_ctx (ctx, resbuf); +} + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +md5_stream (stream, resblock) + FILE *stream; + void *resblock; +{ + /* Important: BLOCKSIZE must be a multiple of 64. */ +#define BLOCKSIZE 4096 + struct md5_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + do + { + n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + } + while (sum < BLOCKSIZE && n != 0); + if (n == 0 && ferror (stream)) + return 1; + + /* If end of file is reached, end the loop. */ + if (n == 0) + break; + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + md5_process_block (buffer, BLOCKSIZE, &ctx); + } + + /* Add the last bytes if necessary. */ + if (sum > 0) + md5_process_bytes (buffer, sum, &ctx); + + /* Construct result in desired memory. */ + md5_finish_ctx (&ctx, resblock); + return 0; +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +md5_buffer (buffer, len, resblock) + const char *buffer; + size_t len; + void *resblock; +{ + struct md5_ctx ctx; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return md5_finish_ctx (&ctx, resblock); +} + + +void +md5_process_bytes (buffer, len, ctx) + const void *buffer; + size_t len; + struct md5_ctx *ctx; +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (left_over + add > 64) + { + md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx); + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + (left_over + add) & 63); + ctx->buflen = (left_over + add) & 63; + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len > 64) + { + md5_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + memcpy (ctx->buffer, buffer, len); + ctx->buflen = len; + } +} + + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ + +void +md5_process_block (buffer, len, ctx) + const void *buffer; + size_t len; + struct md5_ctx *ctx; +{ + md5_uint32 correct_words[16]; + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof (md5_uint32); + const md5_uint32 *endp = words + nwords; + md5_uint32 A = ctx->A; + md5_uint32 B = ctx->B; + md5_uint32 C = ctx->C; + md5_uint32 D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + md5_uint32 *cwp = correct_words; + md5_uint32 A_save = A; + md5_uint32 B_save = B; + md5_uint32 C_save = C; + md5_uint32 D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} + + +#ifdef _LIBC +/* Define weak aliases. */ +# undef md5_init_ctx +weak_alias (__md5_init_ctx, md5_init_ctx) +# undef md5_process_block +weak_alias (__md5_process_block, md5_process_block) +# undef md5_process_bytes +weak_alias (__md5_process_bytes, md5_process_bytes) +# undef md5_finish_ctx +weak_alias (__md5_finish_ctx, md5_finish_ctx) +# undef md5_read_ctx +weak_alias (__md5_read_ctx, md5_read_ctx) +# undef md5_stream +weak_alias (__md5_stream, md5_stream) +# undef md5_buffer +weak_alias (__md5_buffer, md5_buffer) +#endif diff --git a/lib/memory.c b/lib/memory.c new file mode 100644 index 00000000..bf142dcf --- /dev/null +++ b/lib/memory.c @@ -0,0 +1,493 @@ +/* + * Memory management routine + * Copyright (C) 1998 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "log.h" +#include "memory.h" + +void alloc_inc (int); +void alloc_dec (int); + +struct message mstr [] = +{ + { MTYPE_THREAD, "thread" }, + { MTYPE_THREAD_MASTER, "thread_master" }, + { MTYPE_VECTOR, "vector" }, + { MTYPE_VECTOR_INDEX, "vector_index" }, + { MTYPE_IF, "interface" }, + { 0, NULL }, +}; + +/* Fatal memory allocation error occured. */ +static void +zerror (const char *fname, int type, size_t size) +{ + fprintf (stderr, "%s : can't allocate memory for `%s' size %d\n", + fname, lookup (mstr, type), (int) size); + exit (1); +} + +/* Memory allocation. */ +void * +zmalloc (int type, size_t size) +{ + void *memory; + + memory = malloc (size); + + if (memory == NULL) + zerror ("malloc", type, size); + + alloc_inc (type); + + return memory; +} + +/* Memory allocation with num * size with cleared. */ +void * +zcalloc (int type, size_t size) +{ + void *memory; + + memory = calloc (1, size); + + if (memory == NULL) + zerror ("calloc", type, size); + + alloc_inc (type); + + return memory; +} + +/* Memory reallocation. */ +void * +zrealloc (int type, void *ptr, size_t size) +{ + void *memory; + + memory = realloc (ptr, size); + if (memory == NULL) + zerror ("realloc", type, size); + return memory; +} + +/* Memory free. */ +void +zfree (int type, void *ptr) +{ + alloc_dec (type); + free (ptr); +} + +/* String duplication. */ +char * +zstrdup (int type, char *str) +{ + void *dup; + + dup = strdup (str); + if (dup == NULL) + zerror ("strdup", type, strlen (str)); + alloc_inc (type); + return dup; +} + +#ifdef MEMORY_LOG +struct +{ + char *name; + unsigned long alloc; + unsigned long t_malloc; + unsigned long c_malloc; + unsigned long t_calloc; + unsigned long c_calloc; + unsigned long t_realloc; + unsigned long t_free; + unsigned long c_strdup; +} mstat [MTYPE_MAX]; + +void +mtype_log (char *func, void *memory, const char *file, int line, int type) +{ + zlog_info ("%s: %s %p %s %d", func, lookup (mstr, type), memory, file, line); +} + +void * +mtype_zmalloc (const char *file, int line, int type, size_t size) +{ + void *memory; + + mstat[type].c_malloc++; + mstat[type].t_malloc++; + + memory = zmalloc (type, size); + mtype_log ("zmalloc", memory, file, line, type); + + return memory; +} + +void * +mtype_zcalloc (const char *file, int line, int type, size_t size) +{ + void *memory; + + mstat[type].c_calloc++; + mstat[type].t_calloc++; + + memory = zcalloc (type, size); + mtype_log ("xcalloc", memory, file, line, type); + + return memory; +} + +void * +mtype_zrealloc (const char *file, int line, int type, void *ptr, size_t size) +{ + void *memory; + + /* Realloc need before allocated pointer. */ + mstat[type].t_realloc++; + + memory = zrealloc (type, ptr, size); + + mtype_log ("xrealloc", memory, file, line, type); + + return memory; +} + +/* Important function. */ +void +mtype_zfree (const char *file, int line, int type, void *ptr) +{ + mstat[type].t_free++; + + mtype_log ("xfree", ptr, file, line, type); + + zfree (type, ptr); +} + +char * +mtype_zstrdup (const char *file, int line, int type, char *str) +{ + char *memory; + + mstat[type].c_strdup++; + + memory = zstrdup (type, str); + + mtype_log ("xstrdup", memory, file, line, type); + + return memory; +} +#else +struct +{ + char *name; + unsigned long alloc; +} mstat [MTYPE_MAX]; +#endif /* MTPYE_LOG */ + +/* Increment allocation counter. */ +void +alloc_inc (int type) +{ + mstat[type].alloc++; +} + +/* Decrement allocation counter. */ +void +alloc_dec (int type) +{ + mstat[type].alloc--; +} + +/* Looking up memory status from vty interface. */ +#include "vector.h" +#include "vty.h" +#include "command.h" + +/* For pretty printng of memory allocate information. */ +struct memory_list +{ + int index; + char *format; +}; + +struct memory_list memory_list_lib[] = +{ + { MTYPE_TMP, "Temporary memory" }, + { MTYPE_ROUTE_TABLE, "Route table " }, + { MTYPE_ROUTE_NODE, "Route node " }, + { MTYPE_RIB, "RIB " }, + { MTYPE_NEXTHOP, "Nexthop " }, + { MTYPE_LINK_LIST, "Link List " }, + { MTYPE_LINK_NODE, "Link Node " }, + { MTYPE_HASH, "Hash " }, + { MTYPE_HASH_BACKET, "Hash Bucket " }, + { MTYPE_ACCESS_LIST, "Access List " }, + { MTYPE_ACCESS_LIST_STR, "Access List Str " }, + { MTYPE_ACCESS_FILTER, "Access Filter " }, + { MTYPE_PREFIX_LIST, "Prefix List " }, + { MTYPE_PREFIX_LIST_STR, "Prefix List Str " }, + { MTYPE_PREFIX_LIST_ENTRY, "Prefix List Entry "}, + { MTYPE_ROUTE_MAP, "Route map " }, + { MTYPE_ROUTE_MAP_NAME, "Route map name " }, + { MTYPE_ROUTE_MAP_INDEX, "Route map index " }, + { MTYPE_ROUTE_MAP_RULE, "Route map rule " }, + { MTYPE_ROUTE_MAP_RULE_STR, "Route map rule str" }, + { MTYPE_DESC, "Command desc " }, + { MTYPE_BUFFER, "Buffer " }, + { MTYPE_BUFFER_DATA, "Buffer data " }, + { MTYPE_STREAM, "Stream " }, + { MTYPE_KEYCHAIN, "Key chain " }, + { MTYPE_KEY, "Key " }, + { MTYPE_VTY, "VTY " }, + { -1, NULL } +}; + +struct memory_list memory_list_bgp[] = +{ + { MTYPE_BGP_PEER, "BGP peer" }, + { MTYPE_ATTR, "BGP attribute" }, + { MTYPE_AS_PATH, "BGP aspath" }, + { MTYPE_AS_SEG, "BGP aspath seg" }, + { MTYPE_AS_STR, "BGP aspath str" }, + { 0, NULL }, + { MTYPE_BGP_TABLE, "BGP table" }, + { MTYPE_BGP_NODE, "BGP node" }, + { MTYPE_BGP_ADVERTISE_ATTR, "BGP adv attr" }, + { MTYPE_BGP_ADVERTISE, "BGP adv" }, + { MTYPE_BGP_ADJ_IN, "BGP adj in" }, + { MTYPE_BGP_ADJ_OUT, "BGP adj out" }, + { 0, NULL }, + { MTYPE_AS_LIST, "BGP AS list" }, + { MTYPE_AS_FILTER, "BGP AS filter" }, + { MTYPE_AS_FILTER_STR, "BGP AS filter str" }, + { 0, NULL }, + { MTYPE_COMMUNITY, "community" }, + { MTYPE_COMMUNITY_VAL, "community val" }, + { MTYPE_COMMUNITY_STR, "community str" }, + { 0, NULL }, + { MTYPE_ECOMMUNITY, "extcommunity" }, + { MTYPE_ECOMMUNITY_VAL, "extcommunity val" }, + { MTYPE_ECOMMUNITY_STR, "extcommunity str" }, + { 0, NULL }, + { MTYPE_COMMUNITY_LIST, "community-list" }, + { MTYPE_COMMUNITY_LIST_NAME, "community-list name" }, + { MTYPE_COMMUNITY_LIST_ENTRY, "community-list entry" }, + { MTYPE_COMMUNITY_LIST_CONFIG, "community-list config" }, + { 0, NULL }, + { MTYPE_CLUSTER, "Cluster list" }, + { MTYPE_CLUSTER_VAL, "Cluster list val" }, + { 0, NULL }, + { MTYPE_TRANSIT, "BGP transit attr" }, + { MTYPE_TRANSIT_VAL, "BGP transit val" }, + { 0, NULL }, + { MTYPE_BGP_DISTANCE, "BGP distance" }, + { MTYPE_BGP_NEXTHOP_CACHE, "BGP nexthop" }, + { MTYPE_BGP_CONFED_LIST, "BGP confed list" }, + { MTYPE_PEER_UPDATE_SOURCE, "peer update if" }, + { MTYPE_BGP_DAMP_INFO, "Dampening info" }, + { MTYPE_BGP_REGEXP, "BGP regexp" }, + { -1, NULL } +}; + +struct memory_list memory_list_rip[] = +{ + { MTYPE_RIP, "RIP structure " }, + { MTYPE_RIP_INFO, "RIP route info " }, + { MTYPE_RIP_INTERFACE, "RIP interface " }, + { MTYPE_RIP_PEER, "RIP peer " }, + { MTYPE_RIP_OFFSET_LIST, "RIP offset list " }, + { MTYPE_RIP_DISTANCE, "RIP distance " }, + { -1, NULL } +}; + +struct memory_list memory_list_ospf[] = +{ + { MTYPE_OSPF_TOP, "OSPF top " }, + { MTYPE_OSPF_AREA, "OSPF area " }, + { MTYPE_OSPF_AREA_RANGE, "OSPF area range " }, + { MTYPE_OSPF_NETWORK, "OSPF network " }, +#ifdef NBMA_ENABLE + { MTYPE_OSPF_NEIGHBOR_STATIC,"OSPF static nbr " }, +#endif /* NBMA_ENABLE */ + { MTYPE_OSPF_IF, "OSPF interface " }, + { MTYPE_OSPF_NEIGHBOR, "OSPF neighbor " }, + { MTYPE_OSPF_ROUTE, "OSPF route " }, + { MTYPE_OSPF_TMP, "OSPF tmp mem " }, + { MTYPE_OSPF_LSA, "OSPF LSA " }, + { MTYPE_OSPF_LSA_DATA, "OSPF LSA data " }, + { MTYPE_OSPF_LSDB, "OSPF LSDB " }, + { MTYPE_OSPF_PACKET, "OSPF packet " }, + { MTYPE_OSPF_FIFO, "OSPF FIFO queue " }, + { MTYPE_OSPF_VERTEX, "OSPF vertex " }, + { MTYPE_OSPF_NEXTHOP, "OSPF nexthop " }, + { MTYPE_OSPF_PATH, "OSPF path " }, + { MTYPE_OSPF_VL_DATA, "OSPF VL data " }, + { MTYPE_OSPF_CRYPT_KEY, "OSPF crypt key " }, + { MTYPE_OSPF_EXTERNAL_INFO, "OSPF ext. info " }, + { MTYPE_OSPF_DISTANCE, "OSPF distance " }, + { MTYPE_OSPF_IF_INFO, "OSPF if info " }, + { MTYPE_OSPF_IF_PARAMS, "OSPF if params " }, + { -1, NULL }, +}; + +struct memory_list memory_list_ospf6[] = +{ + { MTYPE_OSPF6_TOP, "OSPF6 top " }, + { MTYPE_OSPF6_AREA, "OSPF6 area " }, + { MTYPE_OSPF6_IF, "OSPF6 interface " }, + { MTYPE_OSPF6_NEIGHBOR, "OSPF6 neighbor " }, + { MTYPE_OSPF6_ROUTE, "OSPF6 route " }, + { MTYPE_OSPF6_PREFIX, "OSPF6 prefix " }, + { MTYPE_OSPF6_MESSAGE, "OSPF6 message " }, + { MTYPE_OSPF6_LSA, "OSPF6 LSA " }, + { MTYPE_OSPF6_LSA_SUMMARY, "OSPF6 LSA summary " }, + { MTYPE_OSPF6_LSDB, "OSPF6 LSA database" }, + { MTYPE_OSPF6_VERTEX, "OSPF6 vertex " }, + { MTYPE_OSPF6_SPFTREE, "OSPF6 SPF tree " }, + { MTYPE_OSPF6_NEXTHOP, "OSPF6 nexthop " }, + { MTYPE_OSPF6_EXTERNAL_INFO,"OSPF6 ext. info " }, + { MTYPE_OSPF6_OTHER, "OSPF6 other " }, + { -1, NULL }, +}; + +struct memory_list memory_list_separator[] = +{ + { 0, NULL}, + {-1, NULL} +}; + +void +show_memory_vty (struct vty *vty, struct memory_list *list) +{ + struct memory_list *m; + + for (m = list; m->index >= 0; m++) + if (m->index == 0) + vty_out (vty, "-----------------------------\r\n"); + else + vty_out (vty, "%-22s: %5ld\r\n", m->format, mstat[m->index].alloc); +} + +DEFUN (show_memory_all, + show_memory_all_cmd, + "show memory all", + "Show running system information\n" + "Memory statistics\n" + "All memory statistics\n") +{ + show_memory_vty (vty, memory_list_lib); + show_memory_vty (vty, memory_list_separator); + show_memory_vty (vty, memory_list_rip); + show_memory_vty (vty, memory_list_separator); + show_memory_vty (vty, memory_list_ospf); + show_memory_vty (vty, memory_list_separator); + show_memory_vty (vty, memory_list_ospf6); + show_memory_vty (vty, memory_list_separator); + show_memory_vty (vty, memory_list_bgp); + + return CMD_SUCCESS; +} + +ALIAS (show_memory_all, + show_memory_cmd, + "show memory", + "Show running system information\n" + "Memory statistics\n") + +DEFUN (show_memory_lib, + show_memory_lib_cmd, + "show memory lib", + SHOW_STR + "Memory statistics\n" + "Library memory\n") +{ + show_memory_vty (vty, memory_list_lib); + return CMD_SUCCESS; +} + +DEFUN (show_memory_rip, + show_memory_rip_cmd, + "show memory rip", + SHOW_STR + "Memory statistics\n" + "RIP memory\n") +{ + show_memory_vty (vty, memory_list_rip); + return CMD_SUCCESS; +} + +DEFUN (show_memory_bgp, + show_memory_bgp_cmd, + "show memory bgp", + SHOW_STR + "Memory statistics\n" + "BGP memory\n") +{ + show_memory_vty (vty, memory_list_bgp); + return CMD_SUCCESS; +} + +DEFUN (show_memory_ospf, + show_memory_ospf_cmd, + "show memory ospf", + SHOW_STR + "Memory statistics\n" + "OSPF memory\n") +{ + show_memory_vty (vty, memory_list_ospf); + return CMD_SUCCESS; +} + +DEFUN (show_memory_ospf6, + show_memory_ospf6_cmd, + "show memory ospf6", + SHOW_STR + "Memory statistics\n" + "OSPF6 memory\n") +{ + show_memory_vty (vty, memory_list_ospf6); + return CMD_SUCCESS; +} + +void +memory_init () +{ + install_element (VIEW_NODE, &show_memory_cmd); + install_element (VIEW_NODE, &show_memory_all_cmd); + install_element (VIEW_NODE, &show_memory_lib_cmd); + install_element (VIEW_NODE, &show_memory_rip_cmd); + install_element (VIEW_NODE, &show_memory_bgp_cmd); + install_element (VIEW_NODE, &show_memory_ospf_cmd); + install_element (VIEW_NODE, &show_memory_ospf6_cmd); + + install_element (ENABLE_NODE, &show_memory_cmd); + install_element (ENABLE_NODE, &show_memory_all_cmd); + install_element (ENABLE_NODE, &show_memory_lib_cmd); + install_element (ENABLE_NODE, &show_memory_rip_cmd); + install_element (ENABLE_NODE, &show_memory_bgp_cmd); + install_element (ENABLE_NODE, &show_memory_ospf_cmd); + install_element (ENABLE_NODE, &show_memory_ospf6_cmd); +} diff --git a/lib/memory.h b/lib/memory.h new file mode 100644 index 00000000..52e3bc11 --- /dev/null +++ b/lib/memory.h @@ -0,0 +1,245 @@ +/* Memory management routine + Copyright (C) 1998 Kunihiro Ishiguro + +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. */ + +#ifndef _ZEBRA_MEMORY_H +#define _ZEBRA_MEMORY_H + +/* #define MEMORY_LOG */ + +/* For tagging memory, below is the type of the memory. */ +enum +{ + MTYPE_TMP = 1, + MTYPE_STRVEC, + MTYPE_VECTOR, + MTYPE_VECTOR_INDEX, + MTYPE_LINK_LIST, + MTYPE_LINK_NODE, + MTYPE_THREAD, + MTYPE_THREAD_MASTER, + MTYPE_VTY, + MTYPE_VTY_HIST, + MTYPE_VTY_OUT_BUF, + MTYPE_IF, + MTYPE_CONNECTED, + MTYPE_AS_SEG, + MTYPE_AS_STR, + MTYPE_AS_PATH, + MTYPE_CLUSTER, + MTYPE_CLUSTER_VAL, + MTYPE_ATTR, + MTYPE_TRANSIT, + MTYPE_TRANSIT_VAL, + MTYPE_BUFFER, + MTYPE_BUFFER_DATA, + MTYPE_STREAM, + MTYPE_STREAM_DATA, + MTYPE_STREAM_FIFO, + MTYPE_PREFIX, + MTYPE_PREFIX_IPV4, + MTYPE_PREFIX_IPV6, + MTYPE_HASH, + MTYPE_HASH_INDEX, + MTYPE_HASH_BACKET, + MTYPE_RIPNG_ROUTE, + MTYPE_RIPNG_AGGREGATE, + MTYPE_ROUTE_TABLE, + MTYPE_ROUTE_NODE, + MTYPE_ACCESS_LIST, + MTYPE_ACCESS_LIST_STR, + MTYPE_ACCESS_FILTER, + MTYPE_PREFIX_LIST, + MTYPE_PREFIX_LIST_STR, + MTYPE_PREFIX_LIST_ENTRY, + MTYPE_ROUTE_MAP, + MTYPE_ROUTE_MAP_NAME, + MTYPE_ROUTE_MAP_INDEX, + MTYPE_ROUTE_MAP_RULE, + MTYPE_ROUTE_MAP_RULE_STR, + MTYPE_ROUTE_MAP_COMPILED, + + MTYPE_RIB, + MTYPE_DISTRIBUTE, + MTYPE_ZLOG, + MTYPE_ZCLIENT, + MTYPE_NEXTHOP, + MTYPE_RTADV_PREFIX, + MTYPE_IF_RMAP, + MTYPE_SOCKUNION, + MTYPE_STATIC_IPV4, + MTYPE_STATIC_IPV6, + + MTYPE_DESC, + MTYPE_OSPF_TOP, + MTYPE_OSPF_AREA, + MTYPE_OSPF_AREA_RANGE, + MTYPE_OSPF_NETWORK, + MTYPE_OSPF_NEIGHBOR_STATIC, + MTYPE_OSPF_IF, + MTYPE_OSPF_NEIGHBOR, + MTYPE_OSPF_ROUTE, + MTYPE_OSPF_TMP, + MTYPE_OSPF_LSA, + MTYPE_OSPF_LSA_DATA, + MTYPE_OSPF_LSDB, + MTYPE_OSPF_PACKET, + MTYPE_OSPF_FIFO, + MTYPE_OSPF_VERTEX, + MTYPE_OSPF_NEXTHOP, + MTYPE_OSPF_PATH, + MTYPE_OSPF_VL_DATA, + MTYPE_OSPF_CRYPT_KEY, + MTYPE_OSPF_EXTERNAL_INFO, + MTYPE_OSPF_MESSAGE, + MTYPE_OSPF_DISTANCE, + MTYPE_OSPF_IF_INFO, + MTYPE_OSPF_IF_PARAMS, + + MTYPE_OSPF6_TOP, + MTYPE_OSPF6_AREA, + MTYPE_OSPF6_IF, + MTYPE_OSPF6_NEIGHBOR, + MTYPE_OSPF6_ROUTE, + MTYPE_OSPF6_PREFIX, + MTYPE_OSPF6_MESSAGE, + MTYPE_OSPF6_LSA, + MTYPE_OSPF6_LSA_SUMMARY, + MTYPE_OSPF6_LSDB, + MTYPE_OSPF6_VERTEX, + MTYPE_OSPF6_SPFTREE, + MTYPE_OSPF6_NEXTHOP, + MTYPE_OSPF6_EXTERNAL_INFO, + MTYPE_OSPF6_OTHER, + + MTYPE_BGP, + MTYPE_BGP_PEER, + MTYPE_PEER_GROUP, + MTYPE_PEER_DESC, + MTYPE_PEER_UPDATE_SOURCE, + MTYPE_BGP_STATIC, + MTYPE_BGP_AGGREGATE, + MTYPE_BGP_CONFED_LIST, + MTYPE_BGP_NEXTHOP_CACHE, + MTYPE_BGP_DAMP_INFO, + MTYPE_BGP_DAMP_ARRAY, + MTYPE_BGP_ANNOUNCE, + MTYPE_BGP_ATTR_QUEUE, + MTYPE_BGP_ROUTE_QUEUE, + MTYPE_BGP_DISTANCE, + MTYPE_BGP_ROUTE, + MTYPE_BGP_TABLE, + MTYPE_BGP_NODE, + MTYPE_BGP_ADVERTISE_ATTR, + MTYPE_BGP_ADVERTISE, + MTYPE_BGP_ADJ_IN, + MTYPE_BGP_ADJ_OUT, + MTYPE_BGP_REGEXP, + MTYPE_AS_FILTER, + MTYPE_AS_FILTER_STR, + MTYPE_AS_LIST, + + MTYPE_COMMUNITY, + MTYPE_COMMUNITY_VAL, + MTYPE_COMMUNITY_STR, + + MTYPE_ECOMMUNITY, + MTYPE_ECOMMUNITY_VAL, + MTYPE_ECOMMUNITY_STR, + + /* community-list and extcommunity-list. */ + MTYPE_COMMUNITY_LIST_HANDLER, + MTYPE_COMMUNITY_LIST, + MTYPE_COMMUNITY_LIST_NAME, + MTYPE_COMMUNITY_LIST_ENTRY, + MTYPE_COMMUNITY_LIST_CONFIG, + + MTYPE_RIP, + MTYPE_RIP_INTERFACE, + MTYPE_RIP_DISTANCE, + MTYPE_RIP_OFFSET_LIST, + MTYPE_RIP_INFO, + MTYPE_RIP_PEER, + MTYPE_KEYCHAIN, + MTYPE_KEY, + + MTYPE_VTYSH_CONFIG, + MTYPE_VTYSH_CONFIG_LINE, + + MTYPE_VRF, + MTYPE_VRF_NAME, + + MTYPE_MAX +}; + +#ifdef MEMORY_LOG +#define XMALLOC(mtype, size) \ + mtype_zmalloc (__FILE__, __LINE__, (mtype), (size)) +#define XCALLOC(mtype, size) \ + mtype_zcalloc (__FILE__, __LINE__, (mtype), (size)) +#define XREALLOC(mtype, ptr, size) \ + mtype_zrealloc (__FILE__, __LINE__, (mtype), (ptr), (size)) +#define XFREE(mtype, ptr) \ + mtype_zfree (__FILE__, __LINE__, (mtype), (ptr)) +#define XSTRDUP(mtype, str) \ + mtype_zstrdup (__FILE__, __LINE__, (mtype), (str)) +#else +#define XMALLOC(mtype, size) zmalloc ((mtype), (size)) +#define XCALLOC(mtype, size) zcalloc ((mtype), (size)) +#define XREALLOC(mtype, ptr, size) zrealloc ((mtype), (ptr), (size)) +#define XFREE(mtype, ptr) zfree ((mtype), (ptr)) +#define XSTRDUP(mtype, str) zstrdup ((mtype), (str)) +#endif /* MEMORY_LOG */ + +/* Prototypes of memory function. */ +void *zmalloc (int type, size_t size); +void *zcalloc (int type, size_t size); +void *zrealloc (int type, void *ptr, size_t size); +void zfree (int type, void *ptr); +char *zstrdup (int type, char *str); + +void *mtype_zmalloc (const char *file, + int line, + int type, + size_t size); + +void *mtype_zcalloc (const char *file, + int line, + int type, + size_t num, + size_t size); + +void *mtype_zrealloc (const char *file, + int line, + int type, + void *ptr, + size_t size); + +void mtype_zfree (const char *file, + int line, + int type, + void *ptr); + +char *mtype_zstrdup (const char *file, + int line, + int type, + char *str); +void memory_init (); + +#endif /* _ZEBRA_MEMORY_H */ diff --git a/lib/network.c b/lib/network.c new file mode 100644 index 00000000..b68761bc --- /dev/null +++ b/lib/network.c @@ -0,0 +1,71 @@ +/* + * Network library. + * Copyright (C) 1997 Kunihiro Ishiguro + * + * 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 <zebra.h> + +/* Read nbytes from fd and store into ptr. */ +int +readn (int fd, char *ptr, int nbytes) +{ + int nleft; + int nread; + + nleft = nbytes; + + while (nleft > 0) + { + nread = read (fd, ptr, nleft); + + if (nread < 0) + return (nread); + else + if (nread == 0) + break; + + nleft -= nread; + ptr += nread; + } + + return nbytes - nleft; +} + +/* Write nbytes from ptr to fd. */ +int +writen(int fd, char *ptr, int nbytes) +{ + int nleft; + int nwritten; + + nleft = nbytes; + + while (nleft > 0) + { + nwritten = write(fd, ptr, nleft); + + if (nwritten <= 0) + return (nwritten); + + nleft -= nwritten; + ptr += nwritten; + } + return nbytes - nleft; +} diff --git a/lib/network.h b/lib/network.h new file mode 100644 index 00000000..a0212950 --- /dev/null +++ b/lib/network.h @@ -0,0 +1,29 @@ +/* + * Network library header. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_NETWORK_H +#define _ZEBRA_NETWORK_H + +int readn (int, char *, int); +int writen (int, char *, int); + +#endif /* _ZEBRA_NETWORK_H */ diff --git a/lib/pid_output.c b/lib/pid_output.c new file mode 100644 index 00000000..4146244a --- /dev/null +++ b/lib/pid_output.c @@ -0,0 +1,77 @@ +/* + * Process id output. + * Copyright (C) 1998, 1999 Kunihiro Ishiguro + * + * 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 <zebra.h> + +pid_t +pid_output (char *path) +{ + FILE *fp; + pid_t pid; + + pid = getpid(); + + fp = fopen (path, "w"); + if (fp != NULL) + { + fprintf (fp, "%d\n", (int) pid); + fclose (fp); + return -1; + } + return pid; +} + +pid_t +pid_output_lock (char *path) +{ + int tmp; + int fd; + pid_t pid; + char buf[16], *p; + + pid = getpid (); + + fd = open (path, O_RDWR | O_CREAT | O_EXCL, 0644); + if (fd < 0) + { + fd = open (path, O_RDONLY); + if (fd < 0) + fprintf (stderr, "Can't creat pid lock file, exit\n"); + else + { + read (fd, buf, sizeof (buf)); + if ((p = index (buf, '\n')) != NULL) + *p = 0; + fprintf (stderr, "Another process(%s) running, exit\n", buf); + } + exit (-1); + } + else + { + sprintf (buf, "%d\n", (int) pid); + tmp = write (fd, buf, strlen (buf)); + close (fd); + } + + return pid; +} + diff --git a/lib/plist.c b/lib/plist.c new file mode 100644 index 00000000..c2aeea5b --- /dev/null +++ b/lib/plist.c @@ -0,0 +1,2881 @@ +/* Prefix list functions. + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "prefix.h" +#include "command.h" +#include "memory.h" +#include "plist.h" +#include "sockunion.h" +#include "buffer.h" + +/* Each prefix-list's entry. */ +struct prefix_list_entry +{ + int seq; + + int le; + int ge; + + enum prefix_list_type type; + + int any; + struct prefix prefix; + + unsigned long refcnt; + unsigned long hitcnt; + + struct prefix_list_entry *next; + struct prefix_list_entry *prev; +}; + +/* List of struct prefix_list. */ +struct prefix_list_list +{ + struct prefix_list *head; + struct prefix_list *tail; +}; + +/* Master structure of prefix_list. */ +struct prefix_master +{ + /* List of prefix_list which name is number. */ + struct prefix_list_list num; + + /* List of prefix_list which name is string. */ + struct prefix_list_list str; + + /* Whether sequential number is used. */ + int seqnum; + + /* The latest update. */ + struct prefix_list *recent; + + /* Hook function which is executed when new prefix_list is added. */ + void (*add_hook) (); + + /* Hook function which is executed when prefix_list is deleted. */ + void (*delete_hook) (); +}; + +/* Static structure of IPv4 prefix_list's master. */ +static struct prefix_master prefix_master_ipv4 = +{ + {NULL, NULL}, + {NULL, NULL}, + 1, + NULL, + NULL, +}; + +#ifdef HAVE_IPV6 +/* Static structure of IPv6 prefix-list's master. */ +static struct prefix_master prefix_master_ipv6 = +{ + {NULL, NULL}, + {NULL, NULL}, + 1, + NULL, + NULL, +}; +#endif /* HAVE_IPV6*/ + +/* Static structure of BGP ORF prefix_list's master. */ +static struct prefix_master prefix_master_orf = +{ + {NULL, NULL}, + {NULL, NULL}, + 1, + NULL, + NULL, +}; + +struct prefix_master * +prefix_master_get (afi_t afi) +{ + if (afi == AFI_IP) + return &prefix_master_ipv4; +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + return &prefix_master_ipv6; +#endif /* HAVE_IPV6 */ + else if (afi == AFI_ORF_PREFIX) + return &prefix_master_orf; + return NULL; +} + +/* Lookup prefix_list from list of prefix_list by name. */ +struct prefix_list * +prefix_list_lookup (afi_t afi, char *name) +{ + struct prefix_list *plist; + struct prefix_master *master; + + if (name == NULL) + return NULL; + + master = prefix_master_get (afi); + if (master == NULL) + return NULL; + + for (plist = master->num.head; plist; plist = plist->next) + if (strcmp (plist->name, name) == 0) + return plist; + + for (plist = master->str.head; plist; plist = plist->next) + if (strcmp (plist->name, name) == 0) + return plist; + + return NULL; +} + +struct prefix_list * +prefix_list_new () +{ + struct prefix_list *new; + + new = XCALLOC (MTYPE_PREFIX_LIST, sizeof (struct prefix_list)); + return new; +} + +void +prefix_list_free (struct prefix_list *plist) +{ + XFREE (MTYPE_PREFIX_LIST, plist); +} + +struct prefix_list_entry * +prefix_list_entry_new () +{ + struct prefix_list_entry *new; + + new = XCALLOC (MTYPE_PREFIX_LIST_ENTRY, sizeof (struct prefix_list_entry)); + return new; +} + +void +prefix_list_entry_free (struct prefix_list_entry *pentry) +{ + XFREE (MTYPE_PREFIX_LIST_ENTRY, pentry); +} + +/* Insert new prefix list to list of prefix_list. Each prefix_list + is sorted by the name. */ +struct prefix_list * +prefix_list_insert (afi_t afi, char *name) +{ + int i; + long number; + struct prefix_list *plist; + struct prefix_list *point; + struct prefix_list_list *list; + struct prefix_master *master; + + master = prefix_master_get (afi); + if (master == NULL) + return NULL; + + /* Allocate new prefix_list and copy given name. */ + plist = prefix_list_new (); + plist->name = XSTRDUP (MTYPE_PREFIX_LIST_STR, name); + plist->master = master; + + /* If name is made by all digit character. We treat it as + number. */ + for (number = 0, i = 0; i < strlen (name); i++) + { + if (isdigit ((int) name[i])) + number = (number * 10) + (name[i] - '0'); + else + break; + } + + /* In case of name is all digit character */ + if (i == strlen (name)) + { + plist->type = PREFIX_TYPE_NUMBER; + + /* Set prefix_list to number list. */ + list = &master->num; + + for (point = list->head; point; point = point->next) + if (atol (point->name) >= number) + break; + } + else + { + plist->type = PREFIX_TYPE_STRING; + + /* Set prefix_list to string list. */ + list = &master->str; + + /* Set point to insertion point. */ + for (point = list->head; point; point = point->next) + if (strcmp (point->name, name) >= 0) + break; + } + + /* In case of this is the first element of master. */ + if (list->head == NULL) + { + list->head = list->tail = plist; + return plist; + } + + /* In case of insertion is made at the tail of access_list. */ + if (point == NULL) + { + plist->prev = list->tail; + list->tail->next = plist; + list->tail = plist; + return plist; + } + + /* In case of insertion is made at the head of access_list. */ + if (point == list->head) + { + plist->next = list->head; + list->head->prev = plist; + list->head = plist; + return plist; + } + + /* Insertion is made at middle of the access_list. */ + plist->next = point; + plist->prev = point->prev; + + if (point->prev) + point->prev->next = plist; + point->prev = plist; + + return plist; +} + +struct prefix_list * +prefix_list_get (afi_t afi, char *name) +{ + struct prefix_list *plist; + + plist = prefix_list_lookup (afi, name); + + if (plist == NULL) + plist = prefix_list_insert (afi, name); + return plist; +} + +/* Delete prefix-list from prefix_list_master and free it. */ +void +prefix_list_delete (struct prefix_list *plist) +{ + struct prefix_list_list *list; + struct prefix_master *master; + struct prefix_list_entry *pentry; + struct prefix_list_entry *next; + + /* If prefix-list contain prefix_list_entry free all of it. */ + for (pentry = plist->head; pentry; pentry = next) + { + next = pentry->next; + prefix_list_entry_free (pentry); + plist->count--; + } + + master = plist->master; + + if (plist->type == PREFIX_TYPE_NUMBER) + list = &master->num; + else + list = &master->str; + + if (plist->next) + plist->next->prev = plist->prev; + else + list->tail = plist->prev; + + if (plist->prev) + plist->prev->next = plist->next; + else + list->head = plist->next; + + if (plist->desc) + XFREE (MTYPE_TMP, plist->desc); + + /* Make sure master's recent changed prefix-list information is + cleared. */ + master->recent = NULL; + + if (plist->name) + XFREE (MTYPE_PREFIX_LIST_STR, plist->name); + + prefix_list_free (plist); + + if (master->delete_hook) + (*master->delete_hook) (); +} + +struct prefix_list_entry * +prefix_list_entry_make (struct prefix *prefix, enum prefix_list_type type, + int seq, int le, int ge, int any) +{ + struct prefix_list_entry *pentry; + + pentry = prefix_list_entry_new (); + + if (any) + pentry->any = 1; + + prefix_copy (&pentry->prefix, prefix); + pentry->type = type; + pentry->seq = seq; + pentry->le = le; + pentry->ge = ge; + + return pentry; +} + +/* Add hook function. */ +void +prefix_list_add_hook (void (*func) (struct prefix_list *plist)) +{ + prefix_master_ipv4.add_hook = func; +#ifdef HAVE_IPV6 + prefix_master_ipv6.add_hook = func; +#endif /* HAVE_IPV6 */ +} + +/* Delete hook function. */ +void +prefix_list_delete_hook (void (*func) (struct prefix_list *plist)) +{ + prefix_master_ipv4.delete_hook = func; +#ifdef HAVE_IPV6 + prefix_master_ipv6.delete_hook = func; +#endif /* HAVE_IPVt6 */ +} + +/* Calculate new sequential number. */ +int +prefix_new_seq_get (struct prefix_list *plist) +{ + int maxseq; + int newseq; + struct prefix_list_entry *pentry; + + maxseq = newseq = 0; + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + if (maxseq < pentry->seq) + maxseq = pentry->seq; + } + + newseq = ((maxseq / 5) * 5) + 5; + + return newseq; +} + +/* Return prefix list entry which has same seq number. */ +struct prefix_list_entry * +prefix_seq_check (struct prefix_list *plist, int seq) +{ + struct prefix_list_entry *pentry; + + for (pentry = plist->head; pentry; pentry = pentry->next) + if (pentry->seq == seq) + return pentry; + return NULL; +} + +struct prefix_list_entry * +prefix_list_entry_lookup (struct prefix_list *plist, struct prefix *prefix, + enum prefix_list_type type, int seq, int le, int ge) +{ + struct prefix_list_entry *pentry; + + for (pentry = plist->head; pentry; pentry = pentry->next) + if (prefix_same (&pentry->prefix, prefix) && pentry->type == type) + { + if (seq >= 0 && pentry->seq != seq) + continue; + + if (pentry->le != le) + continue; + if (pentry->ge != ge) + continue; + + return pentry; + } + + return NULL; +} + +void +prefix_list_entry_delete (struct prefix_list *plist, + struct prefix_list_entry *pentry, + int update_list) +{ + if (plist == NULL || pentry == NULL) + return; + if (pentry->prev) + pentry->prev->next = pentry->next; + else + plist->head = pentry->next; + if (pentry->next) + pentry->next->prev = pentry->prev; + else + plist->tail = pentry->prev; + + prefix_list_entry_free (pentry); + + plist->count--; + + if (update_list) + { + if (plist->master->delete_hook) + (*plist->master->delete_hook) (plist); + + if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL) + prefix_list_delete (plist); + else + plist->master->recent = plist; + } +} + +void +prefix_list_entry_add (struct prefix_list *plist, + struct prefix_list_entry *pentry) +{ + struct prefix_list_entry *replace; + struct prefix_list_entry *point; + + /* Automatic asignment of seq no. */ + if (pentry->seq == -1) + pentry->seq = prefix_new_seq_get (plist); + + /* Is there any same seq prefix list entry? */ + replace = prefix_seq_check (plist, pentry->seq); + if (replace) + prefix_list_entry_delete (plist, replace, 0); + + /* Check insert point. */ + for (point = plist->head; point; point = point->next) + if (point->seq >= pentry->seq) + break; + + /* In case of this is the first element of the list. */ + pentry->next = point; + + if (point) + { + if (point->prev) + point->prev->next = pentry; + else + plist->head = pentry; + + pentry->prev = point->prev; + point->prev = pentry; + } + else + { + if (plist->tail) + plist->tail->next = pentry; + else + plist->head = pentry; + + pentry->prev = plist->tail; + plist->tail = pentry; + } + + /* Increment count. */ + plist->count++; + + /* Run hook function. */ + if (plist->master->add_hook) + (*plist->master->add_hook) (plist); + + plist->master->recent = plist; +} + +/* Return string of prefix_list_type. */ +static char * +prefix_list_type_str (struct prefix_list_entry *pentry) +{ + switch (pentry->type) + { + case PREFIX_PERMIT: + return "permit"; + break; + case PREFIX_DENY: + return "deny"; + break; + default: + return ""; + break; + } +} + +int +prefix_list_entry_match (struct prefix_list_entry *pentry, struct prefix *p) +{ + int ret; + + ret = prefix_match (&pentry->prefix, p); + if (! ret) + return 0; + + /* In case of le nor ge is specified, exact match is performed. */ + if (! pentry->le && ! pentry->ge) + { + if (pentry->prefix.prefixlen != p->prefixlen) + return 0; + } + else + { + if (pentry->le) + if (p->prefixlen > pentry->le) + return 0; + + if (pentry->ge) + if (p->prefixlen < pentry->ge) + return 0; + } + return 1; +} + +enum prefix_list_type +prefix_list_apply (struct prefix_list *plist, void *object) +{ + struct prefix_list_entry *pentry; + struct prefix *p; + + p = (struct prefix *) object; + + if (plist == NULL) + return PREFIX_DENY; + + if (plist->count == 0) + return PREFIX_PERMIT; + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + pentry->refcnt++; + if (prefix_list_entry_match (pentry, p)) + { + pentry->hitcnt++; + return pentry->type; + } + } + + return PREFIX_DENY; +} + +void +prefix_list_print (struct prefix_list *plist) +{ + struct prefix_list_entry *pentry; + + if (plist == NULL) + return; + + printf ("ip prefix-list %s: %d entries\n", plist->name, plist->count); + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + if (pentry->any) + printf ("any %s\n", prefix_list_type_str (pentry)); + else + { + struct prefix *p; + char buf[BUFSIZ]; + + p = &pentry->prefix; + + printf (" seq %d %s %s/%d", + pentry->seq, + prefix_list_type_str (pentry), + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + if (pentry->ge) + printf (" ge %d", pentry->ge); + if (pentry->le) + printf (" le %d", pentry->le); + printf ("\n"); + } + } +} + +/* Retrun 1 when plist already include pentry policy. */ +struct prefix_list_entry * +prefix_entry_dup_check (struct prefix_list *plist, + struct prefix_list_entry *new) +{ + struct prefix_list_entry *pentry; + int seq = 0; + + if (new->seq == -1) + seq = prefix_new_seq_get (plist); + else + seq = new->seq; + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + if (prefix_same (&pentry->prefix, &new->prefix) + && pentry->type == new->type + && pentry->le == new->le + && pentry->ge == new->ge + && pentry->seq != seq) + return pentry; + } + return NULL; +} + +int +vty_invalid_prefix_range (struct vty *vty, char *prefix) +{ + vty_out (vty, "%% Invalid prefix range for %s, make sure: len < ge-value <= le-value%s", + prefix, VTY_NEWLINE); + return CMD_WARNING; +} + +int +vty_prefix_list_install (struct vty *vty, afi_t afi, + char *name, char *seq, char *typestr, + char *prefix, char *ge, char *le) +{ + int ret; + enum prefix_list_type type; + struct prefix_list *plist; + struct prefix_list_entry *pentry; + struct prefix_list_entry *dup; + struct prefix p; + int any = 0; + int seqnum = -1; + int lenum = 0; + int genum = 0; + + /* Sequential number. */ + if (seq) + seqnum = atoi (seq); + + /* ge and le number */ + if (ge) + genum = atoi (ge); + if (le) + lenum = atoi (le); + + /* Check filter type. */ + if (strncmp ("permit", typestr, 1) == 0) + type = PREFIX_PERMIT; + else if (strncmp ("deny", typestr, 1) == 0) + type = PREFIX_DENY; + else + { + vty_out (vty, "%% prefix type must be permit or deny%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* "any" is special token for matching any IPv4 addresses. */ + if (afi == AFI_IP) + { + if (strncmp ("any", prefix, strlen (prefix)) == 0) + { + ret = str2prefix_ipv4 ("0.0.0.0/0", (struct prefix_ipv4 *) &p); + genum = 0; + lenum = IPV4_MAX_BITLEN; + any = 1; + } + else + ret = str2prefix_ipv4 (prefix, (struct prefix_ipv4 *) &p); + + if (ret <= 0) + { + vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + } +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + { + if (strncmp ("any", prefix, strlen (prefix)) == 0) + { + ret = str2prefix_ipv6 ("::/0", (struct prefix_ipv6 *) &p); + genum = 0; + lenum = IPV6_MAX_BITLEN; + any = 1; + } + else + ret = str2prefix_ipv6 (prefix, (struct prefix_ipv6 *) &p); + + if (ret <= 0) + { + vty_out (vty, "%% Malformed IPv6 prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + } +#endif /* HAVE_IPV6 */ + + /* ge and le check. */ + if (genum && genum <= p.prefixlen) + return vty_invalid_prefix_range (vty, prefix); + + if (lenum && lenum <= p.prefixlen) + return vty_invalid_prefix_range (vty, prefix); + + if (lenum && genum > lenum) + return vty_invalid_prefix_range (vty, prefix); + + if (genum && lenum == (afi == AFI_IP ? 32 : 128)) + lenum = 0; + + /* Get prefix_list with name. */ + plist = prefix_list_get (afi, name); + + /* Make prefix entry. */ + pentry = prefix_list_entry_make (&p, type, seqnum, lenum, genum, any); + + /* Check same policy. */ + dup = prefix_entry_dup_check (plist, pentry); + + if (dup) + { + prefix_list_entry_free (pentry); + vty_out (vty, "%% Insertion failed - prefix-list entry exists:%s", + VTY_NEWLINE); + vty_out (vty, " seq %d %s %s", dup->seq, typestr, prefix); + if (! any && genum) + vty_out (vty, " ge %d", genum); + if (! any && lenum) + vty_out (vty, " le %d", lenum); + vty_out (vty, "%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Install new filter to the access_list. */ + prefix_list_entry_add (plist, pentry); + + return CMD_SUCCESS; +} + +int +vty_prefix_list_uninstall (struct vty *vty, afi_t afi, + char *name, char *seq, char *typestr, + char *prefix, char *ge, char *le) +{ + int ret; + enum prefix_list_type type; + struct prefix_list *plist; + struct prefix_list_entry *pentry; + struct prefix p; + int seqnum = -1; + int lenum = 0; + int genum = 0; + + /* Check prefix list name. */ + plist = prefix_list_lookup (afi, name); + if (! plist) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Only prefix-list name specified, delete the entire prefix-list. */ + if (seq == NULL && typestr == NULL && prefix == NULL && + ge == NULL && le == NULL) + { + prefix_list_delete (plist); + return CMD_SUCCESS; + } + + /* Check sequence number. */ + if (seq) + seqnum = atoi (seq); + + /* ge and le number */ + if (ge) + genum = atoi (ge); + if (le) + lenum = atoi (le); + + /* Check of filter type. */ + if (strncmp ("permit", typestr, 1) == 0) + type = PREFIX_PERMIT; + else if (strncmp ("deny", typestr, 1) == 0) + type = PREFIX_DENY; + else + { + vty_out (vty, "%% prefix type must be permit or deny%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* "any" is special token for matching any IPv4 addresses. */ + if (afi == AFI_IP) + { + if (strncmp ("any", prefix, strlen (prefix)) == 0) + { + ret = str2prefix_ipv4 ("0.0.0.0/0", (struct prefix_ipv4 *) &p); + genum = 0; + lenum = IPV4_MAX_BITLEN; + } + else + ret = str2prefix_ipv4 (prefix, (struct prefix_ipv4 *) &p); + + if (ret <= 0) + { + vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + } +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + { + if (strncmp ("any", prefix, strlen (prefix)) == 0) + { + ret = str2prefix_ipv6 ("::/0", (struct prefix_ipv6 *) &p); + genum = 0; + lenum = IPV6_MAX_BITLEN; + } + else + ret = str2prefix_ipv6 (prefix, (struct prefix_ipv6 *) &p); + + if (ret <= 0) + { + vty_out (vty, "%% Malformed IPv6 prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + } +#endif /* HAVE_IPV6 */ + + /* Lookup prefix entry. */ + pentry = prefix_list_entry_lookup(plist, &p, type, seqnum, lenum, genum); + + if (pentry == NULL) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Install new filter to the access_list. */ + prefix_list_entry_delete (plist, pentry, 1); + + return CMD_SUCCESS; +} + +int +vty_prefix_list_desc_unset (struct vty *vty, afi_t afi, char *name) +{ + struct prefix_list *plist; + + plist = prefix_list_lookup (afi, name); + if (! plist) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (plist->desc) + { + XFREE (MTYPE_TMP, plist->desc); + plist->desc = NULL; + } + + if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL) + prefix_list_delete (plist); + + return CMD_SUCCESS; +} + +enum display_type +{ + normal_display, + summary_display, + detail_display, + sequential_display, + longer_display, + first_match_display +}; + +void +vty_show_prefix_entry (struct vty *vty, afi_t afi, struct prefix_list *plist, + struct prefix_master *master, enum display_type dtype, + int seqnum) +{ + struct prefix_list_entry *pentry; + + if (dtype == normal_display) + { + vty_out (vty, "ip%s prefix-list %s: %d entries%s", + afi == AFI_IP ? "" : "v6", + plist->name, plist->count, VTY_NEWLINE); + if (plist->desc) + vty_out (vty, " Description: %s%s", plist->desc, VTY_NEWLINE); + } + else if (dtype == summary_display || dtype == detail_display) + { + vty_out (vty, "ip%s prefix-list %s:%s", + afi == AFI_IP ? "" : "v6", plist->name, VTY_NEWLINE); + + if (plist->desc) + vty_out (vty, " Description: %s%s", plist->desc, VTY_NEWLINE); + + vty_out (vty, " count: %d, range entries: %d, sequences: %d - %d%s", + plist->count, plist->rangecount, + plist->head ? plist->head->seq : 0, + plist->tail ? plist->tail->seq : 0, + VTY_NEWLINE); + } + + if (dtype != summary_display) + { + for (pentry = plist->head; pentry; pentry = pentry->next) + { + if (dtype == sequential_display && pentry->seq != seqnum) + continue; + + vty_out (vty, " "); + + if (master->seqnum) + vty_out (vty, "seq %d ", pentry->seq); + + vty_out (vty, "%s ", prefix_list_type_str (pentry)); + + if (pentry->any) + vty_out (vty, "any"); + else + { + struct prefix *p = &pentry->prefix; + char buf[BUFSIZ]; + + vty_out (vty, "%s/%d", + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + if (pentry->ge) + vty_out (vty, " ge %d", pentry->ge); + if (pentry->le) + vty_out (vty, " le %d", pentry->le); + } + + if (dtype == detail_display || dtype == sequential_display) + vty_out (vty, " (hit count: %ld, refcount: %ld)", + pentry->hitcnt, pentry->refcnt); + + vty_out (vty, "%s", VTY_NEWLINE); + } + } +} + +int +vty_show_prefix_list (struct vty *vty, afi_t afi, char *name, + char *seq, enum display_type dtype) +{ + struct prefix_list *plist; + struct prefix_master *master; + int seqnum = 0; + + master = prefix_master_get (afi); + if (master == NULL) + return CMD_WARNING; + + if (seq) + seqnum = atoi (seq); + + if (name) + { + plist = prefix_list_lookup (afi, name); + if (! plist) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum); + } + else + { + if (dtype == detail_display || dtype == summary_display) + { + if (master->recent) + vty_out (vty, "Prefix-list with the last deletion/insertion: %s%s", + master->recent->name, VTY_NEWLINE); + } + + for (plist = master->num.head; plist; plist = plist->next) + vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum); + + for (plist = master->str.head; plist; plist = plist->next) + vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum); + } + + return CMD_SUCCESS; +} + +int +vty_show_prefix_list_prefix (struct vty *vty, afi_t afi, char *name, + char *prefix, enum display_type type) +{ + struct prefix_list *plist; + struct prefix_list_entry *pentry; + struct prefix p; + int ret; + int match; + + plist = prefix_list_lookup (afi, name); + if (! plist) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = str2prefix (prefix, &p); + if (ret <= 0) + { + vty_out (vty, "%% prefix is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + match = 0; + + if (type == normal_display || type == first_match_display) + if (prefix_same (&p, &pentry->prefix)) + match = 1; + + if (type == longer_display) + if (prefix_match (&p, &pentry->prefix)) + match = 1; + + if (match) + { + vty_out (vty, " seq %d %s ", + pentry->seq, + prefix_list_type_str (pentry)); + + if (pentry->any) + vty_out (vty, "any"); + else + { + struct prefix *p = &pentry->prefix; + char buf[BUFSIZ]; + + vty_out (vty, "%s/%d", + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + if (pentry->ge) + vty_out (vty, " ge %d", pentry->ge); + if (pentry->le) + vty_out (vty, " le %d", pentry->le); + } + + if (type == normal_display || type == first_match_display) + vty_out (vty, " (hit count: %ld, refcount: %ld)", + pentry->hitcnt, pentry->refcnt); + + vty_out (vty, "%s", VTY_NEWLINE); + + if (type == first_match_display) + return CMD_SUCCESS; + } + } + return CMD_SUCCESS; +} + +int +vty_clear_prefix_list (struct vty *vty, afi_t afi, char *name, char *prefix) +{ + struct prefix_master *master; + struct prefix_list *plist; + struct prefix_list_entry *pentry; + int ret; + struct prefix p; + + master = prefix_master_get (afi); + if (master == NULL) + return CMD_WARNING; + + if (name == NULL && prefix == NULL) + { + for (plist = master->num.head; plist; plist = plist->next) + for (pentry = plist->head; pentry; pentry = pentry->next) + pentry->hitcnt = 0; + + for (plist = master->str.head; plist; plist = plist->next) + for (pentry = plist->head; pentry; pentry = pentry->next) + pentry->hitcnt = 0; + } + else + { + plist = prefix_list_lookup (afi, name); + if (! plist) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (prefix) + { + ret = str2prefix (prefix, &p); + if (ret <= 0) + { + vty_out (vty, "%% prefix is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + if (prefix) + { + if (prefix_match (&pentry->prefix, &p)) + pentry->hitcnt = 0; + } + else + pentry->hitcnt = 0; + } + } + return CMD_SUCCESS; +} + +DEFUN (ip_prefix_list, + ip_prefix_list_cmd, + "ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, + argv[1], argv[2], NULL, NULL); +} + +DEFUN (ip_prefix_list_ge, + ip_prefix_list_ge_cmd, + "ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[3], NULL); +} + +DEFUN (ip_prefix_list_ge_le, + ip_prefix_list_ge_le_cmd, + "ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4]); +} + +DEFUN (ip_prefix_list_le, + ip_prefix_list_le_cmd, + "ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], NULL, argv[3]); +} + +DEFUN (ip_prefix_list_le_ge, + ip_prefix_list_le_ge_cmd, + "ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[4], argv[3]); +} + +DEFUN (ip_prefix_list_seq, + ip_prefix_list_seq_cmd, + "ip prefix-list WORD seq <1-4294967295> (deny|permit) (A.B.C.D/M|any)", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], NULL, NULL); +} + +DEFUN (ip_prefix_list_seq_ge, + ip_prefix_list_seq_ge_cmd, + "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[4], NULL); +} + +DEFUN (ip_prefix_list_seq_ge_le, + ip_prefix_list_seq_ge_le_cmd, + "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5]); +} + +DEFUN (ip_prefix_list_seq_le, + ip_prefix_list_seq_le_cmd, + "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4]); +} + +DEFUN (ip_prefix_list_seq_le_ge, + ip_prefix_list_seq_le_ge_cmd, + "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[5], argv[4]); +} + +DEFUN (no_ip_prefix_list, + no_ip_prefix_list_cmd, + "no ip prefix-list WORD", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, NULL, + NULL, NULL, NULL); +} + +DEFUN (no_ip_prefix_list_prefix, + no_ip_prefix_list_prefix_cmd, + "no ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], NULL, NULL); +} + +DEFUN (no_ip_prefix_list_ge, + no_ip_prefix_list_ge_cmd, + "no ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[3], NULL); +} + +DEFUN (no_ip_prefix_list_ge_le, + no_ip_prefix_list_ge_le_cmd, + "no ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4]); +} + +DEFUN (no_ip_prefix_list_le, + no_ip_prefix_list_le_cmd, + "no ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], NULL, argv[3]); +} + +DEFUN (no_ip_prefix_list_le_ge, + no_ip_prefix_list_le_ge_cmd, + "no ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[4], argv[3]); +} + +DEFUN (no_ip_prefix_list_seq, + no_ip_prefix_list_seq_cmd, + "no ip prefix-list WORD seq <1-4294967295> (deny|permit) (A.B.C.D/M|any)", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], NULL, NULL); +} + +DEFUN (no_ip_prefix_list_seq_ge, + no_ip_prefix_list_seq_ge_cmd, + "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[4], NULL); +} + +DEFUN (no_ip_prefix_list_seq_ge_le, + no_ip_prefix_list_seq_ge_le_cmd, + "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5]); +} + +DEFUN (no_ip_prefix_list_seq_le, + no_ip_prefix_list_seq_le_cmd, + "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4]); +} + +DEFUN (no_ip_prefix_list_seq_le_ge, + no_ip_prefix_list_seq_le_ge_cmd, + "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[5], argv[4]); +} + +DEFUN (ip_prefix_list_sequence_number, + ip_prefix_list_sequence_number_cmd, + "ip prefix-list sequence-number", + IP_STR + PREFIX_LIST_STR + "Include/exclude sequence numbers in NVGEN\n") +{ + prefix_master_ipv4.seqnum = 1; + return CMD_SUCCESS; +} + +DEFUN (no_ip_prefix_list_sequence_number, + no_ip_prefix_list_sequence_number_cmd, + "no ip prefix-list sequence-number", + NO_STR + IP_STR + PREFIX_LIST_STR + "Include/exclude sequence numbers in NVGEN\n") +{ + prefix_master_ipv4.seqnum = 0; + return CMD_SUCCESS; +} + +DEFUN (ip_prefix_list_description, + ip_prefix_list_description_cmd, + "ip prefix-list WORD description .LINE", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n" + "Up to 80 characters describing this prefix-list\n") +{ + struct prefix_list *plist; + struct buffer *b; + int i; + + plist = prefix_list_get (AFI_IP, argv[0]); + + if (plist->desc) + { + XFREE (MTYPE_TMP, plist->desc); + plist->desc = NULL; + } + + /* Below is description get codes. */ + b = buffer_new (1024); + for (i = 1; i < argc; i++) + { + buffer_putstr (b, (u_char *)argv[i]); + buffer_putc (b, ' '); + } + buffer_putc (b, '\0'); + + plist->desc = buffer_getstr (b); + + buffer_free (b); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_prefix_list_description, + no_ip_prefix_list_description_cmd, + "no ip prefix-list WORD description", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n") +{ + return vty_prefix_list_desc_unset (vty, AFI_IP, argv[0]); +} + +ALIAS (no_ip_prefix_list_description, + no_ip_prefix_list_description_arg_cmd, + "no ip prefix-list WORD description .LINE", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n" + "Up to 80 characters describing this prefix-list\n") + +DEFUN (show_ip_prefix_list, + show_ip_prefix_list_cmd, + "show ip prefix-list", + SHOW_STR + IP_STR + PREFIX_LIST_STR) +{ + return vty_show_prefix_list (vty, AFI_IP, NULL, NULL, normal_display); +} + +DEFUN (show_ip_prefix_list_name, + show_ip_prefix_list_name_cmd, + "show ip prefix-list WORD", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, normal_display); +} + +DEFUN (show_ip_prefix_list_name_seq, + show_ip_prefix_list_name_seq_cmd, + "show ip prefix-list WORD seq <1-4294967295>", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n") +{ + return vty_show_prefix_list (vty, AFI_IP, argv[0], argv[1], sequential_display); +} + +DEFUN (show_ip_prefix_list_prefix, + show_ip_prefix_list_prefix_cmd, + "show ip prefix-list WORD A.B.C.D/M", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], normal_display); +} + +DEFUN (show_ip_prefix_list_prefix_longer, + show_ip_prefix_list_prefix_longer_cmd, + "show ip prefix-list WORD A.B.C.D/M longer", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "Lookup longer prefix\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], longer_display); +} + +DEFUN (show_ip_prefix_list_prefix_first_match, + show_ip_prefix_list_prefix_first_match_cmd, + "show ip prefix-list WORD A.B.C.D/M first-match", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" + "First matched prefix\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], first_match_display); +} + +DEFUN (show_ip_prefix_list_summary, + show_ip_prefix_list_summary_cmd, + "show ip prefix-list summary", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Summary of prefix lists\n") +{ + return vty_show_prefix_list (vty, AFI_IP, NULL, NULL, summary_display); +} + +DEFUN (show_ip_prefix_list_summary_name, + show_ip_prefix_list_summary_name_cmd, + "show ip prefix-list summary WORD", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Summary of prefix lists\n" + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, summary_display); +} + + +DEFUN (show_ip_prefix_list_detail, + show_ip_prefix_list_detail_cmd, + "show ip prefix-list detail", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Detail of prefix lists\n") +{ + return vty_show_prefix_list (vty, AFI_IP, NULL, NULL, detail_display); +} + +DEFUN (show_ip_prefix_list_detail_name, + show_ip_prefix_list_detail_name_cmd, + "show ip prefix-list detail WORD", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Detail of prefix lists\n" + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, detail_display); +} + +DEFUN (clear_ip_prefix_list, + clear_ip_prefix_list_cmd, + "clear ip prefix-list", + CLEAR_STR + IP_STR + PREFIX_LIST_STR) +{ + return vty_clear_prefix_list (vty, AFI_IP, NULL, NULL); +} + +DEFUN (clear_ip_prefix_list_name, + clear_ip_prefix_list_name_cmd, + "clear ip prefix-list WORD", + CLEAR_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_clear_prefix_list (vty, AFI_IP, argv[0], NULL); +} + +DEFUN (clear_ip_prefix_list_name_prefix, + clear_ip_prefix_list_name_prefix_cmd, + "clear ip prefix-list WORD A.B.C.D/M", + CLEAR_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n") +{ + return vty_clear_prefix_list (vty, AFI_IP, argv[0], argv[1]); +} + +#ifdef HAVE_IPV6 +DEFUN (ipv6_prefix_list, + ipv6_prefix_list_cmd, + "ipv6 prefix-list WORD (deny|permit) (X:X::X:X/M|any)", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Any prefix match. Same as \"::0/0 le 128\"\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, + argv[1], argv[2], NULL, NULL); +} + +DEFUN (ipv6_prefix_list_ge, + ipv6_prefix_list_ge_cmd, + "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[3], NULL); +} + +DEFUN (ipv6_prefix_list_ge_le, + ipv6_prefix_list_ge_le_cmd, + "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") + +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4]); +} + +DEFUN (ipv6_prefix_list_le, + ipv6_prefix_list_le_cmd, + "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], NULL, argv[3]); +} + +DEFUN (ipv6_prefix_list_le_ge, + ipv6_prefix_list_le_ge_cmd, + "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[4], argv[3]); +} + +DEFUN (ipv6_prefix_list_seq, + ipv6_prefix_list_seq_cmd, + "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) (X:X::X:X/M|any)", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Any prefix match. Same as \"::0/0 le 128\"\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], NULL, NULL); +} + +DEFUN (ipv6_prefix_list_seq_ge, + ipv6_prefix_list_seq_ge_cmd, + "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[4], NULL); +} + +DEFUN (ipv6_prefix_list_seq_ge_le, + ipv6_prefix_list_seq_ge_le_cmd, + "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5]); +} + +DEFUN (ipv6_prefix_list_seq_le, + ipv6_prefix_list_seq_le_cmd, + "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4]); +} + +DEFUN (ipv6_prefix_list_seq_le_ge, + ipv6_prefix_list_seq_le_ge_cmd, + "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[5], argv[4]); +} + +DEFUN (no_ipv6_prefix_list, + no_ipv6_prefix_list_cmd, + "no ipv6 prefix-list WORD", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, NULL, + NULL, NULL, NULL); +} + +DEFUN (no_ipv6_prefix_list_prefix, + no_ipv6_prefix_list_prefix_cmd, + "no ipv6 prefix-list WORD (deny|permit) (X:X::X:X/M|any)", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Any prefix match. Same as \"::0/0 le 128\"\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], NULL, NULL); +} + +DEFUN (no_ipv6_prefix_list_ge, + no_ipv6_prefix_list_ge_cmd, + "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[3], NULL); +} + +DEFUN (no_ipv6_prefix_list_ge_le, + no_ipv6_prefix_list_ge_le_cmd, + "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4]); +} + +DEFUN (no_ipv6_prefix_list_le, + no_ipv6_prefix_list_le_cmd, + "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], NULL, argv[3]); +} + +DEFUN (no_ipv6_prefix_list_le_ge, + no_ipv6_prefix_list_le_ge_cmd, + "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[4], argv[3]); +} + +DEFUN (no_ipv6_prefix_list_seq, + no_ipv6_prefix_list_seq_cmd, + "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) (X:X::X:X/M|any)", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Any prefix match. Same as \"::0/0 le 128\"\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], NULL, NULL); +} + +DEFUN (no_ipv6_prefix_list_seq_ge, + no_ipv6_prefix_list_seq_ge_cmd, + "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[4], NULL); +} + +DEFUN (no_ipv6_prefix_list_seq_ge_le, + no_ipv6_prefix_list_seq_ge_le_cmd, + "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5]); +} + +DEFUN (no_ipv6_prefix_list_seq_le, + no_ipv6_prefix_list_seq_le_cmd, + "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4]); +} + +DEFUN (no_ipv6_prefix_list_seq_le_ge, + no_ipv6_prefix_list_seq_le_ge_cmd, + "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[5], argv[4]); +} + +DEFUN (ipv6_prefix_list_sequence_number, + ipv6_prefix_list_sequence_number_cmd, + "ipv6 prefix-list sequence-number", + IPV6_STR + PREFIX_LIST_STR + "Include/exclude sequence numbers in NVGEN\n") +{ + prefix_master_ipv6.seqnum = 1; + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_prefix_list_sequence_number, + no_ipv6_prefix_list_sequence_number_cmd, + "no ipv6 prefix-list sequence-number", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Include/exclude sequence numbers in NVGEN\n") +{ + prefix_master_ipv6.seqnum = 0; + return CMD_SUCCESS; +} + +DEFUN (ipv6_prefix_list_description, + ipv6_prefix_list_description_cmd, + "ipv6 prefix-list WORD description .LINE", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n" + "Up to 80 characters describing this prefix-list\n") +{ + struct prefix_list *plist; + struct buffer *b; + int i; + + plist = prefix_list_get (AFI_IP6, argv[0]); + + if (plist->desc) + { + XFREE (MTYPE_TMP, plist->desc); + plist->desc = NULL; + } + + /* Below is description get codes. */ + b = buffer_new (1024); + for (i = 1; i < argc; i++) + { + buffer_putstr (b, (u_char *)argv[i]); + buffer_putc (b, ' '); + } + buffer_putc (b, '\0'); + + plist->desc = buffer_getstr (b); + + buffer_free (b); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_prefix_list_description, + no_ipv6_prefix_list_description_cmd, + "no ipv6 prefix-list WORD description", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n") +{ + return vty_prefix_list_desc_unset (vty, AFI_IP6, argv[0]); +} + +ALIAS (no_ipv6_prefix_list_description, + no_ipv6_prefix_list_description_arg_cmd, + "no ipv6 prefix-list WORD description .LINE", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n" + "Up to 80 characters describing this prefix-list\n") + +DEFUN (show_ipv6_prefix_list, + show_ipv6_prefix_list_cmd, + "show ipv6 prefix-list", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR) +{ + return vty_show_prefix_list (vty, AFI_IP6, NULL, NULL, normal_display); +} + +DEFUN (show_ipv6_prefix_list_name, + show_ipv6_prefix_list_name_cmd, + "show ipv6 prefix-list WORD", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, normal_display); +} + +DEFUN (show_ipv6_prefix_list_name_seq, + show_ipv6_prefix_list_name_seq_cmd, + "show ipv6 prefix-list WORD seq <1-4294967295>", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, argv[0], argv[1], sequential_display); +} + +DEFUN (show_ipv6_prefix_list_prefix, + show_ipv6_prefix_list_prefix_cmd, + "show ipv6 prefix-list WORD X:X::X:X/M", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], normal_display); +} + +DEFUN (show_ipv6_prefix_list_prefix_longer, + show_ipv6_prefix_list_prefix_longer_cmd, + "show ipv6 prefix-list WORD X:X::X:X/M longer", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "Lookup longer prefix\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], longer_display); +} + +DEFUN (show_ipv6_prefix_list_prefix_first_match, + show_ipv6_prefix_list_prefix_first_match_cmd, + "show ipv6 prefix-list WORD X:X::X:X/M first-match", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" + "First matched prefix\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], first_match_display); +} + +DEFUN (show_ipv6_prefix_list_summary, + show_ipv6_prefix_list_summary_cmd, + "show ipv6 prefix-list summary", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Summary of prefix lists\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, NULL, NULL, summary_display); +} + +DEFUN (show_ipv6_prefix_list_summary_name, + show_ipv6_prefix_list_summary_name_cmd, + "show ipv6 prefix-list summary WORD", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Summary of prefix lists\n" + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, summary_display); +} + +DEFUN (show_ipv6_prefix_list_detail, + show_ipv6_prefix_list_detail_cmd, + "show ipv6 prefix-list detail", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Detail of prefix lists\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, NULL, NULL, detail_display); +} + +DEFUN (show_ipv6_prefix_list_detail_name, + show_ipv6_prefix_list_detail_name_cmd, + "show ipv6 prefix-list detail WORD", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Detail of prefix lists\n" + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, detail_display); +} + +DEFUN (clear_ipv6_prefix_list, + clear_ipv6_prefix_list_cmd, + "clear ipv6 prefix-list", + CLEAR_STR + IPV6_STR + PREFIX_LIST_STR) +{ + return vty_clear_prefix_list (vty, AFI_IP6, NULL, NULL); +} + +DEFUN (clear_ipv6_prefix_list_name, + clear_ipv6_prefix_list_name_cmd, + "clear ipv6 prefix-list WORD", + CLEAR_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_clear_prefix_list (vty, AFI_IP6, argv[0], NULL); +} + +DEFUN (clear_ipv6_prefix_list_name_prefix, + clear_ipv6_prefix_list_name_prefix_cmd, + "clear ipv6 prefix-list WORD X:X::X:X/M", + CLEAR_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n") +{ + return vty_clear_prefix_list (vty, AFI_IP6, argv[0], argv[1]); +} +#endif /* HAVE_IPV6 */ + +/* Configuration write function. */ +int +config_write_prefix_afi (afi_t afi, struct vty *vty) +{ + struct prefix_list *plist; + struct prefix_list_entry *pentry; + struct prefix_master *master; + int write = 0; + + master = prefix_master_get (afi); + if (master == NULL) + return 0; + + if (! master->seqnum) + { + vty_out (vty, "no ip%s prefix-list sequence-number%s", + afi == AFI_IP ? "" : "v6", VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + } + + for (plist = master->num.head; plist; plist = plist->next) + { + if (plist->desc) + { + vty_out (vty, "ip%s prefix-list %s description %s%s", + afi == AFI_IP ? "" : "v6", + plist->name, plist->desc, VTY_NEWLINE); + write++; + } + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + vty_out (vty, "ip%s prefix-list %s ", + afi == AFI_IP ? "" : "v6", + plist->name); + + if (master->seqnum) + vty_out (vty, "seq %d ", pentry->seq); + + vty_out (vty, "%s ", prefix_list_type_str (pentry)); + + if (pentry->any) + vty_out (vty, "any"); + else + { + struct prefix *p = &pentry->prefix; + char buf[BUFSIZ]; + + vty_out (vty, "%s/%d", + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + if (pentry->ge) + vty_out (vty, " ge %d", pentry->ge); + if (pentry->le) + vty_out (vty, " le %d", pentry->le); + } + vty_out (vty, "%s", VTY_NEWLINE); + write++; + } + /* vty_out (vty, "!%s", VTY_NEWLINE); */ + } + + for (plist = master->str.head; plist; plist = plist->next) + { + if (plist->desc) + { + vty_out (vty, "ip%s prefix-list %s description %s%s", + afi == AFI_IP ? "" : "v6", + plist->name, plist->desc, VTY_NEWLINE); + write++; + } + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + vty_out (vty, "ip%s prefix-list %s ", + afi == AFI_IP ? "" : "v6", + plist->name); + + if (master->seqnum) + vty_out (vty, "seq %d ", pentry->seq); + + vty_out (vty, "%s", prefix_list_type_str (pentry)); + + if (pentry->any) + vty_out (vty, " any"); + else + { + struct prefix *p = &pentry->prefix; + char buf[BUFSIZ]; + + vty_out (vty, " %s/%d", + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + if (pentry->ge) + vty_out (vty, " ge %d", pentry->ge); + if (pentry->le) + vty_out (vty, " le %d", pentry->le); + } + vty_out (vty, "%s", VTY_NEWLINE); + write++; + } + } + + return write; +} + +int stream_putc (struct stream *, u_char); +int stream_putl (struct stream *, u_int32_t); +int stream_put_prefix (struct stream *, struct prefix *); + +struct stream * +prefix_bgp_orf_entry (struct stream *s, struct prefix_list *plist, + u_char init_flag, u_char permit_flag, u_char deny_flag) +{ + struct prefix_list_entry *pentry; + + if (! plist) + return s; + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + u_char flag = init_flag; + struct prefix *p = &pentry->prefix; + + flag |= (pentry->type == PREFIX_PERMIT ? + permit_flag : deny_flag); + stream_putc (s, flag); + stream_putl (s, (u_int32_t)pentry->seq); + stream_putc (s, (u_char)pentry->ge); + stream_putc (s, (u_char)pentry->le); + stream_put_prefix (s, p); + } + + return s; +} + +int +prefix_bgp_orf_set (char *name, afi_t afi, struct orf_prefix *orfp, + int permit, int set) +{ + struct prefix_list *plist; + struct prefix_list_entry *pentry; + + /* ge and le value check */ + if (orfp->ge && orfp->ge <= orfp->p.prefixlen) + return CMD_WARNING; + if (orfp->le && orfp->le <= orfp->p.prefixlen) + return CMD_WARNING; + if (orfp->le && orfp->ge > orfp->le) + return CMD_WARNING; + + if (orfp->ge && orfp->le == (afi == AFI_IP ? 32 : 128)) + orfp->le = 0; + + plist = prefix_list_get (AFI_ORF_PREFIX, name); + if (! plist) + return CMD_WARNING; + + if (set) + { + pentry = prefix_list_entry_make (&orfp->p, + (permit ? PREFIX_PERMIT : PREFIX_DENY), + orfp->seq, orfp->le, orfp->ge, 0); + + if (prefix_entry_dup_check (plist, pentry)) + { + prefix_list_entry_free (pentry); + return CMD_WARNING; + } + + prefix_list_entry_add (plist, pentry); + } + else + { + pentry = prefix_list_entry_lookup (plist, &orfp->p, + (permit ? PREFIX_PERMIT : PREFIX_DENY), + orfp->seq, orfp->le, orfp->ge); + + if (! pentry) + return CMD_WARNING; + + prefix_list_entry_delete (plist, pentry, 1); + } + + return CMD_SUCCESS; +} + +void +prefix_bgp_orf_remove_all (char *name) +{ + struct prefix_list *plist; + + plist = prefix_list_lookup (AFI_ORF_PREFIX, name); + if (plist) + prefix_list_delete (plist); +} + +/* return prefix count */ +int +prefix_bgp_show_prefix_list (struct vty *vty, afi_t afi, char *name) +{ + struct prefix_list *plist; + struct prefix_list_entry *pentry; + + plist = prefix_list_lookup (AFI_ORF_PREFIX, name); + if (! plist) + return 0; + + if (! vty) + return plist->count; + + vty_out (vty, "ip%s prefix-list %s: %d entries%s", + afi == AFI_IP ? "" : "v6", + plist->name, plist->count, VTY_NEWLINE); + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + struct prefix *p = &pentry->prefix; + char buf[BUFSIZ]; + + vty_out (vty, " seq %d %s %s/%d", pentry->seq, + prefix_list_type_str (pentry), + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + if (pentry->ge) + vty_out (vty, " ge %d", pentry->ge); + if (pentry->le) + vty_out (vty, " le %d", pentry->le); + + vty_out (vty, "%s", VTY_NEWLINE); + } + return plist->count; +} + +void +prefix_list_reset_orf () +{ + struct prefix_list *plist; + struct prefix_list *next; + struct prefix_master *master; + + master = prefix_master_get (AFI_ORF_PREFIX); + if (master == NULL) + return; + + for (plist = master->num.head; plist; plist = next) + { + next = plist->next; + prefix_list_delete (plist); + } + for (plist = master->str.head; plist; plist = next) + { + next = plist->next; + prefix_list_delete (plist); + } + + assert (master->num.head == NULL); + assert (master->num.tail == NULL); + + assert (master->str.head == NULL); + assert (master->str.tail == NULL); + + master->seqnum = 1; + master->recent = NULL; +} + + +/* Prefix-list node. */ +struct cmd_node prefix_node = +{ + PREFIX_NODE, + "", /* Prefix list has no interface. */ + 1 +}; + +int +config_write_prefix_ipv4 (struct vty *vty) +{ + return config_write_prefix_afi (AFI_IP, vty); +} + +void +prefix_list_reset_ipv4 () +{ + struct prefix_list *plist; + struct prefix_list *next; + struct prefix_master *master; + + master = prefix_master_get (AFI_IP); + if (master == NULL) + return; + + for (plist = master->num.head; plist; plist = next) + { + next = plist->next; + prefix_list_delete (plist); + } + for (plist = master->str.head; plist; plist = next) + { + next = plist->next; + prefix_list_delete (plist); + } + + assert (master->num.head == NULL); + assert (master->num.tail == NULL); + + assert (master->str.head == NULL); + assert (master->str.tail == NULL); + + master->seqnum = 1; + master->recent = NULL; +} + +void +prefix_list_init_ipv4 () +{ + install_node (&prefix_node, config_write_prefix_ipv4); + + install_element (CONFIG_NODE, &ip_prefix_list_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_ge_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_ge_le_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_le_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_le_ge_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_seq_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_seq_ge_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_seq_ge_le_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_seq_le_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_seq_le_ge_cmd); + + install_element (CONFIG_NODE, &no_ip_prefix_list_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_prefix_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_ge_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_ge_le_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_le_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_le_ge_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_seq_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_seq_ge_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_seq_ge_le_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_seq_le_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_seq_le_ge_cmd); + + install_element (CONFIG_NODE, &ip_prefix_list_description_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_description_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_description_arg_cmd); + + install_element (CONFIG_NODE, &ip_prefix_list_sequence_number_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_sequence_number_cmd); + + install_element (VIEW_NODE, &show_ip_prefix_list_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_name_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_name_seq_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_prefix_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_prefix_longer_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_prefix_first_match_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_summary_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_summary_name_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_detail_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_detail_name_cmd); + + install_element (ENABLE_NODE, &show_ip_prefix_list_cmd); + install_element (ENABLE_NODE, &show_ip_prefix_list_name_cmd); + install_element (ENABLE_NODE, &show_ip_prefix_list_name_seq_cmd); + install_element (ENABLE_NODE, &show_ip_prefix_list_prefix_cmd); + install_element (ENABLE_NODE, &show_ip_prefix_list_prefix_longer_cmd); + install_element (ENABLE_NODE, &show_ip_prefix_list_prefix_first_match_cmd); + install_element (ENABLE_NODE, &show_ip_prefix_list_summary_cmd); + install_element (ENABLE_NODE, &show_ip_prefix_list_summary_name_cmd); + install_element (ENABLE_NODE, &show_ip_prefix_list_detail_cmd); + install_element (ENABLE_NODE, &show_ip_prefix_list_detail_name_cmd); + + install_element (ENABLE_NODE, &clear_ip_prefix_list_cmd); + install_element (ENABLE_NODE, &clear_ip_prefix_list_name_cmd); + install_element (ENABLE_NODE, &clear_ip_prefix_list_name_prefix_cmd); +} + +#ifdef HAVE_IPV6 +/* Prefix-list node. */ +struct cmd_node prefix_ipv6_node = +{ + PREFIX_IPV6_NODE, + "", /* Prefix list has no interface. */ + 1 +}; + +int +config_write_prefix_ipv6 (struct vty *vty) +{ + return config_write_prefix_afi (AFI_IP6, vty); +} + +void +prefix_list_reset_ipv6 () +{ + struct prefix_list *plist; + struct prefix_list *next; + struct prefix_master *master; + + master = prefix_master_get (AFI_IP6); + if (master == NULL) + return; + + for (plist = master->num.head; plist; plist = next) + { + next = plist->next; + prefix_list_delete (plist); + } + for (plist = master->str.head; plist; plist = next) + { + next = plist->next; + prefix_list_delete (plist); + } + + assert (master->num.head == NULL); + assert (master->num.tail == NULL); + + assert (master->str.head == NULL); + assert (master->str.tail == NULL); + + master->seqnum = 1; + master->recent = NULL; +} + +void +prefix_list_init_ipv6 () +{ + install_node (&prefix_ipv6_node, config_write_prefix_ipv6); + + install_element (CONFIG_NODE, &ipv6_prefix_list_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_ge_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_ge_le_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_le_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_le_ge_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_seq_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_seq_ge_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_seq_ge_le_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_seq_le_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_seq_le_ge_cmd); + + install_element (CONFIG_NODE, &no_ipv6_prefix_list_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_prefix_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_ge_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_ge_le_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_le_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_le_ge_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_ge_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_ge_le_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_le_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_le_ge_cmd); + + install_element (CONFIG_NODE, &ipv6_prefix_list_description_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_description_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_description_arg_cmd); + + install_element (CONFIG_NODE, &ipv6_prefix_list_sequence_number_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_sequence_number_cmd); + + install_element (VIEW_NODE, &show_ipv6_prefix_list_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_name_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_name_seq_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_prefix_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_prefix_longer_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_prefix_first_match_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_summary_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_summary_name_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_detail_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_detail_name_cmd); + + install_element (ENABLE_NODE, &show_ipv6_prefix_list_cmd); + install_element (ENABLE_NODE, &show_ipv6_prefix_list_name_cmd); + install_element (ENABLE_NODE, &show_ipv6_prefix_list_name_seq_cmd); + install_element (ENABLE_NODE, &show_ipv6_prefix_list_prefix_cmd); + install_element (ENABLE_NODE, &show_ipv6_prefix_list_prefix_longer_cmd); + install_element (ENABLE_NODE, &show_ipv6_prefix_list_prefix_first_match_cmd); + install_element (ENABLE_NODE, &show_ipv6_prefix_list_summary_cmd); + install_element (ENABLE_NODE, &show_ipv6_prefix_list_summary_name_cmd); + install_element (ENABLE_NODE, &show_ipv6_prefix_list_detail_cmd); + install_element (ENABLE_NODE, &show_ipv6_prefix_list_detail_name_cmd); + + install_element (ENABLE_NODE, &clear_ipv6_prefix_list_cmd); + install_element (ENABLE_NODE, &clear_ipv6_prefix_list_name_cmd); + install_element (ENABLE_NODE, &clear_ipv6_prefix_list_name_prefix_cmd); +} +#endif /* HAVE_IPV6 */ + +void +prefix_list_init () +{ + prefix_list_init_ipv4 (); +#ifdef HAVE_IPV6 + prefix_list_init_ipv6 (); +#endif /* HAVE_IPV6 */ +} + +void +prefix_list_reset () +{ + prefix_list_reset_ipv4 (); +#ifdef HAVE_IPV6 + prefix_list_reset_ipv6 (); +#endif /* HAVE_IPV6 */ + prefix_list_reset_orf (); +} diff --git a/lib/plist.h b/lib/plist.h new file mode 100644 index 00000000..9a9eb710 --- /dev/null +++ b/lib/plist.h @@ -0,0 +1,78 @@ +/* + * Prefix list functions. + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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. + */ + +#define AFI_ORF_PREFIX 65535 + +enum prefix_list_type +{ + PREFIX_DENY, + PREFIX_PERMIT, +}; + +enum prefix_name_type +{ + PREFIX_TYPE_STRING, + PREFIX_TYPE_NUMBER +}; + +struct prefix_list +{ + char *name; + char *desc; + + struct prefix_master *master; + + enum prefix_name_type type; + + int count; + int rangecount; + + struct prefix_list_entry *head; + struct prefix_list_entry *tail; + + struct prefix_list *next; + struct prefix_list *prev; +}; + +struct orf_prefix +{ + u_int32_t seq; + u_char ge; + u_char le; + struct prefix p; +}; + +/* Prototypes. */ +void prefix_list_init (void); +void prefix_list_reset (void); +void prefix_list_add_hook (void (*func) (struct prefix_list *)); +void prefix_list_delete_hook (void (*func) (struct prefix_list *)); + +struct prefix_list *prefix_list_lookup (afi_t, char *); +enum prefix_list_type prefix_list_apply (struct prefix_list *, void *); + +struct stream * +prefix_bgp_orf_entry (struct stream *, struct prefix_list *, + u_char, u_char, u_char); +int prefix_bgp_orf_set (char *, afi_t, struct orf_prefix *, int, int); +void prefix_bgp_orf_remove_all (char *); +int prefix_bgp_show_prefix_list (struct vty *, afi_t, char *); diff --git a/lib/prefix.c b/lib/prefix.c new file mode 100644 index 00000000..61e0f195 --- /dev/null +++ b/lib/prefix.c @@ -0,0 +1,696 @@ +/* + * Prefix related functions. + * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "prefix.h" +#include "vty.h" +#include "sockunion.h" +#include "memory.h" +#include "log.h" + +/* Maskbit. */ +static u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, + 0xf8, 0xfc, 0xfe, 0xff}; + +/* Number of bits in prefix type. */ +#ifndef PNBBY +#define PNBBY 8 +#endif /* PNBBY */ + +#define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff) + +/* Address Famiy Identifier to Address Family converter. */ +int +afi2family (int afi) +{ + if (afi == AFI_IP) + return AF_INET; +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + return AF_INET6; +#endif /* HAVE_IPV6 */ + return 0; +} + +int +family2afi (int family) +{ + if (family == AF_INET) + return AFI_IP; +#ifdef HAVE_IPV6 + else if (family == AF_INET6) + return AFI_IP6; +#endif /* HAVE_IPV6 */ + return 0; +} + +/* If n includes p prefix then return 1 else return 0. */ +int +prefix_match (struct prefix *n, struct prefix *p) +{ + int offset; + int shift; + + /* Set both prefix's head pointer. */ + u_char *np = (u_char *)&n->u.prefix; + u_char *pp = (u_char *)&p->u.prefix; + + /* If n's prefix is longer than p's one return 0. */ + if (n->prefixlen > p->prefixlen) + return 0; + + offset = n->prefixlen / PNBBY; + shift = n->prefixlen % PNBBY; + + if (shift) + if (maskbit[shift] & (np[offset] ^ pp[offset])) + return 0; + + while (offset--) + if (np[offset] != pp[offset]) + return 0; + return 1; +} + +/* Copy prefix from src to dest. */ +void +prefix_copy (struct prefix *dest, struct prefix *src) +{ + dest->family = src->family; + dest->prefixlen = src->prefixlen; + + if (src->family == AF_INET) + dest->u.prefix4 = src->u.prefix4; +#ifdef HAVE_IPV6 + else if (src->family == AF_INET6) + dest->u.prefix6 = src->u.prefix6; +#endif /* HAVE_IPV6 */ + else if (src->family == AF_UNSPEC) + { + dest->u.lp.id = src->u.lp.id; + dest->u.lp.adv_router = src->u.lp.adv_router; + } + else + { + zlog (NULL, LOG_INFO, "prefix_copy(): Unknown address family %d", + src->family); + assert (0); + } +} + +/* If both prefix structure is same then return 1 else return 0. */ +int +prefix_same (struct prefix *p1, struct prefix *p2) +{ + if (p1->family == p2->family && p1->prefixlen == p2->prefixlen) + { + if (p1->family == AF_INET) + if (IPV4_ADDR_SAME (&p1->u.prefix, &p2->u.prefix)) + return 1; +#ifdef HAVE_IPV6 + if (p1->family == AF_INET6 ) + if (IPV6_ADDR_SAME (&p1->u.prefix, &p2->u.prefix)) + return 1; +#endif /* HAVE_IPV6 */ + } + return 0; +} + +/* When both prefix structure is not same, but will be same after + applying mask, return 0. otherwise, return 1 */ +int +prefix_cmp (struct prefix *p1, struct prefix *p2) +{ + int offset; + int shift; + + /* Set both prefix's head pointer. */ + u_char *pp1 = (u_char *)&p1->u.prefix; + u_char *pp2 = (u_char *)&p2->u.prefix; + + if (p1->family != p2->family || p1->prefixlen != p2->prefixlen) + return 1; + + offset = p1->prefixlen / 8; + shift = p1->prefixlen % 8; + + if (shift) + if (maskbit[shift] & (pp1[offset] ^ pp2[offset])) + return 1; + + while (offset--) + if (pp1[offset] != pp2[offset]) + return 1; + + return 0; +} + +/* Return prefix family type string. */ +char * +prefix_family_str (struct prefix *p) +{ + if (p->family == AF_INET) + return "inet"; +#ifdef HAVE_IPV6 + if (p->family == AF_INET6) + return "inet6"; +#endif /* HAVE_IPV6 */ + return "unspec"; +} + +/* Allocate new prefix_ipv4 structure. */ +struct prefix_ipv4 * +prefix_ipv4_new () +{ + struct prefix_ipv4 *p; + + p = XCALLOC (MTYPE_PREFIX_IPV4, sizeof *p); + p->family = AF_INET; + return p; +} + +/* Free prefix_ipv4 structure. */ +void +prefix_ipv4_free (struct prefix_ipv4 *p) +{ + XFREE (MTYPE_PREFIX_IPV4, p); +} + +/* When string format is invalid return 0. */ +int +str2prefix_ipv4 (char *str, struct prefix_ipv4 *p) +{ + int ret; + int plen; + char *pnt; + char *cp; + + /* Find slash inside string. */ + pnt = strchr (str, '/'); + + /* String doesn't contail slash. */ + if (pnt == NULL) + { + /* Convert string to prefix. */ + ret = inet_aton (str, &p->prefix); + if (ret == 0) + return 0; + + /* If address doesn't contain slash we assume it host address. */ + p->family = AF_INET; + p->prefixlen = IPV4_MAX_BITLEN; + + return ret; + } + else + { + cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1); + strncpy (cp, str, pnt - str); + *(cp + (pnt - str)) = '\0'; + ret = inet_aton (cp, &p->prefix); + XFREE (MTYPE_TMP, cp); + + /* Get prefix length. */ + plen = (u_char) atoi (++pnt); + if (plen > 32) + return 0; + + p->family = AF_INET; + p->prefixlen = plen; + } + + return ret; +} + +/* Convert masklen into IP address's netmask. */ +void +masklen2ip (int masklen, struct in_addr *netmask) +{ + u_char *pnt; + int bit; + int offset; + + memset (netmask, 0, sizeof (struct in_addr)); + pnt = (unsigned char *) netmask; + + offset = masklen / 8; + bit = masklen % 8; + + while (offset--) + *pnt++ = 0xff; + + if (bit) + *pnt = maskbit[bit]; +} + +/* Convert IP address's netmask into integer. We assume netmask is + sequential one. Argument netmask should be network byte order. */ +u_char +ip_masklen (struct in_addr netmask) +{ + u_char len; + u_char *pnt; + u_char *end; + u_char val; + + len = 0; + pnt = (u_char *) &netmask; + end = pnt + 4; + + while ((*pnt == 0xff) && pnt < end) + { + len+= 8; + pnt++; + } + + if (pnt < end) + { + val = *pnt; + while (val) + { + len++; + val <<= 1; + } + } + return len; +} + +/* Apply mask to IPv4 prefix. */ +void +apply_mask_ipv4 (struct prefix_ipv4 *p) +{ + u_char *pnt; + int index; + int offset; + + index = p->prefixlen / 8; + + if (index < 4) + { + pnt = (u_char *) &p->prefix; + offset = p->prefixlen % 8; + + pnt[index] &= maskbit[offset]; + index++; + + while (index < 4) + pnt[index++] = 0; + } +} + +/* If prefix is 0.0.0.0/0 then return 1 else return 0. */ +int +prefix_ipv4_any (struct prefix_ipv4 *p) +{ + return (p->prefix.s_addr == 0 && p->prefixlen == 0); +} + +#ifdef HAVE_IPV6 + +/* Allocate a new ip version 6 route */ +struct prefix_ipv6 * +prefix_ipv6_new () +{ + struct prefix_ipv6 *p; + + p = XCALLOC (MTYPE_PREFIX_IPV6, sizeof (struct prefix_ipv6)); + p->family = AF_INET6; + return p; +} + +/* Free prefix for IPv6. */ +void +prefix_ipv6_free (struct prefix_ipv6 *p) +{ + XFREE (MTYPE_PREFIX_IPV6, p); +} + +/* If given string is valid return pin6 else return NULL */ +int +str2prefix_ipv6 (char *str, struct prefix_ipv6 *p) +{ + char *pnt; + char *cp; + int ret; + + pnt = strchr (str, '/'); + + /* If string doesn't contain `/' treat it as host route. */ + if (pnt == NULL) + { + ret = inet_pton (AF_INET6, str, &p->prefix); + if (ret != 1) + return 0; + p->prefixlen = IPV6_MAX_BITLEN; + } + else + { + int plen; + + cp = XMALLOC (0, (pnt - str) + 1); + strncpy (cp, str, pnt - str); + *(cp + (pnt - str)) = '\0'; + ret = inet_pton (AF_INET6, cp, &p->prefix); + free (cp); + if (ret != 1) + return 0; + plen = (u_char) atoi (++pnt); + if (plen > 128) + return 0; + p->prefixlen = plen; + } + p->family = AF_INET6; + + return ret; +} + +/* Convert struct in6_addr netmask into integer. */ +int +ip6_masklen (struct in6_addr netmask) +{ + int len = 0; + unsigned char val; + unsigned char *pnt; + + pnt = (unsigned char *) & netmask; + + while ((*pnt == 0xff) && len < 128) + { + len += 8; + pnt++; + } + + if (len < 128) + { + val = *pnt; + while (val) + { + len++; + val <<= 1; + } + } + return len; +} + +void +masklen2ip6 (int masklen, struct in6_addr *netmask) +{ + unsigned char *pnt; + int bit; + int offset; + + memset (netmask, 0, sizeof (struct in6_addr)); + pnt = (unsigned char *) netmask; + + offset = masklen / 8; + bit = masklen % 8; + + while (offset--) + *pnt++ = 0xff; + + if (bit) + *pnt = maskbit[bit]; +} + +void +apply_mask_ipv6 (struct prefix_ipv6 *p) +{ + u_char *pnt; + int index; + int offset; + + index = p->prefixlen / 8; + + if (index < 16) + { + pnt = (u_char *) &p->prefix; + offset = p->prefixlen % 8; + + pnt[index] &= maskbit[offset]; + index++; + + while (index < 16) + pnt[index++] = 0; + } +} + +void +str2in6_addr (char *str, struct in6_addr *addr) +{ + int i; + unsigned int x; + + /* %x must point to unsinged int */ + for (i = 0; i < 16; i++) + { + sscanf (str + (i * 2), "%02x", &x); + addr->s6_addr[i] = x & 0xff; + } +} +#endif /* HAVE_IPV6 */ + +void +apply_mask (struct prefix *p) +{ + switch (p->family) + { + case AF_INET: + apply_mask_ipv4 ((struct prefix_ipv4 *)p); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + apply_mask_ipv6 ((struct prefix_ipv6 *)p); + break; +#endif /* HAVE_IPV6 */ + default: + break; + } + return; +} + +/* Utility function of convert between struct prefix <=> union sockunion */ +struct prefix * +sockunion2prefix (union sockunion *dest, + union sockunion *mask) +{ + if (dest->sa.sa_family == AF_INET) + { + struct prefix_ipv4 *p; + + p = prefix_ipv4_new (); + p->family = AF_INET; + p->prefix = dest->sin.sin_addr; + p->prefixlen = ip_masklen (mask->sin.sin_addr); + return (struct prefix *) p; + } +#ifdef HAVE_IPV6 + if (dest->sa.sa_family == AF_INET6) + { + struct prefix_ipv6 *p; + + p = prefix_ipv6_new (); + p->family = AF_INET6; + p->prefixlen = ip6_masklen (mask->sin6.sin6_addr); + memcpy (&p->prefix, &dest->sin6.sin6_addr, sizeof (struct in6_addr)); + return (struct prefix *) p; + } +#endif /* HAVE_IPV6 */ + return NULL; +} + +/* Utility function of convert between struct prefix <=> union sockunion */ +struct prefix * +sockunion2hostprefix (union sockunion *su) +{ + if (su->sa.sa_family == AF_INET) + { + struct prefix_ipv4 *p; + + p = prefix_ipv4_new (); + p->family = AF_INET; + p->prefix = su->sin.sin_addr; + p->prefixlen = IPV4_MAX_BITLEN; + return (struct prefix *) p; + } +#ifdef HAVE_IPV6 + if (su->sa.sa_family == AF_INET6) + { + struct prefix_ipv6 *p; + + p = prefix_ipv6_new (); + p->family = AF_INET6; + p->prefixlen = IPV6_MAX_BITLEN; + memcpy (&p->prefix, &su->sin6.sin6_addr, sizeof (struct in6_addr)); + return (struct prefix *) p; + } +#endif /* HAVE_IPV6 */ + return NULL; +} + +int +prefix_blen (struct prefix *p) +{ + switch (p->family) + { + case AF_INET: + return IPV4_MAX_BYTELEN; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + return IPV6_MAX_BYTELEN; + break; +#endif /* HAVE_IPV6 */ + } + return 0; +} + +/* Generic function for conversion string to struct prefix. */ +int +str2prefix (char *str, struct prefix *p) +{ + int ret; + + /* First we try to convert string to struct prefix_ipv4. */ + ret = str2prefix_ipv4 (str, (struct prefix_ipv4 *) p); + if (ret) + return ret; + +#ifdef HAVE_IPV6 + /* Next we try to convert string to struct prefix_ipv6. */ + ret = str2prefix_ipv6 (str, (struct prefix_ipv6 *) p); + if (ret) + return ret; +#endif /* HAVE_IPV6 */ + + return 0; +} + +int +prefix2str (struct prefix *p, char *str, int size) +{ + char buf[BUFSIZ]; + + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ); + snprintf (str, size, "%s/%d", buf, p->prefixlen); + return 0; +} + +struct prefix * +prefix_new () +{ + struct prefix *p; + + p = XCALLOC (MTYPE_PREFIX, sizeof *p); + return p; +} + +/* Free prefix structure. */ +void +prefix_free (struct prefix *p) +{ + XFREE (MTYPE_PREFIX, p); +} + +/* Utility function. Check the string only contains digit + character. */ +int +all_digit (char *str) +{ + for (; *str != '\0'; str++) + if (!isdigit ((int) *str)) + return 0; + return 1; +} + +/* Utility function to convert ipv4 prefixes to Classful prefixes */ +void apply_classful_mask_ipv4 (struct prefix_ipv4 *p) +{ + + u_int32_t destination; + + destination = ntohl (p->prefix.s_addr); + + if (p->prefixlen == 32); + /* do nothing for host routes */ + else if (IN_CLASSC (destination)) + { + p->prefixlen=24; + apply_mask_ipv4(p); + } + else if (IN_CLASSB(destination)) + { + p->prefixlen=16; + apply_mask_ipv4(p); + } + else + { + p->prefixlen=8; + apply_mask_ipv4(p); + } +} + +/* Utility function to convert ipv4 netmask to prefixes + ex.) "1.1.0.0" "255.255.0.0" => "1.1.0.0/16" + ex.) "1.0.0.0" NULL => "1.0.0.0/8" */ +int +netmask_str2prefix_str (char *net_str, char *mask_str, char *prefix_str) +{ + struct in_addr network; + struct in_addr mask; + u_char prefixlen; + u_int32_t destination; + int ret; + + ret = inet_aton (net_str, &network); + if (! ret) + return 0; + + if (mask_str) + { + ret = inet_aton (mask_str, &mask); + if (! ret) + return 0; + + prefixlen = ip_masklen (mask); + } + else + { + destination = ntohl (network.s_addr); + + if (network.s_addr == 0) + prefixlen = 0; + else if (IN_CLASSC (destination)) + prefixlen = 24; + else if (IN_CLASSB (destination)) + prefixlen = 16; + else if (IN_CLASSA (destination)) + prefixlen = 8; + else + return 0; + } + + sprintf (prefix_str, "%s/%d", net_str, prefixlen); + + return 1; +} + diff --git a/lib/prefix.h b/lib/prefix.h new file mode 100644 index 00000000..7d7cde68 --- /dev/null +++ b/lib/prefix.h @@ -0,0 +1,161 @@ +/* + * Prefix structure. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_PREFIX_H +#define _ZEBRA_PREFIX_H + +/* IPv4 and IPv6 unified prefix structure. */ +struct prefix +{ + u_char family; + u_char prefixlen; + union + { + u_char prefix; + struct in_addr prefix4; +#ifdef HAVE_IPV6 + struct in6_addr prefix6; +#endif /* HAVE_IPV6 */ + struct + { + struct in_addr id; + struct in_addr adv_router; + } lp; + u_char val[8]; + } u __attribute__ ((aligned (8))); +}; + +/* IPv4 prefix structure. */ +struct prefix_ipv4 +{ + u_char family; + u_char prefixlen; + struct in_addr prefix __attribute__ ((aligned (8))); +}; + +/* IPv6 prefix structure. */ +#ifdef HAVE_IPV6 +struct prefix_ipv6 +{ + u_char family; + u_char prefixlen; + struct in6_addr prefix __attribute__ ((aligned (8))); +}; +#endif /* HAVE_IPV6 */ + +struct prefix_ls +{ + u_char family; + u_char prefixlen; + struct in_addr id __attribute__ ((aligned (8))); + struct in_addr adv_router; +}; + +/* Prefix for routing distinguisher. */ +struct prefix_rd +{ + u_char family; + u_char prefixlen; + u_char val[8] __attribute__ ((aligned (8))); +}; + +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN 16 +#endif /* INET_ADDRSTRLEN */ + +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif /* INET6_ADDRSTRLEN */ + +#ifndef INET6_BUFSIZ +#define INET6_BUFSIZ 51 +#endif /* INET6_BUFSIZ */ + +/* Max bit/byte length of IPv4 address. */ +#define IPV4_MAX_BYTELEN 4 +#define IPV4_MAX_BITLEN 32 +#define IPV4_MAX_PREFIXLEN 32 +#define IPV4_ADDR_CMP(D,S) memcmp ((D), (S), IPV4_MAX_BYTELEN) +#define IPV4_ADDR_SAME(D,S) (memcmp ((D), (S), IPV4_MAX_BYTELEN) == 0) +#define IPV4_ADDR_COPY(D,S) memcpy ((D), (S), IPV4_MAX_BYTELEN) + +#define IPV4_NET0(a) ((((u_int32_t) (a)) & 0xff000000) == 0x00000000) +#define IPV4_NET127(a) ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000) + +/* Max bit/byte length of IPv6 address. */ +#define IPV6_MAX_BYTELEN 16 +#define IPV6_MAX_BITLEN 128 +#define IPV6_MAX_PREFIXLEN 128 +#define IPV6_ADDR_CMP(D,S) memcmp ((D), (S), IPV6_MAX_BYTELEN) +#define IPV6_ADDR_SAME(D,S) (memcmp ((D), (S), IPV6_MAX_BYTELEN) == 0) +#define IPV6_ADDR_COPY(D,S) memcpy ((D), (S), IPV6_MAX_BYTELEN) + +/* Count prefix size from mask length */ +#define PSIZE(a) (((a) + 7) / (8)) + +/* Prefix's family member. */ +#define PREFIX_FAMILY(p) ((p)->family) + +/* Prototypes. */ +int afi2family (int); +int family2afi (int); + +int prefix2str (struct prefix *, char *, int); +int str2prefix (char *, struct prefix *); +struct prefix *prefix_new (); +void prefix_free (struct prefix *p); + +struct prefix_ipv4 *prefix_ipv4_new (); +void prefix_ipv4_free (); +int str2prefix_ipv4 (char *, struct prefix_ipv4 *); +void apply_mask_ipv4 (struct prefix_ipv4 *); +int prefix_blen (struct prefix *); +u_char ip_masklen (struct in_addr); +int prefix_ipv4_any (struct prefix_ipv4 *); +void masklen2ip (int, struct in_addr *); +void apply_classful_mask_ipv4 (struct prefix_ipv4 *); + +char *prefix_family_str (struct prefix *p); +struct prefix *sockunion2prefix (); +struct prefix *sockunion2hostprefix (); + +#ifdef HAVE_IPV6 +struct prefix_ipv6 *prefix_ipv6_new (); +void prefix_ipv6_free (); +struct prefix *str2routev6 (char *); +int str2prefix_ipv6 (char *str, struct prefix_ipv6 *p); +void apply_mask_ipv6 (struct prefix_ipv6 *p); +void str2in6_addr (char *str, struct in6_addr *addr); +void masklen2ip6 (int masklen, struct in6_addr *netmask); +int ip6_masklen (struct in6_addr netmask); +#endif /* HAVE_IPV6 */ + +void apply_mask (struct prefix *); +int prefix_match (struct prefix *n, struct prefix *p); +int prefix_same (struct prefix *, struct prefix *); +int prefix_cmp (struct prefix *, struct prefix *); +void prefix_copy (struct prefix *, struct prefix *); + +int all_digit (char *); +int netmask_str2prefix_str (char *, char *, char *); + +#endif /* _ZEBRA_PREFIX_H */ diff --git a/lib/print_version.c b/lib/print_version.c new file mode 100644 index 00000000..6b4064d3 --- /dev/null +++ b/lib/print_version.c @@ -0,0 +1,31 @@ +/* Print version function. + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "version.h" + +void +print_version (char *progname) +{ + printf ("%s version %s (%s)\n", progname, ZEBRA_VERSION, host_name); + printf ("Copyright 1996-2001, Kunihiro Ishiguro\n"); +} diff --git a/lib/regex-gnu.h b/lib/regex-gnu.h new file mode 100644 index 00000000..d88ab92b --- /dev/null +++ b/lib/regex-gnu.h @@ -0,0 +1,542 @@ +/* Definitions for data structures and routines for the regular + expression library, version 0.12. + Copyright (C) 1985,89,90,91,92,93,95,96,97,98 Free Software Foundation, Inc. + + This file is part of the GNU C Library. Its master source is NOT part of + the C library, however. The master source lives in /gd/gnu/lib. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _REGEX_H +#define _REGEX_H 1 + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +/* POSIX says that <sys/types.h> must be included (by the caller) before + <regex.h>. */ + +#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && defined VMS +/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it + should be there. */ +# include <stddef.h> +#endif + +/* The following two types have to be signed and unsigned integer type + wide enough to hold a value of a pointer. For most ANSI compilers + ptrdiff_t and size_t should be likely OK. Still size of these two + types is 2 for Microsoft C. Ugh... */ +typedef long int s_reg_t; +typedef unsigned long int active_reg_t; + +/* The following bits are used to determine the regexp syntax we + recognize. The set/not-set meanings are chosen so that Emacs syntax + remains the value 0. The bits are given in alphabetical order, and + the definitions shifted by one from the previous bit; thus, when we + add or remove a bit, only one other definition need change. */ +typedef unsigned long int reg_syntax_t; + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1) + +/* If this bit is not set, then + and ? are operators, and \+ and \? are + literals. + If set, then \+ and \? are operators and + and ? are literals. */ +#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) + +/* If this bit is set, then character classes are supported. They are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) + +/* If this bit is set, then ^ and $ are always anchors (outside bracket + expressions, of course). + If this bit is not set, then it depends: + ^ is an anchor if it is at the beginning of a regular + expression or after an open-group or an alternation operator; + $ is an anchor if it is at the end of a regular expression, or + before a close-group or an alternation operator. + + This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because + POSIX draft 11.2 says that * etc. in leading positions is undefined. + We already implemented a previous draft which made those constructs + invalid, though, so we haven't changed the code back. */ +#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) + +/* If this bit is set, then special characters are always special + regardless of where they are in the pattern. + If this bit is not set, then special characters are special only in + some contexts; otherwise they are ordinary. Specifically, + * + ? and intervals are only special when not after the beginning, + open-group, or alternation operator. */ +#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) + +/* If this bit is set, then *, +, ?, and { cannot be first in an re or + immediately after an alternation or begin-group operator. */ +#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) + +/* If this bit is set, then . matches newline. + If not set, then it doesn't. */ +#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) + +/* If this bit is set, then . doesn't match NUL. + If not set, then it does. */ +#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) + +/* If this bit is set, nonmatching lists [^...] do not match newline. + If not set, they do. */ +#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) + +/* If this bit is set, either \{...\} or {...} defines an + interval, depending on RE_NO_BK_BRACES. + If not set, \{, \}, {, and } are literals. */ +#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) + +/* If this bit is set, +, ? and | aren't recognized as operators. + If not set, they are. */ +#define RE_LIMITED_OPS (RE_INTERVALS << 1) + +/* If this bit is set, newline is an alternation operator. + If not set, newline is literal. */ +#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) + +/* If this bit is set, then `{...}' defines an interval, and \{ and \} + are literals. + If not set, then `\{...\}' defines an interval. */ +#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) + +/* If this bit is set, (...) defines a group, and \( and \) are literals. + If not set, \(...\) defines a group, and ( and ) are literals. */ +#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) + +/* If this bit is set, then \<digit> matches <digit>. + If not set, then \<digit> is a back-reference. */ +#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) + +/* If this bit is set, then | is an alternation operator, and \| is literal. + If not set, then \| is an alternation operator, and | is literal. */ +#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) + +/* If this bit is set, then an ending range point collating higher + than the starting range point, as in [z-a], is invalid. + If not set, then when ending range point collates higher than the + starting range point, the range is ignored. */ +#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) + +/* If this bit is set, then an unmatched ) is ordinary. + If not set, then an unmatched ) is invalid. */ +#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) + +/* If this bit is set, succeed as soon as we match the whole pattern, + without further backtracking. */ +#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) + +/* If this bit is set, do not process the GNU regex operators. + If not set, then the GNU regex operators are recognized. */ +#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1) + +/* If this bit is set, turn on internal regex debugging. + If not set, and debugging was on, turn it off. + This only works if regex.c is compiled -DDEBUG. + We define this bit always, so that all that's needed to turn on + debugging is to recompile regex.c; the calling code can always have + this bit set, and it won't affect anything in the normal case. */ +#define RE_DEBUG (RE_NO_GNU_OPS << 1) + +/* This global variable defines the particular regexp syntax to use (for + some interfaces). When a regexp is compiled, the syntax used is + stored in the pattern buffer, so changing this does not affect + already-compiled regexps. */ +extern reg_syntax_t re_syntax_options; + +/* Define combinations of the above bits for the standard possibilities. + (The [[[ comments delimit what gets put into the Texinfo file, so + don't delete them!) */ +/* [[[begin syntaxes]]] */ +#define RE_SYNTAX_EMACS 0 + +#define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \ + | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GNU_AWK \ + ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \ + & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS)) + +#define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ + | RE_INTERVALS | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +#define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +#define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +#define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +#define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +#define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +#define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS + replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */ +#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) +/* [[[end syntaxes]]] */ + +/* Maximum number of duplicates an interval can allow. Some systems + (erroneously) define this in other header files, but we want our + value, so remove any previous define. */ +#ifdef RE_DUP_MAX +# undef RE_DUP_MAX +#endif +/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */ +#define RE_DUP_MAX (0x7fff) + + +/* POSIX `cflags' bits (i.e., information for `regcomp'). */ + +/* If this bit is set, then use extended regular expression syntax. + If not set, then use basic regular expression syntax. */ +#define REG_EXTENDED 1 + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define REG_ICASE (REG_EXTENDED << 1) + +/* If this bit is set, then anchors do not match at newline + characters in the string. + If not set, then anchors do match at newlines. */ +#define REG_NEWLINE (REG_ICASE << 1) + +/* If this bit is set, then report only success or fail in regexec. + If not set, then returns differ between not matching and errors. */ +#define REG_NOSUB (REG_NEWLINE << 1) + + +/* POSIX `eflags' bits (i.e., information for regexec). */ + +/* If this bit is set, then the beginning-of-line operator doesn't match + the beginning of the string (presumably because it's not the + beginning of a line). + If not set, then the beginning-of-line operator does match the + beginning of the string. */ +#define REG_NOTBOL 1 + +/* Like REG_NOTBOL, except for the end-of-line. */ +#define REG_NOTEOL (1 << 1) + + +/* If any error codes are removed, changed, or added, update the + `re_error_msg' table in regex.c. */ +typedef enum +{ +#ifdef _XOPEN_SOURCE + REG_ENOSYS = -1, /* This will never happen for this implementation. */ +#endif + + REG_NOERROR = 0, /* Success. */ + REG_NOMATCH, /* Didn't find a match (for regexec). */ + + /* POSIX regcomp return error codes. (In the order listed in the + standard.) */ + REG_BADPAT, /* Invalid pattern. */ + REG_ECOLLATE, /* Not implemented. */ + REG_ECTYPE, /* Invalid character class name. */ + REG_EESCAPE, /* Trailing backslash. */ + REG_ESUBREG, /* Invalid back reference. */ + REG_EBRACK, /* Unmatched left bracket. */ + REG_EPAREN, /* Parenthesis imbalance. */ + REG_EBRACE, /* Unmatched \{. */ + REG_BADBR, /* Invalid contents of \{\}. */ + REG_ERANGE, /* Invalid range end. */ + REG_ESPACE, /* Ran out of memory. */ + REG_BADRPT, /* No preceding re for repetition op. */ + + /* Error codes we've added. */ + REG_EEND, /* Premature end. */ + REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ + REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ +} reg_errcode_t; + +/* This data structure represents a compiled pattern. Before calling + the pattern compiler, the fields `buffer', `allocated', `fastmap', + `translate', and `no_sub' can be set. After the pattern has been + compiled, the `re_nsub' field is available. All other fields are + private to the regex routines. */ + +#ifndef RE_TRANSLATE_TYPE +# define RE_TRANSLATE_TYPE char * +#endif + +struct re_pattern_buffer +{ +/* [[[begin pattern_buffer]]] */ + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are + sometimes used as array indexes. */ + unsigned char *buffer; + + /* Number of bytes to which `buffer' points. */ + unsigned long int allocated; + + /* Number of bytes actually used in `buffer'. */ + unsigned long int used; + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t syntax; + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses + the fastmap, if there is one, to skip over impossible + starting points for matches. */ + char *fastmap; + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation + is applied to a pattern when it is compiled and to a string + when it is matched. */ + RE_TRANSLATE_TYPE translate; + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see + whether or not we should use the fastmap, so we don't set + this absolutely perfectly; see `re_compile_fastmap' (the + `duplicate' case). */ + unsigned can_be_null : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#define REGS_UNALLOCATED 0 +#define REGS_REALLOCATE 1 +#define REGS_FIXED 2 + unsigned regs_allocated : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned fastmap_accurate : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned no_sub : 1; + + /* If set, a beginning-of-line anchor doesn't match at the + beginning of the string. */ + unsigned not_bol : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned not_eol : 1; + + /* If true, an anchor at a newline matches. */ + unsigned newline_anchor : 1; + +/* [[[end pattern_buffer]]] */ +}; + +typedef struct re_pattern_buffer regex_t; + +/* Type for byte offsets within the string. POSIX mandates this. */ +typedef int regoff_t; + + +/* This is the structure we store register match data in. See + regex.texinfo for a full description of what registers match. */ +struct re_registers +{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +}; + + +/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, + `re_match_2' returns information about at least this many registers + the first time a `regs' structure is passed. */ +#ifndef RE_NREGS +# define RE_NREGS 30 +#endif + + +/* POSIX specification for registers. Aside from the different names than + `re_registers', POSIX uses an array of structures, instead of a + structure of arrays. */ +typedef struct +{ + regoff_t rm_so; /* Byte offset from string's start to substring's start. */ + regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ +} regmatch_t; + +/* Declarations for routines. */ + +/* To avoid duplicating every routine declaration -- once with a + prototype (if we are ANSI), and once without (if we aren't) -- we + use the following macro to declare argument types. This + unfortunately clutters up the declarations a bit, but I think it's + worth it. */ + +#if __STDC__ + +# define _RE_ARGS(args) args + +#else /* not __STDC__ */ + +# define _RE_ARGS(args) () + +#endif /* not __STDC__ */ + +/* Sets the current default syntax to SYNTAX, and return the old syntax. + You can also simply assign to the `re_syntax_options' variable. */ +extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax)); + +/* Compile the regular expression PATTERN, with length LENGTH + and syntax given by the global `re_syntax_options', into the buffer + BUFFER. Return NULL if successful, and an error string if not. */ +extern const char *re_compile_pattern + _RE_ARGS ((const char *pattern, size_t length, + struct re_pattern_buffer *buffer)); + + +/* Compile a fastmap for the compiled pattern in BUFFER; used to + accelerate searches. Return 0 if successful and -2 if was an + internal error. */ +extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer)); + + +/* Search in the string STRING (with length LENGTH) for the pattern + compiled into BUFFER. Start searching at position START, for RANGE + characters. Return the starting position of the match, -1 for no + match, or -2 for an internal error. Also return register + information in REGS (if REGS and BUFFER->no_sub are nonzero). */ +extern int re_search + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, int range, struct re_registers *regs)); + + +/* Like `re_search', but search in the concatenation of STRING1 and + STRING2. Also, stop searching at index START + STOP. */ +extern int re_search_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, int range, struct re_registers *regs, int stop)); + + +/* Like `re_search', but return how many characters in STRING the regexp + in BUFFER matched, starting at position START. */ +extern int re_match + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, struct re_registers *regs)); + + +/* Relates to `re_match' as `re_search_2' relates to `re_search'. */ +extern int re_match_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, struct re_registers *regs, int stop)); + + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using BUFFER and REGS will use this memory + for recording register information. STARTS and ENDS must be + allocated with malloc, and must each be at least `NUM_REGS * sizeof + (regoff_t)' bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ +extern void re_set_registers + _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, + unsigned num_regs, regoff_t *starts, regoff_t *ends)); + +#if defined _REGEX_RE_COMP || defined _LIBC +# ifndef _CRAY +/* 4.2 bsd compatibility. */ +extern char *re_comp _RE_ARGS ((const char *)); +extern int re_exec _RE_ARGS ((const char *)); +# endif +#endif + +/* POSIX compatibility. */ +extern int regcomp _RE_ARGS ((regex_t *__preg, const char *__pattern, + int __cflags)); + +extern int regexec _RE_ARGS ((const regex_t *__preg, + const char *__string, size_t __nmatch, + regmatch_t __pmatch[], int __eflags)); + +extern size_t regerror _RE_ARGS ((int __errcode, const regex_t *__preg, + char *__errbuf, size_t __errbuf_size)); + +extern void regfree _RE_ARGS ((regex_t *__preg)); + + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* regex.h */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/lib/regex.c b/lib/regex.c new file mode 100644 index 00000000..8c7acd54 --- /dev/null +++ b/lib/regex.c @@ -0,0 +1,5891 @@ +/* Extended regular expression matching and search library, + version 0.12. + (Implements POSIX draft P1003.2/D11.2, except for some of the + internationalization features.) + Copyright (C) 1993, 94, 95, 96, 97, 98, 99 Free Software Foundation, Inc. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* AIX requires this to be the first thing in the file. */ +#if defined _AIX && !defined REGEX_MALLOC + #pragma alloca +#endif + +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifndef PARAMS +# if defined __GNUC__ || (defined __STDC__ && __STDC__) +# define PARAMS(args) args +# else +# define PARAMS(args) () +# endif /* GCC. */ +#endif /* Not PARAMS. */ + +#if defined STDC_HEADERS && !defined emacs +# include <stddef.h> +#else +/* We need this for `regex.h', and perhaps for the Emacs include files. */ +# include <sys/types.h> +#endif + +#define WIDE_CHAR_SUPPORT (HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_BTOWC) + +/* For platform which support the ISO C amendement 1 functionality we + support user defined character classes. */ +#if defined _LIBC || WIDE_CHAR_SUPPORT +/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>. */ +# include <wchar.h> +# include <wctype.h> +#endif + +#ifdef _LIBC +/* We have to keep the namespace clean. */ +# define regfree(preg) __regfree (preg) +# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef) +# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags) +# define regerror(errcode, preg, errbuf, errbuf_size) \ + __regerror(errcode, preg, errbuf, errbuf_size) +# define re_set_registers(bu, re, nu, st, en) \ + __re_set_registers (bu, re, nu, st, en) +# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \ + __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) +# define re_match(bufp, string, size, pos, regs) \ + __re_match (bufp, string, size, pos, regs) +# define re_search(bufp, string, size, startpos, range, regs) \ + __re_search (bufp, string, size, startpos, range, regs) +# define re_compile_pattern(pattern, length, bufp) \ + __re_compile_pattern (pattern, length, bufp) +# define re_set_syntax(syntax) __re_set_syntax (syntax) +# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \ + __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop) +# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp) + +#define btowc __btowc +#endif + +/* This is for other GNU distributions with internationalized messages. */ +#if HAVE_LIBINTL_H || defined _LIBC +# include <libintl.h> +#else +# define gettext(msgid) (msgid) +#endif + +#ifndef gettext_noop +/* This define is so xgettext can find the internationalizable + strings. */ +# define gettext_noop(String) String +#endif + +/* The `emacs' switch turns on certain matching commands + that make sense only in Emacs. */ +#ifdef emacs + +# include "lisp.h" +# include "buffer.h" +# include "syntax.h" + +#else /* not emacs */ + +/* If we are not linking with Emacs proper, + we can't use the relocating allocator + even if config.h says that we can. */ +# undef REL_ALLOC + +# if defined STDC_HEADERS || defined _LIBC +# include <stdlib.h> +# else +char *malloc (); +char *realloc (); +# endif + +/* When used in Emacs's lib-src, we need to get bzero and bcopy somehow. + If nothing else has been done, use the method below. */ +# ifdef INHIBIT_STRING_HEADER +# if !(defined HAVE_BZERO && defined HAVE_BCOPY) +# if !defined bzero && !defined bcopy +# undef INHIBIT_STRING_HEADER +# endif +# endif +# endif + +/* This is the normal way of making sure we have a bcopy and a bzero. + This is used in most programs--a few other programs avoid this + by defining INHIBIT_STRING_HEADER. */ +# ifndef INHIBIT_STRING_HEADER +# if defined HAVE_STRING_H || defined STDC_HEADERS || defined _LIBC +# include <string.h> +# ifndef bzero +# ifndef _LIBC +# define bzero(s, n) (memset (s, '\0', n), (s)) +# else +# define bzero(s, n) __bzero (s, n) +# endif +# endif +# else +# include <strings.h> +# ifndef memcmp +# define memcmp(s1, s2, n) bcmp (s1, s2, n) +# endif +# ifndef memcpy +# define memcpy(d, s, n) (bcopy (s, d, n), (d)) +# endif +# endif +# endif + +/* Define the syntax stuff for \<, \>, etc. */ + +/* This must be nonzero for the wordchar and notwordchar pattern + commands in re_match_2. */ +# ifndef Sword +# define Sword 1 +# endif + +# ifdef SWITCH_ENUM_BUG +# define SWITCH_ENUM_CAST(x) ((int)(x)) +# else +# define SWITCH_ENUM_CAST(x) (x) +# endif + +/* How many characters in the character set. */ +# define CHAR_SET_SIZE 256 + +# ifdef SYNTAX_TABLE + +extern char *re_syntax_table; + +# else /* not SYNTAX_TABLE */ + +static char re_syntax_table[CHAR_SET_SIZE]; + +static void +init_syntax_once () +{ + register int c; + static int done; + + if (done) + return; + + bzero (re_syntax_table, sizeof re_syntax_table); + + for (c = 'a'; c <= 'z'; c++) + re_syntax_table[c] = Sword; + + for (c = 'A'; c <= 'Z'; c++) + re_syntax_table[c] = Sword; + + for (c = '0'; c <= '9'; c++) + re_syntax_table[c] = Sword; + + re_syntax_table['_'] = Sword; + + done = 1; +} + +# endif /* not SYNTAX_TABLE */ + +# define SYNTAX(c) re_syntax_table[c] + +#endif /* not emacs */ + +/* Get the interface, including the syntax bits. */ +#include <regex-gnu.h> + +/* isalpha etc. are used for the character classes. */ +#include <ctype.h> + +/* Jim Meyering writes: + + "... Some ctype macros are valid only for character codes that + isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when + using /bin/cc or gcc but without giving an ansi option). So, all + ctype uses should be through macros like ISPRINT... If + STDC_HEADERS is defined, then autoconf has verified that the ctype + macros don't need to be guarded with references to isascii. ... + Defining isascii to 1 should let any compiler worth its salt + eliminate the && through constant folding." + Solaris defines some of these symbols so we must undefine them first. */ + +#undef ISASCII +#if defined STDC_HEADERS || (!defined isascii && !defined HAVE_ISASCII) +# define ISASCII(c) 1 +#else +# define ISASCII(c) isascii(c) +#endif + +#ifdef isblank +# define ISBLANK(c) (ISASCII (c) && isblank (c)) +#else +# define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#endif +#ifdef isgraph +# define ISGRAPH(c) (ISASCII (c) && isgraph (c)) +#else +# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c)) +#endif + +#undef ISPRINT +#define ISPRINT(c) (ISASCII (c) && isprint (c)) +#define ISDIGIT(c) (ISASCII (c) && isdigit (c)) +#define ISALNUM(c) (ISASCII (c) && isalnum (c)) +#define ISALPHA(c) (ISASCII (c) && isalpha (c)) +#define ISCNTRL(c) (ISASCII (c) && iscntrl (c)) +#define ISLOWER(c) (ISASCII (c) && islower (c)) +#define ISPUNCT(c) (ISASCII (c) && ispunct (c)) +#define ISSPACE(c) (ISASCII (c) && isspace (c)) +#define ISUPPER(c) (ISASCII (c) && isupper (c)) +#define ISXDIGIT(c) (ISASCII (c) && isxdigit (c)) + +#ifdef _tolower +# define TOLOWER(c) _tolower(c) +#else +# define TOLOWER(c) tolower(c) +#endif + +#ifndef NULL +# define NULL (void *)0 +#endif + +/* We remove any previous definition of `SIGN_EXTEND_CHAR', + since ours (we hope) works properly with all combinations of + machines, compilers, `char' and `unsigned char' argument types. + (Per Bothner suggested the basic approach.) */ +#undef SIGN_EXTEND_CHAR +#if __STDC__ +# define SIGN_EXTEND_CHAR(c) ((signed char) (c)) +#else /* not __STDC__ */ +/* As in Harbison and Steele. */ +# define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128) +#endif + +/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we + use `alloca' instead of `malloc'. This is because using malloc in + re_search* or re_match* could cause memory leaks when C-g is used in + Emacs; also, malloc is slower and causes storage fragmentation. On + the other hand, malloc is more portable, and easier to debug. + + Because we sometimes use alloca, some routines have to be macros, + not functions -- `alloca'-allocated space disappears at the end of the + function it is called in. */ + +#ifdef REGEX_MALLOC + +# define REGEX_ALLOCATE malloc +# define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize) +# define REGEX_FREE free + +#else /* not REGEX_MALLOC */ + +/* Emacs already defines alloca, sometimes. */ +# ifndef alloca + +/* Make alloca work the best possible way. */ +# ifdef __GNUC__ +# define alloca __builtin_alloca +# else /* not __GNUC__ */ +# if HAVE_ALLOCA_H +# include <alloca.h> +# endif /* HAVE_ALLOCA_H */ +# endif /* not __GNUC__ */ + +# endif /* not alloca */ + +# define REGEX_ALLOCATE alloca + +/* Assumes a `char *destination' variable. */ +# define REGEX_REALLOCATE(source, osize, nsize) \ + (destination = (char *) alloca (nsize), \ + memcpy (destination, source, osize)) + +/* No need to do anything to free, after alloca. */ +# define REGEX_FREE(arg) ((void)0) /* Do nothing! But inhibit gcc warning. */ + +#endif /* not REGEX_MALLOC */ + +/* Define how to allocate the failure stack. */ + +#if defined REL_ALLOC && defined REGEX_MALLOC + +# define REGEX_ALLOCATE_STACK(size) \ + r_alloc (&failure_stack_ptr, (size)) +# define REGEX_REALLOCATE_STACK(source, osize, nsize) \ + r_re_alloc (&failure_stack_ptr, (nsize)) +# define REGEX_FREE_STACK(ptr) \ + r_alloc_free (&failure_stack_ptr) + +#else /* not using relocating allocator */ + +# ifdef REGEX_MALLOC + +# define REGEX_ALLOCATE_STACK malloc +# define REGEX_REALLOCATE_STACK(source, osize, nsize) realloc (source, nsize) +# define REGEX_FREE_STACK free + +# else /* not REGEX_MALLOC */ + +# define REGEX_ALLOCATE_STACK alloca + +# define REGEX_REALLOCATE_STACK(source, osize, nsize) \ + REGEX_REALLOCATE (source, osize, nsize) +/* No need to explicitly free anything. */ +# define REGEX_FREE_STACK(arg) + +# endif /* not REGEX_MALLOC */ +#endif /* not using relocating allocator */ + + +/* True if `size1' is non-NULL and PTR is pointing anywhere inside + `string1' or just past its end. This works if PTR is NULL, which is + a good thing. */ +#define FIRST_STRING_P(ptr) \ + (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) + +/* (Re)Allocate N items of type T using malloc, or fail. */ +#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t))) +#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t))) +#define RETALLOC_IF(addr, n, t) \ + if (addr) RETALLOC((addr), (n), t); else (addr) = TALLOC ((n), t) +#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t))) + +#define BYTEWIDTH 8 /* In bits. */ + +#define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) + +#undef MAX +#undef MIN +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +typedef char boolean; +#define false 0 +#define true 1 + +static int re_match_2_internal PARAMS ((struct re_pattern_buffer *bufp, + const char *string1, int size1, + const char *string2, int size2, + int pos, + struct re_registers *regs, + int stop)); + +/* These are the command codes that appear in compiled regular + expressions. Some opcodes are followed by argument bytes. A + command code can specify any interpretation whatsoever for its + arguments. Zero bytes may appear in the compiled regular expression. */ + +typedef enum +{ + no_op = 0, + + /* Succeed right away--no more backtracking. */ + succeed, + + /* Followed by one byte giving n, then by n literal bytes. */ + exactn, + + /* Matches any (more or less) character. */ + anychar, + + /* Matches any one char belonging to specified set. First + following byte is number of bitmap bytes. Then come bytes + for a bitmap saying which chars are in. Bits in each byte + are ordered low-bit-first. A character is in the set if its + bit is 1. A character too large to have a bit in the map is + automatically not in the set. */ + charset, + + /* Same parameters as charset, but match any character that is + not one of those specified. */ + charset_not, + + /* Start remembering the text that is matched, for storing in a + register. Followed by one byte with the register number, in + the range 0 to one less than the pattern buffer's re_nsub + field. Then followed by one byte with the number of groups + inner to this one. (This last has to be part of the + start_memory only because we need it in the on_failure_jump + of re_match_2.) */ + start_memory, + + /* Stop remembering the text that is matched and store it in a + memory register. Followed by one byte with the register + number, in the range 0 to one less than `re_nsub' in the + pattern buffer, and one byte with the number of inner groups, + just like `start_memory'. (We need the number of inner + groups here because we don't have any easy way of finding the + corresponding start_memory when we're at a stop_memory.) */ + stop_memory, + + /* Match a duplicate of something remembered. Followed by one + byte containing the register number. */ + duplicate, + + /* Fail unless at beginning of line. */ + begline, + + /* Fail unless at end of line. */ + endline, + + /* Succeeds if at beginning of buffer (if emacs) or at beginning + of string to be matched (if not). */ + begbuf, + + /* Analogously, for end of buffer/string. */ + endbuf, + + /* Followed by two byte relative address to which to jump. */ + jump, + + /* Same as jump, but marks the end of an alternative. */ + jump_past_alt, + + /* Followed by two-byte relative address of place to resume at + in case of failure. */ + on_failure_jump, + + /* Like on_failure_jump, but pushes a placeholder instead of the + current string position when executed. */ + on_failure_keep_string_jump, + + /* Throw away latest failure point and then jump to following + two-byte relative address. */ + pop_failure_jump, + + /* Change to pop_failure_jump if know won't have to backtrack to + match; otherwise change to jump. This is used to jump + back to the beginning of a repeat. If what follows this jump + clearly won't match what the repeat does, such that we can be + sure that there is no use backtracking out of repetitions + already matched, then we change it to a pop_failure_jump. + Followed by two-byte address. */ + maybe_pop_jump, + + /* Jump to following two-byte address, and push a dummy failure + point. This failure point will be thrown away if an attempt + is made to use it for a failure. A `+' construct makes this + before the first repeat. Also used as an intermediary kind + of jump when compiling an alternative. */ + dummy_failure_jump, + + /* Push a dummy failure point and continue. Used at the end of + alternatives. */ + push_dummy_failure, + + /* Followed by two-byte relative address and two-byte number n. + After matching N times, jump to the address upon failure. */ + succeed_n, + + /* Followed by two-byte relative address, and two-byte number n. + Jump to the address N times, then fail. */ + jump_n, + + /* Set the following two-byte relative address to the + subsequent two-byte number. The address *includes* the two + bytes of number. */ + set_number_at, + + wordchar, /* Matches any word-constituent character. */ + notwordchar, /* Matches any char that is not a word-constituent. */ + + wordbeg, /* Succeeds if at word beginning. */ + wordend, /* Succeeds if at word end. */ + + wordbound, /* Succeeds if at a word boundary. */ + notwordbound /* Succeeds if not at a word boundary. */ + +#ifdef emacs + ,before_dot, /* Succeeds if before point. */ + at_dot, /* Succeeds if at point. */ + after_dot, /* Succeeds if after point. */ + + /* Matches any character whose syntax is specified. Followed by + a byte which contains a syntax code, e.g., Sword. */ + syntaxspec, + + /* Matches any character whose syntax is not that specified. */ + notsyntaxspec +#endif /* emacs */ +} re_opcode_t; + +/* Common operations on the compiled pattern. */ + +/* Store NUMBER in two contiguous bytes starting at DESTINATION. */ + +#define STORE_NUMBER(destination, number) \ + do { \ + (destination)[0] = (number) & 0377; \ + (destination)[1] = (number) >> 8; \ + } while (0) + +/* Same as STORE_NUMBER, except increment DESTINATION to + the byte after where the number is stored. Therefore, DESTINATION + must be an lvalue. */ + +#define STORE_NUMBER_AND_INCR(destination, number) \ + do { \ + STORE_NUMBER (destination, number); \ + (destination) += 2; \ + } while (0) + +/* Put into DESTINATION a number stored in two contiguous bytes starting + at SOURCE. */ + +#define EXTRACT_NUMBER(destination, source) \ + do { \ + (destination) = *(source) & 0377; \ + (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \ + } while (0) + +#ifdef DEBUG +static void extract_number _RE_ARGS ((int *dest, unsigned char *source)); +static void +extract_number (dest, source) + int *dest; + unsigned char *source; +{ + int temp = SIGN_EXTEND_CHAR (*(source + 1)); + *dest = *source & 0377; + *dest += temp << 8; +} + +# ifndef EXTRACT_MACROS /* To debug the macros. */ +# undef EXTRACT_NUMBER +# define EXTRACT_NUMBER(dest, src) extract_number (&dest, src) +# endif /* not EXTRACT_MACROS */ + +#endif /* DEBUG */ + +/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number. + SOURCE must be an lvalue. */ + +#define EXTRACT_NUMBER_AND_INCR(destination, source) \ + do { \ + EXTRACT_NUMBER (destination, source); \ + (source) += 2; \ + } while (0) + +#ifdef DEBUG +static void extract_number_and_incr _RE_ARGS ((int *destination, + unsigned char **source)); +static void +extract_number_and_incr (destination, source) + int *destination; + unsigned char **source; +{ + extract_number (destination, *source); + *source += 2; +} + +# ifndef EXTRACT_MACROS +# undef EXTRACT_NUMBER_AND_INCR +# define EXTRACT_NUMBER_AND_INCR(dest, src) \ + extract_number_and_incr (&dest, &src) +# endif /* not EXTRACT_MACROS */ + +#endif /* DEBUG */ + +/* If DEBUG is defined, Regex prints many voluminous messages about what + it is doing (if the variable `debug' is nonzero). If linked with the + main program in `iregex.c', you can enter patterns and strings + interactively. And if linked with the main program in `main.c' and + the other test files, you can run the already-written tests. */ + +#ifdef DEBUG + +/* We use standard I/O for debugging. */ +# include <stdio.h> + +/* It is useful to test things that ``must'' be true when debugging. */ +# include <assert.h> + +static int debug; + +# define DEBUG_STATEMENT(e) e +# define DEBUG_PRINT1(x) if (debug) printf (x) +# define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2) +# define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3) +# define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4) +# define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \ + if (debug) print_partial_compiled_pattern (s, e) +# define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \ + if (debug) print_double_string (w, s1, sz1, s2, sz2) + + +/* Print the fastmap in human-readable form. */ + +void +print_fastmap (fastmap) + char *fastmap; +{ + unsigned was_a_range = 0; + unsigned i = 0; + + while (i < (1 << BYTEWIDTH)) + { + if (fastmap[i++]) + { + was_a_range = 0; + putchar (i - 1); + while (i < (1 << BYTEWIDTH) && fastmap[i]) + { + was_a_range = 1; + i++; + } + if (was_a_range) + { + printf ("-"); + putchar (i - 1); + } + } + } + putchar ('\n'); +} + + +/* Print a compiled pattern string in human-readable form, starting at + the START pointer into it and ending just before the pointer END. */ + +void +print_partial_compiled_pattern (start, end) + unsigned char *start; + unsigned char *end; +{ + int mcnt, mcnt2; + unsigned char *p1; + unsigned char *p = start; + unsigned char *pend = end; + + if (start == NULL) + { + printf ("(null)\n"); + return; + } + + /* Loop over pattern commands. */ + while (p < pend) + { + printf ("%d:\t", p - start); + + switch ((re_opcode_t) *p++) + { + case no_op: + printf ("/no_op"); + break; + + case exactn: + mcnt = *p++; + printf ("/exactn/%d", mcnt); + do + { + putchar ('/'); + putchar (*p++); + } + while (--mcnt); + break; + + case start_memory: + mcnt = *p++; + printf ("/start_memory/%d/%d", mcnt, *p++); + break; + + case stop_memory: + mcnt = *p++; + printf ("/stop_memory/%d/%d", mcnt, *p++); + break; + + case duplicate: + printf ("/duplicate/%d", *p++); + break; + + case anychar: + printf ("/anychar"); + break; + + case charset: + case charset_not: + { + register int c, last = -100; + register int in_range = 0; + + printf ("/charset [%s", + (re_opcode_t) *(p - 1) == charset_not ? "^" : ""); + + assert (p + *p < pend); + + for (c = 0; c < 256; c++) + if (c / 8 < *p + && (p[1 + (c/8)] & (1 << (c % 8)))) + { + /* Are we starting a range? */ + if (last + 1 == c && ! in_range) + { + putchar ('-'); + in_range = 1; + } + /* Have we broken a range? */ + else if (last + 1 != c && in_range) + { + putchar (last); + in_range = 0; + } + + if (! in_range) + putchar (c); + + last = c; + } + + if (in_range) + putchar (last); + + putchar (']'); + + p += 1 + *p; + } + break; + + case begline: + printf ("/begline"); + break; + + case endline: + printf ("/endline"); + break; + + case on_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_jump to %d", p + mcnt - start); + break; + + case on_failure_keep_string_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_keep_string_jump to %d", p + mcnt - start); + break; + + case dummy_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/dummy_failure_jump to %d", p + mcnt - start); + break; + + case push_dummy_failure: + printf ("/push_dummy_failure"); + break; + + case maybe_pop_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/maybe_pop_jump to %d", p + mcnt - start); + break; + + case pop_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/pop_failure_jump to %d", p + mcnt - start); + break; + + case jump_past_alt: + extract_number_and_incr (&mcnt, &p); + printf ("/jump_past_alt to %d", p + mcnt - start); + break; + + case jump: + extract_number_and_incr (&mcnt, &p); + printf ("/jump to %d", p + mcnt - start); + break; + + case succeed_n: + extract_number_and_incr (&mcnt, &p); + p1 = p + mcnt; + extract_number_and_incr (&mcnt2, &p); + printf ("/succeed_n to %d, %d times", p1 - start, mcnt2); + break; + + case jump_n: + extract_number_and_incr (&mcnt, &p); + p1 = p + mcnt; + extract_number_and_incr (&mcnt2, &p); + printf ("/jump_n to %d, %d times", p1 - start, mcnt2); + break; + + case set_number_at: + extract_number_and_incr (&mcnt, &p); + p1 = p + mcnt; + extract_number_and_incr (&mcnt2, &p); + printf ("/set_number_at location %d to %d", p1 - start, mcnt2); + break; + + case wordbound: + printf ("/wordbound"); + break; + + case notwordbound: + printf ("/notwordbound"); + break; + + case wordbeg: + printf ("/wordbeg"); + break; + + case wordend: + printf ("/wordend"); + +# ifdef emacs + case before_dot: + printf ("/before_dot"); + break; + + case at_dot: + printf ("/at_dot"); + break; + + case after_dot: + printf ("/after_dot"); + break; + + case syntaxspec: + printf ("/syntaxspec"); + mcnt = *p++; + printf ("/%d", mcnt); + break; + + case notsyntaxspec: + printf ("/notsyntaxspec"); + mcnt = *p++; + printf ("/%d", mcnt); + break; +# endif /* emacs */ + + case wordchar: + printf ("/wordchar"); + break; + + case notwordchar: + printf ("/notwordchar"); + break; + + case begbuf: + printf ("/begbuf"); + break; + + case endbuf: + printf ("/endbuf"); + break; + + default: + printf ("?%d", *(p-1)); + } + + putchar ('\n'); + } + + printf ("%d:\tend of pattern.\n", p - start); +} + + +void +print_compiled_pattern (bufp) + struct re_pattern_buffer *bufp; +{ + unsigned char *buffer = bufp->buffer; + + print_partial_compiled_pattern (buffer, buffer + bufp->used); + printf ("%ld bytes used/%ld bytes allocated.\n", + bufp->used, bufp->allocated); + + if (bufp->fastmap_accurate && bufp->fastmap) + { + printf ("fastmap: "); + print_fastmap (bufp->fastmap); + } + + printf ("re_nsub: %d\t", bufp->re_nsub); + printf ("regs_alloc: %d\t", bufp->regs_allocated); + printf ("can_be_null: %d\t", bufp->can_be_null); + printf ("newline_anchor: %d\n", bufp->newline_anchor); + printf ("no_sub: %d\t", bufp->no_sub); + printf ("not_bol: %d\t", bufp->not_bol); + printf ("not_eol: %d\t", bufp->not_eol); + printf ("syntax: %lx\n", bufp->syntax); + /* Perhaps we should print the translate table? */ +} + + +void +print_double_string (where, string1, size1, string2, size2) + const char *where; + const char *string1; + const char *string2; + int size1; + int size2; +{ + int this_char; + + if (where == NULL) + printf ("(null)"); + else + { + if (FIRST_STRING_P (where)) + { + for (this_char = where - string1; this_char < size1; this_char++) + putchar (string1[this_char]); + + where = string2; + } + + for (this_char = where - string2; this_char < size2; this_char++) + putchar (string2[this_char]); + } +} + +void +printchar (c) + int c; +{ + putc (c, stderr); +} + +#else /* not DEBUG */ + +# undef assert +# define assert(e) + +# define DEBUG_STATEMENT(e) +# define DEBUG_PRINT1(x) +# define DEBUG_PRINT2(x1, x2) +# define DEBUG_PRINT3(x1, x2, x3) +# define DEBUG_PRINT4(x1, x2, x3, x4) +# define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) +# define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) + +#endif /* not DEBUG */ + +/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can + also be assigned to arbitrarily: each pattern buffer stores its own + syntax, so it can be changed between regex compilations. */ +/* This has no initializer because initialized variables in Emacs + become read-only after dumping. */ +reg_syntax_t re_syntax_options; + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit mask comprised of the various bits + defined in regex.h. We return the old syntax. */ + +reg_syntax_t +re_set_syntax (syntax) + reg_syntax_t syntax; +{ + reg_syntax_t ret = re_syntax_options; + + re_syntax_options = syntax; +#ifdef DEBUG + if (syntax & RE_DEBUG) + debug = 1; + else if (debug) /* was on but now is not */ + debug = 0; +#endif /* DEBUG */ + return ret; +} +#ifdef _LIBC +weak_alias (__re_set_syntax, re_set_syntax) +#endif + +/* This table gives an error message for each of the error codes listed + in regex.h. Obviously the order here has to be same as there. + POSIX doesn't require that we do anything for REG_NOERROR, + but why not be nice? */ + +static const char re_error_msgid[] = + { +#define REG_NOERROR_IDX 0 + gettext_noop ("Success") /* REG_NOERROR */ + "\0" +#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success") + gettext_noop ("No match") /* REG_NOMATCH */ + "\0" +#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match") + gettext_noop ("Invalid regular expression") /* REG_BADPAT */ + "\0" +#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression") + gettext_noop ("Invalid collation character"), /* REG_ECOLLATE */ + "\0" +#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character") + gettext_noop ("Invalid character class name") /* REG_ECTYPE */ + "\0" +#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name") + gettext_noop ("Trailing backslash") /* REG_EESCAPE */ + "\0" +#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash") + gettext_noop ("Invalid back reference") /* REG_ESUBREG */ + "\0" +#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference") + gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */ + "\0" +#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^") + gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */ + "\0" +#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(") + gettext_noop ("Unmatched \\{") /* REG_EBRACE */ + "\0" +#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{") + gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */ + "\0" +#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}") + gettext_noop ("Invalid range end") /* REG_ERANGE */ + "\0" +#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end") + gettext_noop ("Memory exhausted") /* REG_ESPACE */ + "\0" +#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted") + gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */ + "\0" +#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression") + gettext_noop ("Premature end of regular expression") /* REG_EEND */ + "\0" +#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression") + gettext_noop ("Regular expression too big") /* REG_ESIZE */ + "\0" +#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big") + gettext_noop ("Unmatched ) or \\)"), /* REG_ERPAREN */ + }; + +static const size_t re_error_msgid_idx[] = + { + REG_NOERROR_IDX, + REG_NOMATCH_IDX, + REG_BADPAT_IDX, + REG_ECOLLATE_IDX, + REG_ECTYPE_IDX, + REG_EESCAPE_IDX, + REG_ESUBREG_IDX, + REG_EBRACK_IDX, + REG_EPAREN_IDX, + REG_EBRACE_IDX, + REG_BADBR_IDX, + REG_ERANGE_IDX, + REG_ESPACE_IDX, + REG_BADRPT_IDX, + REG_EEND_IDX, + REG_ESIZE_IDX, + REG_ERPAREN_IDX + }; + +/* Avoiding alloca during matching, to placate r_alloc. */ + +/* Define MATCH_MAY_ALLOCATE unless we need to make sure that the + searching and matching functions should not call alloca. On some + systems, alloca is implemented in terms of malloc, and if we're + using the relocating allocator routines, then malloc could cause a + relocation, which might (if the strings being searched are in the + ralloc heap) shift the data out from underneath the regexp + routines. + + Here's another reason to avoid allocation: Emacs + processes input from X in a signal handler; processing X input may + call malloc; if input arrives while a matching routine is calling + malloc, then we're scrod. But Emacs can't just block input while + calling matching routines; then we don't notice interrupts when + they come in. So, Emacs blocks input around all regexp calls + except the matching calls, which it leaves unprotected, in the + faith that they will not malloc. */ + +/* Normally, this is fine. */ +#define MATCH_MAY_ALLOCATE + +/* When using GNU C, we are not REALLY using the C alloca, no matter + what config.h may say. So don't take precautions for it. */ +#ifdef __GNUC__ +# undef C_ALLOCA +#endif + +/* The match routines may not allocate if (1) they would do it with malloc + and (2) it's not safe for them to use malloc. + Note that if REL_ALLOC is defined, matching would not use malloc for the + failure stack, but we would still use it for the register vectors; + so REL_ALLOC should not affect this. */ +#if (defined C_ALLOCA || defined REGEX_MALLOC) && defined emacs +# undef MATCH_MAY_ALLOCATE +#endif + + +/* Failure stack declarations and macros; both re_compile_fastmap and + re_match_2 use a failure stack. These have to be macros because of + REGEX_ALLOCATE_STACK. */ + + +/* Number of failure points for which to initially allocate space + when matching. If this number is exceeded, we allocate more + space, so it is not a hard limit. */ +#ifndef INIT_FAILURE_ALLOC +# define INIT_FAILURE_ALLOC 5 +#endif + +/* Roughly the maximum number of failure points on the stack. Would be + exactly that if always used MAX_FAILURE_ITEMS items each time we failed. + This is a variable only so users of regex can assign to it; we never + change it ourselves. */ + +#ifdef INT_IS_16BIT + +# if defined MATCH_MAY_ALLOCATE +/* 4400 was enough to cause a crash on Alpha OSF/1, + whose default stack limit is 2mb. */ +long int re_max_failures = 4000; +# else +long int re_max_failures = 2000; +# endif + +union fail_stack_elt +{ + unsigned char *pointer; + long int integer; +}; + +typedef union fail_stack_elt fail_stack_elt_t; + +typedef struct +{ + fail_stack_elt_t *stack; + unsigned long int size; + unsigned long int avail; /* Offset of next open position. */ +} fail_stack_type; + +#else /* not INT_IS_16BIT */ + +# if defined MATCH_MAY_ALLOCATE +/* 4400 was enough to cause a crash on Alpha OSF/1, + whose default stack limit is 2mb. */ +int re_max_failures = 20000; +# else +int re_max_failures = 2000; +# endif + +union fail_stack_elt +{ + unsigned char *pointer; + int integer; +}; + +typedef union fail_stack_elt fail_stack_elt_t; + +typedef struct +{ + fail_stack_elt_t *stack; + unsigned size; + unsigned avail; /* Offset of next open position. */ +} fail_stack_type; + +#endif /* INT_IS_16BIT */ + +#define FAIL_STACK_EMPTY() (fail_stack.avail == 0) +#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0) +#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size) + + +/* Define macros to initialize and free the failure stack. + Do `return -2' if the alloc fails. */ + +#ifdef MATCH_MAY_ALLOCATE +# define INIT_FAIL_STACK() \ + do { \ + fail_stack.stack = (fail_stack_elt_t *) \ + REGEX_ALLOCATE_STACK (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \ + \ + if (fail_stack.stack == NULL) \ + return -2; \ + \ + fail_stack.size = INIT_FAILURE_ALLOC; \ + fail_stack.avail = 0; \ + } while (0) + +# define RESET_FAIL_STACK() REGEX_FREE_STACK (fail_stack.stack) +#else +# define INIT_FAIL_STACK() \ + do { \ + fail_stack.avail = 0; \ + } while (0) + +# define RESET_FAIL_STACK() +#endif + + +/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items. + + Return 1 if succeeds, and 0 if either ran out of memory + allocating space for it or it was already too large. + + REGEX_REALLOCATE_STACK requires `destination' be declared. */ + +#define DOUBLE_FAIL_STACK(fail_stack) \ + ((fail_stack).size > (unsigned) (re_max_failures * MAX_FAILURE_ITEMS) \ + ? 0 \ + : ((fail_stack).stack = (fail_stack_elt_t *) \ + REGEX_REALLOCATE_STACK ((fail_stack).stack, \ + (fail_stack).size * sizeof (fail_stack_elt_t), \ + ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \ + \ + (fail_stack).stack == NULL \ + ? 0 \ + : ((fail_stack).size <<= 1, \ + 1))) + + +/* Push pointer POINTER on FAIL_STACK. + Return 1 if was able to do so and 0 if ran out of memory allocating + space to do so. */ +#define PUSH_PATTERN_OP(POINTER, FAIL_STACK) \ + ((FAIL_STACK_FULL () \ + && !DOUBLE_FAIL_STACK (FAIL_STACK)) \ + ? 0 \ + : ((FAIL_STACK).stack[(FAIL_STACK).avail++].pointer = POINTER, \ + 1)) + +/* Push a pointer value onto the failure stack. + Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_POINTER(item) \ + fail_stack.stack[fail_stack.avail++].pointer = (unsigned char *) (item) + +/* This pushes an integer-valued item onto the failure stack. + Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_INT(item) \ + fail_stack.stack[fail_stack.avail++].integer = (item) + +/* Push a fail_stack_elt_t value onto the failure stack. + Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_ELT(item) \ + fail_stack.stack[fail_stack.avail++] = (item) + +/* These three POP... operations complement the three PUSH... operations. + All assume that `fail_stack' is nonempty. */ +#define POP_FAILURE_POINTER() fail_stack.stack[--fail_stack.avail].pointer +#define POP_FAILURE_INT() fail_stack.stack[--fail_stack.avail].integer +#define POP_FAILURE_ELT() fail_stack.stack[--fail_stack.avail] + +/* Used to omit pushing failure point id's when we're not debugging. */ +#ifdef DEBUG +# define DEBUG_PUSH PUSH_FAILURE_INT +# define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_INT () +#else +# define DEBUG_PUSH(item) +# define DEBUG_POP(item_addr) +#endif + + +/* Push the information about the state we will need + if we ever fail back to it. + + Requires variables fail_stack, regstart, regend, reg_info, and + num_regs_pushed be declared. DOUBLE_FAIL_STACK requires `destination' + be declared. + + Does `return FAILURE_CODE' if runs out of memory. */ + +#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \ + do { \ + char *destination; \ + /* Must be int, so when we don't save any registers, the arithmetic \ + of 0 + -1 isn't done as unsigned. */ \ + /* Can't be int, since there is not a shred of a guarantee that int \ + is wide enough to hold a value of something to which pointer can \ + be assigned */ \ + active_reg_t this_reg; \ + \ + DEBUG_STATEMENT (failure_id++); \ + DEBUG_STATEMENT (nfailure_points_pushed++); \ + DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \ + DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\ + DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\ + \ + DEBUG_PRINT2 (" slots needed: %ld\n", NUM_FAILURE_ITEMS); \ + DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \ + \ + /* Ensure we have enough space allocated for what we will push. */ \ + while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \ + { \ + if (!DOUBLE_FAIL_STACK (fail_stack)) \ + return failure_code; \ + \ + DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \ + (fail_stack).size); \ + DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\ + } \ + \ + /* Push the info, starting with the registers. */ \ + DEBUG_PRINT1 ("\n"); \ + \ + if (1) \ + for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \ + this_reg++) \ + { \ + DEBUG_PRINT2 (" Pushing reg: %lu\n", this_reg); \ + DEBUG_STATEMENT (num_regs_pushed++); \ + \ + DEBUG_PRINT2 (" start: %p\n", regstart[this_reg]); \ + PUSH_FAILURE_POINTER (regstart[this_reg]); \ + \ + DEBUG_PRINT2 (" end: %p\n", regend[this_reg]); \ + PUSH_FAILURE_POINTER (regend[this_reg]); \ + \ + DEBUG_PRINT2 (" info: %p\n ", \ + reg_info[this_reg].word.pointer); \ + DEBUG_PRINT2 (" match_null=%d", \ + REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \ + DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \ + DEBUG_PRINT2 (" matched_something=%d", \ + MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT2 (" ever_matched=%d", \ + EVER_MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT1 ("\n"); \ + PUSH_FAILURE_ELT (reg_info[this_reg].word); \ + } \ + \ + DEBUG_PRINT2 (" Pushing low active reg: %ld\n", lowest_active_reg);\ + PUSH_FAILURE_INT (lowest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing high active reg: %ld\n", highest_active_reg);\ + PUSH_FAILURE_INT (highest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing pattern %p:\n", pattern_place); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \ + PUSH_FAILURE_POINTER (pattern_place); \ + \ + DEBUG_PRINT2 (" Pushing string %p: `", string_place); \ + DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \ + size2); \ + DEBUG_PRINT1 ("'\n"); \ + PUSH_FAILURE_POINTER (string_place); \ + \ + DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \ + DEBUG_PUSH (failure_id); \ + } while (0) + +/* This is the number of items that are pushed and popped on the stack + for each register. */ +#define NUM_REG_ITEMS 3 + +/* Individual items aside from the registers. */ +#ifdef DEBUG +# define NUM_NONREG_ITEMS 5 /* Includes failure point id. */ +#else +# define NUM_NONREG_ITEMS 4 +#endif + +/* We push at most this many items on the stack. */ +/* We used to use (num_regs - 1), which is the number of registers + this regexp will save; but that was changed to 5 + to avoid stack overflow for a regexp with lots of parens. */ +#define MAX_FAILURE_ITEMS (5 * NUM_REG_ITEMS + NUM_NONREG_ITEMS) + +/* We actually push this many items. */ +#define NUM_FAILURE_ITEMS \ + (((0 \ + ? 0 : highest_active_reg - lowest_active_reg + 1) \ + * NUM_REG_ITEMS) \ + + NUM_NONREG_ITEMS) + +/* How many items can still be added to the stack without overflowing it. */ +#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail) + + +/* Pops what PUSH_FAIL_STACK pushes. + + We restore into the parameters, all of which should be lvalues: + STR -- the saved data position. + PAT -- the saved pattern position. + LOW_REG, HIGH_REG -- the highest and lowest active registers. + REGSTART, REGEND -- arrays of string positions. + REG_INFO -- array of information about each subexpression. + + Also assumes the variables `fail_stack' and (if debugging), `bufp', + `pend', `string1', `size1', `string2', and `size2'. */ + +#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\ +{ \ + DEBUG_STATEMENT (unsigned failure_id;) \ + active_reg_t this_reg; \ + const unsigned char *string_temp; \ + \ + assert (!FAIL_STACK_EMPTY ()); \ + \ + /* Remove failure points and point to how many regs pushed. */ \ + DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \ + DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \ + DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \ + \ + assert (fail_stack.avail >= NUM_NONREG_ITEMS); \ + \ + DEBUG_POP (&failure_id); \ + DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \ + \ + /* If the saved string location is NULL, it came from an \ + on_failure_keep_string_jump opcode, and we want to throw away the \ + saved NULL, thus retaining our current position in the string. */ \ + string_temp = POP_FAILURE_POINTER (); \ + if (string_temp != NULL) \ + str = (const char *) string_temp; \ + \ + DEBUG_PRINT2 (" Popping string %p: `", str); \ + DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \ + DEBUG_PRINT1 ("'\n"); \ + \ + pat = (unsigned char *) POP_FAILURE_POINTER (); \ + DEBUG_PRINT2 (" Popping pattern %p:\n", pat); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \ + \ + /* Restore register info. */ \ + high_reg = (active_reg_t) POP_FAILURE_INT (); \ + DEBUG_PRINT2 (" Popping high active reg: %ld\n", high_reg); \ + \ + low_reg = (active_reg_t) POP_FAILURE_INT (); \ + DEBUG_PRINT2 (" Popping low active reg: %ld\n", low_reg); \ + \ + if (1) \ + for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \ + { \ + DEBUG_PRINT2 (" Popping reg: %ld\n", this_reg); \ + \ + reg_info[this_reg].word = POP_FAILURE_ELT (); \ + DEBUG_PRINT2 (" info: %p\n", \ + reg_info[this_reg].word.pointer); \ + \ + regend[this_reg] = (const char *) POP_FAILURE_POINTER (); \ + DEBUG_PRINT2 (" end: %p\n", regend[this_reg]); \ + \ + regstart[this_reg] = (const char *) POP_FAILURE_POINTER (); \ + DEBUG_PRINT2 (" start: %p\n", regstart[this_reg]); \ + } \ + else \ + { \ + for (this_reg = highest_active_reg; this_reg > high_reg; this_reg--) \ + { \ + reg_info[this_reg].word.integer = 0; \ + regend[this_reg] = 0; \ + regstart[this_reg] = 0; \ + } \ + highest_active_reg = high_reg; \ + } \ + \ + set_regs_matched_done = 0; \ + DEBUG_STATEMENT (nfailure_points_popped++); \ +} /* POP_FAILURE_POINT */ + + + +/* Structure for per-register (a.k.a. per-group) information. + Other register information, such as the + starting and ending positions (which are addresses), and the list of + inner groups (which is a bits list) are maintained in separate + variables. + + We are making a (strictly speaking) nonportable assumption here: that + the compiler will pack our bit fields into something that fits into + the type of `word', i.e., is something that fits into one item on the + failure stack. */ + + +/* Declarations and macros for re_match_2. */ + +typedef union +{ + fail_stack_elt_t word; + struct + { + /* This field is one if this group can match the empty string, + zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */ +#define MATCH_NULL_UNSET_VALUE 3 + unsigned match_null_string_p : 2; + unsigned is_active : 1; + unsigned matched_something : 1; + unsigned ever_matched_something : 1; + } bits; +} register_info_type; + +#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p) +#define IS_ACTIVE(R) ((R).bits.is_active) +#define MATCHED_SOMETHING(R) ((R).bits.matched_something) +#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something) + + +/* Call this when have matched a real character; it sets `matched' flags + for the subexpressions which we are currently inside. Also records + that those subexprs have matched. */ +#define SET_REGS_MATCHED() \ + do \ + { \ + if (!set_regs_matched_done) \ + { \ + active_reg_t r; \ + set_regs_matched_done = 1; \ + for (r = lowest_active_reg; r <= highest_active_reg; r++) \ + { \ + MATCHED_SOMETHING (reg_info[r]) \ + = EVER_MATCHED_SOMETHING (reg_info[r]) \ + = 1; \ + } \ + } \ + } \ + while (0) + +/* Registers are set to a sentinel when they haven't yet matched. */ +static char reg_unset_dummy; +#define REG_UNSET_VALUE (®_unset_dummy) +#define REG_UNSET(e) ((e) == REG_UNSET_VALUE) + +/* Subroutine declarations and macros for regex_compile. */ + +static reg_errcode_t regex_compile _RE_ARGS ((const char *pattern, size_t size, + reg_syntax_t syntax, + struct re_pattern_buffer *bufp)); +static void store_op1 _RE_ARGS ((re_opcode_t op, unsigned char *loc, int arg)); +static void store_op2 _RE_ARGS ((re_opcode_t op, unsigned char *loc, + int arg1, int arg2)); +static void insert_op1 _RE_ARGS ((re_opcode_t op, unsigned char *loc, + int arg, unsigned char *end)); +static void insert_op2 _RE_ARGS ((re_opcode_t op, unsigned char *loc, + int arg1, int arg2, unsigned char *end)); +static boolean at_begline_loc_p _RE_ARGS ((const char *pattern, const char *p, + reg_syntax_t syntax)); +static boolean at_endline_loc_p _RE_ARGS ((const char *p, const char *pend, + reg_syntax_t syntax)); +static reg_errcode_t compile_range _RE_ARGS ((const char **p_ptr, + const char *pend, + char *translate, + reg_syntax_t syntax, + unsigned char *b)); + +/* Fetch the next character in the uncompiled pattern---translating it + if necessary. Also cast from a signed character in the constant + string passed to us by the user to an unsigned char that we can use + as an array index (in, e.g., `translate'). */ +#ifndef PATFETCH +# define PATFETCH(c) \ + do {if (p == pend) return REG_EEND; \ + c = (unsigned char) *p++; \ + if (translate) c = (unsigned char) translate[c]; \ + } while (0) +#endif + +/* Fetch the next character in the uncompiled pattern, with no + translation. */ +#define PATFETCH_RAW(c) \ + do {if (p == pend) return REG_EEND; \ + c = (unsigned char) *p++; \ + } while (0) + +/* Go backwards one character in the pattern. */ +#define PATUNFETCH p-- + + +/* If `translate' is non-null, return translate[D], else just D. We + cast the subscript to translate because some data is declared as + `char *', to avoid warnings when a string constant is passed. But + when we use a character as a subscript we must make it unsigned. */ +#ifndef TRANSLATE +# define TRANSLATE(d) \ + (translate ? (char) translate[(unsigned char) (d)] : (d)) +#endif + + +/* Macros for outputting the compiled pattern into `buffer'. */ + +/* If the buffer isn't allocated when it comes in, use this. */ +#define INIT_BUF_SIZE 32 + +/* Make sure we have at least N more bytes of space in buffer. */ +#define GET_BUFFER_SPACE(n) \ + while ((unsigned long) (b - bufp->buffer + (n)) > bufp->allocated) \ + EXTEND_BUFFER () + +/* Make sure we have one more byte of buffer space and then add C to it. */ +#define BUF_PUSH(c) \ + do { \ + GET_BUFFER_SPACE (1); \ + *b++ = (unsigned char) (c); \ + } while (0) + + +/* Ensure we have two more bytes of buffer space and then append C1 and C2. */ +#define BUF_PUSH_2(c1, c2) \ + do { \ + GET_BUFFER_SPACE (2); \ + *b++ = (unsigned char) (c1); \ + *b++ = (unsigned char) (c2); \ + } while (0) + + +/* As with BUF_PUSH_2, except for three bytes. */ +#define BUF_PUSH_3(c1, c2, c3) \ + do { \ + GET_BUFFER_SPACE (3); \ + *b++ = (unsigned char) (c1); \ + *b++ = (unsigned char) (c2); \ + *b++ = (unsigned char) (c3); \ + } while (0) + + +/* Store a jump with opcode OP at LOC to location TO. We store a + relative address offset by the three bytes the jump itself occupies. */ +#define STORE_JUMP(op, loc, to) \ + store_op1 (op, loc, (int) ((to) - (loc) - 3)) + +/* Likewise, for a two-argument jump. */ +#define STORE_JUMP2(op, loc, to, arg) \ + store_op2 (op, loc, (int) ((to) - (loc) - 3), arg) + +/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */ +#define INSERT_JUMP(op, loc, to) \ + insert_op1 (op, loc, (int) ((to) - (loc) - 3), b) + +/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */ +#define INSERT_JUMP2(op, loc, to, arg) \ + insert_op2 (op, loc, (int) ((to) - (loc) - 3), arg, b) + + +/* This is not an arbitrary limit: the arguments which represent offsets + into the pattern are two bytes long. So if 2^16 bytes turns out to + be too small, many things would have to change. */ +/* Any other compiler which, like MSC, has allocation limit below 2^16 + bytes will have to use approach similar to what was done below for + MSC and drop MAX_BUF_SIZE a bit. Otherwise you may end up + reallocating to 0 bytes. Such thing is not going to work too well. + You have been warned!! */ +#if defined _MSC_VER && !defined WIN32 +/* Microsoft C 16-bit versions limit malloc to approx 65512 bytes. + The REALLOC define eliminates a flurry of conversion warnings, + but is not required. */ +# define MAX_BUF_SIZE 65500L +# define REALLOC(p,s) realloc ((p), (size_t) (s)) +#else +# define MAX_BUF_SIZE (1L << 16) +# define REALLOC(p,s) realloc ((p), (s)) +#endif + +/* Extend the buffer by twice its current size via realloc and + reset the pointers that pointed into the old block to point to the + correct places in the new one. If extending the buffer results in it + being larger than MAX_BUF_SIZE, then flag memory exhausted. */ +#define EXTEND_BUFFER() \ + do { \ + unsigned char *old_buffer = bufp->buffer; \ + if (bufp->allocated == MAX_BUF_SIZE) \ + return REG_ESIZE; \ + bufp->allocated <<= 1; \ + if (bufp->allocated > MAX_BUF_SIZE) \ + bufp->allocated = MAX_BUF_SIZE; \ + bufp->buffer = (unsigned char *) REALLOC (bufp->buffer, bufp->allocated);\ + if (bufp->buffer == NULL) \ + return REG_ESPACE; \ + /* If the buffer moved, move all the pointers into it. */ \ + if (old_buffer != bufp->buffer) \ + { \ + b = (b - old_buffer) + bufp->buffer; \ + begalt = (begalt - old_buffer) + bufp->buffer; \ + if (fixup_alt_jump) \ + fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\ + if (laststart) \ + laststart = (laststart - old_buffer) + bufp->buffer; \ + if (pending_exact) \ + pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ + } \ + } while (0) + + +/* Since we have one byte reserved for the register number argument to + {start,stop}_memory, the maximum number of groups we can report + things about is what fits in that byte. */ +#define MAX_REGNUM 255 + +/* But patterns can have more than `MAX_REGNUM' registers. We just + ignore the excess. */ +typedef unsigned regnum_t; + + +/* Macros for the compile stack. */ + +/* Since offsets can go either forwards or backwards, this type needs to + be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */ +/* int may be not enough when sizeof(int) == 2. */ +typedef long pattern_offset_t; + +typedef struct +{ + pattern_offset_t begalt_offset; + pattern_offset_t fixup_alt_jump; + pattern_offset_t inner_group_offset; + pattern_offset_t laststart_offset; + regnum_t regnum; +} compile_stack_elt_t; + + +typedef struct +{ + compile_stack_elt_t *stack; + unsigned size; + unsigned avail; /* Offset of next open position. */ +} compile_stack_type; + + +#define INIT_COMPILE_STACK_SIZE 32 + +#define COMPILE_STACK_EMPTY (compile_stack.avail == 0) +#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size) + +/* The next available element. */ +#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail]) + + +/* Set the bit for character C in a list. */ +#define SET_LIST_BIT(c) \ + (b[((unsigned char) (c)) / BYTEWIDTH] \ + |= 1 << (((unsigned char) c) % BYTEWIDTH)) + + +/* Get the next unsigned number in the uncompiled pattern. */ +#define GET_UNSIGNED_NUMBER(num) \ + { if (p != pend) \ + { \ + PATFETCH (c); \ + while (ISDIGIT (c)) \ + { \ + if (num < 0) \ + num = 0; \ + num = num * 10 + c - '0'; \ + if (p == pend) \ + break; \ + PATFETCH (c); \ + } \ + } \ + } + +#if defined _LIBC || WIDE_CHAR_SUPPORT +/* The GNU C library provides support for user-defined character classes + and the functions from ISO C amendement 1. */ +# ifdef CHARCLASS_NAME_MAX +# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX +# else +/* This shouldn't happen but some implementation might still have this + problem. Use a reasonable default value. */ +# define CHAR_CLASS_MAX_LENGTH 256 +# endif + +# ifdef _LIBC +# define IS_CHAR_CLASS(string) __wctype (string) +# else +# define IS_CHAR_CLASS(string) wctype (string) +# endif +#else +# define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ + +# define IS_CHAR_CLASS(string) \ + (STREQ (string, "alpha") || STREQ (string, "upper") \ + || STREQ (string, "lower") || STREQ (string, "digit") \ + || STREQ (string, "alnum") || STREQ (string, "xdigit") \ + || STREQ (string, "space") || STREQ (string, "print") \ + || STREQ (string, "punct") || STREQ (string, "graph") \ + || STREQ (string, "cntrl") || STREQ (string, "blank")) +#endif + +#ifndef MATCH_MAY_ALLOCATE + +/* If we cannot allocate large objects within re_match_2_internal, + we make the fail stack and register vectors global. + The fail stack, we grow to the maximum size when a regexp + is compiled. + The register vectors, we adjust in size each time we + compile a regexp, according to the number of registers it needs. */ + +static fail_stack_type fail_stack; + +/* Size with which the following vectors are currently allocated. + That is so we can make them bigger as needed, + but never make them smaller. */ +static int regs_allocated_size; + +static const char ** regstart, ** regend; +static const char ** old_regstart, ** old_regend; +static const char **best_regstart, **best_regend; +static register_info_type *reg_info; +static const char **reg_dummy; +static register_info_type *reg_info_dummy; + +/* Make the register vectors big enough for NUM_REGS registers, + but don't make them smaller. */ + +static +regex_grow_registers (num_regs) + int num_regs; +{ + if (num_regs > regs_allocated_size) + { + RETALLOC_IF (regstart, num_regs, const char *); + RETALLOC_IF (regend, num_regs, const char *); + RETALLOC_IF (old_regstart, num_regs, const char *); + RETALLOC_IF (old_regend, num_regs, const char *); + RETALLOC_IF (best_regstart, num_regs, const char *); + RETALLOC_IF (best_regend, num_regs, const char *); + RETALLOC_IF (reg_info, num_regs, register_info_type); + RETALLOC_IF (reg_dummy, num_regs, const char *); + RETALLOC_IF (reg_info_dummy, num_regs, register_info_type); + + regs_allocated_size = num_regs; + } +} + +#endif /* not MATCH_MAY_ALLOCATE */ + +static boolean group_in_compile_stack _RE_ARGS ((compile_stack_type + compile_stack, + regnum_t regnum)); + +/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX. + Returns one of error codes defined in `regex.h', or zero for success. + + Assumes the `allocated' (and perhaps `buffer') and `translate' + fields are set in BUFP on entry. + + If it succeeds, results are put in BUFP (if it returns an error, the + contents of BUFP are undefined): + `buffer' is the compiled pattern; + `syntax' is set to SYNTAX; + `used' is set to the length of the compiled pattern; + `fastmap_accurate' is zero; + `re_nsub' is the number of subexpressions in PATTERN; + `not_bol' and `not_eol' are zero; + + The `fastmap' and `newline_anchor' fields are neither + examined nor set. */ + +/* Return, freeing storage we allocated. */ +#define FREE_STACK_RETURN(value) \ + return (free (compile_stack.stack), value) + +static reg_errcode_t +regex_compile (pattern, size, syntax, bufp) + const char *pattern; + size_t size; + reg_syntax_t syntax; + struct re_pattern_buffer *bufp; +{ + /* We fetch characters from PATTERN here. Even though PATTERN is + `char *' (i.e., signed), we declare these variables as unsigned, so + they can be reliably used as array indices. */ + register unsigned char c, c1; + + /* A random temporary spot in PATTERN. */ + const char *p1; + + /* Points to the end of the buffer, where we should append. */ + register unsigned char *b; + + /* Keeps track of unclosed groups. */ + compile_stack_type compile_stack; + + /* Points to the current (ending) position in the pattern. */ + const char *p = pattern; + const char *pend = pattern + size; + + /* How to translate the characters in the pattern. */ + RE_TRANSLATE_TYPE translate = bufp->translate; + + /* Address of the count-byte of the most recently inserted `exactn' + command. This makes it possible to tell if a new exact-match + character can be added to that command or if the character requires + a new `exactn' command. */ + unsigned char *pending_exact = 0; + + /* Address of start of the most recently finished expression. + This tells, e.g., postfix * where to find the start of its + operand. Reset at the beginning of groups and alternatives. */ + unsigned char *laststart = 0; + + /* Address of beginning of regexp, or inside of last group. */ + unsigned char *begalt; + + /* Place in the uncompiled pattern (i.e., the {) to + which to go back if the interval is invalid. */ + const char *beg_interval; + + /* Address of the place where a forward jump should go to the end of + the containing expression. Each alternative of an `or' -- except the + last -- ends with a forward jump of this sort. */ + unsigned char *fixup_alt_jump = 0; + + /* Counts open-groups as they are encountered. Remembered for the + matching close-group on the compile stack, so the same register + number is put in the stop_memory as the start_memory. */ + regnum_t regnum = 0; + +#ifdef DEBUG + DEBUG_PRINT1 ("\nCompiling pattern: "); + if (debug) + { + unsigned debug_count; + + for (debug_count = 0; debug_count < size; debug_count++) + putchar (pattern[debug_count]); + putchar ('\n'); + } +#endif /* DEBUG */ + + /* Initialize the compile stack. */ + compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t); + if (compile_stack.stack == NULL) + return REG_ESPACE; + + compile_stack.size = INIT_COMPILE_STACK_SIZE; + compile_stack.avail = 0; + + /* Initialize the pattern buffer. */ + bufp->syntax = syntax; + bufp->fastmap_accurate = 0; + bufp->not_bol = bufp->not_eol = 0; + + /* Set `used' to zero, so that if we return an error, the pattern + printer (for debugging) will think there's no pattern. We reset it + at the end. */ + bufp->used = 0; + + /* Always count groups, whether or not bufp->no_sub is set. */ + bufp->re_nsub = 0; + +#if !defined emacs && !defined SYNTAX_TABLE + /* Initialize the syntax table. */ + init_syntax_once (); +#endif + + if (bufp->allocated == 0) + { + if (bufp->buffer) + { /* If zero allocated, but buffer is non-null, try to realloc + enough space. This loses if buffer's address is bogus, but + that is the user's responsibility. */ + RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char); + } + else + { /* Caller did not allocate a buffer. Do it for them. */ + bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char); + } + if (!bufp->buffer) FREE_STACK_RETURN (REG_ESPACE); + + bufp->allocated = INIT_BUF_SIZE; + } + + begalt = b = bufp->buffer; + + /* Loop through the uncompiled pattern until we're at the end. */ + while (p != pend) + { + PATFETCH (c); + + switch (c) + { + case '^': + { + if ( /* If at start of pattern, it's an operator. */ + p == pattern + 1 + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's come before. */ + || at_begline_loc_p (pattern, p, syntax)) + BUF_PUSH (begline); + else + goto normal_char; + } + break; + + + case '$': + { + if ( /* If at end of pattern, it's an operator. */ + p == pend + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's next. */ + || at_endline_loc_p (p, pend, syntax)) + BUF_PUSH (endline); + else + goto normal_char; + } + break; + + + case '+': + case '?': + if ((syntax & RE_BK_PLUS_QM) + || (syntax & RE_LIMITED_OPS)) + goto normal_char; + handle_plus: + case '*': + /* If there is no previous pattern... */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + FREE_STACK_RETURN (REG_BADRPT); + else if (!(syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + } + + { + /* Are we optimizing this jump? */ + boolean keep_string_p = false; + + /* 1 means zero (many) matches is allowed. */ + char zero_times_ok = 0, many_times_ok = 0; + + /* If there is a sequence of repetition chars, collapse it + down to just one (the right one). We can't combine + interval operators with these because of, e.g., `a{2}*', + which should only match an even number of `a's. */ + + for (;;) + { + zero_times_ok |= c != '+'; + many_times_ok |= c != '?'; + + if (p == pend) + break; + + PATFETCH (c); + + if (c == '*' + || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?'))) + ; + + else if (syntax & RE_BK_PLUS_QM && c == '\\') + { + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); + + PATFETCH (c1); + if (!(c1 == '+' || c1 == '?')) + { + PATUNFETCH; + PATUNFETCH; + break; + } + + c = c1; + } + else + { + PATUNFETCH; + break; + } + + /* If we get here, we found another repeat character. */ + } + + /* Star, etc. applied to an empty pattern is equivalent + to an empty pattern. */ + if (!laststart) + break; + + /* Now we know whether or not zero matches is allowed + and also whether or not two or more matches is allowed. */ + if (many_times_ok) + { /* More than one repetition is allowed, so put in at the + end a backward relative jump from `b' to before the next + jump we're going to put in below (which jumps from + laststart to after this jump). + + But if we are at the `*' in the exact sequence `.*\n', + insert an unconditional jump backwards to the ., + instead of the beginning of the loop. This way we only + push a failure point once, instead of every time + through the loop. */ + assert (p - 1 > pattern); + + /* Allocate the space for the jump. */ + GET_BUFFER_SPACE (3); + + /* We know we are not at the first character of the pattern, + because laststart was nonzero. And we've already + incremented `p', by the way, to be the character after + the `*'. Do we have to do something analogous here + for null bytes, because of RE_DOT_NOT_NULL? */ + if (TRANSLATE (*(p - 2)) == TRANSLATE ('.') + && zero_times_ok + && p < pend && TRANSLATE (*p) == TRANSLATE ('\n') + && !(syntax & RE_DOT_NEWLINE)) + { /* We have .*\n. */ + STORE_JUMP (jump, b, laststart); + keep_string_p = true; + } + else + /* Anything else. */ + STORE_JUMP (maybe_pop_jump, b, laststart - 3); + + /* We've added more stuff to the buffer. */ + b += 3; + } + + /* On failure, jump from laststart to b + 3, which will be the + end of the buffer after this jump is inserted. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump + : on_failure_jump, + laststart, b + 3); + pending_exact = 0; + b += 3; + + if (!zero_times_ok) + { + /* At least one repetition is required, so insert a + `dummy_failure_jump' before the initial + `on_failure_jump' instruction of the loop. This + effects a skip over that instruction the first time + we hit that loop. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6); + b += 3; + } + } + break; + + + case '.': + laststart = b; + BUF_PUSH (anychar); + break; + + + case '[': + { + boolean had_char_class = false; + + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + /* Ensure that we have enough space to push a charset: the + opcode, the length count, and the bitset; 34 bytes in all. */ + GET_BUFFER_SPACE (34); + + laststart = b; + + /* We test `*p == '^' twice, instead of using an if + statement, so we only need one BUF_PUSH. */ + BUF_PUSH (*p == '^' ? charset_not : charset); + if (*p == '^') + p++; + + /* Remember the first position in the bracket expression. */ + p1 = p; + + /* Push the number of bytes in the bitmap. */ + BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH); + + /* Clear the whole map. */ + bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); + + /* charset_not matches newline according to a syntax bit. */ + if ((re_opcode_t) b[-2] == charset_not + && (syntax & RE_HAT_LISTS_NOT_NEWLINE)) + SET_LIST_BIT ('\n'); + + /* Read in characters and ranges, setting map bits. */ + for (;;) + { + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + PATFETCH (c); + + /* \ might escape characters inside [...] and [^...]. */ + if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\') + { + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); + + PATFETCH (c1); + SET_LIST_BIT (c1); + continue; + } + + /* Could be the end of the bracket expression. If it's + not (i.e., when the bracket expression is `[]' so + far), the ']' character bit gets set way below. */ + if (c == ']' && p != p1 + 1) + break; + + /* Look ahead to see if it's a range when the last thing + was a character class. */ + if (had_char_class && c == '-' && *p != ']') + FREE_STACK_RETURN (REG_ERANGE); + + /* Look ahead to see if it's a range when the last thing + was a character: if this is a hyphen not at the + beginning or the end of a list, then it's the range + operator. */ + if (c == '-' + && !(p - 2 >= pattern && p[-2] == '[') + && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^') + && *p != ']') + { + reg_errcode_t ret + = compile_range (&p, pend, translate, syntax, b); + if (ret != REG_NOERROR) FREE_STACK_RETURN (ret); + } + + else if (p[0] == '-' && p[1] != ']') + { /* This handles ranges made up of characters only. */ + reg_errcode_t ret; + + /* Move past the `-'. */ + PATFETCH (c1); + + ret = compile_range (&p, pend, translate, syntax, b); + if (ret != REG_NOERROR) FREE_STACK_RETURN (ret); + } + + /* See if we're at the beginning of a possible character + class. */ + + else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':') + { /* Leave room for the null. */ + char str[CHAR_CLASS_MAX_LENGTH + 1]; + + PATFETCH (c); + c1 = 0; + + /* If pattern is `[[:'. */ + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + for (;;) + { + PATFETCH (c); + if ((c == ':' && *p == ']') || p == pend) + break; + if (c1 < CHAR_CLASS_MAX_LENGTH) + str[c1++] = c; + else + /* This is in any case an invalid class name. */ + str[0] = '\0'; + } + str[c1] = '\0'; + + /* If isn't a word bracketed by `[:' and `:]': + undo the ending character, the letters, and leave + the leading `:' and `[' (but set bits for them). */ + if (c == ':' && *p == ']') + { +#if defined _LIBC || WIDE_CHAR_SUPPORT + boolean is_lower = STREQ (str, "lower"); + boolean is_upper = STREQ (str, "upper"); + wctype_t wt; + int ch; + + wt = IS_CHAR_CLASS (str); + if (wt == 0) + FREE_STACK_RETURN (REG_ECTYPE); + + /* Throw away the ] at the end of the character + class. */ + PATFETCH (c); + + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + for (ch = 0; ch < 1 << BYTEWIDTH; ++ch) + { +# ifdef _LIBC + if (__iswctype (__btowc (ch), wt)) + SET_LIST_BIT (ch); +# else + if (iswctype (btowc (ch), wt)) + SET_LIST_BIT (ch); +# endif + + if (translate && (is_upper || is_lower) + && (ISUPPER (ch) || ISLOWER (ch))) + SET_LIST_BIT (ch); + } + + had_char_class = true; +#else + int ch; + boolean is_alnum = STREQ (str, "alnum"); + boolean is_alpha = STREQ (str, "alpha"); + boolean is_blank = STREQ (str, "blank"); + boolean is_cntrl = STREQ (str, "cntrl"); + boolean is_digit = STREQ (str, "digit"); + boolean is_graph = STREQ (str, "graph"); + boolean is_lower = STREQ (str, "lower"); + boolean is_print = STREQ (str, "print"); + boolean is_punct = STREQ (str, "punct"); + boolean is_space = STREQ (str, "space"); + boolean is_upper = STREQ (str, "upper"); + boolean is_xdigit = STREQ (str, "xdigit"); + + if (!IS_CHAR_CLASS (str)) + FREE_STACK_RETURN (REG_ECTYPE); + + /* Throw away the ] at the end of the character + class. */ + PATFETCH (c); + + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + for (ch = 0; ch < 1 << BYTEWIDTH; ch++) + { + /* This was split into 3 if's to + avoid an arbitrary limit in some compiler. */ + if ( (is_alnum && ISALNUM (ch)) + || (is_alpha && ISALPHA (ch)) + || (is_blank && ISBLANK (ch)) + || (is_cntrl && ISCNTRL (ch))) + SET_LIST_BIT (ch); + if ( (is_digit && ISDIGIT (ch)) + || (is_graph && ISGRAPH (ch)) + || (is_lower && ISLOWER (ch)) + || (is_print && ISPRINT (ch))) + SET_LIST_BIT (ch); + if ( (is_punct && ISPUNCT (ch)) + || (is_space && ISSPACE (ch)) + || (is_upper && ISUPPER (ch)) + || (is_xdigit && ISXDIGIT (ch))) + SET_LIST_BIT (ch); + if ( translate && (is_upper || is_lower) + && (ISUPPER (ch) || ISLOWER (ch))) + SET_LIST_BIT (ch); + } + had_char_class = true; +#endif /* libc || wctype.h */ + } + else + { + c1++; + while (c1--) + PATUNFETCH; + SET_LIST_BIT ('['); + SET_LIST_BIT (':'); + had_char_class = false; + } + } + else + { + had_char_class = false; + SET_LIST_BIT (c); + } + } + + /* Discard any (non)matching list bytes that are all 0 at the + end of the map. Decrease the map-length byte too. */ + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + b += b[-1]; + } + break; + + + case '(': + if (syntax & RE_NO_BK_PARENS) + goto handle_open; + else + goto normal_char; + + + case ')': + if (syntax & RE_NO_BK_PARENS) + goto handle_close; + else + goto normal_char; + + + case '\n': + if (syntax & RE_NEWLINE_ALT) + goto handle_alt; + else + goto normal_char; + + + case '|': + if (syntax & RE_NO_BK_VBAR) + goto handle_alt; + else + goto normal_char; + + + case '{': + if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES) + goto handle_interval; + else + goto normal_char; + + + case '\\': + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); + + /* Do not translate the character after the \, so that we can + distinguish, e.g., \B from \b, even if we normally would + translate, e.g., B to b. */ + PATFETCH_RAW (c); + + switch (c) + { + case '(': + if (syntax & RE_NO_BK_PARENS) + goto normal_backslash; + + handle_open: + bufp->re_nsub++; + regnum++; + + if (COMPILE_STACK_FULL) + { + RETALLOC (compile_stack.stack, compile_stack.size << 1, + compile_stack_elt_t); + if (compile_stack.stack == NULL) return REG_ESPACE; + + compile_stack.size <<= 1; + } + + /* These are the values to restore when we hit end of this + group. They are all relative offsets, so that if the + whole pattern moves because of realloc, they will still + be valid. */ + COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer; + COMPILE_STACK_TOP.fixup_alt_jump + = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0; + COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer; + COMPILE_STACK_TOP.regnum = regnum; + + /* We will eventually replace the 0 with the number of + groups inner to this one. But do not push a + start_memory for groups beyond the last one we can + represent in the compiled pattern. */ + if (regnum <= MAX_REGNUM) + { + COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2; + BUF_PUSH_3 (start_memory, regnum, 0); + } + + compile_stack.avail++; + + fixup_alt_jump = 0; + laststart = 0; + begalt = b; + /* If we've reached MAX_REGNUM groups, then this open + won't actually generate any code, so we'll have to + clear pending_exact explicitly. */ + pending_exact = 0; + break; + + + case ')': + if (syntax & RE_NO_BK_PARENS) goto normal_backslash; + + if (COMPILE_STACK_EMPTY) + { + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_backslash; + else + FREE_STACK_RETURN (REG_ERPAREN); + } + + handle_close: + if (fixup_alt_jump) + { /* Push a dummy failure point at the end of the + alternative for a possible future + `pop_failure_jump' to pop. See comments at + `push_dummy_failure' in `re_match_2'. */ + BUF_PUSH (push_dummy_failure); + + /* We allocated space for this jump when we assigned + to `fixup_alt_jump', in the `handle_alt' case below. */ + STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1); + } + + /* See similar code for backslashed left paren above. */ + if (COMPILE_STACK_EMPTY) + { + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_char; + else + FREE_STACK_RETURN (REG_ERPAREN); + } + + /* Since we just checked for an empty stack above, this + ``can't happen''. */ + assert (compile_stack.avail != 0); + { + /* We don't just want to restore into `regnum', because + later groups should continue to be numbered higher, + as in `(ab)c(de)' -- the second group is #2. */ + regnum_t this_group_regnum; + + compile_stack.avail--; + begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset; + fixup_alt_jump + = COMPILE_STACK_TOP.fixup_alt_jump + ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 + : 0; + laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset; + this_group_regnum = COMPILE_STACK_TOP.regnum; + /* If we've reached MAX_REGNUM groups, then this open + won't actually generate any code, so we'll have to + clear pending_exact explicitly. */ + pending_exact = 0; + + /* We're at the end of the group, so now we know how many + groups were inside this one. */ + if (this_group_regnum <= MAX_REGNUM) + { + unsigned char *inner_group_loc + = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset; + + *inner_group_loc = regnum - this_group_regnum; + BUF_PUSH_3 (stop_memory, this_group_regnum, + regnum - this_group_regnum); + } + } + break; + + + case '|': /* `\|'. */ + if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR) + goto normal_backslash; + handle_alt: + if (syntax & RE_LIMITED_OPS) + goto normal_char; + + /* Insert before the previous alternative a jump which + jumps to this alternative if the former fails. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (on_failure_jump, begalt, b + 6); + pending_exact = 0; + b += 3; + + /* The alternative before this one has a jump after it + which gets executed if it gets matched. Adjust that + jump so it will jump to this alternative's analogous + jump (put in below, which in turn will jump to the next + (if any) alternative's such jump, etc.). The last such + jump jumps to the correct final destination. A picture: + _____ _____ + | | | | + | v | v + a | b | c + + If we are at `b', then fixup_alt_jump right now points to a + three-byte space after `a'. We'll put in the jump, set + fixup_alt_jump to right after `b', and leave behind three + bytes which we'll fill in when we get to after `c'. */ + + if (fixup_alt_jump) + STORE_JUMP (jump_past_alt, fixup_alt_jump, b); + + /* Mark and leave space for a jump after this alternative, + to be filled in later either by next alternative or + when know we're at the end of a series of alternatives. */ + fixup_alt_jump = b; + GET_BUFFER_SPACE (3); + b += 3; + + laststart = 0; + begalt = b; + break; + + + case '{': + /* If \{ is a literal. */ + if (!(syntax & RE_INTERVALS) + /* If we're at `\{' and it's not the open-interval + operator. */ + || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + || (p - 2 == pattern && p == pend)) + goto normal_backslash; + + handle_interval: + { + /* If got here, then the syntax allows intervals. */ + + /* At least (most) this many matches must be made. */ + int lower_bound = -1, upper_bound = -1; + + beg_interval = p - 1; + + if (p == pend) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_EBRACE); + } + + GET_UNSIGNED_NUMBER (lower_bound); + + if (c == ',') + { + GET_UNSIGNED_NUMBER (upper_bound); + if (upper_bound < 0) upper_bound = RE_DUP_MAX; + } + else + /* Interval such as `{1}' => match exactly once. */ + upper_bound = lower_bound; + + if (lower_bound < 0 || upper_bound > RE_DUP_MAX + || lower_bound > upper_bound) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_BADBR); + } + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (c != '\\') FREE_STACK_RETURN (REG_EBRACE); + + PATFETCH (c); + } + + if (c != '}') + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_BADBR); + } + + /* We just parsed a valid interval. */ + + /* If it's invalid to have no preceding re. */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + FREE_STACK_RETURN (REG_BADRPT); + else if (syntax & RE_CONTEXT_INDEP_OPS) + laststart = b; + else + goto unfetch_interval; + } + + /* If the upper bound is zero, don't want to succeed at + all; jump from `laststart' to `b + 3', which will be + the end of the buffer after we insert the jump. */ + if (upper_bound == 0) + { + GET_BUFFER_SPACE (3); + INSERT_JUMP (jump, laststart, b + 3); + b += 3; + } + + /* Otherwise, we have a nontrivial interval. When + we're all done, the pattern will look like: + set_number_at <jump count> <upper bound> + set_number_at <succeed_n count> <lower bound> + succeed_n <after jump addr> <succeed_n count> + <body of loop> + jump_n <succeed_n addr> <jump count> + (The upper bound and `jump_n' are omitted if + `upper_bound' is 1, though.) */ + else + { /* If the upper bound is > 1, we need to insert + more at the end of the loop. */ + unsigned nbytes = 10 + (upper_bound > 1) * 10; + + GET_BUFFER_SPACE (nbytes); + + /* Initialize lower bound of the `succeed_n', even + though it will be set during matching by its + attendant `set_number_at' (inserted next), + because `re_compile_fastmap' needs to know. + Jump to the `jump_n' we might insert below. */ + INSERT_JUMP2 (succeed_n, laststart, + b + 5 + (upper_bound > 1) * 5, + lower_bound); + b += 5; + + /* Code to initialize the lower bound. Insert + before the `succeed_n'. The `5' is the last two + bytes of this `set_number_at', plus 3 bytes of + the following `succeed_n'. */ + insert_op2 (set_number_at, laststart, 5, lower_bound, b); + b += 5; + + if (upper_bound > 1) + { /* More than one repetition is allowed, so + append a backward jump to the `succeed_n' + that starts this interval. + + When we've reached this during matching, + we'll have matched the interval once, so + jump back only `upper_bound - 1' times. */ + STORE_JUMP2 (jump_n, b, laststart + 5, + upper_bound - 1); + b += 5; + + /* The location we want to set is the second + parameter of the `jump_n'; that is `b-2' as + an absolute address. `laststart' will be + the `set_number_at' we're about to insert; + `laststart+3' the number to set, the source + for the relative address. But we are + inserting into the middle of the pattern -- + so everything is getting moved up by 5. + Conclusion: (b - 2) - (laststart + 3) + 5, + i.e., b - laststart. + + We insert this at the beginning of the loop + so that if we fail during matching, we'll + reinitialize the bounds. */ + insert_op2 (set_number_at, laststart, b - laststart, + upper_bound - 1, b); + b += 5; + } + } + pending_exact = 0; + beg_interval = NULL; + } + break; + + unfetch_interval: + /* If an invalid interval, match the characters as literals. */ + assert (beg_interval); + p = beg_interval; + beg_interval = NULL; + + /* normal_char and normal_backslash need `c'. */ + PATFETCH (c); + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (p > pattern && p[-1] == '\\') + goto normal_backslash; + } + goto normal_char; + +#ifdef emacs + /* There is no way to specify the before_dot and after_dot + operators. rms says this is ok. --karl */ + case '=': + BUF_PUSH (at_dot); + break; + + case 's': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]); + break; + + case 'S': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]); + break; +#endif /* emacs */ + + + case 'w': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + laststart = b; + BUF_PUSH (wordchar); + break; + + + case 'W': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + laststart = b; + BUF_PUSH (notwordchar); + break; + + + case '<': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (wordbeg); + break; + + case '>': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (wordend); + break; + + case 'b': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (wordbound); + break; + + case 'B': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (notwordbound); + break; + + case '`': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (begbuf); + break; + + case '\'': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (endbuf); + break; + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + if (syntax & RE_NO_BK_REFS) + goto normal_char; + + c1 = c - '0'; + + if (c1 > regnum) + FREE_STACK_RETURN (REG_ESUBREG); + + /* Can't back reference to a subexpression if inside of it. */ + if (group_in_compile_stack (compile_stack, (regnum_t) c1)) + goto normal_char; + + laststart = b; + BUF_PUSH_2 (duplicate, c1); + break; + + + case '+': + case '?': + if (syntax & RE_BK_PLUS_QM) + goto handle_plus; + else + goto normal_backslash; + + default: + normal_backslash: + /* You might think it would be useful for \ to mean + not to translate; but if we don't translate it + it will never match anything. */ + c = TRANSLATE (c); + goto normal_char; + } + break; + + + default: + /* Expects the character in `c'. */ + normal_char: + /* If no exactn currently being built. */ + if (!pending_exact + + /* If last exactn not at current position. */ + || pending_exact + *pending_exact + 1 != b + + /* We have only one byte following the exactn for the count. */ + || *pending_exact == (1 << BYTEWIDTH) - 1 + + /* If followed by a repetition operator. */ + || *p == '*' || *p == '^' + || ((syntax & RE_BK_PLUS_QM) + ? *p == '\\' && (p[1] == '+' || p[1] == '?') + : (*p == '+' || *p == '?')) + || ((syntax & RE_INTERVALS) + && ((syntax & RE_NO_BK_BRACES) + ? *p == '{' + : (p[0] == '\\' && p[1] == '{')))) + { + /* Start building a new exactn. */ + + laststart = b; + + BUF_PUSH_2 (exactn, 0); + pending_exact = b - 1; + } + + BUF_PUSH (c); + (*pending_exact)++; + break; + } /* switch (c) */ + } /* while p != pend */ + + + /* Through the pattern now. */ + + if (fixup_alt_jump) + STORE_JUMP (jump_past_alt, fixup_alt_jump, b); + + if (!COMPILE_STACK_EMPTY) + FREE_STACK_RETURN (REG_EPAREN); + + /* If we don't want backtracking, force success + the first time we reach the end of the compiled pattern. */ + if (syntax & RE_NO_POSIX_BACKTRACKING) + BUF_PUSH (succeed); + + free (compile_stack.stack); + + /* We have succeeded; set the length of the buffer. */ + bufp->used = b - bufp->buffer; + +#ifdef DEBUG + if (debug) + { + DEBUG_PRINT1 ("\nCompiled pattern: \n"); + print_compiled_pattern (bufp); + } +#endif /* DEBUG */ + +#ifndef MATCH_MAY_ALLOCATE + /* Initialize the failure stack to the largest possible stack. This + isn't necessary unless we're trying to avoid calling alloca in + the search and match routines. */ + { + int num_regs = bufp->re_nsub + 1; + + /* Since DOUBLE_FAIL_STACK refuses to double only if the current size + is strictly greater than re_max_failures, the largest possible stack + is 2 * re_max_failures failure points. */ + if (fail_stack.size < (2 * re_max_failures * MAX_FAILURE_ITEMS)) + { + fail_stack.size = (2 * re_max_failures * MAX_FAILURE_ITEMS); + +# ifdef emacs + if (! fail_stack.stack) + fail_stack.stack + = (fail_stack_elt_t *) xmalloc (fail_stack.size + * sizeof (fail_stack_elt_t)); + else + fail_stack.stack + = (fail_stack_elt_t *) xrealloc (fail_stack.stack, + (fail_stack.size + * sizeof (fail_stack_elt_t))); +# else /* not emacs */ + if (! fail_stack.stack) + fail_stack.stack + = (fail_stack_elt_t *) malloc (fail_stack.size + * sizeof (fail_stack_elt_t)); + else + fail_stack.stack + = (fail_stack_elt_t *) realloc (fail_stack.stack, + (fail_stack.size + * sizeof (fail_stack_elt_t))); +# endif /* not emacs */ + } + + regex_grow_registers (num_regs); + } +#endif /* not MATCH_MAY_ALLOCATE */ + + return REG_NOERROR; +} /* regex_compile */ + +/* Subroutines for `regex_compile'. */ + +/* Store OP at LOC followed by two-byte integer parameter ARG. */ + +static void +store_op1 (op, loc, arg) + re_opcode_t op; + unsigned char *loc; + int arg; +{ + *loc = (unsigned char) op; + STORE_NUMBER (loc + 1, arg); +} + + +/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */ + +static void +store_op2 (op, loc, arg1, arg2) + re_opcode_t op; + unsigned char *loc; + int arg1, arg2; +{ + *loc = (unsigned char) op; + STORE_NUMBER (loc + 1, arg1); + STORE_NUMBER (loc + 3, arg2); +} + + +/* Copy the bytes from LOC to END to open up three bytes of space at LOC + for OP followed by two-byte integer parameter ARG. */ + +static void +insert_op1 (op, loc, arg, end) + re_opcode_t op; + unsigned char *loc; + int arg; + unsigned char *end; +{ + register unsigned char *pfrom = end; + register unsigned char *pto = end + 3; + + while (pfrom != loc) + *--pto = *--pfrom; + + store_op1 (op, loc, arg); +} + + +/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */ + +static void +insert_op2 (op, loc, arg1, arg2, end) + re_opcode_t op; + unsigned char *loc; + int arg1, arg2; + unsigned char *end; +{ + register unsigned char *pfrom = end; + register unsigned char *pto = end + 5; + + while (pfrom != loc) + *--pto = *--pfrom; + + store_op2 (op, loc, arg1, arg2); +} + + +/* P points to just after a ^ in PATTERN. Return true if that ^ comes + after an alternative or a begin-subexpression. We assume there is at + least one character before the ^. */ + +static boolean +at_begline_loc_p (pattern, p, syntax) + const char *pattern, *p; + reg_syntax_t syntax; +{ + const char *prev = p - 2; + boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\'; + + return + /* After a subexpression? */ + (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash)) + /* After an alternative? */ + || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash)); +} + + +/* The dual of at_begline_loc_p. This one is for $. We assume there is + at least one character after the $, i.e., `P < PEND'. */ + +static boolean +at_endline_loc_p (p, pend, syntax) + const char *p, *pend; + reg_syntax_t syntax; +{ + const char *next = p; + boolean next_backslash = *next == '\\'; + const char *next_next = p + 1 < pend ? p + 1 : 0; + + return + /* Before a subexpression? */ + (syntax & RE_NO_BK_PARENS ? *next == ')' + : next_backslash && next_next && *next_next == ')') + /* Before an alternative? */ + || (syntax & RE_NO_BK_VBAR ? *next == '|' + : next_backslash && next_next && *next_next == '|'); +} + + +/* Returns true if REGNUM is in one of COMPILE_STACK's elements and + false if it's not. */ + +static boolean +group_in_compile_stack (compile_stack, regnum) + compile_stack_type compile_stack; + regnum_t regnum; +{ + int this_element; + + for (this_element = compile_stack.avail - 1; + this_element >= 0; + this_element--) + if (compile_stack.stack[this_element].regnum == regnum) + return true; + + return false; +} + + +/* Read the ending character of a range (in a bracket expression) from the + uncompiled pattern *P_PTR (which ends at PEND). We assume the + starting character is in `P[-2]'. (`P[-1]' is the character `-'.) + Then we set the translation of all bits between the starting and + ending characters (inclusive) in the compiled pattern B. + + Return an error code. + + We use these short variable names so we can use the same macros as + `regex_compile' itself. */ + +static reg_errcode_t +compile_range (p_ptr, pend, translate, syntax, b) + const char **p_ptr, *pend; + RE_TRANSLATE_TYPE translate; + reg_syntax_t syntax; + unsigned char *b; +{ + unsigned this_char; + + const char *p = *p_ptr; + unsigned int range_start, range_end; + + if (p == pend) + return REG_ERANGE; + + /* Even though the pattern is a signed `char *', we need to fetch + with unsigned char *'s; if the high bit of the pattern character + is set, the range endpoints will be negative if we fetch using a + signed char *. + + We also want to fetch the endpoints without translating them; the + appropriate translation is done in the bit-setting loop below. */ + /* The SVR4 compiler on the 3B2 had trouble with unsigned const char *. */ + range_start = ((const unsigned char *) p)[-2]; + range_end = ((const unsigned char *) p)[0]; + + /* Have to increment the pointer into the pattern string, so the + caller isn't still at the ending character. */ + (*p_ptr)++; + + /* If the start is after the end, the range is empty. */ + if (range_start > range_end) + return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR; + + /* Here we see why `this_char' has to be larger than an `unsigned + char' -- the range is inclusive, so if `range_end' == 0xff + (assuming 8-bit characters), we would otherwise go into an infinite + loop, since all characters <= 0xff. */ + for (this_char = range_start; this_char <= range_end; this_char++) + { + SET_LIST_BIT (TRANSLATE (this_char)); + } + + return REG_NOERROR; +} + +/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in + BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible + characters can start a string that matches the pattern. This fastmap + is used by re_search to skip quickly over impossible starting points. + + The caller must supply the address of a (1 << BYTEWIDTH)-byte data + area as BUFP->fastmap. + + We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in + the pattern buffer. + + Returns 0 if we succeed, -2 if an internal error. */ + +int +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + int j, k; +#ifdef MATCH_MAY_ALLOCATE + fail_stack_type fail_stack; +#endif +#ifndef REGEX_MALLOC + char *destination; +#endif + + register char *fastmap = bufp->fastmap; + unsigned char *pattern = bufp->buffer; + unsigned char *p = pattern; + register unsigned char *pend = pattern + bufp->used; + +#ifdef REL_ALLOC + /* This holds the pointer to the failure stack, when + it is allocated relocatably. */ + fail_stack_elt_t *failure_stack_ptr; +#endif + + /* Assume that each path through the pattern can be null until + proven otherwise. We set this false at the bottom of switch + statement, to which we get only if a particular path doesn't + match the empty string. */ + boolean path_can_be_null = true; + + /* We aren't doing a `succeed_n' to begin with. */ + boolean succeed_n_p = false; + + assert (fastmap != NULL && p != NULL); + + INIT_FAIL_STACK (); + bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */ + bufp->fastmap_accurate = 1; /* It will be when we're done. */ + bufp->can_be_null = 0; + + while (1) + { + if (p == pend || *p == succeed) + { + /* We have reached the (effective) end of pattern. */ + if (!FAIL_STACK_EMPTY ()) + { + bufp->can_be_null |= path_can_be_null; + + /* Reset for next path. */ + path_can_be_null = true; + + p = fail_stack.stack[--fail_stack.avail].pointer; + + continue; + } + else + break; + } + + /* We should never be about to go beyond the end of the pattern. */ + assert (p < pend); + + switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++)) + { + + /* I guess the idea here is to simply not bother with a fastmap + if a backreference is used, since it's too hard to figure out + the fastmap for the corresponding group. Setting + `can_be_null' stops `re_search_2' from using the fastmap, so + that is all we do. */ + case duplicate: + bufp->can_be_null = 1; + goto done; + + + /* Following are the cases which match a character. These end + with `break'. */ + + case exactn: + fastmap[p[1]] = 1; + break; + + + case charset: + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) + fastmap[j] = 1; + break; + + + case charset_not: + /* Chars beyond end of map must be allowed. */ + for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) + fastmap[j] = 1; + + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) + fastmap[j] = 1; + break; + + + case wordchar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) == Sword) + fastmap[j] = 1; + break; + + + case notwordchar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) != Sword) + fastmap[j] = 1; + break; + + + case anychar: + { + int fastmap_newline = fastmap['\n']; + + /* `.' matches anything ... */ + for (j = 0; j < (1 << BYTEWIDTH); j++) + fastmap[j] = 1; + + /* ... except perhaps newline. */ + if (!(bufp->syntax & RE_DOT_NEWLINE)) + fastmap['\n'] = fastmap_newline; + + /* Return if we have already set `can_be_null'; if we have, + then the fastmap is irrelevant. Something's wrong here. */ + else if (bufp->can_be_null) + goto done; + + /* Otherwise, have to check alternative paths. */ + break; + } + +#ifdef emacs + case syntaxspec: + k = *p++; + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) == (enum syntaxcode) k) + fastmap[j] = 1; + break; + + + case notsyntaxspec: + k = *p++; + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) != (enum syntaxcode) k) + fastmap[j] = 1; + break; + + + /* All cases after this match the empty string. These end with + `continue'. */ + + + case before_dot: + case at_dot: + case after_dot: + continue; +#endif /* emacs */ + + + case no_op: + case begline: + case endline: + case begbuf: + case endbuf: + case wordbound: + case notwordbound: + case wordbeg: + case wordend: + case push_dummy_failure: + continue; + + + case jump_n: + case pop_failure_jump: + case maybe_pop_jump: + case jump: + case jump_past_alt: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + if (j > 0) + continue; + + /* Jump backward implies we just went through the body of a + loop and matched nothing. Opcode jumped to should be + `on_failure_jump' or `succeed_n'. Just treat it like an + ordinary jump. For a * loop, it has pushed its failure + point already; if so, discard that as redundant. */ + if ((re_opcode_t) *p != on_failure_jump + && (re_opcode_t) *p != succeed_n) + continue; + + p++; + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + + /* If what's on the stack is where we are now, pop it. */ + if (!FAIL_STACK_EMPTY () + && fail_stack.stack[fail_stack.avail - 1].pointer == p) + fail_stack.avail--; + + continue; + + + case on_failure_jump: + case on_failure_keep_string_jump: + handle_on_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + + /* For some patterns, e.g., `(a?)?', `p+j' here points to the + end of the pattern. We don't want to push such a point, + since when we restore it above, entering the switch will + increment `p' past the end of the pattern. We don't need + to push such a point since we obviously won't find any more + fastmap entries beyond `pend'. Such a pattern can match + the null string, though. */ + if (p + j < pend) + { + if (!PUSH_PATTERN_OP (p + j, fail_stack)) + { + RESET_FAIL_STACK (); + return -2; + } + } + else + bufp->can_be_null = 1; + + if (succeed_n_p) + { + EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ + succeed_n_p = false; + } + + continue; + + + case succeed_n: + /* Get to the number of times to succeed. */ + p += 2; + + /* Increment p past the n for when k != 0. */ + EXTRACT_NUMBER_AND_INCR (k, p); + if (k == 0) + { + p -= 4; + succeed_n_p = true; /* Spaghetti code alert. */ + goto handle_on_failure_jump; + } + continue; + + + case set_number_at: + p += 4; + continue; + + + case start_memory: + case stop_memory: + p += 2; + continue; + + + default: + abort (); /* We have listed all the cases. */ + } /* switch *p++ */ + + /* Getting here means we have found the possible starting + characters for one path of the pattern -- and that the empty + string does not match. We need not follow this path further. + Instead, look at the next alternative (remembered on the + stack), or quit if no more. The test at the top of the loop + does these things. */ + path_can_be_null = false; + p = pend; + } /* while p */ + + /* Set `can_be_null' for the last path (also the first path, if the + pattern is empty). */ + bufp->can_be_null |= path_can_be_null; + + done: + RESET_FAIL_STACK (); + return 0; +} /* re_compile_fastmap */ +#ifdef _LIBC +weak_alias (__re_compile_fastmap, re_compile_fastmap) +#endif + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use + this memory for recording register information. STARTS and ENDS + must be allocated using the malloc library routine, and must each + be at least NUM_REGS * sizeof (regoff_t) bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ + +void +re_set_registers (bufp, regs, num_regs, starts, ends) + struct re_pattern_buffer *bufp; + struct re_registers *regs; + unsigned num_regs; + regoff_t *starts, *ends; +{ + if (num_regs) + { + bufp->regs_allocated = REGS_REALLOCATE; + regs->num_regs = num_regs; + regs->start = starts; + regs->end = ends; + } + else + { + bufp->regs_allocated = REGS_UNALLOCATED; + regs->num_regs = 0; + regs->start = regs->end = (regoff_t *) 0; + } +} +#ifdef _LIBC +weak_alias (__re_set_registers, re_set_registers) +#endif + +/* Searching routines. */ + +/* Like re_search_2, below, but only one string is specified, and + doesn't let you say where to stop matching. */ + +int +re_search (bufp, string, size, startpos, range, regs) + struct re_pattern_buffer *bufp; + const char *string; + int size, startpos, range; + struct re_registers *regs; +{ + return re_search_2 (bufp, NULL, 0, string, size, startpos, range, + regs, size); +} +#ifdef _LIBC +weak_alias (__re_search, re_search) +#endif + + +/* Using the compiled pattern in BUFP->buffer, first tries to match the + virtual concatenation of STRING1 and STRING2, starting first at index + STARTPOS, then at STARTPOS + 1, and so on. + + STRING1 and STRING2 have length SIZE1 and SIZE2, respectively. + + RANGE is how far to scan while trying to match. RANGE = 0 means try + only at STARTPOS; in general, the last start tried is STARTPOS + + RANGE. + + In REGS, return the indices of the virtual concatenation of STRING1 + and STRING2 that matched the entire BUFP->buffer and its contained + subexpressions. + + Do not consider matching one past the index STOP in the virtual + concatenation of STRING1 and STRING2. + + We return either the position in the strings at which the match was + found, -1 if no match, or -2 if error (such as failure + stack overflow). */ + +int +re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int startpos; + int range; + struct re_registers *regs; + int stop; +{ + int val; + register char *fastmap = bufp->fastmap; + register RE_TRANSLATE_TYPE translate = bufp->translate; + int total_size = size1 + size2; + int endpos = startpos + range; + + /* Check for out-of-range STARTPOS. */ + if (startpos < 0 || startpos > total_size) + return -1; + + /* Fix up RANGE if it might eventually take us outside + the virtual concatenation of STRING1 and STRING2. + Make sure we won't move STARTPOS below 0 or above TOTAL_SIZE. */ + if (endpos < 0) + range = 0 - startpos; + else if (endpos > total_size) + range = total_size - startpos; + + /* If the search isn't to be a backwards one, don't waste time in a + search for a pattern that must be anchored. */ + if (bufp->used > 0 && range > 0 + && ((re_opcode_t) bufp->buffer[0] == begbuf + /* `begline' is like `begbuf' if it cannot match at newlines. */ + || ((re_opcode_t) bufp->buffer[0] == begline + && !bufp->newline_anchor))) + { + if (startpos > 0) + return -1; + else + range = 1; + } + +#ifdef emacs + /* In a forward search for something that starts with \=. + don't keep searching past point. */ + if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == at_dot && range > 0) + { + range = PT - startpos; + if (range <= 0) + return -1; + } +#endif /* emacs */ + + /* Update the fastmap now if not correct already. */ + if (fastmap && !bufp->fastmap_accurate) + if (re_compile_fastmap (bufp) == -2) + return -2; + + /* Loop through the string, looking for a place to start matching. */ + for (;;) + { + /* If a fastmap is supplied, skip quickly over characters that + cannot be the start of a match. If the pattern can match the + null string, however, we don't need to skip characters; we want + the first null string. */ + if (fastmap && startpos < total_size && !bufp->can_be_null) + { + if (range > 0) /* Searching forwards. */ + { + register const char *d; + register int lim = 0; + int irange = range; + + if (startpos < size1 && startpos + range >= size1) + lim = range - (size1 - startpos); + + d = (startpos >= size1 ? string2 - size1 : string1) + startpos; + + /* Written out as an if-else to avoid testing `translate' + inside the loop. */ + if (translate) + while (range > lim + && !fastmap[(unsigned char) + translate[(unsigned char) *d++]]) + range--; + else + while (range > lim && !fastmap[(unsigned char) *d++]) + range--; + + startpos += irange - range; + } + else /* Searching backwards. */ + { + register char c = (size1 == 0 || startpos >= size1 + ? string2[startpos - size1] + : string1[startpos]); + + if (!fastmap[(unsigned char) TRANSLATE (c)]) + goto advance; + } + } + + /* If can't match the null string, and that's all we have left, fail. */ + if (range >= 0 && startpos == total_size && fastmap + && !bufp->can_be_null) + return -1; + + val = re_match_2_internal (bufp, string1, size1, string2, size2, + startpos, regs, stop); +#ifndef REGEX_MALLOC +# ifdef C_ALLOCA + alloca (0); +# endif +#endif + + if (val >= 0) + return startpos; + + if (val == -2) + return -2; + + advance: + if (!range) + break; + else if (range > 0) + { + range--; + startpos++; + } + else + { + range++; + startpos--; + } + } + return -1; +} /* re_search_2 */ +#ifdef _LIBC +weak_alias (__re_search_2, re_search_2) +#endif + +/* This converts PTR, a pointer into one of the search strings `string1' + and `string2' into an offset from the beginning of that string. */ +#define POINTER_TO_OFFSET(ptr) \ + (FIRST_STRING_P (ptr) \ + ? ((regoff_t) ((ptr) - string1)) \ + : ((regoff_t) ((ptr) - string2 + size1))) + +/* Macros for dealing with the split strings in re_match_2. */ + +#define MATCHING_IN_FIRST_STRING (dend == end_match_1) + +/* Call before fetching a character with *d. This switches over to + string2 if necessary. */ +#define PREFETCH() \ + while (d == dend) \ + { \ + /* End of string2 => fail. */ \ + if (dend == end_match_2) \ + goto fail; \ + /* End of string1 => advance to string2. */ \ + d = string2; \ + dend = end_match_2; \ + } + + +/* Test if at very beginning or at very end of the virtual concatenation + of `string1' and `string2'. If only one string, it's `string2'. */ +#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2) +#define AT_STRINGS_END(d) ((d) == end2) + + +/* Test if D points to a character which is word-constituent. We have + two special cases to check for: if past the end of string1, look at + the first character in string2; and if before the beginning of + string2, look at the last character in string1. */ +#define WORDCHAR_P(d) \ + (SYNTAX ((d) == end1 ? *string2 \ + : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \ + == Sword) + +/* Disabled due to a compiler bug -- see comment at case wordbound */ +#if 0 +/* Test if the character before D and the one at D differ with respect + to being word-constituent. */ +#define AT_WORD_BOUNDARY(d) \ + (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \ + || WORDCHAR_P (d - 1) != WORDCHAR_P (d)) +#endif + +/* Free everything we malloc. */ +#ifdef MATCH_MAY_ALLOCATE +# define FREE_VAR(var) if (var) REGEX_FREE (var); var = NULL +# define FREE_VARIABLES() \ + do { \ + REGEX_FREE_STACK (fail_stack.stack); \ + FREE_VAR (regstart); \ + FREE_VAR (regend); \ + FREE_VAR (old_regstart); \ + FREE_VAR (old_regend); \ + FREE_VAR (best_regstart); \ + FREE_VAR (best_regend); \ + FREE_VAR (reg_info); \ + FREE_VAR (reg_dummy); \ + FREE_VAR (reg_info_dummy); \ + } while (0) +#else +# define FREE_VARIABLES() ((void)0) /* Do nothing! But inhibit gcc warning. */ +#endif /* not MATCH_MAY_ALLOCATE */ + +/* These values must meet several constraints. They must not be valid + register values; since we have a limit of 255 registers (because + we use only one byte in the pattern for the register number), we can + use numbers larger than 255. They must differ by 1, because of + NUM_FAILURE_ITEMS above. And the value for the lowest register must + be larger than the value for the highest register, so we do not try + to actually save any registers when none are active. */ +#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH) +#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1) + +/* Matching routines. */ + +#ifndef emacs /* Emacs never uses this. */ +/* re_match is like re_match_2 except it takes only a single string. */ + +int +re_match (bufp, string, size, pos, regs) + struct re_pattern_buffer *bufp; + const char *string; + int size, pos; + struct re_registers *regs; +{ + int result = re_match_2_internal (bufp, NULL, 0, string, size, + pos, regs, size); +# ifndef REGEX_MALLOC +# ifdef C_ALLOCA + alloca (0); +# endif +# endif + return result; +} +# ifdef _LIBC +weak_alias (__re_match, re_match) +# endif +#endif /* not emacs */ + +static boolean group_match_null_string_p _RE_ARGS ((unsigned char **p, + unsigned char *end, + register_info_type *reg_info)); +static boolean alt_match_null_string_p _RE_ARGS ((unsigned char *p, + unsigned char *end, + register_info_type *reg_info)); +static boolean common_op_match_null_string_p _RE_ARGS ((unsigned char **p, + unsigned char *end, + register_info_type *reg_info)); +static int bcmp_translate _RE_ARGS ((const char *s1, const char *s2, + int len, char *translate)); + +/* re_match_2 matches the compiled pattern in BUFP against the + the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1 + and SIZE2, respectively). We start matching at POS, and stop + matching at STOP. + + If REGS is non-null and the `no_sub' field of BUFP is nonzero, we + store offsets for the substring each group matched in REGS. See the + documentation for exactly how many groups we fill. + + We return -1 if no match, -2 if an internal error (such as the + failure stack overflowing). Otherwise, we return the length of the + matched substring. */ + +int +re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int pos; + struct re_registers *regs; + int stop; +{ + int result = re_match_2_internal (bufp, string1, size1, string2, size2, + pos, regs, stop); +#ifndef REGEX_MALLOC +# ifdef C_ALLOCA + alloca (0); +# endif +#endif + return result; +} +#ifdef _LIBC +weak_alias (__re_match_2, re_match_2) +#endif + +/* This is a separate function so that we can force an alloca cleanup + afterwards. */ +static int +re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int pos; + struct re_registers *regs; + int stop; +{ + /* General temporaries. */ + int mcnt; + unsigned char *p1; + + /* Just past the end of the corresponding string. */ + const char *end1, *end2; + + /* Pointers into string1 and string2, just past the last characters in + each to consider matching. */ + const char *end_match_1, *end_match_2; + + /* Where we are in the data, and the end of the current string. */ + const char *d, *dend; + + /* Where we are in the pattern, and the end of the pattern. */ + unsigned char *p = bufp->buffer; + register unsigned char *pend = p + bufp->used; + + /* Mark the opcode just after a start_memory, so we can test for an + empty subpattern when we get to the stop_memory. */ + unsigned char *just_past_start_mem = 0; + + /* We use this to map every character in the string. */ + RE_TRANSLATE_TYPE translate = bufp->translate; + + /* Failure point stack. Each place that can handle a failure further + down the line pushes a failure point on this stack. It consists of + restart, regend, and reg_info for all registers corresponding to + the subexpressions we're currently inside, plus the number of such + registers, and, finally, two char *'s. The first char * is where + to resume scanning the pattern; the second one is where to resume + scanning the strings. If the latter is zero, the failure point is + a ``dummy''; if a failure happens and the failure point is a dummy, + it gets discarded and the next next one is tried. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */ + fail_stack_type fail_stack; +#endif +#ifdef DEBUG + static unsigned failure_id; + unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0; +#endif + +#ifdef REL_ALLOC + /* This holds the pointer to the failure stack, when + it is allocated relocatably. */ + fail_stack_elt_t *failure_stack_ptr; +#endif + + /* We fill all the registers internally, independent of what we + return, for use in backreferences. The number here includes + an element for register zero. */ + size_t num_regs = bufp->re_nsub + 1; + + /* The currently active registers. */ + active_reg_t lowest_active_reg = NO_LOWEST_ACTIVE_REG; + active_reg_t highest_active_reg = NO_HIGHEST_ACTIVE_REG; + + /* Information on the contents of registers. These are pointers into + the input strings; they record just what was matched (on this + attempt) by a subexpression part of the pattern, that is, the + regnum-th regstart pointer points to where in the pattern we began + matching and the regnum-th regend points to right after where we + stopped matching the regnum-th subexpression. (The zeroth register + keeps track of what the whole pattern matches.) */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **regstart, **regend; +#endif + + /* If a group that's operated upon by a repetition operator fails to + match anything, then the register for its start will need to be + restored because it will have been set to wherever in the string we + are when we last see its open-group operator. Similarly for a + register's end. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **old_regstart, **old_regend; +#endif + + /* The is_active field of reg_info helps us keep track of which (possibly + nested) subexpressions we are currently in. The matched_something + field of reg_info[reg_num] helps us tell whether or not we have + matched any of the pattern so far this time through the reg_num-th + subexpression. These two fields get reset each time through any + loop their register is in. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */ + register_info_type *reg_info; +#endif + + /* The following record the register info as found in the above + variables when we find a match better than any we've seen before. + This happens as we backtrack through the failure points, which in + turn happens only if we have not yet matched the entire string. */ + unsigned best_regs_set = false; +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **best_regstart, **best_regend; +#endif + + /* Logically, this is `best_regend[0]'. But we don't want to have to + allocate space for that if we're not allocating space for anything + else (see below). Also, we never need info about register 0 for + any of the other register vectors, and it seems rather a kludge to + treat `best_regend' differently than the rest. So we keep track of + the end of the best match so far in a separate variable. We + initialize this to NULL so that when we backtrack the first time + and need to test it, it's not garbage. */ + const char *match_end = NULL; + + /* This helps SET_REGS_MATCHED avoid doing redundant work. */ + int set_regs_matched_done = 0; + + /* Used when we pop values we don't care about. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **reg_dummy; + register_info_type *reg_info_dummy; +#endif + +#ifdef DEBUG + /* Counts the total number of registers pushed. */ + unsigned num_regs_pushed = 0; +#endif + + DEBUG_PRINT1 ("\n\nEntering re_match_2.\n"); + + INIT_FAIL_STACK (); + +#ifdef MATCH_MAY_ALLOCATE + /* Do not bother to initialize all the register variables if there are + no groups in the pattern, as it takes a fair amount of time. If + there are groups, we include space for register 0 (the whole + pattern), even though we never use it, since it simplifies the + array indexing. We should fix this. */ + if (bufp->re_nsub) + { + regstart = REGEX_TALLOC (num_regs, const char *); + regend = REGEX_TALLOC (num_regs, const char *); + old_regstart = REGEX_TALLOC (num_regs, const char *); + old_regend = REGEX_TALLOC (num_regs, const char *); + best_regstart = REGEX_TALLOC (num_regs, const char *); + best_regend = REGEX_TALLOC (num_regs, const char *); + reg_info = REGEX_TALLOC (num_regs, register_info_type); + reg_dummy = REGEX_TALLOC (num_regs, const char *); + reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type); + + if (!(regstart && regend && old_regstart && old_regend && reg_info + && best_regstart && best_regend && reg_dummy && reg_info_dummy)) + { + FREE_VARIABLES (); + return -2; + } + } + else + { + /* We must initialize all our variables to NULL, so that + `FREE_VARIABLES' doesn't try to free them. */ + regstart = regend = old_regstart = old_regend = best_regstart + = best_regend = reg_dummy = NULL; + reg_info = reg_info_dummy = (register_info_type *) NULL; + } +#endif /* MATCH_MAY_ALLOCATE */ + + /* The starting position is bogus. */ + if (pos < 0 || pos > size1 + size2) + { + FREE_VARIABLES (); + return -1; + } + + /* Initialize subexpression text positions to -1 to mark ones that no + start_memory/stop_memory has been seen for. Also initialize the + register information struct. */ + for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++) + { + regstart[mcnt] = regend[mcnt] + = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE; + + REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE; + IS_ACTIVE (reg_info[mcnt]) = 0; + MATCHED_SOMETHING (reg_info[mcnt]) = 0; + EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0; + } + + /* We move `string1' into `string2' if the latter's empty -- but not if + `string1' is null. */ + if (size2 == 0 && string1 != NULL) + { + string2 = string1; + size2 = size1; + string1 = 0; + size1 = 0; + } + end1 = string1 + size1; + end2 = string2 + size2; + + /* Compute where to stop matching, within the two strings. */ + if (stop <= size1) + { + end_match_1 = string1 + stop; + end_match_2 = string2; + } + else + { + end_match_1 = end1; + end_match_2 = string2 + stop - size1; + } + + /* `p' scans through the pattern as `d' scans through the data. + `dend' is the end of the input string that `d' points within. `d' + is advanced into the following input string whenever necessary, but + this happens before fetching; therefore, at the beginning of the + loop, `d' can be pointing at the end of a string, but it cannot + equal `string2'. */ + if (size1 > 0 && pos <= size1) + { + d = string1 + pos; + dend = end_match_1; + } + else + { + d = string2 + pos - size1; + dend = end_match_2; + } + + DEBUG_PRINT1 ("The compiled pattern is:\n"); + DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend); + DEBUG_PRINT1 ("The string to match is: `"); + DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2); + DEBUG_PRINT1 ("'\n"); + + /* This loops over pattern commands. It exits by returning from the + function if the match is complete, or it drops through if the match + fails at this starting point in the input data. */ + for (;;) + { +#ifdef _LIBC + DEBUG_PRINT2 ("\n%p: ", p); +#else + DEBUG_PRINT2 ("\n0x%x: ", p); +#endif + + if (p == pend) + { /* End of pattern means we might have succeeded. */ + DEBUG_PRINT1 ("end of pattern ... "); + + /* If we haven't matched the entire string, and we want the + longest match, try backtracking. */ + if (d != end_match_2) + { + /* 1 if this match ends in the same string (string1 or string2) + as the best previous match. */ + boolean same_str_p = (FIRST_STRING_P (match_end) + == MATCHING_IN_FIRST_STRING); + /* 1 if this match is the best seen so far. */ + boolean best_match_p; + + /* AIX compiler got confused when this was combined + with the previous declaration. */ + if (same_str_p) + best_match_p = d > match_end; + else + best_match_p = !MATCHING_IN_FIRST_STRING; + + DEBUG_PRINT1 ("backtracking.\n"); + + if (!FAIL_STACK_EMPTY ()) + { /* More failure points to try. */ + + /* If exceeds best match so far, save it. */ + if (!best_regs_set || best_match_p) + { + best_regs_set = true; + match_end = d; + + DEBUG_PRINT1 ("\nSAVING match as best so far.\n"); + + for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++) + { + best_regstart[mcnt] = regstart[mcnt]; + best_regend[mcnt] = regend[mcnt]; + } + } + goto fail; + } + + /* If no failure points, don't restore garbage. And if + last match is real best match, don't restore second + best one. */ + else if (best_regs_set && !best_match_p) + { + restore_best_regs: + /* Restore best match. It may happen that `dend == + end_match_1' while the restored d is in string2. + For example, the pattern `x.*y.*z' against the + strings `x-' and `y-z-', if the two strings are + not consecutive in memory. */ + DEBUG_PRINT1 ("Restoring best registers.\n"); + + d = match_end; + dend = ((d >= string1 && d <= end1) + ? end_match_1 : end_match_2); + + for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++) + { + regstart[mcnt] = best_regstart[mcnt]; + regend[mcnt] = best_regend[mcnt]; + } + } + } /* d != end_match_2 */ + + succeed_label: + DEBUG_PRINT1 ("Accepting match.\n"); + + /* If caller wants register contents data back, do it. */ + if (regs && !bufp->no_sub) + { + /* Have the register data arrays been allocated? */ + if (bufp->regs_allocated == REGS_UNALLOCATED) + { /* No. So allocate them with malloc. We need one + extra element beyond `num_regs' for the `-1' marker + GNU code uses. */ + regs->num_regs = MAX (RE_NREGS, num_regs + 1); + regs->start = TALLOC (regs->num_regs, regoff_t); + regs->end = TALLOC (regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + { + FREE_VARIABLES (); + return -2; + } + bufp->regs_allocated = REGS_REALLOCATE; + } + else if (bufp->regs_allocated == REGS_REALLOCATE) + { /* Yes. If we need more elements than were already + allocated, reallocate them. If we need fewer, just + leave it alone. */ + if (regs->num_regs < num_regs + 1) + { + regs->num_regs = num_regs + 1; + RETALLOC (regs->start, regs->num_regs, regoff_t); + RETALLOC (regs->end, regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + { + FREE_VARIABLES (); + return -2; + } + } + } + else + { + /* These braces fend off a "empty body in an else-statement" + warning under GCC when assert expands to nothing. */ + assert (bufp->regs_allocated == REGS_FIXED); + } + + /* Convert the pointer data in `regstart' and `regend' to + indices. Register zero has to be set differently, + since we haven't kept track of any info for it. */ + if (regs->num_regs > 0) + { + regs->start[0] = pos; + regs->end[0] = (MATCHING_IN_FIRST_STRING + ? ((regoff_t) (d - string1)) + : ((regoff_t) (d - string2 + size1))); + } + + /* Go through the first `min (num_regs, regs->num_regs)' + registers, since that is all we initialized. */ + for (mcnt = 1; (unsigned) mcnt < MIN (num_regs, regs->num_regs); + mcnt++) + { + if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt])) + regs->start[mcnt] = regs->end[mcnt] = -1; + else + { + regs->start[mcnt] + = (regoff_t) POINTER_TO_OFFSET (regstart[mcnt]); + regs->end[mcnt] + = (regoff_t) POINTER_TO_OFFSET (regend[mcnt]); + } + } + + /* If the regs structure we return has more elements than + were in the pattern, set the extra elements to -1. If + we (re)allocated the registers, this is the case, + because we always allocate enough to have at least one + -1 at the end. */ + for (mcnt = num_regs; (unsigned) mcnt < regs->num_regs; mcnt++) + regs->start[mcnt] = regs->end[mcnt] = -1; + } /* regs && !bufp->no_sub */ + + DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n", + nfailure_points_pushed, nfailure_points_popped, + nfailure_points_pushed - nfailure_points_popped); + DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed); + + mcnt = d - pos - (MATCHING_IN_FIRST_STRING + ? string1 + : string2 - size1); + + DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt); + + FREE_VARIABLES (); + return mcnt; + } + + /* Otherwise match next pattern command. */ + switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++)) + { + /* Ignore these. Used to ignore the n of succeed_n's which + currently have n == 0. */ + case no_op: + DEBUG_PRINT1 ("EXECUTING no_op.\n"); + break; + + case succeed: + DEBUG_PRINT1 ("EXECUTING succeed.\n"); + goto succeed_label; + + /* Match the next n pattern characters exactly. The following + byte in the pattern defines n, and the n bytes after that + are the characters to match. */ + case exactn: + mcnt = *p++; + DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt); + + /* This is written out as an if-else so we don't waste time + testing `translate' inside the loop. */ + if (translate) + { + do + { + PREFETCH (); + if ((unsigned char) translate[(unsigned char) *d++] + != (unsigned char) *p++) + goto fail; + } + while (--mcnt); + } + else + { + do + { + PREFETCH (); + if (*d++ != (char) *p++) goto fail; + } + while (--mcnt); + } + SET_REGS_MATCHED (); + break; + + + /* Match any character except possibly a newline or a null. */ + case anychar: + DEBUG_PRINT1 ("EXECUTING anychar.\n"); + + PREFETCH (); + + if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n') + || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000')) + goto fail; + + SET_REGS_MATCHED (); + DEBUG_PRINT2 (" Matched `%d'.\n", *d); + d++; + break; + + + case charset: + case charset_not: + { + register unsigned char c; + boolean not = (re_opcode_t) *(p - 1) == charset_not; + + DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : ""); + + PREFETCH (); + c = TRANSLATE (*d); /* The character to match. */ + + /* Cast to `unsigned' instead of `unsigned char' in case the + bit list is a full 32 bytes long. */ + if (c < (unsigned) (*p * BYTEWIDTH) + && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + p += 1 + *p; + + if (!not) goto fail; + + SET_REGS_MATCHED (); + d++; + break; + } + + + /* The beginning of a group is represented by start_memory. + The arguments are the register number in the next byte, and the + number of groups inner to this one in the next. The text + matched within the group is recorded (in the internal + registers data structure) under the register number. */ + case start_memory: + DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]); + + /* Find out if this group can match the empty string. */ + p1 = p; /* To send to group_match_null_string_p. */ + + if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE) + REG_MATCH_NULL_STRING_P (reg_info[*p]) + = group_match_null_string_p (&p1, pend, reg_info); + + /* Save the position in the string where we were the last time + we were at this open-group operator in case the group is + operated upon by a repetition operator, e.g., with `(a*)*b' + against `ab'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regstart[*p]) ? d : regstart[*p] + : regstart[*p]; + DEBUG_PRINT2 (" old_regstart: %d\n", + POINTER_TO_OFFSET (old_regstart[*p])); + + regstart[*p] = d; + DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p])); + + IS_ACTIVE (reg_info[*p]) = 1; + MATCHED_SOMETHING (reg_info[*p]) = 0; + + /* Clear this whenever we change the register activity status. */ + set_regs_matched_done = 0; + + /* This is the new highest active register. */ + highest_active_reg = *p; + + /* If nothing was active before, this is the new lowest active + register. */ + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *p; + + /* Move past the register number and inner group count. */ + p += 2; + just_past_start_mem = p; + + break; + + + /* The stop_memory opcode represents the end of a group. Its + arguments are the same as start_memory's: the register + number, and the number of inner groups. */ + case stop_memory: + DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]); + + /* We need to save the string position the last time we were at + this close-group operator in case the group is operated + upon by a repetition operator, e.g., with `((a*)*(b*)*)*' + against `aba'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regend[*p]) ? d : regend[*p] + : regend[*p]; + DEBUG_PRINT2 (" old_regend: %d\n", + POINTER_TO_OFFSET (old_regend[*p])); + + regend[*p] = d; + DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p])); + + /* This register isn't active anymore. */ + IS_ACTIVE (reg_info[*p]) = 0; + + /* Clear this whenever we change the register activity status. */ + set_regs_matched_done = 0; + + /* If this was the only register active, nothing is active + anymore. */ + if (lowest_active_reg == highest_active_reg) + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + { /* We must scan for the new highest active register, since + it isn't necessarily one less than now: consider + (a(b)c(d(e)f)g). When group 3 ends, after the f), the + new highest active register is 1. */ + unsigned char r = *p - 1; + while (r > 0 && !IS_ACTIVE (reg_info[r])) + r--; + + /* If we end up at register zero, that means that we saved + the registers as the result of an `on_failure_jump', not + a `start_memory', and we jumped to past the innermost + `stop_memory'. For example, in ((.)*) we save + registers 1 and 2 as a result of the *, but when we pop + back to the second ), we are at the stop_memory 1. + Thus, nothing is active. */ + if (r == 0) + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + highest_active_reg = r; + } + + /* If just failed to match something this time around with a + group that's operated on by a repetition operator, try to + force exit from the ``loop'', and restore the register + information for this group that we had before trying this + last match. */ + if ((!MATCHED_SOMETHING (reg_info[*p]) + || just_past_start_mem == p - 1) + && (p + 2) < pend) + { + boolean is_a_jump_n = false; + + p1 = p + 2; + mcnt = 0; + switch ((re_opcode_t) *p1++) + { + case jump_n: + is_a_jump_n = true; + case pop_failure_jump: + case maybe_pop_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if (is_a_jump_n) + p1 += 2; + break; + + default: + /* do nothing */ ; + } + p1 += mcnt; + + /* If the next operation is a jump backwards in the pattern + to an on_failure_jump right before the start_memory + corresponding to this stop_memory, exit from the loop + by forcing a failure after pushing on the stack the + on_failure_jump's jump in the pattern, and d. */ + if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump + && (re_opcode_t) p1[3] == start_memory && p1[4] == *p) + { + /* If this group ever matched anything, then restore + what its registers were before trying this last + failed match, e.g., with `(a*)*b' against `ab' for + regstart[1], and, e.g., with `((a*)*(b*)*)*' + against `aba' for regend[3]. + + Also restore the registers for inner groups for, + e.g., `((a*)(b*))*' against `aba' (register 3 would + otherwise get trashed). */ + + if (EVER_MATCHED_SOMETHING (reg_info[*p])) + { + unsigned r; + + EVER_MATCHED_SOMETHING (reg_info[*p]) = 0; + + /* Restore this and inner groups' (if any) registers. */ + for (r = *p; r < (unsigned) *p + (unsigned) *(p + 1); + r++) + { + regstart[r] = old_regstart[r]; + + /* xx why this test? */ + if (old_regend[r] >= regstart[r]) + regend[r] = old_regend[r]; + } + } + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + PUSH_FAILURE_POINT (p1 + mcnt, d, -2); + + goto fail; + } + } + + /* Move past the register number and the inner group count. */ + p += 2; + break; + + + /* \<digit> has been turned into a `duplicate' command which is + followed by the numeric value of <digit> as the register number. */ + case duplicate: + { + register const char *d2, *dend2; + int regno = *p++; /* Get which register to match against. */ + DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno); + + /* Can't back reference a group which we've never matched. */ + if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno])) + goto fail; + + /* Where in input to try to start matching. */ + d2 = regstart[regno]; + + /* Where to stop matching; if both the place to start and + the place to stop matching are in the same string, then + set to the place to stop, otherwise, for now have to use + the end of the first string. */ + + dend2 = ((FIRST_STRING_P (regstart[regno]) + == FIRST_STRING_P (regend[regno])) + ? regend[regno] : end_match_1); + for (;;) + { + /* If necessary, advance to next segment in register + contents. */ + while (d2 == dend2) + { + if (dend2 == end_match_2) break; + if (dend2 == regend[regno]) break; + + /* End of string1 => advance to string2. */ + d2 = string2; + dend2 = regend[regno]; + } + /* At end of register contents => success */ + if (d2 == dend2) break; + + /* If necessary, advance to next segment in data. */ + PREFETCH (); + + /* How many characters left in this segment to match. */ + mcnt = dend - d; + + /* Want how many consecutive characters we can match in + one shot, so, if necessary, adjust the count. */ + if (mcnt > dend2 - d2) + mcnt = dend2 - d2; + + /* Compare that many; failure if mismatch, else move + past them. */ + if (translate + ? bcmp_translate (d, d2, mcnt, translate) + : memcmp (d, d2, mcnt)) + goto fail; + d += mcnt, d2 += mcnt; + + /* Do this because we've match some characters. */ + SET_REGS_MATCHED (); + } + } + break; + + + /* begline matches the empty string at the beginning of the string + (unless `not_bol' is set in `bufp'), and, if + `newline_anchor' is set, after newlines. */ + case begline: + DEBUG_PRINT1 ("EXECUTING begline.\n"); + + if (AT_STRINGS_BEG (d)) + { + if (!bufp->not_bol) break; + } + else if (d[-1] == '\n' && bufp->newline_anchor) + { + break; + } + /* In all other cases, we fail. */ + goto fail; + + + /* endline is the dual of begline. */ + case endline: + DEBUG_PRINT1 ("EXECUTING endline.\n"); + + if (AT_STRINGS_END (d)) + { + if (!bufp->not_eol) break; + } + + /* We have to ``prefetch'' the next character. */ + else if ((d == end1 ? *string2 : *d) == '\n' + && bufp->newline_anchor) + { + break; + } + goto fail; + + + /* Match at the very beginning of the data. */ + case begbuf: + DEBUG_PRINT1 ("EXECUTING begbuf.\n"); + if (AT_STRINGS_BEG (d)) + break; + goto fail; + + + /* Match at the very end of the data. */ + case endbuf: + DEBUG_PRINT1 ("EXECUTING endbuf.\n"); + if (AT_STRINGS_END (d)) + break; + goto fail; + + + /* on_failure_keep_string_jump is used to optimize `.*\n'. It + pushes NULL as the value for the string on the stack. Then + `pop_failure_point' will keep the current value for the + string, instead of restoring it. To see why, consider + matching `foo\nbar' against `.*\n'. The .* matches the foo; + then the . fails against the \n. But the next thing we want + to do is match the \n against the \n; if we restored the + string value, we would be back at the foo. + + Because this is used only in specific cases, we don't need to + check all the things that `on_failure_jump' does, to make + sure the right things get saved on the stack. Hence we don't + share its code. The only reason to push anything on the + stack at all is that otherwise we would have to change + `anychar's code to do something besides goto fail in this + case; that seems worse than this. */ + case on_failure_keep_string_jump: + DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); +#ifdef _LIBC + DEBUG_PRINT3 (" %d (to %p):\n", mcnt, p + mcnt); +#else + DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt); +#endif + + PUSH_FAILURE_POINT (p + mcnt, NULL, -2); + break; + + + /* Uses of on_failure_jump: + + Each alternative starts with an on_failure_jump that points + to the beginning of the next alternative. Each alternative + except the last ends with a jump that in effect jumps past + the rest of the alternatives. (They really jump to the + ending jump of the following alternative, because tensioning + these jumps is a hassle.) + + Repeats start with an on_failure_jump that points past both + the repetition text and either the following jump or + pop_failure_jump back to this on_failure_jump. */ + case on_failure_jump: + on_failure: + DEBUG_PRINT1 ("EXECUTING on_failure_jump"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); +#ifdef _LIBC + DEBUG_PRINT3 (" %d (to %p)", mcnt, p + mcnt); +#else + DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt); +#endif + + /* If this on_failure_jump comes right before a group (i.e., + the original * applied to a group), save the information + for that group and all inner ones, so that if we fail back + to this point, the group's information will be correct. + For example, in \(a*\)*\1, we need the preceding group, + and in \(zz\(a*\)b*\)\2, we need the inner group. */ + + /* We can't use `p' to check ahead because we push + a failure point to `p + mcnt' after we do this. */ + p1 = p; + + /* We need to skip no_op's before we look for the + start_memory in case this on_failure_jump is happening as + the result of a completed succeed_n, as in \(a\)\{1,3\}b\1 + against aba. */ + while (p1 < pend && (re_opcode_t) *p1 == no_op) + p1++; + + if (p1 < pend && (re_opcode_t) *p1 == start_memory) + { + /* We have a new highest active register now. This will + get reset at the start_memory we are about to get to, + but we will have saved all the registers relevant to + this repetition op, as described above. */ + highest_active_reg = *(p1 + 1) + *(p1 + 2); + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *(p1 + 1); + } + + DEBUG_PRINT1 (":\n"); + PUSH_FAILURE_POINT (p + mcnt, d, -2); + break; + + + /* A smart repeat ends with `maybe_pop_jump'. + We change it to either `pop_failure_jump' or `jump'. */ + case maybe_pop_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt); + { + register unsigned char *p2 = p; + + /* Compare the beginning of the repeat with what in the + pattern follows its end. If we can establish that there + is nothing that they would both match, i.e., that we + would have to backtrack because of (as in, e.g., `a*a') + then we can change to pop_failure_jump, because we'll + never have to backtrack. + + This is not true in the case of alternatives: in + `(a|ab)*' we do need to backtrack to the `ab' alternative + (e.g., if the string was `ab'). But instead of trying to + detect that here, the alternative has put on a dummy + failure point which is what we will end up popping. */ + + /* Skip over open/close-group commands. + If what follows this loop is a ...+ construct, + look at what begins its body, since we will have to + match at least one of that. */ + while (1) + { + if (p2 + 2 < pend + && ((re_opcode_t) *p2 == stop_memory + || (re_opcode_t) *p2 == start_memory)) + p2 += 3; + else if (p2 + 6 < pend + && (re_opcode_t) *p2 == dummy_failure_jump) + p2 += 6; + else + break; + } + + p1 = p + mcnt; + /* p1[0] ... p1[2] are the `on_failure_jump' corresponding + to the `maybe_finalize_jump' of this case. Examine what + follows. */ + + /* If we're at the end of the pattern, we can change. */ + if (p2 == pend) + { + /* Consider what happens when matching ":\(.*\)" + against ":/". I don't really understand this code + yet. */ + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 + (" End of pattern: change to `pop_failure_jump'.\n"); + } + + else if ((re_opcode_t) *p2 == exactn + || (bufp->newline_anchor && (re_opcode_t) *p2 == endline)) + { + register unsigned char c + = *p2 == (unsigned char) endline ? '\n' : p2[2]; + + if ((re_opcode_t) p1[3] == exactn && p1[5] != c) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", + c, p1[5]); + } + + else if ((re_opcode_t) p1[3] == charset + || (re_opcode_t) p1[3] == charset_not) + { + int not = (re_opcode_t) p1[3] == charset_not; + + if (c < (unsigned char) (p1[4] * BYTEWIDTH) + && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + /* `not' is equal to 1 if c would match, which means + that we can't change to pop_failure_jump. */ + if (!not) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + } + else if ((re_opcode_t) *p2 == charset) + { +#ifdef DEBUG + register unsigned char c + = *p2 == (unsigned char) endline ? '\n' : p2[2]; +#endif + +#if 0 + if ((re_opcode_t) p1[3] == exactn + && ! ((int) p2[1] * BYTEWIDTH > (int) p1[5] + && (p2[2 + p1[5] / BYTEWIDTH] + & (1 << (p1[5] % BYTEWIDTH))))) +#else + if ((re_opcode_t) p1[3] == exactn + && ! ((int) p2[1] * BYTEWIDTH > (int) p1[4] + && (p2[2 + p1[4] / BYTEWIDTH] + & (1 << (p1[4] % BYTEWIDTH))))) +#endif + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", + c, p1[5]); + } + + else if ((re_opcode_t) p1[3] == charset_not) + { + int idx; + /* We win if the charset_not inside the loop + lists every character listed in the charset after. */ + for (idx = 0; idx < (int) p2[1]; idx++) + if (! (p2[2 + idx] == 0 + || (idx < (int) p1[4] + && ((p2[2 + idx] & ~ p1[5 + idx]) == 0)))) + break; + + if (idx == p2[1]) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + else if ((re_opcode_t) p1[3] == charset) + { + int idx; + /* We win if the charset inside the loop + has no overlap with the one after the loop. */ + for (idx = 0; + idx < (int) p2[1] && idx < (int) p1[4]; + idx++) + if ((p2[2 + idx] & p1[5 + idx]) != 0) + break; + + if (idx == p2[1] || idx == p1[4]) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + } + } + p -= 2; /* Point at relative address again. */ + if ((re_opcode_t) p[-1] != pop_failure_jump) + { + p[-1] = (unsigned char) jump; + DEBUG_PRINT1 (" Match => jump.\n"); + goto unconditional_jump; + } + /* Note fall through. */ + + + /* The end of a simple repeat has a pop_failure_jump back to + its matching on_failure_jump, where the latter will push a + failure point. The pop_failure_jump takes off failure + points put on by this pop_failure_jump's matching + on_failure_jump; we got through the pattern to here from the + matching on_failure_jump, so didn't fail. */ + case pop_failure_jump: + { + /* We need to pass separate storage for the lowest and + highest registers, even though we don't care about the + actual values. Otherwise, we will restore only one + register from the stack, since lowest will == highest in + `pop_failure_point'. */ + active_reg_t dummy_low_reg, dummy_high_reg; + unsigned char *pdummy; + const char *sdummy; + + DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n"); + POP_FAILURE_POINT (sdummy, pdummy, + dummy_low_reg, dummy_high_reg, + reg_dummy, reg_dummy, reg_info_dummy); + } + /* Note fall through. */ + + unconditional_jump: +#ifdef _LIBC + DEBUG_PRINT2 ("\n%p: ", p); +#else + DEBUG_PRINT2 ("\n0x%x: ", p); +#endif + /* Note fall through. */ + + /* Unconditionally jump (without popping any failure points). */ + case jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */ + DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt); + p += mcnt; /* Do the jump. */ +#ifdef _LIBC + DEBUG_PRINT2 ("(to %p).\n", p); +#else + DEBUG_PRINT2 ("(to 0x%x).\n", p); +#endif + break; + + + /* We need this opcode so we can detect where alternatives end + in `group_match_null_string_p' et al. */ + case jump_past_alt: + DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n"); + goto unconditional_jump; + + + /* Normally, the on_failure_jump pushes a failure point, which + then gets popped at pop_failure_jump. We will end up at + pop_failure_jump, also, and with a pattern of, say, `a+', we + are skipping over the on_failure_jump, so we have to push + something meaningless for pop_failure_jump to pop. */ + case dummy_failure_jump: + DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n"); + /* It doesn't matter what we push for the string here. What + the code at `fail' tests is the value for the pattern. */ + PUSH_FAILURE_POINT (NULL, NULL, -2); + goto unconditional_jump; + + + /* At the end of an alternative, we need to push a dummy failure + point in case we are followed by a `pop_failure_jump', because + we don't want the failure point for the alternative to be + popped. For example, matching `(a|ab)*' against `aab' + requires that we match the `ab' alternative. */ + case push_dummy_failure: + DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n"); + /* See comments just above at `dummy_failure_jump' about the + two zeroes. */ + PUSH_FAILURE_POINT (NULL, NULL, -2); + break; + + /* Have to succeed matching what follows at least n times. + After that, handle like `on_failure_jump'. */ + case succeed_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt); + + assert (mcnt >= 0); + /* Originally, this is how many times we HAVE to succeed. */ + if (mcnt > 0) + { + mcnt--; + p += 2; + STORE_NUMBER_AND_INCR (p, mcnt); +#ifdef _LIBC + DEBUG_PRINT3 (" Setting %p to %d.\n", p - 2, mcnt); +#else + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p - 2, mcnt); +#endif + } + else if (mcnt == 0) + { +#ifdef _LIBC + DEBUG_PRINT2 (" Setting two bytes from %p to no_op.\n", p+2); +#else + DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2); +#endif + p[2] = (unsigned char) no_op; + p[3] = (unsigned char) no_op; + goto on_failure; + } + break; + + case jump_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt); + + /* Originally, this is how many times we CAN jump. */ + if (mcnt) + { + mcnt--; + STORE_NUMBER (p + 2, mcnt); +#ifdef _LIBC + DEBUG_PRINT3 (" Setting %p to %d.\n", p + 2, mcnt); +#else + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p + 2, mcnt); +#endif + goto unconditional_jump; + } + /* If don't have to jump any more, skip over the rest of command. */ + else + p += 4; + break; + + case set_number_at: + { + DEBUG_PRINT1 ("EXECUTING set_number_at.\n"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p1 = p + mcnt; + EXTRACT_NUMBER_AND_INCR (mcnt, p); +#ifdef _LIBC + DEBUG_PRINT3 (" Setting %p to %d.\n", p1, mcnt); +#else + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt); +#endif + STORE_NUMBER (p1, mcnt); + break; + } + +#if 0 + /* The DEC Alpha C compiler 3.x generates incorrect code for the + test WORDCHAR_P (d - 1) != WORDCHAR_P (d) in the expansion of + AT_WORD_BOUNDARY, so this code is disabled. Expanding the + macro and introducing temporary variables works around the bug. */ + + case wordbound: + DEBUG_PRINT1 ("EXECUTING wordbound.\n"); + if (AT_WORD_BOUNDARY (d)) + break; + goto fail; + + case notwordbound: + DEBUG_PRINT1 ("EXECUTING notwordbound.\n"); + if (AT_WORD_BOUNDARY (d)) + goto fail; + break; +#else + case wordbound: + { + boolean prevchar, thischar; + + DEBUG_PRINT1 ("EXECUTING wordbound.\n"); + if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)) + break; + + prevchar = WORDCHAR_P (d - 1); + thischar = WORDCHAR_P (d); + if (prevchar != thischar) + break; + goto fail; + } + + case notwordbound: + { + boolean prevchar, thischar; + + DEBUG_PRINT1 ("EXECUTING notwordbound.\n"); + if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)) + goto fail; + + prevchar = WORDCHAR_P (d - 1); + thischar = WORDCHAR_P (d); + if (prevchar != thischar) + goto fail; + break; + } +#endif + + case wordbeg: + DEBUG_PRINT1 ("EXECUTING wordbeg.\n"); + if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1))) + break; + goto fail; + + case wordend: + DEBUG_PRINT1 ("EXECUTING wordend.\n"); + if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1) + && (!WORDCHAR_P (d) || AT_STRINGS_END (d))) + break; + goto fail; + +#ifdef emacs + case before_dot: + DEBUG_PRINT1 ("EXECUTING before_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) >= point) + goto fail; + break; + + case at_dot: + DEBUG_PRINT1 ("EXECUTING at_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) != point) + goto fail; + break; + + case after_dot: + DEBUG_PRINT1 ("EXECUTING after_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) <= point) + goto fail; + break; + + case syntaxspec: + DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt); + mcnt = *p++; + goto matchsyntax; + + case wordchar: + DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n"); + mcnt = (int) Sword; + matchsyntax: + PREFETCH (); + /* Can't use *d++ here; SYNTAX may be an unsafe macro. */ + d++; + if (SYNTAX (d[-1]) != (enum syntaxcode) mcnt) + goto fail; + SET_REGS_MATCHED (); + break; + + case notsyntaxspec: + DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt); + mcnt = *p++; + goto matchnotsyntax; + + case notwordchar: + DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n"); + mcnt = (int) Sword; + matchnotsyntax: + PREFETCH (); + /* Can't use *d++ here; SYNTAX may be an unsafe macro. */ + d++; + if (SYNTAX (d[-1]) == (enum syntaxcode) mcnt) + goto fail; + SET_REGS_MATCHED (); + break; + +#else /* not emacs */ + case wordchar: + DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n"); + PREFETCH (); + if (!WORDCHAR_P (d)) + goto fail; + SET_REGS_MATCHED (); + d++; + break; + + case notwordchar: + DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n"); + PREFETCH (); + if (WORDCHAR_P (d)) + goto fail; + SET_REGS_MATCHED (); + d++; + break; +#endif /* not emacs */ + + default: + abort (); + } + continue; /* Successfully executed one pattern command; keep going. */ + + + /* We goto here if a matching operation fails. */ + fail: + if (!FAIL_STACK_EMPTY ()) + { /* A restart point is known. Restore to that state. */ + DEBUG_PRINT1 ("\nFAIL:\n"); + POP_FAILURE_POINT (d, p, + lowest_active_reg, highest_active_reg, + regstart, regend, reg_info); + + /* If this failure point is a dummy, try the next one. */ + if (!p) + goto fail; + + /* If we failed to the end of the pattern, don't examine *p. */ + assert (p <= pend); + if (p < pend) + { + boolean is_a_jump_n = false; + + /* If failed to a backwards jump that's part of a repetition + loop, need to pop this failure point and use the next one. */ + switch ((re_opcode_t) *p) + { + case jump_n: + is_a_jump_n = true; + case maybe_pop_jump: + case pop_failure_jump: + case jump: + p1 = p + 1; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + + if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n) + || (!is_a_jump_n + && (re_opcode_t) *p1 == on_failure_jump)) + goto fail; + break; + default: + /* do nothing */ ; + } + } + + if (d >= string1 && d <= end1) + dend = end_match_1; + } + else + break; /* Matching at this starting point really fails. */ + } /* for (;;) */ + + if (best_regs_set) + goto restore_best_regs; + + FREE_VARIABLES (); + + return -1; /* Failure to match. */ +} /* re_match_2 */ + +/* Subroutine definitions for re_match_2. */ + + +/* We are passed P pointing to a register number after a start_memory. + + Return true if the pattern up to the corresponding stop_memory can + match the empty string, and false otherwise. + + If we find the matching stop_memory, sets P to point to one past its number. + Otherwise, sets P to an undefined byte less than or equal to END. + + We don't handle duplicates properly (yet). */ + +static boolean +group_match_null_string_p (p, end, reg_info) + unsigned char **p, *end; + register_info_type *reg_info; +{ + int mcnt; + /* Point to after the args to the start_memory. */ + unsigned char *p1 = *p + 2; + + while (p1 < end) + { + /* Skip over opcodes that can match nothing, and return true or + false, as appropriate, when we get to one that can't, or to the + matching stop_memory. */ + + switch ((re_opcode_t) *p1) + { + /* Could be either a loop or a series of alternatives. */ + case on_failure_jump: + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + + /* If the next operation is not a jump backwards in the + pattern. */ + + if (mcnt >= 0) + { + /* Go through the on_failure_jumps of the alternatives, + seeing if any of the alternatives cannot match nothing. + The last alternative starts with only a jump, + whereas the rest start with on_failure_jump and end + with a jump, e.g., here is the pattern for `a|b|c': + + /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6 + /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3 + /exactn/1/c + + So, we have to first go through the first (n-1) + alternatives and then deal with the last one separately. */ + + + /* Deal with the first (n-1) alternatives, which start + with an on_failure_jump (see above) that jumps to right + past a jump_past_alt. */ + + while ((re_opcode_t) p1[mcnt-3] == jump_past_alt) + { + /* `mcnt' holds how many bytes long the alternative + is, including the ending `jump_past_alt' and + its number. */ + + if (!alt_match_null_string_p (p1, p1 + mcnt - 3, + reg_info)) + return false; + + /* Move to right after this alternative, including the + jump_past_alt. */ + p1 += mcnt; + + /* Break if it's the beginning of an n-th alternative + that doesn't begin with an on_failure_jump. */ + if ((re_opcode_t) *p1 != on_failure_jump) + break; + + /* Still have to check that it's not an n-th + alternative that starts with an on_failure_jump. */ + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if ((re_opcode_t) p1[mcnt-3] != jump_past_alt) + { + /* Get to the beginning of the n-th alternative. */ + p1 -= 3; + break; + } + } + + /* Deal with the last alternative: go back and get number + of the `jump_past_alt' just before it. `mcnt' contains + the length of the alternative. */ + EXTRACT_NUMBER (mcnt, p1 - 2); + + if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info)) + return false; + + p1 += mcnt; /* Get past the n-th alternative. */ + } /* if mcnt > 0 */ + break; + + + case stop_memory: + assert (p1[1] == **p); + *p = p1 + 2; + return true; + + + default: + if (!common_op_match_null_string_p (&p1, end, reg_info)) + return false; + } + } /* while p1 < end */ + + return false; +} /* group_match_null_string_p */ + + +/* Similar to group_match_null_string_p, but doesn't deal with alternatives: + It expects P to be the first byte of a single alternative and END one + byte past the last. The alternative can contain groups. */ + +static boolean +alt_match_null_string_p (p, end, reg_info) + unsigned char *p, *end; + register_info_type *reg_info; +{ + int mcnt; + unsigned char *p1 = p; + + while (p1 < end) + { + /* Skip over opcodes that can match nothing, and break when we get + to one that can't. */ + + switch ((re_opcode_t) *p1) + { + /* It's a loop. */ + case on_failure_jump: + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + break; + + default: + if (!common_op_match_null_string_p (&p1, end, reg_info)) + return false; + } + } /* while p1 < end */ + + return true; +} /* alt_match_null_string_p */ + + +/* Deals with the ops common to group_match_null_string_p and + alt_match_null_string_p. + + Sets P to one after the op and its arguments, if any. */ + +static boolean +common_op_match_null_string_p (p, end, reg_info) + unsigned char **p, *end; + register_info_type *reg_info; +{ + int mcnt; + boolean ret; + int reg_no; + unsigned char *p1 = *p; + + switch ((re_opcode_t) *p1++) + { + case no_op: + case begline: + case endline: + case begbuf: + case endbuf: + case wordbeg: + case wordend: + case wordbound: + case notwordbound: +#ifdef emacs + case before_dot: + case at_dot: + case after_dot: +#endif + break; + + case start_memory: + reg_no = *p1; + assert (reg_no > 0 && reg_no <= MAX_REGNUM); + ret = group_match_null_string_p (&p1, end, reg_info); + + /* Have to set this here in case we're checking a group which + contains a group and a back reference to it. */ + + if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE) + REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret; + + if (!ret) + return false; + break; + + /* If this is an optimized succeed_n for zero times, make the jump. */ + case jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if (mcnt >= 0) + p1 += mcnt; + else + return false; + break; + + case succeed_n: + /* Get to the number of times to succeed. */ + p1 += 2; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + + if (mcnt == 0) + { + p1 -= 4; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + } + else + return false; + break; + + case duplicate: + if (!REG_MATCH_NULL_STRING_P (reg_info[*p1])) + return false; + break; + + case set_number_at: + p1 += 4; + + default: + /* All other opcodes mean we cannot match the empty string. */ + return false; + } + + *p = p1; + return true; +} /* common_op_match_null_string_p */ + + +/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN + bytes; nonzero otherwise. */ + +static int +bcmp_translate (s1, s2, len, translate) + const char *s1, *s2; + register int len; + RE_TRANSLATE_TYPE translate; +{ + register const unsigned char *p1 = (const unsigned char *) s1; + register const unsigned char *p2 = (const unsigned char *) s2; + while (len) + { + if (translate[*p1++] != translate[*p2++]) return 1; + len--; + } + return 0; +} + +/* Entry points for GNU code. */ + +/* re_compile_pattern is the GNU regular expression compiler: it + compiles PATTERN (of length SIZE) and puts the result in BUFP. + Returns 0 if the pattern was valid, otherwise an error string. + + Assumes the `allocated' (and perhaps `buffer') and `translate' fields + are set in BUFP on entry. + + We call regex_compile to do the actual compilation. */ + +const char * +re_compile_pattern (pattern, length, bufp) + const char *pattern; + size_t length; + struct re_pattern_buffer *bufp; +{ + reg_errcode_t ret; + + /* GNU code is written to assume at least RE_NREGS registers will be set + (and at least one extra will be -1). */ + bufp->regs_allocated = REGS_UNALLOCATED; + + /* And GNU code determines whether or not to get register information + by passing null for the REGS argument to re_match, etc., not by + setting no_sub. */ + bufp->no_sub = 0; + + /* Match anchors at newline. */ + bufp->newline_anchor = 1; + + ret = regex_compile (pattern, length, re_syntax_options, bufp); + + if (!ret) + return NULL; + return gettext (re_error_msgid + re_error_msgid_idx[(int) ret]); +} +#ifdef _LIBC +weak_alias (__re_compile_pattern, re_compile_pattern) +#endif + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#if defined _REGEX_RE_COMP || defined _LIBC + +/* BSD has one and only one pattern buffer. */ +static struct re_pattern_buffer re_comp_buf; + +char * +#ifdef _LIBC +/* Make these definitions weak in libc, so POSIX programs can redefine + these names if they don't use our functions, and still use + regcomp/regexec below without link errors. */ +weak_function +#endif +re_comp (s) + const char *s; +{ + reg_errcode_t ret; + + if (!s) + { + if (!re_comp_buf.buffer) + return gettext ("No previous regular expression"); + return 0; + } + + if (!re_comp_buf.buffer) + { + re_comp_buf.buffer = (unsigned char *) malloc (200); + if (re_comp_buf.buffer == NULL) + return (char *) gettext (re_error_msgid + + re_error_msgid_idx[(int) REG_ESPACE]); + re_comp_buf.allocated = 200; + + re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH); + if (re_comp_buf.fastmap == NULL) + return (char *) gettext (re_error_msgid + + re_error_msgid_idx[(int) REG_ESPACE]); + } + + /* Since `re_exec' always passes NULL for the `regs' argument, we + don't need to initialize the pattern buffer fields which affect it. */ + + /* Match anchors at newlines. */ + re_comp_buf.newline_anchor = 1; + + ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf); + + if (!ret) + return NULL; + + /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */ + return (char *) gettext (re_error_msgid + re_error_msgid_idx[(int) ret]); +} + + +int +#ifdef _LIBC +weak_function +#endif +re_exec (s) + const char *s; +{ + const int len = strlen (s); + return + 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0); +} + +#endif /* _REGEX_RE_COMP */ + +/* POSIX.2 functions. Don't define these for Emacs. */ + +#ifndef emacs + +/* regcomp takes a regular expression as a string and compiles it. + + PREG is a regex_t *. We do not expect any fields to be initialized, + since POSIX says we shouldn't. Thus, we set + + `buffer' to the compiled pattern; + `used' to the length of the compiled pattern; + `syntax' to RE_SYNTAX_POSIX_EXTENDED if the + REG_EXTENDED bit in CFLAGS is set; otherwise, to + RE_SYNTAX_POSIX_BASIC; + `newline_anchor' to REG_NEWLINE being set in CFLAGS; + `fastmap' to an allocated space for the fastmap; + `fastmap_accurate' to zero; + `re_nsub' to the number of subexpressions in PATTERN. + + PATTERN is the address of the pattern string. + + CFLAGS is a series of bits which affect compilation. + + If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we + use POSIX basic syntax. + + If REG_NEWLINE is set, then . and [^...] don't match newline. + Also, regexec will try a match beginning after every newline. + + If REG_ICASE is set, then we considers upper- and lowercase + versions of letters to be equivalent when matching. + + If REG_NOSUB is set, then when PREG is passed to regexec, that + routine will report only success or failure, and nothing about the + registers. + + It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for + the return codes and their meanings.) */ + +int +regcomp (preg, pattern, cflags) + regex_t *preg; + const char *pattern; + int cflags; +{ + reg_errcode_t ret; + reg_syntax_t syntax + = (cflags & REG_EXTENDED) ? + RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC; + + /* regex_compile will allocate the space for the compiled pattern. */ + preg->buffer = 0; + preg->allocated = 0; + preg->used = 0; + + /* Try to allocate space for the fastmap. */ + preg->fastmap = (char *) malloc (1 << BYTEWIDTH); + + if (cflags & REG_ICASE) + { + unsigned i; + + preg->translate + = (RE_TRANSLATE_TYPE) malloc (CHAR_SET_SIZE + * sizeof (*(RE_TRANSLATE_TYPE)0)); + if (preg->translate == NULL) + return (int) REG_ESPACE; + + /* Map uppercase characters to corresponding lowercase ones. */ + for (i = 0; i < CHAR_SET_SIZE; i++) + preg->translate[i] = ISUPPER (i) ? TOLOWER (i) : i; + } + else + preg->translate = NULL; + + /* If REG_NEWLINE is set, newlines are treated differently. */ + if (cflags & REG_NEWLINE) + { /* REG_NEWLINE implies neither . nor [^...] match newline. */ + syntax &= ~RE_DOT_NEWLINE; + syntax |= RE_HAT_LISTS_NOT_NEWLINE; + /* It also changes the matching behavior. */ + preg->newline_anchor = 1; + } + else + preg->newline_anchor = 0; + + preg->no_sub = !!(cflags & REG_NOSUB); + + /* POSIX says a null character in the pattern terminates it, so we + can use strlen here in compiling the pattern. */ + ret = regex_compile (pattern, strlen (pattern), syntax, preg); + + /* POSIX doesn't distinguish between an unmatched open-group and an + unmatched close-group: both are REG_EPAREN. */ + if (ret == REG_ERPAREN) ret = REG_EPAREN; + + if (ret == REG_NOERROR && preg->fastmap) + { + /* Compute the fastmap now, since regexec cannot modify the pattern + buffer. */ + if (re_compile_fastmap (preg) == -2) + { + /* Some error occured while computing the fastmap, just forget + about it. */ + free (preg->fastmap); + preg->fastmap = NULL; + } + } + + return (int) ret; +} +#ifdef _LIBC +weak_alias (__regcomp, regcomp) +#endif + + +/* regexec searches for a given pattern, specified by PREG, in the + string STRING. + + If NMATCH is zero or REG_NOSUB was set in the cflags argument to + `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at + least NMATCH elements, and we set them to the offsets of the + corresponding matched substrings. + + EFLAGS specifies `execution flags' which affect matching: if + REG_NOTBOL is set, then ^ does not match at the beginning of the + string; if REG_NOTEOL is set, then $ does not match at the end. + + We return 0 if we find a match and REG_NOMATCH if not. */ + +int +regexec (preg, string, nmatch, pmatch, eflags) + const regex_t *preg; + const char *string; + size_t nmatch; + regmatch_t pmatch[]; + int eflags; +{ + int ret; + struct re_registers regs; + regex_t private_preg; + int len = strlen (string); + boolean want_reg_info = !preg->no_sub && nmatch > 0; + + private_preg = *preg; + + private_preg.not_bol = !!(eflags & REG_NOTBOL); + private_preg.not_eol = !!(eflags & REG_NOTEOL); + + /* The user has told us exactly how many registers to return + information about, via `nmatch'. We have to pass that on to the + matching routines. */ + private_preg.regs_allocated = REGS_FIXED; + + if (want_reg_info) + { + regs.num_regs = nmatch; + regs.start = TALLOC (nmatch * 2, regoff_t); + if (regs.start == NULL) + return (int) REG_NOMATCH; + regs.end = regs.start + nmatch; + } + + /* Perform the searching operation. */ + ret = re_search (&private_preg, string, len, + /* start: */ 0, /* range: */ len, + want_reg_info ? ®s : (struct re_registers *) 0); + + /* Copy the register information to the POSIX structure. */ + if (want_reg_info) + { + if (ret >= 0) + { + unsigned r; + + for (r = 0; r < nmatch; r++) + { + pmatch[r].rm_so = regs.start[r]; + pmatch[r].rm_eo = regs.end[r]; + } + } + + /* If we needed the temporary register info, free the space now. */ + free (regs.start); + } + + /* We want zero return to mean success, unlike `re_search'. */ + return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH; +} +#ifdef _LIBC +weak_alias (__regexec, regexec) +#endif + + +/* Returns a message corresponding to an error code, ERRCODE, returned + from either regcomp or regexec. We don't use PREG here. */ + +size_t +regerror (errcode, preg, errbuf, errbuf_size) + int errcode; + const regex_t *preg; + char *errbuf; + size_t errbuf_size; +{ + const char *msg; + size_t msg_size; + + if (errcode < 0 + || errcode >= (int) (sizeof (re_error_msgid_idx) + / sizeof (re_error_msgid_idx[0]))) + /* Only error codes returned by the rest of the code should be passed + to this routine. If we are given anything else, or if other regex + code generates an invalid error code, then the program has a bug. + Dump core so we can fix it. */ + abort (); + + msg = gettext (re_error_msgid + re_error_msgid_idx[errcode]); + + msg_size = strlen (msg) + 1; /* Includes the null. */ + + if (errbuf_size != 0) + { + if (msg_size > errbuf_size) + { +#if defined HAVE_MEMPCPY || defined _LIBC + *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0'; +#else + memcpy (errbuf, msg, errbuf_size - 1); + errbuf[errbuf_size - 1] = 0; +#endif + } + else + memcpy (errbuf, msg, msg_size); + } + + return msg_size; +} +#ifdef _LIBC +weak_alias (__regerror, regerror) +#endif + + +/* Free dynamically allocated space used by PREG. */ + +void +regfree (preg) + regex_t *preg; +{ + if (preg->buffer != NULL) + free (preg->buffer); + preg->buffer = NULL; + + preg->allocated = 0; + preg->used = 0; + + if (preg->fastmap != NULL) + free (preg->fastmap); + preg->fastmap = NULL; + preg->fastmap_accurate = 0; + + if (preg->translate != NULL) + free (preg->translate); + preg->translate = NULL; +} +#ifdef _LIBC +weak_alias (__regfree, regfree) +#endif + +#endif /* not emacs */ diff --git a/lib/routemap.c b/lib/routemap.c new file mode 100644 index 00000000..b000f2fc --- /dev/null +++ b/lib/routemap.c @@ -0,0 +1,1077 @@ +/* Route map function. + Copyright (C) 1998, 1999 Kunihiro Ishiguro + +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 <zebra.h> + +#include "linklist.h" +#include "memory.h" +#include "vector.h" +#include "prefix.h" +#include "routemap.h" +#include "command.h" + +/* Vector for route match rules. */ +static vector route_match_vec; + +/* Vector for route set rules. */ +static vector route_set_vec; + +/* Route map rule. This rule has both `match' rule and `set' rule. */ +struct route_map_rule +{ + /* Rule type. */ + struct route_map_rule_cmd *cmd; + + /* For pretty printing. */ + char *rule_str; + + /* Pre-compiled match rule. */ + void *value; + + /* Linked list. */ + struct route_map_rule *next; + struct route_map_rule *prev; +}; + +/* Making route map list. */ +struct route_map_list +{ + struct route_map *head; + struct route_map *tail; + + void (*add_hook) (char *); + void (*delete_hook) (char *); + void (*event_hook) (route_map_event_t, char *); +}; + +/* Master list of route map. */ +static struct route_map_list route_map_master = { NULL, NULL, NULL, NULL }; + +static void +route_map_rule_delete (struct route_map_rule_list *, + struct route_map_rule *); + +static void +route_map_index_delete (struct route_map_index *, int); + +/* New route map allocation. Please note route map's name must be + specified. */ +static struct route_map * +route_map_new (char *name) +{ + struct route_map *new; + + new = XCALLOC (MTYPE_ROUTE_MAP, sizeof (struct route_map)); + new->name = XSTRDUP (MTYPE_ROUTE_MAP_NAME, name); + return new; +} + +/* Add new name to route_map. */ +static struct route_map * +route_map_add (char *name) +{ + struct route_map *map; + struct route_map_list *list; + + map = route_map_new (name); + list = &route_map_master; + + map->next = NULL; + map->prev = list->tail; + if (list->tail) + list->tail->next = map; + else + list->head = map; + list->tail = map; + + /* Execute hook. */ + if (route_map_master.add_hook) + (*route_map_master.add_hook) (name); + + return map; +} + +/* Route map delete from list. */ +static void +route_map_delete (struct route_map *map) +{ + struct route_map_list *list; + struct route_map_index *index; + char *name; + + while ((index = map->head) != NULL) + route_map_index_delete (index, 0); + + name = map->name; + + list = &route_map_master; + + if (map->next) + map->next->prev = map->prev; + else + list->tail = map->prev; + + if (map->prev) + map->prev->next = map->next; + else + list->head = map->next; + + XFREE (MTYPE_ROUTE_MAP, map); + + /* Execute deletion hook. */ + if (route_map_master.delete_hook) + (*route_map_master.delete_hook) (name); + + if (name) + XFREE (MTYPE_ROUTE_MAP_NAME, name); + +} + +/* Lookup route map by route map name string. */ +struct route_map * +route_map_lookup_by_name (char *name) +{ + struct route_map *map; + + for (map = route_map_master.head; map; map = map->next) + if (strcmp (map->name, name) == 0) + return map; + return NULL; +} + +/* Lookup route map. If there isn't route map create one and return + it. */ +struct route_map * +route_map_get (char *name) +{ + struct route_map *map; + + map = route_map_lookup_by_name (name); + if (map == NULL) + map = route_map_add (name); + return map; +} + +/* Return route map's type string. */ +static char * +route_map_type_str (enum route_map_type type) +{ + switch (type) + { + case RMAP_PERMIT: + return "permit"; + break; + case RMAP_DENY: + return "deny"; + break; + default: + return ""; + break; + } +} + +int +route_map_empty (struct route_map *map) +{ + if (map->head == NULL && map->tail == NULL) + return 1; + else + return 0; +} + +/* For debug. */ +void +route_map_print () +{ + struct route_map *map; + struct route_map_index *index; + struct route_map_rule *rule; + + for (map = route_map_master.head; map; map = map->next) + for (index = map->head; index; index = index->next) + { + printf ("route-map %s %s %d\n", + map->name, + route_map_type_str (index->type), + index->pref); + for (rule = index->match_list.head; rule; rule = rule->next) + printf (" match %s %s\n", rule->cmd->str, rule->rule_str); + for (rule = index->set_list.head; rule; rule = rule->next) + printf (" set %s %s\n", rule->cmd->str, rule->rule_str); + if (index->exitpolicy == RMAP_GOTO) + printf (" on-match goto %d\n", index->nextpref); + if (index->exitpolicy == RMAP_NEXT) + printf (" on-match next\n"); + } +} + +/* New route map allocation. Please note route map's name must be + specified. */ +struct route_map_index * +route_map_index_new () +{ + struct route_map_index *new; + + new = XCALLOC (MTYPE_ROUTE_MAP_INDEX, sizeof (struct route_map_index)); + new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */ + return new; +} + +/* Free route map index. */ +static void +route_map_index_delete (struct route_map_index *index, int notify) +{ + struct route_map_rule *rule; + + /* Free route match. */ + while ((rule = index->match_list.head) != NULL) + route_map_rule_delete (&index->match_list, rule); + + /* Free route set. */ + while ((rule = index->set_list.head) != NULL) + route_map_rule_delete (&index->set_list, rule); + + /* Remove index from route map list. */ + if (index->next) + index->next->prev = index->prev; + else + index->map->tail = index->prev; + + if (index->prev) + index->prev->next = index->next; + else + index->map->head = index->next; + + /* Execute event hook. */ + if (route_map_master.event_hook && notify) + (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED, + index->map->name); + + XFREE (MTYPE_ROUTE_MAP_INDEX, index); +} + +/* Lookup index from route map. */ +struct route_map_index * +route_map_index_lookup (struct route_map *map, enum route_map_type type, + int pref) +{ + struct route_map_index *index; + + for (index = map->head; index; index = index->next) + if ((index->type == type || type == RMAP_ANY) + && index->pref == pref) + return index; + return NULL; +} + +/* Add new index to route map. */ +struct route_map_index * +route_map_index_add (struct route_map *map, enum route_map_type type, + int pref) +{ + struct route_map_index *index; + struct route_map_index *point; + + /* Allocate new route map inex. */ + index = route_map_index_new (); + index->map = map; + index->type = type; + index->pref = pref; + + /* Compare preference. */ + for (point = map->head; point; point = point->next) + if (point->pref >= pref) + break; + + if (map->head == NULL) + { + map->head = map->tail = index; + } + else if (point == NULL) + { + index->prev = map->tail; + map->tail->next = index; + map->tail = index; + } + else if (point == map->head) + { + index->next = map->head; + map->head->prev = index; + map->head = index; + } + else + { + index->next = point; + index->prev = point->prev; + if (point->prev) + point->prev->next = index; + point->prev = index; + } + + /* Execute event hook. */ + if (route_map_master.event_hook) + (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED, + map->name); + + return index; +} + +/* Get route map index. */ +struct route_map_index * +route_map_index_get (struct route_map *map, enum route_map_type type, + int pref) +{ + struct route_map_index *index; + + index = route_map_index_lookup (map, RMAP_ANY, pref); + if (index && index->type != type) + { + /* Delete index from route map. */ + route_map_index_delete (index, 1); + index = NULL; + } + if (index == NULL) + index = route_map_index_add (map, type, pref); + return index; +} + +/* New route map rule */ +struct route_map_rule * +route_map_rule_new () +{ + struct route_map_rule *new; + + new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule)); + return new; +} + +/* Install rule command to the match list. */ +void +route_map_install_match (struct route_map_rule_cmd *cmd) +{ + vector_set (route_match_vec, cmd); +} + +/* Install rule command to the set list. */ +void +route_map_install_set (struct route_map_rule_cmd *cmd) +{ + vector_set (route_set_vec, cmd); +} + +/* Lookup rule command from match list. */ +struct route_map_rule_cmd * +route_map_lookup_match (char *name) +{ + int i; + struct route_map_rule_cmd *rule; + + for (i = 0; i < vector_max (route_match_vec); i++) + if ((rule = vector_slot (route_match_vec, i)) != NULL) + if (strcmp (rule->str, name) == 0) + return rule; + return NULL; +} + +/* Lookup rule command from set list. */ +struct route_map_rule_cmd * +route_map_lookup_set (char *name) +{ + int i; + struct route_map_rule_cmd *rule; + + for (i = 0; i < vector_max (route_set_vec); i++) + if ((rule = vector_slot (route_set_vec, i)) != NULL) + if (strcmp (rule->str, name) == 0) + return rule; + return NULL; +} + +/* Add match and set rule to rule list. */ +static void +route_map_rule_add (struct route_map_rule_list *list, + struct route_map_rule *rule) +{ + rule->next = NULL; + rule->prev = list->tail; + if (list->tail) + list->tail->next = rule; + else + list->head = rule; + list->tail = rule; +} + +/* Delete rule from rule list. */ +static void +route_map_rule_delete (struct route_map_rule_list *list, + struct route_map_rule *rule) +{ + if (rule->cmd->func_free) + (*rule->cmd->func_free) (rule->value); + + if (rule->rule_str) + XFREE (MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str); + + if (rule->next) + rule->next->prev = rule->prev; + else + list->tail = rule->prev; + if (rule->prev) + rule->prev->next = rule->next; + else + list->head = rule->next; + + XFREE (MTYPE_ROUTE_MAP_RULE, rule); +} + +/* strcmp wrapper function which don't crush even argument is NULL. */ +int +rulecmp (char *dst, char *src) +{ + if (dst == NULL) + { + if (src == NULL) + return 0; + else + return 1; + } + else + { + if (src == NULL) + return 1; + else + return strcmp (dst, src); + } + return 1; +} + +/* Add match statement to route map. */ +int +route_map_add_match (struct route_map_index *index, char *match_name, + char *match_arg) +{ + struct route_map_rule *rule; + struct route_map_rule *next; + struct route_map_rule_cmd *cmd; + void *compile; + int replaced = 0; + + /* First lookup rule for add match statement. */ + cmd = route_map_lookup_match (match_name); + if (cmd == NULL) + return RMAP_RULE_MISSING; + + /* Next call compile function for this match statement. */ + if (cmd->func_compile) + { + compile= (*cmd->func_compile)(match_arg); + if (compile == NULL) + return RMAP_COMPILE_ERROR; + } + else + compile = NULL; + + /* If argument is completely same ignore it. */ + for (rule = index->match_list.head; rule; rule = next) + { + next = rule->next; + if (rule->cmd == cmd) + { + route_map_rule_delete (&index->match_list, rule); + replaced = 1; + } + } + + /* Add new route map match rule. */ + rule = route_map_rule_new (); + rule->cmd = cmd; + rule->value = compile; + if (match_arg) + rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, match_arg); + else + rule->rule_str = NULL; + + /* Add new route match rule to linked list. */ + route_map_rule_add (&index->match_list, rule); + + /* Execute event hook. */ + if (route_map_master.event_hook) + (*route_map_master.event_hook) (replaced ? + RMAP_EVENT_MATCH_REPLACED: + RMAP_EVENT_MATCH_ADDED, + index->map->name); + + return 0; +} + +/* Delete specified route match rule. */ +int +route_map_delete_match (struct route_map_index *index, char *match_name, + char *match_arg) +{ + struct route_map_rule *rule; + struct route_map_rule_cmd *cmd; + + cmd = route_map_lookup_match (match_name); + if (cmd == NULL) + return 1; + + for (rule = index->match_list.head; rule; rule = rule->next) + if (rule->cmd == cmd && + (rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL)) + { + route_map_rule_delete (&index->match_list, rule); + /* Execute event hook. */ + if (route_map_master.event_hook) + (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED, + index->map->name); + return 0; + } + /* Can't find matched rule. */ + return 1; +} + +/* Add route-map set statement to the route map. */ +int +route_map_add_set (struct route_map_index *index, char *set_name, + char *set_arg) +{ + struct route_map_rule *rule; + struct route_map_rule *next; + struct route_map_rule_cmd *cmd; + void *compile; + int replaced = 0; + + cmd = route_map_lookup_set (set_name); + if (cmd == NULL) + return RMAP_RULE_MISSING; + + /* Next call compile function for this match statement. */ + if (cmd->func_compile) + { + compile= (*cmd->func_compile)(set_arg); + if (compile == NULL) + return RMAP_COMPILE_ERROR; + } + else + compile = NULL; + + /* Add by WJL. if old set command of same kind exist, delete it first + to ensure only one set command of same kind exist under a + route_map_index. */ + for (rule = index->set_list.head; rule; rule = next) + { + next = rule->next; + if (rule->cmd == cmd) + { + route_map_rule_delete (&index->set_list, rule); + replaced = 1; + } + } + + /* Add new route map match rule. */ + rule = route_map_rule_new (); + rule->cmd = cmd; + rule->value = compile; + if (set_arg) + rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, set_arg); + else + rule->rule_str = NULL; + + /* Add new route match rule to linked list. */ + route_map_rule_add (&index->set_list, rule); + + /* Execute event hook. */ + if (route_map_master.event_hook) + (*route_map_master.event_hook) (replaced ? + RMAP_EVENT_SET_REPLACED: + RMAP_EVENT_SET_ADDED, + index->map->name); + return 0; +} + +/* Delete route map set rule. */ +int +route_map_delete_set (struct route_map_index *index, char *set_name, + char *set_arg) +{ + struct route_map_rule *rule; + struct route_map_rule_cmd *cmd; + + cmd = route_map_lookup_set (set_name); + if (cmd == NULL) + return 1; + + for (rule = index->set_list.head; rule; rule = rule->next) + if ((rule->cmd == cmd) && + (rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL)) + { + route_map_rule_delete (&index->set_list, rule); + /* Execute event hook. */ + if (route_map_master.event_hook) + (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED, + index->map->name); + return 0; + } + /* Can't find matched rule. */ + return 1; +} + +/* Apply route map's each index to the object. */ +/* +** The matrix for a route-map looks like this: +** (note, this includes the description for the "NEXT" +** and "GOTO" frobs now +** +** Match | No Match +** | +** permit a | c +** | +** ------------------+--------------- +** | +** deny b | d +** | +** +** a) Apply Set statements, accept route +** If NEXT is specified, goto NEXT statement +** If GOTO is specified, goto the first clause where pref > nextpref +** If nothing is specified, do as Cisco and finish +** b) Finish route-map processing, and deny route +** c) & d) Goto Next index +** +** If we get no matches after we've processed all updates, then the route +** is dropped too. +** +** Some notes on the new "NEXT" and "GOTO" +** on-match next - If this clause is matched, then the set statements +** are executed and then we drop through to the next clause +** on-match goto n - If this clause is matched, then the set statments +** are executed and then we goto the nth clause, or the +** first clause greater than this. In order to ensure +** route-maps *always* exit, you cannot jump backwards. +** Sorry ;) +** +** We need to make sure our route-map processing matches the above +*/ +route_map_result_t +route_map_apply_index (struct route_map_index *index, struct prefix *prefix, + route_map_object_t type, void *object) +{ + int ret; + struct route_map_rule *match; + struct route_map_rule *set; + + /* Check all match rule and if there is no match rule return 0. */ + for (match = index->match_list.head; match; match = match->next) + { + /* Try each match statement in turn. If any return something + other than RM_MATCH then we don't need to check anymore and can + return */ + ret = (*match->cmd->func_apply)(match->value, prefix, type, object); + if (ret != RMAP_MATCH) + return ret; + } + + /* We get here if all match statements matched From the matrix + above, if this is PERMIT we go on and apply the SET functions. If + we're deny, we return indicating we matched a deny */ + + /* Apply set statement to the object. */ + if (index->type == RMAP_PERMIT) + { + for (set = index->set_list.head; set; set = set->next) + { + ret = (*set->cmd->func_apply)(set->value, prefix, type, object); + } + return RMAP_MATCH; + } + else + { + return RMAP_DENYMATCH; + } + /* Should not get here! */ + return RMAP_MATCH; +} + +/* Apply route map to the object. */ +route_map_result_t +route_map_apply (struct route_map *map, struct prefix *prefix, + route_map_object_t type, void *object) +{ + int ret = 0; + struct route_map_index *index; + + if (map == NULL) + return RMAP_DENYMATCH; + + for (index = map->head; index; index = index->next) + { + /* Apply this index. End here if we get a RM_NOMATCH */ + ret = route_map_apply_index (index, prefix, type, object); + + if (ret != RMAP_NOMATCH) + { + /* We now have to handle the NEXT and GOTO clauses */ + if(index->exitpolicy == RMAP_EXIT) + return ret; + if(index->exitpolicy == RMAP_GOTO) + { + /* Find the next clause to jump to */ + struct route_map_index *next; + + next = index->next; + while (next && next->pref < index->nextpref) + { + index = next; + next = next->next; + } + if (next == NULL) + { + /* No clauses match! */ + return ret; + } + } + /* Otherwise, we fall through as it was a NEXT */ + } + } + /* Finally route-map does not match at all. */ + return RMAP_DENYMATCH; +} + +void +route_map_add_hook (void (*func) (char *)) +{ + route_map_master.add_hook = func; +} + +void +route_map_delete_hook (void (*func) (char *)) +{ + route_map_master.delete_hook = func; +} + +void +route_map_event_hook (void (*func) (route_map_event_t, char *)) +{ + route_map_master.event_hook = func; +} + +void +route_map_init () +{ + /* Make vector for match and set. */ + route_match_vec = vector_init (1); + route_set_vec = vector_init (1); +} + +/* VTY related functions. */ +DEFUN (route_map, + route_map_cmd, + "route-map WORD (deny|permit) <1-65535>", + "Create route-map or enter route-map command mode\n" + "Route map tag\n" + "Route map denies set operations\n" + "Route map permits set operations\n" + "Sequence to insert to/delete from existing route-map entry\n") +{ + int permit; + unsigned long pref; + struct route_map *map; + struct route_map_index *index; + char *endptr = NULL; + + /* Permit check. */ + if (strncmp (argv[1], "permit", strlen (argv[1])) == 0) + permit = RMAP_PERMIT; + else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0) + permit = RMAP_DENY; + else + { + vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Preference check. */ + pref = strtoul (argv[2], &endptr, 10); + if (pref == ULONG_MAX || *endptr != '\0') + { + vty_out (vty, "the fourth field must be positive integer%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (pref == 0 || pref > 65535) + { + vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get route map. */ + map = route_map_get (argv[0]); + index = route_map_index_get (map, permit, pref); + + vty->index = index; + vty->node = RMAP_NODE; + return CMD_SUCCESS; +} + +DEFUN (no_route_map_all, + no_route_map_all_cmd, + "no route-map WORD", + NO_STR + "Create route-map or enter route-map command mode\n" + "Route map tag\n") +{ + struct route_map *map; + + map = route_map_lookup_by_name (argv[0]); + if (map == NULL) + { + vty_out (vty, "%% Could not find route-map %s%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + route_map_delete (map); + + return CMD_SUCCESS; +} + +DEFUN (no_route_map, + no_route_map_cmd, + "no route-map WORD (deny|permit) <1-65535>", + NO_STR + "Create route-map or enter route-map command mode\n" + "Route map tag\n" + "Route map denies set operations\n" + "Route map permits set operations\n" + "Sequence to insert to/delete from existing route-map entry\n") +{ + int permit; + unsigned long pref; + struct route_map *map; + struct route_map_index *index; + char *endptr = NULL; + + /* Permit check. */ + if (strncmp (argv[1], "permit", strlen (argv[1])) == 0) + permit = RMAP_PERMIT; + else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0) + permit = RMAP_DENY; + else + { + vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Preference. */ + pref = strtoul (argv[2], &endptr, 10); + if (pref == ULONG_MAX || *endptr != '\0') + { + vty_out (vty, "the fourth field must be positive integer%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (pref == 0 || pref > 65535) + { + vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Existence check. */ + map = route_map_lookup_by_name (argv[0]); + if (map == NULL) + { + vty_out (vty, "%% Could not find route-map %s%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + /* Lookup route map index. */ + index = route_map_index_lookup (map, permit, pref); + if (index == NULL) + { + vty_out (vty, "%% Could not find route-map entry %s %s%s", + argv[0], argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + + /* Delete index from route map. */ + route_map_index_delete (index, 1); + + /* If this route rule is the last one, delete route map itself. */ + if (route_map_empty (map)) + route_map_delete (map); + + return CMD_SUCCESS; +} + +DEFUN (rmap_onmatch_next, + rmap_onmatch_next_cmd, + "on-match next", + "Exit policy on matches\n" + "Next clause\n") +{ + struct route_map_index *index; + + index = vty->index; + + if (index) + index->exitpolicy = RMAP_NEXT; + + return CMD_SUCCESS; +} + +DEFUN (no_rmap_onmatch_next, + no_rmap_onmatch_next_cmd, + "no on-match next", + NO_STR + "Exit policy on matches\n" + "Next clause\n") +{ + struct route_map_index *index; + + index = vty->index; + + if (index) + index->exitpolicy = RMAP_EXIT; + + return CMD_SUCCESS; +} + +DEFUN (rmap_onmatch_goto, + rmap_onmatch_goto_cmd, + "on-match goto <1-65535>", + "Exit policy on matches\n" + "Goto Clause number\n" + "Number\n") +{ + struct route_map_index *index; + int d = 0; + + if (argv[0]) + d = atoi(argv[0]); + + index = vty->index; + if (index) + { + if (d <= index->pref) + { + /* Can't allow you to do that, Dave */ + vty_out (vty, "can't jump backwards in route-maps%s", + VTY_NEWLINE); + return CMD_WARNING; + } + else + { + index->exitpolicy = RMAP_GOTO; + index->nextpref = d; + } + } + return CMD_SUCCESS; +} + +DEFUN (no_rmap_onmatch_goto, + no_rmap_onmatch_goto_cmd, + "no on-match goto", + NO_STR + "Exit policy on matches\n" + "Next clause\n") +{ + struct route_map_index *index; + + index = vty->index; + + if (index) + index->exitpolicy = RMAP_EXIT; + + return CMD_SUCCESS; +} + +/* Configuration write function. */ +int +route_map_config_write (struct vty *vty) +{ + struct route_map *map; + struct route_map_index *index; + struct route_map_rule *rule; + int first = 1; + int write = 0; + + for (map = route_map_master.head; map; map = map->next) + for (index = map->head; index; index = index->next) + { + if (!first) + vty_out (vty, "!%s", VTY_NEWLINE); + else + first = 0; + + vty_out (vty, "route-map %s %s %d%s", + map->name, + route_map_type_str (index->type), + index->pref, VTY_NEWLINE); + + for (rule = index->match_list.head; rule; rule = rule->next) + vty_out (vty, " match %s %s%s", rule->cmd->str, + rule->rule_str ? rule->rule_str : "", + VTY_NEWLINE); + + for (rule = index->set_list.head; rule; rule = rule->next) + vty_out (vty, " set %s %s%s", rule->cmd->str, + rule->rule_str ? rule->rule_str : "", + VTY_NEWLINE); + if (index->exitpolicy == RMAP_GOTO) + vty_out (vty, " on-match goto %d%s", index->nextpref, + VTY_NEWLINE); + if (index->exitpolicy == RMAP_NEXT) + vty_out (vty," on-match next%s", VTY_NEWLINE); + + write++; + } + return write; +} + +/* Route map node structure. */ +struct cmd_node rmap_node = +{ + RMAP_NODE, + "%s(config-route-map)# ", + 1 +}; + +/* Initialization of route map vector. */ +void +route_map_init_vty () +{ + /* Install route map top node. */ + install_node (&rmap_node, route_map_config_write); + + /* Install route map commands. */ + install_default (RMAP_NODE); + install_element (CONFIG_NODE, &route_map_cmd); + install_element (CONFIG_NODE, &no_route_map_cmd); + install_element (CONFIG_NODE, &no_route_map_all_cmd); + + /* Install the on-match stuff */ + install_element (RMAP_NODE, &route_map_cmd); + install_element (RMAP_NODE, &rmap_onmatch_next_cmd); + install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd); + install_element (RMAP_NODE, &rmap_onmatch_goto_cmd); + install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd); +} diff --git a/lib/routemap.h b/lib/routemap.h new file mode 100644 index 00000000..e37f1e7b --- /dev/null +++ b/lib/routemap.h @@ -0,0 +1,194 @@ +/* Route map function. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_ROUTEMAP_H +#define _ZEBRA_ROUTEMAP_H + +/* Route map's type. */ +enum route_map_type +{ + RMAP_PERMIT, + RMAP_DENY, + RMAP_ANY +}; + +typedef enum +{ + RMAP_MATCH, + RMAP_DENYMATCH, + RMAP_NOMATCH, + RMAP_ERROR, + RMAP_OKAY +} route_map_result_t; + +typedef enum +{ + RMAP_RIP, + RMAP_RIPNG, + RMAP_OSPF, + RMAP_OSPF6, + RMAP_BGP +} route_map_object_t; + +typedef enum +{ + RMAP_EXIT, + RMAP_GOTO, + RMAP_NEXT +} route_map_end_t; + +typedef enum +{ + RMAP_EVENT_SET_ADDED, + RMAP_EVENT_SET_DELETED, + RMAP_EVENT_SET_REPLACED, + RMAP_EVENT_MATCH_ADDED, + RMAP_EVENT_MATCH_DELETED, + RMAP_EVENT_MATCH_REPLACED, + RMAP_EVENT_INDEX_ADDED, + RMAP_EVENT_INDEX_DELETED +} route_map_event_t; + +/* Route map rule structure for matching and setting. */ +struct route_map_rule_cmd +{ + /* Route map rule name (e.g. as-path, metric) */ + char *str; + + /* Function for value set or match. */ + route_map_result_t (*func_apply)(void *, struct prefix *, + route_map_object_t, void *); + + /* Compile argument and return result as void *. */ + void *(*func_compile)(char *); + + /* Free allocated value by func_compile (). */ + void (*func_free)(void *); +}; + +/* Route map apply error. */ +enum +{ + /* Route map rule is missing. */ + RMAP_RULE_MISSING = 1, + + /* Route map rule can't compile */ + RMAP_COMPILE_ERROR +}; + +/* Route map rule list. */ +struct route_map_rule_list +{ + struct route_map_rule *head; + struct route_map_rule *tail; +}; + +/* Route map index structure. */ +struct route_map_index +{ + struct route_map *map; + + /* Preference of this route map rule. */ + int pref; + + /* Route map type permit or deny. */ + enum route_map_type type; + + /* Do we follow old rules, or hop forward? */ + route_map_end_t exitpolicy; + + /* If we're using "GOTO", to where do we go? */ + int nextpref; + + /* Matching rule list. */ + struct route_map_rule_list match_list; + struct route_map_rule_list set_list; + + /* Make linked list. */ + struct route_map_index *next; + struct route_map_index *prev; +}; + +/* Route map list structure. */ +struct route_map +{ + /* Name of route map. */ + char *name; + + /* Route map's rule. */ + struct route_map_index *head; + struct route_map_index *tail; + + /* Make linked list. */ + struct route_map *next; + struct route_map *prev; +}; + +/* Prototypes. */ +void route_map_init (); +void route_map_init_vty (); + +/* Add match statement to route map. */ +int +route_map_add_match (struct route_map_index *index, + char *match_name, + char *match_arg); + +/* Delete specified route match rule. */ +int +route_map_delete_match (struct route_map_index *index, + char *match_name, + char *match_arg); + +/* Add route-map set statement to the route map. */ +int +route_map_add_set (struct route_map_index *index, + char *set_name, + char *set_arg); + +/* Delete route map set rule. */ +int +route_map_delete_set (struct route_map_index *index, char *set_name, + char *set_arg); + +/* Install rule command to the match list. */ +void +route_map_install_match (struct route_map_rule_cmd *cmd); + +/* Install rule command to the set list. */ +void +route_map_install_set (struct route_map_rule_cmd *cmd); + +/* Lookup route map by name. */ +struct route_map * +route_map_lookup_by_name (char *name); + +/* Apply route map to the object. */ +route_map_result_t +route_map_apply (struct route_map *map, struct prefix *, + route_map_object_t object_type, void *object); + +void route_map_add_hook (void (*func) (char *)); +void route_map_delete_hook (void (*func) (char *)); +void route_map_event_hook (void (*func) (route_map_event_t, char *)); + + +#endif /* _ZEBRA_ROUTEMAP_H */ diff --git a/lib/smux.c b/lib/smux.c new file mode 100644 index 00000000..32f8c8ff --- /dev/null +++ b/lib/smux.c @@ -0,0 +1,1501 @@ +/* SNMP support + * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org> + * + * 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 <zebra.h> + +#ifdef HAVE_SNMP + +#include <asn1.h> +#include <snmp.h> +#include <snmp_impl.h> + +#include "smux.h" +#include "log.h" +#include "thread.h" +#include "linklist.h" +#include "command.h" +#include "version.h" +#include "memory.h" +#include "sockunion.h" + +#define min(A,B) ((A) < (B) ? (A) : (B)) + +enum smux_event {SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ}; + +void smux_event (enum smux_event, int); + + +/* SMUX socket. */ +int smux_sock = -1; + +/* SMUX subtree list. */ +struct list *treelist; + +/* SMUX oid. */ +oid *smux_oid; +size_t smux_oid_len; + +/* SMUX default oid. */ +oid *smux_default_oid; +size_t smux_default_oid_len; + +/* SMUX password. */ +char *smux_passwd; +char *smux_default_passwd = ""; + +/* SMUX read threads. */ +struct thread *smux_read_thread; + +/* SMUX connect thrads. */ +struct thread *smux_connect_thread; + +/* SMUX debug flag. */ +int debug_smux = 0; + +/* SMUX failure count. */ +int fail = 0; + +/* SMUX node. */ +struct cmd_node smux_node = +{ + SMUX_NODE, + "" /* SMUX has no interface. */ +}; + +void * +oid_copy (void *dest, void *src, size_t size) +{ + return memcpy (dest, src, size * sizeof (oid)); +} + +void +oid2in_addr (oid oid[], int len, struct in_addr *addr) +{ + int i; + u_char *pnt; + + if (len == 0) + return; + + pnt = (u_char *) addr; + + for (i = 0; i < len; i++) + *pnt++ = oid[i]; +} + +void +oid_copy_addr (oid oid[], struct in_addr *addr, int len) +{ + int i; + u_char *pnt; + + if (len == 0) + return; + + pnt = (u_char *) addr; + + for (i = 0; i < len; i++) + oid[i] = *pnt++; +} + +int +oid_compare (oid *o1, int o1_len, oid *o2, int o2_len) +{ + int i; + + for (i = 0; i < min (o1_len, o2_len); i++) + { + if (o1[i] < o2[i]) + return -1; + else if (o1[i] > o2[i]) + return 1; + } + if (o1_len < o2_len) + return -1; + if (o1_len > o2_len) + return 1; + + return 0; +} + +int +oid_compare_part (oid *o1, int o1_len, oid *o2, int o2_len) +{ + int i; + + for (i = 0; i < min (o1_len, o2_len); i++) + { + if (o1[i] < o2[i]) + return -1; + else if (o1[i] > o2[i]) + return 1; + } + if (o1_len < o2_len) + return -1; + + return 0; +} + +void +smux_oid_dump (char *prefix, oid *oid, size_t oid_len) +{ + int i; + int first = 1; + char buf[MAX_OID_LEN * 3]; + + buf[0] = '\0'; + + for (i = 0; i < oid_len; i++) + { + sprintf (buf + strlen (buf), "%s%d", first ? "" : ".", (int) oid[i]); + first = 0; + } + zlog_info ("%s: %s", prefix, buf); +} + +int +smux_socket () +{ + int ret; +#ifdef HAVE_IPV6 + struct addrinfo hints, *res0, *res; + int gai; +#else + struct sockaddr_in serv; + struct servent *sp; +#endif + int sock = 0; + +#ifdef HAVE_IPV6 + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + gai = getaddrinfo(NULL, "smux", &hints, &res0); + if (gai == EAI_SERVICE) + { + char servbuf[NI_MAXSERV]; + sprintf(servbuf,"%d",SMUX_PORT_DEFAULT); + servbuf[sizeof (servbuf) - 1] = '\0'; + gai = getaddrinfo(NULL, servbuf, &hints, &res0); + } + if (gai) + { + zlog_warn("Cannot locate loopback service smux"); + return -1; + } + for(res=res0; res; res=res->ai_next) + { + if (res->ai_family != AF_INET +#ifdef HAVE_IPV6 + && res->ai_family != AF_INET6 +#endif /* HAVE_IPV6 */ + ) + continue; + + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock < 0) + continue; + sockopt_reuseaddr (sock); + sockopt_reuseport (sock); + ret = connect (sock, res->ai_addr, res->ai_addrlen); + if (ret < 0) + { + close(sock); + sock = -1; + continue; + } + break; + } + freeaddrinfo(res0); + if (sock < 0) + zlog_warn ("Can't connect to SNMP agent with SMUX"); +#else + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + zlog_warn ("Can't make socket for SNMP"); + return -1; + } + + memset (&serv, 0, sizeof (struct sockaddr_in)); + serv.sin_family = AF_INET; +#ifdef HAVE_SIN_LEN + serv.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_SIN_LEN */ + + sp = getservbyname ("smux", "tcp"); + if (sp != NULL) + serv.sin_port = sp->s_port; + else + serv.sin_port = htons (SMUX_PORT_DEFAULT); + + serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + sockopt_reuseaddr (sock); + sockopt_reuseport (sock); + + ret = connect (sock, (struct sockaddr *) &serv, sizeof (struct sockaddr_in)); + if (ret < 0) + { + close (sock); + smux_sock = -1; + zlog_warn ("Can't connect to SNMP agent with SMUX"); + return -1; + } +#endif + return sock; +} + +void +smux_getresp_send (oid objid[], size_t objid_len, long reqid, long errstat, + long errindex, u_char val_type, void *arg, size_t arg_len) +{ + int ret; + u_char buf[BUFSIZ]; + u_char *ptr, *h1, *h1e, *h2, *h2e; + int len, length; + + ptr = buf; + len = BUFSIZ; + length = len; + + if (debug_smux) + { + zlog_info ("SMUX GETRSP send"); + zlog_info ("SMUX GETRSP reqid: %ld", reqid); + } + + h1 = ptr; + /* Place holder h1 for complete sequence */ + ptr = asn_build_sequence (ptr, &len, (u_char) SMUX_GETRSP, 0); + h1e = ptr; + + ptr = asn_build_int (ptr, &len, + (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &reqid, sizeof (reqid)); + + if (debug_smux) + zlog_info ("SMUX GETRSP errstat: %ld", errstat); + + ptr = asn_build_int (ptr, &len, + (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &errstat, sizeof (errstat)); + if (debug_smux) + zlog_info ("SMUX GETRSP errindex: %ld", errindex); + + ptr = asn_build_int (ptr, &len, + (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &errindex, sizeof (errindex)); + + h2 = ptr; + /* Place holder h2 for one variable */ + ptr = asn_build_sequence (ptr, &len, + (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), + 0); + h2e = ptr; + + ptr = snmp_build_var_op (ptr, objid, &objid_len, + val_type, arg_len, arg, &len); + + /* Now variable size is known, fill in size */ + asn_build_sequence(h2,&length,(u_char)(ASN_SEQUENCE|ASN_CONSTRUCTOR),ptr-h2e); + + /* Fill in size of whole sequence */ + asn_build_sequence(h1,&length,(u_char)SMUX_GETRSP,ptr-h1e); + + if (debug_smux) + zlog_info ("SMUX getresp send: %d", ptr - buf); + + ret = send (smux_sock, buf, (ptr - buf), 0); +} + +char * +smux_var (char *ptr, int len, oid objid[], size_t *objid_len, + size_t *var_val_len, + u_char *var_val_type, + void **var_value) +{ + u_char type; + u_char val_type; + size_t val_len; + u_char *val; + + if (debug_smux) + zlog_info ("SMUX var parse: len %d", len); + + /* Parse header. */ + ptr = asn_parse_header (ptr, &len, &type); + + if (debug_smux) + { + zlog_info ("SMUX var parse: type %d len %d", type, len); + zlog_info ("SMUX var parse: type must be %d", + (ASN_SEQUENCE | ASN_CONSTRUCTOR)); + } + + /* Parse var option. */ + *objid_len = MAX_OID_LEN; + ptr = snmp_parse_var_op(ptr, objid, objid_len, &val_type, + &val_len, &val, &len); + + if (var_val_len) + *var_val_len = val_len; + + if (var_value) + *var_value = (void*) val; + + if (var_val_type) + *var_val_type = val_type; + + /* Requested object id length is objid_len. */ + if (debug_smux) + smux_oid_dump ("Request OID", objid, *objid_len); + + if (debug_smux) + zlog_info ("SMUX val_type: %d", val_type); + + /* Check request value type. */ + if (debug_smux) + switch (val_type) + { + case ASN_NULL: + /* In case of SMUX_GET or SMUX_GET_NEXT val_type is set to + ASN_NULL. */ + zlog_info ("ASN_NULL"); + break; + + case ASN_INTEGER: + zlog_info ("ASN_INTEGER"); + break; + case ASN_COUNTER: + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_UINTEGER: + zlog_info ("ASN_COUNTER"); + break; + case ASN_COUNTER64: + zlog_info ("ASN_COUNTER64"); + break; + case ASN_IPADDRESS: + zlog_info ("ASN_IPADDRESS"); + break; + case ASN_OCTET_STR: + zlog_info ("ASN_OCTET_STR"); + break; + case ASN_OPAQUE: + case ASN_NSAP: + case ASN_OBJECT_ID: + zlog_info ("ASN_OPAQUE"); + break; + case SNMP_NOSUCHOBJECT: + zlog_info ("SNMP_NOSUCHOBJECT"); + break; + case SNMP_NOSUCHINSTANCE: + zlog_info ("SNMP_NOSUCHINSTANCE"); + break; + case SNMP_ENDOFMIBVIEW: + zlog_info ("SNMP_ENDOFMIBVIEW"); + break; + case ASN_BIT_STR: + zlog_info ("ASN_BIT_STR"); + break; + default: + zlog_info ("Unknown type"); + break; + } + return ptr; +} + +/* NOTE: all 3 functions (smux_set, smux_get & smux_getnext) are based on + ucd-snmp smux and as such suppose, that the peer receives in the message + only one variable. Fortunately, IBM seems to do the same in AIX. */ + +int +smux_set (oid *reqid, size_t *reqid_len, + u_char val_type, void *val, size_t val_len, int action) +{ + int j; + struct subtree *subtree; + struct variable *v; + int subresult; + oid *suffix; + int suffix_len; + int result; + u_char *statP = NULL; + WriteMethod *write_method = NULL; + struct listnode *node; + + /* Check */ + for (node = treelist->head; node; node = node->next) + { + subtree = node->data; + subresult = oid_compare_part (reqid, *reqid_len, + subtree->name, subtree->name_len); + + /* Subtree matched. */ + if (subresult == 0) + { + /* Prepare suffix. */ + suffix = reqid + subtree->name_len; + suffix_len = *reqid_len - subtree->name_len; + result = subresult; + + /* Check variables. */ + for (j = 0; j < subtree->variables_num; j++) + { + v = &subtree->variables[j]; + + /* Always check suffix */ + result = oid_compare_part (suffix, suffix_len, + v->name, v->namelen); + + /* This is exact match so result must be zero. */ + if (result == 0) + { + if (debug_smux) + zlog_info ("SMUX function call index is %d", v->magic); + + statP = (*v->findVar) (v, suffix, &suffix_len, 1, + &val_len, &write_method); + + if (write_method) + { + return (*write_method)(action, val, val_type, val_len, + statP, suffix, suffix_len, v); + } + else + { + return SNMP_ERR_READONLY; + } + } + + /* If above execution is failed or oid is small (so + there is no further match). */ + if (result < 0) + return SNMP_ERR_NOSUCHNAME; + } + } + } + return SNMP_ERR_NOSUCHNAME; +} + +int +smux_get (oid *reqid, size_t *reqid_len, int exact, + u_char *val_type,void **val, size_t *val_len) +{ + int j; + struct subtree *subtree; + struct variable *v; + int subresult; + oid *suffix; + int suffix_len; + int result; + WriteMethod *write_method=NULL; + struct listnode *node; + + /* Check */ + for (node = treelist->head; node; node = node->next) + { + subtree = node->data; + subresult = oid_compare_part (reqid, *reqid_len, + subtree->name, subtree->name_len); + + /* Subtree matched. */ + if (subresult == 0) + { + /* Prepare suffix. */ + suffix = reqid + subtree->name_len; + suffix_len = *reqid_len - subtree->name_len; + result = subresult; + + /* Check variables. */ + for (j = 0; j < subtree->variables_num; j++) + { + v = &subtree->variables[j]; + + /* Always check suffix */ + result = oid_compare_part (suffix, suffix_len, + v->name, v->namelen); + + /* This is exact match so result must be zero. */ + if (result == 0) + { + if (debug_smux) + zlog_info ("SMUX function call index is %d", v->magic); + + *val = (*v->findVar) (v, suffix, &suffix_len, exact, + val_len, &write_method); + + /* There is no instance. */ + if (*val == NULL) + return SNMP_NOSUCHINSTANCE; + + /* Call is suceed. */ + *val_type = v->type; + + return 0; + } + + /* If above execution is failed or oid is small (so + there is no further match). */ + if (result < 0) + return SNMP_ERR_NOSUCHNAME; + } + } + } + return SNMP_ERR_NOSUCHNAME; +} + +int +smux_getnext (oid *reqid, size_t *reqid_len, int exact, + u_char *val_type,void **val, size_t *val_len) +{ + int j; + oid save[MAX_OID_LEN]; + int savelen = 0; + struct subtree *subtree; + struct variable *v; + int subresult; + oid *suffix; + int suffix_len; + int result; + WriteMethod *write_method=NULL; + struct listnode *node; + + + /* Save incoming request. */ + oid_copy (save, reqid, *reqid_len); + savelen = *reqid_len; + + /* Check */ + for (node = treelist->head; node; node = node->next) + { + subtree = node->data; + subresult = oid_compare_part (reqid, *reqid_len, + subtree->name, subtree->name_len); + + /* If request is in the tree. The agent has to make sure we + only receive requests we have registered for. */ + /* Unfortunately, that's not true. In fact, a SMUX subagent has to + behave as if it manages the whole SNMP MIB tree itself. It's the + duty of the master agent to collect the best answer and return it + to the manager. See RFC 1227 chapter 3.1.6 for the glory details + :-). ucd-snmp really behaves bad here as it actually might ask + multiple times for the same GETNEXT request as it throws away the + answer when it expects it in a different subtree and might come + back later with the very same request. --jochen */ + + if (subresult <= 0) + { + /* Prepare suffix. */ + suffix = reqid + subtree->name_len; + suffix_len = *reqid_len - subtree->name_len; + if (subresult < 0) + { + oid_copy(reqid, subtree->name, subtree->name_len); + *reqid_len = subtree->name_len; + } + for (j = 0; j < subtree->variables_num; j++) + { + result = subresult; + v = &subtree->variables[j]; + + /* Next then check result >= 0. */ + if (result == 0) + result = oid_compare_part (suffix, suffix_len, + v->name, v->namelen); + + if (result <= 0) + { + if (debug_smux) + zlog_info ("SMUX function call index is %d", v->magic); + if(result<0) + { + oid_copy(suffix, v->name, v->namelen); + suffix_len = v->namelen; + } + *val = (*v->findVar) (v, suffix, &suffix_len, exact, + val_len, &write_method); + *reqid_len = suffix_len + subtree->name_len; + if (*val) + { + *val_type = v->type; + return 0; + } + } + } + } + } + memcpy (reqid, save, savelen * sizeof(oid)); + *reqid_len = savelen; + + return SNMP_ERR_NOSUCHNAME; +} + +/* GET message header. */ +char * +smux_parse_get_header (char *ptr, size_t *len, long *reqid) +{ + u_char type; + long errstat; + long errindex; + + /* Request ID. */ + ptr = asn_parse_int (ptr, len, &type, reqid, sizeof (*reqid)); + + if (debug_smux) + zlog_info ("SMUX GET reqid: %d len: %d", (int) *reqid, (int) *len); + + /* Error status. */ + ptr = asn_parse_int (ptr, len, &type, &errstat, sizeof (errstat)); + + if (debug_smux) + zlog_info ("SMUX GET errstat %ld len: %d", errstat, *len); + + /* Error index. */ + ptr = asn_parse_int (ptr, len, &type, &errindex, sizeof (errindex)); + + if (debug_smux) + zlog_info ("SMUX GET errindex %ld len: %d", errindex, *len); + + return ptr; +} + +void +smux_parse_set (char *ptr, size_t len, int action) +{ + long reqid; + oid oid[MAX_OID_LEN]; + size_t oid_len; + u_char val_type; + void *val; + size_t val_len; + int ret; + + if (debug_smux) + zlog_info ("SMUX SET(%s) message parse: len %d", + (RESERVE1 == action) ? "RESERVE1" : ((FREE == action) ? "FREE" : "COMMIT"), + len); + + /* Parse SET message header. */ + ptr = smux_parse_get_header (ptr, &len, &reqid); + + /* Parse SET message object ID. */ + ptr = smux_var (ptr, len, oid, &oid_len, &val_len, &val_type, &val); + + ret = smux_set (oid, &oid_len, val_type, val, val_len, action); + if (debug_smux) + zlog_info ("SMUX SET ret %d", ret); + + /* Return result. */ + if (RESERVE1 == action) + smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0); +} + +void +smux_parse_get (char *ptr, size_t len, int exact) +{ + long reqid; + oid oid[MAX_OID_LEN]; + size_t oid_len; + u_char val_type; + void *val; + size_t val_len; + int ret; + + if (debug_smux) + zlog_info ("SMUX GET message parse: len %d", len); + + /* Parse GET message header. */ + ptr = smux_parse_get_header (ptr, &len, &reqid); + + /* Parse GET message object ID. We needn't the value come */ + ptr = smux_var (ptr, len, oid, &oid_len, NULL, NULL, NULL); + + /* Traditional getstatptr. */ + if (exact) + ret = smux_get (oid, &oid_len, exact, &val_type, &val, &val_len); + else + ret = smux_getnext (oid, &oid_len, exact, &val_type, &val, &val_len); + + /* Return result. */ + if (ret == 0) + smux_getresp_send (oid, oid_len, reqid, 0, 0, val_type, val, val_len); + else + smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0); +} + +/* Parse SMUX_CLOSE message. */ +void +smux_parse_close (char *ptr, int len) +{ + long reason = 0; + + while (len--) + { + reason = (reason << 8) | (long) *ptr; + ptr++; + } + zlog_info ("SMUX_CLOSE with reason: %ld", reason); +} + +/* SMUX_RRSP message. */ +void +smux_parse_rrsp (char *ptr, int len) +{ + char val; + long errstat; + + ptr = asn_parse_int (ptr, &len, &val, &errstat, sizeof (errstat)); + + if (debug_smux) + zlog_info ("SMUX_RRSP value: %d errstat: %ld", val, errstat); +} + +/* Parse SMUX message. */ +int +smux_parse (char *ptr, int len) +{ + /* This buffer we'll use for SOUT message. We could allocate it with + malloc and save only static pointer/lenght, but IMHO static + buffer is a faster solusion. */ + static u_char sout_save_buff[SMUXMAXPKTSIZE]; + static int sout_save_len = 0; + + int len_income = len; /* see note below: YYY */ + u_char type; + u_char rollback; + + rollback = ptr[2]; /* important only for SMUX_SOUT */ + +process_rest: /* see note below: YYY */ + + /* Parse SMUX message type and subsequent length. */ + ptr = asn_parse_header (ptr, &len, &type); + + if (debug_smux) + zlog_info ("SMUX message received type: %d rest len: %d", type, len); + + switch (type) + { + case SMUX_OPEN: + /* Open must be not send from SNMP agent. */ + zlog_warn ("SMUX_OPEN received: resetting connection."); + return -1; + break; + case SMUX_RREQ: + /* SMUX_RREQ message is invalid for us. */ + zlog_warn ("SMUX_RREQ received: resetting connection."); + return -1; + break; + case SMUX_SOUT: + /* SMUX_SOUT message is now valied for us. */ + if (debug_smux) + zlog_info ("SMUX_SOUT(%s)", rollback ? "rollback" : "commit"); + + if (sout_save_len > 0) + { + smux_parse_set (sout_save_buff, sout_save_len, rollback ? FREE : COMMIT); + sout_save_len = 0; + } + else + zlog_warn ("SMUX_SOUT sout_save_len=%d - invalid", (int) sout_save_len); + + if (len_income > 3) + { + /* YYY: this strange code has to solve the "slow peer" + problem: When agent sends SMUX_SOUT message it doesn't + wait any responce and may send some next message to + subagent. Then the peer in 'smux_read()' will recieve + from socket the 'concatenated' buffer, contaning both + SMUX_SOUT message and the next one + (SMUX_GET/SMUX_GETNEXT/SMUX_GET). So we should check: if + the buffer is longer than 3 ( length of SMUX_SOUT ), we + must process the rest of it. This effect may be observed + if 'debug_smux' is set to '1' */ + ptr++; + len = len_income - 3; + goto process_rest; + } + break; + case SMUX_GETRSP: + /* SMUX_GETRSP message is invalid for us. */ + zlog_warn ("SMUX_GETRSP received: resetting connection."); + return -1; + break; + case SMUX_CLOSE: + /* Close SMUX connection. */ + if (debug_smux) + zlog_info ("SMUX_CLOSE"); + smux_parse_close (ptr, len); + return -1; + break; + case SMUX_RRSP: + /* This is response for register message. */ + if (debug_smux) + zlog_info ("SMUX_RRSP"); + smux_parse_rrsp (ptr, len); + break; + case SMUX_GET: + /* Exact request for object id. */ + if (debug_smux) + zlog_info ("SMUX_GET"); + smux_parse_get (ptr, len, 1); + break; + case SMUX_GETNEXT: + /* Next request for object id. */ + if (debug_smux) + zlog_info ("SMUX_GETNEXT"); + smux_parse_get (ptr, len, 0); + break; + case SMUX_SET: + /* SMUX_SET is supported with some limitations. */ + if (debug_smux) + zlog_info ("SMUX_SET"); + + /* save the data for future SMUX_SOUT */ + memcpy (sout_save_buff, ptr, len); + sout_save_len = len; + smux_parse_set (ptr, len, RESERVE1); + break; + default: + zlog_info ("Unknown type: %d", type); + break; + } + return 0; +} + +/* SMUX message read function. */ +int +smux_read (struct thread *t) +{ + int sock; + int len; + u_char buf[SMUXMAXPKTSIZE]; + int ret; + + /* Clear thread. */ + sock = THREAD_FD (t); + smux_read_thread = NULL; + + if (debug_smux) + zlog_info ("SMUX read start"); + + /* Read message from SMUX socket. */ + len = recv (sock, buf, SMUXMAXPKTSIZE, 0); + + if (len < 0) + { + zlog_warn ("Can't read all SMUX packet: %s", strerror (errno)); + close (sock); + smux_sock = -1; + smux_event (SMUX_CONNECT, 0); + return -1; + } + + if (len == 0) + { + zlog_warn ("SMUX connection closed: %d", sock); + close (sock); + smux_sock = -1; + smux_event (SMUX_CONNECT, 0); + return -1; + } + + if (debug_smux) + zlog_info ("SMUX read len: %d", len); + + /* Parse the message. */ + ret = smux_parse (buf, len); + + if (ret < 0) + { + close (sock); + smux_sock = -1; + smux_event (SMUX_CONNECT, 0); + return -1; + } + + /* Regiser read thread. */ + smux_event (SMUX_READ, sock); + + return 0; +} + +int +smux_open (int sock) +{ + u_char buf[BUFSIZ]; + u_char *ptr; + int len; + u_long version; + u_char progname[] = "zebra-" ZEBRA_VERSION; + + if (debug_smux) + { + smux_oid_dump ("SMUX open oid", smux_oid, smux_oid_len); + zlog_info ("SMUX open progname: %s", progname); + zlog_info ("SMUX open password: %s", smux_passwd); + } + + ptr = buf; + len = BUFSIZ; + + /* SMUX Header. As placeholder. */ + ptr = asn_build_header (ptr, &len, (u_char) SMUX_OPEN, 0); + + /* SMUX Open. */ + version = 0; + ptr = asn_build_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &version, sizeof (u_long)); + + /* SMUX connection oid. */ + ptr = asn_build_objid (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID), + smux_oid, smux_oid_len); + + /* SMUX connection description. */ + ptr = asn_build_string (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), + progname, strlen (progname)); + + /* SMUX connection password. */ + ptr = asn_build_string (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), + smux_passwd, strlen (smux_passwd)); + + /* Fill in real SMUX header. We exclude ASN header size (2). */ + len = BUFSIZ; + asn_build_header (buf, &len, (u_char) SMUX_OPEN, (ptr - buf) - 2); + + return send (sock, buf, (ptr - buf), 0); +} + +int +smux_trap (oid *name, size_t namelen, + oid *iname, size_t inamelen, + struct trap_object *trapobj, size_t trapobjlen, + unsigned int tick) +{ + int i; + u_char buf[BUFSIZ]; + u_char *ptr; + int len, length; + struct in_addr addr; + unsigned long val; + u_char *h1, *h1e; + + ptr = buf; + len = BUFSIZ; + length = len; + + /* When SMUX connection is not established. */ + if (smux_sock < 0) + return 0; + + /* SMUX header. */ + ptr = asn_build_header (ptr, &len, (u_char) SMUX_TRAP, 0); + + /* Sub agent enterprise oid. */ + ptr = asn_build_objid (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID), + smux_oid, smux_oid_len); + + /* IP address. */ + addr.s_addr = 0; + ptr = asn_build_string (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_IPADDRESS), + (u_char *)&addr, sizeof (struct in_addr)); + + /* Generic trap integer. */ + val = SNMP_TRAP_ENTERPRISESPECIFIC; + ptr = asn_build_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &val, sizeof (int)); + + /* Specific trap integer. */ + val = 2; + ptr = asn_build_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &val, sizeof (int)); + + /* Timeticks timestamp. */ + val = 0; + ptr = asn_build_unsigned_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_TIMETICKS), + &val, sizeof (int)); + + /* Variables. */ + h1 = ptr; + ptr = asn_build_sequence (ptr, &len, + (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), + 0); + + + /* Iteration for each objects. */ + h1e = ptr; + for (i = 0; i < trapobjlen; i++) + { + int ret; + oid oid[MAX_OID_LEN]; + size_t oid_len; + void *val; + size_t val_len; + u_char val_type; + + /* Make OID. */ + oid_copy (oid, name, namelen); + oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen); + oid_copy (oid + namelen + trapobj[i].namelen, iname, inamelen); + oid_len = namelen + trapobj[i].namelen + inamelen; + + if (debug_smux) + smux_oid_dump ("Trap", oid, oid_len); + + ret = smux_get (oid, &oid_len, 1, &val_type, &val, &val_len); + + if (debug_smux) + zlog_info ("smux_get result %d", ret); + + if (ret == 0) + ptr = snmp_build_var_op (ptr, oid, &oid_len, + val_type, val_len, val, &len); + } + + /* Now variable size is known, fill in size */ + asn_build_sequence(h1, &length, + (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), + ptr - h1e); + + /* Fill in size of whole sequence */ + len = BUFSIZ; + asn_build_header (buf, &len, (u_char) SMUX_TRAP, (ptr - buf) - 2); + + return send (smux_sock, buf, (ptr - buf), 0); +} + +int +smux_register (int sock) +{ + u_char buf[BUFSIZ]; + u_char *ptr; + int len, ret; + long priority; + long operation; + struct subtree *subtree; + struct listnode *node; + + ret = 0; + + for (node = treelist->head; node; node = node->next) + { + ptr = buf; + len = BUFSIZ; + + subtree = node->data; + + /* SMUX RReq Header. */ + ptr = asn_build_header (ptr, &len, (u_char) SMUX_RREQ, 0); + + /* Register MIB tree. */ + ptr = asn_build_objid (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID), + subtree->name, subtree->name_len); + + /* Priority. */ + priority = -1; + ptr = asn_build_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &priority, sizeof (u_long)); + + /* Operation. */ + operation = 2; /* Register R/W */ + ptr = asn_build_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &operation, sizeof (u_long)); + + if (debug_smux) + { + smux_oid_dump ("SMUX register oid", subtree->name, subtree->name_len); + zlog_info ("SMUX register priority: %ld", priority); + zlog_info ("SMUX register operation: %ld", operation); + } + + len = BUFSIZ; + asn_build_header (buf, &len, (u_char) SMUX_RREQ, (ptr - buf) - 2); + ret = send (sock, buf, (ptr - buf), 0); + if (ret < 0) + return ret; + } + return ret; +} + +/* Try to connect to SNMP agent. */ +int +smux_connect (struct thread *t) +{ + int ret; + + if (debug_smux) + zlog_info ("SMUX connect try %d", fail + 1); + + /* Clear thread poner of myself. */ + smux_connect_thread = NULL; + + /* Make socket. Try to connect. */ + smux_sock = smux_socket (); + if (smux_sock < 0) + { + if (++fail < SMUX_MAX_FAILURE) + smux_event (SMUX_CONNECT, 0); + return 0; + } + + /* Send OPEN PDU. */ + ret = smux_open (smux_sock); + if (ret < 0) + { + zlog_warn ("SMUX open message send failed: %s", strerror (errno)); + close (smux_sock); + smux_sock = -1; + if (++fail < SMUX_MAX_FAILURE) + smux_event (SMUX_CONNECT, 0); + return -1; + } + + /* Send any outstanding register PDUs. */ + ret = smux_register (smux_sock); + if (ret < 0) + { + zlog_warn ("SMUX register message send failed: %s", strerror (errno)); + close (smux_sock); + smux_sock = -1; + if (++fail < SMUX_MAX_FAILURE) + smux_event (SMUX_CONNECT, 0); + return -1; + } + + /* Everything goes fine. */ + smux_event (SMUX_READ, smux_sock); + + return 0; +} + +/* Clear all SMUX related resources. */ +void +smux_stop () +{ + if (smux_read_thread) + thread_cancel (smux_read_thread); + if (smux_connect_thread) + thread_cancel (smux_connect_thread); + + if (smux_sock >= 0) + { + close (smux_sock); + smux_sock = -1; + } +} + +extern struct thread_master *master; + +void +smux_event (enum smux_event event, int sock) +{ + switch (event) + { + case SMUX_SCHEDULE: + smux_connect_thread = thread_add_event (master, smux_connect, NULL, 0); + break; + case SMUX_CONNECT: + smux_connect_thread = thread_add_timer (master, smux_connect, NULL, 10); + break; + case SMUX_READ: + smux_read_thread = thread_add_read (master, smux_read, NULL, sock); + break; + default: + break; + } +} + +int +smux_str2oid (char *str, oid *oid, size_t *oid_len) +{ + int len; + int val; + + len = 0; + val = 0; + *oid_len = 0; + + if (*str == '.') + str++; + if (*str == '\0') + return 0; + + while (1) + { + if (! isdigit (*str)) + return -1; + + while (isdigit (*str)) + { + val *= 10; + val += (*str - '0'); + str++; + } + + if (*str == '\0') + break; + if (*str != '.') + return -1; + + oid[len++] = val; + val = 0; + str++; + } + + oid[len++] = val; + *oid_len = len; + + return 0; +} + +oid * +smux_oid_dup (oid *objid, size_t objid_len) +{ + oid *new; + + new = XMALLOC (MTYPE_TMP, sizeof (oid) * objid_len); + oid_copy (new, objid, objid_len); + + return new; +} + +int +smux_peer_oid (struct vty *vty, char *oid_str, char *passwd_str) +{ + int ret; + oid oid[MAX_OID_LEN]; + size_t oid_len; + + ret = smux_str2oid (oid_str, oid, &oid_len); + if (ret != 0) + { + vty_out (vty, "object ID malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (smux_oid && smux_oid != smux_default_oid) + free (smux_oid); + + if (smux_passwd && smux_passwd != smux_default_passwd) + { + free (smux_passwd); + smux_passwd = NULL; + } + + smux_oid = smux_oid_dup (oid, oid_len); + smux_oid_len = oid_len; + + if (passwd_str) + smux_passwd = strdup (passwd_str); + + return CMD_SUCCESS; +} + +int +smux_header_generic (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + oid fulloid[MAX_OID_LEN]; + int ret; + + oid_copy (fulloid, v->name, v->namelen); + fulloid[v->namelen] = 0; + /* Check against full instance. */ + ret = oid_compare (name, *length, fulloid, v->namelen + 1); + + /* Check single instance. */ + if ((exact && (ret != 0)) || (!exact && (ret >= 0))) + return MATCH_FAILED; + + /* In case of getnext, fill in full instance. */ + memcpy (name, fulloid, (v->namelen + 1) * sizeof (oid)); + *length = v->namelen + 1; + + *write_method = 0; + *var_len = sizeof(long); /* default to 'long' results */ + + return MATCH_SUCCEEDED; +} + +int +smux_peer_default () +{ + if (smux_oid != smux_default_oid) + { + free (smux_oid); + smux_oid = smux_default_oid; + smux_oid_len = smux_default_oid_len; + } + if (smux_passwd != smux_default_passwd) + { + free (smux_passwd); + smux_passwd = smux_default_passwd; + } + return CMD_SUCCESS; +} + +DEFUN (smux_peer, + smux_peer_cmd, + "smux peer OID", + "SNMP MUX protocol settings\n" + "SNMP MUX peer settings\n" + "Object ID used in SMUX peering\n") +{ + return smux_peer_oid (vty, argv[0], NULL); +} + +DEFUN (smux_peer_password, + smux_peer_password_cmd, + "smux peer OID PASSWORD", + "SNMP MUX protocol settings\n" + "SNMP MUX peer settings\n" + "SMUX peering object ID\n" + "SMUX peering password\n") +{ + return smux_peer_oid (vty, argv[0], argv[1]); +} + +DEFUN (no_smux_peer, + no_smux_peer_cmd, + "no smux peer OID", + NO_STR + "SNMP MUX protocol settings\n" + "SNMP MUX peer settings\n" + "Object ID used in SMUX peering\n") +{ + return smux_peer_default (); +} + +DEFUN (no_smux_peer_password, + no_smux_peer_password_cmd, + "no smux peer OID PASSWORD", + NO_STR + "SNMP MUX protocol settings\n" + "SNMP MUX peer settings\n" + "SMUX peering object ID\n" + "SMUX peering password\n") +{ + return smux_peer_default (); +} + +int +config_write_smux (struct vty *vty) +{ + int first = 1; + int i; + + if (smux_oid != smux_default_oid || smux_passwd != smux_default_passwd) + { + vty_out (vty, "smux peer "); + for (i = 0; i < smux_oid_len; i++) + { + vty_out (vty, "%s%d", first ? "" : ".", (int) smux_oid[i]); + first = 0; + } + vty_out (vty, " %s%s", smux_passwd, VTY_NEWLINE); + } + return 0; +} + +/* Register subtree to smux master tree. */ +void +smux_register_mib (char *descr, struct variable *var, size_t width, int num, + oid name[], size_t namelen) +{ + struct subtree *tree; + + tree = (struct subtree *)malloc(sizeof(struct subtree)); + oid_copy (tree->name, name, namelen); + tree->name_len = namelen; + tree->variables = var; + tree->variables_num = num; + tree->variables_width = width; + tree->registered = 0; + listnode_add_sort(treelist, tree); +} + +void +smux_reset () +{ + /* Setting configuration to default. */ + smux_peer_default (); +} + +/* Compare function to keep treelist sorted */ +static int +smux_tree_cmp(struct subtree *tree1, struct subtree *tree2) +{ + return oid_compare(tree1->name, tree1->name_len, + tree2->name, tree2->name_len); +} + +/* Initialize some values then schedule first SMUX connection. */ +void +smux_init (oid defoid[], size_t defoid_len) +{ + /* Set default SMUX oid. */ + smux_default_oid = defoid; + smux_default_oid_len = defoid_len; + + smux_oid = smux_default_oid; + smux_oid_len = smux_default_oid_len; + smux_passwd = smux_default_passwd; + + /* Make MIB tree. */ + treelist = list_new(); + treelist->cmp = (int (*)(void *, void *))smux_tree_cmp; + + /* Install commands. */ + install_node (&smux_node, config_write_smux); + + install_element (CONFIG_NODE, &smux_peer_cmd); + install_element (CONFIG_NODE, &smux_peer_password_cmd); + install_element (CONFIG_NODE, &no_smux_peer_cmd); + install_element (CONFIG_NODE, &no_smux_peer_password_cmd); +} + +void +smux_start(void) +{ + /* Schedule first connection. */ + smux_event (SMUX_SCHEDULE, 0); +} +#endif /* HAVE_SNMP */ diff --git a/lib/smux.h b/lib/smux.h new file mode 100644 index 00000000..91c3d46f --- /dev/null +++ b/lib/smux.h @@ -0,0 +1,159 @@ +/* SNMP support + * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org> + * + * 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. + */ + +#ifndef _ZEBRA_SNMP_H +#define _ZEBRA_SNMP_H + +#define SMUX_PORT_DEFAULT 199 + +#define SMUXMAXPKTSIZE 1500 +#define SMUXMAXSTRLEN 256 + +#define SMUX_OPEN (ASN_APPLICATION | ASN_CONSTRUCTOR | 0) +#define SMUX_CLOSE (ASN_APPLICATION | ASN_PRIMITIVE | 1) +#define SMUX_RREQ (ASN_APPLICATION | ASN_CONSTRUCTOR | 2) +#define SMUX_RRSP (ASN_APPLICATION | ASN_PRIMITIVE | 3) +#define SMUX_SOUT (ASN_APPLICATION | ASN_PRIMITIVE | 4) + +#define SMUX_GET (ASN_CONTEXT | ASN_CONSTRUCTOR | 0) +#define SMUX_GETNEXT (ASN_CONTEXT | ASN_CONSTRUCTOR | 1) +#define SMUX_GETRSP (ASN_CONTEXT | ASN_CONSTRUCTOR | 2) +#define SMUX_SET (ASN_CONTEXT | ASN_CONSTRUCTOR | 3) +#define SMUX_TRAP (ASN_CONTEXT | ASN_CONSTRUCTOR | 4) + +#define SMUX_MAX_FAILURE 3 + +/* Structures here are mostly compatible with UCD SNMP 4.1.1 */ +#define MATCH_FAILED (-1) +#define MATCH_SUCCEEDED 0 + +/* SYNTAX TruthValue from SNMPv2-TC. */ +#define SNMP_TRUE 1 +#define SNMP_FALSE 2 + +/* SYNTAX RowStatus from SNMPv2-TC. */ +#define SNMP_VALID 1 +#define SNMP_INVALID 2 + +#define IN_ADDR_SIZE sizeof(struct in_addr) + +struct variable; + +#define REGISTER_MIB(descr, var, vartype, theoid) \ + smux_register_mib(descr, (struct variable *)var, sizeof(struct vartype), \ + sizeof(var)/sizeof(struct vartype), \ + theoid, sizeof(theoid)/sizeof(oid)) + +typedef int (WriteMethod)(int action, + u_char *var_val, + u_char var_val_type, + size_t var_val_len, + u_char *statP, + oid *name, + size_t length, + struct variable *v); + +typedef u_char *(FindVarMethod)(struct variable *v, + oid *name, + size_t *length, + int exact, + size_t *var_len, + WriteMethod **write_method); + +/* SNMP variable */ +struct variable +{ + /* Index of the MIB.*/ + u_char magic; + + /* Type of variable. */ + char type; + + /* Access control list. */ + u_short acl; + + /* Callback function. */ + FindVarMethod *findVar; + + /* Suffix of the MIB. */ + u_char namelen; + oid name[MAX_OID_LEN]; +}; + +/* SNMP tree. */ +struct subtree +{ + /* Tree's oid. */ + oid name[MAX_OID_LEN]; + u_char name_len; + + /* List of the variables. */ + struct variable *variables; + + /* Length of the variables list. */ + int variables_num; + + /* Width of the variables list. */ + int variables_width; + + /* Registered flag. */ + int registered; +}; + +struct trap_object +{ + FindVarMethod *findVar; + u_char namelen; + oid name[MAX_OID_LEN]; +}; + +/* Declare SMUX return value. */ +#define SNMP_LOCAL_VARIABLES \ + static int32_t snmp_int_val; \ + static struct in_addr snmp_in_addr_val; + +#define SNMP_INTEGER(V) \ + ( \ + *var_len = sizeof (int32_t), \ + snmp_int_val = V, \ + (u_char *) &snmp_int_val \ + ) + +#define SNMP_IPADDRESS(V) \ + ( \ + *var_len = sizeof (struct in_addr), \ + snmp_in_addr_val = V, \ + (u_char *) &snmp_in_addr_val \ + ) + +void smux_init (oid [], size_t); +void smux_start (void); +void smux_register_mib(char *, struct variable *, size_t, int, oid [], size_t); +int smux_header_generic (struct variable *, oid [], size_t *, int, size_t *, + WriteMethod **); +int smux_trap (oid *, size_t, oid *, size_t, struct trap_object *, size_t, unsigned int); + +int oid_compare (oid *, int, oid *, int); +void oid2in_addr (oid [], int, struct in_addr *); +void *oid_copy (void *, void *, size_t); +void oid_copy_addr (oid [], struct in_addr *, int); + +#endif /* _ZEBRA_SNMP_H */ diff --git a/lib/sockopt.c b/lib/sockopt.c new file mode 100644 index 00000000..e2beca9f --- /dev/null +++ b/lib/sockopt.c @@ -0,0 +1,199 @@ +/* setsockopt functions + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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 <zebra.h> +#include "log.h" + +#ifdef HAVE_IPV6 +/* Set IPv6 packet info to the socket. */ +int +setsockopt_ipv6_pktinfo (int sock, int val) +{ + int ret; + +#ifdef IPV6_RECVPKTINFO /*2292bis-01*/ + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", strerror (errno)); +#else /*RFC2292*/ + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", strerror (errno)); +#endif /* INIA_IPV6 */ + return ret; +} + +/* Set multicast hops val to the socket. */ +int +setsockopt_ipv6_checksum (int sock, int val) +{ + int ret; + +#ifdef GNU_LINUX + ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); +#else + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val)); +#endif /* GNU_LINUX */ + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_CHECKSUM"); + return ret; +} + +/* Set multicast hops val to the socket. */ +int +setsockopt_ipv6_multicast_hops (int sock, int val) +{ + int ret; + + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS"); + return ret; +} + +/* Set multicast hops val to the socket. */ +int +setsockopt_ipv6_unicast_hops (int sock, int val) +{ + int ret; + + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS"); + return ret; +} + +int +setsockopt_ipv6_hoplimit (int sock, int val) +{ + int ret; + +#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/ + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT"); +#else /*RFC2292*/ + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_HOPLIMIT"); +#endif + return ret; +} + +/* Set multicast loop zero to the socket. */ +int +setsockopt_ipv6_multicast_loop (int sock, int val) +{ + int ret; + + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, + sizeof (val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP"); + return ret; +} + +#endif /* HAVE_IPV6 */ + + +/* Set up a multicast socket options for IPv4 + This is here so that people only have to do their OS multicast mess + in one place rather than all through zebra, ospfd, and ripd + NB: This is a hookpoint for specific OS functionality */ +int +setsockopt_multicast_ipv4(int sock, + int optname, + struct in_addr if_addr, + unsigned int mcast_addr, + unsigned int ifindex) +{ + + /* Linux 2.2.0 and up */ +#if defined(GNU_LINUX) && LINUX_VERSION_CODE > 131584 + /* This is better because it uses ifindex directly */ + struct ip_mreqn mreqn; + + switch (optname) + { + case IP_MULTICAST_IF: + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + memset (&mreqn, 0, sizeof(mreqn)); + + if (mcast_addr) + mreqn.imr_multiaddr.s_addr = mcast_addr; + + if (ifindex) + mreqn.imr_ifindex = ifindex; + else + mreqn.imr_address = if_addr; + + return setsockopt(sock, IPPROTO_IP, optname, (void *)&mreqn, sizeof(mreqn)); + break; + + default: + /* Can out and give an understandable error */ + errno = EINVAL; + return -1; + break; + } + + /* Example defines for another OS, boilerplate off other code in this + function, AND handle optname as per other sections for consistency !! */ + /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */ + /* Add your favourite OS here! */ + +#else /* #if OS_TYPE */ + /* default OS support */ + + struct in_addr m; + struct ip_mreq mreq; + + switch (optname) + { + case IP_MULTICAST_IF: + m = if_addr; + + return setsockopt (sock, IPPROTO_IP, optname, (void *)&m, sizeof(m)); + break; + + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + memset (&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = mcast_addr; + mreq.imr_interface = if_addr; + + return setsockopt (sock, + IPPROTO_IP, + optname, + (void *)&mreq, + sizeof(mreq)); + break; + + default: + /* Can out and give an understandable error */ + errno = EINVAL; + return -1; + break; + } +#endif /* #if OS_TYPE */ + +} diff --git a/lib/sockopt.h b/lib/sockopt.h new file mode 100644 index 00000000..7fb31c18 --- /dev/null +++ b/lib/sockopt.h @@ -0,0 +1,41 @@ +/* Router advertisement + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_SOCKOPT_H +#define _ZEBRA_SOCKOPT_H + +#ifdef HAVE_IPV6 +int setsockopt_ipv6_pktinfo (int, int); +int setsockopt_ipv6_checksum (int, int); +int setsockopt_ipv6_multicast_hops (int, int); +int setsockopt_ipv6_unicast_hops (int, int); +int setsockopt_ipv6_hoplimit (int, int); +int setsockopt_ipv6_multicast_loop (int, int); +#endif /* HAVE_IPV6 */ + +int setsockopt_multicast_ipv4(int sock, + int optname, + struct in_addr if_addr, + unsigned int mcast_addr, + unsigned int ifindex); + + +#endif /*_ZEBRA_SOCKOPT_H */ diff --git a/lib/sockunion.c b/lib/sockunion.c new file mode 100644 index 00000000..21371624 --- /dev/null +++ b/lib/sockunion.c @@ -0,0 +1,756 @@ +/* Socket union related function. + * Copyright (c) 1997, 98 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "prefix.h" +#include "vty.h" +#include "sockunion.h" +#include "memory.h" +#include "str.h" +#include "log.h" + +#ifndef HAVE_INET_ATON +int +inet_aton (const char *cp, struct in_addr *inaddr) +{ + int dots = 0; + register u_long addr = 0; + register u_long val = 0, base = 10; + + do + { + register char c = *cp; + + switch (c) + { + case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + val = (val * base) + (c - '0'); + break; + case '.': + if (++dots > 3) + return 0; + case '\0': + if (val > 255) + return 0; + addr = addr << 8 | val; + val = 0; + break; + default: + return 0; + } + } while (*cp++) ; + + if (dots < 3) + addr <<= 8 * (3 - dots); + if (inaddr) + inaddr->s_addr = htonl (addr); + return 1; +} +#endif /* ! HAVE_INET_ATON */ + + +#ifndef HAVE_INET_PTON +int +inet_pton (int family, const char *strptr, void *addrptr) +{ + if (family == AF_INET) + { + struct in_addr in_val; + + if (inet_aton (strptr, &in_val)) + { + memcpy (addrptr, &in_val, sizeof (struct in_addr)); + return 1; + } + return 0; + } + errno = EAFNOSUPPORT; + return -1; +} +#endif /* ! HAVE_INET_PTON */ + +#ifndef HAVE_INET_NTOP +const char * +inet_ntop (int family, const void *addrptr, char *strptr, size_t len) +{ + unsigned char *p = (unsigned char *) addrptr; + + if (family == AF_INET) + { + char temp[INET_ADDRSTRLEN]; + + snprintf(temp, sizeof(temp), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + if (strlen(temp) >= len) + { + errno = ENOSPC; + return NULL; + } + strcpy(strptr, temp); + return strptr; + } + + errno = EAFNOSUPPORT; + return NULL; +} +#endif /* ! HAVE_INET_NTOP */ + +const char * +inet_sutop (union sockunion *su, char *str) +{ + switch (su->sa.sa_family) + { + case AF_INET: + inet_ntop (AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN); + break; +#endif /* HAVE_IPV6 */ + } + return str; +} + +int +str2sockunion (char *str, union sockunion *su) +{ + int ret; + + memset (su, 0, sizeof (union sockunion)); + + ret = inet_pton (AF_INET, str, &su->sin.sin_addr); + if (ret > 0) /* Valid IPv4 address format. */ + { + su->sin.sin_family = AF_INET; +#ifdef HAVE_SIN_LEN + su->sin.sin_len = sizeof(struct sockaddr_in); +#endif /* HAVE_SIN_LEN */ + return 0; + } +#ifdef HAVE_IPV6 + ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr); + if (ret > 0) /* Valid IPv6 address format. */ + { + su->sin6.sin6_family = AF_INET6; +#ifdef SIN6_LEN + su->sin6.sin6_len = sizeof(struct sockaddr_in6); +#endif /* SIN6_LEN */ + return 0; + } +#endif /* HAVE_IPV6 */ + return -1; +} + +const char * +sockunion2str (union sockunion *su, char *buf, size_t len) +{ + if (su->sa.sa_family == AF_INET) + return inet_ntop (AF_INET, &su->sin.sin_addr, buf, len); +#ifdef HAVE_IPV6 + else if (su->sa.sa_family == AF_INET6) + return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len); +#endif /* HAVE_IPV6 */ + return NULL; +} + +union sockunion * +sockunion_str2su (char *str) +{ + int ret; + union sockunion *su; + + su = XMALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); + memset (su, 0, sizeof (union sockunion)); + + ret = inet_pton (AF_INET, str, &su->sin.sin_addr); + if (ret > 0) /* Valid IPv4 address format. */ + { + su->sin.sin_family = AF_INET; +#ifdef HAVE_SIN_LEN + su->sin.sin_len = sizeof(struct sockaddr_in); +#endif /* HAVE_SIN_LEN */ + return su; + } +#ifdef HAVE_IPV6 + ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr); + if (ret > 0) /* Valid IPv6 address format. */ + { + su->sin6.sin6_family = AF_INET6; +#ifdef SIN6_LEN + su->sin6.sin6_len = sizeof(struct sockaddr_in6); +#endif /* SIN6_LEN */ + return su; + } +#endif /* HAVE_IPV6 */ + + XFREE (MTYPE_SOCKUNION, su); + return NULL; +} + +char * +sockunion_su2str (union sockunion *su) +{ + char str[INET6_ADDRSTRLEN]; + + switch (su->sa.sa_family) + { + case AF_INET: + inet_ntop (AF_INET, &su->sin.sin_addr, str, sizeof (str)); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, sizeof (str)); + break; +#endif /* HAVE_IPV6 */ + } + return strdup (str); +} + +/* Return socket of sockunion. */ +int +sockunion_socket (union sockunion *su) +{ + int sock; + + sock = socket (su->sa.sa_family, SOCK_STREAM, 0); + if (sock < 0) + { + zlog (NULL, LOG_WARNING, "Can't make socket : %s", strerror (errno)); + return -1; + } + + return sock; +} + +/* Return accepted new socket file descriptor. */ +int +sockunion_accept (int sock, union sockunion *su) +{ + socklen_t len; + int client_sock; + + len = sizeof (union sockunion); + client_sock = accept (sock, (struct sockaddr *) su, &len); + + /* Convert IPv4 compatible IPv6 address to IPv4 address. */ +#ifdef HAVE_IPV6 + if (su->sa.sa_family == AF_INET6) + { + if (IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr)) + { + struct sockaddr_in sin; + + memset (&sin, 0, sizeof (struct sockaddr_in)); + sin.sin_family = AF_INET; + memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4); + memcpy (su, &sin, sizeof (struct sockaddr_in)); + } + } +#endif /* HAVE_IPV6 */ + + return client_sock; +} + +/* Return sizeof union sockunion. */ +int +sockunion_sizeof (union sockunion *su) +{ + int ret; + + ret = 0; + switch (su->sa.sa_family) + { + case AF_INET: + ret = sizeof (struct sockaddr_in); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + ret = sizeof (struct sockaddr_in6); + break; +#endif /* AF_INET6 */ + } + return ret; +} + +/* return sockunion structure : this function should be revised. */ +char * +sockunion_log (union sockunion *su) +{ + static char buf[SU_ADDRSTRLEN]; + + switch (su->sa.sa_family) + { + case AF_INET: + snprintf (buf, BUFSIZ, "%s", inet_ntoa (su->sin.sin_addr)); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + snprintf (buf, BUFSIZ, "%s", + inet_ntop (AF_INET6, &(su->sin6.sin6_addr), buf, BUFSIZ)); + break; +#endif /* HAVE_IPV6 */ + default: + snprintf (buf, BUFSIZ, "af_unknown %d ", su->sa.sa_family); + break; + } + return buf; +} + +/* sockunion_connect returns + -1 : error occured + 0 : connect success + 1 : connect is in progress */ +enum connect_result +sockunion_connect (int fd, union sockunion *peersu, unsigned short port, + unsigned int ifindex) +{ + int ret; + int val; + union sockunion su; + + memcpy (&su, peersu, sizeof (union sockunion)); + + switch (su.sa.sa_family) + { + case AF_INET: + su.sin.sin_port = port; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + su.sin6.sin6_port = port; +#ifdef KAME + if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex) + { +#ifdef HAVE_SIN6_SCOPE_ID + /* su.sin6.sin6_scope_id = ifindex; */ +#endif /* HAVE_SIN6_SCOPE_ID */ + SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex); + } +#endif /* KAME */ + break; +#endif /* HAVE_IPV6 */ + } + + /* Make socket non-block. */ + val = fcntl (fd, F_GETFL, 0); + fcntl (fd, F_SETFL, val|O_NONBLOCK); + + /* Call connect function. */ + ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su)); + + /* Immediate success */ + if (ret == 0) + { + fcntl (fd, F_SETFL, val); + return connect_success; + } + + /* If connect is in progress then return 1 else it's real error. */ + if (ret < 0) + { + if (errno != EINPROGRESS) + { + zlog_info ("can't connect to %s fd %d : %s", + sockunion_log (&su), fd, strerror (errno)); + return connect_error; + } + } + + fcntl (fd, F_SETFL, val); + + return connect_in_progress; +} + +/* Make socket from sockunion union. */ +int +sockunion_stream_socket (union sockunion *su) +{ + int sock; + + if (su->sa.sa_family == 0) + su->sa.sa_family = AF_INET_UNION; + + sock = socket (su->sa.sa_family, SOCK_STREAM, 0); + + if (sock < 0) + zlog (NULL, LOG_WARNING, "can't make socket sockunion_stream_socket"); + + return sock; +} + +/* Bind socket to specified address. */ +int +sockunion_bind (int sock, union sockunion *su, unsigned short port, + union sockunion *su_addr) +{ + int size = 0; + int ret; + + if (su->sa.sa_family == AF_INET) + { + size = sizeof (struct sockaddr_in); + su->sin.sin_port = htons (port); +#ifdef HAVE_SIN_LEN + su->sin.sin_len = size; +#endif /* HAVE_SIN_LEN */ + if (su_addr == NULL) + su->sin.sin_addr.s_addr = htonl (INADDR_ANY); + } +#ifdef HAVE_IPV6 + else if (su->sa.sa_family == AF_INET6) + { + size = sizeof (struct sockaddr_in6); + su->sin6.sin6_port = htons (port); +#ifdef SIN6_LEN + su->sin6.sin6_len = size; +#endif /* SIN6_LEN */ + if (su_addr == NULL) + { +#if defined(LINUX_IPV6) || defined(NRL) + memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr)); +#else + su->sin6.sin6_addr = in6addr_any; +#endif /* LINUX_IPV6 */ + } + } +#endif /* HAVE_IPV6 */ + + + ret = bind (sock, (struct sockaddr *)su, size); + if (ret < 0) + zlog (NULL, LOG_WARNING, "can't bind socket : %s", strerror (errno)); + + return ret; +} + +int +sockopt_reuseaddr (int sock) +{ + int ret; + int on = 1; + + ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, + (void *) &on, sizeof (on)); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEADDR to socket %d", sock); + return -1; + } + return 0; +} + +#ifdef SO_REUSEPORT +int +sockopt_reuseport (int sock) +{ + int ret; + int on = 1; + + ret = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT, + (void *) &on, sizeof (on)); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEADDR to socket %d", sock); + return -1; + } + return 0; +} +#else +int +sockopt_reuseport (int sock) +{ + return 0; +} +#endif /* 0 */ + +int +sockopt_ttl (int family, int sock, int ttl) +{ + int ret; + +#ifdef IP_TTL + if (family == AF_INET) + { + ret = setsockopt (sock, IPPROTO_IP, IP_TTL, + (void *) &ttl, sizeof (int)); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "can't set sockopt IP_TTL %d to socket %d", ttl, sock); + return -1; + } + return 0; + } +#endif /* IP_TTL */ +#ifdef HAVE_IPV6 + if (family == AF_INET6) + { + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + (void *) &ttl, sizeof (int)); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d", + ttl, sock); + return -1; + } + return 0; + } +#endif /* HAVE_IPV6 */ + return 0; +} + +/* If same family and same prefix return 1. */ +int +sockunion_same (union sockunion *su1, union sockunion *su2) +{ + int ret = 0; + + if (su1->sa.sa_family != su2->sa.sa_family) + return 0; + + switch (su1->sa.sa_family) + { + case AF_INET: + ret = memcmp (&su1->sin.sin_addr, &su2->sin.sin_addr, + sizeof (struct in_addr)); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr, + sizeof (struct in6_addr)); + break; +#endif /* HAVE_IPV6 */ + } + if (ret == 0) + return 1; + else + return 0; +} + +/* After TCP connection is established. Get local address and port. */ +union sockunion * +sockunion_getsockname (int fd) +{ + int ret; + int len; + union + { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef HAVE_IPV6 + struct sockaddr_in6 sin6; +#endif /* HAVE_IPV6 */ + char tmp_buffer[128]; + } name; + union sockunion *su; + + memset (&name, 0, sizeof name); + len = sizeof name; + + ret = getsockname (fd, (struct sockaddr *)&name, &len); + if (ret < 0) + { + zlog_warn ("Can't get local address and port by getsockname: %s", + strerror (errno)); + return NULL; + } + + if (name.sa.sa_family == AF_INET) + { + su = XCALLOC (MTYPE_TMP, sizeof (union sockunion)); + memcpy (su, &name, sizeof (struct sockaddr_in)); + return su; + } +#ifdef HAVE_IPV6 + if (name.sa.sa_family == AF_INET6) + { + su = XCALLOC (MTYPE_TMP, sizeof (union sockunion)); + memcpy (su, &name, sizeof (struct sockaddr_in6)); + + if (IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr)) + { + struct sockaddr_in sin; + + sin.sin_family = AF_INET; + memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4); + sin.sin_port = su->sin6.sin6_port; + memcpy (su, &sin, sizeof (struct sockaddr_in)); + } + return su; + } +#endif /* HAVE_IPV6 */ + return NULL; +} + +/* After TCP connection is established. Get remote address and port. */ +union sockunion * +sockunion_getpeername (int fd) +{ + int ret; + int len; + union + { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef HAVE_IPV6 + struct sockaddr_in6 sin6; +#endif /* HAVE_IPV6 */ + char tmp_buffer[128]; + } name; + union sockunion *su; + + memset (&name, 0, sizeof name); + len = sizeof name; + ret = getpeername (fd, (struct sockaddr *)&name, &len); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "Can't get remote address and port: %s", + strerror (errno)); + return NULL; + } + + if (name.sa.sa_family == AF_INET) + { + su = XCALLOC (MTYPE_TMP, sizeof (union sockunion)); + memcpy (su, &name, sizeof (struct sockaddr_in)); + return su; + } +#ifdef HAVE_IPV6 + if (name.sa.sa_family == AF_INET6) + { + su = XCALLOC (MTYPE_TMP, sizeof (union sockunion)); + memcpy (su, &name, sizeof (struct sockaddr_in6)); + + if (IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr)) + { + struct sockaddr_in sin; + + sin.sin_family = AF_INET; + memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4); + sin.sin_port = su->sin6.sin6_port; + memcpy (su, &sin, sizeof (struct sockaddr_in)); + } + return su; + } +#endif /* HAVE_IPV6 */ + return NULL; +} + +/* Print sockunion structure */ +void +sockunion_print (union sockunion *su) +{ + if (su == NULL) + return; + + switch (su->sa.sa_family) + { + case AF_INET: + printf ("%s\n", inet_ntoa (su->sin.sin_addr)); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + { + char buf [64]; + + printf ("%s\n", inet_ntop (AF_INET6, &(su->sin6.sin6_addr), + buf, sizeof (buf))); + } + break; +#endif /* HAVE_IPV6 */ + +#ifdef AF_LINK + case AF_LINK: + { + struct sockaddr_dl *sdl; + + sdl = (struct sockaddr_dl *)&(su->sa); + printf ("link#%d\n", sdl->sdl_index); + } + break; +#endif /* AF_LINK */ + default: + printf ("af_unknown %d\n", su->sa.sa_family); + break; + } +} + +#ifdef HAVE_IPV6 +int +in6addr_cmp (struct in6_addr *addr1, struct in6_addr *addr2) +{ + int i; + u_char *p1, *p2; + + p1 = (u_char *)addr1; + p2 = (u_char *)addr2; + + for (i = 0; i < sizeof (struct in6_addr); i++) + { + if (p1[i] > p2[i]) + return 1; + else if (p1[i] < p2[i]) + return -1; + } + return 0; +} +#endif /* HAVE_IPV6 */ + +int +sockunion_cmp (union sockunion *su1, union sockunion *su2) +{ + if (su1->sa.sa_family > su2->sa.sa_family) + return 1; + if (su1->sa.sa_family < su2->sa.sa_family) + return -1; + + if (su1->sa.sa_family == AF_INET) + { + if (ntohl (su1->sin.sin_addr.s_addr) == ntohl (su2->sin.sin_addr.s_addr)) + return 0; + if (ntohl (su1->sin.sin_addr.s_addr) > ntohl (su2->sin.sin_addr.s_addr)) + return 1; + else + return -1; + } +#ifdef HAVE_IPV6 + if (su1->sa.sa_family == AF_INET6) + return in6addr_cmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr); +#endif /* HAVE_IPV6 */ + return 0; +} + +/* Duplicate sockunion. */ +union sockunion * +sockunion_dup (union sockunion *su) +{ + union sockunion *dup = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); + memcpy (dup, su, sizeof (union sockunion)); + return dup; +} + +void +sockunion_free (union sockunion *su) +{ + XFREE (MTYPE_SOCKUNION, su); +} diff --git a/lib/sockunion.h b/lib/sockunion.h new file mode 100644 index 00000000..99bdf6a3 --- /dev/null +++ b/lib/sockunion.h @@ -0,0 +1,128 @@ +/* + * Socket union header. + * Copyright (c) 1997 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_SOCKUNION_H +#define _ZEBRA_SOCKUNION_H + +#if 0 +union sockunion { + struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; + } su_si; + struct sockaddr_in su_sin; + struct sockaddr_in6 su_sin6; +}; +#define su_len su_si.si_len +#define su_family su_si.si_family +#define su_port su_si.si_port +#endif /* 0 */ + +union sockunion +{ + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef HAVE_IPV6 + struct sockaddr_in6 sin6; +#endif /* HAVE_IPV6 */ +}; + +enum connect_result +{ + connect_error, + connect_success, + connect_in_progress +}; + +/* Default address family. */ +#ifdef HAVE_IPV6 +#define AF_INET_UNION AF_INET6 +#else +#define AF_INET_UNION AF_INET +#endif + +/* Sockunion address string length. Same as INET6_ADDRSTRLEN. */ +#define SU_ADDRSTRLEN 46 + +/* Macro to set link local index to the IPv6 address. For KAME IPv6 + stack. */ +#ifdef KAME +#define IN6_LINKLOCAL_IFINDEX(a) ((a).s6_addr[2] << 8 | (a).s6_addr[3]) +#define SET_IN6_LINKLOCAL_IFINDEX(a, i) \ + do { \ + (a).s6_addr[2] = ((i) >> 8) & 0xff; \ + (a).s6_addr[3] = (i) & 0xff; \ + } while (0) +#else +#define IN6_LINKLOCAL_IFINDEX(a) +#define SET_IN6_LINKLOCAL_IFINDEX(a, i) +#endif /* KAME */ + +/* shortcut macro to specify address field of struct sockaddr */ +#define sock2ip(X) (((struct sockaddr_in *)(X))->sin_addr.s_addr) +#ifdef HAVE_IPV6 +#define sock2ip6(X) (((struct sockaddr_in6 *)(X))->sin6_addr.s6_addr) +#endif /* HAVE_IPV6 */ + +#define sockunion_family(X) (X)->sa.sa_family + +/* Prototypes. */ +int str2sockunion (char *, union sockunion *); +const char *sockunion2str (union sockunion *, char *, size_t); +int sockunion_cmp (union sockunion *, union sockunion *); +int sockunion_same (union sockunion *, union sockunion *); + +char *sockunion_su2str (union sockunion *su); +union sockunion *sockunion_str2su (char *str); +struct in_addr sockunion_get_in_addr (union sockunion *su); +int sockunion_accept (int sock, union sockunion *); +int sockunion_stream_socket (union sockunion *); +int sockopt_reuseaddr (int); +int sockopt_reuseport (int); +int sockunion_bind (int sock, union sockunion *, unsigned short, union sockunion *); +int sockopt_ttl (int family, int sock, int ttl); +int sockunion_socket (union sockunion *su); +const char *inet_sutop (union sockunion *su, char *str); +enum connect_result +sockunion_connect (int fd, union sockunion *su, unsigned short port, unsigned int); +union sockunion *sockunion_getsockname (int); +union sockunion *sockunion_getpeername (int); +union sockunion *sockunion_dup (union sockunion *); +void sockunion_free (union sockunion *); + +#ifndef HAVE_INET_NTOP +const char * +inet_ntop (int family, const void *addrptr, char *strptr, size_t len); +#endif /* HAVE_INET_NTOP */ + +#ifndef HAVE_INET_PTON +int +inet_pton (int family, const char *strptr, void *addrptr); +#endif /* HAVE_INET_PTON */ + +#ifndef HAVE_INET_ATON +int +inet_aton (const char *cp, struct in_addr *inaddr); +#endif + +#endif /* _ZEBRA_SOCKUNION_H */ diff --git a/lib/str.c b/lib/str.c new file mode 100644 index 00000000..797e9b87 --- /dev/null +++ b/lib/str.c @@ -0,0 +1,62 @@ +/* + * zebra string function + * + * these functions are just very basic wrappers around exiting ones and + * do not offer the protection that might be expected against buffer + * overruns etc + */ + +#include <zebra.h> + +#include "str.h" + +#ifndef HAVE_SNPRINTF +/* + * snprint() is a real basic wrapper around the standard sprintf() + * without any bounds checking + */ +int +snprintf(char *str, size_t size, const char *format, ...) +{ + va_list args; + + va_start (args, format); + + return vsprintf (str, format, args); +} +#endif + +#ifndef HAVE_STRLCPY +/* + * strlcpy is a safer version of strncpy(), checking the total + * size of the buffer + */ +size_t +strlcpy(char *dst, const char *src, size_t size) +{ + strncpy(dst, src, size); + + return (strlen(dst)); +} +#endif + +#ifndef HAVE_STRLCAT +/* + * strlcat is a safer version of strncat(), checking the total + * size of the buffer + */ +size_t +strlcat(char *dst, const char *src, size_t size) +{ + /* strncpy(dst, src, size - strlen(dst)); */ + + /* I've just added below code only for workable under Linux. So + need rewrite -- Kunihiro. */ + if (strlen (dst) + strlen (src) >= size) + return -1; + + strcat (dst, src); + + return (strlen(dst)); +} +#endif diff --git a/lib/str.h b/lib/str.h new file mode 100644 index 00000000..4098896a --- /dev/null +++ b/lib/str.h @@ -0,0 +1,24 @@ +/* + * $Id: str.h,v 1.1 2002/12/13 20:15:29 paul Exp $ + */ + +#ifndef _ZEBRA_STR_H +#define _ZEBRA_STR_H + +#ifndef HAVE_SNPRINTF +int snprintf(char *, size_t, const char *, ...); +#endif + +#ifndef HAVE_VSNPRINTF +#define vsnprintf(buf, size, format, args) vsprintf(buf, format, args) +#endif + +#ifndef HAVE_STRLCPY +size_t strlcpy(char *, const char *, size_t); +#endif + +#ifndef HAVE_STRLCAT +size_t strlcat(char *, const char *, size_t); +#endif + +#endif diff --git a/lib/stream.c b/lib/stream.c new file mode 100644 index 00000000..2d4de760 --- /dev/null +++ b/lib/stream.c @@ -0,0 +1,479 @@ +/* + * Packet interface + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "stream.h" +#include "memory.h" +#include "network.h" +#include "prefix.h" + + +/*A macro to check pointers in order to not + go behind the allocated mem block + S -- stream reference + Z -- size of data to be written +*/ + +#define CHECK_SIZE(S, Z) \ + if (((S)->putp + (Z)) > (S)->size) \ + (Z) = (S)->size - (S)->putp; + +/* Stream is fixed length buffer for network output/input. */ + +/* Make stream buffer. */ +struct stream * +stream_new (size_t size) +{ + struct stream *s; + + s = XCALLOC (MTYPE_STREAM, sizeof (struct stream)); + + s->data = XCALLOC (MTYPE_STREAM_DATA, size); + s->size = size; + return s; +} + +/* Free it now. */ +void +stream_free (struct stream *s) +{ + XFREE (MTYPE_STREAM_DATA, s->data); + XFREE (MTYPE_STREAM, s); +} + +unsigned long +stream_get_getp (struct stream *s) +{ + return s->getp; +} + +unsigned long +stream_get_putp (struct stream *s) +{ + return s->putp; +} + +unsigned long +stream_get_endp (struct stream *s) +{ + return s->endp; +} + +unsigned long +stream_get_size (struct stream *s) +{ + return s->size; +} + +/* Stream structre' stream pointer related functions. */ +void +stream_set_getp (struct stream *s, unsigned long pos) +{ + s->getp = pos; +} + +void +stream_set_putp (struct stream *s, unsigned long pos) +{ + s->putp = pos; +} + +/* Forward pointer. */ +void +stream_forward (struct stream *s, int size) +{ + s->getp += size; +} + +/* Copy from stream to destination. */ +void +stream_get (void *dst, struct stream *s, size_t size) +{ + memcpy (dst, s->data + s->getp, size); + s->getp += size; +} + +/* Get next character from the stream. */ +u_char +stream_getc (struct stream *s) +{ + u_char c; + + c = s->data[s->getp]; + s->getp++; + return c; +} + +/* Get next character from the stream. */ +u_char +stream_getc_from (struct stream *s, unsigned long from) +{ + u_char c; + + c = s->data[from]; + return c; +} + +/* Get next word from the stream. */ +u_int16_t +stream_getw (struct stream *s) +{ + u_int16_t w; + + w = s->data[s->getp++] << 8; + w |= s->data[s->getp++]; + return w; +} + +/* Get next word from the stream. */ +u_int16_t +stream_getw_from (struct stream *s, unsigned long from) +{ + u_int16_t w; + + w = s->data[from++] << 8; + w |= s->data[from]; + return w; +} + +/* Get next long word from the stream. */ +u_int32_t +stream_getl (struct stream *s) +{ + u_int32_t l; + + l = s->data[s->getp++] << 24; + l |= s->data[s->getp++] << 16; + l |= s->data[s->getp++] << 8; + l |= s->data[s->getp++]; + return l; +} + +/* Get next long word from the stream. */ +u_int32_t +stream_get_ipv4 (struct stream *s) +{ + u_int32_t l; + + memcpy (&l, s->data + s->getp, 4); + s->getp += 4; + + return l; +} + +/* Copy to source to stream. */ +void +stream_put (struct stream *s, void *src, size_t size) +{ + + CHECK_SIZE(s, size); + + if (src) + memcpy (s->data + s->putp, src, size); + else + memset (s->data + s->putp, 0, size); + + s->putp += size; + if (s->putp > s->endp) + s->endp = s->putp; +} + +/* Put character to the stream. */ +int +stream_putc (struct stream *s, u_char c) +{ + if (s->putp >= s->size) return 0; + + s->data[s->putp] = c; + s->putp++; + if (s->putp > s->endp) + s->endp = s->putp; + return 1; +} + +/* Put word to the stream. */ +int +stream_putw (struct stream *s, u_int16_t w) +{ + if ((s->size - s->putp) < 2) return 0; + + s->data[s->putp++] = (u_char)(w >> 8); + s->data[s->putp++] = (u_char) w; + + if (s->putp > s->endp) + s->endp = s->putp; + return 2; +} + +/* Put long word to the stream. */ +int +stream_putl (struct stream *s, u_int32_t l) +{ + if ((s->size - s->putp) < 4) return 0; + + s->data[s->putp++] = (u_char)(l >> 24); + s->data[s->putp++] = (u_char)(l >> 16); + s->data[s->putp++] = (u_char)(l >> 8); + s->data[s->putp++] = (u_char)l; + + if (s->putp > s->endp) + s->endp = s->putp; + return 4; +} + +int +stream_putc_at (struct stream *s, unsigned long putp, u_char c) +{ + s->data[putp] = c; + return 1; +} + +int +stream_putw_at (struct stream *s, unsigned long putp, u_int16_t w) +{ + s->data[putp] = (u_char)(w >> 8); + s->data[putp + 1] = (u_char) w; + return 2; +} + +int +stream_putl_at (struct stream *s, unsigned long putp, u_int32_t l) +{ + s->data[putp] = (u_char)(l >> 24); + s->data[putp + 1] = (u_char)(l >> 16); + s->data[putp + 2] = (u_char)(l >> 8); + s->data[putp + 3] = (u_char)l; + return 4; +} + +/* Put long word to the stream. */ +int +stream_put_ipv4 (struct stream *s, u_int32_t l) +{ + if ((s->size - s->putp) < 4) + return 0; + + memcpy (s->data + s->putp, &l, 4); + s->putp += 4; + + if (s->putp > s->endp) + s->endp = s->putp; + return 4; +} + +/* Put long word to the stream. */ +int +stream_put_in_addr (struct stream *s, struct in_addr *addr) +{ + if ((s->size - s->putp) < 4) + return 0; + + memcpy (s->data + s->putp, addr, 4); + s->putp += 4; + + if (s->putp > s->endp) + s->endp = s->putp; + return 4; +} + +/* Put prefix by nlri type format. */ +int +stream_put_prefix (struct stream *s, struct prefix *p) +{ + u_char psize; + + psize = PSIZE (p->prefixlen); + + if ((s->size - s->putp) < psize) return 0; + + stream_putc (s, p->prefixlen); + memcpy (s->data + s->putp, &p->u.prefix, psize); + s->putp += psize; + + if (s->putp > s->endp) + s->endp = s->putp; + + return psize; +} + +/* Read size from fd. */ +int +stream_read (struct stream *s, int fd, size_t size) +{ + int nbytes; + + nbytes = readn (fd, s->data + s->putp, size); + + if (nbytes > 0) + { + s->putp += nbytes; + s->endp += nbytes; + } + return nbytes; +} + +/* Read size from fd. */ +int +stream_read_unblock (struct stream *s, int fd, size_t size) +{ + int nbytes; + int val; + + val = fcntl (fd, F_GETFL, 0); + fcntl (fd, F_SETFL, val|O_NONBLOCK); + nbytes = read (fd, s->data + s->putp, size); + fcntl (fd, F_SETFL, val); + + if (nbytes > 0) + { + s->putp += nbytes; + s->endp += nbytes; + } + return nbytes; +} + +/* Write data to buffer. */ +int +stream_write (struct stream *s, u_char *ptr, size_t size) +{ + + CHECK_SIZE(s, size); + + memcpy (s->data + s->putp, ptr, size); + s->putp += size; + if (s->putp > s->endp) + s->endp = s->putp; + return size; +} + +/* Return current read pointer. */ +u_char * +stream_pnt (struct stream *s) +{ + return s->data + s->getp; +} + +/* Check does this stream empty? */ +int +stream_empty (struct stream *s) +{ + if (s->putp == 0 && s->endp == 0 && s->getp == 0) + return 1; + else + return 0; +} + +/* Reset stream. */ +void +stream_reset (struct stream *s) +{ + s->putp = 0; + s->endp = 0; + s->getp = 0; +} + +/* Write stream contens to the file discriptor. */ +int +stream_flush (struct stream *s, int fd) +{ + int nbytes; + + nbytes = write (fd, s->data + s->getp, s->endp - s->getp); + + return nbytes; +} + +/* Stream first in first out queue. */ + +struct stream_fifo * +stream_fifo_new () +{ + struct stream_fifo *new; + + new = XCALLOC (MTYPE_STREAM_FIFO, sizeof (struct stream_fifo)); + return new; +} + +/* Add new stream to fifo. */ +void +stream_fifo_push (struct stream_fifo *fifo, struct stream *s) +{ + if (fifo->tail) + fifo->tail->next = s; + else + fifo->head = s; + + fifo->tail = s; + + fifo->count++; +} + +/* Delete first stream from fifo. */ +struct stream * +stream_fifo_pop (struct stream_fifo *fifo) +{ + struct stream *s; + + s = fifo->head; + + if (s) + { + fifo->head = s->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + } + + fifo->count--; + + return s; +} + +/* Return first fifo entry. */ +struct stream * +stream_fifo_head (struct stream_fifo *fifo) +{ + return fifo->head; +} + +void +stream_fifo_clean (struct stream_fifo *fifo) +{ + struct stream *s; + struct stream *next; + + for (s = fifo->head; s; s = next) + { + next = s->next; + stream_free (s); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +void +stream_fifo_free (struct stream_fifo *fifo) +{ + stream_fifo_clean (fifo); + XFREE (MTYPE_STREAM_FIFO, fifo); +} diff --git a/lib/stream.h b/lib/stream.h new file mode 100644 index 00000000..c6ef3c81 --- /dev/null +++ b/lib/stream.h @@ -0,0 +1,113 @@ +/* + * Packet interface + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_STREAM_H +#define _ZEBRA_STREAM_H + +/* Stream buffer. */ +struct stream +{ + struct stream *next; + + unsigned char *data; + + /* Put pointer. */ + unsigned long putp; + + /* Get pointer. */ + unsigned long getp; + + /* End of pointer. */ + unsigned long endp; + + /* Data size. */ + unsigned long size; +}; + +/* First in first out queue structure. */ +struct stream_fifo +{ + unsigned long count; + + struct stream *head; + struct stream *tail; +}; + +/* Utility macros. */ +#define STREAM_PNT(S) ((S)->data + (S)->getp) +#define STREAM_SIZE(S) ((S)->size) +#define STREAM_REMAIN(S) ((S)->size - (S)->putp) +#define STREAM_DATA(S) ((S)->data) + +/* Stream prototypes. */ +struct stream *stream_new (size_t); +void stream_free (struct stream *); + +unsigned long stream_get_getp (struct stream *); +unsigned long stream_get_putp (struct stream *); +unsigned long stream_get_endp (struct stream *); +unsigned long stream_get_size (struct stream *); +u_char *stream_get_data (struct stream *); + +void stream_set_getp (struct stream *, unsigned long); +void stream_set_putp (struct stream *, unsigned long); + +void stream_forward (struct stream *, int); + +void stream_put (struct stream *, void *, size_t); +int stream_putc (struct stream *, u_char); +int stream_putc_at (struct stream *, unsigned long, u_char); +int stream_putw (struct stream *, u_int16_t); +int stream_putw_at (struct stream *, unsigned long, u_int16_t); +int stream_putl (struct stream *, u_int32_t); +int stream_putl_at (struct stream *, unsigned long, u_int32_t); +int stream_put_ipv4 (struct stream *, u_int32_t); +int stream_put_in_addr (struct stream *, struct in_addr *); + +void stream_get (void *, struct stream *, size_t); +u_char stream_getc (struct stream *); +u_char stream_getc_from (struct stream *, unsigned long); +u_int16_t stream_getw (struct stream *); +u_int16_t stream_getw_from (struct stream *, unsigned long); +u_int32_t stream_getl (struct stream *); +u_int32_t stream_get_ipv4 (struct stream *); + +#undef stream_read +#undef stream_write +int stream_read (struct stream *, int, size_t); +int stream_read_unblock (struct stream *, int, size_t); +int stream_write (struct stream *, u_char *, size_t); + +u_char *stream_pnt (struct stream *); +void stream_reset (struct stream *); +int stream_flush (struct stream *, int); +int stream_empty (struct stream *); + +/* Stream fifo. */ +struct stream_fifo *stream_fifo_new (); +void stream_fifo_push (struct stream_fifo *fifo, struct stream *s); +struct stream *stream_fifo_pop (struct stream_fifo *fifo); +struct stream *stream_fifo_head (struct stream_fifo *fifo); +void stream_fifo_clean (struct stream_fifo *fifo); +void stream_fifo_free (struct stream_fifo *fifo); + +#endif /* _ZEBRA_STREAM_H */ diff --git a/lib/table.c b/lib/table.c new file mode 100644 index 00000000..00ba58d9 --- /dev/null +++ b/lib/table.c @@ -0,0 +1,503 @@ +/* + * Routing Table functions. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "prefix.h" +#include "table.h" +#include "memory.h" +#include "sockunion.h" + +void route_node_delete (struct route_node *); +void route_table_free (struct route_table *); + +struct route_table * +route_table_init (void) +{ + struct route_table *rt; + + rt = XCALLOC (MTYPE_ROUTE_TABLE, sizeof (struct route_table)); + return rt; +} + +void +route_table_finish (struct route_table *rt) +{ + route_table_free (rt); +} + +/* Allocate new route node. */ +struct route_node * +route_node_new () +{ + struct route_node *node; + node = XCALLOC (MTYPE_ROUTE_NODE, sizeof (struct route_node)); + return node; +} + +/* Allocate new route node with prefix set. */ +struct route_node * +route_node_set (struct route_table *table, struct prefix *prefix) +{ + struct route_node *node; + + node = route_node_new (); + + prefix_copy (&node->p, prefix); + node->table = table; + + return node; +} + +/* Free route node. */ +void +route_node_free (struct route_node *node) +{ + XFREE (MTYPE_ROUTE_NODE, node); +} + +/* Free route table. */ +void +route_table_free (struct route_table *rt) +{ + struct route_node *tmp_node; + struct route_node *node; + + if (rt == NULL) + return; + + node = rt->top; + + while (node) + { + if (node->l_left) + { + node = node->l_left; + continue; + } + + if (node->l_right) + { + node = node->l_right; + continue; + } + + tmp_node = node; + node = node->parent; + + if (node != NULL) + { + if (node->l_left == tmp_node) + node->l_left = NULL; + else + node->l_right = NULL; + + route_node_free (tmp_node); + } + else + { + route_node_free (tmp_node); + break; + } + } + + XFREE (MTYPE_ROUTE_TABLE, rt); + return; +} + +/* Utility mask array. */ +static u_char maskbit[] = +{ + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff +}; + +/* Common prefix route genaration. */ +static void +route_common (struct prefix *n, struct prefix *p, struct prefix *new) +{ + int i; + u_char diff; + u_char mask; + + u_char *np = (u_char *)&n->u.prefix; + u_char *pp = (u_char *)&p->u.prefix; + u_char *newp = (u_char *)&new->u.prefix; + + for (i = 0; i < p->prefixlen / 8; i++) + { + if (np[i] == pp[i]) + newp[i] = np[i]; + else + break; + } + + new->prefixlen = i * 8; + + if (new->prefixlen != p->prefixlen) + { + diff = np[i] ^ pp[i]; + mask = 0x80; + while (new->prefixlen < p->prefixlen && !(mask & diff)) + { + mask >>= 1; + new->prefixlen++; + } + newp[i] = np[i] & maskbit[new->prefixlen % 8]; + } +} + +/* Macro version of check_bit (). */ +#define CHECK_BIT(X,P) ((((u_char *)(X))[(P) / 8]) >> (7 - ((P) % 8)) & 1) + +/* Check bit of the prefix. */ +static int +check_bit (u_char *prefix, u_char prefixlen) +{ + int offset; + int shift; + u_char *p = (u_char *)prefix; + + assert (prefixlen <= 128); + + offset = prefixlen / 8; + shift = 7 - (prefixlen % 8); + + return (p[offset] >> shift & 1); +} + +/* Macro version of set_link (). */ +#define SET_LINK(X,Y) (X)->link[CHECK_BIT(&(Y)->prefix,(X)->prefixlen)] = (Y);\ + (Y)->parent = (X) + +static void +set_link (struct route_node *node, struct route_node *new) +{ + int bit; + + bit = check_bit (&new->p.u.prefix, node->p.prefixlen); + + assert (bit == 0 || bit == 1); + + node->link[bit] = new; + new->parent = node; +} + +/* Lock node. */ +struct route_node * +route_lock_node (struct route_node *node) +{ + node->lock++; + return node; +} + +/* Unlock node. */ +void +route_unlock_node (struct route_node *node) +{ + node->lock--; + + if (node->lock == 0) + route_node_delete (node); +} + +/* Dump routing table. */ +void +route_dump_node (struct route_table *t) +{ + struct route_node *node; + char buf[46]; + + for (node = route_top (t); node != NULL; node = route_next (node)) + { + printf ("[%d] %p %s/%d\n", + node->lock, + node->info, + inet_ntop (node->p.family, &node->p.u.prefix, buf, 46), + node->p.prefixlen); + } +} + +/* Find matched prefix. */ +struct route_node * +route_node_match (struct route_table *table, struct prefix *p) +{ + struct route_node *node; + struct route_node *matched; + + matched = NULL; + node = table->top; + + /* Walk down tree. If there is matched route then store it to + matched. */ + while (node && node->p.prefixlen <= p->prefixlen && + prefix_match (&node->p, p)) + { + if (node->info) + matched = node; + node = node->link[check_bit(&p->u.prefix, node->p.prefixlen)]; + } + + /* If matched route found, return it. */ + if (matched) + return route_lock_node (matched); + + return NULL; +} + +struct route_node * +route_node_match_ipv4 (struct route_table *table, struct in_addr *addr) +{ + struct prefix_ipv4 p; + + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = *addr; + + return route_node_match (table, (struct prefix *) &p); +} + +#ifdef HAVE_IPV6 +struct route_node * +route_node_match_ipv6 (struct route_table *table, struct in6_addr *addr) +{ + struct prefix_ipv6 p; + + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_PREFIXLEN; + p.prefix = *addr; + + return route_node_match (table, (struct prefix *) &p); +} +#endif /* HAVE_IPV6 */ + +/* Lookup same prefix node. Return NULL when we can't find route. */ +struct route_node * +route_node_lookup (struct route_table *table, struct prefix *p) +{ + struct route_node *node; + + node = table->top; + + while (node && node->p.prefixlen <= p->prefixlen && + prefix_match (&node->p, p)) + { + if (node->p.prefixlen == p->prefixlen && node->info) + return route_lock_node (node); + + node = node->link[check_bit(&p->u.prefix, node->p.prefixlen)]; + } + + return NULL; +} + +/* Add node to routing table. */ +struct route_node * +route_node_get (struct route_table *table, struct prefix *p) +{ + struct route_node *new; + struct route_node *node; + struct route_node *match; + + match = NULL; + node = table->top; + while (node && node->p.prefixlen <= p->prefixlen && + prefix_match (&node->p, p)) + { + if (node->p.prefixlen == p->prefixlen) + { + route_lock_node (node); + return node; + } + match = node; + node = node->link[check_bit(&p->u.prefix, node->p.prefixlen)]; + } + + if (node == NULL) + { + new = route_node_set (table, p); + if (match) + set_link (match, new); + else + table->top = new; + } + else + { + new = route_node_new (); + route_common (&node->p, p, &new->p); + new->p.family = p->family; + new->table = table; + set_link (new, node); + + if (match) + set_link (match, new); + else + table->top = new; + + if (new->p.prefixlen != p->prefixlen) + { + match = new; + new = route_node_set (table, p); + set_link (match, new); + } + } + route_lock_node (new); + + return new; +} + +/* Delete node from the routing table. */ +void +route_node_delete (struct route_node *node) +{ + struct route_node *child; + struct route_node *parent; + + assert (node->lock == 0); + assert (node->info == NULL); + + if (node->l_left && node->l_right) + return; + + if (node->l_left) + child = node->l_left; + else + child = node->l_right; + + parent = node->parent; + + if (child) + child->parent = parent; + + if (parent) + { + if (parent->l_left == node) + parent->l_left = child; + else + parent->l_right = child; + } + else + node->table->top = child; + + route_node_free (node); + + /* If parent node is stub then delete it also. */ + if (parent && parent->lock == 0) + route_node_delete (parent); +} + +/* Get fist node and lock it. This function is useful when one want + to lookup all the node exist in the routing table. */ +struct route_node * +route_top (struct route_table *table) +{ + /* If there is no node in the routing table return NULL. */ + if (table->top == NULL) + return NULL; + + /* Lock the top node and return it. */ + route_lock_node (table->top); + return table->top; +} + +/* Unlock current node and lock next node then return it. */ +struct route_node * +route_next (struct route_node *node) +{ + struct route_node *next; + struct route_node *start; + + /* Node may be deleted from route_unlock_node so we have to preserve + next node's pointer. */ + + if (node->l_left) + { + next = node->l_left; + route_lock_node (next); + route_unlock_node (node); + return next; + } + if (node->l_right) + { + next = node->l_right; + route_lock_node (next); + route_unlock_node (node); + return next; + } + + start = node; + while (node->parent) + { + if (node->parent->l_left == node && node->parent->l_right) + { + next = node->parent->l_right; + route_lock_node (next); + route_unlock_node (start); + return next; + } + node = node->parent; + } + route_unlock_node (start); + return NULL; +} + +/* Unlock current node and lock next node until limit. */ +struct route_node * +route_next_until (struct route_node *node, struct route_node *limit) +{ + struct route_node *next; + struct route_node *start; + + /* Node may be deleted from route_unlock_node so we have to preserve + next node's pointer. */ + + if (node->l_left) + { + next = node->l_left; + route_lock_node (next); + route_unlock_node (node); + return next; + } + if (node->l_right) + { + next = node->l_right; + route_lock_node (next); + route_unlock_node (node); + return next; + } + + start = node; + while (node->parent && node != limit) + { + if (node->parent->l_left == node && node->parent->l_right) + { + next = node->parent->l_right; + route_lock_node (next); + route_unlock_node (start); + return next; + } + node = node->parent; + } + route_unlock_node (start); + return NULL; +} diff --git a/lib/table.h b/lib/table.h new file mode 100644 index 00000000..6f090995 --- /dev/null +++ b/lib/table.h @@ -0,0 +1,74 @@ +/* + * Routing Table + * Copyright (C) 1998 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_TABLE_H +#define _ZEBRA_TABLE_H + +/* Routing table top structure. */ +struct route_table +{ + struct route_node *top; +}; + +/* Each routing entry. */ +struct route_node +{ + /* Actual prefix of this radix. */ + struct prefix p; + + /* Tree link. */ + struct route_table *table; + struct route_node *parent; + struct route_node *link[2]; +#define l_left link[0] +#define l_right link[1] + + /* Lock of this radix */ + unsigned int lock; + + /* Each node of route. */ + void *info; + + /* Aggregation. */ + void *aggregate; +}; + +/* Prototypes. */ +struct route_table *route_table_init (void); +void route_table_finish (struct route_table *); +void route_unlock_node (struct route_node *node); +void route_node_delete (struct route_node *node); +struct route_node *route_top (struct route_table *); +struct route_node *route_next (struct route_node *); +struct route_node *route_next_until (struct route_node *, struct route_node *); +struct route_node *route_node_get (struct route_table *, struct prefix *); +struct route_node *route_node_lookup (struct route_table *, struct prefix *); +struct route_node *route_lock_node (struct route_node *node); +struct route_node *route_node_match (struct route_table *, struct prefix *); +struct route_node *route_node_match_ipv4 (struct route_table *, + struct in_addr *); +#ifdef HAVE_IPV6 +struct route_node *route_node_match_ipv6 (struct route_table *, + struct in6_addr *); +#endif /* HAVE_IPV6 */ + +#endif /* _ZEBRA_TABLE_H */ diff --git a/lib/tcpfilter.c b/lib/tcpfilter.c new file mode 100644 index 00000000..4895ab5b --- /dev/null +++ b/lib/tcpfilter.c @@ -0,0 +1,69 @@ +/* Route filtering function for TCP and UDP. + * Copyright (C) 2000 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "command.h" +#include "prefix.h" + +#define FILTER_TYPE_IP 1 +#define FILTER_TYPE_TCP 2 +#define FILTER_TYPE_UDP 3 + +DEFUN (al_tcp_filter, + al_tcp_filter_cmd, + "access-list WORD (deny|permit) tcp (A.B.C.D/M|any) (A.B.C.D/M|any)", + "Add an access list entry\n" + "Access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Transmission Control Protocol\n" + "Source address prefix\n" + "Any source host\n" + "Destination address prefix\n" + "Any destination host\n") +{ + return CMD_SUCCESS; +} + +DEFUN (al_tcp_filter_eq, + al_tcp_filter_eq_cmd, + "access-list WORD (deny|permit) tcp (A.B.C.D/M|any) (A.B.C.D/M|any) eq <0-65535>", + "Add an access list entry\n" + "Access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Transmission Control Protocol\n" + "Source address prefix\n" + "Any source host\n" + "Destination address prefix\n" + "Any destination host\n" + "Port number\n") +{ + return CMD_SUCCESS; +} + +void +tcpfilter_init () +{ + install_element (CONFIG_NODE, &al_tcp_filter_cmd); + install_element (CONFIG_NODE, &al_tcp_filter_eq_cmd); +} diff --git a/lib/tcpfilter.h b/lib/tcpfilter.h new file mode 100644 index 00000000..c3d30081 --- /dev/null +++ b/lib/tcpfilter.h @@ -0,0 +1,21 @@ +/* Route filtering function for TCP and UDP. + * Copyright (C) 2000 Kunihiro Ishiguro + * + * 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. + */ + diff --git a/lib/thread.c b/lib/thread.c new file mode 100644 index 00000000..31bbcd77 --- /dev/null +++ b/lib/thread.c @@ -0,0 +1,668 @@ +/* Thread management routine + * Copyright (C) 1998, 2000 Kunihiro Ishiguro <kunihiro@zebra.org> + * + * 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. + */ + +/* #define DEBUG */ + +#include <zebra.h> + +#include "thread.h" +#include "memory.h" +#include "log.h" + +/* Struct timeval's tv_usec one second value. */ +#define TIMER_SECOND_MICRO 1000000L + +struct timeval +timeval_adjust (struct timeval a) +{ + while (a.tv_usec >= TIMER_SECOND_MICRO) + { + a.tv_usec -= TIMER_SECOND_MICRO; + a.tv_sec++; + } + + while (a.tv_usec < 0) + { + a.tv_usec += TIMER_SECOND_MICRO; + a.tv_sec--; + } + + if (a.tv_sec < 0) + { + a.tv_sec = 0; + a.tv_usec = 10; + } + + if (a.tv_sec > TIMER_SECOND_MICRO) + a.tv_sec = TIMER_SECOND_MICRO; + + return a; +} + +static struct timeval +timeval_subtract (struct timeval a, struct timeval b) +{ + struct timeval ret; + + ret.tv_usec = a.tv_usec - b.tv_usec; + ret.tv_sec = a.tv_sec - b.tv_sec; + + return timeval_adjust (ret); +} + +static int +timeval_cmp (struct timeval a, struct timeval b) +{ + return (a.tv_sec == b.tv_sec + ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec); +} + +static unsigned long +timeval_elapsed (struct timeval a, struct timeval b) +{ + return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) + + (a.tv_usec - b.tv_usec)); +} + +/* List allocation and head/tail print out. */ +static void +thread_list_debug (struct thread_list *list) +{ + printf ("count [%d] head [%p] tail [%p]\n", + list->count, list->head, list->tail); +} + +/* Debug print for thread_master. */ +void +thread_master_debug (struct thread_master *m) +{ + printf ("-----------\n"); + printf ("readlist : "); + thread_list_debug (&m->read); + printf ("writelist : "); + thread_list_debug (&m->write); + printf ("timerlist : "); + thread_list_debug (&m->timer); + printf ("eventlist : "); + thread_list_debug (&m->event); + printf ("unuselist : "); + thread_list_debug (&m->unuse); + printf ("total alloc: [%ld]\n", m->alloc); + printf ("-----------\n"); +} + +/* Allocate new thread master. */ +struct thread_master * +thread_master_create () +{ + return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER, + sizeof (struct thread_master)); +} + +/* Add a new thread to the list. */ +static void +thread_list_add (struct thread_list *list, struct thread *thread) +{ + thread->next = NULL; + thread->prev = list->tail; + if (list->tail) + list->tail->next = thread; + else + list->head = thread; + list->tail = thread; + list->count++; +} + +/* Add a new thread just before the point. */ +static void +thread_list_add_before (struct thread_list *list, + struct thread *point, + struct thread *thread) +{ + thread->next = point; + thread->prev = point->prev; + if (point->prev) + point->prev->next = thread; + else + list->head = thread; + point->prev = thread; + list->count++; +} + +/* Delete a thread from the list. */ +static struct thread * +thread_list_delete (struct thread_list *list, struct thread *thread) +{ + if (thread->next) + thread->next->prev = thread->prev; + else + list->tail = thread->prev; + if (thread->prev) + thread->prev->next = thread->next; + else + list->head = thread->next; + thread->next = thread->prev = NULL; + list->count--; + return thread; +} + +/* Move thread to unuse list. */ +static void +thread_add_unuse (struct thread_master *m, struct thread *thread) +{ + assert (m != NULL); + assert (thread->next == NULL); + assert (thread->prev == NULL); + assert (thread->type == THREAD_UNUSED); + thread_list_add (&m->unuse, thread); +} + +/* Free all unused thread. */ +static void +thread_list_free (struct thread_master *m, struct thread_list *list) +{ + struct thread *t; + struct thread *next; + + for (t = list->head; t; t = next) + { + next = t->next; + XFREE (MTYPE_THREAD, t); + list->count--; + m->alloc--; + } +} + +/* Stop thread scheduler. */ +void +thread_master_free (struct thread_master *m) +{ + thread_list_free (m, &m->read); + thread_list_free (m, &m->write); + thread_list_free (m, &m->timer); + thread_list_free (m, &m->event); + thread_list_free (m, &m->ready); + thread_list_free (m, &m->unuse); + + XFREE (MTYPE_THREAD_MASTER, m); +} + +/* Delete top of the list and return it. */ +static struct thread * +thread_trim_head (struct thread_list *list) +{ + if (list->head) + return thread_list_delete (list, list->head); + return NULL; +} + +/* Thread list is empty or not. */ +int +thread_empty (struct thread_list *list) +{ + return list->head ? 0 : 1; +} + +/* Return remain time in second. */ +unsigned long +thread_timer_remain_second (struct thread *thread) +{ + struct timeval timer_now; + + gettimeofday (&timer_now, NULL); + + if (thread->u.sands.tv_sec - timer_now.tv_sec > 0) + return thread->u.sands.tv_sec - timer_now.tv_sec; + else + return 0; +} + +/* Get new thread. */ +static struct thread * +thread_get (struct thread_master *m, u_char type, + int (*func) (struct thread *), void *arg) +{ + struct thread *thread; + + if (m->unuse.head) + thread = thread_trim_head (&m->unuse); + else + { + thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread)); + m->alloc++; + } + thread->type = type; + thread->master = m; + thread->func = func; + thread->arg = arg; + + return thread; +} + +/* Add new read thread. */ +struct thread * +thread_add_read (struct thread_master *m, + int (*func) (struct thread *), void *arg, int fd) +{ + struct thread *thread; + + assert (m != NULL); + + if (FD_ISSET (fd, &m->readfd)) + { + zlog (NULL, LOG_WARNING, "There is already read fd [%d]", fd); + return NULL; + } + + thread = thread_get (m, THREAD_READ, func, arg); + FD_SET (fd, &m->readfd); + thread->u.fd = fd; + thread_list_add (&m->read, thread); + + return thread; +} + +/* Add new write thread. */ +struct thread * +thread_add_write (struct thread_master *m, + int (*func) (struct thread *), void *arg, int fd) +{ + struct thread *thread; + + assert (m != NULL); + + if (FD_ISSET (fd, &m->writefd)) + { + zlog (NULL, LOG_WARNING, "There is already write fd [%d]", fd); + return NULL; + } + + thread = thread_get (m, THREAD_WRITE, func, arg); + FD_SET (fd, &m->writefd); + thread->u.fd = fd; + thread_list_add (&m->write, thread); + + return thread; +} + +/* Add timer event thread. */ +struct thread * +thread_add_timer (struct thread_master *m, + int (*func) (struct thread *), void *arg, long timer) +{ + struct timeval timer_now; + struct thread *thread; +#ifndef TIMER_NO_SORT + struct thread *tt; +#endif /* TIMER_NO_SORT */ + + assert (m != NULL); + + thread = thread_get (m, THREAD_TIMER, func, arg); + + /* Do we need jitter here? */ + gettimeofday (&timer_now, NULL); + timer_now.tv_sec += timer; + thread->u.sands = timer_now; + + /* Sort by timeval. */ +#ifdef TIMER_NO_SORT + thread_list_add (&m->timer, thread); +#else + for (tt = m->timer.head; tt; tt = tt->next) + if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0) + break; + + if (tt) + thread_list_add_before (&m->timer, tt, thread); + else + thread_list_add (&m->timer, thread); +#endif /* TIMER_NO_SORT */ + + return thread; +} + +/* Add simple event thread. */ +struct thread * +thread_add_event (struct thread_master *m, + int (*func) (struct thread *), void *arg, int val) +{ + struct thread *thread; + + assert (m != NULL); + + thread = thread_get (m, THREAD_EVENT, func, arg); + thread->u.val = val; + thread_list_add (&m->event, thread); + + return thread; +} + +/* Cancel thread from scheduler. */ +void +thread_cancel (struct thread *thread) +{ + switch (thread->type) + { + case THREAD_READ: + assert (FD_ISSET (thread->u.fd, &thread->master->readfd)); + FD_CLR (thread->u.fd, &thread->master->readfd); + thread_list_delete (&thread->master->read, thread); + break; + case THREAD_WRITE: + assert (FD_ISSET (thread->u.fd, &thread->master->writefd)); + FD_CLR (thread->u.fd, &thread->master->writefd); + thread_list_delete (&thread->master->write, thread); + break; + case THREAD_TIMER: + thread_list_delete (&thread->master->timer, thread); + break; + case THREAD_EVENT: + thread_list_delete (&thread->master->event, thread); + break; + case THREAD_READY: + thread_list_delete (&thread->master->ready, thread); + break; + default: + break; + } + thread->type = THREAD_UNUSED; + thread_add_unuse (thread->master, thread); +} + +/* Delete all events which has argument value arg. */ +void +thread_cancel_event (struct thread_master *m, void *arg) +{ + struct thread *thread; + + thread = m->event.head; + while (thread) + { + struct thread *t; + + t = thread; + thread = t->next; + + if (t->arg == arg) + { + thread_list_delete (&m->event, t); + t->type = THREAD_UNUSED; + thread_add_unuse (m, t); + } + } +} + +#ifdef TIMER_NO_SORT +struct timeval * +thread_timer_wait (struct thread_master *m, struct timeval *timer_val) +{ + struct timeval timer_now; + struct timeval timer_min; + struct timeval *timer_wait; + + gettimeofday (&timer_now, NULL); + + timer_wait = NULL; + for (thread = m->timer.head; thread; thread = thread->next) + { + if (! timer_wait) + timer_wait = &thread->u.sands; + else if (timeval_cmp (thread->u.sands, *timer_wait) < 0) + timer_wait = &thread->u.sands; + } + + if (m->timer.head) + { + timer_min = *timer_wait; + timer_min = timeval_subtract (timer_min, timer_now); + if (timer_min.tv_sec < 0) + { + timer_min.tv_sec = 0; + timer_min.tv_usec = 10; + } + timer_wait = &timer_min; + } + else + timer_wait = NULL; + + if (timer_wait) + { + *timer_val = timer_wait; + return timer_val; + } + return NULL; +} +#else /* ! TIMER_NO_SORT */ +struct timeval * +thread_timer_wait (struct thread_master *m, struct timeval *timer_val) +{ + struct timeval timer_now; + struct timeval timer_min; + + if (m->timer.head) + { + gettimeofday (&timer_now, NULL); + timer_min = m->timer.head->u.sands; + timer_min = timeval_subtract (timer_min, timer_now); + if (timer_min.tv_sec < 0) + { + timer_min.tv_sec = 0; + timer_min.tv_usec = 10; + } + *timer_val = timer_min; + return timer_val; + } + return NULL; +} +#endif /* TIMER_NO_SORT */ + +struct thread * +thread_run (struct thread_master *m, struct thread *thread, + struct thread *fetch) +{ + *fetch = *thread; + thread->type = THREAD_UNUSED; + thread_add_unuse (m, thread); + return fetch; +} + +int +thread_process_fd (struct thread_master *m, struct thread_list *list, + fd_set *fdset, fd_set *mfdset) +{ + struct thread *thread; + struct thread *next; + int ready = 0; + + for (thread = list->head; thread; thread = next) + { + next = thread->next; + + if (FD_ISSET (THREAD_FD (thread), fdset)) + { + assert (FD_ISSET (THREAD_FD (thread), mfdset)); + FD_CLR(THREAD_FD (thread), mfdset); + thread_list_delete (list, thread); + thread_list_add (&m->ready, thread); + thread->type = THREAD_READY; + ready++; + } + } + return ready; +} + +/* Fetch next ready thread. */ +struct thread * +thread_fetch (struct thread_master *m, struct thread *fetch) +{ + int num; + int ready; + struct thread *thread; + fd_set readfd; + fd_set writefd; + fd_set exceptfd; + struct timeval timer_now; + struct timeval timer_val; + struct timeval *timer_wait; + struct timeval timer_nowait; + + timer_nowait.tv_sec = 0; + timer_nowait.tv_usec = 0; + + while (1) + { + /* Normal event is the highest priority. */ + if ((thread = thread_trim_head (&m->event)) != NULL) + return thread_run (m, thread, fetch); + + /* Execute timer. */ + gettimeofday (&timer_now, NULL); + + for (thread = m->timer.head; thread; thread = thread->next) + if (timeval_cmp (timer_now, thread->u.sands) >= 0) + { + thread_list_delete (&m->timer, thread); + return thread_run (m, thread, fetch); + } + + /* If there are any ready threads, process top of them. */ + if ((thread = thread_trim_head (&m->ready)) != NULL) + return thread_run (m, thread, fetch); + + /* Structure copy. */ + readfd = m->readfd; + writefd = m->writefd; + exceptfd = m->exceptfd; + + /* Calculate select wait timer. */ + timer_wait = thread_timer_wait (m, &timer_val); + + num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait); + + if (num == 0) + continue; + + if (num < 0) + { + if (errno == EINTR) + continue; + + zlog_warn ("select() error: %s", strerror (errno)); + return NULL; + } + + /* Normal priority read thead. */ + ready = thread_process_fd (m, &m->read, &readfd, &m->readfd); + + /* Write thead. */ + ready = thread_process_fd (m, &m->write, &writefd, &m->writefd); + + if ((thread = thread_trim_head (&m->ready)) != NULL) + return thread_run (m, thread, fetch); + } +} + +static unsigned long +thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start) +{ + unsigned long thread_time; + +#ifdef HAVE_RUSAGE + /* This is 'user + sys' time. */ + thread_time = timeval_elapsed (now->ru_utime, start->ru_utime); + thread_time += timeval_elapsed (now->ru_stime, start->ru_stime); +#else + /* When rusage is not available, simple elapsed time is used. */ + thread_time = timeval_elapsed (*now, *start); +#endif /* HAVE_RUSAGE */ + + return thread_time; +} + +/* We should aim to yield after THREAD_YIELD_TIME_SLOT + milliseconds. */ +int +thread_should_yield (struct thread *thread) +{ + RUSAGE_T ru; + + GETRUSAGE (&ru); + + if (thread_consumed_time (&ru, &thread->ru) > THREAD_YIELD_TIME_SLOT) + return 1; + else + return 0; +} + +/* We check thread consumed time. If the system has getrusage, we'll + use that to get indepth stats on the performance of the thread. If + not - we'll use gettimeofday for some guestimation. */ +void +thread_call (struct thread *thread) +{ + unsigned long thread_time; + RUSAGE_T ru; + + GETRUSAGE (&thread->ru); + + (*thread->func) (thread); + + GETRUSAGE (&ru); + + thread_time = thread_consumed_time (&ru, &thread->ru); + +#ifdef THREAD_CONSUMED_TIME_CHECK + if (thread_time > 200000L) + { + /* + * We have a CPU Hog on our hands. + * Whinge about it now, so we're aware this is yet another task + * to fix. + */ + zlog_err ("CPU HOG task %lx ran for %ldms", + /* FIXME: report the name of the function somehow */ + (unsigned long) thread->func, + thread_time / 1000L); + } +#endif /* THREAD_CONSUMED_TIME_CHECK */ +} + +/* Execute thread */ +struct thread * +thread_execute (struct thread_master *m, + int (*func)(struct thread *), + void *arg, + int val) +{ + struct thread dummy; + + memset (&dummy, 0, sizeof (struct thread)); + + dummy.type = THREAD_EVENT; + dummy.master = NULL; + dummy.func = func; + dummy.arg = arg; + dummy.u.val = val; + thread_call (&dummy); + + return NULL; +} diff --git a/lib/thread.h b/lib/thread.h new file mode 100644 index 00000000..9de62cdd --- /dev/null +++ b/lib/thread.h @@ -0,0 +1,139 @@ +/* Thread management routine header. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_THREAD_H +#define _ZEBRA_THREAD_H + +#ifdef HAVE_RUSAGE +#define RUSAGE_T struct rusage +#define GETRUSAGE(X) getrusage (RUSAGE_SELF, X); +#else +#define RUSAGE_T struct timeval +#define GETRUSAGE(X) gettimeofday (X, NULL); +#endif /* HAVE_RUSAGE */ + +/* Linked list of thread. */ +struct thread_list +{ + struct thread *head; + struct thread *tail; + int count; +}; + +/* Master of the theads. */ +struct thread_master +{ + struct thread_list read; + struct thread_list write; + struct thread_list timer; + struct thread_list event; + struct thread_list ready; + struct thread_list unuse; + fd_set readfd; + fd_set writefd; + fd_set exceptfd; + unsigned long alloc; +}; + +/* Thread itself. */ +struct thread +{ + unsigned char type; /* thread type */ + struct thread *next; /* next pointer of the thread */ + struct thread *prev; /* previous pointer of the thread */ + struct thread_master *master; /* pointer to the struct thread_master. */ + int (*func) (struct thread *); /* event function */ + void *arg; /* event argument */ + union { + int val; /* second argument of the event. */ + int fd; /* file descriptor in case of read/write. */ + struct timeval sands; /* rest of time sands value. */ + } u; + RUSAGE_T ru; /* Indepth usage info. */ +}; + +/* Thread types. */ +#define THREAD_READ 0 +#define THREAD_WRITE 1 +#define THREAD_TIMER 2 +#define THREAD_EVENT 3 +#define THREAD_READY 4 +#define THREAD_UNUSED 5 + +/* Thread yield time. */ +#define THREAD_YIELD_TIME_SLOT 100 * 1000L /* 100ms */ + +/* Macros. */ +#define THREAD_ARG(X) ((X)->arg) +#define THREAD_FD(X) ((X)->u.fd) +#define THREAD_VAL(X) ((X)->u.val) + +#define THREAD_READ_ON(master,thread,func,arg,sock) \ + do { \ + if (! thread) \ + thread = thread_add_read (master, func, arg, sock); \ + } while (0) + +#define THREAD_WRITE_ON(master,thread,func,arg,sock) \ + do { \ + if (! thread) \ + thread = thread_add_write (master, func, arg, sock); \ + } while (0) + +#define THREAD_TIMER_ON(master,thread,func,arg,time) \ + do { \ + if (! thread) \ + thread = thread_add_timer (master, func, arg, time); \ + } while (0) + +#define THREAD_OFF(thread) \ + do { \ + if (thread) \ + { \ + thread_cancel (thread); \ + thread = NULL; \ + } \ + } while (0) + +#define THREAD_READ_OFF(thread) THREAD_OFF(thread) +#define THREAD_WRITE_OFF(thread) THREAD_OFF(thread) +#define THREAD_TIMER_OFF(thread) THREAD_OFF(thread) + +/* Prototypes. */ +struct thread_master *thread_master_create (); +struct thread *thread_add_read (struct thread_master *, + int (*)(struct thread *), void *, int); +struct thread *thread_add_write (struct thread_master *, + int (*)(struct thread *), void *, int); +struct thread *thread_add_timer (struct thread_master *, + int (*)(struct thread *), void *, long); +struct thread *thread_add_event (struct thread_master *, + int (*)(struct thread *), void *, int ); +void thread_cancel (struct thread *); +void thread_cancel_event (struct thread_master *, void *); + +struct thread *thread_fetch (struct thread_master *, struct thread *); +struct thread *thread_execute (struct thread_master *, + int (*)(struct thread *), void *, int); +void thread_call (struct thread *); +unsigned long thread_timer_remain_second (struct thread *); + +#endif /* _ZEBRA_THREAD_H */ diff --git a/lib/vector.c b/lib/vector.c new file mode 100644 index 00000000..31cdc77d --- /dev/null +++ b/lib/vector.c @@ -0,0 +1,189 @@ +/* Generic vector interface routine + * Copyright (C) 1997 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "vector.h" +#include "memory.h" + +/* Initialize vector : allocate memory and return vector. */ +vector +vector_init (unsigned int size) +{ + vector v = XCALLOC (MTYPE_VECTOR, sizeof (struct _vector)); + + /* allocate at least one slot */ + if (size == 0) + size = 1; + + v->alloced = size; + v->max = 0; + v->index = XCALLOC (MTYPE_VECTOR_INDEX, sizeof (void *) * size); + return v; +} + +void +vector_only_wrapper_free (vector v) +{ + XFREE (MTYPE_VECTOR, v); +} + +void +vector_only_index_free (void *index) +{ + XFREE (MTYPE_VECTOR_INDEX, index); +} + +void +vector_free (vector v) +{ + XFREE (MTYPE_VECTOR_INDEX, v->index); + XFREE (MTYPE_VECTOR, v); +} + +vector +vector_copy (vector v) +{ + unsigned int size; + vector new = XCALLOC (MTYPE_VECTOR, sizeof (struct _vector)); + + new->max = v->max; + new->alloced = v->alloced; + + size = sizeof (void *) * (v->alloced); + new->index = XCALLOC (MTYPE_VECTOR_INDEX, size); + memcpy (new->index, v->index, size); + + return new; +} + +/* Check assigned index, and if it runs short double index pointer */ +void +vector_ensure (vector v, unsigned int num) +{ + if (v->alloced > num) + return; + + v->index = XREALLOC (MTYPE_VECTOR_INDEX, + v->index, sizeof (void *) * (v->alloced * 2)); + memset (&v->index[v->alloced], 0, sizeof (void *) * v->alloced); + v->alloced *= 2; + + if (v->alloced <= num) + vector_ensure (v, num); +} + +/* This function only returns next empty slot index. It dose not mean + the slot's index memory is assigned, please call vector_ensure() + after calling this function. */ +int +vector_empty_slot (vector v) +{ + unsigned int i; + + if (v->max == 0) + return 0; + + for (i = 0; i < v->max; i++) + if (v->index[i] == 0) + return i; + + return i; +} + +/* Set value to the smallest empty slot. */ +int +vector_set (vector v, void *val) +{ + unsigned int i; + + i = vector_empty_slot (v); + vector_ensure (v, i); + + v->index[i] = val; + + if (v->max <= i) + v->max = i + 1; + + return i; +} + +/* Set value to specified index slot. */ +int +vector_set_index (vector v, unsigned int i, void *val) +{ + vector_ensure (v, i); + + v->index[i] = val; + + if (v->max <= i) + v->max = i + 1; + + return i; +} + +/* Look up vector. */ +void * +vector_lookup (vector v, unsigned int i) +{ + if (i >= v->max) + return NULL; + return v->index[i]; +} + +/* Lookup vector, ensure it. */ +void * +vector_lookup_ensure (vector v, unsigned int i) +{ + vector_ensure (v, i); + return v->index[i]; +} + +/* Unset value at specified index slot. */ +void +vector_unset (vector v, unsigned int i) +{ + if (i >= v->alloced) + return; + + v->index[i] = NULL; + + if (i + 1 == v->max) + { + v->max--; + while (i && v->index[--i] == NULL && v->max--) + ; /* Is this ugly ? */ + } +} + +/* Count the number of not emplty slot. */ +unsigned int +vector_count (vector v) +{ + unsigned int i; + unsigned count = 0; + + for (i = 0; i < v->max; i++) + if (v->index[i] != NULL) + count++; + + return count; +} diff --git a/lib/vector.h b/lib/vector.h new file mode 100644 index 00000000..7e00c397 --- /dev/null +++ b/lib/vector.h @@ -0,0 +1,58 @@ +/* + * Generic vector interface header. + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_VECTOR_H +#define _ZEBRA_VECTOR_H + +/* struct for vector */ +struct _vector +{ + unsigned int max; /* max number of used slot */ + unsigned int alloced; /* number of allocated slot */ + void **index; /* index to data */ +}; +typedef struct _vector *vector; + +#define VECTOR_MIN_SIZE 1 + +/* (Sometimes) usefull macros. This macro convert index expression to + array expression. */ +#define vector_slot(V,I) ((V)->index[(I)]) +#define vector_max(V) ((V)->max) + +/* Prototypes. */ +vector vector_init (unsigned int size); +void vector_ensure (vector v, unsigned int num); +int vector_empty_slot (vector v); +int vector_set (vector v, void *val); +int vector_set_index (vector v, unsigned int i, void *val); +void vector_unset (vector v, unsigned int i); +unsigned int vector_count (vector v); +void vector_only_wrapper_free (vector v); +void vector_only_index_free (void *index); +void vector_free (vector v); +vector vector_copy (vector v); + +void *vector_lookup (vector, unsigned int); +void *vector_lookup_ensure (vector, unsigned int); + +#endif /* _ZEBRA_VECTOR_H */ diff --git a/lib/version.h b/lib/version.h new file mode 100644 index 00000000..9a90bf4e --- /dev/null +++ b/lib/version.h @@ -0,0 +1,39 @@ +/* Zebra version + * Copyright (C) 1997, 1999 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_VERSION_H +#define _ZEBRA_VERSION_H + +#define ZEBRA_VERSION "0.93b" + +#define ZEBRA_BUG_ADDRESS "bug-zebra@gnu.org" + +extern char *host_name; + +void print_version(char *); +pid_t pid_output (char *); +pid_t pid_output_lock (char *); + +#ifndef HAVE_DAEMON +int daemon(int, int); +#endif + +#endif /* _ZEBRA_VERSION_H */ diff --git a/lib/vty.c b/lib/vty.c new file mode 100644 index 00000000..d31521cc --- /dev/null +++ b/lib/vty.c @@ -0,0 +1,2792 @@ +/* + * Virtual terminal [aka TeletYpe] interface routine. + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "linklist.h" +#include "buffer.h" +#include "version.h" +#include "command.h" +#include "sockunion.h" +#include "thread.h" +#include "memory.h" +#include "str.h" +#include "log.h" +#include "prefix.h" +#include "filter.h" + +/* Vty events */ +enum event +{ + VTY_SERV, + VTY_READ, + VTY_WRITE, + VTY_TIMEOUT_RESET, +#ifdef VTYSH + VTYSH_SERV, + VTYSH_READ +#endif /* VTYSH */ +}; + +static void vty_event (enum event, int, struct vty *); + +/* Extern host structure from command.c */ +extern struct host host; + +/* Vector which store each vty structure. */ +static vector vtyvec; + +/* Vty timeout value. */ +static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT; + +/* Vty access-class command */ +static char *vty_accesslist_name = NULL; + +/* Vty access-calss for IPv6. */ +static char *vty_ipv6_accesslist_name = NULL; + +/* VTY server thread. */ +vector Vvty_serv_thread; + +/* Current directory. */ +char *vty_cwd = NULL; + +/* Configure lock. */ +static int vty_config; + +/* Login password check. */ +static int no_password_check = 0; + +/* Integrated configuration file path */ +char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; + + +/* VTY standard output function. */ +int +vty_out (struct vty *vty, const char *format, ...) +{ + va_list args; + int len = 0; + int size = 1024; + char buf[1024]; + char *p = NULL; + + va_start (args, format); + + if (vty_shell (vty)) + vprintf (format, args); + else + { + /* Try to write to initial buffer. */ + len = vsnprintf (buf, sizeof buf, format, args); + + /* Initial buffer is not enough. */ + if (len < 0 || len >= size) + { + while (1) + { + if (len > -1) + size = len + 1; + else + size = size * 2; + + p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size); + if (! p) + return -1; + + len = vsnprintf (p, size, format, args); + + if (len > -1 && len < size) + break; + } + } + + /* When initial buffer is enough to store all output. */ + if (! p) + p = buf; + + /* Pointer p must point out buffer. */ + if (vty_shell_serv (vty)) + write (vty->fd, (u_char *) p, len); + else + buffer_write (vty->obuf, (u_char *) p, len); + + /* If p is not different with buf, it is allocated buffer. */ + if (p != buf) + XFREE (MTYPE_VTY_OUT_BUF, p); + } + + va_end (args); + + return len; +} + +int +vty_log_out (struct vty *vty, const char *proto_str, const char *format, + va_list va) +{ + int len; + char buf[1024]; + + snprintf (buf, sizeof buf, "%s: ", proto_str); + write (vty->fd, buf, strlen (proto_str) + 2); + + len = vsnprintf (buf, sizeof buf, format, va); + if (len < 0) + return -1; + write (vty->fd, (u_char *)buf, len); + + snprintf (buf, sizeof buf, "\r\n"); + write (vty->fd, buf, 2); + + return len; +} + +/* Output current time to the vty. */ +void +vty_time_print (struct vty *vty, int cr) +{ + time_t clock; + struct tm *tm; +#define TIME_BUF 25 + char buf [TIME_BUF]; + int ret; + + time (&clock); + tm = localtime (&clock); + + ret = strftime (buf, TIME_BUF, "%Y/%m/%d %H:%M:%S", tm); + if (ret == 0) + { + zlog (NULL, LOG_INFO, "strftime error"); + return; + } + if (cr) + vty_out (vty, "%s\n", buf); + else + vty_out (vty, "%s ", buf); + + return; +} + +/* Say hello to vty interface. */ +void +vty_hello (struct vty *vty) +{ + if (host.motd) + vty_out (vty, host.motd); +} + +/* Put out prompt and wait input from user. */ +static void +vty_prompt (struct vty *vty) +{ + struct utsname names; + const char*hostname; + + if (vty->type == VTY_TERM) + { + hostname = host.name; + if (!hostname) + { + uname (&names); + hostname = names.nodename; + } + vty_out (vty, cmd_prompt (vty->node), hostname); + } +} + +/* Send WILL TELOPT_ECHO to remote server. */ +void +vty_will_echo (struct vty *vty) +{ + char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Make suppress Go-Ahead telnet option. */ +static void +vty_will_suppress_go_ahead (struct vty *vty) +{ + char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Make don't use linemode over telnet. */ +static void +vty_dont_linemode (struct vty *vty) +{ + char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Use window size. */ +static void +vty_do_window_size (struct vty *vty) +{ + char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' }; + vty_out (vty, "%s", cmd); +} + +#if 0 /* Currently not used. */ +/* Make don't use lflow vty interface. */ +static void +vty_dont_lflow_ahead (struct vty *vty) +{ + char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' }; + vty_out (vty, "%s", cmd); +} +#endif /* 0 */ + +/* Allocate new vty struct. */ +struct vty * +vty_new () +{ + struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty)); + + new->obuf = (struct buffer *) buffer_new (100); + new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ); + new->max = VTY_BUFSIZ; + new->sb_buffer = NULL; + + return new; +} + +/* Authentication of vty */ +static void +vty_auth (struct vty *vty, char *buf) +{ + char *passwd = NULL; + enum node_type next_node = 0; + int fail; + char *crypt (const char *, const char *); + + switch (vty->node) + { + case AUTH_NODE: + if (host.encrypt) + passwd = host.password_encrypt; + else + passwd = host.password; + if (host.advanced) + next_node = host.enable ? VIEW_NODE : ENABLE_NODE; + else + next_node = VIEW_NODE; + break; + case AUTH_ENABLE_NODE: + if (host.encrypt) + passwd = host.enable_encrypt; + else + passwd = host.enable; + next_node = ENABLE_NODE; + break; + } + + if (passwd) + { + if (host.encrypt) + fail = strcmp (crypt(buf, passwd), passwd); + else + fail = strcmp (buf, passwd); + } + else + fail = 1; + + if (! fail) + { + vty->fail = 0; + vty->node = next_node; /* Success ! */ + } + else + { + vty->fail++; + if (vty->fail >= 3) + { + if (vty->node == AUTH_NODE) + { + vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); + vty->status = VTY_CLOSE; + } + else + { + /* AUTH_ENABLE_NODE */ + vty->fail = 0; + vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); + vty->node = VIEW_NODE; + } + } + } +} + +/* Command execution over the vty interface. */ +int +vty_command (struct vty *vty, char *buf) +{ + int ret; + vector vline; + + /* Split readline string up into the vector */ + vline = cmd_make_strvec (buf); + + if (vline == NULL) + return CMD_SUCCESS; + + ret = cmd_execute_command (vline, vty, NULL); + + if (ret != CMD_SUCCESS) + switch (ret) + { + case CMD_WARNING: + if (vty->type == VTY_FILE) + vty_out (vty, "Warning...%s", VTY_NEWLINE); + break; + case CMD_ERR_AMBIGUOUS: + vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + break; + case CMD_ERR_NO_MATCH: + vty_out (vty, "%% Unknown command.%s", VTY_NEWLINE); + break; + case CMD_ERR_INCOMPLETE: + vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); + break; + } + cmd_free_strvec (vline); + + return ret; +} + +char telnet_backward_char = 0x08; +char telnet_space_char = ' '; + +/* Basic function to write buffer to vty. */ +static void +vty_write (struct vty *vty, char *buf, size_t nbytes) +{ + if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) + return; + + /* Should we do buffering here ? And make vty_flush (vty) ? */ + buffer_write (vty->obuf, (u_char *)buf, nbytes); +} + +/* Ensure length of input buffer. Is buffer is short, double it. */ +static void +vty_ensure (struct vty *vty, int length) +{ + if (vty->max <= length) + { + vty->max *= 2; + vty->buf = XREALLOC (MTYPE_VTY, vty->buf, vty->max); + } +} + +/* Basic function to insert character into vty. */ +static void +vty_self_insert (struct vty *vty, char c) +{ + int i; + int length; + + vty_ensure (vty, vty->length + 1); + length = vty->length - vty->cp; + memmove (&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length); + vty->buf[vty->cp] = c; + + vty_write (vty, &vty->buf[vty->cp], length + 1); + for (i = 0; i < length; i++) + vty_write (vty, &telnet_backward_char, 1); + + vty->cp++; + vty->length++; +} + +/* Self insert character 'c' in overwrite mode. */ +static void +vty_self_insert_overwrite (struct vty *vty, char c) +{ + vty_ensure (vty, vty->length + 1); + vty->buf[vty->cp++] = c; + + if (vty->cp > vty->length) + vty->length++; + + if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) + return; + + vty_write (vty, &c, 1); +} + +/* Insert a word into vty interface with overwrite mode. */ +static void +vty_insert_word_overwrite (struct vty *vty, char *str) +{ + int len = strlen (str); + vty_write (vty, str, len); + strcpy (&vty->buf[vty->cp], str); + vty->cp += len; + vty->length = vty->cp; +} + +/* Forward character. */ +static void +vty_forward_char (struct vty *vty) +{ + if (vty->cp < vty->length) + { + vty_write (vty, &vty->buf[vty->cp], 1); + vty->cp++; + } +} + +/* Backward character. */ +static void +vty_backward_char (struct vty *vty) +{ + if (vty->cp > 0) + { + vty->cp--; + vty_write (vty, &telnet_backward_char, 1); + } +} + +/* Move to the beginning of the line. */ +static void +vty_beginning_of_line (struct vty *vty) +{ + while (vty->cp) + vty_backward_char (vty); +} + +/* Move to the end of the line. */ +static void +vty_end_of_line (struct vty *vty) +{ + while (vty->cp < vty->length) + vty_forward_char (vty); +} + +static void vty_kill_line_from_beginning (struct vty *); +static void vty_redraw_line (struct vty *); + +/* Print command line history. This function is called from + vty_next_line and vty_previous_line. */ +static void +vty_history_print (struct vty *vty) +{ + int length; + + vty_kill_line_from_beginning (vty); + + /* Get previous line from history buffer */ + length = strlen (vty->hist[vty->hp]); + memcpy (vty->buf, vty->hist[vty->hp], length); + vty->cp = vty->length = length; + + /* Redraw current line */ + vty_redraw_line (vty); +} + +/* Show next command line history. */ +void +vty_next_line (struct vty *vty) +{ + int try_index; + + if (vty->hp == vty->hindex) + return; + + /* Try is there history exist or not. */ + try_index = vty->hp; + if (try_index == (VTY_MAXHIST - 1)) + try_index = 0; + else + try_index++; + + /* If there is not history return. */ + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + vty_history_print (vty); +} + +/* Show previous command line history. */ +void +vty_previous_line (struct vty *vty) +{ + int try_index; + + try_index = vty->hp; + if (try_index == 0) + try_index = VTY_MAXHIST - 1; + else + try_index--; + + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + vty_history_print (vty); +} + +/* This function redraw all of the command line character. */ +static void +vty_redraw_line (struct vty *vty) +{ + vty_write (vty, vty->buf, vty->length); + vty->cp = vty->length; +} + +/* Forward word. */ +static void +vty_forward_word (struct vty *vty) +{ + while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') + vty_forward_char (vty); + + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') + vty_forward_char (vty); +} + +/* Backward word without skipping training space. */ +static void +vty_backward_pure_word (struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_backward_char (vty); +} + +/* Backward word. */ +static void +vty_backward_word (struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + vty_backward_char (vty); + + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_backward_char (vty); +} + +/* When '^D' is typed at the beginning of the line we move to the down + level. */ +static void +vty_down_level (struct vty *vty) +{ + vty_out (vty, "%s", VTY_NEWLINE); + config_exit (NULL, vty, 0, NULL); + vty_prompt (vty); + vty->cp = 0; +} + +/* When '^Z' is received from vty, move down to the enable mode. */ +void +vty_end_config (struct vty *vty) +{ + vty_out (vty, "%s", VTY_NEWLINE); + + switch (vty->node) + { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case INTERFACE_NODE: + case ZEBRA_NODE: + case RIP_NODE: + case RIPNG_NODE: + case BGP_NODE: + case BGP_VPNV4_NODE: + case BGP_IPV4_NODE: + case BGP_IPV4M_NODE: + case BGP_IPV6_NODE: + case RMAP_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case KEYCHAIN_NODE: + case KEYCHAIN_KEY_NODE: + case MASC_NODE: + case VTY_NODE: + vty_config_unlock (vty); + vty->node = ENABLE_NODE; + break; + default: + /* Unknown node, we have to ignore it. */ + break; + } + + vty_prompt (vty); + vty->cp = 0; +} + +/* Delete a charcter at the current point. */ +static void +vty_delete_char (struct vty *vty) +{ + int i; + int size; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return; + + if (vty->length == 0) + { + vty_down_level (vty); + return; + } + + if (vty->cp == vty->length) + return; /* completion need here? */ + + size = vty->length - vty->cp; + + vty->length--; + memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1); + vty->buf[vty->length] = '\0'; + + vty_write (vty, &vty->buf[vty->cp], size - 1); + vty_write (vty, &telnet_space_char, 1); + + for (i = 0; i < size; i++) + vty_write (vty, &telnet_backward_char, 1); +} + +/* Delete a character before the point. */ +static void +vty_delete_backward_char (struct vty *vty) +{ + if (vty->cp == 0) + return; + + vty_backward_char (vty); + vty_delete_char (vty); +} + +/* Kill rest of line from current point. */ +static void +vty_kill_line (struct vty *vty) +{ + int i; + int size; + + size = vty->length - vty->cp; + + if (size == 0) + return; + + for (i = 0; i < size; i++) + vty_write (vty, &telnet_space_char, 1); + for (i = 0; i < size; i++) + vty_write (vty, &telnet_backward_char, 1); + + memset (&vty->buf[vty->cp], 0, size); + vty->length = vty->cp; +} + +/* Kill line from the beginning. */ +static void +vty_kill_line_from_beginning (struct vty *vty) +{ + vty_beginning_of_line (vty); + vty_kill_line (vty); +} + +/* Delete a word before the point. */ +static void +vty_forward_kill_word (struct vty *vty) +{ + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') + vty_delete_char (vty); + while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') + vty_delete_char (vty); +} + +/* Delete a word before the point. */ +static void +vty_backward_kill_word (struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + vty_delete_backward_char (vty); + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_delete_backward_char (vty); +} + +/* Transpose chars before or at the point. */ +static void +vty_transpose_chars (struct vty *vty) +{ + char c1, c2; + + /* If length is short or point is near by the beginning of line then + return. */ + if (vty->length < 2 || vty->cp < 1) + return; + + /* In case of point is located at the end of the line. */ + if (vty->cp == vty->length) + { + c1 = vty->buf[vty->cp - 1]; + c2 = vty->buf[vty->cp - 2]; + + vty_backward_char (vty); + vty_backward_char (vty); + vty_self_insert_overwrite (vty, c1); + vty_self_insert_overwrite (vty, c2); + } + else + { + c1 = vty->buf[vty->cp]; + c2 = vty->buf[vty->cp - 1]; + + vty_backward_char (vty); + vty_self_insert_overwrite (vty, c1); + vty_self_insert_overwrite (vty, c2); + } +} + +/* Do completion at vty interface. */ +static void +vty_complete_command (struct vty *vty) +{ + int i; + int ret; + char **matched = NULL; + vector vline; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return; + + vline = cmd_make_strvec (vty->buf); + if (vline == NULL) + return; + + /* In case of 'help \t'. */ + if (isspace ((int) vty->buf[vty->length - 1])) + vector_set (vline, '\0'); + + matched = cmd_complete_command (vline, vty, &ret); + + cmd_free_strvec (vline); + + vty_out (vty, "%s", VTY_NEWLINE); + switch (ret) + { + case CMD_ERR_AMBIGUOUS: + vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + vty_prompt (vty); + vty_redraw_line (vty); + break; + case CMD_ERR_NO_MATCH: + /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */ + vty_prompt (vty); + vty_redraw_line (vty); + break; + case CMD_COMPLETE_FULL_MATCH: + vty_prompt (vty); + vty_redraw_line (vty); + vty_backward_pure_word (vty); + vty_insert_word_overwrite (vty, matched[0]); + vty_self_insert (vty, ' '); + XFREE (MTYPE_TMP, matched[0]); + break; + case CMD_COMPLETE_MATCH: + vty_prompt (vty); + vty_redraw_line (vty); + vty_backward_pure_word (vty); + vty_insert_word_overwrite (vty, matched[0]); + XFREE (MTYPE_TMP, matched[0]); + vector_only_index_free (matched); + return; + break; + case CMD_COMPLETE_LIST_MATCH: + for (i = 0; matched[i] != NULL; i++) + { + if (i != 0 && ((i % 6) == 0)) + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%-10s ", matched[i]); + XFREE (MTYPE_TMP, matched[i]); + } + vty_out (vty, "%s", VTY_NEWLINE); + + vty_prompt (vty); + vty_redraw_line (vty); + break; + case CMD_ERR_NOTHING_TODO: + vty_prompt (vty); + vty_redraw_line (vty); + break; + default: + break; + } + if (matched) + vector_only_index_free (matched); +} + +void +vty_describe_fold (struct vty *vty, int cmd_width, + int desc_width, struct desc *desc) +{ + char *buf, *cmd, *p; + int pos; + + cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; + + if (desc_width <= 0) + { + vty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE); + return; + } + + buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1); + + for (p = desc->str; strlen (p) > desc_width; p += pos + 1) + { + for (pos = desc_width; pos > 0; pos--) + if (*(p + pos) == ' ') + break; + + if (pos == 0) + break; + + strncpy (buf, p, pos); + buf[pos] = '\0'; + vty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE); + + cmd = ""; + } + + vty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE); + + XFREE (MTYPE_TMP, buf); +} + +/* Describe matched command function. */ +static void +vty_describe_command (struct vty *vty) +{ + int ret; + vector vline; + vector describe; + int i, width, desc_width; + struct desc *desc, *desc_cr = NULL; + + vline = cmd_make_strvec (vty->buf); + + /* In case of '> ?'. */ + if (vline == NULL) + { + vline = vector_init (1); + vector_set (vline, '\0'); + } + else + if (isspace ((int) vty->buf[vty->length - 1])) + vector_set (vline, '\0'); + + describe = cmd_describe_command (vline, vty, &ret); + + vty_out (vty, "%s", VTY_NEWLINE); + + /* Ambiguous error. */ + switch (ret) + { + case CMD_ERR_AMBIGUOUS: + cmd_free_strvec (vline); + vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + vty_prompt (vty); + vty_redraw_line (vty); + return; + break; + case CMD_ERR_NO_MATCH: + cmd_free_strvec (vline); + vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); + vty_prompt (vty); + vty_redraw_line (vty); + return; + break; + } + + /* Get width of command string. */ + width = 0; + for (i = 0; i < vector_max (describe); i++) + if ((desc = vector_slot (describe, i)) != NULL) + { + int len; + + if (desc->cmd[0] == '\0') + continue; + + len = strlen (desc->cmd); + if (desc->cmd[0] == '.') + len--; + + if (width < len) + width = len; + } + + /* Get width of description string. */ + desc_width = vty->width - (width + 6); + + /* Print out description. */ + for (i = 0; i < vector_max (describe); i++) + if ((desc = vector_slot (describe, i)) != NULL) + { + if (desc->cmd[0] == '\0') + continue; + + if (strcmp (desc->cmd, "<cr>") == 0) + { + desc_cr = desc; + continue; + } + + if (!desc->str) + vty_out (vty, " %-s%s", + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + VTY_NEWLINE); + else if (desc_width >= strlen (desc->str)) + vty_out (vty, " %-*s %s%s", width, + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str, VTY_NEWLINE); + else + vty_describe_fold (vty, width, desc_width, desc); + +#if 0 + vty_out (vty, " %-*s %s%s", width + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str ? desc->str : "", VTY_NEWLINE); +#endif /* 0 */ + } + + if ((desc = desc_cr)) + { + if (!desc->str) + vty_out (vty, " %-s%s", + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + VTY_NEWLINE); + else if (desc_width >= strlen (desc->str)) + vty_out (vty, " %-*s %s%s", width, + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str, VTY_NEWLINE); + else + vty_describe_fold (vty, width, desc_width, desc); + } + + cmd_free_strvec (vline); + vector_free (describe); + + vty_prompt (vty); + vty_redraw_line (vty); +} + +void +vty_clear_buf (struct vty *vty) +{ + memset (vty->buf, 0, vty->max); +} + +/* ^C stop current input and do not add command line to the history. */ +static void +vty_stop_input (struct vty *vty) +{ + vty->cp = vty->length = 0; + vty_clear_buf (vty); + vty_out (vty, "%s", VTY_NEWLINE); + + switch (vty->node) + { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case INTERFACE_NODE: + case ZEBRA_NODE: + case RIP_NODE: + case RIPNG_NODE: + case BGP_NODE: + case RMAP_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case KEYCHAIN_NODE: + case KEYCHAIN_KEY_NODE: + case MASC_NODE: + case VTY_NODE: + vty_config_unlock (vty); + vty->node = ENABLE_NODE; + break; + default: + /* Unknown node, we have to ignore it. */ + break; + } + vty_prompt (vty); + + /* Set history pointer to the latest one. */ + vty->hp = vty->hindex; +} + +/* Add current command line to the history buffer. */ +static void +vty_hist_add (struct vty *vty) +{ + int index; + + if (vty->length == 0) + return; + + index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1; + + /* Ignore the same string as previous one. */ + if (vty->hist[index]) + if (strcmp (vty->buf, vty->hist[index]) == 0) + { + vty->hp = vty->hindex; + return; + } + + /* Insert history entry. */ + if (vty->hist[vty->hindex]) + XFREE (MTYPE_VTY_HIST, vty->hist[vty->hindex]); + vty->hist[vty->hindex] = XSTRDUP (MTYPE_VTY_HIST, vty->buf); + + /* History index rotation. */ + vty->hindex++; + if (vty->hindex == VTY_MAXHIST) + vty->hindex = 0; + + vty->hp = vty->hindex; +} + +/* #define TELNET_OPTION_DEBUG */ + +/* Get telnet window size. */ +static int +vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) +{ +#ifdef TELNET_OPTION_DEBUG + int i; + + for (i = 0; i < nbytes; i++) + { + switch (buf[i]) + { + case IAC: + vty_out (vty, "IAC "); + break; + case WILL: + vty_out (vty, "WILL "); + break; + case WONT: + vty_out (vty, "WONT "); + break; + case DO: + vty_out (vty, "DO "); + break; + case DONT: + vty_out (vty, "DONT "); + break; + case SB: + vty_out (vty, "SB "); + break; + case SE: + vty_out (vty, "SE "); + break; + case TELOPT_ECHO: + vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); + break; + case TELOPT_SGA: + vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); + break; + case TELOPT_NAWS: + vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); + break; + default: + vty_out (vty, "%x ", buf[i]); + break; + } + } + vty_out (vty, "%s", VTY_NEWLINE); + +#endif /* TELNET_OPTION_DEBUG */ + + switch (buf[0]) + { + case SB: + buffer_reset(vty->sb_buffer); + vty->iac_sb_in_progress = 1; + return 0; + break; + case SE: + { + char *buffer = (char *)vty->sb_buffer->head->data; + int length = vty->sb_buffer->length; + + if (buffer == NULL) + return 0; + + if (!vty->iac_sb_in_progress) + return 0; + + if (buffer[0] == '\0') + { + vty->iac_sb_in_progress = 0; + return 0; + } + switch (buffer[0]) + { + case TELOPT_NAWS: + if (length < 5) + break; + vty->width = buffer[2]; + vty->height = vty->lines >= 0 ? vty->lines : buffer[4]; + break; + } + vty->iac_sb_in_progress = 0; + return 0; + break; + } + default: + break; + } + return 1; +} + +/* Execute current command line. */ +static int +vty_execute (struct vty *vty) +{ + int ret; + + ret = CMD_SUCCESS; + + switch (vty->node) + { + case AUTH_NODE: + case AUTH_ENABLE_NODE: + vty_auth (vty, vty->buf); + break; + default: + ret = vty_command (vty, vty->buf); + if (vty->type == VTY_TERM) + vty_hist_add (vty); + break; + } + + /* Clear command line buffer. */ + vty->cp = vty->length = 0; + vty_clear_buf (vty); + + if (vty->status != VTY_CLOSE + && vty->status != VTY_START + && vty->status != VTY_CONTINUE) + vty_prompt (vty); + + return ret; +} + +#define CONTROL(X) ((X) - '@') +#define VTY_NORMAL 0 +#define VTY_PRE_ESCAPE 1 +#define VTY_ESCAPE 2 + +/* Escape character command map. */ +static void +vty_escape_map (unsigned char c, struct vty *vty) +{ + switch (c) + { + case ('A'): + vty_previous_line (vty); + break; + case ('B'): + vty_next_line (vty); + break; + case ('C'): + vty_forward_char (vty); + break; + case ('D'): + vty_backward_char (vty); + break; + default: + break; + } + + /* Go back to normal mode. */ + vty->escape = VTY_NORMAL; +} + +/* Quit print out to the buffer. */ +static void +vty_buffer_reset (struct vty *vty) +{ + buffer_reset (vty->obuf); + vty_prompt (vty); + vty_redraw_line (vty); +} + +/* Read data via vty socket. */ +static int +vty_read (struct thread *thread) +{ + int i; + int ret; + int nbytes; + unsigned char buf[VTY_READ_BUFSIZ]; + + int vty_sock = THREAD_FD (thread); + struct vty *vty = THREAD_ARG (thread); + vty->t_read = NULL; + + /* Read raw data from socket */ + nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ); + if (nbytes <= 0) + vty->status = VTY_CLOSE; + + for (i = 0; i < nbytes; i++) + { + if (buf[i] == IAC) + { + if (!vty->iac) + { + vty->iac = 1; + continue; + } + else + { + vty->iac = 0; + } + } + + if (vty->iac_sb_in_progress && !vty->iac) + { + buffer_putc(vty->sb_buffer, buf[i]); + continue; + } + + if (vty->iac) + { + /* In case of telnet command */ + ret = vty_telnet_option (vty, buf + i, nbytes - i); + vty->iac = 0; + i += ret; + continue; + } + + if (vty->status == VTY_MORE) + { + switch (buf[i]) + { + case CONTROL('C'): + case 'q': + case 'Q': + if (vty->output_func) + (*vty->output_func) (vty, 1); + vty_buffer_reset (vty); + break; +#if 0 /* More line does not work for "show ip bgp". */ + case '\n': + case '\r': + vty->status = VTY_MORELINE; + break; +#endif + default: + if (vty->output_func) + (*vty->output_func) (vty, 0); + break; + } + continue; + } + + /* Escape character. */ + if (vty->escape == VTY_ESCAPE) + { + vty_escape_map (buf[i], vty); + continue; + } + + /* Pre-escape status. */ + if (vty->escape == VTY_PRE_ESCAPE) + { + switch (buf[i]) + { + case '[': + vty->escape = VTY_ESCAPE; + break; + case 'b': + vty_backward_word (vty); + vty->escape = VTY_NORMAL; + break; + case 'f': + vty_forward_word (vty); + vty->escape = VTY_NORMAL; + break; + case 'd': + vty_forward_kill_word (vty); + vty->escape = VTY_NORMAL; + break; + case CONTROL('H'): + case 0x7f: + vty_backward_kill_word (vty); + vty->escape = VTY_NORMAL; + break; + default: + vty->escape = VTY_NORMAL; + break; + } + continue; + } + + switch (buf[i]) + { + case CONTROL('A'): + vty_beginning_of_line (vty); + break; + case CONTROL('B'): + vty_backward_char (vty); + break; + case CONTROL('C'): + vty_stop_input (vty); + break; + case CONTROL('D'): + vty_delete_char (vty); + break; + case CONTROL('E'): + vty_end_of_line (vty); + break; + case CONTROL('F'): + vty_forward_char (vty); + break; + case CONTROL('H'): + case 0x7f: + vty_delete_backward_char (vty); + break; + case CONTROL('K'): + vty_kill_line (vty); + break; + case CONTROL('N'): + vty_next_line (vty); + break; + case CONTROL('P'): + vty_previous_line (vty); + break; + case CONTROL('T'): + vty_transpose_chars (vty); + break; + case CONTROL('U'): + vty_kill_line_from_beginning (vty); + break; + case CONTROL('W'): + vty_backward_kill_word (vty); + break; + case CONTROL('Z'): + vty_end_config (vty); + break; + case '\n': + case '\r': + vty_out (vty, "%s", VTY_NEWLINE); + vty_execute (vty); + break; + case '\t': + vty_complete_command (vty); + break; + case '?': + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + vty_self_insert (vty, buf[i]); + else + vty_describe_command (vty); + break; + case '\033': + if (i + 1 < nbytes && buf[i + 1] == '[') + { + vty->escape = VTY_ESCAPE; + i++; + } + else + vty->escape = VTY_PRE_ESCAPE; + break; + default: + if (buf[i] > 31 && buf[i] < 127) + vty_self_insert (vty, buf[i]); + break; + } + } + + /* Check status. */ + if (vty->status == VTY_CLOSE) + vty_close (vty); + else + { + vty_event (VTY_WRITE, vty_sock, vty); + vty_event (VTY_READ, vty_sock, vty); + } + return 0; +} + +/* Flush buffer to the vty. */ +static int +vty_flush (struct thread *thread) +{ + int erase; + int dont_more; + int vty_sock = THREAD_FD (thread); + struct vty *vty = THREAD_ARG (thread); + vty->t_write = NULL; + + /* Tempolary disable read thread. */ + if (vty->lines == 0) + if (vty->t_read) + { + thread_cancel (vty->t_read); + vty->t_read = NULL; + } + + /* Function execution continue. */ + if (vty->status == VTY_START || vty->status == VTY_CONTINUE) + { + if (vty->status == VTY_CONTINUE) + erase = 1; + else + erase = 0; + + if (vty->output_func == NULL) + dont_more = 1; + else + dont_more = 0; + + if (vty->lines == 0) + { + erase = 0; + dont_more = 1; + } + + buffer_flush_vty_all (vty->obuf, vty->fd, erase, dont_more); + + if (vty->status == VTY_CLOSE) + { + vty_close (vty); + return 0; + } + + if (vty->output_func == NULL) + { + vty->status = VTY_NORMAL; + vty_prompt (vty); + vty_event (VTY_WRITE, vty_sock, vty); + } + else + vty->status = VTY_MORE; + + if (vty->lines == 0) + { + if (vty->output_func == NULL) + vty_event (VTY_READ, vty_sock, vty); + else + { + if (vty->output_func) + (*vty->output_func) (vty, 0); + vty_event (VTY_WRITE, vty_sock, vty); + } + } + } + else + { + if (vty->status == VTY_MORE || vty->status == VTY_MORELINE) + erase = 1; + else + erase = 0; + + if (vty->lines == 0) + buffer_flush_window (vty->obuf, vty->fd, vty->width, 25, 0, 1); + else if (vty->status == VTY_MORELINE) + buffer_flush_window (vty->obuf, vty->fd, vty->width, 1, erase, 0); + else + buffer_flush_window (vty->obuf, vty->fd, vty->width, + vty->lines >= 0 ? vty->lines : vty->height, + erase, 0); + + if (buffer_empty (vty->obuf)) + { + if (vty->status == VTY_CLOSE) + vty_close (vty); + else + { + vty->status = VTY_NORMAL; + + if (vty->lines == 0) + vty_event (VTY_READ, vty_sock, vty); + } + } + else + { + vty->status = VTY_MORE; + + if (vty->lines == 0) + vty_event (VTY_WRITE, vty_sock, vty); + } + } + + return 0; +} + +/* Create new vty structure. */ +struct vty * +vty_create (int vty_sock, union sockunion *su) +{ + struct vty *vty; + + /* Allocate new vty structure and set up default values. */ + vty = vty_new (); + vty->fd = vty_sock; + vty->type = VTY_TERM; + vty->address = sockunion_su2str (su); + if (no_password_check) + { + if (host.advanced) + vty->node = ENABLE_NODE; + else + vty->node = VIEW_NODE; + } + else + vty->node = AUTH_NODE; + vty->fail = 0; + vty->cp = 0; + vty_clear_buf (vty); + vty->length = 0; + memset (vty->hist, 0, sizeof (vty->hist)); + vty->hp = 0; + vty->hindex = 0; + vector_set_index (vtyvec, vty_sock, vty); + vty->status = VTY_NORMAL; + vty->v_timeout = vty_timeout_val; + if (host.lines >= 0) + vty->lines = host.lines; + else + vty->lines = -1; + vty->iac = 0; + vty->iac_sb_in_progress = 0; + vty->sb_buffer = buffer_new (1024); + + if (! no_password_check) + { + /* Vty is not available if password isn't set. */ + if (host.password == NULL && host.password_encrypt == NULL) + { + vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); + vty->status = VTY_CLOSE; + vty_close (vty); + return NULL; + } + } + + /* Say hello to the world. */ + vty_hello (vty); + if (! no_password_check) + vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + /* Setting up terminal. */ + vty_will_echo (vty); + vty_will_suppress_go_ahead (vty); + + vty_dont_linemode (vty); + vty_do_window_size (vty); + /* vty_dont_lflow_ahead (vty); */ + + vty_prompt (vty); + + /* Add read/write thread. */ + vty_event (VTY_WRITE, vty_sock, vty); + vty_event (VTY_READ, vty_sock, vty); + + return vty; +} + +/* Accept connection from the network. */ +static int +vty_accept (struct thread *thread) +{ + int vty_sock; + struct vty *vty; + union sockunion su; + int ret; + unsigned int on; + int accept_sock; + struct prefix *p = NULL; + struct access_list *acl = NULL; + + accept_sock = THREAD_FD (thread); + + /* We continue hearing vty socket. */ + vty_event (VTY_SERV, accept_sock, NULL); + + memset (&su, 0, sizeof (union sockunion)); + + /* We can handle IPv4 or IPv6 socket. */ + vty_sock = sockunion_accept (accept_sock, &su); + if (vty_sock < 0) + { + zlog_warn ("can't accept vty socket : %s", strerror (errno)); + return -1; + } + + p = sockunion2hostprefix (&su); + + /* VTY's accesslist apply. */ + if (p->family == AF_INET && vty_accesslist_name) + { + if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) && + (access_list_apply (acl, p) == FILTER_DENY)) + { + char *buf; + zlog (NULL, LOG_INFO, "Vty connection refused from %s", + (buf = sockunion_su2str (&su))); + free (buf); + close (vty_sock); + + /* continue accepting connections */ + vty_event (VTY_SERV, accept_sock, NULL); + + prefix_free (p); + + return 0; + } + } + +#ifdef HAVE_IPV6 + /* VTY's ipv6 accesslist apply. */ + if (p->family == AF_INET6 && vty_ipv6_accesslist_name) + { + if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) && + (access_list_apply (acl, p) == FILTER_DENY)) + { + char *buf; + zlog (NULL, LOG_INFO, "Vty connection refused from %s", + (buf = sockunion_su2str (&su))); + free (buf); + close (vty_sock); + + /* continue accepting connections */ + vty_event (VTY_SERV, accept_sock, NULL); + + prefix_free (p); + + return 0; + } + } +#endif /* HAVE_IPV6 */ + + prefix_free (p); + + on = 1; + ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &on, sizeof (on)); + if (ret < 0) + zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", + strerror (errno)); + + vty = vty_create (vty_sock, &su); + + return 0; +} + +#if defined(HAVE_IPV6) && !defined(NRL) +void +vty_serv_sock_addrinfo (const char *hostname, unsigned short port) +{ + int ret; + struct addrinfo req; + struct addrinfo *ainfo; + struct addrinfo *ainfo_save; + int sock; + char port_str[BUFSIZ]; + + memset (&req, 0, sizeof (struct addrinfo)); + req.ai_flags = AI_PASSIVE; + req.ai_family = AF_UNSPEC; + req.ai_socktype = SOCK_STREAM; + sprintf (port_str, "%d", port); + port_str[sizeof (port_str) - 1] = '\0'; + + ret = getaddrinfo (hostname, port_str, &req, &ainfo); + + if (ret != 0) + { + fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret)); + exit (1); + } + + ainfo_save = ainfo; + + do + { + if (ainfo->ai_family != AF_INET +#ifdef HAVE_IPV6 + && ainfo->ai_family != AF_INET6 +#endif /* HAVE_IPV6 */ + ) + continue; + + sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol); + if (sock < 0) + continue; + + sockopt_reuseaddr (sock); + sockopt_reuseport (sock); + + ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen); + if (ret < 0) + { + close (sock); /* Avoid sd leak. */ + continue; + } + + ret = listen (sock, 3); + if (ret < 0) + { + close (sock); /* Avoid sd leak. */ + continue; + } + + vty_event (VTY_SERV, sock, NULL); + } + while ((ainfo = ainfo->ai_next) != NULL); + + freeaddrinfo (ainfo_save); +} +#endif /* HAVE_IPV6 && ! NRL */ + +/* Make vty server socket. */ +void +vty_serv_sock_family (unsigned short port, int family) +{ + int ret; + union sockunion su; + int accept_sock; + + memset (&su, 0, sizeof (union sockunion)); + su.sa.sa_family = family; + + /* Make new socket. */ + accept_sock = sockunion_stream_socket (&su); + if (accept_sock < 0) + return; + + /* This is server, so reuse address. */ + sockopt_reuseaddr (accept_sock); + sockopt_reuseport (accept_sock); + + /* Bind socket to universal address and given port. */ + ret = sockunion_bind (accept_sock, &su, port, NULL); + if (ret < 0) + { + close (accept_sock); /* Avoid sd leak. */ + return; + } + + /* Listen socket under queue 3. */ + ret = listen (accept_sock, 3); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "can't listen socket"); + close (accept_sock); /* Avoid sd leak. */ + return; + } + + /* Add vty server event. */ + vty_event (VTY_SERV, accept_sock, NULL); +} + +#ifdef VTYSH +/* For sockaddr_un. */ +#include <sys/un.h> + +/* VTY shell UNIX domain socket. */ +void +vty_serv_un (char *path) +{ + int ret; + int sock, len; + struct sockaddr_un serv; + mode_t old_mask; + + /* First of all, unlink existing socket */ + unlink (path); + + /* Set umask */ + old_mask = umask (0077); + + /* Make UNIX domain socket. */ + sock = socket (AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + { + perror ("sock"); + return; + } + + /* Make server socket. */ + memset (&serv, 0, sizeof (struct sockaddr_un)); + serv.sun_family = AF_UNIX; + strncpy (serv.sun_path, path, strlen (path)); +#ifdef HAVE_SUN_LEN + len = serv.sun_len = SUN_LEN(&serv); +#else + len = sizeof (serv.sun_family) + strlen (serv.sun_path); +#endif /* HAVE_SUN_LEN */ + + ret = bind (sock, (struct sockaddr *) &serv, len); + if (ret < 0) + { + perror ("bind"); + close (sock); /* Avoid sd leak. */ + return; + } + + ret = listen (sock, 5); + if (ret < 0) + { + perror ("listen"); + close (sock); /* Avoid sd leak. */ + return; + } + + umask (old_mask); + + vty_event (VTYSH_SERV, sock, NULL); +} + +/* #define VTYSH_DEBUG 1 */ + +static int +vtysh_accept (struct thread *thread) +{ + int accept_sock; + int sock; + int client_len; + struct sockaddr_un client; + struct vty *vty; + + accept_sock = THREAD_FD (thread); + + vty_event (VTYSH_SERV, accept_sock, NULL); + + memset (&client, 0, sizeof (struct sockaddr_un)); + client_len = sizeof (struct sockaddr_un); + + sock = accept (accept_sock, (struct sockaddr *) &client, &client_len); + + if (sock < 0) + { + zlog_warn ("can't accept vty socket : %s", strerror (errno)); + return -1; + } + +#ifdef VTYSH_DEBUG + printf ("VTY shell accept\n"); +#endif /* VTYSH_DEBUG */ + + vty = vty_new (); + vty->fd = sock; + vty->type = VTY_SHELL_SERV; + vty->node = VIEW_NODE; + + vty_event (VTYSH_READ, sock, vty); + + return 0; +} + +static int +vtysh_read (struct thread *thread) +{ + int ret; + int sock; + int nbytes; + struct vty *vty; + unsigned char buf[VTY_READ_BUFSIZ]; + u_char header[4] = {0, 0, 0, 0}; + + sock = THREAD_FD (thread); + vty = THREAD_ARG (thread); + vty->t_read = NULL; + + nbytes = read (sock, buf, VTY_READ_BUFSIZ); + if (nbytes <= 0) + { + vty_close (vty); +#ifdef VTYSH_DEBUG + printf ("close vtysh\n"); +#endif /* VTYSH_DEBUG */ + return 0; + } + +#ifdef VTYSH_DEBUG + printf ("line: %s\n", buf); +#endif /* VTYSH_DEBUG */ + + vty_ensure (vty, nbytes); + memcpy (vty->buf, buf, nbytes); + + /* Pass this line to parser. */ + ret = vty_execute (vty); + + vty_clear_buf (vty); + + /* Return result. */ +#ifdef VTYSH_DEBUG + printf ("result: %d\n", ret); + printf ("vtysh node: %d\n", vty->node); +#endif /* VTYSH_DEBUG */ + + header[3] = ret; + write (vty->fd, header, 4); + + vty_event (VTYSH_READ, sock, vty); + + return 0; +} +#endif /* VTYSH */ + +/* Determine address family to bind. */ +void +vty_serv_sock (const char *hostname, unsigned short port, char *path) +{ + /* If port is set to 0, do not listen on TCP/IP at all! */ + if (port) + { + +#ifdef HAVE_IPV6 +#ifdef NRL + vty_serv_sock_family (port, AF_INET); + vty_serv_sock_family (port, AF_INET6); +#else /* ! NRL */ + vty_serv_sock_addrinfo (hostname, port); +#endif /* NRL*/ +#else /* ! HAVE_IPV6 */ + vty_serv_sock_family (port, AF_INET); +#endif /* HAVE_IPV6 */ + } + +#ifdef VTYSH + vty_serv_un (path); +#endif /* VTYSH */ +} + +/* Close vty interface. */ +void +vty_close (struct vty *vty) +{ + int i; + + /* Cancel threads.*/ + if (vty->t_read) + thread_cancel (vty->t_read); + if (vty->t_write) + thread_cancel (vty->t_write); + if (vty->t_timeout) + thread_cancel (vty->t_timeout); + if (vty->t_output) + thread_cancel (vty->t_output); + + /* Flush buffer. */ + if (! buffer_empty (vty->obuf)) + buffer_flush_all (vty->obuf, vty->fd); + + /* Free input buffer. */ + buffer_free (vty->obuf); + + /* Free SB buffer. */ + if (vty->sb_buffer) + buffer_free (vty->sb_buffer); + + /* Free command history. */ + for (i = 0; i < VTY_MAXHIST; i++) + if (vty->hist[i]) + XFREE (MTYPE_VTY_HIST, vty->hist[i]); + + /* Unset vector. */ + vector_unset (vtyvec, vty->fd); + + /* Close socket. */ + if (vty->fd > 0) + close (vty->fd); + + if (vty->address) + XFREE (0, vty->address); + if (vty->buf) + XFREE (MTYPE_VTY, vty->buf); + + /* Check configure. */ + vty_config_unlock (vty); + + /* OK free vty. */ + XFREE (MTYPE_VTY, vty); +} + +/* When time out occur output message then close connection. */ +static int +vty_timeout (struct thread *thread) +{ + struct vty *vty; + + vty = THREAD_ARG (thread); + vty->t_timeout = NULL; + vty->v_timeout = 0; + + /* Clear buffer*/ + buffer_reset (vty->obuf); + vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE); + + /* Close connection. */ + vty->status = VTY_CLOSE; + vty_close (vty); + + return 0; +} + +/* Read up configuration file from file_name. */ +static void +vty_read_file (FILE *confp) +{ + int ret; + struct vty *vty; + + vty = vty_new (); + vty->fd = 0; /* stdout */ + vty->type = VTY_TERM; + vty->node = CONFIG_NODE; + + /* Execute configuration file */ + ret = config_from_file (vty, confp); + + if (ret != CMD_SUCCESS) + { + switch (ret) + { + case CMD_ERR_AMBIGUOUS: + fprintf (stderr, "Ambiguous command.\n"); + break; + case CMD_ERR_NO_MATCH: + fprintf (stderr, "There is no such command.\n"); + break; + } + fprintf (stderr, "Error occured during reading below line.\n%s\n", + vty->buf); + vty_close (vty); + exit (1); + } + + vty_close (vty); +} + +FILE * +vty_use_backup_config (char *fullpath) +{ + char *fullpath_sav, *fullpath_tmp; + FILE *ret = NULL; + struct stat buf; + int tmp, sav; + int c; + char buffer[512]; + + fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1); + strcpy (fullpath_sav, fullpath); + strcat (fullpath_sav, CONF_BACKUP_EXT); + if (stat (fullpath_sav, &buf) == -1) + { + free (fullpath_sav); + return NULL; + } + + fullpath_tmp = malloc (strlen (fullpath) + 8); + sprintf (fullpath_tmp, "%s.XXXXXX", fullpath); + + /* Open file to configuration write. */ + tmp = mkstemp (fullpath_tmp); + if (tmp < 0) + { + free (fullpath_sav); + free (fullpath_tmp); + return NULL; + } + + sav = open (fullpath_sav, O_RDONLY); + if (sav < 0) + { + free (fullpath_sav); + free (fullpath_tmp); + unlink (fullpath_tmp); + return NULL; + } + + while((c = read (sav, buffer, 512)) > 0) + write (tmp, buffer, c); + + close (sav); + close (tmp); + + if (link (fullpath_tmp, fullpath) == 0) + ret = fopen (fullpath, "r"); + + unlink (fullpath_tmp); + + free (fullpath_sav); + free (fullpath_tmp); + return fopen (fullpath, "r"); +} + +/* Read up configuration file from file_name. */ +void +vty_read_config (char *config_file, + char *config_current_dir, + char *config_default_dir) +{ + char *cwd; + FILE *confp = NULL; + char *fullpath; + + /* If -f flag specified. */ + if (config_file != NULL) + { + if (! IS_DIRECTORY_SEP (config_file[0])) + { + cwd = getcwd (NULL, MAXPATHLEN); + fullpath = XMALLOC (MTYPE_TMP, + strlen (cwd) + strlen (config_file) + 2); + sprintf (fullpath, "%s/%s", cwd, config_file); + } + else + fullpath = config_file; + + confp = fopen (fullpath, "r"); + + if (confp == NULL) + { + confp = vty_use_backup_config (fullpath); + if (confp) + fprintf (stderr, "WARNING: using backup configuration file!\n"); + else + { + fprintf (stderr, "can't open configuration file [%s]\n", + config_file); + exit(1); + } + } + } + else + { + /* Relative path configuration file open. */ + if (config_current_dir) + { + confp = fopen (config_current_dir, "r"); + if (confp == NULL) + { + confp = vty_use_backup_config (config_current_dir); + if (confp) + fprintf (stderr, "WARNING: using backup configuration file!\n"); + } + } + + /* If there is no relative path exists, open system default file. */ + if (confp == NULL) + { +#ifdef VTYSH + int ret; + struct stat conf_stat; + + /* !!!!PLEASE LEAVE!!!! + This is NEEDED for use with vtysh -b, or else you can get + a real configuration food fight with a lot garbage in the + merged configuration file it creates coming from the per + daemon configuration files. This also allows the daemons + to start if there default configuration file is not + present or ignore them, as needed when using vtysh -b to + configure the daemons at boot - MAG */ + + /* Stat for vtysh Zebra.conf, if found startup and wait for + boot configuration */ + + if ( strstr(config_default_dir, "vtysh") == NULL) + { + ret = stat (integrate_default, &conf_stat); + if (ret >= 0) + { + return; + } + } +#endif /* VTYSH */ + + confp = fopen (config_default_dir, "r"); + if (confp == NULL) + { + confp = vty_use_backup_config (config_default_dir); + if (confp) + { + fprintf (stderr, "WARNING: using backup configuration file!\n"); + fullpath = config_default_dir; + } + else + { + fprintf (stderr, "can't open configuration file [%s]\n", + config_default_dir); + exit (1); + } + } + else + fullpath = config_default_dir; + } + else + { + /* Rleative path configuration file. */ + cwd = getcwd (NULL, MAXPATHLEN); + fullpath = XMALLOC (MTYPE_TMP, + strlen (cwd) + strlen (config_current_dir) + 2); + sprintf (fullpath, "%s/%s", cwd, config_current_dir); + } + } + vty_read_file (confp); + + fclose (confp); + + host_config_set (fullpath); +} + +/* Small utility function which output log to the VTY. */ +void +vty_log (const char *proto_str, const char *format, va_list va) +{ + int i; + struct vty *vty; + + for (i = 0; i < vector_max (vtyvec); i++) + if ((vty = vector_slot (vtyvec, i)) != NULL) + if (vty->monitor) + vty_log_out (vty, proto_str, format, va); +} + +int +vty_config_lock (struct vty *vty) +{ + if (vty_config == 0) + { + vty->config = 1; + vty_config = 1; + } + return vty->config; +} + +int +vty_config_unlock (struct vty *vty) +{ + if (vty_config == 1 && vty->config == 1) + { + vty->config = 0; + vty_config = 0; + } + return vty->config; +} + +/* Master of the threads. */ +extern struct thread_master *master; +/* struct thread_master *master; */ + +static void +vty_event (enum event event, int sock, struct vty *vty) +{ + struct thread *vty_serv_thread; + + switch (event) + { + case VTY_SERV: + vty_serv_thread = thread_add_read (master, vty_accept, vty, sock); + vector_set_index (Vvty_serv_thread, sock, vty_serv_thread); + break; +#ifdef VTYSH + case VTYSH_SERV: + thread_add_read (master, vtysh_accept, vty, sock); + break; + case VTYSH_READ: + thread_add_read (master, vtysh_read, vty, sock); + break; +#endif /* VTYSH */ + case VTY_READ: + vty->t_read = thread_add_read (master, vty_read, vty, sock); + + /* Time out treatment. */ + if (vty->v_timeout) + { + if (vty->t_timeout) + thread_cancel (vty->t_timeout); + vty->t_timeout = + thread_add_timer (master, vty_timeout, vty, vty->v_timeout); + } + break; + case VTY_WRITE: + if (! vty->t_write) + vty->t_write = thread_add_write (master, vty_flush, vty, sock); + break; + case VTY_TIMEOUT_RESET: + if (vty->t_timeout) + { + thread_cancel (vty->t_timeout); + vty->t_timeout = NULL; + } + if (vty->v_timeout) + { + vty->t_timeout = + thread_add_timer (master, vty_timeout, vty, vty->v_timeout); + } + break; + } +} + +DEFUN (config_who, + config_who_cmd, + "who", + "Display who is on vty\n") +{ + int i; + struct vty *v; + + for (i = 0; i < vector_max (vtyvec); i++) + if ((v = vector_slot (vtyvec, i)) != NULL) + vty_out (vty, "%svty[%d] connected from %s.%s", + v->config ? "*" : " ", + i, v->address, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Move to vty configuration mode. */ +DEFUN (line_vty, + line_vty_cmd, + "line vty", + "Configure a terminal line\n" + "Virtual terminal\n") +{ + vty->node = VTY_NODE; + return CMD_SUCCESS; +} + +/* Set time out value. */ +int +exec_timeout (struct vty *vty, char *min_str, char *sec_str) +{ + unsigned long timeout = 0; + + /* min_str and sec_str are already checked by parser. So it must be + all digit string. */ + if (min_str) + { + timeout = strtol (min_str, NULL, 10); + timeout *= 60; + } + if (sec_str) + timeout += strtol (sec_str, NULL, 10); + + vty_timeout_val = timeout; + vty->v_timeout = timeout; + vty_event (VTY_TIMEOUT_RESET, 0, vty); + + + return CMD_SUCCESS; +} + +DEFUN (exec_timeout_min, + exec_timeout_min_cmd, + "exec-timeout <0-35791>", + "Set timeout value\n" + "Timeout value in minutes\n") +{ + return exec_timeout (vty, argv[0], NULL); +} + +DEFUN (exec_timeout_sec, + exec_timeout_sec_cmd, + "exec-timeout <0-35791> <0-2147483>", + "Set the EXEC timeout\n" + "Timeout in minutes\n" + "Timeout in seconds\n") +{ + return exec_timeout (vty, argv[0], argv[1]); +} + +DEFUN (no_exec_timeout, + no_exec_timeout_cmd, + "no exec-timeout", + NO_STR + "Set the EXEC timeout\n") +{ + return exec_timeout (vty, NULL, NULL); +} + +/* Set vty access class. */ +DEFUN (vty_access_class, + vty_access_class_cmd, + "access-class WORD", + "Filter connections based on an IP access list\n" + "IP access list\n") +{ + if (vty_accesslist_name) + XFREE(MTYPE_VTY, vty_accesslist_name); + + vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]); + + return CMD_SUCCESS; +} + +/* Clear vty access class. */ +DEFUN (no_vty_access_class, + no_vty_access_class_cmd, + "no access-class [WORD]", + NO_STR + "Filter connections based on an IP access list\n" + "IP access list\n") +{ + if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0]))) + { + vty_out (vty, "Access-class is not currently applied to vty%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + XFREE(MTYPE_VTY, vty_accesslist_name); + + vty_accesslist_name = NULL; + + return CMD_SUCCESS; +} + +#ifdef HAVE_IPV6 +/* Set vty access class. */ +DEFUN (vty_ipv6_access_class, + vty_ipv6_access_class_cmd, + "ipv6 access-class WORD", + IPV6_STR + "Filter connections based on an IP access list\n" + "IPv6 access list\n") +{ + if (vty_ipv6_accesslist_name) + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); + + vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]); + + return CMD_SUCCESS; +} + +/* Clear vty access class. */ +DEFUN (no_vty_ipv6_access_class, + no_vty_ipv6_access_class_cmd, + "no ipv6 access-class [WORD]", + NO_STR + IPV6_STR + "Filter connections based on an IP access list\n" + "IPv6 access list\n") +{ + if (! vty_ipv6_accesslist_name || + (argc && strcmp(vty_ipv6_accesslist_name, argv[0]))) + { + vty_out (vty, "IPv6 access-class is not currently applied to vty%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); + + vty_ipv6_accesslist_name = NULL; + + return CMD_SUCCESS; +} +#endif /* HAVE_IPV6 */ + +/* vty login. */ +DEFUN (vty_login, + vty_login_cmd, + "login", + "Enable password checking\n") +{ + no_password_check = 0; + return CMD_SUCCESS; +} + +DEFUN (no_vty_login, + no_vty_login_cmd, + "no login", + NO_STR + "Enable password checking\n") +{ + no_password_check = 1; + return CMD_SUCCESS; +} + +DEFUN (service_advanced_vty, + service_advanced_vty_cmd, + "service advanced-vty", + "Set up miscellaneous service\n" + "Enable advanced mode vty interface\n") +{ + host.advanced = 1; + return CMD_SUCCESS; +} + +DEFUN (no_service_advanced_vty, + no_service_advanced_vty_cmd, + "no service advanced-vty", + NO_STR + "Set up miscellaneous service\n" + "Enable advanced mode vty interface\n") +{ + host.advanced = 0; + return CMD_SUCCESS; +} + +DEFUN (terminal_monitor, + terminal_monitor_cmd, + "terminal monitor", + "Set terminal line parameters\n" + "Copy debug output to the current terminal line\n") +{ + vty->monitor = 1; + return CMD_SUCCESS; +} + +DEFUN (terminal_no_monitor, + terminal_no_monitor_cmd, + "terminal no monitor", + "Set terminal line parameters\n" + NO_STR + "Copy debug output to the current terminal line\n") +{ + vty->monitor = 0; + return CMD_SUCCESS; +} + +DEFUN (show_history, + show_history_cmd, + "show history", + SHOW_STR + "Display the session command history\n") +{ + int index; + + for (index = vty->hindex + 1; index != vty->hindex;) + { + if (index == VTY_MAXHIST) + { + index = 0; + continue; + } + + if (vty->hist[index] != NULL) + vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE); + + index++; + } + + return CMD_SUCCESS; +} + +/* Display current configuration. */ +int +vty_config_write (struct vty *vty) +{ + vty_out (vty, "line vty%s", VTY_NEWLINE); + + if (vty_accesslist_name) + vty_out (vty, " access-class %s%s", + vty_accesslist_name, VTY_NEWLINE); + + if (vty_ipv6_accesslist_name) + vty_out (vty, " ipv6 access-class %s%s", + vty_ipv6_accesslist_name, VTY_NEWLINE); + + /* exec-timeout */ + if (vty_timeout_val != VTY_TIMEOUT_DEFAULT) + vty_out (vty, " exec-timeout %ld %ld%s", + vty_timeout_val / 60, + vty_timeout_val % 60, VTY_NEWLINE); + + /* login */ + if (no_password_check) + vty_out (vty, " no login%s", VTY_NEWLINE); + + vty_out (vty, "!%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +struct cmd_node vty_node = +{ + VTY_NODE, + "%s(config-line)# ", +}; + +/* Reset all VTY status. */ +void +vty_reset () +{ + int i; + struct vty *vty; + struct thread *vty_serv_thread; + + for (i = 0; i < vector_max (vtyvec); i++) + if ((vty = vector_slot (vtyvec, i)) != NULL) + { + buffer_reset (vty->obuf); + vty->status = VTY_CLOSE; + vty_close (vty); + } + + for (i = 0; i < vector_max (Vvty_serv_thread); i++) + if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL) + { + thread_cancel (vty_serv_thread); + vector_slot (Vvty_serv_thread, i) = NULL; + close (i); + } + + vty_timeout_val = VTY_TIMEOUT_DEFAULT; + + if (vty_accesslist_name) + { + XFREE(MTYPE_VTY, vty_accesslist_name); + vty_accesslist_name = NULL; + } + + if (vty_ipv6_accesslist_name) + { + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); + vty_ipv6_accesslist_name = NULL; + } +} + +/* for ospf6d easy temprary reload function */ +/* vty_reset + close accept socket */ +void +vty_finish () +{ + int i; + struct vty *vty; + struct thread *vty_serv_thread; + + for (i = 0; i < vector_max (vtyvec); i++) + if ((vty = vector_slot (vtyvec, i)) != NULL) + { + buffer_reset (vty->obuf); + vty->status = VTY_CLOSE; + vty_close (vty); + } + + for (i = 0; i < vector_max (Vvty_serv_thread); i++) + if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL) + { + thread_cancel (vty_serv_thread); + vector_slot (Vvty_serv_thread, i) = NULL; + close (i); + } + + vty_timeout_val = VTY_TIMEOUT_DEFAULT; + + if (vty_accesslist_name) + { + XFREE(MTYPE_VTY, vty_accesslist_name); + vty_accesslist_name = NULL; + } + + if (vty_ipv6_accesslist_name) + { + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); + vty_ipv6_accesslist_name = NULL; + } +} + +void +vty_save_cwd () +{ + char *cwd; + + cwd = getcwd (NULL, MAXPATHLEN); + + vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1); + strcpy (vty_cwd, cwd); +} + +char * +vty_get_cwd () +{ + return vty_cwd; +} + +int +vty_shell (struct vty *vty) +{ + return vty->type == VTY_SHELL ? 1 : 0; +} + +int +vty_shell_serv (struct vty *vty) +{ + return vty->type == VTY_SHELL_SERV ? 1 : 0; +} + +void +vty_init_vtysh () +{ + vtyvec = vector_init (VECTOR_MIN_SIZE); +} + +/* Install vty's own commands like `who' command. */ +void +vty_init () +{ + /* For further configuration read, preserve current directory. */ + vty_save_cwd (); + + vtyvec = vector_init (VECTOR_MIN_SIZE); + + /* Initilize server thread vector. */ + Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE); + + /* Install bgp top node. */ + install_node (&vty_node, vty_config_write); + + install_element (VIEW_NODE, &config_who_cmd); + install_element (VIEW_NODE, &show_history_cmd); + install_element (ENABLE_NODE, &config_who_cmd); + install_element (CONFIG_NODE, &line_vty_cmd); + install_element (CONFIG_NODE, &service_advanced_vty_cmd); + install_element (CONFIG_NODE, &no_service_advanced_vty_cmd); + install_element (CONFIG_NODE, &show_history_cmd); + install_element (ENABLE_NODE, &terminal_monitor_cmd); + install_element (ENABLE_NODE, &terminal_no_monitor_cmd); + install_element (ENABLE_NODE, &show_history_cmd); + + install_default (VTY_NODE); + install_element (VTY_NODE, &exec_timeout_min_cmd); + install_element (VTY_NODE, &exec_timeout_sec_cmd); + install_element (VTY_NODE, &no_exec_timeout_cmd); + install_element (VTY_NODE, &vty_access_class_cmd); + install_element (VTY_NODE, &no_vty_access_class_cmd); + install_element (VTY_NODE, &vty_login_cmd); + install_element (VTY_NODE, &no_vty_login_cmd); +#ifdef HAVE_IPV6 + install_element (VTY_NODE, &vty_ipv6_access_class_cmd); + install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd); +#endif /* HAVE_IPV6 */ +} diff --git a/lib/vty.h b/lib/vty.h new file mode 100644 index 00000000..4d2a6a03 --- /dev/null +++ b/lib/vty.h @@ -0,0 +1,205 @@ +/* Virtual terminal [aka TeletYpe] interface routine + Copyright (C) 1997 Kunihiro Ishiguro + +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. */ + +#ifndef _ZEBRA_VTY_H +#define _ZEBRA_VTY_H + +#define VTY_BUFSIZ 512 +#define VTY_MAXHIST 20 + +/* VTY struct. */ +struct vty +{ + /* File descripter of this vty. */ + int fd; + + /* Is this vty connect to file or not */ + enum {VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV} type; + + /* Node status of this vty */ + int node; + + /* What address is this vty comming from. */ + char *address; + + /* Privilege level of this vty. */ + int privilege; + + /* Failure count */ + int fail; + + /* Output buffer. */ + struct buffer *obuf; + + /* Command input buffer */ + char *buf; + + /* Command cursor point */ + int cp; + + /* Command length */ + int length; + + /* Command max length. */ + int max; + + /* Histry of command */ + char *hist[VTY_MAXHIST]; + + /* History lookup current point */ + int hp; + + /* History insert end point */ + int hindex; + + /* For current referencing point of interface, route-map, + access-list etc... */ + void *index; + + /* For multiple level index treatment such as key chain and key. */ + void *index_sub; + + /* For escape character. */ + unsigned char escape; + + /* Current vty status. */ + enum {VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE, + VTY_START, VTY_CONTINUE} status; + + /* IAC handling */ + unsigned char iac; + + /* IAC SB handling */ + unsigned char iac_sb_in_progress; + struct buffer *sb_buffer; + + /* Window width/height. */ + int width; + int height; + + int scroll_one; + + /* Configure lines. */ + int lines; + + /* Current executing function pointer. */ + int (*func) (struct vty *, void *arg); + + /* Terminal monitor. */ + int monitor; + + /* In configure mode. */ + int config; + + /* Read and write thread. */ + struct thread *t_read; + struct thread *t_write; + + /* Timeout seconds and thread. */ + unsigned long v_timeout; + struct thread *t_timeout; + + /* Thread output function. */ + struct thread *t_output; + + /* Output data pointer. */ + int (*output_func) (struct vty *, int); + void (*output_clean) (struct vty *); + void *output_rn; + unsigned long output_count; + int output_type; + void *output_arg; +}; + +/* Integrated configuration file. */ +#define INTEGRATE_DEFAULT_CONFIG "Zebra.conf" + +/* Small macro to determine newline is newline only or linefeed needed. */ +#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n") + +/* Default time out value */ +#define VTY_TIMEOUT_DEFAULT 600 + +/* Vty read buffer size. */ +#define VTY_READ_BUFSIZ 512 + +/* Directory separator. */ +#ifndef DIRECTORY_SEP +#define DIRECTORY_SEP '/' +#endif /* DIRECTORY_SEP */ + +#ifndef IS_DIRECTORY_SEP +#define IS_DIRECTORY_SEP(c) ((c) == DIRECTORY_SEP) +#endif + +/* GCC have printf type attribute check. */ +#ifdef __GNUC__ +#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#else +#define PRINTF_ATTRIBUTE(a,b) +#endif /* __GNUC__ */ + +/* Utility macro to convert VTY argument to unsigned integer. */ +#define VTY_GET_INTEGER(NAME,V,STR) \ +{ \ + char *endptr = NULL; \ + (V) = strtoul ((STR), &endptr, 10); \ + if ((V) == ULONG_MAX || *endptr != '\0') \ + { \ + vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE); \ + return CMD_WARNING; \ + } \ +} + +#define VTY_GET_INTEGER_RANGE(NAME,V,STR,MIN,MAX) \ +{ \ + char *endptr = NULL; \ + (V) = strtoul ((STR), &endptr, 10); \ + if ((V) == ULONG_MAX || *endptr != '\0' \ + || (V) < (MIN) || (V) > (MAX)) \ + { \ + vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE); \ + return CMD_WARNING; \ + } \ +} + +/* Exported variables */ +extern char integrate_default[]; + +/* Prototypes. */ +void vty_init (void); +void vty_init_vtysh (void); +void vty_reset (void); +void vty_finish (void); +struct vty *vty_new (void); +int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3); +void vty_read_config (char *, char *, char *); +void vty_time_print (struct vty *, int); +void vty_serv_sock (const char *, unsigned short, char *); +void vty_close (struct vty *); +char *vty_get_cwd (void); +void vty_log (const char *, const char *, va_list); +int vty_config_lock (struct vty *); +int vty_config_unlock (struct vty *); +int vty_shell (struct vty *); +int vty_shell_serv (struct vty *); +void vty_hello (struct vty *); + +#endif /* _ZEBRA_VTY_H */ diff --git a/lib/zclient.c b/lib/zclient.c new file mode 100644 index 00000000..5e371546 --- /dev/null +++ b/lib/zclient.c @@ -0,0 +1,901 @@ +/* Zebra's client library. + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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 <zebra.h> + +#include "prefix.h" +#include "stream.h" +#include "network.h" +#include "if.h" +#include "log.h" +#include "thread.h" +#include "zclient.h" +#include "memory.h" +#include "table.h" + +#include "zebra/rib.h" +#include "zebra/zserv.h" + +/* Zebra client events. */ +enum event {ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT}; + +/* Prototype for event manager. */ +static void zclient_event (enum event, struct zclient *); + +/* This file local debug flag. */ +int zclient_debug = 0; + +/* Allocate zclient structure. */ +struct zclient * +zclient_new () +{ + struct zclient *zclient; + zclient = XMALLOC (MTYPE_ZCLIENT, sizeof (struct zclient)); + memset (zclient, 0, sizeof (struct zclient)); + + zclient->ibuf = stream_new (ZEBRA_MAX_PACKET_SIZ); + zclient->obuf = stream_new (ZEBRA_MAX_PACKET_SIZ); + + return zclient; +} + +/* Free zclient structure. */ +void +zclient_free (struct zclient *zclient) +{ + XFREE (MTYPE_ZCLIENT, zclient); +} + +/* Initialize zebra client. Argument redist_default is unwanted + redistribute route type. */ +void +zclient_init (struct zclient *zclient, int redist_default) +{ + int i; + + /* Enable zebra client connection by default. */ + zclient->enable = 1; + + /* Set -1 to the default socket value. */ + zclient->sock = -1; + + /* Clear redistribution flags. */ + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + zclient->redist[i] = 0; + + /* Set unwanted redistribute route. bgpd does not need BGP route + redistribution. */ + zclient->redist_default = redist_default; + zclient->redist[redist_default] = 1; + + /* Set default-information redistribute to zero. */ + zclient->default_information = 0; + + /* Schedule first zclient connection. */ + if (zclient_debug) + zlog_info ("zclient start scheduled"); + + zclient_event (ZCLIENT_SCHEDULE, zclient); +} + +/* Stop zebra client services. */ +void +zclient_stop (struct zclient *zclient) +{ + if (zclient_debug) + zlog_info ("zclient stopped"); + + /* Stop threads. */ + if (zclient->t_read) + { + thread_cancel (zclient->t_read); + zclient->t_read = NULL; + } + if (zclient->t_connect) + { + thread_cancel (zclient->t_connect); + zclient->t_connect = NULL; + } + + /* Close socket. */ + if (zclient->sock >= 0) + { + close (zclient->sock); + zclient->sock = -1; + } + zclient->fail = 0; +} + +void +zclient_reset (struct zclient *zclient) +{ + zclient_stop (zclient); + zclient_init (zclient, zclient->redist_default); +} + +/* Make socket to zebra daemon. Return zebra socket. */ +int +zclient_socket () +{ + int sock; + int ret; + struct sockaddr_in serv; + + /* We should think about IPv6 connection. */ + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + return -1; + + /* Make server socket. */ + memset (&serv, 0, sizeof (struct sockaddr_in)); + serv.sin_family = AF_INET; + serv.sin_port = htons (ZEBRA_PORT); +#ifdef HAVE_SIN_LEN + serv.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_SIN_LEN */ + serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + /* Connect to zebra. */ + ret = connect (sock, (struct sockaddr *) &serv, sizeof (serv)); + if (ret < 0) + { + close (sock); + return -1; + } + return sock; +} + +/* For sockaddr_un. */ +#include <sys/un.h> + +int +zclient_socket_un (char *path) +{ + int ret; + int sock, len; + struct sockaddr_un addr; + + sock = socket (AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return -1; + + /* Make server socket. */ + memset (&addr, 0, sizeof (struct sockaddr_un)); + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, path, strlen (path)); +#ifdef HAVE_SUN_LEN + len = addr.sun_len = SUN_LEN(&addr); +#else + len = sizeof (addr.sun_family) + strlen (addr.sun_path); +#endif /* HAVE_SUN_LEN */ + + ret = connect (sock, (struct sockaddr *) &addr, len); + if (ret < 0) + { + close (sock); + return -1; + } + return sock; +} + +/* Send simple Zebra message. */ +int +zebra_message_send (struct zclient *zclient, int command) +{ + struct stream *s; + + /* Get zclient output buffer. */ + s = zclient->obuf; + stream_reset (s); + + /* Send very simple command only Zebra message. */ + stream_putw (s, 3); + stream_putc (s, command); + + return writen (zclient->sock, s->data, 3); +} + +/* Make connection to zebra daemon. */ +int +zclient_start (struct zclient *zclient) +{ + int i; + + if (zclient_debug) + zlog_info ("zclient_start is called"); + + /* zclient is disabled. */ + if (! zclient->enable) + return 0; + + /* If already connected to the zebra. */ + if (zclient->sock >= 0) + return 0; + + /* Check connect thread. */ + if (zclient->t_connect) + return 0; + + /* Make socket. */ +#ifdef HAVE_TCP_ZEBRA + zclient->sock = zclient_socket (); +#else + zclient->sock = zclient_socket_un (ZEBRA_SERV_PATH); +#endif /* HAVE_TCP_ZEBRA */ + if (zclient->sock < 0) + { + if (zclient_debug) + zlog_info ("zclient connection fail"); + zclient->fail++; + zclient_event (ZCLIENT_CONNECT, zclient); + return -1; + } + + /* Clear fail count. */ + zclient->fail = 0; + if (zclient_debug) + zlog_info ("zclient connect success with socket [%d]", zclient->sock); + + /* Create read thread. */ + zclient_event (ZCLIENT_READ, zclient); + + /* We need interface information. */ + zebra_message_send (zclient, ZEBRA_INTERFACE_ADD); + + /* Flush all redistribute request. */ + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + if (i != zclient->redist_default && zclient->redist[i]) + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient->sock, i); + + /* If default information is needed. */ + if (zclient->default_information) + zebra_message_send (zclient, ZEBRA_REDISTRIBUTE_DEFAULT_ADD); + + return 0; +} + +/* This function is a wrapper function for calling zclient_start from + timer or event thread. */ +int +zclient_connect (struct thread *t) +{ + struct zclient *zclient; + + zclient = THREAD_ARG (t); + zclient->t_connect = NULL; + + if (zclient_debug) + zlog_info ("zclient_connect is called"); + + return zclient_start (zclient); +} + +int +zapi_ipv4_add (struct zclient *zclient, struct prefix_ipv4 *p, + struct zapi_ipv4 *api) +{ + int i; + int psize; + struct stream *s; + + /* Reset stream. */ + s = zclient->obuf; + stream_reset (s); + + /* Length place holder. */ + stream_putw (s, 0); + + /* Put command, type and nexthop. */ + stream_putc (s, ZEBRA_IPV4_ROUTE_ADD); + stream_putc (s, api->type); + stream_putc (s, api->flags); + stream_putc (s, api->message); + + /* Put prefix information. */ + psize = PSIZE (p->prefixlen); + stream_putc (s, p->prefixlen); + stream_write (s, (u_char *)&p->prefix, psize); + + /* Nexthop, ifindex, distance and metric information. */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) + { + if (CHECK_FLAG (api->flags, ZEBRA_FLAG_BLACKHOLE)) + { + stream_putc (s, 1); + stream_putc (s, ZEBRA_NEXTHOP_BLACKHOLE); + } + else + stream_putc (s, api->nexthop_num + api->ifindex_num); + + for (i = 0; i < api->nexthop_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IPV4); + stream_put_in_addr (s, api->nexthop[i]); + } + for (i = 0; i < api->ifindex_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IFINDEX); + stream_putl (s, api->ifindex[i]); + } + } + + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_DISTANCE)) + stream_putc (s, api->distance); + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC)) + stream_putl (s, api->metric); + + /* Put length at the first point of the stream. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + return writen (zclient->sock, s->data, stream_get_endp (s)); +} + +int +zapi_ipv4_delete (struct zclient *zclient, struct prefix_ipv4 *p, + struct zapi_ipv4 *api) +{ + int i; + int psize; + struct stream *s; + + /* Reset stream. */ + s = zclient->obuf; + stream_reset (s); + + /* Length place holder. */ + stream_putw (s, 0); + + /* Put command, type and nexthop. */ + stream_putc (s, ZEBRA_IPV4_ROUTE_DELETE); + stream_putc (s, api->type); + stream_putc (s, api->flags); + stream_putc (s, api->message); + + /* Put prefix information. */ + psize = PSIZE (p->prefixlen); + stream_putc (s, p->prefixlen); + stream_write (s, (u_char *)&p->prefix, psize); + + /* Nexthop, ifindex, distance and metric information. */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) + { + if (CHECK_FLAG (api->flags, ZEBRA_FLAG_BLACKHOLE)) + { + stream_putc (s, 1); + stream_putc (s, ZEBRA_NEXTHOP_BLACKHOLE); + } + else + stream_putc (s, api->nexthop_num + api->ifindex_num); + + for (i = 0; i < api->nexthop_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IPV4); + stream_put_in_addr (s, api->nexthop[i]); + } + for (i = 0; i < api->ifindex_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IFINDEX); + stream_putl (s, api->ifindex[i]); + } + } + + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_DISTANCE)) + stream_putc (s, api->distance); + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC)) + stream_putl (s, api->metric); + + /* Put length at the first point of the stream. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + return writen (zclient->sock, s->data, stream_get_endp (s)); +} + +#ifdef HAVE_IPV6 +int +zapi_ipv6_add (struct zclient *zclient, struct prefix_ipv6 *p, + struct zapi_ipv6 *api) +{ + int i; + int psize; + struct stream *s; + + /* Reset stream. */ + s = zclient->obuf; + stream_reset (s); + + /* Length place holder. */ + stream_putw (s, 0); + + /* Put command, type and nexthop. */ + stream_putc (s, ZEBRA_IPV6_ROUTE_ADD); + stream_putc (s, api->type); + stream_putc (s, api->flags); + stream_putc (s, api->message); + + /* Put prefix information. */ + psize = PSIZE (p->prefixlen); + stream_putc (s, p->prefixlen); + stream_write (s, (u_char *)&p->prefix, psize); + + /* Nexthop, ifindex, distance and metric information. */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) + { + stream_putc (s, api->nexthop_num + api->ifindex_num); + + for (i = 0; i < api->nexthop_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IPV6); + stream_write (s, (u_char *)api->nexthop[i], 16); + } + for (i = 0; i < api->ifindex_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IFINDEX); + stream_putl (s, api->ifindex[i]); + } + } + + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_DISTANCE)) + stream_putc (s, api->distance); + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC)) + stream_putl (s, api->metric); + + /* Put length at the first point of the stream. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + return writen (zclient->sock, s->data, stream_get_endp (s)); +} + +int +zapi_ipv6_delete (struct zclient *zclient, struct prefix_ipv6 *p, + struct zapi_ipv6 *api) +{ + int i; + int psize; + struct stream *s; + + /* Reset stream. */ + s = zclient->obuf; + stream_reset (s); + + /* Length place holder. */ + stream_putw (s, 0); + + /* Put command, type and nexthop. */ + stream_putc (s, ZEBRA_IPV6_ROUTE_DELETE); + stream_putc (s, api->type); + stream_putc (s, api->flags); + stream_putc (s, api->message); + + /* Put prefix information. */ + psize = PSIZE (p->prefixlen); + stream_putc (s, p->prefixlen); + stream_write (s, (u_char *)&p->prefix, psize); + + /* Nexthop, ifindex, distance and metric information. */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) + { + stream_putc (s, api->nexthop_num + api->ifindex_num); + + for (i = 0; i < api->nexthop_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IPV6); + stream_write (s, (u_char *)api->nexthop[i], 16); + } + for (i = 0; i < api->ifindex_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IFINDEX); + stream_putl (s, api->ifindex[i]); + } + } + + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_DISTANCE)) + stream_putc (s, api->distance); + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC)) + stream_putl (s, api->metric); + + /* Put length at the first point of the stream. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + return writen (zclient->sock, s->data, stream_get_endp (s)); +} + +#endif /* HAVE_IPV6 */ + +int +zebra_redistribute_send (int command, int sock, int type) +{ + int ret; + struct stream *s; + + s = stream_new (ZEBRA_MAX_PACKET_SIZ); + + /* Total length of the messages. */ + stream_putw (s, 4); + + stream_putc (s, command); + stream_putc (s, type); + + ret = writen (sock, s->data, 4); + + stream_free (s); + + return ret; +} + +/* Interface addition from zebra daemon. */ +struct interface * +zebra_interface_add_read (struct stream *s) +{ + struct interface *ifp; + u_char ifname_tmp[INTERFACE_NAMSIZ]; + + /* Read interface name. */ + stream_get (ifname_tmp, s, INTERFACE_NAMSIZ); + + /* Lookup this by interface name. */ + ifp = if_lookup_by_name (ifname_tmp); + + /* If such interface does not exist, make new one. */ + if (! ifp) + { + ifp = if_create (); + strncpy (ifp->name, ifname_tmp, IFNAMSIZ); + } + + /* Read interface's index. */ + ifp->ifindex = stream_getl (s); + + /* Read interface's value. */ + ifp->flags = stream_getl (s); + ifp->metric = stream_getl (s); + ifp->mtu = stream_getl (s); + ifp->bandwidth = stream_getl (s); +#ifdef HAVE_SOCKADDR_DL + stream_get (&ifp->sdl, s, sizeof (ifp->sdl)); +#else + ifp->hw_addr_len = stream_getl (s); + if (ifp->hw_addr_len) + stream_get (ifp->hw_addr, s, ifp->hw_addr_len); +#endif /* HAVE_SOCKADDR_DL */ + + return ifp; +} + +/* Read interface up/down msg from zebra daemon. */ +struct interface * +zebra_interface_state_read (struct stream *s) +{ + struct interface *ifp; + u_char ifname_tmp[INTERFACE_NAMSIZ]; + + /* Read interface name. */ + stream_get (ifname_tmp, s, INTERFACE_NAMSIZ); + + /* Lookup this by interface index. */ + ifp = if_lookup_by_name (ifname_tmp); + + /* If such interface does not exist, indicate an error */ + if (! ifp) + return NULL; + + /* Read interface's index. */ + ifp->ifindex = stream_getl (s); + + /* Read interface's value. */ + ifp->flags = stream_getl (s); + ifp->metric = stream_getl (s); + ifp->mtu = stream_getl (s); + ifp->bandwidth = stream_getl (s); + + return ifp; +} + +struct connected * +zebra_interface_address_add_read (struct stream *s) +{ + unsigned int ifindex; + struct interface *ifp; + struct connected *ifc; + struct prefix *p; + int family; + int plen; + + /* Get interface index. */ + ifindex = stream_getl (s); + + /* Lookup index. */ + ifp = if_lookup_by_index (ifindex); + if (ifp == NULL) + { + zlog_warn ("zebra_interface_address_add_read: Can't find interface by ifindex: %d ", ifindex); + return NULL; + } + + /* Allocate new connected address. */ + ifc = connected_new (); + ifc->ifp = ifp; + + /* Fetch flag. */ + ifc->flags = stream_getc (s); + + /* Fetch interface address. */ + p = prefix_new (); + family = p->family = stream_getc (s); + + plen = prefix_blen (p); + stream_get (&p->u.prefix, s, plen); + p->prefixlen = stream_getc (s); + ifc->address = p; + + /* Fetch destination address. */ + p = prefix_new (); + stream_get (&p->u.prefix, s, plen); + p->family = family; + + ifc->destination = p; + + p = ifc->address; + + /* Add connected address to the interface. */ + listnode_add (ifp->connected, ifc); + + return ifc; +} + +struct connected * +zebra_interface_address_delete_read (struct stream *s) +{ + unsigned int ifindex; + struct interface *ifp; + struct connected *ifc; + struct prefix p; + struct prefix d; + int family; + int len; + u_char flags; + + /* Get interface index. */ + ifindex = stream_getl (s); + + /* Lookup index. */ + ifp = if_lookup_by_index (ifindex); + if (ifp == NULL) + { + zlog_warn ("zebra_interface_address_delete_read: Can't find interface by ifindex: %d ", ifindex); + return NULL; + } + + /* Fetch flag. */ + flags = stream_getc (s); + + /* Fetch interface address. */ + family = p.family = stream_getc (s); + + len = prefix_blen (&p); + stream_get (&p.u.prefix, s, len); + p.prefixlen = stream_getc (s); + + /* Fetch destination address. */ + stream_get (&d.u.prefix, s, len); + d.family = family; + + ifc = connected_delete_by_prefix (ifp, &p); + + return ifc; +} + +/* Zebra client message read function. */ +int +zclient_read (struct thread *thread) +{ + int ret; + int nbytes; + int sock; + zebra_size_t length; + zebra_command_t command; + struct zclient *zclient; + + /* Get socket to zebra. */ + sock = THREAD_FD (thread); + zclient = THREAD_ARG (thread); + zclient->t_read = NULL; + + /* Clear input buffer. */ + stream_reset (zclient->ibuf); + + /* Read zebra header. */ + nbytes = stream_read (zclient->ibuf, sock, ZEBRA_HEADER_SIZE); + + /* zebra socket is closed. */ + if (nbytes == 0) + { + if (zclient_debug) + zlog_info ("zclient connection closed socket [%d].", sock); + zclient->fail++; + zclient_stop (zclient); + zclient_event (ZCLIENT_CONNECT, zclient); + return -1; + } + + /* zebra read error. */ + if (nbytes < 0 || nbytes != ZEBRA_HEADER_SIZE) + { + if (zclient_debug) + zlog_info ("Can't read all packet (length %d).", nbytes); + zclient->fail++; + zclient_stop (zclient); + zclient_event (ZCLIENT_CONNECT, zclient); + return -1; + } + + /* Fetch length and command. */ + length = stream_getw (zclient->ibuf); + command = stream_getc (zclient->ibuf); + + /* Length check. */ + if (length >= zclient->ibuf->size) + { + stream_free (zclient->ibuf); + zclient->ibuf = stream_new (length + 1); + } + length -= ZEBRA_HEADER_SIZE; + + /* Read rest of zebra packet. */ + nbytes = stream_read (zclient->ibuf, sock, length); + if (nbytes != length) + { + if (zclient_debug) + zlog_info ("zclient connection closed socket [%d].", sock); + zclient->fail++; + zclient_stop (zclient); + zclient_event (ZCLIENT_CONNECT, zclient); + return -1; + } + + switch (command) + { + case ZEBRA_INTERFACE_ADD: + if (zclient->interface_add) + ret = (*zclient->interface_add) (command, zclient, length); + break; + case ZEBRA_INTERFACE_DELETE: + if (zclient->interface_delete) + ret = (*zclient->interface_delete) (command, zclient, length); + break; + case ZEBRA_INTERFACE_ADDRESS_ADD: + if (zclient->interface_address_add) + ret = (*zclient->interface_address_add) (command, zclient, length); + break; + case ZEBRA_INTERFACE_ADDRESS_DELETE: + if (zclient->interface_address_delete) + ret = (*zclient->interface_address_delete) (command, zclient, length); + break; + case ZEBRA_INTERFACE_UP: + if (zclient->interface_up) + ret = (*zclient->interface_up) (command, zclient, length); + break; + case ZEBRA_INTERFACE_DOWN: + if (zclient->interface_down) + ret = (*zclient->interface_down) (command, zclient, length); + break; + case ZEBRA_IPV4_ROUTE_ADD: + if (zclient->ipv4_route_add) + ret = (*zclient->ipv4_route_add) (command, zclient, length); + break; + case ZEBRA_IPV4_ROUTE_DELETE: + if (zclient->ipv4_route_delete) + ret = (*zclient->ipv4_route_delete) (command, zclient, length); + break; + case ZEBRA_IPV6_ROUTE_ADD: + if (zclient->ipv6_route_add) + ret = (*zclient->ipv6_route_add) (command, zclient, length); + break; + case ZEBRA_IPV6_ROUTE_DELETE: + if (zclient->ipv6_route_delete) + ret = (*zclient->ipv6_route_delete) (command, zclient, length); + break; + default: + break; + } + + /* Register read thread. */ + zclient_event (ZCLIENT_READ, zclient); + + return 0; +} + +void +zclient_redistribute_set (struct zclient *zclient, int type) +{ + if (zclient->redist[type]) + return; + + zclient->redist[type] = 1; + + if (zclient->sock > 0) + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient->sock, type); +} + +void +zclient_redistribute_unset (struct zclient *zclient, int type) +{ + if (! zclient->redist[type]) + return; + + zclient->redist[type] = 0; + + if (zclient->sock > 0) + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient->sock, type); +} + +void +zclient_redistribute_default_set (struct zclient *zclient) +{ + if (zclient->default_information) + return; + + zclient->default_information = 1; + + if (zclient->sock > 0) + zebra_message_send (zclient, ZEBRA_REDISTRIBUTE_DEFAULT_ADD); +} + +void +zclient_redistribute_default_unset (struct zclient *zclient) +{ + if (! zclient->default_information) + return; + + zclient->default_information = 0; + + if (zclient->sock > 0) + zebra_message_send (zclient, ZEBRA_REDISTRIBUTE_DEFAULT_DELETE); +} + +extern struct thread_master *master; + +static void +zclient_event (enum event event, struct zclient *zclient) +{ + switch (event) + { + case ZCLIENT_SCHEDULE: + if (! zclient->t_connect) + zclient->t_connect = + thread_add_event (master, zclient_connect, zclient, 0); + break; + case ZCLIENT_CONNECT: + if (zclient->fail >= 10) + return; + if (zclient_debug) + zlog_info ("zclient connect schedule interval is %d", + zclient->fail < 3 ? 10 : 60); + if (! zclient->t_connect) + zclient->t_connect = + thread_add_timer (master, zclient_connect, zclient, + zclient->fail < 3 ? 10 : 60); + break; + case ZCLIENT_READ: + zclient->t_read = + thread_add_read (master, zclient_read, zclient, zclient->sock); + break; + } +} diff --git a/lib/zclient.h b/lib/zclient.h new file mode 100644 index 00000000..66307c94 --- /dev/null +++ b/lib/zclient.h @@ -0,0 +1,164 @@ +/* Zebra's client header. + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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. + */ + +#ifndef _ZEBRA_ZCLIENT_H +#define _ZEBRA_ZCLIENT_H + +/* For struct interface and struct connected. */ +#include "if.h" + +/* For input/output buffer to zebra. */ +#define ZEBRA_MAX_PACKET_SIZ 4096 + +/* Zebra header size. */ +#define ZEBRA_HEADER_SIZE 3 + +/* Structure for the zebra client. */ +struct zclient +{ + /* Socket to zebra daemon. */ + int sock; + + /* Flag of communication to zebra is enabled or not. Default is on. + This flag is disabled by `no router zebra' statement. */ + int enable; + + /* Connection failure count. */ + int fail; + + /* Input buffer for zebra message. */ + struct stream *ibuf; + + /* Output buffer for zebra message. */ + struct stream *obuf; + + /* Read and connect thread. */ + struct thread *t_read; + struct thread *t_connect; + + /* Redistribute information. */ + u_char redist_default; + u_char redist[ZEBRA_ROUTE_MAX]; + + /* Redistribute defauilt. */ + u_char default_information; + + /* Pointer to the callback functions. */ + int (*interface_add) (int, struct zclient *, zebra_size_t); + int (*interface_delete) (int, struct zclient *, zebra_size_t); + int (*interface_up) (int, struct zclient *, zebra_size_t); + int (*interface_down) (int, struct zclient *, zebra_size_t); + int (*interface_address_add) (int, struct zclient *, zebra_size_t); + int (*interface_address_delete) (int, struct zclient *, zebra_size_t); + int (*ipv4_route_add) (int, struct zclient *, zebra_size_t); + int (*ipv4_route_delete) (int, struct zclient *, zebra_size_t); + int (*ipv6_route_add) (int, struct zclient *, zebra_size_t); + int (*ipv6_route_delete) (int, struct zclient *, zebra_size_t); +}; + +/* Zebra API message flag. */ +#define ZAPI_MESSAGE_NEXTHOP 0x01 +#define ZAPI_MESSAGE_IFINDEX 0x02 +#define ZAPI_MESSAGE_DISTANCE 0x04 +#define ZAPI_MESSAGE_METRIC 0x08 + +/* Zebra IPv4 route message API. */ +struct zapi_ipv4 +{ + u_char type; + + u_char flags; + + u_char message; + + u_char nexthop_num; + struct in_addr **nexthop; + + u_char ifindex_num; + unsigned int *ifindex; + + u_char distance; + + u_int32_t metric; +}; + +int +zapi_ipv4_add (struct zclient *, struct prefix_ipv4 *, struct zapi_ipv4 *); + +int +zapi_ipv4_delete (struct zclient *, struct prefix_ipv4 *, struct zapi_ipv4 *); + +/* Prototypes of zebra client service functions. */ +struct zclient *zclient_new (void); +void zclient_free (struct zclient *); +void zclient_init (struct zclient *, int); +int zclient_start (struct zclient *); +void zclient_stop (struct zclient *); +void zclient_reset (struct zclient *); +int zclient_socket (); +int zclient_socket_un (char *); + +void zclient_redistribute_set (struct zclient *, int); +void zclient_redistribute_unset (struct zclient *, int); + +void zclient_redistribute_default_set (struct zclient *); +void zclient_redistribute_default_unset (struct zclient *); + +/* struct zebra *zebra_new (); */ +int zebra_redistribute_send (int, int, int); + +struct interface *zebra_interface_add_read (struct stream *); +struct interface *zebra_interface_state_read (struct stream *s); +struct connected *zebra_interface_address_add_read (struct stream *); +struct connected *zebra_interface_address_delete_read (struct stream *); + +#ifdef HAVE_IPV6 +/* IPv6 prefix add and delete function prototype. */ + +struct zapi_ipv6 +{ + u_char type; + + u_char flags; + + u_char message; + + u_char nexthop_num; + struct in6_addr **nexthop; + + u_char ifindex_num; + unsigned int *ifindex; + + u_char distance; + + u_int32_t metric; +}; + +int +zapi_ipv6_add (struct zclient *zclient, struct prefix_ipv6 *p, + struct zapi_ipv6 *api); +int +zapi_ipv6_delete (struct zclient *zclient, struct prefix_ipv6 *p, + struct zapi_ipv6 *api); + +#endif /* HAVE_IPV6 */ + +#endif /* _ZEBRA_ZCLIENT_H */ diff --git a/lib/zebra.h b/lib/zebra.h new file mode 100644 index 00000000..06302b3d --- /dev/null +++ b/lib/zebra.h @@ -0,0 +1,312 @@ +/* Zebra common header. + Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Kunihiro Ishiguro + +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. */ + +#ifndef _ZEBRA_H +#define _ZEBRA_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef SUNOS_5 +#define _XPG4_2 +#define __EXTENSIONS__ +#endif /* SUNOS_5 */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <string.h> +#ifdef HAVE_STROPTS_H +#include <stropts.h> +#endif /* HAVE_STROPTS_H */ +#include <sys/fcntl.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif /* HAVE_SYS_SELECT_H */ +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/param.h> +#ifdef HAVE_SYS_SYSCTL_H +#include <sys/sysctl.h> +#endif /* HAVE_SYS_SYSCTL_H */ +#include <sys/ioctl.h> +#ifdef HAVE_SYS_CONF_H +#include <sys/conf.h> +#endif /* HAVE_SYS_CONF_H */ +#ifdef HAVE_SYS_KSYM_H +#include <sys/ksym.h> +#endif /* HAVE_SYS_KSYM_H */ +#include <syslog.h> +#include <time.h> +#include <sys/uio.h> +#include <sys/utsname.h> +#ifdef HAVE_RUSAGE +#include <sys/resource.h> +#endif /* HAVE_RUSAGE */ + +/* machine dependent includes */ +#ifdef SUNOS_5 +#include <limits.h> +#include <strings.h> +#endif /* SUNOS_5 */ + +/* machine dependent includes */ +#ifdef HAVE_LINUX_VERSION_H +#include <linux/version.h> +#endif /* HAVE_LINUX_VERSION_H */ + +#ifdef HAVE_ASM_TYPES_H +#include <asm/types.h> +#endif /* HAVE_ASM_TYPES_H */ + +/* misc include group */ +#include <stdarg.h> +#include <assert.h> + +/* network include group */ + +#include <sys/socket.h> + +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif /* HAVE_SYS_SOCKIO_H */ + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif /* HAVE_NETINET_IN_H */ +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#ifdef HAVE_NET_NETOPT_H +#include <net/netopt.h> +#endif /* HAVE_NET_NETOPT_H */ + +#include <net/if.h> + +#ifdef HAVE_NET_IF_DL_H +#include <net/if_dl.h> +#endif /* HAVE_NET_IF_DL_H */ + +#ifdef HAVE_NET_IF_VAR_H +#include <net/if_var.h> +#endif /* HAVE_NET_IF_VAR_H */ + +#include <net/route.h> + +#ifdef HAVE_NETLINK +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#else +#define RT_TABLE_MAIN 0 +#endif /* HAVE_NETLINK */ + +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif /* HAVE_NETDB_H */ + +#include <arpa/inet.h> +#include <arpa/telnet.h> + +#ifdef HAVE_INET_ND_H +#include <inet/nd.h> +#endif /* HAVE_INET_ND_H */ + +#ifdef HAVE_NETINET_IN_VAR_H +#include <netinet/in_var.h> +#endif /* HAVE_NETINET_IN_VAR_H */ + +#ifdef HAVE_NETINET_IN6_VAR_H +#include <netinet/in6_var.h> +#endif /* HAVE_NETINET_IN6_VAR_H */ + +#ifdef HAVE_NETINET6_IN_H +#include <netinet6/in.h> +#endif /* HAVE_NETINET6_IN_H */ + + +#ifdef HAVE_NETINET6_IP6_H +#include <netinet6/ip6.h> +#endif /* HAVE_NETINET6_IP6_H */ + +#ifdef HAVE_NETINET_ICMP6_H +#include <netinet/icmp6.h> +#endif /* HAVE_NETINET_ICMP6_H */ + +#ifdef HAVE_NETINET6_ND6_H +#include <netinet6/nd6.h> +#endif /* HAVE_NETINET6_ND6_H */ + +#ifdef HAVE_LIBUTIL_H +#include <libutil.h> +#endif /* HAVE_LIBUTIL_H */ + +#ifdef BSDI_NRL + +#ifdef HAVE_NETINET6_IN6_H +#include <netinet6/in6.h> +#endif /* HAVE_NETINET6_IN6_H */ + +#ifdef NRL +#include <netinet6/in6.h> +#endif /* NRL */ + +#define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL + +/* BSD/OS 4.0 has lost belows defines, it should appear at + /usr/include/sys/socket.h. */ +#define CMSG_ALIGN(n) (((n) + 3) & ~3) +#define CMSG_SPACE(l) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(l)) +#define CMSG_LEN(l) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (l)) + +#endif /* BSDI_NRL */ + +/* The definition of struct in_pktinfo is missing in old version of + GLIBC 2.1 (Redhat 6.1). */ +#if defined (GNU_LINUX) && ! defined (HAVE_INPKTINFO) +struct in_pktinfo +{ + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; +}; +#endif + +/* For old definition. */ +#ifndef IN6_ARE_ADDR_EQUAL +#define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL +#endif /* IN6_ARE_ADDR_EQUAL */ + +/* Zebra message types. */ +#define ZEBRA_INTERFACE_ADD 1 +#define ZEBRA_INTERFACE_DELETE 2 +#define ZEBRA_INTERFACE_ADDRESS_ADD 3 +#define ZEBRA_INTERFACE_ADDRESS_DELETE 4 +#define ZEBRA_INTERFACE_UP 5 +#define ZEBRA_INTERFACE_DOWN 6 +#define ZEBRA_IPV4_ROUTE_ADD 7 +#define ZEBRA_IPV4_ROUTE_DELETE 8 +#define ZEBRA_IPV6_ROUTE_ADD 9 +#define ZEBRA_IPV6_ROUTE_DELETE 10 +#define ZEBRA_REDISTRIBUTE_ADD 11 +#define ZEBRA_REDISTRIBUTE_DELETE 12 +#define ZEBRA_REDISTRIBUTE_DEFAULT_ADD 13 +#define ZEBRA_REDISTRIBUTE_DEFAULT_DELETE 14 +#define ZEBRA_IPV4_NEXTHOP_LOOKUP 15 +#define ZEBRA_IPV6_NEXTHOP_LOOKUP 16 +#define ZEBRA_IPV4_IMPORT_LOOKUP 17 +#define ZEBRA_IPV6_IMPORT_LOOKUP 18 +#define ZEBRA_MESSAGE_MAX 19 + +/* Zebra route's types. */ +#define ZEBRA_ROUTE_SYSTEM 0 +#define ZEBRA_ROUTE_KERNEL 1 +#define ZEBRA_ROUTE_CONNECT 2 +#define ZEBRA_ROUTE_STATIC 3 +#define ZEBRA_ROUTE_RIP 4 +#define ZEBRA_ROUTE_RIPNG 5 +#define ZEBRA_ROUTE_OSPF 6 +#define ZEBRA_ROUTE_OSPF6 7 +#define ZEBRA_ROUTE_BGP 8 +#define ZEBRA_ROUTE_MAX 9 + +/* Zebra's family types. */ +#define ZEBRA_FAMILY_IPV4 1 +#define ZEBRA_FAMILY_IPV6 2 +#define ZEBRA_FAMILY_MAX 3 + +/* Error codes of zebra. */ +#define ZEBRA_ERR_RTEXIST -1 +#define ZEBRA_ERR_RTUNREACH -2 +#define ZEBRA_ERR_EPERM -3 +#define ZEBRA_ERR_RTNOEXIST -4 + +/* Zebra message flags */ +#define ZEBRA_FLAG_INTERNAL 0x01 +#define ZEBRA_FLAG_SELFROUTE 0x02 +#define ZEBRA_FLAG_BLACKHOLE 0x04 +#define ZEBRA_FLAG_IBGP 0x08 +#define ZEBRA_FLAG_SELECTED 0x10 +#define ZEBRA_FLAG_CHANGED 0x20 +#define ZEBRA_FLAG_STATIC 0x40 + +/* Zebra nexthop flags. */ +#define ZEBRA_NEXTHOP_IFINDEX 1 +#define ZEBRA_NEXTHOP_IFNAME 2 +#define ZEBRA_NEXTHOP_IPV4 3 +#define ZEBRA_NEXTHOP_IPV4_IFINDEX 4 +#define ZEBRA_NEXTHOP_IPV4_IFNAME 5 +#define ZEBRA_NEXTHOP_IPV6 6 +#define ZEBRA_NEXTHOP_IPV6_IFINDEX 7 +#define ZEBRA_NEXTHOP_IPV6_IFNAME 8 +#define ZEBRA_NEXTHOP_BLACKHOLE 9 + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */ +#endif + +/* Address family numbers from RFC1700. */ +#define AFI_IP 1 +#define AFI_IP6 2 +#define AFI_MAX 3 + +/* Subsequent Address Family Identifier. */ +#define SAFI_UNICAST 1 +#define SAFI_MULTICAST 2 +#define SAFI_UNICAST_MULTICAST 3 +#define SAFI_MPLS_VPN 4 +#define SAFI_MAX 5 + +/* Filter direction. */ +#define FILTER_IN 0 +#define FILTER_OUT 1 +#define FILTER_MAX 2 + +/* Default Administrative Distance of each protocol. */ +#define ZEBRA_KERNEL_DISTANCE_DEFAULT 0 +#define ZEBRA_CONNECT_DISTANCE_DEFAULT 0 +#define ZEBRA_STATIC_DISTANCE_DEFAULT 1 +#define ZEBRA_RIP_DISTANCE_DEFAULT 120 +#define ZEBRA_RIPNG_DISTANCE_DEFAULT 120 +#define ZEBRA_OSPF_DISTANCE_DEFAULT 110 +#define ZEBRA_OSPF6_DISTANCE_DEFAULT 110 +#define ZEBRA_IBGP_DISTANCE_DEFAULT 200 +#define ZEBRA_EBGP_DISTANCE_DEFAULT 20 + +/* Flag manipulation macros. */ +#define CHECK_FLAG(V,F) ((V) & (F)) +#define SET_FLAG(V,F) (V) = (V) | (F) +#define UNSET_FLAG(V,F) (V) = (V) & ~(F) + +/* AFI and SAFI type. */ +typedef u_int16_t afi_t; +typedef u_char safi_t; + +/* Zebra types. */ +typedef u_int16_t zebra_size_t; +typedef u_int8_t zebra_command_t; + +#endif /* _ZEBRA_H */ |