]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Initial merge of Poger dhclient and linux hacks
authorTed Lemon <source@isc.org>
Thu, 2 Jan 1997 12:00:19 +0000 (12:00 +0000)
committerTed Lemon <source@isc.org>
Thu, 2 Jan 1997 12:00:19 +0000 (12:00 +0000)
20 files changed:
Makefile.dist
bpf.c
cf/linux.h
cf/netbsd.h
client/dhclient.c
common/bpf.c
common/dispatch.c
common/nit.c
common/socket.c
dhclient.c
dhcp.h
dhcpd.h
dispatch.c
includes/cf/linux.h
includes/cf/netbsd.h
includes/dhcp.h
includes/dhcpd.h
nit.c
route.c
socket.c

index 431fe06d894c800812c9cfd28e868659b3fd9929..8c1d61c24e8ad9e0cfdf4256a62da4178b812356 100644 (file)
@@ -185,10 +185,10 @@ VARDB = /var/db
 
 CSRC   = options.c errwarn.c convert.c \
         tree.c memory.c alloc.c print.c hash.c tables.c inet.c \
-        dispatch.c bpf.c packet.c raw.c nit.c socket.c
+        dispatch.c bpf.c packet.c raw.c nit.c socket.c route.c
 COBJ   = options.o errwarn.o convert.o \
         tree.o memory.o alloc.o print.o hash.o tables.o inet.o \
-        dispatch.o bpf.o packet.o raw.o nit.o socket.o
+        dispatch.o bpf.o packet.o raw.o nit.o socket.o route.o
 XOBJ   = dhcpxlt.o xconflex.o
 SRCS   = dhcpd.c dhcp.c bootp.c conflex.c confpars.c db.c
 OBJS   = dhcpd.o dhcp.o bootp.o conflex.o confpars.o db.o
diff --git a/bpf.c b/bpf.c
index 1ef37958a7a8bafda65bae08277699d4e5832c10..c0d802b8dbfa9ba30f65ac0d53c24eb2d6799008 100644 (file)
--- a/bpf.c
+++ b/bpf.c
@@ -42,7 +42,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: bpf.c,v 1.13 1996/09/02 21:14:58 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: bpf.c,v 1.14 1997/01/02 12:00:14 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -368,3 +368,25 @@ size_t receive_packet (interface, buf, len, from, hfrom)
        return 0;
 }
 #endif
+
+#if defined (USE_BPF_SEND)
+void if_enable (interface)
+       struct interface_info *interface;
+{
+       struct ifreq ifr;
+       int sock;
+
+       if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+               error ("Can't create addrlist socket");
+
+       /* Bring the interface down and then up again to clear
+        * all its routes. */
+       strncpy(ifr.ifr_name, interface -> name, IFNAMSIZ);
+       if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0)
+               error ("SIOCGIFFLAGS %s: %m", interface -> name);
+
+       ifr.ifr_flags |= (IFF_UP|IFF_RUNNING);
+       if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
+               error ("SIOCSIFFLAGS %s: %m", interface -> name);
+}
+#endif
index 8e439d0b745030cd80fe296da8b3100fb5947403..d08a986d00b8b91863a60580691faad870a45f80 100644 (file)
@@ -60,6 +60,9 @@ typedef unsigned long u_int32_t;
 extern int h_errno;
 
 #include <net/if.h>
+#include <net/route.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
 
 #include <sys/time.h>          /* gettimeofday()*/
 #include <linux/time.h>                /* also necessary */
index 23f0b30da235609b606b6f0785f7bf0a94c40b52..6e9ecc7b9efd38b906dd08c5e090dd4a684dc041 100644 (file)
@@ -52,6 +52,10 @@ extern int h_errno;
 
 #include <net/if.h>
 #include <net/if_dl.h>
+#include <net/route.h>
+#include <sys/sockio.h>
+
+#define ifr_netmask ifr_addr
 
 /* Varargs stuff... */
 #include <stdarg.h>
index 8bc551ef9893b2d65c1300111babdde662d30d44..a87773a53df5a6b5f9676e501626c9a5e672654c 100644 (file)
@@ -1,6 +1,6 @@
-/* dhcp.c
+/* dhclient.c
 
-   DHCP Client (really lame DHCP client). */
+   DHCP Client (less lame DHCP client). */
 
 /*
  * Copyright (c) 1995, 1996 The Internet Software Consortium.
  * 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
+ * while he was working on the MosquitoNet project at Stanford.
  */
 
 #ifndef lint
 static char copyright[] =
-"$Id: dhclient.c,v 1.21 1996/09/11 18:53:32 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: dhclient.c,v 1.22 1997/01/02 12:00:14 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -56,6 +59,31 @@ struct iaddr server_identifier;
 int server_identifier_matched;
 int log_perror = 1;
 
+const u_long broadcast_address = INADDR_BROADCAST;
+const int max_retransmissions = 4;
+const int min_delay = 4;
+
+/* Globals for the real_dhcp_client state machine. */
+TIME lease_expiry, T1_expiry, T2_expiry;
+struct packet *sendpacket;
+struct packet *recvpacket;
+struct packet *leasepacket;
+int sendpacket_flag, recvpacket_flag;
+u_long destination;
+
+enum {
+       S_INIT, S_SELECTING, S_REQUESTING, 
+       S_BOUND, S_RENEWING, S_REBINDING
+} state;
+
+/* ASSERT_STATE() does nothing now; it used to be 
+ * assert(state_is==state_shouldbe); */
+#define ASSERT_STATE(state_is, state_shouldbe) {}
+
+int retransmissions_left, retransmission_delay;
+struct interface_info *dhclient_interface;
+
 #ifdef USE_FALLBACK
 struct interface_info fallback_interface;
 #endif
@@ -99,13 +127,24 @@ int main (argc, argv, envp)
                        server_port = htons (atoi (argv [i]));
                        debug ("binding to user-specified port %d",
                               ntohs (server_port));
-               } else
-                       usage ();
+               } 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 = tmp;
+               }
        }
-
        /* Default to the DHCP/BOOTP port. */
