+config.h now has the compile time options, instead of being dotted around.
+Added -E option, which reads the last info file and uses the information there
+if we timeout and the lease is still valid, thanks to Roberto Angelino.
Compiles ok on NetBSD, but doesn't add routes yet - thanks to Gabor Z. Papp.
If the current hostname contains dots then send it as a FQDN.
Ensure that static routes are always added before routers.
-VERSION = 3.0.18_pre3
+VERSION = 3.1.0_pre1
CFLAGS ?= -O2 -pipe
# Should work for both GNU make and BSD make
dhcpcd_H = version.h
dhcpcd_OBJS = arp.o client.o common.o configure.o dhcp.o dhcpcd.o \
- interface.o logger.o signals.o socket.o
+ info.o interface.o logger.o signals.o socket.o
# By default we don't need to link to anything
# Except on Darwin where we need -lresolv, so they need to uncomment this
#include <string.h>
#include <unistd.h>
+#include "config.h"
#include "common.h"
#include "arp.h"
#include "interface.h"
#define arphdr_len(ap) (arphdr_len2 ((ap)->ar_hln, (ap)->ar_pln))
#endif
+#ifdef ENABLE_ARP
int arp_check (interface_t *iface, struct in_addr address)
{
union {
iface->fd = -1;
return 0;
}
-
+#endif
#ifndef ARP_H
#define ARP_H
+#ifdef ENABLE_ARP
#include <netinet/in.h>
#include "interface.h"
int arp_check (interface_t *iface, struct in_addr address);
+#endif
#endif
#include <sys/types.h>
#include <sys/select.h>
+#include <sys/time.h>
#include <arpa/inet.h>
#ifdef __linux__
#include <netinet/ether.h>
#include <time.h>
#include <unistd.h>
+#include "config.h"
#include "common.h"
+#ifdef ENABLE_ARP
#include "arp.h"
+#endif
#include "client.h"
#include "configure.h"
#include "dhcp.h"
#include "dhcpcd.h"
+#ifdef ENABLE_INFO
+#include "info.h"
+#endif
#include "interface.h"
#include "logger.h"
#include "signals.h"
if (timeout > 0 || (options->timeout == 0 &&
(state != STATE_INIT || xid)))
{
- if (options->timeout == 0 ||
+ if ((options->timeout == 0 && xid) ||
(dhcp->leasetime == (unsigned) -1 && state == STATE_BOUND))
{
int retry = 0;
if (xid == 0)
xid = random_xid ();
else {
+ SOCKET_MODE (SOCKET_CLOSED);
logger (LOG_ERR, "timed out");
+#ifdef ENABLE_INFO
+ if (options->dolastlease) {
+ unsigned int offset = 0;
+
+ logger (LOG_INFO, "trying to use old lease in `%s'",
+ iface->infofile);
+ if (! read_info (iface, dhcp)) {
+ if (! daemonised) {
+ retval = EXIT_FAILURE;
+ goto eexit;
+ }
+ }
+
+ /* Ensure that we can still use the lease */
+ if (gettimeofday (&tv, NULL) == -1) {
+ logger (LOG_ERR, "gettimeofday: %s", strerror (errno));
+ retval = EXIT_FAILURE;
+ goto eexit;
+ }
+
+ offset = tv.tv_sec - dhcp->leasedfrom;
+ if (dhcp->leasedfrom &&
+ tv.tv_sec - dhcp->leasedfrom > dhcp->leasetime)
+ {
+ logger (LOG_ERR, "lease expired %u seconds ago",
+ offset + dhcp->leasetime);
+ if (! daemonised) {
+ retval = EXIT_FAILURE;
+ goto eexit;
+ }
+ } else {
+ logger (LOG_INFO, "using last known IP address %s",
+ inet_ntoa (dhcp->address));
+ if (configure (options, iface, dhcp)) {
+ retval = EXIT_FAILURE;
+ goto eexit;
+ }
+
+ state = STATE_BOUND;
+ /* We'll timeout above if timeout is negative,
+ * so no need for special handling */
+ if (dhcp->leasedfrom == 0)
+ offset = 0;
+ timeout = dhcp->renewaltime - offset;
+ iface->start_uptime = uptime ();
+
+ if (! daemonised && options->daemonise) {
+ if ((daemonise (options->pidfile)) < 0) {
+ retval = -1;
+ goto eexit;
+ }
+ daemonised = true;
+ }
+ continue;
+ }
+ }
+#endif
if (! daemonised) {
retval = EXIT_FAILURE;
goto eexit;
logger (LOG_ERR, "lost lease, attemping to rebind");
memset (&dhcp->address, 0, sizeof (struct in_addr));
SOCKET_MODE (SOCKET_OPEN);
+ if (xid == 0)
+ xid = random_xid ();
SEND_MESSAGE (DHCP_REQUEST);
timeout = dhcp->leasetime - dhcp->rebindtime;
state = STATE_REQUESTING;
case STATE_REBINDING:
if (type == DHCP_ACK) {
SOCKET_MODE (SOCKET_CLOSED);
+#ifdef ENABLE_ARP
if (options->doarp && iface->previous_address.s_addr !=
dhcp->address.s_addr)
{
continue;
}
}
+#endif
if (dhcp->leasetime == (unsigned) -1) {
dhcp->renewaltime = dhcp->rebindtime = dhcp->leasetime;
{
struct timespec tp;
- if (clock_gettime(CLOCK_MONOTONIC, &tp) == -1) {
+ if (clock_gettime (CLOCK_MONOTONIC, &tp) == -1) {
logger (LOG_ERR, "clock_gettime: %s", strerror (errno));
return -1;
}
#include <stdlib.h>
#include <unistd.h>
+#include "config.h"
#include "common.h"
#include "configure.h"
#include "dhcp.h"
+#ifdef ENABLE_INFO
+#include "info.h"
+#endif
#include "interface.h"
#include "dhcpcd.h"
#include "pathnames.h"
logger (LOG_DEBUG, "exec \"%s %s %s\"", script, infofile, arg);
exec_cmd (script, infofile, arg, (char *) NULL);
#else
- logger (LOG_DEBUG, "exec \"%s \"\" %s\"", script, infofile, arg);
- exec_cmd (script, infofile, "", arg, (char *) NULL);
+ infofile = NULL; /* appease gcc */
+ logger (LOG_DEBUG, "exec \"%s \"\" %s\"", script, arg);
+ exec_cmd (script, "", arg, (char *) NULL);
#endif
}
}
#endif
-#ifdef ENABLE_INFO
-static char *cleanmetas (const char *cstr)
-{
- /* The largest single element we can have is 256 bytes according to the RFC,
- so this buffer size should be safe even if it's all ' */
- static char buffer[1024];
- char *b = buffer;
-
- memset (buffer, 0, sizeof (buffer));
- if (cstr == NULL || strlen (cstr) == 0)
- return b;
-
- do
- if (*cstr == 39) {
- *b++ = '\'';
- *b++ = '\\';
- *b++ = '\'';
- *b++ = '\'';
- } else
- *b++ = *cstr;
- while (*cstr++);
-
- *b++ = 0;
- b = buffer;
-
- return b;
-}
-
-static int write_info(const interface_t *iface, const dhcp_t *dhcp,
- const options_t *options)
-{
- FILE *f;
- route_t *route;
- address_t *address;
-
- logger (LOG_DEBUG, "writing %s", iface->infofile);
- if ((f = fopen (iface->infofile, "w")) == NULL) {
- logger (LOG_ERR, "fopen `%s': %s", iface->infofile, strerror (errno));
- return -1;
- }
-
- fprintf (f, "IPADDR='%s'\n", inet_ntoa (dhcp->address));
- fprintf (f, "NETMASK='%s'\n", inet_ntoa (dhcp->netmask));
- fprintf (f, "BROADCAST='%s'\n", inet_ntoa (dhcp->broadcast));
- if (dhcp->mtu > 0)
- fprintf (f, "MTU='%d'\n", dhcp->mtu);
-
- if (dhcp->routes) {
- bool doneone = false;
- fprintf (f, "ROUTES='");
- for (route = dhcp->routes; route; route = route->next) {
- if (route->destination.s_addr != 0) {
- if (doneone)
- fprintf (f, " ");
- fprintf (f, "%s", inet_ntoa (route->destination));
- fprintf (f, ",%s", inet_ntoa (route->netmask));
- fprintf (f, ",%s", inet_ntoa (route->gateway));
- doneone = true;
- }
- }
- fprintf (f, "'\n");
-
- doneone = false;
- fprintf (f, "GATEWAYS='");
- for (route = dhcp->routes; route; route = route->next) {
- if (route->destination.s_addr == 0) {
- if (doneone)
- fprintf (f, " ");
- fprintf (f, "%s", inet_ntoa (route->gateway));
- doneone = true;
- }
- }
- fprintf (f, "'\n");
- }
-
- if (dhcp->hostname)
- fprintf (f, "HOSTNAME='%s'\n", cleanmetas (dhcp->hostname));
-
- if (dhcp->dnsdomain)
- fprintf (f, "DNSDOMAIN='%s'\n", cleanmetas (dhcp->dnsdomain));
-
- if (dhcp->dnssearch)
- fprintf (f, "DNSSEARCH='%s'\n", cleanmetas (dhcp->dnssearch));
-
- if (dhcp->dnsservers) {
- fprintf (f, "DNSSERVERS='");
- for (address = dhcp->dnsservers; address; address = address->next) {
- fprintf (f, "%s", inet_ntoa (address->address));
- if (address->next)
- fprintf (f, " ");
- }
- fprintf (f, "'\n");
- }
-
- if (dhcp->fqdn) {
- fprintf (f, "FQDNFLAGS='%u'\n", dhcp->fqdn->flags);
- fprintf (f, "FQDNRCODE1='%u'\n", dhcp->fqdn->r1);
- fprintf (f, "FQDNRCODE2='%u'\n", dhcp->fqdn->r2);
- fprintf (f, "FQDNHOSTNAME='%s'\n", dhcp->fqdn->name);
- }
-
- if (dhcp->ntpservers) {
- fprintf (f, "NTPSERVERS='");
- for (address = dhcp->ntpservers; address; address = address->next) {
- fprintf (f, "%s", inet_ntoa (address->address));
- if (address->next)
- fprintf (f, " ");
- }
- fprintf (f, "'\n");
- }
-
- if (dhcp->nisdomain)
- fprintf (f, "NISDOMAIN='%s'\n", cleanmetas (dhcp->nisdomain));
-
- if (dhcp->nisservers) {
- fprintf (f, "NISSERVERS='");
- for (address = dhcp->nisservers; address; address = address->next) {
- fprintf (f, "%s", inet_ntoa (address->address));
- if (address->next)
- fprintf (f, " ");
- }
- fprintf (f, "'\n");
- }
-
- if (dhcp->rootpath)
- fprintf (f, "ROOTPATH='%s'\n", cleanmetas (dhcp->rootpath));
-
- fprintf (f, "DHCPSID='%s'\n", inet_ntoa (dhcp->serveraddress));
- fprintf (f, "DHCPSNAME='%s'\n", cleanmetas (dhcp->servername));
- fprintf (f, "LEASETIME='%u'\n", dhcp->leasetime);
- fprintf (f, "RENEWALTIME='%u'\n", dhcp->renewaltime);
- fprintf (f, "REBINDTIME='%u'\n", dhcp->rebindtime);
- fprintf (f, "INTERFACE='%s'\n", iface->name);
- fprintf (f, "CLASSID='%s'\n", cleanmetas (options->classid));
- if (options->clientid[0])
- fprintf (f, "CLIENTID='%s'\n", cleanmetas (options->clientid));
- else
- fprintf (f, "CLIENTID='%s'\n", hwaddr_ntoa (iface->hwaddr, iface->hwlen));
- fprintf (f, "DHCPCHADDR='%s'\n", hwaddr_ntoa (iface->hwaddr, iface->hwlen));
-
-#ifdef ENABLE_INFO_COMPAT
- /* Support the old .info settings if we need to */
- fprintf (f, "\n# dhcpcd-1.x and 2.x compatible variables\n");
- if (dhcp->dnsservers) {
- fprintf (f, "DNS='");
- for (address = dhcp->dnsservers; address; address = address->next) {
- fprintf (f, "%s", inet_ntoa (address->address));
- if (address->next)
- fprintf (f, ",");
- }
- fprintf (f, "'\n");
- }
-
- if (dhcp->routes) {
- bool doneone = false;
- fprintf (f, "GATEWAY='");
- for (route = dhcp->routes; route; route = route->next) {
- if (route->destination.s_addr == 0) {
- if (doneone)
- fprintf (f, ",");
- fprintf (f, "%s", inet_ntoa (route->gateway));
- doneone = true;
- }
- }
- fprintf (f, "'\n");
- }
-#endif
-
- fprintf (f, "\n");
- fclose (f);
- return 0;
-}
-#endif
-
int configure (const options_t *options, interface_t *iface,
const dhcp_t *dhcp)
{
}
#ifdef ENABLE_INFO
- write_info (iface, dhcp, options);
+ if (! dhcp->frominfo)
+ write_info (iface, dhcp, options);
#endif
if (iface->previous_address.s_addr != dhcp->address.s_addr ||
#ifndef DHCPCONFIG_H
#define DHCPCONFIG_H
-/* If you disable all 3 options you can shrink the binary by around 5-10k
- unstripped depending on platform and CFLAGS
- */
-#define ENABLE_NTP
-#define ENABLE_NIS
-#define ENABLE_INFO
-
-/* Define this to enable some compatability with 1.x and 2.x info files */
-// #define ENABLE_INFO_COMPAT
-
#include "dhcpcd.h"
#include "interface.h"
#include "dhcp.h"
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/time.h>
#include <netinet/in.h>
#include <net/if_arp.h>
+#include <errno.h>
#include <limits.h>
#include <math.h>
#include <stdint.h>
unsigned int len = 0;
int i;
int retval = -1;
+ struct timeval tv;
route_t *routers = NULL;
route_t *routersp = NULL;
route_t *static_routes = NULL;
end += sizeof (message->options);
+ if (gettimeofday (&tv, NULL) == -1) {
+ logger (LOG_ERR, "gettimeofday: %s", strerror (errno));
+ return (-1);
+ }
+ dhcp->leasedfrom = tv.tv_sec;
+ dhcp->frominfo = false;
+
dhcp->address.s_addr = message->yiaddr;
strlcpy (dhcp->servername, message->servername,
sizeof (dhcp->servername));
struct in_addr broadcast;
unsigned short mtu;
+ unsigned int leasedfrom;
unsigned int leasetime;
unsigned int renewaltime;
unsigned int rebindtime;
char *message;
char *rootpath;
+
+ bool frominfo;
} dhcp_t;
.in +.5i
.ti -.5i
dhcpcd
-\%[\-adknpGHNRY]
+\%[\-adknpGHMNRTY]
\%[\-c\ script]
\%[\-h\ hostname]
\%[\-i\ vendorClassID]
.I /etc/resolv.conf
or using resolvconf.
.TP
+.BI \-T
+Will read
+.I /var/lib/dhcpcd/dhcpcd-<interface>.info
+file and use last known good lease if
+.B dhcpcd
+is unable to reach the DHCP server and the lease has not expired.
+This puts \fBdhcpcd\fR into the BOUND or REBINDING state depending on
+how long the lease has until expiry.
+.TP
.BI \-Y
Prevents
.B dhcpcd
static void usage ()
{
- printf ("usage: "PACKAGE" [-adknpGHMNRY] [-c script] [-h hostame] [-i classID]\n"
+ printf ("usage: "PACKAGE" [-adknpGHLMNRY] [-c script] [-h hostame] [-i classID]\n"
" [-l leasetime] [-m metric] [-s ipaddress] [-t timeout]\n"
" [-u userclass] [-F [none | ptr | both]] [-I clientID]\n");
}
{"request", required_argument, NULL, 's'},
{"timeout", required_argument, NULL, 't'},
{"userclass", required_argument, NULL, 'u'},
- {"fqdn", optional_argument, NULL, 'F'},
+ {"lastlease", no_argument, NULL, 'E'},
+ {"fqdn", optional_argument, NULL, 'F'},
{"nogateway", no_argument, NULL, 'G'},
{"sethostname", no_argument, NULL, 'H'},
{"clientid", required_argument, NULL, 'I'},
{"nomtu", no_argument, NULL, 'M'},
{"nontp", no_argument, NULL, 'N'},
{"nodns", no_argument, NULL, 'R'},
- {"nonis", no_argument, NULL, 'Y'},
+ {"nonis", no_argument, NULL, 'Y'},
{"help", no_argument, &dohelp, 1},
{"version", no_argument, &doversion, 1},
{NULL, 0, NULL, 0}
options.daemonise = true;
options.timeout = DEFAULT_TIMEOUT;
- while ((ch = getopt_long(argc, argv, "ac:dh:i:kl:m:nps:t:u:F:GHI:MNRY", longopts,
+ while ((ch = getopt_long(argc, argv, "ac:dh:i:kl:m:nps:t:u:EF:GHI:MNRY", longopts,
&option_index)) != -1)
switch (ch) {
case 0:
options.userclass_len += (strlen (optarg)) + 1;
}
break;
+ case 'E':
+ options.dolastlease = true;
+ break;
case 'F':
if (strncmp (optarg, "none", strlen (optarg)) == 0)
options.fqdn = FQDN_NONE;
bool domtu;
bool donis;
bool dontp;
+ bool dolastlease;
int signal;
bool persistent;