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
#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"
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
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 */
#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>
-/* 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"
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
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);
/* 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;
packet -> raw -> chaddr));
dump_packet (packet);
+ note ("DHCPREQUEST to %s", packet -> interface -> name);
send_request (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;
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;
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;
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
}
#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"
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
#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"
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);
#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"
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
* 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"
struct sockaddr_in name;
int sock;
int flag;
+
+#ifndef SO_BINDTODEVICE
static int once = 0;
/* Make sure only one interface is registered. */
"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;
(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 */
(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
-/* 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"
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
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);
/* 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;
packet -> raw -> chaddr));
dump_packet (packet);
+ note ("DHCPREQUEST to %s", packet -> interface -> name);
send_request (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;
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;
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;
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
}
#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 */
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)
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
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
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 *));
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));
#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"
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);
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 */
#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>
#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 */
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)
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
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
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 *));
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));
#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"
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
-/* 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
+}
* 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"
struct sockaddr_in name;
int sock;
int flag;
+
+#ifndef SO_BINDTODEVICE
static int once = 0;
/* Make sure only one interface is registered. */
"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;
(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 */
(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