README for Princeton mods version 6 July 21 1998 irwin@princeton.edu This file contains information about version 6 of the Princeton mods to CMU-dhcpd 3.3.7. The patch is available via: http://www.princeton.edu/~irwin/dhcpd.html --------------------------------------------------------------------- UPDATING FROM PATCHLEVEL 5 The patch is against dhcpd version 3.3.7 as distributed by CMU, with Princeton patches 1, 2, 3, 4, and 5 already applied. Assuming you have already applied the previous patches, you can apply this patch as follows: 1. Make a record of any local customizations you have made to the product, such as changes you have made to 'Makefile' (or possibly 'Makefile.in'). 2. Change to the directory containing the dhcpd source, and feed the patch file to the 'patch' program. (The 'patch' program is not supplied here; it is included with many operating systems, and is freely available from many software libraries.) E.g. using GNU patch: 'patch -p0 < CMU-dhcpd-3.3.7-PU-patch6'. 3. Run 'make clean' to remove old objects. 4. Run 'make distclean' to remove old Makefiles and configuration info. 5. If you had previously customized any 'Makefile.in' files, now's the time to re-apply your customizations. (You *did* make a record of them, right?) 6. Run 'configure' to figure out your system's configuration and generate new Makefiles from the 'Makefile.in' files. 7. If you had previously customized any 'Makefile' files, now's the time to re-apply your customizations. (You *did* make a record of them, right?) 8. Run 'make' to compile the program. 9. Kill any running dhcpd, then install the new dhcpd executable. (It often lives in /etc/dhcpd, but you may install it elsewhere.) 10. Start up the new dhcpd executable. 11. Optionally install the new dhcpd.8, bootptab.5, and dhcpd.conf.5 man pages. --------------------------------------------------------------------- BUILDING FROM SCRATCH If you are starting from scratch (you don't already have dhcpd patchlevel 5 source, then first get and unpack dhcpd 3.3.7 from CMU. Then change to the directory containing the dhcpd source, and feed each Princeton patch file to the 'patch' program, in order. (The 'patch' program is not supplied here; it is included with many operating systems, and is freely available from many software libraries.) Before applying patch 5, issue the following command: mkdir tools After applying patch 5, issue the following command: chmod 0555 tools/gen-config Then see the "Installation" document for instructions about compiling and installing the software. Read the dhcpd.8, bootptab.5, and dhcpd.conf.5 man pages. --------------------------------------------------------------------- PLATFORMS This code is currently in production at Princeton, but has NOT been tested extensively in all environments. In particular, I've only compiled on SunOS 4.1.4 and Solaris 2.5.1, and most of my testing is on Solaris 2.5.1. --------------------------------------------------------------------- WHAT'S CHANGED Changes since PU patch 5: --- When the same relay agent was listed in more than one 'network' statement in the dhcpd.conf file, the resulting struct gateway member of the gwhashtbl was corrupt. The gw->nets field pointed to freed memory, instead of to an array of pointers to the names of the pools of dynamic IP addresses behind this relay agent. This could cause all sorts of havoc. --- The server now supports multiple IP networks on the same wire; code contributed by Mel Lew . Use the new 'proxy' statement in the dhcpd.conf file to define the additional IP networks. The syntax is: proxy relayAgent-interface-ipaddr secondary-network-number secondary-network-netmask [secondary-network-number secondary-network-netmask]... The relayAgent-interface-ipaddr is the IP address of a relay agent already defined in an earlier 'gateway' statement. The secondary-network-number and secondary-network-netmask describe the additional IP network running on the same wire associated with that earlier 'gateway' statement. If more IP networks are on that same wire, you may specify additional pairs of secondary-network-number and secondary-network-netmask, or you may list them on separate 'proxy' statements. If you are also serving pools of dynamic IP addresses on the additional IP network, you first define the pool as usual, with a 'network' statement. The relayagentipaddr and relayagentnetmask that appear in that 'network' statement should be the relay agent interface's *primary* IP address (that which appears in a 'gateway' statement), *not* the secondary IP address, since it's the former that will be inserted by the relay agent into a relayed packet's 'giaddr' field. If you are also serving pools of dynamic IP addresses on the additional IP network, you also specify the poolname on the 'gateway' statement corresponding to the relay agent. You don't specify poolnames in 'proxy' statements. When a request from a client behind the relay agent arrives, it may be assigned a dynamic IP address from *any* of the pools listed on that agent's 'gateway' statement (since there's no way to tell which "pool" the client should draw from -- the client's request is simply stamped with the relay agent interface's primary IP address). If the server's interface is attached to a wire that contains multiple IP networks, define these the same way. I.e. in addition to specifying a 'gateway' statement for the server's interface, use a 'proxy' statement to define the additional IP networks on that interface's wire. And list any dynamic IP address pools in the 'gateway' statement. The sample dhcpd.conf file in sample/dhcpd.conf.sample2 has been updated to show the new feature, as has the dhcpd.conf man page. --- The code that walked the interfaces, getif() and init_interface_tab() in getif.c, tended to be the least-portable of the code. I've replaced init_interface_tab() with a new version init_interfaces() which is based on the example given by W. Richard Stevens in "UNIX Network Programming, Volume 1", Second edition. I'm hoping this will improve portability. I've also added a note to 'Installation' that changing the CDEFS in the top-level Makefile can sometimes resolve startup ioctl() errors. The interface information is now stored in a linked list instead of a statically-sized array, so there's no longer a MAX_INTERFACES constant you'd have to increase on a server with more than 10 interfaces. I've revised the other functions in getif.c accordingly. --- Previous configure script was generated by autoconf 2.1, current one was generated by autoconf 2.12. In PU patch 3, code for defining HAVE_STROPTS_H based on the existance of stropts.h was added to configure and config.h.in, but was never added to configure.in. Now it is in configure.in. In PU patch 3, code for defining CONFIG_SOCKARGLENTYPE based on whether the type socklen_t was defined was added to configure and config.h.in, but was never added to configure.in. If CONFIG_SOCKARGLENTYPE was not defined, defs.h would typedef socklen_t to an int. Now the check for the socklen_t type is integrated in configure.in; if socklen_t is not defined, it is defined (by config.h) as an unsigned int. In PU patch 3, code for checking for the existance of libgen and adding it to LIBS was added to configure, but was never added to configure.in and config.h.in. Now that's been integrated. In PU patch 3, code was added to configure and the Makefiles to check if the system was UnixWare, and if so, to define XDEFS to -D_KMEMUSER and to add libresolv to LIBS, to make dhcpcmd and snmpd work. Now the check for UnixWare has been integrated into configure.in. The -KMEMUSER symbol is now defined accordingly in config.h.in so XDEFS is no longer needed. In PU patch 3, code was added to configure and config.h.in to check if the system was UnixWare, and if so, define the SVR4 symbol. That symbol was used in getif.c and hwaddr.c to select the use of stream ioctls intead of BSD ioctls when working with sockets. Now the decision to use stream ioctls is based instead on a more specific USE_STREAM_IOCTLS symbol. I've added code to configure.in to define USE_STREAM_IOCTLS when appropriate. The heuristic it presently uses is to check for the presence of the stropts.h header. If it is present, then we check if the system is on a short list of systems known to require stream ioctls for sockets (presently that list only includes UnixWare). If your system should be added to this list, please let me know, and define USE_STREAM_IOCTLS manually for now. New files acconfig.h and aclocal.m4 added in support of autoconf. --- Previously lease expirations were only logged when the debuglevel was 4 or greater. Now you can force them to be logged regardless of the debuglevel with the new -e option. --- Mel Lew points out that if a client specifies a DHCP Client ID option with a value different than its hardware address, it can cause problems. This is because we were being lenient when looking up Client ID's in the bootptab. Sometimes when looking for a bootptab entry matching an incoming packet's Client ID, if we did not find it, we'd retry the lookup, this time using the incoming packets htype and haddr. If we found it on the second try, we'd consider that a match. This eventually led to inconsistencies in the tables recording leases. We're no longer lenient; the retry described above no longer happens. As a result, clients that were specifying garbage DHCP Client ID option values but receiving service by virtue of the retry will no longer get service. And bad bootptab entries that specified garbage 'cl' tag values but still worked by virtue of the retry will also no longer work. The bad DHCP client's should be reconfigured (or fixed) to specify good (or no) DHCP Client ID option, and the bad bootptab entries should be corrected. --- If we received a packet which contained a htype (hardware type) code that we've never heard of, we'd dump core. Now we ignore the packet, logging a message at LOG_NOTICE if debuglevel is 3 or higher. --- mark@sunsite.unc.edu (mark@zang.com) has made a compiled version of dhcpd with the Princeton patches for Solaris available on Sunsite; you may want to retrieve that if you have difficulty compiling. It's presently available as a Solaris package (for 2.5, 2.5.1, and 2.6; earlier releases may work also) for SPARC hardware. Visit: http://sunsite.unc.edu/pub/packages/solaris/sparc ftp://sunsite.unc.edu/pub/packages/solaris/sparc (Look for a filename that starts with 'dhcp' and check to see what version number (patchlevel) it includes.) --- When we receive a DHCPREQUEST from a client in INIT-REBOOT, and the requested IP address is for a dynamic IP address, we now have an additional check before granting the request. We check to see if the client id appears in the bootptab with a static IP address assigned. If so, and if that static IP address would be appropriate given the network to which the client is presently attached, then we NAK the DHCPREQUEST. Previously we would ACK the request, so the client continued using the dynamic IP address it had requested. That was in keeping with the spec, but was rarely what you wanted to happen. If you've changed the client's entry in bootptab so that it now has a static IP address appropriate for the network to which it is attached, you normally want it to be assigned the static IP address in preference to a dynamic IP address. By NAKing the request, we force the client from INIT-REBOOT back to INIT. Now when the client sends a DHCPDISCOVER, we'll offer the static IP address. (And at the same time, if our bindings indicate that the client id is presently bound to the dynamic address, we'll remove that binding at the same time, since we know the client is no longer as we've determined he's in INIT state.) Note that this change in policy only affects clients in INIT-REBOOT state. (I.e. the client has to unload/reload the IP stack to get into this state; e.g. by rebooting the client computer.) The policy change does *not* affect a client in RENEWING (or REBINDING) state. Those clients can still renew dynamic addresses presently leased to them, even if a an static address has become available (due to a change in bootptab), as per spec. (Recall that the server generally cannot determine to what network a RENEWING or REBINDING client is attached; it is forced to believe that the client's requested IP address is reasonable for the client's present network. It is up to the user to power-down a machine before moving it to another network, to ensure it begins in the new network in INIT or INIT-REBOOT state.) --- When processing a DHCPDISCOVER or DHCPREQUEST, when we found a current binding for this client and decided it was OK to continue to use that binding, we neglected to check to see if the current binding had already expired. Such bindings are still present in our current binding tables until they are garbage-collected. In most cases, this wasn't a problem; we just extended the lease as necessary, which made the binding good. But there may have been problems if the current binding we were continuing to use was in fact just the result of an OFFER that the client never accepted. In that case, the "binding" was never a complete lease, so it may not be safe to just extend it. It's possible that is what led to the occassional "Sending DHCPNACK: Lease wasn't offerred" situation that seemed wrong since the server *had* just sent an OFFER to the client. --- When we receive a DHCPREQUEST from a client in INIT-REBOOT, if we already have a binding for this cliid, but the binding was for an IP address different than the one the client is now requesting, we thought there was something wrong with our binding hash tables. We'd remove the old binding, log an error, and NAK the client. Now we recognize that this just means the client has surrendered the old binding; we still remove the old binding, but do not log an error nor send a NAK. We don't respond to the DHCPREQUEST; we let the client timeout and resend it (or timeout and fall back to INIT); either way when we get the new request, we'll be able to process it without being distracted by the old binding which has now been removed. (That's not entirely in keeping with the spec, but is simpler than trying to restart processing of the original packet.) --- bootptab(5) man page says the tag to specify IP Broadcast Address is 'bc'. That's a typo; it is 'ba'; fixed. (Dennis Moreno ) --- When DEBUG was defined, hash_Delete() was producing some debug messages even if the debuglevel was < 16. --- When sending DHCP replies through a relay agent, we neglected to convert the destination port (bootps_port) to network byte order. (Chris Tengi , Stephen C. Trier , and Petr Lampa ) --- When sending the Time Offset option to a DHCP client, we neglected to convert it to network byte order. (Petr Lampa ) --- ipcmp() now checks if either arg is NULL, to avoid crashing on deref NULL ptr. --- We weren't always checking that malloc() succeeded. --- Made the compiler happier here and there. --- Ignore requests from clients that specify an Ethernet broadcast or multicast address in the chaddr field, or in the DHCP Client ID option. If debuglevel > 2, we'll log these. (Stephen C. Trier ) --- When sending DHCP lease expiration, renew, and rebind times, we were neglecting to convert them to network-byte order. (Stephen C. Trier and Petr Lampa ) --- When sending the Boot File Size option in DHCP (but not BootP) replies, we were neglecting to convert it to network-byte order. (Petr Lampa ) --- When checking the 'flags' header field in received DHCP packets, we were neglecting to convert to host-byte order before performing the check to see if all must-be-zero bits were zero. When we believed we found a MBZ bit set, we cleared it in the reply packet, but that value too was not being converted correctly. --- When we sent the Boot File Size option in a DHCP reply packet, we sent it as a 32-bit value. It should be a 16-bit value. --- When we received the Maximum DHCP Message Size option in a DHCP packet, we neglected to convert it to host-byte order before using it. (Petr Lampa ) --- When dumping the current bindings or the internal (parsed) bootptab database to a file, we neglected to show the value of any NetBios over TCP/IP Name Servers. (Petr Lampa ) --- The "Maximum DHCP Lease" tag documented in the bootptab man page ("ml") was not parsed. Now we'll parse it. (Petr Lampa ) At this time, we don't use any "ml" value you specify. That's because we don't pay attention to any Lease Time suggested by the client in DHCPDISCOVER or DHCPREQUEST packets. (If we did pay attention to the client's suggestions, "ml" would presumably be used to provide an upper bound on the lease we grant, possibly with some interaction with the "dl" tag (if specified). And we'd probably want to have a lower bound too.) The length of the DHCP lease we offer is still the the value of the "DHCP Lease Time" bootptab tag ("dl") if specified; otherwise it is the value of the DEFAULT_LEASE definition (6 hours, as distributed). --- Added -S option, which you may use to disable the "BootP siaddr Local Optimization" feature. That feature (which is not new, and continues to be the default behavior) is now more clearly described in the dhcpd.8 man page. (Mel Lew ) --- Added -C option, which you may use to add some compact log messages when debugging is enabled. If you want information about each packet but don't want to run at debuglevel 2 or higher, you may want to enable these (Mel Lew ). Typical messages look like: Mar 27 12:42:29 dhcpd[11554]: DHCPDISCOVER 00609796E682 [128.59.33.1]: found 128.59.33.11 (test.cc.columbia.edu) Mar 27 12:42:29 dhcpd[11554]: DHCPDISCOVER 00609796E682 [128.59.33.1]: offer 128.59.33.11 (test.cc.columbia.edu) Mar 27 12:42:29 dhcpd[11554]: DHCPREQUEST 00609796E682 [128.59.33.1]: request 128.59.33.11 Mar 27 12:42:29 dhcpd[11554]: DHCPREQUEST 00609796E682 [128.59.33.1]: lease 128.59.33.11 (test.cc.columbia.edu) until Fri Mar 27 18:42:29 1998 (21600 seconds) Mar 27 12:42:29 dhcpd[11554]: DHCPpkterror 0102030405060708090A0B0C0D0E0F10 [128.59.33.1]: bad address length 16 != 6 --- You may now increment the debuglevel by sending the process a SIGABRT signal (SIGIOT on older systems). You can reset the debuglevel to 0 by sending it a SIGSYS signal (if this signal is available on your system). Signal calls have been cleaned up for consistency. (Mel Lew ) --- dhcp_opts() and cliid_btoa() failed to limit the scope of the the temp variable 'i' they used internally; they'd overwrite the value of 'i' in the calling scope. (Mel Lew ) --- The optional 'dhcpcmd' program had the filename /etc/snmpd.private hardcoded into it. Now that's a new compile-time definition PRIV_FILE you may modify by editing the Makefile. (Mel Lew ) --- If your bootptab contained entries for IP addresses that were marked 'dynamic', but did not happen to fall into any of the pools defined by 'network' statements in your dhcpd.conf, then we could crash in dhcp_request() when a client asked for one of those addresses. Now we know well enough to ignore the client's request. (Mel Lew ) --------------------------------------------------------------------- LEGAL STUFF You may copy, modify, and redistribute these modifications and documentation (the "product"), as long as doing so will not violate any copyrights, patents, or other restrictions that may be imposed on the underlying product by Carnegie Mellon University. The product" is provided "as is" without warranty of any kind, either express or implied, including without limitation any warranty with respect to its mechantability or its fitness for any particular purpose. The entire risk as to the quality and performance of the product is with you. The author, and Princeton University, does not warrant that the functions contained in the product will meet your requirements or that the operation of the product will be uninterrupted or error-free, or that defects in the the product will be corrected.