-       if (!server_port)
-       {
+       if (!server_port) {
                ent = getservbyname ("dhcpc", "udp");
                if (!ent)
                        server_port = htons (68);
@@ -120,25 +159,544 @@ int main (argc, argv, envp)
        /* Discover all the network interfaces and initialize them. */
        discover_interfaces (0);
 
-       for (interface = interfaces; interface; interface = interface -> next)
-               send_discover (interface);
-
-       /* Receive packets and dispatch them... */
-       dispatch ();
+       /* This is a real DHCP client!
+        * SO, fork off a dhclient state machine for each
+        * interface. */
+       for (dhclient_interface = interfaces; dhclient_interface; 
+            dhclient_interface = dhclient_interface -> next) {
+               if (!dhclient_interface -> next || fork() == 0) {
+                       srandom(cur_time + *(int *)
+                               &dhclient_interface->hw_address.haddr);
+                       dhclient_state_machine();
+                       dhclient_fail();
+               }
+       }
 
-       /* Not reached */
        return 0;
 }
 
+/* This routine should be called when the DHCP client quits for any reason.
+ * It makes the interface unusable and outputs an error message before
+ * exiting.  A warn() message should be given first for specific errors. */
+
+void dhclient_fail()
+{
+       disable_interface(dhclient_interface);
+       error ("dhclient for '%s' halted",
+              dhclient_interface->name);
+}
+
+/* Handles the KILL signal for this DHCP client process.
+ * Sends a DHCPRELEASE message to the server (to be a good neighbor) before
+ * calling failure routine. */
+void handle_kill(dummy)
+     int dummy;
+{
+       warn("dhclient has been killed!");
+       /* XXX figure out whether the lease has expired... */
+       if (leasepacket -> packet_type == DHCPACK) {
+               warn("sending DHCPRELEASE");
+               send_release(leasepacket);
+       }
+       dhclient_fail ();
+}
+
+/* Make this network interface unusable by most programs.  This routine is
+ * called whenever dhclient discovers this host holds no lease.
+ * dhclient must still be able to use it, though, so we can get a lease!!!
+ * Just remove all the routes to this interface, and set its IP address to 
+ * 0.0.0.0.  dhclient will have to find some way to communicate with it. */
+void disable_interface(interface)
+     struct interface_info *interface;
+{
+       struct in_addr zero_addr;
+
+       remove_all_if_routes (interface);
+       zero_addr.s_addr = 0;
+       set_ip_address (interface, zero_addr);
+}
+
+void apply_parameters(interface, packet)
+     struct interface_info *interface;
+     struct packet *packet;
+{
+       struct in_addr net_addr, tmp;
+
+       /* Eventually, set IP address, netmask, router, DNS servers, 
+        * domain name, broadcast addr, etc.
+        *
+        * For now, set IP addr, netmask, and broadcast addr, add gateway,
+        * and update routes.
+        * Call OS-dependent routines to do the dirty work.
+        */
+       
+       note("Setting parameters for %s interface...", interface->name);
+               
+       remove_all_if_routes (interface);
+       set_ip_address (interface, packet -> raw -> yiaddr);
+
+       memcpy (&tmp, packet->options[DHO_SUBNET_MASK].data, sizeof tmp);
+       set_netmask (interface, tmp);
+
+       memcpy (&tmp, packet->options[DHO_BROADCAST_ADDRESS].data, sizeof tmp);
+       set_broadcast_addr(interface, tmp);
+
+       /* network addr = (my IP addr) AND (netmask) */
+       memcpy (&tmp, packet -> options[DHO_SUBNET_MASK].data, sizeof tmp);
+       net_addr.s_addr = packet -> raw -> yiaddr.s_addr & tmp.s_addr;
+       memcpy (&tmp, packet->options[DHO_SUBNET_MASK].data, sizeof tmp);
+       add_route_net(interface, net_addr, tmp);
+
+       memcpy (&tmp, packet->options[DHO_ROUTERS].data, sizeof tmp);
+       add_route_default_gateway(interface, tmp);
+}
+
 static void usage ()
 {
-       error ("Usage: dhclient [-p <port>]");
+       error ("Usage: dhclient [-c] [-p <port>] [interface]");
 }
 
 void cleanup ()
 {
 }
 
+/* The state machine for a true DHCP client, on one particular interface
+ * (stored in global dhclient_interface).
+ * This function should not be exited except on a fatal error. */
+void dhclient_state_machine ()
+{
+       fd_set read_fd;
+       struct timeval tv, timeout;
+       int reset_timer_flag;
+       long int rnd;
+
+       recvpacket = new_packet("recvpacket");
+       recvpacket->raw = new_dhcp_packet("recvpacket->raw");
+       sendpacket = new_packet("sendpacket");
+       sendpacket->raw = new_dhcp_packet("sendpacket->raw");
+       leasepacket = new_packet("leasepacket");
+       leasepacket->raw = new_dhcp_packet("leasepacket->raw");
+
+       /* Figure out when we time out... */
+       gettimeofday (&timeout, (struct timezone *)0);
+       timeout.tv_sec++;
+
+       state = S_INIT;
+       sendpacket_flag = 0;
+       recvpacket_flag = 0;
+       retransmissions_left = 0;
+
+       signal(SIGTERM, handle_kill);
+       signal(SIGINT, handle_kill);
+       while (1) {
+               /* We have awakened for some reason.  Possible reasons are:
+                * -> just entered INIT state (special case)
+                * -> packet received on this port (recvbuffer != NULL)
+                * -> timeout  (we may want to retransmit a request)
+                */
+               GET_TIME (&cur_time);
+               sendpacket_flag = 0;
+
+               if (state == S_INIT) {
+                       reset_timer_flag = state_init();
+               } else if ( (!recvpacket_flag) && (retransmissions_left>0) ) {
+                       /* Timed out; retransmit. */
+                       retransmissions_left--;
+                       retransmission_delay = retransmission_delay*2;
+                       reset_timer_flag = 1;
+                       sendpacket_flag = 1;
+               } else {
+                       /* Either a packet was received 
+                        * or we timed out and ran out of retransmissions.
+                        */
+                       if (recvpacket_flag) {
+                               read_packet(dhclient_interface, recvpacket);
+                       }                       
+                       /* If a packet was received, first check to make
+                        * sure it's relevant to our last request; if it's
+                        * not, ignore it and go back to sleep.
+                        */
+                       if ((recvpacket_flag) && 
+                           ((!recvpacket->options_valid) ||
+                            (recvpacket->raw->op != BOOTREPLY) ||
+                            (recvpacket->raw->xid!=sendpacket->raw->xid) ) ) {
+                               reset_timer_flag = 0;
+                       } else {
+                               note ("%s for %s",
+                                     (recvpacket -> packet_type == 0
+                                      ? "BOOTREPLY" :
+                                      (recvpacket -> packet_type == DHCPOFFER
+                                       ? "DHCPOFFER" :
+                                       (recvpacket -> packet_type == DHCPACK
+                                        ? "DHCPACK" :
+                                        (recvpacket -> packet_type == DHCPNAK
+                                         ? "DHCPNAK" : "DHCP Unknown")))),
+                                     inet_ntoa (recvpacket -> raw -> yiaddr));
+
+                               /* Call the appropriate routine for this state.
+                                * This routine handles both timeout 
+                                * and packet-received cases.
+                                *
+                                * The routine returns 1 if the select()
+                                * timer should be reset (which is ALWAYS the
+                                * case when sending a new packet).
+                                * Otherwise, we just continue our last select.
+                                */
+                               switch (state) {
+                               case S_SELECTING:
+                                       reset_timer_flag = state_selecting();
+                                       break;
+                               case S_REQUESTING:
+                                       reset_timer_flag = state_requesting();
+                                       break;
+                               case S_BOUND:
+                                       reset_timer_flag = state_bound();
+                                       break;
+                               case S_RENEWING:
+                                       reset_timer_flag = state_renewing();
+                                       break;
+                               case S_REBINDING:
+                                       reset_timer_flag = state_rebinding();
+                                       break;
+                               default:
+                                       warn("dhclient entered bad state");
+                                       dhclient_fail();
+                               }
+                       }       
+               }                       
+
+               /* Send a packet and reset the timer OR
+                * set a new timer with no outstanding request OR
+                * just go back to sleep because we're still waiting for
+                * something. */
+               if (reset_timer_flag) {
+                       if (sendpacket_flag) {
+                               send_packet_struct(dhclient_interface, 
+                                                  destination, sendpacket); 
+                       }
+                       rnd = random();
+                       tv.tv_sec = retransmission_delay - 1 + (rnd&1);
+                       tv.tv_usec = rnd % 1000000;
+                       gettimeofday (&timeout, (struct timezone *)0);
+                       timeout.tv_usec += tv.tv_usec;
+                       if (timeout.tv_usec > 1000000) {
+                               timeout.tv_usec -= 1000000;
+                               timeout.tv_sec++;
+                       }
+                       timeout.tv_sec += tv.tv_sec;
+               } else {
+                       gettimeofday (&tv, (struct timezone *)0);
+                       tv.tv_usec = timeout.tv_usec - tv.tv_usec;
+                       if (tv.tv_usec < 0) {
+                               tv.tv_usec += 1000000;
+                               tv.tv_sec--;
+                       }
+                       tv.tv_sec = timeout.tv_sec - tv.tv_sec;
+                       if (tv.tv_sec < 0) {
+                               warn ("timer expired unexpectedly!");
+                               dhclient_fail ();
+                       }
+               }
+
+               FD_ZERO (&read_fd);
+               FD_SET  (dhclient_interface->rfdesc, &read_fd);
+               recvpacket_flag = select(dhclient_interface->rfdesc+1, 
+                                        &read_fd, NULL, NULL, &tv);
+               if (recvpacket_flag < 0) {
+                       warn ("select: %m");
+                       dhclient_fail();
+               }
+       }
+       /* keep cycling through the state machine until dhclient_fail() */
+}
+
+/* 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 global variables are used to keep track of the process:
+ *   recvpacket: most recent packet received
+ *   sendpacket: most recent packet sent or to be sent
+ *   leasepacket: copy of most recent valid lease DHCPACK packet
+ *   recvpacket_flag: recvpacket is not old hat
+ *   sendpacket_flag: sendpacket is to be sent
+ *   destination: IP address to send sendpacket to
+ *   retransmissions_left: # of times remaining to resend this sendpacket
+ *   retransmission_delay: # of seconds to wait before next retransmission
+ *   T1_expiry, T2_expiry, lease_expiry: lease milestones
+ */
+
+int state_init()
+{
+       ASSERT_STATE(state, S_INIT);
+
+       /* Don't let anyone else use this interface until we get a lease,
+        * but make sure we can use it to obtain a lease! */
+       disable_interface(dhclient_interface);
+       if_enable (dhclient_interface);
+
+       make_discover(dhclient_interface, sendpacket);
+       sendpacket_flag = 1;
+       retransmissions_left = max_retransmissions;
+       retransmission_delay = min_delay;
+       destination = broadcast_address;
+       state = S_SELECTING;
+       return 1;
+}
+
+int state_selecting()
+{
+       ASSERT_STATE(state, S_SELECTING);
+
+       if (!recvpacket_flag) {
+               warn("no DHCP server found");
+               dhclient_fail();
+               /*NOTREACHED*/
+               return 0;
+       } else if (recvpacket->packet_type != DHCPOFFER) {
+               return 0; /* wait for a DHCPOFFER */
+       } else {
+               /* Got a DHCPOFFER! */
+               make_request(recvpacket, sendpacket);
+               sendpacket->raw->xid = random();
+               sendpacket_flag = 1;
+               destination = broadcast_address;
+               retransmissions_left = max_retransmissions;
+               retransmission_delay = min_delay;
+               state = S_REQUESTING;
+               return 1;
+       }
+}  
+
+int state_requesting()
+{
+       ASSERT_STATE(state, S_REQUESTING);
+
+       if (!recvpacket_flag) {
+               warn("lost contact with DHCP server");
+               state = S_INIT;
+               return state_init();
+       } else if (recvpacket->packet_type == DHCPNAK) {
+               warn("DHCPOFFER withdrawn");
+               state = S_INIT;
+               return state_init();
+       } else if (recvpacket->packet_type == DHCPACK) {
+               /* We got a lease!  Apply its parameters,
+                * record lease info, and set timer to T1. */
+               deep_packet_copy(leasepacket, recvpacket);
+               apply_parameters(dhclient_interface, leasepacket);
+               lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME);
+               T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME);
+               T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME);
+               retransmissions_left = 0;
+               retransmission_delay = T1_expiry - cur_time;
+               sendpacket_flag = 0; /* just set the timer, that's all */
+               state = S_BOUND;
+               return 1;
+       } else {
+               /* Ignore some superfluous packet. */
+               return 0;
+       }
+}  
+
+int state_bound()
+{
+       ASSERT_STATE(state, S_BOUND);
+
+       if (!recvpacket_flag) {
+               /* T1 has expired. */
+               make_request(leasepacket, sendpacket);
+               sendpacket_flag = 1;
+               /* XXX: should really unicast here, but to whom? */
+               destination = broadcast_address;
+               /* XXX: SHOULD WE DO RETRANSMISSIONS HERE? */
+               retransmissions_left = 0;
+               retransmission_delay = T2_expiry - cur_time;
+               state = S_RENEWING;
+               return 1;
+       } else {
+               /* Ignore some superfluous packet. */
+               return 0;
+       }
+}  
+
+int state_renewing()
+{
+       ASSERT_STATE(state, S_RENEWING);
+
+       if (!recvpacket_flag) {
+               /* T2 has expired. */
+               debug("T2 expired");
+               sendpacket_flag = 1;
+               destination = broadcast_address;
+               /* XXX: SHOULD WE DO RETRANSMISSIONS HERE? */
+               retransmissions_left = 0;
+               retransmission_delay = lease_expiry - cur_time;
+               state = S_REBINDING;
+               return 1;
+       } else if (recvpacket->packet_type == DHCPNAK) {
+               warn("lease renewal denied!");
+               state = S_INIT;
+               return state_init();
+       } else if (recvpacket->packet_type == DHCPACK) {
+               /* Record new lease info, and set timer to T1. */
+               debug("lease renewed");
+               deep_packet_copy(leasepacket, recvpacket);
+               lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME);
+               T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME);
+               T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME);
+               retransmissions_left = 0;
+               retransmission_delay = T1_expiry - cur_time;
+               sendpacket_flag = 0; /* just set the timer, that's all */
+               state = S_BOUND;
+               return 1;
+       } else {
+               /* Ignore some superfluous packet. */
+               return 0;
+       }
+}  
+
+int state_rebinding()
+{
+       ASSERT_STATE(state, S_REBINDING);
+
+       if (!recvpacket_flag) {
+                       /* Lease has expired! */
+               warn("DHCP lease expired!");
+               state = S_INIT;
+               return state_init();
+       } else if (recvpacket->packet_type == DHCPNAK) {
+               warn("lease rebinding denied!");
+               state = S_INIT;
+               return state_init();
+       } else if (recvpacket->packet_type == DHCPACK) {
+               /* Record new lease info, and set timer to T1. */
+               debug("lease rebound");
+               deep_packet_copy(leasepacket, recvpacket);
+               lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME);
+               T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME);
+               T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME);
+               retransmissions_left = 0;
+               retransmission_delay = T1_expiry - cur_time;
+               sendpacket_flag = 0; /* just set the timer, that's all */
+               state = S_BOUND;
+               return 1;
+       } else {
+               /* Ignore some superfluous packet. */
+               return 0;
+       }
+}  
+
+/* Read the time offset from a given DHCP option and return it as an
+ * absolute moment in time (offset from cur_time). */
+TIME abs_time(pkt, optnum)
+     struct packet *pkt;
+     int optnum;
+{
+       if (pkt == NULL) {
+               error ("Bad parameter passed to abs_time");
+       }
+
+       return cur_time + 
+               (TIME) ntohl(*((u_int32_t *)pkt->options[optnum].data));
+}
+
+/* This little number lets you record an interesting packet's contents in
+ * another (pre-allocated) packet struct. */
+/* XXX: what about haddr? */
+void deep_packet_copy(to, from)
+     struct packet *to;
+     struct packet *from;
+{
+       struct dhcp_packet *raw;
+
+       if ((to==NULL) || (to->raw==NULL)) {
+               error("Bad parameter passed to deep_packet_copy");
+       }
+
+       raw = to->raw;
+       memcpy (to->raw, from->raw, sizeof *from->raw);
+       memcpy (to, from, sizeof *from);
+       to->raw = raw;
+}
+
+
+/* Read a DHCP packet from this interface into this (pre-allocated) packet. 
+ * If the data on this interface is bogus (non-DHCP), 
+ * set pkt->options_valid = 0.
+ * Much of this code is stolen from dispatch.c.
+ */
+void read_packet(interface, pkt) 
+     struct interface_info *interface;
+     struct packet *pkt;
+{
+       struct dhcp_packet *dhcp_pkt;
+       struct sockaddr_in from;
+       struct hardware *hfrom;
+       struct hardware fudge_factor; /* XXX */
+       struct iaddr ifrom;
+       int len;
+       static unsigned char packbuf [4095]; /* Packet input buffer.
+                                               Must be as large as largest
+                                               possible MTU. */
+
+       if (pkt==NULL) {
+               error("Bad parameter passed to read_packet");
+       }
+       pkt->haddr = &fudge_factor;
+       if ((pkt->haddr==NULL) || (pkt->raw==NULL)) {
+               error("Bad parameter passed to read_packet");
+       }
+
+       hfrom = pkt->haddr;
+       if ((len = receive_packet (interface, packbuf, sizeof packbuf,
+                                     &from, hfrom)) < 0) {
+               warn ("receive_packet failed on %s: %m", interface -> name);
+               pkt->options_valid = 0;
+               return;
+       }
+       if (len == 0) {
+               pkt->options_valid = 0;
+               return;
+       }
+
+       ifrom.len = 4;
+       memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len);
+       
+       dhcp_pkt = pkt->raw;
+       memcpy (dhcp_pkt, packbuf, len);
+       memset (pkt, 0, sizeof (struct packet));
+       pkt->raw = dhcp_pkt;
+       pkt->packet_length = len;
+       pkt->client_port = from.sin_port;
+       pkt->client_addr = ifrom;
+       pkt->interface = interface;
+       pkt->haddr = hfrom;
+       
+       parse_options (pkt);
+       if (pkt->options_valid &&
+           pkt->options [DHO_DHCP_MESSAGE_TYPE].data) {
+               pkt->packet_type =
+                       pkt->options [DHO_DHCP_MESSAGE_TYPE].data [0];
+       } else {
+               pkt->options_valid = 0;
+       }
+       pkt->haddr = NULL; /* XXX */
+
+#ifdef DEBUG
+       dump_packet(pkt);
+#endif
+}
+
 int commit_leases ()
 {
        return 0;
@@ -193,6 +751,7 @@ void dhcpoffer (packet)
                             packet -> raw -> chaddr));
 
        dump_packet (packet);
+       note ("DHCPREQUEST to %s", packet -> interface -> name);
        send_request (packet);
 }
 
@@ -215,34 +774,109 @@ void dhcpnak (packet)
                             packet -> raw -> chaddr));
 }
 
-static u_int8_t requested_options [] = {
-       DHO_DHCP_REQUESTED_ADDRESS,
-       DHO_SUBNET_MASK,
-       DHO_ROUTERS,
-       DHO_DOMAIN_NAME_SERVERS,
-       DHO_HOST_NAME,
-       DHO_DOMAIN_NAME,
-       DHO_BROADCAST_ADDRESS };
-
 void send_discover (interface)
        struct interface_info *interface;
+{
+       struct packet outgoing;
+       struct dhcp_packet raw;
+
+       outgoing.raw = &raw;
+       make_discover (interface, &outgoing);
+       send_packet_struct (interface, htonl(INADDR_BROADCAST), &outgoing);
+}
+
+void send_request (packet)
+       struct packet *packet; /* incoming packet we're responding to */
+{
+       struct packet outgoing;
+       struct dhcp_packet raw;
+
+       outgoing.raw = &raw;
+       make_request (packet, &outgoing);
+       send_packet_struct (packet->interface, htonl(INADDR_BROADCAST), 
+                           &outgoing);
+}
+
+void send_release (packet)
+       struct packet *packet; /* DHCPACK packet from lease we're releasing */
+{
+       struct packet outgoing;
+       struct dhcp_packet raw;
+
+       outgoing.raw = &raw;
+       make_release (packet, &outgoing);
+       send_packet_struct (packet->interface, htonl(INADDR_BROADCAST), 
+                           &outgoing);
+}
+
+/* Send the given packet to the given host IPaddr, over the given interface. */
+void send_packet_struct (interface, dest_host, sendpkt)
+       struct interface_info *interface;
+       u_long dest_host;  /* in network order! */
+       struct packet *sendpkt;
 {
        struct sockaddr_in to;
        int result;
-       struct dhcp_packet raw;
+       char dhcpbuf [128];
+
+       /* Set up the common stuff... */
+       to.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+       to.sin_len = sizeof to;
+#endif
+       memset (to.sin_zero, 0, sizeof to.sin_zero);
+
+       to.sin_addr.s_addr = dest_host;
+       to.sin_port = htons (ntohs (server_port) - 1); /* XXX */
+
+       sprintf (dhcpbuf, "DHCP Unknown %d", sendpkt -> packet_type);
+       note ("%s to %s",
+             sendpkt -> packet_type == DHCPDISCOVER ? "DHCPDISCOVER"
+             : (sendpkt -> packet_type == DHCPREQUEST ? "DHCPREQUEST"
+                : (sendpkt -> packet_type == DHCPNAK ? "DHCPNAK"
+                   : (sendpkt -> packet_type == DHCPINFORM ? "DHCPINFORM"
+                      : dhcpbuf))),
+             inet_ntoa (to.sin_addr));
+             
+       if (sendpkt->packet_length < DHCP_MIN_LEN)
+               sendpkt->packet_length = DHCP_MIN_LEN;
+
+       errno = 0;
+       result = send_packet (interface, (struct packet *)0,
+                             sendpkt->raw, sendpkt->packet_length,
+                             sendpkt->raw->siaddr, &to, (struct hardware *)0);
+       /* XXX: is the above 'siaddr' correct? */
+       if (result < 0)
+               warn ("send_packet: %m");
+}
+
+void make_discover (interface, sendpkt)
+       struct interface_info *interface;
+       struct packet *sendpkt;
+{
+       struct dhcp_packet *raw;
        unsigned char discover = DHCPDISCOVER;
-       struct packet outgoing;
 
        struct tree_cache *options [256];
        struct tree_cache dhcpdiscover_tree;
        struct tree_cache dhcprqo_tree;
 
+       u_int8_t requested_options [] = {
+               DHO_SUBNET_MASK,
+               DHO_ROUTERS,
+               DHO_DOMAIN_NAME_SERVERS,
+               DHO_HOST_NAME,
+               DHO_DOMAIN_NAME,
+               DHO_BROADCAST_ADDRESS };
+
+       raw = sendpkt->raw;
        memset (options, 0, sizeof options);
-       memset (&outgoing, 0, sizeof outgoing);
-       memset (&raw, 0, sizeof raw);
-       outgoing.raw = &raw;
+       memset (sendpkt, 0, sizeof (*sendpkt));
+       memset (raw, 0, sizeof (*raw));
+       sendpkt->raw = raw;
+       sendpkt -> packet_type = DHCPDISCOVER;
 
-       /* Set DHCP_MESSAGE_TYPE to DHCPNAK */
+       /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */
        options [DHO_DHCP_MESSAGE_TYPE] = &dhcpdiscover_tree;
        options [DHO_DHCP_MESSAGE_TYPE] -> value = &discover;
        options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof discover;
@@ -250,65 +884,44 @@ void send_discover (interface)
        options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF;
        options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0;
 
-       /* Set DHCP_MESSAGE to whatever the message is */
-       options [DHO_DHCP_MESSAGE] = &dhcprqo_tree;
-       options [DHO_DHCP_MESSAGE] -> value = requested_options;
-       options [DHO_DHCP_MESSAGE] -> len = sizeof requested_options;
-       options [DHO_DHCP_MESSAGE] -> buf_size = sizeof requested_options;
-       options [DHO_DHCP_MESSAGE] -> timeout = 0xFFFFFFFF;
-       options [DHO_DHCP_MESSAGE] -> tree = (struct tree *)0;
+       /* Request the parameters we want */
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] = &dhcprqo_tree;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> value = requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> len = sizeof requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> buf_size = sizeof requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> timeout = 0xFFFFFFFF;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> tree = (struct tree *)0;
 
        /* Set up the option buffer... */
