diff options
| author | russ <russ@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2002-10-14 21:41:28 +0000 |
|---|---|---|
| committer | russ <russ@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2002-10-14 21:41:28 +0000 |
| commit | ee4f31f4963edadf70a7dafc9746d1183acedd8f (patch) | |
| tree | e3b93e0a694be81939f8c4762553c43ffdb9b10b | |
| parent | 06bcc768d224c3fed6f298d58b2b88873de4a4d1 (diff) | |
| download | busybox-w32-ee4f31f4963edadf70a7dafc9746d1183acedd8f.tar.gz busybox-w32-ee4f31f4963edadf70a7dafc9746d1183acedd8f.tar.bz2 busybox-w32-ee4f31f4963edadf70a7dafc9746d1183acedd8f.zip | |
added full udhcp integration
git-svn-id: svn://busybox.net/trunk/busybox@5647 69ca8d6d-28ef-0310-b511-8ec308f3f277
48 files changed, 4421 insertions, 1597 deletions
| @@ -22,7 +22,7 @@ include $(TOPDIR).config | |||
| 22 | include $(TOPDIR)Rules.mak | 22 | include $(TOPDIR)Rules.mak |
| 23 | SUBDIRS:=applets archival archival/libunarchive console-tools \ | 23 | SUBDIRS:=applets archival archival/libunarchive console-tools \ |
| 24 | editors fileutils findutils init miscutils modutils networking \ | 24 | editors fileutils findutils init miscutils modutils networking \ |
| 25 | procps loginutils shell shellutils sysklogd \ | 25 | networking/udhcp procps loginutils shell shellutils sysklogd \ |
| 26 | textutils util-linux libbb libpwdgrp | 26 | textutils util-linux libbb libpwdgrp |
| 27 | 27 | ||
| 28 | all: do-it-all | 28 | all: do-it-all |
diff --git a/examples/udhcpc/default.bound b/examples/udhcp/sample.bound index 98f3aa97e..200352672 100755 --- a/examples/udhcpc/default.bound +++ b/examples/udhcp/sample.bound | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/sh |
| 2 | # Sample udhcpc renew script | 2 | # Sample udhcpc renew script |
| 3 | 3 | ||
| 4 | RESOLV_CONF="/etc/resolv.conf" | 4 | RESOLV_CONF="/etc/udhcpc/resolv.conf" |
| 5 | 5 | ||
| 6 | [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" | 6 | [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" |
| 7 | [ -n "$subnet" ] && NETMASK="netmask $subnet" | 7 | [ -n "$subnet" ] && NETMASK="netmask $subnet" |
diff --git a/examples/udhcpc/default.deconfig b/examples/udhcp/sample.deconfig index b221bcf12..b221bcf12 100755 --- a/examples/udhcpc/default.deconfig +++ b/examples/udhcp/sample.deconfig | |||
diff --git a/examples/udhcp/sample.nak b/examples/udhcp/sample.nak new file mode 100755 index 000000000..f4d08e669 --- /dev/null +++ b/examples/udhcp/sample.nak | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # Sample udhcpc nak script | ||
| 3 | |||
| 4 | echo Received a NAK: $message | ||
diff --git a/examples/udhcpc/default.renew b/examples/udhcp/sample.renew index b64c1fe72..c953e9758 100755 --- a/examples/udhcpc/default.renew +++ b/examples/udhcp/sample.renew | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/sh |
| 2 | # Sample udhcpc bound script | 2 | # Sample udhcpc bound script |
| 3 | 3 | ||
| 4 | RESOLV_CONF="/etc/resolv.conf" | 4 | RESOLV_CONF="/etc/udhcpc/resolv.conf" |
| 5 | 5 | ||
| 6 | [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" | 6 | [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" |
| 7 | [ -n "$subnet" ] && NETMASK="netmask $subnet" | 7 | [ -n "$subnet" ] && NETMASK="netmask $subnet" |
| @@ -27,4 +27,4 @@ for i in $dns | |||
| 27 | do | 27 | do |
| 28 | echo adding dns $i | 28 | echo adding dns $i |
| 29 | echo nameserver $i >> $RESOLV_CONF | 29 | echo nameserver $i >> $RESOLV_CONF |
| 30 | done | 30 | done \ No newline at end of file |
diff --git a/examples/udhcpc/default.script b/examples/udhcp/sample.script index f5920ba96..9b717ac3c 100644 --- a/examples/udhcpc/default.script +++ b/examples/udhcp/sample.script | |||
| @@ -4,4 +4,4 @@ | |||
| 4 | # common initialization first, especially if more dhcp event notifications | 4 | # common initialization first, especially if more dhcp event notifications |
| 5 | # are added. | 5 | # are added. |
| 6 | 6 | ||
| 7 | exec /usr/share/udhcpc/default.$1 | 7 | exec /usr/share/udhcpc/sample.$1 |
diff --git a/examples/udhcp/simple.script b/examples/udhcp/simple.script new file mode 100644 index 000000000..a52a7f812 --- /dev/null +++ b/examples/udhcp/simple.script | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | # udhcpc script edited by Tim Riker <Tim@Rikers.org> | ||
| 4 | |||
| 5 | [ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1 | ||
| 6 | |||
| 7 | RESOLV_CONF="/etc/resolv.conf" | ||
| 8 | [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" | ||
| 9 | [ -n "$subnet" ] && NETMASK="netmask $subnet" | ||
| 10 | |||
| 11 | case "$1" in | ||
| 12 | deconfig) | ||
| 13 | /sbin/ifconfig $interface 0.0.0.0 | ||
| 14 | ;; | ||
| 15 | |||
| 16 | renew|bound) | ||
| 17 | /sbin/ifconfig $interface $ip $BROADCAST $NETMASK | ||
| 18 | |||
| 19 | if [ -n "$router" ] ; then | ||
| 20 | echo "deleting routers" | ||
| 21 | while route del default gw 0.0.0.0 dev $interface ; do | ||
| 22 | : | ||
| 23 | done | ||
| 24 | |||
| 25 | for i in $router ; do | ||
| 26 | route add default gw $i dev $interface | ||
| 27 | done | ||
| 28 | fi | ||
| 29 | |||
| 30 | echo -n > $RESOLV_CONF | ||
| 31 | [ -n "$domain" ] && echo search $domain >> $RESOLV_CONF | ||
| 32 | for i in $dns ; do | ||
| 33 | echo adding dns $i | ||
| 34 | echo nameserver $i >> $RESOLV_CONF | ||
| 35 | done | ||
| 36 | ;; | ||
| 37 | esac | ||
| 38 | |||
| 39 | exit 0 | ||
diff --git a/examples/udhcp/udhcpd.conf b/examples/udhcp/udhcpd.conf new file mode 100644 index 000000000..36cb58c3f --- /dev/null +++ b/examples/udhcp/udhcpd.conf | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | # Sample udhcpd configuration file (/etc/udhcpd.conf) | ||
| 2 | |||
| 3 | # The start and end of the IP lease block | ||
| 4 | |||
| 5 | start 192.168.0.20 #default: 192.168.0.20 | ||
| 6 | end 192.168.0.254 #default: 192.168.0.254 | ||
| 7 | |||
| 8 | |||
| 9 | # The interface that udhcpd will use | ||
| 10 | |||
| 11 | interface eth0 #default: eth0 | ||
| 12 | |||
| 13 | |||
| 14 | # The maximim number of leases (includes addressesd reserved | ||
| 15 | # by OFFER's, DECLINE's, and ARP conficts | ||
| 16 | |||
| 17 | #max_leases 254 #default: 254 | ||
| 18 | |||
| 19 | |||
| 20 | # If remaining is true (default), udhcpd will store the time | ||
| 21 | # remaining for each lease in the udhcpd leases file. This is | ||
| 22 | # for embedded systems that cannot keep time between reboots. | ||
| 23 | # If you set remaining to no, the absolute time that the lease | ||
| 24 | # expires at will be stored in the dhcpd.leases file. | ||
| 25 | |||
| 26 | #remaining yes #default: yes | ||
| 27 | |||
| 28 | |||
| 29 | # The time period at which udhcpd will write out a dhcpd.leases | ||
| 30 | # file. If this is 0, udhcpd will never automatically write a | ||
| 31 | # lease file. (specified in seconds) | ||
| 32 | |||
| 33 | #auto_time 7200 #default: 7200 (2 hours) | ||
| 34 | |||
| 35 | |||
| 36 | # The amount of time that an IP will be reserved (leased) for if a | ||
| 37 | # DHCP decline message is received (seconds). | ||
| 38 | |||
| 39 | #decline_time 3600 #default: 3600 (1 hour) | ||
| 40 | |||
| 41 | |||
| 42 | # The amount of time that an IP will be reserved (leased) for if an | ||
| 43 | # ARP conflct occurs. (seconds | ||
| 44 | |||
| 45 | #conflict_time 3600 #default: 3600 (1 hour) | ||
| 46 | |||
| 47 | |||
| 48 | # How long an offered address is reserved (leased) in seconds | ||
| 49 | |||
| 50 | #offer_time 60 #default: 60 (1 minute) | ||
| 51 | |||
| 52 | # If a lease to be given is below this value, the full lease time is | ||
| 53 | # instead used (seconds). | ||
| 54 | |||
| 55 | #min_lease 60 #defult: 60 | ||
| 56 | |||
| 57 | |||
| 58 | # The location of the leases file | ||
| 59 | |||
| 60 | #lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases | ||
| 61 | |||
| 62 | # The location of the pid file | ||
| 63 | #pidfile /var/run/udhcpd.pid #default: /var/run/udhcpd.pid | ||
| 64 | |||
| 65 | # Everytime udhcpd writes a leases file, the below script will be called. | ||
| 66 | # Useful for writing the lease file to flash every few hours. | ||
| 67 | |||
| 68 | #notify_file #default: (no script) | ||
| 69 | |||
| 70 | #notify_file dumpleases # <--- usefull for debugging | ||
| 71 | |||
| 72 | # The following are bootp specific options, setable by udhcpd. | ||
| 73 | |||
| 74 | #siaddr 192.168.0.22 #default: 0.0.0.0 | ||
| 75 | |||
| 76 | #sname zorak #default: (none) | ||
| 77 | |||
| 78 | #boot_file /var/nfs_root #default: (none) | ||
| 79 | |||
| 80 | # The remainer of options are DHCP options and can be specifed with the | ||
| 81 | # keyword 'opt' or 'option'. If an option can take multiple items, such | ||
| 82 | # as the dns option, they can be listed on the same line, or multiple | ||
| 83 | # lines. The only option with a default is 'lease'. | ||
| 84 | |||
| 85 | #Examles | ||
| 86 | opt dns 192.168.10.2 192.168.10.10 | ||
| 87 | option subnet 255.255.255.0 | ||
| 88 | opt router 192.168.10.2 | ||
| 89 | opt wins 192.168.10.10 | ||
| 90 | option dns 129.219.13.81 # appened to above DNS servers for a total of 3 | ||
| 91 | option domain local | ||
| 92 | option lease 864000 # 10 days of seconds | ||
| 93 | |||
| 94 | |||
| 95 | # Currently supported options, for more info, see options.c | ||
| 96 | #subnet | ||
| 97 | #timezone | ||
| 98 | #router | ||
| 99 | #timesvr | ||
| 100 | #namesvr | ||
| 101 | #dns | ||
| 102 | #logsvr | ||
| 103 | #cookiesvr | ||
| 104 | #lprsvr | ||
| 105 | #bootsize | ||
| 106 | #domain | ||
| 107 | #swapsvr | ||
| 108 | #rootpath | ||
| 109 | #ipttl | ||
| 110 | #mtu | ||
| 111 | #broadcast | ||
| 112 | #wins | ||
| 113 | #lease | ||
| 114 | #ntpsrv | ||
| 115 | #tftp | ||
| 116 | #bootfile \ No newline at end of file | ||
diff --git a/include/applets.h b/include/applets.h index 60928aef2..e59342051 100644 --- a/include/applets.h +++ b/include/applets.h | |||
| @@ -152,6 +152,9 @@ | |||
| 152 | #ifdef CONFIG_DUMPKMAP | 152 | #ifdef CONFIG_DUMPKMAP |
| 153 | APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN, _BB_SUID_NEVER) | 153 | APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN, _BB_SUID_NEVER) |
| 154 | #endif | 154 | #endif |
| 155 | #ifdef CONFIG_DUMPLEASES | ||
| 156 | APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) | ||
| 157 | #endif | ||
| 155 | #ifdef CONFIG_DUTMP | 158 | #ifdef CONFIG_DUTMP |
| 156 | APPLET(dutmp, dutmp_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) | 159 | APPLET(dutmp, dutmp_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) |
| 157 | #endif | 160 | #endif |
| @@ -495,7 +498,10 @@ | |||
| 495 | APPLET(tty, tty_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) | 498 | APPLET(tty, tty_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) |
| 496 | #endif | 499 | #endif |
| 497 | #ifdef CONFIG_UDHCPC | 500 | #ifdef CONFIG_UDHCPC |
| 498 | APPLET(udhcpc, udhcpc_main, _BB_DIR_SBIN, _BB_SUID_NEVER) | 501 | APPLET(udhcpc, udhcpc_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) |
| 502 | #endif | ||
| 503 | #ifdef CONFIG_UDHCPD | ||
| 504 | APPLET(udhcpd, udhcpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) | ||
| 499 | #endif | 505 | #endif |
| 500 | #ifdef CONFIG_UMOUNT | 506 | #ifdef CONFIG_UMOUNT |
| 501 | APPLET(umount, umount_main, _BB_DIR_BIN, _BB_SUID_NEVER) | 507 | APPLET(umount, umount_main, _BB_DIR_BIN, _BB_SUID_NEVER) |
diff --git a/include/usage.h b/include/usage.h index 8dd501dde..324341524 100644 --- a/include/usage.h +++ b/include/usage.h | |||
| @@ -385,6 +385,14 @@ | |||
| 385 | #define dumpkmap_example_usage \ | 385 | #define dumpkmap_example_usage \ |
| 386 | "$ dumpkmap > keymap\n" | 386 | "$ dumpkmap > keymap\n" |
| 387 | 387 | ||
| 388 | #define dumpleases_trivial_usage \ | ||
| 389 | "[-r|-a] [-f LEASEFILE]" | ||
| 390 | #define dumpleases_full_usage \ | ||
| 391 | "Usage: dumpleases -f <file> -[r|a]\n" \ | ||
| 392 | "\t-f,\t--file=FILENAME\tLeases file to load\n" \ | ||
| 393 | "\t-r,\t--remaining\tInterepret lease times as time remaing\n" \ | ||
| 394 | "\t-a,\t--absolute\tInterepret lease times as expire time\n" | ||
| 395 | |||
| 388 | #define dutmp_trivial_usage \ | 396 | #define dutmp_trivial_usage \ |
| 389 | "[FILE]" | 397 | "[FILE]" |
| 390 | #define dutmp_full_usage \ | 398 | #define dutmp_full_usage \ |
| @@ -1945,12 +1953,14 @@ | |||
| 1945 | "/dev/tty2\n" | 1953 | "/dev/tty2\n" |
| 1946 | 1954 | ||
| 1947 | #define udhcpc_trivial_usage \ | 1955 | #define udhcpc_trivial_usage \ |
| 1948 | "[-fqv] [-c CLIENTID] [-H HOSTNAME] [-i INTERFACE]\n[-p file] [-r IP] [-s script]" | 1956 | "[-fbnqv] [-c CLIENTID] [-H HOSTNAME] [-i INTERFACE]\n[-p pidfile] [-r IP] [-s script]" |
| 1949 | #define udhcpc_full_usage \ | 1957 | #define udhcpc_full_usage \ |
| 1950 | "\tUsage: udhcpcd [OPTIONS]\n" \ | 1958 | "\tUsage: udhcpc [OPTIONS]\n" \ |
| 1951 | "\t-c,\t--clientid=CLIENTID\tClient identifier\n" \ | 1959 | "\t-c,\t--clientid=CLIENTID\tClient identifier\n" \ |
| 1952 | "\t-H,\t--hostname=HOSTNAME\tClient hostname\n" \ | 1960 | "\t-H,\t--hostname=HOSTNAME\tClient hostname\n" \ |
| 1961 | "\t-h,\t \tAlias for -h\n" \ | ||
| 1953 | "\t-f,\t--foreground\tDo not fork after getting lease\n" \ | 1962 | "\t-f,\t--foreground\tDo not fork after getting lease\n" \ |
| 1963 | "\t-b,\t--background\tFork to background if lease cannot be immediately negotiated.\n" \ | ||
| 1954 | "\t-i,\t--interface=INTERFACE\tInterface to use (default: eth0)\n" \ | 1964 | "\t-i,\t--interface=INTERFACE\tInterface to use (default: eth0)\n" \ |
| 1955 | "\t-n,\t--now\tExit with failure if lease cannot be immediately negotiated.\n" \ | 1965 | "\t-n,\t--now\tExit with failure if lease cannot be immediately negotiated.\n" \ |
| 1956 | "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \ | 1966 | "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \ |
| @@ -1959,6 +1969,12 @@ | |||
| 1959 | "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \ | 1969 | "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \ |
| 1960 | "\t-v,\t--version\tDisplay version" | 1970 | "\t-v,\t--version\tDisplay version" |
| 1961 | 1971 | ||
| 1972 | #define udhcpd_trivial_usage \ | ||
| 1973 | "[configfile]\n" \ | ||
| 1974 | |||
| 1975 | #define udhcpd_full_usage \ | ||
| 1976 | "\tUsage: udhcpd [configfile]\n" | ||
| 1977 | |||
| 1962 | #ifdef CONFIG_FEATURE_MOUNT_FORCE | 1978 | #ifdef CONFIG_FEATURE_MOUNT_FORCE |
| 1963 | #define USAGE_MOUNT_FORCE(a) a | 1979 | #define USAGE_MOUNT_FORCE(a) a |
| 1964 | #else | 1980 | #else |
diff --git a/networking/Makefile.in b/networking/Makefile.in index 510c8119d..d6c8592d2 100644 --- a/networking/Makefile.in +++ b/networking/Makefile.in | |||
| @@ -35,7 +35,6 @@ NETWORKING-$(CONFIG_TELNET) += telnet.o | |||
| 35 | NETWORKING-$(CONFIG_TELNETD) += telnetd.o | 35 | NETWORKING-$(CONFIG_TELNETD) += telnetd.o |
| 36 | NETWORKING-$(CONFIG_TFTP) += tftp.o | 36 | NETWORKING-$(CONFIG_TFTP) += tftp.o |
| 37 | NETWORKING-$(CONFIG_TRACEROUTE) += traceroute.o | 37 | NETWORKING-$(CONFIG_TRACEROUTE) += traceroute.o |
| 38 | NETWORKING-$(CONFIG_UDHCPC) += udhcpc.o | ||
| 39 | NETWORKING-$(CONFIG_WGET) += wget.o | 38 | NETWORKING-$(CONFIG_WGET) += wget.o |
| 40 | 39 | ||
| 41 | libraries-y+=$(NETWORKING_DIR)$(NETWORKING_AR) | 40 | libraries-y+=$(NETWORKING_DIR)$(NETWORKING_AR) |
diff --git a/networking/config.in b/networking/config.in index 4438a8c6c..bc14940d5 100644 --- a/networking/config.in +++ b/networking/config.in | |||
| @@ -47,12 +47,13 @@ if [ "$CONFIG_TRACEROUTE" = "y" ]; then | |||
| 47 | bool ' Enable verbose output' CONFIG_FEATURE_TRACEROUTE_VERBOSE | 47 | bool ' Enable verbose output' CONFIG_FEATURE_TRACEROUTE_VERBOSE |
| 48 | bool ' Enable SO_DEBUG option' CONFIG_FEATURE_TRACEROUTE_SO_DEBUG | 48 | bool ' Enable SO_DEBUG option' CONFIG_FEATURE_TRACEROUTE_SO_DEBUG |
| 49 | fi | 49 | fi |
| 50 | bool 'udhcpc' CONFIG_UDHCPC | ||
| 51 | bool 'wget' CONFIG_WGET | 50 | bool 'wget' CONFIG_WGET |
| 52 | if [ "$CONFIG_WGET" = "y" ]; then | 51 | if [ "$CONFIG_WGET" = "y" ]; then |
| 53 | bool ' Enable a nifty process meter (+2k)' CONFIG_FEATURE_WGET_STATUSBAR | 52 | bool ' Enable a nifty process meter (+2k)' CONFIG_FEATURE_WGET_STATUSBAR |
| 54 | bool ' Enable HTTP authentication' CONFIG_FEATURE_WGET_AUTHENTICATION | 53 | bool ' Enable HTTP authentication' CONFIG_FEATURE_WGET_AUTHENTICATION |
| 55 | fi | 54 | fi |
| 56 | 55 | ||
| 56 | source networking/udhcp/config.in | ||
| 57 | |||
| 57 | endmenu | 58 | endmenu |
| 58 | 59 | ||
diff --git a/networking/udhcp/AUTHORS b/networking/udhcp/AUTHORS new file mode 100644 index 000000000..89a6de41d --- /dev/null +++ b/networking/udhcp/AUTHORS | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | udhcp server/client package | ||
| 2 | ----------------------- | ||
| 3 | |||
| 4 | Russ Dill <Russ.Dill@asu.edu> | ||
| 5 | Matthew Ramsay <matthewr@moreton.com.au> | ||
| 6 | Chris Trew <christ@moreton.com.au> | ||
| 7 | |||
| 8 | Other Credits: | ||
| 9 | -------------- | ||
| 10 | Moreton Bay (http://www.moretonbay.com/) | ||
| 11 | Lineo (http://opensource.lineo.com) | ||
| 12 | |||
| 13 | |||
diff --git a/networking/udhcp/COPYING b/networking/udhcp/COPYING new file mode 100644 index 000000000..a43ea2126 --- /dev/null +++ b/networking/udhcp/COPYING | |||
| @@ -0,0 +1,339 @@ | |||
| 1 | GNU GENERAL PUBLIC LICENSE | ||
| 2 | Version 2, June 1991 | ||
| 3 | |||
| 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. | ||
| 5 | 675 Mass Ave, Cambridge, MA 02139, USA | ||
| 6 | Everyone is permitted to copy and distribute verbatim copies | ||
| 7 | of this license document, but changing it is not allowed. | ||
| 8 | |||
| 9 | Preamble | ||
| 10 | |||
| 11 | The licenses for most software are designed to take away your | ||
| 12 | freedom to share and change it. By contrast, the GNU General Public | ||
| 13 | License is intended to guarantee your freedom to share and change free | ||
| 14 | software--to make sure the software is free for all its users. This | ||
| 15 | General Public License applies to most of the Free Software | ||
| 16 | Foundation's software and to any other program whose authors commit to | ||
| 17 | using it. (Some other Free Software Foundation software is covered by | ||
| 18 | the GNU Library General Public License instead.) You can apply it to | ||
| 19 | your programs, too. | ||
| 20 | |||
| 21 | When we speak of free software, we are referring to freedom, not | ||
| 22 | price. Our General Public Licenses are designed to make sure that you | ||
| 23 | have the freedom to distribute copies of free software (and charge for | ||
| 24 | this service if you wish), that you receive source code or can get it | ||
| 25 | if you want it, that you can change the software or use pieces of it | ||
| 26 | in new free programs; and that you know you can do these things. | ||
| 27 | |||
| 28 | To protect your rights, we need to make restrictions that forbid | ||
| 29 | anyone to deny you these rights or to ask you to surrender the rights. | ||
| 30 | These restrictions translate to certain responsibilities for you if you | ||
| 31 | distribute copies of the software, or if you modify it. | ||
| 32 | |||
| 33 | For example, if you distribute copies of such a program, whether | ||
| 34 | gratis or for a fee, you must give the recipients all the rights that | ||
| 35 | you have. You must make sure that they, too, receive or can get the | ||
| 36 | source code. And you must show them these terms so they know their | ||
| 37 | rights. | ||
| 38 | |||
| 39 | We protect your rights with two steps: (1) copyright the software, and | ||
| 40 | (2) offer you this license which gives you legal permission to copy, | ||
| 41 | distribute and/or modify the software. | ||
| 42 | |||
| 43 | Also, for each author's protection and ours, we want to make certain | ||
| 44 | that everyone understands that there is no warranty for this free | ||
| 45 | software. If the software is modified by someone else and passed on, we | ||
| 46 | want its recipients to know that what they have is not the original, so | ||
| 47 | that any problems introduced by others will not reflect on the original | ||
| 48 | authors' reputations. | ||
| 49 | |||
| 50 | Finally, any free program is threatened constantly by software | ||
| 51 | patents. We wish to avoid the danger that redistributors of a free | ||
| 52 | program will individually obtain patent licenses, in effect making the | ||
| 53 | program proprietary. To prevent this, we have made it clear that any | ||
| 54 | patent must be licensed for everyone's free use or not licensed at all. | ||
| 55 | |||
| 56 | The precise terms and conditions for copying, distribution and | ||
| 57 | modification follow. | ||
| 58 | |||
| 59 | GNU GENERAL PUBLIC LICENSE | ||
| 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||
| 61 | |||
| 62 | 0. This License applies to any program or other work which contains | ||
| 63 | a notice placed by the copyright holder saying it may be distributed | ||
| 64 | under the terms of this General Public License. The "Program", below, | ||
| 65 | refers to any such program or work, and a "work based on the Program" | ||
| 66 | means either the Program or any derivative work under copyright law: | ||
| 67 | that is to say, a work containing the Program or a portion of it, | ||
| 68 | either verbatim or with modifications and/or translated into another | ||
| 69 | language. (Hereinafter, translation is included without limitation in | ||
| 70 | the term "modification".) Each licensee is addressed as "you". | ||
| 71 | |||
| 72 | Activities other than copying, distribution and modification are not | ||
| 73 | covered by this License; they are outside its scope. The act of | ||
| 74 | running the Program is not restricted, and the output from the Program | ||
| 75 | is covered only if its contents constitute a work based on the | ||
| 76 | Program (independent of having been made by running the Program). | ||
| 77 | Whether that is true depends on what the Program does. | ||
| 78 | |||
| 79 | 1. You may copy and distribute verbatim copies of the Program's | ||
| 80 | source code as you receive it, in any medium, provided that you | ||
| 81 | conspicuously and appropriately publish on each copy an appropriate | ||
| 82 | copyright notice and disclaimer of warranty; keep intact all the | ||
| 83 | notices that refer to this License and to the absence of any warranty; | ||
| 84 | and give any other recipients of the Program a copy of this License | ||
| 85 | along with the Program. | ||
| 86 | |||
| 87 | You may charge a fee for the physical act of transferring a copy, and | ||
| 88 | you may at your option offer warranty protection in exchange for a fee. | ||
| 89 | |||
| 90 | 2. You may modify your copy or copies of the Program or any portion | ||
| 91 | of it, thus forming a work based on the Program, and copy and | ||
| 92 | distribute such modifications or work under the terms of Section 1 | ||
| 93 | above, provided that you also meet all of these conditions: | ||
| 94 | |||
| 95 | a) You must cause the modified files to carry prominent notices | ||
| 96 | stating that you changed the files and the date of any change. | ||
| 97 | |||
| 98 | b) You must cause any work that you distribute or publish, that in | ||
| 99 | whole or in part contains or is derived from the Program or any | ||
| 100 | part thereof, to be licensed as a whole at no charge to all third | ||
| 101 | parties under the terms of this License. | ||
| 102 | |||
| 103 | c) If the modified program normally reads commands interactively | ||
| 104 | when run, you must cause it, when started running for such | ||
| 105 | interactive use in the most ordinary way, to print or display an | ||
| 106 | announcement including an appropriate copyright notice and a | ||
| 107 | notice that there is no warranty (or else, saying that you provide | ||
| 108 | a warranty) and that users may redistribute the program under | ||
| 109 | these conditions, and telling the user how to view a copy of this | ||
| 110 | License. (Exception: if the Program itself is interactive but | ||
| 111 | does not normally print such an announcement, your work based on | ||
| 112 | the Program is not required to print an announcement.) | ||
| 113 | |||
| 114 | These requirements apply to the modified work as a whole. If | ||
| 115 | identifiable sections of that work are not derived from the Program, | ||
| 116 | and can be reasonably considered independent and separate works in | ||
| 117 | themselves, then this License, and its terms, do not apply to those | ||
| 118 | sections when you distribute them as separate works. But when you | ||
| 119 | distribute the same sections as part of a whole which is a work based | ||
| 120 | on the Program, the distribution of the whole must be on the terms of | ||
| 121 | this License, whose permissions for other licensees extend to the | ||
| 122 | entire whole, and thus to each and every part regardless of who wrote it. | ||
| 123 | |||
| 124 | Thus, it is not the intent of this section to claim rights or contest | ||
| 125 | your rights to work written entirely by you; rather, the intent is to | ||
| 126 | exercise the right to control the distribution of derivative or | ||
| 127 | collective works based on the Program. | ||
| 128 | |||
| 129 | In addition, mere aggregation of another work not based on the Program | ||
| 130 | with the Program (or with a work based on the Program) on a volume of | ||
| 131 | a storage or distribution medium does not bring the other work under | ||
| 132 | the scope of this License. | ||
| 133 | |||
| 134 | 3. You may copy and distribute the Program (or a work based on it, | ||
| 135 | under Section 2) in object code or executable form under the terms of | ||
| 136 | Sections 1 and 2 above provided that you also do one of the following: | ||
| 137 | |||
| 138 | a) Accompany it with the complete corresponding machine-readable | ||
| 139 | source code, which must be distributed under the terms of Sections | ||
| 140 | 1 and 2 above on a medium customarily used for software interchange; or, | ||
| 141 | |||
| 142 | b) Accompany it with a written offer, valid for at least three | ||
| 143 | years, to give any third party, for a charge no more than your | ||
| 144 | cost of physically performing source distribution, a complete | ||
| 145 | machine-readable copy of the corresponding source code, to be | ||
| 146 | distributed under the terms of Sections 1 and 2 above on a medium | ||
| 147 | customarily used for software interchange; or, | ||
| 148 | |||
| 149 | c) Accompany it with the information you received as to the offer | ||
| 150 | to distribute corresponding source code. (This alternative is | ||
| 151 | allowed only for noncommercial distribution and only if you | ||
| 152 | received the program in object code or executable form with such | ||
| 153 | an offer, in accord with Subsection b above.) | ||
| 154 | |||
| 155 | The source code for a work means the preferred form of the work for | ||
| 156 | making modifications to it. For an executable work, complete source | ||
| 157 | code means all the source code for all modules it contains, plus any | ||
| 158 | associated interface definition files, plus the scripts used to | ||
| 159 | control compilation and installation of the executable. However, as a | ||
| 160 | special exception, the source code distributed need not include | ||
| 161 | anything that is normally distributed (in either source or binary | ||
| 162 | form) with the major components (compiler, kernel, and so on) of the | ||
| 163 | operating system on which the executable runs, unless that component | ||
| 164 | itself accompanies the executable. | ||
| 165 | |||
| 166 | If distribution of executable or object code is made by offering | ||
| 167 | access to copy from a designated place, then offering equivalent | ||
| 168 | access to copy the source code from the same place counts as | ||
| 169 | distribution of the source code, even though third parties are not | ||
| 170 | compelled to copy the source along with the object code. | ||
| 171 | |||
| 172 | 4. You may not copy, modify, sublicense, or distribute the Program | ||
| 173 | except as expressly provided under this License. Any attempt | ||
| 174 | otherwise to copy, modify, sublicense or distribute the Program is | ||
| 175 | void, and will automatically terminate your rights under this License. | ||
| 176 | However, parties who have received copies, or rights, from you under | ||
| 177 | this License will not have their licenses terminated so long as such | ||
| 178 | parties remain in full compliance. | ||
| 179 | |||
| 180 | 5. You are not required to accept this License, since you have not | ||
| 181 | signed it. However, nothing else grants you permission to modify or | ||
| 182 | distribute the Program or its derivative works. These actions are | ||
| 183 | prohibited by law if you do not accept this License. Therefore, by | ||
| 184 | modifying or distributing the Program (or any work based on the | ||
| 185 | Program), you indicate your acceptance of this License to do so, and | ||
| 186 | all its terms and conditions for copying, distributing or modifying | ||
| 187 | the Program or works based on it. | ||
| 188 | |||
| 189 | 6. Each time you redistribute the Program (or any work based on the | ||
| 190 | Program), the recipient automatically receives a license from the | ||
| 191 | original licensor to copy, distribute or modify the Program subject to | ||
| 192 | these terms and conditions. You may not impose any further | ||
| 193 | restrictions on the recipients' exercise of the rights granted herein. | ||
| 194 | You are not responsible for enforcing compliance by third parties to | ||
| 195 | this License. | ||
| 196 | |||
| 197 | 7. If, as a consequence of a court judgment or allegation of patent | ||
| 198 | infringement or for any other reason (not limited to patent issues), | ||
| 199 | conditions are imposed on you (whether by court order, agreement or | ||
| 200 | otherwise) that contradict the conditions of this License, they do not | ||
| 201 | excuse you from the conditions of this License. If you cannot | ||
| 202 | distribute so as to satisfy simultaneously your obligations under this | ||
| 203 | License and any other pertinent obligations, then as a consequence you | ||
| 204 | may not distribute the Program at all. For example, if a patent | ||
| 205 | license would not permit royalty-free redistribution of the Program by | ||
| 206 | all those who receive copies directly or indirectly through you, then | ||
| 207 | the only way you could satisfy both it and this License would be to | ||
| 208 | refrain entirely from distribution of the Program. | ||
| 209 | |||
| 210 | If any portion of this section is held invalid or unenforceable under | ||
| 211 | any particular circumstance, the balance of the section is intended to | ||
| 212 | apply and the section as a whole is intended to apply in other | ||
| 213 | circumstances. | ||
| 214 | |||
| 215 | It is not the purpose of this section to induce you to infringe any | ||
| 216 | patents or other property right claims or to contest validity of any | ||
| 217 | such claims; this section has the sole purpose of protecting the | ||
| 218 | integrity of the free software distribution system, which is | ||
| 219 | implemented by public license practices. Many people have made | ||
| 220 | generous contributions to the wide range of software distributed | ||
| 221 | through that system in reliance on consistent application of that | ||
| 222 | system; it is up to the author/donor to decide if he or she is willing | ||
| 223 | to distribute software through any other system and a licensee cannot | ||
| 224 | impose that choice. | ||
| 225 | |||
| 226 | This section is intended to make thoroughly clear what is believed to | ||
| 227 | be a consequence of the rest of this License. | ||
| 228 | |||
| 229 | 8. If the distribution and/or use of the Program is restricted in | ||
| 230 | certain countries either by patents or by copyrighted interfaces, the | ||
| 231 | original copyright holder who places the Program under this License | ||
| 232 | may add an explicit geographical distribution limitation excluding | ||
| 233 | those countries, so that distribution is permitted only in or among | ||
| 234 | countries not thus excluded. In such case, this License incorporates | ||
| 235 | the limitation as if written in the body of this License. | ||
| 236 | |||
| 237 | 9. The Free Software Foundation may publish revised and/or new versions | ||
| 238 | of the General Public License from time to time. Such new versions will | ||
| 239 | be similar in spirit to the present version, but may differ in detail to | ||
| 240 | address new problems or concerns. | ||
| 241 | |||
| 242 | Each version is given a distinguishing version number. If the Program | ||
| 243 | specifies a version number of this License which applies to it and "any | ||
| 244 | later version", you have the option of following the terms and conditions | ||
| 245 | either of that version or of any later version published by the Free | ||
| 246 | Software Foundation. If the Program does not specify a version number of | ||
| 247 | this License, you may choose any version ever published by the Free Software | ||
| 248 | Foundation. | ||
| 249 | |||
| 250 | 10. If you wish to incorporate parts of the Program into other free | ||
| 251 | programs whose distribution conditions are different, write to the author | ||
| 252 | to ask for permission. For software which is copyrighted by the Free | ||
| 253 | Software Foundation, write to the Free Software Foundation; we sometimes | ||
| 254 | make exceptions for this. Our decision will be guided by the two goals | ||
| 255 | of preserving the free status of all derivatives of our free software and | ||
| 256 | of promoting the sharing and reuse of software generally. | ||
| 257 | |||
| 258 | NO WARRANTY | ||
| 259 | |||
| 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | ||
| 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | ||
| 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | ||
| 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | ||
| 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS | ||
| 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE | ||
| 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | ||
| 268 | REPAIR OR CORRECTION. | ||
| 269 | |||
| 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||
| 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | ||
| 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | ||
| 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | ||
| 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | ||
| 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | ||
| 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | ||
| 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | ||
| 278 | POSSIBILITY OF SUCH DAMAGES. | ||
| 279 | |||
| 280 | END OF TERMS AND CONDITIONS | ||
| 281 | |||
| 282 | Appendix: How to Apply These Terms to Your New Programs | ||
| 283 | |||
| 284 | If you develop a new program, and you want it to be of the greatest | ||
| 285 | possible use to the public, the best way to achieve this is to make it | ||
| 286 | free software which everyone can redistribute and change under these terms. | ||
| 287 | |||
| 288 | To do so, attach the following notices to the program. It is safest | ||
| 289 | to attach them to the start of each source file to most effectively | ||
| 290 | convey the exclusion of warranty; and each file should have at least | ||
| 291 | the "copyright" line and a pointer to where the full notice is found. | ||
| 292 | |||
| 293 | <one line to give the program's name and a brief idea of what it does.> | ||
| 294 | Copyright (C) 19yy <name of author> | ||
| 295 | |||
| 296 | This program is free software; you can redistribute it and/or modify | ||
| 297 | it under the terms of the GNU General Public License as published by | ||
| 298 | the Free Software Foundation; either version 2 of the License, or | ||
| 299 | (at your option) any later version. | ||
| 300 | |||
| 301 | This program is distributed in the hope that it will be useful, | ||
| 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 304 | GNU General Public License for more details. | ||
| 305 | |||
| 306 | You should have received a copy of the GNU General Public License | ||
| 307 | along with this program; if not, write to the Free Software | ||
| 308 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 309 | |||
| 310 | Also add information on how to contact you by electronic and paper mail. | ||
| 311 | |||
| 312 | If the program is interactive, make it output a short notice like this | ||
| 313 | when it starts in an interactive mode: | ||
| 314 | |||
| 315 | Gnomovision version 69, Copyright (C) 19yy name of author | ||
| 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | ||
| 317 | This is free software, and you are welcome to redistribute it | ||
| 318 | under certain conditions; type `show c' for details. | ||
| 319 | |||
| 320 | The hypothetical commands `show w' and `show c' should show the appropriate | ||
| 321 | parts of the General Public License. Of course, the commands you use may | ||
| 322 | be called something other than `show w' and `show c'; they could even be | ||
| 323 | mouse-clicks or menu items--whatever suits your program. | ||
| 324 | |||
| 325 | You should also get your employer (if you work as a programmer) or your | ||
| 326 | school, if any, to sign a "copyright disclaimer" for the program, if | ||
| 327 | necessary. Here is a sample; alter the names: | ||
| 328 | |||
| 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program | ||
| 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. | ||
| 331 | |||
| 332 | <signature of Ty Coon>, 1 April 1989 | ||
| 333 | Ty Coon, President of Vice | ||
| 334 | |||
| 335 | This General Public License does not permit incorporating your program into | ||
| 336 | proprietary programs. If your program is a subroutine library, you may | ||
| 337 | consider it more useful to permit linking proprietary applications with the | ||
| 338 | library. If this is what you want to do, use the GNU Library General | ||
| 339 | Public License instead of this License. | ||
diff --git a/networking/udhcp/ChangeLog b/networking/udhcp/ChangeLog new file mode 100644 index 000000000..9c269d85d --- /dev/null +++ b/networking/udhcp/ChangeLog | |||
| @@ -0,0 +1,246 @@ | |||
| 1 | 0.9.8 (pending) | ||
| 2 | + udhcp now fits nicely into busybox | ||
| 3 | (Glenn McGrath <bug1@optushome.com.au> as well as myself) | ||
| 4 | + updated client manpage (me) | ||
| 5 | + both client and server now use sockets for signal handling, | ||
| 6 | hopefully, this will be the last needed change in signal | ||
| 7 | handling, I'm fairly certain all the possible races are now | ||
| 8 | closed. (me) | ||
| 9 | + The server now restarts the auto_time timer when it receives | ||
| 10 | a SIGUSR1 (write out config file). (me) | ||
| 11 | + Improve signal handling (David Poole) | ||
| 12 | + Fix to config file parsing (Matt Kraai) | ||
| 13 | + Fix load lease logic (me) | ||
| 14 | + Fix clear_lease logic (me) | ||
| 15 | + -h is now an alias for -H (udhcp bug #1253) | ||
| 16 | + Shorter timeout on not receiving offers (me) | ||
| 17 | + Improved signal behavior by client (me) | ||
| 18 | + Would never assign end address (Keith Smith <keith@ksmith.com>) | ||
| 19 | + Was improperly reporting yiaddr as siaddr (ben-udhcp@bdlow.net) | ||
| 20 | udhcp bug#1256 | ||
| 21 | + Fixed reading of client id (David Poole <davep@portsmith.com>) | ||
| 22 | + change sys_errlist[] to strerror() as it aparently doesn't exist | ||
| 23 | (andersee <andersee@codepoet.org>) | ||
| 24 | + fixed get_raw_packet so it returns -2 on non fatal errors | ||
| 25 | (Ted Lemon <Ted.Lemon@nominum.com>) | ||
| 26 | + Improved (hopefully) NAKing behavior (me) | ||
| 27 | + Added -b option (Jouni Malinen) | ||
| 28 | + Compute checksums correctly on big endian hosts | ||
| 29 | (Jouni Malinen <jkmaline@cc.hut.fi>) | ||
| 30 | |||
| 31 | 0.9.7 (020526) | ||
| 32 | + Use add_lease in read_leases, sanitizes leases more, and clears out exprired | ||
| 33 | ones if there is no more room (me) | ||
| 34 | + Moved udhcpd.leases to /var/lib/misc/udhcpd.leases (Debian bug #147747) | ||
| 35 | + Change (obsolete) AF_INET in arping.c to PF_PACKET (Debian bug #127049) | ||
| 36 | + Added script hook for DHCPNAK (nak), as well as providing the message option | ||
| 37 | (me) | ||
| 38 | + Generate the paramaters request list by seeing what options in options.c are | ||
| 39 | ored with OPTION_REQ in options.c | ||
| 40 | + Fix dhcp renew forgetfullness on client (bug #1230) | ||
| 41 | + Fix dhcp release bug on client (bug #1231) | ||
| 42 | + Set option request list for DHCP renew (bug #1233) | ||
| 43 | + Set BOOTREQUEST/REPLY properly | ||
| 44 | + Change client-identifier field to popularly expected behavior (me) | ||
| 45 | + Only reopen port on errors (me) | ||
| 46 | + Change fork/close/setsid structures to daemon() (me) | ||
| 47 | + Allow user to specify udhcpd config file at run time (Steven, me) | ||
| 48 | + Write pidfile after changing it (Steven CTR Carr <Steven.CTR.Carr@tc.faa.gov>) | ||
| 49 | + Added env var docs to udhcpc man page (Matt) | ||
| 50 | + Standardized lowercase udhcp in documentation (me) | ||
| 51 | + Accept packets without a UDP checksum (me) | ||
| 52 | + Accept packets with extra garbage (me) | ||
| 53 | + Better error handling in files.c (me) | ||
| 54 | + Combined read_interface function to reduce COMBINED_BINARY size (me) | ||
| 55 | + Drop calc_length(), some servers choke on smaller packets (me) | ||
| 56 | + Try to clean some fat out (me) | ||
| 57 | |||
| 58 | 0.9.6 (011001) | ||
| 59 | + Added bootp paramaters to server (me) | ||
| 60 | + Added bootp paramaters to client (me) | ||
| 61 | + Added vendor id to client (me) | ||
| 62 | + Better pidfile handling in client and server (me) | ||
| 63 | + Added man pages (Matt Kraai <kraai@alumni.carnegiemellon.edu>) | ||
| 64 | |||
| 65 | 0.9.5 (010914) | ||
| 66 | + Fixed $HOME and $PATH env passing (me) | ||
| 67 | + Fixed client to only listen for raw packets on correct interface (me) | ||
| 68 | + added --quit,-q option to quit after a lease is obtained (me) | ||
| 69 | + Fixed 100% CPU utilization by client when interface is down (me) | ||
| 70 | |||
| 71 | 0.9.4 (010827) | ||
| 72 | + Force broadcast to broken clients that request unicast (ie, MSFT 98) | ||
| 73 | + Make install rules (Adam J. Richter <adam@yggdrasil.com>) | ||
| 74 | + One scripts, instead of many (Adam) | ||
| 75 | + Removed script paramater info files (env vars only) (Adam) | ||
| 76 | + Controlling of forking behavior in client (Adam) | ||
| 77 | + General script.c/dhcpc.c cleanups (Adam) | ||
| 78 | |||
| 79 | 0.9.3 (010820) | ||
| 80 | + Increased debugging verbosity (me) | ||
| 81 | + Cut trailing whitespace when reading config file (me) | ||
| 82 | + added hostname option to client (me) | ||
| 83 | + fixed a strncpy bug in script.c (me) | ||
| 84 | + fixed a leaky socket in dhcpc.c (me) | ||
| 85 | + fixed a leaky socket in dhcpd.c (me) | ||
| 86 | |||
| 87 | 0.9.2 (010810) | ||
| 88 | + Added raw sockets to client (me) | ||
| 89 | + alignment fixes (Mark Huang) | ||
| 90 | + compiler warning fixes (Mark Huang) | ||
| 91 | + client now sends parameter list (Mark Huang/me) | ||
| 92 | + added ipttl option | ||
| 93 | + Does now not request broadcast packets | ||
| 94 | |||
| 95 | 0.9.1 (010806) | ||
| 96 | + Added udhcpc client | ||
| 97 | + reorganized functions/files | ||
| 98 | + listening socket now only binds to one interface | ||
| 99 | |||
| 100 | 0.9.0 (010720) Major rewrite, current changes, goals: | ||
| 101 | + should not segfault on bogus packets. | ||
| 102 | + Options can be read from sname and file fields. | ||
| 103 | + supports all DHCP messages (release, decline, inform). | ||
| 104 | + IP block is now specified by a range of IP's. | ||
| 105 | + Leases file now contains lease time (relative, or absolute). | ||
| 106 | + Just about any DHCP option is now supported. | ||
| 107 | + DNS entries are no longer read from resolv.conf | ||
| 108 | + Lease file can be written periodically when the process receives a SIGUSR1 | ||
| 109 | + arpping should be supported on all arches. | ||
| 110 | + support for DHCP relays. | ||
| 111 | + DHCP messages can be unicast if the client requests it. | ||
| 112 | + many, many, many other things. | ||
| 113 | |||
| 114 | 0.8.29 (000323) | ||
| 115 | + stable(?) release | ||
| 116 | |||
| 117 | |||
| 118 | 0.8.28 (000323) | ||
| 119 | + removed alarm as it was causing server to go down | ||
| 120 | + removed debugging | ||
| 121 | + break down dhcpd.c into manageable files | ||
| 122 | |||
| 123 | |||
| 124 | 0.8.27 (000221) | ||
| 125 | + OFFER also sends gateway/subnet (for picky dhcp clients) | ||
| 126 | + multiple DNS now handled from resolv.conf if available | ||
| 127 | + multiple WINS (from dhcpd.conf) | ||
| 128 | |||
| 129 | 0.8.25 (000120) | ||
| 130 | + now compiles *and* runs on a generic linux system | ||
| 131 | tested with a windows 98 client and the sample config | ||
| 132 | files in the samples directory. | ||
| 133 | |||
| 134 | 0.8.24 (000117) | ||
| 135 | + makeiplist tool has basic functionality in place | ||
| 136 | + new sample config files | ||
| 137 | + route add -host 255.255.255.255 dev eth0 added for generic linux | ||
| 138 | |||
| 139 | 0.8.23 (000117) | ||
| 140 | + NETtel specific fix for ignoring dhcp requests on 2nd interface | ||
| 141 | |||
| 142 | 0.8.22 (000113) | ||
| 143 | + minor changes to compile under a generic linux system | ||
| 144 | + minor config file location changes for a generic linux system | ||
| 145 | + makeiplist fixes.. still incomplete.. but etting closer | ||
| 146 | |||
| 147 | 0.8.21 (000113) | ||
| 148 | + now sends the correct server ip instead of hardcoded value | ||
| 149 | + minor debugging fixes for critical messages | ||
| 150 | |||
| 151 | 0.8.20 (000106) | ||
| 152 | + cut out dhcp server checking.. this was causing dialout ppp | ||
| 153 | sessions with idle time set to never time out. | ||
| 154 | + also removed the 10 second pause before launching.. as this | ||
| 155 | was originally to stop it replying to a dhcp client | ||
| 156 | on a NETtel which was really a bad way to do it in the | ||
| 157 | first place :-) | ||
| 158 | |||
| 159 | 0.8.19 (000104) | ||
| 160 | + fixes for route add -host on a machine that needs to run both | ||
| 161 | a DHCP client and server (dual eth box) | ||
| 162 | |||
| 163 | 0.8.18 (991220) | ||
| 164 | |||
| 165 | + Race conditions fixed by disabling alarm whilst the server is busy | ||
| 166 | + Fixed continous clearing of the offered array so that it is only cleared | ||
| 167 | when it is dirty - (could change the position of when dirty is set) | ||
| 168 | |||
| 169 | 0.8.17 (991212) | ||
| 170 | |||
| 171 | - has problems clearing out the offered array | ||
| 172 | |||
| 173 | 0.8.16 (991203) | ||
| 174 | + Non blocking error is changes to informational as it is not really | ||
| 175 | an error | ||
| 176 | |||
| 177 | 0.8.15 (991129) | ||
| 178 | + Servs the dns field 3 times (Nettel only) so that windows servers | ||
| 179 | dont time out whilst nettel is booting | ||
| 180 | |||
| 181 | 0.8.14 (991126) | ||
| 182 | + added owner check for the offered array so clean out time may be | ||
| 183 | increased | ||
| 184 | + added new func to print out chadder/MAC | ||
| 185 | |||
| 186 | 0.8.13 (991125) | ||
| 187 | + added win95 support (w95 changed xid halfway through conversation) | ||
| 188 | + had to change the offered array to use hardware addresses instead of xid | ||
| 189 | + fixed re offered bug | ||
| 190 | + added more debugging | ||
| 191 | |||
| 192 | 0.8.12 (991111) | ||
| 193 | + debugging was real bad.. cleaned up a bit.. needs overhaul | ||
| 194 | |||
| 195 | |||
| 196 | 0.8.11 (991110) | ||
| 197 | + fixed up offeredAddr array to actually be used now!! offeredAddr is | ||
| 198 | used to see if another simultaneous connecting client was offered | ||
| 199 | an address that we are about to offer another client (multiple | ||
| 200 | client bug) | ||
| 201 | + removed re_offered variable as it breaks multiple client support | ||
| 202 | + added lease time to ACK -- doesn't work if in OFFER | ||
| 203 | + decreased internal array clear delay to 60 seconds | ||
| 204 | + minor findAddr bug (returning -1 instead of 0) | ||
| 205 | + if clients xid already in offeredAddr offer the same addr and don't add a | ||
| 206 | new addr to offered (caused by a client issuing multiple DISCOVERs) | ||
| 207 | |||
| 208 | 0.8.10 (991105) | ||
| 209 | + \n bug in arpping | ||
| 210 | + minor debugging changes (removed printfs etc) | ||
| 211 | + started browseiplist (not finished) | ||
| 212 | |||
| 213 | 0.8.9 (19991105) | ||
| 214 | + fixed options array size bug (options were cut off) | ||
| 215 | |||
| 216 | 0.8.8 (19991105) | ||
| 217 | + ignores requests from dhcpcd on the same machine | ||
| 218 | |||
| 219 | 0.8.7 (19991104) | ||
| 220 | + don't die if we can't bind to search for existing DHCP server | ||
| 221 | + slightly more verbose syslogging | ||
| 222 | |||
| 223 | 0.8.6 (19991103) | ||
| 224 | + added makeiplist (not finished -- core dumps) | ||
| 225 | + minor debug changes | ||
| 226 | |||
| 227 | 0.8.5 (19991029) | ||
| 228 | + exits if another DHCP server is already on the network | ||
| 229 | + added Linux Makefile | ||
| 230 | |||
| 231 | 0.8.4 (19991026) | ||
| 232 | + minor bug fix in findaddr preventing an addr being found | ||
| 233 | |||
| 234 | 0.8.3 (19991025) | ||
| 235 | + fixed up debugging | ||
| 236 | + minor hwaddr issues | ||
| 237 | |||
| 238 | 0.8.2 (19991022) | ||
| 239 | + free leases (new arpping code from dhcpcd) | ||
| 240 | + fixed bug where crashes if no leases/iplist file | ||
| 241 | + syslogging and debugging switch | ||
| 242 | + serve DNS from resolv.conf | ||
| 243 | + fixed bug where new lease added if same mac offered | ||
| 244 | + now checks the ip is free b4 offering | ||
| 245 | + now supports wins server | ||
| 246 | |||
diff --git a/networking/udhcp/Makefile.in b/networking/udhcp/Makefile.in new file mode 100644 index 000000000..d76715baa --- /dev/null +++ b/networking/udhcp/Makefile.in | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | # Makefile for busybox | ||
| 2 | # | ||
| 3 | # Copyright (C) 1999-2002 Erik Andersen <andersee@debian.org> | ||
| 4 | # | ||
| 5 | # This program is free software; you can redistribute it and/or modify | ||
| 6 | # it under the terms of the GNU General Public License as published by | ||
| 7 | # the Free Software Foundation; either version 2 of the License, or | ||
| 8 | # (at your option) any later version. | ||
| 9 | # | ||
| 10 | # This program is distributed in the hope that it will be useful, | ||
| 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | # General Public License for more details. | ||
| 14 | # | ||
| 15 | # You should have received a copy of the GNU General Public License | ||
| 16 | # along with this program; if not, write to the Free Software | ||
| 17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 18 | # | ||
| 19 | |||
| 20 | UDHCP_AR:=udhcp.a | ||
| 21 | ifndef $(UDHCP_DIR) | ||
| 22 | UDHCP_DIR:=$(TOPDIR)networking/udhcp/ | ||
| 23 | endif | ||
| 24 | |||
| 25 | #ok, so I forgot how to do an or, but this is a quick and dirty hack | ||
| 26 | ifeq ($(CONFIG_UDHCPC), y) | ||
| 27 | CONFIG_UDHCP_SHARED=y | ||
| 28 | else | ||
| 29 | ifeq ($(CONFIG_UDHCPD), y) | ||
| 30 | CONFIG_UDHCP_SHARED=y | ||
| 31 | else | ||
| 32 | CONFIG_UDHCP_SHARED=n | ||
| 33 | endif | ||
| 34 | endif | ||
| 35 | |||
| 36 | UDHCP-y:= | ||
| 37 | UDHCP-$(CONFIG_UDHCP_SHARED) += options.o socket.o packet.o pidfile.o | ||
| 38 | UDHCP-$(CONFIG_UDHCPC) += dhcpc.o clientpacket.o script.o | ||
| 39 | UDHCP-$(CONFIG_UDHCPD) += dhcpd.o arpping.o files.o leases.o serverpacket.o | ||
| 40 | UDHCP-$(CONFIG_DUMPLEASES) += dumpleases.o | ||
| 41 | |||
| 42 | libraries-y+=$(UDHCP_DIR)$(UDHCP_AR) | ||
| 43 | |||
| 44 | $(UDHCP_DIR)$(UDHCP_AR): $(patsubst %,$(UDHCP_DIR)%, $(UDHCP-y)) | ||
| 45 | $(AR) -ro $@ $(patsubst %,$(UDHCP_DIR)%, $(UDHCP-y)) | ||
| 46 | |||
diff --git a/networking/udhcp/README b/networking/udhcp/README new file mode 100644 index 000000000..d3e5e3fbc --- /dev/null +++ b/networking/udhcp/README | |||
| @@ -0,0 +1,197 @@ | |||
| 1 | udhcp server/client package readme | ||
| 2 | ------------------------- | ||
| 3 | |||
| 4 | The udhcp server/client package is primarily geared towards embedded | ||
| 5 | systems. It does however, strive to be fully functional, and RFC | ||
| 6 | compliant. | ||
| 7 | |||
| 8 | udhcp server (udhcpd) | ||
| 9 | -------------------- | ||
| 10 | |||
| 11 | The only command line argument to udhcpd is an optional specifed | ||
| 12 | config file. If no config file is specified, udhcpd uses the default | ||
| 13 | config file, /etc/udhcpd.conf. Ex: | ||
| 14 | |||
| 15 | udhcpd /etc/udhcpd.eth1.conf | ||
| 16 | |||
| 17 | The udhcp server employs a number of simple config files: | ||
| 18 | |||
| 19 | udhcpd.leases | ||
| 20 | ------------ | ||
| 21 | |||
| 22 | The udhcpd.leases behavior is designed for an embedded system. The | ||
| 23 | file is written either every auto_time seconds, or when a SIGUSR1 | ||
| 24 | is received (the auto_time timer restarts if a SIGUSR1 is received). | ||
| 25 | If you send a SIGTERM to udhcpd directly after a SIGUSR1, udhcpd will | ||
| 26 | finish writing the leases file and wait for the aftermentioned script | ||
| 27 | to be executed and finish before quiting, so you do not need to sleep | ||
| 28 | between sending signals. When the file is written, a script can be | ||
| 29 | optionally called to commit the file to flash. Lease times are stored | ||
| 30 | in the file by time remaining in lease (for systems without clock | ||
| 31 | that works when there is no power), or by the absolute time that it | ||
| 32 | expires in seconds from epoch. In the remainig format, expired leases | ||
| 33 | are stored as zero. The file is of the format: | ||
| 34 | |||
| 35 | 16 byte MAC | ||
| 36 | 4 byte ip address | ||
| 37 | u32 expire time | ||
| 38 | 16 byte MAC | ||
| 39 | 4 byte ip address | ||
| 40 | u32 expire time | ||
| 41 | . | ||
| 42 | etc. | ||
| 43 | |||
| 44 | example: hexdump udhcpd.leases | ||
| 45 | |||
| 46 | 0000000 1000 c95a 27d9 0000 0000 0000 0000 0000 | ||
| 47 | 0000010 a8c0 150a 0d00 2d29 5000 23fc 8566 0000 | ||
| 48 | 0000020 0000 0000 0000 0000 a8c0 140a 0d00 4e29 | ||
| 49 | 0000030 | ||
| 50 | |||
| 51 | |||
| 52 | udhcpd.conf | ||
| 53 | ---------- | ||
| 54 | |||
| 55 | The format is fairly simple, there is a sample file with all the | ||
| 56 | available options and comments describing them in samples/udhcpd.conf | ||
| 57 | |||
| 58 | |||
| 59 | udhcp client (udhcpc) | ||
| 60 | -------------------- | ||
| 61 | |||
| 62 | The udhcp client negotiates a lease with the DHCP server and notifies | ||
| 63 | a set of scripts when a leases is obtained or lost. The command line | ||
| 64 | options for the udhcp client are: | ||
| 65 | |||
| 66 | -c, --clientid=CLIENTID Client identifier | ||
| 67 | -H, --hostname=HOSTNAME Client hostname | ||
| 68 | -h, Alias for -H | ||
| 69 | -f, --foreground Do not fork after getting lease | ||
| 70 | -b, --background Fork to background if lease cannot be | ||
| 71 | immediately negotiated. | ||
| 72 | -i, --interface=INTERFACE Interface to use (default: eth0) | ||
| 73 | -n, --now Exit with failure if lease cannot be | ||
| 74 | immediately negotiated. | ||
| 75 | -p, --pidfile=file Store process ID of daemon in file | ||
| 76 | -q, --quit Quit after obtaining lease | ||
| 77 | -r, --request=IP IP address to request (default: none) | ||
| 78 | -s, --script=file Run file at dhcp events (default: | ||
| 79 | /usr/share/udhcpc/default.script) | ||
| 80 | -v, --version Display version | ||
| 81 | |||
| 82 | If the requested IP address cannot be obtained, the client accepts the | ||
| 83 | address that the server offers. | ||
| 84 | |||
| 85 | When an event occurs, udhcpc calls the action script. The script by | ||
| 86 | default is /usr/share/udhcpc/default.script but this can be changed via | ||
| 87 | the command line arguments. The three possible arguments to the script | ||
| 88 | are: | ||
| 89 | |||
| 90 | deconfig: This argument is used when udhcpc starts, and | ||
| 91 | when a leases is lost. The script should put the interface in an | ||
| 92 | up, but deconfigured state, ie: ifconfig $interface 0.0.0.0. | ||
| 93 | |||
| 94 | bound: This argument is used when udhcpc moves from an | ||
| 95 | unbound, to a bound state. All of the paramaters are set in | ||
| 96 | enviromental variables, The script should configure the interface, | ||
| 97 | and set any other relavent parameters (default gateway, dns server, | ||
| 98 | etc). | ||
| 99 | |||
| 100 | renew: This argument is used when a DHCP lease is renewed. All of | ||
| 101 | the paramaters are set in enviromental variables. This argument is | ||
| 102 | used when the interface is already configured, so the IP address, | ||
| 103 | will not change, however, the other DHCP paramaters, such as the | ||
| 104 | default gateway, subnet mask, and dns server may change. | ||
| 105 | |||
| 106 | nak: This argument is used with udhcpc receives a NAK message. | ||
| 107 | The script with the deconfig argument will be called directly | ||
| 108 | afterwards, so no changes to the network interface are neccessary. | ||
| 109 | This hook is provided for purely informational purposes (the | ||
| 110 | message option may contain a reason for the NAK). | ||
| 111 | |||
| 112 | The paramaters for enviromental variables are as follows: | ||
| 113 | |||
| 114 | $HOME - The set $HOME env or "/" | ||
| 115 | $PATH - the set $PATH env or "/bin:/usr/bin:/sbin:/usr/sbin" | ||
| 116 | $1 - What action the script should perform | ||
| 117 | interface - The interface this was obtained on | ||
| 118 | ip - The obtained IP | ||
| 119 | siaddr - The bootp next server option | ||
| 120 | sname - The bootp server name option | ||
| 121 | boot_file - The bootp boot file option | ||
| 122 | subnet - The assigend subnet mask | ||
| 123 | timezone - Offset in seconds from UTC | ||
| 124 | router - A list of routers | ||
| 125 | timesvr - A list of time servers | ||
| 126 | namesvr - A list of IEN 116 name servers | ||
| 127 | dns - A list of DNS server | ||
| 128 | logsvr - A list of MIT-LCS UDP log servers | ||
| 129 | cookiesvr - A list of RFC 865 cookie servers | ||
| 130 | lprsvr - A list of LPR servers | ||
| 131 | hostname - The assigned hostname | ||
| 132 | bootsize - The length in 512 octect blocks of the bootfile | ||
| 133 | domain - The domain name of the network | ||
| 134 | swapsvr - The IP address of the client's swap server | ||
| 135 | rootpath - The path name of the client's root disk | ||
| 136 | ipttl - The TTL to use for this network | ||
| 137 | mtu - The MTU to use for this network | ||
| 138 | broadcast - The broadcast address for this network | ||
| 139 | ntpsrv - A list of NTP servers | ||
| 140 | wins - A list of WINS servers | ||
| 141 | lease - The lease time, in seconds | ||
| 142 | dhcptype - DHCP message type (safely ignored) | ||
| 143 | serverid - The IP of the server | ||
| 144 | message - Reason for a DHCPNAK | ||
| 145 | tftp - The TFTP server name | ||
| 146 | bootfile - The bootfile name | ||
| 147 | |||
| 148 | additional options are easily added in options.c. | ||
| 149 | |||
| 150 | udhcpc also responds to SIGUSR1 and SIGUSR2. SIGUSR1 will force a renew state, | ||
| 151 | and SIGUSR2 will force a release of the current lease, and cause udhcpc to | ||
| 152 | go into an inactive state (until it is killed, or receives a SIGUSR1). You do | ||
| 153 | not need to sleep between sending signals, as signals received are processed | ||
| 154 | sequencially in the order they are received. | ||
| 155 | |||
| 156 | |||
| 157 | |||
| 158 | compile time options | ||
| 159 | ------------------- | ||
| 160 | |||
| 161 | The Makefile contains three of the compile time options: | ||
| 162 | |||
| 163 | DEBUG: If DEBUG is defined, udhcpd will output extra debugging | ||
| 164 | output, compile with -g, and not fork to the background when run. | ||
| 165 | SYSLOG: If SYSLOG is defined, udhcpd will log all its messages | ||
| 166 | syslog, otherwise, it will attempt to log them to stdout. | ||
| 167 | |||
| 168 | COMBINED_BINARY: If COMBINED_BINARY is define, one binary, udhcpd, | ||
| 169 | is created. If called as udhcpd, the dhcp server will be started. | ||
| 170 | If called as udhcpc, the dhcp client will be started. | ||
| 171 | |||
| 172 | dhcpd.h contains the other two compile time options: | ||
| 173 | |||
| 174 | LEASE_TIME: The default lease time if not specified in the config | ||
| 175 | file. | ||
| 176 | |||
| 177 | DHCPD_CONFIG_FILE: The defualt config file to use. | ||
| 178 | |||
| 179 | options.c contains a set of dhcp options for the client: | ||
| 180 | |||
| 181 | name[10]: The name of the option as it will appear in scripts | ||
| 182 | |||
| 183 | flags: The type of option, as well as if it will be requested | ||
| 184 | by the client (OPTION_REQ) | ||
| 185 | |||
| 186 | code: The DHCP code for this option | ||
| 187 | |||
| 188 | busybox drop-in | ||
| 189 | -------------- | ||
| 190 | udhcp is now a drop-in component for busybox (http://busybox.net). | ||
| 191 | To update busybox to the latest revision, simply do a: | ||
| 192 | |||
| 193 | cp *.[ch] README AUTHORS COPYING ChangeLog TODO \ | ||
| 194 | <busybox_source>/networking/udhcp | ||
| 195 | |||
| 196 | The only two files udhcp does not provide are config.in and | ||
| 197 | Makefile.in, so these may need to be updated from time to time. | ||
diff --git a/networking/udhcp/TODO b/networking/udhcp/TODO new file mode 100644 index 000000000..47e451213 --- /dev/null +++ b/networking/udhcp/TODO | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | TODO | ||
| 2 | ---- | ||
| 3 | + make failure of reading functions revert to previous value, not the default | ||
| 4 | + sanity code for option[OPT_LEN] | ||
| 5 | + fix aliasing (ie: eth0:0) | ||
| 6 | + DONE: Make sure get_raw_packet only accepts packets on the specified interface | ||
| 7 | + better standard linux distro support | ||
| 8 | + DONE: make config file a command line option for server | ||
| 9 | + IMPLEMENTED: make forking a command line option | ||
| 10 | + make sure packet generation works on a wide varitey of arches | ||
| 11 | + Interoperability testing | ||
| 12 | + Hooks within the DHCP server | ||
| 13 | + Additional bootp support in client/server | ||
| 14 | + Make serverid option in server configurable | ||
| 15 | + DONE: cause client to generate DHCP_VENDOR option | ||
| 16 | + Possibly add failure message to DHCP NAK | ||
| 17 | + Possibly log DHCP NAK failure message in client \ No newline at end of file | ||
diff --git a/networking/udhcp/arpping.c b/networking/udhcp/arpping.c new file mode 100644 index 000000000..d71cd10f4 --- /dev/null +++ b/networking/udhcp/arpping.c | |||
| @@ -0,0 +1,105 @@ | |||
| 1 | /* | ||
| 2 | * arpping.c | ||
| 3 | * | ||
| 4 | * Mostly stolen from: dhcpcd - DHCP client daemon | ||
| 5 | * by Yoichi Hariguchi <yoichi@fore.com> | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <sys/types.h> | ||
| 9 | #include <sys/time.h> | ||
| 10 | #include <time.h> | ||
| 11 | #include <sys/types.h> | ||
| 12 | #include <sys/socket.h> | ||
| 13 | #include <netinet/if_ether.h> | ||
| 14 | #include <net/if_arp.h> | ||
| 15 | #include <netinet/in.h> | ||
| 16 | #include <stdio.h> | ||
| 17 | #include <string.h> | ||
| 18 | #include <unistd.h> | ||
| 19 | #include <errno.h> | ||
| 20 | |||
| 21 | #include "dhcpd.h" | ||
| 22 | #include "debug.h" | ||
| 23 | #include "arpping.h" | ||
| 24 | |||
| 25 | /* args: yiaddr - what IP to ping | ||
| 26 | * ip - our ip | ||
| 27 | * mac - our arp address | ||
| 28 | * interface - interface to use | ||
| 29 | * retn: 1 addr free | ||
| 30 | * 0 addr used | ||
| 31 | * -1 error | ||
| 32 | */ | ||
| 33 | |||
| 34 | /* FIXME: match response against chaddr */ | ||
| 35 | int arpping(u_int32_t yiaddr, u_int32_t ip, unsigned char *mac, char *interface) | ||
| 36 | { | ||
| 37 | |||
| 38 | int timeout = 2; | ||
| 39 | int optval = 1; | ||
| 40 | int s; /* socket */ | ||
| 41 | int rv = 1; /* return value */ | ||
| 42 | struct sockaddr addr; /* for interface name */ | ||
| 43 | struct arpMsg arp; | ||
| 44 | fd_set fdset; | ||
| 45 | struct timeval tm; | ||
| 46 | time_t prevTime; | ||
| 47 | |||
| 48 | |||
| 49 | if ((s = socket (PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1) { | ||
| 50 | LOG(LOG_ERR, "Could not open raw socket"); | ||
| 51 | return -1; | ||
| 52 | } | ||
| 53 | |||
| 54 | if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) { | ||
| 55 | LOG(LOG_ERR, "Could not setsocketopt on raw socket"); | ||
| 56 | close(s); | ||
| 57 | return -1; | ||
| 58 | } | ||
| 59 | |||
| 60 | /* send arp request */ | ||
| 61 | memset(&arp, 0, sizeof(arp)); | ||
| 62 | memcpy(arp.ethhdr.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */ | ||
| 63 | memcpy(arp.ethhdr.h_source, mac, 6); /* MAC SA */ | ||
| 64 | arp.ethhdr.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */ | ||
| 65 | arp.htype = htons(ARPHRD_ETHER); /* hardware type */ | ||
| 66 | arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */ | ||
| 67 | arp.hlen = 6; /* hardware address length */ | ||
| 68 | arp.plen = 4; /* protocol address length */ | ||
| 69 | arp.operation = htons(ARPOP_REQUEST); /* ARP op code */ | ||
| 70 | *((u_int *) arp.sInaddr) = ip; /* source IP address */ | ||
| 71 | memcpy(arp.sHaddr, mac, 6); /* source hardware address */ | ||
| 72 | *((u_int *) arp.tInaddr) = yiaddr; /* target IP address */ | ||
| 73 | |||
| 74 | memset(&addr, 0, sizeof(addr)); | ||
| 75 | strcpy(addr.sa_data, interface); | ||
| 76 | if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) | ||
| 77 | rv = 0; | ||
| 78 | |||
| 79 | /* wait arp reply, and check it */ | ||
| 80 | tm.tv_usec = 0; | ||
| 81 | time(&prevTime); | ||
| 82 | while (timeout > 0) { | ||
| 83 | FD_ZERO(&fdset); | ||
| 84 | FD_SET(s, &fdset); | ||
| 85 | tm.tv_sec = timeout; | ||
| 86 | if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) { | ||
| 87 | DEBUG(LOG_ERR, "Error on ARPING request: %s", strerror(errno)); | ||
| 88 | if (errno != EINTR) rv = 0; | ||
| 89 | } else if (FD_ISSET(s, &fdset)) { | ||
| 90 | if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0; | ||
| 91 | if (arp.operation == htons(ARPOP_REPLY) && | ||
| 92 | bcmp(arp.tHaddr, mac, 6) == 0 && | ||
| 93 | *((u_int *) arp.sInaddr) == yiaddr) { | ||
| 94 | DEBUG(LOG_INFO, "Valid arp reply receved for this address"); | ||
| 95 | rv = 0; | ||
| 96 | break; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | timeout -= time(NULL) - prevTime; | ||
| 100 | time(&prevTime); | ||
| 101 | } | ||
| 102 | close(s); | ||
| 103 | DEBUG(LOG_INFO, "%salid arp replies for this address", rv ? "No v" : "V"); | ||
| 104 | return rv; | ||
| 105 | } | ||
diff --git a/networking/udhcp/arpping.h b/networking/udhcp/arpping.h new file mode 100644 index 000000000..92b828db5 --- /dev/null +++ b/networking/udhcp/arpping.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* | ||
| 2 | * arpping .h | ||
| 3 | */ | ||
| 4 | |||
| 5 | #ifndef ARPPING_H | ||
| 6 | #define ARPPING_H | ||
| 7 | |||
| 8 | #include <netinet/if_ether.h> | ||
| 9 | #include <net/if_arp.h> | ||
| 10 | #include <net/if.h> | ||
| 11 | #include <netinet/in.h> | ||
| 12 | |||
| 13 | struct arpMsg { | ||
| 14 | struct ethhdr ethhdr; /* Ethernet header */ | ||
| 15 | u_short htype; /* hardware type (must be ARPHRD_ETHER) */ | ||
| 16 | u_short ptype; /* protocol type (must be ETH_P_IP) */ | ||
| 17 | u_char hlen; /* hardware address length (must be 6) */ | ||
| 18 | u_char plen; /* protocol address length (must be 4) */ | ||
| 19 | u_short operation; /* ARP opcode */ | ||
| 20 | u_char sHaddr[6]; /* sender's hardware address */ | ||
| 21 | u_char sInaddr[4]; /* sender's IP address */ | ||
| 22 | u_char tHaddr[6]; /* target's hardware address */ | ||
| 23 | u_char tInaddr[4]; /* target's IP address */ | ||
| 24 | u_char pad[18]; /* pad for min. Ethernet payload (60 bytes) */ | ||
| 25 | }; | ||
| 26 | |||
| 27 | /* function prototypes */ | ||
| 28 | int arpping(u_int32_t yiaddr, u_int32_t ip, unsigned char *arp, char *interface); | ||
| 29 | |||
| 30 | #endif | ||
diff --git a/networking/udhcp/clientpacket.c b/networking/udhcp/clientpacket.c new file mode 100644 index 000000000..ecbd7953f --- /dev/null +++ b/networking/udhcp/clientpacket.c | |||
| @@ -0,0 +1,239 @@ | |||
| 1 | /* clientpacket.c | ||
| 2 | * | ||
| 3 | * Packet generation and dispatching functions for the DHCP client. | ||
| 4 | * | ||
| 5 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <string.h> | ||
| 23 | #include <sys/socket.h> | ||
| 24 | #include <features.h> | ||
| 25 | #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 | ||
| 26 | #include <netpacket/packet.h> | ||
| 27 | #include <net/ethernet.h> | ||
| 28 | #else | ||
| 29 | #include <asm/types.h> | ||
| 30 | #include <linux/if_packet.h> | ||
| 31 | #include <linux/if_ether.h> | ||
| 32 | #endif | ||
| 33 | #include <stdlib.h> | ||
| 34 | #include <time.h> | ||
| 35 | #include <unistd.h> | ||
| 36 | #include <netinet/in.h> | ||
| 37 | #include <arpa/inet.h> | ||
| 38 | |||
| 39 | |||
| 40 | #include "dhcpd.h" | ||
| 41 | #include "packet.h" | ||
| 42 | #include "options.h" | ||
| 43 | #include "dhcpc.h" | ||
| 44 | #include "debug.h" | ||
| 45 | |||
| 46 | |||
| 47 | /* Create a random xid */ | ||
| 48 | unsigned long random_xid(void) | ||
| 49 | { | ||
| 50 | static int initialized; | ||
| 51 | if (!initialized) { | ||
| 52 | srand(time(0)); | ||
| 53 | initialized++; | ||
| 54 | } | ||
| 55 | return rand(); | ||
| 56 | } | ||
| 57 | |||
| 58 | |||
| 59 | /* initialize a packet with the proper defaults */ | ||
| 60 | static void init_packet(struct dhcpMessage *packet, char type) | ||
| 61 | { | ||
| 62 | struct vendor { | ||
| 63 | char vendor, length; | ||
| 64 | char str[sizeof("udhcp "VERSION)]; | ||
| 65 | } vendor_id = { DHCP_VENDOR, sizeof("udhcp "VERSION) - 1, "udhcp "VERSION}; | ||
| 66 | |||
| 67 | init_header(packet, type); | ||
| 68 | memcpy(packet->chaddr, client_config.arp, 6); | ||
| 69 | add_option_string(packet->options, client_config.clientid); | ||
| 70 | if (client_config.hostname) add_option_string(packet->options, client_config.hostname); | ||
| 71 | add_option_string(packet->options, (unsigned char *) &vendor_id); | ||
| 72 | } | ||
| 73 | |||
| 74 | |||
| 75 | /* Add a paramater request list for stubborn DHCP servers. Pull the data | ||
| 76 | * from the struct in options.c. Don't do bounds checking here because it | ||
| 77 | * goes towards the head of the packet. */ | ||
| 78 | static void add_requests(struct dhcpMessage *packet) | ||
| 79 | { | ||
| 80 | int end = end_option(packet->options); | ||
| 81 | int i, len = 0; | ||
| 82 | |||
| 83 | packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; | ||
| 84 | for (i = 0; options[i].code; i++) | ||
| 85 | if (options[i].flags & OPTION_REQ) | ||
| 86 | packet->options[end + OPT_DATA + len++] = options[i].code; | ||
| 87 | packet->options[end + OPT_LEN] = len; | ||
| 88 | packet->options[end + OPT_DATA + len] = DHCP_END; | ||
| 89 | |||
| 90 | } | ||
| 91 | |||
| 92 | |||
| 93 | /* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ | ||
| 94 | int send_discover(unsigned long xid, unsigned long requested) | ||
| 95 | { | ||
| 96 | struct dhcpMessage packet; | ||
| 97 | |||
| 98 | init_packet(&packet, DHCPDISCOVER); | ||
| 99 | packet.xid = xid; | ||
| 100 | if (requested) | ||
| 101 | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); | ||
| 102 | |||
| 103 | add_requests(&packet); | ||
| 104 | LOG(LOG_DEBUG, "Sending discover..."); | ||
| 105 | return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, | ||
| 106 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); | ||
| 107 | } | ||
| 108 | |||
| 109 | |||
| 110 | /* Broadcasts a DHCP request message */ | ||
| 111 | int send_selecting(unsigned long xid, unsigned long server, unsigned long requested) | ||
| 112 | { | ||
| 113 | struct dhcpMessage packet; | ||
| 114 | struct in_addr addr; | ||
| 115 | |||
| 116 | init_packet(&packet, DHCPREQUEST); | ||
| 117 | packet.xid = xid; | ||
| 118 | |||
| 119 | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); | ||
| 120 | add_simple_option(packet.options, DHCP_SERVER_ID, server); | ||
| 121 | |||
| 122 | add_requests(&packet); | ||
| 123 | addr.s_addr = requested; | ||
| 124 | LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr)); | ||
| 125 | return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, | ||
| 126 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); | ||
| 127 | } | ||
| 128 | |||
| 129 | |||
| 130 | /* Unicasts or broadcasts a DHCP renew message */ | ||
| 131 | int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr) | ||
| 132 | { | ||
| 133 | struct dhcpMessage packet; | ||
| 134 | int ret = 0; | ||
| 135 | |||
| 136 | init_packet(&packet, DHCPREQUEST); | ||
| 137 | packet.xid = xid; | ||
| 138 | packet.ciaddr = ciaddr; | ||
| 139 | |||
| 140 | add_requests(&packet); | ||
| 141 | LOG(LOG_DEBUG, "Sending renew..."); | ||
| 142 | if (server) | ||
| 143 | ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); | ||
| 144 | else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, | ||
| 145 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); | ||
| 146 | return ret; | ||
| 147 | } | ||
| 148 | |||
| 149 | |||
| 150 | /* Unicasts a DHCP release message */ | ||
| 151 | int send_release(unsigned long server, unsigned long ciaddr) | ||
| 152 | { | ||
| 153 | struct dhcpMessage packet; | ||
| 154 | |||
| 155 | init_packet(&packet, DHCPRELEASE); | ||
| 156 | packet.xid = random_xid(); | ||
| 157 | packet.ciaddr = ciaddr; | ||
| 158 | |||
| 159 | add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr); | ||
| 160 | add_simple_option(packet.options, DHCP_SERVER_ID, server); | ||
| 161 | |||
| 162 | LOG(LOG_DEBUG, "Sending release..."); | ||
| 163 | return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); | ||
| 164 | } | ||
| 165 | |||
| 166 | |||
| 167 | /* return -1 on errors that are fatal for the socket, -2 for those that aren't */ | ||
| 168 | int get_raw_packet(struct dhcpMessage *payload, int fd) | ||
| 169 | { | ||
| 170 | int bytes; | ||
| 171 | struct udp_dhcp_packet packet; | ||
| 172 | u_int32_t source, dest; | ||
| 173 | u_int16_t check; | ||
| 174 | |||
| 175 | memset(&packet, 0, sizeof(struct udp_dhcp_packet)); | ||
| 176 | bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet)); | ||
| 177 | if (bytes < 0) { | ||
| 178 | DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring"); | ||
| 179 | usleep(500000); /* possible down interface, looping condition */ | ||
| 180 | return -1; | ||
| 181 | } | ||
| 182 | |||
| 183 | if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { | ||
| 184 | DEBUG(LOG_INFO, "message too short, ignoring"); | ||
| 185 | return -2; | ||
| 186 | } | ||
| 187 | |||
| 188 | if (bytes < ntohs(packet.ip.tot_len)) { | ||
| 189 | DEBUG(LOG_INFO, "Truncated packet"); | ||
| 190 | return -2; | ||
| 191 | } | ||
| 192 | |||
| 193 | /* ignore any extra garbage bytes */ | ||
| 194 | bytes = ntohs(packet.ip.tot_len); | ||
| 195 | |||
| 196 | /* Make sure its the right packet for us, and that it passes sanity checks */ | ||
| 197 | if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION || | ||
| 198 | packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) || | ||
| 199 | bytes > (int) sizeof(struct udp_dhcp_packet) || | ||
| 200 | ntohs(packet.udp.len) != (short) (bytes - sizeof(packet.ip))) { | ||
| 201 | DEBUG(LOG_INFO, "unrelated/bogus packet"); | ||
| 202 | return -2; | ||
| 203 | } | ||
| 204 | |||
| 205 | /* check IP checksum */ | ||
| 206 | check = packet.ip.check; | ||
| 207 | packet.ip.check = 0; | ||
| 208 | if (check != checksum(&(packet.ip), sizeof(packet.ip))) { | ||
| 209 | DEBUG(LOG_INFO, "bad IP header checksum, ignoring"); | ||
| 210 | return -1; | ||
| 211 | } | ||
| 212 | |||
| 213 | /* verify the UDP checksum by replacing the header with a psuedo header */ | ||
| 214 | source = packet.ip.saddr; | ||
| 215 | dest = packet.ip.daddr; | ||
| 216 | check = packet.udp.check; | ||
| 217 | packet.udp.check = 0; | ||
| 218 | memset(&packet.ip, 0, sizeof(packet.ip)); | ||
| 219 | |||
| 220 | packet.ip.protocol = IPPROTO_UDP; | ||
| 221 | packet.ip.saddr = source; | ||
| 222 | packet.ip.daddr = dest; | ||
| 223 | packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */ | ||
| 224 | if (check && check != checksum(&packet, bytes)) { | ||
| 225 | DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring"); | ||
| 226 | return -2; | ||
| 227 | } | ||
| 228 | |||
| 229 | memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp))); | ||
| 230 | |||
| 231 | if (ntohl(payload->cookie) != DHCP_MAGIC) { | ||
| 232 | LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring"); | ||
| 233 | return -2; | ||
| 234 | } | ||
| 235 | DEBUG(LOG_INFO, "oooooh!!! got some!"); | ||
| 236 | return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); | ||
| 237 | |||
| 238 | } | ||
| 239 | |||
diff --git a/networking/udhcp/clientpacket.h b/networking/udhcp/clientpacket.h new file mode 100644 index 000000000..2a6facbc8 --- /dev/null +++ b/networking/udhcp/clientpacket.h | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | #ifndef _CLIENTPACKET_H | ||
| 2 | #define _CLIENTPACKET_H | ||
| 3 | |||
| 4 | unsigned long random_xid(void); | ||
| 5 | int send_discover(unsigned long xid, unsigned long requested); | ||
| 6 | int send_selecting(unsigned long xid, unsigned long server, unsigned long requested); | ||
| 7 | int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); | ||
| 8 | int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); | ||
| 9 | int send_release(unsigned long server, unsigned long ciaddr); | ||
| 10 | int get_raw_packet(struct dhcpMessage *payload, int fd); | ||
| 11 | |||
| 12 | #endif | ||
diff --git a/networking/udhcp/config.in b/networking/udhcp/config.in new file mode 100644 index 000000000..ede632a8a --- /dev/null +++ b/networking/udhcp/config.in | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # | ||
| 2 | # For a description of the syntax of this configuration file, | ||
| 3 | # see scripts/kbuild/config-language.txt. | ||
| 4 | # | ||
| 5 | |||
| 6 | mainmenu_option next_comment | ||
| 7 | comment 'udhcp Server/Client' | ||
| 8 | |||
| 9 | bool 'udhcp Server (udhcpd)' CONFIG_UDHCPD | ||
| 10 | bool 'udhcp Client (udhcpc)' CONFIG_UDHCPC | ||
| 11 | bool 'Lease display utility (dumpleases)' CONFIG_DUMPLEASES | ||
| 12 | if [ "$CONFIG_UDHCPD" = "y" -o "$CONFIG_UDHCPC" = "y" ]; then | ||
| 13 | bool ' Log udhcp messages to syslog (instead of stdout)' CONFIG_FEATURE_UDHCP_SYSLOG | ||
| 14 | bool ' Compile udhcp with noisy debugging messages' CONFIG_FEATURE_UDHCP_DEBUG | ||
| 15 | fi | ||
| 16 | |||
| 17 | endmenu | ||
| 18 | |||
diff --git a/networking/udhcp/debug.h b/networking/udhcp/debug.h new file mode 100644 index 000000000..a1e197412 --- /dev/null +++ b/networking/udhcp/debug.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | #ifndef _DEBUG_H | ||
| 2 | #define _DEBUG_H | ||
| 3 | |||
| 4 | #include "libbb_udhcp.h" | ||
| 5 | |||
| 6 | #include <stdio.h> | ||
| 7 | #ifdef SYSLOG | ||
| 8 | #include <syslog.h> | ||
| 9 | #endif | ||
| 10 | |||
| 11 | |||
| 12 | #ifdef SYSLOG | ||
| 13 | # define LOG(level, str, args...) do { printf(str, ## args); \ | ||
| 14 | printf("\n"); \ | ||
| 15 | syslog(level, str, ## args); } while(0) | ||
| 16 | # define OPEN_LOG(name) openlog(name, 0, 0) | ||
| 17 | #define CLOSE_LOG() closelog() | ||
| 18 | #else | ||
| 19 | # define LOG_EMERG "EMERGENCY!" | ||
| 20 | # define LOG_ALERT "ALERT!" | ||
| 21 | # define LOG_CRIT "critical!" | ||
| 22 | # define LOG_WARNING "warning" | ||
| 23 | # define LOG_ERR "error" | ||
| 24 | # define LOG_INFO "info" | ||
| 25 | # define LOG_DEBUG "debug" | ||
| 26 | # define LOG(level, str, args...) do { printf("%s, ", level); \ | ||
| 27 | printf(str, ## args); \ | ||
| 28 | printf("\n"); } while(0) | ||
| 29 | # define OPEN_LOG(name) do {;} while(0) | ||
| 30 | #define CLOSE_LOG() do {;} while(0) | ||
| 31 | #endif | ||
| 32 | |||
| 33 | #ifdef DEBUG | ||
| 34 | # undef DEBUG | ||
| 35 | # define DEBUG(level, str, args...) LOG(level, str, ## args) | ||
| 36 | # define DEBUGGING | ||
| 37 | #else | ||
| 38 | # define DEBUG(level, str, args...) do {;} while(0) | ||
| 39 | #endif | ||
| 40 | |||
| 41 | #endif | ||
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c new file mode 100644 index 000000000..ae40ec9c2 --- /dev/null +++ b/networking/udhcp/dhcpc.c | |||
| @@ -0,0 +1,559 @@ | |||
| 1 | /* dhcpc.c | ||
| 2 | * | ||
| 3 | * udhcp DHCP client | ||
| 4 | * | ||
| 5 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <stdio.h> | ||
| 23 | #include <sys/time.h> | ||
| 24 | #include <sys/types.h> | ||
| 25 | #include <sys/file.h> | ||
| 26 | #include <unistd.h> | ||
| 27 | #include <getopt.h> | ||
| 28 | #include <stdlib.h> | ||
| 29 | #include <sys/socket.h> | ||
| 30 | #include <netinet/in.h> | ||
| 31 | #include <arpa/inet.h> | ||
| 32 | #include <signal.h> | ||
| 33 | #include <time.h> | ||
| 34 | #include <string.h> | ||
| 35 | #include <sys/ioctl.h> | ||
| 36 | #include <net/if.h> | ||
| 37 | #include <errno.h> | ||
| 38 | |||
| 39 | #include "dhcpd.h" | ||
| 40 | #include "dhcpc.h" | ||
| 41 | #include "options.h" | ||
| 42 | #include "clientpacket.h" | ||
| 43 | #include "packet.h" | ||
| 44 | #include "script.h" | ||
| 45 | #include "socket.h" | ||
| 46 | #include "debug.h" | ||
| 47 | #include "pidfile.h" | ||
| 48 | |||
| 49 | static int state; | ||
| 50 | static unsigned long requested_ip; /* = 0 */ | ||
| 51 | static unsigned long server_addr; | ||
| 52 | static unsigned long timeout; | ||
| 53 | static int packet_num; /* = 0 */ | ||
| 54 | static int fd; | ||
| 55 | static int signal_pipe[2]; | ||
| 56 | |||
| 57 | #define LISTEN_NONE 0 | ||
| 58 | #define LISTEN_KERNEL 1 | ||
| 59 | #define LISTEN_RAW 2 | ||
| 60 | static int listen_mode; | ||
| 61 | |||
| 62 | #define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" | ||
| 63 | |||
| 64 | struct client_config_t client_config = { | ||
| 65 | /* Default options. */ | ||
| 66 | abort_if_no_lease: 0, | ||
| 67 | foreground: 0, | ||
| 68 | quit_after_lease: 0, | ||
| 69 | background_if_no_lease: 0, | ||
| 70 | interface: "eth0", | ||
| 71 | pidfile: NULL, | ||
| 72 | script: DEFAULT_SCRIPT, | ||
| 73 | clientid: NULL, | ||
| 74 | hostname: NULL, | ||
| 75 | ifindex: 0, | ||
| 76 | arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */ | ||
| 77 | }; | ||
| 78 | |||
| 79 | #ifndef BB_VER | ||
| 80 | static void show_usage(void) | ||
| 81 | { | ||
| 82 | printf( | ||
| 83 | "Usage: udhcpc [OPTIONS]\n\n" | ||
| 84 | " -c, --clientid=CLIENTID Client identifier\n" | ||
| 85 | " -H, --hostname=HOSTNAME Client hostname\n" | ||
| 86 | " -h Alias for -H\n" | ||
| 87 | " -f, --foreground Do not fork after getting lease\n" | ||
| 88 | " -b, --background Fork to background if lease cannot be\n" | ||
| 89 | " immediately negotiated.\n" | ||
| 90 | " -i, --interface=INTERFACE Interface to use (default: eth0)\n" | ||
| 91 | " -n, --now Exit with failure if lease cannot be\n" | ||
| 92 | " immediately negotiated.\n" | ||
| 93 | " -p, --pidfile=file Store process ID of daemon in file\n" | ||
| 94 | " -q, --quit Quit after obtaining lease\n" | ||
| 95 | " -r, --request=IP IP address to request (default: none)\n" | ||
| 96 | " -s, --script=file Run file at dhcp events (default:\n" | ||
| 97 | " " DEFAULT_SCRIPT ")\n" | ||
| 98 | " -v, --version Display version\n" | ||
| 99 | ); | ||
| 100 | exit(0); | ||
| 101 | } | ||
| 102 | #endif | ||
| 103 | |||
| 104 | |||
| 105 | /* just a little helper */ | ||
| 106 | static void change_mode(int new_mode) | ||
| 107 | { | ||
| 108 | DEBUG(LOG_INFO, "entering %s listen mode", | ||
| 109 | new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); | ||
| 110 | close(fd); | ||
| 111 | fd = -1; | ||
| 112 | listen_mode = new_mode; | ||
| 113 | } | ||
| 114 | |||
| 115 | |||
| 116 | /* perform a renew */ | ||
| 117 | static void perform_renew(void) | ||
| 118 | { | ||
| 119 | LOG(LOG_INFO, "Performing a DHCP renew"); | ||
| 120 | switch (state) { | ||
| 121 | case RENEWING: | ||
| 122 | run_script(NULL, "deconfig"); | ||
| 123 | case BOUND: | ||
| 124 | case REBINDING: | ||
| 125 | change_mode(LISTEN_KERNEL); | ||
| 126 | state = RENEW_REQUESTED; | ||
| 127 | break; | ||
| 128 | case RENEW_REQUESTED: | ||
| 129 | case REQUESTING: | ||
| 130 | case RELEASED: | ||
| 131 | change_mode(LISTEN_RAW); | ||
| 132 | state = INIT_SELECTING; | ||
| 133 | break; | ||
| 134 | case INIT_SELECTING: | ||
| 135 | } | ||
| 136 | |||
| 137 | /* start things over */ | ||
| 138 | packet_num = 0; | ||
| 139 | |||
| 140 | /* Kill any timeouts because the user wants this to hurry along */ | ||
| 141 | timeout = 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | |||
| 145 | /* perform a release */ | ||
| 146 | static void perform_release(void) | ||
| 147 | { | ||
| 148 | char buffer[16]; | ||
| 149 | struct in_addr temp_addr; | ||
| 150 | |||
| 151 | /* send release packet */ | ||
| 152 | if (state == BOUND || state == RENEWING || state == REBINDING) { | ||
| 153 | temp_addr.s_addr = server_addr; | ||
| 154 | sprintf(buffer, "%s", inet_ntoa(temp_addr)); | ||
| 155 | temp_addr.s_addr = requested_ip; | ||
| 156 | LOG(LOG_INFO, "Unicasting a release of %s to %s", | ||
| 157 | inet_ntoa(temp_addr), buffer); | ||
| 158 | send_release(server_addr, requested_ip); /* unicast */ | ||
| 159 | run_script(NULL, "deconfig"); | ||
| 160 | } | ||
| 161 | LOG(LOG_INFO, "Entering released state"); | ||
| 162 | |||
| 163 | change_mode(LISTEN_NONE); | ||
| 164 | state = RELEASED; | ||
| 165 | timeout = 0x7fffffff; | ||
| 166 | } | ||
| 167 | |||
| 168 | |||
| 169 | /* Exit and cleanup */ | ||
| 170 | static void exit_client(int retval) | ||
| 171 | { | ||
| 172 | pidfile_delete(client_config.pidfile); | ||
| 173 | CLOSE_LOG(); | ||
| 174 | exit(retval); | ||
| 175 | } | ||
| 176 | |||
| 177 | |||
| 178 | /* Signal handler */ | ||
| 179 | static void signal_handler(int sig) | ||
| 180 | { | ||
| 181 | if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) { | ||
| 182 | LOG(LOG_ERR, "Could not send signal: %s", | ||
| 183 | strerror(errno)); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | |||
| 188 | static void background(void) | ||
| 189 | { | ||
| 190 | int pid_fd; | ||
| 191 | |||
| 192 | pid_fd = pidfile_acquire(client_config.pidfile); /* hold lock during fork. */ | ||
| 193 | while (pid_fd >= 0 && pid_fd < 3) pid_fd = dup(pid_fd); /* don't let daemon close it */ | ||
| 194 | if (daemon(0, 0) == -1) { | ||
| 195 | perror("fork"); | ||
| 196 | exit_client(1); | ||
| 197 | } | ||
| 198 | client_config.foreground = 1; /* Do not fork again. */ | ||
| 199 | pidfile_write_release(pid_fd); | ||
| 200 | } | ||
| 201 | |||
| 202 | |||
| 203 | #ifdef COMBINED_BINARY | ||
| 204 | int udhcpc_main(int argc, char *argv[]) | ||
| 205 | #else | ||
| 206 | int main(int argc, char *argv[]) | ||
| 207 | #endif | ||
| 208 | { | ||
| 209 | unsigned char *temp, *message; | ||
| 210 | unsigned long t1 = 0, t2 = 0, xid = 0; | ||
| 211 | unsigned long start = 0, lease; | ||
| 212 | fd_set rfds; | ||
| 213 | int retval; | ||
| 214 | struct timeval tv; | ||
| 215 | int c, len; | ||
| 216 | struct dhcpMessage packet; | ||
| 217 | struct in_addr temp_addr; | ||
| 218 | int pid_fd; | ||
| 219 | time_t now; | ||
| 220 | int max_fd; | ||
| 221 | int sig; | ||
| 222 | |||
| 223 | static struct option arg_options[] = { | ||
| 224 | {"clientid", required_argument, 0, 'c'}, | ||
| 225 | {"foreground", no_argument, 0, 'f'}, | ||
| 226 | {"background", no_argument, 0, 'b'}, | ||
| 227 | {"hostname", required_argument, 0, 'H'}, | ||
| 228 | {"hostname", required_argument, 0, 'h'}, | ||
| 229 | {"interface", required_argument, 0, 'i'}, | ||
| 230 | {"now", no_argument, 0, 'n'}, | ||
| 231 | {"pidfile", required_argument, 0, 'p'}, | ||
| 232 | {"quit", no_argument, 0, 'q'}, | ||
| 233 | {"request", required_argument, 0, 'r'}, | ||
| 234 | {"script", required_argument, 0, 's'}, | ||
| 235 | {"version", no_argument, 0, 'v'}, | ||
| 236 | {"help", no_argument, 0, '?'}, | ||
| 237 | {0, 0, 0, 0} | ||
| 238 | }; | ||
| 239 | |||
| 240 | /* get options */ | ||
| 241 | while (1) { | ||
| 242 | int option_index = 0; | ||
| 243 | c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index); | ||
| 244 | if (c == -1) break; | ||
| 245 | |||
| 246 | switch (c) { | ||
| 247 | case 'c': | ||
| 248 | len = strlen(optarg) > 255 ? 255 : strlen(optarg); | ||
| 249 | if (client_config.clientid) free(client_config.clientid); | ||
| 250 | client_config.clientid = xmalloc(len + 2); | ||
| 251 | client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; | ||
| 252 | client_config.clientid[OPT_LEN] = len; | ||
| 253 | client_config.clientid[OPT_DATA] = '\0'; | ||
| 254 | strncpy(client_config.clientid + OPT_DATA, optarg, len); | ||
| 255 | break; | ||
| 256 | case 'f': | ||
| 257 | client_config.foreground = 1; | ||
| 258 | break; | ||
| 259 | case 'b': | ||
| 260 | client_config.background_if_no_lease = 1; | ||
| 261 | break; | ||
| 262 | case 'h': | ||
| 263 | case 'H': | ||
| 264 | len = strlen(optarg) > 255 ? 255 : strlen(optarg); | ||
| 265 | if (client_config.hostname) free(client_config.hostname); | ||
| 266 | client_config.hostname = xmalloc(len + 2); | ||
| 267 | client_config.hostname[OPT_CODE] = DHCP_HOST_NAME; | ||
| 268 | client_config.hostname[OPT_LEN] = len; | ||
| 269 | strncpy(client_config.hostname + 2, optarg, len); | ||
| 270 | break; | ||
| 271 | case 'i': | ||
| 272 | client_config.interface = optarg; | ||
| 273 | break; | ||
| 274 | case 'n': | ||
| 275 | client_config.abort_if_no_lease = 1; | ||
| 276 | break; | ||
| 277 | case 'p': | ||
| 278 | client_config.pidfile = optarg; | ||
| 279 | break; | ||
| 280 | case 'q': | ||
| 281 | client_config.quit_after_lease = 1; | ||
| 282 | break; | ||
| 283 | case 'r': | ||
| 284 | requested_ip = inet_addr(optarg); | ||
| 285 | break; | ||
| 286 | case 's': | ||
| 287 | client_config.script = optarg; | ||
| 288 | break; | ||
| 289 | case 'v': | ||
| 290 | printf("udhcpcd, version %s\n\n", VERSION); | ||
| 291 | exit_client(0); | ||
| 292 | break; | ||
| 293 | default: | ||
| 294 | show_usage(); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | OPEN_LOG("udhcpc"); | ||
| 299 | LOG(LOG_INFO, "udhcp client (v%s) started", VERSION); | ||
| 300 | |||
| 301 | pid_fd = pidfile_acquire(client_config.pidfile); | ||
| 302 | pidfile_write_release(pid_fd); | ||
| 303 | |||
| 304 | if (read_interface(client_config.interface, &client_config.ifindex, | ||
| 305 | NULL, client_config.arp) < 0) | ||
| 306 | exit_client(1); | ||
| 307 | |||
| 308 | if (!client_config.clientid) { | ||
| 309 | client_config.clientid = xmalloc(6 + 3); | ||
| 310 | client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; | ||
| 311 | client_config.clientid[OPT_LEN] = 7; | ||
| 312 | client_config.clientid[OPT_DATA] = 1; | ||
| 313 | memcpy(client_config.clientid + 3, client_config.arp, 6); | ||
| 314 | } | ||
| 315 | |||
| 316 | /* setup signal handlers */ | ||
| 317 | socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); | ||
| 318 | signal(SIGUSR1, signal_handler); | ||
| 319 | signal(SIGUSR2, signal_handler); | ||
| 320 | signal(SIGTERM, signal_handler); | ||
| 321 | |||
| 322 | state = INIT_SELECTING; | ||
| 323 | run_script(NULL, "deconfig"); | ||
| 324 | change_mode(LISTEN_RAW); | ||
| 325 | |||
| 326 | for (;;) { | ||
| 327 | |||
| 328 | tv.tv_sec = timeout - time(0); | ||
| 329 | tv.tv_usec = 0; | ||
| 330 | FD_ZERO(&rfds); | ||
| 331 | |||
| 332 | if (listen_mode != LISTEN_NONE && fd < 0) { | ||
| 333 | if (listen_mode == LISTEN_KERNEL) | ||
| 334 | fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface); | ||
| 335 | else | ||
| 336 | fd = raw_socket(client_config.ifindex); | ||
| 337 | if (fd < 0) { | ||
| 338 | LOG(LOG_ERR, "FATAL: couldn't listen on socket, %s", strerror(errno)); | ||
| 339 | exit_client(0); | ||
| 340 | } | ||
| 341 | } | ||
| 342 | if (fd >= 0) FD_SET(fd, &rfds); | ||
| 343 | FD_SET(signal_pipe[0], &rfds); | ||
| 344 | |||
| 345 | if (tv.tv_sec > 0) { | ||
| 346 | DEBUG(LOG_INFO, "Waiting on select...\n"); | ||
| 347 | max_fd = signal_pipe[0] > fd ? signal_pipe[0] : fd; | ||
| 348 | retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); | ||
| 349 | } else retval = 0; /* If we already timed out, fall through */ | ||
| 350 | |||
| 351 | now = time(0); | ||
| 352 | if (retval == 0) { | ||
| 353 | /* timeout dropped to zero */ | ||
| 354 | switch (state) { | ||
| 355 | case INIT_SELECTING: | ||
| 356 | if (packet_num < 3) { | ||
| 357 | if (packet_num == 0) | ||
| 358 | xid = random_xid(); | ||
| 359 | |||
| 360 | /* send discover packet */ | ||
| 361 | send_discover(xid, requested_ip); /* broadcast */ | ||
| 362 | |||
| 363 | timeout = now + ((packet_num == 2) ? 4 : 2); | ||
| 364 | packet_num++; | ||
| 365 | } else { | ||
| 366 | if (client_config.background_if_no_lease) { | ||
| 367 | LOG(LOG_INFO, "No lease, forking to background."); | ||
| 368 | background(); | ||
| 369 | } else if (client_config.abort_if_no_lease) { | ||
| 370 | LOG(LOG_INFO, "No lease, failing."); | ||
| 371 | exit_client(1); | ||
| 372 | } | ||
| 373 | /* wait to try again */ | ||
| 374 | packet_num = 0; | ||
| 375 | timeout = now + 60; | ||
| 376 | } | ||
| 377 | break; | ||
| 378 | case RENEW_REQUESTED: | ||
| 379 | case REQUESTING: | ||
| 380 | if (packet_num < 3) { | ||
| 381 | /* send request packet */ | ||
| 382 | if (state == RENEW_REQUESTED) | ||
| 383 | send_renew(xid, server_addr, requested_ip); /* unicast */ | ||
| 384 | else send_selecting(xid, server_addr, requested_ip); /* broadcast */ | ||
| 385 | |||
| 386 | timeout = now + ((packet_num == 2) ? 10 : 2); | ||
| 387 | packet_num++; | ||
| 388 | } else { | ||
| 389 | /* timed out, go back to init state */ | ||
| 390 | state = INIT_SELECTING; | ||
| 391 | timeout = now; | ||
| 392 | packet_num = 0; | ||
| 393 | change_mode(LISTEN_RAW); | ||
| 394 | } | ||
| 395 | break; | ||
| 396 | case BOUND: | ||
| 397 | /* Lease is starting to run out, time to enter renewing state */ | ||
| 398 | state = RENEWING; | ||
| 399 | change_mode(LISTEN_KERNEL); | ||
| 400 | DEBUG(LOG_INFO, "Entering renew state"); | ||
| 401 | /* fall right through */ | ||
| 402 | case RENEWING: | ||
| 403 | /* Either set a new T1, or enter REBINDING state */ | ||
| 404 | if ((t2 - t1) <= (lease / 14400 + 1)) { | ||
| 405 | /* timed out, enter rebinding state */ | ||
| 406 | state = REBINDING; | ||
| 407 | timeout = now + (t2 - t1); | ||
| 408 | DEBUG(LOG_INFO, "Entering rebinding state"); | ||
| 409 | } else { | ||
| 410 | /* send a request packet */ | ||
| 411 | send_renew(xid, server_addr, requested_ip); /* unicast */ | ||
| 412 | |||
| 413 | t1 = (t2 - t1) / 2 + t1; | ||
| 414 | timeout = t1 + start; | ||
| 415 | } | ||
| 416 | break; | ||
| 417 | case REBINDING: | ||
| 418 | /* Either set a new T2, or enter INIT state */ | ||
| 419 | if ((lease - t2) <= (lease / 14400 + 1)) { | ||
| 420 | /* timed out, enter init state */ | ||
| 421 | state = INIT_SELECTING; | ||
| 422 | LOG(LOG_INFO, "Lease lost, entering init state"); | ||
| 423 | run_script(NULL, "deconfig"); | ||
| 424 | timeout = now; | ||
| 425 | packet_num = 0; | ||
| 426 | change_mode(LISTEN_RAW); | ||
| 427 | } else { | ||
| 428 | /* send a request packet */ | ||
| 429 | send_renew(xid, 0, requested_ip); /* broadcast */ | ||
| 430 | |||
| 431 | t2 = (lease - t2) / 2 + t2; | ||
| 432 | timeout = t2 + start; | ||
| 433 | } | ||
| 434 | break; | ||
| 435 | case RELEASED: | ||
| 436 | /* yah, I know, *you* say it would never happen */ | ||
| 437 | timeout = 0x7fffffff; | ||
| 438 | break; | ||
| 439 | } | ||
| 440 | } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) { | ||
| 441 | /* a packet is ready, read it */ | ||
| 442 | |||
| 443 | if (listen_mode == LISTEN_KERNEL) | ||
| 444 | len = get_packet(&packet, fd); | ||
| 445 | else len = get_raw_packet(&packet, fd); | ||
| 446 | |||
| 447 | if (len == -1 && errno != EINTR) { | ||
| 448 | DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno)); | ||
| 449 | change_mode(listen_mode); /* just close and reopen */ | ||
| 450 | } | ||
| 451 | if (len < 0) continue; | ||
| 452 | |||
| 453 | if (packet.xid != xid) { | ||
| 454 | DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)", | ||
| 455 | (unsigned long) packet.xid, xid); | ||
| 456 | continue; | ||
| 457 | } | ||
| 458 | |||
| 459 | if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { | ||
| 460 | DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring"); | ||
| 461 | continue; | ||
| 462 | } | ||
| 463 | |||
| 464 | switch (state) { | ||
| 465 | case INIT_SELECTING: | ||
| 466 | /* Must be a DHCPOFFER to one of our xid's */ | ||
| 467 | if (*message == DHCPOFFER) { | ||
| 468 | if ((temp = get_option(&packet, DHCP_SERVER_ID))) { | ||
| 469 | memcpy(&server_addr, temp, 4); | ||
| 470 | xid = packet.xid; | ||
| 471 | requested_ip = packet.yiaddr; | ||
| 472 | |||
| 473 | /* enter requesting state */ | ||
| 474 | state = REQUESTING; | ||
| 475 | timeout = now; | ||
| 476 | packet_num = 0; | ||
| 477 | } else { | ||
| 478 | DEBUG(LOG_ERR, "No server ID in message"); | ||
| 479 | } | ||
| 480 | } | ||
| 481 | break; | ||
| 482 | case RENEW_REQUESTED: | ||
| 483 | case REQUESTING: | ||
| 484 | case RENEWING: | ||
| 485 | case REBINDING: | ||
| 486 | if (*message == DHCPACK) { | ||
| 487 | if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) { | ||
| 488 | LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease"); | ||
| 489 | lease = 60 * 60; | ||
| 490 | } else { | ||
| 491 | memcpy(&lease, temp, 4); | ||
| 492 | lease = ntohl(lease); | ||
| 493 | } | ||
| 494 | |||
| 495 | /* enter bound state */ | ||
| 496 | t1 = lease / 2; | ||
| 497 | |||
| 498 | /* little fixed point for n * .875 */ | ||
| 499 | t2 = (lease * 0x7) >> 3; | ||
| 500 | temp_addr.s_addr = packet.yiaddr; | ||
| 501 | LOG(LOG_INFO, "Lease of %s obtained, lease time %ld", | ||
| 502 | inet_ntoa(temp_addr), lease); | ||
| 503 | start = now; | ||
| 504 | timeout = t1 + start; | ||
| 505 | requested_ip = packet.yiaddr; | ||
| 506 | run_script(&packet, | ||
| 507 | ((state == RENEWING || state == REBINDING) ? "renew" : "bound")); | ||
| 508 | |||
| 509 | state = BOUND; | ||
| 510 | change_mode(LISTEN_NONE); | ||
| 511 | if (client_config.quit_after_lease) | ||
| 512 | exit_client(0); | ||
| 513 | if (!client_config.foreground) | ||
| 514 | background(); | ||
| 515 | |||
| 516 | } else if (*message == DHCPNAK) { | ||
| 517 | /* return to init state */ | ||
| 518 | LOG(LOG_INFO, "Received DHCP NAK"); | ||
| 519 | run_script(&packet, "nak"); | ||
| 520 | if (state != REQUESTING) | ||
| 521 | run_script(NULL, "deconfig"); | ||
| 522 | state = INIT_SELECTING; | ||
| 523 | timeout = now; | ||
| 524 | requested_ip = 0; | ||
| 525 | packet_num = 0; | ||
| 526 | change_mode(LISTEN_RAW); | ||
| 527 | sleep(3); /* avoid excessive network traffic */ | ||
| 528 | } | ||
| 529 | break; | ||
| 530 | /* case BOUND, RELEASED: - ignore all packets */ | ||
| 531 | } | ||
| 532 | } else if (retval > 0 && FD_ISSET(signal_pipe[0], &rfds)) { | ||
| 533 | if (read(signal_pipe[0], &sig, sizeof(signal)) < 0) { | ||
| 534 | DEBUG(LOG_ERR, "Could not read signal: %s", | ||
| 535 | strerror(errno)); | ||
| 536 | continue; /* probably just EINTR */ | ||
| 537 | } | ||
| 538 | switch (sig) { | ||
| 539 | case SIGUSR1: | ||
| 540 | perform_renew(); | ||
| 541 | break; | ||
| 542 | case SIGUSR2: | ||
| 543 | perform_release(); | ||
| 544 | break; | ||
| 545 | case SIGTERM: | ||
| 546 | LOG(LOG_INFO, "Received SIGTERM"); | ||
| 547 | exit_client(0); | ||
| 548 | } | ||
| 549 | } else if (retval == -1 && errno == EINTR) { | ||
| 550 | /* a signal was caught */ | ||
| 551 | } else { | ||
| 552 | /* An error occured */ | ||
| 553 | DEBUG(LOG_ERR, "Error on select"); | ||
| 554 | } | ||
| 555 | |||
| 556 | } | ||
| 557 | return 0; | ||
| 558 | } | ||
| 559 | |||
diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h new file mode 100644 index 000000000..52157f5c8 --- /dev/null +++ b/networking/udhcp/dhcpc.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | /* dhcpc.h */ | ||
| 2 | #ifndef _DHCPC_H | ||
| 3 | #define _DHCPC_H | ||
| 4 | |||
| 5 | #include "libbb_udhcp.h" | ||
| 6 | |||
| 7 | #define INIT_SELECTING 0 | ||
| 8 | #define REQUESTING 1 | ||
| 9 | #define BOUND 2 | ||
| 10 | #define RENEWING 3 | ||
| 11 | #define REBINDING 4 | ||
| 12 | #define INIT_REBOOT 5 | ||
| 13 | #define RENEW_REQUESTED 6 | ||
| 14 | #define RELEASED 7 | ||
| 15 | |||
| 16 | |||
| 17 | struct client_config_t { | ||
| 18 | char foreground; /* Do not fork */ | ||
| 19 | char quit_after_lease; /* Quit after obtaining lease */ | ||
| 20 | char abort_if_no_lease; /* Abort if no lease */ | ||
| 21 | char background_if_no_lease; /* Fork to background if no lease */ | ||
| 22 | char *interface; /* The name of the interface to use */ | ||
| 23 | char *pidfile; /* Optionally store the process ID */ | ||
| 24 | char *script; /* User script to run at dhcp events */ | ||
| 25 | unsigned char *clientid; /* Optional client id to use */ | ||
| 26 | unsigned char *hostname; /* Optional hostname to use */ | ||
| 27 | int ifindex; /* Index number of the interface to use */ | ||
| 28 | unsigned char arp[6]; /* Our arp address */ | ||
| 29 | }; | ||
| 30 | |||
| 31 | extern struct client_config_t client_config; | ||
| 32 | |||
| 33 | |||
| 34 | #endif | ||
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c new file mode 100644 index 000000000..6c16dfeb0 --- /dev/null +++ b/networking/udhcp/dhcpd.c | |||
| @@ -0,0 +1,287 @@ | |||
| 1 | /* dhcpd.c | ||
| 2 | * | ||
| 3 | * udhcp Server | ||
| 4 | * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au> | ||
| 5 | * Chris Trew <ctrew@moreton.com.au> | ||
| 6 | * | ||
| 7 | * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to the Free Software | ||
| 21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <fcntl.h> | ||
| 25 | #include <string.h> | ||
| 26 | #include <stdlib.h> | ||
| 27 | #include <sys/wait.h> | ||
| 28 | #include <sys/stat.h> | ||
| 29 | #include <arpa/inet.h> | ||
| 30 | #include <netdb.h> | ||
| 31 | #include <netinet/in.h> | ||
| 32 | #include <stdio.h> | ||
| 33 | #include <sys/types.h> | ||
| 34 | #include <sys/socket.h> | ||
| 35 | #include <unistd.h> | ||
| 36 | #include <signal.h> | ||
| 37 | #include <errno.h> | ||
| 38 | #include <sys/ioctl.h> | ||
| 39 | #include <time.h> | ||
| 40 | #include <sys/time.h> | ||
| 41 | |||
| 42 | #include "debug.h" | ||
| 43 | #include "dhcpd.h" | ||
| 44 | #include "arpping.h" | ||
| 45 | #include "socket.h" | ||
| 46 | #include "options.h" | ||
| 47 | #include "files.h" | ||
| 48 | #include "leases.h" | ||
| 49 | #include "packet.h" | ||
| 50 | #include "serverpacket.h" | ||
| 51 | #include "pidfile.h" | ||
| 52 | |||
| 53 | |||
| 54 | /* globals */ | ||
| 55 | struct dhcpOfferedAddr *leases; | ||
| 56 | struct server_config_t server_config; | ||
| 57 | static int signal_pipe[2]; | ||
| 58 | |||
| 59 | /* Exit and cleanup */ | ||
| 60 | static void exit_server(int retval) | ||
| 61 | { | ||
| 62 | pidfile_delete(server_config.pidfile); | ||
| 63 | CLOSE_LOG(); | ||
| 64 | exit(retval); | ||
| 65 | } | ||
| 66 | |||
| 67 | |||
| 68 | /* Signal handler */ | ||
| 69 | static void signal_handler(int sig) | ||
| 70 | { | ||
| 71 | if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) { | ||
| 72 | LOG(LOG_ERR, "Could not send signal: %s", | ||
| 73 | strerror(errno)); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | |||
| 78 | #ifdef COMBINED_BINARY | ||
| 79 | int udhcpd_main(int argc, char *argv[]) | ||
| 80 | #else | ||
| 81 | int main(int argc, char *argv[]) | ||
| 82 | #endif | ||
| 83 | { | ||
| 84 | fd_set rfds; | ||
| 85 | struct timeval tv; | ||
| 86 | int server_socket = -1; | ||
| 87 | int bytes, retval; | ||
| 88 | struct dhcpMessage packet; | ||
| 89 | unsigned char *state; | ||
| 90 | unsigned char *server_id, *requested; | ||
| 91 | u_int32_t server_id_align, requested_align; | ||
| 92 | unsigned long timeout_end; | ||
| 93 | struct option_set *option; | ||
| 94 | struct dhcpOfferedAddr *lease; | ||
| 95 | int pid_fd; | ||
| 96 | int max_sock; | ||
| 97 | int sig; | ||
| 98 | |||
| 99 | OPEN_LOG("udhcpd"); | ||
| 100 | LOG(LOG_INFO, "udhcp server (v%s) started", VERSION); | ||
| 101 | |||
| 102 | memset(&server_config, 0, sizeof(struct server_config_t)); | ||
| 103 | |||
| 104 | if (argc < 2) | ||
| 105 | read_config(DHCPD_CONF_FILE); | ||
| 106 | else read_config(argv[1]); | ||
| 107 | |||
| 108 | pid_fd = pidfile_acquire(server_config.pidfile); | ||
| 109 | pidfile_write_release(pid_fd); | ||
| 110 | |||
| 111 | if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) { | ||
| 112 | memcpy(&server_config.lease, option->data + 2, 4); | ||
| 113 | server_config.lease = ntohl(server_config.lease); | ||
| 114 | } | ||
| 115 | else server_config.lease = LEASE_TIME; | ||
| 116 | |||
| 117 | leases = malloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases); | ||
| 118 | memset(leases, 0, sizeof(struct dhcpOfferedAddr) * server_config.max_leases); | ||
| 119 | read_leases(server_config.lease_file); | ||
| 120 | |||
| 121 | if (read_interface(server_config.interface, &server_config.ifindex, | ||
| 122 | &server_config.server, server_config.arp) < 0) | ||
| 123 | exit_server(1); | ||
| 124 | |||
| 125 | #ifndef DEBUGGING | ||
| 126 | pid_fd = pidfile_acquire(server_config.pidfile); /* hold lock during fork. */ | ||
| 127 | if (daemon(0, 0) == -1) { | ||
| 128 | perror("fork"); | ||
| 129 | exit_server(1); | ||
| 130 | } | ||
| 131 | pidfile_write_release(pid_fd); | ||
| 132 | #endif | ||
| 133 | |||
| 134 | |||
| 135 | socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); | ||
| 136 | signal(SIGUSR1, signal_handler); | ||
| 137 | signal(SIGTERM, signal_handler); | ||
| 138 | |||
| 139 | timeout_end = time(0) + server_config.auto_time; | ||
| 140 | while(1) { /* loop until universe collapses */ | ||
| 141 | |||
| 142 | if (server_socket < 0) | ||
| 143 | if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) { | ||
| 144 | LOG(LOG_ERR, "FATAL: couldn't create server socket, %s", strerror(errno)); | ||
| 145 | exit_server(0); | ||
| 146 | } | ||
| 147 | |||
| 148 | FD_ZERO(&rfds); | ||
| 149 | FD_SET(server_socket, &rfds); | ||
| 150 | FD_SET(signal_pipe[0], &rfds); | ||
| 151 | if (server_config.auto_time) { | ||
| 152 | tv.tv_sec = timeout_end - time(0); | ||
| 153 | tv.tv_usec = 0; | ||
| 154 | } | ||
| 155 | if (!server_config.auto_time || tv.tv_sec > 0) { | ||
| 156 | max_sock = server_socket > signal_pipe[0] ? server_socket : signal_pipe[0]; | ||
| 157 | retval = select(max_sock + 1, &rfds, NULL, NULL, | ||
| 158 | server_config.auto_time ? &tv : NULL); | ||
| 159 | } else retval = 0; /* If we already timed out, fall through */ | ||
| 160 | |||
| 161 | if (retval == 0) { | ||
| 162 | write_leases(); | ||
| 163 | timeout_end = time(0) + server_config.auto_time; | ||
| 164 | continue; | ||
| 165 | } else if (retval < 0 && errno != EINTR) { | ||
| 166 | DEBUG(LOG_INFO, "error on select"); | ||
| 167 | continue; | ||
| 168 | } | ||
| 169 | |||
| 170 | if (FD_ISSET(signal_pipe[0], &rfds)) { | ||
| 171 | if (read(signal_pipe[0], &sig, sizeof(sig)) < 0) | ||
| 172 | continue; /* probably just EINTR */ | ||
| 173 | switch (sig) { | ||
| 174 | case SIGUSR1: | ||
| 175 | LOG(LOG_INFO, "Received a SIGUSR1"); | ||
| 176 | write_leases(); | ||
| 177 | /* why not just reset the timeout, eh */ | ||
| 178 | timeout_end = time(0) + server_config.auto_time; | ||
| 179 | continue; | ||
| 180 | case SIGTERM: | ||
| 181 | LOG(LOG_INFO, "Received a SIGTERM"); | ||
| 182 | exit_server(0); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */ | ||
| 187 | if (bytes == -1 && errno != EINTR) { | ||
| 188 | DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno)); | ||
| 189 | close(server_socket); | ||
| 190 | server_socket = -1; | ||
| 191 | } | ||
| 192 | continue; | ||
| 193 | } | ||
| 194 | |||
| 195 | if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { | ||
| 196 | DEBUG(LOG_ERR, "couldn't get option from packet, ignoring"); | ||
| 197 | continue; | ||
| 198 | } | ||
| 199 | |||
| 200 | /* ADDME: look for a static lease */ | ||
| 201 | lease = find_lease_by_chaddr(packet.chaddr); | ||
| 202 | switch (state[0]) { | ||
| 203 | case DHCPDISCOVER: | ||
| 204 | DEBUG(LOG_INFO,"received DISCOVER"); | ||
| 205 | |||
| 206 | if (sendOffer(&packet) < 0) { | ||
| 207 | LOG(LOG_ERR, "send OFFER failed"); | ||
| 208 | } | ||
| 209 | break; | ||
| 210 | case DHCPREQUEST: | ||
| 211 | DEBUG(LOG_INFO, "received REQUEST"); | ||
| 212 | |||
| 213 | requested = get_option(&packet, DHCP_REQUESTED_IP); | ||
| 214 | server_id = get_option(&packet, DHCP_SERVER_ID); | ||
| 215 | |||
| 216 | if (requested) memcpy(&requested_align, requested, 4); | ||
| 217 | if (server_id) memcpy(&server_id_align, server_id, 4); | ||
| 218 | |||
| 219 | if (lease) { /*ADDME: or static lease */ | ||
| 220 | if (server_id) { | ||
| 221 | /* SELECTING State */ | ||
| 222 | DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align)); | ||
| 223 | if (server_id_align == server_config.server && requested && | ||
| 224 | requested_align == lease->yiaddr) { | ||
| 225 | sendACK(&packet, lease->yiaddr); | ||
| 226 | } | ||
| 227 | } else { | ||
| 228 | if (requested) { | ||
| 229 | /* INIT-REBOOT State */ | ||
| 230 | if (lease->yiaddr == requested_align) | ||
| 231 | sendACK(&packet, lease->yiaddr); | ||
| 232 | else sendNAK(&packet); | ||
| 233 | } else { | ||
| 234 | /* RENEWING or REBINDING State */ | ||
| 235 | if (lease->yiaddr == packet.ciaddr) | ||
| 236 | sendACK(&packet, lease->yiaddr); | ||
| 237 | else { | ||
| 238 | /* don't know what to do!!!! */ | ||
| 239 | sendNAK(&packet); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | /* what to do if we have no record of the client */ | ||
| 245 | } else if (server_id) { | ||
| 246 | /* SELECTING State */ | ||
| 247 | |||
| 248 | } else if (requested) { | ||
| 249 | /* INIT-REBOOT State */ | ||
| 250 | if ((lease = find_lease_by_yiaddr(requested_align))) { | ||
| 251 | if (lease_expired(lease)) { | ||
| 252 | /* probably best if we drop this lease */ | ||
| 253 | memset(lease->chaddr, 0, 16); | ||
| 254 | /* make some contention for this address */ | ||
| 255 | } else sendNAK(&packet); | ||
| 256 | } else if (requested_align < server_config.start || | ||
| 257 | requested_align > server_config.end) { | ||
| 258 | sendNAK(&packet); | ||
| 259 | } /* else remain silent */ | ||
| 260 | |||
| 261 | } else { | ||
| 262 | /* RENEWING or REBINDING State */ | ||
| 263 | } | ||
| 264 | break; | ||
| 265 | case DHCPDECLINE: | ||
| 266 | DEBUG(LOG_INFO,"received DECLINE"); | ||
| 267 | if (lease) { | ||
| 268 | memset(lease->chaddr, 0, 16); | ||
| 269 | lease->expires = time(0) + server_config.decline_time; | ||
| 270 | } | ||
| 271 | break; | ||
| 272 | case DHCPRELEASE: | ||
| 273 | DEBUG(LOG_INFO,"received RELEASE"); | ||
| 274 | if (lease) lease->expires = time(0); | ||
| 275 | break; | ||
| 276 | case DHCPINFORM: | ||
| 277 | DEBUG(LOG_INFO,"received INFORM"); | ||
| 278 | send_inform(&packet); | ||
| 279 | break; | ||
| 280 | default: | ||
| 281 | LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]); | ||
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | |||
diff --git a/networking/udhcp/dhcpd.h b/networking/udhcp/dhcpd.h new file mode 100644 index 000000000..2971e19db --- /dev/null +++ b/networking/udhcp/dhcpd.h | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | /* dhcpd.h */ | ||
| 2 | #ifndef _DHCPD_H | ||
| 3 | #define _DHCPD_H | ||
| 4 | |||
| 5 | #include <netinet/ip.h> | ||
| 6 | #include <netinet/udp.h> | ||
| 7 | |||
| 8 | #include "libbb_udhcp.h" | ||
| 9 | #include "leases.h" | ||
| 10 | |||
| 11 | /************************************/ | ||
| 12 | /* Defaults _you_ may want to tweak */ | ||
| 13 | /************************************/ | ||
| 14 | |||
| 15 | /* the period of time the client is allowed to use that address */ | ||
| 16 | #define LEASE_TIME (60*60*24*10) /* 10 days of seconds */ | ||
| 17 | |||
| 18 | /* where to find the DHCP server configuration file */ | ||
| 19 | #define DHCPD_CONF_FILE "/etc/udhcpd.conf" | ||
| 20 | |||
| 21 | /*****************************************************************/ | ||
| 22 | /* Do not modify below here unless you know what you are doing!! */ | ||
| 23 | /*****************************************************************/ | ||
| 24 | |||
| 25 | /* DHCP protocol -- see RFC 2131 */ | ||
| 26 | #define SERVER_PORT 67 | ||
| 27 | #define CLIENT_PORT 68 | ||
| 28 | |||
| 29 | #define DHCP_MAGIC 0x63825363 | ||
| 30 | |||
| 31 | /* DHCP option codes (partial list) */ | ||
| 32 | #define DHCP_PADDING 0x00 | ||
| 33 | #define DHCP_SUBNET 0x01 | ||
| 34 | #define DHCP_TIME_OFFSET 0x02 | ||
| 35 | #define DHCP_ROUTER 0x03 | ||
| 36 | #define DHCP_TIME_SERVER 0x04 | ||
| 37 | #define DHCP_NAME_SERVER 0x05 | ||
| 38 | #define DHCP_DNS_SERVER 0x06 | ||
| 39 | #define DHCP_LOG_SERVER 0x07 | ||
| 40 | #define DHCP_COOKIE_SERVER 0x08 | ||
| 41 | #define DHCP_LPR_SERVER 0x09 | ||
| 42 | #define DHCP_HOST_NAME 0x0c | ||
| 43 | #define DHCP_BOOT_SIZE 0x0d | ||
| 44 | #define DHCP_DOMAIN_NAME 0x0f | ||
| 45 | #define DHCP_SWAP_SERVER 0x10 | ||
| 46 | #define DHCP_ROOT_PATH 0x11 | ||
| 47 | #define DHCP_IP_TTL 0x17 | ||
| 48 | #define DHCP_MTU 0x1a | ||
| 49 | #define DHCP_BROADCAST 0x1c | ||
| 50 | #define DHCP_NTP_SERVER 0x2a | ||
| 51 | #define DHCP_WINS_SERVER 0x2c | ||
| 52 | #define DHCP_REQUESTED_IP 0x32 | ||
| 53 | #define DHCP_LEASE_TIME 0x33 | ||
| 54 | #define DHCP_OPTION_OVER 0x34 | ||
| 55 | #define DHCP_MESSAGE_TYPE 0x35 | ||
| 56 | #define DHCP_SERVER_ID 0x36 | ||
| 57 | #define DHCP_PARAM_REQ 0x37 | ||
| 58 | #define DHCP_MESSAGE 0x38 | ||
| 59 | #define DHCP_MAX_SIZE 0x39 | ||
| 60 | #define DHCP_T1 0x3a | ||
| 61 | #define DHCP_T2 0x3b | ||
| 62 | #define DHCP_VENDOR 0x3c | ||
| 63 | #define DHCP_CLIENT_ID 0x3d | ||
| 64 | |||
| 65 | #define DHCP_END 0xFF | ||
| 66 | |||
| 67 | |||
| 68 | #define BOOTREQUEST 1 | ||
| 69 | #define BOOTREPLY 2 | ||
| 70 | |||
| 71 | #define ETH_10MB 1 | ||
| 72 | #define ETH_10MB_LEN 6 | ||
| 73 | |||
| 74 | #define DHCPDISCOVER 1 | ||
| 75 | #define DHCPOFFER 2 | ||
| 76 | #define DHCPREQUEST 3 | ||
| 77 | #define DHCPDECLINE 4 | ||
| 78 | #define DHCPACK 5 | ||
| 79 | #define DHCPNAK 6 | ||
| 80 | #define DHCPRELEASE 7 | ||
| 81 | #define DHCPINFORM 8 | ||
| 82 | |||
| 83 | #define BROADCAST_FLAG 0x8000 | ||
| 84 | |||
| 85 | #define OPTION_FIELD 0 | ||
| 86 | #define FILE_FIELD 1 | ||
| 87 | #define SNAME_FIELD 2 | ||
| 88 | |||
| 89 | /* miscellaneous defines */ | ||
| 90 | #define MAC_BCAST_ADDR (unsigned char *) "\xff\xff\xff\xff\xff\xff" | ||
| 91 | #define OPT_CODE 0 | ||
| 92 | #define OPT_LEN 1 | ||
| 93 | #define OPT_DATA 2 | ||
| 94 | |||
| 95 | struct option_set { | ||
| 96 | unsigned char *data; | ||
| 97 | struct option_set *next; | ||
| 98 | }; | ||
| 99 | |||
| 100 | struct server_config_t { | ||
| 101 | u_int32_t server; /* Our IP, in network order */ | ||
| 102 | u_int32_t start; /* Start address of leases, network order */ | ||
| 103 | u_int32_t end; /* End of leases, network order */ | ||
| 104 | struct option_set *options; /* List of DHCP options loaded from the config file */ | ||
| 105 | char *interface; /* The name of the interface to use */ | ||
| 106 | int ifindex; /* Index number of the interface to use */ | ||
| 107 | unsigned char arp[6]; /* Our arp address */ | ||
| 108 | unsigned long lease; /* lease time in seconds (host order) */ | ||
| 109 | unsigned long max_leases; /* maximum number of leases (including reserved address) */ | ||
| 110 | char remaining; /* should the lease file be interpreted as lease time remaining, or | ||
| 111 | * as the time the lease expires */ | ||
| 112 | unsigned long auto_time; /* how long should udhcpd wait before writing a config file. | ||
| 113 | * if this is zero, it will only write one on SIGUSR1 */ | ||
| 114 | unsigned long decline_time; /* how long an address is reserved if a client returns a | ||
| 115 | * decline message */ | ||
| 116 | unsigned long conflict_time; /* how long an arp conflict offender is leased for */ | ||
| 117 | unsigned long offer_time; /* how long an offered address is reserved */ | ||
| 118 | unsigned long min_lease; /* minimum lease a client can request*/ | ||
| 119 | char *lease_file; | ||
| 120 | char *pidfile; | ||
| 121 | char *notify_file; /* What to run whenever leases are written */ | ||
| 122 | u_int32_t siaddr; /* next server bootp option */ | ||
| 123 | char *sname; /* bootp server name */ | ||
| 124 | char *boot_file; /* bootp boot file option */ | ||
| 125 | }; | ||
| 126 | |||
| 127 | extern struct server_config_t server_config; | ||
| 128 | extern struct dhcpOfferedAddr *leases; | ||
| 129 | |||
| 130 | |||
| 131 | #endif | ||
diff --git a/networking/udhcp/dumpleases.c b/networking/udhcp/dumpleases.c new file mode 100644 index 000000000..2b19d9768 --- /dev/null +++ b/networking/udhcp/dumpleases.c | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | #include <fcntl.h> | ||
| 2 | #include <string.h> | ||
| 3 | #include <stdlib.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <sys/wait.h> | ||
| 6 | #include <sys/stat.h> | ||
| 7 | #include <arpa/inet.h> | ||
| 8 | #include <netdb.h> | ||
| 9 | #include <netinet/in.h> | ||
| 10 | #include <stdio.h> | ||
| 11 | #include <sys/types.h> | ||
| 12 | #include <sys/socket.h> | ||
| 13 | #include <unistd.h> | ||
| 14 | #include <syslog.h> | ||
| 15 | #include <signal.h> | ||
| 16 | #include <errno.h> | ||
| 17 | #include <getopt.h> | ||
| 18 | #include <time.h> | ||
| 19 | |||
| 20 | #include "libbb_udhcp.h" | ||
| 21 | |||
| 22 | #define REMAINING 0 | ||
| 23 | #define ABSOLUTE 1 | ||
| 24 | |||
| 25 | struct lease_t { | ||
| 26 | unsigned char chaddr[16]; | ||
| 27 | u_int32_t yiaddr; | ||
| 28 | u_int32_t expires; | ||
| 29 | }; | ||
| 30 | |||
| 31 | #ifdef BB_VER | ||
| 32 | int dumpleases_main(int argc, char *argv[]) | ||
| 33 | #else | ||
| 34 | int main(int argc, char *argv[]) | ||
| 35 | #endif | ||
| 36 | { | ||
| 37 | FILE *fp; | ||
| 38 | int i, c, mode = REMAINING; | ||
| 39 | long expires; | ||
| 40 | char file[255] = "/var/lib/misc/udhcpd.leases"; | ||
| 41 | struct lease_t lease; | ||
| 42 | struct in_addr addr; | ||
| 43 | |||
| 44 | static struct option options[] = { | ||
| 45 | {"absolute", 0, 0, 'a'}, | ||
| 46 | {"remaining", 0, 0, 'r'}, | ||
| 47 | {"file", 1, 0, 'f'}, | ||
| 48 | {"help", 0, 0, 'h'}, | ||
| 49 | {0, 0, 0, 0} | ||
| 50 | }; | ||
| 51 | |||
| 52 | while (1) { | ||
| 53 | int option_index = 0; | ||
| 54 | c = getopt_long(argc, argv, "arf:h", options, &option_index); | ||
| 55 | if (c == -1) break; | ||
| 56 | |||
| 57 | switch (c) { | ||
| 58 | case 'a': mode = ABSOLUTE; break; | ||
| 59 | case 'r': mode = REMAINING; break; | ||
| 60 | case 'f': | ||
| 61 | strncpy(file, optarg, 255); | ||
| 62 | file[254] = '\0'; | ||
| 63 | break; | ||
| 64 | case 'h': | ||
| 65 | printf("Usage: dumpleases -f <file> -[r|a]\n\n"); | ||
| 66 | printf(" -f, --file=FILENAME Leases file to load\n"); | ||
| 67 | printf(" -r, --remaining Interepret lease times as time remaing\n"); | ||
| 68 | printf(" -a, --absolute Interepret lease times as expire time\n"); | ||
| 69 | break; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | if (!(fp = fopen(file, "r"))) { | ||
| 74 | perror("could not open input file"); | ||
| 75 | return 0; | ||
| 76 | } | ||
| 77 | |||
| 78 | printf("Mac Address IP-Address Expires %s\n", mode == REMAINING ? "in" : "at"); | ||
| 79 | /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */ | ||
| 80 | while (fread(&lease, sizeof(lease), 1, fp)) { | ||
| 81 | |||
| 82 | for (i = 0; i < 6; i++) { | ||
| 83 | printf("%02x", lease.chaddr[i]); | ||
| 84 | if (i != 5) printf(":"); | ||
| 85 | } | ||
| 86 | addr.s_addr = lease.yiaddr; | ||
| 87 | printf(" %-15s", inet_ntoa(addr)); | ||
| 88 | expires = ntohl(lease.expires); | ||
| 89 | printf(" "); | ||
| 90 | if (mode == REMAINING) { | ||
| 91 | if (!expires) printf("expired\n"); | ||
| 92 | else { | ||
| 93 | if (expires > 60*60*24) { | ||
| 94 | printf("%ld days, ", expires / (60*60*24)); | ||
| 95 | expires %= 60*60*24; | ||
| 96 | } | ||
| 97 | if (expires > 60*60) { | ||
| 98 | printf("%ld hours, ", expires / (60*60)); | ||
| 99 | expires %= 60*60; | ||
| 100 | } | ||
| 101 | if (expires > 60) { | ||
| 102 | printf("%ld minutes, ", expires / 60); | ||
| 103 | expires %= 60; | ||
| 104 | } | ||
| 105 | printf("%ld seconds\n", expires); | ||
| 106 | } | ||
| 107 | } else printf("%s", ctime(&expires)); | ||
| 108 | } | ||
| 109 | fclose(fp); | ||
| 110 | |||
| 111 | return 0; | ||
| 112 | } | ||
diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c new file mode 100644 index 000000000..842e0f2db --- /dev/null +++ b/networking/udhcp/files.c | |||
| @@ -0,0 +1,284 @@ | |||
| 1 | /* | ||
| 2 | * files.c -- DHCP server file manipulation * | ||
| 3 | * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <stdio.h> | ||
| 7 | #include <sys/socket.h> | ||
| 8 | #include <arpa/inet.h> | ||
| 9 | #include <string.h> | ||
| 10 | #include <stdlib.h> | ||
| 11 | #include <time.h> | ||
| 12 | #include <ctype.h> | ||
| 13 | #include <netdb.h> | ||
| 14 | |||
| 15 | #include "debug.h" | ||
| 16 | #include "dhcpd.h" | ||
| 17 | #include "files.h" | ||
| 18 | #include "options.h" | ||
| 19 | #include "leases.h" | ||
| 20 | |||
| 21 | /* on these functions, make sure you datatype matches */ | ||
| 22 | static int read_ip(char *line, void *arg) | ||
| 23 | { | ||
| 24 | struct in_addr *addr = arg; | ||
| 25 | struct hostent *host; | ||
| 26 | int retval = 1; | ||
| 27 | |||
| 28 | if (!inet_aton(line, addr)) { | ||
| 29 | if ((host = gethostbyname(line))) | ||
| 30 | addr->s_addr = *((unsigned long *) host->h_addr_list[0]); | ||
| 31 | else retval = 0; | ||
| 32 | } | ||
| 33 | return retval; | ||
| 34 | } | ||
| 35 | |||
| 36 | |||
| 37 | static int read_str(char *line, void *arg) | ||
| 38 | { | ||
| 39 | char **dest = arg; | ||
| 40 | |||
| 41 | if (*dest) free(*dest); | ||
| 42 | *dest = strdup(line); | ||
| 43 | |||
| 44 | return 1; | ||
| 45 | } | ||
| 46 | |||
| 47 | |||
| 48 | static int read_u32(char *line, void *arg) | ||
| 49 | { | ||
| 50 | u_int32_t *dest = arg; | ||
| 51 | char *endptr; | ||
| 52 | *dest = strtoul(line, &endptr, 0); | ||
| 53 | return endptr[0] == '\0'; | ||
| 54 | } | ||
| 55 | |||
| 56 | |||
| 57 | static int read_yn(char *line, void *arg) | ||
| 58 | { | ||
| 59 | char *dest = arg; | ||
| 60 | int retval = 1; | ||
| 61 | |||
| 62 | if (!strcasecmp("yes", line)) | ||
| 63 | *dest = 1; | ||
| 64 | else if (!strcasecmp("no", line)) | ||
| 65 | *dest = 0; | ||
| 66 | else retval = 0; | ||
| 67 | |||
| 68 | return retval; | ||
| 69 | } | ||
| 70 | |||
| 71 | |||
| 72 | /* read a dhcp option and add it to opt_list */ | ||
| 73 | static int read_opt(char *line, void *arg) | ||
| 74 | { | ||
| 75 | struct option_set **opt_list = arg; | ||
| 76 | char *opt, *val, *endptr; | ||
| 77 | struct dhcp_option *option = NULL; | ||
| 78 | int retval = 0, length = 0; | ||
| 79 | char buffer[255]; | ||
| 80 | u_int16_t result_u16; | ||
| 81 | u_int32_t result_u32; | ||
| 82 | int i; | ||
| 83 | |||
| 84 | if (!(opt = strtok(line, " \t="))) return 0; | ||
| 85 | |||
| 86 | for (i = 0; options[i].code; i++) | ||
| 87 | if (!strcmp(options[i].name, opt)) | ||
| 88 | option = &(options[i]); | ||
| 89 | |||
| 90 | if (!option) return 0; | ||
| 91 | |||
| 92 | do { | ||
| 93 | val = strtok(NULL, ", \t"); | ||
| 94 | if (val) { | ||
| 95 | length = option_lengths[option->flags & TYPE_MASK]; | ||
| 96 | retval = 0; | ||
| 97 | switch (option->flags & TYPE_MASK) { | ||
| 98 | case OPTION_IP: | ||
| 99 | retval = read_ip(val, buffer); | ||
| 100 | break; | ||
| 101 | case OPTION_IP_PAIR: | ||
| 102 | retval = read_ip(val, buffer); | ||
| 103 | if (!(val = strtok(NULL, ", \t/-"))) retval = 0; | ||
| 104 | if (retval) retval = read_ip(val, buffer + 4); | ||
| 105 | break; | ||
| 106 | case OPTION_STRING: | ||
| 107 | length = strlen(val); | ||
| 108 | if (length > 0) { | ||
| 109 | if (length > 254) length = 254; | ||
| 110 | memcpy(buffer, val, length); | ||
| 111 | retval = 1; | ||
| 112 | } | ||
| 113 | break; | ||
| 114 | case OPTION_BOOLEAN: | ||
| 115 | retval = read_yn(val, buffer); | ||
| 116 | break; | ||
| 117 | case OPTION_U8: | ||
| 118 | buffer[0] = strtoul(val, &endptr, 0); | ||
| 119 | retval = (endptr[0] == '\0'); | ||
| 120 | break; | ||
| 121 | case OPTION_U16: | ||
| 122 | result_u16 = htons(strtoul(val, &endptr, 0)); | ||
| 123 | memcpy(buffer, &result_u16, 2); | ||
| 124 | retval = (endptr[0] == '\0'); | ||
| 125 | break; | ||
| 126 | case OPTION_S16: | ||
| 127 | result_u16 = htons(strtol(val, &endptr, 0)); | ||
| 128 | memcpy(buffer, &result_u16, 2); | ||
| 129 | retval = (endptr[0] == '\0'); | ||
| 130 | break; | ||
| 131 | case OPTION_U32: | ||
| 132 | result_u32 = htonl(strtoul(val, &endptr, 0)); | ||
| 133 | memcpy(buffer, &result_u32, 4); | ||
| 134 | retval = (endptr[0] == '\0'); | ||
| 135 | break; | ||
| 136 | case OPTION_S32: | ||
| 137 | result_u32 = htonl(strtol(val, &endptr, 0)); | ||
| 138 | memcpy(buffer, &result_u32, 4); | ||
| 139 | retval = (endptr[0] == '\0'); | ||
| 140 | break; | ||
| 141 | default: | ||
| 142 | break; | ||
| 143 | } | ||
| 144 | if (retval) | ||
| 145 | attach_option(opt_list, option, buffer, length); | ||
| 146 | }; | ||
| 147 | } while (val && retval && option->flags & OPTION_LIST); | ||
| 148 | return retval; | ||
| 149 | } | ||
| 150 | |||
| 151 | |||
| 152 | static struct config_keyword keywords[] = { | ||
| 153 | /* keyword[14] handler variable address default[20] */ | ||
| 154 | {"start", read_ip, &(server_config.start), "192.168.0.20"}, | ||
| 155 | {"end", read_ip, &(server_config.end), "192.168.0.254"}, | ||
| 156 | {"interface", read_str, &(server_config.interface), "eth0"}, | ||
| 157 | {"option", read_opt, &(server_config.options), ""}, | ||
| 158 | {"opt", read_opt, &(server_config.options), ""}, | ||
| 159 | {"max_leases", read_u32, &(server_config.max_leases), "254"}, | ||
| 160 | {"remaining", read_yn, &(server_config.remaining), "yes"}, | ||
| 161 | {"auto_time", read_u32, &(server_config.auto_time), "7200"}, | ||
| 162 | {"decline_time",read_u32, &(server_config.decline_time),"3600"}, | ||
| 163 | {"conflict_time",read_u32,&(server_config.conflict_time),"3600"}, | ||
| 164 | {"offer_time", read_u32, &(server_config.offer_time), "60"}, | ||
| 165 | {"min_lease", read_u32, &(server_config.min_lease), "60"}, | ||
| 166 | {"lease_file", read_str, &(server_config.lease_file), "/var/lib/misc/udhcpd.leases"}, | ||
| 167 | {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"}, | ||
| 168 | {"notify_file", read_str, &(server_config.notify_file), ""}, | ||
| 169 | {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"}, | ||
| 170 | {"sname", read_str, &(server_config.sname), ""}, | ||
| 171 | {"boot_file", read_str, &(server_config.boot_file), ""}, | ||
| 172 | /*ADDME: static lease */ | ||
| 173 | {"", NULL, NULL, ""} | ||
| 174 | }; | ||
| 175 | |||
| 176 | |||
| 177 | int read_config(char *file) | ||
| 178 | { | ||
| 179 | FILE *in; | ||
| 180 | char buffer[80], orig[80], *token, *line; | ||
| 181 | int i; | ||
| 182 | |||
| 183 | for (i = 0; strlen(keywords[i].keyword); i++) | ||
| 184 | if (strlen(keywords[i].def)) | ||
| 185 | keywords[i].handler(keywords[i].def, keywords[i].var); | ||
| 186 | |||
| 187 | if (!(in = fopen(file, "r"))) { | ||
| 188 | LOG(LOG_ERR, "unable to open config file: %s", file); | ||
| 189 | return 0; | ||
| 190 | } | ||
| 191 | |||
| 192 | while (fgets(buffer, 80, in)) { | ||
| 193 | if (strchr(buffer, '\n')) *(strchr(buffer, '\n')) = '\0'; | ||
| 194 | strncpy(orig, buffer, 80); | ||
| 195 | if (strchr(buffer, '#')) *(strchr(buffer, '#')) = '\0'; | ||
| 196 | token = buffer + strspn(buffer, " \t"); | ||
| 197 | if (*token == '\0') continue; | ||
| 198 | line = token + strcspn(token, " \t="); | ||
| 199 | if (*line == '\0') continue; | ||
| 200 | *line = '\0'; | ||
| 201 | line++; | ||
| 202 | |||
| 203 | /* eat leading whitespace */ | ||
| 204 | line = line + strspn(line, " \t="); | ||
| 205 | /* eat trailing whitespace */ | ||
| 206 | for (i = strlen(line); i > 0 && isspace(line[i - 1]); i--); | ||
| 207 | line[i] = '\0'; | ||
| 208 | |||
| 209 | for (i = 0; strlen(keywords[i].keyword); i++) | ||
| 210 | if (!strcasecmp(token, keywords[i].keyword)) | ||
| 211 | if (!keywords[i].handler(line, keywords[i].var)) { | ||
| 212 | LOG(LOG_ERR, "unable to parse '%s'", orig); | ||
| 213 | /* reset back to the default value */ | ||
| 214 | keywords[i].handler(keywords[i].def, keywords[i].var); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | fclose(in); | ||
| 218 | return 1; | ||
| 219 | } | ||
| 220 | |||
| 221 | |||
| 222 | void write_leases(void) | ||
| 223 | { | ||
| 224 | FILE *fp; | ||
| 225 | unsigned int i; | ||
| 226 | char buf[255]; | ||
| 227 | time_t curr = time(0); | ||
| 228 | unsigned long lease_time; | ||
| 229 | |||
| 230 | if (!(fp = fopen(server_config.lease_file, "w"))) { | ||
| 231 | LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file); | ||
| 232 | return; | ||
| 233 | } | ||
| 234 | |||
| 235 | for (i = 0; i < server_config.max_leases; i++) { | ||
| 236 | if (leases[i].yiaddr != 0) { | ||
| 237 | if (server_config.remaining) { | ||
| 238 | if (lease_expired(&(leases[i]))) | ||
| 239 | lease_time = 0; | ||
| 240 | else lease_time = leases[i].expires - curr; | ||
| 241 | } else lease_time = leases[i].expires; | ||
| 242 | lease_time = htonl(lease_time); | ||
| 243 | fwrite(leases[i].chaddr, 16, 1, fp); | ||
| 244 | fwrite(&(leases[i].yiaddr), 4, 1, fp); | ||
| 245 | fwrite(&lease_time, 4, 1, fp); | ||
| 246 | } | ||
| 247 | } | ||
| 248 | fclose(fp); | ||
| 249 | |||
| 250 | if (server_config.notify_file) { | ||
| 251 | sprintf(buf, "%s %s", server_config.notify_file, server_config.lease_file); | ||
| 252 | system(buf); | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | |||
| 257 | void read_leases(char *file) | ||
| 258 | { | ||
| 259 | FILE *fp; | ||
| 260 | unsigned int i = 0; | ||
| 261 | struct dhcpOfferedAddr lease; | ||
| 262 | |||
| 263 | if (!(fp = fopen(file, "r"))) { | ||
| 264 | LOG(LOG_ERR, "Unable to open %s for reading", file); | ||
| 265 | return; | ||
| 266 | } | ||
| 267 | |||
| 268 | while (i < server_config.max_leases && (fread(&lease, sizeof lease, 1, fp) == 1)) { | ||
| 269 | /* ADDME: is it a static lease */ | ||
| 270 | if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) { | ||
| 271 | lease.expires = ntohl(lease.expires); | ||
| 272 | if (!server_config.remaining) lease.expires -= time(0); | ||
| 273 | if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) { | ||
| 274 | LOG(LOG_WARNING, "Too many leases while loading %s\n", file); | ||
| 275 | break; | ||
| 276 | } | ||
| 277 | i++; | ||
| 278 | } | ||
| 279 | } | ||
| 280 | DEBUG(LOG_INFO, "Read %d leases", i); | ||
| 281 | fclose(fp); | ||
| 282 | } | ||
| 283 | |||
| 284 | |||
diff --git a/networking/udhcp/files.h b/networking/udhcp/files.h new file mode 100644 index 000000000..1f2638fe9 --- /dev/null +++ b/networking/udhcp/files.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /* files.h */ | ||
| 2 | #ifndef _FILES_H | ||
| 3 | #define _FILES_H | ||
| 4 | |||
| 5 | struct config_keyword { | ||
| 6 | char keyword[14]; | ||
| 7 | int (*handler)(char *line, void *var); | ||
| 8 | void *var; | ||
| 9 | char def[30]; | ||
| 10 | }; | ||
| 11 | |||
| 12 | |||
| 13 | int read_config(char *file); | ||
| 14 | void write_leases(void); | ||
| 15 | void read_leases(char *file); | ||
| 16 | |||
| 17 | #endif | ||
diff --git a/networking/udhcp/frontend.c b/networking/udhcp/frontend.c new file mode 100644 index 000000000..de5779508 --- /dev/null +++ b/networking/udhcp/frontend.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | #include <string.h> | ||
| 2 | |||
| 3 | extern int udhcpd_main(int argc, char *argv[]); | ||
| 4 | extern int udhcpc_main(int argc, char *argv[]); | ||
| 5 | |||
| 6 | int main(int argc, char *argv[]) | ||
| 7 | { | ||
| 8 | int ret = 0; | ||
| 9 | char *base = strrchr(argv[0], '/'); | ||
| 10 | |||
| 11 | if (strstr(base ? (base + 1) : argv[0], "dhcpd")) | ||
| 12 | ret = udhcpd_main(argc, argv); | ||
| 13 | else ret = udhcpc_main(argc, argv); | ||
| 14 | |||
| 15 | return ret; | ||
| 16 | } | ||
diff --git a/networking/udhcp/leases.c b/networking/udhcp/leases.c new file mode 100644 index 000000000..7ade91d93 --- /dev/null +++ b/networking/udhcp/leases.c | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | /* | ||
| 2 | * leases.c -- tools to manage DHCP leases | ||
| 3 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <time.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include <sys/socket.h> | ||
| 9 | #include <netinet/in.h> | ||
| 10 | #include <arpa/inet.h> | ||
| 11 | |||
| 12 | #include "debug.h" | ||
| 13 | #include "dhcpd.h" | ||
| 14 | #include "files.h" | ||
| 15 | #include "options.h" | ||
| 16 | #include "leases.h" | ||
| 17 | #include "arpping.h" | ||
| 18 | |||
| 19 | unsigned char blank_chaddr[] = {[0 ... 15] = 0}; | ||
| 20 | |||
| 21 | /* clear every lease out that chaddr OR yiaddr matches and is nonzero */ | ||
| 22 | void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr) | ||
| 23 | { | ||
| 24 | unsigned int i, j; | ||
| 25 | |||
| 26 | for (j = 0; j < 16 && !chaddr[j]; j++); | ||
| 27 | |||
| 28 | for (i = 0; i < server_config.max_leases; i++) | ||
| 29 | if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) || | ||
| 30 | (yiaddr && leases[i].yiaddr == yiaddr)) { | ||
| 31 | memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr)); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | |||
| 36 | /* add a lease into the table, clearing out any old ones */ | ||
| 37 | struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease) | ||
| 38 | { | ||
| 39 | struct dhcpOfferedAddr *oldest; | ||
| 40 | |||
| 41 | /* clean out any old ones */ | ||
| 42 | clear_lease(chaddr, yiaddr); | ||
| 43 | |||
| 44 | oldest = oldest_expired_lease(); | ||
| 45 | |||
| 46 | if (oldest) { | ||
| 47 | memcpy(oldest->chaddr, chaddr, 16); | ||
| 48 | oldest->yiaddr = yiaddr; | ||
| 49 | oldest->expires = time(0) + lease; | ||
| 50 | } | ||
| 51 | |||
| 52 | return oldest; | ||
| 53 | } | ||
| 54 | |||
| 55 | |||
| 56 | /* true if a lease has expired */ | ||
| 57 | int lease_expired(struct dhcpOfferedAddr *lease) | ||
| 58 | { | ||
| 59 | return (lease->expires < (unsigned long) time(0)); | ||
| 60 | } | ||
| 61 | |||
| 62 | |||
| 63 | /* Find the oldest expired lease, NULL if there are no expired leases */ | ||
| 64 | struct dhcpOfferedAddr *oldest_expired_lease(void) | ||
| 65 | { | ||
| 66 | struct dhcpOfferedAddr *oldest = NULL; | ||
| 67 | unsigned long oldest_lease = time(0); | ||
| 68 | unsigned int i; | ||
| 69 | |||
| 70 | |||
| 71 | for (i = 0; i < server_config.max_leases; i++) | ||
| 72 | if (oldest_lease > leases[i].expires) { | ||
| 73 | oldest_lease = leases[i].expires; | ||
| 74 | oldest = &(leases[i]); | ||
| 75 | } | ||
| 76 | return oldest; | ||
| 77 | |||
| 78 | } | ||
| 79 | |||
| 80 | |||
| 81 | /* Find the first lease that matches chaddr, NULL if no match */ | ||
| 82 | struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr) | ||
| 83 | { | ||
| 84 | unsigned int i; | ||
| 85 | |||
| 86 | for (i = 0; i < server_config.max_leases; i++) | ||
| 87 | if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]); | ||
| 88 | |||
| 89 | return NULL; | ||
| 90 | } | ||
| 91 | |||
| 92 | |||
| 93 | /* Find the first lease that matches yiaddr, NULL is no match */ | ||
| 94 | struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr) | ||
| 95 | { | ||
| 96 | unsigned int i; | ||
| 97 | |||
| 98 | for (i = 0; i < server_config.max_leases; i++) | ||
| 99 | if (leases[i].yiaddr == yiaddr) return &(leases[i]); | ||
| 100 | |||
| 101 | return NULL; | ||
| 102 | } | ||
| 103 | |||
| 104 | |||
| 105 | /* find an assignable address, it check_expired is true, we check all the expired leases as well. | ||
| 106 | * Maybe this should try expired leases by age... */ | ||
| 107 | u_int32_t find_address(int check_expired) | ||
| 108 | { | ||
| 109 | u_int32_t addr, ret; | ||
| 110 | struct dhcpOfferedAddr *lease = NULL; | ||
| 111 | |||
| 112 | addr = ntohl(server_config.start); /* addr is in host order here */ | ||
| 113 | for (;addr <= ntohl(server_config.end); addr++) { | ||
| 114 | |||
| 115 | /* ie, 192.168.55.0 */ | ||
| 116 | if (!(addr & 0xFF)) continue; | ||
| 117 | |||
| 118 | /* ie, 192.168.55.255 */ | ||
| 119 | if ((addr & 0xFF) == 0xFF) continue; | ||
| 120 | |||
| 121 | /* lease is not taken */ | ||
| 122 | ret = htonl(addr); | ||
| 123 | if ((!(lease = find_lease_by_yiaddr(ret)) || | ||
| 124 | |||
| 125 | /* or it expired and we are checking for expired leases */ | ||
| 126 | (check_expired && lease_expired(lease))) && | ||
| 127 | |||
| 128 | /* and it isn't on the network */ | ||
| 129 | !check_ip(ret)) { | ||
| 130 | return ret; | ||
| 131 | break; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | return 0; | ||
| 135 | } | ||
| 136 | |||
| 137 | |||
| 138 | /* check is an IP is taken, if it is, add it to the lease table */ | ||
| 139 | int check_ip(u_int32_t addr) | ||
| 140 | { | ||
| 141 | struct in_addr temp; | ||
| 142 | |||
| 143 | if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) { | ||
| 144 | temp.s_addr = addr; | ||
| 145 | LOG(LOG_INFO, "%s belongs to someone, reserving it for %ld seconds", | ||
| 146 | inet_ntoa(temp), server_config.conflict_time); | ||
| 147 | add_lease(blank_chaddr, addr, server_config.conflict_time); | ||
| 148 | return 1; | ||
| 149 | } else return 0; | ||
| 150 | } | ||
| 151 | |||
diff --git a/networking/udhcp/leases.h b/networking/udhcp/leases.h new file mode 100644 index 000000000..bc50f68de --- /dev/null +++ b/networking/udhcp/leases.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | /* leases.h */ | ||
| 2 | #ifndef _LEASES_H | ||
| 3 | #define _LEASES_H | ||
| 4 | |||
| 5 | |||
| 6 | struct dhcpOfferedAddr { | ||
| 7 | u_int8_t chaddr[16]; | ||
| 8 | u_int32_t yiaddr; /* network order */ | ||
| 9 | u_int32_t expires; /* host order */ | ||
| 10 | }; | ||
| 11 | |||
| 12 | extern unsigned char blank_chaddr[]; | ||
| 13 | |||
| 14 | void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr); | ||
| 15 | struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease); | ||
| 16 | int lease_expired(struct dhcpOfferedAddr *lease); | ||
| 17 | struct dhcpOfferedAddr *oldest_expired_lease(void); | ||
| 18 | struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr); | ||
| 19 | struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr); | ||
| 20 | u_int32_t find_address(int check_expired); | ||
| 21 | int check_ip(u_int32_t addr); | ||
| 22 | |||
| 23 | |||
| 24 | #endif | ||
diff --git a/networking/udhcp/libbb_udhcp.h b/networking/udhcp/libbb_udhcp.h new file mode 100644 index 000000000..d3f72c65b --- /dev/null +++ b/networking/udhcp/libbb_udhcp.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | /* libbb_udhcp.h - busybox compatability wrapper */ | ||
| 2 | |||
| 3 | #ifndef _LIBBB_UDHCP_H | ||
| 4 | #define _LIBBB_UDHCP_H | ||
| 5 | |||
| 6 | #ifdef BB_VER | ||
| 7 | #include "libbb.h" | ||
| 8 | |||
| 9 | #ifdef CONFIG_FEATURE_UDHCP_SYSLOG | ||
| 10 | #define SYSLOG | ||
| 11 | #endif | ||
| 12 | |||
| 13 | #ifdef CONFIG_FEATURE_UDHCP_DEBUG | ||
| 14 | #define DEBUG | ||
| 15 | #endif | ||
| 16 | |||
| 17 | #define COMBINED_BINARY | ||
| 18 | #define VERSION "0.9.8-pre" | ||
| 19 | |||
| 20 | #else /* ! BB_VER */ | ||
| 21 | |||
| 22 | #define TRUE 1 | ||
| 23 | #define FALSE 0 | ||
| 24 | |||
| 25 | #define xmalloc malloc | ||
| 26 | |||
| 27 | #endif /* BB_VER */ | ||
| 28 | |||
| 29 | #endif /* _LIBBB_UDHCP_H */ | ||
diff --git a/networking/udhcp/options.c b/networking/udhcp/options.c new file mode 100644 index 000000000..58144728e --- /dev/null +++ b/networking/udhcp/options.c | |||
| @@ -0,0 +1,230 @@ | |||
| 1 | /* | ||
| 2 | * options.c -- DHCP server option packet tools | ||
| 3 | * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <stdio.h> | ||
| 7 | #include <stdlib.h> | ||
| 8 | #include <string.h> | ||
| 9 | |||
| 10 | #include "debug.h" | ||
| 11 | #include "dhcpd.h" | ||
| 12 | #include "files.h" | ||
| 13 | #include "options.h" | ||
| 14 | #include "leases.h" | ||
| 15 | |||
| 16 | |||
| 17 | /* supported options are easily added here */ | ||
| 18 | struct dhcp_option options[] = { | ||
| 19 | /* name[10] flags code */ | ||
| 20 | {"subnet", OPTION_IP | OPTION_REQ, 0x01}, | ||
| 21 | {"timezone", OPTION_S32, 0x02}, | ||
| 22 | {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03}, | ||
| 23 | {"timesvr", OPTION_IP | OPTION_LIST, 0x04}, | ||
| 24 | {"namesvr", OPTION_IP | OPTION_LIST, 0x05}, | ||
| 25 | {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06}, | ||
| 26 | {"logsvr", OPTION_IP | OPTION_LIST, 0x07}, | ||
| 27 | {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08}, | ||
| 28 | {"lprsvr", OPTION_IP | OPTION_LIST, 0x09}, | ||
| 29 | {"hostname", OPTION_STRING | OPTION_REQ, 0x0c}, | ||
| 30 | {"bootsize", OPTION_U16, 0x0d}, | ||
| 31 | {"domain", OPTION_STRING | OPTION_REQ, 0x0f}, | ||
| 32 | {"swapsvr", OPTION_IP, 0x10}, | ||
| 33 | {"rootpath", OPTION_STRING, 0x11}, | ||
| 34 | {"ipttl", OPTION_U8, 0x17}, | ||
| 35 | {"mtu", OPTION_U16, 0x1a}, | ||
| 36 | {"broadcast", OPTION_IP | OPTION_REQ, 0x1c}, | ||
| 37 | {"ntpsrv", OPTION_IP | OPTION_LIST, 0x2a}, | ||
| 38 | {"wins", OPTION_IP | OPTION_LIST, 0x2c}, | ||
| 39 | {"requestip", OPTION_IP, 0x32}, | ||
| 40 | {"lease", OPTION_U32, 0x33}, | ||
| 41 | {"dhcptype", OPTION_U8, 0x35}, | ||
| 42 | {"serverid", OPTION_IP, 0x36}, | ||
| 43 | {"message", OPTION_STRING, 0x38}, | ||
| 44 | {"tftp", OPTION_STRING, 0x42}, | ||
| 45 | {"bootfile", OPTION_STRING, 0x43}, | ||
| 46 | {"", 0x00, 0x00} | ||
| 47 | }; | ||
| 48 | |||
| 49 | /* Lengths of the different option types */ | ||
| 50 | int option_lengths[] = { | ||
| 51 | [OPTION_IP] = 4, | ||
| 52 | [OPTION_IP_PAIR] = 8, | ||
| 53 | [OPTION_BOOLEAN] = 1, | ||
| 54 | [OPTION_STRING] = 1, | ||
| 55 | [OPTION_U8] = 1, | ||
| 56 | [OPTION_U16] = 2, | ||
| 57 | [OPTION_S16] = 2, | ||
| 58 | [OPTION_U32] = 4, | ||
| 59 | [OPTION_S32] = 4 | ||
| 60 | }; | ||
| 61 | |||
| 62 | |||
| 63 | /* get an option with bounds checking (warning, not aligned). */ | ||
| 64 | unsigned char *get_option(struct dhcpMessage *packet, int code) | ||
| 65 | { | ||
| 66 | int i, length; | ||
| 67 | unsigned char *optionptr; | ||
| 68 | int over = 0, done = 0, curr = OPTION_FIELD; | ||
| 69 | |||
| 70 | optionptr = packet->options; | ||
| 71 | i = 0; | ||
| 72 | length = 308; | ||
| 73 | while (!done) { | ||
| 74 | if (i >= length) { | ||
| 75 | LOG(LOG_WARNING, "bogus packet, option fields too long."); | ||
| 76 | return NULL; | ||
| 77 | } | ||
| 78 | if (optionptr[i + OPT_CODE] == code) { | ||
| 79 | if (i + 1 + optionptr[i + OPT_LEN] >= length) { | ||
| 80 | LOG(LOG_WARNING, "bogus packet, option fields too long."); | ||
| 81 | return NULL; | ||
| 82 | } | ||
| 83 | return optionptr + i + 2; | ||
| 84 | } | ||
| 85 | switch (optionptr[i + OPT_CODE]) { | ||
| 86 | case DHCP_PADDING: | ||
| 87 | i++; | ||
| 88 | break; | ||
| 89 | case DHCP_OPTION_OVER: | ||
| 90 | if (i + 1 + optionptr[i + OPT_LEN] >= length) { | ||
| 91 | LOG(LOG_WARNING, "bogus packet, option fields too long."); | ||
| 92 | return NULL; | ||
| 93 | } | ||
| 94 | over = optionptr[i + 3]; | ||
| 95 | i += optionptr[OPT_LEN] + 2; | ||
| 96 | break; | ||
| 97 | case DHCP_END: | ||
| 98 | if (curr == OPTION_FIELD && over & FILE_FIELD) { | ||
| 99 | optionptr = packet->file; | ||
| 100 | i = 0; | ||
| 101 | length = 128; | ||
| 102 | curr = FILE_FIELD; | ||
| 103 | } else if (curr == FILE_FIELD && over & SNAME_FIELD) { | ||
| 104 | optionptr = packet->sname; | ||
| 105 | i = 0; | ||
| 106 | length = 64; | ||
| 107 | curr = SNAME_FIELD; | ||
| 108 | } else done = 1; | ||
| 109 | break; | ||
| 110 | default: | ||
| 111 | i += optionptr[OPT_LEN + i] + 2; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | return NULL; | ||
| 115 | } | ||
| 116 | |||
| 117 | |||
| 118 | /* return the position of the 'end' option (no bounds checking) */ | ||
| 119 | int end_option(unsigned char *optionptr) | ||
| 120 | { | ||
| 121 | int i = 0; | ||
| 122 | |||
| 123 | while (optionptr[i] != DHCP_END) { | ||
| 124 | if (optionptr[i] == DHCP_PADDING) i++; | ||
| 125 | else i += optionptr[i + OPT_LEN] + 2; | ||
| 126 | } | ||
| 127 | return i; | ||
| 128 | } | ||
| 129 | |||
| 130 | |||
| 131 | /* add an option string to the options (an option string contains an option code, | ||
| 132 | * length, then data) */ | ||
| 133 | int add_option_string(unsigned char *optionptr, unsigned char *string) | ||
| 134 | { | ||
| 135 | int end = end_option(optionptr); | ||
| 136 | |||
| 137 | /* end position + string length + option code/length + end option */ | ||
| 138 | if (end + string[OPT_LEN] + 2 + 1 >= 308) { | ||
| 139 | LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", string[OPT_CODE]); | ||
| 140 | return 0; | ||
| 141 | } | ||
| 142 | DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]); | ||
| 143 | memcpy(optionptr + end, string, string[OPT_LEN] + 2); | ||
| 144 | optionptr[end + string[OPT_LEN] + 2] = DHCP_END; | ||
| 145 | return string[OPT_LEN] + 2; | ||
| 146 | } | ||
| 147 | |||
| 148 | |||
| 149 | /* add a one to four byte option to a packet */ | ||
| 150 | int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data) | ||
| 151 | { | ||
| 152 | char length = 0; | ||
| 153 | int i; | ||
| 154 | unsigned char option[2 + 4]; | ||
| 155 | unsigned char *u8; | ||
| 156 | u_int16_t *u16; | ||
| 157 | u_int32_t *u32; | ||
| 158 | u_int32_t aligned; | ||
| 159 | u8 = (unsigned char *) &aligned; | ||
| 160 | u16 = (u_int16_t *) &aligned; | ||
| 161 | u32 = &aligned; | ||
| 162 | |||
| 163 | for (i = 0; options[i].code; i++) | ||
| 164 | if (options[i].code == code) { | ||
| 165 | length = option_lengths[options[i].flags & TYPE_MASK]; | ||
| 166 | } | ||
| 167 | |||
| 168 | if (!length) { | ||
| 169 | DEBUG(LOG_ERR, "Could not add option 0x%02x", code); | ||
| 170 | return 0; | ||
| 171 | } | ||
| 172 | |||
| 173 | option[OPT_CODE] = code; | ||
| 174 | option[OPT_LEN] = length; | ||
| 175 | |||
| 176 | switch (length) { | ||
| 177 | case 1: *u8 = data; break; | ||
| 178 | case 2: *u16 = data; break; | ||
| 179 | case 4: *u32 = data; break; | ||
| 180 | } | ||
| 181 | memcpy(option + 2, &aligned, length); | ||
| 182 | return add_option_string(optionptr, option); | ||
| 183 | } | ||
| 184 | |||
| 185 | |||
| 186 | /* find option 'code' in opt_list */ | ||
| 187 | struct option_set *find_option(struct option_set *opt_list, char code) | ||
| 188 | { | ||
| 189 | while (opt_list && opt_list->data[OPT_CODE] < code) | ||
| 190 | opt_list = opt_list->next; | ||
| 191 | |||
| 192 | if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list; | ||
| 193 | else return NULL; | ||
| 194 | } | ||
| 195 | |||
| 196 | |||
| 197 | /* add an option to the opt_list */ | ||
| 198 | void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length) | ||
| 199 | { | ||
| 200 | struct option_set *existing, *new, **curr; | ||
| 201 | |||
| 202 | /* add it to an existing option */ | ||
| 203 | if ((existing = find_option(*opt_list, option->code))) { | ||
| 204 | DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name); | ||
| 205 | if (option->flags & OPTION_LIST) { | ||
| 206 | if (existing->data[OPT_LEN] + length <= 255) { | ||
| 207 | existing->data = realloc(existing->data, | ||
| 208 | existing->data[OPT_LEN] + length + 2); | ||
| 209 | memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length); | ||
| 210 | existing->data[OPT_LEN] += length; | ||
| 211 | } /* else, ignore the data, we could put this in a second option in the future */ | ||
| 212 | } /* else, ignore the new data */ | ||
| 213 | } else { | ||
| 214 | DEBUG(LOG_INFO, "Attaching option %s to list", option->name); | ||
| 215 | |||
| 216 | /* make a new option */ | ||
| 217 | new = malloc(sizeof(struct option_set)); | ||
| 218 | new->data = malloc(length + 2); | ||
| 219 | new->data[OPT_CODE] = option->code; | ||
| 220 | new->data[OPT_LEN] = length; | ||
| 221 | memcpy(new->data + 2, buffer, length); | ||
| 222 | |||
| 223 | curr = opt_list; | ||
| 224 | while (*curr && (*curr)->data[OPT_CODE] < option->code) | ||
| 225 | curr = &(*curr)->next; | ||
| 226 | |||
| 227 | new->next = *curr; | ||
| 228 | *curr = new; | ||
| 229 | } | ||
| 230 | } | ||
diff --git a/networking/udhcp/options.h b/networking/udhcp/options.h new file mode 100644 index 000000000..1fded2ef4 --- /dev/null +++ b/networking/udhcp/options.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | /* options.h */ | ||
| 2 | #ifndef _OPTIONS_H | ||
| 3 | #define _OPTIONS_H | ||
| 4 | |||
| 5 | #include "packet.h" | ||
| 6 | |||
| 7 | #define TYPE_MASK 0x0F | ||
| 8 | |||
| 9 | enum { | ||
| 10 | OPTION_IP=1, | ||
| 11 | OPTION_IP_PAIR, | ||
| 12 | OPTION_STRING, | ||
| 13 | OPTION_BOOLEAN, | ||
| 14 | OPTION_U8, | ||
| 15 | OPTION_U16, | ||
| 16 | OPTION_S16, | ||
| 17 | OPTION_U32, | ||
| 18 | OPTION_S32 | ||
| 19 | }; | ||
| 20 | |||
| 21 | #define OPTION_REQ 0x10 /* have the client request this option */ | ||
| 22 | #define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */ | ||
| 23 | |||
| 24 | struct dhcp_option { | ||
| 25 | char name[10]; | ||
| 26 | char flags; | ||
| 27 | unsigned char code; | ||
| 28 | }; | ||
| 29 | |||
| 30 | extern struct dhcp_option options[]; | ||
| 31 | extern int option_lengths[]; | ||
| 32 | |||
| 33 | unsigned char *get_option(struct dhcpMessage *packet, int code); | ||
| 34 | int end_option(unsigned char *optionptr); | ||
| 35 | int add_option_string(unsigned char *optionptr, unsigned char *string); | ||
| 36 | int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data); | ||
| 37 | struct option_set *find_option(struct option_set *opt_list, char code); | ||
| 38 | void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length); | ||
| 39 | |||
| 40 | #endif | ||
diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c new file mode 100644 index 000000000..d9f715971 --- /dev/null +++ b/networking/udhcp/packet.c | |||
| @@ -0,0 +1,203 @@ | |||
| 1 | #include <unistd.h> | ||
| 2 | #include <string.h> | ||
| 3 | #include <netinet/in.h> | ||
| 4 | #include <sys/types.h> | ||
| 5 | #include <sys/socket.h> | ||
| 6 | #include <features.h> | ||
| 7 | #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 | ||
| 8 | #include <netpacket/packet.h> | ||
| 9 | #include <net/ethernet.h> | ||
| 10 | #else | ||
| 11 | #include <asm/types.h> | ||
| 12 | #include <linux/if_packet.h> | ||
| 13 | #include <linux/if_ether.h> | ||
| 14 | #endif | ||
| 15 | #include <errno.h> | ||
| 16 | |||
| 17 | #include "packet.h" | ||
| 18 | #include "debug.h" | ||
| 19 | #include "dhcpd.h" | ||
| 20 | #include "options.h" | ||
| 21 | |||
| 22 | |||
| 23 | void init_header(struct dhcpMessage *packet, char type) | ||
| 24 | { | ||
| 25 | memset(packet, 0, sizeof(struct dhcpMessage)); | ||
| 26 | switch (type) { | ||
| 27 | case DHCPDISCOVER: | ||
| 28 | case DHCPREQUEST: | ||
| 29 | case DHCPRELEASE: | ||
| 30 | case DHCPINFORM: | ||
| 31 | packet->op = BOOTREQUEST; | ||
| 32 | break; | ||
| 33 | case DHCPOFFER: | ||
| 34 | case DHCPACK: | ||
| 35 | case DHCPNAK: | ||
| 36 | packet->op = BOOTREPLY; | ||
| 37 | } | ||
| 38 | packet->htype = ETH_10MB; | ||
| 39 | packet->hlen = ETH_10MB_LEN; | ||
| 40 | packet->cookie = htonl(DHCP_MAGIC); | ||
| 41 | packet->options[0] = DHCP_END; | ||
| 42 | add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type); | ||
| 43 | } | ||
| 44 | |||
| 45 | |||
| 46 | /* read a packet from socket fd, return -1 on read error, -2 on packet error */ | ||
| 47 | int get_packet(struct dhcpMessage *packet, int fd) | ||
| 48 | { | ||
| 49 | int bytes; | ||
| 50 | int i; | ||
| 51 | const char broken_vendors[][8] = { | ||
| 52 | "MSFT 98", | ||
| 53 | "" | ||
| 54 | }; | ||
| 55 | char unsigned *vendor; | ||
| 56 | |||
| 57 | memset(packet, 0, sizeof(struct dhcpMessage)); | ||
| 58 | bytes = read(fd, packet, sizeof(struct dhcpMessage)); | ||
| 59 | if (bytes < 0) { | ||
| 60 | DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring"); | ||
| 61 | return -1; | ||
| 62 | } | ||
| 63 | |||
| 64 | if (ntohl(packet->cookie) != DHCP_MAGIC) { | ||
| 65 | LOG(LOG_ERR, "received bogus message, ignoring"); | ||
| 66 | return -2; | ||
| 67 | } | ||
| 68 | DEBUG(LOG_INFO, "Received a packet"); | ||
| 69 | |||
| 70 | if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) { | ||
| 71 | for (i = 0; broken_vendors[i][0]; i++) { | ||
| 72 | if (vendor[OPT_LEN - 2] == (unsigned char) strlen(broken_vendors[i]) && | ||
| 73 | !strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) { | ||
| 74 | DEBUG(LOG_INFO, "broken client (%s), forcing broadcast", | ||
| 75 | broken_vendors[i]); | ||
| 76 | packet->flags |= htons(BROADCAST_FLAG); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | |||
| 82 | return bytes; | ||
| 83 | } | ||
| 84 | |||
| 85 | |||
| 86 | u_int16_t checksum(void *addr, int count) | ||
| 87 | { | ||
| 88 | /* Compute Internet Checksum for "count" bytes | ||
| 89 | * beginning at location "addr". | ||
| 90 | */ | ||
| 91 | register int32_t sum = 0; | ||
| 92 | u_int16_t *source = (u_int16_t *) addr; | ||
| 93 | |||
| 94 | while (count > 1) { | ||
| 95 | /* This is the inner loop */ | ||
| 96 | sum += *source++; | ||
| 97 | count -= 2; | ||
| 98 | } | ||
| 99 | |||
| 100 | /* Add left-over byte, if any */ | ||
| 101 | if (count > 0) { | ||
| 102 | /* Make sure that the left-over byte is added correctly both | ||
| 103 | * with little and big endian hosts */ | ||
| 104 | u_int16_t tmp = 0; | ||
| 105 | *(unsigned char *) (&tmp) = * (unsigned char *) source; | ||
| 106 | sum += tmp; | ||
| 107 | } | ||
| 108 | /* Fold 32-bit sum to 16 bits */ | ||
| 109 | while (sum >> 16) | ||
| 110 | sum = (sum & 0xffff) + (sum >> 16); | ||
| 111 | |||
| 112 | return ~sum; | ||
| 113 | } | ||
| 114 | |||
| 115 | |||
| 116 | /* Constuct a ip/udp header for a packet, and specify the source and dest hardware address */ | ||
| 117 | int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, | ||
| 118 | u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex) | ||
| 119 | { | ||
| 120 | int fd; | ||
| 121 | int result; | ||
| 122 | struct sockaddr_ll dest; | ||
| 123 | struct udp_dhcp_packet packet; | ||
| 124 | |||
| 125 | if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { | ||
| 126 | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); | ||
| 127 | return -1; | ||
| 128 | } | ||
| 129 | |||
| 130 | memset(&dest, 0, sizeof(dest)); | ||
| 131 | memset(&packet, 0, sizeof(packet)); | ||
| 132 | |||
| 133 | dest.sll_family = AF_PACKET; | ||
| 134 | dest.sll_protocol = htons(ETH_P_IP); | ||
| 135 | dest.sll_ifindex = ifindex; | ||
| 136 | dest.sll_halen = 6; | ||
| 137 | memcpy(dest.sll_addr, dest_arp, 6); | ||
| 138 | if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) { | ||
| 139 | DEBUG(LOG_ERR, "bind call failed: %s", strerror(errno)); | ||
| 140 | close(fd); | ||
| 141 | return -1; | ||
| 142 | } | ||
| 143 | |||
| 144 | packet.ip.protocol = IPPROTO_UDP; | ||
| 145 | packet.ip.saddr = source_ip; | ||
| 146 | packet.ip.daddr = dest_ip; | ||
| 147 | packet.udp.source = htons(source_port); | ||
| 148 | packet.udp.dest = htons(dest_port); | ||
| 149 | packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */ | ||
| 150 | packet.ip.tot_len = packet.udp.len; | ||
| 151 | memcpy(&(packet.data), payload, sizeof(struct dhcpMessage)); | ||
| 152 | packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet)); | ||
| 153 | |||
| 154 | packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet)); | ||
| 155 | packet.ip.ihl = sizeof(packet.ip) >> 2; | ||
| 156 | packet.ip.version = IPVERSION; | ||
| 157 | packet.ip.ttl = IPDEFTTL; | ||
| 158 | packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip)); | ||
| 159 | |||
| 160 | result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest)); | ||
| 161 | if (result <= 0) { | ||
| 162 | DEBUG(LOG_ERR, "write on socket failed: %s", strerror(errno)); | ||
| 163 | } | ||
| 164 | close(fd); | ||
| 165 | return result; | ||
| 166 | } | ||
| 167 | |||
| 168 | |||
| 169 | /* Let the kernel do all the work for packet generation */ | ||
| 170 | int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, | ||
| 171 | u_int32_t dest_ip, int dest_port) | ||
| 172 | { | ||
| 173 | int n = 1; | ||
| 174 | int fd, result; | ||
| 175 | struct sockaddr_in client; | ||
| 176 | |||
| 177 | if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) | ||
| 178 | return -1; | ||
| 179 | |||
| 180 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) | ||
| 181 | return -1; | ||
| 182 | |||
| 183 | memset(&client, 0, sizeof(client)); | ||
| 184 | client.sin_family = AF_INET; | ||
| 185 | client.sin_port = htons(source_port); | ||
| 186 | client.sin_addr.s_addr = source_ip; | ||
| 187 | |||
| 188 | if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) | ||
| 189 | return -1; | ||
| 190 | |||
| 191 | memset(&client, 0, sizeof(client)); | ||
| 192 | client.sin_family = AF_INET; | ||
| 193 | client.sin_port = htons(dest_port); | ||
| 194 | client.sin_addr.s_addr = dest_ip; | ||
| 195 | |||
| 196 | if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) | ||
| 197 | return -1; | ||
| 198 | |||
| 199 | result = write(fd, payload, sizeof(struct dhcpMessage)); | ||
| 200 | close(fd); | ||
| 201 | return result; | ||
| 202 | } | ||
| 203 | |||
diff --git a/networking/udhcp/packet.h b/networking/udhcp/packet.h new file mode 100644 index 000000000..1a263ef8b --- /dev/null +++ b/networking/udhcp/packet.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | #ifndef _PACKET_H | ||
| 2 | #define _PACKET_H | ||
| 3 | |||
| 4 | #include <netinet/udp.h> | ||
| 5 | #include <netinet/ip.h> | ||
| 6 | |||
| 7 | struct dhcpMessage { | ||
| 8 | u_int8_t op; | ||
| 9 | u_int8_t htype; | ||
| 10 | u_int8_t hlen; | ||
| 11 | u_int8_t hops; | ||
| 12 | u_int32_t xid; | ||
| 13 | u_int16_t secs; | ||
| 14 | u_int16_t flags; | ||
| 15 | u_int32_t ciaddr; | ||
| 16 | u_int32_t yiaddr; | ||
| 17 | u_int32_t siaddr; | ||
| 18 | u_int32_t giaddr; | ||
| 19 | u_int8_t chaddr[16]; | ||
| 20 | u_int8_t sname[64]; | ||
| 21 | u_int8_t file[128]; | ||
| 22 | u_int32_t cookie; | ||
| 23 | u_int8_t options[308]; /* 312 - cookie */ | ||
| 24 | }; | ||
| 25 | |||
| 26 | struct udp_dhcp_packet { | ||
| 27 | struct iphdr ip; | ||
| 28 | struct udphdr udp; | ||
| 29 | struct dhcpMessage data; | ||
| 30 | }; | ||
| 31 | |||
| 32 | void init_header(struct dhcpMessage *packet, char type); | ||
| 33 | int get_packet(struct dhcpMessage *packet, int fd); | ||
| 34 | u_int16_t checksum(void *addr, int count); | ||
| 35 | int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, | ||
| 36 | u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex); | ||
| 37 | int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, | ||
| 38 | u_int32_t dest_ip, int dest_port); | ||
| 39 | |||
| 40 | |||
| 41 | #endif | ||
diff --git a/networking/udhcp/pidfile.c b/networking/udhcp/pidfile.c new file mode 100644 index 000000000..246a64aa1 --- /dev/null +++ b/networking/udhcp/pidfile.c | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | /* pidfile.c | ||
| 2 | * | ||
| 3 | * Functions to assist in the writing and removing of pidfiles. | ||
| 4 | * | ||
| 5 | * Russ Dill <Russ.Dill@asu.edu> Soptember 2001 | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <sys/types.h> | ||
| 23 | #include <sys/stat.h> | ||
| 24 | #include <fcntl.h> | ||
| 25 | #include <unistd.h> | ||
| 26 | #include <errno.h> | ||
| 27 | #include <string.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | |||
| 30 | #include "debug.h" | ||
| 31 | |||
| 32 | int pidfile_acquire(char *pidfile) | ||
| 33 | { | ||
| 34 | int pid_fd; | ||
| 35 | if (pidfile == NULL) return -1; | ||
| 36 | |||
| 37 | pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644); | ||
| 38 | if (pid_fd < 0) { | ||
| 39 | LOG(LOG_ERR, "Unable to open pidfile %s: %s\n", | ||
| 40 | pidfile, strerror(errno)); | ||
| 41 | } else { | ||
| 42 | lockf(pid_fd, F_LOCK, 0); | ||
| 43 | } | ||
| 44 | |||
| 45 | return pid_fd; | ||
| 46 | } | ||
| 47 | |||
| 48 | |||
| 49 | void pidfile_write_release(int pid_fd) | ||
| 50 | { | ||
| 51 | FILE *out; | ||
| 52 | |||
| 53 | if (pid_fd < 0) return; | ||
| 54 | |||
| 55 | if ((out = fdopen(pid_fd, "w")) != NULL) { | ||
| 56 | fprintf(out, "%d\n", getpid()); | ||
| 57 | fclose(out); | ||
| 58 | } | ||
| 59 | lockf(pid_fd, F_UNLCK, 0); | ||
| 60 | close(pid_fd); | ||
| 61 | } | ||
| 62 | |||
| 63 | |||
| 64 | void pidfile_delete(char *pidfile) | ||
| 65 | { | ||
| 66 | if (pidfile) unlink(pidfile); | ||
| 67 | } | ||
| 68 | |||
| 69 | |||
diff --git a/networking/udhcp/pidfile.h b/networking/udhcp/pidfile.h new file mode 100644 index 000000000..0e2b148a9 --- /dev/null +++ b/networking/udhcp/pidfile.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | /* pidfile.h | ||
| 2 | * | ||
| 3 | * Functions to assist in the writing and removing of pidfiles. | ||
| 4 | * | ||
| 5 | * Russ Dill <Russ.Dill@asu.edu> Soptember 2001 | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | |||
| 23 | int pidfile_acquire(char *pidfile); | ||
| 24 | void pidfile_write_release(int pid_fd); | ||
| 25 | void pidfile_delete(char *pidfile); | ||
| 26 | |||
diff --git a/networking/udhcp/script.c b/networking/udhcp/script.c new file mode 100644 index 000000000..4ce23aafc --- /dev/null +++ b/networking/udhcp/script.c | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | /* script.c | ||
| 2 | * | ||
| 3 | * Functions to call the DHCP client notification scripts | ||
| 4 | * | ||
| 5 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <string.h> | ||
| 23 | #include <unistd.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <stdlib.h> | ||
| 26 | #include <sys/socket.h> | ||
| 27 | #include <netinet/in.h> | ||
| 28 | #include <arpa/inet.h> | ||
| 29 | #include <sys/types.h> | ||
| 30 | #include <sys/wait.h> | ||
| 31 | #include <errno.h> | ||
| 32 | |||
| 33 | #include "options.h" | ||
| 34 | #include "dhcpd.h" | ||
| 35 | #include "dhcpc.h" | ||
| 36 | #include "packet.h" | ||
| 37 | #include "options.h" | ||
| 38 | #include "debug.h" | ||
| 39 | |||
| 40 | /* get a rough idea of how long an option will be (rounding up...) */ | ||
| 41 | static int max_option_length[] = { | ||
| 42 | [OPTION_IP] = sizeof("255.255.255.255 "), | ||
| 43 | [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, | ||
| 44 | [OPTION_STRING] = 1, | ||
| 45 | [OPTION_BOOLEAN] = sizeof("yes "), | ||
| 46 | [OPTION_U8] = sizeof("255 "), | ||
| 47 | [OPTION_U16] = sizeof("65535 "), | ||
| 48 | [OPTION_S16] = sizeof("-32768 "), | ||
| 49 | [OPTION_U32] = sizeof("4294967295 "), | ||
| 50 | [OPTION_S32] = sizeof("-2147483684 "), | ||
| 51 | }; | ||
| 52 | |||
| 53 | |||
| 54 | static int upper_length(int length, struct dhcp_option *option) | ||
| 55 | { | ||
| 56 | return max_option_length[option->flags & TYPE_MASK] * | ||
| 57 | (length / option_lengths[option->flags & TYPE_MASK]); | ||
| 58 | } | ||
| 59 | |||
| 60 | |||
| 61 | static int sprintip(char *dest, char *pre, unsigned char *ip) { | ||
| 62 | return sprintf(dest, "%s%d.%d.%d.%d ", pre, ip[0], ip[1], ip[2], ip[3]); | ||
| 63 | } | ||
| 64 | |||
| 65 | |||
| 66 | /* Fill dest with the text of option 'option'. */ | ||
| 67 | static void fill_options(char *dest, unsigned char *option, struct dhcp_option *type_p) | ||
| 68 | { | ||
| 69 | int type, optlen; | ||
| 70 | u_int16_t val_u16; | ||
| 71 | int16_t val_s16; | ||
| 72 | u_int32_t val_u32; | ||
| 73 | int32_t val_s32; | ||
| 74 | int len = option[OPT_LEN - 2]; | ||
| 75 | |||
| 76 | dest += sprintf(dest, "%s=", type_p->name); | ||
| 77 | |||
| 78 | type = type_p->flags & TYPE_MASK; | ||
| 79 | optlen = option_lengths[type]; | ||
| 80 | for(;;) { | ||
| 81 | switch (type) { | ||
| 82 | case OPTION_IP_PAIR: | ||
| 83 | dest += sprintip(dest, "", option); | ||
| 84 | *(dest++) = '/'; | ||
| 85 | option += 4; | ||
| 86 | optlen = 4; | ||
| 87 | case OPTION_IP: /* Works regardless of host byte order. */ | ||
| 88 | dest += sprintip(dest, "", option); | ||
| 89 | break; | ||
| 90 | case OPTION_BOOLEAN: | ||
| 91 | dest += sprintf(dest, *option ? "yes " : "no "); | ||
| 92 | break; | ||
| 93 | case OPTION_U8: | ||
| 94 | dest += sprintf(dest, "%u ", *option); | ||
| 95 | break; | ||
| 96 | case OPTION_U16: | ||
| 97 | memcpy(&val_u16, option, 2); | ||
| 98 | dest += sprintf(dest, "%u ", ntohs(val_u16)); | ||
| 99 | break; | ||
| 100 | case OPTION_S16: | ||
| 101 | memcpy(&val_s16, option, 2); | ||
| 102 | dest += sprintf(dest, "%d ", ntohs(val_s16)); | ||
| 103 | break; | ||
| 104 | case OPTION_U32: | ||
| 105 | memcpy(&val_u32, option, 4); | ||
| 106 | dest += sprintf(dest, "%lu ", (unsigned long) ntohl(val_u32)); | ||
| 107 | break; | ||
| 108 | case OPTION_S32: | ||
| 109 | memcpy(&val_s32, option, 4); | ||
| 110 | dest += sprintf(dest, "%ld ", (long) ntohl(val_s32)); | ||
| 111 | break; | ||
| 112 | case OPTION_STRING: | ||
| 113 | memcpy(dest, option, len); | ||
| 114 | dest[len] = '\0'; | ||
| 115 | return; /* Short circuit this case */ | ||
| 116 | } | ||
| 117 | option += optlen; | ||
| 118 | len -= optlen; | ||
| 119 | if (len <= 0) break; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | |||
| 124 | static char *find_env(const char *prefix, char *defaultstr) | ||
| 125 | { | ||
| 126 | extern char **environ; | ||
| 127 | char **ptr; | ||
| 128 | const int len = strlen(prefix); | ||
| 129 | |||
| 130 | for (ptr = environ; *ptr != NULL; ptr++) { | ||
| 131 | if (strncmp(prefix, *ptr, len) == 0) | ||
| 132 | return *ptr; | ||
| 133 | } | ||
| 134 | return defaultstr; | ||
| 135 | } | ||
| 136 | |||
| 137 | |||
| 138 | /* put all the paramaters into an environment */ | ||
| 139 | static char **fill_envp(struct dhcpMessage *packet) | ||
| 140 | { | ||
| 141 | int num_options = 0; | ||
| 142 | int i, j; | ||
| 143 | char **envp; | ||
| 144 | unsigned char *temp; | ||
| 145 | char over = 0; | ||
| 146 | |||
| 147 | if (packet == NULL) | ||
| 148 | num_options = 0; | ||
| 149 | else { | ||
| 150 | for (i = 0; options[i].code; i++) | ||
| 151 | if (get_option(packet, options[i].code)) | ||
| 152 | num_options++; | ||
| 153 | if (packet->siaddr) num_options++; | ||
| 154 | if ((temp = get_option(packet, DHCP_OPTION_OVER))) | ||
| 155 | over = *temp; | ||
| 156 | if (!(over & FILE_FIELD) && packet->file[0]) num_options++; | ||
| 157 | if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++; | ||
| 158 | } | ||
| 159 | |||
| 160 | envp = xmalloc((num_options + 5) * sizeof(char *)); | ||
| 161 | envp[0] = xmalloc(sizeof("interface=") + strlen(client_config.interface)); | ||
| 162 | sprintf(envp[0], "interface=%s", client_config.interface); | ||
| 163 | envp[1] = find_env("PATH", "PATH=/bin:/usr/bin:/sbin:/usr/sbin"); | ||
| 164 | envp[2] = find_env("HOME", "HOME=/"); | ||
| 165 | |||
| 166 | if (packet == NULL) { | ||
| 167 | envp[3] = NULL; | ||
| 168 | return envp; | ||
| 169 | } | ||
| 170 | |||
| 171 | envp[3] = xmalloc(sizeof("ip=255.255.255.255")); | ||
| 172 | sprintip(envp[3], "ip=", (unsigned char *) &packet->yiaddr); | ||
| 173 | for (i = 0, j = 4; options[i].code; i++) { | ||
| 174 | if ((temp = get_option(packet, options[i].code))) { | ||
| 175 | envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], &options[i]) + strlen(options[i].name) + 2); | ||
| 176 | fill_options(envp[j], temp, &options[i]); | ||
| 177 | j++; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | if (packet->siaddr) { | ||
| 181 | envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); | ||
| 182 | sprintip(envp[j++], "siaddr=", (unsigned char *) &packet->siaddr); | ||
| 183 | } | ||
| 184 | if (!(over & FILE_FIELD) && packet->file[0]) { | ||
| 185 | /* watch out for invalid packets */ | ||
| 186 | packet->file[sizeof(packet->file) - 1] = '\0'; | ||
| 187 | envp[j] = xmalloc(sizeof("boot_file=") + strlen(packet->file)); | ||
| 188 | sprintf(envp[j++], "boot_file=%s", packet->file); | ||
| 189 | } | ||
| 190 | if (!(over & SNAME_FIELD) && packet->sname[0]) { | ||
| 191 | /* watch out for invalid packets */ | ||
| 192 | packet->sname[sizeof(packet->sname) - 1] = '\0'; | ||
| 193 | envp[j] = xmalloc(sizeof("sname=") + strlen(packet->sname)); | ||
| 194 | sprintf(envp[j++], "sname=%s", packet->sname); | ||
| 195 | } | ||
| 196 | envp[j] = NULL; | ||
| 197 | return envp; | ||
| 198 | } | ||
| 199 | |||
| 200 | |||
| 201 | /* Call a script with a par file and env vars */ | ||
| 202 | void run_script(struct dhcpMessage *packet, const char *name) | ||
| 203 | { | ||
| 204 | int pid; | ||
| 205 | char **envp; | ||
| 206 | |||
| 207 | if (client_config.script == NULL) | ||
| 208 | return; | ||
| 209 | |||
| 210 | /* call script */ | ||
| 211 | pid = fork(); | ||
| 212 | if (pid) { | ||
| 213 | waitpid(pid, NULL, 0); | ||
| 214 | return; | ||
| 215 | } else if (pid == 0) { | ||
| 216 | envp = fill_envp(packet); | ||
| 217 | |||
| 218 | /* close fd's? */ | ||
| 219 | |||
| 220 | /* exec script */ | ||
| 221 | DEBUG(LOG_INFO, "execle'ing %s", client_config.script); | ||
| 222 | execle(client_config.script, client_config.script, | ||
| 223 | name, NULL, envp); | ||
| 224 | LOG(LOG_ERR, "script %s failed: %s", | ||
| 225 | client_config.script, strerror(errno)); | ||
| 226 | exit(1); | ||
| 227 | } | ||
| 228 | } | ||
diff --git a/networking/udhcp/script.h b/networking/udhcp/script.h new file mode 100644 index 000000000..87a20cc17 --- /dev/null +++ b/networking/udhcp/script.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef _SCRIPT_H | ||
| 2 | #define _SCRIPT_H | ||
| 3 | |||
| 4 | void run_script(struct dhcpMessage *packet, const char *name); | ||
| 5 | |||
| 6 | #endif | ||
diff --git a/networking/udhcp/serverpacket.c b/networking/udhcp/serverpacket.c new file mode 100644 index 000000000..e969c7af8 --- /dev/null +++ b/networking/udhcp/serverpacket.c | |||
| @@ -0,0 +1,263 @@ | |||
| 1 | /* serverpacket.c | ||
| 2 | * | ||
| 3 | * Constuct and send DHCP server packets | ||
| 4 | * | ||
| 5 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <sys/socket.h> | ||
| 23 | #include <netinet/in.h> | ||
| 24 | #include <arpa/inet.h> | ||
| 25 | #include <string.h> | ||
| 26 | #include <time.h> | ||
| 27 | |||
| 28 | #include "packet.h" | ||
| 29 | #include "debug.h" | ||
| 30 | #include "dhcpd.h" | ||
| 31 | #include "options.h" | ||
| 32 | #include "leases.h" | ||
| 33 | |||
| 34 | /* send a packet to giaddr using the kernel ip stack */ | ||
| 35 | static int send_packet_to_relay(struct dhcpMessage *payload) | ||
| 36 | { | ||
| 37 | DEBUG(LOG_INFO, "Forwarding packet to relay"); | ||
| 38 | |||
| 39 | return kernel_packet(payload, server_config.server, SERVER_PORT, | ||
| 40 | payload->giaddr, SERVER_PORT); | ||
| 41 | } | ||
| 42 | |||
| 43 | |||
| 44 | /* send a packet to a specific arp address and ip address by creating our own ip packet */ | ||
| 45 | static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast) | ||
| 46 | { | ||
| 47 | unsigned char *chaddr; | ||
| 48 | u_int32_t ciaddr; | ||
| 49 | |||
| 50 | if (force_broadcast) { | ||
| 51 | DEBUG(LOG_INFO, "broadcasting packet to client (NAK)"); | ||
| 52 | ciaddr = INADDR_BROADCAST; | ||
| 53 | chaddr = MAC_BCAST_ADDR; | ||
| 54 | } else if (payload->ciaddr) { | ||
| 55 | DEBUG(LOG_INFO, "unicasting packet to client ciaddr"); | ||
| 56 | ciaddr = payload->ciaddr; | ||
| 57 | chaddr = payload->chaddr; | ||
| 58 | } else if (ntohs(payload->flags) & BROADCAST_FLAG) { | ||
| 59 | DEBUG(LOG_INFO, "broadcasting packet to client (requested)"); | ||
| 60 | ciaddr = INADDR_BROADCAST; | ||
| 61 | chaddr = MAC_BCAST_ADDR; | ||
| 62 | } else { | ||
| 63 | DEBUG(LOG_INFO, "unicasting packet to client yiaddr"); | ||
| 64 | ciaddr = payload->yiaddr; | ||
| 65 | chaddr = payload->chaddr; | ||
| 66 | } | ||
| 67 | return raw_packet(payload, server_config.server, SERVER_PORT, | ||
| 68 | ciaddr, CLIENT_PORT, chaddr, server_config.ifindex); | ||
| 69 | } | ||
| 70 | |||
| 71 | |||
| 72 | /* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */ | ||
| 73 | static int send_packet(struct dhcpMessage *payload, int force_broadcast) | ||
| 74 | { | ||
| 75 | int ret; | ||
| 76 | |||
| 77 | if (payload->giaddr) | ||
| 78 | ret = send_packet_to_relay(payload); | ||
| 79 | else ret = send_packet_to_client(payload, force_broadcast); | ||
| 80 | return ret; | ||
| 81 | } | ||
| 82 | |||
| 83 | |||
| 84 | static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type) | ||
| 85 | { | ||
| 86 | init_header(packet, type); | ||
| 87 | packet->xid = oldpacket->xid; | ||
| 88 | memcpy(packet->chaddr, oldpacket->chaddr, 16); | ||
| 89 | packet->flags = oldpacket->flags; | ||
| 90 | packet->giaddr = oldpacket->giaddr; | ||
| 91 | packet->ciaddr = oldpacket->ciaddr; | ||
| 92 | add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server); | ||
| 93 | } | ||
| 94 | |||
| 95 | |||
| 96 | /* add in the bootp options */ | ||
| 97 | static void add_bootp_options(struct dhcpMessage *packet) | ||
| 98 | { | ||
| 99 | packet->siaddr = server_config.siaddr; | ||
| 100 | if (server_config.sname) | ||
| 101 | strncpy(packet->sname, server_config.sname, sizeof(packet->sname) - 1); | ||
| 102 | if (server_config.boot_file) | ||
| 103 | strncpy(packet->file, server_config.boot_file, sizeof(packet->file) - 1); | ||
| 104 | } | ||
| 105 | |||
| 106 | |||
| 107 | /* send a DHCP OFFER to a DHCP DISCOVER */ | ||
| 108 | int sendOffer(struct dhcpMessage *oldpacket) | ||
| 109 | { | ||
| 110 | struct dhcpMessage packet; | ||
| 111 | struct dhcpOfferedAddr *lease = NULL; | ||
| 112 | u_int32_t req_align, lease_time_align = server_config.lease; | ||
| 113 | unsigned char *req, *lease_time; | ||
| 114 | struct option_set *curr; | ||
| 115 | struct in_addr addr; | ||
| 116 | |||
| 117 | init_packet(&packet, oldpacket, DHCPOFFER); | ||
| 118 | |||
| 119 | /* ADDME: if static, short circuit */ | ||
| 120 | /* the client is in our lease/offered table */ | ||
| 121 | if ((lease = find_lease_by_chaddr(oldpacket->chaddr))) { | ||
| 122 | if (!lease_expired(lease)) | ||
| 123 | lease_time_align = lease->expires - time(0); | ||
| 124 | packet.yiaddr = lease->yiaddr; | ||
| 125 | |||
| 126 | /* Or the client has a requested ip */ | ||
| 127 | } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) && | ||
| 128 | |||
| 129 | /* Don't look here (ugly hackish thing to do) */ | ||
| 130 | memcpy(&req_align, req, 4) && | ||
| 131 | |||
| 132 | /* and the ip is in the lease range */ | ||
| 133 | ntohl(req_align) >= ntohl(server_config.start) && | ||
| 134 | ntohl(req_align) <= ntohl(server_config.end) && | ||
| 135 | |||
| 136 | /* and its not already taken/offered */ /* ADDME: check that its not a static lease */ | ||
| 137 | ((!(lease = find_lease_by_yiaddr(req_align)) || | ||
| 138 | |||
| 139 | /* or its taken, but expired */ /* ADDME: or maybe in here */ | ||
| 140 | lease_expired(lease)))) { | ||
| 141 | packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */ | ||
| 142 | |||
| 143 | /* otherwise, find a free IP */ /*ADDME: is it a static lease? */ | ||
| 144 | } else { | ||
| 145 | packet.yiaddr = find_address(0); | ||
| 146 | |||
| 147 | /* try for an expired lease */ | ||
| 148 | if (!packet.yiaddr) packet.yiaddr = find_address(1); | ||
| 149 | } | ||
| 150 | |||
| 151 | if(!packet.yiaddr) { | ||
| 152 | LOG(LOG_WARNING, "no IP addresses to give -- OFFER abandoned"); | ||
| 153 | return -1; | ||
| 154 | } | ||
| 155 | |||
| 156 | if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) { | ||
| 157 | LOG(LOG_WARNING, "lease pool is full -- OFFER abandoned"); | ||
| 158 | return -1; | ||
| 159 | } | ||
| 160 | |||
| 161 | if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { | ||
| 162 | memcpy(&lease_time_align, lease_time, 4); | ||
| 163 | lease_time_align = ntohl(lease_time_align); | ||
| 164 | if (lease_time_align > server_config.lease) | ||
| 165 | lease_time_align = server_config.lease; | ||
| 166 | } | ||
| 167 | |||
| 168 | /* Make sure we aren't just using the lease time from the previous offer */ | ||
| 169 | if (lease_time_align < server_config.min_lease) | ||
| 170 | lease_time_align = server_config.lease; | ||
| 171 | /* ADDME: end of short circuit */ | ||
| 172 | add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); | ||
| 173 | |||
| 174 | curr = server_config.options; | ||
| 175 | while (curr) { | ||
| 176 | if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) | ||
| 177 | add_option_string(packet.options, curr->data); | ||
| 178 | curr = curr->next; | ||
| 179 | } | ||
| 180 | |||
| 181 | add_bootp_options(&packet); | ||
| 182 | |||
| 183 | addr.s_addr = packet.yiaddr; | ||
| 184 | LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr)); | ||
| 185 | return send_packet(&packet, 0); | ||
| 186 | } | ||
| 187 | |||
| 188 | |||
| 189 | int sendNAK(struct dhcpMessage *oldpacket) | ||
| 190 | { | ||
| 191 | struct dhcpMessage packet; | ||
| 192 | |||
| 193 | init_packet(&packet, oldpacket, DHCPNAK); | ||
| 194 | |||
| 195 | DEBUG(LOG_INFO, "sending NAK"); | ||
| 196 | return send_packet(&packet, 1); | ||
| 197 | } | ||
| 198 | |||
| 199 | |||
| 200 | int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr) | ||
| 201 | { | ||
| 202 | struct dhcpMessage packet; | ||
| 203 | struct option_set *curr; | ||
| 204 | unsigned char *lease_time; | ||
| 205 | u_int32_t lease_time_align = server_config.lease; | ||
| 206 | struct in_addr addr; | ||
| 207 | |||
| 208 | init_packet(&packet, oldpacket, DHCPACK); | ||
| 209 | packet.yiaddr = yiaddr; | ||
| 210 | |||
| 211 | if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { | ||
| 212 | memcpy(&lease_time_align, lease_time, 4); | ||
| 213 | lease_time_align = ntohl(lease_time_align); | ||
| 214 | if (lease_time_align > server_config.lease) | ||
| 215 | lease_time_align = server_config.lease; | ||
| 216 | else if (lease_time_align < server_config.min_lease) | ||
| 217 | lease_time_align = server_config.lease; | ||
| 218 | } | ||
| 219 | |||
| 220 | add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); | ||
| 221 | |||
| 222 | curr = server_config.options; | ||
| 223 | while (curr) { | ||
| 224 | if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) | ||
| 225 | add_option_string(packet.options, curr->data); | ||
| 226 | curr = curr->next; | ||
| 227 | } | ||
| 228 | |||
| 229 | add_bootp_options(&packet); | ||
| 230 | |||
| 231 | addr.s_addr = packet.yiaddr; | ||
| 232 | LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(addr)); | ||
| 233 | |||
| 234 | if (send_packet(&packet, 0) < 0) | ||
| 235 | return -1; | ||
| 236 | |||
| 237 | add_lease(packet.chaddr, packet.yiaddr, lease_time_align); | ||
| 238 | |||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | |||
| 242 | |||
| 243 | int send_inform(struct dhcpMessage *oldpacket) | ||
| 244 | { | ||
| 245 | struct dhcpMessage packet; | ||
| 246 | struct option_set *curr; | ||
| 247 | |||
| 248 | init_packet(&packet, oldpacket, DHCPACK); | ||
| 249 | |||
| 250 | curr = server_config.options; | ||
| 251 | while (curr) { | ||
| 252 | if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) | ||
| 253 | add_option_string(packet.options, curr->data); | ||
| 254 | curr = curr->next; | ||
| 255 | } | ||
| 256 | |||
| 257 | add_bootp_options(&packet); | ||
| 258 | |||
| 259 | return send_packet(&packet, 0); | ||
| 260 | } | ||
| 261 | |||
| 262 | |||
| 263 | |||
diff --git a/networking/udhcp/serverpacket.h b/networking/udhcp/serverpacket.h new file mode 100644 index 000000000..5a4fb2768 --- /dev/null +++ b/networking/udhcp/serverpacket.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #ifndef _SERVERPACKET_H | ||
| 2 | #define _SERVERPACKET_H | ||
| 3 | |||
| 4 | |||
| 5 | int sendOffer(struct dhcpMessage *oldpacket); | ||
| 6 | int sendNAK(struct dhcpMessage *oldpacket); | ||
| 7 | int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr); | ||
| 8 | int send_inform(struct dhcpMessage *oldpacket); | ||
| 9 | |||
| 10 | |||
| 11 | #endif | ||
diff --git a/networking/udhcp/socket.c b/networking/udhcp/socket.c new file mode 100644 index 000000000..3a2261561 --- /dev/null +++ b/networking/udhcp/socket.c | |||
| @@ -0,0 +1,157 @@ | |||
| 1 | /* | ||
| 2 | * socket.c -- DHCP server client/server socket creation | ||
| 3 | * | ||
| 4 | * udhcp client/server | ||
| 5 | * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au> | ||
| 6 | * Chris Trew <ctrew@moreton.com.au> | ||
| 7 | * | ||
| 8 | * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, | ||
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | * GNU General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License | ||
| 21 | * along with this program; if not, write to the Free Software | ||
| 22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 23 | */ | ||
| 24 | |||
| 25 | #include <sys/types.h> | ||
| 26 | #include <sys/socket.h> | ||
| 27 | #include <sys/ioctl.h> | ||
| 28 | #include <netinet/in.h> | ||
| 29 | #include <unistd.h> | ||
| 30 | #include <string.h> | ||
| 31 | #include <arpa/inet.h> | ||
| 32 | #include <net/if.h> | ||
| 33 | #include <errno.h> | ||
| 34 | #include <features.h> | ||
| 35 | #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 | ||
| 36 | #include <netpacket/packet.h> | ||
| 37 | #include <net/ethernet.h> | ||
| 38 | #else | ||
| 39 | #include <asm/types.h> | ||
| 40 | #include <linux/if_packet.h> | ||
| 41 | #include <linux/if_ether.h> | ||
| 42 | #endif | ||
| 43 | |||
| 44 | #include "debug.h" | ||
| 45 | |||
| 46 | int read_interface(char *interface, int *ifindex, u_int32_t *addr, unsigned char *arp) | ||
| 47 | { | ||
| 48 | int fd; | ||
| 49 | struct ifreq ifr; | ||
| 50 | struct sockaddr_in *our_ip; | ||
| 51 | |||
| 52 | memset(&ifr, 0, sizeof(struct ifreq)); | ||
| 53 | if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) { | ||
| 54 | ifr.ifr_addr.sa_family = AF_INET; | ||
| 55 | strcpy(ifr.ifr_name, interface); | ||
| 56 | |||
| 57 | if (addr) { | ||
| 58 | if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { | ||
| 59 | our_ip = (struct sockaddr_in *) &ifr.ifr_addr; | ||
| 60 | *addr = our_ip->sin_addr.s_addr; | ||
| 61 | DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr)); | ||
| 62 | } else { | ||
| 63 | LOG(LOG_ERR, "SIOCGIFADDR failed, is the interface up and configured?: %s", | ||
| 64 | strerror(errno)); | ||
| 65 | return -1; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) { | ||
| 70 | DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex); | ||
| 71 | *ifindex = ifr.ifr_ifindex; | ||
| 72 | } else { | ||
| 73 | LOG(LOG_ERR, "SIOCGIFINDEX failed!: %s", strerror(errno)); | ||
| 74 | return -1; | ||
| 75 | } | ||
| 76 | if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) { | ||
| 77 | memcpy(arp, ifr.ifr_hwaddr.sa_data, 6); | ||
| 78 | DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", | ||
| 79 | arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]); | ||
| 80 | } else { | ||
| 81 | LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %s", strerror(errno)); | ||
| 82 | return -1; | ||
| 83 | } | ||
| 84 | } else { | ||
| 85 | LOG(LOG_ERR, "socket failed!: %s", strerror(errno)); | ||
| 86 | return -1; | ||
| 87 | } | ||
| 88 | close(fd); | ||
| 89 | return 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | |||
| 93 | int listen_socket(unsigned int ip, int port, char *inf) | ||
| 94 | { | ||
| 95 | struct ifreq interface; | ||
| 96 | int fd; | ||
| 97 | struct sockaddr_in addr; | ||
| 98 | int n = 1; | ||
| 99 | |||
| 100 | DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s\n", ip, port, inf); | ||
| 101 | if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { | ||
| 102 | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); | ||
| 103 | return -1; | ||
| 104 | } | ||
| 105 | |||
| 106 | memset(&addr, 0, sizeof(addr)); | ||
| 107 | addr.sin_family = AF_INET; | ||
| 108 | addr.sin_port = htons(port); | ||
| 109 | addr.sin_addr.s_addr = ip; | ||
| 110 | |||
| 111 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) { | ||
| 112 | close(fd); | ||
| 113 | return -1; | ||
| 114 | } | ||
| 115 | if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) { | ||
| 116 | close(fd); | ||
| 117 | return -1; | ||
| 118 | } | ||
| 119 | |||
| 120 | strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ); | ||
| 121 | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,(char *)&interface, sizeof(interface)) < 0) { | ||
| 122 | close(fd); | ||
| 123 | return -1; | ||
| 124 | } | ||
| 125 | |||
| 126 | if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) { | ||
| 127 | close(fd); | ||
| 128 | return -1; | ||
| 129 | } | ||
| 130 | |||
| 131 | return fd; | ||
| 132 | } | ||
| 133 | |||
| 134 | |||
| 135 | int raw_socket(int ifindex) | ||
| 136 | { | ||
| 137 | int fd; | ||
| 138 | struct sockaddr_ll sock; | ||
| 139 | |||
| 140 | DEBUG(LOG_INFO, "Opening raw socket on ifindex %d\n", ifindex); | ||
| 141 | if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { | ||
| 142 | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); | ||
| 143 | return -1; | ||
| 144 | } | ||
| 145 | |||
| 146 | sock.sll_family = AF_PACKET; | ||
| 147 | sock.sll_protocol = htons(ETH_P_IP); | ||
| 148 | sock.sll_ifindex = ifindex; | ||
| 149 | if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) { | ||
| 150 | DEBUG(LOG_ERR, "bind call failed: %s", strerror(errno)); | ||
| 151 | close(fd); | ||
| 152 | return -1; | ||
| 153 | } | ||
| 154 | |||
| 155 | return fd; | ||
| 156 | } | ||
| 157 | |||
diff --git a/networking/udhcp/socket.h b/networking/udhcp/socket.h new file mode 100644 index 000000000..333994b8f --- /dev/null +++ b/networking/udhcp/socket.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | /* socket.h */ | ||
| 2 | #ifndef _SOCKET_H | ||
| 3 | #define _SOCKET_H | ||
| 4 | |||
| 5 | int read_interface(char *interface, int *ifindex, u_int32_t *addr, unsigned char *arp); | ||
| 6 | int listen_socket(unsigned int ip, int port, char *inf); | ||
| 7 | int raw_socket(int ifindex); | ||
| 8 | |||
| 9 | #endif | ||
diff --git a/networking/udhcpc.c b/networking/udhcpc.c deleted file mode 100644 index c05ca4557..000000000 --- a/networking/udhcpc.c +++ /dev/null | |||
| @@ -1,1587 +0,0 @@ | |||
| 1 | /* dhcpd.c | ||
| 2 | * | ||
| 3 | * udhcp DHCP client | ||
| 4 | * | ||
| 5 | * Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
| 6 | * | ||
| 7 | * Converted to busybox by Glenn McGrath August 2002 | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to the Free Software | ||
| 21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <stdio.h> | ||
| 25 | #include <sys/time.h> | ||
| 26 | #include <sys/types.h> | ||
| 27 | #include <sys/file.h> | ||
| 28 | #include <unistd.h> | ||
| 29 | #include <getopt.h> | ||
| 30 | #include <stdlib.h> | ||
| 31 | #include <sys/socket.h> | ||
| 32 | #include <netinet/in.h> | ||
| 33 | #include <arpa/inet.h> | ||
| 34 | #include <signal.h> | ||
| 35 | #include <time.h> | ||
| 36 | #include <string.h> | ||
| 37 | #include <sys/ioctl.h> | ||
| 38 | #include <net/if.h> | ||
| 39 | #include <errno.h> | ||
| 40 | #include <netinet/ip.h> | ||
| 41 | #include <netinet/udp.h> | ||
| 42 | #include <sys/types.h> | ||
| 43 | #include <sys/wait.h> | ||
| 44 | |||
| 45 | #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 | ||
| 46 | #include <netpacket/packet.h> | ||
| 47 | #include <net/ethernet.h> | ||
| 48 | #else | ||
| 49 | #include <asm/types.h> | ||
| 50 | #include <linux/if_packet.h> | ||
| 51 | #include <linux/if_ether.h> | ||
| 52 | #endif | ||
| 53 | #include "libbb.h" | ||
| 54 | |||
| 55 | static int state; | ||
| 56 | static unsigned long requested_ip; /* = 0 */ | ||
| 57 | static unsigned long server_addr; | ||
| 58 | static unsigned long timeout; | ||
| 59 | static int packet_num; /* = 0 */ | ||
| 60 | static int fd_main; | ||
| 61 | |||
| 62 | /* #define DEBUG */ | ||
| 63 | |||
| 64 | #define VERSION "0.9.7" | ||
| 65 | |||
| 66 | #define LISTEN_NONE 0 | ||
| 67 | #define LISTEN_KERNEL 1 | ||
| 68 | #define LISTEN_RAW 2 | ||
| 69 | static int listen_mode; | ||
| 70 | |||
| 71 | #define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" | ||
| 72 | |||
| 73 | #define DHCP_END 0xFF | ||
| 74 | #define TYPE_MASK 0x0F | ||
| 75 | #define BROADCAST_FLAG 0x8000 | ||
| 76 | |||
| 77 | #define SERVER_PORT 67 | ||
| 78 | |||
| 79 | #define DHCP_MAGIC 0x63825363 | ||
| 80 | |||
| 81 | #define BOOTREQUEST 1 | ||
| 82 | #define BOOTREPLY 2 | ||
| 83 | |||
| 84 | #define ETH_10MB 1 | ||
| 85 | #define ETH_10MB_LEN 6 | ||
| 86 | |||
| 87 | #define OPTION_FIELD 0 | ||
| 88 | #define FILE_FIELD 1 | ||
| 89 | #define SNAME_FIELD 2 | ||
| 90 | |||
| 91 | #define INIT_SELECTING 0 | ||
| 92 | #define REQUESTING 1 | ||
| 93 | #define BOUND 2 | ||
| 94 | #define RENEWING 3 | ||
| 95 | #define REBINDING 4 | ||
| 96 | #define INIT_REBOOT 5 | ||
| 97 | #define RENEW_REQUESTED 6 | ||
| 98 | #define RELEASED 7 | ||
| 99 | |||
| 100 | #define CLIENT_PORT 68 | ||
| 101 | |||
| 102 | #define DHCPDISCOVER 1 | ||
| 103 | #define DHCPOFFER 2 | ||
| 104 | #define DHCPREQUEST 3 | ||
| 105 | #define DHCPDECLINE 4 | ||
| 106 | #define DHCPACK 5 | ||
| 107 | #define DHCPNAK 6 | ||
| 108 | #define DHCPRELEASE 7 | ||
| 109 | #define DHCPINFORM 8 | ||
| 110 | |||
| 111 | /* DHCP option codes (partial list) */ | ||
| 112 | #define DHCP_PADDING 0x00 | ||
| 113 | #define DHCP_SUBNET 0x01 | ||
| 114 | #define DHCP_TIME_OFFSET 0x02 | ||
| 115 | #define DHCP_ROUTER 0x03 | ||
| 116 | #define DHCP_TIME_SERVER 0x04 | ||
| 117 | #define DHCP_NAME_SERVER 0x05 | ||
| 118 | #define DHCP_DNS_SERVER 0x06 | ||
| 119 | #define DHCP_LOG_SERVER 0x07 | ||
| 120 | #define DHCP_COOKIE_SERVER 0x08 | ||
| 121 | #define DHCP_LPR_SERVER 0x09 | ||
| 122 | #define DHCP_HOST_NAME 0x0c | ||
| 123 | #define DHCP_BOOT_SIZE 0x0d | ||
| 124 | #define DHCP_DOMAIN_NAME 0x0f | ||
| 125 | #define DHCP_SWAP_SERVER 0x10 | ||
| 126 | #define DHCP_ROOT_PATH 0x11 | ||
| 127 | #define DHCP_IP_TTL 0x17 | ||
| 128 | #define DHCP_MTU 0x1a | ||
| 129 | #define DHCP_BROADCAST 0x1c | ||
| 130 | #define DHCP_NTP_SERVER 0x2a | ||
| 131 | #define DHCP_WINS_SERVER 0x2c | ||
| 132 | #define DHCP_REQUESTED_IP 0x32 | ||
| 133 | #define DHCP_LEASE_TIME 0x33 | ||
| 134 | #define DHCP_OPTION_OVER 0x34 | ||
| 135 | #define DHCP_MESSAGE_TYPE 0x35 | ||
| 136 | #define DHCP_SERVER_ID 0x36 | ||
| 137 | #define DHCP_PARAM_REQ 0x37 | ||
| 138 | #define DHCP_MESSAGE 0x38 | ||
| 139 | #define DHCP_MAX_SIZE 0x39 | ||
| 140 | #define DHCP_T1 0x3a | ||
| 141 | #define DHCP_T2 0x3b | ||
| 142 | #define DHCP_VENDOR 0x3c | ||
| 143 | #define DHCP_CLIENT_ID 0x3d | ||
| 144 | |||
| 145 | /* miscellaneous defines */ | ||
| 146 | #define MAC_BCAST_ADDR (unsigned char *) "\xff\xff\xff\xff\xff\xff" | ||
| 147 | #define OPT_CODE 0 | ||
| 148 | #define OPT_LEN 1 | ||
| 149 | #define OPT_DATA 2 | ||
| 150 | |||
| 151 | enum { | ||
| 152 | OPTION_IP = 1, | ||
| 153 | OPTION_IP_PAIR, | ||
| 154 | OPTION_STRING, | ||
| 155 | OPTION_BOOLEAN, | ||
| 156 | OPTION_U8, | ||
| 157 | OPTION_U16, | ||
| 158 | OPTION_S16, | ||
| 159 | OPTION_U32, | ||
| 160 | OPTION_S32 | ||
| 161 | }; | ||
| 162 | |||
| 163 | #define OPTION_REQ 0x10 /* have the client request this option */ | ||
| 164 | #define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */ | ||
| 165 | |||
| 166 | #ifdef SYSLOG | ||
| 167 | # define LOG(level, str, args...) do { printf(str, ## args); \ | ||
| 168 | printf("\n"); \ | ||
| 169 | syslog(level, str, ## args); } while(0) | ||
| 170 | # define OPEN_LOG(name) openlog(name, 0, 0) | ||
| 171 | # define CLOSE_LOG() closelog() | ||
| 172 | #else | ||
| 173 | # define LOG_EMERG "EMERGENCY!" | ||
| 174 | # define LOG_ALERT "ALERT!" | ||
| 175 | # define LOG_CRIT "critical!" | ||
| 176 | # define LOG_WARNING "warning" | ||
| 177 | # define LOG_ERR "error" | ||
| 178 | # define LOG_INFO "info" | ||
| 179 | # define LOG_DEBUG "debug" | ||
| 180 | # define LOG(level, str, args...) do { printf("%s, " str "\n", level, ## args); } while(0) | ||
| 181 | # define OPEN_LOG(name) | ||
| 182 | # define CLOSE_LOG() | ||
| 183 | #endif | ||
| 184 | |||
| 185 | #ifdef DEBUG | ||
| 186 | # undef DEBUG | ||
| 187 | # define DEBUG(level, str, args...) LOG(level, str, ## args) | ||
| 188 | # define DEBUGGING | ||
| 189 | #else | ||
| 190 | # define DEBUG(level, str, args...) | ||
| 191 | #endif | ||
| 192 | |||
| 193 | struct dhcpMessage { | ||
| 194 | u_int8_t op; | ||
| 195 | u_int8_t htype; | ||
| 196 | u_int8_t hlen; | ||
| 197 | u_int8_t hops; | ||
| 198 | u_int32_t xid; | ||
| 199 | u_int16_t secs; | ||
| 200 | u_int16_t flags; | ||
| 201 | u_int32_t ciaddr; | ||
| 202 | u_int32_t yiaddr; | ||
| 203 | u_int32_t siaddr; | ||
| 204 | u_int32_t giaddr; | ||
| 205 | u_int8_t chaddr[16]; | ||
| 206 | u_int8_t sname[64]; | ||
| 207 | u_int8_t file[128]; | ||
| 208 | u_int32_t cookie; | ||
| 209 | u_int8_t options[308]; /* 312 - cookie */ | ||
| 210 | }; | ||
| 211 | |||
| 212 | struct client_config_t { | ||
| 213 | char foreground; /* Do not fork */ | ||
| 214 | char quit_after_lease; /* Quit after obtaining lease */ | ||
| 215 | char abort_if_no_lease; /* Abort if no lease */ | ||
| 216 | char *interface; /* The name of the interface to use */ | ||
| 217 | char *pidfile; /* Optionally store the process ID */ | ||
| 218 | char *script; /* User script to run at dhcp events */ | ||
| 219 | unsigned char *clientid; /* Optional client id to use */ | ||
| 220 | unsigned char *hostname; /* Optional hostname to use */ | ||
| 221 | int ifindex; /* Index number of the interface to use */ | ||
| 222 | unsigned char arp[6]; /* Our arp address */ | ||
| 223 | }; | ||
| 224 | |||
| 225 | struct client_config_t client_config = { | ||
| 226 | /* Default options. */ | ||
| 227 | abort_if_no_lease:0, | ||
| 228 | foreground:0, | ||
| 229 | quit_after_lease:0, | ||
| 230 | interface:"eth0", | ||
| 231 | pidfile:NULL, | ||
| 232 | script:DEFAULT_SCRIPT, | ||
| 233 | clientid:NULL, | ||
| 234 | hostname:NULL, | ||
| 235 | ifindex:0, | ||
| 236 | arp:"\0\0\0\0\0\0", /* appease gcc-3.0 */ | ||
| 237 | }; | ||
| 238 | |||
| 239 | struct dhcp_option { | ||
| 240 | char name[10]; | ||
| 241 | char flags; | ||
| 242 | unsigned char code; | ||
| 243 | }; | ||
| 244 | |||
| 245 | struct udp_dhcp_packet { | ||
| 246 | struct iphdr ip; | ||
| 247 | struct udphdr udp; | ||
| 248 | struct dhcpMessage data; | ||
| 249 | }; | ||
| 250 | |||
| 251 | static const struct dhcp_option options[] = { | ||
| 252 | /* name[10] flags code */ | ||
| 253 | {"subnet", OPTION_IP | OPTION_REQ, 0x01}, | ||
| 254 | {"timezone", OPTION_S32, 0x02}, | ||
| 255 | {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03}, | ||
| 256 | {"timesvr", OPTION_IP | OPTION_LIST, 0x04}, | ||
| 257 | {"namesvr", OPTION_IP | OPTION_LIST, 0x05}, | ||
| 258 | {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06}, | ||
| 259 | {"logsvr", OPTION_IP | OPTION_LIST, 0x07}, | ||
| 260 | {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08}, | ||
| 261 | {"lprsvr", OPTION_IP | OPTION_LIST, 0x09}, | ||
| 262 | {"hostname", OPTION_STRING | OPTION_REQ, 0x0c}, | ||
| 263 | {"bootsize", OPTION_U16, 0x0d}, | ||
| 264 | {"domain", OPTION_STRING | OPTION_REQ, 0x0f}, | ||
| 265 | {"swapsvr", OPTION_IP, 0x10}, | ||
| 266 | {"rootpath", OPTION_STRING, 0x11}, | ||
| 267 | {"ipttl", OPTION_U8, 0x17}, | ||
| 268 | {"mtu", OPTION_U16, 0x1a}, | ||
| 269 | {"broadcast", OPTION_IP | OPTION_REQ, 0x1c}, | ||
| 270 | {"ntpsrv", OPTION_IP | OPTION_LIST, 0x2a}, | ||
| 271 | {"wins", OPTION_IP | OPTION_LIST, 0x2c}, | ||
| 272 | {"requestip", OPTION_IP, 0x32}, | ||
| 273 | {"lease", OPTION_U32, 0x33}, | ||
| 274 | {"dhcptype", OPTION_U8, 0x35}, | ||
| 275 | {"serverid", OPTION_IP, 0x36}, | ||
| 276 | {"message", OPTION_STRING, 0x38}, | ||
| 277 | {"tftp", OPTION_STRING, 0x42}, | ||
| 278 | {"bootfile", OPTION_STRING, 0x43}, | ||
| 279 | {"", 0x00, 0x00} | ||
| 280 | }; | ||
| 281 | |||
| 282 | /* Lengths of the different option types */ | ||
| 283 | static const unsigned char option_lengths[] = { | ||
| 284 | [OPTION_IP] = 4, | ||
| 285 | [OPTION_IP_PAIR] = 8, | ||
| 286 | [OPTION_BOOLEAN] = 1, | ||
| 287 | [OPTION_STRING] = 1, | ||
| 288 | [OPTION_U8] = 1, | ||
| 289 | [OPTION_U16] = 2, | ||
| 290 | [OPTION_S16] = 2, | ||
| 291 | [OPTION_U32] = 4, | ||
| 292 | [OPTION_S32] = 4 | ||
| 293 | }; | ||
| 294 | |||
| 295 | /* get a rough idea of how long an option will be (rounding up...) */ | ||
| 296 | static const unsigned char max_option_length[] = { | ||
| 297 | [OPTION_IP] = sizeof("255.255.255.255 "), | ||
| 298 | [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, | ||
| 299 | [OPTION_STRING] = 1, | ||
| 300 | [OPTION_BOOLEAN] = sizeof("yes "), | ||
| 301 | [OPTION_U8] = sizeof("255 "), | ||
| 302 | [OPTION_U16] = sizeof("65535 "), | ||
| 303 | [OPTION_S16] = sizeof("-32768 "), | ||
| 304 | [OPTION_U32] = sizeof("4294967295 "), | ||
| 305 | [OPTION_S32] = sizeof("-2147483684 "), | ||
| 306 | }; | ||
| 307 | |||
| 308 | /* return the position of the 'end' option (no bounds checking) */ | ||
| 309 | static int end_option(unsigned char *optionptr) | ||
| 310 | { | ||
| 311 | int i = 0; | ||
| 312 | |||
| 313 | while (optionptr[i] != DHCP_END) { | ||
| 314 | if (optionptr[i] == DHCP_PADDING) | ||
| 315 | i++; | ||
| 316 | else | ||
| 317 | i += optionptr[i + OPT_LEN] + 2; | ||
| 318 | } | ||
| 319 | return i; | ||
| 320 | } | ||
| 321 | |||
| 322 | /* add an option string to the options (an option string contains an option code, | ||
| 323 | * length, then data) */ | ||
| 324 | static int add_option_string(unsigned char *optionptr, unsigned char *string) | ||
| 325 | { | ||
| 326 | int end = end_option(optionptr); | ||
| 327 | |||
| 328 | /* end position + string length + option code/length + end option */ | ||
| 329 | if (end + string[OPT_LEN] + 2 + 1 >= 308) { | ||
| 330 | LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", | ||
| 331 | string[OPT_CODE]); | ||
| 332 | return 0; | ||
| 333 | } | ||
| 334 | DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]); | ||
| 335 | memcpy(optionptr + end, string, string[OPT_LEN] + 2); | ||
| 336 | optionptr[end + string[OPT_LEN] + 2] = DHCP_END; | ||
| 337 | return string[OPT_LEN] + 2; | ||
| 338 | } | ||
| 339 | |||
| 340 | /* add a one to four byte option to a packet */ | ||
| 341 | static int add_simple_option(unsigned char *optionptr, unsigned char code, | ||
| 342 | u_int32_t data) | ||
| 343 | { | ||
| 344 | char length = 0; | ||
| 345 | int i; | ||
| 346 | unsigned char option[2 + 4]; | ||
| 347 | unsigned char *u8; | ||
| 348 | u_int16_t *u16; | ||
| 349 | u_int32_t *u32; | ||
| 350 | u_int32_t aligned; | ||
| 351 | |||
| 352 | u8 = (unsigned char *) &aligned; | ||
| 353 | u16 = (u_int16_t *) & aligned; | ||
| 354 | u32 = &aligned; | ||
| 355 | |||
| 356 | for (i = 0; options[i].code; i++) | ||
| 357 | if (options[i].code == code) { | ||
| 358 | length = option_lengths[options[i].flags & TYPE_MASK]; | ||
| 359 | } | ||
| 360 | |||
| 361 | if (!length) { | ||
| 362 | DEBUG(LOG_ERR, "Could not add option 0x%02x", code); | ||
| 363 | return 0; | ||
| 364 | } | ||
| 365 | |||
| 366 | option[OPT_CODE] = code; | ||
| 367 | option[OPT_LEN] = length; | ||
| 368 | |||
| 369 | switch (length) { | ||
| 370 | case 1: | ||
| 371 | *u8 = data; | ||
| 372 | break; | ||
| 373 | case 2: | ||
| 374 | *u16 = data; | ||
| 375 | break; | ||
| 376 | case 4: | ||
| 377 | *u32 = data; | ||
| 378 | break; | ||
| 379 | } | ||
| 380 | |||
| 381 | memcpy(option + 2, &aligned, length); | ||
| 382 | return add_option_string(optionptr, option); | ||
| 383 | } | ||
| 384 | |||
| 385 | static u_int16_t checksum(void *addr, int count) | ||
| 386 | { | ||
| 387 | /* Compute Internet Checksum for "count" bytes | ||
| 388 | * beginning at location "addr". | ||
| 389 | */ | ||
| 390 | register int32_t sum = 0; | ||
| 391 | u_int16_t *source = (u_int16_t *) addr; | ||
| 392 | |||
| 393 | while (count > 1) { | ||
| 394 | /* This is the inner loop */ | ||
| 395 | sum += *source++; | ||
| 396 | count -= 2; | ||
| 397 | } | ||
| 398 | |||
| 399 | /* Add left-over byte, if any */ | ||
| 400 | if (count > 0) { | ||
| 401 | sum += *(unsigned char *) source; | ||
| 402 | } | ||
| 403 | |||
| 404 | /* Fold 32-bit sum to 16 bits */ | ||
| 405 | while (sum >> 16) { | ||
| 406 | sum = (sum & 0xffff) + (sum >> 16); | ||
| 407 | } | ||
| 408 | |||
| 409 | return ~sum; | ||
| 410 | } | ||
| 411 | |||
| 412 | /* Constuct a ip/udp header for a packet, and specify the source and dest hardware address */ | ||
| 413 | static int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, | ||
| 414 | int source_port, u_int32_t dest_ip, int dest_port, | ||
| 415 | unsigned char *dest_arp, int ifindex) | ||
| 416 | { | ||
| 417 | int l_fd; | ||
| 418 | int result; | ||
| 419 | struct sockaddr_ll dest; | ||
| 420 | struct udp_dhcp_packet packet; | ||
| 421 | |||
| 422 | if ((l_fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { | ||
| 423 | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); | ||
| 424 | return -1; | ||
| 425 | } | ||
| 426 | |||
| 427 | memset(&dest, 0, sizeof(dest)); | ||
| 428 | memset(&packet, 0, sizeof(packet)); | ||
| 429 | |||
| 430 | dest.sll_family = AF_PACKET; | ||
| 431 | dest.sll_protocol = htons(ETH_P_IP); | ||
| 432 | dest.sll_ifindex = ifindex; | ||
| 433 | dest.sll_halen = 6; | ||
| 434 | memcpy(dest.sll_addr, dest_arp, 6); | ||
| 435 | if (bind(l_fd, (struct sockaddr *) &dest, sizeof(struct sockaddr_ll)) < 0) { | ||
| 436 | DEBUG(LOG_ERR, "bind call failed: %s", strerror(errno)); | ||
| 437 | close(l_fd); | ||
| 438 | return -1; | ||
| 439 | } | ||
| 440 | |||
| 441 | packet.ip.protocol = IPPROTO_UDP; | ||
| 442 | packet.ip.saddr = source_ip; | ||
| 443 | packet.ip.daddr = dest_ip; | ||
| 444 | packet.udp.source = htons(source_port); | ||
| 445 | packet.udp.dest = htons(dest_port); | ||
| 446 | packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */ | ||
| 447 | packet.ip.tot_len = packet.udp.len; | ||
| 448 | memcpy(&(packet.data), payload, sizeof(struct dhcpMessage)); | ||
| 449 | packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet)); | ||
| 450 | |||
| 451 | packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet)); | ||
| 452 | packet.ip.ihl = sizeof(packet.ip) >> 2; | ||
| 453 | packet.ip.version = IPVERSION; | ||
| 454 | packet.ip.ttl = IPDEFTTL; | ||
| 455 | packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip)); | ||
| 456 | |||
| 457 | result = | ||
| 458 | sendto(l_fd, &packet, sizeof(struct udp_dhcp_packet), 0, | ||
| 459 | (struct sockaddr *) &dest, sizeof(dest)); | ||
| 460 | if (result <= 0) { | ||
| 461 | DEBUG(LOG_ERR, "write on socket failed: %s", strerror(errno)); | ||
| 462 | } | ||
| 463 | close(l_fd); | ||
| 464 | return result; | ||
| 465 | } | ||
| 466 | |||
| 467 | /* Let the kernel do all the work for packet generation */ | ||
| 468 | static int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, | ||
| 469 | int source_port, u_int32_t dest_ip, int dest_port) | ||
| 470 | { | ||
| 471 | int n = 1; | ||
| 472 | int l_fd, result; | ||
| 473 | struct sockaddr_in client; | ||
| 474 | |||
| 475 | if ((l_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { | ||
| 476 | return -1; | ||
| 477 | } | ||
| 478 | |||
| 479 | if (setsockopt(l_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == | ||
| 480 | -1) { | ||
| 481 | return -1; | ||
| 482 | } | ||
| 483 | |||
| 484 | memset(&client, 0, sizeof(client)); | ||
| 485 | client.sin_family = AF_INET; | ||
| 486 | client.sin_port = htons(source_port); | ||
| 487 | client.sin_addr.s_addr = source_ip; | ||
| 488 | |||
| 489 | if (bind(l_fd, (struct sockaddr *) &client, sizeof(struct sockaddr)) == | ||
| 490 | -1) { | ||
| 491 | return -1; | ||
| 492 | } | ||
| 493 | |||
| 494 | memset(&client, 0, sizeof(client)); | ||
| 495 | client.sin_family = AF_INET; | ||
| 496 | client.sin_port = htons(dest_port); | ||
| 497 | client.sin_addr.s_addr = dest_ip; | ||
| 498 | |||
| 499 | if (connect(l_fd, (struct sockaddr *) &client, sizeof(struct sockaddr)) == | ||
| 500 | -1) { | ||
| 501 | return -1; | ||
| 502 | } | ||
| 503 | |||
| 504 | result = write(l_fd, payload, sizeof(struct dhcpMessage)); | ||
| 505 | close(l_fd); | ||
| 506 | return result; | ||
| 507 | } | ||
| 508 | |||
| 509 | /* initialize a packet with the proper defaults */ | ||
| 510 | static void init_packet(struct dhcpMessage *packet, char type) | ||
| 511 | { | ||
| 512 | struct vendor { | ||
| 513 | char vendor, length; | ||
| 514 | char str[sizeof("udhcp " VERSION)]; | ||
| 515 | } | ||
| 516 | vendor_id = { | ||
| 517 | DHCP_VENDOR, sizeof("udhcp " VERSION) - 1, "udhcp " VERSION}; | ||
| 518 | |||
| 519 | memset(packet, 0, sizeof(struct dhcpMessage)); | ||
| 520 | switch (type) { | ||
| 521 | case DHCPDISCOVER: | ||
| 522 | case DHCPREQUEST: | ||
| 523 | case DHCPRELEASE: | ||
| 524 | case DHCPINFORM: | ||
| 525 | packet->op = BOOTREQUEST; | ||
| 526 | break; | ||
| 527 | case DHCPOFFER: | ||
| 528 | case DHCPACK: | ||
| 529 | case DHCPNAK: | ||
| 530 | packet->op = BOOTREPLY; | ||
| 531 | } | ||
| 532 | packet->htype = ETH_10MB; | ||
| 533 | packet->hlen = ETH_10MB_LEN; | ||
| 534 | packet->cookie = htonl(DHCP_MAGIC); | ||
| 535 | packet->options[0] = DHCP_END; | ||
| 536 | add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type); | ||
| 537 | |||
| 538 | memcpy(packet->chaddr, client_config.arp, 6); | ||
| 539 | add_option_string(packet->options, client_config.clientid); | ||
| 540 | if (client_config.hostname) { | ||
| 541 | add_option_string(packet->options, client_config.hostname); | ||
| 542 | } | ||
| 543 | add_option_string(packet->options, (unsigned char *) &vendor_id); | ||
| 544 | } | ||
| 545 | |||
| 546 | |||
| 547 | /* Add a paramater request list for stubborn DHCP servers. Pull the data | ||
| 548 | * from the struct in options.c. Don't do bounds checking here because it | ||
| 549 | * goes towards the head of the packet. */ | ||
| 550 | static void add_requests(struct dhcpMessage *packet) | ||
| 551 | { | ||
| 552 | int end = end_option(packet->options); | ||
| 553 | int i, len = 0; | ||
| 554 | |||
| 555 | packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; | ||
| 556 | for (i = 0; options[i].code; i++) { | ||
| 557 | if (options[i].flags & OPTION_REQ) { | ||
| 558 | packet->options[end + OPT_DATA + len++] = options[i].code; | ||
| 559 | } | ||
| 560 | } | ||
| 561 | packet->options[end + OPT_LEN] = len; | ||
| 562 | packet->options[end + OPT_DATA + len] = DHCP_END; | ||
| 563 | |||
| 564 | } | ||
| 565 | |||
| 566 | /* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ | ||
| 567 | static inline int send_discover(unsigned long xid, unsigned long requested) | ||
| 568 | { | ||
| 569 | struct dhcpMessage packet; | ||
| 570 | |||
| 571 | init_packet(&packet, DHCPDISCOVER); | ||
| 572 | packet.xid = xid; | ||
| 573 | if (requested) { | ||
| 574 | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); | ||
| 575 | } | ||
| 576 | add_requests(&packet); | ||
| 577 | DEBUG(LOG_DEBUG, "Sending discover..."); | ||
| 578 | return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, | ||
| 579 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); | ||
| 580 | } | ||
| 581 | |||
| 582 | /* Broadcasts a DHCP request message */ | ||
| 583 | static inline int send_selecting(unsigned long xid, unsigned long server, | ||
| 584 | unsigned long requested) | ||
| 585 | { | ||
| 586 | struct dhcpMessage packet; | ||
| 587 | struct in_addr addr; | ||
| 588 | |||
| 589 | init_packet(&packet, DHCPREQUEST); | ||
| 590 | packet.xid = xid; | ||
| 591 | |||
| 592 | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); | ||
| 593 | add_simple_option(packet.options, DHCP_SERVER_ID, server); | ||
| 594 | |||
| 595 | add_requests(&packet); | ||
| 596 | addr.s_addr = requested; | ||
| 597 | DEBUG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr)); | ||
| 598 | return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, | ||
| 599 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); | ||
| 600 | } | ||
| 601 | |||
| 602 | |||
| 603 | /* Unicasts or broadcasts a DHCP renew message */ | ||
| 604 | static int send_renew(unsigned long xid, unsigned long server, | ||
| 605 | unsigned long ciaddr) | ||
| 606 | { | ||
| 607 | struct dhcpMessage packet; | ||
| 608 | |||
| 609 | init_packet(&packet, DHCPREQUEST); | ||
| 610 | packet.xid = xid; | ||
| 611 | packet.ciaddr = ciaddr; | ||
| 612 | |||
| 613 | add_requests(&packet); | ||
| 614 | DEBUG(LOG_DEBUG, "Sending renew..."); | ||
| 615 | if (server) { | ||
| 616 | return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, | ||
| 617 | SERVER_PORT); | ||
| 618 | } | ||
| 619 | return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, | ||
| 620 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); | ||
| 621 | } | ||
| 622 | |||
| 623 | /* Create a random xid */ | ||
| 624 | static unsigned long random_xid(void) | ||
| 625 | { | ||
| 626 | static int initialized; | ||
| 627 | |||
| 628 | if (!initialized) { | ||
| 629 | srand(time(0)); | ||
| 630 | initialized++; | ||
| 631 | } | ||
| 632 | return rand(); | ||
| 633 | } | ||
| 634 | |||
| 635 | /* just a little helper */ | ||
| 636 | static void change_mode(int new_mode) | ||
| 637 | { | ||
| 638 | DEBUG(LOG_INFO, "entering %s listen mode", | ||
| 639 | new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); | ||
| 640 | close(fd_main); | ||
| 641 | fd_main = -1; | ||
| 642 | listen_mode = new_mode; | ||
| 643 | } | ||
| 644 | |||
| 645 | |||
| 646 | /* SIGUSR1 handler (renew) */ | ||
| 647 | static void renew_requested(int sig) | ||
| 648 | { | ||
| 649 | sig = 0; | ||
| 650 | LOG(LOG_INFO, "Received SIGUSR1"); | ||
| 651 | if (state == BOUND || state == RENEWING || state == REBINDING || | ||
| 652 | state == RELEASED) { | ||
| 653 | change_mode(LISTEN_KERNEL); | ||
| 654 | packet_num = 0; | ||
| 655 | state = RENEW_REQUESTED; | ||
| 656 | } | ||
| 657 | |||
| 658 | if (state == RELEASED) { | ||
| 659 | change_mode(LISTEN_RAW); | ||
| 660 | state = INIT_SELECTING; | ||
| 661 | } | ||
| 662 | |||
| 663 | /* Kill any timeouts because the user wants this to hurry along */ | ||
| 664 | timeout = 0; | ||
| 665 | } | ||
| 666 | |||
| 667 | /* get an option with bounds checking (warning, not aligned). */ | ||
| 668 | static unsigned char *get_option(struct dhcpMessage *packet, int code) | ||
| 669 | { | ||
| 670 | int i, length; | ||
| 671 | unsigned char *optionptr; | ||
| 672 | int over = 0, done = 0, curr = OPTION_FIELD; | ||
| 673 | |||
| 674 | optionptr = packet->options; | ||
| 675 | i = 0; | ||
| 676 | length = 308; | ||
| 677 | while (!done) { | ||
| 678 | if (i >= length) { | ||
| 679 | LOG(LOG_WARNING, "bogus packet, option fields too long."); | ||
| 680 | return NULL; | ||
| 681 | } | ||
| 682 | if (optionptr[i + OPT_CODE] == code) { | ||
| 683 | if (i + 1 + optionptr[i + OPT_LEN] >= length) { | ||
| 684 | LOG(LOG_WARNING, "bogus packet, option fields too long."); | ||
| 685 | return NULL; | ||
| 686 | } | ||
| 687 | return optionptr + i + 2; | ||
| 688 | } | ||
| 689 | switch (optionptr[i + OPT_CODE]) { | ||
| 690 | case DHCP_PADDING: | ||
| 691 | i++; | ||
| 692 | break; | ||
| 693 | case DHCP_OPTION_OVER: | ||
| 694 | if (i + 1 + optionptr[i + OPT_LEN] >= length) { | ||
| 695 | LOG(LOG_WARNING, "bogus packet, option fields too long."); | ||
| 696 | return NULL; | ||
| 697 | } | ||
| 698 | over = optionptr[i + 3]; | ||
| 699 | i += optionptr[OPT_LEN] + 2; | ||
| 700 | break; | ||
| 701 | case DHCP_END: | ||
| 702 | if (curr == OPTION_FIELD && over & FILE_FIELD) { | ||
| 703 | optionptr = packet->file; | ||
| 704 | i = 0; | ||
| 705 | length = 128; | ||
| 706 | curr = FILE_FIELD; | ||
| 707 | } else if (curr == FILE_FIELD && over & SNAME_FIELD) { | ||
| 708 | optionptr = packet->sname; | ||
| 709 | i = 0; | ||
| 710 | length = 64; | ||
| 711 | curr = SNAME_FIELD; | ||
| 712 | } else { | ||
| 713 | done = 1; | ||
| 714 | } | ||
| 715 | break; | ||
| 716 | default: | ||
| 717 | i += optionptr[OPT_LEN + i] + 2; | ||
| 718 | } | ||
| 719 | } | ||
| 720 | return NULL; | ||
| 721 | } | ||
| 722 | |||
| 723 | static int sprintip(char *dest, char *pre, unsigned char *ip) | ||
| 724 | { | ||
| 725 | return sprintf(dest, "%s%d.%d.%d.%d ", pre, ip[0], ip[1], ip[2], ip[3]); | ||
| 726 | } | ||
| 727 | |||
| 728 | |||
| 729 | /* Fill dest with the text of option 'option'. */ | ||
| 730 | static inline void fill_options(char *dest, unsigned char *option, | ||
| 731 | const struct dhcp_option *type_p) | ||
| 732 | { | ||
| 733 | int type, optlen; | ||
| 734 | u_int16_t val_u16; | ||
| 735 | int16_t val_s16; | ||
| 736 | u_int32_t val_u32; | ||
| 737 | int32_t val_s32; | ||
| 738 | int len = option[OPT_LEN - 2]; | ||
| 739 | |||
| 740 | dest += sprintf(dest, "%s=", type_p->name); | ||
| 741 | |||
| 742 | type = type_p->flags & TYPE_MASK; | ||
| 743 | optlen = option_lengths[type]; | ||
| 744 | for (;;) { | ||
| 745 | switch (type) { | ||
| 746 | case OPTION_IP_PAIR: | ||
| 747 | dest += sprintip(dest, "", option); | ||
| 748 | *(dest++) = '/'; | ||
| 749 | option += 4; | ||
| 750 | optlen = 4; | ||
| 751 | case OPTION_IP: /* Works regardless of host byte order. */ | ||
| 752 | dest += sprintip(dest, "", option); | ||
| 753 | break; | ||
| 754 | case OPTION_BOOLEAN: | ||
| 755 | dest += sprintf(dest, *option ? "yes " : "no "); | ||
| 756 | break; | ||
| 757 | case OPTION_U8: | ||
| 758 | dest += sprintf(dest, "%u ", *option); | ||
| 759 | break; | ||
| 760 | case OPTION_U16: | ||
| 761 | memcpy(&val_u16, option, 2); | ||
| 762 | dest += sprintf(dest, "%u ", ntohs(val_u16)); | ||
| 763 | break; | ||
| 764 | case OPTION_S16: | ||
| 765 | memcpy(&val_s16, option, 2); | ||
| 766 | dest += sprintf(dest, "%d ", ntohs(val_s16)); | ||
| 767 | break; | ||
| 768 | case OPTION_U32: | ||
| 769 | memcpy(&val_u32, option, 4); | ||
| 770 | dest += sprintf(dest, "%lu ", (unsigned long) ntohl(val_u32)); | ||
| 771 | break; | ||
| 772 | case OPTION_S32: | ||
| 773 | memcpy(&val_s32, option, 4); | ||
| 774 | dest += sprintf(dest, "%ld ", (long) ntohl(val_s32)); | ||
| 775 | break; | ||
| 776 | case OPTION_STRING: | ||
| 777 | memcpy(dest, option, len); | ||
| 778 | dest[len] = '\0'; | ||
| 779 | return; /* Short circuit this case */ | ||
| 780 | } | ||
| 781 | option += optlen; | ||
| 782 | len -= optlen; | ||
| 783 | if (len <= 0) { | ||
| 784 | break; | ||
| 785 | } | ||
| 786 | } | ||
| 787 | } | ||
| 788 | |||
| 789 | static char *find_env(const char *prefix, char *defaultstr) | ||
| 790 | { | ||
| 791 | extern char **environ; | ||
| 792 | char **ptr; | ||
| 793 | const int len = strlen(prefix); | ||
| 794 | |||
| 795 | for (ptr = environ; *ptr != NULL; ptr++) { | ||
| 796 | if (strncmp(prefix, *ptr, len) == 0) { | ||
| 797 | return *ptr; | ||
| 798 | } | ||
| 799 | } | ||
| 800 | return defaultstr; | ||
| 801 | } | ||
| 802 | |||
| 803 | /* put all the paramaters into an environment */ | ||
| 804 | static char **fill_envp(struct dhcpMessage *packet) | ||
| 805 | { | ||
| 806 | /* supported options are easily added here */ | ||
| 807 | int num_options = 0; | ||
| 808 | int i, j; | ||
| 809 | char **envp; | ||
| 810 | unsigned char *temp; | ||
| 811 | char over = 0; | ||
| 812 | |||
| 813 | if (packet == NULL) { | ||
| 814 | num_options = 0; | ||
| 815 | } else { | ||
| 816 | for (i = 0; options[i].code; i++) { | ||
| 817 | if (get_option(packet, options[i].code)) { | ||
| 818 | num_options++; | ||
| 819 | } | ||
| 820 | } | ||
| 821 | if (packet->siaddr) { | ||
| 822 | num_options++; | ||
| 823 | } | ||
| 824 | if ((temp = get_option(packet, DHCP_OPTION_OVER))) { | ||
| 825 | over = *temp; | ||
| 826 | } | ||
| 827 | if (!(over & FILE_FIELD) && packet->file[0]) { | ||
| 828 | num_options++; | ||
| 829 | } | ||
| 830 | if (!(over & SNAME_FIELD) && packet->sname[0]) { | ||
| 831 | num_options++; | ||
| 832 | } | ||
| 833 | } | ||
| 834 | |||
| 835 | envp = xmalloc((num_options + 5) * sizeof(char *)); | ||
| 836 | envp[0] = xmalloc(sizeof("interface=") + strlen(client_config.interface)); | ||
| 837 | sprintf(envp[0], "interface=%s", client_config.interface); | ||
| 838 | envp[1] = find_env("PATH", "PATH=/bin:/usr/bin:/sbin:/usr/sbin"); | ||
| 839 | envp[2] = find_env("HOME", "HOME=/"); | ||
| 840 | |||
| 841 | if (packet == NULL) { | ||
| 842 | envp[3] = NULL; | ||
| 843 | return envp; | ||
| 844 | } | ||
| 845 | |||
| 846 | envp[3] = xmalloc(sizeof("ip=255.255.255.255")); | ||
| 847 | sprintip(envp[3], "ip=", (unsigned char *) &packet->yiaddr); | ||
| 848 | for (i = 0, j = 4; options[i].code; i++) { | ||
| 849 | if ((temp = get_option(packet, options[i].code))) { | ||
| 850 | envp[j] = | ||
| 851 | xmalloc(max_option_length[(&options[i])->flags & TYPE_MASK] * | ||
| 852 | (temp[OPT_LEN - 2] / | ||
| 853 | option_lengths[(&options[i])->flags & TYPE_MASK]) + | ||
| 854 | strlen((&options[i])->name) + 2); | ||
| 855 | fill_options(envp[j], temp, &options[i]); | ||
| 856 | j++; | ||
| 857 | } | ||
| 858 | } | ||
| 859 | if (packet->siaddr) { | ||
| 860 | envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); | ||
| 861 | sprintip(envp[j++], "siaddr=", (unsigned char *) &packet->yiaddr); | ||
| 862 | } | ||
| 863 | if (!(over & FILE_FIELD) && packet->file[0]) { | ||
| 864 | /* watch out for invalid packets */ | ||
| 865 | packet->file[sizeof(packet->file) - 1] = '\0'; | ||
| 866 | envp[j] = xmalloc(sizeof("boot_file=") + strlen(packet->file)); | ||
| 867 | sprintf(envp[j++], "boot_file=%s", packet->file); | ||
| 868 | } | ||
| 869 | if (!(over & SNAME_FIELD) && packet->sname[0]) { | ||
| 870 | /* watch out for invalid packets */ | ||
| 871 | packet->sname[sizeof(packet->sname) - 1] = '\0'; | ||
| 872 | envp[j] = xmalloc(sizeof("sname=") + strlen(packet->sname)); | ||
| 873 | sprintf(envp[j++], "sname=%s", packet->sname); | ||
| 874 | } | ||
| 875 | envp[j] = NULL; | ||
| 876 | return envp; | ||
| 877 | } | ||
| 878 | |||
| 879 | /* Call a script with a par file and env vars */ | ||
| 880 | static void run_script(struct dhcpMessage *packet, const char *name) | ||
| 881 | { | ||
| 882 | int pid; | ||
| 883 | char **envp; | ||
| 884 | |||
| 885 | if (client_config.script == NULL) { | ||
| 886 | return; | ||
| 887 | } | ||
| 888 | |||
| 889 | /* call script */ | ||
| 890 | pid = fork(); | ||
| 891 | if (pid) { | ||
| 892 | waitpid(pid, NULL, 0); | ||
| 893 | return; | ||
| 894 | } else if (pid == 0) { | ||
| 895 | envp = fill_envp(packet); | ||
| 896 | |||
| 897 | /* close fd's? */ | ||
| 898 | |||
| 899 | /* exec script */ | ||
| 900 | DEBUG(LOG_INFO, "execle'ing %s", client_config.script); | ||
| 901 | execle(client_config.script, client_config.script, name, NULL, envp); | ||
| 902 | LOG(LOG_ERR, "script %s failed: %s", | ||
| 903 | client_config.script, strerror(errno)); | ||
| 904 | exit(1); | ||
| 905 | } | ||
| 906 | } | ||
| 907 | |||
| 908 | /* SIGUSR2 handler (release) */ | ||
| 909 | static void release_requested(int sig) | ||
| 910 | { | ||
| 911 | sig = 0; | ||
| 912 | LOG(LOG_INFO, "Received SIGUSR2"); | ||
| 913 | /* send release packet */ | ||
| 914 | if (state == BOUND || state == RENEWING || state == REBINDING) { | ||
| 915 | struct dhcpMessage packet; | ||
| 916 | |||
| 917 | init_packet(&packet, DHCPRELEASE); | ||
| 918 | packet.xid = random_xid(); | ||
| 919 | packet.ciaddr = requested_ip; | ||
| 920 | |||
| 921 | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested_ip); | ||
| 922 | add_simple_option(packet.options, DHCP_SERVER_ID, server_addr); | ||
| 923 | |||
| 924 | DEBUG(LOG_DEBUG, "Sending release..."); | ||
| 925 | kernel_packet(&packet, requested_ip, CLIENT_PORT, server_addr, | ||
| 926 | SERVER_PORT); | ||
| 927 | run_script(NULL, "deconfig"); | ||
| 928 | } | ||
| 929 | |||
| 930 | change_mode(LISTEN_NONE); | ||
| 931 | state = RELEASED; | ||
| 932 | timeout = 0x7fffffff; | ||
| 933 | } | ||
| 934 | |||
| 935 | |||
| 936 | static int pidfile_acquire(char *pidfile) | ||
| 937 | { | ||
| 938 | int pid_fd; | ||
| 939 | |||
| 940 | if (pidfile == NULL) { | ||
| 941 | return -1; | ||
| 942 | } | ||
| 943 | pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644); | ||
| 944 | if (pid_fd < 0) { | ||
| 945 | LOG(LOG_ERR, "Unable to open pidfile %s: %s\n", | ||
| 946 | pidfile, strerror(errno)); | ||
| 947 | } else { | ||
| 948 | lockf(pid_fd, F_LOCK, 0); | ||
| 949 | } | ||
| 950 | |||
| 951 | return pid_fd; | ||
| 952 | } | ||
| 953 | |||
| 954 | |||
| 955 | static void pidfile_write_release(int pid_fd) | ||
| 956 | { | ||
| 957 | FILE *out; | ||
| 958 | |||
| 959 | if (pid_fd < 0) { | ||
| 960 | return; | ||
| 961 | } | ||
| 962 | if ((out = fdopen(pid_fd, "w")) != NULL) { | ||
| 963 | fprintf(out, "%d\n", getpid()); | ||
| 964 | fclose(out); | ||
| 965 | } | ||
| 966 | lockf(pid_fd, F_UNLCK, 0); | ||
| 967 | close(pid_fd); | ||
| 968 | } | ||
| 969 | |||
| 970 | /* Exit and cleanup */ | ||
| 971 | static void exit_client(int retval) | ||
| 972 | { | ||
| 973 | unlink(client_config.pidfile); | ||
| 974 | if (client_config.pidfile) { | ||
| 975 | unlink(client_config.pidfile); | ||
| 976 | } | ||
| 977 | CLOSE_LOG(); | ||
| 978 | exit(retval); | ||
| 979 | } | ||
| 980 | |||
| 981 | |||
| 982 | /* SIGTERM handler */ | ||
| 983 | static void terminate(int sig) | ||
| 984 | { | ||
| 985 | sig = 0; | ||
| 986 | LOG(LOG_INFO, "Received SIGTERM"); | ||
| 987 | exit_client(0); | ||
| 988 | } | ||
| 989 | |||
| 990 | |||
| 991 | static inline int read_interface(char *interface, int *ifindex, | ||
| 992 | u_int32_t * addr, unsigned char *arp) | ||
| 993 | { | ||
| 994 | int l_fd; | ||
| 995 | struct ifreq ifr; | ||
| 996 | struct sockaddr_in *s_in; | ||
| 997 | |||
| 998 | memset(&ifr, 0, sizeof(struct ifreq)); | ||
| 999 | if ((l_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) { | ||
| 1000 | ifr.ifr_addr.sa_family = AF_INET; | ||
| 1001 | strcpy(ifr.ifr_name, interface); | ||
| 1002 | |||
| 1003 | if (addr) { | ||
| 1004 | if (ioctl(l_fd, SIOCGIFADDR, &ifr) == 0) { | ||
| 1005 | s_in = (struct sockaddr_in *) &ifr.ifr_addr; | ||
| 1006 | *addr = s_in->sin_addr.s_addr; | ||
| 1007 | DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, | ||
| 1008 | inet_ntoa(s_in->sin_addr)); | ||
| 1009 | } else { | ||
| 1010 | LOG(LOG_ERR, "SIOCGIFADDR failed!: %s", strerror(errno)); | ||
| 1011 | return -1; | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | if (ioctl(l_fd, SIOCGIFINDEX, &ifr) == 0) { | ||
| 1016 | DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex); | ||
| 1017 | *ifindex = ifr.ifr_ifindex; | ||
| 1018 | } else { | ||
| 1019 | LOG(LOG_ERR, "SIOCGIFINDEX failed!: %s", strerror(errno)); | ||
| 1020 | return -1; | ||
| 1021 | } | ||
| 1022 | if (ioctl(l_fd, SIOCGIFHWADDR, &ifr) == 0) { | ||
| 1023 | memcpy(arp, ifr.ifr_hwaddr.sa_data, 6); | ||
| 1024 | DEBUG(LOG_INFO, | ||
| 1025 | "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", | ||
| 1026 | arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]); | ||
| 1027 | } else { | ||
| 1028 | LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %s", strerror(errno)); | ||
| 1029 | return -1; | ||
| 1030 | } | ||
| 1031 | } else { | ||
| 1032 | LOG(LOG_ERR, "socket failed!: %s", strerror(errno)); | ||
| 1033 | return -1; | ||
| 1034 | } | ||
| 1035 | close(l_fd); | ||
| 1036 | return 0; | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | |||
| 1040 | static inline int listen_socket(unsigned int ip, int port, char *inf) | ||
| 1041 | { | ||
| 1042 | struct ifreq interface; | ||
| 1043 | int l_fd; | ||
| 1044 | struct sockaddr_in addr; | ||
| 1045 | int n = 1; | ||
| 1046 | |||
| 1047 | DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s\n", ip, port, inf); | ||
| 1048 | if ((l_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { | ||
| 1049 | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); | ||
| 1050 | return -1; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | memset(&addr, 0, sizeof(addr)); | ||
| 1054 | addr.sin_family = AF_INET; | ||
| 1055 | addr.sin_port = htons(port); | ||
| 1056 | addr.sin_addr.s_addr = ip; | ||
| 1057 | |||
| 1058 | if (setsockopt(l_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == | ||
| 1059 | -1) { | ||
| 1060 | close(l_fd); | ||
| 1061 | return -1; | ||
| 1062 | } | ||
| 1063 | if (setsockopt(l_fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == | ||
| 1064 | -1) { | ||
| 1065 | close(l_fd); | ||
| 1066 | return -1; | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ); | ||
| 1070 | if (setsockopt | ||
| 1071 | (l_fd, SOL_SOCKET, SO_BINDTODEVICE, (char *) &interface, | ||
| 1072 | sizeof(interface)) < 0) { | ||
| 1073 | close(l_fd); | ||
| 1074 | return -1; | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | if (bind(l_fd, (struct sockaddr *) &addr, sizeof(struct sockaddr)) == -1) { | ||
| 1078 | close(l_fd); | ||
| 1079 | return -1; | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | return l_fd; | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | |||
| 1086 | static int raw_socket(int ifindex) | ||
| 1087 | { | ||
| 1088 | int l_fd; | ||
| 1089 | struct sockaddr_ll sock; | ||
| 1090 | |||
| 1091 | DEBUG(LOG_INFO, "Opening raw socket on ifindex %d\n", ifindex); | ||
| 1092 | if ((l_fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { | ||
| 1093 | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); | ||
| 1094 | return -1; | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | sock.sll_family = AF_PACKET; | ||
| 1098 | sock.sll_protocol = htons(ETH_P_IP); | ||
| 1099 | sock.sll_ifindex = ifindex; | ||
| 1100 | if (bind(l_fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) { | ||
| 1101 | DEBUG(LOG_ERR, "bind call failed: %s", strerror(errno)); | ||
| 1102 | close(l_fd); | ||
| 1103 | return -1; | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | return l_fd; | ||
| 1107 | |||
| 1108 | } | ||
| 1109 | |||
| 1110 | /* read a packet from socket fd, return -1 on read error, -2 on packet error */ | ||
| 1111 | static int get_packet(struct dhcpMessage *packet, int l_fd) | ||
| 1112 | { | ||
| 1113 | int bytes; | ||
| 1114 | int i; | ||
| 1115 | const char broken_vendors[][8] = { | ||
| 1116 | "MSFT 98", | ||
| 1117 | "" | ||
| 1118 | }; | ||
| 1119 | char unsigned *vendor; | ||
| 1120 | |||
| 1121 | memset(packet, 0, sizeof(struct dhcpMessage)); | ||
| 1122 | bytes = read(l_fd, packet, sizeof(struct dhcpMessage)); | ||
| 1123 | if (bytes < 0) { | ||
| 1124 | DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring"); | ||
| 1125 | return -1; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | if (ntohl(packet->cookie) != DHCP_MAGIC) { | ||
| 1129 | LOG(LOG_ERR, "received bogus message, ignoring"); | ||
| 1130 | return -2; | ||
| 1131 | } | ||
| 1132 | DEBUG(LOG_INFO, "Received a packet"); | ||
| 1133 | |||
| 1134 | if (packet->op == BOOTREQUEST | ||
| 1135 | && (vendor = get_option(packet, DHCP_VENDOR))) { | ||
| 1136 | for (i = 0; broken_vendors[i][0]; i++) { | ||
| 1137 | if (vendor[OPT_LEN - 2] == | ||
| 1138 | (unsigned char) strlen(broken_vendors[i]) | ||
| 1139 | && !strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) { | ||
| 1140 | DEBUG(LOG_INFO, "broken client (%s), forcing broadcast", | ||
| 1141 | broken_vendors[i]); | ||
| 1142 | packet->flags |= htons(BROADCAST_FLAG); | ||
| 1143 | } | ||
| 1144 | } | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | return bytes; | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | static inline int get_raw_packet(struct dhcpMessage *payload, int l_fd) | ||
| 1151 | { | ||
| 1152 | int bytes; | ||
| 1153 | struct udp_dhcp_packet packet; | ||
| 1154 | u_int32_t source, dest; | ||
| 1155 | u_int16_t check; | ||
| 1156 | |||
| 1157 | memset(&packet, 0, sizeof(struct udp_dhcp_packet)); | ||
| 1158 | bytes = read(l_fd, &packet, sizeof(struct udp_dhcp_packet)); | ||
| 1159 | if (bytes < 0) { | ||
| 1160 | DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring"); | ||
| 1161 | usleep(500000); /* possible down interface, looping condition */ | ||
| 1162 | return -1; | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { | ||
| 1166 | DEBUG(LOG_INFO, "message too short, ignoring"); | ||
| 1167 | return -1; | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | if (bytes < ntohs(packet.ip.tot_len)) { | ||
| 1171 | DEBUG(LOG_INFO, "Truncated packet"); | ||
| 1172 | return -1; | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | /* ignore any extra garbage bytes */ | ||
| 1176 | bytes = ntohs(packet.ip.tot_len); | ||
| 1177 | |||
| 1178 | /* Make sure its the right packet for us, and that it passes sanity checks */ | ||
| 1179 | if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION || | ||
| 1180 | packet.ip.ihl != sizeof(packet.ip) >> 2 | ||
| 1181 | || packet.udp.dest != htons(CLIENT_PORT) | ||
| 1182 | || bytes > (int) sizeof(struct udp_dhcp_packet) | ||
| 1183 | || ntohs(packet.udp.len) != (short) (bytes - sizeof(packet.ip))) { | ||
| 1184 | DEBUG(LOG_INFO, "unrelated/bogus packet"); | ||
| 1185 | return -1; | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | /* check IP checksum */ | ||
| 1189 | check = packet.ip.check; | ||
| 1190 | packet.ip.check = 0; | ||
| 1191 | if (check != checksum(&(packet.ip), sizeof(packet.ip))) { | ||
| 1192 | DEBUG(LOG_INFO, "bad IP header checksum, ignoring"); | ||
| 1193 | return -1; | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | /* verify the UDP checksum by replacing the header with a psuedo header */ | ||
| 1197 | source = packet.ip.saddr; | ||
| 1198 | dest = packet.ip.daddr; | ||
| 1199 | check = packet.udp.check; | ||
| 1200 | packet.udp.check = 0; | ||
| 1201 | memset(&packet.ip, 0, sizeof(packet.ip)); | ||
| 1202 | |||
| 1203 | packet.ip.protocol = IPPROTO_UDP; | ||
| 1204 | packet.ip.saddr = source; | ||
| 1205 | packet.ip.daddr = dest; | ||
| 1206 | packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */ | ||
| 1207 | if (check && check != checksum(&packet, bytes)) { | ||
| 1208 | DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring"); | ||
| 1209 | return -1; | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | memcpy(payload, &(packet.data), | ||
| 1213 | bytes - (sizeof(packet.ip) + sizeof(packet.udp))); | ||
| 1214 | |||
| 1215 | if (ntohl(payload->cookie) != DHCP_MAGIC) { | ||
| 1216 | LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring"); | ||
| 1217 | return -1; | ||
| 1218 | } | ||
| 1219 | DEBUG(LOG_INFO, "oooooh!!! got some!"); | ||
| 1220 | return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); | ||
| 1221 | } | ||
| 1222 | |||
| 1223 | |||
| 1224 | int udhcpc_main(int argc, char *argv[]) | ||
| 1225 | { | ||
| 1226 | unsigned char *temp, *message; | ||
| 1227 | unsigned long t1 = 0, t2 = 0, xid = 0; | ||
| 1228 | unsigned long start = 0, lease; | ||
| 1229 | fd_set rfds; | ||
| 1230 | int retval; | ||
| 1231 | struct timeval tv; | ||
| 1232 | int c, len; | ||
| 1233 | struct dhcpMessage packet; | ||
| 1234 | struct in_addr temp_addr; | ||
| 1235 | int pid_fd; | ||
| 1236 | time_t now; | ||
| 1237 | |||
| 1238 | static struct option l_options[] = { | ||
| 1239 | {"clientid", required_argument, 0, 'c'}, | ||
| 1240 | {"foreground", no_argument, 0, 'f'}, | ||
| 1241 | {"hostname", required_argument, 0, 'H'}, | ||
| 1242 | {"help", no_argument, 0, 'h'}, | ||
| 1243 | {"interface", required_argument, 0, 'i'}, | ||
| 1244 | {"now", no_argument, 0, 'n'}, | ||
| 1245 | {"pidfile", required_argument, 0, 'p'}, | ||
| 1246 | {"quit", no_argument, 0, 'q'}, | ||
| 1247 | {"request", required_argument, 0, 'r'}, | ||
| 1248 | {"script", required_argument, 0, 's'}, | ||
| 1249 | {"version", no_argument, 0, 'v'}, | ||
| 1250 | {0, 0, 0, 0} | ||
| 1251 | }; | ||
| 1252 | |||
| 1253 | /* get options */ | ||
| 1254 | while (1) { | ||
| 1255 | int option_index = 0; | ||
| 1256 | |||
| 1257 | c = getopt_long(argc, argv, "c:fH:i:np:qr:s:v", l_options, | ||
| 1258 | &option_index); | ||
| 1259 | if (c == -1) { | ||
| 1260 | break; | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | switch (c) { | ||
| 1264 | case 'c': | ||
| 1265 | len = strlen(optarg); | ||
| 1266 | if (len > 255) { | ||
| 1267 | len = 255; | ||
| 1268 | } | ||
| 1269 | if (client_config.clientid) { | ||
| 1270 | free(client_config.clientid); | ||
| 1271 | } | ||
| 1272 | client_config.clientid = xmalloc(len + 2); | ||
| 1273 | client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; | ||
| 1274 | client_config.clientid[OPT_LEN] = len; | ||
| 1275 | client_config.clientid[OPT_DATA] = '\0'; | ||
| 1276 | strncpy(client_config.clientid + 3, optarg, len - 1); | ||
| 1277 | break; | ||
| 1278 | case 'f': | ||
| 1279 | client_config.foreground = 1; | ||
| 1280 | break; | ||
| 1281 | case 'H': | ||
| 1282 | len = strlen(optarg); | ||
| 1283 | if (len > 255) { | ||
| 1284 | len = 255; | ||
| 1285 | } | ||
| 1286 | if (client_config.hostname) { | ||
| 1287 | free(client_config.hostname); | ||
| 1288 | } | ||
| 1289 | client_config.hostname = xmalloc(len + 2); | ||
| 1290 | client_config.hostname[OPT_CODE] = DHCP_HOST_NAME; | ||
| 1291 | client_config.hostname[OPT_LEN] = len; | ||
| 1292 | strncpy(client_config.hostname + 2, optarg, len); | ||
| 1293 | break; | ||
| 1294 | case 'i': | ||
| 1295 | client_config.interface = optarg; | ||
| 1296 | break; | ||
| 1297 | case 'n': | ||
| 1298 | client_config.abort_if_no_lease = 1; | ||
| 1299 | break; | ||
| 1300 | case 'p': | ||
| 1301 | client_config.pidfile = optarg; | ||
| 1302 | break; | ||
| 1303 | case 'q': | ||
| 1304 | client_config.quit_after_lease = 1; | ||
| 1305 | break; | ||
| 1306 | case 'r': | ||
| 1307 | requested_ip = inet_addr(optarg); | ||
| 1308 | break; | ||
| 1309 | case 's': | ||
| 1310 | client_config.script = optarg; | ||
| 1311 | break; | ||
| 1312 | case 'v': | ||
| 1313 | printf("udhcpcd, version %s\n\n", VERSION); | ||
| 1314 | exit_client(0); | ||
| 1315 | break; | ||
| 1316 | default: | ||
| 1317 | show_usage(); | ||
| 1318 | } | ||
| 1319 | } | ||
| 1320 | |||
| 1321 | OPEN_LOG("udhcpc"); | ||
| 1322 | LOG(LOG_INFO, "udhcp client (v%s) started", VERSION); | ||
| 1323 | |||
| 1324 | pid_fd = pidfile_acquire(client_config.pidfile); | ||
| 1325 | pidfile_write_release(pid_fd); | ||
| 1326 | |||
| 1327 | if (read_interface | ||
| 1328 | (client_config.interface, &client_config.ifindex, NULL, | ||
| 1329 | client_config.arp) < 0) { | ||
| 1330 | exit_client(1); | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | if (!client_config.clientid) { | ||
| 1334 | client_config.clientid = xmalloc(6 + 3); | ||
| 1335 | client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; | ||
| 1336 | client_config.clientid[OPT_LEN] = 7; | ||
| 1337 | client_config.clientid[OPT_DATA] = 1; | ||
| 1338 | memcpy(client_config.clientid + 3, client_config.arp, 6); | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | /* setup signal handlers */ | ||
| 1342 | signal(SIGUSR1, renew_requested); | ||
| 1343 | signal(SIGUSR2, release_requested); | ||
| 1344 | signal(SIGTERM, terminate); | ||
| 1345 | |||
| 1346 | state = INIT_SELECTING; | ||
| 1347 | run_script(NULL, "deconfig"); | ||
| 1348 | change_mode(LISTEN_RAW); | ||
| 1349 | |||
| 1350 | for (;;) { | ||
| 1351 | tv.tv_sec = timeout - time(0); | ||
| 1352 | tv.tv_usec = 0; | ||
| 1353 | FD_ZERO(&rfds); | ||
| 1354 | |||
| 1355 | if (listen_mode != LISTEN_NONE && fd_main < 0) { | ||
| 1356 | if (listen_mode == LISTEN_KERNEL) { | ||
| 1357 | fd_main = | ||
| 1358 | listen_socket(INADDR_ANY, CLIENT_PORT, | ||
| 1359 | client_config.interface); | ||
| 1360 | } else { | ||
| 1361 | fd_main = raw_socket(client_config.ifindex); | ||
| 1362 | } | ||
| 1363 | if (fd_main < 0) { | ||
| 1364 | LOG(LOG_ERR, "FATAL: couldn't listen on socket, %s", | ||
| 1365 | strerror(errno)); | ||
| 1366 | exit_client(0); | ||
| 1367 | } | ||
| 1368 | } | ||
| 1369 | if (fd_main >= 0) { | ||
| 1370 | FD_SET(fd_main, &rfds); | ||
| 1371 | } | ||
| 1372 | |||
| 1373 | if (tv.tv_sec > 0) { | ||
| 1374 | DEBUG(LOG_INFO, "Waiting on select...\n"); | ||
| 1375 | retval = select(fd_main + 1, &rfds, NULL, NULL, &tv); | ||
| 1376 | } else { | ||
| 1377 | retval = 0; /* If we already timed out, fall through */ | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | now = time(0); | ||
| 1381 | if (retval == 0) { | ||
| 1382 | /* timeout dropped to zero */ | ||
| 1383 | switch (state) { | ||
| 1384 | case INIT_SELECTING: | ||
| 1385 | if (packet_num < 3) { | ||
| 1386 | if (packet_num == 0) { | ||
| 1387 | xid = random_xid(); | ||
| 1388 | } | ||
| 1389 | /* send discover packet */ | ||
| 1390 | send_discover(xid, requested_ip); /* broadcast */ | ||
| 1391 | |||
| 1392 | timeout = now + ((packet_num == 2) ? 10 : 2); | ||
| 1393 | packet_num++; | ||
| 1394 | } else { | ||
| 1395 | if (client_config.abort_if_no_lease) { | ||
| 1396 | LOG(LOG_INFO, "No lease, failing."); | ||
| 1397 | exit_client(1); | ||
| 1398 | } | ||
| 1399 | /* wait to try again */ | ||
| 1400 | packet_num = 0; | ||
| 1401 | timeout = now + 60; | ||
| 1402 | } | ||
| 1403 | break; | ||
| 1404 | case RENEW_REQUESTED: | ||
| 1405 | case REQUESTING: | ||
| 1406 | if (packet_num < 3) { | ||
| 1407 | /* send request packet */ | ||
| 1408 | if (state == RENEW_REQUESTED) { | ||
| 1409 | send_renew(xid, server_addr, requested_ip); /* unicast */ | ||
| 1410 | } else { | ||
| 1411 | send_selecting(xid, server_addr, requested_ip); /* broadcast */ | ||
| 1412 | } | ||
| 1413 | timeout = now + ((packet_num == 2) ? 10 : 2); | ||
| 1414 | packet_num++; | ||
| 1415 | } else { | ||
| 1416 | /* timed out, go back to init state */ | ||
| 1417 | state = INIT_SELECTING; | ||
| 1418 | timeout = now; | ||
| 1419 | packet_num = 0; | ||
| 1420 | change_mode(LISTEN_RAW); | ||
| 1421 | } | ||
| 1422 | break; | ||
| 1423 | case BOUND: | ||
| 1424 | /* Lease is starting to run out, time to enter renewing state */ | ||
| 1425 | state = RENEWING; | ||
| 1426 | change_mode(LISTEN_KERNEL); | ||
| 1427 | DEBUG(LOG_INFO, "Entering renew state"); | ||
| 1428 | /* fall right through */ | ||
| 1429 | case RENEWING: | ||
| 1430 | /* Either set a new T1, or enter REBINDING state */ | ||
| 1431 | if ((t2 - t1) <= (lease / 14400 + 1)) { | ||
| 1432 | /* timed out, enter rebinding state */ | ||
| 1433 | state = REBINDING; | ||
| 1434 | timeout = now + (t2 - t1); | ||
| 1435 | DEBUG(LOG_INFO, "Entering rebinding state"); | ||
| 1436 | } else { | ||
| 1437 | /* send a request packet */ | ||
| 1438 | send_renew(xid, server_addr, requested_ip); /* unicast */ | ||
| 1439 | |||
| 1440 | t1 = (t2 - t1) / 2 + t1; | ||
| 1441 | timeout = t1 + start; | ||
| 1442 | } | ||
| 1443 | break; | ||
| 1444 | case REBINDING: | ||
| 1445 | /* Either set a new T2, or enter INIT state */ | ||
| 1446 | if ((lease - t2) <= (lease / 14400 + 1)) { | ||
| 1447 | /* timed out, enter init state */ | ||
| 1448 | state = INIT_SELECTING; | ||
| 1449 | LOG(LOG_INFO, "Lease lost, entering init state"); | ||
| 1450 | run_script(NULL, "deconfig"); | ||
| 1451 | timeout = now; | ||
| 1452 | packet_num = 0; | ||
| 1453 | change_mode(LISTEN_RAW); | ||
| 1454 | } else { | ||
| 1455 | /* send a request packet */ | ||
| 1456 | send_renew(xid, 0, requested_ip); /* broadcast */ | ||
| 1457 | |||
| 1458 | t2 = (lease - t2) / 2 + t2; | ||
| 1459 | timeout = t2 + start; | ||
| 1460 | } | ||
| 1461 | break; | ||
| 1462 | case RELEASED: | ||
| 1463 | /* yah, I know, *you* say it would never happen */ | ||
| 1464 | timeout = 0x7fffffff; | ||
| 1465 | break; | ||
| 1466 | } | ||
| 1467 | } else if (retval > 0 && listen_mode != LISTEN_NONE | ||
| 1468 | && FD_ISSET(fd_main, &rfds)) { | ||
| 1469 | /* a packet is ready, read it */ | ||
| 1470 | |||
| 1471 | if (listen_mode == LISTEN_KERNEL) { | ||
| 1472 | len = get_packet(&packet, fd_main); | ||
| 1473 | } else { | ||
| 1474 | len = get_raw_packet(&packet, fd_main); | ||
| 1475 | } | ||
| 1476 | if (len == -1 && errno != EINTR) { | ||
| 1477 | DEBUG(LOG_INFO, "error on read, %s, reopening socket", | ||
| 1478 | strerror(errno)); | ||
| 1479 | change_mode(listen_mode); /* just close and reopen */ | ||
| 1480 | } | ||
| 1481 | if (len < 0) { | ||
| 1482 | continue; | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | if (packet.xid != xid) { | ||
| 1486 | DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)", | ||
| 1487 | (unsigned long) packet.xid, xid); | ||
| 1488 | continue; | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { | ||
| 1492 | DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring"); | ||
| 1493 | continue; | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | switch (state) { | ||
| 1497 | case INIT_SELECTING: | ||
| 1498 | /* Must be a DHCPOFFER to one of our xid's */ | ||
| 1499 | if (*message == DHCPOFFER) { | ||
| 1500 | if ((temp = get_option(&packet, DHCP_SERVER_ID))) { | ||
| 1501 | memcpy(&server_addr, temp, 4); | ||
| 1502 | xid = packet.xid; | ||
| 1503 | requested_ip = packet.yiaddr; | ||
| 1504 | |||
| 1505 | /* enter requesting state */ | ||
| 1506 | state = REQUESTING; | ||
| 1507 | timeout = now; | ||
| 1508 | packet_num = 0; | ||
| 1509 | } else { | ||
| 1510 | DEBUG(LOG_ERR, "No server ID in message"); | ||
| 1511 | } | ||
| 1512 | } | ||
| 1513 | break; | ||
| 1514 | case RENEW_REQUESTED: | ||
| 1515 | case REQUESTING: | ||
| 1516 | case RENEWING: | ||
| 1517 | case REBINDING: | ||
| 1518 | if (*message == DHCPACK) { | ||
| 1519 | if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) { | ||
| 1520 | LOG(LOG_ERR, | ||
| 1521 | "No lease time with ACK, using 1 hour lease"); | ||
| 1522 | lease = 60 * 60; | ||
| 1523 | } else { | ||
| 1524 | memcpy(&lease, temp, 4); | ||
| 1525 | lease = ntohl(lease); | ||
| 1526 | } | ||
| 1527 | |||
| 1528 | /* enter bound state */ | ||
| 1529 | t1 = lease / 2; | ||
| 1530 | |||
| 1531 | /* little fixed point for n * .875 */ | ||
| 1532 | t2 = (lease * 0x7) >> 3; | ||
| 1533 | temp_addr.s_addr = packet.yiaddr; | ||
| 1534 | LOG(LOG_INFO, "Lease of %s obtained, lease time %ld", | ||
| 1535 | inet_ntoa(temp_addr), lease); | ||
| 1536 | start = now; | ||
| 1537 | timeout = t1 + start; | ||
| 1538 | requested_ip = packet.yiaddr; | ||
| 1539 | run_script(&packet, | ||
| 1540 | ((state == RENEWING | ||
| 1541 | || state == REBINDING) ? "renew" : "bound")); | ||
| 1542 | |||
| 1543 | state = BOUND; | ||
| 1544 | change_mode(LISTEN_NONE); | ||
| 1545 | { | ||
| 1546 | int pid_fd2; | ||
| 1547 | |||
| 1548 | if (client_config.quit_after_lease) { | ||
| 1549 | exit_client(0); | ||
| 1550 | } else if (!client_config.foreground) { | ||
| 1551 | pid_fd2 = pidfile_acquire(client_config.pidfile); /* hold lock during fork. */ | ||
| 1552 | if (daemon(0, 0) == -1) { | ||
| 1553 | perror("fork"); | ||
| 1554 | exit_client(1); | ||
| 1555 | } | ||
| 1556 | client_config.foreground = 1; /* Do not fork again. */ | ||
| 1557 | pidfile_write_release(pid_fd2); | ||
| 1558 | } | ||
| 1559 | } | ||
| 1560 | } else if (*message == DHCPNAK) { | ||
| 1561 | /* return to init state */ | ||
| 1562 | LOG(LOG_INFO, "Received DHCP NAK"); | ||
| 1563 | run_script(&packet, "nak"); | ||
| 1564 | if (state != REQUESTING) { | ||
| 1565 | run_script(NULL, "deconfig"); | ||
| 1566 | } | ||
| 1567 | state = INIT_SELECTING; | ||
| 1568 | timeout = now; | ||
| 1569 | requested_ip = 0; | ||
| 1570 | packet_num = 0; | ||
| 1571 | change_mode(LISTEN_RAW); | ||
| 1572 | sleep(3); /* avoid excessive network traffic */ | ||
| 1573 | } | ||
| 1574 | break; | ||
| 1575 | /* case BOUND, RELEASED: - ignore all packets */ | ||
| 1576 | } | ||
| 1577 | } else if (retval == -1 && errno == EINTR) { | ||
| 1578 | /* a signal was caught */ | ||
| 1579 | |||
| 1580 | } else { | ||
| 1581 | /* An error occured */ | ||
| 1582 | DEBUG(LOG_ERR, "Error on select"); | ||
| 1583 | } | ||
| 1584 | |||
| 1585 | } | ||
| 1586 | return 0; | ||
| 1587 | } | ||
