added full udhcp integration
No binary (build failed)
—
(log yok)
| @@ -22,7 +22,7 @@ include $(TOPDIR).config | |
| include $(TOPDIR)Rules.mak | |
| SUBDIRS:=applets archival archival/libunarchive console-tools \ | |
| editors fileutils findutils init miscutils modutils networking \ | |
| - | procps loginutils shell shellutils sysklogd \ |
| + | networking/udhcp procps loginutils shell shellutils sysklogd \ |
| textutils util-linux libbb libpwdgrp | |
| all: do-it-all | |
| @@ -1,7 +1,7 @@ | |
| #!/bin/sh | |
| # Sample udhcpc renew script | |
| - | RESOLV_CONF="/etc/resolv.conf" |
| + | RESOLV_CONF="/etc/udhcpc/resolv.conf" |
| [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" | |
| [ -n "$subnet" ] && NETMASK="netmask $subnet" | |
| @@ -0,0 +1,4 @@ | |
| + | #!/bin/sh |
| + | # Sample udhcpc nak script |
| + | |
| + | echo Received a NAK: $message |
| @@ -1,7 +1,7 @@ | |
| #!/bin/sh | |
| # Sample udhcpc bound script | |
| - | RESOLV_CONF="/etc/resolv.conf" |
| + | RESOLV_CONF="/etc/udhcpc/resolv.conf" |
| [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" | |
| [ -n "$subnet" ] && NETMASK="netmask $subnet" | |
| @@ -27,4 +27,4 @@ for i in $dns | |
| do | |
| echo adding dns $i | |
| echo nameserver $i >> $RESOLV_CONF | |
| - | done |
| + | done |
| @@ -4,4 +4,4 @@ | |
| # common initialization first, especially if more dhcp event notifications | |
| # are added. | |
| - | exec /usr/share/udhcpc/default.$1 |
| + | exec /usr/share/udhcpc/sample.$1 |
| @@ -0,0 +1,39 @@ | |
| + | #!/bin/sh |
| + | |
| + | # udhcpc script edited by Tim Riker <[email protected]> |
| + | |
| + | [ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1 |
| + | |
| + | RESOLV_CONF="/etc/resolv.conf" |
| + | [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" |
| + | [ -n "$subnet" ] && NETMASK="netmask $subnet" |
| + | |
| + | case "$1" in |
| + | deconfig) |
| + | /sbin/ifconfig $interface 0.0.0.0 |
| + | ;; |
| + | |
| + | renew|bound) |
| + | /sbin/ifconfig $interface $ip $BROADCAST $NETMASK |
| + | |
| + | if [ -n "$router" ] ; then |
| + | echo "deleting routers" |
| + | while route del default gw 0.0.0.0 dev $interface ; do |
| + | : |
| + | done |
| + | |
| + | for i in $router ; do |
| + | route add default gw $i dev $interface |
| + | done |
| + | fi |
| + | |
| + | echo -n > $RESOLV_CONF |
| + | [ -n "$domain" ] && echo search $domain >> $RESOLV_CONF |
| + | for i in $dns ; do |
| + | echo adding dns $i |
| + | echo nameserver $i >> $RESOLV_CONF |
| + | done |
| + | ;; |
| + | esac |
| + | |
| + | exit 0 |
| @@ -0,0 +1,116 @@ | |
| + | # Sample udhcpd configuration file (/etc/udhcpd.conf) |
| + | |
| + | # The start and end of the IP lease block |
| + | |
| + | start 192.168.0.20 #default: 192.168.0.20 |
| + | end 192.168.0.254 #default: 192.168.0.254 |
| + | |
| + | |
| + | # The interface that udhcpd will use |
| + | |
| + | interface eth0 #default: eth0 |
| + | |
| + | |
| + | # The maximim number of leases (includes addressesd reserved |
| + | # by OFFER's, DECLINE's, and ARP conficts |
| + | |
| + | #max_leases 254 #default: 254 |
| + | |
| + | |
| + | # If remaining is true (default), udhcpd will store the time |
| + | # remaining for each lease in the udhcpd leases file. This is |
| + | # for embedded systems that cannot keep time between reboots. |
| + | # If you set remaining to no, the absolute time that the lease |
| + | # expires at will be stored in the dhcpd.leases file. |
| + | |
| + | #remaining yes #default: yes |
| + | |
| + | |
| + | # The time period at which udhcpd will write out a dhcpd.leases |
| + | # file. If this is 0, udhcpd will never automatically write a |
| + | # lease file. (specified in seconds) |
| + | |
| + | #auto_time 7200 #default: 7200 (2 hours) |
| + | |
| + | |
| + | # The amount of time that an IP will be reserved (leased) for if a |
| + | # DHCP decline message is received (seconds). |
| + | |
| + | #decline_time 3600 #default: 3600 (1 hour) |
| + | |
| + | |
| + | # The amount of time that an IP will be reserved (leased) for if an |
| + | # ARP conflct occurs. (seconds |
| + | |
| + | #conflict_time 3600 #default: 3600 (1 hour) |
| + | |
| + | |
| + | # How long an offered address is reserved (leased) in seconds |
| + | |
| + | #offer_time 60 #default: 60 (1 minute) |
| + | |
| + | # If a lease to be given is below this value, the full lease time is |
| + | # instead used (seconds). |
| + | |
| + | #min_lease 60 #defult: 60 |
| + | |
| + | |
| + | # The location of the leases file |
| + | |
| + | #lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases |
| + | |
| + | # The location of the pid file |
| + | #pidfile /var/run/udhcpd.pid #default: /var/run/udhcpd.pid |
| + | |
| + | # Everytime udhcpd writes a leases file, the below script will be called. |
| + | # Useful for writing the lease file to flash every few hours. |
| + | |
| + | #notify_file #default: (no script) |
| + | |
| + | #notify_file dumpleases # <--- usefull for debugging |
| + | |
| + | # The following are bootp specific options, setable by udhcpd. |
| + | |
| + | #siaddr 192.168.0.22 #default: 0.0.0.0 |
| + | |
| + | #sname zorak #default: (none) |
| + | |
| + | #boot_file /var/nfs_root #default: (none) |
| + | |
| + | # The remainer of options are DHCP options and can be specifed with the |
| + | # keyword 'opt' or 'option'. If an option can take multiple items, such |
| + | # as the dns option, they can be listed on the same line, or multiple |
| + | # lines. The only option with a default is 'lease'. |
| + | |
| + | #Examles |
| + | opt dns 192.168.10.2 192.168.10.10 |
| + | option subnet 255.255.255.0 |
| + | opt router 192.168.10.2 |
| + | opt wins 192.168.10.10 |
| + | option dns 129.219.13.81 # appened to above DNS servers for a total of 3 |
| + | option domain local |
| + | option lease 864000 # 10 days of seconds |
| + | |
| + | |
| + | # Currently supported options, for more info, see options.c |
| + | #subnet |
| + | #timezone |
| + | #router |
| + | #timesvr |
| + | #namesvr |
| + | #dns |
| + | #logsvr |
| + | #cookiesvr |
| + | #lprsvr |
| + | #bootsize |
| + | #domain |
| + | #swapsvr |
| + | #rootpath |
| + | #ipttl |
| + | #mtu |
| + | #broadcast |
| + | #wins |
| + | #lease |
| + | #ntpsrv |
| + | #tftp |
| + | #bootfile |
| @@ -152,6 +152,9 @@ | |
| #ifdef CONFIG_DUMPKMAP | |
| APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN, _BB_SUID_NEVER) | |
| #endif | |
| + | #ifdef CONFIG_DUMPLEASES |
| + | APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) |
| + | #endif |
| #ifdef CONFIG_DUTMP | |
| APPLET(dutmp, dutmp_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) | |
| #endif | |
| @@ -495,7 +498,10 @@ | |
| APPLET(tty, tty_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) | |
| #endif | |
| #ifdef CONFIG_UDHCPC | |
| - | APPLET(udhcpc, udhcpc_main, _BB_DIR_SBIN, _BB_SUID_NEVER) |
| + | APPLET(udhcpc, udhcpc_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) |
| + | #endif |
| + | #ifdef CONFIG_UDHCPD |
| + | APPLET(udhcpd, udhcpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) |
| #endif | |
| #ifdef CONFIG_UMOUNT | |
| APPLET(umount, umount_main, _BB_DIR_BIN, _BB_SUID_NEVER) | |
| @@ -385,6 +385,14 @@ | |
| #define dumpkmap_example_usage \ | |
| "$ dumpkmap > keymap\n" | |
| + | #define dumpleases_trivial_usage \ |
| + | "[-r|-a] [-f LEASEFILE]" |
| + | #define dumpleases_full_usage \ |
| + | "Usage: dumpleases -f <file> -[r|a]\n" \ |
| + | "\t-f,\t--file=FILENAME\tLeases file to load\n" \ |
| + | "\t-r,\t--remaining\tInterepret lease times as time remaing\n" \ |
| + | "\t-a,\t--absolute\tInterepret lease times as expire time\n" |
| + | |
| #define dutmp_trivial_usage \ | |
| "[FILE]" | |
| #define dutmp_full_usage \ | |
| @@ -1945,12 +1953,14 @@ | |
| "/dev/tty2\n" | |
| #define udhcpc_trivial_usage \ | |
| - | "[-fqv] [-c CLIENTID] [-H HOSTNAME] [-i INTERFACE]\n[-p file] [-r IP] [-s script]" |
| + | "[-fbnqv] [-c CLIENTID] [-H HOSTNAME] [-i INTERFACE]\n[-p pidfile] [-r IP] [-s script]" |
| #define udhcpc_full_usage \ | |
| - | "\tUsage: udhcpcd [OPTIONS]\n" \ |
| + | "\tUsage: udhcpc [OPTIONS]\n" \ |
| "\t-c,\t--clientid=CLIENTID\tClient identifier\n" \ | |
| "\t-H,\t--hostname=HOSTNAME\tClient hostname\n" \ | |
| + | "\t-h,\t \tAlias for -h\n" \ |
| "\t-f,\t--foreground\tDo not fork after getting lease\n" \ | |
| + | "\t-b,\t--background\tFork to background if lease cannot be immediately negotiated.\n" \ |
| "\t-i,\t--interface=INTERFACE\tInterface to use (default: eth0)\n" \ | |
| "\t-n,\t--now\tExit with failure if lease cannot be immediately negotiated.\n" \ | |
| "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \ | |
| @@ -1959,6 +1969,12 @@ | |
| "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \ | |
| "\t-v,\t--version\tDisplay version" | |
| + | #define udhcpd_trivial_usage \ |
| + | "[configfile]\n" \ |
| + | |
| + | #define udhcpd_full_usage \ |
| + | "\tUsage: udhcpd [configfile]\n" |
| + | |
| #ifdef CONFIG_FEATURE_MOUNT_FORCE | |
| #define USAGE_MOUNT_FORCE(a) a | |
| #else | |
| @@ -35,7 +35,6 @@ NETWORKING-$(CONFIG_TELNET) += telnet.o | |
| NETWORKING-$(CONFIG_TELNETD) += telnetd.o | |
| NETWORKING-$(CONFIG_TFTP) += tftp.o | |
| NETWORKING-$(CONFIG_TRACEROUTE) += traceroute.o | |
| - | NETWORKING-$(CONFIG_UDHCPC) += udhcpc.o |
| NETWORKING-$(CONFIG_WGET) += wget.o | |
| libraries-y+=$(NETWORKING_DIR)$(NETWORKING_AR) | |
| @@ -47,12 +47,13 @@ if [ "$CONFIG_TRACEROUTE" = "y" ]; then | |
| bool ' Enable verbose output' CONFIG_FEATURE_TRACEROUTE_VERBOSE | |
| bool ' Enable SO_DEBUG option' CONFIG_FEATURE_TRACEROUTE_SO_DEBUG | |
| fi | |
| - | bool 'udhcpc' CONFIG_UDHCPC |
| bool 'wget' CONFIG_WGET | |
| if [ "$CONFIG_WGET" = "y" ]; then | |
| bool ' Enable a nifty process meter (+2k)' CONFIG_FEATURE_WGET_STATUSBAR | |
| bool ' Enable HTTP authentication' CONFIG_FEATURE_WGET_AUTHENTICATION | |
| fi | |
| + | source networking/udhcp/config.in |
| + | |
| endmenu | |
| @@ -0,0 +1,13 @@ | |
| + | udhcp server/client package |
| + | ----------------------- |
| + | |
| + | Russ Dill <[email protected]> |
| + | Matthew Ramsay <[email protected]> |
| + | Chris Trew <[email protected]> |
| + | |
| + | Other Credits: |
| + | -------------- |
| + | Moreton Bay (http://www.moretonbay.com/) |
| + | Lineo (http://opensource.lineo.com) |
| + | |
| + | |
| @@ -0,0 +1,339 @@ | |
| + | GNU GENERAL PUBLIC LICENSE |
| + | Version 2, June 1991 |
| + | |
| + | Copyright (C) 1989, 1991 Free Software Foundation, Inc. |
| + | 675 Mass Ave, Cambridge, MA 02139, USA |
| + | Everyone is permitted to copy and distribute verbatim copies |
| + | of this license document, but changing it is not allowed. |
| + | |
| + | Preamble |
| + | |
| + | The licenses for most software are designed to take away your |
| + | freedom to share and change it. By contrast, the GNU General Public |
| + | License is intended to guarantee your freedom to share and change free |
| + | software--to make sure the software is free for all its users. This |
| + | General Public License applies to most of the Free Software |
| + | Foundation's software and to any other program whose authors commit to |
| + | using it. (Some other Free Software Foundation software is covered by |
| + | the GNU Library General Public License instead.) You can apply it to |
| + | your programs, too. |
| + | |
| + | When we speak of free software, we are referring to freedom, not |
| + | price. Our General Public Licenses are designed to make sure that you |
| + | have the freedom to distribute copies of free software (and charge for |
| + | this service if you wish), that you receive source code or can get it |
| + | if you want it, that you can change the software or use pieces of it |
| + | in new free programs; and that you know you can do these things. |
| + | |
| + | To protect your rights, we need to make restrictions that forbid |
| + | anyone to deny you these rights or to ask you to surrender the rights. |
| + | These restrictions translate to certain responsibilities for you if you |
| + | distribute copies of the software, or if you modify it. |
| + | |
| + | For example, if you distribute copies of such a program, whether |
| + | gratis or for a fee, you must give the recipients all the rights that |
| + | you have. You must make sure that they, too, receive or can get the |
| + | source code. And you must show them these terms so they know their |
| + | rights. |
| + | |
| + | We protect your rights with two steps: (1) copyright the software, and |
| + | (2) offer you this license which gives you legal permission to copy, |
| + | distribute and/or modify the software. |
| + | |
| + | Also, for each author's protection and ours, we want to make certain |
| + | that everyone understands that there is no warranty for this free |
| + | software. If the software is modified by someone else and passed on, we |
| + | want its recipients to know that what they have is not the original, so |
| + | that any problems introduced by others will not reflect on the original |
| + | authors' reputations. |
| + | |
| + | Finally, any free program is threatened constantly by software |
| + | patents. We wish to avoid the danger that redistributors of a free |
| + | program will individually obtain patent licenses, in effect making the |
| + | program proprietary. To prevent this, we have made it clear that any |
| + | patent must be licensed for everyone's free use or not licensed at all. |
| + | |
| + | The precise terms and conditions for copying, distribution and |
| + | modification follow. |
| + | |
| + | GNU GENERAL PUBLIC LICENSE |
| + | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
| + | |
| + | 0. This License applies to any program or other work which contains |
| + | a notice placed by the copyright holder saying it may be distributed |
| + | under the terms of this General Public License. The "Program", below, |
| + | refers to any such program or work, and a "work based on the Program" |
| + | means either the Program or any derivative work under copyright law: |
| + | that is to say, a work containing the Program or a portion of it, |
| + | either verbatim or with modifications and/or translated into another |
| + | language. (Hereinafter, translation is included without limitation in |
| + | the term "modification".) Each licensee is addressed as "you". |
| + | |
| + | Activities other than copying, distribution and modification are not |
| + | covered by this License; they are outside its scope. The act of |
| + | running the Program is not restricted, and the output from the Program |
| + | is covered only if its contents constitute a work based on the |
| + | Program (independent of having been made by running the Program). |
| + | Whether that is true depends on what the Program does. |
| + | |
| + | 1. You may copy and distribute verbatim copies of the Program's |
| + | source code as you receive it, in any medium, provided that you |
| + | conspicuously and appropriately publish on each copy an appropriate |
| + | copyright notice and disclaimer of warranty; keep intact all the |
| + | notices that refer to this License and to the absence of any warranty; |
| + | and give any other recipients of the Program a copy of this License |
| + | along with the Program. |
| + | |
| + | You may charge a fee for the physical act of transferring a copy, and |
| + | you may at your option offer warranty protection in exchange for a fee. |
| + | |
| + | 2. You may modify your copy or copies of the Program or any portion |
| + | of it, thus forming a work based on the Program, and copy and |
| + | distribute such modifications or work under the terms of Section 1 |
| + | above, provided that you also meet all of these conditions: |
| + | |
| + | a) You must cause the modified files to carry prominent notices |
| + | stating that you changed the files and the date of any change. |
| + | |
| + | b) You must cause any work that you distribute or publish, that in |
| + | whole or in part contains or is derived from the Program or any |
| + | part thereof, to be licensed as a whole at no charge to all third |
| + | parties under the terms of this License. |
| + | |
| + | c) If the modified program normally reads commands interactively |
| + | when run, you must cause it, when started running for such |
| + | interactive use in the most ordinary way, to print or display an |
| + | announcement including an appropriate copyright notice and a |
| + | notice that there is no warranty (or else, saying that you provide |
| + | a warranty) and that users may redistribute the program under |
| + | these conditions, and telling the user how to view a copy of this |
| + | License. (Exception: if the Program itself is interactive but |
| + | does not normally print such an announcement, your work based on |
| + | the Program is not required to print an announcement.) |
| + | |
| + | These requirements apply to the modified work as a whole. If |
| + | identifiable sections of that work are not derived from the Program, |
| + | and can be reasonably considered independent and separate works in |
| + | themselves, then this License, and its terms, do not apply to those |
| + | sections when you distribute them as separate works. But when you |
| + | distribute the same sections as part of a whole which is a work based |
| + | on the Program, the distribution of the whole must be on the terms of |
| + | this License, whose permissions for other licensees extend to the |
| + | entire whole, and thus to each and every part regardless of who wrote it. |
| + | |
| + | Thus, it is not the intent of this section to claim rights or contest |
| + | your rights to work written entirely by you; rather, the intent is to |
| + | exercise the right to control the distribution of derivative or |
| + | collective works based on the Program. |
| + | |
| + | In addition, mere aggregation of another work not based on the Program |
| + | with the Program (or with a work based on the Program) on a volume of |
| + | a storage or distribution medium does not bring the other work under |
| + | the scope of this License. |
| + | |
| + | 3. You may copy and distribute the Program (or a work based on it, |
| + | under Section 2) in object code or executable form under the terms of |
| + | Sections 1 and 2 above provided that you also do one of the following: |
| + | |
| + | a) Accompany it with the complete corresponding machine-readable |
| + | source code, which must be distributed under the terms of Sections |
| + | 1 and 2 above on a medium customarily used for software interchange; or, |
| + | |
| + | b) Accompany it with a written offer, valid for at least three |
| + | years, to give any third party, for a charge no more than your |
| + | cost of physically performing source distribution, a complete |
| + | machine-readable copy of the corresponding source code, to be |
| + | distributed under the terms of Sections 1 and 2 above on a medium |
| + | customarily used for software interchange; or, |
| + | |
| + | c) Accompany it with the information you received as to the offer |
| + | to distribute corresponding source code. (This alternative is |
| + | allowed only for noncommercial distribution and only if you |
| + | received the program in object code or executable form with such |
| + | an offer, in accord with Subsection b above.) |
| + | |
| + | The source code for a work means the preferred form of the work for |
| + | making modifications to it. For an executable work, complete source |
| + | code means all the source code for all modules it contains, plus any |
| + | associated interface definition files, plus the scripts used to |
| + | control compilation and installation of the executable. However, as a |
| + | special exception, the source code distributed need not include |
| + | anything that is normally distributed (in either source or binary |
| + | form) with the major components (compiler, kernel, and so on) of the |
| + | operating system on which the executable runs, unless that component |
| + | itself accompanies the executable. |
| + | |
| + | If distribution of executable or object code is made by offering |
| + | access to copy from a designated place, then offering equivalent |
| + | access to copy the source code from the same place counts as |
| + | distribution of the source code, even though third parties are not |
| + | compelled to copy the source along with the object code. |
| + | |
| + | 4. You may not copy, modify, sublicense, or distribute the Program |
| + | except as expressly provided under this License. Any attempt |
| + | otherwise to copy, modify, sublicense or distribute the Program is |
| + | void, and will automatically terminate your rights under this License. |
| + | However, parties who have received copies, or rights, from you under |
| + | this License will not have their licenses terminated so long as such |
| + | parties remain in full compliance. |
| + | |
| + | 5. You are not required to accept this License, since you have not |
| + | signed it. However, nothing else grants you permission to modify or |
| + | distribute the Program or its derivative works. These actions are |
| + | prohibited by law if you do not accept this License. Therefore, by |
| + | modifying or distributing the Program (or any work based on the |
| + | Program), you indicate your acceptance of this License to do so, and |
| + | all its terms and conditions for copying, distributing or modifying |
| + | the Program or works based on it. |
| + | |
| + | 6. Each time you redistribute the Program (or any work based on the |
| + | Program), the recipient automatically receives a license from the |
| + | original licensor to copy, distribute or modify the Program subject to |
| + | these terms and conditions. You may not impose any further |
| + | restrictions on the recipients' exercise of the rights granted herein. |
| + | You are not responsible for enforcing compliance by third parties to |
| + | this License. |
| + | |
| + | 7. If, as a consequence of a court judgment or allegation of patent |
| + | infringement or for any other reason (not limited to patent issues), |
| + | conditions are imposed on you (whether by court order, agreement or |
| + | otherwise) that contradict the conditions of this License, they do not |
| + | excuse you from the conditions of this License. If you cannot |
| + | distribute so as to satisfy simultaneously your obligations under this |
| + | License and any other pertinent obligations, then as a consequence you |
| + | may not distribute the Program at all. For example, if a patent |
| + | license would not permit royalty-free redistribution of the Program by |
| + | all those who receive copies directly or indirectly through you, then |
| + | the only way you could satisfy both it and this License would be to |
| + | refrain entirely from distribution of the Program. |
| + | |
| + | If any portion of this section is held invalid or unenforceable under |
| + | any particular circumstance, the balance of the section is intended to |
| + | apply and the section as a whole is intended to apply in other |
| + | circumstances. |
| + | |
| + | It is not the purpose of this section to induce you to infringe any |
| + | patents or other property right claims or to contest validity of any |
| + | such claims; this section has the sole purpose of protecting the |
| + | integrity of the free software distribution system, which is |
| + | implemented by public license practices. Many people have made |
| + | generous contributions to the wide range of software distributed |
| + | through that system in reliance on consistent application of that |
| + | system; it is up to the author/donor to decide if he or she is willing |
| + | to distribute software through any other system and a licensee cannot |
| + | impose that choice. |
| + | |
| + | This section is intended to make thoroughly clear what is believed to |
| + | be a consequence of the rest of this License. |
| + | |
| + | 8. If the distribution and/or use of the Program is restricted in |
| + | certain countries either by patents or by copyrighted interfaces, the |
| + | original copyright holder who places the Program under this License |
| + | may add an explicit geographical distribution limitation excluding |
| + | those countries, so that distribution is permitted only in or among |
| + | countries not thus excluded. In such case, this License incorporates |
| + | the limitation as if written in the body of this License. |
| + | |
| + | 9. The Free Software Foundation may publish revised and/or new versions |
| + | of the General Public License from time to time. Such new versions will |
| + | be similar in spirit to the present version, but may differ in detail to |
| + | address new problems or concerns. |
| + | |
| + | Each version is given a distinguishing version number. If the Program |
| + | specifies a version number of this License which applies to it and "any |
| + | later version", you have the option of following the terms and conditions |
| + | either of that version or of any later version published by the Free |
| + | Software Foundation. If the Program does not specify a version number of |
| + | this License, you may choose any version ever published by the Free Software |
| + | Foundation. |
| + | |
| + | 10. If you wish to incorporate parts of the Program into other free |
| + | programs whose distribution conditions are different, write to the author |
| + | to ask for permission. For software which is copyrighted by the Free |
| + | Software Foundation, write to the Free Software Foundation; we sometimes |
| + | make exceptions for this. Our decision will be guided by the two goals |
| + | of preserving the free status of all derivatives of our free software and |
| + | of promoting the sharing and reuse of software generally. |
| + | |
| + | NO WARRANTY |
| + | |
| + | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY |
| + | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN |
| + | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES |
| + | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED |
| + | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| + | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS |
| + | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE |
| + | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, |
| + | REPAIR OR CORRECTION. |
| + | |
| + | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
| + | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR |
| + | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, |
| + | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
| + | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED |
| + | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY |
| + | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
| + | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
| + | POSSIBILITY OF SUCH DAMAGES. |
| + | |
| + | END OF TERMS AND CONDITIONS |
| + | |
| + | Appendix: How to Apply These Terms to Your New Programs |
| + | |
| + | If you develop a new program, and you want it to be of the greatest |
| + | possible use to the public, the best way to achieve this is to make it |
| + | free software which everyone can redistribute and change under these terms. |
| + | |
| + | To do so, attach the following notices to the program. It is safest |
| + | to attach them to the start of each source file to most effectively |
| + | convey the exclusion of warranty; and each file should have at least |
| + | the "copyright" line and a pointer to where the full notice is found. |
| + | |
| + | <one line to give the program's name and a brief idea of what it does.> |
| + | Copyright (C) 19yy <name of author> |
| + | |
| + | This program is free software; you can redistribute it and/or modify |
| + | it under the terms of the GNU General Public License as published by |
| + | the Free Software Foundation; either version 2 of the License, or |
| + | (at your option) any later version. |
| + | |
| + | This program is distributed in the hope that it will be useful, |
| + | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + | GNU General Public License for more details. |
| + | |
| + | You should have received a copy of the GNU General Public License |
| + | along with this program; if not, write to the Free Software |
| + | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| + | |
| + | Also add information on how to contact you by electronic and paper mail. |
| + | |
| + | If the program is interactive, make it output a short notice like this |
| + | when it starts in an interactive mode: |
| + | |
| + | Gnomovision version 69, Copyright (C) 19yy name of author |
| + | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |
| + | This is free software, and you are welcome to redistribute it |
| + | under certain conditions; type `show c' for details. |
| + | |
| + | The hypothetical commands `show w' and `show c' should show the appropriate |
| + | parts of the General Public License. Of course, the commands you use may |
| + | be called something other than `show w' and `show c'; they could even be |
| + | mouse-clicks or menu items--whatever suits your program. |
| + | |
| + | You should also get your employer (if you work as a programmer) or your |
| + | school, if any, to sign a "copyright disclaimer" for the program, if |
| + | necessary. Here is a sample; alter the names: |
| + | |
| + | Yoyodyne, Inc., hereby disclaims all copyright interest in the program |
| + | `Gnomovision' (which makes passes at compilers) written by James Hacker. |
| + | |
| + | <signature of Ty Coon>, 1 April 1989 |
| + | Ty Coon, President of Vice |
| + | |
| + | This General Public License does not permit incorporating your program into |
| + | proprietary programs. If your program is a subroutine library, you may |
| + | consider it more useful to permit linking proprietary applications with the |
| + | library. If this is what you want to do, use the GNU Library General |
| + | Public License instead of this License. |
| @@ -0,0 +1,246 @@ | |
| + | 0.9.8 (pending) |
| + | + udhcp now fits nicely into busybox |
| + | (Glenn McGrath <[email protected]> as well as myself) |
| + | + updated client manpage (me) |
| + | + both client and server now use sockets for signal handling, |
| + | hopefully, this will be the last needed change in signal |
| + | handling, I'm fairly certain all the possible races are now |
| + | closed. (me) |
| + | + The server now restarts the auto_time timer when it receives |
| + | a SIGUSR1 (write out config file). (me) |
| + | + Improve signal handling (David Poole) |
| + | + Fix to config file parsing (Matt Kraai) |
| + | + Fix load lease logic (me) |
| + | + Fix clear_lease logic (me) |
| + | + -h is now an alias for -H (udhcp bug #1253) |
| + | + Shorter timeout on not receiving offers (me) |
| + | + Improved signal behavior by client (me) |
| + | + Would never assign end address (Keith Smith <[email protected]>) |
| + | + Was improperly reporting yiaddr as siaddr ([email protected]) |
| + | udhcp bug#1256 |
| + | + Fixed reading of client id (David Poole <[email protected]>) |
| + | + change sys_errlist[] to strerror() as it aparently doesn't exist |
| + | (andersee <[email protected]>) |
| + | + fixed get_raw_packet so it returns -2 on non fatal errors |
| + | (Ted Lemon <[email protected]>) |
| + | + Improved (hopefully) NAKing behavior (me) |
| + | + Added -b option (Jouni Malinen) |
| + | + Compute checksums correctly on big endian hosts |
| + | (Jouni Malinen <[email protected]>) |
| + | |
| + | 0.9.7 (020526) |
| + | + Use add_lease in read_leases, sanitizes leases more, and clears out exprired |
| + | ones if there is no more room (me) |
| + | + Moved udhcpd.leases to /var/lib/misc/udhcpd.leases (Debian bug #147747) |
| + | + Change (obsolete) AF_INET in arping.c to PF_PACKET (Debian bug #127049) |
| + | + Added script hook for DHCPNAK (nak), as well as providing the message option |
| + | (me) |
| + | + Generate the paramaters request list by seeing what options in options.c are |
| + | ored with OPTION_REQ in options.c |
| + | + Fix dhcp renew forgetfullness on client (bug #1230) |
| + | + Fix dhcp release bug on client (bug #1231) |
| + | + Set option request list for DHCP renew (bug #1233) |
| + | + Set BOOTREQUEST/REPLY properly |
| + | + Change client-identifier field to popularly expected behavior (me) |
| + | + Only reopen port on errors (me) |
| + | + Change fork/close/setsid structures to daemon() (me) |
| + | + Allow user to specify udhcpd config file at run time (Steven, me) |
| + | + Write pidfile after changing it (Steven CTR Carr <[email protected]>) |
| + | + Added env var docs to udhcpc man page (Matt) |
| + | + Standardized lowercase udhcp in documentation (me) |
| + | + Accept packets without a UDP checksum (me) |
| + | + Accept packets with extra garbage (me) |
| + | + Better error handling in files.c (me) |
| + | + Combined read_interface function to reduce COMBINED_BINARY size (me) |
| + | + Drop calc_length(), some servers choke on smaller packets (me) |
| + | + Try to clean some fat out (me) |
| + | |
| + | 0.9.6 (011001) |
| + | + Added bootp paramaters to server (me) |
| + | + Added bootp paramaters to client (me) |
| + | + Added vendor id to client (me) |
| + | + Better pidfile handling in client and server (me) |
| + | + Added man pages (Matt Kraai <[email protected]>) |
| + | |
| + | 0.9.5 (010914) |
| + | + Fixed $HOME and $PATH env passing (me) |
| + | + Fixed client to only listen for raw packets on correct interface (me) |
| + | + added --quit,-q option to quit after a lease is obtained (me) |
| + | + Fixed 100% CPU utilization by client when interface is down (me) |
| + | |
| + | 0.9.4 (010827) |
| + | + Force broadcast to broken clients that request unicast (ie, MSFT 98) |
| + | + Make install rules (Adam J. Richter <[email protected]>) |
| + | + One scripts, instead of many (Adam) |
| + | + Removed script paramater info files (env vars only) (Adam) |
| + | + Controlling of forking behavior in client (Adam) |
| + | + General script.c/dhcpc.c cleanups (Adam) |
| + | |
| + | 0.9.3 (010820) |
| + | + Increased debugging verbosity (me) |
| + | + Cut trailing whitespace when reading config file (me) |
| + | + added hostname option to client (me) |
| + | + fixed a strncpy bug in script.c (me) |
| + | + fixed a leaky socket in dhcpc.c (me) |
| + | + fixed a leaky socket in dhcpd.c (me) |
| + | |
| + | 0.9.2 (010810) |
| + | + Added raw sockets to client (me) |
| + | + alignment fixes (Mark Huang) |
| + | + compiler warning fixes (Mark Huang) |
| + | + client now sends parameter list (Mark Huang/me) |
| + | + added ipttl option |
| + | + Does now not request broadcast packets |
| + | |
| + | 0.9.1 (010806) |
| + | + Added udhcpc client |
| + | + reorganized functions/files |
| + | + listening socket now only binds to one interface |
| + | |
| + | 0.9.0 (010720) Major rewrite, current changes, goals: |
| + | + should not segfault on bogus packets. |
| + | + Options can be read from sname and file fields. |
| + | + supports all DHCP messages (release, decline, inform). |
| + | + IP block is now specified by a range of IP's. |
| + | + Leases file now contains lease time (relative, or absolute). |
| + | + Just about any DHCP option is now supported. |
| + | + DNS entries are no longer read from resolv.conf |
| + | + Lease file can be written periodically when the process receives a SIGUSR1 |
| + | + arpping should be supported on all arches. |
| + | + support for DHCP relays. |
| + | + DHCP messages can be unicast if the client requests it. |
| + | + many, many, many other things. |
| + | |
| + | 0.8.29 (000323) |
| + | + stable(?) release |
| + | |
| + | |
| + | 0.8.28 (000323) |
| + | + removed alarm as it was causing server to go down |
| + | + removed debugging |
| + | + break down dhcpd.c into manageable files |
| + | |
| + | |
| + | 0.8.27 (000221) |
| + | + OFFER also sends gateway/subnet (for picky dhcp clients) |
| + | + multiple DNS now handled from resolv.conf if available |
| + | + multiple WINS (from dhcpd.conf) |
| + | |
| + | 0.8.25 (000120) |
| + | + now compiles *and* runs on a generic linux system |
| + | tested with a windows 98 client and the sample config |
| + | files in the samples directory. |
| + | |
| + | 0.8.24 (000117) |
| + | + makeiplist tool has basic functionality in place |
| + | + new sample config files |
| + | + route add -host 255.255.255.255 dev eth0 added for generic linux |
| + | |
| + | 0.8.23 (000117) |
| + | + NETtel specific fix for ignoring dhcp requests on 2nd interface |
| + | |
| + | 0.8.22 (000113) |
| + | + minor changes to compile under a generic linux system |
| + | + minor config file location changes for a generic linux system |
| + | + makeiplist fixes.. still incomplete.. but etting closer |
| + | |
| + | 0.8.21 (000113) |
| + | + now sends the correct server ip instead of hardcoded value |
| + | + minor debugging fixes for critical messages |
| + | |
| + | 0.8.20 (000106) |
| + | + cut out dhcp server checking.. this was causing dialout ppp |
| + | sessions with idle time set to never time out. |
| + | + also removed the 10 second pause before launching.. as this |
| + | was originally to stop it replying to a dhcp client |
| + | on a NETtel which was really a bad way to do it in the |
| + | first place :-) |
| + | |
| + | 0.8.19 (000104) |
| + | + fixes for route add -host on a machine that needs to run both |
| + | a DHCP client and server (dual eth box) |
| + | |
| + | 0.8.18 (991220) |
| + | |
| + | + Race conditions fixed by disabling alarm whilst the server is busy |
| + | + Fixed continous clearing of the offered array so that it is only cleared |
| + | when it is dirty - (could change the position of when dirty is set) |
| + | |
| + | 0.8.17 (991212) |
| + | |
| + | - has problems clearing out the offered array |
| + | |
| + | 0.8.16 (991203) |
| + | + Non blocking error is changes to informational as it is not really |
| + | an error |
| + | |
| + | 0.8.15 (991129) |
| + | + Servs the dns field 3 times (Nettel only) so that windows servers |
| + | dont time out whilst nettel is booting |
| + | |
| + | 0.8.14 (991126) |
| + | + added owner check for the offered array so clean out time may be |
| + | increased |
| + | + added new func to print out chadder/MAC |
| + | |
| + | 0.8.13 (991125) |
| + | + added win95 support (w95 changed xid halfway through conversation) |
| + | + had to change the offered array to use hardware addresses instead of xid |
| + | + fixed re offered bug |
| + | + added more debugging |
| + | |
| + | 0.8.12 (991111) |
| + | + debugging was real bad.. cleaned up a bit.. needs overhaul |
| + | |
| + | |
| + | 0.8.11 (991110) |
| + | + fixed up offeredAddr array to actually be used now!! offeredAddr is |
| + | used to see if another simultaneous connecting client was offered |
| + | an address that we are about to offer another client (multiple |
| + | client bug) |
| + | + removed re_offered variable as it breaks multiple client support |
| + | + added lease time to ACK -- doesn't work if in OFFER |
| + | + decreased internal array clear delay to 60 seconds |
| + | + minor findAddr bug (returning -1 instead of 0) |
| + | + if clients xid already in offeredAddr offer the same addr and don't add a |
| + | new addr to offered (caused by a client issuing multiple DISCOVERs) |
| + | |
| + | 0.8.10 (991105) |
| + | + \n bug in arpping |
| + | + minor debugging changes (removed printfs etc) |
| + | + started browseiplist (not finished) |
| + | |
| + | 0.8.9 (19991105) |
| + | + fixed options array size bug (options were cut off) |
| + | |
| + | 0.8.8 (19991105) |
| + | + ignores requests from dhcpcd on the same machine |
| + | |
| + | 0.8.7 (19991104) |
| + | + don't die if we can't bind to search for existing DHCP server |
| + | + slightly more verbose syslogging |
| + | |
| + | 0.8.6 (19991103) |
| + | + added makeiplist (not finished -- core dumps) |
| + | + minor debug changes |
| + | |
| + | 0.8.5 (19991029) |
| + | + exits if another DHCP server is already on the network |
| + | + added Linux Makefile |
| + | |
| + | 0.8.4 (19991026) |
| + | + minor bug fix in findaddr preventing an addr being found |
| + | |
| + | 0.8.3 (19991025) |
| + | + fixed up debugging |
| + | + minor hwaddr issues |
| + | |
| + | 0.8.2 (19991022) |
| + | + free leases (new arpping code from dhcpcd) |
| + | + fixed bug where crashes if no leases/iplist file |
| + | + syslogging and debugging switch |
| + | + serve DNS from resolv.conf |
| + | + fixed bug where new lease added if same mac offered |
| + | + now checks the ip is free b4 offering |
| + | + now supports wins server |
| + | |
| @@ -0,0 +1,46 @@ | |
| + | # Makefile for busybox |
| + | # |
| + | # Copyright (C) 1999-2002 Erik Andersen <[email protected]> |
| + | # |
| + | # This program is free software; you can redistribute it and/or modify |
| + | # it under the terms of the GNU General Public License as published by |
| + | # the Free Software Foundation; either version 2 of the License, or |
| + | # (at your option) any later version. |
| + | # |
| + | # This program is distributed in the hope that it will be useful, |
| + | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| + | # General Public License for more details. |
| + | # |
| + | # You should have received a copy of the GNU General Public License |
| + | # along with this program; if not, write to the Free Software |
| + | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| + | # |
| + | |
| + | UDHCP_AR:=udhcp.a |
| + | ifndef $(UDHCP_DIR) |
| + | UDHCP_DIR:=$(TOPDIR)networking/udhcp/ |
| + | endif |
| + | |
| + | #ok, so I forgot how to do an or, but this is a quick and dirty hack |
| + | ifeq ($(CONFIG_UDHCPC), y) |
| + | CONFIG_UDHCP_SHARED=y |
| + | else |
| + | ifeq ($(CONFIG_UDHCPD), y) |
| + | CONFIG_UDHCP_SHARED=y |
| + | else |
| + | CONFIG_UDHCP_SHARED=n |
| + | endif |
| + | endif |
| + | |
| + | UDHCP-y:= |
| + | UDHCP-$(CONFIG_UDHCP_SHARED) += options.o socket.o packet.o pidfile.o |
| + | UDHCP-$(CONFIG_UDHCPC) += dhcpc.o clientpacket.o script.o |
| + | UDHCP-$(CONFIG_UDHCPD) += dhcpd.o arpping.o files.o leases.o serverpacket.o |
| + | UDHCP-$(CONFIG_DUMPLEASES) += dumpleases.o |
| + | |
| + | libraries-y+=$(UDHCP_DIR)$(UDHCP_AR) |
| + | |
| + | $(UDHCP_DIR)$(UDHCP_AR): $(patsubst %,$(UDHCP_DIR)%, $(UDHCP-y)) |
| + | $(AR) -ro $@ $(patsubst %,$(UDHCP_DIR)%, $(UDHCP-y)) |
| + | |
| @@ -0,0 +1,197 @@ | |
| + | udhcp server/client package readme |
| + | ------------------------- |
| + | |
| + | The udhcp server/client package is primarily geared towards embedded |
| + | systems. It does however, strive to be fully functional, and RFC |
| + | compliant. |
| + | |
| + | udhcp server (udhcpd) |
| + | -------------------- |
| + | |
| + | The only command line argument to udhcpd is an optional specifed |
| + | config file. If no config file is specified, udhcpd uses the default |
| + | config file, /etc/udhcpd.conf. Ex: |
| + | |
| + | udhcpd /etc/udhcpd.eth1.conf |
| + | |
| + | The udhcp server employs a number of simple config files: |
| + | |
| + | udhcpd.leases |
| + | ------------ |
| + | |
| + | The udhcpd.leases behavior is designed for an embedded system. The |
| + | file is written either every auto_time seconds, or when a SIGUSR1 |
| + | is received (the auto_time timer restarts if a SIGUSR1 is received). |
| + | If you send a SIGTERM to udhcpd directly after a SIGUSR1, udhcpd will |
| + | finish writing the leases file and wait for the aftermentioned script |
| + | to be executed and finish before quiting, so you do not need to sleep |
| + | between sending signals. When the file is written, a script can be |
| + | optionally called to commit the file to flash. Lease times are stored |
| + | in the file by time remaining in lease (for systems without clock |
| + | that works when there is no power), or by the absolute time that it |
| + | expires in seconds from epoch. In the remainig format, expired leases |
| + | are stored as zero. The file is of the format: |
| + | |
| + | 16 byte MAC |
| + | 4 byte ip address |
| + | u32 expire time |
| + | 16 byte MAC |
| + | 4 byte ip address |
| + | u32 expire time |
| + | . |
| + | etc. |
| + | |
| + | example: hexdump udhcpd.leases |
| + | |
| + | 0000000 1000 c95a 27d9 0000 0000 0000 0000 0000 |
| + | 0000010 a8c0 150a 0d00 2d29 5000 23fc 8566 0000 |
| + | 0000020 0000 0000 0000 0000 a8c0 140a 0d00 4e29 |
| + | 0000030 |
| + | |
| + | |
| + | udhcpd.conf |
| + | ---------- |
| + | |
| + | The format is fairly simple, there is a sample file with all the |
| + | available options and comments describing them in samples/udhcpd.conf |
| + | |
| + | |
| + | udhcp client (udhcpc) |
| + | -------------------- |
| + | |
| + | The udhcp client negotiates a lease with the DHCP server and notifies |
| + | a set of scripts when a leases is obtained or lost. The command line |
| + | options for the udhcp client are: |
| + | |
| + | -c, --clientid=CLIENTID Client identifier |
| + | -H, --hostname=HOSTNAME Client hostname |
| + | -h, Alias for -H |
| + | -f, --foreground Do not fork after getting lease |
| + | -b, --background Fork to background if lease cannot be |
| + | immediately negotiated. |
| + | -i, --interface=INTERFACE Interface to use (default: eth0) |
| + | -n, --now Exit with failure if lease cannot be |
| + | immediately negotiated. |
| + | -p, --pidfile=file Store process ID of daemon in file |
| + | -q, --quit Quit after obtaining lease |
| + | -r, --request=IP IP address to request (default: none) |
| + | -s, --script=file Run file at dhcp events (default: |
| + | /usr/share/udhcpc/default.script) |
| + | -v, --version Display version |
| + | |
| + | If the requested IP address cannot be obtained, the client accepts the |
| + | address that the server offers. |
| + | |
| + | When an event occurs, udhcpc calls the action script. The script by |
| + | default is /usr/share/udhcpc/default.script but this can be changed via |
| + | the command line arguments. The three possible arguments to the script |
| + | are: |
| + | |
| + | deconfig: This argument is used when udhcpc starts, and |
| + | when a leases is lost. The script should put the interface in an |
| + | up, but deconfigured state, ie: ifconfig $interface 0.0.0.0. |
| + | |
| + | bound: This argument is used when udhcpc moves from an |
| + | unbound, to a bound state. All of the paramaters are set in |
| + | enviromental variables, The script should configure the interface, |
| + | and set any other relavent parameters (default gateway, dns server, |
| + | etc). |
| + | |
| + | renew: This argument is used when a DHCP lease is renewed. All of |
| + | the paramaters are set in enviromental variables. This argument is |
| + | used when the interface is already configured, so the IP address, |
| + | will not change, however, the other DHCP paramaters, such as the |
| + | default gateway, subnet mask, and dns server may change. |
| + | |
| + | nak: This argument is used with udhcpc receives a NAK message. |
| + | The script with the deconfig argument will be called directly |
| + | afterwards, so no changes to the network interface are neccessary. |
| + | This hook is provided for purely informational purposes (the |
| + | message option may contain a reason for the NAK). |
| + | |
| + | The paramaters for enviromental variables are as follows: |
| + | |
| + | $HOME - The set $HOME env or "/" |
| + | $PATH - the set $PATH env or "/bin:/usr/bin:/sbin:/usr/sbin" |
| + | $1 - What action the script should perform |
| + | interface - The interface this was obtained on |
| + | ip - The obtained IP |
| + | siaddr - The bootp next server option |
| + | sname - The bootp server name option |
| + | boot_file - The bootp boot file option |
| + | subnet - The assigend subnet mask |
| + | timezone - Offset in seconds from UTC |
| + | router - A list of routers |
| + | timesvr - A list of time servers |
| + | namesvr - A list of IEN 116 name servers |
| + | dns - A list of DNS server |
| + | logsvr - A list of MIT-LCS UDP log servers |
| + | cookiesvr - A list of RFC 865 cookie servers |
| + | lprsvr - A list of LPR servers |
| + | hostname - The assigned hostname |
| + | bootsize - The length in 512 octect blocks of the bootfile |
| + | domain - The domain name of the network |
| + | swapsvr - The IP address of the client's swap server |
| + | rootpath - The path name of the client's root disk |
| + | ipttl - The TTL to use for this network |
| + | mtu - The MTU to use for this network |
| + | broadcast - The broadcast address for this network |
| + | ntpsrv - A list of NTP servers |
| + | wins - A list of WINS servers |
| + | lease - The lease time, in seconds |
| + | dhcptype - DHCP message type (safely ignored) |
| + | serverid - The IP of the server |
| + | message - Reason for a DHCPNAK |
| + | tftp - The TFTP server name |
| + | bootfile - The bootfile name |
| + | |
| + | additional options are easily added in options.c. |
| + | |
| + | udhcpc also responds to SIGUSR1 and SIGUSR2. SIGUSR1 will force a renew state, |
| + | and SIGUSR2 will force a release of the current lease, and cause udhcpc to |
| + | go into an inactive state (until it is killed, or receives a SIGUSR1). You do |
| + | not need to sleep between sending signals, as signals received are processed |
| + | sequencially in the order they are received. |
| + | |
| + | |
| + | |
| + | compile time options |
| + | ------------------- |
| + | |
| + | The Makefile contains three of the compile time options: |
| + | |
| + | DEBUG: If DEBUG is defined, udhcpd will output extra debugging |
| + | output, compile with -g, and not fork to the background when run. |
| + | SYSLOG: If SYSLOG is defined, udhcpd will log all its messages |
| + | syslog, otherwise, it will attempt to log them to stdout. |
| + | |
| + | COMBINED_BINARY: If COMBINED_BINARY is define, one binary, udhcpd, |
| + | is created. If called as udhcpd, the dhcp server will be started. |
| + | If called as udhcpc, the dhcp client will be started. |
| + | |
| + | dhcpd.h contains the other two compile time options: |
| + | |
| + | LEASE_TIME: The default lease time if not specified in the config |
| + | file. |
| + | |
| + | DHCPD_CONFIG_FILE: The defualt config file to use. |
| + | |
| + | options.c contains a set of dhcp options for the client: |
| + | |
| + | name[10]: The name of the option as it will appear in scripts |
| + | |
| + | flags: The type of option, as well as if it will be requested |
| + | by the client (OPTION_REQ) |
| + | |
| + | code: The DHCP code for this option |
| + | |
| + | busybox drop-in |
| + | -------------- |
| + | udhcp is now a drop-in component for busybox (http://busybox.net). |
| + | To update busybox to the latest revision, simply do a: |
| + | |
| + | cp *.[ch] README AUTHORS COPYING ChangeLog TODO \ |
| + | <busybox_source>/networking/udhcp |
| + | |
| + | The only two files udhcp does not provide are config.in and |
| + | Makefile.in, so these may need to be updated from time to time. |
| @@ -0,0 +1,17 @@ | |
| + | TODO |
| + | ---- |
| + | + make failure of reading functions revert to previous value, not the default |
| + | + sanity code for option[OPT_LEN] |
| + | + fix aliasing (ie: eth0:0) |
| + | + DONE: Make sure get_raw_packet only accepts packets on the specified interface |
| + | + better standard linux distro support |
| + | + DONE: make config file a command line option for server |
| + | + IMPLEMENTED: make forking a command line option |
| + | + make sure packet generation works on a wide varitey of arches |
| + | + Interoperability testing |
| + | + Hooks within the DHCP server |
| + | + Additional bootp support in client/server |
| + | + Make serverid option in server configurable |
| + | + DONE: cause client to generate DHCP_VENDOR option |
| + | + Possibly add failure message to DHCP NAK |
| + | + Possibly log DHCP NAK failure message in client |
| @@ -0,0 +1,105 @@ | |
| + | /* |
| + | * arpping.c |
| + | * |
| + | * Mostly stolen from: dhcpcd - DHCP client daemon |
| + | * by Yoichi Hariguchi <[email protected]> |
| + | */ |
| + | |
| + | #include <sys/types.h> |
| + | #include <sys/time.h> |
| + | #include <time.h> |
| + | #include <sys/types.h> |
| + | #include <sys/socket.h> |
| + | #include <netinet/if_ether.h> |
| + | #include <net/if_arp.h> |
| + | #include <netinet/in.h> |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #include <unistd.h> |
| + | #include <errno.h> |
| + | |
| + | #include "dhcpd.h" |
| + | #include "debug.h" |
| + | #include "arpping.h" |
| + | |
| + | /* args: yiaddr - what IP to ping |
| + | * ip - our ip |
| + | * mac - our arp address |
| + | * interface - interface to use |
| + | * retn: 1 addr free |
| + | * 0 addr used |
| + | * -1 error |
| + | */ |
| + | |
| + | /* FIXME: match response against chaddr */ |
| + | int arpping(u_int32_t yiaddr, u_int32_t ip, unsigned char *mac, char *interface) |
| + | { |
| + | |
| + | int timeout = 2; |
| + | int optval = 1; |
| + | int s; /* socket */ |
| + | int rv = 1; /* return value */ |
| + | struct sockaddr addr; /* for interface name */ |
| + | struct arpMsg arp; |
| + | fd_set fdset; |
| + | struct timeval tm; |
| + | time_t prevTime; |
| + | |
| + | |
| + | if ((s = socket (PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1) { |
| + | LOG(LOG_ERR, "Could not open raw socket"); |
| + | return -1; |
| + | } |
| + | |
| + | if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) { |
| + | LOG(LOG_ERR, "Could not setsocketopt on raw socket"); |
| + | close(s); |
| + | return -1; |
| + | } |
| + | |
| + | /* send arp request */ |
| + | memset(&arp, 0, sizeof(arp)); |
| + | memcpy(arp.ethhdr.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */ |
| + | memcpy(arp.ethhdr.h_source, mac, 6); /* MAC SA */ |
| + | arp.ethhdr.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */ |
| + | arp.htype = htons(ARPHRD_ETHER); /* hardware type */ |
| + | arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */ |
| + | arp.hlen = 6; /* hardware address length */ |
| + | arp.plen = 4; /* protocol address length */ |
| + | arp.operation = htons(ARPOP_REQUEST); /* ARP op code */ |
| + | *((u_int *) arp.sInaddr) = ip; /* source IP address */ |
| + | memcpy(arp.sHaddr, mac, 6); /* source hardware address */ |
| + | *((u_int *) arp.tInaddr) = yiaddr; /* target IP address */ |
| + | |
| + | memset(&addr, 0, sizeof(addr)); |
| + | strcpy(addr.sa_data, interface); |
| + | if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) |
| + | rv = 0; |
| + | |
| + | /* wait arp reply, and check it */ |
| + | tm.tv_usec = 0; |
| + | time(&prevTime); |
| + | while (timeout > 0) { |
| + | FD_ZERO(&fdset); |
| + | FD_SET(s, &fdset); |
| + | tm.tv_sec = timeout; |
| + | if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) { |
| + | DEBUG(LOG_ERR, "Error on ARPING request: %s", strerror(errno)); |
| + | if (errno != EINTR) rv = 0; |
| + | } else if (FD_ISSET(s, &fdset)) { |
| + | if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0; |
| + | if (arp.operation == htons(ARPOP_REPLY) && |
| + | bcmp(arp.tHaddr, mac, 6) == 0 && |
| + | *((u_int *) arp.sInaddr) == yiaddr) { |
| + | DEBUG(LOG_INFO, "Valid arp reply receved for this address"); |
| + | rv = 0; |
| + | break; |
| + | } |
| + | } |
| + | timeout -= time(NULL) - prevTime; |
| + | time(&prevTime); |
| + | } |
| + | close(s); |
| + | DEBUG(LOG_INFO, "%salid arp replies for this address", rv ? "No v" : "V"); |
| + | return rv; |
| + | } |
| @@ -0,0 +1,30 @@ | |
| + | /* |
| + | * arpping .h |
| + | */ |
| + | |
| + | #ifndef ARPPING_H |
| + | #define ARPPING_H |
| + | |
| + | #include <netinet/if_ether.h> |
| + | #include <net/if_arp.h> |
| + | #include <net/if.h> |
| + | #include <netinet/in.h> |
| + | |
| + | struct arpMsg { |
| + | struct ethhdr ethhdr; /* Ethernet header */ |
| + | u_short htype; /* hardware type (must be ARPHRD_ETHER) */ |
| + | u_short ptype; /* protocol type (must be ETH_P_IP) */ |
| + | u_char hlen; /* hardware address length (must be 6) */ |
| + | u_char plen; /* protocol address length (must be 4) */ |
| + | u_short operation; /* ARP opcode */ |
| + | u_char sHaddr[6]; /* sender's hardware address */ |
| + | u_char sInaddr[4]; /* sender's IP address */ |
| + | u_char tHaddr[6]; /* target's hardware address */ |
| + | u_char tInaddr[4]; /* target's IP address */ |
| + | u_char pad[18]; /* pad for min. Ethernet payload (60 bytes) */ |
| + | }; |
| + | |
| + | /* function prototypes */ |
| + | int arpping(u_int32_t yiaddr, u_int32_t ip, unsigned char *arp, char *interface); |
| + | |
| + | #endif |
| @@ -0,0 +1,239 @@ | |
| + | /* clientpacket.c |
| + | * |
| + | * Packet generation and dispatching functions for the DHCP client. |
| + | * |
| + | * Russ Dill <[email protected]> July 2001 |
| + | * |
| + | * This program is free software; you can redistribute it and/or modify |
| + | * it under the terms of the GNU General Public License as published by |
| + | * the Free Software Foundation; either version 2 of the License, or |
| + | * (at your option) any later version. |
| + | * |
| + | * This program is distributed in the hope that it will be useful, |
| + | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + | * GNU General Public License for more details. |
| + | * |
| + | * You should have received a copy of the GNU General Public License |
| + | * along with this program; if not, write to the Free Software |
| + | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| + | */ |
| + | |
| + | #include <string.h> |
| + | #include <sys/socket.h> |
| + | #include <features.h> |
| + | #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 |
| + | #include <netpacket/packet.h> |
| + | #include <net/ethernet.h> |
| + | #else |
| + | #include <asm/types.h> |
| + | #include <linux/if_packet.h> |
| + | #include <linux/if_ether.h> |
| + | #endif |
| + | #include <stdlib.h> |
| + | #include <time.h> |
| + | #include <unistd.h> |
| + | #include <netinet/in.h> |
| + | #include <arpa/inet.h> |
| + | |
| + | |
| + | #include "dhcpd.h" |
| + | #include "packet.h" |
| + | #include "options.h" |
| + | #include "dhcpc.h" |
| + | #include "debug.h" |
| + | |
| + | |
| + | /* Create a random xid */ |
| + | unsigned long random_xid(void) |
| + | { |
| + | static int initialized; |
| + | if (!initialized) { |
| + | srand(time(0)); |
| + | initialized++; |
| + | } |
| + | return rand(); |
| + | } |
| + | |
| + | |
| + | /* initialize a packet with the proper defaults */ |
| + | static void init_packet(struct dhcpMessage *packet, char type) |
| + | { |
| + | struct vendor { |
| + | char vendor, length; |
| + | char str[sizeof("udhcp "VERSION)]; |
| + | } vendor_id = { DHCP_VENDOR, sizeof("udhcp "VERSION) - 1, "udhcp "VERSION}; |
| + | |
| + | init_header(packet, type); |
| + | memcpy(packet->chaddr, client_config.arp, 6); |
| + | add_option_string(packet->options, client_config.clientid); |
| + | if (client_config.hostname) add_option_string(packet->options, client_config.hostname); |
| + | add_option_string(packet->options, (unsigned char *) &vendor_id); |
| + | } |
| + | |
| + | |
| + | /* Add a paramater request list for stubborn DHCP servers. Pull the data |
| + | * from the struct in options.c. Don't do bounds checking here because it |
| + | * goes towards the head of the packet. */ |
| + | static void add_requests(struct dhcpMessage *packet) |
| + | { |
| + | int end = end_option(packet->options); |
| + | int i, len = 0; |
| + | |
| + | packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; |
| + | for (i = 0; options[i].code; i++) |
| + | if (options[i].flags & OPTION_REQ) |
| + | packet->options[end + OPT_DATA + len++] = options[i].code; |
| + | packet->options[end + OPT_LEN] = len; |
| + | packet->options[end + OPT_DATA + len] = DHCP_END; |
| + | |
| + | } |
| + | |
| + | |
| + | /* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ |
| + | int send_discover(unsigned long xid, unsigned long requested) |
| + | { |
| + | struct dhcpMessage packet; |
| + | |
| + | init_packet(&packet, DHCPDISCOVER); |
| + | packet.xid = xid; |
| + | if (requested) |
| + | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); |
| + | |
| + | add_requests(&packet); |
| + | LOG(LOG_DEBUG, "Sending discover..."); |
| + | return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, |
| + | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); |
| + | } |
| + | |
| + | |
| + | /* Broadcasts a DHCP request message */ |
| + | int send_selecting(unsigned long xid, unsigned long server, unsigned long requested) |
| + | { |
| + | struct dhcpMessage packet; |
| + | struct in_addr addr; |
| + | |
| + | init_packet(&packet, DHCPREQUEST); |
| + | packet.xid = xid; |
| + | |
| + | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); |
| + | add_simple_option(packet.options, DHCP_SERVER_ID, server); |
| + | |
| + | add_requests(&packet); |
| + | addr.s_addr = requested; |
| + | LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr)); |
| + | return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, |
| + | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); |
| + | } |
| + | |
| + | |
| + | /* Unicasts or broadcasts a DHCP renew message */ |
| + | int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr) |
| + | { |
| + | struct dhcpMessage packet; |
| + | int ret = 0; |
| + | |
| + | init_packet(&packet, DHCPREQUEST); |
| + | packet.xid = xid; |
| + | packet.ciaddr = ciaddr; |
| + | |
| + | add_requests(&packet); |
| + | LOG(LOG_DEBUG, "Sending renew..."); |
| + | if (server) |
| + | ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); |
| + | else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, |
| + | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); |
| + | return ret; |
| + | } |
| + | |
| + | |
| + | /* Unicasts a DHCP release message */ |
| + | int send_release(unsigned long server, unsigned long ciaddr) |
| + | { |
| + | struct dhcpMessage packet; |
| + | |
| + | init_packet(&packet, DHCPRELEASE); |
| + | packet.xid = random_xid(); |
| + | packet.ciaddr = ciaddr; |
| + | |
| + | add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr); |
| + | add_simple_option(packet.options, DHCP_SERVER_ID, server); |
| + | |
| + | LOG(LOG_DEBUG, "Sending release..."); |
| + | return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); |
| + | } |
| + | |
| + | |
| + | /* return -1 on errors that are fatal for the socket, -2 for those that aren't */ |
| + | int get_raw_packet(struct dhcpMessage *payload, int fd) |
| + | { |
| + | int bytes; |
| + | struct udp_dhcp_packet packet; |
| + | u_int32_t source, dest; |
| + | u_int16_t check; |
| + | |
| + | memset(&packet, 0, sizeof(struct udp_dhcp_packet)); |
| + | bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet)); |
| + | if (bytes < 0) { |
| + | DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring"); |
| + | usleep(500000); /* possible down interface, looping condition */ |
| + | return -1; |
| + | } |
| + | |
| + | if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { |
| + | DEBUG(LOG_INFO, "message too short, ignoring"); |
| + | return -2; |
| + | } |
| + | |
| + | if (bytes < ntohs(packet.ip.tot_len)) { |
| + | DEBUG(LOG_INFO, "Truncated packet"); |
| + | return -2; |
| + | } |
| + | |
| + | /* ignore any extra garbage bytes */ |
| + | bytes = ntohs(packet.ip.tot_len); |
| + | |
| + | /* Make sure its the right packet for us, and that it passes sanity checks */ |
| + | if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION || |
| + | packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) || |
| + | bytes > (int) sizeof(struct udp_dhcp_packet) || |
| + | ntohs(packet.udp.len) != (short) (bytes - sizeof(packet.ip))) { |
| + | DEBUG(LOG_INFO, "unrelated/bogus packet"); |
| + | return -2; |
| + | } |
| + | |
| + | /* check IP checksum */ |
| + | check = packet.ip.check; |
| + | packet.ip.check = 0; |
| + | if (check != checksum(&(packet.ip), sizeof(packet.ip))) { |
| + | DEBUG(LOG_INFO, "bad IP header checksum, ignoring"); |
| + | return -1; |
| + | } |
| + | |
| + | /* verify the UDP checksum by replacing the header with a psuedo header */ |
| + | source = packet.ip.saddr; |
| + | dest = packet.ip.daddr; |
| + | check = packet.udp.check; |
| + | packet.udp.check = 0; |
| + | memset(&packet.ip, 0, sizeof(packet.ip)); |
| + | |
| + | packet.ip.protocol = IPPROTO_UDP; |
| + | packet.ip.saddr = source; |
| + | packet.ip.daddr = dest; |
| + | packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */ |
| + | if (check && check != checksum(&packet, bytes)) { |
| + | DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring"); |
| + | return -2; |
| + | } |
| + | |
| + | memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp))); |
| + | |
| + | if (ntohl(payload->cookie) != DHCP_MAGIC) { |
| + | LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring"); |
| + | return -2; |
| + | } |
| + | DEBUG(LOG_INFO, "oooooh!!! got some!"); |
| + | return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); |
| + | |
| + | } |
| + | |
| @@ -0,0 +1,12 @@ | |
| + | #ifndef _CLIENTPACKET_H |
| + | #define _CLIENTPACKET_H |
| + | |
| + | unsigned long random_xid(void); |
| + | int send_discover(unsigned long xid, unsigned long requested); |
| + | int send_selecting(unsigned long xid, unsigned long server, unsigned long requested); |
| + | int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); |
| + | int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); |
| + | int send_release(unsigned long server, unsigned long ciaddr); |
| + | int get_raw_packet(struct dhcpMessage *payload, int fd); |
| + | |
| + | #endif |
| @@ -0,0 +1,18 @@ | |
| + | # |
| + | # For a description of the syntax of this configuration file, |
| + | # see scripts/kbuild/config-language.txt. |
| + | # |
| + | |
| + | mainmenu_option next_comment |
| + | comment 'udhcp Server/Client' |
| + | |
| + | bool 'udhcp Server (udhcpd)' CONFIG_UDHCPD |
| + | bool 'udhcp Client (udhcpc)' CONFIG_UDHCPC |
| + | bool 'Lease display utility (dumpleases)' CONFIG_DUMPLEASES |
| + | if [ "$CONFIG_UDHCPD" = "y" -o "$CONFIG_UDHCPC" = "y" ]; then |
| + | bool ' Log udhcp messages to syslog (instead of stdout)' CONFIG_FEATURE_UDHCP_SYSLOG |
| + | bool ' Compile udhcp with noisy debugging messages' CONFIG_FEATURE_UDHCP_DEBUG |
| + | fi |
| + | |
| + | endmenu |
| + | |
| @@ -0,0 +1,41 @@ | |
| + | #ifndef _DEBUG_H |
| + | #define _DEBUG_H |
| + | |
| + | #include "libbb_udhcp.h" |
| + | |
| + | #include <stdio.h> |
| + | #ifdef SYSLOG |
| + | #include <syslog.h> |
| + | #endif |
| + | |
| + | |
| + | #ifdef SYSLOG |
| + | # define LOG(level, str, args...) do { printf(str, ## args); \ |
| + | printf("\n"); \ |
| + | syslog(level, str, ## args); } while(0) |
| + | # define OPEN_LOG(name) openlog(name, 0, 0) |
| + | #define CLOSE_LOG() closelog() |
| + | #else |
| + | # define LOG_EMERG "EMERGENCY!" |
| + | # define LOG_ALERT "ALERT!" |
| + | # define LOG_CRIT "critical!" |
| + | # define LOG_WARNING "warning" |
| + | # define LOG_ERR "error" |
| + | # define LOG_INFO "info" |
| + | # define LOG_DEBUG "debug" |
| + | # define LOG(level, str, args...) do { printf("%s, ", level); \ |
| + | printf(str, ## args); \ |
| + | printf("\n"); } while(0) |
| + | # define OPEN_LOG(name) do {;} while(0) |
| + | #define CLOSE_LOG() do {;} while(0) |
| + | #endif |
| + | |
| + | #ifdef DEBUG |
| + | # undef DEBUG |
| + | # define DEBUG(level, str, args...) LOG(level, str, ## args) |
| + | # define DEBUGGING |
| + | #else |
| + | # define DEBUG(level, str, args...) do {;} while(0) |
| + | #endif |
| + | |
| + | #endif |
| @@ -0,0 +1,559 @@ | |
| + | /* dhcpc.c |
| + | * |
| + | * udhcp DHCP client |
| + | * |
| + | * Russ Dill <[email protected]> July 2001 |
| + | * |
| + | * This program is free software; you can redistribute it and/or modify |
| + | * it under the terms of the GNU General Public License as published by |
| + | * the Free Software Foundation; either version 2 of the License, or |
| + | * (at your option) any later version. |
| + | * |
| + | * This program is distributed in the hope that it will be useful, |
| + | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + | * GNU General Public License for more details. |
| + | * |
| + | * You should have received a copy of the GNU General Public License |
| + | * along with this program; if not, write to the Free Software |
| + | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <sys/time.h> |
| + | #include <sys/types.h> |
| + | #include <sys/file.h> |
| + | #include <unistd.h> |
| + | #include <getopt.h> |
| + | #include <stdlib.h> |
| + | #include <sys/socket.h> |
| + | #include <netinet/in.h> |
| + | #include <arpa/inet.h> |
| + | #include <signal.h> |
| + | #include <time.h> |
| + | #include <string.h> |
| + | #include <sys/ioctl.h> |
| + | #include <net/if.h> |
| + | #include <errno.h> |
| + | |
| + | #include "dhcpd.h" |
| + | #include "dhcpc.h" |
| + | #include "options.h" |
| + | #include "clientpacket.h" |
| + | #include "packet.h" |
| + | #include "script.h" |
| + | #include "socket.h" |
| + | #include "debug.h" |
| + | #include "pidfile.h" |
| + | |
| + | static int state; |
| + | static unsigned long requested_ip; /* = 0 */ |
| + | static unsigned long server_addr; |
| + | static unsigned long timeout; |
| + | static int packet_num; /* = 0 */ |
| + | static int fd; |
| + | static int signal_pipe[2]; |
| + | |
| + | #define LISTEN_NONE 0 |
| + | #define LISTEN_KERNEL 1 |
| + | #define LISTEN_RAW 2 |
| + | static int listen_mode; |
| + | |
| + | #define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" |
| + | |
| + | struct client_config_t client_config = { |
| + | /* Default options. */ |
| + | abort_if_no_lease: 0, |
| + | foreground: 0, |
| + | quit_after_lease: 0, |
| + | background_if_no_lease: 0, |
| + | interface: "eth0", |
| + | pidfile: NULL, |
| + | script: DEFAULT_SCRIPT, |
| + | clientid: NULL, |
| + | hostname: NULL, |
| + | ifindex: 0, |
| + | arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */ |
| + | }; |
| + | |
| + | #ifndef BB_VER |
| + | static void show_usage(void) |
| + | { |
| + | printf( |
| + | "Usage: udhcpc [OPTIONS]\n\n" |
| + | " -c, --clientid=CLIENTID Client identifier\n" |
| + | " -H, --hostname=HOSTNAME Client hostname\n" |
| + | " -h Alias for -H\n" |
| + | " -f, --foreground Do not fork after getting lease\n" |
| + | " -b, --background Fork to background if lease cannot be\n" |
| + | " immediately negotiated.\n" |
| + | " -i, --interface=INTERFACE Interface to use (default: eth0)\n" |
| + | " -n, --now Exit with failure if lease cannot be\n" |
| + | " immediately negotiated.\n" |
| + | " -p, --pidfile=file Store process ID of daemon in file\n" |
| + | " -q, --quit Quit after obtaining lease\n" |
| + | " -r, --request=IP IP address to request (default: none)\n" |
| + | " -s, --script=file Run file at dhcp events (default:\n" |
| + | " " DEFAULT_SCRIPT ")\n" |
| + | " -v, --version Display version\n" |
| + | ); |
| + | exit(0); |
| + | } |
| + | #endif |
| + | |
| + | |
| + | /* just a little helper */ |
| + | static void change_mode(int new_mode) |
| + | { |
| + | DEBUG(LOG_INFO, "entering %s listen mode", |
| + | new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); |
| + | close(fd); |
| + | fd = -1; |
| + | listen_mode = new_mode; |
| + | } |
| + | |
| + | |
| + | /* perform a renew */ |
| + | static void perform_renew(void) |
| + | { |
| + | LOG(LOG_INFO, "Performing a DHCP renew"); |
| + | switch (state) { |
| + | case RENEWING: |
| + | run_script(NULL, "deconfig"); |
| + | case BOUND: |
| + | case REBINDING: |
| + | change_mode(LISTEN_KERNEL); |
| + | state = RENEW_REQUESTED; |
| + | break; |
| + | case RENEW_REQUESTED: |
| + | case REQUESTING: |
| + | case RELEASED: |
| + | change_mode(LISTEN_RAW); |
| + | state = INIT_SELECTING; |
| + | break; |
| + | case INIT_SELECTING: |
| + | } |
| + | |
| + | /* start things over */ |
| + | packet_num = 0; |
| + | |
| + | /* Kill any timeouts because the user wants this to hurry along */ |
| + | timeout = 0; |
| + | } |
| + | |
| + | |
| + | /* perform a release */ |
| + | static void perform_release(void) |
| + | { |
| + | char buffer[16]; |
| + | struct in_addr temp_addr; |
| + | |
| + | /* send release packet */ |
| + | if (state == BOUND || state == RENEWING || state == REBINDING) { |
| + | temp_addr.s_addr = server_addr; |
| + | sprintf(buffer, "%s", inet_ntoa(temp_addr)); |
| + | temp_addr.s_addr = requested_ip; |
| + | LOG(LOG_INFO, "Unicasting a release of %s to %s", |
| + | inet_ntoa(temp_addr), buffer); |
| + | send_release(server_addr, requested_ip); /* unicast */ |
| + | run_script(NULL, "deconfig"); |
| + | } |
| + | LOG(LOG_INFO, "Entering released state"); |
| + | |
| + | change_mode(LISTEN_NONE); |
| + | state = RELEASED; |
| + | timeout = 0x7fffffff; |
| + | } |
| + | |
| + | |
| + | /* Exit and cleanup */ |
| + | static void exit_client(int retval) |
| + | { |
| + | pidfile_delete(client_config.pidfile); |
| + | CLOSE_LOG(); |
| + | exit(retval); |
| + | } |
| + | |
| + | |
| + | /* Signal handler */ |
| + | static void signal_handler(int sig) |
| + | { |
| + | if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) { |
| + | LOG(LOG_ERR, "Could not send signal: %s", |
| + | strerror(errno)); |
| + | } |
| + | } |
| + | |
| + | |
| + | static void background(void) |
| + | { |
| + | int pid_fd; |
| + | |
| + | pid_fd = pidfile_acquire(client_config.pidfile); /* hold lock during fork. */ |
| + | while (pid_fd >= 0 && pid_fd < 3) pid_fd = dup(pid_fd); /* don't let daemon close it */ |
| + | if (daemon(0, 0) == -1) { |
| + | perror("fork"); |
| + | exit_client(1); |
| + | } |
| + | client_config.foreground = 1; /* Do not fork again. */ |
| + | pidfile_write_release(pid_fd); |
| + | } |
| + | |
| + | |
| + | #ifdef COMBINED_BINARY |
| + | int udhcpc_main(int argc, char *argv[]) |
| + | #else |
| + | int main(int argc, char *argv[]) |
| + | #endif |
| + | { |
| + | unsigned char *temp, *message; |
| + | unsigned long t1 = 0, t2 = 0, xid = 0; |
| + | unsigned long start = 0, lease; |
| + | fd_set rfds; |
| + | int retval; |
| + | struct timeval tv; |
| + | int c, len; |
| + | struct dhcpMessage packet; |
| + | struct in_addr temp_addr; |
| + | int pid_fd; |
| + | time_t now; |
| + | int max_fd; |
| + | int sig; |
| + | |
| + | static struct option arg_options[] = { |
| + | {"clientid", required_argument, 0, 'c'}, |
| + | {"foreground", no_argument, 0, 'f'}, |
| + | {"background", no_argument, 0, 'b'}, |
| + | {"hostname", required_argument, 0, 'H'}, |
| + | {"hostname", required_argument, 0, 'h'}, |
| + | {"interface", required_argument, 0, 'i'}, |
| + | {"now", no_argument, 0, 'n'}, |
| + | {"pidfile", required_argument, 0, 'p'}, |
| + | {"quit", no_argument, 0, 'q'}, |
| + | {"request", required_argument, 0, 'r'}, |
| + | {"script", required_argument, 0, 's'}, |
| + | {"version", no_argument, 0, 'v'}, |
| + | {"help", no_argument, 0, '?'}, |
| + | {0, 0, 0, 0} |
| + | }; |
| + | |
| + | /* get options */ |
| + | while (1) { |
| + | int option_index = 0; |
| + | c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index); |
| + | if (c == -1) break; |
| + | |
| + | switch (c) { |
| + | case 'c': |
| + | len = strlen(optarg) > 255 ? 255 : strlen(optarg); |
| + | if (client_config.clientid) free(client_config.clientid); |
| + | client_config.clientid = xmalloc(len + 2); |
| + | client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; |
| + | client_config.clientid[OPT_LEN] = len; |
| + | client_config.clientid[OPT_DATA] = '\0'; |
| + | strncpy(client_config.clientid + OPT_DATA, optarg, len); |
| + | break; |
| + | case 'f': |
| + | client_config.foreground = 1; |
| + | break; |
| + | case 'b': |
| + | client_config.background_if_no_lease = 1; |
| + | break; |
| + | case 'h': |
| + | case 'H': |
| + | len = strlen(optarg) > 255 ? 255 : strlen(optarg); |
| + | if (client_config.hostname) free(client_config.hostname); |
| + | client_config.hostname = xmalloc(len + 2); |
| + | client_config.hostname[OPT_CODE] = DHCP_HOST_NAME; |
| + | client_config.hostname[OPT_LEN] = len; |
| + | strncpy(client_config.hostname + 2, optarg, len); |
| + | break; |
| + | case 'i': |
| + | client_config.interface = optarg; |
| + | break; |
| + | case 'n': |
| + | client_config.abort_if_no_lease = 1; |
| + | break; |
| + | case 'p': |
| + | client_config.pidfile = optarg; |
| + | break; |
| + | case 'q': |
| + | client_config.quit_after_lease = 1; |
| + | break; |
| + | case 'r': |
| + | requested_ip = inet_addr(optarg); |
| + | break; |
| + | case 's': |
| + | client_config.script = optarg; |
| + | break; |
| + | case 'v': |
| + | printf("udhcpcd, version %s\n\n", VERSION); |
| + | exit_client(0); |
| + | break; |
| + | default: |
| + | show_usage(); |
| + | } |
| + | } |
| + | |
| + | OPEN_LOG("udhcpc"); |
| + | LOG(LOG_INFO, "udhcp client (v%s) started", VERSION); |
| + | |
| + | pid_fd = pidfile_acquire(client_config.pidfile); |
| + | pidfile_write_release(pid_fd); |
| + | |
| + | if (read_interface(client_config.interface, &client_config.ifindex, |
| + | NULL, client_config.arp) < 0) |
| + | exit_client(1); |
| + | |
| + | if (!client_config.clientid) { |
| + | client_config.clientid = xmalloc(6 + 3); |
| + | client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; |
| + | client_config.clientid[OPT_LEN] = 7; |
| + | client_config.clientid[OPT_DATA] = 1; |
| + | memcpy(client_config.clientid + 3, client_config.arp, 6); |
| + | } |
| + | |
| + | /* setup signal handlers */ |
| + | socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); |
| + | signal(SIGUSR1, signal_handler); |
| + | signal(SIGUSR2, signal_handler); |
| + | signal(SIGTERM, signal_handler); |
| + | |
| + | state = INIT_SELECTING; |
| + | run_script(NULL, "deconfig"); |
| + | change_mode(LISTEN_RAW); |
| + | |
| + | for (;;) { |
| + | |
| + | tv.tv_sec = timeout - time(0); |
| + | tv.tv_usec = 0; |
| + | FD_ZERO(&rfds); |
| + | |
| + | if (listen_mode != LISTEN_NONE && fd < 0) { |
| + | if (listen_mode == LISTEN_KERNEL) |
| + | fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface); |
| + | else |
| + | fd = raw_socket(client_config.ifindex); |
| + | if (fd < 0) { |
| + | LOG(LOG_ERR, "FATAL: couldn't listen on socket, %s", strerror(errno)); |
| + | exit_client(0); |
| + | } |
| + | } |
| + | if (fd >= 0) FD_SET(fd, &rfds); |
| + | FD_SET(signal_pipe[0], &rfds); |
| + | |
| + | if (tv.tv_sec > 0) { |
| + | DEBUG(LOG_INFO, "Waiting on select...\n"); |
| + | max_fd = signal_pipe[0] > fd ? signal_pipe[0] : fd; |
| + | retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); |
| + | } else retval = 0; /* If we already timed out, fall through */ |
| + | |
| + | now = time(0); |
| + | if (retval == 0) { |
| + | /* timeout dropped to zero */ |
| + | switch (state) { |
| + | case INIT_SELECTING: |
| + | if (packet_num < 3) { |
| + | if (packet_num == 0) |
| + | xid = random_xid(); |
| + | |
| + | /* send discover packet */ |
| + | send_discover(xid, requested_ip); /* broadcast */ |
| + | |
| + | timeout = now + ((packet_num == 2) ? 4 : 2); |
| + | packet_num++; |
| + | } else { |
| + | if (client_config.background_if_no_lease) { |
| + | LOG(LOG_INFO, "No lease, forking to background."); |
| + | background(); |
| + | } else if (client_config.abort_if_no_lease) { |
| + | LOG(LOG_INFO, "No lease, failing."); |
| + | exit_client(1); |
| + | } |
| + | /* wait to try again */ |
| + | packet_num = 0; |
| + | timeout = now + 60; |
| + | } |
| + | break; |
| + | case RENEW_REQUESTED: |
| + | case REQUESTING: |
| + | if (packet_num < 3) { |
| + | /* send request packet */ |
| + | if (state == RENEW_REQUESTED) |
| + | send_renew(xid, server_addr, requested_ip); /* unicast */ |
| + | else send_selecting(xid, server_addr, requested_ip); /* broadcast */ |
| + | |
| + | timeout = now + ((packet_num == 2) ? 10 : 2); |
| + | packet_num++; |
| + | } else { |
| + | /* timed out, go back to init state */ |
| + | state = INIT_SELECTING; |
| + | timeout = now; |
| + | packet_num = 0; |
| + | change_mode(LISTEN_RAW); |
| + | } |
| + | break; |
| + | case BOUND: |
| + | /* Lease is starting to run out, time to enter renewing state */ |
| + | state = RENEWING; |
| + | change_mode(LISTEN_KERNEL); |
| + | DEBUG(LOG_INFO, "Entering renew state"); |
| + | /* fall right through */ |
| + | case RENEWING: |
| + | /* Either set a new T1, or enter REBINDING state */ |
| + | if ((t2 - t1) <= (lease / 14400 + 1)) { |
| + | /* timed out, enter rebinding state */ |
| + | state = REBINDING; |
| + | timeout = now + (t2 - t1); |
| + | DEBUG(LOG_INFO, "Entering rebinding state"); |
| + | } else { |
| + | /* send a request packet */ |
| + | send_renew(xid, server_addr, requested_ip); /* unicast */ |
| + | |
| + | t1 = (t2 - t1) / 2 + t1; |
| + | timeout = t1 + start; |
| + | } |
| + | break; |
| + | case REBINDING: |
| + | /* Either set a new T2, or enter INIT state */ |
| + | if ((lease - t2) <= (lease / 14400 + 1)) { |
| + | /* timed out, enter init state */ |
| + | state = INIT_SELECTING; |
| + | LOG(LOG_INFO, "Lease lost, entering init state"); |
| + | run_script(NULL, "deconfig"); |
| + | timeout = now; |
| + | packet_num = 0; |
| + | change_mode(LISTEN_RAW); |
| + | } else { |
| + | /* send a request packet */ |
| + | send_renew(xid, 0, requested_ip); /* broadcast */ |
| + | |
| + | t2 = (lease - t2) / 2 + t2; |
| + | timeout = t2 + start; |
| + | } |
| + | break; |
| + | case RELEASED: |
| + | /* yah, I know, *you* say it would never happen */ |
| + | timeout = 0x7fffffff; |
| + | break; |
| + | } |
| + | } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) { |
| + | /* a packet is ready, read it */ |
| + | |
| + | if (listen_mode == LISTEN_KERNEL) |
| + | len = get_packet(&packet, fd); |
| + | else len = get_raw_packet(&packet, fd); |
| + | |
| + | if (len == -1 && errno != EINTR) { |
| + | DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno)); |
| + | change_mode(listen_mode); /* just close and reopen */ |
| + | } |
| + | if (len < 0) continue; |
| + | |
| + | if (packet.xid != xid) { |
| + | DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)", |
| + | (unsigned long) packet.xid, xid); |
| + | continue; |
| + | } |
| + | |
| + | if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { |
| + | DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring"); |
| + | continue; |
| + | } |
| + | |
| + | switch (state) { |
| + | case INIT_SELECTING: |
| + | /* Must be a DHCPOFFER to one of our xid's */ |
| + | if (*message == DHCPOFFER) { |
| + | if ((temp = get_option(&packet, DHCP_SERVER_ID))) { |
| + | memcpy(&server_addr, temp, 4); |
| + | xid = packet.xid; |
| + | requested_ip = packet.yiaddr; |
| + | |
| + | /* enter requesting state */ |
| + | state = REQUESTING; |
| + | timeout = now; |
| + | packet_num = 0; |
| + | } else { |
| + | DEBUG(LOG_ERR, "No server ID in message"); |
| + | } |
| + | } |
| + | break; |
| + | case RENEW_REQUESTED: |
| + | case REQUESTING: |
| + | case RENEWING: |
| + | case REBINDING: |
| + | if (*message == DHCPACK) { |
| + | if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) { |
| + | LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease"); |
| + | lease = 60 * 60; |
| + | } else { |
| + | memcpy(&lease, temp, 4); |
| + | lease = ntohl(lease); |
| + | } |
| + | |
| + | /* enter bound state */ |
| + | t1 = lease / 2; |
| + | |
| + | /* little fixed point for n * .875 */ |
| + | t2 = (lease * 0x7) >> 3; |
| + | temp_addr.s_addr = packet.yiaddr; |
| + | LOG(LOG_INFO, "Lease of %s obtained, lease time %ld", |
| + | inet_ntoa(temp_addr), lease); |
| + | start = now; |
| + | timeout = t1 + start; |
| + | requested_ip = packet.yiaddr; |
| + | run_script(&packet, |
| + | ((state == RENEWING || state == REBINDING) ? "renew" : "bound")); |
| + | |
| + | state = BOUND; |
| + | change_mode(LISTEN_NONE); |
| + | if (client_config.quit_after_lease) |
| + | exit_client(0); |
| + | if (!client_config.foreground) |
| + | background(); |
| + | |
| + | } else if (*message == DHCPNAK) { |
| + | /* return to init state */ |
| + | LOG(LOG_INFO, "Received DHCP NAK"); |
| + | run_script(&packet, "nak"); |
| + | if (state != REQUESTING) |
| + | run_script(NULL, "deconfig"); |
| + | state = INIT_SELECTING; |
| + | timeout = now; |
| + | requested_ip = 0; |
| + | packet_num = 0; |
| + | change_mode(LISTEN_RAW); |
| + | sleep(3); /* avoid excessive network traffic */ |
| + | } |
| + | break; |
| + | /* case BOUND, RELEASED: - ignore all packets */ |
| + | } |
| + | } else if (retval > 0 && FD_ISSET(signal_pipe[0], &rfds)) { |
| + | if (read(signal_pipe[0], &sig, sizeof(signal)) < 0) { |
| + | DEBUG(LOG_ERR, "Could not read signal: %s", |
| + | strerror(errno)); |
| + | continue; /* probably just EINTR */ |
| + | } |
| + | switch (sig) { |
| + | case SIGUSR1: |
| + | perform_renew(); |
| + | break; |
| + | case SIGUSR2: |
| + | perform_release(); |
| + | break; |
| + | case SIGTERM: |
| + | LOG(LOG_INFO, "Received SIGTERM"); |
| + | exit_client(0); |
| + | } |
| + | } else if (retval == -1 && errno == EINTR) { |
| + | /* a signal was caught */ |
| + | } else { |
| + | /* An error occured */ |
| + | DEBUG(LOG_ERR, "Error on select"); |
| + | } |
| + | |
| + | } |
| + | return 0; |
| + | } |
| + | |
| @@ -0,0 +1,34 @@ | |
| + | /* dhcpc.h */ |
| + | #ifndef _DHCPC_H |
| + | #define _DHCPC_H |
| + | |
| + | #include "libbb_udhcp.h" |
| + | |
| + | #define INIT_SELECTING 0 |
| + | #define REQUESTING 1 |
| + | #define BOUND 2 |
| + | #define RENEWING 3 |
| + | #define REBINDING 4 |
| + | #define INIT_REBOOT 5 |
| + | #define RENEW_REQUESTED 6 |
| + | #define RELEASED 7 |
| + | |
| + | |
| + | struct client_config_t { |
| + | char foreground; /* Do not fork */ |
| + | char quit_after_lease; /* Quit after obtaining lease */ |
| + | char abort_if_no_lease; /* Abort if no lease */ |
| + | char background_if_no_lease; /* Fork to background if no lease */ |
| + | char *interface; /* The name of the interface to use */ |
| + | char *pidfile; /* Optionally store the process ID */ |
| + | char *script; /* User script to run at dhcp events */ |
| + | unsigned char *clientid; /* Optional client id to use */ |
| + | unsigned char *hostname; /* Optional hostname to use */ |
| + | int ifindex; /* Index number of the interface to use */ |
| + | unsigned char arp[6]; /* Our arp address */ |
| + | }; |
| + | |
| + | extern struct client_config_t client_config; |
| + | |
| + | |
| + | #endif |
| @@ -0,0 +1,287 @@ | |
| + | /* dhcpd.c |
| + | * |
| + | * udhcp Server |
| + | * Copyright (C) 1999 Matthew Ramsay <[email protected]> |
| + | * Chris Trew <[email protected]> |
| + | * |
| + | * Rewrite by Russ Dill <[email protected]> July 2001 |
| + | * |
| + | * This program is free software; you can redistribute it and/or modify |
| + | * it under the terms of the GNU General Public License as published by |
| + | * the Free Software Foundation; either version 2 of the License, or |
| + | * (at your option) any later version. |
| + | * |
| + | * This program is distributed in the hope that it will be useful, |
| + | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + | * GNU General Public License for more details. |
| + | * |
| + | * You should have received a copy of the GNU General Public License |
| + | * along with this program; if not, write to the Free Software |
| + | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| + | */ |
| + | |
| + | #include <fcntl.h> |
| + | #include <string.h> |
| + | #include <stdlib.h> |
| + | #include <sys/wait.h> |
| + | #include <sys/stat.h> |
| + | #include <arpa/inet.h> |
| + | #include <netdb.h> |
| + | #include <netinet/in.h> |
| + | #include <stdio.h> |
| + | #include <sys/types.h> |
| + | #include <sys/socket.h> |
| + | #include <unistd.h> |
| + | #include <signal.h> |
| + | #include <errno.h> |
| + | #include <sys/ioctl.h> |
| + | #include <time.h> |
| + | #include <sys/time.h> |
| + | |
| + | #include "debug.h" |
| + | #include "dhcpd.h" |
| + | #include "arpping.h" |
| + | #include "socket.h" |
| + | #include "options.h" |
| + | #include "files.h" |
| + | #include "leases.h" |
| + | #include "packet.h" |
| + | #include "serverpacket.h" |
| + | #include "pidfile.h" |
| + | |
| + | |
| + | /* globals */ |
| + | struct dhcpOfferedAddr *leases; |
| + | struct server_config_t server_config; |
| + | static int signal_pipe[2]; |
| + | |
| + | /* Exit and cleanup */ |
| + | static void exit_server(int retval) |
| + | { |
| + | pidfile_delete(server_config.pidfile); |
| + | CLOSE_LOG(); |
| + | exit(retval); |
| + | } |
| + | |
| + | |
| + | /* Signal handler */ |
| + | static void signal_handler(int sig) |
| + | { |
| + | if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) { |
| + | LOG(LOG_ERR, "Could not send signal: %s", |
| + | strerror(errno)); |
| + | } |
| + | } |
| + | |
| + | |
| + | #ifdef COMBINED_BINARY |
| + | int udhcpd_main(int argc, char *argv[]) |
| + | #else |
| + | int main(int argc, char *argv[]) |
| + | #endif |
| + | { |
| + | fd_set rfds; |
| + | struct timeval tv; |
| + | int server_socket = -1; |
| + | int bytes, retval; |
| + | struct dhcpMessage packet; |
| + | unsigned char *state; |
| + | unsigned char *server_id, *requested; |
| + | u_int32_t server_id_align, requested_align; |
| + | unsigned long timeout_end; |
| + | struct option_set *option; |
| + | struct dhcpOfferedAddr *lease; |
| + | int pid_fd; |
| + | int max_sock; |
| + | int sig; |
| + | |
| + | OPEN_LOG("udhcpd"); |
| + | LOG(LOG_INFO, "udhcp server (v%s) started", VERSION); |
| + | |
| + | memset(&server_config, 0, sizeof(struct server_config_t)); |
| + | |
| + | if (argc < 2) |
| + | read_config(DHCPD_CONF_FILE); |
| + | else read_config(argv[1]); |
| + | |
| + | pid_fd = pidfile_acquire(server_config.pidfile); |
| + | pidfile_write_release(pid_fd); |
| + | |
| + | if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) { |
| + | memcpy(&server_config.lease, option->data + 2, 4); |
| + | server_config.lease = ntohl(server_config.lease); |
| + | } |
| + | else server_config.lease = LEASE_TIME; |
| + | |
| + | leases = malloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases); |
| + | memset(leases, 0, sizeof(struct dhcpOfferedAddr) * server_config.max_leases); |
| + | read_leases(server_config.lease_file); |
| + | |
| + | if (read_interface(server_config.interface, &server_config.ifindex, |
| + | &server_config.server, server_config.arp) < 0) |
| + | exit_server(1); |
| + | |
| + | #ifndef DEBUGGING |
| + | pid_fd = pidfile_acquire(server_config.pidfile); /* hold lock during fork. */ |
| + | if (daemon(0, 0) == -1) { |
| + | perror("fork"); |
| + | exit_server(1); |
| + | } |
| + | pidfile_write_release(pid_fd); |
| + | #endif |
| + | |
| + | |
| + | socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); |
| + | signal(SIGUSR1, signal_handler); |
| + | signal(SIGTERM, signal_handler); |
| + | |
| + | timeout_end = time(0) + server_config.auto_time; |
| + | while(1) { /* loop until universe collapses */ |
| + | |
| + | if (server_socket < 0) |
| + | if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) { |
| + | LOG(LOG_ERR, "FATAL: couldn't create server socket, %s", strerror(errno)); |
| + | exit_server(0); |
| + | } |
| + | |
| + | FD_ZERO(&rfds); |
| + | FD_SET(server_socket, &rfds); |
| + | FD_SET(signal_pipe[0], &rfds); |
| + | if (server_config.auto_time) { |
| + | tv.tv_sec = timeout_end - time(0); |
| + | tv.tv_usec = 0; |
| + | } |
| + | if (!server_config.auto_time || tv.tv_sec > 0) { |
| + | max_sock = server_socket > signal_pipe[0] ? server_socket : signal_pipe[0]; |
| + | retval = select(max_sock + 1, &rfds, NULL, NULL, |
| + | server_config.auto_time ? &tv : NULL); |
| + | } else retval = 0; /* If we already timed out, fall through */ |
| + | |
| + | if (retval == 0) { |
| + | write_leases(); |
| + | timeout_end = time(0) + server_config.auto_time; |
| + | continue; |
| + | } else if (retval < 0 && errno != EINTR) { |
| + | DEBUG(LOG_INFO, "error on select"); |
| + | continue; |
| + | } |
| + | |
| + | if (FD_ISSET(signal_pipe[0], &rfds)) { |
| + | if (read(signal_pipe[0], &sig, sizeof(sig)) < 0) |
| + | continue; /* probably just EINTR */ |
| + | switch (sig) { |
| + | case SIGUSR1: |
| + | LOG(LOG_INFO, "Received a SIGUSR1"); |
| + | write_leases(); |
| + | /* why not just reset the timeout, eh */ |
| + | timeout_end = time(0) + server_config.auto_time; |
| + | continue; |
| + | case SIGTERM: |
| + | LOG(LOG_INFO, "Received a SIGTERM"); |
| + | exit_server(0); |
| + | } |
| + | } |
| + | |
| + | if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */ |
| + | if (bytes == -1 && errno != EINTR) { |
| + | DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno)); |
| + | close(server_socket); |
| + | server_socket = -1; |
| + | } |
| + | continue; |
| + | } |
| + | |
| + | if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { |
| + | DEBUG(LOG_ERR, "couldn't get option from packet, ignoring"); |
| + | continue; |
| + | } |
| + | |
| + | /* ADDME: look for a static lease */ |
| + | lease = find_lease_by_chaddr(packet.chaddr); |
| + | switch (state[0]) { |
| + | case DHCPDISCOVER: |
| + | DEBUG(LOG_INFO,"received DISCOVER"); |
| + | |
| + | if (sendOffer(&packet) < 0) { |
| + | LOG(LOG_ERR, "send OFFER failed"); |
| + | } |
| + | break; |
| + | case DHCPREQUEST: |
| + | DEBUG(LOG_INFO, "received REQUEST"); |
| + | |
| + | requested = get_option(&packet, DHCP_REQUESTED_IP); |
| + | server_id = get_option(&packet, DHCP_SERVER_ID); |
| + | |
| + | if (requested) memcpy(&requested_align, requested, 4); |
| + | if (server_id) memcpy(&server_id_align, server_id, 4); |
| + | |
| + | if (lease) { /*ADDME: or static lease */ |
| + | if (server_id) { |
| + | /* SELECTING State */ |
| + | DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align)); |
| + | if (server_id_align == server_config.server && requested && |
| + | requested_align == lease->yiaddr) { |
| + | sendACK(&packet, lease->yiaddr); |
| + | } |
| + | } else { |
| + | if (requested) { |
| + | /* INIT-REBOOT State */ |
| + | if (lease->yiaddr == requested_align) |
| + | sendACK(&packet, lease->yiaddr); |
| + | else sendNAK(&packet); |
| + | } else { |
| + | /* RENEWING or REBINDING State */ |
| + | if (lease->yiaddr == packet.ciaddr) |
| + | sendACK(&packet, lease->yiaddr); |
| + | else { |
| + | /* don't know what to do!!!! */ |
| + | sendNAK(&packet); |
| + | } |
| + | } |
| + | } |
| + | |
| + | /* what to do if we have no record of the client */ |
| + | } else if (server_id) { |
| + | /* SELECTING State */ |
| + | |
| + | } else if (requested) { |
| + | /* INIT-REBOOT State */ |
| + | if ((lease = find_lease_by_yiaddr(requested_align))) { |
| + | if (lease_expired(lease)) { |
| + | /* probably best if we drop this lease */ |
| + | memset(lease->chaddr, 0, 16); |
| + | /* make some contention for this address */ |
| + | } else sendNAK(&packet); |
| + | } else if (requested_align < server_config.start || |
| + | requested_align > server_config.end) { |
| + | sendNAK(&packet); |
| + | } /* else remain silent */ |
| + | |
| + | } else { |
| + | /* RENEWING or REBINDING State */ |
| + | } |
| + | break; |
| + | case DHCPDECLINE: |
| + | DEBUG(LOG_INFO,"received DECLINE"); |
| + | if (lease) { |
| + | memset(lease->chaddr, 0, 16); |
| + | lease->expires = time(0) + server_config.decline_time; |
| + | } |
| + | break; |
| + | case DHCPRELEASE: |
| + | DEBUG(LOG_INFO,"received RELEASE"); |
| + | if (lease) lease->expires = time(0); |
| + | break; |
| + | case DHCPINFORM: |
| + | DEBUG(LOG_INFO,"received INFORM"); |
| + | send_inform(&packet); |
| + | break; |
| + | default: |
| + | LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]); |
| + | } |
| + | } |
| + | |
| + | return 0; |
| + | } |
| + | |
| @@ -0,0 +1,131 @@ | |
| + | /* dhcpd.h */ |
| + | #ifndef _DHCPD_H |
| + | #define _DHCPD_H |
| + | |
| + | #include <netinet/ip.h> |
| + | #include <netinet/udp.h> |
| + | |
| + | #include "libbb_udhcp.h" |
| + | #include "leases.h" |
| + | |
| + | /************************************/ |
| + | /* Defaults _you_ may want to tweak */ |
| + | /************************************/ |
| + | |
| + | /* the period of time the client is allowed to use that address */ |
| + | #define LEASE_TIME (60*60*24*10) /* 10 days of seconds */ |
| + | |
| + | /* where to find the DHCP server configuration file */ |
| + | #define DHCPD_CONF_FILE "/etc/udhcpd.conf" |
| + | |
| + | /*****************************************************************/ |
| + | /* Do not modify below here unless you know what you are doing!! */ |
| + | /*****************************************************************/ |
| + | |
| + | /* DHCP protocol -- see RFC 2131 */ |
| + | #define SERVER_PORT 67 |
| + | #define CLIENT_PORT 68 |
| + | |
| + | #define DHCP_MAGIC 0x63825363 |
| + | |
| + | /* DHCP option codes (partial list) */ |
| + | #define DHCP_PADDING 0x00 |
| + | #define DHCP_SUBNET 0x01 |
| + | #define DHCP_TIME_OFFSET 0x02 |
| + | #define DHCP_ROUTER 0x03 |
| + | #define DHCP_TIME_SERVER 0x04 |
| + | #define DHCP_NAME_SERVER 0x05 |
| + | #define DHCP_DNS_SERVER 0x06 |
| + | #define DHCP_LOG_SERVER 0x07 |
| + | #define DHCP_COOKIE_SERVER 0x08 |
| + | #define DHCP_LPR_SERVER 0x09 |
| + | #define DHCP_HOST_NAME 0x0c |
| + | #define DHCP_BOOT_SIZE 0x0d |
| + | #define DHCP_DOMAIN_NAME 0x0f |
| + | #define DHCP_SWAP_SERVER 0x10 |
| + | #define DHCP_ROOT_PATH 0x11 |
| + | #define DHCP_IP_TTL 0x17 |
| + | #define DHCP_MTU 0x1a |
| + | #define DHCP_BROADCAST 0x1c |
| + | #define DHCP_NTP_SERVER 0x2a |
| + | #define DHCP_WINS_SERVER 0x2c |
| + | #define DHCP_REQUESTED_IP 0x32 |
| + | #define DHCP_LEASE_TIME 0x33 |
| + | #define DHCP_OPTION_OVER 0x34 |
| + | #define DHCP_MESSAGE_TYPE 0x35 |
| + | #define DHCP_SERVER_ID 0x36 |
| + | #define DHCP_PARAM_REQ 0x37 |
| + | #define DHCP_MESSAGE 0x38 |
| + | #define DHCP_MAX_SIZE 0x39 |
| + | #define DHCP_T1 0x3a |
| + | #define DHCP_T2 0x3b |
| + | #define DHCP_VENDOR 0x3c |
| + | #define DHCP_CLIENT_ID 0x3d |
| + | |
| + | #define DHCP_END 0xFF |
| + | |
| + | |
| + | #define BOOTREQUEST 1 |
| + | #define BOOTREPLY 2 |
| + | |
| + | #define ETH_10MB 1 |
| + | #define ETH_10MB_LEN 6 |
| + | |
| + | #define DHCPDISCOVER 1 |
| + | #define DHCPOFFER 2 |
| + | #define DHCPREQUEST 3 |
| + | #define DHCPDECLINE 4 |
| + | #define DHCPACK 5 |
| + | #define DHCPNAK 6 |
| + | #define DHCPRELEASE 7 |
| + | #define DHCPINFORM 8 |
| + | |
| + | #define BROADCAST_FLAG 0x8000 |
| + | |
| + | #define OPTION_FIELD 0 |
| + | #define FILE_FIELD 1 |
| + | #define SNAME_FIELD 2 |
| + | |
| + | /* miscellaneous defines */ |
| + | #define MAC_BCAST_ADDR (unsigned char *) "\xff\xff\xff\xff\xff\xff" |
| + | #define OPT_CODE 0 |
| + | #define OPT_LEN 1 |
| + | #define OPT_DATA 2 |
| + | |
| + | struct option_set { |
| + | unsigned char *data; |
| + | struct option_set *next; |
| + | }; |
| + | |
| + | struct server_config_t { |
| + | u_int32_t server; /* Our IP, in network order */ |
| + | u_int32_t start; /* Start address of leases, network order */ |
| + | u_int32_t end; /* End of leases, network order */ |
| + | struct option_set *options; /* List of DHCP options loaded from the config file */ |
| + | char *interface; /* The name of the interface to use */ |
| + | int ifindex; /* Index number of the interface to use */ |
| + | unsigned char arp[6]; /* Our arp address */ |
| + | unsigned long lease; /* lease time in seconds (host order) */ |
| + | unsigned long max_leases; /* maximum number of leases (including reserved address) */ |
| + | char remaining; /* should the lease file be interpreted as lease time remaining, or |
| + | * as the time the lease expires */ |
| + | unsigned long auto_time; /* how long should udhcpd wait before writing a config file. |
| + | * if this is zero, it will only write one on SIGUSR1 */ |
| + | unsigned long decline_time; /* how long an address is reserved if a client returns a |
| + | * decline message */ |
| + | unsigned long conflict_time; /* how long an arp conflict offender is leased for */ |
| + | unsigned long offer_time; /* how long an offered address is reserved */ |
| + | unsigned long min_lease; /* minimum lease a client can request*/ |
| + | char *lease_file; |
| + | char *pidfile; |
| + | char *notify_file; /* What to run whenever leases are written */ |
| + | u_int32_t siaddr; /* next server bootp option */ |
| + | char *sname; /* bootp server name */ |
| + | char *boot_file; /* bootp boot file option */ |
| + | }; |
| + | |
| + | extern struct server_config_t server_config; |
| + | extern struct dhcpOfferedAddr *leases; |
| + | |
| + | |
| + | #endif |
| @@ -0,0 +1,112 @@ | |
| + | #include <fcntl.h> |
| + | #include <string.h> |
| + | #include <stdlib.h> |
| + | #include <stdio.h> |
| + | #include <sys/wait.h> |
| + | #include <sys/stat.h> |
| + | #include <arpa/inet.h> |
| + | #include <netdb.h> |
| + | #include <netinet/in.h> |
| + | #include <stdio.h> |
| + | #include <sys/types.h> |
| + | #include <sys/socket.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <signal.h> |
| + | #include <errno.h> |
| + | #include <getopt.h> |
| + | #include <time.h> |
| + | |
| + | #include "libbb_udhcp.h" |
| + | |
| + | #define REMAINING 0 |
| + | #define ABSOLUTE 1 |
| + | |
| + | struct lease_t { |
| + | unsigned char chaddr[16]; |
| + | u_int32_t yiaddr; |
| + | u_int32_t expires; |
| + | }; |
| + | |
| + | #ifdef BB_VER |
| + | int dumpleases_main(int argc, char *argv[]) |
| + | #else |
| + | int main(int argc, char *argv[]) |
| + | #endif |
| + | { |
| + | FILE *fp; |
| + | int i, c, mode = REMAINING; |
| + | long expires; |
| + | char file[255] = "/var/lib/misc/udhcpd.leases"; |
| + | struct lease_t lease; |
| + | struct in_addr addr; |
| + | |
| + | static struct option options[] = { |
| + | {"absolute", 0, 0, 'a'}, |
| + | {"remaining", 0, 0, 'r'}, |
| + | {"file", 1, 0, 'f'}, |
| + | {"help", 0, 0, 'h'}, |
| + | {0, 0, 0, 0} |
| + | }; |
| + | |
| + | while (1) { |
| + | int option_index = 0; |
| + | c = getopt_long(argc, argv, "arf:h", options, &option_index); |
| + | if (c == -1) break; |
| + | |
| + | switch (c) { |
| + | case 'a': mode = ABSOLUTE; break; |
| + | case 'r': mode = REMAINING; break; |
| + | case 'f': |
| + | strncpy(file, optarg, 255); |
| + | file[254] = '\0'; |
| + | break; |
| + | case 'h': |
| + | printf("Usage: dumpleases -f <file> -[r|a]\n\n"); |
| + | printf(" -f, --file=FILENAME Leases file to load\n"); |
| + | printf(" -r, --remaining Interepret lease times as time remaing\n"); |
| + | printf(" -a, --absolute Interepret lease times as expire time\n"); |
| + | break; |
| + | } |
| + | } |
| + | |
| + | if (!(fp = fopen(file, "r"))) { |
| + | perror("could not open input file"); |
| + | return 0; |
| + | } |
| + | |
| + | printf("Mac Address IP-Address Expires %s\n", mode == REMAINING ? "in" : "at"); |
| + | /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */ |
| + | while (fread(&lease, sizeof(lease), 1, fp)) { |
| + | |
| + | for (i = 0; i < 6; i++) { |
| + | printf("%02x", lease.chaddr[i]); |
| + | if (i != 5) printf(":"); |
| + | } |
| + | addr.s_addr = lease.yiaddr; |
| + | printf(" %-15s", inet_ntoa(addr)); |
| + | expires = ntohl(lease.expires); |
| + | printf(" "); |
| + | if (mode == REMAINING) { |
| + | if (!expires) printf("expired\n"); |
| + | else { |
| + | if (expires > 60*60*24) { |
| + | printf("%ld days, ", expires / (60*60*24)); |
| + | expires %= 60*60*24; |
| + | } |
| + | if (expires > 60*60) { |
| + | printf("%ld hours, ", expires / (60*60)); |
| + | expires %= 60*60; |
| + | } |
| + | if (expires > 60) { |
| + | printf("%ld minutes, ", expires / 60); |
| + | expires %= 60; |
| + | } |
| + | printf("%ld seconds\n", expires); |
| + | } |
| + | } else printf("%s", ctime(&expires)); |
| + | } |
| + | fclose(fp); |
| + | |
| + | return 0; |
| + | } |
| @@ -0,0 +1,284 @@ | |
| + | /* |
| + | * files.c -- DHCP server file manipulation * |
| + | * Rewrite by Russ Dill <[email protected]> July 2001 |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <sys/socket.h> |
| + | #include <arpa/inet.h> |
| + | #include <string.h> |
| + | #include <stdlib.h> |
| + | #include <time.h> |
| + | #include <ctype.h> |
| + | #include <netdb.h> |
| + | |
| + | #include "debug.h" |
| + | #include "dhcpd.h" |
| + | #include "files.h" |
| + | #include "options.h" |
| + | #include "leases.h" |
| + | |
| + | /* on these functions, make sure you datatype matches */ |
| + | static int read_ip(char *line, void *arg) |
| + | { |
| + | struct in_addr *addr = arg; |
| + | struct hostent *host; |
| + | int retval = 1; |
| + | |
| + | if (!inet_aton(line, addr)) { |
| + | if ((host = gethostbyname(line))) |
| + | addr->s_addr = *((unsigned long *) host->h_addr_list[0]); |
| + | else retval = 0; |
| + | } |
| + | return retval; |
| + | } |
| + | |
| + | |
| + | static int read_str(char *line, void *arg) |
| + | { |
| + | char **dest = arg; |
| + | |
| + | if (*dest) free(*dest); |
| + | *dest = strdup(line); |
| + | |
| + | return 1; |
| + | } |
| + | |
| + | |
| + | static int read_u32(char *line, void *arg) |
| + | { |
| + | u_int32_t *dest = arg; |
| + | char *endptr; |
| + | *dest = strtoul(line, &endptr, 0); |
| + | return endptr[0] == '\0'; |
| + | } |
| + | |
| + | |
| + | static int read_yn(char *line, void *arg) |
| + | { |
| + | char *dest = arg; |
| + | int retval = 1; |
| + | |
| + | if (!strcasecmp("yes", line)) |
| + | *dest = 1; |
| + | else if (!strcasecmp("no", line)) |
| + | *dest = 0; |
| + | else retval = 0; |
| + | |
| + | return retval; |
| + | } |
| + | |
| + | |
| + | /* read a dhcp option and add it to opt_list */ |
| + | static int read_opt(char *line, void *arg) |
| + | { |
| + | struct option_set **opt_list = arg; |
| + | char *opt, *val, *endptr; |
| + | struct dhcp_option *option = NULL; |
| + | int retval = 0, length = 0; |
| + | char buffer[255]; |
| + | u_int16_t result_u16; |
| + | u_int32_t result_u32; |
| + | int i; |
| + | |
| + | if (!(opt = strtok(line, " \t="))) return 0; |
| + | |
| + | for (i = 0; options[i].code; i++) |
| + | if (!strcmp(options[i].name, opt)) |
| + | option = &(options[i]); |
| + | |
| + | if (!option) return 0; |
| + | |
| + | do { |
| + | val = strtok(NULL, ", \t"); |
| + | if (val) { |
| + | length = option_lengths[option->flags & TYPE_MASK]; |
| + | retval = 0; |
| + | switch (option->flags & TYPE_MASK) { |
| + | case OPTION_IP: |
| + | retval = read_ip(val, buffer); |
| + | break; |
| + | case OPTION_IP_PAIR: |
| + | retval = read_ip(val, buffer); |
| + | if (!(val = strtok(NULL, ", \t/-"))) retval = 0; |
| + | if (retval) retval = read_ip(val, buffer + 4); |
| + | break; |
| + | case OPTION_STRING: |
| + | length = strlen(val); |
| + | if (length > 0) { |
| + | if (length > 254) length = 254; |
| + | memcpy(buffer, val, length); |
| + | retval = 1; |
| + | } |
| + | break; |
| + | case OPTION_BOOLEAN: |
| + | retval = read_yn(val, buffer); |
| + | break; |
| + | case OPTION_U8: |
| + | buffer[0] = strtoul(val, &endptr, 0); |
| + | retval = (endptr[0] == '\0'); |
| + | break; |
| + | case OPTION_U16: |
| + | result_u16 = htons(strtoul(val, &endptr, 0)); |
| + | memcpy(buffer, &result_u16, 2); |
| + | retval = (endptr[0] == '\0'); |
| + | break; |
| + | case OPTION_S16: |
| + | result_u16 = htons(strtol(val, &endptr, 0)); |
| + | memcpy(buffer, &result_u16, 2); |
| + | retval = (endptr[0] == '\0'); |
| + | break; |
| + | case OPTION_U32: |
| + | result_u32 = htonl(strtoul(val, &endptr, 0)); |
| + | memcpy(buffer, &result_u32, 4); |
| + | retval = (endptr[0] == '\0'); |
| + | break; |
| + | case OPTION_S32: |
| + | result_u32 = htonl(strtol(val, &endptr, 0)); |
| + | memcpy(buffer, &result_u32, 4); |
| + | retval = (endptr[0] == '\0'); |
| + | break; |
| + | default: |
| + | break; |
| + | } |
| + | if (retval) |
| + | attach_option(opt_list, option, buffer, length); |
| + | }; |
| + | } while (val && retval && option->flags & OPTION_LIST); |
| + | return retval; |
| + | } |
| + | |
| + | |
| + | static struct config_keyword keywords[] = { |
| + | /* keyword[14] handler variable address default[20] */ |
| + | {"start", read_ip, &(server_config.start), "192.168.0.20"}, |
| + | {"end", read_ip, &(server_config.end), "192.168.0.254"}, |
| + | {"interface", read_str, &(server_config.interface), "eth0"}, |
| + | {"option", read_opt, &(server_config.options), ""}, |
| + | {"opt", read_opt, &(server_config.options), ""}, |
| + | {"max_leases", read_u32, &(server_config.max_leases), "254"}, |
| + | {"remaining", read_yn, &(server_config.remaining), "yes"}, |
| + | {"auto_time", read_u32, &(server_config.auto_time), "7200"}, |
| + | {"decline_time",read_u32, &(server_config.decline_time),"3600"}, |
| + | {"conflict_time",read_u32,&(server_config.conflict_time),"3600"}, |
| + | {"offer_time", read_u32, &(server_config.offer_time), "60"}, |
| + | {"min_lease", read_u32, &(server_config.min_lease), "60"}, |
| + | {"lease_file", read_str, &(server_config.lease_file), "/var/lib/misc/udhcpd.leases"}, |
| + | {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"}, |
| + | {"notify_file", read_str, &(server_config.notify_file), ""}, |
| + | {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"}, |
| + | {"sname", read_str, &(server_config.sname), ""}, |
| + | {"boot_file", read_str, &(server_config.boot_file), ""}, |
| + | /*ADDME: static lease */ |
| + | {"", NULL, NULL, ""} |
| + | }; |
| + | |
| + | |
| + | int read_config(char *file) |
| + | { |
| + | FILE *in; |
| + | char buffer[80], orig[80], *token, *line; |
| + | int i; |
| + | |
| + | for (i = 0; strlen(keywords[i].keyword); i++) |
| + | if (strlen(keywords[i].def)) |
| + | keywords[i].handler(keywords[i].def, keywords[i].var); |
| + | |
| + | if (!(in = fopen(file, "r"))) { |
| + | LOG(LOG_ERR, "unable to open config file: %s", file); |
| + | return 0; |
| + | } |
| + | |
| + | while (fgets(buffer, 80, in)) { |
| + | if (strchr(buffer, '\n')) *(strchr(buffer, '\n')) = '\0'; |
| + | strncpy(orig, buffer, 80); |
| + | if (strchr(buffer, '#')) *(strchr(buffer, '#')) = '\0'; |
| + | token = buffer + strspn(buffer, " \t"); |
| + | if (*token == '\0') continue; |
| + | line = token + strcspn(token, " \t="); |
| + | if (*line == '\0') continue; |
| + | *line = '\0'; |
| + | line++; |
| + | |
| + | /* eat leading whitespace */ |
| + | line = line + strspn(line, " \t="); |
| + | /* eat trailing whitespace */ |
| + | for (i = strlen(line); i > 0 && isspace(line[i - 1]); i--); |
| + | line[i] = '\0'; |
| + | |
| + | for (i = 0; strlen(keywords[i].keyword); i++) |
| + | if (!strcasecmp(token, keywords[i].keyword)) |
| + | if (!keywords[i].handler(line, keywords[i].var)) { |
| + | LOG(LOG_ERR, "unable to parse '%s'", orig); |
| + | /* reset back to the default value */ |
| + | keywords[i].handler(keywords[i].def, keywords[i].var); |
| + | } |
| + | } |
| + | fclose(in); |
| + | return 1; |
| + | } |
| + | |
| + | |
| + | void write_leases(void) |
| + | { |
| + | FILE *fp; |
| + | unsigned int i; |
| + | char buf[255]; |
| + | time_t curr = time(0); |
| + | unsigned long lease_time; |
| + | |
| + | if (!(fp = fopen(server_config.lease_file, "w"))) { |
| + | LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file); |
| + | return; |
| + | } |
| + | |
| + | for (i = 0; i < server_config.max_leases; i++) { |
| + | if (leases[i].yiaddr != 0) { |
| + | if (server_config.remaining) { |
| + | if (lease_expired(&(leases[i]))) |
| + | lease_time = 0; |
| + | else lease_time = leases[i].expires - curr; |
| + | } else lease_time = leases[i].expires; |
| + | lease_time = htonl(lease_time); |
| + | fwrite(leases[i].chaddr, 16, 1, fp); |
| + | fwrite(&(leases[i].yiaddr), 4, 1, fp); |
| + | fwrite(&lease_time, 4, 1, fp); |
| + | } |
| + | } |
| + | fclose(fp); |
| + | |
| + | if (server_config.notify_file) { |
| + | sprintf(buf, "%s %s", server_config.notify_file, server_config.lease_file); |
| + | system(buf); |
| + | } |
| + | } |
| + | |
| + | |
| + | void read_leases(char *file) |
| + | { |
| + | FILE *fp; |
| + | unsigned int i = 0; |
| + | struct dhcpOfferedAddr lease; |
| + | |
| + | if (!(fp = fopen(file, "r"))) { |
| + | LOG(LOG_ERR, "Unable to open %s for reading", file); |
| + | return; |
| + | } |
| + | |
| + | while (i < server_config.max_leases && (fread(&lease, sizeof lease, 1, fp) == 1)) { |
| + | /* ADDME: is it a static lease */ |
| + | if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) { |
| + | lease.expires = ntohl(lease.expires); |
| + | if (!server_config.remaining) lease.expires -= time(0); |
| + | if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) { |
| + | LOG(LOG_WARNING, "Too many leases while loading %s\n", file); |
| + | break; |
| + | } |
| + | i++; |
| + | } |
| + | } |
| + | DEBUG(LOG_INFO, "Read %d leases", i); |
| + | fclose(fp); |
| + | } |
| + | |
| + | |
| @@ -0,0 +1,17 @@ | |
| + | /* files.h */ |
| + | #ifndef _FILES_H |
| + | #define _FILES_H |
| + | |
| + | struct config_keyword { |
| + | char keyword[14]; |
| + | int (*handler)(char *line, void *var); |
| + | void *var; |
| + | char def[30]; |
| + | }; |
| + | |
| + | |
| + | int read_config(char *file); |
| + | void write_leases(void); |
| + | void read_leases(char *file); |
| + | |
| + | #endif |
| @@ -0,0 +1,16 @@ | |
| + | #include <string.h> |
| + | |
| + | extern int udhcpd_main(int argc, char *argv[]); |
| + | extern int udhcpc_main(int argc, char *argv[]); |
| + | |
| + | int main(int argc, char *argv[]) |
| + | { |
| + | int ret = 0; |
| + | char *base = strrchr(argv[0], '/'); |
| + | |
| + | if (strstr(base ? (base + 1) : argv[0], "dhcpd")) |
| + | ret = udhcpd_main(argc, argv); |
| + | else ret = udhcpc_main(argc, argv); |
| + | |
| + | return ret; |
| + | } |
| @@ -0,0 +1,151 @@ | |
| + | /* |
| + | * leases.c -- tools to manage DHCP leases |
| + | * Russ Dill <[email protected]> July 2001 |
| + | */ |
| + | |
| + | #include <time.h> |
| + | #include <string.h> |
| + | #include <sys/socket.h> |
| + | #include <netinet/in.h> |
| + | #include <arpa/inet.h> |
| + | |
| + | #include "debug.h" |
| + | #include "dhcpd.h" |
| + | #include "files.h" |
| + | #include "options.h" |
| + | #include "leases.h" |
| + | #include "arpping.h" |
| + | |
| + | unsigned char blank_chaddr[] = {[0 ... 15] = 0}; |
| + | |
| + | /* clear every lease out that chaddr OR yiaddr matches and is nonzero */ |
| + | void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr) |
| + | { |
| + | unsigned int i, j; |
| + | |
| + | for (j = 0; j < 16 && !chaddr[j]; j++); |
| + | |
| + | for (i = 0; i < server_config.max_leases; i++) |
| + | if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) || |
| + | (yiaddr && leases[i].yiaddr == yiaddr)) { |
| + | memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr)); |
| + | } |
| + | } |
| + | |
| + | |
| + | /* add a lease into the table, clearing out any old ones */ |
| + | struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease) |
| + | { |
| + | struct dhcpOfferedAddr *oldest; |
| + | |
| + | /* clean out any old ones */ |
| + | clear_lease(chaddr, yiaddr); |
| + | |
| + | oldest = oldest_expired_lease(); |
| + | |
| + | if (oldest) { |
| + | memcpy(oldest->chaddr, chaddr, 16); |
| + | oldest->yiaddr = yiaddr; |
| + | oldest->expires = time(0) + lease; |
| + | } |
| + | |
| + | return oldest; |
| + | } |
| + | |
| + | |
| + | /* true if a lease has expired */ |
| + | int lease_expired(struct dhcpOfferedAddr *lease) |
| + | { |
| + | return (lease->expires < (unsigned long) time(0)); |
| + | } |
| + | |
| + | |
| + | /* Find the oldest expired lease, NULL if there are no expired leases */ |
| + | struct dhcpOfferedAddr *oldest_expired_lease(void) |
| + | { |
| + | struct dhcpOfferedAddr *oldest = NULL; |
| + | unsigned long oldest_lease = time(0); |
| + | unsigned int i; |
| + | |
| + | |
| + | for (i = 0; i < server_config.max_leases; i++) |
| + | if (oldest_lease > leases[i].expires) { |
| + | oldest_lease = leases[i].expires; |
| + | oldest = &(leases[i]); |
| + | } |
| + | return oldest; |
| + | |
| + | } |
| + | |
| + | |
| + | /* Find the first lease that matches chaddr, NULL if no match */ |
| + | struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr) |
| + | { |
| + | unsigned int i; |
| + | |
| + | for (i = 0; i < server_config.max_leases; i++) |
| + | if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]); |
| + | |
| + | return NULL; |
| + | } |
| + | |
| + | |
| + | /* Find the first lease that matches yiaddr, NULL is no match */ |
| + | struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr) |
| + | { |
| + | unsigned int i; |
| + | |
| + | for (i = 0; i < server_config.max_leases; i++) |
| + | if (leases[i].yiaddr == yiaddr) return &(leases[i]); |
| + | |
| + | return NULL; |
| + | } |
| + | |
| + | |
| + | /* find an assignable address, it check_expired is true, we check all the expired leases as well. |
| + | * Maybe this should try expired leases by age... */ |
| + | u_int32_t find_address(int check_expired) |
| + | { |
| + | u_int32_t addr, ret; |
| + | struct dhcpOfferedAddr *lease = NULL; |
| + | |
| + | addr = ntohl(server_config.start); /* addr is in host order here */ |
| + | for (;addr <= ntohl(server_config.end); addr++) { |
| + | |
| + | /* ie, 192.168.55.0 */ |
| + | if (!(addr & 0xFF)) continue; |
| + | |
| + | /* ie, 192.168.55.255 */ |
| + | if ((addr & 0xFF) == 0xFF) continue; |
| + | |
| + | /* lease is not taken */ |
| + | ret = htonl(addr); |
| + | if ((!(lease = find_lease_by_yiaddr(ret)) || |
| + | |
| + | /* or it expired and we are checking for expired leases */ |
| + | (check_expired && lease_expired(lease))) && |
| + | |
| + | /* and it isn't on the network */ |
| + | !check_ip(ret)) { |
| + | return ret; |
| + | break; |
| + | } |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | /* check is an IP is taken, if it is, add it to the lease table */ |
| + | int check_ip(u_int32_t addr) |
| + | { |
| + | struct in_addr temp; |
| + | |
| + | if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) { |
| + | temp.s_addr = addr; |
| + | LOG(LOG_INFO, "%s belongs to someone, reserving it for %ld seconds", |
| + | inet_ntoa(temp), server_config.conflict_time); |
| + | add_lease(blank_chaddr, addr, server_config.conflict_time); |
| + | return 1; |
| + | } else return 0; |
| + | } |
| + | |
| @@ -0,0 +1,24 @@ | |
| + | /* leases.h */ |
| + | #ifndef _LEASES_H |
| + | #define _LEASES_H |
| + | |
| + | |
| + | struct dhcpOfferedAddr { |
| + | u_int8_t chaddr[16]; |
| + | u_int32_t yiaddr; /* network order */ |
| + | u_int32_t expires; /* host order */ |
| + | }; |
| + | |
| + | extern unsigned char blank_chaddr[]; |
| + | |
| + | void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr); |
| + | struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease); |
| + | int lease_expired(struct dhcpOfferedAddr *lease); |
| + | struct dhcpOfferedAddr *oldest_expired_lease(void); |
| + | struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr); |
| + | struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr); |
| + | u_int32_t find_address(int check_expired); |
| + | int check_ip(u_int32_t addr); |
| + | |
| + | |
| + | #endif |
| @@ -0,0 +1,29 @@ | |
| + | /* libbb_udhcp.h - busybox compatability wrapper */ |
| + | |
| + | #ifndef _LIBBB_UDHCP_H |
| + | #define _LIBBB_UDHCP_H |
| + | |
| + | #ifdef BB_VER |
| + | #include "libbb.h" |
| + | |
| + | #ifdef CONFIG_FEATURE_UDHCP_SYSLOG |
| + | #define SYSLOG |
| + | #endif |
| + | |
| + | #ifdef CONFIG_FEATURE_UDHCP_DEBUG |
| + | #define DEBUG |
| + | #endif |
| + | |
| + | #define COMBINED_BINARY |
| + | #define VERSION "0.9.8-pre" |
| + | |
| + | #else /* ! BB_VER */ |
| + | |
| + | #define TRUE 1 |
| + | #define FALSE 0 |
| + | |
| + | #define xmalloc malloc |
| + | |
| + | #endif /* BB_VER */ |
| + | |
| + | #endif /* _LIBBB_UDHCP_H */ |
| @@ -0,0 +1,230 @@ | |
| + | /* |
| + | * options.c -- DHCP server option packet tools |
| + | * Rewrite by Russ Dill <[email protected]> July 2001 |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <string.h> |
| + | |
| + | #include "debug.h" |
| + | #include "dhcpd.h" |
| + | #include "files.h" |
| + | #include "options.h" |
| + | #include "leases.h" |
| + | |
| + | |
| + | /* supported options are easily added here */ |
| + | struct dhcp_option options[] = { |
| + | /* name[10] flags code */ |
| + | {"subnet", OPTION_IP | OPTION_REQ, 0x01}, |
| + | {"timezone", OPTION_S32, 0x02}, |
| + | {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03}, |
| + | {"timesvr", OPTION_IP | OPTION_LIST, 0x04}, |
| + | {"namesvr", OPTION_IP | OPTION_LIST, 0x05}, |
| + | {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06}, |
| + | {"logsvr", OPTION_IP | OPTION_LIST, 0x07}, |
| + | {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08}, |
| + | {"lprsvr", OPTION_IP | OPTION_LIST, 0x09}, |
| + | {"hostname", OPTION_STRING | OPTION_REQ, 0x0c}, |
| + | {"bootsize", OPTION_U16, 0x0d}, |
| + | {"domain", OPTION_STRING | OPTION_REQ, 0x0f}, |
| + | {"swapsvr", OPTION_IP, 0x10}, |
| + | {"rootpath", OPTION_STRING, 0x11}, |
| + | {"ipttl", OPTION_U8, 0x17}, |
| + | {"mtu", OPTION_U16, 0x1a}, |
| + | {"broadcast", OPTION_IP | OPTION_REQ, 0x1c}, |
| + | {"ntpsrv", OPTION_IP | OPTION_LIST, 0x2a}, |
| + | {"wins", OPTION_IP | OPTION_LIST, 0x2c}, |
| + | {"requestip", OPTION_IP, 0x32}, |
| + | {"lease", OPTION_U32, 0x33}, |
| + | {"dhcptype", OPTION_U8, 0x35}, |
| + | {"serverid", OPTION_IP, 0x36}, |
| + | {"message", OPTION_STRING, 0x38}, |
| + | {"tftp", OPTION_STRING, 0x42}, |
| + | {"bootfile", OPTION_STRING, 0x43}, |
| + | {"", 0x00, 0x00} |
| + | }; |
| + | |
| + | /* Lengths of the different option types */ |
| + | int option_lengths[] = { |
| + | [OPTION_IP] = 4, |
| + | [OPTION_IP_PAIR] = 8, |
| + | [OPTION_BOOLEAN] = 1, |
| + | [OPTION_STRING] = 1, |
| + | [OPTION_U8] = 1, |
| + | [OPTION_U16] = 2, |
| + | [OPTION_S16] = 2, |
| + | [OPTION_U32] = 4, |
| + | [OPTION_S32] = 4 |
| + | }; |
| + | |
| + | |
| + | /* get an option with bounds checking (warning, not aligned). */ |
| + | unsigned char *get_option(struct dhcpMessage *packet, int code) |
| + | { |
| + | int i, length; |
| + | unsigned char *optionptr; |
| + | int over = 0, done = 0, curr = OPTION_FIELD; |
| + | |
| + | optionptr = packet->options; |
| + | i = 0; |
| + | length = 308; |
| + | while (!done) { |
| + | if (i >= length) { |
| + | LOG(LOG_WARNING, "bogus packet, option fields too long."); |
| + | return NULL; |
| + | } |
| + | if (optionptr[i + OPT_CODE] == code) { |
| + | if (i + 1 + optionptr[i + OPT_LEN] >= length) { |
| + | LOG(LOG_WARNING, "bogus packet, option fields too long."); |
| + | return NULL; |
| + | } |
| + | return optionptr + i + 2; |
| + | } |
| + | switch (optionptr[i + OPT_CODE]) { |
| + | case DHCP_PADDING: |
| + | i++; |
| + | break; |
| + | case DHCP_OPTION_OVER: |
| + | if (i + 1 + optionptr[i + OPT_LEN] >= length) { |
| + | LOG(LOG_WARNING, "bogus packet, option fields too long."); |
| + | return NULL; |
| + | } |
| + | over = optionptr[i + 3]; |
| + | i += optionptr[OPT_LEN] + 2; |
| + | break; |
| + | case DHCP_END: |
| + | if (curr == OPTION_FIELD && over & FILE_FIELD) { |
| + | optionptr = packet->file; |
| + | i = 0; |
| + | length = 128; |
| + | curr = FILE_FIELD; |
| + | } else if (curr == FILE_FIELD && over & SNAME_FIELD) { |
| + | optionptr = packet->sname; |
| + | i = 0; |
| + | length = 64; |
| + | curr = SNAME_FIELD; |
| + | } else done = 1; |
| + | break; |
| + | default: |
| + | i += optionptr[OPT_LEN + i] + 2; |
| + | } |
| + | } |
| + | return NULL; |
| + | } |
| + | |
| + | |
| + | /* return the position of the 'end' option (no bounds checking) */ |
| + | int end_option(unsigned char *optionptr) |
| + | { |
| + | int i = 0; |
| + | |
| + | while (optionptr[i] != DHCP_END) { |
| + | if (optionptr[i] == DHCP_PADDING) i++; |
| + | else i += optionptr[i + OPT_LEN] + 2; |
| + | } |
| + | return i; |
| + | } |
| + | |
| + | |
| + | /* add an option string to the options (an option string contains an option code, |
| + | * length, then data) */ |
| + | int add_option_string(unsigned char *optionptr, unsigned char *string) |
| + | { |
| + | int end = end_option(optionptr); |
| + | |
| + | /* end position + string length + option code/length + end option */ |
| + | if (end + string[OPT_LEN] + 2 + 1 >= 308) { |
| + | LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", string[OPT_CODE]); |
| + | return 0; |
| + | } |
| + | DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]); |
| + | memcpy(optionptr + end, string, string[OPT_LEN] + 2); |
| + | optionptr[end + string[OPT_LEN] + 2] = DHCP_END; |
| + | return string[OPT_LEN] + 2; |
| + | } |
| + | |
| + | |
| + | /* add a one to four byte option to a packet */ |
| + | int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data) |
| + | { |
| + | char length = 0; |
| + | int i; |
| + | unsigned char option[2 + 4]; |
| + | unsigned char *u8; |
| + | u_int16_t *u16; |
| + | u_int32_t *u32; |
| + | u_int32_t aligned; |
| + | u8 = (unsigned char *) &aligned; |
| + | u16 = (u_int16_t *) &aligned; |
| + | u32 = &aligned; |
| + | |
| + | for (i = 0; options[i].code; i++) |
| + | if (options[i].code == code) { |
| + | length = option_lengths[options[i].flags & TYPE_MASK]; |
| + | } |
| + | |
| + | if (!length) { |
| + | DEBUG(LOG_ERR, "Could not add option 0x%02x", code); |
| + | return 0; |
| + | } |
| + | |
| + | option[OPT_CODE] = code; |
| + | option[OPT_LEN] = length; |
| + | |
| + | switch (length) { |
| + | case 1: *u8 = data; break; |
| + | case 2: *u16 = data; break; |
| + | case 4: *u32 = data; break; |
| + | } |
| + | memcpy(option + 2, &aligned, length); |
| + | return add_option_string(optionptr, option); |
| + | } |
| + | |
| + | |
| + | /* find option 'code' in opt_list */ |
| + | struct option_set *find_option(struct option_set *opt_list, char code) |
| + | { |
| + | while (opt_list && opt_list->data[OPT_CODE] < code) |
| + | opt_list = opt_list->next; |
| + | |
| + | if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list; |
| + | else return NULL; |
| + | } |
| + | |
| + | |
| + | /* add an option to the opt_list */ |
| + | void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length) |
| + | { |
| + | struct option_set *existing, *new, **curr; |
| + | |
| + | /* add it to an existing option */ |
| + | if ((existing = find_option(*opt_list, option->code))) { |
| + | DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name); |
| + | if (option->flags & OPTION_LIST) { |
| + | if (existing->data[OPT_LEN] + length <= 255) { |
| + | existing->data = realloc(existing->data, |
| + | existing->data[OPT_LEN] + length + 2); |
| + | memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length); |
| + | existing->data[OPT_LEN] += length; |
| + | } /* else, ignore the data, we could put this in a second option in the future */ |
| + | } /* else, ignore the new data */ |
| + | } else { |
| + | DEBUG(LOG_INFO, "Attaching option %s to list", option->name); |
| + | |
| + | /* make a new option */ |
| + | new = malloc(sizeof(struct option_set)); |
| + | new->data = malloc(length + 2); |
| + | new->data[OPT_CODE] = option->code; |
| + | new->data[OPT_LEN] = length; |
| + | memcpy(new->data + 2, buffer, length); |
| + | |
| + | curr = opt_list; |
| + | while (*curr && (*curr)->data[OPT_CODE] < option->code) |
| + | curr = &(*curr)->next; |
| + | |
| + | new->next = *curr; |
| + | *curr = new; |
| + | } |
| + | } |
| @@ -0,0 +1,40 @@ | |
| + | /* options.h */ |
| + | #ifndef _OPTIONS_H |
| + | #define _OPTIONS_H |
| + | |
| + | #include "packet.h" |
| + | |
| + | #define TYPE_MASK 0x0F |
| + | |
| + | enum { |
| + | OPTION_IP=1, |
| + | OPTION_IP_PAIR, |
| + | OPTION_STRING, |
| + | OPTION_BOOLEAN, |
| + | OPTION_U8, |
| + | OPTION_U16, |
| + | OPTION_S16, |
| + | OPTION_U32, |
| + | OPTION_S32 |
| + | }; |
| + | |
| + | #define OPTION_REQ 0x10 /* have the client request this option */ |
| + | #define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */ |
| + | |
| + | struct dhcp_option { |
| + | char name[10]; |
| + | char flags; |
| + | unsigned char code; |
| + | }; |
| + | |
| + | extern struct dhcp_option options[]; |
| + | extern int option_lengths[]; |
| + | |
| + | unsigned char *get_option(struct dhcpMessage *packet, int code); |
| + | int end_option(unsigned char *optionptr); |
| + | int add_option_string(unsigned char *optionptr, unsigned char *string); |
| + | int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data); |
| + | struct option_set *find_option(struct option_set *opt_list, char code); |
| + | void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length); |
| + | |
| + | #endif |
| @@ -0,0 +1,203 @@ | |
| + | #include <unistd.h> |
| + | #include <string.h> |
| + | #include <netinet/in.h> |
| + | #include <sys/types.h> |
| + | #include <sys/socket.h> |
| + | #include <features.h> |
| + | #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 |
| + | #include <netpacket/packet.h> |
| + | #include <net/ethernet.h> |
| + | #else |
| + | #include <asm/types.h> |
| + | #include <linux/if_packet.h> |
| + | #include <linux/if_ether.h> |
| + | #endif |
| + | #include <errno.h> |
| + | |
| + | #include "packet.h" |
| + | #include "debug.h" |
| + | #include "dhcpd.h" |
| + | #include "options.h" |
| + | |
| + | |
| + | void init_header(struct dhcpMessage *packet, char type) |
| + | { |
| + | memset(packet, 0, sizeof(struct dhcpMessage)); |
| + | switch (type) { |
| + | case DHCPDISCOVER: |
| + | case DHCPREQUEST: |
| + | case DHCPRELEASE: |
| + | case DHCPINFORM: |
| + | packet->op = BOOTREQUEST; |
| + | break; |
| + | case DHCPOFFER: |
| + | case DHCPACK: |
| + | case DHCPNAK: |
| + | packet->op = BOOTREPLY; |
| + | } |
| + | packet->htype = ETH_10MB; |
| + | packet->hlen = ETH_10MB_LEN; |
| + | packet->cookie = htonl(DHCP_MAGIC); |
| + | packet->options[0] = DHCP_END; |
| + | add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type); |
| + | } |
| + | |
| + | |
| + | /* read a packet from socket fd, return -1 on read error, -2 on packet error */ |
| + | int get_packet(struct dhcpMessage *packet, int fd) |
| + | { |
| + | int bytes; |
| + | int i; |
| + | const char broken_vendors[][8] = { |
| + | "MSFT 98", |
| + | "" |
| + | }; |
| + | char unsigned *vendor; |
| + | |
| + | memset(packet, 0, sizeof(struct dhcpMessage)); |
| + | bytes = read(fd, packet, sizeof(struct dhcpMessage)); |
| + | if (bytes < 0) { |
| + | DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring"); |
| + | return -1; |
| + | } |
| + | |
| + | if (ntohl(packet->cookie) != DHCP_MAGIC) { |
| + | LOG(LOG_ERR, "received bogus message, ignoring"); |
| + | return -2; |
| + | } |
| + | DEBUG(LOG_INFO, "Received a packet"); |
| + | |
| + | if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) { |
| + | for (i = 0; broken_vendors[i][0]; i++) { |
| + | if (vendor[OPT_LEN - 2] == (unsigned char) strlen(broken_vendors[i]) && |
| + | !strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) { |
| + | DEBUG(LOG_INFO, "broken client (%s), forcing broadcast", |
| + | broken_vendors[i]); |
| + | packet->flags |= htons(BROADCAST_FLAG); |
| + | } |
| + | } |
| + | } |
| + | |
| + | |
| + | return bytes; |
| + | } |
| + | |
| + | |
| + | u_int16_t checksum(void *addr, int count) |
| + | { |
| + | /* Compute Internet Checksum for "count" bytes |
| + | * beginning at location "addr". |
| + | */ |
| + | register int32_t sum = 0; |
| + | u_int16_t *source = (u_int16_t *) addr; |
| + | |
| + | while (count > 1) { |
| + | /* This is the inner loop */ |
| + | sum += *source++; |
| + | count -= 2; |
| + | } |
| + | |
| + | /* Add left-over byte, if any */ |
| + | if (count > 0) { |
| + | /* Make sure that the left-over byte is added correctly both |
| + | * with little and big endian hosts */ |
| + | u_int16_t tmp = 0; |
| + | *(unsigned char *) (&tmp) = * (unsigned char *) source; |
| + | sum += tmp; |
| + | } |
| + | /* Fold 32-bit sum to 16 bits */ |
| + | while (sum >> 16) |
| + | sum = (sum & 0xffff) + (sum >> 16); |
| + | |
| + | return ~sum; |
| + | } |
| + | |
| + | |
| + | /* Constuct a ip/udp header for a packet, and specify the source and dest hardware address */ |
| + | int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, |
| + | u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex) |
| + | { |
| + | int fd; |
| + | int result; |
| + | struct sockaddr_ll dest; |
| + | struct udp_dhcp_packet packet; |
| + | |
| + | if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { |
| + | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); |
| + | return -1; |
| + | } |
| + | |
| + | memset(&dest, 0, sizeof(dest)); |
| + | memset(&packet, 0, sizeof(packet)); |
| + | |
| + | dest.sll_family = AF_PACKET; |
| + | dest.sll_protocol = htons(ETH_P_IP); |
| + | dest.sll_ifindex = ifindex; |
| + | dest.sll_halen = 6; |
| + | memcpy(dest.sll_addr, dest_arp, 6); |
| + | if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) { |
| + | DEBUG(LOG_ERR, "bind call failed: %s", strerror(errno)); |
| + | close(fd); |
| + | return -1; |
| + | } |
| + | |
| + | packet.ip.protocol = IPPROTO_UDP; |
| + | packet.ip.saddr = source_ip; |
| + | packet.ip.daddr = dest_ip; |
| + | packet.udp.source = htons(source_port); |
| + | packet.udp.dest = htons(dest_port); |
| + | packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */ |
| + | packet.ip.tot_len = packet.udp.len; |
| + | memcpy(&(packet.data), payload, sizeof(struct dhcpMessage)); |
| + | packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet)); |
| + | |
| + | packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet)); |
| + | packet.ip.ihl = sizeof(packet.ip) >> 2; |
| + | packet.ip.version = IPVERSION; |
| + | packet.ip.ttl = IPDEFTTL; |
| + | packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip)); |
| + | |
| + | result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest)); |
| + | if (result <= 0) { |
| + | DEBUG(LOG_ERR, "write on socket failed: %s", strerror(errno)); |
| + | } |
| + | close(fd); |
| + | return result; |
| + | } |
| + | |
| + | |
| + | /* Let the kernel do all the work for packet generation */ |
| + | int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, |
| + | u_int32_t dest_ip, int dest_port) |
| + | { |
| + | int n = 1; |
| + | int fd, result; |
| + | struct sockaddr_in client; |
| + | |
| + | if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) |
| + | return -1; |
| + | |
| + | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) |
| + | return -1; |
| + | |
| + | memset(&client, 0, sizeof(client)); |
| + | client.sin_family = AF_INET; |
| + | client.sin_port = htons(source_port); |
| + | client.sin_addr.s_addr = source_ip; |
| + | |
| + | if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) |
| + | return -1; |
| + | |
| + | memset(&client, 0, sizeof(client)); |
| + | client.sin_family = AF_INET; |
| + | client.sin_port = htons(dest_port); |
| + | client.sin_addr.s_addr = dest_ip; |
| + | |
| + | if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) |
| + | return -1; |
| + | |
| + | result = write(fd, payload, sizeof(struct dhcpMessage)); |
| + | close(fd); |
| + | return result; |
| + | } |
| + | |
| @@ -0,0 +1,41 @@ | |
| + | #ifndef _PACKET_H |
| + | #define _PACKET_H |
| + | |
| + | #include <netinet/udp.h> |
| + | #include <netinet/ip.h> |
| + | |
| + | struct dhcpMessage { |
| + | u_int8_t op; |
| + | u_int8_t htype; |
| + | u_int8_t hlen; |
| + | u_int8_t hops; |
| + | u_int32_t xid; |
| + | u_int16_t secs; |
| + | u_int16_t flags; |
| + | u_int32_t ciaddr; |
| + | u_int32_t yiaddr; |
| + | u_int32_t siaddr; |
| + | u_int32_t giaddr; |
| + | u_int8_t chaddr[16]; |
| + | u_int8_t sname[64]; |
| + | u_int8_t file[128]; |
| + | u_int32_t cookie; |
| + | u_int8_t options[308]; /* 312 - cookie */ |
| + | }; |
| + | |
| + | struct udp_dhcp_packet { |
| + | struct iphdr ip; |
| + | struct udphdr udp; |
| + | struct dhcpMessage data; |
| + | }; |
| + | |
| + | void init_header(struct dhcpMessage *packet, char type); |
| + | int get_packet(struct dhcpMessage *packet, int fd); |
| + | u_int16_t checksum(void *addr, int count); |
| + | int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, |
| + | u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex); |
| + | int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, |
| + | u_int32_t dest_ip, int dest_port); |
| + | |
| + | |
| + | #endif |
| @@ -0,0 +1,69 @@ | |
| + | /* pidfile.c |
| + | * |
| + | * Functions to assist in the writing and removing of pidfiles. |
| + | * |
| + | * Russ Dill <[email protected]> Soptember 2001 |
| + | * |
| + | * This program is free software; you can redistribute it and/or modify |
| + | * it under the terms of the GNU General Public License as published by |
| + | * the Free Software Foundation; either version 2 of the License, or |
| + | * (at your option) any later version. |
| + | * |
| + | * This program is distributed in the hope that it will be useful, |
| + | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + | * GNU General Public License for more details. |
| + | * |
| + | * You should have received a copy of the GNU General Public License |
| + | * along with this program; if not, write to the Free Software |
| + | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| + | */ |
| + | |
| + | #include <sys/types.h> |
| + | #include <sys/stat.h> |
| + | #include <fcntl.h> |
| + | #include <unistd.h> |
| + | #include <errno.h> |
| + | #include <string.h> |
| + | #include <stdio.h> |
| + | |
| + | #include "debug.h" |
| + | |
| + | int pidfile_acquire(char *pidfile) |
| + | { |
| + | int pid_fd; |
| + | if (pidfile == NULL) return -1; |
| + | |
| + | pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644); |
| + | if (pid_fd < 0) { |
| + | LOG(LOG_ERR, "Unable to open pidfile %s: %s\n", |
| + | pidfile, strerror(errno)); |
| + | } else { |
| + | lockf(pid_fd, F_LOCK, 0); |
| + | } |
| + | |
| + | return pid_fd; |
| + | } |
| + | |
| + | |
| + | void pidfile_write_release(int pid_fd) |
| + | { |
| + | FILE *out; |
| + | |
| + | if (pid_fd < 0) return; |
| + | |
| + | if ((out = fdopen(pid_fd, "w")) != NULL) { |
| + | fprintf(out, "%d\n", getpid()); |
| + | fclose(out); |
| + | } |
| + | lockf(pid_fd, F_UNLCK, 0); |
| + | close(pid_fd); |
| + | } |
| + | |
| + | |
| + | void pidfile_delete(char *pidfile) |
| + | { |
| + | if (pidfile) unlink(pidfile); |
| + | } |
| + | |
| + | |
| @@ -0,0 +1,26 @@ | |
| + | /* pidfile.h |
| + | * |
| + | * Functions to assist in the writing and removing of pidfiles. |
| + | * |
| + | * Russ Dill <[email protected]> Soptember 2001 |
| + | * |
| + | * This program is free software; you can redistribute it and/or modify |
| + | * it under the terms of the GNU General Public License as published by |
| + | * the Free Software Foundation; either version 2 of the License, or |
| + | * (at your option) any later version. |
| + | * |
| + | * This program is distributed in the hope that it will be useful, |
| + | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + | * GNU General Public License for more details. |
| + | * |
| + | * You should have received a copy of the GNU General Public License |
| + | * along with this program; if not, write to the Free Software |
| + | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| + | */ |
| + | |
| + | |
| + | int pidfile_acquire(char *pidfile); |
| + | void pidfile_write_release(int pid_fd); |
| + | void pidfile_delete(char *pidfile); |
| + | |
| @@ -0,0 +1,228 @@ | |
| + | /* script.c |
| + | * |
| + | * Functions to call the DHCP client notification scripts |
| + | * |
| + | * Russ Dill <[email protected]> July 2001 |
| + | * |
| + | * This program is free software; you can redistribute it and/or modify |
| + | * it under the terms of the GNU General Public License as published by |
| + | * the Free Software Foundation; either version 2 of the License, or |
| + | * (at your option) any later version. |
| + | * |
| + | * This program is distributed in the hope that it will be useful, |
| + | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + | * GNU General Public License for more details. |
| + | * |
| + | * You should have received a copy of the GNU General Public License |
| + | * along with this program; if not, write to the Free Software |
| + | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| + | */ |
| + | |
| + | #include <string.h> |
| + | #include <unistd.h> |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <sys/socket.h> |
| + | #include <netinet/in.h> |
| + | #include <arpa/inet.h> |
| + | #include <sys/types.h> |
| + | #include <sys/wait.h> |
| + | #include <errno.h> |
| + | |
| + | #include "options.h" |
| + | #include "dhcpd.h" |
| + | #include "dhcpc.h" |
| + | #include "packet.h" |
| + | #include "options.h" |
| + | #include "debug.h" |
| + | |
| + | /* get a rough idea of how long an option will be (rounding up...) */ |
| + | static int max_option_length[] = { |
| + | [OPTION_IP] = sizeof("255.255.255.255 "), |
| + | [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, |
| + | [OPTION_STRING] = 1, |
| + | [OPTION_BOOLEAN] = sizeof("yes "), |
| + | [OPTION_U8] = sizeof("255 "), |
| + | [OPTION_U16] = sizeof("65535 "), |
| + | [OPTION_S16] = sizeof("-32768 "), |
| + | [OPTION_U32] = sizeof("4294967295 "), |
| + | [OPTION_S32] = sizeof("-2147483684 "), |
| + | }; |
| + | |
| + | |
| + | static int upper_length(int length, struct dhcp_option *option) |
| + | { |
| + | return max_option_length[option->flags & TYPE_MASK] * |
| + | (length / option_lengths[option->flags & TYPE_MASK]); |
| + | } |
| + | |
| + | |
| + | static int sprintip(char *dest, char *pre, unsigned char *ip) { |
| + | return sprintf(dest, "%s%d.%d.%d.%d ", pre, ip[0], ip[1], ip[2], ip[3]); |
| + | } |
| + | |
| + | |
| + | /* Fill dest with the text of option 'option'. */ |
| + | static void fill_options(char *dest, unsigned char *option, struct dhcp_option *type_p) |
| + | { |
| + | int type, optlen; |
| + | u_int16_t val_u16; |
| + | int16_t val_s16; |
| + | u_int32_t val_u32; |
| + | int32_t val_s32; |
| + | int len = option[OPT_LEN - 2]; |
| + | |
| + | dest += sprintf(dest, "%s=", type_p->name); |
| + | |
| + | type = type_p->flags & TYPE_MASK; |
| + | optlen = option_lengths[type]; |
| + | for(;;) { |
| + | switch (type) { |
| + | case OPTION_IP_PAIR: |
| + | dest += sprintip(dest, "", option); |
| + | *(dest++) = '/'; |
| + | option += 4; |
| + | optlen = 4; |
| + | case OPTION_IP: /* Works regardless of host byte order. */ |
| + | dest += sprintip(dest, "", option); |
| + | break; |
| + | case OPTION_BOOLEAN: |
| + | dest += sprintf(dest, *option ? "yes " : "no "); |
| + | break; |
| + | case OPTION_U8: |
| + | dest += sprintf(dest, "%u ", *option); |
| + | break; |
| + | case OPTION_U16: |
| + | memcpy(&val_u16, option, 2); |
| + | dest += sprintf(dest, "%u ", ntohs(val_u16)); |
| + | break; |
| + | case OPTION_S16: |
| + | memcpy(&val_s16, option, 2); |
| + | dest += sprintf(dest, "%d ", ntohs(val_s16)); |
| + | break; |
| + | case OPTION_U32: |
| + | memcpy(&val_u32, option, 4); |
| + | dest += sprintf(dest, "%lu ", (unsigned long) ntohl(val_u32)); |
| + | break; |
| + | case OPTION_S32: |
| + | memcpy(&val_s32, option, 4); |
| + | dest += sprintf(dest, "%ld ", (long) ntohl(val_s32)); |
| + | break; |
| + | case OPTION_STRING: |
| + | memcpy(dest, option, len); |
| + | dest[len] = '\0'; |
| + | return; /* Short circuit this case */ |
| + | } |
| + | option += optlen; |
| + | len -= optlen; |
| + | if (len <= 0) break; |
| + | } |
| + | } |
| + | |
| + | |
| + | static char *find_env(const char *prefix, char *defaultstr) |
| + | { |
| + | extern char **environ; |
| + | char **ptr; |
| + | const int len = strlen(prefix); |
| + | |
| + | for (ptr = environ; *ptr != NULL; ptr++) { |
| + | if (strncmp(prefix, *ptr, len) == 0) |
| + | return *ptr; |
| + | } |
| + | return defaultstr; |
| + | } |
| + | |
| + | |
| + | /* put all the paramaters into an environment */ |
| + | static char **fill_envp(struct dhcpMessage *packet) |
| + | { |
| + | int num_options = 0; |
| + | int i, j; |
| + | char **envp; |
| + | unsigned char *temp; |
| + | char over = 0; |
| + | |
| + | if (packet == NULL) |
| + | num_options = 0; |
| + | else { |
| + | for (i = 0; options[i].code; i++) |
| + | if (get_option(packet, options[i].code)) |
| + | num_options++; |
| + | if (packet->siaddr) num_options++; |
| + | if ((temp = get_option(packet, DHCP_OPTION_OVER))) |
| + | over = *temp; |
| + | if (!(over & FILE_FIELD) && packet->file[0]) num_options++; |
| + | if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++; |
| + | } |
| + | |
| + | envp = xmalloc((num_options + 5) * sizeof(char *)); |
| + | envp[0] = xmalloc(sizeof("interface=") + strlen(client_config.interface)); |
| + | sprintf(envp[0], "interface=%s", client_config.interface); |
| + | envp[1] = find_env("PATH", "PATH=/bin:/usr/bin:/sbin:/usr/sbin"); |
| + | envp[2] = find_env("HOME", "HOME=/"); |
| + | |
| + | if (packet == NULL) { |
| + | envp[3] = NULL; |
| + | return envp; |
| + | } |
| + | |
| + | envp[3] = xmalloc(sizeof("ip=255.255.255.255")); |
| + | sprintip(envp[3], "ip=", (unsigned char *) &packet->yiaddr); |
| + | for (i = 0, j = 4; options[i].code; i++) { |
| + | if ((temp = get_option(packet, options[i].code))) { |
| + | envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], &options[i]) + strlen(options[i].name) + 2); |
| + | fill_options(envp[j], temp, &options[i]); |
| + | j++; |
| + | } |
| + | } |
| + | if (packet->siaddr) { |
| + | envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); |
| + | sprintip(envp[j++], "siaddr=", (unsigned char *) &packet->siaddr); |
| + | } |
| + | if (!(over & FILE_FIELD) && packet->file[0]) { |
| + | /* watch out for invalid packets */ |
| + | packet->file[sizeof(packet->file) - 1] = '\0'; |
| + | envp[j] = xmalloc(sizeof("boot_file=") + strlen(packet->file)); |
| + | sprintf(envp[j++], "boot_file=%s", packet->file); |
| + | } |
| + | if (!(over & SNAME_FIELD) && packet->sname[0]) { |
| + | /* watch out for invalid packets */ |
| + | packet->sname[sizeof(packet->sname) - 1] = '\0'; |
| + | envp[j] = xmalloc(sizeof("sname=") + strlen(packet->sname)); |
| + | sprintf(envp[j++], "sname=%s", packet->sname); |
| + | } |
| + | envp[j] = NULL; |
| + | return envp; |
| + | } |
| + | |
| + | |
| + | /* Call a script with a par file and env vars */ |
| + | void run_script(struct dhcpMessage *packet, const char *name) |
| + | { |
| + | int pid; |
| + | char **envp; |
| + | |
| + | if (client_config.script == NULL) |
| + | return; |
| + | |
| + | /* call script */ |
| + | pid = fork(); |
| + | if (pid) { |
| + | waitpid(pid, NULL, 0); |
| + | return; |
| + | } else if (pid == 0) { |
| + | envp = fill_envp(packet); |
| + | |
| + | /* close fd's? */ |
| + | |
| + | /* exec script */ |
| + | DEBUG(LOG_INFO, "execle'ing %s", client_config.script); |
| + | execle(client_config.script, client_config.script, |
| + | name, NULL, envp); |
| + | LOG(LOG_ERR, "script %s failed: %s", |
| + | client_config.script, strerror(errno)); |
| + | exit(1); |
| + | } |
| + | } |
| @@ -0,0 +1,6 @@ | |
| + | #ifndef _SCRIPT_H |
| + | #define _SCRIPT_H |
| + | |
| + | void run_script(struct dhcpMessage *packet, const char *name); |
| + | |
| + | #endif |
| @@ -0,0 +1,263 @@ | |
| + | /* serverpacket.c |
| + | * |
| + | * Constuct and send DHCP server packets |
| + | * |
| + | * Russ Dill <[email protected]> July 2001 |
| + | * |
| + | * This program is free software; you can redistribute it and/or modify |
| + | * it under the terms of the GNU General Public License as published by |
| + | * the Free Software Foundation; either version 2 of the License, or |
| + | * (at your option) any later version. |
| + | * |
| + | * This program is distributed in the hope that it will be useful, |
| + | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + | * GNU General Public License for more details. |
| + | * |
| + | * You should have received a copy of the GNU General Public License |
| + | * along with this program; if not, write to the Free Software |
| + | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| + | */ |
| + | |
| + | #include <sys/socket.h> |
| + | #include <netinet/in.h> |
| + | #include <arpa/inet.h> |
| + | #include <string.h> |
| + | #include <time.h> |
| + | |
| + | #include "packet.h" |
| + | #include "debug.h" |
| + | #include "dhcpd.h" |
| + | #include "options.h" |
| + | #include "leases.h" |
| + | |
| + | /* send a packet to giaddr using the kernel ip stack */ |
| + | static int send_packet_to_relay(struct dhcpMessage *payload) |
| + | { |
| + | DEBUG(LOG_INFO, "Forwarding packet to relay"); |
| + | |
| + | return kernel_packet(payload, server_config.server, SERVER_PORT, |
| + | payload->giaddr, SERVER_PORT); |
| + | } |
| + | |
| + | |
| + | /* send a packet to a specific arp address and ip address by creating our own ip packet */ |
| + | static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast) |
| + | { |
| + | unsigned char *chaddr; |
| + | u_int32_t ciaddr; |
| + | |
| + | if (force_broadcast) { |
| + | DEBUG(LOG_INFO, "broadcasting packet to client (NAK)"); |
| + | ciaddr = INADDR_BROADCAST; |
| + | chaddr = MAC_BCAST_ADDR; |
| + | } else if (payload->ciaddr) { |
| + | DEBUG(LOG_INFO, "unicasting packet to client ciaddr"); |
| + | ciaddr = payload->ciaddr; |
| + | chaddr = payload->chaddr; |
| + | } else if (ntohs(payload->flags) & BROADCAST_FLAG) { |
| + | DEBUG(LOG_INFO, "broadcasting packet to client (requested)"); |
| + | ciaddr = INADDR_BROADCAST; |
| + | chaddr = MAC_BCAST_ADDR; |
| + | } else { |
| + | DEBUG(LOG_INFO, "unicasting packet to client yiaddr"); |
| + | ciaddr = payload->yiaddr; |
| + | chaddr = payload->chaddr; |
| + | } |
| + | return raw_packet(payload, server_config.server, SERVER_PORT, |
| + | ciaddr, CLIENT_PORT, chaddr, server_config.ifindex); |
| + | } |
| + | |
| + | |
| + | /* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */ |
| + | static int send_packet(struct dhcpMessage *payload, int force_broadcast) |
| + | { |
| + | int ret; |
| + | |
| + | if (payload->giaddr) |
| + | ret = send_packet_to_relay(payload); |
| + | else ret = send_packet_to_client(payload, force_broadcast); |
| + | return ret; |
| + | } |
| + | |
| + | |
| + | static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type) |
| + | { |
| + | init_header(packet, type); |
| + | packet->xid = oldpacket->xid; |
| + | memcpy(packet->chaddr, oldpacket->chaddr, 16); |
| + | packet->flags = oldpacket->flags; |
| + | packet->giaddr = oldpacket->giaddr; |
| + | packet->ciaddr = oldpacket->ciaddr; |
| + | add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server); |
| + | } |
| + | |
| + | |
| + | /* add in the bootp options */ |
| + | static void add_bootp_options(struct dhcpMessage *packet) |
| + | { |
| + | packet->siaddr = server_config.siaddr; |
| + | if (server_config.sname) |
| + | strncpy(packet->sname, server_config.sname, sizeof(packet->sname) - 1); |
| + | if (server_config.boot_file) |
| + | strncpy(packet->file, server_config.boot_file, sizeof(packet->file) - 1); |
| + | } |
| + | |
| + | |
| + | /* send a DHCP OFFER to a DHCP DISCOVER */ |
| + | int sendOffer(struct dhcpMessage *oldpacket) |
| + | { |
| + | struct dhcpMessage packet; |
| + | struct dhcpOfferedAddr *lease = NULL; |
| + | u_int32_t req_align, lease_time_align = server_config.lease; |
| + | unsigned char *req, *lease_time; |
| + | struct option_set *curr; |
| + | struct in_addr addr; |
| + | |
| + | init_packet(&packet, oldpacket, DHCPOFFER); |
| + | |
| + | /* ADDME: if static, short circuit */ |
| + | /* the client is in our lease/offered table */ |
| + | if ((lease = find_lease_by_chaddr(oldpacket->chaddr))) { |
| + | if (!lease_expired(lease)) |
| + | lease_time_align = lease->expires - time(0); |
| + | packet.yiaddr = lease->yiaddr; |
| + | |
| + | /* Or the client has a requested ip */ |
| + | } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) && |
| + | |
| + | /* Don't look here (ugly hackish thing to do) */ |
| + | memcpy(&req_align, req, 4) && |
| + | |
| + | /* and the ip is in the lease range */ |
| + | ntohl(req_align) >= ntohl(server_config.start) && |
| + | ntohl(req_align) <= ntohl(server_config.end) && |
| + | |
| + | /* and its not already taken/offered */ /* ADDME: check that its not a static lease */ |
| + | ((!(lease = find_lease_by_yiaddr(req_align)) || |
| + | |
| + | /* or its taken, but expired */ /* ADDME: or maybe in here */ |
| + | lease_expired(lease)))) { |
| + | packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */ |
| + | |
| + | /* otherwise, find a free IP */ /*ADDME: is it a static lease? */ |
| + | } else { |
| + | packet.yiaddr = find_address(0); |
| + | |
| + | /* try for an expired lease */ |
| + | if (!packet.yiaddr) packet.yiaddr = find_address(1); |
| + | } |
| + | |
| + | if(!packet.yiaddr) { |
| + | LOG(LOG_WARNING, "no IP addresses to give -- OFFER abandoned"); |
| + | return -1; |
| + | } |
| + | |
| + | if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) { |
| + | LOG(LOG_WARNING, "lease pool is full -- OFFER abandoned"); |
| + | return -1; |
| + | } |
| + | |
| + | if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { |
| + | memcpy(&lease_time_align, lease_time, 4); |
| + | lease_time_align = ntohl(lease_time_align); |
| + | if (lease_time_align > server_config.lease) |
| + | lease_time_align = server_config.lease; |
| + | } |
| + | |
| + | /* Make sure we aren't just using the lease time from the previous offer */ |
| + | if (lease_time_align < server_config.min_lease) |
| + | lease_time_align = server_config.lease; |
| + | /* ADDME: end of short circuit */ |
| + | add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); |
| + | |
| + | curr = server_config.options; |
| + | while (curr) { |
| + | if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) |
| + | add_option_string(packet.options, curr->data); |
| + | curr = curr->next; |
| + | } |
| + | |
| + | add_bootp_options(&packet); |
| + | |
| + | addr.s_addr = packet.yiaddr; |
| + | LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr)); |
| + | return send_packet(&packet, 0); |
| + | } |
| + | |
| + | |
| + | int sendNAK(struct dhcpMessage *oldpacket) |
| + | { |
| + | struct dhcpMessage packet; |
| + | |
| + | init_packet(&packet, oldpacket, DHCPNAK); |
| + | |
| + | DEBUG(LOG_INFO, "sending NAK"); |
| + | return send_packet(&packet, 1); |
| + | } |
| + | |
| + | |
| + | int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr) |
| + | { |
| + | struct dhcpMessage packet; |
| + | struct option_set *curr; |
| + | unsigned char *lease_time; |
| + | u_int32_t lease_time_align = server_config.lease; |
| + | struct in_addr addr; |
| + | |
| + | init_packet(&packet, oldpacket, DHCPACK); |
| + | packet.yiaddr = yiaddr; |
| + | |
| + | if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { |
| + | memcpy(&lease_time_align, lease_time, 4); |
| + | lease_time_align = ntohl(lease_time_align); |
| + | if (lease_time_align > server_config.lease) |
| + | lease_time_align = server_config.lease; |
| + | else if (lease_time_align < server_config.min_lease) |
| + | lease_time_align = server_config.lease; |
| + | } |
| + | |
| + | add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); |
| + | |
| + | curr = server_config.options; |
| + | while (curr) { |
| + | if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) |
| + | add_option_string(packet.options, curr->data); |
| + | curr = curr->next; |
| + | } |
| + | |
| + | add_bootp_options(&packet); |
| + | |
| + | addr.s_addr = packet.yiaddr; |
| + | LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(addr)); |
| + | |
| + | if (send_packet(&packet, 0) < 0) |
| + | return -1; |
| + | |
| + | add_lease(packet.chaddr, packet.yiaddr, lease_time_align); |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | int send_inform(struct dhcpMessage *oldpacket) |
| + | { |
| + | struct dhcpMessage packet; |
| + | struct option_set *curr; |
| + | |
| + | init_packet(&packet, oldpacket, DHCPACK); |
| + | |
| + | curr = server_config.options; |
| + | while (curr) { |
| + | if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) |
| + | add_option_string(packet.options, curr->data); |
| + | curr = curr->next; |
| + | } |
| + | |
| + | add_bootp_options(&packet); |
| + | |
| + | return send_packet(&packet, 0); |
| + | } |
| + | |
| + | |
| + | |
| @@ -0,0 +1,11 @@ | |
| + | #ifndef _SERVERPACKET_H |
| + | #define _SERVERPACKET_H |
| + | |
| + | |
| + | int sendOffer(struct dhcpMessage *oldpacket); |
| + | int sendNAK(struct dhcpMessage *oldpacket); |
| + | int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr); |
| + | int send_inform(struct dhcpMessage *oldpacket); |
| + | |
| + | |
| + | #endif |
| @@ -0,0 +1,157 @@ | |
| + | /* |
| + | * socket.c -- DHCP server client/server socket creation |
| + | * |
| + | * udhcp client/server |
| + | * Copyright (C) 1999 Matthew Ramsay <[email protected]> |
| + | * Chris Trew <[email protected]> |
| + | * |
| + | * Rewrite by Russ Dill <[email protected]> July 2001 |
| + | * |
| + | * This program is free software; you can redistribute it and/or modify |
| + | * it under the terms of the GNU General Public License as published by |
| + | * the Free Software Foundation; either version 2 of the License, or |
| + | * (at your option) any later version. |
| + | * |
| + | * This program is distributed in the hope that it will be useful, |
| + | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + | * GNU General Public License for more details. |
| + | * |
| + | * You should have received a copy of the GNU General Public License |
| + | * along with this program; if not, write to the Free Software |
| + | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| + | */ |
| + | |
| + | #include <sys/types.h> |
| + | #include <sys/socket.h> |
| + | #include <sys/ioctl.h> |
| + | #include <netinet/in.h> |
| + | #include <unistd.h> |
| + | #include <string.h> |
| + | #include <arpa/inet.h> |
| + | #include <net/if.h> |
| + | #include <errno.h> |
| + | #include <features.h> |
| + | #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 |
| + | #include <netpacket/packet.h> |
| + | #include <net/ethernet.h> |
| + | #else |
| + | #include <asm/types.h> |
| + | #include <linux/if_packet.h> |
| + | #include <linux/if_ether.h> |
| + | #endif |
| + | |
| + | #include "debug.h" |
| + | |
| + | int read_interface(char *interface, int *ifindex, u_int32_t *addr, unsigned char *arp) |
| + | { |
| + | int fd; |
| + | struct ifreq ifr; |
| + | struct sockaddr_in *our_ip; |
| + | |
| + | memset(&ifr, 0, sizeof(struct ifreq)); |
| + | if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) { |
| + | ifr.ifr_addr.sa_family = AF_INET; |
| + | strcpy(ifr.ifr_name, interface); |
| + | |
| + | if (addr) { |
| + | if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { |
| + | our_ip = (struct sockaddr_in *) &ifr.ifr_addr; |
| + | *addr = our_ip->sin_addr.s_addr; |
| + | DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr)); |
| + | } else { |
| + | LOG(LOG_ERR, "SIOCGIFADDR failed, is the interface up and configured?: %s", |
| + | strerror(errno)); |
| + | return -1; |
| + | } |
| + | } |
| + | |
| + | if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) { |
| + | DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex); |
| + | *ifindex = ifr.ifr_ifindex; |
| + | } else { |
| + | LOG(LOG_ERR, "SIOCGIFINDEX failed!: %s", strerror(errno)); |
| + | return -1; |
| + | } |
| + | if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) { |
| + | memcpy(arp, ifr.ifr_hwaddr.sa_data, 6); |
| + | DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", |
| + | arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]); |
| + | } else { |
| + | LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %s", strerror(errno)); |
| + | return -1; |
| + | } |
| + | } else { |
| + | LOG(LOG_ERR, "socket failed!: %s", strerror(errno)); |
| + | return -1; |
| + | } |
| + | close(fd); |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | int listen_socket(unsigned int ip, int port, char *inf) |
| + | { |
| + | struct ifreq interface; |
| + | int fd; |
| + | struct sockaddr_in addr; |
| + | int n = 1; |
| + | |
| + | DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s\n", ip, port, inf); |
| + | if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { |
| + | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); |
| + | return -1; |
| + | } |
| + | |
| + | memset(&addr, 0, sizeof(addr)); |
| + | addr.sin_family = AF_INET; |
| + | addr.sin_port = htons(port); |
| + | addr.sin_addr.s_addr = ip; |
| + | |
| + | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) { |
| + | close(fd); |
| + | return -1; |
| + | } |
| + | if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) { |
| + | close(fd); |
| + | return -1; |
| + | } |
| + | |
| + | strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ); |
| + | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,(char *)&interface, sizeof(interface)) < 0) { |
| + | close(fd); |
| + | return -1; |
| + | } |
| + | |
| + | if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) { |
| + | close(fd); |
| + | return -1; |
| + | } |
| + | |
| + | return fd; |
| + | } |
| + | |
| + | |
| + | int raw_socket(int ifindex) |
| + | { |
| + | int fd; |
| + | struct sockaddr_ll sock; |
| + | |
| + | DEBUG(LOG_INFO, "Opening raw socket on ifindex %d\n", ifindex); |
| + | if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { |
| + | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); |
| + | return -1; |
| + | } |
| + | |
| + | sock.sll_family = AF_PACKET; |
| + | sock.sll_protocol = htons(ETH_P_IP); |
| + | sock.sll_ifindex = ifindex; |
| + | if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) { |
| + | DEBUG(LOG_ERR, "bind call failed: %s", strerror(errno)); |
| + | close(fd); |
| + | return -1; |
| + | } |
| + | |
| + | return fd; |
| + | } |
| + | |
| @@ -0,0 +1,9 @@ | |
| + | /* socket.h */ |
| + | #ifndef _SOCKET_H |
| + | #define _SOCKET_H |
| + | |
| + | int read_interface(char *interface, int *ifindex, u_int32_t *addr, unsigned char *arp); |
| + | int listen_socket(unsigned int ip, int port, char *inf); |
| + | int raw_socket(int ifindex); |
| + | |
| + | #endif |
| @@ -1,1587 +0,0 @@ | |
| - | /* dhcpd.c |
| - | * |
| - | * udhcp DHCP client |
| - | * |
| - | * Russ Dill <[email protected]> July 2001 |
| - | * |
| - | * Converted to busybox by Glenn McGrath August 2002 |
| - | * |
| - | * This program is free software; you can redistribute it and/or modify |
| - | * it under the terms of the GNU General Public License as published by |
| - | * the Free Software Foundation; either version 2 of the License, or |
| - | * (at your option) any later version. |
| - | * |
| - | * This program is distributed in the hope that it will be useful, |
| - | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| - | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| - | * GNU General Public License for more details. |
| - | * |
| - | * You should have received a copy of the GNU General Public License |
| - | * along with this program; if not, write to the Free Software |
| - | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| - | */ |
| - | |
| - | #include <stdio.h> |
| - | #include <sys/time.h> |
| - | #include <sys/types.h> |
| - | #include <sys/file.h> |
| - | #include <unistd.h> |
| - | #include <getopt.h> |
| - | #include <stdlib.h> |
| - | #include <sys/socket.h> |
| - | #include <netinet/in.h> |
| - | #include <arpa/inet.h> |
| - | #include <signal.h> |
| - | #include <time.h> |
| - | #include <string.h> |
| - | #include <sys/ioctl.h> |
| - | #include <net/if.h> |
| - | #include <errno.h> |
| - | #include <netinet/ip.h> |
| - | #include <netinet/udp.h> |
| - | #include <sys/types.h> |
| - | #include <sys/wait.h> |
| - | |
| - | #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 |
| - | #include <netpacket/packet.h> |
| - | #include <net/ethernet.h> |
| - | #else |
| - | #include <asm/types.h> |
| - | #include <linux/if_packet.h> |
| - | #include <linux/if_ether.h> |
| - | #endif |
| - | #include "libbb.h" |
| - | |
| - | static int state; |
| - | static unsigned long requested_ip; /* = 0 */ |
| - | static unsigned long server_addr; |
| - | static unsigned long timeout; |
| - | static int packet_num; /* = 0 */ |
| - | static int fd_main; |
| - | |
| - | /* #define DEBUG */ |
| - | |
| - | #define VERSION "0.9.7" |
| - | |
| - | #define LISTEN_NONE 0 |
| - | #define LISTEN_KERNEL 1 |
| - | #define LISTEN_RAW 2 |
| - | static int listen_mode; |
| - | |
| - | #define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" |
| - | |
| - | #define DHCP_END 0xFF |
| - | #define TYPE_MASK 0x0F |
| - | #define BROADCAST_FLAG 0x8000 |
| - | |
| - | #define SERVER_PORT 67 |
| - | |
| - | #define DHCP_MAGIC 0x63825363 |
| - | |
| - | #define BOOTREQUEST 1 |
| - | #define BOOTREPLY 2 |
| - | |
| - | #define ETH_10MB 1 |
| - | #define ETH_10MB_LEN 6 |
| - | |
| - | #define OPTION_FIELD 0 |
| - | #define FILE_FIELD 1 |
| - | #define SNAME_FIELD 2 |
| - | |
| - | #define INIT_SELECTING 0 |
| - | #define REQUESTING 1 |
| - | #define BOUND 2 |
| - | #define RENEWING 3 |
| - | #define REBINDING 4 |
| - | #define INIT_REBOOT 5 |
| - | #define RENEW_REQUESTED 6 |
| - | #define RELEASED 7 |
| - | |
| - | #define CLIENT_PORT 68 |
| - | |
| - | #define DHCPDISCOVER 1 |
| - | #define DHCPOFFER 2 |
| - | #define DHCPREQUEST 3 |
| - | #define DHCPDECLINE 4 |
| - | #define DHCPACK 5 |
| - | #define DHCPNAK 6 |
| - | #define DHCPRELEASE 7 |
| - | #define DHCPINFORM 8 |
| - | |
| - | /* DHCP option codes (partial list) */ |
| - | #define DHCP_PADDING 0x00 |
| - | #define DHCP_SUBNET 0x01 |
| - | #define DHCP_TIME_OFFSET 0x02 |
| - | #define DHCP_ROUTER 0x03 |
| - | #define DHCP_TIME_SERVER 0x04 |
| - | #define DHCP_NAME_SERVER 0x05 |
| - | #define DHCP_DNS_SERVER 0x06 |
| - | #define DHCP_LOG_SERVER 0x07 |
| - | #define DHCP_COOKIE_SERVER 0x08 |
| - | #define DHCP_LPR_SERVER 0x09 |
| - | #define DHCP_HOST_NAME 0x0c |
| - | #define DHCP_BOOT_SIZE 0x0d |
| - | #define DHCP_DOMAIN_NAME 0x0f |
| - | #define DHCP_SWAP_SERVER 0x10 |
| - | #define DHCP_ROOT_PATH 0x11 |
| - | #define DHCP_IP_TTL 0x17 |
| - | #define DHCP_MTU 0x1a |
| - | #define DHCP_BROADCAST 0x1c |
| - | #define DHCP_NTP_SERVER 0x2a |
| - | #define DHCP_WINS_SERVER 0x2c |
| - | #define DHCP_REQUESTED_IP 0x32 |
| - | #define DHCP_LEASE_TIME 0x33 |
| - | #define DHCP_OPTION_OVER 0x34 |
| - | #define DHCP_MESSAGE_TYPE 0x35 |
| - | #define DHCP_SERVER_ID 0x36 |
| - | #define DHCP_PARAM_REQ 0x37 |
| - | #define DHCP_MESSAGE 0x38 |
| - | #define DHCP_MAX_SIZE 0x39 |
| - | #define DHCP_T1 0x3a |
| - | #define DHCP_T2 0x3b |
| - | #define DHCP_VENDOR 0x3c |
| - | #define DHCP_CLIENT_ID 0x3d |
| - | |
| - | /* miscellaneous defines */ |
| - | #define MAC_BCAST_ADDR (unsigned char *) "\xff\xff\xff\xff\xff\xff" |
| - | #define OPT_CODE 0 |
| - | #define OPT_LEN 1 |
| - | #define OPT_DATA 2 |
| - | |
| - | enum { |
| - | OPTION_IP = 1, |
| - | OPTION_IP_PAIR, |
| - | OPTION_STRING, |
| - | OPTION_BOOLEAN, |
| - | OPTION_U8, |
| - | OPTION_U16, |
| - | OPTION_S16, |
| - | OPTION_U32, |
| - | OPTION_S32 |
| - | }; |
| - | |
| - | #define OPTION_REQ 0x10 /* have the client request this option */ |
| - | #define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */ |
| - | |
| - | #ifdef SYSLOG |
| - | # define LOG(level, str, args...) do { printf(str, ## args); \ |
| - | printf("\n"); \ |
| - | syslog(level, str, ## args); } while(0) |
| - | # define OPEN_LOG(name) openlog(name, 0, 0) |
| - | # define CLOSE_LOG() closelog() |
| - | #else |
| - | # define LOG_EMERG "EMERGENCY!" |
| - | # define LOG_ALERT "ALERT!" |
| - | # define LOG_CRIT "critical!" |
| - | # define LOG_WARNING "warning" |
| - | # define LOG_ERR "error" |
| - | # define LOG_INFO "info" |
| - | # define LOG_DEBUG "debug" |
| - | # define LOG(level, str, args...) do { printf("%s, " str "\n", level, ## args); } while(0) |
| - | # define OPEN_LOG(name) |
| - | # define CLOSE_LOG() |
| - | #endif |
| - | |
| - | #ifdef DEBUG |
| - | # undef DEBUG |
| - | # define DEBUG(level, str, args...) LOG(level, str, ## args) |
| - | # define DEBUGGING |
| - | #else |
| - | # define DEBUG(level, str, args...) |
| - | #endif |
| - | |
| - | struct dhcpMessage { |
| - | u_int8_t op; |
| - | u_int8_t htype; |
| - | u_int8_t hlen; |
| - | u_int8_t hops; |
| - | u_int32_t xid; |
| - | u_int16_t secs; |
| - | u_int16_t flags; |
| - | u_int32_t ciaddr; |
| - | u_int32_t yiaddr; |
| - | u_int32_t siaddr; |
| - | u_int32_t giaddr; |
| - | u_int8_t chaddr[16]; |
| - | u_int8_t sname[64]; |
| - | u_int8_t file[128]; |
| - | u_int32_t cookie; |
| - | u_int8_t options[308]; /* 312 - cookie */ |
| - | }; |
| - | |
| - | struct client_config_t { |
| - | char foreground; /* Do not fork */ |
| - | char quit_after_lease; /* Quit after obtaining lease */ |
| - | char abort_if_no_lease; /* Abort if no lease */ |
| - | char *interface; /* The name of the interface to use */ |
| - | char *pidfile; /* Optionally store the process ID */ |
| - | char *script; /* User script to run at dhcp events */ |
| - | unsigned char *clientid; /* Optional client id to use */ |
| - | unsigned char *hostname; /* Optional hostname to use */ |
| - | int ifindex; /* Index number of the interface to use */ |
| - | unsigned char arp[6]; /* Our arp address */ |
| - | }; |
| - | |
| - | struct client_config_t client_config = { |
| - | /* Default options. */ |
| - | abort_if_no_lease:0, |
| - | foreground:0, |
| - | quit_after_lease:0, |
| - | interface:"eth0", |
| - | pidfile:NULL, |
| - | script:DEFAULT_SCRIPT, |
| - | clientid:NULL, |
| - | hostname:NULL, |
| - | ifindex:0, |
| - | arp:"\0\0\0\0\0\0", /* appease gcc-3.0 */ |
| - | }; |
| - | |
| - | struct dhcp_option { |
| - | char name[10]; |
| - | char flags; |
| - | unsigned char code; |
| - | }; |
| - | |
| - | struct udp_dhcp_packet { |
| - | struct iphdr ip; |
| - | struct udphdr udp; |
| - | struct dhcpMessage data; |
| - | }; |
| - | |
| - | static const struct dhcp_option options[] = { |
| - | /* name[10] flags code */ |
| - | {"subnet", OPTION_IP | OPTION_REQ, 0x01}, |
| - | {"timezone", OPTION_S32, 0x02}, |
| - | {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03}, |
| - | {"timesvr", OPTION_IP | OPTION_LIST, 0x04}, |
| - | {"namesvr", OPTION_IP | OPTION_LIST, 0x05}, |
| - | {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06}, |
| - | {"logsvr", OPTION_IP | OPTION_LIST, 0x07}, |
| - | {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08}, |
| - | {"lprsvr", OPTION_IP | OPTION_LIST, 0x09}, |
| - | {"hostname", OPTION_STRING | OPTION_REQ, 0x0c}, |
| - | {"bootsize", OPTION_U16, 0x0d}, |
| - | {"domain", OPTION_STRING | OPTION_REQ, 0x0f}, |
| - | {"swapsvr", OPTION_IP, 0x10}, |
| - | {"rootpath", OPTION_STRING, 0x11}, |
| - | {"ipttl", OPTION_U8, 0x17}, |
| - | {"mtu", OPTION_U16, 0x1a}, |
| - | {"broadcast", OPTION_IP | OPTION_REQ, 0x1c}, |
| - | {"ntpsrv", OPTION_IP | OPTION_LIST, 0x2a}, |
| - | {"wins", OPTION_IP | OPTION_LIST, 0x2c}, |
| - | {"requestip", OPTION_IP, 0x32}, |
| - | {"lease", OPTION_U32, 0x33}, |
| - | {"dhcptype", OPTION_U8, 0x35}, |
| - | {"serverid", OPTION_IP, 0x36}, |
| - | {"message", OPTION_STRING, 0x38}, |
| - | {"tftp", OPTION_STRING, 0x42}, |
| - | {"bootfile", OPTION_STRING, 0x43}, |
| - | {"", 0x00, 0x00} |
| - | }; |
| - | |
| - | /* Lengths of the different option types */ |
| - | static const unsigned char option_lengths[] = { |
| - | [OPTION_IP] = 4, |
| - | [OPTION_IP_PAIR] = 8, |
| - | [OPTION_BOOLEAN] = 1, |
| - | [OPTION_STRING] = 1, |
| - | [OPTION_U8] = 1, |
| - | [OPTION_U16] = 2, |
| - | [OPTION_S16] = 2, |
| - | [OPTION_U32] = 4, |
| - | [OPTION_S32] = 4 |
| - | }; |
| - | |
| - | /* get a rough idea of how long an option will be (rounding up...) */ |
| - | static const unsigned char max_option_length[] = { |
| - | [OPTION_IP] = sizeof("255.255.255.255 "), |
| - | [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, |
| - | [OPTION_STRING] = 1, |
| - | [OPTION_BOOLEAN] = sizeof("yes "), |
| - | [OPTION_U8] = sizeof("255 "), |
| - | [OPTION_U16] = sizeof("65535 "), |
| - | [OPTION_S16] = sizeof("-32768 "), |
| - | [OPTION_U32] = sizeof("4294967295 "), |
| - | [OPTION_S32] = sizeof("-2147483684 "), |
| - | }; |
| - | |
| - | /* return the position of the 'end' option (no bounds checking) */ |
| - | static int end_option(unsigned char *optionptr) |
| - | { |
| - | int i = 0; |
| - | |
| - | while (optionptr[i] != DHCP_END) { |
| - | if (optionptr[i] == DHCP_PADDING) |
| - | i++; |
| - | else |
| - | i += optionptr[i + OPT_LEN] + 2; |
| - | } |
| - | return i; |
| - | } |
| - | |
| - | /* add an option string to the options (an option string contains an option code, |
| - | * length, then data) */ |
| - | static int add_option_string(unsigned char *optionptr, unsigned char *string) |
| - | { |
| - | int end = end_option(optionptr); |
| - | |
| - | /* end position + string length + option code/length + end option */ |
| - | if (end + string[OPT_LEN] + 2 + 1 >= 308) { |
| - | LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", |
| - | string[OPT_CODE]); |
| - | return 0; |
| - | } |
| - | DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]); |
| - | memcpy(optionptr + end, string, string[OPT_LEN] + 2); |
| - | optionptr[end + string[OPT_LEN] + 2] = DHCP_END; |
| - | return string[OPT_LEN] + 2; |
| - | } |
| - | |
| - | /* add a one to four byte option to a packet */ |
| - | static int add_simple_option(unsigned char *optionptr, unsigned char code, |
| - | u_int32_t data) |
| - | { |
| - | char length = 0; |
| - | int i; |
| - | unsigned char option[2 + 4]; |
| - | unsigned char *u8; |
| - | u_int16_t *u16; |
| - | u_int32_t *u32; |
| - | u_int32_t aligned; |
| - | |
| - | u8 = (unsigned char *) &aligned; |
| - | u16 = (u_int16_t *) & aligned; |
| - | u32 = &aligned; |
| - | |
| - | for (i = 0; options[i].code; i++) |
| - | if (options[i].code == code) { |
| - | length = option_lengths[options[i].flags & TYPE_MASK]; |
| - | } |
| - | |
| - | if (!length) { |
| - | DEBUG(LOG_ERR, "Could not add option 0x%02x", code); |
| - | return 0; |
| - | } |
| - | |
| - | option[OPT_CODE] = code; |
| - | option[OPT_LEN] = length; |
| - | |
| - | switch (length) { |
| - | case 1: |
| - | *u8 = data; |
| - | break; |
| - | case 2: |
| - | *u16 = data; |
| - | break; |
| - | case 4: |
| - | *u32 = data; |
| - | break; |
| - | } |
| - | |
| - | memcpy(option + 2, &aligned, length); |
| - | return add_option_string(optionptr, option); |
| - | } |
| - | |
| - | static u_int16_t checksum(void *addr, int count) |
| - | { |
| - | /* Compute Internet Checksum for "count" bytes |
| - | * beginning at location "addr". |
| - | */ |
| - | register int32_t sum = 0; |
| - | u_int16_t *source = (u_int16_t *) addr; |
| - | |
| - | while (count > 1) { |
| - | /* This is the inner loop */ |
| - | sum += *source++; |
| - | count -= 2; |
| - | } |
| - | |
| - | /* Add left-over byte, if any */ |
| - | if (count > 0) { |
| - | sum += *(unsigned char *) source; |
| - | } |
| - | |
| - | /* Fold 32-bit sum to 16 bits */ |
| - | while (sum >> 16) { |
| - | sum = (sum & 0xffff) + (sum >> 16); |
| - | } |
| - | |
| - | return ~sum; |
| - | } |
| - | |
| - | /* Constuct a ip/udp header for a packet, and specify the source and dest hardware address */ |
| - | static int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, |
| - | int source_port, u_int32_t dest_ip, int dest_port, |
| - | unsigned char *dest_arp, int ifindex) |
| - | { |
| - | int l_fd; |
| - | int result; |
| - | struct sockaddr_ll dest; |
| - | struct udp_dhcp_packet packet; |
| - | |
| - | if ((l_fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { |
| - | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); |
| - | return -1; |
| - | } |
| - | |
| - | memset(&dest, 0, sizeof(dest)); |
| - | memset(&packet, 0, sizeof(packet)); |
| - | |
| - | dest.sll_family = AF_PACKET; |
| - | dest.sll_protocol = htons(ETH_P_IP); |
| - | dest.sll_ifindex = ifindex; |
| - | dest.sll_halen = 6; |
| - | memcpy(dest.sll_addr, dest_arp, 6); |
| - | if (bind(l_fd, (struct sockaddr *) &dest, sizeof(struct sockaddr_ll)) < 0) { |
| - | DEBUG(LOG_ERR, "bind call failed: %s", strerror(errno)); |
| - | close(l_fd); |
| - | return -1; |
| - | } |
| - | |
| - | packet.ip.protocol = IPPROTO_UDP; |
| - | packet.ip.saddr = source_ip; |
| - | packet.ip.daddr = dest_ip; |
| - | packet.udp.source = htons(source_port); |
| - | packet.udp.dest = htons(dest_port); |
| - | packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */ |
| - | packet.ip.tot_len = packet.udp.len; |
| - | memcpy(&(packet.data), payload, sizeof(struct dhcpMessage)); |
| - | packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet)); |
| - | |
| - | packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet)); |
| - | packet.ip.ihl = sizeof(packet.ip) >> 2; |
| - | packet.ip.version = IPVERSION; |
| - | packet.ip.ttl = IPDEFTTL; |
| - | packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip)); |
| - | |
| - | result = |
| - | sendto(l_fd, &packet, sizeof(struct udp_dhcp_packet), 0, |
| - | (struct sockaddr *) &dest, sizeof(dest)); |
| - | if (result <= 0) { |
| - | DEBUG(LOG_ERR, "write on socket failed: %s", strerror(errno)); |
| - | } |
| - | close(l_fd); |
| - | return result; |
| - | } |
| - | |
| - | /* Let the kernel do all the work for packet generation */ |
| - | static int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, |
| - | int source_port, u_int32_t dest_ip, int dest_port) |
| - | { |
| - | int n = 1; |
| - | int l_fd, result; |
| - | struct sockaddr_in client; |
| - | |
| - | if ((l_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { |
| - | return -1; |
| - | } |
| - | |
| - | if (setsockopt(l_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == |
| - | -1) { |
| - | return -1; |
| - | } |
| - | |
| - | memset(&client, 0, sizeof(client)); |
| - | client.sin_family = AF_INET; |
| - | client.sin_port = htons(source_port); |
| - | client.sin_addr.s_addr = source_ip; |
| - | |
| - | if (bind(l_fd, (struct sockaddr *) &client, sizeof(struct sockaddr)) == |
| - | -1) { |
| - | return -1; |
| - | } |
| - | |
| - | memset(&client, 0, sizeof(client)); |
| - | client.sin_family = AF_INET; |
| - | client.sin_port = htons(dest_port); |
| - | client.sin_addr.s_addr = dest_ip; |
| - | |
| - | if (connect(l_fd, (struct sockaddr *) &client, sizeof(struct sockaddr)) == |
| - | -1) { |
| - | return -1; |
| - | } |
| - | |
| - | result = write(l_fd, payload, sizeof(struct dhcpMessage)); |
| - | close(l_fd); |
| - | return result; |
| - | } |
| - | |
| - | /* initialize a packet with the proper defaults */ |
| - | static void init_packet(struct dhcpMessage *packet, char type) |
| - | { |
| - | struct vendor { |
| - | char vendor, length; |
| - | char str[sizeof("udhcp " VERSION)]; |
| - | } |
| - | vendor_id = { |
| - | DHCP_VENDOR, sizeof("udhcp " VERSION) - 1, "udhcp " VERSION}; |
| - | |
| - | memset(packet, 0, sizeof(struct dhcpMessage)); |
| - | switch (type) { |
| - | case DHCPDISCOVER: |
| - | case DHCPREQUEST: |
| - | case DHCPRELEASE: |
| - | case DHCPINFORM: |
| - | packet->op = BOOTREQUEST; |
| - | break; |
| - | case DHCPOFFER: |
| - | case DHCPACK: |
| - | case DHCPNAK: |
| - | packet->op = BOOTREPLY; |
| - | } |
| - | packet->htype = ETH_10MB; |
| - | packet->hlen = ETH_10MB_LEN; |
| - | packet->cookie = htonl(DHCP_MAGIC); |
| - | packet->options[0] = DHCP_END; |
| - | add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type); |
| - | |
| - | memcpy(packet->chaddr, client_config.arp, 6); |
| - | add_option_string(packet->options, client_config.clientid); |
| - | if (client_config.hostname) { |
| - | add_option_string(packet->options, client_config.hostname); |
| - | } |
| - | add_option_string(packet->options, (unsigned char *) &vendor_id); |
| - | } |
| - | |
| - | |
| - | /* Add a paramater request list for stubborn DHCP servers. Pull the data |
| - | * from the struct in options.c. Don't do bounds checking here because it |
| - | * goes towards the head of the packet. */ |
| - | static void add_requests(struct dhcpMessage *packet) |
| - | { |
| - | int end = end_option(packet->options); |
| - | int i, len = 0; |
| - | |
| - | packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; |
| - | for (i = 0; options[i].code; i++) { |
| - | if (options[i].flags & OPTION_REQ) { |
| - | packet->options[end + OPT_DATA + len++] = options[i].code; |
| - | } |
| - | } |
| - | packet->options[end + OPT_LEN] = len; |
| - | packet->options[end + OPT_DATA + len] = DHCP_END; |
| - | |
| - | } |
| - | |
| - | /* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ |
| - | static inline int send_discover(unsigned long xid, unsigned long requested) |
| - | { |
| - | struct dhcpMessage packet; |
| - | |
| - | init_packet(&packet, DHCPDISCOVER); |
| - | packet.xid = xid; |
| - | if (requested) { |
| - | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); |
| - | } |
| - | add_requests(&packet); |
| - | DEBUG(LOG_DEBUG, "Sending discover..."); |
| - | return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, |
| - | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); |
| - | } |
| - | |
| - | /* Broadcasts a DHCP request message */ |
| - | static inline int send_selecting(unsigned long xid, unsigned long server, |
| - | unsigned long requested) |
| - | { |
| - | struct dhcpMessage packet; |
| - | struct in_addr addr; |
| - | |
| - | init_packet(&packet, DHCPREQUEST); |
| - | packet.xid = xid; |
| - | |
| - | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); |
| - | add_simple_option(packet.options, DHCP_SERVER_ID, server); |
| - | |
| - | add_requests(&packet); |
| - | addr.s_addr = requested; |
| - | DEBUG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr)); |
| - | return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, |
| - | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); |
| - | } |
| - | |
| - | |
| - | /* Unicasts or broadcasts a DHCP renew message */ |
| - | static int send_renew(unsigned long xid, unsigned long server, |
| - | unsigned long ciaddr) |
| - | { |
| - | struct dhcpMessage packet; |
| - | |
| - | init_packet(&packet, DHCPREQUEST); |
| - | packet.xid = xid; |
| - | packet.ciaddr = ciaddr; |
| - | |
| - | add_requests(&packet); |
| - | DEBUG(LOG_DEBUG, "Sending renew..."); |
| - | if (server) { |
| - | return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, |
| - | SERVER_PORT); |
| - | } |
| - | return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, |
| - | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); |
| - | } |
| - | |
| - | /* Create a random xid */ |
| - | static unsigned long random_xid(void) |
| - | { |
| - | static int initialized; |
| - | |
| - | if (!initialized) { |
| - | srand(time(0)); |
| - | initialized++; |
| - | } |
| - | return rand(); |
| - | } |
| - | |
| - | /* just a little helper */ |
| - | static void change_mode(int new_mode) |
| - | { |
| - | DEBUG(LOG_INFO, "entering %s listen mode", |
| - | new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); |
| - | close(fd_main); |
| - | fd_main = -1; |
| - | listen_mode = new_mode; |
| - | } |
| - | |
| - | |
| - | /* SIGUSR1 handler (renew) */ |
| - | static void renew_requested(int sig) |
| - | { |
| - | sig = 0; |
| - | LOG(LOG_INFO, "Received SIGUSR1"); |
| - | if (state == BOUND || state == RENEWING || state == REBINDING || |
| - | state == RELEASED) { |
| - | change_mode(LISTEN_KERNEL); |
| - | packet_num = 0; |
| - | state = RENEW_REQUESTED; |
| - | } |
| - | |
| - | if (state == RELEASED) { |
| - | change_mode(LISTEN_RAW); |
| - | state = INIT_SELECTING; |
| - | } |
| - | |
| - | /* Kill any timeouts because the user wants this to hurry along */ |
| - | timeout = 0; |
| - | } |
| - | |
| - | /* get an option with bounds checking (warning, not aligned). */ |
| - | static unsigned char *get_option(struct dhcpMessage *packet, int code) |
| - | { |
| - | int i, length; |
| - | unsigned char *optionptr; |
| - | int over = 0, done = 0, curr = OPTION_FIELD; |
| - | |
| - | optionptr = packet->options; |
| - | i = 0; |
| - | length = 308; |
| - | while (!done) { |
| - | if (i >= length) { |
| - | LOG(LOG_WARNING, "bogus packet, option fields too long."); |
| - | return NULL; |
| - | } |
| - | if (optionptr[i + OPT_CODE] == code) { |
| - | if (i + 1 + optionptr[i + OPT_LEN] >= length) { |
| - | LOG(LOG_WARNING, "bogus packet, option fields too long."); |
| - | return NULL; |
| - | } |
| - | return optionptr + i + 2; |
| - | } |
| - | switch (optionptr[i + OPT_CODE]) { |
| - | case DHCP_PADDING: |
| - | i++; |
| - | break; |
| - | case DHCP_OPTION_OVER: |
| - | if (i + 1 + optionptr[i + OPT_LEN] >= length) { |
| - | LOG(LOG_WARNING, "bogus packet, option fields too long."); |
| - | return NULL; |
| - | } |
| - | over = optionptr[i + 3]; |
| - | i += optionptr[OPT_LEN] + 2; |
| - | break; |
| - | case DHCP_END: |
| - | if (curr == OPTION_FIELD && over & FILE_FIELD) { |
| - | optionptr = packet->file; |
| - | i = 0; |
| - | length = 128; |
| - | curr = FILE_FIELD; |
| - | } else if (curr == FILE_FIELD && over & SNAME_FIELD) { |
| - | optionptr = packet->sname; |
| - | i = 0; |
| - | length = 64; |
| - | curr = SNAME_FIELD; |
| - | } else { |
| - | done = 1; |
| - | } |
| - | break; |
| - | default: |
| - | i += optionptr[OPT_LEN + i] + 2; |
| - | } |
| - | } |
| - | return NULL; |
| - | } |
| - | |
| - | static int sprintip(char *dest, char *pre, unsigned char *ip) |
| - | { |
| - | return sprintf(dest, "%s%d.%d.%d.%d ", pre, ip[0], ip[1], ip[2], ip[3]); |
| - | } |
| - | |
| - | |
| - | /* Fill dest with the text of option 'option'. */ |
| - | static inline void fill_options(char *dest, unsigned char *option, |
| - | const struct dhcp_option *type_p) |
| - | { |
| - | int type, optlen; |
| - | u_int16_t val_u16; |
| - | int16_t val_s16; |
| - | u_int32_t val_u32; |
| - | int32_t val_s32; |
| - | int len = option[OPT_LEN - 2]; |
| - | |
| - | dest += sprintf(dest, "%s=", type_p->name); |
| - | |
| - | type = type_p->flags & TYPE_MASK; |
| - | optlen = option_lengths[type]; |
| - | for (;;) { |
| - | switch (type) { |
| - | case OPTION_IP_PAIR: |
| - | dest += sprintip(dest, "", option); |
| - | *(dest++) = '/'; |
| - | option += 4; |
| - | optlen = 4; |
| - | case OPTION_IP: /* Works regardless of host byte order. */ |
| - | dest += sprintip(dest, "", option); |
| - | break; |
| - | case OPTION_BOOLEAN: |
| - | dest += sprintf(dest, *option ? "yes " : "no "); |
| - | break; |
| - | case OPTION_U8: |
| - | dest += sprintf(dest, "%u ", *option); |
| - | break; |
| - | case OPTION_U16: |
| - | memcpy(&val_u16, option, 2); |
| - | dest += sprintf(dest, "%u ", ntohs(val_u16)); |
| - | break; |
| - | case OPTION_S16: |
| - | memcpy(&val_s16, option, 2); |
| - | dest += sprintf(dest, "%d ", ntohs(val_s16)); |
| - | break; |
| - | case OPTION_U32: |
| - | memcpy(&val_u32, option, 4); |
| - | dest += sprintf(dest, "%lu ", (unsigned long) ntohl(val_u32)); |
| - | break; |
| - | case OPTION_S32: |
| - | memcpy(&val_s32, option, 4); |
| - | dest += sprintf(dest, "%ld ", (long) ntohl(val_s32)); |
| - | break; |
| - | case OPTION_STRING: |
| - | memcpy(dest, option, len); |
| - | dest[len] = '\0'; |
| - | return; /* Short circuit this case */ |
| - | } |
| - | option += optlen; |
| - | len -= optlen; |
| - | if (len <= 0) { |
| - | break; |
| - | } |
| - | } |
| - | } |
| - | |
| - | static char *find_env(const char *prefix, char *defaultstr) |
| - | { |
| - | extern char **environ; |
| - | char **ptr; |
| - | const int len = strlen(prefix); |
| - | |
| - | for (ptr = environ; *ptr != NULL; ptr++) { |
| - | if (strncmp(prefix, *ptr, len) == 0) { |
| - | return *ptr; |
| - | } |
| - | } |
| - | return defaultstr; |
| - | } |
| - | |
| - | /* put all the paramaters into an environment */ |
| - | static char **fill_envp(struct dhcpMessage *packet) |
| - | { |
| - | /* supported options are easily added here */ |
| - | int num_options = 0; |
| - | int i, j; |
| - | char **envp; |
| - | unsigned char *temp; |
| - | char over = 0; |
| - | |
| - | if (packet == NULL) { |
| - | num_options = 0; |
| - | } else { |
| - | for (i = 0; options[i].code; i++) { |
| - | if (get_option(packet, options[i].code)) { |
| - | num_options++; |
| - | } |
| - | } |
| - | if (packet->siaddr) { |
| - | num_options++; |
| - | } |
| - | if ((temp = get_option(packet, DHCP_OPTION_OVER))) { |
| - | over = *temp; |
| - | } |
| - | if (!(over & FILE_FIELD) && packet->file[0]) { |
| - | num_options++; |
| - | } |
| - | if (!(over & SNAME_FIELD) && packet->sname[0]) { |
| - | num_options++; |
| - | } |
| - | } |
| - | |
| - | envp = xmalloc((num_options + 5) * sizeof(char *)); |
| - | envp[0] = xmalloc(sizeof("interface=") + strlen(client_config.interface)); |
| - | sprintf(envp[0], "interface=%s", client_config.interface); |
| - | envp[1] = find_env("PATH", "PATH=/bin:/usr/bin:/sbin:/usr/sbin"); |
| - | envp[2] = find_env("HOME", "HOME=/"); |
| - | |
| - | if (packet == NULL) { |
| - | envp[3] = NULL; |
| - | return envp; |
| - | } |
| - | |
| - | envp[3] = xmalloc(sizeof("ip=255.255.255.255")); |
| - | sprintip(envp[3], "ip=", (unsigned char *) &packet->yiaddr); |
| - | for (i = 0, j = 4; options[i].code; i++) { |
| - | if ((temp = get_option(packet, options[i].code))) { |
| - | envp[j] = |
| - | xmalloc(max_option_length[(&options[i])->flags & TYPE_MASK] * |
| - | (temp[OPT_LEN - 2] / |
| - | option_lengths[(&options[i])->flags & TYPE_MASK]) + |
| - | strlen((&options[i])->name) + 2); |
| - | fill_options(envp[j], temp, &options[i]); |
| - | j++; |
| - | } |
| - | } |
| - | if (packet->siaddr) { |
| - | envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); |
| - | sprintip(envp[j++], "siaddr=", (unsigned char *) &packet->yiaddr); |
| - | } |
| - | if (!(over & FILE_FIELD) && packet->file[0]) { |
| - | /* watch out for invalid packets */ |
| - | packet->file[sizeof(packet->file) - 1] = '\0'; |
| - | envp[j] = xmalloc(sizeof("boot_file=") + strlen(packet->file)); |
| - | sprintf(envp[j++], "boot_file=%s", packet->file); |
| - | } |
| - | if (!(over & SNAME_FIELD) && packet->sname[0]) { |
| - | /* watch out for invalid packets */ |
| - | packet->sname[sizeof(packet->sname) - 1] = '\0'; |
| - | envp[j] = xmalloc(sizeof("sname=") + strlen(packet->sname)); |
| - | sprintf(envp[j++], "sname=%s", packet->sname); |
| - | } |
| - | envp[j] = NULL; |
| - | return envp; |
| - | } |
| - | |
| - | /* Call a script with a par file and env vars */ |
| - | static void run_script(struct dhcpMessage *packet, const char *name) |
| - | { |
| - | int pid; |
| - | char **envp; |
| - | |
| - | if (client_config.script == NULL) { |
| - | return; |
| - | } |
| - | |
| - | /* call script */ |
| - | pid = fork(); |
| - | if (pid) { |
| - | waitpid(pid, NULL, 0); |
| - | return; |
| - | } else if (pid == 0) { |
| - | envp = fill_envp(packet); |
| - | |
| - | /* close fd's? */ |
| - | |
| - | /* exec script */ |
| - | DEBUG(LOG_INFO, "execle'ing %s", client_config.script); |
| - | execle(client_config.script, client_config.script, name, NULL, envp); |
| - | LOG(LOG_ERR, "script %s failed: %s", |
| - | client_config.script, strerror(errno)); |
| - | exit(1); |
| - | } |
| - | } |
| - | |
| - | /* SIGUSR2 handler (release) */ |
| - | static void release_requested(int sig) |
| - | { |
| - | sig = 0; |
| - | LOG(LOG_INFO, "Received SIGUSR2"); |
| - | /* send release packet */ |
| - | if (state == BOUND || state == RENEWING || state == REBINDING) { |
| - | struct dhcpMessage packet; |
| - | |
| - | init_packet(&packet, DHCPRELEASE); |
| - | packet.xid = random_xid(); |
| - | packet.ciaddr = requested_ip; |
| - | |
| - | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested_ip); |
| - | add_simple_option(packet.options, DHCP_SERVER_ID, server_addr); |
| - | |
| - | DEBUG(LOG_DEBUG, "Sending release..."); |
| - | kernel_packet(&packet, requested_ip, CLIENT_PORT, server_addr, |
| - | SERVER_PORT); |
| - | run_script(NULL, "deconfig"); |
| - | } |
| - | |
| - | change_mode(LISTEN_NONE); |
| - | state = RELEASED; |
| - | timeout = 0x7fffffff; |
| - | } |
| - | |
| - | |
| - | static int pidfile_acquire(char *pidfile) |
| - | { |
| - | int pid_fd; |
| - | |
| - | if (pidfile == NULL) { |
| - | return -1; |
| - | } |
| - | pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644); |
| - | if (pid_fd < 0) { |
| - | LOG(LOG_ERR, "Unable to open pidfile %s: %s\n", |
| - | pidfile, strerror(errno)); |
| - | } else { |
| - | lockf(pid_fd, F_LOCK, 0); |
| - | } |
| - | |
| - | return pid_fd; |
| - | } |
| - | |
| - | |
| - | static void pidfile_write_release(int pid_fd) |
| - | { |
| - | FILE *out; |
| - | |
| - | if (pid_fd < 0) { |
| - | return; |
| - | } |
| - | if ((out = fdopen(pid_fd, "w")) != NULL) { |
| - | fprintf(out, "%d\n", getpid()); |
| - | fclose(out); |
| - | } |
| - | lockf(pid_fd, F_UNLCK, 0); |
| - | close(pid_fd); |
| - | } |
| - | |
| - | /* Exit and cleanup */ |
| - | static void exit_client(int retval) |
| - | { |
| - | unlink(client_config.pidfile); |
| - | if (client_config.pidfile) { |
| - | unlink(client_config.pidfile); |
| - | } |
| - | CLOSE_LOG(); |
| - | exit(retval); |
| - | } |
| - | |
| - | |
| - | /* SIGTERM handler */ |
| - | static void terminate(int sig) |
| - | { |
| - | sig = 0; |
| - | LOG(LOG_INFO, "Received SIGTERM"); |
| - | exit_client(0); |
| - | } |
| - | |
| - | |
| - | static inline int read_interface(char *interface, int *ifindex, |
| - | u_int32_t * addr, unsigned char *arp) |
| - | { |
| - | int l_fd; |
| - | struct ifreq ifr; |
| - | struct sockaddr_in *s_in; |
| - | |
| - | memset(&ifr, 0, sizeof(struct ifreq)); |
| - | if ((l_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) { |
| - | ifr.ifr_addr.sa_family = AF_INET; |
| - | strcpy(ifr.ifr_name, interface); |
| - | |
| - | if (addr) { |
| - | if (ioctl(l_fd, SIOCGIFADDR, &ifr) == 0) { |
| - | s_in = (struct sockaddr_in *) &ifr.ifr_addr; |
| - | *addr = s_in->sin_addr.s_addr; |
| - | DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, |
| - | inet_ntoa(s_in->sin_addr)); |
| - | } else { |
| - | LOG(LOG_ERR, "SIOCGIFADDR failed!: %s", strerror(errno)); |
| - | return -1; |
| - | } |
| - | } |
| - | |
| - | if (ioctl(l_fd, SIOCGIFINDEX, &ifr) == 0) { |
| - | DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex); |
| - | *ifindex = ifr.ifr_ifindex; |
| - | } else { |
| - | LOG(LOG_ERR, "SIOCGIFINDEX failed!: %s", strerror(errno)); |
| - | return -1; |
| - | } |
| - | if (ioctl(l_fd, SIOCGIFHWADDR, &ifr) == 0) { |
| - | memcpy(arp, ifr.ifr_hwaddr.sa_data, 6); |
| - | DEBUG(LOG_INFO, |
| - | "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", |
| - | arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]); |
| - | } else { |
| - | LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %s", strerror(errno)); |
| - | return -1; |
| - | } |
| - | } else { |
| - | LOG(LOG_ERR, "socket failed!: %s", strerror(errno)); |
| - | return -1; |
| - | } |
| - | close(l_fd); |
| - | return 0; |
| - | } |
| - | |
| - | |
| - | static inline int listen_socket(unsigned int ip, int port, char *inf) |
| - | { |
| - | struct ifreq interface; |
| - | int l_fd; |
| - | struct sockaddr_in addr; |
| - | int n = 1; |
| - | |
| - | DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s\n", ip, port, inf); |
| - | if ((l_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { |
| - | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); |
| - | return -1; |
| - | } |
| - | |
| - | memset(&addr, 0, sizeof(addr)); |
| - | addr.sin_family = AF_INET; |
| - | addr.sin_port = htons(port); |
| - | addr.sin_addr.s_addr = ip; |
| - | |
| - | if (setsockopt(l_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == |
| - | -1) { |
| - | close(l_fd); |
| - | return -1; |
| - | } |
| - | if (setsockopt(l_fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == |
| - | -1) { |
| - | close(l_fd); |
| - | return -1; |
| - | } |
| - | |
| - | strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ); |
| - | if (setsockopt |
| - | (l_fd, SOL_SOCKET, SO_BINDTODEVICE, (char *) &interface, |
| - | sizeof(interface)) < 0) { |
| - | close(l_fd); |
| - | return -1; |
| - | } |
| - | |
| - | if (bind(l_fd, (struct sockaddr *) &addr, sizeof(struct sockaddr)) == -1) { |
| - | close(l_fd); |
| - | return -1; |
| - | } |
| - | |
| - | return l_fd; |
| - | } |
| - | |
| - | |
| - | static int raw_socket(int ifindex) |
| - | { |
| - | int l_fd; |
| - | struct sockaddr_ll sock; |
| - | |
| - | DEBUG(LOG_INFO, "Opening raw socket on ifindex %d\n", ifindex); |
| - | if ((l_fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { |
| - | DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); |
| - | return -1; |
| - | } |
| - | |
| - | sock.sll_family = AF_PACKET; |
| - | sock.sll_protocol = htons(ETH_P_IP); |
| - | sock.sll_ifindex = ifindex; |
| - | if (bind(l_fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) { |
| - | DEBUG(LOG_ERR, "bind call failed: %s", strerror(errno)); |
| - | close(l_fd); |
| - | return -1; |
| - | } |
| - | |
| - | return l_fd; |
| - | |
| - | } |
| - | |
| - | /* read a packet from socket fd, return -1 on read error, -2 on packet error */ |
| - | static int get_packet(struct dhcpMessage *packet, int l_fd) |
| - | { |
| - | int bytes; |
| - | int i; |
| - | const char broken_vendors[][8] = { |
| - | "MSFT 98", |
| - | "" |
| - | }; |
| - | char unsigned *vendor; |
| - | |
| - | memset(packet, 0, sizeof(struct dhcpMessage)); |
| - | bytes = read(l_fd, packet, sizeof(struct dhcpMessage)); |
| - | if (bytes < 0) { |
| - | DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring"); |
| - | return -1; |
| - | } |
| - | |
| - | if (ntohl(packet->cookie) != DHCP_MAGIC) { |
| - | LOG(LOG_ERR, "received bogus message, ignoring"); |
| - | return -2; |
| - | } |
| - | DEBUG(LOG_INFO, "Received a packet"); |
| - | |
| - | if (packet->op == BOOTREQUEST |
| - | && (vendor = get_option(packet, DHCP_VENDOR))) { |
| - | for (i = 0; broken_vendors[i][0]; i++) { |
| - | if (vendor[OPT_LEN - 2] == |
| - | (unsigned char) strlen(broken_vendors[i]) |
| - | && !strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) { |
| - | DEBUG(LOG_INFO, "broken client (%s), forcing broadcast", |
| - | broken_vendors[i]); |
| - | packet->flags |= htons(BROADCAST_FLAG); |
| - | } |
| - | } |
| - | } |
| - | |
| - | return bytes; |
| - | } |
| - | |
| - | static inline int get_raw_packet(struct dhcpMessage *payload, int l_fd) |
| - | { |
| - | int bytes; |
| - | struct udp_dhcp_packet packet; |
| - | u_int32_t source, dest; |
| - | u_int16_t check; |
| - | |
| - | memset(&packet, 0, sizeof(struct udp_dhcp_packet)); |
| - | bytes = read(l_fd, &packet, sizeof(struct udp_dhcp_packet)); |
| - | if (bytes < 0) { |
| - | DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring"); |
| - | usleep(500000); /* possible down interface, looping condition */ |
| - | return -1; |
| - | } |
| - | |
| - | if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { |
| - | DEBUG(LOG_INFO, "message too short, ignoring"); |
| - | return -1; |
| - | } |
| - | |
| - | if (bytes < ntohs(packet.ip.tot_len)) { |
| - | DEBUG(LOG_INFO, "Truncated packet"); |
| - | return -1; |
| - | } |
| - | |
| - | /* ignore any extra garbage bytes */ |
| - | bytes = ntohs(packet.ip.tot_len); |
| - | |
| - | /* Make sure its the right packet for us, and that it passes sanity checks */ |
| - | if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION || |
| - | packet.ip.ihl != sizeof(packet.ip) >> 2 |
| - | || packet.udp.dest != htons(CLIENT_PORT) |
| - | || bytes > (int) sizeof(struct udp_dhcp_packet) |
| - | || ntohs(packet.udp.len) != (short) (bytes - sizeof(packet.ip))) { |
| - | DEBUG(LOG_INFO, "unrelated/bogus packet"); |
| - | return -1; |
| - | } |
| - | |
| - | /* check IP checksum */ |
| - | check = packet.ip.check; |
| - | packet.ip.check = 0; |
| - | if (check != checksum(&(packet.ip), sizeof(packet.ip))) { |
| - | DEBUG(LOG_INFO, "bad IP header checksum, ignoring"); |
| - | return -1; |
| - | } |
| - | |
| - | /* verify the UDP checksum by replacing the header with a psuedo header */ |
| - | source = packet.ip.saddr; |
| - | dest = packet.ip.daddr; |
| - | check = packet.udp.check; |
| - | packet.udp.check = 0; |
| - | memset(&packet.ip, 0, sizeof(packet.ip)); |
| - | |
| - | packet.ip.protocol = IPPROTO_UDP; |
| - | packet.ip.saddr = source; |
| - | packet.ip.daddr = dest; |
| - | packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */ |
| - | if (check && check != checksum(&packet, bytes)) { |
| - | DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring"); |
| - | return -1; |
| - | } |
| - | |
| - | memcpy(payload, &(packet.data), |
| - | bytes - (sizeof(packet.ip) + sizeof(packet.udp))); |
| - | |
| - | if (ntohl(payload->cookie) != DHCP_MAGIC) { |
| - | LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring"); |
| - | return -1; |
| - | } |
| - | DEBUG(LOG_INFO, "oooooh!!! got some!"); |
| - | return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); |
| - | } |
| - | |
| - | |
| - | int udhcpc_main(int argc, char *argv[]) |
| - | { |
| - | unsigned char *temp, *message; |
| - | unsigned long t1 = 0, t2 = 0, xid = 0; |
| - | unsigned long start = 0, lease; |
| - | fd_set rfds; |
| - | int retval; |
| - | struct timeval tv; |
| - | int c, len; |
| - | struct dhcpMessage packet; |
| - | struct in_addr temp_addr; |
| - | int pid_fd; |
| - | time_t now; |
| - | |
| - | static struct option l_options[] = { |
| - | {"clientid", required_argument, 0, 'c'}, |
| - | {"foreground", no_argument, 0, 'f'}, |
| - | {"hostname", required_argument, 0, 'H'}, |
| - | {"help", no_argument, 0, 'h'}, |
| - | {"interface", required_argument, 0, 'i'}, |
| - | {"now", no_argument, 0, 'n'}, |
| - | {"pidfile", required_argument, 0, 'p'}, |
| - | {"quit", no_argument, 0, 'q'}, |
| - | {"request", required_argument, 0, 'r'}, |
| - | {"script", required_argument, 0, 's'}, |
| - | {"version", no_argument, 0, 'v'}, |
| - | {0, 0, 0, 0} |
| - | }; |
| - | |
| - | /* get options */ |
| - | while (1) { |
| - | int option_index = 0; |
| - | |
| - | c = getopt_long(argc, argv, "c:fH:i:np:qr:s:v", l_options, |
| - | &option_index); |
| - | if (c == -1) { |
| - | break; |
| - | } |
| - | |
| - | switch (c) { |
| - | case 'c': |
| - | len = strlen(optarg); |
| - | if (len > 255) { |
| - | len = 255; |
| - | } |
| - | if (client_config.clientid) { |
| - | free(client_config.clientid); |
| - | } |
| - | client_config.clientid = xmalloc(len + 2); |
| - | client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; |
| - | client_config.clientid[OPT_LEN] = len; |
| - | client_config.clientid[OPT_DATA] = '\0'; |
| - | strncpy(client_config.clientid + 3, optarg, len - 1); |
| - | break; |
| - | case 'f': |
| - | client_config.foreground = 1; |
| - | break; |
| - | case 'H': |
| - | len = strlen(optarg); |
| - | if (len > 255) { |
| - | len = 255; |
| - | } |
| - | if (client_config.hostname) { |
| - | free(client_config.hostname); |
| - | } |
| - | client_config.hostname = xmalloc(len + 2); |
| - | client_config.hostname[OPT_CODE] = DHCP_HOST_NAME; |
| - | client_config.hostname[OPT_LEN] = len; |
| - | strncpy(client_config.hostname + 2, optarg, len); |
| - | break; |
| - | case 'i': |
| - | client_config.interface = optarg; |
| - | break; |
| - | case 'n': |
| - | client_config.abort_if_no_lease = 1; |
| - | break; |
| - | case 'p': |
| - | client_config.pidfile = optarg; |
| - | break; |
| - | case 'q': |
| - | client_config.quit_after_lease = 1; |
| - | break; |
| - | case 'r': |
| - | requested_ip = inet_addr(optarg); |
| - | break; |
| - | case 's': |
| - | client_config.script = optarg; |
| - | break; |
| - | case 'v': |
| - | printf("udhcpcd, version %s\n\n", VERSION); |
| - | exit_client(0); |
| - | break; |
| - | default: |
| - | show_usage(); |
| - | } |
| - | } |
| - | |
| - | OPEN_LOG("udhcpc"); |
| - | LOG(LOG_INFO, "udhcp client (v%s) started", VERSION); |
| - | |
| - | pid_fd = pidfile_acquire(client_config.pidfile); |
| - | pidfile_write_release(pid_fd); |
| - | |
| - | if (read_interface |
| - | (client_config.interface, &client_config.ifindex, NULL, |
| - | client_config.arp) < 0) { |
| - | exit_client(1); |
| - | } |
| - | |
| - | if (!client_config.clientid) { |
| - | client_config.clientid = xmalloc(6 + 3); |
| - | client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; |
| - | client_config.clientid[OPT_LEN] = 7; |
| - | client_config.clientid[OPT_DATA] = 1; |
| - | memcpy(client_config.clientid + 3, client_config.arp, 6); |
| - | } |
| - | |
| - | /* setup signal handlers */ |
| - | signal(SIGUSR1, renew_requested); |
| - | signal(SIGUSR2, release_requested); |
| - | signal(SIGTERM, terminate); |
| - | |
| - | state = INIT_SELECTING; |
| - | run_script(NULL, "deconfig"); |
| - | change_mode(LISTEN_RAW); |
| - | |
| - | for (;;) { |
| - | tv.tv_sec = timeout - time(0); |
| - | tv.tv_usec = 0; |
| - | FD_ZERO(&rfds); |
| - | |
| - | if (listen_mode != LISTEN_NONE && fd_main < 0) { |
| - | if (listen_mode == LISTEN_KERNEL) { |
| - | fd_main = |
| - | listen_socket(INADDR_ANY, CLIENT_PORT, |
| - | client_config.interface); |
| - | } else { |
| - | fd_main = raw_socket(client_config.ifindex); |
| - | } |
| - | if (fd_main < 0) { |
| - | LOG(LOG_ERR, "FATAL: couldn't listen on socket, %s", |
| - | strerror(errno)); |
| - | exit_client(0); |
| - | } |
| - | } |
| - | if (fd_main >= 0) { |
| - | FD_SET(fd_main, &rfds); |
| - | } |
| - | |
| - | if (tv.tv_sec > 0) { |
| - | DEBUG(LOG_INFO, "Waiting on select...\n"); |
| - | retval = select(fd_main + 1, &rfds, NULL, NULL, &tv); |
| - | } else { |
| - | retval = 0; /* If we already timed out, fall through */ |
| - | } |
| - | |
| - | now = time(0); |
| - | if (retval == 0) { |
| - | /* timeout dropped to zero */ |
| - | switch (state) { |
| - | case INIT_SELECTING: |
| - | if (packet_num < 3) { |
| - | if (packet_num == 0) { |
| - | xid = random_xid(); |
| - | } |
| - | /* send discover packet */ |
| - | send_discover(xid, requested_ip); /* broadcast */ |
| - | |
| - | timeout = now + ((packet_num == 2) ? 10 : 2); |
| - | packet_num++; |
| - | } else { |
| - | if (client_config.abort_if_no_lease) { |
| - | LOG(LOG_INFO, "No lease, failing."); |
| - | exit_client(1); |
| - | } |
| - | /* wait to try again */ |
| - | packet_num = 0; |
| - | timeout = now + 60; |
| - | } |
| - | break; |
| - | case RENEW_REQUESTED: |
| - | case REQUESTING: |
| - | if (packet_num < 3) { |
| - | /* send request packet */ |
| - | if (state == RENEW_REQUESTED) { |
| - | send_renew(xid, server_addr, requested_ip); /* unicast */ |
| - | } else { |
| - | send_selecting(xid, server_addr, requested_ip); /* broadcast */ |
| - | } |
| - | timeout = now + ((packet_num == 2) ? 10 : 2); |
| - | packet_num++; |
| - | } else { |
| - | /* timed out, go back to init state */ |
| - | state = INIT_SELECTING; |
| - | timeout = now; |
| - | packet_num = 0; |
| - | change_mode(LISTEN_RAW); |
| - | } |
| - | break; |
| - | case BOUND: |
| - | /* Lease is starting to run out, time to enter renewing state */ |
| - | state = RENEWING; |
| - | change_mode(LISTEN_KERNEL); |
| - | DEBUG(LOG_INFO, "Entering renew state"); |
| - | /* fall right through */ |
| - | case RENEWING: |
| - | /* Either set a new T1, or enter REBINDING state */ |
| - | if ((t2 - t1) <= (lease / 14400 + 1)) { |
| - | /* timed out, enter rebinding state */ |
| - | state = REBINDING; |
| - | timeout = now + (t2 - t1); |
| - | DEBUG(LOG_INFO, "Entering rebinding state"); |
| - | } else { |
| - | /* send a request packet */ |
| - | send_renew(xid, server_addr, requested_ip); /* unicast */ |
| - | |
| - | t1 = (t2 - t1) / 2 + t1; |
| - | timeout = t1 + start; |
| - | } |
| - | break; |
| - | case REBINDING: |
| - | /* Either set a new T2, or enter INIT state */ |
| - | if ((lease - t2) <= (lease / 14400 + 1)) { |
| - | /* timed out, enter init state */ |
| - | state = INIT_SELECTING; |
| - | LOG(LOG_INFO, "Lease lost, entering init state"); |
| - | run_script(NULL, "deconfig"); |
| - | timeout = now; |
| - | packet_num = 0; |
| - | change_mode(LISTEN_RAW); |
| - | } else { |
| - | /* send a request packet */ |
| - | send_renew(xid, 0, requested_ip); /* broadcast */ |
| - | |
| - | t2 = (lease - t2) / 2 + t2; |
| - | timeout = t2 + start; |
| - | } |
| - | break; |
| - | case RELEASED: |
| - | /* yah, I know, *you* say it would never happen */ |
| - | timeout = 0x7fffffff; |
| - | break; |
| - | } |
| - | } else if (retval > 0 && listen_mode != LISTEN_NONE |
| - | && FD_ISSET(fd_main, &rfds)) { |
| - | /* a packet is ready, read it */ |
| - | |
| - | if (listen_mode == LISTEN_KERNEL) { |
| - | len = get_packet(&packet, fd_main); |
| - | } else { |
| - | len = get_raw_packet(&packet, fd_main); |
| - | } |
| - | if (len == -1 && errno != EINTR) { |
| - | DEBUG(LOG_INFO, "error on read, %s, reopening socket", |
| - | strerror(errno)); |
| - | change_mode(listen_mode); /* just close and reopen */ |
| - | } |
| - | if (len < 0) { |
| - | continue; |
| - | } |
| - | |
| - | if (packet.xid != xid) { |
| - | DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)", |
| - | (unsigned long) packet.xid, xid); |
| - | continue; |
| - | } |
| - | |
| - | if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { |
| - | DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring"); |
| - | continue; |
| - | } |
| - | |
| - | switch (state) { |
| - | case INIT_SELECTING: |
| - | /* Must be a DHCPOFFER to one of our xid's */ |
| - | if (*message == DHCPOFFER) { |
| - | if ((temp = get_option(&packet, DHCP_SERVER_ID))) { |
| - | memcpy(&server_addr, temp, 4); |
| - | xid = packet.xid; |
| - | requested_ip = packet.yiaddr; |
| - | |
| - | /* enter requesting state */ |
| - | state = REQUESTING; |
| - | timeout = now; |
| - | packet_num = 0; |
| - | } else { |
| - | DEBUG(LOG_ERR, "No server ID in message"); |
| - | } |
| - | } |
| - | break; |
| - | case RENEW_REQUESTED: |
| - | case REQUESTING: |
| - | case RENEWING: |
| - | case REBINDING: |
| - | if (*message == DHCPACK) { |
| - | if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) { |
| - | LOG(LOG_ERR, |
| - | "No lease time with ACK, using 1 hour lease"); |
| - | lease = 60 * 60; |
| - | } else { |
| - | memcpy(&lease, temp, 4); |
| - | lease = ntohl(lease); |
| - | } |
| - | |
| - | /* enter bound state */ |
| - | t1 = lease / 2; |
| - | |
| - | /* little fixed point for n * .875 */ |
| - | t2 = (lease * 0x7) >> 3; |
| - | temp_addr.s_addr = packet.yiaddr; |
| - | LOG(LOG_INFO, "Lease of %s obtained, lease time %ld", |
| - | inet_ntoa(temp_addr), lease); |
| - | start = now; |
| - | timeout = t1 + start; |
| - | requested_ip = packet.yiaddr; |
| - | run_script(&packet, |
| - | ((state == RENEWING |
| - | || state == REBINDING) ? "renew" : "bound")); |
| - | |
| - | state = BOUND; |
| - | change_mode(LISTEN_NONE); |
| - | { |
| - | int pid_fd2; |
| - | |
| - | if (client_config.quit_after_lease) { |
| - | exit_client(0); |
| - | } else if (!client_config.foreground) { |
| - | pid_fd2 = pidfile_acquire(client_config.pidfile); /* hold lock during fork. */ |
| - | if (daemon(0, 0) == -1) { |
| - | perror("fork"); |
| - | exit_client(1); |
| - | } |
| - | client_config.foreground = 1; /* Do not fork again. */ |
| - | pidfile_write_release(pid_fd2); |
| - | } |
| - | } |
| - | } else if (*message == DHCPNAK) { |
| - | /* return to init state */ |
| - | LOG(LOG_INFO, "Received DHCP NAK"); |
| - | run_script(&packet, "nak"); |
| - | if (state != REQUESTING) { |
| - | run_script(NULL, "deconfig"); |
| - | } |
| - | state = INIT_SELECTING; |
| - | timeout = now; |
| - | requested_ip = 0; |
| - | packet_num = 0; |
| - | change_mode(LISTEN_RAW); |
| - | sleep(3); /* avoid excessive network traffic */ |
| - | } |
| - | break; |
| - | /* case BOUND, RELEASED: - ignore all packets */ |
| - | } |
| - | } else if (retval == -1 && errno == EINTR) { |
| - | /* a signal was caught */ |
| - | |
| - | } else { |
| - | /* An error occured */ |
| - | DEBUG(LOG_ERR, "Error on select"); |
| - | } |
| - | |
| - | } |
| - | return 0; |
| - | } |