-       cons_options ((struct packet *)0, &outgoing, options, 0, 0);
+       cons_options ((struct packet *)0, sendpkt, options, 0, 0);
 
-       memset (&raw.ciaddr, 0, sizeof raw.ciaddr);
-       memset (&raw.siaddr, 0, sizeof raw.siaddr);
-       memset (&raw.giaddr, 0, sizeof raw.giaddr);
-       memcpy (raw.chaddr,
+       raw->op = BOOTREQUEST;
+       raw->htype = interface -> hw_address.htype;
+       raw->hlen = interface -> hw_address.hlen;
+       raw->hops = 0;
+       raw->xid = random ();
+       raw->secs = 0; /* XXX */
+       raw->flags = htons (BOOTP_BROADCAST);
+       memset (&(raw->ciaddr), 0, sizeof raw->ciaddr);
+       memset (&(raw->yiaddr), 0, sizeof raw->yiaddr);
+       memset (&(raw->siaddr), 0, sizeof raw->siaddr);
+       memset (&(raw->giaddr), 0, sizeof raw->giaddr);
+       memcpy (raw->chaddr,
                interface -> hw_address.haddr, interface -> hw_address.hlen);
-       raw.hlen = interface -> hw_address.hlen;
-       raw.htype = interface -> hw_address.htype;
-
-       raw.xid = random ();
-       raw.secs = 1;
-       raw.flags = htons (BOOTP_BROADCAST);
-       raw.hops = 0;
-       raw.op = BOOTREQUEST;
-
-       /* Report what we're sending... */
-       note ("DHCPDISCOVER to %s", interface -> name);
 
 #ifdef DEBUG_PACKET
-       dump_packet (&outgoing);
-       dump_raw ((unsigned char *)&raw, outgoing.packet_length);
+       dump_packet (sendpkt);
+       dump_raw ((unsigned char *)raw, sendpkt->packet_length);
 #endif
-
-       /* Set up the common stuff... */
-       to.sin_family = AF_INET;
-#ifdef HAVE_SA_LEN
-       to.sin_len = sizeof to;
-#endif
-       memset (to.sin_zero, 0, sizeof to.sin_zero);
-
-       to.sin_addr.s_addr = htonl (INADDR_BROADCAST);
-       to.sin_port = htons (ntohs (server_port) - 1); /* XXX */
-
-       errno = 0;
-       result = send_packet (interface, (struct packet *)0,
-                             &raw, outgoing.packet_length,
-                             raw.siaddr, &to, (struct hardware *)0);
-       if (result < 0)
-               warn ("send_packet: %m");
 }
 
-void send_request (packet)
-       struct packet *packet;
+
+void make_request (recvpkt, sendpkt)
+     struct packet *recvpkt;
+     struct packet *sendpkt;
 {
-       struct sockaddr_in to;
-       int result;
-       struct dhcp_packet raw;
+       struct dhcp_packet *raw;
        unsigned char request = DHCPREQUEST;
-       struct packet outgoing;
 
        struct tree_cache *options [256];
        struct tree_cache dhcprequest_tree;
@@ -316,12 +929,23 @@ void send_request (packet)
        struct tree_cache dhcprqa_tree;
        struct tree_cache dhcpsid_tree;
 
+       u_int8_t requested_options [] = {
+               DHO_SUBNET_MASK,
+               DHO_ROUTERS,
+               DHO_DOMAIN_NAME_SERVERS,
+               DHO_HOST_NAME,
+               DHO_DOMAIN_NAME,
+               DHO_BROADCAST_ADDRESS };
+
+       raw = sendpkt->raw;
        memset (options, 0, sizeof options);
-       memset (&outgoing, 0, sizeof outgoing);
-       memset (&raw, 0, sizeof raw);
-       outgoing.raw = &raw;
+       memset (sendpkt, 0, sizeof (*sendpkt));
+       memset (raw, 0, sizeof (*raw));
+       sendpkt->raw = raw;
 
-       /* Set DHCP_MESSAGE_TYPE to DHCPNAK */
+       sendpkt -> packet_type = DHCPREQUEST;
+
+       /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */
        options [DHO_DHCP_MESSAGE_TYPE] = &dhcprequest_tree;
        options [DHO_DHCP_MESSAGE_TYPE] -> value = &request;
        options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof request;
@@ -329,74 +953,113 @@ void send_request (packet)
        options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF;
        options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0;
 
-       /* Set DHCP_MESSAGE to whatever the message is */
-       options [DHO_DHCP_MESSAGE] = &dhcprqo_tree;
-       options [DHO_DHCP_MESSAGE] -> value = requested_options;
-       options [DHO_DHCP_MESSAGE] -> len = sizeof requested_options;
-       options [DHO_DHCP_MESSAGE] -> buf_size = sizeof requested_options;
-       options [DHO_DHCP_MESSAGE] -> timeout = 0xFFFFFFFF;
-       options [DHO_DHCP_MESSAGE] -> tree = (struct tree *)0;
-
-       /* Request the address we were offered... */
-       options [DHO_DHCP_REQUESTED_ADDRESS] = &dhcprqa_tree;
-       options [DHO_DHCP_REQUESTED_ADDRESS] -> value = 
-               (unsigned char *)&packet -> raw -> yiaddr;
-       options [DHO_DHCP_REQUESTED_ADDRESS] -> len = 4;
-       options [DHO_DHCP_REQUESTED_ADDRESS] -> buf_size = 4;
-       options [DHO_DHCP_REQUESTED_ADDRESS] -> timeout = 0xFFFFFFFF;
-       options [DHO_DHCP_REQUESTED_ADDRESS] -> tree = (struct tree *)0;
+       /* Request the parameters we want */
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] = &dhcprqo_tree;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> value = requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> len = sizeof requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> buf_size = sizeof requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> timeout = 0xFFFFFFFF;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> tree = (struct tree *)0;
 
        /* Send back the server identifier... */
         options [DHO_DHCP_SERVER_IDENTIFIER] = &dhcpsid_tree;
         options [DHO_DHCP_SERVER_IDENTIFIER] -> value =
-                packet -> options [DHO_DHCP_SERVER_IDENTIFIER].data;
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].data;
         options [DHO_DHCP_SERVER_IDENTIFIER] -> len =
-                packet -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
         options [DHO_DHCP_SERVER_IDENTIFIER] -> buf_size =
-                packet -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
         options [DHO_DHCP_SERVER_IDENTIFIER] -> timeout = 0xFFFFFFFF;
         options [DHO_DHCP_SERVER_IDENTIFIER] -> tree = (struct tree *)0;
 
        /* Set up the option buffer... */
-       cons_options ((struct packet *)0, &outgoing, options, 0, 0);
-
-       memset (&raw.ciaddr, 0, sizeof raw.ciaddr);
-       raw.siaddr = packet -> raw -> siaddr;
-       raw.giaddr = packet -> raw -> giaddr;
-       memcpy (raw.chaddr,
-               packet -> interface -> hw_address.haddr,
-               packet -> interface -> hw_address.hlen);
-       raw.hlen = packet -> interface -> hw_address.hlen;
-       raw.htype = packet -> interface -> hw_address.htype;
-
-       raw.xid = packet -> raw -> xid;
-       raw.secs = packet -> raw -> secs;
-       raw.flags = htons (BOOTP_BROADCAST);
-       raw.hops = packet -> raw -> hops;
-       raw.op = BOOTREQUEST;
-
-       /* Report what we're sending... */
-       note ("DHCPREQUEST to %s", packet -> interface -> name);
+       cons_options ((struct packet *)0, sendpkt, options, 0, 0);
+
+       raw->op = BOOTREQUEST;
+       raw->htype = recvpkt -> interface -> hw_address.htype;
+       raw->hlen = recvpkt -> interface -> hw_address.hlen;
+       raw->hops = 0;
+       raw->xid = recvpkt -> raw -> xid;
+       raw->secs = recvpkt -> raw -> secs; /* XXX */
+       raw->flags = htons (BOOTP_BROADCAST);
+       raw->ciaddr = recvpkt -> raw -> yiaddr; /* XXX ????? */
+       memset (&raw->yiaddr, 0, sizeof raw->yiaddr);
+       memset (&raw->siaddr, 0, sizeof raw->siaddr);
+       memset (&raw->giaddr, 0, sizeof raw->giaddr);
+       memcpy (raw->chaddr,
+               recvpkt -> interface -> hw_address.haddr,
+               recvpkt -> interface -> hw_address.hlen);
 
 #ifdef DEBUG_PACKET
-       dump_packet (&outgoing);
-       dump_raw ((unsigned char *)&raw, outgoing.packet_length);
+       dump_packet (sendpkt);
+       dump_raw ((unsigned char *)raw, sendpkt->packet_length);
 #endif
+}
 
-       /* Set up the common stuff... */
-       to.sin_family = AF_INET;
-#ifdef HAVE_SA_LEN
-       to.sin_len = sizeof to;
-#endif
-       memset (to.sin_zero, 0, sizeof to.sin_zero);
+void make_release (recvpkt, sendpkt)
+     struct packet *recvpkt;
+     struct packet *sendpkt;
+{
+       struct dhcp_packet *raw;
+       unsigned char request = DHCPRELEASE;
 
-       to.sin_addr.s_addr = htonl (INADDR_BROADCAST);
-       to.sin_port = htons (ntohs (server_port) - 1); /* XXX */
+       struct tree_cache *options [256];
+       struct tree_cache dhcprequest_tree;
+       struct tree_cache dhcprqo_tree;
+       struct tree_cache dhcprqa_tree;
+       struct tree_cache dhcpsid_tree;
 
-       errno = 0;
-       result = send_packet (packet -> interface, (struct packet *)0,
-                             &raw, outgoing.packet_length,
-                             raw.siaddr, &to, (struct hardware *)0);
-       if (result < 0)
-               warn ("send_packet: %m");
+       raw = sendpkt->raw;
+       memset (options, 0, sizeof options);
+       memset (sendpkt, 0, sizeof (*sendpkt));
+       memset (raw, 0, sizeof (*raw));
+       sendpkt->raw = raw;
+
+       sendpkt -> packet_type = DHCPRELEASE;
+
+       /* Set DHCP_MESSAGE_TYPE to DHCPRELEASE */
+       options [DHO_DHCP_MESSAGE_TYPE] = &dhcprequest_tree;
+       options [DHO_DHCP_MESSAGE_TYPE] -> value = &request;
+       options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof request;
+       options [DHO_DHCP_MESSAGE_TYPE] -> buf_size = sizeof request;
+       options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF;
+       options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0;
+
+       /* Send back the server identifier... */
+        options [DHO_DHCP_SERVER_IDENTIFIER] = &dhcpsid_tree;
+        options [DHO_DHCP_SERVER_IDENTIFIER] -> value =
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].data;
+        options [DHO_DHCP_SERVER_IDENTIFIER] -> len =
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
+        options [DHO_DHCP_SERVER_IDENTIFIER] -> buf_size =
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
+        options [DHO_DHCP_SERVER_IDENTIFIER] -> timeout = 0xFFFFFFFF;
+        options [DHO_DHCP_SERVER_IDENTIFIER] -> tree = (struct tree *)0;
+
+       /* Set DHCP_MESSAGE to whatever the message is */
+       /* XXX */
+
+       /* Set up the option buffer... */
+       cons_options ((struct packet *)0, sendpkt, options, 0, 0);
+
+       raw->op = BOOTREQUEST;
+       raw->htype = recvpkt -> interface -> hw_address.htype;
+       raw->hlen = recvpkt -> interface -> hw_address.hlen;
+       raw->hops = 0;
+       raw->xid = recvpkt -> raw -> xid;
+       raw->secs = 0;
+       raw->flags = 0;
+       raw->ciaddr = recvpkt -> raw -> yiaddr;
+       memset (&raw->yiaddr, 0, sizeof raw->yiaddr);
+       memset (&raw->siaddr, 0, sizeof raw->siaddr);
+       memset (&raw->giaddr, 0, sizeof raw->giaddr);
+       memcpy (raw->chaddr,
+               recvpkt -> interface -> hw_address.haddr,
+               recvpkt -> interface -> hw_address.hlen);
+
+
+#ifdef DEBUG_PACKET
+       dump_packet (sendpkt);
+       dump_raw ((unsigned char *)raw, sendpkt->packet_length);
+#endif
 }
index 1ef37958a7a8bafda65bae08277699d4e5832c10..c0d802b8dbfa9ba30f65ac0d53c24eb2d6799008 100644 (file)
@@ -42,7 +42,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: bpf.c,v 1.13 1996/09/02 21:14:58 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: bpf.c,v 1.14 1997/01/02 12:00:14 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -368,3 +368,25 @@ size_t receive_packet (interface, buf, len, from, hfrom)
        return 0;
 }
 #endif
