aboutsummaryrefslogtreecommitdiff
path: root/contrib/isc-dhcp/client/dhclient.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/isc-dhcp/client/dhclient.c')
-rw-r--r--contrib/isc-dhcp/client/dhclient.c2052
1 files changed, 2052 insertions, 0 deletions
diff --git a/contrib/isc-dhcp/client/dhclient.c b/contrib/isc-dhcp/client/dhclient.c
new file mode 100644
index 000000000000..eb4d268f9768
--- /dev/null
+++ b/contrib/isc-dhcp/client/dhclient.c
@@ -0,0 +1,2052 @@
+/* dhclient.c
+
+ DHCP Client. */
+
+/*
+ * Copyright (c) 1995, 1996, 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 <mellon@fugue.com> 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 copyright[] =
+"$Id: dhclient.c,v 1.44.2.1 1997/12/06 11:24:31 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n";
+#endif /* not lint */
+
+#include "dhcpd.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) {}
+
+#ifdef USE_FALLBACK
+struct interface_info fallback_interface;
+#endif
+
+u_int16_t local_port;
+u_int16_t remote_port;
+int log_priority;
+int no_daemon;
+int save_scripts;
+
+static void usage PROTO ((void));
+
+int main (argc, argv, envp)
+ int argc;
+ char **argv, **envp;
+{
+ int i;
+ struct servent *ent;
+ struct interface_info *ip;
+ int seed;
+
+#ifdef SYSLOG_4_2
+ openlog ("dhclient", LOG_NDELAY);
+ log_priority = LOG_DAEMON;
+#else
+ openlog ("dhclient", LOG_NDELAY, LOG_DAEMON);
+#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 ();
+ 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 (argv [i][0] == '-') {
+ usage ();
+ } 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]);
+ tmp -> next = interfaces;
+ tmp -> flags = INTERFACE_REQUESTED;
+ interfaces_requested = 1;
+ interfaces = tmp;
+ }
+ }
+ /* 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);
+
+ /* Nothing more to do. */
+ exit (0);
+ } else {
+ /* Call the script with the list of interfaces. */
+ for (ip = interfaces; ip; ip = ip -> next) {
+ 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 ()
+{
+ error ("Usage: dhclient [-c] [-p <port>] [interface]");
+}
+
+void cleanup ()
+{
+}
+
+/* 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 a DHCPREQUEST packet, and set appropriate per-interface
+ flags. */
+ make_request (ip, ip -> client -> active);
+ ip -> client -> xid = ip -> client -> packet.xid;
+ 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;
+ int i;
+
+ /* 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) {
+ debug ("DHCPACK in wrong transaction.");
+ return;
+ }
+
+ if (ip -> client -> state != S_REBOOTING &&
+ ip -> client -> state != S_REQUESTING &&
+ ip -> client -> state != S_RENEWING &&
+ ip -> client -> state != S_REBINDING) {
+ debug ("DHCPACK in wrong state.");
+ 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);
+
+ /* 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;
+ ip -> client -> new -> renewal += cur_time;
+ ip -> client -> new -> rebind += cur_time;
+
+ 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);
+
+ /* 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");
+ struct iaddrlist *ap;
+
+#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) {
+ debug ("%s in wrong transaction.", name);
+ 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) {
+ debug ("DHCPNAK in wrong transaction.");
+ return;
+ }
+
+ if (ip -> client -> state != S_REBOOTING &&
+ ip -> client -> state != S_REQUESTING &&
+ ip -> client -> state != S_RENEWING &&
+ ip -> client -> state != S_REBINDING) {
+ debug ("DHCPNAK in wrong state.");
+ 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 -> interval));
+ } 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 < 255)
+ ip -> client -> packet.secs = interval;
+ else
+ ip -> client -> packet.secs = 255;
+
+ 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);
+ if (result < 0)
+ warn ("send_packet: %m");
+
+ 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. */
+ 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);
+
+ 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 ||
+ 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 (interval < 255)
+ ip -> client -> packet.secs = interval;
+ else
+ ip -> client -> packet.secs = 255;
+
+ note ("DHCPREQUEST on %s to %s port %d", ip -> name,
+ inet_ntoa (destination.sin_addr),
+ ntohs (destination.sin_port));
+
+#ifdef USE_FALLBACK
+ if (destination.sin_addr.s_addr != INADDR_BROADCAST)
+ result = send_fallback (&fallback_interface,
+ (struct packet *)0,
+ &ip -> client -> packet,
+ ip -> client -> packet_length,
+ from, &destination,
+ (struct hardware *)0);
+ else
+#endif /* USE_FALLBACK */
+ /* Send out a packet. */
+ result = send_packet (ip, (struct packet *)0,
+ &ip -> client -> packet,
+ ip -> client -> packet_length,
+ from, &destination,
+ (struct hardware *)0);
+
+ if (result < 0)
+ warn ("send_packet: %m");
+
+ 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);
+ if (result < 0)
+ warn ("send_packet: %m");
+}
+
+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);
+ if (result < 0)
+ warn ("send_packet: %m");
+}
+
+void make_discover (ip, lease)
+ struct interface_info *ip;
+ struct client_lease *lease;
+{
+ struct dhcp_packet *raw;
+ 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,
+ options, 0, 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. */
+ ip -> client -> packet.flags = htons (BOOTP_BROADCAST); /* XXX */
+ 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,
+ options, 0, 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 = htons (BOOTP_BROADCAST);
+
+ /* 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);
+ else
+ 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_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,
+ options, 0, 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 = htons (BOOTP_BROADCAST);
+
+ /* 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 requested_address_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,
+ options, 0, 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 -> packet.xid;
+ ip -> client -> packet.secs = 0;
+ ip -> client -> packet.flags = 0;
+ memcpy (&ip -> client -> packet.ciaddr,
+ lease -> address.iabuf, lease -> address.len);
+ 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 /var/db/dhclient.leases: %m");
+
+ /* 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);
+ }
+ if (ip -> client -> active)
+ write_client_lease (ip, ip -> client -> active);
+ }
+
+ /* 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);
+ }
+ if (ip -> client -> active)
+ write_client_lease (ip, ip -> client -> active);
+ }
+ fflush (leaseFile);
+}
+
+void write_client_lease (ip, lease)
+ struct interface_info *ip;
+ struct client_lease *lease;
+{
+ int i;
+ struct tm *t;
+
+ /* 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 /var/db/dhclient.leases: %m");
+ }
+
+ 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 -> filename);
+ 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));
+ }
+ }
+ 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
+ mktemp (scriptName);
+ fd = creat (scriptName, 0600);
+ } while (fd < 0);
+#endif
+
+ 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");
+ }
+ 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);
+
+ /* 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);
+
+ 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");
+ }
+ }
+ }
+ }
+
+ 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);
+ }
+ 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",
+ "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);
+ }
+ } 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);
+ }
+ }
+ fprintf (scriptFile, "%sexpiry=\"%d\"\n",
+ prefix, (int)lease -> expiry); /* XXX */
+ fprintf (scriptFile, "export %sexpiry\n", prefix);
+}
+
+int script_go (ip)
+ struct interface_info *ip;
+{
+ int rval;
+
+ 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;
+}
+
+char *dhcp_option_ev_name (option)
+ 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 (option -> name [i] == '-')
+ evbuf [i] = '_';
+ else
+ evbuf [i] = option -> name [i];
+ }
+
+ evbuf [i] = 0;
+ return evbuf;
+}
+
+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 ();
+
+ 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);
+ }
+}