summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorpaul <paul>2002-12-13 20:15:29 +0000
committerpaul <paul>2002-12-13 20:15:29 +0000
commit718e3744195351130f4ce7dbe0613f4b3e23df93 (patch)
treebac2ad39971cd43f31241ef123bd4e470f695ac9 /lib
Initial revision
Diffstat (limited to 'lib')
-rw-r--r--lib/.cvsignore4
-rw-r--r--lib/ChangeLog1926
-rw-r--r--lib/Makefile.am29
-rw-r--r--lib/Makefile.in469
-rw-r--r--lib/buffer.c568
-rw-r--r--lib/buffer.h77
-rw-r--r--lib/checksum.c47
-rw-r--r--lib/command.c2981
-rw-r--r--lib/command.h308
-rw-r--r--lib/daemon.c80
-rw-r--r--lib/distribute.c709
-rw-r--r--lib/distribute.h57
-rw-r--r--lib/filter.c2058
-rw-r--r--lib/filter.h67
-rw-r--r--lib/getopt.c1054
-rw-r--r--lib/getopt.h133
-rw-r--r--lib/getopt1.c190
-rw-r--r--lib/hash.c182
-rw-r--r--lib/hash.h71
-rw-r--r--lib/if.c713
-rw-r--r--lib/if.h222
-rw-r--r--lib/if_rmap.c305
-rw-r--r--lib/if_rmap.h47
-rw-r--r--lib/keychain.c1001
-rw-r--r--lib/keychain.h56
-rw-r--r--lib/linklist.c312
-rw-r--r--lib/linklist.h101
-rw-r--r--lib/log.c483
-rw-r--r--lib/log.h128
-rw-r--r--lib/md5-gnu.h156
-rw-r--r--lib/md5.c447
-rw-r--r--lib/memory.c493
-rw-r--r--lib/memory.h245
-rw-r--r--lib/network.c71
-rw-r--r--lib/network.h29
-rw-r--r--lib/pid_output.c77
-rw-r--r--lib/plist.c2881
-rw-r--r--lib/plist.h78
-rw-r--r--lib/prefix.c696
-rw-r--r--lib/prefix.h161
-rw-r--r--lib/print_version.c31
-rw-r--r--lib/regex-gnu.h542
-rw-r--r--lib/regex.c5891
-rw-r--r--lib/routemap.c1077
-rw-r--r--lib/routemap.h194
-rw-r--r--lib/smux.c1501
-rw-r--r--lib/smux.h159
-rw-r--r--lib/sockopt.c199
-rw-r--r--lib/sockopt.h41
-rw-r--r--lib/sockunion.c756
-rw-r--r--lib/sockunion.h128
-rw-r--r--lib/str.c62
-rw-r--r--lib/str.h24
-rw-r--r--lib/stream.c479
-rw-r--r--lib/stream.h113
-rw-r--r--lib/table.c503
-rw-r--r--lib/table.h74
-rw-r--r--lib/tcpfilter.c69
-rw-r--r--lib/tcpfilter.h21
-rw-r--r--lib/thread.c668
-rw-r--r--lib/thread.h139
-rw-r--r--lib/vector.c189
-rw-r--r--lib/vector.h58
-rw-r--r--lib/version.h39
-rw-r--r--lib/vty.c2792
-rw-r--r--lib/vty.h205
-rw-r--r--lib/zclient.c901
-rw-r--r--lib/zclient.h164
-rw-r--r--lib/zebra.h312
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, &copy_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 (&reg_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 ? &regs : (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 */