+
+#if defined (USE_BPF_SEND)
+void if_enable (interface)
+       struct interface_info *interface;
+{
+       struct ifreq ifr;
+       int sock;
+
+       if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+               error ("Can't create addrlist socket");
+
+       /* Bring the interface down and then up again to clear
+        * all its routes. */
+       strncpy(ifr.ifr_name, interface -> name, IFNAMSIZ);
+       if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0)
+               error ("SIOCGIFFLAGS %s: %m", interface -> name);
+
+       ifr.ifr_flags |= (IFF_UP|IFF_RUNNING);
+       if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
+               error ("SIOCSIFFLAGS %s: %m", interface -> name);
+}
+#endif
index 1985fb4e99419cda151e9572bdcd31592477a534..929c8b3d4cc284ba6d4244df64e6d89ce9932a93 100644 (file)
@@ -42,7 +42,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: dispatch.c,v 1.27 1996/11/08 20:06:29 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: dispatch.c,v 1.28 1997/01/02 12:00:16 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -162,6 +162,44 @@ void discover_interfaces (serverP)
                if (ifp -> ifr_addr.sa_family == AF_INET) {
                        struct iaddr addr;
 
+#if defined (SIOCGIFHWADDR) && !defined (AF_LINK)
+                       struct ifreq ifr;
+                       struct sockaddr sa;
+                       int b, sk;
+                       
+                       /* Read the hardware address from this interface. */
+                       ifr = *ifp;
+                       if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0)
+                               error ("Can't get hardware address for %s: %m",
+                                      ifr.ifr_name);
+
+                       sa = *(struct sockaddr *)&ifr.ifr_hwaddr;
+                                       
+                       switch (sa.sa_family) {
+                             case ARPHRD_LOOPBACK:
+                               /* ignore loopback interface */
+                               break;
+
+                             case ARPHRD_ETHER:
+                               tmp -> hw_address.hlen = 6;
+                               tmp -> hw_address.htype = ARPHRD_ETHER;
+                               memcpy (tmp -> hw_address.haddr,
+                                       sa.sa_data, 6);
+                               break;
+
+                             case ARPHRD_METRICOM:
+                               tmp -> hw_address.hlen = 6;
+                               tmp -> hw_address.htype = ARPHRD_METRICOM;
+                               memcpy (tmp -> hw_address.haddr,
+                                       sa.sa_data, 6);
+                               break;
+
+                             default:
+                               error ("%s: unknown hardware address type %d",
+                                      ifr.ifr_name, sa.sa_family);
+                       }
+#endif /* defined (SIOCGIFHWADDR) && !defined (AF_LINK) */
+
                        /* Get a pointer to the address... */
                        memcpy (&foo, &ifp -> ifr_addr,
                                sizeof ifp -> ifr_addr);
index 43fa7e49638ad5fa50cb0f7d62bb92fe74ae8add..0de8d62bce0815b36028191eadbfb911b8fad547 100644 (file)
@@ -42,7 +42,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: nit.c,v 1.9 1996/09/05 23:56:52 mellon Exp $ Copyright (c) 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: nit.c,v 1.10 1997/01/02 12:00:17 mellon Exp $ Copyright (c) 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -329,3 +329,21 @@ size_t receive_packet (interface, buf, len, from, hfrom)
        return length;
 }
 #endif
+
+#if defined (USE_NIT_SEND)
+void if_enable (interface)
+       struct interface_info *interface;
+{
+       struct ifreq ifr;
+
+       /* Bring the interface down and then up again to clear
+        * all its routes. */
+       strncpy(ifr.ifr_name, interface -> name, IFNAMSIZ);
+       if (ioctl(interface -> rfdesc, SIOCGIFFLAGS, &ifr) < 0)
+               error ("SIOCGIFFLAGS %s: %m", interface -> name);
+
+       ifr.ifr_flags |= (IFF_UP|IFF_RUNNING);
+       if (ioctl(interface -> rfdesc, SIOCSIFFLAGS, &ifr) == -1)
+               error ("SIOCSIFFLAGS %s: %m", interface -> name);
+}
+#endif
index dcd758a57cf7ebf9291ecbc40abaeb9d0078eb22..7e7bc912ac8f57cecc062d8333710ac171d714b2 100644 (file)
  * Enterprises, see ``http://www.vix.com''.
  */
 
+/* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu).
+ * This sockopt allows a socket to be bound to a particular interface,
+ * thus enabling the use of DHCPD on a multihomed host.
+ * If SO_BINDTODEVICE is defined in your system header files, the use of
+ * this sockopt will be automatically enabled. 
+ * I have implemented it under Linux; other systems should be doable also.
+ */
+
 #ifndef lint
 static char copyright[] =
-"$Id: socket.c,v 1.16 1996/08/27 09:54:48 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: socket.c,v 1.17 1997/01/02 12:00:18 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -62,6 +70,8 @@ int if_register_socket (info, interface)
        struct sockaddr_in name;
        int sock;
        int flag;
+
+#ifndef SO_BINDTODEVICE
        static int once = 0;
 
        /* Make sure only one interface is registered. */
@@ -72,22 +82,8 @@ int if_register_socket (info, interface)
                       "you must compile in BPF or NIT support.   If neither ",
                       "option is supported on your system, please let us ",
                       "know.");
-
-       /* Technically, we need to know what interface every packet
-          comes in on, which means that we can only operate on a
-          machine with a single interface configured.   However,
-          we generally don't expect to get broadcast packets on
-          point-to-point interfaces, so we can bend the rules a bit
-          and not count them.   This won't allow DHCP-over-PPP,
-          but it's probably right in a lot of cases, and the issue
-          will have to be revisited when DHCP-over-PPP support is
-          done.   Currently we determine whether an interface is
-          point-to-point by seeing if it has a link-level address.
-          This only works on 4.4BSD and derivative networking. */
-#ifdef AF_LINK
-       if (info -> hw_address.hlen) /* XXX */
+       once = 1;
 #endif
-               once = 1;
 
        /* Set up the address we're going to bind to. */
        name.sin_family = AF_INET;
@@ -111,10 +107,26 @@ int if_register_socket (info, interface)
                        (char *)&flag, sizeof flag) < 0)
                error ("Can't set SO_BROADCAST option on dhcp socket: %m");
 
+#ifndef USE_SOCKET_FALLBACK
+       /* The following will make all-ones broadcasts go out this interface
+        * on those platforms which use the standard sockets API (assuming 
+        * the OS-specific routines called by enable_sending() are present
+        * for this platform). */
+       if_enable (info);
+#endif
        /* Bind the socket to this interface's IP address. */
        if (bind (sock, (struct sockaddr *)&name, sizeof name) < 0)
                error ("Can't bind to dhcp address: %m");
 
+#ifdef SO_BINDTODEVICE
+       /* Bind this socket to this interface. */
+       if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
+                      (char *)interface, sizeof(*interface)) < 0) {
+               error("setting SO_BINDTODEVICE");
+       }
+#endif
+
        return sock;
 }
 #endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE */
@@ -197,3 +209,30 @@ size_t fallback_discard (interface)
                         (struct sockaddr *)&from, &flen);
 }
 #endif /* USE_SOCKET_RECEIVE */
+
+#if defined (USE_SOCKET_SEND) && !defined (USE_SOCKET_FALLBACK)
+/* If we're using the standard socket API without SO_BINDTODEVICE,
+ * we need this kludge to force DHCP broadcasts to go out
+ * this interface, even though it's not available for general
+ * use until we get a lease!
+ * This should work _OK_, but it will cause ALL all-ones
+ * broadcasts on this host to go out this interface--it
+ * could interfere with other interfaces.  And God help you
+ * if you run this on multiple interfaces simultaneously.
+ * SO_BINDTODEVICE really is better! */
+void if_enable (interface)
+     struct interface_info *interface;
+{
+#ifndef SO_BINDTODEVICE
+       struct in_addr broad_addr;
+       broad_addr.s_addr = htonl(INADDR_BROADCAST);
+
+       /* Delete old routes for broadcast address. */
+       remove_routes(NULL, &broad_addr);
+
+       /* Add a route for broadcast address to this interface. */
+       /* POTENTIAL PROBLEM: Don't do this to more than one interface! */
+       add_route_direct(interface, &broad_addr);
+#endif
+}
+#endif
index 8bc551ef9893b2d65c1300111babdde662d30d44..a87773a53df5a6b5f9676e501626c9a5e672654c 100644 (file)
@@ -1,6 +1,6 @@
-/* dhcp.c
+/* dhclient.c
 
-   DHCP Client (really lame DHCP client). */
+   DHCP Client (less lame DHCP client). */
 
 /*
  * Copyright (c) 1995, 1996 The Internet Software Consortium.
  * 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
+ * while he was working on the MosquitoNet project at Stanford.
  */
 
 #ifndef lint
 static char copyright[] =
-"$Id: dhclient.c,v 1.21 1996/09/11 18:53:32 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: dhclient.c,v 1.22 1997/01/02 12:00:14 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -56,6 +59,31 @@ struct iaddr server_identifier;
 int server_identifier_matched;
 int log_perror = 1;
 
+const u_long broadcast_address = INADDR_BROADCAST;
+const int max_retransmissions = 4;
+const int min_delay = 4;
+
+/* Globals for the real_dhcp_client state machine. */
+TIME lease_expiry, T1_expiry, T2_expiry;
+struct packet *sendpacket;
+struct packet *recvpacket;
+struct packet *leasepacket;
+int sendpacket_flag, recvpacket_flag;
+u_long destination;
+
+enum {
+       S_INIT, S_SELECTING, S_REQUESTING, 
+       S_BOUND, S_RENEWING, S_REBINDING
+} state;
+
+/* ASSERT_STATE() does nothing now; it used to be 
+ * assert(state_is==state_shouldbe); */
+#define ASSERT_STATE(state_is, state_shouldbe) {}
+
+int retransmissions_left, retransmission_delay;
+struct interface_info *dhclient_interface;
+
 #ifdef USE_FALLBACK
 struct interface_info fallback_interface;
 #endif
@@ -99,13 +127,24 @@ int main (argc, argv, envp)
                        server_port = htons (atoi (argv [i]));
                        debug ("binding to user-specified port %d",
                               ntohs (server_port));
-               } else
-                       usage ();
+               } 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 = tmp;
+               }
        }
-
        /* Default to the DHCP/BOOTP port. */
