summaryrefslogtreecommitdiff
path: root/i/pc104/initrd/conf/busybox/networking
diff options
context:
space:
mode:
Diffstat (limited to 'i/pc104/initrd/conf/busybox/networking')
-rw-r--r--i/pc104/initrd/conf/busybox/networking/Config.in719
-rw-r--r--i/pc104/initrd/conf/busybox/networking/Kbuild40
-rw-r--r--i/pc104/initrd/conf/busybox/networking/arp.c496
-rw-r--r--i/pc104/initrd/conf/busybox/networking/arping.c417
-rw-r--r--i/pc104/initrd/conf/busybox/networking/dnsd.c426
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ether-wake.c284
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ftpgetput.c359
-rw-r--r--i/pc104/initrd/conf/busybox/networking/hostname.c101
-rw-r--r--i/pc104/initrd/conf/busybox/networking/httpd.c2049
-rw-r--r--i/pc104/initrd/conf/busybox/networking/httpd_index_cgi_example50
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ifconfig.c546
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ifupdown.c1273
-rw-r--r--i/pc104/initrd/conf/busybox/networking/inetd.c1791
-rw-r--r--i/pc104/initrd/conf/busybox/networking/interface.c1195
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ip.c50
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ipaddr.c26
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ipcalc.c195
-rw-r--r--i/pc104/initrd/conf/busybox/networking/iplink.c26
-rw-r--r--i/pc104/initrd/conf/busybox/networking/iproute.c26
-rw-r--r--i/pc104/initrd/conf/busybox/networking/iprule.c26
-rw-r--r--i/pc104/initrd/conf/busybox/networking/iptunnel.c26
-rw-r--r--i/pc104/initrd/conf/busybox/networking/isrv.c352
-rw-r--r--i/pc104/initrd/conf/busybox/networking/isrv.h33
-rw-r--r--i/pc104/initrd/conf/busybox/networking/isrv_identd.c144
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/Kbuild66
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ip_common.h35
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ip_parse_common_args.c79
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ipaddress.c824
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/iplink.c350
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/iproute.c878
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/iprule.c334
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/iptunnel.c547
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.c396
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.h43
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ll_addr.c85
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.c186
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.h13
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ll_proto.c121
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ll_types.c117
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.c365
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.h28
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.c110
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.h11
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/utils.c330
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/utils.h91
-rw-r--r--i/pc104/initrd/conf/busybox/networking/nameif.c171
-rw-r--r--i/pc104/initrd/conf/busybox/networking/nc.c197
-rw-r--r--i/pc104/initrd/conf/busybox/networking/netstat.c596
-rw-r--r--i/pc104/initrd/conf/busybox/networking/nslookup.c155
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ping.c752
-rw-r--r--i/pc104/initrd/conf/busybox/networking/route.c697
-rw-r--r--i/pc104/initrd/conf/busybox/networking/telnet.c665
-rw-r--r--i/pc104/initrd/conf/busybox/networking/telnetd.c573
-rw-r--r--i/pc104/initrd/conf/busybox/networking/tftp.c518
-rw-r--r--i/pc104/initrd/conf/busybox/networking/traceroute.c1342
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/Config.in75
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/Kbuild19
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/arpping.c114
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/clientpacket.c224
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/clientsocket.c51
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/common.c61
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/common.h104
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.c509
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.h50
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.c223
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.h190
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dhcprelay.c338
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/domain_codec.c205
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dumpleases.c75
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/files.c426
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/leases.c145
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/options.c180
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/options.h44
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/packet.c211
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/pidfile.c60
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/script.c224
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/serverpacket.c261
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/signalpipe.c77
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/socket.c126
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/static_leases.c99
-rw-r--r--i/pc104/initrd/conf/busybox/networking/vconfig.c165
-rw-r--r--i/pc104/initrd/conf/busybox/networking/wget.c808
-rw-r--r--i/pc104/initrd/conf/busybox/networking/zcip.c548
83 files changed, 26937 insertions, 0 deletions
diff --git a/i/pc104/initrd/conf/busybox/networking/Config.in b/i/pc104/initrd/conf/busybox/networking/Config.in
new file mode 100644
index 0000000..11e7812
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/Config.in
@@ -0,0 +1,719 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Networking Utilities"
+
+config FEATURE_IPV6
+ bool "Enable IPv6 support"
+ default n
+ help
+ Enable IPv6 support in busybox.
+ This adds IPv6 support in the networking applets.
+
+config VERBOSE_RESOLUTION_ERRORS
+ bool "Verbose resolution errors"
+ default n
+ help
+ Enable if you are not satisfied with simplistic
+ "can't resolve 'hostname.com'" and want to know more.
+ This may increase size of your executable a bit.
+
+config ARP
+ bool "arp"
+ default n
+ help
+ Manipulate the system ARP cache
+
+config ARPING
+ bool "arping"
+ default n
+ help
+ Ping hosts by ARP packets
+
+config DNSD
+ bool "dnsd"
+ default n
+ help
+ Small and static DNS server daemon.
+
+config ETHER_WAKE
+ bool "ether-wake"
+ default n
+ help
+ Send a magic packet to wake up sleeping machines.
+
+config FAKEIDENTD
+ bool "fakeidentd"
+ default n
+ select FEATURE_SYSLOG
+ help
+ fakeidentd listens on the ident port and returns a predefined
+ fake value on any query.
+
+config FTPGET
+ bool "ftpget"
+ default n
+ help
+ Retrieve a remote file via FTP.
+
+config FTPPUT
+ bool "ftpput"
+ default n
+ help
+ Store a remote file via FTP.
+
+config FEATURE_FTPGETPUT_LONG_OPTIONS
+ bool "Enable long options in ftpget/ftpput"
+ default n
+ depends on GETOPT_LONG && (FTPGET || FTPPUT)
+ help
+ Support long options for the ftpget/ftpput applet.
+
+config HOSTNAME
+ bool "hostname"
+ default n
+ help
+ Show or set the system's host name
+
+config HTTPD
+ bool "httpd"
+ default n
+ help
+ Serve web pages via an HTTP server.
+
+config FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+ bool "Support reloading the global config file using hup signal"
+ default n
+ depends on HTTPD
+ help
+ This option enables processing of SIGHUP to reload cached
+ configuration settings.
+
+config FEATURE_HTTPD_SETUID
+ bool "Enable -u <user> option"
+ default n
+ depends on 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 privileges to change to a
+ different user.
+
+config FEATURE_HTTPD_BASIC_AUTH
+ bool "Enable Basic http Authentication"
+ default y
+ depends on HTTPD
+ help
+ Utilizes password settings from /etc/httpd.conf for basic
+ authentication on a per url basis.
+
+config FEATURE_HTTPD_AUTH_MD5
+ bool "Support MD5 crypted passwords for http Authentication"
+ default n
+ depends on FEATURE_HTTPD_BASIC_AUTH
+ help
+ Enables basic per URL authentication from /etc/httpd.conf
+ using md5 passwords.
+
+config FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ bool "Support loading additional MIME types at run-time"
+ default n
+ depends on HTTPD
+ help
+ This option enables support for additional MIME types at
+ run-time to be specified in the configuration file.
+
+config FEATURE_HTTPD_CGI
+ bool "Support Common Gateway Interface (CGI)"
+ default y
+ depends on HTTPD
+ help
+ This option allows scripts and executables to be invoked
+ when specific URLs are requested.
+
+config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ bool "Enable support for running scripts through an interpreter"
+ default n
+ depends on FEATURE_HTTPD_CGI
+ help
+ This option enables support for running scripts through an
+ interpreter. Turn this on if you want PHP scripts to work
+ properly. You need to supply an addition line in your httpd
+ config file:
+ *.php:/path/to/your/php
+
+config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+ bool "Support the REMOTE_PORT environment variable for CGI"
+ default n
+ depends on FEATURE_HTTPD_CGI
+ help
+ Use of this option can assist scripts in generating
+ references that contain a unique port number.
+
+config FEATURE_HTTPD_ENCODE_URL_STR
+ bool "Enable the -e option for shell script CGI simplification."
+ default y
+ depends on HTTPD
+ help
+ 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 IFCONFIG
+ bool "ifconfig"
+ default n
+ help
+ Ifconfig is used to configure the kernel-resident network interfaces.
+
+config FEATURE_IFCONFIG_STATUS
+ bool "Enable status reporting output (+7k)"
+ default y
+ depends on IFCONFIG
+ help
+ If ifconfig is called with no arguments it will display the status
+ of the currently active interfaces.
+
+config FEATURE_IFCONFIG_SLIP
+ bool "Enable slip-specific options \"keepalive\" and \"outfill\""
+ default n
+ depends on IFCONFIG
+ help
+ Allow "keepalive" and "outfill" support for SLIP. If you're not
+ planning on using serial lines, leave this unchecked.
+
+config FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ bool "Enable options \"mem_start\", \"io_addr\", and \"irq\""
+ default n
+ depends on 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 FEATURE_IFCONFIG_HW
+ bool "Enable option \"hw\" (ether only)"
+ default y
+ depends on IFCONFIG
+ help
+ Set the hardware address of this interface, if the device driver
+ supports this operation. Currently, we only support the 'ether'
+ class.
+
+config FEATURE_IFCONFIG_BROADCAST_PLUS
+ bool "Set the broadcast automatically"
+ default n
+ depends on IFCONFIG
+ help
+ Setting this will make ifconfig attempt to find the broadcast
+ automatically if the value '+' is used.
+
+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 IFCONFIG and ROUTE, or enable
+ FEATURE_IFUPDOWN_IP and the various 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", "route" and "run-parts" or the "ip" command, either
+ via busybox or via
+ standalone utilities.
+
+config FEATURE_IFUPDOWN_IP
+ bool "Use ip applet"
+ default n
+ depends on IFUPDOWN
+ help
+ Use the iproute "ip" command to implement "ifup" and "ifdown", rather
+ than the default of using the older 'ifconfig' and 'route' utilities.
+
+config FEATURE_IFUPDOWN_IP_BUILTIN
+ bool "Use busybox ip applet"
+ default y
+ depends on FEATURE_IFUPDOWN_IP
+ select IP
+ select FEATURE_IP_ADDRESS
+ select FEATURE_IP_LINK
+ select FEATURE_IP_ROUTE
+ help
+ Use the busybox iproute "ip" applet to implement "ifupdown".
+
+ If left disabled, you must install the full-blown iproute2
+ utility or the "ifup" and "ifdown" applets will not work.
+
+config FEATURE_IFUPDOWN_IFCONFIG_BUILTIN
+ bool "Use busybox ifconfig and route applets"
+ default y
+ depends on IFUPDOWN && !FEATURE_IFUPDOWN_IP
+ select IFCONFIG
+ select ROUTE
+ help
+ Use the busybox iproute "ifconfig" and "route" applets to
+ implement the "ifup" and "ifdown" utilities.
+
+ If left disabled, you must install the full-blown ifconfig
+ and route utilities, or the "ifup" and "ifdown" applets will not
+ work.
+
+config FEATURE_IFUPDOWN_IPV4
+ bool "Enable support for IPv4"
+ default y
+ depends on IFUPDOWN
+ help
+ If you want busybox to talk IPv4, leave this on.
+
+config FEATURE_IFUPDOWN_IPV6
+ bool "Enable support for IPv6"
+ default n
+ depends on IFUPDOWN && FEATURE_IPV6
+ help
+ If you need support for IPv6, turn this option on.
+
+config FEATURE_IFUPDOWN_IPX
+ bool "Enable support for IPX"
+ default n
+ depends on IFUPDOWN
+ help
+ If this option is selected you can use busybox to work with IPX
+ networks.
+
+config FEATURE_IFUPDOWN_MAPPING
+ bool "Enable mapping support"
+ default n
+ depends on IFUPDOWN
+ help
+ This enables support for the "mapping" stanza, unless you have
+ a weird network setup you don't need it.
+
+config INETD
+ bool "inetd"
+ default n
+ select FEATURE_SYSLOG
+ help
+ Internet superserver daemon
+
+config FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+ bool "Support echo service"
+ default y
+ depends on INETD
+ help
+ Echo received data internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+ bool "Support discard service"
+ default y
+ depends on INETD
+ help
+ Internet /dev/null internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_TIME
+ bool "Support time service"
+ default y
+ depends on INETD
+ help
+ Return 32 bit time since 1900 internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+ bool "Support daytime service"
+ default y
+ depends on INETD
+ help
+ Return human-readable time internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+ bool "Support chargen service"
+ default y
+ depends on INETD
+ help
+ Familiar character generator internal inetd service
+
+config FEATURE_INETD_RPC
+ bool "Support RPC services"
+ default n
+ depends on INETD && FEATURE_HAVE_RPC
+ help
+ Support Sun-RPC based services
+
+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.
+
+config FEATURE_IP_ADDRESS
+ bool "ip address"
+ default y
+ depends on IP
+ help
+ Address manipulation support for the "ip" applet.
+
+config FEATURE_IP_LINK
+ bool "ip link"
+ default y
+ depends on IP
+ help
+ Configure network devices with "ip".
+
+config FEATURE_IP_ROUTE
+ bool "ip route"
+ default y
+ depends on IP
+ help
+ Add support for routing table management to "ip".
+
+config FEATURE_IP_TUNNEL
+ bool "ip tunnel"
+ default n
+ depends on IP
+ help
+ Add support for tunneling commands to "ip".
+
+config FEATURE_IP_RULE
+ bool "ip rule"
+ default n
+ depends on IP
+ help
+ Add support for rule commands to "ip".
+
+config FEATURE_IP_SHORT_FORMS
+ bool "Support short forms of ip commands."
+ default n
+ depends on IP
+ help
+ Also support short-form of ip <OBJECT> commands:
+ ip addr -> ipaddr
+ ip link -> iplink
+ ip route -> iproute
+ ip tunnel -> iptunnel
+ ip rule -> iprule
+
+ Say N unless you desparately need the short form of the ip
+ object commands.
+
+config IPADDR
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ADDRESS
+
+config IPLINK
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_LINK
+
+config IPROUTE
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ROUTE
+
+config IPTUNNEL
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_TUNNEL
+
+config IPRULE
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_RULE
+
+config IPCALC
+ bool "ipcalc"
+ default n
+ help
+ ipcalc takes an IP address and netmask and calculates the
+ resulting broadcast, network, and host range.
+
+config FEATURE_IPCALC_FANCY
+ bool "Fancy IPCALC, more options, adds 1 kbyte"
+ default y
+ depends on IPCALC
+ help
+ Adds the options hostname, prefix and silent to the output of "ipcalc".
+
+config FEATURE_IPCALC_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on IPCALC && GETOPT_LONG
+ help
+ Support long options for the ipcalc applet.
+
+config NAMEIF
+ bool "nameif"
+ default n
+ select FEATURE_SYSLOG
+ help
+ nameif is used to rename network interface by its MAC address.
+ Renamed interfaces MUST be in the down state.
+ It is possible to use a file (default: /etc/mactab)
+ with list of new interface names and MACs.
+ Maximum interface name length: IF_NAMESIZE = 16
+ File fields are separated by space or tab.
+ File format:
+ # Comment
+ new_interface_name XX:XX:XX:XX:XX:XX
+
+config NC
+ bool "nc"
+ default n
+ help
+ A simple Unix utility which reads and writes data across network
+ connections.
+
+config NC_SERVER
+ bool "Netcat server options (-lp)"
+ default n
+ depends on NC
+ help
+ Allow netcat to act as a server.
+
+config NC_EXTRA
+ bool "Netcat extensions (-eiw and filename)"
+ default n
+ depends on NC
+ help
+ Add -e (support for executing the rest of the command line after
+ making or receiving a successful connection), -i (delay interval for
+ lines sent), -w (timeout for initial connection).
+
+config NETSTAT
+ bool "netstat"
+ default n
+ help
+ netstat prints information about the Linux networking subsystem.
+
+config NSLOOKUP
+ bool "nslookup"
+ default n
+ help
+ nslookup is a tool to query Internet name servers.
+
+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 PING6
+ bool "ping6"
+ default n
+ depends on FEATURE_IPV6 && PING
+ help
+ This will give you a ping that can talk IPv6.
+
+config FEATURE_FANCY_PING
+ bool "Enable fancy ping output"
+ default y
+ depends on PING
+ help
+ Make the output from the ping applet include statistics, and at the
+ same time provide full support for ICMP packets.
+
+config ROUTE
+ bool "route"
+ default n
+ help
+ Route displays or manipulates the kernel's IP routing tables.
+
+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 FEATURE_TELNET_TTYPE
+ bool "Pass TERM type to remote host"
+ default y
+ depends on 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 FEATURE_TELNET_AUTOLOGIN
+ bool "Pass USER type to remote host"
+ default y
+ depends on TELNET
+ help
+ Setting this option will forward the USER environment variable to the
+ remote host you are connecting to. This is useful when you need to
+ log into a machine without telling the username (autologin). This
+ option enables `-a' and `-l USER' arguments.
+
+config TELNETD
+ bool "telnetd"
+ default n
+ select FEATURE_SYSLOG
+ help
+ A daemon for the TELNET protocol, allowing you to log onto 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 an
+ SSH daemon and you trust your network, you may say 'y' here. As a
+ more secure alternative, you should seriously consider installing the
+ very small Dropbear SSH daemon instead:
+ http://matt.ucc.asn.au/dropbear/dropbear.html
+
+ Note that for busybox telnetd to work you need several things:
+ First of all, your kernel needs:
+ UNIX98_PTYS=y
+ DEVPTS_FS=y
+
+ Next, you need a /dev/pts directory on your root filesystem:
+
+ $ ls -ld /dev/pts
+ drwxr-xr-x 2 root root 0 Sep 23 13:21 /dev/pts/
+
+ Next you need the pseudo terminal master multiplexer /dev/ptmx:
+
+ $ ls -la /dev/ptmx
+ crw-rw-rw- 1 root tty 5, 2 Sep 23 13:55 /dev/ptmx
+
+ Any /dev/ttyp[0-9]* files you may have can be removed.
+ Next, you need to mount the devpts filesystem on /dev/pts using:
+
+ mount -t devpts devpts /dev/pts
+
+ You need to be sure that Busybox has LOGIN and
+ FEATURE_SUID enabled. And finally, you should make
+ certain that Busybox has been installed setuid root:
+
+ chown root.root /bin/busybox
+ chmod 4755 /bin/busybox
+
+ with all that done, telnetd _should_ work....
+
+
+config FEATURE_TELNETD_STANDALONE
+ bool "Support standalone telnetd (not inetd only)"
+ default n
+ depends on TELNETD
+ help
+ Selecting this will make telnetd able to run standalone.
+
+config TFTP
+ bool "tftp"
+ default n
+ help
+ This enables the Trivial File Transfer Protocol client program. TFTP
+ is usually used for simple, small transfers such as a root image
+ for a network-enabled bootloader.
+
+config FEATURE_TFTP_GET
+ bool "Enable \"get\" command"
+ default y
+ depends on TFTP
+ help
+ Add support for the GET command within the TFTP client. This allows
+ a client to retrieve a file from a TFTP server.
+
+config FEATURE_TFTP_PUT
+ bool "Enable \"put\" command"
+ default y
+ depends on 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 FEATURE_TFTP_BLOCKSIZE
+ bool "Enable \"blocksize\" command"
+ default n
+ depends on TFTP
+ help
+ Allow the client to specify the desired block size for transfers.
+
+config DEBUG_TFTP
+ bool "Enable debug"
+ default n
+ depends on 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 TRACEROUTE
+ bool "traceroute"
+ default n
+ help
+ Utility to trace the route of IP packets
+
+config FEATURE_TRACEROUTE_VERBOSE
+ bool "Enable verbose output"
+ default n
+ depends on TRACEROUTE
+ help
+ Add some verbosity to traceroute. This includes amongst other things
+ hostnames and ICMP response types.
+
+config FEATURE_TRACEROUTE_SOURCE_ROUTE
+ bool "Enable loose source route"
+ default n
+ depends on TRACEROUTE
+ help
+ Add option to specify a loose source route gateway
+ (8 maximum).
+
+config FEATURE_TRACEROUTE_USE_ICMP
+ bool "Use ICMP instead of UDP"
+ default n
+ depends on TRACEROUTE
+ help
+ Add feature to allow for ICMP ECHO instead of UDP datagrams.
+
+source networking/udhcp/Config.in
+
+config VCONFIG
+ bool "vconfig"
+ default n
+ help
+ Creates, removes, and configures VLAN interfaces
+
+config WGET
+ bool "wget"
+ default n
+ help
+ wget is a utility for non-interactive download of files from HTTP,
+ HTTPS, and FTP servers.
+
+config FEATURE_WGET_STATUSBAR
+ bool "Enable a nifty process meter (+2k)"
+ default y
+ depends on WGET
+ help
+ Enable the transfer progress bar for wget transfers.
+
+config FEATURE_WGET_AUTHENTICATION
+ bool "Enable HTTP authentication"
+ default y
+ depends on WGET
+ help
+ Support authenticated HTTP transfers.
+
+config FEATURE_WGET_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on WGET && GETOPT_LONG
+ help
+ Support long options for the wget applet.
+
+config ZCIP
+ bool "zcip"
+ default n
+ select FEATURE_SYSLOG
+ help
+ ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927.
+ It's a daemon that allocates and defends a dynamically assigned
+ address on the 169.254/16 network, requiring no system administrator.
+
+ See http://www.zeroconf.org for further details, and "zcip.script"
+ in the busybox examples.
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/networking/Kbuild b/i/pc104/initrd/conf/busybox/networking/Kbuild
new file mode 100644
index 0000000..68d3613
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/Kbuild
@@ -0,0 +1,40 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_ARP) += arp.o interface.o
+lib-$(CONFIG_ARPING) += arping.o
+lib-$(CONFIG_DNSD) += dnsd.o
+lib-$(CONFIG_ETHER_WAKE) += ether-wake.o
+lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o
+lib-$(CONFIG_FTPGET) += ftpgetput.o
+lib-$(CONFIG_FTPPUT) += ftpgetput.o
+lib-$(CONFIG_HOSTNAME) += hostname.o
+lib-$(CONFIG_HTTPD) += httpd.o
+lib-$(CONFIG_IFCONFIG) += ifconfig.o interface.o
+lib-$(CONFIG_IFUPDOWN) += ifupdown.o
+lib-$(CONFIG_INETD) += inetd.o
+lib-$(CONFIG_IP) += ip.o
+lib-$(CONFIG_IPCALC) += ipcalc.o
+lib-$(CONFIG_IPADDR) += ipaddr.o
+lib-$(CONFIG_IPLINK) += iplink.o
+lib-$(CONFIG_IPROUTE) += iproute.o
+lib-$(CONFIG_IPRULE) += iprule.o
+lib-$(CONFIG_IPTUNNEL) += iptunnel.o
+lib-$(CONFIG_NAMEIF) += nameif.o
+lib-$(CONFIG_NC) += nc.o
+lib-$(CONFIG_NETSTAT) += netstat.o
+lib-$(CONFIG_NSLOOKUP) += nslookup.o
+lib-$(CONFIG_PING) += ping.o
+lib-$(CONFIG_PING6) += ping.o
+lib-$(CONFIG_ROUTE) += route.o
+lib-$(CONFIG_TELNET) += telnet.o
+lib-$(CONFIG_TELNETD) += telnetd.o
+lib-$(CONFIG_TFTP) += tftp.o
+lib-$(CONFIG_TRACEROUTE) += traceroute.o
+lib-$(CONFIG_VCONFIG) += vconfig.o
+lib-$(CONFIG_WGET) += wget.o
+lib-$(CONFIG_ZCIP) += zcip.o
diff --git a/i/pc104/initrd/conf/busybox/networking/arp.c b/i/pc104/initrd/conf/busybox/networking/arp.c
new file mode 100644
index 0000000..7a36f44
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/arp.c
@@ -0,0 +1,496 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * arp.c - Manipulate the system ARP cache
+ *
+ * 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: Fred N. van Kempen, <waltje at uwalt.nl.mugnet.org>
+ * Busybox port: Paul van Gool <pvangool at mimotech.com>
+ *
+ * modified for getopt32 by Arne Bernin <arne [at] alamut.de>
+ */
+
+#include "busybox.h"
+#include "inet_common.h"
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+
+#define DEBUG 0
+
+#define DFLT_AF "inet"
+#define DFLT_HW "ether"
+
+#define ARP_OPT_A (0x1)
+#define ARP_OPT_p (0x2)
+#define ARP_OPT_H (0x4)
+#define ARP_OPT_t (0x8)
+#define ARP_OPT_i (0x10)
+#define ARP_OPT_a (0x20)
+#define ARP_OPT_d (0x40)
+#define ARP_OPT_n (0x80) /* do not resolve addresses */
+#define ARP_OPT_D (0x100) /* HW-address is devicename */
+#define ARP_OPT_s (0x200)
+#define ARP_OPT_v (0x400 * DEBUG) /* debugging output flag */
+
+
+static const struct aftype *ap; /* current address family */
+static const struct hwtype *hw; /* current hardware type */
+static int sockfd; /* active socket descriptor */
+static smallint hw_set; /* flag if hw-type was set (-H) */
+static const char *device = ""; /* current device */
+
+static const char *const options[] = {
+ "pub",
+ "priv",
+ "temp",
+ "trail",
+ "dontpub",
+ "auto",
+ "dev",
+ "netmask",
+ NULL
+};
+
+/* Delete an entry from the ARP cache. */
+/* Called only from main, once */
+static int arp_del(char **args)
+{
+ char *host;
+ struct arpreq req;
+ struct sockaddr sa;
+ int flags = 0;
+ int err;
+
+ memset(&req, 0, sizeof(req));
+
+ /* Resolve the host name. */
+ host = *args;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+
+ /* If a host has more than one address, use the correct one! */
+ memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
+
+ if (hw_set)
+ req.arp_ha.sa_family = hw->type;
+
+ req.arp_flags = ATF_PERM;
+ args++;
+ while (*args != NULL) {
+ switch (index_in_str_array(options, *args)) {
+ case 0: /* "pub" */
+ flags |= 1;
+ args++;
+ break;
+ case 1: /* "priv" */
+ flags |= 2;
+ args++;
+ break;
+ case 2: /* "temp" */
+ req.arp_flags &= ~ATF_PERM;
+ args++;
+ break;
+ case 3: /* "trail" */
+ req.arp_flags |= ATF_USETRAILERS;
+ args++;
+ break;
+ case 4: /* "dontpub" */
+#ifdef HAVE_ATF_DONTPUB
+ req.arp_flags |= ATF_DONTPUB;
+#else
+ bb_error_msg("feature ATF_DONTPUB is not supported");
+#endif
+ args++;
+ break;
+ case 5: /* "auto" */
+#ifdef HAVE_ATF_MAGIC
+ req.arp_flags |= ATF_MAGIC;
+#else
+ bb_error_msg("feature ATF_MAGIC is not supported");
+#endif
+ args++;
+ break;
+ case 6: /* "dev" */
+ if (*++args == NULL)
+ bb_show_usage();
+ device = *args;
+ args++;
+ break;
+ case 7: /* "netmask" */
+ if (*++args == NULL)
+ bb_show_usage();
+ if (strcmp(*args, "255.255.255.255") != 0) {
+ host = *args;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+ memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
+ req.arp_flags |= ATF_NETMASK;
+ }
+ args++;
+ break;
+ default:
+ bb_show_usage();
+ break;
+ }
+ }
+ if (flags == 0)
+ flags = 3;
+
+ strncpy(req.arp_dev, device, sizeof(req.arp_dev));
+
+ err = -1;
+
+ /* Call the kernel. */
+ if (flags & 2) {
+ if (option_mask32 & ARP_OPT_v)
+ bb_error_msg("SIOCDARP(nopub)");
+ err = ioctl(sockfd, SIOCDARP, &req);
+ if (err < 0) {
+ if (errno == ENXIO) {
+ if (flags & 1)
+ goto nopub;
+ printf("No ARP entry for %s\n", host);
+ return -1;
+ }
+ bb_perror_msg_and_die("SIOCDARP(priv)");
+ }
+ }
+ if ((flags & 1) && err) {
+ nopub:
+ req.arp_flags |= ATF_PUBL;
+ if (option_mask32 & ARP_OPT_v)
+ bb_error_msg("SIOCDARP(pub)");
+ if (ioctl(sockfd, SIOCDARP, &req) < 0) {
+ if (errno == ENXIO) {
+ printf("No ARP entry for %s\n", host);
+ return -1;
+ }
+ bb_perror_msg_and_die("SIOCDARP(pub)");
+ }
+ }
+ return 0;
+}
+
+/* Get the hardware address to a specified interface name */
+static void arp_getdevhw(char *ifname, struct sockaddr *sa,
+ const struct hwtype *hwt)
+{
+ struct ifreq ifr;
+ const struct hwtype *xhw;
+
+ strcpy(ifr.ifr_name, ifname);
+ if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
+ bb_perror_msg_and_die("cant get HW-Address for '%s'", ifname);
+ }
+ if (hwt && (ifr.ifr_hwaddr.sa_family != hw->type)) {
+ bb_error_msg_and_die("protocol type mismatch");
+ }
+ memcpy(sa, &(ifr.ifr_hwaddr), sizeof(struct sockaddr));
+
+ if (option_mask32 & ARP_OPT_v) {
+ xhw = get_hwntype(ifr.ifr_hwaddr.sa_family);
+ if (!xhw || !xhw->print) {
+ xhw = get_hwntype(-1);
+ }
+ bb_error_msg("device '%s' has HW address %s '%s'",
+ ifname, xhw->name,
+ xhw->print((char *) &ifr.ifr_hwaddr.sa_data));
+ }
+}
+
+/* Set an entry in the ARP cache. */
+/* Called only from main, once */
+static int arp_set(char **args)
+{
+ char *host;
+ struct arpreq req;
+ struct sockaddr sa;
+ int flags;
+
+ memset(&req, 0, sizeof(req));
+
+ host = *args++;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+ /* If a host has more than one address, use the correct one! */
+ memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
+
+ /* Fetch the hardware address. */
+ if (*args == NULL) {
+ bb_error_msg_and_die("need hardware address");
+ }
+ if (option_mask32 & ARP_OPT_D) {
+ arp_getdevhw(*args++, &req.arp_ha, hw_set ? hw : NULL);
+ } else {
+ if (hw->input(*args++, &req.arp_ha) < 0) {
+ bb_error_msg_and_die("invalid hardware address");
+ }
+ }
+
+ /* Check out any modifiers. */
+ flags = ATF_PERM | ATF_COM;
+ while (*args != NULL) {
+ switch (index_in_str_array(options, *args)) {
+ case 0: /* "pub" */
+ flags |= ATF_PUBL;
+ args++;
+ break;
+ case 1: /* "priv" */
+ flags &= ~ATF_PUBL;
+ args++;
+ break;
+ case 2: /* "temp" */
+ flags &= ~ATF_PERM;
+ args++;
+ break;
+ case 3: /* "trail" */
+ flags |= ATF_USETRAILERS;
+ args++;
+ break;
+ case 4: /* "dontpub" */
+#ifdef HAVE_ATF_DONTPUB
+ flags |= ATF_DONTPUB;
+#else
+ bb_error_msg("feature ATF_DONTPUB is not supported");
+#endif
+ args++;
+ break;
+ case 5: /* "auto" */
+#ifdef HAVE_ATF_MAGIC
+ flags |= ATF_MAGIC;
+#else
+ bb_error_msg("feature ATF_MAGIC is not supported");
+#endif
+ args++;
+ break;
+ case 6: /* "dev" */
+ if (*++args == NULL)
+ bb_show_usage();
+ device = *args;
+ args++;
+ break;
+ case 7: /* "netmask" */
+ if (*++args == NULL)
+ bb_show_usage();
+ if (strcmp(*args, "255.255.255.255") != 0) {
+ host = *args;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+ memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
+ flags |= ATF_NETMASK;
+ }
+ args++;
+ break;
+ default:
+ bb_show_usage();
+ break;
+ }
+ }
+
+ /* Fill in the remainder of the request. */
+ req.arp_flags = flags;
+
+ strncpy(req.arp_dev, device, sizeof(req.arp_dev));
+
+ /* Call the kernel. */
+ if (option_mask32 & ARP_OPT_v)
+ bb_error_msg("SIOCSARP()");
+ if (ioctl(sockfd, SIOCSARP, &req) < 0) {
+ bb_perror_msg_and_die("SIOCSARP");
+ }
+ return 0;
+}
+
+
+/* Print the contents of an ARP request block. */
+static void
+arp_disp(const char *name, char *ip, int type, int arp_flags,
+ char *hwa, char *mask, char *dev)
+{
+ const struct hwtype *xhw;
+
+ xhw = get_hwntype(type);
+ if (xhw == NULL)
+ xhw = get_hwtype(DFLT_HW);
+
+ printf("%s (%s) at ", name, ip);
+
+ if (!(arp_flags & ATF_COM)) {
+ if (arp_flags & ATF_PUBL)
+ printf("* ");
+ else
+ printf("<incomplete> ");
+ } else {
+ printf("%s [%s] ", hwa, xhw->name);
+ }
+
+ if (arp_flags & ATF_NETMASK)
+ printf("netmask %s ", mask);
+
+ if (arp_flags & ATF_PERM)
+ printf("PERM ");
+ if (arp_flags & ATF_PUBL)
+ printf("PUP ");
+#ifdef HAVE_ATF_MAGIC
+ if (arp_flags & ATF_MAGIC)
+ printf("AUTO ");
+#endif
+#ifdef HAVE_ATF_DONTPUB
+ if (arp_flags & ATF_DONTPUB)
+ printf("DONTPUB ");
+#endif
+ if (arp_flags & ATF_USETRAILERS)
+ printf("TRAIL ");
+
+ printf("on %s\n", dev);
+}
+
+/* Display the contents of the ARP cache in the kernel. */
+/* Called only from main, once */
+static int arp_show(char *name)
+{
+ const char *host;
+ const char *hostname;
+ FILE *fp;
+ struct sockaddr sa;
+ int type, flags;
+ int num;
+ unsigned entries = 0, shown = 0;
+ char ip[128];
+ char hwa[128];
+ char mask[128];
+ char line[128];
+ char dev[128];
+
+ host = NULL;
+ if (name != NULL) {
+ /* Resolve the host name. */
+ if (ap->input(name, &sa) < 0) {
+ bb_herror_msg_and_die("%s", name);
+ }
+ host = xstrdup(ap->sprint(&sa, 1));
+ }
+ fp = xfopen("/proc/net/arp", "r");
+ /* Bypass header -- read one line */
+ fgets(line, sizeof(line), fp);
+
+ /* Read the ARP cache entries. */
+ while (fgets(line, sizeof(line), fp)) {
+
+ mask[0] = '-'; mask[1] = '\0';
+ dev[0] = '-'; dev[1] = '\0';
+ /* All these strings can't overflow
+ * because fgets above reads limited amount of data */
+ num = sscanf(line, "%s 0x%x 0x%x %s %s %s\n",
+ ip, &type, &flags, hwa, mask, dev);
+ if (num < 4)
+ break;
+
+ entries++;
+ /* if the user specified hw-type differs, skip it */
+ if (hw_set && (type != hw->type))
+ continue;
+
+ /* if the user specified address differs, skip it */
+ if (host && strcmp(ip, host) != 0)
+ continue;
+
+ /* if the user specified device differs, skip it */
+ if (device[0] && strcmp(dev, device) != 0)
+ continue;
+
+ shown++;
+ /* This IS ugly but it works -be */
+ hostname = "?";
+ if (!(option_mask32 & ARP_OPT_n)) {
+ if (ap->input(ip, &sa) < 0)
+ hostname = ip;
+ else
+ hostname = ap->sprint(&sa, (option_mask32 & ARP_OPT_n) | 0x8000);
+ if (strcmp(hostname, ip) == 0)
+ hostname = "?";
+ }
+
+ arp_disp(hostname, ip, type, flags, hwa, mask, dev);
+ }
+ if (option_mask32 & ARP_OPT_v)
+ printf("Entries: %d\tSkipped: %d\tFound: %d\n",
+ entries, entries - shown, shown);
+
+ if (!shown) {
+ if (hw_set || host || device[0])
+ printf("No match found in %d entries\n", entries);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free((char*)host);
+ fclose(fp);
+ }
+ return 0;
+}
+
+int arp_main(int argc, char **argv);
+int arp_main(int argc, char **argv)
+{
+ char *hw_type;
+ char *protocol;
+
+ /* Initialize variables... */
+ ap = get_aftype(DFLT_AF);
+ if (!ap)
+ bb_error_msg_and_die("%s: %s not supported", DFLT_AF, "address family");
+
+ getopt32(argc, argv, "A:p:H:t:i:adnDsv", &protocol, &protocol,
+ &hw_type, &hw_type, &device);
+ argv += optind;
+ if (option_mask32 & ARP_OPT_A || option_mask32 & ARP_OPT_p) {
+ ap = get_aftype(protocol);
+ if (ap == NULL)
+ bb_error_msg_and_die("%s: unknown %s", protocol, "address family");
+ }
+ if (option_mask32 & ARP_OPT_A || option_mask32 & ARP_OPT_p) {
+ hw = get_hwtype(hw_type);
+ if (hw == NULL)
+ bb_error_msg_and_die("%s: unknown %s", hw_type, "hardware type");
+ hw_set = 1;
+ }
+ //if (option_mask32 & ARP_OPT_i)... -i
+
+ if (ap->af != AF_INET) {
+ bb_error_msg_and_die("%s: kernel only supports 'inet'", ap->name);
+ }
+
+ /* If no hw type specified get default */
+ if (!hw) {
+ hw = get_hwtype(DFLT_HW);
+ if (!hw)
+ bb_error_msg_and_die("%s: %s not supported", DFLT_HW, "hardware type");
+ }
+
+ if (hw->alen <= 0) {
+ bb_error_msg_and_die("%s: %s without ARP support",
+ hw->name, "hardware type");
+ }
+ sockfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ /* Now see what we have to do here... */
+ if (option_mask32 & (ARP_OPT_d|ARP_OPT_s)) {
+ if (argv[0] == NULL)
+ bb_error_msg_and_die("need host name");
+ if (option_mask32 & ARP_OPT_s)
+ return arp_set(argv);
+ return arp_del(argv);
+ }
+ //if (option_mask32 & ARP_OPT_a) - default
+ return arp_show(argv[0]);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/arping.c b/i/pc104/initrd/conf/busybox/networking/arping.c
new file mode 100644
index 0000000..d71ac49
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/arping.c
@@ -0,0 +1,417 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * arping.c - Ping hosts by ARP requests/replies
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ * Busybox port: Nick Fedchik <nick@fedchik.org.ua>
+ */
+
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+
+#include "busybox.h"
+
+static struct in_addr src;
+static struct in_addr dst;
+static struct sockaddr_ll me;
+static struct sockaddr_ll he;
+static struct timeval last;
+
+enum cfg_e {
+ dad = 1,
+ unsolicited = 2,
+ advert = 4,
+ quiet = 8,
+ quit_on_reply = 16,
+ broadcast_only = 32,
+ unicasting = 64
+};
+static int cfg;
+
+static int s;
+static unsigned count = UINT_MAX;
+static unsigned timeout;
+static int sent;
+static int brd_sent;
+static int received;
+static int brd_recv;
+static int req_recv;
+
+#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \
+ ((tv1).tv_usec-(tv2).tv_usec)/1000 )
+
+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;
+ RESERVE_CONFIG_UBUFFER(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 = cfg & 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 (cfg & 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 (!(cfg & unicasting))
+ brd_sent++;
+ }
+ RELEASE_CONFIG_BUFFER(buf);
+ return err;
+}
+
+static void finish(void)
+{
+ if (!(cfg & quiet)) {
+ printf("Sent %d probe(s) (%d broadcast(s))\n"
+ "Received %d repl%s"
+ " (%d request(s), %d broadcast(s))\n",
+ sent, brd_sent,
+ received, (received == 1) ? "ies" : "y",
+ req_recv, brd_recv);
+ }
+ if (cfg & dad)
+ exit(!!received);
+ if (cfg & unsolicited)
+ exit(0);
+ exit(!received);
+}
+
+static void catcher(void)
+{
+ struct timeval tv;
+ static 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 && (cfg & unsolicited))
+ finish();
+ }
+ alarm(1);
+}
+
+static int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
+{
+ struct arphdr *ah = (struct arphdr *) buf;
+ unsigned char *p = (unsigned char *) (ah + 1);
+ struct in_addr src_ip, dst_ip;
+
+ /* 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 (!(cfg & 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 (!(cfg & quiet)) {
+ int s_printed = 0;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ printf("%scast re%s from %s [%s]",
+ FROM->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
+ ah->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
+ inet_ntoa(src_ip),
+ 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 (cfg & quit_on_reply)
+ finish();
+ if (!(cfg & broadcast_only)) {
+ memcpy(he.sll_addr, p, me.sll_halen);
+ cfg |= unicasting;
+ }
+ return 1;
+}
+
+int arping_main(int argc, char **argv);
+int arping_main(int argc, char **argv)
+{
+ const char *device = "eth0";
+ int ifindex;
+ char *source = NULL;
+ char *target;
+
+ s = xsocket(PF_PACKET, SOCK_DGRAM, 0);
+
+ // Drop suid root privileges
+ xsetuid(getuid());
+
+ {
+ unsigned opt;
+ char *_count, *_timeout;
+
+ /* Dad also sets quit_on_reply.
+ * Advert also sets unsolicited.
+ */
+ opt_complementary = "Df:AU";
+ opt = getopt32(argc, argv, "DUAqfbc:w:i:s:",
+ &_count, &_timeout, &device, &source);
+ cfg |= opt & 0x3f; /* set respective flags */
+ if (opt & 0x40) /* -c: count */
+ count = xatou(_count);
+ if (opt & 0x80) /* -w: timeout */
+ timeout = xatoul_range(_timeout, 0, INT_MAX/2000);
+ //if (opt & 0x100) /* -i: interface */
+ if (strlen(device) > IF_NAMESIZE) {
+ bb_error_msg_and_die("interface name '%s' is too long",
+ device);
+ }
+ //if (opt & 0x200) /* -s: source */
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ bb_show_usage();
+
+ target = *argv;
+
+ xfunc_error_retval = 2;
+
+ {
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, device, IFNAMSIZ - 1);
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ bb_error_msg_and_die("interface %s not found", device);
+ }
+ ifindex = ifr.ifr_ifindex;
+
+ if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) {
+ bb_error_msg_and_die("SIOCGIFFLAGS");
+ }
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ bb_error_msg_and_die("interface %s is down", device);
+ }
+ if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
+ bb_error_msg("interface %s is not ARPable", device);
+ return (cfg & dad ? 0 : 2);
+ }
+ }
+
+ if (!inet_aton(target, &dst)) {
+ len_and_sockaddr *lsa;
+ lsa = xhost_and_af2sockaddr(target, 0, AF_INET);
+ memcpy(&dst, &lsa->sin.sin_addr.s_addr, 4);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(lsa);
+ }
+
+ if (source && !inet_aton(source, &src)) {
+ bb_error_msg_and_die("invalid source address %s", source);
+ }
+
+ if (!(cfg & dad) && (cfg & unsolicited) && src.s_addr == 0)
+ src = dst;
+
+ if (!(cfg & dad) || src.s_addr) {
+ struct sockaddr_in saddr;
+ int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ 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;
+ xbind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
+ } else if (!(cfg & dad)) {
+ socklen_t alen = sizeof(saddr);
+
+ saddr.sin_port = htons(1025);
+ saddr.sin_addr = dst;
+
+ if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)) == -1)
+ bb_perror_msg("warning: setsockopt(SO_DONTROUTE)");
+ xconnect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
+ if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == -1) {
+ bb_error_msg_and_die("getsockname");
+ }
+ src = saddr.sin_addr;
+ }
+ close(probe_fd);
+ }
+
+ me.sll_family = AF_PACKET;
+ me.sll_ifindex = ifindex;
+ me.sll_protocol = htons(ETH_P_ARP);
+ xbind(s, (struct sockaddr *) &me, sizeof(me));
+
+ {
+ socklen_t alen = sizeof(me);
+
+ if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) {
+ bb_error_msg_and_die("getsockname");
+ }
+ }
+ if (me.sll_halen == 0) {
+ bb_error_msg("interface \"%s\" is not ARPable (no ll address)", device);
+ return (cfg & dad ? 0 : 2);
+ }
+ he = me;
+ memset(he.sll_addr, -1, he.sll_halen);
+
+ if (!(cfg & quiet)) {
+ printf("ARPING to %s from %s via %s\n",
+ inet_ntoa(dst), inet_ntoa(src),
+ device ? device : "unknown");
+ }
+
+ if (!src.s_addr && !(cfg & dad)) {
+ bb_error_msg_and_die("no src address in the non-DAD mode");
+ }
+
+ {
+ 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;
+ RESERVE_CONFIG_UBUFFER(packet, 4096);
+ struct sockaddr_ll from;
+ socklen_t alen = sizeof(from);
+ int cc;
+
+ cc = recvfrom(s, packet, 4096, 0, (struct sockaddr *) &from, &alen);
+ if (cc < 0) {
+ bb_perror_msg("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);
+ RELEASE_CONFIG_BUFFER(packet);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/dnsd.c b/i/pc104/initrd/conf/busybox/networking/dnsd.c
new file mode 100644
index 0000000..78722d6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/dnsd.c
@@ -0,0 +1,426 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini DNS server implementation for busybox
+ *
+ * Copyright (C) 2005 Roberto A. Foglietta (me@roberto.foglietta.name)
+ * Copyright (C) 2005 Odd Arild Olsen (oao at fibula dot no)
+ * Copyright (C) 2003 Paul Sheer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Odd Arild Olsen started out with the sheerdns [1] of Paul Sheer and rewrote
+ * it into a shape which I believe is both easier to understand and maintain.
+ * I also reused the input buffer for output and removed services he did not
+ * need. [1] http://threading.2038bug.com/sheerdns/
+ *
+ * Some bugfix and minor changes was applied by Roberto A. Foglietta who made
+ * the first porting of oao' scdns to busybox also.
+ */
+
+#include "busybox.h"
+
+static const char *fileconf = "/etc/dnsd.conf";
+#define LOCK_FILE "/var/run/dnsd.lock"
+
+// Must match getopt32 call
+#define OPT_daemon (option_mask32 & 0x10)
+#define OPT_verbose (option_mask32 & 0x20)
+
+//#define DEBUG 1
+#define DEBUG 0
+
+enum {
+ MAX_HOST_LEN = 16, // longest host name allowed is 15
+ IP_STRING_LEN = 18, // .xxx.xxx.xxx.xxx\0
+
+//must be strlen('.in-addr.arpa') larger than IP_STRING_LEN
+ MAX_NAME_LEN = (IP_STRING_LEN + 13),
+
+/* Cannot get bigger packets than 512 per RFC1035
+ In practice this can be set considerably smaller:
+ Length of response packet is header (12B) + 2*type(4B) + 2*class(4B) +
+ ttl(4B) + rlen(2B) + r (MAX_NAME_LEN =21B) +
+ 2*querystring (2 MAX_NAME_LEN= 42B), all together 90 Byte
+*/
+ MAX_PACK_LEN = 512 + 1,
+
+ DEFAULT_TTL = 30, // increase this when not testing?
+
+ REQ_A = 1,
+ REQ_PTR = 12
+};
+
+struct dns_repl { // resource record, add 0 or 1 to accepted dns_msg in resp
+ uint16_t rlen;
+ uint8_t *r; // resource
+ uint16_t flags;
+};
+
+struct dns_head { // the message from client and first part of response mag
+ uint16_t id;
+ uint16_t flags;
+ uint16_t nquer; // accepts 0
+ uint16_t nansw; // 1 in response
+ uint16_t nauth; // 0
+ uint16_t nadd; // 0
+};
+struct dns_prop {
+ uint16_t type;
+ uint16_t class;
+};
+struct dns_entry { // element of known name, ip address and reversed ip address
+ struct dns_entry *next;
+ char ip[IP_STRING_LEN]; // dotted decimal IP
+ char rip[IP_STRING_LEN]; // length decimal reversed IP
+ char name[MAX_HOST_LEN];
+};
+
+static struct dns_entry *dnsentry = NULL;
+static uint32_t ttl = DEFAULT_TTL;
+
+/*
+ * Convert host name from C-string to dns length/string.
+ */
+static void convname(char *a, uint8_t *q)
+{
+ int i = (q[0] == '.') ? 0 : 1;
+ for (; i < MAX_HOST_LEN-1 && *q; i++, q++)
+ a[i] = tolower(*q);
+ a[0] = i - 1;
+ a[i] = 0;
+}
+
+/*
+ * Insert length of substrings instead of dots
+ */
+static void undot(uint8_t * rip)
+{
+ int i = 0, s = 0;
+ while (rip[i])
+ i++;
+ for (--i; i >= 0; i--) {
+ if (rip[i] == '.') {
+ rip[i] = s;
+ s = 0;
+ } else s++;
+ }
+}
+
+/*
+ * Read one line of hostname/IP from file
+ * Returns 0 for each valid entry read, -1 at EOF
+ * Assumes all host names are lower case only
+ * Hostnames with more than one label are not handled correctly.
+ * Presently the dot is copied into name without
+ * converting to a length/string substring for that label.
+ */
+
+static int getfileentry(FILE * fp, struct dns_entry *s)
+{
+ unsigned int a,b,c,d;
+ char *line, *r, *name;
+
+ restart:
+ line = r = xmalloc_fgets(fp);
+ if (!r)
+ return -1;
+ while (*r == ' ' || *r == '\t') {
+ r++;
+ if (!*r || *r == '#' || *r == '\n') {
+ free(line);
+ goto restart; /* skipping empty/blank and commented lines */
+ }
+ }
+ name = r;
+ while (*r != ' ' && *r != '\t')
+ r++;
+ *r++ = '\0';
+ if (sscanf(r, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) {
+ free(line);
+ goto restart; /* skipping wrong lines */
+ }
+
+ sprintf(s->ip, "%u.%u.%u.%u", a, b, c, d);
+ sprintf(s->rip, ".%u.%u.%u.%u", d, c, b, a);
+ undot((uint8_t*)s->rip);
+ convname(s->name, (uint8_t*)name);
+
+ if (OPT_verbose)
+ fprintf(stderr, "\tname:%s, ip:%s\n", &(s->name[1]),s->ip);
+
+ free(line);
+ return 0;
+}
+
+/*
+ * Read hostname/IP records from file
+ */
+static void dnsentryinit(void)
+{
+ FILE *fp;
+ struct dns_entry *m, *prev;
+
+ prev = dnsentry = NULL;
+ fp = xfopen(fileconf, "r");
+
+ while (1) {
+ m = xzalloc(sizeof(*m));
+ /*m->next = NULL;*/
+ if (getfileentry(fp, m))
+ break;
+
+ if (prev == NULL)
+ dnsentry = m;
+ else
+ prev->next = m;
+ prev = m;
+ }
+ fclose(fp);
+}
+
+/*
+ * Look query up in dns records and return answer if found
+ * qs is the query string, first byte the string length
+ */
+static int table_lookup(uint16_t type, uint8_t * as, uint8_t * qs)
+{
+ int i;
+ struct dns_entry *d=dnsentry;
+
+ do {
+#if DEBUG
+ char *p,*q;
+ q = (char *)&(qs[1]);
+ p = &(d->name[1]);
+ fprintf(stderr, "\n%s: %d/%d p:%s q:%s %d",
+ __FUNCTION__, (int)strlen(p), (int)(d->name[0]),
+ p, q, (int)strlen(q));
+#endif
+ if (type == REQ_A) { /* search by host name */
+ for (i = 1; i <= (int)(d->name[0]); i++)
+ if (tolower(qs[i]) != d->name[i])
+ break;
+ if (i > (int)(d->name[0])) {
+#if DEBUG
+ fprintf(stderr, " OK");
+#endif
+ strcpy((char *)as, d->ip);
+#if DEBUG
+ fprintf(stderr, " as:%s\n", as);
+#endif
+ return 0;
+ }
+ } else
+ if (type == REQ_PTR) { /* search by IP-address */
+ if (!strncmp((char*)&d->rip[1], (char*)&qs[1], strlen(d->rip)-1)) {
+ strcpy((char *)as, d->name);
+ return 0;
+ }
+ }
+ d = d->next;
+ } while (d);
+ return -1;
+}
+
+
+/*
+ * Decode message and generate answer
+ */
+#define eret(s) do { fputs(s, stderr); return -1; } while (0)
+static int process_packet(uint8_t * buf)
+{
+ struct dns_head *head;
+ struct dns_prop *qprop;
+ struct dns_repl outr;
+ void *next, *from, *answb;
+
+ uint8_t answstr[MAX_NAME_LEN + 1];
+ int lookup_result, type, len, packet_len;
+ uint16_t flags;
+
+ answstr[0] = '\0';
+
+ head = (struct dns_head *)buf;
+ if (head->nquer == 0)
+ eret("no queries\n");
+
+ if (head->flags & 0x8000)
+ eret("ignoring response packet\n");
+
+ from = (void *)&head[1]; // start of query string
+ next = answb = from + strlen((char *)from) + 1 + sizeof(struct dns_prop); // where to append answer block
+
+ outr.rlen = 0; // may change later
+ outr.r = NULL;
+ outr.flags = 0;
+
+ qprop = (struct dns_prop *)(answb - 4);
+ type = ntohs(qprop->type);
+
+ // only let REQ_A and REQ_PTR pass
+ if (!(type == REQ_A || type == REQ_PTR)) {
+ goto empty_packet; /* we can't handle the query type */
+ }
+
+ if (ntohs(qprop->class) != 1 /* class INET */ ) {
+ outr.flags = 4; /* not supported */
+ goto empty_packet;
+ }
+ /* we only support standard queries */
+
+ if ((ntohs(head->flags) & 0x7800) != 0)
+ goto empty_packet;
+
+ // We have a standard query
+ bb_info_msg("%s", (char *)from);
+ lookup_result = table_lookup(type, answstr, (uint8_t*)from);
+ if (lookup_result != 0) {
+ outr.flags = 3 | 0x0400; //name do not exist and auth
+ goto empty_packet;
+ }
+ if (type == REQ_A) { // return an address
+ struct in_addr a;
+ if (!inet_aton((char*)answstr, &a)) {//dotted dec to long conv
+ outr.flags = 1; /* Frmt err */
+ goto empty_packet;
+ }
+ memcpy(answstr, &a.s_addr, 4); // save before a disappears
+ outr.rlen = 4; // uint32_t IP
+ }
+ else
+ outr.rlen = strlen((char *)answstr) + 1; // a host name
+ outr.r = answstr; // 32 bit ip or a host name
+ outr.flags |= 0x0400; /* authority-bit */
+ // we have an answer
+ head->nansw = htons(1);
+
+ // copy query block to answer block
+ len = answb - from;
+ memcpy(answb, from, len);
+ next += len;
+
+ // and append answer rr
+ *(uint32_t *) next = htonl(ttl);
+ next += 4;
+ *(uint16_t *) next = htons(outr.rlen);
+ next += 2;
+ memcpy(next, (void *)answstr, outr.rlen);
+ next += outr.rlen;
+
+ empty_packet:
+
+ flags = ntohs(head->flags);
+ // clear rcode and RA, set responsebit and our new flags
+ flags |= (outr.flags & 0xff80) | 0x8000;
+ head->flags = htons(flags);
+ head->nauth = head->nadd = htons(0);
+ head->nquer = htons(1);
+
+ packet_len = next - (void *)buf;
+ return packet_len;
+}
+
+/*
+ * Exit on signal
+ */
+static void interrupt(int x)
+{
+ unlink(LOCK_FILE);
+ bb_error_msg("interrupt, exiting\n");
+ exit(2);
+}
+
+int dnsd_main(int argc, char **argv);
+int dnsd_main(int argc, char **argv)
+{
+ char *listen_interface = NULL;
+ char *sttl, *sport;
+ len_and_sockaddr *lsa;
+ int udps;
+ uint16_t port = 53;
+ uint8_t buf[MAX_PACK_LEN];
+
+ getopt32(argc, argv, "i:c:t:p:dv", &listen_interface, &fileconf, &sttl, &sport);
+ //if (option_mask32 & 0x1) // -i
+ //if (option_mask32 & 0x2) // -c
+ if (option_mask32 & 0x4) // -t
+ ttl = xatou_range(sttl, 1, 0xffffffff);
+ if (option_mask32 & 0x8) // -p
+ port = xatou_range(sttl, 1, 0xffff);
+
+ if (OPT_verbose) {
+ bb_info_msg("listen_interface: %s", listen_interface);
+ bb_info_msg("ttl: %d, port: %d", ttl, port);
+ bb_info_msg("fileconf: %s", fileconf);
+ }
+
+ if (OPT_daemon) {
+//FIXME: NOMMU will NOT set LOGMODE_SYSLOG!
+#ifdef BB_NOMMU
+ /* reexec for vfork() do continue parent */
+ vfork_daemon_rexec(1, 0, argc, argv, "-d");
+#else
+ xdaemon(1, 0);
+#endif
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ dnsentryinit();
+
+ signal(SIGINT, interrupt);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+#ifdef SIGURG
+ signal(SIGURG, SIG_IGN);
+#endif
+
+ lsa = xhost2sockaddr(listen_interface, port);
+ udps = xsocket(lsa->sa.sa_family, SOCK_DGRAM, 0);
+ xbind(udps, &lsa->sa, lsa->len);
+ // xlisten(udps, 50); - ?!! DGRAM sockets are never listened on I think?
+ bb_info_msg("Accepting UDP packets on %s",
+ xmalloc_sockaddr2dotted(&lsa->sa, lsa->len));
+
+ while (1) {
+ fd_set fdset;
+ int r;
+
+ FD_ZERO(&fdset);
+ FD_SET(udps, &fdset);
+ // Block until a message arrives
+// FIXME: Fantastic. select'ing on just one fd??
+// Why no just block on it doing recvfrom() ?
+ r = select(udps + 1, &fdset, NULL, NULL, NULL);
+ if (r < 0)
+ bb_perror_msg_and_die("select error");
+ if (r == 0)
+ bb_perror_msg_and_die("select spurious return");
+
+ /* Can this test ever be false? - yes */
+ if (FD_ISSET(udps, &fdset)) {
+ socklen_t fromlen = lsa->len;
+// FIXME: need to get *DEST* address (to which of our addresses
+// this query was directed), and reply from the same address.
+// Or else we can exhibit usual UDP ugliness:
+// [ip1.multihomed.ip2] <= query to ip1 <= peer
+// [ip1.multihomed.ip2] => reply from ip2 => peer (confused)
+ r = recvfrom(udps, buf, sizeof(buf), 0, &lsa->sa, &fromlen);
+ if (OPT_verbose)
+ bb_info_msg("Got UDP packet");
+
+ if (r < 12 || r > 512) {
+ bb_error_msg("invalid packet size");
+ continue;
+ }
+ if (r <= 0)
+ continue;
+ r = process_packet(buf);
+ if (r <= 0)
+ continue;
+ sendto(udps, buf, r, 0, &lsa->sa, fromlen);
+ }
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ether-wake.c b/i/pc104/initrd/conf/busybox/networking/ether-wake.c
new file mode 100644
index 0000000..73f693e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ether-wake.c
@@ -0,0 +1,284 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ether-wake.c - Send a magic packet to wake up sleeping machines.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Author: Donald Becker, http://www.scyld.com/"; http://www.scyld.com/wakeonlan.html
+ * Busybox port: Christian Volkmann <haveaniceday@online.de>
+ * Used version of ether-wake.c: v1.09 11/12/2003 Donald Becker, http://www.scyld.com/";
+ */
+
+/* full usage according Donald Becker
+ * usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n"
+ *
+ * This program generates and transmits a Wake-On-LAN (WOL)\n"
+ * \"Magic Packet\", used for restarting machines that have been\n"
+ * soft-powered-down (ACPI D3-warm state).\n"
+ * It currently generates the standard AMD Magic Packet format, with\n"
+ * an optional password appended.\n"
+ *
+ * The single required parameter is the Ethernet MAC (station) address\n"
+ * of the machine to wake or a host ID with known NSS 'ethers' entry.\n"
+ * The MAC address may be found with the 'arp' program while the target\n"
+ * machine is awake.\n"
+ *
+ * Options:\n"
+ * -b Send wake-up packet to the broadcast address.\n"
+ * -D Increase the debug level.\n"
+ * -i ifname Use interface IFNAME instead of the default 'eth0'.\n"
+ * -p <pw> Append the four or six byte password PW to the packet.\n"
+ * A password is only required for a few adapter types.\n"
+ * The password may be specified in ethernet hex format\n"
+ * or dotted decimal (Internet address)\n"
+ * -p 00:22:44:66:88:aa\n"
+ * -p 192.168.1.1\n";
+ *
+ *
+ * This program generates and transmits a Wake-On-LAN (WOL) "Magic Packet",
+ * used for restarting machines that have been soft-powered-down
+ * (ACPI D3-warm state). It currently generates the standard AMD Magic Packet
+ * format, with an optional password appended.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ * Contact the author for use under other terms.
+ *
+ * This source file was originally part of the network tricks package, and
+ * is now distributed to support the Scyld Beowulf system.
+ * Copyright 1999-2003 Donald Becker and Scyld Computing Corporation.
+ *
+ * The author may be reached as becker@scyld, or C/O
+ * Scyld Computing Corporation
+ * 914 Bay Ridge Road, Suite 220
+ * Annapolis MD 21403
+ *
+ * Notes:
+ * On some systems dropping root capability allows the process to be
+ * dumped, traced or debugged.
+ * If someone traces this program, they get control of a raw socket.
+ * Linux handles this safely, but beware when porting this program.
+ *
+ * An alternative to needing 'root' is using a UDP broadcast socket, however
+ * doing so only works with adapters configured for unicast+broadcast Rx
+ * filter. That configuration consumes more power.
+*/
+
+
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+#include <linux/if.h>
+
+#include "busybox.h"
+
+/* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to
+ * work as non-root, but we need SOCK_PACKET to specify the Ethernet
+ * destination address.
+ */
+#ifdef PF_PACKET
+# define whereto_t sockaddr_ll
+# define make_socket() xsocket(PF_PACKET, SOCK_RAW, 0)
+#else
+# define whereto_t sockaddr
+# define make_socket() xsocket(AF_INET, SOCK_PACKET, SOCK_PACKET)
+#endif
+
+#ifdef DEBUG
+# define bb_debug_msg(fmt, args...) fprintf(stderr, fmt, ## args)
+void bb_debug_dump_packet(unsigned char *outpack, int pktsize)
+{
+ int i;
+ printf("packet dump:\n");
+ for (i = 0; i < pktsize; ++i) {
+ printf("%2.2x ", outpack[i]);
+ if (i % 20 == 19) puts("");
+ }
+ printf("\n\n");
+}
+#else
+# define bb_debug_msg(fmt, args...)
+# define bb_debug_dump_packet(outpack, pktsize)
+#endif
+
+static inline void get_dest_addr(const char *arg, struct ether_addr *eaddr);
+static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast);
+static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd);
+
+int ether_wake_main(int argc, char *argv[]);
+int ether_wake_main(int argc, char *argv[])
+{
+ const char *ifname = "eth0";
+ char *pass = NULL;
+ unsigned long flags;
+ unsigned char wol_passwd[6];
+ int wol_passwd_sz = 0;
+
+ int s; /* Raw socket */
+ int pktsize;
+ unsigned char outpack[1000];
+
+ struct ether_addr eaddr;
+ struct whereto_t whereto; /* who to wake up */
+
+ /* handle misc user options */
+ flags = getopt32(argc, argv, "bi:p:", &ifname, &pass);
+ if (optind == argc)
+ bb_show_usage();
+ if (pass)
+ wol_passwd_sz = get_wol_pw(pass, wol_passwd);
+
+ /* create the raw socket */
+ s = make_socket();
+
+ /* now that we have a raw socket we can drop root */
+ xsetuid(getuid());
+
+ /* look up the dest mac address */
+ get_dest_addr(argv[optind], &eaddr);
+
+ /* fill out the header of the packet */
+ pktsize = get_fill(outpack, &eaddr, flags /*& 1 [OPT_BROADCAST]*/);
+
+ bb_debug_dump_packet(outpack, pktsize);
+
+ /* Fill in the source address, if possible. */
+#ifdef __linux__
+ {
+ struct ifreq if_hwaddr;
+
+ strncpy(if_hwaddr.ifr_name, ifname, sizeof(if_hwaddr.ifr_name));
+ if (ioctl(s, SIOCGIFHWADDR, &if_hwaddr) < 0)
+ bb_perror_msg_and_die("SIOCGIFHWADDR on %s failed", ifname);
+
+ memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6);
+
+# ifdef DEBUG
+ {
+ unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data;
+ printf("The hardware address (SIOCGIFHWADDR) of %s is type %d "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n\n", ifname,
+ if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1],
+ hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
+ }
+# endif
+ }
+#endif /* __linux__ */
+
+ bb_debug_dump_packet(outpack, pktsize);
+
+ /* append the password if specified */
+ if (wol_passwd_sz > 0) {
+ memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz);
+ pktsize += wol_passwd_sz;
+ }
+
+ bb_debug_dump_packet(outpack, pktsize);
+
+ /* This is necessary for broadcasts to work */
+ if (flags /*& 1 [OPT_BROADCAST]*/) {
+ if (setsockopt_broadcast(s) < 0)
+ bb_perror_msg("SO_BROADCAST");
+ }
+
+#if defined(PF_PACKET)
+ {
+ struct ifreq ifr;
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFINDEX, &ifr) == -1)
+ bb_perror_msg_and_die("SIOCGIFINDEX");
+ memset(&whereto, 0, sizeof(whereto));
+ whereto.sll_family = AF_PACKET;
+ whereto.sll_ifindex = ifr.ifr_ifindex;
+ /* The manual page incorrectly claims the address must be filled.
+ We do so because the code may change to match the docs. */
+ whereto.sll_halen = ETH_ALEN;
+ memcpy(whereto.sll_addr, outpack, ETH_ALEN);
+ }
+#else
+ whereto.sa_family = 0;
+ strcpy(whereto.sa_data, ifname);
+#endif
+
+ if (sendto(s, outpack, pktsize, 0, (struct sockaddr *)&whereto, sizeof(whereto)) < 0)
+ bb_perror_msg(bb_msg_write_error);
+
+ close(s);
+
+ return EXIT_SUCCESS;
+}
+
+/* Convert the host ID string to a MAC address.
+ * The string may be a:
+ * Host name
+ * IP address string
+ * MAC address string
+*/
+static inline void get_dest_addr(const char *hostid, struct ether_addr *eaddr)
+{
+ struct ether_addr *eap;
+
+ eap = ether_aton(hostid);
+ if (eap) {
+ *eaddr = *eap;
+ bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eaddr));
+#if !defined(__UCLIBC__)
+ } else if (ether_hostton(hostid, eaddr) == 0) {
+ bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr));
+#endif
+ } else
+ bb_show_usage();
+}
+
+static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast)
+{
+ int offset, i;
+ unsigned char *station_addr = eaddr->ether_addr_octet;
+
+ if (broadcast)
+ memset(pkt+0, 0xff, 6);
+ else
+ memcpy(pkt, station_addr, 6);
+ memcpy(pkt+6, station_addr, 6);
+ pkt[12] = 0x08; /* Or 0x0806 for ARP, 0x8035 for RARP */
+ pkt[13] = 0x42;
+ offset = 14;
+
+ memset(pkt+offset, 0xff, 6);
+ offset += 6;
+
+ for (i = 0; i < 16; ++i) {
+ memcpy(pkt+offset, station_addr, 6);
+ offset += 6;
+ }
+
+ return offset;
+}
+
+static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd)
+{
+ int passwd[6];
+ int byte_cnt, i;
+
+ /* handle MAC format */
+ byte_cnt = sscanf(ethoptarg, "%2x:%2x:%2x:%2x:%2x:%2x",
+ &passwd[0], &passwd[1], &passwd[2],
+ &passwd[3], &passwd[4], &passwd[5]);
+ /* handle IP format */
+ if (byte_cnt < 4)
+ byte_cnt = sscanf(ethoptarg, "%d.%d.%d.%d",
+ &passwd[0], &passwd[1], &passwd[2], &passwd[3]);
+ if (byte_cnt < 4) {
+ bb_error_msg("cannot read Wake-On-LAN pass");
+ return 0;
+ }
+
+ for (i = 0; i < byte_cnt; ++i)
+ wol_passwd[i] = passwd[i];
+
+ bb_debug_msg("password: %2.2x %2.2x %2.2x %2.2x (%d)\n\n",
+ wol_passwd[0], wol_passwd[1], wol_passwd[2], wol_passwd[3],
+ byte_cnt);
+
+ return byte_cnt;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ftpgetput.c b/i/pc104/initrd/conf/busybox/networking/ftpgetput.c
new file mode 100644
index 0000000..9b0510d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ftpgetput.c
@@ -0,0 +1,359 @@
+/* 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@iinet.net.au>
+ *
+ * Based on wget.c by Chip Rosenthal Covad Communications
+ * <chip@laserlink.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <getopt.h>
+
+typedef struct ftp_host_info_s {
+ const char *user;
+ const char *password;
+ struct len_and_sockaddr *lsa;
+} ftp_host_info_t;
+
+static smallint verbose_flag;
+static smallint do_continue;
+
+static void ftp_die(const char *msg, const char *remote) ATTRIBUTE_NORETURN;
+static void ftp_die(const char *msg, const char *remote)
+{
+ /* Guard against garbage from remote server */
+ const char *cp = remote;
+ while (*cp >= ' ' && *cp < '\x7f') cp++;
+ bb_error_msg_and_die("unexpected server response%s%s: %.*s",
+ msg ? " to " : "", msg ? msg : "",
+ (int)(cp - remote), remote);
+}
+
+
+static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf)
+{
+ unsigned n;
+ if (verbose_flag) {
+ bb_error_msg("cmd %s %s", s1, s2);
+ }
+
+ if (s1) {
+ if (s2) {
+ fprintf(stream, "%s %s\r\n", s1, s2);
+ } else {
+ fprintf(stream, "%s\r\n", s1);
+ }
+ }
+ do {
+ char *buf_ptr;
+
+ if (fgets(buf, 510, stream) == NULL) {
+ bb_perror_msg_and_die("fgets");
+ }
+ buf_ptr = strstr(buf, "\r\n");
+ if (buf_ptr) {
+ *buf_ptr = '\0';
+ }
+ } while (!isdigit(buf[0]) || buf[3] != ' ');
+
+ buf[3] = '\0';
+ n = xatou(buf);
+ buf[3] = ' ';
+ return n;
+}
+
+static int xconnect_ftpdata(ftp_host_info_t *server, char *buf)
+{
+ char *buf_ptr;
+ unsigned short port_num;
+
+ /* Response is "NNN garbageN1,N2,N3,N4,P1,P2[)garbage]
+ * Server's IP is N1.N2.N3.N4 (we ignore it)
+ * Server's port for data connection is P1*256+P2 */
+ buf_ptr = strrchr(buf, ')');
+ if (buf_ptr) *buf_ptr = '\0';
+
+ buf_ptr = strrchr(buf, ',');
+ *buf_ptr = '\0';
+ port_num = xatoul_range(buf_ptr + 1, 0, 255);
+
+ buf_ptr = strrchr(buf, ',');
+ *buf_ptr = '\0';
+ port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256;
+
+ set_nport(server->lsa, htons(port_num));
+ return xconnect_stream(server->lsa);
+}
+
+static FILE *ftp_login(ftp_host_info_t *server)
+{
+ FILE *control_stream;
+ char buf[512];
+
+ /* Connect to the command socket */
+ control_stream = fdopen(xconnect_stream(server->lsa), "r+");
+ if (control_stream == NULL) {
+ /* fdopen failed - extremely unlikely */
+ bb_perror_nomsg_and_die();
+ }
+
+ if (ftpcmd(NULL, NULL, control_stream, buf) != 220) {
+ ftp_die(NULL, buf);
+ }
+
+ /* 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) {
+ ftp_die("PASS", buf);
+ }
+ break;
+ default:
+ ftp_die("USER", buf);
+ }
+
+ ftpcmd("TYPE I", NULL, control_stream, buf);
+
+ return control_stream;
+}
+
+#if !ENABLE_FTPGET
+int ftp_receive(ftp_host_info_t *server, FILE *control_stream,
+ const char *local_path, char *server_path);
+#else
+static
+int ftp_receive(ftp_host_info_t *server, FILE *control_stream,
+ const char *local_path, char *server_path)
+{
+ char buf[512];
+/* I think 'filesize' usage here is bogus. Let's see... */
+ //off_t filesize = -1;
+#define filesize ((off_t)-1)
+ int fd_data;
+ int fd_local = -1;
+ off_t beg_range = 0;
+
+ /* Connect to the data socket */
+ if (ftpcmd("PASV", NULL, control_stream, buf) != 227) {
+ ftp_die("PASV", buf);
+ }
+ fd_data = xconnect_ftpdata(server, buf);
+
+ if (ftpcmd("SIZE", server_path, control_stream, buf) == 213) {
+ //filesize = BB_STRTOOFF(buf + 4, NULL, 10);
+ //if (errno || filesize < 0)
+ // ftp_die("SIZE", buf);
+ } else {
+ do_continue = 0;
+ }
+
+ if (LONE_DASH(local_path)) {
+ fd_local = STDOUT_FILENO;
+ do_continue = 0;
+ }
+
+ if (do_continue) {
+ struct stat sbuf;
+ if (lstat(local_path, &sbuf) < 0) {
+ bb_perror_msg_and_die("lstat");
+ }
+ if (sbuf.st_size > 0) {
+ beg_range = sbuf.st_size;
+ } else {
+ do_continue = 0;
+ }
+ }
+
+ if (do_continue) {
+ sprintf(buf, "REST %"OFF_FMT"d", beg_range);
+ if (ftpcmd(buf, NULL, control_stream, buf) != 350) {
+ do_continue = 0;
+ } else {
+ //if (filesize != -1)
+ // filesize -= beg_range;
+ }
+ }
+
+ if (ftpcmd("RETR", server_path, control_stream, buf) > 150) {
+ ftp_die("RETR", buf);
+ }
+
+ /* only make a local file if we know that one exists on the remote server */
+ if (fd_local == -1) {
+ if (do_continue) {
+ fd_local = xopen(local_path, O_APPEND | O_WRONLY);
+ } else {
+ fd_local = xopen(local_path, O_CREAT | O_TRUNC | O_WRONLY);
+ }
+ }
+
+ /* Copy the file */
+ if (filesize != -1) {
+ if (bb_copyfd_size(fd_data, fd_local, filesize) == -1)
+ return EXIT_FAILURE;
+ } else {
+ if (bb_copyfd_eof(fd_data, fd_local) == -1)
+ return EXIT_FAILURE;
+ }
+
+ /* close it all down */
+ close(fd_data);
+ if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
+ ftp_die(NULL, buf);
+ }
+ ftpcmd("QUIT", NULL, control_stream, buf);
+
+ return EXIT_SUCCESS;
+}
+#endif
+
+#if !ENABLE_FTPPUT
+int ftp_send(ftp_host_info_t *server, FILE *control_stream,
+ const char *server_path, char *local_path);
+#else
+static
+int ftp_send(ftp_host_info_t *server, FILE *control_stream,
+ 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) {
+ ftp_die("PASV", buf);
+ }
+ fd_data = xconnect_ftpdata(server, buf);
+
+ /* get the local file */
+ fd_local = STDIN_FILENO;
+ if (NOT_LONE_DASH(local_path)) {
+ fd_local = xopen(local_path, O_RDONLY);
+ fstat(fd_local, &sbuf);
+
+ sprintf(buf, "ALLO %"OFF_FMT"u", sbuf.st_size);
+ response = ftpcmd(buf, NULL, control_stream, buf);
+ switch (response) {
+ case 200:
+ case 202:
+ break;
+ default:
+ close(fd_local);
+ ftp_die("ALLO", buf);
+ break;
+ }
+ }
+ response = ftpcmd("STOR", server_path, control_stream, buf);
+ switch (response) {
+ case 125:
+ case 150:
+ break;
+ default:
+ close(fd_local);
+ ftp_die("STOR", buf);
+ }
+
+ /* transfer the file */
+ if (bb_copyfd_eof(fd_local, fd_data) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* close it all down */
+ close(fd_data);
+ if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
+ ftp_die("close", buf);
+ }
+ ftpcmd("QUIT", NULL, control_stream, buf);
+
+ return EXIT_SUCCESS;
+}
+#endif
+
+#define FTPGETPUT_OPT_CONTINUE 1
+#define FTPGETPUT_OPT_VERBOSE 2
+#define FTPGETPUT_OPT_USER 4
+#define FTPGETPUT_OPT_PASSWORD 8
+#define FTPGETPUT_OPT_PORT 16
+
+#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
+static const struct option ftpgetput_long_options[] = {
+ { "continue", 1, NULL, 'c' },
+ { "verbose", 0, NULL, 'v' },
+ { "username", 1, NULL, 'u' },
+ { "password", 1, NULL, 'p' },
+ { "port", 1, NULL, 'P' },
+ { 0, 0, 0, 0 }
+};
+#endif
+
+int ftpgetput_main(int argc, char **argv);
+int ftpgetput_main(int argc, char **argv)
+{
+ /* content-length of the file */
+ unsigned opt;
+ const char *port = "ftp";
+ /* socket to ftp server */
+ FILE *control_stream;
+ /* continue previous transfer (-c) */
+ ftp_host_info_t *server;
+
+#if ENABLE_FTPPUT && !ENABLE_FTPGET
+# define ftp_action ftp_send
+#elif ENABLE_FTPGET && !ENABLE_FTPPUT
+# define ftp_action ftp_receive
+#else
+ int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = ftp_send;
+ /* Check to see if the command is ftpget or ftput */
+ if (applet_name[3] == 'g') {
+ ftp_action = ftp_receive;
+ }
+#endif
+
+ /* Set default values */
+ server = xmalloc(sizeof(*server));
+ server->user = "anonymous";
+ server->password = "busybox@";
+
+ /*
+ * Decipher the command line
+ */
+#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
+ applet_long_options = ftpgetput_long_options;
+#endif
+ opt_complementary = "=3"; /* must have 3 params */
+ opt = getopt32(argc, argv, "cvu:p:P:", &server->user, &server->password, &port);
+ argv += optind;
+
+ /* Process the non-option command line arguments */
+ if (opt & FTPGETPUT_OPT_CONTINUE) {
+ do_continue = 1;
+ }
+ if (opt & FTPGETPUT_OPT_VERBOSE) {
+ verbose_flag = 1;
+ }
+
+ /* We want to do exactly _one_ DNS lookup, since some
+ * sites (i.e. ftp.us.debian.org) use round-robin DNS
+ * and we want to connect to only one IP... */
+ server->lsa = xhost2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21));
+ if (verbose_flag) {
+ printf("Connecting to %s (%s)\n", argv[0],
+ xmalloc_sockaddr2dotted(&server->lsa->sa, server->lsa->len));
+ }
+
+ /* Connect/Setup/Configure the FTP session */
+ control_stream = ftp_login(server);
+
+ return ftp_action(server, control_stream, argv[1], argv[2]);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/hostname.c b/i/pc104/initrd/conf/busybox/networking/hostname.c
new file mode 100644
index 0000000..3f6a658
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/hostname.c
@@ -0,0 +1,101 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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.
+ *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+static void do_sethostname(char *s, int isfile)
+{
+ FILE *f;
+
+ if (!s)
+ return;
+ if (!isfile) {
+ if (sethostname(s, strlen(s)) < 0) {
+ if (errno == EPERM)
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+ else
+ bb_perror_msg_and_die("sethostname");
+ }
+ } else {
+ f = xfopen(s, "r");
+ while (fgets(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), f) != NULL) {
+ if (bb_common_bufsiz1[0] == '#') {
+ continue;
+ }
+ chomp(bb_common_bufsiz1);
+ do_sethostname(bb_common_bufsiz1, 0);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ fclose(f);
+ }
+}
+
+int hostname_main(int argc, char **argv);
+int hostname_main(int argc, char **argv)
+{
+ enum {
+ OPT_d = 0x1,
+ OPT_f = 0x2,
+ OPT_i = 0x4,
+ OPT_s = 0x8,
+ OPT_F = 0x10,
+ OPT_dfis = 0xf,
+ };
+
+ char buf[256];
+ char *hostname_str;
+
+ if (argc < 1)
+ bb_show_usage();
+
+ getopt32(argc, argv, "dfisF:", &hostname_str);
+
+ /* Output in desired format */
+ if (option_mask32 & OPT_dfis) {
+ struct hostent *hp;
+ char *p;
+ gethostname(buf, sizeof(buf));
+ hp = xgethostbyname(buf);
+ p = strchr(hp->h_name, '.');
+ if (option_mask32 & OPT_f) {
+ puts(hp->h_name);
+ } else if (option_mask32 & OPT_s) {
+ if (p != NULL) {
+ *p = '\0';
+ }
+ puts(hp->h_name);
+ } else if (option_mask32 & OPT_d) {
+ if (p)
+ puts(p + 1);
+ } else if (option_mask32 & OPT_i) {
+ while (hp->h_addr_list[0]) {
+ printf("%s ", inet_ntoa(*(struct in_addr *) (*hp->h_addr_list++)));
+ }
+ puts("");
+ }
+ }
+ /* Set the hostname */
+ else if (option_mask32 & OPT_F) {
+ do_sethostname(hostname_str, 1);
+ } else if (optind < argc) {
+ do_sethostname(argv[optind], 0);
+ }
+ /* Or if all else fails,
+ * just print the current hostname */
+ else {
+ gethostname(buf, sizeof(buf));
+ puts(buf);
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/httpd.c b/i/pc104/initrd/conf/busybox/networking/httpd.c
new file mode 100644
index 0000000..72b03de
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/httpd.c
@@ -0,0 +1,2049 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * httpd implementation for busybox
+ *
+ * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
+ * Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * simplify patch stolen from libbb without using strdup
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ *****************************************************************************
+ *
+ * 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.
+ *
+ * Doc:
+ * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html
+ *
+ * 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
+ * presentation. -d decodes a url-encoded argument while -e encodes in html
+ * for page display.
+ *
+ * httpd.conf has the following format:
+ *
+ * A:172.20. # Allow address from 172.20.0.0/16
+ * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127
+ * A:10.0.0.0/255.255.255.128 # Allow any address that previous set
+ * 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
+ * *.php:/path/php # running cgi.php scripts through an interpreter
+ *
+ * A/D may be as a/d or allow/deny - first char case insensitive
+ * 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: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.
+ *
+ * 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 "busybox.h"
+
+/* amount of buffering in a pipe */
+#ifndef PIPE_BUF
+# define PIPE_BUF 4096
+#endif
+
+static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004";
+static const char default_path_httpd_conf[] = "/etc";
+static const char httpd_conf[] = "httpd.conf";
+static const char home[] = "./";
+
+#define TIMEOUT 60
+
+// Note: busybox 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
+#define DEBUG 0
+
+#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 HT_ACCESS_IP {
+ unsigned int ip;
+ unsigned int mask;
+ int allow_deny;
+ struct HT_ACCESS_IP *next;
+} Htaccess_IP;
+
+typedef struct {
+ char buf[MAX_MEMORY_BUFF];
+
+ USE_FEATURE_HTTPD_BASIC_AUTH(const char *realm;)
+ USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
+
+ const char *query;
+
+ USE_FEATURE_HTTPD_CGI(char *referer;)
+
+ const char *configFile;
+
+ unsigned int rmt_ip;
+#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
+ char *rmt_ip_str; /* for set env REMOTE_ADDR */
+#endif
+ unsigned port; /* server initial port and for
+ set env REMOTE_PORT */
+ const char *found_mime_type;
+ const char *found_moved_temporarily;
+
+ off_t ContentLength; /* -1 - unknown */
+ time_t last_mod;
+
+ Htaccess_IP *ip_a_d; /* config allow/deny lines */
+ int flg_deny_all;
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ Htaccess *auth; /* config user:password lines */
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ Htaccess *mime_a; /* config mime types */
+#endif
+
+ int server_socket;
+ int accepted_socket;
+ volatile int alarm_signaled;
+
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ Htaccess *script_i; /* config script interpreters */
+#endif
+} HttpdConfig;
+
+static HttpdConfig *config;
+
+static const char request_GET[] = "GET"; /* size algorithmic optimize */
+
+static const char* const suffixTable [] = {
+/* Warning: shorted equivalent 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_MOVED_TEMPORARILY = 302,
+ HTTP_BAD_REQUEST = 400, /* malformed syntax */
+ HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
+ HTTP_NOT_FOUND = 404,
+ HTTP_FORBIDDEN = 403,
+ HTTP_REQUEST_TIMEOUT = 408,
+ HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
+ 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_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", NULL },
+ { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
+ { HTTP_REQUEST_TIMEOUT, "Request Timeout",
+ "No request appeared within a reasonable time period." },
+ { HTTP_NOT_IMPLEMENTED, "Not Implemented",
+ "The requested method is not recognized by this server." },
+#if ENABLE_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_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";
+
+
+#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
+
+
+static int scan_ip(const char **ep, unsigned int *ip, unsigned char endc)
+{
+ const char *p = *ep;
+ int auto_mask = 8;
+ int j;
+
+ *ip = 0;
+ for (j = 0; j < 4; j++) {
+ unsigned int octet;
+
+ if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0)
+ return -auto_mask;
+ octet = 0;
+ while (*p >= '0' && *p <= '9') {
+ octet *= 10;
+ octet += *p - '0';
+ if (octet > 255)
+ return -auto_mask;
+ p++;
+ }
+ if (*p == '.')
+ p++;
+ if (*p != '/' && *p != 0)
+ auto_mask += 8;
+ *ip = ((*ip) << 8) | octet;
+ }
+ if (*p != 0) {
+ if (*p != endc)
+ return -auto_mask;
+ p++;
+ if (*p == 0)
+ return -auto_mask;
+ }
+ *ep = p;
+ return auto_mask;
+}
+
+static int scan_ip_mask(const char *ipm, unsigned int *ip, unsigned int *mask)
+{
+ int i;
+ unsigned int msk;
+
+ i = scan_ip(&ipm, ip, '/');
+ if (i < 0)
+ return i;
+ if (*ipm) {
+ const char *p = ipm;
+
+ i = 0;
+ while (*p) {
+ if (*p < '0' || *p > '9') {
+ if (*p == '.') {
+ i = scan_ip(&ipm, mask, 0);
+ return i != 32;
+ }
+ return -1;
+ }
+ i *= 10;
+ i += *p - '0';
+ p++;
+ }
+ }
+ if (i > 32 || i < 0)
+ return -1;
+ msk = 0x80000000;
+ *mask = 0;
+ while (i > 0) {
+ *mask |= msk;
+ msk >>= 1;
+ i--;
+ }
+ return 0;
+}
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+static void free_config_lines(Htaccess **pprev)
+{
+ Htaccess *prev = *pprev;
+
+ while (prev) {
+ Htaccess *cur = prev;
+
+ prev = cur->next;
+ free(cur);
+ }
+ *pprev = NULL;
+}
+#endif
+
+/* 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;
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ Htaccess *prev;
+#endif
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ Htaccess *cur;
+#endif
+
+ const char *cf = config->configFile;
+ char buf[160];
+ char *p0 = NULL;
+ char *c, *p;
+
+ /* free previous ip setup if present */
+ Htaccess_IP *pip = config->ip_a_d;
+
+ while (pip) {
+ Htaccess_IP *cur_ipl = pip;
+
+ pip = cur_ipl->next;
+ free(cur_ipl);
+ }
+ config->ip_a_d = NULL;
+
+ config->flg_deny_all = 0;
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ /* retain previous auth and mime config only for subdir parse */
+ if (flag != SUBDIR_PARSE) {
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ free_config_lines(&config->auth);
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ free_config_lines(&config->mime_a);
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ free_config_lines(&config->script_i);
+#endif
+ }
+#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;
+ }
+
+#if ENABLE_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' && *p0 != 'A'
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ && *p0 != '/'
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ && *p0 != '.'
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ && *p0 != '*'
+#endif
+ )
+ continue;
+ if (*p0 == 'A' || *p0 == 'D') {
+ /* storing current config IP line */
+ pip = xzalloc(sizeof(Htaccess_IP));
+ if (pip) {
+ if (scan_ip_mask(c, &(pip->ip), &(pip->mask))) {
+ /* syntax IP{/mask} error detected, protect all */
+ *p0 = 'D';
+ pip->mask = 0;
+ }
+ pip->allow_deny = *p0;
+ if (*p0 == 'D') {
+ /* Deny:form_IP move top */
+ pip->next = config->ip_a_d;
+ config->ip_a_d = pip;
+ } else {
+ /* add to bottom A:form_IP config line */
+ Htaccess_IP *prev_IP = config->ip_a_d;
+
+ if (prev_IP == NULL) {
+ config->ip_a_d = pip;
+ } else {
+ while (prev_IP->next)
+ prev_IP = prev_IP->next;
+ prev_IP->next = pip;
+ }
+ }
+ }
+ continue;
+ }
+#if ENABLE_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
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ /* storing current config line */
+ cur = xzalloc(sizeof(Htaccess) + strlen(p0));
+ if (cur) {
+ cf = strcpy(cur->before_colon, p0);
+ c = strchr(cf, ':');
+ *c++ = 0;
+ cur->after_colon = c;
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ if (*cf == '.') {
+ /* config .mime line move top for overwrite previous */
+ cur->next = config->mime_a;
+ config->mime_a = cur;
+ continue;
+ }
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ if (*cf == '*' && cf[1] == '.') {
+ /* config script interpreter line move top for overwrite previous */
+ cur->next = config->script_i;
+ config->script_i = cur;
+ continue;
+ }
+#endif
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ free(p0);
+ 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;
+ size_t 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
+ }
+#endif
+ }
+ fclose(f);
+}
+
+#if ENABLE_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 = xmalloc(len * 6 + 1);
+ char *p = out;
+ char ch;
+
+ 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 /* 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) option_d . . 1 if called for httpd -d
+ *
+ * $Return: (char *) . . . . A pointer to the decoded string (same as input).
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static char *decodeString(char *orig, int option_d)
+{
+ /* note that decoded string is always shorter than original */
+ char *string = orig;
+ char *ptr = string;
+ char c;
+
+ while ((c = *ptr++) != '\0') {
+ unsigned value1, value2;
+
+ if (option_d && c == '+') {
+ *string++ = ' ';
+ continue;
+ }
+ if (c != '%') {
+ *string++ = c;
+ continue;
+ }
+ if (sscanf(ptr, "%1X", &value1) != 1
+ || sscanf(ptr+1, "%1X", &value2) != 1
+ ) {
+ if (!option_d)
+ return NULL;
+ *string++ = '%';
+ continue;
+ }
+ value1 = value1 * 16 + value2;
+ if (!option_d && (value1 == '/' || value1 == '\0')) {
+ /* caller takes it as indication of invalid
+ * (dangerous wrt exploits) chars */
+ return orig + 1;
+ }
+ *string++ = value1;
+ ptr += 2;
+ }
+ *string = '\0';
+ return orig;
+}
+
+
+#if ENABLE_FEATURE_HTTPD_CGI
+/****************************************************************************
+ * setenv helpers
+ ****************************************************************************/
+static void setenv1(const char *name, const char *value)
+{
+ if (!value)
+ value = "";
+ setenv(name, value, 1);
+}
+static void setenv_long(const char *name, long value)
+{
+ char buf[sizeof(value)*3 + 1];
+ sprintf(buf, "%ld", value);
+ setenv(name, buf, 1);
+}
+#endif
+
+#if ENABLE_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 = (const unsigned char *)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
+
+
+/****************************************************************************
+ *
+ > $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)
+{
+ int fd;
+
+ /* create the socket right now */
+ fd = create_and_bind_stream_or_die(NULL, config->port);
+ xlisten(fd, 9);
+ return fd;
+}
+
+/****************************************************************************
+ *
+ > $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;
+ const char *mime_type;
+ unsigned i;
+ time_t timer = time(0);
+ char timeStr[80];
+ int len;
+ enum {
+ numNames = sizeof(httpResponseNames) / sizeof(httpResponseNames[0])
+ };
+
+ for (i = 0; i < numNames; i++) {
+ if (httpResponseNames[i].type == responseNum) {
+ responseString = httpResponseNames[i].name;
+ infoString = httpResponseNames[i].info;
+ break;
+ }
+ }
+ /* error message is HTML */
+ mime_type = responseNum == HTTP_OK ?
+ config->found_mime_type : "text/html";
+
+ /* emit the current date */
+ strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
+ len = sprintf(buf,
+ "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
+ "Date: %s\r\nConnection: close\r\n",
+ responseNum, responseString, mime_type, timeStr);
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (responseNum == HTTP_UNAUTHORIZED) {
+ len += sprintf(buf+len,
+ "WWW-Authenticate: Basic realm=\"%s\"\r\n",
+ config->realm);
+ }
+#endif
+ if (responseNum == HTTP_MOVED_TEMPORARILY) {
+ len += sprintf(buf+len, "Location: %s/%s%s\r\n",
+ config->found_moved_temporarily,
+ (config->query ? "?" : ""),
+ (config->query ? config->query : ""));
+ }
+
+ if (config->ContentLength != -1) { /* file */
+ strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
+ len += sprintf(buf+len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\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);
+ }
+ if (DEBUG)
+ fprintf(stderr, "headers: '%s'\n", buf);
+ i = config->accepted_socket;
+ if (i == 0) i++; /* write to fd# 1 in inetd mode */
+ return full_write(i, 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.
+ *
+ * $Return: (int) . . . . number of characters read. -1 if error.
+ *
+ ****************************************************************************/
+static int getLine(void)
+{
+ int count = 0;
+ char *buf = config->buf;
+
+ while (read(config->accepted_socket, 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 overflow */
+ count++;
+ }
+ if (count) return count;
+ else return -1;
+}
+
+#if ENABLE_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 /).
+ * (int bodyLen) . . . . . . . . Length of the post body.
+ * (const char *cookie) . . . . . For set HTTP_COOKIE.
+ * (const char *content_type) . . For set CONTENT_TYPE.
+ *
+ * $Return: (char *) . . . . A pointer to the decoded string (same as input).
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static int sendCgi(const char *url,
+ const char *request, int bodyLen, const char *cookie,
+ const char *content_type)
+{
+ 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 buf_count;
+ int status;
+ size_t post_read_size, post_read_idx;
+
+ if (pipe(fromCgi) != 0)
+ return 0;
+ if (pipe(toCgi) != 0)
+ return 0;
+
+/*
+ * Note: We can use vfork() here in the no-mmu case, although
+ * the child modifies the parent's variables, due to:
+ * 1) The parent does not use the child-modified variables.
+ * 2) The allocated memory (in the child) is freed when the process
+ * exits. This happens instantly after the child finishes,
+ * since httpd is run from inetd (and it can't run standalone
+ * in uClinux).
+ */
+#ifdef BB_NOMMU
+ pid = vfork();
+#else
+ pid = fork();
+#endif
+ if (pid < 0)
+ return 0;
+
+ if (!pid) {
+ /* child process */
+ char *script;
+ char *purl;
+ char realpath_buff[MAXPATHLEN];
+
+ if (config->accepted_socket > 1)
+ close(config->accepted_socket);
+ if (config->server_socket > 1)
+ close(config->server_socket);
+
+ dup2(toCgi[0], 0); // replace stdin with the pipe
+ dup2(fromCgi[1], 1); // replace stdout with the pipe
+ /* Huh? User seeing stderr can be a security problem...
+ * and if CGI really wants that, it can always dup2(1,2)...
+ if (!DEBUG)
+ dup2(fromCgi[1], 2); // replace stderr with the pipe
+ */
+ /* I think we cannot inadvertently close 0, 1 here... */
+ close(toCgi[0]);
+ close(toCgi[1]);
+ close(fromCgi[0]);
+ close(fromCgi[1]);
+
+ /*
+ * Find PATH_INFO.
+ */
+ xfunc_error_retval = 242;
+ purl = xstrdup(url);
+ 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 '/' */
+ }
+ setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */
+ /* setenv1("PATH", getenv("PATH")); redundant */
+ setenv1("REQUEST_METHOD", request);
+ if (config->query) {
+ char *uri = alloca(strlen(purl) + 2 + strlen(config->query));
+ if (uri)
+ sprintf(uri, "%s?%s", purl, config->query);
+ setenv1("REQUEST_URI", uri);
+ } else {
+ setenv1("REQUEST_URI", purl);
+ }
+ if (script != NULL)
+ *script = '\0'; /* cut off /PATH_INFO */
+ /* SCRIPT_FILENAME required by PHP in CGI mode */
+ if (!realpath(purl + 1, realpath_buff))
+ goto error_execing_cgi;
+ setenv1("SCRIPT_FILENAME", realpath_buff);
+ /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
+ setenv1("SCRIPT_NAME", purl);
+ /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html:
+ * QUERY_STRING: The information which follows the ? in the URL
+ * which referenced this script. This is the query information.
+ * It should not be decoded in any fashion. This variable
+ * should always be set when there is query information,
+ * regardless of command line decoding. */
+ /* (Older versions of bbox seem to do some decoding) */
+ setenv1("QUERY_STRING", config->query);
+ setenv1("SERVER_SOFTWARE", httpdVersion);
+ putenv((char*)"SERVER_PROTOCOL=HTTP/1.0");
+ putenv((char*)"GATEWAY_INTERFACE=CGI/1.1");
+ /* Having _separate_ variables for IP and port defeats
+ * the purpose of having socket abstraction. Which "port"
+ * are you using on Unix domain socket?
+ * IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense.
+ * Oh well... */
+ {
+ char *p = config->rmt_ip_str ? : (char*)"";
+ char *cp = strrchr(p, ':');
+ if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']'))
+ cp = NULL;
+ if (cp) *cp = '\0'; /* delete :PORT */
+ setenv1("REMOTE_ADDR", p);
+ }
+#if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+ setenv_long("REMOTE_PORT", config->port);
+#endif
+ if (bodyLen)
+ setenv_long("CONTENT_LENGTH", bodyLen);
+ if (cookie)
+ setenv1("HTTP_COOKIE", cookie);
+ if (content_type)
+ setenv1("CONTENT_TYPE", content_type);
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (config->remoteuser) {
+ setenv1("REMOTE_USER", config->remoteuser);
+ putenv((char*)"AUTH_TYPE=Basic");
+ }
+#endif
+ if (config->referer)
+ setenv1("HTTP_REFERER", config->referer);
+
+ /* set execve argp[0] without path */
+ argp[0] = strrchr(purl, '/') + 1;
+ /* but script argp[0] must have absolute path and chdiring to this */
+ script = strrchr(realpath_buff, '/');
+ if (!script)
+ goto error_execing_cgi;
+ *script = '\0';
+ if (chdir(realpath_buff) == 0) {
+ // Now run the program. If it fails,
+ // use _exit() so no destructors
+ // get called and make a mess.
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ char *interpr = NULL;
+ char *suffix = strrchr(purl, '.');
+
+ if (suffix) {
+ Htaccess *cur;
+ for (cur = config->script_i; cur; cur = cur->next) {
+ if (strcmp(cur->before_colon + 1, suffix) == 0) {
+ interpr = cur->after_colon;
+ break;
+ }
+ }
+ }
+#endif
+ *script = '/';
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ if (interpr)
+ execv(interpr, argp);
+ else
+#endif
+ execv(realpath_buff, argp);
+ }
+ error_execing_cgi:
+ /* send to stdout (even if we are not from inetd) */
+ config->accepted_socket = 1;
+ sendHeaders(HTTP_NOT_FOUND);
+ _exit(242);
+ } /* end child */
+
+ /* parent process */
+
+ buf_count = 0;
+ post_read_size = 0;
+ post_read_idx = 0; /* for gcc */
+ inFd = fromCgi[0];
+ outFd = toCgi[1];
+ close(fromCgi[1]);
+ close(toCgi[0]);
+ signal(SIGPIPE, SIG_IGN);
+
+ while (1) {
+ fd_set readSet;
+ fd_set writeSet;
+ char wbuf[128];
+ int nfound;
+ int count;
+
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_SET(inFd, &readSet);
+ if (bodyLen > 0 || post_read_size > 0) {
+ FD_SET(outFd, &writeSet);
+ nfound = outFd > inFd ? outFd : inFd;
+ if (post_read_size == 0) {
+ FD_SET(config->accepted_socket, &readSet);
+ if (nfound < config->accepted_socket)
+ nfound = config->accepted_socket;
+ }
+ /* Now wait on the set of sockets! */
+ nfound = select(nfound + 1, &readSet, &writeSet, NULL, NULL);
+ } else {
+ if (!bodyLen) {
+ close(outFd); /* no more POST data to CGI */
+ bodyLen = -1;
+ }
+ nfound = select(inFd + 1, &readSet, NULL, NULL, NULL);
+ }
+
+ if (nfound <= 0) {
+ if (waitpid(pid, &status, WNOHANG) <= 0) {
+ /* Weird. CGI didn't exit and no fd's
+ * are ready, yet select returned?! */
+ continue;
+ }
+ close(inFd);
+ if (DEBUG && WIFEXITED(status))
+ bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
+ if (DEBUG && WIFSIGNALED(status))
+ bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
+ break;
+ }
+
+ if (post_read_size > 0 && FD_ISSET(outFd, &writeSet)) {
+ /* Have data from peer and can write to CGI */
+ // huh? why full_write? what if we will block?
+ // (imagine that CGI does not read its stdin...)
+ count = full_write(outFd, wbuf + post_read_idx, post_read_size);
+ if (count > 0) {
+ post_read_idx += count;
+ post_read_size -= count;
+ } else {
+ post_read_size = bodyLen = 0; /* broken pipe to CGI */
+ }
+ } else if (bodyLen > 0 && post_read_size == 0
+ && FD_ISSET(config->accepted_socket, &readSet)
+ ) {
+ /* We expect data, prev data portion is eaten by CGI
+ * and there *is* data to read from the peer
+ * (POSTDATA?) */
+ count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen;
+ count = safe_read(config->accepted_socket, wbuf, count);
+ if (count > 0) {
+ post_read_size = count;
+ post_read_idx = 0;
+ bodyLen -= count;
+ } else {
+ bodyLen = 0; /* closed */
+ }
+ }
+
+#define PIPESIZE PIPE_BUF
+#if PIPESIZE >= MAX_MEMORY_BUFF
+# error "PIPESIZE >= MAX_MEMORY_BUFF"
+#endif
+ if (FD_ISSET(inFd, &readSet)) {
+ /* There is something to read from CGI */
+ int s = config->accepted_socket;
+ char *rbuf = config->buf;
+
+ /* Are we still buffering CGI output? */
+ if (buf_count >= 0) {
+ static const char HTTP_200[] = "HTTP/1.0 200 OK\r\n";
+ /* Must use safe_read, not full_read, because
+ * CGI may output a few first bytes and then wait
+ * for POSTDATA without closing stdout.
+ * With full_read we may wait here forever. */
+ count = safe_read(inFd, rbuf + buf_count, PIPESIZE - 4);
+ if (count <= 0) {
+ /* eof (or error) and there was no "HTTP",
+ * so add one and write out the received data */
+ if (buf_count) {
+ full_write(s, HTTP_200, sizeof(HTTP_200)-1);
+ full_write(s, rbuf, buf_count);
+ }
+ break; /* closed */
+ }
+ buf_count += count;
+ count = 0;
+ if (buf_count >= 4) {
+ /* check to see if CGI added "HTTP" */
+ if (memcmp(rbuf, HTTP_200, 4) != 0) {
+ /* there is no "HTTP", do it ourself */
+ if (full_write(s, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
+ break;
+ }
+ /* example of valid CGI without "Content-type:"
+ * echo -en "HTTP/1.0 302 Found\r\n"
+ * echo -en "Location: http://www.busybox.net\r\n"
+ * echo -en "\r\n"
+ if (!strstr(rbuf, "ontent-")) {
+ full_write(s, "Content-type: text/plain\r\n\r\n", 28);
+ }
+ */
+ count = buf_count;
+ buf_count = -1; /* buffering off */
+ }
+ } else {
+ count = safe_read(inFd, rbuf, PIPESIZE);
+ if (count <= 0)
+ break; /* eof (or error) */
+ }
+ if (full_write(s, rbuf, count) != count)
+ break;
+ if (DEBUG)
+ fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
+ } /* if (FD_ISSET(inFd)) */
+ } /* while (1) */
+ return 0;
+}
+#endif /* FEATURE_HTTPD_CGI */
+
+/****************************************************************************
+ *
+ > $Function: sendFile()
+ *
+ * $Description: Send a file response to a HTTP request
+ *
+ * $Parameter:
+ * (const char *) url . . The URL requested.
+ *
+ * $Return: (int) . . . . . . Always 0.
+ *
+ ****************************************************************************/
+static int sendFile(const char *url)
+{
+ 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];
+#if ENABLE_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 /* FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
+
+ if (DEBUG)
+ fprintf(stderr, "sending file '%s' content-type: %s\n",
+ url, config->found_mime_type);
+
+ f = open(url, O_RDONLY);
+ if (f >= 0) {
+ int count;
+ char *buf = config->buf;
+
+ sendHeaders(HTTP_OK);
+ /* TODO: sendfile() */
+ while ((count = full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
+ int fd = config->accepted_socket;
+ if (fd == 0) fd++; /* write to fd# 1 in inetd mode */
+ if (full_write(fd, buf, count) != count)
+ break;
+ }
+ close(f);
+ } else {
+ if (DEBUG)
+ bb_perror_msg("cannot open '%s'", url);
+ sendHeaders(HTTP_NOT_FOUND);
+ }
+
+ return 0;
+}
+
+static int checkPermIP(void)
+{
+ Htaccess_IP * cur;
+
+ /* This could stand some work */
+ for (cur = config->ip_a_d; cur; cur = cur->next) {
+#if ENABLE_FEATURE_HTTPD_CGI && DEBUG
+ fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str);
+#endif
+#if DEBUG
+ fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
+ (unsigned char)(cur->ip >> 24),
+ (unsigned char)(cur->ip >> 16),
+ (unsigned char)(cur->ip >> 8),
+ (unsigned char)(cur->ip),
+ (unsigned char)(cur->mask >> 24),
+ (unsigned char)(cur->mask >> 16),
+ (unsigned char)(cur->mask >> 8),
+ (unsigned char)(cur->mask)
+ );
+#endif
+ if ((config->rmt_ip & cur->mask) == cur->ip)
+ return cur->allow_deny == 'A'; /* Allow/Deny */
+ }
+
+ /* if unconfigured, return 1 - access from all */
+ return !config->flg_deny_all;
+}
+
+/****************************************************************************
+ *
+ > $Function: checkPerm()
+ *
+ * $Description: Check the permission file for access password protected.
+ *
+ * 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.
+ * (const char *) request . . . User information to validate.
+ *
+ * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise.
+ *
+ ****************************************************************************/
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+static int checkPerm(const char *path, const char *request)
+{
+ Htaccess * cur;
+ const char *p;
+ const char *p0;
+
+ const char *prev = NULL;
+
+ /* This could stand some work */
+ for (cur = config->auth; cur; cur = cur->next) {
+ size_t l;
+
+ p0 = cur->before_colon;
+ if (prev != NULL && strcmp(prev, p0) != 0)
+ continue; /* find next identical */
+ p = cur->after_colon;
+ if (DEBUG)
+ fprintf(stderr, "checkPerm: '%s' ? '%s'\n", p0, request);
+
+ l = strlen(p0);
+ if (strncmp(p0, path, l) == 0
+ && (l == 1 || path[l] == '/' || path[l] == '\0')
+ ) {
+ char *u;
+ /* path match found. Check request */
+ /* for check next /path:user:password */
+ prev = p0;
+ u = strchr(request, ':');
+ if (u == NULL) {
+ /* bad request, ':' required */
+ break;
+ }
+
+ if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
+ char *cipher;
+ char *pp;
+
+ 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)
+ goto set_remoteuser_var; /* Ok */
+ /* unauthorized */
+ continue;
+ }
+ }
+
+ if (strcmp(p, request) == 0) {
+set_remoteuser_var:
+ config->remoteuser = strdup(request);
+ if (config->remoteuser)
+ config->remoteuser[(u - request)] = 0;
+ return 1; /* Ok */
+ }
+ /* unauthorized */
+ }
+ } /* for */
+
+ return prev == NULL;
+}
+
+#endif /* FEATURE_HTTPD_BASIC_AUTH */
+
+/****************************************************************************
+ *
+ > $Function: handle_sigalrm()
+ *
+ * $Description: Handle timeouts
+ *
+ ****************************************************************************/
+
+static void handle_sigalrm(int sig)
+{
+ sendHeaders(HTTP_REQUEST_TIMEOUT);
+ config->alarm_signaled = sig;
+}
+
+/****************************************************************************
+ *
+ > $Function: handleIncoming()
+ *
+ * $Description: Handle an incoming http request.
+ *
+ ****************************************************************************/
+static void handleIncoming(void)
+{
+ char *buf = config->buf;
+ char *url;
+ char *purl;
+ int blank = -1;
+ char *test;
+ struct stat sb;
+ int ip_allowed;
+#if ENABLE_FEATURE_HTTPD_CGI
+ const char *prequest = request_GET;
+ unsigned long length = 0;
+ char *cookie = 0;
+ char *content_type = 0;
+#endif
+ fd_set s_fd;
+ struct timeval tv;
+ int retval;
+ struct sigaction sa;
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ int credentials = -1; /* if not required this is Ok */
+#endif
+
+ sa.sa_handler = handle_sigalrm;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0; /* no SA_RESTART */
+ sigaction(SIGALRM, &sa, NULL);
+
+ do {
+ int count;
+
+ (void) alarm(TIMEOUT);
+ if (getLine() <= 0)
+ break; /* closed */
+
+ purl = strpbrk(buf, " \t");
+ if (purl == NULL) {
+ BAD_REQUEST:
+ sendHeaders(HTTP_BAD_REQUEST);
+ break;
+ }
+ *purl = '\0';
+#if ENABLE_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);
+
+ if (count < 1 || buf[0] != '/') {
+ /* Garbled request/URL */
+ goto BAD_REQUEST;
+ }
+ url = alloca(strlen(buf) + sizeof("/index.html"));
+ if (url == NULL) {
+ sendHeaders(HTTP_INTERNAL_SERVER_ERROR);
+ break;
+ }
+ strcpy(url, buf);
+ /* extract url args if present */
+ test = strchr(url, '?');
+ config->query = NULL;
+ if (test) {
+ *test++ = '\0';
+ config->query = test;
+ }
+
+ test = decodeString(url, 0);
+ if (test == NULL)
+ goto BAD_REQUEST;
+ if (test == url+1) {
+ /* '/' or NUL is encoded */
+ sendHeaders(HTTP_NOT_FOUND);
+ break;
+ }
+
+ /* 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 == '/') {
+ /* skip duplicate (or initial) slash */
+ if (*test == '/') {
+ continue;
+ }
+ if (*test == '.') {
+ /* skip extra '.' */
+ if (test[1] == '/' || test[1] == 0) {
+ continue;
+ } else
+ /* '..': be careful */
+ 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)) {
+ config->found_moved_temporarily = url;
+ }
+ }
+ if (DEBUG)
+ fprintf(stderr, "url='%s', args=%s\n", url, config->query);
+
+ test = url;
+ ip_allowed = checkPermIP();
+ 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 = checkPermIP();
+ }
+ *test = '/';
+ }
+ if (blank >= 0) {
+ /* read until blank line for HTTP version specified, else parse immediate */
+ while (1) {
+ alarm(TIMEOUT);
+ count = getLine();
+ if (count <= 0)
+ break;
+
+ if (DEBUG)
+ fprintf(stderr, "header: '%s'\n", buf);
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ /* try and do our best to parse more lines */
+ if ((STRNCASECMP(buf, "Content-length:") == 0)) {
+ /* extra read only for POST */
+ if (prequest != request_GET) {
+ test = buf + sizeof("Content-length:")-1;
+ if (!test[0])
+ goto bail_out;
+ errno = 0;
+ /* not using strtoul: it ignores leading munis! */
+ length = strtol(test, &test, 10);
+ /* length is "ulong", but we need to pass it to int later */
+ /* so we check for negative or too large values in one go: */
+ /* (long -> ulong conv caused negatives to be seen as > INT_MAX) */
+ if (test[0] || errno || length > INT_MAX)
+ goto bail_out;
+ }
+ } else if ((STRNCASECMP(buf, "Cookie:") == 0)) {
+ cookie = strdup(skip_whitespace(buf + sizeof("Cookie:")-1));
+ } else if ((STRNCASECMP(buf, "Content-Type:") == 0)) {
+ content_type = strdup(skip_whitespace(buf + sizeof("Content-Type:")-1));
+ } else if ((STRNCASECMP(buf, "Referer:") == 0)) {
+ config->referer = strdup(skip_whitespace(buf + sizeof("Referer:")-1));
+ }
+#endif
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (STRNCASECMP(buf, "Authorization:") == 0) {
+ /* We only allow Basic credentials.
+ * It shows up as "Authorization: Basic <userid:password>" where
+ * the userid:password is base64 encoded.
+ */
+ test = skip_whitespace(buf + sizeof("Authorization:")-1);
+ if (STRNCASECMP(test, "Basic") != 0)
+ continue;
+ test += sizeof("Basic")-1;
+ /* decodeBase64() skips whitespace itself */
+ decodeBase64(test);
+ credentials = checkPerm(url, test);
+ }
+#endif /* FEATURE_HTTPD_BASIC_AUTH */
+
+ } /* while extra header reading */
+ }
+ alarm(0);
+ if (config->alarm_signaled)
+ break;
+
+ if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
+ /* protect listing [/path]/httpd_conf or IP deny */
+#if ENABLE_FEATURE_HTTPD_CGI
+ FORBIDDEN: /* protect listing /cgi-bin */
+#endif
+ sendHeaders(HTTP_FORBIDDEN);
+ break;
+ }
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (credentials <= 0 && checkPerm(url, ":") == 0) {
+ sendHeaders(HTTP_UNAUTHORIZED);
+ break;
+ }
+#endif
+
+ if (config->found_moved_temporarily) {
+ sendHeaders(HTTP_MOVED_TEMPORARILY);
+ /* clear unforked memory flag */
+ config->found_moved_temporarily = NULL;
+ break;
+ }
+
+ test = url + 1; /* skip first '/' */
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ if (strncmp(test, "cgi-bin", 7) == 0) {
+ if (test[7] == '/' && test[8] == 0)
+ goto FORBIDDEN; /* protect listing cgi-bin/ */
+ sendCgi(url, prequest, length, cookie, content_type);
+ break;
+ }
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ {
+ char *suffix = strrchr(test, '.');
+ if (suffix) {
+ Htaccess *cur;
+ for (cur = config->script_i; cur; cur = cur->next) {
+ if (strcmp(cur->before_colon + 1, suffix) == 0) {
+ sendCgi(url, prequest, length, cookie, content_type);
+ goto bail_out;
+ }
+ }
+ }
+ }
+#endif
+ if (prequest != request_GET) {
+ sendHeaders(HTTP_NOT_IMPLEMENTED);
+ break;
+ }
+#endif /* FEATURE_HTTPD_CGI */
+ if (purl[-1] == '/')
+ strcpy(purl, "index.html");
+ if (stat(test, &sb) == 0) {
+ /* It's a dir URL and there is index.html */
+ config->ContentLength = sb.st_size;
+ config->last_mod = sb.st_mtime;
+ }
+#if ENABLE_FEATURE_HTTPD_CGI
+ else if (purl[-1] == '/') {
+ /* It's a dir URL and there is no index.html
+ * Try cgi-bin/index.cgi */
+ if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
+ purl[0] = '\0';
+ config->query = url;
+ sendCgi("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
+ break;
+ }
+ }
+#endif /* FEATURE_HTTPD_CGI */
+ sendFile(test);
+ config->ContentLength = -1;
+ } while (0);
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ bail_out:
+#endif
+
+ if (DEBUG)
+ fprintf(stderr, "closing socket\n\n");
+#if ENABLE_FEATURE_HTTPD_CGI
+ free(cookie);
+ free(content_type);
+ free(config->referer);
+ config->referer = NULL;
+# if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ free(config->remoteuser);
+ config->remoteuser = NULL;
+# endif
+#endif
+ shutdown(config->accepted_socket, SHUT_WR);
+
+ /* Properly wait for remote to closed */
+ FD_ZERO(&s_fd);
+ FD_SET(config->accepted_socket, &s_fd);
+
+ do {
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ retval = select(config->accepted_socket + 1, &s_fd, NULL, NULL, &tv);
+ } while (retval > 0 && read(config->accepted_socket, buf, sizeof(config->buf) > 0));
+
+ shutdown(config->accepted_socket, SHUT_RD);
+ /* In inetd case, we close fd 1 (stdout) here. We will exit soon anyway */
+ close(config->accepted_socket);
+}
+
+/****************************************************************************
+ *
+ > $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.
+ *
+ ****************************************************************************/
+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) {
+ int s;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ USE_FEATURE_IPV6(struct sockaddr_in6 sin6;)
+ } fromAddr;
+ socklen_t fromAddrLen = sizeof(fromAddr);
+
+ /* Now wait INDEFINITELY on the set of sockets! */
+ readfd = portfd;
+ if (select(server + 1, &readfd, 0, 0, 0) <= 0)
+ continue;
+ if (!FD_ISSET(server, &readfd))
+ continue;
+ s = accept(server, &fromAddr.sa, &fromAddrLen);
+ if (s < 0)
+ continue;
+ config->accepted_socket = s;
+ config->rmt_ip = 0;
+ config->port = 0;
+#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
+ free(config->rmt_ip_str);
+ config->rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr.sa, fromAddrLen);
+#if DEBUG
+ bb_error_msg("connection from '%s'", config->rmt_ip_str);
+#endif
+#endif /* FEATURE_HTTPD_CGI */
+ if (fromAddr.sa.sa_family == AF_INET) {
+ config->rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ config->port = ntohs(fromAddr.sin.sin_port);
+ }
+#if ENABLE_FEATURE_IPV6
+ if (fromAddr.sa.sa_family == AF_INET6) {
+ //config->rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ config->port = ntohs(fromAddr.sin6.sin6_port);
+ }
+#endif
+
+ /* set the KEEPALIVE option to cull dead connections */
+ setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+
+ if (DEBUG || fork() == 0) {
+ /* child */
+#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+ /* protect reload config, may be confuse checking */
+ signal(SIGHUP, SIG_IGN);
+#endif
+ handleIncoming();
+ if (!DEBUG)
+ exit(0);
+ }
+ close(s);
+ } /* while (1) */
+ return 0;
+}
+
+/* from inetd */
+static int miniHttpd_inetd(void)
+{
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ USE_FEATURE_IPV6(struct sockaddr_in6 sin6;)
+ } fromAddr;
+ socklen_t fromAddrLen = sizeof(fromAddr);
+
+ getpeername(0, &fromAddr.sa, &fromAddrLen);
+ config->rmt_ip = 0;
+ config->port = 0;
+#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
+ free(config->rmt_ip_str);
+ config->rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr.sa, fromAddrLen);
+#endif
+ if (fromAddr.sa.sa_family == AF_INET) {
+ config->rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ config->port = ntohs(fromAddr.sin.sin_port);
+ }
+#if ENABLE_FEATURE_IPV6
+ if (fromAddr.sa.sa_family == AF_INET6) {
+ //config->rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ config->port = ntohs(fromAddr.sin6.sin6_port);
+ }
+#endif
+ handleIncoming();
+ return 0;
+}
+
+#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+static void sighup_handler(int sig)
+{
+ /* set and reset */
+ struct sigaction sa;
+
+ parse_conf(default_path_httpd_conf, sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
+ sa.sa_handler = sighup_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGHUP, &sa, NULL);
+}
+#endif
+
+enum {
+ c_opt_config_file = 0,
+ d_opt_decode_url,
+ h_opt_home_httpd,
+ USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
+ USE_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,)
+ USE_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,)
+ USE_FEATURE_HTTPD_SETUID( u_opt_setuid ,)
+ p_opt_port ,
+ p_opt_inetd ,
+ p_opt_foreground,
+ OPT_CONFIG_FILE = 1 << c_opt_config_file,
+ OPT_DECODE_URL = 1 << d_opt_decode_url,
+ OPT_HOME_HTTPD = 1 << h_opt_home_httpd,
+ OPT_ENCODE_URL = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
+ OPT_REALM = USE_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0,
+ OPT_MD5 = USE_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0,
+ OPT_SETUID = USE_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0,
+ OPT_PORT = 1 << p_opt_port,
+ OPT_INETD = 1 << p_opt_inetd,
+ OPT_FOREGROUND = 1 << p_opt_foreground,
+};
+
+static const char httpd_opts[] = "c:d:h:"
+ USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
+ USE_FEATURE_HTTPD_BASIC_AUTH("r:")
+ USE_FEATURE_HTTPD_AUTH_MD5("m:")
+ USE_FEATURE_HTTPD_SETUID("u:")
+ "p:if";
+
+
+int httpd_main(int argc, char *argv[]);
+int httpd_main(int argc, char *argv[])
+{
+ unsigned opt;
+ const char *home_httpd = home;
+ char *url_for_decode;
+ USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
+ const char *s_port;
+ USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
+ USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
+ USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
+
+#if ENABLE_LOCALE_SUPPORT
+ /* Undo busybox.c: we want to speak English in http (dates etc) */
+ setlocale(LC_TIME, "C");
+#endif
+
+ config = xzalloc(sizeof(*config));
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ config->realm = "Web Server Authentication";
+#endif
+ config->port = 80;
+ config->ContentLength = -1;
+
+ opt = getopt32(argc, argv, httpd_opts,
+ &(config->configFile), &url_for_decode, &home_httpd
+ USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
+ USE_FEATURE_HTTPD_BASIC_AUTH(, &(config->realm))
+ USE_FEATURE_HTTPD_AUTH_MD5(, &pass)
+ USE_FEATURE_HTTPD_SETUID(, &s_ugid)
+ , &s_port
+ );
+ if (opt & OPT_DECODE_URL) {
+ printf("%s", decodeString(url_for_decode, 1));
+ return 0;
+ }
+#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
+ if (opt & OPT_ENCODE_URL) {
+ printf("%s", encodeString(url_for_encode));
+ return 0;
+ }
+#endif
+#if ENABLE_FEATURE_HTTPD_AUTH_MD5
+ if (opt & OPT_MD5) {
+ puts(pw_encrypt(pass, "$1$"));
+ return 0;
+ }
+#endif
+ if (opt & OPT_PORT)
+ config->port = xatou16(s_port);
+
+#if ENABLE_FEATURE_HTTPD_SETUID
+ if (opt & OPT_SETUID) {
+ if (!get_uidgid(&ugid, s_ugid, 1))
+ bb_error_msg_and_die("unrecognized user[:group] "
+ "name '%s'", s_ugid);
+ }
+#endif
+
+ xchdir(home_httpd);
+ if (!(opt & OPT_INETD)) {
+ signal(SIGCHLD, SIG_IGN);
+ config->server_socket = openServer();
+#if ENABLE_FEATURE_HTTPD_SETUID
+ /* drop privileges */
+ if (opt & OPT_SETUID) {
+ if (ugid.gid != (gid_t)-1) {
+ if (setgroups(1, &ugid.gid) == -1)
+ bb_perror_msg_and_die("setgroups");
+ xsetgid(ugid.gid);
+ }
+ xsetuid(ugid.uid);
+ }
+#endif
+ }
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ {
+ char *p = getenv("PATH");
+ p = xstrdup(p); /* if gets NULL, returns NULL */
+ clearenv();
+ if (p)
+ setenv1("PATH", p);
+ if (!(opt & OPT_INETD))
+ setenv_long("SERVER_PORT", config->port);
+ }
+#endif
+
+#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+ sighup_handler(0);
+#else
+ parse_conf(default_path_httpd_conf, FIRST_PARSE);
+#endif
+
+ if (opt & OPT_INETD)
+ return miniHttpd_inetd();
+
+ if (!(opt & OPT_FOREGROUND))
+ xdaemon(1, 0); /* don't change current directory */
+ return miniHttpd(config->server_socket);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/httpd_index_cgi_example b/i/pc104/initrd/conf/busybox/networking/httpd_index_cgi_example
new file mode 100644
index 0000000..9c8e022
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/httpd_index_cgi_example
@@ -0,0 +1,50 @@
+#!/bin/sh
+# This CGI creates directory index.
+# Put it into cgi-bin/index.cgi and chmod 0755.
+#
+# Problems:
+# * Unsafe wrt weird filenames with <>"'& etc...
+# * Not efficient: calls stat (program, not syscall) for each file
+# * Probably requires bash
+#
+# If you want speed and safety, you need to code it in C
+
+# Must start with '/'
+test "${QUERY_STRING:0:1}" = "/" || exit 1
+# /../ is not allowed
+test "${QUERY_STRING%/../*}" = "$QUERY_STRING" || exit 1
+test "${QUERY_STRING%/..}" = "$QUERY_STRING" || exit 1
+
+# Outta cgi-bin...
+cd .. 2>/dev/null || exit 1
+# Strip leading '/', go to target dir
+cd "${QUERY_STRING:1}" 2>/dev/null || exit 1
+
+f=`dirname "$QUERY_STRING"`
+test "$f" = "/" && f=""
+
+printf "%s" \
+$'HTTP/1.0 200 OK\r\n'\
+$'Content-type: text/html\r\n\r\n'\
+"<html><head><title>Index of $QUERY_STRING</title></head>"$'\r\n'\
+"<body><h1>Index of $QUERY_STRING</h1><pre>"$'\r\n'\
+$'<table width=100%>\r\n'\
+$'<col><col><col width=0*>\r\n'\
+$'<tr><th>Name<th align=right>Last modified<th align=right>Size\r\n'\
+\
+"<tr><td><a href='$f/'>..</a><td><td>"$'\r\n'
+
+IFS='#'
+for f in *; do
+ # Guard against empty dirs...
+ test -e "$f" && \
+ stat -c "%F#%s#%z" "$f" | {
+ read type size cdt junk
+ dir=''
+ test "$type" = "directory" && dir='/'
+ cdt="${cdt//.*}" # no fractional seconds
+ cdt="${cdt// /&nbsp;}" # prevent wrapping around space
+ printf "%s" "<tr><td><a href='$f$dir'>$f</a><td align=right>$cdt<td align=right>$size"$'\r\n'
+ }
+done
+printf "</table></pre><hr></body></html>"$'\r\n'
diff --git a/i/pc104/initrd/conf/busybox/networking/ifconfig.c b/i/pc104/initrd/conf/busybox/networking/ifconfig.c
new file mode 100644
index 0000000..3dcc902
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ifconfig.c
@@ -0,0 +1,546 @@
+/* vi: set sw=4 ts=4: */
+/* 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>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/*
+ * 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 <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#if defined(__GLIBC__) && __GLIBC__ >=2 && __GLIBC_MINOR__ >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <sys/types.h>
+#include <netinet/if_ether.h>
+#endif
+#include "inet_common.h"
+#include "busybox.h"
+
+#if ENABLE_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
+
+#if ENABLE_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. */
+#if ENABLE_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_ARG_REQ | 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;
+ int selector;
+ unsigned short ifr_offset;
+};
+
+struct options {
+ const char *name;
+#if ENABLE_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)},
+#if ENABLE_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
+#if ENABLE_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. */
+#if ENABLE_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},
+#if ENABLE_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
+#if ENABLE_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
+#if ENABLE_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.
+ */
+
+#if ENABLE_FEATURE_IFCONFIG_HW
+static int in_ether(const char *bufp, struct sockaddr *sap);
+#endif
+
+/*
+ * Our main function.
+ */
+
+int ifconfig_main(int argc, char **argv);
+int ifconfig_main(int argc, char **argv)
+{
+ struct ifreq ifr;
+ struct sockaddr_in sai;
+#if ENABLE_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 selector;
+#if ENABLE_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];*/
+ const char *host = NULL; /* make gcc happy */
+
+ did_flags = 0;
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+ sai_hostname = 0;
+ sai_netmask = 0;
+#endif
+
+ /* skip argv[0] */
+ ++argv;
+ --argc;
+
+#if ENABLE_FEATURE_IFCONFIG_STATUS
+ if (argc > 0 && (argv[0][0] == '-' && argv[0][1] == 'a' && !argv[0][2])) {
+ interface_opt_a = 1;
+ --argc;
+ ++argv;
+ }
+#endif
+
+ if (argc <= 1) {
+#if ENABLE_FEATURE_IFCONFIG_STATUS
+ return display_interfaces(argc ? *argv : NULL);
+#else
+ bb_error_msg_and_die("no support for status display");
+#endif
+ }
+
+ /* Create a channel to the NET kernel. */
+ sockfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ /* 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... */
+ mask &= op->flags;
+ if (mask) /* set the mask and go. */
+ goto FOUND_ARG;
+ /* If we get here, there was a valid arg with an */
+ /* invalid '-' prefix. */
+ bb_error_msg_and_die("bad: '%s'", p-1);
+ }
+ }
+
+ /* 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();
+ --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) {
+#if ENABLE_FEATURE_IFCONFIG_HW
+ if (mask & A_CAST_RESOLVE) {
+#endif
+#if ENABLE_FEATURE_IPV6
+ char *prefix;
+ int prefix_len = 0;
+#endif
+ /*safe_strncpy(host, *argv, (sizeof host));*/
+ host = *argv;
+#if ENABLE_FEATURE_IPV6
+ prefix = strchr(host, '/');
+ if (prefix) {
+ prefix_len = xatou_range(prefix + 1, 0, 128);
+ *prefix = '\0';
+ }
+#endif
+ sai.sin_family = AF_INET;
+ sai.sin_port = 0;
+ if (!strcmp(host, bb_str_default)) {
+ /* Default is special, meaning 0.0.0.0. */
+ sai.sin_addr.s_addr = INADDR_ANY;
+ }
+#if ENABLE_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
+ else {
+ len_and_sockaddr *lsa;
+ if (strcmp(host, "inet") == 0)
+ continue; /* compat stuff */
+ lsa = xhost2sockaddr(host, 0);
+#if ENABLE_FEATURE_IPV6
+ if (lsa->sa.sa_family == AF_INET6) {
+ int sockfd6;
+ struct in6_ifreq ifr6;
+
+ memcpy((char *) &ifr6.ifr6_addr,
+ (char *) &(lsa->sin6.sin6_addr),
+ sizeof(struct in6_addr));
+
+ /* Create a channel to the NET kernel. */
+ sockfd6 = xsocket(AF_INET6, SOCK_DGRAM, 0);
+ if (ioctl(sockfd6, SIOGIFINDEX, &ifr) < 0)
+ bb_perror_msg_and_die("SIOGIFINDEX");
+ ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+ ifr6.ifr6_prefixlen = prefix_len;
+ if (ioctl(sockfd6, a1op->selector, &ifr6) < 0)
+ bb_perror_msg_and_die(a1op->name);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(lsa);
+ continue;
+ }
+#endif
+ sai.sin_addr = lsa->sin.sin_addr;
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(lsa);
+ }
+#if ENABLE_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;
+#if ENABLE_FEATURE_IFCONFIG_HW
+ } else { /* A_CAST_HOST_COPY_IN_ETHER */
+ /* This is the "hw" arg case. */
+ if (strcmp("ether", *argv) || !*++argv)
+ bb_show_usage();
+ /*safe_strncpy(host, *argv, sizeof(host));*/
+ host = *argv;
+ if (in_ether(host, &sa))
+ bb_error_msg_and_die("invalid hw-addr %s", host);
+ p = (char *) &sa;
+ }
+#endif
+ memcpy( (((char *)&ifr) + a1op->ifr_offset),
+ p, sizeof(struct sockaddr));
+ } else {
+ /* FIXME: error check?? */
+ unsigned long i = strtoul(*argv, NULL, 0);
+ p = ((char *)&ifr) + a1op->ifr_offset;
+#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ if (mask & A_MAP_TYPE) {
+ if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0)
+ bb_perror_msg_and_die("SIOCGIFMAP");
+ 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)
+ bb_perror_msg_and_die(a1op->name);
+#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)
+ bb_perror_msg_and_die("SIOCGIFFLAGS");
+ selector = op->selector;
+ if (mask & SET_MASK)
+ ifr.ifr_flags |= selector;
+ else
+ ifr.ifr_flags &= ~selector;
+ if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0)
+ bb_perror_msg_and_die("SIOCSIFFLAGS");
+ } /* while () */
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(sockfd);
+ return 0;
+}
+
+#if ENABLE_FEATURE_IFCONFIG_HW
+/* Input an Ethernet address and convert to binary. */
+static int in_ether(const char *bufp, struct sockaddr *sap)
+{
+ 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 *bufp; /* Error if we don't end at end of string. */
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/ifupdown.c b/i/pc104/initrd/conf/busybox/networking/ifupdown.c
new file mode 100644
index 0000000..887c2ee
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ifupdown.c
@@ -0,0 +1,1273 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ifupdown for busybox
+ * Copyright (c) 2002 Glenn McGrath <bug1@iinet.net.au>
+ * Copyright (c) 2003-2004 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.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <sys/utsname.h>
+#include <fnmatch.h>
+#include <getopt.h>
+
+#define MAX_OPT_DEPTH 10
+#define EUNBALBRACK 10001
+#define EUNDEFVAR 10002
+#define EUNBALPER 10000
+
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+#define MAX_INTERFACE_LENGTH 10
+#endif
+
+#define debug_noise(args...) /*fprintf(stderr, args)*/
+
+/* Forward declaration */
+struct interface_defn_t;
+
+typedef int execfn(char *command);
+
+struct method_t {
+ const char *name;
+ int (*up)(struct interface_defn_t *ifd, execfn *e);
+ int (*down)(struct interface_defn_t *ifd, execfn *e);
+};
+
+struct address_family_t {
+ const char *name;
+ int n_methods;
+ const 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 {
+ const struct address_family_t *address_family;
+ const struct method_t *method;
+
+ char *iface;
+ 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;
+};
+
+#define OPTION_STR "anvf" USE_FEATURE_IFUPDOWN_MAPPING("m") "i:"
+enum {
+ OPT_do_all = 0x1,
+ OPT_no_act = 0x2,
+ OPT_verbose = 0x4,
+ OPT_force = 0x8,
+ OPT_no_mappings = 0x10,
+};
+#define DO_ALL (option_mask32 & OPT_do_all)
+#define NO_ACT (option_mask32 & OPT_no_act)
+#define VERBOSE (option_mask32 & OPT_verbose)
+#define FORCE (option_mask32 & OPT_force)
+#define NO_MAPPINGS (option_mask32 & OPT_no_mappings)
+
+static char **my_environ;
+
+static const char *startup_PATH;
+
+#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
+
+#if ENABLE_FEATURE_IFUPDOWN_IP
+
+static unsigned count_bits(unsigned a)
+{
+ unsigned 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 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;
+ // FIXME: will be confused by e.g. 255.0.255.0
+ result = count_bits(a);
+ result += count_bits(b);
+ result += count_bits(c);
+ result += count_bits(d);
+ return (int)result;
+}
+#endif
+
+static void addstr(char **bufp, const char *str, size_t str_length)
+{
+ /* xasprintf trick will be smaller, but we are often
+ * called with str_length == 1 - don't want to have
+ * THAT much of malloc/freeing! */
+ char *buf = *bufp;
+ int len = (buf ? strlen(buf) : 0);
+ str_length++;
+ buf = xrealloc(buf, len + str_length);
+ /* copies at most str_length-1 chars! */
+ safe_strncpy(buf + len, str, str_length);
+ *bufp = buf;
+}
+
+static int strncmpz(const char *l, const char *r, size_t llen)
+{
+ int i = strncmp(l, r, llen);
+
+ if (i == 0)
+ return -r[llen];
+ return i;
+}
+
+static char *get_var(const 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];
+ safe_strncpy(label_buf, ifd->iface, sizeof(label_buf));
+ result = strchr(label_buf, ':');
+ if (result) {
+ *result = '\0';
+ }
+ return label_buf;
+ }
+ if (strncmpz(id, "label", idlen) == 0) {
+ return ifd->iface;
+ }
+ 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(const char *command, struct interface_defn_t *ifd)
+{
+ size_t old_pos[MAX_OPT_DEPTH] = { 0 };
+ int okay[MAX_OPT_DEPTH] = { 1 };
+ int opt_depth = 1;
+ char *result = NULL;
+
+ while (*command) {
+ switch (*command) {
+ default:
+ addstr(&result, command, 1);
+ command++;
+ break;
+ case '\\':
+ if (command[1]) {
+ addstr(&result, command + 1, 1);
+ command += 2;
+ } else {
+ addstr(&result, command, 1);
+ command++;
+ }
+ break;
+ case '[':
+ if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
+ old_pos[opt_depth] = result ? strlen(result) : 0;
+ okay[opt_depth] = 1;
+ opt_depth++;
+ command += 2;
+ } else {
+ addstr(&result, "[", 1);
+ command++;
+ }
+ break;
+ case ']':
+ if (command[1] == ']' && opt_depth > 1) {
+ opt_depth--;
+ if (!okay[opt_depth]) {
+ result[old_pos[opt_depth]] = '\0';
+ }
+ command += 2;
+ } else {
+ addstr(&result, "]", 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, varvalue, strlen(varvalue));
+ } else {
+#if ENABLE_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) {
+ unsigned res;
+ varvalue = get_var("netmask", 7, ifd);
+ if (varvalue && (res = count_netmask_bits(varvalue)) > 0) {
+ const char *argument = utoa(res);
+ addstr(&result, argument, 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;
+}
+
+/* execute() returns 1 for success and 0 for failure */
+static int execute(const char *command, struct interface_defn_t *ifd, execfn *exec)
+{
+ char *out;
+ int ret;
+
+ out = parse(command, ifd);
+ if (!out) {
+ /* parse error? */
+ return 0;
+ }
+ /* out == "": parsed ok but not all needed variables known, skip */
+ ret = out[0] ? (*exec)(out) : 1;
+
+ free(out);
+ if (ret != 1) {
+ return 0;
+ }
+ return 1;
+}
+#endif
+
+#if ENABLE_FEATURE_IFUPDOWN_IPV6
+static int loopback_up6(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ int result;
+ result = execute("ip addr add ::1 dev %iface%", ifd, exec);
+ result += execute("ip link set %iface% up", ifd, exec);
+ return ((result == 2) ? 2 : 0);
+#else
+ return execute("ifconfig %iface% add ::1", ifd, exec);
+#endif
+}
+
+static int loopback_down6(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_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;
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec);
+ result += execute("ip link set[[ mtu %mtu%]][[ address %hwaddress%]] %iface% up", ifd, exec);
+ /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */
+ 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 == 3) ? 3 : 0);
+}
+
+static int static_down6(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ return execute("ip link set %iface% down", ifd, exec);
+#else
+ return execute("ifconfig %iface% down", ifd, exec);
+#endif
+}
+
+#if ENABLE_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%", ifd, exec);
+ result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec);
+ return ((result == 4) ? 4 : 0);
+}
+
+static int v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
+{
+ return execute("ip tunnel del %iface%", ifd, exec);
+}
+#endif
+
+static const struct method_t methods6[] = {
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ { "v4tunnel", v4tunnel_up, v4tunnel_down, },
+#endif
+ { "static", static_up6, static_down6, },
+ { "loopback", loopback_up6, loopback_down6, },
+};
+
+static const struct address_family_t addr_inet6 = {
+ "inet6",
+ sizeof(methods6) / sizeof(struct method_t),
+ methods6
+};
+#endif /* FEATURE_IFUPDOWN_IPV6 */
+
+#if ENABLE_FEATURE_IFUPDOWN_IPV4
+static int loopback_up(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ int result;
+ result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec);
+ result += execute("ip link set %iface% up", ifd, exec);
+ return ((result == 2) ? 2 : 0);
+#else
+ return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec);
+#endif
+}
+
+static int loopback_down(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_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 == 2) ? 2 : 0);
+#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;
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
+ "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec);
+ result += execute("ip link set[[ mtu %mtu%]][[ address %hwaddress%]] %iface% up", ifd, exec);
+ result += execute("[[ip route add default via %gateway% dev %iface%]]", ifd, exec);
+ return ((result == 3) ? 3 : 0);
+#else
+ /* ifconfig said to set iface up before it processes hw %hwaddress%,
+ * which then of course fails. Thus we run two separate ifconfig */
+ result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
+ ifd, exec);
+ result += execute("ifconfig %iface% %address% netmask %netmask%"
+ "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
+ ifd, exec);
+ result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec);
+ return ((result == 3) ? 3 : 0);
+#endif
+}
+
+static int static_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ int result;
+#if ENABLE_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 == 2) ? 2 : 0);
+}
+
+#if !ENABLE_APP_UDHCPC
+struct dhcp_client_t
+{
+ const char *name;
+ const char *startcmd;
+ const char *stopcmd;
+};
+
+static const struct dhcp_client_t ext_dhcp_clients[] = {
+ { "udhcpc",
+ "udhcpc -R -n -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
+ "kill -TERM `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
+ },
+ { "pump",
+ "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
+ "pump -i %iface% -k",
+ },
+ { "dhclient",
+ "dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
+ "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
+ },
+ { "dhcpcd",
+ "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%",
+ "dhcpcd -k %iface%",
+ },
+};
+#endif
+
+static int dhcp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_APP_UDHCPC
+ return execute("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid "
+ "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
+ ifd, exec);
+#else
+ int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]);
+ for (i = 0; i < nclients; i++) {
+ if (exists_execable(ext_dhcp_clients[i].name))
+ return execute(ext_dhcp_clients[i].startcmd, ifd, exec);
+ }
+ bb_error_msg("no dhcp clients found");
+ return 0;
+#endif
+}
+
+static int dhcp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_APP_UDHCPC
+ return execute("kill -TERM "
+ "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
+#else
+ int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]);
+ for (i = 0; i < nclients; i++) {
+ if (exists_execable(ext_dhcp_clients[i].name))
+ return execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
+ }
+ bb_error_msg("no dhcp clients found, using static interface shutdown");
+ return static_down(ifd, exec);
+#endif
+}
+
+static int manual_up_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ return 1;
+}
+
+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 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("start-stop-daemon --start -x wvdial "
+ "-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd, exec);
+}
+
+static int wvdial_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ return execute("start-stop-daemon --stop -x wvdial "
+ "-p /var/run/wvdial.%iface% -s 2", ifd, exec);
+}
+
+static const struct method_t methods[] = {
+ { "manual", manual_up_down, manual_up_down, },
+ { "wvdial", wvdial_up, wvdial_down, },
+ { "ppp", ppp_up, ppp_down, },
+ { "static", static_up, static_down, },
+ { "bootp", bootp_up, static_down, },
+ { "dhcp", dhcp_up, dhcp_down, },
+ { "loopback", loopback_up, loopback_down, },
+};
+
+static const struct address_family_t addr_inet = {
+ "inet",
+ sizeof(methods) / sizeof(struct method_t),
+ methods
+};
+
+#endif /* if ENABLE_FEATURE_IFUPDOWN_IPV4 */
+
+static char *next_word(char **buf)
+{
+ unsigned short length;
+ char *word;
+
+ if (!buf || !*buf || !**buf) {
+ return NULL;
+ }
+
+ /* Skip over leading whitespace */
+ word = skip_whitespace(*buf);
+
+ /* 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 const struct address_family_t *get_address_family(const struct address_family_t *const af[], char *name)
+{
+ int i;
+
+ if (!name)
+ return NULL;
+
+ for (i = 0; af[i]; i++) {
+ if (strcmp(af[i]->name, name) == 0) {
+ return af[i];
+ }
+ }
+ return NULL;
+}
+
+static const struct method_t *get_method(const struct address_family_t *af, char *name)
+{
+ int i;
+
+ if (!name)
+ return NULL;
+
+ for (i = 0; i < af->n_methods; i++) {
+ if (strcmp(af->method[i].name, name) == 0) {
+ return &af->method[i];
+ }
+ }
+ return NULL;
+}
+
+static const llist_t *find_list_string(const llist_t *list, const char *string)
+{
+ if (string == NULL)
+ return NULL;
+
+ while (list) {
+ if (strcmp(list->data, string) == 0) {
+ return list;
+ }
+ list = list->link;
+ }
+ return NULL;
+}
+
+static struct interfaces_file_t *read_interfaces(const char *filename)
+{
+#if ENABLE_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 = xzalloc(sizeof(struct interfaces_file_t));
+
+ f = xfopen(filename, "r");
+
+ while ((buf = xmalloc_getline(f)) != NULL) {
+ char *buf_ptr = buf;
+
+ firstword = next_word(&buf_ptr);
+ if (firstword == NULL) {
+ free(buf);
+ continue; /* blank line */
+ }
+
+ if (strcmp(firstword, "mapping") == 0) {
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+ currmap = xzalloc(sizeof(struct mapping_defn_t));
+
+ 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++] = 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) {
+ static const struct address_family_t *const addr_fams[] = {
+#if ENABLE_FEATURE_IFUPDOWN_IPV4
+ &addr_inet,
+#endif
+#if ENABLE_FEATURE_IFUPDOWN_IPV6
+ &addr_inet6,
+#endif
+ NULL
+ };
+
+ char *iface_name;
+ char *address_family_name;
+ char *method_name;
+ llist_t *iface_list;
+
+ currif = xzalloc(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 */
+ buf_ptr = skip_whitespace(buf_ptr);
+
+ if (buf_ptr[0] != '\0') {
+ bb_error_msg("too many parameters \"%s\"", buf);
+ return NULL;
+ }
+
+ currif->iface = 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\"", address_family_name);
+ return NULL;
+ }
+
+ currif->method = get_method(currif->address_family, method_name);
+ if (!currif->method) {
+ bb_error_msg("unknown method \"%s\"", method_name);
+ return NULL;
+ }
+
+ for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) {
+ struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data;
+ if ((strcmp(tmp->iface, currif->iface) == 0) &&
+ (tmp->address_family == currif->address_family)) {
+ bb_error_msg("duplicate interface \"%s\"", tmp->iface);
+ return NULL;
+ }
+ }
+ 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 */
+ llist_add_to_end(&(defn->autointerfaces), xstrdup(firstword));
+ debug_noise("\nauto %s\n", firstword);
+ }
+ currently_processing = NONE;
+ } else {
+ switch (currently_processing) {
+ case IFACE:
+ {
+ int i;
+
+ if (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 = xstrdup(firstword);
+ currif->option[currif->n_options].value = 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:
+#if ENABLE_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 = 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] = 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(const char *format, const char *name, const char *value)
+{
+ char *result;
+ char *here;
+ char *there;
+
+ result = xasprintf(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, strlen(there) + 1);
+
+ return result;
+}
+
+static void set_environ(struct interface_defn_t *iface, const char *mode)
+{
+ char **environend;
+ int i;
+ const int n_env_entries = iface->n_options + 5;
+ char **ppch;
+
+ if (my_environ != NULL) {
+ for (ppch = my_environ; *ppch; ppch++) {
+ free(*ppch);
+ *ppch = NULL;
+ }
+ free(my_environ);
+ }
+ my_environ = xzalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ ));
+ environend = my_environ;
+
+ 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++) = setlocalenv("%s=%s", "IFACE", iface->iface);
+ *(environend++) = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
+ *(environend++) = setlocalenv("%s=%s", "METHOD", iface->method->name);
+ *(environend++) = setlocalenv("%s=%s", "MODE", mode);
+ *(environend++) = setlocalenv("%s=%s", "PATH", startup_PATH);
+}
+
+static int doit(char *str)
+{
+ if (option_mask32 & (OPT_no_act|OPT_verbose)) {
+ puts(str);
+ }
+ if (!(option_mask32 & OPT_no_act)) {
+ pid_t child;
+ int status;
+
+ fflush(NULL);
+ child = fork();
+ switch (child) {
+ case -1: /* failure */
+ return 0;
+ case 0: /* child */
+ execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, my_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, const char *opt)
+{
+ int i;
+ char *buf;
+ for (i = 0; i < ifd->n_options; i++) {
+ if (strcmp(ifd->option[i].name, opt) == 0) {
+ if (!doit(ifd->option[i].value)) {
+ return 0;
+ }
+ }
+ }
+
+ buf = xasprintf("run-parts /etc/network/if-%s.d", opt);
+ /* heh, we don't bother free'ing it */
+ return doit(buf);
+}
+
+static int check(char *str)
+{
+ return str != NULL;
+}
+
+static int iface_up(struct interface_defn_t *iface)
+{
+ if (!iface->method->up(iface, check)) return -1;
+ set_environ(iface, "start");
+ if (!execute_all(iface, "pre-up")) return 0;
+ if (!iface->method->up(iface, doit)) return 0;
+ if (!execute_all(iface, "up")) return 0;
+ return 1;
+}
+
+static int iface_down(struct interface_defn_t *iface)
+{
+ if (!iface->method->down(iface,check)) return -1;
+ set_environ(iface, "stop");
+ if (!execute_all(iface, "down")) return 0;
+ if (!iface->method->down(iface, doit)) return 0;
+ if (!execute_all(iface, "post-down")) return 0;
+ return 1;
+}
+
+#if ENABLE_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]);
+ BB_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 = 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 = 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 + 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 our
+ * freshly allocated memory. */
+ free(new_logical);
+ }
+ }
+
+ fclose(out);
+
+ return logical;
+}
+#endif /* FEATURE_IFUPDOWN_MAPPING */
+
+static llist_t *find_iface_state(llist_t *state_list, const char *iface)
+{
+ unsigned short iface_len = 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;
+}
+
+int ifupdown_main(int argc, char **argv);
+int ifupdown_main(int argc, char **argv)
+{
+ int (*cmds)(struct interface_defn_t *) = NULL;
+ struct interfaces_file_t *defn;
+ llist_t *state_list = NULL;
+ llist_t *target_list = NULL;
+ const char *interfaces = "/etc/network/interfaces";
+ FILE *state_fp;
+ bool any_failures = 0;
+
+ cmds = iface_down;
+ if (applet_name[2] == 'u') {
+ /* ifup command */
+ cmds = iface_up;
+ }
+
+ getopt32(argc, argv, OPTION_STR, &interfaces);
+ 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) {
+ return EXIT_FAILURE;
+ }
+
+ startup_PATH = getenv("PATH");
+ if (!startup_PATH) startup_PATH = "";
+
+ /* Read the previous state from the state file */
+ state_fp = fopen("/var/run/ifstate", "r");
+ if (state_fp) {
+ char *start, *end_ptr;
+ while ((start = xmalloc_fgets(state_fp)) != NULL) {
+ /* We should only need to check for a single character */
+ end_ptr = start + strcspn(start, " \t\n");
+ *end_ptr = '\0';
+ llist_add_to(&state_list, start);
+ }
+ fclose(state_fp);
+ }
+
+ /* Create a list of interfaces to work on */
+ if (DO_ALL) {
+ if (cmds == iface_up) {
+ target_list = defn->autointerfaces;
+ } else {
+ /* iface_down */
+ const llist_t *list = state_list;
+ while (list) {
+ llist_add_to_end(&target_list, xstrdup(list->data));
+ list = list->link;
+ }
+ target_list = defn->autointerfaces;
+ }
+ } else {
+ 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;
+ bool okay = 0;
+ unsigned cmds_ret;
+
+ iface = xstrdup(target_list->data);
+ target_list = target_list->link;
+
+ pch = strchr(iface, '=');
+ if (pch) {
+ *pch = '\0';
+ liface = xstrdup(pch + 1);
+ } else {
+ liface = xstrdup(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;
+ }
+ }
+ }
+
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+ if ((cmds == iface_up) && !NO_MAPPINGS) {
+ struct mapping_defn_t *currmap;
+
+ for (currmap = defn->mappings; currmap; currmap = currmap->next) {
+ int i;
+ 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() */
+ cmds_ret = cmds(currif);
+ if (cmds_ret == -1) {
+ bb_error_msg("don't seem to have all the variables for %s/%s",
+ liface, currif->address_family->name);
+ any_failures = 1;
+ } else if (cmds_ret == 0) {
+ any_failures = 1;
+ }
+
+ currif->iface = oldiface;
+ }
+ iface_list = iface_list->link;
+ }
+ if (VERBOSE) {
+ puts("");
+ }
+
+ if (!okay && !FORCE) {
+ bb_error_msg("ignoring unknown interface %s", liface);
+ any_failures = 1;
+ } else {
+ llist_t *iface_state = find_iface_state(state_list, iface);
+
+ if (cmds == iface_up) {
+ char * const newiface = xasprintf("%s=%s", iface, liface);
+ if (iface_state == NULL) {
+ llist_add_to_end(&state_list, newiface);
+ } else {
+ free(iface_state->data);
+ iface_state->data = newiface;
+ }
+ } else {
+ /* Remove an interface from state_list */
+ llist_unlink(&state_list, iface_state);
+ free(llist_pop(&iface_state));
+ }
+ }
+ }
+
+ /* Actually write the new state */
+ if (!NO_ACT) {
+ state_fp = xfopen("/var/run/ifstate", "w");
+ while (state_list) {
+ if (state_list->data) {
+ fprintf(state_fp, "%s\n", state_list->data);
+ }
+ state_list = state_list->link;
+ }
+ fclose(state_fp);
+ }
+
+ return any_failures;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/inetd.c b/i/pc104/initrd/conf/busybox/networking/inetd.c
new file mode 100644
index 0000000..c037d11
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/inetd.c
@@ -0,0 +1,1791 @@
+/* vi: set sw=4 ts=4: */
+/* $Slackware: inetd.c 1.79s 2001/02/06 13:18:00 volkerdi Exp $ */
+/* $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $ */
+/* $NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $ */
+/* Busybox port by Vladimir Oleynik (C) 2001-2005 <dzo@simtreas.ru> */
+/*
+ * Copyright (c) 1983,1991 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+/* 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 begin 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] or user[:group] user/group to run daemon as
+ * server program full path name
+ * server program arguments maximum of MAXARGS (20)
+ *
+ * For RPC services
+ * service name/version must be in /etc/rpc
+ * socket type stream/dgram/raw/rdm/seqpacket
+ * protocol must be in /etc/protocols
+ * wait/nowait[.max] single-threaded/multi-threaded
+ * user[.group] or user[:group] user to run daemon as
+ * server program full path name
+ * server program arguments maximum of MAXARGS (20)
+ *
+ * For non-RPC services, the "service name" can be of the form
+ * hostaddress:servicename, in which case the hostaddress is used
+ * as the host portion of the address to listen on. If hostaddress
+ * consists of a single `*' character, INADDR_ANY is used.
+ *
+ * A line can also consist of just
+ * hostaddress:
+ * where hostaddress is as in the preceding paragraph. Such a line must
+ * have no further fields; the specified hostaddress is remembered and
+ * used for all further lines that have no hostaddress specified,
+ * until the next such line (or EOF). (This is why * is provided to
+ * allow explicit specification of INADDR_ANY.) A line
+ * *:
+ * is implicitly in effect at the beginning of the file.
+ *
+ * The hostaddress specifier may (and often will) contain dots;
+ * the service name must not.
+ *
+ * For RPC services, host-address specifiers are accepted and will
+ * work to some extent; however, because of limitations in the
+ * portmapper interface, it will not work to try to give more than
+ * one line for any given RPC service, even if the host-address
+ * specifiers are different.
+ *
+ * Comment lines are indicated by a `#' in column 1.
+ */
+
+/* inetd rules for passing file descriptors to children
+ * (http://www.freebsd.org/cgi/man.cgi?query=inetd):
+ *
+ * The wait/nowait entry specifies whether the server that is invoked by
+ * inetd will take over the socket associated with the service access point,
+ * and thus whether inetd should wait for the server to exit before listen-
+ * ing for new service requests. Datagram servers must use "wait", as
+ * they are always invoked with the original datagram socket bound to the
+ * specified service address. These servers must read at least one datagram
+ * from the socket before exiting. If a datagram server connects to its
+ * peer, freeing the socket so inetd can receive further messages on the
+ * socket, it is said to be a "multi-threaded" server; it should read one
+ * datagram from the socket and create a new socket connected to the peer.
+ * It should fork, and the parent should then exit to allow inetd to check
+ * for new service requests to spawn new servers. Datagram servers which
+ * process all incoming datagrams on a socket and eventually time out are
+ * said to be "single-threaded". The comsat(8), (biff(1)) and talkd(8)
+ * utilities are both examples of the latter type of datagram server. The
+ * tftpd(8) utility is an example of a multi-threaded datagram server.
+ *
+ * Servers using stream sockets generally are multi-threaded and use the
+ * "nowait" entry. Connection requests for these services are accepted by
+ * inetd, and the server is given only the newly-accepted socket connected
+ * to a client of the service. Most stream-based services operate in this
+ * manner. Stream-based servers that use "wait" are started with the lis-
+ * tening service socket, and must accept at least one connection request
+ * before exiting. Such a server would normally accept and process incoming
+ * connection requests until a timeout.
+ */
+
+/* Here's the scoop concerning the user[.:]group feature:
+ *
+ * 1) set-group-option off.
+ *
+ * a) user = root: NO setuid() or setgid() is done
+ *
+ * b) other: setgid(primary group as found in passwd)
+ * initgroups(name, primary group)
+ * setuid()
+ *
+ * 2) set-group-option on.
+ *
+ * a) user = root: setgid(specified group)
+ * NO initgroups()
+ * NO setuid()
+ *
+ * b) other: setgid(specified group)
+ * initgroups(name, specified group)
+ * setuid()
+ */
+
+#include "busybox.h"
+#include <syslog.h>
+#include <sys/un.h>
+
+//#define ENABLE_FEATURE_INETD_RPC 1
+//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO 1
+//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD 1
+//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME 1
+//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME 1
+//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 1
+//#define ENABLE_FEATURE_IPV6 1
+
+#if ENABLE_FEATURE_INETD_RPC
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#endif
+
+#define _PATH_INETDCONF "/etc/inetd.conf"
+#define _PATH_INETDPID "/var/run/inetd.pid"
+
+
+#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
+#define RETRYTIME (60*10) /* retry after bind or server fail */
+
+#ifndef RLIMIT_NOFILE
+#define RLIMIT_NOFILE RLIMIT_OFILE
+#endif
+
+#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 rlim_t rlim_ofile_cur = OPEN_MAX;
+static struct rlimit rlim_ofile;
+
+
+/* Check unsupporting builtin */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+# define INETD_FEATURE_ENABLED
+#endif
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+# define INETD_SETPROCTITLE
+#endif
+
+typedef struct servtab {
+ char *se_hostaddr; /* host address to listen on */
+ char *se_service; /* name of service */
+ int se_socktype; /* type of socket to use */
+ int se_family; /* address family */
+ char *se_proto; /* protocol used */
+#if ENABLE_FEATURE_INETD_RPC
+ int se_rpcprog; /* rpc program number */
+ int se_rpcversl; /* rpc program lowest version */
+ int se_rpcversh; /* rpc program highest version */
+#define isrpcservice(sep) ((sep)->se_rpcversl != 0)
+#else
+#define isrpcservice(sep) 0
+#endif
+ pid_t 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 */
+#ifdef INETD_FEATURE_ENABLED
+ const struct builtin *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;
+#if ENABLE_FEATURE_IPV6
+ struct sockaddr_in6 se_un_ctrladdr_in6;
+#endif
+ 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_in6 se_un.se_un_ctrladdr_in6
+#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_t;
+
+static servtab_t *servtab;
+
+#ifdef INETD_FEATURE_ENABLED
+struct builtin {
+ 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, servtab_t *);
+};
+
+ /* Echo received data */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+static void echo_stream(int, servtab_t *);
+static void echo_dg(int, servtab_t *);
+#endif
+ /* Internet /dev/null */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+static void discard_stream(int, servtab_t *);
+static void discard_dg(int, servtab_t *);
+#endif
+ /* Return 32 bit time since 1900 */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
+static void machtime_stream(int, servtab_t *);
+static void machtime_dg(int, servtab_t *);
+#endif
+ /* Return human-readable time */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+static void daytime_stream(int, servtab_t *);
+static void daytime_dg(int, servtab_t *);
+#endif
+ /* Familiar character generator */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+static void chargen_stream(int, servtab_t *);
+static void chargen_dg(int, servtab_t *);
+#endif
+
+static const struct builtin builtins[] = {
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+ /* Echo received data */
+ {"echo", SOCK_STREAM, 1, 0, echo_stream,},
+ {"echo", SOCK_DGRAM, 0, 0, echo_dg,},
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+ /* Internet /dev/null */
+ {"discard", SOCK_STREAM, 1, 0, discard_stream,},
+ {"discard", SOCK_DGRAM, 0, 0, discard_dg,},
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
+ /* Return 32 bit time since 1900 */
+ {"time", SOCK_STREAM, 0, 0, machtime_stream,},
+ {"time", SOCK_DGRAM, 0, 0, machtime_dg,},
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+ /* Return human-readable time */
+ {"daytime", SOCK_STREAM, 0, 0, daytime_stream,},
+ {"daytime", SOCK_DGRAM, 0, 0, daytime_dg,},
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_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_FEATURE_ENABLED */
+
+static int global_queuelen = 128;
+static int nsock, maxsock;
+static fd_set allsock;
+static int toomany;
+static int timingout;
+static struct servent *sp;
+static uid_t uid;
+
+static const char *CONFIG = _PATH_INETDCONF;
+
+static FILE *fconfig;
+static char line[1024];
+static char *defhost;
+
+/* xstrdup(NULL) returns NULL, but this one
+ * will return newly-allocated "" if called with NULL arg
+ * TODO: audit whether this makes any real difference
+ */
+static char *xxstrdup(char *cp)
+{
+ return xstrdup(cp ? cp : "");
+}
+
+static int setconfig(void)
+{
+ free(defhost);
+ defhost = xstrdup("*");
+ if (fconfig != NULL) {
+ fseek(fconfig, 0L, SEEK_SET);
+ return 1;
+ }
+ fconfig = fopen(CONFIG, "r");
+ return (fconfig != NULL);
+}
+
+static void endconfig(void)
+{
+ if (fconfig) {
+ (void) fclose(fconfig);
+ fconfig = NULL;
+ }
+ free(defhost);
+ defhost = 0;
+}
+
+#if ENABLE_FEATURE_INETD_RPC
+static void register_rpc(servtab_t *sep)
+{
+ int n;
+ struct sockaddr_in ir_sin;
+ struct protoent *pp;
+ socklen_t size;
+
+ if ((pp = getprotobyname(sep->se_proto + 4)) == NULL) {
+ bb_perror_msg("%s: getproto", sep->se_proto);
+ return;
+ }
+ size = sizeof ir_sin;
+ if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) {
+ bb_perror_msg("%s/%s: getsockname",
+ sep->se_service, sep->se_proto);
+ return;
+ }
+
+ for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
+ (void) pmap_unset(sep->se_rpcprog, n);
+ if (!pmap_set(sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port)))
+ bb_perror_msg("%s %s: pmap_set: %u %u %u %u",
+ sep->se_service, sep->se_proto,
+ sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port));
+ }
+}
+
+static void unregister_rpc(servtab_t *sep)
+{
+ int n;
+
+ for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
+ if (!pmap_unset(sep->se_rpcprog, n))
+ bb_error_msg("pmap_unset(%u, %u)", sep->se_rpcprog, n);
+ }
+}
+#endif /* FEATURE_INETD_RPC */
+
+static void freeconfig(servtab_t *cp)
+{
+ int i;
+
+ free(cp->se_hostaddr);
+ free(cp->se_service);
+ free(cp->se_proto);
+ free(cp->se_user);
+ free(cp->se_group);
+ free(cp->se_server);
+ for (i = 0; i < MAXARGV; i++)
+ free(cp->se_argv[i]);
+}
+
+static int bump_nofile(void)
+{
+#define FD_CHUNK 32
+
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
+ bb_perror_msg("getrlimit");
+ return -1;
+ }
+ rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
+ rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK);
+ if (rl.rlim_cur <= rlim_ofile_cur) {
+ bb_error_msg("bump_nofile: cannot extend file limit, max = %d",
+ (int) rl.rlim_cur);
+ return -1;
+ }
+
+ if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
+ bb_perror_msg("setrlimit");
+ return -1;
+ }
+
+ rlim_ofile_cur = rl.rlim_cur;
+ return 0;
+}
+
+static void setup(servtab_t *sep)
+{
+ int r;
+
+ sep->se_fd = socket(sep->se_family, sep->se_socktype, 0);
+ if (sep->se_fd < 0) {
+ bb_perror_msg("%s/%s: socket", sep->se_service, sep->se_proto);
+ return;
+ }
+ if (setsockopt_reuseaddr(sep->se_fd) < 0)
+ bb_perror_msg("setsockopt(SO_REUSEADDR)");
+
+#if ENABLE_FEATURE_INETD_RPC
+ if (isrpcservice(sep)) {
+ struct passwd *pwd;
+
+ /*
+ * for RPC services, attempt to use a reserved port
+ * if they are going to be running as root.
+ *
+ * Also, zero out the port for all RPC services; let bind()
+ * find one.
+ */
+ sep->se_ctrladdr_in.sin_port = 0;
+ if (sep->se_user && (pwd = getpwnam(sep->se_user)) &&
+ pwd->pw_uid == 0 && uid == 0)
+ r = bindresvport(sep->se_fd, &sep->se_ctrladdr_in);
+ else {
+ r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size);
+ if (r == 0) {
+ socklen_t len = sep->se_ctrladdr_size;
+ int saveerrno = errno;
+
+ /* update se_ctrladdr_in.sin_port */
+ r = getsockname(sep->se_fd, &sep->se_ctrladdr, &len);
+ if (r <= 0)
+ errno = saveerrno;
+ }
+ }
+ } else
+#endif
+ r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size);
+ if (r < 0) {
+ bb_perror_msg("%s/%s (%d): bind",
+ sep->se_service, sep->se_proto, sep->se_ctrladdr.sa_family);
+ 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 ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN)
+ bump_nofile();
+ }
+}
+
+static char *nextline(void)
+{
+ char *cp;
+ FILE *fd = fconfig;
+
+ if (fgets(line, sizeof(line), fd) == NULL)
+ return NULL;
+ cp = strchr(line, '\n');
+ if (cp)
+ *cp = '\0';
+ return line;
+}
+
+static char *skip(char **cpp) /* int report; */
+{
+ char *cp = *cpp;
+ char *start;
+
+/* erp: */
+ if (*cpp == NULL) {
+ /* if (report) */
+ /* bb_error_msg("syntax error in inetd config file"); */
+ return NULL;
+ }
+
+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()))
+ goto again;
+ *cpp = NULL;
+ /* goto erp; */
+ return NULL;
+ }
+ start = cp;
+ while (*cp && *cp != ' ' && *cp != '\t')
+ cp++;
+ if (*cp != '\0')
+ *cp++ = '\0';
+ /* if ((*cpp = cp) == NULL) */
+ /* goto erp; */
+
+ *cpp = cp;
+ return start;
+}
+
+static servtab_t *new_servtab(void)
+{
+ return xmalloc(sizeof(servtab_t));
+}
+
+static servtab_t *dupconfig(servtab_t *sep)
+{
+ servtab_t *newtab;
+ int argc;
+
+ newtab = new_servtab();
+ memset(newtab, 0, sizeof(servtab_t));
+ newtab->se_service = xstrdup(sep->se_service);
+ newtab->se_socktype = sep->se_socktype;
+ newtab->se_family = sep->se_family;
+ newtab->se_proto = xstrdup(sep->se_proto);
+#if ENABLE_FEATURE_INETD_RPC
+ newtab->se_rpcprog = sep->se_rpcprog;
+ newtab->se_rpcversl = sep->se_rpcversl;
+ newtab->se_rpcversh = sep->se_rpcversh;
+#endif
+ newtab->se_wait = sep->se_wait;
+ newtab->se_user = xstrdup(sep->se_user);
+ newtab->se_group = xstrdup(sep->se_group);
+#ifdef INETD_FEATURE_ENABLED
+ newtab->se_bi = sep->se_bi;
+#endif
+ newtab->se_server = xstrdup(sep->se_server);
+
+ for (argc = 0; argc <= MAXARGV; argc++)
+ newtab->se_argv[argc] = xstrdup(sep->se_argv[argc]);
+ newtab->se_max = sep->se_max;
+
+ return newtab;
+}
+
+static servtab_t *getconfigent(void)
+{
+ servtab_t *sep;
+ int argc;
+ char *cp, *arg;
+ char *hostdelim;
+ servtab_t *nsep;
+ servtab_t *psep;
+
+ sep = new_servtab();
+
+ /* memset(sep, 0, sizeof *sep); */
+ more:
+ /* freeconfig(sep); */
+
+ while ((cp = nextline()) && *cp == '#') /* skip comment line */;
+ if (cp == NULL) {
+ /* free(sep); */
+ return NULL;
+ }
+
+ memset((char *) sep, 0, sizeof *sep);
+ arg = skip(&cp);
+ if (arg == NULL) {
+ /* A blank line. */
+ goto more;
+ }
+
+ /* Check for a host name. */
+ hostdelim = strrchr(arg, ':');
+ if (hostdelim) {
+ *hostdelim = '\0';
+ sep->se_hostaddr = xstrdup(arg);
+ arg = hostdelim + 1;
+ /*
+ * If the line is of the form `host:', then just change the
+ * default host for the following lines.
+ */
+ if (*arg == '\0') {
+ arg = skip(&cp);
+ if (cp == NULL) {
+ free(defhost);
+ defhost = sep->se_hostaddr;
+ goto more;
+ }
+ }
+ } else
+ sep->se_hostaddr = xxstrdup(defhost);
+
+ sep->se_service = xxstrdup(arg);
+ arg = skip(&cp);
+
+ 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 = xxstrdup(skip(&cp));
+
+ if (strcmp(sep->se_proto, "unix") == 0) {
+ sep->se_family = AF_UNIX;
+ } else {
+ sep->se_family = AF_INET;
+ if (sep->se_proto[strlen(sep->se_proto) - 1] == '6')
+#if ENABLE_FEATURE_IPV6
+ sep->se_family = AF_INET6;
+#else
+ bb_error_msg("%s: IPV6 not supported", sep->se_proto);
+#endif
+ if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
+#if ENABLE_FEATURE_INETD_RPC
+ char *p, *ccp;
+ long l;
+
+ p = strchr(sep->se_service, '/');
+ if (p == 0) {
+ bb_error_msg("%s: no rpc version", sep->se_service);
+ goto more;
+ }
+ *p++ = '\0';
+ l = strtol(p, &ccp, 0);
+ if (ccp == p || l < 0 || l > INT_MAX) {
+ badafterall:
+ bb_error_msg("%s/%s: bad rpc version", sep->se_service, p);
+ goto more;
+ }
+ sep->se_rpcversl = sep->se_rpcversh = l;
+ if (*ccp == '-') {
+ p = ccp + 1;
+ l = strtol(p, &ccp, 0);
+ if (ccp == p || l < 0 || l > INT_MAX || l < sep->se_rpcversl || *ccp)
+ goto badafterall;
+ sep->se_rpcversh = l;
+ } else if (*ccp != '\0')
+ goto badafterall;
+#else
+ bb_error_msg("%s: rpc services not supported", sep->se_service);
+#endif
+ }
+ }
+ arg = skip(&cp);
+ if (arg == NULL)
+ goto more;
+
+ {
+ char *s = strchr(arg, '.');
+ if (s) {
+ *s++ = '\0';
+ sep->se_max = xatoi(s);
+ } else
+ sep->se_max = toomany;
+ }
+ sep->se_wait = strcmp(arg, "wait") == 0;
+ /* if ((arg = skip(&cp, 1)) == NULL) */
+ /* goto more; */
+ sep->se_user = xxstrdup(skip(&cp));
+ arg = strchr(sep->se_user, '.');
+ if (arg == NULL)
+ arg = strchr(sep->se_user, ':');
+ if (arg) {
+ *arg++ = '\0';
+ sep->se_group = xstrdup(arg);
+ }
+ /* if ((arg = skip(&cp, 1)) == NULL) */
+ /* goto more; */
+
+ sep->se_server = xxstrdup(skip(&cp));
+ if (strcmp(sep->se_server, "internal") == 0) {
+#ifdef INETD_FEATURE_ENABLED
+ const struct builtin *bi;
+
+ for (bi = builtins; 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) {
+ bb_error_msg("internal service %s unknown", sep->se_service);
+ goto more;
+ }
+ sep->se_bi = bi;
+ sep->se_wait = bi->bi_wait;
+#else
+ bb_perror_msg("internal service %s unknown", sep->se_service);
+ goto more;
+#endif
+ }
+#ifdef INETD_FEATURE_ENABLED
+ else
+ sep->se_bi = NULL;
+#endif
+ argc = 0;
+ for (arg = skip(&cp); cp; arg = skip(&cp)) {
+ if (argc < MAXARGV)
+ sep->se_argv[argc++] = xxstrdup(arg);
+ }
+ while (argc <= MAXARGV)
+ sep->se_argv[argc++] = NULL;
+
+ /*
+ * Now that we've processed the entire line, check if the hostname
+ * specifier was a comma separated list of hostnames. If so
+ * we'll make new entries for each address.
+ */
+ while ((hostdelim = strrchr(sep->se_hostaddr, ',')) != NULL) {
+ nsep = dupconfig(sep);
+
+ /*
+ * NULL terminate the hostname field of the existing entry,
+ * and make a dup for the new entry.
+ */
+ *hostdelim++ = '\0';
+ nsep->se_hostaddr = xstrdup(hostdelim);
+
+ nsep->se_next = sep->se_next;
+ sep->se_next = nsep;
+ }
+
+ nsep = sep;
+ while (nsep != NULL) {
+ nsep->se_checked = 1;
+ if (nsep->se_family == AF_INET) {
+ if (LONE_CHAR(nsep->se_hostaddr, '*'))
+ nsep->se_ctrladdr_in.sin_addr.s_addr = INADDR_ANY;
+ else if (!inet_aton(nsep->se_hostaddr, &nsep->se_ctrladdr_in.sin_addr)) {
+ struct hostent *hp;
+
+ hp = gethostbyname(nsep->se_hostaddr);
+ if (hp == 0) {
+ bb_error_msg("%s: unknown host", nsep->se_hostaddr);
+ nsep->se_checked = 0;
+ goto skip;
+ } else if (hp->h_addrtype != AF_INET) {
+ bb_error_msg("%s: address isn't an Internet "
+ "address", nsep->se_hostaddr);
+ nsep->se_checked = 0;
+ goto skip;
+ } else {
+ int i = 1;
+
+ memmove(&nsep->se_ctrladdr_in.sin_addr,
+ hp->h_addr_list[0], sizeof(struct in_addr));
+ while (hp->h_addr_list[i] != NULL) {
+ psep = dupconfig(nsep);
+ psep->se_hostaddr = xxstrdup(nsep->se_hostaddr);
+ psep->se_checked = 1;
+ memmove(&psep->se_ctrladdr_in.sin_addr,
+ hp->h_addr_list[i], sizeof(struct in_addr));
+ psep->se_ctrladdr_size = sizeof(psep->se_ctrladdr_in);
+ i++;
+ /* Prepend to list, don't want to look up */
+ /* its hostname again. */
+ psep->se_next = sep;
+ sep = psep;
+ }
+ }
+ }
+ }
+/* XXX BUG?: is this skip: label supposed to remain? */
+ skip:
+ nsep = nsep->se_next;
+ }
+
+ /*
+ * Finally, free any entries which failed the gethostbyname
+ * check.
+ */
+ psep = NULL;
+ nsep = sep;
+ while (nsep != NULL) {
+ servtab_t *tsep;
+
+ if (nsep->se_checked == 0) {
+ tsep = nsep;
+ if (psep == NULL) {
+ sep = nsep->se_next;
+ nsep = sep;
+ } else {
+ nsep = nsep->se_next;
+ psep->se_next = nsep;
+ }
+ freeconfig(tsep);
+ } else {
+ nsep->se_checked = 0;
+ psep = nsep;
+ nsep = nsep->se_next;
+ }
+ }
+
+ return sep;
+}
+
+#define Block_Using_Signals(m) do { \
+ sigemptyset(&m); \
+ sigaddset(&m, SIGCHLD); \
+ sigaddset(&m, SIGHUP); \
+ sigaddset(&m, SIGALRM); \
+ sigprocmask(SIG_BLOCK, &m, NULL); \
+} while (0)
+
+static servtab_t *enter(servtab_t *cp)
+{
+ servtab_t *sep;
+ sigset_t omask;
+
+ sep = new_servtab();
+ *sep = *cp;
+ sep->se_fd = -1;
+#if ENABLE_FEATURE_INETD_RPC
+ sep->se_rpcprog = -1;
+#endif
+ Block_Using_Signals(omask);
+ sep->se_next = servtab;
+ servtab = sep;
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+ return sep;
+}
+
+static int matchconf(servtab_t *old, servtab_t *new)
+{
+ if (strcmp(old->se_service, new->se_service) != 0)
+ return 0;
+
+ if (strcmp(old->se_hostaddr, new->se_hostaddr) != 0)
+ return 0;
+
+ if (strcmp(old->se_proto, new->se_proto) != 0)
+ return 0;
+
+ /*
+ * If the new servtab is bound to a specific address, check that the
+ * old servtab is bound to the same entry. If the new service is not
+ * bound to a specific address then the check of se_hostaddr above
+ * is sufficient.
+ */
+
+ if (old->se_family == AF_INET && new->se_family == AF_INET &&
+ memcmp(&old->se_ctrladdr_in.sin_addr,
+ &new->se_ctrladdr_in.sin_addr,
+ sizeof(new->se_ctrladdr_in.sin_addr)) != 0)
+ return 0;
+
+#if ENABLE_FEATURE_IPV6
+ if (old->se_family == AF_INET6 && new->se_family == AF_INET6 &&
+ memcmp(&old->se_ctrladdr_in6.sin6_addr,
+ &new->se_ctrladdr_in6.sin6_addr,
+ sizeof(new->se_ctrladdr_in6.sin6_addr)) != 0)
+ return 0;
+#endif
+ return 1;
+}
+
+static void config(int sig ATTRIBUTE_UNUSED)
+{
+ servtab_t *sep, *cp, **sepp;
+ sigset_t omask;
+ size_t n;
+ char protoname[10];
+
+ if (!setconfig()) {
+ bb_perror_msg("%s", CONFIG);
+ return;
+ }
+ for (sep = servtab; sep; sep = sep->se_next)
+ sep->se_checked = 0;
+ cp = getconfigent();
+ while (cp != NULL) {
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (matchconf(sep, cp))
+ break;
+
+ if (sep != 0) {
+ int i;
+
+#define SWAP(type, a, b) do {type c=(type)a; a=(type)b; b=(type)c;} while (0)
+
+ Block_Using_Signals(omask);
+ /*
+ * 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 (
+#ifdef INETD_FEATURE_ENABLED
+ cp->se_bi == 0 &&
+#endif
+ (sep->se_wait == 1 || cp->se_wait == 0))
+ sep->se_wait = cp->se_wait;
+ SWAP(int, cp->se_max, sep->se_max);
+ SWAP(char *, sep->se_user, cp->se_user);
+ SWAP(char *, sep->se_group, cp->se_group);
+ 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
+
+#if ENABLE_FEATURE_INETD_RPC
+ if (isrpcservice(sep))
+ unregister_rpc(sep);
+ sep->se_rpcversl = cp->se_rpcversl;
+ sep->se_rpcversh = cp->se_rpcversh;
+#endif
+ sigprocmask(SIG_UNBLOCK, &omask, 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;
+ safe_strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n + 1);
+ 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;
+ /* se_ctrladdr_in was set in getconfigent */
+ sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
+
+#if ENABLE_FEATURE_INETD_RPC
+ if (isrpcservice(sep)) {
+ struct rpcent *rp;
+ // FIXME: atoi_or_else(str, 0) would be handy here
+ sep->se_rpcprog = atoi(sep->se_service);
+ if (sep->se_rpcprog == 0) {
+ rp = getrpcbyname(sep->se_service);
+ if (rp == 0) {
+ bb_error_msg("%s: unknown rpc service", sep->se_service);
+ goto serv_unknown;
+ }
+ sep->se_rpcprog = rp->r_number;
+ }
+ if (sep->se_fd == -1)
+ setup(sep);
+ if (sep->se_fd != -1)
+ register_rpc(sep);
+ } else
+#endif
+ {
+ uint16_t port = htons(atoi(sep->se_service));
+ // FIXME: atoi_or_else(str, 0) would be handy here
+ if (!port) {
+ /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname));
+ if (isdigit(protoname[strlen(protoname) - 1]))
+ protoname[strlen(protoname) - 1] = '\0';
+ sp = getservbyname(sep->se_service, protoname);
+ if (sp == 0) {
+ bb_error_msg("%s/%s: unknown service",
+ sep->se_service, sep->se_proto);
+ goto serv_unknown;
+ }
+ 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);
+ }
+ break;
+#if ENABLE_FEATURE_IPV6
+ case AF_INET6:
+ sep->se_ctrladdr_in6.sin6_family = AF_INET6;
+ /* se_ctrladdr_in was set in getconfigent */
+ sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in6;
+
+#if ENABLE_FEATURE_INETD_RPC
+ if (isrpcservice(sep)) {
+ struct rpcent *rp;
+
+ sep->se_rpcprog = atoi(sep->se_service);
+ if (sep->se_rpcprog == 0) {
+ rp = getrpcbyname(sep->se_service);
+ if (rp == 0) {
+ bb_error_msg("%s: unknown rpc service", sep->se_service);
+ goto serv_unknown;
+ }
+ sep->se_rpcprog = rp->r_number;
+ }
+ if (sep->se_fd == -1)
+ setup(sep);
+ if (sep->se_fd != -1)
+ register_rpc(sep);
+ } else
+#endif
+ {
+ uint16_t port = htons(atoi(sep->se_service));
+
+ if (!port) {
+ /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname));
+ if (isdigit(protoname[strlen(protoname) - 1]))
+ protoname[strlen(protoname) - 1] = '\0';
+ sp = getservbyname(sep->se_service, protoname);
+ if (sp == 0) {
+ bb_error_msg("%s/%s: unknown service",
+ sep->se_service, sep->se_proto);
+ goto serv_unknown;
+ }
+ port = sp->s_port;
+ }
+ if (port != sep->se_ctrladdr_in6.sin6_port) {
+ sep->se_ctrladdr_in6.sin6_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);
+ }
+ break;
+#endif /* FEATURE_IPV6 */
+ }
+ serv_unknown:
+ if (cp->se_next != NULL) {
+ servtab_t *tmp = cp;
+
+ cp = cp->se_next;
+ free(tmp);
+ } else {
+ free(cp);
+ cp = getconfigent();
+ }
+ }
+ endconfig();
+ /*
+ * Purge anything not looked at above.
+ */
+ Block_Using_Signals(omask);
+ sepp = &servtab;
+ while ((sep = *sepp)) {
+ 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 ENABLE_FEATURE_INETD_RPC
+ if (isrpcservice(sep))
+ unregister_rpc(sep);
+#endif
+ if (sep->se_family == AF_UNIX)
+ (void) unlink(sep->se_service);
+ freeconfig(sep);
+ free(sep);
+ }
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+}
+
+
+static void reapchild(int sig ATTRIBUTE_UNUSED)
+{
+ pid_t pid;
+ int save_errno = errno, status;
+ servtab_t *sep;
+
+ for (;;) {
+ pid = wait3(&status, WNOHANG, NULL);
+ if (pid <= 0)
+ break;
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (sep->se_wait == pid) {
+ if (WIFEXITED(status) && WEXITSTATUS(status))
+ bb_error_msg("%s: exit status 0x%x",
+ sep->se_server, WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ bb_error_msg("%s: exit signal 0x%x",
+ sep->se_server, WTERMSIG(status));
+ sep->se_wait = 1;
+ FD_SET(sep->se_fd, &allsock);
+ nsock++;
+ }
+ }
+ errno = save_errno;
+}
+
+static void retry(int sig ATTRIBUTE_UNUSED)
+{
+ servtab_t *sep;
+
+ 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:
+#if ENABLE_FEATURE_IPV6
+ case AF_INET6:
+#endif
+ setup(sep);
+#if ENABLE_FEATURE_INETD_RPC
+ if (sep->se_fd != -1 && isrpcservice(sep))
+ register_rpc(sep);
+#endif
+ break;
+ }
+ }
+ }
+}
+
+static void goaway(int sig ATTRIBUTE_UNUSED)
+{
+ servtab_t *sep;
+
+ /* XXX signal race walking sep list */
+ for (sep = servtab; sep; sep = sep->se_next) {
+ if (sep->se_fd == -1)
+ continue;
+
+ switch (sep->se_family) {
+ case AF_UNIX:
+ (void) unlink(sep->se_service);
+ break;
+ case AF_INET:
+#if ENABLE_FEATURE_IPV6
+ case AF_INET6:
+#endif
+#if ENABLE_FEATURE_INETD_RPC
+ if (sep->se_wait == 1 && isrpcservice(sep))
+ unregister_rpc(sep); /* XXX signal race */
+#endif
+ break;
+ }
+ (void) close(sep->se_fd);
+ }
+ (void) unlink(_PATH_INETDPID);
+ exit(0);
+}
+
+
+#ifdef INETD_SETPROCTITLE
+static char **Argv;
+static char *LastArg;
+
+static void
+inetd_setproctitle(char *a, int s)
+{
+ socklen_t size;
+ char *cp;
+ struct sockaddr_in prt_sin;
+ char buf[80];
+
+ cp = Argv[0];
+ size = sizeof(prt_sin);
+ (void) snprintf(buf, sizeof buf, "-%s", a);
+ if (getpeername(s, (struct sockaddr *) &prt_sin, &size) == 0) {
+ char *sa = inet_ntoa(prt_sin.sin_addr);
+
+ buf[sizeof(buf) - 1 - strlen(sa) - 3] = '\0';
+ strcat(buf, " [");
+ strcat(buf, sa);
+ strcat(buf, "]");
+ }
+ strncpy(cp, buf, LastArg - cp);
+ cp += strlen(cp);
+ while (cp < LastArg)
+ *cp++ = ' ';
+}
+#endif
+
+
+int inetd_main(int argc, char *argv[]);
+int inetd_main(int argc, char *argv[])
+{
+ servtab_t *sep;
+ struct passwd *pwd;
+ struct group *grp = NULL;
+ int tmpint;
+ struct sigaction sa, sapipe;
+ int opt;
+ pid_t pid;
+ char buf[50];
+ char *stoomany;
+ sigset_t omask, wait_mask;
+
+#ifdef INETD_SETPROCTITLE
+ extern char **environ;
+ char **envp = environ;
+
+ Argv = argv;
+ if (envp == 0 || *envp == 0)
+ envp = argv;
+ while (*envp)
+ envp++;
+ LastArg = envp[-1] + strlen(envp[-1]);
+#endif
+
+ opt = getopt32(argc, argv, "R:f", &stoomany);
+ if(opt & 1) {
+ toomany = xatoi_u(stoomany);
+ }
+ argc -= optind;
+ argv += optind;
+
+ uid = getuid();
+ if (uid != 0)
+ CONFIG = NULL;
+ if (argc > 0)
+ CONFIG = argv[0];
+ if (CONFIG == NULL)
+ bb_error_msg_and_die("non-root must specify a config file");
+
+#ifdef BB_NOMMU
+ if (!(opt & 2)) {
+ /* reexec for vfork() do continue parent */
+ vfork_daemon_rexec(0, 0, argc, argv, "-f");
+ }
+ bb_sanitize_stdio();
+#else
+ bb_sanitize_stdio_maybe_daemonize(!(opt & 2));
+#endif
+ openlog(applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+
+ if (uid == 0) {
+ /* If run by hand, ensure groups vector gets trashed */
+ gid_t gid = getgid();
+ setgroups(1, &gid);
+ }
+
+ {
+ FILE *fp = fopen(_PATH_INETDPID, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%u\n", getpid());
+ fclose(fp);
+ }
+ }
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
+ bb_perror_msg("getrlimit");
+ } else {
+ rlim_ofile_cur = rlim_ofile.rlim_cur;
+ if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
+ rlim_ofile_cur = OPEN_MAX;
+ }
+
+ memset((char *) &sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sa.sa_handler = retry;
+ sigaction(SIGALRM, &sa, NULL);
+ config(SIGHUP);
+ 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, &sapipe);
+ memset(&wait_mask, 0, sizeof(wait_mask));
+ {
+ /* 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 = -1;
+ fd_set readable;
+
+ if (nsock == 0) {
+ Block_Using_Signals(omask);
+ while (nsock == 0)
+ sigsuspend(&wait_mask);
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+ }
+
+ readable = allsock;
+ n = select(maxsock + 1, &readable, NULL, NULL, NULL);
+ if (n <= 0) {
+ if (n < 0 && errno != EINTR) {
+ bb_perror_msg("select");
+ sleep(1);
+ }
+ continue;
+ }
+
+ for (sep = servtab; n && sep; sep = sep->se_next) {
+ if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable))
+ continue;
+
+ n--;
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
+ ctrl = accept(sep->se_fd, NULL, NULL);
+ if (ctrl < 0) {
+ if (errno == EINTR)
+ continue;
+ bb_perror_msg("accept (for %s)", sep->se_service);
+ continue;
+ }
+ if (sep->se_family == AF_INET && sep->se_socktype == SOCK_STREAM) {
+ struct sockaddr_in peer;
+ socklen_t plen = sizeof(peer);
+
+ if (getpeername(ctrl, (struct sockaddr *) &peer, &plen) < 0) {
+ bb_error_msg("cannot getpeername");
+ close(ctrl);
+ continue;
+ }
+ if (ntohs(peer.sin_port) == 20) {
+ /* XXX ftp bounce */
+ close(ctrl);
+ continue;
+ }
+ }
+ } else
+ ctrl = sep->se_fd;
+
+ Block_Using_Signals(omask);
+ pid = 0;
+#ifdef INETD_FEATURE_ENABLED
+ if (sep->se_bi == 0 || sep->se_bi->bi_fork)
+#endif
+ {
+ if (sep->se_count++ == 0)
+ (void) gettimeofday(&sep->se_time, NULL);
+ else if (toomany > 0 && sep->se_count >= sep->se_max) {
+ struct timeval now;
+
+ (void) gettimeofday(&now, NULL);
+ if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) {
+ sep->se_time = now;
+ sep->se_count = 1;
+ } else {
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ if (sep->se_family == AF_INET &&
+ ntohs(sep->se_ctrladdr_in.sin_port) >= IPPORT_RESERVED) {
+ /*
+ * Cannot close it -- there are
+ * thieves on the system.
+ * Simply ignore the connection.
+ */
+ --sep->se_count;
+ continue;
+ }
+ bb_error_msg("%s/%s server failing (looping), service terminated",
+ sep->se_service, sep->se_proto);
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ FD_CLR(sep->se_fd, &allsock);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ sep->se_count = 0;
+ nsock--;
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+ if (!timingout) {
+ timingout = 1;
+ alarm(RETRYTIME);
+ }
+ continue;
+ }
+ }
+ pid = fork();
+ }
+ if (pid < 0) {
+ bb_perror_msg("fork");
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+ sleep(1);
+ continue;
+ }
+ if (pid && sep->se_wait) {
+ sep->se_wait = pid;
+ FD_CLR(sep->se_fd, &allsock);
+ nsock--;
+ }
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+ if (pid == 0) {
+#ifdef INETD_FEATURE_ENABLED
+ if (sep->se_bi) {
+ (*sep->se_bi->bi_fn)(ctrl, sep);
+ } else
+#endif
+ {
+ pwd = getpwnam(sep->se_user);
+ if (pwd == NULL) {
+ bb_error_msg("getpwnam: %s: no such user", sep->se_user);
+ goto do_exit1;
+ }
+ if (setsid() < 0)
+ bb_perror_msg("%s: setsid", sep->se_service);
+ if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {
+ bb_error_msg("getgrnam: %s: no such group", sep->se_group);
+ goto do_exit1;
+ }
+ if (uid != 0) {
+ /* a user running private inetd */
+ if (uid != pwd->pw_uid)
+ _exit(1);
+ } else if (pwd->pw_uid) {
+ if (sep->se_group)
+ pwd->pw_gid = grp->gr_gid;
+ xsetgid((gid_t) pwd->pw_gid);
+ initgroups(pwd->pw_name, pwd->pw_gid);
+ xsetuid((uid_t) pwd->pw_uid);
+ } else if (sep->se_group) {
+ xsetgid(grp->gr_gid);
+ setgroups(1, &grp->gr_gid);
+ }
+ dup2(ctrl, 0);
+ if (ctrl) close(ctrl);
+ dup2(0, 1);
+ dup2(0, 2);
+ if (rlim_ofile.rlim_cur != rlim_ofile_cur)
+ if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
+ bb_perror_msg("setrlimit");
+ closelog();
+ for (tmpint = rlim_ofile_cur - 1; --tmpint > 2;)
+ (void) close(tmpint);
+ sigaction(SIGPIPE, &sapipe, NULL);
+ execv(sep->se_server, sep->se_argv);
+ bb_perror_msg("execv %s", sep->se_server);
+do_exit1:
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof(buf), 0);
+ _exit(1);
+ }
+ }
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ } /* for (sep = servtab...) */
+ } /* for (;;) */
+}
+
+/*
+ * Internet services provided internally by inetd:
+ */
+#define BUFSIZE 4096
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+static int dg_badinput(struct sockaddr_in *dg_sin)
+{
+ if (ntohs(dg_sin->sin_port) < IPPORT_RESERVED)
+ return 1;
+ if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST))
+ return 1;
+ /* XXX compare against broadcast addresses in SIOCGIFCONF list? */
+ return 0;
+}
+#endif
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+/* Echo service -- echo data back */
+/* ARGSUSED */
+static void
+echo_stream(int s, servtab_t *sep)
+{
+ char buffer[BUFSIZE];
+ int i;
+
+ inetd_setproctitle(sep->se_service, s);
+ while (1) {
+ i = read(s, buffer, sizeof(buffer));
+ if (i <= 0) break;
+ /* FIXME: this isnt correct - safe_write()? */
+ if (write(s, buffer, i) <= 0) break;
+ }
+ exit(0);
+}
+
+/* Echo service -- echo data back */
+/* ARGSUSED */
+static void
+echo_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ char buffer[BUFSIZE];
+ int i;
+ socklen_t size;
+ /* struct sockaddr_storage ss; */
+ struct sockaddr sa;
+
+ size = sizeof(sa);
+ i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size);
+ if (i < 0)
+ return;
+ if (dg_badinput((struct sockaddr_in *) &sa))
+ return;
+ (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_ECHO */
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+/* Discard service -- ignore data */
+/* ARGSUSED */
+static void
+discard_stream(int s, servtab_t *sep)
+{
+ char buffer[BUFSIZE];
+
+ inetd_setproctitle(sep->se_service, s);
+ while (1) {
+ errno = 0;
+ if (read(s, buffer, sizeof(buffer)) <= 0 && errno != EINTR)
+ exit(0);
+ }
+}
+
+/* Discard service -- ignore data */
+/* ARGSUSED */
+static void
+discard_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ char buffer[BUFSIZE];
+
+ (void) read(s, buffer, sizeof(buffer));
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DISCARD */
+
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+#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 */
+/* ARGSUSED */
+static void
+chargen_stream(int s, servtab_t *sep)
+{
+ char *rs;
+ int len;
+ char text[LINESIZ + 2];
+
+ inetd_setproctitle(sep->se_service, s);
+
+ if (!endring) {
+ initring();
+ rs = ring;
+ }
+
+ text[LINESIZ] = '\r';
+ text[LINESIZ + 1] = '\n';
+ rs = ring;
+ for (;;) {
+ len = endring - rs;
+ if (len >= LINESIZ)
+ memmove(text, rs, LINESIZ);
+ else {
+ memmove(text, rs, len);
+ memmove(text + len, ring, LINESIZ - len);
+ }
+ if (++rs == endring)
+ rs = ring;
+ if (write(s, text, sizeof(text)) != sizeof(text))
+ break;
+ }
+ exit(0);
+}
+
+/* Character generator */
+/* ARGSUSED */
+static void
+chargen_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ /* struct sockaddr_storage ss; */
+ struct sockaddr sa;
+ static char *rs;
+ int len;
+ char text[LINESIZ + 2];
+ socklen_t size;
+
+ if (endring == 0) {
+ initring();
+ rs = ring;
+ }
+
+ size = sizeof(sa);
+ if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
+ return;
+ if (dg_badinput((struct sockaddr_in *) &sa))
+ return;
+
+ if ((len = endring - rs) >= LINESIZ)
+ memmove(text, rs, LINESIZ);
+ else {
+ memmove(text, rs, len);
+ memmove(text + len, ring, 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 /* FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN */
+
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_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 unsigned machtime(void)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ fprintf(stderr, "Unable to get time of day\n");
+ return 0L;
+ }
+ return htonl((unsigned) tv.tv_sec + 2208988800UL);
+}
+
+/* ARGSUSED */
+static void
+machtime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ unsigned result;
+
+ result = machtime();
+ (void) write(s, (char *) &result, sizeof(result));
+}
+
+/* ARGSUSED */
+static void
+machtime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ unsigned result;
+ /* struct sockaddr_storage ss; */
+ struct sockaddr sa;
+ struct sockaddr_in *dg_sin;
+ socklen_t size;
+
+ size = sizeof(sa);
+ if (recvfrom(s, (char *) &result, sizeof(result), 0, &sa, &size) < 0)
+ return;
+ /* if (dg_badinput((struct sockaddr *)&ss)) */
+ dg_sin = (struct sockaddr_in *) &sa;
+ if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST) ||
+ ntohs(dg_sin->sin_port) < IPPORT_RESERVED / 2)
+ return;
+ result = machtime();
+ (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_TIME */
+
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+/* Return human-readable time of day */
+/* ARGSUSED */
+static void daytime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ char buffer[32];
+ time_t t;
+
+ t = time(NULL);
+
+// fdprintf instead?
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&t));
+ (void) write(s, buffer, strlen(buffer));
+}
+
+/* Return human-readable time of day */
+/* ARGSUSED */
+void
+daytime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ char buffer[256];
+ time_t t;
+ /* struct sockaddr_storage ss; */
+ struct sockaddr sa;
+ socklen_t size;
+
+ t = time(NULL);
+
+ size = sizeof(sa);
+ if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
+ return;
+ if (dg_badinput((struct sockaddr_in *) &sa))
+ return;
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&t));
+ (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME */
diff --git a/i/pc104/initrd/conf/busybox/networking/interface.c b/i/pc104/initrd/conf/busybox/networking/interface.c
new file mode 100644
index 0000000..b1b935e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/interface.c
@@ -0,0 +1,1195 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stolen from net-tools-1.59 and stripped down for busybox by
+ * Erik Andersen <andersen@codepoet.org>
+ *
+ * Heavily modified by Manuel Novoa III Mar 12, 2001
+ *
+ * Added print_bytes_scaled function to reduce code size.
+ * Added some (potentially) missing defines.
+ * Improved display support for -a and for a named interface.
+ *
+ * -----------------------------------------------------------
+ *
+ * ifconfig This file contains an implementation of the command
+ * that either displays or sets the characteristics of
+ * one or more of the system's networking interfaces.
+ *
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ * and others. Copyright 1993 MicroWalt Corporation
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Patched to support 'add' and 'del' keywords for INET(4) addresses
+ * by Mrs. Brisby <mrs.brisby@nimh.org>
+ *
+ * {1.34} - 19980630 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - gettext instead of catgets for i18n
+ * 10/1998 - Andi Kleen. Use interface list primitives.
+ * 20001008 - Bernd Eckenfels, Patch from RH for setting mtu
+ * (default AF was wrong)
+ */
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include "inet_common.h"
+#include "busybox.h"
+
+#if ENABLE_FEATURE_IPV6
+# define HAVE_AFINET6 1
+#else
+# undef HAVE_AFINET6
+#endif
+
+#define _PATH_PROCNET_DEV "/proc/net/dev"
+#define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6"
+
+#ifdef HAVE_AFINET6
+
+#ifndef _LINUX_IN6_H
+/*
+ * This is in linux/include/net/ipv6.h.
+ */
+
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ uint32_t ifr6_prefixlen;
+ unsigned int ifr6_ifindex;
+};
+
+#endif
+
+#endif /* HAVE_AFINET6 */
+
+/* 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 HAVE_TXQUEUELEN
+#define HAVE_TXQUEUELEN 1
+#endif
+
+#ifndef IFF_DYNAMIC
+#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */
+#endif
+
+/* Display an Internet socket address. */
+static const char *INET_sprint(struct sockaddr *sap, int numeric)
+{
+ static char buff[128];
+
+ if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+ return "[NONE SET]";
+
+ if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap,
+ numeric, 0xffffff00) != 0)
+ return NULL;
+
+ return buff;
+}
+
+#ifdef UNUSED_AND_BUGGY
+static int INET_getsock(char *bufp, struct sockaddr *sap)
+{
+ char *sp = bufp, *bp;
+ unsigned int i;
+ unsigned val;
+ struct sockaddr_in *sock_in;
+
+ sock_in = (struct sockaddr_in *) sap;
+ sock_in->sin_family = AF_INET;
+ sock_in->sin_port = 0;
+
+ val = 0;
+ bp = (char *) &val;
+ for (i = 0; i < sizeof(sock_in->sin_addr.s_addr); i++) {
+ *sp = toupper(*sp);
+
+ if ((unsigned)(*sp - 'A') <= 5)
+ bp[i] |= (int) (*sp - ('A' - 10));
+ else if (isdigit(*sp))
+ bp[i] |= (int) (*sp - '0');
+ else
+ return -1;
+
+ bp[i] <<= 4;
+ sp++;
+ *sp = toupper(*sp);
+
+ if ((unsigned)(*sp - 'A') <= 5)
+ bp[i] |= (int) (*sp - ('A' - 10));
+ else if (isdigit(*sp))
+ bp[i] |= (int) (*sp - '0');
+ else
+ return -1;
+
+ sp++;
+ }
+ sock_in->sin_addr.s_addr = htonl(val);
+
+ return (sp - bufp);
+}
+#endif
+
+static int INET_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
+{
+ return INET_resolve(bufp, (struct sockaddr_in *) sap, 0);
+/*
+ switch (type) {
+ case 1:
+ return (INET_getsock(bufp, sap));
+ case 256:
+ return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1));
+ default:
+ return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0));
+ }
+*/
+}
+
+static const struct aftype inet_aftype = {
+ .name = "inet",
+ .title = "DARPA Internet",
+ .af = AF_INET,
+ .alen = 4,
+ .sprint = INET_sprint,
+ .input = INET_input,
+};
+
+#ifdef HAVE_AFINET6
+
+/* Display an Internet socket address. */
+/* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */
+static const char *INET6_sprint(struct sockaddr *sap, int numeric)
+{
+ static char buff[128];
+
+ if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+ return "[NONE SET]";
+ if (INET6_rresolve(buff, sizeof(buff), (struct sockaddr_in6 *) sap, numeric))
+ return "[UNKNOWN]";
+ return buff;
+}
+
+#ifdef UNUSED
+static int INET6_getsock(char *bufp, struct sockaddr *sap)
+{
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *) sap;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+
+ if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0)
+ return -1;
+
+ return 16; /* ?;) */
+}
+#endif
+
+static int INET6_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
+{
+ return INET6_resolve(bufp, (struct sockaddr_in6 *) sap);
+/*
+ switch (type) {
+ case 1:
+ return (INET6_getsock(bufp, sap));
+ default:
+ return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap));
+ }
+*/
+}
+
+static const struct aftype inet6_aftype = {
+ .name = "inet6",
+ .title = "IPv6",
+ .af = AF_INET6,
+ .alen = sizeof(struct in6_addr),
+ .sprint = INET6_sprint,
+ .input = INET6_input,
+};
+
+#endif /* HAVE_AFINET6 */
+
+/* Display an UNSPEC address. */
+static char *UNSPEC_print(unsigned char *ptr)
+{
+ static char buff[sizeof(struct sockaddr) * 3 + 1];
+ char *pos;
+ unsigned int i;
+
+ pos = buff;
+ for (i = 0; i < sizeof(struct sockaddr); i++) {
+ /* careful -- not every libc's sprintf returns # bytes written */
+ sprintf(pos, "%02X-", (*ptr++ & 0377));
+ pos += 3;
+ }
+ /* Erase trailing "-". Works as long as sizeof(struct sockaddr) != 0 */
+ *--pos = '\0';
+ return buff;
+}
+
+/* Display an UNSPEC socket address. */
+static const char *UNSPEC_sprint(struct sockaddr *sap, int numeric)
+{
+ if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+ return "[NONE SET]";
+ return UNSPEC_print((unsigned char *)sap->sa_data);
+}
+
+static const struct aftype unspec_aftype = {
+ .name = "unspec",
+ .title = "UNSPEC",
+ .af = AF_UNSPEC,
+ .alen = 0,
+ .print = UNSPEC_print,
+ .sprint = UNSPEC_sprint,
+};
+
+static const struct aftype *const aftypes[] = {
+ &inet_aftype,
+#ifdef HAVE_AFINET6
+ &inet6_aftype,
+#endif
+ &unspec_aftype,
+ NULL
+};
+
+/* Check our protocol family table for this family. */
+const struct aftype *get_aftype(const char *name)
+{
+ const struct aftype *const *afp;
+
+ afp = aftypes;
+ while (*afp != NULL) {
+ if (!strcmp((*afp)->name, name))
+ return (*afp);
+ afp++;
+ }
+ return NULL;
+}
+
+/* Check our protocol family table for this family. */
+static const struct aftype *get_afntype(int af)
+{
+ const struct aftype *const *afp;
+
+ afp = aftypes;
+ while (*afp != NULL) {
+ if ((*afp)->af == af)
+ return *afp;
+ afp++;
+ }
+ return NULL;
+}
+
+struct user_net_device_stats {
+ unsigned long long rx_packets; /* total packets received */
+ unsigned long long tx_packets; /* total packets transmitted */
+ unsigned long long rx_bytes; /* total bytes received */
+ unsigned long long tx_bytes; /* total bytes transmitted */
+ unsigned long rx_errors; /* bad packets received */
+ unsigned long tx_errors; /* packet transmit problems */
+ unsigned long rx_dropped; /* no space in linux buffers */
+ unsigned long tx_dropped; /* no space available in linux */
+ unsigned long rx_multicast; /* multicast packets received */
+ unsigned long rx_compressed;
+ unsigned long tx_compressed;
+ unsigned long collisions;
+
+ /* detailed rx_errors: */
+ unsigned long rx_length_errors;
+ unsigned long rx_over_errors; /* receiver ring buff overflow */
+ unsigned long rx_crc_errors; /* recved pkt with crc error */
+ unsigned long rx_frame_errors; /* recv'd frame alignment error */
+ unsigned long rx_fifo_errors; /* recv'r fifo overrun */
+ unsigned long rx_missed_errors; /* receiver missed packet */
+ /* detailed tx_errors */
+ unsigned long tx_aborted_errors;
+ unsigned long tx_carrier_errors;
+ unsigned long tx_fifo_errors;
+ unsigned long tx_heartbeat_errors;
+ unsigned long tx_window_errors;
+};
+
+struct interface {
+ struct interface *next, *prev;
+ char name[IFNAMSIZ]; /* interface name */
+ short type; /* if type */
+ short flags; /* various flags */
+ int metric; /* routing metric */
+ int mtu; /* MTU value */
+ int tx_queue_len; /* transmit queue length */
+ struct ifmap map; /* hardware setup */
+ struct sockaddr addr; /* IP address */
+ struct sockaddr dstaddr; /* P-P IP address */
+ struct sockaddr broadaddr; /* IP broadcast address */
+ struct sockaddr netmask; /* IP network mask */
+ int has_ip;
+ char hwaddr[32]; /* HW address */
+ int statistics_valid;
+ struct user_net_device_stats stats; /* statistics */
+ int keepalive; /* keepalive value for SLIP */
+ int outfill; /* outfill value for SLIP */
+};
+
+
+int interface_opt_a; /* show all interfaces */
+
+static struct interface *int_list, *int_last;
+
+
+#if 0
+/* like strcmp(), but knows about numbers */
+except that the freshly added calls to xatoul() brf on ethernet aliases with
+uClibc with e.g.: ife->name='lo' name='eth0:1'
+static int nstrcmp(const char *a, const char *b)
+{
+ const char *a_ptr = a;
+ const char *b_ptr = b;
+
+ while (*a == *b) {
+ if (*a == '\0') {
+ return 0;
+ }
+ if (!isdigit(*a) && isdigit(*(a+1))) {
+ a_ptr = a+1;
+ b_ptr = b+1;
+ }
+ a++;
+ b++;
+ }
+
+ if (isdigit(*a) && isdigit(*b)) {
+ return xatoul(a_ptr) > xatoul(b_ptr) ? 1 : -1;
+ }
+ return *a - *b;
+}
+#endif
+
+static struct interface *add_interface(char *name)
+{
+ struct interface *ife, **nextp, *new;
+
+ for (ife = int_last; ife; ife = ife->prev) {
+ int n = /*n*/strcmp(ife->name, name);
+
+ if (n == 0)
+ return ife;
+ if (n < 0)
+ break;
+ }
+
+ new = xzalloc(sizeof(*new));
+ safe_strncpy(new->name, name, IFNAMSIZ);
+ nextp = ife ? &ife->next : &int_list;
+ new->prev = ife;
+ new->next = *nextp;
+ if (new->next)
+ new->next->prev = new;
+ else
+ int_last = new;
+ *nextp = new;
+ return new;
+}
+
+static char *get_name(char *name, char *p)
+{
+ /* Extract <name> from nul-terminated p where p matches
+ <name>: after leading whitespace.
+ If match is not made, set name empty and return unchanged p */
+ int namestart=0, nameend=0;
+ while (isspace(p[namestart]))
+ namestart++;
+ nameend=namestart;
+ while (p[nameend] && p[nameend]!=':' && !isspace(p[nameend]))
+ nameend++;
+ if (p[nameend]==':') {
+ if ((nameend-namestart)<IFNAMSIZ) {
+ memcpy(name,&p[namestart],nameend-namestart);
+ name[nameend-namestart]='\0';
+ p=&p[nameend];
+ } else {
+ /* Interface name too large */
+ name[0]='\0';
+ }
+ } else {
+ /* trailing ':' not found - return empty */
+ name[0]='\0';
+ }
+ return p + 1;
+}
+
+/* If scanf supports size qualifiers for %n conversions, then we can
+ * use a modified fmt that simply stores the position in the fields
+ * having no associated fields in the proc string. Of course, we need
+ * to zero them again when we're done. But that is smaller than the
+ * old approach of multiple scanf occurrences with large numbers of
+ * args. */
+
+/* static const char *const ss_fmt[] = { */
+/* "%lln%llu%lu%lu%lu%lu%ln%ln%lln%llu%lu%lu%lu%lu%lu", */
+/* "%llu%llu%lu%lu%lu%lu%ln%ln%llu%llu%lu%lu%lu%lu%lu", */
+/* "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" */
+/* }; */
+
+ /* Lie about the size of the int pointed to for %n. */
+#if INT_MAX == LONG_MAX
+static const char *const ss_fmt[] = {
+ "%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u",
+ "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u",
+ "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u"
+};
+#else
+static const char *const ss_fmt[] = {
+ "%n%llu%lu%lu%lu%lu%n%n%n%llu%lu%lu%lu%lu%lu",
+ "%llu%llu%lu%lu%lu%lu%n%n%llu%llu%lu%lu%lu%lu%lu",
+ "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu"
+};
+
+#endif
+
+static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn)
+{
+ memset(&ife->stats, 0, sizeof(struct user_net_device_stats));
+
+ sscanf(bp, ss_fmt[procnetdev_vsn],
+ &ife->stats.rx_bytes, /* missing for 0 */
+ &ife->stats.rx_packets,
+ &ife->stats.rx_errors,
+ &ife->stats.rx_dropped,
+ &ife->stats.rx_fifo_errors,
+ &ife->stats.rx_frame_errors,
+ &ife->stats.rx_compressed, /* missing for <= 1 */
+ &ife->stats.rx_multicast, /* missing for <= 1 */
+ &ife->stats.tx_bytes, /* missing for 0 */
+ &ife->stats.tx_packets,
+ &ife->stats.tx_errors,
+ &ife->stats.tx_dropped,
+ &ife->stats.tx_fifo_errors,
+ &ife->stats.collisions,
+ &ife->stats.tx_carrier_errors,
+ &ife->stats.tx_compressed /* missing for <= 1 */
+ );
+
+ if (procnetdev_vsn <= 1) {
+ if (procnetdev_vsn == 0) {
+ ife->stats.rx_bytes = 0;
+ ife->stats.tx_bytes = 0;
+ }
+ ife->stats.rx_multicast = 0;
+ ife->stats.rx_compressed = 0;
+ ife->stats.tx_compressed = 0;
+ }
+}
+
+static inline int procnetdev_version(char *buf)
+{
+ if (strstr(buf, "compressed"))
+ return 2;
+ if (strstr(buf, "bytes"))
+ return 1;
+ return 0;
+}
+
+static int if_readconf(void)
+{
+ int numreqs = 30;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ int n, err = -1;
+ int skfd;
+
+ ifc.ifc_buf = NULL;
+
+ /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets
+ (as of 2.1.128) */
+ skfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (skfd < 0) {
+ bb_perror_msg("error: no inet socket available");
+ return -1;
+ }
+
+ for (;;) {
+ ifc.ifc_len = sizeof(struct ifreq) * numreqs;
+ ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len);
+
+ if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) {
+ perror("SIOCGIFCONF");
+ goto out;
+ }
+ if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) {
+ /* assume it overflowed and try again */
+ numreqs += 10;
+ continue;
+ }
+ break;
+ }
+
+ ifr = ifc.ifc_req;
+ for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
+ add_interface(ifr->ifr_name);
+ ifr++;
+ }
+ err = 0;
+
+ out:
+ close(skfd);
+ free(ifc.ifc_buf);
+ return err;
+}
+
+static int if_readlist_proc(char *target)
+{
+ static int proc_read;
+ FILE *fh;
+ char buf[512];
+ struct interface *ife;
+ int err, procnetdev_vsn;
+
+ if (proc_read)
+ return 0;
+ if (!target)
+ proc_read = 1;
+
+ fh = fopen(_PATH_PROCNET_DEV, "r");
+ if (!fh) {
+ bb_perror_msg("warning: cannot open %s, limiting output", _PATH_PROCNET_DEV);
+ return if_readconf();
+ }
+ fgets(buf, sizeof buf, fh); /* eat line */
+ fgets(buf, sizeof buf, fh);
+
+ procnetdev_vsn = procnetdev_version(buf);
+
+ err = 0;
+ while (fgets(buf, sizeof buf, fh)) {
+ char *s, name[128];
+
+ s = get_name(name, buf);
+ ife = add_interface(name);
+ get_dev_fields(s, ife, procnetdev_vsn);
+ ife->statistics_valid = 1;
+ if (target && !strcmp(target, name))
+ break;
+ }
+ if (ferror(fh)) {
+ perror(_PATH_PROCNET_DEV);
+ err = -1;
+ proc_read = 0;
+ }
+ fclose(fh);
+ return err;
+}
+
+static int if_readlist(void)
+{
+ int err = if_readlist_proc(NULL);
+ /* Needed in order to get ethN:M aliases */
+ if (!err)
+ err = if_readconf();
+ return err;
+}
+
+static int for_all_interfaces(int (*doit) (struct interface *, void *),
+ void *cookie)
+{
+ struct interface *ife;
+
+ if (!int_list && (if_readlist() < 0))
+ return -1;
+ for (ife = int_list; ife; ife = ife->next) {
+ int err = doit(ife, cookie);
+
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/* Fetch the interface configuration from the kernel. */
+static int if_fetch(struct interface *ife)
+{
+ struct ifreq ifr;
+ char *ifname = ife->name;
+ int skfd;
+
+ skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) {
+ close(skfd);
+ return -1;
+ }
+ ife->flags = ifr.ifr_flags;
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ memset(ife->hwaddr, 0, 32);
+ if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0)
+ memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
+
+ ife->type = ifr.ifr_hwaddr.sa_family;
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ife->metric = 0;
+ if (ioctl(skfd, SIOCGIFMETRIC, &ifr) >= 0)
+ ife->metric = ifr.ifr_metric;
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ife->mtu = 0;
+ if (ioctl(skfd, SIOCGIFMTU, &ifr) >= 0)
+ ife->mtu = ifr.ifr_mtu;
+
+ memset(&ife->map, 0, sizeof(struct ifmap));
+#ifdef SIOCGIFMAP
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0)
+ ife->map = ifr.ifr_map;
+#endif
+
+#ifdef HAVE_TXQUEUELEN
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ife->tx_queue_len = -1; /* unknown value */
+ if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) >= 0)
+ ife->tx_queue_len = ifr.ifr_qlen;
+#else
+ ife->tx_queue_len = -1; /* unknown value */
+#endif
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_addr.sa_family = AF_INET;
+ memset(&ife->addr, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFADDR, &ifr) == 0) {
+ ife->has_ip = 1;
+ ife->addr = ifr.ifr_addr;
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) >= 0)
+ ife->dstaddr = ifr.ifr_dstaddr;
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) >= 0)
+ ife->broadaddr = ifr.ifr_broadaddr;
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ memset(&ife->netmask, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFNETMASK, &ifr) >= 0)
+ ife->netmask = ifr.ifr_netmask;
+ }
+
+ close(skfd);
+ return 0;
+}
+
+
+static int do_if_fetch(struct interface *ife)
+{
+ if (if_fetch(ife) < 0) {
+ const char *errmsg;
+
+ if (errno == ENODEV) {
+ /* Give better error message for this case. */
+ errmsg = "Device not found";
+ } else {
+ errmsg = strerror(errno);
+ }
+ bb_error_msg("%s: error fetching interface information: %s",
+ ife->name, errmsg);
+ return -1;
+ }
+ return 0;
+}
+
+static const struct hwtype unspec_hwtype = {
+ .name = "unspec",
+ .title = "UNSPEC",
+ .type = -1,
+ .print = UNSPEC_print
+};
+
+static const struct hwtype loop_hwtype = {
+ .name = "loop",
+ .title = "Local Loopback",
+ .type = ARPHRD_LOOPBACK
+};
+
+#include <net/if_arp.h>
+
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+/* Display an Ethernet address in readable format. */
+static char *pr_ether(unsigned char *ptr)
+{
+ static char buff[64];
+
+ snprintf(buff, sizeof(buff), "%02X:%02X:%02X:%02X:%02X:%02X",
+ (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
+ (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
+ );
+ return buff;
+}
+
+static int in_ether(const char *bufp, struct sockaddr *sap);
+
+static struct hwtype ether_hwtype = {
+ .name = "ether",
+ .title = "Ethernet",
+ .type = ARPHRD_ETHER,
+ .alen = ETH_ALEN,
+ .print = pr_ether,
+ .input = in_ether
+};
+
+static unsigned hexchar2int(char c)
+{
+ if (isdigit(c))
+ return c - '0';
+ c &= ~0x20; /* a -> A */
+ if ((unsigned)(c - 'A') <= 5)
+ return c - ('A' - 10);
+ return ~0U;
+}
+
+/* Input an Ethernet address and convert to binary. */
+static int in_ether(const char *bufp, struct sockaddr *sap)
+{
+ unsigned char *ptr;
+ char c;
+ int i;
+ unsigned val;
+
+ sap->sa_family = ether_hwtype.type;
+ ptr = (unsigned char*) sap->sa_data;
+
+ i = 0;
+ while ((*bufp != '\0') && (i < ETH_ALEN)) {
+ val = hexchar2int(*bufp++) * 0x10;
+ if (val > 0xff) {
+ errno = EINVAL;
+ return -1;
+ }
+ c = *bufp;
+ if (c == ':' || c == 0)
+ val >>= 4;
+ else {
+ val |= hexchar2int(c);
+ if (val > 0xff) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ if (c != 0)
+ bufp++;
+ *ptr++ = (unsigned char) val;
+ i++;
+
+ /* We might get a semicolon here - not required. */
+ if (*bufp == ':') {
+ bufp++;
+ }
+ }
+ return 0;
+}
+
+#include <net/if_arp.h>
+
+static const struct hwtype ppp_hwtype = {
+ .name = "ppp",
+ .title = "Point-to-Point Protocol",
+ .type = ARPHRD_PPP
+};
+
+#if ENABLE_FEATURE_IPV6
+static const struct hwtype sit_hwtype = {
+ .name = "sit",
+ .title = "IPv6-in-IPv4",
+ .type = ARPHRD_SIT,
+ .print = UNSPEC_print,
+ .suppress_null_addr = 1
+} ;
+#endif
+
+static const struct hwtype *const hwtypes[] = {
+ &loop_hwtype,
+ &ether_hwtype,
+ &ppp_hwtype,
+ &unspec_hwtype,
+#if ENABLE_FEATURE_IPV6
+ &sit_hwtype,
+#endif
+ NULL
+};
+
+#ifdef IFF_PORTSEL
+static const char *const if_port_text[] = {
+ /* Keep in step with <linux/netdevice.h> */
+ "unknown",
+ "10base2",
+ "10baseT",
+ "AUI",
+ "100baseT",
+ "100baseTX",
+ "100baseFX",
+ NULL
+};
+#endif
+
+/* Check our hardware type table for this type. */
+const struct hwtype *get_hwtype(const char *name)
+{
+ const struct hwtype *const *hwp;
+
+ hwp = hwtypes;
+ while (*hwp != NULL) {
+ if (!strcmp((*hwp)->name, name))
+ return (*hwp);
+ hwp++;
+ }
+ return NULL;
+}
+
+/* Check our hardware type table for this type. */
+const struct hwtype *get_hwntype(int type)
+{
+ const struct hwtype *const *hwp;
+
+ hwp = hwtypes;
+ while (*hwp != NULL) {
+ if ((*hwp)->type == type)
+ return *hwp;
+ hwp++;
+ }
+ return NULL;
+}
+
+/* return 1 if address is all zeros */
+static int hw_null_address(const struct hwtype *hw, void *ap)
+{
+ unsigned int i;
+ unsigned char *address = (unsigned char *) ap;
+
+ for (i = 0; i < hw->alen; i++)
+ if (address[i])
+ return 0;
+ return 1;
+}
+
+static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti";
+
+static void print_bytes_scaled(unsigned long long ull, const char *end)
+{
+ unsigned long long int_part;
+ const char *ext;
+ unsigned int frac_part;
+ int i;
+
+ frac_part = 0;
+ ext = TRext;
+ int_part = ull;
+ i = 4;
+ do {
+ if (int_part >= 1024) {
+ frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
+ int_part /= 1024;
+ ext += 3; /* KiB, MiB, GiB, TiB */
+ }
+ --i;
+ } while (i);
+
+ printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end);
+}
+
+static const char *const ife_print_flags_strs[] = {
+ "UP ",
+ "BROADCAST ",
+ "DEBUG ",
+ "LOOPBACK ",
+ "POINTOPOINT ",
+ "NOTRAILERS ",
+ "RUNNING ",
+ "NOARP ",
+ "PROMISC ",
+ "ALLMULTI ",
+ "SLAVE ",
+ "MASTER ",
+ "MULTICAST ",
+#ifdef HAVE_DYNAMIC
+ "DYNAMIC "
+#endif
+};
+
+static const unsigned short ife_print_flags_mask[] = {
+ IFF_UP,
+ IFF_BROADCAST,
+ IFF_DEBUG,
+ IFF_LOOPBACK,
+ IFF_POINTOPOINT,
+ IFF_NOTRAILERS,
+ IFF_RUNNING,
+ IFF_NOARP,
+ IFF_PROMISC,
+ IFF_ALLMULTI,
+ IFF_SLAVE,
+ IFF_MASTER,
+ IFF_MULTICAST,
+#ifdef HAVE_DYNAMIC
+ IFF_DYNAMIC
+#endif
+ 0
+};
+
+static void ife_print(struct interface *ptr)
+{
+ const struct aftype *ap;
+ const struct hwtype *hw;
+ int hf;
+ int can_compress = 0;
+
+#ifdef HAVE_AFINET6
+ FILE *f;
+ char addr6[40], devname[20];
+ struct sockaddr_in6 sap;
+ int plen, scope, dad_status, if_idx;
+ char addr6p[8][5];
+#endif
+
+ ap = get_afntype(ptr->addr.sa_family);
+ if (ap == NULL)
+ ap = get_afntype(0);
+
+ hf = ptr->type;
+
+ if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
+ can_compress = 1;
+
+ hw = get_hwntype(hf);
+ if (hw == NULL)
+ hw = get_hwntype(-1);
+
+ printf("%-9.9s Link encap:%s ", ptr->name, hw->title);
+ /* For some hardware types (eg Ash, ATM) we don't print the
+ hardware address if it's null. */
+ if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) &&
+ hw->suppress_null_addr)))
+ printf("HWaddr %s ", hw->print((unsigned char *)ptr->hwaddr));
+#ifdef IFF_PORTSEL
+ if (ptr->flags & IFF_PORTSEL) {
+ printf("Media:%s", if_port_text[ptr->map.port] /* [0] */);
+ if (ptr->flags & IFF_AUTOMEDIA)
+ printf("(auto)");
+ }
+#endif
+ puts("");
+
+ if (ptr->has_ip) {
+ printf(" %s addr:%s ", ap->name,
+ ap->sprint(&ptr->addr, 1));
+ if (ptr->flags & IFF_POINTOPOINT) {
+ printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
+ }
+ if (ptr->flags & IFF_BROADCAST) {
+ printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1));
+ }
+ printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1));
+ }
+
+#ifdef HAVE_AFINET6
+
+#define IPV6_ADDR_ANY 0x0000U
+
+#define IPV6_ADDR_UNICAST 0x0001U
+#define IPV6_ADDR_MULTICAST 0x0002U
+#define IPV6_ADDR_ANYCAST 0x0004U
+
+#define IPV6_ADDR_LOOPBACK 0x0010U
+#define IPV6_ADDR_LINKLOCAL 0x0020U
+#define IPV6_ADDR_SITELOCAL 0x0040U
+
+#define IPV6_ADDR_COMPATv4 0x0080U
+
+#define IPV6_ADDR_SCOPE_MASK 0x00f0U
+
+#define IPV6_ADDR_MAPPED 0x1000U
+#define IPV6_ADDR_RESERVED 0x2000U /* reserved address space */
+
+ f = fopen(_PATH_PROCNET_IFINET6, "r");
+ if (f != NULL) {
+ while (fscanf
+ (f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n",
+ addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
+ addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
+ &dad_status, devname) != EOF
+ ) {
+ if (!strcmp(devname, ptr->name)) {
+ sprintf(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 *) &sap.sin6_addr);
+ sap.sin6_family = AF_INET6;
+ printf(" inet6 addr: %s/%d",
+ inet6_aftype.sprint((struct sockaddr *) &sap, 1),
+ plen);
+ printf(" Scope:");
+ switch (scope & IPV6_ADDR_SCOPE_MASK) {
+ case 0:
+ printf("Global");
+ break;
+ case IPV6_ADDR_LINKLOCAL:
+ printf("Link");
+ break;
+ case IPV6_ADDR_SITELOCAL:
+ printf("Site");
+ break;
+ case IPV6_ADDR_COMPATv4:
+ printf("Compat");
+ break;
+ case IPV6_ADDR_LOOPBACK:
+ printf("Host");
+ break;
+ default:
+ printf("Unknown");
+ }
+ puts("");
+ }
+ }
+ fclose(f);
+ }
+#endif
+
+ printf(" ");
+ /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
+
+ if (ptr->flags == 0) {
+ printf("[NO FLAGS] ");
+ } else {
+ int i = 0;
+ do {
+ if (ptr->flags & ife_print_flags_mask[i]) {
+ printf(ife_print_flags_strs[i]);
+ }
+ } while (ife_print_flags_mask[++i]);
+ }
+
+ /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
+ printf(" MTU:%d Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1);
+#ifdef SIOCSKEEPALIVE
+ if (ptr->outfill || ptr->keepalive)
+ printf(" Outfill:%d Keepalive:%d", ptr->outfill, ptr->keepalive);
+#endif
+ puts("");
+
+ /* If needed, display the interface statistics. */
+
+ if (ptr->statistics_valid) {
+ /* XXX: statistics are currently only printed for the primary address,
+ * not for the aliases, although strictly speaking they're shared
+ * by all addresses.
+ */
+ printf(" ");
+
+ printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
+ ptr->stats.rx_packets, ptr->stats.rx_errors,
+ ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
+ ptr->stats.rx_frame_errors);
+ if (can_compress)
+ printf(" compressed:%lu\n",
+ ptr->stats.rx_compressed);
+ printf(" ");
+ printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
+ ptr->stats.tx_packets, ptr->stats.tx_errors,
+ ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
+ ptr->stats.tx_carrier_errors);
+ printf(" collisions:%lu ", ptr->stats.collisions);
+ if (can_compress)
+ printf("compressed:%lu ", ptr->stats.tx_compressed);
+ if (ptr->tx_queue_len != -1)
+ printf("txqueuelen:%d ", ptr->tx_queue_len);
+ printf("\n R");
+ print_bytes_scaled(ptr->stats.rx_bytes, " T");
+ print_bytes_scaled(ptr->stats.tx_bytes, "\n");
+
+ }
+
+ if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma ||
+ ptr->map.base_addr)) {
+ printf(" ");
+ if (ptr->map.irq)
+ printf("Interrupt:%d ", ptr->map.irq);
+ if (ptr->map.base_addr >= 0x100) /* Only print devices using it for
+ I/O maps */
+ printf("Base address:0x%lx ",
+ (unsigned long) ptr->map.base_addr);
+ if (ptr->map.mem_start) {
+ printf("Memory:%lx-%lx ", ptr->map.mem_start,
+ ptr->map.mem_end);
+ }
+ if (ptr->map.dma)
+ printf("DMA chan:%x ", ptr->map.dma);
+ puts("");
+ }
+ puts("");
+}
+
+
+static int do_if_print(struct interface *ife, void *cookie)
+{
+ int *opt_a = (int *) cookie;
+ int res;
+
+ res = do_if_fetch(ife);
+ if (res >= 0) {
+ if ((ife->flags & IFF_UP) || *opt_a)
+ ife_print(ife);
+ }
+ return res;
+}
+
+static struct interface *lookup_interface(char *name)
+{
+ struct interface *ife = NULL;
+
+ if (if_readlist_proc(name) < 0)
+ return NULL;
+ ife = add_interface(name);
+ return ife;
+}
+
+/* for ipv4 add/del modes */
+static int if_print(char *ifname)
+{
+ int res;
+
+ if (!ifname) {
+ res = for_all_interfaces(do_if_print, &interface_opt_a);
+ } else {
+ struct interface *ife;
+
+ ife = lookup_interface(ifname);
+ res = do_if_fetch(ife);
+ if (res >= 0)
+ ife_print(ife);
+ }
+ return res;
+}
+
+int display_interfaces(char *ifname)
+{
+ int status;
+
+ status = if_print(ifname);
+
+ return (status < 0); /* status < 0 == 1 -- error */
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ip.c b/i/pc104/initrd/conf/busybox/networking/ip.c
new file mode 100644
index 0000000..fdd8480
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ip.c
@@ -0,0 +1,50 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "busybox.h"
+
+#include "libiproute/utils.h"
+#include "libiproute/ip_common.h"
+
+int ip_main(int argc, char **argv);
+int ip_main(int argc, char **argv)
+{
+ int ret = EXIT_FAILURE;
+
+ ip_parse_common_args(&argc, &argv);
+
+ if (argc > 1) {
+ if (ENABLE_FEATURE_IP_ADDRESS && matches(argv[1], "address") == 0) {
+ ret = do_ipaddr(argc-2, argv+2);
+ }
+ if (ENABLE_FEATURE_IP_ROUTE && matches(argv[1], "route") == 0) {
+ ret = do_iproute(argc-2, argv+2);
+ }
+ if (ENABLE_FEATURE_IP_LINK && matches(argv[1], "link") == 0) {
+ ret = do_iplink(argc-2, argv+2);
+ }
+ if (ENABLE_FEATURE_IP_TUNNEL
+ && (matches(argv[1], "tunnel") == 0 || strcmp(argv[1], "tunl") == 0)
+ ) {
+ ret = do_iptunnel(argc-2, argv+2);
+ }
+ if (ENABLE_FEATURE_IP_RULE && matches(argv[1], "rule") == 0) {
+ ret = do_iprule(argc-2, argv+2);
+ }
+ }
+ if (ret) {
+ bb_show_usage();
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ipaddr.c b/i/pc104/initrd/conf/busybox/networking/ipaddr.c
new file mode 100644
index 0000000..fb02137
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ipaddr.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * 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);
+int ipaddr_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_ipaddr(argc-1, argv+1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ipcalc.c b/i/pc104/initrd/conf/busybox/networking/ipcalc.c
new file mode 100644
index 0000000..63ce99f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ipcalc.c
@@ -0,0 +1,195 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ipcalc implementation for busybox
+ *
+ * By Jordan Crouse <jordan@cosmicpenguin.net>
+ * Stephan Linz <linz@li-pro.net>
+ *
+ * This is a complete reimplementation of the ipcalc program
+ * from Red Hat. I didn't look at their source code, but there
+ * is no denying that this is a loving reimplementation
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <ctype.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#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;
+}
+
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+static int get_prefix(unsigned long netmask)
+{
+ unsigned long msk = 0x80000000;
+ int ret = 0;
+
+ netmask = htonl(netmask);
+ while (msk) {
+ if (netmask & msk)
+ ret++;
+ msk >>= 1;
+ }
+ return ret;
+}
+#else
+int get_prefix(unsigned long netmask);
+#endif
+
+
+#define NETMASK 0x01
+#define BROADCAST 0x02
+#define NETWORK 0x04
+#define NETPREFIX 0x08
+#define HOSTNAME 0x10
+#define SILENT 0x20
+
+#if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
+ static const struct option long_options[] = {
+ { "netmask", no_argument, NULL, 'm' },
+ { "broadcast", no_argument, NULL, 'b' },
+ { "network", no_argument, NULL, 'n' },
+# if ENABLE_FEATURE_IPCALC_FANCY
+ { "prefix", no_argument, NULL, 'p' },
+ { "hostname", no_argument, NULL, 'h' },
+ { "silent", no_argument, NULL, 's' },
+# endif
+ { NULL, 0, NULL, 0 }
+ };
+#endif
+
+int ipcalc_main(int argc, char **argv);
+int ipcalc_main(int argc, char **argv)
+{
+ unsigned opt;
+ int have_netmask = 0;
+ in_addr_t netmask, broadcast, network, ipaddr;
+ struct in_addr a;
+ char *ipstr;
+
+#if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
+ applet_long_options = long_options;
+#endif
+ opt = getopt32(argc, argv, "mbn" USE_FEATURE_IPCALC_FANCY("phs"));
+ argc -= optind;
+ argv += optind;
+ if (opt & (BROADCAST | NETWORK | NETPREFIX)) {
+ if (argc > 2 || argc <= 0)
+ bb_show_usage();
+ } else {
+ if (argc != 1)
+ bb_show_usage();
+ }
+ if (opt & SILENT)
+ logmode = LOGMODE_NONE; /* Suppress error_msg() output */
+
+ ipstr = argv[0];
+ if (ENABLE_FEATURE_IPCALC_FANCY) {
+ unsigned long netprefix = 0;
+ char *prefixstr;
+
+ prefixstr = ipstr;
+
+ while (*prefixstr) {
+ if (*prefixstr == '/') {
+ *prefixstr = (char)0;
+ prefixstr++;
+ if (*prefixstr) {
+ unsigned msk;
+ netprefix = xatoul_range(prefixstr, 0, 32);
+ netmask = 0;
+ msk = 0x80000000;
+ while (netprefix > 0) {
+ netmask |= msk;
+ msk >>= 1;
+ netprefix--;
+ }
+ netmask = htonl(netmask);
+ /* Even if it was 0, we will signify that we have a netmask. This allows */
+ /* for specification of default routes, etc which have a 0 netmask/prefix */
+ have_netmask = 1;
+ }
+ break;
+ }
+ prefixstr++;
+ }
+ }
+ ipaddr = inet_aton(ipstr, &a);
+
+ if (ipaddr == 0) {
+ bb_error_msg_and_die("bad IP address: %s", argv[0]);
+ }
+ ipaddr = a.s_addr;
+
+ if (argc == 2) {
+ if (ENABLE_FEATURE_IPCALC_FANCY && have_netmask) {
+ bb_error_msg_and_die("use prefix or netmask, not both");
+ }
+
+ netmask = inet_aton(argv[1], &a);
+ if (netmask == 0) {
+ bb_error_msg_and_die("bad netmask: %s", argv[1]);
+ }
+ netmask = a.s_addr;
+ } else {
+
+ /* JHC - If the netmask wasn't provided then calculate it */
+ if (!ENABLE_FEATURE_IPCALC_FANCY || !have_netmask)
+ netmask = get_netmask(ipaddr);
+ }
+
+ if (opt & NETMASK) {
+ printf("NETMASK=%s\n", inet_ntoa((*(struct in_addr *) &netmask)));
+ }
+
+ if (opt & BROADCAST) {
+ broadcast = (ipaddr & netmask) | ~netmask;
+ printf("BROADCAST=%s\n", inet_ntoa((*(struct in_addr *) &broadcast)));
+ }
+
+ if (opt & NETWORK) {
+ network = ipaddr & netmask;
+ printf("NETWORK=%s\n", inet_ntoa((*(struct in_addr *) &network)));
+ }
+
+ if (ENABLE_FEATURE_IPCALC_FANCY) {
+ if (opt & NETPREFIX) {
+ printf("PREFIX=%i\n", get_prefix(netmask));
+ }
+
+ if (opt & HOSTNAME) {
+ struct hostent *hostinfo;
+ int x;
+
+ hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET);
+ if (!hostinfo) {
+ bb_herror_msg_and_die("cannot find hostname for %s", argv[0]);
+ }
+ for (x = 0; hostinfo->h_name[x]; x++) {
+ hostinfo->h_name[x] = tolower(hostinfo->h_name[x]);
+ }
+
+ printf("HOSTNAME=%s\n", hostinfo->h_name);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/iplink.c b/i/pc104/initrd/conf/busybox/networking/iplink.c
new file mode 100644
index 0000000..54087e9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/iplink.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * 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);
+int iplink_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_iplink(argc-1, argv+1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/iproute.c b/i/pc104/initrd/conf/busybox/networking/iproute.c
new file mode 100644
index 0000000..3d540b2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/iproute.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * 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);
+int iproute_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_iproute(argc-1, argv+1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/iprule.c b/i/pc104/initrd/conf/busybox/networking/iprule.c
new file mode 100644
index 0000000..9c1fb50
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/iprule.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * 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 iprule_main(int argc, char **argv);
+int iprule_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_iprule(argc-1, argv+1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/iptunnel.c b/i/pc104/initrd/conf/busybox/networking/iptunnel.c
new file mode 100644
index 0000000..8a65413
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/iptunnel.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * 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);
+int iptunnel_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_iptunnel(argc-1, argv+1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/isrv.c b/i/pc104/initrd/conf/busybox/networking/isrv.c
new file mode 100644
index 0000000..f0df222
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/isrv.c
@@ -0,0 +1,352 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Generic non-forking server infrastructure.
+ * Intended to make writing telnetd-type servers easier.
+ *
+ * Copyright (C) 2007 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include "isrv.h"
+
+#define DEBUG 0
+
+#if DEBUG
+#define DPRINTF(args...) bb_error_msg(args)
+#else
+#define DPRINTF(args...) ((void)0)
+#endif
+
+/* Helpers */
+
+/* Even if _POSIX_MONOTONIC_CLOCK is defined, this
+ * may require librt */
+#if 0 /*def _POSIX_MONOTONIC_CLOCK*/
+static time_t monotonic_time(void)
+{
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ time(&ts.tv_sec);
+ return ts.tv_sec;
+}
+#else
+#define monotonic_time() (time(NULL))
+#endif
+
+/* Opaque structure */
+
+struct isrv_state_t {
+ short *fd2peer; /* one per registered fd */
+ void **param_tbl; /* one per registered peer */
+ /* one per registered peer; doesn't exist if !timeout */
+ time_t *timeo_tbl;
+ int (*new_peer)(isrv_state_t *state, int fd);
+ time_t curtime;
+ int timeout;
+ int fd_count;
+ int peer_count;
+ int wr_count;
+ fd_set rd;
+ fd_set wr;
+};
+#define FD2PEER (state->fd2peer)
+#define PARAM_TBL (state->param_tbl)
+#define TIMEO_TBL (state->timeo_tbl)
+#define CURTIME (state->curtime)
+#define TIMEOUT (state->timeout)
+#define FD_COUNT (state->fd_count)
+#define PEER_COUNT (state->peer_count)
+#define WR_COUNT (state->wr_count)
+
+/* callback */
+void isrv_want_rd(isrv_state_t *state, int fd)
+{
+ FD_SET(fd, &state->rd);
+}
+
+/* callback */
+void isrv_want_wr(isrv_state_t *state, int fd)
+{
+ if (!FD_ISSET(fd, &state->wr)) {
+ WR_COUNT++;
+ FD_SET(fd, &state->wr);
+ }
+}
+
+/* callback */
+void isrv_dont_want_rd(isrv_state_t *state, int fd)
+{
+ FD_CLR(fd, &state->rd);
+}
+
+/* callback */
+void isrv_dont_want_wr(isrv_state_t *state, int fd)
+{
+ if (FD_ISSET(fd, &state->wr)) {
+ WR_COUNT--;
+ FD_CLR(fd, &state->wr);
+ }
+}
+
+/* callback */
+int isrv_register_fd(isrv_state_t *state, int peer, int fd)
+{
+ int n;
+
+ DPRINTF("register_fd(peer:%d,fd:%d)", peer, fd);
+
+ if (FD_COUNT >= FD_SETSIZE) return -1;
+ if (FD_COUNT <= fd) {
+ n = FD_COUNT;
+ FD_COUNT = fd + 1;
+
+ DPRINTF("register_fd: FD_COUNT %d", FD_COUNT);
+
+ FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+ while (n < fd) FD2PEER[n++] = -1;
+ }
+
+ DPRINTF("register_fd: FD2PEER[%d] = %d", fd, peer);
+
+ FD2PEER[fd] = peer;
+ return 0;
+}
+
+/* callback */
+void isrv_close_fd(isrv_state_t *state, int fd)
+{
+ DPRINTF("close_fd(%d)", fd);
+
+ close(fd);
+ isrv_dont_want_rd(state, fd);
+ if (WR_COUNT) isrv_dont_want_wr(state, fd);
+
+ FD2PEER[fd] = -1;
+ if (fd == FD_COUNT-1) {
+ do fd--; while (fd >= 0 && FD2PEER[fd] == -1);
+ FD_COUNT = fd + 1;
+
+ DPRINTF("close_fd: FD_COUNT %d", FD_COUNT);
+
+ FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+ }
+}
+
+/* callback */
+int isrv_register_peer(isrv_state_t *state, void *param)
+{
+ int n;
+
+ if (PEER_COUNT >= FD_SETSIZE) return -1;
+ n = PEER_COUNT++;
+
+ DPRINTF("register_peer: PEER_COUNT %d", PEER_COUNT);
+
+ PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+ PARAM_TBL[n] = param;
+ if (TIMEOUT) {
+ TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+ TIMEO_TBL[n] = CURTIME;
+ }
+ return n;
+}
+
+static void remove_peer(isrv_state_t *state, int peer)
+{
+ int movesize;
+ int fd;
+
+ DPRINTF("remove_peer(%d)", peer);
+
+ fd = FD_COUNT - 1;
+ while (fd >= 0) {
+ if (FD2PEER[fd] == peer) {
+ isrv_close_fd(state, fd);
+ fd--;
+ continue;
+ }
+ if (FD2PEER[fd] > peer)
+ FD2PEER[fd]--;
+ fd--;
+ }
+
+ PEER_COUNT--;
+ DPRINTF("remove_peer: PEER_COUNT %d", PEER_COUNT);
+
+ movesize = (PEER_COUNT - peer) * sizeof(void*);
+ if (movesize > 0) {
+ memcpy(&PARAM_TBL[peer], &PARAM_TBL[peer+1], movesize);
+ if (TIMEOUT)
+ memcpy(&TIMEO_TBL[peer], &TIMEO_TBL[peer+1], movesize);
+ }
+ PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+ if (TIMEOUT)
+ TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+}
+
+static void handle_accept(isrv_state_t *state, int fd)
+{
+ int n, newfd;
+
+ /* suppress gcc warning "cast from ptr to int of different size" */
+ fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]) | O_NONBLOCK);
+ newfd = accept(fd, NULL, 0);
+ fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]));
+ if (newfd < 0) {
+ if (errno == EAGAIN) return;
+ /* Most probably someone gave us wrong fd type
+ * (for example, non-socket). Don't want
+ * to loop forever. */
+ bb_perror_msg_and_die("accept");
+ }
+
+ DPRINTF("new_peer(%d)", newfd);
+ n = state->new_peer(state, newfd);
+ if (n)
+ remove_peer(state, n); /* unsuccesful peer start */
+}
+
+void BUG_sizeof_fd_set_is_strange(void);
+static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void **))
+{
+ enum { LONG_CNT = sizeof(fd_set) / sizeof(long) };
+ int fds_pos;
+ int fd, peer;
+ /* need to know value at _the beginning_ of this routine */
+ int fd_cnt = FD_COUNT;
+
+ if (LONG_CNT * sizeof(long) != sizeof(fd_set))
+ BUG_sizeof_fd_set_is_strange();
+
+ fds_pos = 0;
+ while (1) {
+ /* Find next nonzero bit */
+ while (fds_pos < LONG_CNT) {
+ if (((long*)fds)[fds_pos] == 0) {
+ fds_pos++;
+ continue;
+ }
+ /* Found non-zero word */
+ fd = fds_pos * sizeof(long)*8; /* word# -> bit# */
+ while (1) {
+ if (FD_ISSET(fd, fds)) {
+ FD_CLR(fd, fds);
+ goto found_fd;
+ }
+ fd++;
+ }
+ }
+ break; /* all words are zero */
+ found_fd:
+ if (fd >= fd_cnt) { /* paranoia */
+ DPRINTF("handle_fd_set: fd > fd_cnt?? (%d > %d)",
+ fd, fd_cnt);
+ break;
+ }
+ DPRINTF("handle_fd_set: fd %d is active", fd);
+ peer = FD2PEER[fd];
+ if (peer < 0)
+ continue; /* peer is already gone */
+ if (peer == 0) {
+ handle_accept(state, fd);
+ continue;
+ }
+ DPRINTF("h(fd:%d)", fd);
+ if (h(fd, &PARAM_TBL[peer])) {
+ /* this peer is gone */
+ remove_peer(state, peer);
+ } else if (TIMEOUT) {
+ TIMEO_TBL[peer] = monotonic_time();
+ }
+ }
+}
+
+static void handle_timeout(isrv_state_t *state, int (*do_timeout)(void **))
+{
+ int n, peer;
+ peer = PEER_COUNT-1;
+ /* peer 0 is not checked */
+ while (peer > 0) {
+ DPRINTF("peer %d: time diff %d", peer,
+ (int)(CURTIME - TIMEO_TBL[peer]));
+ if ((CURTIME - TIMEO_TBL[peer]) >= TIMEOUT) {
+ DPRINTF("peer %d: do_timeout()", peer);
+ n = do_timeout(&PARAM_TBL[peer]);
+ if (n)
+ remove_peer(state, peer);
+ }
+ peer--;
+ }
+}
+
+/* Driver */
+void isrv_run(
+ int listen_fd,
+ int (*new_peer)(isrv_state_t *state, int fd),
+ int (*do_rd)(int fd, void **),
+ int (*do_wr)(int fd, void **),
+ int (*do_timeout)(void **),
+ int timeout,
+ int linger_timeout)
+{
+ isrv_state_t *state = xzalloc(sizeof(*state));
+ state->new_peer = new_peer;
+ state->timeout = timeout;
+
+ /* register "peer" #0 - it will accept new connections */
+ isrv_register_peer(state, NULL);
+ isrv_register_fd(state, /*peer:*/ 0, listen_fd);
+ isrv_want_rd(state, listen_fd);
+ /* remember flags to make blocking<->nonblocking switch faster */
+ /* (suppress gcc warning "cast from ptr to int of different size") */
+ PARAM_TBL[0] = (void*)(ptrdiff_t)(fcntl(listen_fd, F_GETFL, 0));
+
+ while (1) {
+ struct timeval tv;
+ fd_set rd;
+ fd_set wr;
+ fd_set *wrp = NULL;
+ int n;
+
+ tv.tv_sec = timeout;
+ if (PEER_COUNT <= 1)
+ tv.tv_sec = linger_timeout;
+ tv.tv_usec = 0;
+ rd = state->rd;
+ if (WR_COUNT) {
+ wr = state->wr;
+ wrp = &wr;
+ }
+
+ DPRINTF("run: select(FD_COUNT:%d,timeout:%d)...",
+ FD_COUNT, (int)tv.tv_sec);
+ n = select(FD_COUNT, &rd, wrp, NULL, tv.tv_sec ? &tv : NULL);
+ DPRINTF("run: ...select:%d", n);
+
+ if (n < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("select");
+ continue;
+ }
+
+ if (n == 0 && linger_timeout && PEER_COUNT <= 1)
+ break;
+
+ if (timeout) {
+ time_t t = monotonic_time();
+ if (t != CURTIME) {
+ CURTIME = t;
+ handle_timeout(state, do_timeout);
+ }
+ }
+ if (n > 0) {
+ handle_fd_set(state, &rd, do_rd);
+ if (wrp)
+ handle_fd_set(state, wrp, do_wr);
+ }
+ }
+ DPRINTF("run: bailout");
+ /* NB: accept socket is not closed. Caller is to decide what to do */
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/isrv.h b/i/pc104/initrd/conf/busybox/networking/isrv.h
new file mode 100644
index 0000000..c786770
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/isrv.h
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Generic non-forking server infrastructure.
+ * Intended to make writing telnetd-type servers easier.
+ *
+ * Copyright (C) 2007 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+/* opaque structure */
+struct isrv_state_t;
+typedef struct isrv_state_t isrv_state_t;
+
+/* callbacks */
+void isrv_want_rd(isrv_state_t *state, int fd);
+void isrv_want_wr(isrv_state_t *state, int fd);
+void isrv_dont_want_rd(isrv_state_t *state, int fd);
+void isrv_dont_want_wr(isrv_state_t *state, int fd);
+int isrv_register_fd(isrv_state_t *state, int peer, int fd);
+void isrv_close_fd(isrv_state_t *state, int fd);
+int isrv_register_peer(isrv_state_t *state, void *param);
+
+/* driver */
+void isrv_run(
+ int listen_fd,
+ int (*new_peer)(isrv_state_t *state, int fd),
+ int (*do_rd)(int fd, void **),
+ int (*do_wr)(int fd, void **),
+ int (*do_timeout)(void **),
+ int timeout,
+ int linger_timeout
+);
diff --git a/i/pc104/initrd/conf/busybox/networking/isrv_identd.c b/i/pc104/initrd/conf/busybox/networking/isrv_identd.c
new file mode 100644
index 0000000..f9ab0b2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/isrv_identd.c
@@ -0,0 +1,144 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Fake identd server.
+ *
+ * Copyright (C) 2007 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include <syslog.h>
+#include "busybox.h"
+#include "isrv.h"
+
+enum { TIMEOUT = 20 };
+
+typedef struct identd_buf_t {
+ int pos;
+ int fd_flag;
+ char buf[64 - 2*sizeof(int)];
+} identd_buf_t;
+
+static const char *bogouser = "nobody";
+
+static int new_peer(isrv_state_t *state, int fd)
+{
+ int peer;
+ identd_buf_t *buf = xzalloc(sizeof(*buf));
+
+ peer = isrv_register_peer(state, buf);
+ if (peer < 0)
+ return 0; /* failure */
+ if (isrv_register_fd(state, peer, fd) < 0)
+ return peer; /* failure, unregister peer */
+
+ buf->fd_flag = fcntl(fd, F_GETFL, 0) | O_NONBLOCK;
+ isrv_want_rd(state, fd);
+ return 0;
+}
+
+static int do_rd(int fd, void **paramp)
+{
+ identd_buf_t *buf = *paramp;
+ char *cur, *p;
+ int retval = 0; /* session is ok (so far) */
+ int sz;
+
+ cur = buf->buf + buf->pos;
+
+ if (buf->fd_flag & O_NONBLOCK)
+ fcntl(fd, F_SETFL, buf->fd_flag);
+ sz = safe_read(fd, cur, sizeof(buf->buf) - buf->pos);
+
+ if (sz < 0) {
+ if (errno != EAGAIN)
+ goto term; /* terminate this session if !EAGAIN */
+ goto ok;
+ }
+
+ buf->pos += sz;
+ buf->buf[buf->pos] = '\0';
+ p = strpbrk(cur, "\r\n");
+ if (p)
+ *p = '\0';
+ if (!p && sz && buf->pos <= sizeof(buf->buf))
+ goto ok;
+ /* Terminate session. If we are in server mode, then
+ * fd is still in nonblocking mode - we never block here */
+ if (fd == 0) fd++; /* inetd mode? then write to fd 1 */
+ fdprintf(fd, "%s : USERID : UNIX : %s\r\n", buf->buf, bogouser);
+ term:
+ free(buf);
+ retval = 1; /* terminate */
+ ok:
+ if (buf->fd_flag & O_NONBLOCK)
+ fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
+ return retval;
+}
+
+static int do_timeout(void **paramp)
+{
+ return 1; /* terminate session */
+}
+
+static void inetd_mode(void)
+{
+ identd_buf_t *buf = xzalloc(sizeof(*buf));
+ /* buf->pos = 0; - xzalloc did it */
+ /* We do NOT want nonblocking I/O here! */
+ /* buf->fd_flag = 0; - xzalloc did it */
+ do
+ alarm(TIMEOUT);
+ while (do_rd(0, (void*)&buf) == 0);
+}
+
+int fakeidentd_main(int argc, char **argv);
+int fakeidentd_main(int argc, char **argv)
+{
+ enum {
+ OPT_foreground = 0x1,
+ OPT_inetd = 0x2,
+ OPT_inetdwait = 0x4,
+ OPT_fiw = 0x7,
+ OPT_bindaddr = 0x8,
+ };
+
+ const char *bind_address = NULL;
+ unsigned opt;
+ int fd;
+
+ opt = getopt32(argc, argv, "fiwb:", &bind_address);
+ if (optind < argc)
+ bogouser = argv[optind];
+
+ /* Daemonize if no -f and no -i and no -w */
+ bb_sanitize_stdio_maybe_daemonize(!(opt & OPT_fiw));
+ /* Where to log in inetd modes? "Classic" inetd
+ * probably has its stderr /dev/null'ed (we need log to syslog?),
+ * but daemontools-like utilities usually expect that children
+ * log to stderr. I like daemontools more. Go their way.
+ * (Or maybe we need yet another option "log to syslog") */
+ if (!(opt & OPT_fiw) /* || (opt & OPT_syslog) */) {
+ openlog(applet_name, 0, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ if (opt & OPT_inetd) {
+ inetd_mode();
+ return 0;
+ }
+
+ /* Ignore closed connections when writing */
+ signal(SIGPIPE, SIG_IGN);
+
+ fd = 0;
+ if (!(opt & OPT_inetdwait)) {
+ fd = create_and_bind_stream_or_die(bind_address,
+ bb_lookup_port("identd", "tcp", 113));
+ xlisten(fd, 5);
+ }
+
+ isrv_run(fd, new_peer, do_rd, /*do_wr:*/ NULL, do_timeout,
+ TIMEOUT, (opt & OPT_inetdwait) ? TIMEOUT : 0);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/Kbuild b/i/pc104/initrd/conf/busybox/networking/libiproute/Kbuild
new file mode 100644
index 0000000..8383630
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/Kbuild
@@ -0,0 +1,66 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+#
+
+lib-y:=
+lib-$(CONFIG_IP) += \
+ ip_parse_common_args.o \
+ ipaddress.o \
+ iplink.o \
+ iproute.o \
+ iptunnel.o \
+ iprule.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_proto.o \
+ ll_types.o \
+ rt_names.o \
+ rtm_map.o \
+ utils.o
+
+lib-$(CONFIG_IPADDR) += \
+ ip_parse_common_args.o \
+ ipaddress.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_types.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_IPLINK) += \
+ ip_parse_common_args.o \
+ ipaddress.o \
+ iplink.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_types.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_IPROUTE) += \
+ ip_parse_common_args.o \
+ iproute.o \
+ libnetlink.o \
+ ll_map.o \
+ rt_names.o \
+ rtm_map.o \
+ utils.o
+
+lib-$(CONFIG_IPTUNNEL) += \
+ ip_parse_common_args.o \
+ iptunnel.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_IPRULE) += \
+ ip_parse_common_args.o \
+ iprule.o \
+ rt_names.o \
+ utils.o
+
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ip_common.h b/i/pc104/initrd/conf/busybox/networking/libiproute/ip_common.h
new file mode 100644
index 0000000..05a6a95
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ip_common.h
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+#ifndef _IP_COMMON_H
+#define _IP_COMMON_H 1
+
+#include "busybox.h"
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#if !defined IFA_RTA
+#include <linux/if_addr.h>
+#endif
+#if !defined IFLA_RTA
+#include <linux/if_link.h>
+#endif
+
+extern int preferred_family;
+//FIXME! Appears in two .h files!
+extern const 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);
+#endif /* ip_common.h */
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ip_parse_common_args.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ip_parse_common_args.c
new file mode 100644
index 0000000..00a8912
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ip_parse_common_args.c
@@ -0,0 +1,79 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 "libbb.h"
+#include "utils.h"
+#include "ip_common.h"
+
+
+int preferred_family = AF_UNSPEC;
+int oneline = 0;
+const 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], "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" ;
+ *argcp = argc;
+ *argvp = argv;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ipaddress.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ipaddress.c
new file mode 100644
index 0000000..bc9963f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ipaddress.c
@@ -0,0 +1,824 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ipaddress.c "ip address".
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
+ */
+
+#include "libbb.h"
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <fnmatch.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.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;
+
+static 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));
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_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 ATTRIBUTE_UNUSED *who,
+ const struct nlmsghdr *n, void ATTRIBUTE_UNUSED *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 ATTRIBUTE_UNUSED *who,
+ struct nlmsghdr *n, void ATTRIBUTE_UNUSED *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;
+}
+
+int ipaddr_list_or_flush(int argc, char **argv, int flush)
+{
+ static const char *const 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) {
+ bb_error_msg(bb_msg_requires_arg, "flush");
+ return -1;
+ }
+ if (filter.family == AF_PACKET) {
+ bb_error_msg("cannot flush link addresses");
+ return -1;
+ }
+ }
+
+ while (argc > 0) {
+ const int option_num = index_in_str_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 */
+ {
+ uint32_t scope = 0;
+ NEXT_ARG();
+ filter.scopemask = -1;
+ if (rtnl_rtscope_a2n(&scope, *argv)) {
+ if (strcmp(*argv, "all") != 0) {
+ invarg(*argv, "scope");
+ }
+ 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) {
+ 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) {
+ fflush(stdout);
+ return 0;
+ }
+ 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 && *(uint8_t*)&lcl->data == 127)
+ return RT_SCOPE_HOST;
+ }
+ return 0;
+}
+
+static int ipaddr_modify(int cmd, int argc, char **argv)
+{
+ static const char *const 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 int option_num = index_in_str_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 (LONE_CHAR(*argv, '+')) {
+ brd_len = -1;
+ }
+ else if (LONE_DASH(*argv)) {
+ 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 */
+ {
+ uint32_t scope = 0;
+ NEXT_ARG();
+ if (rtnl_rtscope_a2n(&scope, *argv)) {
+ invarg(*argv, "scope");
+ }
+ 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(bb_msg_requires_arg,"\"dev\"");
+ 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);
+}
+
+int do_ipaddr(int argc, char **argv)
+{
+ static const char *const commands[] = {
+ "add", "delete", "list", "show", "lst", "flush", 0
+ };
+
+ int command_num = 2;
+
+ if (*argv) {
+ command_num = index_in_substr_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/i/pc104/initrd/conf/busybox/networking/libiproute/iplink.c b/i/pc104/initrd/conf/busybox/networking/libiproute/iplink.c
new file mode 100644
index 0000000..cb6ee96
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/iplink.c
@@ -0,0 +1,350 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iplink.c "ip link".
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_packet.h>
+#include <netpacket/packet.h>
+
+#include <net/ethernet.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+/* take from linux/sockios.h */
+#define SIOCSIFNAME 0x8923 /* set interface name */
+
+static int on_off(const 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;
+ bb_perror_msg("cannot create control socket");
+ return -1;
+}
+
+static int do_chflags(char *dev, uint32_t flags, uint32_t mask)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ fd = get_ctl_fd();
+ if (fd < 0)
+ return -1;
+ err = ioctl(fd, SIOCGIFFLAGS, &ifr);
+ if (err) {
+ bb_perror_msg("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)
+ bb_perror_msg("SIOCSIFFLAGS");
+ }
+ close(fd);
+ return err;
+}
+
+static int do_changename(char *dev, char *newdev)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ strncpy(ifr.ifr_newname, newdev, sizeof(ifr.ifr_newname));
+ fd = get_ctl_fd();
+ if (fd < 0)
+ return -1;
+ err = ioctl(fd, SIOCSIFNAME, &ifr);
+ if (err) {
+ bb_perror_msg("SIOCSIFNAME");
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return err;
+}
+
+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));
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ ifr.ifr_qlen = qlen;
+ if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
+ bb_perror_msg("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));
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ ifr.ifr_mtu = mtu;
+ if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
+ bb_perror_msg("SIOCSIFMTU");
+ close(s);
+ return -1;
+ }
+ close(s);
+
+ return 0;
+}
+
+static int get_address(char *dev, int *htype)
+{
+ struct ifreq ifr;
+ struct sockaddr_ll me;
+ socklen_t alen;
+ int s;
+
+ s = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ bb_perror_msg("socket(PF_PACKET)");
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ bb_perror_msg("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) {
+ bb_perror_msg("bind");
+ close(s);
+ return -1;
+ }
+
+ alen = sizeof(me);
+ if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
+ bb_perror_msg("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));
+ strncpy(ifr->ifr_name, dev, sizeof(ifr->ifr_name));
+ ifr->ifr_hwaddr.sa_family = hatype;
+ alen = ll_addr_a2n((unsigned char *)(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) {
+ bb_perror_msg(brd ? "SIOCSIFHWBROADCAST" : "SIOCSIFHWADDR");
+ close(s);
+ return -1;
+ }
+ close(s);
+ return 0;
+}
+
+
+static int do_set(int argc, char **argv)
+{
+ char *dev = NULL;
+ uint32_t mask = 0;
+ uint32_t 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(*argv, "mtu");
+ } 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, "addr") == 0) {
+ NEXT_ARG();
+ newaddr = *argv;
+ } else {
+ if (strcmp(*argv, "dev") == 0) {
+ NEXT_ARG();
+ }
+ if (dev)
+ duparg2("dev", *argv);
+ dev = *argv;
+ }
+ argc--; argv++;
+ }
+
+ if (!dev) {
+ bb_error_msg(bb_msg_requires_arg, "\"dev\"");
+ 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;
+ 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", *argv);
+ exit(-1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/iproute.c b/i/pc104/initrd/conf/busybox/networking/libiproute/iproute.c
new file mode 100644
index 0000000..27d37ac
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/iproute.c
@@ -0,0 +1,878 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iproute.c "ip route".
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * 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 "libbb.h"
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.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 unsigned get_hz(void)
+{
+ static unsigned hz_internal;
+ FILE *fp;
+
+ if (hz_internal)
+ return hz_internal;
+
+ 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_internal = denom;
+ fclose(fp);
+ }
+ if (!hz_internal)
+ hz_internal = sysconf(_SC_CLK_TCK);
+ return hz_internal;
+}
+
+static int print_route(struct sockaddr_nl *who ATTRIBUTE_UNUSED,
+ 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 ", *(uint32_t*)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)) {
+ if (r->rtm_flags & RTM_F_CLONED) {
+ fprintf(fp, "%s cache ", _SL_);
+ }
+ if (ci->rta_expires) {
+ fprintf(fp, " expires %dsec", ci->rta_expires / get_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(*argv, "mtu");
+ }
+ rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
+ } else if (matches(*argv, "protocol") == 0) {
+ uint32_t prot;
+ NEXT_ARG();
+ if (rtnl_rtprot_a2n(&prot, *argv))
+ invarg(*argv, "protocol");
+ req.r.rtm_protocol = prot;
+ proto_ok =1;
+#if ENABLE_FEATURE_IP_RULE
+ } else if (matches(*argv, "table") == 0) {
+ uint32_t tid;
+ NEXT_ARG();
+ if (rtnl_rttable_a2n(&tid, *argv))
+ invarg(*argv, "table");
+ req.r.rtm_table = tid;
+#endif
+ } 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) {
+ idx = ll_name_to_index(d);
+ if (idx == 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)
+{
+ static const char fn[] = "/proc/sys/net/ipv4/route/flush";
+ int flush_fd = open(fn, O_WRONLY);
+ if (flush_fd < 0) {
+ bb_perror_msg("cannot open '%s'", fn);
+ return -1;
+ }
+
+ if (write(flush_fd, "-1", 2) < 2) {
+ bb_perror_msg("cannot flush routing cache");
+ 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) {
+ bb_error_msg(bb_msg_requires_arg, "\"ip route flush\"");
+ return -1;
+ }
+
+ while (argc > 0) {
+ if (matches(*argv, "protocol") == 0) {
+ uint32_t prot = 0;
+ NEXT_ARG();
+ filter.protocolmask = -1;
+ if (rtnl_rtprot_a2n(&prot, *argv)) {
+ if (strcmp(*argv, "all") != 0) {
+ invarg(*argv, "protocol");
+ }
+ 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, "table") == 0) {
+ NEXT_ARG();
+ if (matches(*argv, "cache") == 0) {
+ filter.tb = -1;
+#if 0 && ENABLE_FEATURE_IP_RULE
+
+#else
+ } else if (matches(*argv, "main") != 0) {
+ invarg(*argv, "table");
+ }
+#endif
+ } else if (matches(*argv, "cache") == 0) {
+ filter.tb = -1;
+ } 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) {
+ 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");
+ return -1;
+ }
+ if (filter.flushed == 0) {
+ fflush(stdout);
+ return 0;
+ }
+ 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;
+ static const char * const 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 (index_in_str_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)
+{
+ static const char * const ip_route_commands[] = {
+ /*0-3*/ "add", "append", "change", "chg",
+ /*4-7*/ "delete", "get", "list", "show",
+ /*8..*/ "prepend", "replace", "test", "flush", 0
+ };
+ int command_num = 6;
+ unsigned int flags = 0;
+ int cmd = RTM_NEWROUTE;
+
+ /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
+ /* It probably means that it is using "first match" rule */
+ if (*argv) {
+ command_num = index_in_substr_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/i/pc104/initrd/conf/busybox/networking/libiproute/iprule.c b/i/pc104/initrd/conf/busybox/networking/libiproute/iprule.c
new file mode 100644
index 0000000..4b31303
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/iprule.c
@@ -0,0 +1,334 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iprule.c "ip rule".
+ *
+ * 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
+ * initially integrated into busybox by Bernhard Fischer
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+/*
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: ip rule [ list | add | del ] SELECTOR ACTION\n");
+ fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n");
+ fprintf(stderr, " [ dev STRING ] [ pref NUMBER ]\n");
+ fprintf(stderr, "ACTION := [ table TABLE_ID ] [ nat ADDRESS ]\n");
+ fprintf(stderr, " [ prohibit | reject | unreachable ]\n");
+ fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n");
+ fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n");
+ exit(-1);
+}
+*/
+static int print_rule(struct sockaddr_nl *who ATTRIBUTE_UNUSED,
+ struct nlmsghdr *n, void *arg)
+{
+ FILE *fp = (FILE*)arg;
+ struct rtmsg *r = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ int host_len = -1;
+ struct rtattr * tb[RTA_MAX+1];
+ char abuf[256];
+ SPRINT_BUF(b1);
+
+ if (n->nlmsg_type != RTM_NEWRULE)
+ return 0;
+
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0)
+ return -1;
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+ if (r->rtm_family == AF_INET)
+ host_len = 32;
+ else if (r->rtm_family == AF_INET6)
+ host_len = 128;
+/* else if (r->rtm_family == AF_DECnet)
+ host_len = 16;
+ else if (r->rtm_family == AF_IPX)
+ host_len = 80;
+*/
+ if (tb[RTA_PRIORITY])
+ fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[RTA_PRIORITY]));
+ else
+ fprintf(fp, "0:\t");
+
+ fprintf(fp, "from ");
+ if (tb[RTA_SRC]) {
+ if (r->rtm_src_len != host_len) {
+ fprintf(fp, "%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, "%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, "0/%d", r->rtm_src_len);
+ } else {
+ fprintf(fp, "all");
+ }
+ fprintf(fp, " ");
+
+ if (tb[RTA_DST]) {
+ if (r->rtm_dst_len != host_len) {
+ fprintf(fp, "to %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, "to %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, "to 0/%d ", r->rtm_dst_len);
+ }
+
+ if (r->rtm_tos) {
+ fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
+ }
+ if (tb[RTA_PROTOINFO]) {
+ fprintf(fp, "fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
+ }
+
+ if (tb[RTA_IIF]) {
+ fprintf(fp, "iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
+ }
+
+ if (r->rtm_table)
+ fprintf(fp, "lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1)));
+
+ if (tb[RTA_FLOW]) {
+ uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
+ uint32_t from = to>>16;
+ to &= 0xFFFF;
+ if (from) {
+ fprintf(fp, "realms %s/",
+ rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+ }
+ fprintf(fp, "%s ",
+ rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+ }
+
+ if (r->rtm_type == RTN_NAT) {
+ if (tb[RTA_GATEWAY]) {
+ fprintf(fp, "map-to %s ",
+ format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_GATEWAY]),
+ RTA_DATA(tb[RTA_GATEWAY]),
+ abuf, sizeof(abuf)));
+ } else
+ fprintf(fp, "masquerade");
+ } else if (r->rtm_type != RTN_UNICAST)
+ fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+
+ fprintf(fp, "\n");
+ fflush(fp);
+ return 0;
+}
+
+static int iprule_list(int argc, char **argv)
+{
+ struct rtnl_handle rth;
+ int af = preferred_family;
+
+ if (af == AF_UNSPEC)
+ af = AF_INET;
+
+ if (argc > 0) {
+ //bb_error_msg("\"rule show\" needs no arguments");
+ bb_warn_ignoring_args(argc);
+ return -1;
+ }
+
+ if (rtnl_open(&rth, 0) < 0)
+ return 1;
+
+ if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) {
+ bb_perror_msg("Cannot send dump request");
+ return 1;
+ }
+
+ if (rtnl_dump_filter(&rth, print_rule, stdout, NULL, NULL) < 0) {
+ bb_error_msg("Dump terminated");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int iprule_modify(int cmd, int argc, char **argv)
+{
+ int table_ok = 0;
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_type = cmd;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.r.rtm_family = preferred_family;
+ req.r.rtm_protocol = RTPROT_BOOT;
+ req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+ req.r.rtm_table = 0;
+ req.r.rtm_type = RTN_UNSPEC;
+
+ if (cmd == RTM_NEWRULE) {
+ req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+ req.r.rtm_type = RTN_UNICAST;
+ }
+
+ while (argc > 0) {
+ if (strcmp(*argv, "from") == 0) {
+ inet_prefix dst;
+ NEXT_ARG();
+ get_prefix(&dst, *argv, req.r.rtm_family);
+ req.r.rtm_src_len = dst.bitlen;
+ addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen);
+ } else if (strcmp(*argv, "to") == 0) {
+ inet_prefix dst;
+ NEXT_ARG();
+ get_prefix(&dst, *argv, req.r.rtm_family);
+ req.r.rtm_dst_len = dst.bitlen;
+ addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
+ } else if (matches(*argv, "preference") == 0 ||
+ matches(*argv, "order") == 0 ||
+ matches(*argv, "priority") == 0) {
+ uint32_t pref;
+ NEXT_ARG();
+ if (get_u32(&pref, *argv, 0))
+ invarg("preference value", *argv);
+ addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref);
+ } else if (strcmp(*argv, "tos") == 0) {
+ uint32_t tos;
+ NEXT_ARG();
+ if (rtnl_dsfield_a2n(&tos, *argv))
+ invarg("TOS value", *argv);
+ req.r.rtm_tos = tos;
+ } else if (strcmp(*argv, "fwmark") == 0) {
+ uint32_t fwmark;
+ NEXT_ARG();
+ if (get_u32(&fwmark, *argv, 0))
+ invarg("fwmark value", *argv);
+ addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark);
+ } else if (matches(*argv, "realms") == 0) {
+ uint32_t realm;
+ NEXT_ARG();
+ if (get_rt_realms(&realm, *argv))
+ invarg("realms", *argv);
+ addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
+ } else if (matches(*argv, "table") == 0 ||
+ strcmp(*argv, "lookup") == 0) {
+ uint32_t tid;
+ NEXT_ARG();
+ if (rtnl_rttable_a2n(&tid, *argv))
+ invarg("table ID", *argv);
+ req.r.rtm_table = tid;
+ table_ok = 1;
+ } else if (strcmp(*argv, "dev") == 0 ||
+ strcmp(*argv, "iif") == 0) {
+ NEXT_ARG();
+ addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1);
+ } else if (strcmp(*argv, "nat") == 0 ||
+ matches(*argv, "map-to") == 0) {
+ NEXT_ARG();
+ addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
+ req.r.rtm_type = RTN_NAT;
+ } else {
+ int type;
+
+ if (strcmp(*argv, "type") == 0) {
+ NEXT_ARG();
+ }
+ if (matches(*argv, "help") == 0)
+ bb_show_usage();
+ if (rtnl_rtntype_a2n(&type, *argv))
+ invarg("Failed to parse rule type", *argv);
+ req.r.rtm_type = type;
+ }
+ argc--;
+ argv++;
+ }
+
+ if (req.r.rtm_family == AF_UNSPEC)
+ req.r.rtm_family = AF_INET;
+
+ if (!table_ok && cmd == RTM_NEWRULE)
+ req.r.rtm_table = RT_TABLE_MAIN;
+
+ if (rtnl_open(&rth, 0) < 0)
+ return 1;
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ return 2;
+
+ return 0;
+}
+
+int do_iprule(int argc, char **argv)
+{
+ static const char * const ip_rule_commands[] =
+ {"add", "delete", "list", "show", 0};
+ int cmd = 2; /* list */
+
+ if (argc < 1)
+ return iprule_list(0, NULL);
+ if (*argv)
+ cmd = index_in_substr_array(ip_rule_commands, *argv);
+
+ switch (cmd) {
+ case 0: /* add */
+ cmd = RTM_NEWRULE;
+ break;
+ case 1: /* delete */
+ cmd = RTM_DELRULE;
+ break;
+ case 2: /* list */
+ case 3: /* show */
+ return iprule_list(argc-1, argv+1);
+ break;
+ default:
+ bb_error_msg_and_die("unknown command %s", *argv);
+ }
+ return iprule_modify(cmd, argc-1, argv+1);
+}
+
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/iptunnel.c b/i/pc104/initrd/conf/busybox/networking/libiproute/iptunnel.c
new file mode 100644
index 0000000..e2e75fc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/iptunnel.c
@@ -0,0 +1,547 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iptunnel.c "ip tunnel"
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * 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 "libbb.h"
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/ip.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include <asm/types.h>
+#ifndef __constant_htons
+#define __constant_htons htons
+#endif
+#include <linux/if_tunnel.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+
+static int do_ioctl_get_ifindex(char *dev)
+{
+ struct ifreq ifr;
+ int fd;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ fd = xsocket(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;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ fd = xsocket(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 = xsocket(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(const char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = xsocket(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, const char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ if (cmd == SIOCCHGTUNNEL && p->name[0]) {
+ strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name));
+ } else {
+ strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
+ }
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = xsocket(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(const char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ if (p->name[0]) {
+ strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name));
+ } else {
+ strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
+ }
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = xsocket(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(*argv, "TTL");
+ if (uval > 255)
+ invarg(*argv, "TTL must be <=255");
+ p->iph.ttl = uval;
+ }
+ } else if (strcmp(*argv, "tos") == 0 ||
+ matches(*argv, "dsfield") == 0) {
+ uint32_t uval;
+ NEXT_ARG();
+ if (strcmp(*argv, "inherit") != 0) {
+ if (rtnl_dsfield_a2n(&uval, *argv))
+ invarg(*argv, "TOS");
+ 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;
+}
+
+static 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;
+}
+
+static 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; - fgets is safe anyway */
+ ptr = strchr(buf, ':');
+ if (ptr == NULL ||
+ (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
+ bb_error_msg("wrong format of /proc/net/dev. Sorry");
+ return -1;
+ }
+ if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
+ &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);
+ puts("");
+ }
+ 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);
+ puts("");
+ 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_and_die("command \"%s\" is unknown", *argv);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.c b/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.c
new file mode 100644
index 0000000..9696480
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.c
@@ -0,0 +1,396 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 "libbb.h"
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/uio.h>
+
+#include "libnetlink.h"
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+ close(rth->fd);
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ socklen_t 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 (nladdr.nl_pid != 0 ||
+ 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 (nladdr.nl_pid != peer ||
+ 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 addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t 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, uint32_t 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/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.h b/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.h
new file mode 100644
index 0000000..9a5a9d3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.h
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <linux/types.h>
+/* We need linux/types.h because older kernels use __u32 etc
+ * in linux/[rt]netlink.h. 2.6.19 seems to be ok, though */
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ uint32_t seq;
+ uint32_t 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, uint32_t 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, uint32_t 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);
+
+#endif /* __LIBNETLINK_H__ */
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ll_addr.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_addr.c
new file mode 100644
index 0000000..b4a2187
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_addr.c
@@ -0,0 +1,85 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 "libbb.h"
+
+#include <string.h>
+#include <net/if_arp.h>
+
+#include "rt_names.h"
+#include "utils.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"+1, 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/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.c
new file mode 100644
index 0000000..f429763
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.c
@@ -0,0 +1,186 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 "libbb.h"
+#include <string.h>
+
+#include "libnetlink.h"
+#include "ll_map.h"
+
+#include <sys/socket.h> /* socket() */
+#include <net/if.h> /* struct ifreq and co. */
+#include <sys/ioctl.h> /* ioctl() & SIOCGIFINDEX */
+
+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 = xmalloc(sizeof(*im));
+ 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 sock_fd;
+ 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;
+ }
+ }
+ }
+ /* We have not found the interface in our cache, but the kernel
+ * may still know about it. One reason is that we may be using
+ * module on-demand loading, which means that the kernel will
+ * load the module and make the interface exist only when
+ * we explicitely request it (check for dev_load() in net/core/dev.c).
+ * I can think of other similar scenario, but they are less common...
+ * Jean II */
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd) {
+ struct ifreq ifr;
+ int ret;
+ strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ ifr.ifr_ifindex = -1;
+ ret = ioctl(sock_fd, SIOCGIFINDEX, &ifr);
+ close(sock_fd);
+ if (ret >= 0)
+ /* In theory, we should redump the interface list
+ * to update our cache, this is left as an exercise
+ * to the reader... Jean II */
+ return ifr.ifr_ifindex;
+ }
+
+ return 0;
+}
+
+int ll_init_map(struct rtnl_handle *rth)
+{
+ if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
+ bb_perror_msg_and_die("cannot send dump request");
+ }
+
+ if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) {
+ bb_error_msg_and_die("dump terminated");
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.h b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.h
new file mode 100644
index 0000000..226d48f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.h
@@ -0,0 +1,13 @@
+/* vi: set sw=4 ts=4: */
+#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/i/pc104/initrd/conf/busybox/networking/libiproute/ll_proto.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_proto.c
new file mode 100644
index 0000000..aad460b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_proto.c
@@ -0,0 +1,121 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 "libbb.h"
+
+#include "rt_names.h"
+#include "utils.h"
+
+#if defined(__GLIBC__) && __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;
+ const 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
+
+
+const 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/i/pc104/initrd/conf/busybox/networking/libiproute/ll_types.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_types.c
new file mode 100644
index 0000000..416ea6b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_types.c
@@ -0,0 +1,117 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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>
+
+#include "rt_names.h"
+
+const char* ll_type_n2a(int type, char *buf, int len)
+{
+#define __PF(f,n) { ARPHRD_##f, #n },
+static const struct {
+ int type;
+ const 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/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.c b/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.c
new file mode 100644
index 0000000..797c83b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.c
@@ -0,0 +1,365 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 "libbb.h"
+#include "rt_names.h"
+
+static void rtnl_tab_initialize(const char *file, const 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
+ ) {
+ bb_error_msg("database %s is corrupted at %s",
+ file, p);
+ return;
+ }
+
+ if (id < 0 || id > size)
+ continue;
+
+ tab[id] = xstrdup(namebuf);
+ }
+ fclose(fp);
+}
+
+
+static const char **rtnl_rtprot_tab; /* [256] */
+
+static void rtnl_rtprot_initialize(void)
+{
+ static const char *const init_tab[] = {
+ "none",
+ "redirect",
+ "kernel",
+ "boot",
+ "static",
+ NULL,
+ NULL,
+ NULL,
+ "gated",
+ "ra",
+ "mrt",
+ "zebra",
+ "bird",
+ };
+ if (rtnl_rtprot_tab) return;
+ rtnl_rtprot_tab = xzalloc(256 * sizeof(rtnl_rtprot_tab[0]));
+ memcpy(rtnl_rtprot_tab, init_tab, sizeof(init_tab));
+ rtnl_tab_initialize("/etc/iproute2/rt_protos",
+ rtnl_rtprot_tab, 256);
+}
+
+
+const char* rtnl_rtprot_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ 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 const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ 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 = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+static const char **rtnl_rtscope_tab; /* [256] */
+
+static void rtnl_rtscope_initialize(void)
+{
+ if (rtnl_rtscope_tab) return;
+ rtnl_rtscope_tab = xzalloc(256 * sizeof(rtnl_rtscope_tab[0]));
+ rtnl_rtscope_tab[0] = "global";
+ 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);
+}
+
+
+const char* rtnl_rtscope_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ 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 const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ 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 = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+static const char **rtnl_rtrealm_tab; /* [256] */
+
+static void rtnl_rtrealm_initialize(void)
+{
+ if (rtnl_rtrealm_tab) return;
+ rtnl_rtrealm_tab = xzalloc(256 * sizeof(rtnl_rtrealm_tab[0]));
+ rtnl_rtrealm_tab[0] = "unknown";
+ rtnl_tab_initialize("/etc/iproute2/rt_realms",
+ rtnl_rtrealm_tab, 256);
+}
+
+
+int rtnl_rtrealm_a2n(uint32_t *id, char *arg)
+{
+ static const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ 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 = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+#if ENABLE_FEATURE_IP_RULE
+const char* rtnl_rtrealm_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rtrealm_initialize();
+
+ if (rtnl_rtrealm_tab[id])
+ return rtnl_rtrealm_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+#endif
+
+
+static const char **rtnl_rtdsfield_tab; /* [256] */
+
+static void rtnl_rtdsfield_initialize(void)
+{
+ if (rtnl_rtdsfield_tab) return;
+ rtnl_rtdsfield_tab = xzalloc(256 * sizeof(rtnl_rtdsfield_tab[0]));
+ rtnl_rtdsfield_tab[0] = "0";
+ rtnl_tab_initialize("/etc/iproute2/rt_dsfield",
+ rtnl_rtdsfield_tab, 256);
+}
+
+
+const char * rtnl_dsfield_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ 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 const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ 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 = bb_strtoul(arg, NULL, 16);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+#if ENABLE_FEATURE_IP_RULE
+static const char **rtnl_rttable_tab; /* [256] */
+
+static void rtnl_rttable_initialize(void)
+{
+ if (rtnl_rtdsfield_tab) return;
+ rtnl_rttable_tab = xzalloc(256 * sizeof(rtnl_rttable_tab[0]));
+ rtnl_rttable_tab[0] = "unspec";
+ rtnl_rttable_tab[255] = "local";
+ rtnl_rttable_tab[254] = "main";
+ rtnl_rttable_tab[253] = "default";
+ rtnl_tab_initialize("/etc/iproute2/rt_tables", rtnl_rttable_tab, 256);
+}
+
+
+const char *rtnl_rttable_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ 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;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ rtnl_rttable_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rttable_tab[i] && strcmp(rtnl_rttable_tab[i], arg) == 0) {
+ cache = (char*)rtnl_rttable_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ i = bb_strtoul(arg, NULL, 0);
+ if (errno || i > 255)
+ return -1;
+ *id = i;
+ return 0;
+}
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.h b/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.h
new file mode 100644
index 0000000..98c22b0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.h
@@ -0,0 +1,28 @@
+/* vi: set sw=4 ts=4: */
+#ifndef RT_NAMES_H_
+#define RT_NAMES_H_ 1
+
+#include <stdint.h>
+
+extern const char* rtnl_rtprot_n2a(int id, char *buf, int len);
+extern const char* rtnl_rtscope_n2a(int id, char *buf, int len);
+extern const char* rtnl_rtrealm_n2a(int id, char *buf, int len);
+extern const char* rtnl_dsfield_n2a(int id, char *buf, int len);
+extern const char* rtnl_rttable_n2a(int id, char *buf, int len);
+extern int rtnl_rtprot_a2n(uint32_t *id, char *arg);
+extern int rtnl_rtscope_a2n(uint32_t *id, char *arg);
+extern int rtnl_rtrealm_a2n(uint32_t *id, char *arg);
+extern int rtnl_dsfield_a2n(uint32_t *id, char *arg);
+extern int rtnl_rttable_a2n(uint32_t *id, char *arg);
+
+
+extern const char* ll_type_n2a(int type, char *buf, int len);
+
+extern const char* ll_addr_n2a(unsigned char *addr, int alen, int type,
+ char *buf, int blen);
+extern int ll_addr_a2n(unsigned char *lladdr, int len, char *arg);
+
+extern const char* ll_proto_n2a(unsigned short id, char *buf, int len);
+extern int ll_proto_a2n(unsigned short *id, char *buf);
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.c b/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.c
new file mode 100644
index 0000000..c16406b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.c
@@ -0,0 +1,110 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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"
+
+const 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(uint32_t *realms, char *arg)
+{
+ uint32_t 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/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.h b/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.h
new file mode 100644
index 0000000..cbbcc21
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.h
@@ -0,0 +1,11 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __RTM_MAP_H__
+#define __RTM_MAP_H__ 1
+
+const char *rtnl_rtntype_n2a(int id, char *buf, int len);
+int rtnl_rtntype_a2n(int *id, char *arg);
+
+int get_rt_realms(uint32_t *realms, char *arg);
+
+
+#endif /* __RTM_MAP_H__ */
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/utils.c b/i/pc104/initrd/conf/busybox/networking/libiproute/utils.c
new file mode 100644
index 0000000..591c893
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/utils.c
@@ -0,0 +1,330 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * utils.c
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "libbb.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include "utils.h"
+#include "inet_common.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(uint32_t * 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(uint16_t * 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(uint8_t * 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(int16_t * 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(int8_t * 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, bb_str_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;
+ int plen;
+ char *slash;
+
+ memset(dst, 0, sizeof(*dst));
+
+ if (strcmp(arg, bb_str_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;
+}
+
+uint32_t 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(void)
+{
+ bb_error_msg_and_die("command line is not complete, try option \"help\"");
+}
+
+void invarg(const char *arg, const char *opt)
+{
+ bb_error_msg_and_die(bb_msg_invalid_arg, arg, opt);
+}
+
+void duparg(const char *key, const char *arg)
+{
+ bb_error_msg_and_die("duplicate \"%s\": \"%s\" is the second value", key, arg);
+}
+
+void duparg2(const char *key, const char *arg)
+{
+ bb_error_msg_and_die("either \"%s\" is duplicate, or \"%s\" is garbage", key, arg);
+}
+
+int matches(const char *cmd, const char *pattern)
+{
+ int len = strlen(cmd);
+
+ return strncmp(pattern, cmd, len);
+}
+
+int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits)
+{
+ uint32_t *a1 = a->data;
+ uint32_t *a2 = b->data;
+ int words = bits >> 0x05;
+
+ bits &= 0x1f;
+
+ if (words)
+ if (memcmp(a1, a2, words << 2))
+ return -1;
+
+ if (bits) {
+ uint32_t w1, w2;
+ uint32_t mask;
+
+ w1 = a1[words];
+ w2 = a2[words];
+
+ mask = htonl((0xffffffff) << (0x20 - bits));
+
+ if ((w1 ^ w2) & mask)
+ return 1;
+ }
+
+ return 0;
+}
+
+const char *rt_addr_n2a(int af, int ATTRIBUTE_UNUSED 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/i/pc104/initrd/conf/busybox/networking/libiproute/utils.h b/i/pc104/initrd/conf/busybox/networking/libiproute/utils.h
new file mode 100644
index 0000000..556541f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/utils.h
@@ -0,0 +1,91 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __UTILS_H__
+#define __UTILS_H__ 1
+
+#include "libbb.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;
+//FIXME! Appears in two .h files!
+extern const 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
+{
+ uint8_t family;
+ uint8_t bytelen;
+ int16_t bitlen;
+ uint32_t 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 {
+ uint32_t ipx_net;
+ uint8_t ipx_node[IPX_NODE_LEN];
+};
+
+extern uint32_t 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(uint32_t *val, char *arg, int base);
+extern int get_u16(uint16_t *val, char *arg, int base);
+extern int get_s16(int16_t *val, char *arg, int base);
+extern int get_u8(uint8_t *val, char *arg, int base);
+extern int get_s8(int8_t *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(const char *, const char *) ATTRIBUTE_NORETURN;
+void duparg(const char *, const char *) ATTRIBUTE_NORETURN;
+void duparg2(const char *, const char *) ATTRIBUTE_NORETURN;
+int matches(const char *arg, const 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);
+
+#endif /* __UTILS_H__ */
diff --git a/i/pc104/initrd/conf/busybox/networking/nameif.c b/i/pc104/initrd/conf/busybox/networking/nameif.c
new file mode 100644
index 0000000..ec77512
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/nameif.c
@@ -0,0 +1,171 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * nameif.c - Naming Interfaces based on MAC address for busybox.
+ *
+ * Written 2000 by Andi Kleen.
+ * Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua>
+ * Glenn McGrath <bug1@iinet.net.au>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <syslog.h>
+#include <net/if.h>
+#include <netinet/ether.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;
+
+/* Check ascii str_macaddr, convert and copy to *mac */
+static struct ether_addr *cc_macaddr(const char *str_macaddr)
+{
+ struct ether_addr *lmac, *mac;
+
+ lmac = ether_aton(str_macaddr);
+ if (lmac == NULL)
+ bb_error_msg_and_die("cannot parse MAC %s", str_macaddr);
+ mac = xmalloc(ETH_ALEN);
+ memcpy(mac, lmac, ETH_ALEN);
+
+ return mac;
+}
+
+int nameif_main(int argc, char **argv);
+int nameif_main(int argc, char **argv)
+{
+ mactable_t *clist = NULL;
+ FILE *ifh;
+ const char *fname = "/etc/mactab";
+ char *line;
+ int ctl_sk;
+ int if_index = 1;
+ mactable_t *ch;
+
+ if (1 & getopt32(argc, argv, "sc:", &fname)) {
+ openlog(applet_name, 0, LOG_LOCAL0);
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ if ((argc - optind) & 1)
+ bb_show_usage();
+
+ if (optind < argc) {
+ char **a = argv + optind;
+
+ while (*a) {
+ if (strlen(*a) > IF_NAMESIZE)
+ bb_error_msg_and_die("interface name '%s' "
+ "too long", *a);
+ ch = xzalloc(sizeof(mactable_t));
+ ch->ifname = xstrdup(*a++);
+ ch->mac = cc_macaddr(*a++);
+ if (clist)
+ clist->prev = ch;
+ ch->next = clist;
+ clist = ch;
+ }
+ } else {
+ ifh = xfopen(fname, "r");
+
+ while ((line = xmalloc_fgets(ifh)) != NULL) {
+ char *line_ptr;
+ size_t name_length;
+
+ line_ptr = line + strspn(line, " \t");
+ if ((line_ptr[0] == '#') || (line_ptr[0] == '\n')) {
+ free(line);
+ continue;
+ }
+ name_length = strcspn(line_ptr, " \t");
+ ch = xzalloc(sizeof(mactable_t));
+ ch->ifname = xstrndup(line_ptr, name_length);
+ if (name_length > IF_NAMESIZE)
+ bb_error_msg_and_die("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);
+ }
+
+ ctl_sk = xsocket(PF_INET, SOCK_DGRAM, 0);
+
+ while (clist) {
+ struct ifreq ifr;
+
+ memset(&ifr, 0, 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)
+ bb_perror_msg_and_die("cannot change ifname %s to %s",
+ 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;
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free(ch->ifname);
+ free(ch->mac);
+ free(ch);
+ }
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/nc.c b/i/pc104/initrd/conf/busybox/networking/nc.c
new file mode 100644
index 0000000..1bdecaf
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/nc.c
@@ -0,0 +1,197 @@
+/* vi: set sw=4 ts=4: */
+/* nc: mini-netcat - built from the ground up for LRP
+ *
+ * Copyright (C) 1998, 1999 Charles P. Wright
+ * Copyright (C) 1998 Dave Cinege
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+/* Lots of small differences in features
+ * when compared to "standard" nc
+ */
+
+static void timeout(int signum)
+{
+ bb_error_msg_and_die("timed out");
+}
+
+int nc_main(int argc, char **argv);
+int nc_main(int argc, char **argv)
+{
+ /* sfd sits _here_ only because of "repeat" option (-l -l). */
+ int sfd = sfd; /* for gcc */
+ int cfd = 0;
+ unsigned lport = 0;
+ SKIP_NC_SERVER(const) unsigned do_listen = 0;
+ SKIP_NC_EXTRA (const) unsigned wsecs = 0;
+ SKIP_NC_EXTRA (const) unsigned delay = 0;
+ SKIP_NC_EXTRA (const int execparam = 0;)
+ USE_NC_EXTRA (char **execparam = NULL;)
+ len_and_sockaddr *lsa;
+ fd_set readfds, testfds;
+ int opt; /* must be signed (getopt returns -1) */
+
+ if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) {
+ /* getopt32 is _almost_ usable:
+ ** it cannot handle "... -e prog -prog-opt" */
+ while ((opt = getopt(argc, argv,
+ "" USE_NC_SERVER("lp:") USE_NC_EXTRA("w:i:f:e:") )) > 0
+ ) {
+ if (ENABLE_NC_SERVER && opt=='l') USE_NC_SERVER(do_listen++);
+ else if (ENABLE_NC_SERVER && opt=='p') {
+ USE_NC_SERVER(lport = bb_lookup_port(optarg, "tcp", 0));
+ }
+ else if (ENABLE_NC_EXTRA && opt=='w') USE_NC_EXTRA( wsecs = xatou(optarg));
+ else if (ENABLE_NC_EXTRA && opt=='i') USE_NC_EXTRA( delay = xatou(optarg));
+ else if (ENABLE_NC_EXTRA && opt=='f') USE_NC_EXTRA( cfd = xopen(optarg, O_RDWR));
+ else if (ENABLE_NC_EXTRA && opt=='e' && optind<=argc) {
+ /* We cannot just 'break'. We should let getopt finish.
+ ** Or else we won't be able to find where
+ ** 'host' and 'port' params are
+ ** (think "nc -w 60 host port -e prog"). */
+ USE_NC_EXTRA(
+ char **p;
+ // +2: one for progname (optarg) and one for NULL
+ execparam = xzalloc(sizeof(char*) * (argc - optind + 2));
+ p = execparam;
+ *p++ = optarg;
+ while (optind < argc) {
+ *p++ = argv[optind++];
+ }
+ )
+ /* optind points to argv[arvc] (NULL) now.
+ ** FIXME: we assume that getopt will not count options
+ ** possibly present on "-e prog args" and will not
+ ** include them into final value of optind
+ ** which is to be used ... */
+ } else bb_show_usage();
+ }
+ argv += optind; /* ... here! */
+ argc -= optind;
+ // -l and -f don't mix
+ if (do_listen && cfd) bb_show_usage();
+ // Listen or file modes need zero arguments, client mode needs 2
+ if (do_listen || cfd) {
+ if (argc) bb_show_usage();
+ } else {
+ if (!argc || argc > 2) bb_show_usage();
+ }
+ } else {
+ if (argc != 3) bb_show_usage();
+ argc--;
+ argv++;
+ }
+
+ if (wsecs) {
+ signal(SIGALRM, timeout);
+ alarm(wsecs);
+ }
+
+ if (!cfd) {
+ if (do_listen) {
+ /* create_and_bind_stream_or_die(NULL, lport)
+ * would've work wonderfully, but we need
+ * to know lsa */
+ sfd = xsocket_stream(&lsa);
+ if (lport)
+ set_nport(lsa, htons(lport));
+ setsockopt_reuseaddr(sfd);
+ xbind(sfd, &lsa->sa, lsa->len);
+ xlisten(sfd, do_listen); /* can be > 1 */
+ /* If we didn't specify a port number,
+ * query and print it after listen() */
+ if (!lport) {
+ socklen_t addrlen = lsa->len;
+ getsockname(sfd, &lsa->sa, &addrlen);
+ lport = get_nport(lsa);
+ fdprintf(2, "%d\n", ntohs(lport));
+ }
+ fcntl(sfd, F_SETFD, FD_CLOEXEC);
+ accept_again:
+ cfd = accept(sfd, NULL, 0);
+ if (cfd < 0)
+ bb_perror_msg_and_die("accept");
+ if (!execparam)
+ close(sfd);
+ } else {
+ cfd = create_and_connect_stream_or_die(argv[0],
+ argv[1] ? bb_lookup_port(argv[1], "tcp", 0) : 0);
+ }
+ }
+
+ if (wsecs) {
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ }
+
+ /* -e given? */
+ if (execparam) {
+ signal(SIGCHLD, SIG_IGN);
+ // With more than one -l, repeatedly act as server.
+ if (do_listen > 1 && vfork()) {
+ /* parent */
+ // This is a bit weird as cleanup goes, since we wind up with no
+ // stdin/stdout/stderr. But it's small and shouldn't hurt anything.
+ // We check for cfd == 0 above.
+ logmode = LOGMODE_NONE;
+ close(0);
+ close(1);
+ close(2);
+ goto accept_again;
+ }
+ /* child (or main thread if no multiple -l) */
+ if (cfd) {
+ dup2(cfd, 0);
+ close(cfd);
+ }
+ dup2(0, 1);
+ dup2(0, 2);
+ USE_NC_EXTRA(BB_EXECVP(execparam[0], execparam);)
+ /* Don't print stuff or it will go over the wire.... */
+ _exit(127);
+ }
+
+ // Select loop copying stdin to cfd, and cfd to stdout.
+
+ FD_ZERO(&readfds);
+ FD_SET(cfd, &readfds);
+ FD_SET(STDIN_FILENO, &readfds);
+
+ for (;;) {
+ 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)) {
+ nread = safe_read(fd, bb_common_bufsiz1,
+ sizeof(bb_common_bufsiz1));
+
+ if (fd == cfd) {
+ if (nread < 1)
+ exit(0);
+ ofd = STDOUT_FILENO;
+ } else {
+ if (nread<1) {
+ // Close outgoing half-connection so they get EOF, but
+ // leave incoming alone so we can see response.
+ shutdown(cfd, 1);
+ FD_CLR(STDIN_FILENO, &readfds);
+ }
+ ofd = cfd;
+ }
+
+ xwrite(ofd, bb_common_bufsiz1, nread);
+ if (delay > 0) sleep(delay);
+ }
+ }
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/netstat.c b/i/pc104/initrd/conf/busybox/networking/netstat.c
new file mode 100644
index 0000000..d89d3aa
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/netstat.c
@@ -0,0 +1,596 @@
+/* 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>
+ *
+ * 2002-04-20
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include "inet_common.h"
+
+#define NETSTAT_CONNECTED 0x01
+#define NETSTAT_LISTENING 0x02
+#define NETSTAT_NUMERIC 0x04
+/* Must match getopt32 option string */
+#define NETSTAT_TCP 0x10
+#define NETSTAT_UDP 0x20
+#define NETSTAT_RAW 0x40
+#define NETSTAT_UNIX 0x80
+#define NETSTAT_ALLPROTO (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX)
+
+static int flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO;
+
+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 const char *get_sname(int port, const char *proto, int num)
+{
+ /* hummm, we return static buffer here!! */
+ const char *str = itoa(ntohs(port));
+ if (!num) {
+ 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, const char *proto, int numeric)
+{
+ const char *port_name;
+
+#if ENABLE_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;
+#if ENABLE_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) {
+#if ENABLE_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];
+ const char *state_str;
+ char more[512];
+ int num, local_port, rem_port, d, state, timer_run, uid, timeout;
+#if ENABLE_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) {
+#if ENABLE_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;
+ }
+
+#if ENABLE_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;
+#if ENABLE_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) {
+#if ENABLE_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);
+
+#if ENABLE_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];
+ const 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 *))
+{
+ int lnr = 0;
+ FILE *procinfo;
+
+ procinfo = fopen(file, "r");
+ if (procinfo == NULL) {
+ if (errno != ENOENT) {
+ bb_perror_msg("%s", file);
+ } else {
+ bb_error_msg("no support for '%s' on this system", name);
+ }
+ return;
+ }
+ do {
+ char *buffer = xmalloc_fgets(procinfo);
+ if (buffer) {
+ (proc)(lnr++, buffer);
+ free(buffer);
+ }
+ } while (!feof(procinfo));
+ fclose(procinfo);
+}
+
+/*
+ * Our main function.
+ */
+
+int netstat_main(int argc, char **argv);
+int netstat_main(int argc, char **argv)
+{
+ enum {
+ OPT_extended = 0x4,
+ OPT_showroute = 0x100,
+ };
+ unsigned opt;
+#if ENABLE_FEATURE_IPV6
+ int inet = 1;
+ int inet6 = 1;
+#else
+ enum { inet = 1, inet6 = 0 };
+#endif
+
+ /* Option string must match NETSTAT_xxx constants */
+ opt = getopt32(argc, argv, "laentuwxr");
+ if (opt & 0x1) { // -l
+ flags &= ~NETSTAT_CONNECTED;
+ flags |= NETSTAT_LISTENING;
+ }
+ if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a
+ //if (opt & 0x4) // -e
+ if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n
+ //if (opt & 0x10) // -t: NETSTAT_TCP
+ //if (opt & 0x20) // -u: NETSTAT_UDP
+ //if (opt & 0x40) // -w: NETSTAT_RAW
+ //if (opt & 0x80) // -x: NETSTAT_UNIX
+ if (opt & OPT_showroute) { // -r
+#if ENABLE_ROUTE
+ bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended));
+ return 0;
+#else
+ bb_error_msg_and_die("-r (display routing table) is not compiled in");
+#endif
+ }
+
+ opt &= NETSTAT_ALLPROTO;
+ if (opt) {
+ flags &= ~NETSTAT_ALLPROTO;
+ flags |= opt;
+ }
+ 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);
+#if ENABLE_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);
+#if ENABLE_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);
+#if ENABLE_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/i/pc104/initrd/conf/busybox/networking/nslookup.c b/i/pc104/initrd/conf/busybox/networking/nslookup.c
new file mode 100644
index 0000000..8076aff
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/nslookup.c
@@ -0,0 +1,155 @@
+/* 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
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <resolv.h>
+#include "busybox.h"
+
+/*
+ * I'm only implementing non-interactive mode;
+ * I totally forgot nslookup even had an interactive mode.
+ */
+
+/* Examples of 'standard' nslookup output
+ * $ nslookup yahoo.com
+ * Server: 128.193.0.10
+ * Address: 128.193.0.10#53
+ *
+ * Non-authoritative answer:
+ * Name: yahoo.com
+ * Address: 216.109.112.135
+ * Name: yahoo.com
+ * Address: 66.94.234.13
+ *
+ * $ nslookup 204.152.191.37
+ * Server: 128.193.4.20
+ * Address: 128.193.4.20#53
+ *
+ * Non-authoritative answer:
+ * 37.191.152.204.in-addr.arpa canonical name = 37.32-27.191.152.204.in-addr.arpa.
+ * 37.32-27.191.152.204.in-addr.arpa name = zeus-pub2.kernel.org.
+ *
+ * Authoritative answers can be found from:
+ * 32-27.191.152.204.in-addr.arpa nameserver = ns1.kernel.org.
+ * 32-27.191.152.204.in-addr.arpa nameserver = ns2.kernel.org.
+ * 32-27.191.152.204.in-addr.arpa nameserver = ns3.kernel.org.
+ * ns1.kernel.org internet address = 140.211.167.34
+ * ns2.kernel.org internet address = 204.152.191.4
+ * ns3.kernel.org internet address = 204.152.191.36
+ */
+
+static int print_host(const char *hostname, const char *header)
+{
+ /* We can't use xhost2sockaddr() - we want to get ALL addresses,
+ * not just one */
+
+ struct addrinfo *result = NULL;
+ int rc;
+ struct addrinfo hint;
+
+ memset(&hint, 0 , sizeof(hint));
+ /* hint.ai_family = AF_UNSPEC; - zero anyway */
+ /* Needed. Or else we will get each address thrice (or more)
+ * for each possible socket type (tcp,udp,raw...): */
+ hint.ai_socktype = SOCK_STREAM;
+ // hint.ai_flags = AI_CANONNAME;
+ rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result);
+
+ if (!rc) {
+ struct addrinfo *cur = result;
+ unsigned cnt = 0;
+
+ printf("%-10s %s\n", header, hostname);
+ // printf("%s\n", cur->ai_canonname); ?
+ while (cur) {
+ char *dotted, *revhost;
+ dotted = xmalloc_sockaddr2dotted_noport(cur->ai_addr, cur->ai_addrlen);
+ revhost = xmalloc_sockaddr2hostonly_noport(cur->ai_addr, cur->ai_addrlen);
+
+ printf("Address %u: %s%c", ++cnt, dotted, revhost ? ' ' : '\n');
+ if (revhost) {
+ puts(revhost);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(revhost);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(dotted);
+ cur = cur->ai_next;
+ }
+ } else {
+#if ENABLE_VERBOSE_RESOLUTION_ERRORS
+ bb_error_msg("can't resolve '%s': %s", hostname, gai_strerror(rc));
+#else
+ bb_error_msg("can't resolve '%s'", hostname);
+#endif
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ freeaddrinfo(result);
+ return (rc != 0);
+}
+
+/* lookup the default nameserver and display it */
+static void server_print(void)
+{
+ char *server;
+
+ server = xmalloc_sockaddr2dotted_noport((struct sockaddr*)&_res.nsaddr_list[0],
+ sizeof(struct sockaddr_in));
+ /* I honestly don't know what to do if DNS server has _IPv6 address_.
+ * Probably it is listed in
+ * _res._u._ext_.nsaddrs[MAXNS] (of type "struct sockaddr_in6*" each)
+ * but how to find out whether resolver uses
+ * _res.nsaddr_list[] or _res._u._ext_.nsaddrs[], or both?
+ * Looks like classic design from hell, BIND-grade. Hard to surpass. */
+ print_host(server, "Server:");
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(server);
+ puts("");
+}
+
+/* alter the global _res nameserver structure to use
+ an explicit dns server instead of what is in /etc/resolv.h */
+static void set_default_dns(char *server)
+{
+ struct in_addr server_in_addr;
+
+ if (inet_pton(AF_INET, server, &server_in_addr) > 0) {
+ _res.nscount = 1;
+ _res.nsaddr_list[0].sin_addr = server_in_addr;
+ }
+}
+
+int nslookup_main(int argc, char **argv);
+int nslookup_main(int argc, char **argv)
+{
+ /* 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();
+
+ /* initialize DNS structure _res used in printing the default
+ * name server and in the explicit name server option feature. */
+ res_init();
+ /* rfc2133 says this enables IPv6 lookups */
+ /* (but it also says "may be enabled in /etc/resolv.conf|) */
+ /*_res.options |= RES_USE_INET6;*/
+
+ if(argc == 3)
+ set_default_dns(argv[2]);
+
+ server_print();
+ return print_host(argv[1], "Name:");
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ping.c b/i/pc104/initrd/conf/busybox/networking/ping.c
new file mode 100644
index 0000000..bbe2c9f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ping.c
@@ -0,0 +1,752 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ping implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * Adapted from the ping in netkit-base 0.10:
+ * 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.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+/* from ping6.c:
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * This version of ping is adapted from the ping in netkit-base 0.10,
+ * which is:
+ *
+ * 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 <net/if.h>
+#include <netinet/ip_icmp.h>
+#include "busybox.h"
+
+#if ENABLE_PING6
+#include <netinet/icmp6.h>
+/* I see RENUMBERED constants in bits/in.h - !!?
+ * What a fuck is going on with libc? Is it a glibc joke? */
+#ifdef IPV6_2292HOPLIMIT
+#undef IPV6_HOPLIMIT
+#define IPV6_HOPLIMIT IPV6_2292HOPLIMIT
+#endif
+#endif
+
+enum {
+ DEFDATALEN = 56,
+ MAXIPLEN = 60,
+ MAXICMPLEN = 76,
+ MAXPACKET = 65468,
+ MAX_DUP_CHK = (8 * 128),
+ MAXWAIT = 10,
+ PINGINTERVAL = 1, /* 1 second */
+};
+
+/* 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;
+}
+
+#if !ENABLE_FEATURE_FANCY_PING
+
+/* simple version */
+
+static char *hostname;
+
+static void noresp(int ign ATTRIBUTE_UNUSED)
+{
+ printf("No response from %s\n", hostname);
+ exit(EXIT_FAILURE);
+}
+
+static void ping4(len_and_sockaddr *lsa)
+{
+ struct sockaddr_in pingaddr;
+ struct icmp *pkt;
+ int pingsock, c;
+ char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+
+ pingsock = create_icmp_socket();
+ pingaddr = lsa->sin;
+
+ 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, DEFDATALEN + ICMP_MINLEN, 0,
+ (struct sockaddr *) &pingaddr, sizeof(pingaddr));
+
+ if (c < 0) {
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(pingsock);
+ bb_perror_msg_and_die("sendto");
+ }
+
+ /* listen for replies */
+ while (1) {
+ struct sockaddr_in from;
+ socklen_t fromlen = sizeof(from);
+
+ c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (c < 0) {
+ if (errno != EINTR)
+ 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;
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(pingsock);
+}
+
+#if ENABLE_PING6
+static void ping6(len_and_sockaddr *lsa)
+{
+ struct sockaddr_in6 pingaddr;
+ struct icmp6_hdr *pkt;
+ int pingsock, c;
+ int sockopt;
+ char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+
+ pingsock = create_icmp6_socket();
+ pingaddr = lsa->sin6;
+
+ 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, &sockopt, sizeof(sockopt));
+
+ c = sendto(pingsock, packet, DEFDATALEN + sizeof (struct icmp6_hdr), 0,
+ (struct sockaddr *) &pingaddr, sizeof(pingaddr));
+
+ if (c < 0) {
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(pingsock);
+ bb_perror_msg_and_die("sendto");
+ }
+
+ /* listen for replies */
+ while (1) {
+ struct sockaddr_in6 from;
+ socklen_t fromlen = sizeof(from);
+
+ c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (c < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ if (c >= 8) { /* icmp6_hdr */
+ pkt = (struct icmp6_hdr *) packet;
+ if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
+ break;
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(pingsock);
+}
+#endif
+
+int ping_main(int argc, char **argv);
+int ping_main(int argc, char **argv)
+{
+ len_and_sockaddr *lsa;
+#if ENABLE_PING6
+ sa_family_t af = AF_UNSPEC;
+ while (++argv[0][0] == '-') {
+ if (argv[0][1] == '4') {
+ af = AF_INET;
+ continue;
+ }
+ if (argv[0][1] == '6') {
+ af = AF_INET6;
+ continue;
+ }
+ bb_show_usage();
+ }
+#else
+ argv++;
+#endif
+
+ hostname = *argv;
+ if (!hostname)
+ bb_show_usage();
+
+#if ENABLE_PING6
+ lsa = xhost_and_af2sockaddr(hostname, 0, af);
+#else
+ lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
+#endif
+ /* Set timer _after_ DNS resolution */
+ signal(SIGALRM, noresp);
+ alarm(5); /* give the host 5000ms to respond */
+
+#if ENABLE_PING6
+ if (lsa->sa.sa_family == AF_INET6)
+ ping6(lsa);
+ else
+#endif
+ ping4(lsa);
+ printf("%s is alive!\n", hostname);
+ return EXIT_SUCCESS;
+}
+
+
+#else /* FEATURE_FANCY_PING */
+
+
+/* full(er) version */
+
+#define OPT_STRING ("qvc:s:I:4" USE_PING6("6"))
+enum {
+ OPT_QUIET = 1 << 0,
+ OPT_VERBOSE = 1 << 1,
+ OPT_c = 1 << 2,
+ OPT_s = 1 << 3,
+ OPT_I = 1 << 4,
+ OPT_IPV4 = 1 << 5,
+ OPT_IPV6 = (1 << 6) * ENABLE_PING6,
+};
+
+
+static union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+#if ENABLE_PING6
+ struct sockaddr_in6 sin6;
+#endif
+} pingaddr;
+static len_and_sockaddr *source_lsa;
+static int pingsock = -1;
+static unsigned datalen; /* intentionally uninitialized to work around gcc bug */
+
+static int if_index;
+
+static unsigned long ntransmitted, nreceived, nrepeats, pingcount;
+static int myid;
+static unsigned tmin = UINT_MAX, tmax;
+static unsigned long tsum;
+static char rcvd_tbl[MAX_DUP_CHK / 8];
+
+static const char *hostname;
+static const char *dotted;
+
+#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 pingstats(int junk ATTRIBUTE_UNUSED)
+{
+ signal(SIGINT, SIG_IGN);
+
+ printf("\n--- %s ping statistics ---\n", hostname);
+ printf("%lu packets transmitted, ", ntransmitted);
+ printf("%lu packets received, ", nreceived);
+ if (nrepeats)
+ printf("%lu duplicates, ", nrepeats);
+ if (ntransmitted)
+ ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted;
+ printf("%lu%% packet loss\n", ntransmitted);
+ if (tmin != UINT_MAX)
+ printf("round-trip min/avg/max = %u.%u/%lu.%lu/%u.%u ms\n",
+ tmin / 10, tmin % 10,
+ (tsum / (nreceived + nrepeats)) / 10,
+ (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
+ exit(nreceived == 0); /* (nreceived == 0) is true (1) -- 'failure' */
+}
+
+static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt)
+{
+ int sz;
+
+ CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
+ ntransmitted++;
+
+ /* sizeof(pingaddr) can be larger than real sa size, but I think
+ * it doesn't matter */
+ sz = sendto(pingsock, pkt, size_pkt, 0, &pingaddr.sa, sizeof(pingaddr));
+ if (sz < 0)
+ bb_perror_msg_and_die("sendto");
+ if (sz != size_pkt)
+ bb_error_msg_and_die("ping wrote %d chars; %d expected", sz,
+ size_pkt);
+
+ signal(SIGALRM, sp);
+ 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 void sendping4(int junk ATTRIBUTE_UNUSED)
+{
+ struct icmp *pkt = alloca(datalen + ICMP_MINLEN);
+
+ pkt->icmp_type = ICMP_ECHO;
+ pkt->icmp_code = 0;
+ pkt->icmp_cksum = 0;
+ pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
+ pkt->icmp_id = myid;
+ gettimeofday((struct timeval *) &pkt->icmp_dun, NULL);
+ pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN);
+
+ sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN);
+}
+#if ENABLE_PING6
+static void sendping6(int junk ATTRIBUTE_UNUSED)
+{
+ struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr));
+
+ pkt->icmp6_type = ICMP6_ECHO_REQUEST;
+ pkt->icmp6_code = 0;
+ pkt->icmp6_cksum = 0;
+ pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
+ pkt->icmp6_id = myid;
+ gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL);
+
+ sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr));
+}
+#endif
+
+static const 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";
+ }
+}
+#if ENABLE_PING6
+/* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
+ * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
+#ifndef MLD_LISTENER_QUERY
+# define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
+#endif
+#ifndef MLD_LISTENER_REPORT
+# define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
+#endif
+#ifndef MLD_LISTENER_REDUCTION
+# define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
+#endif
+static const 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 MLD_LISTENER_QUERY: return "Listener Query";
+ case MLD_LISTENER_REPORT: return "Listener Report";
+ case MLD_LISTENER_REDUCTION: return "Listener Reduction";
+ default: return "unknown ICMP type";
+ }
+}
+#endif
+
+static void unpack_tail(int sz, struct timeval *tp,
+ const char *from_str,
+ uint16_t recv_seq, int ttl)
+{
+ const char *dupmsg = " (DUP!)";
+ unsigned triptime = triptime; /* for gcc */
+
+ ++nreceived;
+
+ if (tp) {
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ tv.tv_usec -= tp->tv_usec;
+ if (tv.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(recv_seq % MAX_DUP_CHK)) {
+ ++nrepeats;
+ --nreceived;
+ } else {
+ SET(recv_seq % MAX_DUP_CHK);
+ dupmsg += 7;
+ }
+
+ if (option_mask32 & OPT_QUIET)
+ return;
+
+ printf("%d bytes from %s: seq=%u ttl=%d", sz,
+ from_str, recv_seq, ttl);
+ if (tp)
+ printf(" time=%u.%u ms", triptime / 10, triptime % 10);
+ puts(dupmsg);
+ fflush(stdout);
+}
+static void unpack4(char *buf, int sz, struct sockaddr_in *from)
+{
+ struct icmp *icmppkt;
+ struct iphdr *iphdr;
+ int hlen;
+
+ /* discard if too short */
+ if (sz < (datalen + ICMP_MINLEN))
+ return;
+
+ /* check IP header */
+ iphdr = (struct iphdr *) buf;
+ hlen = iphdr->ihl << 2;
+ sz -= hlen;
+ icmppkt = (struct icmp *) (buf + hlen);
+ if (icmppkt->icmp_id != myid)
+ return; /* not our ping */
+
+ if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
+ uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
+ struct timeval *tp = NULL;
+
+ if (sz >= ICMP_MINLEN + sizeof(struct timeval))
+ tp = (struct timeval *) icmppkt->icmp_data;
+ unpack_tail(sz, tp,
+ inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
+ recv_seq, iphdr->ttl);
+ } else if (icmppkt->icmp_type != ICMP_ECHO) {
+ bb_error_msg("warning: got ICMP %d (%s)",
+ icmppkt->icmp_type,
+ icmp_type_name(icmppkt->icmp_type));
+ }
+}
+#if ENABLE_PING6
+static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
+{
+ struct icmp6_hdr *icmppkt;
+ char buf[INET6_ADDRSTRLEN];
+
+ /* 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) {
+ uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
+ struct timeval *tp = NULL;
+
+ if (sz >= sizeof(struct icmp6_hdr) + sizeof(struct timeval))
+ tp = (struct timeval *) &icmppkt->icmp6_data8[4];
+ unpack_tail(sz, tp,
+ inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr,
+ buf, sizeof(buf)),
+ recv_seq, hoplimit);
+ } 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));
+ }
+}
+#endif
+
+static void ping4(len_and_sockaddr *lsa)
+{
+ char packet[datalen + MAXIPLEN + MAXICMPLEN];
+ int sockopt;
+
+ pingsock = create_icmp_socket();
+ pingaddr.sin = lsa->sin;
+ if (source_lsa)
+ xbind(pingsock, &lsa->sa, lsa->len);
+
+ /* enable broadcast pings */
+ setsockopt_broadcast(pingsock);
+
+ /* set recv buf for broadcast pings */
+ sockopt = 48 * 1024; /* explain why 48k? */
+ setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
+
+ signal(SIGINT, pingstats);
+
+ /* start the ping's going ... */
+ sendping4(0);
+
+ /* listen for replies */
+ while (1) {
+ struct sockaddr_in from;
+ socklen_t fromlen = (socklen_t) sizeof(from);
+ int c;
+
+ c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (c < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ unpack4(packet, c, &from);
+ if (pingcount > 0 && nreceived >= pingcount)
+ break;
+ }
+}
+#if ENABLE_PING6
+extern int BUG_bad_offsetof_icmp6_cksum(void);
+static void ping6(len_and_sockaddr *lsa)
+{
+ char packet[datalen + MAXIPLEN + MAXICMPLEN];
+ int sockopt;
+ struct msghdr msg;
+ struct sockaddr_in6 from;
+ struct iovec iov;
+ char control_buf[CMSG_SPACE(36)];
+
+ pingsock = create_icmp6_socket();
+ pingaddr.sin6 = lsa->sin6;
+ /* untested whether "-I addr" really works for IPv6: */
+ if (source_lsa)
+ xbind(pingsock, &lsa->sa, lsa->len);
+
+#ifdef ICMP6_FILTER
+ {
+ struct icmp6_filter filt;
+ if (!(option_mask32 & OPT_VERBOSE)) {
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ 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 */
+ setsockopt_broadcast(pingsock);
+
+ /* set recv buf for broadcast pings */
+ sockopt = 48 * 1024; /* explain why 48k? */
+ setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
+
+ sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
+ if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2)
+ BUG_bad_offsetof_icmp6_cksum();
+ setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
+
+ /* request ttl info to be returned in ancillary data */
+ setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1));
+
+ if (if_index)
+ pingaddr.sin6.sin6_scope_id = if_index;
+
+ signal(SIGINT, pingstats);
+
+ /* start the ping's going ... */
+ sendping6(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 *mp;
+ int hoplimit = -1;
+ msg.msg_controllen = sizeof(control_buf);
+
+ c = recvmsg(pingsock, &msg, 0);
+ if (c < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
+ if (mp->cmsg_level == SOL_IPV6
+ && mp->cmsg_type == IPV6_HOPLIMIT
+ /* don't check len - we trust the kernel: */
+ /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
+ ) {
+ hoplimit = *(int*)CMSG_DATA(mp);
+ }
+ }
+ unpack6(packet, c, &from, hoplimit);
+ if (pingcount > 0 && nreceived >= pingcount)
+ break;
+ }
+}
+#endif
+
+static void ping(len_and_sockaddr *lsa)
+{
+ printf("PING %s (%s)", hostname, dotted);
+ if (source_lsa) {
+ printf(" from %s",
+ xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len));
+ }
+ printf(": %d data bytes\n", datalen);
+
+#if ENABLE_PING6
+ if (lsa->sa.sa_family == AF_INET6)
+ ping6(lsa);
+ else
+#endif
+ ping4(lsa);
+}
+
+int ping_main(int argc, char **argv);
+int ping_main(int argc, char **argv)
+{
+ len_and_sockaddr *lsa;
+ char *opt_c, *opt_s, *opt_I;
+ USE_PING6(sa_family_t af = AF_UNSPEC;)
+
+ datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
+
+ /* exactly one argument needed, -v and -q don't mix */
+ opt_complementary = "=1:q--v:v--q";
+ getopt32(argc, argv, OPT_STRING, &opt_c, &opt_s, &opt_I);
+ if (option_mask32 & OPT_c) pingcount = xatoul(opt_c); // -c
+ if (option_mask32 & OPT_s) datalen = xatou16(opt_s); // -s
+ if (option_mask32 & OPT_I) { // -I
+ if_index = if_nametoindex(opt_I);
+ if (!if_index) {
+ /* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */
+ /* (ping doesn't support source IPv6 addresses yet anyway) */
+ source_lsa = xdotted2sockaddr(opt_I, 0);
+ }
+ }
+ myid = (int16_t) getpid();
+ hostname = argv[optind];
+#if ENABLE_PING6
+ if (option_mask32 & OPT_IPV4)
+ af = AF_INET;
+ if (option_mask32 & OPT_IPV6)
+ af = AF_INET6;
+ lsa = xhost_and_af2sockaddr(hostname, 0, af);
+#else
+ lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
+#endif
+
+ if (source_lsa && source_lsa->sa.sa_family != lsa->sa.sa_family)
+ /* leaking it here... */
+ source_lsa = NULL;
+
+ dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len);
+ ping(lsa);
+ pingstats(0);
+ return EXIT_SUCCESS;
+}
+#endif /* FEATURE_FANCY_PING */
+
+
+#if ENABLE_PING6
+int ping6_main(int argc, char **argv);
+int ping6_main(int argc, char **argv)
+{
+ argv[0] = (char*)"-6";
+ return ping_main(argc + 1, argv - 1);
+}
+#endif
+
+/* from ping6.c:
+ * 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/i/pc104/initrd/conf/busybox/networking/route.c b/i/pc104/initrd/conf/busybox/networking/route.c
new file mode 100644
index 0000000..0697390
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/route.c
@@ -0,0 +1,697 @@
+/* vi: set sw=4 ts=4: */
+/* 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')
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ *
+ * 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>
+ */
+
+/* 2004/03/09 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Rewritten to fix several bugs, add additional error checking, and
+ * remove ridiculous amounts of bloat.
+ */
+
+#include "busybox.h"
+#include "inet_common.h"
+#include <getopt.h>
+#include <net/route.h>
+#include <net/if.h>
+
+
+#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
+
+#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */
+#define HAVE_NEW_ADDRT 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
+
+/* The RTACTION entries must agree with tbl_verb[] below! */
+#define RTACTION_ADD 1
+#define RTACTION_DEL 2
+
+/* For the various tbl_*[] arrays, the 1st byte is the offset to
+ * the next entry and the 2nd byte is return value. */
+
+#define NET_FLAG 1
+#define HOST_FLAG 2
+
+/* We remap '-' to '#' to avoid problems with getopt. */
+static const char tbl_hash_net_host[] =
+ "\007\001#net\0"
+/* "\010\002#host\0" */
+ "\007\002#host" /* Since last, we can save a byte. */
+;
+
+#define KW_TAKES_ARG 020
+#define KW_SETS_FLAG 040
+
+#define KW_IPVx_METRIC 020
+#define KW_IPVx_NETMASK 021
+#define KW_IPVx_GATEWAY 022
+#define KW_IPVx_MSS 023
+#define KW_IPVx_WINDOW 024
+#define KW_IPVx_IRTT 025
+#define KW_IPVx_DEVICE 026
+
+#define KW_IPVx_FLAG_ONLY 040
+#define KW_IPVx_REJECT 040
+#define KW_IPVx_MOD 041
+#define KW_IPVx_DYN 042
+#define KW_IPVx_REINSTATE 043
+
+static const char tbl_ipvx[] =
+ /* 020 is the "takes an arg" bit */
+#if HAVE_NEW_ADDRT
+ "\011\020metric\0"
+#endif
+ "\012\021netmask\0"
+ "\005\022gw\0"
+ "\012\022gateway\0"
+ "\006\023mss\0"
+ "\011\024window\0"
+#ifdef RTF_IRTT
+ "\007\025irtt\0"
+#endif
+ "\006\026dev\0"
+ "\011\026device\0"
+ /* 040 is the "sets a flag" bit - MUST match flags_ipvx[] values below. */
+#ifdef RTF_REJECT
+ "\011\040reject\0"
+#endif
+ "\006\041mod\0"
+ "\006\042dyn\0"
+/* "\014\043reinstate\0" */
+ "\013\043reinstate" /* Since last, we can save a byte. */
+;
+
+static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
+#ifdef RTF_REJECT
+ RTF_REJECT,
+#endif
+ RTF_MODIFIED,
+ RTF_DYNAMIC,
+ RTF_REINSTATE
+};
+
+static int kw_lookup(const char *kwtbl, char ***pargs)
+{
+ if (**pargs) {
+ do {
+ if (strcmp(kwtbl+2, **pargs) == 0) { /* Found a match. */
+ *pargs += 1;
+ if (kwtbl[1] & KW_TAKES_ARG) {
+ if (!**pargs) { /* No more args! */
+ bb_show_usage();
+ }
+ *pargs += 1; /* Calling routine will use args[-1]. */
+ }
+ return kwtbl[1];
+ }
+ kwtbl += *kwtbl;
+ } while (*kwtbl);
+ }
+ return 0;
+}
+
+/* Add or delete a route, depending on action. */
+
+static void INET_setroute(int action, char **args)
+{
+ struct rtentry rt;
+ const char *netmask = NULL;
+ int skfd, isnet, xflag;
+
+ /* Grab the -net or -host options. Remember they were transformed. */
+ xflag = kw_lookup(tbl_hash_net_host, &args);
+
+ /* If we did grab -net or -host, make sure we still have an arg left. */
+ if (*args == NULL) {
+ bb_show_usage();
+ }
+
+ /* Clean out the RTREQ structure. */
+ memset(&rt, 0, sizeof(rt));
+
+ {
+ const char *target = *args++;
+ char *prefix;
+
+ /* recognize x.x.x.x/mask format. */
+ prefix = strchr(target, '/');
+ if(prefix) {
+ int prefix_len;
+
+ prefix_len = xatoul_range(prefix+1, 0, 32);
+ mask_in_addr(rt) = htonl( ~ (0xffffffffUL >> prefix_len));
+ *prefix = '\0';
+#if HAVE_NEW_ADDRT
+ rt.rt_genmask.sa_family = AF_INET;
+#endif
+ } else {
+ /* Default netmask. */
+ netmask = bb_str_default;
+ }
+ /* Prefer hostname lookup is -host flag (xflag==1) was given. */
+ isnet = INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst,
+ (xflag & HOST_FLAG));
+ if (isnet < 0) {
+ bb_error_msg_and_die("resolving %s", target);
+ }
+ if(prefix) {
+ /* do not destroy prefix for process args */
+ *prefix = '/';
+ }
+ }
+
+ if (xflag) { /* Reinit isnet if -net or -host was specified. */
+ isnet = (xflag & NET_FLAG);
+ }
+
+ /* Fill in the other fields. */
+ rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST));
+
+ while (*args) {
+ int k = kw_lookup(tbl_ipvx, &args);
+ const char *args_m1 = args[-1];
+
+ if (k & KW_IPVx_FLAG_ONLY) {
+ rt.rt_flags |= flags_ipvx[k & 3];
+ continue;
+ }
+
+#if HAVE_NEW_ADDRT
+ if (k == KW_IPVx_METRIC) {
+ rt.rt_metric = xatoul(args_m1) + 1;
+ continue;
+ }
+#endif
+
+ if (k == KW_IPVx_NETMASK) {
+ struct sockaddr mask;
+
+ if (mask_in_addr(rt)) {
+ bb_show_usage();
+ }
+
+ netmask = args_m1;
+ isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0);
+ if (isnet < 0) {
+ bb_error_msg_and_die("resolving %s", netmask);
+ }
+ rt.rt_genmask = full_mask(mask);
+ continue;
+ }
+
+ if (k == KW_IPVx_GATEWAY) {
+ if (rt.rt_flags & RTF_GATEWAY) {
+ bb_show_usage();
+ }
+
+ isnet = INET_resolve(args_m1,
+ (struct sockaddr_in *) &rt.rt_gateway, 1);
+ rt.rt_flags |= RTF_GATEWAY;
+
+ if (isnet) {
+ if (isnet < 0) {
+ bb_error_msg_and_die("resolving %s", args_m1);
+ }
+ bb_error_msg_and_die("gateway %s is a NETWORK", args_m1);
+ }
+ continue;
+ }
+
+ if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */
+ rt.rt_flags |= RTF_MSS;
+ rt.rt_mss = xatoul_range(args_m1, 64, 32768);
+ continue;
+ }
+
+ if (k == KW_IPVx_WINDOW) { /* Check valid window bounds. */
+ rt.rt_flags |= RTF_WINDOW;
+ rt.rt_window = xatoul_range(args_m1, 128, INT_MAX);
+ continue;
+ }
+
+#ifdef RTF_IRTT
+ if (k == KW_IPVx_IRTT) {
+ rt.rt_flags |= RTF_IRTT;
+ rt.rt_irtt = xatoul(args_m1);
+ 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_and_die("bad irtt");
+ }
+#endif
+ continue;
+ }
+#endif
+
+ /* Device is special in that it can be the last arg specified
+ * and doesn't requre the dev/device keyword in that case. */
+ if (!rt.rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+ /* Don't use args_m1 here since args may have changed! */
+ rt.rt_dev = args[-1];
+ continue;
+ }
+
+ /* Nothing matched. */
+ bb_show_usage();
+ }
+
+#ifdef RTF_REJECT
+ if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) {
+ rt.rt_dev = (char*)"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_and_die("netmask %.8x and host route conflict",
+ (unsigned int) mask);
+ }
+ if (mask & (mask + 1)) {
+ bb_error_msg_and_die("bogus netmask %s", netmask);
+ }
+ mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
+ if (mask & ~mask_in_addr(rt)) {
+ bb_error_msg_and_die("netmask and route address conflict");
+ }
+ }
+
+ /* 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. */
+ skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
+ bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
+}
+
+#if ENABLE_FEATURE_IPV6
+
+static void INET6_setroute(int action, char **args)
+{
+ struct sockaddr_in6 sa6;
+ struct in6_rtmsg rt;
+ int prefix_len, skfd;
+ const char *devname;
+
+ /* We know args isn't NULL from the check in route_main. */
+ const char *target = *args++;
+
+ if (strcmp(target, bb_str_default) == 0) {
+ prefix_len = 0;
+ memset(&sa6, 0, sizeof(sa6));
+ } else {
+ char *cp;
+ if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */
+ *cp = 0;
+ prefix_len = xatoul_range(cp+1, 0, 128);
+ } else {
+ prefix_len = 128;
+ }
+ if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
+ bb_error_msg_and_die("resolving %s", target);
+ }
+ }
+
+ /* Clean out the RTREQ structure. */
+ memset(&rt, 0, sizeof(rt));
+
+ memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
+
+ /* Fill in the other fields. */
+ rt.rtmsg_dst_len = prefix_len;
+ rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP);
+ rt.rtmsg_metric = 1;
+
+ devname = NULL;
+
+ while (*args) {
+ int k = kw_lookup(tbl_ipvx, &args);
+ const char *args_m1 = args[-1];
+
+ if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) {
+ rt.rtmsg_flags |= flags_ipvx[k & 3];
+ continue;
+ }
+
+ if (k == KW_IPVx_METRIC) {
+ rt.rtmsg_metric = xatoul(args_m1);
+ continue;
+ }
+
+ if (k == KW_IPVx_GATEWAY) {
+ if (rt.rtmsg_flags & RTF_GATEWAY) {
+ bb_show_usage();
+ }
+
+ if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) {
+ bb_error_msg_and_die("resolving %s", args_m1);
+ }
+ memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ rt.rtmsg_flags |= RTF_GATEWAY;
+ continue;
+ }
+
+ /* Device is special in that it can be the last arg specified
+ * and doesn't requre the dev/device keyword in that case. */
+ if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+ /* Don't use args_m1 here since args may have changed! */
+ devname = args[-1];
+ continue;
+ }
+
+ /* Nothing matched. */
+ bb_show_usage();
+ }
+
+ /* Create a socket to the INET6 kernel. */
+ skfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
+
+ rt.rtmsg_ifindex = 0;
+
+ if (devname) {
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name));
+
+ if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) {
+ bb_perror_msg_and_die("SIOGIFINDEX");
+ }
+ rt.rtmsg_ifindex = ifr.ifr_ifindex;
+ }
+
+ /* Tell the kernel to accept this route. */
+ if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
+ bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
+}
+#endif
+
+static const unsigned int flagvals[] = { /* Must agree with flagchars[]. */
+ RTF_GATEWAY,
+ RTF_HOST,
+ RTF_REINSTATE,
+ RTF_DYNAMIC,
+ RTF_MODIFIED,
+#if ENABLE_FEATURE_IPV6
+ RTF_DEFAULT,
+ RTF_ADDRCONF,
+ RTF_CACHE
+#endif
+};
+
+#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
+#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE)
+
+static const char flagchars[] = /* Must agree with flagvals[]. */
+ "GHRDM"
+#if ENABLE_FEATURE_IPV6
+ "DAC"
+#endif
+;
+
+static void set_flags(char *flagstr, int flags)
+{
+ int i;
+
+ *flagstr++ = 'U';
+
+ for (i = 0; (*flagstr = flagchars[i]) != 0; i++) {
+ if (flags & flagvals[i]) {
+ ++flagstr;
+ }
+ }
+}
+
+/* also used in netstat */
+void bb_displayroutes(int noresolve, int netstatfmt)
+{
+ char devname[64], flags[16], sdest[16], sgw[16];
+ unsigned long int d, g, m;
+ int flgs, ref, use, metric, mtu, win, ir;
+ struct sockaddr_in s_addr;
+ struct in_addr mask;
+
+ FILE *fp = xfopen("/proc/net/route", "r");
+
+ printf("Kernel IP routing table\n"
+ "Destination Gateway Genmask Flags %s Iface\n",
+ netstatfmt ? " MSS Window irtt" : "Metric Ref Use");
+
+ if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */
+ goto ERROR; /* Empty or missing line, or read error. */
+ }
+ while (1) {
+ int r;
+ r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
+ devname, &d, &g, &flgs, &ref, &use, &metric, &m,
+ &mtu, &win, &ir);
+ if (r != 11) {
+ if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
+ break;
+ }
+ ERROR:
+ bb_error_msg_and_die("fscanf");
+ }
+
+ if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */
+ continue;
+ }
+
+ set_flags(flags, (flgs & IPV4_MASK));
+#ifdef RTF_REJECT
+ if (flgs & RTF_REJECT) {
+ flags[0] = '!';
+ }
+#endif
+
+ memset(&s_addr, 0, sizeof(struct sockaddr_in));
+ s_addr.sin_family = AF_INET;
+ s_addr.sin_addr.s_addr = d;
+ INET_rresolve(sdest, sizeof(sdest), &s_addr,
+ (noresolve | 0x8000), m); /* Default instead of *. */
+
+ s_addr.sin_addr.s_addr = g;
+ INET_rresolve(sgw, sizeof(sgw), &s_addr,
+ (noresolve | 0x4000), m); /* Host instead of net. */
+
+ mask.s_addr = m;
+ printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask), flags);
+ if (netstatfmt) {
+ printf("%5d %-5d %6d %s\n", mtu, win, ir, devname);
+ } else {
+ printf("%-6d %-2d %7d %s\n", metric, ref, use, devname);
+ }
+ }
+}
+
+#if ENABLE_FEATURE_IPV6
+
+static void INET6_displayroutes(int noresolve)
+{
+ char addr6[128], naddr6[128];
+ /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses.
+ * We read the non-delimited strings into the tail of the buffer
+ * using fscanf and then modify the buffer by shifting forward
+ * while inserting ':'s and the nul terminator for the first string.
+ * Hence the strings are at addr6x and addr6x+40. This generates
+ * _much_ less code than the previous (upstream) approach. */
+ char addr6x[80];
+ char iface[16], flags[16];
+ int iflags, metric, refcnt, use, prefix_len, slen;
+ struct sockaddr_in6 snaddr6;
+
+ FILE *fp = xfopen("/proc/net/ipv6_route", "r");
+
+ printf("Kernel IPv6 routing table\n%-44s%-40s"
+ "Flags Metric Ref Use Iface\n",
+ "Destination", "Next Hop");
+
+ while (1) {
+ int r;
+ r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n",
+ addr6x+14, &prefix_len, &slen, addr6x+40+7,
+ &metric, &use, &refcnt, &iflags, iface);
+ if (r != 9) {
+ if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
+ break;
+ }
+ ERROR:
+ bb_error_msg_and_die("fscanf");
+ }
+
+ /* Do the addr6x shift-and-insert changes to ':'-delimit addresses.
+ * For now, always do this to validate the proc route format, even
+ * if the interface is down. */
+ {
+ int i = 0;
+ char *p = addr6x+14;
+
+ do {
+ if (!*p) {
+ if (i==40) { /* nul terminator for 1st address? */
+ addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */
+ ++p; /* Skip and continue. */
+ continue;
+ }
+ goto ERROR;
+ }
+ addr6x[i++] = *p++;
+ if (!((i+1) % 5)) {
+ addr6x[i++] = ':';
+ }
+ } while (i < 40+28+7);
+ }
+
+ if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */
+ continue;
+ }
+
+ set_flags(flags, (iflags & IPV6_MASK));
+
+ r = 0;
+ do {
+ inet_pton(AF_INET6, addr6x + r,
+ (struct sockaddr *) &snaddr6.sin6_addr);
+ snaddr6.sin6_family = AF_INET6;
+ INET6_rresolve(naddr6, sizeof(naddr6),
+ (struct sockaddr_in6 *) &snaddr6,
+ 0x0fff /* Apparently, upstream never resolves. */
+ );
+
+ if (!r) { /* 1st pass */
+ snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len);
+ r += 40;
+ } else { /* 2nd pass */
+ /* Print the info. */
+ printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
+ addr6, naddr6, flags, metric, refcnt, use, iface);
+ break;
+ }
+ } while (1);
+ }
+}
+
+#endif
+
+#define ROUTE_OPT_A 0x01
+#define ROUTE_OPT_n 0x02
+#define ROUTE_OPT_e 0x04
+#define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */
+
+/* 1st byte is offset to next entry offset. 2nd byte is return value. */
+static const char tbl_verb[] = /* 2nd byte matches RTACTION_* code */
+ "\006\001add\0"
+ "\006\002del\0"
+/* "\011\002delete\0" */
+ "\010\002delete" /* Since last, we can save a byte. */
+;
+
+int route_main(int argc, char **argv);
+int route_main(int argc, char **argv)
+{
+ unsigned opt;
+ int what;
+ char *family;
+ char **p;
+
+ /* First, remap '-net' and '-host' to avoid getopt problems. */
+ p = argv;
+ while (*++p) {
+ if (strcmp(*p, "-net") == 0 || strcmp(*p, "-host") == 0) {
+ p[0][0] = '#';
+ }
+ }
+
+ opt = getopt32(argc, argv, "A:ne", &family);
+
+ if ((opt & ROUTE_OPT_A) && strcmp(family, "inet") != 0) {
+#if ENABLE_FEATURE_IPV6
+ if (strcmp(family, "inet6") == 0) {
+ opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */
+ } else
+#endif
+ bb_show_usage();
+ }
+
+ argv += optind;
+
+ /* No more args means display the routing table. */
+ if (!*argv) {
+ int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0;
+#if ENABLE_FEATURE_IPV6
+ if (opt & ROUTE_OPT_INET6)
+ INET6_displayroutes(noresolve);
+ else
+#endif
+ bb_displayroutes(noresolve, opt & ROUTE_OPT_e);
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+ }
+
+ /* Check verb. At the moment, must be add, del, or delete. */
+ what = kw_lookup(tbl_verb, &argv);
+ if (!what || !*argv) { /* Unknown verb or no more args. */
+ bb_show_usage();
+ }
+
+#if ENABLE_FEATURE_IPV6
+ if (opt & ROUTE_OPT_INET6)
+ INET6_setroute(what, argv);
+ else
+#endif
+ INET_setroute(what, argv);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/telnet.c b/i/pc104/initrd/conf/busybox/networking/telnet.c
new file mode 100644
index 0000000..cd88a9b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/telnet.c
@@ -0,0 +1,665 @@
+/* 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
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * 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>
+ * Modified 2004/02/11 to add ability to pass the USER variable to remote host
+ * by Fernando Silveira <swrh@gmx.net>
+ *
+ */
+
+#include <termios.h>
+#include <arpa/telnet.h>
+#include <netinet/in.h>
+#include "busybox.h"
+
+#ifdef DOTRACE
+#define TRACE(x, y) do { if (x) printf y; } while (0)
+#else
+#define TRACE(x, y)
+#endif
+
+enum {
+ DATABUFSIZE = 128,
+ IACBUFSIZE = 128,
+
+ CHM_TRY = 0,
+ CHM_ON = 1,
+ CHM_OFF = 2,
+
+ UF_ECHO = 0x01,
+ UF_SGA = 0x02,
+
+ TS_0 = 1,
+ TS_IAC = 2,
+ TS_OPT = 3,
+ TS_SUB1 = 4,
+ TS_SUB2 = 5,
+};
+
+typedef unsigned char byte;
+
+
+struct globals {
+ int netfd; /* console fd:s are 0 and 1 (and 2) */
+ short iaclen; /* could even use byte */
+ byte telstate; /* telnet negotiation state from network input */
+ byte telwish; /* DO, DONT, WILL, WONT */
+ byte charmode;
+ byte telflags;
+ byte gotsig;
+ byte do_termios;
+#if ENABLE_FEATURE_TELNET_TTYPE
+ char *ttype;
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ const char *autologin;
+#endif
+#if ENABLE_FEATURE_AUTOWIDTH
+ int win_width, win_height;
+#endif
+ /* same buffer used both for network and console read/write */
+ char buf[DATABUFSIZE];
+ /* buffer to handle telnet negotiations */
+ char iacbuf[IACBUFSIZE];
+ struct termios termios_def;
+ struct termios termios_raw;
+};
+
+#define G (*(struct globals*)bb_common_bufsiz1)
+
+
+/* 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);
+
+static void iacflush(void)
+{
+ write(G.netfd, G.iacbuf, G.iaclen);
+ G.iaclen = 0;
+}
+
+#define write_str(fd, str) write(fd, str, sizeof(str) - 1)
+
+static void doexit(int ev)
+{
+ cookmode();
+ exit(ev);
+}
+
+static void conescape(void)
+{
+ char b;
+
+ if (G.gotsig) /* came from line mode... go raw */
+ rawmode();
+
+ write_str(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);
+ }
+
+ write_str(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 = (byte*)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 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 ENABLE_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
+
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+static void putiac_subopt_autologin(void)
+{
+ int len = strlen(G.autologin) + 6; // (2 + 1 + 1 + strlen + 2)
+ const char *user = "USER";
+
+ if (G.iaclen + len > IACBUFSIZE)
+ iacflush();
+
+ putiac(IAC);
+ putiac(SB);
+ putiac(TELOPT_NEW_ENVIRON);
+ putiac(TELQUAL_IS);
+ putiac(NEW_ENV_VAR);
+
+ while (*user)
+ putiac(*user++);
+
+ putiac(NEW_ENV_VALUE);
+
+ while (*G.autologin)
+ putiac(*G.autologin++);
+
+ putiac(IAC);
+ putiac(SE);
+}
+#endif
+
+#if ENABLE_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
+
+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 void to_notsup(char c)
+{
+ if (G.telwish == WILL)
+ putiac2(DONT, c);
+ else if (G.telwish == DO)
+ putiac2(WONT, c);
+}
+
+static void to_echo(void)
+{
+ /* if server requests ECHO, don't agree */
+ if (G.telwish == DO) {
+ putiac2(WONT, TELOPT_ECHO);
+ return;
+ }
+ 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();
+ write_str(1, "\r\n"); /* sudden modec */
+}
+
+static 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);
+}
+
+#if ENABLE_FEATURE_TELNET_TTYPE
+static void to_ttype(void)
+{
+ /* Tell server we will (or won't) do TTYPE */
+
+ if (G.ttype)
+ putiac2(WILL, TELOPT_TTYPE);
+ else
+ putiac2(WONT, TELOPT_TTYPE);
+}
+#endif
+
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+static void to_new_environ(void)
+{
+ /* Tell server we will (or will not) do AUTOLOGIN */
+
+ if (G.autologin)
+ putiac2(WILL, TELOPT_NEW_ENVIRON);
+ else
+ putiac2(WONT, TELOPT_NEW_ENVIRON);
+}
+#endif
+
+#if ENABLE_FEATURE_AUTOWIDTH
+static void to_naws(void)
+{
+ /* Tell server we will do NAWS */
+ putiac2(WILL, TELOPT_NAWS);
+}
+#endif
+
+static void telopt(byte c)
+{
+ switch (c) {
+ case TELOPT_ECHO:
+ to_echo(); break;
+ case TELOPT_SGA:
+ to_sga(); break;
+#if ENABLE_FEATURE_TELNET_TTYPE
+ case TELOPT_TTYPE:
+ to_ttype(); break;
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ case TELOPT_NEW_ENVIRON:
+ to_new_environ(); break;
+#endif
+#if ENABLE_FEATURE_AUTOWIDTH
+ case TELOPT_NAWS:
+ to_naws();
+ putiac_naws(c, G.win_width, G.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;
+#if ENABLE_FEATURE_TELNET_TTYPE
+ else
+ if (c == TELOPT_TTYPE)
+ putiac_subopt(TELOPT_TTYPE, G.ttype);
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ else
+ if (c == TELOPT_NEW_ENVIRON)
+ putiac_subopt_autologin();
+#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)
+{
+ if (G.do_termios)
+ tcsetattr(0, TCSADRAIN, &G.termios_raw);
+}
+
+static void cookmode(void)
+{
+ if (G.do_termios)
+ tcsetattr(0, TCSADRAIN, &G.termios_def);
+}
+
+void BUG_telnet_globals_too_big(void);
+
+int telnet_main(int argc, char** argv);
+int telnet_main(int argc, char** argv)
+{
+ char *host;
+ int port;
+ int len;
+#ifdef USE_POLL
+ struct pollfd ufds[2];
+#else
+ fd_set readfds;
+ int maxfd;
+#endif
+
+ if (sizeof(G) > sizeof(bb_common_bufsiz1))
+ BUG_telnet_globals_too_big();
+ /* memset(&G, 0, sizeof G); - already is */
+
+#if ENABLE_FEATURE_AUTOWIDTH
+ get_terminal_width_height(0, &G.win_width, &G.win_height);
+#endif
+
+#if ENABLE_FEATURE_TELNET_TTYPE
+ G.ttype = getenv("TERM");
+#endif
+
+ if (tcgetattr(0, &G.termios_def) >= 0) {
+ G.do_termios = 1;
+ G.termios_raw = G.termios_def;
+ cfmakeraw(&G.termios_raw);
+ }
+
+ if (argc < 2)
+ bb_show_usage();
+
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ if (1 & getopt32(argc, argv, "al:", &G.autologin))
+ G.autologin = getenv("USER");
+ argv += optind;
+#else
+ argv++;
+#endif
+ if (!*argv)
+ bb_show_usage();
+ host = *argv++;
+ port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23);
+ if (*argv) /* extra params?? */
+ bb_show_usage();
+
+ G.netfd = create_and_connect_stream_or_die(host, port);
+
+ setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+
+ 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) {
+ write_str(1, "Connection closed by foreign host\r\n");
+ doexit(1);
+ }
+ TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len));
+ handlenetinput(len);
+ }
+ }
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/telnetd.c b/i/pc104/initrd/conf/busybox/networking/telnetd.c
new file mode 100644
index 0000000..ef9b1ac
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/telnetd.c
@@ -0,0 +1,573 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Simple telnet server
+ * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * ---------------------------------------------------------------------------
+ * (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 */
+#define DEBUG 0
+
+#include "busybox.h"
+
+#if DEBUG
+#define TELCMDS
+#define TELOPTS
+#endif
+#include <arpa/telnet.h>
+#include <sys/syslog.h>
+
+
+#define BUFSIZE 4000
+
+#if ENABLE_LOGIN
+static const char *loginpath = "/bin/login";
+#else
+static const char *loginpath = DEFAULT_SHELL;
+#endif
+
+static const char *issuefile = "/etc/issue.net";
+
+/* shell name and arguments */
+
+static const char *argv_init[2];
+
+/* structure that describes a session */
+
+struct tsession {
+ struct tsession *next;
+ int sockfd_read, sockfd_write, ptyfd;
+ 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 (received 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.
+
+ CR-LF ->'s CR mapping is also done here, for convenience
+ */
+static char *
+remove_iacs(struct tsession *ts, int *pnum_totty)
+{
+ unsigned char *ptr0 = (unsigned char *)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) {
+ int c = *ptr;
+ *totty++ = *ptr++;
+ /* We now map \r\n ==> \r for pragmatic reasons.
+ * Many client implementations send \r\n when
+ * the user hits the CarriageReturn key.
+ */
+ if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
+ ptr++;
+ } else {
+ /*
+ * TELOPT_NAWS support!
+ */
+ if ((ptr+2) >= end) {
+ /* only the beginning of the IAC is in the
+ buffer we were asked to process, we can't
+ process this char. */
+ break;
+ }
+
+ /*
+ * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
+ */
+ else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
+ struct winsize ws;
+ if ((ptr+8) >= end)
+ break; /* incomplete, can't process */
+ ws.ws_col = (ptr[3] << 8) | ptr[4];
+ ws.ws_row = (ptr[5] << 8) | ptr[6];
+ ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
+ ptr += 9;
+ } else {
+ /* skip 3-byte IAC non-SB cmd */
+#if DEBUG
+ fprintf(stderr, "Ignoring IAC %s,%s\n",
+ TELCMD(ptr[1]), TELOPT(ptr[2]));
+#endif
+ ptr += 3;
+ }
+ }
+ }
+
+ 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 size)
+{
+ int p;
+#if ENABLE_FEATURE_DEVPTS
+ p = open("/dev/ptmx", O_RDWR);
+ if (p > 0) {
+ const char *name;
+ grantpt(p);
+ unlockpt(p);
+ name = ptsname(p);
+ if (!name) {
+ bb_perror_msg("ptsname error (is /dev/pts mounted?)");
+ return -1;
+ }
+ safe_strncpy(line, name, size);
+ 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 (DEBUG)
+ fprintf(stderr, "Trying to open device: %s\n", line);
+ p = open(line, O_RDWR | O_NOCTTY);
+ if (p >= 0) {
+ line[5] = 't';
+ return p;
+ }
+ }
+ }
+#endif /* 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 *
+make_new_session(
+ USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
+ SKIP_FEATURE_TELNETD_STANDALONE(void)
+) {
+ struct termios termbuf;
+ int fd, pid;
+ char tty_name[32];
+ struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
+
+ ts->buf1 = (char *)(&ts[1]);
+ ts->buf2 = ts->buf1 + BUFSIZE;
+
+ /* Got a new connection, set up a tty. */
+ fd = getpty(tty_name, 32);
+ if (fd < 0) {
+ bb_error_msg("all terminals in use");
+ return NULL;
+ }
+ if (fd > maxfd) maxfd = fd;
+ ndelay_on(ts->ptyfd = fd);
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ if (sock_w > maxfd) maxfd = sock_w;
+ if (sock_r > maxfd) maxfd = sock_r;
+ ndelay_on(ts->sockfd_write = sock_w);
+ ndelay_on(ts->sockfd_read = sock_r);
+#else
+ ts->sockfd_write = 1;
+ /* xzalloc: ts->sockfd_read = 0; */
+ ndelay_on(0);
+ ndelay_on(1);
+#endif
+ /* 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_NAWS);
+ send_iac(ts, DO, TELOPT_LFLOW);
+ send_iac(ts, WILL, TELOPT_ECHO);
+ send_iac(ts, WILL, TELOPT_SGA);
+
+ pid = fork();
+ if (pid < 0) {
+ free(ts);
+ close(fd);
+ bb_perror_msg("fork");
+ return NULL;
+ }
+ if (pid > 0) {
+ /* parent */
+ ts->shell_pid = pid;
+ return ts;
+ }
+
+ /* child */
+
+ /* make new process group */
+ setsid();
+ tcsetpgrp(0, getpid());
+ /* ^^^ strace says: "ioctl(0, TIOCSPGRP, [pid]) = -1 ENOTTY" -- ??! */
+
+ /* open the child's side of the tty. */
+ /* NB: setsid() disconnects from any previous ctty's. Therefore
+ * we must open child's side of the tty AFTER setsid! */
+ fd = xopen(tty_name, O_RDWR); /* becomes our ctty */
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ while (fd > 2) close(fd--);
+
+ /* 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);
+ bb_perror_msg_and_die("execv");
+}
+
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+
+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_read);
+ /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */
+ close(ts->sockfd_write);
+ free(ts);
+
+ /* scan all sessions and find new maxfd */
+ ts = sessions;
+ maxfd = 0;
+ while (ts) {
+ if (maxfd < ts->ptyfd)
+ maxfd = ts->ptyfd;
+ if (maxfd < ts->sockfd_read)
+ maxfd = ts->sockfd_read;
+ if (maxfd < ts->sockfd_write)
+ maxfd = ts->sockfd_write;
+ ts = ts->next;
+ }
+}
+
+#else /* !FEATURE_TELNETD_STANDALONE */
+
+/* Never actually called */
+void free_session(struct tsession *ts);
+
+#endif
+
+
+int telnetd_main(int argc, char **argv);
+int telnetd_main(int argc, char **argv)
+{
+ fd_set rdfdset, wrfdset;
+ unsigned opt;
+ int selret, maxlen, w, r;
+ struct tsession *ts;
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+#define IS_INETD (opt & OPT_INETD)
+ int master_fd = -1; /* be happy, gcc */
+ unsigned portnbr = 23;
+ char *opt_bindaddr = NULL;
+ char *opt_portnbr;
+#else
+ enum {
+ IS_INETD = 1,
+ master_fd = -1,
+ portnbr = 23,
+ };
+#endif
+ enum {
+ OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,
+ OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,
+ OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
+ };
+
+ opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
+ &issuefile, &loginpath
+ USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
+ /* Redirect log to syslog early, if needed */
+ if (IS_INETD || !(opt & OPT_FOREGROUND)) {
+ openlog(applet_name, 0, LOG_USER);
+ logmode = LOGMODE_SYSLOG;
+ }
+ //if (opt & 1) // -f
+ //if (opt & 2) // -l
+ USE_FEATURE_TELNETD_STANDALONE(
+ if (opt & OPT_PORT) // -p
+ portnbr = xatou16(opt_portnbr);
+ //if (opt & 8) // -b
+ //if (opt & 0x10) // -F
+ //if (opt & 0x20) // -i
+ );
+
+ /* Used to check access(loginpath, X_OK) here. Pointless.
+ * exec will do this for us for free later. */
+ argv_init[0] = loginpath;
+
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ if (IS_INETD) {
+ sessions = make_new_session(0, 1);
+ } else {
+ master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
+ xlisten(master_fd, 1);
+ if (!(opt & OPT_FOREGROUND))
+ xdaemon(0, 0);
+ }
+#else
+ sessions = make_new_session();
+#endif
+
+ /* We don't want to die if just one session is broken */
+ signal(SIGPIPE, SIG_IGN);
+
+ again:
+ FD_ZERO(&rdfdset);
+ FD_ZERO(&wrfdset);
+ if (!IS_INETD) {
+ FD_SET(master_fd, &rdfdset);
+ /* This is needed because free_session() does not
+ * take into account master_fd when it finds new
+ * maxfd among remaining fd's: */
+ if (master_fd > maxfd)
+ maxfd = master_fd;
+ }
+
+ /* select on the master socket, all telnet sockets and their
+ * ptys if there is room in their session buffers. */
+ ts = sessions;
+ while (ts) {
+ /* buf1 is used from socket to pty
+ * buf2 is used from pty to socket */
+ if (ts->size1 > 0) /* can write to pty */
+ FD_SET(ts->ptyfd, &wrfdset);
+ if (ts->size1 < BUFSIZE) /* can read from socket */
+ FD_SET(ts->sockfd_read, &rdfdset);
+ if (ts->size2 > 0) /* can write to socket */
+ FD_SET(ts->sockfd_write, &wrfdset);
+ if (ts->size2 < BUFSIZE) /* can read from pty */
+ FD_SET(ts->ptyfd, &rdfdset);
+ ts = ts->next;
+ }
+
+ selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
+ if (!selret)
+ return 0;
+
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ /* First check for and accept new sessions. */
+ if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
+ int fd;
+ struct tsession *new_ts;
+
+ fd = accept(master_fd, NULL, 0);
+ if (fd < 0)
+ goto again;
+ /* Create a new session and link it into our active list */
+ new_ts = make_new_session(fd, fd);
+ if (new_ts) {
+ new_ts->next = sessions;
+ sessions = new_ts;
+ } else {
+ close(fd);
+ }
+ }
+#endif
+
+ /* Then check for data tunneling. */
+ ts = sessions;
+ while (ts) { /* For all sessions... */
+ struct tsession *next = ts->next; /* in case we free ts. */
+
+ 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 = safe_write(ts->ptyfd, ptr, num_totty);
+ /* needed? if (w < 0 && errno == EAGAIN) continue; */
+ if (w < 0) {
+ if (IS_INETD)
+ return 0;
+ free_session(ts);
+ ts = next;
+ continue;
+ }
+ ts->wridx1 += w;
+ ts->size1 -= w;
+ if (ts->wridx1 == BUFSIZE)
+ ts->wridx1 = 0;
+ }
+
+ if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
+ /* Write to socket from buffer 2. */
+ maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
+ w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
+ /* needed? if (w < 0 && errno == EAGAIN) continue; */
+ if (w < 0) {
+ if (IS_INETD)
+ return 0;
+ free_session(ts);
+ ts = next;
+ continue;
+ }
+ ts->wridx2 += w;
+ ts->size2 -= w;
+ if (ts->wridx2 == BUFSIZE)
+ ts->wridx2 = 0;
+ }
+
+ if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
+ /* Read from socket to buffer 1. */
+ maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
+ r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
+ if (r < 0 && errno == EAGAIN) continue;
+ if (r <= 0) {
+ if (IS_INETD)
+ return 0;
+ free_session(ts);
+ ts = next;
+ continue;
+ }
+ if (!ts->buf1[ts->rdidx1 + r - 1])
+ 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 = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
+ if (r < 0 && errno == EAGAIN) continue;
+ if (r <= 0) {
+ if (IS_INETD)
+ return 0;
+ free_session(ts);
+ ts = next;
+ continue;
+ }
+ 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;
+ }
+ ts = next;
+ }
+ goto again;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/tftp.c b/i/pc104/initrd/conf/busybox/networking/tftp.c
new file mode 100644
index 0000000..bbed9ac
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/tftp.c
@@ -0,0 +1,518 @@
+/* vi: set sw=4 ts=4: */
+/* -------------------------------------------------------------------------
+ * 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>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * ------------------------------------------------------------------------- */
+
+#include "busybox.h"
+
+
+#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
+
+#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
+#define TFTP_TIMEOUT 5 /* seconds */
+#define TFTP_NUM_RETRIES 5 /* number of retries */
+
+/* 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 *const 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"
+};
+
+#if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
+#define USE_GETPUT(a)
+#define CMD_GET(cmd) 1
+#define CMD_PUT(cmd) 0
+#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
+#define USE_GETPUT(a)
+#define CMD_GET(cmd) 0
+#define CMD_PUT(cmd) 1
+#else
+#define USE_GETPUT(a) a
+/* masks coming from getpot32 */
+#define CMD_GET(cmd) ((cmd) & 1)
+#define CMD_PUT(cmd) ((cmd) & 2)
+#endif
+/* NB: in the code below
+ * CMD_GET(cmd) and CMD_GET(cmd) are mutually exclusive
+ */
+
+
+#if ENABLE_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 > 65564)
+ ) {
+ bb_error_msg("bad blocksize");
+ return 0;
+ }
+
+ return blocksize;
+}
+
+static char *tftp_option_get(char *buf, int len, const char * const 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 int tftp(
+#if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
+ const int cmd,
+#endif
+ len_and_sockaddr *peer_lsa,
+ const char *remotefile, const int localfd,
+ unsigned port, int tftp_bufsize)
+{
+ struct timeval tv;
+ fd_set rfds;
+ int socketfd;
+ int len;
+ int opcode = 0;
+ int finished = 0;
+ int timeout = TFTP_NUM_RETRIES;
+ uint16_t block_nr = 1;
+ uint16_t tmp;
+ char *cp;
+
+ USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
+
+ unsigned org_port;
+ len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
+
+ /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
+ * size varies meaning BUFFERS_GO_ON_STACK would fail */
+ /* We must keep the transmit and receive buffers seperate */
+ /* In case we rcv a garbage pkt and we need to rexmit the last pkt */
+ char *xbuf = xmalloc(tftp_bufsize += 4);
+ char *rbuf = xmalloc(tftp_bufsize);
+
+ port = org_port = htons(port);
+
+ socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
+
+ /* build opcode */
+ opcode = TFTP_WRQ;
+ if (CMD_GET(cmd)) {
+ opcode = TFTP_RRQ;
+ }
+
+ while (1) {
+ cp = xbuf;
+
+ /* first create the opcode part */
+ /* (this 16bit store is aligned) */
+ *((uint16_t*)cp) = htons(opcode);
+ cp += 2;
+
+ /* add filename and mode */
+ if (CMD_GET(cmd) ? (opcode == TFTP_RRQ) : (opcode == TFTP_WRQ)) {
+ int too_long = 0;
+
+ /* see if the filename fits into xbuf
+ * and fill in packet. */
+ len = strlen(remotefile) + 1;
+
+ if ((cp + len) >= &xbuf[tftp_bufsize - 1]) {
+ too_long = 1;
+ } else {
+ safe_strncpy(cp, remotefile, len);
+ cp += len;
+ }
+
+ if (too_long || (&xbuf[tftp_bufsize - 1] - cp) < sizeof("octet")) {
+ bb_error_msg("remote filename too long");
+ break;
+ }
+
+ /* add "mode" part of the package */
+ memcpy(cp, "octet", sizeof("octet"));
+ cp += sizeof("octet");
+
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+
+ len = tftp_bufsize - 4; /* data block size */
+
+ if (len != TFTP_BLOCKSIZE_DEFAULT) {
+
+ if ((&xbuf[tftp_bufsize - 1] - cp) < 15) {
+ bb_error_msg("remote filename too long");
+ break;
+ }
+
+ /* add "blksize" + number of blocks */
+ memcpy(cp, "blksize", sizeof("blksize"));
+ cp += sizeof("blksize");
+ cp += snprintf(cp, 6, "%d", len) + 1;
+
+ want_option_ack = 1;
+ }
+#endif
+ }
+
+ /* add ack and data */
+
+ if (CMD_GET(cmd) ? (opcode == TFTP_ACK) : (opcode == TFTP_DATA)) {
+ /* TODO: unaligned access! */
+ *((uint16_t*)cp) = htons(block_nr);
+ cp += 2;
+ block_nr++;
+
+ if (CMD_PUT(cmd) && (opcode == TFTP_DATA)) {
+ len = full_read(localfd, cp, tftp_bufsize - 4);
+
+ if (len < 0) {
+ bb_perror_msg(bb_msg_read_error);
+ break;
+ }
+
+ if (len != (tftp_bufsize - 4)) {
+ finished++;
+ }
+
+ cp += len;
+ }
+ }
+
+ /* send packet */
+
+ timeout = TFTP_NUM_RETRIES; /* re-initialize */
+ do {
+ len = cp - xbuf;
+#if ENABLE_DEBUG_TFTP
+ fprintf(stderr, "sending %u bytes\n", len);
+ for (cp = xbuf; cp < &xbuf[len]; cp++)
+ fprintf(stderr, "%02x ", (unsigned char) *cp);
+ fprintf(stderr, "\n");
+#endif
+ if (sendto(socketfd, xbuf, len, 0,
+ &peer_lsa->sa, peer_lsa->len) < 0) {
+ bb_perror_msg("send");
+ len = -1;
+ break;
+ }
+
+ if (finished && (opcode == TFTP_ACK)) {
+ break;
+ }
+
+ /* receive packet */
+ recv_again:
+ tv.tv_sec = TFTP_TIMEOUT;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(socketfd, &rfds);
+
+ switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
+ unsigned from_port;
+ case 1:
+ from->len = peer_lsa->len;
+ memset(&from->sa, 0, peer_lsa->len);
+ len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
+ &from->sa, &from->len);
+ if (len < 0) {
+ bb_perror_msg("recvfrom");
+ break;
+ }
+ from_port = get_nport(from);
+ if (port == org_port) {
+ /* Our first query went to port 69
+ * but reply will come from different one.
+ * Remember and use this new port */
+ port = from_port;
+ set_nport(peer_lsa, from_port);
+ }
+ if (port != from_port)
+ goto recv_again;
+ timeout = 0;
+ break;
+ 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 */
+ /* (both accesses seems to be aligned) */
+
+ opcode = ntohs( ((uint16_t*)rbuf)[0] );
+ tmp = ntohs( ((uint16_t*)rbuf)[1] );
+
+#if ENABLE_DEBUG_TFTP
+ fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
+#endif
+
+ if (opcode == TFTP_ERROR) {
+ const char *msg = NULL;
+
+ if (rbuf[4] != '\0') {
+ msg = &rbuf[4];
+ rbuf[tftp_bufsize - 1] = '\0';
+ } else if (tmp < (sizeof(tftp_bb_error_msg)
+ / sizeof(char *))) {
+ msg = tftp_bb_error_msg[tmp];
+ }
+
+ if (msg) {
+ bb_error_msg("server says: %s", msg);
+ }
+
+ break;
+ }
+#if ENABLE_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(&rbuf[2], len - 2, "blksize");
+
+ if (res) {
+ int blksize = xatoi_u(res);
+
+ if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
+ if (CMD_PUT(cmd)) {
+ opcode = TFTP_DATA;
+ } else {
+ opcode = TFTP_ACK;
+ }
+#if ENABLE_DEBUG_TFTP
+ fprintf(stderr, "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(cmd) && (opcode == TFTP_DATA)) {
+ if (tmp == block_nr) {
+ len = full_write(localfd, &rbuf[4], len - 4);
+
+ if (len < 0) {
+ bb_perror_msg(bb_msg_write_error);
+ break;
+ }
+
+ if (len != (tftp_bufsize - 4)) {
+ finished++;
+ }
+
+ opcode = TFTP_ACK;
+ continue;
+ }
+ /* in case the last ack disappeared into the ether */
+ if (tmp == (block_nr - 1)) {
+ --block_nr;
+ opcode = TFTP_ACK;
+ continue;
+// tmp==(block_nr-1) and (tmp+1)==block_nr is always same, I think. wtf?
+ } else if (tmp + 1 == block_nr) {
+ /* Server lost our TFTP_ACK. Resend it */
+ block_nr = tmp;
+ opcode = TFTP_ACK;
+ continue;
+ }
+ }
+
+ if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
+ if (tmp == (uint16_t) (block_nr - 1)) {
+ if (finished) {
+ break;
+ }
+
+ opcode = TFTP_DATA;
+ continue;
+ }
+ }
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ close(socketfd);
+ free(xbuf);
+ free(rbuf);
+ }
+
+ return finished ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+int tftp_main(int argc, char **argv);
+int tftp_main(int argc, char **argv)
+{
+ len_and_sockaddr *peer_lsa;
+ const char *localfile = NULL;
+ const char *remotefile = NULL;
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ const char *sblocksize = NULL;
+#endif
+ int port;
+ USE_GETPUT(int cmd;)
+ int fd = -1;
+ int flags = 0;
+ int result;
+ int blocksize = TFTP_BLOCKSIZE_DEFAULT;
+
+ /* -p or -g is mandatory, and they are mutually exclusive */
+ opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
+ USE_GETPUT("?g--p:p--g");
+
+ USE_GETPUT(cmd =) getopt32(argc, argv,
+ USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
+ "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
+ &localfile, &remotefile
+ USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));
+
+ flags = O_RDONLY;
+ if (CMD_GET(cmd))
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
+
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ if (sblocksize) {
+ blocksize = xatoi_u(sblocksize);
+ if (!tftp_blocksize_check(blocksize, 0)) {
+ return EXIT_FAILURE;
+ }
+ }
+#endif
+
+ if (localfile == NULL)
+ localfile = remotefile;
+ if (remotefile == NULL)
+ remotefile = localfile;
+ if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))
+ bb_show_usage();
+
+ if (localfile == NULL || LONE_DASH(localfile)) {
+ fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
+ } else {
+ fd = xopen3(localfile, flags, 0644);
+ }
+
+ port = bb_lookup_port(argv[optind + 1], "udp", 69);
+ peer_lsa = xhost2sockaddr(argv[optind], port);
+
+#if ENABLE_DEBUG_TFTP
+ fprintf(stderr, "using server \"%s\", "
+ "remotefile \"%s\", localfile \"%s\".\n",
+ xmalloc_sockaddr2dotted(&peer_lsa->sa, peer_lsa->len),
+ remotefile, localfile);
+#endif
+
+ result = tftp(
+#if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
+ cmd,
+#endif
+ peer_lsa, remotefile, fd, port, blocksize);
+
+ if (fd > 1) {
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(fd);
+ if (CMD_GET(cmd) && result != EXIT_SUCCESS)
+ unlink(localfile);
+ }
+ return result;
+}
+
+#endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */
diff --git a/i/pc104/initrd/conf/busybox/networking/traceroute.c b/i/pc104/initrd/conf/busybox/networking/traceroute.c
new file mode 100644
index 0000000..fd4c20e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/traceroute.c
@@ -0,0 +1,1342 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Busybox port by Vladimir Oleynik (C) 2005 <dzo@simtreas.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+//#define version "1.4a12"
+
+
+/*
+ * traceroute host - trace the route ip packets follow going to "host".
+ *
+ * Attempt to trace the route an ip packet would follow to some
+ * internet host. We find out intermediate hops by launching probe
+ * packets with a small ttl (time to live) then listening for an
+ * icmp "time exceeded" reply from a gateway. We start our probes
+ * with a ttl of one and increase by one until we get an icmp "port
+ * unreachable" (which means we got to "host") or hit a max (which
+ * defaults to 30 hops & can be changed with the -m flag). Three
+ * probes (change with -q flag) are sent at each ttl setting and a
+ * line is printed showing the ttl, address of the gateway and
+ * round trip time of each probe. If the probe answers come from
+ * different gateways, the address of each responding system will
+ * be printed. If there is no response within a 5 sec. timeout
+ * interval (changed with the -w flag), a "*" is printed for that
+ * probe.
+ *
+ * Probe packets are UDP format. We don't want the destination
+ * host to process them so the destination port is set to an
+ * unlikely value (if some clod on the destination is using that
+ * value, it can be changed with the -p flag).
+ *
+ * A sample use might be:
+ *
+ * [yak 71]% traceroute nis.nsf.net.
+ * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
+ * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms
+ * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms
+ * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms
+ * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms
+ *
+ * Note that lines 2 & 3 are the same. This is due to a buggy
+ * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
+ * packets with a zero ttl.
+ *
+ * A more interesting example is:
+ *
+ * [yak 72]% traceroute allspice.lcs.mit.edu.
+ * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms
+ * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms
+ * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms
+ * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms
+ * 12 * * *
+ * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms
+ * 14 * * *
+ * 15 * * *
+ * 16 * * *
+ * 17 * * *
+ * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms
+ *
+ * (I start to see why I'm having so much trouble with mail to
+ * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away
+ * either don't send ICMP "time exceeded" messages or send them
+ * with a ttl too small to reach us. 14 - 17 are running the
+ * MIT C Gateway code that doesn't send "time exceeded"s. God
+ * only knows what's going on with 12.
+ *
+ * The silent gateway 12 in the above may be the result of a bug in
+ * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3)
+ * sends an unreachable message using whatever ttl remains in the
+ * original datagram. Since, for gateways, the remaining ttl is
+ * zero, the icmp "time exceeded" is guaranteed to not make it back
+ * to us. The behavior of this bug is slightly more interesting
+ * when it appears on the destination system:
+ *
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms
+ * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms
+ * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms
+ * 7 * * *
+ * 8 * * *
+ * 9 * * *
+ * 10 * * *
+ * 11 * * *
+ * 12 * * *
+ * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms !
+ *
+ * Notice that there are 12 "gateways" (13 is the final
+ * destination) and exactly the last half of them are "missing".
+ * What's really happening is that rip (a Sun-3 running Sun OS3.5)
+ * is using the ttl from our arriving datagram as the ttl in its
+ * icmp reply. So, the reply will time out on the return path
+ * (with no notice sent to anyone since icmp's aren't sent for
+ * icmp's) until we probe with a ttl that's at least twice the path
+ * length. I.e., rip is really only 7 hops away. A reply that
+ * returns with a ttl of 1 is a clue this problem exists.
+ * Traceroute prints a "!" after the time if the ttl is <= 1.
+ * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
+ * non-standard (HPUX) software, expect to see this problem
+ * frequently and/or take care picking the target host of your
+ * probes.
+ *
+ * Other possible annotations after the time are !H, !N, !P (got a host,
+ * network or protocol unreachable, respectively), !S or !F (source
+ * route failed or fragmentation needed -- neither of these should
+ * ever occur and the associated gateway is busted if you see one). If
+ * almost all the probes result in some kind of unreachable, traceroute
+ * will give up and exit.
+ *
+ * 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.)
+ *
+ * This program requires a kernel mod that does not appear in any
+ * system available from Berkeley: A raw ip socket using proto
+ * IPPROTO_RAW must interpret the data sent as an ip datagram (as
+ * opposed to data to be wrapped in a ip datagram). See the README
+ * file that came with the source to this program for a description
+ * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may
+ * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
+ * MODIFIED TO RUN THIS PROGRAM.
+ *
+ * The udp port usage may appear bizarre (well, ok, it is bizarre).
+ * The problem is that an icmp message only contains 8 bytes of
+ * data from the original datagram. 8 bytes is the size of a udp
+ * header so, if we want to associate replies with the original
+ * datagram, the necessary information must be encoded into the
+ * udp header (the ip id could be used but there's no way to
+ * interlock with the kernel's assignment of ip id's and, anyway,
+ * it would have taken a lot more kernel hacking to allow this
+ * code to set the ip id). So, to allow two or more users to
+ * use traceroute simultaneously, we use this task's pid as the
+ * source port (the high bit is set to move the port number out
+ * of the "likely" range). To keep track of which probe is being
+ * replied to (so times and/or hop counts don't get confused by a
+ * reply that was delayed in transit), we increment the destination
+ * port number before each probe.
+ *
+ * Don't use this as a coding example. I was trying to find a
+ * routing problem and this code sort-of popped out after 48 hours
+ * without sleep. I was amazed it ever compiled, much less ran.
+ *
+ * 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@ee.lbl.gov)
+ * Tue Dec 20 03:50:13 PST 1988
+ */
+
+#define TRACEROUTE_SO_DEBUG 0
+
+/* TODO: undefs were uncommented - ??! we have config system for that! */
+/* probably ok to remove altogether */
+//#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+//#define CONFIG_FEATURE_TRACEROUTE_VERBOSE
+//#undef CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
+//#define CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
+//#undef CONFIG_FEATURE_TRACEROUTE_USE_ICMP
+//#define CONFIG_FEATURE_TRACEROUTE_USE_ICMP
+
+
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include "busybox.h"
+#include "inet_common.h"
+
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+#ifndef IPPROTO_ICMP
+/* Grrrr.... */
+#define IPPROTO_ICMP 1
+#endif
+#ifndef IPPROTO_IP
+#define IPPROTO_IP 0
+#endif
+
+/*
+ * Overlay for ip header used by other protocols (tcp, udp).
+ */
+struct ipovly {
+ unsigned char ih_x1[9]; /* (unused) */
+ unsigned char ih_pr; /* protocol */
+ short ih_len; /* protocol length */
+ struct in_addr ih_src; /* source internet address */
+ struct in_addr ih_dst; /* destination internet address */
+};
+
+/*
+ * UDP kernel structures and variables.
+ */
+struct udpiphdr {
+ struct ipovly ui_i; /* overlaid ip structure */
+ struct udphdr ui_u; /* udp header */
+};
+#define ui_next ui_i.ih_next
+#define ui_prev ui_i.ih_prev
+#define ui_x1 ui_i.ih_x1
+#define ui_pr ui_i.ih_pr
+#define ui_len ui_i.ih_len
+#define ui_src ui_i.ih_src
+#define ui_dst ui_i.ih_dst
+#define ui_sport ui_u.uh_sport
+#define ui_dport ui_u.uh_dport
+#define ui_ulen ui_u.uh_ulen
+#define ui_sum ui_u.uh_sum
+
+
+/* Host name and address list */
+struct hostinfo {
+ char *name;
+ int n;
+ uint32_t *addrs;
+};
+
+/* Data section of the probe packet */
+struct outdata {
+ unsigned char seq; /* sequence number of this packet */
+ unsigned char ttl; /* ttl packet left with */
+ struct timeval tv ATTRIBUTE_PACKED; /* time packet left */
+};
+
+struct IFADDRLIST {
+ uint32_t addr;
+ char device[sizeof(struct ifreq)];
+};
+
+
+static const char route[] = "/proc/net/route";
+
+/* last inbound (icmp) packet */
+static unsigned char packet[512] ATTRIBUTE_ALIGNED(32);
+
+static struct ip *outip; /* last output (udp) packet */
+static struct udphdr *outudp; /* last output (udp) packet */
+static struct outdata *outdata; /* last output (udp) packet */
+
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+static struct icmp *outicmp; /* last output (icmp) packet */
+#endif
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+/* Maximum number of gateways (include room for one noop) */
+#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t)))
+/* loose source route gateway list (including room for final destination) */
+static uint32_t gwlist[NGATEWAYS + 1];
+#endif
+
+static int s; /* receive (icmp) socket file descriptor */
+static int sndsock; /* send (udp/icmp) socket file descriptor */
+
+static struct sockaddr_storage whereto; /* Who to try to reach */
+static struct sockaddr_storage wherefrom; /* Who we are */
+static int packlen; /* total length of packet */
+static int minpacket; /* min ip packet size */
+static int maxpacket = 32 * 1024; /* max ip packet size */
+static int pmtu; /* Path MTU Discovery (RFC1191) */
+
+static char *hostname;
+
+static uint16_t ident;
+static uint16_t port = 32768 + 666; /* start udp dest port # for probe packets */
+
+static int waittime = 5; /* time to wait for response (in seconds) */
+static int nflag; /* print addresses numerically */
+static int doipcksum = 1; /* calculate ip checksums by default */
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+static int optlen; /* length of ip options */
+#else
+#define optlen 0
+#endif
+
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+static int useicmp; /* use icmp echo instead of udp packets */
+#endif
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+static int verbose;
+#endif
+
+/*
+ * Return the interface list
+ */
+static int
+ifaddrlist(struct IFADDRLIST **ipaddrp)
+{
+ int fd, nipaddr;
+#ifdef HAVE_SOCKADDR_SA_LEN
+ int n;
+#endif
+ struct ifreq *ifrp, *ifend, *ifnext;
+ struct sockaddr_in *addr_sin;
+ struct IFADDRLIST *al;
+ struct ifconf ifc;
+ struct ifreq ibuf[(32 * 1024) / sizeof(struct ifreq)], ifr;
+ struct IFADDRLIST *st_ifaddrlist;
+
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ ifc.ifc_len = sizeof(ibuf);
+ ifc.ifc_buf = (caddr_t)ibuf;
+
+ if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 ||
+ ifc.ifc_len < sizeof(struct ifreq)) {
+ if (errno == EINVAL)
+ bb_error_msg_and_die(
+ "SIOCGIFCONF: ifreq struct too small (%d bytes)",
+ (int)sizeof(ibuf));
+ else
+ bb_perror_msg_and_die("SIOCGIFCONF");
+ }
+ ifrp = ibuf;
+ ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
+
+ nipaddr = 1 + (ifc.ifc_len / sizeof(struct ifreq));
+ st_ifaddrlist = xzalloc(nipaddr * sizeof(struct IFADDRLIST));
+ al = st_ifaddrlist;
+ nipaddr = 0;
+
+ for (; ifrp < ifend; ifrp = ifnext) {
+#ifdef HAVE_SOCKADDR_SA_LEN
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ ifnext = ifrp + 1;
+ else
+ ifnext = (struct ifreq *)((char *)ifrp + n);
+ if (ifrp->ifr_addr.sa_family != AF_INET)
+ continue;
+#else
+ ifnext = ifrp + 1;
+#endif
+ /*
+ * Need a template to preserve address info that is
+ * used below to locate the next entry. (Otherwise,
+ * SIOCGIFFLAGS stomps over it because the requests
+ * are returned in a union.)
+ */
+ strncpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name));
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
+ if (errno == ENXIO)
+ continue;
+ bb_perror_msg_and_die("SIOCGIFFLAGS: %.*s",
+ (int)sizeof(ifr.ifr_name), ifr.ifr_name);
+ }
+
+ /* Must be up */
+ if ((ifr.ifr_flags & IFF_UP) == 0)
+ continue;
+
+ safe_strncpy(al->device, ifr.ifr_name, sizeof(ifr.ifr_name) + 1);
+#ifdef sun
+ /* Ignore sun virtual interfaces */
+ if (strchr(al->device, ':') != NULL)
+ continue;
+#endif
+ if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0)
+ bb_perror_msg_and_die("SIOCGIFADDR: %s", al->device);
+
+ addr_sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ al->addr = addr_sin->sin_addr.s_addr;
+ ++al;
+ ++nipaddr;
+ }
+ if (nipaddr == 0)
+ bb_error_msg_and_die ("can't find any network interfaces");
+ (void)close(fd);
+
+ *ipaddrp = st_ifaddrlist;
+ return nipaddr;
+}
+
+
+static void
+setsin(struct sockaddr_in *addr_sin, uint32_t addr)
+{
+ memset(addr_sin, 0, sizeof(*addr_sin));
+#ifdef HAVE_SOCKADDR_SA_LEN
+ addr_sin->sin_len = sizeof(*addr_sin);
+#endif
+ addr_sin->sin_family = AF_INET;
+ addr_sin->sin_addr.s_addr = addr;
+}
+
+
+/*
+ * Return the source address for the given destination address
+ */
+static void
+findsaddr(const struct sockaddr_in *to, struct sockaddr_in *from)
+{
+ int i, n;
+ FILE *f;
+ uint32_t mask;
+ uint32_t dest, tmask;
+ struct IFADDRLIST *al;
+ char buf[256], tdevice[256], device[256];
+
+ f = xfopen(route, "r");
+
+ /* Find the appropriate interface */
+ n = 0;
+ mask = 0;
+ device[0] = '\0';
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ ++n;
+ if (n == 1 && strncmp(buf, "Iface", 5) == 0)
+ continue;
+ if ((i = sscanf(buf, "%255s %x %*s %*s %*s %*s %*s %x",
+ tdevice, &dest, &tmask)) != 3)
+ bb_error_msg_and_die ("junk in buffer");
+ if ((to->sin_addr.s_addr & tmask) == dest &&
+ (tmask > mask || mask == 0)) {
+ mask = tmask;
+ strcpy(device, tdevice);
+ }
+ }
+ fclose(f);
+
+ if (device[0] == '\0')
+ bb_error_msg_and_die ("can't find interface");
+
+ /* Get the interface address list */
+ n = ifaddrlist(&al);
+
+ /* Find our appropriate source address */
+ for (i = n; i > 0; --i, ++al)
+ if (strcmp(device, al->device) == 0)
+ break;
+ if (i <= 0)
+ bb_error_msg_and_die("can't find interface %s", device);
+
+ setsin(from, al->addr);
+}
+
+/*
+"Usage: %s [-dFIlnrvx] [-g gateway] [-i iface] [-f first_ttl]\n"
+"\t[-m max_ttl] [ -p port] [-q nqueries] [-s src_addr] [-t tos]\n"
+"\t[-w waittime] [-z pausemsecs] host [packetlen]"
+
+*/
+
+/*
+ * Subtract 2 timeval structs: out = out - in.
+ * Out is assumed to be >= in.
+ */
+static inline void
+tvsub(struct timeval *out, struct timeval *in)
+{
+
+ if ((out->tv_usec -= in->tv_usec) < 0) {
+ --out->tv_sec;
+ out->tv_usec += 1000000;
+ }
+ out->tv_sec -= in->tv_sec;
+}
+
+static int
+wait_for_reply(int sock, struct sockaddr_in *fromp, const struct timeval *tp)
+{
+ fd_set fds;
+ struct timeval now, tvwait;
+ struct timezone tz;
+ int cc = 0;
+ socklen_t fromlen = sizeof(*fromp);
+
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+
+ tvwait.tv_sec = tp->tv_sec + waittime;
+ tvwait.tv_usec = tp->tv_usec;
+ (void)gettimeofday(&now, &tz);
+ tvsub(&tvwait, &now);
+
+ if (select(sock + 1, &fds, NULL, NULL, &tvwait) > 0)
+ cc = recvfrom(sock, (char *)packet, sizeof(packet), 0,
+ (struct sockaddr *)fromp, &fromlen);
+
+ return cc;
+}
+
+/*
+ * Checksum routine for Internet Protocol family headers (C Version)
+ */
+static uint16_t
+in_cksum(uint16_t *addr, int len)
+{
+ int nleft = len;
+ uint16_t *w = addr;
+ uint16_t answer;
+ int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1)
+ sum += *(unsigned char *)w;
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return answer;
+}
+
+
+static void
+send_probe(int seq, int ttl, struct timeval *tp)
+{
+ int cc;
+ struct udpiphdr *ui, *oui;
+ struct ip tip;
+
+ outip->ip_ttl = ttl;
+ outip->ip_id = htons(ident + seq);
+
+ /*
+ * In most cases, the kernel will recalculate the ip checksum.
+ * But we must do it anyway so that the udp checksum comes out
+ * right.
+ */
+ if (doipcksum) {
+ outip->ip_sum =
+ in_cksum((uint16_t *)outip, sizeof(*outip) + optlen);
+ if (outip->ip_sum == 0)
+ outip->ip_sum = 0xffff;
+ }
+
+ /* Payload */
+ outdata->seq = seq;
+ outdata->ttl = ttl;
+ memcpy(&outdata->tv, tp, sizeof(outdata->tv));
+
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp)
+ outicmp->icmp_seq = htons(seq);
+ else
+#endif
+ outudp->dest = htons(port + seq);
+
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp) {
+ /* Always calculate checksum for icmp packets */
+ outicmp->icmp_cksum = 0;
+ outicmp->icmp_cksum = in_cksum((uint16_t *)outicmp,
+ packlen - (sizeof(*outip) + optlen));
+ if (outicmp->icmp_cksum == 0)
+ outicmp->icmp_cksum = 0xffff;
+ } else
+#endif
+ if (doipcksum) {
+ /* Checksum (we must save and restore ip header) */
+ tip = *outip;
+ ui = (struct udpiphdr *)outip;
+ oui = (struct udpiphdr *)&tip;
+ /* Easier to zero and put back things that are ok */
+ memset((char *)ui, 0, sizeof(ui->ui_i));
+ ui->ui_src = oui->ui_src;
+ ui->ui_dst = oui->ui_dst;
+ ui->ui_pr = oui->ui_pr;
+ ui->ui_len = outudp->len;
+ outudp->check = 0;
+ outudp->check = in_cksum((uint16_t *)ui, packlen);
+ if (outudp->check == 0)
+ outudp->check = 0xffff;
+ *outip = tip;
+ }
+
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+ /* XXX undocumented debugging hack */
+ if (verbose > 1) {
+ const uint16_t *sp;
+ int nshorts, i;
+
+ sp = (uint16_t *)outip;
+ nshorts = (unsigned)packlen / sizeof(uint16_t);
+ i = 0;
+ printf("[ %d bytes", packlen);
+ while (--nshorts >= 0) {
+ if ((i++ % 8) == 0)
+ printf("\n\t");
+ printf(" %04x", ntohs(*sp));
+ sp++;
+ }
+ if (packlen & 1) {
+ if ((i % 8) == 0)
+ printf("\n\t");
+ printf(" %02x", *(unsigned char *)sp);
+ }
+ printf("]\n");
+ }
+#endif
+
+#if !defined(IP_HDRINCL) && defined(IP_TTL)
+ if (setsockopt(sndsock, IPPROTO_IP, IP_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0) {
+ bb_perror_msg_and_die("setsockopt ttl %d", ttl);
+ }
+#endif
+
+ cc = sendto(sndsock, (char *)outip,
+ packlen, 0, (struct sockaddr *)&whereto, sizeof(whereto));
+ if (cc < 0 || cc != packlen) {
+ if (cc < 0)
+ bb_perror_msg_and_die("sendto");
+ printf("%s: wrote %s %d chars, ret=%d\n",
+ applet_name, hostname, packlen, cc);
+ (void)fflush(stdout);
+ }
+}
+
+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;
+}
+
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+/*
+ * Convert an ICMP "type" field to a printable string.
+ */
+static inline const char *
+pr_type(unsigned char t)
+{
+ static const char * const ttab[] = {
+ "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
+ "Source Quench", "Redirect", "ICMP 6", "ICMP 7",
+ "Echo", "Router Advert", "Router Solicit", "Time Exceeded",
+ "Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
+ "Info Reply", "Mask Request", "Mask Reply"
+ };
+
+ if (t > 18)
+ return "OUT-OF-RANGE";
+
+ return ttab[t];
+}
+#endif
+
+static int
+packet_ok(unsigned char *buf, int cc, struct sockaddr_in *from, int seq)
+{
+ struct icmp *icp;
+ unsigned char type, code;
+ int hlen;
+ struct ip *ip;
+
+ ip = (struct ip *) buf;
+ hlen = ip->ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN) {
+#if ENABLE_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;
+ /* Path MTU Discovery (RFC1191) */
+ if (code != ICMP_UNREACH_NEEDFRAG)
+ pmtu = 0;
+ else {
+ pmtu = ntohs(icp->icmp_nextmtu);
+ }
+ if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
+ type == ICMP_UNREACH || type == ICMP_ECHOREPLY) {
+ struct ip *hip;
+ struct udphdr *up;
+
+ hip = &icp->icmp_ip;
+ hlen = hip->ip_hl << 2;
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp) {
+ struct icmp *hicmp;
+
+ /* XXX */
+ if (type == ICMP_ECHOREPLY &&
+ icp->icmp_id == htons(ident) &&
+ icp->icmp_seq == htons(seq))
+ return -2;
+
+ hicmp = (struct icmp *)((unsigned char *)hip + hlen);
+ /* XXX 8 is a magic number */
+ if (hlen + 8 <= cc &&
+ hip->ip_p == IPPROTO_ICMP &&
+ hicmp->icmp_id == htons(ident) &&
+ hicmp->icmp_seq == htons(seq))
+ return (type == ICMP_TIMXCEED ? -1 : code + 1);
+ } else
+#endif
+ {
+ up = (struct udphdr *)((unsigned char *)hip + hlen);
+ /* XXX 8 is a magic number */
+ 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);
+ }
+ }
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+ if (verbose) {
+ int i;
+ uint32_t *lp = (uint32_t *)&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(*lp))
+ printf("%2d: x%8.8x\n", i, *lp++);
+ }
+#endif
+ return 0;
+}
+
+
+/*
+ * 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)
+{
+ const char *n = NULL;
+ const char *ina;
+ char name[257];
+
+ if (!nflag && from->sin_addr.s_addr != INADDR_ANY) {
+ if (INET_rresolve(name, sizeof(name), from, 0x4000,
+ 0xffffffff) >= 0)
+ n = name;
+ }
+ ina = inet_ntoa(from->sin_addr);
+ if (nflag)
+ printf(" %s", ina);
+ else
+ printf(" %s (%s)", (n ? n : ina), ina);
+}
+
+static inline void
+print(unsigned 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);
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+ if (verbose)
+ printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
+#endif
+}
+
+
+static struct hostinfo *
+gethostinfo(const char *host)
+{
+ int n;
+ struct hostent *hp;
+ struct hostinfo *hi;
+ char **p;
+ uint32_t addr, *ap;
+
+ hi = xzalloc(sizeof(*hi));
+ addr = inet_addr(host);
+ if (addr != 0xffffffff) {
+ hi->name = xstrdup(host);
+ hi->n = 1;
+ hi->addrs = xzalloc(sizeof(hi->addrs[0]));
+ hi->addrs[0] = addr;
+ return hi;
+ }
+
+ hp = xgethostbyname(host);
+ if (hp->h_addrtype != AF_INET || hp->h_length != 4)
+ bb_perror_msg_and_die("bad host %s", host);
+ hi->name = xstrdup(hp->h_name);
+ for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p)
+ continue;
+ hi->n = n;
+ hi->addrs = xzalloc(n * sizeof(hi->addrs[0]));
+ for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p)
+ memcpy(ap, *p, sizeof(*ap));
+ return hi;
+}
+
+static void
+freehostinfo(struct hostinfo *hi)
+{
+ free(hi->name);
+ hi->name = NULL;
+ free((char *)hi->addrs);
+ free((char *)hi);
+}
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+static void
+getaddr(uint32_t *ap, const char *host)
+{
+ struct hostinfo *hi;
+
+ hi = gethostinfo(host);
+ *ap = hi->addrs[0];
+ freehostinfo(hi);
+}
+#endif
+
+
+int traceroute_main(int argc, char *argv[]);
+int traceroute_main(int argc, char *argv[])
+{
+ int code, n;
+ unsigned char *outp;
+ uint32_t *ap;
+ struct sockaddr_in *from = (struct sockaddr_in *)&wherefrom;
+ struct sockaddr_in *to = (struct sockaddr_in *)&whereto;
+ struct hostinfo *hi;
+ int ttl, probe, i;
+ int seq = 0;
+ int tos = 0;
+ char *tos_str = NULL;
+ char *source = NULL;
+ unsigned long op;
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ int lsrr = 0;
+#endif
+ uint16_t off = 0;
+ struct IFADDRLIST *al;
+ char *device = NULL;
+ int max_ttl = 30;
+ char *max_ttl_str = NULL;
+ char *port_str = NULL;
+ int nprobes = 3;
+ char *nprobes_str = NULL;
+ char *waittime_str = NULL;
+ unsigned pausemsecs = 0;
+ char *pausemsecs_str = NULL;
+ int first_ttl = 1;
+ char *first_ttl_str = NULL;
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ llist_t *sourse_route_list = NULL;
+#endif
+
+ opterr = 0;
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ opt_complementary = "x-x:g::";
+#else
+ opt_complementary = "x-x";
+#endif
+
+ op = getopt32(argc, argv, "FIlnrdvxt:i:m:p:q:s:w:z:f:"
+#define USAGE_OP_DONT_FRAGMNT (1<<0) /* F */
+#define USAGE_OP_USE_ICMP (1<<1) /* I */
+#define USAGE_OP_TTL_FLAG (1<<2) /* l */
+#define USAGE_OP_ADDR_NUM (1<<3) /* n */
+#define USAGE_OP_BYPASS_ROUTE (1<<4) /* r */
+#define USAGE_OP_DEBUG (1<<5) /* d */
+#define USAGE_OP_VERBOSE (1<<6) /* v */
+#define USAGE_OP_IP_CHKSUM (1<<7) /* x */
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ "g:"
+#endif
+ , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str,
+ &source, &waittime_str, &pausemsecs_str, &first_ttl_str
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ , &sourse_route_list
+#endif
+ );
+
+ if (op & USAGE_OP_DONT_FRAGMNT)
+ off = IP_DF;
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ useicmp = op & USAGE_OP_USE_ICMP;
+#endif
+ nflag = op & USAGE_OP_ADDR_NUM;
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+ verbose = op & USAGE_OP_VERBOSE;
+#endif
+ if (op & USAGE_OP_IP_CHKSUM) {
+ doipcksum = 0;
+ bb_error_msg("warning: ip checksums disabled");
+ }
+ if (tos_str)
+ tos = xatoul_range(tos_str, 0, 255);
+ if (max_ttl_str)
+ max_ttl = xatoul_range(max_ttl_str, 1, 255);
+ if (port_str)
+ port = xatou16(port_str);
+ if (nprobes_str)
+ nprobes = xatoul_range(nprobes_str, 1, INT_MAX);
+ if (source) {
+ /*
+ * set the ip source address of the outbound
+ * probe (e.g., on a multi-homed host).
+ */
+ if (getuid()) bb_error_msg_and_die("-s %s: permission denied", source);
+ }
+ if (waittime_str)
+ waittime = xatoul_range(waittime_str, 2, 24 * 60 * 60);
+ if (pausemsecs_str)
+ pausemsecs = xatoul_range(pausemsecs_str, 0, 60 * 60 * 1000);
+ if (first_ttl_str)
+ first_ttl = xatoul_range(first_ttl_str, 1, 255);
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ if (sourse_route_list) {
+ llist_t *l_sr;
+
+ for (l_sr = sourse_route_list; l_sr; ) {
+ if (lsrr >= NGATEWAYS)
+ bb_error_msg_and_die("no more than %d gateways", NGATEWAYS);
+ getaddr(gwlist + lsrr, l_sr->data);
+ ++lsrr;
+ l_sr = l_sr->link;
+ free(sourse_route_list);
+ sourse_route_list = l_sr;
+ }
+ optlen = (lsrr + 1) * sizeof(gwlist[0]);
+ }
+#endif
+
+ if (first_ttl > max_ttl) {
+ bb_error_msg_and_die(
+ "first ttl (%d) may not be greater than max ttl (%d)",
+ first_ttl, max_ttl);
+ }
+
+ minpacket = sizeof(*outip) + sizeof(*outdata) + optlen;
+
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp)
+ minpacket += 8; /* XXX magic number */
+ else
+#endif
+ minpacket += sizeof(*outudp);
+ packlen = minpacket; /* minimum sized packet */
+
+ /* Process destination and optional packet size */
+ switch (argc - optind) {
+
+ case 2:
+ packlen = xatoul_range(argv[optind + 1], minpacket, maxpacket);
+ /* Fall through */
+
+ case 1:
+ hostname = argv[optind];
+ hi = gethostinfo(hostname);
+ setsin(to, hi->addrs[0]);
+ if (hi->n > 1)
+ bb_error_msg("warning: %s has multiple addresses; using %s",
+ hostname, inet_ntoa(to->sin_addr));
+ hostname = hi->name;
+ hi->name = NULL;
+ freehostinfo(hi);
+ break;
+
+ default:
+ bb_show_usage();
+ }
+
+ /* Ensure the socket fds won't be 0, 1 or 2 */
+ bb_sanitize_stdio();
+
+ s = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+
+#if TRACEROUTE_SO_DEBUG
+ if (op & USAGE_OP_DEBUG)
+ setsockopt(s, SOL_SOCKET, SO_DEBUG,
+ &const_int_1, sizeof(const_int_1));
+#endif
+ if (op & USAGE_OP_BYPASS_ROUTE)
+ setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
+ &const_int_1, sizeof(const_int_1));
+
+ sndsock = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+#if defined(IP_OPTIONS)
+ if (lsrr > 0) {
+ unsigned char optlist[MAX_IPOPTLEN];
+
+ /* final hop */
+ gwlist[lsrr] = to->sin_addr.s_addr;
+ ++lsrr;
+
+ /* force 4 byte alignment */
+ optlist[0] = IPOPT_NOP;
+ /* loose source route option */
+ optlist[1] = IPOPT_LSRR;
+ i = lsrr * sizeof(gwlist[0]);
+ optlist[2] = i + 3;
+ /* Pointer to LSRR addresses */
+ optlist[3] = IPOPT_MINOFF;
+ memcpy(optlist + 4, gwlist, i);
+
+ if ((setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS,
+ (char *)optlist, i + sizeof(gwlist[0]))) < 0) {
+ bb_perror_msg_and_die("IP_OPTIONS");
+ }
+ }
+#endif /* IP_OPTIONS */
+#endif /* CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE */
+
+#ifdef SO_SNDBUF
+ if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) {
+ bb_perror_msg_and_die("SO_SNDBUF");
+ }
+#endif
+#ifdef IP_HDRINCL
+ if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, &const_int_1, sizeof(const_int_1)) < 0
+ && errno != ENOPROTOOPT
+ ) {
+ bb_perror_msg_and_die("IP_HDRINCL");
+ }
+#else
+#ifdef IP_TOS
+ if (tos_str && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
+ bb_perror_msg_and_die("setsockopt tos %d", tos);
+ }
+#endif
+#endif
+#if TRACEROUTE_SO_DEBUG
+ if (op & USAGE_OP_DEBUG)
+ setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
+ &const_int_1, sizeof(const_int_1));
+#endif
+ if (op & USAGE_OP_BYPASS_ROUTE)
+ setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
+ &const_int_1, sizeof(const_int_1));
+
+ /* Revert to non-privileged user after opening sockets */
+ xsetgid(getgid());
+ xsetuid(getuid());
+
+ outip = xzalloc(packlen);
+
+ outip->ip_v = IPVERSION;
+ if (tos_str)
+ outip->ip_tos = tos;
+ outip->ip_len = htons(packlen);
+ outip->ip_off = htons(off);
+ outp = (unsigned char *)(outip + 1);
+ outip->ip_dst = to->sin_addr;
+
+ outip->ip_hl = (outp - (unsigned char *)outip) >> 2;
+ ident = (getpid() & 0xffff) | 0x8000;
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp) {
+ outip->ip_p = IPPROTO_ICMP;
+ outicmp = (struct icmp *)outp;
+ outicmp->icmp_type = ICMP_ECHO;
+ outicmp->icmp_id = htons(ident);
+ outdata = (struct outdata *)(outp + 8); /* XXX magic number */
+ } else
+#endif
+ {
+ outip->ip_p = IPPROTO_UDP;
+ outudp = (struct udphdr *)outp;
+ outudp->source = htons(ident);
+ outudp->len = htons((uint16_t)(packlen - (sizeof(*outip) + optlen)));
+ outdata = (struct outdata *)(outudp + 1);
+ }
+
+ /* Get the interface address list */
+ n = ifaddrlist(&al);
+
+ /* Look for a specific device */
+ if (device != NULL) {
+ for (i = n; i > 0; --i, ++al)
+ if (strcmp(device, al->device) == 0)
+ break;
+ if (i <= 0) {
+ bb_error_msg_and_die("can't find interface %s", device);
+ }
+ }
+
+ /* Determine our source address */
+ if (source == NULL) {
+ /*
+ * If a device was specified, use the interface address.
+ * Otherwise, try to determine our source address.
+ */
+ if (device != NULL)
+ setsin(from, al->addr);
+ findsaddr(to, from);
+ } else {
+ hi = gethostinfo(source);
+ source = hi->name;
+ hi->name = NULL;
+ /*
+ * If the device was specified make sure it
+ * corresponds to the source address specified.
+ * Otherwise, use the first address (and warn if
+ * there are more than one).
+ */
+ if (device != NULL) {
+ for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap)
+ if (*ap == al->addr)
+ break;
+ if (i <= 0) {
+ bb_error_msg_and_die(
+ "%s is not on interface %s",
+ source, device);
+ }
+ setsin(from, *ap);
+ } else {
+ setsin(from, hi->addrs[0]);
+ if (hi->n > 1)
+ bb_error_msg(
+ "Warning: %s has multiple addresses; using %s",
+ source, inet_ntoa(from->sin_addr));
+ }
+ freehostinfo(hi);
+ }
+
+ outip->ip_src = from->sin_addr;
+#ifndef IP_HDRINCL
+ xbind(sndsock, (struct sockaddr *)from, sizeof(*from));
+#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, packlen);
+ (void)fflush(stderr);
+
+ for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
+ uint32_t lastaddr = 0;
+ int gotlastaddr = 0;
+ int got_there = 0;
+ int unreachable = 0;
+ int sentfirst = 0;
+
+ printf("%2d ", ttl);
+ for (probe = 0; probe < nprobes; ++probe) {
+ int cc;
+ struct timeval t1, t2;
+ struct timezone tz;
+ struct ip *ip;
+
+ if (sentfirst && pausemsecs > 0)
+ usleep(pausemsecs * 1000);
+ (void)gettimeofday(&t1, &tz);
+ send_probe(++seq, ttl, &t1);
+ ++sentfirst;
+ while ((cc = wait_for_reply(s, from, &t1)) != 0) {
+ (void)gettimeofday(&t2, &tz);
+ i = packet_ok(packet, cc, from, seq);
+ /* Skip short packet */
+ if (i == 0)
+ continue;
+ if (!gotlastaddr ||
+ from->sin_addr.s_addr != lastaddr) {
+ print(packet, cc, from);
+ lastaddr = from->sin_addr.s_addr;
+ ++gotlastaddr;
+ }
+ printf(" %.3f ms", deltaT(&t1, &t2));
+ ip = (struct ip *)packet;
+ if (op & USAGE_OP_TTL_FLAG)
+ printf(" (%d)", ip->ip_ttl);
+ if (i == -2) {
+ if (ip->ip_ttl <= 1)
+ printf(" !");
+ ++got_there;
+ break;
+ }
+ /* time exceeded in transit */
+ if (i == -1)
+ break;
+ code = i - 1;
+ switch (code) {
+
+ case ICMP_UNREACH_PORT:
+ 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-%d", pmtu);
+ break;
+
+ case ICMP_UNREACH_SRCFAIL:
+ ++unreachable;
+ printf(" !S");
+ break;
+
+ case ICMP_UNREACH_FILTER_PROHIB:
+ case ICMP_UNREACH_NET_PROHIB: /* misuse */
+ ++unreachable;
+ printf(" !A");
+ break;
+
+ case ICMP_UNREACH_HOST_PROHIB:
+ ++unreachable;
+ printf(" !C");
+ break;
+
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ ++unreachable;
+ printf(" !V");
+ break;
+
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ ++unreachable;
+ printf(" !C");
+ break;
+
+ case ICMP_UNREACH_NET_UNKNOWN:
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ ++unreachable;
+ printf(" !U");
+ break;
+
+ case ICMP_UNREACH_ISOLATED:
+ ++unreachable;
+ printf(" !I");
+ break;
+
+ case ICMP_UNREACH_TOSNET:
+ case ICMP_UNREACH_TOSHOST:
+ ++unreachable;
+ printf(" !T");
+ break;
+
+ default:
+ ++unreachable;
+ printf(" !<%d>", code);
+ break;
+ }
+ break;
+ }
+ if (cc == 0)
+ printf(" *");
+ (void)fflush(stdout);
+ }
+ putchar('\n');
+ if (got_there ||
+ (unreachable > 0 && unreachable >= nprobes - 1))
+ break;
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/Config.in b/i/pc104/initrd/conf/busybox/networking/udhcp/Config.in
new file mode 100644
index 0000000..9dd02c4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/Config.in
@@ -0,0 +1,75 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+config APP_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 APP_DHCPRELAY
+ bool "dhcprelay"
+ default n
+ depends on APP_UDHCPD
+ help
+ dhcprelay listens for dhcp requests on one or more interfaces
+ and forwards these requests to a different interface or dhcp
+ server.
+
+config APP_DUMPLEASES
+ bool "Lease display utility (dumpleases)"
+ default n
+ depends on APP_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 APP_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 lease is obtained or lost.
+
+ See http://udhcp.busybox.net for further details.
+
+config FEATURE_UDHCP_SYSLOG
+ bool "Log udhcp messages to syslog"
+ default n
+ depends on APP_UDHCPD || APP_UDHCPC
+ select FEATURE_SYSLOG
+ help
+ If not daemonized, udhcpd prints its messages to stdout/stderr.
+ If this option is selected, it will also log them to syslog.
+
+ See http://udhcp.busybox.net for further details.
+
+config FEATURE_UDHCP_DEBUG
+ bool "Compile udhcp with noisy debugging messages"
+ default n
+ depends on APP_UDHCPD || APP_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.
+
+config FEATURE_RFC3397
+ bool "Support for RFC3397 domain search (experimental)"
+ default n
+ depends on APP_UDHCPD || APP_UDHCPC
+ help
+ If selected, both client and server will support passing of domain
+ search lists via option 119, specified in RFC3397.
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/Kbuild b/i/pc104/initrd/conf/busybox/networking/udhcp/Kbuild
new file mode 100644
index 0000000..57d2511
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/Kbuild
@@ -0,0 +1,19 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+#
+
+lib-y:=
+lib-$(CONFIG_APP_UDHCPC) += common.o options.o packet.o pidfile.o \
+ signalpipe.o socket.o
+lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o pidfile.o \
+ signalpipe.o socket.o
+lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \
+ script.o
+lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \
+ serverpacket.o static_leases.o
+lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o
+lib-$(CONFIG_APP_DHCPRELAY) += dhcprelay.o
+lib-$(CONFIG_FEATURE_RFC3397) += domain_codec.o
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/arpping.c b/i/pc104/initrd/conf/busybox/networking/udhcp/arpping.c
new file mode 100644
index 0000000..f78fd3f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/arpping.c
@@ -0,0 +1,114 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * arpping.c
+ *
+ * Mostly stolen from: dhcpcd - DHCP client daemon
+ * by Yoichi Hariguchi <yoichi@fore.com>
+ */
+
+#include <netinet/if_ether.h>
+#include <net/if_arp.h>
+
+#include "common.h"
+#include "dhcpd.h"
+
+
+struct arpMsg {
+ /* Ethernet header */
+ uint8_t h_dest[6]; /* destination ether addr */
+ uint8_t h_source[6]; /* source ether addr */
+ uint16_t h_proto; /* packet type ID field */
+
+ /* ARP packet */
+ uint16_t htype; /* hardware type (must be ARPHRD_ETHER) */
+ uint16_t ptype; /* protocol type (must be ETH_P_IP) */
+ uint8_t hlen; /* hardware address length (must be 6) */
+ uint8_t plen; /* protocol address length (must be 4) */
+ uint16_t operation; /* ARP opcode */
+ uint8_t sHaddr[6]; /* sender's hardware address */
+ uint8_t sInaddr[4]; /* sender's IP address */
+ uint8_t tHaddr[6]; /* target's hardware address */
+ uint8_t tInaddr[4]; /* target's IP address */
+ uint8_t pad[18]; /* pad for min. Ethernet payload (60 bytes) */
+} ATTRIBUTE_PACKED;
+
+/* 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(uint32_t yiaddr, uint32_t ip, uint8_t *mac, char *interface)
+{
+ int timeout = 2;
+ 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;
+
+
+ s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
+ if (s == -1) {
+ bb_perror_msg(bb_msg_can_not_create_raw_socket);
+ return -1;
+ }
+
+ if (setsockopt_broadcast(s) == -1) {
+ bb_perror_msg("cannot setsocketopt on raw socket");
+ close(s);
+ return -1;
+ }
+
+ /* send arp request */
+ memset(&arp, 0, sizeof(arp));
+ memcpy(arp.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */
+ memcpy(arp.h_source, mac, 6); /* MAC SA */
+ arp.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;
+ prevTime = uptime();
+ 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) {
+ bb_perror_msg("error on ARPING request");
+ 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) &&
+ memcmp(arp.tHaddr, mac, 6) == 0 &&
+ *((uint32_t *) arp.sInaddr) == yiaddr) {
+ DEBUG("Valid arp reply received for this address");
+ rv = 0;
+ break;
+ }
+ }
+ timeout -= uptime() - prevTime;
+ prevTime = uptime();
+ }
+ close(s);
+ DEBUG("%salid arp replies for this address", rv ? "No v" : "V");
+ return rv;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/clientpacket.c b/i/pc104/initrd/conf/busybox/networking/udhcp/clientpacket.c
new file mode 100644
index 0000000..69d7f2d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/clientpacket.c
@@ -0,0 +1,224 @@
+/* vi: set sw=4 ts=4: */
+/* clientpacket.c
+ *
+ * Packet generation and dispatching functions for the DHCP client.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <features.h>
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
+#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"
+#include "dhcpd.h"
+#include "dhcpc.h"
+#include "options.h"
+
+
+/* Create a random xid */
+unsigned long random_xid(void)
+{
+ static int initialized;
+ if (!initialized) {
+ unsigned long seed;
+
+ if (open_read_close("/dev/urandom", &seed, sizeof(seed)) < 0) {
+ bb_info_msg("Cannot load seed "
+ "from /dev/urandom: %s", strerror(errno));
+ seed = time(0);
+ }
+ srand(seed);
+ initialized++;
+ }
+ return rand();
+}
+
+
+/* initialize a packet with the proper defaults */
+static void init_packet(struct dhcpMessage *packet, char type)
+{
+ udhcp_init_header(packet, type);
+ memcpy(packet->chaddr, client_config.arp, 6);
+ if (client_config.clientid)
+ add_option_string(packet->options, client_config.clientid);
+ if (client_config.hostname) add_option_string(packet->options, client_config.hostname);
+ if (client_config.fqdn) add_option_string(packet->options, client_config.fqdn);
+ add_option_string(packet->options, client_config.vendorclass);
+}
+
+
+/* Add a parameter 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; dhcp_options[i].code; i++)
+ if (dhcp_options[i].flags & OPTION_REQ)
+ packet->options[end + OPT_DATA + len++] = dhcp_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);
+ bb_info_msg("Sending discover...");
+ return udhcp_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;
+ bb_info_msg("Sending select for %s...", inet_ntoa(addr));
+ return udhcp_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);
+ bb_info_msg("Sending renew...");
+ if (server)
+ ret = udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
+ else ret = udhcp_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);
+
+ bb_info_msg("Sending release...");
+ return udhcp_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;
+ uint32_t source, dest;
+ uint16_t check;
+
+ memset(&packet, 0, sizeof(struct udp_dhcp_packet));
+ bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet));
+ if (bytes < 0) {
+ DEBUG("Cannot 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("Message too short, ignoring");
+ return -2;
+ }
+
+ if (bytes < ntohs(packet.ip.tot_len)) {
+ DEBUG("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) != (uint16_t)(bytes - sizeof(packet.ip))
+ ) {
+ DEBUG("Unrelated/bogus packet");
+ return -2;
+ }
+
+ /* check IP checksum */
+ check = packet.ip.check;
+ packet.ip.check = 0;
+ if (check != udhcp_checksum(&(packet.ip), sizeof(packet.ip))) {
+ DEBUG("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 != udhcp_checksum(&packet, bytes)) {
+ bb_error_msg("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) {
+ bb_error_msg("received bogus message (bad magic) - ignoring");
+ return -2;
+ }
+ DEBUG("oooooh!!! got some!");
+ return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
+
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/clientsocket.c b/i/pc104/initrd/conf/busybox/networking/udhcp/clientsocket.c
new file mode 100644
index 0000000..541f883
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/clientsocket.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * clientsocket.c -- DHCP client socket creation
+ *
+ * udhcp 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 <features.h>
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
+#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 raw_socket(int ifindex)
+{
+ int fd;
+ struct sockaddr_ll sock;
+
+ DEBUG("Opening raw socket on ifindex %d", ifindex);
+ fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+
+ sock.sll_family = AF_PACKET;
+ sock.sll_protocol = htons(ETH_P_IP);
+ sock.sll_ifindex = ifindex;
+ xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
+
+ return fd;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/common.c b/i/pc104/initrd/conf/busybox/networking/udhcp/common.c
new file mode 100644
index 0000000..3704ba7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/common.c
@@ -0,0 +1,61 @@
+/* vi: set sw=4 ts=4: */
+/* common.c
+ *
+ * Functions for debugging and logging as well as some other
+ * simple helper functions.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> 2001-2003
+ * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <syslog.h>
+
+#include "common.h"
+
+
+long uptime(void)
+{
+ struct sysinfo info;
+ sysinfo(&info);
+ return info.uptime;
+}
+
+void udhcp_background(const char *pidfile)
+{
+#ifdef __uClinux__
+ bb_error_msg("cannot background in uclinux (yet)");
+#else /* __uClinux__ */
+ int pid_fd;
+
+ /* hold lock during fork. */
+ pid_fd = pidfile_acquire(pidfile);
+ setsid();
+ xdaemon(0, 0);
+ logmode &= ~LOGMODE_STDIO;
+ pidfile_write_release(pid_fd);
+#endif /* __uClinux__ */
+}
+
+void udhcp_start_log_and_pid(const char *pidfile)
+{
+ int pid_fd;
+
+ /* Make sure our syslog fd isn't overwritten */
+ bb_sanitize_stdio();
+
+ /* do some other misc startup stuff while we are here to save bytes */
+ pid_fd = pidfile_acquire(pidfile);
+ pidfile_write_release(pid_fd);
+
+ /* equivelent of doing a fflush after every \n */
+ setlinebuf(stdout);
+
+ if (ENABLE_FEATURE_UDHCP_SYSLOG) {
+ openlog(applet_name, LOG_PID, LOG_LOCAL0);
+ logmode |= LOGMODE_SYSLOG;
+ }
+
+ bb_info_msg("%s (v%s) started", applet_name, BB_VER);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/common.h b/i/pc104/initrd/conf/busybox/networking/udhcp/common.h
new file mode 100644
index 0000000..a80691b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/common.h
@@ -0,0 +1,104 @@
+/* vi: set sw=4 ts=4: */
+/* common.h
+ *
+ * Russ Dill <Russ.Dill@asu.edu> September 2001
+ * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+#include "busybox.h"
+
+#define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script"
+
+#define COMBINED_BINARY
+
+
+/*** packet.h ***/
+
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+
+struct dhcpMessage {
+ uint8_t op;
+ uint8_t htype;
+ uint8_t hlen;
+ uint8_t hops;
+ uint32_t xid;
+ uint16_t secs;
+ uint16_t flags;
+ uint32_t ciaddr;
+ uint32_t yiaddr;
+ uint32_t siaddr;
+ uint32_t giaddr;
+ uint8_t chaddr[16];
+ uint8_t sname[64];
+ uint8_t file[128];
+ uint32_t cookie;
+ uint8_t options[308]; /* 312 - cookie */
+};
+
+struct udp_dhcp_packet {
+ struct iphdr ip;
+ struct udphdr udp;
+ struct dhcpMessage data;
+};
+
+void udhcp_init_header(struct dhcpMessage *packet, char type);
+int udhcp_get_packet(struct dhcpMessage *packet, int fd);
+uint16_t udhcp_checksum(void *addr, int count);
+int udhcp_raw_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex);
+int udhcp_kernel_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port);
+
+
+/**/
+
+void udhcp_background(const char *pidfile);
+void udhcp_start_log_and_pid(const char *pidfile);
+
+void udhcp_run_script(struct dhcpMessage *packet, const char *name);
+
+// Still need to clean these up...
+
+/* from pidfile.h */
+#define pidfile_acquire udhcp_pidfile_acquire
+#define pidfile_write_release udhcp_pidfile_write_release
+/* from options.h */
+#define get_option udhcp_get_option
+#define end_option udhcp_end_option
+#define add_option_string udhcp_add_option_string
+#define add_simple_option udhcp_add_simple_option
+#define option_lengths udhcp_option_lengths
+/* from socket.h */
+#define listen_socket udhcp_listen_socket
+#define read_interface udhcp_read_interface
+/* from dhcpc.h */
+#define client_config udhcp_client_config
+/* from dhcpd.h */
+#define server_config udhcp_server_config
+
+long uptime(void);
+void udhcp_sp_setup(void);
+int udhcp_sp_fd_set(fd_set *rfds, int extra_fd);
+int udhcp_sp_read(fd_set *rfds);
+int raw_socket(int ifindex);
+int read_interface(const char *interface, int *ifindex, uint32_t *addr, uint8_t *arp);
+int listen_socket(uint32_t ip, int port, const char *inf);
+int pidfile_acquire(const char *pidfile);
+void pidfile_write_release(int pid_fd);
+int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *arp, char *interface);
+
+#if ENABLE_FEATURE_UDHCP_DEBUG
+# define DEBUG(str, args...) bb_info_msg(str, ## args)
+#else
+# define DEBUG(str, args...) do {;} while (0)
+#endif
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.c b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.c
new file mode 100644
index 0000000..a59c5db
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.c
@@ -0,0 +1,509 @@
+/* vi: set sw=4 ts=4: */
+/* dhcpc.c
+ *
+ * udhcp DHCP client
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include <getopt.h>
+
+#include "common.h"
+#include "dhcpd.h"
+#include "dhcpc.h"
+#include "options.h"
+
+
+static int state;
+/* Something is definitely wrong here. IPv4 addresses
+ * in variables of type long?? BTW, we use inet_ntoa()
+ * in the code. Manpage says that struct in_addr has a member of type long (!)
+ * which holds IPv4 address, and the struct is passed by value (!!)
+ */
+static unsigned long requested_ip; /* = 0 */
+static uint32_t 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;
+
+struct client_config_t client_config;
+
+
+/* just a little helper */
+static void change_mode(int new_mode)
+{
+ DEBUG("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)
+{
+ bb_info_msg("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 */
+ udhcp_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;
+ bb_info_msg("Unicasting a release of %s to %s",
+ inet_ntoa(temp_addr), buffer);
+ send_release(server_addr, requested_ip); /* unicast */
+ udhcp_run_script(NULL, "deconfig");
+ }
+ bb_info_msg("Entering released state");
+
+ change_mode(LISTEN_NONE);
+ state = RELEASED;
+ timeout = 0x7fffffff;
+}
+
+
+static void client_background(void)
+{
+ udhcp_background(client_config.pidfile);
+ client_config.foreground = 1; /* Do not fork again. */
+ client_config.background_if_no_lease = 0;
+}
+
+
+static uint8_t* alloc_dhcp_option(int code, const char *str, int extra)
+{
+ uint8_t *storage;
+ int len = strlen(str);
+ if (len > 255) len = 255;
+ storage = xzalloc(len + extra + OPT_DATA);
+ storage[OPT_CODE] = code;
+ storage[OPT_LEN] = len + extra;
+ memcpy(storage + extra + OPT_DATA, str, len);
+ return storage;
+}
+
+
+int udhcpc_main(int argc, char *argv[]);
+int udhcpc_main(int argc, char *argv[])
+{
+ uint8_t *temp, *message;
+ char *str_c, *str_V, *str_h, *str_F, *str_r, *str_T, *str_t;
+ unsigned long t1 = 0, t2 = 0, xid = 0;
+ unsigned long start = 0, lease = 0;
+ long now;
+ unsigned opt;
+ int max_fd;
+ int sig;
+ int retval;
+ int len;
+ int no_clientid = 0;
+ fd_set rfds;
+ struct timeval tv;
+ struct dhcpMessage packet;
+ struct in_addr temp_addr;
+
+ enum {
+ OPT_c = 1 << 0,
+ OPT_C = 1 << 1,
+ OPT_V = 1 << 2,
+ OPT_f = 1 << 3,
+ OPT_b = 1 << 4,
+ OPT_H = 1 << 5,
+ OPT_h = 1 << 6,
+ OPT_F = 1 << 7,
+ OPT_i = 1 << 8,
+ OPT_n = 1 << 9,
+ OPT_p = 1 << 10,
+ OPT_q = 1 << 11,
+ OPT_R = 1 << 12,
+ OPT_r = 1 << 13,
+ OPT_s = 1 << 14,
+ OPT_T = 1 << 15,
+ OPT_t = 1 << 16,
+ OPT_v = 1 << 17,
+ };
+#if ENABLE_GETOPT_LONG
+ static const struct option arg_options[] = {
+ { "clientid", required_argument, 0, 'c' },
+ { "clientid-none", no_argument, 0, 'C' },
+ { "vendorclass", required_argument, 0, 'V' },
+ { "foreground", no_argument, 0, 'f' },
+ { "background", no_argument, 0, 'b' },
+ { "hostname", required_argument, 0, 'H' },
+ { "hostname", required_argument, 0, 'h' },
+ { "fqdn", required_argument, 0, 'F' },
+ { "interface", required_argument, 0, 'i' },
+ { "now", no_argument, 0, 'n' },
+ { "pidfile", required_argument, 0, 'p' },
+ { "quit", no_argument, 0, 'q' },
+ { "release", no_argument, 0, 'R' },
+ { "request", required_argument, 0, 'r' },
+ { "script", required_argument, 0, 's' },
+ { "timeout", required_argument, 0, 'T' },
+ { "version", no_argument, 0, 'v' },
+ { "retries", required_argument, 0, 't' },
+ { 0, 0, 0, 0 }
+ };
+#endif
+ /* Default options. */
+ client_config.interface = "eth0";
+ client_config.script = DEFAULT_SCRIPT;
+ client_config.retries = 3;
+ client_config.timeout = 3;
+
+ /* Parse command line */
+ opt_complementary = "?:c--C:C--c" // mutually exclusive
+ ":hH:Hh"; // -h and -H are the same
+#if ENABLE_GETOPT_LONG
+ applet_long_options = arg_options;
+#endif
+ opt = getopt32(argc, argv, "c:CV:fbH:h:F:i:np:qRr:s:T:t:v",
+ &str_c, &str_V, &str_h, &str_h, &str_F,
+ &client_config.interface, &client_config.pidfile, &str_r,
+ &client_config.script, &str_T, &str_t
+ );
+
+ if (opt & OPT_c)
+ client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, str_c, 0);
+ if (opt & OPT_C)
+ no_clientid = 1;
+ if (opt & OPT_V)
+ client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0);
+ if (opt & OPT_f)
+ client_config.foreground = 1;
+ if (opt & OPT_b)
+ client_config.background_if_no_lease = 1;
+ if (opt & OPT_h)
+ client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0);
+ if (opt & OPT_F) {
+ client_config.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3);
+ /* Flags: 0000NEOS
+ S: 1 => Client requests Server to update A RR in DNS as well as PTR
+ O: 1 => Server indicates to client that DNS has been updated regardless
+ E: 1 => Name data is DNS format, i.e. <4>host<6>domain<4>com<0> not "host.domain.com"
+ N: 1 => Client requests Server to not update DNS
+ */
+ client_config.fqdn[OPT_DATA + 0] = 0x1;
+ /* client_config.fqdn[OPT_DATA + 1] = 0; - redundant */
+ /* client_config.fqdn[OPT_DATA + 2] = 0; - redundant */
+ }
+ // if (opt & OPT_i) client_config.interface = ...
+ if (opt & OPT_n)
+ client_config.abort_if_no_lease = 1;
+ // if (opt & OPT_p) client_config.pidfile = ...
+ if (opt & OPT_q)
+ client_config.quit_after_lease = 1;
+ if (opt & OPT_R)
+ client_config.release_on_quit = 1;
+ if (opt & OPT_r)
+ requested_ip = inet_addr(str_r);
+ // if (opt & OPT_s) client_config.script = ...
+ if (opt & OPT_T)
+ client_config.timeout = xatoi_u(str_T);
+ if (opt & OPT_t)
+ client_config.retries = xatoi_u(str_t);
+ if (opt & OPT_v) {
+ printf("version %s\n\n", BB_VER);
+ return 0;
+ }
+
+ /* Start the log, sanitize fd's, and write a pid file */
+ udhcp_start_log_and_pid(client_config.pidfile);
+
+ if (read_interface(client_config.interface, &client_config.ifindex,
+ NULL, client_config.arp) < 0)
+ return 1;
+
+ /* if not set, and not suppressed, setup the default client ID */
+ if (!client_config.clientid && !no_clientid) {
+ client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7);
+ client_config.clientid[OPT_DATA] = 1;
+ memcpy(client_config.clientid + OPT_DATA+1, client_config.arp, 6);
+ }
+
+ if (!client_config.vendorclass)
+ client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, "udhcp "BB_VER, 0);
+
+ /* setup the signal pipe */
+ udhcp_sp_setup();
+
+ state = INIT_SELECTING;
+ udhcp_run_script(NULL, "deconfig");
+ change_mode(LISTEN_RAW);
+
+ for (;;) {
+ tv.tv_sec = timeout - uptime();
+ tv.tv_usec = 0;
+
+ 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);
+ }
+ max_fd = udhcp_sp_fd_set(&rfds, fd);
+
+ if (tv.tv_sec > 0) {
+ DEBUG("Waiting on select...");
+ retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
+ } else retval = 0; /* If we already timed out, fall through */
+
+ now = uptime();
+ if (retval == 0) {
+ /* timeout dropped to zero */
+ switch (state) {
+ case INIT_SELECTING:
+ if (packet_num < client_config.retries) {
+ if (packet_num == 0)
+ xid = random_xid();
+
+ /* send discover packet */
+ send_discover(xid, requested_ip); /* broadcast */
+
+ timeout = now + client_config.timeout;
+ packet_num++;
+ } else {
+ udhcp_run_script(NULL, "leasefail");
+ if (client_config.background_if_no_lease) {
+ bb_info_msg("No lease, forking to background");
+ client_background();
+ } else if (client_config.abort_if_no_lease) {
+ bb_info_msg("No lease, failing");
+ return 1;
+ }
+ /* wait to try again */
+ packet_num = 0;
+ timeout = now + 60;
+ }
+ break;
+ case RENEW_REQUESTED:
+ case REQUESTING:
+ if (packet_num < client_config.retries) {
+ /* 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) udhcp_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("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("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;
+ bb_info_msg("Lease lost, entering init state");
+ udhcp_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 = udhcp_get_packet(&packet, fd);
+ else len = get_raw_packet(&packet, fd);
+
+ if (len == -1 && errno != EINTR) {
+ DEBUG("error on read, %s, reopening socket", strerror(errno));
+ change_mode(listen_mode); /* just close and reopen */
+ }
+ if (len < 0) continue;
+
+ if (packet.xid != xid) {
+ DEBUG("Ignoring XID %lx (our xid is %lx)",
+ (unsigned long) packet.xid, xid);
+ continue;
+ }
+
+ /* Ignore packets that aren't for us */
+ if (memcmp(packet.chaddr, client_config.arp, 6)) {
+ DEBUG("Packet does not have our chaddr - ignoring");
+ continue;
+ }
+
+ if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
+ bb_error_msg("cannot get option from packet - ignoring");
+ continue;
+ }
+
+ switch (state) {
+ case INIT_SELECTING:
+ /* Must be a DHCPOFFER to one of our xid's */
+ if (*message == DHCPOFFER) {
+ temp = get_option(&packet, DHCP_SERVER_ID);
+ if (temp) {
+ /* can be misaligned, thus memcpy */
+ memcpy(&server_addr, temp, 4);
+ xid = packet.xid;
+ requested_ip = packet.yiaddr;
+
+ /* enter requesting state */
+ state = REQUESTING;
+ timeout = now;
+ packet_num = 0;
+ } else {
+ bb_error_msg("no server ID in message");
+ }
+ }
+ break;
+ case RENEW_REQUESTED:
+ case REQUESTING:
+ case RENEWING:
+ case REBINDING:
+ if (*message == DHCPACK) {
+ temp = get_option(&packet, DHCP_LEASE_TIME);
+ if (!temp) {
+ bb_error_msg("no lease time with ACK, using 1 hour lease");
+ lease = 60 * 60;
+ } else {
+ /* can be misaligned, thus memcpy */
+ 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;
+ bb_info_msg("Lease of %s obtained, lease time %ld",
+ inet_ntoa(temp_addr), lease);
+ start = now;
+ timeout = t1 + start;
+ requested_ip = packet.yiaddr;
+ udhcp_run_script(&packet,
+ ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
+
+ state = BOUND;
+ change_mode(LISTEN_NONE);
+ if (client_config.quit_after_lease) {
+ if (client_config.release_on_quit)
+ perform_release();
+ return 0;
+ }
+ if (!client_config.foreground)
+ client_background();
+
+ } else if (*message == DHCPNAK) {
+ /* return to init state */
+ bb_info_msg("Received DHCP NAK");
+ udhcp_run_script(&packet, "nak");
+ if (state != REQUESTING)
+ udhcp_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 && (sig = udhcp_sp_read(&rfds))) {
+ switch (sig) {
+ case SIGUSR1:
+ perform_renew();
+ break;
+ case SIGUSR2:
+ perform_release();
+ break;
+ case SIGTERM:
+ bb_info_msg("Received SIGTERM");
+ if (client_config.release_on_quit)
+ perform_release();
+ return 0;
+ }
+ } else if (retval == -1 && errno == EINTR) {
+ /* a signal was caught */
+ } else {
+ /* An error occured */
+ bb_perror_msg("select");
+ }
+
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.h b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.h
new file mode 100644
index 0000000..4ddd121
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.h
@@ -0,0 +1,50 @@
+/* vi: set sw=4 ts=4: */
+/* 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 {
+ /* TODO: combine flag fields into single "unsigned opt" */
+ /* (can be set directly to the result of getopt32) */
+ char foreground; /* Do not fork */
+ char quit_after_lease; /* Quit after obtaining lease */
+ char release_on_quit; /* perform release on quit */
+ char abort_if_no_lease; /* Abort if no lease */
+ char background_if_no_lease; /* Fork to background if no lease */
+ const char *interface; /* The name of the interface to use */
+ char *pidfile; /* Optionally store the process ID */
+ const char *script; /* User script to run at dhcp events */
+ uint8_t *clientid; /* Optional client id to use */
+ uint8_t *vendorclass; /* Optional vendor class-id to use */
+ uint8_t *hostname; /* Optional hostname to use */
+ uint8_t *fqdn; /* Optional fully qualified domain name to use */
+ int ifindex; /* Index number of the interface to use */
+ int retries; /* Max number of request packets */
+ int timeout; /* Number of seconds to try to get a lease */
+ uint8_t arp[6]; /* Our arp address */
+};
+
+extern struct client_config_t client_config;
+
+
+/*** 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/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.c b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.c
new file mode 100644
index 0000000..90d8f0d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.c
@@ -0,0 +1,223 @@
+/* vi: set sw=4 ts=4: */
+/* 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
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+#include "options.h"
+
+
+/* globals */
+struct dhcpOfferedAddr *leases;
+struct server_config_t server_config;
+
+
+int udhcpd_main(int argc, char *argv[]);
+int udhcpd_main(int argc, char *argv[])
+{
+ fd_set rfds;
+ struct timeval tv;
+ int server_socket = -1, bytes, retval, max_sock;
+ struct dhcpMessage packet;
+ uint8_t *state, *server_id, *requested;
+ uint32_t server_id_align, requested_align, static_lease_ip;
+ unsigned long timeout_end, num_ips;
+ struct option_set *option;
+ struct dhcpOfferedAddr *lease, static_lease;
+
+ read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]);
+
+ /* Start the log, sanitize fd's, and write a pid file */
+ udhcp_start_log_and_pid(server_config.pidfile);
+
+ 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) + 1;
+ if (server_config.max_leases > num_ips) {
+ bb_error_msg("max_leases value (%lu) not sane, "
+ "setting to %lu instead",
+ server_config.max_leases, num_ips);
+ server_config.max_leases = num_ips;
+ }
+
+ leases = xzalloc(server_config.max_leases * sizeof(struct dhcpOfferedAddr));
+ read_leases(server_config.lease_file);
+
+ if (read_interface(server_config.interface, &server_config.ifindex,
+ &server_config.server, server_config.arp) < 0)
+ return 1;
+
+ if (!ENABLE_FEATURE_UDHCP_DEBUG)
+ udhcp_background(server_config.pidfile); /* hold lock during fork. */
+
+ /* Setup the signal pipe */
+ udhcp_sp_setup();
+
+ timeout_end = time(0) + server_config.auto_time;
+ while (1) { /* loop until universe collapses */
+
+ if (server_socket < 0) {
+ server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface);
+ }
+
+ max_sock = udhcp_sp_fd_set(&rfds, server_socket);
+ 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) {
+ 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("error on select");
+ continue;
+ }
+
+ switch (udhcp_sp_read(&rfds)) {
+ case SIGUSR1:
+ bb_info_msg("Received a SIGUSR1");
+ write_leases();
+ /* why not just reset the timeout, eh */
+ timeout_end = time(0) + server_config.auto_time;
+ continue;
+ case SIGTERM:
+ bb_info_msg("Received a SIGTERM");
+ return 0;
+ case 0: break; /* no signal */
+ default: continue; /* signal or error (probably EINTR) */
+ }
+
+ if ((bytes = udhcp_get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */
+ if (bytes == -1 && errno != EINTR) {
+ DEBUG("error on read, %s, reopening socket", strerror(errno));
+ close(server_socket);
+ server_socket = -1;
+ }
+ continue;
+ }
+
+ if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
+ bb_error_msg("cannot get option from packet, ignoring");
+ continue;
+ }
+
+ /* Look for a static lease */
+ static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr);
+
+ if (static_lease_ip) {
+ bb_info_msg("Found static lease: %x", static_lease_ip);
+
+ memcpy(&static_lease.chaddr, &packet.chaddr, 16);
+ static_lease.yiaddr = static_lease_ip;
+ static_lease.expires = 0;
+
+ lease = &static_lease;
+
+ } else {
+ lease = find_lease_by_chaddr(packet.chaddr);
+ }
+
+ switch (state[0]) {
+ case DHCPDISCOVER:
+ DEBUG("Received DISCOVER");
+
+ if (sendOffer(&packet) < 0) {
+ bb_error_msg("send OFFER failed");
+ }
+ break;
+ case DHCPREQUEST:
+ DEBUG("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) {
+ if (server_id) {
+ /* SELECTING State */
+ DEBUG("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("Received DECLINE");
+ if (lease) {
+ memset(lease->chaddr, 0, 16);
+ lease->expires = time(0) + server_config.decline_time;
+ }
+ break;
+ case DHCPRELEASE:
+ DEBUG("Received RELEASE");
+ if (lease) lease->expires = time(0);
+ break;
+ case DHCPINFORM:
+ DEBUG("Received INFORM");
+ send_inform(&packet);
+ break;
+ default:
+ bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]);
+ }
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.h b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.h
new file mode 100644
index 0000000..40959e4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.h
@@ -0,0 +1,190 @@
+/* vi: set sw=4 ts=4: */
+/* dhcpd.h */
+#ifndef _DHCPD_H
+#define _DHCPD_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 */
+#define LEASES_FILE "/var/lib/misc/udhcpd.leases"
+
+/* 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_FQDN 0x51
+
+#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 (uint8_t *) "\xff\xff\xff\xff\xff\xff"
+#define OPT_CODE 0
+#define OPT_LEN 1
+#define OPT_DATA 2
+
+struct option_set {
+ uint8_t *data;
+ struct option_set *next;
+};
+
+struct static_lease {
+ uint8_t *mac;
+ uint32_t *ip;
+ struct static_lease *next;
+};
+
+struct server_config_t {
+ uint32_t server; /* Our IP, in network order */
+ uint32_t start; /* Start address of leases, network order */
+ uint32_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 */
+ uint8_t 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 */
+ uint32_t siaddr; /* next server bootp option */
+ char *sname; /* bootp server name */
+ char *boot_file; /* bootp boot file option */
+ struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */
+};
+
+extern struct server_config_t server_config;
+extern struct dhcpOfferedAddr *leases;
+
+
+/*** leases.h ***/
+
+struct dhcpOfferedAddr {
+ uint8_t chaddr[16];
+ uint32_t yiaddr; /* network order */
+ uint32_t expires; /* host order */
+};
+
+extern uint8_t blank_chaddr[];
+
+void clear_lease(uint8_t *chaddr, uint32_t yiaddr);
+struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease);
+int lease_expired(struct dhcpOfferedAddr *lease);
+struct dhcpOfferedAddr *oldest_expired_lease(void);
+struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr);
+struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr);
+uint32_t find_address(int check_expired);
+
+
+/*** static_leases.h ***/
+
+/* Config file will pass static lease info to this function which will add it
+ * to a data structure that can be searched later */
+int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip);
+/* Check to see if a mac has an associated static lease */
+uint32_t getIpByMac(struct static_lease *lease_struct, void *arg);
+/* Check to see if an ip is reserved as a static ip */
+uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip);
+/* Print out static leases just to check what's going on (debug code) */
+void printStaticLeases(struct static_lease **lease_struct);
+
+
+/*** serverpacket.h ***/
+
+int sendOffer(struct dhcpMessage *oldpacket);
+int sendNAK(struct dhcpMessage *oldpacket);
+int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr);
+int send_inform(struct dhcpMessage *oldpacket);
+
+
+/*** 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);
+struct option_set *find_option(struct option_set *opt_list, char code);
+
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/dhcprelay.c b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcprelay.c
new file mode 100644
index 0000000..9bb7aea
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcprelay.c
@@ -0,0 +1,338 @@
+/* vi: set sw=4 ts=4: */
+/* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com>
+ *
+ * Licensed under GPL v2, see file LICENSE in this tarball for details.
+ *
+ * DHCP Relay for 'DHCPv4 Configuration of IPSec Tunnel Mode' support
+ * Copyright (C) 2002 Mario Strasser <mast@gmx.net>,
+ * Zuercher Hochschule Winterthur,
+ * Netbeat AG
+ * Upstream has GPL v2 or later
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+#include "options.h"
+
+/* constants */
+#define SELECT_TIMEOUT 5 /* select timeout in sec. */
+#define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */
+#define MAX_INTERFACES 9
+
+
+/* This list holds information about clients. The xid_* functions manipulate this list. */
+static struct xid_item {
+ uint32_t xid;
+ struct sockaddr_in ip;
+ int client;
+ time_t timestamp;
+ struct xid_item *next;
+} dhcprelay_xid_list = {0, {0}, 0, 0, NULL};
+
+
+static struct xid_item * xid_add(uint32_t xid, struct sockaddr_in *ip, int client)
+{
+ struct xid_item *item;
+
+ /* create new xid entry */
+ item = xmalloc(sizeof(struct xid_item));
+
+ /* add xid entry */
+ item->ip = *ip;
+ item->xid = xid;
+ item->client = client;
+ item->timestamp = time(NULL);
+ item->next = dhcprelay_xid_list.next;
+ dhcprelay_xid_list.next = item;
+
+ return item;
+}
+
+
+static void xid_expire(void)
+{
+ struct xid_item *item = dhcprelay_xid_list.next;
+ struct xid_item *last = &dhcprelay_xid_list;
+ time_t current_time = time(NULL);
+
+ while (item != NULL) {
+ if ((current_time-item->timestamp) > MAX_LIFETIME) {
+ last->next = item->next;
+ free(item);
+ item = last->next;
+ } else {
+ last = item;
+ item = item->next;
+ }
+ }
+}
+
+static struct xid_item * xid_find(uint32_t xid)
+{
+ struct xid_item *item = dhcprelay_xid_list.next;
+ while (item != NULL) {
+ if (item->xid == xid) {
+ return item;
+ }
+ item = item->next;
+ }
+ return NULL;
+}
+
+static void xid_del(uint32_t xid)
+{
+ struct xid_item *item = dhcprelay_xid_list.next;
+ struct xid_item *last = &dhcprelay_xid_list;
+ while (item != NULL) {
+ if (item->xid == xid) {
+ last->next = item->next;
+ free(item);
+ item = last->next;
+ } else {
+ last = item;
+ item = item->next;
+ }
+ }
+}
+
+
+/**
+ * get_dhcp_packet_type - gets the message type of a dhcp packet
+ * p - pointer to the dhcp packet
+ * returns the message type on success, -1 otherwise
+ */
+static int get_dhcp_packet_type(struct dhcpMessage *p)
+{
+ uint8_t *op;
+
+ /* it must be either a BOOTREQUEST or a BOOTREPLY */
+ if (p->op != BOOTREQUEST && p->op != BOOTREPLY)
+ return -1;
+ /* get message type option */
+ op = get_option(p, DHCP_MESSAGE_TYPE);
+ if (op != NULL)
+ return op[0];
+ return -1;
+}
+
+/**
+ * signal_handler - handles signals ;-)
+ * sig - sent signal
+ */
+static int dhcprelay_stopflag;
+static void dhcprelay_signal_handler(int sig)
+{
+ dhcprelay_stopflag = 1;
+}
+
+/**
+ * get_client_devices - parses the devices list
+ * dev_list - comma separated list of devices
+ * returns array
+ */
+static char ** get_client_devices(char *dev_list, int *client_number)
+{
+ char *s, *list, **client_dev;
+ int i, cn;
+
+ /* copy list */
+ list = xstrdup(dev_list);
+ if (list == NULL) return NULL;
+
+ /* get number of items */
+ for (s = dev_list, cn = 1; *s; s++)
+ if (*s == ',')
+ cn++;
+
+ client_dev = xzalloc(cn * sizeof(*client_dev));
+
+ /* parse list */
+ s = strtok(list, ",");
+ i = 0;
+ while (s != NULL) {
+ client_dev[i++] = xstrdup(s);
+ s = strtok(NULL, ",");
+ }
+
+ /* free copy and exit */
+ free(list);
+ *client_number = cn;
+ return client_dev;
+}
+
+
+/* Creates listen sockets (in fds) and returns the number allocated. */
+static int init_sockets(char **client, int num_clients,
+ char *server, int *fds, int *max_socket)
+{
+ int i;
+
+ /* talk to real server on bootps */
+ fds[0] = listen_socket(htonl(INADDR_ANY), 67, server);
+ *max_socket = fds[0];
+
+ /* array starts at 1 since server is 0 */
+ num_clients++;
+
+ for (i=1; i < num_clients; i++) {
+ /* listen for clients on bootps */
+ fds[i] = listen_socket(htonl(INADDR_ANY), 67, client[i-1]);
+ if (fds[i] > *max_socket) *max_socket = fds[i];
+ }
+
+ return i;
+}
+
+
+/**
+ * pass_on() - forwards dhcp packets from client to server
+ * p - packet to send
+ * client - number of the client
+ */
+static void pass_on(struct dhcpMessage *p, int packet_len, int client, int *fds,
+ struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
+{
+ int res, type;
+ struct xid_item *item;
+
+ /* check packet_type */
+ type = get_dhcp_packet_type(p);
+ if (type != DHCPDISCOVER && type != DHCPREQUEST
+ && type != DHCPDECLINE && type != DHCPRELEASE
+ && type != DHCPINFORM
+ ) {
+ return;
+ }
+
+ /* create new xid entry */
+ item = xid_add(p->xid, client_addr, client);
+
+ /* forward request to LAN (server) */
+ res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr,
+ sizeof(struct sockaddr_in));
+ if (res != packet_len) {
+ bb_perror_msg("pass_on");
+ return;
+ }
+}
+
+/**
+ * pass_back() - forwards dhcp packets from server to client
+ * p - packet to send
+ */
+static void pass_back(struct dhcpMessage *p, int packet_len, int *fds)
+{
+ int res, type;
+ struct xid_item *item;
+
+ /* check xid */
+ item = xid_find(p->xid);
+ if (!item) {
+ return;
+ }
+
+ /* check packet type */
+ type = get_dhcp_packet_type(p);
+ if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
+ return;
+ }
+
+ if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
+ item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ if (item->client > MAX_INTERFACES)
+ return;
+ res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*)(&item->ip),
+ sizeof(item->ip));
+ if (res != packet_len) {
+ bb_perror_msg("pass_back");
+ return;
+ }
+
+ /* remove xid entry */
+ xid_del(p->xid);
+}
+
+static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients,
+ struct sockaddr_in *server_addr, uint32_t gw_ip)
+{
+ struct dhcpMessage dhcp_msg;
+ fd_set rfds;
+ size_t packlen;
+ socklen_t addr_size;
+ struct sockaddr_in client_addr;
+ struct timeval tv;
+ int i;
+
+ while (!dhcprelay_stopflag) {
+ FD_ZERO(&rfds);
+ for (i = 0; i < num_sockets; i++)
+ FD_SET(fds[i], &rfds);
+ tv.tv_sec = SELECT_TIMEOUT;
+ tv.tv_usec = 0;
+ if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
+ /* server */
+ if (FD_ISSET(fds[0], &rfds)) {
+ packlen = udhcp_get_packet(&dhcp_msg, fds[0]);
+ if (packlen > 0) {
+ pass_back(&dhcp_msg, packlen, fds);
+ }
+ }
+ for (i = 1; i < num_sockets; i++) {
+ /* clients */
+ if (!FD_ISSET(fds[i], &rfds))
+ continue;
+ addr_size = sizeof(struct sockaddr_in);
+ packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
+ (struct sockaddr *)(&client_addr), &addr_size);
+ if (packlen <= 0)
+ continue;
+ if (read_interface(clients[i-1], NULL, &dhcp_msg.giaddr, NULL) < 0)
+ dhcp_msg.giaddr = gw_ip;
+ pass_on(&dhcp_msg, packlen, i, fds, &client_addr, server_addr);
+ }
+ }
+ xid_expire();
+ }
+}
+
+int dhcprelay_main(int argc, char **argv);
+int dhcprelay_main(int argc, char **argv)
+{
+ int i, num_sockets, max_socket, fds[MAX_INTERFACES];
+ uint32_t gw_ip;
+ char **clients;
+ struct sockaddr_in server_addr;
+
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(67);
+ if (argc == 4) {
+ if (!inet_aton(argv[3], &server_addr.sin_addr))
+ bb_perror_msg_and_die("didn't grok server");
+ } else if (argc == 3) {
+ server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ } else {
+ bb_show_usage();
+ }
+ clients = get_client_devices(argv[1], &num_sockets);
+ if (!clients) return 0;
+
+ signal(SIGTERM, dhcprelay_signal_handler);
+ signal(SIGQUIT, dhcprelay_signal_handler);
+ signal(SIGINT, dhcprelay_signal_handler);
+
+ num_sockets = init_sockets(clients, num_sockets, argv[2], fds, &max_socket);
+
+ if (read_interface(argv[2], NULL, &gw_ip, NULL) == -1)
+ return 1;
+
+ dhcprelay_loop(fds, num_sockets, max_socket, clients, &server_addr, gw_ip);
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ for (i = 0; i < num_sockets; i++) {
+ close(fds[i]);
+ free(clients[i]);
+ }
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/domain_codec.c b/i/pc104/initrd/conf/busybox/networking/udhcp/domain_codec.c
new file mode 100644
index 0000000..239ae5b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/domain_codec.c
@@ -0,0 +1,205 @@
+/* vi: set sw=4 ts=4: */
+
+/* RFC1035 domain compression routines (C) 2007 Gabriel Somlo <somlo at cmu.edu>
+ *
+ * Loosely based on the isc-dhcpd implementation by dhankins@isc.org
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#if ENABLE_FEATURE_RFC3397
+
+#include "common.h"
+#include "options.h"
+
+#define NS_MAXDNAME 1025 /* max domain name length */
+#define NS_MAXCDNAME 255 /* max compressed domain name length */
+#define NS_MAXLABEL 63 /* max label length */
+#define NS_MAXDNSRCH 6 /* max domains in search path */
+#define NS_CMPRSFLGS 0xc0 /* name compression pointer flag */
+
+
+/* expand a RFC1035-compressed list of domain names "cstr", of length "clen";
+ * returns a newly allocated string containing the space-separated domains,
+ * prefixed with the contents of string pre, or NULL if an error occurs.
+ */
+char *dname_dec(const uint8_t *cstr, int clen, const char *pre)
+{
+ const uint8_t *c;
+ int crtpos, retpos, depth, plen = 0, len = 0;
+ char *dst = NULL;
+
+ if (!cstr)
+ return NULL;
+
+ if (pre)
+ plen = strlen(pre);
+
+ /* We make two passes over the cstr string. First, we compute
+ * how long the resulting string would be. Then we allocate a
+ * new buffer of the required length, and fill it in with the
+ * expanded content. The advantage of this approach is not
+ * having to deal with requiring callers to supply their own
+ * buffer, then having to check if it's sufficiently large, etc.
+ */
+
+ while (!dst) {
+
+ if (len > 0) { /* second pass? allocate dst buffer and copy pre */
+ dst = xmalloc(len + plen);
+ memcpy(dst, pre, plen);
+ }
+
+ crtpos = retpos = depth = len = 0;
+
+ while (crtpos < clen) {
+ c = cstr + crtpos;
+
+ if ((*c & NS_CMPRSFLGS) != 0) { /* pointer */
+ if (crtpos + 2 > clen) /* no offset to jump to? abort */
+ return NULL;
+ if (retpos == 0) /* toplevel? save return spot */
+ retpos = crtpos + 2;
+ depth++;
+ crtpos = ((*c & 0x3f) << 8) | (*(c + 1) & 0xff); /* jump */
+ } else if (*c) { /* label */
+ if (crtpos + *c + 1 > clen) /* label too long? abort */
+ return NULL;
+ if (dst)
+ memcpy(dst + plen + len, c + 1, *c);
+ len += *c + 1;
+ crtpos += *c + 1;
+ if (dst)
+ *(dst + plen + len - 1) = '.';
+ } else { /* null: end of current domain name */
+ if (retpos == 0) { /* toplevel? keep going */
+ crtpos++;
+ } else { /* return to toplevel saved spot */
+ crtpos = retpos;
+ retpos = depth = 0;
+ }
+ if (dst)
+ *(dst + plen + len - 1) = ' ';
+ }
+
+ if (depth > NS_MAXDNSRCH || /* too many jumps? abort, it's a loop */
+ len > NS_MAXDNAME * NS_MAXDNSRCH) /* result too long? abort */
+ return NULL;
+ }
+
+ if (!len) /* expanded string has 0 length? abort */
+ return NULL;
+
+ if (dst)
+ *(dst + plen + len - 1) = '\0';
+ }
+
+ return dst;
+}
+
+/* Convert a domain name (src) from human-readable "foo.blah.com" format into
+ * RFC1035 encoding "\003foo\004blah\003com\000". Return allocated string, or
+ * NULL if an error occurs.
+ */
+static uint8_t *convert_dname(const char *src)
+{
+ uint8_t c, *res, *lp, *rp;
+ int len;
+
+ res = xmalloc(strlen(src) + 2);
+ rp = lp = res;
+ rp++;
+
+ for (;;) {
+ c = (uint8_t)*src++;
+ if (c == '.' || c == '\0') { /* end of label */
+ len = rp - lp - 1;
+ /* label too long, too short, or two '.'s in a row? abort */
+ if (len > NS_MAXLABEL || len == 0 || (c == '.' && *src == '.')) {
+ free(res);
+ return NULL;
+ }
+ *lp = len;
+ lp = rp++;
+ if (c == '\0' || *src == '\0') /* end of dname */
+ break;
+ } else {
+ if (c >= 0x41 && c <= 0x5A) /* uppercase? convert to lower */
+ c += 0x20;
+ *rp++ = c;
+ }
+ }
+
+ *lp = 0;
+ if (rp - res > NS_MAXCDNAME) { /* dname too long? abort */
+ free(res);
+ return NULL;
+ }
+ return res;
+}
+
+/* returns the offset within cstr at which dname can be found, or -1
+ */
+static int find_offset(const uint8_t *cstr, int clen, const uint8_t *dname)
+{
+ const uint8_t *c, *d;
+ int off, inc;
+
+ /* find all labels in cstr */
+ off = 0;
+ while (off < clen) {
+ c = cstr + off;
+
+ if ((*c & NS_CMPRSFLGS) != 0) { /* pointer, skip */
+ off += 2;
+ } else if (*c) { /* label, try matching dname */
+ inc = *c + 1;
+ d = dname;
+ while (*c == *d && memcmp(c + 1, d + 1, *c) == 0) {
+ if (*c == 0) /* match, return offset */
+ return off;
+ d += *c + 1;
+ c += *c + 1;
+ if ((*c & NS_CMPRSFLGS) != 0) /* pointer, jump */
+ c = cstr + (((*c & 0x3f) << 8) | (*(c + 1) & 0xff));
+ }
+ off += inc;
+ } else { /* null, skip */
+ off++;
+ }
+ }
+
+ return -1;
+}
+
+/* computes string to be appended to cstr so that src would be added to
+ * the compression (best case, it's a 2-byte pointer to some offset within
+ * cstr; worst case, it's all of src, converted to rfc3011 format).
+ * The computed string is returned directly; its length is returned via retlen;
+ * NULL and 0, respectively, are returned if an error occurs.
+ */
+uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen)
+{
+ uint8_t *d, *dname;
+ int off;
+
+ dname = convert_dname(src);
+ if (dname == NULL) {
+ *retlen = 0;
+ return NULL;
+ }
+
+ for (d = dname; *d != 0; d += *d + 1) {
+ off = find_offset(cstr, clen, d);
+ if (off >= 0) { /* found a match, add pointer and terminate string */
+ *d++ = NS_CMPRSFLGS;
+ *d = off;
+ break;
+ }
+ }
+
+ *retlen = d - dname + 1;
+ return dname;
+}
+
+#endif /* ENABLE_FEATURE_RFC3397 */
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/dumpleases.c b/i/pc104/initrd/conf/busybox/networking/udhcp/dumpleases.c
new file mode 100644
index 0000000..ce73c47
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dumpleases.c
@@ -0,0 +1,75 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+#include <getopt.h>
+
+#include "common.h"
+#include "dhcpd.h"
+
+
+#define REMAINING 0
+#define ABSOLUTE 1
+
+int dumpleases_main(int argc, char *argv[]);
+int dumpleases_main(int argc, char *argv[])
+{
+ int fp;
+ int i, c, mode = REMAINING;
+ unsigned 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 = xopen(file, O_RDONLY);
+
+ 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 (full_read(fp, &lease, sizeof(lease)) == sizeof(lease)) {
+ printf(":%02x"+1, lease.chaddr[0]);
+ for (i = 1; i < 6; i++) {
+ printf(":%02x", lease.chaddr[i]);
+ }
+ addr.s_addr = lease.yiaddr;
+ printf(" %-15s ", inet_ntoa(addr));
+ expires = ntohl(lease.expires);
+ if (mode == REMAINING) {
+ if (!expires)
+ printf("expired\n");
+ else {
+ unsigned d, h, m;
+ d = expires / (24*60*60); expires %= (24*60*60);
+ h = expires / (60*60); expires %= (60*60);
+ m = expires / 60; expires %= 60;
+ if (d) printf("%u days ", d);
+ printf("%02u:%02u:%02u\n", h, m, (unsigned)expires);
+ }
+ } else fputs(ctime(&expires), stdout);
+ }
+ /* close(fp); */
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/files.c b/i/pc104/initrd/conf/busybox/networking/udhcp/files.c
new file mode 100644
index 0000000..ab6f4a3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/files.c
@@ -0,0 +1,426 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * files.c -- DHCP server file manipulation *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include <netinet/ether.h>
+
+#include "common.h"
+#include "dhcpd.h"
+#include "options.h"
+
+
+/*
+ * Domain names may have 254 chars, and string options can be 254
+ * chars long. However, 80 bytes will be enough for most, and won't
+ * hog up memory. If you have a special application, change it
+ */
+#define READ_CONFIG_BUF_SIZE 80
+
+/* on these functions, make sure you datatype matches */
+static int read_ip(const char *line, void *arg)
+{
+ len_and_sockaddr *lsa;
+ int retval = 0;
+
+ lsa = host_and_af2sockaddr(line, 0, AF_INET);
+ if (lsa) {
+ *(struct in_addr*)arg = lsa->sin.sin_addr;
+ free(lsa);
+ retval = 1;
+ }
+ return retval;
+}
+
+static int read_mac(const char *line, void *arg)
+{
+ uint8_t *mac_bytes = arg;
+ struct ether_addr *temp_ether_addr;
+ int retval = 1;
+
+ temp_ether_addr = ether_aton(line);
+
+ if (temp_ether_addr == NULL)
+ retval = 0;
+ else
+ memcpy(mac_bytes, temp_ether_addr, 6);
+
+ return retval;
+}
+
+
+static int read_str(const char *line, void *arg)
+{
+ char **dest = arg;
+
+ free(*dest);
+ *dest = xstrdup(line);
+
+ return 1;
+}
+
+
+static int read_u32(const char *line, void *arg)
+{
+ *((uint32_t*)arg) = bb_strtou32(line, NULL, 10);
+ return errno == 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;
+}
+
+
+/* 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 */
+static void attach_option(struct option_set **opt_list,
+ const struct dhcp_option *option, char *buffer, int length)
+{
+ struct option_set *existing, *new, **curr;
+
+ existing = find_option(*opt_list, option->code);
+ if (!existing) {
+ DEBUG("Attaching option %s to list", option->name);
+
+#if ENABLE_FEATURE_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035)
+ /* reuse buffer and length for RFC1035-formatted string */
+ buffer = dname_enc(NULL, 0, buffer, &length);
+#endif
+
+ /* 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;
+#if ENABLE_FEATURE_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL)
+ free(buffer);
+#endif
+ return;
+ }
+
+ /* add it to an existing option */
+ DEBUG("Attaching option %s to existing member of list", option->name);
+ if (option->flags & OPTION_LIST) {
+#if ENABLE_FEATURE_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035)
+ /* reuse buffer and length for RFC1035-formatted string */
+ buffer = dname_enc(existing->data + 2,
+ existing->data[OPT_LEN], buffer, &length);
+#endif
+ if (existing->data[OPT_LEN] + length <= 255) {
+ existing->data = xrealloc(existing->data,
+ existing->data[OPT_LEN] + length + 3);
+ if ((option->flags & TYPE_MASK) == OPTION_STRING) {
+ /* ' ' can bring us to 256 - bad */
+ if (existing->data[OPT_LEN] + length >= 255)
+ return;
+ /* add space separator between STRING options in a list */
+ existing->data[existing->data[OPT_LEN] + 2] = ' ';
+ existing->data[OPT_LEN]++;
+ }
+ 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 */
+#if ENABLE_FEATURE_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL)
+ free(buffer);
+#endif
+ } /* else, ignore the new data */
+}
+
+
+/* read a dhcp option and add it to opt_list */
+static int read_opt(const char *const_line, void *arg)
+{
+ struct option_set **opt_list = arg;
+ char *opt, *val, *endptr;
+ const struct dhcp_option *option;
+ int retval = 0, length;
+ char buffer[8];
+ char *line;
+ uint16_t *result_u16 = (uint16_t *) buffer;
+ uint32_t *result_u32 = (uint32_t *) buffer;
+
+ /* Cheat, the only const line we'll actually get is "" */
+ line = (char *) const_line;
+ opt = strtok(line, " \t=");
+ if (!opt) return 0;
+
+ option = dhcp_options;
+ while (1) {
+ if (!option->code)
+ return 0;
+ if (!strcasecmp(option->name, opt))
+ break;
+ option++;
+ }
+
+ do {
+ val = strtok(NULL, ", \t");
+ if (!val) break;
+ length = option_lengths[option->flags & TYPE_MASK];
+ retval = 0;
+ opt = buffer; /* new meaning for variable opt */
+ 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:
+#if ENABLE_FEATURE_RFC3397
+ case OPTION_STR1035:
+#endif
+ length = strlen(val);
+ if (length > 0) {
+ if (length > 254) length = 254;
+ opt = val;
+ retval = 1;
+ }
+ break;
+ case OPTION_BOOLEAN:
+ retval = read_yn(val, buffer);
+ break;
+ case OPTION_U8:
+ buffer[0] = strtoul(val, &endptr, 0);
+ retval = (endptr[0] == '\0');
+ break;
+ case OPTION_U16:
+ *result_u16 = htons(strtoul(val, &endptr, 0));
+ retval = (endptr[0] == '\0');
+ break;
+ case OPTION_S16:
+ *result_u16 = htons(strtol(val, &endptr, 0));
+ retval = (endptr[0] == '\0');
+ break;
+ case OPTION_U32:
+ *result_u32 = htonl(strtoul(val, &endptr, 0));
+ retval = (endptr[0] == '\0');
+ break;
+ case OPTION_S32:
+ *result_u32 = htonl(strtol(val, &endptr, 0));
+ retval = (endptr[0] == '\0');
+ break;
+ default:
+ break;
+ }
+ if (retval)
+ attach_option(opt_list, option, opt, length);
+ } while (retval && option->flags & OPTION_LIST);
+ return retval;
+}
+
+static int read_staticlease(const char *const_line, void *arg)
+{
+ char *line;
+ char *mac_string;
+ char *ip_string;
+ uint8_t *mac_bytes;
+ uint32_t *ip;
+
+
+ /* Allocate memory for addresses */
+ mac_bytes = xmalloc(sizeof(unsigned char) * 8);
+ ip = xmalloc(sizeof(uint32_t));
+
+ /* Read mac */
+ line = (char *) const_line;
+ mac_string = strtok(line, " \t");
+ read_mac(mac_string, mac_bytes);
+
+ /* Read ip */
+ ip_string = strtok(NULL, " \t");
+ read_ip(ip_string, ip);
+
+ addStaticLease(arg, mac_bytes, ip);
+
+ if (ENABLE_FEATURE_UDHCP_DEBUG) printStaticLeases(arg);
+
+ return 1;
+}
+
+
+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), ""},
+ {"static_lease",read_staticlease, &(server_config.static_leases), ""},
+ /*ADDME: static lease */
+ {"", NULL, NULL, ""}
+};
+
+
+int read_config(const char *file)
+{
+ FILE *in;
+ char buffer[READ_CONFIG_BUF_SIZE], *token, *line;
+ int i, lm = 0;
+
+ for (i = 0; keywords[i].keyword[0]; i++)
+ if (keywords[i].def[0])
+ keywords[i].handler(keywords[i].def, keywords[i].var);
+
+ in = fopen(file, "r");
+ if (!in) {
+ bb_error_msg("cannot open config file: %s", file);
+ return 0;
+ }
+
+ while (fgets(buffer, READ_CONFIG_BUF_SIZE, in)) {
+ char debug_orig[READ_CONFIG_BUF_SIZE];
+ char *p;
+
+ lm++;
+ p = strchr(buffer, '\n');
+ if (p) *p = '\0';
+ if (ENABLE_FEATURE_UDHCP_DEBUG) strcpy(debug_orig, buffer);
+ p = strchr(buffer, '#');
+ if (p) *p = '\0';
+
+ if (!(token = strtok(buffer, " \t"))) continue;
+ if (!(line = strtok(NULL, ""))) continue;
+
+ /* eat leading whitespace */
+ line = skip_whitespace(line);
+ /* eat trailing whitespace */
+ i = strlen(line) - 1;
+ while (i >= 0 && isspace(line[i]))
+ line[i--] = '\0';
+
+ for (i = 0; keywords[i].keyword[0]; i++)
+ if (!strcasecmp(token, keywords[i].keyword))
+ if (!keywords[i].handler(line, keywords[i].var)) {
+ bb_error_msg("cannot parse line %d of %s", lm, file);
+ if (ENABLE_FEATURE_UDHCP_DEBUG)
+ bb_error_msg("cannot parse '%s'", debug_orig);
+ /* reset back to the default value */
+ keywords[i].handler(keywords[i].def, keywords[i].var);
+ }
+ }
+ fclose(in);
+ return 1;
+}
+
+
+void write_leases(void)
+{
+ int fp;
+ unsigned i;
+ time_t curr = time(0);
+ unsigned long tmp_time;
+
+ fp = open(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (fp < 0) {
+ bb_error_msg("cannot open %s for writing", server_config.lease_file);
+ return;
+ }
+
+ for (i = 0; i < server_config.max_leases; i++) {
+ if (leases[i].yiaddr != 0) {
+
+ /* screw with the time in the struct, for easier writing */
+ tmp_time = leases[i].expires;
+
+ if (server_config.remaining) {
+ if (lease_expired(&(leases[i])))
+ leases[i].expires = 0;
+ else leases[i].expires -= curr;
+ } /* else stick with the time we got */
+ leases[i].expires = htonl(leases[i].expires);
+ // FIXME: error check??
+ full_write(fp, &leases[i], sizeof(leases[i]));
+
+ /* then restore it when done */
+ leases[i].expires = tmp_time;
+ }
+ }
+ close(fp);
+
+ if (server_config.notify_file) {
+ char *cmd = xasprintf("%s %s", server_config.notify_file, server_config.lease_file);
+ system(cmd);
+ free(cmd);
+ }
+}
+
+
+void read_leases(const char *file)
+{
+ int fp;
+ unsigned int i = 0;
+ struct dhcpOfferedAddr lease;
+
+ fp = open(file, O_RDONLY);
+ if (fp < 0) {
+ bb_error_msg("cannot open %s for reading", file);
+ return;
+ }
+
+ while (i < server_config.max_leases
+ && full_read(fp, &lease, sizeof(lease)) == sizeof(lease)
+ ) {
+ /* 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))) {
+ bb_error_msg("too many leases while loading %s", file);
+ break;
+ }
+ i++;
+ }
+ }
+ DEBUG("Read %d leases", i);
+ close(fp);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/leases.c b/i/pc104/initrd/conf/busybox/networking/udhcp/leases.c
new file mode 100644
index 0000000..2f7847d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/leases.c
@@ -0,0 +1,145 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * leases.c -- tools to manage DHCP leases
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+
+
+uint8_t blank_chaddr[] = {[0 ... 15] = 0};
+
+/* clear every lease out that chaddr OR yiaddr matches and is nonzero */
+void clear_lease(uint8_t *chaddr, uint32_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(uint8_t *chaddr, uint32_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(uint8_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(uint32_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(uint32_t addr)
+{
+ struct in_addr temp;
+
+ if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) {
+ temp.s_addr = addr;
+ bb_info_msg("%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... */
+uint32_t find_address(int check_expired)
+{
+ uint32_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;
+
+ /* Only do if it isn't an assigned as a static lease */
+ if (!reservedIp(server_config.static_leases, htonl(addr))) {
+
+ /* lease is not taken */
+ ret = htonl(addr);
+ lease = find_lease_by_yiaddr(ret);
+
+ /* no lease or it expired and we are checking for expired leases */
+ if ( (!lease || (check_expired && lease_expired(lease)))
+ && /* and it isn't on the network */ !check_ip(ret)
+ ) {
+ return ret;
+ break;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/options.c b/i/pc104/initrd/conf/busybox/networking/udhcp/options.c
new file mode 100644
index 0000000..a58adb9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/options.c
@@ -0,0 +1,180 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * options.c -- DHCP server option packet tools
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+#include "options.h"
+
+
+/* supported options are easily added here */
+const struct dhcp_option dhcp_options[] = {
+ /* name[12] 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_LIST | 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},
+ {"nisdomain", OPTION_STRING | OPTION_REQ, 0x28},
+ {"nissrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x29},
+ {"ntpsrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 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},
+ {"vendorclass", OPTION_STRING, 0x3C},
+ {"clientid", OPTION_STRING, 0x3D},
+ {"tftp", OPTION_STRING, 0x42},
+ {"bootfile", OPTION_STRING, 0x43},
+ {"userclass", OPTION_STRING, 0x4D},
+#if ENABLE_FEATURE_RFC3397
+ {"search", OPTION_STR1035 | OPTION_LIST | OPTION_REQ, 0x77},
+#endif
+ /* MSIE's "Web Proxy Autodiscovery Protocol" support */
+ {"wpad", OPTION_STRING, 0xfc},
+ {"", 0x00, 0x00}
+};
+
+/* Lengths of the different option types */
+const unsigned char option_lengths[] = {
+ [OPTION_IP] = 4,
+ [OPTION_IP_PAIR] = 8,
+ [OPTION_BOOLEAN] = 1,
+ [OPTION_STRING] = 1,
+#if ENABLE_FEATURE_RFC3397
+ [OPTION_STR1035] = 1,
+#endif
+ [OPTION_U8] = 1,
+ [OPTION_U16] = 2,
+ [OPTION_S16] = 2,
+ [OPTION_U32] = 4,
+ [OPTION_S32] = 4
+};
+
+
+/* get an option with bounds checking (warning, not aligned). */
+uint8_t *get_option(struct dhcpMessage *packet, int code)
+{
+ int i, length;
+ uint8_t *optionptr;
+ int over = 0, done = 0, curr = OPTION_FIELD;
+
+ optionptr = packet->options;
+ i = 0;
+ length = 308;
+ while (!done) {
+ if (i >= length) {
+ bb_error_msg("bogus packet, option fields too long");
+ return NULL;
+ }
+ if (optionptr[i + OPT_CODE] == code) {
+ if (i + 1 + optionptr[i + OPT_LEN] >= length) {
+ bb_error_msg("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) {
+ bb_error_msg("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(uint8_t *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(uint8_t *optionptr, uint8_t *string)
+{
+ int end = end_option(optionptr);
+
+ /* end position + string length + option code/length + end option */
+ if (end + string[OPT_LEN] + 2 + 1 >= 308) {
+ bb_error_msg("option 0x%02x did not fit into the packet",
+ string[OPT_CODE]);
+ return 0;
+ }
+ DEBUG("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(uint8_t *optionptr, uint8_t code, uint32_t data)
+{
+ const struct dhcp_option *dh;
+
+ for (dh = dhcp_options; dh->code; dh++) {
+ if (dh->code == code) {
+ uint8_t option[6], len;
+
+ option[OPT_CODE] = code;
+ len = option_lengths[dh->flags & TYPE_MASK];
+ option[OPT_LEN] = len;
+ if (BB_BIG_ENDIAN) data <<= 8 * (4 - len);
+ /* This memcpy is for broken processors which can't
+ * handle a simple unaligned 32-bit assignment */
+ memcpy(&option[OPT_DATA], &data, 4);
+ return add_option_string(optionptr, option);
+ }
+ }
+
+ bb_error_msg("cannot add option 0x%02x", code);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/options.h b/i/pc104/initrd/conf/busybox/networking/udhcp/options.h
new file mode 100644
index 0000000..11f013f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/options.h
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/* options.h */
+#ifndef _OPTIONS_H
+#define _OPTIONS_H
+
+#define TYPE_MASK 0x0F
+
+enum {
+ OPTION_IP=1,
+ OPTION_IP_PAIR,
+ OPTION_STRING,
+#if ENABLE_FEATURE_RFC3397
+ OPTION_STR1035, /* RFC1035 compressed domain name list */
+#endif
+ 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[12];
+ char flags;
+ uint8_t code;
+};
+
+extern const struct dhcp_option dhcp_options[];
+extern const unsigned char option_lengths[];
+
+uint8_t *get_option(struct dhcpMessage *packet, int code);
+int end_option(uint8_t *optionptr);
+int add_option_string(uint8_t *optionptr, uint8_t *string);
+int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data);
+#if ENABLE_FEATURE_RFC3397
+char *dname_dec(const uint8_t *cstr, int clen, const char *pre);
+uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen);
+#endif
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/packet.c b/i/pc104/initrd/conf/busybox/networking/udhcp/packet.c
new file mode 100644
index 0000000..25c55fa
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/packet.c
@@ -0,0 +1,211 @@
+/* vi: set sw=4 ts=4: */
+
+#include <netinet/in.h>
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
+#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"
+#include "dhcpd.h"
+#include "options.h"
+
+
+void udhcp_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 udhcp_get_packet(struct dhcpMessage *packet, int fd)
+{
+ static const char broken_vendors[][8] = {
+ "MSFT 98",
+ ""
+ };
+ int bytes;
+ int i;
+ char unsigned *vendor;
+
+ memset(packet, 0, sizeof(struct dhcpMessage));
+ bytes = read(fd, packet, sizeof(struct dhcpMessage));
+ if (bytes < 0) {
+ DEBUG("cannot read on listening socket, ignoring");
+ return -1;
+ }
+
+ if (ntohl(packet->cookie) != DHCP_MAGIC) {
+ bb_error_msg("received bogus message, ignoring");
+ return -2;
+ }
+ DEBUG("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] == (uint8_t)strlen(broken_vendors[i])
+ && !strncmp((char*)vendor, broken_vendors[i], vendor[OPT_LEN - 2])
+ ) {
+ DEBUG("broken client (%s), forcing broadcast",
+ broken_vendors[i]);
+ packet->flags |= htons(BROADCAST_FLAG);
+ }
+ }
+ }
+
+ return bytes;
+}
+
+
+uint16_t udhcp_checksum(void *addr, int count)
+{
+ /* Compute Internet Checksum for "count" bytes
+ * beginning at location "addr".
+ */
+ int32_t sum = 0;
+ uint16_t *source = (uint16_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 */
+ uint16_t tmp = 0;
+ *(uint8_t *) (&tmp) = * (uint8_t *) source;
+ sum += tmp;
+ }
+ /* Fold 32-bit sum to 16 bits */
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return ~sum;
+}
+
+
+/* Construct a ip/udp header for a packet, and specify the source and dest hardware address */
+void BUG_sizeof_struct_udp_dhcp_packet_must_be_576(void);
+int udhcp_raw_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex)
+{
+ int fd;
+ int result;
+ struct sockaddr_ll dest;
+ struct udp_dhcp_packet packet;
+
+ fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+ if (fd < 0) {
+ bb_perror_msg("socket");
+ 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) {
+ bb_perror_msg("bind");
+ 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 = udhcp_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 = udhcp_checksum(&(packet.ip), sizeof(packet.ip));
+
+ if (sizeof(struct udp_dhcp_packet) != 576)
+ BUG_sizeof_struct_udp_dhcp_packet_must_be_576();
+
+ result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0,
+ (struct sockaddr *) &dest, sizeof(dest));
+ if (result <= 0) {
+ bb_perror_msg("sendto");
+ }
+ close(fd);
+ return result;
+}
+
+
+/* Let the kernel do all the work for packet generation */
+int udhcp_kernel_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port)
+{
+ int fd, result;
+ struct sockaddr_in client;
+
+ fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0)
+ return -1;
+
+ if (setsockopt_reuseaddr(fd) == -1) {
+ close(fd);
+ 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) {
+ close(fd);
+ 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) {
+ close(fd);
+ return -1;
+ }
+
+ result = write(fd, payload, sizeof(struct dhcpMessage));
+ close(fd);
+ return result;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/pidfile.c b/i/pc104/initrd/conf/busybox/networking/udhcp/pidfile.c
new file mode 100644
index 0000000..be65d5f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/pidfile.c
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4: */
+/* pidfile.c
+ *
+ * Functions to assist in the writing and removing of pidfiles.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> September 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 "common.h"
+
+
+static const char *saved_pidfile;
+
+static void pidfile_delete(void)
+{
+ if (saved_pidfile) unlink(saved_pidfile);
+}
+
+
+int pidfile_acquire(const char *pidfile)
+{
+ int pid_fd;
+ if (!pidfile) return -1;
+
+ pid_fd = open(pidfile, O_CREAT|O_WRONLY|O_TRUNC, 0644);
+ if (pid_fd < 0) {
+ bb_perror_msg("cannot open pidfile %s", pidfile);
+ } else {
+ lockf(pid_fd, F_LOCK, 0);
+ if (!saved_pidfile)
+ atexit(pidfile_delete);
+ saved_pidfile = pidfile;
+ }
+
+ return pid_fd;
+}
+
+
+void pidfile_write_release(int pid_fd)
+{
+ if (pid_fd < 0) return;
+
+ fdprintf(pid_fd, "%d\n", getpid());
+ lockf(pid_fd, F_UNLCK, 0);
+ close(pid_fd);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/script.c b/i/pc104/initrd/conf/busybox/networking/udhcp/script.c
new file mode 100644
index 0000000..dc8ff7a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/script.c
@@ -0,0 +1,224 @@
+/* vi: set sw=4 ts=4: */
+/* script.c
+ *
+ * Functions to call the DHCP client notification scripts
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+#include "dhcpc.h"
+#include "options.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,
+#if ENABLE_FEATURE_RFC3397
+ [OPTION_STR1035] = 1,
+#endif
+ [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, const char *pre, const uint8_t *ip)
+{
+ return sprintf(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;
+}
+
+
+/* Allocate and fill with the text of option 'option'. */
+static char *alloc_fill_opts(uint8_t *option, const struct dhcp_option *type_p)
+{
+ int len, type, optlen;
+ uint16_t val_u16;
+ int16_t val_s16;
+ uint32_t val_u32;
+ int32_t val_s32;
+ char *dest, *ret;
+
+ len = option[OPT_LEN - 2];
+ type = type_p->flags & TYPE_MASK;
+ optlen = option_lengths[type];
+
+ dest = ret = xmalloc(upper_length(len, type) + strlen(type_p->name) + 2);
+ dest += sprintf(ret, "%s=", type_p->name);
+
+ 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 ret; /* Short circuit this case */
+#if ENABLE_FEATURE_RFC3397
+ case OPTION_STR1035:
+ /* unpack option into dest; use ret for prefix (i.e., "optname=") */
+ dest = dname_dec(option, len, ret);
+ free(ret);
+ return dest;
+#endif
+ }
+ option += optlen;
+ len -= optlen;
+ if (len <= 0) break;
+ dest += sprintf(dest, " ");
+ }
+ return ret;
+}
+
+
+/* put all the parameters into an environment */
+static char **fill_envp(struct dhcpMessage *packet)
+{
+ int num_options = 0;
+ int i, j;
+ char **envp;
+ uint8_t *temp;
+ struct in_addr subnet;
+ char over = 0;
+
+ if (packet == NULL)
+ num_options = 0;
+ else {
+ for (i = 0; dhcp_options[i].code; i++)
+ if (get_option(packet, dhcp_options[i].code)) {
+ num_options++;
+ if (dhcp_options[i].code == DHCP_SUBNET)
+ num_options++; /* for mton */
+ }
+ 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 = xzalloc(sizeof(char *) * (num_options + 5));
+ j = 0;
+ envp[j++] = xasprintf("interface=%s", client_config.interface);
+ envp[j++] = xasprintf("PATH=%s",
+ getenv("PATH") ? : "/bin:/usr/bin:/sbin:/usr/sbin");
+ envp[j++] = xasprintf("HOME=%s", getenv("HOME") ? : "/");
+
+ if (packet == NULL) return envp;
+
+ envp[j] = xmalloc(sizeof("ip=255.255.255.255"));
+ sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr);
+
+ for (i = 0; dhcp_options[i].code; i++) {
+ temp = get_option(packet, dhcp_options[i].code);
+ if (!temp)
+ continue;
+ envp[j++] = alloc_fill_opts(temp, &dhcp_options[i]);
+
+ /* Fill in a subnet bits option for things like /24 */
+ if (dhcp_options[i].code == DHCP_SUBNET) {
+ memcpy(&subnet, temp, 4);
+ envp[j++] = xasprintf("mask=%d", mton(&subnet));
+ }
+ }
+ if (packet->siaddr) {
+ envp[j] = xmalloc(sizeof("siaddr=255.255.255.255"));
+ sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr);
+ }
+ if (!(over & FILE_FIELD) && packet->file[0]) {
+ /* watch out for invalid packets */
+ packet->file[sizeof(packet->file) - 1] = '\0';
+ envp[j++] = xasprintf("boot_file=%s", packet->file);
+ }
+ if (!(over & SNAME_FIELD) && packet->sname[0]) {
+ /* watch out for invalid packets */
+ packet->sname[sizeof(packet->sname) - 1] = '\0';
+ envp[j++] = xasprintf("sname=%s", packet->sname);
+ }
+ return envp;
+}
+
+
+/* Call a script with a par file and env vars */
+void udhcp_run_script(struct dhcpMessage *packet, const char *name)
+{
+ int pid;
+ char **envp, **curr;
+
+ if (client_config.script == NULL)
+ return;
+
+ DEBUG("vfork'ing and execle'ing %s", client_config.script);
+
+ envp = fill_envp(packet);
+ /* call script */
+ pid = vfork();
+ if (pid) {
+ waitpid(pid, NULL, 0);
+ for (curr = envp; *curr; curr++) free(*curr);
+ free(envp);
+ return;
+ } else if (pid == 0) {
+ /* close fd's? */
+ /* exec script */
+ execle(client_config.script, client_config.script,
+ name, NULL, envp);
+ bb_perror_msg("script %s failed", client_config.script);
+ exit(1);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/serverpacket.c b/i/pc104/initrd/conf/busybox/networking/udhcp/serverpacket.c
new file mode 100644
index 0000000..8889fda
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/serverpacket.c
@@ -0,0 +1,261 @@
+/* vi: set sw=4 ts=4: */
+/* serverpacket.c
+ *
+ * Construct 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 "common.h"
+#include "dhcpd.h"
+#include "options.h"
+
+
+/* send a packet to giaddr using the kernel ip stack */
+static int send_packet_to_relay(struct dhcpMessage *payload)
+{
+ DEBUG("Forwarding packet to relay");
+
+ return udhcp_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)
+{
+ uint8_t *chaddr;
+ uint32_t ciaddr;
+
+ if (force_broadcast) {
+ DEBUG("broadcasting packet to client (NAK)");
+ ciaddr = INADDR_BROADCAST;
+ chaddr = MAC_BCAST_ADDR;
+ } else if (payload->ciaddr) {
+ DEBUG("unicasting packet to client ciaddr");
+ ciaddr = payload->ciaddr;
+ chaddr = payload->chaddr;
+ } else if (ntohs(payload->flags) & BROADCAST_FLAG) {
+ DEBUG("broadcasting packet to client (requested)");
+ ciaddr = INADDR_BROADCAST;
+ chaddr = MAC_BCAST_ADDR;
+ } else {
+ DEBUG("unicasting packet to client yiaddr");
+ ciaddr = payload->yiaddr;
+ chaddr = payload->chaddr;
+ }
+ return udhcp_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)
+{
+ udhcp_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((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1);
+ if (server_config.boot_file)
+ strncpy((char*)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;
+ uint32_t req_align, lease_time_align = server_config.lease;
+ uint8_t *req, *lease_time;
+ struct option_set *curr;
+ struct in_addr addr;
+
+ uint32_t static_lease_ip;
+
+ init_packet(&packet, oldpacket, DHCPOFFER);
+
+ static_lease_ip = getIpByMac(server_config.static_leases, oldpacket->chaddr);
+
+ /* ADDME: if static, short circuit */
+ if (!static_lease_ip) {
+ /* the client is in our lease/offered table */
+ lease = find_lease_by_chaddr(oldpacket->chaddr);
+ if (lease) {
+ 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)
+ && !static_lease_ip /* Check that its not a static lease */
+ /* and is not already taken/offered */
+ && (!(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 */
+ } else {
+ /* Is it a static lease? (No, because find_address skips static lease) */
+ packet.yiaddr = find_address(0);
+ /* try for an expired lease */
+ if (!packet.yiaddr) packet.yiaddr = find_address(1);
+ }
+
+ if (!packet.yiaddr) {
+ bb_error_msg("no IP addresses to give - OFFER abandoned");
+ return -1;
+ }
+ if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) {
+ bb_error_msg("lease pool is full - OFFER abandoned");
+ return -1;
+ }
+ lease_time = get_option(oldpacket, DHCP_LEASE_TIME);
+ if (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 */
+ } else {
+ /* It is a static lease... use it */
+ packet.yiaddr = static_lease_ip;
+ }
+
+ 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;
+ bb_info_msg("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("Sending NAK");
+ return send_packet(&packet, 1);
+}
+
+
+int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr)
+{
+ struct dhcpMessage packet;
+ struct option_set *curr;
+ uint8_t *lease_time;
+ uint32_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;
+ bb_info_msg("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/i/pc104/initrd/conf/busybox/networking/udhcp/signalpipe.c b/i/pc104/initrd/conf/busybox/networking/udhcp/signalpipe.c
new file mode 100644
index 0000000..3615965
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/signalpipe.c
@@ -0,0 +1,77 @@
+/* vi: set sw=4 ts=4: */
+/* signalpipe.c
+ *
+ * Signal pipe infrastructure. A reliable way of delivering signals.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> December 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 "common.h"
+
+
+static int signal_pipe[2];
+
+static void signal_handler(int sig)
+{
+ if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0)
+ bb_perror_msg("cannot send signal");
+}
+
+
+/* Call this before doing anything else. Sets up the socket pair
+ * and installs the signal handler */
+void udhcp_sp_setup(void)
+{
+ socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe);
+ fcntl(signal_pipe[0], F_SETFD, FD_CLOEXEC);
+ fcntl(signal_pipe[1], F_SETFD, FD_CLOEXEC);
+ signal(SIGUSR1, signal_handler);
+ signal(SIGUSR2, signal_handler);
+ signal(SIGTERM, signal_handler);
+}
+
+
+/* Quick little function to setup the rfds. Will return the
+ * max_fd for use with select. Limited in that you can only pass
+ * one extra fd */
+int udhcp_sp_fd_set(fd_set *rfds, int extra_fd)
+{
+ FD_ZERO(rfds);
+ FD_SET(signal_pipe[0], rfds);
+ if (extra_fd >= 0) {
+ fcntl(extra_fd, F_SETFD, FD_CLOEXEC);
+ FD_SET(extra_fd, rfds);
+ }
+ return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd;
+}
+
+
+/* Read a signal from the signal pipe. Returns 0 if there is
+ * no signal, -1 on error (and sets errno appropriately), and
+ * your signal on success */
+int udhcp_sp_read(fd_set *rfds)
+{
+ int sig;
+
+ if (!FD_ISSET(signal_pipe[0], rfds))
+ return 0;
+
+ if (read(signal_pipe[0], &sig, sizeof(sig)) < 0)
+ return -1;
+
+ return sig;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/socket.c b/i/pc104/initrd/conf/busybox/networking/udhcp/socket.c
new file mode 100644
index 0000000..d294fb2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/socket.c
@@ -0,0 +1,126 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 <net/if.h>
+#include <features.h>
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
+#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(const char *interface, int *ifindex, uint32_t *addr, uint8_t *arp)
+{
+ int fd;
+ struct ifreq ifr;
+ struct sockaddr_in *our_ip;
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+ fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+ if (fd < 0) {
+ bb_perror_msg("socket failed");
+ return -1;
+ }
+
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
+ if (addr) {
+ if (ioctl(fd, SIOCGIFADDR, &ifr) != 0) {
+ bb_perror_msg("SIOCGIFADDR failed, is the interface up and configured?");
+ close(fd);
+ return -1;
+ }
+ our_ip = (struct sockaddr_in *) &ifr.ifr_addr;
+ *addr = our_ip->sin_addr.s_addr;
+ DEBUG("%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr));
+ }
+
+ if (ifindex) {
+ if (ioctl(fd, SIOCGIFINDEX, &ifr) != 0) {
+ bb_perror_msg("SIOCGIFINDEX failed");
+ close(fd);
+ return -1;
+ }
+ DEBUG("adapter index %d", ifr.ifr_ifindex);
+ *ifindex = ifr.ifr_ifindex;
+ }
+
+ if (arp) {
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) {
+ bb_perror_msg("SIOCGIFHWADDR failed");
+ close(fd);
+ return -1;
+ }
+ memcpy(arp, ifr.ifr_hwaddr.sa_data, 6);
+ DEBUG("adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
+ arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]);
+ }
+
+ return 0;
+}
+
+
+int listen_socket(uint32_t ip, int port, const char *inf)
+{
+ struct ifreq interface;
+ int fd;
+ struct sockaddr_in addr;
+
+ DEBUG("Opening listen socket on 0x%08x:%d %s", ip, port, inf);
+ fd = xsocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = ip;
+
+ if (setsockopt_reuseaddr(fd) == -1) {
+ close(fd);
+ return -1;
+ }
+ if (setsockopt_broadcast(fd) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ strncpy(interface.ifr_name, inf, IFNAMSIZ);
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &interface, sizeof(interface)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/static_leases.c b/i/pc104/initrd/conf/busybox/networking/udhcp/static_leases.c
new file mode 100644
index 0000000..aabfb81
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/static_leases.c
@@ -0,0 +1,99 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * static_leases.c -- Couple of functions to assist with storing and
+ * retrieving data for static leases
+ *
+ * Wade Berrier <wberrier@myrealbox.com> September 2004
+ *
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+
+
+/* Takes the address of the pointer to the static_leases linked list,
+ * Address to a 6 byte mac address
+ * Address to a 4 byte ip address */
+int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip)
+{
+ struct static_lease *cur;
+ struct static_lease *new_static_lease;
+
+ /* Build new node */
+ new_static_lease = xmalloc(sizeof(struct static_lease));
+ new_static_lease->mac = mac;
+ new_static_lease->ip = ip;
+ new_static_lease->next = NULL;
+
+ /* If it's the first node to be added... */
+ if (*lease_struct == NULL) {
+ *lease_struct = new_static_lease;
+ } else {
+ cur = *lease_struct;
+ while (cur->next) {
+ cur = cur->next;
+ }
+
+ cur->next = new_static_lease;
+ }
+
+ return 1;
+}
+
+/* Check to see if a mac has an associated static lease */
+uint32_t getIpByMac(struct static_lease *lease_struct, void *arg)
+{
+ uint32_t return_ip;
+ struct static_lease *cur = lease_struct;
+ uint8_t *mac = arg;
+
+ return_ip = 0;
+
+ while (cur) {
+ /* If the client has the correct mac */
+ if (memcmp(cur->mac, mac, 6) == 0) {
+ return_ip = *(cur->ip);
+ }
+
+ cur = cur->next;
+ }
+
+ return return_ip;
+}
+
+/* Check to see if an ip is reserved as a static ip */
+uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip)
+{
+ struct static_lease *cur = lease_struct;
+
+ uint32_t return_val = 0;
+
+ while (cur) {
+ /* If the client has the correct ip */
+ if (*cur->ip == ip)
+ return_val = 1;
+
+ cur = cur->next;
+ }
+
+ return return_val;
+}
+
+#if ENABLE_FEATURE_UDHCP_DEBUG
+/* Print out static leases just to check what's going on */
+/* Takes the address of the pointer to the static_leases linked list */
+void printStaticLeases(struct static_lease **arg)
+{
+ /* Get a pointer to the linked list */
+ struct static_lease *cur = *arg;
+
+ while (cur) {
+ /* printf("PrintStaticLeases: Lease mac Address: %x\n", cur->mac); */
+ printf("PrintStaticLeases: Lease mac Value: %x\n", *(cur->mac));
+ /* printf("PrintStaticLeases: Lease ip Address: %x\n", cur->ip); */
+ printf("PrintStaticLeases: Lease ip Value: %x\n", *(cur->ip));
+
+ cur = cur->next;
+ }
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/vconfig.c b/i/pc104/initrd/conf/busybox/networking/vconfig.c
new file mode 100644
index 0000000..06c06cb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/vconfig.c
@@ -0,0 +1,165 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * vconfig implementation for busybox
+ *
+ * Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 N/A */
+
+#include "busybox.h"
+#include <net/if.h>
+
+/* Stuff from linux/if_vlan.h, kernel version 2.4.23 */
+enum vlan_ioctl_cmds {
+ ADD_VLAN_CMD,
+ DEL_VLAN_CMD,
+ SET_VLAN_INGRESS_PRIORITY_CMD,
+ SET_VLAN_EGRESS_PRIORITY_CMD,
+ GET_VLAN_INGRESS_PRIORITY_CMD,
+ GET_VLAN_EGRESS_PRIORITY_CMD,
+ SET_VLAN_NAME_TYPE_CMD,
+ SET_VLAN_FLAG_CMD
+};
+enum vlan_name_types {
+ VLAN_NAME_TYPE_PLUS_VID, /* Name will look like: vlan0005 */
+ VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like: eth1.0005 */
+ VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like: vlan5 */
+ VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like: eth0.5 */
+ VLAN_NAME_TYPE_HIGHEST
+};
+
+struct vlan_ioctl_args {
+ int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */
+ char device1[24];
+
+ union {
+ char device2[24];
+ int VID;
+ unsigned int skb_priority;
+ unsigned int name_type;
+ unsigned int bind_type;
+ unsigned int flag; /* Matches vlan_dev_info flags */
+ } u;
+
+ short vlan_qos;
+};
+
+#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,
+ 5, 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);
+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. */
+ /* Will die if 802.1q is not present */
+ xopen(conf_file_name, O_RDONLY);
+
+ 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", 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 = xatoul_range(p, 0, 1);
+ /* DM: in order to set reorder header, qos must be set */
+ ifr.vlan_qos = xatoul_range(argv[3], 0, 7);
+ } else if (ifr.cmd == ADD_VLAN_CMD) { /* add */
+ ifr.u.VID = xatoul_range(p, 0, VLAN_GROUP_ARRAY_LEN-1);
+ } else if (ifr.cmd != DEL_VLAN_CMD) { /* set_{egress|ingress}_map */
+ ifr.u.skb_priority = xatou(p);
+ ifr.vlan_qos = xatoul_range(argv[3], 0, 7);
+ }
+ }
+
+ fd = xsocket(AF_INET, SOCK_STREAM, 0);
+ if (ioctl(fd, SIOCSIFVLAN, &ifr) < 0) {
+ bb_perror_msg_and_die("ioctl error for %s", *argv);
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/wget.c b/i/pc104/initrd/conf/busybox/networking/wget.c
new file mode 100644
index 0000000..db22215
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/wget.c
@@ -0,0 +1,808 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * wget - retrieve a file using HTTP or FTP
+ *
+ * Chip Rosenthal Covad Communications <chip@laserlink.net>
+ *
+ */
+
+/* We want libc to give us xxx64 functions also */
+/* http://www.unix.org/version2/whatsnew/lfs20mar.html */
+#define _LARGEFILE64_SOURCE 1
+
+#include "busybox.h"
+#include <getopt.h> /* for struct option */
+
+struct host_info {
+ // May be used if we ever will want to free() all xstrdup()s...
+ /* char *allocated; */
+ 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(len_and_sockaddr *lsa);
+static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc);
+static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf);
+
+/* Globals (can be accessed from signal handlers */
+static off_t content_len; /* Content-length of the file */
+static off_t beg_range; /* Range at which continue begins */
+#if ENABLE_FEATURE_WGET_STATUSBAR
+static off_t transferred; /* Number of bytes transferred so far */
+#endif
+static int chunked; /* chunked transfer encoding */
+#if ENABLE_FEATURE_WGET_STATUSBAR
+static void progressmeter(int flag);
+static const char *curfile; /* Name of current file being transferred */
+static struct timeval start; /* Time a transfer started */
+enum {
+ STALLTIME = 5 /* Seconds when xfer considered "stalled" */
+};
+#else
+static void progressmeter(int flag) {}
+#endif
+
+/* 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;
+}
+
+/* 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;
+}
+
+#if ENABLE_FEATURE_WGET_AUTHENTICATION
+/*
+ * Base64-encode character string and return the string.
+ */
+static char *base64enc(unsigned char *p, char *buf, int len)
+{
+ bb_uuencode(p, buf, len, bb_uuenc_tbl_base64);
+ return buf;
+}
+#endif
+
+int wget_main(int argc, char **argv);
+int wget_main(int argc, char **argv)
+{
+ char buf[512];
+ struct host_info server, target;
+ len_and_sockaddr *lsa;
+ int n, status;
+ int port;
+ int try = 5;
+ unsigned opt;
+ char *s;
+ char *proxy = 0;
+ char *dir_prefix = NULL;
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ char *extra_headers = NULL;
+ llist_t *headers_llist = NULL;
+#endif
+
+ /* server.allocated = target.allocated = NULL; */
+
+ 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 got_clen = 0; /* got content-length: from server */
+ int output_fd = -1;
+ int use_proxy = 1; /* Use proxies if env vars are set */
+ const char *proxy_flag = "on"; /* Use proxies if env vars are set */
+ const char *user_agent = "Wget";/* Content of the "User-Agent" header field */
+
+ /*
+ * Crack command line.
+ */
+ enum {
+ WGET_OPT_CONTINUE = 0x1,
+ WGET_OPT_QUIET = 0x2,
+ WGET_OPT_OUTNAME = 0x4,
+ WGET_OPT_PREFIX = 0x8,
+ WGET_OPT_PROXY = 0x10,
+ WGET_OPT_USER_AGENT = 0x20,
+ WGET_OPT_PASSIVE = 0x40,
+ WGET_OPT_HEADER = 0x80,
+ };
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ static const struct option wget_long_options[] = {
+ // name, has_arg, flag, val
+ { "continue", no_argument, NULL, 'c' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "output-document", required_argument, NULL, 'O' },
+ { "directory-prefix", required_argument, NULL, 'P' },
+ { "proxy", required_argument, NULL, 'Y' },
+ { "user-agent", required_argument, NULL, 'U' },
+ { "passive-ftp", no_argument, NULL, 0xff },
+ { "header", required_argument, NULL, 0xfe },
+ { 0, 0, 0, 0 }
+ };
+ applet_long_options = wget_long_options;
+#endif
+ opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
+ opt = getopt32(argc, argv, "cqO:P:Y:U:",
+ &fname_out, &dir_prefix,
+ &proxy_flag, &user_agent
+ USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
+ );
+ if (strcmp(proxy_flag, "off") == 0) {
+ /* Use the proxy if necessary. */
+ use_proxy = 0;
+ }
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ if (headers_llist) {
+ int size = 1;
+ char *cp;
+ llist_t *ll = headers_llist = llist_rev(headers_llist);
+ while (ll) {
+ size += strlen(ll->data) + 2;
+ ll = ll->link;
+ }
+ extra_headers = cp = xmalloc(size);
+ while (headers_llist) {
+ cp += sprintf(cp, "%s\r\n", headers_llist->data);
+ headers_llist = headers_llist->link;
+ }
+ }
+#endif
+
+ parse_url(argv[optind], &target);
+ server.host = target.host;
+ server.port = target.port;
+
+ /*
+ * Use the proxy if necessary.
+ */
+ if (use_proxy) {
+ proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
+ if (proxy && *proxy) {
+ parse_url(proxy, &server);
+ } else {
+ use_proxy = 0;
+ }
+ }
+
+ /* Guess an output filename */
+ if (!fname_out) {
+ // Dirty hack. Needed because bb_get_last_path_component
+ // will destroy trailing / by storing '\0' in last byte!
+ if (!last_char_is(target.path, '/')) {
+ fname_out = bb_get_last_path_component(target.path);
+#if ENABLE_FEATURE_WGET_STATUSBAR
+ curfile = fname_out;
+#endif
+ }
+ if (!fname_out || !fname_out[0]) {
+ /* bb_get_last_path_component writes
+ * to last '/' only. We don't have one here... */
+ fname_out = (char*)"index.html";
+#if ENABLE_FEATURE_WGET_STATUSBAR
+ curfile = fname_out;
+#endif
+ }
+ if (dir_prefix != NULL)
+ fname_out = concat_path_file(dir_prefix, fname_out);
+#if ENABLE_FEATURE_WGET_STATUSBAR
+ } else {
+ curfile = bb_get_last_path_component(fname_out);
+#endif
+ }
+ /* Impossible?
+ if ((opt & WGET_OPT_CONTINUE) && !fname_out)
+ bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
+
+ /*
+ * Determine where to start transfer.
+ */
+ if (LONE_DASH(fname_out)) {
+ output_fd = 1;
+ opt &= ~WGET_OPT_CONTINUE;
+ }
+ if (opt & WGET_OPT_CONTINUE) {
+ output_fd = open(fname_out, O_WRONLY);
+ if (output_fd >= 0) {
+ beg_range = xlseek(output_fd, 0, SEEK_END);
+ }
+ /* File doesn't exist. We do not create file here yet.
+ We are not sure it exists on remove side */
+ }
+
+ /* We want to do exactly _one_ DNS lookup, since some
+ * sites (i.e. ftp.us.debian.org) use round-robin DNS
+ * and we want to connect to only one IP... */
+ lsa = xhost2sockaddr(server.host, server.port);
+ if (!(opt & WGET_OPT_QUIET)) {
+ fprintf(stderr, "Connecting to %s (%s)\n", server.host,
+ xmalloc_sockaddr2dotted(&lsa->sa, lsa->len));
+ /* We leak result of xmalloc_sockaddr2dotted */
+ }
+
+ if (use_proxy || !target.is_ftp) {
+ /*
+ * HTTP session
+ */
+ do {
+ got_clen = chunked = 0;
+
+ if (!--try)
+ bb_error_msg_and_die("too many redirections");
+
+ /*
+ * Open socket to http server
+ */
+ if (sfp) fclose(sfp);
+ sfp = open_socket(lsa);
+
+ /*
+ * Send HTTP request.
+ */
+ if (use_proxy) {
+ fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
+ target.is_ftp ? "f" : "ht", target.host,
+ target.path);
+ } else {
+ fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
+ }
+
+ fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
+ target.host, user_agent);
+
+#if ENABLE_FEATURE_WGET_AUTHENTICATION
+ if (target.user) {
+ fprintf(sfp, "Authorization: Basic %s\r\n",
+ base64enc((unsigned char*)target.user, buf, sizeof(buf)));
+ }
+ if (use_proxy && server.user) {
+ fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
+ base64enc((unsigned char*)server.user, buf, sizeof(buf)));
+ }
+#endif
+
+ if (beg_range)
+ fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ if (extra_headers)
+ fputs(extra_headers, sfp);
+#endif
+ 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)
+ bb_error_msg_and_die("no response from server");
+
+ s = buf;
+ while (*s != '\0' && !isspace(*s)) ++s;
+ s = skip_whitespace(s);
+ // FIXME: no error check
+ // xatou wouldn't work: "200 OK"
+ status = atoi(s);
+ switch (status) {
+ case 0:
+ case 100:
+ while (gethdr(buf, sizeof(buf), sfp, &n) != NULL)
+ /* eat all remaining headers */;
+ goto read_response;
+ case 200:
+ break;
+ case 300: /* redirection */
+ case 301:
+ case 302:
+ case 303:
+ break;
+ case 206:
+ if (beg_range)
+ break;
+ /*FALLTHRU*/
+ default:
+ /* Show first line only and kill any ESC tricks */
+ buf[strcspn(buf, "\n\r\x1b")] = '\0';
+ bb_error_msg_and_die("server returned error: %s", buf);
+ }
+
+ /*
+ * Retrieve HTTP headers.
+ */
+ while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) {
+ if (strcasecmp(buf, "content-length") == 0) {
+ content_len = BB_STRTOOFF(s, NULL, 10);
+ if (errno || content_len < 0) {
+ bb_error_msg_and_die("content-length %s is garbage", s);
+ }
+ got_clen = 1;
+ continue;
+ }
+ if (strcasecmp(buf, "transfer-encoding") == 0) {
+ if (strcasecmp(s, "chunked") != 0)
+ bb_error_msg_and_die("server wants to do %s transfer encoding", s);
+ chunked = got_clen = 1;
+ }
+ if (strcasecmp(buf, "location") == 0) {
+ if (s[0] == '/')
+ /* free(target.allocated); */
+ target.path = /* target.allocated = */ xstrdup(s+1);
+ else {
+ parse_url(s, &target);
+ if (use_proxy == 0) {
+ server.host = target.host;
+ server.port = target.port;
+ }
+ free(lsa);
+ lsa = xhost2sockaddr(server.host, server.port);
+ break;
+ }
+ }
+ }
+ } while (status >= 300);
+
+ dfp = sfp;
+
+ } else {
+
+ /*
+ * FTP session
+ */
+ if (!target.user)
+ target.user = xstrdup("anonymous:busybox@");
+
+ sfp = open_socket(lsa);
+ if (ftpcmd(NULL, NULL, sfp, buf) != 220)
+ bb_error_msg_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:
+ bb_error_msg_and_die("ftp login: %s", buf+4);
+ }
+
+ ftpcmd("TYPE I", NULL, sfp, buf);
+
+ /*
+ * Querying file size
+ */
+ if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) {
+ content_len = BB_STRTOOFF(buf+4, NULL, 10);
+ if (errno || content_len < 0) {
+ bb_error_msg_and_die("SIZE value is garbage");
+ }
+ got_clen = 1;
+ }
+
+ /*
+ * Entering passive mode
+ */
+ if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
+ pasv_error:
+ bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
+ }
+ // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
+ // Server's IP is N1.N2.N3.N4 (we ignore it)
+ // Server's port for data connection is P1*256+P2
+ s = strrchr(buf, ')');
+ if (s) s[0] = '\0';
+ s = strrchr(buf, ',');
+ if (!s) goto pasv_error;
+ port = xatou_range(s+1, 0, 255);
+ *s = '\0';
+ s = strrchr(buf, ',');
+ if (!s) goto pasv_error;
+ port += xatou_range(s+1, 0, 255) * 256;
+ set_nport(lsa, htons(port));
+ dfp = open_socket(lsa);
+
+ if (beg_range) {
+ sprintf(buf, "REST %"OFF_FMT"d", beg_range);
+ if (ftpcmd(buf, NULL, sfp, buf) == 350)
+ content_len -= beg_range;
+ }
+
+ if (ftpcmd("RETR ", target.path, sfp, buf) > 150)
+ bb_error_msg_and_die("bad response to RETR: %s", buf);
+ }
+
+
+ /*
+ * Retrieve file
+ */
+ if (chunked) {
+ fgets(buf, sizeof(buf), dfp);
+ content_len = STRTOOFF(buf, NULL, 16);
+ /* FIXME: error check?? */
+ }
+
+ /* Do it before progressmeter (want to have nice error message) */
+ if (output_fd < 0)
+ output_fd = xopen(fname_out,
+ O_WRONLY|O_CREAT|O_EXCL|O_TRUNC);
+
+ if (!(opt & WGET_OPT_QUIET))
+ progressmeter(-1);
+
+ do {
+ while (content_len > 0 || !got_clen) {
+ unsigned rdsz = sizeof(buf);
+ if (content_len < sizeof(buf) && (chunked || got_clen))
+ rdsz = (unsigned)content_len;
+ n = safe_fread(buf, 1, rdsz, dfp);
+ if (n <= 0)
+ break;
+ if (full_write(output_fd, buf, n) != n) {
+ bb_perror_msg_and_die(bb_msg_write_error);
+ }
+#if ENABLE_FEATURE_WGET_STATUSBAR
+ transferred += n;
+#endif
+ if (got_clen) {
+ content_len -= n;
+ }
+ }
+
+ if (chunked) {
+ safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
+ safe_fgets(buf, sizeof(buf), dfp);
+ content_len = STRTOOFF(buf, NULL, 16);
+ /* FIXME: error check? */
+ if (content_len == 0) {
+ chunked = 0; /* all done! */
+ }
+ }
+
+ if (n == 0 && ferror(dfp)) {
+ bb_perror_msg_and_die(bb_msg_read_error);
+ }
+ } while (chunked);
+
+ if (!(opt & WGET_OPT_QUIET))
+ progressmeter(1);
+
+ if ((use_proxy == 0) && 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);
+}
+
+
+static void parse_url(char *src_url, struct host_info *h)
+{
+ char *url, *p, *sp;
+
+ /* h->allocated = */ url = xstrdup(src_url);
+
+ if (strncmp(url, "http://", 7) == 0) {
+ h->port = bb_lookup_port("http", "tcp", 80);
+ h->host = url + 7;
+ h->is_ftp = 0;
+ } else if (strncmp(url, "ftp://", 6) == 0) {
+ h->port = bb_lookup_port("ftp", "tcp", 21);
+ h->host = url + 6;
+ h->is_ftp = 1;
+ } else
+ bb_error_msg_and_die("not an http or ftp url: %s", url);
+
+ // FYI:
+ // "Real" wget 'http://busybox.net?var=a/b' sends this request:
+ // 'GET /?var=a/b HTTP 1.0'
+ // and saves 'index.html?var=a%2Fb' (we save 'b')
+ // wget 'http://busybox.net?login=john@doe':
+ // request: 'GET /?login=john@doe HTTP/1.0'
+ // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
+ // wget 'http://busybox.net#test/test':
+ // request: 'GET / HTTP/1.0'
+ // saves: 'index.html' (we save 'test')
+ //
+ // We also don't add unique .N suffix if file exists...
+ sp = strchr(h->host, '/');
+ p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
+ p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
+ if (!sp) {
+ /* must be writable because of bb_get_last_path_component() */
+ static char nullstr[] = "";
+ h->path = nullstr;
+ } else if (*sp == '/') {
+ *sp = '\0';
+ h->path = sp + 1;
+ } else { // '#' or '?'
+ // http://busybox.net?login=john@doe is a valid URL
+ // memmove converts to:
+ // http:/busybox.nett?login=john@doe...
+ memmove(h->host-1, h->host, sp - h->host);
+ h->host--;
+ sp[-1] = '\0';
+ h->path = sp;
+ }
+
+ sp = strrchr(h->host, '@');
+ h->user = NULL;
+ if (sp != NULL) {
+ h->user = h->host;
+ *sp = '\0';
+ h->host = sp + 1;
+ }
+
+ sp = h->host;
+}
+
+
+static FILE *open_socket(len_and_sockaddr *lsa)
+{
+ FILE *fp;
+
+ /* glibc 2.4 seems to try seeking on it - ??! */
+ /* hopefully it understands what ESPIPE means... */
+ fp = fdopen(xconnect_stream(lsa), "r+");
+ if (fp == NULL)
+ bb_perror_msg_and_die("fdopen");
+
+ return fp;
+}
+
+
+static 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(const char *s1, const char *s2, FILE *fp, char *buf)
+{
+ int result;
+ if (s1) {
+ if (!s2) s2 = "";
+ fprintf(fp, "%s%s\r\n", s1, s2);
+ fflush(fp);
+ }
+
+ do {
+ char *buf_ptr;
+
+ if (fgets(buf, 510, fp) == NULL) {
+ bb_perror_msg_and_die("error getting response");
+ }
+ buf_ptr = strstr(buf, "\r\n");
+ if (buf_ptr) {
+ *buf_ptr = '\0';
+ }
+ } while (!isdigit(buf[0]) || buf[3] != ' ');
+
+ buf[3] = '\0';
+ result = xatoi_u(buf);
+ buf[3] = ' ';
+ return result;
+}
+
+#if ENABLE_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)
+{
+ int width;
+ get_terminal_width_height(0, &width, NULL);
+ return width;
+}
+
+static void
+updateprogressmeter(int ignore)
+{
+ int save_errno = errno;
+
+ progressmeter(0);
+ errno = save_errno;
+}
+
+static void alarmtimer(int iwait)
+{
+ struct itimerval itv;
+
+ itv.it_value.tv_sec = iwait;
+ itv.it_value.tv_usec = 0;
+ itv.it_interval = itv.it_value;
+ setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+
+static void
+progressmeter(int flag)
+{
+ static struct timeval lastupdate;
+ static off_t lastsize, totalsize;
+
+ struct timeval now, td, tvwait;
+ off_t abbrevsize;
+ int elapsed, ratio, barlength, i;
+ char buf[256];
+
+ if (flag == -1) { /* first call to progressmeter */
+ gettimeofday(&start, (struct timezone *) 0);
+ lastupdate = start;
+ lastsize = 0;
+ totalsize = content_len + beg_range; /* as content_len changes.. */
+ }
+
+ gettimeofday(&now, (struct timezone *) 0);
+ ratio = 100;
+ if (totalsize != 0 && !chunked) {
+ /* long long helps to have working ETA even if !LFS */
+ ratio = (int) (100 * (unsigned long long)(transferred+beg_range) / totalsize);
+ ratio = MIN(ratio, 100);
+ }
+
+ fprintf(stderr, "\r%-20.20s%4d%% ", curfile, ratio);
+
+ barlength = getttywidth() - 51;
+ if (barlength > 0 && barlength < sizeof(buf)) {
+ i = barlength * ratio / 100;
+ memset(buf, '*', i);
+ memset(buf + i, ' ', barlength - i);
+ buf[barlength] = '\0';
+ fprintf(stderr, "|%s|", buf);
+ }
+ i = 0;
+ abbrevsize = transferred + beg_range;
+ while (abbrevsize >= 100000) {
+ i++;
+ abbrevsize >>= 10;
+ }
+ /* see http://en.wikipedia.org/wiki/Tera */
+ fprintf(stderr, "%6d %c%c ", (int)abbrevsize, " KMGTPEZY"[i], i?'B':' ');
+
+ timersub(&now, &lastupdate, &tvwait);
+ if (transferred > lastsize) {
+ lastupdate = now;
+ lastsize = transferred;
+ if (tvwait.tv_sec >= STALLTIME)
+ timeradd(&start, &tvwait, &start);
+ tvwait.tv_sec = 0;
+ }
+ timersub(&now, &start, &td);
+ elapsed = td.tv_sec;
+
+ if (tvwait.tv_sec >= STALLTIME) {
+ fprintf(stderr, " - stalled -");
+ } else {
+ off_t to_download = totalsize - beg_range;
+ if (transferred <= 0 || elapsed <= 0 || transferred > to_download || chunked) {
+ fprintf(stderr, "--:--:-- ETA");
+ } else {
+ /* to_download / (transferred/elapsed) - elapsed: */
+ int eta = (int) ((unsigned long long)to_download*elapsed/transferred - elapsed);
+ /* (long long helps to have working ETA even if !LFS) */
+ i = eta % 3600;
+ fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60);
+ }
+ }
+
+ if (flag == -1) { /* first call to progressmeter */
+ 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) { /* last call to progressmeter */
+ alarmtimer(0);
+ transferred = 0;
+ putc('\n', stderr);
+ }
+}
+#endif
+
+/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
+ * much of which was blatantly 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.
+ *
+ */
diff --git a/i/pc104/initrd/conf/busybox/networking/zcip.c b/i/pc104/initrd/conf/busybox/networking/zcip.c
new file mode 100644
index 0000000..a8bfee6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/zcip.c
@@ -0,0 +1,548 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * RFC3927 ZeroConf IPv4 Link-Local addressing
+ * (see <http://www.zeroconf.org/>)
+ *
+ * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
+ * Copyright (C) 2004 by David Brownell
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/*
+ * ZCIP just manages the 169.254.*.* addresses. That network is not
+ * routed at the IP level, though various proxies or bridges can
+ * certainly be used. Its naming is built over multicast DNS.
+ */
+
+//#define DEBUG
+
+// TODO:
+// - more real-world usage/testing, especially daemon mode
+// - kernel packet filters to reduce scheduling noise
+// - avoid silent script failures, especially under load...
+// - link status monitoring (restart on link-up; stop on link-down)
+
+#include "busybox.h"
+#include <syslog.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <netinet/ether.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include <linux/if_packet.h>
+#include <linux/sockios.h>
+
+
+struct arp_packet {
+ struct ether_header hdr;
+ struct ether_arp arp;
+} ATTRIBUTE_PACKED;
+
+enum {
+/* 169.254.0.0 */
+ LINKLOCAL_ADDR = 0xa9fe0000,
+
+/* protocol timeout parameters, specified in seconds */
+ PROBE_WAIT = 1,
+ PROBE_MIN = 1,
+ PROBE_MAX = 2,
+ PROBE_NUM = 3,
+ MAX_CONFLICTS = 10,
+ RATE_LIMIT_INTERVAL = 60,
+ ANNOUNCE_WAIT = 2,
+ ANNOUNCE_NUM = 2,
+ ANNOUNCE_INTERVAL = 2,
+ DEFEND_INTERVAL = 10
+};
+
+/* States during the configuration process. */
+enum {
+ PROBE = 0,
+ RATE_LIMIT_PROBE,
+ ANNOUNCE,
+ MONITOR,
+ DEFEND
+};
+
+#define VDBG(fmt,args...) \
+ do { } while (0)
+
+static unsigned opts;
+#define FOREGROUND (opts & 1)
+#define QUIT (opts & 2)
+
+/**
+ * Pick a random link local IP address on 169.254/16, except that
+ * the first and last 256 addresses are reserved.
+ */
+static void pick(struct in_addr *ip)
+{
+ unsigned tmp;
+
+ /* use cheaper math than lrand48() mod N */
+ do {
+ tmp = (lrand48() >> 16) & IN_CLASSB_HOST;
+ } while (tmp > (IN_CLASSB_HOST - 0x0200));
+ ip->s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
+}
+
+/* TODO: we need a flag to direct bb_[p]error_msg output to stderr. */
+
+/**
+ * Broadcast an ARP packet.
+ */
+static void arp(int fd, struct sockaddr *saddr, int op,
+ const struct ether_addr *source_addr, struct in_addr source_ip,
+ const struct ether_addr *target_addr, struct in_addr target_ip)
+{
+ struct arp_packet p;
+ memset(&p, 0, sizeof(p));
+
+ // ether header
+ p.hdr.ether_type = htons(ETHERTYPE_ARP);
+ memcpy(p.hdr.ether_shost, source_addr, ETH_ALEN);
+ memset(p.hdr.ether_dhost, 0xff, ETH_ALEN);
+
+ // arp request
+ p.arp.arp_hrd = htons(ARPHRD_ETHER);
+ p.arp.arp_pro = htons(ETHERTYPE_IP);
+ p.arp.arp_hln = ETH_ALEN;
+ p.arp.arp_pln = 4;
+ p.arp.arp_op = htons(op);
+ memcpy(&p.arp.arp_sha, source_addr, ETH_ALEN);
+ memcpy(&p.arp.arp_spa, &source_ip, sizeof(p.arp.arp_spa));
+ memcpy(&p.arp.arp_tha, target_addr, ETH_ALEN);
+ memcpy(&p.arp.arp_tpa, &target_ip, sizeof(p.arp.arp_tpa));
+
+ // send it
+ if (sendto(fd, &p, sizeof(p), 0, saddr, sizeof(*saddr)) < 0) {
+ bb_perror_msg("sendto");
+ //return -errno;
+ }
+ // Currently all callers ignore errors, that's why returns are
+ // commented out...
+ //return 0;
+}
+
+/**
+ * Run a script.
+ */
+static int run(const char *script, const char *arg, const char *intf, struct in_addr *ip)
+{
+ int pid, status;
+ const char *why;
+
+ if(1) { //always true: if (script != NULL)
+ VDBG("%s run %s %s\n", intf, script, arg);
+ if (ip != NULL) {
+ char *addr = inet_ntoa(*ip);
+ setenv("ip", addr, 1);
+ bb_info_msg("%s %s %s", arg, intf, addr);
+ }
+
+ pid = vfork();
+ if (pid < 0) { // error
+ why = "vfork";
+ goto bad;
+ } else if (pid == 0) { // child
+ execl(script, script, arg, NULL);
+ bb_perror_msg("execl");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (waitpid(pid, &status, 0) <= 0) {
+ why = "waitpid";
+ goto bad;
+ }
+ if (WEXITSTATUS(status) != 0) {
+ bb_error_msg("script %s failed, exit=%d",
+ script, WEXITSTATUS(status));
+ return -errno;
+ }
+ }
+ return 0;
+bad:
+ status = -errno;
+ bb_perror_msg("%s %s, %s", arg, intf, why);
+ return status;
+}
+
+
+/**
+ * Return milliseconds of random delay, up to "secs" seconds.
+ */
+static unsigned ATTRIBUTE_ALWAYS_INLINE ms_rdelay(unsigned secs)
+{
+ return lrand48() % (secs * 1000);
+}
+
+/**
+ * main program
+ */
+
+/* Used to be auto variables on main() stack, but
+ * most of them were zero-inited. Moving them to bss
+ * is more space-efficient.
+ */
+static const struct in_addr null_ip; // = { 0 };
+static const struct ether_addr null_addr; // = { {0, 0, 0, 0, 0, 0} };
+
+static struct sockaddr saddr; // memset(0);
+static struct in_addr ip; // = { 0 };
+static struct ifreq ifr; //memset(0);
+
+static char *intf; // = NULL;
+static char *script; // = NULL;
+static suseconds_t timeout; // = 0; // milliseconds
+static unsigned conflicts; // = 0;
+static unsigned nprobes; // = 0;
+static unsigned nclaims; // = 0;
+static int ready; // = 0;
+static int verbose; // = 0;
+static int state = PROBE;
+
+int zcip_main(int argc, char *argv[]);
+int zcip_main(int argc, char *argv[])
+{
+ struct ether_addr eth_addr;
+ const char *why;
+ int fd;
+
+ // parse commandline: prog [options] ifname script
+ char *r_opt;
+ opt_complementary = "vv:vf"; // -v accumulates and implies -f
+ opts = getopt32(argc, argv, "fqr:v", &r_opt, &verbose);
+ if (!FOREGROUND) {
+ /* Do it early, before all bb_xx_msg calls */
+ logmode = LOGMODE_SYSLOG;
+ openlog(applet_name, 0, LOG_DAEMON);
+ }
+ if (opts & 4) { // -r n.n.n.n
+ if (inet_aton(r_opt, &ip) == 0
+ || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR
+ ) {
+ bb_error_msg_and_die("invalid link address");
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 2)
+ bb_show_usage();
+ intf = argv[0];
+ script = argv[1];
+ setenv("interface", intf, 1);
+
+ // initialize the interface (modprobe, ifup, etc)
+ if (run(script, "init", intf, NULL) < 0)
+ return EXIT_FAILURE;
+
+ // initialize saddr
+ //memset(&saddr, 0, sizeof(saddr));
+ safe_strncpy(saddr.sa_data, intf, sizeof(saddr.sa_data));
+
+ // open an ARP socket
+ fd = xsocket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
+ // bind to the interface's ARP socket
+ xbind(fd, &saddr, sizeof(saddr));
+
+ // get the interface's ethernet address
+ //memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, intf, sizeof(ifr.ifr_name));
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+ bb_perror_msg_and_die("get ethernet address");
+ }
+ memcpy(&eth_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ // start with some stable ip address, either a function of
+ // the hardware address or else the last address we used.
+ // NOTE: the sequence of addresses we try changes only
+ // depending on when we detect conflicts.
+ // (SVID 3 bogon: who says that "short" is always 16 bits?)
+ seed48( (unsigned short*)&ifr.ifr_hwaddr.sa_data );
+ if (ip.s_addr == 0)
+ pick(&ip);
+
+ // FIXME cases to handle:
+ // - zcip already running!
+ // - link already has local address... just defend/update
+
+ // daemonize now; don't delay system startup
+ if (!FOREGROUND) {
+ /* bb_daemonize(); - bad, will close fd! */
+ xdaemon(0, 0);
+ bb_info_msg("start, interface %s", intf);
+ }
+
+ // run the dynamic address negotiation protocol,
+ // restarting after address conflicts:
+ // - start with some address we want to try
+ // - short random delay
+ // - arp probes to see if another host else uses it
+ // - arp announcements that we're claiming it
+ // - use it
+ // - defend it, within limits
+ while (1) {
+ struct pollfd fds[1];
+ struct timeval tv1;
+ struct arp_packet p;
+
+ int source_ip_conflict = 0;
+ int target_ip_conflict = 0;
+
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+
+ // poll, being ready to adjust current timeout
+ if (!timeout) {
+ timeout = ms_rdelay(PROBE_WAIT);
+ // FIXME setsockopt(fd, SO_ATTACH_FILTER, ...) to
+ // make the kernel filter out all packets except
+ // ones we'd care about.
+ }
+ // set tv1 to the point in time when we timeout
+ gettimeofday(&tv1, NULL);
+ tv1.tv_usec += (timeout % 1000) * 1000;
+ while (tv1.tv_usec > 1000000) {
+ tv1.tv_usec -= 1000000;
+ tv1.tv_sec++;
+ }
+ tv1.tv_sec += timeout / 1000;
+
+ VDBG("...wait %ld %s nprobes=%d, nclaims=%d\n",
+ timeout, intf, nprobes, nclaims);
+ switch (poll(fds, 1, timeout)) {
+
+ // timeout
+ case 0:
+ VDBG("state = %d\n", state);
+ switch (state) {
+ case PROBE:
+ // timeouts in the PROBE state mean no conflicting ARP packets
+ // have been received, so we can progress through the states
+ if (nprobes < PROBE_NUM) {
+ nprobes++;
+ VDBG("probe/%d %s@%s\n",
+ nprobes, intf, inet_ntoa(ip));
+ arp(fd, &saddr, ARPOP_REQUEST,
+ &eth_addr, null_ip,
+ &null_addr, ip);
+ timeout = PROBE_MIN * 1000;
+ timeout += ms_rdelay(PROBE_MAX
+ - PROBE_MIN);
+ }
+ else {
+ // Switch to announce state.
+ state = ANNOUNCE;
+ nclaims = 0;
+ VDBG("announce/%d %s@%s\n",
+ nclaims, intf, inet_ntoa(ip));
+ arp(fd, &saddr, ARPOP_REQUEST,
+ &eth_addr, ip,
+ &eth_addr, ip);
+ timeout = ANNOUNCE_INTERVAL * 1000;
+ }
+ break;
+ case RATE_LIMIT_PROBE:
+ // timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets
+ // have been received, so we can move immediately to the announce state
+ state = ANNOUNCE;
+ nclaims = 0;
+ VDBG("announce/%d %s@%s\n",
+ nclaims, intf, inet_ntoa(ip));
+ arp(fd, &saddr, ARPOP_REQUEST,
+ &eth_addr, ip,
+ &eth_addr, ip);
+ timeout = ANNOUNCE_INTERVAL * 1000;
+ break;
+ case ANNOUNCE:
+ // timeouts in the ANNOUNCE state mean no conflicting ARP packets
+ // have been received, so we can progress through the states
+ if (nclaims < ANNOUNCE_NUM) {
+ nclaims++;
+ VDBG("announce/%d %s@%s\n",
+ nclaims, intf, inet_ntoa(ip));
+ arp(fd, &saddr, ARPOP_REQUEST,
+ &eth_addr, ip,
+ &eth_addr, ip);
+ timeout = ANNOUNCE_INTERVAL * 1000;
+ }
+ else {
+ // Switch to monitor state.
+ state = MONITOR;
+ // link is ok to use earlier
+ // FIXME update filters
+ run(script, "config", intf, &ip);
+ ready = 1;
+ conflicts = 0;
+ timeout = -1; // Never timeout in the monitor state.
+
+ // NOTE: all other exit paths
+ // should deconfig ...
+ if (QUIT)
+ return EXIT_SUCCESS;
+ }
+ break;
+ case DEFEND:
+ // We won! No ARP replies, so just go back to monitor.
+ state = MONITOR;
+ timeout = -1;
+ conflicts = 0;
+ break;
+ default:
+ // Invalid, should never happen. Restart the whole protocol.
+ state = PROBE;
+ pick(&ip);
+ timeout = 0;
+ nprobes = 0;
+ nclaims = 0;
+ break;
+ } // switch (state)
+ break; // case 0 (timeout)
+ // packets arriving
+ case 1:
+ // We need to adjust the timeout in case we didn't receive
+ // a conflicting packet.
+ if (timeout > 0) {
+ struct timeval tv2;
+
+ gettimeofday(&tv2, NULL);
+ if (timercmp(&tv1, &tv2, <)) {
+ // Current time is greater than the expected timeout time.
+ // Should never happen.
+ VDBG("missed an expected timeout\n");
+ timeout = 0;
+ } else {
+ VDBG("adjusting timeout\n");
+ timersub(&tv1, &tv2, &tv1);
+ timeout = 1000 * tv1.tv_sec
+ + tv1.tv_usec / 1000;
+ }
+ }
+
+ if ((fds[0].revents & POLLIN) == 0) {
+ if (fds[0].revents & POLLERR) {
+ // FIXME: links routinely go down;
+ // this shouldn't necessarily exit.
+ bb_error_msg("%s: poll error", intf);
+ if (ready) {
+ run(script, "deconfig",
+ intf, &ip);
+ }
+ return EXIT_FAILURE;
+ }
+ continue;
+ }
+
+ // read ARP packet
+ if (recv(fd, &p, sizeof(p), 0) < 0) {
+ why = "recv";
+ goto bad;
+ }
+ if (p.hdr.ether_type != htons(ETHERTYPE_ARP))
+ continue;
+
+#ifdef DEBUG
+ {
+ struct ether_addr * sha = (struct ether_addr *) p.arp.arp_sha;
+ struct ether_addr * tha = (struct ether_addr *) p.arp.arp_tha;
+ struct in_addr * spa = (struct in_addr *) p.arp.arp_spa;
+ struct in_addr * tpa = (struct in_addr *) p.arp.arp_tpa;
+ VDBG("%s recv arp type=%d, op=%d,\n",
+ intf, ntohs(p.hdr.ether_type),
+ ntohs(p.arp.arp_op));
+ VDBG("\tsource=%s %s\n",
+ ether_ntoa(sha),
+ inet_ntoa(*spa));
+ VDBG("\ttarget=%s %s\n",
+ ether_ntoa(tha),
+ inet_ntoa(*tpa));
+ }
+#endif
+ if (p.arp.arp_op != htons(ARPOP_REQUEST)
+ && p.arp.arp_op != htons(ARPOP_REPLY))
+ continue;
+
+ if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0 &&
+ memcmp(&eth_addr, &p.arp.arp_sha, ETH_ALEN) != 0) {
+ source_ip_conflict = 1;
+ }
+ if (memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 &&
+ p.arp.arp_op == htons(ARPOP_REQUEST) &&
+ memcmp(&eth_addr, &p.arp.arp_tha, ETH_ALEN) != 0) {
+ target_ip_conflict = 1;
+ }
+
+ VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n",
+ state, source_ip_conflict, target_ip_conflict);
+ switch (state) {
+ case PROBE:
+ case ANNOUNCE:
+ // When probing or announcing, check for source IP conflicts
+ // and other hosts doing ARP probes (target IP conflicts).
+ if (source_ip_conflict || target_ip_conflict) {
+ conflicts++;
+ if (conflicts >= MAX_CONFLICTS) {
+ VDBG("%s ratelimit\n", intf);
+ timeout = RATE_LIMIT_INTERVAL * 1000;
+ state = RATE_LIMIT_PROBE;
+ }
+
+ // restart the whole protocol
+ pick(&ip);
+ timeout = 0;
+ nprobes = 0;
+ nclaims = 0;
+ }
+ break;
+ case MONITOR:
+ // If a conflict, we try to defend with a single ARP probe.
+ if (source_ip_conflict) {
+ VDBG("monitor conflict -- defending\n");
+ state = DEFEND;
+ timeout = DEFEND_INTERVAL * 1000;
+ arp(fd, &saddr,
+ ARPOP_REQUEST,
+ &eth_addr, ip,
+ &eth_addr, ip);
+ }
+ break;
+ case DEFEND:
+ // Well, we tried. Start over (on conflict).
+ if (source_ip_conflict) {
+ state = PROBE;
+ VDBG("defend conflict -- starting over\n");
+ ready = 0;
+ run(script, "deconfig", intf, &ip);
+
+ // restart the whole protocol
+ pick(&ip);
+ timeout = 0;
+ nprobes = 0;
+ nclaims = 0;
+ }
+ break;
+ default:
+ // Invalid, should never happen. Restart the whole protocol.
+ VDBG("invalid state -- starting over\n");
+ state = PROBE;
+ pick(&ip);
+ timeout = 0;
+ nprobes = 0;
+ nclaims = 0;
+ break;
+ } // switch state
+
+ break; // case 1 (packets arriving)
+ default:
+ why = "poll";
+ goto bad;
+ } // switch poll
+ }
+bad:
+ bb_perror_msg("%s, %s", intf, why);
+ return EXIT_FAILURE;
+}