Add RFC 3927 (aka IPV4LL aka APIPA) support by default.
+We now do ARP checking by default as recommended by RFC 2131.
+Add RFC 3927 (aka IPV4LL aka APIPA) support by default.
Suport DHCP option (52) overload.
Added -T test option. This just sends a DHCP_DISCOVER message and then
prints the configuration data to stdout - we don't request a lease,
-VERSION = 3.1.0_pre4
+VERSION = 3.1.0_pre5
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 duid.o \
- info.o interface.o logger.o signals.o socket.o
+ info.o interface.o ipv4ll.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 <net/if.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
+#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "logger.h"
#include "socket.h"
-/* Longer is safer and slower - 2 seconds seems a happy medium */
-#define TIMEOUT 2
+/* These are really for IPV4LL */
+#define NPROBES 3
+#define PROBE_INTERVAL 200000
+#define NCLAIMS 2
+#define CLAIM_INTERVAL 200000
/* Linux does not seem to define these handy macros */
#ifndef ar_sha
#define IP_MIN_FRAME_LENGTH 46
#ifdef ENABLE_ARP
-int arp_check (interface_t *iface, struct in_addr address)
+
+static int send_arp (interface_t *iface, int op, struct in_addr sip,
+ unsigned char *taddr, struct in_addr tip)
{
struct arphdr *arp;
- struct arphdr *reply = NULL;
- long timeout = 0;
- unsigned char *buffer;
int arpsize = arphdr_len2 (iface->hwlen, sizeof (struct in_addr));
- int retval = 0;
-
- if (! iface->arpable) {
- logger (LOG_DEBUG, "arp_check: interface `%s' is not ARPable",
- iface->name);
- return (0);
- }
+ int retval;
- inet_aton ("192.168.0.3", &address);
arp = xmalloc (arpsize);
memset (arp, 0, arpsize);
arp->ar_pro = htons (ETHERTYPE_IP);
arp->ar_hln = iface->hwlen;
arp->ar_pln = sizeof (struct in_addr);
- arp->ar_op = htons (ARPOP_REQUEST);
+ arp->ar_op = htons (op);
memcpy (ar_sha (arp), &iface->hwaddr, arp->ar_hln);
- memcpy (ar_tpa (arp), &address, arp->ar_pln);
+ memcpy (ar_spa (arp), &sip, arp->ar_pln);
+ if (taddr)
+ memcpy (ar_tha (arp), taddr, arp->ar_hln);
+ memcpy (ar_tpa (arp), &tip, arp->ar_pln);
+
+ retval = send_packet (iface, ETHERTYPE_ARP,
+ (unsigned char *) arp, arphdr_len (arp));
+ free (arp);
+ return (retval);
+}
+
+int arp_claim (interface_t *iface, struct in_addr address)
+{
+ struct arphdr *reply = NULL;
+ long timeout = 0;
+ unsigned char *buffer;
+ int retval = -1;
+ int nprobes = 0;
+ int nclaims = 0;
+ struct in_addr null_address;
+
+ if (! iface->arpable) {
+ logger (LOG_DEBUG, "interface `%s' is not ARPable", iface->name);
+ return (0);
+ }
logger (LOG_INFO, "checking %s is available on attached networks",
inet_ntoa (address));
- open_socket (iface, true);
- send_packet (iface, ETHERTYPE_ARP, (unsigned char *) arp,
- arphdr_len (arp));
+ if (! open_socket (iface, true))
+ return (0);
- timeout = uptime() + TIMEOUT;
+ memset (&null_address, 0, sizeof (null_address));
buffer = xmalloc (sizeof (char *) * iface->buffer_length);
int buflen = sizeof (char *) * iface->buffer_length;
fd_set rset;
int bytes;
+ int s;
- tv.tv_sec = timeout - uptime();
- tv.tv_usec = 0;
-
- if (tv.tv_sec < 1)
- break; /* Time out */
+ tv.tv_sec = 0;
+ tv.tv_usec = timeout;
FD_ZERO (&rset);
FD_SET (iface->fd, &rset);
- if (select (FD_SETSIZE, &rset, NULL, NULL, &tv) == 0)
+ errno = 0;
+ if ((s = select (FD_SETSIZE, &rset, NULL, NULL, &tv)) == -1) {
+ if (errno != EINTR)
+ logger (LOG_ERR, "select: `%s'", strerror (errno));
break;
-
+ } else if (s == 0) {
+ /* Timed out */
+ if (nprobes < NPROBES) {
+ nprobes ++;
+ timeout = PROBE_INTERVAL;
+ logger (LOG_DEBUG, "sending ARP probe #%d", nprobes);
+ send_arp (iface, ARPOP_REQUEST, null_address, NULL, address);
+ } else if (nclaims < NCLAIMS) {
+ nclaims ++;
+ timeout = CLAIM_INTERVAL;
+ logger (LOG_DEBUG, "sending ARP claim #%d", nclaims);
+ send_arp (iface, ARPOP_REQUEST, address, iface->hwaddr, address);
+ } else {
+ /* No replies, so done */
+ retval = 0;
+ break;
+ }
+ }
+
if (! FD_ISSET (iface->fd, &rset))
continue;
-
+
memset (buffer, 0, buflen);
while (bufpos != 0) {
union {
eexit:
close (iface->fd);
iface->fd = -1;
- free (arp);
free (buffer);
free (reply);
return (retval);
#include "interface.h"
-int arp_check (interface_t *iface, struct in_addr address);
+int arp_claim (interface_t *iface, struct in_addr address);
#endif
#endif
#include "dhcpcd.h"
#include "info.h"
#include "interface.h"
+#ifdef ENABLE_IPV4LL
+# include "ipv4ll.h"
+#endif
#include "logger.h"
#include "signals.h"
#include "socket.h"
return (true);
}
-static unsigned long random_xid (void)
-{
- static int initialized;
-
- if (! initialized) {
- int fd;
- unsigned long seed;
-
- fd = open ("/dev/urandom", 0);
- if (fd < 0 || read (fd, &seed, sizeof(seed)) < 0) {
- logger (LOG_WARNING, "Could not load seed from /dev/urandom: %s",
- strerror (errno));
- seed = time (0);
- }
- if (fd >= 0)
- close(fd);
-
- srand(seed);
- initialized++;
- }
-
- return rand();
-}
-
#ifdef ENABLE_INFO
-static bool get_old_lease (interface_t *iface, dhcp_t *dhcp, long *timeout)
+static bool get_old_lease (const options_t *options, interface_t *iface,
+ dhcp_t *dhcp, long *timeout)
{
struct timeval tv;
unsigned int offset = 0;
memset (&dhcp->serveraddress, 0, sizeof (struct in_addr));
memset (dhcp->servername, 0, sizeof (dhcp->servername));
+#ifdef ENABLE_ARP
+ /* Check that no-one is using the address */
+ if ((options->dolastlease ||
+ IN_LINKLOCAL (dhcp->address.s_addr)) &&
+ arp_claim (iface, dhcp->address)) {
+ memset (&dhcp->address, 0, sizeof (struct in_addr));
+ memset (&dhcp->netmask, 0, sizeof (struct in_addr));
+ memset (&dhcp->broadcast, 0, sizeof (struct in_addr));
+ return (false);
+ }
+
+ /* Ok, lets use this */
+ if (IN_LINKLOCAL (dhcp->address.s_addr))
+ return (true);
+#endif
/* Ensure that we can still use the lease */
if (gettimeofday (&tv, NULL) == -1) {
if (timeout)
*timeout = dhcp->renewaltime - offset;
iface->start_uptime = uptime ();
- logger (LOG_INFO, "using last known IP address %s", inet_ntoa (dhcp->address));
return (true);
}
#endif
(options->doinform || options->dorequest))
{
#ifdef ENABLE_INFO
- if (! get_old_lease (iface, dhcp, NULL))
+ if (! get_old_lease (options, iface, dhcp, NULL))
#endif
{
free (dhcp);
{
logger (LOG_INFO, "received SIGHUP, releasing lease");
SOCKET_MODE (SOCKET_OPEN);
- xid = random_xid ();
+ xid = random ();
if ((open_socket (iface, false)) >= 0)
SEND_MESSAGE (DHCP_RELEASE);
SOCKET_MODE (SOCKET_CLOSED);
/* timed out */
switch (state) {
case STATE_INIT:
- if (iface->previous_address.s_addr != 0 && ! options->doinform) {
+ if (iface->previous_address.s_addr != 0 &&
+#ifdef ENABLE_IPV4LL
+ ! IN_LINKLOCAL (iface->previous_address.s_addr) &&
+#endif
+ ! options->doinform) {
logger (LOG_ERR, "lost lease");
xid = 0;
SOCKET_MODE (SOCKET_CLOSED);
}
if (xid == 0)
- xid = random_xid ();
+ xid = random ();
else {
SOCKET_MODE (SOCKET_CLOSED);
logger (LOG_ERR, "timed out");
+
+ free_dhcp (dhcp);
+ memset (dhcp, 0, sizeof (dhcp_t));
#ifdef ENABLE_INFO
- if (options->dolastlease) {
- if (! get_old_lease (iface, dhcp, &timeout))
- if (! daemonised ||
- configure (options, iface, dhcp, true))
- {
- retval = EXIT_FAILURE;
- goto eexit;
- }
+ if (! get_old_lease (options, iface, dhcp, &timeout)) {
+ if (options->dolastlease) {
+ retval = EXIT_FAILURE;
+ goto eexit;
+ }
+
+ free_dhcp (dhcp);
+ memset (dhcp, 0, sizeof (dhcp_t));
+ }
+#endif
+
+#ifdef ENABLE_IPV4LL
+ if (! dhcp->address.s_addr ||
+ (! IN_LINKLOCAL (dhcp->address.s_addr) &&
+ ! options->dolastlease))
+ {
+ logger (LOG_INFO, "probing for an IPV4LL address");
+ free_dhcp (dhcp);
+ memset (dhcp, 0, sizeof (dhcp_t));
+ if (ipv4ll_get_address (iface, dhcp) == -1)
+ break;
+ timeout = dhcp->renewaltime;
+ }
+#endif
+
+#if defined (ENABLE_INFO) || defined (ENABLE_IPV4LL)
+ if (dhcp->address.s_addr)
+ {
+ if (configure (options, iface, dhcp, true) < 0 &&
+ ! daemonised)
+ {
+ retval = EXIT_FAILURE;
+ goto eexit;
+ }
state = STATE_BOUND;
if (! daemonised && options->daemonise) {
if ((daemonise (pidfd)) < 0) {
- retval = -1;
+ retval = EXIT_FAILURE;
goto eexit;
}
daemonised = true;
- }
- continue;
+ }
+
+ timeout = dhcp->renewaltime;
+ xid = 0;
+ break;
}
#endif
+
if (! daemonised) {
retval = EXIT_FAILURE;
goto eexit;
break;
case STATE_BOUND:
case STATE_RENEW_REQUESTED:
+#ifdef ENABLE_IPV4LL
+ if (IN_LINKLOCAL (dhcp->address.s_addr)) {
+ memset (&dhcp->address, 0, sizeof (struct in_addr));
+ state = STATE_INIT;
+ xid = 0;
+ break;
+ }
+#endif
state = STATE_RENEWING;
- xid = random_xid ();
+ xid = random ();
case STATE_RENEWING:
iface->start_uptime = uptime ();
+ free_dhcp (dhcp);
logger (LOG_INFO, "renewing lease of %s", inet_ntoa
(dhcp->address));
SOCKET_MODE (SOCKET_OPEN);
memset (&dhcp->address, 0, sizeof (struct in_addr));
SOCKET_MODE (SOCKET_OPEN);
if (xid == 0)
- xid = random_xid ();
+ xid = random ();
SEND_MESSAGE (DHCP_REQUEST);
timeout = dhcp->leasetime - dhcp->rebindtime;
state = STATE_REQUESTING;
if (options->doarp && iface->previous_address.s_addr !=
dhcp->address.s_addr)
{
- if (arp_check (iface, dhcp->address)) {
+ if (arp_claim (iface, dhcp->address)) {
SOCKET_MODE (SOCKET_OPEN);
SEND_MESSAGE (DHCP_DECLINE);
SOCKET_MODE (SOCKET_CLOSED);
#include <errno.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include "common.h"
#include "logger.h"
+/* OK, this should be in dhcpcd.c
+ * It's here to make dhcpcd more readable */
+#ifdef __linux__
+#include <fcntl.h>
+#include <unistd.h>
+void srandomdev (void) {
+ int fd;
+ unsigned long seed;
+
+ fd = open ("/dev/urandom", 0);
+ if (fd == -1 || read (fd, &seed, sizeof(seed)) == -1) {
+ logger (LOG_WARNING, "Could not load seed from /dev/urandom: %s",
+ strerror (errno));
+ seed = time (0);
+ }
+ if (fd >= 0)
+ close(fd);
+
+ srandom (seed);
+}
+#endif
+
/* strlcpy is nice, shame glibc does not define it */
#ifdef __GLIBC__
# if ! defined(__UCLIBC__) && ! defined (__dietlibc__)
}
#elif __APPLE__
/* Darwin doesn't appear to have an uptime, so try and make one ourselves */
-#include <sys/time.h>
long uptime (void)
{
struct timeval tv;
return tv.tv_sec - start;
}
#else
-#include <time.h>
long uptime (void)
{
struct timespec tp;
# endif
#endif
+#ifdef __linux__
+void srandomdev (void);
+#endif
+
long uptime (void);
void *xmalloc (size_t size);
char *xstrdup (const char *str);
/* Define this to enable some compatability with 1.x and 2.x info files */
// #define ENABLE_INFO_COMPAT
+/* IPV4LL, aka ZeroConf, aka APIPA, aka RFC 3927.
+ * Needs ARP. */
+#define ENABLE_IPV4LL
+
/* We will auto create a DUID_LLT file if it doesn't exist.
* You can always create your own DUID file that just contains the
* hex string that represents the DUID.
.\" $Id$
.\"
-.TH DHCPCD 8 "12 Jul 2007" "dhcpcd 3.1"
+.TH DHCPCD 8 "18 Jul 2007" "dhcpcd 3.1"
.SH NAME
dhcpcd \- DHCP client daemon
.in +.5i
.ti -.5i
dhcpcd
-\%[\-adknpEGHMNRTY]
+\%[\-dknpAEGHMLNRTY]
\%[\-c\ script]
\%[\-h\ hostname]
\%[\-i\ vendorClassID]
.SH DESCRIPTION
.B dhcpcd
is an implementation of the DHCP client specified in
-.B RFC2131.
+.B RFC 2131.
It gets the host information (IP address, netmask, broadcast address,
etc.) from a DHCP server and configures the network interface of the
machine on which it is running. It also tries to renew the lease time
according to
-.B RFC2131.
+.B RFC 2131.
+
+If
+.B dhcpcd
+fails to get a lease then we attempt Dynamic Configuration of IPv4
+Link-Local Addresses unless the
+.B \-L
+option is given.
.SH OPTIONS
.TP
.BI interface
Specifies the network interface name (eth0, eth1, etc.).
.TP
-.BI \-a
-Do an
-.B ARP
-check on the IP address give to us by the DHCP server. We may need to do this
-if a client on the same network segment has the same IP address, however we do
-not do this by default as most DHCP servers test the IP briefly with an ICMP
-Echo request before assigning the IP address.
-.TP
.BI \-c \ script
.B dhcpcd
will try to execute
total length must be less than 255 characters, -1 character for each user
class.
.TP
+.BI \-A
+Don't do an
+.B ARP
+check on the IP address.
+.TP
.BI \-E
Will read
.I /var/lib/dhcpcd/dhcpcd-<interface>.info
uses the MAC address of the network interface. If \fB-I\fR is not given
an option then we use the MAC address of the network interface.
.TP
+.BI \-L
+Prevents dhcpcd from probing for IPV4LL addresses. IPV4LL is otherwise
+known as ZeroConf or APIPA and is \fBRFC 3927\fR.
+.TP
.BI \-M
Prevents
.B dhcpcd
.SH SEE ALSO
.LP
.I Dynamic Host Configuration Protocol,
-RFC2132
+RFC 2131
.LP
.I DHCP Options and BOOTP Vendor Extensions,
-RFC2132
+RFC 2132
+.LP
+.I Dynamic Configuration of IPv4 Link-Local Addresses,
+RFC 3927
.LP
.I DHCP FQDN Option specification,
-RFC4702
+RFC 4702
.SH BUGS
Please report them to http://dhcpcd.berlios.de or http://bugs.gentoo.org.
{"nogateway", no_argument, NULL, 'G'},
{"sethostname", no_argument, NULL, 'H'},
{"clientid", optional_argument, NULL, 'I'},
+ {"noipv4ll", no_argument, NULL, 'L'},
{"nomtu", no_argument, NULL, 'M'},
{"nontp", no_argument, NULL, 'N'},
{"nodns", no_argument, NULL, 'R'},
snprintf (options.classid, CLASS_ID_MAX_LEN, "%s %s", PACKAGE, VERSION);
options.classid_len = strlen (options.classid);
- options.doarp = false;
+ options.doarp = true;
options.dodns = true;
options.domtu = true;
options.donis = true;
options.dogateway = true;
options.daemonise = true;
options.doinform = false;
+ options.doipv4ll = true;
options.timeout = DEFAULT_TIMEOUT;
gethostname (options.hostname, sizeof (options.hostname));
/* Don't set any optional arguments here so we retain POSIX
* compatibility with getopt */
- while ((opt = getopt_long(argc, argv, "ac:dh:i:kl:m:npr:s:t:u:EF:GHI:MNRTY",
+ while ((opt = getopt_long(argc, argv, "c:dh:i:kl:m:npr:s:t:u:AEF:GHI:LMNRTY",
longopts, &option_index)) != -1)
{
switch (opt) {
longopts[option_index].name);
exit (EXIT_FAILURE);
break;
-
- case 'a':
-#ifndef ENABLE_ARP
- logger (LOG_ERR, "arp support not compiled into dhcpcd");
- exit (EXIT_FAILURE);
-#endif
- options.doarp = true;
- break;
case 'c':
options.script = optarg;
break;
options.userclass_len += (strlen (optarg)) + 1;
}
break;
+ case 'A':
+#ifndef ENABLE_ARP
+ logger (LOG_ERR, "arp support not compiled into dhcpcd");
+ exit (EXIT_FAILURE);
+#endif
+ options.doarp = false;
+ break;
case 'E':
#ifndef ENABLE_INFO
logger (LOG_ERR, "info support not compiled into dhcpcd");
options.clientid_len = -1;
}
break;
+ case 'L':
+ options.doipv4ll = false;
+ break;
case 'M':
options.domtu = false;
break;
logger (LOG_INFO, PACKAGE " " VERSION " starting");
}
+ /* Seed random */
+ srandomdev ();
+
if (dhcp_run (&options, &pidfd)) {
if (pidfd > -1)
close (pidfd);
bool dolastlease;
bool doinform;
bool dorequest;
+ bool doipv4ll;
struct in_addr request_address;
struct in_addr request_netmask;
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright 2006-2007 Roy Marples <uberlord@gentoo.org>
+ *
+ * dhcpcd is an RFC2131 compliant DHCP client daemon.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "config.h"
+#include "arp.h"
+#include "ipv4ll.h"
+
+#ifdef ENABLE_IPV4LL
+
+#ifndef ENABLE_ARP
+#error IPV4LL requires ARP
+#endif
+
+#define IPV4LL_LEASETIME 10
+
+int ipv4ll_get_address (interface_t *iface, dhcp_t *dhcp) {
+ struct in_addr addr;
+
+ while (1) {
+ addr.s_addr = htonl (LINKLOCAL_ADDR |
+ ((abs (random ()) % 0xFD00) + 0x0100));
+ errno = 0;
+ if (! arp_claim (iface, addr))
+ break;
+ /* Our ARP may have been interrupted */
+ if (errno == EINTR)
+ return (-1);
+ }
+
+ dhcp->address.s_addr = addr.s_addr;
+ dhcp->netmask.s_addr = htonl (LINKLOCAL_MASK);
+ dhcp->broadcast.s_addr = htonl (LINKLOCAL_BRDC);
+
+ /* Finally configure some DHCP like lease times */
+ dhcp->leasetime = IPV4LL_LEASETIME;
+ dhcp->renewaltime = (dhcp->leasetime * 0.5);
+ dhcp->rebindtime = (dhcp->leasetime * 0.875);
+
+ return (0);
+}
+
+#endif
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon -
+ * Copyright 2005 - 2007 Roy Marples <uberlord@gentoo.org>
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef IPV4LL_H
+#define IPV4LL_H
+
+#ifdef ENABLE_IPV4LL
+
+#include "dhcp.h"
+#include "interface.h"
+
+#define LINKLOCAL_ADDR 0xa9fe0000
+#define LINKLOCAL_MASK 0xffff0000
+#define LINKLOCAL_BRDC 0xa9feffff
+
+#define IN_LINKLOCAL(addr) ((ntohl (addr) & IN_CLASSB_NET) == LINKLOCAL_ADDR)
+
+int ipv4ll_get_address (interface_t *iface, dhcp_t *dhcp);
+
+#endif
+#endif