-       if (!server_port)
-       {
+       if (!server_port) {
                ent = getservbyname ("dhcpc", "udp");
                if (!ent)
                        server_port = htons (68);
@@ -120,25 +159,544 @@ int main (argc, argv, envp)
        /* Discover all the network interfaces and initialize them. */
        discover_interfaces (0);
 
-       for (interface = interfaces; interface; interface = interface -> next)
-               send_discover (interface);
-
-       /* Receive packets and dispatch them... */
-       dispatch ();
+       /* This is a real DHCP client!
+        * SO, fork off a dhclient state machine for each
+        * interface. */
+       for (dhclient_interface = interfaces; dhclient_interface; 
+            dhclient_interface = dhclient_interface -> next) {
+               if (!dhclient_interface -> next || fork() == 0) {
+                       srandom(cur_time + *(int *)
+                               &dhclient_interface->hw_address.haddr);
+                       dhclient_state_machine();
+                       dhclient_fail();
+               }
+       }
 
-       /* Not reached */
        return 0;
 }
 
+/* This routine should be called when the DHCP client quits for any reason.
+ * It makes the interface unusable and outputs an error message before
+ * exiting.  A warn() message should be given first for specific errors. */
+
+void dhclient_fail()
+{
+       disable_interface(dhclient_interface);
+       error ("dhclient for '%s' halted",
+              dhclient_interface->name);
+}
+
+/* Handles the KILL signal for this DHCP client process.
+ * Sends a DHCPRELEASE message to the server (to be a good neighbor) before
+ * calling failure routine. */
+void handle_kill(dummy)
+     int dummy;
+{
+       warn("dhclient has been killed!");
+       /* XXX figure out whether the lease has expired... */
+       if (leasepacket -> packet_type == DHCPACK) {
+               warn("sending DHCPRELEASE");
+               send_release(leasepacket);
+       }
+       dhclient_fail ();
+}
+
+/* Make this network interface unusable by most programs.  This routine is
+ * called whenever dhclient discovers this host holds no lease.
+ * dhclient must still be able to use it, though, so we can get a lease!!!
+ * Just remove all the routes to this interface, and set its IP address to 
+ * 0.0.0.0.  dhclient will have to find some way to communicate with it. */
+void disable_interface(interface)
+     struct interface_info *interface;
+{
+       struct in_addr zero_addr;
+
+       remove_all_if_routes (interface);
+       zero_addr.s_addr = 0;
+       set_ip_address (interface, zero_addr);
+}
+
+void apply_parameters(interface, packet)
+     struct interface_info *interface;
+     struct packet *packet;
+{
+       struct in_addr net_addr, tmp;
+
+       /* Eventually, set IP address, netmask, router, DNS servers, 
+        * domain name, broadcast addr, etc.
+        *
+        * For now, set IP addr, netmask, and broadcast addr, add gateway,
+        * and update routes.
+        * Call OS-dependent routines to do the dirty work.
+        */
+       
+       note("Setting parameters for %s interface...", interface->name);
+               
+       remove_all_if_routes (interface);
+       set_ip_address (interface, packet -> raw -> yiaddr);
+
+       memcpy (&tmp, packet->options[DHO_SUBNET_MASK].data, sizeof tmp);
+       set_netmask (interface, tmp);
+
+       memcpy (&tmp, packet->options[DHO_BROADCAST_ADDRESS].data, sizeof tmp);
+       set_broadcast_addr(interface, tmp);
+
+       /* network addr = (my IP addr) AND (netmask) */
+       memcpy (&tmp, packet -> options[DHO_SUBNET_MASK].data, sizeof tmp);
+       net_addr.s_addr = packet -> raw -> yiaddr.s_addr & tmp.s_addr;
+       memcpy (&tmp, packet->options[DHO_SUBNET_MASK].data, sizeof tmp);
+       add_route_net(interface, net_addr, tmp);
+
+       memcpy (&tmp, packet->options[DHO_ROUTERS].data, sizeof tmp);
+       add_route_default_gateway(interface, tmp);
+}
+
 static void usage ()
 {
-       error ("Usage: dhclient [-p <port>]");
+       error ("Usage: dhclient [-c] [-p <port>] [interface]");
 }
 
 void cleanup ()
 {
 }
 
+/* The state machine for a true DHCP client, on one particular interface
+ * (stored in global dhclient_interface).
+ * This function should not be exited except on a fatal error. */
+void dhclient_state_machine ()
+{
+       fd_set read_fd;
+       struct timeval tv, timeout;
+       int reset_timer_flag;
+       long int rnd;
+
+       recvpacket = new_packet("recvpacket");
+       recvpacket->raw = new_dhcp_packet("recvpacket->raw");
+       sendpacket = new_packet("sendpacket");
+       sendpacket->raw = new_dhcp_packet("sendpacket->raw");
+       leasepacket = new_packet("leasepacket");
+       leasepacket->raw = new_dhcp_packet("leasepacket->raw");
+
+       /* Figure out when we time out... */
+       gettimeofday (&timeout, (struct timezone *)0);
+       timeout.tv_sec++;
+
+       state = S_INIT;
+       sendpacket_flag = 0;
+       recvpacket_flag = 0;
+       retransmissions_left = 0;
+
+       signal(SIGTERM, handle_kill);
+       signal(SIGINT, handle_kill);
+       while (1) {
+               /* We have awakened for some reason.  Possible reasons are:
+                * -> just entered INIT state (special case)
+                * -> packet received on this port (recvbuffer != NULL)
+                * -> timeout  (we may want to retransmit a request)
+                */
+               GET_TIME (&cur_time);
+               sendpacket_flag = 0;
+
+               if (state == S_INIT) {
+                       reset_timer_flag = state_init();
+               } else if ( (!recvpacket_flag) && (retransmissions_left>0) ) {
+                       /* Timed out; retransmit. */
+                       retransmissions_left--;
+                       retransmission_delay = retransmission_delay*2;
+                       reset_timer_flag = 1;
+                       sendpacket_flag = 1;
+               } else {
+                       /* Either a packet was received 
+                        * or we timed out and ran out of retransmissions.
+                        */
+                       if (recvpacket_flag) {
+                               read_packet(dhclient_interface, recvpacket);
+                       }                       
+                       /* If a packet was received, first check to make
+                        * sure it's relevant to our last request; if it's
+                        * not, ignore it and go back to sleep.
+                        */
+                       if ((recvpacket_flag) && 
+                           ((!recvpacket->options_valid) ||
+                            (recvpacket->raw->op != BOOTREPLY) ||
+                            (recvpacket->raw->xid!=sendpacket->raw->xid) ) ) {
+                               reset_timer_flag = 0;
+                       } else {
+                               note ("%s for %s",
+                                     (recvpacket -> packet_type == 0
+                                      ? "BOOTREPLY" :
+                                      (recvpacket -> packet_type == DHCPOFFER
+                                       ? "DHCPOFFER" :
+                                       (recvpacket -> packet_type == DHCPACK
+                                        ? "DHCPACK" :
+                                        (recvpacket -> packet_type == DHCPNAK
+                                         ? "DHCPNAK" : "DHCP Unknown")))),
+                                     inet_ntoa (recvpacket -> raw -> yiaddr));
+
+                               /* Call the appropriate routine for this state.
+                                * This routine handles both timeout 
+                                * and packet-received cases.
+                                *
+                                * The routine returns 1 if the select()
+                                * timer should be reset (which is ALWAYS the
+                                * case when sending a new packet).
+                                * Otherwise, we just continue our last select.
+                                */
+                               switch (state) {
+                               case S_SELECTING:
+                                       reset_timer_flag = state_selecting();
+                                       break;
+                               case S_REQUESTING:
+                                       reset_timer_flag = state_requesting();
+                                       break;
+                               case S_BOUND:
+                                       reset_timer_flag = state_bound();
+                                       break;
+                               case S_RENEWING:
+                                       reset_timer_flag = state_renewing();
+                                       break;
+                               case S_REBINDING:
+                                       reset_timer_flag = state_rebinding();
+                                       break;
+                               default:
+                                       warn("dhclient entered bad state");
+                                       dhclient_fail();
+                               }
+                       }       
+               }                       
+
+               /* Send a packet and reset the timer OR
+                * set a new timer with no outstanding request OR
+                * just go back to sleep because we're still waiting for
+                * something. */
+               if (reset_timer_flag) {
+                       if (sendpacket_flag) {
+                               send_packet_struct(dhclient_interface, 
+                                                  destination, sendpacket); 
+                       }
+                       rnd = random();
+                       tv.tv_sec = retransmission_delay - 1 + (rnd&1);
+                       tv.tv_usec = rnd % 1000000;
+                       gettimeofday (&timeout, (struct timezone *)0);
+                       timeout.tv_usec += tv.tv_usec;
+                       if (timeout.tv_usec > 1000000) {
+                               timeout.tv_usec -= 1000000;
+                               timeout.tv_sec++;
+                       }
+                       timeout.tv_sec += tv.tv_sec;
+               } else {
+                       gettimeofday (&tv, (struct timezone *)0);
+                       tv.tv_usec = timeout.tv_usec - tv.tv_usec;
+                       if (tv.tv_usec < 0) {
+                               tv.tv_usec += 1000000;
+                               tv.tv_sec--;
+                       }
+                       tv.tv_sec = timeout.tv_sec - tv.tv_sec;
+                       if (tv.tv_sec < 0) {
+                               warn ("timer expired unexpectedly!");
+                               dhclient_fail ();
+                       }
+               }
+
+               FD_ZERO (&read_fd);
+               FD_SET  (dhclient_interface->rfdesc, &read_fd);
+               recvpacket_flag = select(dhclient_interface->rfdesc+1, 
+                                        &read_fd, NULL, NULL, &tv);
+               if (recvpacket_flag < 0) {
+                       warn ("select: %m");
+                       dhclient_fail();
+               }
+       }
+       /* keep cycling through the state machine until dhclient_fail() */
+}
+
+/* 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 global variables are used to keep track of the process:
+ *   recvpacket: most recent packet received
+ *   sendpacket: most recent packet sent or to be sent
+ *   leasepacket: copy of most recent valid lease DHCPACK packet
+ *   recvpacket_flag: recvpacket is not old hat
+ *   sendpacket_flag: sendpacket is to be sent
+ *   destination: IP address to send sendpacket to
+ *   retransmissions_left: # of times remaining to resend this sendpacket
+ *   retransmission_delay: # of seconds to wait before next retransmission
+ *   T1_expiry, T2_expiry, lease_expiry: lease milestones
+ */
+
+int state_init()
+{
+       ASSERT_STATE(state, S_INIT);
+
+       /* Don't let anyone else use this interface until we get a lease,
+        * but make sure we can use it to obtain a lease! */
+       disable_interface(dhclient_interface);
+       if_enable (dhclient_interface);
+
+       make_discover(dhclient_interface, sendpacket);
+       sendpacket_flag = 1;
+       retransmissions_left = max_retransmissions;
+       retransmission_delay = min_delay;
+       destination = broadcast_address;
+       state = S_SELECTING;
+       return 1;
+}
+
+int state_selecting()
+{
+       ASSERT_STATE(state, S_SELECTING);
+
+       if (!recvpacket_flag) {
+               warn("no DHCP server found");
+               dhclient_fail();
+               /*NOTREACHED*/
+               return 0;
+       } else if (recvpacket->packet_type != DHCPOFFER) {
+               return 0; /* wait for a DHCPOFFER */
+       } else {
+               /* Got a DHCPOFFER! */
+               make_request(recvpacket, sendpacket);
+               sendpacket->raw->xid = random();
+               sendpacket_flag = 1;
+               destination = broadcast_address;
+               retransmissions_left = max_retransmissions;
+               retransmission_delay = min_delay;
+               state = S_REQUESTING;
+               return 1;
+       }
+}  
+
+int state_requesting()
+{
+       ASSERT_STATE(state, S_REQUESTING);
+
+       if (!recvpacket_flag) {
+               warn("lost contact with DHCP server");
+               state = S_INIT;
+               return state_init();
+       } else if (recvpacket->packet_type == DHCPNAK) {
+               warn("DHCPOFFER withdrawn");
+               state = S_INIT;
+               return state_init();
+       } else if (recvpacket->packet_type == DHCPACK) {
+               /* We got a lease!  Apply its parameters,
+                * record lease info, and set timer to T1. */
+               deep_packet_copy(leasepacket, recvpacket);
+               apply_parameters(dhclient_interface, leasepacket);
+               lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME);
+               T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME);
+               T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME);
+               retransmissions_left = 0;
+               retransmission_delay = T1_expiry - cur_time;
+               sendpacket_flag = 0; /* just set the timer, that's all */
+               state = S_BOUND;
+               return 1;
+       } else {
+               /* Ignore some superfluous packet. */
+               return 0;
+       }
+}  
+
+int state_bound()
+{
+       ASSERT_STATE(state, S_BOUND);
+
+       if (!recvpacket_flag) {
+               /* T1 has expired. */
+               make_request(leasepacket, sendpacket);
+               sendpacket_flag = 1;
+               /* XXX: should really unicast here, but to whom? */
+               destination = broadcast_address;
+               /* XXX: SHOULD WE DO RETRANSMISSIONS HERE? */
+               retransmissions_left = 0;
+               retransmission_delay = T2_expiry - cur_time;
+               state = S_RENEWING;
+               return 1;
+       } else {
+               /* Ignore some superfluous packet. */
+               return 0;
+       }
+}  
+
+int state_renewing()
+{
+       ASSERT_STATE(state, S_RENEWING);
+
+       if (!recvpacket_flag) {
+               /* T2 has expired. */
+               debug("T2 expired");
+               sendpacket_flag = 1;
+               destination = broadcast_address;
+               /* XXX: SHOULD WE DO RETRANSMISSIONS HERE? */
+               retransmissions_left = 0;
+               retransmission_delay = lease_expiry - cur_time;
+               state = S_REBINDING;
+               return 1;
+       } else if (recvpacket->packet_type == DHCPNAK) {
+               warn("lease renewal denied!");
+               state = S_INIT;
+               return state_init();
+       } else if (recvpacket->packet_type == DHCPACK) {
+               /* Record new lease info, and set timer to T1. */
+               debug("lease renewed");
+               deep_packet_copy(leasepacket, recvpacket);
+               lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME);
+               T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME);
+               T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME);
+               retransmissions_left = 0;
+               retransmission_delay = T1_expiry - cur_time;
+               sendpacket_flag = 0; /* just set the timer, that's all */
+               state = S_BOUND;
+               return 1;
+       } else {
+               /* Ignore some superfluous packet. */
+               return 0;
+       }
+}  
+
+int state_rebinding()
+{
+       ASSERT_STATE(state, S_REBINDING);
+
+       if (!recvpacket_flag) {
+                       /* Lease has expired! */
+               warn("DHCP lease expired!");
+               state = S_INIT;
+               return state_init();
+       } else if (recvpacket->packet_type == DHCPNAK) {
+               warn("lease rebinding denied!");
+               state = S_INIT;
+               return state_init();
+       } else if (recvpacket->packet_type == DHCPACK) {
+               /* Record new lease info, and set timer to T1. */
+               debug("lease rebound");
+               deep_packet_copy(leasepacket, recvpacket);
+               lease_expiry = abs_time(leasepacket, DHO_DHCP_LEASE_TIME);
+               T1_expiry = abs_time(leasepacket, DHO_DHCP_RENEWAL_TIME);
+               T2_expiry = abs_time(leasepacket, DHO_DHCP_REBINDING_TIME);
+               retransmissions_left = 0;
+               retransmission_delay = T1_expiry - cur_time;
+               sendpacket_flag = 0; /* just set the timer, that's all */
+               state = S_BOUND;
+               return 1;
+       } else {
+               /* Ignore some superfluous packet. */
+               return 0;
+       }
+}  
+
+/* Read the time offset from a given DHCP option and return it as an
+ * absolute moment in time (offset from cur_time). */
+TIME abs_time(pkt, optnum)
+     struct packet *pkt;
+     int optnum;
+{
+       if (pkt == NULL) {
+               error ("Bad parameter passed to abs_time");
+       }
+
+       return cur_time + 
+               (TIME) ntohl(*((u_int32_t *)pkt->options[optnum].data));
+}
+
+/* This little number lets you record an interesting packet's contents in
+ * another (pre-allocated) packet struct. */
+/* XXX: what about haddr? */
+void deep_packet_copy(to, from)
+     struct packet *to;
+     struct packet *from;
+{
+       struct dhcp_packet *raw;
+
+       if ((to==NULL) || (to->raw==NULL)) {
+               error("Bad parameter passed to deep_packet_copy");
+       }
+
+       raw = to->raw;
+       memcpy (to->raw, from->raw, sizeof *from->raw);
+       memcpy (to, from, sizeof *from);
+       to->raw = raw;
+}
+
+
+/* Read a DHCP packet from this interface into this (pre-allocated) packet. 
+ * If the data on this interface is bogus (non-DHCP), 
+ * set pkt->options_valid = 0.
+ * Much of this code is stolen from dispatch.c.
+ */
+void read_packet(interface, pkt) 
+     struct interface_info *interface;
+     struct packet *pkt;
+{
+       struct dhcp_packet *dhcp_pkt;
+       struct sockaddr_in from;
+       struct hardware *hfrom;
+       struct hardware fudge_factor; /* XXX */
+       struct iaddr ifrom;
+       int len;
+       static unsigned char packbuf [4095]; /* Packet input buffer.
+                                               Must be as large as largest
+                                               possible MTU. */
+
+       if (pkt==NULL) {
+               error("Bad parameter passed to read_packet");
+       }
+       pkt->haddr = &fudge_factor;
+       if ((pkt->haddr==NULL) || (pkt->raw==NULL)) {
+               error("Bad parameter passed to read_packet");
+       }
+
+       hfrom = pkt->haddr;
+       if ((len = receive_packet (interface, packbuf, sizeof packbuf,
+                                     &from, hfrom)) < 0) {
+               warn ("receive_packet failed on %s: %m", interface -> name);
+               pkt->options_valid = 0;
+               return;
+       }
+       if (len == 0) {
+               pkt->options_valid = 0;
+               return;
+       }
+
+       ifrom.len = 4;
+       memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len);
+       
+       dhcp_pkt = pkt->raw;
+       memcpy (dhcp_pkt, packbuf, len);
+       memset (pkt, 0, sizeof (struct packet));
+       pkt->raw = dhcp_pkt;
+       pkt->packet_length = len;
+       pkt->client_port = from.sin_port;
+       pkt->client_addr = ifrom;
+       pkt->interface = interface;
+       pkt->haddr = hfrom;
+       
+       parse_options (pkt);
+       if (pkt->options_valid &&
+           pkt->options [DHO_DHCP_MESSAGE_TYPE].data) {
+               pkt->packet_type =
+                       pkt->options [DHO_DHCP_MESSAGE_TYPE].data [0];
+       } else {
+               pkt->options_valid = 0;
+       }
+       pkt->haddr = NULL; /* XXX */
+
+#ifdef DEBUG
+       dump_packet(pkt);
+#endif
+}
+
 int commit_leases ()
 {
        return 0;
@@ -193,6 +751,7 @@ void dhcpoffer (packet)
                             packet -> raw -> chaddr));
 
        dump_packet (packet);
