summaryrefslogtreecommitdiff
path: root/release/src/router/busybox/networking
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2015-01-03 12:04:58 +0100
committerAndreas Baumann <mail@andreasbaumann.cc>2015-01-03 12:04:58 +0100
commit008d0be72b2f160382c6e880765e96b64a050c65 (patch)
tree36f48a98a3815a408e2ce1693dd182af90f80305 /release/src/router/busybox/networking
parent611becfb8726c60cb060368541ad98191d4532f5 (diff)
downloadtomato-008d0be72b2f160382c6e880765e96b64a050c65.tar.gz
tomato-008d0be72b2f160382c6e880765e96b64a050c65.tar.bz2
imported original firmware WRT54GL_v4.30.11_11_US
Diffstat (limited to 'release/src/router/busybox/networking')
-rwxr-xr-xrelease/src/router/busybox/networking/Config.in573
-rw-r--r--release/src/router/busybox/networking/Makefile30
-rwxr-xr-xrelease/src/router/busybox/networking/Makefile.in65
-rw-r--r--release/src/router/busybox/networking/arping.c499
-rw-r--r--release/src/router/busybox/networking/ftpgetput.c369
-rw-r--r--release/src/router/busybox/networking/hostname.c130
-rw-r--r--release/src/router/busybox/networking/httpd.c1915
-rw-r--r--release/src/router/busybox/networking/ifconfig.c598
-rw-r--r--release/src/router/busybox/networking/ifupdown.c1453
-rw-r--r--release/src/router/busybox/networking/inetd.c1302
-rw-r--r--release/src/router/busybox/networking/ip.c115
-rw-r--r--release/src/router/busybox/networking/ipaddr.c27
-rw-r--r--release/src/router/busybox/networking/ipcalc.c150
-rw-r--r--release/src/router/busybox/networking/iplink.c27
-rw-r--r--release/src/router/busybox/networking/iproute.c27
-rw-r--r--release/src/router/busybox/networking/iptunnel.c27
-rw-r--r--release/src/router/busybox/networking/libiproute/Makefile30
-rwxr-xr-xrelease/src/router/busybox/networking/libiproute/Makefile.in44
-rw-r--r--release/src/router/busybox/networking/libiproute/ip_common.h18
-rw-r--r--release/src/router/busybox/networking/libiproute/ip_parse_common_args.c74
-rw-r--r--release/src/router/busybox/networking/libiproute/ipaddress.c825
-rw-r--r--release/src/router/busybox/networking/libiproute/iplink.c367
-rw-r--r--release/src/router/busybox/networking/libiproute/iproute.c852
-rw-r--r--release/src/router/busybox/networking/libiproute/iptunnel.c548
-rw-r--r--release/src/router/busybox/networking/libiproute/libnetlink.c522
-rw-r--r--release/src/router/busybox/networking/libiproute/libnetlink.h46
-rw-r--r--release/src/router/busybox/networking/libiproute/linux/pkt_sched.h413
-rw-r--r--release/src/router/busybox/networking/libiproute/ll_addr.c81
-rw-r--r--release/src/router/busybox/networking/libiproute/ll_map.c164
-rw-r--r--release/src/router/busybox/networking/libiproute/ll_map.h12
-rw-r--r--release/src/router/busybox/networking/libiproute/ll_proto.c120
-rw-r--r--release/src/router/busybox/networking/libiproute/ll_types.c115
-rw-r--r--release/src/router/busybox/networking/libiproute/rt_names.c385
-rw-r--r--release/src/router/busybox/networking/libiproute/rt_names.h30
-rw-r--r--release/src/router/busybox/networking/libiproute/rtm_map.c110
-rw-r--r--release/src/router/busybox/networking/libiproute/rtm_map.h10
-rw-r--r--release/src/router/busybox/networking/libiproute/utils.c359
-rw-r--r--release/src/router/busybox/networking/libiproute/utils.h101
-rw-r--r--release/src/router/busybox/networking/nameif.c222
-rw-r--r--release/src/router/busybox/networking/nc.c177
-rw-r--r--release/src/router/busybox/networking/netstat.c661
-rw-r--r--release/src/router/busybox/networking/nslookup.c202
-rw-r--r--release/src/router/busybox/networking/ping.c472
-rw-r--r--release/src/router/busybox/networking/ping6.c515
-rw-r--r--release/src/router/busybox/networking/route.c705
-rw-r--r--release/src/router/busybox/networking/telnet.c692
-rw-r--r--release/src/router/busybox/networking/telnetd.c628
-rw-r--r--release/src/router/busybox/networking/tftp.c587
-rw-r--r--release/src/router/busybox/networking/traceroute.c548
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/AUTHORS14
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/COPYING339
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/ChangeLog262
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/Config.in62
-rw-r--r--release/src/router/busybox/networking/udhcp/Makefile30
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/Makefile.in61
-rw-r--r--release/src/router/busybox/networking/udhcp/README50
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/README.dumpleases17
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/README.udhcpc141
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/README.udhcpd59
-rwxr-xr-xrelease/src/router/busybox/networking/udhcp/TODO14
-rw-r--r--release/src/router/busybox/networking/udhcp/arpping.c102
-rw-r--r--release/src/router/busybox/networking/udhcp/arpping.h30
-rw-r--r--release/src/router/busybox/networking/udhcp/clientpacket.c247
-rw-r--r--release/src/router/busybox/networking/udhcp/clientpacket.h12
-rw-r--r--release/src/router/busybox/networking/udhcp/common.c150
-rw-r--r--release/src/router/busybox/networking/udhcp/common.h52
-rw-r--r--release/src/router/busybox/networking/udhcp/debug.h41
-rw-r--r--release/src/router/busybox/networking/udhcp/dhcpc.c492
-rw-r--r--release/src/router/busybox/networking/udhcp/dhcpc.h32
-rw-r--r--release/src/router/busybox/networking/udhcp/dhcpd.c253
-rw-r--r--release/src/router/busybox/networking/udhcp/dhcpd.h130
-rw-r--r--release/src/router/busybox/networking/udhcp/dumpleases.c88
-rw-r--r--release/src/router/busybox/networking/udhcp/files.c287
-rw-r--r--release/src/router/busybox/networking/udhcp/files.h17
-rw-r--r--release/src/router/busybox/networking/udhcp/frontend.c16
-rw-r--r--release/src/router/busybox/networking/udhcp/leases.c149
-rw-r--r--release/src/router/busybox/networking/udhcp/leases.h25
-rw-r--r--release/src/router/busybox/networking/udhcp/leases_file.c1
-rw-r--r--release/src/router/busybox/networking/udhcp/libbb_udhcp.h29
-rw-r--r--release/src/router/busybox/networking/udhcp/options.c228
-rw-r--r--release/src/router/busybox/networking/udhcp/options.h40
-rw-r--r--release/src/router/busybox/networking/udhcp/packet.c201
-rw-r--r--release/src/router/busybox/networking/udhcp/packet.h41
-rw-r--r--release/src/router/busybox/networking/udhcp/pidfile.c69
-rw-r--r--release/src/router/busybox/networking/udhcp/pidfile.h26
-rw-r--r--release/src/router/busybox/networking/udhcp/script.c243
-rw-r--r--release/src/router/busybox/networking/udhcp/script.h6
-rw-r--r--release/src/router/busybox/networking/udhcp/serverpacket.c258
-rw-r--r--release/src/router/busybox/networking/udhcp/serverpacket.h11
-rw-r--r--release/src/router/busybox/networking/udhcp/socket.c155
-rw-r--r--release/src/router/busybox/networking/udhcp/socket.h9
-rw-r--r--release/src/router/busybox/networking/udhcp/version.h6
-rw-r--r--release/src/router/busybox/networking/vconfig.c150
-rw-r--r--release/src/router/busybox/networking/wget.c828
94 files changed, 23415 insertions, 0 deletions
diff --git a/release/src/router/busybox/networking/Config.in b/release/src/router/busybox/networking/Config.in
new file mode 100755
index 00000000..42f8a79c
--- /dev/null
+++ b/release/src/router/busybox/networking/Config.in
@@ -0,0 +1,573 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Networking Utilities"
+
+config CONFIG_FEATURE_IPV6
+ bool "Enable IPv6 support"
+ default n
+ help
+ Enable IPv6 support to busybox. This makes applets that talk IP
+ able to work with IPv6.
+
+config CONFIG_ARPING
+ bool "arping"
+ default n
+ help
+ Ping hosts by ARP packets
+
+config CONFIG_FTPGET
+ bool "ftpget"
+ default n
+ help
+ Retrieve a remote file via FTP.
+
+config CONFIG_FTPPUT
+ bool "ftpput"
+ default n
+ help
+ Store a remote file via FTP.
+
+config CONFIG_HOSTNAME
+ bool "hostname"
+ default n
+ help
+ Show or set the system's host name
+
+config CONFIG_HTTPD
+ bool "httpd"
+ default n
+ help
+ Serve web pages via an HTTP server.
+
+config CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ bool " Support using httpd only from inetd"
+ default n
+ depends on CONFIG_HTTPD
+ help
+ This option disables uid and port options for the httpd applet
+ but requires inetd server daemon.
+
+config CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ bool " Enable Basic http Authentication"
+ default y
+ depends on CONFIG_HTTPD
+ help
+ Utilizes password settings from /etc/httpd.conf for basic
+ authentication on a per url basis.
+
+config CONFIG_FEATURE_HTTPD_AUTH_MD5
+ bool " Support MD5 crypted passwords for http Authentication"
+ default n
+ depends on CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ help
+ Enables basic per url authentication from /etc/httpd.conf
+ using md5 passwords.
+
+
+if !CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+config CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+ bool " Support reloading the global config file using hup signal"
+ default n
+ depends on CONFIG_HTTPD
+ help
+ This option enables processing of SIGHUP to reload cached
+ configuration settings.
+
+config CONFIG_FEATURE_HTTPD_SETUID
+ bool " Enable support -u <user> option"
+ default n
+ depends on CONFIG_HTTPD
+ help
+ This option allows the server to run as a specific user
+ rather than defaulting to the user that starts the server.
+ Use of this option requires special privilegies to change to a
+ different user.
+endif
+
+config CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ bool " Support loading additional mime types at run-time"
+ default n
+ depends on CONFIG_HTTPD
+ help
+ This option enables support for additional mime types at
+ run-time to be specified in the configuration file.
+
+config CONFIG_FEATURE_HTTPD_CGI
+ bool " Support Common Gateway Interface (CGI)"
+ default y
+ depends on CONFIG_HTTPD
+ help
+ This option allows scripts and executables to be invoked
+ when specific urls are requested.
+
+config CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+ bool " Support the REMOTE_PORT environment variable for CGI"
+ default n
+ depends on CONFIG_FEATURE_HTTPD_CGI
+ help
+ Use of this option can assist scripts in generating
+ references that contain a unique port number.
+
+config CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
+ bool " Enable setting of CGI_varname=value environment vars for CGI"
+ default n
+ depends on CONFIG_FEATURE_HTTPD_CGI
+ help
+ This option parses POST or GET arguments from a form and
+ sets environment variables with their value. This simplifies
+ and speeds up CGI scripts. A form argument of foo=bar would
+ result in a script having the environment variable CGI_foo set
+ to 'bar'. In addition, this option sets a variable that
+ lists all the argument names. e.g. CGI_VARNAMES_="name1 name2".
+
+config CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+ bool " Enable the -e option for shell script CGI simplification."
+ default y
+ depends on CONFIG_HTTPD
+ help
+ After set, this option allows html encoding arbitrary
+ strings for display of the browser. Output goes to stdout.
+ For example, httpd -e "<Hello World>" as
+ "&#60Hello&#32World&#62".
+
+config CONFIG_IFCONFIG
+ bool "ifconfig"
+ default n
+ help
+ Ifconfig is used to configure the kernel-resident network interfaces.
+
+config CONFIG_FEATURE_IFCONFIG_STATUS
+ bool " Enable status reporting output (+7k)"
+ default y
+ depends on CONFIG_IFCONFIG
+ help
+ If ifconfig is called with no arguments it will display the status
+ of the currently active interfaces.
+
+config CONFIG_FEATURE_IFCONFIG_SLIP
+ bool " Enable slip-specific options \"keepalive\" and \"outfill\""
+ default n
+ depends on CONFIG_IFCONFIG
+ help
+ Allow "keepalive" and "outfill" support for SLIP. If you're not
+ planning on using serial lines, leave this unchecked.
+
+config CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ bool " Enable options \"mem_start\", \"io_addr\", and \"irq\""
+ default n
+ depends on CONFIG_IFCONFIG
+ help
+ Allow the start address for shared memory, start address for I/O,
+ and/or the interrupt line used by the specified device.
+
+config CONFIG_FEATURE_IFCONFIG_HW
+ bool " Enable option \"hw\" (ether only)"
+ default y
+ depends on CONFIG_IFCONFIG
+ help
+ Set the hardware address of this interface, if the device driver
+ supports this operation. Currently, we only support the 'ether'
+ class.
+
+config CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+ bool " Set the broadcast automatically"
+ default n
+ depends on CONFIG_IFCONFIG
+ help
+ Setting this will make ifconfig attempt to find the broadcast
+ automatically if the value '+' is used.
+
+config CONFIG_IFUPDOWN
+ bool "ifupdown"
+ default n
+ help
+ Activate or deactivate the specified interfaces. This applet makes
+ use of either "ifconfig" and "route" or the "ip" command to actually
+ configure network interfaces. Therefore, you will probably also want
+ to enable either CONFIG_IFCONFIG and CONFIG_ROUTE, or enable
+ CONFIG_FEATURE_IFUPDOWN_IP and the various CONFIG_IP options. Of
+ course you could use non-busybox versions of these programs, so
+ against my better judgement (since this will surely result in plenty
+ of support questions on the mailing list), I do not force you to
+ enable these additional options. It is up to you to supply either
+ "ifconfig" and "route" or the "ip" command, either via busybox or via
+ standalone utilities.
+
+# I really should force these to be enabled
+# && CONFIG_IP && CONFIG_FEATURE_IP_ADDRESS && CONFIG_FEATURE_IP_LINK && CONFIG_FEATURE_IP_ROUTE
+# but then people could not use the full-blown iproute2 program...
+config CONFIG_FEATURE_IFUPDOWN_IP
+ bool " Use ip applet"
+ default n
+ depends on CONFIG_IFUPDOWN
+ help
+ Use the iproute "ip" command to implement "ifupdown". You will
+ probably want to also enable CONFIG_IP, CONFIG_FEATURE_IP_ADDRESS,
+ CONFIG_FEATURE_IP_LINK, and CONFIG_FEATURE_IP_ROUTE. Of course
+ if you wanted to use the full-blown iproute2 program you could
+ leave the the busybox CONFIG_IP* options disabled.
+
+config CONFIG_FEATURE_IFUPDOWN_IPV4
+ bool " Enable support for IPv4"
+ default y
+ depends on CONFIG_IFUPDOWN
+ help
+ If you want busybox to talk IPv4, leave this on.
+
+config CONFIG_FEATURE_IFUPDOWN_IPV6
+ bool " Enable support for IPv6"
+ default n
+ depends on CONFIG_IFUPDOWN
+ help
+ If you need support for IPv6, turn this option on.
+
+config CONFIG_FEATURE_IFUPDOWN_IPX
+ bool " Enable support for IPX"
+ default n
+ depends on CONFIG_IFUPDOWN
+ help
+ If this option is selected you can use busybox to work with IPX
+ networks.
+
+config CONFIG_FEATURE_IFUPDOWN_MAPPING
+ bool " Enable mapping support"
+ default n
+ depends on CONFIG_IFUPDOWN
+ help
+ This enables support for the "mapping" stanza, unless you have
+ a weird network setup you dont need it.
+
+config CONFIG_INETD
+ bool "inetd"
+ default n
+ help
+ Internet superserver daemon
+
+config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
+ bool " Support echo service"
+ default y
+ depends on CONFIG_INETD
+ help
+ Echo received data internal inetd service
+
+config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
+ bool " Support discard service"
+ default y
+ depends on CONFIG_INETD
+ help
+ Internet /dev/null internal inetd service
+
+config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
+ bool " Support time service"
+ default y
+ depends on CONFIG_INETD
+ help
+ Return 32 bit time since 1900 internal inetd service
+
+config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
+ bool " Support daytime service"
+ default y
+ depends on CONFIG_INETD
+ help
+ Return human-readable time internal inetd service
+
+config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
+ bool " Support chargen service"
+ default y
+ depends on CONFIG_INETD
+ help
+ Familiar character generator internal inetd service
+
+
+config CONFIG_IP
+ bool "ip"
+ default n
+ help
+ The "ip" applet is a TCP/IP interface configuration and routing
+ utility. You generally don't need "ip" to use busybox with
+ TCP/IP.
+
+if CONFIG_IP && CONFIG_IPADDR
+ config CONFIG_FEATURE_IP_ADDRESS
+ default y
+ comment " address (forced enabled for ipaddr)"
+endif
+if ! (CONFIG_IP && CONFIG_IPADDR)
+ config CONFIG_FEATURE_IP_ADDRESS
+ bool " address"
+ default y
+ depends on CONFIG_IP
+ help
+ Address manipulation support for the "ip" applet.
+endif
+
+if CONFIG_IP && CONFIG_IPLINK
+ config CONFIG_FEATURE_IP_LINK
+ default y
+ comment " link (forced enabled for iplink)"
+endif
+if !(CONFIG_IP && CONFIG_IPLINK)
+ config CONFIG_FEATURE_IP_LINK
+ bool " link"
+ default y
+ depends on CONFIG_IP
+ help
+ Configure network devices with "ip".
+endif
+
+if CONFIG_IP && CONFIG_IPROUTE
+ config CONFIG_FEATURE_IP_ROUTE
+ default y
+ comment " route (forced enabled for iproute)"
+endif
+if !(CONFIG_IP && CONFIG_IPROUTE)
+ config CONFIG_FEATURE_IP_ROUTE
+ bool " route"
+ default y
+ depends on CONFIG_IP
+ help
+ Add support for routing table management to "ip".
+endif
+
+if CONFIG_IP && CONFIG_IPTUNNEL
+ config CONFIG_FEATURE_IP_TUNNEL
+ default y
+ comment " tunnel (forced enabled for iptunnel)"
+endif
+if !(CONFIG_IP && CONFIG_IPTUNNEL)
+ config CONFIG_FEATURE_IP_TUNNEL
+ bool " tunnel"
+ default n
+ depends on CONFIG_IP
+ help
+ Add support for tunneling commands to "ip".
+endif
+
+config CONFIG_IPCALC
+ bool "ipcalc"
+ default n
+ help
+ ipcalc takes an IP address and netmask and calculates the
+ resulting broadcast, network, and host range.
+
+config CONFIG_FEATURE_IPCALC_FANCY
+ bool " Fancy IPCALC, more options, adds 300 bytes"
+ default y
+ depends on CONFIG_IPCALC
+ help
+ Adds the fields hostname and silent to the output of "ipcalc".
+
+config CONFIG_IPADDR
+ bool "ipaddr"
+ default n
+ help
+ Equivalent to selecting address support to "ip", above.
+
+config CONFIG_IPLINK
+ bool "iplink"
+ default n
+ help
+ Equivalent to selecting link support to "ip", above.
+
+config CONFIG_IPROUTE
+ bool "iproute"
+ default n
+ help
+ Equivalent to selecting route support to "ip", above.
+
+config CONFIG_IPTUNNEL
+ bool "iptunnel"
+ default n
+ help
+ Equivalent to selecting tunnel support to "ip", above.
+
+config CONFIG_NAMEIF
+ bool "nameif"
+ default n
+ help
+ nameif used to rename network interface by its MAC address.
+ Renamed interfaces MUST be in the down state.
+ It is possible to use file (default: /etc/mactab)
+ with list of new interface names and MACs.
+ Maximum interface name length: IF_NAMESIZE = 16
+ File fields are sepatated by space or tab.
+ File format:
+ # Comment
+ new_interface_name XX:XX:XX:XX:XX:XX
+
+config CONFIG_NC
+ bool "nc"
+ default n
+ help
+ A simple Unix utility which reads and writes data across network
+ connections.
+
+config CONFIG_NETSTAT
+ bool "netstat"
+ default n
+ help
+ Netstat prints information about the Linux networking subsystem.
+
+config CONFIG_NSLOOKUP
+ bool "nslookup"
+ default n
+ help
+ Nslookup is a tool to query Internet name servers.
+
+config CONFIG_PING
+ bool "ping"
+ default n
+ help
+ Ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
+ elicit an ICMP ECHO_RESPONSE from a host or gateway.
+
+config CONFIG_FEATURE_FANCY_PING
+ bool " Enable fancy ping output"
+ default y
+ depends on CONFIG_PING
+ help
+ Make the output from the ping applet include statistics, and at the
+ same time provide full support for ICMP packets.
+
+config CONFIG_PING6
+ bool "ping6"
+ default n
+ depends on CONFIG_FEATURE_IPV6
+ help
+ This will give you a ping that can talk IPv6.
+
+config CONFIG_FEATURE_FANCY_PING6
+ bool " Enable fancy ping6 output"
+ default y
+ depends on CONFIG_PING6
+ help
+ Make the output from the ping6 applet include statistics, and at the
+ same time provide full support for ICMP packets.
+
+config CONFIG_ROUTE
+ bool "route"
+ default n
+ help
+ Route displays or manipulates the kernel's IP routing tables.
+
+config CONFIG_TELNET
+ bool "telnet"
+ default n
+ help
+ Telnet is an interface to the TELNET protocol, but is also commonly
+ used to test other simple protocols.
+
+config CONFIG_FEATURE_TELNET_TTYPE
+ bool " Pass TERM type to remote host"
+ default y
+ depends on CONFIG_TELNET
+ help
+ Setting this option will forward the TERM environment variable to the
+ remote host you are connecting to. This is useful to make sure that
+ things like ANSI colors and other control sequences behave.
+
+config CONFIG_TELNETD
+ bool "telnetd"
+ default n
+ help
+ A daemon for the TELNET protocol, allowing you to log on to the host
+ running the daemon. Please keep in mind that the TELNET protocol
+ sends passwords in plain text. If you can't afford the space for
+ any SSH daemon and you trust your network, say 'y' here.
+
+config CONFIG_FEATURE_TELNETD_INETD
+ bool " Support call from inetd only"
+ default n
+ depends on CONFIG_TELNETD
+ help
+ Selecting this will make telnetd only callable from inetd, removing the
+ standalone support.
+
+config CONFIG_TFTP
+ bool "tftp"
+ default n
+ help
+ This enables the Tirvial File Transfer Protocol client program. TFTP
+ is usually used for simple, small transfers such as a root image
+ for a network-enabled bootloader.
+
+config CONFIG_FEATURE_TFTP_GET
+ bool " Enable \"get\" command"
+ default y
+ depends on CONFIG_TFTP
+ help
+ Add support for the GET command within the TFTP client. This allows
+ a client to retreive a file from a TFTP server.
+
+config CONFIG_FEATURE_TFTP_PUT
+ bool " Enable \"put\" command"
+ default y
+ depends on CONFIG_TFTP
+ help
+ Add support for the PUT command within the TFTP client. This allows
+ a client to transfer a file to a TFTP server.
+
+config CONFIG_FEATURE_TFTP_BLOCKSIZE
+ bool " Enable \"blocksize\" command"
+ default n
+ depends on CONFIG_TFTP
+ help
+ Allow the client to specify the desired block size for transfers.
+
+config CONFIG_FEATURE_TFTP_DEBUG
+ bool " Enable debug"
+ default n
+ depends on CONFIG_TFTP
+ help
+ Enable debug settings for tftp. This is useful if you're running
+ into problems with tftp as the protocol doesn't help you much when
+ you run into problems.
+
+config CONFIG_TRACEROUTE
+ bool "traceroute"
+ default n
+ help
+ Utility to trace the route of IP packets
+
+config CONFIG_FEATURE_TRACEROUTE_VERBOSE
+ bool " Enable verbose output"
+ default n
+ depends on CONFIG_TRACEROUTE
+ help
+ Add some verbosity to traceroute. This includes amongst other things
+ hostnames and ICMP response types.
+
+config CONFIG_VCONFIG
+ bool "vconfig"
+ default n
+ help
+ Creates, removes, and configures VLAN interfaces
+
+config CONFIG_WGET
+ bool "wget"
+ default n
+ help
+ Wget is a utility for non-interactive download of files from HTTP,
+ HTTPS, and FTP servers.
+
+config CONFIG_FEATURE_WGET_STATUSBAR
+ bool " Enable a nifty process meter (+2k)"
+ default y
+ depends on CONFIG_WGET
+ help
+ Enable the transfer progress bar for wget transfers.
+
+config CONFIG_FEATURE_WGET_AUTHENTICATION
+ bool " Enable HTTP authentication"
+ default y
+ depends on CONFIG_WGET
+ help
+ Support authenticated HTTP transfers.
+
+source networking/udhcp/Config.in
+
+endmenu
+
diff --git a/release/src/router/busybox/networking/Makefile b/release/src/router/busybox/networking/Makefile
new file mode 100644
index 00000000..022f29e4
--- /dev/null
+++ b/release/src/router/busybox/networking/Makefile
@@ -0,0 +1,30 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.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 of the License, 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
+#
+
+TOPDIR:= ../
+NETWORKING_DIR:=./
+include $(TOPDIR).config
+include $(TOPDIR)Rules.mak
+include Makefile.in
+all: $(libraries-y)
+-include $(TOPDIR).depend
+
+clean:
+ rm -f *.o *.a $(AR_TARGET)
+
diff --git a/release/src/router/busybox/networking/Makefile.in b/release/src/router/busybox/networking/Makefile.in
new file mode 100755
index 00000000..7748d066
--- /dev/null
+++ b/release/src/router/busybox/networking/Makefile.in
@@ -0,0 +1,65 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.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 of the License, 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
+#
+
+NETWORKING_AR:=networking.a
+ifndef $(NETWORKING_DIR)
+NETWORKING_DIR:=$(TOPDIR)networking/
+endif
+
+NETWORKING-y:=
+NETWORKING-$(CONFIG_ARPING) += arping.o
+NETWORKING-$(CONFIG_FTPGET) += ftpgetput.o
+NETWORKING-$(CONFIG_FTPPUT) += ftpgetput.o
+NETWORKING-$(CONFIG_HOSTNAME) += hostname.o
+NETWORKING-$(CONFIG_HTTPD) += httpd.o
+NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o
+NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o
+NETWORKING-$(CONFIG_INETD) += inetd.o
+NETWORKING-$(CONFIG_IP) += ip.o
+NETWORKING-$(CONFIG_IPCALC) += ipcalc.o
+NETWORKING-$(CONFIG_IPADDR) += ipaddr.o
+NETWORKING-$(CONFIG_IPLINK) += iplink.o
+NETWORKING-$(CONFIG_IPROUTE) += iproute.o
+NETWORKING-$(CONFIG_IPTUNNEL) += iptunnel.o
+NETWORKING-$(CONFIG_NAMEIF) += nameif.o
+NETWORKING-$(CONFIG_NC) += nc.o
+NETWORKING-$(CONFIG_NETSTAT) += netstat.o
+NETWORKING-$(CONFIG_NSLOOKUP) += nslookup.o
+NETWORKING-$(CONFIG_PING) += ping.o
+NETWORKING-$(CONFIG_PING6) += ping6.o
+NETWORKING-$(CONFIG_ROUTE) += route.o
+NETWORKING-$(CONFIG_TELNET) += telnet.o
+NETWORKING-$(CONFIG_TELNETD) += telnetd.o
+NETWORKING-$(CONFIG_TFTP) += tftp.o
+NETWORKING-$(CONFIG_TRACEROUTE) += traceroute.o
+NETWORKING-$(CONFIG_VCONFIG) += vconfig.o
+NETWORKING-$(CONFIG_WGET) += wget.o
+
+libraries-y+=$(NETWORKING_DIR)$(NETWORKING_AR)
+
+needcrypt-y:=
+needcrypt-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) := y
+
+ifeq ($(needcrypt-y),y)
+ LIBRARIES += -lcrypt
+endif
+
+$(NETWORKING_DIR)$(NETWORKING_AR): $(patsubst %,$(NETWORKING_DIR)%, $(NETWORKING-y))
+ $(AR) -ro $@ $(patsubst %,$(NETWORKING_DIR)%, $(NETWORKING-y))
+
diff --git a/release/src/router/busybox/networking/arping.c b/release/src/router/busybox/networking/arping.c
new file mode 100644
index 00000000..2e1adf0a
--- /dev/null
+++ b/release/src/router/busybox/networking/arping.c
@@ -0,0 +1,499 @@
+/*
+ * arping.c - Ping hosts by ARP requests/replies
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ * Busybox port: Nick Fedchik <nick@fedchik.org.ua>
+ */
+
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+
+#include "busybox.h"
+
+#define APPLET_NAME "arping"
+
+struct in_addr src;
+struct in_addr dst;
+struct sockaddr_ll me;
+struct sockaddr_ll he;
+struct timeval last;
+int dad;
+int unsolicited;
+int advert;
+int quiet;
+int quit_on_reply = 0;
+int count = -1;
+int timeout;
+int unicasting;
+int s;
+int broadcast_only;
+int sent;
+int brd_sent;
+int received;
+int brd_recv;
+int req_recv;
+
+#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \
+ ((tv1).tv_usec-(tv2).tv_usec)/1000 )
+#if 0
+static void set_signal(int signo, void (*handler) (void))
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = (void (*)(int)) handler;
+ sa.sa_flags = SA_RESTART;
+ sigaction(signo, &sa, NULL);
+}
+#endif
+
+static int send_pack(int sock, struct in_addr *src_addr,
+ struct in_addr *dst_addr, struct sockaddr_ll *ME,
+ struct sockaddr_ll *HE)
+{
+ int err;
+ struct timeval now;
+ unsigned char buf[256];
+ struct arphdr *ah = (struct arphdr *) buf;
+ unsigned char *p = (unsigned char *) (ah + 1);
+
+ ah->ar_hrd = htons(ME->sll_hatype);
+ ah->ar_hrd = htons(ARPHRD_ETHER);
+ ah->ar_pro = htons(ETH_P_IP);
+ ah->ar_hln = ME->sll_halen;
+ ah->ar_pln = 4;
+ ah->ar_op = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
+
+ memcpy(p, &ME->sll_addr, ah->ar_hln);
+ p += ME->sll_halen;
+
+ memcpy(p, src_addr, 4);
+ p += 4;
+
+ if (advert)
+ memcpy(p, &ME->sll_addr, ah->ar_hln);
+ else
+ memcpy(p, &HE->sll_addr, ah->ar_hln);
+ p += ah->ar_hln;
+
+ memcpy(p, dst_addr, 4);
+ p += 4;
+
+ gettimeofday(&now, NULL);
+ err = sendto(sock, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
+ if (err == p - buf) {
+ last = now;
+ sent++;
+ if (!unicasting)
+ brd_sent++;
+ }
+ return err;
+}
+
+void finish(void)
+{
+ if (!quiet) {
+ printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent);
+ printf("Received %d repl%s", received, (received > 1) ? "ies" : "y");
+ if (brd_recv || req_recv) {
+ printf(" (");
+ if (req_recv)
+ printf("%d request(s)", req_recv);
+ if (brd_recv)
+ printf("%s%d broadcast(s)", req_recv ? ", " : "", brd_recv);
+ putchar(')');
+ }
+ putchar('\n');
+ fflush(stdout);
+ }
+ if (dad)
+ exit(!!received);
+ if (unsolicited)
+ exit(0);
+ exit(!received);
+}
+
+void catcher(void)
+{
+ struct timeval tv;
+ struct timeval start;
+
+ gettimeofday(&tv, NULL);
+
+ if (start.tv_sec == 0)
+ start = tv;
+
+ if (count-- == 0
+ || (timeout && MS_TDIFF(tv, start) > timeout * 1000 + 500))
+ finish();
+
+ if (last.tv_sec == 0 || MS_TDIFF(tv, last) > 500) {
+ send_pack(s, &src, &dst, &me, &he);
+ if (count == 0 && unsolicited)
+ finish();
+ }
+ alarm(1);
+}
+
+int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
+{
+ struct timeval tv;
+ struct arphdr *ah = (struct arphdr *) buf;
+ unsigned char *p = (unsigned char *) (ah + 1);
+ struct in_addr src_ip, dst_ip;
+
+ gettimeofday(&tv, NULL);
+
+ /* Filter out wild packets */
+ if (FROM->sll_pkttype != PACKET_HOST &&
+ FROM->sll_pkttype != PACKET_BROADCAST &&
+ FROM->sll_pkttype != PACKET_MULTICAST)
+ return 0;
+
+ /* Only these types are recognised */
+ if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
+ return 0;
+
+ /* ARPHRD check and this darned FDDI hack here :-( */
+ if (ah->ar_hrd != htons(FROM->sll_hatype) &&
+ (FROM->sll_hatype != ARPHRD_FDDI
+ || ah->ar_hrd != htons(ARPHRD_ETHER)))
+ return 0;
+
+ /* Protocol must be IP. */
+ if (ah->ar_pro != htons(ETH_P_IP))
+ return 0;
+ if (ah->ar_pln != 4)
+ return 0;
+ if (ah->ar_hln != me.sll_halen)
+ return 0;
+ if (len < sizeof(*ah) + 2 * (4 + ah->ar_hln))
+ return 0;
+ memcpy(&src_ip, p + ah->ar_hln, 4);
+ memcpy(&dst_ip, p + ah->ar_hln + 4 + ah->ar_hln, 4);
+ if (!dad) {
+ if (src_ip.s_addr != dst.s_addr)
+ return 0;
+ if (src.s_addr != dst_ip.s_addr)
+ return 0;
+ if (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln))
+ return 0;
+ } else {
+ /* DAD packet was:
+ src_ip = 0 (or some src)
+ src_hw = ME
+ dst_ip = tested address
+ dst_hw = <unspec>
+
+ We fail, if receive request/reply with:
+ src_ip = tested_address
+ src_hw != ME
+ if src_ip in request was not zero, check
+ also that it matches to dst_ip, otherwise
+ dst_ip/dst_hw do not matter.
+ */
+ if (src_ip.s_addr != dst.s_addr)
+ return 0;
+ if (memcmp(p, &me.sll_addr, me.sll_halen) == 0)
+ return 0;
+ if (src.s_addr && src.s_addr != dst_ip.s_addr)
+ return 0;
+ }
+ if (!quiet) {
+ int s_printed = 0;
+
+ printf("%s ",
+ FROM->sll_pkttype == PACKET_HOST ? "Unicast" : "Broadcast");
+ printf("%s from ",
+ ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request");
+ printf("%s ", inet_ntoa(src_ip));
+ printf("[%s]", ether_ntoa((struct ether_addr *) p));
+ if (dst_ip.s_addr != src.s_addr) {
+ printf("for %s ", inet_ntoa(dst_ip));
+ s_printed = 1;
+ }
+ if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
+ if (!s_printed)
+ printf("for ");
+ printf("[%s]",
+ ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
+ }
+ if (last.tv_sec) {
+ long usecs = (tv.tv_sec - last.tv_sec) * 1000000 +
+ tv.tv_usec - last.tv_usec;
+ long msecs = (usecs + 500) / 1000;
+
+ usecs -= msecs * 1000 - 500;
+ printf(" %ld.%03ldms\n", msecs, usecs);
+ } else {
+ printf(" UNSOLICITED?\n");
+ }
+ fflush(stdout);
+ }
+ received++;
+ if (FROM->sll_pkttype != PACKET_HOST)
+ brd_recv++;
+ if (ah->ar_op == htons(ARPOP_REQUEST))
+ req_recv++;
+ if (quit_on_reply)
+ finish();
+ if (!broadcast_only) {
+ memcpy(he.sll_addr, p, me.sll_halen);
+ unicasting = 1;
+ }
+ return 1;
+}
+
+int arping_main(int argc, char **argv)
+{
+ int socket_errno;
+ int ch;
+ uid_t uid = getuid();
+ char *device = "eth0";
+ int ifindex = 0;
+ char *source = NULL;
+ char *target;
+
+ s = socket(PF_PACKET, SOCK_DGRAM, 0);
+ socket_errno = errno;
+
+ setuid(uid);
+
+ while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:")) != EOF) {
+ switch (ch) {
+ case 'b':
+ broadcast_only = 1;
+ break;
+ case 'D':
+ dad++;
+ quit_on_reply = 1;
+ break;
+ case 'U':
+ unsolicited++;
+ break;
+ case 'A':
+ advert++;
+ unsolicited++;
+ break;
+ case 'q':
+ quiet++;
+ break;
+ case 'c':
+ count = atoi(optarg);
+ break;
+ case 'w':
+ timeout = atoi(optarg);
+ break;
+ case 'I':
+ if (optarg == NULL)
+ bb_show_usage();
+ if (bb_strlen(optarg) > IF_NAMESIZE) {
+ bb_error_msg("Interface name `%s' must be less than %d", optarg,
+ IF_NAMESIZE);
+ exit(2);
+ }
+ device = optarg;
+ break;
+ case 'f':
+ quit_on_reply = 1;
+ break;
+ case 's':
+ source = optarg;
+ break;
+ case 'h':
+ case '?':
+ default:
+ bb_show_usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ bb_show_usage();
+
+ target = *argv;
+
+
+ if (s < 0) {
+ bb_error_msg("socket");
+ exit(socket_errno);
+ }
+
+ {
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, device, IFNAMSIZ - 1);
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ bb_error_msg("Interface %s not found", device);
+ exit(2);
+ }
+ ifindex = ifr.ifr_ifindex;
+
+ if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) {
+ bb_error_msg("SIOCGIFFLAGS");
+ exit(2);
+ }
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ bb_error_msg("Interface %s is down", device);
+ exit(2);
+ }
+ if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
+ bb_error_msg("Interface %s is not ARPable", device);
+ exit(dad ? 0 : 2);
+ }
+ }
+
+ if (!inet_aton(target, &dst)) {
+ struct hostent *hp;
+
+ hp = gethostbyname2(target, AF_INET);
+ if (!hp) {
+ bb_error_msg("invalid or unknown target %s", target);
+ exit(2);
+ }
+ memcpy(&dst, hp->h_addr, 4);
+ }
+
+ if (source && !inet_aton(source, &src)) {
+ bb_error_msg("invalid source address %s", source);
+ exit(2);
+ }
+
+ if (!dad && unsolicited && src.s_addr == 0)
+ src = dst;
+
+ if (!dad || src.s_addr) {
+ struct sockaddr_in saddr;
+ int probe_fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (probe_fd < 0) {
+ bb_error_msg("socket");
+ exit(2);
+ }
+ if (device) {
+ if (setsockopt
+ (probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device,
+ strlen(device) + 1) == -1)
+ bb_error_msg("WARNING: interface %s is ignored", device);
+ }
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ if (src.s_addr) {
+ saddr.sin_addr = src;
+ if (bind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) {
+ bb_error_msg("bind");
+ exit(2);
+ }
+ } else if (!dad) {
+ int on = 1;
+ int alen = sizeof(saddr);
+
+ saddr.sin_port = htons(1025);
+ saddr.sin_addr = dst;
+
+ if (setsockopt
+ (probe_fd, SOL_SOCKET, SO_DONTROUTE, (char *) &on,
+ sizeof(on)) == -1)
+ perror("WARNING: setsockopt(SO_DONTROUTE)");
+ if (connect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr))
+ == -1) {
+ bb_error_msg("connect");
+ exit(2);
+ }
+ if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) ==
+ -1) {
+ bb_error_msg("getsockname");
+ exit(2);
+ }
+ src = saddr.sin_addr;
+ }
+ close(probe_fd);
+ };
+
+ me.sll_family = AF_PACKET;
+ me.sll_ifindex = ifindex;
+ me.sll_protocol = htons(ETH_P_ARP);
+ if (bind(s, (struct sockaddr *) &me, sizeof(me)) == -1) {
+ bb_error_msg("bind");
+ exit(2);
+ }
+
+ {
+ int alen = sizeof(me);
+
+ if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) {
+ bb_error_msg("getsockname");
+ exit(2);
+ }
+ }
+ if (me.sll_halen == 0) {
+ bb_error_msg("Interface \"%s\" is not ARPable (no ll address)", device);
+ exit(dad ? 0 : 2);
+ }
+ he = me;
+ memset(he.sll_addr, -1, he.sll_halen);
+
+ if (!quiet) {
+ printf("ARPING to %s", inet_ntoa(dst));
+ printf(" from %s via %s\n", inet_ntoa(src),
+ device ? device : "unknown");
+ }
+
+ if (!src.s_addr && !dad) {
+ bb_error_msg("no src address in the non-DAD mode");
+ exit(2);
+ }
+
+ {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_RESTART;
+
+ sa.sa_handler = (void (*)(int)) finish;
+ sigaction(SIGINT, &sa, NULL);
+
+ sa.sa_handler = (void (*)(int)) catcher;
+ sigaction(SIGALRM, &sa, NULL);
+ }
+
+ catcher();
+
+ while (1) {
+ sigset_t sset, osset;
+ char packet[4096];
+ struct sockaddr_ll from;
+ int alen = sizeof(from);
+ int cc;
+
+ if ((cc = recvfrom(s, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &alen)) < 0) {
+ perror("recvfrom");
+ continue;
+ }
+ sigemptyset(&sset);
+ sigaddset(&sset, SIGALRM);
+ sigaddset(&sset, SIGINT);
+ sigprocmask(SIG_BLOCK, &sset, &osset);
+ recv_pack(packet, cc, &from);
+ sigprocmask(SIG_SETMASK, &osset, NULL);
+ }
+}
diff --git a/release/src/router/busybox/networking/ftpgetput.c b/release/src/router/busybox/networking/ftpgetput.c
new file mode 100644
index 00000000..41f45414
--- /dev/null
+++ b/release/src/router/busybox/networking/ftpgetput.c
@@ -0,0 +1,369 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ftpget
+ *
+ * Mini implementation of FTP to retrieve a remote file.
+ *
+ * Copyright (C) 2002 Jeff Angielski, The PTR Group <jeff@theptrgroup.com>
+ * Copyright (C) 2002 Glenn McGrath <bug1@optushome.com.au>
+ *
+ * Based on wget.c by Chip Rosenthal Covad Communications
+ * <chip@laserlink.net>
+ *
+ * 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 of the License, 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
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "busybox.h"
+
+typedef struct ftp_host_info_s {
+ char *host;
+ char *port;
+ char *user;
+ char *password;
+} ftp_host_info_t;
+
+static char verbose_flag;
+static char do_continue = 0;
+
+static ftp_host_info_t *ftp_init(void)
+{
+ ftp_host_info_t *host;
+
+ host = xcalloc(1, sizeof(ftp_host_info_t));
+
+ /* Set the default port */
+ if (getservbyname("ftp", "tcp")) {
+ host->port = "ftp";
+ } else {
+ host->port = "21";
+ }
+ host->user = "anonymous";
+ host->password = "busybox@";
+
+ return(host);
+}
+
+static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf)
+{
+ if (verbose_flag) {
+ bb_error_msg("cmd %s%s", s1, s2);
+ }
+
+ if (s1) {
+ if (s2) {
+ fprintf(stream, "%s%s\n", s1, s2);
+ } else {
+ fprintf(stream, "%s\n", s1);
+ }
+ }
+
+ do {
+ if (fgets(buf, 510, stream) == NULL) {
+ bb_perror_msg_and_die("fgets()");
+ }
+ } while (! isdigit(buf[0]) || buf[3] != ' ');
+
+ return atoi(buf);
+}
+
+static int xconnect_ftpdata(const char *target_host, const char *buf)
+{
+ char *buf_ptr;
+ char data_port[6];
+ unsigned short port_num;
+
+ buf_ptr = strrchr(buf, ',');
+ *buf_ptr = '\0';
+ port_num = atoi(buf_ptr + 1);
+
+ buf_ptr = strrchr(buf, ',');
+ *buf_ptr = '\0';
+ port_num += atoi(buf_ptr + 1) * 256;
+
+ sprintf(data_port, "%d", port_num);
+ return(xconnect(target_host, data_port));
+}
+
+static FILE *ftp_login(ftp_host_info_t *server)
+{
+ FILE *control_stream;
+ char buf[512];
+ int control_fd;
+
+ /* Connect to the command socket */
+ control_fd = xconnect(server->host, server->port);
+ control_stream = fdopen(control_fd, "r+");
+ if (control_stream == NULL) {
+ bb_perror_msg_and_die("Couldnt open control stream");
+ }
+
+ if (ftpcmd(NULL, NULL, control_stream, buf) != 220) {
+ bb_error_msg_and_die("%s", buf + 4);
+ }
+
+ /* Login to the server */
+ switch (ftpcmd("USER ", server->user, control_stream, buf)) {
+ case 230:
+ break;
+ case 331:
+ if (ftpcmd("PASS ", server->password, control_stream, buf) != 230) {
+ bb_error_msg_and_die("PASS error: %s", buf + 4);
+ }
+ break;
+ default:
+ bb_error_msg_and_die("USER error: %s", buf + 4);
+ }
+
+ ftpcmd("TYPE I", NULL, control_stream, buf);
+
+ return(control_stream);
+}
+
+#ifdef CONFIG_FTPGET
+static int ftp_recieve(FILE *control_stream, const char *host, const char *local_path, char *server_path)
+{
+ char *filename;
+ char *local_file;
+ char buf[512];
+ off_t filesize = 0;
+ int fd_data;
+ int fd_local;
+ off_t beg_range = 0;
+
+ filename = bb_get_last_path_component(server_path);
+ local_file = concat_path_file(local_path, filename);
+
+ /* Connect to the data socket */
+ if (ftpcmd("PASV", NULL, control_stream, buf) != 227) {
+ bb_error_msg_and_die("PASV error: %s", buf + 4);
+ }
+ fd_data = xconnect_ftpdata(host, buf);
+
+ if (ftpcmd("SIZE ", server_path, control_stream, buf) == 213) {
+ filesize = atol(buf + 4);
+ }
+
+ if (do_continue) {
+ struct stat sbuf;
+ if (lstat(local_file, &sbuf) < 0) {
+ bb_perror_msg_and_die("fstat()");
+ }
+ if (sbuf.st_size > 0) {
+ beg_range = sbuf.st_size;
+ } else {
+ do_continue = 0;
+ }
+ }
+
+ if (do_continue) {
+ sprintf(buf, "REST %ld", (long)beg_range);
+ if (ftpcmd(buf, NULL, control_stream, buf) != 350) {
+ do_continue = 0;
+ } else {
+ filesize -= beg_range;
+ }
+ }
+
+ if (ftpcmd("RETR ", server_path, control_stream, buf) > 150) {
+ bb_error_msg_and_die("RETR error: %s", buf + 4);
+ }
+
+ /* only make a local file if we know that one exists on the remote server */
+ if (do_continue) {
+ fd_local = bb_xopen(local_file, O_APPEND | O_WRONLY);
+ } else {
+ fd_local = bb_xopen(local_file, O_CREAT | O_TRUNC | O_WRONLY);
+ }
+
+ /* Copy the file */
+ if (bb_copyfd(fd_data, fd_local, filesize) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* close it all down */
+ close(fd_data);
+ if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
+ bb_error_msg_and_die("ftp error: %s", buf + 4);
+ }
+ ftpcmd("QUIT", NULL, control_stream, buf);
+
+ return(EXIT_SUCCESS);
+}
+#endif
+
+#ifdef CONFIG_FTPPUT
+static int ftp_send(FILE *control_stream, const char *host, const char *server_path, char *local_path)
+{
+ struct stat sbuf;
+ char buf[512];
+ int fd_data;
+ int fd_local;
+ int response;
+
+ /* Connect to the data socket */
+ if (ftpcmd("PASV", NULL, control_stream, buf) != 227) {
+ bb_error_msg_and_die("PASV error: %s", buf + 4);
+ }
+ fd_data = xconnect_ftpdata(host, buf);
+
+ if (ftpcmd("CWD ", server_path, control_stream, buf) != 250) {
+ bb_error_msg_and_die("CWD error: %s", buf + 4);
+ }
+
+ /* get the local file */
+ fd_local = bb_xopen(local_path, O_RDONLY);
+ fstat(fd_local, &sbuf);
+
+ sprintf(buf, "ALLO %lu", (unsigned long)sbuf.st_size);
+ response = ftpcmd(buf, NULL, control_stream, buf);
+ switch (response) {
+ case 200:
+ case 202:
+ break;
+ default:
+ close(fd_local);
+ bb_error_msg_and_die("ALLO error: %s", buf + 4);
+ break;
+ }
+
+ response = ftpcmd("STOR ", local_path, control_stream, buf);
+ switch (response) {
+ case 125:
+ case 150:
+ break;
+ default:
+ close(fd_local);
+ bb_error_msg_and_die("STOR error: %s", buf + 4);
+ }
+
+ /* transfer the file */
+ if (bb_copyfd(fd_local, fd_data, 0) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* close it all down */
+ close(fd_data);
+ if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
+ bb_error_msg_and_die("error: %s", buf + 4);
+ }
+ ftpcmd("QUIT", NULL, control_stream, buf);
+
+ return(EXIT_SUCCESS);
+}
+#endif
+
+int ftpgetput_main(int argc, char **argv)
+{
+ /* content-length of the file */
+ int option_index = -1;
+ int opt;
+
+ /* socket to ftp server */
+ FILE *control_stream;
+
+ /* continue a prev transfer (-c) */
+ ftp_host_info_t *server;
+
+ int (*ftp_action)(FILE *, const char *, const char *, char *) = NULL;
+
+ struct option long_options[] = {
+ {"username", 1, NULL, 'u'},
+ {"password", 1, NULL, 'p'},
+ {"port", 1, NULL, 'P'},
+ {"continue", 1, NULL, 'c'},
+ {"verbose", 0, NULL, 'v'},
+ {0, 0, 0, 0}
+ };
+
+#ifdef CONFIG_FTPPUT
+ if (bb_applet_name[3] == 'p') {
+ ftp_action = ftp_send;
+ }
+#endif
+#ifdef CONFIG_FTPGET
+ if (bb_applet_name[3] == 'g') {
+ ftp_action = ftp_recieve;
+ }
+#endif
+
+ /* Set default values */
+ server = ftp_init();
+ verbose_flag = 0;
+
+ /*
+ * Decipher the command line
+ */
+ while ((opt = getopt_long(argc, argv, "u:p:P:cv", long_options, &option_index)) != EOF) {
+ switch(opt) {
+ case 'c':
+ do_continue = 1;
+ break;
+ case 'u':
+ server->user = optarg;
+ break;
+ case 'p':
+ server->password = optarg;
+ break;
+ case 'P':
+ server->port = optarg;
+ break;
+ case 'v':
+ verbose_flag = 1;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+
+ /*
+ * Process the non-option command line arguments
+ */
+ if (argc - optind != 3) {
+ bb_show_usage();
+ }
+
+ /* Connect/Setup/Configure the FTP session */
+ server->host = argv[optind];
+ control_stream = ftp_login(server);
+
+ return(ftp_action(control_stream, argv[optind], argv[optind + 1], argv[optind + 2]));
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/release/src/router/busybox/networking/hostname.c b/release/src/router/busybox/networking/hostname.c
new file mode 100644
index 00000000..11593e7a
--- /dev/null
+++ b/release/src/router/busybox/networking/hostname.c
@@ -0,0 +1,130 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * $Id: hostname.c,v 1.1.3.1 2004/12/29 07:07:45 honor Exp $
+ * Mini hostname implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * adjusted by Erik Andersen <andersen@codepoet.org> to remove
+ * use of long options and GNU getopt. Improved the usage info.
+ *
+ * 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 of the License, 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
+ */
+
+#include <errno.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "busybox.h"
+
+extern char *optarg; /* in unistd.h */
+extern int optind, opterr, optopt; /* in unistd.h */
+
+static void do_sethostname(char *s, int isfile)
+{
+ FILE *f;
+ char buf[255];
+
+ if (!s)
+ return;
+ if (!isfile) {
+ if (sethostname(s, strlen(s)) < 0) {
+ if (errno == EPERM)
+ bb_error_msg_and_die("you must be root to change the hostname");
+ else
+ bb_perror_msg_and_die("sethostname");
+ }
+ } else {
+ f = bb_xfopen(s, "r");
+ while (fgets(buf, 255, f) != NULL) {
+ if (buf[0] =='#') {
+ continue;
+ }
+ chomp(buf);
+ do_sethostname(buf, 0);
+ }
+#ifdef CONFIG_FEATURE_CLEAN_UP
+ fclose(f);
+#endif
+ }
+}
+
+int hostname_main(int argc, char **argv)
+{
+ int opt;
+ int type = 0;
+ struct hostent *hp;
+ char *filename = NULL;
+ char buf[255];
+ char *p = NULL;
+
+ if (argc < 1)
+ bb_show_usage();
+
+ while ((opt = getopt(argc, argv, "dfisF:")) > 0) {
+ switch (opt) {
+ case 'd':
+ case 'f':
+ case 'i':
+ case 's':
+ type = opt;
+ break;
+ case 'F':
+ filename = optarg;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+
+ /* Output in desired format */
+ if (type != 0) {
+ gethostname(buf, 255);
+ hp = xgethostbyname(buf);
+ p = strchr(hp->h_name, '.');
+ if (type == 'f') {
+ puts(hp->h_name);
+ } else if (type == 's') {
+ if (p != NULL) {
+ *p = 0;
+ }
+ puts(hp->h_name);
+ } else if (type == 'd') {
+ if (p) puts(p + 1);
+ } else if (type == 'i') {
+ while (hp->h_addr_list[0]) {
+ printf("%s ", inet_ntoa(*(struct in_addr *) (*hp->h_addr_list++)));
+ }
+ printf("\n");
+ }
+ }
+ /* Set the hostname */
+ else if (filename != NULL) {
+ do_sethostname(filename, 1);
+ } else if (optind < argc) {
+ do_sethostname(argv[optind], 0);
+ }
+ /* Or if all else fails,
+ * just print the current hostname */
+ else {
+ gethostname(buf, 255);
+ puts(buf);
+ }
+ return(0);
+}
diff --git a/release/src/router/busybox/networking/httpd.c b/release/src/router/busybox/networking/httpd.c
new file mode 100644
index 00000000..d58414b5
--- /dev/null
+++ b/release/src/router/busybox/networking/httpd.c
@@ -0,0 +1,1915 @@
+/*
+ * httpd implementation for busybox
+ *
+ * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
+ * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * simplify patch stolen from libbb without using strdup
+ *
+ * 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 of the License, 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
+ *
+ *****************************************************************************
+ *
+ * Typical usage:
+ * for non root user
+ * httpd -p 8080 -h $HOME/public_html
+ * or for daemon start from rc script with uid=0:
+ * httpd -u www
+ * This is equivalent if www user have uid=80 to
+ * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"
+ *
+ *
+ * When a url contains "cgi-bin" it is assumed to be a cgi script. The
+ * server changes directory to the location of the script and executes it
+ * after setting QUERY_STRING and other environment variables. If url args
+ * are included in the url or as a post, the args are placed into decoded
+ * environment variables. e.g. /cgi-bin/setup?foo=Hello%20World will set
+ * the $CGI_foo environment variable to "Hello World" while
+ * CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV enabled.
+ *
+ * The server can also be invoked as a url arg decoder and html text encoder
+ * as follows:
+ * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
+ * bar=`httpd -e "<Hello World>"` # encode as "&#60Hello&#32World&#62"
+ * Note that url encoding for arguments is not the same as html encoding for
+ * presenation. -d decodes a url-encoded argument while -e encodes in html
+ * for page display.
+ *
+ * httpd.conf has the following format:
+ *
+ * A:172.20. # Allow any address that begins with 172.20
+ * A:10.10. # Allow any address that begins with 10.10.
+ * A:10.20 # Allow any address that previous set and 10.200-209.X.X
+ * A:127.0.0.1 # Allow local loopback connections
+ * D:* # Deny from other IP connections
+ * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
+ * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
+ * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
+ * .au:audio/basic # additional mime type for audio.au files
+ *
+ * A/D may be as a/d or allow/deny - first char case unsensitive
+ * Deny IP rules take precedence over allow rules.
+ *
+ *
+ * The Deny/Allow IP logic:
+ *
+ * - Default is to allow all. No addresses are denied unless
+ * denied with a D: rule.
+ * - Order of Deny/Allow rules is significant
+ * - Deny rules take precedence over allow rules.
+ * - If a deny all rule (D:*) is used it acts as a catch-all for unmatched
+ * addresses.
+ * - Specification of Allow all (A:*) is a no-op
+ *
+ * Example:
+ * 1. Allow only specified addresses
+ * A:172.20. # Allow any address that begins with 172.20
+ * A:10.10. # Allow any address that begins with 10.10.
+ * A:10.10 # Allow any address that previous set and 10.100-109.X.X
+ * A:127.0.0.1 # Allow local loopback connections
+ * D:* # Deny from other IP connections
+ *
+ * 2. Only deny specified addresses
+ * D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
+ * D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
+ * A:* # (optional line added for clarity)
+ *
+ * If a sub directory contains a config file it is parsed and merged with
+ * any existing settings as if it was appended to the original configuration
+ * except that all previous IP config rules are discarded.
+ *
+ * subdir paths are relative to the containing subdir and thus cannot
+ * affect the parent rules.
+ *
+ * Note that since the sub dir is parsed in the forked thread servicing the
+ * subdir http request, any merge is discarded when the process exits. As a
+ * result, the subdir settings only have a lifetime of a single request.
+ *
+ *
+ * If -c is not set, an attempt will be made to open the default
+ * root configuration file. If -c is set and the file is not found, the
+ * server exits with an error.
+ *
+*/
+
+
+#include <stdio.h>
+#include <ctype.h> /* for isspace */
+#include <string.h>
+#include <stdlib.h> /* for malloc */
+#include <time.h>
+#include <unistd.h> /* for close */
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h> /* for connect and socket*/
+#include <netinet/in.h> /* for sockaddr_in */
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h> /* for open modes */
+#include "busybox.h"
+
+
+static const char httpdVersion[] = "busybox httpd/1.28 22-Jun-2003";
+static const char default_path_httpd_conf[] = "/etc";
+static const char httpd_conf[] = "httpd.conf";
+static const char home[] = "./";
+
+// Note: bussybox xfuncs are not used because we want the server to keep running
+// if something bad happens due to a malformed user request.
+// As a result, all memory allocation after daemonize
+// is checked rigorously
+
+//#define DEBUG 1
+
+/* Configure options, disabled by default as custom httpd feature */
+
+/* disabled as optional features */
+//#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
+//#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+//#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+//#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+//#define CONFIG_FEATURE_HTTPD_SETUID
+//#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+
+/* If set, use this server from internet superserver only */
+//#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+
+/* You can use this server as standalone, require libbb.a for linking */
+//#define HTTPD_STANDALONE
+
+/* Config options, disable this for do very small module */
+//#define CONFIG_FEATURE_HTTPD_CGI
+//#define CONFIG_FEATURE_HTTPD_BASIC_AUTH
+//#define CONFIG_FEATURE_HTTPD_AUTH_MD5
+
+#ifdef HTTPD_STANDALONE
+/* standalone, enable all features */
+#undef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+/* unset config option for remove warning as redefined */
+#undef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+#undef CONFIG_FEATURE_HTTPD_AUTH_MD5
+#undef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
+#undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+#undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+#undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+#undef CONFIG_FEATURE_HTTPD_CGI
+#undef CONFIG_FEATURE_HTTPD_SETUID
+#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+/* enable all features now */
+#define CONFIG_FEATURE_HTTPD_BASIC_AUTH
+#define CONFIG_FEATURE_HTTPD_AUTH_MD5
+#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
+#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+#define CONFIG_FEATURE_HTTPD_CGI
+#define CONFIG_FEATURE_HTTPD_SETUID
+#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+
+/* require from libbb.a for linking */
+const char *bb_applet_name = "httpd";
+
+void bb_show_usage(void)
+{
+ fprintf(stderr, "Usage: %s [-p <port>] [-c configFile] [-d/-e <string>] "
+ "[-r realm] [-u user] [-h homedir]\n", bb_applet_name);
+ exit(1);
+}
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+#undef CONFIG_FEATURE_HTTPD_SETUID /* use inetd user.group config settings */
+#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP /* so is not daemon */
+/* inetd set stderr to accepted socket and we can`t true see debug messages */
+#undef DEBUG
+#endif
+
+/* CGI environ size */
+#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
+#define ENVSIZE 70 /* set max CGI variable */
+#else
+#define ENVSIZE 15 /* minimal requires */
+#endif
+
+#define MAX_POST_SIZE (64*1024) /* 64k. Its Small? May be ;) */
+
+#define MAX_MEMORY_BUFF 8192 /* IO buffer */
+
+typedef struct HT_ACCESS {
+ char *after_colon;
+ struct HT_ACCESS *next;
+ char before_colon[1]; /* really bigger, must last */
+} Htaccess;
+
+typedef struct
+{
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+ char *envp[ENVSIZE+1];
+ int envCount;
+#endif
+ char buf[MAX_MEMORY_BUFF];
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ const char *realm;
+#endif
+ const char *configFile;
+
+ char rmt_ip[16]; /* for set env REMOTE_ADDR */
+ unsigned port; /* server initial port and for
+ set env REMOTE_PORT */
+
+ const char *found_mime_type;
+ off_t ContentLength; /* -1 - unknown */
+ time_t last_mod;
+
+ Htaccess *ip_a_d; /* config allow/deny lines */
+ int flg_deny_all;
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ Htaccess *auth; /* config user:password lines */
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ Htaccess *mime_a; /* config mime types */
+#endif
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ int accepted_socket;
+#define a_c_r config->accepted_socket
+#define a_c_w config->accepted_socket
+ int debugHttpd; /* if seted, don`t stay daemon */
+#else
+#define a_c_r 0
+#define a_c_w 1
+#endif
+} HttpdConfig;
+
+static HttpdConfig *config;
+
+static const char request_GET[] = "GET"; /* size algorithic optimize */
+
+static const char* const suffixTable [] = {
+/* Warning: shorted equalent suffix in one line must be first */
+ ".htm.html", "text/html",
+ ".jpg.jpeg", "image/jpeg",
+ ".gif", "image/gif",
+ ".png", "image/png",
+ ".txt.h.c.cc.cpp", "text/plain",
+ ".css", "text/css",
+ ".wav", "audio/wav",
+ ".avi", "video/x-msvideo",
+ ".qt.mov", "video/quicktime",
+ ".mpe.mpeg", "video/mpeg",
+ ".mid.midi", "audio/midi",
+ ".mp3", "audio/mpeg",
+#if 0 /* unpopular */
+ ".au", "audio/basic",
+ ".pac", "application/x-ns-proxy-autoconfig",
+ ".vrml.wrl", "model/vrml",
+#endif
+ 0, "application/octet-stream" /* default */
+ };
+
+typedef enum
+{
+ HTTP_OK = 200,
+ HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
+ HTTP_NOT_FOUND = 404,
+ HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
+ HTTP_BAD_REQUEST = 400, /* malformed syntax */
+ HTTP_FORBIDDEN = 403,
+ HTTP_INTERNAL_SERVER_ERROR = 500,
+#if 0 /* future use */
+ HTTP_CONTINUE = 100,
+ HTTP_SWITCHING_PROTOCOLS = 101,
+ HTTP_CREATED = 201,
+ HTTP_ACCEPTED = 202,
+ HTTP_NON_AUTHORITATIVE_INFO = 203,
+ HTTP_NO_CONTENT = 204,
+ HTTP_MULTIPLE_CHOICES = 300,
+ HTTP_MOVED_PERMANENTLY = 301,
+ HTTP_MOVED_TEMPORARILY = 302,
+ HTTP_NOT_MODIFIED = 304,
+ HTTP_PAYMENT_REQUIRED = 402,
+ HTTP_BAD_GATEWAY = 502,
+ HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
+ HTTP_RESPONSE_SETSIZE=0xffffffff
+#endif
+} HttpResponseNum;
+
+typedef struct
+{
+ HttpResponseNum type;
+ const char *name;
+ const char *info;
+} HttpEnumString;
+
+static const HttpEnumString httpResponseNames[] = {
+ { HTTP_OK, "OK" },
+ { HTTP_NOT_IMPLEMENTED, "Not Implemented",
+ "The requested method is not recognized by this server." },
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ { HTTP_UNAUTHORIZED, "Unauthorized", "" },
+#endif
+ { HTTP_NOT_FOUND, "Not Found",
+ "The requested URL was not found on this server." },
+ { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
+ { HTTP_FORBIDDEN, "Forbidden", "" },
+ { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
+ "Internal Server Error" },
+#if 0 /* not implemented */
+ { HTTP_CREATED, "Created" },
+ { HTTP_ACCEPTED, "Accepted" },
+ { HTTP_NO_CONTENT, "No Content" },
+ { HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
+ { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
+ { HTTP_MOVED_TEMPORARILY, "Moved Temporarily" },
+ { HTTP_NOT_MODIFIED, "Not Modified" },
+ { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
+ { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
+#endif
+};
+
+
+static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
+static const char Content_length[] = "Content-length:";
+
+
+
+static void free_config_lines(Htaccess **pprev)
+{
+ Htaccess *prev = *pprev;
+
+ while( prev ) {
+ Htaccess *cur = prev;
+
+ prev = cur->next;
+ free(cur);
+ }
+ *pprev = NULL;
+}
+
+/* flag */
+#define FIRST_PARSE 0
+#define SUBDIR_PARSE 1
+#define SIGNALED_PARSE 2
+#define FIND_FROM_HTTPD_ROOT 3
+/****************************************************************************
+ *
+ > $Function: parse_conf()
+ *
+ * $Description: parse configuration file into in-memory linked list.
+ *
+ * The first non-white character is examined to determine if the config line
+ * is one of the following:
+ * .ext:mime/type # new mime type not compiled into httpd
+ * [adAD]:from # ip address allow/deny, * for wildcard
+ * /path:user:pass # username/password
+ *
+ * Any previous IP rules are discarded.
+ * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
+ * are also discarded. That is, previous settings are retained if flag is
+ * SUBDIR_PARSE.
+ *
+ * $Parameters:
+ * (const char *) path . . null for ip address checks, path for password
+ * checks.
+ * (int) flag . . . . . . the source of the parse request.
+ *
+ * $Return: (None)
+ *
+ ****************************************************************************/
+static void parse_conf(const char *path, int flag)
+{
+ FILE *f;
+ Htaccess *cur;
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ Htaccess *prev;
+#endif
+
+ const char *cf = config->configFile;
+ char buf[160];
+ char *p0 = NULL;
+ char *c, *p;
+
+ /* free previous ip setup if present */
+ free_config_lines(&config->ip_a_d);
+ config->flg_deny_all = 0;
+ /* retain previous auth and mime config only for subdir parse */
+ if(flag != SUBDIR_PARSE) {
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ free_config_lines(&config->auth)
+#endif
+ ; /* appease compiler warnings if option is not set */
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ free_config_lines(&config->mime_a);
+#endif
+ }
+
+ if(flag == SUBDIR_PARSE || cf == NULL) {
+ cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
+ if(cf == NULL) {
+ if(flag == FIRST_PARSE)
+ bb_error_msg_and_die(bb_msg_memory_exhausted);
+ return;
+ }
+ sprintf((char *)cf, "%s/%s", path, httpd_conf);
+ }
+
+ while((f = fopen(cf, "r")) == NULL) {
+ if(flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {
+ /* config file not found, no changes to config */
+ return;
+ }
+ if(config->configFile && flag == FIRST_PARSE) /* if -c option given */
+ bb_perror_msg_and_die("%s", cf);
+ flag = FIND_FROM_HTTPD_ROOT;
+ cf = httpd_conf;
+ }
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ prev = config->auth;
+#endif
+ /* This could stand some work */
+ while ( (p0 = fgets(buf, sizeof(buf), f)) != NULL) {
+ c = NULL;
+ for(p = p0; *p0 != 0 && *p0 != '#'; p0++) {
+ if(!isspace(*p0)) {
+ *p++ = *p0;
+ if(*p0 == ':' && c == NULL)
+ c = p;
+ }
+ }
+ *p = 0;
+
+ /* test for empty or strange line */
+ if (c == NULL || *c == 0)
+ continue;
+ p0 = buf;
+ if(*p0 == 'd')
+ *p0 = 'D';
+ if(*c == '*') {
+ if(*p0 == 'D') {
+ /* memorize deny all */
+ config->flg_deny_all++;
+ }
+ /* skip default other "word:*" config lines */
+ continue;
+ }
+
+ if(*p0 == 'a')
+ *p0 = 'A';
+ else if(*p0 != 'D'
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ && *p0 != '/'
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ && *p0 != '.'
+#endif
+ )
+ continue;
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ if(*p0 == '/') {
+ /* make full path from httpd root / curent_path / config_line_path */
+ cf = flag == SUBDIR_PARSE ? path : "";
+ p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
+ if(p0 == NULL)
+ continue;
+ c[-1] = 0;
+ sprintf(p0, "/%s%s", cf, buf);
+
+ /* another call bb_simplify_path */
+ cf = p = p0;
+
+ do {
+ if (*p == '/') {
+ if (*cf == '/') { /* skip duplicate (or initial) slash */
+ continue;
+ } else if (*cf == '.') {
+ if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
+ continue;
+ } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
+ ++cf;
+ if (p > p0) {
+ while (*--p != '/'); /* omit previous dir */
+ }
+ continue;
+ }
+ }
+ }
+ *++p = *cf;
+ } while (*++cf);
+
+ if ((p == p0) || (*p != '/')) { /* not a trailing slash */
+ ++p; /* so keep last character */
+ }
+ *p = 0;
+ sprintf(p0, "%s:%s", p0, c);
+ }
+#endif
+ /* storing current config line */
+
+ cur = calloc(1, sizeof(Htaccess) + strlen(p0));
+ if(cur) {
+ cf = strcpy(cur->before_colon, p0);
+ c = strchr(cf, ':');
+ *c++ = 0;
+ cur->after_colon = c;
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ if(*cf == '/')
+ free(p0);
+#endif
+ if(*cf == 'A' || *cf == 'D') {
+ if(*cf == 'D') {
+ /* Deny:form_IP move top */
+ cur->next = config->ip_a_d;
+ config->ip_a_d = cur;
+ } else {
+ /* add to bottom A:form_IP config line */
+ Htaccess *prev_IP = config->ip_a_d;
+
+ if(prev_IP == NULL) {
+ config->ip_a_d = cur;
+ } else {
+ while(prev_IP->next)
+ prev_IP = prev_IP->next;
+ prev_IP->next = cur;
+ }
+ }
+ }
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ else if(*cf == '.') {
+ /* config .mime line move top for overwrite previous */
+ cur->next = config->mime_a;
+ config->mime_a = cur;
+ }
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ else if(prev == NULL) {
+ /* first line */
+ config->auth = prev = cur;
+ } else {
+ /* sort path, if current lenght eq or bigger then move up */
+ Htaccess *prev_hti = config->auth;
+ int l = strlen(cf);
+ Htaccess *hti;
+
+ for(hti = prev_hti; hti; hti = hti->next) {
+ if(l >= strlen(hti->before_colon)) {
+ /* insert before hti */
+ cur->next = hti;
+ if(prev_hti != hti) {
+ prev_hti->next = cur;
+ } else {
+ /* insert as top */
+ config->auth = cur;
+ }
+ break;
+ }
+ if(prev_hti != hti)
+ prev_hti = prev_hti->next;
+ }
+ if(!hti) { /* not inserted, add to bottom */
+ prev->next = cur;
+ prev = cur;
+ }
+ }
+#endif
+ }
+ }
+ fclose(f);
+}
+
+#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+/****************************************************************************
+ *
+ > $Function: encodeString()
+ *
+ * $Description: Given a string, html encode special characters.
+ * This is used for the -e command line option to provide an easy way
+ * for scripts to encode result data without confusing browsers. The
+ * returned string pointer is memory allocated by malloc().
+ *
+ * $Parameters:
+ * (const char *) string . . The first string to encode.
+ *
+ * $Return: (char *) . . . .. . . A pointer to the encoded string.
+ *
+ * $Errors: Returns a null string ("") if memory is not available.
+ *
+ ****************************************************************************/
+static char *encodeString(const char *string)
+{
+ /* take the simple route and encode everything */
+ /* could possibly scan once to get length. */
+ int len = strlen(string);
+ char *out = malloc(len*5 +1);
+ char *p=out;
+ char ch;
+
+ if (!out) return "";
+ while ((ch = *string++)) {
+ // very simple check for what to encode
+ if (isalnum(ch)) *p++ = ch;
+ else p += sprintf(p, "&#%d", (unsigned char) ch);
+ }
+ *p=0;
+ return out;
+}
+#endif /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */
+
+/****************************************************************************
+ *
+ > $Function: decodeString()
+ *
+ * $Description: Given a URL encoded string, convert it to plain ascii.
+ * Since decoding always makes strings smaller, the decode is done in-place.
+ * Thus, callers should strdup() the argument if they do not want the
+ * argument modified. The return is the original pointer, allowing this
+ * function to be easily used as arguments to other functions.
+ *
+ * $Parameters:
+ * (char *) string . . . The first string to decode.
+ * (int) flag . . . 1 if require decode '+' as ' ' for CGI
+ *
+ * $Return: (char *) . . . . A pointer to the decoded string (same as input).
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static char *decodeString(char *orig, int flag_plus_to_space)
+{
+ /* note that decoded string is always shorter than original */
+ char *string = orig;
+ char *ptr = string;
+
+ while (*ptr)
+ {
+ if (*ptr == '+' && flag_plus_to_space) { *string++ = ' '; ptr++; }
+ else if (*ptr != '%') *string++ = *ptr++;
+ else {
+ unsigned int value;
+ sscanf(ptr+1, "%2X", &value);
+ *string++ = value;
+ ptr += 3;
+ }
+ }
+ *string = '\0';
+ return orig;
+}
+
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+/****************************************************************************
+ *
+ > $Function: addEnv()
+ *
+ * $Description: Add an enviornment variable setting to the global list.
+ * A NAME=VALUE string is allocated, filled, and added to the list of
+ * environment settings passed to the cgi execution script.
+ *
+ * $Parameters:
+ * (char *) name_before_underline - The first part environment variable name.
+ * (char *) name_after_underline - The second part environment variable name.
+ * (char *) value . . The value to which the env variable is set.
+ *
+ * $Return: (void)
+ *
+ * $Errors: Silently returns if the env runs out of space to hold the new item
+ *
+ ****************************************************************************/
+static void addEnv(const char *name_before_underline,
+ const char *name_after_underline, const char *value)
+{
+ char *s;
+ const char *underline;
+
+ if (config->envCount >= ENVSIZE)
+ return;
+ if (!value)
+ value = "";
+ underline = *name_after_underline ? "_" : "";
+ asprintf(&s, "%s%s%s=%s", name_before_underline, underline,
+ name_after_underline, value);
+ if(s) {
+ config->envp[config->envCount++] = s;
+ config->envp[config->envCount] = 0;
+ }
+}
+
+#if defined(CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV) || !defined(CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY)
+/* set environs SERVER_PORT and REMOTE_PORT */
+static void addEnvPort(const char *port_name)
+{
+ char buf[16];
+
+ sprintf(buf, "%u", config->port);
+ addEnv(port_name, "PORT", buf);
+}
+#endif
+#endif /* CONFIG_FEATURE_HTTPD_CGI */
+
+#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
+/****************************************************************************
+ *
+ > $Function: addEnvCgi
+ *
+ * $Description: Create environment variables given a URL encoded arg list.
+ * For each variable setting the URL encoded arg list, create a corresponding
+ * environment variable. URL encoded arguments have the form
+ * name1=value1&name2=value2&name3=&ignores
+ * from this example, name3 set empty value, tail without '=' skiping
+ *
+ * $Parameters:
+ * (char *) pargs . . . . A pointer to the URL encoded arguments.
+ *
+ * $Return: None
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static void addEnvCgi(const char *pargs)
+{
+ char *args;
+ char *memargs;
+ char *namelist; /* space separated list of arg names */
+ if (pargs==0) return;
+
+ /* args are a list of name=value&name2=value2 sequences */
+ namelist = (char *) malloc(strlen(pargs));
+ if (namelist) namelist[0]=0;
+ memargs = args = strdup(pargs);
+ while (args && *args) {
+ const char *name = args;
+ char *value = strchr(args, '=');
+
+ if (!value) /* &XXX without '=' */
+ break;
+ *value++ = 0;
+ args = strchr(value, '&');
+ if (args)
+ *args++ = 0;
+ addEnv("CGI", name, decodeString(value, 1));
+ if (*namelist) strcat(namelist, " ");
+ strcat(namelist, name);
+ }
+ free(memargs);
+ if (namelist) {
+ addEnv("CGI", "ARGLIST_", namelist);
+ free(namelist);
+ }
+}
+#endif /* CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV */
+
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+/****************************************************************************
+ *
+ > $Function: decodeBase64()
+ *
+ > $Description: Decode a base 64 data stream as per rfc1521.
+ * Note that the rfc states that none base64 chars are to be ignored.
+ * Since the decode always results in a shorter size than the input, it is
+ * OK to pass the input arg as an output arg.
+ *
+ * $Parameter:
+ * (char *) Data . . . . A pointer to a base64 encoded string.
+ * Where to place the decoded data.
+ *
+ * $Return: void
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static void decodeBase64(char *Data)
+{
+
+ const unsigned char *in = Data;
+ // The decoded size will be at most 3/4 the size of the encoded
+ unsigned long ch = 0;
+ int i = 0;
+
+ while (*in) {
+ int t = *in++;
+
+ if(t >= '0' && t <= '9')
+ t = t - '0' + 52;
+ else if(t >= 'A' && t <= 'Z')
+ t = t - 'A';
+ else if(t >= 'a' && t <= 'z')
+ t = t - 'a' + 26;
+ else if(t == '+')
+ t = 62;
+ else if(t == '/')
+ t = 63;
+ else if(t == '=')
+ t = 0;
+ else
+ continue;
+
+ ch = (ch << 6) | t;
+ i++;
+ if (i == 4) {
+ *Data++ = (char) (ch >> 16);
+ *Data++ = (char) (ch >> 8);
+ *Data++ = (char) ch;
+ i = 0;
+ }
+ }
+ *Data = 0;
+}
+#endif
+
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+/****************************************************************************
+ *
+ > $Function: openServer()
+ *
+ * $Description: create a listen server socket on the designated port.
+ *
+ * $Return: (int) . . . A connection socket. -1 for errors.
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static int openServer(void)
+{
+ struct sockaddr_in lsocket;
+ int fd;
+
+ /* create the socket right now */
+ /* inet_addr() returns a value that is already in network order */
+ memset(&lsocket, 0, sizeof(lsocket));
+ lsocket.sin_family = AF_INET;
+ lsocket.sin_addr.s_addr = INADDR_ANY;
+ lsocket.sin_port = htons(config->port) ;
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd >= 0) {
+ /* tell the OS it's OK to reuse a previous address even though */
+ /* it may still be in a close down state. Allows bind to succeed. */
+ int on = 1;
+#ifdef SO_REUSEPORT
+ setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) ;
+#else
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) ;
+#endif
+ if (bind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)) == 0) {
+ listen(fd, 9);
+ signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */
+ } else {
+ bb_perror_msg_and_die("bind");
+ }
+ } else {
+ bb_perror_msg_and_die("create socket");
+ }
+ return fd;
+}
+#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
+
+/****************************************************************************
+ *
+ > $Function: sendHeaders()
+ *
+ * $Description: Create and send HTTP response headers.
+ * The arguments are combined and sent as one write operation. Note that
+ * IE will puke big-time if the headers are not sent in one packet and the
+ * second packet is delayed for any reason.
+ *
+ * $Parameter:
+ * (HttpResponseNum) responseNum . . . The result code to send.
+ *
+ * $Return: (int) . . . . writing errors
+ *
+ ****************************************************************************/
+static int sendHeaders(HttpResponseNum responseNum)
+{
+ char *buf = config->buf;
+ const char *responseString = "";
+ const char *infoString = 0;
+ unsigned int i;
+ time_t timer = time(0);
+ char timeStr[80];
+ int len;
+
+ for (i = 0;
+ i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) {
+ if (httpResponseNames[i].type == responseNum) {
+ responseString = httpResponseNames[i].name;
+ infoString = httpResponseNames[i].info;
+ break;
+ }
+ }
+ if (responseNum != HTTP_OK) {
+ config->found_mime_type = "text/html"; // error message is HTML
+ }
+
+ /* emit the current date */
+ strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
+ len = sprintf(buf,
+ "HTTP/1.0 %d %s\nContent-type: %s\r\n"
+ "Date: %s\r\nConnection: close\r\n",
+ responseNum, responseString, config->found_mime_type, timeStr);
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ if (responseNum == HTTP_UNAUTHORIZED) {
+ len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n",
+ config->realm);
+ }
+#endif
+ if (config->ContentLength != -1) { /* file */
+ strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
+ len += sprintf(buf+len, "Last-Modified: %s\r\n%s %ld\r\n",
+ timeStr, Content_length, config->ContentLength);
+ }
+ strcat(buf, "\r\n");
+ len += 2;
+ if (infoString) {
+ len += sprintf(buf+len,
+ "<HEAD><TITLE>%d %s</TITLE></HEAD>\n"
+ "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n",
+ responseNum, responseString,
+ responseNum, responseString, infoString);
+ }
+#ifdef DEBUG
+ if (config->debugHttpd) fprintf(stderr, "Headers: '%s'", buf);
+#endif
+ return bb_full_write(a_c_w, buf, len);
+}
+
+/****************************************************************************
+ *
+ > $Function: getLine()
+ *
+ * $Description: Read from the socket until an end of line char found.
+ *
+ * Characters are read one at a time until an eol sequence is found.
+ *
+ * $Parameters:
+ * (char *) buf . . Where to place the read result.
+ *
+ * $Return: (int) . . . . number of characters read. -1 if error.
+ *
+ ****************************************************************************/
+static int getLine(char *buf)
+{
+ int count = 0;
+
+ while (read(a_c_r, buf + count, 1) == 1) {
+ if (buf[count] == '\r') continue;
+ if (buf[count] == '\n') {
+ buf[count] = 0;
+ return count;
+ }
+ if(count < (MAX_MEMORY_BUFF-1)) /* check owerflow */
+ count++;
+ }
+ if (count) return count;
+ else return -1;
+}
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+/****************************************************************************
+ *
+ > $Function: sendCgi()
+ *
+ * $Description: Execute a CGI script and send it's stdout back
+ *
+ * Environment variables are set up and the script is invoked with pipes
+ * for stdin/stdout. If a post is being done the script is fed the POST
+ * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
+ *
+ * $Parameters:
+ * (const char *) url . . . The requested URL (with leading /).
+ * (const char *urlArgs). . Any URL arguments.
+ * (const char *body) . . . POST body contents.
+ * (int bodyLen) . . . . . Length of the post body.
+ * (const char *cookie) . . For set HTTP_COOKIE.
+
+ *
+ * $Return: (char *) . . . . A pointer to the decoded string (same as input).
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static int sendCgi(const char *url,
+ const char *request, const char *urlArgs,
+ const char *body, int bodyLen, const char *cookie)
+{
+ int fromCgi[2]; /* pipe for reading data from CGI */
+ int toCgi[2]; /* pipe for sending data to CGI */
+
+ static char * argp[] = { 0, 0 };
+ int pid = 0;
+ int inFd;
+ int outFd;
+ int firstLine = 1;
+
+ do {
+ if (pipe(fromCgi) != 0) {
+ break;
+ }
+ if (pipe(toCgi) != 0) {
+ break;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ pid = 0;
+ break;
+ }
+
+ if (!pid) {
+ /* child process */
+ char *script;
+ char *purl = strdup( url );
+ char realpath_buff[MAXPATHLEN];
+
+ if(purl == NULL)
+ _exit(242);
+
+ inFd = toCgi[0];
+ outFd = fromCgi[1];
+
+ dup2(inFd, 0); // replace stdin with the pipe
+ dup2(outFd, 1); // replace stdout with the pipe
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ if (!config->debugHttpd)
+#endif
+ dup2(outFd, 2); // replace stderr with the pipe
+
+ close(toCgi[0]);
+ close(toCgi[1]);
+ close(fromCgi[0]);
+ close(fromCgi[1]);
+
+ /*
+ * Find PATH_INFO.
+ */
+ script = purl;
+ while((script = strchr( script + 1, '/' )) != NULL) {
+ /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
+ struct stat sb;
+
+ *script = '\0';
+ if(is_directory(purl + 1, 1, &sb) == 0) {
+ /* not directory, found script.cgi/PATH_INFO */
+ *script = '/';
+ break;
+ }
+ *script = '/'; /* is directory, find next '/' */
+ }
+ addEnv("PATH", "INFO", script); /* set /PATH_INFO or NULL */
+ addEnv("PATH", "", getenv("PATH"));
+ addEnv("REQUEST", "METHOD", request);
+ if(urlArgs) {
+ char *uri = alloca(strlen(purl) + 2 + strlen(urlArgs));
+ if(uri)
+ sprintf(uri, "%s?%s", purl, urlArgs);
+ addEnv("REQUEST", "URI", uri);
+ } else {
+ addEnv("REQUEST", "URI", purl);
+ }
+ if(script != NULL)
+ *script = '\0'; /* reduce /PATH_INFO */
+ /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
+ addEnv("SCRIPT_NAME", "", purl);
+ addEnv("QUERY_STRING", "", urlArgs);
+ addEnv("SERVER", "SOFTWARE", httpdVersion);
+ addEnv("SERVER", "PROTOCOL", "HTTP/1.0");
+ addEnv("GATEWAY_INTERFACE", "", "CGI/1.1");
+#ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+ addEnv("REMOTE", "ADDR", config->rmt_ip);
+ addEnvPort("REMOTE");
+#else
+ addEnv("REMOTE_ADDR", "", config->rmt_ip);
+#endif
+ if(bodyLen) {
+ char sbl[32];
+
+ sprintf(sbl, "%d", bodyLen);
+ addEnv("CONTENT_LENGTH", "", sbl);
+ }
+ if(cookie)
+ addEnv("HTTP_COOKIE", "", cookie);
+
+#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
+ if (request != request_GET) {
+ addEnvCgi(body);
+ } else {
+ addEnvCgi(urlArgs);
+ }
+#endif
+
+ /* set execve argp[0] without path */
+ argp[0] = strrchr( purl, '/' ) + 1;
+ /* but script argp[0] must have absolute path and chdiring to this */
+ if(realpath(purl + 1, realpath_buff) != NULL) {
+ script = strrchr(realpath_buff, '/');
+ if(script) {
+ *script = '\0';
+ if(chdir(realpath_buff) == 0) {
+ *script = '/';
+ // now run the program. If it fails,
+ // use _exit() so no destructors
+ // get called and make a mess.
+ execve(realpath_buff, argp, config->envp);
+ }
+ }
+ }
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ config->accepted_socket = 1; /* send to stdout */
+#endif
+ sendHeaders(HTTP_NOT_FOUND);
+ _exit(242);
+ } /* end child */
+
+ } while (0);
+
+ if (pid) {
+ /* parent process */
+ int status;
+
+ inFd = fromCgi[0];
+ outFd = toCgi[1];
+ close(fromCgi[1]);
+ close(toCgi[0]);
+ if (body) bb_full_write(outFd, body, bodyLen);
+ close(outFd);
+
+ while (1) {
+ struct timeval timeout;
+ fd_set readSet;
+ char buf[160];
+ int nfound;
+ int count;
+
+ FD_ZERO(&readSet);
+ FD_SET(inFd, &readSet);
+
+ /* Now wait on the set of sockets! */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+ nfound = select(inFd + 1, &readSet, 0, 0, &timeout);
+
+ if (nfound <= 0) {
+ if (waitpid(pid, &status, WNOHANG) > 0) {
+ close(inFd);
+#ifdef DEBUG
+ if (config->debugHttpd) {
+ if (WIFEXITED(status))
+ bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
+ if (WIFSIGNALED(status))
+ bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
+ }
+#endif
+ pid = -1;
+ break;
+ }
+ } else {
+ int s = a_c_w;
+
+ // There is something to read
+ count = bb_full_read(inFd, buf, sizeof(buf)-1);
+ // If a read returns 0 at this point then some type of error has
+ // occurred. Bail now.
+ if (count == 0) break;
+ if (count > 0) {
+ if (firstLine) {
+ /* check to see if the user script added headers */
+ if (strncmp(buf, "HTTP/1.0 200 OK\n", 4) != 0) {
+ bb_full_write(s, "HTTP/1.0 200 OK\n", 16);
+ }
+ if (strstr(buf, "ontent-") == 0) {
+ bb_full_write(s, "Content-type: text/plain\n\n", 26);
+ }
+ firstLine=0;
+ }
+ bb_full_write(s, buf, count);
+#ifdef DEBUG
+ if (config->debugHttpd)
+ fprintf(stderr, "cgi read %d bytes\n", count);
+#endif
+ }
+ }
+ }
+ }
+ return 0;
+}
+#endif /* CONFIG_FEATURE_HTTPD_CGI */
+
+/****************************************************************************
+ *
+ > $Function: sendFile()
+ *
+ * $Description: Send a file response to an HTTP request
+ *
+ * $Parameter:
+ * (const char *) url . . The URL requested.
+ * (char *) buf . . . . . The stack buffer.
+ *
+ * $Return: (int) . . . . . . Always 0.
+ *
+ ****************************************************************************/
+static int sendFile(const char *url, char *buf)
+{
+ char * suffix;
+ int f;
+ const char * const * table;
+ const char * try_suffix;
+
+ suffix = strrchr(url, '.');
+
+ for (table = suffixTable; *table; table += 2)
+ if(suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) {
+ try_suffix += strlen(suffix);
+ if(*try_suffix == 0 || *try_suffix == '.')
+ break;
+ }
+ /* also, if not found, set default as "application/octet-stream"; */
+ config->found_mime_type = *(table+1);
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ if (suffix) {
+ Htaccess * cur;
+
+ for (cur = config->mime_a; cur; cur = cur->next) {
+ if(strcmp(cur->before_colon, suffix) == 0) {
+ config->found_mime_type = cur->after_colon;
+ break;
+ }
+ }
+ }
+#endif /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
+
+#ifdef DEBUG
+ if (config->debugHttpd)
+ fprintf(stderr, "Sending file '%s' Content-type: %s\n",
+ url, config->found_mime_type);
+#endif
+
+ f = open(url, O_RDONLY);
+ if (f >= 0) {
+ int count;
+
+ sendHeaders(HTTP_OK);
+ while ((count = bb_full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
+ bb_full_write(a_c_w, buf, count);
+ }
+ close(f);
+ } else {
+#ifdef DEBUG
+ if (config->debugHttpd)
+ bb_perror_msg("Unable to open '%s'", url);
+#endif
+ sendHeaders(HTTP_NOT_FOUND);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ > $Function: checkPerm()
+ *
+ * $Description: Check the permission file for access.
+ *
+ * If config file isn't present, everything is allowed.
+ * Entries are of the form you can see example from header source
+ *
+ * $Parameters:
+ * (const char *) path . . . . The file path or NULL for ip addresses.
+ * (const char *) request . . . User information to validate.
+ *
+ * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+static int checkPerm(const char *path, const char *request)
+{
+ Htaccess * cur;
+ const char *p;
+ const char *p0;
+
+ int ipaddr = path == NULL;
+ const char *prev = NULL;
+
+ /* This could stand some work */
+ for (cur = ipaddr ? config->ip_a_d : config->auth; cur; cur = cur->next) {
+ p0 = cur->before_colon;
+ if(prev != NULL && strcmp(prev, p0) != 0)
+ continue; /* find next identical */
+ p = cur->after_colon;
+#ifdef DEBUG
+ if (config->debugHttpd)
+ fprintf(stderr,"checkPerm: '%s' ? '%s'\n",
+ (ipaddr ? (*p ? p : "*") : p0), request);
+#endif
+ if(ipaddr) {
+ if(strncmp(p, request, strlen(p)) != 0)
+ continue;
+ return *p0 == 'A'; /* Allow/Deny */
+ } else {
+ int l = strlen(p0);
+
+ if(strncmp(p0, path, l) == 0 &&
+ (l == 1 || path[l] == '/' || path[l] == 0)) {
+ /* path match found. Check request */
+
+ /* for check next /path:user:password */
+ prev = p0;
+#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+ {
+ char *cipher;
+ char *pp;
+ char *u = strchr(request, ':');
+
+ if(u == NULL) {
+ /* bad request, ':' required */
+ continue;
+ }
+ if(strncmp(p, request, u-request) != 0) {
+ /* user uncompared */
+ continue;
+ }
+ pp = strchr(p, ':');
+ if(pp && pp[1] == '$' && pp[2] == '1' &&
+ pp[3] == '$' && pp[4]) {
+ pp++;
+ cipher = pw_encrypt(u+1, pp);
+ if (strcmp(cipher, pp) == 0)
+ return 1; /* Ok */
+ /* unauthorized */
+ continue;
+ }
+ }
+#endif
+ if (strcmp(p, request) == 0)
+ return 1; /* Ok */
+ /* unauthorized */
+ }
+ }
+ } /* for */
+
+ if(ipaddr)
+ return !config->flg_deny_all;
+ return prev == NULL;
+}
+
+#else /* ifndef CONFIG_FEATURE_HTTPD_BASIC_AUTH */
+static int checkPermIP(const char *request)
+{
+ Htaccess * cur;
+ const char *p;
+
+ /* This could stand some work */
+ for (cur = config->ip_a_d; cur; cur = cur->next) {
+ p = cur->after_colon;
+#ifdef DEBUG
+ if (config->debugHttpd)
+ fprintf(stderr, "checkPerm: '%s' ? '%s'\n",
+ (*p ? p : "*"), request);
+#endif
+ if(strncmp(p, request, strlen(p)) == 0)
+ return *cur->before_colon == 'A'; /* Allow/Deny */
+ }
+
+ /* if uncofigured, return 1 - access from all */
+ return !config->flg_deny_all;
+}
+#define checkPerm(null, request) checkPermIP(request)
+#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
+
+
+/****************************************************************************
+ *
+ > $Function: handleIncoming()
+ *
+ * $Description: Handle an incoming http request.
+ *
+ ****************************************************************************/
+static void handleIncoming(void)
+{
+ char *buf = config->buf;
+ char *url;
+ char *purl;
+ int blank = -1;
+ char *urlArgs;
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+ const char *prequest = request_GET;
+ char *body = 0;
+ long length=0;
+ char *cookie = 0;
+#endif
+ char *test;
+ struct stat sb;
+ int ip_allowed;
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ int credentials = -1; /* if not requred this is Ok */
+#endif
+
+ do {
+ int count;
+
+ if (getLine(buf) <= 0)
+ break; /* closed */
+
+ purl = strpbrk(buf, " \t");
+ if(purl == NULL) {
+BAD_REQUEST:
+ sendHeaders(HTTP_BAD_REQUEST);
+ break;
+ }
+ *purl = 0;
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+ if(strcasecmp(buf, prequest) != 0) {
+ prequest = "POST";
+ if(strcasecmp(buf, prequest) != 0) {
+ sendHeaders(HTTP_NOT_IMPLEMENTED);
+ break;
+ }
+ }
+#else
+ if(strcasecmp(buf, request_GET) != 0) {
+ sendHeaders(HTTP_NOT_IMPLEMENTED);
+ break;
+ }
+#endif
+ *purl = ' ';
+ count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
+
+ decodeString(buf, 0);
+ if (count < 1 || buf[0] != '/') {
+ /* Garbled request/URL */
+ goto BAD_REQUEST;
+ }
+ url = alloca(strlen(buf) + 12); /* + sizeof("/index.html\0") */
+ if(url == NULL) {
+ sendHeaders(HTTP_INTERNAL_SERVER_ERROR);
+ break;
+ }
+ strcpy(url, buf);
+ /* extract url args if present */
+ urlArgs = strchr(url, '?');
+ if (urlArgs)
+ *urlArgs++ = 0;
+
+ /* algorithm stolen from libbb bb_simplify_path(),
+ but don`t strdup and reducing trailing slash and protect out root */
+ purl = test = url;
+
+ do {
+ if (*purl == '/') {
+ if (*test == '/') { /* skip duplicate (or initial) slash */
+ continue;
+ } else if (*test == '.') {
+ if (test[1] == '/' || test[1] == 0) { /* skip extra '.' */
+ continue;
+ } else if ((test[1] == '.') && (test[2] == '/' || test[2] == 0)) {
+ ++test;
+ if (purl == url) {
+ /* protect out root */
+ goto BAD_REQUEST;
+ }
+ while (*--purl != '/'); /* omit previous dir */
+ continue;
+ }
+ }
+ }
+ *++purl = *test;
+ } while (*++test);
+
+ *++purl = 0; /* so keep last character */
+ test = purl; /* end ptr */
+
+ /* If URL is directory, adding '/' */
+ if(test[-1] != '/') {
+ if ( is_directory(url + 1, 1, &sb) ) {
+ *test++ = '/';
+ *test = 0;
+ purl = test; /* end ptr */
+ }
+ }
+#ifdef DEBUG
+ if (config->debugHttpd)
+ fprintf(stderr, "url='%s', args=%s\n", url, urlArgs);
+#endif
+
+ test = url;
+ ip_allowed = checkPerm(NULL, config->rmt_ip);
+ while(ip_allowed && (test = strchr( test + 1, '/' )) != NULL) {
+ /* have path1/path2 */
+ *test = '\0';
+ if( is_directory(url + 1, 1, &sb) ) {
+ /* may be having subdir config */
+ parse_conf(url + 1, SUBDIR_PARSE);
+ ip_allowed = checkPerm(NULL, config->rmt_ip);
+ }
+ *test = '/';
+ }
+
+ // read until blank line for HTTP version specified, else parse immediate
+ while (blank >= 0 && (count = getLine(buf)) > 0) {
+
+#ifdef DEBUG
+ if (config->debugHttpd) fprintf(stderr, "Header: '%s'\n", buf);
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+ /* try and do our best to parse more lines */
+ if ((strncasecmp(buf, Content_length, 15) == 0)) {
+ if(prequest != request_GET)
+ length = strtol(buf + 15, 0, 0); // extra read only for POST
+ } else if ((strncasecmp(buf, "Cookie:", 7) == 0)) {
+ for(test = buf + 7; isspace(*test); test++)
+ ;
+ cookie = strdup(test);
+ }
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ if (strncasecmp(buf, "Authorization:", 14) == 0) {
+ /* We only allow Basic credentials.
+ * It shows up as "Authorization: Basic <userid:password>" where
+ * the userid:password is base64 encoded.
+ */
+ for(test = buf + 14; isspace(*test); test++)
+ ;
+ if (strncasecmp(test, "Basic", 5) != 0)
+ continue;
+
+ test += 5; /* decodeBase64() skiping space self */
+ decodeBase64(test);
+ credentials = checkPerm(url, test);
+ }
+#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
+
+ } /* while extra header reading */
+
+
+ if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
+ /* protect listing [/path]/httpd_conf or IP deny */
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+FORBIDDEN: /* protect listing /cgi-bin */
+#endif
+ sendHeaders(HTTP_FORBIDDEN);
+ break;
+ }
+
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ if (credentials <= 0 && checkPerm(url, ":") == 0) {
+ sendHeaders(HTTP_UNAUTHORIZED);
+ break;
+ }
+#endif
+
+ test = url + 1; /* skip first '/' */
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+ /* if strange Content-Length */
+ if (length < 0 || length > MAX_POST_SIZE)
+ break;
+
+ if (length > 0) {
+ body = malloc(length + 1);
+ if (body) {
+ length = bb_full_read(a_c_r, body, length);
+ if(length < 0) // closed
+ length = 0;
+ body[length] = 0; // always null terminate for safety
+ }
+ }
+
+ if (strncmp(test, "cgi-bin", 7) == 0) {
+ if(test[7] == '/' && test[8] == 0)
+ goto FORBIDDEN; // protect listing cgi-bin/
+ sendCgi(url, prequest, urlArgs, body, length, cookie);
+ } else {
+ if (prequest != request_GET)
+ sendHeaders(HTTP_NOT_IMPLEMENTED);
+ else {
+#endif /* CONFIG_FEATURE_HTTPD_CGI */
+ if(purl[-1] == '/')
+ strcpy(purl, "index.html");
+ if ( stat(test, &sb ) == 0 ) {
+ config->ContentLength = sb.st_size;
+ config->last_mod = sb.st_mtime;
+ }
+ sendFile(test, buf);
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ /* unset if non inetd looped */
+ config->ContentLength = -1;
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_CGI
+ }
+ }
+#endif
+
+ } while (0);
+
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+/* from inetd don`t looping: freeing, closing automatic from exit always */
+# ifdef DEBUG
+ if (config->debugHttpd) fprintf(stderr, "closing socket\n");
+# endif
+# ifdef CONFIG_FEATURE_HTTPD_CGI
+ free(body);
+ free(cookie);
+# endif
+ shutdown(a_c_w, SHUT_WR);
+ shutdown(a_c_r, SHUT_RD);
+ close(config->accepted_socket);
+#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
+}
+
+/****************************************************************************
+ *
+ > $Function: miniHttpd()
+ *
+ * $Description: The main http server function.
+ *
+ * Given an open socket fildes, listen for new connections and farm out
+ * the processing as a forked process.
+ *
+ * $Parameters:
+ * (int) server. . . The server socket fildes.
+ *
+ * $Return: (int) . . . . Always 0.
+ *
+ ****************************************************************************/
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+static int miniHttpd(int server)
+{
+ fd_set readfd, portfd;
+
+ FD_ZERO(&portfd);
+ FD_SET(server, &portfd);
+
+ /* copy the ports we are watching to the readfd set */
+ while (1) {
+ readfd = portfd;
+
+ /* Now wait INDEFINATELY on the set of sockets! */
+ if (select(server + 1, &readfd, 0, 0, 0) > 0) {
+ if (FD_ISSET(server, &readfd)) {
+ int on;
+ struct sockaddr_in fromAddr;
+
+ unsigned int addr;
+ socklen_t fromAddrLen = sizeof(fromAddr);
+ int s = accept(server,
+ (struct sockaddr *)&fromAddr, &fromAddrLen);
+
+ if (s < 0) {
+ continue;
+ }
+ config->accepted_socket = s;
+ addr = ntohl(fromAddr.sin_addr.s_addr);
+ sprintf(config->rmt_ip, "%u.%u.%u.%u",
+ (unsigned char)(addr >> 24),
+ (unsigned char)(addr >> 16),
+ (unsigned char)(addr >> 8),
+ addr & 0xff);
+ config->port = ntohs(fromAddr.sin_port);
+#ifdef DEBUG
+ if (config->debugHttpd) {
+ bb_error_msg("connection from IP=%s, port %u\n",
+ config->rmt_ip, config->port);
+ }
+#endif
+ /* set the KEEPALIVE option to cull dead connections */
+ on = 1;
+ setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on));
+
+ if (config->debugHttpd || fork() == 0) {
+ /* This is the spawned thread */
+#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+ /* protect reload config, may be confuse checking */
+ signal(SIGHUP, SIG_IGN);
+#endif
+ handleIncoming();
+ if(!config->debugHttpd)
+ exit(0);
+ }
+ close(s);
+ }
+ }
+ } // while (1)
+ return 0;
+}
+
+#else
+ /* from inetd */
+
+static int miniHttpd(void)
+{
+ struct sockaddr_in fromAddrLen;
+ socklen_t sinlen = sizeof (struct sockaddr_in);
+ unsigned int addr;
+
+ getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen);
+ addr = ntohl(fromAddrLen.sin_addr.s_addr);
+ sprintf(config->rmt_ip, "%u.%u.%u.%u",
+ (unsigned char)(addr >> 24),
+ (unsigned char)(addr >> 16),
+ (unsigned char)(addr >> 8),
+ addr & 0xff);
+ config->port = ntohs(fromAddrLen.sin_port);
+ handleIncoming();
+ return 0;
+}
+#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
+
+#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+static void sighup_handler(int sig)
+{
+ /* set and reset */
+ struct sigaction sa;
+
+ sa.sa_handler = sighup_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGHUP, &sa, NULL);
+ parse_conf(default_path_httpd_conf,
+ sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
+}
+#endif
+
+
+static const char httpd_opts[]="c:d:h:"
+#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+ "e:"
+#define OPT_INC_1 1
+#else
+#define OPT_INC_1 0
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ "r:"
+# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+ "m:"
+# define OPT_INC_2 2
+# else
+# define OPT_INC_2 1
+#endif
+#else
+#define OPT_INC_2 0
+#endif
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ "p:v"
+#ifdef CONFIG_FEATURE_HTTPD_SETUID
+ "u:"
+#endif
+#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
+ ;
+
+#define OPT_CONFIG_FILE (1<<0)
+#define OPT_DECODE_URL (1<<1)
+#define OPT_HOME_HTTPD (1<<2)
+#define OPT_ENCODE_URL (1<<(2+OPT_INC_1))
+#define OPT_REALM (1<<(3+OPT_INC_1))
+#define OPT_MD5 (1<<(4+OPT_INC_1))
+#define OPT_PORT (1<<(3+OPT_INC_1+OPT_INC_2))
+#define OPT_DEBUG (1<<(4+OPT_INC_1+OPT_INC_2))
+#define OPT_SETUID (1<<(5+OPT_INC_1+OPT_INC_2))
+
+
+#ifdef HTTPD_STANDALONE
+int main(int argc, char *argv[])
+#else
+int httpd_main(int argc, char *argv[])
+#endif
+{
+ unsigned long opt;
+ const char *home_httpd = home;
+ char *url_for_decode;
+#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+ const char *url_for_encode;
+#endif
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ const char *s_port;
+#endif
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ int server;
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_SETUID
+ const char *s_uid;
+ long uid = -1;
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+ const char *pass;
+#endif
+
+ config = xcalloc(1, sizeof(*config));
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ config->realm = "Web Server Authentication";
+#endif
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ config->port = 80;
+#endif
+
+ config->ContentLength = -1;
+
+ opt = bb_getopt_ulflags(argc, argv, httpd_opts,
+ &(config->configFile), &url_for_decode, &home_httpd
+#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+ , &url_for_encode
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+ , &(config->realm)
+# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+ , &pass
+# endif
+#endif
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ , &s_port
+#ifdef CONFIG_FEATURE_HTTPD_SETUID
+ , &s_uid
+#endif
+#endif
+ );
+
+ if(opt & OPT_DECODE_URL) {
+ printf("%s", decodeString(url_for_decode, 1));
+ return 0;
+ }
+#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
+ if(opt & OPT_ENCODE_URL) {
+ printf("%s", encodeString(url_for_encode));
+ return 0;
+ }
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+ if(opt & OPT_MD5) {
+ printf("%s\n", pw_encrypt(pass, "$1$"));
+ return 0;
+ }
+#endif
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ if(opt & OPT_PORT)
+ config->port = bb_xgetlarg(s_port, 10, 1, 0xffff);
+ config->debugHttpd = opt & OPT_DEBUG;
+#ifdef CONFIG_FEATURE_HTTPD_SETUID
+ if(opt & OPT_SETUID) {
+ char *e;
+
+ uid = strtol(s_uid, &e, 0);
+ if(*e != '\0') {
+ /* not integer */
+ uid = my_getpwnam(s_uid);
+ }
+ }
+#endif
+#endif
+
+ if(chdir(home_httpd)) {
+ bb_perror_msg_and_die("can`t chdir to %s", home_httpd);
+ }
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ server = openServer();
+# ifdef CONFIG_FEATURE_HTTPD_SETUID
+ /* drop privilegies */
+ if(uid > 0)
+ setuid(uid);
+# endif
+# ifdef CONFIG_FEATURE_HTTPD_CGI
+ addEnvPort("SERVER");
+# endif
+#endif
+
+#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+ sighup_handler(0);
+#else
+ parse_conf(default_path_httpd_conf, FIRST_PARSE);
+#endif
+
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+ if (!config->debugHttpd) {
+ if (daemon(1, 0) < 0) /* don`t change curent directory */
+ bb_perror_msg_and_die("daemon");
+ }
+ return miniHttpd(server);
+#else
+ return miniHttpd();
+#endif
+}
diff --git a/release/src/router/busybox/networking/ifconfig.c b/release/src/router/busybox/networking/ifconfig.c
new file mode 100644
index 00000000..edaeaff3
--- /dev/null
+++ b/release/src/router/busybox/networking/ifconfig.c
@@ -0,0 +1,598 @@
+/* ifconfig
+ *
+ * Similar to the standard Unix ifconfig, but with only the necessary
+ * parts for AF_INET, and without any printing of if info (for now).
+ *
+ * Bjorn Wesen, Axis Communications AB
+ *
+ *
+ * Authors of the original ifconfig was:
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.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 of the License, or (at
+ * your option) any later version.
+ *
+ * $Id: ifconfig.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
+ *
+ */
+
+/*
+ * Heavily modified by Manuel Novoa III Mar 6, 2001
+ *
+ * From initial port to busybox, removed most of the redundancy by
+ * converting to a table-driven approach. Added several (optional)
+ * args missing from initial port.
+ *
+ * Still missing: media, tunnel.
+ *
+ * 2002-04-20
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> /* strcmp and friends */
+#include <ctype.h> /* isdigit and friends */
+#include <stddef.h> /* offsetof */
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_ether.h>
+#endif
+#include "inet_common.h"
+#include "busybox.h"
+
+#ifdef CONFIG_FEATURE_IFCONFIG_SLIP
+# include <net/if_slip.h>
+#endif
+
+/* I don't know if this is needed for busybox or not. Anyone? */
+#define QUESTIONABLE_ALIAS_CASE
+
+
+/* Defines for glibc2.0 users. */
+#ifndef SIOCSIFTXQLEN
+# define SIOCSIFTXQLEN 0x8943
+# define SIOCGIFTXQLEN 0x8942
+#endif
+
+/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
+#ifndef ifr_qlen
+# define ifr_qlen ifr_ifru.ifru_mtu
+#endif
+
+#ifndef IFF_DYNAMIC
+# define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */
+#endif
+
+#ifdef CONFIG_FEATURE_IPV6
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ uint32_t ifr6_prefixlen;
+ int ifr6_ifindex;
+};
+#endif
+
+/*
+ * Here are the bit masks for the "flags" member of struct options below.
+ * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'.
+ * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg.
+ */
+#define N_CLR 0x01
+#define M_CLR 0x02
+#define N_SET 0x04
+#define M_SET 0x08
+#define N_ARG 0x10
+#define M_ARG 0x20
+
+#define M_MASK (M_CLR | M_SET | M_ARG)
+#define N_MASK (N_CLR | N_SET | N_ARG)
+#define SET_MASK (N_SET | M_SET)
+#define CLR_MASK (N_CLR | M_CLR)
+#define SET_CLR_MASK (SET_MASK | CLR_MASK)
+#define ARG_MASK (M_ARG | N_ARG)
+
+/*
+ * Here are the bit masks for the "arg_flags" member of struct options below.
+ */
+
+/*
+ * cast type:
+ * 00 int
+ * 01 char *
+ * 02 HOST_COPY in_ether
+ * 03 HOST_COPY INET_resolve
+ */
+#define A_CAST_TYPE 0x03
+/*
+ * map type:
+ * 00 not a map type (mem_start, io_addr, irq)
+ * 04 memstart (unsigned long)
+ * 08 io_addr (unsigned short)
+ * 0C irq (unsigned char)
+ */
+#define A_MAP_TYPE 0x0C
+#define A_ARG_REQ 0x10 /* Set if an arg is required. */
+#define A_NETMASK 0x20 /* Set if netmask (check for multiple sets). */
+#define A_SET_AFTER 0x40 /* Set a flag at the end. */
+#define A_COLON_CHK 0x80 /* Is this needed? See below. */
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+#define A_HOSTNAME 0x100 /* Set if it is ip addr. */
+#define A_BROADCAST 0x200 /* Set if it is broadcast addr. */
+#else
+#define A_HOSTNAME 0
+#define A_BROADCAST 0
+#endif
+
+/*
+ * These defines are for dealing with the A_CAST_TYPE field.
+ */
+#define A_CAST_CHAR_PTR 0x01
+#define A_CAST_RESOLVE 0x01
+#define A_CAST_HOST_COPY 0x02
+#define A_CAST_HOST_COPY_IN_ETHER A_CAST_HOST_COPY
+#define A_CAST_HOST_COPY_RESOLVE (A_CAST_HOST_COPY | A_CAST_RESOLVE)
+
+/*
+ * These defines are for dealing with the A_MAP_TYPE field.
+ */
+#define A_MAP_ULONG 0x04 /* memstart */
+#define A_MAP_USHORT 0x08 /* io_addr */
+#define A_MAP_UCHAR 0x0C /* irq */
+
+/*
+ * Define the bit masks signifying which operations to perform for each arg.
+ */
+
+#define ARG_METRIC (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_MTU (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_TXQUEUELEN (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_MEM_START (A_ARG_REQ | A_MAP_ULONG)
+#define ARG_IO_ADDR (A_ARG_REQ | A_MAP_ULONG)
+#define ARG_IRQ (A_ARG_REQ | A_MAP_UCHAR)
+#define ARG_DSTADDR (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE)
+#define ARG_NETMASK (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK)
+#define ARG_BROADCAST (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST)
+#define ARG_HW (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER)
+#define ARG_POINTOPOINT (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
+#define ARG_KEEPALIVE (A_ARG_REQ | A_CAST_CHAR_PTR)
+#define ARG_OUTFILL (A_ARG_REQ | A_CAST_CHAR_PTR)
+#define ARG_HOSTNAME (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME)
+#define ARG_ADD_DEL (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
+
+
+/*
+ * Set up the tables. Warning! They must have corresponding order!
+ */
+
+struct arg1opt {
+ const char *name;
+ unsigned short selector;
+ unsigned short ifr_offset;
+};
+
+struct options {
+ const char *name;
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+ const unsigned int flags:6;
+ const unsigned int arg_flags:10;
+#else
+ const unsigned char flags;
+ const unsigned char arg_flags;
+#endif
+ const unsigned short selector;
+};
+
+#define ifreq_offsetof(x) offsetof(struct ifreq, x)
+
+static const struct arg1opt Arg1Opt[] = {
+ {"SIOCSIFMETRIC", SIOCSIFMETRIC, ifreq_offsetof(ifr_metric)},
+ {"SIOCSIFMTU", SIOCSIFMTU, ifreq_offsetof(ifr_mtu)},
+ {"SIOCSIFTXQLEN", SIOCSIFTXQLEN, ifreq_offsetof(ifr_qlen)},
+ {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
+ {"SIOCSIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask)},
+ {"SIOCSIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr)},
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+ {"SIOCSIFHWADDR", SIOCSIFHWADDR, ifreq_offsetof(ifr_hwaddr)},
+#endif
+ {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
+#ifdef SIOCSKEEPALIVE
+ {"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)},
+#endif
+#ifdef SIOCSOUTFILL
+ {"SIOCSOUTFILL", SIOCSOUTFILL, ifreq_offsetof(ifr_data)},
+#endif
+#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.mem_start)},
+ {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.base_addr)},
+ {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.irq)},
+#endif
+ /* Last entry if for unmatched (possibly hostname) arg. */
+#ifdef CONFIG_FEATURE_IPV6
+ {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
+ {"SIOCDIFADDR", SIOCDIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
+#endif
+ {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)},
+};
+
+static const struct options OptArray[] = {
+ {"metric", N_ARG, ARG_METRIC, 0},
+ {"mtu", N_ARG, ARG_MTU, 0},
+ {"txqueuelen", N_ARG, ARG_TXQUEUELEN, 0},
+ {"dstaddr", N_ARG, ARG_DSTADDR, 0},
+ {"netmask", N_ARG, ARG_NETMASK, 0},
+ {"broadcast", N_ARG | M_CLR, ARG_BROADCAST, IFF_BROADCAST},
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+ {"hw", N_ARG, ARG_HW, 0},
+#endif
+ {"pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT},
+#ifdef SIOCSKEEPALIVE
+ {"keepalive", N_ARG, ARG_KEEPALIVE, 0},
+#endif
+#ifdef SIOCSOUTFILL
+ {"outfill", N_ARG, ARG_OUTFILL, 0},
+#endif
+#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ {"mem_start", N_ARG, ARG_MEM_START, 0},
+ {"io_addr", N_ARG, ARG_IO_ADDR, 0},
+ {"irq", N_ARG, ARG_IRQ, 0},
+#endif
+#ifdef CONFIG_FEATURE_IPV6
+ {"add", N_ARG, ARG_ADD_DEL, 0},
+ {"del", N_ARG, ARG_ADD_DEL, 0},
+#endif
+ {"arp", N_CLR | M_SET, 0, IFF_NOARP},
+ {"trailers", N_CLR | M_SET, 0, IFF_NOTRAILERS},
+ {"promisc", N_SET | M_CLR, 0, IFF_PROMISC},
+ {"multicast", N_SET | M_CLR, 0, IFF_MULTICAST},
+ {"allmulti", N_SET | M_CLR, 0, IFF_ALLMULTI},
+ {"dynamic", N_SET | M_CLR, 0, IFF_DYNAMIC},
+ {"up", N_SET, 0, (IFF_UP | IFF_RUNNING)},
+ {"down", N_CLR, 0, IFF_UP},
+ {NULL, 0, ARG_HOSTNAME, (IFF_UP | IFF_RUNNING)}
+};
+
+/*
+ * A couple of prototypes.
+ */
+
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+static int in_ether(char *bufp, struct sockaddr *sap);
+#endif
+
+#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
+extern int interface_opt_a;
+extern int display_interfaces(char *ifname);
+#endif
+
+/*
+ * Our main function.
+ */
+
+int ifconfig_main(int argc, char **argv)
+{
+ struct ifreq ifr;
+ struct sockaddr_in sai;
+#ifdef CONFIG_FEATURE_IPV6
+ struct sockaddr_in6 sai6;
+#endif
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+ struct sockaddr sa;
+#endif
+ const struct arg1opt *a1op;
+ const struct options *op;
+ int sockfd; /* socket fd we use to manipulate stuff with */
+ int goterr;
+ int selector;
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+ unsigned int mask;
+ unsigned int did_flags;
+ unsigned int sai_hostname, sai_netmask;
+#else
+ unsigned char mask;
+ unsigned char did_flags;
+#endif
+ char *p;
+ char host[128];
+
+ goterr = 0;
+ did_flags = 0;
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+ sai_hostname = 0;
+ sai_netmask = 0;
+#endif
+
+ /* skip argv[0] */
+ ++argv;
+ --argc;
+
+#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
+ if ((argc > 0) && (((*argv)[0] == '-') && ((*argv)[1] == 'a') && !(*argv)[2])) {
+ interface_opt_a = 1;
+ --argc;
+ ++argv;
+ }
+#endif
+
+ if (argc <= 1) {
+#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
+ return display_interfaces(argc ? *argv : NULL);
+#else
+ bb_error_msg_and_die
+ ("ifconfig was not compiled with interface status display support.");
+#endif
+ }
+
+ /* Create a channel to the NET kernel. */
+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ bb_perror_msg_and_die("socket");
+ }
+
+ /* get interface name */
+ safe_strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
+
+ /* Process the remaining arguments. */
+ while (*++argv != (char *) NULL) {
+ p = *argv;
+ mask = N_MASK;
+ if (*p == '-') { /* If the arg starts with '-'... */
+ ++p; /* advance past it and */
+ mask = M_MASK; /* set the appropriate mask. */
+ }
+ for (op = OptArray; op->name; op++) { /* Find table entry. */
+ if (strcmp(p, op->name) == 0) { /* If name matches... */
+ if ((mask &= op->flags)) { /* set the mask and go. */
+ goto FOUND_ARG;;
+ }
+ /* If we get here, there was a valid arg with an */
+ /* invalid '-' prefix. */
+ ++goterr;
+ goto LOOP;
+ }
+ }
+
+ /* We fell through, so treat as possible hostname. */
+ a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1;
+ mask = op->arg_flags;
+ goto HOSTNAME;
+
+ FOUND_ARG:
+ if (mask & ARG_MASK) {
+ mask = op->arg_flags;
+ a1op = Arg1Opt + (op - OptArray);
+ if (mask & A_NETMASK & did_flags) {
+ bb_show_usage();
+ }
+ if (*++argv == NULL) {
+ if (mask & A_ARG_REQ) {
+ bb_show_usage();
+ } else {
+ --argv;
+ mask &= A_SET_AFTER; /* just for broadcast */
+ }
+ } else { /* got an arg so process it */
+ HOSTNAME:
+ did_flags |= (mask & (A_NETMASK|A_HOSTNAME));
+ if (mask & A_CAST_HOST_COPY) {
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+ if (mask & A_CAST_RESOLVE) {
+#endif
+#ifdef CONFIG_FEATURE_IPV6
+ char *prefix;
+ int prefix_len = 0;
+#endif
+
+ safe_strncpy(host, *argv, (sizeof host));
+#ifdef CONFIG_FEATURE_IPV6
+ if ((prefix = strchr(host, '/'))) {
+ prefix_len = atol(prefix + 1);
+ if ((prefix_len < 0) || (prefix_len > 128)) {
+ ++goterr;
+ goto LOOP;
+ }
+ *prefix = 0;
+ }
+#endif
+
+ sai.sin_family = AF_INET;
+ sai.sin_port = 0;
+ if (!strcmp(host, bb_INET_default)) {
+ /* Default is special, meaning 0.0.0.0. */
+ sai.sin_addr.s_addr = INADDR_ANY;
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+ } else if (((host[0] == '+') && !host[1]) && (mask & A_BROADCAST) &&
+ (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME)) {
+ /* + is special, meaning broadcast is derived. */
+ sai.sin_addr.s_addr = (~sai_netmask) | (sai_hostname & sai_netmask);
+#endif
+#ifdef CONFIG_FEATURE_IPV6
+ } else if (inet_pton(AF_INET6, host, &sai6.sin6_addr) > 0) {
+ int sockfd6;
+ struct in6_ifreq ifr6;
+
+ memcpy((char *) &ifr6.ifr6_addr,
+ (char *) &sai6.sin6_addr,
+ sizeof(struct in6_addr));
+
+ /* Create a channel to the NET kernel. */
+ if ((sockfd6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ bb_perror_msg_and_die("socket6");
+ }
+ if (ioctl(sockfd6, SIOGIFINDEX, &ifr) < 0) {
+ perror("SIOGIFINDEX");
+ ++goterr;
+ continue;
+ }
+ ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+ ifr6.ifr6_prefixlen = prefix_len;
+ if (ioctl(sockfd6, a1op->selector, &ifr6) < 0) {
+ perror(a1op->name);
+ ++goterr;
+ }
+ continue;
+#endif
+ } else if (inet_aton(host, &sai.sin_addr) == 0) {
+ /* It's not a dotted quad. */
+ ++goterr;
+ continue;
+ }
+#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
+ if (mask & A_HOSTNAME) {
+ sai_hostname = sai.sin_addr.s_addr;
+ }
+ if (mask & A_NETMASK) {
+ sai_netmask = sai.sin_addr.s_addr;
+ }
+#endif
+ p = (char *) &sai;
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+ } else { /* A_CAST_HOST_COPY_IN_ETHER */
+ /* This is the "hw" arg case. */
+ if (strcmp("ether", *argv) || (*++argv == NULL)) {
+ bb_show_usage();
+ }
+ safe_strncpy(host, *argv, (sizeof host));
+ if (in_ether(host, &sa)) {
+ bb_error_msg("invalid hw-addr %s", host);
+ ++goterr;
+ continue;
+ }
+ p = (char *) &sa;
+ }
+#endif
+ memcpy((((char *) (&ifr)) + a1op->ifr_offset),
+ p, sizeof(struct sockaddr));
+ } else {
+ unsigned int i = strtoul(*argv, NULL, 0);
+
+ p = ((char *) (&ifr)) + a1op->ifr_offset;
+#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ if (mask & A_MAP_TYPE) {
+ if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0) {
+ ++goterr;
+ continue;
+ }
+ if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) {
+ *((unsigned char *) p) = i;
+ } else if (mask & A_MAP_USHORT) {
+ *((unsigned short *) p) = i;
+ } else {
+ *((unsigned long *) p) = i;
+ }
+ } else
+#endif
+ if (mask & A_CAST_CHAR_PTR) {
+ *((caddr_t *) p) = (caddr_t) i;
+ } else { /* A_CAST_INT */
+ *((int *) p) = i;
+ }
+ }
+
+ if (ioctl(sockfd, a1op->selector, &ifr) < 0) {
+ perror(a1op->name);
+ ++goterr;
+ continue;
+ }
+#ifdef QUESTIONABLE_ALIAS_CASE
+ if (mask & A_COLON_CHK) {
+ /*
+ * Don't do the set_flag() if the address is an alias with
+ * a - at the end, since it's deleted already! - Roman
+ *
+ * Should really use regex.h here, not sure though how well
+ * it'll go with the cross-platform support etc.
+ */
+ char *ptr;
+ short int found_colon = 0;
+
+ for (ptr = ifr.ifr_name; *ptr; ptr++) {
+ if (*ptr == ':') {
+ found_colon++;
+ }
+ }
+
+ if (found_colon && *(ptr - 1) == '-') {
+ continue;
+ }
+ }
+#endif
+ }
+ if (!(mask & A_SET_AFTER)) {
+ continue;
+ }
+ mask = N_SET;
+ }
+
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
+ perror("SIOCGIFFLAGS");
+ ++goterr;
+ } else {
+ selector = op->selector;
+ if (mask & SET_MASK) {
+ ifr.ifr_flags |= selector;
+ } else {
+ ifr.ifr_flags &= ~selector;
+ }
+ if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
+ perror("SIOCSIFFLAGS");
+ ++goterr;
+ }
+ }
+ LOOP:
+ continue;
+ } /* end of while-loop */
+
+ return goterr;
+}
+
+#ifdef CONFIG_FEATURE_IFCONFIG_HW
+/* Input an Ethernet address and convert to binary. */
+static int in_ether(char *bufp, struct sockaddr *sap)
+{
+ unsigned char *ptr;
+ int i, j;
+ unsigned char val;
+ unsigned char c;
+
+ sap->sa_family = ARPHRD_ETHER;
+ ptr = sap->sa_data;
+
+ i = 0;
+ do {
+ j = val = 0;
+
+ /* We might get a semicolon here - not required. */
+ if (i && (*bufp == ':')) {
+ bufp++;
+ }
+
+ do {
+ c = *bufp;
+ if (((unsigned char)(c - '0')) <= 9) {
+ c -= '0';
+ } else if (((unsigned char)((c|0x20) - 'a')) <= 5) {
+ c = (c|0x20) - ('a'-10);
+ } else if (j && (c == ':' || c == 0)) {
+ break;
+ } else {
+ return -1;
+ }
+ ++bufp;
+ val <<= 4;
+ val += c;
+ } while (++j < 2);
+ *ptr++ = val;
+ } while (++i < ETH_ALEN);
+
+ return (int) (*bufp); /* Error if we don't end at end of string. */
+}
+#endif
diff --git a/release/src/router/busybox/networking/ifupdown.c b/release/src/router/busybox/networking/ifupdown.c
new file mode 100644
index 00000000..bae9f4ea
--- /dev/null
+++ b/release/src/router/busybox/networking/ifupdown.c
@@ -0,0 +1,1453 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ifupdown for busybox
+ * Copyright (c) 2002 Glenn McGrath <bug1@optushome.com.au>
+ * Copyright (c) 2003 Erik Andersen <andersen@codepoet.org>
+ *
+ * Based on ifupdown v 0.6.4 by Anthony Towns
+ * Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au>
+ *
+ * Changes to upstream version
+ * Remove checks for kernel version, assume kernel version 2.2.0 or better.
+ * Lines in the interfaces file cannot wrap.
+ * To adhere to the FHS, the default state file is /var/run/ifstate.
+ *
+ * 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 of the License, 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.
+ */
+
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libbb.h"
+
+#define MAX_OPT_DEPTH 10
+#define EUNBALBRACK 10001
+#define EUNDEFVAR 10002
+#define EUNBALPER 10000
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+#define MAX_INTERFACE_LENGTH 10
+#endif
+
+#if 0
+#define debug_noise(fmt, args...) printf(fmt, ## args)
+#else
+#define debug_noise(fmt, args...)
+#endif
+
+/* Forward declaration */
+struct interface_defn_t;
+
+typedef int (execfn)(char *command);
+typedef int (command_set)(struct interface_defn_t *ifd, execfn *e);
+
+extern llist_t *llist_add_to_end(llist_t *list_head, char *data)
+{
+ llist_t *new_item, *tmp, *prev;
+
+ new_item = xmalloc(sizeof(llist_t));
+ new_item->data = data;
+ new_item->link = NULL;
+
+ prev = NULL;
+ tmp = list_head;
+ while(tmp) {
+ prev = tmp;
+ tmp = tmp->link;
+ }
+ if (prev) {
+ prev->link = new_item;
+ } else {
+ list_head = new_item;
+ }
+
+ return(list_head);
+}
+
+struct method_t
+{
+ char *name;
+ command_set *up;
+ command_set *down;
+};
+
+struct address_family_t
+{
+ char *name;
+ int n_methods;
+ struct method_t *method;
+};
+
+struct mapping_defn_t
+{
+ struct mapping_defn_t *next;
+
+ int max_matches;
+ int n_matches;
+ char **match;
+
+ char *script;
+
+ int max_mappings;
+ int n_mappings;
+ char **mapping;
+};
+
+struct variable_t
+{
+ char *name;
+ char *value;
+};
+
+struct interface_defn_t
+{
+ struct interface_defn_t *prev;
+ struct interface_defn_t *next;
+
+ char *iface;
+ struct address_family_t *address_family;
+ struct method_t *method;
+
+ int automatic;
+
+ int max_options;
+ int n_options;
+ struct variable_t *option;
+};
+
+struct interfaces_file_t
+{
+ llist_t *autointerfaces;
+ llist_t *ifaces;
+ struct mapping_defn_t *mappings;
+};
+
+static char no_act = 0;
+static char verbose = 0;
+static char **environ = NULL;
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+
+static unsigned int count_bits(unsigned int a)
+{
+ unsigned int result;
+ result = (a & 0x55) + ((a >> 1) & 0x55);
+ result = (result & 0x33) + ((result >> 2) & 0x33);
+ return((result & 0x0F) + ((result >> 4) & 0x0F));
+}
+
+static int count_netmask_bits(char *dotted_quad)
+{
+ unsigned int result, a, b, c, d;
+ /* Found a netmask... Check if it is dotted quad */
+ if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
+ return -1;
+ result = count_bits(a);
+ result += count_bits(b);
+ result += count_bits(c);
+ result += count_bits(d);
+ return ((int)result);
+}
+#endif
+
+static void addstr(char **buf, size_t *len, size_t *pos, char *str, size_t str_length)
+{
+ if (*pos + str_length >= *len) {
+ char *newbuf;
+
+ newbuf = xrealloc(*buf, *len * 2 + str_length + 1);
+ *buf = newbuf;
+ *len = *len * 2 + str_length + 1;
+ }
+
+ while (str_length-- >= 1) {
+ (*buf)[(*pos)++] = *str;
+ str++;
+ }
+ (*buf)[*pos] = '\0';
+}
+
+static int strncmpz(char *l, char *r, size_t llen)
+{
+ int i = strncmp(l, r, llen);
+
+ if (i == 0) {
+ return(-r[llen]);
+ } else {
+ return(i);
+ }
+}
+
+static char *get_var(char *id, size_t idlen, struct interface_defn_t *ifd)
+{
+ int i;
+
+ if (strncmpz(id, "iface", idlen) == 0) {
+ char *result;
+ static char label_buf[20];
+ strncpy(label_buf, ifd->iface, 19);
+ label_buf[19]=0;
+ result = strchr(label_buf, ':');
+ if (result) {
+ *result=0;
+ }
+ return( label_buf);
+ } else if (strncmpz(id, "label", idlen) == 0) {
+ return (ifd->iface);
+ } else {
+ for (i = 0; i < ifd->n_options; i++) {
+ if (strncmpz(id, ifd->option[i].name, idlen) == 0) {
+ return (ifd->option[i].value);
+ }
+ }
+ }
+
+ return(NULL);
+}
+
+static char *parse(char *command, struct interface_defn_t *ifd)
+{
+
+ char *result = NULL;
+ size_t pos = 0, len = 0;
+ size_t old_pos[MAX_OPT_DEPTH] = { 0 };
+ int okay[MAX_OPT_DEPTH] = { 1 };
+ int opt_depth = 1;
+
+ while (*command) {
+ switch (*command) {
+
+ default:
+ addstr(&result, &len, &pos, command, 1);
+ command++;
+ break;
+ case '\\':
+ if (command[1]) {
+ addstr(&result, &len, &pos, command + 1, 1);
+ command += 2;
+ } else {
+ addstr(&result, &len, &pos, command, 1);
+ command++;
+ }
+ break;
+ case '[':
+ if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
+ old_pos[opt_depth] = pos;
+ okay[opt_depth] = 1;
+ opt_depth++;
+ command += 2;
+ } else {
+ addstr(&result, &len, &pos, "[", 1);
+ command++;
+ }
+ break;
+ case ']':
+ if (command[1] == ']' && opt_depth > 1) {
+ opt_depth--;
+ if (!okay[opt_depth]) {
+ pos = old_pos[opt_depth];
+ result[pos] = '\0';
+ }
+ command += 2;
+ } else {
+ addstr(&result, &len, &pos, "]", 1);
+ command++;
+ }
+ break;
+ case '%':
+ {
+ char *nextpercent;
+ char *varvalue;
+
+ command++;
+ nextpercent = strchr(command, '%');
+ if (!nextpercent) {
+ errno = EUNBALPER;
+ free(result);
+ return (NULL);
+ }
+
+ varvalue = get_var(command, nextpercent - command, ifd);
+
+ if (varvalue) {
+ addstr(&result, &len, &pos, varvalue, bb_strlen(varvalue));
+ } else {
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+ /* Sigh... Add a special case for 'ip' to convert from
+ * dotted quad to bit count style netmasks. */
+ if (strncmp(command, "bnmask", 6)==0) {
+ int res;
+ varvalue = get_var("netmask", 7, ifd);
+ if (varvalue && (res=count_netmask_bits(varvalue)) > 0) {
+ char argument[255];
+ sprintf(argument, "%d", res);
+ addstr(&result, &len, &pos, argument, bb_strlen(argument));
+ command = nextpercent + 1;
+ break;
+ }
+ }
+#endif
+ okay[opt_depth - 1] = 0;
+ }
+
+ command = nextpercent + 1;
+ }
+ break;
+ }
+ }
+
+ if (opt_depth > 1) {
+ errno = EUNBALBRACK;
+ free(result);
+ return(NULL);
+ }
+
+ if (!okay[0]) {
+ errno = EUNDEFVAR;
+ free(result);
+ return(NULL);
+ }
+
+ return(result);
+}
+
+static int execute(char *command, struct interface_defn_t *ifd, execfn *exec)
+{
+ char *out;
+ int ret;
+
+ out = parse(command, ifd);
+ if (!out) {
+ return(0);
+ }
+ ret = (*exec) (out);
+
+ free(out);
+ return(1);
+}
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPX
+static int static_up_ipx(struct interface_defn_t *ifd, execfn *exec)
+{
+ return(execute("ipx_interface add %iface% %frame% %netnum%", ifd, exec));
+}
+
+static int static_down_ipx(struct interface_defn_t *ifd, execfn *exec)
+{
+ return(execute("ipx_interface del %iface% %frame%", ifd, exec));
+}
+
+static int dynamic_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ return(execute("ipx_interface add %iface% %frame%", ifd, exec));
+}
+
+static int dynamic_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ return(execute("ipx_interface del %iface% %frame%", ifd, exec));
+}
+
+static struct method_t methods_ipx[] = {
+ { "dynamic", dynamic_up, dynamic_down, },
+ { "static", static_up_ipx, static_down_ipx, },
+};
+
+struct address_family_t addr_ipx = {
+ "ipx",
+ sizeof(methods_ipx) / sizeof(struct method_t),
+ methods_ipx
+};
+#endif /* IFUP_FEATURE_IPX */
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPV6
+static int loopback_up6(struct interface_defn_t *ifd, execfn *exec)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+ int result;
+ result =execute("ip addr add ::1 dev %iface% label %label%", ifd, exec);
+ result += execute("ip link set %iface% up", ifd, exec);
+ return( result);
+#else
+ return( execute("ifconfig %iface% add ::1", ifd, exec));
+#endif
+}
+
+static int loopback_down6(struct interface_defn_t *ifd, execfn *exec)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+ return(execute("ip link set %iface% down", ifd, exec));
+#else
+ return(execute("ifconfig %iface% del ::1", ifd, exec));
+#endif
+}
+
+static int static_up6(struct interface_defn_t *ifd, execfn *exec)
+{
+ int result;
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+ result = execute("ip addr add %address%/%netmask% dev %iface% label %label%", ifd, exec);
+ result += execute("ip link set %iface% up", ifd, exec);
+ result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec);
+#else
+ result = execute("ifconfig %iface% [[media %media%]] [[hw %hwaddress%]] [[mtu %mtu%]] up", ifd, exec);
+ result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec);
+ result += execute("[[ route -A inet6 add ::/0 gw %gateway% ]]", ifd, exec);
+#endif
+ return( result);
+}
+
+static int static_down6(struct interface_defn_t *ifd, execfn *exec)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+ return(execute("ip link set %iface% down", ifd, exec));
+#else
+ return(execute("ifconfig %iface% down", ifd, exec));
+#endif
+}
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+static int v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ int result;
+ result = execute("ip tunnel add %iface% mode sit remote "
+ "%endpoint% [[local %local%]] [[ttl %ttl%]]", ifd, exec);
+ result += execute("ip link set %iface% up", ifd, exec);
+ result += execute("ip addr add %address%/%netmask% dev %iface% label %label%", ifd, exec);
+ result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec);
+ return( result);
+}
+
+static int v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
+{
+ return( execute("ip tunnel del %iface%", ifd, exec));
+}
+#endif
+
+static struct method_t methods6[] = {
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+ { "v4tunnel", v4tunnel_up, v4tunnel_down, },
+#endif
+ { "static", static_up6, static_down6, },
+ { "loopback", loopback_up6, loopback_down6, },
+};
+
+struct address_family_t addr_inet6 = {
+ "inet6",
+ sizeof(methods6) / sizeof(struct method_t),
+ methods6
+};
+#endif /* CONFIG_FEATURE_IFUPDOWN_IPV6 */
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPV4
+static int loopback_up(struct interface_defn_t *ifd, execfn *exec)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+ int result;
+ result = execute("ip addr add 127.0.0.1/8 dev %iface% label %label%", ifd, exec);
+ result += execute("ip link set %iface% up", ifd, exec);
+ return(result);
+#else
+ return( execute("ifconfig %iface% 127.0.0.1 up", ifd, exec));
+#endif
+}
+
+static int loopback_down(struct interface_defn_t *ifd, execfn *exec)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+ int result;
+ result = execute("ip addr flush dev %iface%", ifd, exec);
+ result += execute("ip link set %iface% down", ifd, exec);
+ return(result);
+#else
+ return( execute("ifconfig %iface% 127.0.0.1 down", ifd, exec));
+#endif
+}
+
+static int static_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ int result;
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+ result = execute("ip addr add %address%/%bnmask% [[broadcast %broadcast%]] "
+ "dev %iface% label %label%", ifd, exec);
+ result += execute("ip link set %iface% up", ifd, exec);
+ result += execute("[[ ip route add default via %gateway% dev %iface% ]]", ifd, exec);
+#else
+ result = execute("ifconfig %iface% %address% netmask %netmask% "
+ "[[broadcast %broadcast%]] [[pointopoint %pointopoint%]] "
+ "[[media %media%]] [[mtu %mtu%]] [[hw %hwaddress%]] up",
+ ifd, exec);
+ result += execute("[[ route add default gw %gateway% %iface% ]]", ifd, exec);
+#endif
+ return(result);
+}
+
+static int static_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ int result;
+#ifdef CONFIG_FEATURE_IFUPDOWN_IP
+ result = execute("ip addr flush dev %iface%", ifd, exec);
+ result += execute("ip link set %iface% down", ifd, exec);
+#else
+ result = execute("[[ route del default gw %gateway% %iface% ]]", ifd, exec);
+ result += execute("ifconfig %iface% down", ifd, exec);
+#endif
+ return(result);
+}
+
+static int execable(char *program)
+{
+ struct stat buf;
+ if (0 == stat(program, &buf)) {
+ if (S_ISREG(buf.st_mode) && (S_IXUSR & buf.st_mode)) {
+ return(1);
+ }
+ }
+ return(0);
+}
+
+static int dhcp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ if (execable("/sbin/udhcpc")) {
+ return( execute("udhcpc -n -p /var/run/udhcpc.%iface%.pid -i "
+ "%iface% [[-H %hostname%]] [[-c %clientid%]]", ifd, exec));
+ } else if (execable("/sbin/pump")) {
+ return( execute("pump -i %iface% [[-h %hostname%]] [[-l %leasehours%]]", ifd, exec));
+ } else if (execable("/sbin/dhclient")) {
+ return( execute("dhclient -pf /var/run/dhclient.%iface%.pid %iface%", ifd, exec));
+ } else if (execable("/sbin/dhcpcd")) {
+ return( execute("dhcpcd [[-h %hostname%]] [[-i %vendor%]] [[-I %clientid%]] "
+ "[[-l %leasetime%]] %iface%", ifd, exec));
+ }
+ return(0);
+}
+
+static int dhcp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ int result = 0;
+ if (execable("/sbin/udhcpc")) {
+ execute("kill -9 `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
+ } else if (execable("/sbin/pump")) {
+ result = execute("pump -i %iface% -k", ifd, exec);
+ } else if (execable("/sbin/dhclient")) {
+ execute("kill -9 `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
+ } else if (execable("/sbin/dhcpcd")) {
+ result = execute("dhcpcd -k %iface%", ifd, exec);
+ }
+ return (result || execute("ifconfig %iface% down", ifd, exec));
+}
+
+static int bootp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ return( execute("bootpc [[--bootfile %bootfile%]] --dev %iface% "
+ "[[--server %server%]] [[--hwaddr %hwaddr%]] "
+ "--returniffail --serverbcast", ifd, exec));
+}
+
+static int bootp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ return( execute("ifconfig down %iface%", ifd, exec));
+}
+
+static int ppp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ return( execute("pon [[%provider%]]", ifd, exec));
+}
+
+static int ppp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ return( execute("poff [[%provider%]]", ifd, exec));
+}
+
+static int wvdial_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ return( execute("/sbin/start-stop-daemon --start -x /usr/bin/wvdial "
+ "-p /var/run/wvdial.%iface% -b -m -- [[ %provider% ]]", ifd, exec));
+}
+
+static int wvdial_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ return( execute("/sbin/start-stop-daemon --stop -x /usr/bin/wvdial "
+ "-p /var/run/wvdial.%iface% -s 2", ifd, exec));
+}
+
+static struct method_t methods[] =
+{
+ { "wvdial", wvdial_up, wvdial_down, },
+ { "ppp", ppp_up, ppp_down, },
+ { "static", static_up, static_down, },
+ { "bootp", bootp_up, bootp_down, },
+ { "dhcp", dhcp_up, dhcp_down, },
+ { "loopback", loopback_up, loopback_down, },
+};
+
+struct address_family_t addr_inet =
+{
+ "inet",
+ sizeof(methods) / sizeof(struct method_t),
+ methods
+};
+
+#endif /* ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 */
+
+static char *next_word(char **buf)
+{
+ unsigned short length;
+ char *word;
+
+ if ((buf == NULL) || (*buf == NULL) || (**buf == '\0')) {
+ return NULL;
+ }
+
+ /* Skip over leading whitespace */
+ word = *buf;
+ while (isspace(*word)) {
+ ++word;
+ }
+
+ /* Skip over comments */
+ if (*word == '#') {
+ return(NULL);
+ }
+
+ /* Find the length of this word */
+ length = strcspn(word, " \t\n");
+ if (length == 0) {
+ return(NULL);
+ }
+ *buf = word + length;
+ /*DBU:[dave@cray.com] if we are already at EOL dont't increment beyond it */
+ if (**buf) {
+ **buf = '\0';
+ (*buf)++;
+ }
+
+ return word;
+}
+
+static struct address_family_t *get_address_family(struct address_family_t *af[], char *name)
+{
+ int i;
+
+ for (i = 0; af[i]; i++) {
+ if (strcmp(af[i]->name, name) == 0) {
+ return af[i];
+ }
+ }
+ return NULL;
+}
+
+static struct method_t *get_method(struct address_family_t *af, char *name)
+{
+ int i;
+
+ for (i = 0; i < af->n_methods; i++) {
+ if (strcmp(af->method[i].name, name) == 0) {
+ return &af->method[i];
+ }
+ }
+ return(NULL);
+}
+
+static int duplicate_if(struct interface_defn_t *ifa, struct interface_defn_t *ifb)
+{
+ if (strcmp(ifa->iface, ifb->iface) != 0) {
+ return(0);
+ }
+ if (ifa->address_family != ifb->address_family) {
+ return(0);
+ }
+ return(1);
+}
+
+static const llist_t *find_list_string(const llist_t *list, const char *string)
+{
+ while (list) {
+ if (strcmp(list->data, string) == 0) {
+ return(list);
+ }
+ list = list->link;
+ }
+ return(NULL);
+}
+
+static struct interfaces_file_t *read_interfaces(char *filename)
+{
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+ struct mapping_defn_t *currmap = NULL;
+#endif
+ struct interface_defn_t *currif = NULL;
+ struct interfaces_file_t *defn;
+ FILE *f;
+ char *firstword;
+ char *buf;
+
+ enum { NONE, IFACE, MAPPING } currently_processing = NONE;
+
+ defn = xmalloc(sizeof(struct interfaces_file_t));
+ defn->autointerfaces = NULL;
+ defn->mappings = NULL;
+ defn->ifaces = NULL;
+
+ f = bb_xfopen(filename, "r");
+
+ while ((buf = bb_get_chomped_line_from_file(f)) != NULL) {
+ char *buf_ptr = buf;
+
+ firstword = next_word(&buf_ptr);
+ if (firstword == NULL) {
+ free(buf);
+ continue; /* blank line */
+ }
+
+ if (strcmp(firstword, "mapping") == 0) {
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+ currmap = xmalloc(sizeof(struct mapping_defn_t));
+ currmap->max_matches = 0;
+ currmap->n_matches = 0;
+ currmap->match = NULL;
+
+ while ((firstword = next_word(&buf_ptr)) != NULL) {
+ if (currmap->max_matches == currmap->n_matches) {
+ currmap->max_matches = currmap->max_matches * 2 + 1;
+ currmap->match = xrealloc(currmap->match, sizeof(currmap->match) * currmap->max_matches);
+ }
+
+ currmap->match[currmap->n_matches++] = bb_xstrdup(firstword);
+ }
+ currmap->max_mappings = 0;
+ currmap->n_mappings = 0;
+ currmap->mapping = NULL;
+ currmap->script = NULL;
+ {
+ struct mapping_defn_t **where = &defn->mappings;
+ while (*where != NULL) {
+ where = &(*where)->next;
+ }
+ *where = currmap;
+ currmap->next = NULL;
+ }
+ debug_noise("Added mapping\n");
+#endif
+ currently_processing = MAPPING;
+ } else if (strcmp(firstword, "iface") == 0) {
+ {
+ char *iface_name;
+ char *address_family_name;
+ char *method_name;
+ struct address_family_t *addr_fams[] = {
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPV4
+ &addr_inet,
+#endif
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPV6
+ &addr_inet6,
+#endif
+#ifdef CONFIG_FEATURE_IFUPDOWN_IPX
+ &addr_ipx,
+#endif
+ NULL
+ };
+
+ currif = xmalloc(sizeof(struct interface_defn_t));
+ iface_name = next_word(&buf_ptr);
+ address_family_name = next_word(&buf_ptr);
+ method_name = next_word(&buf_ptr);
+
+ if (buf_ptr == NULL) {
+ bb_error_msg("too few parameters for line \"%s\"", buf);
+ return NULL;
+ }
+
+ /* ship any trailing whitespace */
+ while (isspace(*buf_ptr)) {
+ ++buf_ptr;
+ }
+
+ if (buf_ptr[0] != '\0') {
+ bb_error_msg("too many parameters \"%s\"", buf);
+ return NULL;
+ }
+
+ currif->iface = bb_xstrdup(iface_name);
+
+ currif->address_family = get_address_family(addr_fams, address_family_name);
+ if (!currif->address_family) {
+ bb_error_msg("unknown address type \"%s\"", buf);
+ return NULL;
+ }
+
+ currif->method = get_method(currif->address_family, method_name);
+ if (!currif->method) {
+ bb_error_msg("unknown method \"%s\"", buf);
+ return NULL;
+ }
+
+ currif->automatic = 1;
+ currif->max_options = 0;
+ currif->n_options = 0;
+ currif->option = NULL;
+
+ {
+ struct interface_defn_t *tmp;
+ llist_t *iface_list;
+ iface_list = defn->ifaces;
+ while (iface_list) {
+ tmp = (struct interface_defn_t *) iface_list->data;
+ if (duplicate_if(tmp, currif)) {
+ bb_error_msg("duplicate interface \"%s\"", tmp->iface);
+ return NULL;
+ }
+ iface_list = iface_list->link;
+ }
+
+ defn->ifaces = llist_add_to_end(defn->ifaces, (char*)currif);
+ }
+ debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name);
+ }
+ currently_processing = IFACE;
+ } else if (strcmp(firstword, "auto") == 0) {
+ while ((firstword = next_word(&buf_ptr)) != NULL) {
+
+ /* Check the interface isnt already listed */
+ if (find_list_string(defn->autointerfaces, firstword)) {
+ bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf);
+ }
+
+ /* Add the interface to the list */
+ defn->autointerfaces = llist_add_to_end(defn->autointerfaces, strdup(firstword));
+ debug_noise("\nauto %s\n", firstword);
+ }
+ currently_processing = NONE;
+ } else {
+ switch (currently_processing) {
+ case IFACE:
+ {
+ int i;
+
+ if (bb_strlen(buf_ptr) == 0) {
+ bb_error_msg("option with empty value \"%s\"", buf);
+ return NULL;
+ }
+
+ if (strcmp(firstword, "up") != 0
+ && strcmp(firstword, "down") != 0
+ && strcmp(firstword, "pre-up") != 0
+ && strcmp(firstword, "post-down") != 0) {
+ for (i = 0; i < currif->n_options; i++) {
+ if (strcmp(currif->option[i].name, firstword) == 0) {
+ bb_error_msg("duplicate option \"%s\"", buf);
+ return NULL;
+ }
+ }
+ }
+ }
+ if (currif->n_options >= currif->max_options) {
+ struct variable_t *opt;
+
+ currif->max_options = currif->max_options + 10;
+ opt = xrealloc(currif->option, sizeof(*opt) * currif->max_options);
+ currif->option = opt;
+ }
+ currif->option[currif->n_options].name = bb_xstrdup(firstword);
+ currif->option[currif->n_options].value = bb_xstrdup(buf_ptr);
+ if (!currif->option[currif->n_options].name) {
+ perror(filename);
+ return NULL;
+ }
+ if (!currif->option[currif->n_options].value) {
+ perror(filename);
+ return NULL;
+ }
+ debug_noise("\t%s=%s\n", currif->option[currif->n_options].name,
+ currif->option[currif->n_options].value);
+ currif->n_options++;
+ break;
+ case MAPPING:
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+ if (strcmp(firstword, "script") == 0) {
+ if (currmap->script != NULL) {
+ bb_error_msg("duplicate script in mapping \"%s\"", buf);
+ return NULL;
+ } else {
+ currmap->script = bb_xstrdup(next_word(&buf_ptr));
+ }
+ } else if (strcmp(firstword, "map") == 0) {
+ if (currmap->max_mappings == currmap->n_mappings) {
+ currmap->max_mappings = currmap->max_mappings * 2 + 1;
+ currmap->mapping = xrealloc(currmap->mapping, sizeof(char *) * currmap->max_mappings);
+ }
+ currmap->mapping[currmap->n_mappings] = bb_xstrdup(next_word(&buf_ptr));
+ currmap->n_mappings++;
+ } else {
+ bb_error_msg("misplaced option \"%s\"", buf);
+ return NULL;
+ }
+#endif
+ break;
+ case NONE:
+ default:
+ bb_error_msg("misplaced option \"%s\"", buf);
+ return NULL;
+ }
+ }
+ free(buf);
+ }
+ if (ferror(f) != 0) {
+ bb_perror_msg_and_die("%s", filename);
+ }
+ fclose(f);
+
+ return defn;
+}
+
+static char *setlocalenv(char *format, char *name, char *value)
+{
+ char *result;
+ char *here;
+ char *there;
+
+ result = xmalloc(bb_strlen(format) + bb_strlen(name) + bb_strlen(value) + 1);
+
+ sprintf(result, format, name, value);
+
+ for (here = there = result; *there != '=' && *there; there++) {
+ if (*there == '-')
+ *there = '_';
+ if (isalpha(*there))
+ *there = toupper(*there);
+
+ if (isalnum(*there) || *there == '_') {
+ *here = *there;
+ here++;
+ }
+ }
+ memmove(here, there, bb_strlen(there) + 1);
+
+ return result;
+}
+
+static void set_environ(struct interface_defn_t *iface, char *mode)
+{
+ char **environend;
+ int i;
+ const int n_env_entries = iface->n_options + 5;
+ char **ppch;
+
+ if (environ != NULL) {
+ for (ppch = environ; *ppch; ppch++) {
+ free(*ppch);
+ *ppch = NULL;
+ }
+ free(environ);
+ environ = NULL;
+ }
+ environ = xmalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ ));
+ environend = environ;
+ *environend = NULL;
+
+ for (i = 0; i < iface->n_options; i++) {
+ if (strcmp(iface->option[i].name, "up") == 0
+ || strcmp(iface->option[i].name, "down") == 0
+ || strcmp(iface->option[i].name, "pre-up") == 0
+ || strcmp(iface->option[i].name, "post-down") == 0) {
+ continue;
+ }
+ *(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
+ *environend = NULL;
+ }
+
+ *(environend++) = setlocalenv("%s=%s", "IFACE", iface->iface);
+ *environend = NULL;
+ *(environend++) = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
+ *environend = NULL;
+ *(environend++) = setlocalenv("%s=%s", "METHOD", iface->method->name);
+ *environend = NULL;
+ *(environend++) = setlocalenv("%s=%s", "MODE", mode);
+ *environend = NULL;
+ *(environend++) = setlocalenv("%s=%s", "PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
+ *environend = NULL;
+}
+
+static int doit(char *str)
+{
+ if (verbose || no_act) {
+ printf("%s\n", str);
+ }
+ if (!no_act) {
+ pid_t child;
+ int status;
+
+ fflush(NULL);
+ switch (child = fork()) {
+ case -1: /* failure */
+ return 0;
+ case 0: /* child */
+ execle("/bin/sh", "/bin/sh", "-c", str, NULL, environ);
+ exit(127);
+ }
+ waitpid(child, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ return 0;
+ }
+ }
+ return (1);
+}
+
+static int execute_all(struct interface_defn_t *ifd, execfn *exec, const char *opt)
+{
+ int i;
+ char *buf[2];
+
+ for (i = 0; i < ifd->n_options; i++) {
+ if (strcmp(ifd->option[i].name, opt) == 0) {
+ if (!(*exec) (ifd->option[i].value)) {
+ return 0;
+ }
+ }
+ }
+
+ bb_xasprintf(&buf[0], "/etc/network/if-%s.d", opt);
+ buf[1] = NULL;
+
+ run_parts(buf, 2, environ);
+ free(buf[0]);
+ return (1);
+}
+
+static int check(char *str) {
+ return str != NULL;
+}
+
+static int iface_up(struct interface_defn_t *iface)
+{
+ int result;
+ if (!iface->method->up(iface,check)) return -1;
+ set_environ(iface, "start");
+ result = execute_all(iface, doit, "pre-up");
+ result += iface->method->up(iface, doit);
+ result += execute_all(iface, doit, "up");
+ return(result);
+}
+
+static int iface_down(struct interface_defn_t *iface)
+{
+ int result;
+ if (!iface->method->down(iface,check)) return -1;
+ set_environ(iface, "stop");
+ result = execute_all(iface, doit, "down");
+ result += iface->method->down(iface, doit);
+ result += execute_all(iface, doit, "post-down");
+ return(result);
+}
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+static int popen2(FILE **in, FILE **out, char *command, ...)
+{
+ va_list ap;
+ char *argv[11] = { command };
+ int argc;
+ int infd[2], outfd[2];
+ pid_t pid;
+
+ argc = 1;
+ va_start(ap, command);
+ while ((argc < 10) && (argv[argc] = va_arg(ap, char *))) {
+ argc++;
+ }
+ argv[argc] = NULL; /* make sure */
+ va_end(ap);
+
+ if (pipe(infd) != 0) {
+ return 0;
+ }
+
+ if (pipe(outfd) != 0) {
+ close(infd[0]);
+ close(infd[1]);
+ return 0;
+ }
+
+ fflush(NULL);
+ switch (pid = fork()) {
+ case -1: /* failure */
+ close(infd[0]);
+ close(infd[1]);
+ close(outfd[0]);
+ close(outfd[1]);
+ return 0;
+ case 0: /* child */
+ dup2(infd[0], 0);
+ dup2(outfd[1], 1);
+ close(infd[0]);
+ close(infd[1]);
+ close(outfd[0]);
+ close(outfd[1]);
+ execvp(command, argv);
+ exit(127);
+ default: /* parent */
+ *in = fdopen(infd[1], "w");
+ *out = fdopen(outfd[0], "r");
+ close(infd[0]);
+ close(outfd[1]);
+ return pid;
+ }
+ /* unreached */
+}
+
+static char * run_mapping(char *physical, struct mapping_defn_t * map)
+{
+ FILE *in, *out;
+ int i, status;
+ pid_t pid;
+
+ char *logical = bb_xstrdup(physical);
+
+ /* Run the mapping script. */
+ pid = popen2(&in, &out, map->script, physical, NULL);
+
+ /* popen2() returns 0 on failure. */
+ if (pid == 0)
+ return logical;
+
+ /* Write mappings to stdin of mapping script. */
+ for (i = 0; i < map->n_mappings; i++) {
+ fprintf(in, "%s\n", map->mapping[i]);
+ }
+ fclose(in);
+ waitpid(pid, &status, 0);
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ /* If the mapping script exited successfully, try to
+ * grab a line of output and use that as the name of the
+ * logical interface. */
+ char *new_logical = (char *)xmalloc(MAX_INTERFACE_LENGTH);
+
+ if (fgets(new_logical, MAX_INTERFACE_LENGTH, out)) {
+ /* If we are able to read a line of output from the script,
+ * remove any trailing whitespace and use this value
+ * as the name of the logical interface. */
+ char *pch = new_logical + bb_strlen(new_logical) - 1;
+
+ while (pch >= new_logical && isspace(*pch))
+ *(pch--) = '\0';
+
+ free(logical);
+ logical = new_logical;
+ } else {
+ /* If we are UNABLE to read a line of output, discard are
+ * freshly allocated memory. */
+ free(new_logical);
+ }
+ }
+
+ fclose(out);
+
+ return logical;
+}
+#endif /* CONFIG_FEATURE_IFUPDOWN_MAPPING */
+
+static llist_t *find_iface_state(llist_t *state_list, const char *iface)
+{
+ unsigned short iface_len = bb_strlen(iface);
+ llist_t *search = state_list;
+
+ while (search) {
+ if ((strncmp(search->data, iface, iface_len) == 0) &&
+ (search->data[iface_len] == '=')) {
+ return(search);
+ }
+ search = search->link;
+ }
+ return(NULL);
+}
+
+extern int ifupdown_main(int argc, char **argv)
+{
+ int (*cmds) (struct interface_defn_t *) = NULL;
+ struct interfaces_file_t *defn;
+ FILE *state_fp = NULL;
+ llist_t *state_list = NULL;
+ llist_t *target_list = NULL;
+ char *interfaces = "/etc/network/interfaces";
+ const char *statefile = "/var/run/ifstate";
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+ int run_mappings = 1;
+#endif
+ int do_all = 0;
+ int force = 0;
+ int i;
+
+ if (bb_applet_name[2] == 'u') {
+ /* ifup command */
+ cmds = iface_up;
+ } else {
+ /* ifdown command */
+ cmds = iface_down;
+ }
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+ while ((i = getopt(argc, argv, "i:hvnamf")) != -1)
+#else
+ while ((i = getopt(argc, argv, "i:hvnaf")) != -1)
+#endif
+ {
+ switch (i) {
+ case 'i': /* interfaces */
+ interfaces = bb_xstrdup(optarg);
+ break;
+ case 'v': /* verbose */
+ verbose = 1;
+ break;
+ case 'a': /* all */
+ do_all = 1;
+ break;
+ case 'n': /* no-act */
+ no_act = 1;
+ break;
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+ case 'm': /* no-mappings */
+ run_mappings = 0;
+ break;
+#endif
+ case 'f': /* force */
+ force = 1;
+ break;
+ default:
+ bb_show_usage();
+ break;
+ }
+ }
+
+ if (argc - optind > 0) {
+ if (do_all) {
+ bb_show_usage();
+ }
+ } else {
+ if (!do_all) {
+ bb_show_usage();
+ }
+ }
+
+ debug_noise("reading %s file:\n", interfaces);
+ defn = read_interfaces(interfaces);
+ debug_noise("\ndone reading %s\n\n", interfaces);
+
+ if (!defn) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (no_act) {
+ state_fp = fopen(statefile, "r");
+ }
+
+ /* Create a list of interfaces to work on */
+ if (do_all) {
+ if (cmds == iface_up) {
+ target_list = defn->autointerfaces;
+ } else {
+#if 0
+ /* iface_down */
+ llist_t *new_item;
+ const llist_t *list = state_list;
+ while (list) {
+ new_item = xmalloc(sizeof(llist_t));
+ new_item->data = strdup(list->data);
+ new_item->link = NULL;
+ list = target_list;
+ if (list == NULL)
+ target_list = new_item;
+ else {
+ while (list->link) {
+ list = list->link;
+ }
+ list = new_item;
+ }
+ list = list->link;
+ }
+ target_list = defn->autointerfaces;
+#else
+
+ /* iface_down */
+ const llist_t *list = state_list;
+ while (list) {
+ target_list = llist_add_to_end(target_list, strdup(list->data));
+ list = list->link;
+ }
+ target_list = defn->autointerfaces;
+#endif
+ }
+ } else {
+ target_list = llist_add_to_end(target_list, argv[optind]);
+ }
+
+
+ /* Update the interfaces */
+ while (target_list) {
+ llist_t *iface_list;
+ struct interface_defn_t *currif;
+ char *iface;
+ char *liface;
+ char *pch;
+ int okay = 0;
+
+ iface = strdup(target_list->data);
+ target_list = target_list->link;
+
+ pch = strchr(iface, '=');
+ if (pch) {
+ *pch = '\0';
+ liface = strdup(pch + 1);
+ } else {
+ liface = strdup(iface);
+ }
+
+ if (!force) {
+ const llist_t *iface_state = find_iface_state(state_list, iface);
+
+ if (cmds == iface_up) {
+ /* ifup */
+ if (iface_state) {
+ bb_error_msg("interface %s already configured", iface);
+ continue;
+ }
+ } else {
+ /* ifdown */
+ if (iface_state) {
+ bb_error_msg("interface %s not configured", iface);
+ continue;
+ }
+ }
+ }
+
+#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
+ if ((cmds == iface_up) && run_mappings) {
+ struct mapping_defn_t *currmap;
+
+ for (currmap = defn->mappings; currmap; currmap = currmap->next) {
+
+ for (i = 0; i < currmap->n_matches; i++) {
+ if (fnmatch(currmap->match[i], liface, 0) != 0)
+ continue;
+ if (verbose) {
+ printf("Running mapping script %s on %s\n", currmap->script, liface);
+ }
+ liface = run_mapping(iface, currmap);
+ break;
+ }
+ }
+ }
+#endif
+
+
+ iface_list = defn->ifaces;
+ while (iface_list) {
+ currif = (struct interface_defn_t *) iface_list->data;
+ if (strcmp(liface, currif->iface) == 0) {
+ char *oldiface = currif->iface;
+
+ okay = 1;
+ currif->iface = iface;
+
+ debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
+
+ /* Call the cmds function pointer, does either iface_up() or iface_down() */
+ if (cmds(currif) == -1) {
+ bb_error_msg("Don't seem to be have all the variables for %s/%s.",
+ liface, currif->address_family->name);
+ }
+
+ currif->iface = oldiface;
+ }
+ iface_list = iface_list->link;
+ }
+ if (verbose) {
+ printf("\n");
+ }
+
+ if (!okay && !force) {
+ bb_error_msg("Ignoring unknown interface %s", liface);
+ } else {
+ llist_t *iface_state = find_iface_state(state_list, iface);
+
+ if (cmds == iface_up) {
+ char *newiface = xmalloc(bb_strlen(iface) + 1 + bb_strlen(liface) + 1);
+ sprintf(newiface, "%s=%s", iface, liface);
+ if (iface_state == NULL) {
+ state_list = llist_add_to_end(state_list, newiface);
+ } else {
+ free(iface_state->data);
+ iface_state->data = newiface;
+ }
+ } else if (cmds == iface_down) {
+ /* Remove an interface from the linked list */
+ if (iface_state) {
+ /* This needs to be done better */
+ free(iface_state->data);
+ free(iface_state->link);
+ if (iface_state->link) {
+ iface_state->data = iface_state->link->data;
+ iface_state->link = iface_state->link->link;
+ } else {
+ iface_state->data = NULL;
+ iface_state->link = NULL;
+ }
+ }
+ }
+ }
+ }
+
+ /* Actually write the new state */
+ if (!no_act) {
+
+ if (state_fp)
+ fclose(state_fp);
+ state_fp = bb_xfopen(statefile, "a+");
+
+ if (ftruncate(fileno(state_fp), 0) < 0) {
+ bb_error_msg_and_die("failed to truncate statefile %s: %s", statefile, strerror(errno));
+ }
+
+ rewind(state_fp);
+
+ while (state_list) {
+ if (state_list->data) {
+ fputs(state_list->data, state_fp);
+ fputc('\n', state_fp);
+ }
+ state_list = state_list->link;
+ }
+ fflush(state_fp);
+ }
+
+ /* Cleanup */
+ if (state_fp != NULL) {
+ fclose(state_fp);
+ state_fp = NULL;
+ }
+
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/inetd.c b/release/src/router/busybox/networking/inetd.c
new file mode 100644
index 00000000..4c46495a
--- /dev/null
+++ b/release/src/router/busybox/networking/inetd.c
@@ -0,0 +1,1302 @@
+/*
+ * Copyright (c) 1983,1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David A. Holland.
+ *
+ * Busybox port by Vladimir Oleynik (C) 2001-2003 <dzo@simtreas.ru>
+ *
+ * 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 of the License, 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
+ *
+ */
+
+/*
+ * Inetd - Internet super-server
+ *
+ * This program invokes all internet services as needed.
+ * connection-oriented services are invoked each time a
+ * connection is made, by creating a process. This process
+ * is passed the connection as file descriptor 0 and is
+ * expected to do a getpeername to find out the source host
+ * and port.
+ *
+ * Datagram oriented services are invoked when a datagram
+ * arrives; a process is created and passed a pending message
+ * on file descriptor 0. Datagram servers may either connect
+ * to their peer, freeing up the original socket for inetd
+ * to receive further messages on, or ``take over the socket'',
+ * processing all arriving datagrams and, eventually, timing
+ * out. The first type of server is said to be ``multi-threaded'';
+ * the second type of server ``single-threaded''.
+ *
+ * Inetd uses a configuration file which is read at startup
+ * and, possibly, at some later time in response to a hangup signal.
+ * The configuration file is ``free format'' with fields given in the
+ * order shown below. Continuation lines for an entry must being with
+ * a space or tab. All fields must be present in each entry.
+ *
+ * service name must be in /etc/services
+ * socket type stream/dgram/raw/rdm/seqpacket
+ * protocol must be in /etc/protocols
+ * wait/nowait[.max] single-threaded/multi-threaded, max #
+ * user[.group] user/group to run daemon as
+ * server program full path name
+ * server program arguments maximum of MAXARGS (20)
+ *
+ * RPC services unsuported
+ *
+ * Comment lines are indicated by a `#' in column 1.
+ */
+
+/*
+ * Here's the scoop concerning the user.group feature:
+ *
+ * 1) No group listed.
+ *
+ * a) for root: NO setuid() or setgid() is done
+ *
+ * b) nonroot: setuid()
+ * setgid(primary group as found in passwd)
+ * initgroups(name, primary group)
+ *
+ * 2) set-group-option on.
+ *
+ * a) for root: NO setuid()
+ * setgid(specified group)
+ * setgroups(1, specified group)
+ *
+ * b) nonroot: setuid()
+ * setgid(specified group)
+ * initgroups(name, specified group)
+ *
+ * All supplementary groups are discarded at startup in case inetd was
+ * run manually.
+ */
+
+#define __USE_BSD_SIGNAL
+
+#include "busybox.h"
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifndef __linux__
+#ifndef RLIMIT_NOFILE
+#define RLIMIT_NOFILE RLIMIT_OFILE
+#endif
+#endif
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <time.h>
+
+#define _PATH_INETDCONF "/etc/inetd.conf"
+#define _PATH_INETDPID "/var/run/inetd.pid"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define TOOMANY 40 /* don't start more than TOOMANY */
+#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
+#define RETRYTIME (60*10) /* retry after bind or server fail */
+
+#ifndef OPEN_MAX
+#define OPEN_MAX 64
+#endif
+
+
+/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
+#define FD_MARGIN (8)
+static int rlim_ofile_cur = OPEN_MAX;
+
+#ifdef RLIMIT_NOFILE
+static struct rlimit rlim_ofile;
+#endif
+
+#define INETD_UNSUPPORT_BILTIN 1
+
+/* Check unsupporting builtin */
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
+#undef INETD_UNSUPPORT_BILTIN
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
+#undef INETD_UNSUPPORT_BILTIN
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
+#undef INETD_UNSUPPORT_BILTIN
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
+#undef INETD_UNSUPPORT_BILTIN
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
+#undef INETD_UNSUPPORT_BILTIN
+#endif
+
+static struct servtab {
+ char *se_service; /* name of service */
+ int se_socktype; /* type of socket to use */
+ int se_family; /* address family */
+ char *se_proto; /* protocol used */
+ short se_wait; /* single threaded server */
+ short se_checked; /* looked at during merge */
+ char *se_user; /* user name to run as */
+ char *se_group; /* group name to run as */
+#ifndef INETD_UNSUPPORT_BILTIN
+ const struct biltin *se_bi; /* if built-in, description */
+#endif
+ char *se_server; /* server program */
+#define MAXARGV 20
+ char *se_argv[MAXARGV+1]; /* program arguments */
+ int se_fd; /* open descriptor */
+ union {
+ struct sockaddr se_un_ctrladdr;
+ struct sockaddr_in se_un_ctrladdr_in;
+ struct sockaddr_un se_un_ctrladdr_un;
+ } se_un; /* bound address */
+#define se_ctrladdr se_un.se_un_ctrladdr
+#define se_ctrladdr_in se_un.se_un_ctrladdr_in
+#define se_ctrladdr_un se_un.se_un_ctrladdr_un
+ int se_ctrladdr_size;
+ int se_max; /* max # of instances of this service */
+ int se_count; /* number started since se_time */
+ struct timeval se_time; /* start of se_count */
+ struct servtab *se_next;
+} *servtab;
+
+/* Length of socket listen queue. Should be per-service probably. */
+static int global_queuelen = 128;
+
+static int nsock, maxsock;
+static fd_set allsock;
+static int timingout;
+static sigset_t blockmask, emptymask;
+
+
+ /* Echo received data */
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
+static void echo_stream(int, struct servtab *);
+static void echo_dg(int, struct servtab *);
+#endif
+ /* Internet /dev/null */
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
+static void discard_stream(int, struct servtab *);
+static void discard_dg(int, struct servtab *);
+#endif
+ /* Return 32 bit time since 1900 */
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
+static void machtime_stream(int, struct servtab *);
+static void machtime_dg(int, struct servtab *);
+#endif
+ /* Return human-readable time */
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
+static void daytime_stream(int, struct servtab *);
+static void daytime_dg(int, struct servtab *);
+#endif
+ /* Familiar character generator */
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
+static void chargen_stream(int, struct servtab *);
+static void chargen_dg(int, struct servtab *);
+#endif
+
+#ifndef INETD_UNSUPPORT_BILTIN
+struct biltin {
+ const char *bi_service; /* internally provided service name */
+ int bi_socktype; /* type of socket supported */
+ short bi_fork; /* 1 if should fork before call */
+ short bi_wait; /* 1 if should wait for child */
+ void (*bi_fn)(int, struct servtab *); /* fn which performs it */
+};
+
+static const struct biltin biltins[] = {
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
+ /* Echo received data */
+ { "echo", SOCK_STREAM, 1, 0, echo_stream, },
+ { "echo", SOCK_DGRAM, 0, 0, echo_dg, },
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
+ /* Internet /dev/null */
+ { "discard", SOCK_STREAM, 1, 0, discard_stream, },
+ { "discard", SOCK_DGRAM, 0, 0, discard_dg, },
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
+ /* Return 32 bit time since 1900 */
+ { "time", SOCK_STREAM, 0, 0, machtime_stream, },
+ { "time", SOCK_DGRAM, 0, 0, machtime_dg, },
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
+ /* Return human-readable time */
+ { "daytime", SOCK_STREAM, 0, 0, daytime_stream, },
+ { "daytime", SOCK_DGRAM, 0, 0, daytime_dg, },
+#endif
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
+ /* Familiar character generator */
+ { "chargen", SOCK_STREAM, 1, 0, chargen_stream, },
+ { "chargen", SOCK_DGRAM, 0, 0, chargen_dg, },
+#endif
+ { NULL, 0, 0, 0, NULL }
+};
+#endif /* INETD_UNSUPPORT_BILTIN */
+
+#define NUMINT (sizeof(intab) / sizeof(struct inent))
+static const char *CONFIG = _PATH_INETDCONF;
+
+#define BCOPY(s, d, z) memcpy(d, s, z)
+
+static void
+syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
+ __attribute__ ((noreturn, format (printf, 2, 3)));
+
+static void
+syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
+{
+ char buf[50];
+ va_list p;
+
+ va_start(p, msg);
+ vsyslog(LOG_ERR, msg, p);
+ if (se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof (buf), 0);
+ _exit(1);
+}
+
+static FILE *fconfig;
+static char line[256];
+
+static FILE *
+setconfig(void)
+{
+ FILE *f = fconfig;
+
+ if (f != NULL) {
+ fseek(f, 0L, L_SET);
+ } else {
+ f = fconfig = fopen(CONFIG, "r");
+ if(f == NULL)
+ syslog(LOG_ERR, "%s: %m", CONFIG);
+ }
+ return f;
+}
+
+static char *
+nextline(void)
+{
+ char *cp;
+ FILE *fd = fconfig;
+
+ if (fgets(line, sizeof (line), fd) == NULL)
+ return ((char *)0);
+ cp = strchr(line, '\n');
+ if (cp)
+ *cp = '\0';
+ return (line);
+}
+
+static char *
+skip(char **cpp)
+{
+ char *cp = *cpp;
+ char *start;
+
+ if (*cpp == NULL)
+ return ((char *)0);
+
+again:
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == '\0') {
+ int c;
+
+ c = getc(fconfig);
+ (void) ungetc(c, fconfig);
+ if (c == ' ' || c == '\t')
+ if ((cp = nextline()) != NULL)
+ goto again;
+ *cpp = NULL;
+ return NULL;
+ }
+ start = cp;
+ while (*cp && *cp != ' ' && *cp != '\t')
+ cp++;
+ if (*cp != '\0')
+ *cp++ = '\0';
+ *cpp = cp;
+ return (start);
+}
+
+static char *
+newstr(char *cp)
+{
+ cp = strdup(cp ? cp : "");
+ if (cp)
+ return(cp);
+
+ syslog_err_and_discard_dg(SOCK_STREAM, "strdup: %m");
+}
+
+
+static struct servtab *
+getconfigent(void)
+{
+ static struct servtab serv;
+ struct servtab *sep = &serv;
+ int argc;
+ char *cp, *arg;
+
+more:
+ while ((cp = nextline()) && *cp == '#')
+ ;
+ if (cp == NULL)
+ return ((struct servtab *)0);
+ memset((char *)sep, 0, sizeof *sep);
+ sep->se_service = newstr(skip(&cp));
+ arg = skip(&cp);
+ if (arg == NULL)
+ goto more;
+
+ if (strcmp(arg, "stream") == 0)
+ sep->se_socktype = SOCK_STREAM;
+ else if (strcmp(arg, "dgram") == 0)
+ sep->se_socktype = SOCK_DGRAM;
+ else if (strcmp(arg, "rdm") == 0)
+ sep->se_socktype = SOCK_RDM;
+ else if (strcmp(arg, "seqpacket") == 0)
+ sep->se_socktype = SOCK_SEQPACKET;
+ else if (strcmp(arg, "raw") == 0)
+ sep->se_socktype = SOCK_RAW;
+ else
+ sep->se_socktype = -1;
+
+ sep->se_proto = newstr(skip(&cp));
+ if (strcmp(sep->se_proto, "unix") == 0) {
+ sep->se_family = AF_UNIX;
+ } else {
+ sep->se_family = AF_INET;
+ if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
+ syslog(LOG_ERR, "%s: rpc services not suported",
+ sep->se_service);
+ goto more;
+ }
+ }
+ arg = skip(&cp);
+ if (arg == NULL)
+ goto more;
+ {
+ char *s = strchr(arg, '.');
+ if (s) {
+ *s++ = '\0';
+ sep->se_max = atoi(s);
+ } else
+ sep->se_max = TOOMANY;
+ }
+ sep->se_wait = strcmp(arg, "wait") == 0;
+ sep->se_user = newstr(skip(&cp));
+ sep->se_group = strchr(sep->se_user, '.');
+ if (sep->se_group) {
+ *sep->se_group++ = '\0';
+ }
+ sep->se_server = newstr(skip(&cp));
+ if (strcmp(sep->se_server, "internal") == 0) {
+#ifndef INETD_UNSUPPORT_BILTIN
+ const struct biltin *bi;
+
+ for (bi = biltins; bi->bi_service; bi++)
+ if (bi->bi_socktype == sep->se_socktype &&
+ strcmp(bi->bi_service, sep->se_service) == 0)
+ break;
+ if (bi->bi_service == 0) {
+ syslog(LOG_ERR, "internal service %s unknown",
+ sep->se_service);
+ goto more;
+ }
+ sep->se_bi = bi;
+ sep->se_wait = bi->bi_wait;
+#else
+ syslog(LOG_ERR, "internal service %s unknown",
+ sep->se_service);
+ goto more;
+#endif
+ } else
+#ifndef INETD_UNSUPPORT_BILTIN
+ sep->se_bi = NULL
+#endif
+ ;
+ argc = 0;
+ for (arg = skip(&cp); cp; arg = skip(&cp)) {
+ if (argc < MAXARGV)
+ sep->se_argv[argc++] = newstr(arg);
+ }
+ while (argc <= MAXARGV)
+ sep->se_argv[argc++] = NULL;
+ return (sep);
+}
+
+static void
+freeconfig(struct servtab *cp)
+{
+ int i;
+
+ free(cp->se_service);
+ free(cp->se_proto);
+ free(cp->se_user);
+ /* Note: se_group is part of the newstr'ed se_user */
+ free(cp->se_server);
+ for (i = 0; i < MAXARGV; i++)
+ free(cp->se_argv[i]);
+}
+
+#ifndef INETD_UNSUPPORT_BILTIN
+static char **Argv;
+static char *LastArg;
+
+static void
+setproctitle(char *a, int s)
+{
+ size_t size;
+ char *cp;
+ struct sockaddr_in sn;
+ char buf[80];
+
+ cp = Argv[0];
+ size = sizeof(sn);
+ if (getpeername(s, (struct sockaddr *)&sn, &size) == 0)
+ (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sn.sin_addr));
+ else
+ (void) sprintf(buf, "-%s", a);
+ strncpy(cp, buf, LastArg - cp);
+ cp += strlen(cp);
+ while (cp < LastArg)
+ *cp++ = ' ';
+}
+#endif /* INETD_UNSUPPORT_BILTIN */
+
+static struct servtab *
+enter(struct servtab *cp)
+{
+ struct servtab *sep;
+ sigset_t oldmask;
+
+ sep = (struct servtab *)malloc(sizeof (*sep));
+ if (sep == NULL) {
+ syslog_err_and_discard_dg(SOCK_STREAM, bb_msg_memory_exhausted);
+ }
+ *sep = *cp;
+ sep->se_fd = -1;
+ sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
+ sep->se_next = servtab;
+ servtab = sep;
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ return (sep);
+}
+
+static int
+bump_nofile(void)
+{
+#ifdef RLIMIT_NOFILE
+
+#define FD_CHUNK 32
+
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
+ syslog(LOG_ERR, "getrlimit: %m");
+ return -1;
+ }
+ rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
+ if (rl.rlim_cur <= rlim_ofile_cur) {
+ syslog(LOG_ERR,
+#if _FILE_OFFSET_BITS == 64
+ "bump_nofile: cannot extend file limit, max = %lld",
+#else
+ "bump_nofile: cannot extend file limit, max = %ld",
+#endif
+ rl.rlim_cur);
+ return -1;
+ }
+
+ if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
+ syslog(LOG_ERR, "setrlimit: %m");
+ return -1;
+ }
+
+ rlim_ofile_cur = rl.rlim_cur;
+ return 0;
+
+#else
+ syslog(LOG_ERR, "bump_nofile: cannot extend file limit");
+ return -1;
+#endif
+}
+
+
+static void
+setup(struct servtab *sep)
+{
+ int on = 1;
+
+ if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
+ syslog(LOG_ERR, "%s/%s: socket: %m",
+ sep->se_service, sep->se_proto);
+ return;
+ }
+ if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
+ sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
+ if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) {
+ syslog(LOG_ERR, "%s/%s: bind: %m",
+ sep->se_service, sep->se_proto);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ if (!timingout) {
+ timingout = 1;
+ alarm(RETRYTIME);
+ }
+ return;
+ }
+ if (sep->se_socktype == SOCK_STREAM)
+ listen(sep->se_fd, global_queuelen);
+
+ FD_SET(sep->se_fd, &allsock);
+ nsock++;
+ if (sep->se_fd > maxsock) {
+ maxsock = sep->se_fd;
+ if (maxsock > rlim_ofile_cur - FD_MARGIN)
+ bump_nofile();
+ }
+}
+
+static void
+config(int signum)
+{
+ struct servtab *sep, *cp, **sepp;
+ sigset_t oldmask;
+ unsigned n;
+
+ (void)signum;
+ if (setconfig() == NULL)
+ return;
+
+ for (sep = servtab; sep; sep = sep->se_next)
+ sep->se_checked = 0;
+ while ((cp = getconfigent()) != NULL) {
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (strcmp(sep->se_service, cp->se_service) == 0 &&
+ strcmp(sep->se_proto, cp->se_proto) == 0)
+ break;
+ if (sep != 0) {
+ int i;
+
+#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
+
+ sigprocmask(SIG_BLOCK, &emptymask, &oldmask);
+ /*
+ * sep->se_wait may be holding the pid of a daemon
+ * that we're waiting for. If so, don't overwrite
+ * it unless the config file explicitly says don't
+ * wait.
+ */
+ if (
+#ifndef INETD_UNSUPPORT_BILTIN
+ cp->se_bi == 0 &&
+#endif
+ (sep->se_wait == 1 || cp->se_wait == 0))
+ sep->se_wait = cp->se_wait;
+ if (cp->se_max != sep->se_max)
+ SWAP(int, cp->se_max, sep->se_max);
+ if (cp->se_user)
+ SWAP(char *, sep->se_user, cp->se_user);
+ if (cp->se_group)
+ SWAP(char *, sep->se_group, cp->se_group);
+ if (cp->se_server)
+ SWAP(char *, sep->se_server, cp->se_server);
+ for (i = 0; i < MAXARGV; i++)
+ SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
+#undef SWAP
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ freeconfig(cp);
+ } else {
+ sep = enter(cp);
+ }
+ sep->se_checked = 1;
+
+ switch (sep->se_family) {
+ case AF_UNIX:
+ if (sep->se_fd != -1)
+ break;
+ (void)unlink(sep->se_service);
+ n = strlen(sep->se_service);
+ if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1)
+ n = sizeof(sep->se_ctrladdr_un.sun_path) - 1;
+ strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n);
+ sep->se_ctrladdr_un.sun_family = AF_UNIX;
+ sep->se_ctrladdr_size = n +
+ sizeof sep->se_ctrladdr_un.sun_family;
+ setup(sep);
+ break;
+ case AF_INET:
+ sep->se_ctrladdr_in.sin_family = AF_INET;
+ sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
+ {
+ u_short port = htons(atoi(sep->se_service));
+
+ if (!port) {
+ struct servent *sp;
+ sp = getservbyname(sep->se_service,
+ sep->se_proto);
+ if (sp == 0) {
+ syslog(LOG_ERR,
+ "%s/%s: unknown service",
+ sep->se_service, sep->se_proto);
+ continue;
+ }
+ port = sp->s_port;
+ }
+ if (port != sep->se_ctrladdr_in.sin_port) {
+ sep->se_ctrladdr_in.sin_port = port;
+ if (sep->se_fd != -1) {
+ FD_CLR(sep->se_fd, &allsock);
+ nsock--;
+ (void) close(sep->se_fd);
+ }
+ sep->se_fd = -1;
+ }
+ if (sep->se_fd == -1)
+ setup(sep);
+ }
+ }
+ }
+ if (fconfig) {
+ (void) fclose(fconfig);
+ fconfig = NULL;
+ }
+ /*
+ * Purge anything not looked at above.
+ */
+ sigprocmask(SIG_SETMASK, &blockmask, &oldmask);
+ sepp = &servtab;
+ while ((sep = *sepp) != NULL) {
+ if (sep->se_checked) {
+ sepp = &sep->se_next;
+ continue;
+ }
+ *sepp = sep->se_next;
+ if (sep->se_fd != -1) {
+ FD_CLR(sep->se_fd, &allsock);
+ nsock--;
+ (void) close(sep->se_fd);
+ }
+ if (sep->se_family == AF_UNIX)
+ (void)unlink(sep->se_service);
+ freeconfig(sep);
+ free((char *)sep);
+ }
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+}
+
+
+
+static void
+reapchild(int signum)
+{
+ int status;
+ int pid;
+ struct servtab *sep;
+
+ (void)signum;
+ for (;;) {
+ pid = wait3(&status, WNOHANG, (struct rusage *)0);
+ if (pid <= 0)
+ break;
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (sep->se_wait == pid) {
+ if (WIFEXITED(status) && WEXITSTATUS(status))
+ syslog(LOG_WARNING,
+ "%s: exit status 0x%x",
+ sep->se_server, WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ syslog(LOG_WARNING,
+ "%s: exit signal 0x%x",
+ sep->se_server, WTERMSIG(status));
+ sep->se_wait = 1;
+ FD_SET(sep->se_fd, &allsock);
+ nsock++;
+ }
+ }
+}
+
+static void
+retry(int signum)
+{
+ struct servtab *sep;
+
+ (void)signum;
+ timingout = 0;
+ for (sep = servtab; sep; sep = sep->se_next) {
+ if (sep->se_fd == -1) {
+ switch (sep->se_family) {
+ case AF_UNIX:
+ case AF_INET:
+ setup(sep);
+ break;
+ }
+ }
+ }
+}
+
+static void
+goaway(int signum)
+{
+ struct servtab *sep;
+
+ (void)signum;
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (sep->se_fd != -1 && sep->se_family == AF_UNIX)
+ (void)unlink(sep->se_service);
+ (void)unlink(_PATH_INETDPID);
+ exit(0);
+}
+
+
+
+extern int
+inetd_main(int argc, char *argv[])
+{
+ struct servtab *sep;
+ struct passwd *pwd;
+ struct group *grp = NULL;
+ struct sigaction sa;
+ int pid;
+ unsigned long opt;
+ char *sq;
+ gid_t gid;
+
+#ifdef INETD_UNSUPPORT_BILTIN
+# define dofork 1
+#else
+ int dofork;
+ extern char **environ;
+#endif
+
+ gid = getgid();
+ setgroups(1, &gid);
+
+#ifndef INETD_UNSUPPORT_BILTIN
+ Argv = argv;
+ if (environ == 0 || *environ == 0)
+ environ = argv;
+ while (*environ)
+ environ++;
+ LastArg = environ[-1] + strlen(environ[-1]);
+#endif
+
+#if defined(__uClinux__)
+ opt = bb_getopt_ulflags(argc, argv, "q:f", &sq);
+ if (!(opt & 2)) {
+ daemon(0, 0);
+ /* reexec for vfork() do continue parent */
+ vfork_daemon_rexec(argc, argv, "-f");
+ }
+#else
+ opt = bb_getopt_ulflags(argc, argv, "q:", &sq);
+ daemon(0, 0);
+#endif /* uClinux */
+
+ if(opt & 1) {
+ global_queuelen = atoi(optarg);
+ if (global_queuelen < 8) global_queuelen=8;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ CONFIG = argv[0];
+
+ openlog(bb_applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ {
+ FILE *fp;
+
+ if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) {
+ fprintf(fp, "%u\n", getpid());
+ (void)fclose(fp);
+ }
+ }
+
+#ifdef RLIMIT_NOFILE
+ if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
+ syslog(LOG_ERR, "getrlimit: %m");
+ } else {
+ rlim_ofile_cur = rlim_ofile.rlim_cur;
+ if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
+ rlim_ofile_cur = OPEN_MAX;
+ }
+#endif
+
+ config(0);
+
+ sigemptyset(&emptymask);
+ sigemptyset(&blockmask);
+ sigaddset(&blockmask, SIGCHLD);
+ sigaddset(&blockmask, SIGHUP);
+ sigaddset(&blockmask, SIGALRM);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_mask = blockmask;
+ sa.sa_handler = retry;
+ sigaction(SIGALRM, &sa, NULL);
+ sa.sa_handler = config;
+ sigaction(SIGHUP, &sa, NULL);
+ sa.sa_handler = reapchild;
+ sigaction(SIGCHLD, &sa, NULL);
+ sa.sa_handler = goaway;
+ sigaction(SIGTERM, &sa, NULL);
+ sa.sa_handler = goaway;
+ sigaction(SIGINT, &sa, NULL);
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, NULL);
+
+ {
+ /* space for daemons to overwrite environment for ps */
+#define DUMMYSIZE 100
+ char dummy[DUMMYSIZE];
+
+ (void)memset(dummy, 'x', DUMMYSIZE - 1);
+ dummy[DUMMYSIZE - 1] = '\0';
+
+ (void)setenv("inetd_dummy", dummy, 1);
+ }
+
+ for (;;) {
+ int n, ctrl;
+ fd_set readable;
+
+ if (nsock == 0) {
+ sigprocmask(SIG_BLOCK, &blockmask, NULL);
+ while (nsock == 0)
+ sigsuspend(&emptymask);
+ sigprocmask(SIG_SETMASK, &emptymask, NULL);
+ }
+ readable = allsock;
+ if ((n = select(maxsock + 1, &readable, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0)) <= 0) {
+ if (n < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "select: %m");
+ sleep(1);
+ continue;
+ }
+ for (sep = servtab; n && sep; sep = sep->se_next)
+ if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
+ n--;
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
+ /* Fixed AGC */
+ fcntl(sep->se_fd, F_SETFL, O_NDELAY);
+ /* --------- */
+ ctrl = accept(sep->se_fd, NULL, NULL);
+ fcntl(sep->se_fd, F_SETFL, 0);
+ if (ctrl < 0) {
+ if (errno == EINTR || errno == EWOULDBLOCK)
+ continue;
+ syslog(LOG_WARNING, "accept (for %s): %m",
+ sep->se_service);
+ continue;
+ }
+ } else
+ ctrl = sep->se_fd;
+ sigprocmask(SIG_BLOCK, &blockmask, NULL);
+ pid = 0;
+#ifndef INETD_UNSUPPORT_BILTIN
+ dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
+#endif
+ if (dofork) {
+ if (sep->se_count++ == 0)
+ (void)gettimeofday(&sep->se_time,
+ (struct timezone *)0);
+ else if (sep->se_count >= sep->se_max) {
+ struct timeval now;
+
+ (void)gettimeofday(&now, (struct timezone *)0);
+ if (now.tv_sec - sep->se_time.tv_sec >
+ CNT_INTVL) {
+ sep->se_time = now;
+ sep->se_count = 1;
+ } else {
+ syslog(LOG_ERR,
+ "%s/%s server failing (looping), service terminated",
+ sep->se_service, sep->se_proto);
+ FD_CLR(sep->se_fd, &allsock);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ sep->se_count = 0;
+ nsock--;
+ sigprocmask(SIG_SETMASK, &emptymask,
+ NULL);
+ if (!timingout) {
+ timingout = 1;
+ alarm(RETRYTIME);
+ }
+ continue;
+ }
+ }
+ pid = fork();
+ }
+ if (pid < 0) {
+ syslog(LOG_ERR, "fork: %m");
+ if (sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ sigprocmask(SIG_SETMASK, &emptymask, NULL);
+ sleep(1);
+ continue;
+ }
+ if (pid && sep->se_wait) {
+ sep->se_wait = pid;
+ FD_CLR(sep->se_fd, &allsock);
+ nsock--;
+ }
+ sigprocmask(SIG_SETMASK, &emptymask, NULL);
+ if (pid == 0) {
+#ifndef INETD_UNSUPPORT_BILTIN
+ if (sep->se_bi)
+ (*sep->se_bi->bi_fn)(ctrl, sep);
+ else
+#endif
+ {
+ if ((pwd = getpwnam(sep->se_user)) == NULL) {
+ syslog_err_and_discard_dg(
+ sep->se_socktype,
+ "getpwnam: %s: No such user",
+ sep->se_user);
+ }
+ if (sep->se_group &&
+ (grp = getgrnam(sep->se_group)) == NULL) {
+ syslog_err_and_discard_dg(
+ sep->se_socktype,
+ "getgrnam: %s: No such group",
+ sep->se_group);
+ }
+ /*
+ * Ok. There are four cases here:
+ * 1. nonroot user, no group specified
+ * 2. nonroot user, some group specified
+ * 3. root user, no group specified
+ * 4. root user, some group specified
+ * In cases 2 and 4 we setgid to the specified
+ * group. In cases 1 and 2 we run initgroups
+ * to run with the groups of the given user.
+ * In case 4 we do setgroups to run with the
+ * given group. In case 3 we do nothing.
+ */
+ if (pwd->pw_uid) {
+ if (sep->se_group)
+ pwd->pw_gid = grp->gr_gid;
+ setgid((gid_t)pwd->pw_gid);
+ initgroups(pwd->pw_name, pwd->pw_gid);
+ setuid((uid_t)pwd->pw_uid);
+ } else if (sep->se_group) {
+ setgid((gid_t)grp->gr_gid);
+ setgroups(1, &grp->gr_gid);
+ }
+ dup2(ctrl, 0);
+ close(ctrl);
+ dup2(0, 1);
+ dup2(0, 2);
+#ifdef RLIMIT_NOFILE
+ if (rlim_ofile.rlim_cur != rlim_ofile_cur) {
+ if (setrlimit(RLIMIT_NOFILE,
+ &rlim_ofile) < 0)
+ syslog(LOG_ERR,"setrlimit: %m");
+ }
+#endif
+ for (ctrl = rlim_ofile_cur-1; --ctrl > 2; )
+ (void)close(ctrl);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGPIPE, &sa, NULL);
+
+ execv(sep->se_server, sep->se_argv);
+ syslog_err_and_discard_dg(sep->se_socktype,
+ "execv %s: %m", sep->se_server);
+ }
+ }
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ }
+ }
+}
+
+
+/*
+ * Internet services provided internally by inetd:
+ */
+#define BUFSIZE 4096
+
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
+/* Echo service -- echo data back */
+static void
+echo_stream(int s, struct servtab *sep)
+{
+ char buffer[BUFSIZE];
+ int i;
+
+ setproctitle(sep->se_service, s);
+ while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
+ write(s, buffer, i) > 0)
+ ;
+ exit(0);
+}
+
+/* Echo service -- echo data back */
+static void
+echo_dg(int s, struct servtab *sep)
+{
+ char buffer[BUFSIZE];
+ int i;
+ size_t size;
+ struct sockaddr sa;
+
+ (void)sep;
+
+ size = sizeof(sa);
+ if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
+ return;
+ (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
+}
+#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */
+
+
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
+/* Discard service -- ignore data */
+static void
+discard_stream(int s, struct servtab *sep)
+{
+ char buffer[BUFSIZE];
+
+ setproctitle(sep->se_service, s);
+ while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
+ errno == EINTR)
+ ;
+ exit(0);
+}
+
+/* Discard service -- ignore data */
+static void
+discard_dg(int s, struct servtab *sep)
+{
+ char buffer[BUFSIZE];
+ (void)sep;
+ read(s, buffer, sizeof(buffer));
+}
+#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */
+
+
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
+#include <ctype.h>
+#define LINESIZ 72
+static char ring[128];
+static char *endring;
+
+static void
+initring(void)
+{
+ int i;
+
+ endring = ring;
+
+ for (i = 0; i <= 128; ++i)
+ if (isprint(i))
+ *endring++ = i;
+}
+
+/* Character generator */
+static void
+chargen_stream(int s, struct servtab *sep)
+{
+ char *rs;
+ int len;
+ char text[LINESIZ+2];
+
+ setproctitle(sep->se_service, s);
+
+ if (!endring) {
+ initring();
+ rs = ring;
+ }
+
+ text[LINESIZ] = '\r';
+ text[LINESIZ + 1] = '\n';
+ for (rs = ring;;) {
+ if ((len = endring - rs) >= LINESIZ)
+ BCOPY(rs, text, LINESIZ);
+ else {
+ BCOPY(rs, text, len);
+ BCOPY(ring, text + len, LINESIZ - len);
+ }
+ if (++rs == endring)
+ rs = ring;
+ if (write(s, text, sizeof(text)) != sizeof(text))
+ break;
+ }
+ exit(0);
+}
+
+/* Character generator */
+static void
+chargen_dg(int s, struct servtab *sep)
+{
+ struct sockaddr sa;
+ static char *rs;
+ size_t len, size;
+ char text[LINESIZ+2];
+
+ (void)sep;
+
+ if (endring == 0) {
+ initring();
+ rs = ring;
+ }
+
+ size = sizeof(sa);
+ if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
+ return;
+
+ if ((len = endring - rs) >= LINESIZ)
+ BCOPY(rs, text, LINESIZ);
+ else {
+ BCOPY(rs, text, len);
+ BCOPY(ring, text + len, LINESIZ - len);
+ }
+ if (++rs == endring)
+ rs = ring;
+ text[LINESIZ] = '\r';
+ text[LINESIZ + 1] = '\n';
+ (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
+}
+#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */
+
+
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
+/*
+ * Return a machine readable date and time, in the form of the
+ * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
+ * returns the number of seconds since midnight, Jan 1, 1970,
+ * we must add 2208988800 seconds to this figure to make up for
+ * some seventy years Bell Labs was asleep.
+ */
+
+static long
+machtime(void)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, (struct timezone *)0) < 0) {
+ fprintf(stderr, "Unable to get time of day\n");
+ return (0L);
+ }
+ return (htonl((long)tv.tv_sec + 2208988800UL));
+}
+
+static void
+machtime_stream(int s, struct servtab *sep)
+{
+ long result;
+ (void)sep;
+
+ result = machtime();
+ write(s, (char *) &result, sizeof(result));
+}
+
+static void
+machtime_dg(int s, struct servtab *sep)
+{
+ long result;
+ struct sockaddr sa;
+ size_t size;
+ (void)sep;
+
+ size = sizeof(sa);
+ if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
+ return;
+ result = machtime();
+ (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
+}
+#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */
+
+
+#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
+/* Return human-readable time of day */
+static int
+human_readable_time_sprintf(char *buffer)
+{
+ time_t clocc = time(NULL);
+
+ return sprintf(buffer, "%.24s\r\n", ctime(&clocc));
+}
+
+static void
+daytime_stream(int s, struct servtab *sep)
+{
+ char buffer[256];
+ size_t st = human_readable_time_sprintf(buffer);
+
+ (void)sep;
+
+ write(s, buffer, st);
+}
+
+/* Return human-readable time of day */
+static void
+daytime_dg(int s, struct servtab *sep)
+{
+ char buffer[256];
+ struct sockaddr sa;
+ size_t size;
+
+ (void)sep;
+
+ size = sizeof(sa);
+ if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
+ return;
+ size = human_readable_time_sprintf(buffer);
+ sendto(s, buffer, size, 0, &sa, sizeof(sa));
+}
+#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME */
diff --git a/release/src/router/busybox/networking/ip.c b/release/src/router/busybox/networking/ip.c
new file mode 100644
index 00000000..a0781bdb
--- /dev/null
+++ b/release/src/router/busybox/networking/ip.c
@@ -0,0 +1,115 @@
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include "./libiproute/utils.h"
+#include "./libiproute/ip_common.h"
+
+#include "busybox.h"
+
+#if 0
+int preferred_family = AF_UNSPEC;
+int oneline = 0;
+char * _SL_ = NULL;
+
+void ip_parse_common_args(int *argcp, char ***argvp)
+{
+ int argc = *argcp;
+ char **argv = *argvp;
+
+ while (argc > 1) {
+ char *opt = argv[1];
+
+ if (strcmp(opt,"--") == 0) {
+ argc--; argv++;
+ break;
+ }
+
+ if (opt[0] != '-')
+ break;
+
+ if (opt[1] == '-')
+ opt++;
+
+ if (matches(opt, "-family") == 0) {
+ argc--;
+ argv++;
+ if (strcmp(argv[1], "inet") == 0)
+ preferred_family = AF_INET;
+ else if (strcmp(argv[1], "inet6") == 0)
+ preferred_family = AF_INET6;
+ else if (strcmp(argv[1], "link") == 0)
+ preferred_family = AF_PACKET;
+ else
+ invarg(argv[1], "invalid protocol family");
+ } else if (strcmp(opt, "-4") == 0) {
+ preferred_family = AF_INET;
+ } else if (strcmp(opt, "-6") == 0) {
+ preferred_family = AF_INET6;
+ } else if (strcmp(opt, "-0") == 0) {
+ preferred_family = AF_PACKET;
+ } else if (matches(opt, "-oneline") == 0) {
+ ++oneline;
+ } else {
+ bb_show_usage();
+ }
+ argc--; argv++;
+ }
+ _SL_ = oneline ? "\\" : "\n" ;
+}
+#endif
+
+int ip_main(int argc, char **argv)
+{
+ int ret = EXIT_FAILURE;
+
+ ip_parse_common_args(&argc, &argv);
+
+ if (argc > 1) {
+#ifdef CONFIG_FEATURE_IP_ADDRESS
+ if (matches(argv[1], "address") == 0) {
+ ret = do_ipaddr(argc-2, argv+2);
+ }
+#endif
+#ifdef CONFIG_FEATURE_IP_ROUTE
+ else if (matches(argv[1], "route") == 0) {
+ ret = do_iproute(argc-2, argv+2);
+ }
+#endif
+#ifdef CONFIG_FEATURE_IP_LINK
+ else if (matches(argv[1], "link") == 0) {
+ ret = do_iplink(argc-2, argv+2);
+ }
+#endif
+#ifdef CONFIG_FEATURE_IP_TUNNEL
+ else if (matches(argv[1], "tunnel") == 0 || strcmp(argv[1], "tunl") == 0) {
+ ret = do_iptunnel(argc-2, argv+2);
+ }
+#endif
+ }
+ if (ret) {
+ bb_show_usage();
+ }
+ return(EXIT_SUCCESS);
+}
diff --git a/release/src/router/busybox/networking/ipaddr.c b/release/src/router/busybox/networking/ipaddr.c
new file mode 100644
index 00000000..7826194c
--- /dev/null
+++ b/release/src/router/busybox/networking/ipaddr.c
@@ -0,0 +1,27 @@
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "./libiproute/utils.h"
+#include "./libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int ipaddr_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_ipaddr(argc-1, argv+1);
+}
diff --git a/release/src/router/busybox/networking/ipcalc.c b/release/src/router/busybox/networking/ipcalc.c
new file mode 100644
index 00000000..94b747ef
--- /dev/null
+++ b/release/src/router/busybox/networking/ipcalc.c
@@ -0,0 +1,150 @@
+/* vi: set sw=4 ts=4 ai: */
+/*
+ * Mini ipcalc implementation for busybox
+ *
+ * By Jordan Crouse <jordan@cosmicpenguin.net>
+ * Stephan Linz <linz@li-pro.net>
+ *
+ * This is a complete reimplentation of the ipcalc program
+ * from Redhat. I didn't look at their source code, but there
+ * is no denying that this is a loving reimplementation
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "busybox.h"
+
+#define IPCALC_MSG(CMD,ALTCMD) if (mode & SILENT) {ALTCMD;} else {CMD;}
+
+#define CLASS_A_NETMASK ntohl(0xFF000000)
+#define CLASS_B_NETMASK ntohl(0xFFFF0000)
+#define CLASS_C_NETMASK ntohl(0xFFFFFF00)
+
+static unsigned long get_netmask(unsigned long ipaddr)
+{
+ ipaddr = htonl(ipaddr);
+
+ if ((ipaddr & 0xC0000000) == 0xC0000000)
+ return CLASS_C_NETMASK;
+ else if ((ipaddr & 0x80000000) == 0x80000000)
+ return CLASS_B_NETMASK;
+ else if ((ipaddr & 0x80000000) == 0)
+ return CLASS_A_NETMASK;
+ else
+ return 0;
+}
+
+#define NETMASK 0x01
+#define BROADCAST 0x02
+#define NETWORK 0x04
+#define HOSTNAME 0x08
+#define SILENT 0x10
+
+int ipcalc_main(int argc, char **argv)
+{
+ unsigned long mode;
+
+ unsigned long netmask = 0;
+ unsigned long broadcast;
+ unsigned long network;
+ unsigned long ipaddr;
+
+ static const struct option long_options[] = {
+ {"netmask", no_argument, NULL, 'n'},
+ {"broadcast", no_argument, NULL, 'b'},
+ {"network", no_argument, NULL, 'w'},
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+ {"hostname", no_argument, NULL, 'h'},
+ {"silent", no_argument, NULL, 's'},
+#endif
+ {NULL, 0, NULL, 0}
+ };
+
+ bb_applet_long_options = long_options;
+ mode = bb_getopt_ulflags(argc, argv,
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+ "nbwhs");
+#else
+ "nbw");
+#endif
+ if (mode & (BROADCAST | NETWORK)) {
+ if (argc - optind > 2) {
+ bb_show_usage();
+ }
+ } else {
+ if (argc - optind != 1) {
+ bb_show_usage();
+ }
+ }
+
+ ipaddr = inet_addr(argv[optind]);
+
+ if (ipaddr == INADDR_NONE) {
+ IPCALC_MSG(bb_error_msg_and_die("bad IP address: %s", argv[optind]),
+ exit(EXIT_FAILURE));
+ }
+
+
+ if (argc - optind == 2) {
+ netmask = inet_addr(argv[optind + 1]);
+ }
+
+ if (ipaddr == INADDR_NONE) {
+ IPCALC_MSG(bb_error_msg_and_die("bad netmask: %s", argv[optind + 1]),
+ exit(EXIT_FAILURE));
+ }
+
+ /* JHC - If the netmask wasn't provided then calculate it */
+ if (!netmask) {
+ netmask = get_netmask(ipaddr);
+ }
+
+ if (mode & NETMASK) {
+ printf("NETMASK=%s\n", inet_ntoa((*(struct in_addr *) &netmask)));
+ }
+
+ if (mode & BROADCAST) {
+ broadcast = (ipaddr & netmask) | ~netmask;
+ printf("BROADCAST=%s\n", inet_ntoa((*(struct in_addr *) &broadcast)));
+ }
+
+ if (mode & NETWORK) {
+ network = ipaddr & netmask;
+ printf("NETWORK=%s\n", inet_ntoa((*(struct in_addr *) &network)));
+ }
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+ if (mode & HOSTNAME) {
+ struct hostent *hostinfo;
+ int x;
+
+ hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET);
+ if (!hostinfo) {
+ IPCALC_MSG(bb_herror_msg_and_die(
+ "cannot find hostname for %s", argv[optind]),);
+ exit(EXIT_FAILURE);
+ }
+ for (x = 0; hostinfo->h_name[x]; x++) {
+ hostinfo->h_name[x] = tolower(hostinfo->h_name[x]);
+ }
+
+ printf("HOSTNAME=%s\n", hostinfo->h_name);
+ }
+#endif
+
+ return EXIT_SUCCESS;
+}
+
+/* END CODE */
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/release/src/router/busybox/networking/iplink.c b/release/src/router/busybox/networking/iplink.c
new file mode 100644
index 00000000..7207e176
--- /dev/null
+++ b/release/src/router/busybox/networking/iplink.c
@@ -0,0 +1,27 @@
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "./libiproute/utils.h"
+#include "./libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int iplink_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_iplink(argc-1, argv+1);
+}
diff --git a/release/src/router/busybox/networking/iproute.c b/release/src/router/busybox/networking/iproute.c
new file mode 100644
index 00000000..d049a87c
--- /dev/null
+++ b/release/src/router/busybox/networking/iproute.c
@@ -0,0 +1,27 @@
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "./libiproute/utils.h"
+#include "./libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int iproute_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_iproute(argc-1, argv+1);
+}
diff --git a/release/src/router/busybox/networking/iptunnel.c b/release/src/router/busybox/networking/iptunnel.c
new file mode 100644
index 00000000..f2b2e8a7
--- /dev/null
+++ b/release/src/router/busybox/networking/iptunnel.c
@@ -0,0 +1,27 @@
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "./libiproute/utils.h"
+#include "./libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int iptunnel_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_iptunnel(argc-1, argv+1);
+}
diff --git a/release/src/router/busybox/networking/libiproute/Makefile b/release/src/router/busybox/networking/libiproute/Makefile
new file mode 100644
index 00000000..c1f18be7
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/Makefile
@@ -0,0 +1,30 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.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 of the License, 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
+#
+
+TOPDIR:= ../../
+LIBIPROUTE_DIR:=./
+include $(TOPDIR).config
+include $(TOPDIR)Rules.mak
+include Makefile.in
+all: $(libraries-y)
+-include $(TOPDIR).depend
+
+clean:
+ rm -f *.o *.a $(AR_TARGET)
+
diff --git a/release/src/router/busybox/networking/libiproute/Makefile.in b/release/src/router/busybox/networking/libiproute/Makefile.in
new file mode 100755
index 00000000..f39eeb3e
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/Makefile.in
@@ -0,0 +1,44 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.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 of the License, 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
+#
+
+LIBIPROUTE_AR:=libiproute.a
+ifndef $(LIBIPROUTE_DIR)
+LIBIPROUTE_DIR:=$(TOPDIR)networking/libiproute/
+endif
+
+LIBIPROUTE-$(CONFIG_IP) += \
+ ip_parse_common_args.o \
+ ipaddress.o \
+ iplink.o \
+ iproute.o \
+ iptunnel.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_proto.o \
+ ll_types.o \
+ rt_names.o \
+ rtm_map.o \
+ utils.o
+
+libraries-y+=$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR)
+
+$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR): $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
+ $(AR) -ro $@ $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
+
diff --git a/release/src/router/busybox/networking/libiproute/ip_common.h b/release/src/router/busybox/networking/libiproute/ip_common.h
new file mode 100644
index 00000000..25e9c6c8
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/ip_common.h
@@ -0,0 +1,18 @@
+extern int preferred_family;
+extern char * _SL_;
+
+extern void ip_parse_common_args(int *argcp, char ***argvp);
+extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int ipaddr_list_or_flush(int argc, char **argv, int flush);
+extern int iproute_monitor(int argc, char **argv);
+extern void iplink_usage(void) __attribute__((noreturn));
+extern void ipneigh_reset_filter(void);
+extern int do_ipaddr(int argc, char **argv);
+extern int do_iproute(int argc, char **argv);
+extern int do_iprule(int argc, char **argv);
+extern int do_ipneigh(int argc, char **argv);
+extern int do_iptunnel(int argc, char **argv);
+extern int do_iplink(int argc, char **argv);
+extern int do_ipmonitor(int argc, char **argv);
+extern int do_multiaddr(int argc, char **argv);
+extern int do_multiroute(int argc, char **argv);
diff --git a/release/src/router/busybox/networking/libiproute/ip_parse_common_args.c b/release/src/router/busybox/networking/libiproute/ip_parse_common_args.c
new file mode 100644
index 00000000..21e9f74b
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/ip_parse_common_args.c
@@ -0,0 +1,74 @@
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include <string.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+#include "busybox.h"
+
+int preferred_family = AF_UNSPEC;
+int oneline = 0;
+char * _SL_ = NULL;
+
+void ip_parse_common_args(int *argcp, char ***argvp)
+{
+ int argc = *argcp;
+ char **argv = *argvp;
+
+ while (argc > 1) {
+ char *opt = argv[1];
+
+ if (strcmp(opt,"--") == 0) {
+ argc--; argv++;
+ break;
+ }
+
+ if (opt[0] != '-')
+ break;
+
+ if (opt[1] == '-')
+ opt++;
+
+ if (matches(opt, "-family") == 0) {
+ argc--;
+ argv++;
+ if (! argv[1])
+ bb_show_usage();
+ if (strcmp(argv[1], "inet") == 0)
+ preferred_family = AF_INET;
+ else if (strcmp(argv[1], "inet6") == 0)
+ preferred_family = AF_INET6;
+ else if (strcmp(argv[1], "link") == 0)
+ preferred_family = AF_PACKET;
+ else
+ invarg(argv[1], "invalid protocol family");
+ } else if (strcmp(opt, "-4") == 0) {
+ preferred_family = AF_INET;
+ } else if (strcmp(opt, "-6") == 0) {
+ preferred_family = AF_INET6;
+ } else if (strcmp(opt, "-0") == 0) {
+ preferred_family = AF_PACKET;
+ } else if (matches(opt, "-oneline") == 0) {
+ ++oneline;
+ } else {
+ bb_show_usage();
+ }
+ argc--; argv++;
+ }
+ _SL_ = oneline ? "\\" : "\n" ;
+}
diff --git a/release/src/router/busybox/networking/libiproute/ipaddress.c b/release/src/router/busybox/networking/libiproute/ipaddress.c
new file mode 100644
index 00000000..44e871ee
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/ipaddress.c
@@ -0,0 +1,825 @@
+/*
+ * ipaddress.c "ip address".
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
+ */
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <fnmatch.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+#include "libbb.h"
+
+static struct
+{
+ int ifindex;
+ int family;
+ int oneline;
+ int showqueue;
+ inet_prefix pfx;
+ int scope, scopemask;
+ int flags, flagmask;
+ int up;
+ char *label;
+ int flushed;
+ char *flushb;
+ int flushp;
+ int flushe;
+ struct rtnl_handle *rth;
+} filter;
+
+void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
+{
+ fprintf(fp, "<");
+ flags &= ~IFF_RUNNING;
+#define _PF(f) if (flags&IFF_##f) { \
+ flags &= ~IFF_##f ; \
+ fprintf(fp, #f "%s", flags ? "," : ""); }
+ _PF(LOOPBACK);
+ _PF(BROADCAST);
+ _PF(POINTOPOINT);
+ _PF(MULTICAST);
+ _PF(NOARP);
+#if 0
+ _PF(ALLMULTI);
+ _PF(PROMISC);
+ _PF(MASTER);
+ _PF(SLAVE);
+ _PF(DEBUG);
+ _PF(DYNAMIC);
+ _PF(AUTOMEDIA);
+ _PF(PORTSEL);
+ _PF(NOTRAILERS);
+#endif
+ _PF(UP);
+#undef _PF
+ if (flags)
+ fprintf(fp, "%x", flags);
+ if (mdown)
+ fprintf(fp, ",M-DOWN");
+ fprintf(fp, "> ");
+}
+
+static void print_queuelen(char *name)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ return;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, name);
+ if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
+ perror("SIOCGIFXQLEN");
+ close(s);
+ return;
+ }
+ close(s);
+
+ if (ifr.ifr_qlen)
+ printf("qlen %d", ifr.ifr_qlen);
+}
+
+static int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+ FILE *fp = (FILE*)arg;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct rtattr * tb[IFLA_MAX+1];
+ int len = n->nlmsg_len;
+ unsigned m_flag = 0;
+
+ if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
+ return 0;
+
+ len -= NLMSG_LENGTH(sizeof(*ifi));
+ if (len < 0)
+ return -1;
+
+ if (filter.ifindex && ifi->ifi_index != filter.ifindex)
+ return 0;
+ if (filter.up && !(ifi->ifi_flags&IFF_UP))
+ return 0;
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+ if (tb[IFLA_IFNAME] == NULL) {
+ bb_error_msg("nil ifname");
+ return -1;
+ }
+ if (filter.label &&
+ (!filter.family || filter.family == AF_PACKET) &&
+ fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
+ return 0;
+
+ if (n->nlmsg_type == RTM_DELLINK)
+ fprintf(fp, "Deleted ");
+
+ fprintf(fp, "%d: %s", ifi->ifi_index,
+ tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
+
+ if (tb[IFLA_LINK]) {
+ SPRINT_BUF(b1);
+ int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
+ if (iflink == 0)
+ fprintf(fp, "@NONE: ");
+ else {
+ fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
+ m_flag = ll_index_to_flags(iflink);
+ m_flag = !(m_flag & IFF_UP);
+ }
+ } else {
+ fprintf(fp, ": ");
+ }
+ print_link_flags(fp, ifi->ifi_flags, m_flag);
+
+ if (tb[IFLA_MTU])
+ fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
+ if (tb[IFLA_QDISC])
+ fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
+#ifdef IFLA_MASTER
+ if (tb[IFLA_MASTER]) {
+ SPRINT_BUF(b1);
+ fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
+ }
+#endif
+ if (filter.showqueue)
+ print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
+
+ if (!filter.family || filter.family == AF_PACKET) {
+ SPRINT_BUF(b1);
+ fprintf(fp, "%s", _SL_);
+ fprintf(fp, " link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
+
+ if (tb[IFLA_ADDRESS]) {
+ fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
+ RTA_PAYLOAD(tb[IFLA_ADDRESS]),
+ ifi->ifi_type,
+ b1, sizeof(b1)));
+ }
+ if (tb[IFLA_BROADCAST]) {
+ if (ifi->ifi_flags&IFF_POINTOPOINT)
+ fprintf(fp, " peer ");
+ else
+ fprintf(fp, " brd ");
+ fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
+ RTA_PAYLOAD(tb[IFLA_BROADCAST]),
+ ifi->ifi_type,
+ b1, sizeof(b1)));
+ }
+ }
+ fprintf(fp, "\n");
+ fflush(fp);
+ return 0;
+}
+
+static int flush_update(void)
+{
+ if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
+ perror("Failed to send flush request\n");
+ return -1;
+ }
+ filter.flushp = 0;
+ return 0;
+}
+
+static int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+ FILE *fp = (FILE*)arg;
+ struct ifaddrmsg *ifa = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr * rta_tb[IFA_MAX+1];
+ char abuf[256];
+ SPRINT_BUF(b1);
+
+ if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
+ return 0;
+ len -= NLMSG_LENGTH(sizeof(*ifa));
+ if (len < 0) {
+ bb_error_msg("wrong nlmsg len %d", len);
+ return -1;
+ }
+
+ if (filter.flushb && n->nlmsg_type != RTM_NEWADDR)
+ return 0;
+
+ memset(rta_tb, 0, sizeof(rta_tb));
+ parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+
+ if (!rta_tb[IFA_LOCAL])
+ rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
+ if (!rta_tb[IFA_ADDRESS])
+ rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+
+ if (filter.ifindex && filter.ifindex != ifa->ifa_index)
+ return 0;
+ if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
+ return 0;
+ if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
+ return 0;
+ if (filter.label) {
+ const char *label;
+ if (rta_tb[IFA_LABEL])
+ label = RTA_DATA(rta_tb[IFA_LABEL]);
+ else
+ label = ll_idx_n2a(ifa->ifa_index, b1);
+ if (fnmatch(filter.label, label, 0) != 0)
+ return 0;
+ }
+ if (filter.pfx.family) {
+ if (rta_tb[IFA_LOCAL]) {
+ inet_prefix dst;
+ memset(&dst, 0, sizeof(dst));
+ dst.family = ifa->ifa_family;
+ memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL]));
+ if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+ return 0;
+ }
+ }
+
+ if (filter.flushb) {
+ struct nlmsghdr *fn;
+ if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
+ if (flush_update())
+ return -1;
+ }
+ fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+ memcpy(fn, n, n->nlmsg_len);
+ fn->nlmsg_type = RTM_DELADDR;
+ fn->nlmsg_flags = NLM_F_REQUEST;
+ fn->nlmsg_seq = ++filter.rth->seq;
+ filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+ filter.flushed++;
+ return 0;
+ }
+
+ if (n->nlmsg_type == RTM_DELADDR)
+ fprintf(fp, "Deleted ");
+
+ if (filter.oneline)
+ fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
+ if (ifa->ifa_family == AF_INET)
+ fprintf(fp, " inet ");
+ else if (ifa->ifa_family == AF_INET6)
+ fprintf(fp, " inet6 ");
+ else
+ fprintf(fp, " family %d ", ifa->ifa_family);
+
+ if (rta_tb[IFA_LOCAL]) {
+ fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family,
+ RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
+ RTA_DATA(rta_tb[IFA_LOCAL]),
+ abuf, sizeof(abuf)));
+
+ if (rta_tb[IFA_ADDRESS] == NULL ||
+ memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
+ fprintf(fp, "/%d ", ifa->ifa_prefixlen);
+ } else {
+ fprintf(fp, " peer %s/%d ",
+ rt_addr_n2a(ifa->ifa_family,
+ RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
+ RTA_DATA(rta_tb[IFA_ADDRESS]),
+ abuf, sizeof(abuf)),
+ ifa->ifa_prefixlen);
+ }
+ }
+
+ if (rta_tb[IFA_BROADCAST]) {
+ fprintf(fp, "brd %s ",
+ rt_addr_n2a(ifa->ifa_family,
+ RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
+ RTA_DATA(rta_tb[IFA_BROADCAST]),
+ abuf, sizeof(abuf)));
+ }
+ if (rta_tb[IFA_ANYCAST]) {
+ fprintf(fp, "any %s ",
+ rt_addr_n2a(ifa->ifa_family,
+ RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
+ RTA_DATA(rta_tb[IFA_ANYCAST]),
+ abuf, sizeof(abuf)));
+ }
+ fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
+ if (ifa->ifa_flags&IFA_F_SECONDARY) {
+ ifa->ifa_flags &= ~IFA_F_SECONDARY;
+ fprintf(fp, "secondary ");
+ }
+ if (ifa->ifa_flags&IFA_F_TENTATIVE) {
+ ifa->ifa_flags &= ~IFA_F_TENTATIVE;
+ fprintf(fp, "tentative ");
+ }
+ if (ifa->ifa_flags&IFA_F_DEPRECATED) {
+ ifa->ifa_flags &= ~IFA_F_DEPRECATED;
+ fprintf(fp, "deprecated ");
+ }
+ if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
+ fprintf(fp, "dynamic ");
+ } else
+ ifa->ifa_flags &= ~IFA_F_PERMANENT;
+ if (ifa->ifa_flags)
+ fprintf(fp, "flags %02x ", ifa->ifa_flags);
+ if (rta_tb[IFA_LABEL])
+ fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL]));
+ if (rta_tb[IFA_CACHEINFO]) {
+ struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
+ char buf[128];
+ fprintf(fp, "%s", _SL_);
+ if (ci->ifa_valid == 0xFFFFFFFFU)
+ sprintf(buf, "valid_lft forever");
+ else
+ sprintf(buf, "valid_lft %dsec", ci->ifa_valid);
+ if (ci->ifa_prefered == 0xFFFFFFFFU)
+ sprintf(buf+strlen(buf), " preferred_lft forever");
+ else
+ sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered);
+ fprintf(fp, " %s", buf);
+ }
+ fprintf(fp, "\n");
+ fflush(fp);
+ return 0;
+}
+
+
+struct nlmsg_list
+{
+ struct nlmsg_list *next;
+ struct nlmsghdr h;
+};
+
+static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
+{
+ for ( ;ainfo ; ainfo = ainfo->next) {
+ struct nlmsghdr *n = &ainfo->h;
+ struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+ if (n->nlmsg_type != RTM_NEWADDR)
+ continue;
+
+ if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa)))
+ return -1;
+
+ if (ifa->ifa_index != ifindex ||
+ (filter.family && filter.family != ifa->ifa_family))
+ continue;
+
+ print_addrinfo(NULL, n, fp);
+ }
+ return 0;
+}
+
+
+static int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+ struct nlmsg_list **linfo = (struct nlmsg_list**)arg;
+ struct nlmsg_list *h;
+ struct nlmsg_list **lp;
+
+ h = malloc(n->nlmsg_len+sizeof(void*));
+ if (h == NULL)
+ return -1;
+
+ memcpy(&h->h, n, n->nlmsg_len);
+ h->next = NULL;
+
+ for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */;
+ *lp = h;
+
+ ll_remember_index(who, n, NULL);
+ return 0;
+}
+
+static void ipaddr_reset_filter(int _oneline)
+{
+ memset(&filter, 0, sizeof(filter));
+ filter.oneline = _oneline;
+}
+
+extern int ipaddr_list_or_flush(int argc, char **argv, int flush)
+{
+ const char *option[] = { "to", "scope", "up", "label", "dev", 0 };
+ struct nlmsg_list *linfo = NULL;
+ struct nlmsg_list *ainfo = NULL;
+ struct nlmsg_list *l;
+ struct rtnl_handle rth;
+ char *filter_dev = NULL;
+ int no_link = 0;
+
+ ipaddr_reset_filter(oneline);
+ filter.showqueue = 1;
+
+ if (filter.family == AF_UNSPEC)
+ filter.family = preferred_family;
+
+ if (flush) {
+ if (argc <= 0) {
+ fprintf(stderr, "Flush requires arguments.\n");
+ return -1;
+ }
+ if (filter.family == AF_PACKET) {
+ fprintf(stderr, "Cannot flush link addresses.\n");
+ return -1;
+ }
+ }
+
+ while (argc > 0) {
+ const unsigned short option_num = compare_string_array(option, *argv);
+ switch (option_num) {
+ case 0: /* to */
+ NEXT_ARG();
+ get_prefix(&filter.pfx, *argv, filter.family);
+ if (filter.family == AF_UNSPEC) {
+ filter.family = filter.pfx.family;
+ }
+ break;
+ case 1: /* scope */
+ {
+ int scope = 0;
+ NEXT_ARG();
+ filter.scopemask = -1;
+ if (rtnl_rtscope_a2n(&scope, *argv)) {
+ if (strcmp(*argv, "all") != 0) {
+ invarg("invalid \"scope\"\n", *argv);
+ }
+ scope = RT_SCOPE_NOWHERE;
+ filter.scopemask = 0;
+ }
+ filter.scope = scope;
+ break;
+ }
+ case 2: /* up */
+ filter.up = 1;
+ break;
+ case 3: /* label */
+ NEXT_ARG();
+ filter.label = *argv;
+ break;
+ case 4: /* dev */
+ NEXT_ARG();
+ default:
+ if (filter_dev) {
+ duparg2("dev", *argv);
+ }
+ filter_dev = *argv;
+ }
+ argv++;
+ argc--;
+ }
+
+ if (rtnl_open(&rth, 0) < 0)
+ exit(1);
+
+ if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
+ bb_perror_msg_and_die("Cannot send dump request");
+ }
+
+ if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
+ bb_error_msg_and_die("Dump terminated");
+ }
+
+ if (filter_dev) {
+ filter.ifindex = ll_name_to_index(filter_dev);
+ if (filter.ifindex <= 0) {
+ bb_error_msg("Device \"%s\" does not exist.", filter_dev);
+ return -1;
+ }
+ }
+
+ if (flush) {
+ int round = 0;
+ char flushb[4096-512];
+
+ filter.flushb = flushb;
+ filter.flushp = 0;
+ filter.flushe = sizeof(flushb);
+ filter.rth = &rth;
+
+ for (;;) {
+ if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
+ perror("Cannot send dump request");
+ exit(1);
+ }
+ filter.flushed = 0;
+ if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) {
+ fprintf(stderr, "Flush terminated\n");
+ exit(1);
+ }
+ if (filter.flushed == 0) {
+#if 0
+ if (round == 0)
+ fprintf(stderr, "Nothing to flush.\n");
+#endif
+ fflush(stdout);
+ return 0;
+ }
+ round++;
+ if (flush_update() < 0)
+ exit(1);
+ }
+ }
+
+ if (filter.family != AF_PACKET) {
+ if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
+ bb_perror_msg_and_die("Cannot send dump request");
+ }
+
+ if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) {
+ bb_error_msg_and_die("Dump terminated");
+ }
+ }
+
+
+ if (filter.family && filter.family != AF_PACKET) {
+ struct nlmsg_list **lp;
+ lp=&linfo;
+
+ if (filter.oneline)
+ no_link = 1;
+
+ while ((l=*lp)!=NULL) {
+ int ok = 0;
+ struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+ struct nlmsg_list *a;
+
+ for (a=ainfo; a; a=a->next) {
+ struct nlmsghdr *n = &a->h;
+ struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+ if (ifa->ifa_index != ifi->ifi_index ||
+ (filter.family && filter.family != ifa->ifa_family))
+ continue;
+ if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
+ continue;
+ if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
+ continue;
+ if (filter.pfx.family || filter.label) {
+ struct rtattr *tb[IFA_MAX+1];
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
+ if (!tb[IFA_LOCAL])
+ tb[IFA_LOCAL] = tb[IFA_ADDRESS];
+
+ if (filter.pfx.family && tb[IFA_LOCAL]) {
+ inet_prefix dst;
+ memset(&dst, 0, sizeof(dst));
+ dst.family = ifa->ifa_family;
+ memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
+ if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+ continue;
+ }
+ if (filter.label) {
+ SPRINT_BUF(b1);
+ const char *label;
+ if (tb[IFA_LABEL])
+ label = RTA_DATA(tb[IFA_LABEL]);
+ else
+ label = ll_idx_n2a(ifa->ifa_index, b1);
+ if (fnmatch(filter.label, label, 0) != 0)
+ continue;
+ }
+ }
+
+ ok = 1;
+ break;
+ }
+ if (!ok)
+ *lp = l->next;
+ else
+ lp = &l->next;
+ }
+ }
+
+ for (l=linfo; l; l = l->next) {
+ if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
+ struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+ if (filter.family != AF_PACKET)
+ print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
+ }
+ fflush(stdout);
+ }
+
+ exit(0);
+}
+
+static int default_scope(inet_prefix *lcl)
+{
+ if (lcl->family == AF_INET) {
+ if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127)
+ return RT_SCOPE_HOST;
+ }
+ return 0;
+}
+
+static int ipaddr_modify(int cmd, int argc, char **argv)
+{
+ const char *option[] = { "peer", "remote", "broadcast", "brd",
+ "anycast", "scope", "dev", "label", "local", 0 };
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct ifaddrmsg ifa;
+ char buf[256];
+ } req;
+ char *d = NULL;
+ char *l = NULL;
+ inet_prefix lcl;
+ inet_prefix peer;
+ int local_len = 0;
+ int peer_len = 0;
+ int brd_len = 0;
+ int any_len = 0;
+ int scoped = 0;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = cmd;
+ req.ifa.ifa_family = preferred_family;
+
+ while (argc > 0) {
+ const unsigned short option_num = compare_string_array(option, *argv);
+ switch (option_num) {
+ case 0: /* peer */
+ case 1: /* remote */
+ NEXT_ARG();
+
+ if (peer_len) {
+ duparg("peer", *argv);
+ }
+ get_prefix(&peer, *argv, req.ifa.ifa_family);
+ peer_len = peer.bytelen;
+ if (req.ifa.ifa_family == AF_UNSPEC) {
+ req.ifa.ifa_family = peer.family;
+ }
+ addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
+ req.ifa.ifa_prefixlen = peer.bitlen;
+ break;
+ case 2: /* broadcast */
+ case 3: /* brd */
+ {
+ inet_prefix addr;
+ NEXT_ARG();
+ if (brd_len) {
+ duparg("broadcast", *argv);
+ }
+ if (strcmp(*argv, "+") == 0) {
+ brd_len = -1;
+ }
+ else if (strcmp(*argv, "-") == 0) {
+ brd_len = -2;
+ } else {
+ get_addr(&addr, *argv, req.ifa.ifa_family);
+ if (req.ifa.ifa_family == AF_UNSPEC)
+ req.ifa.ifa_family = addr.family;
+ addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
+ brd_len = addr.bytelen;
+ }
+ break;
+ }
+ case 4: /* anycast */
+ {
+ inet_prefix addr;
+ NEXT_ARG();
+ if (any_len) {
+ duparg("anycast", *argv);
+ }
+ get_addr(&addr, *argv, req.ifa.ifa_family);
+ if (req.ifa.ifa_family == AF_UNSPEC) {
+ req.ifa.ifa_family = addr.family;
+ }
+ addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
+ any_len = addr.bytelen;
+ break;
+ }
+ case 5: /* scope */
+ {
+ int scope = 0;
+ NEXT_ARG();
+ if (rtnl_rtscope_a2n(&scope, *argv)) {
+ invarg(*argv, "invalid scope value.");
+ }
+ req.ifa.ifa_scope = scope;
+ scoped = 1;
+ break;
+ }
+ case 6: /* dev */
+ NEXT_ARG();
+ d = *argv;
+ break;
+ case 7: /* label */
+ NEXT_ARG();
+ l = *argv;
+ addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
+ break;
+ case 8: /* local */
+ NEXT_ARG();
+ default:
+ if (local_len) {
+ duparg2("local", *argv);
+ }
+ get_prefix(&lcl, *argv, req.ifa.ifa_family);
+ if (req.ifa.ifa_family == AF_UNSPEC) {
+ req.ifa.ifa_family = lcl.family;
+ }
+ addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
+ local_len = lcl.bytelen;
+ }
+ argc--;
+ argv++;
+ }
+
+ if (d == NULL) {
+ bb_error_msg("Not enough information: \"dev\" argument is required.");
+ return -1;
+ }
+ if (l && matches(d, l) != 0) {
+ bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s).", d, l);
+ }
+
+ if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
+ peer = lcl;
+ addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
+ }
+ if (req.ifa.ifa_prefixlen == 0)
+ req.ifa.ifa_prefixlen = lcl.bitlen;
+
+ if (brd_len < 0 && cmd != RTM_DELADDR) {
+ inet_prefix brd;
+ int i;
+ if (req.ifa.ifa_family != AF_INET) {
+ bb_error_msg("Broadcast can be set only for IPv4 addresses");
+ return -1;
+ }
+ brd = peer;
+ if (brd.bitlen <= 30) {
+ for (i=31; i>=brd.bitlen; i--) {
+ if (brd_len == -1)
+ brd.data[0] |= htonl(1<<(31-i));
+ else
+ brd.data[0] &= ~htonl(1<<(31-i));
+ }
+ addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
+ brd_len = brd.bytelen;
+ }
+ }
+ if (!scoped && cmd != RTM_DELADDR)
+ req.ifa.ifa_scope = default_scope(&lcl);
+
+ if (rtnl_open(&rth, 0) < 0)
+ exit(1);
+
+ ll_init_map(&rth);
+
+ if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
+ bb_error_msg("Cannot find device \"%s\"", d);
+ return -1;
+ }
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ exit(2);
+
+ exit(0);
+}
+
+extern int do_ipaddr(int argc, char **argv)
+{
+ const char *commands[] = { "add", "delete", "list", "show", "lst", "flush", 0 };
+ unsigned short command_num = 2;
+
+ if (*argv) {
+ command_num = compare_string_array(commands, *argv);
+ }
+ switch (command_num) {
+ case 0: /* add */
+ return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1);
+ case 1: /* delete */
+ return ipaddr_modify(RTM_DELADDR, argc-1, argv+1);
+ case 2: /* list */
+ case 3: /* show */
+ case 4: /* lst */
+ return ipaddr_list_or_flush(argc-1, argv+1, 0);
+ case 5: /* flush */
+ return ipaddr_list_or_flush(argc-1, argv+1, 1);
+ }
+ bb_error_msg_and_die("Unknown command %s", *argv);
+}
diff --git a/release/src/router/busybox/networking/libiproute/iplink.c b/release/src/router/busybox/networking/libiproute/iplink.c
new file mode 100644
index 00000000..daab6938
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/iplink.c
@@ -0,0 +1,367 @@
+/*
+ * iplink.c "ip link".
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/version.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <net/if_packet.h>
+#include <netpacket/packet.h>
+
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+#include "libbb.h"
+
+
+/* take from linux/sockios.h */
+#define SIOCSIFNAME 0x8923 /* set interface name */
+
+static int do_link;
+
+static int on_off(char *msg)
+{
+ bb_error_msg("Error: argument of \"%s\" must be \"on\" or \"off\"", msg);
+ return -1;
+}
+
+static int get_ctl_fd(void)
+{
+ int s_errno;
+ int fd;
+
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd >= 0)
+ return fd;
+ s_errno = errno;
+ fd = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (fd >= 0)
+ return fd;
+ fd = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (fd >= 0)
+ return fd;
+ errno = s_errno;
+ perror("Cannot create control socket");
+ return -1;
+}
+
+static int do_chflags(char *dev, __u32 flags, __u32 mask)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ strcpy(ifr.ifr_name, dev);
+ fd = get_ctl_fd();
+ if (fd < 0)
+ return -1;
+ err = ioctl(fd, SIOCGIFFLAGS, &ifr);
+ if (err) {
+ perror("SIOCGIFFLAGS");
+ close(fd);
+ return -1;
+ }
+ if ((ifr.ifr_flags^flags)&mask) {
+ ifr.ifr_flags &= ~mask;
+ ifr.ifr_flags |= mask&flags;
+ err = ioctl(fd, SIOCSIFFLAGS, &ifr);
+ if (err)
+ perror("SIOCSIFFLAGS");
+ }
+ close(fd);
+ return err;
+}
+
+static int do_changename(char *dev, char *newdev)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ strcpy(ifr.ifr_name, dev);
+ strcpy(ifr.ifr_newname, newdev);
+ fd = get_ctl_fd();
+ if (fd < 0)
+ return -1;
+ err = ioctl(fd, SIOCSIFNAME, &ifr);
+ if (err) {
+ perror("SIOCSIFNAME");
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return err;
+#endif
+ return 0;
+}
+
+static int set_qlen(char *dev, int qlen)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = get_ctl_fd();
+ if (s < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, dev);
+ ifr.ifr_qlen = qlen;
+ if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
+ perror("SIOCSIFXQLEN");
+ close(s);
+ return -1;
+ }
+ close(s);
+
+ return 0;
+}
+
+static int set_mtu(char *dev, int mtu)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = get_ctl_fd();
+ if (s < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, dev);
+ ifr.ifr_mtu = mtu;
+ if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
+ perror("SIOCSIFMTU");
+ close(s);
+ return -1;
+ }
+ close(s);
+
+ return 0;
+}
+
+static int get_address(char *dev, int *htype)
+{
+ struct ifreq ifr;
+ struct sockaddr_ll me;
+ int alen;
+ int s;
+
+ s = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket(PF_PACKET)");
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, dev);
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ perror("SIOCGIFINDEX");
+ close(s);
+ return -1;
+ }
+
+ memset(&me, 0, sizeof(me));
+ me.sll_family = AF_PACKET;
+ me.sll_ifindex = ifr.ifr_ifindex;
+ me.sll_protocol = htons(ETH_P_LOOP);
+ if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
+ perror("bind");
+ close(s);
+ return -1;
+ }
+
+ alen = sizeof(me);
+ if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
+ perror("getsockname");
+ close(s);
+ return -1;
+ }
+ close(s);
+ *htype = me.sll_hatype;
+ return me.sll_halen;
+}
+
+static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
+{
+ int alen;
+
+ memset(ifr, 0, sizeof(*ifr));
+ strcpy(ifr->ifr_name, dev);
+ ifr->ifr_hwaddr.sa_family = hatype;
+ alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
+ if (alen < 0)
+ return -1;
+ if (alen != halen) {
+ bb_error_msg("Wrong address (%s) length: expected %d bytes", lla, halen);
+ return -1;
+ }
+ return 0;
+}
+
+static int set_address(struct ifreq *ifr, int brd)
+{
+ int s;
+
+ s = get_ctl_fd();
+ if (s < 0)
+ return -1;
+ if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
+ perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
+ close(s);
+ return -1;
+ }
+ close(s);
+ return 0;
+}
+
+
+static int do_set(int argc, char **argv)
+{
+ char *dev = NULL;
+ __u32 mask = 0;
+ __u32 flags = 0;
+ int qlen = -1;
+ int mtu = -1;
+ char *newaddr = NULL;
+ char *newbrd = NULL;
+ struct ifreq ifr0, ifr1;
+ char *newname = NULL;
+ int htype, halen;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "up") == 0) {
+ mask |= IFF_UP;
+ flags |= IFF_UP;
+ } else if (strcmp(*argv, "down") == 0) {
+ mask |= IFF_UP;
+ flags &= ~IFF_UP;
+ } else if (strcmp(*argv, "name") == 0) {
+ NEXT_ARG();
+ newname = *argv;
+ } else if (strcmp(*argv, "mtu") == 0) {
+ NEXT_ARG();
+ if (mtu != -1)
+ duparg("mtu", *argv);
+ if (get_integer(&mtu, *argv, 0))
+ invarg("Invalid \"mtu\" value\n", *argv);
+ } else if (strcmp(*argv, "multicast") == 0) {
+ NEXT_ARG();
+ mask |= IFF_MULTICAST;
+ if (strcmp(*argv, "on") == 0) {
+ flags |= IFF_MULTICAST;
+ } else if (strcmp(*argv, "off") == 0) {
+ flags &= ~IFF_MULTICAST;
+ } else
+ return on_off("multicast");
+ } else if (strcmp(*argv, "arp") == 0) {
+ NEXT_ARG();
+ mask |= IFF_NOARP;
+ if (strcmp(*argv, "on") == 0) {
+ flags &= ~IFF_NOARP;
+ } else if (strcmp(*argv, "off") == 0) {
+ flags |= IFF_NOARP;
+ } else
+ return on_off("noarp");
+ } else {
+ if (strcmp(*argv, "dev") == 0) {
+ NEXT_ARG();
+ }
+ if (dev)
+ duparg2("dev", *argv);
+ dev = *argv;
+ }
+ argc--; argv++;
+ }
+
+ if (!dev) {
+ bb_error_msg("Not enough of information: \"dev\" argument is required.");
+ exit(-1);
+ }
+
+ if (newaddr || newbrd) {
+ halen = get_address(dev, &htype);
+ if (halen < 0)
+ return -1;
+ if (newaddr) {
+ if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
+ return -1;
+ }
+ if (newbrd) {
+ if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
+ return -1;
+ }
+ }
+
+ if (newname && strcmp(dev, newname)) {
+ if (do_changename(dev, newname) < 0)
+ return -1;
+ dev = newname;
+ }
+ if (qlen != -1) {
+ if (set_qlen(dev, qlen) < 0)
+ return -1;
+ }
+ if (mtu != -1) {
+ if (set_mtu(dev, mtu) < 0)
+ return -1;
+ }
+ if (newaddr || newbrd) {
+ if (newbrd) {
+ if (set_address(&ifr1, 1) < 0)
+ return -1;
+ }
+ if (newaddr) {
+ if (set_address(&ifr0, 0) < 0)
+ return -1;
+ }
+ }
+ if (mask)
+ return do_chflags(dev, flags, mask);
+ return 0;
+}
+
+static int ipaddr_list_link(int argc, char **argv)
+{
+ preferred_family = AF_PACKET;
+ do_link = 1;
+ return ipaddr_list_or_flush(argc, argv, 0);
+}
+
+int do_iplink(int argc, char **argv)
+{
+ if (argc > 0) {
+ if (matches(*argv, "set") == 0)
+ return do_set(argc-1, argv+1);
+ if (matches(*argv, "show") == 0 ||
+ matches(*argv, "lst") == 0 ||
+ matches(*argv, "list") == 0)
+ return ipaddr_list_link(argc-1, argv+1);
+ } else
+ return ipaddr_list_link(0, NULL);
+
+ bb_error_msg("Command \"%s\" is unknown, try \"ip link help\".", *argv);
+ exit(-1);
+}
diff --git a/release/src/router/busybox/networking/libiproute/iproute.c b/release/src/router/busybox/networking/libiproute/iproute.c
new file mode 100644
index 00000000..70ddf9f4
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/iproute.c
@@ -0,0 +1,852 @@
+/*
+ * iproute.c "ip route".
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
+ */
+
+#include <sys/socket.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+#include "libbb.h"
+
+#ifndef RTAX_RTTVAR
+#define RTAX_RTTVAR RTAX_HOPS
+#endif
+
+
+static struct
+{
+ int tb;
+ int flushed;
+ char *flushb;
+ int flushp;
+ int flushe;
+ struct rtnl_handle *rth;
+ int protocol, protocolmask;
+ int scope, scopemask;
+ int type, typemask;
+ int tos, tosmask;
+ int iif, iifmask;
+ int oif, oifmask;
+ int realm, realmmask;
+ inet_prefix rprefsrc;
+ inet_prefix rvia;
+ inet_prefix rdst;
+ inet_prefix mdst;
+ inet_prefix rsrc;
+ inet_prefix msrc;
+} filter;
+
+static int flush_update(void)
+{
+ if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
+ perror("Failed to send flush request\n");
+ return -1;
+ }
+ filter.flushp = 0;
+ return 0;
+}
+
+static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+ FILE *fp = (FILE*)arg;
+ struct rtmsg *r = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr * tb[RTA_MAX+1];
+ char abuf[256];
+ inet_prefix dst;
+ inet_prefix src;
+ int host_len = -1;
+ SPRINT_BUF(b1);
+
+
+ if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
+ fprintf(stderr, "Not a route: %08x %08x %08x\n",
+ n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+ return 0;
+ }
+ if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
+ return 0;
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0) {
+ bb_error_msg("wrong nlmsg len %d", len);
+ return -1;
+ }
+
+ if (r->rtm_family == AF_INET6)
+ host_len = 128;
+ else if (r->rtm_family == AF_INET)
+ host_len = 32;
+
+ if (r->rtm_family == AF_INET6) {
+ if (filter.tb) {
+ if (filter.tb < 0) {
+ if (!(r->rtm_flags&RTM_F_CLONED)) {
+ return 0;
+ }
+ } else {
+ if (r->rtm_flags&RTM_F_CLONED) {
+ return 0;
+ }
+ if (filter.tb == RT_TABLE_LOCAL) {
+ if (r->rtm_type != RTN_LOCAL) {
+ return 0;
+ }
+ } else if (filter.tb == RT_TABLE_MAIN) {
+ if (r->rtm_type == RTN_LOCAL) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+ }
+ } else {
+ if (filter.tb > 0 && filter.tb != r->rtm_table) {
+ return 0;
+ }
+ }
+ if (filter.rdst.family &&
+ (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) {
+ return 0;
+ }
+ if (filter.mdst.family &&
+ (r->rtm_family != filter.mdst.family ||
+ (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) {
+ return 0;
+ }
+ if (filter.rsrc.family &&
+ (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) {
+ return 0;
+ }
+ if (filter.msrc.family &&
+ (r->rtm_family != filter.msrc.family ||
+ (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) {
+ return 0;
+ }
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+ if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen))
+ return 0;
+ if (filter.mdst.family && filter.mdst.bitlen >= 0 &&
+ inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len))
+ return 0;
+
+ if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen))
+ return 0;
+ if (filter.msrc.family && filter.msrc.bitlen >= 0 &&
+ inet_addr_match(&src, &filter.msrc, r->rtm_src_len))
+ return 0;
+
+ if (filter.flushb &&
+ r->rtm_family == AF_INET6 &&
+ r->rtm_dst_len == 0 &&
+ r->rtm_type == RTN_UNREACHABLE &&
+ tb[RTA_PRIORITY] &&
+ *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1)
+ return 0;
+
+ if (filter.flushb) {
+ struct nlmsghdr *fn;
+ if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
+ if (flush_update())
+ return -1;
+ }
+ fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+ memcpy(fn, n, n->nlmsg_len);
+ fn->nlmsg_type = RTM_DELROUTE;
+ fn->nlmsg_flags = NLM_F_REQUEST;
+ fn->nlmsg_seq = ++filter.rth->seq;
+ filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+ filter.flushed++;
+ return 0;
+ }
+
+ if (n->nlmsg_type == RTM_DELROUTE) {
+ fprintf(fp, "Deleted ");
+ }
+ if (r->rtm_type != RTN_UNICAST && !filter.type) {
+ fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+ }
+
+ if (tb[RTA_DST]) {
+ if (r->rtm_dst_len != host_len) {
+ fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_DST]),
+ RTA_DATA(tb[RTA_DST]),
+ abuf, sizeof(abuf)),
+ r->rtm_dst_len
+ );
+ } else {
+ fprintf(fp, "%s ", format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_DST]),
+ RTA_DATA(tb[RTA_DST]),
+ abuf, sizeof(abuf))
+ );
+ }
+ } else if (r->rtm_dst_len) {
+ fprintf(fp, "0/%d ", r->rtm_dst_len);
+ } else {
+ fprintf(fp, "default ");
+ }
+ if (tb[RTA_SRC]) {
+ if (r->rtm_src_len != host_len) {
+ fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_SRC]),
+ RTA_DATA(tb[RTA_SRC]),
+ abuf, sizeof(abuf)),
+ r->rtm_src_len
+ );
+ } else {
+ fprintf(fp, "from %s ", format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_SRC]),
+ RTA_DATA(tb[RTA_SRC]),
+ abuf, sizeof(abuf))
+ );
+ }
+ } else if (r->rtm_src_len) {
+ fprintf(fp, "from 0/%u ", r->rtm_src_len);
+ }
+ if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
+ fprintf(fp, "via %s ",
+ format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_GATEWAY]),
+ RTA_DATA(tb[RTA_GATEWAY]),
+ abuf, sizeof(abuf)));
+ }
+ if (tb[RTA_OIF] && filter.oifmask != -1) {
+ fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
+ }
+
+ if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
+ /* Do not use format_host(). It is our local addr
+ and symbolic name will not be useful.
+ */
+ fprintf(fp, " src %s ",
+ rt_addr_n2a(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_PREFSRC]),
+ RTA_DATA(tb[RTA_PREFSRC]),
+ abuf, sizeof(abuf)));
+ }
+ if (tb[RTA_PRIORITY]) {
+ fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY]));
+ }
+ if (r->rtm_family == AF_INET6) {
+ struct rta_cacheinfo *ci = NULL;
+ if (tb[RTA_CACHEINFO]) {
+ ci = RTA_DATA(tb[RTA_CACHEINFO]);
+ }
+ if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
+ static int hz;
+ if (!hz) {
+ hz = get_hz();
+ }
+ if (r->rtm_flags & RTM_F_CLONED) {
+ fprintf(fp, "%s cache ", _SL_);
+ }
+ if (ci->rta_expires) {
+ fprintf(fp, " expires %dsec", ci->rta_expires/hz);
+ }
+ if (ci->rta_error != 0) {
+ fprintf(fp, " error %d", ci->rta_error);
+ }
+ } else if (ci) {
+ if (ci->rta_error != 0)
+ fprintf(fp, " error %d", ci->rta_error);
+ }
+ }
+ if (tb[RTA_IIF] && filter.iifmask != -1) {
+ fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
+ }
+ fprintf(fp, "\n");
+ fflush(fp);
+ return 0;
+}
+
+static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
+{
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+ char mxbuf[256];
+ struct rtattr * mxrta = (void*)mxbuf;
+ unsigned mxlock = 0;
+ char *d = NULL;
+ int gw_ok = 0;
+ int dst_ok = 0;
+ int proto_ok = 0;
+ int type_ok = 0;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+ req.n.nlmsg_type = cmd;
+ req.r.rtm_family = preferred_family;
+ req.r.rtm_table = RT_TABLE_MAIN;
+ req.r.rtm_scope = RT_SCOPE_NOWHERE;
+
+ if (cmd != RTM_DELROUTE) {
+ req.r.rtm_protocol = RTPROT_BOOT;
+ req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+ req.r.rtm_type = RTN_UNICAST;
+ }
+
+ mxrta->rta_type = RTA_METRICS;
+ mxrta->rta_len = RTA_LENGTH(0);
+
+ while (argc > 0) {
+ if (strcmp(*argv, "src") == 0) {
+ inet_prefix addr;
+ NEXT_ARG();
+ get_addr(&addr, *argv, req.r.rtm_family);
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = addr.family;
+ }
+ addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
+ } else if (strcmp(*argv, "via") == 0) {
+ inet_prefix addr;
+ gw_ok = 1;
+ NEXT_ARG();
+ get_addr(&addr, *argv, req.r.rtm_family);
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = addr.family;
+ }
+ addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
+ } else if (strcmp(*argv, "mtu") == 0) {
+ unsigned mtu;
+ NEXT_ARG();
+ if (strcmp(*argv, "lock") == 0) {
+ mxlock |= (1<<RTAX_MTU);
+ NEXT_ARG();
+ }
+ if (get_unsigned(&mtu, *argv, 0)) {
+ invarg("\"mtu\" value is invalid\n", *argv);
+ }
+ rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
+ } else if (matches(*argv, "protocol") == 0) {
+ int prot;
+ NEXT_ARG();
+ if (rtnl_rtprot_a2n(&prot, *argv))
+ invarg("\"protocol\" value is invalid\n", *argv);
+ req.r.rtm_protocol = prot;
+ proto_ok =1;
+ } else if (strcmp(*argv, "dev") == 0 ||
+ strcmp(*argv, "oif") == 0) {
+ NEXT_ARG();
+ d = *argv;
+ } else {
+ int type;
+ inet_prefix dst;
+
+ if (strcmp(*argv, "to") == 0) {
+ NEXT_ARG();
+ }
+ if ((**argv < '0' || **argv > '9') &&
+ rtnl_rtntype_a2n(&type, *argv) == 0) {
+ NEXT_ARG();
+ req.r.rtm_type = type;
+ type_ok = 1;
+ }
+
+ if (dst_ok) {
+ duparg2("to", *argv);
+ }
+ get_prefix(&dst, *argv, req.r.rtm_family);
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = dst.family;
+ }
+ req.r.rtm_dst_len = dst.bitlen;
+ dst_ok = 1;
+ if (dst.bytelen) {
+ addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
+ }
+ }
+ argc--; argv++;
+ }
+
+ if (rtnl_open(&rth, 0) < 0) {
+ exit(1);
+ }
+
+ if (d) {
+ int idx;
+
+ ll_init_map(&rth);
+
+ if (d) {
+ if ((idx = ll_name_to_index(d)) == 0) {
+ bb_error_msg("Cannot find device \"%s\"", d);
+ return -1;
+ }
+ addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+ }
+ }
+
+ if (mxrta->rta_len > RTA_LENGTH(0)) {
+ if (mxlock) {
+ rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
+ }
+ addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
+ }
+
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = AF_INET;
+ }
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
+ exit(2);
+ }
+
+ return 0;
+}
+
+static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtmsg rtm;
+ } req;
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ memset(&req, 0, sizeof(req));
+ nladdr.nl_family = AF_NETLINK;
+
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = RTM_GETROUTE;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.rtm.rtm_family = family;
+ req.rtm.rtm_flags |= RTM_F_CLONED;
+
+ return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+static int iproute_flush_cache(void)
+{
+#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush"
+
+ int len;
+ int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY);
+ char *buffer = "-1";
+
+ if (flush_fd < 0) {
+ fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH);
+ return -1;
+ }
+
+ len = strlen (buffer);
+
+ if ((write (flush_fd, (void *)buffer, len)) < len) {
+ fprintf (stderr, "Cannot flush routing cache\n");
+ return -1;
+ }
+ close(flush_fd);
+ return 0;
+}
+
+static void iproute_reset_filter(void)
+{
+ memset(&filter, 0, sizeof(filter));
+ filter.mdst.bitlen = -1;
+ filter.msrc.bitlen = -1;
+}
+
+static int iproute_list_or_flush(int argc, char **argv, int flush)
+{
+ int do_ipv6 = preferred_family;
+ struct rtnl_handle rth;
+ char *id = NULL;
+ char *od = NULL;
+
+ iproute_reset_filter();
+ filter.tb = RT_TABLE_MAIN;
+
+ if (flush && argc <= 0) {
+ fprintf(stderr, "\"ip route flush\" requires arguments.\n");
+ return -1;
+ }
+
+ while (argc > 0) {
+ if (matches(*argv, "protocol") == 0) {
+ int prot = 0;
+ NEXT_ARG();
+ filter.protocolmask = -1;
+ if (rtnl_rtprot_a2n(&prot, *argv)) {
+ if (strcmp(*argv, "all") != 0) {
+ invarg("invalid \"protocol\"\n", *argv);
+ }
+ prot = 0;
+ filter.protocolmask = 0;
+ }
+ filter.protocol = prot;
+ } else if (strcmp(*argv, "dev") == 0 ||
+ strcmp(*argv, "oif") == 0) {
+ NEXT_ARG();
+ od = *argv;
+ } else if (strcmp(*argv, "iif") == 0) {
+ NEXT_ARG();
+ id = *argv;
+ } else if (matches(*argv, "from") == 0) {
+ NEXT_ARG();
+ if (matches(*argv, "root") == 0) {
+ NEXT_ARG();
+ get_prefix(&filter.rsrc, *argv, do_ipv6);
+ } else if (matches(*argv, "match") == 0) {
+ NEXT_ARG();
+ get_prefix(&filter.msrc, *argv, do_ipv6);
+ } else {
+ if (matches(*argv, "exact") == 0) {
+ NEXT_ARG();
+ }
+ get_prefix(&filter.msrc, *argv, do_ipv6);
+ filter.rsrc = filter.msrc;
+ }
+ } else {
+ if (matches(*argv, "to") == 0) {
+ NEXT_ARG();
+ }
+ if (matches(*argv, "root") == 0) {
+ NEXT_ARG();
+ get_prefix(&filter.rdst, *argv, do_ipv6);
+ } else if (matches(*argv, "match") == 0) {
+ NEXT_ARG();
+ get_prefix(&filter.mdst, *argv, do_ipv6);
+ } else {
+ if (matches(*argv, "exact") == 0) {
+ NEXT_ARG();
+ }
+ get_prefix(&filter.mdst, *argv, do_ipv6);
+ filter.rdst = filter.mdst;
+ }
+ }
+ argc--; argv++;
+ }
+
+ if (do_ipv6 == AF_UNSPEC && filter.tb) {
+ do_ipv6 = AF_INET;
+ }
+
+ if (rtnl_open(&rth, 0) < 0) {
+ exit(1);
+ }
+
+ ll_init_map(&rth);
+
+ if (id || od) {
+ int idx;
+
+ if (id) {
+ if ((idx = ll_name_to_index(id)) == 0) {
+ bb_error_msg("Cannot find device \"%s\"", id);
+ return -1;
+ }
+ filter.iif = idx;
+ filter.iifmask = -1;
+ }
+ if (od) {
+ if ((idx = ll_name_to_index(od)) == 0) {
+ bb_error_msg("Cannot find device \"%s\"", od);
+ }
+ filter.oif = idx;
+ filter.oifmask = -1;
+ }
+ }
+
+ if (flush) {
+ int round = 0;
+ char flushb[4096-512];
+
+ if (filter.tb == -1) {
+ if (do_ipv6 != AF_INET6)
+ iproute_flush_cache();
+ if (do_ipv6 == AF_INET)
+ return 0;
+ }
+
+ filter.flushb = flushb;
+ filter.flushp = 0;
+ filter.flushe = sizeof(flushb);
+ filter.rth = &rth;
+
+ for (;;) {
+ if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
+ perror("Cannot send dump request");
+ return -1;
+ }
+ filter.flushed = 0;
+ if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
+ bb_error_msg("Flush terminated\n");
+ return -1;
+ }
+ if (filter.flushed == 0) {
+ if (round == 0) {
+ if (filter.tb != -1 || do_ipv6 == AF_INET6)
+ fprintf(stderr, "Nothing to flush.\n");
+ }
+ fflush(stdout);
+ return 0;
+ }
+ round++;
+ if (flush_update() < 0)
+ exit(1);
+ }
+ }
+
+ if (filter.tb != -1) {
+ if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
+ bb_perror_msg_and_die("Cannot send dump request");
+ }
+ } else {
+ if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
+ bb_perror_msg_and_die("Cannot send dump request");
+ }
+ }
+
+ if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
+ bb_error_msg_and_die("Dump terminated");
+ }
+
+ exit(0);
+}
+
+
+static int iproute_get(int argc, char **argv)
+{
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+ char *idev = NULL;
+ char *odev = NULL;
+ int connected = 0;
+ int from_ok = 0;
+ const char *options[] = { "from", "iif", "oif", "dev", "notify", "connected", "to", 0 };
+
+ memset(&req, 0, sizeof(req));
+
+ iproute_reset_filter();
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETROUTE;
+ req.r.rtm_family = preferred_family;
+ req.r.rtm_table = 0;
+ req.r.rtm_protocol = 0;
+ req.r.rtm_scope = 0;
+ req.r.rtm_type = 0;
+ req.r.rtm_src_len = 0;
+ req.r.rtm_dst_len = 0;
+ req.r.rtm_tos = 0;
+
+ while (argc > 0) {
+ switch (compare_string_array(options, *argv)) {
+ case 0: /* from */
+ {
+ inet_prefix addr;
+ NEXT_ARG();
+ from_ok = 1;
+ get_prefix(&addr, *argv, req.r.rtm_family);
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = addr.family;
+ }
+ if (addr.bytelen) {
+ addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
+ }
+ req.r.rtm_src_len = addr.bitlen;
+ break;
+ }
+ case 1: /* iif */
+ NEXT_ARG();
+ idev = *argv;
+ break;
+ case 2: /* oif */
+ case 3: /* dev */
+ NEXT_ARG();
+ odev = *argv;
+ break;
+ case 4: /* notify */
+ req.r.rtm_flags |= RTM_F_NOTIFY;
+ break;
+ case 5: /* connected */
+ connected = 1;
+ break;
+ case 6: /* to */
+ NEXT_ARG();
+ default:
+ {
+ inet_prefix addr;
+ get_prefix(&addr, *argv, req.r.rtm_family);
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = addr.family;
+ }
+ if (addr.bytelen) {
+ addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
+ }
+ req.r.rtm_dst_len = addr.bitlen;
+ }
+ argc--; argv++;
+ }
+ }
+
+ if (req.r.rtm_dst_len == 0) {
+ bb_error_msg_and_die("need at least destination address");
+ }
+
+ if (rtnl_open(&rth, 0) < 0)
+ exit(1);
+
+ ll_init_map(&rth);
+
+ if (idev || odev) {
+ int idx;
+
+ if (idev) {
+ if ((idx = ll_name_to_index(idev)) == 0) {
+ bb_error_msg("Cannot find device \"%s\"", idev);
+ return -1;
+ }
+ addattr32(&req.n, sizeof(req), RTA_IIF, idx);
+ }
+ if (odev) {
+ if ((idx = ll_name_to_index(odev)) == 0) {
+ bb_error_msg("Cannot find device \"%s\"", odev);
+ return -1;
+ }
+ addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+ }
+ }
+
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = AF_INET;
+ }
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+ exit(2);
+ }
+
+ if (connected && !from_ok) {
+ struct rtmsg *r = NLMSG_DATA(&req.n);
+ int len = req.n.nlmsg_len;
+ struct rtattr * tb[RTA_MAX+1];
+
+ if (print_route(NULL, &req.n, (void*)stdout) < 0) {
+ bb_error_msg_and_die("An error :-)");
+ }
+
+ if (req.n.nlmsg_type != RTM_NEWROUTE) {
+ bb_error_msg("Not a route?");
+ return -1;
+ }
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0) {
+ bb_error_msg("Wrong len %d", len);
+ return -1;
+ }
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+ if (tb[RTA_PREFSRC]) {
+ tb[RTA_PREFSRC]->rta_type = RTA_SRC;
+ r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
+ } else if (!tb[RTA_SRC]) {
+ bb_error_msg("Failed to connect the route");
+ return -1;
+ }
+ if (!odev && tb[RTA_OIF]) {
+ tb[RTA_OIF]->rta_type = 0;
+ }
+ if (tb[RTA_GATEWAY]) {
+ tb[RTA_GATEWAY]->rta_type = 0;
+ }
+ if (!idev && tb[RTA_IIF]) {
+ tb[RTA_IIF]->rta_type = 0;
+ }
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETROUTE;
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+ exit(2);
+ }
+ }
+
+ if (print_route(NULL, &req.n, (void*)stdout) < 0) {
+ bb_error_msg_and_die("An error :-)");
+ }
+
+ exit(0);
+}
+
+int do_iproute(int argc, char **argv)
+{
+ const char *ip_route_commands[] = { "add", "append", "change", "chg",
+ "delete", "get", "list", "show", "prepend", "replace", "test", "flush", 0 };
+ unsigned short command_num = 6;
+ unsigned int flags = 0;
+ int cmd = RTM_NEWROUTE;
+
+ if (*argv) {
+ command_num = compare_string_array(ip_route_commands, *argv);
+ }
+ switch(command_num) {
+ case 0: /* add*/
+ flags = NLM_F_CREATE|NLM_F_EXCL;
+ break;
+ case 1: /* append */
+ flags = NLM_F_CREATE|NLM_F_APPEND;
+ break;
+ case 2: /* change */
+ case 3: /* chg */
+ flags = NLM_F_REPLACE;
+ break;
+ case 4: /* delete */
+ cmd = RTM_DELROUTE;
+ break;
+ case 5: /* get */
+ return iproute_get(argc-1, argv+1);
+ case 6: /* list */
+ case 7: /* show */
+ return iproute_list_or_flush(argc-1, argv+1, 0);
+ case 8: /* prepend */
+ flags = NLM_F_CREATE;
+ case 9: /* replace */
+ flags = NLM_F_CREATE|NLM_F_REPLACE;
+ case 10: /* test */
+ flags = NLM_F_EXCL;
+ case 11: /* flush */
+ return iproute_list_or_flush(argc-1, argv+1, 1);
+ default:
+ bb_error_msg_and_die("Unknown command %s", *argv);
+ }
+
+ return iproute_modify(cmd, flags, argc-1, argv+1);
+}
+
diff --git a/release/src/router/busybox/networking/libiproute/iptunnel.c b/release/src/router/busybox/networking/libiproute/iptunnel.c
new file mode 100644
index 00000000..eae5bb09
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/iptunnel.c
@@ -0,0 +1,548 @@
+/*
+ * iptunnel.c "ip tunnel"
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
+ * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag
+ */
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netinet/in.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include <asm/types.h>
+#define __constant_htons htons
+#include <linux/if_tunnel.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+#include "libbb.h"
+
+static int do_ioctl_get_ifindex(char *dev)
+{
+ struct ifreq ifr;
+ int fd;
+
+ strcpy(ifr.ifr_name, dev);
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
+ bb_perror_msg("ioctl");
+ return 0;
+ }
+ close(fd);
+ return ifr.ifr_ifindex;
+}
+
+static int do_ioctl_get_iftype(char *dev)
+{
+ struct ifreq ifr;
+ int fd;
+
+ strcpy(ifr.ifr_name, dev);
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr)) {
+ bb_perror_msg("ioctl");
+ return -1;
+ }
+ close(fd);
+ return ifr.ifr_addr.sa_family;
+}
+
+
+static char *do_ioctl_get_ifname(int idx)
+{
+ static struct ifreq ifr;
+ int fd;
+
+ ifr.ifr_ifindex = idx;
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ioctl(fd, SIOCGIFNAME, &ifr)) {
+ bb_perror_msg("ioctl");
+ return NULL;
+ }
+ close(fd);
+ return ifr.ifr_name;
+}
+
+
+
+static int do_get_ioctl(char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ strcpy(ifr.ifr_name, basedev);
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl(fd, SIOCGETTUNNEL, &ifr);
+ if (err) {
+ bb_perror_msg("ioctl");
+ }
+ close(fd);
+ return err;
+}
+
+static int do_add_ioctl(int cmd, char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ if (cmd == SIOCCHGTUNNEL && p->name[0]) {
+ strcpy(ifr.ifr_name, p->name);
+ } else {
+ strcpy(ifr.ifr_name, basedev);
+ }
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl(fd, cmd, &ifr);
+ if (err) {
+ bb_perror_msg("ioctl");
+ }
+ close(fd);
+ return err;
+}
+
+static int do_del_ioctl(char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ if (p->name[0]) {
+ strcpy(ifr.ifr_name, p->name);
+ } else {
+ strcpy(ifr.ifr_name, basedev);
+ }
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl(fd, SIOCDELTUNNEL, &ifr);
+ if (err) {
+ bb_perror_msg("ioctl");
+ }
+ close(fd);
+ return err;
+}
+
+static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
+{
+ int count = 0;
+ char medium[IFNAMSIZ];
+ memset(p, 0, sizeof(*p));
+ memset(&medium, 0, sizeof(medium));
+
+ p->iph.version = 4;
+ p->iph.ihl = 5;
+#ifndef IP_DF
+#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
+#endif
+ p->iph.frag_off = htons(IP_DF);
+
+ while (argc > 0) {
+ if (strcmp(*argv, "mode") == 0) {
+ NEXT_ARG();
+ if (strcmp(*argv, "ipip") == 0 ||
+ strcmp(*argv, "ip/ip") == 0) {
+ if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
+ bb_error_msg("You managed to ask for more than one tunnel mode.");
+ exit(-1);
+ }
+ p->iph.protocol = IPPROTO_IPIP;
+ } else if (strcmp(*argv, "gre") == 0 ||
+ strcmp(*argv, "gre/ip") == 0) {
+ if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
+ bb_error_msg("You managed to ask for more than one tunnel mode.");
+ exit(-1);
+ }
+ p->iph.protocol = IPPROTO_GRE;
+ } else if (strcmp(*argv, "sit") == 0 ||
+ strcmp(*argv, "ipv6/ip") == 0) {
+ if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
+ bb_error_msg("You managed to ask for more than one tunnel mode.");
+ exit(-1);
+ }
+ p->iph.protocol = IPPROTO_IPV6;
+ } else {
+ bb_error_msg("Cannot guess tunnel mode.");
+ exit(-1);
+ }
+ } else if (strcmp(*argv, "key") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->i_flags |= GRE_KEY;
+ p->o_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->i_key = p->o_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ bb_error_msg("invalid value of \"key\"");
+ exit(-1);
+ }
+ p->i_key = p->o_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "ikey") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->i_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->o_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ bb_error_msg("invalid value of \"ikey\"");
+ exit(-1);
+ }
+ p->i_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "okey") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->o_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->o_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ bb_error_msg("invalid value of \"okey\"");
+ exit(-1);
+ }
+ p->o_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "seq") == 0) {
+ p->i_flags |= GRE_SEQ;
+ p->o_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "iseq") == 0) {
+ p->i_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "oseq") == 0) {
+ p->o_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "csum") == 0) {
+ p->i_flags |= GRE_CSUM;
+ p->o_flags |= GRE_CSUM;
+ } else if (strcmp(*argv, "icsum") == 0) {
+ p->i_flags |= GRE_CSUM;
+ } else if (strcmp(*argv, "ocsum") == 0) {
+ p->o_flags |= GRE_CSUM;
+ } else if (strcmp(*argv, "nopmtudisc") == 0) {
+ p->iph.frag_off = 0;
+ } else if (strcmp(*argv, "pmtudisc") == 0) {
+ p->iph.frag_off = htons(IP_DF);
+ } else if (strcmp(*argv, "remote") == 0) {
+ NEXT_ARG();
+ if (strcmp(*argv, "any"))
+ p->iph.daddr = get_addr32(*argv);
+ } else if (strcmp(*argv, "local") == 0) {
+ NEXT_ARG();
+ if (strcmp(*argv, "any"))
+ p->iph.saddr = get_addr32(*argv);
+ } else if (strcmp(*argv, "dev") == 0) {
+ NEXT_ARG();
+ strncpy(medium, *argv, IFNAMSIZ-1);
+ } else if (strcmp(*argv, "ttl") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ if (strcmp(*argv, "inherit") != 0) {
+ if (get_unsigned(&uval, *argv, 0))
+ invarg("invalid TTL\n", *argv);
+ if (uval > 255)
+ invarg("TTL must be <=255\n", *argv);
+ p->iph.ttl = uval;
+ }
+ } else if (strcmp(*argv, "tos") == 0 ||
+ matches(*argv, "dsfield") == 0) {
+ __u32 uval;
+ NEXT_ARG();
+ if (strcmp(*argv, "inherit") != 0) {
+ if (rtnl_dsfield_a2n(&uval, *argv))
+ invarg("bad TOS value", *argv);
+ p->iph.tos = uval;
+ } else
+ p->iph.tos = 1;
+ } else {
+ if (strcmp(*argv, "name") == 0) {
+ NEXT_ARG();
+ }
+ if (p->name[0])
+ duparg2("name", *argv);
+ strncpy(p->name, *argv, IFNAMSIZ);
+ if (cmd == SIOCCHGTUNNEL && count == 0) {
+ struct ip_tunnel_parm old_p;
+ memset(&old_p, 0, sizeof(old_p));
+ if (do_get_ioctl(*argv, &old_p))
+ return -1;
+ *p = old_p;
+ }
+ }
+ count++;
+ argc--; argv++;
+ }
+
+
+ if (p->iph.protocol == 0) {
+ if (memcmp(p->name, "gre", 3) == 0)
+ p->iph.protocol = IPPROTO_GRE;
+ else if (memcmp(p->name, "ipip", 4) == 0)
+ p->iph.protocol = IPPROTO_IPIP;
+ else if (memcmp(p->name, "sit", 3) == 0)
+ p->iph.protocol = IPPROTO_IPV6;
+ }
+
+ if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
+ if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
+ bb_error_msg("Keys are not allowed with ipip and sit.");
+ return -1;
+ }
+ }
+
+ if (medium[0]) {
+ p->link = do_ioctl_get_ifindex(medium);
+ if (p->link == 0)
+ return -1;
+ }
+
+ if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
+ p->i_key = p->iph.daddr;
+ p->i_flags |= GRE_KEY;
+ }
+ if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
+ p->o_key = p->iph.daddr;
+ p->o_flags |= GRE_KEY;
+ }
+ if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
+ bb_error_msg("Broadcast tunnel requires a source address.");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int do_add(int cmd, int argc, char **argv)
+{
+ struct ip_tunnel_parm p;
+
+ if (parse_args(argc, argv, cmd, &p) < 0)
+ return -1;
+
+ if (p.iph.ttl && p.iph.frag_off == 0) {
+ bb_error_msg("ttl != 0 and noptmudisc are incompatible");
+ return -1;
+ }
+
+ switch (p.iph.protocol) {
+ case IPPROTO_IPIP:
+ return do_add_ioctl(cmd, "tunl0", &p);
+ case IPPROTO_GRE:
+ return do_add_ioctl(cmd, "gre0", &p);
+ case IPPROTO_IPV6:
+ return do_add_ioctl(cmd, "sit0", &p);
+ default:
+ bb_error_msg("cannot determine tunnel mode (ipip, gre or sit)");
+ return -1;
+ }
+ return -1;
+}
+
+int do_del(int argc, char **argv)
+{
+ struct ip_tunnel_parm p;
+
+ if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
+ return -1;
+
+ switch (p.iph.protocol) {
+ case IPPROTO_IPIP:
+ return do_del_ioctl("tunl0", &p);
+ case IPPROTO_GRE:
+ return do_del_ioctl("gre0", &p);
+ case IPPROTO_IPV6:
+ return do_del_ioctl("sit0", &p);
+ default:
+ return do_del_ioctl(p.name, &p);
+ }
+ return -1;
+}
+
+void print_tunnel(struct ip_tunnel_parm *p)
+{
+ char s1[256];
+ char s2[256];
+ char s3[64];
+ char s4[64];
+
+ format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1));
+ format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2));
+ inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
+ inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
+
+ printf("%s: %s/ip remote %s local %s ",
+ p->name,
+ p->iph.protocol == IPPROTO_IPIP ? "ip" :
+ (p->iph.protocol == IPPROTO_GRE ? "gre" :
+ (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
+ p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any");
+ if (p->link) {
+ char *n = do_ioctl_get_ifname(p->link);
+ if (n)
+ printf(" dev %s ", n);
+ }
+ if (p->iph.ttl)
+ printf(" ttl %d ", p->iph.ttl);
+ else
+ printf(" ttl inherit ");
+ if (p->iph.tos) {
+ SPRINT_BUF(b1);
+ printf(" tos");
+ if (p->iph.tos&1)
+ printf(" inherit");
+ if (p->iph.tos&~1)
+ printf("%c%s ", p->iph.tos&1 ? '/' : ' ',
+ rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1)));
+ }
+ if (!(p->iph.frag_off&htons(IP_DF)))
+ printf(" nopmtudisc");
+
+ if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key)
+ printf(" key %s", s3);
+ else if ((p->i_flags|p->o_flags)&GRE_KEY) {
+ if (p->i_flags&GRE_KEY)
+ printf(" ikey %s ", s3);
+ if (p->o_flags&GRE_KEY)
+ printf(" okey %s ", s4);
+ }
+
+ if (p->i_flags&GRE_SEQ)
+ printf("%s Drop packets out of sequence.\n", _SL_);
+ if (p->i_flags&GRE_CSUM)
+ printf("%s Checksum in received packet is required.", _SL_);
+ if (p->o_flags&GRE_SEQ)
+ printf("%s Sequence packets on output.", _SL_);
+ if (p->o_flags&GRE_CSUM)
+ printf("%s Checksum output packets.", _SL_);
+}
+
+static int do_tunnels_list(struct ip_tunnel_parm *p)
+{
+ char name[IFNAMSIZ];
+ unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
+ rx_fifo, rx_frame,
+ tx_bytes, tx_packets, tx_errs, tx_drops,
+ tx_fifo, tx_colls, tx_carrier, rx_multi;
+ int type;
+ struct ip_tunnel_parm p1;
+
+ char buf[512];
+ FILE *fp = fopen("/proc/net/dev", "r");
+ if (fp == NULL) {
+ perror("fopen");
+ return -1;
+ }
+
+ fgets(buf, sizeof(buf), fp);
+ fgets(buf, sizeof(buf), fp);
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ char *ptr;
+ buf[sizeof(buf) - 1] = 0;
+ if ((ptr = strchr(buf, ':')) == NULL ||
+ (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
+ bb_error_msg("Wrong format of /proc/net/dev. Sorry.");
+ return -1;
+ }
+ if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld",
+ &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
+ &rx_fifo, &rx_frame, &rx_multi,
+ &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
+ &tx_fifo, &tx_colls, &tx_carrier) != 14)
+ continue;
+ if (p->name[0] && strcmp(p->name, name))
+ continue;
+ type = do_ioctl_get_iftype(name);
+ if (type == -1) {
+ bb_error_msg("Failed to get type of [%s]", name);
+ continue;
+ }
+ if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
+ continue;
+ memset(&p1, 0, sizeof(p1));
+ if (do_get_ioctl(name, &p1))
+ continue;
+ if ((p->link && p1.link != p->link) ||
+ (p->name[0] && strcmp(p1.name, p->name)) ||
+ (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
+ (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
+ (p->i_key && p1.i_key != p->i_key))
+ continue;
+ print_tunnel(&p1);
+ printf("\n");
+ }
+ return 0;
+}
+
+static int do_show(int argc, char **argv)
+{
+ int err;
+ struct ip_tunnel_parm p;
+
+ if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
+ return -1;
+
+ switch (p.iph.protocol) {
+ case IPPROTO_IPIP:
+ err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
+ break;
+ case IPPROTO_GRE:
+ err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
+ break;
+ case IPPROTO_IPV6:
+ err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
+ break;
+ default:
+ do_tunnels_list(&p);
+ return 0;
+ }
+ if (err)
+ return -1;
+
+ print_tunnel(&p);
+ printf("\n");
+ return 0;
+}
+
+int do_iptunnel(int argc, char **argv)
+{
+ if (argc > 0) {
+ if (matches(*argv, "add") == 0)
+ return do_add(SIOCADDTUNNEL, argc-1, argv+1);
+ if (matches(*argv, "change") == 0)
+ return do_add(SIOCCHGTUNNEL, argc-1, argv+1);
+ if (matches(*argv, "del") == 0)
+ return do_del(argc-1, argv+1);
+ if (matches(*argv, "show") == 0 ||
+ matches(*argv, "lst") == 0 ||
+ matches(*argv, "list") == 0)
+ return do_show(argc-1, argv+1);
+ } else
+ return do_show(0, NULL);
+
+ bb_error_msg("Command \"%s\" is unknown, try \"ip tunnel help\".", *argv);
+ exit(-1);
+}
diff --git a/release/src/router/busybox/networking/libiproute/libnetlink.c b/release/src/router/busybox/networking/libiproute/libnetlink.c
new file mode 100644
index 00000000..9390e56f
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/libnetlink.c
@@ -0,0 +1,522 @@
+/*
+ * libnetlink.c RTnetlink service routines.
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/uio.h>
+
+#include "libnetlink.h"
+#include "libbb.h"
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+ close(rth->fd);
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ int addr_len;
+
+ memset(rth, 0, sizeof(rth));
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (rth->fd < 0) {
+ bb_perror_msg("Cannot open netlink socket");
+ return -1;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = subscriptions;
+
+ if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+ bb_perror_msg("Cannot bind netlink socket");
+ return -1;
+ }
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+ bb_perror_msg("Cannot getsockname");
+ return -1;
+ }
+ if (addr_len != sizeof(rth->local)) {
+ bb_error_msg("Wrong address length %d", addr_len);
+ return -1;
+ }
+ if (rth->local.nl_family != AF_NETLINK) {
+ bb_error_msg("Wrong address family %d", rth->local.nl_family);
+ return -1;
+ }
+ rth->seq = time(NULL);
+ return 0;
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.g.rtgen_family = family;
+
+ return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_send(struct rtnl_handle *rth, char *buf, int len)
+{
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+ struct nlmsghdr nlh;
+ struct sockaddr_nl nladdr;
+ struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
+ struct msghdr msg = {
+ (void*)&nladdr, sizeof(nladdr),
+ iov, 2,
+ NULL, 0,
+ 0
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ nlh.nlmsg_len = NLMSG_LENGTH(len);
+ nlh.nlmsg_type = type;
+ nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ nlh.nlmsg_pid = 0;
+ nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+ int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *arg1,
+ int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *arg2)
+{
+ char buf[8192];
+ struct sockaddr_nl nladdr;
+ struct iovec iov = { buf, sizeof(buf) };
+
+ while (1) {
+ int status;
+ struct nlmsghdr *h;
+
+ struct msghdr msg = {
+ (void*)&nladdr, sizeof(nladdr),
+ &iov, 1,
+ NULL, 0,
+ 0
+ };
+
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ bb_perror_msg("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ bb_error_msg("EOF on netlink");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
+ }
+
+ h = (struct nlmsghdr*)buf;
+ while (NLMSG_OK(h, status)) {
+ int err;
+
+ if (h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump) {
+ if (junk) {
+ err = junk(&nladdr, h, arg2);
+ if (err < 0) {
+ return err;
+ }
+ }
+ goto skip_it;
+ }
+
+ if (h->nlmsg_type == NLMSG_DONE) {
+ return 0;
+ }
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ bb_error_msg("ERROR truncated");
+ } else {
+ errno = -l_err->error;
+ bb_perror_msg("RTNETLINK answers");
+ }
+ return -1;
+ }
+ err = filter(&nladdr, h, arg1);
+ if (err < 0) {
+ return err;
+ }
+
+skip_it:
+ h = NLMSG_NEXT(h, status);
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ bb_error_msg("Message truncated");
+ continue;
+ }
+ if (status) {
+ bb_error_msg_and_die("!!!Remnant of size %d", status);
+ }
+ }
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *jarg)
+{
+ int status;
+ unsigned seq;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = { (void*)n, n->nlmsg_len };
+ char buf[8192];
+ struct msghdr msg = {
+ (void*)&nladdr, sizeof(nladdr),
+ &iov, 1,
+ NULL, 0,
+ 0
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = peer;
+ nladdr.nl_groups = groups;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+ if (answer == NULL) {
+ n->nlmsg_flags |= NLM_F_ACK;
+ }
+ status = sendmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ bb_perror_msg("Cannot talk to rtnetlink");
+ return -1;
+ }
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ bb_perror_msg("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ bb_error_msg("EOF on netlink");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+ int l_err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ bb_error_msg("Truncated message");
+ return -1;
+ }
+ bb_error_msg_and_die("!!!malformed message: len=%d", len);
+ }
+
+ if (h->nlmsg_pid != rtnl->local.nl_pid ||
+ h->nlmsg_seq != seq) {
+ if (junk) {
+ l_err = junk(&nladdr, h, jarg);
+ if (l_err < 0) {
+ return l_err;
+ }
+ }
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (l < sizeof(struct nlmsgerr)) {
+ bb_error_msg("ERROR truncated");
+ } else {
+ errno = -err->error;
+ if (errno == 0) {
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ }
+ return 0;
+ }
+ bb_perror_msg("RTNETLINK answers");
+ }
+ return -1;
+ }
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ bb_error_msg("Unexpected reply!!!");
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ bb_error_msg("Message truncated");
+ continue;
+ }
+ if (status) {
+ bb_error_msg_and_die("!!!Remnant of size %d", status);
+ }
+ }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl,
+ int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *jarg)
+{
+ int status;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ char buf[8192];
+ struct msghdr msg = {
+ (void*)&nladdr, sizeof(nladdr),
+ &iov, 1,
+ NULL, 0,
+ 0
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ bb_perror_msg("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ bb_error_msg("EOF on netlink");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ bb_error_msg_and_die("Sender address length == %d", msg.msg_namelen);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ bb_error_msg("Truncated message");
+ return -1;
+ }
+ bb_error_msg_and_die("!!!malformed message: len=%d", len);
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0) {
+ return err;
+ }
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ bb_error_msg("Message truncated");
+ continue;
+ }
+ if (status) {
+ bb_error_msg_and_die("!!!Remnant of size %d", status);
+ }
+ }
+}
+
+int rtnl_from_file(FILE *rtnl,
+ int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *jarg)
+{
+ int status;
+ struct sockaddr_nl nladdr;
+ char buf[8192];
+ struct nlmsghdr *h = (void*)buf;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ while (1) {
+ int err, len, type;
+ int l;
+
+ status = fread(&buf, 1, sizeof(*h), rtnl);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ bb_perror_msg("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status == 0)
+ return 0;
+
+ len = h->nlmsg_len;
+ type= h->nlmsg_type;
+ l = len - sizeof(*h);
+
+ if (l<0 || len>sizeof(buf)) {
+ bb_error_msg("!!!malformed message: len=%d @%lu",
+ len, ftell(rtnl));
+ return -1;
+ }
+
+ status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+ if (status < 0) {
+ bb_perror_msg("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status < l) {
+ bb_error_msg("rtnl-from_file: truncated message");
+ return -1;
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *rta;
+ if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+ return -1;
+ rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, 4);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+ return -1;
+ rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *subrta;
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, 4);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max) {
+ tb[rta->rta_type] = rta;
+ }
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len) {
+ bb_error_msg("!!!Deficit %d, rta_len=%d", len, rta->rta_len);
+ }
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/libiproute/libnetlink.h b/release/src/router/busybox/networking/libiproute/libnetlink.h
new file mode 100644
index 00000000..45d3ad2b
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/libnetlink.h
@@ -0,0 +1,46 @@
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ __u32 seq;
+ __u32 dump;
+};
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+extern int rtnl_dump_filter(struct rtnl_handle *rth,
+ int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *arg1,
+ int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *arg2);
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *jarg);
+extern int rtnl_send(struct rtnl_handle *rth, char *buf, int);
+
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+extern int rtnl_listen(struct rtnl_handle *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *jarg);
+extern int rtnl_from_file(FILE *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *jarg);
+
+#endif /* __LIBNETLINK_H__ */
+
diff --git a/release/src/router/busybox/networking/libiproute/linux/pkt_sched.h b/release/src/router/busybox/networking/libiproute/linux/pkt_sched.h
new file mode 100644
index 00000000..e174588f
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/linux/pkt_sched.h
@@ -0,0 +1,413 @@
+#ifndef __LINUX_PKT_SCHED_H
+#define __LINUX_PKT_SCHED_H
+
+/* Logical priority bands not depending on specific packet scheduler.
+ Every scheduler will map them to real traffic classes, if it has
+ no more precise mechanism to classify packets.
+
+ These numbers have no special meaning, though their coincidence
+ with obsolete IPv6 values is not occasional :-). New IPv6 drafts
+ preferred full anarchy inspired by diffserv group.
+
+ Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy
+ class, actually, as rule it will be handled with more care than
+ filler or even bulk.
+ */
+
+#include <asm/types.h>
+
+#define TC_PRIO_BESTEFFORT 0
+#define TC_PRIO_FILLER 1
+#define TC_PRIO_BULK 2
+#define TC_PRIO_INTERACTIVE_BULK 4
+#define TC_PRIO_INTERACTIVE 6
+#define TC_PRIO_CONTROL 7
+
+#define TC_PRIO_MAX 15
+
+/* Generic queue statistics, available for all the elements.
+ Particular schedulers may have also their private records.
+ */
+
+struct tc_stats
+{
+ __u64 bytes; /* NUmber of enqueues bytes */
+ __u32 packets; /* Number of enqueued packets */
+ __u32 drops; /* Packets dropped because of lack of resources */
+ __u32 overlimits; /* Number of throttle events when this
+ * flow goes out of allocated bandwidth */
+ __u32 bps; /* Current flow byte rate */
+ __u32 pps; /* Current flow packet rate */
+ __u32 qlen;
+ __u32 backlog;
+#ifdef __KERNEL__
+ spinlock_t *lock;
+#endif
+};
+
+struct tc_estimator
+{
+ char interval;
+ unsigned char ewma_log;
+};
+
+/* "Handles"
+ ---------
+
+ All the traffic control objects have 32bit identifiers, or "handles".
+
+ They can be considered as opaque numbers from user API viewpoint,
+ but actually they always consist of two fields: major and
+ minor numbers, which are interpreted by kernel specially,
+ that may be used by applications, though not recommended.
+
+ F.e. qdisc handles always have minor number equal to zero,
+ classes (or flows) have major equal to parent qdisc major, and
+ minor uniquely identifying class inside qdisc.
+
+ Macros to manipulate handles:
+ */
+
+#define TC_H_MAJ_MASK (0xFFFF0000U)
+#define TC_H_MIN_MASK (0x0000FFFFU)
+#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
+#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
+#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
+
+#define TC_H_UNSPEC (0U)
+#define TC_H_ROOT (0xFFFFFFFFU)
+#define TC_H_INGRESS (0xFFFFFFF1U)
+
+struct tc_ratespec
+{
+ unsigned char cell_log;
+ unsigned char __reserved;
+ unsigned short feature;
+ short addend;
+ unsigned short mpu;
+ __u32 rate;
+};
+
+/* FIFO section */
+
+struct tc_fifo_qopt
+{
+ __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */
+};
+
+/* PRIO section */
+
+#define TCQ_PRIO_BANDS 16
+
+struct tc_prio_qopt
+{
+ int bands; /* Number of bands */
+ __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */
+};
+
+/* CSZ section */
+
+struct tc_csz_qopt
+{
+ int flows; /* Maximal number of guaranteed flows */
+ unsigned char R_log; /* Fixed point position for round number */
+ unsigned char delta_log; /* Log of maximal managed time interval */
+ __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> CSZ band */
+};
+
+struct tc_csz_copt
+{
+ struct tc_ratespec slice;
+ struct tc_ratespec rate;
+ struct tc_ratespec peakrate;
+ __u32 limit;
+ __u32 buffer;
+ __u32 mtu;
+};
+
+enum
+{
+ TCA_CSZ_UNSPEC,
+ TCA_CSZ_PARMS,
+ TCA_CSZ_RTAB,
+ TCA_CSZ_PTAB,
+};
+
+/* TBF section */
+
+struct tc_tbf_qopt
+{
+ struct tc_ratespec rate;
+ struct tc_ratespec peakrate;
+ __u32 limit;
+ __u32 buffer;
+ __u32 mtu;
+};
+
+enum
+{
+ TCA_TBF_UNSPEC,
+ TCA_TBF_PARMS,
+ TCA_TBF_RTAB,
+ TCA_TBF_PTAB,
+};
+
+
+/* TEQL section */
+
+/* TEQL does not require any parameters */
+
+/* SFQ section */
+
+struct tc_sfq_qopt
+{
+ unsigned quantum; /* Bytes per round allocated to flow */
+ int perturb_period; /* Period of hash perturbation */
+ __u32 limit; /* Maximal packets in queue */
+ unsigned divisor; /* Hash divisor */
+ unsigned flows; /* Maximal number of flows */
+};
+
+/*
+ * NOTE: limit, divisor and flows are hardwired to code at the moment.
+ *
+ * limit=flows=128, divisor=1024;
+ *
+ * The only reason for this is efficiency, it is possible
+ * to change these parameters in compile time.
+ */
+
+/* RED section */
+
+enum
+{
+ TCA_RED_UNSPEC,
+ TCA_RED_PARMS,
+ TCA_RED_STAB,
+};
+
+struct tc_red_qopt
+{
+ __u32 limit; /* HARD maximal queue length (bytes) */
+ __u32 qth_min; /* Min average length threshold (bytes) */
+ __u32 qth_max; /* Max average length threshold (bytes) */
+ unsigned char Wlog; /* log(W) */
+ unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
+ unsigned char Scell_log; /* cell size for idle damping */
+ unsigned char flags;
+#define TC_RED_ECN 1
+};
+
+struct tc_red_xstats
+{
+ __u32 early; /* Early drops */
+ __u32 pdrop; /* Drops due to queue limits */
+ __u32 other; /* Drops due to drop() calls */
+ __u32 marked; /* Marked packets */
+};
+
+/* GRED section */
+
+#define MAX_DPs 16
+
+enum
+{
+ TCA_GRED_UNSPEC,
+ TCA_GRED_PARMS,
+ TCA_GRED_STAB,
+ TCA_GRED_DPS,
+};
+
+#define TCA_SET_OFF TCA_GRED_PARMS
+struct tc_gred_qopt
+{
+ __u32 limit; /* HARD maximal queue length (bytes)
+*/
+ __u32 qth_min; /* Min average length threshold (bytes)
+*/
+ __u32 qth_max; /* Max average length threshold (bytes)
+*/
+ __u32 DP; /* upto 2^32 DPs */
+ __u32 backlog;
+ __u32 qave;
+ __u32 forced;
+ __u32 early;
+ __u32 other;
+ __u32 pdrop;
+
+ unsigned char Wlog; /* log(W) */
+ unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
+ unsigned char Scell_log; /* cell size for idle damping */
+ __u8 prio; /* prio of this VQ */
+ __u32 packets;
+ __u32 bytesin;
+};
+/* gred setup */
+struct tc_gred_sopt
+{
+ __u32 DPs;
+ __u32 def_DP;
+ __u8 grio;
+};
+
+/* HTB section */
+#define TC_HTB_NUMPRIO 4
+#define TC_HTB_MAXDEPTH 4
+
+struct tc_htb_opt
+{
+ struct tc_ratespec rate;
+ struct tc_ratespec ceil;
+ __u32 buffer;
+ __u32 cbuffer;
+ __u32 quantum; /* out only */
+ __u32 level; /* out only */
+ __u8 prio;
+ __u8 injectd; /* inject class distance */
+ __u8 pad[2];
+};
+struct tc_htb_glob
+{
+ __u32 rate2quantum; /* bps->quantum divisor */
+ __u32 defcls; /* default class number */
+ __u32 use_dcache; /* use dequeue cache ? */
+ __u32 debug; /* debug flags */
+
+
+ /* stats */
+ __u32 deq_rate; /* dequeue rate */
+ __u32 utilz; /* dequeue utilization */
+ __u32 trials; /* deq_prio trials per dequeue */
+ __u32 dcache_hits;
+ __u32 direct_pkts; /* count of non shapped packets */
+};
+enum
+{
+ TCA_HTB_UNSPEC,
+ TCA_HTB_PARMS,
+ TCA_HTB_INIT,
+ TCA_HTB_CTAB,
+ TCA_HTB_RTAB,
+};
+struct tc_htb_xstats
+{
+ __u32 lends;
+ __u32 borrows;
+ __u32 giants; /* too big packets (rate will not be accurate) */
+ __u32 injects; /* how many times leaf used injected bw */
+ __u32 tokens;
+ __u32 ctokens;
+};
+
+/* CBQ section */
+
+#define TC_CBQ_MAXPRIO 8
+#define TC_CBQ_MAXLEVEL 8
+#define TC_CBQ_DEF_EWMA 5
+
+struct tc_cbq_lssopt
+{
+ unsigned char change;
+ unsigned char flags;
+#define TCF_CBQ_LSS_BOUNDED 1
+#define TCF_CBQ_LSS_ISOLATED 2
+ unsigned char ewma_log;
+ unsigned char level;
+#define TCF_CBQ_LSS_FLAGS 1
+#define TCF_CBQ_LSS_EWMA 2
+#define TCF_CBQ_LSS_MAXIDLE 4
+#define TCF_CBQ_LSS_MINIDLE 8
+#define TCF_CBQ_LSS_OFFTIME 0x10
+#define TCF_CBQ_LSS_AVPKT 0x20
+ __u32 maxidle;
+ __u32 minidle;
+ __u32 offtime;
+ __u32 avpkt;
+};
+
+struct tc_cbq_wrropt
+{
+ unsigned char flags;
+ unsigned char priority;
+ unsigned char cpriority;
+ unsigned char __reserved;
+ __u32 allot;
+ __u32 weight;
+};
+
+struct tc_cbq_ovl
+{
+ unsigned char strategy;
+#define TC_CBQ_OVL_CLASSIC 0
+#define TC_CBQ_OVL_DELAY 1
+#define TC_CBQ_OVL_LOWPRIO 2
+#define TC_CBQ_OVL_DROP 3
+#define TC_CBQ_OVL_RCLASSIC 4
+ unsigned char priority2;
+ __u32 penalty;
+};
+
+struct tc_cbq_police
+{
+ unsigned char police;
+ unsigned char __res1;
+ unsigned short __res2;
+};
+
+struct tc_cbq_fopt
+{
+ __u32 split;
+ __u32 defmap;
+ __u32 defchange;
+};
+
+struct tc_cbq_xstats
+{
+ __u32 borrows;
+ __u32 overactions;
+ __s32 avgidle;
+ __s32 undertime;
+};
+
+enum
+{
+ TCA_CBQ_UNSPEC,
+ TCA_CBQ_LSSOPT,
+ TCA_CBQ_WRROPT,
+ TCA_CBQ_FOPT,
+ TCA_CBQ_OVL_STRATEGY,
+ TCA_CBQ_RATE,
+ TCA_CBQ_RTAB,
+ TCA_CBQ_POLICE,
+};
+
+#define TCA_CBQ_MAX TCA_CBQ_POLICE
+
+/* dsmark section */
+
+enum {
+ TCA_DSMARK_UNSPEC,
+ TCA_DSMARK_INDICES,
+ TCA_DSMARK_DEFAULT_INDEX,
+ TCA_DSMARK_SET_TC_INDEX,
+ TCA_DSMARK_MASK,
+ TCA_DSMARK_VALUE
+};
+
+#define TCA_DSMARK_MAX TCA_DSMARK_VALUE
+
+/* ATM section */
+
+enum {
+ TCA_ATM_UNSPEC,
+ TCA_ATM_FD, /* file/socket descriptor */
+ TCA_ATM_PTR, /* pointer to descriptor - later */
+ TCA_ATM_HDR, /* LL header */
+ TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */
+ TCA_ATM_ADDR, /* PVC address (for output only) */
+ TCA_ATM_STATE /* VC state (ATM_VS_*; for output only) */
+};
+
+#define TCA_ATM_MAX TCA_ATM_STATE
+
+#endif
diff --git a/release/src/router/busybox/networking/libiproute/ll_addr.c b/release/src/router/busybox/networking/libiproute/ll_addr.c
new file mode 100644
index 00000000..ada685f4
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/ll_addr.c
@@ -0,0 +1,81 @@
+/*
+ * ll_addr.c
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include <arpa/inet.h>
+#include <string.h>
+#include <net/if_arp.h>
+#include "utils.h"
+#include "libbb.h"
+
+const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen)
+{
+ int i;
+ int l;
+
+ if (alen == 4 &&
+ (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) {
+ return inet_ntop(AF_INET, addr, buf, blen);
+ }
+ l = 0;
+ for (i=0; i<alen; i++) {
+ if (i==0) {
+ snprintf(buf+l, blen, "%02x", addr[i]);
+ blen -= 2;
+ l += 2;
+ } else {
+ snprintf(buf+l, blen, ":%02x", addr[i]);
+ blen -= 3;
+ l += 3;
+ }
+ }
+ return buf;
+}
+
+int ll_addr_a2n(unsigned char *lladdr, int len, char *arg)
+{
+ if (strchr(arg, '.')) {
+ inet_prefix pfx;
+ if (get_addr_1(&pfx, arg, AF_INET)) {
+ bb_error_msg("\"%s\" is invalid lladdr.", arg);
+ return -1;
+ }
+ if (len < 4) {
+ return -1;
+ }
+ memcpy(lladdr, pfx.data, 4);
+ return 4;
+ } else {
+ int i;
+
+ for (i=0; i<len; i++) {
+ int temp;
+ char *cp = strchr(arg, ':');
+ if (cp) {
+ *cp = 0;
+ cp++;
+ }
+ if (sscanf(arg, "%x", &temp) != 1) {
+ bb_error_msg("\"%s\" is invalid lladdr.", arg);
+ return -1;
+ }
+ if (temp < 0 || temp > 255) {
+ bb_error_msg("\"%s\" is invalid lladdr.", arg);
+ return -1;
+ }
+ lladdr[i] = temp;
+ if (!cp) {
+ break;
+ }
+ arg = cp;
+ }
+ return i+1;
+ }
+}
diff --git a/release/src/router/busybox/networking/libiproute/ll_map.c b/release/src/router/busybox/networking/libiproute/ll_map.c
new file mode 100644
index 00000000..b7a82842
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/ll_map.c
@@ -0,0 +1,164 @@
+/*
+ * ll_map.c
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+
+#include "libnetlink.h"
+
+struct idxmap
+{
+ struct idxmap * next;
+ int index;
+ int type;
+ int alen;
+ unsigned flags;
+ unsigned char addr[8];
+ char name[16];
+};
+
+static struct idxmap *idxmap[16];
+
+int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+ int h;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct idxmap *im, **imp;
+ struct rtattr *tb[IFLA_MAX+1];
+
+ if (n->nlmsg_type != RTM_NEWLINK)
+ return 0;
+
+ if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
+ return -1;
+
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+ if (tb[IFLA_IFNAME] == NULL)
+ return 0;
+
+ h = ifi->ifi_index&0xF;
+
+ for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next)
+ if (im->index == ifi->ifi_index)
+ break;
+
+ if (im == NULL) {
+ im = malloc(sizeof(*im));
+ if (im == NULL)
+ return 0;
+ im->next = *imp;
+ im->index = ifi->ifi_index;
+ *imp = im;
+ }
+
+ im->type = ifi->ifi_type;
+ im->flags = ifi->ifi_flags;
+ if (tb[IFLA_ADDRESS]) {
+ int alen;
+ im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+ if (alen > sizeof(im->addr))
+ alen = sizeof(im->addr);
+ memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
+ } else {
+ im->alen = 0;
+ memset(im->addr, 0, sizeof(im->addr));
+ }
+ strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME]));
+ return 0;
+}
+
+const char *ll_idx_n2a(int idx, char *buf)
+{
+ struct idxmap *im;
+
+ if (idx == 0)
+ return "*";
+ for (im = idxmap[idx&0xF]; im; im = im->next)
+ if (im->index == idx)
+ return im->name;
+ snprintf(buf, 16, "if%d", idx);
+ return buf;
+}
+
+
+const char *ll_index_to_name(int idx)
+{
+ static char nbuf[16];
+
+ return ll_idx_n2a(idx, nbuf);
+}
+
+int ll_index_to_type(int idx)
+{
+ struct idxmap *im;
+
+ if (idx == 0)
+ return -1;
+ for (im = idxmap[idx&0xF]; im; im = im->next)
+ if (im->index == idx)
+ return im->type;
+ return -1;
+}
+
+unsigned ll_index_to_flags(int idx)
+{
+ struct idxmap *im;
+
+ if (idx == 0)
+ return 0;
+
+ for (im = idxmap[idx&0xF]; im; im = im->next)
+ if (im->index == idx)
+ return im->flags;
+ return 0;
+}
+
+int ll_name_to_index(char *name)
+{
+ static char ncache[16];
+ static int icache;
+ struct idxmap *im;
+ int i;
+
+ if (name == NULL)
+ return 0;
+ if (icache && strcmp(name, ncache) == 0)
+ return icache;
+ for (i=0; i<16; i++) {
+ for (im = idxmap[i]; im; im = im->next) {
+ if (strcmp(im->name, name) == 0) {
+ icache = im->index;
+ strcpy(ncache, name);
+ return im->index;
+ }
+ }
+ }
+ return 0;
+}
+
+int ll_init_map(struct rtnl_handle *rth)
+{
+ if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
+ perror("Cannot send dump request");
+ exit(1);
+ }
+
+ if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ exit(1);
+ }
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/libiproute/ll_map.h b/release/src/router/busybox/networking/libiproute/ll_map.h
new file mode 100644
index 00000000..739f157e
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/ll_map.h
@@ -0,0 +1,12 @@
+#ifndef __LL_MAP_H__
+#define __LL_MAP_H__ 1
+
+extern int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int ll_init_map(struct rtnl_handle *rth);
+extern int ll_name_to_index(char *name);
+extern const char *ll_index_to_name(int idx);
+extern const char *ll_idx_n2a(int idx, char *buf);
+extern int ll_index_to_type(int idx);
+extern unsigned ll_index_to_flags(int idx);
+
+#endif /* __LL_MAP_H__ */
diff --git a/release/src/router/busybox/networking/libiproute/ll_proto.c b/release/src/router/busybox/networking/libiproute/ll_proto.c
new file mode 100644
index 00000000..45afdb82
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/ll_proto.c
@@ -0,0 +1,120 @@
+/*
+ * ll_proto.c
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include "utils.h"
+
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+#define __PF(f,n) { ETH_P_##f, #n },
+static struct {
+ int id;
+ char *name;
+} llproto_names[] = {
+__PF(LOOP,loop)
+__PF(PUP,pup)
+#ifdef ETH_P_PUPAT
+__PF(PUPAT,pupat)
+#endif
+__PF(IP,ip)
+__PF(X25,x25)
+__PF(ARP,arp)
+__PF(BPQ,bpq)
+#ifdef ETH_P_IEEEPUP
+__PF(IEEEPUP,ieeepup)
+#endif
+#ifdef ETH_P_IEEEPUPAT
+__PF(IEEEPUPAT,ieeepupat)
+#endif
+__PF(DEC,dec)
+__PF(DNA_DL,dna_dl)
+__PF(DNA_RC,dna_rc)
+__PF(DNA_RT,dna_rt)
+__PF(LAT,lat)
+__PF(DIAG,diag)
+__PF(CUST,cust)
+__PF(SCA,sca)
+__PF(RARP,rarp)
+__PF(ATALK,atalk)
+__PF(AARP,aarp)
+__PF(IPX,ipx)
+__PF(IPV6,ipv6)
+#ifdef ETH_P_PPP_DISC
+__PF(PPP_DISC,ppp_disc)
+#endif
+#ifdef ETH_P_PPP_SES
+__PF(PPP_SES,ppp_ses)
+#endif
+#ifdef ETH_P_ATMMPOA
+__PF(ATMMPOA,atmmpoa)
+#endif
+#ifdef ETH_P_ATMFATE
+__PF(ATMFATE,atmfate)
+#endif
+
+__PF(802_3,802_3)
+__PF(AX25,ax25)
+__PF(ALL,all)
+__PF(802_2,802_2)
+__PF(SNAP,snap)
+__PF(DDCMP,ddcmp)
+__PF(WAN_PPP,wan_ppp)
+__PF(PPP_MP,ppp_mp)
+__PF(LOCALTALK,localtalk)
+__PF(PPPTALK,ppptalk)
+__PF(TR_802_2,tr_802_2)
+__PF(MOBITEX,mobitex)
+__PF(CONTROL,control)
+__PF(IRDA,irda)
+#ifdef ETH_P_ECONET
+__PF(ECONET,econet)
+#endif
+
+{ 0x8100, "802.1Q" },
+{ ETH_P_IP, "ipv4" },
+};
+#undef __PF
+
+
+char * ll_proto_n2a(unsigned short id, char *buf, int len)
+{
+ int i;
+
+ id = ntohs(id);
+
+ for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
+ if (llproto_names[i].id == id)
+ return llproto_names[i].name;
+ }
+ snprintf(buf, len, "[%d]", id);
+ return buf;
+}
+
+int ll_proto_a2n(unsigned short *id, char *buf)
+{
+ int i;
+ for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
+ if (strcasecmp(llproto_names[i].name, buf) == 0) {
+ *id = htons(llproto_names[i].id);
+ return 0;
+ }
+ }
+ if (get_u16(id, buf, 0))
+ return -1;
+ *id = htons(*id);
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/libiproute/ll_types.c b/release/src/router/busybox/networking/libiproute/ll_types.c
new file mode 100644
index 00000000..f39f777e
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/ll_types.c
@@ -0,0 +1,115 @@
+/*
+ * ll_types.c
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+#include <stdio.h>
+#include <arpa/inet.h>
+
+#include <linux/if_arp.h>
+
+char * ll_type_n2a(int type, char *buf, int len)
+{
+#define __PF(f,n) { ARPHRD_##f, #n },
+static struct {
+ int type;
+ char *name;
+} arphrd_names[] = {
+{ 0, "generic" },
+__PF(ETHER,ether)
+__PF(EETHER,eether)
+__PF(AX25,ax25)
+__PF(PRONET,pronet)
+__PF(CHAOS,chaos)
+#ifdef ARPHRD_IEEE802_TR
+__PF(IEEE802,ieee802)
+#else
+__PF(IEEE802,tr)
+#endif
+__PF(ARCNET,arcnet)
+__PF(APPLETLK,atalk)
+__PF(DLCI,dlci)
+#ifdef ARPHRD_ATM
+__PF(ATM,atm)
+#endif
+__PF(METRICOM,metricom)
+#ifdef ARPHRD_IEEE1394
+__PF(IEEE1394,ieee1394)
+#endif
+
+__PF(SLIP,slip)
+__PF(CSLIP,cslip)
+__PF(SLIP6,slip6)
+__PF(CSLIP6,cslip6)
+__PF(RSRVD,rsrvd)
+__PF(ADAPT,adapt)
+__PF(ROSE,rose)
+__PF(X25,x25)
+#ifdef ARPHRD_HWX25
+__PF(HWX25,hwx25)
+#endif
+__PF(PPP,ppp)
+__PF(HDLC,hdlc)
+__PF(LAPB,lapb)
+#ifdef ARPHRD_DDCMP
+__PF(DDCMP,ddcmp)
+__PF(RAWHDLC,rawhdlc)
+#endif
+
+__PF(TUNNEL,ipip)
+__PF(TUNNEL6,tunnel6)
+__PF(FRAD,frad)
+__PF(SKIP,skip)
+__PF(LOOPBACK,loopback)
+__PF(LOCALTLK,ltalk)
+__PF(FDDI,fddi)
+__PF(BIF,bif)
+__PF(SIT,sit)
+__PF(IPDDP,ip/ddp)
+__PF(IPGRE,gre)
+__PF(PIMREG,pimreg)
+__PF(HIPPI,hippi)
+__PF(ASH,ash)
+__PF(ECONET,econet)
+__PF(IRDA,irda)
+__PF(FCPP,fcpp)
+__PF(FCAL,fcal)
+__PF(FCPL,fcpl)
+__PF(FCFABRIC,fcfb0)
+__PF(FCFABRIC+1,fcfb1)
+__PF(FCFABRIC+2,fcfb2)
+__PF(FCFABRIC+3,fcfb3)
+__PF(FCFABRIC+4,fcfb4)
+__PF(FCFABRIC+5,fcfb5)
+__PF(FCFABRIC+6,fcfb6)
+__PF(FCFABRIC+7,fcfb7)
+__PF(FCFABRIC+8,fcfb8)
+__PF(FCFABRIC+9,fcfb9)
+__PF(FCFABRIC+10,fcfb10)
+__PF(FCFABRIC+11,fcfb11)
+__PF(FCFABRIC+12,fcfb12)
+#ifdef ARPHRD_IEEE802_TR
+__PF(IEEE802_TR,tr)
+#endif
+#ifdef ARPHRD_IEEE80211
+__PF(IEEE80211,ieee802.11)
+#endif
+#ifdef ARPHRD_VOID
+__PF(VOID,void)
+#endif
+};
+#undef __PF
+
+ int i;
+ for (i=0; i<sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) {
+ if (arphrd_names[i].type == type)
+ return arphrd_names[i].name;
+ }
+ snprintf(buf, len, "[%d]", type);
+ return buf;
+}
diff --git a/release/src/router/busybox/networking/libiproute/rt_names.c b/release/src/router/busybox/networking/libiproute/rt_names.c
new file mode 100644
index 00000000..d503645b
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/rt_names.c
@@ -0,0 +1,385 @@
+/*
+ * rt_names.c rtnetlink names DB.
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stdint.h>
+
+static void rtnl_tab_initialize(char *file, char **tab, int size)
+{
+ char buf[512];
+ FILE *fp;
+
+ fp = fopen(file, "r");
+ if (!fp)
+ return;
+ while (fgets(buf, sizeof(buf), fp)) {
+ char *p = buf;
+ int id;
+ char namebuf[512];
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '#' || *p == '\n' || *p == 0)
+ continue;
+ if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2 &&
+ sscanf(p, "0x%x %s #", &id, namebuf) != 2 &&
+ sscanf(p, "%d %s\n", &id, namebuf) != 2 &&
+ sscanf(p, "%d %s #", &id, namebuf) != 2) {
+ fprintf(stderr, "Database %s is corrupted at %s\n",
+ file, p);
+ return;
+ }
+
+ if (id<0 || id>size)
+ continue;
+
+ tab[id] = strdup(namebuf);
+ }
+ fclose(fp);
+}
+
+
+static char * rtnl_rtprot_tab[256] = {
+ "none",
+ "redirect",
+ "kernel",
+ "boot",
+ "static",
+ NULL,
+ NULL,
+ NULL,
+ "gated",
+ "ra",
+ "mrt",
+ "zebra",
+ "bird",
+};
+
+
+
+static int rtnl_rtprot_init;
+
+static void rtnl_rtprot_initialize(void)
+{
+ rtnl_rtprot_init = 1;
+ rtnl_tab_initialize("/etc/iproute2/rt_protos",
+ rtnl_rtprot_tab, 256);
+}
+
+char * rtnl_rtprot_n2a(int id, char *buf, int len)
+{
+ if (id<0 || id>=256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+ if (!rtnl_rtprot_tab[id]) {
+ if (!rtnl_rtprot_init)
+ rtnl_rtprot_initialize();
+ }
+ if (rtnl_rtprot_tab[id])
+ return rtnl_rtprot_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+
+int rtnl_rtprot_a2n(uint32_t *id, char *arg)
+{
+ static char *cache = NULL;
+ static unsigned long res;
+ char *end;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ if (!rtnl_rtprot_init)
+ rtnl_rtprot_initialize();
+
+ for (i=0; i<256; i++) {
+ if (rtnl_rtprot_tab[i] &&
+ strcmp(rtnl_rtprot_tab[i], arg) == 0) {
+ cache = rtnl_rtprot_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = strtoul(arg, &end, 0);
+ if (!end || end == arg || *end || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+
+static char * rtnl_rtscope_tab[256] = {
+ "global",
+};
+
+static int rtnl_rtscope_init;
+
+static void rtnl_rtscope_initialize(void)
+{
+ rtnl_rtscope_init = 1;
+ rtnl_rtscope_tab[255] = "nowhere";
+ rtnl_rtscope_tab[254] = "host";
+ rtnl_rtscope_tab[253] = "link";
+ rtnl_rtscope_tab[200] = "site";
+ rtnl_tab_initialize("/etc/iproute2/rt_scopes",
+ rtnl_rtscope_tab, 256);
+}
+
+char * rtnl_rtscope_n2a(int id, char *buf, int len)
+{
+ if (id<0 || id>=256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+ if (!rtnl_rtscope_tab[id]) {
+ if (!rtnl_rtscope_init)
+ rtnl_rtscope_initialize();
+ }
+ if (rtnl_rtscope_tab[id])
+ return rtnl_rtscope_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+
+int rtnl_rtscope_a2n(uint32_t *id, char *arg)
+{
+ static char *cache = NULL;
+ static unsigned long res;
+ char *end;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ if (!rtnl_rtscope_init)
+ rtnl_rtscope_initialize();
+
+ for (i=0; i<256; i++) {
+ if (rtnl_rtscope_tab[i] &&
+ strcmp(rtnl_rtscope_tab[i], arg) == 0) {
+ cache = rtnl_rtscope_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = strtoul(arg, &end, 0);
+ if (!end || end == arg || *end || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+
+static char * rtnl_rtrealm_tab[256] = {
+ "unknown",
+};
+
+static int rtnl_rtrealm_init;
+
+static void rtnl_rtrealm_initialize(void)
+{
+ rtnl_rtrealm_init = 1;
+ rtnl_tab_initialize("/etc/iproute2/rt_realms",
+ rtnl_rtrealm_tab, 256);
+}
+
+char * rtnl_rtrealm_n2a(int id, char *buf, int len)
+{
+ if (id<0 || id>=256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+ if (!rtnl_rtrealm_tab[id]) {
+ if (!rtnl_rtrealm_init)
+ rtnl_rtrealm_initialize();
+ }
+ if (rtnl_rtrealm_tab[id])
+ return rtnl_rtrealm_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+
+
+int rtnl_rtrealm_a2n(uint32_t *id, char *arg)
+{
+ static char *cache = NULL;
+ static unsigned long res;
+ char *end;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ if (!rtnl_rtrealm_init)
+ rtnl_rtrealm_initialize();
+
+ for (i=0; i<256; i++) {
+ if (rtnl_rtrealm_tab[i] &&
+ strcmp(rtnl_rtrealm_tab[i], arg) == 0) {
+ cache = rtnl_rtrealm_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = strtoul(arg, &end, 0);
+ if (!end || end == arg || *end || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+
+static char * rtnl_rttable_tab[256] = {
+ "unspec",
+};
+
+static int rtnl_rttable_init;
+
+static void rtnl_rttable_initialize(void)
+{
+ rtnl_rttable_init = 1;
+ rtnl_rttable_tab[255] = "local";
+ rtnl_rttable_tab[254] = "main";
+ rtnl_tab_initialize("/etc/iproute2/rt_tables",
+ rtnl_rttable_tab, 256);
+}
+
+char * rtnl_rttable_n2a(int id, char *buf, int len)
+{
+ if (id<0 || id>=256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+ if (!rtnl_rttable_tab[id]) {
+ if (!rtnl_rttable_init)
+ rtnl_rttable_initialize();
+ }
+ if (rtnl_rttable_tab[id])
+ return rtnl_rttable_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+
+int rtnl_rttable_a2n(uint32_t *id, char *arg)
+{
+ static char *cache = NULL;
+ static unsigned long res;
+ char *end;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ if (!rtnl_rttable_init)
+ rtnl_rttable_initialize();
+
+ for (i=0; i<256; i++) {
+ if (rtnl_rttable_tab[i] &&
+ strcmp(rtnl_rttable_tab[i], arg) == 0) {
+ cache = rtnl_rttable_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ i = strtoul(arg, &end, 0);
+ if (!end || end == arg || *end || i > 255)
+ return -1;
+ *id = i;
+ return 0;
+}
+
+
+static char * rtnl_rtdsfield_tab[256] = {
+ "0",
+};
+
+static int rtnl_rtdsfield_init;
+
+static void rtnl_rtdsfield_initialize(void)
+{
+ rtnl_rtdsfield_init = 1;
+ rtnl_tab_initialize("/etc/iproute2/rt_dsfield",
+ rtnl_rtdsfield_tab, 256);
+}
+
+char * rtnl_dsfield_n2a(int id, char *buf, int len)
+{
+ if (id<0 || id>=256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+ if (!rtnl_rtdsfield_tab[id]) {
+ if (!rtnl_rtdsfield_init)
+ rtnl_rtdsfield_initialize();
+ }
+ if (rtnl_rtdsfield_tab[id])
+ return rtnl_rtdsfield_tab[id];
+ snprintf(buf, len, "0x%02x", id);
+ return buf;
+}
+
+
+int rtnl_dsfield_a2n(uint32_t *id, char *arg)
+{
+ static char *cache = NULL;
+ static unsigned long res;
+ char *end;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ if (!rtnl_rtdsfield_init)
+ rtnl_rtdsfield_initialize();
+
+ for (i=0; i<256; i++) {
+ if (rtnl_rtdsfield_tab[i] &&
+ strcmp(rtnl_rtdsfield_tab[i], arg) == 0) {
+ cache = rtnl_rtdsfield_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = strtoul(arg, &end, 16);
+ if (!end || end == arg || *end || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
diff --git a/release/src/router/busybox/networking/libiproute/rt_names.h b/release/src/router/busybox/networking/libiproute/rt_names.h
new file mode 100644
index 00000000..97bc6169
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/rt_names.h
@@ -0,0 +1,30 @@
+#ifndef RT_NAMES_H_
+#define RT_NAMES_H_ 1
+
+#include <stdint.h>
+
+const char* rtnl_rtprot_n2a(int id, char *buf, int len);
+const char* rtnl_rtscope_n2a(int id, char *buf, int len);
+const char* rtnl_rttable_n2a(int id, char *buf, int len);
+const char* rtnl_rtrealm_n2a(int id, char *buf, int len);
+const char* rtnl_dsfield_n2a(int id, char *buf, int len);
+int rtnl_rtprot_a2n(int *id, char *arg);
+int rtnl_rtscope_a2n(int *id, char *arg);
+int rtnl_rttable_a2n(int *id, char *arg);
+int rtnl_rtrealm_a2n(uint32_t *id, char *arg);
+int rtnl_dsfield_a2n(uint32_t *id, char *arg);
+
+const char *inet_proto_n2a(int proto, char *buf, int len);
+int inet_proto_a2n(char *buf);
+
+
+const char * ll_type_n2a(int type, char *buf, int len);
+
+const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen);
+int ll_addr_a2n(unsigned char *lladdr, int len, char *arg);
+
+const char * ll_proto_n2a(unsigned short id, char *buf, int len);
+int ll_proto_a2n(unsigned short *id, char *buf);
+
+
+#endif
diff --git a/release/src/router/busybox/networking/libiproute/rtm_map.c b/release/src/router/busybox/networking/libiproute/rtm_map.c
new file mode 100644
index 00000000..5f6a9e69
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/rtm_map.c
@@ -0,0 +1,110 @@
+/*
+ * rtm_map.c
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+char *rtnl_rtntype_n2a(int id, char *buf, int len)
+{
+ switch (id) {
+ case RTN_UNSPEC:
+ return "none";
+ case RTN_UNICAST:
+ return "unicast";
+ case RTN_LOCAL:
+ return "local";
+ case RTN_BROADCAST:
+ return "broadcast";
+ case RTN_ANYCAST:
+ return "anycast";
+ case RTN_MULTICAST:
+ return "multicast";
+ case RTN_BLACKHOLE:
+ return "blackhole";
+ case RTN_UNREACHABLE:
+ return "unreachable";
+ case RTN_PROHIBIT:
+ return "prohibit";
+ case RTN_THROW:
+ return "throw";
+ case RTN_NAT:
+ return "nat";
+ case RTN_XRESOLVE:
+ return "xresolve";
+ default:
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+}
+
+
+int rtnl_rtntype_a2n(int *id, char *arg)
+{
+ char *end;
+ unsigned long res;
+
+ if (strcmp(arg, "local") == 0)
+ res = RTN_LOCAL;
+ else if (strcmp(arg, "nat") == 0)
+ res = RTN_NAT;
+ else if (matches(arg, "broadcast") == 0 ||
+ strcmp(arg, "brd") == 0)
+ res = RTN_BROADCAST;
+ else if (matches(arg, "anycast") == 0)
+ res = RTN_ANYCAST;
+ else if (matches(arg, "multicast") == 0)
+ res = RTN_MULTICAST;
+ else if (matches(arg, "prohibit") == 0)
+ res = RTN_PROHIBIT;
+ else if (matches(arg, "unreachable") == 0)
+ res = RTN_UNREACHABLE;
+ else if (matches(arg, "blackhole") == 0)
+ res = RTN_BLACKHOLE;
+ else if (matches(arg, "xresolve") == 0)
+ res = RTN_XRESOLVE;
+ else if (matches(arg, "unicast") == 0)
+ res = RTN_UNICAST;
+ else if (strcmp(arg, "throw") == 0)
+ res = RTN_THROW;
+ else {
+ res = strtoul(arg, &end, 0);
+ if (!end || end == arg || *end || res > 255)
+ return -1;
+ }
+ *id = res;
+ return 0;
+}
+
+int get_rt_realms(__u32 *realms, char *arg)
+{
+ __u32 realm = 0;
+ char *p = strchr(arg, '/');
+
+ *realms = 0;
+ if (p) {
+ *p = 0;
+ if (rtnl_rtrealm_a2n(realms, arg)) {
+ *p = '/';
+ return -1;
+ }
+ *realms <<= 16;
+ *p = '/';
+ arg = p+1;
+ }
+ if (*arg && rtnl_rtrealm_a2n(&realm, arg))
+ return -1;
+ *realms |= realm;
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/libiproute/rtm_map.h b/release/src/router/busybox/networking/libiproute/rtm_map.h
new file mode 100644
index 00000000..70bda7d0
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/rtm_map.h
@@ -0,0 +1,10 @@
+#ifndef __RTM_MAP_H__
+#define __RTM_MAP_H__ 1
+
+char *rtnl_rtntype_n2a(int id, char *buf, int len);
+int rtnl_rtntype_a2n(int *id, char *arg);
+
+int get_rt_realms(__u32 *realms, char *arg);
+
+
+#endif /* __RTM_MAP_H__ */
diff --git a/release/src/router/busybox/networking/libiproute/utils.c b/release/src/router/busybox/networking/libiproute/utils.c
new file mode 100644
index 00000000..fa154860
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/utils.c
@@ -0,0 +1,359 @@
+/*
+ * utils.c
+ *
+ * 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 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include "utils.h"
+#include "libbb.h"
+
+int get_integer(int *val, char *arg, int base)
+{
+ long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtol(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_unsigned(unsigned *val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_u32(__u32 * val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_u16(__u16 * val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0xFFFF)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_u8(__u8 * val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0xFF)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_s16(__s16 * val, char *arg, int base)
+{
+ long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtol(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_s8(__s8 * val, char *arg, int base)
+{
+ long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtol(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_addr_1(inet_prefix * addr, char *name, int family)
+{
+ char *cp;
+ unsigned char *ap = (unsigned char *) addr->data;
+ int i;
+
+ memset(addr, 0, sizeof(*addr));
+
+ if (strcmp(name, "default") == 0 ||
+ strcmp(name, "all") == 0 || strcmp(name, "any") == 0) {
+ addr->family = family;
+ addr->bytelen = (family == AF_INET6 ? 16 : 4);
+ addr->bitlen = -1;
+ return 0;
+ }
+
+ if (strchr(name, ':')) {
+ addr->family = AF_INET6;
+ if (family != AF_UNSPEC && family != AF_INET6)
+ return -1;
+ if (inet_pton(AF_INET6, name, addr->data) <= 0)
+ return -1;
+ addr->bytelen = 16;
+ addr->bitlen = -1;
+ return 0;
+ }
+
+ addr->family = AF_INET;
+ if (family != AF_UNSPEC && family != AF_INET)
+ return -1;
+ addr->bytelen = 4;
+ addr->bitlen = -1;
+ for (cp = name, i = 0; *cp; cp++) {
+ if (*cp <= '9' && *cp >= '0') {
+ ap[i] = 10 * ap[i] + (*cp - '0');
+ continue;
+ }
+ if (*cp == '.' && ++i <= 3)
+ continue;
+ return -1;
+ }
+ return 0;
+}
+
+int get_prefix_1(inet_prefix * dst, char *arg, int family)
+{
+ int err;
+ unsigned plen;
+ char *slash;
+
+ memset(dst, 0, sizeof(*dst));
+
+ if (strcmp(arg, "default") == 0 || strcmp(arg, "any") == 0) {
+ dst->family = family;
+ dst->bytelen = 0;
+ dst->bitlen = 0;
+ return 0;
+ }
+
+ slash = strchr(arg, '/');
+ if (slash)
+ *slash = 0;
+ err = get_addr_1(dst, arg, family);
+ if (err == 0) {
+ switch (dst->family) {
+ case AF_INET6:
+ dst->bitlen = 128;
+ break;
+ default:
+ case AF_INET:
+ dst->bitlen = 32;
+ }
+ if (slash) {
+ if (get_integer(&plen, slash + 1, 0) || plen > dst->bitlen) {
+ err = -1;
+ goto done;
+ }
+ dst->bitlen = plen;
+ }
+ }
+ done:
+ if (slash)
+ *slash = '/';
+ return err;
+}
+
+int get_addr(inet_prefix * dst, char *arg, int family)
+{
+ if (family == AF_PACKET) {
+ bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context.", arg);
+ }
+ if (get_addr_1(dst, arg, family)) {
+ bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg);
+ }
+ return 0;
+}
+
+int get_prefix(inet_prefix * dst, char *arg, int family)
+{
+ if (family == AF_PACKET) {
+ bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context.", arg);
+ }
+ if (get_prefix_1(dst, arg, family)) {
+ bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg);
+ }
+ return 0;
+}
+
+__u32 get_addr32(char *name)
+{
+ inet_prefix addr;
+
+ if (get_addr_1(&addr, name, AF_INET)) {
+ bb_error_msg_and_die("an IP address is expected rather than \"%s\"", name);
+ }
+ return addr.data[0];
+}
+
+void incomplete_command()
+{
+ bb_error_msg("Command line is not complete. Try option \"help\"");
+ exit(-1);
+}
+
+void invarg(char *msg, char *arg)
+{
+ bb_error_msg("argument \"%s\" is wrong: %s", arg, msg);
+ exit(-1);
+}
+
+void duparg(char *key, char *arg)
+{
+ bb_error_msg("duplicate \"%s\": \"%s\" is the second value.", key, arg);
+ exit(-1);
+}
+
+void duparg2(char *key, char *arg)
+{
+ bb_error_msg("either \"%s\" is duplicate, or \"%s\" is a garbage.", key, arg);
+ exit(-1);
+}
+
+int matches(char *cmd, char *pattern)
+{
+ int len = strlen(cmd);
+
+ if (len > strlen(pattern)) {
+ return -1;
+ }
+ return memcmp(pattern, cmd, len);
+}
+
+int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits)
+{
+ __u32 *a1 = a->data;
+ __u32 *a2 = b->data;
+ int words = bits >> 0x05;
+
+ bits &= 0x1f;
+
+ if (words)
+ if (memcmp(a1, a2, words << 2))
+ return -1;
+
+ if (bits) {
+ __u32 w1, w2;
+ __u32 mask;
+
+ w1 = a1[words];
+ w2 = a2[words];
+
+ mask = htonl((0xffffffff) << (0x20 - bits));
+
+ if ((w1 ^ w2) & mask)
+ return 1;
+ }
+
+ return 0;
+}
+
+int __iproute2_hz_internal;
+
+int __get_hz(void)
+{
+ int hz = 0;
+ FILE *fp = fopen("/proc/net/psched", "r");
+
+ if (fp) {
+ unsigned nom, denom;
+
+ if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
+ if (nom == 1000000)
+ hz = denom;
+ fclose(fp);
+ }
+ if (hz)
+ return hz;
+ return sysconf(_SC_CLK_TCK);
+}
+
+const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen)
+{
+ switch (af) {
+ case AF_INET:
+ case AF_INET6:
+ return inet_ntop(af, addr, buf, buflen);
+ default:
+ return "???";
+ }
+}
+
+
+const char *format_host(int af, int len, void *addr, char *buf, int buflen)
+{
+#ifdef RESOLVE_HOSTNAMES
+ if (resolve_hosts) {
+ struct hostent *h_ent;
+
+ if (len <= 0) {
+ switch (af) {
+ case AF_INET:
+ len = 4;
+ break;
+ case AF_INET6:
+ len = 16;
+ break;
+ default:;
+ }
+ }
+ if (len > 0 && (h_ent = gethostbyaddr(addr, len, af)) != NULL) {
+ snprintf(buf, buflen - 1, "%s", h_ent->h_name);
+ return buf;
+ }
+ }
+#endif
+ return rt_addr_n2a(af, len, addr, buf, buflen);
+}
diff --git a/release/src/router/busybox/networking/libiproute/utils.h b/release/src/router/busybox/networking/libiproute/utils.h
new file mode 100644
index 00000000..dc28c1b6
--- /dev/null
+++ b/release/src/router/busybox/networking/libiproute/utils.h
@@ -0,0 +1,101 @@
+#ifndef __UTILS_H__
+#define __UTILS_H__ 1
+
+#include <asm/types.h>
+#include <resolv.h>
+
+#include "libnetlink.h"
+#include "ll_map.h"
+#include "rtm_map.h"
+
+extern int preferred_family;
+extern int show_stats;
+extern int show_details;
+extern int show_raw;
+extern int resolve_hosts;
+extern int oneline;
+extern char * _SL_;
+
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+
+#define SPRINT_BSIZE 64
+#define SPRINT_BUF(x) char x[SPRINT_BSIZE]
+
+extern void incomplete_command(void) __attribute__((noreturn));
+
+#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0)
+
+typedef struct
+{
+ __u8 family;
+ __u8 bytelen;
+ __s16 bitlen;
+ __u32 data[4];
+} inet_prefix;
+
+#define DN_MAXADDL 20
+#ifndef AF_DECnet
+#define AF_DECnet 12
+#endif
+
+struct dn_naddr
+{
+ unsigned short a_len;
+ unsigned char a_addr[DN_MAXADDL];
+};
+
+#define IPX_NODE_LEN 6
+
+struct ipx_addr {
+ u_int32_t ipx_net;
+ u_int8_t ipx_node[IPX_NODE_LEN];
+};
+
+extern __u32 get_addr32(char *name);
+extern int get_addr_1(inet_prefix *dst, char *arg, int family);
+extern int get_prefix_1(inet_prefix *dst, char *arg, int family);
+extern int get_addr(inet_prefix *dst, char *arg, int family);
+extern int get_prefix(inet_prefix *dst, char *arg, int family);
+
+extern int get_integer(int *val, char *arg, int base);
+extern int get_unsigned(unsigned *val, char *arg, int base);
+#define get_byte get_u8
+#define get_ushort get_u16
+#define get_short get_s16
+extern int get_u32(__u32 *val, char *arg, int base);
+extern int get_u16(__u16 *val, char *arg, int base);
+extern int get_s16(__s16 *val, char *arg, int base);
+extern int get_u8(__u8 *val, char *arg, int base);
+extern int get_s8(__s8 *val, char *arg, int base);
+
+extern const char *format_host(int af, int len, void *addr, char *buf, int buflen);
+extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen);
+
+void invarg(char *, char *) __attribute__((noreturn));
+void duparg(char *, char *) __attribute__((noreturn));
+void duparg2(char *, char *) __attribute__((noreturn));
+int matches(char *arg, char *pattern);
+extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
+
+const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
+int dnet_pton(int af, const char *src, void *addr);
+
+const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
+int ipx_pton(int af, const char *src, void *addr);
+
+extern int __iproute2_hz_internal;
+extern int __get_hz(void);
+
+static __inline__ int get_hz(void)
+{
+ if (__iproute2_hz_internal == 0)
+ __iproute2_hz_internal = __get_hz();
+ return __iproute2_hz_internal;
+}
+
+#endif /* __UTILS_H__ */
diff --git a/release/src/router/busybox/networking/nameif.c b/release/src/router/busybox/networking/nameif.c
new file mode 100644
index 00000000..dca7c8c0
--- /dev/null
+++ b/release/src/router/busybox/networking/nameif.c
@@ -0,0 +1,222 @@
+/*
+ * nameif.c - Naming Interfaces based on MAC address for busybox.
+ *
+ * Writen 2000 by Andi Kleen.
+ * Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua>
+ * Glenn McGrath <bug1@optushome.com.au>
+ *
+ * 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 of the License, 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
+ *
+ */
+
+
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+
+#include "busybox.h"
+
+/* Older versions of net/if.h do not appear to define IF_NAMESIZE. */
+#ifndef IF_NAMESIZE
+# ifdef IFNAMSIZ
+# define IF_NAMESIZE IFNAMSIZ
+# else
+# define IF_NAMESIZE 16
+# endif
+#endif
+
+/* take from linux/sockios.h */
+#define SIOCSIFNAME 0x8923 /* set interface name */
+
+/* Octets in one ethernet addr, from <linux/if_ether.h> */
+#define ETH_ALEN 6
+
+#ifndef ifr_newname
+#define ifr_newname ifr_ifru.ifru_slave
+#endif
+
+typedef struct mactable_s {
+ struct mactable_s *next;
+ struct mactable_s *prev;
+ char *ifname;
+ struct ether_addr *mac;
+} mactable_t;
+
+static unsigned char use_syslog;
+
+static void serror(const char *s, ...) __attribute__ ((noreturn));
+
+static void serror(const char *s, ...)
+{
+ va_list ap;
+
+ va_start(ap, s);
+
+ if (use_syslog) {
+ openlog(bb_applet_name, 0, LOG_LOCAL0);
+ vsyslog(LOG_ERR, s, ap);
+ closelog();
+ } else {
+ bb_verror_msg(s, ap);
+ putc('\n', stderr);
+ }
+
+ va_end(ap);
+
+ exit(EXIT_FAILURE);
+}
+
+/* Check ascii str_macaddr, convert and copy to *mac */
+struct ether_addr *cc_macaddr(char *str_macaddr)
+{
+ struct ether_addr *lmac, *mac;
+
+ lmac = ether_aton(str_macaddr);
+ if (lmac == NULL)
+ serror("cannot parse MAC %s", str_macaddr);
+ mac = xmalloc(ETH_ALEN);
+ memcpy(mac, lmac, ETH_ALEN);
+
+ return mac;
+}
+
+int nameif_main(int argc, char **argv)
+{
+ mactable_t *clist = NULL;
+ FILE *ifh;
+ const char *fname = "/etc/mactab";
+ char *line;
+ int ctl_sk;
+ int opt;
+ int if_index = 1;
+ mactable_t *ch;
+
+
+ while ((opt = getopt(argc, argv, "c:s")) != -1) {
+ switch (opt) {
+ case 'c':
+ fname = optarg;
+ break;
+ case 's':
+ use_syslog = 1;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+
+ if ((argc - optind) & 1)
+ bb_show_usage();
+
+ if (optind < argc) {
+ char **a = argv + optind;
+
+ while (*a) {
+
+ if (strlen(*a) > IF_NAMESIZE)
+ serror("interface name `%s' too long", *a);
+ ch = xcalloc(1, sizeof(mactable_t));
+ ch->ifname = bb_xstrdup(*a++);
+ ch->mac = cc_macaddr(*a++);
+ if (clist)
+ clist->prev = ch;
+ ch->next = clist;
+ clist = ch;
+ }
+ } else {
+ ifh = bb_xfopen(fname, "r");
+
+ while ((line = bb_get_line_from_file(ifh)) != NULL) {
+ char *line_ptr;
+ size_t name_length;
+
+ line_ptr = line + strspn(line, " \t");
+ if ((line_ptr[0] == '#') || (line_ptr[0] == '\n'))
+ continue;
+ name_length = strcspn(line_ptr, " \t");
+ ch = xcalloc(1, sizeof(mactable_t));
+ ch->ifname = bb_xstrndup(line_ptr, name_length);
+ if (name_length > IF_NAMESIZE)
+ serror("interface name `%s' too long", ch->ifname);
+ line_ptr += name_length;
+ line_ptr += strspn(line_ptr, " \t");
+ name_length = strspn(line_ptr, "0123456789ABCDEFabcdef:");
+ line_ptr[name_length] = '\0';
+ ch->mac = cc_macaddr(line_ptr);
+ if (clist)
+ clist->prev = ch;
+ ch->next = clist;
+ clist = ch;
+ free(line);
+ }
+ fclose(ifh);
+ }
+
+ if ((ctl_sk = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
+ serror("socket: %m");
+
+ while (clist) {
+ struct ifreq ifr;
+
+ bzero(&ifr, sizeof(struct ifreq));
+ if_index++;
+ ifr.ifr_ifindex = if_index;
+
+ /* Get ifname by index or die */
+ if (ioctl(ctl_sk, SIOCGIFNAME, &ifr))
+ break;
+
+ /* Has this device hwaddr? */
+ if (ioctl(ctl_sk, SIOCGIFHWADDR, &ifr))
+ continue;
+
+ /* Search for mac like in ifr.ifr_hwaddr.sa_data */
+ for (ch = clist; ch; ch = ch->next)
+ if (!memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN))
+ break;
+
+ /* Nothing found for current ifr.ifr_hwaddr.sa_data */
+ if (ch == NULL)
+ continue;
+
+ strcpy(ifr.ifr_newname, ch->ifname);
+ if (ioctl(ctl_sk, SIOCSIFNAME, &ifr) < 0)
+ serror("cannot change ifname %s to %s: %m",
+ ifr.ifr_name, ch->ifname);
+
+ /* Remove list entry of renamed interface */
+ if (ch->prev != NULL) {
+ (ch->prev)->next = ch->next;
+ } else {
+ clist = ch->next;
+ }
+ if (ch->next != NULL)
+ (ch->next)->prev = ch->prev;
+#ifdef CONFIG_FEATURE_CLEAN_UP
+ free(ch->ifname);
+ free(ch->mac);
+ free(ch);
+#endif
+ }
+
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/nc.c b/release/src/router/busybox/networking/nc.c
new file mode 100644
index 00000000..4888ccce
--- /dev/null
+++ b/release/src/router/busybox/networking/nc.c
@@ -0,0 +1,177 @@
+/* vi: set sw=4 ts=4: */
+/* nc: mini-netcat - built from the ground up for LRP
+ Copyright (C) 1998 Charles P. Wright
+
+ 0.0.1 6K It works.
+ 0.0.2 5K Smaller and you can also check the exit condition if you wish.
+ 0.0.3 Uses select()
+
+ 19980918 Busy Boxed! Dave Cinege
+ 19990512 Uses Select. Charles P. Wright
+ 19990513 Fixes stdin stupidity and uses buffers. Charles P. Wright
+
+ 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 of the License, 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.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+#define GAPING_SECURITY_HOLE
+
+int nc_main(int argc, char **argv)
+{
+ int do_listen = 0, lport = 0, delay = 0, tmpfd, opt, sfd, x;
+ char buf[BUFSIZ];
+#ifdef GAPING_SECURITY_HOLE
+ char * pr00gie = NULL;
+#endif
+
+ struct sockaddr_in address;
+ struct hostent *hostinfo;
+
+ fd_set readfds, testfds;
+
+ while ((opt = getopt(argc, argv, "lp:i:e:")) > 0) {
+ switch (opt) {
+ case 'l':
+ do_listen++;
+ break;
+ case 'p':
+ lport = atoi(optarg);
+ break;
+ case 'i':
+ delay = atoi(optarg);
+ break;
+#ifdef GAPING_SECURITY_HOLE
+ case 'e':
+ pr00gie = optarg;
+ break;
+#endif
+ default:
+ bb_show_usage();
+ }
+ }
+
+#ifdef GAPING_SECURITY_HOLE
+ if (pr00gie) {
+ /* won't need stdin */
+ close (fileno(stdin));
+ }
+#endif /* GAPING_SECURITY_HOLE */
+
+
+ if ((do_listen && optind != argc) || (!do_listen && optind + 2 != argc))
+ bb_show_usage();
+
+ if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ bb_perror_msg_and_die("socket");
+ x = 1;
+ if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x)) == -1)
+ bb_perror_msg_and_die ("reuseaddr failed");
+ address.sin_family = AF_INET;
+
+ if (lport != 0) {
+ memset(&address.sin_addr, 0, sizeof(address.sin_addr));
+ address.sin_port = htons(lport);
+
+ if (bind(sfd, (struct sockaddr *) &address, sizeof(address)) < 0)
+ bb_perror_msg_and_die("bind");
+ }
+
+ if (do_listen) {
+ socklen_t addrlen = sizeof(address);
+
+ if (listen(sfd, 1) < 0)
+ bb_perror_msg_and_die("listen");
+
+ if ((tmpfd = accept(sfd, (struct sockaddr *) &address, &addrlen)) < 0)
+ bb_perror_msg_and_die("accept");
+
+ close(sfd);
+ sfd = tmpfd;
+ } else {
+ hostinfo = xgethostbyname(argv[optind]);
+
+ address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
+ address.sin_port = htons(atoi(argv[optind+1]));
+
+ if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0)
+ bb_perror_msg_and_die("connect");
+ }
+
+#ifdef GAPING_SECURITY_HOLE
+ /* -e given? */
+ if (pr00gie) {
+ dup2(sfd, 0);
+ close(sfd);
+ dup2 (0, 1);
+ dup2 (0, 2);
+ execl (pr00gie, pr00gie, NULL);
+ /* Don't print stuff or it will go over the wire.... */
+ _exit(-1);
+ }
+#endif /* GAPING_SECURITY_HOLE */
+
+
+ FD_ZERO(&readfds);
+ FD_SET(sfd, &readfds);
+ FD_SET(STDIN_FILENO, &readfds);
+
+ while (1) {
+ int fd;
+ int ofd;
+ int nread;
+
+ testfds = readfds;
+
+ if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0)
+ bb_perror_msg_and_die("select");
+
+ for (fd = 0; fd < FD_SETSIZE; fd++) {
+ if (FD_ISSET(fd, &testfds)) {
+ if ((nread = safe_read(fd, buf, sizeof(buf))) < 0)
+ bb_perror_msg_and_die("read");
+
+ if (fd == sfd) {
+ if (nread == 0)
+ exit(0);
+ ofd = STDOUT_FILENO;
+ } else {
+ if (nread == 0)
+ shutdown(sfd, 1);
+ ofd = sfd;
+ }
+
+ if (bb_full_write(ofd, buf, nread) < 0)
+ bb_perror_msg_and_die("write");
+ if (delay > 0) {
+ sleep(delay);
+ }
+ }
+ }
+ }
+}
diff --git a/release/src/router/busybox/networking/netstat.c b/release/src/router/busybox/networking/netstat.c
new file mode 100644
index 00000000..17a58876
--- /dev/null
+++ b/release/src/router/busybox/networking/netstat.c
@@ -0,0 +1,661 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini netstat implementation(s) for busybox
+ * based in part on the netstat implementation from net-tools.
+ *
+ * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com>
+ *
+ * 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 of the License, 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
+ *
+ * 2002-04-20
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include "inet_common.h"
+#include "busybox.h"
+#include "pwd_.h"
+
+#ifdef CONFIG_ROUTE
+extern void displayroutes(int noresolve, int netstatfmt);
+#endif
+
+#define NETSTAT_CONNECTED 0x01
+#define NETSTAT_LISTENING 0x02
+#define NETSTAT_NUMERIC 0x04
+#define NETSTAT_TCP 0x10
+#define NETSTAT_UDP 0x20
+#define NETSTAT_RAW 0x40
+#define NETSTAT_UNIX 0x80
+
+static int flags = NETSTAT_CONNECTED |
+ NETSTAT_TCP | NETSTAT_UDP | NETSTAT_RAW | NETSTAT_UNIX;
+
+#define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH)
+#define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s)
+#define PROGNAME_WIDTH2(s) #s
+
+#define PRG_HASH_SIZE 211
+
+enum {
+ TCP_ESTABLISHED = 1,
+ TCP_SYN_SENT,
+ TCP_SYN_RECV,
+ TCP_FIN_WAIT1,
+ TCP_FIN_WAIT2,
+ TCP_TIME_WAIT,
+ TCP_CLOSE,
+ TCP_CLOSE_WAIT,
+ TCP_LAST_ACK,
+ TCP_LISTEN,
+ TCP_CLOSING /* now a valid state */
+};
+
+static const char * const tcp_state[] =
+{
+ "",
+ "ESTABLISHED",
+ "SYN_SENT",
+ "SYN_RECV",
+ "FIN_WAIT1",
+ "FIN_WAIT2",
+ "TIME_WAIT",
+ "CLOSE",
+ "CLOSE_WAIT",
+ "LAST_ACK",
+ "LISTEN",
+ "CLOSING"
+};
+
+typedef enum {
+ SS_FREE = 0, /* not allocated */
+ SS_UNCONNECTED, /* unconnected to any socket */
+ SS_CONNECTING, /* in process of connecting */
+ SS_CONNECTED, /* connected to socket */
+ SS_DISCONNECTING /* in process of disconnecting */
+} socket_state;
+
+#define SO_ACCEPTCON (1<<16) /* performed a listen */
+#define SO_WAITDATA (1<<17) /* wait data to read */
+#define SO_NOSPACE (1<<18) /* no space to write */
+
+static char *itoa(unsigned int i)
+{
+ /* 21 digits plus null terminator, good for 64-bit or smaller ints */
+ static char local[22];
+ char *p = &local[21];
+ *p-- = '\0';
+ do {
+ *p-- = '0' + i % 10;
+ i /= 10;
+ } while (i > 0);
+ return p + 1;
+}
+
+static char *get_sname(int port, const char *proto, int num)
+{
+ char *str=itoa(ntohs(port));
+ if (num) {
+ } else {
+ struct servent *se=getservbyport(port,proto);
+ if (se)
+ str=se->s_name;
+ }
+ if (!port) {
+ str="*";
+ }
+ return str;
+}
+
+static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric)
+{
+ char *port_name;
+
+#ifdef CONFIG_FEATURE_IPV6
+ if (addr->sa_family == AF_INET6) {
+ INET6_rresolve(ip_port, size, (struct sockaddr_in6 *)addr,
+ (numeric&NETSTAT_NUMERIC) ? 0x0fff : 0);
+ } else
+#endif
+ {
+ INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
+ 0x4000 | ((numeric&NETSTAT_NUMERIC) ? 0x0fff : 0),
+ 0xffffffff);
+ }
+ port_name=get_sname(htons(port), proto, numeric);
+ if ((strlen(ip_port) + strlen(port_name)) > 22)
+ ip_port[22 - strlen(port_name)] = '\0';
+ ip_port+=strlen(ip_port);
+ strcat(ip_port, ":");
+ strcat(ip_port, port_name);
+}
+
+static void tcp_do_one(int lnr, const char *line)
+{
+ char local_addr[64], rem_addr[64];
+ const char *state_str;
+ char more[512];
+ int num, local_port, rem_port, d, state, timer_run, uid, timeout;
+#ifdef CONFIG_FEATURE_IPV6
+ struct sockaddr_in6 localaddr, remaddr;
+ char addr6[INET6_ADDRSTRLEN];
+ struct in6_addr in6;
+#else
+ struct sockaddr_in localaddr, remaddr;
+#endif
+ unsigned long rxq, txq, time_len, retr, inode;
+
+ if (lnr == 0)
+ return;
+
+ more[0] = '\0';
+ num = sscanf(line,
+ "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
+ &d, local_addr, &local_port,
+ rem_addr, &rem_port, &state,
+ &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+
+ if (strlen(local_addr) > 8) {
+#ifdef CONFIG_FEATURE_IPV6
+ sscanf(local_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
+ sscanf(rem_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
+ localaddr.sin6_family = AF_INET6;
+ remaddr.sin6_family = AF_INET6;
+#endif
+ } else {
+ sscanf(local_addr, "%X",
+ &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
+ sscanf(rem_addr, "%X",
+ &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
+ ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
+ ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+ }
+
+ if (num < 10) {
+ bb_error_msg("warning, got bogus tcp line.");
+ return;
+ }
+ state_str = tcp_state[state];
+ if ((rem_port && (flags&NETSTAT_CONNECTED)) ||
+ (!rem_port && (flags&NETSTAT_LISTENING)))
+ {
+ snprint_ip_port(local_addr, sizeof(local_addr),
+ (struct sockaddr *) &localaddr, local_port,
+ "tcp", flags&NETSTAT_NUMERIC);
+
+ snprint_ip_port(rem_addr, sizeof(rem_addr),
+ (struct sockaddr *) &remaddr, rem_port,
+ "tcp", flags&NETSTAT_NUMERIC);
+
+ printf("tcp %6ld %6ld %-23s %-23s %-12s\n",
+ rxq, txq, local_addr, rem_addr, state_str);
+
+ }
+}
+
+static void udp_do_one(int lnr, const char *line)
+{
+ char local_addr[64], rem_addr[64];
+ char *state_str, more[512];
+ int num, local_port, rem_port, d, state, timer_run, uid, timeout;
+#ifdef CONFIG_FEATURE_IPV6
+ struct sockaddr_in6 localaddr, remaddr;
+ char addr6[INET6_ADDRSTRLEN];
+ struct in6_addr in6;
+#else
+ struct sockaddr_in localaddr, remaddr;
+#endif
+ unsigned long rxq, txq, time_len, retr, inode;
+
+ if (lnr == 0)
+ return;
+
+ more[0] = '\0';
+ num = sscanf(line,
+ "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
+ &d, local_addr, &local_port,
+ rem_addr, &rem_port, &state,
+ &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+
+ if (strlen(local_addr) > 8) {
+#ifdef CONFIG_FEATURE_IPV6
+ /* Demangle what the kernel gives us */
+ sscanf(local_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
+ sscanf(rem_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
+ localaddr.sin6_family = AF_INET6;
+ remaddr.sin6_family = AF_INET6;
+#endif
+ } else {
+ sscanf(local_addr, "%X",
+ &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
+ sscanf(rem_addr, "%X",
+ &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
+ ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
+ ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+ }
+
+ if (num < 10) {
+ bb_error_msg("warning, got bogus udp line.");
+ return;
+ }
+ switch (state) {
+ case TCP_ESTABLISHED:
+ state_str = "ESTABLISHED";
+ break;
+
+ case TCP_CLOSE:
+ state_str = "";
+ break;
+
+ default:
+ state_str = "UNKNOWN";
+ break;
+ }
+
+#ifdef CONFIG_FEATURE_IPV6
+# define notnull(A) (((A.sin6_family == AF_INET6) && \
+ ((A.sin6_addr.s6_addr32[0]) || \
+ (A.sin6_addr.s6_addr32[1]) || \
+ (A.sin6_addr.s6_addr32[2]) || \
+ (A.sin6_addr.s6_addr32[3]))) || \
+ ((A.sin6_family == AF_INET) && \
+ ((struct sockaddr_in *) &A)->sin_addr.s_addr))
+#else
+# define notnull(A) (A.sin_addr.s_addr)
+#endif
+ if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
+ (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
+ {
+ snprint_ip_port(local_addr, sizeof(local_addr),
+ (struct sockaddr *) &localaddr, local_port,
+ "udp", flags&NETSTAT_NUMERIC);
+
+ snprint_ip_port(rem_addr, sizeof(rem_addr),
+ (struct sockaddr *) &remaddr, rem_port,
+ "udp", flags&NETSTAT_NUMERIC);
+
+ printf("udp %6ld %6ld %-23s %-23s %-12s\n",
+ rxq, txq, local_addr, rem_addr, state_str);
+
+ }
+}
+
+static void raw_do_one(int lnr, const char *line)
+{
+ char local_addr[64], rem_addr[64];
+ char *state_str, more[512];
+ int num, local_port, rem_port, d, state, timer_run, uid, timeout;
+#ifdef CONFIG_FEATURE_IPV6
+ struct sockaddr_in6 localaddr, remaddr;
+ char addr6[INET6_ADDRSTRLEN];
+ struct in6_addr in6;
+#else
+ struct sockaddr_in localaddr, remaddr;
+#endif
+ unsigned long rxq, txq, time_len, retr, inode;
+
+ if (lnr == 0)
+ return;
+
+ more[0] = '\0';
+ num = sscanf(line,
+ "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
+ &d, local_addr, &local_port,
+ rem_addr, &rem_port, &state,
+ &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+
+ if (strlen(local_addr) > 8) {
+#ifdef CONFIG_FEATURE_IPV6
+ sscanf(local_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
+ sscanf(rem_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
+ localaddr.sin6_family = AF_INET6;
+ remaddr.sin6_family = AF_INET6;
+#endif
+ } else {
+ sscanf(local_addr, "%X",
+ &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
+ sscanf(rem_addr, "%X",
+ &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
+ ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
+ ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+ }
+
+ if (num < 10) {
+ bb_error_msg("warning, got bogus raw line.");
+ return;
+ }
+ state_str=itoa(state);
+
+#ifdef CONFIG_FEATURE_IPV6
+# define notnull(A) (((A.sin6_family == AF_INET6) && \
+ ((A.sin6_addr.s6_addr32[0]) || \
+ (A.sin6_addr.s6_addr32[1]) || \
+ (A.sin6_addr.s6_addr32[2]) || \
+ (A.sin6_addr.s6_addr32[3]))) || \
+ ((A.sin6_family == AF_INET) && \
+ ((struct sockaddr_in *) &A)->sin_addr.s_addr))
+#else
+# define notnull(A) (A.sin_addr.s_addr)
+#endif
+ if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
+ (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
+ {
+ snprint_ip_port(local_addr, sizeof(local_addr),
+ (struct sockaddr *) &localaddr, local_port,
+ "raw", flags&NETSTAT_NUMERIC);
+
+ snprint_ip_port(rem_addr, sizeof(rem_addr),
+ (struct sockaddr *) &remaddr, rem_port,
+ "raw", flags&NETSTAT_NUMERIC);
+
+ printf("raw %6ld %6ld %-23s %-23s %-12s\n",
+ rxq, txq, local_addr, rem_addr, state_str);
+
+ }
+}
+
+#define HAS_INODE 1
+
+static void unix_do_one(int nr, const char *line)
+{
+ static int has = 0;
+ char path[PATH_MAX], ss_flags[32];
+ char *ss_proto, *ss_state, *ss_type;
+ int num, state, type, inode;
+ void *d;
+ unsigned long refcnt, proto, unix_flags;
+
+ if (nr == 0) {
+ if (strstr(line, "Inode"))
+ has |= HAS_INODE;
+ return;
+ }
+ path[0] = '\0';
+ num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
+ &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
+ if (num < 6) {
+ bb_error_msg("warning, got bogus unix line.");
+ return;
+ }
+ if (!(has & HAS_INODE))
+ snprintf(path,sizeof(path),"%d",inode);
+
+ if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
+ if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
+ if (!(flags&NETSTAT_LISTENING))
+ return;
+ } else {
+ if (!(flags&NETSTAT_CONNECTED))
+ return;
+ }
+ }
+
+ switch (proto) {
+ case 0:
+ ss_proto = "unix";
+ break;
+
+ default:
+ ss_proto = "??";
+ }
+
+ switch (type) {
+ case SOCK_STREAM:
+ ss_type = "STREAM";
+ break;
+
+ case SOCK_DGRAM:
+ ss_type = "DGRAM";
+ break;
+
+ case SOCK_RAW:
+ ss_type = "RAW";
+ break;
+
+ case SOCK_RDM:
+ ss_type = "RDM";
+ break;
+
+ case SOCK_SEQPACKET:
+ ss_type = "SEQPACKET";
+ break;
+
+ default:
+ ss_type = "UNKNOWN";
+ }
+
+ switch (state) {
+ case SS_FREE:
+ ss_state = "FREE";
+ break;
+
+ case SS_UNCONNECTED:
+ /*
+ * Unconnected sockets may be listening
+ * for something.
+ */
+ if (unix_flags & SO_ACCEPTCON) {
+ ss_state = "LISTENING";
+ } else {
+ ss_state = "";
+ }
+ break;
+
+ case SS_CONNECTING:
+ ss_state = "CONNECTING";
+ break;
+
+ case SS_CONNECTED:
+ ss_state = "CONNECTED";
+ break;
+
+ case SS_DISCONNECTING:
+ ss_state = "DISCONNECTING";
+ break;
+
+ default:
+ ss_state = "UNKNOWN";
+ }
+
+ strcpy(ss_flags, "[ ");
+ if (unix_flags & SO_ACCEPTCON)
+ strcat(ss_flags, "ACC ");
+ if (unix_flags & SO_WAITDATA)
+ strcat(ss_flags, "W ");
+ if (unix_flags & SO_NOSPACE)
+ strcat(ss_flags, "N ");
+
+ strcat(ss_flags, "]");
+
+ printf("%-5s %-6ld %-11s %-10s %-13s ",
+ ss_proto, refcnt, ss_flags, ss_type, ss_state);
+ if (has & HAS_INODE)
+ printf("%-6d ",inode);
+ else
+ printf("- ");
+ puts(path);
+}
+
+#define _PATH_PROCNET_UDP "/proc/net/udp"
+#define _PATH_PROCNET_UDP6 "/proc/net/udp6"
+#define _PATH_PROCNET_TCP "/proc/net/tcp"
+#define _PATH_PROCNET_TCP6 "/proc/net/tcp6"
+#define _PATH_PROCNET_RAW "/proc/net/raw"
+#define _PATH_PROCNET_RAW6 "/proc/net/raw6"
+#define _PATH_PROCNET_UNIX "/proc/net/unix"
+
+static void do_info(const char *file, const char *name, void (*proc)(int, const char *))
+{
+ char buffer[8192];
+ int lnr = 0;
+ FILE *procinfo;
+
+ procinfo = fopen(file, "r");
+ if (procinfo == NULL) {
+ if (errno != ENOENT) {
+ perror(file);
+ } else {
+ bb_error_msg("no support for `%s' on this system.", name);
+ }
+ } else {
+ do {
+ if (fgets(buffer, sizeof(buffer), procinfo))
+ (proc)(lnr++, buffer);
+ } while (!feof(procinfo));
+ fclose(procinfo);
+ }
+}
+
+/*
+ * Our main function.
+ */
+
+int netstat_main(int argc, char **argv)
+{
+ int opt;
+ int new_flags=0;
+ int showroute = 0, extended = 0;
+#ifdef CONFIG_FEATURE_IPV6
+ int inet=1;
+ int inet6=1;
+#else
+# define inet 1
+# define inet6 0
+#endif
+ while ((opt = getopt(argc, argv, "laenrtuwx")) != -1)
+ switch (opt) {
+ case 'l':
+ flags &= ~NETSTAT_CONNECTED;
+ flags |= NETSTAT_LISTENING;
+ break;
+ case 'a':
+ flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED;
+ break;
+ case 'n':
+ flags |= NETSTAT_NUMERIC;
+ break;
+ case 'r':
+ showroute = 1;
+ break;
+ case 'e':
+ extended = 1;
+ break;
+ case 't':
+ new_flags |= NETSTAT_TCP;
+ break;
+ case 'u':
+ new_flags |= NETSTAT_UDP;
+ break;
+ case 'w':
+ new_flags |= NETSTAT_RAW;
+ break;
+ case 'x':
+ new_flags |= NETSTAT_UNIX;
+ break;
+ default:
+ bb_show_usage();
+ }
+ if ( showroute ) {
+#ifdef CONFIG_ROUTE
+ displayroutes ( flags & NETSTAT_NUMERIC, !extended );
+ return 0;
+#else
+ bb_error_msg_and_die( "-r (display routing table) is not compiled in." );
+#endif
+ }
+
+ if (new_flags) {
+ flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX);
+ flags |= new_flags;
+ }
+ if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
+ printf("Active Internet connections "); /* xxx */
+
+ if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
+ printf("(servers and established)");
+ else {
+ if (flags&NETSTAT_LISTENING)
+ printf("(only servers)");
+ else
+ printf("(w/o servers)");
+ }
+ printf("\nProto Recv-Q Send-Q Local Address Foreign Address State \n");
+ }
+ if (inet && flags&NETSTAT_TCP)
+ do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one);
+#ifdef CONFIG_FEATURE_IPV6
+ if (inet6 && flags&NETSTAT_TCP)
+ do_info(_PATH_PROCNET_TCP6,"AF INET6 (tcp)",tcp_do_one);
+#endif
+ if (inet && flags&NETSTAT_UDP)
+ do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one);
+#ifdef CONFIG_FEATURE_IPV6
+ if (inet6 && flags&NETSTAT_UDP)
+ do_info(_PATH_PROCNET_UDP6,"AF INET6 (udp)",udp_do_one);
+#endif
+ if (inet && flags&NETSTAT_RAW)
+ do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one);
+#ifdef CONFIG_FEATURE_IPV6
+ if (inet6 && flags&NETSTAT_RAW)
+ do_info(_PATH_PROCNET_RAW6,"AF INET6 (raw)",raw_do_one);
+#endif
+ if (flags&NETSTAT_UNIX) {
+ printf("Active UNIX domain sockets ");
+ if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
+ printf("(servers and established)");
+ else {
+ if (flags&NETSTAT_LISTENING)
+ printf("(only servers)");
+ else
+ printf("(w/o servers)");
+ }
+
+ printf("\nProto RefCnt Flags Type State I-Node Path\n");
+ do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one);
+ }
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/nslookup.c b/release/src/router/busybox/networking/nslookup.c
new file mode 100644
index 00000000..307f6104
--- /dev/null
+++ b/release/src/router/busybox/networking/nslookup.c
@@ -0,0 +1,202 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini nslookup implementation for busybox
+ *
+ * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * Correct default name server display and explicit name server option
+ * added by Ben Zeckel <bzeckel@hmc.edu> June 2001
+ *
+ * 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 of the License, 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
+ *
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <resolv.h>
+#include <arpa/inet.h>
+#include "busybox.h"
+
+/*
+ | I'm only implementing non-interactive mode;
+ | I totally forgot nslookup even had an interactive mode.
+ */
+
+/* only works for IPv4 */
+static int addr_fprint(char *addr)
+{
+ u_int8_t split[4];
+ u_int32_t ip;
+ u_int32_t *x = (u_int32_t *) addr;
+
+ ip = ntohl(*x);
+ split[0] = (ip & 0xff000000) >> 24;
+ split[1] = (ip & 0x00ff0000) >> 16;
+ split[2] = (ip & 0x0000ff00) >> 8;
+ split[3] = (ip & 0x000000ff);
+ printf("%d.%d.%d.%d", split[0], split[1], split[2], split[3]);
+ return 0;
+}
+
+/* takes the NULL-terminated array h_addr_list, and
+ * prints its contents appropriately
+ */
+static int addr_list_fprint(char **h_addr_list)
+{
+ int i, j;
+ char *addr_string = (h_addr_list[1])
+ ? "Addresses: " : "Address: ";
+
+ printf("%s ", addr_string);
+ for (i = 0, j = 0; h_addr_list[i]; i++, j++) {
+ addr_fprint(h_addr_list[i]);
+
+ /* real nslookup does this */
+ if (j == 4) {
+ if (h_addr_list[i + 1]) {
+ printf("\n ");
+ }
+ j = 0;
+ } else {
+ if (h_addr_list[i + 1]) {
+ printf(", ");
+ }
+ }
+
+ }
+ printf("\n");
+ return 0;
+}
+
+/* print the results as nslookup would */
+static struct hostent *hostent_fprint(struct hostent *host, const char *server_host)
+{
+ if (host) {
+ printf("%s %s\n", server_host, host->h_name);
+ addr_list_fprint(host->h_addr_list);
+ } else {
+ printf("*** Unknown host\n");
+ }
+ return host;
+}
+
+/* changes a c-string matching the perl regex \d+\.\d+\.\d+\.\d+
+ * into a u_int32_t
+ */
+static u_int32_t str_to_addr(const char *addr)
+{
+ u_int32_t split[4];
+ u_int32_t ip;
+
+ sscanf(addr, "%d.%d.%d.%d",
+ &split[0], &split[1], &split[2], &split[3]);
+
+ /* assuming sscanf worked */
+ ip = (split[0] << 24) |
+ (split[1] << 16) | (split[2] << 8) | (split[3]);
+
+ return htonl(ip);
+}
+
+/* gethostbyaddr wrapper */
+static struct hostent *gethostbyaddr_wrapper(const char *address)
+{
+ struct in_addr addr;
+
+ addr.s_addr = str_to_addr(address);
+ return gethostbyaddr((char *) &addr, 4, AF_INET); /* IPv4 only for now */
+}
+
+/* lookup the default nameserver and display it */
+static inline void server_print(void)
+{
+ struct sockaddr_in def = _res.nsaddr_list[0];
+ char *ip = inet_ntoa(def.sin_addr);
+
+ hostent_fprint(gethostbyaddr_wrapper(ip), "Server:");
+ printf("\n");
+}
+
+/* alter the global _res nameserver structure to use
+ an explicit dns server instead of what is in /etc/resolv.h */
+static inline void set_default_dns(char *server)
+{
+ struct in_addr server_in_addr;
+
+ if(inet_aton(server,&server_in_addr))
+ {
+ _res.nscount = 1;
+ _res.nsaddr_list[0].sin_addr = server_in_addr;
+ }
+}
+
+/* naive function to check whether char *s is an ip address */
+static int is_ip_address(const char *s)
+{
+ while (*s) {
+ if ((isdigit(*s)) || (*s == '.')) {
+ s++;
+ continue;
+ }
+ return 0;
+ }
+ return 1;
+}
+
+/* ________________________________________________________________________ */
+int nslookup_main(int argc, char **argv)
+{
+ struct hostent *host;
+
+ /*
+ * initialize DNS structure _res used in printing the default
+ * name server and in the explicit name server option feature.
+ */
+
+ res_init();
+
+ /*
+ * We allow 1 or 2 arguments.
+ * The first is the name to be looked up and the second is an
+ * optional DNS server with which to do the lookup.
+ * More than 3 arguments is an error to follow the pattern of the
+ * standard nslookup
+ */
+
+ if (argc < 2 || *argv[1]=='-' || argc > 3)
+ bb_show_usage();
+ else if(argc == 3)
+ set_default_dns(argv[2]);
+
+ server_print();
+ if (is_ip_address(argv[1])) {
+ host = gethostbyaddr_wrapper(argv[1]);
+ } else {
+ host = xgethostbyname(argv[1]);
+ }
+ hostent_fprint(host, "Name: ");
+ return EXIT_SUCCESS;
+}
+
+/* $Id: nslookup.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $ */
diff --git a/release/src/router/busybox/networking/ping.c b/release/src/router/busybox/networking/ping.c
new file mode 100644
index 00000000..dbc95d6a
--- /dev/null
+++ b/release/src/router/busybox/networking/ping.c
@@ -0,0 +1,472 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * $Id: ping.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
+ * Mini ping implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.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 of the License, 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 version of ping is adapted from the ping in netkit-base 0.10,
+ * which is:
+ *
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/signal.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+
+static const int DEFDATALEN = 56;
+static const int MAXIPLEN = 60;
+static const int MAXICMPLEN = 76;
+static const int MAXPACKET = 65468;
+#define MAX_DUP_CHK (8 * 128)
+static const int MAXWAIT = 10;
+static const int PINGINTERVAL = 1; /* second */
+
+#define O_QUIET (1 << 0)
+
+#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
+#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
+#define SET(bit) (A(bit) |= B(bit))
+#define CLR(bit) (A(bit) &= (~B(bit)))
+#define TST(bit) (A(bit) & B(bit))
+
+static void ping(const char *host);
+
+/* common routines */
+static int in_cksum(unsigned short *buf, int sz)
+{
+ int nleft = sz;
+ int sum = 0;
+ unsigned short *w = buf;
+ unsigned short ans = 0;
+
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ if (nleft == 1) {
+ *(unsigned char *) (&ans) = *(unsigned char *) w;
+ sum += ans;
+ }
+
+ sum = (sum >> 16) + (sum & 0xFFFF);
+ sum += (sum >> 16);
+ ans = ~sum;
+ return (ans);
+}
+
+/* simple version */
+#ifndef CONFIG_FEATURE_FANCY_PING
+static char *hostname = NULL;
+static void noresp(int ign)
+{
+ printf("No response from %s\n", hostname);
+ exit(EXIT_FAILURE);
+}
+
+static void ping(const char *host)
+{
+ struct hostent *h;
+ struct sockaddr_in pingaddr;
+ struct icmp *pkt;
+ int pingsock, c;
+ char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+
+ pingsock = create_icmp_socket();
+
+ memset(&pingaddr, 0, sizeof(struct sockaddr_in));
+
+ pingaddr.sin_family = AF_INET;
+ h = xgethostbyname(host);
+ memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
+ hostname = h->h_name;
+
+ pkt = (struct icmp *) packet;
+ memset(pkt, 0, sizeof(packet));
+ pkt->icmp_type = ICMP_ECHO;
+ pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
+
+ c = sendto(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
+
+ if (c < 0 || c != sizeof(packet))
+ bb_perror_msg_and_die("sendto");
+
+ signal(SIGALRM, noresp);
+ alarm(5); /* give the host 5000ms to respond */
+ /* listen for replies */
+ while (1) {
+ struct sockaddr_in from;
+ size_t fromlen = sizeof(from);
+
+ if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen)) < 0) {
+ if (errno == EINTR)
+ continue;
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ if (c >= 76) { /* ip + icmp */
+ struct iphdr *iphdr = (struct iphdr *) packet;
+
+ pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */
+ if (pkt->icmp_type == ICMP_ECHOREPLY)
+ break;
+ }
+ }
+ printf("%s is alive!\n", hostname);
+ return;
+}
+
+extern int ping_main(int argc, char **argv)
+{
+ argc--;
+ argv++;
+ if (argc < 1)
+ bb_show_usage();
+ ping(*argv);
+ return EXIT_SUCCESS;
+}
+
+#else /* ! CONFIG_FEATURE_FANCY_PING */
+/* full(er) version */
+static struct sockaddr_in pingaddr;
+static int pingsock = -1;
+static int datalen; /* intentionally uninitialized to work around gcc bug */
+
+static long ntransmitted, nreceived, nrepeats, pingcount;
+static int myid, options;
+static unsigned long tmin = ULONG_MAX, tmax, tsum;
+static char rcvd_tbl[MAX_DUP_CHK / 8];
+
+struct hostent *hostent;
+
+static void sendping(int);
+static void pingstats(int);
+static void unpack(char *, int, struct sockaddr_in *);
+
+/**************************************************************************/
+
+static void pingstats(int junk)
+{
+ int status;
+
+ signal(SIGINT, SIG_IGN);
+
+ printf("\n--- %s ping statistics ---\n", hostent->h_name);
+ printf("%ld packets transmitted, ", ntransmitted);
+ printf("%ld packets received, ", nreceived);
+ if (nrepeats)
+ printf("%ld duplicates, ", nrepeats);
+ if (ntransmitted)
+ printf("%ld%% packet loss\n",
+ (ntransmitted - nreceived) * 100 / ntransmitted);
+ if (nreceived)
+ printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
+ tmin / 10, tmin % 10,
+ (tsum / (nreceived + nrepeats)) / 10,
+ (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
+ if (nreceived != 0)
+ status = EXIT_SUCCESS;
+ else
+ status = EXIT_FAILURE;
+ exit(status);
+}
+
+static void sendping(int junk)
+{
+ struct icmp *pkt;
+ int i;
+ char packet[datalen + 8];
+
+ pkt = (struct icmp *) packet;
+
+ pkt->icmp_type = ICMP_ECHO;
+ pkt->icmp_code = 0;
+ pkt->icmp_cksum = 0;
+ pkt->icmp_seq = ntransmitted++;
+ pkt->icmp_id = myid;
+ CLR(pkt->icmp_seq % MAX_DUP_CHK);
+
+ gettimeofday((struct timeval *) &packet[8], NULL);
+ pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
+
+ i = sendto(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
+
+ if (i < 0)
+ bb_perror_msg_and_die("sendto");
+ else if ((size_t)i != sizeof(packet))
+ bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
+ (int)sizeof(packet));
+
+ signal(SIGALRM, sendping);
+ if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
+ alarm(PINGINTERVAL);
+ } else { /* done, wait for the last ping to come back */
+ /* todo, don't necessarily need to wait so long... */
+ signal(SIGALRM, pingstats);
+ alarm(MAXWAIT);
+ }
+}
+
+static char *icmp_type_name (int id)
+{
+ switch (id) {
+ case ICMP_ECHOREPLY: return "Echo Reply";
+ case ICMP_DEST_UNREACH: return "Destination Unreachable";
+ case ICMP_SOURCE_QUENCH: return "Source Quench";
+ case ICMP_REDIRECT: return "Redirect (change route)";
+ case ICMP_ECHO: return "Echo Request";
+ case ICMP_TIME_EXCEEDED: return "Time Exceeded";
+ case ICMP_PARAMETERPROB: return "Parameter Problem";
+ case ICMP_TIMESTAMP: return "Timestamp Request";
+ case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
+ case ICMP_INFO_REQUEST: return "Information Request";
+ case ICMP_INFO_REPLY: return "Information Reply";
+ case ICMP_ADDRESS: return "Address Mask Request";
+ case ICMP_ADDRESSREPLY: return "Address Mask Reply";
+ default: return "unknown ICMP type";
+ }
+}
+
+static void unpack(char *buf, int sz, struct sockaddr_in *from)
+{
+ struct icmp *icmppkt;
+ struct iphdr *iphdr;
+ struct timeval tv, *tp;
+ int hlen, dupflag;
+ unsigned long triptime;
+
+ gettimeofday(&tv, NULL);
+
+ /* check IP header */
+ iphdr = (struct iphdr *) buf;
+ hlen = iphdr->ihl << 2;
+ /* discard if too short */
+ if (sz < (datalen + ICMP_MINLEN))
+ return;
+
+ sz -= hlen;
+ icmppkt = (struct icmp *) (buf + hlen);
+
+ if (icmppkt->icmp_id != myid)
+ return; /* not our ping */
+
+ if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
+ ++nreceived;
+ tp = (struct timeval *) icmppkt->icmp_data;
+
+ if ((tv.tv_usec -= tp->tv_usec) < 0) {
+ --tv.tv_sec;
+ tv.tv_usec += 1000000;
+ }
+ tv.tv_sec -= tp->tv_sec;
+
+ triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
+ tsum += triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+
+ if (TST(icmppkt->icmp_seq % MAX_DUP_CHK)) {
+ ++nrepeats;
+ --nreceived;
+ dupflag = 1;
+ } else {
+ SET(icmppkt->icmp_seq % MAX_DUP_CHK);
+ dupflag = 0;
+ }
+
+ if (options & O_QUIET)
+ return;
+
+ printf("%d bytes from %s: icmp_seq=%u", sz,
+ inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
+ icmppkt->icmp_seq);
+ printf(" ttl=%d", iphdr->ttl);
+ printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
+ if (dupflag)
+ printf(" (DUP!)");
+ printf("\n");
+ } else
+ if (icmppkt->icmp_type != ICMP_ECHO)
+ bb_error_msg("Warning: Got ICMP %d (%s)",
+ icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type));
+}
+
+static void ping(const char *host)
+{
+ char packet[datalen + MAXIPLEN + MAXICMPLEN];
+ int sockopt;
+
+ pingsock = create_icmp_socket();
+
+ memset(&pingaddr, 0, sizeof(struct sockaddr_in));
+
+ pingaddr.sin_family = AF_INET;
+ hostent = xgethostbyname(host);
+ if (hostent->h_addrtype != AF_INET)
+ bb_error_msg_and_die("unknown address type; only AF_INET is currently supported.");
+
+ memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr));
+
+ /* enable broadcast pings */
+ sockopt = 1;
+ setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
+ sizeof(sockopt));
+
+ /* set recv buf for broadcast pings */
+ sockopt = 48 * 1024;
+ setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
+ sizeof(sockopt));
+
+ printf("PING %s (%s): %d data bytes\n",
+ hostent->h_name,
+ inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr),
+ datalen);
+
+ signal(SIGINT, pingstats);
+
+ /* start the ping's going ... */
+ sendping(0);
+
+ /* listen for replies */
+ while (1) {
+ struct sockaddr_in from;
+ socklen_t fromlen = (socklen_t) sizeof(from);
+ int c;
+
+ if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen)) < 0) {
+ if (errno == EINTR)
+ continue;
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ unpack(packet, c, &from);
+ if (pingcount > 0 && nreceived >= pingcount)
+ break;
+ }
+ pingstats(0);
+}
+
+extern int ping_main(int argc, char **argv)
+{
+ char *thisarg;
+
+ datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
+
+ argc--;
+ argv++;
+ options = 0;
+ /* Parse any options */
+ while (argc >= 1 && **argv == '-') {
+ thisarg = *argv;
+ thisarg++;
+ switch (*thisarg) {
+ case 'q':
+ options |= O_QUIET;
+ break;
+ case 'c':
+ if (--argc <= 0)
+ bb_show_usage();
+ argv++;
+ pingcount = atoi(*argv);
+ break;
+ case 's':
+ if (--argc <= 0)
+ bb_show_usage();
+ argv++;
+ datalen = atoi(*argv);
+ break;
+ default:
+ bb_show_usage();
+ }
+ argc--;
+ argv++;
+ }
+ if (argc < 1)
+ bb_show_usage();
+
+ myid = getpid() & 0xFFFF;
+ ping(*argv);
+ return EXIT_SUCCESS;
+}
+#endif /* ! CONFIG_FEATURE_FANCY_PING */
+
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/release/src/router/busybox/networking/ping6.c b/release/src/router/busybox/networking/ping6.c
new file mode 100644
index 00000000..b68794c3
--- /dev/null
+++ b/release/src/router/busybox/networking/ping6.c
@@ -0,0 +1,515 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * $Id: ping6.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
+ * Mini ping implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.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 of the License, 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 version of ping is adapted from the ping in netkit-base 0.10,
+ * which is:
+ *
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Original copyright notice is retained at the end of this file.
+ *
+ * This version is an adaptation of ping.c from busybox.
+ * The code was modified by Bart Visscher <magick@linux-fan.com>
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/signal.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h> /* offsetof */
+#include "busybox.h"
+
+static const int DEFDATALEN = 56;
+static const int MAXIPLEN = 60;
+static const int MAXICMPLEN = 76;
+static const int MAXPACKET = 65468;
+#define MAX_DUP_CHK (8 * 128)
+static const int MAXWAIT = 10;
+static const int PINGINTERVAL = 1; /* second */
+
+#define O_QUIET (1 << 0)
+#define O_VERBOSE (1 << 1)
+
+#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
+#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
+#define SET(bit) (A(bit) |= B(bit))
+#define CLR(bit) (A(bit) &= (~B(bit)))
+#define TST(bit) (A(bit) & B(bit))
+
+static void ping(const char *host);
+
+/* simple version */
+#ifndef CONFIG_FEATURE_FANCY_PING6
+static struct hostent *h;
+
+void noresp(int ign)
+{
+ printf("No response from %s\n", h->h_name);
+ exit(EXIT_FAILURE);
+}
+
+static void ping(const char *host)
+{
+ struct sockaddr_in6 pingaddr;
+ struct icmp6_hdr *pkt;
+ int pingsock, c;
+ int sockopt;
+ char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+
+ pingsock = create_icmp6_socket();
+
+ memset(&pingaddr, 0, sizeof(struct sockaddr_in));
+
+ pingaddr.sin6_family = AF_INET6;
+ h = xgethostbyname2(host, AF_INET6);
+ memcpy(&pingaddr.sin6_addr, h->h_addr, sizeof(pingaddr.sin6_addr));
+
+ pkt = (struct icmp6_hdr *) packet;
+ memset(pkt, 0, sizeof(packet));
+ pkt->icmp6_type = ICMP6_ECHO_REQUEST;
+
+ sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
+ setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt,
+ sizeof(sockopt));
+
+ c = sendto(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6));
+
+ if (c < 0 || c != sizeof(packet))
+ bb_perror_msg_and_die("sendto");
+
+ signal(SIGALRM, noresp);
+ alarm(5); /* give the host 5000ms to respond */
+ /* listen for replies */
+ while (1) {
+ struct sockaddr_in6 from;
+ size_t fromlen = sizeof(from);
+
+ if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen)) < 0) {
+ if (errno == EINTR)
+ continue;
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ if (c >= 8) { /* icmp6_hdr */
+ pkt = (struct icmp6_hdr *) packet;
+ if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
+ break;
+ }
+ }
+ printf("%s is alive!\n", h->h_name);
+ return;
+}
+
+extern int ping6_main(int argc, char **argv)
+{
+ argc--;
+ argv++;
+ if (argc < 1)
+ bb_show_usage();
+ ping(*argv);
+ return EXIT_SUCCESS;
+}
+
+#else /* ! CONFIG_FEATURE_FANCY_PING6 */
+/* full(er) version */
+static struct sockaddr_in6 pingaddr;
+static int pingsock = -1;
+static int datalen; /* intentionally uninitialized to work around gcc bug */
+static char* ifname;
+
+static long ntransmitted, nreceived, nrepeats, pingcount;
+static int myid, options;
+static unsigned long tmin = ULONG_MAX, tmax, tsum;
+static char rcvd_tbl[MAX_DUP_CHK / 8];
+
+# ifdef CONFIG_FEATURE_FANCY_PING
+extern
+# endif
+ struct hostent *hostent;
+
+static void sendping(int);
+static void pingstats(int);
+static void unpack(char *, int, struct sockaddr_in6 *, int);
+
+/**************************************************************************/
+
+static void pingstats(int junk)
+{
+ int status;
+
+ signal(SIGINT, SIG_IGN);
+
+ printf("\n--- %s ping statistics ---\n", hostent->h_name);
+ printf("%ld packets transmitted, ", ntransmitted);
+ printf("%ld packets received, ", nreceived);
+ if (nrepeats)
+ printf("%ld duplicates, ", nrepeats);
+ if (ntransmitted)
+ printf("%ld%% packet loss\n",
+ (ntransmitted - nreceived) * 100 / ntransmitted);
+ if (nreceived)
+ printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
+ tmin / 10, tmin % 10,
+ (tsum / (nreceived + nrepeats)) / 10,
+ (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
+ if (nreceived != 0)
+ status = EXIT_SUCCESS;
+ else
+ status = EXIT_FAILURE;
+ exit(status);
+}
+
+static void sendping(int junk)
+{
+ struct icmp6_hdr *pkt;
+ int i;
+ char packet[datalen + 8];
+
+ pkt = (struct icmp6_hdr *) packet;
+
+ pkt->icmp6_type = ICMP6_ECHO_REQUEST;
+ pkt->icmp6_code = 0;
+ pkt->icmp6_cksum = 0;
+ pkt->icmp6_seq = ntransmitted++;
+ pkt->icmp6_id = myid;
+ CLR(pkt->icmp6_seq % MAX_DUP_CHK);
+
+ gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL);
+
+ i = sendto(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6));
+
+ if (i < 0)
+ bb_perror_msg_and_die("sendto");
+ else if ((size_t)i != sizeof(packet))
+ bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
+ (int)sizeof(packet));
+
+ signal(SIGALRM, sendping);
+ if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
+ alarm(PINGINTERVAL);
+ } else { /* done, wait for the last ping to come back */
+ /* todo, don't necessarily need to wait so long... */
+ signal(SIGALRM, pingstats);
+ alarm(MAXWAIT);
+ }
+}
+
+static char *icmp6_type_name (int id)
+{
+ switch (id) {
+ case ICMP6_DST_UNREACH: return "Destination Unreachable";
+ case ICMP6_PACKET_TOO_BIG: return "Packet too big";
+ case ICMP6_TIME_EXCEEDED: return "Time Exceeded";
+ case ICMP6_PARAM_PROB: return "Parameter Problem";
+ case ICMP6_ECHO_REPLY: return "Echo Reply";
+ case ICMP6_ECHO_REQUEST: return "Echo Request";
+ case ICMP6_MEMBERSHIP_QUERY: return "Membership Query";
+ case ICMP6_MEMBERSHIP_REPORT: return "Membership Report";
+ case ICMP6_MEMBERSHIP_REDUCTION: return "Membership Reduction";
+ default: return "unknown ICMP type";
+ }
+}
+
+static void unpack(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
+{
+ struct icmp6_hdr *icmppkt;
+ struct timeval tv, *tp;
+ int dupflag;
+ unsigned long triptime;
+ char buf[INET6_ADDRSTRLEN];
+
+ gettimeofday(&tv, NULL);
+
+ /* discard if too short */
+ if (sz < (datalen + sizeof(struct icmp6_hdr)))
+ return;
+
+ icmppkt = (struct icmp6_hdr *) packet;
+
+ if (icmppkt->icmp6_id != myid)
+ return; /* not our ping */
+
+ if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
+ ++nreceived;
+ tp = (struct timeval *) &icmppkt->icmp6_data8[4];
+
+ if ((tv.tv_usec -= tp->tv_usec) < 0) {
+ --tv.tv_sec;
+ tv.tv_usec += 1000000;
+ }
+ tv.tv_sec -= tp->tv_sec;
+
+ triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
+ tsum += triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+
+ if (TST(icmppkt->icmp6_seq % MAX_DUP_CHK)) {
+ ++nrepeats;
+ --nreceived;
+ dupflag = 1;
+ } else {
+ SET(icmppkt->icmp6_seq % MAX_DUP_CHK);
+ dupflag = 0;
+ }
+
+ if (options & O_QUIET)
+ return;
+
+ printf("%d bytes from %s: icmp6_seq=%u", sz,
+ inet_ntop(AF_INET6, (struct in_addr6 *) &pingaddr.sin6_addr,
+ buf, sizeof(buf)),
+ icmppkt->icmp6_seq);
+ printf(" ttl=%d time=%lu.%lu ms", hoplimit,
+ triptime / 10, triptime % 10);
+ if (dupflag)
+ printf(" (DUP!)");
+ printf("\n");
+ } else
+ if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST)
+ bb_error_msg("Warning: Got ICMP %d (%s)",
+ icmppkt->icmp6_type, icmp6_type_name (icmppkt->icmp6_type));
+}
+
+static void ping(const char *host)
+{
+ char packet[datalen + MAXIPLEN + MAXICMPLEN];
+ char buf[INET6_ADDRSTRLEN];
+ int sockopt;
+ struct msghdr msg;
+ struct sockaddr_in6 from;
+ struct iovec iov;
+ char control_buf[CMSG_SPACE(36)];
+
+ pingsock = create_icmp6_socket();
+
+ memset(&pingaddr, 0, sizeof(struct sockaddr_in));
+
+ pingaddr.sin6_family = AF_INET6;
+ hostent = xgethostbyname2(host, AF_INET6);
+ if (hostent->h_addrtype != AF_INET6)
+ bb_error_msg_and_die("unknown address type; only AF_INET6 is currently supported.");
+
+ memcpy(&pingaddr.sin6_addr, hostent->h_addr, sizeof(pingaddr.sin6_addr));
+
+#ifdef ICMP6_FILTER
+ {
+ struct icmp6_filter filt;
+ if (!(options & O_VERBOSE)) {
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+#if 0
+ if ((options & F_FQDN) || (options & F_FQDNOLD) ||
+ (options & F_NODEADDR) || (options & F_SUPTYPES))
+ ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt);
+ else
+#endif
+ ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
+ } else {
+ ICMP6_FILTER_SETPASSALL(&filt);
+ }
+ if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0)
+ bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
+ }
+#endif /*ICMP6_FILTER*/
+
+ /* enable broadcast pings */
+ sockopt = 1;
+ setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
+ sizeof(sockopt));
+
+ /* set recv buf for broadcast pings */
+ sockopt = 48 * 1024;
+ setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
+ sizeof(sockopt));
+
+ sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
+ setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt,
+ sizeof(sockopt));
+
+ sockopt = 1;
+ setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, (char *) &sockopt,
+ sizeof(sockopt));
+
+ if (ifname) {
+ if ((pingaddr.sin6_scope_id = if_nametoindex(ifname)) == 0)
+ bb_error_msg_and_die("%s: invalid interface name", ifname);
+ }
+
+ printf("PING %s (%s): %d data bytes\n",
+ hostent->h_name,
+ inet_ntop(AF_INET6, (struct in_addr6 *) &pingaddr.sin6_addr,
+ buf, sizeof(buf)),
+ datalen);
+
+ signal(SIGINT, pingstats);
+
+ /* start the ping's going ... */
+ sendping(0);
+
+ /* listen for replies */
+ msg.msg_name=&from;
+ msg.msg_namelen=sizeof(from);
+ msg.msg_iov=&iov;
+ msg.msg_iovlen=1;
+ msg.msg_control=control_buf;
+ iov.iov_base=packet;
+ iov.iov_len=sizeof(packet);
+ while (1) {
+ int c;
+ struct cmsghdr *cmsgptr = NULL;
+ int hoplimit=-1;
+ msg.msg_controllen=sizeof(control_buf);
+
+ if ((c = recvmsg(pingsock, &msg, 0)) < 0) {
+ if (errno == EINTR)
+ continue;
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
+ cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+ if (cmsgptr->cmsg_level == SOL_IPV6 &&
+ cmsgptr->cmsg_type == IPV6_HOPLIMIT ) {
+ hoplimit=*(int*)CMSG_DATA(cmsgptr);
+ }
+ }
+ unpack(packet, c, &from, hoplimit);
+ if (pingcount > 0 && nreceived >= pingcount)
+ break;
+ }
+ pingstats(0);
+}
+
+extern int ping6_main(int argc, char **argv)
+{
+ char *thisarg;
+
+ datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
+
+ argc--;
+ argv++;
+ options = 0;
+ /* Parse any options */
+ while (argc >= 1 && **argv == '-') {
+ thisarg = *argv;
+ thisarg++;
+ switch (*thisarg) {
+ case 'v':
+ options &= ~O_QUIET;
+ options |= O_VERBOSE;
+ break;
+ case 'q':
+ options &= ~O_VERBOSE;
+ options |= O_QUIET;
+ break;
+ case 'c':
+ if (--argc <= 0)
+ bb_show_usage();
+ argv++;
+ pingcount = atoi(*argv);
+ break;
+ case 's':
+ if (--argc <= 0)
+ bb_show_usage();
+ argv++;
+ datalen = atoi(*argv);
+ break;
+ case 'I':
+ if (--argc <= 0)
+ bb_show_usage();
+ argv++;
+ ifname = *argv;
+ break;
+ default:
+ bb_show_usage();
+ }
+ argc--;
+ argv++;
+ }
+ if (argc < 1)
+ bb_show_usage();
+
+ myid = getpid() & 0xFFFF;
+ ping(*argv);
+ return EXIT_SUCCESS;
+}
+#endif /* ! CONFIG_FEATURE_FANCY_PING6 */
+
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/release/src/router/busybox/networking/route.c b/release/src/router/busybox/networking/route.c
new file mode 100644
index 00000000..ff74cfd1
--- /dev/null
+++ b/release/src/router/busybox/networking/route.c
@@ -0,0 +1,705 @@
+/* route
+ *
+ * Similar to the standard Unix route, but with only the necessary
+ * parts for AF_INET and AF_INET6
+ *
+ * Bjorn Wesen, Axis Communications AB
+ *
+ * Author of the original route:
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ * (derived from FvK's 'route.c 1.70 01/04/94')
+ *
+ * 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 of the License, or (at
+ * your option) any later version.
+ *
+ * $Id: route.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
+ *
+ * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
+ * adjustments by Larry Doolittle <LRDoolittle@lbl.gov>
+ *
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include "inet_common.h"
+#include <net/route.h>
+#include <net/if.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "busybox.h"
+
+#define _(x) x
+
+#define RTACTION_ADD 1
+#define RTACTION_DEL 2
+#define RTACTION_HELP 3
+#define RTACTION_FLUSH 4
+#define RTACTION_SHOW 5
+
+#define E_NOTFOUND 8
+#define E_SOCK 7
+#define E_LOOKUP 6
+#define E_VERSION 5
+#define E_USAGE 4
+#define E_OPTERR 3
+#define E_INTERN 2
+#define E_NOSUPP 1
+
+#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */
+#define HAVE_NEW_ADDRT 1
+#endif
+#ifdef RTF_IRTT /* route */
+#define HAVE_RTF_IRTT 1
+#endif
+#ifdef RTF_REJECT /* route */
+#define HAVE_RTF_REJECT 1
+#endif
+
+#if HAVE_NEW_ADDRT
+#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
+#define full_mask(x) (x)
+#else
+#define mask_in_addr(x) ((x).rt_genmask)
+#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
+#endif
+
+
+
+/* add or delete a route depending on action */
+
+static int INET_setroute(int action, int options, char **args)
+{
+ struct rtentry rt;
+ char target[128], gateway[128] = "NONE";
+ const char *netmask = bb_INET_default;
+ int xflag, isnet;
+ int skfd;
+
+ xflag = 0;
+
+ if (*args == NULL)
+ bb_show_usage();
+ if (strcmp(*args, "-net") == 0) {
+ xflag = 1;
+ args++;
+ } else if (strcmp(*args, "-host") == 0) {
+ xflag = 2;
+ args++;
+ }
+ if (*args == NULL)
+ bb_show_usage();
+ safe_strncpy(target, *args++, (sizeof target));
+
+ /* Clean out the RTREQ structure. */
+ memset((char *) &rt, 0, sizeof(struct rtentry));
+
+
+ if ((isnet =
+ INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst,
+ xflag != 1)) < 0) {
+ bb_error_msg(_("can't resolve %s"), target);
+ return EXIT_FAILURE; /* XXX change to E_something */
+ }
+
+ switch (xflag) {
+ case 1:
+ isnet = 1;
+ break;
+
+ case 2:
+ isnet = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Fill in the other fields. */
+ rt.rt_flags = (RTF_UP | RTF_HOST);
+ if (isnet)
+ rt.rt_flags &= ~RTF_HOST;
+
+ while (*args) {
+ if (strcmp(*args, "metric") == 0) {
+ int metric;
+
+ args++;
+ if (!*args || !isdigit(**args))
+ bb_show_usage();
+ metric = atoi(*args);
+#if HAVE_NEW_ADDRT
+ rt.rt_metric = metric + 1;
+#else
+ ENOSUPP("inet_setroute", "NEW_ADDRT (metric)"); /* XXX Fixme */
+#endif
+ args++;
+ continue;
+ }
+
+ if (strcmp(*args, "netmask") == 0) {
+ struct sockaddr mask;
+
+ args++;
+ if (!*args || mask_in_addr(rt))
+ bb_show_usage();
+ netmask = *args;
+ if ((isnet =
+ INET_resolve(netmask, (struct sockaddr_in *) &mask,
+ 0)) < 0) {
+ bb_error_msg(_("can't resolve netmask %s"), netmask);
+ return E_LOOKUP;
+ }
+ rt.rt_genmask = full_mask(mask);
+ args++;
+ continue;
+ }
+
+ if (strcmp(*args, "gw") == 0 || strcmp(*args, "gateway") == 0) {
+ args++;
+ if (!*args)
+ bb_show_usage();
+ if (rt.rt_flags & RTF_GATEWAY)
+ bb_show_usage();
+ safe_strncpy(gateway, *args, (sizeof gateway));
+ if ((isnet =
+ INET_resolve(gateway, (struct sockaddr_in *) &rt.rt_gateway,
+ 1)) < 0) {
+ bb_error_msg(_("can't resolve gw %s"), gateway);
+ return E_LOOKUP;
+ }
+ if (isnet) {
+ bb_error_msg(_("%s: cannot use a NETWORK as gateway!"), gateway);
+ return E_OPTERR;
+ }
+ rt.rt_flags |= RTF_GATEWAY;
+ args++;
+ continue;
+ }
+
+ if (strcmp(*args, "mss") == 0) {
+ args++;
+ rt.rt_flags |= RTF_MSS;
+ if (!*args)
+ bb_show_usage();
+ rt.rt_mss = atoi(*args);
+ args++;
+ if (rt.rt_mss < 64 || rt.rt_mss > 32768) {
+ bb_error_msg(_("Invalid MSS."));
+ return E_OPTERR;
+ }
+ continue;
+ }
+
+ if (strcmp(*args, "window") == 0) {
+ args++;
+ if (!*args)
+ bb_show_usage();
+ rt.rt_flags |= RTF_WINDOW;
+ rt.rt_window = atoi(*args);
+ args++;
+ if (rt.rt_window < 128) {
+ bb_error_msg(_("Invalid window."));
+ return E_OPTERR;
+ }
+ continue;
+ }
+
+ if (strcmp(*args, "irtt") == 0) {
+ args++;
+ if (!*args)
+ bb_show_usage();
+ args++;
+#if HAVE_RTF_IRTT
+ rt.rt_flags |= RTF_IRTT;
+ rt.rt_irtt = atoi(*(args - 1));
+ rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */
+#if 0 /* FIXME: do we need to check anything of this? */
+ if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
+ bb_error_msg(_("Invalid initial rtt."));
+ return E_OPTERR;
+ }
+#endif
+#else
+ ENOSUPP("inet_setroute", "RTF_IRTT"); /* XXX Fixme */
+#endif
+ continue;
+ }
+
+ if (strcmp(*args, "reject") == 0) {
+ args++;
+#if HAVE_RTF_REJECT
+ rt.rt_flags |= RTF_REJECT;
+#else
+ ENOSUPP("inet_setroute", "RTF_REJECT"); /* XXX Fixme */
+#endif
+ continue;
+ }
+ if (strcmp(*args, "mod") == 0) {
+ args++;
+ rt.rt_flags |= RTF_MODIFIED;
+ continue;
+ }
+ if (strcmp(*args, "dyn") == 0) {
+ args++;
+ rt.rt_flags |= RTF_DYNAMIC;
+ continue;
+ }
+ if (strcmp(*args, "reinstate") == 0) {
+ args++;
+ rt.rt_flags |= RTF_REINSTATE;
+ continue;
+ }
+ if (strcmp(*args, "device") == 0 || strcmp(*args, "dev") == 0) {
+ args++;
+ if (rt.rt_dev || *args == NULL)
+ bb_show_usage();
+ rt.rt_dev = *args++;
+ continue;
+ }
+ /* nothing matches */
+ if (!rt.rt_dev) {
+ rt.rt_dev = *args++;
+ if (*args)
+ bb_show_usage(); /* must be last to catch typos */
+ } else {
+ bb_show_usage();
+ }
+ }
+
+#if HAVE_RTF_REJECT
+ if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev)
+ rt.rt_dev = "lo";
+#endif
+
+ /* sanity checks.. */
+ if (mask_in_addr(rt)) {
+ unsigned long mask = mask_in_addr(rt);
+
+ mask = ~ntohl(mask);
+ if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
+ bb_error_msg(_("netmask %.8x doesn't make sense with host route"),
+ (unsigned int) mask);
+ return E_OPTERR;
+ }
+ if (mask & (mask + 1)) {
+ bb_error_msg(_("bogus netmask %s"), netmask);
+ return E_OPTERR;
+ }
+ mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
+ if (mask & ~mask_in_addr(rt)) {
+ bb_error_msg(_("netmask doesn't match route address"));
+ return E_OPTERR;
+ }
+ }
+ /* Fill out netmask if still unset */
+ if ((action == RTACTION_ADD) && rt.rt_flags & RTF_HOST)
+ mask_in_addr(rt) = 0xffffffff;
+
+ /* Create a socket to the INET kernel. */
+ if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return E_SOCK;
+ }
+ /* Tell the kernel to accept this route. */
+ if (action == RTACTION_DEL) {
+ if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
+ perror("SIOCDELRT");
+ close(skfd);
+ return E_SOCK;
+ }
+ } else {
+ if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
+ perror("SIOCADDRT");
+ close(skfd);
+ return E_SOCK;
+ }
+ }
+
+ /* Close the socket. */
+ (void) close(skfd);
+ return EXIT_SUCCESS;
+}
+
+#ifdef CONFIG_FEATURE_IPV6
+static int INET6_setroute(int action, int options, char **args)
+{
+ struct in6_rtmsg rt;
+ struct ifreq ifr;
+ struct sockaddr_in6 sa6;
+ char target[128], gateway[128] = "NONE";
+ int metric, prefix_len;
+ char *devname = NULL;
+ char *cp;
+ int skfd;
+
+ if (*args == NULL)
+ bb_show_usage();
+
+ strcpy(target, *args++);
+ if (!strcmp(target, "default")) {
+ prefix_len = 0;
+ memset(&sa6, 0, sizeof(sa6));
+ } else {
+ if ((cp = strchr(target, '/'))) {
+ prefix_len = atol(cp + 1);
+ if ((prefix_len < 0) || (prefix_len > 128))
+ bb_show_usage();
+ *cp = 0;
+ } else {
+ prefix_len = 128;
+ }
+ if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
+ bb_error_msg(_("can't resolve %s"), target);
+ return EXIT_FAILURE; /* XXX change to E_something */
+ }
+ }
+
+ /* Clean out the RTREQ structure. */
+ memset((char *) &rt, 0, sizeof(struct in6_rtmsg));
+
+ memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
+
+ /* Fill in the other fields. */
+ rt.rtmsg_flags = RTF_UP;
+ if (prefix_len == 128)
+ rt.rtmsg_flags |= RTF_HOST;
+ rt.rtmsg_metric = 1;
+ rt.rtmsg_dst_len = prefix_len;
+
+ while (*args) {
+ if (!strcmp(*args, "metric")) {
+
+ args++;
+ if (!*args || !isdigit(**args))
+ bb_show_usage();
+ metric = atoi(*args);
+ rt.rtmsg_metric = metric;
+ args++;
+ continue;
+ }
+ if (!strcmp(*args, "gw") || !strcmp(*args, "gateway")) {
+ args++;
+ if (!*args)
+ bb_show_usage();
+ if (rt.rtmsg_flags & RTF_GATEWAY)
+ bb_show_usage();
+ strcpy(gateway, *args);
+ if (INET6_resolve(gateway, (struct sockaddr_in6 *) &sa6) < 0) {
+ bb_error_msg(_("can't resolve gw %s"), gateway);
+ return (E_LOOKUP);
+ }
+ memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ rt.rtmsg_flags |= RTF_GATEWAY;
+ args++;
+ continue;
+ }
+ if (!strcmp(*args, "mod")) {
+ args++;
+ rt.rtmsg_flags |= RTF_MODIFIED;
+ continue;
+ }
+ if (!strcmp(*args, "dyn")) {
+ args++;
+ rt.rtmsg_flags |= RTF_DYNAMIC;
+ continue;
+ }
+ if (!strcmp(*args, "device") || !strcmp(*args, "dev")) {
+ args++;
+ if (!*args)
+ bb_show_usage();
+ } else if (args[1])
+ bb_show_usage();
+
+ devname = *args;
+ args++;
+ }
+
+ /* Create a socket to the INET6 kernel. */
+ if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return (E_SOCK);
+ }
+ if (devname) {
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, devname);
+
+ if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) {
+ perror("SIOGIFINDEX");
+ return (E_SOCK);
+ }
+ rt.rtmsg_ifindex = ifr.ifr_ifindex;
+ } else
+ rt.rtmsg_ifindex = 0;
+
+ /* Tell the kernel to accept this route. */
+ if (action == RTACTION_DEL) {
+ if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
+ perror("SIOCDELRT");
+ close(skfd);
+ return (E_SOCK);
+ }
+ } else {
+ if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
+ perror("SIOCADDRT");
+ close(skfd);
+ return (E_SOCK);
+ }
+ }
+
+ /* Close the socket. */
+ (void) close(skfd);
+ return (0);
+}
+#endif
+
+#ifndef RTF_UP
+/* Keep this in sync with /usr/src/linux/include/linux/route.h */
+#define RTF_UP 0x0001 /* route usable */
+#define RTF_GATEWAY 0x0002 /* destination is a gateway */
+#define RTF_HOST 0x0004 /* host entry (net otherwise) */
+#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */
+#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
+#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
+#define RTF_MTU 0x0040 /* specific MTU for this route */
+#ifndef RTF_MSS
+#define RTF_MSS RTF_MTU /* Compatibility :-( */
+#endif
+#define RTF_WINDOW 0x0080 /* per route window clamping */
+#define RTF_IRTT 0x0100 /* Initial round trip time */
+#define RTF_REJECT 0x0200 /* Reject route */
+#endif
+
+void displayroutes(int noresolve, int netstatfmt)
+{
+ char buff[256];
+ int nl = 0;
+ struct in_addr dest;
+ struct in_addr gw;
+ struct in_addr mask;
+ int flgs, ref, use, metric, mtu, win, ir;
+ char flags[64];
+ unsigned long int d, g, m;
+
+ char sdest[16], sgw[16];
+
+ FILE *fp = bb_xfopen("/proc/net/route", "r");
+
+ if (noresolve)
+ noresolve = 0x0fff;
+
+ printf("Kernel IP routing table\n");
+ printf
+ ("Destination Gateway Genmask Flags %s Iface\n",
+ netstatfmt ? " MSS Window irtt" : "Metric Ref Use");
+
+ while (fgets(buff, sizeof(buff), fp) != NULL) {
+ if (nl) {
+ int ifl = 0;
+ int numeric;
+ struct sockaddr_in s_addr;
+
+ while (buff[ifl] != ' ' && buff[ifl] != '\t' && buff[ifl] != '\0')
+ ifl++;
+ buff[ifl] = 0; /* interface */
+ if (sscanf(buff + ifl + 1, "%lx%lx%X%d%d%d%lx%d%d%d",
+ &d, &g, &flgs, &ref, &use, &metric, &m, &mtu, &win,
+ &ir) != 10) {
+ bb_error_msg_and_die("Unsuported kernel route format\n");
+ }
+ ifl = 0; /* parse flags */
+ if (flgs & RTF_UP) {
+ if (flgs & RTF_REJECT)
+ flags[ifl++] = '!';
+ else
+ flags[ifl++] = 'U';
+ if (flgs & RTF_GATEWAY)
+ flags[ifl++] = 'G';
+ if (flgs & RTF_HOST)
+ flags[ifl++] = 'H';
+ if (flgs & RTF_REINSTATE)
+ flags[ifl++] = 'R';
+ if (flgs & RTF_DYNAMIC)
+ flags[ifl++] = 'D';
+ if (flgs & RTF_MODIFIED)
+ flags[ifl++] = 'M';
+ flags[ifl] = 0;
+ dest.s_addr = d;
+ gw.s_addr = g;
+ mask.s_addr = m;
+ memset(&s_addr, 0, sizeof(struct sockaddr_in));
+ s_addr.sin_family = AF_INET;
+ s_addr.sin_addr = dest;
+ numeric = noresolve | 0x8000; /* default instead of * */
+ INET_rresolve(sdest, sizeof(sdest), &s_addr, numeric, m);
+ numeric = noresolve | 0x4000; /* host instead of net */
+ s_addr.sin_addr = gw;
+ INET_rresolve(sgw, sizeof(sgw), &s_addr, numeric, m);
+
+ printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask),
+ flags);
+ if (netstatfmt)
+ printf("%5d %-5d %6d %s\n", mtu, win, ir, buff);
+ else
+ printf("%-6d %-2d %7d %s\n", metric, ref, use, buff);
+ }
+ }
+ nl++;
+ }
+}
+
+#ifdef CONFIG_FEATURE_IPV6
+static void INET6_displayroutes(int noresolve)
+{
+ char buff[256];
+ char iface[16], flags[16];
+ char addr6[128], naddr6[128];
+ struct sockaddr_in6 saddr6, snaddr6;
+ int iflags, metric, refcnt, use, prefix_len, slen;
+ int numeric;
+
+ char addr6p[8][5], saddr6p[8][5], naddr6p[8][5];
+
+ FILE *fp = bb_xfopen("/proc/net/ipv6_route", "r");
+
+ flags[0] = 'U';
+
+ if (noresolve)
+ noresolve = 0x0fff;
+ numeric = noresolve | 0x8000; /* default instead of * */
+
+ printf("Kernel IPv6 routing table\n"
+ "Destination "
+ "Next Hop "
+ "Flags Metric Ref Use Iface\n");
+
+ while (fgets(buff, sizeof(buff), fp) != NULL) {
+ int ifl;
+
+ if (sscanf(buff, "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
+ "%4s%4s%4s%4s%4s%4s%4s%4s %02x "
+ "%4s%4s%4s%4s%4s%4s%4s%4s %08x %08x %08x %08x %s\n",
+ addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+ addr6p[4], addr6p[5], addr6p[6], addr6p[7],
+ &prefix_len,
+ saddr6p[0], saddr6p[1], saddr6p[2], saddr6p[3],
+ saddr6p[4], saddr6p[5], saddr6p[6], saddr6p[7],
+ &slen,
+ naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
+ naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7],
+ &metric, &use, &refcnt, &iflags, iface) != 31) {
+ bb_error_msg_and_die("Unsuported kernel route format\n");
+ }
+
+ ifl = 1; /* parse flags */
+ if (!(iflags & RTF_UP))
+ continue;
+ if (iflags & RTF_GATEWAY)
+ flags[ifl++] = 'G';
+ if (iflags & RTF_HOST)
+ flags[ifl++] = 'H';
+ if (iflags & RTF_DEFAULT)
+ flags[ifl++] = 'D';
+ if (iflags & RTF_ADDRCONF)
+ flags[ifl++] = 'A';
+ if (iflags & RTF_CACHE)
+ flags[ifl++] = 'C';
+ flags[ifl] = 0;
+
+ /* Fetch and resolve the target address. */
+ snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s",
+ addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+ addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &saddr6.sin6_addr);
+ saddr6.sin6_family = AF_INET6;
+
+ INET6_rresolve(addr6, sizeof(addr6), (struct sockaddr_in6 *) &saddr6,
+ numeric);
+ snprintf(addr6, sizeof(addr6), "%s/%d", addr6, prefix_len);
+
+ /* Fetch and resolve the nexthop address. */
+ snprintf(naddr6, sizeof(naddr6), "%s:%s:%s:%s:%s:%s:%s:%s",
+ naddr6p[0], naddr6p[1], naddr6p[2], naddr6p[3],
+ naddr6p[4], naddr6p[5], naddr6p[6], naddr6p[7]);
+ inet_pton(AF_INET6, naddr6, (struct sockaddr *) &snaddr6.sin6_addr);
+ snaddr6.sin6_family = AF_INET6;
+
+ INET6_rresolve(naddr6, sizeof(naddr6),
+ (struct sockaddr_in6 *) &snaddr6, numeric);
+
+ /* Print the info. */
+ printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
+ addr6, naddr6, flags, metric, refcnt, use, iface);
+ }
+}
+#endif
+
+int route_main(int argc, char **argv)
+{
+ int opt;
+ int what = 0;
+
+#ifdef CONFIG_FEATURE_IPV6
+ int af = AF_INET;
+#endif
+
+ if (!argv[1] || (argv[1][0] == '-')) {
+ /* check options */
+ int noresolve = 0;
+ int extended = 0;
+
+ while ((opt = getopt(argc, argv, "A:ne")) > 0) {
+ switch (opt) {
+ case 'n':
+ noresolve = 1;
+ break;
+ case 'e':
+ extended = 1;
+ break;
+ case 'A':
+#ifdef CONFIG_FEATURE_IPV6
+ if (strcmp(optarg, "inet6") == 0)
+ af = AF_INET6;
+ break;
+#endif
+ default:
+ bb_show_usage();
+ }
+ }
+
+#ifdef CONFIG_FEATURE_IPV6
+ if (af == AF_INET6)
+ INET6_displayroutes(*argv != NULL);
+ else
+#endif
+ displayroutes(noresolve, extended);
+ return EXIT_SUCCESS;
+ } else {
+ /* check verb */
+ if (strcmp(argv[1], "add") == 0)
+ what = RTACTION_ADD;
+ else if (strcmp(argv[1], "del") == 0
+ || strcmp(argv[1], "delete") == 0)
+ what = RTACTION_DEL;
+ else if (strcmp(argv[1], "flush") == 0)
+ what = RTACTION_FLUSH;
+ else
+ bb_show_usage();
+ }
+
+#ifdef CONFIG_FEATURE_IPV6
+ if (af == AF_INET6)
+ return INET6_setroute(what, 0, argv + 2);
+#endif
+ return INET_setroute(what, 0, argv + 2);
+}
diff --git a/release/src/router/busybox/networking/telnet.c b/release/src/router/busybox/networking/telnet.c
new file mode 100644
index 00000000..88607f65
--- /dev/null
+++ b/release/src/router/busybox/networking/telnet.c
@@ -0,0 +1,692 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * telnet implementation for busybox
+ *
+ * Author: Tomi Ollila <too@iki.fi>
+ * Copyright (C) 1994-2000 by Tomi Ollila
+ *
+ * Created: Thu Apr 7 13:29:41 1994 too
+ * Last modified: Fri Jun 9 14:34:24 2000 too
+ *
+ * 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 of the License, 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
+ *
+ * HISTORY
+ * Revision 3.1 1994/04/17 11:31:54 too
+ * initial revision
+ * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
+ * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
+ * <jam@ltsp.org>
+ *
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <arpa/telnet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "busybox.h"
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+# include <sys/ioctl.h>
+#endif
+
+#if 0
+static const int DOTRACE = 1;
+#endif
+
+#ifdef DOTRACE
+#include <arpa/inet.h> /* for inet_ntoa()... */
+#define TRACE(x, y) do { if (x) printf y; } while (0)
+#else
+#define TRACE(x, y)
+#endif
+
+#if 0
+#define USE_POLL
+#include <sys/poll.h>
+#else
+#include <sys/time.h>
+#endif
+
+#define DATABUFSIZE 128
+#define IACBUFSIZE 128
+
+static const int CHM_TRY = 0;
+static const int CHM_ON = 1;
+static const int CHM_OFF = 2;
+
+static const int UF_ECHO = 0x01;
+static const int UF_SGA = 0x02;
+
+enum {
+ TS_0 = 1,
+ TS_IAC = 2,
+ TS_OPT = 3,
+ TS_SUB1 = 4,
+ TS_SUB2 = 5,
+};
+
+#define WriteCS(fd, str) write(fd, str, sizeof str -1)
+
+typedef unsigned char byte;
+
+/* use globals to reduce size ??? */ /* test this hypothesis later */
+static struct Globalvars {
+ int netfd; /* console fd:s are 0 and 1 (and 2) */
+ /* same buffer used both for network and console read/write */
+ char buf[DATABUFSIZE]; /* allocating so static size is smaller */
+ byte telstate; /* telnet negotiation state from network input */
+ byte telwish; /* DO, DONT, WILL, WONT */
+ byte charmode;
+ byte telflags;
+ byte gotsig;
+ /* buffer to handle telnet negotiations */
+ char iacbuf[IACBUFSIZE];
+ short iaclen; /* could even use byte */
+ struct termios termios_def;
+ struct termios termios_raw;
+} G;
+
+#define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
+
+#ifdef USE_GLOBALVAR_PTR
+struct Globalvars * Gptr;
+#define G (*Gptr)
+#endif
+
+static inline void iacflush(void)
+{
+ write(G.netfd, G.iacbuf, G.iaclen);
+ G.iaclen = 0;
+}
+
+/* Function prototypes */
+static void rawmode(void);
+static void cookmode(void);
+static void do_linemode(void);
+static void will_charmode(void);
+static void telopt(byte c);
+static int subneg(byte c);
+
+/* Some globals */
+static int one = 1;
+
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+static char *ttype;
+#endif
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+static int win_width, win_height;
+#endif
+
+static void doexit(int ev)
+{
+ cookmode();
+ exit(ev);
+}
+
+static void conescape(void)
+{
+ char b;
+
+ if (G.gotsig) /* came from line mode... go raw */
+ rawmode();
+
+ WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
+ " l go to line mode\r\n"
+ " c go to character mode\r\n"
+ " z suspend telnet\r\n"
+ " e exit telnet\r\n");
+
+ if (read(0, &b, 1) <= 0)
+ doexit(1);
+
+ switch (b)
+ {
+ case 'l':
+ if (!G.gotsig)
+ {
+ do_linemode();
+ goto rrturn;
+ }
+ break;
+ case 'c':
+ if (G.gotsig)
+ {
+ will_charmode();
+ goto rrturn;
+ }
+ break;
+ case 'z':
+ cookmode();
+ kill(0, SIGTSTP);
+ rawmode();
+ break;
+ case 'e':
+ doexit(0);
+ }
+
+ WriteCS(1, "continuing...\r\n");
+
+ if (G.gotsig)
+ cookmode();
+
+ rrturn:
+ G.gotsig = 0;
+
+}
+static void handlenetoutput(int len)
+{
+ /* here we could do smart tricks how to handle 0xFF:s in output
+ * stream like writing twice every sequence of FF:s (thus doing
+ * many write()s. But I think interactive telnet application does
+ * not need to be 100% 8-bit clean, so changing every 0xff:s to
+ * 0x7f:s
+ *
+ * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
+ * I don't agree.
+ * first - I cannot use programs like sz/rz
+ * second - the 0x0D is sent as one character and if the next
+ * char is 0x0A then it's eaten by a server side.
+ * third - whay doy you have to make 'many write()s'?
+ * I don't understand.
+ * So I implemented it. It's realy useful for me. I hope that
+ * others people will find it interesting to.
+ */
+
+ int i, j;
+ byte * p = G.buf;
+ byte outbuf[4*DATABUFSIZE];
+
+ for (i = len, j = 0; i > 0; i--, p++)
+ {
+ if (*p == 0x1d)
+ {
+ conescape();
+ return;
+ }
+ outbuf[j++] = *p;
+ if (*p == 0xff)
+ outbuf[j++] = 0xff;
+ else if (*p == 0x0d)
+ outbuf[j++] = 0x00;
+ }
+ if (j > 0 )
+ write(G.netfd, outbuf, j);
+}
+
+
+static void handlenetinput(int len)
+{
+ int i;
+ int cstart = 0;
+
+ for (i = 0; i < len; i++)
+ {
+ byte c = G.buf[i];
+
+ if (G.telstate == 0) /* most of the time state == 0 */
+ {
+ if (c == IAC)
+ {
+ cstart = i;
+ G.telstate = TS_IAC;
+ }
+ }
+ else
+ switch (G.telstate)
+ {
+ case TS_0:
+ if (c == IAC)
+ G.telstate = TS_IAC;
+ else
+ G.buf[cstart++] = c;
+ break;
+
+ case TS_IAC:
+ if (c == IAC) /* IAC IAC -> 0xFF */
+ {
+ G.buf[cstart++] = c;
+ G.telstate = TS_0;
+ break;
+ }
+ /* else */
+ switch (c)
+ {
+ case SB:
+ G.telstate = TS_SUB1;
+ break;
+ case DO:
+ case DONT:
+ case WILL:
+ case WONT:
+ G.telwish = c;
+ G.telstate = TS_OPT;
+ break;
+ default:
+ G.telstate = TS_0; /* DATA MARK must be added later */
+ }
+ break;
+ case TS_OPT: /* WILL, WONT, DO, DONT */
+ telopt(c);
+ G.telstate = TS_0;
+ break;
+ case TS_SUB1: /* Subnegotiation */
+ case TS_SUB2: /* Subnegotiation */
+ if (subneg(c))
+ G.telstate = TS_0;
+ break;
+ }
+ }
+ if (G.telstate)
+ {
+ if (G.iaclen) iacflush();
+ if (G.telstate == TS_0) G.telstate = 0;
+
+ len = cstart;
+ }
+
+ if (len)
+ write(1, G.buf, len);
+}
+
+
+/* ******************************* */
+
+static inline void putiac(int c)
+{
+ G.iacbuf[G.iaclen++] = c;
+}
+
+
+static void putiac2(byte wwdd, byte c)
+{
+ if (G.iaclen + 3 > IACBUFSIZE)
+ iacflush();
+
+ putiac(IAC);
+ putiac(wwdd);
+ putiac(c);
+}
+
+#if 0
+static void putiac1(byte c)
+{
+ if (G.iaclen + 2 > IACBUFSIZE)
+ iacflush();
+
+ putiac(IAC);
+ putiac(c);
+}
+#endif
+
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+static void putiac_subopt(byte c, char *str)
+{
+ int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
+
+ if (G.iaclen + len > IACBUFSIZE)
+ iacflush();
+
+ putiac(IAC);
+ putiac(SB);
+ putiac(c);
+ putiac(0);
+
+ while(*str)
+ putiac(*str++);
+
+ putiac(IAC);
+ putiac(SE);
+}
+#endif
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+static void putiac_naws(byte c, int x, int y)
+{
+ if (G.iaclen + 9 > IACBUFSIZE)
+ iacflush();
+
+ putiac(IAC);
+ putiac(SB);
+ putiac(c);
+
+ putiac((x >> 8) & 0xff);
+ putiac(x & 0xff);
+ putiac((y >> 8) & 0xff);
+ putiac(y & 0xff);
+
+ putiac(IAC);
+ putiac(SE);
+}
+#endif
+
+/* void putiacstring (subneg strings) */
+
+/* ******************************* */
+
+static char const escapecharis[] = "\r\nEscape character is ";
+
+static void setConMode(void)
+{
+ if (G.telflags & UF_ECHO)
+ {
+ if (G.charmode == CHM_TRY) {
+ G.charmode = CHM_ON;
+ printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
+ rawmode();
+ }
+ }
+ else
+ {
+ if (G.charmode != CHM_OFF) {
+ G.charmode = CHM_OFF;
+ printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
+ cookmode();
+ }
+ }
+}
+
+/* ******************************* */
+
+static void will_charmode(void)
+{
+ G.charmode = CHM_TRY;
+ G.telflags |= (UF_ECHO | UF_SGA);
+ setConMode();
+
+ putiac2(DO, TELOPT_ECHO);
+ putiac2(DO, TELOPT_SGA);
+ iacflush();
+}
+
+static void do_linemode(void)
+{
+ G.charmode = CHM_TRY;
+ G.telflags &= ~(UF_ECHO | UF_SGA);
+ setConMode();
+
+ putiac2(DONT, TELOPT_ECHO);
+ putiac2(DONT, TELOPT_SGA);
+ iacflush();
+}
+
+/* ******************************* */
+
+static inline void to_notsup(char c)
+{
+ if (G.telwish == WILL) putiac2(DONT, c);
+ else if (G.telwish == DO) putiac2(WONT, c);
+}
+
+static inline void to_echo(void)
+{
+ /* if server requests ECHO, don't agree */
+ if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; }
+ else if (G.telwish == DONT) return;
+
+ if (G.telflags & UF_ECHO)
+ {
+ if (G.telwish == WILL)
+ return;
+ }
+ else
+ if (G.telwish == WONT)
+ return;
+
+ if (G.charmode != CHM_OFF)
+ G.telflags ^= UF_ECHO;
+
+ if (G.telflags & UF_ECHO)
+ putiac2(DO, TELOPT_ECHO);
+ else
+ putiac2(DONT, TELOPT_ECHO);
+
+ setConMode();
+ WriteCS(1, "\r\n"); /* sudden modec */
+}
+
+static inline void to_sga(void)
+{
+ /* daemon always sends will/wont, client do/dont */
+
+ if (G.telflags & UF_SGA)
+ {
+ if (G.telwish == WILL)
+ return;
+ }
+ else
+ if (G.telwish == WONT)
+ return;
+
+ if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
+ putiac2(DO, TELOPT_SGA);
+ else
+ putiac2(DONT, TELOPT_SGA);
+
+ return;
+}
+
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+static inline void to_ttype(void)
+{
+ /* Tell server we will (or won't) do TTYPE */
+
+ if(ttype)
+ putiac2(WILL, TELOPT_TTYPE);
+ else
+ putiac2(WONT, TELOPT_TTYPE);
+
+ return;
+}
+#endif
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+static inline void to_naws(void)
+{
+ /* Tell server we will do NAWS */
+ putiac2(WILL, TELOPT_NAWS);
+ return;
+}
+#endif
+
+static void telopt(byte c)
+{
+ switch (c)
+ {
+ case TELOPT_ECHO: to_echo(); break;
+ case TELOPT_SGA: to_sga(); break;
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+ case TELOPT_TTYPE: to_ttype();break;
+#endif
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+ case TELOPT_NAWS: to_naws();
+ putiac_naws(c, win_width, win_height);
+ break;
+#endif
+ default: to_notsup(c);
+ break;
+ }
+}
+
+
+/* ******************************* */
+
+/* subnegotiation -- ignore all (except TTYPE,NAWS) */
+
+static int subneg(byte c)
+{
+ switch (G.telstate)
+ {
+ case TS_SUB1:
+ if (c == IAC)
+ G.telstate = TS_SUB2;
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+ else
+ if (c == TELOPT_TTYPE)
+ putiac_subopt(TELOPT_TTYPE,ttype);
+#endif
+ break;
+ case TS_SUB2:
+ if (c == SE)
+ return TRUE;
+ G.telstate = TS_SUB1;
+ /* break; */
+ }
+ return FALSE;
+}
+
+/* ******************************* */
+
+static void fgotsig(int sig)
+{
+ G.gotsig = sig;
+}
+
+
+static void rawmode(void)
+{
+ tcsetattr(0, TCSADRAIN, &G.termios_raw);
+}
+
+static void cookmode(void)
+{
+ tcsetattr(0, TCSADRAIN, &G.termios_def);
+}
+
+extern int telnet_main(int argc, char** argv)
+{
+ char *host;
+ char *port;
+ int len;
+#ifdef USE_POLL
+ struct pollfd ufds[2];
+#else
+ fd_set readfds;
+ int maxfd;
+#endif
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+ struct winsize winp;
+ if( ioctl(0, TIOCGWINSZ, &winp) == 0 ) {
+ win_width = winp.ws_col;
+ win_height = winp.ws_row;
+ }
+#endif
+
+#ifdef CONFIG_FEATURE_TELNET_TTYPE
+ ttype = getenv("TERM");
+#endif
+
+ memset(&G, 0, sizeof G);
+
+ if (tcgetattr(0, &G.termios_def) < 0)
+ exit(1);
+
+ G.termios_raw = G.termios_def;
+ cfmakeraw(&G.termios_raw);
+
+ if (argc < 2) bb_show_usage();
+ port = (argc > 2)? argv[2] : "23";
+
+ host = argv[1];
+
+ G.netfd = xconnect(host, port);
+
+ setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
+
+ signal(SIGINT, fgotsig);
+
+#ifdef USE_POLL
+ ufds[0].fd = 0; ufds[1].fd = G.netfd;
+ ufds[0].events = ufds[1].events = POLLIN;
+#else
+ FD_ZERO(&readfds);
+ FD_SET(0, &readfds);
+ FD_SET(G.netfd, &readfds);
+ maxfd = G.netfd + 1;
+#endif
+
+ while (1)
+ {
+#ifndef USE_POLL
+ fd_set rfds = readfds;
+
+ switch (select(maxfd, &rfds, NULL, NULL, NULL))
+#else
+ switch (poll(ufds, 2, -1))
+#endif
+ {
+ case 0:
+ /* timeout */
+ case -1:
+ /* error, ignore and/or log something, bay go to loop */
+ if (G.gotsig)
+ conescape();
+ else
+ sleep(1);
+ break;
+ default:
+
+#ifdef USE_POLL
+ if (ufds[0].revents) /* well, should check POLLIN, but ... */
+#else
+ if (FD_ISSET(0, &rfds))
+#endif
+ {
+ len = read(0, G.buf, DATABUFSIZE);
+
+ if (len <= 0)
+ doexit(0);
+
+ TRACE(0, ("Read con: %d\n", len));
+
+ handlenetoutput(len);
+ }
+
+#ifdef USE_POLL
+ if (ufds[1].revents) /* well, should check POLLIN, but ... */
+#else
+ if (FD_ISSET(G.netfd, &rfds))
+#endif
+ {
+ len = read(G.netfd, G.buf, DATABUFSIZE);
+
+ if (len <= 0)
+ {
+ WriteCS(1, "Connection closed by foreign host.\r\n");
+ doexit(1);
+ }
+ TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len));
+
+ handlenetinput(len);
+ }
+ }
+ }
+}
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
diff --git a/release/src/router/busybox/networking/telnetd.c b/release/src/router/busybox/networking/telnetd.c
new file mode 100644
index 00000000..e41cfe58
--- /dev/null
+++ b/release/src/router/busybox/networking/telnetd.c
@@ -0,0 +1,628 @@
+/* $Id: telnetd.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
+ *
+ * Simple telnet server
+ * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
+ *
+ * This file is distributed under the Gnu Public License (GPL),
+ * please see the file LICENSE for further information.
+ *
+ * ---------------------------------------------------------------------------
+ * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
+ ****************************************************************************
+ *
+ * The telnetd manpage says it all:
+ *
+ * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
+ * a client, then creating a login process which has the slave side of the
+ * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
+ * master side of the pseudo-terminal, implementing the telnet protocol and
+ * passing characters between the remote client and the login process.
+ *
+ * Vladimir Oleynik <dzo@simtreas.ru> 2001
+ * Set process group corrections, initial busybox port
+ */
+
+/*#define DEBUG 1 */
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <signal.h>
+#include <termios.h>
+#ifdef DEBUG
+#define TELCMDS
+#define TELOPTS
+#endif
+#include <arpa/telnet.h>
+#include <ctype.h>
+#include <sys/syslog.h>
+
+#include "busybox.h"
+
+#define BUFSIZE 4000
+
+static const char *loginpath =
+#ifdef CONFIG_LOGIN
+"/bin/login";
+#else
+"/bin/sh";
+#endif
+static const char *issuefile = "/etc/issue.net";
+
+/* shell name and arguments */
+
+static const char *argv_init[] = {NULL, NULL};
+
+/* structure that describes a session */
+
+struct tsession {
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ int sockfd_read, sockfd_write, ptyfd;
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+ struct tsession *next;
+ int sockfd, ptyfd;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ int shell_pid;
+ /* two circular buffers */
+ char *buf1, *buf2;
+ int rdidx1, wridx1, size1;
+ int rdidx2, wridx2, size2;
+};
+
+/*
+
+ This is how the buffers are used. The arrows indicate the movement
+ of data.
+
+ +-------+ wridx1++ +------+ rdidx1++ +----------+
+ | | <-------------- | buf1 | <-------------- | |
+ | | size1-- +------+ size1++ | |
+ | pty | | socket |
+ | | rdidx2++ +------+ wridx2++ | |
+ | | --------------> | buf2 | --------------> | |
+ +-------+ size2++ +------+ size2-- +----------+
+
+ Each session has got two buffers.
+
+*/
+
+static int maxfd;
+
+static struct tsession *sessions;
+
+
+/*
+
+ Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored
+ and must be removed so as to not be interpreted by the terminal). Make an
+ uninterrupted string of characters fit for the terminal. Do this by packing
+ all characters meant for the terminal sequentially towards the end of bf.
+
+ Return a pointer to the beginning of the characters meant for the terminal.
+ and make *num_totty the number of characters that should be sent to
+ the terminal.
+
+ Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
+ past (bf + len) then that IAC will be left unprocessed and *processed will be
+ less than len.
+
+ FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
+ what is the escape character? We aren't handling that situation here.
+
+ */
+static char *
+remove_iacs(struct tsession *ts, int *pnum_totty) {
+ unsigned char *ptr0 = ts->buf1 + ts->wridx1;
+ unsigned char *ptr = ptr0;
+ unsigned char *totty = ptr;
+ unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
+ int processed;
+ int num_totty;
+
+ while (ptr < end) {
+ if (*ptr != IAC) {
+ *totty++ = *ptr++;
+ }
+ else {
+ if ((ptr+2) < end) {
+ /* the entire IAC is contained in the buffer
+ we were asked to process. */
+#ifdef DEBUG
+ fprintf(stderr, "Ignoring IAC %s,%s\n",
+ *ptr, TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
+#endif
+ ptr += 3;
+ } else {
+ /* only the beginning of the IAC is in the
+ buffer we were asked to process, we can't
+ process this char. */
+ break;
+ }
+ }
+ }
+
+ processed = ptr - ptr0;
+ num_totty = totty - ptr0;
+ /* the difference between processed and num_to tty
+ is all the iacs we removed from the stream.
+ Adjust buf1 accordingly. */
+ ts->wridx1 += processed - num_totty;
+ ts->size1 -= processed - num_totty;
+ *pnum_totty = num_totty;
+ /* move the chars meant for the terminal towards the end of the
+ buffer. */
+ return memmove(ptr - num_totty, ptr0, num_totty);
+}
+
+
+static int
+getpty(char *line)
+{
+ int p;
+#ifdef CONFIG_FEATURE_DEVPTS
+ p = open("/dev/ptmx", 2);
+ if (p > 0) {
+ grantpt(p);
+ unlockpt(p);
+ strcpy(line, ptsname(p));
+ return(p);
+ }
+#else
+ struct stat stb;
+ int i;
+ int j;
+
+ strcpy(line, "/dev/ptyXX");
+
+ for (i = 0; i < 16; i++) {
+ line[8] = "pqrstuvwxyzabcde"[i];
+ line[9] = '0';
+ if (stat(line, &stb) < 0) {
+ continue;
+ }
+ for (j = 0; j < 16; j++) {
+ line[9] = j < 10 ? j + '0' : j - 10 + 'a';
+ if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
+ line[5] = 't';
+ return p;
+ }
+ }
+ }
+#endif /* CONFIG_FEATURE_DEVPTS */
+ return -1;
+}
+
+
+static void
+send_iac(struct tsession *ts, unsigned char command, int option)
+{
+ /* We rely on that there is space in the buffer for now. */
+ char *b = ts->buf2 + ts->rdidx2;
+ *b++ = IAC;
+ *b++ = command;
+ *b++ = option;
+ ts->rdidx2 += 3;
+ ts->size2 += 3;
+}
+
+
+static struct tsession *
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+make_new_session(void)
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+make_new_session(int sockfd)
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+{
+ struct termios termbuf;
+ int pty, pid;
+ char tty_name[32];
+ struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2);
+
+ ts->buf1 = (char *)(&ts[1]);
+ ts->buf2 = ts->buf1 + BUFSIZE;
+
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ ts->sockfd_read = 0;
+ ts->sockfd_write = 1;
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+ ts->sockfd = sockfd;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+ ts->rdidx1 = ts->wridx1 = ts->size1 = 0;
+ ts->rdidx2 = ts->wridx2 = ts->size2 = 0;
+
+ /* Got a new connection, set up a tty and spawn a shell. */
+
+ pty = getpty(tty_name);
+
+ if (pty < 0) {
+ syslog_msg(LOG_USER, LOG_ERR, "All network ports in use!");
+ return 0;
+ }
+
+ if (pty > maxfd)
+ maxfd = pty;
+
+ ts->ptyfd = pty;
+
+ /* Make the telnet client understand we will echo characters so it
+ * should not do it locally. We don't tell the client to run linemode,
+ * because we want to handle line editing and tab completion and other
+ * stuff that requires char-by-char support.
+ */
+
+ send_iac(ts, DO, TELOPT_ECHO);
+ send_iac(ts, DO, TELOPT_LFLOW);
+ send_iac(ts, WILL, TELOPT_ECHO);
+ send_iac(ts, WILL, TELOPT_SGA);
+
+
+ if ((pid = fork()) < 0) {
+ syslog_msg(LOG_USER, LOG_ERR, "Can`t forking");
+ }
+ if (pid == 0) {
+ /* In child, open the child's side of the tty. */
+ int i;
+
+ for(i = 0; i <= maxfd; i++)
+ close(i);
+ /* make new process group */
+ setsid();
+
+ if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
+ syslog_msg(LOG_USER, LOG_ERR, "Could not open tty");
+ exit(1);
+ }
+ dup(0);
+ dup(0);
+
+ tcsetpgrp(0, getpid());
+
+ /* The pseudo-terminal allocated to the client is configured to operate in
+ * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
+ */
+
+ tcgetattr(0, &termbuf);
+ termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
+ termbuf.c_oflag |= ONLCR|XTABS;
+ termbuf.c_iflag |= ICRNL;
+ termbuf.c_iflag &= ~IXOFF;
+ /*termbuf.c_lflag &= ~ICANON;*/
+ tcsetattr(0, TCSANOW, &termbuf);
+
+ print_login_issue(issuefile, NULL);
+
+ /* exec shell, with correct argv and env */
+ execv(loginpath, (char *const *)argv_init);
+
+ /* NOT REACHED */
+ syslog_msg(LOG_USER, LOG_ERR, "execv error");
+ exit(1);
+ }
+
+ ts->shell_pid = pid;
+
+ return ts;
+}
+
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+static void
+free_session(struct tsession *ts)
+{
+ struct tsession *t = sessions;
+
+ /* Unlink this telnet session from the session list. */
+ if(t == ts)
+ sessions = ts->next;
+ else {
+ while(t->next != ts)
+ t = t->next;
+ t->next = ts->next;
+ }
+
+ kill(ts->shell_pid, SIGKILL);
+
+ wait4(ts->shell_pid, NULL, 0, NULL);
+
+ close(ts->ptyfd);
+ close(ts->sockfd);
+
+ if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
+ maxfd--;
+ if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
+ maxfd--;
+
+ free(ts);
+}
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+int
+telnetd_main(int argc, char **argv)
+{
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+ struct sockaddr_in sa;
+ int master_fd;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ fd_set rdfdset, wrfdset;
+ int selret;
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+ int on = 1;
+ int portnbr = 23;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ int c;
+ static const char options[] =
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ "f:l:";
+#else /* CONFIG_EATURE_TELNETD_INETD */
+ "f:l:p:";
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ int maxlen, w, r;
+
+ for (;;) {
+ c = getopt( argc, argv, options);
+ if (c == EOF) break;
+ switch (c) {
+ case 'f':
+ issuefile = strdup (optarg);
+ break;
+ case 'l':
+ loginpath = strdup (optarg);
+ break;
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+ case 'p':
+ portnbr = atoi(optarg);
+ break;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ default:
+ bb_show_usage();
+ }
+ }
+
+ if (access(loginpath, X_OK) < 0) {
+ bb_error_msg_and_die ("'%s' unavailable.", loginpath);
+ }
+
+ argv_init[0] = loginpath;
+
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ maxfd = 1;
+ sessions = make_new_session();
+#else /* CONFIG_EATURE_TELNETD_INETD */
+ sessions = 0;
+
+ /* Grab a TCP socket. */
+
+ master_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (master_fd < 0) {
+ bb_perror_msg_and_die("socket");
+ }
+ (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ /* Set it to listen to specified port. */
+
+ memset((void *)&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(portnbr);
+
+ if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+ bb_perror_msg_and_die("bind");
+ }
+
+ if (listen(master_fd, 1) < 0) {
+ bb_perror_msg_and_die("listen");
+ }
+
+ if (daemon(0, 0) < 0)
+ bb_perror_msg_and_die("daemon");
+
+
+ maxfd = master_fd;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+ do {
+ struct tsession *ts;
+
+ FD_ZERO(&rdfdset);
+ FD_ZERO(&wrfdset);
+
+ /* select on the master socket, all telnet sockets and their
+ * ptys if there is room in their respective session buffers.
+ */
+
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+ FD_SET(master_fd, &rdfdset);
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+ ts = sessions;
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+ while (ts) {
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ /* buf1 is used from socket to pty
+ * buf2 is used from pty to socket
+ */
+ if (ts->size1 > 0) {
+ FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
+ }
+ if (ts->size1 < BUFSIZE) {
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+ FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ }
+ if (ts->size2 > 0) {
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+ FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ }
+ if (ts->size2 < BUFSIZE) {
+ FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
+ }
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+ ts = ts->next;
+ }
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+ selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
+
+ if (!selret)
+ break;
+
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+ /* First check for and accept new sessions. */
+ if (FD_ISSET(master_fd, &rdfdset)) {
+ int fd, salen;
+
+ salen = sizeof(sa);
+ if ((fd = accept(master_fd, (struct sockaddr *)&sa,
+ &salen)) < 0) {
+ continue;
+ } else {
+ /* Create a new session and link it into
+ our active list. */
+ struct tsession *new_ts = make_new_session(fd);
+ if (new_ts) {
+ new_ts->next = sessions;
+ sessions = new_ts;
+ if (fd > maxfd)
+ maxfd = fd;
+ } else {
+ close(fd);
+ }
+ }
+ }
+
+ /* Then check for data tunneling. */
+
+ ts = sessions;
+ while (ts) { /* For all sessions... */
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+ struct tsession *next = ts->next; /* in case we free ts. */
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+ if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
+ int num_totty;
+ char *ptr;
+ /* Write to pty from buffer 1. */
+
+ ptr = remove_iacs(ts, &num_totty);
+
+ w = write(ts->ptyfd, ptr, num_totty);
+ if (w < 0) {
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ exit(0);
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+ free_session(ts);
+ ts = next;
+ continue;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ }
+ ts->wridx1 += w;
+ ts->size1 -= w;
+ if (ts->wridx1 == BUFSIZE)
+ ts->wridx1 = 0;
+ }
+
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+ if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ /* Write to socket from buffer 2. */
+ maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
+ if (w < 0)
+ exit(0);
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+ w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
+ if (w < 0) {
+ free_session(ts);
+ ts = next;
+ continue;
+ }
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ ts->wridx2 += w;
+ ts->size2 -= w;
+ if (ts->wridx2 == BUFSIZE)
+ ts->wridx2 = 0;
+ }
+
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+ if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ /* Read from socket to buffer 1. */
+ maxlen = MIN(BUFSIZE - ts->rdidx1,
+ BUFSIZE - ts->size1);
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
+ if (!r || (r < 0 && errno != EINTR))
+ exit(0);
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+ r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
+ if (!r || (r < 0 && errno != EINTR)) {
+ free_session(ts);
+ ts = next;
+ continue;
+ }
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ if(!*(ts->buf1 + ts->rdidx1 + r - 1)) {
+ r--;
+ if(!r)
+ continue;
+ }
+ ts->rdidx1 += r;
+ ts->size1 += r;
+ if (ts->rdidx1 == BUFSIZE)
+ ts->rdidx1 = 0;
+ }
+
+ if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
+ /* Read from pty to buffer 2. */
+ maxlen = MIN(BUFSIZE - ts->rdidx2,
+ BUFSIZE - ts->size2);
+ r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
+ if (!r || (r < 0 && errno != EINTR)) {
+#ifdef CONFIG_FEATURE_TELNETD_INETD
+ exit(0);
+#else /* CONFIG_FEATURE_TELNETD_INETD */
+ free_session(ts);
+ ts = next;
+ continue;
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ }
+ ts->rdidx2 += r;
+ ts->size2 += r;
+ if (ts->rdidx2 == BUFSIZE)
+ ts->rdidx2 = 0;
+ }
+
+ if (ts->size1 == 0) {
+ ts->rdidx1 = 0;
+ ts->wridx1 = 0;
+ }
+ if (ts->size2 == 0) {
+ ts->rdidx2 = 0;
+ ts->wridx2 = 0;
+ }
+#ifndef CONFIG_FEATURE_TELNETD_INETD
+ ts = next;
+ }
+#endif /* CONFIG_FEATURE_TELNETD_INETD */
+
+ } while (1);
+
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/tftp.c b/release/src/router/busybox/networking/tftp.c
new file mode 100644
index 00000000..a1a79a09
--- /dev/null
+++ b/release/src/router/busybox/networking/tftp.c
@@ -0,0 +1,587 @@
+/* ------------------------------------------------------------------------- */
+/* tftp.c */
+/* */
+/* A simple tftp client for busybox. */
+/* Tries to follow RFC1350. */
+/* Only "octet" mode supported. */
+/* Optional blocksize negotiation (RFC2347 + RFC2348) */
+/* */
+/* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
+/* */
+/* Parts of the code based on: */
+/* */
+/* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */
+/* and Remi Lefebvre <remi@debian.org> */
+/* */
+/* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */
+/* */
+/* 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 of the License, 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 */
+/* */
+/* ------------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "busybox.h"
+
+//#define CONFIG_FEATURE_TFTP_DEBUG
+
+#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
+#define TFTP_TIMEOUT 5 /* seconds */
+
+/* opcodes we support */
+
+#define TFTP_RRQ 1
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+#define TFTP_OACK 6
+
+static const char *tftp_bb_error_msg[] = {
+ "Undefined error",
+ "File not found",
+ "Access violation",
+ "Disk full or allocation error",
+ "Illegal TFTP operation",
+ "Unknown transfer ID",
+ "File already exists",
+ "No such user"
+};
+
+const int tftp_cmd_get = 1;
+const int tftp_cmd_put = 2;
+
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+
+static int tftp_blocksize_check(int blocksize, int bufsize)
+{
+ /* Check if the blocksize is valid:
+ * RFC2348 says between 8 and 65464,
+ * but our implementation makes it impossible
+ * to use blocksizes smaller than 22 octets.
+ */
+
+ if ((bufsize && (blocksize > bufsize)) ||
+ (blocksize < 8) || (blocksize > 65464)) {
+ bb_error_msg("bad blocksize");
+ return 0;
+ }
+
+ return blocksize;
+}
+
+static char *tftp_option_get(char *buf, int len, char *option)
+{
+ int opt_val = 0;
+ int opt_found = 0;
+ int k;
+
+ while (len > 0) {
+
+ /* Make sure the options are terminated correctly */
+
+ for (k = 0; k < len; k++) {
+ if (buf[k] == '\0') {
+ break;
+ }
+ }
+
+ if (k >= len) {
+ break;
+ }
+
+ if (opt_val == 0) {
+ if (strcasecmp(buf, option) == 0) {
+ opt_found = 1;
+ }
+ }
+ else {
+ if (opt_found) {
+ return buf;
+ }
+ }
+
+ k++;
+
+ buf += k;
+ len -= k;
+
+ opt_val ^= 1;
+ }
+
+ return NULL;
+}
+
+#endif
+
+static inline int tftp(const int cmd, const struct hostent *host,
+ const char *remotefile, int localfd, const int port, int tftp_bufsize)
+{
+ const int cmd_get = cmd & tftp_cmd_get;
+ const int cmd_put = cmd & tftp_cmd_put;
+ const int bb_tftp_num_retries = 5;
+
+ struct sockaddr_in sa;
+ struct sockaddr_in from;
+ struct timeval tv;
+ socklen_t fromlen;
+ fd_set rfds;
+ char *cp;
+ unsigned short tmp;
+ int socketfd;
+ int len;
+ int opcode = 0;
+ int finished = 0;
+ int timeout = bb_tftp_num_retries;
+ int block_nr = 1;
+
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+ int want_option_ack = 0;
+#endif
+
+ /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
+ * size varies meaning BUFFERS_GO_ON_STACK would fail */
+ char *buf=xmalloc(tftp_bufsize + 4);
+
+ tftp_bufsize += 4;
+
+ if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ bb_perror_msg("socket");
+ return EXIT_FAILURE;
+ }
+
+ len = sizeof(sa);
+
+ memset(&sa, 0, len);
+ bind(socketfd, (struct sockaddr *)&sa, len);
+
+ sa.sin_family = host->h_addrtype;
+ sa.sin_port = htons(port);
+ memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
+ sizeof(sa.sin_addr));
+
+ /* build opcode */
+
+ if (cmd_get) {
+ opcode = TFTP_RRQ;
+ }
+
+ if (cmd_put) {
+ opcode = TFTP_WRQ;
+ }
+
+ while (1) {
+
+ cp = buf;
+
+ /* first create the opcode part */
+
+ *((unsigned short *) cp) = htons(opcode);
+
+ cp += 2;
+
+ /* add filename and mode */
+
+ if ((cmd_get && (opcode == TFTP_RRQ)) ||
+ (cmd_put && (opcode == TFTP_WRQ))) {
+ int too_long = 0;
+
+ /* see if the filename fits into buf */
+ /* and fill in packet */
+
+ len = strlen(remotefile) + 1;
+
+ if ((cp + len) >= &buf[tftp_bufsize - 1]) {
+ too_long = 1;
+ }
+ else {
+ safe_strncpy(cp, remotefile, len);
+ cp += len;
+ }
+
+ if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
+ bb_error_msg("too long remote-filename");
+ break;
+ }
+
+ /* add "mode" part of the package */
+
+ memcpy(cp, "octet", 6);
+ cp += 6;
+
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+
+ len = tftp_bufsize - 4; /* data block size */
+
+ if (len != TFTP_BLOCKSIZE_DEFAULT) {
+
+ if ((&buf[tftp_bufsize - 1] - cp) < 15) {
+ bb_error_msg("too long remote-filename");
+ break;
+ }
+
+ /* add "blksize" + number of blocks */
+
+ memcpy(cp, "blksize", 8);
+ cp += 8;
+
+ cp += snprintf(cp, 6, "%d", len) + 1;
+
+ want_option_ack = 1;
+ }
+#endif
+ }
+
+ /* add ack and data */
+
+ if ((cmd_get && (opcode == TFTP_ACK)) ||
+ (cmd_put && (opcode == TFTP_DATA))) {
+
+ *((unsigned short *) cp) = htons(block_nr);
+
+ cp += 2;
+
+ block_nr++;
+
+ if (cmd_put && (opcode == TFTP_DATA)) {
+ len = read(localfd, cp, tftp_bufsize - 4);
+
+ if (len < 0) {
+ bb_perror_msg("read");
+ break;
+ }
+
+ if (len != (tftp_bufsize - 4)) {
+ finished++;
+ }
+
+ cp += len;
+ }
+ }
+
+
+ /* send packet */
+
+
+ timeout = bb_tftp_num_retries; /* re-initialize */
+ do {
+
+ len = cp - buf;
+
+#ifdef CONFIG_FEATURE_TFTP_DEBUG
+ printf("sending %u bytes\n", len);
+ for (cp = buf; cp < &buf[len]; cp++)
+ printf("%02x ", *cp);
+ printf("\n");
+#endif
+ if (sendto(socketfd, buf, len, 0,
+ (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+ bb_perror_msg("send");
+ len = -1;
+ break;
+ }
+
+
+ if (finished && (opcode == TFTP_ACK)) {
+ break;
+ }
+
+ /* receive packet */
+
+ memset(&from, 0, sizeof(from));
+ fromlen = sizeof(from);
+
+ tv.tv_sec = TFTP_TIMEOUT;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(socketfd, &rfds);
+
+ switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
+ case 1:
+ len = recvfrom(socketfd, buf, tftp_bufsize, 0,
+ (struct sockaddr *) &from, &fromlen);
+
+ if (len < 0) {
+ bb_perror_msg("recvfrom");
+ break;
+ }
+
+ timeout = 0;
+
+ if (sa.sin_port == htons(port)) {
+ sa.sin_port = from.sin_port;
+ }
+ if (sa.sin_port == from.sin_port) {
+ break;
+ }
+
+ /* fall-through for bad packets! */
+ /* discard the packet - treat as timeout */
+ timeout = bb_tftp_num_retries;
+
+ case 0:
+ bb_error_msg("timeout");
+
+ timeout--;
+ if (timeout == 0) {
+ len = -1;
+ bb_error_msg("last timeout");
+ }
+ break;
+
+ default:
+ bb_perror_msg("select");
+ len = -1;
+ }
+
+ } while (timeout && (len >= 0));
+
+ if ((finished) || (len < 0)) {
+ break;
+ }
+
+ /* process received packet */
+
+
+ opcode = ntohs(*((unsigned short *) buf));
+ tmp = ntohs(*((unsigned short *) &buf[2]));
+
+#ifdef CONFIG_FEATURE_TFTP_DEBUG
+ printf("received %d bytes: %04x %04x\n", len, opcode, tmp);
+#endif
+
+ if (opcode == TFTP_ERROR) {
+ char *msg = NULL;
+
+ if (buf[4] != '\0') {
+ msg = &buf[4];
+ buf[tftp_bufsize - 1] = '\0';
+ } else if (tmp < (sizeof(tftp_bb_error_msg)
+ / sizeof(char *))) {
+
+ msg = (char *) tftp_bb_error_msg[tmp];
+ }
+
+ if (msg) {
+ bb_error_msg("server says: %s", msg);
+ }
+
+ break;
+ }
+
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+ if (want_option_ack) {
+
+ want_option_ack = 0;
+
+ if (opcode == TFTP_OACK) {
+
+ /* server seems to support options */
+
+ char *res;
+
+ res = tftp_option_get(&buf[2], len-2,
+ "blksize");
+
+ if (res) {
+ int blksize = atoi(res);
+
+ if (tftp_blocksize_check(blksize,
+ tftp_bufsize - 4)) {
+
+ if (cmd_put) {
+ opcode = TFTP_DATA;
+ }
+ else {
+ opcode = TFTP_ACK;
+ }
+#ifdef CONFIG_FEATURE_TFTP_DEBUG
+ printf("using blksize %u\n", blksize);
+#endif
+ tftp_bufsize = blksize + 4;
+ block_nr = 0;
+ continue;
+ }
+ }
+ /* FIXME:
+ * we should send ERROR 8 */
+ bb_error_msg("bad server option");
+ break;
+ }
+
+ bb_error_msg("warning: blksize not supported by server"
+ " - reverting to 512");
+
+ tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
+ }
+#endif
+
+ if (cmd_get && (opcode == TFTP_DATA)) {
+
+ if (tmp == block_nr) {
+
+ len = write(localfd, &buf[4], len - 4);
+
+ if (len < 0) {
+ bb_perror_msg("write");
+ break;
+ }
+
+ if (len != (tftp_bufsize - 4)) {
+ finished++;
+ }
+
+ opcode = TFTP_ACK;
+ continue;
+ }
+ }
+
+ if (cmd_put && (opcode == TFTP_ACK)) {
+
+ if (tmp == (block_nr - 1)) {
+ if (finished) {
+ break;
+ }
+
+ opcode = TFTP_DATA;
+ continue;
+ }
+ }
+ }
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+ close(socketfd);
+
+ free(buf);
+#endif
+
+ return finished ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+int tftp_main(int argc, char **argv)
+{
+ struct hostent *host = NULL;
+ char *localfile = NULL;
+ char *remotefile = NULL;
+ int port = 69;
+ int cmd = 0;
+ int fd = -1;
+ int flags = 0;
+ int opt;
+ int result;
+ int blocksize = TFTP_BLOCKSIZE_DEFAULT;
+
+ /* figure out what to pass to getopt */
+
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+#define BS "b:"
+#else
+#define BS
+#endif
+
+#ifdef CONFIG_FEATURE_TFTP_GET
+#define GET "g"
+#else
+#define GET
+#endif
+
+#ifdef CONFIG_FEATURE_TFTP_PUT
+#define PUT "p"
+#else
+#define PUT
+#endif
+
+ while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
+ switch (opt) {
+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+ case 'b':
+ blocksize = atoi(optarg);
+ if (!tftp_blocksize_check(blocksize, 0)) {
+ return EXIT_FAILURE;
+ }
+ break;
+#endif
+#ifdef CONFIG_FEATURE_TFTP_GET
+ case 'g':
+ cmd = tftp_cmd_get;
+ flags = O_WRONLY | O_CREAT;
+ break;
+#endif
+#ifdef CONFIG_FEATURE_TFTP_PUT
+ case 'p':
+ cmd = tftp_cmd_put;
+ flags = O_RDONLY;
+ break;
+#endif
+ case 'l':
+ localfile = bb_xstrdup(optarg);
+ break;
+ case 'r':
+ remotefile = bb_xstrdup(optarg);
+ break;
+ }
+ }
+
+ if ((cmd == 0) || (optind == argc)) {
+ bb_show_usage();
+ }
+ if(localfile && strcmp(localfile, "-") == 0) {
+ fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
+ }
+ if(localfile == NULL)
+ localfile = remotefile;
+ if(remotefile == NULL)
+ remotefile = localfile;
+ if (fd==-1) {
+ fd = open(localfile, flags, 0644);
+ }
+ if (fd < 0) {
+ bb_perror_msg_and_die("local file");
+ }
+
+ host = xgethostbyname(argv[optind]);
+
+ if (optind + 2 == argc) {
+ port = atoi(argv[optind + 1]);
+ }
+
+#ifdef CONFIG_FEATURE_TFTP_DEBUG
+ printf("using server \"%s\", remotefile \"%s\", "
+ "localfile \"%s\".\n",
+ inet_ntoa(*((struct in_addr *) host->h_addr)),
+ remotefile, localfile);
+#endif
+
+ result = tftp(cmd, host, remotefile, fd, port, blocksize);
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+ if (!(fd == fileno(stdout) || fd == fileno(stdin))) {
+ close(fd);
+ }
+#endif
+ return(result);
+}
diff --git a/release/src/router/busybox/networking/traceroute.c b/release/src/router/busybox/networking/traceroute.c
new file mode 100644
index 00000000..5f8989fd
--- /dev/null
+++ b/release/src/router/busybox/networking/traceroute.c
@@ -0,0 +1,548 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson.
+ *
+ * Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * traceroute host - trace the route ip packets follow going to "host".
+ * Notes
+ * -----
+ * This program must be run by root or be setuid. (I suggest that
+ * you *don't* make it setuid -- casual use could result in a lot
+ * of unnecessary traffic on our poor, congested nets.)
+ *
+ * I stole the idea for this program from Steve Deering. Since
+ * the first release, I've learned that had I attended the right
+ * IETF working group meetings, I also could have stolen it from Guy
+ * Almes or Matt Mathis. I don't know (or care) who came up with
+ * the idea first. I envy the originators' perspicacity and I'm
+ * glad they didn't keep the idea a secret.
+ *
+ * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
+ * enhancements to the original distribution.
+ *
+ * I've hacked up a round-trip-route version of this that works by
+ * sending a loose-source-routed udp datagram through the destination
+ * back to yourself. Unfortunately, SO many gateways botch source
+ * routing, the thing is almost worthless. Maybe one day...
+ *
+ * -- Van Jacobson (van@helios.ee.lbl.gov)
+ * Tue Dec 20 03:50:13 PST 1988
+ */
+
+#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+//#define CONFIG_FEATURE_TRACEROUTE_VERBOSE
+#undef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG /* not in documentation man */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "inet_common.h"
+#include <netdb.h>
+#include <endian.h>
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+
+#define MAXPACKET 65535 /* max ip packet size */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+/*
+ * format of a (udp) probe packet.
+ */
+struct opacket {
+ struct ip ip;
+ struct udphdr udp;
+ u_char seq; /* sequence number of this packet */
+ u_char ttl; /* ttl packet left with */
+ struct timeval tv; /* time packet left */
+};
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+
+#include "busybox.h"
+
+static u_char packet[512]; /* last inbound (icmp) packet */
+static struct opacket *outpacket; /* last output (udp) packet */
+
+static int s; /* receive (icmp) socket file descriptor */
+static int sndsock; /* send (udp) socket file descriptor */
+
+static struct sockaddr whereto; /* Who to try to reach */
+static int datalen; /* How much data */
+
+static char *hostname;
+
+static int max_ttl = 30;
+static u_short ident;
+static u_short port = 32768+666; /* start udp dest port # for probe packets */
+
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+static int verbose;
+#endif
+static int waittime = 5; /* time to wait for response (in seconds) */
+static int nflag; /* print addresses numerically */
+
+/*
+ * Construct an Internet address representation.
+ * If the nflag has been supplied, give
+ * numeric value, otherwise try for symbolic name.
+ */
+static inline void
+inetname(struct sockaddr_in *from)
+{
+ char *cp;
+ static char domain[MAXHOSTNAMELEN + 1];
+ char name[MAXHOSTNAMELEN + 1];
+ static int first = 1;
+ const char *ina;
+
+ if (first && !nflag) {
+ first = 0;
+ if (getdomainname(domain, MAXHOSTNAMELEN) != 0)
+ domain[0] = 0;
+ }
+ cp = 0;
+ if (!nflag && from->sin_addr.s_addr != INADDR_ANY) {
+ if(INET_rresolve(name, sizeof(name), from, 0x4000, 0xffffffff) >= 0) {
+ if ((cp = strchr(name, '.')) &&
+ !strcmp(cp + 1, domain))
+ *cp = 0;
+ cp = (char *)name;
+ }
+ }
+ ina = inet_ntoa(from->sin_addr);
+ if (nflag)
+ printf(" %s", ina);
+ else
+ printf(" %s (%s)", (cp ? cp : ina), ina);
+}
+
+static inline void
+print(u_char *buf, int cc, struct sockaddr_in *from)
+{
+ struct ip *ip;
+ int hlen;
+
+ ip = (struct ip *) buf;
+ hlen = ip->ip_hl << 2;
+ cc -= hlen;
+
+ inetname(from);
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+ if (verbose)
+ printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
+#endif
+}
+
+static inline double
+deltaT(struct timeval *t1p, struct timeval *t2p)
+{
+ double dt;
+
+ dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
+ (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
+ return (dt);
+}
+
+static inline int
+wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer)
+{
+ fd_set fds;
+ static struct timeval wait;
+ int cc = 0;
+ int fromlen = sizeof (*from);
+
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+ if (reset_timer) {
+ /*
+ * traceroute could hang if someone else has a ping
+ * running and our ICMP reply gets dropped but we don't
+ * realize it because we keep waking up to handle those
+ * other ICMP packets that keep coming in. To fix this,
+ * "reset_timer" will only be true if the last packet that
+ * came in was for us or if this is the first time we're
+ * waiting for a reply since sending out a probe. Note
+ * that this takes advantage of the select() feature on
+ * Linux where the remaining timeout is written to the
+ * struct timeval area.
+ */
+ wait.tv_sec = waittime;
+ wait.tv_usec = 0;
+ }
+
+ if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
+ cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
+ (struct sockaddr *)from, &fromlen);
+
+ return(cc);
+}
+
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+/*
+ * Convert an ICMP "type" field to a printable string.
+ */
+static inline const char *
+pr_type(u_char t)
+{
+ static const char * const ttab[] = {
+ "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
+ "Source Quench", "Redirect", "ICMP 6", "ICMP 7",
+ "Echo", "ICMP 9", "ICMP 10", "Time Exceeded",
+ "Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
+ "Info Reply"
+ };
+
+ if(t > 16)
+ return("OUT-OF-RANGE");
+
+ return(ttab[t]);
+}
+#endif
+
+static inline int
+packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq)
+{
+ struct icmp *icp;
+ u_char type, code;
+ int hlen;
+ struct ip *ip;
+
+ ip = (struct ip *) buf;
+ hlen = ip->ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN) {
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+ if (verbose)
+ printf("packet too short (%d bytes) from %s\n", cc,
+ inet_ntoa(from->sin_addr));
+#endif
+ return (0);
+ }
+ cc -= hlen;
+ icp = (struct icmp *)(buf + hlen);
+ type = icp->icmp_type; code = icp->icmp_code;
+ if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
+ type == ICMP_UNREACH) {
+ struct ip *hip;
+ struct udphdr *up;
+
+ hip = &icp->icmp_ip;
+ hlen = hip->ip_hl << 2;
+ up = (struct udphdr *)((u_char *)hip + hlen);
+ if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
+ up->source == htons(ident) &&
+ up->dest == htons(port+seq))
+ return (type == ICMP_TIMXCEED? -1 : code+1);
+ }
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+ if (verbose) {
+ int i;
+ u_long *lp = (u_long *)&icp->icmp_ip;
+
+ printf("\n%d bytes from %s to %s: icmp type %d (%s) code %d\n",
+ cc, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst),
+ type, pr_type(type), icp->icmp_code);
+ for (i = 4; i < cc ; i += sizeof(long))
+ printf("%2d: x%8.8lx\n", i, *lp++);
+ }
+#endif
+ return(0);
+}
+
+static void /* not inline */
+send_probe(int seq, int ttl)
+{
+ struct opacket *op = outpacket;
+ struct ip *ip = &op->ip;
+ struct udphdr *up = &op->udp;
+ int i;
+ struct timezone tz;
+
+ ip->ip_off = 0;
+ ip->ip_hl = sizeof(*ip) >> 2;
+ ip->ip_p = IPPROTO_UDP;
+ ip->ip_len = datalen;
+ ip->ip_ttl = ttl;
+ ip->ip_v = IPVERSION;
+ ip->ip_id = htons(ident+seq);
+
+ up->source = htons(ident);
+ up->dest = htons(port+seq);
+ up->len = htons((u_short)(datalen - sizeof(struct ip)));
+ up->check = 0;
+
+ op->seq = seq;
+ op->ttl = ttl;
+ (void) gettimeofday(&op->tv, &tz);
+
+ i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
+ sizeof(struct sockaddr));
+ if (i < 0 || i != datalen) {
+ if (i<0)
+ perror("sendto");
+ printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
+ datalen, i);
+ (void) fflush(stdout);
+ }
+}
+
+
+int
+#ifndef CONFIG_TRACEROUTE
+main(int argc, char *argv[])
+#else
+traceroute_main(int argc, char *argv[])
+#endif
+{
+ extern char *optarg;
+ extern int optind;
+ struct hostent *hp;
+ struct sockaddr_in from, *to;
+ int ch, i, on, probe, seq, tos, ttl;
+
+ int options = 0; /* socket options */
+ char *source = 0;
+ int nprobes = 3;
+
+ on = 1;
+ seq = tos = 0;
+ to = (struct sockaddr_in *)&whereto;
+ while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
+ switch(ch) {
+ case 'd':
+#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
+ options |= SO_DEBUG;
+#endif
+ break;
+ case 'm':
+ max_ttl = atoi(optarg);
+ if (max_ttl <= 1)
+ bb_error_msg_and_die("max ttl must be >1.");
+ break;
+ case 'n':
+ nflag++;
+ break;
+ case 'p':
+ port = atoi(optarg);
+ if (port < 1)
+ bb_error_msg_and_die("port must be >0.");
+ break;
+ case 'q':
+ nprobes = atoi(optarg);
+ if (nprobes < 1)
+ bb_error_msg_and_die("nprobes must be >0.");
+ break;
+ case 'r':
+ options |= SO_DONTROUTE;
+ break;
+ case 's':
+ /*
+ * set the ip source address of the outbound
+ * probe (e.g., on a multi-homed host).
+ */
+ source = optarg;
+ break;
+ case 't':
+ tos = atoi(optarg);
+ if (tos < 0 || tos > 255)
+ bb_error_msg_and_die("tos must be 0 to 255.");
+ break;
+ case 'v':
+#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+ verbose++;
+#endif
+ break;
+ case 'w':
+ waittime = atoi(optarg);
+ if (waittime <= 1)
+ bb_error_msg_and_die("wait must be >1 sec.");
+ break;
+ default:
+ bb_show_usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ bb_show_usage();
+
+ setlinebuf (stdout);
+
+ memset(&whereto, 0, sizeof(struct sockaddr));
+ hp = xgethostbyname(*argv);
+ to->sin_family = hp->h_addrtype;
+ memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
+ hostname = (char *)hp->h_name;
+ if (*++argv)
+ datalen = atoi(*argv);
+ if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
+ bb_error_msg_and_die("packet size must be 0 <= s < %d.",
+ MAXPACKET - sizeof(struct opacket));
+ datalen += sizeof(struct opacket);
+ outpacket = (struct opacket *)xmalloc((unsigned)datalen);
+ memset(outpacket, 0, datalen);
+ outpacket->ip.ip_dst = to->sin_addr;
+ outpacket->ip.ip_tos = tos;
+ outpacket->ip.ip_v = IPVERSION;
+ outpacket->ip.ip_id = 0;
+
+ ident = (getpid() & 0xffff) | 0x8000;
+
+ if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
+ bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
+
+ s = create_icmp_socket();
+
+#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
+ if (options & SO_DEBUG)
+ (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof(on));
+#endif
+ if (options & SO_DONTROUTE)
+ (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
+ (char *)&on, sizeof(on));
+#ifdef SO_SNDBUF
+ if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
+ sizeof(datalen)) < 0)
+ bb_perror_msg_and_die("SO_SNDBUF");
+#endif
+#ifdef IP_HDRINCL
+ if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
+ sizeof(on)) < 0)
+ bb_perror_msg_and_die("IP_HDRINCL");
+#endif
+#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
+ if (options & SO_DEBUG)
+ (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof(on));
+#endif
+ if (options & SO_DONTROUTE)
+ (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
+ (char *)&on, sizeof(on));
+
+ if (source) {
+ memset(&from, 0, sizeof(struct sockaddr));
+ from.sin_family = AF_INET;
+ from.sin_addr.s_addr = inet_addr(source);
+ if (from.sin_addr.s_addr == -1)
+ bb_error_msg_and_die("unknown host %s", source);
+ outpacket->ip.ip_src = from.sin_addr;
+#ifndef IP_HDRINCL
+ if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
+ bb_perror_msg_and_die("bind");
+#endif
+ }
+
+ fprintf(stderr, "traceroute to %s (%s)", hostname,
+ inet_ntoa(to->sin_addr));
+ if (source)
+ fprintf(stderr, " from %s", source);
+ fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
+
+ for (ttl = 1; ttl <= max_ttl; ++ttl) {
+ u_long lastaddr = 0;
+ int got_there = 0;
+ int unreachable = 0;
+
+ printf("%2d ", ttl);
+ for (probe = 0; probe < nprobes; ++probe) {
+ int cc, reset_timer;
+ struct timeval t1, t2;
+ struct timezone tz;
+ struct ip *ip;
+
+ (void) gettimeofday(&t1, &tz);
+ send_probe(++seq, ttl);
+ reset_timer = 1;
+ while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) {
+ (void) gettimeofday(&t2, &tz);
+ if ((i = packet_ok(packet, cc, &from, seq))) {
+ reset_timer = 1;
+ if (from.sin_addr.s_addr != lastaddr) {
+ print(packet, cc, &from);
+ lastaddr = from.sin_addr.s_addr;
+ }
+ printf(" %g ms", deltaT(&t1, &t2));
+ switch(i - 1) {
+ case ICMP_UNREACH_PORT:
+ ip = (struct ip *)packet;
+ if (ip->ip_ttl <= 1)
+ printf(" !");
+ ++got_there;
+ break;
+ case ICMP_UNREACH_NET:
+ ++unreachable;
+ printf(" !N");
+ break;
+ case ICMP_UNREACH_HOST:
+ ++unreachable;
+ printf(" !H");
+ break;
+ case ICMP_UNREACH_PROTOCOL:
+ ++got_there;
+ printf(" !P");
+ break;
+ case ICMP_UNREACH_NEEDFRAG:
+ ++unreachable;
+ printf(" !F");
+ break;
+ case ICMP_UNREACH_SRCFAIL:
+ ++unreachable;
+ printf(" !S");
+ break;
+ }
+ break;
+ } else
+ reset_timer = 0;
+ }
+ if (cc == 0)
+ printf(" *");
+ (void) fflush(stdout);
+ }
+ putchar('\n');
+ if (got_there || unreachable >= nprobes-1)
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/udhcp/AUTHORS b/release/src/router/busybox/networking/udhcp/AUTHORS
new file mode 100755
index 00000000..3772aedb
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/AUTHORS
@@ -0,0 +1,14 @@
+udhcp server/client package
+-----------------------
+
+Russ Dill <Russ.Dill@asu.edu>
+Matthew Ramsay <matthewr@moreton.com.au>
+Chris Trew <christ@moreton.com.au>
+
+Other Credits:
+--------------
+Moreton Bay (http://www.moretonbay.com/)
+Lineo (http://opensource.lineo.com)
+Vladimir Oleynik <dzo@simtrea.ru>, optimize and more integrate for busybox
+
+
diff --git a/release/src/router/busybox/networking/udhcp/COPYING b/release/src/router/busybox/networking/udhcp/COPYING
new file mode 100755
index 00000000..a43ea212
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/release/src/router/busybox/networking/udhcp/ChangeLog b/release/src/router/busybox/networking/udhcp/ChangeLog
new file mode 100755
index 00000000..9ee97534
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/ChangeLog
@@ -0,0 +1,262 @@
+0.9.10
+ Size optimization (over 3k), more busybox integration
+ (Vladimir Oleynik <dzo@simtreas.ru>
+
+0.9.9 (pending)
++ Fixed a little endian problem in mton (Bastian Blank <waldi@debian.org>)
++ Fixed a arpping alignment problem (Rui He <rhe@3eti.com>)
++ Added sanity check for max_leases (udhcp bug #1285) (me)
++ Finally got rid of the trailing space in enviromental vars (me)
++ added an new enviromental variable: $mask. It contains the number
+ of subnet bits for tools like ip route that require it.
+ (Bastian Blank <waldi@debian.org>, me)
+
+0.9.8 (021031)
++ split up README files (me)
++ use /dev/urandom to seed xid's (instead of time(0)) (me)
++ fixed renew behavior (me)
++ udhcp now fits nicely into busybox
+ (Glenn McGrath <bug1@optushome.com.au> as well as myself)
++ updated client manpage (me)
++ both client and server now use sockets for signal handling,
+ hopefully, this will be the last needed change in signal
+ handling, I'm fairly certain all the possible races are now
+ closed. (me)
++ The server now restarts the auto_time timer when it receives
+ a SIGUSR1 (write out config file). (me)
++ Improve signal handling (David Poole)
++ Fix to config file parsing (Matt Kraai)
++ Fix load lease logic (me)
++ Fix clear_lease logic (me)
++ -h is now an alias for -H (udhcp bug #1253)
++ Shorter timeout on not receiving offers (me)
++ Improved signal behavior by client (me)
++ Would never assign end address (Keith Smith <keith@ksmith.com>)
++ Was improperly reporting yiaddr as siaddr (ben-udhcp@bdlow.net)
+ udhcp bug#1256
++ Fixed reading of client id (David Poole <davep@portsmith.com>)
++ change sys_errlist[] to strerror() as it aparently doesn't exist
+ (Erik Andersen <andersen@codepoet.org>)
++ fixed get_raw_packet so it returns -2 on non fatal errors
+ (Ted Lemon <Ted.Lemon@nominum.com>)
++ Improved (hopefully) NAKing behavior (me)
++ Added -b option (Jouni Malinen)
++ Compute checksums correctly on big endian hosts
+ (Jouni Malinen <jkmaline@cc.hut.fi>)
+
+0.9.7 (020526)
++ Use add_lease in read_leases, sanitizes leases more, and clears out exprired
+ ones if there is no more room (me)
++ Moved udhcpd.leases to /var/lib/misc/udhcpd.leases (Debian bug #147747)
++ Change (obsolete) AF_INET in arping.c to PF_PACKET (Debian bug #127049)
++ Added script hook for DHCPNAK (nak), as well as providing the message option
+ (me)
++ Generate the paramaters request list by seeing what options in options.c are
+ ored with OPTION_REQ in options.c
++ Fix dhcp renew forgetfullness on client (bug #1230)
++ Fix dhcp release bug on client (bug #1231)
++ Set option request list for DHCP renew (bug #1233)
++ Set BOOTREQUEST/REPLY properly
++ Change client-identifier field to popularly expected behavior (me)
++ Only reopen port on errors (me)
++ Change fork/close/setsid structures to daemon() (me)
++ Allow user to specify udhcpd config file at run time (Steven, me)
++ Write pidfile after changing it (Steven CTR Carr <Steven.CTR.Carr@tc.faa.gov>)
++ Added env var docs to udhcpc man page (Matt)
++ Standardized lowercase udhcp in documentation (me)
++ Accept packets without a UDP checksum (me)
++ Accept packets with extra garbage (me)
++ Better error handling in files.c (me)
++ Combined read_interface function to reduce COMBINED_BINARY size (me)
++ Drop calc_length(), some servers choke on smaller packets (me)
++ Try to clean some fat out (me)
+
+0.9.6 (011001)
++ Added bootp paramaters to server (me)
++ Added bootp paramaters to client (me)
++ Added vendor id to client (me)
++ Better pidfile handling in client and server (me)
++ Added man pages (Matt Kraai <kraai@alumni.carnegiemellon.edu>)
+
+0.9.5 (010914)
++ Fixed $HOME and $PATH env passing (me)
++ Fixed client to only listen for raw packets on correct interface (me)
++ added --quit,-q option to quit after a lease is obtained (me)
++ Fixed 100% CPU utilization by client when interface is down (me)
+
+0.9.4 (010827)
++ Force broadcast to broken clients that request unicast (ie, MSFT 98)
++ Make install rules (Adam J. Richter <adam@yggdrasil.com>)
++ One scripts, instead of many (Adam)
++ Removed script paramater info files (env vars only) (Adam)
++ Controlling of forking behavior in client (Adam)
++ General script.c/dhcpc.c cleanups (Adam)
+
+0.9.3 (010820)
++ Increased debugging verbosity (me)
++ Cut trailing whitespace when reading config file (me)
++ added hostname option to client (me)
++ fixed a strncpy bug in script.c (me)
++ fixed a leaky socket in dhcpc.c (me)
++ fixed a leaky socket in dhcpd.c (me)
+
+0.9.2 (010810)
++ Added raw sockets to client (me)
++ alignment fixes (Mark Huang)
++ compiler warning fixes (Mark Huang)
++ client now sends parameter list (Mark Huang/me)
++ added ipttl option
++ Does now not request broadcast packets
+
+0.9.1 (010806)
++ Added udhcpc client
++ reorganized functions/files
++ listening socket now only binds to one interface
+
+0.9.0 (010720) Major rewrite, current changes, goals:
++ should not segfault on bogus packets.
++ Options can be read from sname and file fields.
++ supports all DHCP messages (release, decline, inform).
++ IP block is now specified by a range of IP's.
++ Leases file now contains lease time (relative, or absolute).
++ Just about any DHCP option is now supported.
++ DNS entries are no longer read from resolv.conf
++ Lease file can be written periodically when the process receives a SIGUSR1
++ arpping should be supported on all arches.
++ support for DHCP relays.
++ DHCP messages can be unicast if the client requests it.
++ many, many, many other things.
+
+0.8.29 (000323)
++ stable(?) release
+
+
+0.8.28 (000323)
++ removed alarm as it was causing server to go down
++ removed debugging
++ break down dhcpd.c into manageable files
+
+
+0.8.27 (000221)
++ OFFER also sends gateway/subnet (for picky dhcp clients)
++ multiple DNS now handled from resolv.conf if available
++ multiple WINS (from dhcpd.conf)
+
+0.8.25 (000120)
++ now compiles *and* runs on a generic linux system
+ tested with a windows 98 client and the sample config
+ files in the samples directory.
+
+0.8.24 (000117)
++ makeiplist tool has basic functionality in place
++ new sample config files
++ route add -host 255.255.255.255 dev eth0 added for generic linux
+
+0.8.23 (000117)
++ NETtel specific fix for ignoring dhcp requests on 2nd interface
+
+0.8.22 (000113)
++ minor changes to compile under a generic linux system
++ minor config file location changes for a generic linux system
++ makeiplist fixes.. still incomplete.. but etting closer
+
+0.8.21 (000113)
++ now sends the correct server ip instead of hardcoded value
++ minor debugging fixes for critical messages
+
+0.8.20 (000106)
++ cut out dhcp server checking.. this was causing dialout ppp
+ sessions with idle time set to never time out.
++ also removed the 10 second pause before launching.. as this
+ was originally to stop it replying to a dhcp client
+ on a NETtel which was really a bad way to do it in the
+ first place :-)
+
+0.8.19 (000104)
++ fixes for route add -host on a machine that needs to run both
+ a DHCP client and server (dual eth box)
+
+0.8.18 (991220)
+
++ Race conditions fixed by disabling alarm whilst the server is busy
++ Fixed continous clearing of the offered array so that it is only cleared
+ when it is dirty - (could change the position of when dirty is set)
+
+0.8.17 (991212)
+
+- has problems clearing out the offered array
+
+0.8.16 (991203)
++ Non blocking error is changes to informational as it is not really
+ an error
+
+0.8.15 (991129)
++ Servs the dns field 3 times (Nettel only) so that windows servers
+ dont time out whilst nettel is booting
+
+0.8.14 (991126)
++ added owner check for the offered array so clean out time may be
+ increased
++ added new func to print out chadder/MAC
+
+0.8.13 (991125)
++ added win95 support (w95 changed xid halfway through conversation)
++ had to change the offered array to use hardware addresses instead of xid
++ fixed re offered bug
++ added more debugging
+
+0.8.12 (991111)
++ debugging was real bad.. cleaned up a bit.. needs overhaul
+
+
+0.8.11 (991110)
++ fixed up offeredAddr array to actually be used now!! offeredAddr is
+ used to see if another simultaneous connecting client was offered
+ an address that we are about to offer another client (multiple
+ client bug)
++ removed re_offered variable as it breaks multiple client support
++ added lease time to ACK -- doesn't work if in OFFER
++ decreased internal array clear delay to 60 seconds
++ minor findAddr bug (returning -1 instead of 0)
++ if clients xid already in offeredAddr offer the same addr and don't add a
+ new addr to offered (caused by a client issuing multiple DISCOVERs)
+
+0.8.10 (991105)
++ \n bug in arpping
++ minor debugging changes (removed printfs etc)
++ started browseiplist (not finished)
+
+0.8.9 (19991105)
++ fixed options array size bug (options were cut off)
+
+0.8.8 (19991105)
++ ignores requests from dhcpcd on the same machine
+
+0.8.7 (19991104)
++ don't die if we can't bind to search for existing DHCP server
++ slightly more verbose syslogging
+
+0.8.6 (19991103)
++ added makeiplist (not finished -- core dumps)
++ minor debug changes
+
+0.8.5 (19991029)
++ exits if another DHCP server is already on the network
++ added Linux Makefile
+
+0.8.4 (19991026)
++ minor bug fix in findaddr preventing an addr being found
+
+0.8.3 (19991025)
++ fixed up debugging
++ minor hwaddr issues
+
+0.8.2 (19991022)
++ free leases (new arpping code from dhcpcd)
++ fixed bug where crashes if no leases/iplist file
++ syslogging and debugging switch
++ serve DNS from resolv.conf
++ fixed bug where new lease added if same mac offered
++ now checks the ip is free b4 offering
++ now supports wins server
+
diff --git a/release/src/router/busybox/networking/udhcp/Config.in b/release/src/router/busybox/networking/udhcp/Config.in
new file mode 100755
index 00000000..a3223de9
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/Config.in
@@ -0,0 +1,62 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "udhcp Server/Client"
+
+config CONFIG_UDHCPD
+ bool "udhcp Server (udhcpd)"
+ default n
+ help
+ uDHCPd is a DHCP server geared primarily toward embedded systems,
+ while striving to be fully functional and RFC compliant.
+
+ See http://udhcp.busybox.net for further details.
+
+config CONFIG_UDHCPC
+ bool "udhcp Client (udhcpc)"
+ default n
+ help
+ uDHCPc is a DHCP client geared primarily toward embedded systems,
+ while striving to be fully functional and RFC compliant.
+
+ The udhcp client negotiates a lease with the DHCP server and
+ notifies a set of scripts when a leases is obtained or lost.
+
+ See http://udhcp.busybox.net for further details.
+
+config CONFIG_DUMPLEASES
+ bool "Lease display utility (dumpleases)"
+ default n
+ depends on CONFIG_UDHCPD
+ help
+ dumpleases displays the leases written out by the udhcpd server.
+ Lease times are stored in the file by time remaining in lease, or
+ by the absolute time that it expires in seconds from epoch.
+
+ See http://udhcp.busybox.net for further details.
+
+config CONFIG_FEATURE_UDHCP_SYSLOG
+ bool " Log udhcp messages to syslog (instead of stdout)"
+ default n
+ depends on CONFIG_UDHCPD || CONFIG_UDHCPC
+ help
+ If selected, udhcpd will log all its messages to syslog, otherwise,
+ it will attempt to log them to stdout.
+
+ See http://udhcp.busybox.net for further details.
+
+config CONFIG_FEATURE_UDHCP_DEBUG
+ bool " Compile udhcp with noisy debugging messages"
+ default n
+ depends on CONFIG_UDHCPD || CONFIG_UDHCPC
+ help
+ If selected, udhcpd will output extra debugging output. If using
+ this option, compile uDHCP with "-g", and do not fork the daemon to
+ the background.
+
+ See http://udhcp.busybox.net for further details.
+
+endmenu
+
diff --git a/release/src/router/busybox/networking/udhcp/Makefile b/release/src/router/busybox/networking/udhcp/Makefile
new file mode 100644
index 00000000..ee34d48f
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/Makefile
@@ -0,0 +1,30 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.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 of the License, 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
+#
+
+TOPDIR:= ../../
+UDHCP_DIR:=./
+include $(TOPDIR).config
+include $(TOPDIR)Rules.mak
+include Makefile.in
+all: $(libraries-y)
+-include $(TOPDIR).depend
+
+clean:
+ rm -f *.o *.a $(AR_TARGET)
+
diff --git a/release/src/router/busybox/networking/udhcp/Makefile.in b/release/src/router/busybox/networking/udhcp/Makefile.in
new file mode 100755
index 00000000..502aa1c3
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/Makefile.in
@@ -0,0 +1,61 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.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 of the License, 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
+#
+
+UDHCP_AR:=udhcp.a
+ifndef $(UDHCP_DIR)
+UDHCP_DIR:=$(TOPDIR)networking/udhcp/
+endif
+
+#ok, so I forgot how to do an or, but this is a quick and dirty hack
+ifeq ($(CONFIG_UDHCPC), y)
+CONFIG_UDHCP_SHARED=y
+else
+ifeq ($(CONFIG_UDHCPD), y)
+CONFIG_UDHCP_SHARED=y
+else
+CONFIG_UDHCP_SHARED=n
+endif
+endif
+
+ifeq ($(CONFIG_UDHCPD), y)
+CONFIG_UDHCP_LEASES_FILE=y
+else
+ifeq ($(CONFIG_UDHCPD), y)
+CONFIG_UDHCP_LEASES_FILE=y
+else
+CONFIG_UDHCP_LEASES_FILE=n
+endif
+endif
+
+UDHCP-y:=
+UDHCP-$(CONFIG_UDHCP_SHARED) += options.c socket.c packet.c common.c
+UDHCP-$(CONFIG_UDHCPC) += dhcpc.c clientpacket.c script.c
+UDHCP-$(CONFIG_UDHCPD) += dhcpd.c arpping.c files.c leases.c serverpacket.c
+UDHCP-$(CONFIG_DUMPLEASES) += dumpleases.c
+UDHCP-$(CONFIG_UDHCP_LEASES_FILE) += leases_file.c
+UDHCP_OBJS=$(patsubst %.c,$(UDHCP_DIR)%.o, $(UDHCP-y))
+
+libraries-y+=$(UDHCP_DIR)$(UDHCP_AR)
+
+$(UDHCP_DIR)$(UDHCP_AR): $(UDHCP_OBJS)
+ $(AR) -ro $@ $(UDHCP_OBJS)
+
+$(UDHCP_OBJS): %.o : %.c
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@
+
diff --git a/release/src/router/busybox/networking/udhcp/README b/release/src/router/busybox/networking/udhcp/README
new file mode 100644
index 00000000..5f4bb78a
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/README
@@ -0,0 +1,50 @@
+udhcp server/client package readme
+-------------------------
+
+The udhcp server/client package is primarily geared towards embedded
+systems. It does however, strive to be fully functional, and RFC
+compliant.
+
+
+compile time options
+-------------------
+
+The Makefile contains three of the compile time options:
+
+ DEBUG: If DEBUG is defined, udhcpd will output extra debugging
+ output, compile with -g, and not fork to the background when run.
+ SYSLOG: If SYSLOG is defined, udhcpd will log all its messages
+ syslog, otherwise, it will attempt to log them to stdout.
+
+ COMBINED_BINARY: If COMBINED_BINARY is define, one binary, udhcpd,
+ is created. If called as udhcpd, the dhcp server will be started.
+ If called as udhcpc, the dhcp client will be started.
+
+dhcpd.h contains the other two compile time options:
+
+ LEASE_TIME: The default lease time if not specified in the config
+ file.
+
+ DHCPD_CONFIG_FILE: The defualt config file to use.
+
+options.c contains a set of dhcp options for the client:
+
+ name[10]: The name of the option as it will appear in scripts
+
+ flags: The type of option, as well as if it will be requested
+ by the client (OPTION_REQ)
+
+ code: The DHCP code for this option
+
+
+busybox drop-in
+--------------
+udhcp is now a drop-in component for busybox (http://busybox.net).
+To update busybox to the latest revision, simply do a:
+
+cp *.[ch] README AUTHORS COPYING ChangeLog TODO \
+ <busybox_source>/networking/udhcp
+
+The only two files udhcp does not provide are config.in and
+Makefile.in, so these may need to be updated from time to time.
+
diff --git a/release/src/router/busybox/networking/udhcp/README.dumpleases b/release/src/router/busybox/networking/udhcp/README.dumpleases
new file mode 100755
index 00000000..6367710c
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/README.dumpleases
@@ -0,0 +1,17 @@
+udhcp lease dump (dumpleases)
+----------------------------
+
+dumpleases displays the leases written out by the udhcpd server. Lease
+times are stored in the file by time remaining in lease (for systems
+without clock that works when there is no power), or by the absolute
+time that it expires in seconds from epoch. dumpleases accepts the
+following command line options:
+
+-a, --absolute Interpret lease times as expiration time.
+-r, --remaining Interpret lease times as remaining time.
+-f, --file=FILE Read lease information from FILE.
+-h, --help Display help.
+
+Note that if udhcpd has not written a leases file recently, the output
+of may not be up to date.
+
diff --git a/release/src/router/busybox/networking/udhcp/README.udhcpc b/release/src/router/busybox/networking/udhcp/README.udhcpc
new file mode 100755
index 00000000..8aee9814
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/README.udhcpc
@@ -0,0 +1,141 @@
+udhcp client (udhcpc)
+--------------------
+
+The udhcp client negotiates a lease with the DHCP server and notifies
+a set of scripts when a leases is obtained or lost.
+
+
+command line options
+-------------------
+
+The command line options for the udhcp client are:
+
+-c, --clientid=CLIENTID Client identifier
+-H, --hostname=HOSTNAME Client hostname
+-h, Alias for -H
+-f, --foreground Do not fork after getting lease
+-b, --background Fork to background if lease cannot be
+ immediately negotiated.
+-i, --interface=INTERFACE Interface to use (default: eth0)
+-n, --now Exit with failure if lease cannot be
+ immediately negotiated.
+-p, --pidfile=file Store process ID of daemon in file
+-q, --quit Quit after obtaining lease
+-r, --request=IP IP address to request (default: none)
+-s, --script=file Run file at dhcp events (default:
+ /usr/share/udhcpc/default.script)
+-v, --version Display version
+
+
+If the requested IP address cannot be obtained, the client accepts the
+address that the server offers.
+
+
+udhcp client scripts
+-------------------
+
+When an event occurs, udhcpc calls the action script. udhcpc never does
+any configuration of the network interface itself, but instead relies on
+a set of scripts. The script by default is
+/usr/share/udhcpc/default.script but this can be changed via the command
+line arguments. The three possible arguments to the script are:
+
+ deconfig: This argument is used when udhcpc starts, and
+ when a leases is lost. The script must put the interface in an
+ up, but deconfigured state, ie: ifconfig $interface 0.0.0.0.
+
+ bound: This argument is used when udhcpc moves from an
+ unbound, to a bound state. All of the paramaters are set in
+ enviromental variables, The script should configure the interface,
+ and set any other relavent parameters (default gateway, dns server,
+ etc).
+
+ renew: This argument is used when a DHCP lease is renewed. All of
+ the paramaters are set in enviromental variables. This argument is
+ used when the interface is already configured, so the IP address,
+ will not change, however, the other DHCP paramaters, such as the
+ default gateway, subnet mask, and dns server may change.
+
+ nak: This argument is used with udhcpc receives a NAK message.
+ The script with the deconfig argument will be called directly
+ afterwards, so no changes to the network interface are neccessary.
+ This hook is provided for purely informational purposes (the
+ message option may contain a reason for the NAK).
+
+The paramaters for enviromental variables are as follows:
+
+ $HOME - The set $HOME env or "/"
+ $PATH - the set $PATH env or "/bin:/usr/bin:/sbin:/usr/sbin"
+ $1 - What action the script should perform
+ interface - The interface this was obtained on
+ ip - The obtained IP
+ mask - The number of bits in the netmask (ie: 24)
+ siaddr - The bootp next server option
+ sname - The bootp server name option
+ boot_file - The bootp boot file option
+ subnet - The assigend subnet mask
+ timezone - Offset in seconds from UTC
+ router - A list of routers
+ timesvr - A list of time servers
+ namesvr - A list of IEN 116 name servers
+ dns - A list of DNS server
+ logsvr - A list of MIT-LCS UDP log servers
+ cookiesvr - A list of RFC 865 cookie servers
+ lprsvr - A list of LPR servers
+ hostname - The assigned hostname
+ bootsize - The length in 512 octect blocks of the bootfile
+ domain - The domain name of the network
+ swapsvr - The IP address of the client's swap server
+ rootpath - The path name of the client's root disk
+ ipttl - The TTL to use for this network
+ mtu - The MTU to use for this network
+ broadcast - The broadcast address for this network
+ ntpsrv - A list of NTP servers
+ wins - A list of WINS servers
+ lease - The lease time, in seconds
+ dhcptype - DHCP message type (safely ignored)
+ serverid - The IP of the server
+ message - Reason for a DHCPNAK
+ tftp - The TFTP server name
+ bootfile - The bootfile name
+
+additional options are easily added in options.c.
+
+
+note on udhcpc's random seed
+---------------------------
+
+udhcpc will seed its random number generator (used for generating xid's)
+by reading /dev/urandom. If you have a lot of embedded systems on the same
+network, with no entropy, you can either seed /dev/urandom by a method of
+your own, or doing the following on startup:
+
+ifconfig eth0 > /dev/urandom
+
+in order to seed /dev/urandom with some data (mac address) unique to your
+system. If reading /dev/urandom fails, udhcpc will fall back to its old
+behavior of seeding with time(0).
+
+
+signals accepted by udhcpc
+-------------------------
+
+udhcpc also responds to SIGUSR1 and SIGUSR2. SIGUSR1 will force a renew state,
+and SIGUSR2 will force a release of the current lease, and cause udhcpc to
+go into an inactive state (until it is killed, or receives a SIGUSR1). You do
+not need to sleep between sending signals, as signals received are processed
+sequencially in the order they are received.
+
+
+compile time options
+-------------------
+
+options.c contains a set of dhcp options for the client:
+
+ name[10]: The name of the option as it will appear in scripts
+
+ flags: The type of option, as well as if it will be requested
+ by the client (OPTION_REQ)
+
+ code: The DHCP code for this option
+
diff --git a/release/src/router/busybox/networking/udhcp/README.udhcpd b/release/src/router/busybox/networking/udhcp/README.udhcpd
new file mode 100755
index 00000000..bc6137de
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/README.udhcpd
@@ -0,0 +1,59 @@
+udhcp server (udhcpd)
+--------------------
+
+The only command line argument to udhcpd is an optional specifed
+config file. If no config file is specified, udhcpd uses the default
+config file, /etc/udhcpd.conf. Ex:
+
+udhcpd /etc/udhcpd.eth1.conf
+
+The udhcp server employs a number of simple config files:
+
+udhcpd.leases
+------------
+
+The udhcpd.leases behavior is designed for an embedded system. The
+file is written either every auto_time seconds, or when a SIGUSR1
+is received (the auto_time timer restarts if a SIGUSR1 is received).
+If you send a SIGTERM to udhcpd directly after a SIGUSR1, udhcpd will
+finish writing the leases file and wait for the aftermentioned script
+to be executed and finish before quiting, so you do not need to sleep
+between sending signals. When the file is written, a script can be
+optionally called to commit the file to flash. Lease times are stored
+in the file by time remaining in lease (for systems without clock
+that works when there is no power), or by the absolute time that it
+expires in seconds from epoch. In the remaining format, expired leases
+are stored as zero. The file is of the format:
+
+16 byte MAC
+4 byte ip address
+u32 expire time
+16 byte MAC
+4 byte ip address
+u32 expire time
+.
+etc.
+
+example: hexdump udhcpd.leases
+
+0000000 1000 c95a 27d9 0000 0000 0000 0000 0000
+0000010 a8c0 150a 0d00 2d29 5000 23fc 8566 0000
+0000020 0000 0000 0000 0000 a8c0 140a 0d00 4e29
+0000030
+
+
+udhcpd.conf
+----------
+
+The format is fairly simple, there is a sample file with all the
+available options and comments describing them in samples/udhcpd.conf
+
+compile time options
+-------------------
+
+dhcpd.h contains the other two compile time options:
+
+ LEASE_TIME: The default lease time if not specified in the config
+ file.
+
+ DHCPD_CONFIG_FILE: The defualt config file to use.
diff --git a/release/src/router/busybox/networking/udhcp/TODO b/release/src/router/busybox/networking/udhcp/TODO
new file mode 100755
index 00000000..f88694a8
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/TODO
@@ -0,0 +1,14 @@
+TODO
+----
++ Integrade README.*'s with manpages
++ using time(0) breaks if the system clock changes, find a portable solution
++ make failure of reading functions revert to previous value, not the default
++ sanity code for option[OPT_LEN]
++ fix aliasing (ie: eth0:0)
++ better standard linux distro support
++ make sure packet generation works on a wide varitey of arches
++ Interoperability testing
++ Hooks within the DHCP server
++ Additional bootp support in client/server
++ Make serverid option in server configurable
++ Possibly add failure message to DHCP NAK
diff --git a/release/src/router/busybox/networking/udhcp/arpping.c b/release/src/router/busybox/networking/udhcp/arpping.c
new file mode 100644
index 00000000..e20395a9
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/arpping.c
@@ -0,0 +1,102 @@
+/*
+ * arpping.c
+ *
+ * Mostly stolen from: dhcpcd - DHCP client daemon
+ * by Yoichi Hariguchi <yoichi@fore.com>
+ */
+
+#include <sys/time.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/if_ether.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "dhcpd.h"
+#include "arpping.h"
+#include "common.h"
+
+/* args: yiaddr - what IP to ping
+ * ip - our ip
+ * mac - our arp address
+ * interface - interface to use
+ * retn: 1 addr free
+ * 0 addr used
+ * -1 error
+ */
+
+/* FIXME: match response against chaddr */
+int arpping(u_int32_t yiaddr, u_int32_t ip, unsigned char *mac, char *interface)
+{
+
+ int timeout = 2;
+ int optval = 1;
+ int s; /* socket */
+ int rv = 1; /* return value */
+ struct sockaddr addr; /* for interface name */
+ struct arpMsg arp;
+ fd_set fdset;
+ struct timeval tm;
+ time_t prevTime;
+
+
+ if ((s = socket (PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1) {
+ LOG(LOG_ERR, bb_msg_can_not_create_raw_socket);
+ return -1;
+ }
+
+ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) {
+ LOG(LOG_ERR, "Could not setsocketopt on raw socket");
+ close(s);
+ return -1;
+ }
+
+ /* send arp request */
+ memset(&arp, 0, sizeof(arp));
+ memcpy(arp.ethhdr.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */
+ memcpy(arp.ethhdr.h_source, mac, 6); /* MAC SA */
+ arp.ethhdr.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */
+ arp.htype = htons(ARPHRD_ETHER); /* hardware type */
+ arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */
+ arp.hlen = 6; /* hardware address length */
+ arp.plen = 4; /* protocol address length */
+ arp.operation = htons(ARPOP_REQUEST); /* ARP op code */
+ memcpy(arp.sInaddr, &ip, sizeof(ip)); /* source IP address */
+ memcpy(arp.sHaddr, mac, 6); /* source hardware address */
+ memcpy(arp.tInaddr, &yiaddr, sizeof(yiaddr)); /* target IP address */
+
+ memset(&addr, 0, sizeof(addr));
+ strcpy(addr.sa_data, interface);
+ if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0)
+ rv = 0;
+
+ /* wait arp reply, and check it */
+ tm.tv_usec = 0;
+ time(&prevTime);
+ while (timeout > 0) {
+ FD_ZERO(&fdset);
+ FD_SET(s, &fdset);
+ tm.tv_sec = timeout;
+ if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) {
+ DEBUG(LOG_ERR, "Error on ARPING request: %m");
+ if (errno != EINTR) rv = 0;
+ } else if (FD_ISSET(s, &fdset)) {
+ if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0;
+ if (arp.operation == htons(ARPOP_REPLY) &&
+ bcmp(arp.tHaddr, mac, 6) == 0 &&
+ *((u_int *) arp.sInaddr) == yiaddr) {
+ DEBUG(LOG_INFO, "Valid arp reply receved for this address");
+ rv = 0;
+ break;
+ }
+ }
+ timeout -= time(NULL) - prevTime;
+ time(&prevTime);
+ }
+ close(s);
+ DEBUG(LOG_INFO, "%salid arp replies for this address", rv ? "No v" : "V");
+ return rv;
+}
diff --git a/release/src/router/busybox/networking/udhcp/arpping.h b/release/src/router/busybox/networking/udhcp/arpping.h
new file mode 100644
index 00000000..92b828db
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/arpping.h
@@ -0,0 +1,30 @@
+/*
+ * arpping .h
+ */
+
+#ifndef ARPPING_H
+#define ARPPING_H
+
+#include <netinet/if_ether.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+struct arpMsg {
+ struct ethhdr ethhdr; /* Ethernet header */
+ u_short htype; /* hardware type (must be ARPHRD_ETHER) */
+ u_short ptype; /* protocol type (must be ETH_P_IP) */
+ u_char hlen; /* hardware address length (must be 6) */
+ u_char plen; /* protocol address length (must be 4) */
+ u_short operation; /* ARP opcode */
+ u_char sHaddr[6]; /* sender's hardware address */
+ u_char sInaddr[4]; /* sender's IP address */
+ u_char tHaddr[6]; /* target's hardware address */
+ u_char tInaddr[4]; /* target's IP address */
+ u_char pad[18]; /* pad for min. Ethernet payload (60 bytes) */
+};
+
+/* function prototypes */
+int arpping(u_int32_t yiaddr, u_int32_t ip, unsigned char *arp, char *interface);
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/clientpacket.c b/release/src/router/busybox/networking/udhcp/clientpacket.c
new file mode 100644
index 00000000..7debac2e
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/clientpacket.c
@@ -0,0 +1,247 @@
+/* clientpacket.c
+ *
+ * Packet generation and dispatching functions for the DHCP client.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <sys/socket.h>
+#include <features.h>
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+
+
+#include "dhcpd.h"
+#include "options.h"
+#include "dhcpc.h"
+#include "common.h"
+
+
+/* Create a random xid */
+unsigned long random_xid(void)
+{
+ static int initialized;
+ if (!initialized) {
+ int fd;
+ unsigned long seed;
+
+ fd = open("/dev/urandom", 0);
+ if (fd < 0 || read(fd, &seed, sizeof(seed)) < 0) {
+ LOG(LOG_WARNING, "Could not load seed from /dev/urandom: %m");
+ seed = time(0);
+ }
+ if (fd >= 0) close(fd);
+ srand(seed);
+ initialized++;
+ }
+ return rand();
+}
+
+
+/* initialize a packet with the proper defaults */
+static void init_packet(struct dhcpMessage *packet, char type)
+{
+ struct vendor {
+ char vendor, length;
+ char str[sizeof("udhcp "VERSION)];
+ } vendor_id = { DHCP_VENDOR, sizeof("udhcp "VERSION) - 1, "udhcp "VERSION};
+
+ init_header(packet, type);
+ memcpy(packet->chaddr, client_config.arp, 6);
+ add_option_string(packet->options, client_config.clientid);
+ if (client_config.hostname) add_option_string(packet->options, client_config.hostname);
+ add_option_string(packet->options, (unsigned char *) &vendor_id);
+}
+
+
+/* Add a paramater request list for stubborn DHCP servers. Pull the data
+ * from the struct in options.c. Don't do bounds checking here because it
+ * goes towards the head of the packet. */
+static void add_requests(struct dhcpMessage *packet)
+{
+ int end = end_option(packet->options);
+ int i, len = 0;
+
+ packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
+ for (i = 0; options[i].code; i++)
+ if (options[i].flags & OPTION_REQ)
+ packet->options[end + OPT_DATA + len++] = options[i].code;
+ packet->options[end + OPT_LEN] = len;
+ packet->options[end + OPT_DATA + len] = DHCP_END;
+
+}
+
+
+/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
+int send_discover(unsigned long xid, unsigned long requested)
+{
+ struct dhcpMessage packet;
+
+ init_packet(&packet, DHCPDISCOVER);
+ packet.xid = xid;
+ if (requested)
+ add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
+
+ add_requests(&packet);
+ LOG(LOG_DEBUG, "Sending discover...");
+ return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
+ SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
+}
+
+
+/* Broadcasts a DHCP request message */
+int send_selecting(unsigned long xid, unsigned long server, unsigned long requested)
+{
+ struct dhcpMessage packet;
+ struct in_addr addr;
+
+ init_packet(&packet, DHCPREQUEST);
+ packet.xid = xid;
+
+ add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
+ add_simple_option(packet.options, DHCP_SERVER_ID, server);
+
+ add_requests(&packet);
+ addr.s_addr = requested;
+ LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr));
+ return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
+ SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
+}
+
+
+/* Unicasts or broadcasts a DHCP renew message */
+int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr)
+{
+ struct dhcpMessage packet;
+ int ret = 0;
+
+ init_packet(&packet, DHCPREQUEST);
+ packet.xid = xid;
+ packet.ciaddr = ciaddr;
+
+ add_requests(&packet);
+ LOG(LOG_DEBUG, "Sending renew...");
+ if (server)
+ ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
+ else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
+ SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
+ return ret;
+}
+
+
+/* Unicasts a DHCP release message */
+int send_release(unsigned long server, unsigned long ciaddr)
+{
+ struct dhcpMessage packet;
+
+ init_packet(&packet, DHCPRELEASE);
+ packet.xid = random_xid();
+ packet.ciaddr = ciaddr;
+
+ add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr);
+ add_simple_option(packet.options, DHCP_SERVER_ID, server);
+
+ LOG(LOG_DEBUG, "Sending release...");
+ return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
+}
+
+
+/* return -1 on errors that are fatal for the socket, -2 for those that aren't */
+int get_raw_packet(struct dhcpMessage *payload, int fd)
+{
+ int bytes;
+ struct udp_dhcp_packet packet;
+ u_int32_t source, dest;
+ u_int16_t check;
+
+ memset(&packet, 0, sizeof(struct udp_dhcp_packet));
+ bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet));
+ if (bytes < 0) {
+ DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring");
+ usleep(500000); /* possible down interface, looping condition */
+ return -1;
+ }
+
+ if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) {
+ DEBUG(LOG_INFO, "message too short, ignoring");
+ return -2;
+ }
+
+ if (bytes < ntohs(packet.ip.tot_len)) {
+ DEBUG(LOG_INFO, "Truncated packet");
+ return -2;
+ }
+
+ /* ignore any extra garbage bytes */
+ bytes = ntohs(packet.ip.tot_len);
+
+ /* Make sure its the right packet for us, and that it passes sanity checks */
+ if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION ||
+ packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) ||
+ bytes > (int) sizeof(struct udp_dhcp_packet) ||
+ ntohs(packet.udp.len) != (short) (bytes - sizeof(packet.ip))) {
+ DEBUG(LOG_INFO, "unrelated/bogus packet");
+ return -2;
+ }
+
+ /* check IP checksum */
+ check = packet.ip.check;
+ packet.ip.check = 0;
+ if (check != checksum(&(packet.ip), sizeof(packet.ip))) {
+ DEBUG(LOG_INFO, "bad IP header checksum, ignoring");
+ return -1;
+ }
+
+ /* verify the UDP checksum by replacing the header with a psuedo header */
+ source = packet.ip.saddr;
+ dest = packet.ip.daddr;
+ check = packet.udp.check;
+ packet.udp.check = 0;
+ memset(&packet.ip, 0, sizeof(packet.ip));
+
+ packet.ip.protocol = IPPROTO_UDP;
+ packet.ip.saddr = source;
+ packet.ip.daddr = dest;
+ packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */
+ if (check && check != checksum(&packet, bytes)) {
+ DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring");
+ return -2;
+ }
+
+ memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
+
+ if (ntohl(payload->cookie) != DHCP_MAGIC) {
+ LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring");
+ return -2;
+ }
+ DEBUG(LOG_INFO, "oooooh!!! got some!");
+ return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
+
+}
diff --git a/release/src/router/busybox/networking/udhcp/clientpacket.h b/release/src/router/busybox/networking/udhcp/clientpacket.h
new file mode 100644
index 00000000..2a6facbc
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/clientpacket.h
@@ -0,0 +1,12 @@
+#ifndef _CLIENTPACKET_H
+#define _CLIENTPACKET_H
+
+unsigned long random_xid(void);
+int send_discover(unsigned long xid, unsigned long requested);
+int send_selecting(unsigned long xid, unsigned long server, unsigned long requested);
+int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
+int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
+int send_release(unsigned long server, unsigned long ciaddr);
+int get_raw_packet(struct dhcpMessage *payload, int fd);
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/common.c b/release/src/router/busybox/networking/udhcp/common.c
new file mode 100644
index 00000000..c01cd576
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/common.c
@@ -0,0 +1,150 @@
+/* common.c
+ *
+ * Functions to assist in the writing and removing of pidfiles.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> Soptember 2001
+ * Rewrited by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ *
+ * 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include "common.h"
+
+
+static int daemonized;
+
+#ifdef CONFIG_FEATURE_UDHCP_SYSLOG
+
+void udhcp_logging(int level, const char *fmt, ...)
+{
+ int e = errno;
+ va_list p;
+ va_list p2;
+
+ va_start(p, fmt);
+ __va_copy(p2, p);
+ if(!daemonized) {
+ vprintf(fmt, p);
+ putchar('\n');
+ errno = e;
+ }
+ vsyslog(level, fmt, p2);
+ va_end(p);
+}
+
+void start_log(const char *client_server)
+{
+ openlog(bb_applet_name, LOG_PID | LOG_CONS, LOG_LOCAL0);
+ udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
+}
+
+#else
+
+static char *syslog_level_msg[] = {
+ [LOG_EMERG] = "EMERGENCY!",
+ [LOG_ALERT] = "ALERT!",
+ [LOG_CRIT] = "critical!",
+ [LOG_WARNING] = "warning",
+ [LOG_ERR] = "error",
+ [LOG_INFO] = "info",
+ [LOG_DEBUG] = "debug"
+};
+
+void udhcp_logging(int level, const char *fmt, ...)
+{
+ int e = errno;
+ va_list p;
+
+ va_start(p, fmt);
+ if(!daemonized) {
+ printf("%s, ", syslog_level_msg[level]);
+ errno = e;
+ vprintf(fmt, p);
+ putchar('\n');
+ }
+ va_end(p);
+}
+
+void start_log(const char *client_server)
+{
+ udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
+}
+#endif
+
+static const char *saved_pidfile;
+
+static void exit_fun(void)
+{
+ if (saved_pidfile) unlink(saved_pidfile);
+}
+
+void background(const char *pidfile)
+{
+ int pid_fd = -1;
+
+ if (pidfile) {
+ pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644);
+ if (pid_fd < 0) {
+ LOG(LOG_ERR, "Unable to open pidfile %s: %m", pidfile);
+ } else {
+ lockf(pid_fd, F_LOCK, 0);
+ if(!saved_pidfile)
+ atexit(exit_fun); /* set atexit one only */
+ saved_pidfile = pidfile; /* but may be rewrite */
+ }
+ }
+ while (pid_fd >= 0 && pid_fd < 3) pid_fd = dup(pid_fd); /* don't let daemon close it */
+ if (daemon(0, 0) == -1) {
+ perror("fork");
+ exit(1);
+ }
+ daemonized++;
+ if (pid_fd >= 0) {
+ FILE *out;
+
+ if ((out = fdopen(pid_fd, "w")) != NULL) {
+ fprintf(out, "%d\n", getpid());
+ fclose(out);
+ }
+ lockf(pid_fd, F_UNLCK, 0);
+ close(pid_fd);
+ }
+}
+
+/* Signal handler */
+int udhcp_signal_pipe[2];
+static void signal_handler(int sig)
+{
+ if (send(udhcp_signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) {
+ LOG(LOG_ERR, "Could not send signal: %m");
+ }
+}
+
+void udhcp_set_signal_pipe(int sig_add)
+{
+ socketpair(AF_UNIX, SOCK_STREAM, 0, udhcp_signal_pipe);
+ signal(SIGUSR1, signal_handler);
+ signal(SIGTERM, signal_handler);
+ if(sig_add)
+ signal(sig_add, signal_handler);
+}
diff --git a/release/src/router/busybox/networking/udhcp/common.h b/release/src/router/busybox/networking/udhcp/common.h
new file mode 100644
index 00000000..768f551b
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/common.h
@@ -0,0 +1,52 @@
+/* common.h
+ *
+ * Russ Dill <Russ.Dill@asu.edu> Soptember 2001
+ * Rewrited by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ *
+ * 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "version.h"
+#include "busybox.h"
+
+#ifndef CONFIG_FEATURE_UDHCP_SYSLOG
+enum syslog_levels {
+ LOG_EMERG = 0,
+ LOG_ALERT,
+ LOG_CRIT,
+ LOG_WARNING,
+ LOG_ERR,
+ LOG_INFO,
+ LOG_DEBUG
+};
+#else
+#include <syslog.h>
+#endif
+
+void start_log(const char *client_server);
+void background(const char *pidfile);
+void udhcp_logging(int level, const char *fmt, ...);
+
+extern int udhcp_signal_pipe[2];
+void udhcp_set_signal_pipe(int sig_add);
+
+
+#define LOG(level, str, args...) udhcp_logging(level, str, ## args)
+
+#ifdef CONFIG_FEATURE_UDHCP_DEBUG
+# define DEBUG(level, str, args...) udhcp_logging(level, str, ## args)
+#else
+# define DEBUG(level, str, args...) do {;} while(0)
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/debug.h b/release/src/router/busybox/networking/udhcp/debug.h
new file mode 100644
index 00000000..a1e19741
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/debug.h
@@ -0,0 +1,41 @@
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+#include "libbb_udhcp.h"
+
+#include <stdio.h>
+#ifdef SYSLOG
+#include <syslog.h>
+#endif
+
+
+#ifdef SYSLOG
+# define LOG(level, str, args...) do { printf(str, ## args); \
+ printf("\n"); \
+ syslog(level, str, ## args); } while(0)
+# define OPEN_LOG(name) openlog(name, 0, 0)
+#define CLOSE_LOG() closelog()
+#else
+# define LOG_EMERG "EMERGENCY!"
+# define LOG_ALERT "ALERT!"
+# define LOG_CRIT "critical!"
+# define LOG_WARNING "warning"
+# define LOG_ERR "error"
+# define LOG_INFO "info"
+# define LOG_DEBUG "debug"
+# define LOG(level, str, args...) do { printf("%s, ", level); \
+ printf(str, ## args); \
+ printf("\n"); } while(0)
+# define OPEN_LOG(name) do {;} while(0)
+#define CLOSE_LOG() do {;} while(0)
+#endif
+
+#ifdef DEBUG
+# undef DEBUG
+# define DEBUG(level, str, args...) LOG(level, str, ## args)
+# define DEBUGGING
+#else
+# define DEBUG(level, str, args...) do {;} while(0)
+#endif
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/dhcpc.c b/release/src/router/busybox/networking/udhcp/dhcpc.c
new file mode 100644
index 00000000..d18cb423
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/dhcpc.c
@@ -0,0 +1,492 @@
+/* dhcpc.c
+ *
+ * udhcp DHCP client
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/time.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <time.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <errno.h>
+
+#include "dhcpd.h"
+#include "dhcpc.h"
+#include "options.h"
+#include "clientpacket.h"
+#include "script.h"
+#include "socket.h"
+#include "common.h"
+
+static int state;
+static unsigned long requested_ip; /* = 0 */
+static unsigned long server_addr;
+static unsigned long timeout;
+static int packet_num; /* = 0 */
+static int fd = -1;
+
+#define LISTEN_NONE 0
+#define LISTEN_KERNEL 1
+#define LISTEN_RAW 2
+static int listen_mode;
+
+#ifdef CONFIG_INSTALL_NO_USR
+#define DEFAULT_SCRIPT "/share/udhcpc/default.script"
+#else
+#define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script"
+#endif
+
+struct client_config_t client_config = {
+ /* Default options. */
+ abort_if_no_lease: 0,
+ foreground: 0,
+ quit_after_lease: 0,
+ background_if_no_lease: 0,
+ interface: "eth0",
+ pidfile: NULL,
+ script: DEFAULT_SCRIPT,
+ clientid: NULL,
+ hostname: NULL,
+ ifindex: 0,
+ arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */
+};
+
+/* just a little helper */
+static void change_mode(int new_mode)
+{
+ DEBUG(LOG_INFO, "entering %s listen mode",
+ new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
+ if (fd >= 0) close(fd);
+ fd = -1;
+ listen_mode = new_mode;
+}
+
+
+/* perform a renew */
+static void perform_renew(void)
+{
+ LOG(LOG_INFO, "Performing a DHCP renew");
+ switch (state) {
+ case BOUND:
+ change_mode(LISTEN_KERNEL);
+ case RENEWING:
+ case REBINDING:
+ state = RENEW_REQUESTED;
+ break;
+ case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
+ run_script(NULL, "deconfig");
+ case REQUESTING:
+ case RELEASED:
+ change_mode(LISTEN_RAW);
+ state = INIT_SELECTING;
+ break;
+ case INIT_SELECTING:
+ break;
+ }
+
+ /* start things over */
+ packet_num = 0;
+
+ /* Kill any timeouts because the user wants this to hurry along */
+ timeout = 0;
+}
+
+
+/* perform a release */
+static void perform_release(void)
+{
+ char buffer[16];
+ struct in_addr temp_addr;
+
+ /* send release packet */
+ if (state == BOUND || state == RENEWING || state == REBINDING) {
+ temp_addr.s_addr = server_addr;
+ sprintf(buffer, "%s", inet_ntoa(temp_addr));
+ temp_addr.s_addr = requested_ip;
+ LOG(LOG_INFO, "Unicasting a release of %s to %s",
+ inet_ntoa(temp_addr), buffer);
+ send_release(server_addr, requested_ip); /* unicast */
+ run_script(NULL, "deconfig");
+ }
+ LOG(LOG_INFO, "Entering released state");
+
+ change_mode(LISTEN_NONE);
+ state = RELEASED;
+ timeout = 0x7fffffff;
+}
+
+
+static void client_background(void)
+{
+ background(client_config.pidfile);
+ client_config.foreground = 1; /* Do not fork again. */
+ client_config.background_if_no_lease = 0;
+}
+
+
+int udhcpc_main(int argc, char *argv[])
+{
+ unsigned char *temp, *message;
+ unsigned long t1 = 0, t2 = 0, xid = 0;
+ unsigned long start = 0, lease;
+ fd_set rfds;
+ int retval;
+ struct timeval tv;
+ int c, len;
+ struct dhcpMessage packet;
+ struct in_addr temp_addr;
+ time_t now;
+ int max_fd;
+ int sig;
+
+ static const struct option arg_options[] = {
+ {"clientid", required_argument, 0, 'c'},
+ {"foreground", no_argument, 0, 'f'},
+ {"background", no_argument, 0, 'b'},
+ {"hostname", required_argument, 0, 'H'},
+ {"hostname", required_argument, 0, 'h'},
+ {"interface", required_argument, 0, 'i'},
+ {"now", no_argument, 0, 'n'},
+ {"pidfile", required_argument, 0, 'p'},
+ {"quit", no_argument, 0, 'q'},
+ {"request", required_argument, 0, 'r'},
+ {"script", required_argument, 0, 's'},
+ {"version", no_argument, 0, 'v'},
+ {0, 0, 0, 0}
+ };
+
+ /* get options */
+ while (1) {
+ int option_index = 0;
+ c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index);
+ if (c == -1) break;
+
+ switch (c) {
+ case 'c':
+ len = strlen(optarg) > 255 ? 255 : strlen(optarg);
+ if (client_config.clientid) free(client_config.clientid);
+ client_config.clientid = xmalloc(len + 2);
+ client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
+ client_config.clientid[OPT_LEN] = len;
+ client_config.clientid[OPT_DATA] = '\0';
+ strncpy(client_config.clientid + OPT_DATA, optarg, len);
+ break;
+ case 'f':
+ client_config.foreground = 1;
+ break;
+ case 'b':
+ client_config.background_if_no_lease = 1;
+ break;
+ case 'h':
+ case 'H':
+ len = strlen(optarg) > 255 ? 255 : strlen(optarg);
+ if (client_config.hostname) free(client_config.hostname);
+ client_config.hostname = xmalloc(len + 2);
+ client_config.hostname[OPT_CODE] = DHCP_HOST_NAME;
+ client_config.hostname[OPT_LEN] = len;
+ strncpy(client_config.hostname + 2, optarg, len);
+ break;
+ case 'i':
+ client_config.interface = optarg;
+ break;
+ case 'n':
+ client_config.abort_if_no_lease = 1;
+ break;
+ case 'p':
+ client_config.pidfile = optarg;
+ break;
+ case 'q':
+ client_config.quit_after_lease = 1;
+ break;
+ case 'r':
+ requested_ip = inet_addr(optarg);
+ break;
+ case 's':
+ client_config.script = optarg;
+ break;
+ case 'v':
+ bb_error_msg("version %s\n", VERSION);
+ return(0);
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+
+ start_log("client");
+ if (read_interface(client_config.interface, &client_config.ifindex,
+ NULL, client_config.arp) < 0)
+ return(1);
+
+ if (!client_config.clientid) {
+ client_config.clientid = xmalloc(6 + 3);
+ client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
+ client_config.clientid[OPT_LEN] = 7;
+ client_config.clientid[OPT_DATA] = 1;
+ memcpy(client_config.clientid + 3, client_config.arp, 6);
+ }
+
+ /* setup signal handlers */
+ udhcp_set_signal_pipe(SIGUSR2);
+
+ state = INIT_SELECTING;
+ run_script(NULL, "deconfig");
+ change_mode(LISTEN_RAW);
+
+ for (;;) {
+
+ tv.tv_sec = timeout - time(0);
+ tv.tv_usec = 0;
+ FD_ZERO(&rfds);
+
+ if (listen_mode != LISTEN_NONE && fd < 0) {
+ if (listen_mode == LISTEN_KERNEL)
+ fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface);
+ else
+ fd = raw_socket(client_config.ifindex);
+ if (fd < 0) {
+ LOG(LOG_ERR, "FATAL: couldn't listen on socket, %m");
+ return(0);
+ }
+ }
+ if (fd >= 0) FD_SET(fd, &rfds);
+ FD_SET(udhcp_signal_pipe[0], &rfds);
+
+ if (tv.tv_sec > 0) {
+ DEBUG(LOG_INFO, "Waiting on select...\n");
+ max_fd = udhcp_signal_pipe[0] > fd ? udhcp_signal_pipe[0] : fd;
+ retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
+ } else retval = 0; /* If we already timed out, fall through */
+
+ now = time(0);
+ if (retval == 0) {
+ /* timeout dropped to zero */
+ switch (state) {
+ case INIT_SELECTING:
+ if (packet_num < 3) {
+ if (packet_num == 0)
+ xid = random_xid();
+
+ /* send discover packet */
+ send_discover(xid, requested_ip); /* broadcast */
+
+ timeout = now + ((packet_num == 2) ? 4 : 2);
+ packet_num++;
+ } else {
+ if (client_config.background_if_no_lease) {
+ LOG(LOG_INFO, "No lease, forking to background.");
+ client_background();
+ } else if (client_config.abort_if_no_lease) {
+ LOG(LOG_INFO, "No lease, failing.");
+ return(1);
+ }
+ /* wait to try again */
+ packet_num = 0;
+ timeout = now + 60;
+ }
+ break;
+ case RENEW_REQUESTED:
+ case REQUESTING:
+ if (packet_num < 3) {
+ /* send request packet */
+ if (state == RENEW_REQUESTED)
+ send_renew(xid, server_addr, requested_ip); /* unicast */
+ else send_selecting(xid, server_addr, requested_ip); /* broadcast */
+
+ timeout = now + ((packet_num == 2) ? 10 : 2);
+ packet_num++;
+ } else {
+ /* timed out, go back to init state */
+ if (state == RENEW_REQUESTED) run_script(NULL, "deconfig");
+ state = INIT_SELECTING;
+ timeout = now;
+ packet_num = 0;
+ change_mode(LISTEN_RAW);
+ }
+ break;
+ case BOUND:
+ /* Lease is starting to run out, time to enter renewing state */
+ state = RENEWING;
+ change_mode(LISTEN_KERNEL);
+ DEBUG(LOG_INFO, "Entering renew state");
+ /* fall right through */
+ case RENEWING:
+ /* Either set a new T1, or enter REBINDING state */
+ if ((t2 - t1) <= (lease / 14400 + 1)) {
+ /* timed out, enter rebinding state */
+ state = REBINDING;
+ timeout = now + (t2 - t1);
+ DEBUG(LOG_INFO, "Entering rebinding state");
+ } else {
+ /* send a request packet */
+ send_renew(xid, server_addr, requested_ip); /* unicast */
+
+ t1 = (t2 - t1) / 2 + t1;
+ timeout = t1 + start;
+ }
+ break;
+ case REBINDING:
+ /* Either set a new T2, or enter INIT state */
+ if ((lease - t2) <= (lease / 14400 + 1)) {
+ /* timed out, enter init state */
+ state = INIT_SELECTING;
+ LOG(LOG_INFO, "Lease lost, entering init state");
+ run_script(NULL, "deconfig");
+ timeout = now;
+ packet_num = 0;
+ change_mode(LISTEN_RAW);
+ } else {
+ /* send a request packet */
+ send_renew(xid, 0, requested_ip); /* broadcast */
+
+ t2 = (lease - t2) / 2 + t2;
+ timeout = t2 + start;
+ }
+ break;
+ case RELEASED:
+ /* yah, I know, *you* say it would never happen */
+ timeout = 0x7fffffff;
+ break;
+ }
+ } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
+ /* a packet is ready, read it */
+
+ if (listen_mode == LISTEN_KERNEL)
+ len = get_packet(&packet, fd);
+ else len = get_raw_packet(&packet, fd);
+
+ if (len == -1 && errno != EINTR) {
+ DEBUG(LOG_INFO, "error on read, %m, reopening socket");
+ change_mode(listen_mode); /* just close and reopen */
+ }
+ if (len < 0) continue;
+
+ if (packet.xid != xid) {
+ DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
+ (unsigned long) packet.xid, xid);
+ continue;
+ }
+
+ if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
+ DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
+ continue;
+ }
+
+ switch (state) {
+ case INIT_SELECTING:
+ /* Must be a DHCPOFFER to one of our xid's */
+ if (*message == DHCPOFFER) {
+ if ((temp = get_option(&packet, DHCP_SERVER_ID))) {
+ memcpy(&server_addr, temp, 4);
+ xid = packet.xid;
+ requested_ip = packet.yiaddr;
+
+ /* enter requesting state */
+ state = REQUESTING;
+ timeout = now;
+ packet_num = 0;
+ } else {
+ DEBUG(LOG_ERR, "No server ID in message");
+ }
+ }
+ break;
+ case RENEW_REQUESTED:
+ case REQUESTING:
+ case RENEWING:
+ case REBINDING:
+ if (*message == DHCPACK) {
+ if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) {
+ LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease");
+ lease = 60 * 60;
+ } else {
+ memcpy(&lease, temp, 4);
+ lease = ntohl(lease);
+ }
+
+ /* enter bound state */
+ t1 = lease / 2;
+
+ /* little fixed point for n * .875 */
+ t2 = (lease * 0x7) >> 3;
+ temp_addr.s_addr = packet.yiaddr;
+ LOG(LOG_INFO, "Lease of %s obtained, lease time %ld",
+ inet_ntoa(temp_addr), lease);
+ start = now;
+ timeout = t1 + start;
+ requested_ip = packet.yiaddr;
+ run_script(&packet,
+ ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
+
+ state = BOUND;
+ change_mode(LISTEN_NONE);
+ if (client_config.quit_after_lease)
+ return(0);
+ if (!client_config.foreground)
+ client_background();
+
+ } else if (*message == DHCPNAK) {
+ /* return to init state */
+ LOG(LOG_INFO, "Received DHCP NAK");
+ run_script(&packet, "nak");
+ if (state != REQUESTING)
+ run_script(NULL, "deconfig");
+ state = INIT_SELECTING;
+ timeout = now;
+ requested_ip = 0;
+ packet_num = 0;
+ change_mode(LISTEN_RAW);
+ sleep(3); /* avoid excessive network traffic */
+ }
+ break;
+ /* case BOUND, RELEASED: - ignore all packets */
+ }
+ } else if (retval > 0 && FD_ISSET(udhcp_signal_pipe[0], &rfds)) {
+ if (read(udhcp_signal_pipe[0], &sig, sizeof(sig)) < 0) {
+ DEBUG(LOG_ERR, "Could not read signal: %m");
+ continue; /* probably just EINTR */
+ }
+ switch (sig) {
+ case SIGUSR1:
+ perform_renew();
+ break;
+ case SIGUSR2:
+ perform_release();
+ break;
+ case SIGTERM:
+ LOG(LOG_INFO, "Received SIGTERM");
+ return(0);
+ }
+ } else if (retval == -1 && errno == EINTR) {
+ /* a signal was caught */
+ } else {
+ /* An error occured */
+ DEBUG(LOG_ERR, "Error on select");
+ }
+
+ }
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/udhcp/dhcpc.h b/release/src/router/busybox/networking/udhcp/dhcpc.h
new file mode 100644
index 00000000..e23d9d4f
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/dhcpc.h
@@ -0,0 +1,32 @@
+/* dhcpc.h */
+#ifndef _DHCPC_H
+#define _DHCPC_H
+
+#define INIT_SELECTING 0
+#define REQUESTING 1
+#define BOUND 2
+#define RENEWING 3
+#define REBINDING 4
+#define INIT_REBOOT 5
+#define RENEW_REQUESTED 6
+#define RELEASED 7
+
+
+struct client_config_t {
+ char foreground; /* Do not fork */
+ char quit_after_lease; /* Quit after obtaining lease */
+ char abort_if_no_lease; /* Abort if no lease */
+ char background_if_no_lease; /* Fork to background if no lease */
+ char *interface; /* The name of the interface to use */
+ char *pidfile; /* Optionally store the process ID */
+ char *script; /* User script to run at dhcp events */
+ unsigned char *clientid; /* Optional client id to use */
+ unsigned char *hostname; /* Optional hostname to use */
+ int ifindex; /* Index number of the interface to use */
+ unsigned char arp[6]; /* Our arp address */
+};
+
+extern struct client_config_t client_config;
+
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/dhcpd.c b/release/src/router/busybox/networking/udhcp/dhcpd.c
new file mode 100644
index 00000000..c21cb72a
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/dhcpd.c
@@ -0,0 +1,253 @@
+/* dhcpd.c
+ *
+ * udhcp Server
+ * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
+ * Chris Trew <ctrew@moreton.com.au>
+ *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "dhcpd.h"
+#include "arpping.h"
+#include "socket.h"
+#include "options.h"
+#include "files.h"
+#include "serverpacket.h"
+#include "common.h"
+
+
+/* globals */
+struct dhcpOfferedAddr *leases;
+struct server_config_t server_config;
+
+
+int udhcpd_main(int argc, char *argv[])
+{
+ fd_set rfds;
+ struct timeval tv;
+ int server_socket = -1;
+ int bytes, retval;
+ struct dhcpMessage packet;
+ unsigned char *state;
+ unsigned char *server_id, *requested;
+ u_int32_t server_id_align, requested_align;
+ unsigned long timeout_end;
+ struct option_set *option;
+ struct dhcpOfferedAddr *lease;
+ int max_sock;
+ int sig;
+ unsigned long num_ips;
+
+ start_log("server");
+
+ memset(&server_config, 0, sizeof(struct server_config_t));
+
+ if (argc < 2)
+ read_config(DHCPD_CONF_FILE);
+ else read_config(argv[1]);
+
+ if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) {
+ memcpy(&server_config.lease, option->data + 2, 4);
+ server_config.lease = ntohl(server_config.lease);
+ }
+ else server_config.lease = LEASE_TIME;
+
+ /* Sanity check */
+ num_ips = ntohl(server_config.end) - ntohl(server_config.start);
+ if (server_config.max_leases > num_ips) {
+ LOG(LOG_ERR, "max_leases value (%lu) not sane, "
+ "setting to %lu instead",
+ server_config.max_leases, num_ips);
+ server_config.max_leases = num_ips;
+ }
+
+ leases = xcalloc(sizeof(struct dhcpOfferedAddr), server_config.max_leases);
+ read_leases(server_config.lease_file);
+
+ if (read_interface(server_config.interface, &server_config.ifindex,
+ &server_config.server, server_config.arp) < 0)
+ return(1);
+
+#ifndef CONFIG_FEATURE_UDHCP_DEBUG
+ background(server_config.pidfile);
+#endif
+
+ udhcp_set_signal_pipe(0);
+
+ timeout_end = time(0) + server_config.auto_time;
+ while(1) { /* loop until universe collapses */
+
+ if (server_socket < 0)
+ if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) {
+ LOG(LOG_ERR, "FATAL: couldn't create server socket, %m");
+ return(2);
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(server_socket, &rfds);
+ FD_SET(udhcp_signal_pipe[0], &rfds);
+ if (server_config.auto_time) {
+ tv.tv_sec = timeout_end - time(0);
+ tv.tv_usec = 0;
+ }
+ if (!server_config.auto_time || tv.tv_sec > 0) {
+ max_sock = server_socket > udhcp_signal_pipe[0] ? server_socket : udhcp_signal_pipe[0];
+ retval = select(max_sock + 1, &rfds, NULL, NULL,
+ server_config.auto_time ? &tv : NULL);
+ } else retval = 0; /* If we already timed out, fall through */
+
+ if (retval == 0) {
+ write_leases();
+ timeout_end = time(0) + server_config.auto_time;
+ continue;
+ } else if (retval < 0 && errno != EINTR) {
+ DEBUG(LOG_INFO, "error on select");
+ continue;
+ }
+
+ if (FD_ISSET(udhcp_signal_pipe[0], &rfds)) {
+ if (read(udhcp_signal_pipe[0], &sig, sizeof(sig)) < 0)
+ continue; /* probably just EINTR */
+ switch (sig) {
+ case SIGUSR1:
+ LOG(LOG_INFO, "Received a SIGUSR1");
+ write_leases();
+ /* why not just reset the timeout, eh */
+ timeout_end = time(0) + server_config.auto_time;
+ continue;
+ case SIGTERM:
+ LOG(LOG_INFO, "Received a SIGTERM");
+ return(0);
+ }
+ }
+
+ if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */
+ if (bytes == -1 && errno != EINTR) {
+ DEBUG(LOG_INFO, "error on read, %m, reopening socket");
+ close(server_socket);
+ server_socket = -1;
+ }
+ continue;
+ }
+
+ if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
+ DEBUG(LOG_ERR, "couldn't get option from packet, ignoring");
+ continue;
+ }
+
+ /* ADDME: look for a static lease */
+ lease = find_lease_by_chaddr(packet.chaddr);
+ switch (state[0]) {
+ case DHCPDISCOVER:
+ DEBUG(LOG_INFO,"received DISCOVER");
+
+ if (sendOffer(&packet) < 0) {
+ LOG(LOG_ERR, "send OFFER failed");
+ }
+ break;
+ case DHCPREQUEST:
+ DEBUG(LOG_INFO, "received REQUEST");
+
+ requested = get_option(&packet, DHCP_REQUESTED_IP);
+ server_id = get_option(&packet, DHCP_SERVER_ID);
+
+ if (requested) memcpy(&requested_align, requested, 4);
+ if (server_id) memcpy(&server_id_align, server_id, 4);
+
+ if (lease) { /*ADDME: or static lease */
+ if (server_id) {
+ /* SELECTING State */
+ DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align));
+ if (server_id_align == server_config.server && requested &&
+ requested_align == lease->yiaddr) {
+ sendACK(&packet, lease->yiaddr);
+ }
+ } else {
+ if (requested) {
+ /* INIT-REBOOT State */
+ if (lease->yiaddr == requested_align)
+ sendACK(&packet, lease->yiaddr);
+ else sendNAK(&packet);
+ } else {
+ /* RENEWING or REBINDING State */
+ if (lease->yiaddr == packet.ciaddr)
+ sendACK(&packet, lease->yiaddr);
+ else {
+ /* don't know what to do!!!! */
+ sendNAK(&packet);
+ }
+ }
+ }
+
+ /* what to do if we have no record of the client */
+ } else if (server_id) {
+ /* SELECTING State */
+
+ } else if (requested) {
+ /* INIT-REBOOT State */
+ if ((lease = find_lease_by_yiaddr(requested_align))) {
+ if (lease_expired(lease)) {
+ /* probably best if we drop this lease */
+ memset(lease->chaddr, 0, 16);
+ /* make some contention for this address */
+ } else sendNAK(&packet);
+ } else if (requested_align < server_config.start ||
+ requested_align > server_config.end) {
+ sendNAK(&packet);
+ } /* else remain silent */
+
+ } else {
+ /* RENEWING or REBINDING State */
+ }
+ break;
+ case DHCPDECLINE:
+ DEBUG(LOG_INFO,"received DECLINE");
+ if (lease) {
+ memset(lease->chaddr, 0, 16);
+ lease->expires = time(0) + server_config.decline_time;
+ }
+ break;
+ case DHCPRELEASE:
+ DEBUG(LOG_INFO,"received RELEASE");
+ if (lease) lease->expires = time(0);
+ break;
+ case DHCPINFORM:
+ DEBUG(LOG_INFO,"received INFORM");
+ send_inform(&packet);
+ break;
+ default:
+ LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]);
+ }
+ }
+
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/udhcp/dhcpd.h b/release/src/router/busybox/networking/udhcp/dhcpd.h
new file mode 100644
index 00000000..e219143c
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/dhcpd.h
@@ -0,0 +1,130 @@
+/* dhcpd.h */
+#ifndef _DHCPD_H
+#define _DHCPD_H
+
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "leases.h"
+
+/************************************/
+/* Defaults _you_ may want to tweak */
+/************************************/
+
+/* the period of time the client is allowed to use that address */
+#define LEASE_TIME (60*60*24*10) /* 10 days of seconds */
+
+/* where to find the DHCP server configuration file */
+#define DHCPD_CONF_FILE "/etc/udhcpd.conf"
+
+/*****************************************************************/
+/* Do not modify below here unless you know what you are doing!! */
+/*****************************************************************/
+
+/* DHCP protocol -- see RFC 2131 */
+#define SERVER_PORT 67
+#define CLIENT_PORT 68
+
+#define DHCP_MAGIC 0x63825363
+
+/* DHCP option codes (partial list) */
+#define DHCP_PADDING 0x00
+#define DHCP_SUBNET 0x01
+#define DHCP_TIME_OFFSET 0x02
+#define DHCP_ROUTER 0x03
+#define DHCP_TIME_SERVER 0x04
+#define DHCP_NAME_SERVER 0x05
+#define DHCP_DNS_SERVER 0x06
+#define DHCP_LOG_SERVER 0x07
+#define DHCP_COOKIE_SERVER 0x08
+#define DHCP_LPR_SERVER 0x09
+#define DHCP_HOST_NAME 0x0c
+#define DHCP_BOOT_SIZE 0x0d
+#define DHCP_DOMAIN_NAME 0x0f
+#define DHCP_SWAP_SERVER 0x10
+#define DHCP_ROOT_PATH 0x11
+#define DHCP_IP_TTL 0x17
+#define DHCP_MTU 0x1a
+#define DHCP_BROADCAST 0x1c
+#define DHCP_NTP_SERVER 0x2a
+#define DHCP_WINS_SERVER 0x2c
+#define DHCP_REQUESTED_IP 0x32
+#define DHCP_LEASE_TIME 0x33
+#define DHCP_OPTION_OVER 0x34
+#define DHCP_MESSAGE_TYPE 0x35
+#define DHCP_SERVER_ID 0x36
+#define DHCP_PARAM_REQ 0x37
+#define DHCP_MESSAGE 0x38
+#define DHCP_MAX_SIZE 0x39
+#define DHCP_T1 0x3a
+#define DHCP_T2 0x3b
+#define DHCP_VENDOR 0x3c
+#define DHCP_CLIENT_ID 0x3d
+
+#define DHCP_END 0xFF
+
+
+#define BOOTREQUEST 1
+#define BOOTREPLY 2
+
+#define ETH_10MB 1
+#define ETH_10MB_LEN 6
+
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+#define BROADCAST_FLAG 0x8000
+
+#define OPTION_FIELD 0
+#define FILE_FIELD 1
+#define SNAME_FIELD 2
+
+/* miscellaneous defines */
+#define MAC_BCAST_ADDR (unsigned char *) "\xff\xff\xff\xff\xff\xff"
+#define OPT_CODE 0
+#define OPT_LEN 1
+#define OPT_DATA 2
+
+struct option_set {
+ unsigned char *data;
+ struct option_set *next;
+};
+
+struct server_config_t {
+ u_int32_t server; /* Our IP, in network order */
+ u_int32_t start; /* Start address of leases, network order */
+ u_int32_t end; /* End of leases, network order */
+ struct option_set *options; /* List of DHCP options loaded from the config file */
+ char *interface; /* The name of the interface to use */
+ int ifindex; /* Index number of the interface to use */
+ unsigned char arp[6]; /* Our arp address */
+ unsigned long lease; /* lease time in seconds (host order) */
+ unsigned long max_leases; /* maximum number of leases (including reserved address) */
+ char remaining; /* should the lease file be interpreted as lease time remaining, or
+ * as the time the lease expires */
+ unsigned long auto_time; /* how long should udhcpd wait before writing a config file.
+ * if this is zero, it will only write one on SIGUSR1 */
+ unsigned long decline_time; /* how long an address is reserved if a client returns a
+ * decline message */
+ unsigned long conflict_time; /* how long an arp conflict offender is leased for */
+ unsigned long offer_time; /* how long an offered address is reserved */
+ unsigned long min_lease; /* minimum lease a client can request*/
+ char *lease_file;
+ char *pidfile;
+ char *notify_file; /* What to run whenever leases are written */
+ u_int32_t siaddr; /* next server bootp option */
+ char *sname; /* bootp server name */
+ char *boot_file; /* bootp boot file option */
+};
+
+extern struct server_config_t server_config;
+extern struct dhcpOfferedAddr *leases;
+
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/dumpleases.c b/release/src/router/busybox/networking/udhcp/dumpleases.c
new file mode 100644
index 00000000..4c6107cf
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/dumpleases.c
@@ -0,0 +1,88 @@
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <time.h>
+
+#include "leases.h"
+#include "busybox.h"
+
+#define REMAINING 0
+#define ABSOLUTE 1
+
+int dumpleases_main(int argc, char *argv[])
+{
+ FILE *fp;
+ int i, c, mode = REMAINING;
+ long expires;
+ const char *file = leases_file;
+ struct dhcpOfferedAddr lease;
+ struct in_addr addr;
+
+ static const struct option options[] = {
+ {"absolute", 0, 0, 'a'},
+ {"remaining", 0, 0, 'r'},
+ {"file", 1, 0, 'f'},
+ {0, 0, 0, 0}
+ };
+
+ while (1) {
+ int option_index = 0;
+ c = getopt_long(argc, argv, "arf:", options, &option_index);
+ if (c == -1) break;
+
+ switch (c) {
+ case 'a': mode = ABSOLUTE; break;
+ case 'r': mode = REMAINING; break;
+ case 'f':
+ file = optarg;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+
+ fp = bb_xfopen(file, "r");
+
+ printf("Mac Address IP-Address Expires %s\n", mode == REMAINING ? "in" : "at");
+ /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */
+ while (fread(&lease, sizeof(lease), 1, fp)) {
+
+ for (i = 0; i < 6; i++) {
+ printf("%02x", lease.chaddr[i]);
+ if (i != 5) printf(":");
+ }
+ addr.s_addr = lease.yiaddr;
+ printf(" %-15s", inet_ntoa(addr));
+ expires = ntohl(lease.expires);
+ printf(" ");
+ if (mode == REMAINING) {
+ if (!expires) printf("expired\n");
+ else {
+ if (expires > 60*60*24) {
+ printf("%ld days, ", expires / (60*60*24));
+ expires %= 60*60*24;
+ }
+ if (expires > 60*60) {
+ printf("%ld hours, ", expires / (60*60));
+ expires %= 60*60;
+ }
+ if (expires > 60) {
+ printf("%ld minutes, ", expires / 60);
+ expires %= 60;
+ }
+ printf("%ld seconds\n", expires);
+ }
+ } else printf("%s", ctime(&expires));
+ }
+ fclose(fp);
+
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/udhcp/files.c b/release/src/router/busybox/networking/udhcp/files.c
new file mode 100644
index 00000000..3d0344b0
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/files.c
@@ -0,0 +1,287 @@
+/*
+ * files.c -- DHCP server file manipulation *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "dhcpd.h"
+#include "files.h"
+#include "options.h"
+#include "common.h"
+
+/* on these functions, make sure you datatype matches */
+static int read_ip(const char *line, void *arg)
+{
+ struct in_addr *addr = arg;
+ struct hostent *host;
+ int retval = 1;
+
+ if (!inet_aton(line, addr)) {
+ if ((host = gethostbyname(line)))
+ addr->s_addr = *((unsigned long *) host->h_addr_list[0]);
+ else retval = 0;
+ }
+ return retval;
+}
+
+
+static int read_str(const char *line, void *arg)
+{
+ char **dest = arg;
+
+ if (*dest) free(*dest);
+ *dest = strdup(line);
+
+ return 1;
+}
+
+
+static int read_u32(const char *line, void *arg)
+{
+ u_int32_t *dest = arg;
+ char *endptr;
+ *dest = strtoul(line, &endptr, 0);
+ return endptr[0] == '\0';
+}
+
+
+static int read_yn(const char *line, void *arg)
+{
+ char *dest = arg;
+ int retval = 1;
+
+ if (!strcasecmp("yes", line))
+ *dest = 1;
+ else if (!strcasecmp("no", line))
+ *dest = 0;
+ else retval = 0;
+
+ return retval;
+}
+
+#define READ_CONFIG_BUF_SIZE 512 /* domainname may have 254 chars */
+
+/* read a dhcp option and add it to opt_list */
+static int read_opt(const char *const_line, void *arg)
+{
+ char line[READ_CONFIG_BUF_SIZE];
+ struct option_set **opt_list = arg;
+ char *opt, *val, *endptr;
+ struct dhcp_option *option;
+ int retval = 0, length;
+ char buffer[256]; /* max opt length */
+ u_int16_t result_u16;
+ u_int32_t result_u32;
+ void *valptr;
+
+ if ((opt = strtok(strcpy(line, const_line), " \t="))) {
+
+ for (option = options; option->code; option++)
+ if (!strcasecmp(option->name, opt))
+ break;
+
+ if (option->code) do {
+ val = strtok(NULL, ", \t");
+ if(!val)
+ break;
+ length = option_lengths[option->flags & TYPE_MASK];
+ valptr = NULL;
+ switch (option->flags & TYPE_MASK) {
+ case OPTION_IP:
+ retval = read_ip(val, buffer);
+ break;
+ case OPTION_IP_PAIR:
+ retval = read_ip(val, buffer);
+ if (!(val = strtok(NULL, ", \t/-"))) retval = 0;
+ if (retval) retval = read_ip(val, buffer + 4);
+ break;
+ case OPTION_STRING:
+ length = strlen(val);
+ if (length > 0) {
+ if (length > 254) length = 254;
+ endptr = buffer + length;
+ endptr[0] = 0;
+ valptr = val;
+ }
+ break;
+ case OPTION_BOOLEAN:
+ retval = read_yn(val, buffer);
+ break;
+ case OPTION_U8:
+ buffer[0] = strtoul(val, &endptr, 0);
+ valptr = buffer;
+ break;
+ case OPTION_U16:
+ result_u16 = htons(strtoul(val, &endptr, 0));
+ valptr = &result_u16;
+ break;
+ case OPTION_S16:
+ result_u16 = htons(strtol(val, &endptr, 0));
+ valptr = &result_u16;
+ break;
+ case OPTION_U32:
+ result_u32 = htonl(strtoul(val, &endptr, 0));
+ valptr = &result_u32;
+ break;
+ case OPTION_S32:
+ result_u32 = htonl(strtol(val, &endptr, 0));
+ valptr = &result_u32;
+ break;
+ default:
+ retval = 0;
+ break;
+ }
+ if(valptr) {
+ memcpy(buffer, valptr, length);
+ retval = (endptr[0] == '\0');
+ }
+ if (retval)
+ attach_option(opt_list, option, buffer, length);
+ else
+ break;
+ } while (option->flags & OPTION_LIST);
+ }
+ return retval;
+}
+
+
+static const struct config_keyword keywords[] = {
+ /* keyword handler variable address default */
+ {"start", read_ip, &(server_config.start), "192.168.0.20"},
+ {"end", read_ip, &(server_config.end), "192.168.0.254"},
+ {"interface", read_str, &(server_config.interface), "eth0"},
+ {"option", read_opt, &(server_config.options), ""},
+ {"opt", read_opt, &(server_config.options), ""},
+ {"max_leases", read_u32, &(server_config.max_leases), "254"},
+ {"remaining", read_yn, &(server_config.remaining), "yes"},
+ {"auto_time", read_u32, &(server_config.auto_time), "7200"},
+ {"decline_time",read_u32, &(server_config.decline_time),"3600"},
+ {"conflict_time",read_u32,&(server_config.conflict_time),"3600"},
+ {"offer_time", read_u32, &(server_config.offer_time), "60"},
+ {"min_lease", read_u32, &(server_config.min_lease), "60"},
+ {"lease_file", read_str, &(server_config.lease_file), leases_file},
+ {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"},
+ {"notify_file", read_str, &(server_config.notify_file), ""},
+ {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"},
+ {"sname", read_str, &(server_config.sname), ""},
+ {"boot_file", read_str, &(server_config.boot_file), ""},
+ /*ADDME: static lease */
+ {"", NULL, NULL, ""}
+};
+
+
+int read_config(const char *file)
+{
+ FILE *in;
+ char buffer[READ_CONFIG_BUF_SIZE], orig[READ_CONFIG_BUF_SIZE];
+ char *token, *line;
+ int i;
+
+ for (i = 0; keywords[i].keyword[0]; i++)
+ if (keywords[i].def[0])
+ keywords[i].handler(keywords[i].def, keywords[i].var);
+
+ if (!(in = fopen(file, "r"))) {
+ LOG(LOG_ERR, "unable to open config file: %s", file);
+ return 0;
+ }
+
+ while (fgets(buffer, READ_CONFIG_BUF_SIZE, in)) {
+ if (strchr(buffer, '\n')) *(strchr(buffer, '\n')) = '\0';
+ strcpy(orig, buffer);
+ if (strchr(buffer, '#')) *(strchr(buffer, '#')) = '\0';
+ token = strtok(buffer, " \t");
+ if(!token)
+ continue;
+ line = strtok(NULL, "");
+ if(!line)
+ continue;
+ while(*line == '=' || isspace(*line))
+ line++;
+ /* eat trailing whitespace */
+ for (i = strlen(line); i > 0 && isspace(line[i - 1]); i--);
+ line[i] = '\0';
+ if (*line == '\0')
+ continue;
+
+ for (i = 0; keywords[i].keyword[0]; i++)
+ if (!strcasecmp(token, keywords[i].keyword))
+ if (!keywords[i].handler(line, keywords[i].var)) {
+ LOG(LOG_ERR, "unable to parse '%s'", orig);
+ /* reset back to the default value */
+ keywords[i].handler(keywords[i].def, keywords[i].var);
+ }
+ }
+ fclose(in);
+ return 1;
+}
+
+
+void write_leases(void)
+{
+ FILE *fp;
+ unsigned int i;
+ char buf[255];
+ time_t curr = time(0);
+ unsigned long lease_time;
+
+ if (!(fp = fopen(server_config.lease_file, "w"))) {
+ LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file);
+ return;
+ }
+
+ for (i = 0; i < server_config.max_leases; i++) {
+ struct dhcpOfferedAddr lease;
+ if (leases[i].yiaddr != 0) {
+ if (server_config.remaining) {
+ if (lease_expired(&(leases[i])))
+ lease_time = 0;
+ else lease_time = leases[i].expires - curr;
+ } else lease_time = leases[i].expires;
+ lease.expires = htonl(lease_time);
+ memcpy(lease.chaddr, leases[i].chaddr, 16);
+ lease.yiaddr = leases[i].yiaddr;
+ fwrite(leases, sizeof(lease), 1, fp);
+ }
+ }
+ fclose(fp);
+
+ if (server_config.notify_file) {
+ sprintf(buf, "%s %s", server_config.notify_file, server_config.lease_file);
+ system(buf);
+ }
+}
+
+
+void read_leases(const char *file)
+{
+ FILE *fp;
+ unsigned int i = 0;
+ struct dhcpOfferedAddr lease;
+
+ if (!(fp = fopen(file, "r"))) {
+ LOG(LOG_ERR, "Unable to open %s for reading", file);
+ return;
+ }
+
+ while (i < server_config.max_leases && (fread(&lease, sizeof lease, 1, fp) == 1)) {
+ /* ADDME: is it a static lease */
+ if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) {
+ lease.expires = ntohl(lease.expires);
+ if (!server_config.remaining) lease.expires -= time(0);
+ if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) {
+ LOG(LOG_WARNING, "Too many leases while loading %s\n", file);
+ break;
+ }
+ i++;
+ }
+ }
+ DEBUG(LOG_INFO, "Read %d leases", i);
+ fclose(fp);
+}
diff --git a/release/src/router/busybox/networking/udhcp/files.h b/release/src/router/busybox/networking/udhcp/files.h
new file mode 100644
index 00000000..279738ad
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/files.h
@@ -0,0 +1,17 @@
+/* files.h */
+#ifndef _FILES_H
+#define _FILES_H
+
+struct config_keyword {
+ const char *keyword;
+ int (* const handler)(const char *line, void *var);
+ void *var;
+ const char *def;
+};
+
+
+int read_config(const char *file);
+void write_leases(void);
+void read_leases(const char *file);
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/frontend.c b/release/src/router/busybox/networking/udhcp/frontend.c
new file mode 100644
index 00000000..de577950
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/frontend.c
@@ -0,0 +1,16 @@
+#include <string.h>
+
+extern int udhcpd_main(int argc, char *argv[]);
+extern int udhcpc_main(int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ char *base = strrchr(argv[0], '/');
+
+ if (strstr(base ? (base + 1) : argv[0], "dhcpd"))
+ ret = udhcpd_main(argc, argv);
+ else ret = udhcpc_main(argc, argv);
+
+ return ret;
+}
diff --git a/release/src/router/busybox/networking/udhcp/leases.c b/release/src/router/busybox/networking/udhcp/leases.c
new file mode 100644
index 00000000..0b6b409c
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/leases.c
@@ -0,0 +1,149 @@
+/*
+ * leases.c -- tools to manage DHCP leases
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include <time.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "dhcpd.h"
+#include "files.h"
+#include "options.h"
+#include "arpping.h"
+#include "common.h"
+
+
+unsigned char blank_chaddr[] = {[0 ... 15] = 0};
+
+/* clear every lease out that chaddr OR yiaddr matches and is nonzero */
+void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr)
+{
+ unsigned int i, j;
+
+ for (j = 0; j < 16 && !chaddr[j]; j++);
+
+ for (i = 0; i < server_config.max_leases; i++)
+ if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) ||
+ (yiaddr && leases[i].yiaddr == yiaddr)) {
+ memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr));
+ }
+}
+
+
+/* add a lease into the table, clearing out any old ones */
+struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease)
+{
+ struct dhcpOfferedAddr *oldest;
+
+ /* clean out any old ones */
+ clear_lease(chaddr, yiaddr);
+
+ oldest = oldest_expired_lease();
+
+ if (oldest) {
+ memcpy(oldest->chaddr, chaddr, 16);
+ oldest->yiaddr = yiaddr;
+ oldest->expires = time(0) + lease;
+ }
+
+ return oldest;
+}
+
+
+/* true if a lease has expired */
+int lease_expired(struct dhcpOfferedAddr *lease)
+{
+ return (lease->expires < (unsigned long) time(0));
+}
+
+
+/* Find the oldest expired lease, NULL if there are no expired leases */
+struct dhcpOfferedAddr *oldest_expired_lease(void)
+{
+ struct dhcpOfferedAddr *oldest = NULL;
+ unsigned long oldest_lease = time(0);
+ unsigned int i;
+
+
+ for (i = 0; i < server_config.max_leases; i++)
+ if (oldest_lease > leases[i].expires) {
+ oldest_lease = leases[i].expires;
+ oldest = &(leases[i]);
+ }
+ return oldest;
+
+}
+
+
+/* Find the first lease that matches chaddr, NULL if no match */
+struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr)
+{
+ unsigned int i;
+
+ for (i = 0; i < server_config.max_leases; i++)
+ if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]);
+
+ return NULL;
+}
+
+
+/* Find the first lease that matches yiaddr, NULL is no match */
+struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr)
+{
+ unsigned int i;
+
+ for (i = 0; i < server_config.max_leases; i++)
+ if (leases[i].yiaddr == yiaddr) return &(leases[i]);
+
+ return NULL;
+}
+
+
+/* check is an IP is taken, if it is, add it to the lease table */
+static int check_ip(u_int32_t addr)
+{
+ struct in_addr temp;
+
+ if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) {
+ temp.s_addr = addr;
+ LOG(LOG_INFO, "%s belongs to someone, reserving it for %ld seconds",
+ inet_ntoa(temp), server_config.conflict_time);
+ add_lease(blank_chaddr, addr, server_config.conflict_time);
+ return 1;
+ } else return 0;
+}
+
+/* find an assignable address, it check_expired is true, we check all the expired leases as well.
+ * Maybe this should try expired leases by age... */
+u_int32_t find_address(int check_expired)
+{
+ u_int32_t addr, ret;
+ struct dhcpOfferedAddr *lease = NULL;
+
+ addr = ntohl(server_config.start); /* addr is in host order here */
+ for (;addr <= ntohl(server_config.end); addr++) {
+
+ /* ie, 192.168.55.0 */
+ if (!(addr & 0xFF)) continue;
+
+ /* ie, 192.168.55.255 */
+ if ((addr & 0xFF) == 0xFF) continue;
+
+ /* lease is not taken */
+ ret = htonl(addr);
+ if ((!(lease = find_lease_by_yiaddr(ret)) ||
+
+ /* or it expired and we are checking for expired leases */
+ (check_expired && lease_expired(lease))) &&
+
+ /* and it isn't on the network */
+ !check_ip(ret)) {
+ return ret;
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/release/src/router/busybox/networking/udhcp/leases.h b/release/src/router/busybox/networking/udhcp/leases.h
new file mode 100644
index 00000000..d54cd11c
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/leases.h
@@ -0,0 +1,25 @@
+/* leases.h */
+#ifndef _LEASES_H
+#define _LEASES_H
+
+
+struct dhcpOfferedAddr {
+ u_int8_t chaddr[16];
+ u_int32_t yiaddr; /* network order */
+ u_int32_t expires; /* host order */
+};
+
+extern const char leases_file[];
+
+extern unsigned char blank_chaddr[];
+
+void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr);
+struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease);
+int lease_expired(struct dhcpOfferedAddr *lease);
+struct dhcpOfferedAddr *oldest_expired_lease(void);
+struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr);
+struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr);
+u_int32_t find_address(int check_expired);
+
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/leases_file.c b/release/src/router/busybox/networking/udhcp/leases_file.c
new file mode 100644
index 00000000..96e2f2d1
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/leases_file.c
@@ -0,0 +1 @@
+const char leases_file[] = "/var/lib/misc/udhcpd.leases";
diff --git a/release/src/router/busybox/networking/udhcp/libbb_udhcp.h b/release/src/router/busybox/networking/udhcp/libbb_udhcp.h
new file mode 100644
index 00000000..e0942980
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/libbb_udhcp.h
@@ -0,0 +1,29 @@
+/* libbb_udhcp.h - busybox compatability wrapper */
+
+#ifndef _LIBBB_UDHCP_H
+#define _LIBBB_UDHCP_H
+
+#ifdef IN_BUSYBOX
+#include "libbb.h"
+
+#ifdef CONFIG_FEATURE_UDHCP_SYSLOG
+#define SYSLOG
+#endif
+
+#ifdef CONFIG_FEATURE_UDHCP_DEBUG
+#define DEBUG
+#endif
+
+#define COMBINED_BINARY
+#include "version.h"
+
+#else /* ! BB_VER */
+
+#define TRUE 1
+#define FALSE 0
+
+#define xmalloc malloc
+
+#endif /* BB_VER */
+
+#endif /* _LIBBB_UDHCP_H */
diff --git a/release/src/router/busybox/networking/udhcp/options.c b/release/src/router/busybox/networking/udhcp/options.c
new file mode 100644
index 00000000..a3edd0b8
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/options.c
@@ -0,0 +1,228 @@
+/*
+ * options.c -- DHCP server option packet tools
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dhcpd.h"
+#include "files.h"
+#include "options.h"
+#include "common.h"
+
+
+/* supported options are easily added here */
+struct dhcp_option options[] = {
+ /* name[10] flags code */
+ {"subnet", OPTION_IP | OPTION_REQ, 0x01},
+ {"timezone", OPTION_S32, 0x02},
+ {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03},
+ {"timesvr", OPTION_IP | OPTION_LIST, 0x04},
+ {"namesvr", OPTION_IP | OPTION_LIST, 0x05},
+ {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06},
+ {"logsvr", OPTION_IP | OPTION_LIST, 0x07},
+ {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08},
+ {"lprsvr", OPTION_IP | OPTION_LIST, 0x09},
+ {"hostname", OPTION_STRING | OPTION_REQ, 0x0c},
+ {"bootsize", OPTION_U16, 0x0d},
+ {"domain", OPTION_STRING | OPTION_REQ, 0x0f},
+ {"swapsvr", OPTION_IP, 0x10},
+ {"rootpath", OPTION_STRING, 0x11},
+ {"ipttl", OPTION_U8, 0x17},
+ {"mtu", OPTION_U16, 0x1a},
+ {"broadcast", OPTION_IP | OPTION_REQ, 0x1c},
+ {"ntpsrv", OPTION_IP | OPTION_LIST, 0x2a},
+ {"wins", OPTION_IP | OPTION_LIST, 0x2c},
+ {"requestip", OPTION_IP, 0x32},
+ {"lease", OPTION_U32, 0x33},
+ {"dhcptype", OPTION_U8, 0x35},
+ {"serverid", OPTION_IP, 0x36},
+ {"message", OPTION_STRING, 0x38},
+ {"tftp", OPTION_STRING, 0x42},
+ {"bootfile", OPTION_STRING, 0x43},
+ {"", 0x00, 0x00}
+};
+
+/* Lengths of the different option types */
+int option_lengths[] = {
+ [OPTION_IP] = 4,
+ [OPTION_IP_PAIR] = 8,
+ [OPTION_BOOLEAN] = 1,
+ [OPTION_STRING] = 1,
+ [OPTION_U8] = 1,
+ [OPTION_U16] = 2,
+ [OPTION_S16] = 2,
+ [OPTION_U32] = 4,
+ [OPTION_S32] = 4
+};
+
+
+/* get an option with bounds checking (warning, not aligned). */
+unsigned char *get_option(struct dhcpMessage *packet, int code)
+{
+ int i, length;
+ unsigned char *optionptr;
+ int over = 0, done = 0, curr = OPTION_FIELD;
+
+ optionptr = packet->options;
+ i = 0;
+ length = 308;
+ while (!done) {
+ if (i >= length) {
+ LOG(LOG_WARNING, "bogus packet, option fields too long.");
+ return NULL;
+ }
+ if (optionptr[i + OPT_CODE] == code) {
+ if (i + 1 + optionptr[i + OPT_LEN] >= length) {
+ LOG(LOG_WARNING, "bogus packet, option fields too long.");
+ return NULL;
+ }
+ return optionptr + i + 2;
+ }
+ switch (optionptr[i + OPT_CODE]) {
+ case DHCP_PADDING:
+ i++;
+ break;
+ case DHCP_OPTION_OVER:
+ if (i + 1 + optionptr[i + OPT_LEN] >= length) {
+ LOG(LOG_WARNING, "bogus packet, option fields too long.");
+ return NULL;
+ }
+ over = optionptr[i + 3];
+ i += optionptr[OPT_LEN] + 2;
+ break;
+ case DHCP_END:
+ if (curr == OPTION_FIELD && over & FILE_FIELD) {
+ optionptr = packet->file;
+ i = 0;
+ length = 128;
+ curr = FILE_FIELD;
+ } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
+ optionptr = packet->sname;
+ i = 0;
+ length = 64;
+ curr = SNAME_FIELD;
+ } else done = 1;
+ break;
+ default:
+ i += optionptr[OPT_LEN + i] + 2;
+ }
+ }
+ return NULL;
+}
+
+
+/* return the position of the 'end' option (no bounds checking) */
+int end_option(unsigned char *optionptr)
+{
+ int i = 0;
+
+ while (optionptr[i] != DHCP_END) {
+ if (optionptr[i] == DHCP_PADDING) i++;
+ else i += optionptr[i + OPT_LEN] + 2;
+ }
+ return i;
+}
+
+
+/* add an option string to the options (an option string contains an option code,
+ * length, then data) */
+int add_option_string(unsigned char *optionptr, unsigned char *string)
+{
+ int end = end_option(optionptr);
+
+ /* end position + string length + option code/length + end option */
+ if (end + string[OPT_LEN] + 2 + 1 >= 308) {
+ LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", string[OPT_CODE]);
+ return 0;
+ }
+ DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]);
+ memcpy(optionptr + end, string, string[OPT_LEN] + 2);
+ optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
+ return string[OPT_LEN] + 2;
+}
+
+
+/* add a one to four byte option to a packet */
+int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data)
+{
+ char length = 0;
+ int i;
+ unsigned char option[2 + 4];
+ unsigned char *u8;
+ u_int16_t *u16;
+ u_int32_t *u32;
+ u_int32_t aligned;
+ u8 = (unsigned char *) &aligned;
+ u16 = (u_int16_t *) &aligned;
+ u32 = &aligned;
+
+ for (i = 0; options[i].code; i++)
+ if (options[i].code == code) {
+ length = option_lengths[options[i].flags & TYPE_MASK];
+ }
+
+ if (!length) {
+ DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
+ return 0;
+ }
+
+ option[OPT_CODE] = code;
+ option[OPT_LEN] = length;
+
+ switch (length) {
+ case 1: *u8 = data; break;
+ case 2: *u16 = data; break;
+ case 4: *u32 = data; break;
+ }
+ memcpy(option + 2, &aligned, length);
+ return add_option_string(optionptr, option);
+}
+
+
+/* find option 'code' in opt_list */
+struct option_set *find_option(struct option_set *opt_list, char code)
+{
+ while (opt_list && opt_list->data[OPT_CODE] < code)
+ opt_list = opt_list->next;
+
+ if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list;
+ else return NULL;
+}
+
+
+/* add an option to the opt_list */
+void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length)
+{
+ struct option_set *existing, *new, **curr;
+
+ /* add it to an existing option */
+ if ((existing = find_option(*opt_list, option->code))) {
+ DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name);
+ if (option->flags & OPTION_LIST) {
+ if (existing->data[OPT_LEN] + length <= 255) {
+ existing->data = realloc(existing->data,
+ existing->data[OPT_LEN] + length + 2);
+ memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
+ existing->data[OPT_LEN] += length;
+ } /* else, ignore the data, we could put this in a second option in the future */
+ } /* else, ignore the new data */
+ } else {
+ DEBUG(LOG_INFO, "Attaching option %s to list", option->name);
+
+ /* make a new option */
+ new = xmalloc(sizeof(struct option_set));
+ new->data = xmalloc(length + 2);
+ new->data[OPT_CODE] = option->code;
+ new->data[OPT_LEN] = length;
+ memcpy(new->data + 2, buffer, length);
+
+ curr = opt_list;
+ while (*curr && (*curr)->data[OPT_CODE] < option->code)
+ curr = &(*curr)->next;
+
+ new->next = *curr;
+ *curr = new;
+ }
+}
diff --git a/release/src/router/busybox/networking/udhcp/options.h b/release/src/router/busybox/networking/udhcp/options.h
new file mode 100644
index 00000000..1fded2ef
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/options.h
@@ -0,0 +1,40 @@
+/* options.h */
+#ifndef _OPTIONS_H
+#define _OPTIONS_H
+
+#include "packet.h"
+
+#define TYPE_MASK 0x0F
+
+enum {
+ OPTION_IP=1,
+ OPTION_IP_PAIR,
+ OPTION_STRING,
+ OPTION_BOOLEAN,
+ OPTION_U8,
+ OPTION_U16,
+ OPTION_S16,
+ OPTION_U32,
+ OPTION_S32
+};
+
+#define OPTION_REQ 0x10 /* have the client request this option */
+#define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */
+
+struct dhcp_option {
+ char name[10];
+ char flags;
+ unsigned char code;
+};
+
+extern struct dhcp_option options[];
+extern int option_lengths[];
+
+unsigned char *get_option(struct dhcpMessage *packet, int code);
+int end_option(unsigned char *optionptr);
+int add_option_string(unsigned char *optionptr, unsigned char *string);
+int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data);
+struct option_set *find_option(struct option_set *opt_list, char code);
+void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length);
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/packet.c b/release/src/router/busybox/networking/udhcp/packet.c
new file mode 100644
index 00000000..0f2a3bc6
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/packet.c
@@ -0,0 +1,201 @@
+#include <unistd.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <features.h>
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+#include <errno.h>
+
+#include "dhcpd.h"
+#include "options.h"
+#include "common.h"
+
+
+void init_header(struct dhcpMessage *packet, char type)
+{
+ memset(packet, 0, sizeof(struct dhcpMessage));
+ switch (type) {
+ case DHCPDISCOVER:
+ case DHCPREQUEST:
+ case DHCPRELEASE:
+ case DHCPINFORM:
+ packet->op = BOOTREQUEST;
+ break;
+ case DHCPOFFER:
+ case DHCPACK:
+ case DHCPNAK:
+ packet->op = BOOTREPLY;
+ }
+ packet->htype = ETH_10MB;
+ packet->hlen = ETH_10MB_LEN;
+ packet->cookie = htonl(DHCP_MAGIC);
+ packet->options[0] = DHCP_END;
+ add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type);
+}
+
+
+/* read a packet from socket fd, return -1 on read error, -2 on packet error */
+int get_packet(struct dhcpMessage *packet, int fd)
+{
+ int bytes;
+ int i;
+ const char broken_vendors[][8] = {
+ "MSFT 98",
+ ""
+ };
+ char unsigned *vendor;
+
+ memset(packet, 0, sizeof(struct dhcpMessage));
+ bytes = read(fd, packet, sizeof(struct dhcpMessage));
+ if (bytes < 0) {
+ DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring");
+ return -1;
+ }
+
+ if (ntohl(packet->cookie) != DHCP_MAGIC) {
+ LOG(LOG_ERR, "received bogus message, ignoring");
+ return -2;
+ }
+ DEBUG(LOG_INFO, "Received a packet");
+
+ if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) {
+ for (i = 0; broken_vendors[i][0]; i++) {
+ if (vendor[OPT_LEN - 2] == (unsigned char) strlen(broken_vendors[i]) &&
+ !strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) {
+ DEBUG(LOG_INFO, "broken client (%s), forcing broadcast",
+ broken_vendors[i]);
+ packet->flags |= htons(BROADCAST_FLAG);
+ }
+ }
+ }
+
+
+ return bytes;
+}
+
+
+u_int16_t checksum(void *addr, int count)
+{
+ /* Compute Internet Checksum for "count" bytes
+ * beginning at location "addr".
+ */
+ register int32_t sum = 0;
+ u_int16_t *source = (u_int16_t *) addr;
+
+ while (count > 1) {
+ /* This is the inner loop */
+ sum += *source++;
+ count -= 2;
+ }
+
+ /* Add left-over byte, if any */
+ if (count > 0) {
+ /* Make sure that the left-over byte is added correctly both
+ * with little and big endian hosts */
+ u_int16_t tmp = 0;
+ *(unsigned char *) (&tmp) = * (unsigned char *) source;
+ sum += tmp;
+ }
+ /* Fold 32-bit sum to 16 bits */
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return ~sum;
+}
+
+
+/* Constuct a ip/udp header for a packet, and specify the source and dest hardware address */
+int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port,
+ u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex)
+{
+ int fd;
+ int result;
+ struct sockaddr_ll dest;
+ struct udp_dhcp_packet packet;
+
+ if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
+ DEBUG(LOG_ERR, "socket call failed: %m");
+ return -1;
+ }
+
+ memset(&dest, 0, sizeof(dest));
+ memset(&packet, 0, sizeof(packet));
+
+ dest.sll_family = AF_PACKET;
+ dest.sll_protocol = htons(ETH_P_IP);
+ dest.sll_ifindex = ifindex;
+ dest.sll_halen = 6;
+ memcpy(dest.sll_addr, dest_arp, 6);
+ if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) {
+ DEBUG(LOG_ERR, "bind call failed: %m");
+ close(fd);
+ return -1;
+ }
+
+ packet.ip.protocol = IPPROTO_UDP;
+ packet.ip.saddr = source_ip;
+ packet.ip.daddr = dest_ip;
+ packet.udp.source = htons(source_port);
+ packet.udp.dest = htons(dest_port);
+ packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */
+ packet.ip.tot_len = packet.udp.len;
+ memcpy(&(packet.data), payload, sizeof(struct dhcpMessage));
+ packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet));
+
+ packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet));
+ packet.ip.ihl = sizeof(packet.ip) >> 2;
+ packet.ip.version = IPVERSION;
+ packet.ip.ttl = IPDEFTTL;
+ packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip));
+
+ result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest));
+ if (result <= 0) {
+ DEBUG(LOG_ERR, "write on socket failed: %m");
+ }
+ close(fd);
+ return result;
+}
+
+
+/* Let the kernel do all the work for packet generation */
+int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port,
+ u_int32_t dest_ip, int dest_port)
+{
+ int n = 1;
+ int fd, result;
+ struct sockaddr_in client;
+
+ if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ return -1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1)
+ return -1;
+
+ memset(&client, 0, sizeof(client));
+ client.sin_family = AF_INET;
+ client.sin_port = htons(source_port);
+ client.sin_addr.s_addr = source_ip;
+
+ if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
+ return -1;
+
+ memset(&client, 0, sizeof(client));
+ client.sin_family = AF_INET;
+ client.sin_port = htons(dest_port);
+ client.sin_addr.s_addr = dest_ip;
+
+ if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
+ return -1;
+
+ result = write(fd, payload, sizeof(struct dhcpMessage));
+ close(fd);
+ return result;
+}
diff --git a/release/src/router/busybox/networking/udhcp/packet.h b/release/src/router/busybox/networking/udhcp/packet.h
new file mode 100644
index 00000000..1a263ef8
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/packet.h
@@ -0,0 +1,41 @@
+#ifndef _PACKET_H
+#define _PACKET_H
+
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+
+struct dhcpMessage {
+ u_int8_t op;
+ u_int8_t htype;
+ u_int8_t hlen;
+ u_int8_t hops;
+ u_int32_t xid;
+ u_int16_t secs;
+ u_int16_t flags;
+ u_int32_t ciaddr;
+ u_int32_t yiaddr;
+ u_int32_t siaddr;
+ u_int32_t giaddr;
+ u_int8_t chaddr[16];
+ u_int8_t sname[64];
+ u_int8_t file[128];
+ u_int32_t cookie;
+ u_int8_t options[308]; /* 312 - cookie */
+};
+
+struct udp_dhcp_packet {
+ struct iphdr ip;
+ struct udphdr udp;
+ struct dhcpMessage data;
+};
+
+void init_header(struct dhcpMessage *packet, char type);
+int get_packet(struct dhcpMessage *packet, int fd);
+u_int16_t checksum(void *addr, int count);
+int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port,
+ u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex);
+int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port,
+ u_int32_t dest_ip, int dest_port);
+
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/pidfile.c b/release/src/router/busybox/networking/udhcp/pidfile.c
new file mode 100644
index 00000000..246a64aa
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/pidfile.c
@@ -0,0 +1,69 @@
+/* pidfile.c
+ *
+ * Functions to assist in the writing and removing of pidfiles.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> Soptember 2001
+ *
+ * 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "debug.h"
+
+int pidfile_acquire(char *pidfile)
+{
+ int pid_fd;
+ if (pidfile == NULL) return -1;
+
+ pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644);
+ if (pid_fd < 0) {
+ LOG(LOG_ERR, "Unable to open pidfile %s: %s\n",
+ pidfile, strerror(errno));
+ } else {
+ lockf(pid_fd, F_LOCK, 0);
+ }
+
+ return pid_fd;
+}
+
+
+void pidfile_write_release(int pid_fd)
+{
+ FILE *out;
+
+ if (pid_fd < 0) return;
+
+ if ((out = fdopen(pid_fd, "w")) != NULL) {
+ fprintf(out, "%d\n", getpid());
+ fclose(out);
+ }
+ lockf(pid_fd, F_UNLCK, 0);
+ close(pid_fd);
+}
+
+
+void pidfile_delete(char *pidfile)
+{
+ if (pidfile) unlink(pidfile);
+}
+
+
diff --git a/release/src/router/busybox/networking/udhcp/pidfile.h b/release/src/router/busybox/networking/udhcp/pidfile.h
new file mode 100644
index 00000000..0e2b148a
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/pidfile.h
@@ -0,0 +1,26 @@
+/* pidfile.h
+ *
+ * Functions to assist in the writing and removing of pidfiles.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> Soptember 2001
+ *
+ * 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+int pidfile_acquire(char *pidfile);
+void pidfile_write_release(int pid_fd);
+void pidfile_delete(char *pidfile);
+
diff --git a/release/src/router/busybox/networking/udhcp/script.c b/release/src/router/busybox/networking/udhcp/script.c
new file mode 100644
index 00000000..41b680d6
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/script.c
@@ -0,0 +1,243 @@
+/* script.c
+ *
+ * Functions to call the DHCP client notification scripts
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "options.h"
+#include "dhcpd.h"
+#include "dhcpc.h"
+#include "options.h"
+#include "common.h"
+
+/* get a rough idea of how long an option will be (rounding up...) */
+static const int max_option_length[] = {
+ [OPTION_IP] = sizeof("255.255.255.255 "),
+ [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2,
+ [OPTION_STRING] = 1,
+ [OPTION_BOOLEAN] = sizeof("yes "),
+ [OPTION_U8] = sizeof("255 "),
+ [OPTION_U16] = sizeof("65535 "),
+ [OPTION_S16] = sizeof("-32768 "),
+ [OPTION_U32] = sizeof("4294967295 "),
+ [OPTION_S32] = sizeof("-2147483684 "),
+};
+
+
+static inline int upper_length(int length, int opt_index)
+{
+ return max_option_length[opt_index] *
+ (length / option_lengths[opt_index]);
+}
+
+
+static int sprintip(char *dest, unsigned char *ip)
+{
+ return sprintf(dest, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+}
+
+static void asprintip(char **dest, char *pre, unsigned char *ip)
+{
+ asprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]);
+}
+
+
+/* really simple implementation, just count the bits */
+static int mton(struct in_addr *mask)
+{
+ int i;
+ unsigned long bits = ntohl(mask->s_addr);
+ /* too bad one can't check the carry bit, etc in c bit
+ * shifting */
+ for (i = 0; i < 32 && !((bits >> i) & 1); i++);
+ return 32 - i;
+}
+
+
+/* Fill dest with the text of option 'option'. */
+static void fill_options(char *dest, unsigned char *option, struct dhcp_option *type_p)
+{
+ int type, optlen;
+ u_int16_t val_u16;
+ int16_t val_s16;
+ u_int32_t val_u32;
+ int32_t val_s32;
+ int len = option[OPT_LEN - 2];
+
+ dest += sprintf(dest, "%s=", type_p->name);
+
+ type = type_p->flags & TYPE_MASK;
+ optlen = option_lengths[type];
+ for(;;) {
+ switch (type) {
+ case OPTION_IP_PAIR:
+ dest += sprintip(dest, option);
+ *(dest++) = '/';
+ option += 4;
+ optlen = 4;
+ case OPTION_IP: /* Works regardless of host byte order. */
+ dest += sprintip(dest, option);
+ break;
+ case OPTION_BOOLEAN:
+ dest += sprintf(dest, *option ? "yes" : "no");
+ break;
+ case OPTION_U8:
+ dest += sprintf(dest, "%u", *option);
+ break;
+ case OPTION_U16:
+ memcpy(&val_u16, option, 2);
+ dest += sprintf(dest, "%u", ntohs(val_u16));
+ break;
+ case OPTION_S16:
+ memcpy(&val_s16, option, 2);
+ dest += sprintf(dest, "%d", ntohs(val_s16));
+ break;
+ case OPTION_U32:
+ memcpy(&val_u32, option, 4);
+ dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32));
+ break;
+ case OPTION_S32:
+ memcpy(&val_s32, option, 4);
+ dest += sprintf(dest, "%ld", (long) ntohl(val_s32));
+ break;
+ case OPTION_STRING:
+ memcpy(dest, option, len);
+ dest[len] = '\0';
+ return; /* Short circuit this case */
+ }
+ option += optlen;
+ len -= optlen;
+ if (len <= 0) break;
+ dest += sprintf(dest, " ");
+ }
+}
+
+
+static char *find_env(const char *prefix, char *defaultstr)
+{
+ char *ptr;
+
+ ptr = getenv(prefix);
+ return ptr ? ptr : defaultstr;
+}
+
+
+/* put all the paramaters into an environment */
+static char **fill_envp(struct dhcpMessage *packet)
+{
+ int num_options = 0;
+ int i, j;
+ char **envp;
+ unsigned char *temp;
+ struct in_addr subnet;
+ char over = 0;
+
+ if (packet == NULL)
+ num_options = 0;
+ else {
+ for (i = 0; options[i].code; i++)
+ if (get_option(packet, options[i].code))
+ num_options++;
+ if (packet->siaddr) num_options++;
+ if ((temp = get_option(packet, DHCP_OPTION_OVER)))
+ over = *temp;
+ if (!(over & FILE_FIELD) && packet->file[0]) num_options++;
+ if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++;
+ }
+
+ envp = xmalloc((num_options + 5) * sizeof(char *));
+ j = 0;
+ asprintf(&envp[j++], "interface=%s", client_config.interface);
+ envp[j++] = find_env("PATH", "PATH=/bin:/usr/bin:/sbin:/usr/sbin");
+ envp[j++] = find_env("HOME", "HOME=/");
+
+ if (packet == NULL) {
+ envp[j++] = NULL;
+ return envp;
+ }
+
+ asprintip(&envp[j++], "ip=", (unsigned char *) &packet->yiaddr);
+
+
+ for (i = 0; options[i].code; i++) {
+ if (!(temp = get_option(packet, options[i].code)))
+ continue;
+ envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], options[i].flags & TYPE_MASK) + strlen(options[i].name) + 2);
+ fill_options(envp[j++], temp, &options[i]);
+
+ /* Fill in a subnet bits option for things like /24 */
+ if (options[i].code == DHCP_SUBNET) {
+ memcpy(&subnet, temp, 4);
+ asprintf(&envp[j++], "mask=%d", mton(&subnet));
+ }
+ }
+ if (packet->siaddr) {
+ asprintip(&envp[j++], "siaddr=", (unsigned char *) &packet->siaddr);
+ }
+ if (!(over & FILE_FIELD) && packet->file[0]) {
+ /* watch out for invalid packets */
+ packet->file[sizeof(packet->file) - 1] = '\0';
+ asprintf(&envp[j++], "boot_file=%s", packet->file);
+ }
+ if (!(over & SNAME_FIELD) && packet->sname[0]) {
+ /* watch out for invalid packets */
+ packet->sname[sizeof(packet->sname) - 1] = '\0';
+ asprintf(&envp[j++], "sname=%s", packet->sname);
+ }
+ envp[j] = NULL;
+ return envp;
+}
+
+
+/* Call a script with a par file and env vars */
+void run_script(struct dhcpMessage *packet, const char *name)
+{
+ int pid;
+ char **envp;
+
+ if (client_config.script == NULL)
+ return;
+
+ /* call script */
+ pid = fork();
+ if (pid) {
+ waitpid(pid, NULL, 0);
+ return;
+ } else if (pid == 0) {
+ envp = fill_envp(packet);
+
+ /* close fd's? */
+
+ /* exec script */
+ DEBUG(LOG_INFO, "execle'ing %s", client_config.script);
+ execle(client_config.script, client_config.script,
+ name, NULL, envp);
+ LOG(LOG_ERR, "script %s failed: %m", client_config.script);
+ exit(1);
+ }
+}
diff --git a/release/src/router/busybox/networking/udhcp/script.h b/release/src/router/busybox/networking/udhcp/script.h
new file mode 100644
index 00000000..87a20cc1
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/script.h
@@ -0,0 +1,6 @@
+#ifndef _SCRIPT_H
+#define _SCRIPT_H
+
+void run_script(struct dhcpMessage *packet, const char *name);
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/serverpacket.c b/release/src/router/busybox/networking/udhcp/serverpacket.c
new file mode 100644
index 00000000..09682444
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/serverpacket.c
@@ -0,0 +1,258 @@
+/* serverpacket.c
+ *
+ * Constuct and send DHCP server packets
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <time.h>
+
+#include "dhcpd.h"
+#include "options.h"
+#include "common.h"
+
+/* send a packet to giaddr using the kernel ip stack */
+static int send_packet_to_relay(struct dhcpMessage *payload)
+{
+ DEBUG(LOG_INFO, "Forwarding packet to relay");
+
+ return kernel_packet(payload, server_config.server, SERVER_PORT,
+ payload->giaddr, SERVER_PORT);
+}
+
+
+/* send a packet to a specific arp address and ip address by creating our own ip packet */
+static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast)
+{
+ unsigned char *chaddr;
+ u_int32_t ciaddr;
+
+ if (force_broadcast) {
+ DEBUG(LOG_INFO, "broadcasting packet to client (NAK)");
+ ciaddr = INADDR_BROADCAST;
+ chaddr = MAC_BCAST_ADDR;
+ } else if (payload->ciaddr) {
+ DEBUG(LOG_INFO, "unicasting packet to client ciaddr");
+ ciaddr = payload->ciaddr;
+ chaddr = payload->chaddr;
+ } else if (ntohs(payload->flags) & BROADCAST_FLAG) {
+ DEBUG(LOG_INFO, "broadcasting packet to client (requested)");
+ ciaddr = INADDR_BROADCAST;
+ chaddr = MAC_BCAST_ADDR;
+ } else {
+ DEBUG(LOG_INFO, "unicasting packet to client yiaddr");
+ ciaddr = payload->yiaddr;
+ chaddr = payload->chaddr;
+ }
+ return raw_packet(payload, server_config.server, SERVER_PORT,
+ ciaddr, CLIENT_PORT, chaddr, server_config.ifindex);
+}
+
+
+/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */
+static int send_packet(struct dhcpMessage *payload, int force_broadcast)
+{
+ int ret;
+
+ if (payload->giaddr)
+ ret = send_packet_to_relay(payload);
+ else ret = send_packet_to_client(payload, force_broadcast);
+ return ret;
+}
+
+
+static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type)
+{
+ init_header(packet, type);
+ packet->xid = oldpacket->xid;
+ memcpy(packet->chaddr, oldpacket->chaddr, 16);
+ packet->flags = oldpacket->flags;
+ packet->giaddr = oldpacket->giaddr;
+ packet->ciaddr = oldpacket->ciaddr;
+ add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server);
+}
+
+
+/* add in the bootp options */
+static void add_bootp_options(struct dhcpMessage *packet)
+{
+ packet->siaddr = server_config.siaddr;
+ if (server_config.sname)
+ strncpy(packet->sname, server_config.sname, sizeof(packet->sname) - 1);
+ if (server_config.boot_file)
+ strncpy(packet->file, server_config.boot_file, sizeof(packet->file) - 1);
+}
+
+
+/* send a DHCP OFFER to a DHCP DISCOVER */
+int sendOffer(struct dhcpMessage *oldpacket)
+{
+ struct dhcpMessage packet;
+ struct dhcpOfferedAddr *lease = NULL;
+ u_int32_t req_align, lease_time_align = server_config.lease;
+ unsigned char *req, *lease_time;
+ struct option_set *curr;
+ struct in_addr addr;
+
+ init_packet(&packet, oldpacket, DHCPOFFER);
+
+ /* ADDME: if static, short circuit */
+ /* the client is in our lease/offered table */
+ if ((lease = find_lease_by_chaddr(oldpacket->chaddr))) {
+ if (!lease_expired(lease))
+ lease_time_align = lease->expires - time(0);
+ packet.yiaddr = lease->yiaddr;
+
+ /* Or the client has a requested ip */
+ } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) &&
+
+ /* Don't look here (ugly hackish thing to do) */
+ memcpy(&req_align, req, 4) &&
+
+ /* and the ip is in the lease range */
+ ntohl(req_align) >= ntohl(server_config.start) &&
+ ntohl(req_align) <= ntohl(server_config.end) &&
+
+ /* and its not already taken/offered */ /* ADDME: check that its not a static lease */
+ ((!(lease = find_lease_by_yiaddr(req_align)) ||
+
+ /* or its taken, but expired */ /* ADDME: or maybe in here */
+ lease_expired(lease)))) {
+ packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */
+
+ /* otherwise, find a free IP */ /*ADDME: is it a static lease? */
+ } else {
+ packet.yiaddr = find_address(0);
+
+ /* try for an expired lease */
+ if (!packet.yiaddr) packet.yiaddr = find_address(1);
+ }
+
+ if(!packet.yiaddr) {
+ LOG(LOG_WARNING, "no IP addresses to give -- OFFER abandoned");
+ return -1;
+ }
+
+ if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) {
+ LOG(LOG_WARNING, "lease pool is full -- OFFER abandoned");
+ return -1;
+ }
+
+ if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
+ memcpy(&lease_time_align, lease_time, 4);
+ lease_time_align = ntohl(lease_time_align);
+ if (lease_time_align > server_config.lease)
+ lease_time_align = server_config.lease;
+ }
+
+ /* Make sure we aren't just using the lease time from the previous offer */
+ if (lease_time_align < server_config.min_lease)
+ lease_time_align = server_config.lease;
+ /* ADDME: end of short circuit */
+ add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
+
+ curr = server_config.options;
+ while (curr) {
+ if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
+ add_option_string(packet.options, curr->data);
+ curr = curr->next;
+ }
+
+ add_bootp_options(&packet);
+
+ addr.s_addr = packet.yiaddr;
+ LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr));
+ return send_packet(&packet, 0);
+}
+
+
+int sendNAK(struct dhcpMessage *oldpacket)
+{
+ struct dhcpMessage packet;
+
+ init_packet(&packet, oldpacket, DHCPNAK);
+
+ DEBUG(LOG_INFO, "sending NAK");
+ return send_packet(&packet, 1);
+}
+
+
+int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr)
+{
+ struct dhcpMessage packet;
+ struct option_set *curr;
+ unsigned char *lease_time;
+ u_int32_t lease_time_align = server_config.lease;
+ struct in_addr addr;
+
+ init_packet(&packet, oldpacket, DHCPACK);
+ packet.yiaddr = yiaddr;
+
+ if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
+ memcpy(&lease_time_align, lease_time, 4);
+ lease_time_align = ntohl(lease_time_align);
+ if (lease_time_align > server_config.lease)
+ lease_time_align = server_config.lease;
+ else if (lease_time_align < server_config.min_lease)
+ lease_time_align = server_config.lease;
+ }
+
+ add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
+
+ curr = server_config.options;
+ while (curr) {
+ if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
+ add_option_string(packet.options, curr->data);
+ curr = curr->next;
+ }
+
+ add_bootp_options(&packet);
+
+ addr.s_addr = packet.yiaddr;
+ LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(addr));
+
+ if (send_packet(&packet, 0) < 0)
+ return -1;
+
+ add_lease(packet.chaddr, packet.yiaddr, lease_time_align);
+
+ return 0;
+}
+
+
+int send_inform(struct dhcpMessage *oldpacket)
+{
+ struct dhcpMessage packet;
+ struct option_set *curr;
+
+ init_packet(&packet, oldpacket, DHCPACK);
+
+ curr = server_config.options;
+ while (curr) {
+ if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
+ add_option_string(packet.options, curr->data);
+ curr = curr->next;
+ }
+
+ add_bootp_options(&packet);
+
+ return send_packet(&packet, 0);
+}
diff --git a/release/src/router/busybox/networking/udhcp/serverpacket.h b/release/src/router/busybox/networking/udhcp/serverpacket.h
new file mode 100644
index 00000000..5a4fb276
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/serverpacket.h
@@ -0,0 +1,11 @@
+#ifndef _SERVERPACKET_H
+#define _SERVERPACKET_H
+
+
+int sendOffer(struct dhcpMessage *oldpacket);
+int sendNAK(struct dhcpMessage *oldpacket);
+int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr);
+int send_inform(struct dhcpMessage *oldpacket);
+
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/socket.c b/release/src/router/busybox/networking/udhcp/socket.c
new file mode 100644
index 00000000..a51a7436
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/socket.c
@@ -0,0 +1,155 @@
+/*
+ * socket.c -- DHCP server client/server socket creation
+ *
+ * udhcp client/server
+ * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
+ * Chris Trew <ctrew@moreton.com.au>
+ *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <errno.h>
+#include <features.h>
+#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+
+#include "common.h"
+
+int read_interface(char *interface, int *ifindex, u_int32_t *addr, unsigned char *arp)
+{
+ int fd;
+ struct ifreq ifr;
+ struct sockaddr_in *our_ip;
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+ if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) {
+ ifr.ifr_addr.sa_family = AF_INET;
+ strcpy(ifr.ifr_name, interface);
+
+ if (addr) {
+ if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
+ our_ip = (struct sockaddr_in *) &ifr.ifr_addr;
+ *addr = our_ip->sin_addr.s_addr;
+ DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr));
+ } else {
+ LOG(LOG_ERR, "SIOCGIFADDR failed, is the interface up and configured?: %m");
+ return -1;
+ }
+ }
+
+ if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
+ DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex);
+ *ifindex = ifr.ifr_ifindex;
+ } else {
+ LOG(LOG_ERR, "SIOCGIFINDEX failed!: %m");
+ return -1;
+ }
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
+ memcpy(arp, ifr.ifr_hwaddr.sa_data, 6);
+ DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
+ arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]);
+ } else {
+ LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %m");
+ return -1;
+ }
+ } else {
+ LOG(LOG_ERR, "socket failed!: %m");
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
+
+int listen_socket(unsigned int ip, int port, char *inf)
+{
+ struct ifreq interface;
+ int fd;
+ struct sockaddr_in addr;
+ int n = 1;
+
+ DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s\n", ip, port, inf);
+ if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ DEBUG(LOG_ERR, "socket call failed: %m");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = ip;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) {
+ close(fd);
+ return -1;
+ }
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ);
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,(char *)&interface, sizeof(interface)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+
+int raw_socket(int ifindex)
+{
+ int fd;
+ struct sockaddr_ll sock;
+
+ DEBUG(LOG_INFO, "Opening raw socket on ifindex %d\n", ifindex);
+ if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
+ DEBUG(LOG_ERR, "socket call failed: %m");
+ return -1;
+ }
+
+ sock.sll_family = AF_PACKET;
+ sock.sll_protocol = htons(ETH_P_IP);
+ sock.sll_ifindex = ifindex;
+ if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
+ DEBUG(LOG_ERR, "bind call failed: %m");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
diff --git a/release/src/router/busybox/networking/udhcp/socket.h b/release/src/router/busybox/networking/udhcp/socket.h
new file mode 100644
index 00000000..333994b8
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/socket.h
@@ -0,0 +1,9 @@
+/* socket.h */
+#ifndef _SOCKET_H
+#define _SOCKET_H
+
+int read_interface(char *interface, int *ifindex, u_int32_t *addr, unsigned char *arp);
+int listen_socket(unsigned int ip, int port, char *inf);
+int raw_socket(int ifindex);
+
+#endif
diff --git a/release/src/router/busybox/networking/udhcp/version.h b/release/src/router/busybox/networking/udhcp/version.h
new file mode 100644
index 00000000..3862539f
--- /dev/null
+++ b/release/src/router/busybox/networking/udhcp/version.h
@@ -0,0 +1,6 @@
+#ifndef _UDHCP_VERSION_H
+#define _UDHCP_VERSION_H
+
+#define VERSION "0.9.9-pre"
+
+#endif
diff --git a/release/src/router/busybox/networking/vconfig.c b/release/src/router/busybox/networking/vconfig.c
new file mode 100644
index 00000000..d58c375f
--- /dev/null
+++ b/release/src/router/busybox/networking/vconfig.c
@@ -0,0 +1,150 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * vconfig implementation for busybox
+ *
+ * Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.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 of the License, 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
+ *
+ */
+
+/* BB_AUDIT SUSv3 N/A */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/if_vlan.h>
+#include <string.h>
+#include <limits.h>
+#include "busybox.h"
+
+#define VLAN_GROUP_ARRAY_LEN 4096
+#define SIOCSIFVLAN 0x8983 /* Set 802.1Q VLAN options */
+
+/* On entry, table points to the length of the current string plus
+ * nul terminator plus data length for the subsequent entry. The
+ * return value is the last data entry for the matching string. */
+static const char *xfind_str(const char *table, const char *str)
+{
+ while (strcasecmp(str, table+1) != 0) {
+ if (!*(table += table[0])) {
+ bb_show_usage();
+ }
+ }
+ return table - 1;
+}
+
+static const char cmds[] = {
+ 4, ADD_VLAN_CMD, 7,
+ 'a', 'd', 'd', 0,
+ 3, DEL_VLAN_CMD, 7,
+ 'r', 'e', 'm', 0,
+ 3, SET_VLAN_NAME_TYPE_CMD, 17,
+ 's', 'e', 't', '_',
+ 'n', 'a', 'm', 'e', '_',
+ 't', 'y', 'p', 'e', 0,
+ 4, SET_VLAN_FLAG_CMD, 12,
+ 's', 'e', 't', '_',
+ 'f', 'l', 'a', 'g', 0,
+ 5, SET_VLAN_EGRESS_PRIORITY_CMD, 18,
+ 's', 'e', 't', '_',
+ 'e', 'g', 'r', 'e', 's', 's', '_',
+ 'm', 'a', 'p', 0,
+ 5, SET_VLAN_INGRESS_PRIORITY_CMD, 16,
+ 's', 'e', 't', '_',
+ 'i', 'n', 'g', 'r', 'e', 's', 's', '_',
+ 'm', 'a', 'p', 0,
+};
+
+static const char name_types[] = {
+ VLAN_NAME_TYPE_PLUS_VID, 16,
+ 'V', 'L', 'A', 'N',
+ '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+ 0,
+ VLAN_NAME_TYPE_PLUS_VID_NO_PAD, 22,
+ 'V', 'L', 'A', 'N',
+ '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+ '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
+ VLAN_NAME_TYPE_RAW_PLUS_VID, 15,
+ 'D', 'E', 'V',
+ '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+ 0,
+ VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, 20,
+ 'D', 'E', 'V',
+ '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+ '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
+};
+
+static const char conf_file_name[] = "/proc/net/vlan/config";
+
+int vconfig_main(int argc, char **argv)
+{
+ struct vlan_ioctl_args ifr;
+ const char *p;
+ int fd;
+
+ if (argc < 3) {
+ bb_show_usage();
+ }
+
+ /* Don't bother closing the filedes. It will be closed on cleanup. */
+ if (open(conf_file_name, O_RDONLY) < 0) { /* Is 802.1q is present? */
+ bb_perror_msg_and_die("open %s", conf_file_name);
+ }
+
+ memset(&ifr, 0, sizeof(struct vlan_ioctl_args));
+
+ ++argv;
+ p = xfind_str(cmds+2, *argv);
+ ifr.cmd = *p;
+ if (argc != p[-1]) {
+ bb_show_usage();
+ }
+
+ if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) { /* set_name_type */
+ ifr.u.name_type = *xfind_str(name_types+1, argv[1]);
+ } else {
+ if (strlen(argv[1]) >= IF_NAMESIZE) {
+ bb_error_msg_and_die("if_name >= %d chars\n", IF_NAMESIZE);
+ }
+ strcpy(ifr.device1, argv[1]);
+ p = argv[2];
+
+ /* I suppose one could try to combine some of the function calls below,
+ * since ifr.u.flag, ifr.u.VID, and ifr.u.skb_priority are all same-sized
+ * (unsigned) int members of a unions. But because of the range checking,
+ * doing so wouldn't save that much space and would also make maintainence
+ * more of a pain. */
+ if (ifr.cmd == SET_VLAN_FLAG_CMD) { /* set_flag */
+ ifr.u.flag = bb_xgetularg10_bnd(p, 0, 1);
+ } else if (ifr.cmd == ADD_VLAN_CMD) { /* add */
+ ifr.u.VID = bb_xgetularg10_bnd(p, 0, VLAN_GROUP_ARRAY_LEN-1);
+ } else if (ifr.cmd != DEL_VLAN_CMD) { /* set_{egress|ingress}_map */
+ ifr.u.skb_priority = bb_xgetularg10_bnd(p, 0, ULONG_MAX);
+ ifr.vlan_qos = bb_xgetularg10_bnd(argv[3], 0, 7);
+ }
+ }
+
+ if (((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ || (ioctl(fd, SIOCSIFVLAN, &ifr) < 0)
+ ) {
+ bb_perror_msg_and_die("socket or ioctl error for %s", *argv);
+ }
+
+ return 0;
+}
+
diff --git a/release/src/router/busybox/networking/wget.c b/release/src/router/busybox/networking/wget.c
new file mode 100644
index 00000000..f1d363d2
--- /dev/null
+++ b/release/src/router/busybox/networking/wget.c
@@ -0,0 +1,828 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * wget - retrieve a file using HTTP or FTP
+ *
+ * Chip Rosenthal Covad Communications <chip@laserlink.net>
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <getopt.h>
+
+#include "busybox.h"
+
+struct host_info {
+ char *host;
+ int port;
+ char *path;
+ int is_ftp;
+ char *user;
+};
+
+static void parse_url(char *url, struct host_info *h);
+static FILE *open_socket(char *host, int port);
+static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc);
+static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf);
+
+/* Globals (can be accessed from signal handlers */
+static off_t filesize = 0; /* content-length of the file */
+static int chunked = 0; /* chunked transfer encoding */
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+static void progressmeter(int flag);
+static char *curfile; /* Name of current file being transferred. */
+static struct timeval start; /* Time a transfer started. */
+static volatile unsigned long statbytes = 0; /* Number of bytes transferred so far. */
+/* For progressmeter() -- number of seconds before xfer considered "stalled" */
+static const int STALLTIME = 5;
+#endif
+
+static void close_and_delete_outfile(FILE* output, char *fname_out, int do_continue)
+{
+ if (output != stdout && do_continue==0) {
+ fclose(output);
+ unlink(fname_out);
+ }
+}
+
+/* Read NMEMB elements of SIZE bytes into PTR from STREAM. Returns the
+ * number of elements read, and a short count if an eof or non-interrupt
+ * error is encountered. */
+static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+ size_t ret = 0;
+
+ do {
+ clearerr(stream);
+ ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream);
+ } while (ret < nmemb && ferror(stream) && errno == EINTR);
+
+ return ret;
+}
+
+/* Write NMEMB elements of SIZE bytes from PTR to STREAM. Returns the
+ * number of elements written, and a short count if an eof or non-interrupt
+ * error is encountered. */
+static size_t safe_fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+ size_t ret = 0;
+
+ do {
+ clearerr(stream);
+ ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream);
+ } while (ret < nmemb && ferror(stream) && errno == EINTR);
+
+ return ret;
+}
+
+/* Read a line or SIZE - 1 bytes into S, whichever is less, from STREAM.
+ * Returns S, or NULL if an eof or non-interrupt error is encountered. */
+static char *safe_fgets(char *s, int size, FILE *stream)
+{
+ char *ret;
+
+ do {
+ clearerr(stream);
+ ret = fgets(s, size, stream);
+ } while (ret == NULL && ferror(stream) && errno == EINTR);
+
+ return ret;
+}
+
+#define close_delete_and_die(s...) { \
+ close_and_delete_outfile(output, fname_out, do_continue); \
+ bb_error_msg_and_die(s); }
+
+
+#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION
+/*
+ * Base64-encode character string
+ * oops... isn't something similar in uuencode.c?
+ * It would be better to use already existing code
+ */
+char *base64enc(char *p, char *buf, int len) {
+
+ char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+ char *s = buf;
+
+ while(*p) {
+ if (s >= buf+len-4)
+ bb_error_msg_and_die("buffer overflow");
+ *(s++) = al[(*p >> 2) & 0x3F];
+ *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)];
+ *s = *(s+1) = '=';
+ *(s+2) = 0;
+ if (! *(++p)) break;
+ *(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)];
+ if (! *(++p)) break;
+ *(s++) = al[*(p++) & 0x3F];
+ }
+
+ return buf;
+}
+#endif
+
+int wget_main(int argc, char **argv)
+{
+ int n, try=5, status;
+ int port;
+ char *proxy = 0;
+ char *dir_prefix=NULL;
+ char *s, buf[512];
+ struct stat sbuf;
+ char extra_headers[1024];
+ char *extra_headers_ptr = extra_headers;
+ int extra_headers_left = sizeof(extra_headers);
+ int which_long_opt = 0, option_index = -1;
+ struct host_info server, target;
+
+ FILE *sfp = NULL; /* socket to web/ftp server */
+ FILE *dfp = NULL; /* socket to ftp server (data) */
+ char *fname_out = NULL; /* where to direct output (-O) */
+ int do_continue = 0; /* continue a prev transfer (-c) */
+ long beg_range = 0L; /* range at which continue begins */
+ int got_clen = 0; /* got content-length: from server */
+ FILE *output; /* socket to web server */
+ int quiet_flag = FALSE; /* Be verry, verry quiet... */
+ int noproxy = 0; /* Use proxies if env vars are set */
+
+#define LONG_HEADER 1
+#define LONG_PASSIVE 2
+
+ struct option long_options[] = {
+ { "continue", 0, NULL, 'c' },
+ { "quiet", 0, NULL, 'q' },
+ { "output-document", 1, NULL, 'O' },
+ { "header", 1, &which_long_opt, LONG_HEADER },
+ { "proxy", 1, NULL, 'Y' },
+ { "passive-ftp", 0, &which_long_opt, LONG_PASSIVE },
+ { 0, 0, 0, 0 }
+ };
+ /*
+ * Crack command line.
+ */
+ while ((n = getopt_long(argc, argv, "cqO:P:Y:", long_options, &option_index)) != EOF) {
+ switch (n) {
+ case 'c':
+ ++do_continue;
+ break;
+ case 'P':
+ dir_prefix = optarg;
+ break;
+ case 'q':
+ quiet_flag = TRUE;
+ break;
+ case 'O':
+ /* can't set fname_out to NULL if outputting to stdout, because
+ * this gets interpreted as the auto-gen output filename
+ * case below - tausq@debian.org
+ */
+ fname_out = optarg;
+ break;
+ case 'Y':
+ if (strcmp(optarg, "off") == 0)
+ noproxy=1;
+ break;
+ case 0:
+ switch (which_long_opt) {
+ case LONG_HEADER: {
+ int arglen = strlen(optarg);
+ if(extra_headers_left - arglen - 2 <= 0)
+ bb_error_msg_and_die("extra_headers buffer too small(need %i)", extra_headers_left - arglen);
+ strcpy(extra_headers_ptr, optarg);
+ extra_headers_ptr += arglen;
+ extra_headers_left -= ( arglen + 2 );
+ *extra_headers_ptr++ = '\r';
+ *extra_headers_ptr++ = '\n';
+ *(extra_headers_ptr + 1) = 0;
+ break;
+ }
+ case LONG_PASSIVE:
+ // ignore -- we always use passive mode
+ break;
+ }
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+
+ if (argc - optind != 1)
+ bb_show_usage();
+
+ parse_url(argv[optind], &target);
+ server.host = target.host;
+ server.port = target.port;
+
+ /*
+ * Use the proxy if necessary.
+ */
+ if (!noproxy) {
+ proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
+ if (proxy)
+ parse_url(bb_xstrdup(proxy), &server);
+ }
+
+ /* Guess an output filename */
+ if (!fname_out) {
+ fname_out =
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+ curfile =
+#endif
+ bb_get_last_path_component(target.path);
+ if (fname_out==NULL || strlen(fname_out)<1) {
+ fname_out =
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+ curfile =
+#endif
+ "index.html";
+ }
+ if (dir_prefix != NULL)
+ fname_out = concat_path_file(dir_prefix, fname_out);
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+ } else {
+ curfile = bb_get_last_path_component(fname_out);
+#endif
+ }
+ if (do_continue && !fname_out)
+ bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)");
+
+
+ /*
+ * Open the output file stream.
+ */
+ if (strcmp(fname_out, "-") == 0) {
+ output = stdout;
+ quiet_flag = TRUE;
+ } else {
+ output = bb_xfopen(fname_out, (do_continue ? "a" : "w"));
+ }
+
+ /*
+ * Determine where to start transfer.
+ */
+ if (do_continue) {
+ if (fstat(fileno(output), &sbuf) < 0)
+ bb_perror_msg_and_die("fstat()");
+ if (sbuf.st_size > 0)
+ beg_range = sbuf.st_size;
+ else
+ do_continue = 0;
+ }
+
+ if (proxy || !target.is_ftp) {
+ /*
+ * HTTP session
+ */
+ do {
+ if (! --try)
+ close_delete_and_die("too many redirections");
+
+ /*
+ * Open socket to http server
+ */
+ if (sfp) fclose(sfp);
+ sfp = open_socket(server.host, server.port);
+
+ /*
+ * Send HTTP request.
+ */
+ if (proxy) {
+ fprintf(sfp, "GET %stp://%s:%d/%s HTTP/1.1\r\n",
+ target.is_ftp ? "f" : "ht", target.host,
+ target.port, target.path);
+ } else {
+ fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
+ }
+
+ fprintf(sfp, "Host: %s\r\nUser-Agent: Wget\r\n", target.host);
+
+#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION
+ if (target.user) {
+ fprintf(sfp, "Authorization: Basic %s\r\n",
+ base64enc(target.user, buf, sizeof(buf)));
+ }
+ if (proxy && server.user) {
+ fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
+ base64enc(server.user, buf, sizeof(buf)));
+ }
+#endif
+
+ if (do_continue)
+ fprintf(sfp, "Range: bytes=%ld-\r\n", beg_range);
+ if(extra_headers_left < sizeof(extra_headers))
+ fputs(extra_headers,sfp);
+ fprintf(sfp,"Connection: close\r\n\r\n");
+
+ /*
+ * Retrieve HTTP response line and check for "200" status code.
+ */
+read_response: if (fgets(buf, sizeof(buf), sfp) == NULL)
+ close_delete_and_die("no response from server");
+
+ for (s = buf ; *s != '\0' && !isspace(*s) ; ++s)
+ ;
+ for ( ; isspace(*s) ; ++s)
+ ;
+ switch (status = atoi(s)) {
+ case 0:
+ case 100:
+ while (gethdr(buf, sizeof(buf), sfp, &n) != NULL);
+ goto read_response;
+ case 200:
+ if (do_continue && output != stdout)
+ output = freopen(fname_out, "w", output);
+ do_continue = 0;
+ break;
+ case 300: /* redirection */
+ case 301:
+ case 302:
+ case 303:
+ break;
+ case 206:
+ if (do_continue)
+ break;
+ /*FALLTHRU*/
+ default:
+ chomp(buf);
+ close_delete_and_die("server returned error %d: %s", atoi(s), buf);
+ }
+
+ /*
+ * Retrieve HTTP headers.
+ */
+ while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) {
+ if (strcasecmp(buf, "content-length") == 0) {
+ filesize = atol(s);
+ got_clen = 1;
+ continue;
+ }
+ if (strcasecmp(buf, "transfer-encoding") == 0) {
+ if (strcasecmp(s, "chunked") == 0) {
+ chunked = got_clen = 1;
+ } else {
+ close_delete_and_die("server wants to do %s transfer encoding", s);
+ }
+ }
+ if (strcasecmp(buf, "location") == 0) {
+ if (s[0] == '/')
+ target.path = bb_xstrdup(s+1);
+ else {
+ parse_url(bb_xstrdup(s), &target);
+ if (!proxy) {
+ server.host = target.host;
+ server.port = target.port;
+ }
+ }
+ }
+ }
+ } while(status >= 300);
+
+ dfp = sfp;
+ }
+ else
+ {
+ /*
+ * FTP session
+ */
+ if (! target.user)
+ target.user = bb_xstrdup("anonymous:busybox@");
+
+ sfp = open_socket(server.host, server.port);
+ if (ftpcmd(NULL, NULL, sfp, buf) != 220)
+ close_delete_and_die("%s", buf+4);
+
+ /*
+ * Splitting username:password pair,
+ * trying to log in
+ */
+ s = strchr(target.user, ':');
+ if (s)
+ *(s++) = '\0';
+ switch(ftpcmd("USER ", target.user, sfp, buf)) {
+ case 230:
+ break;
+ case 331:
+ if (ftpcmd("PASS ", s, sfp, buf) == 230)
+ break;
+ /* FALLTHRU (failed login) */
+ default:
+ close_delete_and_die("ftp login: %s", buf+4);
+ }
+
+ ftpcmd("CDUP", NULL, sfp, buf);
+ ftpcmd("TYPE I", NULL, sfp, buf);
+
+ /*
+ * Querying file size
+ */
+ if (ftpcmd("SIZE /", target.path, sfp, buf) == 213) {
+ filesize = atol(buf+4);
+ got_clen = 1;
+ }
+
+ /*
+ * Entering passive mode
+ */
+ if (ftpcmd("PASV", NULL, sfp, buf) != 227)
+ close_delete_and_die("PASV: %s", buf+4);
+ s = strrchr(buf, ',');
+ *s = 0;
+ port = atoi(s+1);
+ s = strrchr(buf, ',');
+ port += atoi(s+1) * 256;
+ dfp = open_socket(server.host, port);
+
+ if (do_continue) {
+ sprintf(buf, "REST %ld", beg_range);
+ if (ftpcmd(buf, NULL, sfp, buf) != 350) {
+ if (output != stdout)
+ output = freopen(fname_out, "w", output);
+ do_continue = 0;
+ } else
+ filesize -= beg_range;
+ }
+
+ if (ftpcmd("RETR /", target.path, sfp, buf) > 150)
+ close_delete_and_die("RETR: %s", buf+4);
+
+ }
+
+
+ /*
+ * Retrieve file
+ */
+ if (chunked) {
+ fgets(buf, sizeof(buf), dfp);
+ filesize = strtol(buf, (char **) NULL, 16);
+ }
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+ if (quiet_flag==FALSE)
+ progressmeter(-1);
+#endif
+ do {
+ while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, chunked ? (filesize > sizeof(buf) ? sizeof(buf) : filesize) : sizeof(buf), dfp)) > 0) {
+ if (safe_fwrite(buf, 1, n, output) != n)
+ bb_perror_msg_and_die("write error");
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+ statbytes+=n;
+#endif
+ if (got_clen)
+ filesize -= n;
+ }
+
+ if (chunked) {
+ safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
+ safe_fgets(buf, sizeof(buf), dfp);
+ filesize = strtol(buf, (char **) NULL, 16);
+ if (filesize==0) chunked = 0; /* all done! */
+ }
+
+ if (n == 0 && ferror(dfp))
+ bb_perror_msg_and_die("network read error");
+ } while (chunked);
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+ if (quiet_flag==FALSE)
+ progressmeter(1);
+#endif
+ if (!proxy && target.is_ftp) {
+ fclose(dfp);
+ if (ftpcmd(NULL, NULL, sfp, buf) != 226)
+ bb_error_msg_and_die("ftp error: %s", buf+4);
+ ftpcmd("QUIT", NULL, sfp, buf);
+ }
+ exit(EXIT_SUCCESS);
+}
+
+
+void parse_url(char *url, struct host_info *h)
+{
+ char *cp, *sp, *up;
+
+ if (strncmp(url, "http://", 7) == 0) {
+ h->port = 80;
+ h->host = url + 7;
+ h->is_ftp = 0;
+ } else if (strncmp(url, "ftp://", 6) == 0) {
+ h->port = 21;
+ h->host = url + 6;
+ h->is_ftp = 1;
+ } else
+ bb_error_msg_and_die("not an http or ftp url: %s", url);
+
+ sp = strchr(h->host, '/');
+ if (sp != NULL) {
+ *sp++ = '\0';
+ h->path = sp;
+ } else
+ h->path = bb_xstrdup("");
+
+ up = strrchr(h->host, '@');
+ if (up != NULL) {
+ h->user = h->host;
+ *up++ = '\0';
+ h->host = up;
+ } else
+ h->user = NULL;
+
+ cp = strchr(h->host, ':');
+ if (cp != NULL) {
+ *cp++ = '\0';
+ h->port = atoi(cp);
+ }
+
+}
+
+
+FILE *open_socket(char *host, int port)
+{
+ int fd;
+ FILE *fp;
+ char port_str[10];
+
+ snprintf(port_str, sizeof(port_str), "%d", port);
+ fd=xconnect(host, port_str);
+
+ /*
+ * Get the server onto a stdio stream.
+ */
+ if ((fp = fdopen(fd, "r+")) == NULL)
+ bb_perror_msg_and_die("fdopen()");
+
+ return fp;
+}
+
+
+char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc)
+{
+ char *s, *hdrval;
+ int c;
+
+ *istrunc = 0;
+
+ /* retrieve header line */
+ if (fgets(buf, bufsiz, fp) == NULL)
+ return NULL;
+
+ /* see if we are at the end of the headers */
+ for (s = buf ; *s == '\r' ; ++s)
+ ;
+ if (s[0] == '\n')
+ return NULL;
+
+ /* convert the header name to lower case */
+ for (s = buf ; isalnum(*s) || *s == '-' ; ++s)
+ *s = tolower(*s);
+
+ /* verify we are at the end of the header name */
+ if (*s != ':')
+ bb_error_msg_and_die("bad header line: %s", buf);
+
+ /* locate the start of the header value */
+ for (*s++ = '\0' ; *s == ' ' || *s == '\t' ; ++s)
+ ;
+ hdrval = s;
+
+ /* locate the end of header */
+ while (*s != '\0' && *s != '\r' && *s != '\n')
+ ++s;
+
+ /* end of header found */
+ if (*s != '\0') {
+ *s = '\0';
+ return hdrval;
+ }
+
+ /* Rats! The buffer isn't big enough to hold the entire header value. */
+ while (c = getc(fp), c != EOF && c != '\n')
+ ;
+ *istrunc = 1;
+ return hdrval;
+}
+
+static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf)
+{
+ char *p;
+
+ if (s1) {
+ if (!s2) s2="";
+ fprintf(fp, "%s%s\r\n", s1, s2);
+ fflush(fp);
+ }
+
+ do {
+ p = fgets(buf, 510, fp);
+ if (!p)
+ bb_perror_msg_and_die("fgets()");
+ } while (! isdigit(buf[0]) || buf[3] != ' ');
+
+ return atoi(buf);
+}
+
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+/* Stuff below is from BSD rcp util.c, as added to openshh.
+ * Original copyright notice is retained at the end of this file.
+ *
+ */
+
+
+static int
+getttywidth(void)
+{
+ struct winsize winsize;
+
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1)
+ return (winsize.ws_col ? winsize.ws_col : 80);
+ else
+ return (80);
+}
+
+static void
+updateprogressmeter(int ignore)
+{
+ int save_errno = errno;
+
+ progressmeter(0);
+ errno = save_errno;
+}
+
+static void
+alarmtimer(int wait)
+{
+ struct itimerval itv;
+
+ itv.it_value.tv_sec = wait;
+ itv.it_value.tv_usec = 0;
+ itv.it_interval = itv.it_value;
+ setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+
+static void
+progressmeter(int flag)
+{
+ static const char prefixes[] = " KMGTP";
+ static struct timeval lastupdate;
+ static off_t lastsize, totalsize;
+ struct timeval now, td, wait;
+ off_t cursize, abbrevsize;
+ double elapsed;
+ int ratio, barlength, i, remaining;
+ char buf[256];
+
+ if (flag == -1) {
+ (void) gettimeofday(&start, (struct timezone *) 0);
+ lastupdate = start;
+ lastsize = 0;
+ totalsize = filesize; /* as filesize changes.. */
+ }
+
+ (void) gettimeofday(&now, (struct timezone *) 0);
+ cursize = statbytes;
+ if (totalsize != 0 && !chunked) {
+ ratio = 100.0 * cursize / totalsize;
+ ratio = MAX(ratio, 0);
+ ratio = MIN(ratio, 100);
+ } else
+ ratio = 100;
+
+ snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio);
+
+ barlength = getttywidth() - 51;
+ if (barlength > 0) {
+ i = barlength * ratio / 100;
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ "|%.*s%*s|", i,
+ "*****************************************************************************"
+ "*****************************************************************************",
+ barlength - i, "");
+ }
+ i = 0;
+ abbrevsize = cursize;
+ while (abbrevsize >= 100000 && i < sizeof(prefixes)) {
+ i++;
+ abbrevsize >>= 10;
+ }
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5d %c%c ",
+ (int) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' :
+ 'B');
+
+ timersub(&now, &lastupdate, &wait);
+ if (cursize > lastsize) {
+ lastupdate = now;
+ lastsize = cursize;
+ if (wait.tv_sec >= STALLTIME) {
+ start.tv_sec += wait.tv_sec;
+ start.tv_usec += wait.tv_usec;
+ }
+ wait.tv_sec = 0;
+ }
+ timersub(&now, &start, &td);
+ elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
+
+ if (wait.tv_sec >= STALLTIME) {
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ " - stalled -");
+ } else if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalsize || chunked) {
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ " --:-- ETA");
+ } else {
+ remaining = (int) (totalsize / (statbytes / elapsed) - elapsed);
+ i = remaining / 3600;
+ if (i)
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ "%2d:", i);
+ else
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ " ");
+ i = remaining % 3600;
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ "%02d:%02d ETA", i / 60, i % 60);
+ }
+ write(fileno(stderr), buf, strlen(buf));
+
+ if (flag == -1) {
+ struct sigaction sa;
+ sa.sa_handler = updateprogressmeter;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGALRM, &sa, NULL);
+ alarmtimer(1);
+ } else if (flag == 1) {
+ alarmtimer(0);
+ statbytes = 0;
+ putc('\n', stderr);
+ }
+}
+#endif
+
+/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
+ * much of which was blatently stolen from openssh. */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: wget.c,v 1.1.3.1 2004/12/29 07:07:46 honor Exp $
+ */
+
+
+
+/*
+Local Variables:
+c-file-style: "linux"
+c-basic-offset: 4
+tab-width: 4
+End:
+*/
+
+
+