README for Princeton Patch 9 May 15, 2002 irwin@princeton.edu This file accompanies Princeton Patch 9 to CMU-dhcpd 3.3.7; it describes how to apply the patch, and what has changed. After you have applied the patch, you no longer need this file, since the information in it also appears in the RELEASE_NOTES file which results from applying the patch. This patch (as well as the complete pre-patched current distribution) is available via: http://www.net.princeton.edu/software/dhcpd/ ========================================================================= UPDATING FROM PATCHLEVEL 8 The patch is against dhcpd version 3.3.7 as distributed by CMU, with Princeton patches 1, 2, 3, 4, 5, 6, 7, and 8 already applied. Assuming you have already applied the previous patches, you can apply this patch as follows: 0. Since this patch introduces new behavior as well as bug fixes, review the "WHAT'S CHANGED" section below before proceeding. 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') and 'defaults.h'. If you did customize either 'Makefile.in' or 'defaults.h' files, put the original version back to ensure the patch will apply cleanly. 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-patch9 3. Run 'make distclean' to remove old object files, Makefiles and configuration info. 4. If you had previously customized any 'Makefile.in' files (as opposed to customizing any 'Makefile' files) , now's the time to re-apply your customizations if necessary. (You *did* make a record of them, right?) Most of the definitions that used to be in Makefile.in have moved to defaults.h, so you may not need to make any changes to any 'Makefile.in' files at this time. Before re-apply any of your old customizations, first check to see if you can now accomplish what you need with a commandline option instead of a compile-time definition; most defaults and features that once required a compile-time definition can now be overridden using commandline options. Commandline options are covered in the in the dhcpd.8 man page. If you do change any 'Makefile.in' files, save the originals first, so you'll have clean copies available when it comes time to apply the next patch. 5. Run './configure' to figure out your system's configuration and generate new 'Makefile' files from the 'Makefile.in' files. 6. If you had previously customized any 'Makefile' files (as opposed to any 'Makefile.in' files), now's the time to re-apply your customizations. if necessary. (You *did* make a record of them, right?) Most of the definitions that used to be in 'Makefile' files have moved to defaults.h, so you may not need to make any changes to 'Makefile' files at this time. Before re-apply any of your old customizations, first check to see if you can now accomplish what you need with a commandline option instead of a compile-time definition; most defaults and features that once required a compile-time definition can now be overridden using commandline options. Commandline options are covered in the dhcpd.8 man page. 7. Examine the 'defaults.h' file and customize it if necessary. This file now contains many of the definitions that previously lived in Makefile.in. Avoid changing items that can also be set using a commandline option. If you do change the 'defaults.h file, save the original first, so you'll have a clean copy available when it comes time to apply the next patch. 8. Run 'make' to compile the program. If you receive any compile-time errors, review the INSTALL document for tips. 9. Kill any running dhcpd, then install the new dhcpd executable. (It often lives in /etc/dhcpd or /usr/local/etc/dhcpd, but you may install it elsewhere.) You'll probably want to back up your old dhcpd executable before installing the new one. 10. Optionally update any startup script you may use to take advantage of new commandline options added in this patch. 11. Optionally update your bootptab file to take advantage of new tags added in this patch. 12. Optionally update your dhcpd.conf file to take advantage of new configuration features added in this patch. 13. Start the new dhcpd executable. 14. Optionally install the new dhcpd.8, bootptab.5, and dhcpd.conf.5 man pages manually. --------------------------------------------------------------------- BUILDING FROM SCRATCH You can get the current version of the source from http://www.net.princeton.edu/software/dhcpd/ Then see the INSTALL document for instructions. --------------------------------------------------------------------- PLATFORMS This code is currently in production at Princeton, but has NOT been tested extensively in all environments. In particular, I'm presently compiling and running on Solaris 8 SPARC systems, compiling with gcc 3.0.4. --------------------------------------------------------------------- WHAT'S CHANGED Changes since PU patch 8: --- Updated README and dhcpd.8 to reflect that the product's home is now: http://www.net.princeton.edu/software/dhcpd/ --- Corrected INSTALL doc; it suggested you block the bootp server port at your firewall router, but said the port port number was 161. The bootp server port is 67. --- Changed behavior in processing DHCPDECLINE messages. RFC2131 says that the message MUST contain a Server Identifier option, but does not specify how the server should process/verify this field (e.g. should we ignore the message if the field is missing or specifies a Server Identifier other than ourself?). Previously we ignored the field. Now we examine the field; if it is present and not equal to my_ip_addr, we ignore the message. If it is not present at all, we continue to process the message (as before). Some DHCP clients (Win98??) mistakenly set the Server Identifier option to 255.255.255.255 in DHCPDECLINE messages. To continue to accomodate these, when we see this Server Identifier, we *will* process the message as before. --- Added the optional 'known_server_identifier' keyword to the dhcpd.conf file. You may use this to ask the server to log each time it encounters a client that specifies a Server Identifier Option value for an unknown DHCP server. This may help alert you to rogue DHCP servers. Here is the description from the dhcpd.conf man page: known_server_identifier The known_server_identifier statement is optionally used to describe each DHCP Server on the network. The format is: known_server_identifier ipaddress Specify the ipaddress of a DHCP server operating on your internetwork. If there are multiple DHCP servers, specify each in a separate statement. This statement is optional; if present, the server will attempt to log each time a client specifies a Server Iden- tifier Option containing a value that is neither my_ip_addr nor one of those specified as a known_server_identifier. This may help alert you to rogue DHCP servers. The server checks for a Server Identifier Option in DHCPRE- QUEST messages (when a client is SELECTING), DHCPRELEASE messages, and DHCPDECLINE messages. When the Server Iden- tifier Option is specified in one of these messages, but is not equal to my_ip_addr, the server checks to see if you have specified any known_server_identifier values. If so, it checks to see if the Server Identifier Option matches one of those; if it matches none of them, the server logs it as an "unknown Server Identifier." Since the server does not examine the known_server_identifier values when the Server Identifier Option matches my_ip_addr, you need not list your server's own IP address as a known_server_identifier. The known_server_identifier statement is optional; if you have none, the server skips the check; it will not log any "unknown Server Identifier." This check is only used to provide you with some hint that there may be rogue DHCP servers operating on your network. It is possible for rogue DHCP servers to operate on your network and not be noticed by this check. Note the statement does not allow you to declare a set of Server Identifier Option values that the server will con- sider equivalent to itself. --- Relocated configuration code that determines if ioctls on sockets should use STREAMS ioctls instead of SIOCxxx ioctls. It used to be in configure.in; it's now in a new macro IST_SYS_SOCKET_IOCTLS_USE_STREAMS in aclocal.4. The preprocessor symbol defined has changed from USE_STREAM_IOCTLS to SYS_SOCKET_IOCTLS_USE_STREAMS. --- An error message we may produce implied that the error is within the DHCP server (inconsistent hash tables) while in fact the error may be caused by the client. Producing the message requires a specific combination of circumstances. The client (cliid) must have an existing DHCP lease. The client must then send a DHCPREQUEST asking to RENEW a lease, but specify a 'ciaddr' field (which is the IP address of the lease the client is asked to renew) which is *not* the client's current leased address. Moreover, this incorrect ciaddr must be an IP address listed in the server's bootptab as a dynamically-assigned IP address, not presently bound to any client, and also be one that this cliid *could* be awarded based upon access policy (if it made the appropriate request from the appropriate subnet). In that circumstance, the message we produced was: dhcp_request(): RENEWING/REBINDING/BOUND: did *NOT* find client's IP (192.168.2.2) in ipboundhshtbl,... but did find entry for this cliid (01 01 02 03 04 05 06 ) in boundhshtbl, assigned IP address 192.168.1.1! freeing binding It's been reworded to say: dhcp_request(): RENEWING/REBINDING/BOUND: did *NOT* find client's claimed IP (ciaddr=192.168.2.2) in ipboundhshtbl,... but did find entry for the cliid (01 01 02 03 04 05 06 ) in boundhshtbl, assigned IP address 192.168.1.1!... I.e. the client is trying to renew a lease on an IP address (192.168.2.2) that is not leased to it. freeing binding Note that we still take the same action as before; we discard the client's current binding, on the assumption that the client is very confused, and apparently no longer thinks it is bound to the IP address we think the client is bound to. Whether or not this message should still be logged as an "error" (since it may be just a confused client, not a server bug) is an open question. --- Sometimes a broken DHCP client will obtain a lease on a dynamic IP address, allow the lease to expire, then resume using the IP address well after the expiration. Sometimes the broken client sends DHCPREQUEST packets attempting to renew the lease, acting like it is in RENEWING or REBINDING state. Previously, if the DHCP server could tell the specified IP address is topologically inappropriate for the client (based on giaddr), the server would NAK the client. If the DHCP server could tell the specified IP address was inappropriate for the client based on access policy set for the dynamic address pool, the server would NAK the client. If the DHCP server had a current lease for the IP address, but for a different Client Identifier, the server would NAK the client. Otherwise if the IP address was not in the bootptab, or the IP address in the bootptab was marked dynamic but not a member of any dynamic pool this DHCP server handles, the server ignored the client. All that remains the same, but the following has changed: If the specified IP address is in the server's bootptab, is marked dynamic, is a member of a dynamic pool this server handles, and this server has no current lease for this IP address, the server will now NAK the client instead of ignoring it. This is because the server knows the client is wrong. (The server is authoritative for this IP address and knows there is no lease for it, so the client trying to renew a lease on this IP address must be wrong. The client could never succesfully renew an unbound dynamic address from this server, and even if the client is REBINDING, no other DHCP server will renew that particular IP address, since it's THIS server that's authoritative for that IP address.) We NAK the client in the hope it will send it back to INIT state (although the client may be so broken that it may ignore us entirely). When it does this, it logs messages like the following at LOG_INFO when debuglevel > 2: client 01 01 02 03 04 05 06 in RENEWING/REBINDING/BOUND state tried to renew dynamic IP address 192.168.1.2 ... but this IP address is not presently bound and is a member of one of my pools, so we will NAK the client --- If a DHCP packet received from a client failed to terminate its options with an "END" option, we treated this as an error, logging a message like the following, and ignoring the packet: error parsing DHCP packet, ignoring DHCP client (chaddr=01 01 02 03 04 05 06 , but may be inaccurate), (IPsrc=192.168.4.5) The spec does not require the options sent by the client to always terminate with an "END" option; if no "DHPC Options Overload" option is present, leaving off the "END" option simply means the options continue through the end of the relevant field in the packet. We no longer complain when the "END" option is not present, and we no longer ignore the packet. --- A malformed DHCP packet received from a client might cause the server to loop. Specifically, if the packet's "Options" field contained a DHCP Overload Option that specified that the "file" field is overloaded, and the "file" field contained another DHCP Overload Option that specified that the "file" field is overloaded, we could loop processing the "file" field. A similar problem could happen with the "sname" field. We will no longer try to process an overloaded field (file, sname) more than once. --- A malformed DHCP packet received from a client might cause the server to try to parse data from memory as "option" data. Specifically, if the packet's "Options" field contained an option that contained a "length" byte indicating the option extended beyond the end of the "Options" field (or the option began on the final byte of the field), we could attempt to read bytes in memory that followed that field as if they were option values. Results could be unpredictable. The same problem affected parsing of overloaded file and sname fields. Now we report that the "DHCP options overflowed available space in packet", and discard the malformed packet. --- Fix typo in log message: dchp_request(): no current lease or offer for SELECTING client, sending NAK (cliid=01 00 01 02 03 04 05) ^^^^ now reads: dhcp_request(): no current lease or offer for SELECTING client, sending NAK (cliid=01 00 01 02 03 04 05) --- Added a "Lone DHCP Server" feature, suggested by Mike Weller . Here is the relevant section of the man page: Specifying the -L commandline option enables the Lone DHCP Server feature. The feature causes the server to behave in ways that may be helpful, but would normally be unsafe in the presence of other DHCP servers. RENEWING versus REBINDING When we receive a DHCPREQUEST from a client, it is not always possible for us to distinguish the client state RENEWING from REBINDING. (This is because in some situa- tions, we must see the IP destination address of the DHCPRE- QUEST packet to distinguish the two cases. The IP destina- tion address is not available to an application using a datagram socket in many BSD-derived sockets implementa- tions.) This often does not create any difficulty, however, there are times we wish to respond to a client differently depend- ing on whether the client is in the RENEWING or the REBIND- ING state. When a client asks us to renew a lease which we have not granted the client, we want to send a DHCPNAK. How- ever, if the client is REBINDING, the client may have received the lease from another DHCP server, and in that case, we must be silent. That's because we do not know definitively that the client may not be granted the requested lease, as another DHCP server may be able to grant the client's request. Our implementation normally follows the conservative approach in the situations above: it assumes the client may be REBINDING, so we are silent. The result is that REBINDING clients will work properly, but RENEWING clients that we'd like to NAK may be allowed to keep using their old leases (until the client believes the lease has expired, or the client reboots). This is the only safe way we can run if there are other DHCP servers on the network. If you enable the Lone DHCP Server feature, instead of fol- lowing the conservative approach above, we will assume the client is in the RENEWING state, so we will send a DHCPNAK. Sending a DHCPNAK may force the client back to INIT state, allowing it to then proceed to obtain a valid DHCP lease. However, note that many clients will ignore the DHCPNAK, instead continuing to use the IP address they believe they have leased, until they believe the lease expires. --- If a client obtains a DHCP lease (or a dynamically-assigned IP address via BootP), then you later change bootptab so this client identifier or hardware address is tagged with the ':de:' tag (Deny Client), the existing assignment was not being invalidated. We now invalidate the existing assignment. --- Added a new ':dd:' tag (Deny client dynamically-assigned IP addresses). Here is the relevant section from the bootptab man page: The dd tag is strictly a boolean tag; it does not take the usual equals-sign and value. It may only be specified in entries which contain a hardware address or client identif- ier. Its presence indicates special handling for packets received from this client's hardware address or client iden- tifier; the intent is to never award a dynamically-assigned IP address to this client, even if it would otherwise be eligible for one. It takes precedence over the access flags that might be specified in the network statement within the dhcpd.conf file, and also takes precedence over any ro that might be specified. It has no effect on awarding a static IP address to this client. --- We could crash when trying to read the lastbindings from disk on any platform that treats 'char' as a signed type. Within a client's lastbinding record on disk, four fields are variable-length: the Client ID, the Client Hardware Address, the (optional) vend field, and the (optional) user field. If the length of any of these fields was greater than 127, we eventually misinterpreted the field length as a very large number. We tried to malloc() a huge amount of memory to store the field, possibly failing there. If the malloc() succeeded, we'd typically fail as we tried to bcopy() more data that we had. Once we got into this state, restarting the server would not help, as the same lastbindings data would consistently cause the same failure. This wasn't seen often, because of the four fields that could trigger the problem, the first two are normally quite short, and the latter two are usually shorter than 127 bytes (when present at all). The problem would be triggered when some client had a vend or user field > 127 bytes. A short-term workaround was to discard the lastbindings data file containing the problematic data. (Or more likely, discard all the lastbindings data files.) We no longer misinterpret the field length. --- Clarify some of the messages we log by adding the IP address in question. --- Added support for BootP/DHCP options: Option Tag Code Name Description ---------------------------------------------------------- 19 if IP Forwarding Enable/Disable 20 cr Non-Local Source Routing Enable/Disable 23 tt Default IP Time-to-live (TTL) 27 sl All Subnets are Local 29 md Perform Mask Discovery 30 mu Mask Supplier 32 rs Router Solicitation Address 34 te Trailer Encapsulation 36 en Ethernet Encapsulation 37 tl TCP Default TTL 39 kg TCP Keepalive Garbage 45 nd NetBIOS over TCP/IP Datagram Distribution Server 48 xf X Windows System Font Server 49 xd X Windows System Display Manager 64 pd Network Information Service+ (NIS+) Domain 65 ps Network Information Service+ (NIS+) Servers 69 ma Simple Mail Transport Protocol (SMTP) Server 70 po Post Office Protocol (POP3) Server 71 nn Network News Transport Protocol (NNTP) Server 72 ww default World Wide Web (WWW) Server 73 fi default Finger Server 74 ir default Internet Relay Chat (IRC) Server -- Fix Makefile.in to properly list dependancies for dynamic.o. --- Update configure to check for definition of struct ortentry{}. If we decide to add a route for the broadcast address, we make use of ortentry{] if it is defined, else we use rtentry{}. This may improve portability to systems that have redefined rtentry{} to something very different (e.g. FreeBSD). (Based on a (different) patch submitted by Howard Harvey .) --- Replace config.guess with latest version (2002-03-20) from GNU. Replace config.sub with latest version (2002-04-26) from GNU. --- If any generic (Txxx) tag specified for an entry did not fit into a BOOTPREPLY packet, sometimes we failed to log that the option didn't fit. --- Added a new feature to filter the options inserted into the "vendor options" field in a BOOTPREPLY. Here is the relevant section from the bootptab(5) man page: FILTERING BOOTPREPLY VENDOR OPTIONS The be and bi tags can be used to filter the options that may be returned in the "vendor options" field of a BOOTPRE- PLY. The "vendor options" field in a BOOTPREPLY packet is (strictly speaking) limited to 64 bytes, much less than the field in a DHCP response packet. If you have specified a significant amount of data in a bootptab entry, there may be too much to fit into the packet if the client speaks BootP. The be and bi tags can help, by letting you filter the options that we will try to insert into the BOOTPREPLY. These tags only apply to replies sent to clients speaking BOOTP (not DHCP) that choose to use of the RFC1048 cookie. Either tag takes a value which is an ASCII string surrounded by double quotes ("). The value consists of whitespace- delimited tag names (including generic tag names); e.g. be="lp lg nt T144". If the be tag is specified, then any elements specified in its list will be excluded from a BOOTPREPLY vendor options field. If the bi tag is specified, then any elements not specified in its list will be excluded from a BOOTPREPLY vendor options field. (That is, be and bi act as filters on our reply.) You may not specify both be and bi in the same bootptab entry. (If the entry has inherited one of these tags from a template, cancel it (e.g. be@) before applying the other tag.) Because be and bi tags apply to an IP address (rather than a hardware address or client identifier), they only make sense to specify in an entry that has an IP address, or an entry that is used as a template for other another entry that has an IP address. If nr is specified in combination with be (or bi), the filtering specified by be (or bi) takes precedence. The tag names specified within a be or bi list need not appear elsewhere in the bootptab entry. Do not specify a tag in a be or bi list using the generic (Txxx) syntax if the tag has a well-defined bootptab tag name; if you do, the results may be unpredictable. It is an error to specify in a be or bi list any tag names which do not correspond to an option that might actually appear in a BOOTPREPLY "vendor options" field. E.g. because any bootfile name would appear elsewhere in the BOOTPREPLY packet, it is an error to specify bf as an element with a be or bi list. The same is true for tag names that correspond to directives that modify the behavior of the server itself (e.g. de). We do not permit you to filter a few options from a BOOTPREPLY's vendor options field. They include: subnet mask, router addresses, extension file, pad, end. --- Fixed the bug that caused a generic tag to be left out of a DHCP reply packet when the 'nr' option was specified in the bootptab entry. (We still have the bug that causes a generic tag to be left out of a DHCP reply packet when the client specifically requests that tag via a Parameter Request List option, if the 'nr' tag is not specified in the bootptab entry.) --- Attempting to specify generic tag T0 or T225 is now a syntax error. The generic tag syntax (Txxx=value) doesn't support options that have no length byte. --- Parsing of generic tags (Txxx) in bootptab is stricter. If you specify an ASCII (not hex) value for the tag, you must surround it in double-quotes, as the bootptab man page specifies. In the past, leaving out the quotes appeared to work. Now, if you specify an ASCII value without double-quotes, it will be interpreted as a hex value. E.g. the following will not work anymore: T200=foo Instead write: T200="foo" This only affects generic (Txxx) tags. Other tags that accepted unquoted ASCII strings in the past still do. Note that if do use the incorrect syntax, we will be able to detect a syntax error if the value begins with characters that cannot possibly be a hex value (e.g. T200=foo). If the value begins with characters that could be interpreted as a hex value (e.g. T200=about), we will silently misinterpret the value as hex.