+       note ("DHCPREQUEST to %s", packet -> interface -> name);
        send_request (packet);
 }
 
@@ -215,34 +774,109 @@ void dhcpnak (packet)
                             packet -> raw -> chaddr));
 }
 
-static u_int8_t requested_options [] = {
-       DHO_DHCP_REQUESTED_ADDRESS,
-       DHO_SUBNET_MASK,
-       DHO_ROUTERS,
-       DHO_DOMAIN_NAME_SERVERS,
-       DHO_HOST_NAME,
-       DHO_DOMAIN_NAME,
-       DHO_BROADCAST_ADDRESS };
-
 void send_discover (interface)
        struct interface_info *interface;
+{
+       struct packet outgoing;
+       struct dhcp_packet raw;
+
+       outgoing.raw = &raw;
+       make_discover (interface, &outgoing);
+       send_packet_struct (interface, htonl(INADDR_BROADCAST), &outgoing);
+}
+
+void send_request (packet)
+       struct packet *packet; /* incoming packet we're responding to */
+{
+       struct packet outgoing;
+       struct dhcp_packet raw;
+
+       outgoing.raw = &raw;
+       make_request (packet, &outgoing);
+       send_packet_struct (packet->interface, htonl(INADDR_BROADCAST), 
+                           &outgoing);
+}
+
+void send_release (packet)
+       struct packet *packet; /* DHCPACK packet from lease we're releasing */
+{
+       struct packet outgoing;
+       struct dhcp_packet raw;
+
+       outgoing.raw = &raw;
+       make_release (packet, &outgoing);
+       send_packet_struct (packet->interface, htonl(INADDR_BROADCAST), 
+                           &outgoing);
+}
+
+/* Send the given packet to the given host IPaddr, over the given interface. */
+void send_packet_struct (interface, dest_host, sendpkt)
+       struct interface_info *interface;
+       u_long dest_host;  /* in network order! */
+       struct packet *sendpkt;
 {
        struct sockaddr_in to;
        int result;
-       struct dhcp_packet raw;
+       char dhcpbuf [128];
+
+       /* Set up the common stuff... */
+       to.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+       to.sin_len = sizeof to;
+#endif
+       memset (to.sin_zero, 0, sizeof to.sin_zero);
+
+       to.sin_addr.s_addr = dest_host;
+       to.sin_port = htons (ntohs (server_port) - 1); /* XXX */
+
+       sprintf (dhcpbuf, "DHCP Unknown %d", sendpkt -> packet_type);
+       note ("%s to %s",
+             sendpkt -> packet_type == DHCPDISCOVER ? "DHCPDISCOVER"
+             : (sendpkt -> packet_type == DHCPREQUEST ? "DHCPREQUEST"
+                : (sendpkt -> packet_type == DHCPNAK ? "DHCPNAK"
+                   : (sendpkt -> packet_type == DHCPINFORM ? "DHCPINFORM"
+                      : dhcpbuf))),
+             inet_ntoa (to.sin_addr));
+             
+       if (sendpkt->packet_length < DHCP_MIN_LEN)
+               sendpkt->packet_length = DHCP_MIN_LEN;
+
+       errno = 0;
+       result = send_packet (interface, (struct packet *)0,
+                             sendpkt->raw, sendpkt->packet_length,
+                             sendpkt->raw->siaddr, &to, (struct hardware *)0);
+       /* XXX: is the above 'siaddr' correct? */
+       if (result < 0)
+               warn ("send_packet: %m");
+}
+
+void make_discover (interface, sendpkt)
+       struct interface_info *interface;
+       struct packet *sendpkt;
+{
+       struct dhcp_packet *raw;
        unsigned char discover = DHCPDISCOVER;
-       struct packet outgoing;
 
        struct tree_cache *options [256];
        struct tree_cache dhcpdiscover_tree;
        struct tree_cache dhcprqo_tree;
 
+       u_int8_t requested_options [] = {
+               DHO_SUBNET_MASK,
+               DHO_ROUTERS,
+               DHO_DOMAIN_NAME_SERVERS,
+               DHO_HOST_NAME,
+               DHO_DOMAIN_NAME,
+               DHO_BROADCAST_ADDRESS };
+
+       raw = sendpkt->raw;
        memset (options, 0, sizeof options);
-       memset (&outgoing, 0, sizeof outgoing);
-       memset (&raw, 0, sizeof raw);
-       outgoing.raw = &raw;
+       memset (sendpkt, 0, sizeof (*sendpkt));
+       memset (raw, 0, sizeof (*raw));
+       sendpkt->raw = raw;
+       sendpkt -> packet_type = DHCPDISCOVER;
 
-       /* Set DHCP_MESSAGE_TYPE to DHCPNAK */
+       /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */
        options [DHO_DHCP_MESSAGE_TYPE] = &dhcpdiscover_tree;
        options [DHO_DHCP_MESSAGE_TYPE] -> value = &discover;
        options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof discover;
@@ -250,65 +884,44 @@ void send_discover (interface)
        options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF;
        options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0;
 
-       /* Set DHCP_MESSAGE to whatever the message is */
-       options [DHO_DHCP_MESSAGE] = &dhcprqo_tree;
-       options [DHO_DHCP_MESSAGE] -> value = requested_options;
-       options [DHO_DHCP_MESSAGE] -> len = sizeof requested_options;
-       options [DHO_DHCP_MESSAGE] -> buf_size = sizeof requested_options;
-       options [DHO_DHCP_MESSAGE] -> timeout = 0xFFFFFFFF;
-       options [DHO_DHCP_MESSAGE] -> tree = (struct tree *)0;
+       /* Request the parameters we want */
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] = &dhcprqo_tree;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> value = requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> len = sizeof requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> buf_size = sizeof requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> timeout = 0xFFFFFFFF;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> tree = (struct tree *)0;
 
        /* Set up the option buffer... */
-       cons_options ((struct packet *)0, &outgoing, options, 0, 0);
+       cons_options ((struct packet *)0, sendpkt, options, 0, 0);
 
-       memset (&raw.ciaddr, 0, sizeof raw.ciaddr);
-       memset (&raw.siaddr, 0, sizeof raw.siaddr);
-       memset (&raw.giaddr, 0, sizeof raw.giaddr);
-       memcpy (raw.chaddr,
+       raw->op = BOOTREQUEST;
+       raw->htype = interface -> hw_address.htype;
+       raw->hlen = interface -> hw_address.hlen;
+       raw->hops = 0;
+       raw->xid = random ();
+       raw->secs = 0; /* XXX */
+       raw->flags = htons (BOOTP_BROADCAST);
+       memset (&(raw->ciaddr), 0, sizeof raw->ciaddr);
+       memset (&(raw->yiaddr), 0, sizeof raw->yiaddr);
+       memset (&(raw->siaddr), 0, sizeof raw->siaddr);
+       memset (&(raw->giaddr), 0, sizeof raw->giaddr);
+       memcpy (raw->chaddr,
                interface -> hw_address.haddr, interface -> hw_address.hlen);
-       raw.hlen = interface -> hw_address.hlen;
-       raw.htype = interface -> hw_address.htype;
-
-       raw.xid = random ();
-       raw.secs = 1;
-       raw.flags = htons (BOOTP_BROADCAST);
-       raw.hops = 0;
-       raw.op = BOOTREQUEST;
-
-       /* Report what we're sending... */
-       note ("DHCPDISCOVER to %s", interface -> name);
 
 #ifdef DEBUG_PACKET
-       dump_packet (&outgoing);
-       dump_raw ((unsigned char *)&raw, outgoing.packet_length);
+       dump_packet (sendpkt);
+       dump_raw ((unsigned char *)raw, sendpkt->packet_length);
 #endif
-
-       /* Set up the common stuff... */
-       to.sin_family = AF_INET;
-#ifdef HAVE_SA_LEN
-       to.sin_len = sizeof to;
-#endif
-       memset (to.sin_zero, 0, sizeof to.sin_zero);
-
-       to.sin_addr.s_addr = htonl (INADDR_BROADCAST);
-       to.sin_port = htons (ntohs (server_port) - 1); /* XXX */
-
-       errno = 0;
-       result = send_packet (interface, (struct packet *)0,
-                             &raw, outgoing.packet_length,
-                             raw.siaddr, &to, (struct hardware *)0);
-       if (result < 0)
-               warn ("send_packet: %m");
 }
 
-void send_request (packet)
-       struct packet *packet;
+
+void make_request (recvpkt, sendpkt)
+     struct packet *recvpkt;
+     struct packet *sendpkt;
 {
-       struct sockaddr_in to;
-       int result;
-       struct dhcp_packet raw;
+       struct dhcp_packet *raw;
        unsigned char request = DHCPREQUEST;
-       struct packet outgoing;
 
        struct tree_cache *options [256];
        struct tree_cache dhcprequest_tree;
@@ -316,12 +929,23 @@ void send_request (packet)
        struct tree_cache dhcprqa_tree;
        struct tree_cache dhcpsid_tree;
 
+       u_int8_t requested_options [] = {
+               DHO_SUBNET_MASK,
+               DHO_ROUTERS,
+               DHO_DOMAIN_NAME_SERVERS,
+               DHO_HOST_NAME,
+               DHO_DOMAIN_NAME,
+               DHO_BROADCAST_ADDRESS };
+
+       raw = sendpkt->raw;
        memset (options, 0, sizeof options);
-       memset (&outgoing, 0, sizeof outgoing);
-       memset (&raw, 0, sizeof raw);
-       outgoing.raw = &raw;
+       memset (sendpkt, 0, sizeof (*sendpkt));
+       memset (raw, 0, sizeof (*raw));
+       sendpkt->raw = raw;
 
-       /* Set DHCP_MESSAGE_TYPE to DHCPNAK */
+       sendpkt -> packet_type = DHCPREQUEST;
+
+       /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */
        options [DHO_DHCP_MESSAGE_TYPE] = &dhcprequest_tree;
        options [DHO_DHCP_MESSAGE_TYPE] -> value = &request;
        options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof request;
@@ -329,74 +953,113 @@ void send_request (packet)
        options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF;
        options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0;
 
-       /* Set DHCP_MESSAGE to whatever the message is */
-       options [DHO_DHCP_MESSAGE] = &dhcprqo_tree;
-       options [DHO_DHCP_MESSAGE] -> value = requested_options;
-       options [DHO_DHCP_MESSAGE] -> len = sizeof requested_options;
-       options [DHO_DHCP_MESSAGE] -> buf_size = sizeof requested_options;
-       options [DHO_DHCP_MESSAGE] -> timeout = 0xFFFFFFFF;
-       options [DHO_DHCP_MESSAGE] -> tree = (struct tree *)0;
-
-       /* Request the address we were offered... */
-       options [DHO_DHCP_REQUESTED_ADDRESS] = &dhcprqa_tree;
-       options [DHO_DHCP_REQUESTED_ADDRESS] -> value = 
-               (unsigned char *)&packet -> raw -> yiaddr;
-       options [DHO_DHCP_REQUESTED_ADDRESS] -> len = 4;
-       options [DHO_DHCP_REQUESTED_ADDRESS] -> buf_size = 4;
-       options [DHO_DHCP_REQUESTED_ADDRESS] -> timeout = 0xFFFFFFFF;
-       options [DHO_DHCP_REQUESTED_ADDRESS] -> tree = (struct tree *)0;
+       /* Request the parameters we want */
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] = &dhcprqo_tree;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> value = requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> len = sizeof requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> buf_size = sizeof requested_options;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> timeout = 0xFFFFFFFF;
+       options [DHO_DHCP_PARAMETER_REQUEST_LIST] -> tree = (struct tree *)0;
 
        /* Send back the server identifier... */
         options [DHO_DHCP_SERVER_IDENTIFIER] = &dhcpsid_tree;
         options [DHO_DHCP_SERVER_IDENTIFIER] -> value =
-                packet -> options [DHO_DHCP_SERVER_IDENTIFIER].data;
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].data;
         options [DHO_DHCP_SERVER_IDENTIFIER] -> len =
-                packet -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
         options [DHO_DHCP_SERVER_IDENTIFIER] -> buf_size =
-                packet -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
         options [DHO_DHCP_SERVER_IDENTIFIER] -> timeout = 0xFFFFFFFF;
         options [DHO_DHCP_SERVER_IDENTIFIER] -> tree = (struct tree *)0;
 
        /* Set up the option buffer... */
-       cons_options ((struct packet *)0, &outgoing, options, 0, 0);
-
-       memset (&raw.ciaddr, 0, sizeof raw.ciaddr);
-       raw.siaddr = packet -> raw -> siaddr;
-       raw.giaddr = packet -> raw -> giaddr;
-       memcpy (raw.chaddr,
-               packet -> interface -> hw_address.haddr,
-               packet -> interface -> hw_address.hlen);
-       raw.hlen = packet -> interface -> hw_address.hlen;
-       raw.htype = packet -> interface -> hw_address.htype;
-
-       raw.xid = packet -> raw -> xid;
-       raw.secs = packet -> raw -> secs;
-       raw.flags = htons (BOOTP_BROADCAST);
-       raw.hops = packet -> raw -> hops;
-       raw.op = BOOTREQUEST;
-
-       /* Report what we're sending... */
-       note ("DHCPREQUEST to %s", packet -> interface -> name);
+       cons_options ((struct packet *)0, sendpkt, options, 0, 0);
+
+       raw->op = BOOTREQUEST;
+       raw->htype = recvpkt -> interface -> hw_address.htype;
+       raw->hlen = recvpkt -> interface -> hw_address.hlen;
+       raw->hops = 0;
+       raw->xid = recvpkt -> raw -> xid;
+       raw->secs = recvpkt -> raw -> secs; /* XXX */
+       raw->flags = htons (BOOTP_BROADCAST);
+       raw->ciaddr = recvpkt -> raw -> yiaddr; /* XXX ????? */
+       memset (&raw->yiaddr, 0, sizeof raw->yiaddr);
+       memset (&raw->siaddr, 0, sizeof raw->siaddr);
+       memset (&raw->giaddr, 0, sizeof raw->giaddr);
+       memcpy (raw->chaddr,
+               recvpkt -> interface -> hw_address.haddr,
+               recvpkt -> interface -> hw_address.hlen);
 
 #ifdef DEBUG_PACKET
