aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk13
-rw-r--r--MODULE_LICENSE_BSD_LIKE0
-rw-r--r--Makefile134
-rw-r--r--Makefile.inc9
-rw-r--r--README76
-rw-r--r--ThirdPartyProject.prop4
-rw-r--r--arp.c308
-rw-r--r--arp.h49
-rw-r--r--bind.c234
-rw-r--r--bind.h39
-rw-r--r--bpf-filter.h2
-rw-r--r--bpf.c14
-rw-r--r--common.c173
-rw-r--r--common.h63
-rw-r--r--compat/arc4random.c158
-rw-r--r--compat/arc4random.h36
-rw-r--r--compat/closefrom.c42
-rw-r--r--compat/closefrom.h31
-rw-r--r--compat/getline.c75
-rw-r--r--compat/getline.h36
-rw-r--r--compat/linkaddr.c120
-rw-r--r--compat/strlcpy.c51
-rw-r--r--compat/strlcpy.h34
-rw-r--r--config.h85
-rw-r--r--config.mk20
-rw-r--r--configure.c796
-rw-r--r--configure.h15
-rw-r--r--control.c208
-rw-r--r--control.h45
-rw-r--r--defs.h52
-rw-r--r--dhcp.c379
-rw-r--r--dhcp.h65
-rw-r--r--dhcpcd-hooks/02-dump5
-rw-r--r--dhcpcd-hooks/20-resolv.conf101
-rw-r--r--dhcpcd-hooks/29-lookup-hostname18
-rw-r--r--dhcpcd-hooks/30-hostname26
-rw-r--r--dhcpcd-hooks/50-dhcpcd-compat44
-rw-r--r--dhcpcd-hooks/50-ntp.conf87
-rw-r--r--dhcpcd-hooks/50-yp.conf44
-rw-r--r--dhcpcd-hooks/50-ypbind74
-rw-r--r--dhcpcd-hooks/Makefile26
-rw-r--r--dhcpcd-run-hooks.860
-rw-r--r--dhcpcd-run-hooks.8.in47
-rw-r--r--dhcpcd-run-hooks.in167
-rw-r--r--dhcpcd.8343
-rw-r--r--dhcpcd.8.in305
-rw-r--r--dhcpcd.c2655
-rw-r--r--dhcpcd.conf24
-rw-r--r--dhcpcd.conf.5181
-rw-r--r--dhcpcd.conf.5.in172
-rw-r--r--dhcpcd.h164
-rw-r--r--duid.c100
-rw-r--r--duid.h35
-rw-r--r--eloop.c366
-rw-r--r--eloop.h51
-rw-r--r--if-bsd.c323
-rw-r--r--if-linux-wireless.c89
-rw-r--r--if-linux.c347
-rw-r--r--if-options.c912
-rw-r--r--if-options.h123
-rw-r--r--if-pref.c108
-rw-r--r--if-pref.h34
-rw-r--r--ifaddrs.c146
-rw-r--r--ifaddrs.h34
-rw-r--r--ipv4ll.c156
-rw-r--r--ipv4ll.h33
-rw-r--r--logger.c2
-rw-r--r--lpf.c8
-rw-r--r--net.c654
-rw-r--r--net.h114
-rw-r--r--platform-bsd.c50
-rw-r--r--platform-linux.c104
-rw-r--r--platform.h33
-rw-r--r--showlease.c8
-rw-r--r--signals.c19
-rw-r--r--signals.h3
76 files changed, 9229 insertions, 2532 deletions
diff --git a/Android.mk b/Android.mk
index d9eeae5..3ced2e9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -7,17 +7,20 @@ hooks_dir := dhcpcd-hooks
hooks_target := $(etc_dir)/$(hooks_dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := common.c dhcp.c dhcpcd.c logger.c net.c \
- signals.c configure.c client.c if-linux.c lpf.c
-LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
-LOCAL_SHARED_LIBRARIES := libc libcutils
+LOCAL_SRC_FILES := arp.c bind.c common.c control.c dhcp.c dhcpcd.c duid.c \
+ eloop.c if-options.c if-pref.c ipv4ll.c net.c signals.c configure.c \
+ if-linux.c if-linux-wireless.c lpf.c compat/getline.c \
+ platform-linux.c compat/closefrom.c ifaddrs.c
+
+#LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_SHARED_LIBRARIES := libc libcutils libnetutils
LOCAL_MODULE = dhcpcd
LOCAL_MODULE_TAGS := user
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := showlease.c
-LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+#LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
LOCAL_SHARED_LIBRARIES := libc
LOCAL_MODULE = showlease
LOCAL_MODULE_TAGS := debug
diff --git a/MODULE_LICENSE_BSD_LIKE b/MODULE_LICENSE_BSD_LIKE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_BSD_LIKE
diff --git a/Makefile b/Makefile
index a6b066b..ea6431f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,51 +1,127 @@
-# Makefile based on BSD make.
-# Our mk stubs also work with GNU make.
-# Copyright 2008 Roy Marples <roy@marples.name>
+# dhcpcd Makefile
PROG= dhcpcd
-SRCS= common.c dhcp.c dhcpcd.c logger.c net.c signals.c
-SRCS+= configure.c client.c
-SRCS+= ${SRC_IF} ${SRC_PF}
+SRCS= arp.c bind.c common.c control.c dhcp.c dhcpcd.c duid.c eloop.c
+SRCS+= if-options.c if-pref.c ipv4ll.c net.c signals.c
+SRCS+= configure.c
+
+CFLAGS?= -O2
+CSTD?= c99
+CFLAGS+= -std=${CSTD}
+include config.mk
+
+OBJS+= ${SRCS:.c=.o} ${COMPAT_SRCS:.c=.o}
-LIBEXECDIR?= ${PREFIX}/system/etc/dhcpcd
SCRIPT= ${LIBEXECDIR}/dhcpcd-run-hooks
HOOKDIR= ${LIBEXECDIR}/dhcpcd-hooks
-BINDIR= ${PREFIX}/sbin
-DBDIR= /data/misc/dhcp
-SYSCONFDIR?= ${PREFIX}/system/etc/dhcpcd
-
-MAN= dhcpcd.conf.5 dhcpcd.8 dhcpcd-run-hooks.8
+MAN5= dhcpcd.conf.5
+MAN8= dhcpcd.8 dhcpcd-run-hooks.8
CLEANFILES= dhcpcd.conf.5 dhcpcd.8 dhcpcd-run-hooks.8
SCRIPTS= dhcpcd-run-hooks
SCRIPTSDIR= ${LIBEXECDIR}
CLEANFILES+= dhcpcd-run-hooks
+CLEANFILES+= .depend
FILES= dhcpcd.conf
FILESDIR= ${SYSCONFDIR}
-CPPFLAGS+= -DDBDIR=\"${DBDIR}\"
-CPPFLAGS+= -DSCRIPT=\"${SCRIPT}\"
-CPPFLAGS+= -DSYSCONFDIR=\"${SYSCONFDIR}\"
-LDADD+= ${LIBRT}
-
SUBDIRS= dhcpcd-hooks
-.SUFFIXES: .in .sh.in
+SED_DBDIR= -e 's:@DBDIR@:${DBDIR}:g'
+SED_HOOKDIR= -e 's:@HOOKDIR@:${HOOKDIR}:g'
+SED_SERVICEEXISTS= -e 's:@SERVICEEXISTS@:${SERVICEEXISTS}:g'
+SED_SERVICECMD= -e 's:@SERVICECMD@:${SERVICECMD}:g'
+SED_SERVICESTATUS= -e 's:@SERVICESTATUS@:${SERVICESTATUS}:g'
+SED_SCRIPT= -e 's:@SCRIPT@:${SCRIPT}:g'
+SED_SYS= -e 's:@SYSCONFDIR@:${SYSCONFDIR}:g'
+
+_DEPEND_SH= test -e .depend && echo ".depend" || echo ""
+_DEPEND!= ${_DEPEND_SH}
+DEPEND= ${_DEPEND}$(shell ${_DEPEND_SH})
+
+_VERSION_SH= sed -n 's/\#define VERSION[[:space:]]*"\(.*\)".*/\1/p' defs.h
+_VERSION!= ${_VERSION_SH}
+VERSION= ${_VERSION}$(shell ${_VERSION_SH})
-SED_DBDIR= -e 's:@DBDIR@:${DBDIR}:g'
-SED_HOOKDIR= -e 's:@HOOKDIR@:${HOOKDIR}:g'
-SED_SCRIPT= -e 's:@SCRIPT@:${SCRIPT}:g'
-SED_SYS= -e 's:@SYSCONFDIR@:${SYSCONFDIR}:g'
+GITREF?= HEAD
+DISTPREFIX?= ${PROG}-${VERSION}
+DISTFILE?= ${DISTPREFIX}.tar.bz2
+
+CLEANFILES+= *.tar.bz2
+
+.PHONY: import import-bsd
+
+.SUFFIXES: .in
.in:
- ${SED} ${SED_DBDIR} ${SED_HOOKDIR} ${SED_SCRIPT} ${SED_SYS} $< > $@
+ ${SED} ${SED_DBDIR} ${SED_HOOKDIR} ${SED_SCRIPT} ${SED_SYS} \
+ ${SED_SERVICEEXISTS} ${SED_SERVICECMD} ${SED_SERVICESTATUS} \
+ $< > $@
+
+all: config.h ${PROG} ${SCRIPTS} ${MAN5} ${MAN8}
+
+.c.o:
+ ${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
+
+.depend: ${SRCS} ${COMPAT_SRCS}
+ ${CC} ${CPPFLAGS} -MM ${SRCS} ${COMPAT_SRCS} > .depend
+
+depend: .depend
+
+${PROG}: ${DEPEND} ${OBJS}
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}
+
+_proginstall: ${PROG}
+ ${INSTALL} -d ${DESTDIR}${SBINDIR}
+ ${INSTALL} -m ${BINMODE} ${PROG} ${DESTDIR}${SBINDIR}
+ ${INSTALL} -d ${DESTDIR}${DBDIR}
+
+_scriptsinstall: ${SCRIPTS}
+ ${INSTALL} -d ${DESTDIR}${SCRIPTSDIR}
+ ${INSTALL} -m ${BINMODE} ${SCRIPTS} ${DESTDIR}${SCRIPTSDIR}
+
+_maninstall: ${MAN5} ${MAN8}
+ ${INSTALL} -d ${DESTDIR}${MANDIR}/man5
+ ${INSTALL} -m ${MANMODE} ${MAN5} ${DESTDIR}${MANDIR}/man5
+ ${INSTALL} -d ${DESTDIR}${MANDIR}/man8
+ ${INSTALL} -m ${MANMODE} ${MAN8} ${DESTDIR}${MANDIR}/man8
+
+_confinstall:
+ ${INSTALL} -d ${DESTDIR}${SYSCONFDIR}
+ test -e ${DESTDIR}${SYSCONFDIR}/dhcpcd.conf || \
+ ${INSTALL} -m ${CONFMODE} dhcpcd.conf ${DESTDIR}${SYSCONFDIR}
+
+install: _proginstall _scriptsinstall _maninstall _confinstall
+ for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@; cd ..; done
+
+clean:
+ rm -f ${OBJS} ${PROG} ${PROG}.core ${CLEANFILES}
+
+distclean: clean
+ rm -f .depend config.h config.mk
+
+dist:
+ git archive --prefix=${DISTPREFIX}/ ${GITREF} | bzip2 > ${DISTFILE}
-.sh.in.sh:
- ${SED} ${SED_HOOKDIR} ${SED_SCRIPT} ${SED_SYS} $< > $@
+import:
+ rm -rf /tmp/${DISTPREFIX}
+ ${INSTALL} -d /tmp/${DISTPREFIX}
+ cp ${SRCS} dhcpcd.conf *.in /tmp/${DISTPREFIX}
+ cp $$(${CC} ${CPPFLAGS} -MM ${SRCS} | \
+ sed -e 's/^.*\.c //g' -e 's/.*\.c$$//g' -e 's/\\//g' | \
+ tr ' ' '\n' | \
+ sed -e '/^compat\//d' | \
+ sort -u) /tmp/${DISTPREFIX}
+ if test -n "${COMPAT_SRCS}"; then \
+ ${INSTALL} -d /tmp/${DISTPREFIX}/compat; \
+ cp ${COMPAT_SRCS} /tmp/${DISTPREFIX}/compat; \
+ cp $$(${CC} ${CPPFLAGS} -MM ${COMPAT_SRCS} | \
+ sed -e 's/^.*c //g' -e 's/.*\.c$$//g' -e 's/\\//g' | \
+ tr ' ' '\n' | \
+ sort -u) /tmp/${DISTPREFIX}/compat; \
+ fi;
+ cd dhcpcd-hooks; ${MAKE} DISTPREFIX=${DISTPREFIX} $@
-MK= mk
-include ${MK}/sys.mk
-include ${MK}/os.mk
-include ${MK}/prog.mk
+include Makefile.inc
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644
index 0000000..fd7ead4
--- /dev/null
+++ b/Makefile.inc
@@ -0,0 +1,9 @@
+# System definitions
+
+BINMODE?= 0555
+NONBINMODE?= 0444
+MANMODE?= ${NONBINMODE}
+CONFMODE?= 0644
+
+INSTALL?= install
+SED?= sed
diff --git a/README b/README
index 1dff417..cfbf3cd 100644
--- a/README
+++ b/README
@@ -1,29 +1,29 @@
-dhcpcd-4 - DHCP client daemon
-Copyright 2006-2008 Roy Marples <roy@marples.name>
+dhcpcd - DHCP client daemon
+Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
Installation
------------
-Edit config.h to match your building requirements.
-Then just make; make install
+./configure; make; make install
man dhcpcd for command line options
+man dhcpcd.conf for configuration options
+man dhcpcd-run-hooks to learn how to hook scripts into dhcpcd events
Notes
-----
-If you're cross compiling you may need to set the below knobs to avoid
-automatic tests.
-OS=BSD | Linux
+If you're cross compiling you may need set the platform if OS is different
+from the host.
+--target=sparc-sun-netbsd5.0
If you're building for an MMU-less system where fork() does not work, you
-should add -DTHERE_IS_NO_FORK to your CPPFLAGS.
+should ./configure --disable-fork.
This also puts the --no-background flag on and stops the --background flag
from working.
-You can change the default dir with these knobs.
+You can change the default dirs with these knobs.
For example, to satisfy FHS compliance you would do this:-
-LIBEXECDIR=/lib/dhcpcd
-DBDIR=/var/lib/dhcpcd
+./configure --libexecdir=/lib/dhcpcd dbdir=/var/lib/dhcpcd
We now default to using -std=c99. For 64-bit linux, this always works, but
for 32-bit linux it requires either gnu99 or a patch to asm/types.h.
@@ -32,39 +32,45 @@ linux-2.6.24 finally ships with a working 32-bit header.
If your linux headers are older, or your distro hasn't patched them you can
set CSTD=gnu99 to work around this.
+Some BSD systems do not allow the manipulation of automatically added subnet
+routes. You can find discussion here:
+ http://mail-index.netbsd.org/tech-net/2008/12/03/msg000896.html
+BSD systems where this has been fixed are:
+ NetBSD-5.0
+
+We try and detect how dhcpcd should interact with system services during the
+configure stage. If we cannot auto-detect how do to this, or it is wrong then
+you can change this by passing shell commands to --service-exists,
+--servicecmd and optionally --servicestatus.
+
+To prepare dhcpcd for import into a platform source tree (like NetBSD)
+you can use the make import target to create /tmp/dhcpcd-$version and
+populate it with all the source files and hooks needed.
+In this instance, you may wish to disable some configured tests when
+the binary has to run on older versions which lack support, such as getline.
+./configure --without-getline
+
Hooks
-----
Not all the hooks in dhcpcd-hooks are installed by default.
-By default we install 01-test, 10-mtu, 20-resolv.conf and 30-hostname.
-To add more simply add them in the HOOKSCRIPTS variable.
-make HOOKSCRIPTS=50-ntp install
+By default we install 01-test, 10-mtu, 20-resolv.conf,
+29-lookup-hostname and 30-hostname.
+The default dhcpcd.conf disables the lookup-hostname hook by default.
+The configure program attempts to find hooks for systems you have installed.
+To add more simply
+./configure -with-hook=ntp.conf
Compatibility
-------------
-If you require compatibility with dhcpcd-3 and older style variables,
-you can install 50-dhcpcd-compat into the directory $LIBEXECDIR/dhcpcd-hooks
-We don't install this by default.
-You should also add -DCMDLINE_COMPAT to your CPPFLAGS if you need to be fully
-commandline compatible with prior versions.
-
-dhcpcd-3 enabled DUID support by default - this has changed in dhcpcd-4.
-You can enable it via the --duid, -D command line option or by using the
-duid directive in dhcpcd.conf.
-If CMDLINE_COMPAT is defined the we renable DUID support by default IF
-the dhcpcd.duid file exits. This keeps the clients working as they were,
-which is good.
-
-dhcpcd no longer sends a default ClientID for ethernet interfaces.
-This is so we can re-use the address the kernel DHCP client found.
-To retain the old behaviour of sending a default ClientID based on the
-hardware address for interface, simply add the keyword clientid to dhcpcd.conf.
-If CMDLINE_COMPAT is defined, we renable the sending of ClientID by default
-AND adding clientid to dhcpcd.conf causes it NOT to be sent.
+dhcpcd-5.0 is only fully command line compatible with dhcpcd-4.0
+For compatibility with older versions, use dhcpcd-4.0
-dhcpcd-4 is NOT fully commandline compatible with dhcpcd-2 and older and
-changes the meaning of some options.
+dhcpcd no longer sends a default ClientID for ethernet interfaces.
+This is so we can re-use the address the kernel DHCP client found.
+To retain the old behaviour of sending a default ClientID based on the
+hardware address for interface, simply add the keyword clientid to dhcpcd.conf.
ChangeLog
diff --git a/ThirdPartyProject.prop b/ThirdPartyProject.prop
index 1874fb9..e8bf489 100644
--- a/ThirdPartyProject.prop
+++ b/ThirdPartyProject.prop
@@ -1,7 +1,7 @@
# Copyright 2010 Google Inc. All Rights Reserved.
#Fri Jul 16 10:03:08 PDT 2010
-currentVersion=5.1.5
-version=4.0.1
+currentVersion=5.2.10
+version=5.2.10
isNative=true
feedurl=http\://git.marples.name/projects/dhcpcd/wiki
name=dhcpcd
diff --git a/arp.c b/arp.c
new file mode 100644
index 0000000..89d63fe
--- /dev/null
+++ b/arp.c
@@ -0,0 +1,308 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "arp.h"
+#include "bind.h"
+#include "common.h"
+#include "dhcpcd.h"
+#include "eloop.h"
+#include "if-options.h"
+#include "ipv4ll.h"
+#include "net.h"
+
+#define ARP_LEN \
+ (sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + (2 * HWADDR_LEN))
+
+static int
+send_arp(const struct interface *iface, int op, in_addr_t sip, in_addr_t tip)
+{
+ uint8_t arp_buffer[ARP_LEN];
+ struct arphdr ar;
+ size_t len;
+ uint8_t *p;
+ int retval;
+
+ ar.ar_hrd = htons(iface->family);
+ ar.ar_pro = htons(ETHERTYPE_IP);
+ ar.ar_hln = iface->hwlen;
+ ar.ar_pln = sizeof(sip);
+ ar.ar_op = htons(op);
+ memcpy(arp_buffer, &ar, sizeof(ar));
+ p = arp_buffer + sizeof(ar);
+ memcpy(p, iface->hwaddr, iface->hwlen);
+ p += iface->hwlen;
+ memcpy(p, &sip, sizeof(sip));
+ p += sizeof(sip);
+ /* ARP requests should ignore this */
+ retval = iface->hwlen;
+ while (retval--)
+ *p++ = '\0';
+ memcpy(p, &tip, sizeof(tip));
+ p += sizeof(tip);
+ len = p - arp_buffer;
+ retval = send_raw_packet(iface, ETHERTYPE_ARP, arp_buffer, len);
+ return retval;
+}
+
+static void
+handle_arp_failure(struct interface *iface)
+{
+
+ /* If we failed without a magic cookie then we need to try
+ * and defend our IPv4LL address. */
+ if ((iface->state->offer != NULL &&
+ iface->state->offer->cookie != htonl(MAGIC_COOKIE)) ||
+ (iface->state->new != NULL &&
+ iface->state->new->cookie != htonl(MAGIC_COOKIE)))
+ {
+ handle_ipv4ll_failure(iface);
+ return;
+ }
+
+ unlink(iface->leasefile);
+ if (!iface->state->lease.frominfo)
+ send_decline(iface);
+ close_sockets(iface);
+ delete_timeout(NULL, iface);
+ if (iface->state->lease.frominfo)
+ start_interface(iface);
+ else
+ add_timeout_sec(DHCP_ARP_FAIL, start_interface, iface);
+}
+
+static void
+handle_arp_packet(void *arg)
+{
+ struct interface *iface = arg;
+ uint8_t arp_buffer[ARP_LEN];
+ struct arphdr ar;
+ uint32_t reply_s;
+ uint32_t reply_t;
+ uint8_t *hw_s, *hw_t;
+ ssize_t bytes;
+ struct if_state *state = iface->state;
+ struct if_options *opts = state->options;
+ const char *hwaddr;
+ struct in_addr ina;
+
+ state->fail.s_addr = 0;
+ for(;;) {
+ bytes = get_raw_packet(iface, ETHERTYPE_ARP,
+ arp_buffer, sizeof(arp_buffer));
+ if (bytes == 0 || bytes == -1)
+ return;
+ /* We must have a full ARP header */
+ if ((size_t)bytes < sizeof(ar))
+ continue;
+ memcpy(&ar, arp_buffer, sizeof(ar));
+ /* Protocol must be IP. */
+ if (ar.ar_pro != htons(ETHERTYPE_IP))
+ continue;
+ if (ar.ar_pln != sizeof(reply_s))
+ continue;
+ /* Only these types are recognised */
+ if (ar.ar_op != htons(ARPOP_REPLY) &&
+ ar.ar_op != htons(ARPOP_REQUEST))
+ continue;
+
+ /* Get pointers to the hardware addreses */
+ hw_s = arp_buffer + sizeof(ar);
+ hw_t = hw_s + ar.ar_hln + ar.ar_pln;
+ /* Ensure we got all the data */
+ if ((hw_t + ar.ar_hln + ar.ar_pln) - arp_buffer > bytes)
+ continue;
+ /* Ignore messages from ourself */
+ if (ar.ar_hln == iface->hwlen &&
+ memcmp(hw_s, iface->hwaddr, iface->hwlen) == 0)
+ continue;
+ /* Copy out the IP addresses */
+ memcpy(&reply_s, hw_s + ar.ar_hln, ar.ar_pln);
+ memcpy(&reply_t, hw_t + ar.ar_hln, ar.ar_pln);
+
+ /* Check for arping */
+ if (state->arping_index &&
+ state->arping_index <= opts->arping_len &&
+ (reply_s == opts->arping[state->arping_index - 1] ||
+ (reply_s == 0 &&
+ reply_t == opts->arping[state->arping_index - 1])))
+ {
+ ina.s_addr = reply_s;
+ hwaddr = hwaddr_ntoa((unsigned char *)hw_s,
+ (size_t)ar.ar_hln);
+ syslog(LOG_INFO,
+ "%s: found %s on hardware address %s",
+ iface->name, inet_ntoa(ina), hwaddr);
+ if (select_profile(iface, hwaddr) == -1 &&
+ errno == ENOENT)
+ select_profile(iface, inet_ntoa(ina));
+ close_sockets(iface);
+ delete_timeout(NULL, iface);
+ start_interface(iface);
+ return;
+ }
+
+ /* Check for conflict */
+ if (state->offer &&
+ (reply_s == state->offer->yiaddr ||
+ (reply_s == 0 && reply_t == state->offer->yiaddr)))
+ state->fail.s_addr = state->offer->yiaddr;
+
+ /* Handle IPv4LL conflicts */
+ if (IN_LINKLOCAL(htonl(iface->addr.s_addr)) &&
+ (reply_s == iface->addr.s_addr ||
+ (reply_s == 0 && reply_t == iface->addr.s_addr)))
+ state->fail.s_addr = iface->addr.s_addr;
+
+ if (state->fail.s_addr) {
+ syslog(LOG_ERR, "%s: hardware address %s claims %s",
+ iface->name,
+ hwaddr_ntoa((unsigned char *)hw_s,
+ (size_t)ar.ar_hln),
+ inet_ntoa(state->fail));
+ errno = EEXIST;
+ handle_arp_failure(iface);
+ return;
+ }
+ }
+}
+
+void
+send_arp_announce(void *arg)
+{
+ struct interface *iface = arg;
+ struct if_state *state = iface->state;
+ struct timeval tv;
+
+ if (iface->arp_fd == -1) {
+ open_socket(iface, ETHERTYPE_ARP);
+ add_event(iface->arp_fd, handle_arp_packet, iface);
+ }
+ if (++state->claims < ANNOUNCE_NUM)
+ syslog(LOG_DEBUG,
+ "%s: sending ARP announce (%d of %d), "
+ "next in %d.00 seconds",
+ iface->name, state->claims, ANNOUNCE_NUM, ANNOUNCE_WAIT);
+ else
+ syslog(LOG_DEBUG,
+ "%s: sending ARP announce (%d of %d)",
+ iface->name, state->claims, ANNOUNCE_NUM);
+ if (send_arp(iface, ARPOP_REQUEST,
+ state->new->yiaddr, state->new->yiaddr) == -1)
+ syslog(LOG_ERR, "send_arp: %m");
+ if (state->claims < ANNOUNCE_NUM) {
+ add_timeout_sec(ANNOUNCE_WAIT, send_arp_announce, iface);
+ return;
+ }
+ if (state->new->cookie != htonl(MAGIC_COOKIE)) {
+ /* We should pretend to be at the end
+ * of the DHCP negotation cycle unless we rebooted */
+ if (state->interval != 0)
+ state->interval = 64;
+ state->probes = 0;
+ state->claims = 0;
+ tv.tv_sec = state->interval - DHCP_RAND_MIN;
+ tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+ timernorm(&tv);
+ add_timeout_tv(&tv, start_discover, iface);
+ } else {
+ delete_event(iface->arp_fd);
+ close(iface->arp_fd);
+ iface->arp_fd = -1;
+ }
+}
+
+void
+send_arp_probe(void *arg)
+{
+ struct interface *iface = arg;
+ struct if_state *state = iface->state;
+ struct in_addr addr;
+ struct timeval tv;
+ int arping = 0;
+
+ if (state->arping_index < state->options->arping_len) {
+ addr.s_addr = state->options->arping[state->arping_index];
+ arping = 1;
+ } else if (state->offer) {
+ if (state->offer->yiaddr)
+ addr.s_addr = state->offer->yiaddr;
+ else
+ addr.s_addr = state->offer->ciaddr;
+ } else
+ addr.s_addr = iface->addr.s_addr;
+
+ if (iface->arp_fd == -1) {
+ open_socket(iface, ETHERTYPE_ARP);
+ add_event(iface->arp_fd, handle_arp_packet, iface);
+ }
+ if (state->probes == 0) {
+ if (arping)
+ syslog(LOG_INFO, "%s: searching for %s",
+ iface->name, inet_ntoa(addr));
+ else
+ syslog(LOG_INFO, "%s: checking for %s",
+ iface->name, inet_ntoa(addr));
+ }
+ if (++state->probes < PROBE_NUM) {
+ tv.tv_sec = PROBE_MIN;
+ tv.tv_usec = arc4random() % (PROBE_MAX_U - PROBE_MIN_U);
+ timernorm(&tv);
+ add_timeout_tv(&tv, send_arp_probe, iface);
+ } else {
+ tv.tv_sec = ANNOUNCE_WAIT;
+ tv.tv_usec = 0;
+ if (arping) {
+ state->probes = 0;
+ if (++state->arping_index < state->options->arping_len)
+ add_timeout_tv(&tv, send_arp_probe, iface);
+ else
+ add_timeout_tv(&tv, start_interface, iface);
+ } else
+ add_timeout_tv(&tv, bind_interface, iface);
+ }
+ syslog(LOG_DEBUG,
+ "%s: sending ARP probe (%d of %d), next in %0.2f seconds",
+ iface->name, state->probes ? state->probes : PROBE_NUM, PROBE_NUM,
+ timeval_to_double(&tv));
+ if (send_arp(iface, ARPOP_REQUEST, 0, addr.s_addr) == -1)
+ syslog(LOG_ERR, "send_arp: %m");
+}
+
+void
+start_arping(struct interface *iface)
+{
+ iface->state->probes = 0;
+ iface->state->arping_index = 0;
+ send_arp_probe(iface);
+}
diff --git a/arp.h b/arp.h
new file mode 100644
index 0000000..b97c38b
--- /dev/null
+++ b/arp.h
@@ -0,0 +1,49 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef ARP_H
+#define ARP_H
+
+/* These are for IPV4LL, RFC 3927.
+ * We put them here as we use the timings for all ARP foo. */
+#define PROBE_WAIT 1
+#define PROBE_NUM 3
+#define PROBE_MIN 1
+#define PROBE_MAX 2
+#define ANNOUNCE_WAIT 2
+#define ANNOUNCE_NUM 2
+#define ANNOUNCE_INTERVAL 2
+#define MAX_CONFLICTS 10
+#define RATE_LIMIT_INTERVAL 60
+#define DEFEND_INTERVAL 10
+
+#include "dhcpcd.h"
+
+void send_arp_announce(void *);
+void send_arp_probe(void *);
+void start_arping(struct interface *);
+#endif
diff --git a/bind.c b/bind.c
new file mode 100644
index 0000000..eed64a6
--- /dev/null
+++ b/bind.c
@@ -0,0 +1,234 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <fcntl.h>
+#ifdef BSD
+# include <paths.h>
+#endif
+#include <signal.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "arp.h"
+#include "bind.h"
+#include "common.h"
+#include "configure.h"
+#include "dhcpcd.h"
+#include "eloop.h"
+#include "if-options.h"
+#include "net.h"
+#include "signals.h"
+
+#ifndef _PATH_DEVNULL
+# define _PATH_DEVNULL "/dev/null"
+#endif
+
+/* We do things after aquiring the lease, so ensure we have enough time for them */
+#define DHCP_MIN_LEASE 20
+
+#ifndef THERE_IS_NO_FORK
+pid_t
+daemonise(void)
+{
+ pid_t pid;
+ sigset_t full;
+ sigset_t old;
+ char buf = '\0';
+ int sidpipe[2], fd;
+
+ if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
+ return 0;
+ sigfillset(&full);
+ sigprocmask(SIG_SETMASK, &full, &old);
+ /* Setup a signal pipe so parent knows when to exit. */
+ if (pipe(sidpipe) == -1) {
+ syslog(LOG_ERR, "pipe: %m");
+ return -1;
+ }
+ syslog(LOG_DEBUG, "forking to background");
+ switch (pid = fork()) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ case 0:
+ setsid();
+ /* Notify parent it's safe to exit as we've detached. */
+ close(sidpipe[0]);
+ if (write(sidpipe[1], &buf, 1) == -1)
+ syslog(LOG_ERR, "failed to notify parent: %m");
+ close(sidpipe[1]);
+ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO)
+ close(fd);
+ }
+ break;
+ default:
+ signal_reset();
+ /* Wait for child to detach */
+ close(sidpipe[1]);
+ if (read(sidpipe[0], &buf, 1) == -1)
+ syslog(LOG_ERR, "failed to read child: %m");
+ close(sidpipe[0]);
+ break;
+ }
+ /* Done with the fd now */
+ if (pid != 0) {
+ syslog(LOG_INFO, "forked to background, child pid %d",pid);
+ writepid(pidfd, pid);
+ close(pidfd);
+ pidfd = -1;
+ exit(EXIT_SUCCESS);
+ }
+ options |= DHCPCD_DAEMONISED;
+ sigprocmask(SIG_SETMASK, &old, NULL);
+ return pid;
+}
+#endif
+
+void
+bind_interface(void *arg)
+{
+ struct interface *iface = arg;
+ struct if_state *state = iface->state;
+ struct if_options *ifo = state->options;
+ struct dhcp_lease *lease = &state->lease;
+ struct timeval tv;
+
+ /* We're binding an address now - ensure that sockets are closed */
+ close_sockets(iface);
+ state->reason = NULL;
+ delete_timeout(handle_exit_timeout, NULL);
+ if (clock_monotonic)
+ get_monotonic(&lease->boundtime);
+ state->xid = 0;
+ free(state->old);
+ state->old = state->new;
+ state->new = state->offer;
+ state->offer = NULL;
+ get_lease(lease, state->new);
+ if (ifo->options & DHCPCD_STATIC) {
+ syslog(LOG_INFO, "%s: using static address %s",
+ iface->name, inet_ntoa(lease->addr));
+ lease->leasetime = ~0U;
+ lease->net.s_addr = ifo->req_mask.s_addr;
+ state->reason = "STATIC";
+ } else if (state->new->cookie != htonl(MAGIC_COOKIE)) {
+ syslog(LOG_INFO, "%s: using IPv4LL address %s",
+ iface->name, inet_ntoa(lease->addr));
+ lease->leasetime = ~0U;
+ state->reason = "IPV4LL";
+ } else if (ifo->options & DHCPCD_INFORM) {
+ if (ifo->req_addr.s_addr != 0)
+ lease->addr.s_addr = ifo->req_addr.s_addr;
+ else
+ lease->addr.s_addr = iface->addr.s_addr;
+ syslog(LOG_INFO, "%s: received approval for %s", iface->name,
+ inet_ntoa(lease->addr));
+ lease->leasetime = ~0U;
+ state->reason = "INFORM";
+ } else {
+ if (gettimeofday(&tv, NULL) == 0)
+ lease->leasedfrom = tv.tv_sec;
+ else if (lease->frominfo)
+ state->reason = "TIMEOUT";
+ if (lease->leasetime == ~0U) {
+ lease->renewaltime =
+ lease->rebindtime =
+ lease->leasetime;
+ syslog(LOG_INFO, "%s: leased %s for infinity",
+ iface->name, inet_ntoa(lease->addr));
+ } else {
+ if (lease->leasetime < DHCP_MIN_LEASE) {
+ syslog(LOG_WARNING,
+ "%s: minimum lease is %d seconds",
+ iface->name, DHCP_MIN_LEASE);
+ lease->leasetime = DHCP_MIN_LEASE;
+ }
+ if (lease->rebindtime == 0)
+ lease->rebindtime = lease->leasetime * T2;
+ else if (lease->rebindtime >= lease->leasetime) {
+ lease->rebindtime = lease->leasetime * T2;
+ syslog(LOG_ERR,
+ "%s: rebind time greater than lease "
+ "time, forcing to %u seconds",
+ iface->name, lease->rebindtime);
+ }
+ if (lease->renewaltime == 0)
+ lease->renewaltime = lease->leasetime * T1;
+ else if (lease->renewaltime > lease->rebindtime) {
+ lease->renewaltime = lease->leasetime * T1;
+ syslog(LOG_ERR,
+ "%s: renewal time greater than rebind "
+ "time, forcing to %u seconds",
+ iface->name, lease->renewaltime);
+ }
+ syslog(LOG_INFO,
+ "%s: leased %s for %u seconds", iface->name,
+ inet_ntoa(lease->addr), lease->leasetime);
+ }
+ }
+ if (options & DHCPCD_TEST) {
+ state->reason = "TEST";
+ run_script(iface);
+ exit(EXIT_SUCCESS);
+ }
+ if (state->reason == NULL) {
+ if (state->old) {
+ if (state->old->yiaddr == state->new->yiaddr &&
+ lease->server.s_addr)
+ state->reason = "RENEW";
+ else
+ state->reason = "REBIND";
+ } else if (state->state == DHS_REBOOT)
+ state->reason = "REBOOT";
+ else
+ state->reason = "BOUND";
+ }
+ if (lease->leasetime == ~0U)
+ lease->renewaltime = lease->rebindtime = lease->leasetime;
+ else {
+ add_timeout_sec(lease->renewaltime, start_renew, iface);
+ add_timeout_sec(lease->rebindtime, start_rebind, iface);
+ add_timeout_sec(lease->leasetime, start_expire, iface);
+ }
+ ifo->options &= ~ DHCPCD_CSR_WARNED;
+ configure(iface);
+ daemonise();
+ state->state = DHS_BOUND;
+ if (ifo->options & DHCPCD_ARP) {
+ state->claims = 0;
+ send_arp_announce(iface);
+ }
+}
diff --git a/bind.h b/bind.h
new file mode 100644
index 0000000..375a0f3
--- /dev/null
+++ b/bind.h
@@ -0,0 +1,39 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef BIND_H
+#define BIND_H
+
+#include "config.h"
+#ifdef THERE_IS_NO_FORK
+# define daemonise() {}
+#else
+pid_t daemonise(void);
+#endif
+
+void bind_interface(void *);
+#endif
diff --git a/bpf-filter.h b/bpf-filter.h
index 881f678..b68ee49 100644
--- a/bpf-filter.h
+++ b/bpf-filter.h
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/bpf.c b/bpf.c
index 96e53a1..beda1ba 100644
--- a/bpf.c
+++ b/bpf.c
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -39,12 +39,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
#include <unistd.h>
#include "config.h"
#include "common.h"
#include "dhcp.h"
-#include "logger.h"
#include "net.h"
#include "bpf-filter.h"
@@ -81,7 +81,7 @@ open_socket(struct interface *iface, int protocol)
goto eexit;
if (pv.bv_major != BPF_MAJOR_VERSION ||
pv.bv_minor < BPF_MINOR_VERSION) {
- logger(LOG_ERR, "BPF version mismatch - recompile " PACKAGE);
+ syslog(LOG_ERR, "BPF version mismatch - recompile");
goto eexit;
}
@@ -136,7 +136,7 @@ eexit:
ssize_t
send_raw_packet(const struct interface *iface, int protocol,
- const void *data, ssize_t len)
+ const void *data, ssize_t len)
{
struct iovec iov[2];
struct ether_header hw;
@@ -160,7 +160,7 @@ send_raw_packet(const struct interface *iface, int protocol,
* So we pass the buffer in the API so we can loop on >1 packet. */
ssize_t
get_raw_packet(struct interface *iface, int protocol,
- void *data, ssize_t len)
+ void *data, ssize_t len)
{
int fd = -1;
struct bpf_hdr packet;
@@ -184,7 +184,7 @@ get_raw_packet(struct interface *iface, int protocol,
}
bytes = -1;
memcpy(&packet, iface->buffer + iface->buffer_pos,
- sizeof(packet));
+ sizeof(packet));
if (packet.bh_caplen != packet.bh_datalen)
goto next; /* Incomplete packet, drop. */
if (iface->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
@@ -197,7 +197,7 @@ get_raw_packet(struct interface *iface, int protocol,
memcpy(data, payload, bytes);
next:
iface->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
- packet.bh_caplen);
+ packet.bh_caplen);
if (iface->buffer_pos >= iface->buffer_len)
iface->buffer_len = iface->buffer_pos = 0;
if (bytes != -1)
diff --git a/common.c b/common.c
index da22a5c..0642055 100644
--- a/common.c
+++ b/common.c
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -25,6 +25,13 @@
* SUCH DAMAGE.
*/
+/* Needed define to get at getline for glibc and FreeBSD */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <sys/cdefs.h>
+
#ifdef __APPLE__
# include <mach/mach_time.h>
# include <mach/kern_return.h>
@@ -35,6 +42,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#ifdef BSD
# include <paths.h>
#endif
@@ -42,119 +50,60 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include "common.h"
-#include "logger.h"
#ifndef _PATH_DEVNULL
# define _PATH_DEVNULL "/dev/null"
#endif
-int clock_monotonic = 0;
-
-/* Handy routine to read very long lines in text files.
- * This means we read the whole line and avoid any nasty buffer overflows. */
-ssize_t
-get_line(char **line, size_t *len, FILE *fp)
-{
- char *p;
- size_t last = 0;
-
- while(!feof(fp)) {
- if (*line == NULL || last != 0) {
- *len += BUFSIZ;
- *line = xrealloc(*line, *len);
- }
- p = *line + last;
- memset(p, 0, BUFSIZ);
- if (fgets(p, BUFSIZ, fp) == NULL)
- break;
- last += strlen(p);
- if (last && (*line)[last - 1] == '\n') {
- (*line)[last - 1] = '\0';
- break;
- }
- }
- return last;
-}
-
-/* Simple hack to return a random number without arc4random */
-#ifndef HAVE_ARC4RANDOM
-uint32_t arc4random(void)
-{
- int fd;
- static unsigned long seed = 0;
-
- if (!seed) {
- fd = open("/dev/urandom", 0);
- if (fd == -1 || read(fd, &seed, sizeof(seed)) == -1)
- seed = time(0);
- if (fd >= 0)
- close(fd);
- srandom(seed);
- }
-
- return (uint32_t)random();
-}
+int clock_monotonic;
+static char *lbuf;
+static size_t lbuf_len;
+#ifdef DEBUG_MEMORY
+static char lbuf_set;
#endif
-/* strlcpy is nice, shame glibc does not define it */
-#if HAVE_STRLCPY
-#else
-size_t
-strlcpy(char *dst, const char *src, size_t size)
+#ifdef DEBUG_MEMORY
+static void
+free_lbuf(void)
{
- const char *s = src;
- size_t n = size;
-
- if (n && --n)
- do {
- if (!(*dst++ = *src++))
- break;
- } while (--n);
-
- if (!n) {
- if (size)
- *dst = '\0';
- while (*src++);
- }
-
- return src - s - 1;
+ free(lbuf);
+ lbuf = NULL;
}
#endif
-#if HAVE_CLOSEFROM
-#else
-int
-closefrom(int fd)
+/* Handy routine to read very long lines in text files.
+ * This means we read the whole line and avoid any nasty buffer overflows.
+ * We strip leading space and avoid comment lines, making the code that calls
+ * us smaller.
+ * As we don't use threads, this API is clean too. */
+char *
+get_line(FILE * __restrict fp)
{
- int max = getdtablesize();
- int i;
- int r = 0;
+ char *p;
+ ssize_t bytes;
- for (i = fd; i < max; i++)
- r += close(i);
- return r;
-}
+#ifdef DEBUG_MEMORY
+ if (lbuf_set == 0) {
+ atexit(free_lbuf);
+ lbuf_set = 1;
+ }
#endif
-/* Close our fd's */
-int
-close_fds(void)
-{
- int fd;
-
- if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1)
- return -1;
-
- dup2(fd, fileno(stdin));
- dup2(fd, fileno(stdout));
- dup2(fd, fileno(stderr));
- if (fd > 2)
- close(fd);
- return 0;
+ do {
+ bytes = getline(&lbuf, &lbuf_len, fp);
+ if (bytes == -1)
+ return NULL;
+ for (p = lbuf; *p == ' ' || *p == '\t'; p++)
+ ;
+ } while (*p == '\0' || *p == '\n' || *p == '#' || *p == ';');
+ if (lbuf[--bytes] == '\n')
+ lbuf[bytes] = '\0';
+ return p;
}
int
@@ -162,10 +111,10 @@ set_cloexec(int fd)
{
int flags;
- if ((flags = fcntl(fd, F_GETFD, 0)) == -1
- || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
+ fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
{
- logger(LOG_ERR, "fcntl: %s", strerror(errno));
+ syslog(LOG_ERR, "fcntl: %m");
return -1;
}
return 0;
@@ -176,10 +125,10 @@ set_nonblock(int fd)
{
int flags;
- if ((flags = fcntl(fd, F_GETFL, 0)) == -1
- || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
{
- logger(LOG_ERR, "fcntl: %s", strerror(errno));
+ syslog(LOG_ERR, "fcntl: %m");
return -1;
}
return 0;
@@ -199,7 +148,7 @@ get_monotonic(struct timeval *tp)
struct timespec ts;
static clockid_t posix_clock;
- if (posix_clock_set == 0) {
+ if (!posix_clock_set) {
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
posix_clock = CLOCK_MONOTONIC;
clock_monotonic = posix_clock_set = 1;
@@ -222,7 +171,7 @@ get_monotonic(struct timeval *tp)
uint64_t nano;
long rem;
- if (posix_clock_set == 0) {
+ if (!posix_clock_set) {
if (mach_timebase_info(&info) == KERN_SUCCESS) {
factor = (double)info.numer / (double)info.denom;
clock_monotonic = posix_clock_set = 1;
@@ -245,7 +194,7 @@ get_monotonic(struct timeval *tp)
/* Something above failed, so fall back to gettimeofday */
if (!posix_clock_set) {
- logger(LOG_WARNING, NO_MONOTONIC);
+ syslog(LOG_WARNING, NO_MONOTONIC);
posix_clock_set = 1;
}
return gettimeofday(tp, NULL);
@@ -281,9 +230,9 @@ xmalloc(size_t s)
{
void *value = malloc(s);
- if (value)
+ if (value != NULL)
return value;
- logger(LOG_ERR, "memory exhausted");
+ syslog(LOG_ERR, "memory exhausted (xalloc %zu bytes)", s);
exit (EXIT_FAILURE);
/* NOTREACHED */
}
@@ -302,9 +251,9 @@ xrealloc(void *ptr, size_t s)
{
void *value = realloc(ptr, s);
- if (value)
- return (value);
- logger(LOG_ERR, "memory exhausted");
+ if (value != NULL)
+ return value;
+ syslog(LOG_ERR, "memory exhausted (xrealloc %zu bytes)", s);
exit(EXIT_FAILURE);
/* NOTREACHED */
}
@@ -314,13 +263,13 @@ xstrdup(const char *str)
{
char *value;
- if (!str)
+ if (str == NULL)
return NULL;
- if ((value = strdup(str)))
+ if ((value = strdup(str)) != NULL)
return value;
- logger(LOG_ERR, "memory exhausted");
+ syslog(LOG_ERR, "memory exhausted (xstrdup)");
exit(EXIT_FAILURE);
/* NOTREACHED */
}
diff --git a/common.h b/common.h
index 2522663..fbbfc18 100644
--- a/common.h
+++ b/common.h
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -28,53 +28,48 @@
#ifndef COMMON_H
#define COMMON_H
-/* string.h pulls in features.h so the below define checks work */
-#include <sys/types.h>
-#include <sys/time.h>
-#include <stdint.h>
#include <stdio.h>
-#include <string.h>
+#include <time.h>
+
+#include "config.h"
+#include "defs.h"
#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#define timeval_to_double(tv) ((tv)->tv_sec * 1.0 + (tv)->tv_usec * 1.0e-6)
+#define timernorm(tvp) \
+ do { \
+ while ((tvp)->tv_usec >= 1000000) { \
+ (tvp)->tv_sec++; \
+ (tvp)->tv_usec -= 1000000; \
+ } \
+ } while (0 /* CONSTCOND */);
+
#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
-# define _unused __attribute__((__unused__))
+# define _noreturn __attribute__((__noreturn__))
+# define _packed __attribute__((__packed__))
+# define _unused __attribute__((__unused__))
#else
+# define _noreturn
+# define _packed
# define _unused
#endif
-#ifndef HAVE_ARC4RANDOM
-# ifdef __GLIBC__
-uint32_t arc4random(void);
-#else
-# define HAVE_ARC4RANDOM
-# endif
-#endif
-
-#ifndef HAVE_STRLCPY
-# define HAVE_STRLCPY 1
-#endif
-/* Only GLIBC doesn't support strlcpy */
-#ifdef __GLIBC__
-# if !defined(__UCLIBC__) && !defined (__dietlibc__)
-# undef HAVE_STRLCPY
-size_t strlcpy(char *, const char *, size_t);
-# endif
-#endif
-
-#ifndef HAVE_CLOSEFROM
-# if defined(__NetBSD__) || defined(__OpenBSD__)
-# define HAVE_CLOSEFROM 1
+/* We don't really need this as our supported systems define __restrict
+ * automatically for us, but it is here for completeness. */
+#ifndef __restrict
+# if defined(__lint__)
+# define __restrict
+# elif __STDC_VERSION__ >= 199901L
+# define __restrict restrict
+# elif !(2 < __GNUC__ || (2 == __GNU_C && 95 <= __GNUC_VERSION__))
+# define __restrict
# endif
#endif
-#ifndef HAVE_CLOSEFROM
-int closefrom(int);
-#endif
-int close_fds(void);
int set_cloexec(int);
int set_nonblock(int);
-ssize_t get_line(char **, size_t *, FILE *);
+char *get_line(FILE * __restrict);
extern int clock_monotonic;
int get_monotonic(struct timeval *);
time_t uptime(void);
diff --git a/compat/arc4random.c b/compat/arc4random.c
new file mode 100644
index 0000000..48ef29d
--- /dev/null
+++ b/compat/arc4random.c
@@ -0,0 +1,158 @@
+/*
+ * Arc4 random number generator for OpenBSD.
+ * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
+ *
+ * Modification and redistribution in source and binary forms is
+ * permitted provided that due credit is given to the author and the
+ * OpenBSD project by leaving this copyright notice intact.
+ */
+
+/*
+ * This code is derived from section 17.1 of Applied Cryptography,
+ * second edition, which describes a stream cipher allegedly
+ * compatible with RSA Labs "RC4" cipher (the actual description of
+ * which is a trade secret). The same algorithm is used as a stream
+ * cipher called "arcfour" in Tatu Ylonen's ssh package.
+ *
+ * Here the stream cipher has been modified always to include the time
+ * when initializing the state. That makes it impossible to
+ * regenerate the same random sequence twice, so this can't be used
+ * for encryption, but will generate good random numbers.
+ *
+ * RC4 is a registered trademark of RSA Laboratories.
+ */
+
+#include <sys/time.h>
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "arc4random.h"
+
+struct arc4_stream {
+ uint8_t i;
+ uint8_t j;
+ uint8_t s[256];
+};
+
+static int rs_initialized;
+static struct arc4_stream rs;
+static int arc4_count;
+
+static void
+arc4_init(struct arc4_stream *as)
+{
+ int n;
+
+ for (n = 0; n < 256; n++)
+ as->s[n] = n;
+ as->i = 0;
+ as->j = 0;
+}
+
+static void
+arc4_addrandom(struct arc4_stream *as, unsigned char *dat, int datlen)
+{
+ int n;
+ uint8_t si;
+
+ as->i--;
+ for (n = 0; n < 256; n++) {
+ as->i = (as->i + 1);
+ si = as->s[as->i];
+ as->j = (as->j + si + dat[n % datlen]);
+ as->s[as->i] = as->s[as->j];
+ as->s[as->j] = si;
+ }
+ as->j = as->i;
+}
+
+static uint8_t
+arc4_getbyte(struct arc4_stream *as)
+{
+ uint8_t si, sj;
+
+ as->i = (as->i + 1);
+ si = as->s[as->i];
+ as->j = (as->j + si);
+ sj = as->s[as->j];
+ as->s[as->i] = sj;
+ as->s[as->j] = si;
+ return (as->s[(si + sj) & 0xff]);
+}
+
+static uint32_t
+arc4_getword(struct arc4_stream *as)
+{
+ uint32_t val;
+
+ val = arc4_getbyte(as) << 24;
+ val |= arc4_getbyte(as) << 16;
+ val |= arc4_getbyte(as) << 8;
+ val |= arc4_getbyte(as);
+ return val;
+}
+
+static void
+arc4_stir(struct arc4_stream *as)
+{
+ int fd;
+ struct {
+ struct timeval tv;
+ unsigned int rnd[(128 - sizeof(struct timeval)) /
+ sizeof(unsigned int)];
+ } rdat;
+ int n;
+
+ gettimeofday(&rdat.tv, NULL);
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd != -1) {
+ n = read(fd, rdat.rnd, sizeof(rdat.rnd));
+ close(fd);
+ }
+
+ /* fd < 0? Ah, what the heck. We'll just take
+ * whatever was on the stack... */
+ arc4_addrandom(as, (void *) &rdat, sizeof(rdat));
+
+ /*
+ * Throw away the first N words of output, as suggested in the
+ * paper "Weaknesses in the Key Scheduling Algorithm of RC4"
+ * by Fluher, Mantin, and Shamir. (N = 256 in our case.)
+ */
+ for (n = 0; n < 256 * 4; n++)
+ arc4_getbyte(as);
+ arc4_count = 1600000;
+}
+
+void
+arc4random_stir()
+{
+
+ if (!rs_initialized) {
+ arc4_init(&rs);
+ rs_initialized = 1;
+ }
+ arc4_stir(&rs);
+}
+
+void
+arc4random_addrandom(unsigned char *dat, int datlen)
+{
+
+ if (!rs_initialized)
+ arc4random_stir();
+ arc4_addrandom(&rs, dat, datlen);
+}
+
+uint32_t
+arc4random()
+{
+
+ arc4_count -= 4;
+ if (!rs_initialized || arc4_count <= 0)
+ arc4random_stir();
+ return arc4_getword(&rs);
+}
diff --git a/compat/arc4random.h b/compat/arc4random.h
new file mode 100644
index 0000000..2b10902
--- /dev/null
+++ b/compat/arc4random.h
@@ -0,0 +1,36 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef ARC4RANDOM_H
+#define ARC4RANDOM_H
+
+#include <stdint.h>
+
+void arc4random_stir(void);
+void arc4random_addrandom(unsigned char *, int);
+uint32_t arc4random(void);
+#endif
diff --git a/compat/closefrom.c b/compat/closefrom.c
new file mode 100644
index 0000000..664c4bc
--- /dev/null
+++ b/compat/closefrom.c
@@ -0,0 +1,42 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <unistd.h>
+
+#include "closefrom.h"
+
+int
+closefrom(int fd)
+{
+ int max = getdtablesize();
+ int i;
+ int r = 0;
+
+ for (i = fd; i < max; i++)
+ r += close(i);
+ return r;
+}
diff --git a/compat/closefrom.h b/compat/closefrom.h
new file mode 100644
index 0000000..b028507
--- /dev/null
+++ b/compat/closefrom.h
@@ -0,0 +1,31 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef CLOSEFROM_H
+#define CLOSEFROM_H
+int closefrom(int);
+#endif
diff --git a/compat/getline.c b/compat/getline.c
new file mode 100644
index 0000000..3f01b66
--- /dev/null
+++ b/compat/getline.c
@@ -0,0 +1,75 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "getline.h"
+
+/* Redefine a small buffer for our simple text config files */
+#undef BUFSIZ
+#define BUFSIZ 128
+
+ssize_t
+getline(char ** __restrict buf, size_t * __restrict buflen,
+ FILE * __restrict fp)
+{
+ size_t bytes, newlen;
+ char *newbuf, *p;
+
+ if (buf == NULL || buflen == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (*buf == NULL)
+ *buflen = 0;
+
+ bytes = 0;
+ do {
+ if (feof(fp))
+ break;
+ if (*buf == NULL || bytes != 0) {
+ newlen = *buflen + BUFSIZ;
+ newbuf = realloc(*buf, newlen);
+ if (newbuf == NULL)
+ return -1;
+ *buf = newbuf;
+ *buflen = newlen;
+ }
+ p = *buf + bytes;
+ memset(p, 0, BUFSIZ);
+ if (fgets(p, BUFSIZ, fp) == NULL)
+ break;
+ bytes += strlen(p);
+ } while (bytes == 0 || *(*buf + (bytes - 1)) != '\n');
+ if (bytes == 0)
+ return -1;
+ return bytes;
+}
diff --git a/compat/getline.h b/compat/getline.h
new file mode 100644
index 0000000..390632c
--- /dev/null
+++ b/compat/getline.h
@@ -0,0 +1,36 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef GETLINE_H
+#define GETLINE_H
+
+#include <sys/types.h>
+#include <stdio.h>
+
+ssize_t getline(char ** __restrict buf, size_t * __restrict buflen,
+ FILE * __restrict fp);
+#endif
diff --git a/compat/linkaddr.c b/compat/linkaddr.c
new file mode 100644
index 0000000..c4e6fa5
--- /dev/null
+++ b/compat/linkaddr.c
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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 University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)linkaddr.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if_dl.h>
+
+#include <string.h>
+
+/* States*/
+#define NAMING 0
+#define GOTONE 1
+#define GOTTWO 2
+#define RESET 3
+/* Inputs */
+#define DIGIT (4*0)
+#define END (4*1)
+#define DELIM (4*2)
+#define LETTER (4*3)
+
+void
+link_addr(addr, sdl)
+ const char *addr;
+ struct sockaddr_dl *sdl;
+{
+ char *cp = sdl->sdl_data;
+ char *cplim = sdl->sdl_len + (char *)(void *)sdl;
+ int byte = 0, state = NAMING;
+ int newaddr = 0;
+
+ (void)memset(&sdl->sdl_family, 0, (size_t)sdl->sdl_len - 1);
+ sdl->sdl_family = AF_LINK;
+ do {
+ state &= ~LETTER;
+ if ((*addr >= '0') && (*addr <= '9')) {
+ newaddr = *addr - '0';
+ } else if ((*addr >= 'a') && (*addr <= 'f')) {
+ newaddr = *addr - 'a' + 10;
+ } else if ((*addr >= 'A') && (*addr <= 'F')) {
+ newaddr = *addr - 'A' + 10;
+ } else if (*addr == 0) {
+ state |= END;
+ } else if (state == NAMING &&
+ (((*addr >= 'A') && (*addr <= 'Z')) ||
+ ((*addr >= 'a') && (*addr <= 'z'))))
+ state |= LETTER;
+ else
+ state |= DELIM;
+ addr++;
+ switch (state /* | INPUT */) {
+ case NAMING | DIGIT:
+ case NAMING | LETTER:
+ *cp++ = addr[-1];
+ continue;
+ case NAMING | DELIM:
+ state = RESET;
+ sdl->sdl_nlen = cp - sdl->sdl_data;
+ continue;
+ case GOTTWO | DIGIT:
+ *cp++ = byte;
+ /* FALLTHROUGH */
+ case RESET | DIGIT:
+ state = GOTONE;
+ byte = newaddr;
+ continue;
+ case GOTONE | DIGIT:
+ state = GOTTWO;
+ byte = newaddr + (byte << 4);
+ continue;
+ default: /* | DELIM */
+ state = RESET;
+ *cp++ = byte;
+ byte = 0;
+ continue;
+ case GOTONE | END:
+ case GOTTWO | END:
+ *cp++ = byte;
+ /* FALLTHROUGH */
+ case RESET | END:
+ break;
+ }
+ break;
+ } while (cp < cplim);
+ sdl->sdl_alen = cp - LLADDR(sdl);
+ newaddr = cp - (char *)(void *)sdl;
+ if ((size_t) newaddr > sizeof(*sdl))
+ sdl->sdl_len = newaddr;
+ return;
+}
diff --git a/compat/strlcpy.c b/compat/strlcpy.c
new file mode 100644
index 0000000..e44d19c
--- /dev/null
+++ b/compat/strlcpy.c
@@ -0,0 +1,51 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+
+#include "strlcpy.h"
+
+size_t
+strlcpy(char *dst, const char *src, size_t size)
+{
+ const char *s = src;
+ size_t n = size;
+
+ if (n && --n)
+ do {
+ if (!(*dst++ = *src++))
+ break;
+ } while (--n);
+
+ if (!n) {
+ if (size)
+ *dst = '\0';
+ while (*src++);
+ }
+
+ return src - s - 1;
+}
diff --git a/compat/strlcpy.h b/compat/strlcpy.h
new file mode 100644
index 0000000..0ff3854
--- /dev/null
+++ b/compat/strlcpy.h
@@ -0,0 +1,34 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef STRLCPY_H
+#define STRLCPY_H
+
+#include <sys/types.h>
+
+size_t strlcpy(char *, const char *, size_t);
+#endif
diff --git a/config.h b/config.h
index 595fb94..180c63e 100644
--- a/config.h
+++ b/config.h
@@ -1,75 +1,18 @@
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
- */
+/* linux */
+#define SYSCONFDIR "/system/etc/dhcpcd"
+#define SBINDIR "/system/etc/dhcpcd"
+#define LIBEXECDIR "/system/etc/dhcpcd"
+#define DBDIR "/data/misc/dhcp"
+#define RUNDIR "/data/misc/dhcp"
+#include "compat/arc4random.h"
+#include "compat/closefrom.h"
+#include "compat/strlcpy.h"
+#include "compat/getline.h"
-#ifndef CONFIG_H
-#define CONFIG_H
-
-#define PACKAGE "dhcpcd"
-#define VERSION "4.0.15"
-
-/*
- * By default we don't add a local link route if we got a routeable address.
- * This is because dhcpcd can't really decide which interface should allow
- * link local routing when we have more than one interface.
- * Ideally the host network scripts should add the link local route for us.
- * If not, you can define this to get dhcpcd to always add the link local route.
- */
-// #define IPV4LL_ALWAYSROUTE
-
-/* Some systems do not have a working fork. */
-/* #define THERE_IS_NO_FORK */
-
-/* Paths to things */
-#ifndef SYSCONFDIR
-# define SYSCONFDIR "/system/etc/dhcpcd"
-#endif
-#ifndef LIBEXECDIR
-# define LIBEXECDIR "/system/etc/dhcpcd"
-#endif
-#ifndef RUNDIR
-# define RUNDIR "/data/misc/dhcp"
-#endif
-#ifndef DBDIR
-# define DBDIR "/data/misc/dhcp"
-#endif
-
-#ifndef CONFIG
-# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"
-#endif
-#ifndef SCRIPT
-# define SCRIPT LIBEXECDIR "/" PACKAGE "-run-hooks"
-#endif
-#ifndef DUID
-# define DUID SYSCONFDIR "/" PACKAGE ".duid"
-#endif
-#ifndef LEASEFILE
-# define LEASEFILE DBDIR "/" PACKAGE "-%s.lease"
-#endif
-#ifndef PIDFILE
-# define PIDFILE RUNDIR "/" PACKAGE "-%s.pid"
+#ifndef MAX
+#define MAX(a,b) ((a) >= (b) ? (a) : (b))
#endif
+#ifndef MIN
+#define MIN(a,b) ((a) <= (b) ? (a) : (b))
#endif
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..c7a9eca
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,20 @@
+# linux
+SYSCONFDIR= /etc
+SBINDIR= /sbin
+LIBEXECDIR= /libexec
+DBDIR= /var/db
+RUNDIR= /var/run
+LIBDIR= /lib
+MANDIR= /usr/share/man
+CC= gcc
+CPPFLAGS+= -D_BSD_SOURCE -D_XOPEN_SOURCE=600
+SRCS+= if-linux.c if-linux-wireless.c lpf.c
+SRCS+= platform-linux.c
+LDADD+= -lrt
+COMPAT_SRCS+= compat/arc4random.c
+COMPAT_SRCS+= compat/closefrom.c
+COMPAT_SRCS+= compat/strlcpy.c
+SERVICEEXISTS= /usr/sbin/invoke-rc.d --query --quiet $$1 start >/dev/null 2>\&1 || [ $$? = 104 ]
+SERVICECMD= /usr/sbin/invoke-rc.d $$1 $$2
+SERVICESTATUS= service_command $$1 status >/dev/null 2>\&1
+HOOKSCRIPTS= 50-ntp.conf
diff --git a/configure.c b/configure.c
index 1e6daeb..fb28669 100644
--- a/configure.c
+++ b/configure.c
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -26,6 +26,7 @@
*/
#include <sys/stat.h>
+#include <sys/uio.h>
#include <sys/wait.h>
#include <netinet/in.h>
@@ -35,19 +36,33 @@
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
#include <unistd.h>
#include "config.h"
#include "common.h"
#include "configure.h"
#include "dhcp.h"
-#include "dhcpcd.h"
-#include "logger.h"
+#include "if-options.h"
+#include "if-pref.h"
#include "net.h"
#include "signals.h"
#define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
+/* Some systems have route metrics */
+#ifndef HAVE_ROUTE_METRIC
+# ifdef __linux__
+# define HAVE_ROUTE_METRIC 1
+# endif
+# ifndef HAVE_ROUTE_METRIC
+# define HAVE_ROUTE_METRIC 0
+# endif
+#endif
+
+static struct rt *routes;
+
static int
exec_script(char *const *argv, char *const *env)
@@ -63,12 +78,12 @@ exec_script(char *const *argv, char *const *env)
switch (pid = vfork()) {
case -1:
- logger(LOG_ERR, "vfork: %s", strerror(errno));
+ syslog(LOG_ERR, "vfork: %m");
break;
case 0:
sigprocmask(SIG_SETMASK, &old, NULL);
execve(argv[0], argv, env);
- logger(LOG_ERR, "%s: %s", argv[0], strerror(errno));
+ syslog(LOG_ERR, "%s: %m", argv[0]);
_exit(127);
/* NOTREACHED */
}
@@ -79,70 +94,231 @@ exec_script(char *const *argv, char *const *env)
return pid;
}
-int
-run_script(const struct options *options, const char *iface,
- const char *reason,
- const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
+static char *
+make_var(const char *prefix, const char *var)
{
- char *const argv[2] = { UNCONST(options->script), NULL };
- char **env = NULL, **ep;
- char *path;
- ssize_t e, elen;
- pid_t pid;
- int status = 0;
+ size_t len;
+ char *v;
+
+ len = strlen(prefix) + strlen(var) + 2;
+ v = xmalloc(len);
+ snprintf(v, len, "%s_%s", prefix, var);
+ return v;
+}
+
- logger(LOG_DEBUG, "executing `%s', reason %s", options->script, reason);
+static void
+append_config(char ***env, ssize_t *len,
+ const char *prefix, const char *const *config)
+{
+ ssize_t i, j, e1;
+ char **ne, *eq;
+
+ if (config == NULL)
+ return;
+
+ ne = *env;
+ for (i = 0; config[i] != NULL; i++) {
+ eq = strchr(config[i], '=');
+ e1 = eq - config[i] + 1;
+ for (j = 0; j < *len; j++) {
+ if (strncmp(ne[j] + strlen(prefix) + 1,
+ config[i], e1) == 0)
+ {
+ free(ne[j]);
+ ne[j] = make_var(prefix, config[i]);
+ break;
+ }
+ }
+ if (j == *len) {
+ j++;
+ ne = xrealloc(ne, sizeof(char *) * (j + 1));
+ ne[j - 1] = make_var(prefix, config[i]);
+ *len = j;
+ }
+ }
+ *env = ne;
+}
+
+static size_t
+arraytostr(const char *const *argv, char **s)
+{
+ const char *const *ap;
+ char *p;
+ size_t len, l;
+
+ len = 0;
+ ap = argv;
+ while (*ap)
+ len += strlen(*ap++) + 1;
+ *s = p = xmalloc(len);
+ ap = argv;
+ while (*ap) {
+ l = strlen(*ap) + 1;
+ memcpy(p, *ap, l);
+ p += l;
+ ap++;
+ }
+ return len;
+}
+
+static ssize_t
+make_env(const struct interface *iface, char ***argv)
+{
+ char **env, *p;
+ ssize_t e, elen, l;
+ const struct if_options *ifo = iface->state->options;
+ const struct interface *ifp;
/* Make our env */
- elen = 5;
+ elen = 8;
env = xmalloc(sizeof(char *) * (elen + 1));
- path = getenv("PATH");
- if (path) {
- e = strlen("PATH") + strlen(path) + 2;
- env[0] = xmalloc(e);
- snprintf(env[0], e, "PATH=%s", path);
- } else
- env[0] = xstrdup(DEFAULT_PATH);
- e = strlen("interface") + strlen(iface) + 2;
+ e = strlen("interface") + strlen(iface->name) + 2;
+ env[0] = xmalloc(e);
+ snprintf(env[0], e, "interface=%s", iface->name);
+ e = strlen("reason") + strlen(iface->state->reason) + 2;
env[1] = xmalloc(e);
- snprintf(env[1], e, "interface=%s", iface);
- e = strlen("reason") + strlen(reason) + 2;
- env[2] = xmalloc(e);
- snprintf(env[2], e, "reason=%s", reason);
+ snprintf(env[1], e, "reason=%s", iface->state->reason);
e = 20;
+ env[2] = xmalloc(e);
+ snprintf(env[2], e, "pid=%d", getpid());
env[3] = xmalloc(e);
- snprintf(env[3], e, "pid=%d", getpid());
+ snprintf(env[3], e, "ifmetric=%d", iface->metric);
env[4] = xmalloc(e);
- snprintf(env[4], e, "metric=%d", options->metric);
- if (dhcpo) {
- e = configure_env(NULL, NULL, dhcpo, options);
+ snprintf(env[4], e, "ifwireless=%d", iface->wireless);
+ env[5] = xmalloc(e);
+ snprintf(env[5], e, "ifflags=%u", iface->flags);
+ env[6] = xmalloc(e);
+ snprintf(env[6], e, "ifmtu=%d", get_mtu(iface->name));
+ l = e = strlen("interface_order=");
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ e += strlen(ifp->name) + 1;
+ p = env[7] = xmalloc(e);
+ strlcpy(p, "interface_order=", e);
+ e -= l;
+ p += l;
+ for (ifp = ifaces; ifp; ifp = ifp->next) {
+ l = strlcpy(p, ifp->name, e);
+ p += l;
+ e -= l;
+ *p++ = ' ';
+ e--;
+ }
+ *--p = '\0';
+ if (*iface->state->profile) {
+ e = strlen("profile=") + strlen(iface->state->profile) + 2;
+ env[elen] = xmalloc(e);
+ snprintf(env[elen++], e, "profile=%s", iface->state->profile);
+ }
+ if (iface->wireless) {
+ e = strlen("new_ssid=") + strlen(iface->ssid) + 2;
+ if (iface->state->new != NULL ||
+ strcmp(iface->state->reason, "CARRIER") == 0)
+ {
+ env = xrealloc(env, sizeof(char *) * (elen + 2));
+ env[elen] = xmalloc(e);
+ snprintf(env[elen++], e, "new_ssid=%s", iface->ssid);
+ }
+ if (iface->state->old != NULL ||
+ strcmp(iface->state->reason, "NOCARRIER") == 0)
+ {
+ env = xrealloc(env, sizeof(char *) * (elen + 2));
+ env[elen] = xmalloc(e);
+ snprintf(env[elen++], e, "old_ssid=%s", iface->ssid);
+ }
+ }
+ if (iface->state->old) {
+ e = configure_env(NULL, NULL, iface->state->old, ifo);
if (e > 0) {
env = xrealloc(env, sizeof(char *) * (elen + e + 1));
- elen += configure_env(env + elen, "old", dhcpo, options);
+ elen += configure_env(env + elen, "old",
+ iface->state->old, ifo);
}
+ append_config(&env, &elen, "old",
+ (const char *const *)ifo->config);
}
- if (dhcpn) {
- e = configure_env(NULL, NULL, dhcpn, options);
+ if (iface->state->new) {
+ e = configure_env(NULL, NULL, iface->state->new, ifo);
if (e > 0) {
env = xrealloc(env, sizeof(char *) * (elen + e + 1));
- elen += configure_env(env + elen, "new", dhcpn, options);
+ elen += configure_env(env + elen, "new",
+ iface->state->new, ifo);
}
+ append_config(&env, &elen, "new",
+ (const char *const *)ifo->config);
}
+
/* Add our base environment */
- if (options->environ) {
+ if (ifo->environ) {
e = 0;
- while (options->environ[e++])
+ while (ifo->environ[e++])
;
env = xrealloc(env, sizeof(char *) * (elen + e + 1));
e = 0;
- while (options->environ[e]) {
- env[elen + e] = xstrdup(options->environ[e]);
+ while (ifo->environ[e]) {
+ env[elen + e] = xstrdup(ifo->environ[e]);
e++;
}
elen += e;
}
env[elen] = '\0';
+ *argv = env;
+ return elen;
+}
+
+int
+send_interface(int fd, const struct interface *iface)
+{
+ char **env, **ep, *s;
+ ssize_t elen;
+ struct iovec iov[2];
+ int retval;
+
+ retval = 0;
+ make_env(iface, &env);
+ elen = arraytostr((const char *const *)env, &s);
+ iov[0].iov_base = &elen;
+ iov[0].iov_len = sizeof(ssize_t);
+ iov[1].iov_base = s;
+ iov[1].iov_len = elen;
+ retval = writev(fd, iov, 2);
+ ep = env;
+ while (*ep)
+ free(*ep++);
+ free(env);
+ free(s);
+ return retval;
+}
+
+int
+run_script(const struct interface *iface)
+{
+ char *const argv[2] = { UNCONST(iface->state->options->script), NULL };
+ char **env = NULL, **ep;
+ char *path, *bigenv;
+ ssize_t e, elen = 0;
+ pid_t pid;
+ int status = 0;
+ const struct fd_list *fd;
+ struct iovec iov[2];
+
+ syslog(LOG_DEBUG, "%s: executing `%s', reason %s",
+ iface->name, argv[0], iface->state->reason);
+
+ /* Make our env */
+ elen = make_env(iface, &env);
+ env = xrealloc(env, sizeof(char *) * (elen + 2));
+ /* Add path to it */
+ path = getenv("PATH");
+ if (path) {
+ e = strlen("PATH") + strlen(path) + 2;
+ env[elen] = xmalloc(e);
+ snprintf(env[elen], e, "PATH=%s", path);
+ } else
+ env[elen] = xstrdup(DEFAULT_PATH);
+ env[++elen] = '\0';
+
pid = exec_script(argv, env);
if (pid == -1)
status = -1;
@@ -150,13 +326,31 @@ run_script(const struct options *options, const char *iface,
/* Wait for the script to finish */
while (waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
- logger(LOG_ERR, "waitpid: %s", strerror(errno));
+ syslog(LOG_ERR, "waitpid: %m");
status = -1;
break;
}
}
}
+ /* Send to our listeners */
+ bigenv = NULL;
+ for (fd = fds; fd != NULL; fd = fd->next) {
+ if (fd->listener) {
+ if (bigenv == NULL) {
+ elen = arraytostr((const char *const *)env,
+ &bigenv);
+ iov[0].iov_base = &elen;
+ iov[0].iov_len = sizeof(ssize_t);
+ iov[1].iov_base = bigenv;
+ iov[1].iov_len = elen;
+ }
+ if (writev(fd->fd, iov, 2) == -1)
+ syslog(LOG_ERR, "writev: %m");
+ }
+ }
+ free(bigenv);
+
/* Cleanup */
ep = env;
while (*ep)
@@ -166,251 +360,419 @@ run_script(const struct options *options, const char *iface,
}
static struct rt *
-reverse_routes(struct rt *routes)
+find_route(struct rt *rts, const struct rt *r, struct rt **lrt,
+ const struct rt *srt)
{
struct rt *rt;
- struct rt *rtn = NULL;
-
- while (routes) {
- rt = routes->next;
- routes->next = rtn;
- rtn = routes;
- routes = rt;
+
+ if (lrt)
+ *lrt = NULL;
+ for (rt = rts; rt; rt = rt->next) {
+ if (rt->dest.s_addr == r->dest.s_addr &&
+#if HAVE_ROUTE_METRIC
+ (srt || (!rt->iface ||
+ rt->iface->metric == r->iface->metric)) &&
+#endif
+ (!srt || srt != rt) &&
+ rt->net.s_addr == r->net.s_addr)
+ return rt;
+ if (lrt)
+ *lrt = rt;
+ }
+ return NULL;
+}
+
+static void
+desc_route(const char *cmd, const struct rt *rt, const char *ifname)
+{
+ char addr[sizeof("000.000.000.000") + 1];
+
+ strlcpy(addr, inet_ntoa(rt->dest), sizeof(addr));
+ if (rt->gate.s_addr == INADDR_ANY)
+ syslog(LOG_DEBUG, "%s: %s route to %s/%d", ifname, cmd,
+ addr, inet_ntocidr(rt->net));
+ else if (rt->gate.s_addr == rt->dest.s_addr &&
+ rt->net.s_addr == INADDR_BROADCAST)
+ syslog(LOG_DEBUG, "%s: %s host route to %s", ifname, cmd,
+ addr);
+ else if (rt->dest.s_addr == INADDR_ANY && rt->net.s_addr == INADDR_ANY)
+ syslog(LOG_DEBUG, "%s: %s default route via %s", ifname, cmd,
+ inet_ntoa(rt->gate));
+ else
+ syslog(LOG_DEBUG, "%s: %s route to %s/%d via %s", ifname, cmd,
+ addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
+}
+
+/* If something other than dhcpcd removes a route,
+ * we need to remove it from our internal table. */
+int
+route_deleted(const struct rt *rt)
+{
+ struct rt *f, *l;
+
+ f = find_route(routes, rt, &l, NULL);
+ if (f == NULL)
+ return 0;
+ desc_route("removing", f, f->iface->name);
+ if (l)
+ l->next = f->next;
+ else
+ routes = f->next;
+ free(f);
+ return 1;
+}
+
+static int
+n_route(struct rt *rt, const struct interface *iface)
+{
+ /* Don't set default routes if not asked to */
+ if (rt->dest.s_addr == 0 &&
+ rt->net.s_addr == 0 &&
+ !(iface->state->options->options & DHCPCD_GATEWAY))
+ return -1;
+
+ desc_route("adding", rt, iface->name);
+ if (!add_route(iface, &rt->dest, &rt->net, &rt->gate, iface->metric))
+ return 0;
+ if (errno == EEXIST) {
+ /* Pretend we added the subnet route */
+ if (rt->dest.s_addr == (iface->addr.s_addr & iface->net.s_addr) &&
+ rt->net.s_addr == iface->net.s_addr &&
+ rt->gate.s_addr == 0)
+ return 0;
+ else
+ return -1;
}
- return rtn;
+ syslog(LOG_ERR, "%s: add_route: %m", iface->name);
+ return -1;
}
static int
-delete_route(const struct interface *iface, struct rt *rt, int metric)
+c_route(struct rt *ort, struct rt *nrt, const struct interface *iface)
+{
+ /* Don't set default routes if not asked to */
+ if (nrt->dest.s_addr == 0 &&
+ nrt->net.s_addr == 0 &&
+ !(iface->state->options->options & DHCPCD_GATEWAY))
+ return -1;
+
+ desc_route("changing", nrt, iface->name);
+ /* We delete and add the route so that we can change metric.
+ * This also has the nice side effect of flushing ARP entries so
+ * we don't have to do that manually. */
+ del_route(ort->iface, &ort->dest, &ort->net, &ort->gate,
+ ort->iface->metric);
+ if (!add_route(iface, &nrt->dest, &nrt->net, &nrt->gate,
+ iface->metric))
+ return 0;
+ syslog(LOG_ERR, "%s: add_route: %m", iface->name);
+ return -1;
+}
+
+static int
+d_route(struct rt *rt, const struct interface *iface, int metric)
{
- char *addr;
int retval;
- addr = xstrdup(inet_ntoa(rt->dest));
- logger(LOG_DEBUG, "deleting route %s/%d via %s",
- addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
- free(addr);
+ desc_route("deleting", rt, iface->name);
retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric);
if (retval != 0 && errno != ENOENT && errno != ESRCH)
- logger(LOG_ERR," del_route: %s", strerror(errno));
+ syslog(LOG_ERR,"%s: del_route: %m", iface->name);
return retval;
}
-static int
-delete_routes(struct interface *iface, int metric)
+static struct rt *
+get_subnet_route(struct dhcp_message *dhcp)
{
+ in_addr_t addr;
+ struct in_addr net;
struct rt *rt;
- struct rt *rtn;
- int retval = 0;
- rt = reverse_routes(iface->routes);
- while (rt) {
- rtn = rt->next;
- retval += delete_route(iface, rt, metric);
- free(rt);
- rt = rtn;
- }
- iface->routes = NULL;
+ addr = dhcp->yiaddr;
+ if (addr == 0)
+ addr = dhcp->ciaddr;
+ /* Ensure we have all the needed values */
+ if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1)
+ net.s_addr = get_netmask(addr);
+ if (net.s_addr == INADDR_BROADCAST || net.s_addr == INADDR_ANY)
+ return NULL;
+ rt = malloc(sizeof(*rt));
+ rt->dest.s_addr = addr & net.s_addr;
+ rt->net.s_addr = net.s_addr;
+ rt->gate.s_addr = 0;
+ return rt;
+}
- return retval;
+static struct rt *
+add_subnet_route(struct rt *rt, const struct interface *iface)
+{
+ struct rt *r;
+
+ if (iface->net.s_addr == INADDR_BROADCAST ||
+ iface->net.s_addr == INADDR_ANY ||
+ (iface->state->options->options &
+ (DHCPCD_INFORM | DHCPCD_STATIC) &&
+ iface->state->options->req_addr.s_addr == INADDR_ANY))
+ return rt;
+
+ r = xmalloc(sizeof(*r));
+ r->dest.s_addr = iface->addr.s_addr & iface->net.s_addr;
+ r->net.s_addr = iface->net.s_addr;
+ r->gate.s_addr = 0;
+ r->next = rt;
+ return r;
}
-static int
-in_routes(const struct rt *routes, const struct rt *rt)
+static struct rt *
+get_routes(const struct interface *iface)
{
- while (routes) {
- if (routes->dest.s_addr == rt->dest.s_addr &&
- routes->net.s_addr == rt->net.s_addr &&
- routes->gate.s_addr == rt->gate.s_addr)
- return 0;
- routes = routes->next;
+ struct rt *rt, *nrt = NULL, *r = NULL;
+
+ if (iface->state->options->routes != NULL) {
+ for (rt = iface->state->options->routes;
+ rt != NULL;
+ rt = rt->next)
+ {
+ if (rt->gate.s_addr == 0)
+ break;
+ if (r == NULL)
+ r = nrt = xmalloc(sizeof(*r));
+ else {
+ r->next = xmalloc(sizeof(*r));
+ r = r->next;
+ }
+ memcpy(r, rt, sizeof(*r));
+ r->next = NULL;
+ }
+ return nrt;
}
- return -1;
+
+ return get_option_routes(iface->state->new,
+ iface->name, &iface->state->options->options);
}
-static int
-configure_routes(struct interface *iface, const struct dhcp_message *dhcp,
- const struct options *options)
+/* Some DHCP servers add set host routes by setting the gateway
+ * to the assinged IP address. This differs from our notion of a host route
+ * where the gateway is the destination address, so we fix it. */
+static struct rt *
+massage_host_routes(struct rt *rt, const struct interface *iface)
{
- struct rt *rt, *ort;
- struct rt *rtn = NULL, *nr = NULL;
- int remember;
- int retval = 0;
- char *addr;
+ struct rt *r;
- ort = get_option_routes(dhcp);
+ for (r = rt; r; r = r->next)
+ if (r->gate.s_addr == iface->addr.s_addr &&
+ r->net.s_addr == INADDR_BROADCAST)
+ r->gate.s_addr = r->dest.s_addr;
+ return rt;
+}
-#ifdef IPV4LL_ALWAYSROUTE
- if (options->options & DHCPCD_IPV4LL &&
- IN_PRIVATE(ntohl(dhcp->yiaddr)))
- {
- for (rt = ort; rt; rt = rt->next) {
- /* Check if we have already got a link locale route
- * dished out by the DHCP server */
- if (rt->dest.s_addr == htonl(LINKLOCAL_ADDR) &&
- rt->net.s_addr == htonl(LINKLOCAL_MASK))
+static struct rt *
+add_destination_route(struct rt *rt, const struct interface *iface)
+{
+ struct rt *r;
+
+ if (!(iface->flags & IFF_POINTOPOINT) ||
+ !has_option_mask(iface->state->options->dstmask, DHO_ROUTER))
+ return rt;
+ r = xmalloc(sizeof(*r));
+ r->dest.s_addr = INADDR_ANY;
+ r->net.s_addr = INADDR_ANY;
+ r->gate.s_addr = iface->dst.s_addr;
+ r->next = rt;
+ return r;
+}
+
+/* We should check to ensure the routers are on the same subnet
+ * OR supply a host route. If not, warn and add a host route. */
+static struct rt *
+add_router_host_route(struct rt *rt, const struct interface *ifp)
+{
+ struct rt *rtp, *rtl, *rtn;
+ const char *cp, *cp2, *cp3, *cplim;
+
+ for (rtp = rt, rtl = NULL; rtp; rtl = rtp, rtp = rtp->next) {
+ if (rtp->dest.s_addr != INADDR_ANY)
+ continue;
+ /* Scan for a route to match */
+ for (rtn = rt; rtn != rtp; rtn = rtn->next) {
+ /* match host */
+ if (rtn->dest.s_addr == rtp->gate.s_addr)
+ break;
+ /* match subnet */
+ cp = (const char *)&rtp->gate.s_addr;
+ cp2 = (const char *)&rtn->dest.s_addr;
+ cp3 = (const char *)&rtn->net.s_addr;
+ cplim = cp3 + sizeof(rtn->net.s_addr);
+ while (cp3 < cplim) {
+ if ((*cp++ ^ *cp2++) & *cp3++)
+ break;
+ }
+ if (cp3 == cplim)
break;
- rtn = rt;
}
-
- if (!rt) {
- rt = xmalloc(sizeof(*rt));
- rt->dest.s_addr = htonl(LINKLOCAL_ADDR);
- rt->net.s_addr = htonl(LINKLOCAL_MASK);
- rt->gate.s_addr = 0;
- rt->next = NULL;
- if (rtn)
- rtn->next = rt;
- else
- ort = rt;
+ if (rtn != rtp)
+ continue;
+ if (ifp->flags & IFF_NOARP) {
+ syslog(LOG_WARNING,
+ "%s: forcing router %s through interface",
+ ifp->name, inet_ntoa(rtp->gate));
+ rtp->gate.s_addr = 0;
+ continue;
}
+ syslog(LOG_WARNING, "%s: router %s requires a host route",
+ ifp->name, inet_ntoa(rtp->gate));
+ rtn = xmalloc(sizeof(*rtn));
+ rtn->dest.s_addr = rtp->gate.s_addr;
+ rtn->net.s_addr = INADDR_BROADCAST;
+ rtn->gate.s_addr = rtp->gate.s_addr;
+ rtn->next = rtp;
+ if (rtl == NULL)
+ rt = rtn;
+ else
+ rtl->next = rtn;
}
-#endif
-
- /* Now remove old routes we no longer use.
- * We should do this in reverse order. */
- iface->routes = reverse_routes(iface->routes);
- for (rt = iface->routes; rt; rt = rt->next)
- if (in_routes(ort, rt) != 0)
- delete_route(iface, rt, options->metric);
-
- for (rt = ort; rt; rt = rt->next) {
- /* Don't set default routes if not asked to */
- if (rt->dest.s_addr == 0 &&
- rt->net.s_addr == 0 &&
- !(options->options & DHCPCD_GATEWAY))
- continue;
+ return rt;
+}
- addr = xstrdup(inet_ntoa(rt->dest));
- logger(LOG_DEBUG, "adding route to %s/%d via %s",
- addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
- free(addr);
- remember = add_route(iface, &rt->dest,
- &rt->net, &rt->gate,
- options->metric);
- retval += remember;
-
- /* If we failed to add the route, we may have already added it
- ourselves. If so, remember it again. */
- if (remember < 0) {
- if (errno != EEXIST)
- logger(LOG_ERR, "add_route: %s",
- strerror(errno));
- if (in_routes(iface->routes, rt) == 0)
- remember = 1;
- }
+void
+build_routes(void)
+{
+ struct rt *nrs = NULL, *dnr, *or, *rt, *rtn, *rtl, *lrt = NULL;
+ const struct interface *ifp;
- /* This login is split from above due to the #ifdef below */
- if (remember >= 0) {
- if (nr) {
- rtn->next = xmalloc(sizeof(*rtn));
- rtn = rtn->next;
+ for (ifp = ifaces; ifp; ifp = ifp->next) {
+ if (ifp->state->new == NULL)
+ continue;
+ dnr = get_routes(ifp);
+ dnr = massage_host_routes(dnr, ifp);
+ dnr = add_subnet_route(dnr, ifp);
+ dnr = add_router_host_route(dnr, ifp);
+ dnr = add_destination_route(dnr, ifp);
+ for (rt = dnr; rt && (rtn = rt->next, 1); lrt = rt, rt = rtn) {
+ rt->iface = ifp;
+ /* Is this route already in our table? */
+ if ((find_route(nrs, rt, NULL, NULL)) != NULL)
+ continue;
+ /* Do we already manage it? */
+ if ((or = find_route(routes, rt, &rtl, NULL))) {
+ if (or->iface != ifp ||
+ rt->gate.s_addr != or->gate.s_addr)
+ {
+ if (c_route(or, rt, ifp) != 0)
+ continue;
+ }
+ if (rtl != NULL)
+ rtl->next = or->next;
+ else
+ routes = or->next;
+ free(or);
} else {
- nr = rtn = xmalloc(sizeof(*rtn));
+ if (n_route(rt, ifp) != 0)
+ continue;
}
- rtn->dest.s_addr = rt->dest.s_addr;
- rtn->net.s_addr = rt->net.s_addr;
- rtn->gate.s_addr = rt->gate.s_addr;
- rtn->next = NULL;
+ if (dnr == rt)
+ dnr = rtn;
+ else if (lrt)
+ lrt->next = rtn;
+ rt->next = nrs;
+ nrs = rt;
}
+ free_routes(dnr);
}
- free_routes(ort);
- free_routes(iface->routes);
- iface->routes = nr;
- return retval;
+
+ /* Remove old routes we used to manage */
+ for (rt = routes; rt; rt = rt->next) {
+ if (find_route(nrs, rt, NULL, NULL) == NULL)
+ d_route(rt, rt->iface, rt->iface->metric);
+ }
+
+ free_routes(routes);
+ routes = nrs;
}
static int
delete_address(struct interface *iface)
{
int retval;
- logger(LOG_DEBUG, "deleting IP address %s/%d",
- inet_ntoa(iface->addr),
- inet_ntocidr(iface->net));
- retval = del_address(iface->name, &iface->addr, &iface->net);
+ struct if_options *ifo;
+
+ ifo = iface->state->options;
+ if (ifo->options & DHCPCD_INFORM ||
+ (ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0))
+ return 0;
+ syslog(LOG_DEBUG, "%s: deleting IP address %s/%d",
+ iface->name,
+ inet_ntoa(iface->addr),
+ inet_ntocidr(iface->net));
+ retval = del_address(iface, &iface->addr, &iface->net);
if (retval == -1 && errno != EADDRNOTAVAIL)
- logger(LOG_ERR, "del_address: %s", strerror(errno));
+ syslog(LOG_ERR, "del_address: %m");
iface->addr.s_addr = 0;
iface->net.s_addr = 0;
return retval;
}
int
-configure(struct interface *iface, const char *reason,
- const struct dhcp_message *dhcp, const struct dhcp_message *old,
- const struct dhcp_lease *lease, const struct options *options,
- int up)
+configure(struct interface *iface)
{
- struct in_addr addr;
- struct in_addr net;
- struct in_addr brd;
-#ifdef __linux__
- struct in_addr dest;
- struct in_addr gate;
-#endif
+ struct dhcp_message *dhcp = iface->state->new;
+ struct dhcp_lease *lease = &iface->state->lease;
+ struct if_options *ifo = iface->state->options;
+ struct rt *rt;
- /* Grab our IP config */
- if (dhcp == NULL)
- up = 0;
- else {
- addr.s_addr = dhcp->yiaddr;
- if (addr.s_addr == 0)
- addr.s_addr = lease->addr.s_addr;
- /* Ensure we have all the needed values */
- if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1)
- net.s_addr = get_netmask(addr.s_addr);
- if (get_option_addr(&brd, dhcp, DHO_BROADCAST) == -1)
- brd.s_addr = addr.s_addr | ~net.s_addr;
- }
+ /* As we are now adjusting an interface, we need to ensure
+ * we have them in the right order for routing and configuration. */
+ sort_interfaces();
- /* If we aren't up, then reset the interface as much as we can */
- if (!up) {
- /* Only reset things if we had set them before */
- if (iface->addr.s_addr != 0) {
- delete_routes(iface, options->metric);
- delete_address(iface);
+ if (dhcp == NULL) {
+ if (!(ifo->options & DHCPCD_PERSISTENT)) {
+ build_routes();
+ if (iface->addr.s_addr != 0)
+ delete_address(iface);
+ run_script(iface);
}
-
- run_script(options, iface->name, reason, NULL, old);
return 0;
}
/* This also changes netmask */
- if (!(options->options & DHCPCD_INFORM) ||
- !has_address(iface->name, &addr, &net)) {
- logger(LOG_DEBUG, "adding IP address %s/%d",
- inet_ntoa(addr), inet_ntocidr(net));
- if (add_address(iface->name, &addr, &net, &brd) == -1 &&
+ if (!(ifo->options & DHCPCD_INFORM) ||
+ !has_address(iface->name, &lease->addr, &lease->net))
+ {
+ syslog(LOG_DEBUG, "%s: adding IP address %s/%d",
+ iface->name, inet_ntoa(lease->addr),
+ inet_ntocidr(lease->net));
+ if (add_address(iface,
+ &lease->addr, &lease->net, &lease->brd) == -1 &&
errno != EEXIST)
{
- logger(LOG_ERR, "add_address: %s", strerror(errno));
+ syslog(LOG_ERR, "add_address: %m");
return -1;
}
}
/* Now delete the old address if different */
- if (iface->addr.s_addr != addr.s_addr &&
+ if (iface->addr.s_addr != lease->addr.s_addr &&
iface->addr.s_addr != 0)
delete_address(iface);
-#ifdef __linux__
- /* On linux, we need to change the subnet route to have our metric. */
- if (iface->addr.s_addr != lease->addr.s_addr &&
- options->metric > 0 && net.s_addr != INADDR_BROADCAST)
- {
- dest.s_addr = addr.s_addr & net.s_addr;
- gate.s_addr = 0;
- add_route(iface, &dest, &net, &gate, options->metric);
- del_route(iface, &dest, &net, &gate, 0);
+ iface->addr.s_addr = lease->addr.s_addr;
+ iface->net.s_addr = lease->net.s_addr;
+
+ /* We need to delete the subnet route to have our metric or
+ * prefer the interface. */
+ rt = get_subnet_route(dhcp);
+ if (rt != NULL) {
+ rt->iface = iface;
+ if (!find_route(routes, rt, NULL, NULL))
+ del_route(iface, &rt->dest, &rt->net, &rt->gate, 0);
+ free(rt);
}
-#endif
- iface->addr.s_addr = addr.s_addr;
- iface->net.s_addr = net.s_addr;
- configure_routes(iface, dhcp, options);
- if (!lease->frominfo)
+ build_routes();
+ if (!iface->state->lease.frominfo &&
+ !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
if (write_lease(iface, dhcp) == -1)
- logger(LOG_ERR, "write_lease: %s", strerror(errno));
- run_script(options, iface->name, reason, dhcp, old);
+ syslog(LOG_ERR, "write_lease: %m");
+ run_script(iface);
return 0;
}
diff --git a/configure.h b/configure.h
index fe065db..17c506e 100644
--- a/configure.h
+++ b/configure.h
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -28,14 +28,11 @@
#ifndef DHCPCONFIG_H
#define DHCPCONFIG_H
-#include "dhcpcd.h"
-#include "dhcp.h"
#include "net.h"
-int run_script(const struct options *, const char *, const char *,
- const struct dhcp_message *, const struct dhcp_message *);
-int configure(struct interface *, const char *,
- const struct dhcp_message *, const struct dhcp_message *,
- const struct dhcp_lease *, const struct options *, int);
-
+int send_interface(int, const struct interface *);
+int run_script(const struct interface *);
+void build_routes(void);
+int configure(struct interface *);
+int route_deleted(const struct rt *);
#endif
diff --git a/control.c b/control.c
new file mode 100644
index 0000000..24fb354
--- /dev/null
+++ b/control.c
@@ -0,0 +1,208 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <linux/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcpcd.h"
+#include "control.h"
+#include "eloop.h"
+
+static int fd = -1;
+static char buffer[1024];
+static char *argvp[255];
+
+struct sockaddr_un sun;
+struct fd_list *fds = NULL;
+
+static void
+remove_control_data(void *arg)
+{
+ struct fd_list *l, *last = NULL;
+
+ for (l = fds; l != NULL; l = l->next) {
+ if (l == arg) {
+ close(l->fd);
+ delete_event(l->fd);
+ if (last == NULL)
+ fds = l->next;
+ else
+ last->next = l->next;
+ free(l);
+ break;
+ }
+ last = l;
+ }
+}
+
+static void
+handle_control_data(void *arg)
+{
+ struct fd_list *l = arg;
+ ssize_t bytes;
+ int argc;
+ char *e, *p;
+ char **ap;
+
+ bytes = read(l->fd, buffer, sizeof(buffer) - 1);
+ if (bytes == -1 || bytes == 0) {
+ remove_control_data(l);
+ return;
+ }
+ buffer[bytes] = '\0';
+ p = buffer;
+ e = buffer + bytes;
+ argc = 0;
+ ap = argvp;
+ while (p < e && (size_t)argc < sizeof(argvp)) {
+ argc++;
+ *ap++ = p;
+ p += strlen(p) + 1;
+ }
+ handle_args(l, argc, argvp);
+}
+
+/* ARGSUSED */
+static void
+handle_control(_unused void *arg)
+{
+ struct sockaddr_un run;
+ socklen_t len;
+ struct fd_list *l;
+ int f;
+
+ len = sizeof(run);
+ if ((f = accept(fd, (struct sockaddr *)&run, &len)) == -1)
+ return;
+ l = xmalloc(sizeof(*l));
+ l->fd = f;
+ l->listener = 0;
+ l->next = fds;
+ fds = l;
+ add_event(l->fd, handle_control_data, l);
+}
+
+static int
+make_sock(void)
+{
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ return -1;
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, CONTROLSOCKET, sizeof(sun.sun_path));
+ return sizeof(sun.sun_family) + strlen(sun.sun_path) + 1;
+}
+
+int
+start_control(void)
+{
+ int len;
+
+ if ((len = make_sock()) == -1)
+ return -1;
+ unlink(CONTROLSOCKET);
+ if (bind(fd, (struct sockaddr *)&sun, len) == -1 ||
+ chmod(CONTROLSOCKET,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1 ||
+ set_cloexec(fd) == -1 ||
+ set_nonblock(fd) == -1 ||
+ listen(fd, sizeof(fds)) == -1)
+ {
+ close(fd);
+ return -1;
+ }
+ add_event(fd, handle_control, NULL);
+ return fd;
+}
+
+int
+stop_control(void)
+{
+ int retval = 0;
+ struct fd_list *l, *ll;
+
+ delete_event(fd);
+ if (shutdown(fd, SHUT_RDWR) == -1)
+ retval = 1;
+ fd = -1;
+ if (unlink(CONTROLSOCKET) == -1)
+ retval = -1;
+
+ l = fds;
+ while (l != NULL) {
+ ll = l->next;
+ delete_event(l->fd);
+ shutdown(l->fd, SHUT_RDWR);
+ free(l);
+ l = ll;
+ }
+
+ return retval;
+}
+
+int
+open_control(void)
+{
+ int len;
+
+ if ((len = make_sock()) == -1)
+ return -1;
+ return connect(fd, (struct sockaddr *)&sun, len);
+}
+
+int
+send_control(int argc, char * const *argv)
+{
+ char *p = buffer;
+ int i;
+ size_t len;
+
+ if (argc > 255) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ for (i = 0; i < argc; i++) {
+ len = strlen(argv[i]) + 1;
+ if ((p - buffer) + len > sizeof(buffer)) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(p, argv[i], len);
+ p += len;
+ }
+ return write(fd, buffer, p - buffer);
+}
diff --git a/control.h b/control.h
new file mode 100644
index 0000000..f0faa40
--- /dev/null
+++ b/control.h
@@ -0,0 +1,45 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef CONTROL_H
+#define CONTROL_H
+
+#include "dhcpcd.h"
+
+struct fd_list {
+ int fd;
+ int listener;
+ struct fd_list *next;
+};
+extern struct fd_list *fds;
+
+int start_control(void);
+int stop_control(void);
+int open_control(void);
+int send_control(int, char * const *);
+
+#endif
diff --git a/defs.h b/defs.h
new file mode 100644
index 0000000..81cf213
--- /dev/null
+++ b/defs.h
@@ -0,0 +1,52 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define PACKAGE "dhcpcd"
+#define VERSION "5.2.10"
+
+#ifndef CONFIG
+# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"
+#endif
+#ifndef SCRIPT
+# define SCRIPT LIBEXECDIR "/" PACKAGE "-run-hooks"
+#endif
+#ifndef DUID
+# define DUID SYSCONFDIR "/" PACKAGE ".duid"
+#endif
+#ifndef LEASEFILE
+# define LEASEFILE DBDIR "/" PACKAGE "-%s.lease"
+#endif
+#ifndef PIDFILE
+# define PIDFILE RUNDIR "/" PACKAGE "%s%s.pid"
+#endif
+#ifndef CONTROLSOCKET
+# define CONTROLSOCKET RUNDIR "/" PACKAGE ".sock"
+#endif
+
+#endif
diff --git a/dhcp.c b/dhcp.c
index 244f5f2..0642ba1 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -30,6 +30,7 @@
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
#include <unistd.h>
#include "config.h"
@@ -57,7 +58,7 @@
/* Our aggregate option buffer.
* We ONLY use this when options are split, which for most purposes is
* practically never. See RFC3396 for details. */
-static uint8_t *dhcp_opt_buffer = NULL;
+static uint8_t *opt_buffer;
struct dhcp_opt {
uint8_t option;
@@ -67,9 +68,10 @@ struct dhcp_opt {
static const struct dhcp_opt const dhcp_opts[] = {
{ 1, IPV4 | REQUEST, "subnet_mask" },
- /* RFC 3442 states that the CSR has to come before all other routes.
- * For completeness, we also specify static routes, then routers. */
- { 121, RFC3442 | REQUEST, "classless_static_routes" },
+ /* RFC 3442 states that the CSR has to come before all other
+ * routes. For completeness, we also specify static routes,
+ * then routers. */
+ { 121, RFC3442, "classless_static_routes" },
{ 249, RFC3442, "ms_classless_static_routes" },
{ 33, IPV4 | ARRAY | REQUEST, "static_routes" },
{ 3, IPV4 | ARRAY | REQUEST, "routers" },
@@ -160,22 +162,52 @@ static const struct dhcp_opt const dhcp_opts[] = {
{ 0, 0, NULL }
};
+static const char *if_params[] = {
+ "interface",
+ "reason",
+ "pid",
+ "ifmetric",
+ "ifwireless",
+ "ifflags",
+ "profile",
+ "interface_order",
+ NULL
+};
+
+static const char *dhcp_params[] = {
+ "ip_address",
+ "subnet_cidr",
+ "network_number",
+ "ssid",
+ "filename",
+ "server_name",
+ NULL
+};
+
void
print_options(void)
{
const struct dhcp_opt *opt;
+ const char **p;
+
+ for (p = if_params; *p; p++)
+ printf(" - %s\n", *p);
+
+ for (p = dhcp_params; *p; p++)
+ printf(" %s\n", *p);
for (opt = dhcp_opts; opt->option; opt++)
if (opt->var)
printf("%03d %s\n", opt->option, opt->var);
}
-int make_option_mask(uint8_t *mask, char **opts, int add)
+int make_option_mask(uint8_t *mask, const char *opts, int add)
{
- char *token, *p = *opts, *t;
+ char *token, *o, *p, *t;
const struct dhcp_opt *opt;
int match, n;
+ o = p = xstrdup(opts);
while ((token = strsep(&p, ", "))) {
if (*token == '\0')
continue;
@@ -192,22 +224,28 @@ int make_option_mask(uint8_t *mask, char **opts, int add)
if (opt->option == n)
match = 1;
}
- if (match) {
- if (add == 1)
+ if (match) {
+ if (add == 2 && !(opt->type & IPV4)) {
+ free(o);
+ errno = EINVAL;
+ return -1;
+ }
+ if (add == 1 || add == 2)
add_option_mask(mask,
- opt->option);
+ opt->option);
else
del_option_mask(mask,
- opt->option);
+ opt->option);
break;
}
}
if (!opt->option) {
- *opts = token;
+ free(o);
errno = ENOENT;
return -1;
}
}
+ free(o);
return 0;
}
@@ -227,7 +265,9 @@ valid_length(uint8_t option, int dl, int *type)
if (type)
*type = opt->type;
- if (opt->type == 0 || opt->type & STRING || opt->type & RFC3442)
+ if (opt->type == 0 ||
+ opt->type & STRING ||
+ opt->type & RFC3442)
return 0;
sz = 0;
@@ -246,11 +286,13 @@ valid_length(uint8_t option, int dl, int *type)
return 0;
}
+#ifdef DEBUG_MEMORY
static void
free_option_buffer(void)
{
- free(dhcp_opt_buffer);
+ free(opt_buffer);
}
+#endif
#define get_option_raw(dhcp, opt) get_option(dhcp, opt, NULL, NULL)
static const uint8_t *
@@ -269,12 +311,14 @@ get_option(const struct dhcp_message *dhcp, uint8_t opt, int *len, int *type)
o = *p++;
if (o == opt) {
if (op) {
- if (!dhcp_opt_buffer) {
- dhcp_opt_buffer = xmalloc(sizeof(struct dhcp_message));
+ if (!opt_buffer) {
+ opt_buffer = xmalloc(sizeof(*dhcp));
+#ifdef DEBUG_MEMORY
atexit(free_option_buffer);
+#endif
}
if (!bp)
- bp = dhcp_opt_buffer;
+ bp = opt_buffer;
memcpy(bp, op, ol);
bp += ol;
}
@@ -318,7 +362,7 @@ exit:
*len = bl;
if (bp) {
memcpy(bp, op, ol);
- return (const uint8_t *)dhcp_opt_buffer;
+ return (const uint8_t *)opt_buffer;
}
if (op)
return op;
@@ -371,12 +415,13 @@ get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option)
if (!p)
return -1;
- *i = *(p);
+ if (i)
+ *i = *(p);
return 0;
}
/* Decode an RFC3397 DNS search order option into a space
- * seperated string. Returns length of string (including
+ * separated string. Returns length of string (including
* terminating zero) or zero on error. out may be NULL
* to just determine output length. */
static ssize_t
@@ -442,10 +487,8 @@ static ssize_t
decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p)
{
const uint8_t *e;
- ssize_t bytes = 0;
- ssize_t b;
+ ssize_t b, bytes = 0, ocets;
uint8_t cidr;
- uint8_t ocets;
struct in_addr addr;
char *o = out;
@@ -479,7 +522,7 @@ decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p)
/* If we have ocets then we have a destination and netmask */
if (ocets > 0) {
addr.s_addr = 0;
- memcpy(&addr.s_addr, p, (size_t)ocets);
+ memcpy(&addr.s_addr, p, ocets);
b = snprintf(o, len, "%s/%d", inet_ntoa(addr), cidr);
p += ocets;
} else
@@ -506,7 +549,7 @@ decode_rfc3442_rt(int dl, const uint8_t *data)
const uint8_t *p = data;
const uint8_t *e;
uint8_t cidr;
- uint8_t ocets;
+ size_t ocets;
struct rt *routes = NULL;
struct rt *rt = NULL;
@@ -534,15 +577,9 @@ decode_rfc3442_rt(int dl, const uint8_t *data)
ocets = (cidr + 7) / 8;
/* If we have ocets then we have a destination and netmask */
if (ocets > 0) {
- memcpy(&rt->dest.s_addr, p, (size_t)ocets);
- memset(&rt->net.s_addr, 255, (size_t)ocets - 1);
- memset((uint8_t *)&rt->net.s_addr +
- (ocets - 1),
- (256 - (1 << (32 - cidr) % 8)), 1);
+ memcpy(&rt->dest.s_addr, p, ocets);
p += ocets;
- } else {
- rt->dest.s_addr = 0;
- rt->net.s_addr = 0;
+ rt->net.s_addr = htonl(~0U << (32 - cidr));
}
/* Finally, snag the router */
@@ -664,7 +701,8 @@ route_netmask(uint32_t ip_in)
* If we have a CSR then we only use that.
* Otherwise we add static routes and then routers. */
struct rt *
-get_option_routes(const struct dhcp_message *dhcp)
+get_option_routes(const struct dhcp_message *dhcp,
+ const char *ifname, int *opts)
{
const uint8_t *p;
const uint8_t *e;
@@ -679,8 +717,13 @@ get_option_routes(const struct dhcp_message *dhcp)
p = get_option(dhcp, DHO_MSCSR, &len, NULL);
if (p) {
routes = decode_rfc3442_rt(len, p);
- if (routes)
+ if (routes && !(*opts & DHCPCD_CSR_WARNED)) {
+ syslog(LOG_DEBUG,
+ "%s: using Classless Static Routes (RFC3442)",
+ ifname);
+ *opts |= DHCPCD_CSR_WARNED;
return routes;
+ }
}
/* OK, get our static routes first. */
@@ -747,17 +790,42 @@ encode_rfc1035(const char *src, uint8_t *dst)
return p - dst;
}
-#define PUTADDR(_type, _val) \
-{ \
- *p++ = _type; \
- *p++ = 4; \
- memcpy(p, &_val.s_addr, 4); \
- p += 4; \
+#define PUTADDR(_type, _val) \
+ { \
+ *p++ = _type; \
+ *p++ = 4; \
+ memcpy(p, &_val.s_addr, 4); \
+ p += 4; \
+ }
+
+int
+dhcp_message_add_addr(struct dhcp_message *dhcp,
+ uint8_t type, struct in_addr addr)
+{
+ uint8_t *p;
+ size_t len;
+
+ p = dhcp->options;
+ while (*p != DHO_END) {
+ p++;
+ p += *p + 1;
+ }
+
+ len = p - (uint8_t *)dhcp;
+ if (len + 6 > sizeof(*dhcp)) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ PUTADDR(type, addr);
+ *p = DHO_END;
+ return 0;
}
+
ssize_t
make_message(struct dhcp_message **message,
- const struct interface *iface, const struct dhcp_lease *lease,
- uint32_t xid, uint8_t type, const struct options *options)
+ const struct interface *iface,
+ uint8_t type)
{
struct dhcp_message *dhcp;
uint8_t *m, *lp, *p;
@@ -765,27 +833,26 @@ make_message(struct dhcp_message **message,
time_t up = uptime() - iface->start_uptime;
uint32_t ul;
uint16_t sz;
- const struct dhcp_opt *opt;
size_t len;
const char *hp;
+ const struct dhcp_opt *opt;
+ const struct if_options *ifo = iface->state->options;
+ const struct dhcp_lease *lease = &iface->state->lease;
dhcp = xzalloc(sizeof (*dhcp));
m = (uint8_t *)dhcp;
p = dhcp->options;
- if ((type == DHCP_INFORM ||
- type == DHCP_RELEASE ||
- type == DHCP_REQUEST) &&
- !IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
+ if ((type == DHCP_INFORM || type == DHCP_RELEASE ||
+ (type == DHCP_REQUEST &&
+ iface->net.s_addr == lease->net.s_addr &&
+ (iface->state->new == NULL ||
+ iface->state->new->cookie == htonl(MAGIC_COOKIE)))))
{
dhcp->ciaddr = iface->addr.s_addr;
- /* Just incase we haven't actually configured the address yet */
+ /* In-case we haven't actually configured the address yet */
if (type == DHCP_INFORM && iface->addr.s_addr == 0)
dhcp->ciaddr = lease->addr.s_addr;
- /* Zero the address if we're currently on a different subnet */
- if (type == DHCP_REQUEST &&
- iface->net.s_addr != lease->net.s_addr)
- dhcp->ciaddr = 0;
}
dhcp->op = DHCP_BOOTREQUEST;
@@ -793,25 +860,24 @@ make_message(struct dhcp_message **message,
switch (iface->family) {
case ARPHRD_ETHER:
case ARPHRD_IEEE802:
- dhcp->hwlen = ETHER_ADDR_LEN;
- memcpy(&dhcp->chaddr, &iface->hwaddr, ETHER_ADDR_LEN);
- break;
- case ARPHRD_IEEE1394:
- case ARPHRD_INFINIBAND:
- dhcp->hwlen = 0;
- if (dhcp->ciaddr == 0 &&
- type != DHCP_DECLINE && type != DHCP_RELEASE)
- dhcp->flags = htons(BROADCAST_FLAG);
+ dhcp->hwlen = iface->hwlen;
+ memcpy(&dhcp->chaddr, &iface->hwaddr, iface->hwlen);
break;
}
+ if (ifo->options & DHCPCD_BROADCAST &&
+ dhcp->ciaddr == 0 &&
+ type != DHCP_DECLINE &&
+ type != DHCP_RELEASE)
+ dhcp->flags = htons(BROADCAST_FLAG);
+
if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
if (up < 0 || up > (time_t)UINT16_MAX)
dhcp->secs = htons((uint16_t)UINT16_MAX);
else
dhcp->secs = htons(up);
}
- dhcp->xid = xid;
+ dhcp->xid = iface->state->xid;
dhcp->cookie = htonl(MAGIC_COOKIE);
*p++ = DHO_MESSAGETYPE;
@@ -824,16 +890,20 @@ make_message(struct dhcp_message **message,
p += iface->clientid[0] + 1;
}
- if (lease->addr.s_addr && !IN_LINKLOCAL(htonl(lease->addr.s_addr))) {
+ if (lease->addr.s_addr && lease->cookie == htonl(MAGIC_COOKIE)) {
if (type == DHCP_DECLINE ||
- type == DHCP_DISCOVER ||
(type == DHCP_REQUEST &&
- lease->addr.s_addr != iface->addr.s_addr))
+ lease->addr.s_addr != iface->addr.s_addr))
{
PUTADDR(DHO_IPADDRESS, lease->addr);
if (lease->server.s_addr)
PUTADDR(DHO_SERVERID, lease->server);
}
+
+ if (type == DHCP_RELEASE) {
+ if (lease->server.s_addr)
+ PUTADDR(DHO_SERVERID, lease->server);
+ }
}
if (type == DHCP_DECLINE) {
@@ -844,10 +914,8 @@ make_message(struct dhcp_message **message,
p += len;
}
- if (type == DHCP_RELEASE) {
- if (lease->server.s_addr)
- PUTADDR(DHO_SERVERID, lease->server);
- }
+ if (type == DHCP_DISCOVER && ifo->options & DHCPCD_REQUEST)
+ PUTADDR(DHO_IPADDRESS, ifo->req_addr);
if (type == DHCP_DISCOVER ||
type == DHCP_INFORM ||
@@ -859,29 +927,35 @@ make_message(struct dhcp_message **message,
if (sz < MTU_MIN) {
if (set_mtu(iface->name, MTU_MIN) == 0)
sz = MTU_MIN;
+ } else if (sz > MTU_MAX) {
+ /* Even though our MTU could be greater than
+ * MTU_MAX (1500) dhcpcd does not presently
+ * handle DHCP packets any bigger. */
+ sz = MTU_MAX;
}
sz = htons(sz);
memcpy(p, &sz, 2);
p += 2;
- if (options->userclass[0]) {
+ if (ifo->userclass[0]) {
*p++ = DHO_USERCLASS;
- memcpy(p, options->userclass, options->userclass[0] + 1);
- p += options->userclass[0] + 1;
+ memcpy(p, ifo->userclass, ifo->userclass[0] + 1);
+ p += ifo->userclass[0] + 1;
}
- if (options->vendorclassid[0]) {
+ if (ifo->vendorclassid[0]) {
*p++ = DHO_VENDORCLASSID;
- memcpy(p, options->vendorclassid,
- options->vendorclassid[0] + 1);
- p += options->vendorclassid[0] + 1;
+ memcpy(p, ifo->vendorclassid,
+ ifo->vendorclassid[0] + 1);
+ p += ifo->vendorclassid[0] + 1;
}
+
if (type != DHCP_INFORM) {
- if (options->leasetime != 0) {
+ if (ifo->leasetime != 0) {
*p++ = DHO_LEASETIME;
*p++ = 4;
- ul = htonl(options->leasetime);
+ ul = htonl(ifo->leasetime);
memcpy(p, &ul, 4);
p += 4;
}
@@ -891,18 +965,18 @@ make_message(struct dhcp_message **message,
* upto the first dot (the short hostname) as otherwise
* confuses some DHCP servers when updating DNS.
* The FQDN option should be used if a FQDN is required. */
- if (options->hostname[0]) {
+ if (ifo->options & DHCPCD_HOSTNAME && ifo->hostname[0]) {
*p++ = DHO_HOSTNAME;
- hp = strchr(options->hostname, '.');
+ hp = strchr(ifo->hostname, '.');
if (hp)
- len = hp - options->hostname;
+ len = hp - ifo->hostname;
else
- len = strlen(options->hostname);
+ len = strlen(ifo->hostname);
*p++ = len;
- memcpy(p, options->hostname, len);
+ memcpy(p, ifo->hostname, len);
p += len;
}
- if (options->fqdn != FQDN_DISABLE) {
+ if (ifo->fqdn != FQDN_DISABLE && ifo->hostname[0]) {
/* IETF DHC-FQDN option (81), RFC4702 */
*p++ = DHO_FQDN;
lp = p;
@@ -917,19 +991,19 @@ make_message(struct dhcp_message **message,
* N: 1 => Client requests Server to not
* update DNS
*/
- *p++ = (options->fqdn & 0x09) | 0x04;
+ *p++ = (ifo->fqdn & 0x09) | 0x04;
*p++ = 0; /* from server for PTR RR */
*p++ = 0; /* from server for A RR if S=1 */
- ul = encode_rfc1035(options->hostname, p);
+ ul = encode_rfc1035(ifo->hostname, p);
*lp += ul;
p += ul;
}
/* vendor is already encoded correctly, so just add it */
- if (options->vendor[0]) {
+ if (ifo->vendor[0]) {
*p++ = DHO_VENDOR;
- memcpy(p, options->vendor, options->vendor[0] + 1);
- p += options->vendor[0] + 1;
+ memcpy(p, ifo->vendor, ifo->vendor[0] + 1);
+ p += ifo->vendor[0] + 1;
}
*p++ = DHO_PARAMETERREQUESTLIST;
@@ -937,15 +1011,12 @@ make_message(struct dhcp_message **message,
*p++ = 0;
for (opt = dhcp_opts; opt->option; opt++) {
if (!(opt->type & REQUEST ||
- has_option_mask(options->requestmask, opt->option)))
+ has_option_mask(ifo->requestmask, opt->option)))
+ continue;
+ if (type == DHCP_INFORM &&
+ (opt->option == DHO_RENEWALTIME ||
+ opt->option == DHO_REBINDTIME))
continue;
- switch (opt->option) {
- case DHO_RENEWALTIME: /* FALLTHROUGH */
- case DHO_REBINDTIME:
- if (type == DHCP_INFORM)
- continue;
- break;
- }
*p++ = opt->option;
}
*n_params = p - n_params - 1;
@@ -974,16 +1045,27 @@ write_lease(const struct interface *iface, const struct dhcp_message *dhcp)
uint8_t l;
uint8_t o = 0;
- fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
+ /* We don't write BOOTP leases */
+ if (is_bootp(dhcp)) {
+ unlink(iface->leasefile);
+ return 0;
+ }
+
+ syslog(LOG_DEBUG, "%s: writing lease `%s'",
+ iface->name, iface->leasefile);
+
+ fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0444);
#ifdef ANDROID
if (fd == -1 && errno == EACCES) {
/* the lease file might have been created when dhcpcd was running as root */
unlink(iface->leasefile);
- fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
+ fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0444);
}
#endif
- if (fd == -1)
+ if (fd == -1) {
+ syslog(LOG_ERR, "%s: open: %m", iface->name);
return -1;
+ }
/* Only write as much as we need */
while (p < e) {
@@ -1011,8 +1093,14 @@ read_lease(const struct interface *iface)
ssize_t bytes;
fd = open(iface->leasefile, O_RDONLY);
- if (fd == -1)
+ if (fd == -1) {
+ if (errno != ENOENT)
+ syslog(LOG_ERR, "%s: open `%s': %m",
+ iface->name, iface->leasefile);
return NULL;
+ }
+ syslog(LOG_DEBUG, "%s: reading lease `%s'",
+ iface->name, iface->leasefile);
dhcp = xmalloc(sizeof(*dhcp));
memset(dhcp, 0, sizeof(*dhcp));
bytes = read(fd, dhcp, sizeof(*dhcp));
@@ -1058,21 +1146,21 @@ print_string(char *s, ssize_t len, int dl, const uint8_t *data)
continue;
}
switch (c) {
- case '"': /* FALLTHROUGH */
- case '\'': /* FALLTHROUGH */
- case '$': /* FALLTHROUGH */
- case '`': /* FALLTHROUGH */
- case '\\': /* FALLTHROUGH */
- if (s) {
- if (len < 3) {
- errno = ENOBUFS;
- return -1;
- }
- *s++ = '\\';
- len--;
+ case '"': /* FALLTHROUGH */
+ case '\'': /* FALLTHROUGH */
+ case '$': /* FALLTHROUGH */
+ case '`': /* FALLTHROUGH */
+ case '\\':
+ if (s) {
+ if (len < 3) {
+ errno = ENOBUFS;
+ return -1;
}
- bytes++;
- break;
+ *s++ = '\\';
+ len--;
+ }
+ bytes++;
+ break;
}
if (s) {
*s++ = c;
@@ -1125,17 +1213,22 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
if (!s) {
if (type & UINT8)
l = 3;
- else if (type & UINT16)
+ else if (type & UINT16) {
l = 5;
- else if (type & SINT16)
+ dl /= 2;
+ } else if (type & SINT16) {
l = 6;
- else if (type & UINT32)
+ dl /= 2;
+ } else if (type & UINT32) {
l = 10;
- else if (type & SINT32)
+ dl /= 4;
+ } else if (type & SINT32) {
l = 11;
- else if (type & IPV4)
+ dl /= 4;
+ } else if (type & IPV4) {
l = 16;
- else {
+ dl /= 4;
+ } else {
errno = EINVAL;
return -1;
}
@@ -1199,7 +1292,7 @@ setvar(char ***e, const char *prefix, const char *var, const char *value)
ssize_t
configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
- const struct options *options)
+ const struct if_options *ifo)
{
unsigned int i;
const uint8_t *p;
@@ -1220,12 +1313,12 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
for (opt = dhcp_opts; opt->option; opt++) {
if (!opt->var)
continue;
- if (has_option_mask(options->nomask, opt->option))
+ if (has_option_mask(ifo->nomask, opt->option))
continue;
if (get_option_raw(dhcp, opt->option))
e++;
}
- if (dhcp->yiaddr)
+ if (dhcp->yiaddr || dhcp->ciaddr)
e += 5;
if (*dhcp->bootfile && !(overl & 1))
e++;
@@ -1235,10 +1328,10 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
}
ep = env;
- if (dhcp->yiaddr) {
+ if (dhcp->yiaddr || dhcp->ciaddr) {
/* Set some useful variables that we derive from the DHCP
* message but are not necessarily in the options */
- addr.s_addr = dhcp->yiaddr;
+ addr.s_addr = dhcp->yiaddr ? dhcp->yiaddr : dhcp->ciaddr;
setvar(&ep, prefix, "ip_address", inet_ntoa(addr));
if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1) {
net.s_addr = get_netmask(addr.s_addr);
@@ -1263,7 +1356,7 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
for (opt = dhcp_opts; opt->option; opt++) {
if (!opt->var)
continue;
- if (has_option_mask(options->nomask, opt->option))
+ if (has_option_mask(ifo->nomask, opt->option))
continue;
val = NULL;
p = get_option(dhcp, opt->option, &pl, NULL);
@@ -1286,3 +1379,33 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
return ep - env;
}
+
+void
+get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
+{
+ struct timeval now;
+
+ lease->cookie = dhcp->cookie;
+ /* BOOTP does not set yiaddr for replies when ciaddr is set. */
+ if (dhcp->yiaddr)
+ lease->addr.s_addr = dhcp->yiaddr;
+ else
+ lease->addr.s_addr = dhcp->ciaddr;
+ if (get_option_addr(&lease->net, dhcp, DHO_SUBNETMASK) == -1)
+ lease->net.s_addr = get_netmask(lease->addr.s_addr);
+ if (get_option_addr(&lease->brd, dhcp, DHO_BROADCAST) == -1)
+ lease->brd.s_addr = lease->addr.s_addr | ~lease->net.s_addr;
+ if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) == 0) {
+ /* Ensure that we can use the lease */
+ get_monotonic(&now);
+ if (now.tv_sec + (time_t)lease->leasetime < now.tv_sec)
+ lease->leasetime = ~0U; /* Infinite lease */
+ } else
+ lease->leasetime = ~0U; /* Default to infinite lease */
+ if (get_option_uint32(&lease->renewaltime, dhcp, DHO_RENEWALTIME) != 0)
+ lease->renewaltime = 0;
+ if (get_option_uint32(&lease->rebindtime, dhcp, DHO_REBINDTIME) != 0)
+ lease->rebindtime = 0;
+ if (get_option_addr(&lease->server, dhcp, DHO_SERVERID) != 0)
+ lease->server.s_addr = INADDR_ANY;
+}
diff --git a/dhcp.h b/dhcp.h
index 09e8ecb..cb42275 100644
--- a/dhcp.h
+++ b/dhcp.h
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -29,12 +29,11 @@
#define DHCP_H
#include <arpa/inet.h>
+#include <netinet/in.h>
#include <stdint.h>
-#include "config.h"
-#include "dhcpcd.h"
-#include "net.h"
+#include "common.h"
/* Max MTU - defines dhcp option length */
#define MTU_MAX 1500
@@ -61,9 +60,26 @@
#define DHCP_RELEASE 7
#define DHCP_INFORM 8
+/* Constants taken from RFC 2131. */
+#define T1 0.5
+#define T2 0.875
+#define DHCP_BASE 4
+#define DHCP_MAX 64
+#define DHCP_RAND_MIN -1
+#define DHCP_RAND_MAX 1
+#define DHCP_ARP_FAIL 10
+
+/* number of usecs in a second. */
+#define USECS_SECOND 1000000
+/* As we use timevals, we should use the usec part for
+ * greater randomisation. */
+#define DHCP_RAND_MIN_U DHCP_RAND_MIN * USECS_SECOND
+#define DHCP_RAND_MAX_U DHCP_RAND_MAX * USECS_SECOND
+#define PROBE_MIN_U PROBE_MIN * USECS_SECOND
+#define PROBE_MAX_U PROBE_MAX * USECS_SECOND
+
/* DHCP options */
-enum DHO
-{
+enum DHO {
DHO_PAD = 0,
DHO_SUBNETMASK = 1,
DHO_ROUTER = 3,
@@ -112,13 +128,9 @@ enum FQDN {
#define DHCP_CHADDR_LEN 16
#define SERVERNAME_LEN 64
#define BOOTFILE_LEN 128
-#define DHCP_UDP_LEN (20 + 8)
-#define DHCP_BASE_LEN (4 + 4 + 2 + 2 + 4 + 4 + 4 + 4 + 4)
-#define DHCP_RESERVE_LEN (4 + 4 + 4 + 4 + 2)
-#define DHCP_FIXED_LEN (DHCP_BASE_LEN + DHCP_CHADDR_LEN + \
- + SERVERNAME_LEN + BOOTFILE_LEN)
-#define DHCP_OPTION_LEN (MTU_MAX - DHCP_FIXED_LEN - DHCP_UDP_LEN \
- - DHCP_RESERVE_LEN)
+#define DHCP_UDP_LEN (14 + 20 + 8)
+#define DHCP_FIXED_LEN (DHCP_UDP_LEN + 226)
+#define DHCP_OPTION_LEN (MTU_MAX - DHCP_FIXED_LEN)
/* Some crappy DHCP servers require the BOOTP minimum length */
#define BOOTP_MESSAGE_LENTH_MIN 300
@@ -140,11 +152,12 @@ struct dhcp_message {
uint8_t bootfile[BOOTFILE_LEN]; /* boot file name */
uint32_t cookie;
uint8_t options[DHCP_OPTION_LEN]; /* message options - cookie */
-};
+} _packed;
struct dhcp_lease {
struct in_addr addr;
struct in_addr net;
+ struct in_addr brd;
uint32_t leasetime;
uint32_t renewaltime;
uint32_t rebindtime;
@@ -152,27 +165,37 @@ struct dhcp_lease {
time_t leasedfrom;
struct timeval boundtime;
uint8_t frominfo;
+ uint32_t cookie;
};
+#include "dhcpcd.h"
+#include "if-options.h"
+#include "net.h"
+
#define add_option_mask(var, val) (var[val >> 3] |= 1 << (val & 7))
#define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
#define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7)))
-int make_option_mask(uint8_t *, char **, int);
+int make_option_mask(uint8_t *, const char *, int);
void print_options(void);
char *get_option_string(const struct dhcp_message *, uint8_t);
int get_option_addr(struct in_addr *, const struct dhcp_message *, uint8_t);
int get_option_uint32(uint32_t *, const struct dhcp_message *, uint8_t);
int get_option_uint16(uint16_t *, const struct dhcp_message *, uint8_t);
int get_option_uint8(uint8_t *, const struct dhcp_message *, uint8_t);
-struct rt *get_option_routes(const struct dhcp_message *);
+#define is_bootp(m) (m && \
+ !IN_LINKLOCAL(htonl((m)->yiaddr)) && \
+ get_option_uint8(NULL, m, DHO_MESSAGETYPE) == -1)
+struct rt *get_option_routes(const struct dhcp_message *, const char *, int *);
ssize_t configure_env(char **, const char *, const struct dhcp_message *,
- const struct options *);
+ const struct if_options *);
-ssize_t make_message(struct dhcp_message **,
- const struct interface *, const struct dhcp_lease *,
- uint32_t, uint8_t, const struct options *);
+int dhcp_message_add_addr(struct dhcp_message *, uint8_t, struct in_addr);
+ssize_t make_message(struct dhcp_message **, const struct interface *,
+ uint8_t);
int valid_dhcp_packet(unsigned char *);
ssize_t write_lease(const struct interface *, const struct dhcp_message *);
-struct dhcp_message *read_lease(const struct interface *iface);
+struct dhcp_message *read_lease(const struct interface *);
+void get_lease(struct dhcp_lease *, const struct dhcp_message *);
+
#endif
diff --git a/dhcpcd-hooks/02-dump b/dhcpcd-hooks/02-dump
new file mode 100644
index 0000000..cbb46ea
--- /dev/null
+++ b/dhcpcd-hooks/02-dump
@@ -0,0 +1,5 @@
+# Just echo our DHCP options we have
+
+if [ "$reason" = "DUMP" ]; then
+ set | sed -ne 's/^new_//p' | sort
+fi
diff --git a/dhcpcd-hooks/20-resolv.conf b/dhcpcd-hooks/20-resolv.conf
index e757ddf..628636d 100644
--- a/dhcpcd-hooks/20-resolv.conf
+++ b/dhcpcd-hooks/20-resolv.conf
@@ -6,102 +6,121 @@
# Also, resolvconf can configure local nameservers such as bind
# or dnsmasq. This is important as the libc resolver isn't that powerful.
-resolv_conf_dir="${state_dir}/resolv.conf"
+resolv_conf_dir="$state_dir/resolv.conf"
build_resolv_conf()
{
- local cf="/etc/resolv.conf.${interface}"
+ local cf="$state_dir/resolv.conf.$interface"
local interfaces= header= search= srvs= servers= x=
# Build a list of interfaces
- interfaces=$(list_interfaces "${resolv_conf_dir}")
+ interfaces=$(list_interfaces "$resolv_conf_dir")
# Build the resolv.conf
- if [ -n "${interfaces}" ]; then
+ if [ -n "$interfaces" ]; then
# Build the header
for x in ${interfaces}; do
- header="${header}${header:+, }${x}"
+ header="$header${header:+, }$x"
done
# Build the search list
- search=$(cd "${resolv_conf_dir}"; \
+ domain=$(cd "$resolv_conf_dir"; \
+ key_get_value "domain " ${interfaces})
+ search=$(cd "$resolv_conf_dir"; \
key_get_value "search " ${interfaces})
- [ -n "${search}" ] && search="search $(uniqify ${search})\n"
+ set -- ${domain}
+ unset domain
+ if [ -n "$2" ]; then
+ search="$search $@"
+ elif [ -n "$1" ]; then
+ domain="domain $1\n"
+ fi
+ [ -n "$search" ] && search="search $(uniqify $search)\n"
# Build the nameserver list
- srvs=$(cd "${resolv_conf_dir}"; \
+ srvs=$(cd "$resolv_conf_dir"; \
key_get_value "nameserver " ${interfaces})
for x in $(uniqify ${srvs}); do
- servers="${servers}nameserver ${x}\n"
+ servers="${servers}nameserver $x\n"
done
fi
- header="${signature_base}${header:+ ${from} }${header}"
+ header="$signature_base${header:+ $from }$header"
# Assemble resolv.conf using our head and tail files
- [ -f "${cf}" ] && rm -f "${cf}"
- echo "${header}" > "${cf}"
+ [ -f "$cf" ] && rm -f "$cf"
+ [ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
+ echo "$header" > "$cf"
if [ -f /etc/resolv.conf.head ]; then
- cat /etc/resolv.conf.head >> "${cf}"
+ cat /etc/resolv.conf.head >> "$cf"
else
- echo "# /etc/resolv.conf.head can replace this line" >> "${cf}"
+ echo "# /etc/resolv.conf.head can replace this line" >> "$cf"
fi
- printf "${search}${servers}" >> "${cf}"
+ printf "$domain$search$servers" >> "$cf"
if [ -f /etc/resolv.conf.tail ]; then
- cat /etc/resolv.conf.tail >> "${cf}"
+ cat /etc/resolv.conf.tail >> "$cf"
else
- echo "# /etc/resolv.conf.tail can replace this line" >> "${cf}"
+ echo "# /etc/resolv.conf.tail can replace this line" >> "$cf"
fi
- mv -f "${cf}" /etc/resolv.conf
+ cat "$cf" > /etc/resolv.conf
+ chmod 644 /etc/resolv.conf
+ rm -f "$cf"
}
add_resolv_conf()
{
- local x= conf="${signature}\n"
+ local x= conf="$signature\n"
# If we don't have any configuration, remove it
- if [ -z "${new_domain_name_servers}" -a \
- -z "${new_domain_name}" -a \
- -z "${new_domain_search}" ]; then
+ if [ -z "$new_domain_name_servers" -a \
+ -z "$new_domain_name" -a \
+ -z "$new_domain_search" ]; then
remove_resolv_conf
return $?
fi
- if [ -n "${new_domain_search}" ]; then
- conf="${conf}search ${new_domain_search}\n"
- elif [ -n "${new_domain_name}" ]; then
- conf="${conf}search ${new_domain_name}\n"
+ if [ -n "$new_domain_name" ]; then
+ set -- $new_domain_name
+ new_domain_name="$1"
+ conf="${conf}domain $new_domain_name\n"
+ # Support RFC violating search in domain
+ if [ -z "$new_domain_search" -a -n "$2" ]; then
+ new_domain_search="$@"
+ fi
+ fi
+ if [ -n "$new_domain_search" ]; then
+ conf="${conf}search $new_domain_search\n"
fi
for x in ${new_domain_name_servers}; do
- conf="${conf}nameserver ${x}\n"
+ conf="${conf}nameserver $x\n"
done
if type resolvconf >/dev/null 2>&1; then
- printf "${conf}" | resolvconf -a "${interface}"
+ [ -n "$metric" ] && export IF_METRIC="$metric"
+ printf "$conf" | resolvconf -a "$interface"
return $?
fi
- if [ -e "${resolv_conf_dir}/${interface}" ]; then
- rm -f "${resolv_conf_dir}/${interface}"
- fi
- if [ ! -d "${resolv_conf_dir}" ]; then
- mkdir -p "${resolv_conf_dir}"
+ if [ -e "$resolv_conf_dir/$interface" ]; then
+ rm -f "$resolv_conf_dir/$interface"
fi
- printf "${conf}" > "${resolv_conf_dir}/${interface}"
+ [ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
+ printf "$conf" > "$resolv_conf_dir/$interface"
build_resolv_conf
}
remove_resolv_conf()
{
if type resolvconf >/dev/null 2>&1; then
- resolvconf -d "${interface}" -f
+ resolvconf -d "$interface" -f
else
- if [ -e "${resolv_conf_dir}/${interface}" ]; then
- rm -f "${resolv_conf_dir}/${interface}"
+ if [ -e "$resolv_conf_dir/$interface" ]; then
+ rm -f "$resolv_conf_dir/$interface"
fi
build_resolv_conf
fi
}
-case "${reason}" in
-BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) add_resolv_conf;;
-PREINIT|EXPIRE|FAIL|IPV4LL|RELEASE|STOP) remove_resolv_conf;;
-esac
+if $if_up; then
+ add_resolv_conf
+elif $if_down; then
+ remove_resolv_conf
+fi
diff --git a/dhcpcd-hooks/29-lookup-hostname b/dhcpcd-hooks/29-lookup-hostname
index 3dfade3..8661fcc 100644
--- a/dhcpcd-hooks/29-lookup-hostname
+++ b/dhcpcd-hooks/29-lookup-hostname
@@ -2,19 +2,19 @@
lookup_hostname()
{
- [ -z "${new_ip_address}" ] && return 1
+ [ -z "$new_ip_address" ] && return 1
local h=
# Silly ISC programs love to send error text to stdout
if type dig >/dev/null 2>&1; then
- h=`dig +short -x ${new_ip_address}`
+ h=$(dig +short -x $new_ip_address)
if [ $? = 0 ]; then
- echo "${h}" | sed 's/\.$//'
+ echo "$h" | sed 's/\.$//'
return 0
fi
elif type host >/dev/null 2>&1; then
- h=`host ${new_ip_address}`
+ h=$(host $new_ip_address)
if [ $? = 0 ]; then
- echo "${h}" \
+ echo "$h" \
| sed 's/.* domain name pointer \(.*\)./\1/'
return 0
fi
@@ -24,11 +24,11 @@ lookup_hostname()
set_hostname()
{
- if [ -z "${new_host_name}" -a -z "${new_fqdn_name}" ]; then
+ if [ -z "$new_host_name" -a -z "$new_fqdn_name" ]; then
export new_host_name="$(lookup_hostname)"
fi
}
-case "${reason}" in
-BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) set_hostname;;
-esac
+if $if_up; then
+ set_hostname
+fi
diff --git a/dhcpcd-hooks/30-hostname b/dhcpcd-hooks/30-hostname
index b2e5fc8..87446fb 100644
--- a/dhcpcd-hooks/30-hostname
+++ b/dhcpcd-hooks/30-hostname
@@ -2,10 +2,16 @@
need_hostname()
{
- case "$(hostname)" in
+ local hostname=""
+
+ case "$force_hostname" in
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) ;;
+ *) hostname="$(hostname)";;
+ esac
+ case "$hostname" in
""|"(none)"|localhost|localhost.localdomain)
- [ -n "${new_host_name}" -o -n "${new_fqdn_name}" ];;
- "${old_host_name}"|"${old_fqdn_name}")
+ [ -n "$new_host_name" -o -n "$new_fqdn_name" ];;
+ "$old_host_name"|"$old_fqdn_name")
true;;
*)
false;;
@@ -15,14 +21,14 @@ need_hostname()
set_hostname()
{
if need_hostname; then
- if [ -n "${new_host_name}" ]; then
- hostname "${new_host_name}"
- else
- hostname "${new_fqdn_name}"
+ if [ -n "$new_host_name" ]; then
+ hostname "$new_host_name"
+ elif [ -n "$new_fqdn_name" ]; then
+ hostname "$new_fqdn_name"
fi
fi
}
-case "${reason}" in
-BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) set_hostname;;
-esac
+if $if_up; then
+ set_hostname
+fi
diff --git a/dhcpcd-hooks/50-dhcpcd-compat b/dhcpcd-hooks/50-dhcpcd-compat
index bb31fd3..651bc08 100644
--- a/dhcpcd-hooks/50-dhcpcd-compat
+++ b/dhcpcd-hooks/50-dhcpcd-compat
@@ -1,41 +1,41 @@
# Compat enter hook shim for older dhcpcd versions
-IPADDR=${new_ip_address}
-INTERFACE=${interface}
-NETMASK=${new_subnet_mask}
-BROADCAST=${new_broadcast_address}
-NETWORK=${new_network_number}
-DHCPSID=${new_dhcp_server_identifier}
-GATEWAYS=${new_routers}
-DNSSERVERS=${new_domain_name_servers}
-DNSDOMAIN=${new_domain_name}
-DNSSEARCH=${new_domain_search}
-NISDOMAIN=${new_nis_domain}
-NISSERVERS=${new_nis_servers}
-NTPSERVERS=${new_ntp_servers}
+IPADDR=$new_ip_address
+INTERFACE=$interface
+NETMASK=$new_subnet_mask
+BROADCAST=$new_broadcast_address
+NETWORK=$new_network_number
+DHCPSID=$new_dhcp_server_identifier
+GATEWAYS=$new_routers
+DNSSERVERS=$new_domain_name_servers
+DNSDOMAIN=$new_domain_name
+DNSSEARCH=$new_domain_search
+NISDOMAIN=$new_nis_domain
+NISSERVERS=$new_nis_servers
+NTPSERVERS=$new_ntp_servers
GATEWAY=
-for x in ${new_routers}; do
- GATEWAY="${GATEWAY}${GATEWAY:+,}${x}"
+for x in $new_routers; do
+ GATEWAY="$GATEWAY${GATEWAY:+,}$x"
done
DNS=
-for x in ${new_domain_name_servers}; do
- DNS="${DNS}${DNS:+,}${x}"
+for x in $new_domain_name_servers; do
+ DNS="$DNS${DNS:+,}$x"
done
x="down"
-case "${reason}" in
+case "$reason" in
RENEW) x="up";;
BOUND|INFORM|REBIND|REBOOT|TEST|TIMEOUT|IPV4LL) x="new";;
esac
-if [ "${reason}" != "down" ]; then
- rm -f /var/lib/dhcpcd-"${INTERFACE}".info
+if [ "$reason" != "down" ]; then
+ rm -f /var/lib/dhcpcd-"$INTERFACE".info
for x in IPADDR INTERFACE NETMASK BROADCAST NETWORK DHCPSID GATEWAYS \
DNSSERVERS DNSDOMAIN DNSSEARCH NISDOMAIN NISSERVERS \
NTPSERVERS GATEWAY DNS; do
- eval echo "${x}=\'\$${x}\'" >> /var/lib/dhcpcd-"${INTERFACE}".info
+ eval echo "$x=\'\$$x\'" >> /var/lib/dhcpcd-"$INTERFACE".info
done
fi
-set -- /var/lib/dhcpcd-"${INTERFACE}".info "${x}"
+set -- /var/lib/dhcpcd-"$INTERFACE".info "$x"
diff --git a/dhcpcd-hooks/50-ntp.conf b/dhcpcd-hooks/50-ntp.conf
index 8c92f27..765baa7 100644
--- a/dhcpcd-hooks/50-ntp.conf
+++ b/dhcpcd-hooks/50-ntp.conf
@@ -8,68 +8,76 @@
# NTP_CONF=/usr/pkg/etc/ntpd.conf
# to use openntpd from pkgsrc instead of the system provided ntp.
-# Detect OpenRC or BSD rc
-# Distributions may want to just have their command here instead of this
-if type rc-service >/dev/null 2>&1 && rc-service --exists ntpd; then
- ntpd_restart_cmd="rc-service ntpd -- --ifstarted --quiet restart"
-elif [ -x /etc/rc.d/ntpd ]; then
- ntpd_restart_cmd="/etc/rc.d/ntpd status && /etc/rc.d/ntpd restart"
-elif [ -x /usr/local/etc/rc.d/ntpd ]; then
- ntpd_restart_cmd="/usr/local/etc/rc.d/ntpd status && /usr/local/etc/rc.d/ntpd restart"
+: ${ntpd_restart_cmd:=service_condcommand ntpd restart}
+if type invoke-rc.d >/dev/null 2>&1; then
+ # Debian has a seperate file for DHCP config to avoid stamping on
+ # the master.
+ [ -e /var/lib/ntp ] || mkdir /var/lib/ntp
+ : ${NTP_DHCP_CONF:=/var/lib/ntp/ntp.conf.dhcp}
fi
-ntp_conf_dir="${state_dir}/ntp.conf"
+ntp_conf_dir="$state_dir/ntp.conf"
ntp_conf=${NTP_CONF:-/etc/ntp.conf}
build_ntp_conf()
{
- local cf="${ntp_conf}.${interface}"
+ local cf="$state_dir/ntp.conf.$interface"
local interfaces= header= srvs= servers= x=
# Build a list of interfaces
- interfaces=$(list_interfaces "${ntp_conf_dir}")
+ interfaces=$(list_interfaces "$ntp_conf_dir")
- if [ -n "${interfaces}" ]; then
+ if [ -n "$interfaces" ]; then
# Build the header
for x in ${interfaces}; do
- header="${header}${header:+, }${x}"
+ header="$header${header:+, }$x"
done
# Build a server list
- srvs=$(cd "${ntp_conf_dir}";
- key_get_value "server " ${interfaces})
- if [ -n "${srvs}" ]; then
- for x in $(uniqify ${srvs}); do
- servers="${servers}server ${x}\n"
+ srvs=$(cd "$ntp_conf_dir";
+ key_get_value "server " $interfaces)
+ if [ -n "$srvs" ]; then
+ for x in $(uniqify $srvs); do
+ servers="${servers}server $x\n"
done
fi
fi
# Merge our config into ntp.conf
- [ -e "${cf}" ] && rm -f "${cf}"
- remove_markers "${signature_base}" "${signature_base_end}" \
- /etc/ntp.conf > "${cf}"
- if [ -n "${servers}" ]; then
- echo "${signature_base}${header:+ ${from} }${header}" >> "${cf}"
- printf "${search}${servers}" >> "${cf}"
- echo "${signature_base_end}${header:+ ${from} }${header}" >> "${cf}"
+ [ -e "$cf" ] && rm -f "$cf"
+ [ -d "$ntp_conf_dir" ] || mkdir -p "$ntp_conf_dir"
+
+ if [ -n "$NTP_DHCP_CONF" ]; then
+ cp "$ntp_conf" "$cf"
+ ntp_conf="$NTP_DHCP_CONF"
+ elif [ -e "$ntp_conf" ]; then
+ remove_markers "$signature_base" "$signature_base_end" \
+ "$ntp_conf" > "$cf"
+ fi
+
+ if [ -n "$servers" ]; then
+ echo "$signature_base${header:+ $from }$header" >> "$cf"
+ printf "$search$servers" >> "$cf"
+ echo "$signature_base_end${header:+ $from }$header" >> "$cf"
+ else
+ [ -e "$ntp_conf" ] || return
fi
# If we changed anything, restart ntpd
- if change_file "${ntp_conf}" "${cf}"; then
- [ -n "${ntpd_restart_cmd}" ] && eval ${ntpd_restart_cmd}
+ if change_file "$ntp_conf" "$cf"; then
+ [ -n "$ntpd_restart_cmd" ] && eval $ntpd_restart_cmd
fi
}
add_ntp_conf()
{
- local cf="${ntp_conf_dir}/${interface}" x=
+ local cf="$ntp_conf_dir/$interface" x=
- [ -e "${cf}" ] && rm "${cf}"
- [ -d "${ntp_conf_dir}" ] || mkdir -p "${ntp_conf_dir}"
- if [ -n "${new_ntp_servers}" ]; then
- for x in ${new_ntp_servers}; do
- echo "server ${x}" >> "${cf}"
+ [ -e "$cf" ] && rm "$cf"
+ [ -d "$ntp_conf_dir" ] || mkdir -p "$ntp_conf_dir"
+ if [ -n "$new_ntp_servers" ]; then
+ for x in $new_ntp_servers; do
+ echo "server $x" >> "$cf"
done
fi
build_ntp_conf
@@ -77,13 +85,14 @@ add_ntp_conf()
remove_ntp_conf()
{
- if [ -e "${ntp_conf_dir}/${interface}" ]; then
- rm "${ntp_conf_dir}/${interface}"
+ if [ -e "$ntp_conf_dir/$interface" ]; then
+ rm "$ntp_conf_dir/$interface"
fi
build_ntp_conf
}
-case "${reason}" in
-BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) add_ntp_conf add;;
-PREINIT|EXPIRE|FAIL|IPV4LL|RELEASE|STOP) remove_ntp_conf del;;
-esac
+if $if_up; then
+ add_ntp_conf add
+elif $if_down; then
+ remove_ntp_conf del
+fi
diff --git a/dhcpcd-hooks/50-yp.conf b/dhcpcd-hooks/50-yp.conf
index a2296eb..a1f5798 100644
--- a/dhcpcd-hooks/50-yp.conf
+++ b/dhcpcd-hooks/50-yp.conf
@@ -8,42 +8,44 @@ ypbind_pid()
make_yp_conf()
{
- [ -z "${new_nis_domain}" -a -z "${new_nis_servers}" ] && return 0
- local cf=/etc/yp.conf."${interface}" prefix= x= pid=
- rm -f "${cf}"
- echo "${signature}" > "${cf}"
- if [ -n "${new_nis_domain}" ]; then
- domainname "${new_nis_domain}"
- if [ -n "${new_nis_servers}" ]; then
- prefix="domain ${new_nis_domain} server "
+ [ -z "$new_nis_domain" -a -z "$new_nis_servers" ] && return 0
+ local cf=/etc/yp.conf."$interface" prefix= x= pid=
+ rm -f "$cf"
+ echo "$signature" > "$cf"
+ if [ -n "$new_nis_domain" ]; then
+ domainname "$new_nis_domain"
+ if [ -n "$new_nis_servers" ]; then
+ prefix="domain $new_nis_domain server "
else
- echo "domain ${new_nis_domain} broadcast" >> "${cf}"
+ echo "domain $new_nis_domain broadcast" >> "$cf"
fi
else
prefix="ypserver "
fi
- for x in ${new_nis_servers}; do
- echo "${prefix}${x}" >> "${cf}"
+ for x in $new_nis_servers; do
+ echo "$prefix$x" >> "$cf"
done
save_conf /etc/yp.conf
- mv -f "${cf}" /etc/yp.conf
+ cat "$cf" > /etc/yp.conf
+ rm -f "$cf"
pid="$(ypbind_pid)"
- if [ -n "${pid}" ]; then
- kill -HUP "${pid}"
+ if [ -n "$pid" ]; then
+ kill -HUP "$pid"
fi
}
restore_yp_conf()
{
- [ -n "${old_nis_domain}" ] && domainname ""
+ [ -n "$old_nis_domain" ] && domainname ""
restore_conf /etc/yp.conf || return 0
local pid="$(ypbind_pid)"
- if [ -n "${pid}" ]; then
- kill -HUP "${pid}"
+ if [ -n "$pid" ]; then
+ kill -HUP "$pid"
fi
}
-case "${reason}" in
-BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) make_yp_conf;;
-EXPIRE|FAIL|IPV4LL|RELEASE|STOP) restore_yp_conf;;
-esac
+if $if_up; then
+ make_yp_conf
+elif $if_down; then
+ restore_yp_conf
+fi
diff --git a/dhcpcd-hooks/50-ypbind b/dhcpcd-hooks/50-ypbind
new file mode 100644
index 0000000..9a1fecc
--- /dev/null
+++ b/dhcpcd-hooks/50-ypbind
@@ -0,0 +1,74 @@
+# Sample dhcpcd hook for ypbind
+# This script is only suitable for the Linux version.
+
+: ${ypbind_restart_cmd:=service_command ypbind restart}
+: ${ypbind_stop_cmd:=service_condcommand ypbind stop}
+ypbind_dir="$state_dir/ypbind"
+
+best_domain()
+{
+ local i=
+
+ for i in $interfaces; do
+ if [ -e "$ypbind_dir/$i" ]; then
+ cat "$ypbind_dir/$i"
+ fi
+ done
+ return 1
+}
+
+make_yp_binding()
+{
+ [ -d "$ypbind_dir" ] || mkdir -p "$ypbind_dir"
+ echo "$new_nis_domain" >"$ypbind_dir/$interface"
+ local nd="$(best_domain)"
+
+ local cf=/var/yp/binding/"$new_nis_domain".ypservers
+ if [ -n "$new_nis_servers" ]; then
+ local ncf="$cf.$interface" x=
+ rm -f "$ncf"
+ for x in $new_nis_servers; do
+ echo "$x" >>"$ncf"
+ done
+ change_file "$cf" "$ncf"
+ else
+ # Because this is not an if .. fi then we can use $? below
+ [ -e "$cf" ] && rm "$cf"
+ fi
+
+ if [ $? = 0 -o "$nd" != "$(domainname)" ]; then
+ domainname "$nd"
+ if [ -n "$ypbind_restart_cmd" ]; then
+ eval $ypbind_restart_cmd
+ fi
+ fi
+}
+
+restore_yp_binding()
+{
+ rm -f "$ypbind_dir/$interface"
+ local nd="$(best_domain)"
+ # We need to stop ypbind if there is no best domain
+ # otherwise it will just stall as we cannot set domainname
+ # to blank :/
+ if [ -z "$nd" ]; then
+ if [ -n "$ypbind_stop_cmd" ]; then
+ eval $ypbind_stop_cmd
+ fi
+ elif [ "$nd" != "$(domainname)" ]; then
+ domainname "$nd"
+ if [ -n "$ypbind_restart_cmd" ]; then
+ eval $ypbind_restart_cmd
+ fi
+ fi
+}
+
+if [ "$reason" = PREINIT ]; then
+ rm -f "$ypbind_dir/$interface"
+elif $if_up || $if_down; then
+ if [ -n "$new_nis_domain" ]; then
+ make_yp_binding
+ elif [ -n "$old_nis_domain" ]; then
+ restore_yp_binding
+ fi
+fi
diff --git a/dhcpcd-hooks/Makefile b/dhcpcd-hooks/Makefile
index cfb19f7..7563d2d 100644
--- a/dhcpcd-hooks/Makefile
+++ b/dhcpcd-hooks/Makefile
@@ -1,13 +1,19 @@
-LIBEXECDIR?= ${PREFIX}/libexec
-HOOKDIR= ${LIBEXECDIR}/dhcpcd-hooks
-SYSTEMSCRIPTS= 01-test 10-mtu 20-resolv.conf 30-hostname
-FILES= ${SYSTEMSCRIPTS} ${HOOKSCRIPTS}
-FILESDIR= ${HOOKDIR}
+TOP?= ../
+include ${TOP}/Makefile.inc
+include ${TOP}/config.mk
+
+SCRIPTSDIR= ${LIBEXECDIR}/dhcpcd-hooks
+SCRIPTS= 01-test 02-dump
+SCRIPTS+= 10-mtu 20-resolv.conf 29-lookup-hostname 30-hostname
+SCRIPTS+= ${HOOKSCRIPTS}
all:
-MK= ../mk
-include ${MK}/os.mk
-include ${MK}/sys.mk
-include ${MK}/files.mk
-install: _filesinstall
+install:
+ ${INSTALL} -d ${DESTDIR}${SCRIPTSDIR}
+ ${INSTALL} -m ${NONBINMODE} ${SCRIPTS} ${DESTDIR}${SCRIPTSDIR}
+
+import:
+ ${INSTALL} -d /tmp/${DISTPREFIX}/dhcpcd-hooks
+ ${INSTALL} -m ${NONBINMODE} ${SCRIPTS} /tmp/${DISTPREFIX}/dhcpcd-hooks
+
diff --git a/dhcpcd-run-hooks.8 b/dhcpcd-run-hooks.8
index a6a1849..882b7d5 100644
--- a/dhcpcd-run-hooks.8
+++ b/dhcpcd-run-hooks.8
@@ -1,4 +1,4 @@
-.\" Copyright 2006-2008 Roy Marples
+.\" Copyright (c) 2006-2010 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -22,22 +22,23 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 14, 2008
-.Dt DHCPCD.SH 8 SMM
+.Dd August 24, 2010
+.Dt DHCPCD-RUN-HOOKS 8 SMM
+.Os
.Sh NAME
.Nm dhcpcd-run-hooks
-.Nd DHCP client configuration script
+.Nd DHCP client configuration script
.Sh DESCRIPTION
.Nm
is used by
.Xr dhcpcd 8
to run any system and user defined hook scripts.
System hook scripts are found in
-.Pa /system/etc/dhcpcd/dhcpcd-hooks
-and the user defined hooks are
-.Pa /system/etc/dhcpcd/dhcpcd.enter-hook .
+.Pa /libexec/dhcpcd-hooks
+and the user defined hooks are
+.Pa /etc/dhcpcd.enter-hook .
and
-.Pa /system/etc/dhcpcd/dhcpcd.exit-hook .
+.Pa /etc/dhcpcd.exit-hook .
The default install supplies hook scripts for configuring
.Pa /etc/resolv.conf
and the hostname.
@@ -68,9 +69,12 @@ argument.
Here's a list of reasons why
.Nm
could be invoked:
-.Bl -tag -width indent
+.Bl -tag -width PREINIT
.It Dv PREINIT
dhcpcd is starting up and any pre-initialisation should be done.
+.It Dv CARRIER
+dhcpcd has detected the carrier is up.
+This is generally just a notification and no action need be taken.
.It Dv INFORM
dhcpcd informed a DHCP server about it's address and obtained other
configuration details.
@@ -82,32 +86,52 @@ dhcpcd renewed it's lease.
dhcpcd has rebound to a new DHCP server.
.It Dv REBOOT
dhcpcd successfully requested a lease from a DHCP server.
-.It Dv EXPIRE
-dhcpcd's lease expired and it failed to obtain a new one.
.It Dv IPV4LL
dhcpcd failed to contact any DHCP servers but did obtain an IPV4LL address.
-.It Dv FAIL
-dhcpcd failed to contact any DHCP servers or use an old lease.
+.It Dv STATIC
+dhcpcd has been configured with a static configuration which has not been
+obtained from a DHCP server.
+.It Dv 3RDPARTY
+dhcpcd is monitoring the interface for a 3rd party to give it an IP address.
.It Dv TIMEOUT
dhcpcd failed to contact any DHCP servers but was able to use an old lease.
+.It Dv EXPIRE
+dhcpcd's lease or state expired and it failed to obtain a new one.
+.It Dv RELEASE
+dhcpcd's lease was released back to the DHCP server for re-use.
+.It Dv NAK
+dhcpcd received a NAK from the DHCP server.
+This should be treated as EXPIRE.
+.It Dv NOCARRIER
+dhcpcd lost the carrier.
+The cable may have been unplugged or association to the wireless point lost.
+.It Dv FAIL
+dhcpcd failed to operate on the interface.
+This normally happens when dhcpcd does not support the raw interface, which
+means it cannot work as a DHCP or ZeroConf client.
+Static configuration and DHCP INFORM is still allowed.
+.It Dv STOP
+dhcpcd stopped running on the interface.
+.It Dv DUMP
+dhcpcd has been asked to dump the last lease for the interface.
.It Dv TEST
dhcpcd received an OFFER from a DHCP server but will not configure the
interface.
This is primarily used to test the variables are filled correctly for the
script to process them.
.El
-.Sh FILES
+.Sh FILES
When
.Nm
runs, it loads
-.Pa /system/etc/dhcpcd/dhcpcd.enter-hook
+.Pa /etc/dhcpcd.enter-hook
and any scripts found in
-.Pa /system/etc/dhcpcd/dhcpcd-hooks
+.Pa /libexec/dhcpcd-hooks
in a lexical order and then finally
-.Pa /system/etc/dhcpcd/dhcpcd.exit-hook
+.Pa /etc/dhcpcd.exit-hook
.Sh SEE ALSO
.Xr dhcpcd 8
.Sh AUTHORS
-.An Roy Marples <roy@marples.name>
+.An Roy Marples Aq roy@marples.name
.Sh BUGS
Please report them to http://roy.marples.name/projects/dhcpcd
diff --git a/dhcpcd-run-hooks.8.in b/dhcpcd-run-hooks.8.in
index 6776cf8..bcfc81d 100644
--- a/dhcpcd-run-hooks.8.in
+++ b/dhcpcd-run-hooks.8.in
@@ -1,4 +1,4 @@
-.\" Copyright 2006-2008 Roy Marples
+.\" Copyright (c) 2006-2010 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -22,12 +22,12 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 14, 2008
-.Dt DHCPCD.SH 8 SMM
+.Dd August 24, 2010
+.Dt DHCPCD-RUN-HOOKS 8 SMM
.Os
.Sh NAME
.Nm dhcpcd-run-hooks
-.Nd DHCP client configuration script
+.Nd DHCP client configuration script
.Sh DESCRIPTION
.Nm
is used by
@@ -35,7 +35,7 @@ is used by
to run any system and user defined hook scripts.
System hook scripts are found in
.Pa @HOOKDIR@
-and the user defined hooks are
+and the user defined hooks are
.Pa @SYSCONFDIR@/dhcpcd.enter-hook .
and
.Pa @SYSCONFDIR@/dhcpcd.exit-hook .
@@ -69,9 +69,12 @@ argument.
Here's a list of reasons why
.Nm
could be invoked:
-.Bl -tag -width indent
+.Bl -tag -width PREINIT
.It Dv PREINIT
dhcpcd is starting up and any pre-initialisation should be done.
+.It Dv CARRIER
+dhcpcd has detected the carrier is up.
+This is generally just a notification and no action need be taken.
.It Dv INFORM
dhcpcd informed a DHCP server about it's address and obtained other
configuration details.
@@ -83,21 +86,41 @@ dhcpcd renewed it's lease.
dhcpcd has rebound to a new DHCP server.
.It Dv REBOOT
dhcpcd successfully requested a lease from a DHCP server.
-.It Dv EXPIRE
-dhcpcd's lease expired and it failed to obtain a new one.
.It Dv IPV4LL
dhcpcd failed to contact any DHCP servers but did obtain an IPV4LL address.
-.It Dv FAIL
-dhcpcd failed to contact any DHCP servers or use an old lease.
+.It Dv STATIC
+dhcpcd has been configured with a static configuration which has not been
+obtained from a DHCP server.
+.It Dv 3RDPARTY
+dhcpcd is monitoring the interface for a 3rd party to give it an IP address.
.It Dv TIMEOUT
dhcpcd failed to contact any DHCP servers but was able to use an old lease.
+.It Dv EXPIRE
+dhcpcd's lease or state expired and it failed to obtain a new one.
+.It Dv RELEASE
+dhcpcd's lease was released back to the DHCP server for re-use.
+.It Dv NAK
+dhcpcd received a NAK from the DHCP server.
+This should be treated as EXPIRE.
+.It Dv NOCARRIER
+dhcpcd lost the carrier.
+The cable may have been unplugged or association to the wireless point lost.
+.It Dv FAIL
+dhcpcd failed to operate on the interface.
+This normally happens when dhcpcd does not support the raw interface, which
+means it cannot work as a DHCP or ZeroConf client.
+Static configuration and DHCP INFORM is still allowed.
+.It Dv STOP
+dhcpcd stopped running on the interface.
+.It Dv DUMP
+dhcpcd has been asked to dump the last lease for the interface.
.It Dv TEST
dhcpcd received an OFFER from a DHCP server but will not configure the
interface.
This is primarily used to test the variables are filled correctly for the
script to process them.
.El
-.Sh FILES
+.Sh FILES
When
.Nm
runs, it loads
@@ -109,6 +132,6 @@ in a lexical order and then finally
.Sh SEE ALSO
.Xr dhcpcd 8
.Sh AUTHORS
-.An Roy Marples <roy@marples.name>
+.An Roy Marples Aq roy@marples.name
.Sh BUGS
Please report them to http://roy.marples.name/projects/dhcpcd
diff --git a/dhcpcd-run-hooks.in b/dhcpcd-run-hooks.in
index a848260..cb897b4 100644
--- a/dhcpcd-run-hooks.in
+++ b/dhcpcd-run-hooks.in
@@ -2,38 +2,54 @@
# dhcpcd client configuration script
# Handy variables and functions for our hooks to use
-from="from"
+from=from
signature_base="# Generated by dhcpcd"
-signature="${signature_base} ${from} ${interface}"
+signature="$signature_base $from $interface"
signature_base_end="# End of dhcpcd"
-signature_end="${signature_base_end} ${from} ${interface}"
-state_dir="/var/run/dhcpcd"
+signature_end="$signature_base_end $from $interface"
+state_dir=/var/run/dhcpcd
+
+if_up=false
+if_down=false
+case "$reason" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT|STATIC) if_up=true;;
+PREINIT|EXPIRE|FAIL|IPV4LL|NAK|NOCARRIER|RELEASE|STOP) if_down=true;;
+esac
# Ensure that all arguments are unique
uniqify()
{
- local result=
-
- while [ -n "$1" ]; do
- case " ${result} " in
- *" $1 "*);;
- *) result="${result}${result:+ }$1";;
+ local result= i=
+ for i; do
+ case " $result " in
+ *" $i "*);;
+ *) result="$result $i";;
esac
- shift
done
- echo "${result}"
+ echo "${result# *}"
}
-# List interface config files in a dir
-# We may wish to control the order at some point rather than just lexical
+# List interface config files in a directory.
+# If dhcpcd is running as a single instance then it will have a list of
+# interfaces in the preferred order.
+# Otherwise we just use what we have.
list_interfaces()
{
- local x= interfaces=
+ local i= x= ifaces=
+ for i in $interface_order; do
+ [ -e "$1/$i" ] && ifaces="$ifaces${ifaces:+ }$i"
+ done
for x in "$1"/*; do
- [ -e "${x}" ] || continue
- interfaces="${interfaces}${interfaces:+ }${x##*/}"
+ [ -e "$x" ] || continue
+ for i in $interface_order; do
+ if [ $i = "${x##*/}" ]; then
+ unset x
+ break
+ fi
+ done
+ [ -n "$x" ] && ifaces="$ifaces${ifaces:+ }${x##*/}"
done
- echo "${interfaces}"
+ echo "$ifaces"
}
# We normally use sed to extract values using a key from a list of files
@@ -44,14 +60,14 @@ key_get_value()
shift
if type sed >/dev/null 2>&1; then
- sed -n "s/^${key}//p" $@
+ sed -n "s/^$key//p" $@
else
for x; do
while read line; do
- case "${line}" in
- "${key}"*) echo "${line##${key}}";;
+ case "$line" in
+ "$key"*) echo "${line##$key}";;
esac
- done < "${x}"
+ done < "$x"
done
fi
}
@@ -64,37 +80,40 @@ remove_markers()
shift; shift
if type sed >/dev/null 2>&1; then
- sed "/^${m1}/,/^${m2}/d" $@
+ sed "/^$m1/,/^$m2/d" $@
else
for x; do
while read line; do
- case "${line}" in
- "${m1}"*) in_marker=1;;
- "${m2}"*) in_marker=0;;
- *) [ ${in_marker} = 0 ] && echo "${line}";;
+ case "$line" in
+ "$m1"*) in_marker=1;;
+ "$m2"*) in_marker=0;;
+ *) [ $in_marker = 0 ] && echo "$line";;
esac
- done < "${x}"
+ done < "$x"
done
fi
}
-# Compare two files
-# If different, replace first with second otherwise remove second
+# Compare two files.
+# If different, replace first with second otherwise remove second.
change_file()
{
- if type cmp >/dev/null 2>&1; then
- cmp -s "$1" "$2"
- elif type diff >/dev/null 2>&1; then
- diff -q "$1" "$2" >/dev/null
- else
- # Hopefully we're only working on small text files ...
- [ "$(cat "$1")" = "$(cat "$2")" ]
- fi
- if [ $? -eq 0 ]; then
- rm -f "$2"
- return 1
+ if [ -e "$1" ]; then
+ if type cmp >/dev/null 2>&1; then
+ cmp -s "$1" "$2"
+ elif type diff >/dev/null 2>&1; then
+ diff -q "$1" "$2" >/dev/null
+ else
+ # Hopefully we're only working on small text files ...
+ [ "$(cat "$1")" = "$(cat "$2")" ]
+ fi
+ if [ $? -eq 0 ]; then
+ rm -f "$2"
+ return 1
+ fi
fi
- mv -f "$2" "$1"
+ cat "$2" > "$1"
+ rm -f "$2"
return 0
}
@@ -102,19 +121,59 @@ change_file()
save_conf()
{
if [ -f "$1" ]; then
- rm -f "$1"-pre."${interface}"
- mv -f "$1" "$1"-pre."${interface}"
+ rm -f "$1-pre.$interface"
+ cat "$1" > "$1-pre.$interface"
fi
}
# Restore a config file
restore_conf()
{
- [ -f "$1"-pre."${interface}" ] || return 1
- rm -f "$1"
- mv -f "$1"-pre."${interface}" "$1"
+ [ -f "$1-pre.$interface" ] || return 1
+ cat "$1-pre.$interface" > "$1"
+ rm -f "$1-pre.$interface"
+}
+
+# Write a syslog entry
+syslog()
+{
+ local lvl="$1"
+
+ [ -n "$lvl" ] && shift
+ if [ -n "$*" ]; then
+ if type logger >/dev/null 2>&1; then
+ logger -t dhcpcd -p daemon."$lvl" -s "$*"
+ fi
+ fi
+}
+
+# Check a system service exists
+service_exists()
+{
+ @SERVICEEXISTS@
+}
+
+# Send a command to a system service
+service_cmd()
+{
+ @SERVICECMD@
+}
+
+# Send a command to a system service if it is running
+service_status()
+{
+ @SERVICESTATUS@
}
+# Handy macros for our hooks
+service_command()
+{
+ service_exists $1 && service_cmd $1 $2
+}
+service_condcommand()
+{
+ service_exists $1 && service_status $1 && service_cmd $1 $2
+}
# We source each script into this one so that scripts run earlier can
# remove variables from the environment so later scripts don't see them.
@@ -125,14 +184,14 @@ for hook in \
@HOOKDIR@/* \
@SYSCONFDIR@/dhcpcd.exit-hook
do
- for skip in ${skip_hooks}; do
- case "${hook}" in
- */"${skip}") continue 2;;
- */[0-9][0-9]"-${skip}") continue 2;;
- */[0-9][0-9]"-${skip}.sh") continue 2;;
+ for skip in $skip_hooks; do
+ case "$hook" in
+ */"$skip") continue 2;;
+ */[0-9][0-9]"-$skip") continue 2;;
+ */[0-9][0-9]"-$skip.sh") continue 2;;
esac
done
- if [ -f "${hook}" ]; then
- . "${hook}"
+ if [ -f "$hook" ]; then
+ . "$hook"
fi
done
diff --git a/dhcpcd.8 b/dhcpcd.8
index ac6150c..2acfb90 100644
--- a/dhcpcd.8
+++ b/dhcpcd.8
@@ -1,4 +1,4 @@
-.\" Copyright 2006-2008 Roy Marples
+.\" Copyright (c) 2006-2010 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -22,15 +22,17 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 20, 2008
+.Dd August 31, 2010
.Dt DHCPCD 8 SMM
+.Os
.Sh NAME
.Nm dhcpcd
.Nd an RFC 2131 compliant DHCP client
.Sh SYNOPSIS
.Nm
-.Op Fl bdknpqABDEGKLSTV
+.Op Fl bdgknpqwABDEGHJKLTV
.Op Fl c , -script Ar script
+.Op Fl e , -env Ar value
.Op Fl f , -config Ar file
.Op Fl h , -hostname Ar hostname
.Op Fl i , -vendorclassid Ar vendorclassid
@@ -42,43 +44,61 @@
.Op Fl t , -timeout Ar seconds
.Op Fl u , -userclass Ar class
.Op Fl v , -vendor Ar code , Ar value
+.Op Fl y , -reboot Ar seconds
+.Op Fl z , -allowinterfaces Ar pattern
.Op Fl C , -nohook Ar hook
.Op Fl F , -fqdn Ar FQDN
.Op Fl I , -clientid Ar clientid
.Op Fl O , -nooption Ar option
.Op Fl Q , -require Ar option
-.Op Fl X , -blacklist Ar address
-.Ar interface
+.Op Fl S , -static Ar value
+.Op Fl W , -whitelist Ar address Ns Op Ar /cidr
+.Op Fl X , -blacklist Ar address Ns Op Ar /cidr
+.Op Fl Z , -denyinterfaces Ar pattern
+.Op interface
+.Op ...
.Nm
.Fl k , -release
+.Op interface
+.Nm
+.Fl U, -dumplease
.Ar interface
.Nm
.Fl x , -exit
-.Ar interface
+.Op interface
+.Nm
+.Fl v , -version
.Sh DESCRIPTION
.Nm
is an implementation of the DHCP client specified in
.Li RFC 2131 .
.Nm
gets the host information
-.Po
+.Po
IP address, routes, etc
.Pc
from a DHCP server and configures the network
.Ar interface
of the
-machine on which it is running.
+machine on which it is running.
.Nm
then runs the configuration script which writes DNS information to
.Xr resolvconf 8 ,
if available, otherwise directly to
.Pa /etc/resolv.conf .
-If the hostname is currenly blank, (null) or localhost then
+If the hostname is currently blank, (null) or localhost, or
+.Va force_hostname
+is YES or TRUE or 1 then
.Nm
sets the hostname to the one supplied by the DHCP server.
.Nm
then daemonises and waits for the lease renewal time to lapse.
-Then it attempts to renew its lease and reconfigure if the new lease changes.
+It will then attempt to renew its lease and reconfigure if the new lease
+changes.
+.Pp
+.Nm
+is also an implementation of the BOOTP client specified in
+.Li RFC 951 .
.Ss Local Link configuration
If
.Nm
@@ -97,15 +117,47 @@ installed which always defeats IPv4LL probing.
To disable this behaviour, you can use the
.Fl L , -noipv4ll
option.
+.Ss Multiple interfaces
+If a list of interfaces are given on the command line, then
+.Nm
+only works with those interfaces, otherwise
+.Nm
+discovers available Ethernet interfaces.
+If any interface reports a working carrier then
+.Nm
+will try and obtain a lease before forking to the background,
+otherwise it will fork right away.
+This behaviour can be modified with the
+.Fl b , -background
+and
+.Fl w , -waitip
+options.
+.Pp
+If a single interface is given then
+.Nm
+only works for that interface and runs as a separate instance.
+The
+.Fl w , -waitip
+option is enabled in this instance to maintain compatibility with older
+versions.
+.Pp
+Interfaces are preferred by carrier, DHCP lease/IPv4LL and then lowest metric.
+For systems that support route metrics, each route will be tagged with the
+metric, otherwise
+.Nm
+changes the routes to use the interface with the same route and the lowest
+metric.
+See options below for controlling which interfaces we allow and deny through
+the use of patterns.
.Ss Hooking into DHCP events
.Nm
runs
-.Pa /system/etc/dhcpcd/dhcpcd-run-hooks ,
+.Pa /libexec/dhcpcd-run-hooks ,
or the script specified by the
.Fl c , -script
option.
This script runs each script found in
-.Pa /system/etc/dhcpcd/dhcpcd-hooks
+.Pa /libexec/dhcpcd-hooks
in a lexical order.
The default installation supplies the scripts
.Pa 01-test ,
@@ -122,7 +174,7 @@ for details on how these scripts work.
.Nm
currently ignores the exit code of the script.
.Ss Fine tuning
-You can fine tune the behaviour of
+You can fine-tune the behaviour of
.Nm
with the following options:
.Bl -tag -width indent
@@ -134,27 +186,44 @@ carrier status.
Use this
.Ar script
instead of the default
-.Pa /system/etc/dhcpcd/dhcpcd-run-hooks .
+.Pa /libexec/dhcpcd-run-hooks .
.It Fl d , -debug
-Echo debug and informational messages to the console.
-Subsequent debug options stop
+Echo debug messages to the stderr and syslog.
+.It Fl e , -env Ar value
+Push
+.Ar value
+to the environment for use in
+.Xr dhcpcd-run-hooks 8 .
+For example, you can force the hostname hook to always set the hostname with
+.Fl e
+.Va force_hostname=YES .
+.It Fl g , -reconfigure
+.Nm
+will re-apply IP address, routing and run
+.Xr dhcpcd-run-hooks 8
+for each interface.
+This is useful so that a 3rd party such as PPP or VPN can change the routing
+table and / or DNS, etc and then instruct
.Nm
-from daemonising.
+to put things back afterwards.
+.Nm
+does not read a new configuration when this happens - you should rebind if you
+need that functionality.
.It Fl f , -config Ar file
Specify a config to load instead of
-.Pa /system/etc/dhcpcd/dhcpcd.conf .
+.Pa /etc/dhcpcd.conf .
.Nm
always processes the config file before any command line options.
.It Fl h , -hostname Ar hostname
-By default,
-.Nm
-sends the current hostname to the DHCP server so it can register in DNS.
-You can use this option to specify the
+Sends
.Ar hostname
-sent, or an empty string to
-stop any
+to the DHCP server so it can be registered in DNS.
+If
+.Ar hostname
+is an empty string then the current system hostname is sent.
+If
.Ar hostname
-from being sent.
+is a FQDN (ie, contains a .) then it will be encoded as such.
.It Fl i , -vendorclassid Ar vendorclassid
Override the
.Ar vendorclassid
@@ -166,7 +235,7 @@ This causes an existing
.Nm
process running on the
.Ar interface
-to release its lease, deconfigure the
+to release its lease, de-configure the
.Ar interface
and then exit.
.Nm
@@ -176,61 +245,45 @@ Request a specific lease time in
.Ar seconds .
By default
.Nm
-does not request any lease time and leaves the it in the hands of the
+does not request any lease time and leaves it in the hands of the
DHCP server.
.It Fl m , -metric Ar metric
-Added routes will use the
-.Ar metric
-on systems where this is supported
-.Po
-presently only Linux
-.Pc .
-Route metrics allow the addition of routes to the same destination across
-different interfaces, the lower the metric the more it is preferred.
+Metrics are used to prefer an interface over another one, lowest wins.
+.Nm
+will supply a default metic of 200 +
+.Xr if_nametoindex 3 .
+An extra 100 will be added for wireless interfaces.
.It Fl o , -option Ar option
Request the DHCP
.Ar option
variable for use in
-.Pa /system/etc/dhcpcd/dhcpcd-run-hooks .
+.Pa /libexec/dhcpcd-run-hooks .
.It Fl n , -rebind
-Notifies an existing
-.Nm
-process running on the
-.Ar interface
-to rebind it's lease.
+Notifies
.Nm
-will not re-configure itself or use any other command line arguments.
-.Nm
-will timeout the rebind after 30 seconds at which point the lease will be
-expired and
-.Nm
-will enter the discovery state to obtain a new lease.
-Use the
-.Fl t , -timeout
-option to change this.
+to reload its configuration and rebind its interfaces.
If
.Nm
is not running, then it starts up as normal.
-This option used to be renew, but rebind is more accurate as we need to
-broadcast the request instead of unicasting.
.It Fl p , -persistent
.Nm
-normally deconfigures the
+normally de-configures the
.Ar interface
and configuration when it exits.
-Sometimes, this isn't desirable if for example you have root mounted over NFS.
+Sometimes, this isn't desirable if, for example, you have root mounted over
+NFS.
You can use this option to stop this from happening.
.It Fl r , -request Op Ar address
.Nm
normally sends a DHCP DISCOVER to find servers to offer an address.
.Nm
then requests the address used.
-You can use this option to skip the BROADCAST step and just request the
+You can use this option to skip the DISCOVER phase and just request the
.Ar address .
The downside is if you request an
.Ar address
the DHCP server does not know about or the DHCP server is not
-authorative, it will remain silent.
+authoritative, it will remain silent.
In this situation, we go back to the init state and DISCOVER again.
If no
.Ar address
@@ -246,7 +299,7 @@ This does not get a lease as such, just notifies the DHCP server of the
in use.
You should also include the optional
.Ar cidr
-network number in-case the address is not already configured on the interface.
+network number in case the address is not already configured on the interface.
.Nm
remains running and pretends it has an infinite lease.
.Nm
@@ -267,39 +320,58 @@ to wait forever to get a lease.
.It Fl u , -userclass Ar class
Tags the DHCP message with the userclass
.Ar class .
-DHCP servers use this give members of the class DHCP options other than the
+DHCP servers use this to give members of the class DHCP options other than the
default, without having to know things like hardware address or hostname.
.It Fl v , -vendor Ar code , Ns Ar value
-Add an enscapulated vendor option.
+Add an encapsulated vendor option.
.Ar code
should be between 1 and 254 inclusive.
+To add a raw vendor string, omit
+.Ar code
+but keep the comma.
Examples.
.Pp
Set the vendor option 01 with an IP address.
.D1 dhcpcd \-v 01,192.168.0.2 eth0
Set the vendor option 02 with a hex code.
.D1 dhcpcd \-v 02,01:02:03:04:05 eth0
-Do the above and set a third option with a string and not an IP address.
-.D1 dhcpcd \-v 01,192.168.0.2 \-v 02,01:02:03:04:05 \-v 03,\e"192.168.0.2\e" eth0
+Set the vendor option 03 with an IP address as a string.
+.D1 dhcpcd \-v 03,\e"192.168.0.2\e" eth0
+Set un-encapsulated vendor option to hello world.
+.D1 dhcpcd \-v ,"hello world" eth0
+.It Fl v , -version
+Display both program version and copyright information.
+.Nm
+then exits before doing any configuration.
+.It Fl w , -waitip
+Wait for an address to be assigned before forking to the background.
.It Fl x , -exit
This will signal an existing
.Nm
process running on the
.Ar interface
-to deconfigure the
+to de-configure the
.Ar interface
and exit.
.Nm
then waits until this process has exited.
-.It Fl D , -duid
+.It Fl y , -reboot Ar seconds
+Allow
+.Ar reboot
+seconds before moving to the discover phase if we have an old lease to use.
+The default is 10 seconds.
+A setting of 0 seconds causes
+.Nm
+to skip the reboot phase and go straight into discover.
+.It Fl D , -duid
Generate an
.Li RFC 4361
compliant clientid.
-This requires persistent storage and not all DHCP servers work with it so it's
-not enabled by default.
+This requires persistent storage and not all DHCP servers work with it so it
+is not enabled by default.
.Nm
-generates the DUID and stores in it
-.Pa /system/etc/dhcpcd/dhcpcd.duid
+generates the DUID and stores it in
+.Pa /etc/dhcpcd.duid .
This file should not be copied to other hosts.
.It Fl E , -lastlease
If
@@ -315,20 +387,24 @@ hostname.
Valid values for
.Ar fqdn
are disable, none, ptr and both.
-The current hostname or the hostname specified using the
-.Fl h , -hostname
-option must be a FQDN.
.Nm
itself never does any DNS updates.
.Nm
encodes the FQDN hostname as specified in
.Li RFC1035 .
.It Fl I , -clientid Ar clientid
-Change the default clientid sent from the interface hardware address.
+Send the
+.Ar clientid .
If the string is of the format 01:02:03 then it is encoded as hex.
-If not set then none is sent.
+For interfaces whose hardware address is longer than 8 bytes, or if the
+.Ar clientid
+is an empty string then
+.Nm
+sends a default
+.Ar clientid
+of the hardware family and the hardware address.
.El
-.Ss Restriciting behaviour
+.Ss Restricting behaviour
.Nm
will try to do as much as it can by default.
However, there are sometimes situations where you don't want the things to be
@@ -340,6 +416,14 @@ Quiet
.Nm
on the command line, only warnings and errors will be displayed.
The messages are still logged though.
+.It Fl z , -allowinterfaces Ar pattern
+When discovering interfaces, the interface name must match
+.Ar pattern
+which is a space or comma separated list of patterns passed to
+.Xr fnmatch 3 .
+If the same interface is matched in
+.Fl Z , -denyinterfaces
+then it is still denied.
.It Fl A , -noarp
Don't request or claim the address by ARP.
This also disables IPv4LL.
@@ -352,10 +436,22 @@ Don't run this hook script.
Matches full name, or prefixed with 2 numbers optionally ending with
.Pa .sh .
.Pp
-So to stop dhcpcd from touching your DNS or MTU settings you would do:-
+So to stop
+.Nm
+from touching your DNS or MTU settings you would do:-
.D1 dhcpcd -C resolv.conf -C mtu eth0
.It Fl G , -nogateway
Don't set any default routes.
+.It Fl H , -xidhwaddr
+Use the last four bytes of the hardware address as the DHCP xid instead
+of a randomly generated number.
+.It Fl J , -broadcast
+Instructs the DHCP server to broadcast replies back to the client.
+Normally this is only set for non Ethernet interfaces,
+such as FireWire and InfiniBand.
+In most instances,
+.Nm
+will set this automatically.
.It Fl K , -nolink
Don't receive link messages for carrier status.
You should only have to use this with buggy device drivers or running
@@ -371,45 +467,102 @@ configure the interface and routing.
Requires the
.Ar option
to be present in all DHCP messages, otherwise the message is ignored.
+To enforce that
+.Nm
+only responds to DHCP servers and not BOOTP servers, you can
+.Fl Q
+.Ar dhcp_message_type .
+.It Fl S, -static Ar value
+Configures a static
+.Ar value .
+If you set
+.Ic ip_address
+then
+.Nm
+will not attempt to obtain a lease and just use the value for the address with
+an infinite lease time.
+.Pp
+Here is an example which configures a static address, routes and dns.
+.D1 dhcpcd -S ip_address=192.168.0.10/24 \e
+.D1 -S routers=192.168.0.1 \e
+.D1 -S domain_name_servers=192.168.0.1 \e
+.D1 eth0
.It Fl T, -test
-On receipt of OFFER messages just call
-.Pa /system/etc/dhcpcd/dhcpcd-run-hooks
-with the reason of TEST which echo's the DHCP variables found in the message
+On receipt of DHCP messages just call
+.Pa /libexec/dhcpcd-run-hooks
+with the reason of TEST which echos the DHCP variables found in the message
to the console.
The interface configuration isn't touched and neither are any configuration
files.
+To test INFORM the interface needs to be configured with the desired address
+before starting
+.Nm .
+.It Fl U, -dumplease Ar interface
+Dumps the last lease for the
+.Ar interface
+to stdout.
+.Ar interface
+could also be a path to a DHCP wire formatted file.
.It Fl V, -variables
Display a list of option codes and the associated variable for use in
.Xr dhcpcd-run-hooks 8 .
-.It Fl X, -blacklist Ar address
-Ignores all DHCP messages which have this
-.Ar address
-as the server ID.
-This may be expanded in future releases to ignore all packets
-matching either the IP or hardware
-.Ar address .
+Variables are prefixed with new_ and old_ unless the option number is -.
+Variables without an option are part of the DHCP message and cannot be
+directly requested.
+.It Fl W, -whitelist Ar address Ns Op /cidr
+Only accept packets from
+.Ar address Ns Op /cidr .
+.Fl X, -blacklist
+is ignored if
+.Fl W, -whitelist
+is set.
+.It Fl X, -blacklist Ar address Ns Op Ar /cidr
+Ignore all packets from
+.Ar address Ns Op Ar /cidr .
+.It Fl Z , -denyinterfaces Ar pattern
+When discovering interfaces, the interface name must not match
+.Ar pattern
+which is a space or comma separated list of patterns passed to
+.Xr fnmatch 3 .
.El
+.Sh 3RDPARTY LINK MANAGEMENT
+Some interfaces require configuration by 3rd parties, such as PPP or VPN.
+When an interface configuration in
+.Nm
+is marked as STATIC or INFORM without an address then
+.Nm
+will monitor the interface until an address is added or removed from it and
+act accordingly.
+For point to point interfaces (like PPP), a default route to its
+destination is automatically added to the configuration.
+If the point to point interface if configured for INFORM, then
+.Nm
+unicasts INFORM to the destination, otherwise it defaults to STATIC.
.Sh NOTES
.Nm
requires a Berkley Packet Filter, or BPF device on BSD based systems and a
Linux Socket Filter, or LPF device on Linux based systems.
.Sh FILES
.Bl -ohang
-.It Pa /system/etc/dhcpcd/dhcpcd.conf
+.It Pa /etc/dhcpcd.conf
Configuration file for dhcpcd.
If you always use the same options, put them here.
-.It Pa /system/etc/dhcpcd/dhcpcd.duid
+.It Pa /etc/dhcpcd.duid
Text file that holds the DUID used to identify the host.
-.It Pa /system/etc/dhcpcd/dhcpcd-run-hooks
-Bourne shell script that is run to configure or deconfigure an interface.
-.It Pa /system/etc/dhcpcd/dhcpcd-hooks
+.It Pa /libexec/dhcpcd-run-hooks
+Bourne shell script that is run to configure or de-configure an interface.
+.It Pa /libexec/dhcpcd-hooks
A directory containing bourne shell scripts that are run by the above script.
Each script can be disabled by using the
.Fl C , -nohook
option described above.
-.It Pa /data/misc/dhcp/dhcpcd\- Ns Ar interface Ns .lease
+.It Pa /var/db/dhcpcd\- Ns Ar interface Ns .lease
The actual DHCP message send by the server. We use this when reading the last
lease and use the files mtime as when it was issued.
+.It Pa /var/run/dhcpcd.pid
+Stores the PID of
+.Nm
+running on all interfaces.
.It Pa /var/run/dhcpcd\- Ns Ar interface Ns .pid
Stores the PID of
.Nm
@@ -421,10 +574,12 @@ running on the
.Xr dhcpcd-run-hooks 8 ,
.Xr resolv.conf 5 ,
.Xr resolvconf 8 ,
+.Xr if_nametoindex 3 ,
+.Xr fnmatch 3
.Sh STANDARDS
-RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396, RFC 3397,
-RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702.
+RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396,
+RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702.
.Sh AUTHORS
-.An Roy Marples <roy@marples.name>
+.An Roy Marples Aq roy@marples.name
.Sh BUGS
-Please report them to http://bugs.marples.name
+Please report them to http://roy.marples.name/projects/dhcpcd
diff --git a/dhcpcd.8.in b/dhcpcd.8.in
index d3fbebf..5993878 100644
--- a/dhcpcd.8.in
+++ b/dhcpcd.8.in
@@ -1,4 +1,4 @@
-.\" Copyright 2006-2008 Roy Marples
+.\" Copyright (c) 2006-2010 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd November 18, 2008
+.Dd August 31, 2010
.Dt DHCPCD 8 SMM
.Os
.Sh NAME
@@ -30,8 +30,9 @@
.Nd an RFC 2131 compliant DHCP client
.Sh SYNOPSIS
.Nm
-.Op Fl bdknpqABDEGKLTV
+.Op Fl bdgknpqwABDEGHJKLTV
.Op Fl c , -script Ar script
+.Op Fl e , -env Ar value
.Op Fl f , -config Ar file
.Op Fl h , -hostname Ar hostname
.Op Fl i , -vendorclassid Ar vendorclassid
@@ -43,43 +44,61 @@
.Op Fl t , -timeout Ar seconds
.Op Fl u , -userclass Ar class
.Op Fl v , -vendor Ar code , Ar value
+.Op Fl y , -reboot Ar seconds
+.Op Fl z , -allowinterfaces Ar pattern
.Op Fl C , -nohook Ar hook
.Op Fl F , -fqdn Ar FQDN
.Op Fl I , -clientid Ar clientid
.Op Fl O , -nooption Ar option
.Op Fl Q , -require Ar option
-.Op Fl X , -blacklist Ar address
-.Ar interface
+.Op Fl S , -static Ar value
+.Op Fl W , -whitelist Ar address Ns Op Ar /cidr
+.Op Fl X , -blacklist Ar address Ns Op Ar /cidr
+.Op Fl Z , -denyinterfaces Ar pattern
+.Op interface
+.Op ...
.Nm
.Fl k , -release
+.Op interface
+.Nm
+.Fl U, -dumplease
.Ar interface
.Nm
.Fl x , -exit
-.Ar interface
+.Op interface
+.Nm
+.Fl v , -version
.Sh DESCRIPTION
.Nm
is an implementation of the DHCP client specified in
.Li RFC 2131 .
.Nm
gets the host information
-.Po
+.Po
IP address, routes, etc
.Pc
from a DHCP server and configures the network
.Ar interface
of the
-machine on which it is running.
+machine on which it is running.
.Nm
then runs the configuration script which writes DNS information to
.Xr resolvconf 8 ,
if available, otherwise directly to
.Pa /etc/resolv.conf .
-If the hostname is currenly blank, (null) or localhost then
+If the hostname is currently blank, (null) or localhost, or
+.Va force_hostname
+is YES or TRUE or 1 then
.Nm
sets the hostname to the one supplied by the DHCP server.
.Nm
then daemonises and waits for the lease renewal time to lapse.
-Then it attempts to renew its lease and reconfigure if the new lease changes.
+It will then attempt to renew its lease and reconfigure if the new lease
+changes.
+.Pp
+.Nm
+is also an implementation of the BOOTP client specified in
+.Li RFC 951 .
.Ss Local Link configuration
If
.Nm
@@ -98,6 +117,38 @@ installed which always defeats IPv4LL probing.
To disable this behaviour, you can use the
.Fl L , -noipv4ll
option.
+.Ss Multiple interfaces
+If a list of interfaces are given on the command line, then
+.Nm
+only works with those interfaces, otherwise
+.Nm
+discovers available Ethernet interfaces.
+If any interface reports a working carrier then
+.Nm
+will try and obtain a lease before forking to the background,
+otherwise it will fork right away.
+This behaviour can be modified with the
+.Fl b , -background
+and
+.Fl w , -waitip
+options.
+.Pp
+If a single interface is given then
+.Nm
+only works for that interface and runs as a separate instance.
+The
+.Fl w , -waitip
+option is enabled in this instance to maintain compatibility with older
+versions.
+.Pp
+Interfaces are preferred by carrier, DHCP lease/IPv4LL and then lowest metric.
+For systems that support route metrics, each route will be tagged with the
+metric, otherwise
+.Nm
+changes the routes to use the interface with the same route and the lowest
+metric.
+See options below for controlling which interfaces we allow and deny through
+the use of patterns.
.Ss Hooking into DHCP events
.Nm
runs
@@ -123,7 +174,7 @@ for details on how these scripts work.
.Nm
currently ignores the exit code of the script.
.Ss Fine tuning
-You can fine tune the behaviour of
+You can fine-tune the behaviour of
.Nm
with the following options:
.Bl -tag -width indent
@@ -137,25 +188,42 @@ Use this
instead of the default
.Pa @SCRIPT@ .
.It Fl d , -debug
-Echo debug and informational messages to the console.
-Subsequent debug options stop
+Echo debug messages to the stderr and syslog.
+.It Fl e , -env Ar value
+Push
+.Ar value
+to the environment for use in
+.Xr dhcpcd-run-hooks 8 .
+For example, you can force the hostname hook to always set the hostname with
+.Fl e
+.Va force_hostname=YES .
+.It Fl g , -reconfigure
+.Nm
+will re-apply IP address, routing and run
+.Xr dhcpcd-run-hooks 8
+for each interface.
+This is useful so that a 3rd party such as PPP or VPN can change the routing
+table and / or DNS, etc and then instruct
+.Nm
+to put things back afterwards.
.Nm
-from daemonising.
+does not read a new configuration when this happens - you should rebind if you
+need that functionality.
.It Fl f , -config Ar file
Specify a config to load instead of
.Pa @SYSCONFDIR@/dhcpcd.conf .
.Nm
always processes the config file before any command line options.
.It Fl h , -hostname Ar hostname
-By default,
-.Nm
-sends the current hostname to the DHCP server so it can register in DNS.
-You can use this option to specify the
+Sends
+.Ar hostname
+to the DHCP server so it can be registered in DNS.
+If
.Ar hostname
-sent, or an empty string to
-stop any
+is an empty string then the current system hostname is sent.
+If
.Ar hostname
-from being sent.
+is a FQDN (ie, contains a .) then it will be encoded as such.
.It Fl i , -vendorclassid Ar vendorclassid
Override the
.Ar vendorclassid
@@ -167,7 +235,7 @@ This causes an existing
.Nm
process running on the
.Ar interface
-to release its lease, deconfigure the
+to release its lease, de-configure the
.Ar interface
and then exit.
.Nm
@@ -177,61 +245,45 @@ Request a specific lease time in
.Ar seconds .
By default
.Nm
-does not request any lease time and leaves the it in the hands of the
+does not request any lease time and leaves it in the hands of the
DHCP server.
.It Fl m , -metric Ar metric
-Added routes will use the
-.Ar metric
-on systems where this is supported
-.Po
-presently only Linux
-.Pc .
-Route metrics allow the addition of routes to the same destination across
-different interfaces, the lower the metric the more it is preferred.
+Metrics are used to prefer an interface over another one, lowest wins.
+.Nm
+will supply a default metic of 200 +
+.Xr if_nametoindex 3 .
+An extra 100 will be added for wireless interfaces.
.It Fl o , -option Ar option
Request the DHCP
.Ar option
variable for use in
.Pa @SCRIPT@ .
.It Fl n , -rebind
-Notifies an existing
+Notifies
.Nm
-process running on the
-.Ar interface
-to rebind it's lease.
-.Nm
-will not re-configure itself or use any other command line arguments.
-.Nm
-will timeout the rebind after 30 seconds at which point the lease will be
-expired and
-.Nm
-will enter the discovery state to obtain a new lease.
-Use the
-.Fl t , -timeout
-option to change this.
+to reload its configuration and rebind its interfaces.
If
.Nm
is not running, then it starts up as normal.
-This option used to be renew, but rebind is more accurate as we need to
-broadcast the request instead of unicasting.
.It Fl p , -persistent
.Nm
-normally deconfigures the
+normally de-configures the
.Ar interface
and configuration when it exits.
-Sometimes, this isn't desirable if for example you have root mounted over NFS.
+Sometimes, this isn't desirable if, for example, you have root mounted over
+NFS.
You can use this option to stop this from happening.
.It Fl r , -request Op Ar address
.Nm
normally sends a DHCP DISCOVER to find servers to offer an address.
.Nm
then requests the address used.
-You can use this option to skip the BROADCAST step and just request the
+You can use this option to skip the DISCOVER phase and just request the
.Ar address .
The downside is if you request an
.Ar address
the DHCP server does not know about or the DHCP server is not
-authorative, it will remain silent.
+authoritative, it will remain silent.
In this situation, we go back to the init state and DISCOVER again.
If no
.Ar address
@@ -247,7 +299,7 @@ This does not get a lease as such, just notifies the DHCP server of the
in use.
You should also include the optional
.Ar cidr
-network number in-case the address is not already configured on the interface.
+network number in case the address is not already configured on the interface.
.Nm
remains running and pretends it has an infinite lease.
.Nm
@@ -268,39 +320,58 @@ to wait forever to get a lease.
.It Fl u , -userclass Ar class
Tags the DHCP message with the userclass
.Ar class .
-DHCP servers use this give members of the class DHCP options other than the
+DHCP servers use this to give members of the class DHCP options other than the
default, without having to know things like hardware address or hostname.
.It Fl v , -vendor Ar code , Ns Ar value
-Add an enscapulated vendor option.
+Add an encapsulated vendor option.
.Ar code
should be between 1 and 254 inclusive.
+To add a raw vendor string, omit
+.Ar code
+but keep the comma.
Examples.
.Pp
Set the vendor option 01 with an IP address.
.D1 dhcpcd \-v 01,192.168.0.2 eth0
Set the vendor option 02 with a hex code.
.D1 dhcpcd \-v 02,01:02:03:04:05 eth0
-Do the above and set a third option with a string and not an IP address.
-.D1 dhcpcd \-v 01,192.168.0.2 \-v 02,01:02:03:04:05 \-v 03,\e"192.168.0.2\e" eth0
+Set the vendor option 03 with an IP address as a string.
+.D1 dhcpcd \-v 03,\e"192.168.0.2\e" eth0
+Set un-encapsulated vendor option to hello world.
+.D1 dhcpcd \-v ,"hello world" eth0
+.It Fl v , -version
+Display both program version and copyright information.
+.Nm
+then exits before doing any configuration.
+.It Fl w , -waitip
+Wait for an address to be assigned before forking to the background.
.It Fl x , -exit
This will signal an existing
.Nm
process running on the
.Ar interface
-to deconfigure the
+to de-configure the
.Ar interface
and exit.
.Nm
then waits until this process has exited.
-.It Fl D , -duid
+.It Fl y , -reboot Ar seconds
+Allow
+.Ar reboot
+seconds before moving to the discover phase if we have an old lease to use.
+The default is 10 seconds.
+A setting of 0 seconds causes
+.Nm
+to skip the reboot phase and go straight into discover.
+.It Fl D , -duid
Generate an
.Li RFC 4361
compliant clientid.
-This requires persistent storage and not all DHCP servers work with it so it's
-not enabled by default.
+This requires persistent storage and not all DHCP servers work with it so it
+is not enabled by default.
.Nm
-generates the DUID and stores in it
-.Pa @SYSCONFDIR@/dhcpcd.duid
+generates the DUID and stores it in
+.Pa @SYSCONFDIR@/dhcpcd.duid .
This file should not be copied to other hosts.
.It Fl E , -lastlease
If
@@ -316,9 +387,6 @@ hostname.
Valid values for
.Ar fqdn
are disable, none, ptr and both.
-The current hostname or the hostname specified using the
-.Fl h , -hostname
-option must be a FQDN.
.Nm
itself never does any DNS updates.
.Nm
@@ -348,6 +416,14 @@ Quiet
.Nm
on the command line, only warnings and errors will be displayed.
The messages are still logged though.
+.It Fl z , -allowinterfaces Ar pattern
+When discovering interfaces, the interface name must match
+.Ar pattern
+which is a space or comma separated list of patterns passed to
+.Xr fnmatch 3 .
+If the same interface is matched in
+.Fl Z , -denyinterfaces
+then it is still denied.
.It Fl A , -noarp
Don't request or claim the address by ARP.
This also disables IPv4LL.
@@ -360,10 +436,22 @@ Don't run this hook script.
Matches full name, or prefixed with 2 numbers optionally ending with
.Pa .sh .
.Pp
-So to stop dhcpcd from touching your DNS or MTU settings you would do:-
+So to stop
+.Nm
+from touching your DNS or MTU settings you would do:-
.D1 dhcpcd -C resolv.conf -C mtu eth0
.It Fl G , -nogateway
Don't set any default routes.
+.It Fl H , -xidhwaddr
+Use the last four bytes of the hardware address as the DHCP xid instead
+of a randomly generated number.
+.It Fl J , -broadcast
+Instructs the DHCP server to broadcast replies back to the client.
+Normally this is only set for non Ethernet interfaces,
+such as FireWire and InfiniBand.
+In most instances,
+.Nm
+will set this automatically.
.It Fl K , -nolink
Don't receive link messages for carrier status.
You should only have to use this with buggy device drivers or running
@@ -379,24 +467,77 @@ configure the interface and routing.
Requires the
.Ar option
to be present in all DHCP messages, otherwise the message is ignored.
+To enforce that
+.Nm
+only responds to DHCP servers and not BOOTP servers, you can
+.Fl Q
+.Ar dhcp_message_type .
+.It Fl S, -static Ar value
+Configures a static
+.Ar value .
+If you set
+.Ic ip_address
+then
+.Nm
+will not attempt to obtain a lease and just use the value for the address with
+an infinite lease time.
+.Pp
+Here is an example which configures a static address, routes and dns.
+.D1 dhcpcd -S ip_address=192.168.0.10/24 \e
+.D1 -S routers=192.168.0.1 \e
+.D1 -S domain_name_servers=192.168.0.1 \e
+.D1 eth0
.It Fl T, -test
-On receipt of OFFER messages just call
+On receipt of DHCP messages just call
.Pa @SCRIPT@
-with the reason of TEST which echo's the DHCP variables found in the message
+with the reason of TEST which echos the DHCP variables found in the message
to the console.
The interface configuration isn't touched and neither are any configuration
files.
+To test INFORM the interface needs to be configured with the desired address
+before starting
+.Nm .
+.It Fl U, -dumplease Ar interface
+Dumps the last lease for the
+.Ar interface
+to stdout.
+.Ar interface
+could also be a path to a DHCP wire formatted file.
.It Fl V, -variables
Display a list of option codes and the associated variable for use in
.Xr dhcpcd-run-hooks 8 .
-.It Fl X, -blacklist Ar address
-Ignores all DHCP messages which have this
-.Ar address
-as the server ID.
-This may be expanded in future releases to ignore all packets
-matching either the IP or hardware
-.Ar address .
+Variables are prefixed with new_ and old_ unless the option number is -.
+Variables without an option are part of the DHCP message and cannot be
+directly requested.
+.It Fl W, -whitelist Ar address Ns Op /cidr
+Only accept packets from
+.Ar address Ns Op /cidr .
+.Fl X, -blacklist
+is ignored if
+.Fl W, -whitelist
+is set.
+.It Fl X, -blacklist Ar address Ns Op Ar /cidr
+Ignore all packets from
+.Ar address Ns Op Ar /cidr .
+.It Fl Z , -denyinterfaces Ar pattern
+When discovering interfaces, the interface name must not match
+.Ar pattern
+which is a space or comma separated list of patterns passed to
+.Xr fnmatch 3 .
.El
+.Sh 3RDPARTY LINK MANAGEMENT
+Some interfaces require configuration by 3rd parties, such as PPP or VPN.
+When an interface configuration in
+.Nm
+is marked as STATIC or INFORM without an address then
+.Nm
+will monitor the interface until an address is added or removed from it and
+act accordingly.
+For point to point interfaces (like PPP), a default route to its
+destination is automatically added to the configuration.
+If the point to point interface if configured for INFORM, then
+.Nm
+unicasts INFORM to the destination, otherwise it defaults to STATIC.
.Sh NOTES
.Nm
requires a Berkley Packet Filter, or BPF device on BSD based systems and a
@@ -409,7 +550,7 @@ If you always use the same options, put them here.
.It Pa @SYSCONFDIR@/dhcpcd.duid
Text file that holds the DUID used to identify the host.
.It Pa @SCRIPT@
-Bourne shell script that is run to configure or deconfigure an interface.
+Bourne shell script that is run to configure or de-configure an interface.
.It Pa @HOOKDIR@
A directory containing bourne shell scripts that are run by the above script.
Each script can be disabled by using the
@@ -418,6 +559,10 @@ option described above.
.It Pa @DBDIR@/dhcpcd\- Ns Ar interface Ns .lease
The actual DHCP message send by the server. We use this when reading the last
lease and use the files mtime as when it was issued.
+.It Pa /var/run/dhcpcd.pid
+Stores the PID of
+.Nm
+running on all interfaces.
.It Pa /var/run/dhcpcd\- Ns Ar interface Ns .pid
Stores the PID of
.Nm
@@ -429,10 +574,12 @@ running on the
.Xr dhcpcd-run-hooks 8 ,
.Xr resolv.conf 5 ,
.Xr resolvconf 8 ,
+.Xr if_nametoindex 3 ,
+.Xr fnmatch 3
.Sh STANDARDS
-RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396, RFC 3397,
-RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702.
+RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396,
+RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702.
.Sh AUTHORS
-.An Roy Marples <roy@marples.name>
+.An Roy Marples Aq roy@marples.name
.Sh BUGS
Please report them to http://roy.marples.name/projects/dhcpcd
diff --git a/dhcpcd.c b/dhcpcd.c
index 92ba92e..e984ceb 100644
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -25,31 +25,50 @@
* SUCH DAMAGE.
*/
-const char copyright[] = "Copyright (c) 2006-2008 Roy Marples";
+const char copyright[] = "Copyright (c) 2006-2010 Roy Marples";
#include <sys/file.h>
+#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <sys/types.h>
+#include <sys/uio.h>
#include <arpa/inet.h>
+#include <net/route.h>
+
+#ifdef __linux__
+# include <asm/types.h> /* for systems with broken headers */
+# include <linux/rtnetlink.h>
+#endif
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
+#include <limits.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
#include <unistd.h>
#include <time.h>
+#include "arp.h"
+#include "bind.h"
#include "config.h"
-#include "client.h"
+#include "common.h"
+#include "configure.h"
+#include "control.h"
#include "dhcpcd.h"
-#include "dhcp.h"
+#include "duid.h"
+#include "eloop.h"
+#include "if-options.h"
+#include "if-pref.h"
+#include "ipv4ll.h"
#include "net.h"
-#include "logger.h"
+#include "signals.h"
#ifdef ANDROID
#include <linux/capability.h>
@@ -58,90 +77,63 @@ const char copyright[] = "Copyright (c) 2006-2008 Roy Marples";
#include <private/android_filesystem_config.h>
#endif
-/* Don't set any optional arguments here so we retain POSIX
- * compatibility with getopt */
-#define OPTS "bc:df:h:i:kl:m:no:pqr:s:t:u:v:xABC:DEF:GI:KLO:Q:TVX:"
-
-static int doversion = 0;
-static int dohelp = 0;
-static const struct option longopts[] = {
- {"background", no_argument, NULL, 'b'},
- {"script", required_argument, NULL, 'c'},
- {"debug", no_argument, NULL, 'd'},
- {"config", required_argument, NULL, 'f'},
- {"hostname", optional_argument, NULL, 'h'},
- {"vendorclassid", optional_argument, NULL, 'i'},
- {"release", no_argument, NULL, 'k'},
- {"leasetime", required_argument, NULL, 'l'},
- {"metric", required_argument, NULL, 'm'},
- {"rebind", no_argument, NULL, 'n'},
- {"option", required_argument, NULL, 'o'},
- {"persistent", no_argument, NULL, 'p'},
- {"quiet", no_argument, NULL, 'q'},
- {"request", optional_argument, NULL, 'r'},
- {"inform", optional_argument, NULL, 's'},
- {"timeout", required_argument, NULL, 't'},
- {"userclass", required_argument, NULL, 'u'},
- {"vendor", required_argument, NULL, 'v'},
- {"exit", no_argument, NULL, 'x'},
- {"noarp", no_argument, NULL, 'A'},
- {"nobackground", no_argument, NULL, 'B'},
- {"nohook", required_argument, NULL, 'C'},
- {"duid", no_argument, NULL, 'D'},
- {"lastlease", no_argument, NULL, 'E'},
- {"fqdn", optional_argument, NULL, 'F'},
- {"nogateway", no_argument, NULL, 'G'},
- {"clientid", optional_argument, NULL, 'I'},
- {"nolink", no_argument, NULL, 'K'},
- {"noipv4ll", no_argument, NULL, 'L'},
- {"nooption", optional_argument, NULL, 'O'},
- {"require", required_argument, NULL, 'Q'},
- {"test", no_argument, NULL, 'T'},
- {"variables", no_argument, NULL, 'V'},
- {"blacklist", required_argument, NULL, 'X'},
- {"help", no_argument, &dohelp, 1},
- {"version", no_argument, &doversion, 1},
-#ifdef CMDLINE_COMPAT
- {"classid", optional_argument, NULL, 'i'},
- {"renew", no_argument, NULL, 'n'},
- {"nohostname", no_argument, NULL, 'H'},
- {"nomtu", no_argument, NULL, 'M'},
- {"nontp", no_argument, NULL, 'N'},
- {"nodns", no_argument, NULL, 'R'},
- {"msscr", no_argument, NULL, 'S'},
- {"nonis", no_argument, NULL, 'Y'},
-#endif
- {NULL, 0, NULL, '\0'}
+/* We should define a maximum for the NAK exponential backoff */
+#define NAKOFF_MAX 60
+
+/* Wait N nanoseconds between sending a RELEASE and dropping the address.
+ * This gives the kernel enough time to actually send it. */
+#define RELEASE_DELAY_S 0
+#define RELEASE_DELAY_NS 10000000
+
+int options = 0;
+int pidfd = -1;
+struct interface *ifaces = NULL;
+int ifac = 0;
+char **ifav = NULL;
+int ifdc = 0;
+char **ifdv = NULL;
+
+static char **margv;
+static int margc;
+static struct if_options *if_options;
+static char **ifv;
+static int ifc;
+static char *cffile;
+static char *pidfile;
+static int linkfd = -1;
+
+struct dhcp_op {
+ uint8_t value;
+ const char *name;
};
-#ifdef CMDLINE_COMPAT
-# define EXTRA_OPTS "HMNRSY"
-#endif
+static const struct dhcp_op dhcp_ops[] = {
+ { DHCP_DISCOVER, "DISCOVER" },
+ { DHCP_OFFER, "OFFER" },
+ { DHCP_REQUEST, "REQUEST" },
+ { DHCP_DECLINE, "DECLINE" },
+ { DHCP_ACK, "ACK" },
+ { DHCP_NAK, "NAK" },
+ { DHCP_RELEASE, "RELEASE" },
+ { DHCP_INFORM, "INFORM" },
+ { 0, NULL }
+};
-#ifndef EXTRA_OPTS
-# define EXTRA_OPTS
-#endif
+static void send_release(struct interface *);
-static int
-atoint(const char *s)
+static const char *
+get_dhcp_op(uint8_t type)
{
- char *t;
- long n;
-
- errno = 0;
- n = strtol(s, &t, 0);
- if ((errno != 0 && n == 0) || s == t ||
- (errno == ERANGE && (n == LONG_MAX || n == LONG_MIN)))
- {
- logger(LOG_ERR, "`%s' out of range", s);
- return -1;
- }
+ const struct dhcp_op *d;
- return (int)n;
+ for (d = dhcp_ops; d->name; d++)
+ if (d->value == type)
+ return d->name;
+ return NULL;
}
static pid_t
-read_pid(const char *pidfile)
+read_pid(void)
{
FILE *fp;
pid_t pid;
@@ -150,476 +142,1591 @@ read_pid(const char *pidfile)
errno = ENOENT;
return 0;
}
-
if (fscanf(fp, "%d", &pid) != 1)
pid = 0;
fclose(fp);
-
return pid;
}
static void
usage(void)
{
- printf("usage: "PACKAGE" [-dknpqxADEGHKLOTV] [-c script] [-f file ] [-h hostname]\n"
- " [-i classID ] [-l leasetime] [-m metric] [-o option] [-r ipaddr]\n"
- " [-s ipaddr] [-t timeout] [-u userclass] [-F none|ptr|both]\n"
- " [-I clientID] [-C hookscript] [-Q option] [-X ipaddr] <interface>\n");
+ printf("usage: "PACKAGE" [-dgknpqwxyADEGHJKLOTV] [-c script] [-f file]"
+ " [-e var=val]\n"
+ " [-h hostname] [-i classID ] [-l leasetime]"
+ " [-m metric] [-o option]\n"
+ " [-r ipaddr] [-s ipaddr] [-t timeout]"
+ " [-u userclass]\n"
+ " [-F none|ptr|both] [-I clientID] [-C hookscript]"
+ " [-Q option]\n"
+ " [-X ipaddr] <interface>\n");
}
-static char *
-add_environ(struct options *options, const char *value, int uniq)
+static void
+cleanup(void)
{
- char **newlist;
- char **lst = options->environ;
- size_t i = 0, l, lv;
- char *match = NULL, *p;
-
- match = xstrdup(value);
- p = strchr(match, '=');
- if (p)
- *p++ = '\0';
- l = strlen(match);
-
- while (lst && lst[i]) {
- if (match && strncmp(lst[i], match, l) == 0) {
- if (uniq) {
- free(lst[i]);
- lst[i] = xstrdup(value);
- } else {
- /* Append a space and the value to it */
- l = strlen(lst[i]);
- lv = strlen(p);
- lst[i] = xrealloc(lst[i], l + lv + 2);
- lst[i][l] = ' ';
- memcpy(lst[i] + l + 1, p, lv);
- lst[i][l + lv + 1] = '\0';
- }
- free(match);
- return lst[i];
+#ifdef DEBUG_MEMORY
+ struct interface *iface;
+ int i;
+
+ free_options(if_options);
+
+ while (ifaces) {
+ iface = ifaces;
+ ifaces = iface->next;
+ free_interface(iface);
+ }
+
+ for (i = 0; i < ifac; i++)
+ free(ifav[i]);
+ free(ifav);
+ for (i = 0; i < ifdc; i++)
+ free(ifdv[i]);
+ free(ifdv);
+#endif
+
+ if (linkfd != -1)
+ close(linkfd);
+ if (pidfd > -1) {
+ if (options & DHCPCD_MASTER) {
+ if (stop_control() == -1)
+ syslog(LOG_ERR, "stop_control: %m");
}
- i++;
+ close(pidfd);
+ unlink(pidfile);
}
+#ifdef DEBUG_MEMORY
+ free(pidfile);
+#endif
+}
+
+/* ARGSUSED */
+void
+handle_exit_timeout(_unused void *arg)
+{
+ int timeout;
- newlist = xrealloc(lst, sizeof(char *) * (i + 2));
- newlist[i] = xstrdup(value);
- newlist[i + 1] = NULL;
- options->environ = newlist;
- free(match);
- return newlist[i];
+ syslog(LOG_ERR, "timed out");
+ if (!(options & DHCPCD_TIMEOUT_IPV4LL)) {
+ if (options & DHCPCD_MASTER) {
+ daemonise();
+ return;
+ } else
+ exit(EXIT_FAILURE);
+ }
+ options &= ~DHCPCD_TIMEOUT_IPV4LL;
+ timeout = (PROBE_NUM * PROBE_MAX) + PROBE_WAIT + 1;
+ syslog(LOG_WARNING, "allowing %d seconds for IPv4LL timeout", timeout);
+ add_timeout_sec(timeout, handle_exit_timeout, NULL);
}
-#define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0)
-static ssize_t
-parse_string_hwaddr(char *sbuf, ssize_t slen, char *str, int clid)
+void
+drop_config(struct interface *iface, const char *reason)
{
- ssize_t l;
- char *p;
- int i;
- char c[4];
-
- /* If surrounded by quotes then it's a string */
- if (*str == '"') {
- str++;
- l = strlen(str);
- p = str + l - 1;
- if (*p == '"')
- *p = '\0';
+ free(iface->state->old);
+ iface->state->old = iface->state->new;
+ iface->state->new = NULL;
+ iface->state->reason = reason;
+ configure(iface);
+ free(iface->state->old);
+ iface->state->old = NULL;
+ iface->state->lease.addr.s_addr = 0;
+}
+
+struct interface *
+find_interface(const char *ifname)
+{
+ struct interface *ifp;
+
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ if (strcmp(ifp->name, ifname) == 0)
+ return ifp;
+ return NULL;
+}
+
+static void
+stop_interface(struct interface *iface)
+{
+ struct interface *ifp, *ifl = NULL;
+
+ syslog(LOG_INFO, "%s: removing interface", iface->name);
+ if (strcmp(iface->state->reason, "RELEASE") != 0)
+ drop_config(iface, "STOP");
+ close_sockets(iface);
+ delete_timeout(NULL, iface);
+ for (ifp = ifaces; ifp; ifp = ifp->next) {
+ if (ifp == iface)
+ break;
+ ifl = ifp;
+ }
+ if (ifl)
+ ifl->next = ifp->next;
+ else
+ ifaces = ifp->next;
+ free_interface(ifp);
+ if (!(options & (DHCPCD_MASTER | DHCPCD_TEST)))
+ exit(EXIT_FAILURE);
+}
+
+static uint32_t
+dhcp_xid(struct interface *iface)
+{
+ uint32_t xid;
+
+ if (iface->state->options->options & DHCPCD_XID_HWADDR &&
+ iface->hwlen >= sizeof(xid))
+ /* The lower bits are probably more unique on the network */
+ memcpy(&xid, (iface->hwaddr + iface->hwlen) - sizeof(xid),
+ sizeof(xid));
+ else
+ xid = arc4random();
+
+ return xid;
+}
+
+static void
+send_message(struct interface *iface, int type,
+ void (*callback)(void *))
+{
+ struct if_state *state = iface->state;
+ struct if_options *ifo = state->options;
+ struct dhcp_message *dhcp;
+ uint8_t *udp;
+ ssize_t len, r;
+ struct in_addr from, to;
+ in_addr_t a = 0;
+ struct timeval tv;
+
+ if (!callback)
+ syslog(LOG_DEBUG, "%s: sending %s with xid 0x%x",
+ iface->name, get_dhcp_op(type), state->xid);
+ else {
+ if (state->interval == 0)
+ state->interval = 4;
+ else {
+ state->interval *= 2;
+ if (state->interval > 64)
+ state->interval = 64;
+ }
+ tv.tv_sec = state->interval + DHCP_RAND_MIN;
+ tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+ syslog(LOG_DEBUG,
+ "%s: sending %s (xid 0x%x), next in %0.2f seconds",
+ iface->name, get_dhcp_op(type), state->xid,
+ timeval_to_double(&tv));
+ }
+
+ /* Ensure sockets are open. */
+ open_sockets(iface);
+
+ /* If we couldn't open a UDP port for our IP address
+ * then we cannot renew.
+ * This could happen if our IP was pulled out from underneath us.
+ * Also, we should not unicast from a BOOTP lease. */
+ if (iface->udp_fd == -1 ||
+ (!(ifo->options & DHCPCD_INFORM) && is_bootp(iface->state->new)))
+ {
+ a = iface->addr.s_addr;
+ iface->addr.s_addr = 0;
+ }
+ len = make_message(&dhcp, iface, type);
+ if (a)
+ iface->addr.s_addr = a;
+ from.s_addr = dhcp->ciaddr;
+ if (from.s_addr)
+ to.s_addr = state->lease.server.s_addr;
+ else
+ to.s_addr = 0;
+ if (to.s_addr && to.s_addr != INADDR_BROADCAST) {
+ r = send_packet(iface, to, (uint8_t *)dhcp, len);
+ if (r == -1) {
+ syslog(LOG_ERR, "%s: send_packet: %m", iface->name);
+ close_sockets(iface);
+ }
} else {
- l = hwaddr_aton(NULL, str);
- if (l > 1) {
- if (l > slen) {
- errno = ENOBUFS;
- return -1;
- }
- hwaddr_aton((uint8_t *)sbuf, str);
- return l;
+ len = make_udp_packet(&udp, (uint8_t *)dhcp, len, from, to);
+ r = send_raw_packet(iface, ETHERTYPE_IP, udp, len);
+ free(udp);
+ /* If we failed to send a raw packet this normally means
+ * we don't have the ability to work beneath the IP layer
+ * for this interface.
+ * As such we remove it from consideration without actually
+ * stopping the interface. */
+ if (r == -1) {
+ syslog(LOG_ERR, "%s: send_raw_packet: %m", iface->name);
+ if (!(options & DHCPCD_TEST))
+ drop_config(iface, "FAIL");
+ close_sockets(iface);
+ delete_timeout(NULL, iface);
+ callback = NULL;
}
}
+ free(dhcp);
+
+ /* Even if we fail to send a packet we should continue as we are
+ * as our failure timeouts will change out codepath when needed. */
+ if (callback)
+ add_timeout_tv(&tv, callback, iface);
+}
- /* Process escapes */
- l = 0;
- /* If processing a string on the clientid, first byte should be
- * 0 to indicate a non hardware type */
- if (clid && *str) {
- *sbuf++ = 0;
- l++;
+static void
+send_inform(void *arg)
+{
+ send_message((struct interface *)arg, DHCP_INFORM, send_inform);
+}
+
+static void
+send_discover(void *arg)
+{
+ send_message((struct interface *)arg, DHCP_DISCOVER, send_discover);
+}
+
+static void
+send_request(void *arg)
+{
+ send_message((struct interface *)arg, DHCP_REQUEST, send_request);
+}
+
+static void
+send_renew(void *arg)
+{
+ send_message((struct interface *)arg, DHCP_REQUEST, send_renew);
+}
+
+static void
+send_rebind(void *arg)
+{
+ send_message((struct interface *)arg, DHCP_REQUEST, send_rebind);
+}
+
+void
+start_expire(void *arg)
+{
+ struct interface *iface = arg;
+
+ iface->state->interval = 0;
+ if (iface->addr.s_addr == 0) {
+ /* We failed to reboot, so enter discovery. */
+ iface->state->lease.addr.s_addr = 0;
+ start_discover(iface);
+ return;
}
- c[3] = '\0';
- while (*str) {
- if (++l > slen) {
- errno = ENOBUFS;
- return -1;
+
+ syslog(LOG_ERR, "%s: lease expired", iface->name);
+ delete_timeout(NULL, iface);
+ drop_config(iface, "EXPIRE");
+ unlink(iface->leasefile);
+ if (iface->carrier != LINK_DOWN)
+ start_interface(iface);
+}
+
+static void
+log_dhcp(int lvl, const char *msg,
+ const struct interface *iface, const struct dhcp_message *dhcp,
+ const struct in_addr *from)
+{
+ const char *tfrom;
+ char *a;
+ struct in_addr addr;
+ int r;
+
+ if (strcmp(msg, "NAK:") == 0)
+ a = get_option_string(dhcp, DHO_MESSAGE);
+ else if (dhcp->yiaddr != 0) {
+ addr.s_addr = dhcp->yiaddr;
+ a = xstrdup(inet_ntoa(addr));
+ } else
+ a = NULL;
+
+ tfrom = "from";
+ r = get_option_addr(&addr, dhcp, DHO_SERVERID);
+ if (dhcp->servername[0] && r == 0)
+ syslog(lvl, "%s: %s %s %s %s `%s'", iface->name, msg, a,
+ tfrom, inet_ntoa(addr), dhcp->servername);
+ else {
+ if (r != 0) {
+ tfrom = "via";
+ addr = *from;
}
- if (*str == '\\') {
- str++;
- switch(*str++) {
- case '\0':
- break;
- case 'b':
- *sbuf++ = '\b';
- break;
- case 'n':
- *sbuf++ = '\n';
- break;
- case 'r':
- *sbuf++ = '\r';
- break;
- case 't':
- *sbuf++ = '\t';
- break;
- case 'x':
- /* Grab a hex code */
- c[1] = '\0';
- for (i = 0; i < 2; i++) {
- if (isxdigit((unsigned char)*str) == 0)
- break;
- c[i] = *str++;
- }
- if (c[1] != '\0') {
- c[2] = '\0';
- *sbuf++ = strtol(c, NULL, 16);
- } else
- l--;
- break;
- case '0':
- /* Grab an octal code */
- c[2] = '\0';
- for (i = 0; i < 3; i++) {
- if (*str < '0' || *str > '7')
- break;
- c[i] = *str++;
- }
- if (c[2] != '\0') {
- i = strtol(c, NULL, 8);
- if (i > 255)
- i = 255;
- *sbuf ++= i;
- } else
- l--;
- break;
- default:
- *sbuf++ = *str++;
- }
- } else
- *sbuf++ = *str++;
+ if (a == NULL)
+ syslog(lvl, "%s: %s %s %s",
+ iface->name, msg, tfrom, inet_ntoa(addr));
+ else
+ syslog(lvl, "%s: %s %s %s %s",
+ iface->name, msg, a, tfrom, inet_ntoa(addr));
}
- return l;
+ free(a);
}
static int
-parse_option(int opt, char *oarg, struct options *options)
+blacklisted_ip(const struct if_options *ifo, in_addr_t addr)
{
- int i;
- char *p;
- ssize_t s;
+ size_t i;
+
+ for (i = 0; i < ifo->blacklist_len; i += 2)
+ if (ifo->blacklist[i] == (addr & ifo->blacklist[i + 1]))
+ return 1;
+ return 0;
+}
+
+static int
+whitelisted_ip(const struct if_options *ifo, in_addr_t addr)
+{
+ size_t i;
+
+ if (ifo->whitelist_len == 0)
+ return -1;
+ for (i = 0; i < ifo->whitelist_len; i += 2)
+ if (ifo->whitelist[i] == (addr & ifo->whitelist[i + 1]))
+ return 1;
+ return 0;
+}
+
+static void
+handle_dhcp(struct interface *iface, struct dhcp_message **dhcpp, const struct in_addr *from)
+{
+ struct if_state *state = iface->state;
+ struct if_options *ifo = state->options;
+ struct dhcp_message *dhcp = *dhcpp;
+ struct dhcp_lease *lease = &state->lease;
+ uint8_t type, tmp;
struct in_addr addr;
+ size_t i;
- switch(opt) {
- case 'b':
- options->options |= DHCPCD_BACKGROUND;
- break;
- case 'c':
- strlcpy(options->script, oarg, sizeof(options->script));
- break;
- case 'h':
- if (oarg)
- s = parse_string(options->hostname,
- HOSTNAME_MAX_LEN, oarg);
- else
- s = 0;
- if (s == -1) {
- logger(LOG_ERR, "hostname: %s", strerror(errno));
- return -1;
+ /* reset the message counter */
+ state->interval = 0;
+
+ /* We may have found a BOOTP server */
+ if (get_option_uint8(&type, dhcp, DHO_MESSAGETYPE) == -1)
+ type = 0;
+
+ if (type == DHCP_NAK) {
+ /* For NAK, only check if we require the ServerID */
+ if (has_option_mask(ifo->requiremask, DHO_SERVERID) &&
+ get_option_addr(&addr, dhcp, DHO_SERVERID) == -1)
+ {
+ log_dhcp(LOG_WARNING, "reject NAK", iface, dhcp, from);
+ return;
}
- if (s != 0 && options->hostname[0] == '.') {
- logger(LOG_ERR, "hostname cannot begin with a .");
- return -1;
+ /* We should restart on a NAK */
+ log_dhcp(LOG_WARNING, "NAK:", iface, dhcp, from);
+ if (!(options & DHCPCD_TEST)) {
+ drop_config(iface, "NAK");
+ unlink(iface->leasefile);
}
- options->hostname[s] = '\0';
- break;
- case 'i':
- if (oarg)
- s = parse_string((char *)options->vendorclassid + 1,
- VENDORCLASSID_MAX_LEN, oarg);
- else
- s = 0;
- if (s == -1) {
- logger(LOG_ERR, "vendorclassid: %s", strerror(errno));
- return -1;
+ close_sockets(iface);
+ /* If we constantly get NAKS then we should slowly back off */
+ add_timeout_sec(state->nakoff, start_interface, iface);
+ state->nakoff *= 2;
+ if (state->nakoff > NAKOFF_MAX)
+ state->nakoff = NAKOFF_MAX;
+ return;
+ }
+
+ /* Ensure that all required options are present */
+ for (i = 1; i < 255; i++) {
+ if (has_option_mask(ifo->requiremask, i) &&
+ get_option_uint8(&tmp, dhcp, i) != 0)
+ {
+ /* If we are bootp, then ignore the need for serverid.
+ * To ignore bootp, require dhcp_message_type instead. */
+ if (type == 0 && i == DHO_SERVERID)
+ continue;
+ log_dhcp(LOG_WARNING, "reject DHCP", iface, dhcp, from);
+ return;
}
- *options->vendorclassid = (uint8_t)s;
- break;
- case 'l':
- if (*oarg == '-') {
- logger(LOG_ERR,
- "leasetime must be a positive value");
- return -1;
+ }
+
+ /* No NAK, so reset the backoff */
+ state->nakoff = 1;
+
+ if ((type == 0 || type == DHCP_OFFER) &&
+ state->state == DHS_DISCOVER)
+ {
+ lease->frominfo = 0;
+ lease->addr.s_addr = dhcp->yiaddr;
+ lease->cookie = dhcp->cookie;
+ if (type == 0 ||
+ get_option_addr(&lease->server, dhcp, DHO_SERVERID) != 0)
+ lease->server.s_addr = INADDR_ANY;
+ log_dhcp(LOG_INFO, "offered", iface, dhcp, from);
+ free(state->offer);
+ state->offer = dhcp;
+ *dhcpp = NULL;
+ if (options & DHCPCD_TEST) {
+ free(state->old);
+ state->old = state->new;
+ state->new = state->offer;
+ state->offer = NULL;
+ state->reason = "TEST";
+ run_script(iface);
+ exit(EXIT_SUCCESS);
}
- errno = 0;
- options->leasetime = (uint32_t)strtol(oarg, NULL, 0);
- if (errno == EINVAL || errno == ERANGE) {
- logger(LOG_ERR, "`%s' out of range", oarg);
- return -1;
+ delete_timeout(send_discover, iface);
+ /* We don't request BOOTP addresses */
+ if (type) {
+ /* We used to ARP check here, but that seems to be in
+ * violation of RFC2131 where it only describes
+ * DECLINE after REQUEST.
+ * It also seems that some MS DHCP servers actually
+ * ignore DECLINE if no REQUEST, ie we decline a
+ * DISCOVER. */
+ start_request(iface);
+ return;
}
- break;
- case 'm':
- options->metric = atoint(oarg);
- if (options->metric < 0) {
- logger(LOG_ERR, "metric must be a positive value");
- return -1;
+ }
+
+ if (type) {
+ if (type == DHCP_OFFER) {
+ log_dhcp(LOG_INFO, "ignoring offer of",
+ iface, dhcp, from);
+ return;
}
- break;
- case 'o':
- if (make_option_mask(options->requestmask, &oarg, 1) != 0) {
- logger(LOG_ERR, "unknown option `%s'", oarg);
- return -1;
+
+ /* We should only be dealing with acks */
+ if (type != DHCP_ACK) {
+ log_dhcp(LOG_ERR, "not ACK or OFFER",
+ iface, dhcp, from);
+ return;
}
- break;
- case 'p':
- options->options |= DHCPCD_PERSISTENT;
- break;
- case 'q':
- setloglevel(LOG_WARNING);
- break;
- case 's':
- options->options |= DHCPCD_INFORM;
- options->options |= DHCPCD_PERSISTENT;
- options->options &= ~DHCPCD_ARP;
- if (!oarg || *oarg == '\0') {
- options->request_address.s_addr = 0;
+
+ if (!(ifo->options & DHCPCD_INFORM))
+ log_dhcp(LOG_INFO, "acknowledged", iface, dhcp, from);
+ }
+
+ /* BOOTP could have already assigned this above, so check we still
+ * have a pointer. */
+ if (*dhcpp) {
+ free(state->offer);
+ state->offer = dhcp;
+ *dhcpp = NULL;
+ }
+
+ lease->frominfo = 0;
+ delete_timeout(NULL, iface);
+
+ /* We now have an offer, so close the DHCP sockets.
+ * This allows us to safely ARP when broken DHCP servers send an ACK
+ * follows by an invalid NAK. */
+ close_sockets(iface);
+
+ if (ifo->options & DHCPCD_ARP &&
+ iface->addr.s_addr != state->offer->yiaddr)
+ {
+ /* If the interface already has the address configured
+ * then we can't ARP for duplicate detection. */
+ addr.s_addr = state->offer->yiaddr;
+ if (has_address(iface->name, &addr, NULL) != 1) {
+ state->claims = 0;
+ state->probes = 0;
+ state->conflicts = 0;
+ state->state = DHS_PROBE;
+ send_arp_probe(iface);
+ return;
+ }
+ }
+
+ bind_interface(iface);
+}
+
+static void
+handle_dhcp_packet(void *arg)
+{
+ struct interface *iface = arg;
+ uint8_t *packet;
+ struct dhcp_message *dhcp = NULL;
+ const uint8_t *pp;
+ ssize_t bytes;
+ struct in_addr from;
+ int i;
+
+ /* We loop through until our buffer is empty.
+ * The benefit is that if we get >1 DHCP packet in our buffer and
+ * the first one fails for any reason, we can use the next. */
+ packet = xmalloc(udp_dhcp_len);
+ for(;;) {
+ bytes = get_raw_packet(iface, ETHERTYPE_IP,
+ packet, udp_dhcp_len);
+ if (bytes == 0 || bytes == -1)
break;
- } else {
- if ((p = strchr(oarg, '/'))) {
- /* nullify the slash, so the -r option
- * can read the address */
- *p++ = '\0';
- if (sscanf(p, "%d", &i) != 1 ||
- inet_cidrtoaddr(i, &options->request_netmask) != 0)
- {
- logger(LOG_ERR,
- "`%s' is not a valid CIDR",
- p);
- return -1;
- }
- }
+ if (valid_udp_packet(packet, bytes, &from) == -1) {
+ syslog(LOG_ERR, "%s: invalid UDP packet from %s",
+ iface->name, inet_ntoa(from));
+ continue;
}
- /* FALLTHROUGH */
- case 'r':
- if (!(options->options & DHCPCD_INFORM))
- options->options |= DHCPCD_REQUEST;
- if (oarg && !inet_aton(oarg, &options->request_address)) {
- logger(LOG_ERR, "`%s' is not a valid IP address",
- oarg);
- return -1;
+ i = whitelisted_ip(iface->state->options, from.s_addr);
+ if (i == 0) {
+ syslog(LOG_WARNING,
+ "%s: non whitelisted DHCP packet from %s",
+ iface->name, inet_ntoa(from));
+ continue;
+ } else if (i != 1 &&
+ blacklisted_ip(iface->state->options, from.s_addr) == 1)
+ {
+ syslog(LOG_WARNING,
+ "%s: blacklisted DHCP packet from %s",
+ iface->name, inet_ntoa(from));
+ continue;
}
- break;
- case 't':
- options->timeout = atoint(oarg);
- if (options->timeout < 0) {
- logger (LOG_ERR, "timeout must be a positive value");
- return -1;
+ if (iface->flags & IFF_POINTOPOINT &&
+ iface->dst.s_addr != from.s_addr)
+ {
+ syslog(LOG_WARNING,
+ "%s: server %s is not destination",
+ iface->name, inet_ntoa(from));
}
- break;
- case 'u':
- s = USERCLASS_MAX_LEN - options->userclass[0] - 1;
- s = parse_string((char *)options->userclass + options->userclass[0] + 2,
- s, oarg);
- if (s == -1) {
- logger(LOG_ERR, "userclass: %s", strerror(errno));
- return -1;
+ bytes = get_udp_data(&pp, packet);
+ if ((size_t)bytes > sizeof(*dhcp)) {
+ syslog(LOG_ERR,
+ "%s: packet greater than DHCP size from %s",
+ iface->name, inet_ntoa(from));
+ continue;
+ }
+ if (!dhcp)
+ dhcp = xzalloc(sizeof(*dhcp));
+ memcpy(dhcp, pp, bytes);
+ if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
+ syslog(LOG_DEBUG, "%s: bogus cookie from %s",
+ iface->name, inet_ntoa(from));
+ continue;
+ }
+ /* Ensure it's the right transaction */
+ if (iface->state->xid != dhcp->xid) {
+ syslog(LOG_DEBUG,
+ "%s: wrong xid 0x%x (expecting 0x%x) from %s",
+ iface->name, dhcp->xid, iface->state->xid,
+ inet_ntoa(from));
+ continue;
}
- if (s != 0) {
- options->userclass[options->userclass[0] + 1] = s;
- options->userclass[0] += s + 1;
+ /* Ensure packet is for us */
+ if (iface->hwlen <= sizeof(dhcp->chaddr) &&
+ memcmp(dhcp->chaddr, iface->hwaddr, iface->hwlen))
+ {
+ syslog(LOG_DEBUG, "%s: xid 0x%x is not for hwaddr %s",
+ iface->name, dhcp->xid,
+ hwaddr_ntoa(dhcp->chaddr, sizeof(dhcp->chaddr)));
+ continue;
}
+ handle_dhcp(iface, &dhcp, &from);
+ if (iface->raw_fd == -1)
+ break;
+ }
+ free(packet);
+ free(dhcp);
+}
+
+static void
+send_release(struct interface *iface)
+{
+ struct timespec ts;
+
+ if (iface->state->new != NULL &&
+ iface->state->new->cookie == htonl(MAGIC_COOKIE))
+ {
+ syslog(LOG_INFO, "%s: releasing lease of %s",
+ iface->name, inet_ntoa(iface->state->lease.addr));
+ iface->state->xid = dhcp_xid(iface);
+ send_message(iface, DHCP_RELEASE, NULL);
+ /* Give the packet a chance to go before dropping the ip */
+ ts.tv_sec = RELEASE_DELAY_S;
+ ts.tv_nsec = RELEASE_DELAY_NS;
+ nanosleep(&ts, NULL);
+ drop_config(iface, "RELEASE");
+ }
+ unlink(iface->leasefile);
+}
+
+void
+send_decline(struct interface *iface)
+{
+ send_message(iface, DHCP_DECLINE, NULL);
+}
+
+static void
+configure_interface1(struct interface *iface)
+{
+ struct if_state *ifs = iface->state;
+ struct if_options *ifo = ifs->options;
+ uint8_t *duid;
+ size_t len = 0, ifl;
+
+ /* Do any platform specific configuration */
+ if_conf(iface);
+
+ if (iface->flags & IFF_POINTOPOINT && !(ifo->options & DHCPCD_INFORM))
+ ifo->options |= DHCPCD_STATIC;
+ if (iface->flags & IFF_NOARP ||
+ ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))
+ ifo->options &= ~(DHCPCD_ARP | DHCPCD_IPV4LL);
+ if (ifo->options & DHCPCD_LINK && carrier_status(iface) == -1)
+ ifo->options &= ~DHCPCD_LINK;
+
+ if (ifo->metric != -1)
+ iface->metric = ifo->metric;
+
+ /* If we haven't specified a ClientID and our hardware address
+ * length is greater than DHCP_CHADDR_LEN then we enforce a ClientID
+ * of the hardware address family and the hardware address. */
+ if (iface->hwlen > DHCP_CHADDR_LEN)
+ ifo->options |= DHCPCD_CLIENTID;
+
+ /* Firewire and InfiniBand interfaces require ClientID and
+ * the broadcast option being set. */
+ switch (iface->family) {
+ case ARPHRD_IEEE1394: /* FALLTHROUGH */
+ case ARPHRD_INFINIBAND:
+ ifo->options |= DHCPCD_CLIENTID | DHCPCD_BROADCAST;
break;
- case 'v':
- p = strchr(oarg, ',');
- if (!p || !p[1]) {
- logger(LOG_ERR, "invalid vendor format");
- return -1;
+ }
+
+ free(iface->clientid);
+ iface->clientid = NULL;
+ if (*ifo->clientid) {
+ iface->clientid = xmalloc(ifo->clientid[0] + 1);
+ memcpy(iface->clientid, ifo->clientid, ifo->clientid[0] + 1);
+ } else if (ifo->options & DHCPCD_CLIENTID) {
+ if (ifo->options & DHCPCD_DUID) {
+ duid = xmalloc(DUID_LEN);
+ if ((len = get_duid(duid, iface)) == 0)
+ syslog(LOG_ERR, "get_duid: %m");
}
- *p = '\0';
- i = atoint(oarg);
- oarg = p + 1;
- if (i < 1 || i > 254) {
- logger(LOG_ERR, "vendor option should be between"
- " 1 and 254 inclusive");
- return -1;
+ if (len > 0) {
+ iface->clientid = xmalloc(len + 6);
+ iface->clientid[0] = len + 5;
+ iface->clientid[1] = 255; /* RFC 4361 */
+ ifl = strlen(iface->name);
+ if (ifl < 5) {
+ memcpy(iface->clientid + 2, iface->name, ifl);
+ if (ifl < 4)
+ memset(iface->clientid + 2 + ifl,
+ 0, 4 - ifl);
+ } else {
+ ifl = htonl(if_nametoindex(iface->name));
+ memcpy(iface->clientid + 2, &ifl, 4);
+ }
+ } else if (len == 0) {
+ len = iface->hwlen + 1;
+ iface->clientid = xmalloc(len + 1);
+ iface->clientid[0] = len;
+ iface->clientid[1] = iface->family;
+ memcpy(iface->clientid + 2, iface->hwaddr,
+ iface->hwlen);
}
- s = VENDOR_MAX_LEN - options->vendor[0] - 2;
- if (inet_aton(oarg, &addr) == 1) {
- if (s < 6) {
- s = -1;
- errno = ENOBUFS;
- } else
- memcpy(options->vendor + options->vendor[0] + 3,
- &addr.s_addr, sizeof(addr.s_addr));
- } else {
- s = parse_string((char *)options->vendor + options->vendor[0] + 3,
- s, oarg);
+ }
+ if (ifo->options & DHCPCD_CLIENTID)
+ syslog(LOG_DEBUG, "%s: using ClientID %s", iface->name,
+ hwaddr_ntoa(iface->clientid + 1, *iface->clientid));
+ else
+ syslog(LOG_DEBUG, "%s: using hwaddr %s", iface->name,
+ hwaddr_ntoa(iface->hwaddr, iface->hwlen));
+}
+
+int
+select_profile(struct interface *iface, const char *profile)
+{
+ struct if_options *ifo;
+ int ret;
+
+ ret = 0;
+ ifo = read_config(cffile, iface->name, iface->ssid, profile);
+ if (ifo == NULL) {
+ syslog(LOG_DEBUG, "%s: no profile %s", iface->name, profile);
+ ret = -1;
+ goto exit;
+ }
+ if (profile != NULL) {
+ strlcpy(iface->state->profile, profile,
+ sizeof(iface->state->profile));
+ syslog(LOG_INFO, "%s: selected profile %s",
+ iface->name, profile);
+ } else
+ *iface->state->profile = '\0';
+ free_options(iface->state->options);
+ iface->state->options = ifo;
+
+exit:
+ if (profile)
+ configure_interface1(iface);
+ return ret;
+}
+
+static void
+start_fallback(void *arg)
+{
+ struct interface *iface;
+
+ iface = (struct interface *)arg;
+ select_profile(iface, iface->state->options->fallback);
+ start_interface(iface);
+}
+
+static void
+configure_interface(struct interface *iface, int argc, char **argv)
+{
+ select_profile(iface, NULL);
+ add_options(iface->state->options, argc, argv);
+ configure_interface1(iface);
+}
+
+static void
+handle_carrier(const char *ifname)
+{
+ struct interface *iface;
+ int carrier;
+
+ if (!(options & DHCPCD_LINK))
+ return;
+ for (iface = ifaces; iface; iface = iface->next)
+ if (strcmp(iface->name, ifname) == 0)
+ break;
+ if (!iface || !(iface->state->options->options & DHCPCD_LINK))
+ return;
+ carrier = carrier_status(iface);
+ if (carrier == -1)
+ syslog(LOG_ERR, "%s: carrier_status: %m", ifname);
+ else if (carrier == 0 || !(iface->flags & IFF_RUNNING)) {
+ if (iface->carrier != LINK_DOWN) {
+ iface->carrier = LINK_DOWN;
+ syslog(LOG_INFO, "%s: carrier lost", iface->name);
+ close_sockets(iface);
+ delete_timeouts(iface, start_expire, NULL);
+ drop_config(iface, "NOCARRIER");
}
- if (s == -1) {
- logger(LOG_ERR, "vendor: %s", strerror(errno));
- return -1;
+ } else if (carrier == 1 && (iface->flags & IFF_RUNNING)) {
+ if (iface->carrier != LINK_UP) {
+ iface->carrier = LINK_UP;
+ syslog(LOG_INFO, "%s: carrier acquired", iface->name);
+ if (iface->wireless)
+ getifssid(iface->name, iface->ssid);
+ configure_interface(iface, margc, margv);
+ iface->state->interval = 0;
+ iface->state->reason = "CARRIER";
+ run_script(iface);
+ start_interface(iface);
}
- if (s != 0) {
- options->vendor[options->vendor[0] + 1] = i;
- options->vendor[options->vendor[0] + 2] = s;
- options->vendor[0] += s + 2;
+ }
+}
+
+void
+start_discover(void *arg)
+{
+ struct interface *iface = arg;
+ struct if_options *ifo = iface->state->options;
+
+ iface->state->state = DHS_DISCOVER;
+ iface->state->xid = dhcp_xid(iface);
+ delete_timeout(NULL, iface);
+ if (ifo->fallback)
+ add_timeout_sec(ifo->timeout, start_fallback, iface);
+ else if (ifo->options & DHCPCD_IPV4LL &&
+ !IN_LINKLOCAL(htonl(iface->addr.s_addr)))
+ {
+ if (IN_LINKLOCAL(htonl(iface->state->fail.s_addr)))
+ add_timeout_sec(RATE_LIMIT_INTERVAL, start_ipv4ll, iface);
+ else
+ add_timeout_sec(ifo->timeout, start_ipv4ll, iface);
+ }
+ syslog(LOG_INFO, "%s: broadcasting for a lease", iface->name);
+ send_discover(iface);
+}
+
+void
+start_request(void *arg)
+{
+ struct interface *iface = arg;
+
+ iface->state->state = DHS_REQUEST;
+ send_request(iface);
+}
+
+void
+start_renew(void *arg)
+{
+ struct interface *iface = arg;
+
+ syslog(LOG_INFO, "%s: renewing lease of %s",
+ iface->name, inet_ntoa(iface->state->lease.addr));
+ iface->state->state = DHS_RENEW;
+ iface->state->xid = dhcp_xid(iface);
+ send_renew(iface);
+}
+
+void
+start_rebind(void *arg)
+{
+ struct interface *iface = arg;
+
+ syslog(LOG_ERR, "%s: failed to renew, attempting to rebind",
+ iface->name);
+ iface->state->state = DHS_REBIND;
+ delete_timeout(send_renew, iface);
+ iface->state->lease.server.s_addr = 0;
+ send_rebind(iface);
+}
+
+static void
+start_timeout(void *arg)
+{
+ struct interface *iface = arg;
+
+ bind_interface(iface);
+ iface->state->interval = 0;
+ start_discover(iface);
+}
+
+static struct dhcp_message *
+dhcp_message_new(struct in_addr *addr, struct in_addr *mask)
+{
+ struct dhcp_message *dhcp;
+ uint8_t *p;
+
+ dhcp = xzalloc(sizeof(*dhcp));
+ dhcp->yiaddr = addr->s_addr;
+ p = dhcp->options;
+ if (mask && mask->s_addr != INADDR_ANY) {
+ *p++ = DHO_SUBNETMASK;
+ *p++ = sizeof(mask->s_addr);
+ memcpy(p, &mask->s_addr, sizeof(mask->s_addr));
+ p+= sizeof(mask->s_addr);
+ }
+ *p++ = DHO_END;
+ return dhcp;
+}
+
+static int
+handle_3rdparty(struct interface *iface)
+{
+ struct if_options *ifo;
+ struct in_addr addr, net, dst;
+
+ ifo = iface->state->options;
+ if (ifo->req_addr.s_addr != INADDR_ANY)
+ return 0;
+
+ if (get_address(iface->name, &addr, &net, &dst) == 1)
+ handle_ifa(RTM_NEWADDR, iface->name, &addr, &net, &dst);
+ else {
+ syslog(LOG_INFO,
+ "%s: waiting for 3rd party to configure IP address",
+ iface->name);
+ iface->state->reason = "3RDPARTY";
+ run_script(iface);
+ }
+ return 1;
+}
+
+static void
+start_static(struct interface *iface)
+{
+ struct if_options *ifo;
+
+ if (handle_3rdparty(iface))
+ return;
+ ifo = iface->state->options;
+ iface->state->offer =
+ dhcp_message_new(&ifo->req_addr, &ifo->req_mask);
+ delete_timeout(NULL, iface);
+ bind_interface(iface);
+}
+
+static void
+start_inform(struct interface *iface)
+{
+ if (handle_3rdparty(iface))
+ return;
+
+ if (options & DHCPCD_TEST) {
+ iface->addr.s_addr = iface->state->options->req_addr.s_addr;
+ iface->net.s_addr = iface->state->options->req_mask.s_addr;
+ } else {
+ iface->state->options->options |= DHCPCD_STATIC;
+ start_static(iface);
+ }
+
+ iface->state->state = DHS_INFORM;
+ iface->state->xid = dhcp_xid(iface);
+ send_inform(iface);
+}
+
+void
+start_reboot(struct interface *iface)
+{
+ struct if_options *ifo = iface->state->options;
+
+ if (ifo->options & DHCPCD_LINK && iface->carrier == LINK_DOWN) {
+ syslog(LOG_INFO, "%s: waiting for carrier", iface->name);
+ return;
+ }
+ if (ifo->options & DHCPCD_STATIC) {
+ start_static(iface);
+ return;
+ }
+ if (ifo->reboot == 0 || iface->state->offer == NULL) {
+ start_discover(iface);
+ return;
+ }
+ if (ifo->options & DHCPCD_INFORM) {
+ syslog(LOG_INFO, "%s: informing address of %s",
+ iface->name, inet_ntoa(iface->state->lease.addr));
+ } else if (iface->state->offer->cookie == 0) {
+ if (ifo->options & DHCPCD_IPV4LL) {
+ iface->state->claims = 0;
+ send_arp_announce(iface);
+ } else
+ start_discover(iface);
+ return;
+ } else {
+ syslog(LOG_INFO, "%s: rebinding lease of %s",
+ iface->name, inet_ntoa(iface->state->lease.addr));
+ }
+ iface->state->state = DHS_REBOOT;
+ iface->state->xid = dhcp_xid(iface);
+ iface->state->lease.server.s_addr = 0;
+ delete_timeout(NULL, iface);
+ if (ifo->fallback)
+ add_timeout_sec(ifo->reboot, start_fallback, iface);
+ else if (ifo->options & DHCPCD_LASTLEASE &&
+ iface->state->lease.frominfo)
+ add_timeout_sec(ifo->reboot, start_timeout, iface);
+ else if (!(ifo->options & DHCPCD_INFORM &&
+ options & (DHCPCD_MASTER | DHCPCD_DAEMONISED)))
+ add_timeout_sec(ifo->reboot, start_expire, iface);
+ /* Don't bother ARP checking as the server could NAK us first. */
+ if (ifo->options & DHCPCD_INFORM)
+ send_inform(iface);
+ else
+ send_request(iface);
+}
+
+void
+start_interface(void *arg)
+{
+ struct interface *iface = arg;
+ struct if_options *ifo = iface->state->options;
+ struct stat st;
+ struct timeval now;
+ uint32_t l;
+ int nolease;
+
+ handle_carrier(iface->name);
+ if (iface->carrier == LINK_DOWN) {
+ syslog(LOG_INFO, "%s: waiting for carrier", iface->name);
+ return;
+ }
+
+ iface->start_uptime = uptime();
+ free(iface->state->offer);
+ iface->state->offer = NULL;
+
+ if (iface->state->arping_index < ifo->arping_len) {
+ start_arping(iface);
+ return;
+ }
+ if (ifo->options & DHCPCD_STATIC) {
+ start_static(iface);
+ return;
+ }
+ if (ifo->options & DHCPCD_INFORM) {
+ start_inform(iface);
+ return;
+ }
+ if (iface->hwlen == 0 && ifo->clientid[0] == '\0') {
+ syslog(LOG_WARNING, "%s: needs a clientid to configure",
+ iface->name);
+ drop_config(iface, "FAIL");
+ close_sockets(iface);
+ delete_timeout(NULL, iface);
+ return;
+ }
+ /* We don't want to read the old lease if we NAK an old test */
+ nolease = iface->state->offer && options & DHCPCD_TEST;
+ if (!nolease)
+ iface->state->offer = read_lease(iface);
+ if (iface->state->offer) {
+ get_lease(&iface->state->lease, iface->state->offer);
+ iface->state->lease.frominfo = 1;
+ if (iface->state->offer->cookie == 0) {
+ if (iface->state->offer->yiaddr ==
+ iface->addr.s_addr)
+ {
+ free(iface->state->offer);
+ iface->state->offer = NULL;
+ }
+ } else if (iface->state->lease.leasetime != ~0U &&
+ stat(iface->leasefile, &st) == 0)
+ {
+ /* Offset lease times and check expiry */
+ gettimeofday(&now, NULL);
+ if ((time_t)iface->state->lease.leasetime <
+ (time_t)(now.tv_sec - st.st_mtime))
+ {
+ syslog(LOG_DEBUG,
+ "%s: discarding expired lease",
+ iface->name);
+ free(iface->state->offer);
+ iface->state->offer = NULL;
+ iface->state->lease.addr.s_addr = 0;
+ } else {
+ l = now.tv_sec - st.st_mtime;
+ iface->state->lease.leasetime -= l;
+ iface->state->lease.renewaltime -= l;
+ iface->state->lease.rebindtime -= l;
+ }
}
- break;
- case 'A':
- options->options &= ~DHCPCD_ARP;
- /* IPv4LL requires ARP */
- options->options &= ~DHCPCD_IPV4LL;
- break;
- case 'B':
- options->options &= ~DHCPCD_DAEMONISE;
- break;
- case 'C':
- /* Commas to spaces for shell */
- while ((p = strchr(oarg, ',')))
- *p = ' ';
- s = strlen("skip_hooks=") + strlen(oarg) + 1;
- p = xmalloc(sizeof(char) * s);
- snprintf(p, s, "skip_hooks=%s", oarg);
- add_environ(options, p, 0);
- free(p);
- break;
- case 'D':
- options->options |= DHCPCD_DUID;
- break;
- case 'E':
- options->options |= DHCPCD_LASTLEASE;
- break;
- case 'F':
- if (!oarg) {
- options->fqdn = FQDN_BOTH;
+ }
+ if (iface->state->offer == NULL)
+ start_discover(iface);
+ else if (iface->state->offer->cookie == 0 &&
+ iface->state->options->options & DHCPCD_IPV4LL)
+ start_ipv4ll(iface);
+ else
+ start_reboot(iface);
+}
+
+static void
+init_state(struct interface *iface, int argc, char **argv)
+{
+ struct if_state *ifs;
+
+ if (iface->state)
+ ifs = iface->state;
+ else
+ ifs = iface->state = xzalloc(sizeof(*ifs));
+
+ ifs->state = DHS_INIT;
+ ifs->reason = "PREINIT";
+ ifs->nakoff = 1;
+ configure_interface(iface, argc, argv);
+ if (!(options & DHCPCD_TEST))
+ run_script(iface);
+ /* We need to drop the leasefile so that start_interface
+ * doesn't load it. */
+ if (ifs->options->options & DHCPCD_REQUEST)
+ unlink(iface->leasefile);
+
+ if (ifs->options->options & DHCPCD_LINK) {
+ switch (carrier_status(iface)) {
+ case 0:
+ iface->carrier = LINK_DOWN;
+ ifs->reason = "NOCARRIER";
break;
+ case 1:
+ iface->carrier = LINK_UP;
+ ifs->reason = "CARRIER";
+ break;
+ default:
+ iface->carrier = LINK_UNKNOWN;
+ return;
}
- if (strcmp(oarg, "none") == 0)
- options->fqdn = FQDN_NONE;
- else if (strcmp(oarg, "ptr") == 0)
- options->fqdn = FQDN_PTR;
- else if (strcmp(oarg, "both") == 0)
- options->fqdn = FQDN_BOTH;
- else if (strcmp(oarg, "disable") == 0)
- options->fqdn = FQDN_DISABLE;
- else {
- logger(LOG_ERR, "invalid value `%s' for FQDN",
- oarg);
- return -1;
+ if (!(options & DHCPCD_TEST))
+ run_script(iface);
+ } else
+ iface->carrier = LINK_UNKNOWN;
+}
+
+void
+handle_interface(int action, const char *ifname)
+{
+ struct interface *ifs, *ifp, *ifn, *ifl = NULL;
+ const char * const argv[] = { ifname };
+ int i;
+
+ if (action == -1) {
+ ifp = find_interface(ifname);
+ if (ifp != NULL)
+ stop_interface(ifp);
+ return;
+ } else if (action == 0) {
+ handle_carrier(ifname);
+ return;
+ }
+
+ /* If running off an interface list, check it's in it. */
+ if (ifc) {
+ for (i = 0; i < ifc; i++)
+ if (strcmp(ifv[i], ifname) == 0)
+ break;
+ if (i >= ifc)
+ return;
+ }
+
+ ifs = discover_interfaces(-1, UNCONST(argv));
+ for (ifp = ifs; ifp; ifp = ifp->next) {
+ if (strcmp(ifp->name, ifname) != 0)
+ continue;
+ /* Check if we already have the interface */
+ for (ifn = ifaces; ifn; ifn = ifn->next) {
+ if (strcmp(ifn->name, ifp->name) == 0)
+ break;
+ ifl = ifn;
}
- break;
- case 'G':
- options->options &= ~DHCPCD_GATEWAY;
- break;
- case 'I':
- /* Strings have a type of 0 */;
- options->clientid[1] = 0;
- if (oarg)
- s = parse_string_hwaddr((char *)options->clientid + 1,
- CLIENTID_MAX_LEN, oarg, 1);
- else
- s = 0;
- if (s == -1) {
- logger(LOG_ERR, "clientid: %s", strerror(errno));
- return -1;
+ if (ifn) {
+ /* The flags and hwaddr could have changed */
+ ifn->flags = ifp->flags;
+ ifn->hwlen = ifp->hwlen;
+ if (ifp->hwlen != 0)
+ memcpy(ifn->hwaddr, ifp->hwaddr, ifn->hwlen);
+ } else {
+ if (ifl)
+ ifl->next = ifp;
+ else
+ ifaces = ifp;
}
- options->clientid[0] = (uint8_t)s;
-#ifdef CMDLINE_COMPAT
- if (s == 0) {
- options->options &= ~DHCPCD_DUID;
- options->options &= ~DHCPCD_CLIENTID;
+ init_state(ifp, 0, NULL);
+ start_interface(ifp);
+ }
+}
+
+#ifdef RTM_CHGADDR
+void
+handle_hwaddr(const char *ifname, unsigned char *hwaddr, size_t hwlen)
+{
+ struct interface *ifp;
+ struct if_options *ifo;
+
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ if (strcmp(ifp->name, ifname) == 0 && ifp->hwlen <= hwlen) {
+ ifo = ifp->state->options;
+ if (!(ifo->options &
+ (DHCPCD_INFORM | DHCPCD_STATIC | DHCPCD_CLIENTID))
+ && ifp->state->new != NULL &&
+ ifp->state->new->cookie == htonl(MAGIC_COOKIE))
+ {
+ syslog(LOG_INFO,
+ "%s: expiring for new hardware address",
+ ifp->name);
+ drop_config(ifp, "EXPIRE");
+ }
+ memcpy(ifp->hwaddr, hwaddr, hwlen);
+ ifp->hwlen = hwlen;
+ if (!(ifo->options &
+ (DHCPCD_INFORM | DHCPCD_STATIC | DHCPCD_CLIENTID)))
+ {
+ syslog(LOG_DEBUG, "%s: using hwaddr %s",
+ ifp->name,
+ hwaddr_ntoa(ifp->hwaddr, ifp->hwlen));
+ ifp->state->interval = 0;
+ ifp->state->nakoff = 1;
+ start_interface(ifp);
+ }
}
-#else
- options->options |= DHCPCD_CLIENTID;
+ free(hwaddr);
+}
#endif
+
+void
+handle_ifa(int type, const char *ifname,
+ struct in_addr *addr, struct in_addr *net, struct in_addr *dst)
+{
+ struct interface *ifp;
+ struct if_options *ifo;
+ int i;
+
+ if (addr->s_addr == INADDR_ANY)
+ return;
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ if (strcmp(ifp->name, ifname) == 0)
+ break;
+ if (ifp == NULL)
+ return;
+ ifo = ifp->state->options;
+ if ((ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)) == 0 ||
+ ifo->req_addr.s_addr != INADDR_ANY)
+ return;
+
+ switch (type) {
+ case RTM_DELADDR:
+ if (ifp->state->new &&
+ ifp->state->new->yiaddr == addr->s_addr)
+ drop_config(ifp, "EXPIRE");
break;
- case 'K':
- options->options &= ~DHCPCD_LINK;
- break;
- case 'L':
- options->options &= ~DHCPCD_IPV4LL;
- break;
- case 'O':
- if (make_option_mask(options->requestmask, &oarg, -1) != 0 ||
- make_option_mask(options->requiremask, &oarg, -1) != 0 ||
- make_option_mask(options->nomask, &oarg, 1) != 0)
- {
- logger(LOG_ERR, "unknown option `%s'", oarg);
- return -1;
+ case RTM_NEWADDR:
+ free(ifp->state->old);
+ ifp->state->old = ifp->state->new;
+ ifp->state->new = dhcp_message_new(addr, net);
+ ifp->dst.s_addr = dst ? dst->s_addr : INADDR_ANY;
+ if (dst) {
+ for (i = 1; i < 255; i++)
+ if (i != DHO_ROUTER &&
+ has_option_mask(ifo->dstmask, i))
+ dhcp_message_add_addr(
+ ifp->state->new,
+ i, *dst);
}
- break;
- case 'Q':
- if (make_option_mask(options->requiremask, &oarg, 1) != 0 ||
- make_option_mask(options->requestmask, &oarg, 1) != 0)
- {
- logger(LOG_ERR, "unknown option `%s'", oarg);
- return -1;
+ ifp->state->reason = "STATIC";
+ build_routes();
+ run_script(ifp);
+ if (ifo->options & DHCPCD_INFORM) {
+ ifp->state->state = DHS_INFORM;
+ ifp->state->xid = dhcp_xid(ifp);
+ ifp->state->lease.server.s_addr =
+ dst ? dst->s_addr : INADDR_ANY;
+ ifp->addr = *addr;
+ ifp->net = *net;
+ send_inform(ifp);
}
break;
- case 'X':
- if (!inet_aton(oarg, &addr)) {
- logger(LOG_ERR, "`%s' is not a valid IP address",
- oarg);
- return -1;
+ }
+}
+
+/* ARGSUSED */
+static void
+handle_link(_unused void *arg)
+{
+ if (manage_link(linkfd) == -1)
+ syslog(LOG_ERR, "manage_link: %m");
+}
+
+static void
+if_reboot(struct interface *iface, int argc, char **argv)
+{
+ const struct if_options *ifo;
+ int opt;
+
+ ifo = iface->state->options;
+ opt = ifo->options;
+ configure_interface(iface, argc, argv);
+ ifo = iface->state->options;
+ iface->state->interval = 0;
+ if ((ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC) &&
+ iface->addr.s_addr != ifo->req_addr.s_addr) ||
+ (opt & (DHCPCD_INFORM | DHCPCD_STATIC) &&
+ !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))))
+ {
+ drop_config(iface, "EXPIRE");
+ } else {
+ free(iface->state->offer);
+ iface->state->offer = NULL;
+ }
+ start_interface(iface);
+}
+
+static void
+reconf_reboot(int action, int argc, char **argv, int oi)
+{
+ struct interface *ifl, *ifn, *ifp, *ifs, *ift;
+
+ ifs = discover_interfaces(argc - oi, argv + oi);
+ if (ifs == NULL)
+ return;
+
+ /* Remove any old interfaces */
+ if (ifaces) {
+ for (ifl = NULL; ifl != ifaces;) {
+ /* Work our way backwards */
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ if (ifp->next == ifl) {
+ ifl = ifp;
+ break;
+ }
+ for (ifn = ifs; ifn; ifn = ifn->next)
+ if (strcmp(ifn->name, ifp->name) == 0)
+ break;
+ if (ifn == NULL) {
+ ifl = ifp->next;
+ stop_interface(ifp);
+ }
+ }
+ }
+
+ for (ifp = ifs; ifp && (ift = ifp->next, 1); ifp = ift) {
+ ifl = NULL;
+ for (ifn = ifaces; ifn; ifn = ifn->next) {
+ if (strcmp(ifn->name, ifp->name) == 0)
+ break;
+ ifl = ifn;
+ }
+ if (ifn) {
+ if (action)
+ if_reboot(ifn, argc, argv);
+ else if (ifn->state->new)
+ configure(ifn);
+ free_interface(ifp);
+ } else {
+ ifp->next = NULL;
+ init_state(ifp, argc, argv);
+ start_interface(ifp);
+ if (ifl)
+ ifl->next = ifp;
+ else
+ ifaces = ifp;
}
- options->blacklist = xrealloc(options->blacklist,
- sizeof(in_addr_t) * (options->blacklist_len + 1));
- options->blacklist[options->blacklist_len] = addr.s_addr;
- options->blacklist_len++;
+ }
+
+ sort_interfaces();
+}
+
+/* ARGSUSED */
+static void
+handle_signal(_unused void *arg)
+{
+ struct interface *ifp, *ifl;
+ struct if_options *ifo;
+ int sig = signal_read();
+ int do_release, do_rebind, i;
+
+ do_rebind = do_release = 0;
+ switch (sig) {
+ case SIGINT:
+ syslog(LOG_INFO, "received SIGINT, stopping");
+ break;
+ case SIGTERM:
+ syslog(LOG_INFO, "received SIGTERM, stopping");
break;
+ case SIGALRM:
+ syslog(LOG_INFO, "received SIGALRM, rebinding");
+ for (i = 0; i < ifac; i++)
+ free(ifav[i]);
+ free(ifav);
+ ifav = NULL;
+ ifac = 0;
+ for (i = 0; i < ifdc; i++)
+ free(ifdv[i]);
+ free(ifdv);
+ ifdc = 0;
+ ifdv = NULL;
+ ifo = read_config(cffile, NULL, NULL, NULL);
+ add_options(ifo, margc, margv);
+ /* We need to preserve these two options. */
+ if (options & DHCPCD_MASTER)
+ ifo->options |= DHCPCD_MASTER;
+ if (options & DHCPCD_DAEMONISED)
+ ifo->options |= DHCPCD_DAEMONISED;
+ options = ifo->options;
+ free_options(ifo);
+ reconf_reboot(1, 0, NULL, 0);
+ return;
+ case SIGHUP:
+ syslog(LOG_INFO, "received SIGHUP, releasing");
+ do_release = 1;
+ break;
+ case SIGUSR1:
+ syslog(LOG_INFO, "received SIGUSR, reconfiguring");
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ if (ifp->state->new)
+ configure(ifp);
+ return;
+ case SIGPIPE:
+ syslog(LOG_WARNING, "received SIGPIPE");
+ return;
default:
- return 0;
+ syslog(LOG_ERR,
+ "received signal %d, but don't know what to do with it",
+ sig);
+ return;
}
- return 1;
+ if (options & DHCPCD_TEST)
+ exit(EXIT_FAILURE);
+
+ /* As drop_config could re-arrange the order, we do it like this. */
+ for (;;) {
+ /* Be sane and drop the last config first */
+ ifl = NULL;
+ for (ifp = ifaces; ifp; ifp = ifp->next) {
+ if (ifp->next == NULL)
+ break;
+ ifl = ifp;
+ }
+ if (ifp == NULL)
+ break;
+ if (ifp->carrier != LINK_DOWN &&
+ (do_release ||
+ ifp->state->options->options & DHCPCD_RELEASE))
+ send_release(ifp);
+ stop_interface(ifp);
+ }
+ exit(EXIT_FAILURE);
}
-static int
-parse_config_line(const char *opt, char *line, struct options *options)
+int
+handle_args(struct fd_list *fd, int argc, char **argv)
{
- unsigned int i;
+ struct interface *ifp;
+ int do_exit = 0, do_release = 0, do_reboot = 0, do_reconf = 0;
+ int opt, oi = 0;
+ ssize_t len;
+ size_t l;
+ struct iovec iov[2];
+ char *tmp, *p;
+
+ if (fd != NULL) {
+ /* Special commands for our control socket */
+ if (strcmp(*argv, "--version") == 0) {
+ len = strlen(VERSION) + 1;
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(ssize_t);
+ iov[1].iov_base = UNCONST(VERSION);
+ iov[1].iov_len = len;
+ if (writev(fd->fd, iov, 2) == -1) {
+ syslog(LOG_ERR, "writev: %m");
+ return -1;
+ }
+ return 0;
+ } else if (strcmp(*argv, "--getconfigfile") == 0) {
+ len = strlen(cffile ? cffile : CONFIG) + 1;
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(ssize_t);
+ iov[1].iov_base = cffile ? cffile : UNCONST(CONFIG);
+ iov[1].iov_len = len;
+ if (writev(fd->fd, iov, 2) == -1) {
+ syslog(LOG_ERR, "writev: %m");
+ return -1;
+ }
+ return 0;
+ } else if (strcmp(*argv, "--getinterfaces") == 0) {
+ len = 0;
+ if (argc == 1) {
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ len++;
+ len = write(fd->fd, &len, sizeof(len));
+ if (len != sizeof(len))
+ return -1;
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ send_interface(fd->fd, ifp);
+ return 0;
+ }
+ opt = 0;
+ while (argv[++opt] != NULL) {
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ if (strcmp(argv[opt], ifp->name) == 0)
+ len++;
+ }
+ len = write(fd->fd, &len, sizeof(len));
+ if (len != sizeof(len))
+ return -1;
+ opt = 0;
+ while (argv[++opt] != NULL) {
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ if (strcmp(argv[opt], ifp->name) == 0)
+ send_interface(fd->fd, ifp);
+ }
+ return 0;
+ } else if (strcmp(*argv, "--listen") == 0) {
+ fd->listener = 1;
+ return 0;
+ }
+ }
- for (i = 0; i < sizeof(longopts) / sizeof(longopts[0]); i++) {
- if (!longopts[i].name ||
- strcmp(longopts[i].name, opt) != 0)
- continue;
+ /* Log the command */
+ len = 0;
+ for (opt = 0; opt < argc; opt++)
+ len += strlen(argv[opt]) + 1;
+ tmp = p = xmalloc(len + 1);
+ for (opt = 0; opt < argc; opt++) {
+ l = strlen(argv[opt]);
+ strlcpy(p, argv[opt], l + 1);
+ p += l;
+ *p++ = ' ';
+ }
+ *--p = '\0';
+ syslog(LOG_INFO, "control command: %s", tmp);
+ free(tmp);
- if (longopts[i].has_arg == required_argument && !line) {
- fprintf(stderr,
- PACKAGE ": option requires an argument -- %s\n",
- opt);
- return -1;
+ optind = 0;
+ while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
+ {
+ switch (opt) {
+ case 'g':
+ do_reconf = 1;
+ break;
+ case 'k':
+ do_release = 1;
+ break;
+ case 'n':
+ do_reboot = 1;
+ break;
+ case 'x':
+ do_exit = 1;
+ break;
}
+ }
- return parse_option(longopts[i].val, line, options);
+ /* We need at least one interface */
+ if (optind == argc) {
+ syslog(LOG_ERR, "handle_args: no interface");
+ return -1;
+ }
+
+ if (do_release || do_exit) {
+ for (oi = optind; oi < argc; oi++) {
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ if (strcmp(ifp->name, argv[oi]) == 0)
+ break;
+ if (!ifp)
+ continue;
+ if (do_release)
+ ifp->state->options->options |= DHCPCD_RELEASE;
+ if (ifp->state->options->options & DHCPCD_RELEASE &&
+ ifp->carrier != LINK_DOWN)
+ send_release(ifp);
+ stop_interface(ifp);
+ }
+ return 0;
}
- fprintf(stderr, PACKAGE ": unknown option -- %s\n", opt);
- return -1;
+ reconf_reboot(do_reboot, argc, argv, optind);
+ return 0;
+}
+
+void
+open_sockets(struct interface *iface)
+{
+ if (iface->raw_fd == -1) {
+ if (open_socket(iface, ETHERTYPE_IP) == -1)
+ syslog(LOG_ERR, "%s: open_socket: %m", iface->name);
+ else
+ add_event(iface->raw_fd, handle_dhcp_packet, iface);
+ }
+ if (iface->udp_fd == -1 &&
+ iface->addr.s_addr != 0 &&
+ iface->state->new != NULL &&
+ (iface->state->new->cookie == htonl(MAGIC_COOKIE) ||
+ iface->state->options->options & DHCPCD_INFORM))
+ {
+ if (open_udp_socket(iface) == -1 && errno != EADDRINUSE)
+ syslog(LOG_ERR, "%s: open_udp_socket: %m", iface->name);
+ }
+}
+
+void
+close_sockets(struct interface *iface)
+{
+ if (iface->arp_fd != -1) {
+ delete_event(iface->arp_fd);
+ close(iface->arp_fd);
+ iface->arp_fd = -1;
+ }
+ if (iface->raw_fd != -1) {
+ delete_event(iface->raw_fd);
+ close(iface->raw_fd);
+ iface->raw_fd = -1;
+ }
+ if (iface->udp_fd != -1) {
+ /* we don't listen to events on the udp */
+ close(iface->udp_fd);
+ iface->udp_fd = -1;
+ }
}
#ifdef ANDROID
-void switchUser() {
+void switchUser(void)
+{
gid_t groups[] = { AID_INET, AID_SHELL };
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap;
+
setgroups(sizeof(groups)/sizeof(groups[0]), groups);
prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
setgid(AID_DHCP);
setuid(AID_DHCP);
-
- struct __user_cap_header_struct header;
- struct __user_cap_data_struct cap;
header.version = _LINUX_CAPABILITY_VERSION;
header.pid = 0;
cap.effective = cap.permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
- (1 << CAP_NET_BROADCAST) | (1 << CAP_NET_BIND_SERVICE);
+ (1 << CAP_NET_BROADCAST) | (1 << CAP_NET_BIND_SERVICE);
cap.inheritable = 0;
capset(&header, &cap);
}
@@ -628,186 +1735,39 @@ void switchUser() {
int
main(int argc, char **argv)
{
- struct options *options;
- int opt;
- int option_index = 0;
- char *prefix;
+ struct interface *iface;
+ int opt, oi = 0, signal_fd, sig = 0, i, control_fd;
+ size_t len;
pid_t pid;
- int debug = 0;
- int i, r;
- int pid_fd = -1;
- int sig = 0;
- int retval = EXIT_FAILURE;
- char *line, *option, *p, *buffer = NULL;
- size_t len = 0;
- FILE *f;
- char *cf = NULL;
- char *intf = NULL;
struct timespec ts;
#ifdef ANDROID
switchUser();
#endif
closefrom(3);
- /* Saves calling fflush(stream) in the logger */
- setlinebuf(stdout);
- openlog(PACKAGE, LOG_PID, LOG_DAEMON);
- setlogprefix(PACKAGE ": ");
-
- options = xzalloc(sizeof(*options));
- options->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
- options->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
- options->timeout = DEFAULT_TIMEOUT;
- strlcpy(options->script, SCRIPT, sizeof(options->script));
-
- options->vendorclassid[0] = snprintf((char *)options->vendorclassid + 1,
- VENDORCLASSID_MAX_LEN,
- "%s %s", PACKAGE, VERSION);
-
- /* Explicitly include DNS in the list of parameters requested in the DNS request.
- * Without this some DHCP servers may skip the DNS entries in the DHCP replies.*/
- add_option_mask(options->requestmask, DHO_DNSSERVER);
- add_option_mask(options->requestmask, DHO_DNSDOMAIN);
- add_option_mask(options->requestmask, DHO_DNSSEARCH);
-
-#ifdef CMDLINE_COMPAT
- options->options |= DHCPCD_CLIENTID;
- add_option_mask(options->requestmask, DHO_NISSERVER);
- add_option_mask(options->requestmask, DHO_NISDOMAIN);
- add_option_mask(options->requestmask, DHO_NTPSERVER);
-
- /* If the duid file exists, then enable duid by default
- * This means we don't break existing clients that easily :) */
- if ((f = fopen(DUID, "r"))) {
- options->options |= DHCPCD_DUID;
- fclose(f);
- }
-#endif
-
- gethostname(options->hostname, HOSTNAME_MAX_LEN);
- /* Ensure that the hostname is NULL terminated */
- options->hostname[HOSTNAME_MAX_LEN] = '\0';
- if (strcmp(options->hostname, "(none)") == 0 ||
- strcmp(options->hostname, "localhost") == 0)
- options->hostname[0] = '\0';
-
- while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS,
- longopts, &option_index)) != -1)
- {
- switch (opt) {
- case 0:
- if (longopts[option_index].flag)
- break;
- logger(LOG_ERR, "option `%s' should set a flag",
- longopts[option_index].name);
- goto abort;
- case 'f':
- cf = optarg;
- break;
- case 'V':
- print_options();
- goto abort;
- case '?':
- usage();
- goto abort;
- }
- }
-
- if (doversion)
- printf(""PACKAGE" "VERSION"\n%s\n", copyright);
+ openlog(PACKAGE, LOG_PERROR | LOG_PID, LOG_DAEMON);
+ setlogmask(LOG_UPTO(LOG_INFO));
- if (dohelp)
- usage();
-
- if (optind < argc) {
- if (strlen(argv[optind]) >= IF_NAMESIZE) {
- logger(LOG_ERR,
- "`%s' too long for an interface name (max=%d)",
- argv[optind], IF_NAMESIZE);
- goto abort;
- }
- strlcpy(options->interface, argv[optind],
- sizeof(options->interface));
- } else {
- /* If only version was requested then exit now */
- if (doversion || dohelp) {
- retval = 0;
- goto abort;
- }
-
- logger(LOG_ERR, "no interface specified");
- goto abort;
- }
-
- /* Parse our options file */
- f = fopen(cf ? cf : CONFIG, "r");
- if (f) {
- r = 1;
- while ((get_line(&buffer, &len, f))) {
- line = buffer;
- while ((option = strsep(&line, " \t")))
- if (*option != '\0')
- break;
- if (!option || *option == '\0' || *option == '#')
- continue;
- /* Trim leading whitespace */
- if (line) {
- while (*line != '\0' && (*line == ' ' || *line == '\t'))
- line++;
- }
- /* Trim trailing whitespace */
- if (line && *line) {
- p = line + strlen(line) - 1;
- while (p != line &&
- (*p == ' ' || *p == '\t') &&
- *(p - 1) != '\\')
- *p-- = '\0';
- }
- if (strcmp(option, "interface") == 0) {
- free(intf);
- intf = xstrdup(line);
- continue;
- }
- /* If we're in an interface block don't use these
- * options unless it's for us */
- if (intf && strcmp(intf, options->interface) != 0)
- continue;
- r = parse_config_line(option, line, options);
- if (r != 1)
- break;
- }
- free(buffer);
- free(intf);
- fclose(f);
- if (r == 0)
+ /* Test for --help and --version */
+ if (argc > 1) {
+ if (strcmp(argv[1], "--help") == 0) {
usage();
- if (r != 1)
- goto abort;
- } else {
- if (errno != ENOENT || cf) {
- logger(LOG_ERR, "fopen `%s': %s", cf ? cf : CONFIG,
- strerror(errno));
- goto abort;
+ exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[1], "--version") == 0) {
+ printf(""PACKAGE" "VERSION"\n%s\n", copyright);
+ exit(EXIT_SUCCESS);
}
}
- optind = 0;
- while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS,
- longopts, &option_index)) != -1)
+ i = 0;
+ while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
{
switch (opt) {
- case 'd':
- debug++;
- switch (debug) {
- case 1:
- setloglevel(LOG_DEBUG);
- break;
- case 2:
- options->options &= ~DHCPCD_DAEMONISE;
- break;
- }
- break;
case 'f':
+ cffile = optarg;
+ break;
+ case 'g':
+ sig = SIGUSR1;
break;
case 'k':
sig = SIGHUP;
@@ -819,235 +1779,322 @@ main(int argc, char **argv)
sig = SIGTERM;
break;
case 'T':
- options->options |= DHCPCD_TEST | DHCPCD_PERSISTENT;
+ i = 1;
break;
-#ifdef CMDLINE_COMPAT
- case 'H': /* FALLTHROUGH */
- case 'M':
- del_option_mask(options->requestmask, DHO_MTU);
- add_environ(options, "skip_hooks=mtu", 0);
+ case 'U':
+ i = 2;
break;
- case 'N':
- del_option_mask(options->requestmask, DHO_NTPSERVER);
- add_environ(options, "skip_hooks=ntp.conf", 0);
- break;
- case 'R':
- del_option_mask(options->requestmask, DHO_DNSSERVER);
- del_option_mask(options->requestmask, DHO_DNSDOMAIN);
- del_option_mask(options->requestmask, DHO_DNSSEARCH);
- add_environ(options, "skip_hooks=resolv.conf", 0);
- break;
- case 'S':
- add_option_mask(options->requestmask, DHO_MSCSR);
- break;
- case 'Y':
- del_option_mask(options->requestmask, DHO_NISSERVER);
- del_option_mask(options->requestmask, DHO_NISDOMAIN);
- add_environ(options, "skip_hooks=yp.conf", 0);
- break;
-#endif
- default:
- i = parse_option(opt, optarg, options);
- if (i == 1)
- break;
- if (i == 0)
- usage();
- goto abort;
+ case 'V':
+ print_options();
+ exit(EXIT_SUCCESS);
+ case '?':
+ usage();
+ exit(EXIT_FAILURE);
}
}
+ margv = argv;
+ margc = argc;
+ if_options = read_config(cffile, NULL, NULL, NULL);
+ opt = add_options(if_options, argc, argv);
+ if (opt != 1) {
+ if (opt == 0)
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ options = if_options->options;
+ if (i != 0) {
+ if (i == 1)
+ options |= DHCPCD_TEST;
+ else
+ options |= DHCPCD_DUMPLEASE;
+ options |= DHCPCD_PERSISTENT;
+ options &= ~DHCPCD_DAEMONISE;
+ }
+
#ifdef THERE_IS_NO_FORK
- options->options &= ~DHCPCD_DAEMONISE;
+ options &= ~DHCPCD_DAEMONISE;
#endif
-#ifndef ANDROID
- /* android runs us as user "dhcp" */
- if (geteuid())
- logger(LOG_WARNING, PACKAGE " will not work correctly unless"
- " run as root");
-#endif
-
- if (options->options & DHCPCD_TEST) {
- if (options->options & DHCPCD_REQUEST ||
- options->options & DHCPCD_INFORM) {
- logger(LOG_ERR,
- "cannot test with --inform or --request");
- goto abort;
+ if (options & DHCPCD_DEBUG)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ if (options & DHCPCD_QUIET)
+ close(STDERR_FILENO);
+
+ if (!(options & (DHCPCD_TEST | DHCPCD_DUMPLEASE))) {
+ /* If we have any other args, we should run as a single dhcpcd
+ * instance for that interface. */
+ len = strlen(PIDFILE) + IF_NAMESIZE + 2;
+ pidfile = xmalloc(len);
+ if (optind == argc - 1)
+ snprintf(pidfile, len, PIDFILE, "-", argv[optind]);
+ else {
+ snprintf(pidfile, len, PIDFILE, "", "");
+ options |= DHCPCD_MASTER;
}
+ }
- if (options->options & DHCPCD_LASTLEASE) {
- logger(LOG_ERR, "cannot test with --lastlease");
- goto abort;
- }
+ if (chdir("/") == -1)
+ syslog(LOG_ERR, "chdir `/': %m");
+ atexit(cleanup);
- if (sig != 0) {
- logger(LOG_ERR,
- "cannot test with --release or --renew");
- goto abort;
+ if (options & DHCPCD_DUMPLEASE) {
+ if (optind != argc - 1) {
+ syslog(LOG_ERR, "dumplease requires an interface");
+ exit(EXIT_FAILURE);
+ }
+ ifaces = iface = xzalloc(sizeof(*iface));
+ strlcpy(iface->name, argv[optind], sizeof(iface->name));
+ snprintf(iface->leasefile, sizeof(iface->leasefile),
+ LEASEFILE, iface->name);
+ iface->state = xzalloc(sizeof(*iface->state));
+ iface->state->options = xzalloc(sizeof(*iface->state->options));
+ strlcpy(iface->state->options->script, if_options->script,
+ sizeof(iface->state->options->script));
+ iface->state->new = read_lease(iface);
+ if (iface->state->new == NULL && errno == ENOENT) {
+ strlcpy(iface->leasefile, argv[optind],
+ sizeof(iface->leasefile));
+ iface->state->new = read_lease(iface);
+ }
+ if (iface->state->new == NULL) {
+ if (errno == ENOENT)
+ syslog(LOG_ERR, "%s: no lease to dump",
+ iface->name);
+ exit(EXIT_FAILURE);
}
+ iface->state->reason = "DUMP";
+ run_script(iface);
+ exit(EXIT_SUCCESS);
}
- prefix = xmalloc(sizeof(char) * (IF_NAMESIZE + 3));
- snprintf(prefix, IF_NAMESIZE, "%s: ", options->interface);
- setlogprefix(prefix);
- snprintf(options->pidfile, sizeof(options->pidfile), PIDFILE,
- options->interface);
- free(prefix);
-
- if (options->request_address.s_addr == 0 &&
- (options->options & DHCPCD_INFORM ||
- options->options & DHCPCD_REQUEST))
- {
- errno = 0;
- if (get_address(options->interface,
- &options->request_address,
- &options->request_netmask) != 1)
- {
- if (errno)
- logger(LOG_ERR, "get_address: %s",
- strerror(errno));
- else
- logger(LOG_ERR, "no existing address");
- goto abort;
+ if (!(options & (DHCPCD_MASTER | DHCPCD_TEST))) {
+ control_fd = open_control();
+ if (control_fd != -1) {
+ syslog(LOG_INFO,
+ "sending commands to master dhcpcd process");
+ i = send_control(argc, argv);
+ if (i > 0) {
+ syslog(LOG_DEBUG, "send OK");
+ exit(EXIT_SUCCESS);
+ } else {
+ syslog(LOG_ERR, "failed to send commands");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ if (errno != ENOENT)
+ syslog(LOG_ERR, "open_control: %m");
}
}
- if (IN_LINKLOCAL(ntohl(options->request_address.s_addr))) {
- logger(LOG_ERR,
- "you are not allowed to request a link local address");
- goto abort;
- }
- if (chdir("/") == -1)
- logger(LOG_ERR, "chdir `/': %s", strerror(errno));
- umask(022);
-
- if (sig != 0 && !(options->options & DHCPCD_DAEMONISED)) {
+#ifndef ANDROID
+ /* android runs us as user "dhcp" */
+ if (geteuid())
+ syslog(LOG_WARNING,
+ PACKAGE " will not work correctly unless run as root");
+#endif
+ if (sig != 0) {
#ifdef ANDROID
- char pidpropname[PROPERTY_KEY_MAX];
- char pidpropval[PROPERTY_VALUE_MAX];
-
- i = -1;
- if (snprintf(pidpropname,
- sizeof(pidpropname),
- "dhcp.%s.pid", options->interface) >= PROPERTY_KEY_MAX) {
- goto abort;
- }
- property_get(pidpropname, pidpropval, NULL);
- if (strlen(pidpropval) == 0) {
- goto abort;
- }
- pid = atoi(pidpropval);
+ char pidpropname[PROPERTY_KEY_MAX];
+ char pidpropval[PROPERTY_VALUE_MAX];
+
+ if (snprintf(pidpropname,
+ sizeof(pidpropname),
+ "dhcp.%s.pid", iface->name) >= PROPERTY_KEY_MAX)
+ exit(EXIT_FAILURE);
+ property_get(pidpropname, pidpropval, NULL);
+ if (strlen(pidpropval) == 0)
+ exit(EXIT_FAILURE);
+ pid = atoi(pidpropval);
#else
- i = -1;
- pid = read_pid(options->pidfile);
+ pid = read_pid();
#endif
if (pid != 0)
- logger(LOG_INFO, "sending signal %d to pid %d",
- sig, pid);
-
- if (!pid || (i = kill(pid, sig))) {
+ syslog(LOG_INFO, "sending signal %d to pid %d",
+ sig, pid);
+ if (pid == 0 || kill(pid, sig) != 0) {
if (sig != SIGALRM)
- logger(LOG_ERR, ""PACKAGE" not running");
- unlink(options->pidfile);
- }
- if (i == 0) {
- if (sig == SIGALRM) {
- retval = EXIT_SUCCESS;
- goto abort;
+ syslog(LOG_ERR, ""PACKAGE" not running");
+ if (pid != 0 && errno != ESRCH) {
+ syslog(LOG_ERR, "kill: %m");
+ exit(EXIT_FAILURE);
}
+ unlink(pidfile);
+ if (sig != SIGALRM)
+ exit(EXIT_FAILURE);
+ } else {
+ if (sig == SIGALRM)
+ exit(EXIT_SUCCESS);
/* Spin until it exits */
- logger(LOG_INFO, "waiting for pid %d to exit", pid);
+ syslog(LOG_INFO, "waiting for pid %d to exit", pid);
ts.tv_sec = 0;
ts.tv_nsec = 100000000; /* 10th of a second */
for(i = 0; i < 100; i++) {
nanosleep(&ts, NULL);
- if (read_pid(options->pidfile) == 0) {
- retval = EXIT_SUCCESS;
- break;
- }
+ if (read_pid() == 0)
+ exit(EXIT_SUCCESS);
}
- if (retval != EXIT_SUCCESS)
- logger(LOG_ERR, "pid %d failed to exit", pid);
- goto abort;
+ syslog(LOG_ERR, "pid %d failed to exit", pid);
+ exit(EXIT_FAILURE);
}
- if (sig != SIGALRM)
- goto abort;
}
- if (!(options->options & DHCPCD_TEST) &&
- !(options->options & DHCPCD_DAEMONISED))
- {
+ if (!(options & DHCPCD_TEST)) {
#ifdef ANDROID
- char pidpropname[PROPERTY_KEY_MAX];
- char pidpropval[PROPERTY_VALUE_MAX];
+ char pidpropname[PROPERTY_KEY_MAX];
+ char pidpropval[PROPERTY_VALUE_MAX];
#endif
#ifndef ANDROID
- if ((pid = read_pid(options->pidfile)) > 0 &&
+ if ((pid = read_pid()) > 0 &&
kill(pid, 0) == 0)
{
- logger(LOG_ERR, ""PACKAGE
- " already running on pid %d (%s)",
- pid, options->pidfile);
- goto abort;
+ syslog(LOG_ERR, ""PACKAGE
+ " already running on pid %d (%s)",
+ pid, pidfile);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Ensure we have the needed directories */
+ if (mkdir(RUNDIR, 0755) == -1 && errno != EEXIST) {
+ syslog(LOG_ERR, "mkdir `%s': %m", RUNDIR);
+ exit(EXIT_FAILURE);
+ }
+ if (mkdir(DBDIR, 0755) == -1 && errno != EEXIST) {
+ syslog(LOG_ERR, "mkdir `%s': %m", DBDIR);
+ exit(EXIT_FAILURE);
}
#endif
- pid_fd = open(options->pidfile,
- O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
- if (pid_fd == -1) {
- logger(LOG_ERR, "open `%s': %s",
- options->pidfile, strerror(errno));
- goto abort;
+ pidfd = open(pidfile, O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
+ if (pidfd == -1) {
+ syslog(LOG_ERR, "open `%s': %m", pidfile);
+ exit(EXIT_FAILURE);
}
-
/* Lock the file so that only one instance of dhcpcd runs
* on an interface */
- if (flock(pid_fd, LOCK_EX | LOCK_NB) == -1) {
- logger(LOG_ERR, "flock `%s': %s",
- options->pidfile, strerror(errno));
- goto abort;
+ if (flock(pidfd, LOCK_EX | LOCK_NB) == -1) {
+ syslog(LOG_ERR, "flock `%s': %m", pidfile);
+ exit(EXIT_FAILURE);
}
-
- if (set_cloexec(pid_fd) == -1)
- goto abort;
+ if (set_cloexec(pidfd) == -1)
+ exit(EXIT_FAILURE);
#ifdef ANDROID
- if (snprintf(pidpropname,
- sizeof(pidpropname),
- "dhcp.%s.pid", options->interface) >= PROPERTY_KEY_MAX) {
- goto abort;
- }
- if (snprintf(pidpropval, sizeof(pidpropval), "%d", getpid()) >= PROPERTY_VALUE_MAX) {
- goto abort;
- }
+ if (snprintf(pidpropname,
+ sizeof(pidpropname),
+ "dhcp.%s.pid", iface->name) >= PROPERTY_KEY_MAX)
+ exit(EXIT_FAILURE);
+ if (snprintf(pidpropval, sizeof(pidpropval), "%d", getpid()) >= PROPERTY_VALUE_MAX)
+ exit(EXIT_FAILURE);
property_set(pidpropname, pidpropval);
#else
- writepid(pid_fd, getpid());
+ writepid(pidfd, getpid());
#endif
- logger(LOG_INFO, PACKAGE " " VERSION " starting");
}
- /* Terminate the encapsulated options */
- if (options->vendor[0]) {
- options->vendor[0]++;
- options->vendor[options->vendor[0]] = DHO_END;
+ syslog(LOG_INFO, "version " VERSION " starting");
+
+ if ((signal_fd = signal_init()) == -1)
+ exit(EXIT_FAILURE);
+ if (signal_setup() == -1)
+ exit(EXIT_FAILURE);
+ add_event(signal_fd, handle_signal, NULL);
+
+ if (options & DHCPCD_MASTER) {
+ if (start_control() == -1) {
+ syslog(LOG_ERR, "start_control: %m");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (init_sockets() == -1) {
+ syslog(LOG_ERR, "init_socket: %m");
+ exit(EXIT_FAILURE);
+ }
+ if (if_options->options & DHCPCD_LINK) {
+ linkfd = open_link_socket();
+ if (linkfd == -1)
+ syslog(LOG_ERR, "open_link_socket: %m");
+ else
+ add_event(linkfd, handle_link, NULL);
+ }
+
+ ifc = argc - optind;
+ ifv = argv + optind;
+
+ /* When running dhcpcd against a single interface, we need to retain
+ * the old behaviour of waiting for an IP address */
+ if (ifc == 1)
+ options |= DHCPCD_WAITIP;
+
+ ifaces = discover_interfaces(ifc, ifv);
+ for (i = 0; i < ifc; i++) {
+ for (iface = ifaces; iface; iface = iface->next)
+ if (strcmp(iface->name, ifv[i]) == 0)
+ break;
+ if (!iface)
+ syslog(LOG_ERR, "%s: interface not found or invalid",
+ ifv[i]);
+ }
+ if (!ifaces) {
+ if (ifc == 0)
+ syslog(LOG_ERR, "no valid interfaces found");
+ else
+ exit(EXIT_FAILURE);
+ if (!(options & DHCPCD_LINK)) {
+ syslog(LOG_ERR,
+ "aborting as link detection is disabled");
+ exit(EXIT_FAILURE);
+ }
}
- if (dhcp_run(options, &pid_fd) == 0)
- retval = EXIT_SUCCESS;
+ if (options & DHCPCD_BACKGROUND)
+ daemonise();
-abort:
- /* If we didn't daemonise then we need to punt the pidfile now */
- if (pid_fd > -1) {
- close(pid_fd);
- unlink(options->pidfile);
+ opt = 0;
+ for (iface = ifaces; iface; iface = iface->next) {
+ init_state(iface, argc, argv);
+ if (iface->carrier != LINK_DOWN)
+ opt = 1;
}
- if (options->environ) {
- len = 0;
- while (options->environ[len])
- free(options->environ[len++]);
- free(options->environ);
+
+ if (!(options & DHCPCD_BACKGROUND)) {
+ /* If we don't have a carrier, we may have to wait for a second
+ * before one becomes available if we brought an interface up. */
+ if (opt == 0 &&
+ options & DHCPCD_LINK &&
+ options & DHCPCD_WAITUP &&
+ !(options & DHCPCD_WAITIP))
+ {
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ nanosleep(&ts, NULL);
+ for (iface = ifaces; iface; iface = iface->next) {
+ handle_carrier(iface->name);
+ if (iface->carrier != LINK_DOWN) {
+ opt = 1;
+ break;
+ }
+ }
+ }
+ if (opt == 0 &&
+ options & DHCPCD_LINK &&
+ !(options & DHCPCD_WAITIP))
+ {
+ syslog(LOG_WARNING, "no interfaces have a carrier");
+ daemonise();
+ } else if (if_options->timeout > 0) {
+ if (options & DHCPCD_IPV4LL)
+ options |= DHCPCD_TIMEOUT_IPV4LL;
+ add_timeout_sec(if_options->timeout,
+ handle_exit_timeout, NULL);
+ }
}
- free(options->blacklist);
- free(options);
- exit(retval);
- /* NOTREACHED */
+ free_options(if_options);
+ if_options = NULL;
+
+ sort_interfaces();
+ for (iface = ifaces; iface; iface = iface->next)
+ add_timeout_sec(0, start_interface, iface);
+
+ start_eloop();
+ exit(EXIT_SUCCESS);
}
diff --git a/dhcpcd.conf b/dhcpcd.conf
index cce1795..eb625a7 100644
--- a/dhcpcd.conf
+++ b/dhcpcd.conf
@@ -1,13 +1,23 @@
# A sample configuration for dhcpcd.
# See dhcpcd.conf(5) for details.
-# dhcpcd-run-hooks uses these options.
-option domain_name_servers, domain_name, domain_search, host_name
+# Inform the DHCP server of our hostname for DDNS.
+hostname
+# To share the DHCP lease across OSX and Windows a ClientID is needed.
+# Enabling this may get a different lease than the kernel DHCP client.
+# Some upstream DHCP servers may also require a ClientID, such as FRITZ!Box.
+#clientid
-# Most distros have ntp support.
+# A list of options to request from the DHCP server.
+option domain_name_servers, domain_name, domain_search, host_name
+option classless_static_routes
+# Most distributions have NTP support.
option ntp_servers
+# Respect the network MTU.
+option interface_mtu
+# A ServerID is required by RFC2131.
+require dhcp_server_identifier
-# We should behave nicely on networks and respect their MTU.
-# However, a lot of buggy DHCP servers set invalid MTUs so this is not
-# enabled by default.
-#option interface_mtu
+# A hook script is provided to lookup the hostname if not set by the DHCP
+# server, but it should not be run by default.
+nohook lookup-hostname
diff --git a/dhcpcd.conf.5 b/dhcpcd.conf.5
index 217ecba..03b3521 100644
--- a/dhcpcd.conf.5
+++ b/dhcpcd.conf.5
@@ -1,4 +1,4 @@
-.\" Copyright 2006-2008 Roy Marples
+.\" Copyright (c) 2006-2010 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -22,11 +22,12 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 18, 2008
+.Dd January 28, 2010
.Dt DHCPCD.CONF 5 SMM
+.Os
.Sh NAME
.Nm dhcpcd.conf
-.Nd dhcpcd configuration file
+.Nd dhcpcd configuration file
.Sh DESCRIPTION
Although
.Nm dhcpcd
@@ -43,14 +44,71 @@ Blank lines and lines starting with # are ignored.
.Pp
Here's a list of available options:
.Bl -tag -width indent
+.It Ic allowinterfaces Ar pattern
+When discovering interfaces, the interface name must match
+.Ar pattern
+which is a space or comma separated list of patterns passed to
+.Xr fnmatch 3 .
+If the same interface is matched in
+.Ic denyinterfaces
+then it is still denied.
+.It Ic denyinterfaces Ar pattern
+When discovering interfaces, the interface name must not match
+.Ar pattern
+which is a space or comma separated list of patterns passed to
+.Xr fnmatch 3 .
+.It Ic arping Ar address Op address
+.Nm dhcpcd
+will arping each address in order before attempting DHCP.
+If an address is found, we will select the replying hardware address as the
+profile, otherwise the ip address.
+Example:
+.Pp
+.D1 interface bge0
+.D1 arping 192.168.0.1
+.Pp
+.D1 profile 192.168.0.1
+.D1 static ip_address=192.168.0.10/24
.It Ic background
Background immediately.
This is useful for startup scripts which don't disable link messages for
carrier status.
+.It Ic blacklist Ar address Ns Op /cidr
+Ignores all packets from
+.Ar address Ns Op /cidr .
+.It Ic whitelist Ar address Ns Op /cidr
+Only accept packets from
+.Ar address Ns Op /cidr .
+.Ic blacklist
+is ignored if
+.Ic whitelist
+is set.
+.It Ic broadcast
+Instructs the DHCP server to broadcast replies back to the client.
+Normally this is only set for non Ethernet interfaces,
+such as FireWire and InfiniBand.
+In most cases,
+.Nm dhcpcd
+will set this automatically.
+.It Ic env Ar value
+Push
+.Ar value
+to the environment for use in
+.Xr dhcpcd-run-hooks 8 .
+For example, you can force the hostname hook to always set the hostname with
+.Ic env
+.Va force_hostname=YES .
.It Ic clientid Ar string
-Change the default clientid sent from the interface hardware address.
+Send the
+.Ar clientid .
If the string is of the format 01:02:03 then it is encoded as hex.
-If not set then none is sent.
+For interfaces whose hardware address is longer than 8 bytes, or if the
+.Ar clientid
+is an empty string then
+.Nm dhcpcd
+sends a default
+.Ar clientid
+of the hardware family and the hardware address.
.It Ic duid
Generate an
.Rs
@@ -60,20 +118,24 @@ compliant clientid.
This requires persistent storage and not all DHCP servers work with it so it's
not enabled by default.
The duid generated will be held in
-.Pa /system/etc/dhcpcd/dhcpcd.duid
+.Pa /etc/dhcpcd.duid
and should not be copied to other hosts.
+.It Ic fallback Ar profile
+Fallback to using this profile if DHCP fails.
+This allows you to configure a static profile instead of using ZeroConf.
.It Ic hostname Ar name
-Sends specified
-.Ar hostname
-to the DHCP server so it can be registered in DNS. If
+Sends
+.Ar hostname
+to the DHCP server so it can be registered in DNS.
+If
+.Ar hostname
+is an empty string then the current system hostname is sent.
+If
.Ar hostname
-if a FQDN (ie, contains a .) then it will be encoded as such.
+is a FQDN (ie, contains a .) then it will be encoded as such.
.It Ic fqdn Op none | ptr | both
none disables FQDN encoding, ptr just asks the DHCP server to update the PTR
record of the host in DNS whereas both also updates the A record.
-The current hostname or the hostname specified using the
-.Ic hostname
-option must be a FQDN.
.Nm dhcpcd
itself never does any DNS updates.
.Nm dhcpcd
@@ -85,6 +147,12 @@ Subsequent options are only parsed for this
.It Ic leasetime Ar seconds
Request a leasetime of
.Ar seconds .
+.It Ic metric Ar metric
+Metrics are used to prefer an interface over another one, lowest wins.
+.Nm dhcpcd
+will supply a default metric of 200 +
+.Xr if_nametoindex 3 .
+An extra 100 will be added for wireless interfaces.
.It Ic noarp
Don't send any ARP requests.
This also disables IPv4LL.
@@ -94,6 +162,11 @@ Don't install any default routes.
Don't run this hook script.
Matches full name, or prefixed with 2 numbers optionally ending with
.Pa .sh .
+.Pp
+So to stop
+.Nm dhcpcd
+from touching your DNS or MTU settings you would do:-
+.D1 nohook resolv.conf, mtu
.It Ic noipv4ll
Don't attempt to obtain an IPv4LL address if we failed to get one via DHCP.
See
@@ -110,7 +183,33 @@ from the server.
It can be a variable to be used in
.Xr dhcpcd-run-hooks 8
or the numerical value.
-You can specify more options seperated by commas, spaces or more option lines.
+You can specify more options separated by commas, spaces or more option lines.
+.It Ic nooption Ar option
+Remove the option from the DHCP message.
+This should only be used when a DHCP server sends a non requested option
+that should not be processed.
+.It Ic destination Ar option
+If
+.Nm
+detects an address added to a point to point interface (PPP, TUN, etc) then
+it will set the listed DHCP options to the destination address of the
+interface.
+.It Ic profile Ar name
+Subsequent options are only parsed for this profile
+.Ar name .
+.It Ic quiet
+Suppress any dhcpcd output to the console, except for errors.
+.It Ic reboot Ar seconds
+Allow
+.Ar reboot
+seconds before moving to the discover phase if we have an old lease to use.
+The default is 10 seconds.
+A setting if 0 seconds causes
+.Nm dhcpcd
+to skip the reboot phase and go straight into discover.
+.It Ic release
+.Nm dhcpcd
+will release the lease prior to stopping the interface.
.It Ic require Ar option
Requires the
.Ar option
@@ -118,12 +217,42 @@ to be present in all DHCP messages, otherwise the message is ignored.
It can be a variable to be used in
.Xr dhcpcd-run-hooks 8
or the numerical value.
-You can specify more options seperated by commas, spaces or more require lines.
+You can specify more options separated by commas, spaces or more require lines.
+To enforce that
+.Nm dhcpcd
+only responds to DHCP servers and not BOOTP servers, you can
+.Ic require
+.Ar dhcp_message_type .
.It Ic script Ar script
Use
.Ar script
instead of the default
-.Pa /system/etc/dhcpcd/dhcpcd-run-hooks .
+.Pa /libexec/dhcpcd-run-hooks .
+.It Ic ssid Ar ssid
+Subsequent options are only parsed for this wireless
+.Ar ssid .
+.It Ic static Ar value
+Configures a static
+.Ar value .
+If you set
+.Ic ip_address
+then
+.Nm dhcpcd
+will not attempt to obtain a lease and just use the value for the address with
+an infinite lease time.
+.Pp
+Here is an example which configures a static address, routes and dns.
+.D1 interface eth0
+.D1 static ip_address=192.168.0.10/24
+.D1 static routers=192.168.0.1
+.D1 static domain_name_servers=192.168.0.1
+.Pp
+Here is an example for PPP which gives the destination a default route.
+It uses the special destination keyword to insert the destination address
+into the value.
+.D1 interface ppp0
+.D1 static ip_address=
+.D1 destination routers
.It Ic timeout Ar seconds
The default timeout for waiting for a DHCP response is 30 seconds which may
be too long or too short and can be changed here.
@@ -131,9 +260,12 @@ be too long or too short and can be changed here.
Tag the DHCP messages with the userclass.
You can specify more than one.
.It Ic vendor Ar code , Ns Ar value
-Add an enscapulated vendor option.
+Add an encapsulated vendor option.
.Ar code
should be between 1 and 254 inclusive.
+To add a raw vendor string, omit
+.Ar code
+but keep the comma.
Examples.
.Pp
Set the vendor option 01 with an IP address.
@@ -142,14 +274,23 @@ Set the vendor option 02 with a hex code.
.D1 vendor 02,01:02:03:04:05
Set the vendor option 03 with an IP address as a string.
.D1 vendor 03,\e"192.168.0.2\e"
+Set un-encapsulated vendor option to hello world.
+.D1 vendor ,"hello world"
.It Ic vendorclassid Ar string
Change the default vendorclassid sent from dhcpcd-version.
If not set then none is sent.
+.It Ic waitip
+Wait for an address to be assigned before forking to the background.
+.It Ic xidhwaddr
+Use the last four bytes of the hardware address as the DHCP xid instead
+of a randomly generated number.
.El
.Sh SEE ALSO
.Xr dhcpcd-run-hooks 8 ,
-.Xr dhcpcd 8
+.Xr dhcpcd 8 ,
+.Xr if_nametoindex 3 ,
+.Xr fnmatch 3
.Sh AUTHORS
-.An Roy Marples <roy@marples.name>
+.An Roy Marples Aq roy@marples.name
.Sh BUGS
-Please report them to http://bugs.marples.name
+Please report them to http://roy.marples.name/projects/dhcpcd
diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in
index 9e0a023..c3bfa8f 100644
--- a/dhcpcd.conf.5.in
+++ b/dhcpcd.conf.5.in
@@ -1,4 +1,4 @@
-.\" Copyright 2006-2008 Roy Marples
+.\" Copyright (c) 2006-2010 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -22,12 +22,12 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd November 18, 2008
+.Dd January 28, 2010
.Dt DHCPCD.CONF 5 SMM
.Os
.Sh NAME
.Nm dhcpcd.conf
-.Nd dhcpcd configuration file
+.Nd dhcpcd configuration file
.Sh DESCRIPTION
Although
.Nm dhcpcd
@@ -44,17 +44,60 @@ Blank lines and lines starting with # are ignored.
.Pp
Here's a list of available options:
.Bl -tag -width indent
+.It Ic allowinterfaces Ar pattern
+When discovering interfaces, the interface name must match
+.Ar pattern
+which is a space or comma separated list of patterns passed to
+.Xr fnmatch 3 .
+If the same interface is matched in
+.Ic denyinterfaces
+then it is still denied.
+.It Ic denyinterfaces Ar pattern
+When discovering interfaces, the interface name must not match
+.Ar pattern
+which is a space or comma separated list of patterns passed to
+.Xr fnmatch 3 .
+.It Ic arping Ar address Op address
+.Nm dhcpcd
+will arping each address in order before attempting DHCP.
+If an address is found, we will select the replying hardware address as the
+profile, otherwise the ip address.
+Example:
+.Pp
+.D1 interface bge0
+.D1 arping 192.168.0.1
+.Pp
+.D1 profile 192.168.0.1
+.D1 static ip_address=192.168.0.10/24
.It Ic background
Background immediately.
This is useful for startup scripts which don't disable link messages for
carrier status.
-.It Ic blacklist Ar address
-Ignores all DHCP messages which have this
-.Ar address
-as the server ID.
-This may be expanded in future releases to ignore all packets
-matching either the IP or hardware
-.Ar address .
+.It Ic blacklist Ar address Ns Op /cidr
+Ignores all packets from
+.Ar address Ns Op /cidr .
+.It Ic whitelist Ar address Ns Op /cidr
+Only accept packets from
+.Ar address Ns Op /cidr .
+.Ic blacklist
+is ignored if
+.Ic whitelist
+is set.
+.It Ic broadcast
+Instructs the DHCP server to broadcast replies back to the client.
+Normally this is only set for non Ethernet interfaces,
+such as FireWire and InfiniBand.
+In most cases,
+.Nm dhcpcd
+will set this automatically.
+.It Ic env Ar value
+Push
+.Ar value
+to the environment for use in
+.Xr dhcpcd-run-hooks 8 .
+For example, you can force the hostname hook to always set the hostname with
+.Ic env
+.Va force_hostname=YES .
.It Ic clientid Ar string
Send the
.Ar clientid .
@@ -77,18 +120,22 @@ not enabled by default.
The duid generated will be held in
.Pa @SYSCONFDIR@/dhcpcd.duid
and should not be copied to other hosts.
+.It Ic fallback Ar profile
+Fallback to using this profile if DHCP fails.
+This allows you to configure a static profile instead of using ZeroConf.
.It Ic hostname Ar name
-Sends specified
-.Ar hostname
-to the DHCP server so it can be registered in DNS. If
+Sends
+.Ar hostname
+to the DHCP server so it can be registered in DNS.
+If
.Ar hostname
-if a FQDN (ie, contains a .) then it will be encoded as such.
+is an empty string then the current system hostname is sent.
+If
+.Ar hostname
+is a FQDN (ie, contains a .) then it will be encoded as such.
.It Ic fqdn Op none | ptr | both
none disables FQDN encoding, ptr just asks the DHCP server to update the PTR
record of the host in DNS whereas both also updates the A record.
-The current hostname or the hostname specified using the
-.Ic hostname
-option must be a FQDN.
.Nm dhcpcd
itself never does any DNS updates.
.Nm dhcpcd
@@ -100,6 +147,12 @@ Subsequent options are only parsed for this
.It Ic leasetime Ar seconds
Request a leasetime of
.Ar seconds .
+.It Ic metric Ar metric
+Metrics are used to prefer an interface over another one, lowest wins.
+.Nm dhcpcd
+will supply a default metric of 200 +
+.Xr if_nametoindex 3 .
+An extra 100 will be added for wireless interfaces.
.It Ic noarp
Don't send any ARP requests.
This also disables IPv4LL.
@@ -109,6 +162,11 @@ Don't install any default routes.
Don't run this hook script.
Matches full name, or prefixed with 2 numbers optionally ending with
.Pa .sh .
+.Pp
+So to stop
+.Nm dhcpcd
+from touching your DNS or MTU settings you would do:-
+.D1 nohook resolv.conf, mtu
.It Ic noipv4ll
Don't attempt to obtain an IPv4LL address if we failed to get one via DHCP.
See
@@ -125,9 +183,33 @@ from the server.
It can be a variable to be used in
.Xr dhcpcd-run-hooks 8
or the numerical value.
-You can specify more options seperated by commas, spaces or more option lines.
-.Ic quiet
-Supress any dhcpcd output to the console, except for errors.
+You can specify more options separated by commas, spaces or more option lines.
+.It Ic nooption Ar option
+Remove the option from the DHCP message.
+This should only be used when a DHCP server sends a non requested option
+that should not be processed.
+.It Ic destination Ar option
+If
+.Nm
+detects an address added to a point to point interface (PPP, TUN, etc) then
+it will set the listed DHCP options to the destination address of the
+interface.
+.It Ic profile Ar name
+Subsequent options are only parsed for this profile
+.Ar name .
+.It Ic quiet
+Suppress any dhcpcd output to the console, except for errors.
+.It Ic reboot Ar seconds
+Allow
+.Ar reboot
+seconds before moving to the discover phase if we have an old lease to use.
+The default is 10 seconds.
+A setting if 0 seconds causes
+.Nm dhcpcd
+to skip the reboot phase and go straight into discover.
+.It Ic release
+.Nm dhcpcd
+will release the lease prior to stopping the interface.
.It Ic require Ar option
Requires the
.Ar option
@@ -135,12 +217,42 @@ to be present in all DHCP messages, otherwise the message is ignored.
It can be a variable to be used in
.Xr dhcpcd-run-hooks 8
or the numerical value.
-You can specify more options seperated by commas, spaces or more require lines.
+You can specify more options separated by commas, spaces or more require lines.
+To enforce that
+.Nm dhcpcd
+only responds to DHCP servers and not BOOTP servers, you can
+.Ic require
+.Ar dhcp_message_type .
.It Ic script Ar script
Use
.Ar script
instead of the default
.Pa @SCRIPT@ .
+.It Ic ssid Ar ssid
+Subsequent options are only parsed for this wireless
+.Ar ssid .
+.It Ic static Ar value
+Configures a static
+.Ar value .
+If you set
+.Ic ip_address
+then
+.Nm dhcpcd
+will not attempt to obtain a lease and just use the value for the address with
+an infinite lease time.
+.Pp
+Here is an example which configures a static address, routes and dns.
+.D1 interface eth0
+.D1 static ip_address=192.168.0.10/24
+.D1 static routers=192.168.0.1
+.D1 static domain_name_servers=192.168.0.1
+.Pp
+Here is an example for PPP which gives the destination a default route.
+It uses the special destination keyword to insert the destination address
+into the value.
+.D1 interface ppp0
+.D1 static ip_address=
+.D1 destination routers
.It Ic timeout Ar seconds
The default timeout for waiting for a DHCP response is 30 seconds which may
be too long or too short and can be changed here.
@@ -148,9 +260,12 @@ be too long or too short and can be changed here.
Tag the DHCP messages with the userclass.
You can specify more than one.
.It Ic vendor Ar code , Ns Ar value
-Add an enscapulated vendor option.
+Add an encapsulated vendor option.
.Ar code
should be between 1 and 254 inclusive.
+To add a raw vendor string, omit
+.Ar code
+but keep the comma.
Examples.
.Pp
Set the vendor option 01 with an IP address.
@@ -159,14 +274,23 @@ Set the vendor option 02 with a hex code.
.D1 vendor 02,01:02:03:04:05
Set the vendor option 03 with an IP address as a string.
.D1 vendor 03,\e"192.168.0.2\e"
+Set un-encapsulated vendor option to hello world.
+.D1 vendor ,"hello world"
.It Ic vendorclassid Ar string
Change the default vendorclassid sent from dhcpcd-version.
If not set then none is sent.
+.It Ic waitip
+Wait for an address to be assigned before forking to the background.
+.It Ic xidhwaddr
+Use the last four bytes of the hardware address as the DHCP xid instead
+of a randomly generated number.
.El
.Sh SEE ALSO
.Xr dhcpcd-run-hooks 8 ,
-.Xr dhcpcd 8
+.Xr dhcpcd 8 ,
+.Xr if_nametoindex 3 ,
+.Xr fnmatch 3
.Sh AUTHORS
-.An Roy Marples <roy@marples.name>
+.An Roy Marples Aq roy@marples.name
.Sh BUGS
Please report them to http://roy.marples.name/projects/dhcpcd
diff --git a/dhcpcd.h b/dhcpcd.h
index 7d93315..ec32474 100644
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -28,68 +28,116 @@
#ifndef DHCPCD_H
#define DHCPCD_H
-#include <sys/param.h>
#include <sys/socket.h>
-
#include <net/if.h>
-#include <netinet/in.h>
#include <limits.h>
-#include "common.h"
-
-#define DEFAULT_TIMEOUT 30
-#define DEFAULT_LEASETIME 3600 /* 1 hour */
-
-#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
-#define VENDORCLASSID_MAX_LEN 48
-#define CLIENTID_MAX_LEN 48
-#define USERCLASS_MAX_LEN 255
-#define VENDOR_MAX_LEN 255
-
-#define DHCPCD_ARP (1 << 0)
-#define DHCPCD_DOMAIN (1 << 2)
-#define DHCPCD_GATEWAY (1 << 3)
-#define DHCPCD_LASTLEASE (1 << 7)
-#define DHCPCD_INFORM (1 << 8)
-#define DHCPCD_REQUEST (1 << 9)
-#define DHCPCD_IPV4LL (1 << 10)
-#define DHCPCD_DUID (1 << 11)
-#define DHCPCD_PERSISTENT (1 << 12)
-#define DHCPCD_DAEMONISE (1 << 14)
-#define DHCPCD_DAEMONISED (1 << 15)
-#define DHCPCD_TEST (1 << 16)
-#define DHCPCD_FORKED (1 << 17)
-#define DHCPCD_HOSTNAME (1 << 18)
-#define DHCPCD_CLIENTID (1 << 19)
-#define DHCPCD_LINK (1 << 20)
-#define DHCPCD_BACKGROUND (1 << 21)
-
-struct options {
- char interface[IF_NAMESIZE];
+#include "control.h"
+#include "dhcp.h"
+#include "if-options.h"
+
+#define HWADDR_LEN 20
+#define IF_SSIDSIZE 33
+#define PROFILE_LEN 64
+
+enum DHS {
+ DHS_INIT,
+ DHS_DISCOVER,
+ DHS_REQUEST,
+ DHS_BOUND,
+ DHS_RENEW,
+ DHS_REBIND,
+ DHS_REBOOT,
+ DHS_INFORM,
+ DHS_RENEW_REQUESTED,
+ DHS_INIT_IPV4LL,
+ DHS_PROBE
+};
+
+#define LINK_UP 1
+#define LINK_UNKNOWN 0
+#define LINK_DOWN -1
+
+struct if_state {
+ enum DHS state;
+ char profile[PROFILE_LEN];
+ struct if_options *options;
+ struct dhcp_message *sent;
+ struct dhcp_message *offer;
+ struct dhcp_message *new;
+ struct dhcp_message *old;
+ struct dhcp_lease lease;
+ const char *reason;
+ time_t interval;
+ time_t nakoff;
+ uint32_t xid;
+ int socket;
+ int probes;
+ int claims;
+ int conflicts;
+ time_t defend;
+ struct in_addr fail;
+ size_t arping_index;
+};
+
+struct interface {
+ char name[IF_NAMESIZE];
+ struct if_state *state;
+
+ int flags;
+ sa_family_t family;
+ unsigned char hwaddr[HWADDR_LEN];
+ size_t hwlen;
int metric;
- uint8_t requestmask[256 / 8];
- uint8_t requiremask[256 / 8];
- uint8_t nomask[256 / 8];
- uint32_t leasetime;
- time_t timeout;
- int options;
-
- struct in_addr request_address;
- struct in_addr request_netmask;
-
- char **environ;
- char script[PATH_MAX];
- char pidfile[PATH_MAX];
-
- char hostname[HOSTNAME_MAX_LEN + 1]; /* We don't store the lenth */
- int fqdn;
- uint8_t vendorclassid[VENDORCLASSID_MAX_LEN + 2];
- char clientid[CLIENTID_MAX_LEN + 2];
- uint8_t userclass[USERCLASS_MAX_LEN + 2];
- uint8_t vendor[VENDOR_MAX_LEN + 2];
-
- size_t blacklist_len;
- in_addr_t *blacklist;
+ int carrier;
+ int wireless;
+ char ssid[IF_SSIDSIZE];
+
+ int raw_fd;
+ int udp_fd;
+ int arp_fd;
+ size_t buffer_size, buffer_len, buffer_pos;
+ unsigned char *buffer;
+
+ struct in_addr addr;
+ struct in_addr net;
+ struct in_addr dst;
+
+ char leasefile[PATH_MAX];
+ time_t start_uptime;
+
+ unsigned char *clientid;
+
+ struct interface *next;
};
+
+extern int pidfd;
+extern int options;
+extern int ifac;
+extern char **ifav;
+extern int ifdc;
+extern char **ifdv;
+extern struct interface *ifaces;
+
+struct interface *find_interface(const char *);
+int handle_args(struct fd_list *, int, char **);
+void handle_interface(int, const char *);
+void handle_hwaddr(const char *, unsigned char *, size_t);
+void handle_ifa(int, const char *,
+ struct in_addr *, struct in_addr *, struct in_addr *);
+void handle_exit_timeout(void *);
+void start_interface(void *);
+void start_discover(void *);
+void start_request(void *);
+void start_renew(void *);
+void start_rebind(void *);
+void start_reboot(struct interface *);
+void start_expire(void *);
+void send_decline(struct interface *);
+void open_sockets(struct interface *);
+void close_sockets(struct interface *);
+void drop_config(struct interface *, const char *);
+int select_profile(struct interface *, const char *);
+
#endif
diff --git a/duid.c b/duid.c
new file mode 100644
index 0000000..d67e45e
--- /dev/null
+++ b/duid.c
@@ -0,0 +1,100 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#define THIRTY_YEARS_IN_SECONDS 946707779
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "duid.h"
+#include "net.h"
+
+size_t
+get_duid(unsigned char *duid, const struct interface *iface)
+{
+ FILE *f;
+ uint16_t type = 0;
+ uint16_t hw = 0;
+ uint32_t ul;
+ time_t t;
+ int x = 0;
+ unsigned char *p = duid;
+ size_t len = 0;
+ char *line;
+
+ /* If we already have a DUID then use it as it's never supposed
+ * to change once we have one even if the interfaces do */
+ if ((f = fopen(DUID, "r"))) {
+ while ((line = get_line(f))) {
+ len = hwaddr_aton(NULL, line);
+ if (len && len <= DUID_LEN) {
+ hwaddr_aton(duid, line);
+ break;
+ }
+ len = 0;
+ }
+ fclose(f);
+ if (len)
+ return len;
+ } else {
+ if (errno != ENOENT)
+ return 0;
+ }
+
+ /* No file? OK, lets make one based on our interface */
+ if (!(f = fopen(DUID, "w")))
+ return 0;
+ type = htons(1); /* DUI-D-LLT */
+ memcpy(p, &type, 2);
+ p += 2;
+ hw = htons(iface->family);
+ memcpy(p, &hw, 2);
+ p += 2;
+ /* time returns seconds from jan 1 1970, but DUID-LLT is
+ * seconds from jan 1 2000 modulo 2^32 */
+ t = time(NULL) - THIRTY_YEARS_IN_SECONDS;
+ ul = htonl(t & 0xffffffff);
+ memcpy(p, &ul, 4);
+ p += 4;
+ /* Finally, add the MAC address of the interface */
+ memcpy(p, iface->hwaddr, iface->hwlen);
+ p += iface->hwlen;
+ len = p - duid;
+ x = fprintf(f, "%s\n", hwaddr_ntoa(duid, len));
+ fclose(f);
+ /* Failed to write the duid? scrub it, we cannot use it */
+ if (x < 1) {
+ len = 0;
+ unlink(DUID);
+ }
+ return len;
+}
diff --git a/duid.h b/duid.h
new file mode 100644
index 0000000..98c1bbd
--- /dev/null
+++ b/duid.h
@@ -0,0 +1,35 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef DUID_H
+#define DUID_H
+
+#include "net.h"
+
+size_t get_duid(unsigned char *duid, const struct interface *iface);
+
+#endif
diff --git a/eloop.c b/eloop.c
new file mode 100644
index 0000000..a5d08cb
--- /dev/null
+++ b/eloop.c
@@ -0,0 +1,366 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/time.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include "common.h"
+#include "eloop.h"
+
+static struct timeval now;
+
+static struct event {
+ int fd;
+ void (*callback)(void *);
+ void *arg;
+ struct event *next;
+} *events;
+static struct event *free_events;
+
+static struct timeout {
+ struct timeval when;
+ void (*callback)(void *);
+ void *arg;
+ int queue;
+ struct timeout *next;
+} *timeouts;
+static struct timeout *free_timeouts;
+
+static struct pollfd *fds;
+static size_t fds_len;
+
+void
+add_event(int fd, void (*callback)(void *), void *arg)
+{
+ struct event *e, *last = NULL;
+
+ /* We should only have one callback monitoring the fd */
+ for (e = events; e; e = e->next) {
+ if (e->fd == fd) {
+ e->callback = callback;
+ e->arg = arg;
+ return;
+ }
+ last = e;
+ }
+
+ /* Allocate a new event if no free ones already allocated */
+ if (free_events) {
+ e = free_events;
+ free_events = e->next;
+ } else
+ e = xmalloc(sizeof(*e));
+ e->fd = fd;
+ e->callback = callback;
+ e->arg = arg;
+ e->next = NULL;
+ if (last)
+ last->next = e;
+ else
+ events = e;
+}
+
+void
+delete_event(int fd)
+{
+ struct event *e, *last = NULL;
+
+ for (e = events; e; e = e->next) {
+ if (e->fd == fd) {
+ if (last)
+ last->next = e->next;
+ else
+ events = e->next;
+ e->next = free_events;
+ free_events = e;
+ break;
+ }
+ last = e;
+ }
+}
+
+void
+add_q_timeout_tv(int queue,
+ const struct timeval *when, void (*callback)(void *), void *arg)
+{
+ struct timeval w;
+ struct timeout *t, *tt = NULL;
+
+ get_monotonic(&now);
+ timeradd(&now, when, &w);
+ /* Check for time_t overflow. */
+ if (timercmp(&w, &now, <)) {
+ errno = ERANGE;
+ return;
+ }
+
+ /* Remove existing timeout if present */
+ for (t = timeouts; t; t = t->next) {
+ if (t->callback == callback && t->arg == arg) {
+ if (tt)
+ tt->next = t->next;
+ else
+ timeouts = t->next;
+ break;
+ }
+ tt = t;
+ }
+
+ if (!t) {
+ /* No existing, so allocate or grab one from the free pool */
+ if (free_timeouts) {
+ t = free_timeouts;
+ free_timeouts = t->next;
+ } else
+ t = xmalloc(sizeof(*t));
+ }
+
+ t->when.tv_sec = w.tv_sec;
+ t->when.tv_usec = w.tv_usec;
+ t->callback = callback;
+ t->arg = arg;
+ t->queue = queue;
+
+ /* The timeout list should be in chronological order,
+ * soonest first.
+ * This is the easiest algorithm - check the head, then middle
+ * and finally the end. */
+ if (!timeouts || timercmp(&t->when, &timeouts->when, <)) {
+ t->next = timeouts;
+ timeouts = t;
+ return;
+ }
+ for (tt = timeouts; tt->next; tt = tt->next)
+ if (timercmp(&t->when, &tt->next->when, <)) {
+ t->next = tt->next;
+ tt->next = t;
+ return;
+ }
+ tt->next = t;
+ t->next = NULL;
+}
+
+void
+add_q_timeout_sec(int queue, time_t when, void (*callback)(void *), void *arg)
+{
+ struct timeval tv;
+
+ tv.tv_sec = when;
+ tv.tv_usec = 0;
+ add_q_timeout_tv(queue, &tv, callback, arg);
+}
+
+/* This deletes all timeouts for the interface EXCEPT for ones with the
+ * callbacks given. Handy for deleting everything apart from the expire
+ * timeout. */
+static void
+v_delete_q_timeouts(int queue, void *arg, void (*callback)(void *), va_list v)
+{
+ struct timeout *t, *tt, *last = NULL;
+ va_list va;
+ void (*f)(void *);
+
+ for (t = timeouts; t && (tt = t->next, 1); t = tt) {
+ if (t->queue == queue && t->arg == arg &&
+ t->callback != callback)
+ {
+ va_copy(va, v);
+ while ((f = va_arg(va, void (*)(void *))))
+ if (f == t->callback)
+ break;
+ va_end(va);
+ if (!f) {
+ if (last)
+ last->next = t->next;
+ else
+ timeouts = t->next;
+ t->next = free_timeouts;
+ free_timeouts = t;
+ continue;
+ }
+ }
+ last = t;
+ }
+}
+
+void
+delete_q_timeouts(int queue, void *arg, void (*callback)(void *), ...)
+{
+ va_list va;
+
+ va_start(va, callback);
+ v_delete_q_timeouts(queue, arg, callback, va);
+ va_end(va);
+}
+
+void
+delete_q_timeout(int queue, void (*callback)(void *), void *arg)
+{
+ struct timeout *t, *tt, *last = NULL;
+
+ for (t = timeouts; t && (tt = t->next, 1); t = tt) {
+ if (t->queue == queue && t->arg == arg &&
+ (!callback || t->callback == callback))
+ {
+ if (last)
+ last->next = t->next;
+ else
+ timeouts = t->next;
+ t->next = free_timeouts;
+ free_timeouts = t;
+ continue;
+ }
+ last = t;
+ }
+}
+
+#ifdef DEBUG_MEMORY
+/* Define this to free all malloced memory.
+ * Normally we don't do this as the OS will do it for us at exit,
+ * but it's handy for debugging other leaks in valgrind. */
+static void
+cleanup(void)
+{
+ struct event *e;
+ struct timeout *t;
+
+ while (events) {
+ e = events->next;
+ free(events);
+ events = e;
+ }
+ while (free_events) {
+ e = free_events->next;
+ free(free_events);
+ free_events = e;
+ }
+ while (timeouts) {
+ t = timeouts->next;
+ free(timeouts);
+ timeouts = t;
+ }
+ while (free_timeouts) {
+ t = free_timeouts->next;
+ free(free_timeouts);
+ free_timeouts = t;
+ }
+ free(fds);
+}
+#endif
+
+_noreturn void
+start_eloop(void)
+{
+ int msecs, n;
+ nfds_t nfds, i;
+ struct event *e;
+ struct timeout *t;
+ struct timeval tv;
+
+#ifdef DEBUG_MEMORY
+ atexit(cleanup);
+#endif
+
+ for (;;) {
+ /* Run all timeouts first.
+ * When we have one that has not yet occured,
+ * calculate milliseconds until it does for use in poll. */
+ if (timeouts) {
+ if (timercmp(&now, &timeouts->when, >)) {
+ t = timeouts;
+ timeouts = timeouts->next;
+ t->callback(t->arg);
+ t->next = free_timeouts;
+ free_timeouts = t;
+ continue;
+ }
+ timersub(&timeouts->when, &now, &tv);
+ if (tv.tv_sec > INT_MAX / 1000 ||
+ (tv.tv_sec == INT_MAX / 1000 &&
+ (tv.tv_usec + 999) / 1000 > INT_MAX % 1000))
+ msecs = INT_MAX;
+ else
+ msecs = tv.tv_sec * 1000 +
+ (tv.tv_usec + 999) / 1000;
+ } else
+ /* No timeouts, so wait forever. */
+ msecs = -1;
+
+ /* Allocate memory for our pollfds as and when needed.
+ * We don't bother shrinking it. */
+ nfds = 0;
+ for (e = events; e; e = e->next)
+ nfds++;
+ if (msecs == -1 && nfds == 0) {
+ syslog(LOG_ERR, "nothing to do");
+ exit(EXIT_FAILURE);
+ }
+ if (nfds > fds_len) {
+ free(fds);
+ /* Allocate 5 more than we need for future use */
+ fds_len = nfds + 5;
+ fds = xmalloc(sizeof(*fds) * fds_len);
+ }
+ nfds = 0;
+ for (e = events; e; e = e->next) {
+ fds[nfds].fd = e->fd;
+ fds[nfds].events = POLLIN;
+ fds[nfds].revents = 0;
+ nfds++;
+ }
+ n = poll(fds, nfds, msecs);
+ if (n == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ get_monotonic(&now);
+ continue;
+ }
+ syslog(LOG_ERR, "poll: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Get the now time and process any triggered events. */
+ get_monotonic(&now);
+ if (n == 0)
+ continue;
+ for (i = 0; i < nfds; i++) {
+ if (!(fds[i].revents & (POLLIN | POLLHUP)))
+ continue;
+ for (e = events; e; e = e->next) {
+ if (e->fd == fds[i].fd) {
+ e->callback(e->arg);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/eloop.h b/eloop.h
new file mode 100644
index 0000000..02c9438
--- /dev/null
+++ b/eloop.h
@@ -0,0 +1,51 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef ELOOP_H
+#define ELOOP_H
+
+#include <time.h>
+
+#ifndef ELOOP_QUEUE
+ #define ELOOP_QUEUE 0
+#endif
+
+#define add_timeout_tv(a, b, c) add_q_timeout_tv(ELOOP_QUEUE, a, b, c)
+#define add_timeout_sec(a, b, c) add_q_timeout_sec(ELOOP_QUEUE, a, b, c)
+#define delete_timeout(a, b) delete_q_timeout(ELOOP_QUEUE, a, b)
+#define delete_timeouts(a, ...) delete_q_timeouts(ELOOP_QUEUE, a, __VA_ARGS__)
+
+void add_event(int fd, void (*)(void *), void *);
+void delete_event(int fd);
+void add_q_timeout_sec(int queue, time_t, void (*)(void *), void *);
+void add_q_timeout_tv(int queue, const struct timeval *, void (*)(void *),
+ void *);
+void delete_q_timeout(int, void (*)(void *), void *);
+void delete_q_timeouts(int, void *, void (*)(void *), ...);
+void start_eloop(void);
+
+#endif
diff --git a/if-bsd.c b/if-bsd.c
index d0ff246..462ec2a 100644
--- a/if-bsd.c
+++ b/if-bsd.c
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -25,44 +25,123 @@
* SUCH DAMAGE.
*/
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
#include <arpa/inet.h>
+#include <net/if.h>
#include <net/if_dl.h>
-#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
+#ifdef __DragonFly__
+# include <netproto/802_11/ieee80211_ioctl.h>
+#elif __APPLE__
+ /* FIXME: Add apple includes so we can work out SSID */
+#else
+# include <net80211/ieee80211_ioctl.h>
+#endif
#include <errno.h>
+#include <fnmatch.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
#include <unistd.h>
#include "config.h"
#include "common.h"
+#include "configure.h"
#include "dhcp.h"
+#include "if-options.h"
#include "net.h"
-/* Darwin doesn't define this for some very odd reason */
-#ifndef SA_SIZE
-# define SA_SIZE(sa) \
- ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
- sizeof(long) : \
- 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+/* FIXME: Why do we need to check for sa_family 255 */
+#define COPYOUT(sin, sa) \
+ sin.s_addr = ((sa) != NULL) ? \
+ (((struct sockaddr_in *)(void *)sa)->sin_addr).s_addr : 0
+
+static int r_fd = -1;
+static char *link_buf;
+static ssize_t link_buflen;
+
+int
+if_init(_unused struct interface *iface)
+{
+ /* BSD promotes secondary address by default */
+ return 0;
+}
+
+int
+if_conf(_unused struct interface *iface)
+{
+ /* No extra checks needed on BSD */
+ return 0;
+}
+
+int
+init_sockets(void)
+{
+ if ((socket_afnet = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return -1;
+ set_cloexec(socket_afnet);
+ if ((r_fd = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
+ return -1;
+ set_cloexec(r_fd);
+ return 0;
+}
+
+int
+getifssid(const char *ifname, char *ssid)
+{
+ int retval = -1;
+#if defined(SIOCG80211NWID)
+ struct ifreq ifr;
+ struct ieee80211_nwid nwid;
+#elif defined(IEEE80211_IOC_SSID)
+ struct ieee80211req ireq;
+ char nwid[IEEE80211_NWID_LEN + 1];
#endif
+#if defined(SIOCG80211NWID) /* NetBSD */
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ memset(&nwid, 0, sizeof(nwid));
+ ifr.ifr_data = (void *)&nwid;
+ if (ioctl(socket_afnet, SIOCG80211NWID, &ifr) == 0) {
+ retval = nwid.i_len;
+ memcpy(ssid, nwid.i_nwid, nwid.i_len);
+ ssid[nwid.i_len] = '\0';
+ }
+#elif defined(IEEE80211_IOC_SSID) /* FreeBSD */
+ memset(&ireq, 0, sizeof(ireq));
+ strlcpy(ireq.i_name, ifname, sizeof(ireq.i_name));
+ ireq.i_type = IEEE80211_IOC_SSID;
+ ireq.i_val = -1;
+ ireq.i_data = &nwid;
+ if (ioctl(socket_afnet, SIOCG80211, &ireq) == 0) {
+ retval = ireq.i_len;
+ memcpy(ssid, nwid, ireq.i_len);
+ ssid[ireq.i_len] = '\0';
+ }
+#endif
+ return retval;
+}
+
int
-if_address(const char *ifname, const struct in_addr *address,
- const struct in_addr *netmask, const struct in_addr *broadcast,
- int action)
+if_address(const struct interface *iface, const struct in_addr *address,
+ const struct in_addr *netmask, const struct in_addr *broadcast,
+ int action)
{
- int s;
int retval;
struct ifaliasreq ifa;
union {
@@ -70,39 +149,36 @@ if_address(const char *ifname, const struct in_addr *address,
struct sockaddr_in *sin;
} _s;
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- return -1;
-
memset(&ifa, 0, sizeof(ifa));
- strlcpy(ifa.ifra_name, ifname, sizeof(ifa.ifra_name));
+ strlcpy(ifa.ifra_name, iface->name, sizeof(ifa.ifra_name));
-#define ADDADDR(_var, _addr) \
- _s.sa = &_var; \
- _s.sin->sin_family = AF_INET; \
- _s.sin->sin_len = sizeof(*_s.sin); \
- memcpy(&_s.sin->sin_addr, _addr, sizeof(_s.sin->sin_addr));
+#define ADDADDR(_var, _addr) { \
+ _s.sa = &_var; \
+ _s.sin->sin_family = AF_INET; \
+ _s.sin->sin_len = sizeof(*_s.sin); \
+ memcpy(&_s.sin->sin_addr, _addr, sizeof(_s.sin->sin_addr)); \
+ }
ADDADDR(ifa.ifra_addr, address);
ADDADDR(ifa.ifra_mask, netmask);
- if (action >= 0) {
+ if (action >= 0 && broadcast) {
ADDADDR(ifa.ifra_broadaddr, broadcast);
}
#undef ADDADDR
if (action < 0)
- retval = ioctl(s, SIOCDIFADDR, &ifa);
+ retval = ioctl(socket_afnet, SIOCDIFADDR, &ifa);
else
- retval = ioctl(s, SIOCAIFADDR, &ifa);
- close(s);
+ retval = ioctl(socket_afnet, SIOCAIFADDR, &ifa);
return retval;
}
+/* ARGSUSED4 */
int
if_route(const struct interface *iface, const struct in_addr *dest,
- const struct in_addr *net, const struct in_addr *gate,
- _unused int metric, int action)
+ const struct in_addr *net, const struct in_addr *gate,
+ _unused int metric, int action)
{
- int s;
union sockunion {
struct sockaddr sa;
struct sockaddr_in sin;
@@ -121,21 +197,18 @@ if_route(const struct interface *iface, const struct in_addr *dest,
size_t l;
int retval = 0;
-#define ADDSU(_su) { \
- l = SA_SIZE(&(_su.sa)); \
- memcpy(bp, &(_su), l); \
- bp += l; \
-}
-#define ADDADDR(_addr) { \
- memset (&su, 0, sizeof(su)); \
- su.sin.sin_family = AF_INET; \
- su.sin.sin_len = sizeof(su.sin); \
- memcpy (&su.sin.sin_addr, _addr, sizeof(su.sin.sin_addr)); \
- ADDSU(su); \
-}
-
- if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
- return -1;
+#define ADDSU(_su) { \
+ l = ROUNDUP(_su.sa.sa_len); \
+ memcpy(bp, &(_su), l); \
+ bp += l; \
+ }
+#define ADDADDR(_a) { \
+ memset (&su, 0, sizeof(su)); \
+ su.sin.sin_family = AF_INET; \
+ su.sin.sin_len = sizeof(su.sin); \
+ memcpy (&su.sin.sin_addr, _a, sizeof(su.sin.sin_addr)); \
+ ADDSU(su); \
+ }
memset(&rtm, 0, sizeof(rtm));
rtm.hdr.rtm_version = RTM_VERSION;
@@ -194,41 +267,72 @@ if_route(const struct interface *iface, const struct in_addr *dest,
ADDADDR(&iface->addr);
rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
- if (write(s, &rtm, l) == -1)
+ if (write(r_fd, &rtm, l) == -1)
retval = -1;
- close(s);
return retval;
}
int
-open_link_socket(struct interface *iface)
+open_link_socket(void)
{
int fd;
fd = socket(PF_ROUTE, SOCK_RAW, 0);
- if (fd == -1)
- return -1;
- set_cloexec(fd);
- if (iface->link_fd != -1)
- close(iface->link_fd);
- iface->link_fd = fd;
- return 0;
+ if (fd != -1) {
+ set_cloexec(fd);
+ set_nonblock(fd);
+ }
+ return fd;
+}
+
+static void
+get_addrs(int type, char *cp, struct sockaddr **sa)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (type & (1 << i)) {
+ sa[i] = (struct sockaddr *)cp;
+#ifdef DEBUG
+ printf ("got %d %d %s\n", i, sa[i]->sa_family,
+ inet_ntoa(((struct sockaddr_in *)sa[i])->
+ sin_addr));
+#endif
+ ADVANCE(cp, sa[i]);
+ } else
+ sa[i] = NULL;
+ }
}
-#define BUFFER_LEN 2048
int
-link_changed(struct interface *iface)
+manage_link(int fd)
{
- char buffer[2048], *p;
+ char *p, *e, *cp;
+ char ifname[IF_NAMESIZE];
ssize_t bytes;
struct rt_msghdr *rtm;
+ struct if_announcemsghdr *ifan;
struct if_msghdr *ifm;
- int i;
+ struct ifa_msghdr *ifam;
+ struct rt rt;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ int len;
+#ifdef RTM_CHGADDR
+ struct sockaddr_dl sdl;
+ unsigned char *hwaddr;
+#endif
- if ((i = if_nametoindex(iface->name)) == -1)
- return -1;
for (;;) {
- bytes = recv(iface->link_fd, buffer, BUFFER_LEN, MSG_DONTWAIT);
+ if (ioctl(fd, FIONREAD, &len) == -1)
+ return -1;
+ if (link_buflen < len) {
+ p = realloc(link_buf, len);
+ if (p == NULL)
+ return -1;
+ link_buf = p;
+ link_buflen = len;
+ }
+ bytes = read(fd, link_buf, link_buflen);
if (bytes == -1) {
if (errno == EAGAIN)
return 0;
@@ -236,20 +340,85 @@ link_changed(struct interface *iface)
continue;
return -1;
}
- for (p = buffer; bytes > 0;
- bytes -= ((struct rt_msghdr *)p)->rtm_msglen,
- p += ((struct rt_msghdr *)p)->rtm_msglen)
- {
- rtm = (struct rt_msghdr *)p;
- if (rtm->rtm_type != RTM_IFINFO)
- continue;
- ifm = (struct if_msghdr *)p;
- if (ifm->ifm_index != i)
- continue;
-
- /* Link changed */
- return 1;
+ e = link_buf + bytes;
+ for (p = link_buf; p < e; p += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)(void *)p;
+ switch(rtm->rtm_type) {
+#ifdef RTM_IFANNOUNCE
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *)(void *)p;
+ switch(ifan->ifan_what) {
+ case IFAN_ARRIVAL:
+ handle_interface(1, ifan->ifan_name);
+ break;
+ case IFAN_DEPARTURE:
+ handle_interface(-1, ifan->ifan_name);
+ break;
+ }
+ break;
+#endif
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *)(void *)p;
+ memset(ifname, 0, sizeof(ifname));
+ if (if_indextoname(ifm->ifm_index, ifname))
+ handle_interface(0, ifname);
+ break;
+ case RTM_DELETE:
+ if (!(rtm->rtm_addrs & RTA_DST) ||
+ !(rtm->rtm_addrs & RTA_GATEWAY) ||
+ !(rtm->rtm_addrs & RTA_NETMASK))
+ break;
+ if (rtm->rtm_pid == getpid())
+ break;
+ cp = (char *)(void *)(rtm + 1);
+ sa = (struct sockaddr *)(void *)cp;
+ if (sa->sa_family != AF_INET)
+ break;
+ get_addrs(rtm->rtm_addrs, cp, rti_info);
+ rt.iface = NULL;
+ rt.next = NULL;
+ COPYOUT(rt.dest, rti_info[RTAX_DST]);
+ COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
+ COPYOUT(rt.gate, rti_info[RTAX_GATEWAY]);
+ route_deleted(&rt);
+ break;
+#ifdef RTM_CHGADDR
+ case RTM_CHGADDR: /* FALLTHROUGH */
+#endif
+ case RTM_DELADDR: /* FALLTHROUGH */
+ case RTM_NEWADDR:
+ ifam = (struct ifa_msghdr *)(void *)p;
+ if (!if_indextoname(ifam->ifam_index, ifname))
+ break;
+ cp = (char *)(void *)(ifam + 1);
+ get_addrs(ifam->ifam_addrs, cp, rti_info);
+ if (rti_info[RTAX_IFA] == NULL)
+ break;
+ switch (rti_info[RTAX_IFA]->sa_family) {
+#ifdef RTM_CHGADDR
+ case AF_LINK:
+ if (rtm->rtm_type != RTM_CHGADDR)
+ break;
+ memcpy(&sdl, rti_info[RTAX_IFA],
+ rti_info[RTAX_IFA]->sa_len);
+ hwaddr = xmalloc(sdl.sdl_alen);
+ memcpy(hwaddr, LLADDR(&sdl),
+ sdl.sdl_alen);
+ handle_hwaddr(ifname, hwaddr,
+ sdl.sdl_alen);
+ break;
+#endif
+ case AF_INET:
+ case 255: /* FIXME: Why 255? */
+ COPYOUT(rt.dest, rti_info[RTAX_IFA]);
+ COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
+ COPYOUT(rt.gate, rti_info[RTAX_BRD]);
+ handle_ifa(rtm->rtm_type, ifname,
+ &rt.dest, &rt.net, &rt.gate);
+ break;
+ }
+ break;
+ }
}
}
- return 0;
}
diff --git a/if-linux-wireless.c b/if-linux-wireless.c
new file mode 100644
index 0000000..dce1892
--- /dev/null
+++ b/if-linux-wireless.c
@@ -0,0 +1,89 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2009-2010 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 IS A NASTY HACK THAT SHOULD NEVER HAVE HAPPENED
+ * Basically we cannot include linux/if.h and net/if.h because
+ * they have conflicting structures.
+ * Sadly, linux/wireless.h includes linux/if.h all the time.
+ * Some kernel-header installs fix this and some do not.
+ * This file solely exists for those who do not.
+ *
+ * We *could* include wireless.h as that is designed for userspace,
+ * but that then depends on the correct version of wireless-tools being
+ * installed which isn't always the case.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <linux/rtnetlink.h>
+/* Support older kernels */
+#ifdef IFLA_WIRELESS
+# include <linux/if.h>
+# include <linux/wireless.h>
+#else
+# define IFLA_WIRELESS (IFLA_MASTER + 1)
+#endif
+
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+
+/* We can't include net.h or dhcpcd.h because
+ * they would pull in net/if.h, which defeats the purpose of this hack. */
+#define IF_SSIDSIZE 33
+int getifssid(const char *ifname, char *ssid);
+
+int
+getifssid(const char *ifname, char *ssid)
+{
+#ifdef SIOCGIWESSID
+ int s, retval;
+ struct iwreq iwr;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return -1;
+ memset(&iwr, 0, sizeof(iwr));
+ strlcpy(iwr.ifr_name, ifname, sizeof(iwr.ifr_name));
+ iwr.u.essid.pointer = ssid;
+ iwr.u.essid.length = IF_SSIDSIZE - 1;
+
+ if (ioctl(s, SIOCGIWESSID, &iwr) == 0) {
+ retval = iwr.u.essid.length;
+ ssid[retval] = '\0';
+ } else
+ retval = -1;
+ close(s);
+ return retval;
+#else
+ /* Stop gcc warning about unused paramters */
+ ifname = ssid;
+ return -1;
+#endif
+}
diff --git a/if-linux.c b/if-linux.c
index 114a084..c944a1a 100644
--- a/if-linux.c
+++ b/if-linux.c
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -29,68 +29,127 @@
#include <sys/types.h>
#include <sys/socket.h>
-#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/param.h>
-#include <arpa/inet.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
-#include <netinet/ether.h>
-#include <netpacket/packet.h>
+
+/* Support older kernels */
+#ifndef IFLA_WIRELESS
+# define IFLA_WIRELESS (IFLA_MASTER + 1)
+#endif
#include <errno.h>
+#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-/* Support older kernels */
-#ifndef IFLA_WIRELESS
-# define IFLA_WIRELSSS (IFLFA_MASTER + 1)
-#endif
-
#include "config.h"
#include "common.h"
+#include "configure.h"
#include "dhcp.h"
#include "net.h"
-#define BUFFERLEN 256
+static int sock_fd;
+static struct sockaddr_nl sock_nl;
+
+int
+if_init(struct interface *iface)
+{
+ char path[PATH_MAX];
+ FILE *fp;
+ int n;
+
+ /* We enable promote_secondaries so that we can do this
+ * add 192.168.1.2/24
+ * add 192.168.1.3/24
+ * del 192.168.1.2/24
+ * and the subnet mask moves onto 192.168.1.3/24
+ * This matches the behaviour of BSD which makes coding dhcpcd
+ * a little easier as there's just one behaviour. */
+ snprintf(path, sizeof(path),
+ "/proc/sys/net/ipv4/conf/%s/promote_secondaries",
+ iface->name);
+
+ fp = fopen(path, "w");
+ if (fp == NULL)
+ return errno == ENOENT ? 0 : -1;
+ n = fprintf(fp, "1");
+ fclose(fp);
+ return n == -1 ? -1 : 0;
+}
int
-open_link_socket(struct interface *iface)
+if_conf(struct interface *iface)
+{
+ char path[PATH_MAX], buf[1];
+ FILE *fp;
+
+ /* Some qeth setups require the use of the broadcast flag. */
+ snprintf(path, sizeof(path),
+ "/sys/class/net/%s/device/layer2",
+ iface->name);
+
+ fp = fopen(path, "r");
+ if (fp == NULL)
+ return errno == ENOENT ? 0 : -1;
+ if (fgets(buf, sizeof(buf), fp) != NULL && buf[0] == '0')
+ iface->state->options->options |= DHCPCD_BROADCAST;
+ fclose(fp);
+ return 0;
+}
+
+static int
+_open_link_socket(struct sockaddr_nl *nl)
{
int fd;
- struct sockaddr_nl nl;
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
return -1;
- memset(&nl, 0, sizeof(nl));
- nl.nl_family = AF_NETLINK;
- nl.nl_groups = RTMGRP_LINK;
- if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1)
+ nl->nl_family = AF_NETLINK;
+ if (bind(fd, (struct sockaddr *)nl, sizeof(*nl)) == -1)
return -1;
set_cloexec(fd);
- if (iface->link_fd != -1)
- close(iface->link_fd);
- iface->link_fd = fd;
- return 0;
+ return fd;
+}
+
+int
+init_sockets(void)
+{
+ if ((socket_afnet = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return -1;
+ set_cloexec(socket_afnet);
+ sock_fd = _open_link_socket(&sock_nl);
+ set_cloexec(sock_fd);
+ return sock_fd;
+}
+
+int
+open_link_socket(void)
+{
+ struct sockaddr_nl snl;
+
+ memset(&snl, 0, sizeof(snl));
+ snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR;
+ return _open_link_socket(&snl);
}
static int
get_netlink(int fd, int flags,
- int (*callback)(struct nlmsghdr *, const char *),
- const char *ifname)
+ int (*callback)(struct nlmsghdr *))
{
- char *buffer = NULL;
- ssize_t bytes;
+ char *buf = NULL, *nbuf;
+ ssize_t buflen = 0, bytes;
struct nlmsghdr *nlm;
int r = -1;
- buffer = xzalloc(sizeof(char) * BUFFERLEN);
for (;;) {
- bytes = recv(fd, buffer, BUFFERLEN, flags);
+ bytes = recv(fd, NULL, 0,
+ flags | MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC);
if (bytes == -1) {
if (errno == EAGAIN) {
r = 0;
@@ -99,24 +158,48 @@ get_netlink(int fd, int flags,
if (errno == EINTR)
continue;
goto eexit;
+ } else if (bytes == buflen) {
+ /* Support kernels older than 2.6.22 */
+ if (bytes == 0)
+ bytes = 512;
+ else
+ bytes *= 2;
}
- for (nlm = (struct nlmsghdr *)buffer;
+ if (buflen < bytes) {
+ /* Alloc 1 more so we work with older kernels */
+ buflen = bytes + 1;
+ nbuf = realloc(buf, buflen);
+ if (nbuf == NULL)
+ goto eexit;
+ buf = nbuf;
+ }
+ bytes = recv(fd, buf, buflen, flags);
+ if (bytes == -1) {
+ if (errno == EAGAIN) {
+ r = 0;
+ goto eexit;
+ }
+ if (errno == EINTR)
+ continue;
+ goto eexit;
+ }
+ for (nlm = (struct nlmsghdr *)buf;
NLMSG_OK(nlm, (size_t)bytes);
nlm = NLMSG_NEXT(nlm, bytes))
{
- r = callback(nlm, ifname);
+ r = callback(nlm);
if (r != 0)
goto eexit;
}
}
eexit:
- free(buffer);
+ free(buf);
return r;
}
static int
-err_netlink(struct nlmsghdr *nlm, _unused const char *ifname)
+err_netlink(struct nlmsghdr *nlm)
{
struct nlmsgerr *err;
int l;
@@ -136,13 +219,131 @@ err_netlink(struct nlmsghdr *nlm, _unused const char *ifname)
}
static int
-link_netlink(struct nlmsghdr *nlm, const char *ifname)
+link_route(struct nlmsghdr *nlm)
+{
+ int len, idx, metric;
+ struct rtattr *rta;
+ struct rtmsg *rtm;
+ struct rt rt;
+ char ifn[IF_NAMESIZE + 1];
+
+ if (nlm->nlmsg_type != RTM_DELROUTE)
+ return 0;
+
+ len = nlm->nlmsg_len - sizeof(*nlm);
+ if ((size_t)len < sizeof(*rtm)) {
+ errno = EBADMSG;
+ return -1;
+ }
+ rtm = NLMSG_DATA(nlm);
+ if (rtm->rtm_type != RTN_UNICAST ||
+ rtm->rtm_table != RT_TABLE_MAIN ||
+ rtm->rtm_family != AF_INET ||
+ nlm->nlmsg_pid == (uint32_t)getpid())
+ return 1;
+ rta = (struct rtattr *) ((char *)rtm + NLMSG_ALIGN(sizeof(*rtm)));
+ len = NLMSG_PAYLOAD(nlm, sizeof(*rtm));
+ rt.iface = NULL;
+ rt.dest.s_addr = INADDR_ANY;
+ rt.net.s_addr = INADDR_ANY;
+ rt.gate.s_addr = INADDR_ANY;
+ rt.next = NULL;
+ metric = 0;
+ while (RTA_OK(rta, len)) {
+ switch (rta->rta_type) {
+ case RTA_DST:
+ memcpy(&rt.dest.s_addr, RTA_DATA(rta),
+ sizeof(rt.dest.s_addr));
+ break;
+ case RTA_GATEWAY:
+ memcpy(&rt.gate.s_addr, RTA_DATA(rta),
+ sizeof(rt.gate.s_addr));
+ break;
+ case RTA_OIF:
+ idx = *(int *)RTA_DATA(rta);
+ if (if_indextoname(idx, ifn))
+ rt.iface = find_interface(ifn);
+ break;
+ case RTA_PRIORITY:
+ metric = *(int *)RTA_DATA(rta);
+ break;
+ }
+ rta = RTA_NEXT(rta, len);
+ }
+ if (rt.iface != NULL) {
+ if (metric == rt.iface->metric) {
+ inet_cidrtoaddr(rtm->rtm_dst_len, &rt.net);
+ route_deleted(&rt);
+ }
+ }
+ return 1;
+}
+
+static int
+link_addr(struct nlmsghdr *nlm)
+{
+ int len;
+ struct rtattr *rta;
+ struct ifaddrmsg *ifa;
+ struct in_addr addr, net, dest;
+ char ifn[IF_NAMESIZE + 1];
+ struct interface *iface;
+
+ if (nlm->nlmsg_type != RTM_DELADDR && nlm->nlmsg_type != RTM_NEWADDR)
+ return 0;
+
+ len = nlm->nlmsg_len - sizeof(*nlm);
+ if ((size_t)len < sizeof(*ifa)) {
+ errno = EBADMSG;
+ return -1;
+ }
+ if (nlm->nlmsg_pid == (uint32_t)getpid())
+ return 1;
+ ifa = NLMSG_DATA(nlm);
+ if (if_indextoname(ifa->ifa_index, ifn) == NULL)
+ return -1;
+ iface = find_interface(ifn);
+ if (iface == NULL)
+ return 1;
+ rta = (struct rtattr *) IFA_RTA(ifa);
+ len = NLMSG_PAYLOAD(nlm, sizeof(*ifa));
+ addr.s_addr = dest.s_addr = INADDR_ANY;
+ dest.s_addr = INADDR_ANY;
+ inet_cidrtoaddr(ifa->ifa_prefixlen, &net);
+ while (RTA_OK(rta, len)) {
+ switch (rta->rta_type) {
+ case IFA_ADDRESS:
+ if (iface->flags & IFF_POINTOPOINT) {
+ memcpy(&dest.s_addr, RTA_DATA(rta),
+ sizeof(addr.s_addr));
+ }
+ break;
+ case IFA_LOCAL:
+ memcpy(&addr.s_addr, RTA_DATA(rta),
+ sizeof(addr.s_addr));
+ break;
+ }
+ rta = RTA_NEXT(rta, len);
+ }
+ handle_ifa(nlm->nlmsg_type, ifn, &addr, &net, &dest);
+ return 1;
+}
+
+static int
+link_netlink(struct nlmsghdr *nlm)
{
int len;
struct rtattr *rta;
struct ifinfomsg *ifi;
char ifn[IF_NAMESIZE + 1];
+ len = link_route(nlm);
+ if (len != 0)
+ return len;
+ len = link_addr(nlm);
+ if (len != 0)
+ return len;
+
if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK)
return 0;
len = nlm->nlmsg_len - sizeof(*nlm);
@@ -152,7 +353,7 @@ link_netlink(struct nlmsghdr *nlm, const char *ifname)
}
ifi = NLMSG_DATA(nlm);
if (ifi->ifi_flags & IFF_LOOPBACK)
- return 0;
+ return 1;
rta = (struct rtattr *) ((char *)ifi + NLMSG_ALIGN(sizeof(*ifi)));
len = NLMSG_PAYLOAD(nlm, sizeof(*ifi));
*ifn = '\0';
@@ -161,8 +362,8 @@ link_netlink(struct nlmsghdr *nlm, const char *ifname)
case IFLA_WIRELESS:
/* Ignore wireless messages */
if (nlm->nlmsg_type == RTM_NEWLINK &&
- ifi->ifi_change == 0)
- return 0;
+ ifi->ifi_change == 0)
+ return 1;
break;
case IFLA_IFNAME:
strlcpy(ifn, RTA_DATA(rta), sizeof(ifn));
@@ -170,62 +371,53 @@ link_netlink(struct nlmsghdr *nlm, const char *ifname)
}
rta = RTA_NEXT(rta, len);
}
-
- if (strncmp(ifname, ifn, sizeof(ifn)) == 0)
- return 1;
- return 0;
+ if (nlm->nlmsg_type == RTM_NEWLINK)
+ len = ifi->ifi_change == ~0U ? 1 : 0;
+ else
+ len = -1;
+ handle_interface(len, ifn);
+ return 1;
}
int
-link_changed(struct interface *iface)
+manage_link(int fd)
{
- return get_netlink(iface->link_fd, MSG_DONTWAIT,
- &link_netlink, iface->name);
+ return get_netlink(fd, MSG_DONTWAIT, &link_netlink);
}
static int
send_netlink(struct nlmsghdr *hdr)
{
- int fd, r;
- struct sockaddr_nl nl;
+ int r;
struct iovec iov;
struct msghdr msg;
static unsigned int seq;
- if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
- return -1;
- memset(&nl, 0, sizeof(nl));
- nl.nl_family = AF_NETLINK;
- if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) {
- close(fd);
- return -1;
- }
memset(&iov, 0, sizeof(iov));
iov.iov_base = hdr;
iov.iov_len = hdr->nlmsg_len;
memset(&msg, 0, sizeof(msg));
- msg.msg_name = &nl;
- msg.msg_namelen = sizeof(nl);
+ msg.msg_name = &sock_nl;
+ msg.msg_namelen = sizeof(sock_nl);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* Request a reply */
hdr->nlmsg_flags |= NLM_F_ACK;
hdr->nlmsg_seq = ++seq;
- if (sendmsg(fd, &msg, 0) != -1)
- r = get_netlink(fd, 0, &err_netlink, NULL);
+ if (sendmsg(sock_fd, &msg, 0) != -1)
+ r = get_netlink(sock_fd, 0, &err_netlink);
else
r = -1;
- close(fd);
return r;
}
-#define NLMSG_TAIL(nmsg) \
+#define NLMSG_TAIL(nmsg) \
((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len)))
static int
add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type,
- const void *data, int alen)
+ const void *data, int alen)
{
int len = RTA_LENGTH(alen);
struct rtattr *rta;
@@ -279,9 +471,9 @@ struct nlmr
};
int
-if_address(const char *ifname,
- const struct in_addr *address, const struct in_addr *netmask,
- const struct in_addr *broadcast, int action)
+if_address(const struct interface *iface,
+ const struct in_addr *address, const struct in_addr *netmask,
+ const struct in_addr *broadcast, int action)
{
struct nlma *nlm;
int retval = 0;
@@ -294,7 +486,7 @@ if_address(const char *ifname,
nlm->hdr.nlmsg_type = RTM_NEWADDR;
} else
nlm->hdr.nlmsg_type = RTM_DELADDR;
- if (!(nlm->ifa.ifa_index = if_nametoindex(ifname))) {
+ if (!(nlm->ifa.ifa_index = if_nametoindex(iface->name))) {
free(nlm);
errno = ENODEV;
return -1;
@@ -303,12 +495,12 @@ if_address(const char *ifname,
nlm->ifa.ifa_prefixlen = inet_ntocidr(*netmask);
/* This creates the aliased interface */
add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL,
- ifname, strlen(ifname) + 1);
+ iface->name, strlen(iface->name) + 1);
add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL,
- &address->s_addr, sizeof(address->s_addr));
- if (action >= 0)
+ &address->s_addr, sizeof(address->s_addr));
+ if (action >= 0 && broadcast)
add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST,
- &broadcast->s_addr, sizeof(broadcast->s_addr));
+ &broadcast->s_addr, sizeof(broadcast->s_addr));
if (send_netlink(&nlm->hdr) == -1)
retval = -1;
@@ -318,8 +510,8 @@ if_address(const char *ifname,
int
if_route(const struct interface *iface,
- const struct in_addr *destination, const struct in_addr *netmask,
- const struct in_addr *gateway, int metric, int action)
+ const struct in_addr *destination, const struct in_addr *netmask,
+ const struct in_addr *gateway, int metric, int action)
{
struct nlmr *nlm;
unsigned int ifindex;
@@ -336,14 +528,7 @@ if_route(const struct interface *iface,
if (action == 0)
nlm->hdr.nlmsg_flags = NLM_F_REPLACE;
else if (action == 1)
- /*
- * ers@google:
- * commented out NLM_F_EXCL here and below. We
- * sometimes keep one interface up while we are
- * configuring the other one, and this flag
- * causes route addition to fail.
- */
- nlm->hdr.nlmsg_flags = NLM_F_CREATE /* | NLM_F_EXCL */;
+ nlm->hdr.nlmsg_flags = NLM_F_CREATE /*| NLM_F_EXCL*/;
else
nlm->hdr.nlmsg_type = RTM_DELROUTE;
nlm->hdr.nlmsg_flags |= NLM_F_REQUEST;
@@ -363,7 +548,7 @@ if_route(const struct interface *iface,
nlm->rt.rtm_protocol = RTPROT_BOOT;
if (gateway->s_addr == INADDR_ANY ||
(gateway->s_addr == destination->s_addr &&
- netmask->s_addr == INADDR_BROADCAST))
+ netmask->s_addr == INADDR_BROADCAST))
nlm->rt.rtm_scope = RT_SCOPE_LINK;
else
nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE;
@@ -372,16 +557,16 @@ if_route(const struct interface *iface,
nlm->rt.rtm_dst_len = inet_ntocidr(*netmask);
add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
- &destination->s_addr, sizeof(destination->s_addr));
+ &destination->s_addr, sizeof(destination->s_addr));
if (nlm->rt.rtm_protocol == RTPROT_KERNEL) {
add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_PREFSRC,
- &iface->addr.s_addr, sizeof(iface->addr.s_addr));
+ &iface->addr.s_addr, sizeof(iface->addr.s_addr));
}
/* If destination == gateway then don't add the gateway */
if (destination->s_addr != gateway->s_addr ||
netmask->s_addr != INADDR_BROADCAST)
add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
- &gateway->s_addr, sizeof(gateway->s_addr));
+ &gateway->s_addr, sizeof(gateway->s_addr));
add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex);
add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric);
diff --git a/if-options.c b/if-options.c
new file mode 100644
index 0000000..88b43d7
--- /dev/null
+++ b/if-options.c
@@ -0,0 +1,912 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "config.h"
+#include "common.h"
+#include "if-options.h"
+#include "net.h"
+#include "platform.h"
+
+/* These options only make sense in the config file, so don't use any
+ valid short options for them */
+#define O_BASE MAX('z', 'Z') + 1
+#define O_ARPING O_BASE + 1
+#define O_FALLBACK O_BASE + 2
+#define O_DESTINATION O_BASE + 3
+
+const struct option cf_options[] = {
+ {"background", no_argument, NULL, 'b'},
+ {"script", required_argument, NULL, 'c'},
+ {"debug", no_argument, NULL, 'd'},
+ {"env", required_argument, NULL, 'e'},
+ {"config", required_argument, NULL, 'f'},
+ {"reconfigure", no_argument, NULL, 'g'},
+ {"hostname", optional_argument, NULL, 'h'},
+ {"vendorclassid", optional_argument, NULL, 'i'},
+ {"release", no_argument, NULL, 'k'},
+ {"leasetime", required_argument, NULL, 'l'},
+ {"metric", required_argument, NULL, 'm'},
+ {"rebind", no_argument, NULL, 'n'},
+ {"option", required_argument, NULL, 'o'},
+ {"persistent", no_argument, NULL, 'p'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"request", optional_argument, NULL, 'r'},
+ {"inform", optional_argument, NULL, 's'},
+ {"timeout", required_argument, NULL, 't'},
+ {"userclass", required_argument, NULL, 'u'},
+ {"vendor", required_argument, NULL, 'v'},
+ {"waitip", no_argument, NULL, 'w'},
+ {"exit", no_argument, NULL, 'x'},
+ {"allowinterfaces", required_argument, NULL, 'z'},
+ {"reboot", required_argument, NULL, 'y'},
+ {"noarp", no_argument, NULL, 'A'},
+ {"nobackground", no_argument, NULL, 'B'},
+ {"nohook", required_argument, NULL, 'C'},
+ {"duid", no_argument, NULL, 'D'},
+ {"lastlease", no_argument, NULL, 'E'},
+ {"fqdn", optional_argument, NULL, 'F'},
+ {"nogateway", no_argument, NULL, 'G'},
+ {"xidhwaddr", no_argument, NULL, 'H'},
+ {"clientid", optional_argument, NULL, 'I'},
+ {"broadcast", no_argument, NULL, 'J'},
+ {"nolink", no_argument, NULL, 'K'},
+ {"noipv4ll", no_argument, NULL, 'L'},
+ {"nooption", optional_argument, NULL, 'O'},
+ {"require", required_argument, NULL, 'Q'},
+ {"static", required_argument, NULL, 'S'},
+ {"test", no_argument, NULL, 'T'},
+ {"dumplease", no_argument, NULL, 'U'},
+ {"variables", no_argument, NULL, 'V'},
+ {"whitelist", required_argument, NULL, 'W'},
+ {"blacklist", required_argument, NULL, 'X'},
+ {"denyinterfaces", required_argument, NULL, 'Z'},
+ {"arping", required_argument, NULL, O_ARPING},
+ {"destination", required_argument, NULL, O_DESTINATION},
+ {"fallback", required_argument, NULL, O_FALLBACK},
+ {NULL, 0, NULL, '\0'}
+};
+
+static int
+atoint(const char *s)
+{
+ char *t;
+ long n;
+
+ errno = 0;
+ n = strtol(s, &t, 0);
+ if ((errno != 0 && n == 0) || s == t ||
+ (errno == ERANGE && (n == LONG_MAX || n == LONG_MIN)))
+ {
+ syslog(LOG_ERR, "`%s' out of range", s);
+ return -1;
+ }
+
+ return (int)n;
+}
+
+static char *
+add_environ(struct if_options *ifo, const char *value, int uniq)
+{
+ char **newlist;
+ char **lst = ifo->environ;
+ size_t i = 0, l, lv;
+ char *match = NULL, *p;
+
+ match = xstrdup(value);
+ p = strchr(match, '=');
+ if (p)
+ *p++ = '\0';
+ l = strlen(match);
+
+ while (lst && lst[i]) {
+ if (match && strncmp(lst[i], match, l) == 0) {
+ if (uniq) {
+ free(lst[i]);
+ lst[i] = xstrdup(value);
+ } else {
+ /* Append a space and the value to it */
+ l = strlen(lst[i]);
+ lv = strlen(p);
+ lst[i] = xrealloc(lst[i], l + lv + 2);
+ lst[i][l] = ' ';
+ memcpy(lst[i] + l + 1, p, lv);
+ lst[i][l + lv + 1] = '\0';
+ }
+ free(match);
+ return lst[i];
+ }
+ i++;
+ }
+
+ newlist = xrealloc(lst, sizeof(char *) * (i + 2));
+ newlist[i] = xstrdup(value);
+ newlist[i + 1] = NULL;
+ ifo->environ = newlist;
+ free(match);
+ return newlist[i];
+}
+
+#define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0)
+static ssize_t
+parse_string_hwaddr(char *sbuf, ssize_t slen, const char *str, int clid)
+{
+ ssize_t l;
+ const char *p;
+ int i, punt_last = 0;
+ char c[4];
+
+ /* If surrounded by quotes then it's a string */
+ if (*str == '"') {
+ str++;
+ l = strlen(str);
+ p = str + l - 1;
+ if (*p == '"')
+ punt_last = 1;
+ } else {
+ l = hwaddr_aton(NULL, str);
+ if (l > 1) {
+ if (l > slen) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ hwaddr_aton((uint8_t *)sbuf, str);
+ return l;
+ }
+ }
+
+ /* Process escapes */
+ l = 0;
+ /* If processing a string on the clientid, first byte should be
+ * 0 to indicate a non hardware type */
+ if (clid && *str) {
+ *sbuf++ = 0;
+ l++;
+ }
+ c[3] = '\0';
+ while (*str) {
+ if (++l > slen) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ if (*str == '\\') {
+ str++;
+ switch(*str) {
+ case '\0':
+ break;
+ case 'b':
+ *sbuf++ = '\b';
+ str++;
+ break;
+ case 'n':
+ *sbuf++ = '\n';
+ str++;
+ break;
+ case 'r':
+ *sbuf++ = '\r';
+ str++;
+ break;
+ case 't':
+ *sbuf++ = '\t';
+ str++;
+ break;
+ case 'x':
+ /* Grab a hex code */
+ c[1] = '\0';
+ for (i = 0; i < 2; i++) {
+ if (isxdigit((unsigned char)*str) == 0)
+ break;
+ c[i] = *str++;
+ }
+ if (c[1] != '\0') {
+ c[2] = '\0';
+ *sbuf++ = strtol(c, NULL, 16);
+ } else
+ l--;
+ break;
+ case '0':
+ /* Grab an octal code */
+ c[2] = '\0';
+ for (i = 0; i < 3; i++) {
+ if (*str < '0' || *str > '7')
+ break;
+ c[i] = *str++;
+ }
+ if (c[2] != '\0') {
+ i = strtol(c, NULL, 8);
+ if (i > 255)
+ i = 255;
+ *sbuf ++= i;
+ } else
+ l--;
+ break;
+ default:
+ *sbuf++ = *str++;
+ }
+ } else
+ *sbuf++ = *str++;
+ }
+ if (punt_last) {
+ *--sbuf = '\0';
+ l--;
+ }
+ return l;
+}
+
+static char **
+splitv(int *argc, char **argv, const char *arg)
+{
+ char **v = argv;
+ char *o = xstrdup(arg), *p, *t;
+
+ p = o;
+ while ((t = strsep(&p, ", "))) {
+ (*argc)++;
+ v = xrealloc(v, sizeof(char *) * ((*argc)));
+ v[(*argc) - 1] = xstrdup(t);
+ }
+ free(o);
+ return v;
+}
+
+static int
+parse_addr(struct in_addr *addr, struct in_addr *net, const char *arg)
+{
+ char *p;
+ int i;
+
+ if (arg == NULL || *arg == '\0') {
+ if (addr != NULL)
+ addr->s_addr = 0;
+ if (net != NULL)
+ net->s_addr = 0;
+ return 0;
+ }
+ if ((p = strchr(arg, '/')) != NULL) {
+ *p++ = '\0';
+ if (net != NULL &&
+ (sscanf(p, "%d", &i) != 1 ||
+ inet_cidrtoaddr(i, net) != 0))
+ {
+ syslog(LOG_ERR, "`%s' is not a valid CIDR", p);
+ return -1;
+ }
+ }
+
+ if (addr != NULL && inet_aton(arg, addr) == 0) {
+ syslog(LOG_ERR, "`%s' is not a valid IP address", arg);
+ return -1;
+ }
+ if (p != NULL)
+ *--p = '/';
+ else if (net != NULL)
+ net->s_addr = get_netmask(addr->s_addr);
+ return 0;
+}
+
+static int
+parse_option(struct if_options *ifo, int opt, const char *arg)
+{
+ int i;
+ char *p = NULL, *np;
+ ssize_t s;
+ struct in_addr addr, addr2;
+ struct rt *rt;
+
+ switch(opt) {
+ case 'f': /* FALLTHROUGH */
+ case 'g': /* FALLTHROUGH */
+ case 'n': /* FALLTHROUGH */
+ case 'x': /* FALLTHROUGH */
+ case 'T': /* FALLTHROUGH */
+ case 'U': /* We need to handle non interface options */
+ break;
+ case 'b':
+ ifo->options |= DHCPCD_BACKGROUND;
+ break;
+ case 'c':
+ strlcpy(ifo->script, arg, sizeof(ifo->script));
+ break;
+ case 'd':
+ ifo->options |= DHCPCD_DEBUG;
+ break;
+ case 'e':
+ add_environ(ifo, arg, 1);
+ break;
+ case 'h':
+ if (arg) {
+ s = parse_string(ifo->hostname,
+ HOSTNAME_MAX_LEN, arg);
+ if (s == -1) {
+ syslog(LOG_ERR, "hostname: %m");
+ return -1;
+ }
+ if (s != 0 && ifo->hostname[0] == '.') {
+ syslog(LOG_ERR,
+ "hostname cannot begin with .");
+ return -1;
+ }
+ ifo->hostname[s] = '\0';
+ }
+ if (ifo->hostname[0] == '\0')
+ ifo->options &= ~DHCPCD_HOSTNAME;
+ else
+ ifo->options |= DHCPCD_HOSTNAME;
+ break;
+ case 'i':
+ if (arg)
+ s = parse_string((char *)ifo->vendorclassid + 1,
+ VENDORCLASSID_MAX_LEN, arg);
+ else
+ s = 0;
+ if (s == -1) {
+ syslog(LOG_ERR, "vendorclassid: %m");
+ return -1;
+ }
+ *ifo->vendorclassid = (uint8_t)s;
+ break;
+ case 'k':
+ ifo->options |= DHCPCD_RELEASE;
+ break;
+ case 'l':
+ if (*arg == '-') {
+ syslog(LOG_ERR,
+ "leasetime must be a positive value");
+ return -1;
+ }
+ errno = 0;
+ ifo->leasetime = (uint32_t)strtol(arg, NULL, 0);
+ if (errno == EINVAL || errno == ERANGE) {
+ syslog(LOG_ERR, "`%s' out of range", arg);
+ return -1;
+ }
+ break;
+ case 'm':
+ ifo->metric = atoint(arg);
+ if (ifo->metric < 0) {
+ syslog(LOG_ERR, "metric must be a positive value");
+ return -1;
+ }
+ break;
+ case 'o':
+ if (make_option_mask(ifo->requestmask, arg, 1) != 0) {
+ syslog(LOG_ERR, "unknown option `%s'", arg);
+ return -1;
+ }
+ break;
+ case 'p':
+ ifo->options |= DHCPCD_PERSISTENT;
+ break;
+ case 'q':
+ ifo->options |= DHCPCD_QUIET;
+ break;
+ case 'r':
+ if (parse_addr(&ifo->req_addr, NULL, arg) != 0)
+ return -1;
+ ifo->options |= DHCPCD_REQUEST;
+ ifo->req_mask.s_addr = 0;
+ break;
+ case 's':
+ if (arg && *arg != '\0') {
+ if (parse_addr(&ifo->req_addr, &ifo->req_mask,
+ arg) != 0)
+ return -1;
+ } else {
+ ifo->req_addr.s_addr = 0;
+ ifo->req_mask.s_addr = 0;
+ }
+ ifo->options |= DHCPCD_INFORM | DHCPCD_PERSISTENT;
+ ifo->options &= ~(DHCPCD_ARP | DHCPCD_STATIC);
+ break;
+ case 't':
+ ifo->timeout = atoint(arg);
+ if (ifo->timeout < 0) {
+ syslog(LOG_ERR, "timeout must be a positive value");
+ return -1;
+ }
+ break;
+ case 'u':
+ s = USERCLASS_MAX_LEN - ifo->userclass[0] - 1;
+ s = parse_string((char *)ifo->userclass +
+ ifo->userclass[0] + 2,
+ s, arg);
+ if (s == -1) {
+ syslog(LOG_ERR, "userclass: %m");
+ return -1;
+ }
+ if (s != 0) {
+ ifo->userclass[ifo->userclass[0] + 1] = s;
+ ifo->userclass[0] += s + 1;
+ }
+ break;
+ case 'v':
+ p = strchr(arg, ',');
+ if (!p || !p[1]) {
+ syslog(LOG_ERR, "invalid vendor format");
+ return -1;
+ }
+
+ /* If vendor starts with , then it is not encapsulated */
+ if (p == arg) {
+ arg++;
+ s = parse_string((char *)ifo->vendor + 1,
+ VENDOR_MAX_LEN, arg);
+ if (s == -1) {
+ syslog(LOG_ERR, "vendor: %m");
+ return -1;
+ }
+ ifo->vendor[0] = (uint8_t)s;
+ ifo->options |= DHCPCD_VENDORRAW;
+ break;
+ }
+
+ /* Encapsulated vendor options */
+ if (ifo->options & DHCPCD_VENDORRAW) {
+ ifo->options &= ~DHCPCD_VENDORRAW;
+ ifo->vendor[0] = 0;
+ }
+
+ *p = '\0';
+ i = atoint(arg);
+ arg = p + 1;
+ if (i < 1 || i > 254) {
+ syslog(LOG_ERR, "vendor option should be between"
+ " 1 and 254 inclusive");
+ return -1;
+ }
+ s = VENDOR_MAX_LEN - ifo->vendor[0] - 2;
+ if (inet_aton(arg, &addr) == 1) {
+ if (s < 6) {
+ s = -1;
+ errno = ENOBUFS;
+ } else
+ memcpy(ifo->vendor + ifo->vendor[0] + 3,
+ &addr.s_addr, sizeof(addr.s_addr));
+ } else {
+ s = parse_string((char *)ifo->vendor +
+ ifo->vendor[0] + 3, s, arg);
+ }
+ if (s == -1) {
+ syslog(LOG_ERR, "vendor: %m");
+ return -1;
+ }
+ if (s != 0) {
+ ifo->vendor[ifo->vendor[0] + 1] = i;
+ ifo->vendor[ifo->vendor[0] + 2] = s;
+ ifo->vendor[0] += s + 2;
+ }
+ break;
+ case 'w':
+ ifo->options |= DHCPCD_WAITIP;
+ break;
+ case 'y':
+ ifo->reboot = atoint(arg);
+ if (ifo->reboot < 0) {
+ syslog(LOG_ERR, "reboot must be a positive value");
+ return -1;
+ }
+ break;
+ case 'z':
+ ifav = splitv(&ifac, ifav, arg);
+ break;
+ case 'A':
+ ifo->options &= ~DHCPCD_ARP;
+ /* IPv4LL requires ARP */
+ ifo->options &= ~DHCPCD_IPV4LL;
+ break;
+ case 'B':
+ ifo->options &= ~DHCPCD_DAEMONISE;
+ break;
+ case 'C':
+ /* Commas to spaces for shell */
+ while ((p = strchr(arg, ',')))
+ *p = ' ';
+ s = strlen("skip_hooks=") + strlen(arg) + 1;
+ p = xmalloc(sizeof(char) * s);
+ snprintf(p, s, "skip_hooks=%s", arg);
+ add_environ(ifo, p, 0);
+ free(p);
+ break;
+ case 'D':
+ ifo->options |= DHCPCD_CLIENTID | DHCPCD_DUID;
+ break;
+ case 'E':
+ ifo->options |= DHCPCD_LASTLEASE;
+ break;
+ case 'F':
+ if (!arg) {
+ ifo->fqdn = FQDN_BOTH;
+ break;
+ }
+ if (strcmp(arg, "none") == 0)
+ ifo->fqdn = FQDN_NONE;
+ else if (strcmp(arg, "ptr") == 0)
+ ifo->fqdn = FQDN_PTR;
+ else if (strcmp(arg, "both") == 0)
+ ifo->fqdn = FQDN_BOTH;
+ else if (strcmp(arg, "disable") == 0)
+ ifo->fqdn = FQDN_DISABLE;
+ else {
+ syslog(LOG_ERR, "invalid value `%s' for FQDN", arg);
+ return -1;
+ }
+ break;
+ case 'G':
+ ifo->options &= ~DHCPCD_GATEWAY;
+ break;
+ case 'H':
+ ifo->options |= DHCPCD_XID_HWADDR;
+ break;
+ case 'I':
+ /* Strings have a type of 0 */;
+ ifo->clientid[1] = 0;
+ if (arg)
+ s = parse_string_hwaddr((char *)ifo->clientid + 1,
+ CLIENTID_MAX_LEN, arg, 1);
+ else
+ s = 0;
+ if (s == -1) {
+ syslog(LOG_ERR, "clientid: %m");
+ return -1;
+ }
+ ifo->options |= DHCPCD_CLIENTID;
+ ifo->clientid[0] = (uint8_t)s;
+ break;
+ case 'J':
+ ifo->options |= DHCPCD_BROADCAST;
+ break;
+ case 'K':
+ ifo->options &= ~DHCPCD_LINK;
+ break;
+ case 'L':
+ ifo->options &= ~DHCPCD_IPV4LL;
+ break;
+ case 'O':
+ if (make_option_mask(ifo->requestmask, arg, -1) != 0 ||
+ make_option_mask(ifo->requiremask, arg, -1) != 0 ||
+ make_option_mask(ifo->nomask, arg, 1) != 0)
+ {
+ syslog(LOG_ERR, "unknown option `%s'", arg);
+ return -1;
+ }
+ break;
+ case 'Q':
+ if (make_option_mask(ifo->requiremask, arg, 1) != 0 ||
+ make_option_mask(ifo->requestmask, arg, 1) != 0)
+ {
+ syslog(LOG_ERR, "unknown option `%s'", arg);
+ return -1;
+ }
+ break;
+ case 'S':
+ p = strchr(arg, '=');
+ if (p == NULL) {
+ syslog(LOG_ERR, "static assignment required");
+ return -1;
+ }
+ p++;
+ if (strncmp(arg, "ip_address=", strlen("ip_address=")) == 0) {
+ if (parse_addr(&ifo->req_addr, &ifo->req_mask, p) != 0)
+ return -1;
+
+ ifo->options |= DHCPCD_STATIC;
+ ifo->options &= ~DHCPCD_INFORM;
+ } else if (strncmp(arg, "routes=", strlen("routes=")) == 0 ||
+ strncmp(arg, "static_routes=", strlen("static_routes=")) == 0 ||
+ strncmp(arg, "classless_static_routes=", strlen("classless_static_routes=")) == 0 ||
+ strncmp(arg, "ms_classless_static_routes=", strlen("ms_classless_static_routes=")) == 0)
+ {
+ np = strchr(p, ' ');
+ if (np == NULL) {
+ syslog(LOG_ERR, "all routes need a gateway");
+ return -1;
+ }
+ *np++ = '\0';
+ while (*np == ' ')
+ np++;
+ if (ifo->routes == NULL) {
+ rt = ifo->routes = xmalloc(sizeof(*rt));
+ } else {
+ rt = ifo->routes;
+ while (rt->next)
+ rt = rt->next;
+ rt->next = xmalloc(sizeof(*rt));
+ rt = rt->next;
+ }
+ rt->next = NULL;
+ if (parse_addr(&rt->dest, &rt->net, p) == -1 ||
+ parse_addr(&rt->gate, NULL, np) == -1)
+ return -1;
+ } else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
+ if (ifo->routes == NULL) {
+ rt = ifo->routes = xzalloc(sizeof(*rt));
+ } else {
+ rt = ifo->routes;
+ while (rt->next)
+ rt = rt->next;
+ rt->next = xmalloc(sizeof(*rt));
+ rt = rt->next;
+ }
+ rt->dest.s_addr = INADDR_ANY;
+ rt->net.s_addr = INADDR_ANY;
+ rt->next = NULL;
+ if (parse_addr(&rt->gate, NULL, p) == -1)
+ return -1;
+ } else {
+ s = 0;
+ if (ifo->config != NULL) {
+ while (ifo->config[s] != NULL) {
+ if (strncmp(ifo->config[s], arg,
+ p - arg) == 0)
+ {
+ free(ifo->config[s]);
+ ifo->config[s] = xstrdup(arg);
+ return 1;
+ }
+ s++;
+ }
+ }
+ ifo->config = xrealloc(ifo->config,
+ sizeof(char *) * (s + 2));
+ ifo->config[s] = xstrdup(arg);
+ ifo->config[s + 1] = NULL;
+ }
+ break;
+ case 'W':
+ if (parse_addr(&addr, &addr2, arg) != 0)
+ return -1;
+ if (strchr(arg, '/') == NULL)
+ addr2.s_addr = INADDR_BROADCAST;
+ ifo->whitelist = xrealloc(ifo->whitelist,
+ sizeof(in_addr_t) * (ifo->whitelist_len + 2));
+ ifo->whitelist[ifo->whitelist_len++] = addr.s_addr;
+ ifo->whitelist[ifo->whitelist_len++] = addr2.s_addr;
+ break;
+ case 'X':
+ if (parse_addr(&addr, &addr2, arg) != 0)
+ return -1;
+ if (strchr(arg, '/') == NULL)
+ addr2.s_addr = INADDR_BROADCAST;
+ ifo->blacklist = xrealloc(ifo->blacklist,
+ sizeof(in_addr_t) * (ifo->blacklist_len + 2));
+ ifo->blacklist[ifo->blacklist_len++] = addr.s_addr;
+ ifo->blacklist[ifo->blacklist_len++] = addr2.s_addr;
+ break;
+ case 'Z':
+ ifdv = splitv(&ifdc, ifdv, arg);
+ break;
+ case O_ARPING:
+ if (parse_addr(&addr, NULL, arg) != 0)
+ return -1;
+ ifo->arping = xrealloc(ifo->arping,
+ sizeof(in_addr_t) * (ifo->arping_len + 1));
+ ifo->arping[ifo->arping_len++] = addr.s_addr;
+ break;
+ case O_DESTINATION:
+ if (make_option_mask(ifo->dstmask, arg, 2) != 0) {
+ if (errno == EINVAL)
+ syslog(LOG_ERR, "option `%s' does not take"
+ " an IPv4 address", arg);
+ else
+ syslog(LOG_ERR, "unknown option `%s'", arg);
+ return -1;
+ }
+ break;
+ case O_FALLBACK:
+ free(ifo->fallback);
+ ifo->fallback = xstrdup(arg);
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+parse_config_line(struct if_options *ifo, const char *opt, char *line)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(cf_options) / sizeof(cf_options[0]); i++) {
+ if (!cf_options[i].name ||
+ strcmp(cf_options[i].name, opt) != 0)
+ continue;
+
+ if (cf_options[i].has_arg == required_argument && !line) {
+ fprintf(stderr,
+ PACKAGE ": option requires an argument -- %s\n",
+ opt);
+ return -1;
+ }
+
+ return parse_option(ifo, cf_options[i].val, line);
+ }
+
+ fprintf(stderr, PACKAGE ": unknown option -- %s\n", opt);
+ return -1;
+}
+
+struct if_options *
+read_config(const char *file,
+ const char *ifname, const char *ssid, const char *profile)
+{
+ struct if_options *ifo;
+ FILE *f;
+ char *line, *option, *p, *platform;
+ int skip = 0, have_profile = 0;
+ struct utsname utn;
+
+ /* Seed our default options */
+ ifo = xzalloc(sizeof(*ifo));
+ ifo->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
+ ifo->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
+ ifo->timeout = DEFAULT_TIMEOUT;
+ ifo->reboot = DEFAULT_REBOOT;
+ ifo->metric = -1;
+ strlcpy(ifo->script, SCRIPT, sizeof(ifo->script));
+ gethostname(ifo->hostname, HOSTNAME_MAX_LEN);
+ /* Ensure that the hostname is NULL terminated */
+ ifo->hostname[HOSTNAME_MAX_LEN] = '\0';
+ if (strcmp(ifo->hostname, "(none)") == 0 ||
+ strcmp(ifo->hostname, "localhost") == 0)
+ ifo->hostname[0] = '\0';
+
+ platform = hardware_platform();
+ if (uname(&utn) == 0)
+ ifo->vendorclassid[0] = snprintf((char *)ifo->vendorclassid + 1,
+ VENDORCLASSID_MAX_LEN,
+ "%s-%s:%s-%s:%s%s%s", PACKAGE, VERSION,
+ utn.sysname, utn.release, utn.machine,
+ platform ? ":" : "", platform ? platform : "");
+ else
+ ifo->vendorclassid[0] = snprintf((char *)ifo->vendorclassid + 1,
+ VENDORCLASSID_MAX_LEN, "%s-%s", PACKAGE, VERSION);
+
+ /* Parse our options file */
+ f = fopen(file ? file : CONFIG, "r");
+ if (f == NULL) {
+ if (file != NULL)
+ syslog(LOG_ERR, "fopen `%s': %m", file);
+ return ifo;
+ }
+
+ while ((line = get_line(f))) {
+ option = strsep(&line, " \t");
+ /* Trim trailing whitespace */
+ if (line && *line) {
+ p = line + strlen(line) - 1;
+ while (p != line &&
+ (*p == ' ' || *p == '\t') &&
+ *(p - 1) != '\\')
+ *p-- = '\0';
+ }
+ /* Start of an interface block, skip if not ours */
+ if (strcmp(option, "interface") == 0) {
+ if (ifname && line && strcmp(line, ifname) == 0)
+ skip = 0;
+ else
+ skip = 1;
+ continue;
+ }
+ /* Start of an ssid block, skip if not ours */
+ if (strcmp(option, "ssid") == 0) {
+ if (ssid && line && strcmp(line, ssid) == 0)
+ skip = 0;
+ else
+ skip = 1;
+ continue;
+ }
+ /* Start of a profile block, skip if not ours */
+ if (strcmp(option, "profile") == 0) {
+ if (profile && line && strcmp(line, profile) == 0) {
+ skip = 0;
+ have_profile = 1;
+ } else
+ skip = 1;
+ continue;
+ }
+ if (skip)
+ continue;
+ parse_config_line(ifo, option, line);
+ }
+ fclose(f);
+
+ if (profile && !have_profile) {
+ free_options(ifo);
+ errno = ENOENT;
+ ifo = NULL;
+ }
+
+ /* Terminate the encapsulated options */
+ if (ifo && ifo->vendor[0] && !(ifo->options & DHCPCD_VENDORRAW)) {
+ ifo->vendor[0]++;
+ ifo->vendor[ifo->vendor[0]] = DHO_END;
+ }
+ return ifo;
+}
+
+int
+add_options(struct if_options *ifo, int argc, char **argv)
+{
+ int oi, opt, r = 1;
+
+ optind = 0;
+ while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
+ {
+ r = parse_option(ifo, opt, optarg);
+ if (r != 1)
+ break;
+ }
+ /* Terminate the encapsulated options */
+ if (r == 1 && ifo->vendor[0] && !(ifo->options & DHCPCD_VENDORRAW)) {
+ ifo->vendor[0]++;
+ ifo->vendor[ifo->vendor[0]] = DHO_END;
+ }
+ return r;
+}
+
+void
+free_options(struct if_options *ifo)
+{
+ size_t i;
+
+ if (ifo) {
+ if (ifo->environ) {
+ i = 0;
+ while (ifo->environ[i])
+ free(ifo->environ[i++]);
+ free(ifo->environ);
+ }
+ if (ifo->config) {
+ i = 0;
+ while (ifo->config[i])
+ free(ifo->config[i++]);
+ free(ifo->config);
+ }
+ free_routes(ifo->routes);
+ free(ifo->arping);
+ free(ifo->blacklist);
+ free(ifo->fallback);
+ free(ifo);
+ }
+}
diff --git a/if-options.h b/if-options.h
new file mode 100644
index 0000000..241cb4d
--- /dev/null
+++ b/if-options.h
@@ -0,0 +1,123 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef IF_OPTIONS_H
+#define IF_OPTIONS_H
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <getopt.h>
+#include <limits.h>
+
+/* Don't set any optional arguments here so we retain POSIX
+ * compatibility with getopt */
+#define IF_OPTS "bc:de:f:gh:i:kl:m:no:pqr:s:t:u:v:wxy:z:ABC:DEF:GHI:JKLO:Q:S:TUVW:X:Z:"
+
+#define DEFAULT_TIMEOUT 30
+#define DEFAULT_REBOOT 10
+
+#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
+#define VENDORCLASSID_MAX_LEN 255
+#define CLIENTID_MAX_LEN 48
+#define USERCLASS_MAX_LEN 255
+#define VENDOR_MAX_LEN 255
+
+#define DHCPCD_ARP (1 << 0)
+#define DHCPCD_RELEASE (1 << 1)
+#define DHCPCD_DOMAIN (1 << 2)
+#define DHCPCD_GATEWAY (1 << 3)
+#define DHCPCD_STATIC (1 << 4)
+#define DHCPCD_DEBUG (1 << 5)
+#define DHCPCD_LASTLEASE (1 << 7)
+#define DHCPCD_INFORM (1 << 8)
+#define DHCPCD_REQUEST (1 << 9)
+#define DHCPCD_IPV4LL (1 << 10)
+#define DHCPCD_DUID (1 << 11)
+#define DHCPCD_PERSISTENT (1 << 12)
+#define DHCPCD_DAEMONISE (1 << 14)
+#define DHCPCD_DAEMONISED (1 << 15)
+#define DHCPCD_TEST (1 << 16)
+#define DHCPCD_MASTER (1 << 17)
+#define DHCPCD_HOSTNAME (1 << 18)
+#define DHCPCD_CLIENTID (1 << 19)
+#define DHCPCD_LINK (1 << 20)
+#define DHCPCD_QUIET (1 << 21)
+#define DHCPCD_BACKGROUND (1 << 22)
+#define DHCPCD_VENDORRAW (1 << 23)
+#define DHCPCD_TIMEOUT_IPV4LL (1 << 24)
+#define DHCPCD_WAITIP (1 << 25)
+#define DHCPCD_WAITUP (1 << 26)
+#define DHCPCD_CSR_WARNED (1 << 27)
+#define DHCPCD_XID_HWADDR (1 << 28)
+#define DHCPCD_BROADCAST (1 << 29)
+#define DHCPCD_DUMPLEASE (1 << 30)
+
+extern const struct option cf_options[];
+
+struct if_options {
+ int metric;
+ uint8_t requestmask[256 / 8];
+ uint8_t requiremask[256 / 8];
+ uint8_t nomask[256 / 8];
+ uint8_t dstmask[256 / 8];
+ uint32_t leasetime;
+ time_t timeout;
+ time_t reboot;
+ int options;
+
+ struct in_addr req_addr;
+ struct in_addr req_mask;
+ struct rt *routes;
+ char **config;
+
+ char **environ;
+ char script[PATH_MAX];
+
+ char hostname[HOSTNAME_MAX_LEN + 1]; /* We don't store the length */
+ int fqdn;
+ uint8_t vendorclassid[VENDORCLASSID_MAX_LEN + 2];
+ char clientid[CLIENTID_MAX_LEN + 2];
+ uint8_t userclass[USERCLASS_MAX_LEN + 2];
+ uint8_t vendor[VENDOR_MAX_LEN + 2];
+
+ size_t blacklist_len;
+ in_addr_t *blacklist;
+ size_t whitelist_len;
+ in_addr_t *whitelist;
+ size_t arping_len;
+ in_addr_t *arping;
+ char *fallback;
+};
+
+struct if_options *read_config(const char *,
+ const char *, const char *, const char *);
+int add_options(struct if_options *, int, char **);
+void free_options(struct if_options *);
+
+#endif
diff --git a/if-pref.c b/if-pref.c
new file mode 100644
index 0000000..83b1b0f
--- /dev/null
+++ b/if-pref.c
@@ -0,0 +1,108 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+
+#include "config.h"
+#include "dhcpcd.h"
+#include "if-pref.h"
+#include "net.h"
+
+/* Interface comparer for working out ordering. */
+static int
+ifcmp(struct interface *si, struct interface *ti)
+{
+ int sill, till;
+
+ if (si->state && !ti->state)
+ return -1;
+ if (!si->state && ti->state)
+ return 1;
+ if (!si->state && !ti->state)
+ return 0;
+ /* If one has a lease and the other not, it takes precedence. */
+ if (si->state->new && !ti->state->new)
+ return -1;
+ if (!si->state->new && ti->state->new)
+ return 1;
+ /* If we are either, they neither have a lease, or they both have.
+ * We need to check for IPv4LL and make it non-preferred. */
+ if (si->state->new && ti->state->new) {
+ sill = (si->state->new->cookie == htonl(MAGIC_COOKIE));
+ till = (ti->state->new->cookie == htonl(MAGIC_COOKIE));
+ if (!sill && till)
+ return -1;
+ if (sill && !till)
+ return 1;
+ }
+ /* Then carrier status. */
+ if (si->carrier > ti->carrier)
+ return -1;
+ if (si->carrier < ti->carrier)
+ return 1;
+ /* Finally, metric */
+ if (si->metric < ti->metric)
+ return -1;
+ if (si->metric > ti->metric)
+ return 1;
+ return 0;
+}
+
+/* Sort the interfaces into a preferred order - best first, worst last. */
+void
+sort_interfaces(void)
+{
+ struct interface *sorted, *ifp, *ifn, *ift;
+
+ if (!ifaces || !ifaces->next)
+ return;
+ sorted = ifaces;
+ ifaces = ifaces->next;
+ sorted->next = NULL;
+ for (ifp = ifaces; ifp && (ifn = ifp->next, 1); ifp = ifn) {
+ /* Are we the new head? */
+ if (ifcmp(ifp, sorted) == -1) {
+ ifp->next = sorted;
+ sorted = ifp;
+ continue;
+ }
+ /* Do we fit in the middle? */
+ for (ift = sorted; ift->next; ift = ift->next) {
+ if (ifcmp(ifp, ift->next) == -1) {
+ ifp->next = ift->next;
+ ift->next = ifp;
+ break;
+ }
+ }
+ /* We must be at the end */
+ if (!ift->next) {
+ ift->next = ifp;
+ ifp->next = NULL;
+ }
+ }
+ ifaces = sorted;
+}
diff --git a/if-pref.h b/if-pref.h
new file mode 100644
index 0000000..dcedd60
--- /dev/null
+++ b/if-pref.h
@@ -0,0 +1,34 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef IF_PREF_H
+#define IF_PREF_H
+
+#include "dhcpcd.h"
+
+void sort_interfaces(void);
+#endif
diff --git a/ifaddrs.c b/ifaddrs.c
new file mode 100644
index 0000000..cb8fd76
--- /dev/null
+++ b/ifaddrs.c
@@ -0,0 +1,146 @@
+/* external/dhcpcd/ifaddrs.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");.
+** you may not use this file except in compliance with the License..
+** You may obtain a copy of the License at.
+**
+** http://www.apache.org/licenses/LICENSE-2.0.
+**
+** Unless required by applicable law or agreed to in writing, software.
+** distributed under the License is distributed on an "AS IS" BASIS,.
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied..
+** See the License for the specific language governing permissions and.
+** limitations under the License.
+*/
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include "ifaddrs.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <netinet/ether.h>
+#include <netdb.h>
+#include <linux/if_packet.h>
+#include <netinet/if_ether.h>
+#include <linux/if_arp.h>
+#include <netutils/ifc.h>
+
+struct ifaddrs *get_interface(const char *name, sa_family_t family)
+{
+ unsigned addr, mask, flags;
+ struct ifaddrs *ifa;
+ struct sockaddr_in *saddr = NULL;
+ struct sockaddr_in *smask = NULL;
+ struct sockaddr_ll *hwaddr = NULL;
+ unsigned char hwbuf[ETH_ALEN];
+
+ if(ifc_get_info(name, &addr, &mask, &flags))
+ return NULL;
+
+ if ((family == AF_INET) && (addr == 0))
+ return NULL;
+
+ ifa = malloc(sizeof(struct ifaddrs));
+ if (!ifa)
+ return NULL;
+ memset(ifa, 0, sizeof(struct ifaddrs));
+
+ ifa->ifa_name = malloc(strlen(name)+1);
+ if (!ifa->ifa_name) {
+ free(ifa);
+ return NULL;
+ }
+ strcpy(ifa->ifa_name, name);
+ ifa->ifa_flags = flags;
+
+ if (family == AF_INET) {
+ saddr = malloc(sizeof(struct sockaddr_in));
+ if (saddr) {
+ saddr->sin_addr.s_addr = addr;
+ saddr->sin_family = family;
+ }
+ ifa->ifa_addr = (struct sockaddr *)saddr;
+
+ if (mask != 0) {
+ smask = malloc(sizeof(struct sockaddr_in));
+ if (smask) {
+ smask->sin_addr.s_addr = mask;
+ smask->sin_family = family;
+ }
+ }
+ ifa->ifa_netmask = (struct sockaddr *)smask;
+ } else if (family == AF_PACKET) {
+ if (!ifc_get_hwaddr(name, hwbuf)) {
+ hwaddr = malloc(sizeof(struct sockaddr_ll));
+ if (hwaddr) {
+ memset(hwaddr, 0, sizeof(struct sockaddr_ll));
+ hwaddr->sll_family = family;
+ /* hwaddr->sll_protocol = ETHERTYPE_IP; */
+ hwaddr->sll_hatype = ARPHRD_ETHER;
+ hwaddr->sll_halen = ETH_ALEN;
+ memcpy(hwaddr->sll_addr, hwbuf, ETH_ALEN);
+ }
+ }
+ ifa->ifa_addr = (struct sockaddr *)hwaddr;
+ ifa->ifa_netmask = (struct sockaddr *)smask;
+ }
+ return ifa;
+}
+
+int getifaddrs(struct ifaddrs **ifap)
+{
+ DIR *d;
+ struct dirent *de;
+ struct ifaddrs *ifa;
+ struct ifaddrs *ifah = NULL;
+
+ if (!ifap)
+ return -1;
+ *ifap = NULL;
+
+ if (ifc_init())
+ return -1;
+
+ d = opendir("/sys/class/net");
+ if (d == 0)
+ return -1;
+ while ((de = readdir(d))) {
+ if (de->d_name[0] == '.')
+ continue;
+ ifa = get_interface(de->d_name, AF_INET);
+ if (ifa != NULL) {
+ ifa->ifa_next = ifah;
+ ifah = ifa;
+ }
+ ifa = get_interface(de->d_name, AF_PACKET);
+ if (ifa != NULL) {
+ ifa->ifa_next = ifah;
+ ifah = ifa;
+ }
+ }
+ *ifap = ifah;
+ closedir(d);
+ ifc_close();
+ return 0;
+}
+
+void freeifaddrs(struct ifaddrs *ifa)
+{
+ struct ifaddrs *ifp;
+
+ while (ifa) {
+ ifp = ifa;
+ free(ifp->ifa_name);
+ if (ifp->ifa_addr)
+ free(ifp->ifa_addr);
+ if (ifp->ifa_netmask)
+ free(ifp->ifa_netmask);
+ ifa = ifa->ifa_next;
+ free(ifp);
+ }
+}
diff --git a/ifaddrs.h b/ifaddrs.h
new file mode 100644
index 0000000..6356653
--- /dev/null
+++ b/ifaddrs.h
@@ -0,0 +1,34 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was generated from a glibc header of the same name.
+ *** It contains only constants, structures, and macros generated from
+ *** the original header, and thus, contains no copyrightable information.
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _IFADDRS_H
+#define _IFADDRS_H
+
+#include <sys/socket.h>
+
+struct ifaddrs {
+ struct ifaddrs *ifa_next;
+ char *ifa_name;
+ unsigned int ifa_flags;
+ struct sockaddr *ifa_addr;
+ struct sockaddr *ifa_netmask;
+ union {
+ struct sockaddr *ifu_broadaddr;
+ struct sockaddr *ifu_dstaddr;
+ } ifa_ifu;
+#define ifa_broadaddr ifa_ifu.ifu_broadaddr
+#define ifa_dstaddr ifa_ifu.ifu_dstaddr
+ void *ifa_data;
+};
+
+extern int getifaddrs(struct ifaddrs **ifap);
+
+extern void freeifaddrs(struct ifaddrs *ifa);
+
+#endif
diff --git a/ipv4ll.c b/ipv4ll.c
new file mode 100644
index 0000000..4336540
--- /dev/null
+++ b/ipv4ll.c
@@ -0,0 +1,156 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "arp.h"
+#include "common.h"
+#include "dhcpcd.h"
+#include "eloop.h"
+#include "if-options.h"
+#include "ipv4ll.h"
+#include "net.h"
+
+static struct dhcp_message *
+make_ipv4ll_lease(uint32_t addr)
+{
+ uint32_t u32;
+ struct dhcp_message *dhcp;
+ uint8_t *p;
+
+ dhcp = xzalloc(sizeof(*dhcp));
+ /* Put some LL options in */
+ dhcp->yiaddr = addr;
+ p = dhcp->options;
+ *p++ = DHO_SUBNETMASK;
+ *p++ = sizeof(u32);
+ u32 = htonl(LINKLOCAL_MASK);
+ memcpy(p, &u32, sizeof(u32));
+ p += sizeof(u32);
+ *p++ = DHO_BROADCAST;
+ *p++ = sizeof(u32);
+ u32 = htonl(LINKLOCAL_BRDC);
+ memcpy(p, &u32, sizeof(u32));
+ p += sizeof(u32);
+ *p++ = DHO_END;
+
+ return dhcp;
+}
+
+static struct dhcp_message *
+find_ipv4ll_lease(uint32_t old_addr)
+{
+ uint32_t addr;
+
+ for (;;) {
+ addr = htonl(LINKLOCAL_ADDR |
+ (((uint32_t)abs((int)arc4random())
+ % 0xFD00) + 0x0100));
+ if (addr != old_addr &&
+ IN_LINKLOCAL(ntohl(addr)))
+ break;
+ }
+ return make_ipv4ll_lease(addr);
+}
+
+void
+start_ipv4ll(void *arg)
+{
+ struct interface *iface = arg;
+ uint32_t addr;
+
+ delete_timeout(NULL, iface);
+ iface->state->probes = 0;
+ iface->state->claims = 0;
+ if (iface->addr.s_addr) {
+ iface->state->conflicts = 0;
+ if (IN_LINKLOCAL(htonl(iface->addr.s_addr))) {
+ send_arp_announce(iface);
+ return;
+ }
+ }
+
+ if (iface->state->offer == NULL)
+ addr = 0;
+ else {
+ addr = iface->state->offer->yiaddr;
+ free(iface->state->offer);
+ }
+ /* We maybe rebooting an IPv4LL address. */
+ if (!IN_LINKLOCAL(htonl(addr))) {
+ syslog(LOG_INFO, "%s: probing for an IPv4LL address",
+ iface->name);
+ addr = 0;
+ }
+ if (addr == 0)
+ iface->state->offer = find_ipv4ll_lease(addr);
+ else
+ iface->state->offer = make_ipv4ll_lease(addr);
+ iface->state->lease.frominfo = 0;
+ send_arp_probe(iface);
+}
+
+void
+handle_ipv4ll_failure(void *arg)
+{
+ struct interface *iface = arg;
+ time_t up;
+
+ if (iface->state->fail.s_addr == iface->addr.s_addr) {
+ up = uptime();
+ if (iface->state->defend + DEFEND_INTERVAL > up) {
+ syslog(LOG_DEBUG,
+ "%s: IPv4LL %d second defence failed",
+ iface->name, DEFEND_INTERVAL);
+ drop_config(iface, "EXPIRE");
+ iface->state->conflicts = -1;
+ } else {
+ syslog(LOG_DEBUG, "%s: defended IPv4LL address",
+ iface->name);
+ iface->state->defend = up;
+ return;
+ }
+ }
+
+ close_sockets(iface);
+ free(iface->state->offer);
+ iface->state->offer = NULL;
+ delete_timeout(NULL, iface);
+ if (++iface->state->conflicts > MAX_CONFLICTS) {
+ syslog(LOG_ERR, "%s: failed to acquire an IPv4LL address",
+ iface->name);
+ iface->state->interval = RATE_LIMIT_INTERVAL / 2;
+ start_discover(iface);
+ } else {
+ add_timeout_sec(PROBE_WAIT, start_ipv4ll, iface);
+ }
+}
diff --git a/ipv4ll.h b/ipv4ll.h
new file mode 100644
index 0000000..a5d8e9a
--- /dev/null
+++ b/ipv4ll.h
@@ -0,0 +1,33 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef IPV4LL_H
+#define IPV4LL_H
+
+void start_ipv4ll(void *);
+void handle_ipv4ll_failure(void *);
+#endif
diff --git a/logger.c b/logger.c
index 677228c..1ef423c 100644
--- a/logger.c
+++ b/logger.c
@@ -39,7 +39,7 @@
#include <android/log.h>
#endif
-static int loglevel = LOG_ERR;
+static int loglevel = LOG_INFO;
static char logprefix[12] = {0};
void
diff --git a/lpf.c b/lpf.c
index ae5dd03..2907d90 100644
--- a/lpf.c
+++ b/lpf.c
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -37,7 +37,7 @@
# include <asm/types.h> /* needed for 2.4 kernels for the below header */
# include <linux/filter.h>
# include <netpacket/packet.h>
-# define bpf_insn sock_filter
+# define bpf_insn sock_filter
# define BPF_SKIPTYPE
# define BPF_ETHCOOK -ETH_HLEN
# define BPF_WHOLEPACKET 0x0fffffff /* work around buggy LPF filters */
@@ -120,7 +120,7 @@ eexit:
ssize_t
send_raw_packet(const struct interface *iface, int protocol,
- const void *data, ssize_t len)
+ const void *data, ssize_t len)
{
union sockunion {
struct sockaddr sa;
@@ -140,7 +140,7 @@ send_raw_packet(const struct interface *iface, int protocol,
su.sll.sll_halen = iface->hwlen;
if (iface->family == ARPHRD_INFINIBAND)
memcpy(&su.sll.sll_addr,
- &ipv4_bcast_addr, sizeof(ipv4_bcast_addr));
+ &ipv4_bcast_addr, sizeof(ipv4_bcast_addr));
else
memset(&su.sll.sll_addr, 0xff, iface->hwlen);
if (protocol == ETHERTYPE_ARP)
diff --git a/net.c b/net.c
index 29344f8..e26b8d4 100644
--- a/net.c
+++ b/net.c
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -27,45 +27,52 @@
#include <sys/types.h>
#include <sys/ioctl.h>
+#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
+#include <arpa/inet.h>
#include <net/if.h>
#include <net/if_arp.h>
-#include <arpa/inet.h>
-#include <netinet/in_systm.h>
-#ifdef __linux__
-#include <netinet/ether.h>
-#include <netpacket/packet.h>
+#ifdef AF_LINK
+# include <net/if_dl.h>
+# include <net/if_types.h>
#endif
+#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */
#include <netinet/udp.h>
#undef __FAVOR_BSD
-#ifdef SIOCGIFMEDIA
-#include <net/if_media.h>
+#ifdef AF_PACKET
+# include <netpacket/packet.h>
#endif
-#include <arpa/inet.h>
-#ifdef AF_LINK
-# include <net/if_dl.h>
+#ifdef SIOCGIFMEDIA
+# include <net/if_media.h>
#endif
#include <ctype.h>
#include <errno.h>
+#include <ifaddrs.h>
+#include <fnmatch.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
#include <unistd.h>
#include "config.h"
#include "common.h"
#include "dhcp.h"
-#include "logger.h"
+#include "if-options.h"
#include "net.h"
#include "signals.h"
+static char hwaddr_buffer[(HWADDR_LEN * 3) + 1];
+
+int socket_afnet = -1;
+
int
inet_ntocidr(struct in_addr address)
{
@@ -76,7 +83,6 @@ inet_ntocidr(struct in_addr address)
cidr++;
mask <<= 1;
}
-
return cidr;
}
@@ -85,7 +91,7 @@ inet_cidrtoaddr(int cidr, struct in_addr *addr)
{
int ocets;
- if (cidr < 0 || cidr > 32) {
+ if (cidr < 1 || cidr > 32) {
errno = EINVAL;
return -1;
}
@@ -95,7 +101,7 @@ inet_cidrtoaddr(int cidr, struct in_addr *addr)
if (ocets > 0) {
memset(&addr->s_addr, 255, (size_t)ocets - 1);
memset((unsigned char *)&addr->s_addr + (ocets - 1),
- (256 - (1 << (32 - cidr) % 8)), 1);
+ (256 - (1 << (32 - cidr) % 8)), 1);
}
return 0;
@@ -123,8 +129,7 @@ get_netmask(uint32_t addr)
char *
hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
{
- static char buffer[(HWADDR_LEN * 3) + 1];
- char *p = buffer;
+ char *p = hwaddr_buffer;
size_t i;
for (i = 0; i < hwlen && i < HWADDR_LEN; i++) {
@@ -135,7 +140,7 @@ hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
*p ++= '\0';
- return buffer;
+ return hwaddr_buffer;
}
size_t
@@ -176,274 +181,368 @@ hwaddr_aton(unsigned char *buffer, const char *addr)
return len;
}
-int
-do_interface(const char *ifname,
- _unused unsigned char *hwaddr, _unused size_t *hwlen,
- struct in_addr *addr, struct in_addr *net, int get)
+struct interface *
+init_interface(const char *ifname)
{
- int s;
- struct ifconf ifc;
- int retval = 0, found = 0;
- int len = 10 * sizeof(struct ifreq);
- int lastlen = 0;
- char *p;
- union {
- char *buffer;
- struct ifreq *ifr;
- } ifreqs;
- struct sockaddr_in address;
- struct ifreq *ifr;
- struct sockaddr_in netmask;
-#ifdef AF_LINK
- struct sockaddr_dl *sdl;
-#endif
-
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- return -1;
+ struct ifreq ifr;
+ struct interface *iface = NULL;
- /* Not all implementations return the needed buffer size for
- * SIOGIFCONF so we loop like so for all until it works */
- memset(&ifc, 0, sizeof(ifc));
- for (;;) {
- ifc.ifc_len = len;
- ifc.ifc_buf = xmalloc((size_t)len);
- if (ioctl(s, SIOCGIFCONF, &ifc) == -1) {
- if (errno != EINVAL || lastlen != 0) {
- close(s);
- free(ifc.ifc_buf);
- return -1;
- }
- } else {
- if (ifc.ifc_len == lastlen)
- break;
- lastlen = ifc.ifc_len;
- }
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == -1)
+ goto eexit;
- free(ifc.ifc_buf);
- ifc.ifc_buf = NULL;
- len *= 2;
+ iface = xzalloc(sizeof(*iface));
+ strlcpy(iface->name, ifname, sizeof(iface->name));
+ iface->flags = ifr.ifr_flags;
+ /* We reserve the 100 range for virtual interfaces, if and when
+ * we can work them out. */
+ iface->metric = 200 + if_nametoindex(iface->name);
+ if (getifssid(ifname, iface->ssid) != -1) {
+ iface->wireless = 1;
+ iface->metric += 100;
}
- for (p = (char *)ifc.ifc_buf; p < (char *)ifc.ifc_buf + ifc.ifc_len;) {
- /* Cast the ifc buffer to an ifreq cleanly */
- ifreqs.buffer = p;
- ifr = ifreqs.ifr;
-
-#ifndef __linux__
- if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
- p += offsetof(struct ifreq, ifr_ifru) +
- ifr->ifr_addr.sa_len;
- else
-#endif
- p += sizeof(*ifr);
-
- if (strcmp(ifname, ifr->ifr_name) != 0)
- continue;
-
- found = 1;
+ if (ioctl(socket_afnet, SIOCGIFMTU, &ifr) == -1)
+ goto eexit;
+ /* Ensure that the MTU is big enough for DHCP */
+ if (ifr.ifr_mtu < MTU_MIN) {
+ ifr.ifr_mtu = MTU_MIN;
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(socket_afnet, SIOCSIFMTU, &ifr) == -1)
+ goto eexit;
+ }
-#ifdef AF_LINK
- if (hwaddr && hwlen && ifr->ifr_addr.sa_family == AF_LINK) {
- sdl = xmalloc(ifr->ifr_addr.sa_len);
- memcpy(sdl, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
- *hwlen = sdl->sdl_alen;
- memcpy(hwaddr, LLADDR(sdl), *hwlen);
- free(sdl);
- retval = 1;
- break;
- }
-#endif
+ snprintf(iface->leasefile, sizeof(iface->leasefile),
+ LEASEFILE, ifname);
+ /* 0 is a valid fd, so init to -1 */
+ iface->raw_fd = -1;
+ iface->udp_fd = -1;
+ iface->arp_fd = -1;
+ goto exit;
- if (ifr->ifr_addr.sa_family == AF_INET) {
- memcpy(&address, &ifr->ifr_addr, sizeof(address));
- if (ioctl(s, SIOCGIFNETMASK, ifr) == -1)
- continue;
- memcpy(&netmask, &ifr->ifr_addr, sizeof(netmask));
- if (get) {
- addr->s_addr = address.sin_addr.s_addr;
- net->s_addr = netmask.sin_addr.s_addr;
- retval = 1;
- break;
- } else {
- if (address.sin_addr.s_addr == addr->s_addr &&
- (!net ||
- netmask.sin_addr.s_addr == net->s_addr))
- {
- retval = 1;
- break;
- }
- }
- }
+eexit:
+ free(iface);
+ iface = NULL;
+exit:
+ return iface;
+}
+void
+free_interface(struct interface *iface)
+{
+ if (!iface)
+ return;
+ if (iface->state) {
+ free_options(iface->state->options);
+ free(iface->state->old);
+ free(iface->state->new);
+ free(iface->state->offer);
+ free(iface->state);
}
-
- if (!found)
- errno = ENXIO;
- close(s);
- free(ifc.ifc_buf);
- return retval;
+ free(iface->clientid);
+ free(iface);
}
int
-up_interface(const char *ifname)
+carrier_status(struct interface *iface)
{
- int s;
+ int ret;
struct ifreq ifr;
- int retval = -1;
+#ifdef SIOCGIFMEDIA
+ struct ifmediareq ifmr;
+#endif
#ifdef __linux__
char *p;
#endif
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- return -1;
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
#ifdef __linux__
- /* We can only bring the real interface up */
+ /* We can only test the real interface up */
if ((p = strchr(ifr.ifr_name, ':')))
*p = '\0';
#endif
- if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) {
- if ((ifr.ifr_flags & IFF_UP))
- retval = 0;
- else {
- ifr.ifr_flags |= IFF_UP;
- if (ioctl(s, SIOCSIFFLAGS, &ifr) == 0)
- retval = 0;
- }
- }
- close(s);
- return retval;
+
+ if (ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == -1)
+ return -1;
+ iface->flags = ifr.ifr_flags;
+
+ ret = -1;
+#ifdef SIOCGIFMEDIA
+ memset(&ifmr, 0, sizeof(ifmr));
+ strlcpy(ifmr.ifm_name, iface->name, sizeof(ifmr.ifm_name));
+ if (ioctl(socket_afnet, SIOCGIFMEDIA, &ifmr) != -1 &&
+ ifmr.ifm_status & IFM_AVALID)
+ ret = (ifmr.ifm_status & IFM_ACTIVE) ? 1 : 0;
+#endif
+ if (ret == -1)
+ ret = (ifr.ifr_flags & IFF_RUNNING) ? 1 : 0;
+ return ret;
}
int
-carrier_status(const char *ifname)
+up_interface(struct interface *iface)
{
- int s;
struct ifreq ifr;
int retval = -1;
-#ifdef SIOCGIFMEDIA
- struct ifmediareq ifmr;
-#endif
#ifdef __linux__
char *p;
#endif
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- return -1;
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
#ifdef __linux__
- /* We can only test the real interface up */
+ /* We can only bring the real interface up */
if ((p = strchr(ifr.ifr_name, ':')))
*p = '\0';
#endif
- if ((retval = ioctl(s, SIOCGIFFLAGS, &ifr)) == 0) {
- if (ifr.ifr_flags & IFF_UP && ifr.ifr_flags & IFF_RUNNING)
- retval = 1;
- else
+ if (ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == 0) {
+ if ((ifr.ifr_flags & IFF_UP))
retval = 0;
- }
-
-#ifdef SIOCGIFMEDIA
- if (retval == 1) {
- memset(&ifmr, 0, sizeof(ifmr));
- strncpy(ifmr.ifm_name, ifr.ifr_name, sizeof(ifmr.ifm_name));
- if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1 &&
- ifmr.ifm_status & IFM_AVALID)
- {
- if (!(ifmr.ifm_status & IFM_ACTIVE))
+ else {
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(socket_afnet, SIOCSIFFLAGS, &ifr) == 0)
retval = 0;
}
+ iface->flags = ifr.ifr_flags;
}
-#endif
- close(s);
return retval;
}
struct interface *
-read_interface(const char *ifname, _unused int metric)
+discover_interfaces(int argc, char * const *argv)
{
- int s;
- struct ifreq ifr;
- struct interface *iface = NULL;
- unsigned char *hwaddr = NULL;
- size_t hwlen = 0;
- sa_family_t family = 0;
-
- memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ struct ifaddrs *ifaddrs, *ifa;
+ char *p;
+ int i;
+ struct interface *ifp, *ifs, *ifl;
+#ifdef __linux__
+ char ifn[IF_NAMESIZE];
+#endif
+#ifdef AF_LINK
+ const struct sockaddr_dl *sdl;
+#ifdef IFLR_ACTIVE
+ struct if_laddrreq iflr;
+ int socket_aflink;
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ socket_aflink = socket(AF_LINK, SOCK_DGRAM, 0);
+ if (socket_aflink == -1)
return NULL;
+ memset(&iflr, 0, sizeof(iflr));
+#endif
+#elif AF_PACKET
+ const struct sockaddr_ll *sll;
+#endif
-#ifdef __linux__
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
- if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1)
- goto eexit;
+ if (getifaddrs(&ifaddrs) == -1)
+ return NULL;
- switch (ifr.ifr_hwaddr.sa_family) {
- case ARPHRD_ETHER:
- case ARPHRD_IEEE802:
- hwlen = ETHER_ADDR_LEN;
- break;
- case ARPHRD_IEEE1394:
- hwlen = EUI64_ADDR_LEN;
- case ARPHRD_INFINIBAND:
- hwlen = INFINIBAND_ADDR_LEN;
- break;
- }
+ ifs = ifl = NULL;
+ for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr != NULL) {
+#ifdef AF_LINK
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+#elif AF_PACKET
+ if (ifa->ifa_addr->sa_family != AF_PACKET)
+ continue;
+#endif
+ }
- hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
- memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, hwlen);
- family = ifr.ifr_hwaddr.sa_family;
+ /* It's possible for an interface to have >1 AF_LINK.
+ * For our purposes, we use the first one. */
+ for (ifp = ifs; ifp; ifp = ifp->next)
+ if (strcmp(ifp->name, ifa->ifa_name) == 0)
+ break;
+ if (ifp)
+ continue;
+ if (argc > 0) {
+ for (i = 0; i < argc; i++) {
+#ifdef __linux__
+ /* Check the real interface name */
+ strlcpy(ifn, argv[i], sizeof(ifn));
+ p = strchr(ifn, ':');
+ if (p)
+ *p = '\0';
+ if (strcmp(ifn, ifa->ifa_name) == 0)
+ break;
#else
- ifr.ifr_metric = metric;
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
- if (ioctl(s, SIOCSIFMETRIC, &ifr) == -1)
- goto eexit;
+ if (strcmp(argv[i], ifa->ifa_name) == 0)
+ break;
+#endif
+ }
+ if (i == argc)
+ continue;
+ p = argv[i];
+ } else {
+ /* -1 means we're discovering against a specific
+ * interface, but we still need the below rules
+ * to apply. */
+ if (argc == -1 && strcmp(argv[0], ifa->ifa_name) != 0)
+ continue;
+ for (i = 0; i < ifdc; i++)
+ if (!fnmatch(ifdv[i], ifa->ifa_name, 0))
+ break;
+ if (i < ifdc)
+ continue;
+ for (i = 0; i < ifac; i++)
+ if (!fnmatch(ifav[i], ifa->ifa_name, 0))
+ break;
+ if (ifac && i == ifac)
+ continue;
+ p = ifa->ifa_name;
+ }
+ if ((ifp = init_interface(p)) == NULL)
+ continue;
- hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
- if (do_interface(ifname, hwaddr, &hwlen, NULL, NULL, 0) != 1)
- goto eexit;
+ /* Bring the interface up if not already */
+ if (!(ifp->flags & IFF_UP)
+#ifdef SIOCGIFMEDIA
+ && carrier_status(ifp) != -1
+#endif
+ )
+ {
+ if (up_interface(ifp) == 0)
+ options |= DHCPCD_WAITUP;
+ else
+ syslog(LOG_ERR, "%s: up_interface: %m", ifp->name);
+ }
- family = ARPHRD_ETHER;
+ /* Don't allow loopback unless explicit */
+ if (ifp->flags & IFF_LOOPBACK) {
+ if (argc == 0 && ifac == 0) {
+ free_interface(ifp);
+ continue;
+ }
+ } else if (ifa->ifa_addr != NULL) {
+#ifdef AF_LINK
+ sdl = (const struct sockaddr_dl *)(void *)ifa->ifa_addr;
+
+#ifdef IFLR_ACTIVE
+ /* We need to check for active address */
+ strlcpy(iflr.iflr_name, ifp->name,
+ sizeof(iflr.iflr_name));
+ memcpy(&iflr.addr, ifa->ifa_addr,
+ MIN(ifa->ifa_addr->sa_len, sizeof(iflr.addr)));
+ iflr.flags = IFLR_PREFIX;
+ iflr.prefixlen = sdl->sdl_alen * NBBY;
+ if (ioctl(socket_aflink, SIOCGLIFADDR, &iflr) == -1 ||
+ !(iflr.flags & IFLR_ACTIVE))
+ {
+ free_interface(ifp);
+ continue;
+ }
#endif
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
- if (ioctl(s, SIOCGIFMTU, &ifr) == -1)
- goto eexit;
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+ ifp->family = ARPHRD_ETHER;
+ break;
+ case IFT_IEEE1394:
+ ifp->family = ARPHRD_IEEE1394;
+ break;
+ }
+ ifp->hwlen = sdl->sdl_alen;
+#ifndef CLLADDR
+# define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
+#endif
+ memcpy(ifp->hwaddr, CLLADDR(sdl), ifp->hwlen);
+#elif AF_PACKET
+ sll = (const struct sockaddr_ll *)(void *)ifa->ifa_addr;
+ ifp->family = sll->sll_hatype;
+ ifp->hwlen = sll->sll_halen;
+ if (ifp->hwlen != 0)
+ memcpy(ifp->hwaddr, sll->sll_addr, ifp->hwlen);
+#endif
+ }
- /* Ensure that the MTU is big enough for DHCP */
- if (ifr.ifr_mtu < MTU_MIN) {
- ifr.ifr_mtu = MTU_MIN;
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
- if (ioctl(s, SIOCSIFMTU, &ifr) == -1)
- goto eexit;
+ /* We only work on ethernet by default */
+ if (!(ifp->flags & IFF_POINTOPOINT) &&
+ ifp->family != ARPHRD_ETHER)
+ {
+ if (argc == 0 && ifac == 0) {
+ free_interface(ifp);
+ continue;
+ }
+ switch (ifp->family) {
+ case ARPHRD_IEEE1394: /* FALLTHROUGH */
+ case ARPHRD_INFINIBAND:
+ /* We don't warn for supported families */
+ break;
+ default:
+ syslog(LOG_WARNING,
+ "%s: unknown hardware family", p);
+ }
+ }
+
+ /* Handle any platform init for the interface */
+ if (if_init(ifp) == -1) {
+ syslog(LOG_ERR, "%s: if_init: %m", p);
+ free_interface(ifp);
+ continue;
+ }
+
+ if (ifl)
+ ifl->next = ifp;
+ else
+ ifs = ifp;
+ ifl = ifp;
}
+ freeifaddrs(ifaddrs);
- if (up_interface(ifname) != 0)
- goto eexit;
+#ifdef IFLR_ACTIVE
+ close(socket_aflink);
+#endif
- iface = xzalloc(sizeof(*iface));
- strlcpy(iface->name, ifname, IF_NAMESIZE);
- snprintf(iface->leasefile, PATH_MAX, LEASEFILE, ifname);
- memcpy(&iface->hwaddr, hwaddr, hwlen);
- iface->hwlen = hwlen;
+ return ifs;
+}
- iface->family = family;
- iface->arpable = !(ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK));
+int
+do_address(const char *ifname,
+ struct in_addr *addr, struct in_addr *net, struct in_addr *dst, int act)
+{
+ struct ifaddrs *ifaddrs, *ifa;
+ const struct sockaddr_in *a, *n, *d;
+ int retval;
- /* 0 is a valid fd, so init to -1 */
- iface->raw_fd = -1;
- iface->udp_fd = -1;
- iface->arp_fd = -1;
- iface->link_fd = -1;
+ if (getifaddrs(&ifaddrs) == -1)
+ return -1;
-eexit:
- close(s);
- free(hwaddr);
- return iface;
+ retval = 0;
+ for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL ||
+ ifa->ifa_addr->sa_family != AF_INET ||
+ strcmp(ifa->ifa_name, ifname) != 0)
+ continue;
+ a = (const struct sockaddr_in *)(void *)ifa->ifa_addr;
+ n = (const struct sockaddr_in *)(void *)ifa->ifa_netmask;
+ if (ifa->ifa_flags & IFF_POINTOPOINT)
+ d = (const struct sockaddr_in *)(void *)
+ ifa->ifa_dstaddr;
+ else
+ d = NULL;
+ if (act == 1) {
+ addr->s_addr = a->sin_addr.s_addr;
+ net->s_addr = n->sin_addr.s_addr;
+ if (dst) {
+ if (ifa->ifa_flags & IFF_POINTOPOINT)
+ dst->s_addr = d->sin_addr.s_addr;
+ else
+ dst->s_addr = INADDR_ANY;
+ }
+ retval = 1;
+ break;
+ }
+ if (addr->s_addr == a->sin_addr.s_addr &&
+ (net == NULL || net->s_addr == n->sin_addr.s_addr))
+ {
+ retval = 1;
+ break;
+ }
+ }
+ freeifaddrs(ifaddrs);
+ return retval;
}
int
@@ -451,16 +550,11 @@ do_mtu(const char *ifname, short int mtu)
{
struct ifreq ifr;
int r;
- int s;
-
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- return -1;
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_mtu = mtu;
- r = ioctl(s, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr);
- close(s);
+ r = ioctl(socket_afnet, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr);
if (r == -1)
return -1;
return ifr.ifr_mtu;
@@ -482,13 +576,11 @@ int
open_udp_socket(struct interface *iface)
{
int s;
- union sockunion {
- struct sockaddr sa;
- struct sockaddr_in sin;
- } su;
+ struct sockaddr_in sin;
int n;
#ifdef SO_BINDTODEVICE
struct ifreq ifr;
+ char *p;
#endif
if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
@@ -500,7 +592,12 @@ open_udp_socket(struct interface *iface)
#ifdef SO_BINDTODEVICE
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
- if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
+ /* We can only bind to the real device */
+ p = strchr(ifr.ifr_name, ':');
+ if (p)
+ *p = '\0';
+ if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr,
+ sizeof(ifr)) == -1)
goto eexit;
#endif
/* As we don't use this socket for receiving, set the
@@ -508,11 +605,11 @@ open_udp_socket(struct interface *iface)
n = 1;
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1)
goto eexit;
- memset(&su, 0, sizeof(su));
- su.sin.sin_family = AF_INET;
- su.sin.sin_port = htons(DHCP_CLIENT_PORT);
- su.sin.sin_addr.s_addr = iface->addr.s_addr;
- if (bind(s, &su.sa, sizeof(su)) == -1)
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(DHCP_CLIENT_PORT);
+ sin.sin_addr.s_addr = iface->addr.s_addr;
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)
goto eexit;
iface->udp_fd = s;
@@ -526,19 +623,16 @@ eexit:
ssize_t
send_packet(const struct interface *iface, struct in_addr to,
- const uint8_t *data, ssize_t len)
+ const uint8_t *data, ssize_t len)
{
- union sockunion {
- struct sockaddr sa;
- struct sockaddr_in sin;
- } su;
-
- memset(&su, 0, sizeof(su));
- su.sin.sin_family = AF_INET;
- su.sin.sin_addr.s_addr = to.s_addr;
- su.sin.sin_port = htons(DHCP_SERVER_PORT);
-
- return sendto(iface->udp_fd, data, len, 0, &su.sa, sizeof(su));
+ struct sockaddr_in sin;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = to.s_addr;
+ sin.sin_port = htons(DHCP_SERVER_PORT);
+ return sendto(iface->udp_fd, data, len, 0,
+ (struct sockaddr *)&sin, sizeof(sin));
}
struct udp_dhcp_packet
@@ -574,7 +668,7 @@ checksum(const void *data, uint16_t len)
ssize_t
make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length,
- struct in_addr source, struct in_addr dest)
+ struct in_addr source, struct in_addr dest)
{
struct udp_dhcp_packet *udpp;
struct ip *ip;
@@ -609,14 +703,10 @@ make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length,
udp->uh_sum = checksum(udpp, sizeof(*udpp));
ip->ip_v = IPVERSION;
- ip->ip_hl = 5;
- ip->ip_id = 0;
- ip->ip_tos = IPTOS_LOWDELAY;
- ip->ip_len = htons (sizeof(*ip) + sizeof(*udp) + length);
- ip->ip_id = 0;
- ip->ip_off = htons(IP_DF); /* Don't fragment */
+ ip->ip_hl = sizeof(*ip) >> 2;
+ ip->ip_id = arc4random() & UINT16_MAX;
ip->ip_ttl = IPDEFTTL;
-
+ ip->ip_len = htons(sizeof(*ip) + sizeof(*udp) + length);
ip->ip_sum = checksum(ip, sizeof(*ip));
*packet = (uint8_t *)udpp;
@@ -630,20 +720,30 @@ get_udp_data(const uint8_t **data, const uint8_t *udp)
memcpy(&packet, udp, sizeof(packet));
*data = udp + offsetof(struct udp_dhcp_packet, dhcp);
- return ntohs(packet.ip.ip_len) - sizeof(packet.ip) - sizeof(packet.udp);
+ return ntohs(packet.ip.ip_len) -
+ sizeof(packet.ip) -
+ sizeof(packet.udp);
}
int
-valid_udp_packet(const uint8_t *data, size_t data_len)
+valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from)
{
struct udp_dhcp_packet packet;
uint16_t bytes, udpsum;
+ if (data_len < sizeof(packet.ip)) {
+ if (from)
+ from->s_addr = INADDR_ANY;
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy(&packet, data, MIN(data_len, sizeof(packet)));
+ if (from)
+ from->s_addr = packet.ip.ip_src.s_addr;
if (data_len > sizeof(packet)) {
errno = EINVAL;
return -1;
}
- memcpy(&packet, data, data_len);
if (checksum(&packet.ip, sizeof(packet.ip)) != 0) {
errno = EINVAL;
return -1;
@@ -671,35 +771,3 @@ valid_udp_packet(const uint8_t *data, size_t data_len)
return 0;
}
-
-int
-send_arp(const struct interface *iface, int op, in_addr_t sip, in_addr_t tip)
-{
- struct arphdr *arp;
- size_t arpsize;
- uint8_t *p;
- int retval;
-
- arpsize = sizeof(*arp) + 2 * iface->hwlen + 2 * sizeof(sip);
- arp = xmalloc(arpsize);
- arp->ar_hrd = htons(iface->family);
- arp->ar_pro = htons(ETHERTYPE_IP);
- arp->ar_hln = iface->hwlen;
- arp->ar_pln = sizeof(sip);
- arp->ar_op = htons(op);
- p = (uint8_t *)arp;
- p += sizeof(*arp);
- memcpy(p, iface->hwaddr, iface->hwlen);
- p += iface->hwlen;
- memcpy(p, &sip, sizeof(sip));
- p += sizeof(sip);
- /* ARP requests should ignore this */
- retval = iface->hwlen;
- while (retval--)
- *p++ = '\0';
- memcpy(p, &tip, sizeof(tip));
- p += sizeof(tip);
- retval = send_raw_packet(iface, ETHERTYPE_ARP, arp, arpsize);
- free(arp);
- return retval;
-}
diff --git a/net.h b/net.h
index 1447aba..6d85930 100644
--- a/net.h
+++ b/net.h
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,8 @@
#include <limits.h>
#include "config.h"
+#include "dhcp.h"
+#include "dhcpcd.h"
#ifndef DUID_LEN
# define DUID_LEN 128 + 2
@@ -57,7 +59,6 @@
# define ARPHRD_INFINIBAND 32
#endif
-#define HWADDR_LEN 20
/* Work out if we have a private address or not
* 10/8
@@ -65,9 +66,9 @@
* 192.168/16
*/
#ifndef IN_PRIVATE
-# define IN_PRIVATE(addr) (((addr & IN_CLASSA_NET) == 0x0a000000) || \
- ((addr & 0xfff00000) == 0xac100000) || \
- ((addr & IN_CLASSB_NET) == 0xc0a80000))
+# define IN_PRIVATE(addr) (((addr & IN_CLASSA_NET) == 0x0a000000) || \
+ ((addr & 0xfff00000) == 0xac100000) || \
+ ((addr & IN_CLASSB_NET) == 0xc0a80000))
#endif
#define LINKLOCAL_ADDR 0xa9fe0000
@@ -78,51 +79,24 @@
# define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)
#endif
-/* There is an argument that this should be converted to an STAIL using
- * queue(3). However, that isn't readily available on all libc's that
- * dhcpcd works on. The only benefit of STAILQ over this is the ability to
- * quickly loop backwards through the list - currently we reverse the list
- * and then move through it forwards. This isn't that much of a big deal
- * though as the norm is to just have one default route, and an IPV4LL route.
- * You can (and do) get more routes in the DHCP message, but not enough to
- * really warrant a change to STAIL queue for performance reasons. */
struct rt {
struct in_addr dest;
struct in_addr net;
struct in_addr gate;
+ const struct interface *iface;
struct rt *next;
};
-struct interface
-{
- char name[IF_NAMESIZE];
- sa_family_t family;
- unsigned char hwaddr[HWADDR_LEN];
- size_t hwlen;
- int arpable;
-
- int raw_fd;
- int udp_fd;
- int arp_fd;
- int link_fd;
- size_t buffer_size, buffer_len, buffer_pos;
- unsigned char *buffer;
-
- struct in_addr addr;
- struct in_addr net;
- struct rt *routes;
-
- char leasefile[PATH_MAX];
- time_t start_uptime;
-
- unsigned char *clientid;
-};
+extern int socket_afnet;
uint32_t get_netmask(uint32_t);
char *hwaddr_ntoa(const unsigned char *, size_t);
size_t hwaddr_aton(unsigned char *, const char *);
-struct interface *read_interface(const char *, int);
+int getifssid(const char *, char *);
+struct interface *init_interface(const char *);
+struct interface *discover_interfaces(int, char * const *);
+void free_interface(struct interface *);
int do_mtu(const char *, short int);
#define get_mtu(iface) do_mtu(iface, 0)
#define set_mtu(iface, mtu) do_mtu(iface, mtu)
@@ -130,48 +104,52 @@ int do_mtu(const char *, short int);
int inet_ntocidr(struct in_addr);
int inet_cidrtoaddr(int, struct in_addr *);
-int up_interface(const char *);
-int do_interface(const char *, unsigned char *, size_t *,
- struct in_addr *, struct in_addr *, int);
-int if_address(const char *, const struct in_addr *, const struct in_addr *,
- const struct in_addr *, int);
-#define add_address(ifname, addr, net, brd) \
- if_address(ifname, addr, net, brd, 1)
-#define del_address(ifname, addr, net) \
- if_address(ifname, addr, net, NULL, -1)
-#define has_address(ifname, addr, net) \
- do_interface(ifname, NULL, NULL, addr, net, 0)
-#define get_address(ifname, addr, net) \
- do_interface(ifname, NULL, NULL, addr, net, 1)
-
-int if_route(const struct interface *,
- const struct in_addr *,const struct in_addr *,
- const struct in_addr *, int, int);
-#define add_route(iface, dest, mask, gate, metric) \
+int up_interface(struct interface *);
+int if_conf(struct interface *);
+int if_init(struct interface *);
+
+int do_address(const char *,
+ struct in_addr *, struct in_addr *, struct in_addr *, int);
+int if_address(const struct interface *,
+ const struct in_addr *, const struct in_addr *,
+ const struct in_addr *, int);
+#define add_address(iface, addr, net, brd) \
+ if_address(iface, addr, net, brd, 1)
+#define del_address(iface, addr, net) \
+ if_address(iface, addr, net, NULL, -1)
+#define has_address(iface, addr, net) \
+ do_address(iface, addr, net, NULL, 0)
+#define get_address(iface, addr, net, dst) \
+ do_address(iface, addr, net, dst, 1)
+
+int if_route(const struct interface *, const struct in_addr *,
+ const struct in_addr *, const struct in_addr *, int, int);
+#define add_route(iface, dest, mask, gate, metric) \
if_route(iface, dest, mask, gate, metric, 1)
-#define change_route(iface, dest, mask, gate, metric) \
+#define change_route(iface, dest, mask, gate, metric) \
if_route(iface, dest, mask, gate, metric, 0)
-#define del_route(iface, dest, mask, gate, metric) \
+#define del_route(iface, dest, mask, gate, metric) \
if_route(iface, dest, mask, gate, metric, -1)
+#define del_src_route(iface, dest, mask, gate, metric) \
+ if_route(iface, dest, mask, gate, metric, -2)
void free_routes(struct rt *);
int open_udp_socket(struct interface *);
-const size_t udp_dhcp_len;
+extern const size_t udp_dhcp_len;
ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t,
- struct in_addr, struct in_addr);
+ struct in_addr, struct in_addr);
ssize_t get_udp_data(const uint8_t **, const uint8_t *);
-int valid_udp_packet(const uint8_t *, size_t);
+int valid_udp_packet(const uint8_t *, size_t, struct in_addr *);
int open_socket(struct interface *, int);
ssize_t send_packet(const struct interface *, struct in_addr,
- const uint8_t *, ssize_t);
+ const uint8_t *, ssize_t);
ssize_t send_raw_packet(const struct interface *, int,
- const void *, ssize_t);
+ const void *, ssize_t);
ssize_t get_raw_packet(struct interface *, int, void *, ssize_t);
-int send_arp(const struct interface *, int, in_addr_t, in_addr_t);
-
-int open_link_socket(struct interface *);
-int link_changed(struct interface *);
-int carrier_status(const char *);
+int init_sockets(void);
+int open_link_socket(void);
+int manage_link(int);
+int carrier_status(struct interface *);
#endif
diff --git a/platform-bsd.c b/platform-bsd.c
new file mode 100644
index 0000000..dd5791c
--- /dev/null
+++ b/platform-bsd.c
@@ -0,0 +1,50 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/utsname.h>
+
+#include "platform.h"
+
+#ifndef SYS_NMLN /* OSX */
+# define SYS_NMLN 256
+#endif
+
+static char march[SYS_NMLN];
+
+char *
+hardware_platform(void)
+{
+ int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
+ size_t len = sizeof(march);
+
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
+ march, &len, NULL, 0) != 0)
+ return NULL;
+ return march;
+}
diff --git a/platform-linux.c b/platform-linux.c
new file mode 100644
index 0000000..79562c8
--- /dev/null
+++ b/platform-linux.c
@@ -0,0 +1,104 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "platform.h"
+
+static const char *mproc =
+#if defined(__alpha__)
+ "system type"
+#elif defined(__arm__)
+ "Hardware"
+#elif defined(__avr32__)
+ "cpu family"
+#elif defined(__bfin__)
+ "BOARD Name"
+#elif defined(__cris__)
+ "cpu model"
+#elif defined(__frv__)
+ "System"
+#elif defined(__i386__) || defined(__x86_64__)
+ "vendor_id"
+#elif defined(__ia64__)
+ "vendor"
+#elif defined(__hppa__)
+ "model"
+#elif defined(__m68k__)
+ "MMU"
+#elif defined(__mips__)
+ "system type"
+#elif defined(__powerpc__) || defined(__powerpc64__)
+ "machine"
+#elif defined(__s390__) || defined(__s390x__)
+ "Manufacturer"
+#elif defined(__sh__)
+ "machine"
+#elif defined(sparc) || defined(__sparc__)
+ "cpu"
+#elif defined(__vax__)
+ "cpu"
+#else
+ NULL
+#endif
+ ;
+
+char *
+hardware_platform(void)
+{
+ FILE *fp;
+ char *buf, *p;
+
+ if (mproc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ fp = fopen("/proc/cpuinfo", "r");
+ if (fp == NULL)
+ return NULL;
+
+ p = NULL;
+ while ((buf = get_line(fp))) {
+ if (strncmp(buf, mproc, strlen(mproc)) == 0) {
+ p = strchr(buf, ':');
+ if (p != NULL && ++p != NULL) {
+ while (*p == ' ')
+ p++;
+ break;
+ }
+ }
+ }
+ fclose(fp);
+
+ if (p == NULL)
+ errno = ESRCH;
+ return p;
+}
diff --git a/platform.h b/platform.h
new file mode 100644
index 0000000..24731ac
--- /dev/null
+++ b/platform.h
@@ -0,0 +1,33 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+char * hardware_platform(void);
+
+#endif
diff --git a/showlease.c b/showlease.c
index 27cc543..50f96df 100644
--- a/showlease.c
+++ b/showlease.c
@@ -6,6 +6,10 @@
#include "dhcp.h"
#include "config.h"
+#ifndef DEFAULT_LEASETIME
+#define DEFAULT_LEASETIME 3600 /* 1 hour */
+#endif
+
#define REQUEST (1 << 0)
#define UINT8 (1 << 1)
#define UINT16 (1 << 2)
@@ -119,7 +123,7 @@ static const struct dhcp_opt const dhcp_opts[] = {
};
struct dhcp_message *
-get_lease(const char *leasefile)
+get_lease_from_file(const char *leasefile)
{
int fd;
struct dhcp_message *dhcp;
@@ -322,7 +326,7 @@ main(int argc, char *argv[])
exit(1);
}
snprintf(leasefile, PATH_MAX, LEASEFILE, argv[1]);
- if ((dhcp = get_lease(leasefile)) == NULL) {
+ if ((dhcp = get_lease_from_file(leasefile)) == NULL) {
fprintf(stderr, "Couldn't read lease file: %s\n", strerror(errno));
exit(1);
}
diff --git a/signals.c b/signals.c
index 6576afd..fd3b0c3 100644
--- a/signals.c
+++ b/signals.c
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -40,10 +40,12 @@
static int signal_pipe[2];
static const int handle_sigs[] = {
- SIGHUP,
SIGALRM,
+ SIGHUP,
+ SIGINT,
+ SIGPIPE,
SIGTERM,
- SIGINT
+ SIGUSR1,
};
static void
@@ -52,17 +54,11 @@ signal_handler(int sig)
int serrno = errno;
if (write(signal_pipe[1], &sig, sizeof(sig)) != sizeof(sig))
- syslog(LOG_ERR, "write signal %d: %s", sig, strerror(errno));
+ syslog(LOG_ERR, "failed to write signal %d: %m", sig);
/* Restore errno */
errno = serrno;
}
-int
-signal_fd(void)
-{
- return (signal_pipe[0]);
-}
-
/* Read a signal from the signal pipe. Returns 0 if there is
* no signal, -1 on error (and sets errno appropriately), and
* your signal on success */
@@ -95,7 +91,7 @@ signal_init(void)
return -1;
if (set_cloexec(signal_pipe[1]) == -1)
return -1;
- return 0;
+ return signal_pipe[0];
}
static int
@@ -125,3 +121,4 @@ signal_reset(void)
{
return signal_handle(SIG_DFL);
}
+
diff --git a/signals.h b/signals.h
index f784a68..7098cfb 100644
--- a/signals.h
+++ b/signals.h
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,6 @@
int signal_init(void);
int signal_setup(void);
int signal_reset(void);
-int signal_fd(void);
int signal_read(void);
#endif