mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-08-30 22:05:23 +00:00
Run as daemon after first binding; don't record leases defined in config file; remember leases even if the interface to which they're attached isn't presently configured; preferentially select a lease offer if it's for the address we asked for; if we don't successfully bind, iterate through list of remembered leases and see if one works; arp for lease before accepting it and send dhcpdecline if it's bogus; various bug fixes
This commit is contained in:
@@ -40,12 +40,23 @@
|
||||
* 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.
|
||||
* for use on Linux while he was working on the MosquitoNet project at
|
||||
* Stanford.
|
||||
*
|
||||
* The current version owes much to Elliot's Linux enhancements, but
|
||||
* was substantially reorganized and partially rewritten by Ted Lemon
|
||||
* so as to use the same networking framework that the Internet Software
|
||||
* Consortium DHCP server uses. Much system-specific configuration code
|
||||
* was moved into a shell script so that as support for more operating
|
||||
* systems is added, it will not be necessary to port and maintain
|
||||
* system-specific configuration code to these operating systems - instead,
|
||||
* the shell script can invoke the native tools to accomplish the same
|
||||
* purpose.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char copyright[] =
|
||||
"$Id: dhclient.c,v 1.23 1997/02/18 14:25:53 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n";
|
||||
"$Id: dhclient.c,v 1.24 1997/02/19 10:57:24 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "dhcpd.h"
|
||||
@@ -79,6 +90,7 @@ struct interface_info fallback_interface;
|
||||
u_int16_t local_port;
|
||||
u_int16_t remote_port;
|
||||
int log_priority;
|
||||
int no_daemon;
|
||||
|
||||
static void usage PROTO ((void));
|
||||
|
||||
@@ -108,6 +120,8 @@ int main (argc, argv, envp)
|
||||
local_port = htons (atoi (argv [i]));
|
||||
debug ("binding to user-specified port %d",
|
||||
ntohs (local_port));
|
||||
} else if (!strcmp (argv [i], "-d")) {
|
||||
no_daemon = 1;
|
||||
} else if (argv [i][0] == '-') {
|
||||
usage ();
|
||||
} else {
|
||||
@@ -138,9 +152,13 @@ int main (argc, argv, envp)
|
||||
/* Get the current time... */
|
||||
GET_TIME (&cur_time);
|
||||
|
||||
sockaddr_broadcast.sin_family = AF_INET;
|
||||
sockaddr_broadcast.sin_len = sizeof sockaddr_broadcast;
|
||||
sockaddr_broadcast.sin_port = remote_port;
|
||||
sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
#ifdef HAVE_SA_LEN
|
||||
sockaddr_broadcast.sin_len = sizeof sockaddr_broadcast;
|
||||
#endif
|
||||
|
||||
/* Discover all the network interfaces. */
|
||||
discover_interfaces (DISCOVER_UNCONFIGURED);
|
||||
@@ -254,6 +272,8 @@ void state_init (ip)
|
||||
void state_selecting (ip)
|
||||
struct interface_info *ip;
|
||||
{
|
||||
struct client_lease *lp, *next, *picked;
|
||||
|
||||
ASSERT_STATE(state, S_SELECTING);
|
||||
|
||||
/* Cancel state_selecting and send_discover timeouts, since either
|
||||
@@ -261,23 +281,56 @@ void state_selecting (ip)
|
||||
cancel_timeout (state_selecting, ip);
|
||||
cancel_timeout (send_discover, ip);
|
||||
|
||||
/* We have received one or more DHCPOFFER packets. Currently,
|
||||
the only criterion by which we judge leases is whether or
|
||||
not we get a response when we arp for them. */
|
||||
picked = (struct client_lease *)0;
|
||||
for (lp = ip -> client -> offered_leases; lp; lp = next) {
|
||||
next = lp -> next;
|
||||
|
||||
/* Check to see if we got an ARPREPLY for the address
|
||||
in this particular lease. */
|
||||
if (!picked) {
|
||||
script_init (ip, "ARPCHECK");
|
||||
script_write_params (ip, "check_", lp);
|
||||
|
||||
/* If the ARPCHECK code detects another
|
||||
machine using the offered address, it exits
|
||||
nonzero. We need to send a DHCPDECLINE and
|
||||
toss the lease. */
|
||||
if (script_go (ip)) {
|
||||
make_decline (ip, lp);
|
||||
send_decline (ip);
|
||||
goto freeit;
|
||||
}
|
||||
picked = lp;
|
||||
picked -> next = (struct client_lease *)0;
|
||||
} else {
|
||||
freeit:
|
||||
free_client_lease (lp);
|
||||
}
|
||||
}
|
||||
ip -> client -> offered_leases = (struct client_lease *)0;
|
||||
|
||||
/* If we just tossed all the leases we were offered, go back
|
||||
to square one. */
|
||||
if (!picked) {
|
||||
send_discover (ip);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Go to the REQUESTING state. */
|
||||
ip -> client -> destination = iaddr_broadcast;
|
||||
ip -> client -> state = S_REQUESTING;
|
||||
ip -> client -> first_sending = cur_time;
|
||||
|
||||
/* We have received one or more DHCPOFFER packets. We should
|
||||
be clever and pick the one that most closely matches our
|
||||
request, but for now we just pick the first one on the list
|
||||
and toss the rest. */
|
||||
while (ip -> client -> offered_leases -> next) {
|
||||
struct client_lease *lp =
|
||||
ip -> client -> offered_leases -> next;
|
||||
ip -> client -> offered_leases -> next = lp -> next;
|
||||
free_client_lease (lp);
|
||||
}
|
||||
make_request (ip, ip -> client -> offered_leases);
|
||||
/* Make a DHCPREQUEST packet from the lease we picked. */
|
||||
make_request (ip, picked);
|
||||
ip -> client -> xid = ip -> client -> packet.xid;
|
||||
|
||||
/* Toss the lease we picked - we'll get it back in a DHCPACK. */
|
||||
free_client_lease (picked);
|
||||
|
||||
/* Add an immediate timeout to send the first DHCPREQUEST packet. */
|
||||
send_request (ip);
|
||||
}
|
||||
@@ -379,6 +432,8 @@ void dhcpack (packet)
|
||||
note ("bound: renewal in %d seconds.",
|
||||
ip -> client -> active -> renewal - cur_time);
|
||||
ip -> client -> state = S_BOUND;
|
||||
reinitialize_interfaces ();
|
||||
go_daemon ();
|
||||
}
|
||||
|
||||
/* state_bound is called when we've successfully bound to a particular
|
||||
@@ -460,8 +515,9 @@ void dhcpoffer (packet)
|
||||
struct packet *packet;
|
||||
{
|
||||
struct interface_info *ip = packet -> interface;
|
||||
struct client_lease *lease;
|
||||
struct client_lease *lease, *lp;
|
||||
int i;
|
||||
int arp_timeout_needed, stop_selecting;
|
||||
|
||||
note ("DHCPOFFER from %s",
|
||||
print_hw_addr (packet -> raw -> htype,
|
||||
@@ -508,20 +564,62 @@ void dhcpoffer (packet)
|
||||
return;
|
||||
}
|
||||
|
||||
lease -> next = ip -> client -> offered_leases;
|
||||
ip -> client -> offered_leases = lease;
|
||||
/* Send out an ARP Request for the offered IP address. */
|
||||
script_init (ip, "ARPSEND");
|
||||
script_write_params (ip, "check_", lease);
|
||||
/* If the script can't send an ARP request without waiting,
|
||||
we'll be waiting when we do the ARPCHECK, so don't wait now. */
|
||||
if (script_go (ip))
|
||||
arp_timeout_needed = 0;
|
||||
else
|
||||
arp_timeout_needed = 2;
|
||||
|
||||
/* Figure out when we're supposed to stop selecting. */
|
||||
stop_selecting = (ip -> client -> first_sending +
|
||||
ip -> client -> config -> select_interval);
|
||||
|
||||
/* If this is the lease we asked for, put it at the head of the
|
||||
list, and don't mess with the arp request timeout. */
|
||||
if (lease -> address.len == ip -> client -> requested_address.len &&
|
||||
!memcmp (lease -> address.iabuf,
|
||||
ip -> client -> requested_address.iabuf,
|
||||
ip -> client -> requested_address.len)) {
|
||||
lease -> next = ip -> client -> offered_leases;
|
||||
ip -> client -> offered_leases = lease;
|
||||
} else {
|
||||
/* If we already have an offer, and arping for this
|
||||
offer would take us past the selection timeout,
|
||||
then don't extend the timeout - just hope for the
|
||||
best. */
|
||||
if (ip -> client -> offered_leases &&
|
||||
(cur_time + arp_timeout_needed) > stop_selecting)
|
||||
arp_timeout_needed = 0;
|
||||
|
||||
/* Put the lease at the end of the list. */
|
||||
lease -> next = (struct client_lease *)0;
|
||||
if (!ip -> client -> offered_leases)
|
||||
ip -> client -> offered_leases = lease;
|
||||
else {
|
||||
for (lp = ip -> client -> offered_leases; lp -> next;
|
||||
lp = lp -> next)
|
||||
;
|
||||
lp -> next = lease;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're supposed to stop selecting before we've had time
|
||||
to wait for the ARPREPLY, add some delay to wait for
|
||||
the ARPREPLY. */
|
||||
if (stop_selecting - cur_time < arp_timeout_needed)
|
||||
stop_selecting = cur_time + arp_timeout_needed;
|
||||
|
||||
/* If the selecting interval has expired, go immediately to
|
||||
state_selecting(). Otherwise, time out into
|
||||
state_selecting at the select interval. */
|
||||
if (ip -> client -> config -> select_interval <
|
||||
cur_time - ip -> client -> first_sending) {
|
||||
if (stop_selecting <= 0)
|
||||
state_selecting (ip);
|
||||
} else {
|
||||
add_timeout (ip -> client -> first_sending +
|
||||
ip -> client -> config -> select_interval,
|
||||
state_selecting, ip);
|
||||
}
|
||||
else
|
||||
add_timeout (stop_selecting, state_selecting, ip);
|
||||
}
|
||||
|
||||
/* Allocate a client_lease structure and initialize it from the parameters
|
||||
@@ -637,50 +735,8 @@ void send_discover (ip)
|
||||
/* If we're past the panic timeout, call the script and tell it
|
||||
we haven't found anything for this interface yet. */
|
||||
if (interval > ip -> client -> config -> timeout) {
|
||||
|
||||
/* If there's an active lease left over from the
|
||||
previous run, use it. */
|
||||
if (ip -> client -> active) {
|
||||
if (ip -> client -> active -> expiry < cur_time) {
|
||||
free (ip -> client -> active);
|
||||
ip -> client -> active =
|
||||
(struct client_lease *)0;
|
||||
} else {
|
||||
/* Run the client script with the existing
|
||||
parameters. */
|
||||
script_init (ip, "TIMEOUT");
|
||||
script_write_params (ip, "new_",
|
||||
ip -> client -> active);
|
||||
script_go (ip);
|
||||
|
||||
/* If the old lease is still good and doesn't
|
||||
yet need renewal, go into BOUND state and
|
||||
timeout at the renewal time. */
|
||||
if (cur_time <
|
||||
ip -> client -> active -> renewal) {
|
||||
ip -> client -> state = S_BOUND;
|
||||
note ("bound: renewal in %d seconds.",
|
||||
ip -> client -> active -> renewal
|
||||
- cur_time);
|
||||
add_timeout (ip -> client ->
|
||||
active -> renewal,
|
||||
state_bound, ip);
|
||||
} else {
|
||||
ip -> client -> state = S_BOUND;
|
||||
note ("bound: immediate renewal.");
|
||||
state_bound (ip);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
script_init ((struct interface_info *)0, "FAIL");
|
||||
script_go ((struct interface_info *)0);
|
||||
ip -> client -> state = S_INIT;
|
||||
add_timeout (cur_time +
|
||||
ip -> client -> config -> retry_interval,
|
||||
state_init, ip);
|
||||
return;
|
||||
}
|
||||
state_panic (ip);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Exponential backoff with random element. */
|
||||
@@ -716,6 +772,84 @@ void send_discover (ip)
|
||||
add_timeout (cur_time + interval, send_discover, ip);
|
||||
}
|
||||
|
||||
/* state_panic gets called if we haven't received any offers in a preset
|
||||
amount of time. When this happens, we try to use existing leases that
|
||||
haven't yet expired, and failing that, we call the client script and
|
||||
hope it can do something. */
|
||||
|
||||
void state_panic (ip)
|
||||
struct interface_info *ip;
|
||||
{
|
||||
struct client_lease *loop = ip -> client -> active;
|
||||
struct client_lease *lp;
|
||||
|
||||
note ("No DHCPOFFERS received.");
|
||||
|
||||
/* Run through the list of leases and see if one can be used. */
|
||||
while (ip -> client -> active) {
|
||||
if (ip -> client -> active -> expiry > cur_time) {
|
||||
/* Run the client script with the existing
|
||||
parameters. */
|
||||
script_init (ip, "TIMEOUT");
|
||||
script_write_params (ip, "new_",
|
||||
ip -> client -> active);
|
||||
|
||||
/* If the old lease is still good and doesn't
|
||||
yet need renewal, go into BOUND state and
|
||||
timeout at the renewal time. */
|
||||
if (!script_go (ip)) {
|
||||
if (cur_time <
|
||||
ip -> client -> active -> renewal) {
|
||||
ip -> client -> state = S_BOUND;
|
||||
note ("bound: renewal in %d seconds.",
|
||||
ip -> client -> active -> renewal
|
||||
- cur_time);
|
||||
add_timeout ((ip -> client ->
|
||||
active -> renewal),
|
||||
state_bound, ip);
|
||||
} else {
|
||||
ip -> client -> state = S_BOUND;
|
||||
note ("bound: immediate renewal.");
|
||||
state_bound (ip);
|
||||
}
|
||||
reinitialize_interfaces ();
|
||||
go_daemon ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are no other leases, give up. */
|
||||
if (!ip -> client -> leases) {
|
||||
ip -> client -> leases = ip -> client -> active;
|
||||
ip -> client -> active = (struct client_lease *)0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Otherwise, put the active lease at the end of the
|
||||
lease list, and try another lease.. */
|
||||
for (lp = ip -> client -> leases; lp -> next; lp = lp -> next)
|
||||
;
|
||||
lp -> next = ip -> client -> active;
|
||||
ip -> client -> active = ip -> client -> leases;
|
||||
ip -> client -> leases = ip -> client -> leases -> next;
|
||||
|
||||
/* If we already tried this lease, we've exhausted the
|
||||
set of leases, so we might as well give up for
|
||||
now. */
|
||||
if (ip -> client -> active == loop)
|
||||
break;
|
||||
}
|
||||
|
||||
/* No leases were available, or what was available didn't work, so
|
||||
tell the shell script that we failed to allocate an address,
|
||||
and try again later. */
|
||||
script_init (ip, "FAIL");
|
||||
script_go (ip);
|
||||
ip -> client -> state = S_INIT;
|
||||
add_timeout (cur_time + ip -> client -> config -> retry_interval,
|
||||
state_init, ip);
|
||||
}
|
||||
|
||||
void send_request (ip)
|
||||
struct interface_info *ip;
|
||||
{
|
||||
@@ -767,6 +901,10 @@ void send_request (ip)
|
||||
ip -> client -> destination.iabuf,
|
||||
sizeof destination.sin_addr.s_addr);
|
||||
destination.sin_port = remote_port;
|
||||
destination.sin_family = AF_INET;
|
||||
#ifdef HAVE_SA_LEN
|
||||
destination.sin_len = sizeof destination;
|
||||
#endif
|
||||
|
||||
if (ip -> client -> state != S_REQUESTING)
|
||||
memcpy (&from, ip -> client -> active -> address.iabuf,
|
||||
@@ -801,6 +939,44 @@ void send_request (ip)
|
||||
add_timeout (cur_time + interval, send_request, ip);
|
||||
}
|
||||
|
||||
void send_decline (ip)
|
||||
struct interface_info *ip;
|
||||
{
|
||||
int result;
|
||||
|
||||
note ("DHCPDECLINE on %s to %s port %d", ip -> name,
|
||||
inet_ntoa (sockaddr_broadcast.sin_addr),
|
||||
ntohs (sockaddr_broadcast.sin_port));
|
||||
|
||||
/* Send out a packet. */
|
||||
result = send_packet (ip, (struct packet *)0,
|
||||
&ip -> client -> packet,
|
||||
ip -> client -> packet_length,
|
||||
inaddr_any, &sockaddr_broadcast,
|
||||
(struct hardware *)0);
|
||||
if (result < 0)
|
||||
warn ("send_packet: %m");
|
||||
}
|
||||
|
||||
void send_release (ip)
|
||||
struct interface_info *ip;
|
||||
{
|
||||
int result;
|
||||
|
||||
note ("DHCPRELEASE on %s to %s port %d", ip -> name,
|
||||
inet_ntoa (sockaddr_broadcast.sin_addr),
|
||||
ntohs (sockaddr_broadcast.sin_port));
|
||||
|
||||
/* Send out a packet. */
|
||||
result = send_packet (ip, (struct packet *)0,
|
||||
&ip -> client -> packet,
|
||||
ip -> client -> packet_length,
|
||||
inaddr_any, &sockaddr_broadcast,
|
||||
(struct hardware *)0);
|
||||
if (result < 0)
|
||||
warn ("send_packet: %m");
|
||||
}
|
||||
|
||||
void make_discover (ip, lease)
|
||||
struct interface_info *ip;
|
||||
struct client_lease *lease;
|
||||
@@ -837,12 +1013,15 @@ void make_discover (ip, lease)
|
||||
|
||||
/* If we had an address, try to get it again. */
|
||||
if (lease) {
|
||||
ip -> client -> requested_address = lease -> address;
|
||||
options [DHO_DHCP_REQUESTED_ADDRESS] = &requested_address_tree;
|
||||
requested_address_tree.value = lease -> address.iabuf;
|
||||
requested_address_tree.len = lease -> address.len;
|
||||
requested_address_tree.buf_size = lease -> address.len;
|
||||
requested_address_tree.timeout = 0xFFFFFFFF;
|
||||
requested_address_tree.tree = (struct tree *)0;
|
||||
} else {
|
||||
ip -> client -> requested_address.len = 0;
|
||||
}
|
||||
|
||||
/* Set up the option buffer... */
|
||||
@@ -924,12 +1103,15 @@ void make_request (ip, lease)
|
||||
/* If we are requesting an address that hasn't yet been assigned
|
||||
to us, use the DHCP Requested Address option. */
|
||||
if (ip -> client -> state == S_REQUESTING) {
|
||||
ip -> client -> requested_address = lease -> address;
|
||||
options [DHO_DHCP_REQUESTED_ADDRESS] = &requested_address_tree;
|
||||
requested_address_tree.value = lease -> address.iabuf;
|
||||
requested_address_tree.len = lease -> address.len;
|
||||
requested_address_tree.buf_size = lease -> address.len;
|
||||
requested_address_tree.timeout = 0xFFFFFFFF;
|
||||
requested_address_tree.tree = (struct tree *)0;
|
||||
} else {
|
||||
ip -> client -> requested_address.len = 0;
|
||||
}
|
||||
|
||||
/* Set up the option buffer... */
|
||||
@@ -1134,6 +1316,9 @@ void rewrite_client_leases ()
|
||||
leaseFile = fopen (path_dhclient_db, "w");
|
||||
if (!leaseFile)
|
||||
error ("can't create /var/db/dhclient.leases: %m");
|
||||
|
||||
/* Write out all the leases attached to configured interfaces that
|
||||
we know about. */
|
||||
for (ip = interfaces; ip; ip = ip -> next) {
|
||||
for (lp = ip -> client -> leases; lp; lp = lp -> next) {
|
||||
write_client_lease (ip, lp);
|
||||
@@ -1141,6 +1326,16 @@ void rewrite_client_leases ()
|
||||
if (ip -> client -> active)
|
||||
write_client_lease (ip, ip -> client -> active);
|
||||
}
|
||||
|
||||
/* Write out any leases that are attached to interfaces that aren't
|
||||
currently configured. */
|
||||
for (ip = dummy_interfaces; ip; ip = ip -> next) {
|
||||
for (lp = ip -> client -> leases; lp; lp = lp -> next) {
|
||||
write_client_lease (ip, lp);
|
||||
}
|
||||
if (ip -> client -> active)
|
||||
write_client_lease (ip, ip -> client -> active);
|
||||
}
|
||||
fflush (leaseFile);
|
||||
}
|
||||
|
||||
@@ -1151,6 +1346,11 @@ void write_client_lease (ip, lease)
|
||||
int i;
|
||||
struct tm *t;
|
||||
|
||||
/* If the lease came from the config file, we don't need to stash
|
||||
a copy in the lease database. */
|
||||
if (lease -> is_static)
|
||||
return;
|
||||
|
||||
if (!leaseFile) { /* XXX */
|
||||
leaseFile = fopen (path_dhclient_db, "w");
|
||||
if (!leaseFile)
|
||||
@@ -1297,3 +1497,30 @@ char *dhcp_option_ev_name (option)
|
||||
evbuf [i] = 0;
|
||||
return evbuf;
|
||||
}
|
||||
|
||||
void go_daemon ()
|
||||
{
|
||||
static int state = 0;
|
||||
int pid;
|
||||
|
||||
/* Don't become a daemon if the user requested otherwise. */
|
||||
if (no_daemon)
|
||||
return;
|
||||
|
||||
/* Only do it once. */
|
||||
if (state)
|
||||
return;
|
||||
state = 1;
|
||||
|
||||
/* Stop logging to stderr... */
|
||||
log_perror = 0;
|
||||
|
||||
/* Become a daemon... */
|
||||
if ((pid = fork ()) < 0)
|
||||
error ("Can't fork daemon: %m");
|
||||
else if (pid)
|
||||
exit (0);
|
||||
/* Become session leader and get pid... */
|
||||
pid = setsid ();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user