-       dump_packet (&outgoing);
-       dump_raw ((unsigned char *)&raw, outgoing.packet_length);
+       dump_packet (sendpkt);
+       dump_raw ((unsigned char *)raw, sendpkt->packet_length);
 #endif
+}
 
-       /* Set up the common stuff... */
-       to.sin_family = AF_INET;
-#ifdef HAVE_SA_LEN
-       to.sin_len = sizeof to;
-#endif
-       memset (to.sin_zero, 0, sizeof to.sin_zero);
+void make_release (recvpkt, sendpkt)
+     struct packet *recvpkt;
+     struct packet *sendpkt;
+{
+       struct dhcp_packet *raw;
+       unsigned char request = DHCPRELEASE;
 
-       to.sin_addr.s_addr = htonl (INADDR_BROADCAST);
-       to.sin_port = htons (ntohs (server_port) - 1); /* XXX */
+       struct tree_cache *options [256];
+       struct tree_cache dhcprequest_tree;
+       struct tree_cache dhcprqo_tree;
+       struct tree_cache dhcprqa_tree;
+       struct tree_cache dhcpsid_tree;
 
-       errno = 0;
-       result = send_packet (packet -> interface, (struct packet *)0,
-                             &raw, outgoing.packet_length,
-                             raw.siaddr, &to, (struct hardware *)0);
-       if (result < 0)
-               warn ("send_packet: %m");
+       raw = sendpkt->raw;
+       memset (options, 0, sizeof options);
+       memset (sendpkt, 0, sizeof (*sendpkt));
+       memset (raw, 0, sizeof (*raw));
+       sendpkt->raw = raw;
+
+       sendpkt -> packet_type = DHCPRELEASE;
+
+       /* Set DHCP_MESSAGE_TYPE to DHCPRELEASE */
+       options [DHO_DHCP_MESSAGE_TYPE] = &dhcprequest_tree;
+       options [DHO_DHCP_MESSAGE_TYPE] -> value = &request;
+       options [DHO_DHCP_MESSAGE_TYPE] -> len = sizeof request;
+       options [DHO_DHCP_MESSAGE_TYPE] -> buf_size = sizeof request;
+       options [DHO_DHCP_MESSAGE_TYPE] -> timeout = 0xFFFFFFFF;
+       options [DHO_DHCP_MESSAGE_TYPE] -> tree = (struct tree *)0;
+
+       /* Send back the server identifier... */
+        options [DHO_DHCP_SERVER_IDENTIFIER] = &dhcpsid_tree;
+        options [DHO_DHCP_SERVER_IDENTIFIER] -> value =
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].data;
+        options [DHO_DHCP_SERVER_IDENTIFIER] -> len =
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
+        options [DHO_DHCP_SERVER_IDENTIFIER] -> buf_size =
+                recvpkt -> options [DHO_DHCP_SERVER_IDENTIFIER].len;
+        options [DHO_DHCP_SERVER_IDENTIFIER] -> timeout = 0xFFFFFFFF;
+        options [DHO_DHCP_SERVER_IDENTIFIER] -> tree = (struct tree *)0;
+
+       /* Set DHCP_MESSAGE to whatever the message is */
+       /* XXX */
+
+       /* Set up the option buffer... */
+       cons_options ((struct packet *)0, sendpkt, options, 0, 0);
+
+       raw->op = BOOTREQUEST;
+       raw->htype = recvpkt -> interface -> hw_address.htype;
+       raw->hlen = recvpkt -> interface -> hw_address.hlen;
+       raw->hops = 0;
+       raw->xid = recvpkt -> raw -> xid;
+       raw->secs = 0;
+       raw->flags = 0;
+       raw->ciaddr = recvpkt -> raw -> yiaddr;
+       memset (&raw->yiaddr, 0, sizeof raw->yiaddr);
+       memset (&raw->siaddr, 0, sizeof raw->siaddr);
+       memset (&raw->giaddr, 0, sizeof raw->giaddr);
+       memcpy (raw->chaddr,
+               recvpkt -> interface -> hw_address.haddr,
+               recvpkt -> interface -> hw_address.hlen);
+
+
+#ifdef DEBUG_PACKET
+       dump_packet (sendpkt);
+       dump_raw ((unsigned char *)raw, sendpkt->packet_length);
+#endif
 }
diff --git a/dhcp.h b/dhcp.h
index 23a8bf7377ca9de8615e637338c642a842245c97..181277579527cab53c87d027f741c74e2e30dfdd 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
@@ -52,6 +52,7 @@
 #define DHCP_OPTION_LEN                (DHCP_MTU_MAX - DHCP_FIXED_LEN)
 
 #define BOOTP_MIN_LEN          300
+#define DHCP_MIN_LEN            548
 
 struct dhcp_packet {
        u_int8_t  op;           /* Message opcode/type */
diff --git a/dhcpd.h b/dhcpd.h
index 34aed0b567a7b6e3c169ac95f9dca073b72302c7..d62f78f2e348c955dac63a1fb468c480ed1574de 100644 (file)
--- a/dhcpd.h
+++ b/dhcpd.h
@@ -451,6 +451,9 @@ size_t receive_packet PROTO ((struct interface_info *,
                           unsigned char *, size_t,
                           struct sockaddr_in *, struct hardware *));
 #endif
+#if defined (USE_SOCKET_SEND) && !defined (USE_SOCKET_FALLBACK)
+void if_enable PROTO ((struct interface_info *));
+#endif
 
 /* bpf.c */
 #if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE)
@@ -469,6 +472,9 @@ size_t receive_packet PROTO ((struct interface_info *,
                           unsigned char *, size_t,
                           struct sockaddr_in *, struct hardware *));
 #endif
+#if defined (USE_BPF_SEND)
+void if_enable PROTO ((struct interface_info *));
+#endif
 
 /* nit.c */
 #ifdef USE_NIT_SEND
@@ -484,6 +490,9 @@ size_t receive_packet PROTO ((struct interface_info *,
                           unsigned char *, size_t,
                           struct sockaddr_in *, struct hardware *));
 #endif
+#if defined (USE_BPF_SEND)
+void if_enable PROTO ((struct interface_info *));
+#endif
 
 /* raw.c */
 #ifdef USE_RAW_SEND
@@ -541,6 +550,27 @@ void dhcpack PROTO ((struct packet *));
 void dhcpnak PROTO ((struct packet *));
 void send_discover PROTO ((struct interface_info *));
 void send_request PROTO ((struct packet *));
+void send_release PROTO ((struct packet *));
+void dhclient_fail PROTO ((void));
+void handle_kill PROTO ((int));
+void disable_interface PROTO ((struct interface_info *));
+void route_broadcasts PROTO ((struct interface_info *));
+void apply_parameters PROTO ((struct interface_info *, struct packet *));
+void dhclient_state_machine PROTO ((void));
+int state_init PROTO ((void));
+int state_selecting PROTO ((void));
+int state_requesting PROTO ((void));
+int state_bound PROTO ((void));
+int state_renewing PROTO ((void));
+int state_rebinding PROTO ((void));
+TIME abs_time PROTO ((struct packet *, int));
+void deep_packet_copy PROTO ((struct packet *, struct packet *));
+void read_packet PROTO ((struct interface_info *, struct packet *));
+void send_packet_struct PROTO ((struct interface_info *,
+                               u_long, struct packet *));
+void make_discover PROTO ((struct interface_info *, struct packet *));
+void make_request PROTO ((struct packet *, struct packet *));
+void make_release PROTO ((struct packet *, struct packet *));
 
 /* db.c */
 int write_lease PROTO ((struct lease *));
@@ -584,3 +614,16 @@ 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));
index 1985fb4e99419cda151e9572bdcd31592477a534..929c8b3d4cc284ba6d4244df64e6d89ce9932a93 100644 (file)
@@ -42,7 +42,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: dispatch.c,v 1.27 1996/11/08 20:06:29 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: dispatch.c,v 1.28 1997/01/02 12:00:16 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -162,6 +162,44 @@ void discover_interfaces (serverP)
                if (ifp -> ifr_addr.sa_family == AF_INET) {
                        struct iaddr addr;
 
+#if defined (SIOCGIFHWADDR) && !defined (AF_LINK)
+                       struct ifreq ifr;
+                       struct sockaddr sa;
+                       int b, sk;
+                       
+                       /* Read the hardware address from this interface. */
+                       ifr = *ifp;
+                       if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0)
+                               error ("Can't get hardware address for %s: %m",
+                                      ifr.ifr_name);
+
+                       sa = *(struct sockaddr *)&ifr.ifr_hwaddr;
+                                       
+                       switch (sa.sa_family) {
+                             case ARPHRD_LOOPBACK:
+                               /* ignore loopback interface */
+                               break;
+
+                             case ARPHRD_ETHER:
+                               tmp -> hw_address.hlen = 6;
+                               tmp -> hw_address.htype = ARPHRD_ETHER;
+                               memcpy (tmp -> hw_address.haddr,
+                                       sa.sa_data, 6);
+                               break;
+
+                             case ARPHRD_METRICOM:
+                               tmp -> hw_address.hlen = 6;
+                               tmp -> hw_address.htype = ARPHRD_METRICOM;
+                               memcpy (tmp -> hw_address.haddr,
+                                       sa.sa_data, 6);
+                               break;
+
+                             default:
+                               error ("%s: unknown hardware address type %d",
+                                      ifr.ifr_name, sa.sa_family);
+                       }
+#endif /* defined (SIOCGIFHWADDR) && !defined (AF_LINK) */
+
                        /* Get a pointer to the address... */
                        memcpy (&foo, &ifp -> ifr_addr,
                                sizeof ifp -> ifr_addr);
index 8e439d0b745030cd80fe296da8b3100fb5947403..d08a986d00b8b91863a60580691faad870a45f80 100644 (file)
@@ -60,6 +60,9 @@ typedef unsigned long u_int32_t;
 extern int h_errno;
 
 #include <net/if.h>
+#include <net/route.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
 
 #include <sys/time.h>          /* gettimeofday()*/
 #include <linux/time.h>                /* also necessary */
index 23f0b30da235609b606b6f0785f7bf0a94c40b52..6e9ecc7b9efd38b906dd08c5e090dd4a684dc041 100644 (file)
@@ -52,6 +52,10 @@ extern int h_errno;
 
 #include <net/if.h>
 #include <net/if_dl.h>
+#include <net/route.h>
+#include <sys/sockio.h>
+
+#define ifr_netmask ifr_addr
 
 /* Varargs stuff... */
 #include <stdarg.h>
index 23a8bf7377ca9de8615e637338c642a842245c97..181277579527cab53c87d027f741c74e2e30dfdd 100644 (file)
@@ -52,6 +52,7 @@
 #define DHCP_OPTION_LEN                (DHCP_MTU_MAX - DHCP_FIXED_LEN)
 
 #define BOOTP_MIN_LEN          300
+#define DHCP_MIN_LEN            548
 
 struct dhcp_packet {
        u_int8_t  op;           /* Message opcode/type */
index 34aed0b567a7b6e3c169ac95f9dca073b72302c7..d62f78f2e348c955dac63a1fb468c480ed1574de 100644 (file)
@@ -451,6 +451,9 @@ size_t receive_packet PROTO ((struct interface_info *,
                           unsigned char *, size_t,
                           struct sockaddr_in *, struct hardware *));
 #endif
+#if defined (USE_SOCKET_SEND) && !defined (USE_SOCKET_FALLBACK)
+void if_enable PROTO ((struct interface_info *));
+#endif
 
 /* bpf.c */
 #if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE)
@@ -469,6 +472,9 @@ size_t receive_packet PROTO ((struct interface_info *,
                           unsigned char *, size_t,
                           struct sockaddr_in *, struct hardware *));
 #endif
+#if defined (USE_BPF_SEND)
+void if_enable PROTO ((struct interface_info *));
+#endif
 
 /* nit.c */
 #ifdef USE_NIT_SEND
@@ -484,6 +490,9 @@ size_t receive_packet PROTO ((struct interface_info *,
                           unsigned char *, size_t,
                           struct sockaddr_in *, struct hardware *));
 #endif
+#if defined (USE_BPF_SEND)
+void if_enable PROTO ((struct interface_info *));
+#endif
 
 /* raw.c */
 #ifdef USE_RAW_SEND
@@ -541,6 +550,27 @@ void dhcpack PROTO ((struct packet *));
 void dhcpnak PROTO ((struct packet *));
 void send_discover PROTO ((struct interface_info *));
 void send_request PROTO ((struct packet *));
+void send_release PROTO ((struct packet *));
+void dhclient_fail PROTO ((void));
+void handle_kill PROTO ((int));
+void disable_interface PROTO ((struct interface_info *));
+void route_broadcasts PROTO ((struct interface_info *));
+void apply_parameters PROTO ((struct interface_info *, struct packet *));
+void dhclient_state_machine PROTO ((void));
+int state_init PROTO ((void));
+int state_selecting PROTO ((void));
+int state_requesting PROTO ((void));
+int state_bound PROTO ((void));
+int state_renewing PROTO ((void));
+int state_rebinding PROTO ((void));
+TIME abs_time PROTO ((struct packet *, int));
+void deep_packet_copy PROTO ((struct packet *, struct packet *));
+void read_packet PROTO ((struct interface_info *, struct packet *));
+void send_packet_struct PROTO ((struct interface_info *,
+                               u_long, struct packet *));
+void make_discover PROTO ((struct interface_info *, struct packet *));
+void make_request PROTO ((struct packet *, struct packet *));
+void make_release PROTO ((struct packet *, struct packet *));
 
 /* db.c */
 int write_lease PROTO ((struct lease *));
@@ -584,3 +614,16 @@ 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));
diff --git a/nit.c b/nit.c
index 43fa7e49638ad5fa50cb0f7d62bb92fe74ae8add..0de8d62bce0815b36028191eadbfb911b8fad547 100644 (file)
--- a/nit.c
+++ b/nit.c
@@ -42,7 +42,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: nit.c,v 1.9 1996/09/05 23:56:52 mellon Exp $ Copyright (c) 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: nit.c,v 1.10 1997/01/02 12:00:17 mellon Exp $ Copyright (c) 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -329,3 +329,21 @@ size_t receive_packet (interface, buf, len, from, hfrom)
        return length;
 }
 #endif
