KNOWN BUGS
Where to find documentation
Documentation for this software includes this README file, the
RELNOTES file, and the manual pages, which are in the server, common,
client and relay subdirectories. Internet standards relating to the
DHCP protocol are stored in the doc subdirectory. You will have the
best luck reading the manual pages if you build this software and then
install it, although you can read them directly out of the
distribution if you need to.
DHCP server documentation is in the dhcpd man page. Information about
the DHCP server lease database is in the dhcpd.leases man page.
Server configuration documentation is in the dhcpd.conf man page as
well as the dhcp-options man page. A sample DHCP server
configuration is in the file server/dhcpd.conf. The source for the
dhcpd, dhcpd.leases and dhcpd.conf man pages is in the server/ sub-
directory in the distribution. The source for the dhcp-options.5
man page is in the common/ subdirectory.
DHCP Client documentation is in the dhclient man page. DHCP client
configuration documentation is in the dhclient.conf man page and the
dhcp-options man page. The DHCP client configuration script is
documented in the dhclient-script man page. The format of the DHCP
client lease database is documented in the dhclient.leases man page.
The source for all these man pages is in the client/ subdirectory in
the distribution. In addition, the dhcp-options man page should be
referred to for information about DHCP options.
DHCP relay agent documentation is in the dhcrelay man page, the source
for which is distributed in the relay/ subdirectory.
To read installed manual pages, use the man command. Type "man page"
where page is the name of the manual page. This will only work if
you have installed the ISC DHCP distribution using the ``make install''
command (described later).
If you want to read manual pages that aren't installed, you can type
``nroff -man page |more'' where page is the filename of the
unformatted manual page. The filename of an unformatted manual page
is the name of the manual page, followed by '.', followed by some
number - 5 for documentation about files, and 8 for documentation
about programs. For example, to read the dhcp-options man page,
you would type ``nroff -man common/dhcp-options.5 |more'', assuming
your current working directory is the top level directory of the ISC
DHCP Distribution.
If you do not have the nroff command, you can type ``more catpage''
where catpage is the filename of the catted man page. Catted man
pages names are the name of the manual page followed by ".cat"
followed by 5 or 8, as with unformatted manual pages.
Please note that until you install the manual pages, the pathnames of
files to which they refer will not be correct for your operating
system.
Release status
This is the final release of Version 2 of the Internet Software
Consortium DHCP Distribution. In version 2.0, this distribution
includes a DHCP server, a DHCP client, and a BOOTP/DHCP relay agent.
This release is stable.
In this release, the server and relay agent currently work well on
NetBSD, Linux after kernel version 2.0.30, FreeBSD, BSD/OS, Ultrix,
Digital Alpha OSF/1, Solaris and SunOS 4.1.4. On AIX, HPUX, IRIX and
Linux 2.0.30, only a single broadcast network interface is supported.
They also runs on QNX as long as only one broadcast network interface
is configured and a host route is added from that interface to the
255.255.255.255 broadcast address.
The DHCP client currently only knows how to configure the network on
NetBSD, FreeBSD, BSD/os, Linux, Solaris and NextStep. The client
depends on a system-dependent shell script to do network
configuration - support for other operating systems is simply a matter
of porting this shell script to the new platform.
If you wish to run the DHCP Distribution on Linux, please see the
Linux-specific notes later in this document. If you wish to run on an
SCO release, please see the SCO-specific notes later in this document.
You particularly need to read these notes if you intend to support
Windows 95 clients. If you are running a version of FreeBSD prior to
2.2, please read the note on FreeBSD. If you are running HP-UX or
Ultrix, please read the notes for those operating systems below.
If you are running NeXTSTEP, please see the notes on NeXTSTEP below.
If you start dhcpd and get a message, "no free bpf", that means you
need to configure the Berkeley Packet Filter into your operating
system kernel. On NetBSD, FreeBSD and BSD/os, type ``man bpf'' for
information. On Digital Unix, type ``man pfilt''.
Building the DHCP Distribution
To build the DHCP Distribution, unpack the compressed tar file using
the tar utility and the gzip command - type something like:
- zcat dhcp-2.0pl2.tar.gz |tar xvf -
+ zcat dhcp-2.0pl3.tar.gz |tar xvf -
On BSD/OS, you have to type gzcat, not zcat, and you may run into
similar problems on other operating systems.
-Now, cd to the dhcp-2.0pl2 subdirectory that you've just created and
+
Now, cd to the dhcp-2.0pl3 subdirectory that you've just created and
configure the source tree by typing:
./configure
If the configure utility can figure out what sort of system you're
running on, it will create a custom Makefile for you for that
system; otherwise, it will complain. If it can't figure out what
system you are using, that system is not supported - you are on
your own.
Once you've run configure, just type ``make'', and after a while
you should have a dhcp server. If you get compile errors on one
of the supported systems mentioned earlier, please let us know.
If you get warnings, it's not likely to be a problem - the DHCP
server compiles completely warning-free on as many architectures
as we can manage, but there are a few for which this is difficult.
If you get errors on a system not mentioned above, you will need
to do some programming or debugging on your own to get the DHCP
Distribution working.
Installing the dhcp distribution
Once you have successfully gotten the DHCP Distribution to build, you
can install it by typing ``make install''. If you already have an old
version of the DHCP Distribution installed, you may want to save it
before typing ``make install''.
Using the dhcp distribution
Linux
There are three big LINUX issues: the all-ones broadcast address,
Linux 2.1 ip_bootp_agent enabling, and operations with more than one
network interface. There are also two potential compilation/runtime
problems for Linux 2.1/2.2: the "SO_ATTACH_FILTER undeclared" problem
and the "protocol not configured" problem.
So_attach_filter undeclared
In addition, there is a minor issue that we will mention here because
this release is so close on the heels of the Linux 2.2 release: there
is a symlink in /usr/include that points at the linux asm headers. It
appears to be not uncommon that this link won't be updated correctly,
in which case you'll get the following error when you try to build:
lpf.c: In function `if_register_receive':
lpf.c:152: `SO_ATTACH_FILTER' undeclared (first use this function)
lpf.c:152: (Each undeclared identifier is reported only once
lpf.c:152: for each function it appears in.)
The line numbers may be different, of course. If you see this
header, your linux asm header link is probably bad, and you should
make sure it's pointing to correct linux source directory.
Protocol not configured
One additional Linux 2.1/2.2 issue: if you get the following message,
it's because your kernel doesn't have the linux packetfilter or raw
packet socket configured:
Make sure CONFIG_PACKET (Packet socket) and CONFIG_FILTER (Socket
Filtering) are enabled in your kernel configuration
If this happens, you need to configure your Linux kernel to support
Socket Filtering and the Packet socket. You can do this by typing
``make config'', ``make menuconfig'' or ``make xconfig'', and then
enabling the Packet socket and Socket Filtering options that you'll
see displayed on the menu or in the questionnaire. You can also edit
your linux kernel .config file directly: set CONFIG_FILTER=y and
CONFIG_PACKET=y. If you do this, make sure you run ``make oldconfig''
afterwards, so that the changes you've made are propogated to the
kernel header files. After you've reconfigured, you need to type
``make'' to build a new Linux kernel, and then install it in the
appropriate place (probably /linux). Make sure to save a copy of your
old /linux.
If the preceding paragraph made no sense to you, ask your Linux
vendor/guru for help - please don't ask us.
If you set CONFIG_PACKET=m or CONFIG_FILTER=m, then you must tell the
kernel module loader to load the appropriate modules. If this doesn't
make sense to you, don't use CONFIG_whatever=m - use CONFIG_whatever=y.
Don't ask for help with this on the DHCP mailing list - it's a Linux
kernel issue. This is probably not a problem with the most recent
Linux 2.2.x kernels.
Broadcast
In order for dhcpd to work correctly with picky DHCP clients (e.g.,
Windows 95), it must be able to send packets with an IP destination
address of 255.255.255.255. Unfortunately, Linux changes an IP
destination of 255.255.255.255 into the local subnet broadcast address
(here, that's 192.5.5.223). This isn't a problem on Linux 2.2 and
later kernels, since we completely bypass the Linux IP stack, but on
old versions of Linux 2.1 and all versions of Linux prior to 2.1, it
is a problem - pickier DHCP clients connected to the same network as
the ISC DHCP server or ISC relay agent will not see messages from the
DHCP server.
It is possible to work around this problem on some versions of Linux
by creating a host route from your network interface address to
255.255.255.255. The command you need to use to do this on Linux
varies from version to version. The easiest version is:
route add -host 255.255.255.255 dev eth0
On some older Linux systems, you will get an error if you try to do
this. On those systems, try adding the following entry to your
/etc/hosts file:
255.255.255.255 all-ones
Then, try:
route add -host all-ones dev eth0
Another route that has worked for some users is:
route add -net 255.255.255.0 dev eth0
If you are not using eth0 as your network interface, you should
specify the network interface you *are* using in your route command.
Firewall rules
If you are running the DHCP server or client on a Linux system that's
also acting as a firewall, you must be sure to allow DHCP packets
through the firewall - Linux firewalls make filtering decisions before
they make the forwarding decision, so they will filter packets that
are intended for the firewall itself, as well as packets intended to
be forwarded. In particular, your firewall rules _must_ allow
packets from IP address 0.0.0.0 to IP address 255.255.255.255 from UDP
port 68 to UDP port 67 through. They must also allow packets from
your local firewall's IP address and UDP port 67 through to any
address your DHCP server might serve on UDP port 68. Finally,
packets from relay agents on port 67 to the DHCP server on port 67,
and vice versa, must be permitted.
IP BOOTP agent
Some versions of the Linux 2.1 kernel apparently prevent dhcpd from
working unless you enable it by doing the following:
echo 1 >/proc/sys/net/ipv4/ip_bootp_agent
Multiple interfaces
Very old versions of the Linux kernel do not provide a networking API
that allows dhcpd to operate correctly if the system has more than one
broadcast network interface. However, Linux 2.0 kernels with version
numbers greater than or equal to 2.0.31 add an API feature: the
SO_BINDTODEVICE socket option. If SO_BINDTODEVICE is present, it is
possible for dhcpd to operate on Linux with more than one network
interface. In order to take advantage of this, you must be running a
2.0.31 or greater kernel, and you must have 2.0.31 or later system
headers installed *before* you build the DHCP Distribution.
We have heard reports that you must still add routes to 255.255.255.255
in order for the all-ones broadcast to work, even on 2.0.31 kernels.
In fact, you now need to add a route for each interface. Hopefully
the Linux kernel gurus will get this straight eventually.
Linux 2.1 and later kernels do not use SO_BINDTODEVICE or require the
broadcast address hack, but do support multiple interfaces, using the
Linux Packet Filter.
SCO
SCO has the same problem as Linux (described earlier). The thing is,
SCO *really* doesn't want to let you add a host route to the all-ones
broadcast address. One technique that has been successful on some
versions of SCO is the very bizarre command:
ifconfig net0 alias 10.1.1.1 netmask 8.0.0.0
Apparently this works because of an interaction between SCO's support
for network classes and the weird netmask. The 10.* network is just a
dummy that can generally be assumed to be safe. Don't ask why this
works. Just try it. If it works for you, great. If not, SCO is
supposedly adding hooks to support real DHCP service in a future
release - I have this on good authority from the people at SCO who do
*their* DHCP server and client.
HP-UX
HP-UX has the same problem with the all-ones broadcast address that
SCO and Linux have. One user reported that adding the following to
/etc/rc.config.d/netconf helped (you may have to modify this to suit
your local configuration):
INTERFACE_NAME[0]=lan0
IP_ADDRESS[0]=1.1.1.1
SUBNET_MASK[0]=255.255.255.0
BROADCAST_ADDRESS[0]="255.255.255.255"
LANCONFIG_ARGS[0]="ether"
DHCP_ENABLE[0]=0
Ultrix
Now that we have Ultrix packet filter support, the DHCP Distribution
on Ultrix should be pretty trouble-free. However, one thing you do
need to be aware of is that it now requires that the pfilt device be
configured into your kernel and present in /dev. If you type ``man
packetfilter'', you will get some information on how to configure your
kernel for the packet filter (if it isn't already) and how to make an
entry for it in /dev.
FreeBSD
Versions of FreeBSD prior to 2.2 have a bug in BPF support in that the
ethernet driver swaps the ethertype field in the ethernet header
downstream from BPF, which corrupts the output packet. If you are
running a version of FreeBSD prior to 2.2, and you find that dhcpd
can't communicate with its clients, you should #define BROKEN_FREEBSD_BPF
in site.h and recompile.
NeXTStep
The NeXTSTEP support uses the NeXTSTEP Berkeley Packet Filter
extension, which is not included in the base NextStep system. You
must install this extension in order to get dhcpd or dhclient to work.
Solaris
One problem which has been observed and is not fixed in this
patchlevel has to do with using DLPI on Solaris machines. The symptom
of this problem is that the DHCP server never receives any requests.
If you are using Solaris 2.6, and you encounter this symptom, and
you are running the DHCP server on a machine with a single broadcast
network interface, you may wish to edit the includes/site.h file and
uncomment the #define USE_SOCKETS line. Then type ``make clean;
make''.
The DHCP client on Solaris will only work with DLPI. If you run it
and it just keeps saying it's sending DHCPREQUEST packets, but never
gets a response, you may be having DLPI trouble as described above.
If so, you are SOL. Also, because Solaris requires you to "plumb" an
interface before it can be detected by the DHCP client, you must
either specify the name(s) of the interface(s) you want to configure
on the command line, or must plumb the interfaces prior to invoking
the DHCP client. This can be done with ``ifconfig iface plumb'',
where iface is the name of the interface (e.g., ``ifconfig hme0
plumb'').
It should be noted that Solaris versions from 2.6 onward include a
DHCP client that you can run with ``/sbin/ifconfig iface dhcp start''
rather than using the ISC DHCP client. The feature set of the Solaris
client is different (not necessarily better or worse) than that of the
ISC client, but in most cases it will be a lot easier for you to just
use that. Please do not ask for help in using the Solaris DHCP client
on Internet Software Consortium mailing lists - that's why you're
paying Sun the big bucks. If you're having a problem with the
Solaris client interoperating with the ISC dhcp server, that's another
matter, but please check with Sun first.
Support
The Internet Software Consortium DHCP server is not a commercial
product, and is not supported in that sense. However, it has
attracted a fairly sizable following on the Internet, which means that
there are a lot of knowledgable users who may be able to help you if
you get stuck. These people generally read the dhcp-server@fugue.com
mailing list.
If you are going to use dhcpd, you should probably subscribe to the
dhcp-server and dhcp-announce mailing lists. If you will be using
dhclient, you should subscribe to the dhcp-client mailing list.
If you need help, you should ask on the dhcp-server or dhcp-client
mailing list (or both) - whichever is appropriate to your
application. This includes reporting bugs. Please do not report
bugs in old software releases - fetch the latest release and see if
the bug is still in that copy of the software, and if it's not, _then_
report it. It's okay to report bugs in the latest patchlevel of a
major version that's not the most recent major version, though - for
example, if you're running 2.0, you don't have to upgrade to 3.0
before you can report bugs.
Please read this readme file carefully before reporting bugs!
How to report bugs
When you report bugs, please provide us complete information. A list
of information we need follows. Please read it carefully, and put
all the information you can into your initial bug report, so that we
don't have to ask you any questions in order to figure out your
problem.
Please, do not send queries about non-isc clients
to the dhcp-client mailing list. If you're asking about them on an
ISC mailing list, it's probably because you're using the ISC DHCP
server, so ask there. If you are having problems with a client whose
executable is called dhcpcd, this is not the ISC DHCP client,
and we probably can't help you with it.
Please see
http://www.fugue.com/dhcp/lists for details on how to subscribe.
If you don't have WorldWide Web access, you can send mail to
dhcp-request@fugue.com and tell me which lists you want to subscribe
to, but please use the web interface if you can, since I have to
handle the -request mailing list manually, and I will give you the
third degree if you make me do your subscription manually.
Please do not send requests for help directly to the author!
The number of people using the DHCP Distribution is sufficiently large
that if we take an interrupt every time any one of those people runs into
trouble, we will never get any more coding done.
Please do not call the author on the phone for support!
Answering the phone takes a lot more time and attention than answering
email. If you do call on the phone, you will be told to send email to
the mailing list, so there's no point in doing it.
Exception: if you are a support customer, you already know
how to get in touch with us. To become a support customer, see our
Support web
page.
Known bugs
This release of the DHCP Distribution does not yet contain support for
DHCPINFORM. The Vendor Specific Data option is not supported. Site-
specific options are not supported. All of these are supported in the
3.0 release of the DHCP distribution, which is now in beta testing.
diff --git a/contrib/isc-dhcp/RELNOTES b/contrib/isc-dhcp/RELNOTES
index 7e7a5c3c81ee..60d2958de6ec 100644
--- a/contrib/isc-dhcp/RELNOTES
+++ b/contrib/isc-dhcp/RELNOTES
@@ -1,747 +1,754 @@
Internet Software Consortium
Dynamic Host Configuration Protocol Distribution
- Version 2 Patchlevel 2
- June 30, 2000
+ Version 2 Patchlevel 3
+ July 19, 2000
Release Notes
Version 2 of the ISC DHCP Distribution includes the ISC DHCP server,
DHCP Client and DHCP/BOOTP Relay Agent.
This version has been in a near feature freeze since January of 1998,
was in Beta test from that time to June of 1999, and has now been
released in its final form. It has a number of important features,
and is the release that we would expect most sites to run.
For information on how to install, configure and run this software,
as well as how to find documentation and report bugs, please consult
the README file.
CHANGELOG
This log describes the changes that have been made in version 2.0
since June of 1997.
+ CHANGES FROM VERSION 2.0 PATCHLEVEL 2
+
+- Rather than calling a client environment setup script, set the
+ environment up directly, so as to avoid any possible exploit making
+ use of clever shell metacharacter hacks. This is a security fix
+ that applies to the DHCP client *only*.
+
CHANGES FROM VERSION 2.0 PATCHLEVEL 1
- Fix a case where an unitialized pointer could result from an exceptional
case in DHCPRELEASE and cause a core dump.
CHANGES FROM VERSION 2.0
- Clean up DHCPRELEASE support.
- Don't use the broadcast flag when doing BOOTP unless we need to.
- Clean up the fallback mess.
- Quote all shell special characters in the client script.
- Fix ethernet header alignment on arm32.
- Clarify the "no subnet declaration" message.
- Correctly store the tftp server name in the lease file and the
client script file.
- Avoid a potential spin loop in client when script file creation
fails for reasons other than the presence of an existing file of the
same name.
- Add support for Linux kernel versions greater than 2.2.
- Fix a problem in raw.c on Irix. Thanks to Don Badrak for the
patch.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 29
- Define BYTE_ORDER in includes/cf/hpux.h so that ip.h will compile
correctly on HP-UX.
- Fix a long-standing but minor bug in the way the program name for
syslog was derived.
- Fix a long-standing bug that prevented the DHCP server from broadcasting
responses to BOOTP clients that requested a broadcast response.
- In dhcprequest(), check to make sure that there's a lease before trying
to acknowledge it to the client. This fixes a potential core dump that
a few people observed.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 28
- Fix some pastos I introduced when merging Andrew Chittenden's token
ring support.
- Apply a patch to the token ring support from Andrew Chittenden.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 27
- Add dependencies to makefiles.
- Don't use ping -w 1 in freebsd client script.
- Token ring support for LPF, contributed by Andrew Chittenden.
- Fix a subtle bug that would cause the server to respond incorrectly
in some cases when the client sent duplicate DHCPREQUEST packets.
- Fix option pretty printing for 'X' format.
- Add some special cases to deal with DHCPREQUEST packets from RFC1541
clients.
- Fix an obscure bug in nested subnet mask handling.
- Fix a bug in abandoned lease reclamation.
- Allow maximum message size to be set in configuration file.
- Allow parameter request list to be supplied in configuration file.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 26
- Fix UDP/IP checksum code
- Fix UDP payload length computation to prevent logging of spurious
errors.
- Support compilation on MacOS X
- Add support for some options that were added in RFC2132.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 25
- Use the udp header's length rather than computing the length based
on the number of bytes received, because some broken relay agents
send packets with ip lengths that are longer than then sum of the ip
header size and the udp length.
- Do path keyword substitution on unformatted manual pages before
installing them.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 24
- D'oh! Fix a really stupid mistake in hash.c.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 23
- Support an always-reply-rfc1048 flag, which says to reply with an
RFC1048-style vendor extensions buffer even if the client didn't
send an RFC1048-style magic number.
- Fix a null pointer dereference.
- Use netmask from subnet if no netmask option specified.
- IRIX support (thanks to Don Badrak).
- Install unformatted manual pages on Linux.
- Add note in README about zcat vs. gzcat on BSD/os.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 22
- Test for lease before dereferencing it in dhcprequest.
- Free the client parameter request list in dhcpnak if there is one.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 21
- Fix a pasto in options.c that will cause a core dump whenever a
client sends in a request without a parameter request list.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 20
- Actually do the client fix mentioned below - Patchlevel 20 only contained
half of the fix.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 19
- Removed arp table clearing code from solaris client script.
- Document Linux "protocol not configured" error more thoroughly.
- Clean up some unused variables.
- Add entry and exit hooks to all dhcp client scripts, along with a
make_resolv_conf function that can be redefined in the entry hooks.
Document this new feature set.
- Fix client to take advantage of network APIs that allow it to
receive a unicast instead of requesting that the DHCP server
broadcast its response.
- Add -pf flag to all daemons allowing user to specify PID file name
on command line.
- Undo a previous change that attempted to be clever about testing
interface flags but wound up being stupid instead.
- Enforce access control on DHCPREQUEST messages as well as
DHCPDISCOVER messages.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 18
- Support added for AIX 4.1.5.0 (and hopefully other versions).
- Use /var/run instead of /etc on Digital Unix.
- Change DHCP client exponential backoff code to back off more slowly,
so that it is more robust in lossy environments, at the expense of
being a bit less polite to the server.
- Don't request a specific lease interval in the client unless the
user says to do so.
- Don't print DHCPXXX in wrong xxx messages unless DEBUG is defined.
- Fix handling of secs field.
- Fix handling of append statement.
- Fix documentation for append and prepend statements.
- Fix server support for parameter request list and maximum message
size.
- Parameterize more hardware types in discover_interfaces. Check for
IFF_BROADCAST instead of !IFF_POINTOPOINT
- Print kernel configuration warning message if we get EINVAL when
opening or configuring the Linux packet filter.
- Fix a bug in UDP checksum code (thanks to John Nemeth for figuring
this out) and re-enable UDP checksumming. This allows the client
to work with some buggy DHCP servers that can't handle zero
checksums in the UDP header - in particular, the one John's cable
modem ISP is using.
- Don't report packet header checksum errors unless we see a lot of
them. It's perfectly normal for some number of checksum errors to
occur.
- Refer to the dhcpd.leases man page when printing an error message
prior to exiting because there's no lease database.
- Add information to the README telling the reader how to get to the
manual pages.
- Fix the server packet transmission code to unicast when it can.
- Fix a typo in the dhcpd.conf manual page.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 17
- Fix a bug in the relay agent where messages to the client would be
unicast in the IP header but broadcast in the link header. The
Microsoft DHCP client would reject such packets, preventing it from
being configured. This was only a problem on non-socket-API
platforms.
- Do not attempt to reclaim requested abandoned leases in response to
DHCPDISCOVER messages.
- Allow the maximum lease time parameter in a host declaration to
override the maximum lease time parameter in a subnet declaration.
- Better document the -p flag for dhclient, dhcrelay and dhcpd.
- Apply John Wehle's patch to fix the endianness bug in the dlpi
packet filter on Solaris.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 16
- Fix linux man page install location.
- Fix some confusion in the dhclient-script man page.
- Fix error in includes/cf/linux.h that would have made network API
selections in site.h work incorrectly.
- Fix some major stupidity in the code that figures out where or not a
client owns a particular lease.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 15
- Fix Makefile.conf on Linux to refer to /var/state/dhcp instead of
/var/state/dhcpd.
- Eliminate redundant #defines in includes/cf/linux.h (for neatness).
- Fix an obscure case where dhcpd is started by the /etc/rc system
with exactly the same pid each time, dhcpd.pid is not erased on
reboot, and therefore dhcpd would detect a server (itself) with the
pid in dhcpd.pid and decide that another server was running and
exit.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 14
- Install the dhcp databases in /var/state/dhcp instead of /etc or
/var/dhcpd, as suggested in the Linux Filesystem Hierarchy
Standard.
- Fix an endianness bug in dlpi.c. As a consequence, make the
Solaris/i386 use dlpi again.
- Fix a bunch of bugs in the Solaris client script.
- Add some more information about Solaris to the README file.
- Adjust startup message in interface probe so that the relay agent
and client's unattached status will not trigger questions.
- Update some error messages to provide more help to new users for
some common mistakes.
- Create an interface alias on Solaris when setting up IP aliases,
rather than trying to do things the *BSD way.
- Fix a null pointer dereference bug (this time I went through the
whole function and audited it for more null pointer dereferences,
and I didn't find any, for what that's worth).
- Don't ever release leases in response to a DHCPDISCOVER (I think
this was unlikely anyway, but why not be correct?).
- Remove the shared-network example from the sample dhcpd.conf file.
- Make ``make install'' make all first.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 13
- Support DESTDIR on installs.
- Fix a bug in dhcp.c where a store through a null pointer would
be made under some reasonably common circumstances.
- Add test for ARPHRD_TUNNEL so that client and server do not fail on
versions of Linux running IPsec implementations or the like.
- Move tests for constants defined in O.S. headers into osdep.h - test
for HAVE_whatever in .c files. Define relevant HAVE_whatevers in
linux.h, so that versions of linux that define these constants as
enums will still work.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 12
- Initialize the "quiet" variable in dhclient.c to zero (it was used
without first having been initialized).
- Fix the parser code for the authoritative keyword.
- Adjust lease discovery code to NAK more aggressively for addresses
the server knows it owns.
- Add several new messages for DHCPNAK.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 11
- Use DLPI only on sparcs running Solaris, since it seems not to work
on i386 boxes running Solaris for reasons yet to be determined.
- In the client, close standard I/O descriptors when forking a daemon.
- Don't let large lease lengths wrap lease expiry times - just use
what fits into a TIME value.
- Fix a bug in the SIOCGIFCONF interface scanning code.
- Fix a core dump in the interface scanner that crops up on Linux when
an interface is specified on the command line.
- Don't use %D in strftime because egcs complains about it.
- Print the error message if SO_BINDTODEVICE fails.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 10
- Update top-level Makefile so that it exits correctly on errors in
submakes under bash/gnu make (dunno which is the culprit, and don't
really care).
- Print a more helpful message if no free BPF devices are found.
- Add support for specifying that the server is or is not
authoritative for a particular network segment.
- Fix two stupid typos in lpf.c.
- Print a more helpful message if we can't create an LPF socket or
can't attach a filter to it.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 9
- Correct the hopelessly outdated information about Linux at the top
of the README - many apologies to the Linux people who have had to
read that nonsense for the past couple of snapshots and have been
confused or annoyed by it. I simply hadn't read it recently, and
didn't realize how out-of-date it was.
- Print a message if the client finds no broadcast interfaces to
configure.
- Add support for use-lease-addr-for-default-route flag in server, so
that Windows machines can be made to ARP for all addresses.
- Update README file to mention new Linux gotchas.
- After finally understanding Brian Murrel's code (my fault, not his)
to get interface names from /proc/net/dev on Linux, fix what I broke
of his code and document it.
- Use sendto rather than send for SOCK_PACKET sockets, because they
can't be connected, only bound. :'(
- Fix up SOCK_PACKET creation so that the kernel doesn't complain
about it.
- Fix incorrect tests in linux client script:
[ $relmajor == 2 ] -> [ $relmajor -eq 2 ]
- Make typedefs for u8, u16 and u32 types. These are Linux kernel
internal data types which are unfortunately exposed in the linux
packetfilter header file.
- Don't include in lpf.c - it defines things we're
already correctly defining elsewhere, and doesn't define any useful
new stuff.
- Finally fix client PREINIT bug that causes interfaces not specified
on the command line to be preinitialized. If no interfaces are
specified on the command line, all interfaces are still
preinitialized.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 8
- Fix socket API fallback setup code, which was causing Linux servers
and clients to loop endlessly on select when run as daemons.
- Add support for Linux 2.2 version number (treated the same as Linux
2.1, for now).
- Correct apparent error in DHCPREQUEST destination address handling
when in INIT-REBOOT state.
- Do not set BROADCAST flag if we have a valid IP address.
- Remove hard-coded filenames and use system-specific manifest
constants.
- Add entry and exit hooks to Linux dhclient-script (should be added
to all operating systems once tested).
- Test for linux major and minor version so as to correctly invoke
network configuration programs.
- Add support for Linux's gratuitous name change of bpf_insn structure
(can't pollute precious Linux sources with the "Berkeley" word, I
guess.
- Correct USE_BPF_{SEND,RECEIVE} ifdefs for if_reinitialize_*
functions.
- Ensure that we have ifreq structure before initializing interface -
if an interface was specified on the command line on Linux, this was
not the case.
- Get rid of references to enstamp structure in lpf.c. Correctly
declare and initialize sock_fprog structure (aka bpf_filter
structure on non-Linux machines).
- Define ssize_t on Ultrix.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 7
- Generalize FDDI support.
- Fix potential core dump in interface discovery code.
- Put explicit release versions on startup messages.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 6
- Add support for Linux Packet Filter (thanks to Brian Murrell, Interlinx).
- Add support for FDDI hardware type.
- Fix a long-standing bug in DLPI support where the ethertype was
being set incorrectly (thanks to Gong Wei, CCENet).
- Don't use DLPI RAW mode on Solaris.
- In the client, when a lease expires, the interface to which that
lease is attached is unconfigured. On systems that use the socket
API, the interface needs to then be reconfigured with the 0.0.0.0
address so that it can be used to get a new address.
- Add fallback support for Linux. This fixes a problem with the
relay agent when relaying over non-broadcast links, and may also fix
some obscure problems with unicasting DHCPACKs in both the server and
relay agent.
- When allocating leases, if the oldest lease is abandoned, try to
find a younger-but-still-expired lease rather than reclaiming the
abandoned lease.
- Add more documentation to README.
- The absence of the /etc/dhclient.conf file is no longer considered
an error.
- The dhcp client's lease file name can be specified on the command
line.
- The DHCP client should no longer zap interfaces that it has not been
directed to configure.
- If a client starts up in the init-reboot state, the xid will be a
"random" number rather than always being zero, as was previously the
case.
- In addition to comparing transaction IDs, compare hardware addresses
in response packets to verify that they are ours.
- Rewrite the client lease database after 20 leases have been written.
- Fix the exponential backoff code.
- Add a Y2k comment to indicate that something suspicious-looking is
in fact _not_ a problem.
- Use mkstemp if possible.
- Add missing fi in various client scripts.
- Use "search" instead of "domain" in linux resolv.conf files.
- Specify a hop count in all route command on solaris.
- If an allocation fails, don't try to zero out the allocation buffer
we didn't get.
- Support subnets that are subsets of other subnets - that is, for
example, 10.0.1.0/24 and 10.0.0.0/16. This is useful in fairly
obscure circumstances.
- Don't set the lease end time if it's already expired.
- Don't define INADDR_LOOPBACK on FreeBSD if it's already defined in a
system header.
- Use the broadcast address in the relay agent if we are using the BSD
socket API.
- Allow host declarations without names.
- Allow the server identifier option to be specified.
- Don't dump hostnames into the lease file if they contain
non-printable characters.
- Copy the entire client hardware address buffer that the client sends
to the output packet, not just the portion of it that's supposedly
significant according to the hardware address length field. This
is done for the benefit of certain Microsoft clients.
- Don't send a second ICMP echo request if we receive two DHCPDISCOVER
messages in quick succession. This prevents a rather annoying
timing race in configuring some Win95 clients.
- Fix up dhcp-options man page to make it more readable. Note that
netbios-name-server is the same thing as WINS.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 5
- Define some extra DLPI support flags that make DLPI work much better
on Solaris.
- Fix inet_aton prototype/declaration to match Internet Software
Consortium BIND distribution.
- Document new server-identifier functionality.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 4
- Do not use -Wstrict-prototypes on Solaris with gcc - if the Internet
Software Consortium BIND distribution is not installed, this produces
errors.
- Actually use the new DLPI support on Solaris - although the code was
added in Patchlevel 2, it wasn't enabled (blush).
- Fix a prototype bug that's exposed when DLPI support is enabled on
Solaris.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 3
- Fix a makefile botch that prevents the DHCP Distribution from
from compiling on Solaris with gcc. Sigh.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 2
- Allow server-identifier in any scope. Use in-scope server
identifier option rather than the default, if one exists.
- Delete newlines from abandoned lease reclaimation warning.
- Only release other applicable leases held by a client when the
client sends a DHCPREQUEST.
- Fix core dump when find_lease didn't find a lease.
- Update dhcpd.leases man page.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 1
- Use -Wno-char-subscript on Solaris to prevent bogus warnings from
gcc on Solaris 2.6.
- Add support for Apple's new Rhapsody operating system.
- Use DLPI on Solaris instead of using the BSD Sockets API.
- Fix two network input buffer overflow problems which could allow an
attacker to pervert the stack.
- Fix an ancient typo that could theoretically cause memory
corruption.
- Sort abandoned leases in at current time rather than end of time.
This allows abandoned leases to be reclaimed if there are no
available free leases.
- If a client explicitly requests a lease that's been abandoned, it's
probably the system that was answering pings on that address, so let it
have the lease.
- Fix a bunch of type conversion errors that are flagged by the Solaris
C compiler.
CHANGES FROM VERSION 2.0 BETA 1 PATCHLEVEL 0
- Fix two potential buffer overflow problems.
- Differentiate between versions of Linux for better success in
compiling.
- Fix bug in linux client script regarding routing setup.
- Clarify socket API error message on multiple interfaces.
- Fix broken comparison that was setting IP source address to zero.
- Reclaim abandoned leases if we run out of free leases.
CHANGES FROM THE DECEMBER 2, 1997 SNAPSHOT
- Use %ld to print pid_t and cast pid_t values to long to avoid
inconsistent declarations between different POSIX flavours.
- Add support for ARPHRD_IEEE802 (token ring) hardware type.
- If we own an address and a client requests it, but we can't assign
it to that client, we now NAK it so that the client doesn't try to
reuse it.
CHANGES FROM THE JUNE SNAPSHOT
- Support for NeXTstep 3.x and 4.x
- Added man pages for dhcpd.leases, dhclient-script, dhclient.leases
and dhclient.conf. Move general documentation of DHCP options into
a seperate man page which is referred to by the dhclient.conf and
dhcpd.conf man pages.
- Updated README to answer some frequently asked questions.
- Fixed a bug in command-line interface specification in dhclient - it
was formerly not possible to specify that only certain interfaces be
configured.
- Do not leave client scripts lying around in /tmp after they've been
used unless the -D flag is specified.
- Add a new, non-standard, not-guaranteed-to-stay-the-same system
configuration status message server which can be used to trigger the
client to recheck its address, e.g., after a laptop has been put to
sleep and then awakened (this has yet to be documented).
- Fix handling of media selection in the REBOOT phase - previously the
media type would not be remembered, which could cause severe delays
in reacquiring an address if the default media type was wrong.
- Allocate space for a NUL terminator on the end of client options -
this was previously overlooked, and could cause garbage characters
to be written to the temporary client script files.
- Use mkstemp if it's available.
- Supply network number and broadcast address to the client script so
that on systems that need these values, they don't need to be
computed with an awk script.
- Keep a PID file for the client and the relay agent, and have the
relay agent background itself by default.
- Add client script for bsd/os, fix many niggling bugs in existing
client scripts and add support for static routing tables to all bsd
scripts.
- Add a -q option to the client, server and relay agent so that they
can be started from /etc/rc scripts without spewing a bunch of
garbage on the console. By default, all three daemons still print
startup messages, since these are helpful in bug reporting.
- Don't print anything to stderr or stdout after going into
background.
- Fix bug where hostname keyword was not being recognized in
dhcpd.leases file, resulting in the loss of lease database entries.
- Fix problem on some operating systems where zero-length ifreq
structures were being offset incorrectly when scanning the interface
list on startup.
- Unless a BOOTP client requests it, never send more than 64 bytes of
options.
- Don't ping static leases, since we don't have a lease structure on
the heap to work with later.
- Fixed a compile problem on Solaris 2.6.
- Support interface aliases on Solaris.
- Print day and month with leading zero in lease files if less than
ten, for easier parsing by perl/sed/awk scripts.
- Never make the lease database world writable, even if dhcpd is
invoked with a bogus umask.
- Fix DHCPRELEASE handling (before, addressed would never be
released.)
- If there is more than one lease for a particular client on a
particular network, find the lease the client is asking for so as to
avoid a cycle of NAKs.
- If a BOOTP request is received from a particular client and that
client has previously received a DHCP address, make sure that we
still find a valid BOOTP lease so that we don't cycle through
addresses.
- Remove server-identifier option from documentation, other than to
document that it has been deprecated.
- Don't give up if we get an EINTR or EAGAIN while polling or
selecting - these return statuses can occur spuriously without
indicating a fatal problem.
- Do not select for exceptions, since we don't handle them. This was
causing massive CPU consumption on some systems.
- When a DHCP client has been assigned a fixed address but had
previously had a lease, it will request the old leased address. In
such an event, send a DHCPNAK so that it will discover its new
static binding.
diff --git a/contrib/isc-dhcp/client/Makefile.dist b/contrib/isc-dhcp/client/Makefile.dist
index 2109d3977c83..2b261a7e4610 100644
--- a/contrib/isc-dhcp/client/Makefile.dist
+++ b/contrib/isc-dhcp/client/Makefile.dist
@@ -1,136 +1,137 @@
# Makefile.dist
#
# Copyright (c) 1996, 1997, 1999 The Internet Software Consortium.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of The Internet Software Consortium nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE INTERNET SOFTWARE CONSORTIUM OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# OF THE POSSIBILITY OF SUCH DAMAGE.
#
CATMANPAGES = dhclient.cat8 dhclient.conf.cat5 dhclient-script.cat8 \
dhclient.leases.cat5
SEDMANPAGES = dhclient.man8 dhclient.conf.man5 dhclient-script.man8 \
dhclient.leases.man5
SRCS = dhclient.c clparse.c
OBJS = dhclient.o clparse.o
PROG = dhclient
MAN = dhclient.8 dhclient.conf.5 dhclient-script.8 dhclient.leases.5
DEBUG = -g
INCLUDES = -I.. -I../includes
DHCPLIB = ../common/libdhcp.a
-CFLAGS = $(DEBUG) $(PREDEFINES) $(INCLUDES) $(COPTS)
+CFLAGS = $(DEBUG) $(PREDEFINES) $(INCLUDES) $(COPTS) \
+ -DCLIENT_PATH=${CLIENT_PATH}
all: $(PROG) $(CATMANPAGES)
install: all
for dir in $(CLIENTBINDIR) $(ETC) $(FFMANDIR) $(ADMMANDIR) $(VARDB); \
do \
foo=""; \
for bar in `echo $(DESTDIR)$${dir} |tr / ' '`; do \
foo=$${foo}/$$bar; \
if [ ! -d $$foo ]; then \
mkdir $$foo; \
chmod 755 $$foo; \
fi; \
done; \
done
$(INSTALL) dhclient $(DESTDIR)$(CLIENTBINDIR)
$(CHMOD) 755 $(DESTDIR)$(CLIENTBINDIR)/dhclient
if [ x$(SCRIPT) = xnone ]; then \
echo "No client script available."; \
else \
$(INSTALL) scripts/$(SCRIPT) $(DESTDIR)$(ETC)/dhclient-script; \
$(CHMOD) 700 $(DESTDIR)$(ETC)/dhclient-script; \
fi
$(MANINSTALL) $(MANFROM) dhclient.$(MANCAT)8 $(MANTO) \
$(DESTDIR)$(ADMMANDIR)/dhclient$(ADMMANEXT)
$(MANINSTALL) $(MANFROM) dhclient-script.$(MANCAT)8 $(MANTO) \
$(DESTDIR)$(ADMMANDIR)/dhclient-script$(ADMMANEXT)
$(MANINSTALL) $(MANFROM) dhclient.conf.$(MANCAT)5 $(MANTO) \
$(DESTDIR)$(FFMANDIR)/dhclient.conf$(FFMANEXT)
$(MANINSTALL) $(MANFROM) dhclient.leases.$(MANCAT)5 $(MANTO) \
$(DESTDIR)$(FFMANDIR)/dhclient.leases$(FFMANEXT)
clean:
-rm -f $(OBJS)
realclean: clean
-rm -f $(PROG) $(CATMANPAGES) $(SEDMANPAGES) *~ #*
distclean: realclean
-rm -f Makefile
# These should only be done on 4.4 BSD-based systems, since the mandoc
# macros aren't available on older unices. Catted man pages are
# provided in the distribution so that this doesn't become a problem.
dhclient.cat8: dhclient.man8
nroff -man dhclient.man8 >dhclient.cat8
dhclient.man8: dhclient.8
sed -e "s#ETCDIR#$(ETC)#g" -e "s#DBDIR#$(VARDB)#g" \
-e "s#RUNDIR#$(VARRUN)#g" < dhclient.8 >dhclient.man8
dhclient-script.cat8: dhclient-script.man8
nroff -man dhclient-script.man8 >dhclient-script.cat8
dhclient-script.man8: dhclient-script.8
sed -e "s#ETCDIR#$(ETC)#g" -e "s#DBDIR#$(VARDB)#g" \
-e "s#RUNDIR#$(VARRUN)#g" < dhclient-script.8 \
>dhclient-script.man8
dhclient.conf.man5: dhclient.conf.5
sed -e "s#ETCDIR#$(ETC)#g" -e "s#DBDIR#$(VARDB)#g" \
-e "s#RUNDIR#$(VARRUN)#g" < dhclient.conf.5 \
>dhclient.conf.man5
dhclient.conf.cat5: dhclient.conf.man5
nroff -man dhclient.conf.man5 >dhclient.conf.cat5
dhclient.leases.man5: dhclient.leases.5
sed -e "s#ETCDIR#$(ETC)#g" -e "s#DBDIR#$(VARDB)#g" \
-e "s#RUNDIR#$(VARRUN)#g" < dhclient.leases.5 \
>dhclient.leases.man5
dhclient.leases.cat5: dhclient.leases.man5
nroff -man dhclient.leases.man5 >dhclient.leases.cat5
dhclient: $(OBJS) $(DHCPLIB)
$(CC) $(LFLAGS) -o $(PROG) $(OBJS) $(DHCPLIB) $(LIBS)
# Dependencies (semi-automatically-generated)
dhclient.o: dhclient.c ../includes/dhcpd.h \
../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
../includes/cf/netbsd.h ../includes/dhcp.h \
../includes/tree.h ../includes/hash.h ../includes/inet.h \
../includes/sysconf.h ../includes/version.h
clparse.o: clparse.c ../includes/dhcpd.h \
../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
../includes/cf/netbsd.h ../includes/dhcp.h \
../includes/tree.h ../includes/hash.h ../includes/inet.h \
../includes/sysconf.h ../includes/dhctoken.h
diff --git a/contrib/isc-dhcp/client/clparse.c b/contrib/isc-dhcp/client/clparse.c
index 6c12230b171d..b08e0b60c4ab 100644
--- a/contrib/isc-dhcp/client/clparse.c
+++ b/contrib/isc-dhcp/client/clparse.c
@@ -1,1009 +1,1012 @@
/* clparse.c
Parser for dhclient config and lease files... */
/*
* Copyright (c) 1997 The Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of The Internet Software Consortium nor the names
* of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
* by Ted Lemon in cooperation with Vixie
* Enterprises. To learn more about the Internet Software Consortium,
* see ``http://www.vix.com/isc''. To learn more about Vixie
* Enterprises, see ``http://www.vix.com''.
*/
#ifndef lint
static char copyright[] =
-"$Id: clparse.c,v 1.13.2.4 1999/03/29 21:21:37 mellon Exp $ Copyright (c) 1997 The Internet Software Consortium. All rights reserved.\n";
+"$Id: clparse.c,v 1.13.2.5 2000/07/20 05:06:40 mellon Exp $ Copyright (c) 1997 The Internet Software Consortium. All rights reserved.\n"
+"$FreeBSD$\n";
#endif /* not lint */
#include "dhcpd.h"
#include "dhctoken.h"
struct client_config top_level_config;
+char client_script_name [] = "/sbin/dhclient-script";
+
/* client-conf-file :== client-declarations EOF
client-declarations :==
| client-declaration
| client-declarations client-declaration */
int read_client_conf ()
{
FILE *cfile;
char *val;
int token;
struct client_config *config;
struct interface_info *ip;
new_parse (path_dhclient_conf);
/* Set up the initial dhcp option universe. */
initialize_universes ();
/* Initialize the top level client configuration. */
memset (&top_level_config, 0, sizeof top_level_config);
/* Set some defaults... */
top_level_config.timeout = 60;
top_level_config.select_interval = 0;
top_level_config.reboot_timeout = 10;
top_level_config.retry_interval = 300;
top_level_config.backoff_cutoff = 15;
top_level_config.initial_interval = 3;
top_level_config.bootp_policy = ACCEPT;
- top_level_config.script_name = "/sbin/dhclient-script";
+ top_level_config.script_name = client_script_name;
top_level_config.requested_options
[top_level_config.requested_option_count++] =
DHO_SUBNET_MASK;
top_level_config.requested_options
[top_level_config.requested_option_count++] =
DHO_BROADCAST_ADDRESS;
top_level_config.requested_options
[top_level_config.requested_option_count++] =
DHO_TIME_OFFSET;
top_level_config.requested_options
[top_level_config.requested_option_count++] =
DHO_ROUTERS;
top_level_config.requested_options
[top_level_config.requested_option_count++] =
DHO_DOMAIN_NAME;
top_level_config.requested_options
[top_level_config.requested_option_count++] =
DHO_DOMAIN_NAME_SERVERS;
top_level_config.requested_options
[top_level_config.requested_option_count++] =
DHO_HOST_NAME;
if ((cfile = fopen (path_dhclient_conf, "r")) != NULL) {
do {
token = peek_token (&val, cfile);
if (token == EOF)
break;
parse_client_statement (cfile,
(struct interface_info *)0,
&top_level_config);
} while (1);
token = next_token (&val, cfile); /* Clear the peek buffer */
fclose (cfile);
}
/* Set up state and config structures for clients that don't
have per-interface configuration declarations. */
config = (struct client_config *)0;
for (ip = interfaces; ip; ip = ip -> next) {
if (!ip -> client) {
ip -> client = (struct client_state *)
malloc (sizeof (struct client_state));
if (!ip -> client)
error ("no memory for client state.");
memset (ip -> client, 0, sizeof *(ip -> client));
}
if (!ip -> client -> config) {
if (!config) {
config = (struct client_config *)
malloc (sizeof (struct client_config));
if (!config)
error ("no memory for client config.");
memcpy (config, &top_level_config,
sizeof top_level_config);
}
ip -> client -> config = config;
}
}
return !warnings_occurred;
}
/* lease-file :== client-lease-statements EOF
client-lease-statements :==
| client-lease-statements LEASE client-lease-statement */
void read_client_leases ()
{
FILE *cfile;
char *val;
int token;
new_parse (path_dhclient_db);
/* Open the lease file. If we can't open it, just return -
we can safely trust the server to remember our state. */
if ((cfile = fopen (path_dhclient_db, "r")) == NULL)
return;
do {
token = next_token (&val, cfile);
if (token == EOF)
break;
if (token != LEASE) {
warn ("Corrupt lease file - possible data loss!");
skip_to_semi (cfile);
break;
} else
parse_client_lease_statement (cfile, 0);
} while (1);
}
/* client-declaration :==
SEND option-decl |
DEFAULT option-decl |
SUPERSEDE option-decl |
PREPEND option-decl |
APPEND option-decl |
hardware-declaration |
REQUEST option-list |
REQUIRE option-list |
TIMEOUT number |
RETRY number |
REBOOT number |
SELECT_TIMEOUT number |
SCRIPT string |
interface-declaration |
LEASE client-lease-statement |
ALIAS client-lease-statement */
void parse_client_statement (cfile, ip, config)
FILE *cfile;
struct interface_info *ip;
struct client_config *config;
{
int token;
char *val;
struct option *option;
switch (next_token (&val, cfile)) {
case SEND:
parse_option_decl (cfile, &config -> send_options [0]);
return;
case DEFAULT:
option = parse_option_decl (cfile, &config -> defaults [0]);
if (option)
config -> default_actions [option -> code] =
ACTION_DEFAULT;
return;
case SUPERSEDE:
option = parse_option_decl (cfile, &config -> defaults [0]);
if (option)
config -> default_actions [option -> code] =
ACTION_SUPERSEDE;
return;
case APPEND:
option = parse_option_decl (cfile, &config -> defaults [0]);
if (option)
config -> default_actions [option -> code] =
ACTION_APPEND;
return;
case PREPEND:
option = parse_option_decl (cfile, &config -> defaults [0]);
if (option)
config -> default_actions [option -> code] =
ACTION_PREPEND;
return;
case MEDIA:
parse_string_list (cfile, &config -> media, 1);
return;
case HARDWARE:
if (ip) {
parse_hardware_param (cfile, &ip -> hw_address);
} else {
parse_warn ("hardware address parameter %s",
"not allowed here.");
skip_to_semi (cfile);
}
return;
case REQUEST:
config -> requested_option_count =
parse_option_list (cfile, config -> requested_options);
return;
case REQUIRE:
memset (config -> required_options, 0,
sizeof config -> required_options);
parse_option_list (cfile, config -> required_options);
return;
case TIMEOUT:
parse_lease_time (cfile, &config -> timeout);
return;
case RETRY:
parse_lease_time (cfile, &config -> retry_interval);
return;
case SELECT_TIMEOUT:
parse_lease_time (cfile, &config -> select_interval);
return;
case REBOOT:
parse_lease_time (cfile, &config -> reboot_timeout);
return;
case BACKOFF_CUTOFF:
parse_lease_time (cfile, &config -> backoff_cutoff);
return;
case INITIAL_INTERVAL:
parse_lease_time (cfile, &config -> initial_interval);
return;
case SCRIPT:
config -> script_name = parse_string (cfile);
return;
case INTERFACE:
if (ip)
parse_warn ("nested interface declaration.");
parse_interface_declaration (cfile, config);
return;
case LEASE:
parse_client_lease_statement (cfile, 1);
return;
case ALIAS:
parse_client_lease_statement (cfile, 2);
return;
case REJECT:
parse_reject_statement (cfile, config);
return;
default:
parse_warn ("expecting a statement.");
skip_to_semi (cfile);
break;
}
token = next_token (&val, cfile);
if (token != SEMI) {
parse_warn ("semicolon expected.");
skip_to_semi (cfile);
}
}
int parse_X (cfile, buf, max)
FILE *cfile;
u_int8_t *buf;
int max;
{
int token;
char *val;
int len;
token = peek_token (&val, cfile);
if (token == NUMBER_OR_NAME || token == NUMBER) {
len = 0;
do {
token = next_token (&val, cfile);
if (token != NUMBER && token != NUMBER_OR_NAME) {
parse_warn ("expecting hexadecimal constant.");
skip_to_semi (cfile);
return 0;
}
convert_num (&buf [len], val, 16, 8);
if (len++ > max) {
parse_warn ("hexadecimal constant too long.");
skip_to_semi (cfile);
return 0;
}
token = peek_token (&val, cfile);
if (token == COLON)
token = next_token (&val, cfile);
} while (token == COLON);
val = (char *)buf;
} else if (token == STRING) {
token = next_token (&val, cfile);
len = strlen (val);
if (len + 1 > max) {
parse_warn ("string constant too long.");
skip_to_semi (cfile);
return 0;
}
memcpy (buf, val, len + 1);
} else {
parse_warn ("expecting string or hexadecimal data");
skip_to_semi (cfile);
return 0;
}
return len;
}
/* option-list :== option_name |
option_list COMMA option_name */
int parse_option_list (cfile, list)
FILE *cfile;
u_int8_t *list;
{
int ix, i;
int token;
char *val;
ix = 0;
do {
token = next_token (&val, cfile);
if (!is_identifier (token)) {
parse_warn ("expected option name.");
skip_to_semi (cfile);
return 0;
}
for (i = 0; i < 256; i++) {
if (!strcasecmp (dhcp_options [i].name, val))
break;
}
if (i == 256) {
parse_warn ("%s: expected option name.");
skip_to_semi (cfile);
return 0;
}
list [ix++] = i;
if (ix == 256) {
parse_warn ("%s: too many options.", val);
skip_to_semi (cfile);
return 0;
}
token = next_token (&val, cfile);
} while (token == COMMA);
if (token != SEMI) {
parse_warn ("expecting semicolon.");
skip_to_semi (cfile);
return 0;
}
return ix;
}
/* interface-declaration :==
INTERFACE string LBRACE client-declarations RBRACE */
void parse_interface_declaration (cfile, outer_config)
FILE *cfile;
struct client_config *outer_config;
{
int token;
char *val;
struct interface_info *ip;
token = next_token (&val, cfile);
if (token != STRING) {
parse_warn ("expecting interface name (in quotes).");
skip_to_semi (cfile);
return;
}
ip = interface_or_dummy (val);
if (!ip -> client)
make_client_state (ip);
if (!ip -> client -> config)
make_client_config (ip, outer_config);
ip -> flags &= ~INTERFACE_AUTOMATIC;
interfaces_requested = 1;
token = next_token (&val, cfile);
if (token != LBRACE) {
parse_warn ("expecting left brace.");
skip_to_semi (cfile);
return;
}
do {
token = peek_token (&val, cfile);
if (token == EOF) {
parse_warn ("unterminated interface declaration.");
return;
}
if (token == RBRACE)
break;
parse_client_statement (cfile, ip, ip -> client -> config);
} while (1);
token = next_token (&val, cfile);
}
struct interface_info *interface_or_dummy (name)
char *name;
{
struct interface_info *ip;
/* Find the interface (if any) that matches the name. */
for (ip = interfaces; ip; ip = ip -> next) {
if (!strcmp (ip -> name, name))
break;
}
/* If it's not a real interface, see if it's on the dummy list. */
if (!ip) {
for (ip = dummy_interfaces; ip; ip = ip -> next) {
if (!strcmp (ip -> name, name))
break;
}
}
/* If we didn't find an interface, make a dummy interface as
a placeholder. */
if (!ip) {
ip = ((struct interface_info *)malloc (sizeof *ip));
if (!ip)
error ("Insufficient memory to record interface %s",
name);
memset (ip, 0, sizeof *ip);
- strcpy (ip -> name, name);
+ strlcpy (ip -> name, name, IFNAMSIZ);
ip -> next = dummy_interfaces;
dummy_interfaces = ip;
}
return ip;
}
void make_client_state (ip)
struct interface_info *ip;
{
ip -> client =
((struct client_state *)malloc (sizeof *(ip -> client)));
if (!ip -> client)
error ("no memory for state on %s\n", ip -> name);
memset (ip -> client, 0, sizeof *(ip -> client));
}
void make_client_config (ip, config)
struct interface_info *ip;
struct client_config *config;
{
ip -> client -> config =
((struct client_config *)
malloc (sizeof (struct client_config)));
if (!ip -> client -> config)
error ("no memory for config for %s\n", ip -> name);
memset (ip -> client -> config, 0,
sizeof *(ip -> client -> config));
memcpy (ip -> client -> config, config, sizeof *config);
}
/* client-lease-statement :==
RBRACE client-lease-declarations LBRACE
client-lease-declarations :==
|
client-lease-declaration |
client-lease-declarations client-lease-declaration */
void parse_client_lease_statement (cfile, is_static)
FILE *cfile;
int is_static;
{
struct client_lease *lease, *lp, *pl;
struct interface_info *ip;
int token;
char *val;
token = next_token (&val, cfile);
if (token != LBRACE) {
parse_warn ("expecting left brace.");
skip_to_semi (cfile);
return;
}
lease = (struct client_lease *)malloc (sizeof (struct client_lease));
if (!lease)
error ("no memory for lease.\n");
memset (lease, 0, sizeof *lease);
lease -> is_static = is_static;
ip = (struct interface_info *)0;
do {
token = peek_token (&val, cfile);
if (token == EOF) {
parse_warn ("unterminated lease declaration.");
return;
}
if (token == RBRACE)
break;
parse_client_lease_declaration (cfile, lease, &ip);
} while (1);
token = next_token (&val, cfile);
/* If the lease declaration didn't include an interface
declaration that we recognized, it's of no use to us. */
if (!ip) {
free_client_lease (lease);
return;
}
/* Make sure there's a client state structure... */
if (!ip -> client)
make_client_state (ip);
/* If this is an alias lease, it doesn't need to be sorted in. */
if (is_static == 2) {
ip -> client -> alias = lease;
return;
}
/* The new lease may supersede a lease that's not the
active lease but is still on the lease list, so scan the
lease list looking for a lease with the same address, and
if we find it, toss it. */
pl = (struct client_lease *)0;
for (lp = ip -> client -> leases; lp; lp = lp -> next) {
if (lp -> address.len == lease -> address.len &&
!memcmp (lp -> address.iabuf, lease -> address.iabuf,
lease -> address.len)) {
if (pl)
pl -> next = lp -> next;
else
ip -> client -> leases = lp -> next;
free_client_lease (lp);
break;
}
}
/* If this is a preloaded lease, just put it on the list of recorded
leases - don't make it the active lease. */
if (is_static) {
lease -> next = ip -> client -> leases;
ip -> client -> leases = lease;
return;
}
/* The last lease in the lease file on a particular interface is
the active lease for that interface. Of course, we don't know
what the last lease in the file is until we've parsed the whole
file, so at this point, we assume that the lease we just parsed
is the active lease for its interface. If there's already
an active lease for the interface, and this lease is for the same
ip address, then we just toss the old active lease and replace
it with this one. If this lease is for a different address,
then if the old active lease has expired, we dump it; if not,
we put it on the list of leases for this interface which are
still valid but no longer active. */
if (ip -> client -> active) {
if (ip -> client -> active -> expiry < cur_time)
free_client_lease (ip -> client -> active);
else if (ip -> client -> active -> address.len ==
lease -> address.len &&
!memcmp (ip -> client -> active -> address.iabuf,
lease -> address.iabuf,
lease -> address.len))
free_client_lease (ip -> client -> active);
else {
ip -> client -> active -> next =
ip -> client -> leases;
ip -> client -> leases = ip -> client -> active;
}
}
ip -> client -> active = lease;
/* phew. */
}
/* client-lease-declaration :==
BOOTP |
INTERFACE string |
FIXED_ADDR ip_address |
FILENAME string |
SERVER_NAME string |
OPTION option-decl |
RENEW time-decl |
REBIND time-decl |
EXPIRE time-decl */
void parse_client_lease_declaration (cfile, lease, ipp)
FILE *cfile;
struct client_lease *lease;
struct interface_info **ipp;
{
int token;
char *val;
struct interface_info *ip;
switch (next_token (&val, cfile)) {
case BOOTP:
lease -> is_bootp = 1;
break;
case INTERFACE:
token = next_token (&val, cfile);
if (token != STRING) {
parse_warn ("expecting interface name (in quotes).");
skip_to_semi (cfile);
break;
}
ip = interface_or_dummy (val);
*ipp = ip;
break;
case FIXED_ADDR:
if (!parse_ip_addr (cfile, &lease -> address))
return;
break;
case MEDIUM:
parse_string_list (cfile, &lease -> medium, 0);
return;
case FILENAME:
lease -> filename = parse_string (cfile);
return;
case SERVER_NAME:
lease -> server_name = parse_string (cfile);
return;
case RENEW:
lease -> renewal = parse_date (cfile);
return;
case REBIND:
lease -> rebind = parse_date (cfile);
return;
case EXPIRE:
lease -> expiry = parse_date (cfile);
return;
case OPTION:
parse_option_decl (cfile, lease -> options);
return;
default:
parse_warn ("expecting lease declaration.");
skip_to_semi (cfile);
break;
}
token = next_token (&val, cfile);
if (token != SEMI) {
parse_warn ("expecting semicolon.");
skip_to_semi (cfile);
}
}
struct option *parse_option_decl (cfile, options)
FILE *cfile;
struct option_data *options;
{
char *val;
int token;
u_int8_t buf [4];
u_int8_t hunkbuf [1024];
int hunkix = 0;
char *vendor;
char *fmt;
struct universe *universe;
struct option *option;
struct iaddr ip_addr;
u_int8_t *dp;
int len;
int nul_term = 0;
token = next_token (&val, cfile);
if (!is_identifier (token)) {
parse_warn ("expecting identifier after option keyword.");
if (token != SEMI)
skip_to_semi (cfile);
return (struct option *)0;
}
vendor = malloc (strlen (val) + 1);
if (!vendor)
error ("no memory for vendor information.");
strcpy (vendor, val);
token = peek_token (&val, cfile);
if (token == DOT) {
/* Go ahead and take the DOT token... */
token = next_token (&val, cfile);
/* The next token should be an identifier... */
token = next_token (&val, cfile);
if (!is_identifier (token)) {
parse_warn ("expecting identifier after '.'");
if (token != SEMI)
skip_to_semi (cfile);
return (struct option *)0;
}
/* Look up the option name hash table for the specified
vendor. */
universe = ((struct universe *)
hash_lookup (&universe_hash,
(unsigned char *)vendor, 0));
/* If it's not there, we can't parse the rest of the
declaration. */
if (!universe) {
parse_warn ("no vendor named %s.", vendor);
skip_to_semi (cfile);
return (struct option *)0;
}
} else {
/* Use the default hash table, which contains all the
standard dhcp option names. */
val = vendor;
universe = &dhcp_universe;
}
/* Look up the actual option info... */
option = (struct option *)hash_lookup (universe -> hash,
(unsigned char *)val, 0);
/* If we didn't get an option structure, it's an undefined option. */
if (!option) {
if (val == vendor)
parse_warn ("no option named %s", val);
else
parse_warn ("no option named %s for vendor %s",
val, vendor);
skip_to_semi (cfile);
return (struct option *)0;
}
/* Free the initial identifier token. */
free (vendor);
/* Parse the option data... */
do {
for (fmt = option -> format; *fmt; fmt++) {
if (*fmt == 'A')
break;
switch (*fmt) {
case 'X':
len = parse_X (cfile, &hunkbuf [hunkix],
sizeof hunkbuf - hunkix);
hunkix += len;
break;
case 't': /* Text string... */
token = next_token (&val, cfile);
if (token != STRING) {
parse_warn ("expecting string.");
skip_to_semi (cfile);
return (struct option *)0;
}
len = strlen (val);
if (hunkix + len + 1 > sizeof hunkbuf) {
parse_warn ("option data buffer %s",
"overflow");
skip_to_semi (cfile);
return (struct option *)0;
}
memcpy (&hunkbuf [hunkix], val, len + 1);
nul_term = 1;
hunkix += len;
break;
case 'I': /* IP address. */
if (!parse_ip_addr (cfile, &ip_addr))
return (struct option *)0;
len = ip_addr.len;
dp = ip_addr.iabuf;
alloc:
if (hunkix + len > sizeof hunkbuf) {
parse_warn ("option data buffer %s",
"overflow");
skip_to_semi (cfile);
return (struct option *)0;
}
memcpy (&hunkbuf [hunkix], dp, len);
hunkix += len;
break;
case 'L': /* Unsigned 32-bit integer... */
case 'l': /* Signed 32-bit integer... */
token = next_token (&val, cfile);
if (token != NUMBER) {
need_number:
parse_warn ("expecting number.");
if (token != SEMI)
skip_to_semi (cfile);
return (struct option *)0;
}
convert_num (buf, val, 0, 32);
len = 4;
dp = buf;
goto alloc;
case 's': /* Signed 16-bit integer. */
case 'S': /* Unsigned 16-bit integer. */
token = next_token (&val, cfile);
if (token != NUMBER)
goto need_number;
convert_num (buf, val, 0, 16);
len = 2;
dp = buf;
goto alloc;
case 'b': /* Signed 8-bit integer. */
case 'B': /* Unsigned 8-bit integer. */
token = next_token (&val, cfile);
if (token != NUMBER)
goto need_number;
convert_num (buf, val, 0, 8);
len = 1;
dp = buf;
goto alloc;
case 'f': /* Boolean flag. */
token = next_token (&val, cfile);
if (!is_identifier (token)) {
parse_warn ("expecting identifier.");
bad_flag:
if (token != SEMI)
skip_to_semi (cfile);
return (struct option *)0;
}
if (!strcasecmp (val, "true")
|| !strcasecmp (val, "on"))
buf [0] = 1;
else if (!strcasecmp (val, "false")
|| !strcasecmp (val, "off"))
buf [0] = 0;
else {
parse_warn ("expecting boolean.");
goto bad_flag;
}
len = 1;
dp = buf;
goto alloc;
default:
warn ("Bad format %c in parse_option_param.",
*fmt);
skip_to_semi (cfile);
return (struct option *)0;
}
}
token = next_token (&val, cfile);
} while (*fmt == 'A' && token == COMMA);
if (token != SEMI) {
parse_warn ("semicolon expected.");
skip_to_semi (cfile);
return (struct option *)0;
}
options [option -> code].data =
(unsigned char *)malloc (hunkix + nul_term);
if (!options [option -> code].data)
error ("out of memory allocating option data.");
memcpy (options [option -> code].data, hunkbuf, hunkix + nul_term);
options [option -> code].len = hunkix;
return option;
}
void parse_string_list (cfile, lp, multiple)
FILE *cfile;
struct string_list **lp;
int multiple;
{
int token;
char *val;
struct string_list *cur, *tmp;
/* Find the last medium in the media list. */
if (*lp) {
for (cur = *lp; cur -> next; cur = cur -> next)
;
} else {
cur = (struct string_list *)0;
}
do {
token = next_token (&val, cfile);
if (token != STRING) {
parse_warn ("Expecting media options.");
skip_to_semi (cfile);
return;
}
tmp = (struct string_list *)malloc (strlen (val) + 1 +
sizeof
(struct string_list *));
if (!tmp)
error ("no memory for string list entry.");
strcpy (tmp -> string, val);
tmp -> next = (struct string_list *)0;
/* Store this medium at the end of the media list. */
if (cur)
cur -> next = tmp;
else
*lp = tmp;
cur = tmp;
token = next_token (&val, cfile);
} while (multiple && token == COMMA);
if (token != SEMI) {
parse_warn ("expecting semicolon.");
skip_to_semi (cfile);
}
}
void parse_reject_statement (cfile, config)
FILE *cfile;
struct client_config *config;
{
int token;
char *val;
struct iaddr addr;
struct iaddrlist *list;
do {
if (!parse_ip_addr (cfile, &addr)) {
parse_warn ("expecting IP address.");
skip_to_semi (cfile);
return;
}
list = (struct iaddrlist *)malloc (sizeof (struct iaddrlist));
if (!list)
error ("no memory for reject list!");
list -> addr = addr;
list -> next = config -> reject_list;
config -> reject_list = list;
token = next_token (&val, cfile);
} while (token == COMMA);
if (token != SEMI) {
parse_warn ("expecting semicolon.");
skip_to_semi (cfile);
}
}
diff --git a/contrib/isc-dhcp/client/dhclient.8 b/contrib/isc-dhcp/client/dhclient.8
index 096b1060c997..6cdc06350b29 100644
--- a/contrib/isc-dhcp/client/dhclient.8
+++ b/contrib/isc-dhcp/client/dhclient.8
@@ -1,212 +1,226 @@
.\" dhclient.8
.\"
.\" Copyright (c) 1997 The Internet Software Consortium.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\"
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. Neither the name of The Internet Software Consortium nor the names
.\" of its contributors may be used to endorse or promote products derived
.\" from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
.\" DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" This software has been written for the Internet Software Consortium
.\" by Ted Lemon in cooperation with Vixie
.\" Enterprises. To learn more about the Internet Software Consortium,
.\" see ``http://www.isc.org/isc''. To learn more about Vixie
.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.\" Portions copyright (c) 2000 David E. O'Brien.
+.\" All rights reserved.
+.\" $FreeBSD$
+.\"
.TH dhclient 8
.SH NAME
dhclient - Dynamic Host Configuration Protocol Client
.SH SYNOPSIS
.B dhclient
[
.B -Ddq1
]
[
+.B -cf
+.I config-file
+]
+[
.B -lf
.I lease-file
]
[
.B -p
.I port
]
[
.B -pf
.I pidfile
]
[
.I if0
[
.I ...ifN
]
]
.SH DESCRIPTION
The Internet Software Consortium DHCP Client, dhclient, provides a
means for configuring one or more network interfaces using the Dynamic
Host Configuration Protocol, BOOTP protocol, or if these protocols
fail, by statically assigning an address.
.SH OPERATION
.PP
The DHCP protocol allows a host to contact a central server which
maintains a list of IP addresses which may be assigned on one or more
subnets. A DHCP client may request an address from this pool, and
then use it on a temporary basis for communication on network. The
DHCP protocol also provides a mechanism whereby a client can learn
important details about the network to which it is attached, such as
the location of a default router, the location of a name server, and
so on.
.PP
On startup, dhclient reads the
.IR dhclient.conf
for configuration instructions. It then gets a list of all the
network interfaces that are configured in the current system. For
each interface, it attempts to configure the interface using the DHCP
protocol.
.PP
In order to keep track of leases across system reboots and server
restarts, dhclient keeps a list of leases it has been assigned in the
dhclient.leases(5) file. On startup, after reading the dhclient.conf
file, dhclient reads the dhclient.leases file to refresh its memory
about what leases it has been assigned.
.PP
When a new lease is acquired, it is appended to the end of the
dhclient.leases file. In order to prevent the file from becoming
arbitrarily large, from time to time dhclient creates a new
dhclient.leases file from its in-core lease database. The old version
of the dhclient.leases file is retained under the name
.IR dhcpd.leases~
until the next time dhclient rewrites the database.
.PP
Old leases are kept around in case the DHCP server is unavailable when
dhclient is first invoked (generally during the initial system boot
process). In that event, old leases from the dhclient.leases file
which have not yet expired are tested, and if they are determined to
be valid, they are used until either they expire or the DHCP server
becomes available.
.PP
A mobile host which may sometimes need to access a network on which no
DHCP server exists may be preloaded with a lease for a fixed
address on that network. When all attempts to contact a DHCP server
have failed, dhclient will try to validate the static lease, and if it
succeeds, will use that lease until it is restarted.
.PP
A mobile host may also travel to some networks on which DHCP is not
available but BOOTP is. In that case, it may be advantageous to
arrange with the network administrator for an entry on the BOOTP
database, so that the host can boot quickly on that network rather
than cycling through the list of old leases.
.SH COMMAND LINE
.PP
The names of the network interfaces that dhclient should attempt to
configure may be specified on the command line. If no interface names
are specified on the command line dhclient will identify all network
interfaces, elimininating non-broadcast interfaces if possible, and
attempt to configure each interface.
.PP
The
.B -D
flag causes
.B dhclient
to save the script it creates for use in conjunction with
.B dhclient-script
in
.IR /tmp.
.PP
Dhclient will normally run in the foreground until it has configured
an interface, and then will revert to running in the background.
To run force dhclient to always run as a foreground process, the
.B -d
flag should be specified. This is useful when running dhclient under
a debugger, or when running it out of inittab on System V systems.
.PP
If dhclient should listen and transmit on a port other than the
standard (port 68), the
.B -p
flag may used. It should be followed by the udp port number that
dhclient should use. This is mostly useful for debugging purposes.
If the
.B -p
flag is specified, the client will transmit responses to servers at a
port number that is one less than the one specified - i.e., if you
specify
.B -p
68, then the client will listen on port 68 and transmit to port 67.
Datagrams that must go through relay agents are sent to the port
number specified with the
.B -p
flag - if you wish to use alternate port numbers, you must configure
any relay agents you are using to use the same alternate port numbers.
.PP
The
+.B -cf
+flag may be used to change the shell script from the default of
+/sbin/dhclient-script.
+.PP
+The
.B -lf
flag may be used to change the lease output file from the default of
/var/db/dhclient.leases.
.PP
The
.B -pf
flag may be used to change the PID file from the default of
/var/run/dhclient.pid.
.PP
The
.B -q
flag may be used to reduce the amount of screen output from
.B dhclient.
.PP
The
.B -1
flag cause dhclient to try once to get a lease. If it fails, dhclient exits
with exit code two.
.PP
.SH CONFIGURATION
The syntax of the dhclient.conf(5) file is discussed separately.
.SH FILES
.B /etc/dhclient.conf, /var/db/dhclient.leases, /var/db/dhclient.leases~.
.B /var/run/dhclient.pid,
.SH SEE ALSO
dhclient.conf(5), dhclient.leases(5), dhclient-script(8)
.SH AUTHOR
.B dhclient(8)
has been written for the Internet Software Consortium
by Ted Lemon in cooperation with Vixie
Enterprises. To learn more about the Internet Software Consortium,
see
.B http://www.vix.com/isc.
To learn more about Vixie
Enterprises, see
.B http://www.vix.com.
.PP
This client was substantially modified and enhanced by Elliot Poger
for use on Linux while he was working on the MosquitoNet project at
Stanford.
.PP
The current version owes much to Elliot's Linux enhancements, but
was substantially reorganized and partially rewritten by Ted Lemon
so as to use the same networking framework that the Internet Software
Consortium DHCP server uses. Much system-specific configuration code
was moved into a shell script so that as support for more operating
systems is added, it will not be necessary to port and maintain
system-specific configuration code to these operating systems - instead,
the shell script can invoke the native tools to accomplish the same
purpose.
.PP
diff --git a/contrib/isc-dhcp/client/dhclient.c b/contrib/isc-dhcp/client/dhclient.c
index eeb241b58932..350ba0f39870 100644
--- a/contrib/isc-dhcp/client/dhclient.c
+++ b/contrib/isc-dhcp/client/dhclient.c
@@ -1,2183 +1,2248 @@
/* dhclient.c
DHCP Client. */
/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999
* The Internet Software Consortium. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of The Internet Software Consortium nor the names
* of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
* by Ted Lemon in cooperation with Vixie
* Enterprises. To learn more about the Internet Software Consortium,
* see ``http://www.vix.com/isc''. To learn more about Vixie
* Enterprises, see ``http://www.vix.com''.
*
* This client was substantially modified and enhanced by Elliot Poger
* for use on Linux while he was working on the MosquitoNet project at
* Stanford.
*
* The current version owes much to Elliot's Linux enhancements, but
* was substantially reorganized and partially rewritten by Ted Lemon
* so as to use the same networking framework that the Internet Software
* Consortium DHCP server uses. Much system-specific configuration code
* was moved into a shell script so that as support for more operating
* systems is added, it will not be necessary to port and maintain
* system-specific configuration code to these operating systems - instead,
* the shell script can invoke the native tools to accomplish the same
* purpose.
*/
#ifndef lint
static char ocopyright[] =
-"$Id: dhclient.c,v 1.44.2.44 2000/01/26 12:51:11 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n"
+"$Id: dhclient.c,v 1.44.2.45 2000/07/20 05:06:41 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n"
"$FreeBSD$\n";
#endif /* not lint */
#include "dhcpd.h"
#include "version.h"
TIME cur_time;
TIME default_lease_time = 43200; /* 12 hours... */
TIME max_lease_time = 86400; /* 24 hours... */
struct tree_cache *global_options [256];
char *path_dhclient_conf = _PATH_DHCLIENT_CONF;
char *path_dhclient_db = _PATH_DHCLIENT_DB;
char *path_dhclient_pid = _PATH_DHCLIENT_PID;
int interfaces_requested = 0;
int log_perror = 1;
struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
struct iaddr iaddr_any = { 4, { 0, 0, 0, 0 } };
struct in_addr inaddr_any;
struct sockaddr_in sockaddr_broadcast;
/* ASSERT_STATE() does nothing now; it used to be
assert (state_is == state_shouldbe). */
#define ASSERT_STATE(state_is, state_shouldbe) {}
u_int16_t local_port;
u_int16_t remote_port;
int log_priority;
int no_daemon;
int save_scripts;
int onetry;
static char copyright[] =
"Copyright 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium.";
static char arr [] = "All rights reserved.";
static char message [] = "Internet Software Consortium DHCP Client";
static char contrib [] = "Please contribute if you find this software useful.";
static char url [] = "For info, please visit http://www.isc.org/dhcp-contrib.html";
static void usage PROTO ((char *));
int main (argc, argv, envp)
int argc;
char **argv, **envp;
{
int i;
struct servent *ent;
struct interface_info *ip;
int seed;
int quiet = 1;
char *s;
s = strrchr (argv [0], '/');
if (!s)
s = argv [0];
else
s++;
/* Initially, log errors to stderr as well as to syslogd. */
#ifdef SYSLOG_4_2
openlog (s, LOG_NDELAY);
log_priority = DHCPD_LOG_FACILITY;
#else
openlog (s, LOG_NDELAY, DHCPD_LOG_FACILITY);
#endif
#if !(defined (DEBUG) || defined (SYSLOG_4_2) || defined (__CYGWIN32__))
setlogmask (LOG_UPTO (LOG_INFO));
#endif
for (i = 1; i < argc; i++) {
if (!strcmp (argv [i], "-p")) {
if (++i == argc)
usage (s);
local_port = htons (atoi (argv [i]));
debug ("binding to user-specified port %d",
ntohs (local_port));
} else if (!strcmp (argv [i], "-d")) {
no_daemon = 1;
} else if (!strcmp (argv [i], "-D")) {
save_scripts = 1;
+ } else if (!strcmp (argv [i], "-cf")) {
+ if (++i == argc)
+ usage (s);
+ path_dhclient_conf = argv [i];
} else if (!strcmp (argv [i], "-pf")) {
if (++i == argc)
usage (s);
path_dhclient_pid = argv [i];
} else if (!strcmp (argv [i], "-lf")) {
if (++i == argc)
usage (s);
path_dhclient_db = argv [i];
} else if (!strcmp (argv [i], "-q")) {
quiet = 1;
quiet_interface_discovery = 1;
} else if (!strcmp (argv [i], "-1")) {
onetry = 1;
} else if (argv [i][0] == '-') {
usage (s);
} else {
struct interface_info *tmp =
((struct interface_info *)
dmalloc (sizeof *tmp, "specified_interface"));
if (!tmp)
error ("Insufficient memory to %s %s",
"record interface", argv [i]);
memset (tmp, 0, sizeof *tmp);
- strcpy (tmp -> name, argv [i]);
+ strlcpy (tmp -> name, argv [i], IFNAMSIZ);
tmp -> next = interfaces;
tmp -> flags = INTERFACE_REQUESTED;
interfaces_requested = 1;
interfaces = tmp;
}
}
if (!quiet) {
note ("%s %s", message, DHCP_VERSION);
note (copyright);
note (arr);
note ("");
note (contrib);
note (url);
note ("");
} else
log_perror = 0;
/* Default to the DHCP/BOOTP port. */
if (!local_port) {
ent = getservbyname ("dhcpc", "udp");
if (!ent)
local_port = htons (68);
else
local_port = ent -> s_port;
#ifndef __CYGWIN32__
endservent ();
#endif
}
remote_port = htons (ntohs (local_port) - 1); /* XXX */
/* Get the current time... */
GET_TIME (&cur_time);
sockaddr_broadcast.sin_family = AF_INET;
sockaddr_broadcast.sin_port = remote_port;
sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
#ifdef HAVE_SA_LEN
sockaddr_broadcast.sin_len = sizeof sockaddr_broadcast;
#endif
inaddr_any.s_addr = INADDR_ANY;
/* Discover all the network interfaces. */
discover_interfaces (DISCOVER_UNCONFIGURED);
/* Parse the dhclient.conf file. */
read_client_conf ();
/* Parse the lease database. */
read_client_leases ();
/* Rewrite the lease database... */
rewrite_client_leases ();
/* If no broadcast interfaces were discovered, call the script
and tell it so. */
if (!interfaces) {
script_init ((struct interface_info *)0, "NBI",
(struct string_list *)0);
script_go ((struct interface_info *)0);
note ("No broadcast interfaces found - exiting.");
/* Nothing more to do. */
exit (0);
} else {
/* Call the script with the list of interfaces. */
for (ip = interfaces; ip; ip = ip -> next) {
/* If interfaces were specified, don't configure
interfaces that weren't specified! */
if (interfaces_requested &&
((ip -> flags & (INTERFACE_REQUESTED |
INTERFACE_AUTOMATIC)) !=
INTERFACE_REQUESTED))
continue;
script_init (ip, "PREINIT", (struct string_list *)0);
if (ip -> client -> alias)
script_write_params (ip, "alias_",
ip -> client -> alias);
script_go (ip);
}
}
/* At this point, all the interfaces that the script thinks
are relevant should be running, so now we once again call
discover_interfaces(), and this time ask it to actually set
up the interfaces. */
discover_interfaces (interfaces_requested
? DISCOVER_REQUESTED
: DISCOVER_RUNNING);
/* Make up a seed for the random number generator from current
time plus the sum of the last four bytes of each
interface's hardware address interpreted as an integer.
Not much entropy, but we're booting, so we're not likely to
find anything better. */
seed = 0; /* Unfortunately, what's on the stack isn't random. :') */
for (ip = interfaces; ip; ip = ip -> next) {
int junk;
memcpy (&junk,
&ip -> hw_address.haddr [ip -> hw_address.hlen -
sizeof seed], sizeof seed);
seed += junk;
}
srandom (seed + cur_time);
/* Start a configuration state machine for each interface. */
for (ip = interfaces; ip; ip = ip -> next) {
ip -> client -> state = S_INIT;
state_reboot (ip);
}
/* Set up the bootp packet handler... */
bootp_packet_handler = do_packet;
/* Start dispatching packets and timeouts... */
dispatch ();
/*NOTREACHED*/
return 0;
}
static void usage (appname)
char *appname;
{
note (message);
note (copyright);
note (arr);
note ("");
note (contrib);
note (url);
note ("");
- warn ("Usage: %s [-D] [-d] [-p ] [-lf lease-file]", appname);
- error (" [-pf pidfile] [-q] [-1] [interface]");
+ warn ("Usage: %s [-D] [-d] [-p ] [-cf conf-file]", appname);
+ error (" [-lf lease-file] [-pf pidfile] [-q] [-1] [interface]");
}
void cleanup ()
{
+ /* Make sure the pidfile is gone. */
+ unlink (path_dhclient_pid);
}
/* Individual States:
*
* Each routine is called from the dhclient_state_machine() in one of
* these conditions:
* -> entering INIT state
* -> recvpacket_flag == 0: timeout in this state
* -> otherwise: received a packet in this state
*
* Return conditions as handled by dhclient_state_machine():
* Returns 1, sendpacket_flag = 1: send packet, reset timer.
* Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone).
* Returns 0: finish the nap which was interrupted for no good reason.
*
* Several per-interface variables are used to keep track of the process:
* active_lease: the lease that is being used on the interface
* (null pointer if not configured yet).
* offered_leases: leases corresponding to DHCPOFFER messages that have
* been sent to us by DHCP servers.
* acked_leases: leases corresponding to DHCPACK messages that have been
* sent to us by DHCP servers.
* sendpacket: DHCP packet we're trying to send.
* destination: IP address to send sendpacket to
* In addition, there are several relevant per-lease variables.
* T1_expiry, T2_expiry, lease_expiry: lease milestones
* In the active lease, these control the process of renewing the lease;
* In leases on the acked_leases list, this simply determines when we
* can no longer legitimately use the lease.
*/
void state_reboot (ipp)
void *ipp;
{
struct interface_info *ip = ipp;
/* If we don't remember an active lease, go straight to INIT. */
if (!ip -> client -> active ||
ip -> client -> active -> is_bootp) {
state_init (ip);
return;
}
/* We are in the rebooting state. */
ip -> client -> state = S_REBOOTING;
/* make_request doesn't initialize xid because it normally comes
from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER,
so pick an xid now. */
ip -> client -> xid = random ();
/* Make a DHCPREQUEST packet, and set appropriate per-interface
flags. */
make_request (ip, ip -> client -> active);
ip -> client -> destination = iaddr_broadcast;
ip -> client -> first_sending = cur_time;
ip -> client -> interval = ip -> client -> config -> initial_interval;
/* Zap the medium list... */
ip -> client -> medium = (struct string_list *)0;
/* Send out the first DHCPREQUEST packet. */
send_request (ip);
}
/* Called when a lease has completely expired and we've been unable to
renew it. */
void state_init (ipp)
void *ipp;
{
struct interface_info *ip = ipp;
ASSERT_STATE(state, S_INIT);
/* Make a DHCPDISCOVER packet, and set appropriate per-interface
flags. */
make_discover (ip, ip -> client -> active);
ip -> client -> xid = ip -> client -> packet.xid;
ip -> client -> destination = iaddr_broadcast;
ip -> client -> state = S_SELECTING;
ip -> client -> first_sending = cur_time;
ip -> client -> interval = ip -> client -> config -> initial_interval;
/* Add an immediate timeout to cause the first DHCPDISCOVER packet
to go out. */
send_discover (ip);
}
/* state_selecting is called when one or more DHCPOFFER packets have been
received and a configurable period of time has passed. */
void state_selecting (ipp)
void *ipp;
{
struct interface_info *ip = ipp;
struct client_lease *lp, *next, *picked;
ASSERT_STATE(state, S_SELECTING);
/* Cancel state_selecting and send_discover timeouts, since either
one could have got us here. */
cancel_timeout (state_selecting, ip);
cancel_timeout (send_discover, ip);
/* We have received one or more DHCPOFFER packets. Currently,
the only criterion by which we judge leases is whether or
not we get a response when we arp for them. */
picked = (struct client_lease *)0;
for (lp = ip -> client -> offered_leases; lp; lp = next) {
next = lp -> next;
/* Check to see if we got an ARPREPLY for the address
in this particular lease. */
if (!picked) {
script_init (ip, "ARPCHECK", lp -> medium);
script_write_params (ip, "check_", lp);
/* If the ARPCHECK code detects another
machine using the offered address, it exits
nonzero. We need to send a DHCPDECLINE and
toss the lease. */
if (script_go (ip)) {
make_decline (ip, lp);
send_decline (ip);
goto freeit;
}
picked = lp;
picked -> next = (struct client_lease *)0;
} else {
freeit:
free_client_lease (lp);
}
}
ip -> client -> offered_leases = (struct client_lease *)0;
/* If we just tossed all the leases we were offered, go back
to square one. */
if (!picked) {
ip -> client -> state = S_INIT;
state_init (ip);
return;
}
/* If it was a BOOTREPLY, we can just take the address right now. */
if (!picked -> options [DHO_DHCP_MESSAGE_TYPE].len) {
ip -> client -> new = picked;
/* Make up some lease expiry times
XXX these should be configurable. */
ip -> client -> new -> expiry = cur_time + 12000;
ip -> client -> new -> renewal += cur_time + 8000;
ip -> client -> new -> rebind += cur_time + 10000;
ip -> client -> state = S_REQUESTING;
/* Bind to the address we received. */
bind_lease (ip);
return;
}
/* Go to the REQUESTING state. */
ip -> client -> destination = iaddr_broadcast;
ip -> client -> state = S_REQUESTING;
ip -> client -> first_sending = cur_time;
ip -> client -> interval = ip -> client -> config -> initial_interval;
/* Make a DHCPREQUEST packet from the lease we picked. */
make_request (ip, picked);
ip -> client -> xid = ip -> client -> packet.xid;
/* Toss the lease we picked - we'll get it back in a DHCPACK. */
free_client_lease (picked);
/* Add an immediate timeout to send the first DHCPREQUEST packet. */
send_request (ip);
}
/* state_requesting is called when we receive a DHCPACK message after
having sent out one or more DHCPREQUEST packets. */
void dhcpack (packet)
struct packet *packet;
{
struct interface_info *ip = packet -> interface;
struct client_lease *lease;
/* If we're not receptive to an offer right now, or if the offer
has an unrecognizable transaction id, then just drop it. */
if (packet -> interface -> client -> xid != packet -> raw -> xid ||
(packet -> interface -> hw_address.hlen !=
packet -> raw -> hlen) ||
(memcmp (packet -> interface -> hw_address.haddr,
packet -> raw -> chaddr, packet -> raw -> hlen))) {
#if defined (DEBUG)
debug ("DHCPACK in wrong transaction.");
#endif
return;
}
if (ip -> client -> state != S_REBOOTING &&
ip -> client -> state != S_REQUESTING &&
ip -> client -> state != S_RENEWING &&
ip -> client -> state != S_REBINDING) {
#if defined (DEBUG)
debug ("DHCPACK in wrong state.");
#endif
return;
}
note ("DHCPACK from %s", piaddr (packet -> client_addr));
lease = packet_to_lease (packet);
if (!lease) {
note ("packet_to_lease failed.");
return;
}
ip -> client -> new = lease;
/* Stop resending DHCPREQUEST. */
cancel_timeout (send_request, ip);
/* Figure out the lease time. */
ip -> client -> new -> expiry =
getULong (ip -> client ->
new -> options [DHO_DHCP_LEASE_TIME].data);
/* A number that looks negative here is really just very large,
because the lease expiry offset is unsigned. */
if (ip -> client -> new -> expiry < 0)
ip -> client -> new -> expiry = TIME_MAX;
/* Take the server-provided renewal time if there is one;
otherwise figure it out according to the spec. */
if (ip -> client -> new -> options [DHO_DHCP_RENEWAL_TIME].len)
ip -> client -> new -> renewal =
getULong (ip -> client ->
new -> options [DHO_DHCP_RENEWAL_TIME].data);
else
ip -> client -> new -> renewal =
ip -> client -> new -> expiry / 2;
/* Same deal with the rebind time. */
if (ip -> client -> new -> options [DHO_DHCP_REBINDING_TIME].len)
ip -> client -> new -> rebind =
getULong (ip -> client -> new ->
options [DHO_DHCP_REBINDING_TIME].data);
else
ip -> client -> new -> rebind =
ip -> client -> new -> renewal +
ip -> client -> new -> renewal / 2 +
ip -> client -> new -> renewal / 4;
ip -> client -> new -> expiry += cur_time;
/* Lease lengths can never be negative. */
if (ip -> client -> new -> expiry < cur_time)
ip -> client -> new -> expiry = TIME_MAX;
ip -> client -> new -> renewal += cur_time;
if (ip -> client -> new -> renewal < cur_time)
ip -> client -> new -> renewal = TIME_MAX;
ip -> client -> new -> rebind += cur_time;
if (ip -> client -> new -> rebind < cur_time)
ip -> client -> new -> rebind = TIME_MAX;
bind_lease (ip);
}
void bind_lease (ip)
struct interface_info *ip;
{
/* Remember the medium. */
ip -> client -> new -> medium = ip -> client -> medium;
/* Write out the new lease. */
write_client_lease (ip, ip -> client -> new, 0);
/* Run the client script with the new parameters. */
script_init (ip, (ip -> client -> state == S_REQUESTING
? "BOUND"
: (ip -> client -> state == S_RENEWING
? "RENEW"
: (ip -> client -> state == S_REBOOTING
? "REBOOT" : "REBIND"))),
ip -> client -> new -> medium);
if (ip -> client -> active && ip -> client -> state != S_REBOOTING)
script_write_params (ip, "old_", ip -> client -> active);
script_write_params (ip, "new_", ip -> client -> new);
if (ip -> client -> alias)
script_write_params (ip, "alias_", ip -> client -> alias);
script_go (ip);
/* Replace the old active lease with the new one. */
if (ip -> client -> active)
free_client_lease (ip -> client -> active);
ip -> client -> active = ip -> client -> new;
ip -> client -> new = (struct client_lease *)0;
/* Set up a timeout to start the renewal process. */
add_timeout (ip -> client -> active -> renewal,
state_bound, ip);
note ("bound to %s -- renewal in %d seconds.",
piaddr (ip -> client -> active -> address),
ip -> client -> active -> renewal - cur_time);
ip -> client -> state = S_BOUND;
reinitialize_interfaces ();
go_daemon ();
}
/* state_bound is called when we've successfully bound to a particular
lease, but the renewal time on that lease has expired. We are
expected to unicast a DHCPREQUEST to the server that gave us our
original lease. */
void state_bound (ipp)
void *ipp;
{
struct interface_info *ip = ipp;
ASSERT_STATE(state, S_BOUND);
/* T1 has expired. */
make_request (ip, ip -> client -> active);
ip -> client -> xid = ip -> client -> packet.xid;
if (ip -> client -> active ->
options [DHO_DHCP_SERVER_IDENTIFIER].len == 4) {
memcpy (ip -> client -> destination.iabuf,
ip -> client -> active ->
options [DHO_DHCP_SERVER_IDENTIFIER].data, 4);
ip -> client -> destination.len = 4;
} else
ip -> client -> destination = iaddr_broadcast;
ip -> client -> first_sending = cur_time;
ip -> client -> interval = ip -> client -> config -> initial_interval;
ip -> client -> state = S_RENEWING;
/* Send the first packet immediately. */
send_request (ip);
}
int commit_leases ()
{
return 0;
}
int write_lease (lease)
struct lease *lease;
{
return 0;
}
void db_startup ()
{
}
void bootp (packet)
struct packet *packet;
{
struct iaddrlist *ap;
if (packet -> raw -> op != BOOTREPLY)
return;
/* If there's a reject list, make sure this packet's sender isn't
on it. */
for (ap = packet -> interface -> client -> config -> reject_list;
ap; ap = ap -> next) {
if (addr_eq (packet -> client_addr, ap -> addr)) {
note ("BOOTREPLY from %s rejected.",
piaddr (ap -> addr));
return;
}
}
dhcpoffer (packet);
}
void dhcp (packet)
struct packet *packet;
{
struct iaddrlist *ap;
void (*handler) PROTO ((struct packet *));
char *type;
switch (packet -> packet_type) {
case DHCPOFFER:
handler = dhcpoffer;
type = "DHCPOFFER";
break;
case DHCPNAK:
handler = dhcpnak;
type = "DHCPNACK";
break;
case DHCPACK:
handler = dhcpack;
type = "DHCPACK";
break;
default:
return;
}
/* If there's a reject list, make sure this packet's sender isn't
on it. */
for (ap = packet -> interface -> client -> config -> reject_list;
ap; ap = ap -> next) {
if (addr_eq (packet -> client_addr, ap -> addr)) {
note ("%s from %s rejected.",
type, piaddr (ap -> addr));
return;
}
}
(*handler) (packet);
}
void dhcpoffer (packet)
struct packet *packet;
{
struct interface_info *ip = packet -> interface;
struct client_lease *lease, *lp;
int i;
int arp_timeout_needed, stop_selecting;
char *name = (packet -> options [DHO_DHCP_MESSAGE_TYPE].len
? "DHCPOFFER" : "BOOTREPLY");
#ifdef DEBUG_PACKET
dump_packet (packet);
#endif
/* If we're not receptive to an offer right now, or if the offer
has an unrecognizable transaction id, then just drop it. */
if (ip -> client -> state != S_SELECTING ||
packet -> interface -> client -> xid != packet -> raw -> xid ||
(packet -> interface -> hw_address.hlen !=
packet -> raw -> hlen) ||
(memcmp (packet -> interface -> hw_address.haddr,
packet -> raw -> chaddr, packet -> raw -> hlen))) {
#if defined (DEBUG)
debug ("%s in wrong transaction.", name);
#endif
return;
}
note ("%s from %s", name, piaddr (packet -> client_addr));
/* If this lease doesn't supply the minimum required parameters,
blow it off. */
for (i = 0; ip -> client -> config -> required_options [i]; i++) {
if (!packet -> options [ip -> client -> config ->
required_options [i]].len) {
note ("%s isn't satisfactory.", name);
return;
}
}
/* If we've already seen this lease, don't record it again. */
for (lease = ip -> client -> offered_leases;
lease; lease = lease -> next) {
if (lease -> address.len == sizeof packet -> raw -> yiaddr &&
!memcmp (lease -> address.iabuf,
&packet -> raw -> yiaddr, lease -> address.len)) {
debug ("%s already seen.", name);
return;
}
}
lease = packet_to_lease (packet);
if (!lease) {
note ("packet_to_lease failed.");
return;
}
/* If this lease was acquired through a BOOTREPLY, record that
fact. */
if (!packet -> options [DHO_DHCP_MESSAGE_TYPE].len)
lease -> is_bootp = 1;
/* Record the medium under which this lease was offered. */
lease -> medium = ip -> client -> medium;
/* Send out an ARP Request for the offered IP address. */
script_init (ip, "ARPSEND", lease -> medium);
script_write_params (ip, "check_", lease);
/* If the script can't send an ARP request without waiting,
we'll be waiting when we do the ARPCHECK, so don't wait now. */
if (script_go (ip))
arp_timeout_needed = 0;
else
arp_timeout_needed = 2;
/* Figure out when we're supposed to stop selecting. */
stop_selecting = (ip -> client -> first_sending +
ip -> client -> config -> select_interval);
/* If this is the lease we asked for, put it at the head of the
list, and don't mess with the arp request timeout. */
if (lease -> address.len == ip -> client -> requested_address.len &&
!memcmp (lease -> address.iabuf,
ip -> client -> requested_address.iabuf,
ip -> client -> requested_address.len)) {
lease -> next = ip -> client -> offered_leases;
ip -> client -> offered_leases = lease;
} else {
/* If we already have an offer, and arping for this
offer would take us past the selection timeout,
then don't extend the timeout - just hope for the
best. */
if (ip -> client -> offered_leases &&
(cur_time + arp_timeout_needed) > stop_selecting)
arp_timeout_needed = 0;
/* Put the lease at the end of the list. */
lease -> next = (struct client_lease *)0;
if (!ip -> client -> offered_leases)
ip -> client -> offered_leases = lease;
else {
for (lp = ip -> client -> offered_leases; lp -> next;
lp = lp -> next)
;
lp -> next = lease;
}
}
/* If we're supposed to stop selecting before we've had time
to wait for the ARPREPLY, add some delay to wait for
the ARPREPLY. */
if (stop_selecting - cur_time < arp_timeout_needed)
stop_selecting = cur_time + arp_timeout_needed;
/* If the selecting interval has expired, go immediately to
state_selecting(). Otherwise, time out into
state_selecting at the select interval. */
if (stop_selecting <= 0)
state_selecting (ip);
else {
add_timeout (stop_selecting, state_selecting, ip);
cancel_timeout (send_discover, ip);
}
}
/* Allocate a client_lease structure and initialize it from the parameters
in the specified packet. */
struct client_lease *packet_to_lease (packet)
struct packet *packet;
{
struct client_lease *lease;
int i;
lease = (struct client_lease *)malloc (sizeof (struct client_lease));
if (!lease) {
warn ("dhcpoffer: no memory to record lease.\n");
return (struct client_lease *)0;
}
memset (lease, 0, sizeof *lease);
/* Copy the lease options. */
for (i = 0; i < 256; i++) {
if (packet -> options [i].len) {
lease -> options [i].data =
(unsigned char *)
malloc (packet -> options [i].len + 1);
if (!lease -> options [i].data) {
warn ("dhcpoffer: no memory for option %d\n",
i);
free_client_lease (lease);
return (struct client_lease *)0;
} else {
memcpy (lease -> options [i].data,
packet -> options [i].data,
packet -> options [i].len);
lease -> options [i].len =
packet -> options [i].len;
lease -> options [i].data
[lease -> options [i].len] = 0;
}
}
}
lease -> address.len = sizeof (packet -> raw -> yiaddr);
memcpy (lease -> address.iabuf, &packet -> raw -> yiaddr,
lease -> address.len);
/* If the server name was filled out, copy it. */
if ((!packet -> options [DHO_DHCP_OPTION_OVERLOAD].len ||
!(packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2)) &&
packet -> raw -> sname [0]) {
int len;
/* Don't count on the NUL terminator. */
for (len = 0; len < 64; len++)
if (!packet -> raw -> sname [len])
break;
lease -> server_name = malloc (len + 1);
if (!lease -> server_name) {
warn ("dhcpoffer: no memory for filename.\n");
free_client_lease (lease);
return (struct client_lease *)0;
} else {
memcpy (lease -> server_name,
packet -> raw -> sname, len);
lease -> server_name [len] = 0;
}
}
/* Ditto for the filename. */
if ((!packet -> options [DHO_DHCP_OPTION_OVERLOAD].len ||
!(packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1)) &&
packet -> raw -> file [0]) {
int len;
/* Don't count on the NUL terminator. */
for (len = 0; len < 64; len++)
if (!packet -> raw -> file [len])
break;
lease -> filename = malloc (len + 1);
if (!lease -> filename) {
warn ("dhcpoffer: no memory for filename.\n");
free_client_lease (lease);
return (struct client_lease *)0;
} else {
memcpy (lease -> filename,
packet -> raw -> file, len);
lease -> filename [len] = 0;
}
}
return lease;
}
void dhcpnak (packet)
struct packet *packet;
{
struct interface_info *ip = packet -> interface;
/* If we're not receptive to an offer right now, or if the offer
has an unrecognizable transaction id, then just drop it. */
if (packet -> interface -> client -> xid != packet -> raw -> xid ||
(packet -> interface -> hw_address.hlen !=
packet -> raw -> hlen) ||
(memcmp (packet -> interface -> hw_address.haddr,
packet -> raw -> chaddr, packet -> raw -> hlen))) {
#if defined (DEBUG)
debug ("DHCPNAK in wrong transaction.");
#endif
return;
}
if (ip -> client -> state != S_REBOOTING &&
ip -> client -> state != S_REQUESTING &&
ip -> client -> state != S_RENEWING &&
ip -> client -> state != S_REBINDING) {
#if defined (DEBUG)
debug ("DHCPNAK in wrong state.");
#endif
return;
}
note ("DHCPNAK from %s", piaddr (packet -> client_addr));
if (!ip -> client -> active) {
note ("DHCPNAK with no active lease.\n");
return;
}
free_client_lease (ip -> client -> active);
ip -> client -> active = (struct client_lease *)0;
/* Stop sending DHCPREQUEST packets... */
cancel_timeout (send_request, ip);
ip -> client -> state = S_INIT;
state_init (ip);
}
/* Send out a DHCPDISCOVER packet, and set a timeout to send out another
one after the right interval has expired. If we don't get an offer by
the time we reach the panic interval, call the panic function. */
void send_discover (ipp)
void *ipp;
{
struct interface_info *ip = ipp;
int result;
int interval;
int increase = 1;
/* Figure out how long it's been since we started transmitting. */
interval = cur_time - ip -> client -> first_sending;
/* If we're past the panic timeout, call the script and tell it
we haven't found anything for this interface yet. */
if (interval > ip -> client -> config -> timeout) {
state_panic (ip);
return;
}
/* If we're selecting media, try the whole list before doing
the exponential backoff, but if we've already received an
offer, stop looping, because we obviously have it right. */
if (!ip -> client -> offered_leases &&
ip -> client -> config -> media) {
int fail = 0;
again:
if (ip -> client -> medium) {
ip -> client -> medium =
ip -> client -> medium -> next;
increase = 0;
}
if (!ip -> client -> medium) {
if (fail)
error ("No valid media types for %s!",
ip -> name);
ip -> client -> medium =
ip -> client -> config -> media;
increase = 1;
}
note ("Trying medium \"%s\" %d",
ip -> client -> medium -> string, increase);
script_init (ip, "MEDIUM", ip -> client -> medium);
if (script_go (ip)) {
goto again;
}
}
/* If we're supposed to increase the interval, do so. If it's
currently zero (i.e., we haven't sent any packets yet), set
it to one; otherwise, add to it a random number between
zero and two times itself. On average, this means that it
will double with every transmission. */
if (increase) {
if (!ip -> client -> interval)
ip -> client -> interval =
ip -> client -> config -> initial_interval;
else {
ip -> client -> interval +=
((random () >> 2) %
(2 * ip -> client -> interval));
}
/* Don't backoff past cutoff. */
if (ip -> client -> interval >
ip -> client -> config -> backoff_cutoff)
ip -> client -> interval =
((ip -> client -> config -> backoff_cutoff / 2)
+ ((random () >> 2) %
ip -> client -> config -> backoff_cutoff));
} else if (!ip -> client -> interval)
ip -> client -> interval =
ip -> client -> config -> initial_interval;
/* If the backoff would take us to the panic timeout, just use that
as the interval. */
if (cur_time + ip -> client -> interval >
ip -> client -> first_sending + ip -> client -> config -> timeout)
ip -> client -> interval =
(ip -> client -> first_sending +
ip -> client -> config -> timeout) - cur_time + 1;
/* Record the number of seconds since we started sending. */
if (interval < 65536)
ip -> client -> packet.secs = htons (interval);
else
ip -> client -> packet.secs = htons (65535);
ip -> client -> secs = ip -> client -> packet.secs;
note ("DHCPDISCOVER on %s to %s port %d interval %ld",
ip -> name,
inet_ntoa (sockaddr_broadcast.sin_addr),
ntohs (sockaddr_broadcast.sin_port), ip -> client -> interval);
/* Send out a packet. */
result = send_packet (ip, (struct packet *)0,
&ip -> client -> packet,
ip -> client -> packet_length,
inaddr_any, &sockaddr_broadcast,
(struct hardware *)0);
add_timeout (cur_time + ip -> client -> interval, send_discover, ip);
}
/* state_panic gets called if we haven't received any offers in a preset
amount of time. When this happens, we try to use existing leases that
haven't yet expired, and failing that, we call the client script and
hope it can do something. */
void state_panic (ipp)
void *ipp;
{
struct interface_info *ip = ipp;
struct client_lease *loop = ip -> client -> active;
struct client_lease *lp;
note ("No DHCPOFFERS received.");
/* We may not have an active lease, but we may have some
predefined leases that we can try. */
if (!ip -> client -> active && ip -> client -> leases)
goto activate_next;
/* Run through the list of leases and see if one can be used. */
while (ip -> client -> active) {
if (ip -> client -> active -> expiry > cur_time) {
note ("Trying recorded lease %s",
piaddr (ip -> client -> active -> address));
/* Run the client script with the existing
parameters. */
script_init (ip, "TIMEOUT",
ip -> client -> active -> medium);
script_write_params (ip, "new_",
ip -> client -> active);
if (ip -> client -> alias)
script_write_params (ip, "alias_",
ip -> client -> alias);
/* If the old lease is still good and doesn't
yet need renewal, go into BOUND state and
timeout at the renewal time. */
if (!script_go (ip)) {
if (cur_time <
ip -> client -> active -> renewal) {
ip -> client -> state = S_BOUND;
note ("bound: renewal in %d seconds.",
ip -> client -> active -> renewal
- cur_time);
add_timeout ((ip -> client ->
active -> renewal),
state_bound, ip);
} else {
ip -> client -> state = S_BOUND;
note ("bound: immediate renewal.");
state_bound (ip);
}
reinitialize_interfaces ();
go_daemon ();
return;
}
}
/* If there are no other leases, give up. */
if (!ip -> client -> leases) {
ip -> client -> leases = ip -> client -> active;
ip -> client -> active = (struct client_lease *)0;
break;
}
activate_next:
/* Otherwise, put the active lease at the end of the
lease list, and try another lease.. */
for (lp = ip -> client -> leases; lp -> next; lp = lp -> next)
;
lp -> next = ip -> client -> active;
if (lp -> next) {
lp -> next -> next = (struct client_lease *)0;
}
ip -> client -> active = ip -> client -> leases;
ip -> client -> leases = ip -> client -> leases -> next;
/* If we already tried this lease, we've exhausted the
set of leases, so we might as well give up for
now. */
if (ip -> client -> active == loop)
break;
else if (!loop)
loop = ip -> client -> active;
}
/* No leases were available, or what was available didn't work, so
tell the shell script that we failed to allocate an address,
and try again later. */
if (onetry) {
exit(2);
note ("Unable to obtain a lease on first try - exiting.\n");
}
note ("No working leases in persistent database - sleeping.\n");
script_init (ip, "FAIL", (struct string_list *)0);
if (ip -> client -> alias)
script_write_params (ip, "alias_", ip -> client -> alias);
script_go (ip);
ip -> client -> state = S_INIT;
add_timeout (cur_time + ip -> client -> config -> retry_interval,
state_init, ip);
go_daemon ();
}
void send_request (ipp)
void *ipp;
{
struct interface_info *ip = ipp;
int result;
int interval;
struct sockaddr_in destination;
struct in_addr from;
/* Figure out how long it's been since we started transmitting. */
interval = cur_time - ip -> client -> first_sending;
/* If we're in the INIT-REBOOT or REQUESTING state and we're
past the reboot timeout, go to INIT and see if we can
DISCOVER an address... */
/* XXX In the INIT-REBOOT state, if we don't get an ACK, it
means either that we're on a network with no DHCP server,
or that our server is down. In the latter case, assuming
that there is a backup DHCP server, DHCPDISCOVER will get
us a new address, but we could also have successfully
reused our old address. In the former case, we're hosed
anyway. This is not a win-prone situation. */
if ((ip -> client -> state == S_REBOOTING ||
ip -> client -> state == S_REQUESTING) &&
interval > ip -> client -> config -> reboot_timeout) {
cancel:
ip -> client -> state = S_INIT;
cancel_timeout (send_request, ip);
state_init (ip);
return;
}
/* If we're in the reboot state, make sure the media is set up
correctly. */
if (ip -> client -> state == S_REBOOTING &&
!ip -> client -> medium &&
ip -> client -> active -> medium ) {
script_init (ip, "MEDIUM", ip -> client -> active -> medium);
/* If the medium we chose won't fly, go to INIT state. */
if (script_go (ip))
goto cancel;
/* Record the medium. */
ip -> client -> medium = ip -> client -> active -> medium;
}
/* If the lease has expired, relinquish the address and go back
to the INIT state. */
if (ip -> client -> state != S_REQUESTING &&
cur_time > ip -> client -> active -> expiry) {
/* Run the client script with the new parameters. */
script_init (ip, "EXPIRE", (struct string_list *)0);
script_write_params (ip, "old_", ip -> client -> active);
if (ip -> client -> alias)
script_write_params (ip, "alias_",
ip -> client -> alias);
script_go (ip);
/* Now do a preinit on the interface so that we can
discover a new address. */
script_init (ip, "PREINIT", (struct string_list *)0);
if (ip -> client -> alias)
script_write_params (ip, "alias_",
ip -> client -> alias);
script_go (ip);
ip -> client -> state = S_INIT;
state_init (ip);
return;
}
/* Do the exponential backoff... */
if (!ip -> client -> interval)
ip -> client -> interval =
ip -> client -> config -> initial_interval;
else {
ip -> client -> interval +=
((random () >> 2) %
(2 * ip -> client -> interval));
}
/* Don't backoff past cutoff. */
if (ip -> client -> interval >
ip -> client -> config -> backoff_cutoff)
ip -> client -> interval =
((ip -> client -> config -> backoff_cutoff / 2)
+ ((random () >> 2)
% ip -> client -> interval));
/* If the backoff would take us to the expiry time, just set the
timeout to the expiry time. */
if (ip -> client -> state != S_REQUESTING &&
cur_time + ip -> client -> interval >
ip -> client -> active -> expiry)
ip -> client -> interval =
ip -> client -> active -> expiry - cur_time + 1;
/* If the lease T2 time has elapsed, or if we're not yet bound,
broadcast the DHCPREQUEST rather than unicasting. */
if (ip -> client -> state == S_REQUESTING ||
ip -> client -> state == S_REBOOTING ||
cur_time > ip -> client -> active -> rebind)
destination.sin_addr.s_addr = INADDR_BROADCAST;
else
memcpy (&destination.sin_addr.s_addr,
ip -> client -> destination.iabuf,
sizeof destination.sin_addr.s_addr);
destination.sin_port = remote_port;
destination.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
destination.sin_len = sizeof destination;
#endif
if (ip -> client -> state != S_REQUESTING)
memcpy (&from, ip -> client -> active -> address.iabuf,
sizeof from);
else
from.s_addr = INADDR_ANY;
/* Record the number of seconds since we started sending. */
if (ip -> client -> state == S_REQUESTING)
ip -> client -> packet.secs = ip -> client -> secs;
else {
if (interval < 65536)
ip -> client -> packet.secs = htons (interval);
else
ip -> client -> packet.secs = htons (65535);
}
note ("DHCPREQUEST on %s to %s port %d", ip -> name,
inet_ntoa (destination.sin_addr),
ntohs (destination.sin_port));
if (destination.sin_addr.s_addr != INADDR_BROADCAST &&
fallback_interface)
result = send_packet (fallback_interface,
(struct packet *)0,
&ip -> client -> packet,
ip -> client -> packet_length,
from, &destination,
(struct hardware *)0);
else
/* Send out a packet. */
result = send_packet (ip, (struct packet *)0,
&ip -> client -> packet,
ip -> client -> packet_length,
from, &destination,
(struct hardware *)0);
add_timeout (cur_time + ip -> client -> interval,
send_request, ip);
}
void send_decline (ipp)
void *ipp;
{
struct interface_info *ip = ipp;
int result;
note ("DHCPDECLINE on %s to %s port %d", ip -> name,
inet_ntoa (sockaddr_broadcast.sin_addr),
ntohs (sockaddr_broadcast.sin_port));
/* Send out a packet. */
result = send_packet (ip, (struct packet *)0,
&ip -> client -> packet,
ip -> client -> packet_length,
inaddr_any, &sockaddr_broadcast,
(struct hardware *)0);
}
void send_release (ipp)
void *ipp;
{
struct interface_info *ip = ipp;
int result;
note ("DHCPRELEASE on %s to %s port %d", ip -> name,
inet_ntoa (sockaddr_broadcast.sin_addr),
ntohs (sockaddr_broadcast.sin_port));
/* Send out a packet. */
result = send_packet (ip, (struct packet *)0,
&ip -> client -> packet,
ip -> client -> packet_length,
inaddr_any, &sockaddr_broadcast,
(struct hardware *)0);
}
void make_discover (ip, lease)
struct interface_info *ip;
struct client_lease *lease;
{
unsigned char discover = DHCPDISCOVER;
int i;
struct tree_cache *options [256];
struct tree_cache option_elements [256];
memset (option_elements, 0, sizeof option_elements);
memset (options, 0, sizeof options);
memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet));
/* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */
i = DHO_DHCP_MESSAGE_TYPE;
options [i] = &option_elements [i];
options [i] -> value = &discover;
options [i] -> len = sizeof discover;
options [i] -> buf_size = sizeof discover;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
/* Request the options we want */
i = DHO_DHCP_PARAMETER_REQUEST_LIST;
options [i] = &option_elements [i];
options [i] -> value = ip -> client -> config -> requested_options;
options [i] -> len = ip -> client -> config -> requested_option_count;
options [i] -> buf_size =
ip -> client -> config -> requested_option_count;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
/* If we had an address, try to get it again. */
if (lease) {
ip -> client -> requested_address = lease -> address;
i = DHO_DHCP_REQUESTED_ADDRESS;
options [i] = &option_elements [i];
options [i] -> value = lease -> address.iabuf;
options [i] -> len = lease -> address.len;
options [i] -> buf_size = lease -> address.len;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
} else {
ip -> client -> requested_address.len = 0;
}
/* Send any options requested in the config file. */
for (i = 0; i < 256; i++) {
if (!options [i] &&
ip -> client -> config -> send_options [i].data) {
options [i] = &option_elements [i];
options [i] -> value = ip -> client -> config ->
send_options [i].data;
options [i] -> len = ip -> client -> config ->
send_options [i].len;
options [i] -> buf_size = ip -> client -> config ->
send_options [i].len;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
}
}
/* Set up the option buffer... */
ip -> client -> packet_length =
cons_options ((struct packet *)0, &ip -> client -> packet, 0,
options, 0, 0, 0, (u_int8_t *)0, 0);
if (ip -> client -> packet_length < BOOTP_MIN_LEN)
ip -> client -> packet_length = BOOTP_MIN_LEN;
ip -> client -> packet.op = BOOTREQUEST;
ip -> client -> packet.htype = ip -> hw_address.htype;
ip -> client -> packet.hlen = ip -> hw_address.hlen;
ip -> client -> packet.hops = 0;
ip -> client -> packet.xid = random ();
ip -> client -> packet.secs = 0; /* filled in by send_discover. */
if (can_receive_unicast_unconfigured (ip))
ip -> client -> packet.flags = 0;
else
ip -> client -> packet.flags = htons (BOOTP_BROADCAST);
memset (&(ip -> client -> packet.ciaddr),
0, sizeof ip -> client -> packet.ciaddr);
memset (&(ip -> client -> packet.yiaddr),
0, sizeof ip -> client -> packet.yiaddr);
memset (&(ip -> client -> packet.siaddr),
0, sizeof ip -> client -> packet.siaddr);
memset (&(ip -> client -> packet.giaddr),
0, sizeof ip -> client -> packet.giaddr);
memcpy (ip -> client -> packet.chaddr,
ip -> hw_address.haddr, ip -> hw_address.hlen);
#ifdef DEBUG_PACKET
dump_packet (sendpkt);
dump_raw ((unsigned char *)ip -> client -> packet,
sendpkt->packet_length);
#endif
}
void make_request (ip, lease)
struct interface_info *ip;
struct client_lease *lease;
{
unsigned char request = DHCPREQUEST;
int i;
struct tree_cache *options [256];
struct tree_cache option_elements [256];
memset (options, 0, sizeof options);
memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet));
/* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */
i = DHO_DHCP_MESSAGE_TYPE;
options [i] = &option_elements [i];
options [i] -> value = &request;
options [i] -> len = sizeof request;
options [i] -> buf_size = sizeof request;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
/* Request the options we want */
i = DHO_DHCP_PARAMETER_REQUEST_LIST;
options [i] = &option_elements [i];
options [i] -> value = ip -> client -> config -> requested_options;
options [i] -> len = ip -> client -> config -> requested_option_count;
options [i] -> buf_size =
ip -> client -> config -> requested_option_count;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
/* If we are requesting an address that hasn't yet been assigned
to us, use the DHCP Requested Address option. */
if (ip -> client -> state == S_REQUESTING) {
/* Send back the server identifier... */
i = DHO_DHCP_SERVER_IDENTIFIER;
options [i] = &option_elements [i];
options [i] -> value = lease -> options [i].data;
options [i] -> len = lease -> options [i].len;
options [i] -> buf_size = lease -> options [i].len;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
}
if (ip -> client -> state == S_REQUESTING ||
ip -> client -> state == S_REBOOTING) {
ip -> client -> requested_address = lease -> address;
i = DHO_DHCP_REQUESTED_ADDRESS;
options [i] = &option_elements [i];
options [i] -> value = lease -> address.iabuf;
options [i] -> len = lease -> address.len;
options [i] -> buf_size = lease -> address.len;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
} else {
ip -> client -> requested_address.len = 0;
}
/* Send any options requested in the config file. */
for (i = 0; i < 256; i++) {
if (!options [i] &&
ip -> client -> config -> send_options [i].data) {
options [i] = &option_elements [i];
options [i] -> value = ip -> client -> config ->
send_options [i].data;
options [i] -> len = ip -> client -> config ->
send_options [i].len;
options [i] -> buf_size = ip -> client -> config ->
send_options [i].len;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
}
}
/* Set up the option buffer... */
ip -> client -> packet_length =
cons_options ((struct packet *)0, &ip -> client -> packet, 0,
options, 0, 0, 0, (u_int8_t *)0, 0);
if (ip -> client -> packet_length < BOOTP_MIN_LEN)
ip -> client -> packet_length = BOOTP_MIN_LEN;
ip -> client -> packet.op = BOOTREQUEST;
ip -> client -> packet.htype = ip -> hw_address.htype;
ip -> client -> packet.hlen = ip -> hw_address.hlen;
ip -> client -> packet.hops = 0;
ip -> client -> packet.xid = ip -> client -> xid;
ip -> client -> packet.secs = 0; /* Filled in by send_request. */
/* If we own the address we're requesting, put it in ciaddr;
otherwise set ciaddr to zero. */
if (ip -> client -> state == S_BOUND ||
ip -> client -> state == S_RENEWING ||
ip -> client -> state == S_REBINDING) {
memcpy (&ip -> client -> packet.ciaddr,
lease -> address.iabuf, lease -> address.len);
ip -> client -> packet.flags = 0;
} else {
memset (&ip -> client -> packet.ciaddr, 0,
sizeof ip -> client -> packet.ciaddr);
if (can_receive_unicast_unconfigured (ip))
ip -> client -> packet.flags = 0;
else
ip -> client -> packet.flags = htons (BOOTP_BROADCAST);
}
memset (&ip -> client -> packet.yiaddr, 0,
sizeof ip -> client -> packet.yiaddr);
memset (&ip -> client -> packet.siaddr, 0,
sizeof ip -> client -> packet.siaddr);
memset (&ip -> client -> packet.giaddr, 0,
sizeof ip -> client -> packet.giaddr);
memcpy (ip -> client -> packet.chaddr,
ip -> hw_address.haddr, ip -> hw_address.hlen);
#ifdef DEBUG_PACKET
dump_packet (sendpkt);
dump_raw ((unsigned char *)ip -> client -> packet, sendpkt->packet_length);
#endif
}
void make_decline (ip, lease)
struct interface_info *ip;
struct client_lease *lease;
{
unsigned char decline = DHCPDECLINE;
int i;
struct tree_cache *options [256];
struct tree_cache message_type_tree;
struct tree_cache requested_address_tree;
struct tree_cache server_id_tree;
struct tree_cache client_id_tree;
memset (options, 0, sizeof options);
memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet));
/* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */
i = DHO_DHCP_MESSAGE_TYPE;
options [i] = &message_type_tree;
options [i] -> value = &decline;
options [i] -> len = sizeof decline;
options [i] -> buf_size = sizeof decline;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
/* Send back the server identifier... */
i = DHO_DHCP_SERVER_IDENTIFIER;
options [i] = &server_id_tree;
options [i] -> value = lease -> options [i].data;
options [i] -> len = lease -> options [i].len;
options [i] -> buf_size = lease -> options [i].len;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
/* Send back the address we're declining. */
i = DHO_DHCP_REQUESTED_ADDRESS;
options [i] = &requested_address_tree;
options [i] -> value = lease -> address.iabuf;
options [i] -> len = lease -> address.len;
options [i] -> buf_size = lease -> address.len;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
/* Send the uid if the user supplied one. */
i = DHO_DHCP_CLIENT_IDENTIFIER;
if (ip -> client -> config -> send_options [i].len) {
options [i] = &client_id_tree;
options [i] -> value = ip -> client -> config ->
send_options [i].data;
options [i] -> len = ip -> client -> config ->
send_options [i].len;
options [i] -> buf_size = ip -> client -> config ->
send_options [i].len;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
}
/* Set up the option buffer... */
ip -> client -> packet_length =
cons_options ((struct packet *)0, &ip -> client -> packet, 0,
options, 0, 0, 0, (u_int8_t *)0, 0);
if (ip -> client -> packet_length < BOOTP_MIN_LEN)
ip -> client -> packet_length = BOOTP_MIN_LEN;
ip -> client -> packet.op = BOOTREQUEST;
ip -> client -> packet.htype = ip -> hw_address.htype;
ip -> client -> packet.hlen = ip -> hw_address.hlen;
ip -> client -> packet.hops = 0;
ip -> client -> packet.xid = ip -> client -> xid;
ip -> client -> packet.secs = 0; /* Filled in by send_request. */
ip -> client -> packet.flags = 0;
/* ciaddr must always be zero. */
memset (&ip -> client -> packet.ciaddr, 0,
sizeof ip -> client -> packet.ciaddr);
memset (&ip -> client -> packet.yiaddr, 0,
sizeof ip -> client -> packet.yiaddr);
memset (&ip -> client -> packet.siaddr, 0,
sizeof ip -> client -> packet.siaddr);
memset (&ip -> client -> packet.giaddr, 0,
sizeof ip -> client -> packet.giaddr);
memcpy (ip -> client -> packet.chaddr,
ip -> hw_address.haddr, ip -> hw_address.hlen);
#ifdef DEBUG_PACKET
dump_packet (sendpkt);
dump_raw ((unsigned char *)ip -> client -> packet, sendpkt->packet_length);
#endif
}
void make_release (ip, lease)
struct interface_info *ip;
struct client_lease *lease;
{
unsigned char request = DHCPRELEASE;
int i;
struct tree_cache *options [256];
struct tree_cache message_type_tree;
struct tree_cache server_id_tree;
memset (options, 0, sizeof options);
memset (&ip -> client -> packet, 0, sizeof (ip -> client -> packet));
/* Set DHCP_MESSAGE_TYPE to DHCPRELEASE */
i = DHO_DHCP_MESSAGE_TYPE;
options [i] = &message_type_tree;
options [i] -> value = &request;
options [i] -> len = sizeof request;
options [i] -> buf_size = sizeof request;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
/* Send back the server identifier... */
i = DHO_DHCP_SERVER_IDENTIFIER;
options [i] = &server_id_tree;
options [i] -> value = lease -> options [i].data;
options [i] -> len = lease -> options [i].len;
options [i] -> buf_size = lease -> options [i].len;
options [i] -> timeout = 0xFFFFFFFF;
options [i] -> tree = (struct tree *)0;
/* Set up the option buffer... */
ip -> client -> packet_length =
cons_options ((struct packet *)0, &ip -> client -> packet, 0,
options, 0, 0, 0, (u_int8_t *)0, 0);
if (ip -> client -> packet_length < BOOTP_MIN_LEN)
ip -> client -> packet_length = BOOTP_MIN_LEN;
ip -> client -> packet.op = BOOTREQUEST;
ip -> client -> packet.htype = ip -> hw_address.htype;
ip -> client -> packet.hlen = ip -> hw_address.hlen;
ip -> client -> packet.hops = 0;
ip -> client -> packet.xid = random ();
ip -> client -> packet.secs = 0;
ip -> client -> packet.flags = 0;
memset (&ip -> client -> packet.ciaddr, 0,
sizeof ip -> client -> packet.ciaddr);
memset (&ip -> client -> packet.yiaddr, 0,
sizeof ip -> client -> packet.yiaddr);
memset (&ip -> client -> packet.siaddr, 0,
sizeof ip -> client -> packet.siaddr);
memset (&ip -> client -> packet.giaddr, 0,
sizeof ip -> client -> packet.giaddr);
memcpy (ip -> client -> packet.chaddr,
ip -> hw_address.haddr, ip -> hw_address.hlen);
#ifdef DEBUG_PACKET
dump_packet (sendpkt);
dump_raw ((unsigned char *)ip -> client -> packet,
ip -> client -> packet_length);
#endif
}
void free_client_lease (lease)
struct client_lease *lease;
{
int i;
if (lease -> server_name)
free (lease -> server_name);
if (lease -> filename)
free (lease -> filename);
for (i = 0; i < 256; i++) {
if (lease -> options [i].len)
free (lease -> options [i].data);
}
free (lease);
}
FILE *leaseFile;
void rewrite_client_leases ()
{
struct interface_info *ip;
struct client_lease *lp;
if (leaseFile)
fclose (leaseFile);
leaseFile = fopen (path_dhclient_db, "w");
if (!leaseFile)
error ("can't create %s: %m", path_dhclient_db);
/* Write out all the leases attached to configured interfaces that
we know about. */
for (ip = interfaces; ip; ip = ip -> next) {
for (lp = ip -> client -> leases; lp; lp = lp -> next) {
write_client_lease (ip, lp, 1);
}
if (ip -> client -> active)
write_client_lease (ip, ip -> client -> active, 1);
}
/* Write out any leases that are attached to interfaces that aren't
currently configured. */
for (ip = dummy_interfaces; ip; ip = ip -> next) {
for (lp = ip -> client -> leases; lp; lp = lp -> next) {
write_client_lease (ip, lp, 1);
}
if (ip -> client -> active)
write_client_lease (ip, ip -> client -> active, 1);
}
fflush (leaseFile);
}
void write_client_lease (ip, lease, rewrite)
struct interface_info *ip;
struct client_lease *lease;
int rewrite;
{
int i;
struct tm *t;
static int leases_written;
if (!rewrite) {
if (leases_written++ > 20) {
rewrite_client_leases ();
leases_written = 0;
}
}
/* If the lease came from the config file, we don't need to stash
a copy in the lease database. */
if (lease -> is_static)
return;
if (!leaseFile) { /* XXX */
leaseFile = fopen (path_dhclient_db, "w");
if (!leaseFile)
error ("can't create %s: %m", path_dhclient_db);
}
fprintf (leaseFile, "lease {\n");
if (lease -> is_bootp)
fprintf (leaseFile, " bootp;\n");
fprintf (leaseFile, " interface \"%s\";\n", ip -> name);
fprintf (leaseFile, " fixed-address %s;\n",
piaddr (lease -> address));
if (lease -> filename)
fprintf (leaseFile, " filename \"%s\";\n",
lease -> filename);
if (lease -> server_name)
fprintf (leaseFile, " server-name \"%s\";\n",
lease -> server_name);
if (lease -> medium)
fprintf (leaseFile, " medium \"%s\";\n",
lease -> medium -> string);
for (i = 0; i < 256; i++) {
if (lease -> options [i].len) {
fprintf (leaseFile,
" option %s %s;\n",
dhcp_options [i].name,
pretty_print_option
(i, lease -> options [i].data,
lease -> options [i].len, 1, 1));
}
}
/* Note: the following is not a Y2K bug - it's a Y1.9K bug. Until
somebody invents a time machine, I think we can safely disregard
it. */
t = gmtime (&lease -> renewal);
fprintf (leaseFile,
" renew %d %d/%d/%d %02d:%02d:%02d;\n",
t -> tm_wday, t -> tm_year + 1900,
t -> tm_mon + 1, t -> tm_mday,
t -> tm_hour, t -> tm_min, t -> tm_sec);
t = gmtime (&lease -> rebind);
fprintf (leaseFile,
" rebind %d %d/%d/%d %02d:%02d:%02d;\n",
t -> tm_wday, t -> tm_year + 1900,
t -> tm_mon + 1, t -> tm_mday,
t -> tm_hour, t -> tm_min, t -> tm_sec);
t = gmtime (&lease -> expiry);
fprintf (leaseFile,
" expire %d %d/%d/%d %02d:%02d:%02d;\n",
t -> tm_wday, t -> tm_year + 1900,
t -> tm_mon + 1, t -> tm_mday,
t -> tm_hour, t -> tm_min, t -> tm_sec);
fprintf (leaseFile, "}\n");
fflush (leaseFile);
}
/* Variables holding name of script and file pointer for writing to
script. Needless to say, this is not reentrant - only one script
can be invoked at a time. */
char scriptName [256];
FILE *scriptFile;
void script_init (ip, reason, medium)
struct interface_info *ip;
char *reason;
struct string_list *medium;
{
- int fd;
-#ifndef HAVE_MKSTEMP
-
- do {
-#endif
- strcpy (scriptName, "/tmp/dcsXXXXXX");
-#ifdef HAVE_MKSTEMP
- fd = mkstemp (scriptName);
-#else
- if (!mktemp (scriptName))
- error ("can't create temporary client script %s: %m",
- scriptName);
- fd = open (scriptName, O_EXCL | O_CREAT | O_WRONLY, 0600);
- } while (fd < 0 && errno == EEXIST);
-#endif
- if (fd < 0)
- error ("can't create temporary script %s: %m", scriptName);
+ struct string_list *sl, *next;
- scriptFile = fdopen (fd, "w");
- if (!scriptFile)
- error ("can't write script file: %m");
- fprintf (scriptFile, "#!/bin/sh\n\n");
if (ip) {
- fprintf (scriptFile, "interface=\"%s\"\n", ip -> name);
- fprintf (scriptFile, "export interface\n");
- }
- if (medium) {
- fprintf (scriptFile, "medium=\"%s\"\n", medium -> string);
- fprintf (scriptFile, "export medium\n");
+ for (sl = ip -> client -> env; sl; sl = next) {
+ next = sl -> next;
+ dfree (sl, "script_init");
+ }
+ ip -> client -> env = (struct string_list *)0;
+ ip -> client -> envc = 0;
+
+ client_envadd (ip -> client, "", "interface", "%s",
+ ip -> name);
+ if (medium)
+ client_envadd (ip -> client,
+ "", "medium", "%s", medium -> string);
+
+ client_envadd (ip -> client, "", "reason", "%s", reason);
}
- fprintf (scriptFile, "reason=\"%s\"\n", reason);
- fprintf (scriptFile, "export reason\n");
}
void script_write_params (ip, prefix, lease)
struct interface_info *ip;
char *prefix;
struct client_lease *lease;
{
int i;
u_int8_t dbuf [1500];
int len;
- fprintf (scriptFile, "%sip_address=\"%s\"\n",
- prefix, piaddr (lease -> address));
- fprintf (scriptFile, "export %sip_address\n", prefix);
+ client_envadd (ip -> client,
+ prefix, "ip_address", "%s", piaddr (lease -> address));
/* For the benefit of Linux (and operating systems which may
have similar needs), compute the network address based on
the supplied ip address and netmask, if provided. Also
compute the broadcast address (the host address all ones
broadcast address, not the host address all zeroes
broadcast address). */
if (lease -> options [DHO_SUBNET_MASK].len &&
(lease -> options [DHO_SUBNET_MASK].len <
sizeof lease -> address.iabuf)) {
struct iaddr netmask, subnet, broadcast;
memcpy (netmask.iabuf,
lease -> options [DHO_SUBNET_MASK].data,
lease -> options [DHO_SUBNET_MASK].len);
netmask.len = lease -> options [DHO_SUBNET_MASK].len;
subnet = subnet_number (lease -> address, netmask);
if (subnet.len) {
- fprintf (scriptFile, "%snetwork_number=\"%s\";\n",
- prefix, piaddr (subnet));
- fprintf (scriptFile, "export %snetwork_number\n",
- prefix);
+ client_envadd (ip -> client, prefix, "network_number",
+ "%s", piaddr (subnet));
if (!lease -> options [DHO_BROADCAST_ADDRESS].len) {
broadcast = broadcast_addr (subnet, netmask);
if (broadcast.len) {
- fprintf (scriptFile,
- "%s%s=\"%s\";\n", prefix,
- "broadcast_address",
- piaddr (broadcast));
- fprintf (scriptFile,
- "export %s%s\n", prefix,
- "broadcast_address");
+ client_envadd (ip -> client,
+ prefix, "broadcast_address",
+ "%s", piaddr (subnet));
}
}
}
}
- if (lease -> filename) {
- fprintf (scriptFile, "%sfilename=\"%s\";\n",
- prefix, lease -> filename);
- fprintf (scriptFile, "export %sfilename\n", prefix);
- }
- if (lease -> server_name) {
- fprintf (scriptFile, "%sserver_name=\"%s\";\n",
- prefix, lease -> server_name);
- fprintf (scriptFile, "export %sserver_name\n", prefix);
- }
+ if (lease -> filename)
+ client_envadd (ip -> client,
+ prefix, "filename", "%s", lease -> filename);
+ if (lease -> server_name)
+ client_envadd (ip -> client, prefix, "server_name",
+ "%s", lease -> server_name);
for (i = 0; i < 256; i++) {
u_int8_t *dp;
if (ip -> client -> config -> defaults [i].len) {
if (lease -> options [i].len) {
switch (ip -> client ->
config -> default_actions [i]) {
case ACTION_DEFAULT:
dp = lease -> options [i].data;
len = lease -> options [i].len;
break;
case ACTION_SUPERSEDE:
supersede:
dp = ip -> client ->
config -> defaults [i].data;
len = ip -> client ->
config -> defaults [i].len;
break;
case ACTION_PREPEND:
len = (ip -> client ->
config -> defaults [i].len +
lease -> options [i].len);
if (len > sizeof dbuf) {
warn ("no space to %s %s",
"prepend option",
dhcp_options [i].name);
goto supersede;
}
dp = dbuf;
memcpy (dp,
ip -> client ->
config -> defaults [i].data,
ip -> client ->
config -> defaults [i].len);
memcpy (dp + ip -> client ->
config -> defaults [i].len,
lease -> options [i].data,
lease -> options [i].len);
break;
case ACTION_APPEND:
len = (ip -> client ->
config -> defaults [i].len +
lease -> options [i].len);
if (len > sizeof dbuf) {
warn ("no space to %s %s",
"append option",
dhcp_options [i].name);
goto supersede;
}
dp = dbuf;
memcpy (dp,
lease -> options [i].data,
lease -> options [i].len);
memcpy (dp + lease -> options [i].len,
ip -> client ->
config -> defaults [i].data,
ip -> client ->
config -> defaults [i].len);
}
} else {
dp = ip -> client ->
config -> defaults [i].data;
len = ip -> client ->
config -> defaults [i].len;
}
} else if (lease -> options [i].len) {
len = lease -> options [i].len;
dp = lease -> options [i].data;
} else {
len = 0;
}
if (len) {
- char *s = dhcp_option_ev_name (&dhcp_options [i]);
-
- fprintf (scriptFile, "%s%s=\"%s\"\n", prefix, s,
- pretty_print_option (i, dp, len, 0, 0));
- fprintf (scriptFile, "export %s%s\n", prefix, s);
+ char name [256];
+ if (dhcp_option_ev_name (name, sizeof name,
+ &dhcp_options [i])) {
+ client_envadd (ip -> client, prefix, name, "%s",
+ (pretty_print_option (i, dp,
+ len, 0, 0)));
+ }
}
}
- fprintf (scriptFile, "%sexpiry=\"%d\"\n",
- prefix, (int)lease -> expiry); /* XXX */
- fprintf (scriptFile, "export %sexpiry\n", prefix);
+ client_envadd (ip -> client,
+ prefix, "expiry", "%d", (int)(lease -> expiry));
}
int script_go (ip)
struct interface_info *ip;
{
int rval;
+ char *scriptName;
+ char *argv [2];
+ char **envp;
+ char *epp [3];
+ char reason [] = "REASON=NBI";
+ static char client_path [] = CLIENT_PATH;
+ int i;
+ struct string_list *sp, *next;
+ int pid, wpid, wstatus;
- if (ip)
- fprintf (scriptFile, "%s\n",
- ip -> client -> config -> script_name);
- else
- fprintf (scriptFile, "%s\n",
- top_level_config.script_name);
- fprintf (scriptFile, "exit $?\n");
- fclose (scriptFile);
- chmod (scriptName, 0700);
- rval = system (scriptName);
- if (!save_scripts)
- unlink (scriptName);
- return rval;
+ if (ip) {
+ scriptName = ip -> client -> config -> script_name;
+ envp = dmalloc ((ip -> client -> envc + 2) * sizeof (char *),
+ "script_go");
+ if (!envp) {
+ error ("No memory for client script environment.");
+ return 0;
+ }
+ i = 0;
+ for (sp = ip -> client -> env; sp; sp = sp -> next) {
+ envp [i++] = sp -> string;
+ }
+ envp [i++] = client_path;
+ envp [i] = (char *)0;
+ } else {
+ scriptName = top_level_config.script_name;
+ epp [0] = reason;
+ epp [1] = client_path;
+ epp [2] = (char *)0;
+ envp = epp;
+ }
+
+ argv [0] = scriptName;
+ argv [1] = (char *)0;
+
+ pid = fork ();
+ if (pid < 0) {
+ error ("fork: %m");
+ wstatus = 0;
+ } else if (pid) {
+ do {
+ wpid = wait (&wstatus);
+ } while (wpid != pid && wpid > 0);
+ if (wpid < 0) {
+ error ("wait: %m");
+ wstatus = 0;
+ }
+ } else {
+ execve (scriptName, argv, envp);
+ error ("execve (%s, ...): %m", scriptName);
+ exit (0);
+ }
+
+ if (ip) {
+ for (sp = ip -> client -> env; sp; sp = next) {
+ next = sp -> next;
+ dfree (sp, "script_go");
+ }
+ ip -> client -> env = (struct string_list *)0;
+ ip -> client -> envc = 0;
+ dfree (envp, "script_go");
+ }
+ return wstatus & 0xff;
}
-char *dhcp_option_ev_name (option)
+void client_envadd (struct client_state *client,
+ const char *prefix, const char *name, const char *fmt, ...)
+{
+ char spbuf [1024];
+ char *s;
+ unsigned len, i;
+ struct string_list *val;
+ va_list list;
+
+ va_start (list, fmt);
+ len = vsnprintf (spbuf, sizeof spbuf, fmt, list);
+ va_end (list);
+
+ val = dmalloc (strlen (prefix) + strlen (name) + 1 /* = */ +
+ len + sizeof *val, "client_envadd");
+ if (!val)
+ return;
+ s = val -> string;
+ strcpy (s, prefix);
+ strcat (s, name);
+ s += strlen (s);
+ *s++ = '=';
+ if (len >= sizeof spbuf) {
+ va_start (list, fmt);
+ vsnprintf (s, len + 1, fmt, list);
+ va_end (list);
+ } else
+ strcpy (s, spbuf);
+ val -> next = client -> env;
+ client -> env = val;
+ client -> envc++;
+}
+
+int dhcp_option_ev_name (buf, buflen, option)
+ char *buf;
+ unsigned buflen;
struct option *option;
{
- static char evbuf [256];
int i;
- if (strlen (option -> name) + 1 > sizeof evbuf)
- error ("option %s name is larger than static buffer.");
for (i = 0; option -> name [i]; i++) {
+ if (i + 1 == buflen)
+ return 0;
if (option -> name [i] == '-')
- evbuf [i] = '_';
+ buf [i] = '_';
else
- evbuf [i] = option -> name [i];
+ buf [i] = option -> name [i];
}
- evbuf [i] = 0;
- return evbuf;
+ buf [i] = 0;
+ return 1;
}
-
+
void go_daemon ()
{
static int state = 0;
int pid;
/* Don't become a daemon if the user requested otherwise. */
if (no_daemon) {
write_client_pid_file ();
return;
}
/* Only do it once. */
if (state)
return;
state = 1;
/* Stop logging to stderr... */
log_perror = 0;
/* Become a daemon... */
if ((pid = fork ()) < 0)
error ("Can't fork daemon: %m");
else if (pid)
exit (0);
/* Become session leader and get pid... */
pid = setsid ();
/* Close standard I/O descriptors. */
close(0);
close(1);
close(2);
write_client_pid_file ();
}
void write_client_pid_file ()
{
FILE *pf;
int pfdesc;
pfdesc = open (path_dhclient_pid, O_CREAT | O_TRUNC | O_WRONLY, 0644);
if (pfdesc < 0) {
warn ("Can't create %s: %m", path_dhclient_pid);
return;
}
pf = fdopen (pfdesc, "w");
if (!pf)
warn ("Can't fdopen %s: %m", path_dhclient_pid);
else {
fprintf (pf, "%ld\n", (long)getpid ());
fclose (pf);
}
}
diff --git a/contrib/isc-dhcp/includes/dhcpd.h b/contrib/isc-dhcp/includes/dhcpd.h
index ddf34f0ef154..d46ddf1bfae5 100644
--- a/contrib/isc-dhcp/includes/dhcpd.h
+++ b/contrib/isc-dhcp/includes/dhcpd.h
@@ -1,1035 +1,1040 @@
/* dhcpd.h
Definitions for dhcpd... */
/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999
* The Internet Software Consortium. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of The Internet Software Consortium nor the names
* of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
* by Ted Lemon in cooperation with Vixie
* Enterprises. To learn more about the Internet Software Consortium,
* see ``http://www.vix.com/isc''. To learn more about Vixie
* Enterprises, see ``http://www.vix.com''.
*/
#ifndef __CYGWIN32__
#include
#include
#include
#include
#include
#include
#else
#define fd_set cygwin_fd_set
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include "cdefs.h"
#include "osdep.h"
#include "dhcp.h"
#include "tree.h"
#include "hash.h"
#include "inet.h"
#include "sysconf.h"
struct option_data {
int len;
u_int8_t *data;
};
struct string_list {
struct string_list *next;
char string [1];
};
/* A name server, from /etc/resolv.conf. */
struct name_server {
struct name_server *next;
struct sockaddr_in addr;
TIME rcdate;
};
/* A domain search list element. */
struct domain_search_list {
struct domain_search_list *next;
char *domain;
TIME rcdate;
};
/* A dhcp packet and the pointers to its option values. */
struct packet {
struct dhcp_packet *raw;
int packet_length;
int packet_type;
int options_valid;
int client_port;
struct iaddr client_addr;
struct interface_info *interface; /* Interface on which packet
was received. */
struct hardware *haddr; /* Physical link address
of local sender (maybe gateway). */
struct shared_network *shared_network;
struct option_data options [256];
int got_requested_address; /* True if client sent the
dhcp-requested-address option. */
};
struct hardware {
u_int8_t htype;
u_int8_t hlen;
u_int8_t haddr [16];
};
/* A dhcp lease declaration structure. */
struct lease {
struct lease *next;
struct lease *prev;
struct lease *n_uid, *n_hw;
struct lease *waitq_next;
struct iaddr ip_addr;
TIME starts, ends, timestamp;
unsigned char *uid;
int uid_len;
int uid_max;
unsigned char uid_buf [32];
char *hostname;
char *client_hostname;
struct host_decl *host;
struct subnet *subnet;
struct shared_network *shared_network;
struct hardware hardware_addr;
int flags;
# define STATIC_LEASE 1
# define BOOTP_LEASE 2
# define DYNAMIC_BOOTP_OK 4
# define PERSISTENT_FLAGS (DYNAMIC_BOOTP_OK)
# define EPHEMERAL_FLAGS (BOOTP_LEASE)
# define MS_NULL_TERMINATION 8
# define ABANDONED_LEASE 16
struct lease_state *state;
};
struct lease_state {
struct lease_state *next;
struct interface_info *ip;
TIME offered_expiry;
struct tree_cache *options [256];
u_int32_t expiry, renewal, rebind;
char filename [DHCP_FILE_LEN];
char *server_name;
struct iaddr from;
int max_message_size;
u_int8_t *prl;
int prl_len;
int got_requested_address; /* True if client sent the
dhcp-requested-address option. */
int got_server_identifier; /* True if client sent the
dhcp-server-identifier option. */
struct shared_network *shared_network; /* Shared network of interface
on which request arrived. */
u_int32_t xid;
u_int16_t secs;
u_int16_t bootp_flags;
struct in_addr ciaddr;
struct in_addr giaddr;
u_int8_t hops;
u_int8_t offer;
};
#define ROOT_GROUP 0
#define HOST_DECL 1
#define SHARED_NET_DECL 2
#define SUBNET_DECL 3
#define CLASS_DECL 4
#define GROUP_DECL 5
/* Possible modes in which discover_interfaces can run. */
#define DISCOVER_RUNNING 0
#define DISCOVER_SERVER 1
#define DISCOVER_UNCONFIGURED 2
#define DISCOVER_RELAY 3
#define DISCOVER_REQUESTED 4
/* Group of declarations that share common parameters. */
struct group {
struct group *next;
struct subnet *subnet;
struct shared_network *shared_network;
TIME default_lease_time;
TIME max_lease_time;
TIME bootp_lease_cutoff;
TIME bootp_lease_length;
char *filename;
char *server_name;
struct iaddr next_server;
int boot_unknown_clients;
int dynamic_bootp;
int allow_bootp;
int allow_booting;
int one_lease_per_client;
int get_lease_hostnames;
int use_host_decl_names;
int use_lease_addr_for_default_route;
int authoritative;
int always_reply_rfc1048;
struct tree_cache *options [256];
};
/* A dhcp host declaration structure. */
struct host_decl {
struct host_decl *n_ipaddr;
char *name;
struct hardware interface;
struct tree_cache *fixed_addr;
struct group *group;
};
struct shared_network {
struct shared_network *next;
char *name;
struct subnet *subnets;
struct interface_info *interface;
struct lease *leases;
struct lease *insertion_point;
struct lease *last_lease;
struct group *group;
};
struct subnet {
struct subnet *next_subnet;
struct subnet *next_sibling;
struct shared_network *shared_network;
struct interface_info *interface;
struct iaddr interface_address;
struct iaddr net;
struct iaddr netmask;
struct group *group;
};
struct class {
char *name;
struct group *group;
};
/* DHCP client lease structure... */
struct client_lease {
struct client_lease *next; /* Next lease in list. */
TIME expiry, renewal, rebind; /* Lease timeouts. */
struct iaddr address; /* Address being leased. */
char *server_name; /* Name of boot server. */
char *filename; /* Name of file we're supposed to boot. */
struct string_list *medium; /* Network medium. */
unsigned int is_static : 1; /* If set, lease is from config file. */
unsigned int is_bootp: 1; /* If set, lease was aquired with BOOTP. */
struct option_data options [256]; /* Options supplied with lease. */
};
/* Possible states in which the client can be. */
enum dhcp_state {
S_REBOOTING,
S_INIT,
S_SELECTING,
S_REQUESTING,
S_BOUND,
S_RENEWING,
S_REBINDING
};
/* Configuration information from the config file... */
struct client_config {
struct option_data defaults [256]; /* Default values for options. */
enum {
ACTION_DEFAULT, /* Use server value if present,
otherwise default. */
ACTION_SUPERSEDE, /* Always use default. */
ACTION_PREPEND, /* Prepend default to server. */
ACTION_APPEND, /* Append default to server. */
} default_actions [256];
struct option_data send_options [256]; /* Send these to server. */
u_int8_t required_options [256]; /* Options server must supply. */
u_int8_t requested_options [256]; /* Options to request from server. */
int requested_option_count; /* Number of requested options. */
TIME timeout; /* Start to panic if we don't get a
lease in this time period when
SELECTING. */
TIME initial_interval; /* All exponential backoff intervals
start here. */
TIME retry_interval; /* If the protocol failed to produce
an address before the timeout,
try the protocol again after this
many seconds. */
TIME select_interval; /* Wait this many seconds from the
first DHCPDISCOVER before
picking an offered lease. */
TIME reboot_timeout; /* When in INIT-REBOOT, wait this
long before giving up and going
to INIT. */
TIME backoff_cutoff; /* When doing exponential backoff,
never back off to an interval
longer than this amount. */
struct string_list *media; /* Possible network media values. */
char *script_name; /* Name of config script. */
enum { IGNORE, ACCEPT, PREFER } bootp_policy;
/* Ignore, accept or prefer BOOTP
responses. */
struct string_list *medium; /* Current network medium. */
struct iaddrlist *reject_list; /* Servers to reject. */
};
/* Per-interface state used in the dhcp client... */
struct client_state {
struct client_lease *active; /* Currently active lease. */
struct client_lease *new; /* New lease. */
struct client_lease *offered_leases; /* Leases offered to us. */
struct client_lease *leases; /* Leases we currently hold. */
struct client_lease *alias; /* Alias lease. */
enum dhcp_state state; /* Current state for this interface. */
struct iaddr destination; /* Where to send packet. */
u_int32_t xid; /* Transaction ID. */
u_int16_t secs; /* secs value from DHCPDISCOVER. */
TIME first_sending; /* When was first copy sent? */
TIME interval; /* What's the current resend interval? */
struct string_list *medium; /* Last media type tried. */
struct dhcp_packet packet; /* Outgoing DHCP packet. */
int packet_length; /* Actual length of generated packet. */
struct iaddr requested_address; /* Address we would like to get. */
struct client_config *config; /* Information from config file. */
+
+ struct string_list *env; /* Client script environment. */
+ int envc; /* Number of entries in environment. */
};
/* Information about each network interface. */
struct interface_info {
struct interface_info *next; /* Next interface in list... */
struct shared_network *shared_network;
/* Networks connected to this interface. */
struct hardware hw_address; /* Its physical address. */
struct in_addr primary_address; /* Primary interface address. */
char name [IFNAMSIZ]; /* Its name... */
int rfdesc; /* Its read file descriptor. */
int wfdesc; /* Its write file descriptor, if
different. */
unsigned char *rbuf; /* Read buffer, if required. */
size_t rbuf_max; /* Size of read buffer. */
size_t rbuf_offset; /* Current offset into buffer. */
size_t rbuf_len; /* Length of data in buffer. */
struct ifreq *ifp; /* Pointer to ifreq struct. */
u_int32_t flags; /* Control flags... */
#define INTERFACE_REQUESTED 1
#define INTERFACE_AUTOMATIC 2
/* Only used by DHCP client code. */
struct client_state *client;
};
struct hardware_link {
struct hardware_link *next;
char name [IFNAMSIZ];
struct hardware address;
};
struct timeout {
struct timeout *next;
TIME when;
void (*func) PROTO ((void *));
void *what;
};
struct protocol {
struct protocol *next;
int fd;
void (*handler) PROTO ((struct protocol *));
void *local;
};
/* Bitmask of dhcp option codes. */
typedef unsigned char option_mask [16];
/* DHCP Option mask manipulation macros... */
#define OPTION_ZERO(mask) (memset (mask, 0, 16))
#define OPTION_SET(mask, bit) (mask [bit >> 8] |= (1 << (bit & 7)))
#define OPTION_CLR(mask, bit) (mask [bit >> 8] &= ~(1 << (bit & 7)))
#define OPTION_ISSET(mask, bit) (mask [bit >> 8] & (1 << (bit & 7)))
#define OPTION_ISCLR(mask, bit) (!OPTION_ISSET (mask, bit))
/* An option occupies its length plus two header bytes (code and
length) for every 255 bytes that must be stored. */
#define OPTION_SPACE(x) ((x) + 2 * ((x) / 255 + 1))
/* Default path to dhcpd config file. */
#ifdef DEBUG
#undef _PATH_DHCPD_CONF
#define _PATH_DHCPD_CONF "dhcpd.conf"
#undef _PATH_DHCPD_DB
#define _PATH_DHCPD_DB "dhcpd.leases"
#else
#ifndef _PATH_DHCPD_CONF
#define _PATH_DHCPD_CONF "/etc/dhcpd.conf"
#endif
#ifndef _PATH_DHCPD_DB
#define _PATH_DHCPD_DB "/etc/dhcpd.leases"
#endif
#ifndef _PATH_DHCPD_PID
#define _PATH_DHCPD_PID "/var/run/dhcpd.pid"
#endif
#endif
#ifndef _PATH_DHCLIENT_CONF
#define _PATH_DHCLIENT_CONF "/etc/dhclient.conf"
#endif
#ifndef _PATH_DHCLIENT_PID
#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
#endif
#ifndef _PATH_DHCLIENT_DB
#define _PATH_DHCLIENT_DB "/etc/dhclient.leases"
#endif
#ifndef _PATH_RESOLV_CONF
#define _PATH_RESOLV_CONF "/etc/resolv.conf"
#endif
#ifndef _PATH_DHCRELAY_PID
#define _PATH_DHCRELAY_PID "/var/run/dhcrelay.pid"
#endif
#ifndef DHCPD_LOG_FACILITY
#define DHCPD_LOG_FACILITY LOG_DAEMON
#endif
#define MAX_TIME 0x7fffffff
#define MIN_TIME 0
/* External definitions... */
/* options.c */
void parse_options PROTO ((struct packet *));
void parse_option_buffer PROTO ((struct packet *, unsigned char *, int));
int cons_options PROTO ((struct packet *, struct dhcp_packet *, int,
struct tree_cache **, int, int, int,
u_int8_t *, int));
int store_options PROTO ((unsigned char *, int, struct tree_cache **,
unsigned char *, int, int, int, int));
char *pretty_print_option PROTO ((unsigned int,
unsigned char *, int, int, int));
void do_packet PROTO ((struct interface_info *,
struct dhcp_packet *, int,
unsigned int, struct iaddr, struct hardware *));
/* errwarn.c */
extern int warnings_occurred;
void error PROTO ((char *, ...));
int warn PROTO ((char *, ...));
int note PROTO ((char *, ...));
int debug PROTO ((char *, ...));
int parse_warn PROTO ((char *, ...));
/* dhcpd.c */
extern TIME cur_time;
extern struct group root_group;
extern u_int16_t local_port;
extern u_int16_t remote_port;
extern int log_priority;
extern int log_perror;
extern char *path_dhcpd_conf;
extern char *path_dhcpd_db;
extern char *path_dhcpd_pid;
int main PROTO ((int, char **, char **));
void cleanup PROTO ((void));
void lease_pinged PROTO ((struct iaddr, u_int8_t *, int));
void lease_ping_timeout PROTO ((void *));
/* conflex.c */
extern int lexline, lexchar;
extern char *token_line, *tlname;
extern char comments [4096];
extern int comment_index;
extern int eol_token;
void new_parse PROTO ((char *));
int next_token PROTO ((char **, FILE *));
int peek_token PROTO ((char **, FILE *));
/* confpars.c */
int readconf PROTO ((void));
void read_leases PROTO ((void));
int parse_statement PROTO ((FILE *,
struct group *, int, struct host_decl *, int));
void parse_allow_deny PROTO ((FILE *, struct group *, int));
void skip_to_semi PROTO ((FILE *));
int parse_boolean PROTO ((FILE *));
int parse_semi PROTO ((FILE *));
int parse_lbrace PROTO ((FILE *));
void parse_host_declaration PROTO ((FILE *, struct group *));
char *parse_host_name PROTO ((FILE *));
void parse_class_declaration PROTO ((FILE *, struct group *, int));
void parse_lease_time PROTO ((FILE *, TIME *));
void parse_shared_net_declaration PROTO ((FILE *, struct group *));
void parse_subnet_declaration PROTO ((FILE *, struct shared_network *));
void parse_group_declaration PROTO ((FILE *, struct group *));
void parse_hardware_param PROTO ((FILE *, struct hardware *));
char *parse_string PROTO ((FILE *));
struct tree *parse_ip_addr_or_hostname PROTO ((FILE *, int));
struct tree_cache *parse_fixed_addr_param PROTO ((FILE *));
void parse_option_param PROTO ((FILE *, struct group *));
TIME parse_timestamp PROTO ((FILE *));
struct lease *parse_lease_declaration PROTO ((FILE *));
void parse_address_range PROTO ((FILE *, struct subnet *));
TIME parse_date PROTO ((FILE *));
unsigned char *parse_numeric_aggregate PROTO ((FILE *,
unsigned char *, int *,
int, int, int));
void convert_num PROTO ((unsigned char *, char *, int, int));
/* tree.c */
pair cons PROTO ((caddr_t, pair));
struct tree_cache *tree_cache PROTO ((struct tree *));
struct tree *tree_host_lookup PROTO ((char *));
struct dns_host_entry *enter_dns_host PROTO ((char *));
struct tree *tree_const PROTO ((unsigned char *, int));
struct tree *tree_concat PROTO ((struct tree *, struct tree *));
struct tree *tree_limit PROTO ((struct tree *, int));
int tree_evaluate PROTO ((struct tree_cache *));
/* dhcp.c */
extern int outstanding_pings;
void dhcp PROTO ((struct packet *));
void dhcpdiscover PROTO ((struct packet *));
void dhcprequest PROTO ((struct packet *));
void dhcprelease PROTO ((struct packet *));
void dhcpdecline PROTO ((struct packet *));
void dhcpinform PROTO ((struct packet *));
void nak_lease PROTO ((struct packet *, struct iaddr *cip));
void ack_lease PROTO ((struct packet *, struct lease *, unsigned int, TIME));
void dhcp_reply PROTO ((struct lease *));
struct lease *find_lease PROTO ((struct packet *,
struct shared_network *, int *));
struct lease *mockup_lease PROTO ((struct packet *,
struct shared_network *,
struct host_decl *));
/* bootp.c */
void bootp PROTO ((struct packet *));
/* memory.c */
void enter_host PROTO ((struct host_decl *));
struct host_decl *find_hosts_by_haddr PROTO ((int, unsigned char *, int));
struct host_decl *find_hosts_by_uid PROTO ((unsigned char *, int));
struct subnet *find_host_for_network PROTO ((struct host_decl **,
struct iaddr *,
struct shared_network *));
void new_address_range PROTO ((struct iaddr, struct iaddr,
struct subnet *, int));
extern struct subnet *find_grouped_subnet PROTO ((struct shared_network *,
struct iaddr));
extern struct subnet *find_subnet PROTO ((struct iaddr));
void enter_shared_network PROTO ((struct shared_network *));
int subnet_inner_than PROTO ((struct subnet *, struct subnet *, int));
void enter_subnet PROTO ((struct subnet *));
void enter_lease PROTO ((struct lease *));
int supersede_lease PROTO ((struct lease *, struct lease *, int));
void release_lease PROTO ((struct lease *));
void abandon_lease PROTO ((struct lease *, char *));
struct lease *find_lease_by_uid PROTO ((unsigned char *, int));
struct lease *find_lease_by_hw_addr PROTO ((unsigned char *, int));
struct lease *find_lease_by_ip_addr PROTO ((struct iaddr));
void uid_hash_add PROTO ((struct lease *));
void uid_hash_delete PROTO ((struct lease *));
void hw_hash_add PROTO ((struct lease *));
void hw_hash_delete PROTO ((struct lease *));
struct class *add_class PROTO ((int, char *));
struct class *find_class PROTO ((int, unsigned char *, int));
struct group *clone_group PROTO ((struct group *, char *));
void write_leases PROTO ((void));
void dump_subnets PROTO ((void));
/* alloc.c */
VOIDPTR dmalloc PROTO ((int, char *));
void dfree PROTO ((VOIDPTR, char *));
struct packet *new_packet PROTO ((char *));
struct dhcp_packet *new_dhcp_packet PROTO ((char *));
struct tree *new_tree PROTO ((char *));
struct tree_cache *new_tree_cache PROTO ((char *));
struct hash_table *new_hash_table PROTO ((int, char *));
struct hash_bucket *new_hash_bucket PROTO ((char *));
struct lease *new_lease PROTO ((char *));
struct lease *new_leases PROTO ((int, char *));
struct subnet *new_subnet PROTO ((char *));
struct class *new_class PROTO ((char *));
struct shared_network *new_shared_network PROTO ((char *));
struct group *new_group PROTO ((char *));
struct protocol *new_protocol PROTO ((char *));
struct lease_state *new_lease_state PROTO ((char *));
struct domain_search_list *new_domain_search_list PROTO ((char *));
struct name_server *new_name_server PROTO ((char *));
void free_name_server PROTO ((struct name_server *, char *));
void free_domain_search_list PROTO ((struct domain_search_list *, char *));
void free_lease_state PROTO ((struct lease_state *, char *));
void free_protocol PROTO ((struct protocol *, char *));
void free_group PROTO ((struct group *, char *));
void free_shared_network PROTO ((struct shared_network *, char *));
void free_class PROTO ((struct class *, char *));
void free_subnet PROTO ((struct subnet *, char *));
void free_lease PROTO ((struct lease *, char *));
void free_hash_bucket PROTO ((struct hash_bucket *, char *));
void free_hash_table PROTO ((struct hash_table *, char *));
void free_tree_cache PROTO ((struct tree_cache *, char *));
void free_packet PROTO ((struct packet *, char *));
void free_dhcp_packet PROTO ((struct dhcp_packet *, char *));
void free_tree PROTO ((struct tree *, char *));
/* print.c */
char *print_hw_addr PROTO ((int, int, unsigned char *));
void print_lease PROTO ((struct lease *));
void dump_raw PROTO ((unsigned char *, int));
void dump_packet PROTO ((struct packet *));
void hash_dump PROTO ((struct hash_table *));
/* socket.c */
#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_RECEIVE) \
|| defined (USE_SOCKET_FALLBACK)
int if_register_socket PROTO ((struct interface_info *));
#endif
#if defined (USE_SOCKET_FALLBACK) && !defined (USE_SOCKET_SEND)
void if_reinitialize_fallback PROTO ((struct interface_info *));
void if_register_fallback PROTO ((struct interface_info *));
ssize_t send_fallback PROTO ((struct interface_info *,
struct packet *, struct dhcp_packet *, size_t,
struct in_addr,
struct sockaddr_in *, struct hardware *));
#endif
#ifdef USE_SOCKET_SEND
void if_reinitialize_send PROTO ((struct interface_info *));
void if_register_send PROTO ((struct interface_info *));
ssize_t send_packet PROTO ((struct interface_info *,
struct packet *, struct dhcp_packet *, size_t,
struct in_addr,
struct sockaddr_in *, struct hardware *));
#endif
#if defined (USE_SOCKET_FALLBACK)
void fallback_discard PROTO ((struct protocol *));
#endif
#ifdef USE_SOCKET_RECEIVE
void if_reinitialize_receive PROTO ((struct interface_info *));
void if_register_receive PROTO ((struct interface_info *));
ssize_t receive_packet PROTO ((struct interface_info *,
unsigned char *, size_t,
struct sockaddr_in *, struct hardware *));
#endif
#if defined (USE_SOCKET_SEND)
int can_unicast_without_arp PROTO ((void));
int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
void maybe_setup_fallback PROTO ((void));
#endif
/* bpf.c */
#if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE)
int if_register_bpf PROTO ( (struct interface_info *));
#endif
#ifdef USE_BPF_SEND
void if_reinitialize_send PROTO ((struct interface_info *));
void if_register_send PROTO ((struct interface_info *));
ssize_t send_packet PROTO ((struct interface_info *,
struct packet *, struct dhcp_packet *, size_t,
struct in_addr,
struct sockaddr_in *, struct hardware *));
#endif
#ifdef USE_BPF_RECEIVE
void if_reinitialize_receive PROTO ((struct interface_info *));
void if_register_receive PROTO ((struct interface_info *));
ssize_t receive_packet PROTO ((struct interface_info *,
unsigned char *, size_t,
struct sockaddr_in *, struct hardware *));
#endif
#if defined (USE_BPF_SEND)
int can_unicast_without_arp PROTO ((void));
int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
void maybe_setup_fallback PROTO ((void));
#endif
/* lpf.c */
#if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
int if_register_lpf PROTO ( (struct interface_info *));
#endif
#ifdef USE_LPF_SEND
void if_reinitialize_send PROTO ((struct interface_info *));
void if_register_send PROTO ((struct interface_info *));
ssize_t send_packet PROTO ((struct interface_info *,
struct packet *, struct dhcp_packet *, size_t,
struct in_addr,
struct sockaddr_in *, struct hardware *));
#endif
#ifdef USE_LPF_RECEIVE
void if_reinitialize_receive PROTO ((struct interface_info *));
void if_register_receive PROTO ((struct interface_info *));
ssize_t receive_packet PROTO ((struct interface_info *,
unsigned char *, size_t,
struct sockaddr_in *, struct hardware *));
#endif
#if defined (USE_LPF_SEND)
int can_unicast_without_arp PROTO ((void));
int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
void maybe_setup_fallback PROTO ((void));
#endif
/* nit.c */
#if defined (USE_NIT_SEND) || defined (USE_NIT_RECEIVE)
int if_register_nit PROTO ( (struct interface_info *));
#endif
#ifdef USE_NIT_SEND
void if_reinitialize_send PROTO ((struct interface_info *));
void if_register_send PROTO ((struct interface_info *));
ssize_t send_packet PROTO ((struct interface_info *,
struct packet *, struct dhcp_packet *, size_t,
struct in_addr,
struct sockaddr_in *, struct hardware *));
#endif
#ifdef USE_NIT_RECEIVE
void if_reinitialize_receive PROTO ((struct interface_info *));
void if_register_receive PROTO ((struct interface_info *));
ssize_t receive_packet PROTO ((struct interface_info *,
unsigned char *, size_t,
struct sockaddr_in *, struct hardware *));
#endif
#if defined (USE_NIT_SEND)
int can_unicast_without_arp PROTO ((void));
int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
void maybe_setup_fallback PROTO ((void));
#endif
#ifdef USE_DLPI_SEND
void if_reinitialize_send PROTO ((struct interface_info *));
void if_register_send PROTO ((struct interface_info *));
ssize_t send_packet PROTO ((struct interface_info *,
struct packet *, struct dhcp_packet *, size_t,
struct in_addr,
struct sockaddr_in *, struct hardware *));
#endif
#ifdef USE_DLPI_RECEIVE
void if_reinitialize_receive PROTO ((struct interface_info *));
void if_register_receive PROTO ((struct interface_info *));
ssize_t receive_packet PROTO ((struct interface_info *,
unsigned char *, size_t,
struct sockaddr_in *, struct hardware *));
#endif
#if defined (USE_DLPI_SEND)
int can_unicast_without_arp PROTO ((void));
int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
void maybe_setup_fallback PROTO ((void));
#endif
/* raw.c */
#ifdef USE_RAW_SEND
void if_reinitialize_send PROTO ((struct interface_info *));
void if_register_send PROTO ((struct interface_info *));
ssize_t send_packet PROTO ((struct interface_info *,
struct packet *, struct dhcp_packet *, size_t,
struct in_addr,
struct sockaddr_in *, struct hardware *));
int can_unicast_without_arp PROTO ((void));
int can_receive_unicast_unconfigured PROTO ((struct interface_info *));
void maybe_setup_fallback PROTO ((void));
#endif
/* dispatch.c */
extern struct interface_info *interfaces,
*dummy_interfaces, *fallback_interface;
extern struct protocol *protocols;
extern int quiet_interface_discovery;
extern void (*bootp_packet_handler) PROTO ((struct interface_info *,
struct dhcp_packet *, int,
unsigned int,
struct iaddr, struct hardware *));
extern struct timeout *timeouts;
void discover_interfaces PROTO ((int));
struct interface_info *setup_fallback PROTO ((void));
void reinitialize_interfaces PROTO ((void));
void dispatch PROTO ((void));
int locate_network PROTO ((struct packet *));
void got_one PROTO ((struct protocol *));
void add_timeout PROTO ((TIME, void (*) PROTO ((void *)), void *));
void cancel_timeout PROTO ((void (*) PROTO ((void *)), void *));
void add_protocol PROTO ((char *, int,
void (*) PROTO ((struct protocol *)), void *));
void remove_protocol PROTO ((struct protocol *));
/* hash.c */
struct hash_table *new_hash PROTO ((void));
void add_hash PROTO ((struct hash_table *, unsigned char *,
int, unsigned char *));
void delete_hash_entry PROTO ((struct hash_table *, unsigned char *, int));
unsigned char *hash_lookup PROTO ((struct hash_table *, unsigned char *, int));
/* tables.c */
extern struct option dhcp_options [256];
extern unsigned char dhcp_option_default_priority_list [];
extern int sizeof_dhcp_option_default_priority_list;
extern char *hardware_types [256];
extern struct hash_table universe_hash;
extern struct universe dhcp_universe;
void initialize_universes PROTO ((void));
/* convert.c */
u_int32_t getULong PROTO ((unsigned char *));
int32_t getLong PROTO ((unsigned char *));
u_int16_t getUShort PROTO ((unsigned char *));
int16_t getShort PROTO ((unsigned char *));
void putULong PROTO ((unsigned char *, u_int32_t));
void putLong PROTO ((unsigned char *, int32_t));
void putUShort PROTO ((unsigned char *, unsigned int));
void putShort PROTO ((unsigned char *, int));
/* inet.c */
struct iaddr subnet_number PROTO ((struct iaddr, struct iaddr));
struct iaddr ip_addr PROTO ((struct iaddr, struct iaddr, u_int32_t));
struct iaddr broadcast_addr PROTO ((struct iaddr, struct iaddr));
u_int32_t host_addr PROTO ((struct iaddr, struct iaddr));
int addr_eq PROTO ((struct iaddr, struct iaddr));
char *piaddr PROTO ((struct iaddr));
/* dhclient.c */
extern char *path_dhclient_conf;
extern char *path_dhclient_db;
extern char *path_dhclient_pid;
extern int interfaces_requested;
extern struct client_config top_level_config;
void dhcpoffer PROTO ((struct packet *));
void dhcpack PROTO ((struct packet *));
void dhcpnak PROTO ((struct packet *));
void send_discover PROTO ((void *));
void send_request PROTO ((void *));
void send_release PROTO ((void *));
void send_decline PROTO ((void *));
void state_reboot PROTO ((void *));
void state_init PROTO ((void *));
void state_selecting PROTO ((void *));
void state_requesting PROTO ((void *));
void state_bound PROTO ((void *));
void state_panic PROTO ((void *));
void bind_lease PROTO ((struct interface_info *));
void make_discover PROTO ((struct interface_info *, struct client_lease *));
void make_request PROTO ((struct interface_info *, struct client_lease *));
void make_decline PROTO ((struct interface_info *, struct client_lease *));
void make_release PROTO ((struct interface_info *, struct client_lease *));
void free_client_lease PROTO ((struct client_lease *));
void rewrite_client_leases PROTO ((void));
void write_client_lease PROTO ((struct interface_info *,
struct client_lease *, int));
-char *dhcp_option_ev_name PROTO ((struct option *));
void script_init PROTO ((struct interface_info *, char *,
struct string_list *));
void script_write_params PROTO ((struct interface_info *,
char *, struct client_lease *));
int script_go PROTO ((struct interface_info *));
+void client_envadd PROTO ((struct client_state *,
+ const char *, const char *, const char *, ...));
+int dhcp_option_ev_name (char *, size_t, struct option *);
struct client_lease *packet_to_lease PROTO ((struct packet *));
void go_daemon PROTO ((void));
void write_client_pid_file PROTO ((void));
void status_message PROTO ((struct sysconf_header *, void *));
void client_location_changed PROTO ((void));
/* db.c */
int write_lease PROTO ((struct lease *));
int commit_leases PROTO ((void));
void db_startup PROTO ((void));
void new_lease_file PROTO ((void));
/* packet.c */
u_int32_t checksum PROTO ((unsigned char *, int, u_int32_t));
u_int32_t wrapsum PROTO ((u_int32_t));
void assemble_hw_header PROTO ((struct interface_info *, unsigned char *,
int *, struct hardware *));
void assemble_udp_ip_header PROTO ((struct interface_info *, unsigned char *,
int *, u_int32_t, u_int32_t, unsigned int,
unsigned char *, int));
ssize_t decode_hw_header PROTO ((struct interface_info *, unsigned char *,
int, struct hardware *));
ssize_t decode_udp_ip_header PROTO ((struct interface_info *, unsigned char *,
int, struct sockaddr_in *,
unsigned char *, int));
/* ethernet.c */
void assemble_ethernet_header PROTO ((struct interface_info *, unsigned char *,
int *, struct hardware *));
ssize_t decode_ethernet_header PROTO ((struct interface_info *,
unsigned char *,
int, struct hardware *));
/* tr.c */
void assemble_tr_header PROTO ((struct interface_info *, unsigned char *,
int *, struct hardware *));
ssize_t decode_tr_header PROTO ((struct interface_info *,
unsigned char *,
int, struct hardware *));
/* dhxpxlt.c */
void convert_statement PROTO ((FILE *));
void convert_host_statement PROTO ((FILE *, jrefproto));
void convert_host_name PROTO ((FILE *, jrefproto));
void convert_class_statement PROTO ((FILE *, jrefproto, int));
void convert_class_decl PROTO ((FILE *, jrefproto));
void convert_lease_time PROTO ((FILE *, jrefproto, char *));
void convert_shared_net_statement PROTO ((FILE *, jrefproto));
void convert_subnet_statement PROTO ((FILE *, jrefproto));
void convert_subnet_decl PROTO ((FILE *, jrefproto));
void convert_host_decl PROTO ((FILE *, jrefproto));
void convert_hardware_decl PROTO ((FILE *, jrefproto));
void convert_hardware_addr PROTO ((FILE *, jrefproto));
void convert_filename_decl PROTO ((FILE *, jrefproto));
void convert_servername_decl PROTO ((FILE *, jrefproto));
void convert_ip_addr_or_hostname PROTO ((FILE *, jrefproto, int));
void convert_fixed_addr_decl PROTO ((FILE *, jrefproto));
void convert_option_decl PROTO ((FILE *, jrefproto));
void convert_timestamp PROTO ((FILE *, jrefproto));
void convert_lease_statement PROTO ((FILE *, jrefproto));
void convert_address_range PROTO ((FILE *, jrefproto));
void convert_date PROTO ((FILE *, jrefproto, char *));
void convert_numeric_aggregate PROTO ((FILE *, jrefproto, int, int, int, int));
void indent PROTO ((int));
/* route.c */
void add_route_direct PROTO ((struct interface_info *, struct in_addr));
void add_route_net PROTO ((struct interface_info *, struct in_addr,
struct in_addr));
void add_route_default_gateway PROTO ((struct interface_info *,
struct in_addr));
void remove_routes PROTO ((struct in_addr));
void remove_if_route PROTO ((struct interface_info *, struct in_addr));
void remove_all_if_routes PROTO ((struct interface_info *));
void set_netmask PROTO ((struct interface_info *, struct in_addr));
void set_broadcast_addr PROTO ((struct interface_info *, struct in_addr));
void set_ip_address PROTO ((struct interface_info *, struct in_addr));
/* clparse.c */
int read_client_conf PROTO ((void));
void read_client_leases PROTO ((void));
void parse_client_statement PROTO ((FILE *, struct interface_info *,
struct client_config *));
int parse_X PROTO ((FILE *, u_int8_t *, int));
int parse_option_list PROTO ((FILE *, u_int8_t *));
void parse_interface_declaration PROTO ((FILE *, struct client_config *));
struct interface_info *interface_or_dummy PROTO ((char *));
void make_client_state PROTO ((struct interface_info *));
void make_client_config PROTO ((struct interface_info *,
struct client_config *));
void parse_client_lease_statement PROTO ((FILE *, int));
void parse_client_lease_declaration PROTO ((FILE *, struct client_lease *,
struct interface_info **));
struct option *parse_option_decl PROTO ((FILE *, struct option_data *));
void parse_string_list PROTO ((FILE *, struct string_list **, int));
int parse_ip_addr PROTO ((FILE *, struct iaddr *));
void parse_reject_statement PROTO ((FILE *, struct client_config *));
/* dhcrelay.c */
void relay PROTO ((struct interface_info *, struct dhcp_packet *, int,
unsigned int, struct iaddr, struct hardware *));
/* icmp.c */
void icmp_startup PROTO ((int, void (*) PROTO ((struct iaddr,
u_int8_t *, int))));
int icmp_echorequest PROTO ((struct iaddr *));
void icmp_echoreply PROTO ((struct protocol *));
/* dns.c */
void dns_startup PROTO ((void));
int ns_inaddr_lookup PROTO ((u_int16_t, struct iaddr));
void dns_packet PROTO ((struct protocol *));
/* resolv.c */
extern char path_resolv_conf [];
struct name_server *name_servers;
struct domain_search_list *domains;
void read_resolv_conf PROTO ((TIME));
struct sockaddr_in *pick_name_server PROTO ((void));
/* inet_addr.c */
#ifdef NEED_INET_ATON
int inet_aton PROTO ((const char *, struct in_addr *));
#endif
/* sysconf.c */
void sysconf_startup PROTO ((void (*) (struct sysconf_header *, void *)));
void sysconf_restart PROTO ((void *));
void sysconf_message PROTO ((struct protocol *proto));
diff --git a/contrib/isc-dhcp/includes/version.h b/contrib/isc-dhcp/includes/version.h
index ccda68fa1618..70d3d0675177 100644
--- a/contrib/isc-dhcp/includes/version.h
+++ b/contrib/isc-dhcp/includes/version.h
@@ -1,3 +1,3 @@
/* Current version of ISC DHCP Distribution. */
-#define DHCP_VERSION "2.0pl2"
+#define DHCP_VERSION "2.0pl3"