+
+#if defined (USE_NIT_SEND)
+void if_enable (interface)
+       struct interface_info *interface;
+{
+       struct ifreq ifr;
+
+       /* Bring the interface down and then up again to clear
+        * all its routes. */
+       strncpy(ifr.ifr_name, interface -> name, IFNAMSIZ);
+       if (ioctl(interface -> rfdesc, SIOCGIFFLAGS, &ifr) < 0)
+               error ("SIOCGIFFLAGS %s: %m", interface -> name);
+
+       ifr.ifr_flags |= (IFF_UP|IFF_RUNNING);
+       if (ioctl(interface -> rfdesc, SIOCSIFFLAGS, &ifr) == -1)
+               error ("SIOCSIFFLAGS %s: %m", interface -> name);
+}
+#endif
diff --git a/route.c b/route.c
index 8b421b0e89ded06d17211ab417e736bf70f7df8d..b1d8ec83fcbe964a7fc3c3dcff13d3ceadaec887 100644 (file)
--- a/route.c
+++ b/route.c
-/* socket.c
-
-   BSD socket interface code... */
-
-/*
- * Copyright (c) 1995, 1996 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:
+/* route.c
  *
- * 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.
+ * Routines for updating routing tables and configuring interfaces.
+ * 
+ * Copyright 1996 The Board of Trustees of The Leland Stanford
+ * Junior University. All Rights Reserved.
+ * Code originally written by Elliot Poger (poger@leland.stanford.edu).
  *
- * 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''.
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies.  Stanford University
+ * makes no representations about the suitability of this
+ * software for any purpose.  It is provided "as is" without
+ * express or implied warranty.
  */
 
-#ifndef lint
-static char copyright[] =
-"$Id: route.c,v 1.2 1996/08/27 09:53:58 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
-#endif /* not lint */
-
 #include "dhcpd.h"
 
-#if defined (OLD_ROUTE_HACK)
-#include <net/route.h>
+/* Add a route to this destination through this interface. */
+void add_route_direct(interface, destination)
+     struct interface_info *interface;
+     struct in_addr destination;
+{
+       note ("add_route_direct %s: %s",
+             interface -> name, inet_ntoa (destination));
+#if 0
+       struct in_addr directmask;
+
+       directmask.s_addr = htonl (INADDR_BROADCAST); /* this addr only */
+       add_route_net(interface, destination, &directmask);
+#endif
+}
+
+/* Add a route to this subnet through this interface. */
+void add_route_net(interface, destination, netmask)
+     struct interface_info *interface;
+     struct in_addr destination;
+     struct in_addr netmask;
+{
+       char nbuf [128];
+
+       strncpy (nbuf, inet_ntoa (netmask), sizeof nbuf);
+       nbuf [(sizeof nbuf) - 1] = 0;
+
+       note ("add_route_net %s: %s %s",
+             interface -> name, inet_ntoa (destination), nbuf);
+#if 0
+       int sock;
+       struct sockaddr_in *sin;
+       struct rtentry rt;
+
+       memset((char *) &rt, 0, sizeof(struct rtentry));
+       sin = (struct sockaddr_in *) &rt.rt_dst;
+       sin->sin_family = AF_INET;
+       sin->sin_addr.s_addr = destination.s_addr;
+       sin = (struct sockaddr_in *) &rt.rt_genmask;
+       sin->sin_family = AF_INET;
+       sin->sin_addr.s_addr = netmask.s_addr;
+       rt.rt_flags = RTF_UP;
+       rt.rt_dev = interface->name;
+       if (ioctl(interface -> rfdesc, SIOCADDRT, &rt) < 0)
+               error ("Can't add route to %s through %s: %m",
+                      inet_ntoa (destination), interface->name);
+#endif
+}
 
-route_hack (sock)
-       int sock;
+/* Add a route to the default gateway through this interface. */
+void add_route_default_gateway(interface, gateway)
+     struct interface_info *interface;
+     struct in_addr gateway;
 {
-       int rsock = socket (PF_ROUTE, SOCK_RAW, AF_INET);
-       struct rt_msghdr
+       note ("add_route_default_gateway %s: %s",
+             interface -> name, inet_ntoa (gateway));
+#if 0
+       int sock;
+       struct sockaddr_in *sin;
+       struct rtentry rt;
 
-       if (rsock < 0)
-               error ("Can't make routing socket: %m");
+       /* Route through the gateway. */
+       memset((char *) &rt, 0, sizeof(struct rtentry));
+       sin = (struct sockaddr_in *) &rt.rt_dst;
+       sin->sin_family = AF_INET;
+       sin = (struct sockaddr_in *) &rt.rt_gateway;
+       sin->sin_family = AF_INET;
+       sin->sin_addr.s_addr = gateway.s_addr;
+       rt.rt_flags = RTF_UP | RTF_GATEWAY;
+       rt.rt_dev = interface->name;
+       if (ioctl(interface -> rfdesc, SIOCADDRT, &rt) < 0)
+               error ("Can't add route to default gateway");
+#endif
+}
+               
+
+/* Remove all routes matching the specified destination. */
+void remove_routes(destination)
+     struct in_addr destination;
+{
+       note ("remove_routes: %s", inet_ntoa (destination));
+#if 0
+       int sock;
+       struct ifreq ifr;
+       struct sockaddr_in *sin;
+       struct rtentry rt;
+
+       if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+               error("Can't open socket to remove routes");
+       }
+
+       /* Remove all routes to this IP destination. */
+       memset((char *) &rt, 0, sizeof(struct rtentry));
+       sin = (struct sockaddr_in *) &rt.rt_dst;
+       sin->sin_family = AF_INET;
+       sin->sin_addr.s_addr = destination->s_addr;
+       while (ioctl(sock, SIOCDELRT, &rt) >= 0)
+               ;
+       close(sock);
+#endif
+}
+
+/* Remove routes on the specified interface matching the specified
+   destination. */
+void remove_if_route (interface, destination)
+     struct interface_info *interface;
+     struct in_addr destination;
+{
+       note ("remove_if_routes %s: %s",
+             interface -> name, inet_ntoa (destination));
+#if 0
+       int sock;
+       struct ifreq ifr;
+       struct sockaddr_in *sin;
+       struct rtentry rt;
+
+       /* Remove one specific route. */
+       /* XXX: does this even work? */
+       memset((char *) &rt, 0, sizeof(struct rtentry));
+       sin = (struct sockaddr_in *) &rt.rt_dst;
+       sin->sin_family = AF_INET;
+       sin->sin_addr.s_addr = destination.s_addr;
+       rt.rt_dev = interface->name;
+       if (ioctl(interface -> rfdesc, SIOCDELRT, &rt) == -1)
+               warn("Error removing route.");
+#endif
+}
+
+/* Remove all routes on the specified interface. */
+
+void remove_all_if_routes(interface)
+     struct interface_info *interface;
+{
+       note ("remove_all_if_routes %s", interface -> name);
+#if 0
+       struct ifreq ifr;
+
+       /* Bring the interface down and then up again to clear
+        * all its routes. */
+       strncpy(ifr.ifr_name, interface->name, IFNAMSIZ);
+       if (ioctl(interface -> rfdesc, SIOCGIFFLAGS, &ifr) == -1)
+               error ("SIOCGIFFLAGS %s: %m", interface -> name);
+
+       ifr.ifr_flags &= ~(IFF_UP|IFF_RUNNING);
+       if (ioctl(interface -> rfdesc, SIOCSIFFLAGS, &ifr) == -1)
+               error ("Can't bring down interface");
+
+       strncpy(ifr.ifr_name,interface->name,IFNAMSIZ);
+       ifr.ifr_flags |= (IFF_UP|IFF_RUNNING);
+       if (ioctl(interface -> rfdesc, SIOCSIFFLAGS, &ifr) == -1)
+               error("Can't bring interface back up");
+#endif
+}
+
+/* Set the netmask (in network byte order!) of this interface. */
+void set_netmask(interface, netmask)
+     struct interface_info *interface;
+     struct in_addr netmask;
+{
+       note ("set_netmask %s: %s",
+             interface -> name, inet_ntoa (netmask));
+#if 0
+       int sock;
+       struct ifreq ifr;
+
+       if (ioctl (interface -> rfdesc, SIOCGIFNETMASK, &ifr) == -1)
+               error ("Can't get old netmask of interface");
+
+       (*(struct sockaddr_in *)&ifr.ifr_netmask).sin_addr = netmask;
+       if (ioctl (interface -> rfdesc, SIOCSIFNETMASK, &ifr) == -1)
+               error ("Can't set new netmask");
+#endif
+}
+
+/* Set the broadcast address (in network byte order!) of this interface. */
+void set_broadcast_addr(interface, broadcast_addr)
+     struct interface_info *interface;
+     struct in_addr broadcast_addr;
+{
+       note ("set_broadcast_addr %s: %s",
+             interface -> name, inet_ntoa (broadcast_addr));
+#if 0
+       int sock;
+       struct ifreq ifr;
+
+       if (ioctl(interface -> rfdesc, SIOCGIFBRDADDR, &ifr) == -1)
+               error("Can't get old broadcast address of interface");
+
+       (*(struct sockaddr_in *)&ifr.ifr_broadaddr).sin_addr = broadcast_addr;
+       if (ioctl(sock, SIOCSIFBRDADDR, &ifr) == -1) {
+               error("Can't set new broadcast address");
+       }
+#endif
+}
+
+/* Set the IP address (in network byte order!) of this interface. */
+void set_ip_address(interface, ip_addr)
+     struct interface_info *interface;
+     struct in_addr ip_addr;
+{
+       note ("set_ip_address %s: %s",
+             interface -> name, inet_ntoa (ip_addr));
+#if 0
+       int sock;
+       struct ifreq ifr;
 
+       strncpy(ifr.ifr_name, interface->name, IFNAMSIZ);
+       if (ioctl(sock, SIOCGIFADDR, &ifr) < 0)
+               error ("Can't get old IP address of interface");
 
+       (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr = ip_addr;
+       if (ioctl(sock, SIOCSIFADDR, &ifr) < 0)
+               error("Can't set IP address");
+       }
+#endif
+}
index dcd758a57cf7ebf9291ecbc40abaeb9d0078eb22..7e7bc912ac8f57cecc062d8333710ac171d714b2 100644 (file)
--- a/socket.c
+++ b/socket.c
  * Enterprises, see ``http://www.vix.com''.
  */
 
+/* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu).
+ * This sockopt allows a socket to be bound to a particular interface,
+ * thus enabling the use of DHCPD on a multihomed host.
+ * If SO_BINDTODEVICE is defined in your system header files, the use of
+ * this sockopt will be automatically enabled. 
+ * I have implemented it under Linux; other systems should be doable also.
+ */
+
 #ifndef lint
 static char copyright[] =
-"$Id: socket.c,v 1.16 1996/08/27 09:54:48 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
+"$Id: socket.c,v 1.17 1997/01/02 12:00:18 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -62,6 +70,8 @@ int if_register_socket (info, interface)
        struct sockaddr_in name;
        int sock;
        int flag;
+
+#ifndef SO_BINDTODEVICE
        static int once = 0;
 
        /* Make sure only one interface is registered. */
@@ -72,22 +82,8 @@ int if_register_socket (info, interface)
                       "you must compile in BPF or NIT support.   If neither ",
                       "option is supported on your system, please let us ",
                       "know.");
-
-       /* Technically, we need to know what interface every packet
-          comes in on, which means that we can only operate on a
-          machine with a single interface configured.   However,
-          we generally don't expect to get broadcast packets on
-          point-to-point interfaces, so we can bend the rules a bit
-          and not count them.   This won't allow DHCP-over-PPP,
-          but it's probably right in a lot of cases, and the issue
-          will have to be revisited when DHCP-over-PPP support is
-          done.   Currently we determine whether an interface is
-          point-to-point by seeing if it has a link-level address.
-          This only works on 4.4BSD and derivative networking. */
-#ifdef AF_LINK
-       if (info -> hw_address.hlen) /* XXX */
+       once = 1;
 #endif
-               once = 1;
 
        /* Set up the address we're going to bind to. */
        name.sin_family = AF_INET;
@@ -111,10 +107,26 @@ int if_register_socket (info, interface)
                        (char *)&flag, sizeof flag) < 0)
                error ("Can't set SO_BROADCAST option on dhcp socket: %m");
 
+#ifndef USE_SOCKET_FALLBACK
+       /* The following will make all-ones broadcasts go out this interface
+        * on those platforms which use the standard sockets API (assuming 
+        * the OS-specific routines called by enable_sending() are present
+        * for this platform). */
+       if_enable (info);
+#endif
        /* Bind the socket to this interface's IP address. */
        if (bind (sock, (struct sockaddr *)&name, sizeof name) < 0)
                error ("Can't bind to dhcp address: %m");
 
+#ifdef SO_BINDTODEVICE
+       /* Bind this socket to this interface. */
+       if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
+                      (char *)interface, sizeof(*interface)) < 0) {
+               error("setting SO_BINDTODEVICE");
+       }
+#endif
+
        return sock;
 }
 #endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE */
@@ -197,3 +209,30 @@ size_t fallback_discard (interface)
                         (struct sockaddr *)&from, &flen);
 }
 #endif /* USE_SOCKET_RECEIVE */
+
+#if defined (USE_SOCKET_SEND) && !defined (USE_SOCKET_FALLBACK)
+/* If we're using the standard socket API without SO_BINDTODEVICE,
+ * we need this kludge to force DHCP broadcasts to go out
+ * this interface, even though it's not available for general
+ * use until we get a lease!
+ * This should work _OK_, but it will cause ALL all-ones
+ * broadcasts on this host to go out this interface--it
+ * could interfere with other interfaces.  And God help you
+ * if you run this on multiple interfaces simultaneously.
+ * SO_BINDTODEVICE really is better! */
+void if_enable (interface)
+     struct interface_info *interface;
+{
+#ifndef SO_BINDTODEVICE
+       struct in_addr broad_addr;
+       broad_addr.s_addr = htonl(INADDR_BROADCAST);
+
+       /* Delete old routes for broadcast address. */
+       remove_routes(NULL, &broad_addr);
+
+       /* Add a route for broadcast address to this interface. */
+       /* POTENTIAL PROBLEM: Don't do this to more than one interface! */
+       add_route_direct(interface, &broad_addr);
+#endif
+}
+#endif