summaryrefslogtreecommitdiffstats
path: root/dhcpcd
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2014-01-30 13:29:46 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2014-01-30 16:23:03 +0100
commitf2ed769880271654297a4be420f26ab94d39666b (patch)
tree4cbfc23184993e8ef11bb1d60b307cbb3644a259 /dhcpcd
parent9979989ee04b63cdb5e99a6c628af2a0a72c1586 (diff)
downloadrtems-libbsd-f2ed769880271654297a4be420f26ab94d39666b.tar.bz2
DHCPCD(8): Import
Import DHCPCD(8) from: http://roy.marples.name/projects/dhcpcd/ The upstream sources can be obtained via: fossil clone http://roy.marples.name/projects/dhcpcd The imported version is 2014-01-29 19:46:44 [6b209507bb].
Diffstat (limited to 'dhcpcd')
-rw-r--r--dhcpcd/GNUmakefile4
-rw-r--r--dhcpcd/Makefile229
-rw-r--r--dhcpcd/Makefile.inc11
-rw-r--r--dhcpcd/README105
-rw-r--r--dhcpcd/arp.c338
-rw-r--r--dhcpcd/arp.h48
-rw-r--r--dhcpcd/auth.c557
-rw-r--r--dhcpcd/auth.h79
-rw-r--r--dhcpcd/bpf-filter.h101
-rw-r--r--dhcpcd/bpf.c214
-rw-r--r--dhcpcd/common.c279
-rw-r--r--dhcpcd/common.h113
-rw-r--r--dhcpcd/compat/arc4random.c158
-rw-r--r--dhcpcd/compat/arc4random.h36
-rw-r--r--dhcpcd/compat/closefrom.c47
-rw-r--r--dhcpcd/compat/closefrom.h31
-rw-r--r--dhcpcd/compat/getline.c75
-rw-r--r--dhcpcd/compat/getline.h36
-rw-r--r--dhcpcd/compat/linkaddr.c120
-rw-r--r--dhcpcd/compat/pollts.c63
-rw-r--r--dhcpcd/compat/pollts.h39
-rw-r--r--dhcpcd/compat/posix_spawn.c147
-rw-r--r--dhcpcd/compat/posix_spawn.h48
-rw-r--r--dhcpcd/compat/pselect.c65
-rw-r--r--dhcpcd/compat/strlcpy.c51
-rw-r--r--dhcpcd/compat/strlcpy.h34
-rwxr-xr-xdhcpcd/configure803
-rw-r--r--dhcpcd/control.c231
-rw-r--r--dhcpcd/control.h45
-rw-r--r--dhcpcd/crypt/crypt.h33
-rw-r--r--dhcpcd/crypt/hmac_md5.c86
-rw-r--r--dhcpcd/crypt/md5.c242
-rw-r--r--dhcpcd/crypt/md5.h33
-rw-r--r--dhcpcd/defs.h61
-rw-r--r--dhcpcd/dev.c177
-rw-r--r--dhcpcd/dev.h59
-rw-r--r--dhcpcd/dev/Makefile42
-rw-r--r--dhcpcd/dev/udev.c179
-rw-r--r--dhcpcd/dhcp-common.c684
-rw-r--r--dhcpcd/dhcp-common.h105
-rw-r--r--dhcpcd/dhcp.c2797
-rw-r--r--dhcpcd/dhcp.h302
-rw-r--r--dhcpcd/dhcp6.c2811
-rw-r--r--dhcpcd/dhcp6.h245
-rw-r--r--dhcpcd/dhcpcd-definitions.conf282
-rw-r--r--dhcpcd/dhcpcd-embedded.h.in31
-rw-r--r--dhcpcd/dhcpcd-hooks/01-test6
-rw-r--r--dhcpcd/dhcpcd-hooks/02-dump5
-rw-r--r--dhcpcd/dhcpcd-hooks/10-mtu38
-rw-r--r--dhcpcd/dhcpcd-hooks/10-wpa_supplicant113
-rw-r--r--dhcpcd/dhcpcd-hooks/15-timezone51
-rw-r--r--dhcpcd/dhcpcd-hooks/20-resolv.conf165
-rw-r--r--dhcpcd/dhcpcd-hooks/29-lookup-hostname34
-rw-r--r--dhcpcd/dhcpcd-hooks/30-hostname154
-rw-r--r--dhcpcd/dhcpcd-hooks/50-dhcpcd-compat41
-rw-r--r--dhcpcd/dhcpcd-hooks/50-ntp.conf109
-rw-r--r--dhcpcd/dhcpcd-hooks/50-yp.conf56
-rw-r--r--dhcpcd/dhcpcd-hooks/50-ypbind78
-rw-r--r--dhcpcd/dhcpcd-hooks/Makefile35
-rw-r--r--dhcpcd/dhcpcd-run-hooks.8.in150
-rw-r--r--dhcpcd/dhcpcd-run-hooks.in342
-rw-r--r--dhcpcd/dhcpcd.8.in672
-rw-r--r--dhcpcd/dhcpcd.c1477
-rw-r--r--dhcpcd/dhcpcd.conf35
-rw-r--r--dhcpcd/dhcpcd.conf.5.in704
-rw-r--r--dhcpcd/dhcpcd.h94
-rw-r--r--dhcpcd/duid.c162
-rw-r--r--dhcpcd/duid.h38
-rw-r--r--dhcpcd/eloop.c391
-rw-r--r--dhcpcd/eloop.h57
-rwxr-xr-xdhcpcd/genembedc54
-rwxr-xr-xdhcpcd/genembedh15
-rw-r--r--dhcpcd/if-bsd.c722
-rw-r--r--dhcpcd/if-linux-wireless.c91
-rw-r--r--dhcpcd/if-linux.c882
-rw-r--r--dhcpcd/if-options.c2050
-rw-r--r--dhcpcd/if-options.h195
-rw-r--r--dhcpcd/if-pref.c105
-rw-r--r--dhcpcd/if-pref.h34
-rw-r--r--dhcpcd/ipv4.c787
-rw-r--r--dhcpcd/ipv4.h107
-rw-r--r--dhcpcd/ipv4ll.c170
-rw-r--r--dhcpcd/ipv4ll.h34
-rw-r--r--dhcpcd/ipv6.c1047
-rw-r--r--dhcpcd/ipv6.h176
-rw-r--r--dhcpcd/ipv6nd.c1835
-rw-r--r--dhcpcd/ipv6nd.h115
-rw-r--r--dhcpcd/lpf.c206
-rw-r--r--dhcpcd/net.c521
-rw-r--r--dhcpcd/net.h110
-rw-r--r--dhcpcd/platform-bsd.c169
-rw-r--r--dhcpcd/platform-linux.c255
-rw-r--r--dhcpcd/platform.h39
-rw-r--r--dhcpcd/script.c607
-rw-r--r--dhcpcd/script.h37
-rw-r--r--dhcpcd/test/Makefile33
-rw-r--r--dhcpcd/test/test.c38
-rw-r--r--dhcpcd/test/test.h32
-rw-r--r--dhcpcd/test/test_hmac_md5.c173
99 files changed, 27922 insertions, 0 deletions
diff --git a/dhcpcd/GNUmakefile b/dhcpcd/GNUmakefile
new file mode 100644
index 00000000..28d60969
--- /dev/null
+++ b/dhcpcd/GNUmakefile
@@ -0,0 +1,4 @@
+# GNU Make does not automagically include .depend
+# Luckily it does read GNUmakefile over Makefile so we can work around it
+include Makefile
+-include $(shell test -e .depend && echo .depend)
diff --git a/dhcpcd/Makefile b/dhcpcd/Makefile
new file mode 100644
index 00000000..b6fe6dea
--- /dev/null
+++ b/dhcpcd/Makefile
@@ -0,0 +1,229 @@
+# dhcpcd Makefile
+
+PROG= dhcpcd
+SRCS= common.c control.c dhcpcd.c duid.c eloop.c
+SRCS+= if-options.c if-pref.c net.c script.c
+SRCS+= dhcp-common.c
+
+CFLAGS?= -O2
+CSTD?= c99
+MKDIRS=
+include config.mk
+CFLAGS+= -std=${CSTD}
+
+SRCS+= ${DHCPCD_SRCS}
+
+.PATH: ./crypt
+
+VPATH= . ./crypt
+
+CPPFLAGS+= -I./crypt
+SRCS+= auth.c hmac_md5.c ${MD5_SRC}
+
+OBJS+= ${SRCS:.c=.o} ${COMPAT_SRCS:.c=.o}
+
+SCRIPT= ${LIBEXECDIR}/dhcpcd-run-hooks
+HOOKDIR= ${LIBEXECDIR}/dhcpcd-hooks
+
+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}
+
+SUBDIRS= dhcpcd-hooks ${MKDIRS}
+
+SED_RUNDIR= -e 's:@RUNDIR@:${RUNDIR}:g'
+SED_DBDIR= -e 's:@DBDIR@:${DBDIR}:g'
+SED_LIBDIR= -e 's:@LIBDIR@:${LIBDIR}: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})
+
+FOSSILID?= current
+
+DISTPREFIX?= ${PROG}-${VERSION}
+DISTFILEGZ?= ${DISTPREFIX}.tar.gz
+DISTFILE?= ${DISTPREFIX}.tar.bz2
+
+HOST_SH?= /bin/sh
+
+CLEANFILES+= *.tar.bz2
+
+.PHONY: import import-bsd dev test
+
+.SUFFIXES: .in
+
+.in:
+ ${SED} ${SED_RUNDIR} ${SED_DBDIR} ${SED_LIBDIR} ${SED_HOOKDIR} \
+ ${SED_SYS} ${SED_SCRIPT} \
+ ${SED_SERVICEEXISTS} ${SED_SERVICECMD} ${SED_SERVICESTATUS} \
+ $< > $@
+
+all: config.h ${PROG} ${SCRIPTS} ${MAN5} ${MAN8}
+ for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@; cd ..; done
+
+dev:
+ cd dev && ${MAKE}
+
+.c.o:
+ ${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
+
+CLEANFILES+= dhcpcd-embedded.h dhcpcd-embedded.c
+
+dhcpcd-embedded.h: genembedh dhcpcd-definitions.conf dhcpcd-embedded.h.in
+ ${HOST_SH} ${.ALLSRC} $^ > $@
+
+dhcpcd-embedded.c: genembedc dhcpcd-definitions.conf
+ ${HOST_SH} ${.ALLSRC} $^ > $@
+
+if-options.c: dhcpcd-embedded.h
+
+.depend: ${SRCS} ${COMPAT_SRCS}
+ ${CC} ${CPPFLAGS} -MM ${SRCS} ${COMPAT_SRCS} > .depend
+
+depend: .depend
+
+${PROG}: ${DEPEND} ${OBJS}
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}
+
+test:
+ cd $@; ${MAKE} $@; ./$@
+
+_embeddedinstall: dhcpcd-definitions.conf
+ ${INSTALL} -d ${DESTDIR}${SCRIPTSDIR}
+ ${INSTALL} -m ${CONFMODE} dhcpcd-definitions.conf ${DESTDIR}${SCRIPTSDIR}
+
+_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}
+
+proginstall: _proginstall _scriptsinstall ${EMBEDDEDINSTALL}
+ for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@; cd ..; done
+
+_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 _maninstall _confinstall
+
+clean:
+ rm -f ${OBJS} ${PROG} ${PROG}.core ${CLEANFILES}
+ for x in ${SUBDIRS} test; do cd $$x; ${MAKE} $@; cd ..; done
+
+distclean: clean
+ rm -f .depend config.h config.mk
+
+dist:
+ fossil tarball --name ${DISTPREFIX} ${FOSSILID} ${DISTFILEGZ}
+ gunzip -c ${DISTFILEGZ} | bzip2 >${DISTFILE}
+ rm ${DISTFILEGZ}
+
+import: ${SRCS}
+ rm -rf /tmp/${DISTPREFIX}
+ ${INSTALL} -d /tmp/${DISTPREFIX}
+ cp ${SRCS} dhcpcd.conf dhcpcd-definitions.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;
+ if test -n "${IMPORT_RCSID}"; then \
+ for x in \
+ /tmp/${DISTPREFIX}/*.c \
+ /tmp/${DISTPREFIX}/compat/*.c \
+ ; do \
+ if test -e "$$x"; then \
+ printf "${IMPORT_RCSID}\n\n" >"$$x".new; \
+ cat "$$x" >>"$$x".new; \
+ mv "$$x".new "$$x"; \
+ fi; \
+ done; \
+ fi;
+ if test -n "${IMPORT_HID}"; then \
+ for x in \
+ /tmp/${DISTPREFIX}/*.h \
+ /tmp/${DISTPREFIX}/compat/*.h \
+ ; do \
+ if test -e "$$x"; then \
+ printf "${IMPORT_HID}\n\n" >"$$x".new; \
+ cat "$$x" >>"$$x".new; \
+ mv "$$x".new "$$x"; \
+ fi; \
+ done; \
+ fi;
+ if test -n "${IMPORT_MANID}"; then \
+ for x in \
+ /tmp/${DISTPREFIX}/dhcpcd.8.in \
+ /tmp/${DISTPREFIX}/dhcpcd-run-hooks.8.in \
+ /tmp/${DISTPREFIX}/dhcpcd.conf.5.in \
+ ; do \
+ if test -e "$$x"; then \
+ printf "${IMPORT_MANID}\n" >"$$x".new; \
+ cat "$$x" >>"$$x".new; \
+ mv "$$x".new "$$x"; \
+ fi; \
+ done; \
+ fi;
+ if test -n "${IMPORT_SHID}"; then \
+ for x in \
+ /tmp/${DISTPREFIX}/dhcpcd-run-hooks.in \
+ /tmp/${DISTPREFIX}/dhcpcd.conf \
+ /tmp/${DISTPREFIX}/dhcpcd-definitions.conf \
+ ; do \
+ if test -e "$$x"; then \
+ if test "$$(sed -ne 1p $$x)" = "#!/bin/sh" \
+ ; then \
+ echo "#!/bin/sh" > "$$x".new; \
+ printf "${IMPORT_SHID}\n" >>"$$x".new; \
+ echo "" >>"$$x".new; \
+ sed 1d "$$x" >>"$$x".new; \
+ else \
+ printf "${IMPORT_SHID}\n" >>"$$x".new; \
+ echo "" >>"$$x".new; \
+ cat "$$x" >>"$$x".new; \
+ fi; \
+ mv "$$x".new "$$x"; \
+ fi; \
+ done; \
+ fi;
+ cd dhcpcd-hooks; ${MAKE} DISTPREFIX=${DISTPREFIX} $@
+
+include Makefile.inc
diff --git a/dhcpcd/Makefile.inc b/dhcpcd/Makefile.inc
new file mode 100644
index 00000000..98c55d12
--- /dev/null
+++ b/dhcpcd/Makefile.inc
@@ -0,0 +1,11 @@
+# System definitions
+
+PICFLAG?= -fPIC
+
+BINMODE?= 0555
+NONBINMODE?= 0444
+MANMODE?= ${NONBINMODE}
+CONFMODE?= 0644
+
+INSTALL?= install
+SED?= sed
diff --git a/dhcpcd/README b/dhcpcd/README
new file mode 100644
index 00000000..7f416382
--- /dev/null
+++ b/dhcpcd/README
@@ -0,0 +1,105 @@
+dhcpcd - DHCP client daemon
+Copyright (c) 2006-2014 Roy Marples <roy@marples.name>
+
+
+Installation
+------------
+./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 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 ./configure --disable-fork.
+This also puts the --no-background flag on and stops the --background flag
+from working.
+
+You can change the default dirs with these knobs.
+For example, to satisfy FHS compliance you would do this:-
+./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.
+Most distros patch linux headers so this should work fine.
+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
+
+Some BSD systems protect against IPv6 NS/NA messages by ensuring that the
+source address matches an address/prefix on the interface.
+This is an error as the correct check is for on-link prefixes.
+As such, on these systems stateful addressing via DHCPv6 may cause issues
+trying to reach other neighbours.
+BSD systems where this is known to be a problem
+ Occured in NetBSD-5.0, fixed in NetBSD-6.99.29
+ Occured in OpenBSD-4.2, fixed in OpenBSD-5.0
+
+We try and detect how dhcpcd should interact with system services at runtime.
+If we cannot auto-detect how do to this, or it is wrong then
+you can change this by passing shell commands to --serviceexists,
+--servicecmd and optionally --servicestatus to ./configure or overriding
+the service variables in a hook.
+
+Some systems have /dev management systems and some of these like to rename
+interfaces. As this system would listen in the same way as dhcpcd to new
+interface arrivals, dhcpcd needs to listen to the /dev management sytem
+instead of the kernel. However, if the /dev management system breaks, stops
+working, or changes to a new one, dhcpcd should still try and continue to work.
+To facilitate this, dhcpcd allows a plugin to load to instruct dhcpcd when it
+can use an interface. As of the time of writing only udev support is included.
+You can disable this with --without-dev, or without-udev
+
+To shrink dhcpcd you can disable IPv4 or IPv6:
+ --disable-inet
+ --disable-inet6
+
+You can also move the embedded extended configuration from the dhcpcd binary
+to an external file (LIBEXECDIR/dhcpcd-definitions.conf)
+ --disable-embedded
+If dhcpcd cannot load this file at runtime, dhcpcd will work but will not be
+able to decode any DHCP/DHCPv6 options that are not defined by the user
+in /etc/dhcpcd.conf.
+
+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, 02-dump, 10-mtu, 10-wpa_supplicant,
+15-timezone, 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
+-------------
+dhcpcd-5.0 is only fully command line compatible with dhcpcd-4.0
+For compatibility with older versions, use dhcpcd-4.0
+
+
+ChangeLog
+---------
+We no longer supply a ChangeLog.
+However, you're more than welcome to read the commit log at
+http://roy.marples.name/projects/dhcpcd/timeline/
diff --git a/dhcpcd/arp.c b/dhcpcd/arp.c
new file mode 100644
index 00000000..13779a48
--- /dev/null
+++ b/dhcpcd/arp.c
@@ -0,0 +1,338 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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 "config.h"
+#include "arp.h"
+#include "ipv4.h"
+#include "common.h"
+#include "dhcp.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
+arp_send(const struct interface *ifp, 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;
+
+ ar.ar_hrd = htons(ifp->family);
+ ar.ar_pro = htons(ETHERTYPE_IP);
+ ar.ar_hln = ifp->hwlen;
+ ar.ar_pln = sizeof(sip);
+ ar.ar_op = htons(op);
+
+ p = arp_buffer;
+ len = sizeof(arp_buffer);
+
+#define CHECK(fun, b, l) \
+ do { \
+ if (len < (l)) \
+ goto eexit; \
+ fun(p, (b), (l)); \
+ p += (l); \
+ len -= (l); \
+ } while (/* CONSTCOND */ 0)
+#define APPEND(b, l) CHECK(memcpy, b, l)
+#define ZERO(l) CHECK(memset, 0, l)
+
+ APPEND(&ar, sizeof(ar));
+ APPEND(ifp->hwaddr, ifp->hwlen);
+ APPEND(&sip, sizeof(sip));
+ ZERO(ifp->hwlen);
+ APPEND(&tip, sizeof(tip));
+ len = p - arp_buffer;
+ return ipv4_sendrawpacket(ifp, ETHERTYPE_ARP, arp_buffer, len);
+
+eexit:
+ errno = ENOBUFS;
+ return -1;
+}
+
+static void
+arp_failure(struct interface *ifp)
+{
+ const struct dhcp_state *state = D_CSTATE(ifp);
+
+ /* If we failed without a magic cookie then we need to try
+ * and defend our IPv4LL address. */
+ if ((state->offer != NULL &&
+ state->offer->cookie != htonl(MAGIC_COOKIE)) ||
+ (state->new != NULL &&
+ state->new->cookie != htonl(MAGIC_COOKIE)))
+ {
+ ipv4ll_handle_failure(ifp);
+ return;
+ }
+
+ unlink(state->leasefile);
+ if (!state->lease.frominfo)
+ dhcp_decline(ifp);
+ dhcp_close(ifp);
+ eloop_timeout_delete(NULL, ifp);
+ if (state->lease.frominfo)
+ start_interface(ifp);
+ else
+ eloop_timeout_add_sec(DHCP_ARP_FAIL, start_interface, ifp);
+}
+
+static void
+arp_packet(void *arg)
+{
+ struct interface *ifp = 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 dhcp_state *state;
+ struct if_options *opts = ifp->options;
+ const char *hwaddr;
+ struct in_addr ina;
+
+ state = D_STATE(ifp);
+ state->fail.s_addr = 0;
+ for(;;) {
+ bytes = ipv4_getrawpacket(ifp, ETHERTYPE_ARP,
+ arp_buffer, sizeof(arp_buffer), NULL);
+ 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 == ifp->hwlen &&
+ memcmp(hw_s, ifp->hwaddr, ifp->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",
+ ifp->name, inet_ntoa(ina), hwaddr);
+ if (select_profile(ifp, hwaddr) == -1 &&
+ errno == ENOENT)
+ select_profile(ifp, inet_ntoa(ina));
+ dhcp_close(ifp);
+ eloop_timeout_delete(NULL, ifp);
+ start_interface(ifp);
+ return;
+ }
+
+ /* RFC 2131 3.1.5, Client-server interaction
+ * RFC 3927 2.2.1, Probe Conflict Detection */
+ if (state->offer &&
+ (reply_s == state->offer->yiaddr ||
+ (reply_s == 0 && reply_t == state->offer->yiaddr)))
+ state->fail.s_addr = state->offer->yiaddr;
+
+ /* RFC 3927 2.5, Conflict Defense */
+ if (IN_LINKLOCAL(htonl(state->addr.s_addr)) &&
+ reply_s == state->addr.s_addr)
+ state->fail.s_addr = state->addr.s_addr;
+
+ if (state->fail.s_addr) {
+ syslog(LOG_ERR, "%s: hardware address %s claims %s",
+ ifp->name,
+ hwaddr_ntoa((unsigned char *)hw_s,
+ (size_t)ar.ar_hln),
+ inet_ntoa(state->fail));
+ errno = EEXIST;
+ arp_failure(ifp);
+ return;
+ }
+ }
+}
+
+void
+arp_announce(void *arg)
+{
+ struct interface *ifp = arg;
+ struct dhcp_state *state = D_STATE(ifp);
+ struct timeval tv;
+
+ if (state->new == NULL)
+ return;
+ if (state->arp_fd == -1) {
+ state->arp_fd = ipv4_opensocket(ifp, ETHERTYPE_ARP);
+ if (state->arp_fd == -1) {
+ syslog(LOG_ERR, "%s: %s: %m", __func__, ifp->name);
+ return;
+ }
+ eloop_event_add(state->arp_fd, arp_packet, ifp);
+ }
+ if (++state->claims < ANNOUNCE_NUM)
+ syslog(LOG_DEBUG,
+ "%s: sending ARP announce (%d of %d), "
+ "next in %d.0 seconds",
+ ifp->name, state->claims, ANNOUNCE_NUM, ANNOUNCE_WAIT);
+ else
+ syslog(LOG_DEBUG,
+ "%s: sending ARP announce (%d of %d)",
+ ifp->name, state->claims, ANNOUNCE_NUM);
+ if (arp_send(ifp, ARPOP_REQUEST,
+ state->new->yiaddr, state->new->yiaddr) == -1)
+ syslog(LOG_ERR, "send_arp: %m");
+ if (state->claims < ANNOUNCE_NUM) {
+ eloop_timeout_add_sec(ANNOUNCE_WAIT, arp_announce, ifp);
+ return;
+ }
+ if (state->new->cookie != htonl(MAGIC_COOKIE)) {
+ /* Check if doing DHCP */
+ if (!(ifp->options->options & DHCPCD_DHCP))
+ return;
+ /* 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);
+ eloop_timeout_add_tv(&tv, dhcp_discover, ifp);
+ } else {
+ eloop_event_delete(state->arp_fd);
+ close(state->arp_fd);
+ state->arp_fd = -1;
+ }
+}
+
+void
+arp_probe(void *arg)
+{
+ struct interface *ifp = arg;
+ struct dhcp_state *state = D_STATE(ifp);
+ struct in_addr addr;
+ struct timeval tv;
+ int arping = 0;
+
+ if (state->arp_fd == -1) {
+ state->arp_fd = ipv4_opensocket(ifp, ETHERTYPE_ARP);
+ if (state->arp_fd == -1) {
+ syslog(LOG_ERR, "%s: %s: %m", __func__, ifp->name);
+ return;
+ }
+ eloop_event_add(state->arp_fd, arp_packet, ifp);
+ }
+
+ if (state->arping_index < ifp->options->arping_len) {
+ addr.s_addr = ifp->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 = state->addr.s_addr;
+
+ if (state->probes == 0) {
+ if (arping)
+ syslog(LOG_DEBUG, "%s: searching for %s",
+ ifp->name, inet_ntoa(addr));
+ else
+ syslog(LOG_DEBUG, "%s: checking for %s",
+ ifp->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);
+ eloop_timeout_add_tv(&tv, arp_probe, ifp);
+ } else {
+ tv.tv_sec = ANNOUNCE_WAIT;
+ tv.tv_usec = 0;
+ if (arping) {
+ state->probes = 0;
+ if (++state->arping_index < ifp->options->arping_len)
+ eloop_timeout_add_tv(&tv, arp_probe, ifp);
+ else
+ eloop_timeout_add_tv(&tv, start_interface, ifp);
+ } else
+ eloop_timeout_add_tv(&tv, dhcp_bind, ifp);
+ }
+ syslog(LOG_DEBUG,
+ "%s: sending ARP probe (%d of %d), next in %0.1f seconds",
+ ifp->name, state->probes ? state->probes : PROBE_NUM, PROBE_NUM,
+ timeval_to_double(&tv));
+ if (arp_send(ifp, ARPOP_REQUEST, 0, addr.s_addr) == -1)
+ syslog(LOG_ERR, "send_arp: %m");
+}
+
+void
+arp_start(struct interface *ifp)
+{
+ struct dhcp_state *state = D_STATE(ifp);
+
+ state->probes = 0;
+ state->arping_index = 0;
+ arp_probe(ifp);
+}
diff --git a/dhcpcd/arp.h b/dhcpcd/arp.h
new file mode 100644
index 00000000..c2d4fe51
--- /dev/null
+++ b/dhcpcd/arp.h
@@ -0,0 +1,48 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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
+
+/* ARP timings from RFC5227 */
+#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 arp_announce(void *);
+void arp_probe(void *);
+void arp_start(struct interface *);
+#endif
diff --git a/dhcpcd/auth.c b/dhcpcd/auth.c
new file mode 100644
index 00000000..b1b7024a
--- /dev/null
+++ b/dhcpcd/auth.c
@@ -0,0 +1,557 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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/file.h>
+#include <sys/queue.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "auth.h"
+#include "crypt/crypt.h"
+#include "dhcp.h"
+#include "dhcp6.h"
+#include "dhcpcd.h"
+
+#ifndef htonll
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+static inline uint64_t
+htonll(uint64_t x)
+{
+
+ return (uint64_t)htonl((uint32_t)(x >> 32)) |
+ (int64_t)htonl((uint32_t)(x & 0xffffffff)) << 32;
+}
+#else /* (BYTE_ORDER == LITTLE_ENDIAN) */
+#define htonll(x) (x)
+#endif
+#endif /* htonll */
+
+#ifndef ntohll
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+static inline uint64_t
+ntohll(uint64_t x)
+{
+
+ return (uint64_t)ntohl((uint32_t)(x >> 32)) |
+ (int64_t)ntohl((uint32_t)(x & 0xffffffff)) << 32;
+}
+#else /* (BYTE_ORDER == LITTLE_ENDIAN) */
+#define ntohll(x) (x)
+#endif
+#endif /* ntohll */
+
+#define HMAC_LENGTH 16
+
+/*
+ * Authenticate a DHCP message.
+ * m and mlen refer to the whole message.
+ * t is the DHCP type, pass it 4 or 6.
+ * data and dlen refer to the authentication option within the message.
+ */
+const struct token *
+dhcp_auth_validate(struct authstate *state, const struct auth *auth,
+ const uint8_t *m, unsigned int mlen, int mp, int mt,
+ const uint8_t *data, unsigned int dlen)
+{
+ uint8_t protocol, algorithm, rdm, *mm, type;
+ uint64_t replay;
+ uint32_t secretid;
+ const uint8_t *d, *realm;
+ unsigned int realm_len;
+ const struct token *t;
+ time_t now;
+ uint8_t hmac[HMAC_LENGTH];
+
+ if (dlen < 3 + sizeof(replay)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
+ if (data < m || data > m + mlen || data + dlen > m + mlen) {
+ errno = ERANGE;
+ return NULL;
+ }
+
+ d = data;
+ protocol = *d++;
+ algorithm = *d++;
+ rdm = *d++;
+ if (!(auth->options & DHCPCD_AUTH_SEND)) {
+ /* If we didn't send any authorisation, it can only be a
+ * reconfigure key */
+ if (protocol != AUTH_PROTO_RECONFKEY) {
+ errno = EINVAL;
+ return NULL;
+ }
+ } else if (protocol != auth->protocol ||
+ algorithm != auth->algorithm ||
+ rdm != auth->rdm)
+ {
+ errno = EPERM;
+ return NULL;
+ }
+
+ dlen -= 3;
+ memcpy(&replay, d, sizeof(replay));
+ replay = ntohll(replay);
+ d+= sizeof(replay);
+ dlen -= sizeof(replay);
+
+ if (state->token && replay - state->replay <= 0) {
+ /* Replay attack detected */
+ errno = EPERM;
+ return NULL;
+ }
+
+ realm = NULL;
+ realm_len = 0;
+
+ /* Extract realm and secret.
+ * Rest of data is MAC. */
+ switch (protocol) {
+ case AUTH_PROTO_TOKEN:
+ secretid = 0;
+ break;
+ case AUTH_PROTO_DELAYED:
+ if (dlen < sizeof(secretid) + sizeof(hmac)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ memcpy(&secretid, d, sizeof(secretid));
+ d += sizeof(secretid);
+ dlen -= sizeof(secretid);
+ break;
+ case AUTH_PROTO_DELAYEDREALM:
+ if (dlen < sizeof(secretid) + sizeof(hmac)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ realm_len = dlen - (sizeof(secretid) + sizeof(hmac));
+ if (realm_len) {
+ realm = d;
+ d += realm_len;
+ dlen -= realm_len;
+ }
+ memcpy(&secretid, d, sizeof(secretid));
+ d += sizeof(secretid);
+ dlen -= sizeof(secretid);
+ break;
+ case AUTH_PROTO_RECONFKEY:
+ if (dlen != 1 + 16) {
+ errno = EINVAL;
+ return NULL;
+ }
+ type = *d++;
+ dlen--;
+ switch (type) {
+ case 1:
+ if ((mp == 4 && mt == DHCP_ACK) ||
+ (mp == 6 && mt == DHCP6_REPLY))
+ {
+ if (state->reconf == NULL) {
+ state->reconf =
+ malloc(sizeof(*state->reconf));
+ if (state->reconf == NULL)
+ return NULL;
+ state->reconf->key = malloc(16);
+ if (state->reconf->key == NULL) {
+ free(state->reconf);
+ state->reconf = NULL;
+ return NULL;
+ }
+ state->reconf->secretid = 0;
+ state->reconf->expire = 0;
+ state->reconf->realm = NULL;
+ state->reconf->realm_len = 0;
+ state->reconf->key_len = 16;
+ }
+ memcpy(state->reconf->key, d, 16);
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (state->reconf == NULL)
+ errno = ENOENT;
+ /* Nothing to validate, just accepting the key */
+ return state->reconf;
+ case 2:
+ if (state->reconf == NULL) {
+ errno = ENOENT;
+ return NULL;
+ }
+ t = state->reconf;
+ goto gottoken;
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+ default:
+ errno = ENOTSUP;
+ return NULL;
+ }
+
+ /* Find a token for the realm and secret */
+ secretid = ntohl(secretid);
+ TAILQ_FOREACH(t, &auth->tokens, next) {
+ if (t->secretid == secretid &&
+ t->realm_len == realm_len &&
+ (t->realm_len == 0 ||
+ memcmp(t->realm, realm, t->realm_len) == 0))
+ break;
+ }
+ if (t == NULL) {
+ errno = ESRCH;
+ return NULL;
+ }
+ if (t->expire) {
+ if (time(&now) == -1)
+ return NULL;
+ if (t->expire < now) {
+ errno = EFAULT;
+ return NULL;
+ }
+ }
+
+gottoken:
+ /* First message from the server */
+ if (state->token && state->token != t) {
+ errno = EPERM;
+ return NULL;
+ }
+
+ /* Special case as no hashing needs to be done. */
+ if (protocol == AUTH_PROTO_TOKEN) {
+ if (dlen != t->key_len || memcmp(d, t->key, dlen)) {
+ errno = EPERM;
+ return NULL;
+ }
+ goto finish;
+ }
+
+ /* Make a duplicate of the message, but zero out the MAC part */
+ mm = malloc(mlen);
+ if (mm == NULL)
+ return NULL;
+ memcpy(mm, m, mlen);
+ memset(mm + (d - m), 0, dlen);
+
+ /* RFC3318, section 5.2 - zero giaddr and hops */
+ if (mp == 4) {
+ *(mm + offsetof(struct dhcp_message, hwopcount)) = '\0';
+ memset(mm + offsetof(struct dhcp_message, giaddr), 0, 4);
+ }
+
+ memset(hmac, 0, sizeof(hmac));
+ switch (algorithm) {
+ case AUTH_ALG_HMAC_MD5:
+ hmac_md5(mm, mlen, t->key, t->key_len, hmac);
+ break;
+ default:
+ errno = ENOSYS;
+ free(mm);
+ return NULL;
+ }
+
+ free(mm);
+ if (memcmp(d, &hmac, dlen)) {
+ errno = EPERM;
+ return NULL;
+ }
+
+finish:
+ /* If we got here then authentication passed */
+ state->replay = replay;
+ state->token = t;
+
+ return t;
+}
+
+static uint64_t last_rdm;
+static uint8_t last_rdm_set;
+static uint64_t
+get_next_rdm_monotonic(void)
+{
+ FILE *fp;
+ char *line, *ep;
+ uint64_t rdm;
+ int flocked;
+
+ fp = fopen(RDM_MONOFILE, "r+");
+ if (fp == NULL) {
+ if (errno != ENOENT)
+ return ++last_rdm; /* report error? */
+ fp = fopen(RDM_MONOFILE, "w");
+ if (fp == NULL)
+ return ++last_rdm; /* report error? */
+ flocked = flock(fileno(fp), LOCK_EX);
+ rdm = 0;
+ } else {
+ flocked = flock(fileno(fp), LOCK_EX);
+ line = get_line(fp);
+ if (line == NULL)
+ rdm = 0; /* truncated? report error? */
+ else
+ rdm = strtoull(line, &ep, 0);
+ }
+
+ rdm++;
+ if (fseek(fp, 0, SEEK_SET) == -1 ||
+ ftruncate(fileno(fp), 0) == -1 ||
+ fprintf(fp, "0x%016" PRIu64 "\n", rdm) != 19)
+ {
+ if (!last_rdm_set) {
+ last_rdm = rdm;
+ last_rdm_set = 1;
+ } else
+ rdm = ++last_rdm;
+ /* report error? */
+ }
+ fflush(fp);
+ if (flocked == 0)
+ flock(fileno(fp), LOCK_UN);
+ fclose(fp);
+ return rdm;
+}
+
+
+/*
+ * Encode a DHCP message.
+ * Either we know which token to use from the server response
+ * or we are using a basic configuration token.
+ * token is the token to encrypt with.
+ * m and mlen refer to the whole message.
+ * mp is the DHCP type, pass it 4 or 6.
+ * mt is the DHCP message type.
+ * data and dlen refer to the authentication option within the message.
+ */
+int
+dhcp_auth_encode(const struct auth *auth, const struct token *t,
+ uint8_t *m, unsigned int mlen, int mp, int mt,
+ uint8_t *data, unsigned int dlen)
+{
+ uint64_t rdm;
+ uint8_t hmac[HMAC_LENGTH];
+ time_t now;
+ uint8_t hops, *p, info;
+ uint32_t giaddr, secretid;
+
+ if (auth->protocol == 0 && t == NULL) {
+ TAILQ_FOREACH(t, &auth->tokens, next) {
+ if (t->secretid == 0 &&
+ t->realm_len == 0)
+ break;
+ }
+ if (t == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (t->expire) {
+ if (time(&now) == -1)
+ return -1;
+ if (t->expire < now) {
+ errno = EPERM;
+ return -1;
+ }
+ }
+ }
+
+ switch(auth->protocol) {
+ case AUTH_PROTO_TOKEN:
+ case AUTH_PROTO_DELAYED:
+ case AUTH_PROTO_DELAYEDREALM:
+ /* We don't ever send a reconf key */
+ break;
+ default:
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ switch(auth->algorithm) {
+ case AUTH_ALG_HMAC_MD5:
+ break;
+ default:
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ switch(auth->rdm) {
+ case AUTH_RDM_MONOTONIC:
+ break;
+ default:
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ /* DISCOVER or INFORM messages don't write auth info */
+ if ((mp == 4 && (mt == DHCP_DISCOVER || mt == DHCP_INFORM)) ||
+ (mp == 6 && (mt == DHCP6_SOLICIT || mt == DHCP6_INFORMATION_REQ)))
+ info = 0;
+ else
+ info = 1;
+
+ /* Work out the auth area size.
+ * We only need to do this for DISCOVER messages */
+ if (data == NULL) {
+ dlen = 1 + 1 + 1 + 8;
+ switch(auth->protocol) {
+ case AUTH_PROTO_TOKEN:
+ dlen += t->key_len;
+ break;
+ case AUTH_PROTO_DELAYEDREALM:
+ if (info && t)
+ dlen += t->realm_len;
+ /* FALLTHROUGH */
+ case AUTH_PROTO_DELAYED:
+ if (info && t)
+ dlen += sizeof(t->secretid) + sizeof(hmac);
+ break;
+ }
+ return dlen;
+ }
+
+ if (dlen < 1 + 1 + 1 + 8) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ /* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
+ if (data < m || data > m + mlen || data + dlen > m + mlen) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ /* Write out our option */
+ *data++ = auth->protocol;
+ *data++ = auth->algorithm;
+ *data++ = auth->rdm;
+ switch (auth->rdm) {
+ case AUTH_RDM_MONOTONIC:
+ rdm = get_next_rdm_monotonic();
+ break;
+ default:
+ /* This block appeases gcc, clang doesn't need it */
+ rdm = get_next_rdm_monotonic();
+ break;
+ }
+ rdm = htonll(rdm);
+ memcpy(data, &rdm, 8);
+ data += 8;
+ dlen -= 1 + 1 + 1 + 8;
+
+ /* Special case as no hashing needs to be done. */
+ if (auth->protocol == AUTH_PROTO_TOKEN) {
+ /* Should be impossible, but still */
+ if (t == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (dlen < t->key_len) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(data, t->key, t->key_len);
+ return dlen - t->key_len;
+ }
+
+ /* DISCOVER or INFORM messages don't write auth info */
+ if (!info)
+ return dlen;
+
+ /* Loading a saved lease without an authentication option */
+ if (t == NULL)
+ return 0;
+
+ /* Write out the Realm */
+ if (auth->protocol == AUTH_PROTO_DELAYEDREALM) {
+ if (dlen < t->realm_len) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(data, t->realm, t->realm_len);
+ data += t->realm_len;
+ dlen -= t->realm_len;
+ }
+
+ /* Write out the SecretID */
+ if (auth->protocol == AUTH_PROTO_DELAYED ||
+ auth->protocol == AUTH_PROTO_DELAYEDREALM)
+ {
+ if (dlen < sizeof(t->secretid)) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ secretid = htonl(t->secretid);
+ memcpy(data, &secretid, sizeof(secretid));
+ data += sizeof(secretid);
+ dlen -= sizeof(secretid);
+ }
+
+ /* Zero what's left, the MAC */
+ memset(data, 0, dlen);
+
+ /* RFC3318, section 5.2 - zero giaddr and hops */
+ if (mp == 4) {
+ p = m + offsetof(struct dhcp_message, hwopcount);
+ hops = *p;
+ *p = '\0';
+ p = m + offsetof(struct dhcp_message, giaddr);
+ memcpy(&giaddr, p, sizeof(giaddr));
+ memset(p, 0, sizeof(giaddr));
+ } else {
+ /* appease GCC again */
+ hops = 0;
+ giaddr = 0;
+ }
+
+ /* Create our hash and write it out */
+ switch(auth->algorithm) {
+ case AUTH_ALG_HMAC_MD5:
+ hmac_md5(m, mlen, t->key, t->key_len, hmac);
+ memcpy(data, hmac, sizeof(hmac));
+ break;
+ }
+
+ /* RFC3318, section 5.2 - restore giaddr and hops */
+ if (mp == 4) {
+ p = m + offsetof(struct dhcp_message, hwopcount);
+ *p = hops;
+ p = m + offsetof(struct dhcp_message, giaddr);
+ memcpy(p, &giaddr, sizeof(giaddr));
+ }
+
+ /* Done! */
+ return dlen - sizeof(hmac); /* should be zero */
+}
diff --git a/dhcpcd/auth.h b/dhcpcd/auth.h
new file mode 100644
index 00000000..cd5f5443
--- /dev/null
+++ b/dhcpcd/auth.h
@@ -0,0 +1,79 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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 AUTH_H
+#define AUTH_H
+
+#include <sys/queue.h>
+
+#define DHCPCD_AUTH_SEND (1 << 0)
+#define DHCPCD_AUTH_REQUIRE (1 << 1)
+
+#define AUTH_PROTO_TOKEN 0
+#define AUTH_PROTO_DELAYED 1
+#define AUTH_PROTO_DELAYEDREALM 2
+#define AUTH_PROTO_RECONFKEY 3
+
+#define AUTH_ALG_HMAC_MD5 1
+
+#define AUTH_RDM_MONOTONIC 0
+
+struct token {
+ TAILQ_ENTRY(token) next;
+ uint32_t secretid;
+ unsigned int realm_len;
+ unsigned char *realm;
+ unsigned int key_len;
+ unsigned char *key;
+ time_t expire;
+};
+
+TAILQ_HEAD(token_head, token);
+
+struct auth {
+ int options;
+ uint8_t protocol;
+ uint8_t algorithm;
+ uint8_t rdm;
+ struct token_head tokens;
+};
+
+struct authstate {
+ uint64_t replay;
+ const struct token *token;
+ struct token *reconf;
+};
+
+const struct token * dhcp_auth_validate(struct authstate *,
+ const struct auth *,
+ const uint8_t *, unsigned int, int, int,
+ const uint8_t *, unsigned int);
+
+int dhcp_auth_encode(const struct auth *, const struct token *,
+ uint8_t *, unsigned int, int, int,
+ uint8_t *, unsigned int);
+#endif
diff --git a/dhcpcd/bpf-filter.h b/dhcpcd/bpf-filter.h
new file mode 100644
index 00000000..9bd15d8d
--- /dev/null
+++ b/dhcpcd/bpf-filter.h
@@ -0,0 +1,101 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * 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
+ * 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 BPF_ETHCOOK
+# define BPF_ETHCOOK 0
+#endif
+#ifndef BPF_WHOLEPACKET
+# define BPF_WHOLEPACKET ~0U
+#endif
+static const struct bpf_insn arp_bpf_filter [] = {
+#ifndef BPF_SKIPTYPE
+ /* Make sure this is an ARP packet... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 0, 3),
+#endif
+ /* Make sure this is an ARP REQUEST... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 + BPF_ETHCOOK),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),
+ /* or ARP REPLY... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 + BPF_ETHCOOK),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1),
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT(BPF_RET + BPF_K, BPF_WHOLEPACKET),
+ /* Otherwise, drop it. */
+ BPF_STMT(BPF_RET + BPF_K, 0),
+};
+static const size_t arp_bpf_filter_len =
+ sizeof(arp_bpf_filter) / sizeof(arp_bpf_filter[0]);
+
+
+/* dhcp_bpf_filter taken from bpf.c in dhcp-3.1.0
+ *
+ * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * http://www.isc.org/
+ */
+
+static const struct bpf_insn dhcp_bpf_filter [] = {
+#ifndef BPF_SKIPTYPE
+ /* Make sure this is an IP packet... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
+#endif
+ /* Make sure it's a UDP packet... */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23 + BPF_ETHCOOK),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+ /* Make sure this isn't a fragment... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 + BPF_ETHCOOK),
+ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
+ /* Get the IP header length... */
+ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14 + BPF_ETHCOOK),
+ /* Make sure it's to the right port... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16 + BPF_ETHCOOK),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_CLIENT_PORT, 0, 1),
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT(BPF_RET + BPF_K, BPF_WHOLEPACKET),
+ /* Otherwise, drop it. */
+ BPF_STMT(BPF_RET + BPF_K, 0),
+};
+static const size_t dhcp_bpf_filter_len =
+ sizeof(dhcp_bpf_filter) / sizeof(dhcp_bpf_filter[0]);
diff --git a/dhcpcd/bpf.c b/dhcpcd/bpf.c
new file mode 100644
index 00000000..59d17d92
--- /dev/null
+++ b/dhcpcd/bpf.c
@@ -0,0 +1,214 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.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 "ipv4.h"
+#include "bpf-filter.h"
+
+int
+ipv4_opensocket(struct interface *ifp, int protocol)
+{
+ struct dhcp_state *state;
+ int fd = -1;
+ struct ifreq ifr;
+ int buf_len = 0;
+ struct bpf_version pv;
+ struct bpf_program pf;
+#ifdef BIOCIMMEDIATE
+ int flags;
+#endif
+#ifdef _PATH_BPF
+ fd = open(_PATH_BPF, O_RDWR | O_NONBLOCK);
+#else
+ char *device;
+ int n = 0;
+
+ device = malloc(sizeof(char) * PATH_MAX);
+ if (device == NULL)
+ return -1;
+ do {
+ snprintf(device, PATH_MAX, "/dev/bpf%d", n++);
+ fd = open(device, O_RDWR | O_NONBLOCK);
+ } while (fd == -1 && errno == EBUSY);
+ free(device);
+#endif
+
+ if (fd == -1)
+ return -1;
+
+ state = D_STATE(ifp);
+
+ if (ioctl(fd, BIOCVERSION, &pv) == -1)
+ goto eexit;
+ if (pv.bv_major != BPF_MAJOR_VERSION ||
+ pv.bv_minor < BPF_MINOR_VERSION) {
+ syslog(LOG_ERR, "BPF version mismatch - recompile");
+ goto eexit;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
+ if (ioctl(fd, BIOCSETIF, &ifr) == -1)
+ goto eexit;
+
+ /* Get the required BPF buffer length from the kernel. */
+ if (ioctl(fd, BIOCGBLEN, &buf_len) == -1)
+ goto eexit;
+ if (state->buffer_size != (size_t)buf_len) {
+ free(state->buffer);
+ state->buffer = malloc(buf_len);
+ if (state->buffer == NULL)
+ goto eexit;
+ state->buffer_size = buf_len;
+ state->buffer_len = state->buffer_pos = 0;
+ }
+
+#ifdef BIOCIMMEDIATE
+ flags = 1;
+ if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1)
+ goto eexit;
+#endif
+
+ /* Install the DHCP filter */
+ if (protocol == ETHERTYPE_ARP) {
+ pf.bf_insns = UNCONST(arp_bpf_filter);
+ pf.bf_len = arp_bpf_filter_len;
+ } else {
+ pf.bf_insns = UNCONST(dhcp_bpf_filter);
+ pf.bf_len = dhcp_bpf_filter_len;
+ }
+ if (ioctl(fd, BIOCSETF, &pf) == -1)
+ goto eexit;
+ if (set_cloexec(fd) == -1)
+ goto eexit;
+ return fd;
+
+eexit:
+ free(state->buffer);
+ state->buffer = NULL;
+ close(fd);
+ return -1;
+}
+
+ssize_t
+ipv4_sendrawpacket(const struct interface *ifp, int protocol,
+ const void *data, ssize_t len)
+{
+ struct iovec iov[2];
+ struct ether_header hw;
+ int fd;
+ const struct dhcp_state *state;
+
+ memset(&hw, 0, ETHER_HDR_LEN);
+ memset(&hw.ether_dhost, 0xff, ETHER_ADDR_LEN);
+ hw.ether_type = htons(protocol);
+ iov[0].iov_base = &hw;
+ iov[0].iov_len = ETHER_HDR_LEN;
+ iov[1].iov_base = UNCONST(data);
+ iov[1].iov_len = len;
+ state = D_CSTATE(ifp);
+ if (protocol == ETHERTYPE_ARP)
+ fd = state->arp_fd;
+ else
+ fd = state->raw_fd;
+ return writev(fd, iov, 2);
+}
+
+/* BPF requires that we read the entire buffer.
+ * So we pass the buffer in the API so we can loop on >1 packet. */
+ssize_t
+ipv4_getrawpacket(struct interface *ifp, int protocol,
+ void *data, ssize_t len, int *partialcsum)
+{
+ int fd = -1;
+ struct bpf_hdr packet;
+ ssize_t bytes;
+ const unsigned char *payload;
+ struct dhcp_state *state;
+
+ state = D_STATE(ifp);
+ if (protocol == ETHERTYPE_ARP)
+ fd = state->arp_fd;
+ else
+ fd = state->raw_fd;
+
+ if (partialcsum != NULL)
+ *partialcsum = 0; /* Not supported on BSD */
+
+ for (;;) {
+ if (state->buffer_len == 0) {
+ bytes = read(fd, state->buffer, state->buffer_size);
+ if (bytes == -1)
+ return errno == EAGAIN ? 0 : -1;
+ else if ((size_t)bytes < sizeof(packet))
+ return -1;
+ state->buffer_len = bytes;
+ state->buffer_pos = 0;
+ }
+ bytes = -1;
+ memcpy(&packet, state->buffer + state->buffer_pos,
+ sizeof(packet));
+ if (packet.bh_caplen != packet.bh_datalen)
+ goto next; /* Incomplete packet, drop. */
+ if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
+ state->buffer_len)
+ goto next; /* Packet beyond buffer, drop. */
+ payload = state->buffer + state->buffer_pos +
+ packet.bh_hdrlen + ETHER_HDR_LEN;
+ bytes = packet.bh_caplen - ETHER_HDR_LEN;
+ if (bytes > len)
+ bytes = len;
+ memcpy(data, payload, bytes);
+next:
+ state->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
+ packet.bh_caplen);
+ if (state->buffer_pos >= state->buffer_len)
+ state->buffer_len = state->buffer_pos = 0;
+ if (bytes != -1)
+ return bytes;
+ }
+}
diff --git a/dhcpcd/common.c b/dhcpcd/common.c
new file mode 100644
index 00000000..4bf52bad
--- /dev/null
+++ b/dhcpcd/common.c
@@ -0,0 +1,279 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 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.
+ */
+
+/* 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>
+#endif
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#ifdef BSD
+# include <paths.h>
+#endif
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "common.h"
+
+#ifndef _PATH_DEVNULL
+# define _PATH_DEVNULL "/dev/null"
+#endif
+
+static char hostname_buffer[HOSTNAME_MAX_LEN + 1];
+int clock_monotonic;
+static char *lbuf;
+static size_t lbuf_len;
+#ifdef DEBUG_MEMORY
+static char lbuf_set;
+#endif
+
+#ifdef DEBUG_MEMORY
+static void
+free_lbuf(void)
+{
+ free(lbuf);
+ lbuf = NULL;
+}
+#endif
+
+/* 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)
+{
+ char *p;
+ ssize_t bytes;
+
+#ifdef DEBUG_MEMORY
+ if (lbuf_set == 0) {
+ atexit(free_lbuf);
+ lbuf_set = 1;
+ }
+#endif
+
+ 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
+set_cloexec(int fd)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
+ fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ {
+ syslog(LOG_ERR, "fcntl: %m");
+ return -1;
+ }
+ return 0;
+}
+
+int
+set_nonblock(int fd)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ {
+ syslog(LOG_ERR, "fcntl: %m");
+ return -1;
+ }
+ return 0;
+}
+
+const char *
+get_hostname(int short_hostname)
+{
+ char *p;
+
+ gethostname(hostname_buffer, sizeof(hostname_buffer));
+ hostname_buffer[sizeof(hostname_buffer) - 1] = '\0';
+ if (strcmp(hostname_buffer, "(none)") == 0 ||
+ strcmp(hostname_buffer, "localhost") == 0 ||
+ strncmp(hostname_buffer, "localhost.", strlen("localhost.")) == 0 ||
+ hostname_buffer[0] == '.')
+ return NULL;
+
+ if (short_hostname) {
+ p = strchr(hostname_buffer, '.');
+ if (p)
+ *p = '\0';
+ }
+
+ return hostname_buffer;
+}
+
+/* Handy function to get the time.
+ * We only care about time advancements, not the actual time itself
+ * Which is why we use CLOCK_MONOTONIC, but it is not available on all
+ * platforms.
+ */
+#define NO_MONOTONIC "host does not support a monotonic clock - timing can skew"
+int
+get_monotonic(struct timeval *tp)
+{
+ static int posix_clock_set = 0;
+#if defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC)
+ struct timespec ts;
+ static clockid_t posix_clock;
+
+ if (!posix_clock_set) {
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+ posix_clock = CLOCK_MONOTONIC;
+ clock_monotonic = posix_clock_set = 1;
+ }
+ }
+
+ if (clock_monotonic) {
+ if (clock_gettime(posix_clock, &ts) == 0) {
+ tp->tv_sec = ts.tv_sec;
+ tp->tv_usec = ts.tv_nsec / 1000;
+ return 0;
+ }
+ }
+#elif defined(__APPLE__)
+#define NSEC_PER_SEC 1000000000
+ /* We can use mach kernel functions here.
+ * This is crap though - why can't they implement clock_gettime?*/
+ static struct mach_timebase_info info = { 0, 0 };
+ static double factor = 0.0;
+ uint64_t nano;
+ long rem;
+
+ if (!posix_clock_set) {
+ if (mach_timebase_info(&info) == KERN_SUCCESS) {
+ factor = (double)info.numer / (double)info.denom;
+ clock_monotonic = posix_clock_set = 1;
+ }
+ }
+ if (clock_monotonic) {
+ nano = mach_absolute_time();
+ if ((info.denom != 1 || info.numer != 1) && factor != 0.0)
+ nano *= factor;
+ tp->tv_sec = nano / NSEC_PER_SEC;
+ rem = nano % NSEC_PER_SEC;
+ if (rem < 0) {
+ tp->tv_sec--;
+ rem += NSEC_PER_SEC;
+ }
+ tp->tv_usec = rem / 1000;
+ return 0;
+ }
+#endif
+
+ /* Something above failed, so fall back to gettimeofday */
+ if (!posix_clock_set) {
+ syslog(LOG_WARNING, NO_MONOTONIC);
+ posix_clock_set = 1;
+ }
+ return gettimeofday(tp, NULL);
+}
+
+ssize_t
+setvar(char ***e, const char *prefix, const char *var, const char *value)
+{
+ size_t len = strlen(var) + strlen(value) + 3;
+
+ if (prefix)
+ len += strlen(prefix) + 1;
+ **e = malloc(len);
+ if (**e == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return -1;
+ }
+ if (prefix)
+ snprintf(**e, len, "%s_%s=%s", prefix, var, value);
+ else
+ snprintf(**e, len, "%s=%s", var, value);
+ (*e)++;
+ return len;
+}
+
+ssize_t
+setvard(char ***e, const char *prefix, const char *var, int value)
+{
+ char buffer[32];
+
+ snprintf(buffer, sizeof(buffer), "%d", value);
+ return setvar(e, prefix, var, buffer);
+}
+
+
+time_t
+uptime(void)
+{
+ struct timeval tv;
+
+ if (get_monotonic(&tv) == -1)
+ return -1;
+ return tv.tv_sec;
+}
+
+int
+writepid(int fd, pid_t pid)
+{
+ char spid[16];
+ ssize_t len;
+
+ if (ftruncate(fd, (off_t)0) == -1)
+ return -1;
+ snprintf(spid, sizeof(spid), "%u\n", pid);
+ len = pwrite(fd, spid, strlen(spid), (off_t)0);
+ if (len != (ssize_t)strlen(spid))
+ return -1;
+ return 0;
+}
diff --git a/dhcpcd/common.h b/dhcpcd/common.h
new file mode 100644
index 00000000..9e3b48c9
--- /dev/null
+++ b/dhcpcd/common.h
@@ -0,0 +1,113 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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 COMMON_H
+#define COMMON_H
+
+#include <sys/time.h>
+#include <stdio.h>
+
+#include "config.h"
+#include "defs.h"
+
+#ifndef HOSTNAME_MAX_LEN
+#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
+#endif
+
+#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#define STRINGIFY(a) #a
+#define TOSTRING(a) STRINGIFY(a)
+
+#define timeval_to_double(tv) ((tv)->tv_sec * 1.0 + (tv)->tv_usec * 1.0e-6)
+#define timernorm(tv) do { \
+ while ((tv)->tv_usec >= 1000000) { \
+ (tv)->tv_sec++; \
+ (tv)->tv_usec -= 1000000; \
+ } \
+} while (0 /* CONSTCOND */);
+#define tv_to_ms(ms, tv) do { \
+ ms = (tv)->tv_sec * 1000; \
+ ms += (tv)->tv_usec / 1000; \
+} while (0 /* CONSTCOND */);
+#define ms_to_tv(tv, ms) do { \
+ (tv)->tv_sec = ms / 1000; \
+ (tv)->tv_usec = (ms - ((tv)->tv_sec * 1000)) * 1000; \
+} while (0 /* CONSTCOND */);
+
+#ifndef TIMEVAL_TO_TIMESPEC
+#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
+ (ts)->tv_sec = (tv)->tv_sec; \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
+} while (0 /* CONSTCOND */)
+#endif
+
+#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
+# ifndef __dead
+# define __dead __attribute__((__noreturn__))
+# endif
+# ifndef __packed
+# define __packed __attribute__((__packed__))
+# endif
+# ifndef __unused
+# define __unused __attribute__((__unused__))
+# endif
+#else
+# ifndef __dead
+# define __dead
+# endif
+# ifndef __packed
+# define __packed
+# endif
+# ifndef __unused
+# define __unused
+# endif
+#endif
+
+/* 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
+
+int set_cloexec(int);
+int set_nonblock(int);
+char *get_line(FILE * __restrict);
+const char *get_hostname(int);
+extern int clock_monotonic;
+int get_monotonic(struct timeval *);
+ssize_t setvar(char ***, const char *, const char *, const char *);
+ssize_t setvard(char ***, const char *, const char *, int);
+time_t uptime(void);
+int writepid(int, pid_t);
+
+#endif
diff --git a/dhcpcd/compat/arc4random.c b/dhcpcd/compat/arc4random.c
new file mode 100644
index 00000000..48ef29da
--- /dev/null
+++ b/dhcpcd/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/dhcpcd/compat/arc4random.h b/dhcpcd/compat/arc4random.h
new file mode 100644
index 00000000..68f8d642
--- /dev/null
+++ b/dhcpcd/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/dhcpcd/compat/closefrom.c b/dhcpcd/compat/closefrom.c
new file mode 100644
index 00000000..b04e991e
--- /dev/null
+++ b/dhcpcd/compat/closefrom.c
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+#ifdef _SC_OPEN_MAX
+ max = sysconf(_SC_OPEN_MAX);
+#else
+ max = getdtablesize();
+#endif
+ for (i = fd; i < max; i++)
+ r += close(i);
+ return r;
+}
diff --git a/dhcpcd/compat/closefrom.h b/dhcpcd/compat/closefrom.h
new file mode 100644
index 00000000..ed21068a
--- /dev/null
+++ b/dhcpcd/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/dhcpcd/compat/getline.c b/dhcpcd/compat/getline.c
new file mode 100644
index 00000000..68fe0854
--- /dev/null
+++ b/dhcpcd/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/dhcpcd/compat/getline.h b/dhcpcd/compat/getline.h
new file mode 100644
index 00000000..3db807a8
--- /dev/null
+++ b/dhcpcd/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/dhcpcd/compat/linkaddr.c b/dhcpcd/compat/linkaddr.c
new file mode 100644
index 00000000..c4e6fa54
--- /dev/null
+++ b/dhcpcd/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/dhcpcd/compat/pollts.c b/dhcpcd/compat/pollts.c
new file mode 100644
index 00000000..232a87ea
--- /dev/null
+++ b/dhcpcd/compat/pollts.c
@@ -0,0 +1,63 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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 <sys/types.h>
+
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "pollts.h"
+
+#warning "This pollts(2) implementation is not entirely race condition safe."
+#warning "Only operating system support for pollts(2) can correct this."
+
+int
+pollts(struct pollfd *restrict fds, nfds_t nfds,
+ const struct timespec *restrict ts, const sigset_t *restrict sigmask)
+{
+ int r, timeout;
+ sigset_t oldset;
+
+ if (ts == NULL)
+ timeout = -1;
+ else if (ts->tv_sec > INT_MAX / 1000 ||
+ (ts->tv_sec == INT_MAX / 1000 &&
+ (ts->tv_nsec + 999999) / 1000000 > INT_MAX % 1000000))
+ timeout = INT_MAX;
+ else
+ timeout = ts->tv_sec * 1000 + (ts->tv_nsec + 999999) / 1000000;
+ if (sigmask && sigprocmask(SIG_SETMASK, sigmask, &oldset) == -1)
+ return -1;
+ r = poll(fds, nfds, timeout);
+ if (sigmask && sigprocmask(SIG_SETMASK, &oldset, NULL) == -1)
+ return -1;
+
+ return r;
+}
diff --git a/dhcpcd/compat/pollts.h b/dhcpcd/compat/pollts.h
new file mode 100644
index 00000000..60ec3a35
--- /dev/null
+++ b/dhcpcd/compat/pollts.h
@@ -0,0 +1,39 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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 PPOLL_H
+#define PPOLL_H
+
+#include <poll.h>
+#include <signal.h>
+#include <time.h>
+
+int
+pollts(struct pollfd *restrict, nfds_t, const struct timespec *restrict,
+ const sigset_t *restrict);
+
+#endif
diff --git a/dhcpcd/compat/posix_spawn.c b/dhcpcd/compat/posix_spawn.c
new file mode 100644
index 00000000..822425e7
--- /dev/null
+++ b/dhcpcd/compat/posix_spawn.c
@@ -0,0 +1,147 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 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 implementation of posix_spawn is only suitable for the needs of dhcpcd
+ * but it could easily be extended to other applications. */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common.h"
+#include "posix_spawn.h"
+
+#ifndef _NSIG
+#ifdef _SIG_MAXSIG
+#define _NSIG _SIG_MAXSIG + 1
+#else
+/* Guess */
+#define _NSIG SIGPWR + 1
+#endif
+#endif
+
+extern char **environ;
+
+static int
+posix_spawnattr_handle(const posix_spawnattr_t *attrp)
+{
+ struct sigaction sa;
+ int i;
+
+ if (attrp->posix_attr_flags & POSIX_SPAWN_SETSIGMASK)
+ sigprocmask(SIG_SETMASK, &attrp->posix_attr_sigmask, NULL);
+
+ if (attrp->posix_attr_flags & POSIX_SPAWN_SETSIGDEF) {
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ for (i = 1; i < _NSIG; i++) {
+ if (sigismember(&attrp->posix_attr_sigdefault, i)) {
+ if (sigaction(i, &sa, NULL) == -1)
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+posix_spawn(pid_t *pid, const char * path,
+ __unused void *arg,
+ const posix_spawnattr_t *attrp,
+ char *const argv[], char *const envp[])
+{
+ pid_t p;
+ volatile int error;
+
+ error = 0;
+#ifdef THERE_IS_NO_FORK
+ /* Pray we can sanely modify signal foo */
+ p = vfork();
+#else
+ p = fork();
+#endif
+ switch (p) {
+ case -1:
+ return errno;
+ case 0:
+ if (attrp) {
+ error = posix_spawnattr_handle(attrp);
+ if (error)
+ _exit(127);
+ }
+ execve(path, argv, envp);
+ error = errno;
+ _exit(127);
+ default:
+ if (error != 0)
+ waitpid(p, NULL, WNOHANG);
+ else if (pid != NULL)
+ *pid = p;
+ return error;
+ }
+}
+
+int
+posix_spawnattr_init(posix_spawnattr_t *attr)
+{
+
+ memset(attr, 0, sizeof(*attr));
+ attr->posix_attr_flags = 0;
+ sigprocmask(0, NULL, &attr->posix_attr_sigmask);
+ sigemptyset(&attr->posix_attr_sigdefault);
+ return 0;
+}
+
+int
+posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags)
+{
+
+ attr->posix_attr_flags = flags;
+ return 0;
+}
+
+int
+posix_spawnattr_setsigmask(posix_spawnattr_t *attr, const sigset_t *sigmask)
+{
+
+ attr->posix_attr_sigmask = *sigmask;
+ return 0;
+}
+
+int
+posix_spawnattr_setsigdefault(posix_spawnattr_t *attr, const sigset_t *sigmask)
+{
+
+ attr->posix_attr_sigdefault = *sigmask;
+ return 0;
+}
diff --git a/dhcpcd/compat/posix_spawn.h b/dhcpcd/compat/posix_spawn.h
new file mode 100644
index 00000000..fe6958c4
--- /dev/null
+++ b/dhcpcd/compat/posix_spawn.h
@@ -0,0 +1,48 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 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 POSIX_SPAWN_H
+#define POSIX_SPAWN_H
+
+#include <signal.h>
+
+typedef struct {
+ short posix_attr_flags;
+#define POSIX_SPAWN_SETSIGDEF 0x10
+#define POSIX_SPAWN_SETSIGMASK 0x20
+ sigset_t posix_attr_sigmask;
+ sigset_t posix_attr_sigdefault;
+} posix_spawnattr_t;
+
+int posix_spawn(pid_t *, const char *, void *, const posix_spawnattr_t *,
+ char *const [], char *const []);
+int posix_spawnattr_init(posix_spawnattr_t *);
+int posix_spawnattr_setflags(posix_spawnattr_t *, short);
+int posix_spawnattr_setsigmask(posix_spawnattr_t *, const sigset_t *);
+int posix_spawnattr_setsigdefault(posix_spawnattr_t *, const sigset_t *);
+
+#endif
diff --git a/dhcpcd/compat/pselect.c b/dhcpcd/compat/pselect.c
new file mode 100644
index 00000000..8c6d8ed1
--- /dev/null
+++ b/dhcpcd/compat/pselect.c
@@ -0,0 +1,65 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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 <sys/types.h>
+
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "pollts.h"
+
+int
+pollts(struct pollfd *restrict fds, nfds_t nfds,
+ const struct timespec *restrict ts, const sigset_t *restrict sigmask)
+{
+ fd_set read_fds;
+ nfds_t n;
+ int maxfd, r;
+
+ FD_ZERO(&read_fds);
+ maxfd = 0;
+ for (n = 0; n < nfds; n++) {
+ if (fds[n].events & POLLIN) {
+ FD_SET(fds[n].fd, &read_fds);
+ if (fds[n].fd > maxfd)
+ maxfd = fds[n].fd;
+ }
+ }
+
+ r = pselect(maxfd + 1, &read_fds, NULL, NULL, ts, sigmask);
+ if (r > 0) {
+ for (n = 0; n < nfds; n++) {
+ fds[n].revents =
+ FD_ISSET(fds[n].fd, &read_fds) ? POLLIN : 0;
+ }
+ }
+
+ return r;
+}
diff --git a/dhcpcd/compat/strlcpy.c b/dhcpcd/compat/strlcpy.c
new file mode 100644
index 00000000..c2e324e8
--- /dev/null
+++ b/dhcpcd/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/dhcpcd/compat/strlcpy.h b/dhcpcd/compat/strlcpy.h
new file mode 100644
index 00000000..951390a9
--- /dev/null
+++ b/dhcpcd/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/dhcpcd/configure b/dhcpcd/configure
new file mode 100755
index 00000000..a7116ac2
--- /dev/null
+++ b/dhcpcd/configure
@@ -0,0 +1,803 @@
+#!/bin/sh
+# Try and be like autotools configure, but without autotools
+
+# Ensure that we do not inherit these from env
+HOOKSET=false
+INET=
+INET6=
+ARC4RANDOM=
+CLOSEFROM=
+GETLINE=
+STRLCPY=
+UDEV=
+OS=
+BUILD=
+HOST=
+TARGET=
+DEBUG=
+FORK=
+STATIC=
+INCLUDEDIR=
+DEVS=
+EMBEDDED=
+
+for x do
+ opt=${x%%=*}
+ var=${x#*=}
+ case "$opt" in
+ --os|OS) OS=$var;;
+ --with-cc|CC) CC=$var;;
+ --debug) DEBUG=$var;;
+ --disable-debug) DEBUG=no;;
+ --enable-debug) DEBUG=yes;;
+ --fork) FORK=$var;;
+ --disable-fork) FORK=no;;
+ --enable-fork) FORK=yes;;
+ --disable-static) STATIC=no;;
+ --enable-static) STATIC=yes;;
+ --disable-ipv4) INET=no;;
+ --enable-ipv4) INET=yes;;
+ --disable-ipv6) INET6=no;;
+ --enable-ipv6) INET6=yes;;
+ --disable-embedded) EMBEDDED=no;;
+ --enable-embedded) EMBEDDED=no;;
+ --prefix) PREFIX=$var;;
+ --sysconfdir) SYSCONFDIR=$var;;
+ --bindir|--sbindir) SBINDIR=$var;;
+ --libexecdir) LIBEXECDIR=$var;;
+ --statedir|--localstatedir) STATEDIR=$var;;
+ --dbdir) DBDIR=$var;;
+ --rundir) RUNDIR=$var;;
+ --mandir) MANDIR=$var;;
+ --with-ccopts|CFLAGS) CFLAGS=$var;;
+ CPPFLAGS) CPPFLAGS=$var;;
+ --with-hook) HOOKSCRIPTS="$HOOKSCRIPTS${HOOKSCRIPTS:+ }$var";;
+ --with-hooks|HOOKSCRIPTS) HOOKSCRIPTS=$var; HOOKSET=true;;
+ --build) BUILD=$var;;
+ --host) HOST=$var;;
+ --target) TARGET=$var;;
+ --libdir) LIBDIR=$var;;
+ --without-arc4random) ARC4RANDOM=no;;
+ --without-closefrom) CLOSEFROM=no;;
+ --without-getline) GETLINE=no;;
+ --without-strlcpy) STRLCPY=no;;
+ --without-posix_spawn) POSIX_SPAWN=no;;
+ --without-pollts) POLLTS=no;;
+ --with-pollts) POLLTS=$var;;
+ --without-md5) MD5=no;;
+ --without-dev) DEV=no;;
+ --without-udev) UDEV=no;;
+ --serviceexists) SERVICEEXISTS=$var;;
+ --servicecmd) SERVICECMD=$var;;
+ --servicestatus) SERVICESTATUS=$var;;
+ --includedir) eval INCLUDEDIR="$INCLUDEDIR${INCLUDEDIR:+ }$var";;
+ --datadir|--infodir) ;; # ignore autotools
+ --disable-maintainer-mode|--disable-dependency-tracking) ;;
+ -V|--version)
+ v=$(sed -ne 's/.*VERSION[[:space:]]*"\([^"]*\).*/\1/p' defs.h);
+ c=$(sed -ne 's/^.*copyright\[\] = "\([^"]*\).*/\1/p' dhcpcd.c);
+ echo "dhcpcd-$v $c";
+ exit 0;;
+ -h|--help) cat <<EOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: configure [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ -V, --version display version information and exit
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX [/]
+
+By default, \`make install' will install all the files in \'/sbin',
+\`/libexec', etc. You can specify
+an installation prefix other than \`/' using \`--prefix',
+for instance \`--prefix=$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [PREFIX/bin]
+ --sbindir=DIR system admin executables [PREFIX/sbin]
+ --libexecdir=DIR program executables [PREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [PREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --mandir=DIR man documentation [PREFIX/man]
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST build programs to run on HOST [BUILD]
+ --target=TARGET configure for building compilers for TARGET [HOST]
+
+Optional Features:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have
+ headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+
+Use these variables to override the choices made by \`configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+EOF
+exit 0
+;;
+ *) echo "$0: WARNING: unknown option $opt" >&2;;
+ esac
+done
+
+: ${SED:=sed}
+: ${GREP:=grep}
+: ${WC:=wc}
+
+: ${FORK:=yes}
+: ${SYSCONFDIR:=$PREFIX/etc}
+: ${SBINDIR:=$PREFIX/sbin}
+: ${LIBDIR:=$PREFIX/lib}
+: ${LIBEXECDIR:=$PREFIX/libexec}
+: ${STATEDIR:=/var}
+: ${DBDIR:=$STATEDIR/db}
+: ${RUNDIR:=$STATEDIR/run}
+: ${MANDIR:=${PREFIX:-/usr}/share/man}
+
+eval SYSCONFDIR="$SYSCONFDIR"
+eval LIBDIR="$LIBDIR"
+eval LIBEXECDIR="$LIBEXECDIR"
+eval STATEDIR="$STATEDIR"
+eval DBDIR="$DBDIR"
+eval RUNDIR="$RUNDIR"
+eval MANDIR="$MANDIR"
+
+_which()
+{
+ x="$(which "$1" 2>/dev/null)"
+ if [ -n "$x" ]; then
+ echo "$x"
+ return 0
+ fi
+ for x in /sbin/"$1" /usr/sbin/"$1" \
+ /usr/pkg/sbin/"$1" /usr/local/sbin/"$1"
+ do
+ if [ -e "$x" ]; then
+ echo "$x"
+ return 0
+ fi
+ done
+ return 1
+}
+
+CONFIG_H=config.h
+CONFIG_MK=config.mk
+
+if [ -z "$BUILD" ]; then
+ BUILD=`uname -m`-`uname -s | tr '[:upper:]' '[:lower:]'`
+fi
+if [ -z "$HOST" ]; then
+ [ -z "$TARGET" ] && TARGET=$BUILD
+ HOST=$TARGET
+fi
+if [ -z "$TARGET" ]; then
+ [ -z "$HOST" ] && HOST=$BUILD
+ TARGET=$HOST
+fi
+
+# Debian and Slackware have linux in different places when dealing with
+# autoconf, so we deal with that here.
+if [ -z "$OS" ]; then
+ case "$TARGET" in
+ *-linux-*|linux-*|*-linux|linux) OS=linux;;
+ esac
+fi
+
+if [ -z "$OS" ]; then
+ # Derive OS from cpu-manufacturer-os-kernel
+ CPU=${TARGET%%-*}
+ REST=${TARGET#*-}
+ if [ "$CPU" != "$REST" ]; then
+ MANU=${REST%%-*}
+ REST=${REST#*-}
+ if [ "$MANU" != "$REST" ]; then
+ OS=${REST%%-*}
+ REST=${REST#*-}
+ if [ "$OS" != "$REST" ]; then
+ KERNEL=${REST%%-*}
+ else
+ # 3 tupple
+ KERNEL=$OS
+ OS=$MANU
+ MANU=
+ fi
+ else
+ # 2 tupple
+ OS=$MANU
+ MANU=
+ fi
+ fi
+fi
+
+echo "Configuring dhcpcd for ... $OS"
+rm -f $CONFIG_H $CONFIG_MK
+echo "# $OS" >$CONFIG_MK
+echo "/* $OS */" >$CONFIG_H
+
+for x in SYSCONFDIR SBINDIR LIBDIR LIBEXECDIR DBDIR RUNDIR; do
+ eval v=\$$x
+ # Make files look nice for import
+ l=$((10 - ${#x}))
+ unset t
+ [ $l -gt 3 ] && t=" "
+ echo "$x=$t $v" >>$CONFIG_MK
+ unset t
+ [ $l -gt 2 ] && t=" "
+ echo "#define $x$t \"$v\"" >>$CONFIG_H
+done
+echo "LIBDIR= $LIBDIR" >>$CONFIG_MK
+echo "MANDIR= $MANDIR" >>$CONFIG_MK
+
+if [ -z "$CC" ]; then
+ printf "Looking for compiler ... "
+ for b in $TARGET- ""; do
+ for cc in gcc clang pcc icc cc; do
+ if type $b$cc >/dev/null 2>&1; then
+ CC=$b$cc
+ echo "$CC"
+ break
+ fi
+ done
+ [ -n "$CC" ] && break
+ done
+ if [ -z "$CC" ]; then
+ echo
+ echo "no suitable compiler found - aborting" >&2
+ exit 1
+ fi
+else
+ echo "Using compiler $CC"
+fi
+$CC --version | $SED -e '1!d'
+echo "CC= $CC" >>$CONFIG_MK
+
+# Set to blank, then append user config
+# We do this so our SED call to append to XCC remains portable
+if [ -n "$CFLAGS" ]; then
+ echo "CFLAGS=" >>$CONFIG_MK
+ echo "CFLAGS+= $CFLAGS" >>$CONFIG_MK
+fi
+if [ -n "$CPPFLAGS" ]; then
+ echo "CPPFLAGS=" >>$CONFIG_MK
+ echo "CPPFLAGS+= $CPPFLAGS" >>$CONFIG_MK
+fi
+if [ -n "$LDFLAGS" ]; then
+ echo "LDFLAGS=" >>$CONFIG_MK
+ echo "LDFLAGS+= $LDFLAGS" >>$CONFIG_MK
+fi
+
+if [ "$STATIC" = yes ]; then
+ echo "LDFLAGS+= -static" >>$CONFIG_MK
+fi
+for x in $INCLUDEDIR; do
+ echo "CPPFLAGS+= -I$x" >>$CONFIG_MK
+done
+
+if [ -n "$DEBUG" -a "$DEBUG" != no -a "$DEBUG" != false ]; then
+ echo "Enabling memory debugging"
+ echo "CPPFLAGS+= -DDEBUG_MEMORY" >>$CONFIG_MK
+ echo "CFLAGS+= -g" >>$CONFIG_MK
+elif [ -z "$DEBUG" -a -f .fslckout ]; then
+ printf "Found fossil checkout ... "
+ DEBUG=yes
+ echo "CFLAGS+= -g" >>$CONFIG_MK
+else
+ DEBUG=no
+fi
+if [ "$DEBUG" != no -a "$DEBUG" != false ]; then
+ echo "Adding debugging CFLAGS"
+ cat <<EOF >>$CONFIG_MK
+CFLAGS+= -Wall -Wextra -Wimplicit -Wshadow -Wformat=2
+CFLAGS+= -Wmissing-prototypes -Wmissing-declarations
+CFLAGS+= -Wmissing-noreturn -Wmissing-format-attribute
+CFLAGS+= -Wredundant-decls -Wnested-externs
+CFLAGS+= -Winline -Wwrite-strings -Wcast-align -Wcast-qual
+CFLAGS+= -Wpointer-arith -Wstrict-overflow
+CFLAGS+= -Wdeclaration-after-statement -Wsequence-point
+CFLAGS+= -fno-common
+EOF
+fi
+
+if [ -z "$EMBEDDED" -o "$EMBEDDED" = yes ]; then
+ echo "dhcpcd-definitions.conf will be embedded in dhcpcd itself"
+ echo "DHCPCD_SRCS+= dhcpcd-embedded.c" >>$CONFIG_MK
+else
+ echo "dhcpcd-definitions.conf will be installed to $LIBEXECDIR"
+ echo "CFLAGS+= -DEMBEDDED_CONFIG=\\\"$LIBEXECDIR/dhcpcd-definitions.conf\\\"" >>$CONFIG_MK
+ echo "EMBEDDEDINSTALL= _embeddedinstall" >>$CONFIG_MK
+fi
+
+if [ -n "$FORK" -a "$FORK" != yes -a "$FORK" != true ]; then
+ echo "There is no fork"
+ echo "CPPFLAGS+= -DTHERE_IS_NO_FORK" >>$CONFIG_MK
+fi
+
+case "$OS" in
+linux)
+ echo "CPPFLAGS+= -D_BSD_SOURCE -D_XOPEN_SOURCE=700" >>$CONFIG_MK
+ if [ -z "$INET" -o "$INET" = yes ]; then
+ echo "DHCPCD_SRCS+= lpf.c" >>$CONFIG_MK
+ fi
+ echo "DHCPCD_SRCS+= if-linux.c if-linux-wireless.c" >>$CONFIG_MK
+ echo "DHCPCD_SRCS+= platform-linux.c" >>$CONFIG_MK
+ echo "LDADD+= -lrt -ldl" >>$CONFIG_MK
+ ;;
+kfreebsd)
+ echo "CPPFLAGS+= -D_GNU_SOURCE" >>$CONFIG_MK
+ if [ -z "$INET" -o "$INET" = yes ]; then
+ echo "DHCPCD_SRCS+= bpf.c" >>$CONFIG_MK
+ fi
+ echo "DHCPCD_SRCS+= if-bsd.c platform-bsd.c" >>$CONFIG_MK
+ echo "COMPAT_SRCS+= compat/linkaddr.c" >>$CONFIG_MK
+ echo "LDADD+= -lrt -ldl" >>$CONFIG_MK
+ ;;
+*)
+ if [ -z "$INET" -o "$INET" = yes ]; then
+ echo "DHCPCD_SRCS+= bpf.c" >>$CONFIG_MK
+ fi
+ echo "DHCPCD_SRCS+= if-bsd.c platform-bsd.c" >>$CONFIG_MK
+ ;;
+esac
+
+if [ -z "$INET" -o "$INET" = yes ]; then
+ echo "CPPFLAGS+= -DINET" >>$CONFIG_MK
+ echo "DHCPCD_SRCS+= arp.c dhcp.c ipv4.c ipv4ll.c" >>$CONFIG_MK
+fi
+if [ -z "$INET6" -o "$INET6" = yes ]; then
+ echo "CPPFLAGS+= -DINET6" >>$CONFIG_MK
+ echo "DHCPCD_SRCS+= ipv6.c ipv6nd.c dhcp6.c" >>$CONFIG_MK
+fi
+
+# NetBSD: Even if we build for $PREFIX, the clueless user might move us to /
+LDELF=/libexec/ld.elf_so
+if [ -e "$LDELF" ]; then
+ echo "Linking against $LDELF"
+ echo "LDFLAGS+= -Wl,-dynamic-linker=$LDELF" >>$CONFIG_MK
+ echo "LDFLAGS+= -Wl,-rpath=${LIBDIR}" >>$CONFIG_MK
+fi
+
+# Add CPPFLAGS and CFLAGS to CC for testing features
+XCC="$CC `$SED -n -e 's/CPPFLAGS+=*\(.*\)/\1/p' $CONFIG_MK`"
+XCC="$XCC `$SED -n -e 's/CFLAGS+=*\(.*\)/\1/p' $CONFIG_MK`"
+
+abort=false
+# We require the libc to support non standard functions, like getifaddrs
+printf "Testing for getifaddrs ... "
+cat <<EOF >_getifaddrs.c
+#include <sys/types.h>
+#include <ifaddrs.h>
+int main(void) {
+ struct ifaddrs *ifap;
+ return getifaddrs(&ifap);
+}
+EOF
+if $XCC _getifaddrs.c -o _getifaddrs 2>/dev/null; then
+ echo "yes"
+else
+ echo "no"
+ echo "libc support for getifaddrs is required - aborting" >&2
+ abort=true
+fi
+rm -f _getifaddrs.c _getifaddrs
+$abort && exit 1
+
+if [ -z "$ARC4RANDOM" ]; then
+ printf "Testing for arc4random ... "
+ cat <<EOF >_arc4random.c
+#include <stdlib.h>
+int main(void) {
+ arc4random();
+ return 0;
+}
+EOF
+ if $XCC _arc4random.c -o _arc4random 2>/dev/null; then
+ ARC4RANDOM=yes
+ else
+ ARC4RANDOM=no
+ fi
+ echo "$ARC4RANDOM"
+ rm -f _arc4random.c _arc4random
+fi
+if [ "$ARC4RANDOM" = no ]; then
+ echo "COMPAT_SRCS+= compat/arc4random.c" >>$CONFIG_MK
+ echo "#include \"compat/arc4random.h\"" >>$CONFIG_H
+fi
+
+if [ -z "$CLOSEFROM" ]; then
+ printf "Testing for closefrom ... "
+ cat <<EOF >_closefrom.c
+#include <unistd.h>
+int main(void) {
+ closefrom(3);
+ return 0;
+}
+EOF
+ if $XCC _closefrom.c -o _closefrom 2>/dev/null; then
+ CLOSEFROM=yes
+ else
+ CLOSEFROM=no
+ fi
+ echo "$CLOSEFROM"
+ rm -f _closefrom.c _closefrom
+fi
+if [ "$CLOSEFROM" = no ]; then
+ echo "COMPAT_SRCS+= compat/closefrom.c" >>$CONFIG_MK
+ echo "#include \"compat/closefrom.h\"" >>$CONFIG_H
+fi
+
+if [ -z "$GETLINE" ]; then
+ printf "Testing for getline ... "
+ cat <<EOF >_getline.c
+#define _GNU_SOURCE
+#include <stdio.h>
+int main(void) {
+ char *buf = NULL;
+ size_t n = 0;
+ getline(&buf, &n, stdin);
+ return 0;
+}
+EOF
+ if $XCC _getline.c -o _getline 2>/dev/null; then
+ GETLINE=yes
+ else
+ GETLINE=no
+ fi
+ echo "$GETLINE"
+ rm -f _getline.c _getline
+fi
+if [ "$GETLINE" = no ]; then
+ echo "COMPAT_SRCS+= compat/getline.c" >>$CONFIG_MK
+ echo "#include \"compat/getline.h\"" >>$CONFIG_H
+fi
+
+if [ -z "$STRLCPY" ]; then
+ printf "Testing for strlcpy ... "
+ cat <<EOF >_strlcpy.c
+#include <string.h>
+int main(void) {
+ const char s1[] = "foo";
+ char s2[10];
+ strlcpy(s2, s1, sizeof(s2));
+ return 0;
+}
+EOF
+ if $XCC _strlcpy.c -o _strlcpy 2>/dev/null; then
+ STRLCPY=yes
+ else
+ STRLCPY=no
+ fi
+ echo "$STRLCPY"
+ rm -f _strlcpy.c _strlcpy
+fi
+if [ "$STRLCPY" = no ]; then
+ echo "COMPAT_SRCS+= compat/strlcpy.c" >>$CONFIG_MK
+ echo "#include \"compat/strlcpy.h\"" >>$CONFIG_H
+fi
+
+if [ -z "$TAILQ_FOREACH_SAFE" ]; then
+ printf "Testing for TAILQ_FOREACH_SAFE ... "
+ cat <<EOF >_queue.c
+#include <sys/queue.h>
+int main(void) {
+#ifndef TAILQ_FOREACH_SAFE
+#error TAILQ_FOREACH_SAFE
+#endif
+ return 0;
+}
+EOF
+ if $XCC _queue.c -o _queue 2>/dev/null; then
+ TAILQ_FOREACH_SAFE=yes
+ else
+ TAILQ_FOREACH_SAFE=no
+ fi
+ echo "$TAILQ_FOREACH_SAFE"
+ rm -f _queue.c _queue
+fi
+if [ "$TAILQ_FOREACH_SAFE" = no ]; then
+ cat <<EOF >>$CONFIG_H
+#define TAILQ_FOREACH_SAFE(var, head, field, next) \\
+ for ((var) = TAILQ_FIRST((head)); \\
+ (var) && ((next) = TAILQ_NEXT((var), field), 1); \\
+ (var) = (next))
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \\
+ for ((var) = TAILQ_LAST((head), headname); \\
+ (var) && ((prev) = TAILQ_PREV((var), headname, field), 1); \\
+ (var) = (prev))
+EOF
+fi
+
+if [ -z "$POSIX_SPAWN" ]; then
+ printf "Testing for posix_spawn ... "
+ cat <<EOF >_posix_spawn.c
+#include <spawn.h>
+#include <stdlib.h>
+int main(void) {
+ posix_spawn(NULL, NULL, NULL, NULL, NULL, NULL);
+ return 0;
+}
+EOF
+ if $XCC _posix_spawn.c -o _posix_spawn 2>/dev/null; then
+ POSIX_SPAWN=yes
+ else
+ POSIX_SPAWN=no
+ fi
+ echo "$POSIX_SPAWN"
+ rm -f _posix_spawn.c _posix_spawn
+fi
+if [ "$POSIX_SPAWN" = no ]; then
+ echo "COMPAT_SRCS+= compat/posix_spawn.c" >>$CONFIG_MK
+ echo "#include \"compat/posix_spawn.h\"" >>$CONFIG_H
+else
+ echo "#include <spawn.h>" >>$CONFIG_H
+fi
+
+if [ -z "$POLLTS" ]; then
+ printf "Testing for pollts ... "
+ cat <<EOF >_pollts.c
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <time.h>
+int main(void) {
+ pollts(NULL, 0, NULL, NULL);
+ return 0;
+}
+EOF
+ if $XCC _pollts.c -o _pollts 2>/dev/null; then
+ POLLTS=yes
+ else
+ POLLTS=no
+ fi
+ echo "$POLLTS"
+ rm -f _pollts.c _pollts
+fi
+if [ "$POLLTS" = no ]; then
+ printf "Testing for ppoll ... "
+ cat <<EOF >_ppoll.c
+#include <poll.h>
+#include <stdlib.h>
+int main(void) {
+ ppoll(NULL, 0, NULL, NULL);
+ return 0;
+}
+EOF
+ if $XCC _ppoll.c -o _ppoll 2>/dev/null; then
+ POLLTS=ppoll
+ echo "yes"
+ else
+ POLLTS=no
+ echo "no"
+ fi
+ rm -f _ppoll.c _ppoll
+fi
+if [ "$POLLTS" = no ]; then
+ printf "Testing for pselect ... "
+ cat <<EOF >_pselect.c
+#include <sys/select.h>
+#include <stdlib.h>
+int main(void) {
+ pselect(0, NULL, NULL, NULL, NULL, NULL);
+ return 0;
+}
+EOF
+ if $XCC _pselect.c -o _pselect 2>/dev/null; then
+ POLLTS=pselect
+ echo "yes"
+ else
+ POLLTS=no
+ echo "no"
+ fi
+ rm -f _pselect.c _pselect
+fi
+case "$POLLTS" in
+yes)
+ ;;
+ppoll)
+ echo "#define pollts ppoll" >>$CONFIG_H
+ ;;
+pselect)
+ echo "COMPAT_SRCS+= compat/pselect.c" >>$CONFIG_MK
+ echo "#include \"compat/pollts.h\"" >>$CONFIG_H
+ ;;
+*)
+ echo "COMPAT_SRCS+= compat/pollts.c" >>$CONFIG_MK
+ echo "#include \"compat/pollts.h\"" >>$CONFIG_H
+ ;;
+esac
+
+if [ -z "$MD5" ]; then
+ printf "Testing for MD5Init ... "
+ cat <<EOF >_md5.c
+#include <md5.h>
+#include <stdlib.h>
+int main(void) {
+ MD5_CTX context;
+ MD5Init(&context);
+ return 0;
+}
+EOF
+ if $XCC _md5.c -o _md5 2>/dev/null; then
+ MD5=yes
+ else
+ MD5=no
+ fi
+ echo "$MD5"
+ rm -f _md5.c _md5
+fi
+if [ "$MD5" = no ]; then
+ echo "MD5_SRC= md5.c" >>$CONFIG_MK
+else
+ echo "MD5_SRC=" >>$CONFIG_MK
+ echo "#define HAVE_MD5H" >>$CONFIG_H
+fi
+
+if [ "$DEV" != no -a "$UDEV" != no ]; then
+ printf "Checking for libudev ... "
+ LIBUDEV_CFLAGS=$(pkg-config --cflags libudev 2>/dev/null)
+ LIBUDEV_LIBS=$(pkg-config --libs libudev 2>/dev/null)
+fi
+if [ "$DEV" != no -a "$UDEV" != no -a -n "$LIBUDEV_LIBS" ]; then
+ echo "yes"
+ [ -z "$DEV" ] && DEV=yes
+ echo "DEV_PLUGINS+= udev" >>$CONFIG_MK
+ if [ -n "$LIBUDEV_CFLAGS" ]; then
+ echo "LIBUDEV_CFLAGS= $LIBUDEV_CFLAGS" >>$CONFIG_MK
+ fi
+ echo "LIBUDEV_LIBS= $LIBUDEV_LIBS" >>$CONFIG_MK
+
+ printf "Checking udev_monitor_filter_add_match_subsystem_devtype ... "
+ cat <<EOF >_udev.c
+#include <libudev.h>
+#include <stdlib.h>
+int main(void) {
+ udev_monitor_filter_add_match_subsystem_devtype(NULL, NULL, NULL);
+ return 0;
+}
+EOF
+ if $XCC $LIBUDEV_CFLAGS _udev.c -o _udev $LIBUDEV_LIBS 2>/dev/null
+ then
+ echo "yes"
+ else
+ echo "LIBUDEV_CPPFLAGS+= -DLIBUDEV_NOFILTER" >>$CONFIG_MK
+ echo "no"
+ fi
+ rm -f _udev.c _udev
+
+ printf "Checking udev_device_get_is_initialized ... "
+ cat <<EOF >_udev.c
+#include <libudev.h>
+#include <stdlib.h>
+int main(void) {
+ udev_device_get_is_initialized(NULL);
+ return 0;
+}
+EOF
+ if $XCC $LIBUDEV_CFLAGS _udev.c -o _udev $LIBUDEV_LIBS 2>/dev/null
+ then
+ echo "yes"
+ else
+ echo "LIBUDEV_CPPFLAGS+= -DLIBUDEV_NOINIT" >>$CONFIG_MK
+ echo "no"
+ fi
+ rm -f _udev.c _udev
+elif [ "$DEV" != no -a "$UDEV" != no ]; then
+ echo "no"
+fi
+
+if [ "$DEV" = yes ]; then
+ echo "DHCPCD_SRCS+= dev.c" >>$CONFIG_MK
+ echo "CPPFLAGS+= -DPLUGIN_DEV" >>$CONFIG_MK
+ echo "MKDIRS+= dev" >>$CONFIG_MK
+fi
+
+# Transform for a make file
+SERVICEEXISTS=$(echo "$SERVICEEXISTS" | $SED \
+ -e 's:\\:\\\\:g' \
+ -e 's:\&:\\\&:g' \
+ -e 's:\$:\\\\\$\$:g' \
+)
+echo "SERVICEEXISTS= $SERVICEEXISTS" >>config.mk
+SERVICECMD=$(echo "$SERVICECMD" | $SED \
+ -e 's:\\:\\\\:g' \
+ -e 's:\&:\\\&:g' \
+ -e 's:\$:\\\\\$\$:g' \
+)
+echo "SERVICECMD= $SERVICECMD" >>config.mk
+SERVICESTATUS=$(echo "$SERVICESTATUS" | $SED \
+ -e 's:\\:\\\\:g' \
+ -e 's:\&:\\\&:g' \
+ -e 's:\$:\\\\\$\$:g' \
+)
+echo "SERVICESTATUS= $SERVICESTATUS" >>config.mk
+
+HOOKS=
+if ! $HOOKSET; then
+ printf "Checking for ntpd ... "
+ NTPD=$(_which ntpd)
+ if [ -n "$NTPD" ]; then
+ echo "$NTPD (50-ntp.conf)"
+ HOOKS="$HOOKS${HOOKS:+ }50-ntp.conf"
+ else
+ echo "not found"
+ fi
+
+ printf "Checking for ypind ... "
+ YPBIND=$(_which ypbind)
+ if [ -n "$YPBIND" ]; then
+ if strings "$YPBIND" | $GREP -q yp.conf; then
+ YPHOOK="50-yp.conf"
+ else
+ YPHOOK="50-ypbind"
+ fi
+ echo "$YPBIND ($YPHOOK)"
+ HOOKS="$HOOKS${HOOKS:+ }$YPHOOK"
+ else
+ echo "not found"
+ fi
+fi
+
+cd dhcpcd-hooks
+for x in $HOOKSCRIPTS; do
+ printf "Finding hook $x ... "
+ for h in [0-9][0-9]"-$x" [0-9][0-9]"-$x.sh" [0-9][0-9]"-$x.conf"; do
+ [ -e "$h" ] && break
+ done
+ if [ ! -e "$h" ]; then
+ echo "no"
+ else
+ echo "$h"
+ case " $HOOKS " in
+ *" $h "*) ;;
+ *) HOOKS="$HOOKS${HOOKS:+ }$h";;
+ esac
+ fi
+done
+cd ..
+echo "HOOKSCRIPTS= $HOOKS" >>$CONFIG_MK
+
+# Define any RCSIDs for import into 3rd party systems
+case "$OS" in
+netbsd)
+ echo "IMPORT_RCSID= \#include <sys/cdefs.h>\\n" >>$CONFIG_MK
+ echo "IMPORT_RCSID+= __RCSID(\\\"\\\$\$NetBSD: \\\$\$\\\");" >> \
+ $CONFIG_MK
+ echo "IMPORT_HID+= /* \\\$\$NetBSD: \\\$\$ */" >> \
+ $CONFIG_MK
+ echo "IMPORT_MANID= .\\\\\\\\\\\" \\\$\$NetBSD: \$\$ " >> \
+ $CONFIG_MK
+ echo "IMPORT_SHID= \# \\\$\$NetBSD: \$\$ " >>$CONFIG_MK
+ ;;
+esac
+
+echo
+echo " SYSCONFDIR = $SYSCONFDIR"
+echo " SBINDIR = $SBINDIR"
+echo " LIBDIR = $LIBDIR"
+echo " LIBEXECDIR = $LIBEXECDIR"
+echo " DBDIR = $DBDIR"
+echo " RUNDIR = $RUNDIR"
+echo " MANDIR = $MANDIR"
+echo " HOOKSCRIPTS = $HOOKS"
+echo
+
+rm -f dhcpcd tests/test
diff --git a/dhcpcd/control.c b/dhcpcd/control.c
new file mode 100644
index 00000000..fa455517
--- /dev/null
+++ b/dhcpcd/control.c
@@ -0,0 +1,231 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 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/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];
+
+static struct sockaddr_un sun;
+struct fd_list *control_fds = NULL;
+
+#ifdef DEBUG_MEMORY
+static void
+cleanup(void)
+{
+ struct fd_list *f;
+
+ f = control_fds;
+ while (f) {
+ control_fds = f->next;
+ free(f);
+ f = control_fds;
+ }
+}
+#endif
+
+static void
+control_remove(void *arg)
+{
+ struct fd_list *l, *n, *last = NULL;
+
+ l = control_fds;
+ while (l) {
+ n = l->next;
+ if (l == arg) {
+ close(l->fd);
+ eloop_event_delete(l->fd);
+ if (last == NULL)
+ control_fds = l->next;
+ else
+ last->next = l->next;
+ free(l);
+ break;
+ }
+ last = l;
+ l = n;
+ }
+}
+
+static void
+control_handle_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) {
+ control_remove(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
+control_handle(__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;
+ set_cloexec(f);
+ l = malloc(sizeof(*l));
+ if (l) {
+ l->fd = f;
+ l->listener = 0;
+ l->next = control_fds;
+ control_fds = l;
+ eloop_event_add(l->fd, control_handle_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
+control_start(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(control_fds)) == -1)
+ {
+ close(fd);
+ return -1;
+ }
+ eloop_event_add(fd, control_handle, NULL);
+ return fd;
+}
+
+int
+control_stop(void)
+{
+ int retval = 0;
+ struct fd_list *l;
+
+ eloop_event_delete(fd);
+ if (shutdown(fd, SHUT_RDWR) == -1)
+ retval = 1;
+ fd = -1;
+ if (unlink(CONTROLSOCKET) == -1)
+ retval = -1;
+
+ l = control_fds;
+ while (l != NULL) {
+ control_fds = l->next;
+ eloop_event_delete(l->fd);
+ shutdown(l->fd, SHUT_RDWR);
+ free(l);
+ l = control_fds;
+ }
+
+ return retval;
+}
+
+int
+control_open(void)
+{
+ int len;
+
+ if ((len = make_sock()) == -1)
+ return -1;
+#ifdef DEBUG_MEMORY
+ atexit(cleanup);
+#endif
+ return connect(fd, (struct sockaddr *)&sun, len);
+}
+
+int
+control_send(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/dhcpcd/control.h b/dhcpcd/control.h
new file mode 100644
index 00000000..02627eb6
--- /dev/null
+++ b/dhcpcd/control.h
@@ -0,0 +1,45 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 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 *control_fds;
+
+int control_start(void);
+int control_stop(void);
+int control_open(void);
+int control_send(int, char * const *);
+
+#endif
diff --git a/dhcpcd/crypt/crypt.h b/dhcpcd/crypt/crypt.h
new file mode 100644
index 00000000..0a3932e6
--- /dev/null
+++ b/dhcpcd/crypt/crypt.h
@@ -0,0 +1,33 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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 CRYPT_H
+#define CRYPT_H
+
+void hmac_md5(const uint8_t *, int, const uint8_t *, int, uint8_t *);
+
+#endif
diff --git a/dhcpcd/crypt/hmac_md5.c b/dhcpcd/crypt/hmac_md5.c
new file mode 100644
index 00000000..06ea465a
--- /dev/null
+++ b/dhcpcd/crypt/hmac_md5.c
@@ -0,0 +1,86 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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 <inttypes.h>
+#include <string.h>
+
+#include "crypt.h"
+
+#ifdef HAVE_MD5_H
+#include <md5.h>
+#else
+#include "md5.h"
+#endif
+
+#define HMAC_PAD_LEN 64
+#define IPAD 0x36
+#define OPAD 0x5C
+
+/* hmac_md5 as per RFC3118 */
+void
+hmac_md5(const uint8_t *text, int text_len,
+ const uint8_t *key, int key_len,
+ uint8_t *digest)
+{
+ uint8_t k_ipad[HMAC_PAD_LEN], k_opad[HMAC_PAD_LEN];
+ uint8_t tk[MD5_DIGEST_LENGTH];
+ int i;
+ MD5_CTX context;
+
+ /* Ensure key is no bigger than HMAC_PAD_LEN */
+ if (key_len > HMAC_PAD_LEN) {
+ MD5Init(&context);
+ MD5Update(&context, key, key_len);
+ MD5Final(tk, &context);
+ key = tk;
+ key_len = MD5_DIGEST_LENGTH;
+ }
+
+ /* store key in pads */
+ memcpy(k_ipad, key, key_len);
+ memcpy(k_opad, key, key_len);
+ memset(k_ipad + key_len, 0, sizeof(k_ipad) - key_len);
+ memset(k_opad + key_len, 0, sizeof(k_opad) - key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i = 0; i < HMAC_PAD_LEN; i++) {
+ k_ipad[i] ^= IPAD;
+ k_opad[i] ^= OPAD;
+ }
+
+ /* inner MD5 */
+ MD5Init(&context);
+ MD5Update(&context, k_ipad, HMAC_PAD_LEN);
+ MD5Update(&context, text, text_len);
+ MD5Final(digest, &context);
+
+ /* outer MD5 */
+ MD5Init(&context);
+ MD5Update(&context, k_opad, HMAC_PAD_LEN);
+ MD5Update(&context, digest, MD5_DIGEST_LENGTH);
+ MD5Final(digest, &context);
+}
diff --git a/dhcpcd/crypt/md5.c b/dhcpcd/crypt/md5.c
new file mode 100644
index 00000000..9530b88e
--- /dev/null
+++ b/dhcpcd/crypt/md5.c
@@ -0,0 +1,242 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include <sys/param.h>
+#include <inttypes.h>
+
+#include <string.h>
+
+#include "md5.h"
+
+#define PUT_64BIT_LE(cp, value) do { \
+ (cp)[7] = (value) >> 56; \
+ (cp)[6] = (value) >> 48; \
+ (cp)[5] = (value) >> 40; \
+ (cp)[4] = (value) >> 32; \
+ (cp)[3] = (value) >> 24; \
+ (cp)[2] = (value) >> 16; \
+ (cp)[1] = (value) >> 8; \
+ (cp)[0] = (value); } while (0)
+
+#define PUT_32BIT_LE(cp, value) do { \
+ (cp)[3] = (value) >> 24; \
+ (cp)[2] = (value) >> 16; \
+ (cp)[1] = (value) >> 8; \
+ (cp)[0] = (value); } while (0)
+
+static uint8_t PADDING[MD5_BLOCK_LENGTH] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void
+MD5Init(MD5_CTX *ctx)
+{
+ ctx->count = 0;
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xefcdab89;
+ ctx->state[2] = 0x98badcfe;
+ ctx->state[3] = 0x10325476;
+}
+
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD5Transform(uint32_t state[4], const uint8_t block[MD5_BLOCK_LENGTH])
+{
+ uint32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4];
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ memcpy(in, block, sizeof(in));
+#else
+ for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) {
+ in[a] = (uint32_t)(
+ (uint32_t)(block[a * 4 + 0]) |
+ (uint32_t)(block[a * 4 + 1]) << 8 |
+ (uint32_t)(block[a * 4 + 2]) << 16 |
+ (uint32_t)(block[a * 4 + 3]) << 24);
+ }
+#endif
+
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21);
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len)
+{
+ size_t have, need;
+
+ /* Check how many bytes we already have and how many more we need. */
+ have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
+ need = MD5_BLOCK_LENGTH - have;
+
+ /* Update bitcount */
+ ctx->count += (uint64_t)len << 3;
+
+ if (len >= need) {
+ if (have != 0) {
+ memcpy(ctx->buffer + have, input, need);
+ MD5Transform(ctx->state, ctx->buffer);
+ input += need;
+ len -= need;
+ have = 0;
+ }
+
+ /* Process data in MD5_BLOCK_LENGTH-byte chunks. */
+ while (len >= MD5_BLOCK_LENGTH) {
+ MD5Transform(ctx->state, input);
+ input += MD5_BLOCK_LENGTH;
+ len -= MD5_BLOCK_LENGTH;
+ }
+ }
+
+ /* Handle any remaining bytes of data. */
+ if (len != 0)
+ memcpy(ctx->buffer + have, input, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx)
+{
+ uint8_t count[8];
+ size_t padlen;
+ int i;
+
+ /* Convert count to 8 bytes in little endian order. */
+ PUT_64BIT_LE(count, ctx->count);
+
+ /* Pad out to 56 mod 64. */
+ padlen = MD5_BLOCK_LENGTH -
+ ((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
+ if (padlen < 1 + 8)
+ padlen += MD5_BLOCK_LENGTH;
+ MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */
+ MD5Update(ctx, count, 8);
+
+ if (digest != NULL) {
+ for (i = 0; i < 4; i++)
+ PUT_32BIT_LE(digest + i * 4, ctx->state[i]);
+ }
+ memset(ctx, 0, sizeof(*ctx)); /* in case it's sensitive */
+}
+
+
diff --git a/dhcpcd/crypt/md5.h b/dhcpcd/crypt/md5.h
new file mode 100644
index 00000000..402309c3
--- /dev/null
+++ b/dhcpcd/crypt/md5.h
@@ -0,0 +1,33 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef MD5_H_
+#define MD5_H_
+
+#define MD5_DIGEST_LENGTH 16
+#define MD5_BLOCK_LENGTH 64
+
+typedef struct MD5Context {
+ uint32_t state[4]; /* state (ABCD) */
+ uint64_t count; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[MD5_BLOCK_LENGTH]; /* input buffer */
+} MD5_CTX;
+
+void MD5Init(MD5_CTX *);
+void MD5Update(MD5_CTX *, const unsigned char *, size_t);
+void MD5Final(unsigned char[MD5_DIGEST_LENGTH], MD5_CTX *);
+#endif
diff --git a/dhcpcd/defs.h b/dhcpcd/defs.h
new file mode 100644
index 00000000..f477272e
--- /dev/null
+++ b/dhcpcd/defs.h
@@ -0,0 +1,61 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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 "6.2.1"
+
+#ifndef CONFIG
+# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"
+#endif
+#ifndef SCRIPT
+# define SCRIPT LIBEXECDIR "/" PACKAGE "-run-hooks"
+#endif
+#ifndef DEVDIR
+# define DEVDIR LIBDIR "/" PACKAGE "/dev"
+#endif
+#ifndef DUID
+# define DUID SYSCONFDIR "/" PACKAGE ".duid"
+#endif
+#ifndef LEASEFILE
+# define LEASEFILE DBDIR "/" PACKAGE "-%s.lease"
+#endif
+#ifndef LEASEFILE6
+# define LEASEFILE6 DBDIR "/" PACKAGE "-%s.lease6"
+#endif
+#ifndef PIDFILE
+# define PIDFILE RUNDIR "/" PACKAGE "%s%s.pid"
+#endif
+#ifndef CONTROLSOCKET
+# define CONTROLSOCKET RUNDIR "/" PACKAGE ".sock"
+#endif
+#ifndef RDM_MONOFILE
+# define RDM_MONOFILE DBDIR "/" PACKAGE "-rdm.monotonic"
+#endif
+
+#endif
diff --git a/dhcpcd/dev.c b/dhcpcd/dev.c
new file mode 100644
index 00000000..b33a3e00
--- /dev/null
+++ b/dhcpcd/dev.c
@@ -0,0 +1,177 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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.
+ */
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "common.h"
+#include "dev.h"
+#include "eloop.h"
+#include "dhcpcd.h"
+
+static struct dev *dev;
+static void *handle;
+static int fd = -1;
+
+static struct dev_dhcpcd dev_dhcpcd = {
+ .handle_interface = &handle_interface
+};
+
+int
+dev_initialized(const char *ifname)
+{
+
+ if (dev == NULL)
+ return 1;
+ return dev->initialized(ifname);
+}
+
+int
+dev_listening(void)
+{
+
+ if (dev == NULL)
+ return 0;
+ return dev->listening();
+}
+
+void
+dev_stop(void)
+{
+
+ if (dev) {
+ syslog(LOG_DEBUG, "dev: unloaded %s", dev->name);
+ dev->stop();
+ free(dev);
+ dev = NULL;
+ }
+ if (handle) {
+ dlclose(handle);
+ handle = NULL;
+ }
+}
+
+static int
+dev_start2(const char *name)
+{
+ char file[PATH_MAX];
+ void *h;
+ void (*fptr)(struct dev *, const struct dev_dhcpcd *);
+ int r;
+
+ snprintf(file, sizeof(file), DEVDIR "/%s", name);
+ h = dlopen(file, RTLD_LAZY);
+ if (h == NULL) {
+ syslog(LOG_ERR, "dlopen: %s", dlerror());
+ return -1;
+ }
+ fptr = (void (*)(struct dev *, const struct dev_dhcpcd *))
+ dlsym(h, "dev_init");
+ if (fptr == NULL) {
+ syslog(LOG_ERR, "dlsym: %s", dlerror());
+ dlclose(h);
+ return -1;
+ }
+ dev = calloc(1, sizeof(*dev));
+ fptr(dev, &dev_dhcpcd);
+ if (dev->start == NULL || (r = dev->start()) == -1) {
+ free(dev);
+ dev = NULL;
+ dlclose(h);
+ return -1;
+ }
+ syslog(LOG_INFO, "dev: loaded %s", dev->name);
+ handle = h;
+ return r;
+}
+
+static int
+dev_start1(const char *plugin)
+{
+ DIR *dp;
+ struct dirent *d;
+ int r;
+
+ if (dev) {
+ syslog(LOG_ERR, "dev: already started %s", dev->name);
+ return -1;
+ }
+
+ if (plugin)
+ return dev_start2(plugin);
+
+ dp = opendir(DEVDIR);
+ if (dp == NULL) {
+ syslog(LOG_DEBUG, "dev: %s: %m", DEVDIR);
+ return 0;
+ }
+
+ r = 0;
+ while ((d = readdir(dp))) {
+ if (d->d_name[0] == '.')
+ continue;
+
+ r = dev_start2(d->d_name);
+ if (r != -1)
+ break;
+ }
+ closedir(dp);
+ return r;
+}
+
+static void
+dev_handle_data(__unused void *arg)
+{
+
+ if (dev->handle_device() == -1) {
+ /* XXX: an error occured. should we restart dev? */
+ }
+}
+
+int
+dev_start(const char *plugin)
+{
+
+ if (fd != -1) {
+ syslog(LOG_ERR, "%s: already started on fd %d", __func__, fd);
+ return fd;
+ }
+
+ fd = dev_start1(plugin);
+ if (fd != -1) {
+ if (eloop_event_add(fd, dev_handle_data, NULL) == -1) {
+ syslog(LOG_ERR, "%s: eloop_event_add: %m", __func__);
+ dev_stop();
+ return -1;
+ }
+ }
+
+ return fd;
+}
diff --git a/dhcpcd/dev.h b/dhcpcd/dev.h
new file mode 100644
index 00000000..b22ad648
--- /dev/null
+++ b/dhcpcd/dev.h
@@ -0,0 +1,59 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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 DEV_H
+#define DEV_H
+
+// dev plugin setup
+struct dev {
+ const char *name;
+ int (*initialized)(const char *);
+ int (*listening)(void);
+ int (*handle_device)(void);
+ int (*start)(void);
+ void (*stop)(void);
+};
+
+struct dev_dhcpcd {
+ void (*handle_interface)(int, const char *);
+};
+
+int dev_init(struct dev *, const struct dev_dhcpcd *);
+
+// hooks for dhcpcd
+#ifdef PLUGIN_DEV
+int dev_initialized(const char *);
+int dev_listening(void);
+int dev_start(const char *);
+void dev_stop(void);
+#else
+#define dev_initialized(a) 1
+#define dev_listening() 0
+#define dev_start(a) {}
+#define dev_stop() {}
+#endif
+
+#endif
diff --git a/dhcpcd/dev/Makefile b/dhcpcd/dev/Makefile
new file mode 100644
index 00000000..e6b3a2cf
--- /dev/null
+++ b/dhcpcd/dev/Makefile
@@ -0,0 +1,42 @@
+TOP?= ../
+include ${TOP}/Makefile.inc
+include ${TOP}/config.mk
+
+CFLAGS?= -O2
+CSTD?= c99
+CFLAGS+= -std=${CSTD}
+
+DEVDIR= ${LIBDIR}/dhcpcd/dev
+DSRC= ${DEV_PLUGINS:=.c}
+DOBJ= ${DSRC:.c=.o}
+DSOBJ= ${DOBJ:.o=.So}
+DPLUGS= ${DEV_PLUGINS:=.so}
+
+CLEANFILES+= ${DSOBJ} ${DPLUGS}
+
+.SUFFIXES: .So .so
+
+.c.So:
+ ${CC} ${PICFLAG} -DPIC ${CPPFLAGS} ${CFLAGS} -c $< -o $@
+
+.So.so: ${DSOBJ}
+ ${CC} ${LDFLAGS} -shared -Wl,-x -o $@ -Wl,-soname,$@ \
+ $< ${LIBS}
+
+all: ${DPLUGS}
+
+udev.So:
+CFLAGS+= ${LIBUDEV_CFLAGS}
+CPPFLAGS+= ${LIBUDEV_CPPFLAGS}
+
+udev.so:
+LIBS+= ${LIBUDEV_LIBS}
+
+proginstall: ${DPLUGS}
+ ${INSTALL} -d ${DESTDIR}${DEVDIR}
+ ${INSTALL} -m ${BINMODE} ${PROG} ${DPLUGS} ${DESTDIR}${DEVDIR}
+
+install: proginstall
+
+clean:
+ rm -f ${CLEANFILES}
diff --git a/dhcpcd/dev/udev.c b/dhcpcd/dev/udev.c
new file mode 100644
index 00000000..0b02ffb8
--- /dev/null
+++ b/dhcpcd/dev/udev.c
@@ -0,0 +1,179 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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.
+ */
+
+#ifdef LIBUDEV_NOINIT
+# define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+# warning This version of udev is too old does not support
+# warning per device initialization checks.
+# warning As such, dhcpcd will need to depend on the
+# warning udev-settle service or similar if starting
+# warning in master mode.
+#endif
+
+#include <libudev.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "../common.h"
+#include "../dev.h"
+
+static const char udev_name[]="udev";
+static struct udev *udev;
+static struct udev_monitor *monitor;
+
+static const struct dev_dhcpcd *dhcpcd;
+
+static int
+udev_listening(void)
+{
+
+ return monitor ? 1 : 0;
+}
+
+static int
+udev_initialized(const char *ifname)
+{
+ struct udev_device *device;
+ int r;
+
+ device = udev_device_new_from_subsystem_sysname(udev, "net", ifname);
+ if (device) {
+#ifndef LIBUDEV_NOINIT
+ r = udev_device_get_is_initialized(device);
+#else
+ r = 1;
+#endif
+ udev_device_unref(device);
+ } else
+ r = 0;
+ return r;
+}
+
+static int
+udev_handle_device(void)
+{
+ struct udev_device *device;
+ const char *subsystem, *ifname, *action;
+
+ device = udev_monitor_receive_device(monitor);
+ if (device == NULL) {
+ syslog(LOG_ERR, "libudev: received NULL device");
+ return -1;
+ }
+
+ subsystem = udev_device_get_subsystem(device);
+ ifname = udev_device_get_sysname(device);
+ action = udev_device_get_action(device);
+
+ /* udev filter documentation says "usually" so double check */
+ if (strcmp(subsystem, "net") == 0) {
+ syslog(LOG_DEBUG, "%s: libudev: %s", ifname, action);
+ if (strcmp(action, "add") == 0 || strcmp(action, "move") == 0)
+ dhcpcd->handle_interface(1, ifname);
+ else if (strcmp(action, "remove") == 0)
+ dhcpcd->handle_interface(-1, ifname);
+ }
+
+ udev_device_unref(device);
+ return 1;
+}
+
+static void
+udev_stop(void)
+{
+
+ if (monitor) {
+ udev_monitor_unref(monitor);
+ monitor = NULL;
+ }
+
+ if (udev) {
+ udev_unref(udev);
+ udev = NULL;
+ }
+}
+
+static int
+udev_start(void)
+{
+ int fd;
+
+ if (udev) {
+ syslog(LOG_ERR, "udev: already started");
+ return -1;
+ }
+
+ syslog(LOG_DEBUG, "udev: starting");
+ udev = udev_new();
+ if (udev == NULL) {
+ syslog(LOG_ERR, "udev_new: %m");
+ return -1;
+ }
+ monitor = udev_monitor_new_from_netlink(udev, "udev");
+ if (monitor == NULL) {
+ syslog(LOG_ERR, "udev_monitor_new_from_netlink: %m");
+ goto bad;
+ }
+#ifndef LIBUDEV_NOFILTER
+ if (udev_monitor_filter_add_match_subsystem_devtype(monitor,
+ "net", NULL) != 0)
+ {
+ syslog(LOG_ERR,
+ "udev_monitor_filter_add_match_subsystem_devtype: %m");
+ goto bad;
+ }
+#endif
+ if (udev_monitor_enable_receiving(monitor) != 0) {
+ syslog(LOG_ERR, "udev_monitor_enable_receiving: %m");
+ goto bad;
+ }
+ fd = udev_monitor_get_fd(monitor);
+ if (fd == -1) {
+ syslog(LOG_ERR, "udev_monitor_get_fd: %m");
+ goto bad;
+ }
+ return fd;
+
+bad:
+ udev_stop();
+ return -1;
+}
+
+int
+dev_init(struct dev *dev, const struct dev_dhcpcd *dev_dhcpcd)
+{
+
+ dev->name = udev_name;
+ dev->initialized = udev_initialized;
+ dev->listening = udev_listening;
+ dev->handle_device = udev_handle_device;
+ dev->stop = udev_stop;
+ dev->start = udev_start;
+
+ dhcpcd = dev_dhcpcd;
+
+ return 0;
+}
diff --git a/dhcpcd/dhcp-common.c b/dhcpcd/dhcp-common.c
new file mode 100644
index 00000000..c8db0738
--- /dev/null
+++ b/dhcpcd/dhcp-common.c
@@ -0,0 +1,684 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp-common.h"
+#include "dhcp.h"
+
+/* DHCP Enterprise options, RFC3925 */
+struct dhcp_opt *vivso = NULL;
+size_t vivso_len = 0;
+
+struct dhcp_opt *
+vivso_find(uint16_t iana_en, const void *arg)
+{
+ const struct interface *ifp = arg;
+ size_t i;
+ struct dhcp_opt *opt;
+
+ if (arg) {
+ ifp = arg;
+ for (i = 0, opt = ifp->options->vivso_override;
+ i < ifp->options->vivso_override_len;
+ i++, opt++)
+ if (opt->option == iana_en)
+ return opt;
+ }
+ for (i = 0, opt = vivso; i < vivso_len; i++, opt++)
+ if (opt->option == iana_en)
+ return opt;
+ return NULL;
+}
+
+int
+make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len,
+ uint8_t *mask, const char *opts, int add)
+{
+ char *token, *o, *p, *t;
+ const struct dhcp_opt *opt;
+ int match;
+ unsigned int n;
+ size_t i;
+
+ o = p = strdup(opts);
+ if (opts == NULL)
+ return -1;
+ while ((token = strsep(&p, ", "))) {
+ if (*token == '\0')
+ continue;
+ for (i = 0, opt = dopts; i < dopts_len; i++, opt++) {
+ match = 0;
+ if (strcmp(opt->var, token) == 0)
+ match = 1;
+ else {
+ errno = 0;
+ n = strtol(token, &t, 0);
+ if (errno == 0 && !*t)
+ if (opt->option == n)
+ match = 1;
+ }
+ if (match) {
+ if (add == 2 && !(opt->type & ADDRIPV4)) {
+ free(o);
+ errno = EINVAL;
+ return -1;
+ }
+ if (add == 1 || add == 2)
+ add_option_mask(mask,
+ opt->option);
+ else
+ del_option_mask(mask,
+ opt->option);
+ break;
+ }
+ }
+ if (!opt->option) {
+ free(o);
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ free(o);
+ return 0;
+}
+
+size_t
+encode_rfc1035(const char *src, uint8_t *dst)
+{
+ uint8_t *p;
+ uint8_t *lp;
+ size_t len;
+ uint8_t has_dot;
+
+ if (src == NULL || *src == '\0')
+ return 0;
+
+ if (dst) {
+ p = dst;
+ lp = p++;
+ }
+ /* Silence bogus GCC warnings */
+ else
+ p = lp = NULL;
+
+ len = 1;
+ has_dot = 0;
+ for (; *src; src++) {
+ if (*src == '\0')
+ break;
+ if (*src == '.') {
+ /* Skip the trailing . */
+ if (src[1] == '\0')
+ break;
+ has_dot = 1;
+ if (dst) {
+ *lp = p - lp - 1;
+ if (*lp == '\0')
+ return len;
+ lp = p++;
+ }
+ } else if (dst)
+ *p++ = (uint8_t)*src;
+ len++;
+ }
+
+ if (dst) {
+ *lp = p - lp - 1;
+ if (has_dot)
+ *p++ = '\0';
+ }
+
+ if (has_dot)
+ len++;
+
+ return len;
+}
+
+/* Decode an RFC3397 DNS search order option into a space
+ * separated string. Returns length of string (including
+ * terminating zero) or zero on error. out may be NULL
+ * to just determine output length. */
+ssize_t
+decode_rfc3397(char *out, ssize_t len, int pl, const uint8_t *p)
+{
+ const char *start;
+ ssize_t start_len;
+ const uint8_t *r, *q = p;
+ int count = 0, l, hops;
+ uint8_t ltype;
+
+ start = out;
+ start_len = len;
+ while (q - p < pl) {
+ r = NULL;
+ hops = 0;
+ /* Check we are inside our length again in-case
+ * the name isn't fully qualified (ie, not terminated) */
+ while (q - p < pl && (l = *q++)) {
+ ltype = l & 0xc0;
+ if (ltype == 0x80 || ltype == 0x40)
+ return 0;
+ else if (ltype == 0xc0) { /* pointer */
+ l = (l & 0x3f) << 8;
+ l |= *q++;
+ /* save source of first jump. */
+ if (!r)
+ r = q;
+ hops++;
+ if (hops > 255)
+ return 0;
+ q = p + l;
+ if (q - p >= pl)
+ return 0;
+ } else {
+ /* straightforward name segment, add with '.' */
+ count += l + 1;
+ if (out) {
+ if ((ssize_t)l + 1 > len) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ memcpy(out, q, l);
+ out += l;
+ *out++ = '.';
+ len -= l;
+ len--;
+ }
+ q += l;
+ }
+ }
+ /* change last dot to space */
+ if (out && out != start)
+ *(out - 1) = ' ';
+ if (r)
+ q = r;
+ }
+
+ /* change last space to zero terminator */
+ if (out) {
+ if (out != start)
+ *(out - 1) = '\0';
+ else if (start_len > 0)
+ *out = '\0';
+ }
+
+ return count;
+}
+
+ssize_t
+print_string(char *s, ssize_t len, int dl, const uint8_t *data)
+{
+ uint8_t c;
+ const uint8_t *e, *p;
+ ssize_t bytes = 0;
+ ssize_t r;
+
+ e = data + dl;
+ while (data < e) {
+ c = *data++;
+ if (c == '\0') {
+ /* If rest is all NULL, skip it. */
+ for (p = data; p < e; p++)
+ if (*p != '\0')
+ break;
+ if (p == e)
+ break;
+ }
+ if (!isascii(c) || !isprint(c)) {
+ if (s) {
+ if (len < 5) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ r = snprintf(s, len, "\\%03o", c);
+ len -= r;
+ bytes += r;
+ s += r;
+ } else
+ bytes += 4;
+ continue;
+ }
+ switch (c) {
+ case '"': /* FALLTHROUGH */
+ case '\'': /* FALLTHROUGH */
+ case '$': /* FALLTHROUGH */
+ case '`': /* FALLTHROUGH */
+ case '\\': /* FALLTHROUGH */
+ case '|': /* FALLTHROUGH */
+ case '&':
+ if (s) {
+ if (len < 3) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ *s++ = '\\';
+ len--;
+ }
+ bytes++;
+ break;
+ }
+ if (s) {
+ *s++ = c;
+ len--;
+ }
+ bytes++;
+ }
+
+ /* NULL */
+ if (s)
+ *s = '\0';
+ bytes++;
+ return bytes;
+}
+
+#define ADDRSZ 4
+#define ADDR6SZ 16
+static size_t
+dhcp_optlen(const struct dhcp_opt *opt, size_t dl)
+{
+ size_t sz;
+
+ if (dl == 0)
+ return 0;
+
+ if (opt->type == 0 ||
+ opt->type & (STRING | BINHEX | RFC3442 | RFC5969))
+ {
+ if (opt->len) {
+ if ((size_t)opt->len > dl)
+ return 0;
+ return opt->len;
+ }
+ return dl;
+ }
+
+ if ((opt->type & (ADDRIPV4 | ARRAY)) == (ADDRIPV4 | ARRAY)) {
+ if (dl < ADDRSZ)
+ return 0;
+ return dl - (dl % ADDRSZ);
+ }
+
+ if ((opt->type & (ADDRIPV6 | ARRAY)) == (ADDRIPV6 | ARRAY)) {
+ if (dl < ADDR6SZ)
+ return 0;
+ return dl - (dl % ADDR6SZ);
+ }
+
+ sz = 0;
+ if (opt->type & (UINT32 | ADDRIPV4))
+ sz = sizeof(uint32_t);
+ else if (opt->type & UINT16)
+ sz = sizeof(uint16_t);
+ else if (opt->type & UINT8)
+ sz = sizeof(uint8_t);
+ else if (opt->type & ADDRIPV6)
+ sz = ADDR6SZ;
+ else
+ /* If we don't know the size, assume it's valid */
+ return dl;
+ return (dl < sz ? 0 : sz);
+}
+
+#ifdef INET6
+#define PO_IFNAME
+#else
+#define PO_IFNAME __unused
+#endif
+
+ssize_t
+print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data,
+ PO_IFNAME const char *ifname)
+{
+ const uint8_t *e, *t;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ int32_t s32;
+ struct in_addr addr;
+ ssize_t bytes = 0;
+ ssize_t l;
+ char *tmp;
+
+ if (type & RFC3397) {
+ l = decode_rfc3397(NULL, 0, dl, data);
+ if (l < 1)
+ return l;
+ tmp = malloc(l);
+ if (tmp == NULL)
+ return -1;
+ decode_rfc3397(tmp, l, dl, data);
+ l = print_string(s, len, l - 1, (uint8_t *)tmp);
+ free(tmp);
+ return l;
+ }
+
+#ifdef INET
+ if (type & RFC3361) {
+ if ((tmp = decode_rfc3361(dl, data)) == NULL)
+ return -1;
+ l = strlen(tmp);
+ l = print_string(s, len, l, (uint8_t *)tmp);
+ free(tmp);
+ return l;
+ }
+
+ if (type & RFC3442)
+ return decode_rfc3442(s, len, dl, data);
+
+ if (type & RFC5969)
+ return decode_rfc5969(s, len, dl, data);
+#endif
+
+ if (type & STRING) {
+ /* Some DHCP servers return NULL strings */
+ if (*data == '\0')
+ return 0;
+ return print_string(s, len, dl, data);
+ }
+
+ if (type & FLAG) {
+ if (s) {
+ *s++ = '1';
+ *s = '\0';
+ }
+ return 2;
+ }
+
+ if (!s) {
+ if (type & UINT8)
+ l = 3;
+ else if (type & UINT16) {
+ l = 5;
+ dl /= 2;
+ } else if (type & SINT16) {
+ l = 6;
+ dl /= 2;
+ } else if (type & UINT32) {
+ l = 10;
+ dl /= 4;
+ } else if (type & SINT32) {
+ l = 11;
+ dl /= 4;
+ } else if (type & ADDRIPV4) {
+ l = 16;
+ dl /= 4;
+ }
+#ifdef INET6
+ else if (type & ADDRIPV6) {
+ e = data + dl;
+ l = 0;
+ while (data < e) {
+ if (l)
+ l++; /* space */
+ dl = ipv6_printaddr(NULL, 0, data, ifname);
+ if (dl != -1)
+ l += dl;
+ data += 16;
+ }
+ return l + 1;
+ }
+#endif
+ else if (type & BINHEX) {
+ l = 2;
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+ return (l + 1) * dl;
+ }
+
+ t = data;
+ e = data + dl;
+ while (data < e) {
+ if (data != t && type != BINHEX) {
+ *s++ = ' ';
+ bytes++;
+ len--;
+ }
+ if (type & UINT8) {
+ l = snprintf(s, len, "%u", *data);
+ data++;
+ } else if (type & UINT16) {
+ memcpy(&u16, data, sizeof(u16));
+ u16 = ntohs(u16);
+ l = snprintf(s, len, "%u", u16);
+ data += sizeof(u16);
+ } else if (type & SINT16) {
+ memcpy(&s16, data, sizeof(s16));
+ s16 = ntohs(s16);
+ l = snprintf(s, len, "%d", s16);
+ data += sizeof(s16);
+ } else if (type & UINT32) {
+ memcpy(&u32, data, sizeof(u32));
+ u32 = ntohl(u32);
+ l = snprintf(s, len, "%u", u32);
+ data += sizeof(u32);
+ } else if (type & SINT32) {
+ memcpy(&s32, data, sizeof(s32));
+ s32 = ntohl(s32);
+ l = snprintf(s, len, "%d", s32);
+ data += sizeof(s32);
+ } else if (type & ADDRIPV4) {
+ memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
+ l = snprintf(s, len, "%s", inet_ntoa(addr));
+ data += sizeof(addr.s_addr);
+ }
+#ifdef INET6
+ else if (type & ADDRIPV6) {
+ dl = ipv6_printaddr(s, len, data, ifname);
+ if (dl != -1)
+ l = dl;
+ else
+ l = 0;
+ data += 16;
+ }
+#endif
+ else if (type & BINHEX) {
+ l = snprintf(s, len, "%.2x", data[0]);
+ data++;
+ } else
+ l = 0;
+ len -= l;
+ bytes += l;
+ s += l;
+ }
+
+ return bytes;
+}
+
+static size_t
+dhcp_envoption1(char **env, const char *prefix,
+ const struct dhcp_opt *opt, int vname, const uint8_t *od, int ol,
+ const char *ifname)
+{
+ ssize_t len;
+ size_t e;
+ char *v, *val;
+
+ if (opt->len && opt->len < ol)
+ ol = opt->len;
+ len = print_option(NULL, 0, opt->type, ol, od, ifname);
+ if (len < 0)
+ return 0;
+ if (vname)
+ e = strlen(opt->var) + 1;
+ else
+ e = 0;
+ if (prefix)
+ e += strlen(prefix);
+ e += len + 4;
+ if (env == NULL)
+ return e;
+ v = val = *env = malloc(e);
+ if (v == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return 0;
+ }
+ if (vname)
+ v += snprintf(val, e, "%s_%s=", prefix, opt->var);
+ else
+ v += snprintf(val, e, "%s=", prefix);
+ if (len != 0)
+ print_option(v, len, opt->type, ol, od, ifname);
+ return e;
+}
+
+ssize_t
+dhcp_envoption(char **env, const char *prefix,
+ const char *ifname, struct dhcp_opt *opt,
+ const uint8_t *(*dgetopt)(unsigned int *, unsigned int *, unsigned int *,
+ const uint8_t *, unsigned int, struct dhcp_opt **),
+ const uint8_t *od, int ol)
+{
+ ssize_t e, n;
+ size_t i;
+ unsigned int eoc, eos, eol;
+ const uint8_t *eod;
+ int ov;
+ struct dhcp_opt *eopt, *oopt;
+ char *pfx;
+
+ /* If no embedded or encapsulated options, it's easy */
+ if (opt->embopts_len == 0 && opt->encopts_len == 0) {
+ if (dhcp_envoption1(env == NULL ? NULL : &env[0],
+ prefix, opt, 1, od, ol, ifname))
+ return 1;
+ return 0;
+ }
+
+ /* Create a new prefix based on the option */
+ if (env) {
+ if (opt->type & INDEX) {
+ if (opt->index > 999) {
+ errno = ENOBUFS;
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return 0;
+ }
+ }
+ e = strlen(prefix) + strlen(opt->var) + 2 +
+ (opt->type & INDEX ? 3 : 0);
+ pfx = malloc(e);
+ if (pfx == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return 0;
+ }
+ if (opt->type & INDEX)
+ snprintf(pfx, e, "%s_%s%d", prefix,
+ opt->var, ++opt->index);
+ else
+ snprintf(pfx, e, "%s_%s", prefix, opt->var);
+ } else
+ pfx = NULL;
+
+ /* Embedded options are always processed first as that
+ * is a fixed layout */
+ n = 0;
+ for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) {
+ e = dhcp_optlen(eopt, ol);
+ if (e == 0)
+ /* Report error? */
+ return 0;
+ /* Use the option prefix if the embedded option
+ * name is different.
+ * This avoids new_fqdn_fqdn which would be silly. */
+ ov = strcmp(opt->var, eopt->var);
+ if (dhcp_envoption1(env == NULL ? NULL : &env[n],
+ pfx, eopt, ov, od, e, ifname))
+ n++;
+ od += e;
+ ol -= e;
+ }
+
+ /* Enumerate our encapsulated options */
+ if (opt->encopts_len && ol > 0) {
+ /* Zero any option indexes
+ * We assume that referenced encapsulated options are NEVER
+ * recursive as the index order could break. */
+ for (i = 0, eopt = opt->encopts;
+ i < opt->encopts_len;
+ i++, eopt++)
+ {
+ eoc = opt->option;
+ if (eopt->type & OPTION) {
+ dgetopt(NULL, &eoc, NULL, NULL, 0, &oopt);
+ if (oopt)
+ oopt->index = 0;
+ }
+ }
+
+ while ((eod = dgetopt(&eos, &eoc, &eol, od, ol, &oopt))) {
+ for (i = 0, eopt = opt->encopts;
+ i < opt->encopts_len;
+ i++, eopt++)
+ {
+ if (eopt->option == eoc) {
+ if (eopt->type & OPTION) {
+ if (oopt == NULL)
+ /* Report error? */
+ continue;
+ }
+ n += dhcp_envoption(
+ env == NULL ? NULL : &env[n], pfx,
+ ifname,
+ eopt->type & OPTION ? oopt : eopt,
+ dgetopt, eod, eol);
+ break;
+ }
+ }
+ od += eos + eol;
+ ol -= eos + eol;
+ }
+ }
+
+ if (env)
+ free(pfx);
+
+ /* Return number of options found */
+ return n;
+}
+
+void
+dhcp_zero_index(struct dhcp_opt *opt)
+{
+ size_t i;
+ struct dhcp_opt *o;
+
+ opt->index = 0;
+ for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)
+ dhcp_zero_index(o);
+ for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++)
+ dhcp_zero_index(o);
+}
diff --git a/dhcpcd/dhcp-common.h b/dhcpcd/dhcp-common.h
new file mode 100644
index 00000000..d9ccea9c
--- /dev/null
+++ b/dhcpcd/dhcp-common.h
@@ -0,0 +1,105 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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 DHCPCOMMON_H
+#define DHCPCOMMON_H
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <stdint.h>
+
+#include "common.h"
+
+/* Max MTU - defines dhcp option length */
+#define MTU_MAX 1500
+#define MTU_MIN 576
+
+#define REQUEST (1 << 0)
+#define UINT8 (1 << 1)
+#define UINT16 (1 << 2)
+#define SINT16 (1 << 3)
+#define UINT32 (1 << 4)
+#define SINT32 (1 << 5)
+#define ADDRIPV4 (1 << 6)
+#define STRING (1 << 7)
+#define ARRAY (1 << 8)
+#define RFC3361 (1 << 9)
+#define RFC3397 (1 << 10)
+#define RFC3442 (1 << 11)
+#define RFC5969 (1 << 12)
+#define ADDRIPV6 (1 << 13)
+#define BINHEX (1 << 14)
+#define FLAG (1 << 15)
+#define NOREQ (1 << 16)
+#define EMBED (1 << 17)
+#define ENCAP (1 << 18)
+#define INDEX (1 << 19)
+#define OPTION (1 << 20)
+
+struct dhcp_opt {
+ uint32_t option; /* Also used for IANA Enterpise Number */
+ int type;
+ int len;
+ char *var;
+
+ int index; /* Index counter for many instances of the same option */
+
+ /* Embedded options.
+ * The option code is irrelevant here. */
+ struct dhcp_opt *embopts;
+ size_t embopts_len;
+
+ /* Encapsulated options */
+ struct dhcp_opt *encopts;
+ size_t encopts_len;
+};
+
+/* DHCP Vendor-Identifying Vendor Options, RFC3925 */
+extern struct dhcp_opt *vivso;
+extern size_t vivso_len;
+
+struct dhcp_opt *vivso_find(uint16_t, const void *);
+
+#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(const struct dhcp_opt *, size_t,
+ uint8_t *, const char *, int);
+
+size_t encode_rfc1035(const char *src, uint8_t *dst);
+ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *);
+ssize_t print_string(char *, ssize_t, int, const uint8_t *);
+ssize_t print_option(char *, ssize_t, int, int, const uint8_t *, const char *);
+
+ssize_t dhcp_envoption(char **, const char *, const char *, struct dhcp_opt *,
+ const uint8_t *(*dgetopt)(unsigned int *, unsigned int *, unsigned int *,
+ const uint8_t *, unsigned int, struct dhcp_opt **),
+ const uint8_t *od, int ol);
+void dhcp_zero_index(struct dhcp_opt *);
+
+#endif
diff --git a/dhcpcd/dhcp.c b/dhcpcd/dhcp.c
new file mode 100644
index 00000000..1641f44c
--- /dev/null
+++ b/dhcpcd/dhcp.c
@@ -0,0 +1,2797 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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/socket.h>
+#include <sys/stat.h>
+
+#ifdef __linux__
+# include <asm/types.h> /* for systems with broken headers */
+# include <linux/rtnetlink.h>
+#endif
+
+#include <arpa/inet.h>
+#include <net/route.h>
+
+#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
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "arp.h"
+#include "common.h"
+#include "dhcp.h"
+#include "dhcpcd.h"
+#include "dhcp-common.h"
+#include "duid.h"
+#include "eloop.h"
+#include "ipv4.h"
+#include "ipv4ll.h"
+#include "script.h"
+
+#define DAD "Duplicate address detected"
+#define DHCP_MIN_LEASE 20
+
+static uint8_t *packet;
+
+/* 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 *opt_buffer;
+
+#define IPV4A ADDRIPV4 | ARRAY
+#define IPV4R ADDRIPV4 | REQUEST
+
+/* 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
+
+struct dhcp_op {
+ uint8_t value;
+ const char *name;
+};
+
+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 }
+};
+
+static const char * const dhcp_params[] = {
+ "ip_address",
+ "subnet_cidr",
+ "network_number",
+ "filename",
+ "server_name",
+ NULL
+};
+
+struct udp_dhcp_packet
+{
+ struct ip ip;
+ struct udphdr udp;
+ struct dhcp_message dhcp;
+};
+
+struct dhcp_opt *dhcp_opts = NULL;
+size_t dhcp_opts_len = 0;
+
+static const size_t udp_dhcp_len = sizeof(struct udp_dhcp_packet);
+
+static int dhcp_open(struct interface *);
+
+void
+dhcp_printoptions(void)
+{
+ const char * const *p;
+ size_t i;
+ const struct dhcp_opt *opt;
+
+ for (p = dhcp_params; *p; p++)
+ printf(" %s\n", *p);
+
+ for (i = 0, opt = dhcp_opts; i < dhcp_opts_len; i++, opt++)
+ printf("%03d %s\n", opt->option, opt->var);
+}
+
+#ifdef DEBUG_MEMORY
+static void
+dhcp_cleanup(void)
+{
+
+ free(packet);
+ free(opt_buffer);
+}
+#endif
+
+#define get_option_raw(dhcp, opt) get_option(dhcp, opt, NULL)
+static const uint8_t *
+get_option(const struct dhcp_message *dhcp, uint8_t opt, int *len)
+{
+ const uint8_t *p = dhcp->options;
+ const uint8_t *e = p + sizeof(dhcp->options);
+ uint8_t l, ol = 0;
+ uint8_t o = 0;
+ uint8_t overl = 0;
+ uint8_t *bp = NULL;
+ const uint8_t *op = NULL;
+ int bl = 0;
+
+ while (p < e) {
+ o = *p++;
+ if (o == opt) {
+ if (op) {
+ if (!opt_buffer) {
+ opt_buffer = malloc(sizeof(*dhcp));
+ if (opt_buffer == NULL)
+ return NULL;
+ }
+ if (!bp)
+ bp = opt_buffer;
+ memcpy(bp, op, ol);
+ bp += ol;
+ }
+ ol = *p;
+ if (p + ol > e) {
+ errno = EINVAL;
+ return NULL;
+ }
+ op = p + 1;
+ bl += ol;
+ }
+ switch (o) {
+ case DHO_PAD:
+ continue;
+ case DHO_END:
+ if (overl & 1) {
+ /* bit 1 set means parse boot file */
+ overl &= ~1;
+ p = dhcp->bootfile;
+ e = p + sizeof(dhcp->bootfile);
+ } else if (overl & 2) {
+ /* bit 2 set means parse server name */
+ overl &= ~2;
+ p = dhcp->servername;
+ e = p + sizeof(dhcp->servername);
+ } else
+ goto exit;
+ break;
+ case DHO_OPTIONSOVERLOADED:
+ /* Ensure we only get this option once */
+ if (!overl)
+ overl = p[1];
+ break;
+ }
+ l = *p++;
+ p += l;
+ }
+
+exit:
+ if (len)
+ *len = bl;
+ if (bp) {
+ memcpy(bp, op, ol);
+ return (const uint8_t *)opt_buffer;
+ }
+ if (op)
+ return op;
+ errno = ENOENT;
+ return NULL;
+}
+
+int
+get_option_addr(struct in_addr *a, const struct dhcp_message *dhcp,
+ uint8_t option)
+{
+ const uint8_t *p;
+ int len;
+
+ p = get_option(dhcp, option, &len);
+ if (!p || len < (ssize_t)sizeof(a->s_addr))
+ return -1;
+ memcpy(&a->s_addr, p, sizeof(a->s_addr));
+ return 0;
+}
+
+static int
+get_option_uint32(uint32_t *i, const struct dhcp_message *dhcp, uint8_t option)
+{
+ const uint8_t *p;
+ int len;
+ uint32_t d;
+
+ p = get_option(dhcp, option, &len);
+ if (!p || len < (ssize_t)sizeof(d))
+ return -1;
+ memcpy(&d, p, sizeof(d));
+ if (i)
+ *i = ntohl(d);
+ return 0;
+}
+
+static int
+get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option)
+{
+ const uint8_t *p;
+ int len;
+
+ p = get_option(dhcp, option, &len);
+ if (!p || len < (ssize_t)sizeof(*p))
+ return -1;
+ if (i)
+ *i = *(p);
+ return 0;
+}
+
+ssize_t
+decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p)
+{
+ const uint8_t *e;
+ ssize_t b, bytes = 0, ocets;
+ uint8_t cidr;
+ struct in_addr addr;
+ char *o = out;
+
+ /* Minimum is 5 -first is CIDR and a router length of 4 */
+ if (pl < 5) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ e = p + pl;
+ while (p < e) {
+ cidr = *p++;
+ if (cidr > 32) {
+ errno = EINVAL;
+ return -1;
+ }
+ ocets = (cidr + 7) / 8;
+ if (!out) {
+ p += 4 + ocets;
+ bytes += ((4 * 4) * 2) + 4;
+ continue;
+ }
+ if ((((4 * 4) * 2) + 4) > len) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ if (o != out) {
+ *o++ = ' ';
+ len--;
+ }
+ /* If we have ocets then we have a destination and netmask */
+ if (ocets > 0) {
+ addr.s_addr = 0;
+ memcpy(&addr.s_addr, p, ocets);
+ b = snprintf(o, len, "%s/%d", inet_ntoa(addr), cidr);
+ p += ocets;
+ } else
+ b = snprintf(o, len, "0.0.0.0/0");
+ o += b;
+ len -= b;
+
+ /* Finally, snag the router */
+ memcpy(&addr.s_addr, p, 4);
+ p += 4;
+ b = snprintf(o, len, " %s", inet_ntoa(addr));
+ o += b;
+ len -= b;
+ }
+
+ if (out)
+ return o - out;
+ return bytes;
+}
+
+static struct rt_head *
+decode_rfc3442_rt(int dl, const uint8_t *data)
+{
+ const uint8_t *p = data;
+ const uint8_t *e;
+ uint8_t cidr;
+ size_t ocets;
+ struct rt_head *routes;
+ struct rt *rt = NULL;
+
+ /* Minimum is 5 -first is CIDR and a router length of 4 */
+ if (dl < 5)
+ return NULL;
+
+ routes = malloc(sizeof(*routes));
+ TAILQ_INIT(routes);
+ e = p + dl;
+ while (p < e) {
+ cidr = *p++;
+ if (cidr > 32) {
+ ipv4_freeroutes(routes);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ rt = calloc(1, sizeof(*rt));
+ if (rt == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ ipv4_freeroutes(routes);
+ return NULL;
+ }
+ TAILQ_INSERT_TAIL(routes, rt, next);
+
+ ocets = (cidr + 7) / 8;
+ /* If we have ocets then we have a destination and netmask */
+ if (ocets > 0) {
+ memcpy(&rt->dest.s_addr, p, ocets);
+ p += ocets;
+ rt->net.s_addr = htonl(~0U << (32 - cidr));
+ }
+
+ /* Finally, snag the router */
+ memcpy(&rt->gate.s_addr, p, 4);
+ p += 4;
+ }
+ return routes;
+}
+
+char *
+decode_rfc3361(int dl, const uint8_t *data)
+{
+ uint8_t enc;
+ unsigned int l;
+ char *sip = NULL;
+ struct in_addr addr;
+ char *p;
+
+ if (dl < 2) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ enc = *data++;
+ dl--;
+ switch (enc) {
+ case 0:
+ if ((l = decode_rfc3397(NULL, 0, dl, data)) > 0) {
+ sip = malloc(l);
+ if (sip == NULL)
+ return 0;
+ decode_rfc3397(sip, l, dl, data);
+ }
+ break;
+ case 1:
+ if (dl == 0 || dl % 4 != 0) {
+ errno = EINVAL;
+ break;
+ }
+ addr.s_addr = INADDR_BROADCAST;
+ l = ((dl / sizeof(addr.s_addr)) * ((4 * 4) + 1)) + 1;
+ sip = p = malloc(l);
+ if (sip == NULL)
+ return 0;
+ while (dl != 0) {
+ memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
+ data += sizeof(addr.s_addr);
+ p += snprintf(p, l - (p - sip), "%s ", inet_ntoa(addr));
+ dl -= sizeof(addr.s_addr);
+ }
+ *--p = '\0';
+ break;
+ default:
+ errno = EINVAL;
+ return 0;
+ }
+
+ return sip;
+}
+
+/* Decode an RFC5969 6rd order option into a space
+ * separated string. Returns length of string (including
+ * terminating zero) or zero on error. */
+ssize_t
+decode_rfc5969(char *out, ssize_t len, int pl, const uint8_t *p)
+{
+ uint8_t ipv4masklen, ipv6prefixlen;
+ uint8_t ipv6prefix[16];
+ uint8_t br[4];
+ int i;
+ ssize_t b, bytes = 0;
+
+ if (pl < 22) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ ipv4masklen = *p++;
+ pl--;
+ ipv6prefixlen = *p++;
+ pl--;
+
+ for (i = 0; i < 16; i++) {
+ ipv6prefix[i] = *p++;
+ pl--;
+ }
+ if (out) {
+ b= snprintf(out, len,
+ "%d %d "
+ "%02x%02x:%02x%02x:"
+ "%02x%02x:%02x%02x:"
+ "%02x%02x:%02x%02x:"
+ "%02x%02x:%02x%02x",
+ ipv4masklen, ipv6prefixlen,
+ ipv6prefix[0], ipv6prefix[1], ipv6prefix[2], ipv6prefix[3],
+ ipv6prefix[4], ipv6prefix[5], ipv6prefix[6], ipv6prefix[7],
+ ipv6prefix[8], ipv6prefix[9], ipv6prefix[10],ipv6prefix[11],
+ ipv6prefix[12],ipv6prefix[13],ipv6prefix[14], ipv6prefix[15]
+ );
+
+ len -= b;
+ out += b;
+ bytes += b;
+ } else {
+ bytes += 16 * 2 + 8 + 2 + 1 + 2;
+ }
+
+ while (pl >= 4) {
+ br[0] = *p++;
+ br[1] = *p++;
+ br[2] = *p++;
+ br[3] = *p++;
+ pl -= 4;
+
+ if (out) {
+ b= snprintf(out, len, " %d.%d.%d.%d",
+ br[0], br[1], br[2], br[3]);
+ len -= b;
+ out += b;
+ bytes += b;
+ } else {
+ bytes += (4 * 4);
+ }
+ }
+
+ return bytes;
+}
+
+char *
+get_option_string(const struct dhcp_message *dhcp, uint8_t option)
+{
+ int len;
+ const uint8_t *p;
+ char *s;
+
+ p = get_option(dhcp, option, &len);
+ if (!p || len == 0 || *p == '\0')
+ return NULL;
+
+ s = malloc(sizeof(char) * (len + 1));
+ if (s) {
+ memcpy(s, p, len);
+ s[len] = '\0';
+ }
+ return s;
+}
+
+/* This calculates the netmask that we should use for static routes.
+ * This IS different from the calculation used to calculate the netmask
+ * for an interface address. */
+static uint32_t
+route_netmask(uint32_t ip_in)
+{
+ /* used to be unsigned long - check if error */
+ uint32_t p = ntohl(ip_in);
+ uint32_t t;
+
+ if (IN_CLASSA(p))
+ t = ~IN_CLASSA_NET;
+ else {
+ if (IN_CLASSB(p))
+ t = ~IN_CLASSB_NET;
+ else {
+ if (IN_CLASSC(p))
+ t = ~IN_CLASSC_NET;
+ else
+ t = 0;
+ }
+ }
+
+ while (t & p)
+ t >>= 1;
+
+ return (htonl(~t));
+}
+
+/* We need to obey routing options.
+ * If we have a CSR then we only use that.
+ * Otherwise we add static routes and then routers. */
+struct rt_head *
+get_option_routes(struct interface *ifp, const struct dhcp_message *dhcp)
+{
+ struct if_options *ifo = ifp->options;
+ const uint8_t *p;
+ const uint8_t *e;
+ struct rt_head *routes = NULL;
+ struct rt *route = NULL;
+ int len;
+ const char *csr = "";
+
+ /* If we have CSR's then we MUST use these only */
+ if (!has_option_mask(ifo->nomask, DHO_CSR))
+ p = get_option(dhcp, DHO_CSR, &len);
+ else
+ p = NULL;
+ /* Check for crappy MS option */
+ if (!p && !has_option_mask(ifo->nomask, DHO_MSCSR)) {
+ p = get_option(dhcp, DHO_MSCSR, &len);
+ if (p)
+ csr = "MS ";
+ }
+ if (p) {
+ routes = decode_rfc3442_rt(len, p);
+ if (routes) {
+ if (!(ifo->options & DHCPCD_CSR_WARNED)) {
+ syslog(LOG_DEBUG,
+ "%s: using %sClassless Static Routes",
+ ifp->name, csr);
+ ifo->options |= DHCPCD_CSR_WARNED;
+ }
+ return routes;
+ }
+ }
+
+ /* OK, get our static routes first. */
+ routes = malloc(sizeof(*routes));
+ if (routes == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+ TAILQ_INIT(routes);
+ if (!has_option_mask(ifo->nomask, DHO_STATICROUTE))
+ p = get_option(dhcp, DHO_STATICROUTE, &len);
+ else
+ p = NULL;
+ if (p) {
+ e = p + len;
+ while (p < e) {
+ route = calloc(1, sizeof(*route));
+ if (route == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ ipv4_freeroutes(routes);
+ return NULL;
+ }
+ memcpy(&route->dest.s_addr, p, 4);
+ p += 4;
+ memcpy(&route->gate.s_addr, p, 4);
+ p += 4;
+ route->net.s_addr = route_netmask(route->dest.s_addr);
+ TAILQ_INSERT_TAIL(routes, route, next);
+ }
+ }
+
+ /* Now grab our routers */
+ if (!has_option_mask(ifo->nomask, DHO_ROUTER))
+ p = get_option(dhcp, DHO_ROUTER, &len);
+ else
+ p = NULL;
+ if (p) {
+ e = p + len;
+ while (p < e) {
+ route = calloc(1, sizeof(*route));
+ if (route == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ ipv4_freeroutes(routes);
+ return NULL;
+ }
+ memcpy(&route->gate.s_addr, p, 4);
+ p += 4;
+ TAILQ_INSERT_TAIL(routes, route, next);
+ }
+ }
+
+ return routes;
+}
+
+#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,
+ uint8_t type)
+{
+ struct dhcp_message *dhcp;
+ uint8_t *m, *lp, *p, *auth;
+ uint8_t *n_params = NULL;
+ uint32_t ul;
+ uint16_t sz;
+ size_t len, i;
+ int auth_len;
+ const struct dhcp_opt *opt;
+ const struct if_options *ifo = iface->options;
+ const struct dhcp_state *state = D_CSTATE(iface);
+ const struct dhcp_lease *lease = &state->lease;
+ time_t up = uptime() - state->start_uptime;
+ const char *hostname;
+ const struct vivco *vivco;
+
+ dhcp = calloc(1, sizeof (*dhcp));
+ if (dhcp == NULL)
+ return -1;
+ m = (uint8_t *)dhcp;
+ p = dhcp->options;
+
+ if ((type == DHCP_INFORM || type == DHCP_RELEASE ||
+ (type == DHCP_REQUEST &&
+ state->net.s_addr == lease->net.s_addr &&
+ (state->new == NULL ||
+ state->new->cookie == htonl(MAGIC_COOKIE)))))
+ {
+ dhcp->ciaddr = state->addr.s_addr;
+ /* In-case we haven't actually configured the address yet */
+ if (type == DHCP_INFORM && state->addr.s_addr == 0)
+ dhcp->ciaddr = lease->addr.s_addr;
+ }
+
+ dhcp->op = DHCP_BOOTREQUEST;
+ dhcp->hwtype = iface->family;
+ switch (iface->family) {
+ case ARPHRD_ETHER:
+ case ARPHRD_IEEE802:
+ 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 = htonl(state->xid);
+ dhcp->cookie = htonl(MAGIC_COOKIE);
+
+ *p++ = DHO_MESSAGETYPE;
+ *p++ = 1;
+ *p++ = type;
+
+ if (state->clientid) {
+ *p++ = DHO_CLIENTID;
+ memcpy(p, state->clientid, state->clientid[0] + 1);
+ p += state->clientid[0] + 1;
+ }
+
+ if (lease->addr.s_addr && lease->cookie == htonl(MAGIC_COOKIE)) {
+ if (type == DHCP_DECLINE ||
+ (type == DHCP_REQUEST &&
+ lease->addr.s_addr != state->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) {
+ *p++ = DHO_MESSAGE;
+ len = strlen(DAD);
+ *p++ = len;
+ memcpy(p, DAD, len);
+ p += len;
+ }
+
+ if (type == DHCP_DISCOVER &&
+ !(options & DHCPCD_TEST) &&
+ has_option_mask(ifo->requestmask, DHO_RAPIDCOMMIT))
+ {
+ /* RFC 4039 Section 3 */
+ *p++ = DHO_RAPIDCOMMIT;
+ *p++ = 0;
+ }
+
+ if (type == DHCP_DISCOVER && ifo->options & DHCPCD_REQUEST)
+ PUTADDR(DHO_IPADDRESS, ifo->req_addr);
+
+ if (type == DHCP_DISCOVER ||
+ type == DHCP_INFORM ||
+ type == DHCP_REQUEST)
+ {
+ *p++ = DHO_MAXMESSAGESIZE;
+ *p++ = 2;
+ sz = get_mtu(iface->name);
+ 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 (ifo->userclass[0]) {
+ *p++ = DHO_USERCLASS;
+ memcpy(p, ifo->userclass, ifo->userclass[0] + 1);
+ p += ifo->userclass[0] + 1;
+ }
+
+ if (ifo->vendorclassid[0]) {
+ *p++ = DHO_VENDORCLASSID;
+ memcpy(p, ifo->vendorclassid,
+ ifo->vendorclassid[0] + 1);
+ p += ifo->vendorclassid[0] + 1;
+ }
+
+
+ if (type != DHCP_INFORM) {
+ if (ifo->leasetime != 0) {
+ *p++ = DHO_LEASETIME;
+ *p++ = 4;
+ ul = htonl(ifo->leasetime);
+ memcpy(p, &ul, 4);
+ p += 4;
+ }
+ }
+
+ if (ifo->hostname[0] == '\0')
+ hostname = get_hostname(ifo->options &
+ DHCPCD_HOSTNAME_SHORT ? 1 : 0);
+ else
+ hostname = ifo->hostname;
+ if (ifo->fqdn != FQDN_DISABLE) {
+ /* IETF DHC-FQDN option (81), RFC4702 */
+ *p++ = DHO_FQDN;
+ lp = p;
+ *p++ = 3;
+ /*
+ * Flags: 0000NEOS
+ * S: 1 => Client requests Server to update
+ * a RR in DNS as well as PTR
+ * O: 1 => Server indicates to client that
+ * DNS has been updated
+ * E: 1 => Name data is DNS format
+ * N: 1 => Client requests Server to not
+ * update DNS
+ */
+ if (hostname)
+ *p++ = (ifo->fqdn & 0x09) | 0x04;
+ else
+ *p++ = (FQDN_NONE & 0x09) | 0x04;
+ *p++ = 0; /* from server for PTR RR */
+ *p++ = 0; /* from server for A RR if S=1 */
+ if (hostname) {
+ ul = encode_rfc1035(hostname, p);
+ *lp += ul;
+ p += ul;
+ }
+ } else if (ifo->options & DHCPCD_HOSTNAME && hostname) {
+ *p++ = DHO_HOSTNAME;
+ len = strlen(hostname);
+ *p++ = len;
+ memcpy(p, hostname, len);
+ p += len;
+ }
+
+ /* vendor is already encoded correctly, so just add it */
+ if (ifo->vendor[0]) {
+ *p++ = DHO_VENDOR;
+ memcpy(p, ifo->vendor, ifo->vendor[0] + 1);
+ p += ifo->vendor[0] + 1;
+ }
+
+ if (ifo->vivco_len) {
+ *p++ = DHO_VIVCO;
+ lp = p++;
+ *lp = sizeof(ul);
+ ul = htonl(ifo->vivco_en);
+ memcpy(p, &ul, sizeof(ul));
+ p += sizeof(ul);
+ for (i = 0, vivco = ifo->vivco;
+ i < ifo->vivco_len;
+ i++, vivco++)
+ {
+ len = (p - m) + vivco->len + 1;
+ if (len > sizeof(*dhcp))
+ goto toobig;
+ if (vivco->len + 2 + *lp > 255) {
+ syslog(LOG_ERR,
+ "%s: VIVCO option too big",
+ iface->name);
+ free(dhcp);
+ return -1;
+ }
+ *p++ = (uint8_t)vivco->len;
+ memcpy(p, vivco->data, vivco->len);
+ p += vivco->len;
+ *lp += (uint8_t)vivco->len + 1;
+ }
+ }
+
+ len = (p - m) + 3;
+ if (len > sizeof(*dhcp))
+ goto toobig;
+ *p++ = DHO_PARAMETERREQUESTLIST;
+ n_params = p;
+ *p++ = 0;
+ for (i = 0, opt = dhcp_opts; i < dhcp_opts_len; i++, opt++) {
+ if (!(opt->type & REQUEST ||
+ has_option_mask(ifo->requestmask, opt->option)))
+ continue;
+ if (opt->type & NOREQ)
+ continue;
+ if (type == DHCP_INFORM &&
+ (opt->option == DHO_RENEWALTIME ||
+ opt->option == DHO_REBINDTIME))
+ continue;
+ len = (p - m) + 2;
+ if (len > sizeof(*dhcp))
+ goto toobig;
+ *p++ = opt->option;
+ }
+ *n_params = p - n_params - 1;
+ }
+
+ /* silence GCC */
+ auth_len = 0;
+ auth = NULL;
+
+ if (ifo->auth.options & DHCPCD_AUTH_SEND) {
+ auth_len = dhcp_auth_encode(&ifo->auth, state->auth.token,
+ NULL, 0, 4, type, NULL, 0);
+ if (auth_len > 0) {
+ len = (p + auth_len) - m;
+ if (auth_len > 255 || len > sizeof(*dhcp))
+ goto toobig;
+ *p++ = DHO_AUTHENTICATION;
+ *p++ = (uint8_t)auth_len;
+ auth = p;
+ p += auth_len;
+ } else if (auth_len == -1)
+ syslog(LOG_ERR, "%s: dhcp_auth_encode: %m",
+ iface->name);
+ }
+
+ *p++ = DHO_END;
+
+#ifdef BOOTP_MESSAGE_LENTH_MIN
+ /* Some crappy DHCP servers think they have to obey the BOOTP minimum
+ * message length.
+ * They are wrong, but we should still cater for them. */
+ while (p - m < BOOTP_MESSAGE_LENTH_MIN)
+ *p++ = DHO_PAD;
+#endif
+
+ len = p - m;
+ if (ifo->auth.options & DHCPCD_AUTH_SEND && auth_len > 0)
+ dhcp_auth_encode(&ifo->auth, state->auth.token,
+ m, len, 4, type, auth, auth_len);
+
+ *message = dhcp;
+ return len;
+
+toobig:
+ syslog(LOG_ERR, "%s: DHCP messge too big", iface->name);
+ free(dhcp);
+ return -1;
+}
+
+ssize_t
+write_lease(const struct interface *ifp, const struct dhcp_message *dhcp)
+{
+ int fd;
+ ssize_t bytes = sizeof(*dhcp);
+ const uint8_t *p = dhcp->options;
+ const uint8_t *e = p + sizeof(dhcp->options);
+ uint8_t l;
+ uint8_t o = 0;
+ const struct dhcp_state *state = D_CSTATE(ifp);
+
+ /* We don't write BOOTP leases */
+ if (is_bootp(dhcp)) {
+ unlink(state->leasefile);
+ return 0;
+ }
+
+ syslog(LOG_DEBUG, "%s: writing lease `%s'",
+ ifp->name, state->leasefile);
+
+ fd = open(state->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd == -1)
+ return -1;
+
+ /* Only write as much as we need */
+ while (p < e) {
+ o = *p;
+ if (o == DHO_END) {
+ bytes = p - (const uint8_t *)dhcp;
+ break;
+ }
+ p++;
+ if (o != DHO_PAD) {
+ l = *p++;
+ p += l;
+ }
+ }
+ bytes = write(fd, dhcp, bytes);
+ close(fd);
+ return bytes;
+}
+
+struct dhcp_message *
+read_lease(struct interface *ifp)
+{
+ int fd;
+ struct dhcp_message *dhcp;
+ struct dhcp_state *state = D_STATE(ifp);
+ ssize_t bytes;
+ const uint8_t *auth;
+ uint8_t type;
+ int auth_len;
+
+ fd = open(state->leasefile, O_RDONLY);
+ if (fd == -1) {
+ if (errno != ENOENT)
+ syslog(LOG_ERR, "%s: open `%s': %m",
+ ifp->name, state->leasefile);
+ return NULL;
+ }
+ syslog(LOG_DEBUG, "%s: reading lease `%s'",
+ ifp->name, state->leasefile);
+ dhcp = calloc(1, sizeof(*dhcp));
+ if (dhcp == NULL) {
+ close(fd);
+ return NULL;
+ }
+ bytes = read(fd, dhcp, sizeof(*dhcp));
+ close(fd);
+ if (bytes < 0) {
+ free(dhcp);
+ return NULL;
+ }
+
+ /* We may have found a BOOTP server */
+ if (get_option_uint8(&type, dhcp, DHO_MESSAGETYPE) == -1)
+ type = 0;
+ /* Authenticate the message */
+ auth = get_option(dhcp, DHO_AUTHENTICATION, &auth_len);
+ if (auth) {
+ if (dhcp_auth_validate(&state->auth, &ifp->options->auth,
+ (uint8_t *)dhcp, sizeof(*dhcp), 4, type,
+ auth, auth_len) == NULL)
+ {
+ syslog(LOG_DEBUG, "%s: dhcp_auth_validate: %m",
+ ifp->name);
+ free(dhcp);
+ return NULL;
+ }
+ syslog(LOG_DEBUG, "%s: validated using 0x%08" PRIu32,
+ ifp->name, state->auth.token->secretid);
+ }
+
+ return dhcp;
+}
+
+static const struct dhcp_opt *
+dhcp_getoverride(const struct if_options *ifo, uint16_t o)
+{
+ size_t i;
+ const struct dhcp_opt *opt;
+
+ for (i = 0, opt = ifo->dhcp_override;
+ i < ifo->dhcp_override_len;
+ i++, opt++)
+ {
+ if (opt->option == o)
+ return opt;
+ }
+ return NULL;
+}
+
+static const uint8_t *
+dhcp_getoption(unsigned int *os, unsigned int *code, unsigned int *len,
+ const uint8_t *od, unsigned int ol, struct dhcp_opt **oopt)
+{
+ size_t i;
+ struct dhcp_opt *opt;
+
+ if (od) {
+ if (ol < 2) {
+ errno = EINVAL;
+ return NULL;
+ }
+ *os = 2; /* code + len */
+ *code = (int)*od++;
+ *len = (int)*od++;
+ if (*len > ol) {
+ errno = EINVAL;
+ return NULL;
+ }
+ }
+
+ for (i = 0, opt = dhcp_opts; i < dhcp_opts_len; i++, opt++) {
+ if (opt->option == *code) {
+ *oopt = opt;
+ break;
+ }
+ }
+
+ return od;
+}
+
+ssize_t
+dhcp_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
+ const struct interface *ifp)
+{
+ const struct if_options *ifo;
+ const uint8_t *p;
+ int pl;
+ struct in_addr addr;
+ struct in_addr net;
+ struct in_addr brd;
+ struct dhcp_opt *opt, *vo;
+ ssize_t e = 0;
+ char **ep;
+ char cidr[4];
+ uint8_t overl = 0;
+ size_t i;
+ uint32_t en;
+
+ ifo = ifp->options;
+ get_option_uint8(&overl, dhcp, DHO_OPTIONSOVERLOADED);
+
+ if (!env) {
+ if (dhcp->yiaddr || dhcp->ciaddr)
+ e += 5;
+ if (*dhcp->bootfile && !(overl & 1))
+ e++;
+ if (*dhcp->servername && !(overl & 2))
+ e++;
+ for (i = 0, opt = dhcp_opts;
+ i < dhcp_opts_len;
+ i++, opt++)
+ {
+ if (has_option_mask(ifo->nomask, opt->option))
+ continue;
+ if (dhcp_getoverride(ifo, opt->option))
+ continue;
+ p = get_option(dhcp, opt->option, &pl);
+ if (!p)
+ continue;
+ e += dhcp_envoption(NULL, NULL, ifp->name,
+ opt, dhcp_getoption, p, pl);
+ }
+ for (i = 0, opt = ifo->dhcp_override;
+ i < ifo->dhcp_override_len;
+ i++, opt++)
+ {
+ if (has_option_mask(ifo->nomask, opt->option))
+ continue;
+ p = get_option(dhcp, opt->option, &pl);
+ if (!p)
+ continue;
+ e += dhcp_envoption(NULL, NULL, ifp->name,
+ opt, dhcp_getoption, p, pl);
+ }
+ return e;
+ }
+
+ ep = env;
+ 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 ? dhcp->yiaddr : dhcp->ciaddr;
+ setvar(&ep, prefix, "ip_address", inet_ntoa(addr));
+ if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1) {
+ net.s_addr = ipv4_getnetmask(addr.s_addr);
+ setvar(&ep, prefix, "subnet_mask", inet_ntoa(net));
+ }
+ snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net));
+ setvar(&ep, prefix, "subnet_cidr", cidr);
+ if (get_option_addr(&brd, dhcp, DHO_BROADCAST) == -1) {
+ brd.s_addr = addr.s_addr | ~net.s_addr;
+ setvar(&ep, prefix, "broadcast_address",
+ inet_ntoa(brd));
+ }
+ addr.s_addr = dhcp->yiaddr & net.s_addr;
+ setvar(&ep, prefix, "network_number", inet_ntoa(addr));
+ }
+
+ if (*dhcp->bootfile && !(overl & 1))
+ setvar(&ep, prefix, "filename", (const char *)dhcp->bootfile);
+ if (*dhcp->servername && !(overl & 2))
+ setvar(&ep, prefix, "server_name",
+ (const char *)dhcp->servername);
+
+ /* Zero our indexes */
+ if (env) {
+ for (i = 0, opt = dhcp_opts; i < dhcp_opts_len; i++, opt++)
+ dhcp_zero_index(opt);
+ for (i = 0, opt = ifp->options->dhcp_override;
+ i < ifp->options->dhcp_override_len;
+ i++, opt++)
+ dhcp_zero_index(opt);
+ for (i = 0, opt = vivso; i < vivso_len; i++, opt++)
+ dhcp_zero_index(opt);
+ }
+
+ for (i = 0, opt = dhcp_opts;
+ i < dhcp_opts_len;
+ i++, opt++)
+ {
+ if (has_option_mask(ifo->nomask, opt->option))
+ continue;
+ if (dhcp_getoverride(ifo, opt->option))
+ continue;
+ if ((p = get_option(dhcp, opt->option, &pl))) {
+ ep += dhcp_envoption(ep, prefix, ifp->name,
+ opt, dhcp_getoption, p, pl);
+ if (opt->option == DHO_VIVSO &&
+ pl > (int)sizeof(uint32_t))
+ {
+ memcpy(&en, p, sizeof(en));
+ en = ntohl(en);
+ vo = vivso_find(en, ifp);
+ if (vo) {
+ /* Skip over en + total size */
+ p += sizeof(en) + 1;
+ pl -= sizeof(en) + 1;
+ ep += dhcp_envoption(ep, prefix,
+ ifp->name,
+ vo, dhcp_getoption, p, pl);
+ }
+ }
+ }
+ }
+
+ for (i = 0, opt = ifo->dhcp_override;
+ i < ifo->dhcp_override_len;
+ i++, opt++)
+ {
+ if (has_option_mask(ifo->nomask, opt->option))
+ continue;
+ if ((p = get_option(dhcp, opt->option, &pl)))
+ ep += dhcp_envoption(ep, prefix, ifp->name,
+ opt, dhcp_getoption, p, pl);
+ }
+
+ 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 = ipv4_getnetmask(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;
+}
+
+static const char *
+get_dhcp_op(uint8_t type)
+{
+ const struct dhcp_op *d;
+
+ for (d = dhcp_ops; d->name; d++)
+ if (d->value == type)
+ return d->name;
+ return NULL;
+}
+
+static void
+dhcp_fallback(void *arg)
+{
+ struct interface *iface;
+
+ iface = (struct interface *)arg;
+ select_profile(iface, iface->options->fallback);
+ start_interface(iface);
+}
+
+uint32_t
+dhcp_xid(const struct interface *ifp)
+{
+ uint32_t xid;
+
+ if (ifp->options->options & DHCPCD_XID_HWADDR &&
+ ifp->hwlen >= sizeof(xid))
+ /* The lower bits are probably more unique on the network */
+ memcpy(&xid, (ifp->hwaddr + ifp->hwlen) - sizeof(xid),
+ sizeof(xid));
+ else
+ xid = arc4random();
+
+ return xid;
+}
+
+void
+dhcp_close(struct interface *ifp)
+{
+ struct dhcp_state *state = D_STATE(ifp);
+
+ if (state == NULL)
+ return;
+
+ if (state->arp_fd != -1) {
+ eloop_event_delete(state->arp_fd);
+ close(state->arp_fd);
+ state->arp_fd = -1;
+ }
+ if (state->raw_fd != -1) {
+ eloop_event_delete(state->raw_fd);
+ close(state->raw_fd);
+ state->raw_fd = -1;
+ }
+ if (state->udp_fd != -1) {
+ /* we don't listen to events on the udp */
+ close(state->udp_fd);
+ state->udp_fd = -1;
+ }
+
+ state->interval = 0;
+}
+
+static int
+dhcp_openudp(struct interface *iface)
+{
+ int s;
+ struct sockaddr_in sin;
+ int n;
+ struct dhcp_state *state;
+#ifdef SO_BINDTODEVICE
+ struct ifreq ifr;
+ char *p;
+#endif
+
+ if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+ return -1;
+
+ n = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)
+ goto eexit;
+#ifdef SO_BINDTODEVICE
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
+ /* 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
+ * receive buffer to 1 */
+ n = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1)
+ goto eexit;
+ state = D_STATE(iface);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(DHCP_CLIENT_PORT);
+ sin.sin_addr.s_addr = state->addr.s_addr;
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)
+ goto eexit;
+
+ state->udp_fd = s;
+ set_cloexec(s);
+ return 0;
+
+eexit:
+ close(s);
+ return -1;
+}
+
+static ssize_t
+dhcp_sendpacket(const struct interface *iface, struct in_addr to,
+ const uint8_t *data, ssize_t len)
+{
+ 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(D_CSTATE(iface)->udp_fd, data, len, 0,
+ (struct sockaddr *)&sin, sizeof(sin));
+}
+
+static uint16_t
+checksum(const void *data, uint16_t len)
+{
+ const uint8_t *addr = data;
+ uint32_t sum = 0;
+
+ while (len > 1) {
+ sum += addr[0] * 256 + addr[1];
+ addr += 2;
+ len -= 2;
+ }
+
+ if (len == 1)
+ sum += *addr * 256;
+
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += (sum >> 16);
+
+ sum = htons(sum);
+
+ return ~sum;
+}
+
+static ssize_t
+dhcp_makeudppacket(uint8_t **p, const uint8_t *data, size_t length,
+ struct in_addr source, struct in_addr dest)
+{
+ struct udp_dhcp_packet *udpp;
+ struct ip *ip;
+ struct udphdr *udp;
+
+ udpp = calloc(1, sizeof(*udpp));
+ if (udpp == NULL)
+ return -1;
+ ip = &udpp->ip;
+ udp = &udpp->udp;
+
+ /* OK, this is important :)
+ * We copy the data to our packet and then create a small part of the
+ * ip structure and an invalid ip_len (basically udp length).
+ * We then fill the udp structure and put the checksum
+ * of the whole packet into the udp checksum.
+ * Finally we complete the ip structure and ip checksum.
+ * If we don't do the ordering like so then the udp checksum will be
+ * broken, so find another way of doing it! */
+
+ memcpy(&udpp->dhcp, data, length);
+
+ ip->ip_p = IPPROTO_UDP;
+ ip->ip_src.s_addr = source.s_addr;
+ if (dest.s_addr == 0)
+ ip->ip_dst.s_addr = INADDR_BROADCAST;
+ else
+ ip->ip_dst.s_addr = dest.s_addr;
+
+ udp->uh_sport = htons(DHCP_CLIENT_PORT);
+ udp->uh_dport = htons(DHCP_SERVER_PORT);
+ udp->uh_ulen = htons(sizeof(*udp) + length);
+ ip->ip_len = udp->uh_ulen;
+ udp->uh_sum = checksum(udpp, sizeof(*udpp));
+
+ ip->ip_v = IPVERSION;
+ 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));
+
+ *p = (uint8_t *)udpp;
+ return sizeof(*ip) + sizeof(*udp) + length;
+}
+
+static void
+send_message(struct interface *iface, int type,
+ void (*callback)(void *))
+{
+ struct dhcp_state *state = D_STATE(iface);
+ struct if_options *ifo = iface->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);
+ timernorm(&tv);
+ syslog(LOG_DEBUG,
+ "%s: sending %s (xid 0x%x), next in %0.1f seconds",
+ iface->name, get_dhcp_op(type), state->xid,
+ timeval_to_double(&tv));
+ }
+
+ /* Ensure sockets are open. */
+ if (dhcp_open(iface) == -1) {
+ if (!(options & DHCPCD_TEST))
+ dhcp_drop(iface, "FAIL");
+ return;
+ }
+
+ /* 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 (state->udp_fd == -1 ||
+ (!(ifo->options & DHCPCD_INFORM) && is_bootp(state->new)))
+ {
+ a = state->addr.s_addr;
+ state->addr.s_addr = 0;
+ }
+ len = make_message(&dhcp, iface, type);
+ if (a)
+ state->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 = dhcp_sendpacket(iface, to, (uint8_t *)dhcp, len);
+ if (r == -1) {
+ syslog(LOG_ERR, "%s: dhcp_sendpacket: %m", iface->name);
+ dhcp_close(iface);
+ }
+ } else {
+ len = dhcp_makeudppacket(&udp, (uint8_t *)dhcp, len, from, to);
+ if (len == -1)
+ return;
+ r = ipv4_sendrawpacket(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: ipv4_sendrawpacket: %m",
+ iface->name);
+ if (!(options & DHCPCD_TEST))
+ dhcp_drop(iface, "FAIL");
+ dhcp_close(iface);
+ eloop_timeout_delete(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)
+ eloop_timeout_add_tv(&tv, callback, iface);
+}
+
+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
+dhcp_discover(void *arg)
+{
+ struct interface *iface = arg;
+ struct dhcp_state *state = D_STATE(iface);
+ struct if_options *ifo = iface->options;
+ int timeout = ifo->timeout;
+
+ /* If we're rebooting and we're not daemonised then we need
+ * to shorten the normal timeout to ensure we try correctly
+ * for a fallback or IPv4LL address. */
+ if (state->state == DHS_REBOOT && !(options & DHCPCD_DAEMONISED)) {
+ timeout -= ifo->reboot;
+ if (timeout <= 0)
+ timeout = 2;
+ }
+
+ state->state = DHS_DISCOVER;
+ state->xid = dhcp_xid(iface);
+ eloop_timeout_delete(NULL, iface);
+ if (ifo->fallback)
+ eloop_timeout_add_sec(timeout, dhcp_fallback, iface);
+ else if (ifo->options & DHCPCD_IPV4LL &&
+ !IN_LINKLOCAL(htonl(state->addr.s_addr)))
+ {
+ if (IN_LINKLOCAL(htonl(state->fail.s_addr)))
+ eloop_timeout_add_sec(RATE_LIMIT_INTERVAL,
+ ipv4ll_start, iface);
+ else
+ eloop_timeout_add_sec(timeout, ipv4ll_start, iface);
+ }
+ if (ifo->options & DHCPCD_REQUEST)
+ syslog(LOG_INFO, "%s: soliciting a DHCP lease (requesting %s)",
+ iface->name, inet_ntoa(ifo->req_addr));
+ else
+ syslog(LOG_INFO, "%s: soliciting a DHCP lease", iface->name);
+ send_discover(iface);
+}
+
+static void
+dhcp_request(void *arg)
+{
+ struct interface *ifp = arg;
+ struct dhcp_state *state = D_STATE(ifp);
+
+ state->state = DHS_REQUEST;
+ send_request(ifp);
+}
+
+static void
+dhcp_expire(void *arg)
+{
+ struct interface *ifp = arg;
+ struct dhcp_state *state = D_STATE(ifp);
+
+ syslog(LOG_ERR, "%s: DHCP lease expired", ifp->name);
+ eloop_timeout_delete(NULL, ifp);
+ dhcp_drop(ifp, "EXPIRE");
+ unlink(state->leasefile);
+
+ state->interval = 0;
+ dhcp_discover(ifp);
+}
+
+void
+dhcp_decline(struct interface *ifp)
+{
+
+ send_message(ifp, DHCP_DECLINE, NULL);
+}
+
+static void
+dhcp_renew(void *arg)
+{
+ struct interface *ifp = arg;
+ struct dhcp_state *state = D_STATE(ifp);
+ struct dhcp_lease *lease = &state->lease;
+
+ syslog(LOG_DEBUG, "%s: renewing lease of %s",
+ ifp->name, inet_ntoa(lease->addr));
+ syslog(LOG_DEBUG, "%s: rebind in %"PRIu32" seconds,"
+ " expire in %"PRIu32" seconds",
+ ifp->name, lease->rebindtime - lease->renewaltime,
+ lease->leasetime - lease->renewaltime);
+ state->state = DHS_RENEW;
+ state->xid = dhcp_xid(ifp);
+ send_renew(ifp);
+}
+
+static void
+dhcp_rebind(void *arg)
+{
+ struct interface *ifp = arg;
+ struct dhcp_state *state = D_STATE(ifp);
+ struct dhcp_lease *lease = &state->lease;
+
+ syslog(LOG_WARNING, "%s: failed to renew DHCP, rebinding",
+ ifp->name);
+ syslog(LOG_DEBUG, "%s: expire in %"PRIu32" seconds",
+ ifp->name, lease->leasetime - lease->rebindtime);
+ state->state = DHS_REBIND;
+ eloop_timeout_delete(send_renew, ifp);
+ state->lease.server.s_addr = 0;
+ ifp->options->options &= ~ DHCPCD_CSR_WARNED;
+ send_rebind(ifp);
+}
+
+void
+dhcp_bind(void *arg)
+{
+ struct interface *iface = arg;
+ struct dhcp_state *state = D_STATE(iface);
+ struct if_options *ifo = iface->options;
+ struct dhcp_lease *lease = &state->lease;
+ struct timeval tv;
+
+ /* We're binding an address now - ensure that sockets are closed */
+ dhcp_close(iface);
+ state->reason = 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/%d",
+ iface->name, inet_ntoa(lease->addr),
+ inet_ntocidr(lease->net));
+ lease->leasetime = ~0U;
+ 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 = state->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_WARNING,
+ "%s: rebind time greater than lease "
+ "time, forcing to %"PRIu32" 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_WARNING,
+ "%s: renewal time greater than rebind "
+ "time, forcing to %"PRIu32" seconds",
+ iface->name, lease->renewaltime);
+ }
+ syslog(lease->addr.s_addr == state->addr.s_addr ?
+ LOG_DEBUG : LOG_INFO,
+ "%s: leased %s for %"PRIu32" seconds", iface->name,
+ inet_ntoa(lease->addr), lease->leasetime);
+ }
+ }
+ if (options & DHCPCD_TEST) {
+ state->reason = "TEST";
+ script_runreason(iface, state->reason);
+ 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 {
+ eloop_timeout_add_sec(lease->renewaltime, dhcp_renew, iface);
+ eloop_timeout_add_sec(lease->rebindtime, dhcp_rebind, iface);
+ eloop_timeout_add_sec(lease->leasetime, dhcp_expire, iface);
+ syslog(LOG_DEBUG,
+ "%s: renew in %"PRIu32" seconds, rebind in %"PRIu32
+ " seconds",
+ iface->name, lease->renewaltime, lease->rebindtime);
+ }
+ ipv4_applyaddr(iface);
+ daemonise();
+ state->state = DHS_BOUND;
+ if (ifo->options & DHCPCD_ARP) {
+ state->claims = 0;
+ arp_announce(iface);
+ }
+}
+
+static void
+dhcp_timeout(void *arg)
+{
+ struct interface *ifp = arg;
+ struct dhcp_state *state = D_STATE(ifp);
+
+ dhcp_bind(ifp);
+ state->interval = 0;
+ dhcp_discover(ifp);
+}
+
+struct dhcp_message *
+dhcp_message_new(const struct in_addr *addr, const struct in_addr *mask)
+{
+ struct dhcp_message *dhcp;
+ uint8_t *p;
+
+ dhcp = calloc(1, sizeof(*dhcp));
+ if (dhcp == NULL)
+ return NULL;
+ 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 void
+dhcp_static(struct interface *ifp)
+{
+ struct if_options *ifo;
+ struct dhcp_state *state;
+
+ state = D_STATE(ifp);
+ ifo = ifp->options;
+ if (ifo->req_addr.s_addr == INADDR_ANY) {
+ syslog(LOG_INFO,
+ "%s: waiting for 3rd party to "
+ "configure IP address",
+ ifp->name);
+ state->reason = "3RDPARTY";
+ script_runreason(ifp, state->reason);
+ return;
+ }
+ state->offer = dhcp_message_new(&ifo->req_addr, &ifo->req_mask);
+ if (state->offer) {
+ eloop_timeout_delete(NULL, ifp);
+ dhcp_bind(ifp);
+ }
+}
+
+void
+dhcp_inform(struct interface *ifp)
+{
+ struct dhcp_state *state;
+ struct if_options *ifo;
+ struct ipv4_addr *ap;
+
+ state = D_STATE(ifp);
+ ifo = ifp->options;
+ if (options & DHCPCD_TEST) {
+ state->addr.s_addr = ifo->req_addr.s_addr;
+ state->net.s_addr = ifo->req_mask.s_addr;
+ } else {
+ if (ifo->req_addr.s_addr == INADDR_ANY) {
+ state = D_STATE(ifp);
+ ap = ipv4_findaddr(ifp, NULL, NULL);
+ if (ap == NULL) {
+ syslog(LOG_INFO,
+ "%s: waiting for 3rd party to "
+ "configure IP address",
+ ifp->name);
+ state->reason = "3RDPARTY";
+ script_runreason(ifp, state->reason);
+ return;
+ }
+ state->offer =
+ dhcp_message_new(&ap->addr, &ap->net);
+ } else
+ state->offer =
+ dhcp_message_new(&ifo->req_addr, &ifo->req_mask);
+ if (state->offer) {
+ ifo->options |= DHCPCD_STATIC;
+ dhcp_bind(ifp);
+ ifo->options &= ~DHCPCD_STATIC;
+ }
+ }
+
+ state->state = DHS_INFORM;
+ state->xid = dhcp_xid(ifp);
+ send_inform(ifp);
+}
+
+void
+dhcp_reboot_newopts(struct interface *ifp, int oldopts)
+{
+ struct if_options *ifo;
+ struct dhcp_state *state = D_STATE(ifp);
+
+ if (state == NULL)
+ return;
+ ifo = ifp->options;
+ if ((ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC) &&
+ state->addr.s_addr != ifo->req_addr.s_addr) ||
+ (oldopts & (DHCPCD_INFORM | DHCPCD_STATIC) &&
+ !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))))
+ {
+ dhcp_drop(ifp, "EXPIRE");
+ }
+}
+
+static void
+dhcp_reboot(struct interface *ifp)
+{
+ struct if_options *ifo;
+ struct dhcp_state *state = D_STATE(ifp);
+
+ if (state == NULL)
+ return;
+ ifo = ifp->options;
+ state->interval = 0;
+
+ if (ifo->options & DHCPCD_LINK && ifp->carrier == LINK_DOWN) {
+ syslog(LOG_INFO, "%s: waiting for carrier", ifp->name);
+ return;
+ }
+ if (ifo->options & DHCPCD_STATIC) {
+ dhcp_static(ifp);
+ return;
+ }
+ if (ifo->reboot == 0 || state->offer == NULL) {
+ dhcp_discover(ifp);
+ return;
+ }
+ if (ifo->options & DHCPCD_INFORM) {
+ syslog(LOG_INFO, "%s: informing address of %s",
+ ifp->name, inet_ntoa(state->lease.addr));
+ } else if (state->offer->cookie == 0) {
+ if (ifo->options & DHCPCD_IPV4LL) {
+ state->claims = 0;
+ arp_announce(ifp);
+ } else
+ dhcp_discover(ifp);
+ return;
+ } else {
+ syslog(LOG_INFO, "%s: rebinding lease of %s",
+ ifp->name, inet_ntoa(state->lease.addr));
+ }
+ state->state = DHS_REBOOT;
+ state->xid = dhcp_xid(ifp);
+ state->lease.server.s_addr = 0;
+ eloop_timeout_delete(NULL, ifp);
+ if (ifo->fallback)
+ eloop_timeout_add_sec(ifo->reboot, dhcp_fallback, ifp);
+ else if (ifo->options & DHCPCD_LASTLEASE && state->lease.frominfo)
+ eloop_timeout_add_sec(ifo->reboot, dhcp_timeout, ifp);
+ else if (!(ifo->options & DHCPCD_INFORM &&
+ options & (DHCPCD_MASTER | DHCPCD_DAEMONISED)))
+ eloop_timeout_add_sec(ifo->reboot, dhcp_expire, ifp);
+ /* Don't bother ARP checking as the server could NAK us first. */
+ if (ifo->options & DHCPCD_INFORM)
+ dhcp_inform(ifp);
+ else
+ dhcp_request(ifp);
+}
+
+void
+dhcp_drop(struct interface *ifp, const char *reason)
+{
+ struct dhcp_state *state;
+#ifdef RELEASE_SLOW
+ struct timespec ts;
+#endif
+
+ state = D_STATE(ifp);
+ if (state == NULL)
+ return;
+ eloop_timeouts_delete(ifp, dhcp_expire, NULL);
+ if (ifp->options->options & DHCPCD_RELEASE) {
+ unlink(state->leasefile);
+ if (ifp->carrier != LINK_DOWN &&
+ state->new != NULL &&
+ state->new->cookie == htonl(MAGIC_COOKIE))
+ {
+ syslog(LOG_INFO, "%s: releasing lease of %s",
+ ifp->name, inet_ntoa(state->lease.addr));
+ state->xid = dhcp_xid(ifp);
+ send_message(ifp, DHCP_RELEASE, NULL);
+#ifdef RELEASE_SLOW
+ /* Give the packet a chance to go */
+ ts.tv_sec = RELEASE_DELAY_S;
+ ts.tv_nsec = RELEASE_DELAY_NS;
+ nanosleep(&ts, NULL);
+#endif
+ }
+ }
+ free(state->old);
+ state->old = state->new;
+ state->new = NULL;
+ state->reason = reason;
+ ipv4_applyaddr(ifp);
+ free(state->old);
+ state->old = NULL;
+ state->lease.addr.s_addr = 0;
+ ifp->options->options &= ~ DHCPCD_CSR_WARNED;
+ state->auth.token = NULL;
+ state->auth.replay = 0;
+ free(state->auth.reconf);
+ state->auth.reconf = NULL;
+}
+
+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 = strdup(inet_ntoa(addr));
+ if (a == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return;
+ }
+ } 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 (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));
+ }
+ free(a);
+}
+
+static int
+blacklisted_ip(const struct if_options *ifo, in_addr_t addr)
+{
+ 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
+dhcp_handledhcp(struct interface *iface, struct dhcp_message **dhcpp,
+ const struct in_addr *from)
+{
+ struct dhcp_state *state = D_STATE(iface);
+ struct if_options *ifo = iface->options;
+ struct dhcp_message *dhcp = *dhcpp;
+ struct dhcp_lease *lease = &state->lease;
+ uint8_t type, tmp;
+ const uint8_t *auth;
+ struct in_addr addr;
+ size_t i;
+ int auth_len;
+
+ /* We may have found a BOOTP server */
+ if (get_option_uint8(&type, dhcp, DHO_MESSAGETYPE) == -1)
+ type = 0;
+
+ /* Authenticate the message */
+ auth = get_option(dhcp, DHO_AUTHENTICATION, &auth_len);
+ if (auth) {
+ if (dhcp_auth_validate(&state->auth, &ifo->auth,
+ (uint8_t *)*dhcpp, sizeof(**dhcpp), 4, type,
+ auth, auth_len) == NULL)
+ {
+ syslog(LOG_DEBUG, "%s: dhcp_auth_validate: %m",
+ iface->name);
+ log_dhcp(LOG_ERR, "authentication failed",
+ iface, dhcp, from);
+ return;
+ }
+ syslog(LOG_DEBUG, "%s: validated using 0x%08" PRIu32,
+ iface->name, state->auth.token->secretid);
+ } else if (ifo->auth.options & DHCPCD_AUTH_REQUIRE) {
+ log_dhcp(LOG_ERR, "missing authentiation", iface, dhcp, from);
+ return;
+ }
+
+ /* reset the message counter */
+ state->interval = 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;
+ }
+ /* We should restart on a NAK */
+ log_dhcp(LOG_WARNING, "NAK:", iface, dhcp, from);
+ if (!(options & DHCPCD_TEST)) {
+ dhcp_drop(iface, "NAK");
+ unlink(state->leasefile);
+ }
+ dhcp_close(iface);
+ /* If we constantly get NAKS then we should slowly back off */
+ eloop_timeout_add_sec(state->nakoff, dhcp_discover, iface);
+ if (state->nakoff == 0)
+ state->nakoff = 1;
+ else {
+ 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. */
+ if (type == 0 && i == DHO_SERVERID)
+ continue;
+ log_dhcp(LOG_WARNING, "reject DHCP", iface, dhcp, from);
+ return;
+ }
+ }
+
+ /* Ensure that the address offered is valid */
+ if ((type == 0 || type == DHCP_OFFER || type == DHCP_ACK) &&
+ (dhcp->ciaddr == INADDR_ANY || dhcp->ciaddr == INADDR_BROADCAST) &&
+ (dhcp->yiaddr == INADDR_ANY || dhcp->yiaddr == INADDR_BROADCAST))
+ {
+ log_dhcp(LOG_WARNING, "reject invalid address",
+ iface, dhcp, from);
+ return;
+ }
+
+ 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";
+ script_runreason(iface, state->reason);
+ exit(EXIT_SUCCESS);
+ }
+ eloop_timeout_delete(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. */
+ dhcp_request(iface);
+ return;
+ }
+ }
+
+ if (type) {
+ if (type == DHCP_OFFER) {
+ log_dhcp(LOG_WARNING, "ignoring offer of",
+ iface, dhcp, from);
+ return;
+ }
+
+ /* We should only be dealing with acks */
+ if (type != DHCP_ACK) {
+ log_dhcp(LOG_ERR, "not ACK or OFFER",
+ iface, dhcp, from);
+ return;
+ }
+
+ if (!(ifo->options & DHCPCD_INFORM))
+ log_dhcp(LOG_DEBUG, "acknowledged", iface, dhcp, from);
+ else
+ ifo->options &= ~DHCPCD_STATIC;
+ }
+
+
+ /* No NAK, so reset the backoff
+ * We don't reset on an OFFER message because the server could
+ * potentially NAK the REQUEST. */
+ state->nakoff = 0;
+
+ /* 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;
+ eloop_timeout_delete(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. */
+ dhcp_close(iface);
+
+ if (ifo->options & DHCPCD_ARP &&
+ state->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 (!ipv4_findaddr(iface, &addr, NULL)) {
+ state->claims = 0;
+ state->probes = 0;
+ state->conflicts = 0;
+ state->state = DHS_PROBE;
+ arp_probe(iface);
+ return;
+ }
+ }
+
+ dhcp_bind(iface);
+}
+
+static ssize_t
+get_udp_data(const uint8_t **data, const uint8_t *udp)
+{
+ struct udp_dhcp_packet p;
+
+ memcpy(&p, udp, sizeof(p));
+ *data = udp + offsetof(struct udp_dhcp_packet, dhcp);
+ return ntohs(p.ip.ip_len) - sizeof(p.ip) - sizeof(p.udp);
+}
+
+static int
+valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from,
+ int noudpcsum)
+{
+ struct udp_dhcp_packet p;
+ uint16_t bytes, udpsum;
+
+ if (data_len < sizeof(p.ip)) {
+ if (from)
+ from->s_addr = INADDR_ANY;
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy(&p, data, MIN(data_len, sizeof(p)));
+ if (from)
+ from->s_addr = p.ip.ip_src.s_addr;
+ if (data_len > sizeof(p)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (checksum(&p.ip, sizeof(p.ip)) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ bytes = ntohs(p.ip.ip_len);
+ if (data_len < bytes) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (noudpcsum == 0) {
+ udpsum = p.udp.uh_sum;
+ p.udp.uh_sum = 0;
+ p.ip.ip_hl = 0;
+ p.ip.ip_v = 0;
+ p.ip.ip_tos = 0;
+ p.ip.ip_len = p.udp.uh_ulen;
+ p.ip.ip_id = 0;
+ p.ip.ip_off = 0;
+ p.ip.ip_ttl = 0;
+ p.ip.ip_sum = 0;
+ if (udpsum && checksum(&p, bytes) != udpsum) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+dhcp_handlepacket(void *arg)
+{
+ struct interface *iface = arg;
+ struct dhcp_message *dhcp = NULL;
+ const uint8_t *pp;
+ ssize_t bytes;
+ struct in_addr from;
+ int i, partialcsum = 0;
+ const struct dhcp_state *state = D_CSTATE(iface);
+
+ /* 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. */
+ for(;;) {
+ bytes = ipv4_getrawpacket(iface, ETHERTYPE_IP,
+ packet, udp_dhcp_len, &partialcsum);
+ if (bytes == 0 || bytes == -1)
+ break;
+ if (valid_udp_packet(packet, bytes, &from, partialcsum) == -1) {
+ syslog(LOG_ERR, "%s: invalid UDP packet from %s",
+ iface->name, inet_ntoa(from));
+ continue;
+ }
+ i = whitelisted_ip(iface->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->options, from.s_addr) == 1)
+ {
+ syslog(LOG_WARNING,
+ "%s: blacklisted DHCP packet from %s",
+ iface->name, inet_ntoa(from));
+ continue;
+ }
+ if (iface->flags & IFF_POINTOPOINT &&
+ state->dst.s_addr != from.s_addr)
+ {
+ syslog(LOG_WARNING,
+ "%s: server %s is not destination",
+ iface->name, inet_ntoa(from));
+ }
+ 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 == NULL) {
+ dhcp = calloc(1, sizeof(*dhcp));
+ if (dhcp == NULL) {
+ syslog(LOG_ERR, "%s: calloc: %m", __func__);
+ break;
+ }
+ }
+ 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 (state->xid != ntohl(dhcp->xid)) {
+ syslog(LOG_DEBUG,
+ "%s: wrong xid 0x%x (expecting 0x%x) from %s",
+ iface->name, ntohl(dhcp->xid), state->xid,
+ inet_ntoa(from));
+ continue;
+ }
+ /* 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, ntohl(dhcp->xid),
+ hwaddr_ntoa(dhcp->chaddr, sizeof(dhcp->chaddr)));
+ continue;
+ }
+ dhcp_handledhcp(iface, &dhcp, &from);
+ if (state->raw_fd == -1)
+ break;
+ }
+ free(dhcp);
+}
+
+static int
+dhcp_open(struct interface *ifp)
+{
+ struct dhcp_state *state;
+
+ if (packet == NULL) {
+ packet = malloc(udp_dhcp_len);
+ if (packet == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return -1;
+ }
+#ifdef DEBUG_MEMORY
+ atexit(dhcp_cleanup);
+#endif
+ }
+
+ state = D_STATE(ifp);
+ if (state->raw_fd == -1) {
+ state->raw_fd = ipv4_opensocket(ifp, ETHERTYPE_IP);
+ if (state->raw_fd == -1) {
+ syslog(LOG_ERR, "%s: %s: %m", __func__, ifp->name);
+ return -1;
+ }
+ eloop_event_add(state->raw_fd, dhcp_handlepacket, ifp);
+ }
+ if (state->udp_fd == -1 &&
+ state->addr.s_addr != 0 &&
+ state->new != NULL &&
+ (state->new->cookie == htonl(MAGIC_COOKIE) ||
+ ifp->options->options & DHCPCD_INFORM))
+ {
+ if (dhcp_openudp(ifp) == -1 && errno != EADDRINUSE) {
+ syslog(LOG_ERR, "%s: dhcp_openudp: %m", ifp->name);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
+dhcp_dump(const char *ifname)
+{
+ struct interface *ifp;
+ struct dhcp_state *state;
+
+ ifaces = malloc(sizeof(*ifaces));
+ if (ifaces == NULL)
+ goto eexit;
+ TAILQ_INIT(ifaces);
+ ifp = calloc(1, sizeof(*ifp));
+ if (ifp == NULL)
+ goto eexit;
+ TAILQ_INSERT_HEAD(ifaces, ifp, next);
+ ifp->if_data[IF_DATA_DHCP] = state = calloc(1, sizeof(*state));
+ if (state == NULL)
+ goto eexit;
+ ifp->options = calloc(1, sizeof(*ifp->options));
+ if (ifp->options == NULL)
+ goto eexit;
+ strlcpy(ifp->name, ifname, sizeof(ifp->name));
+ snprintf(state->leasefile, sizeof(state->leasefile),
+ LEASEFILE, ifp->name);
+ strlcpy(ifp->options->script, if_options->script,
+ sizeof(ifp->options->script));
+ state->new = read_lease(ifp);
+ if (state->new == NULL && errno == ENOENT) {
+ strlcpy(state->leasefile, ifname, sizeof(state->leasefile));
+ state->new = read_lease(ifp);
+ }
+ if (state->new == NULL) {
+ if (errno == ENOENT)
+ syslog(LOG_ERR, "%s: no lease to dump", ifname);
+ return -1;
+ }
+ state->reason = "DUMP";
+ return script_runreason(ifp, state->reason);
+
+eexit:
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return -1;
+}
+
+void
+dhcp_free(struct interface *ifp)
+{
+ struct dhcp_state *state = D_STATE(ifp);
+
+ if (state) {
+ free(state->old);
+ free(state->new);
+ free(state->offer);
+ free(state->buffer);
+ free(state->clientid);
+ free(state);
+ ifp->if_data[IF_DATA_DHCP] = NULL;
+ }
+}
+
+static int
+dhcp_init(struct interface *ifp)
+{
+ struct dhcp_state *state;
+ const struct if_options *ifo;
+ size_t len;
+
+ state = D_STATE(ifp);
+ if (state == NULL) {
+ ifp->if_data[IF_DATA_DHCP] = calloc(1, sizeof(*state));
+ state = D_STATE(ifp);
+ if (state == NULL)
+ return -1;
+ /* 0 is a valid fd, so init to -1 */
+ state->raw_fd = state->udp_fd = state->arp_fd = -1;
+ }
+
+ state->state = DHS_INIT;
+ state->reason = "PREINIT";
+ state->nakoff = 0;
+ snprintf(state->leasefile, sizeof(state->leasefile),
+ LEASEFILE, ifp->name);
+
+ ifo = ifp->options;
+ /* We need to drop the leasefile so that start_interface
+ * doesn't load it. */
+ if (ifo->options & DHCPCD_REQUEST)
+ unlink(state->leasefile);
+
+ free(state->clientid);
+ state->clientid = NULL;
+
+ if (*ifo->clientid) {
+ state->clientid = malloc(ifo->clientid[0] + 1);
+ if (state->clientid == NULL)
+ goto eexit;
+ memcpy(state->clientid, ifo->clientid, ifo->clientid[0] + 1);
+ } else if (ifo->options & DHCPCD_CLIENTID) {
+ if (ifo->options & DHCPCD_DUID) {
+ state->clientid = malloc(duid_len + 6);
+ if (state->clientid == NULL)
+ goto eexit;
+ state->clientid[0] = duid_len + 5;
+ state->clientid[1] = 255; /* RFC 4361 */
+ memcpy(state->clientid + 2, ifo->iaid, 4);
+ memcpy(state->clientid + 6, duid, duid_len);
+ } else {
+ len = ifp->hwlen + 1;
+ state->clientid = malloc(len + 1);
+ if (state->clientid == NULL)
+ goto eexit;
+ state->clientid[0] = len;
+ state->clientid[1] = ifp->family;
+ memcpy(state->clientid + 2, ifp->hwaddr,
+ ifp->hwlen);
+ }
+ }
+
+ if (ifo->options & DHCPCD_DUID)
+ /* Don't bother logging as DUID and IAID are reported
+ * at device start. */
+ return 0;
+
+ if (ifo->options & DHCPCD_CLIENTID)
+ syslog(LOG_DEBUG, "%s: using ClientID %s", ifp->name,
+ hwaddr_ntoa(state->clientid + 1, state->clientid[0]));
+ else if (ifp->hwlen)
+ syslog(LOG_DEBUG, "%s: using hwaddr %s", ifp->name,
+ hwaddr_ntoa(ifp->hwaddr, ifp->hwlen));
+ return 0;
+
+eexit:
+ syslog(LOG_ERR, "%s: Error making ClientID: %m", __func__);
+ return -1;
+}
+
+void
+dhcp_start(struct interface *ifp)
+{
+ struct if_options *ifo = ifp->options;
+ struct dhcp_state *state;
+ struct stat st;
+ struct timeval now;
+ uint32_t l;
+ int nolease;
+
+ if (!(ifo->options & DHCPCD_IPV4))
+ return;
+
+ if (dhcp_init(ifp) == -1) {
+ syslog(LOG_ERR, "%s: dhcp_init: %m", ifp->name);
+ return;
+ }
+
+ /* Close any pre-existing sockets as we're starting over */
+ dhcp_close(ifp);
+
+ state = D_STATE(ifp);
+ state->start_uptime = uptime();
+ free(state->offer);
+ state->offer = NULL;
+
+ if (state->arping_index < ifo->arping_len) {
+ arp_start(ifp);
+ return;
+ }
+
+ if (ifo->options & DHCPCD_STATIC) {
+ dhcp_static(ifp);
+ return;
+ }
+
+ if (ifo->options & DHCPCD_DHCP && dhcp_open(ifp) == -1)
+ return;
+
+ if (ifo->options & DHCPCD_INFORM) {
+ dhcp_inform(ifp);
+ return;
+ }
+ if (ifp->hwlen == 0 && ifo->clientid[0] == '\0') {
+ syslog(LOG_WARNING, "%s: needs a clientid to configure",
+ ifp->name);
+ dhcp_drop(ifp, "FAIL");
+ dhcp_close(ifp);
+ eloop_timeout_delete(NULL, ifp);
+ return;
+ }
+ /* We don't want to read the old lease if we NAK an old test */
+ nolease = state->offer && options & DHCPCD_TEST;
+ if (!nolease)
+ state->offer = read_lease(ifp);
+ if (state->offer) {
+ get_lease(&state->lease, state->offer);
+ state->lease.frominfo = 1;
+ if (state->offer->cookie == 0) {
+ if (state->offer->yiaddr == state->addr.s_addr) {
+ free(state->offer);
+ state->offer = NULL;
+ }
+ } else if (state->lease.leasetime != ~0U &&
+ stat(state->leasefile, &st) == 0)
+ {
+ /* Offset lease times and check expiry */
+ gettimeofday(&now, NULL);
+ if ((time_t)state->lease.leasetime <
+ now.tv_sec - st.st_mtime)
+ {
+ syslog(LOG_DEBUG,
+ "%s: discarding expired lease",
+ ifp->name);
+ free(state->offer);
+ state->offer = NULL;
+ state->lease.addr.s_addr = 0;
+ } else {
+ l = now.tv_sec - st.st_mtime;
+ state->lease.leasetime -= l;
+ state->lease.renewaltime -= l;
+ state->lease.rebindtime -= l;
+ }
+ }
+ }
+
+ if (!(ifo->options & DHCPCD_DHCP)) {
+ if (ifo->options & DHCPCD_IPV4LL) {
+ if (state->offer && state->offer->cookie != 0) {
+ free(state->offer);
+ state->offer = NULL;
+ }
+ ipv4ll_start(ifp);
+ }
+ return;
+ }
+
+ if (state->offer == NULL)
+ dhcp_discover(ifp);
+ else if (state->offer->cookie == 0 && ifo->options & DHCPCD_IPV4LL)
+ ipv4ll_start(ifp);
+ else
+ dhcp_reboot(ifp);
+}
+
+void
+dhcp_handleifa(int type, struct interface *ifp,
+ const struct in_addr *addr,
+ const struct in_addr *net,
+ const struct in_addr *dst)
+{
+ struct dhcp_state *state;
+ struct if_options *ifo;
+ int i;
+
+ state = D_STATE(ifp);
+ if (state == NULL)
+ return;
+
+ if (type == RTM_DELADDR) {
+ if (state->new &&
+ (state->new->yiaddr == addr->s_addr ||
+ (state->new->yiaddr == INADDR_ANY &&
+ state->new->ciaddr == addr->s_addr)))
+ {
+ syslog(LOG_INFO, "%s: removing IP address %s/%d",
+ ifp->name, inet_ntoa(state->lease.addr),
+ inet_ntocidr(state->lease.net));
+ dhcp_drop(ifp, "EXPIRE");
+ }
+ return;
+ }
+
+ if (type != RTM_NEWADDR)
+ return;
+
+ ifo = ifp->options;
+ if (ifo->options & DHCPCD_INFORM) {
+ if (state->state != DHS_INFORM)
+ dhcp_inform(ifp);
+ return;
+ }
+
+ if (!(ifo->options & DHCPCD_STATIC))
+ return;
+ if (ifo->req_addr.s_addr != INADDR_ANY)
+ return;
+
+ free(state->old);
+ state->old = state->new;
+ state->new = dhcp_message_new(addr, net);
+ if (state->new == NULL)
+ return;
+ state->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(state->new, i, *dst);
+ }
+ state->reason = "STATIC";
+ ipv4_buildroutes();
+ script_runreason(ifp, state->reason);
+ if (ifo->options & DHCPCD_INFORM) {
+ state->state = DHS_INFORM;
+ state->xid = dhcp_xid(ifp);
+ state->lease.server.s_addr = dst ? dst->s_addr : INADDR_ANY;
+ state->addr = *addr;
+ state->net = *net;
+ dhcp_inform(ifp);
+ }
+}
diff --git a/dhcpcd/dhcp.h b/dhcpcd/dhcp.h
new file mode 100644
index 00000000..b1777f85
--- /dev/null
+++ b/dhcpcd/dhcp.h
@@ -0,0 +1,302 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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 DHCP_H
+#define DHCP_H
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <limits.h>
+#include <stdint.h>
+
+#include "auth.h"
+#include "dhcp-common.h"
+
+/* UDP port numbers for DHCP */
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+
+#define MAGIC_COOKIE 0x63825363
+#define BROADCAST_FLAG 0x8000
+
+/* DHCP message OP code */
+#define DHCP_BOOTREQUEST 1
+#define DHCP_BOOTREPLY 2
+
+/* DHCP message type */
+#define DHCP_DISCOVER 1
+#define DHCP_OFFER 2
+#define DHCP_REQUEST 3
+#define DHCP_DECLINE 4
+#define DHCP_ACK 5
+#define DHCP_NAK 6
+#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 2
+
+/* 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 {
+ DHO_PAD = 0,
+ DHO_SUBNETMASK = 1,
+ DHO_ROUTER = 3,
+ DHO_DNSSERVER = 6,
+ DHO_HOSTNAME = 12,
+ DHO_DNSDOMAIN = 15,
+ DHO_MTU = 26,
+ DHO_BROADCAST = 28,
+ DHO_STATICROUTE = 33,
+ DHO_NISDOMAIN = 40,
+ DHO_NISSERVER = 41,
+ DHO_NTPSERVER = 42,
+ DHO_VENDOR = 43,
+ DHO_IPADDRESS = 50,
+ DHO_LEASETIME = 51,
+ DHO_OPTIONSOVERLOADED = 52,
+ DHO_MESSAGETYPE = 53,
+ DHO_SERVERID = 54,
+ DHO_PARAMETERREQUESTLIST = 55,
+ DHO_MESSAGE = 56,
+ DHO_MAXMESSAGESIZE = 57,
+ DHO_RENEWALTIME = 58,
+ DHO_REBINDTIME = 59,
+ DHO_VENDORCLASSID = 60,
+ DHO_CLIENTID = 61,
+ DHO_USERCLASS = 77, /* RFC 3004 */
+ DHO_RAPIDCOMMIT = 80, /* RFC 4039 */
+ DHO_FQDN = 81,
+ DHO_AUTHENTICATION = 90, /* RFC 3118 */
+ DHO_VIVCO = 124, /* RFC 3925 */
+ DHO_VIVSO = 125, /* RFC 3925 */
+ DHO_DNSSEARCH = 119, /* RFC 3397 */
+ DHO_CSR = 121, /* RFC 3442 */
+ DHO_SIXRD = 212, /* RFC 5969 */
+ DHO_MSCSR = 249, /* MS code for RFC 3442 */
+ DHO_END = 255
+};
+
+/* FQDN values - lsnybble used in flags
+ * hsnybble to create order
+ * and to allow 0x00 to mean disable
+ */
+enum FQDN {
+ FQDN_DISABLE = 0x00,
+ FQDN_NONE = 0x18,
+ FQDN_PTR = 0x20,
+ FQDN_BOTH = 0x31
+};
+
+/* Sizes for DHCP options */
+#define DHCP_CHADDR_LEN 16
+#define SERVERNAME_LEN 64
+#define BOOTFILE_LEN 128
+#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
+
+/* Don't import common.h as that defines __unused which causes problems
+ * on some Linux systems which define it as part of a structure */
+#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
+# ifndef __packed
+# define __packed __attribute__((__packed__))
+# endif
+#else
+# ifndef __packed
+# define __packed
+# endif
+#endif
+
+struct dhcp_message {
+ uint8_t op; /* message type */
+ uint8_t hwtype; /* hardware address type */
+ uint8_t hwlen; /* hardware address length */
+ uint8_t hwopcount; /* should be zero in client message */
+ uint32_t xid; /* transaction id */
+ uint16_t secs; /* elapsed time in sec. from boot */
+ uint16_t flags;
+ uint32_t ciaddr; /* (previously allocated) client IP */
+ uint32_t yiaddr; /* 'your' client IP address */
+ uint32_t siaddr; /* should be zero in client's messages */
+ uint32_t giaddr; /* should be zero in client's messages */
+ uint8_t chaddr[DHCP_CHADDR_LEN]; /* client's hardware address */
+ uint8_t servername[SERVERNAME_LEN]; /* server host name */
+ 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;
+ struct in_addr server;
+ time_t leasedfrom;
+ struct timeval boundtime;
+ uint8_t frominfo;
+ uint32_t cookie;
+};
+
+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
+};
+
+struct dhcp_state {
+ enum DHS state;
+ 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;
+
+ 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 authstate auth;
+};
+
+#define D_STATE(ifp) \
+ ((struct dhcp_state *)(ifp)->if_data[IF_DATA_DHCP])
+#define D_CSTATE(ifp) \
+ ((const struct dhcp_state *)(ifp)->if_data[IF_DATA_DHCP])
+
+#include "dhcpcd.h"
+#include "if-options.h"
+#include "net.h"
+
+#ifdef INET
+extern struct dhcp_opt *dhcp_opts;
+extern size_t dhcp_opts_len;
+
+char *decode_rfc3361(int dl, const uint8_t *data);
+ssize_t decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p);
+ssize_t decode_rfc5969(char *out, ssize_t len, int pl, const uint8_t *p);
+
+void dhcp_printoptions(void);
+char *get_option_string(const struct dhcp_message *, uint8_t);
+int get_option_addr(struct in_addr *, const struct dhcp_message *, uint8_t);
+#define is_bootp(m) (m && \
+ !IN_LINKLOCAL(htonl((m)->yiaddr)) && \
+ get_option_uint8(NULL, m, DHO_MESSAGETYPE) == -1)
+struct rt_head *get_option_routes(struct interface *,
+ const struct dhcp_message *);
+ssize_t dhcp_env(char **, const char *, const struct dhcp_message *,
+ const struct interface *);
+
+uint32_t dhcp_xid(const struct interface *);
+struct dhcp_message *dhcp_message_new(const struct in_addr *addr,
+ const struct in_addr *mask);
+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(struct interface *);
+void get_lease(struct dhcp_lease *, const struct dhcp_message *);
+
+void dhcp_handleifa(int, struct interface *,
+ const struct in_addr *, const struct in_addr *, const struct in_addr *);
+
+void dhcp_drop(struct interface *, const char *);
+void dhcp_start(struct interface *);
+void dhcp_stop(struct interface *);
+void dhcp_decline(struct interface *);
+void dhcp_discover(void *);
+void dhcp_inform(struct interface *);
+void dhcp_bind(void *);
+void dhcp_reboot_newopts(struct interface *, int);
+void dhcp_close(struct interface *);
+void dhcp_free(struct interface *);
+int dhcp_dump(const char *);
+#else
+#define dhcp_printoptions
+#define dhcp_drop(a, b)
+#define dhcp_start(a) {}
+#define dhcp_reboot(a, b) b = b
+#define dhcp_reboot_newopts(a, b)
+#define dhcp_close(a)
+#define dhcp_free(a)
+#define dhcp_dump(a) -1
+#endif
+
+#endif
diff --git a/dhcpcd/dhcp6.c b/dhcpcd/dhcp6.c
new file mode 100644
index 00000000..ec585883
--- /dev/null
+++ b/dhcpcd/dhcp6.c
@@ -0,0 +1,2811 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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.
+ */
+
+/* TODO: We should decline dupliate addresses detected */
+
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+#include <netinet/in.h>
+#ifdef __linux__
+# define _LINUX_IN6_H
+# include <linux/ipv6.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#define ELOOP_QUEUE 3
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "dhcp6.h"
+#include "duid.h"
+#include "eloop.h"
+#include "ipv6nd.h"
+#include "platform.h"
+#include "script.h"
+
+#ifndef __UNCONST
+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif
+
+/* DHCPCD Project has been assigned an IANA PEN of 40712 */
+#define DHCPCD_IANA_PEN 40712
+
+/* Unsure if I want this */
+//#define VENDOR_SPLIT
+
+static int sock = -1;
+static const struct in6_addr in6addr_linklocal_alldhcp =
+ IN6ADDR_LINKLOCAL_ALLDHCP_INIT;
+static struct sockaddr_in6 from;
+static struct msghdr sndhdr;
+static struct iovec sndiov[2];
+static unsigned char *sndbuf;
+static struct msghdr rcvhdr;
+static struct iovec rcviov[2];
+static unsigned char *rcvbuf;
+static unsigned char ansbuf[1500];
+static char ntopbuf[INET6_ADDRSTRLEN];
+static char *status;
+static size_t status_len;
+
+struct dhcp6_op {
+ uint16_t type;
+ const char *name;
+};
+
+static const struct dhcp6_op dhcp6_ops[] = {
+ { DHCP6_SOLICIT, "SOLICIT6" },
+ { DHCP6_ADVERTISE, "ADVERTISE6" },
+ { DHCP6_REQUEST, "REQUEST6" },
+ { DHCP6_REPLY, "REPLY6" },
+ { DHCP6_RENEW, "RENEW6" },
+ { DHCP6_REBIND, "REBIND6" },
+ { DHCP6_CONFIRM, "CONFIRM6" },
+ { DHCP6_INFORMATION_REQ, "INFORM6" },
+ { DHCP6_RELEASE, "RELEASE6" },
+ { 0, NULL }
+};
+
+struct dhcp_opt *dhcp6_opts = NULL;
+size_t dhcp6_opts_len = 0;
+
+struct dhcp_compat {
+ uint8_t dhcp_opt;
+ uint16_t dhcp6_opt;
+};
+
+const struct dhcp_compat dhcp_compats[] = {
+ { DHO_DNSSERVER, D6_OPTION_DNS_SERVERS },
+ { DHO_HOSTNAME, D6_OPTION_FQDN },
+ { DHO_DNSDOMAIN, D6_OPTION_FQDN },
+ { DHO_NISSERVER, D6_OPTION_NIS_SERVERS },
+ { DHO_NTPSERVER, D6_OPTION_SNTP_SERVERS },
+ { DHO_RAPIDCOMMIT, D6_OPTION_RAPID_COMMIT },
+ { DHO_FQDN, D6_OPTION_FQDN },
+ { DHO_VIVCO, D6_OPTION_VENDOR_CLASS },
+ { DHO_VIVSO, D6_OPTION_VENDOR_OPTS },
+ { DHO_DNSSEARCH, D6_OPTION_DOMAIN_LIST },
+ { 0, 0 }
+};
+
+static const char * const dhcp6_statuses[] = {
+ "Success",
+ "Unspecified Failure",
+ "No Addresses Available",
+ "No Binding",
+ "Not On Link",
+ "Use Multicast"
+};
+
+#if DEBUG_MEMORY
+static void
+dhcp6_cleanup(void)
+{
+
+ free(sndbuf);
+ free(rcvbuf);
+ free(status);
+}
+#endif
+
+void
+dhcp6_printoptions(void)
+{
+ size_t i;
+ const struct dhcp_opt *opt;
+
+ for (i = 0, opt = dhcp6_opts; i < dhcp6_opts_len; i++, opt++)
+ printf("%05d %s\n", opt->option, opt->var);
+}
+
+static int
+dhcp6_init(void)
+{
+ int len;
+
+#if DEBUG_MEMORY
+ atexit(dhcp6_cleanup);
+#endif
+
+ len = CMSG_SPACE(sizeof(struct in6_pktinfo));
+ sndbuf = calloc(1, len);
+ if (sndbuf == NULL)
+ return -1;
+ sndhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndhdr.msg_iov = sndiov;
+ sndhdr.msg_iovlen = 1;
+ sndhdr.msg_control = sndbuf;
+ sndhdr.msg_controllen = len;
+
+ rcvbuf = calloc(1, len);
+ if (rcvbuf == NULL) {
+ free(sndbuf);
+ sndbuf = NULL;
+ return -1;
+ }
+ rcvhdr.msg_name = &from;
+ rcvhdr.msg_namelen = sizeof(from);
+ rcvhdr.msg_iov = rcviov;
+ rcvhdr.msg_iovlen = 1;
+ rcvhdr.msg_control = rcvbuf;
+ rcvhdr.msg_controllen = len;
+ rcviov[0].iov_base = ansbuf;
+ rcviov[0].iov_len = sizeof(ansbuf);
+
+ return 0;
+}
+
+static size_t
+dhcp6_makevendor(struct dhcp6_option *o, const struct interface *ifp)
+{
+ const struct if_options *ifo;
+ size_t len;
+ uint8_t *p;
+ uint16_t u16;
+ uint32_t u32;
+ size_t vlen, i;
+ const struct vivco *vivco;
+
+ ifo = ifp->options;
+ len = sizeof(uint32_t); /* IANA PEN */
+ if (ifo->vivco_en) {
+ for (i = 0, vivco = ifo->vivco;
+ i < ifo->vivco_len;
+ i++, vivco++)
+ len += sizeof(uint16_t) + vivco->len;
+ vlen = 0; /* silence bogus gcc warning */
+ } else {
+ vlen = strlen(vendor);
+ len += sizeof(uint16_t) + vlen;
+ }
+
+ if (len > UINT16_MAX) {
+ syslog(LOG_ERR, "%s: DHCPv6 Vendor Class too big", ifp->name);
+ return 0;
+ }
+
+ if (o) {
+ o->code = htons(D6_OPTION_VENDOR_CLASS);
+ o->len = htons(len);
+ p = D6_OPTION_DATA(o);
+ u32 = htonl(ifo->vivco_en ? ifo->vivco_en : DHCPCD_IANA_PEN);
+ memcpy(p, &u32, sizeof(u32));
+ p += sizeof(u32);
+ if (ifo->vivco_en) {
+ for (i = 0, vivco = ifo->vivco;
+ i < ifo->vivco_len;
+ i++, vivco++)
+ {
+ u16 = htons(vivco->len);
+ memcpy(p, &u16, sizeof(u16));
+ p += sizeof(u16);
+ memcpy(p, vivco->data, vivco->len);
+ p += vivco->len;
+ }
+ } else {
+ u16 = htons(vlen);
+ memcpy(p, &u16, sizeof(u16));
+ p += sizeof(u16);
+ memcpy(p, vendor, vlen);
+ }
+ }
+
+ return len;
+}
+
+static const struct dhcp6_option *
+dhcp6_findoption(int code, const uint8_t *d, ssize_t len)
+{
+ const struct dhcp6_option *o;
+
+ code = htons(code);
+ for (o = (const struct dhcp6_option *)d;
+ len > (ssize_t)sizeof(*o);
+ o = D6_CNEXT_OPTION(o))
+ {
+ len -= sizeof(*o) + ntohs(o->len);
+ if (len < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (o->code == code)
+ return o;
+ }
+
+ errno = ESRCH;
+ return NULL;
+}
+
+static const uint8_t *
+dhcp6_getoption(unsigned int *os, unsigned int *code, unsigned int *len,
+ const uint8_t *od, unsigned int ol, struct dhcp_opt **oopt)
+{
+ const struct dhcp6_option *o;
+ size_t i;
+ struct dhcp_opt *opt;
+
+ if (od) {
+ *os = sizeof(*o);
+ if (ol < *os) {
+ errno = EINVAL;
+ return NULL;
+ }
+ o = (const struct dhcp6_option *)od;
+ *len = ntohs(o->len);
+ if (*len > ol) {
+ errno = EINVAL;
+ return NULL;
+ }
+ *code = ntohs(o->code);
+ } else
+ o = NULL;
+
+ for (i = 0, opt = dhcp6_opts; i < dhcp6_opts_len; i++, opt++) {
+ if (opt->option == *code) {
+ *oopt = opt;
+ break;
+ }
+ }
+
+ if (o)
+ return D6_COPTION_DATA(o);
+ return NULL;
+}
+
+static const struct dhcp6_option *
+dhcp6_getmoption(int code, const struct dhcp6_message *m, ssize_t len)
+{
+
+ len -= sizeof(*m);
+ return dhcp6_findoption(code,
+ (const uint8_t *)D6_CFIRST_OPTION(m), len);
+}
+
+static int
+dhcp6_updateelapsed(struct interface *ifp, struct dhcp6_message *m, ssize_t len)
+{
+ struct dhcp6_state *state;
+ const struct dhcp6_option *co;
+ struct dhcp6_option *o;
+ time_t up;
+ uint16_t u16;
+
+ co = dhcp6_getmoption(D6_OPTION_ELAPSED, m, len);
+ if (co == NULL)
+ return -1;
+
+ o = __UNCONST(co);
+ state = D6_STATE(ifp);
+ up = uptime() - state->start_uptime;
+ if (up < 0 || up > (time_t)UINT16_MAX)
+ up = (time_t)UINT16_MAX;
+ u16 = htons(up);
+ memcpy(D6_OPTION_DATA(o), &u16, sizeof(u16));
+ return 0;
+}
+
+static void
+dhcp6_newxid(const struct interface *ifp, struct dhcp6_message *m)
+{
+ uint32_t xid;
+
+ if (ifp->options->options & DHCPCD_XID_HWADDR &&
+ ifp->hwlen >= sizeof(xid))
+ /* The lower bits are probably more unique on the network */
+ memcpy(&xid, (ifp->hwaddr + ifp->hwlen) - sizeof(xid),
+ sizeof(xid));
+ else
+ xid = arc4random();
+
+ m->xid[0] = (xid >> 16) & 0xff;
+ m->xid[1] = (xid >> 8) & 0xff;
+ m->xid[2] = xid & 0xff;
+}
+
+static int
+dhcp6_makemessage(struct interface *ifp)
+{
+ struct dhcp6_state *state;
+ struct dhcp6_message *m;
+ struct dhcp6_option *o, *so;
+ const struct dhcp6_option *si, *unicast;
+ ssize_t len, ml;
+ size_t l;
+ int auth_len;
+ uint8_t u8;
+ uint16_t *u16, n_options, type;
+ const struct if_options *ifo;
+ const struct dhcp_opt *opt;
+ uint8_t IA, *p;
+ uint32_t u32;
+ const struct ipv6_addr *ap;
+ const char *hostname = NULL; /* assignment just to appease GCC*/
+ int fqdn;
+
+ state = D6_STATE(ifp);
+ if (state->send) {
+ free(state->send);
+ state->send = NULL;
+ }
+
+ ifo = ifp->options;
+ fqdn = ifo->fqdn;
+
+ if (fqdn == FQDN_DISABLE && ifo->options & DHCPCD_HOSTNAME) {
+ /* We're sending the DHCPv4 hostname option, so send FQDN as
+ * DHCPv6 has no FQDN option and DHCPv4 must not send
+ * hostname and FQDN according to RFC4702 */
+ fqdn = FQDN_BOTH;
+ }
+ if (fqdn != FQDN_DISABLE) {
+ if (ifo->hostname[0] == '\0')
+ hostname = get_hostname(ifo->options &
+ DHCPCD_HOSTNAME_SHORT ? 1 : 0);
+ else
+ hostname = ifo->hostname;
+ }
+
+ /* Work out option size first */
+ n_options = 0;
+ len = 0;
+ si = NULL;
+ if (state->state != DH6S_RELEASE) {
+ for (l = 0, opt = dhcp6_opts; l < dhcp6_opts_len ; l++, opt++) {
+ if (!(opt->type & NOREQ) &&
+ (opt->type & REQUEST ||
+ has_option_mask(ifo->requestmask6, opt->option)))
+ {
+ n_options++;
+ len += sizeof(*u16);
+ }
+ }
+ if (len)
+ len += sizeof(*o);
+
+ if (fqdn != FQDN_DISABLE)
+ len += sizeof(*o) + 1 + encode_rfc1035(hostname, NULL);
+ }
+
+ len += sizeof(*state->send);
+ len += sizeof(*o) + duid_len;
+ len += sizeof(*o) + sizeof(uint16_t); /* elapsed */
+ len += sizeof(*o) + dhcp6_makevendor(NULL, ifp);
+
+ /* IA */
+ m = NULL;
+ ml = 0;
+ switch(state->state) {
+ case DH6S_REQUEST:
+ m = state->recv;
+ ml = state->recv_len;
+ /* FALLTHROUGH */
+ case DH6S_RELEASE:
+ /* FALLTHROUGH */
+ case DH6S_RENEW:
+ if (m == NULL) {
+ m = state->new;
+ ml = state->new_len;
+ }
+ si = dhcp6_getmoption(D6_OPTION_SERVERID, m, ml);
+ if (si == NULL) {
+ errno = ESRCH;
+ return -1;
+ }
+ len += sizeof(*si) + ntohs(si->len);
+ /* FALLTHROUGH */
+ case DH6S_REBIND:
+ /* FALLTHROUGH */
+ case DH6S_CONFIRM:
+ if (m == NULL) {
+ m = state->new;
+ ml = state->new_len;
+ }
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ if (ap->prefix_vltime == 0)
+ continue;
+ if (ifo->ia_type == D6_OPTION_IA_PD)
+ len += sizeof(*o) + sizeof(u8) +
+ sizeof(u32) + sizeof(u32) +
+ sizeof(ap->prefix.s6_addr);
+ else
+ len += sizeof(*o) + sizeof(ap->addr.s6_addr) +
+ sizeof(u32) + sizeof(u32);
+ }
+ /* FALLTHROUGH */
+ case DH6S_INIT: /* FALLTHROUGH */
+ case DH6S_DISCOVER:
+ len += ifo->ia_len * (sizeof(*o) + (sizeof(u32) * 3));
+ IA = 1;
+ break;
+ default:
+ IA = 0;
+ }
+
+ if (state->state == DH6S_DISCOVER &&
+ !(options & DHCPCD_TEST) &&
+ has_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT))
+ len += sizeof(*o);
+
+ if (m == NULL) {
+ m = state->new;
+ ml = state->new_len;
+ }
+ unicast = NULL;
+ /* Depending on state, get the unicast address */
+ switch(state->state) {
+ break;
+ case DH6S_INIT: /* FALLTHROUGH */
+ case DH6S_DISCOVER:
+ type = DHCP6_SOLICIT;
+ break;
+ case DH6S_REQUEST:
+ type = DHCP6_REQUEST;
+ unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
+ break;
+ case DH6S_CONFIRM:
+ type = DHCP6_CONFIRM;
+ break;
+ case DH6S_REBIND:
+ type = DHCP6_REBIND;
+ break;
+ case DH6S_RENEW:
+ type = DHCP6_RENEW;
+ unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
+ break;
+ case DH6S_INFORM:
+ type = DHCP6_INFORMATION_REQ;
+ break;
+ case DH6S_RELEASE:
+ type = DHCP6_RELEASE;
+ unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ifo->auth.options & DHCPCD_AUTH_SEND) {
+ auth_len = dhcp_auth_encode(&ifo->auth, state->auth.token,
+ NULL, 0, 6, type, NULL, 0);
+ if (auth_len > 0)
+ len += sizeof(*o) + auth_len;
+ } else
+ auth_len = 0; /* appease GCC */
+
+ state->send = malloc(len);
+ if (state->send == NULL)
+ return -1;
+
+ state->send_len = len;
+ state->send->type = type;
+
+ /* If we found a unicast option, copy it to our state for sending */
+ if (unicast && ntohs(unicast->len) == sizeof(state->unicast.s6_addr))
+ memcpy(&state->unicast.s6_addr, D6_COPTION_DATA(unicast),
+ sizeof(state->unicast.s6_addr));
+ else
+ state->unicast = in6addr_any;
+
+ dhcp6_newxid(ifp, state->send);
+
+ o = D6_FIRST_OPTION(state->send);
+ o->code = htons(D6_OPTION_CLIENTID);
+ o->len = htons(duid_len);
+ memcpy(D6_OPTION_DATA(o), duid, duid_len);
+
+ if (si) {
+ o = D6_NEXT_OPTION(o);
+ memcpy(o, si, sizeof(*si) + ntohs(si->len));
+ }
+
+ o = D6_NEXT_OPTION(o);
+ o->code = htons(D6_OPTION_ELAPSED);
+ o->len = htons(sizeof(uint16_t));
+ p = D6_OPTION_DATA(o);
+ memset(p, 0, sizeof(u16));
+
+ o = D6_NEXT_OPTION(o);
+ dhcp6_makevendor(o, ifp);
+
+ if (state->state == DH6S_DISCOVER &&
+ !(options & DHCPCD_TEST) &&
+ has_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT))
+ {
+ o = D6_NEXT_OPTION(o);
+ o->code = htons(D6_OPTION_RAPID_COMMIT);
+ o->len = 0;
+ }
+
+ for (l = 0; IA && l < ifo->ia_len; l++) {
+ o = D6_NEXT_OPTION(o);
+ o->code = htons(ifo->ia_type);
+ o->len = htons(sizeof(u32) + sizeof(u32) + sizeof(u32));
+ p = D6_OPTION_DATA(o);
+ memcpy(p, ifo->ia[l].iaid, sizeof(u32));
+ p += sizeof(u32);
+ memset(p, 0, sizeof(u32) + sizeof(u32));
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ if (ap->prefix_vltime == 0)
+ continue;
+ if (memcmp(ifo->ia[l].iaid, ap->iaid, sizeof(u32)))
+ continue;
+ so = D6_NEXT_OPTION(o);
+ if (ifo->ia_type == D6_OPTION_IA_PD) {
+ so->code = htons(D6_OPTION_IAPREFIX);
+ so->len = htons(sizeof(ap->prefix.s6_addr) +
+ sizeof(u32) + sizeof(u32) + sizeof(u8));
+ p = D6_OPTION_DATA(so);
+ u32 = htonl(ap->prefix_pltime);
+ memcpy(p, &u32, sizeof(u32));
+ p += sizeof(u32);
+ u32 = htonl(ap->prefix_vltime);
+ memcpy(p, &u32, sizeof(u32));
+ p += sizeof(u32);
+ u8 = ap->prefix_len;
+ memcpy(p, &u8, sizeof(u8));
+ p += sizeof(u8);
+ memcpy(p, &ap->prefix.s6_addr,
+ sizeof(ap->prefix.s6_addr));
+ /* Avoid a shadowed declaration warning by
+ * moving our addition outside of the htons
+ * macro */
+ u32 = ntohs(o->len) + sizeof(*so)
+ + ntohs(so->len);
+ o->len = htons(u32);
+ } else {
+ so->code = htons(D6_OPTION_IA_ADDR);
+ so->len = htons(sizeof(ap->addr.s6_addr) +
+ sizeof(u32) + sizeof(u32));
+ p = D6_OPTION_DATA(so);
+ memcpy(p, &ap->addr.s6_addr,
+ sizeof(ap->addr.s6_addr));
+ p += sizeof(ap->addr.s6_addr);
+ u32 = htonl(ap->prefix_pltime);
+ memcpy(p, &u32, sizeof(u32));
+ p += sizeof(u32);
+ u32 = htonl(ap->prefix_vltime);
+ memcpy(p, &u32, sizeof(u32));
+ /* Avoid a shadowed declaration warning by
+ * moving our addition outside of the htons
+ * macro */
+ u32 = ntohs(o->len) + sizeof(*so)
+ + ntohs(so->len);
+ o->len = htons(u32);
+ }
+ }
+ }
+
+ if (state->send->type != DHCP6_RELEASE) {
+ if (fqdn != FQDN_DISABLE) {
+ o = D6_NEXT_OPTION(o);
+ o->code = htons(D6_OPTION_FQDN);
+ p = D6_OPTION_DATA(o);
+ switch (fqdn) {
+ case FQDN_BOTH:
+ *p = D6_FQDN_BOTH;
+ break;
+ case FQDN_PTR:
+ *p = D6_FQDN_PTR;
+ break;
+ default:
+ *p = D6_FQDN_NONE;
+ break;
+ }
+ l = encode_rfc1035(hostname, p + 1);
+ if (l == 0)
+ *p = D6_FQDN_NONE;
+ o->len = htons(l + 1);
+ }
+
+ if (n_options) {
+ o = D6_NEXT_OPTION(o);
+ o->code = htons(D6_OPTION_ORO);
+ o->len = 0;
+ u16 = (uint16_t *)(void *)D6_OPTION_DATA(o);
+ for (l = 0, opt = dhcp6_opts;
+ l < dhcp6_opts_len;
+ l++, opt++)
+ {
+ if (!(opt->type & NOREQ) &&
+ (opt->type & REQUEST ||
+ has_option_mask(ifo->requestmask6,
+ opt->option)))
+ {
+ *u16++ = htons(opt->option);
+ o->len += sizeof(*u16);
+ }
+ }
+ o->len = htons(o->len);
+ }
+ }
+
+ /* This has to be the last option */
+ if (ifo->auth.options & DHCPCD_AUTH_SEND && auth_len > 0) {
+ o = D6_NEXT_OPTION(o);
+ o->code = htons(D6_OPTION_AUTH);
+ o->len = htons(auth_len);
+ /* data will be filled at send message time */
+ }
+
+ return 0;
+}
+
+static const char *
+dhcp6_get_op(uint16_t type)
+{
+ const struct dhcp6_op *d;
+
+ for (d = dhcp6_ops; d->name; d++)
+ if (d->type == type)
+ return d->name;
+ return NULL;
+}
+
+static void
+dhcp6_freedrop_addrs(struct interface *ifp, int drop,
+ const struct interface *ifd)
+{
+ struct dhcp6_state *state;
+
+ state = D6_STATE(ifp);
+ if (state) {
+ ipv6_freedrop_addrs(&state->addrs, drop, ifd);
+ if (drop)
+ ipv6_buildroutes();
+ }
+}
+
+static void dhcp6_delete_delegates(struct interface *ifp)
+{
+ struct interface *ifp0;
+
+ if (ifaces) {
+ TAILQ_FOREACH(ifp0, ifaces, next) {
+ if (ifp0 != ifp)
+ dhcp6_freedrop_addrs(ifp0, 1, ifp);
+ }
+ }
+}
+
+
+static int
+dhcp6_update_auth(struct interface *ifp, struct dhcp6_message *m, ssize_t len)
+{
+ struct dhcp6_state *state;
+ const struct dhcp6_option *co;
+ struct dhcp6_option *o;
+
+ co = dhcp6_getmoption(D6_OPTION_AUTH, m, len);
+ if (co == NULL)
+ return -1;
+
+ o = __UNCONST(co);
+ state = D6_STATE(ifp);
+
+ return dhcp_auth_encode(&ifp->options->auth, state->auth.token,
+ (uint8_t *)state->send, state->send_len,
+ 6, state->send->type,
+ D6_OPTION_DATA(o), ntohs(o->len));
+}
+
+static int
+dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
+{
+ struct dhcp6_state *state;
+ struct sockaddr_in6 to;
+ struct cmsghdr *cm;
+ struct in6_pktinfo pi;
+ struct timeval RTprev;
+ double rnd;
+ suseconds_t ms;
+ uint8_t neg;
+ const char *broad_uni;
+
+ memset(&to, 0, sizeof(to));
+ to.sin6_family = AF_INET6;
+ to.sin6_port = htons(DHCP6_SERVER_PORT);
+#ifdef SIN6_LEN
+ to.sin6_len = sizeof(to);
+#endif
+
+ state = D6_STATE(ifp);
+ /* We need to ensure we have sufficient scope to unicast the address */
+ /* XXX FIXME: We should check any added addresses we have like from
+ * a Router Advertisement */
+ if (IN6_IS_ADDR_UNSPECIFIED(&state->unicast) ||
+ (state->state == DH6S_REQUEST &&
+ (!IN6_IS_ADDR_LINKLOCAL(&state->unicast) || !ipv6_linklocal(ifp))))
+ {
+ to.sin6_addr = in6addr_linklocal_alldhcp;
+ broad_uni = "broadcasting";
+ } else {
+ to.sin6_addr = state->unicast;
+ broad_uni = "unicasting";
+ }
+
+ if (!callback)
+ syslog(LOG_DEBUG, "%s: %s %s with xid 0x%02x%02x%02x",
+ ifp->name,
+ broad_uni,
+ dhcp6_get_op(state->send->type),
+ state->send->xid[0],
+ state->send->xid[1],
+ state->send->xid[2]);
+ else {
+ if (state->IMD) {
+ /* Some buggy PPP servers close the link too early
+ * after sending an invalid status in their reply
+ * which means this host won't see it.
+ * 1 second grace seems to be the sweet spot. */
+ if (ifp->flags & IFF_POINTOPOINT)
+ state->RT.tv_sec = 1;
+ else
+ state->RT.tv_sec = 0;
+ state->RT.tv_usec = arc4random() %
+ (state->IMD * 1000000);
+ timernorm(&state->RT);
+ broad_uni = "delaying";
+ goto logsend;
+ }
+ if (state->RTC == 0) {
+ RTprev.tv_sec = state->IRT;
+ RTprev.tv_usec = 0;
+ state->RT.tv_sec = RTprev.tv_sec;
+ state->RT.tv_usec = 0;
+ } else {
+ RTprev = state->RT;
+ timeradd(&state->RT, &state->RT, &state->RT);
+ }
+
+ rnd = DHCP6_RAND_MIN;
+ rnd += arc4random() % (DHCP6_RAND_MAX - DHCP6_RAND_MIN);
+ rnd /= 1000;
+ neg = (rnd < 0.0);
+ if (neg)
+ rnd = -rnd;
+ tv_to_ms(ms, &RTprev);
+ ms *= rnd;
+ ms_to_tv(&RTprev, ms);
+ if (neg)
+ timersub(&state->RT, &RTprev, &state->RT);
+ else
+ timeradd(&state->RT, &RTprev, &state->RT);
+
+ if (state->RT.tv_sec > state->MRT) {
+ RTprev.tv_sec = state->MRT;
+ RTprev.tv_usec = 0;
+ state->RT.tv_sec = state->MRT;
+ state->RT.tv_usec = 0;
+ tv_to_ms(ms, &RTprev);
+ ms *= rnd;
+ ms_to_tv(&RTprev, ms);
+ if (neg)
+ timersub(&state->RT, &RTprev, &state->RT);
+ else
+ timeradd(&state->RT, &RTprev, &state->RT);
+ }
+
+logsend:
+ syslog(LOG_DEBUG,
+ "%s: %s %s (xid 0x%02x%02x%02x),"
+ " next in %0.1f seconds",
+ ifp->name,
+ broad_uni,
+ dhcp6_get_op(state->send->type),
+ state->send->xid[0],
+ state->send->xid[1],
+ state->send->xid[2],
+ timeval_to_double(&state->RT));
+
+ /* Wait the initial delay */
+ if (state->IMD) {
+ state->IMD = 0;
+ eloop_timeout_add_tv(&state->RT, callback, ifp);
+ return 0;
+ }
+ }
+
+ /* Update the elapsed time */
+ dhcp6_updateelapsed(ifp, state->send, state->send_len);
+ if (ifp->options->auth.options & DHCPCD_AUTH_SEND &&
+ dhcp6_update_auth(ifp, state->send, state->send_len) == -1)
+ {
+ syslog(LOG_ERR, "%s: dhcp6_updateauth: %m", ifp->name);
+ return -1;
+ }
+
+ to.sin6_scope_id = ifp->index;
+ sndhdr.msg_name = (caddr_t)&to;
+ sndhdr.msg_iov[0].iov_base = (caddr_t)state->send;
+ sndhdr.msg_iov[0].iov_len = state->send_len;
+
+ /* Set the outbound interface */
+ cm = CMSG_FIRSTHDR(&sndhdr);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(pi));
+ memset(&pi, 0, sizeof(pi));
+ pi.ipi6_ifindex = ifp->index;
+ memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
+
+ if (sendmsg(sock, &sndhdr, 0) == -1) {
+ syslog(LOG_ERR, "%s: %s: sendmsg: %m", ifp->name, __func__);
+ ifp->options->options &= ~DHCPCD_IPV6;
+ dhcp6_drop(ifp, "EXPIRE6");
+ return -1;
+ }
+
+ state->RTC++;
+ if (callback) {
+ if (state->MRC == 0 || state->RTC < state->MRC)
+ eloop_timeout_add_tv(&state->RT, callback, ifp);
+ else if (state->MRC != 0 && state->MRCcallback)
+ eloop_timeout_add_tv(&state->RT, state->MRCcallback,
+ ifp);
+ else
+ syslog(LOG_WARNING, "%s: sent %d times with no reply",
+ ifp->name, state->RTC);
+ }
+ return 0;
+}
+
+static void
+dhcp6_sendinform(void *arg)
+{
+
+ dhcp6_sendmessage(arg, dhcp6_sendinform);
+}
+
+static void
+dhcp6_senddiscover(void *arg)
+{
+
+ dhcp6_sendmessage(arg, dhcp6_senddiscover);
+}
+
+static void
+dhcp6_sendrequest(void *arg)
+{
+
+ dhcp6_sendmessage(arg, dhcp6_sendrequest);
+}
+
+static void
+dhcp6_sendrebind(void *arg)
+{
+
+ dhcp6_sendmessage(arg, dhcp6_sendrebind);
+}
+
+static void
+dhcp6_sendrenew(void *arg)
+{
+
+ dhcp6_sendmessage(arg, dhcp6_sendrenew);
+}
+
+static void
+dhcp6_sendconfirm(void *arg)
+{
+
+ dhcp6_sendmessage(arg, dhcp6_sendconfirm);
+}
+
+/*
+static void
+dhcp6_sendrelease(void *arg)
+{
+
+ dhcp6_sendmessage(arg, dhcp6_sendrelease);
+}
+*/
+
+static void
+dhcp6_startrenew(void *arg)
+{
+ struct interface *ifp;
+ struct dhcp6_state *state;
+
+ ifp = arg;
+ state = D6_STATE(ifp);
+ state->state = DH6S_RENEW;
+ state->start_uptime = uptime();
+ state->RTC = 0;
+ state->IRT = REN_TIMEOUT;
+ state->MRT = REN_MAX_RT;
+ state->MRC = 0;
+
+ if (dhcp6_makemessage(ifp) == -1)
+ syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+ else
+ dhcp6_sendrenew(ifp);
+}
+
+static void
+dhcp6_startdiscover(void *arg)
+{
+ struct interface *ifp;
+ struct dhcp6_state *state;
+
+ ifp = arg;
+ dhcp6_delete_delegates(ifp);
+ syslog(LOG_INFO, "%s: soliciting a DHCPv6 lease", ifp->name);
+ state = D6_STATE(ifp);
+ state->state = DH6S_DISCOVER;
+ state->start_uptime = uptime();
+ state->RTC = 0;
+ state->IMD = SOL_MAX_DELAY;
+ state->IRT = SOL_TIMEOUT;
+ state->MRT = SOL_MAX_RT;
+ state->MRC = 0;
+
+ eloop_timeout_delete(NULL, ifp);
+ free(state->new);
+ state->new = NULL;
+ state->new_len = 0;
+
+ dhcp6_freedrop_addrs(ifp, 0, NULL);
+ unlink(state->leasefile);
+
+ if (dhcp6_makemessage(ifp) == -1)
+ syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+ else
+ dhcp6_senddiscover(ifp);
+}
+
+static void
+dhcp6_failconfirm(void *arg)
+{
+ struct interface *ifp;
+
+ ifp = arg;
+ syslog(LOG_ERR, "%s: failed to confirm prior address", ifp->name);
+ /* Section 18.1.2 says that we SHOULD use the last known
+ * IP address(s) and lifetimes if we didn't get a reply.
+ * I disagree with this. */
+ dhcp6_startdiscover(ifp);
+}
+
+static void
+dhcp6_failrequest(void *arg)
+{
+ struct interface *ifp;
+
+ ifp = arg;
+ syslog(LOG_ERR, "%s: failed to request address", ifp->name);
+ /* Section 18.1.1 says that client local policy dictates
+ * what happens if a REQUEST fails.
+ * Of the possible scenarios listed, moving back to the
+ * DISCOVER phase makes more sense for us. */
+ dhcp6_startdiscover(ifp);
+}
+
+static void
+dhcp6_failrebind(void *arg)
+{
+ struct interface *ifp;
+
+ ifp = arg;
+ syslog(LOG_ERR, "%s: failed to rebind prior delegation", ifp->name);
+ dhcp6_delete_delegates(ifp);
+ /* Section 18.1.2 says that we SHOULD use the last known
+ * IP address(s) and lifetimes if we didn't get a reply.
+ * I disagree with this. */
+ dhcp6_startdiscover(ifp);
+}
+
+static void
+dhcp6_startrebind(void *arg)
+{
+ struct interface *ifp;
+ struct dhcp6_state *state;
+
+ ifp = arg;
+ eloop_timeout_delete(dhcp6_sendrenew, ifp);
+ state = D6_STATE(ifp);
+ if (state->state == DH6S_RENEW)
+ syslog(LOG_WARNING, "%s: failed to renew DHCPv6, rebinding",
+ ifp->name);
+ state->state = DH6S_REBIND;
+ state->RTC = 0;
+ state->MRC = 0;
+
+ /* RFC 3633 section 12.1 */
+ if (ifp->options->ia_type == D6_OPTION_IA_PD) {
+ syslog(LOG_INFO, "%s: confirming Prefix Delegation", ifp->name);
+ state->IMD = CNF_MAX_DELAY;
+ state->IRT = CNF_TIMEOUT;
+ state->MRT = CNF_MAX_RT;
+ } else {
+ state->IRT = REB_TIMEOUT;
+ state->MRT = REB_MAX_RT;
+ }
+
+ if (dhcp6_makemessage(ifp) == -1)
+ syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+ else
+ dhcp6_sendrebind(ifp);
+
+ /* RFC 3633 section 12.1 */
+ if (ifp->options->ia_type == D6_OPTION_IA_PD)
+ eloop_timeout_add_sec(CNF_MAX_RD, dhcp6_failrebind, ifp);
+}
+
+
+static void
+dhcp6_startrequest(struct interface *ifp)
+{
+ struct dhcp6_state *state;
+
+ eloop_timeout_delete(dhcp6_senddiscover, ifp);
+ state = D6_STATE(ifp);
+ state->state = DH6S_REQUEST;
+ state->RTC = 0;
+ state->IRT = REQ_TIMEOUT;
+ state->MRT = REQ_MAX_RT;
+ state->MRC = REQ_MAX_RC;
+ state->MRCcallback = dhcp6_failrequest;
+
+ if (dhcp6_makemessage(ifp) == -1) {
+ syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+ return;
+ }
+
+ dhcp6_sendrequest(ifp);
+}
+
+static void
+dhcp6_startconfirm(struct interface *ifp)
+{
+ struct dhcp6_state *state;
+
+ state = D6_STATE(ifp);
+ state->state = DH6S_CONFIRM;
+ state->start_uptime = uptime();
+ state->RTC = 0;
+ state->IMD = CNF_MAX_DELAY;
+ state->IRT = CNF_TIMEOUT;
+ state->MRT = CNF_MAX_RT;
+ state->MRC = 0;
+
+ syslog(LOG_INFO, "%s: confirming prior DHCPv6 lease", ifp->name);
+ if (dhcp6_makemessage(ifp) == -1) {
+ syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+ return;
+ }
+ dhcp6_sendconfirm(ifp);
+ eloop_timeout_add_sec(CNF_MAX_RD, dhcp6_failconfirm, ifp);
+}
+
+static void
+dhcp6_startinform(void *arg)
+{
+ struct interface *ifp;
+ struct dhcp6_state *state;
+
+ ifp = arg;
+ state = D6_STATE(ifp);
+ if (state->new == NULL || ifp->options->options & DHCPCD_DEBUG)
+ syslog(LOG_INFO, "%s: requesting DHCPv6 information",
+ ifp->name);
+ state->state = DH6S_INFORM;
+ state->start_uptime = uptime();
+ state->RTC = 0;
+ state->IMD = INF_MAX_DELAY;
+ state->IRT = INF_TIMEOUT;
+ state->MRT = INF_MAX_RT;
+ state->MRC = 0;
+
+ if (dhcp6_makemessage(ifp) == -1)
+ syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+ else
+ dhcp6_sendinform(ifp);
+}
+
+static void
+dhcp6_startexpire(void *arg)
+{
+ struct interface *ifp;
+
+ ifp = arg;
+ eloop_timeout_delete(dhcp6_sendrebind, ifp);
+
+ syslog(LOG_ERR, "%s: DHCPv6 lease expired", ifp->name);
+ dhcp6_freedrop_addrs(ifp, 1, NULL);
+ dhcp6_delete_delegates(ifp);
+ script_runreason(ifp, "EXPIRE6");
+ dhcp6_startdiscover(ifp);
+}
+
+static void
+dhcp6_startrelease(struct interface *ifp)
+{
+ struct dhcp6_state *state;
+
+ state = D6_STATE(ifp);
+ if (state->state != DH6S_BOUND)
+ return;
+
+ state->state = DH6S_RELEASE;
+ state->start_uptime = uptime();
+ state->RTC = 0;
+ state->IRT = REL_TIMEOUT;
+ state->MRT = 0;
+ state->MRC = REL_MAX_RC;
+ //state->MRCcallback = dhcp6_failrelease;
+ state->MRCcallback = NULL;
+
+ if (dhcp6_makemessage(ifp) == -1)
+ syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
+ else
+ /* XXX: We should loop a few times
+ * Luckily RFC3315 section 18.1.6 says this is optional */
+ //dhcp6_sendrelease(ifp);
+ dhcp6_sendmessage(ifp, NULL);
+}
+
+static int dhcp6_getstatus(const struct dhcp6_option *o)
+{
+ uint16_t code;
+ char *nstatus;
+ size_t len;
+ const uint8_t *p;
+
+ len = ntohs(o->len);
+ if (len < sizeof(code)) {
+ syslog(LOG_ERR, "status truncated");
+ return -1;
+ }
+ if (ntohs(o->code) != D6_OPTION_STATUS_CODE) {
+ /* unlikely */
+ syslog(LOG_ERR, "not a status");
+ return -1;
+ }
+ p = D6_COPTION_DATA(o);
+ len = ntohs(o->len);
+ memcpy(&code, p, sizeof(code));
+ code = ntohs(code);
+ len -= sizeof(code);
+
+ if (len == 0) {
+ if (code < sizeof(dhcp6_statuses) / sizeof(char *)) {
+ p = (const uint8_t *)dhcp6_statuses[code];
+ len = strlen((const char *)p);
+ } else
+ p = NULL;
+ } else {
+ p = D6_COPTION_DATA(o) + sizeof(uint16_t);
+ }
+ if (status == NULL || len + 1 > status_len) {
+ status_len = len;
+ nstatus = realloc(status, status_len + 1);
+ if (nstatus == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ free(status);
+ }
+ status = nstatus;
+ }
+ if (status) {
+ memcpy(status, p, len);
+ status[len] = '\0';
+ }
+ return code;
+}
+
+static int
+dhcp6_checkstatusok(const struct interface *ifp,
+ const struct dhcp6_message *m, const uint8_t *p, size_t len)
+{
+ const struct dhcp6_option *o;
+
+ if (p)
+ o = dhcp6_findoption(D6_OPTION_STATUS_CODE, p, len);
+ else
+ o = dhcp6_getmoption(D6_OPTION_STATUS_CODE, m, len);
+ if (o == NULL) {
+ //syslog(LOG_DEBUG, "%s: no status", ifp->name);
+ return 0;
+ }
+ if (dhcp6_getstatus(o) != D6_STATUS_OK) {
+ syslog(LOG_ERR, "%s: DHCPv6 REPLY: %s", ifp->name, status);
+ return -1;
+ }
+ //syslog(LOG_DEBUG, "%s: status: %s", ifp->name, status);
+ return 1;
+}
+
+static struct ipv6_addr *
+dhcp6_findaddr(struct interface *ifp, const struct in6_addr *addr)
+{
+ const struct dhcp6_state *state;
+ struct ipv6_addr *ap;
+
+ state = D6_CSTATE(ifp);
+ if (state) {
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ if (addr == NULL) {
+ if ((ap->flags &
+ (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)) ==
+ (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED))
+ return ap;
+ } else if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
+ return ap;
+ }
+ }
+ return NULL;
+}
+
+int
+dhcp6_addrexists(const struct ipv6_addr *addr)
+{
+ struct interface *ifp;
+
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ if (dhcp6_findaddr(ifp, addr == NULL ? NULL : &addr->addr))
+ return 1;
+ }
+ return 0;
+}
+
+static void
+dhcp6_dadcallback(void *arg)
+{
+ struct ipv6_addr *ap = arg;
+ struct interface *ifp;
+ struct dhcp6_state *state;
+ int wascompleted;
+
+ wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED);
+ ipv6nd_cancelprobeaddr(ap);
+ ap->flags |= IPV6_AF_DADCOMPLETED;
+ if (ap->flags & IPV6_AF_DUPLICATED)
+ /* XXX FIXME
+ * We should decline the address */
+ syslog(LOG_WARNING, "%s: DAD detected %s",
+ ap->iface->name, ap->saddr);
+#ifdef IPV6_SEND_DAD
+ else
+ ipv6_addaddr(ap);
+#endif
+
+ if (!wascompleted) {
+ ifp = ap->iface;
+ state = D6_STATE(ifp);
+ if (state->state == DH6S_BOUND ||
+ state->state == DH6S_DELEGATED)
+ {
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
+ wascompleted = 1;
+ break;
+ }
+ }
+ if (!wascompleted) {
+ syslog(LOG_DEBUG, "%s: DHCPv6 DAD completed",
+ ifp->name);
+ script_runreason(ifp, state->reason);
+ daemonise();
+ }
+ }
+ }
+}
+
+static int
+dhcp6_findna(struct interface *ifp, const uint8_t *iaid,
+ const uint8_t *d, size_t l)
+{
+ struct dhcp6_state *state;
+ const struct dhcp6_option *o;
+ const uint8_t *p;
+ struct in6_addr in6;
+ struct ipv6_addr *a;
+ char iabuf[INET6_ADDRSTRLEN];
+ const char *ia;
+ int i;
+ uint32_t u32;
+
+ i = 0;
+ state = D6_STATE(ifp);
+ while ((o = dhcp6_findoption(D6_OPTION_IA_ADDR, d, l))) {
+ l -= ((const uint8_t *)o - d);
+ d += ((const uint8_t *)o - d);
+ u32 = ntohs(o->len);
+ l -= sizeof(*o) + u32;
+ d += sizeof(*o) + u32;
+ if (u32 < 24) {
+ errno = EINVAL;
+ syslog(LOG_ERR, "%s: IA Address option truncated",
+ ifp->name);
+ continue;
+ }
+ p = D6_COPTION_DATA(o);
+ memcpy(&in6.s6_addr, p, sizeof(in6.s6_addr));
+ p += sizeof(in6.s6_addr);
+ a = dhcp6_findaddr(ifp, &in6);
+ if (a == NULL) {
+ a = calloc(1, sizeof(*a));
+ if (a == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ break;
+ }
+ a->iface = ifp;
+ a->flags = IPV6_AF_NEW | IPV6_AF_ONLINK;
+ a->dadcallback = dhcp6_dadcallback;
+ memcpy(a->iaid, iaid, sizeof(a->iaid));
+ memcpy(&a->addr.s6_addr, &in6.s6_addr,
+ sizeof(in6.s6_addr));
+
+ /*
+ * RFC 5942 Section 5
+ * We cannot assume any prefix length, nor tie the
+ * address to an existing one as it could expire
+ * before the address.
+ * As such we just give it a 128 prefix.
+ */
+ a->prefix_len = 128;
+ if (ipv6_makeprefix(&a->prefix, &a->addr,
+ a->prefix_len) == -1)
+ {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ free(a);
+ continue;
+ }
+ ia = inet_ntop(AF_INET6, &a->addr.s6_addr,
+ iabuf, sizeof(iabuf));
+ snprintf(a->saddr, sizeof(a->saddr),
+ "%s/%d", ia, a->prefix_len);
+ }
+ memcpy(&u32, p, sizeof(u32));
+ a->prefix_pltime = ntohl(u32);
+ p += sizeof(u32);
+ memcpy(&u32, p, sizeof(u32));
+ u32 = ntohl(u32);
+ if (a->prefix_vltime != u32) {
+ a->flags |= IPV6_AF_NEW;
+ a->prefix_vltime = u32;
+ }
+ if (a->prefix_pltime && a->prefix_pltime < state->lowpl)
+ state->lowpl = a->prefix_pltime;
+ if (a->prefix_vltime && a->prefix_vltime > state->expire)
+ state->expire = a->prefix_vltime;
+ if (a->flags & IPV6_AF_STALE)
+ a->flags &= ~IPV6_AF_STALE;
+ else
+ TAILQ_INSERT_TAIL(&state->addrs, a, next);
+ i++;
+ }
+ return i;
+}
+
+static int
+dhcp6_findpd(struct interface *ifp, const uint8_t *iaid,
+ const uint8_t *d, size_t l)
+{
+ struct dhcp6_state *state;
+ const struct dhcp6_option *o;
+ const uint8_t *p;
+ struct ipv6_addr *a;
+ char iabuf[INET6_ADDRSTRLEN];
+ const char *ia;
+ int i;
+ uint8_t u8, len;
+ uint32_t u32, pltime, vltime;
+ struct in6_addr prefix;
+
+ i = 0;
+ state = D6_STATE(ifp);
+ while ((o = dhcp6_findoption(D6_OPTION_IAPREFIX, d, l))) {
+ l -= ((const uint8_t *)o - d);
+ d += ((const uint8_t *)o - d);
+ u32 = ntohs(o->len);
+ l -= sizeof(*o) + u32;
+ d += sizeof(*o) + u32;
+ if (u32 < 25) {
+ errno = EINVAL;
+ syslog(LOG_ERR, "%s: IA Prefix option truncated",
+ ifp->name);
+ continue;
+ }
+ p = D6_COPTION_DATA(o);
+ memcpy(&u32, p, sizeof(u32));
+ pltime = ntohl(u32);
+ p += sizeof(u32);
+ memcpy(&u32, p, sizeof(u32));
+ p += sizeof(u32);
+ vltime = ntohl(u32);
+ memcpy(&u8, p, sizeof(u8));
+ p += sizeof(u8);
+ len = u8;
+ memcpy(&prefix.s6_addr, p, sizeof(prefix.s6_addr));
+ p += sizeof(prefix.s6_addr);
+
+ TAILQ_FOREACH(a, &state->addrs, next) {
+ if (IN6_ARE_ADDR_EQUAL(&a->prefix, &prefix))
+ break;
+ }
+ if (a == NULL) {
+ a = calloc(1, sizeof(*a));
+ if (a == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ break;
+ }
+ a->iface = ifp;
+ a->flags = IPV6_AF_NEW;
+ a->dadcallback = dhcp6_dadcallback;
+ memcpy(a->iaid, iaid, sizeof(a->iaid));
+ memcpy(&a->prefix.s6_addr,
+ &prefix.s6_addr, sizeof(a->prefix.s6_addr));
+ a->prefix_len = len;
+ ia = inet_ntop(AF_INET6, &a->prefix.s6_addr,
+ iabuf, sizeof(iabuf));
+ snprintf(a->saddr, sizeof(a->saddr),
+ "%s/%d", ia, a->prefix_len);
+ } else if (a->prefix_vltime != vltime)
+ a->flags |= IPV6_AF_NEW;
+
+ a->prefix_pltime = pltime;
+ a->prefix_vltime = vltime;
+ if (a->prefix_pltime && a->prefix_pltime < state->lowpl)
+ state->lowpl = a->prefix_pltime;
+ if (a->prefix_vltime && a->prefix_vltime > state->expire)
+ state->expire = a->prefix_vltime;
+ if (a->flags & IPV6_AF_STALE)
+ a->flags &= ~IPV6_AF_STALE;
+ else
+ TAILQ_INSERT_TAIL(&state->addrs, a, next);
+ i++;
+ }
+ return i;
+}
+
+static int
+dhcp6_findia(struct interface *ifp, const uint8_t *d, size_t l,
+ const char *sfrom)
+{
+ struct dhcp6_state *state;
+ const struct if_options *ifo;
+ const struct dhcp6_option *o;
+ const uint8_t *p;
+ int i;
+ uint32_t u32, renew, rebind;
+ uint8_t iaid[4];
+ size_t ol;
+ struct ipv6_addr *ap, *nap;
+
+ ifo = ifp->options;
+ i = 0;
+ state = D6_STATE(ifp);
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ ap->flags |= IPV6_AF_STALE;
+ }
+ while ((o = dhcp6_findoption(ifo->ia_type, d, l))) {
+ l -= ((const uint8_t *)o - d);
+ d += ((const uint8_t *)o - d);
+ ol = ntohs(o->len);
+ l -= sizeof(*o) + ol;
+ d += sizeof(*o) + ol;
+ u32 = ifo->ia_type == D6_OPTION_IA_TA ? 4 : 12;
+ if (ol < u32) {
+ errno = EINVAL;
+ syslog(LOG_ERR, "%s: IA option truncated", ifp->name);
+ continue;
+ }
+
+ p = D6_COPTION_DATA(o);
+ memcpy(iaid, p, sizeof(iaid));
+ p += sizeof(iaid);
+ ol -= sizeof(iaid);
+ if (ifo->ia_type != D6_OPTION_IA_TA) {
+ memcpy(&u32, p, sizeof(u32));
+ renew = ntohl(u32);
+ p += sizeof(u32);
+ ol -= sizeof(u32);
+ memcpy(&u32, p, sizeof(u32));
+ rebind = ntohl(u32);
+ p += sizeof(u32);
+ ol -= sizeof(u32);
+ if (renew > rebind && rebind > 0) {
+ if (sfrom)
+ syslog(LOG_WARNING,
+ "%s: T1 (%d) > T2 (%d) from %s",
+ ifp->name, renew, rebind, sfrom);
+ renew = 0;
+ rebind = 0;
+ }
+ if (renew != 0 &&
+ (renew < state->renew || state->renew == 0))
+ state->renew = renew;
+ if (rebind != 0 &&
+ (rebind < state->rebind || state->rebind == 0))
+ state->rebind = rebind;
+ }
+ if (dhcp6_checkstatusok(ifp, NULL, p, ol) == -1)
+ return -1;
+ if (ifo->ia_type == D6_OPTION_IA_PD) {
+ if (dhcp6_findpd(ifp, iaid, p, ol) == 0) {
+ syslog(LOG_ERR,
+ "%s: %s: DHCPv6 REPLY missing Prefix",
+ ifp->name, sfrom);
+ return -1;
+ }
+ } else {
+ if (dhcp6_findna(ifp, iaid, p, ol) == 0) {
+ syslog(LOG_ERR,
+ "%s: %s: DHCPv6 REPLY missing IA Address",
+ ifp->name, sfrom);
+ return -1;
+ }
+ }
+ i++;
+ }
+ TAILQ_FOREACH_SAFE(ap, &state->addrs, next, nap) {
+ if (ap->flags & IPV6_AF_STALE) {
+ TAILQ_REMOVE(&state->addrs, ap, next);
+ if (ap->dadcallback)
+ eloop_q_timeout_delete(0, NULL,
+ ap->dadcallback);
+ free(ap);
+ }
+ }
+ return i;
+}
+
+static int
+dhcp6_validatelease(struct interface *ifp,
+ const struct dhcp6_message *m, size_t len,
+ const char *sfrom)
+{
+ struct dhcp6_state *state;
+ const struct dhcp6_option *o;
+
+ state = D6_STATE(ifp);
+ o = dhcp6_getmoption(ifp->options->ia_type, m, len);
+ if (o == NULL) {
+ if (sfrom &&
+ dhcp6_checkstatusok(ifp, m, NULL, len) != -1)
+ syslog(LOG_ERR, "%s: no IA in REPLY from %s",
+ ifp->name, sfrom);
+ return -1;
+ }
+
+ if (dhcp6_checkstatusok(ifp, m, NULL, len) == -1)
+ return -1;
+
+ state->renew = state->rebind = state->expire = 0;
+ state->lowpl = ND6_INFINITE_LIFETIME;
+ len -= (const char *)o - (const char *)m;
+ return dhcp6_findia(ifp, (const uint8_t *)o, len, sfrom);
+}
+
+static ssize_t
+dhcp6_writelease(const struct interface *ifp)
+{
+ const struct dhcp6_state *state;
+ int fd;
+ ssize_t bytes;
+
+ state = D6_CSTATE(ifp);
+ syslog(LOG_DEBUG, "%s: writing lease `%s'",
+ ifp->name, state->leasefile);
+
+ fd = open(state->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd == -1) {
+ syslog(LOG_ERR, "%s: dhcp6_writelease: %m", ifp->name);
+ return -1;
+ }
+ bytes = write(fd, state->new, state->new_len);
+ close(fd);
+ return bytes;
+}
+
+static int
+dhcp6_readlease(struct interface *ifp)
+{
+ struct dhcp6_state *state;
+ struct stat st;
+ int fd;
+ ssize_t bytes;
+ struct timeval now;
+ const struct dhcp6_option *o;
+
+ state = D6_STATE(ifp);
+ if (stat(state->leasefile, &st) == -1) {
+ if (errno == ENOENT)
+ return 0;
+ return -1;
+ }
+ syslog(LOG_DEBUG, "%s: reading lease `%s'",
+ ifp->name, state->leasefile);
+ state->new = malloc(st.st_size);
+ if (state->new == NULL)
+ return -1;
+ state->new_len = st.st_size;
+ fd = open(state->leasefile, O_RDONLY);
+ if (fd == -1)
+ return -1;
+ bytes = read(fd, state->new, state->new_len);
+ close(fd);
+ if (bytes < (ssize_t)state->new_len) {
+ syslog(LOG_ERR, "%s: read: %m", __func__);
+ goto ex;
+ }
+
+ /* Check to see if the lease is still valid */
+ fd = dhcp6_validatelease(ifp, state->new, state->new_len, NULL);
+ if (fd == -1)
+ goto ex;
+ if (fd == 0) {
+ syslog(LOG_INFO, "%s: lease was for different IA type",
+ ifp->name);
+ goto ex;
+ }
+
+ if (state->expire != ND6_INFINITE_LIFETIME) {
+ gettimeofday(&now, NULL);
+ if ((time_t)state->expire < now.tv_sec - st.st_mtime) {
+ syslog(LOG_DEBUG,"%s: discarding expired lease",
+ ifp->name);
+ goto ex;
+ }
+ }
+
+ /* Authenticate the message */
+ o = dhcp6_getmoption(D6_OPTION_AUTH, state->new, state->new_len);
+ if (o) {
+ if (dhcp_auth_validate(&state->auth, &ifp->options->auth,
+ (uint8_t *)state->new, state->new_len, 6, state->new->type,
+ D6_COPTION_DATA(o), ntohs(o->len)) == NULL)
+ {
+ syslog(LOG_DEBUG, "%s: dhcp_auth_validate: %m",
+ ifp->name);
+ syslog(LOG_ERR, "%s: authentication failed",
+ ifp->name);
+ goto ex;
+ }
+ syslog(LOG_DEBUG, "%s: validated using 0x%08" PRIu32,
+ ifp->name, state->auth.token->secretid);
+ } else if (ifp->options->auth.options & DHCPCD_AUTH_REQUIRE) {
+ syslog(LOG_ERR, "%s: authentication now required", ifp->name);
+ goto ex;
+ }
+
+ return fd;
+
+ex:
+ dhcp6_freedrop_addrs(ifp, 0, NULL);
+ free(state->new);
+ state->new = NULL;
+ state->new_len = 0;
+ unlink(state->leasefile);
+ return 0;
+}
+
+static void
+dhcp6_startinit(struct interface *ifp)
+{
+ struct dhcp6_state *state;
+ int r;
+
+ state = D6_STATE(ifp);
+ state->state = DH6S_INIT;
+ state->expire = ND6_INFINITE_LIFETIME;
+ state->lowpl = ND6_INFINITE_LIFETIME;
+ if (!(options & DHCPCD_TEST) &&
+ ifp->options->ia_type != D6_OPTION_IA_TA &&
+ ifp->options->reboot != 0)
+ {
+ r = dhcp6_readlease(ifp);
+ if (r == -1)
+ syslog(LOG_ERR, "%s: dhcp6_readlease: %s: %m",
+ ifp->name, state->leasefile);
+ else if (r != 0) {
+ /* RFC 3633 section 12.1 */
+ if (ifp->options->ia_type == D6_OPTION_IA_PD)
+ dhcp6_startrebind(ifp);
+ else
+ dhcp6_startconfirm(ifp);
+ return;
+ }
+ }
+ dhcp6_startdiscover(ifp);
+}
+
+static uint32_t
+dhcp6_findsla(void)
+{
+ uint32_t sla;
+ const struct interface *ifp;
+ const struct dhcp6_state *state;
+
+ /* Slow, but finding the lowest free SLA is needed if we get a
+ * /62 or /63 prefix from upstream */
+ for (sla = 0; sla < UINT32_MAX; sla++) {
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ state = D6_CSTATE(ifp);
+ if (state && state->sla_set && state->sla == sla)
+ break;
+ }
+ if (ifp == NULL)
+ return sla;
+ }
+
+ errno = E2BIG;
+ return 0;
+}
+
+static struct ipv6_addr *
+dhcp6_delegate_addr(struct interface *ifp, const struct ipv6_addr *prefix,
+ const struct if_sla *sla, struct interface *ifs)
+{
+ struct dhcp6_state *state;
+ struct if_sla asla;
+ struct in6_addr addr;
+ struct ipv6_addr *a, *ap, *apn;
+ char iabuf[INET6_ADDRSTRLEN];
+ const char *ia;
+
+ state = D6_STATE(ifp);
+ if (state == NULL) {
+ ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
+ state = D6_STATE(ifp);
+ if (state == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+
+ TAILQ_INIT(&state->addrs);
+ state->state = DH6S_DELEGATED;
+ state->reason = "DELEGATED6";
+ }
+
+ if (sla == NULL || sla->sla_set == 0) {
+ if (!state->sla_set) {
+ errno = 0;
+ state->sla = dhcp6_findsla();
+ if (errno) {
+ syslog(LOG_ERR, "%s: dhcp6_find_sla: %m",
+ ifp->name);
+ return NULL;
+ }
+ syslog(LOG_DEBUG, "%s: set SLA %d",
+ ifp->name, state->sla);
+ state->sla_set = 1;
+ }
+ asla.sla = state->sla;
+ asla.prefix_len = 64;
+ sla = &asla;
+ }
+
+ if (ipv6_userprefix(&prefix->prefix, prefix->prefix_len,
+ sla->sla, &addr, sla->prefix_len) == -1)
+ {
+ ia = inet_ntop(AF_INET6, &prefix->prefix.s6_addr,
+ iabuf, sizeof(iabuf));
+ syslog(LOG_ERR, "%s: invalid prefix %s/%d + %d/%d: %m",
+ ifp->name, ia, prefix->prefix_len,
+ sla->sla, sla->prefix_len);
+ return NULL;
+ }
+
+ a = calloc(1, sizeof(*a));
+ if (a == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+ a->iface = ifp;
+ a->flags = IPV6_AF_NEW | IPV6_AF_ONLINK;
+ a->dadcallback = dhcp6_dadcallback;
+ a->delegating_iface = ifs;
+ memcpy(&a->iaid, &prefix->iaid, sizeof(a->iaid));
+ a->prefix_pltime = prefix->prefix_pltime;
+ a->prefix_vltime = prefix->prefix_vltime;
+ memcpy(&a->prefix.s6_addr, &addr.s6_addr, sizeof(a->prefix.s6_addr));
+ a->prefix_len = sla->prefix_len;
+
+ if (ipv6_makeaddr(&a->addr, ifp, &a->prefix, a->prefix_len) == -1)
+ {
+ ia = inet_ntop(AF_INET6, &a->addr.s6_addr,
+ iabuf, sizeof(iabuf));
+ syslog(LOG_ERR, "%s: %m (%s/%d)", __func__, ia, a->prefix_len);
+ free(a);
+ return NULL;
+ }
+
+ /* Remove any exiting address */
+ TAILQ_FOREACH_SAFE(ap, &state->addrs, next, apn) {
+ if (IN6_ARE_ADDR_EQUAL(&ap->addr, &a->addr)) {
+ TAILQ_REMOVE(&state->addrs, ap, next);
+ /* Keep our flags */
+ a->flags |= ap->flags;
+ a->flags &= ~IPV6_AF_NEW;
+ free(ap);
+ }
+ }
+
+ ia = inet_ntop(AF_INET6, &a->addr.s6_addr, iabuf, sizeof(iabuf));
+ snprintf(a->saddr, sizeof(a->saddr), "%s/%d", ia, a->prefix_len);
+ TAILQ_INSERT_TAIL(&state->addrs, a, next);
+ return a;
+}
+
+static void
+dhcp6_delegate_prefix(struct interface *ifp)
+{
+ struct if_options *ifo;
+ struct dhcp6_state *state, *ifd_state;
+ struct ipv6_addr *ap;
+ size_t i, j, k;
+ struct if_ia *ia;
+ struct if_sla *sla;
+ struct interface *ifd;
+ uint8_t carrier_warned;
+
+ ifo = ifp->options;
+ state = D6_STATE(ifp);
+ TAILQ_FOREACH(ifd, ifaces, next) {
+ k = 0;
+ carrier_warned = 0;
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ if (ap->flags & IPV6_AF_NEW) {
+ ap->flags &= ~IPV6_AF_NEW;
+ syslog(LOG_DEBUG, "%s: delegated prefix %s",
+ ifp->name, ap->saddr);
+ }
+ for (i = 0; i < ifo->ia_len; i++) {
+ ia = &ifo->ia[i];
+ if (memcmp(ia->iaid, ap->iaid,
+ sizeof(ia->iaid)))
+ continue;
+ if (ia->sla_len == 0) {
+ /* no SLA configured, so lets
+ * automate it */
+ if (ifp == ifd)
+ continue;
+ if (ifd->carrier == LINK_DOWN) {
+ syslog(LOG_DEBUG,
+ "%s: has no carrier, cannot"
+ " delegate addresses",
+ ifd->name);
+ carrier_warned = 1;
+ break;
+ }
+ if (dhcp6_delegate_addr(ifd, ap,
+ NULL, ifp))
+ k++;
+ }
+ for (j = 0; j < ia->sla_len; j++) {
+ sla = &ia->sla[j];
+ if (strcmp(ifd->name, sla->ifname))
+ continue;
+ if (ifd->carrier == LINK_DOWN) {
+ syslog(LOG_DEBUG,
+ "%s: has no carrier, cannot"
+ " delegate addresses",
+ ifd->name);
+ carrier_warned = 1;
+ break;
+ }
+ if (dhcp6_delegate_addr(ifd, ap,
+ sla, ifp))
+ k++;
+ }
+ if (carrier_warned)
+ break;
+ }
+ if (carrier_warned)
+ break;
+ }
+ if (k && !carrier_warned) {
+ ifd_state = D6_STATE(ifd);
+ ipv6nd_probeaddrs(&ifd_state->addrs);
+ }
+ }
+
+ /* Warn about configured interfaces for delegation that do not exist */
+ for (i = 0; i < ifo->ia_len; i++) {
+ ia = &ifo->ia[i];
+ for (j = 0; j < ia->sla_len; j++) {
+ sla = &ia->sla[j];
+ for (k = 0; k < i; j++)
+ if (strcmp(sla->ifname, ia->sla[j].ifname) == 0)
+ break;
+ if (j >= i && find_interface(sla->ifname) == NULL)
+ syslog(LOG_ERR,
+ "%s: interface does not exist"
+ " for delegation",
+ sla->ifname);
+ }
+ }
+}
+
+static void
+dhcp6_find_delegates1(void *arg)
+{
+
+ dhcp6_find_delegates(arg);
+}
+
+int
+dhcp6_find_delegates(struct interface *ifp)
+{
+ struct if_options *ifo;
+ struct dhcp6_state *state;
+ struct ipv6_addr *ap;
+ size_t i, j, k;
+ struct if_ia *ia;
+ struct if_sla *sla;
+ struct interface *ifd;
+
+ k = 0;
+ TAILQ_FOREACH(ifd, ifaces, next) {
+ ifo = ifd->options;
+ if (ifo->ia_type != D6_OPTION_IA_PD)
+ continue;
+ state = D6_STATE(ifd);
+ if (state == NULL || state->state != DH6S_BOUND)
+ continue;
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ for (i = 0; i < ifo->ia_len; i++) {
+ ia = &ifo->ia[i];
+ if (memcmp(ia->iaid, ap->iaid,
+ sizeof(ia->iaid)))
+ continue;
+ for (j = 0; j < ia->sla_len; j++) {
+ sla = &ia->sla[j];
+ if (strcmp(ifp->name, sla->ifname))
+ continue;
+ if (ipv6_linklocal(ifp) == NULL) {
+ syslog(LOG_DEBUG,
+ "%s: delaying adding"
+ " delegated addresses for"
+ " LL address",
+ ifp->name);
+ ipv6_addlinklocalcallback(ifp,
+ dhcp6_find_delegates1, ifp);
+ return 1;
+ }
+ if (dhcp6_delegate_addr(ifp, ap,
+ sla, ifd))
+ k++;
+ }
+ }
+ }
+ }
+
+ if (k) {
+ syslog(LOG_INFO, "%s: adding delegated prefixes", ifp->name);
+ state = D6_STATE(ifp);
+ state->state = DH6S_DELEGATED;
+ ipv6nd_probeaddrs(&state->addrs);
+ ipv6_buildroutes();
+ }
+ return k;
+}
+
+/* ARGSUSED */
+static void
+dhcp6_handledata(__unused void *arg)
+{
+ ssize_t len;
+ size_t i;
+ struct cmsghdr *cm;
+ struct in6_pktinfo pkt;
+ struct interface *ifp;
+ const char *sfrom, *op;
+ struct dhcp6_message *r;
+ struct dhcp6_state *state;
+ const struct dhcp6_option *o;
+ const struct dhcp_opt *opt;
+ const struct if_options *ifo;
+ struct ipv6_addr *ap;
+ uint8_t has_new;
+ int error;
+ uint32_t u32;
+
+ len = recvmsg(sock, &rcvhdr, 0);
+ if (len == -1) {
+ syslog(LOG_ERR, "recvmsg: %m");
+ return;
+ }
+ sfrom = inet_ntop(AF_INET6, &from.sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN);
+ if ((size_t)len < sizeof(struct dhcp6_message)) {
+ syslog(LOG_ERR, "DHCPv6 RA packet too short from %s", sfrom);
+ return;
+ }
+
+ pkt.ipi6_ifindex = 0;
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvhdr);
+ cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvhdr, cm))
+ {
+ if (cm->cmsg_level != IPPROTO_IPV6)
+ continue;
+ switch(cm->cmsg_type) {
+ case IPV6_PKTINFO:
+ if (cm->cmsg_len == CMSG_LEN(sizeof(pkt)))
+ memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt));
+ break;
+ }
+ }
+
+ if (pkt.ipi6_ifindex == 0) {
+ syslog(LOG_ERR,
+ "DHCPv6 reply did not contain index from %s",
+ sfrom);
+ return;
+ }
+
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ if (ifp->index == (unsigned int)pkt.ipi6_ifindex)
+ break;
+ }
+ if (ifp == NULL) {
+ syslog(LOG_DEBUG,
+ "DHCPv6 reply for unexpected interface from %s", sfrom);
+ return;
+ }
+ state = D6_STATE(ifp);
+ if (state == NULL || state->send == NULL) {
+ syslog(LOG_DEBUG, "%s: DHCPv6 reply received but not running",
+ ifp->name);
+ return;
+ }
+ /* We're already bound and this message is for another machine */
+ /* XXX DELEGATED? */
+ if (state->state == DH6S_BOUND ||
+ state->state == DH6S_INFORMED)
+ return;
+
+ r = (struct dhcp6_message *)rcvhdr.msg_iov[0].iov_base;
+ if (r->xid[0] != state->send->xid[0] ||
+ r->xid[1] != state->send->xid[1] ||
+ r->xid[2] != state->send->xid[2])
+ {
+ syslog(LOG_DEBUG,
+ "%s: wrong xid 0x%02x%02x%02x"
+ " (expecting 0x%02x%02x%02x) from %s",
+ ifp->name,
+ r->xid[0], r->xid[1], r->xid[2],
+ state->send->xid[0], state->send->xid[1],
+ state->send->xid[2],
+ sfrom);
+ return;
+ }
+
+ if (dhcp6_getmoption(D6_OPTION_SERVERID, r, len) == NULL) {
+ syslog(LOG_DEBUG, "%s: no DHCPv6 server ID from %s",
+ ifp->name, sfrom);
+ return;
+ }
+
+ o = dhcp6_getmoption(D6_OPTION_CLIENTID, r, len);
+ if (o == NULL || ntohs(o->len) != duid_len ||
+ memcmp(D6_COPTION_DATA(o), duid, duid_len) != 0)
+ {
+ syslog(LOG_DEBUG, "%s: incorrect client ID from %s",
+ ifp->name, sfrom);
+ return;
+ }
+
+ ifo = ifp->options;
+ for (i = 0, opt = dhcp6_opts; i < dhcp6_opts_len; i++, opt++) {
+ if (has_option_mask(ifo->requiremask6, opt->option) &&
+ dhcp6_getmoption(opt->option, r, len) == NULL)
+ {
+ syslog(LOG_WARNING,
+ "%s: reject DHCPv6 (no option %s) from %s",
+ ifp->name, opt->var, sfrom);
+ return;
+ }
+ }
+
+ /* Authenticate the message */
+ o = dhcp6_getmoption(D6_OPTION_AUTH, r, len);
+ if (o) {
+ if (dhcp_auth_validate(&state->auth, &ifo->auth,
+ (uint8_t *)r, len, 6, r->type,
+ D6_COPTION_DATA(o), ntohs(o->len)) == NULL)
+ {
+ syslog(LOG_DEBUG, "dhcp_auth_validate: %m");
+ syslog(LOG_ERR, "%s: authentication failed from %s",
+ ifp->name, sfrom);
+ return;
+ }
+ syslog(LOG_DEBUG, "%s: validated using 0x%08" PRIu32,
+ ifp->name, state->auth.token->secretid);
+ } else if (ifo->auth.options & DHCPCD_AUTH_REQUIRE) {
+ syslog(LOG_ERR, "%s: missing authentiation from %s",
+ ifp->name, sfrom);
+ return;
+ } else if (ifo->auth.options & DHCPCD_AUTH_SEND)
+ syslog(LOG_WARNING,
+ "%s: missing authentiation from %s",
+ ifp->name, sfrom);
+
+ op = dhcp6_get_op(r->type);
+ switch(r->type) {
+ case DHCP6_REPLY:
+ switch(state->state) {
+ case DH6S_INFORM:
+ /* RFC4242 */
+ o = dhcp6_getmoption(D6_OPTION_INFO_REFRESH_TIME,
+ r, len);
+ if (o == NULL || ntohs(o->len) != sizeof(u32))
+ state->renew = IRT_DEFAULT;
+ else {
+ memcpy(&u32, D6_COPTION_DATA(o), sizeof(u32));
+ state->renew = ntohl(u32);
+ if (state->renew < IRT_MINIMUM)
+ state->renew = IRT_MINIMUM;
+ }
+ break;
+ case DH6S_CONFIRM:
+ error = dhcp6_checkstatusok(ifp, r, NULL, len);
+ /* If we got an OK status the chances are that we
+ * didn't get the IA's returned, so preserve them
+ * from our saved response */
+ if (error == 1)
+ goto recv;
+ if (error == -1 ||
+ dhcp6_validatelease(ifp, r, len, sfrom) == -1) {
+ dhcp6_startdiscover(ifp);
+ return;
+ }
+ break;
+ case DH6S_DISCOVER:
+ if (has_option_mask(ifo->requestmask6,
+ D6_OPTION_RAPID_COMMIT) &&
+ dhcp6_getmoption(D6_OPTION_RAPID_COMMIT, r, len))
+ state->state = DH6S_REQUEST;
+ else
+ op = NULL;
+ case DH6S_REQUEST: /* FALLTHROUGH */
+ case DH6S_RENEW: /* FALLTHROUGH */
+ case DH6S_REBIND:
+ if (dhcp6_validatelease(ifp, r, len, sfrom) == -1) {
+ /* PD doesn't use CONFIRM, so REBIND could
+ * throw up an invalid prefix if we
+ * changed link */
+ if (ifp->options->ia_type == D6_OPTION_IA_PD)
+ dhcp6_startdiscover(ifp);
+ return;
+ }
+ break;
+ default:
+ op = NULL;
+ }
+ break;
+ case DHCP6_ADVERTISE:
+ if (state->state != DH6S_DISCOVER) {
+ op = NULL;
+ break;
+ }
+ if (dhcp6_validatelease(ifp, r, len, sfrom) == -1)
+ return;
+ break;
+ default:
+ syslog(LOG_ERR, "%s: invalid DHCP6 type %s (%d)",
+ ifp->name, op, r->type);
+ return;
+ }
+ if (op == NULL) {
+ syslog(LOG_WARNING, "%s: invalid state for DHCP6 type %s (%d)",
+ ifp->name, op, r->type);
+ return;
+ }
+
+ if (state->recv_len < (size_t)len) {
+ free(state->recv);
+ state->recv = malloc(len);
+ if (state->recv == NULL) {
+ syslog(LOG_ERR, "%s: malloc recv: %m", ifp->name);
+ return;
+ }
+ }
+ memcpy(state->recv, r, len);
+ state->recv_len = len;
+
+ switch(r->type) {
+ case DHCP6_ADVERTISE:
+ if (state->state == DH6S_REQUEST) /* rapid commit */
+ break;
+ ap = TAILQ_FIRST(&state->addrs);
+ syslog(LOG_INFO, "%s: ADV %s from %s",
+ ifp->name, ap->saddr, sfrom);
+ if (options & DHCPCD_TEST)
+ break;
+ dhcp6_startrequest(ifp);
+ return;
+ }
+
+recv:
+ has_new = 0;
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ if (ap->flags & IPV6_AF_NEW) {
+ has_new = 1;
+ break;
+ }
+ }
+ syslog(has_new ? LOG_INFO : LOG_DEBUG,
+ "%s: %s received from %s", ifp->name, op, sfrom);
+
+ state->reason = NULL;
+ eloop_timeout_delete(NULL, ifp);
+ switch(state->state) {
+ case DH6S_INFORM:
+ state->rebind = 0;
+ state->expire = ND6_INFINITE_LIFETIME;
+ state->lowpl = ND6_INFINITE_LIFETIME;
+ state->reason = "INFORM6";
+ break;
+ case DH6S_REQUEST:
+ if (state->reason == NULL)
+ state->reason = "BOUND6";
+ /* FALLTHROUGH */
+ case DH6S_RENEW:
+ if (state->reason == NULL)
+ state->reason = "RENEW6";
+ /* FALLTHROUGH */
+ case DH6S_REBIND:
+ if (state->reason == NULL)
+ state->reason = "REBIND6";
+ /* FALLTHROUGH */
+ case DH6S_CONFIRM:
+ if (state->reason == NULL)
+ state->reason = "REBOOT6";
+ if (state->renew == 0) {
+ if (state->expire == ND6_INFINITE_LIFETIME)
+ state->renew = ND6_INFINITE_LIFETIME;
+ else
+ state->renew = state->lowpl * 0.5;
+ }
+ if (state->rebind == 0) {
+ if (state->expire == ND6_INFINITE_LIFETIME)
+ state->rebind = ND6_INFINITE_LIFETIME;
+ else
+ state->rebind = state->lowpl * 0.8;
+ }
+ break;
+ default:
+ state->reason = "UNKNOWN6";
+ break;
+ }
+
+ if (state->state != DH6S_CONFIRM) {
+ free(state->old);
+ state->old = state->new;
+ state->old_len = state->new_len;
+ state->new = state->recv;
+ state->new_len = state->recv_len;
+ state->recv = NULL;
+ state->recv_len = 0;
+ }
+
+ if (options & DHCPCD_TEST)
+ script_runreason(ifp, "TEST");
+ else {
+ if (state->state == DH6S_INFORM)
+ state->state = DH6S_INFORMED;
+ else
+ state->state = DH6S_BOUND;
+ if (state->renew && state->renew != ND6_INFINITE_LIFETIME)
+ eloop_timeout_add_sec(state->renew,
+ state->state == DH6S_INFORMED ?
+ dhcp6_startinform : dhcp6_startrenew, ifp);
+ if (state->rebind && state->rebind != ND6_INFINITE_LIFETIME)
+ eloop_timeout_add_sec(state->rebind,
+ dhcp6_startrebind, ifp);
+ if (state->expire && state->expire != ND6_INFINITE_LIFETIME)
+ eloop_timeout_add_sec(state->expire,
+ dhcp6_startexpire, ifp);
+ if (ifp->options->ia_type == D6_OPTION_IA_PD)
+ dhcp6_delegate_prefix(ifp);
+ ipv6nd_probeaddrs(&state->addrs);
+ if (state->state == DH6S_INFORMED)
+ syslog(has_new ? LOG_INFO : LOG_DEBUG,
+ "%s: refresh in %"PRIu32" seconds",
+ ifp->name, state->renew);
+ else if (state->renew || state->rebind)
+ syslog(has_new ? LOG_INFO : LOG_DEBUG,
+ "%s: renew in %"PRIu32" seconds,"
+ " rebind in %"PRIu32" seconds",
+ ifp->name, state->renew, state->rebind);
+ ipv6_buildroutes();
+ dhcp6_writelease(ifp);
+
+ len = 1;
+ /* If all addresses have completed DAD run the script */
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ if (ap->flags & IPV6_AF_ONLINK) {
+ if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
+ ipv6_findaddr(ap->iface, &ap->addr))
+ ap->flags |= IPV6_AF_DADCOMPLETED;
+ if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
+ len = 0;
+ break;
+ }
+ }
+ }
+ if (len) {
+ script_runreason(ifp, state->reason);
+ daemonise();
+ } else
+ syslog(LOG_DEBUG,
+ "%s: waiting for DHCPv6 DAD to complete",
+ ifp->name);
+ }
+
+ if (options & DHCPCD_TEST ||
+ (ifp->options->options & DHCPCD_INFORM &&
+ !(options & DHCPCD_MASTER)))
+ {
+#ifdef DEBUG_MEMORY
+ dhcp6_free(ifp);
+#endif
+ exit(EXIT_SUCCESS);
+ }
+}
+
+static int
+dhcp6_open(void)
+{
+ struct sockaddr_in6 sa;
+ int n;
+
+ if (sndbuf == NULL && dhcp6_init() == -1)
+ return -1;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(DHCP6_CLIENT_PORT);
+#ifdef BSD
+ sa.sin6_len = sizeof(sa);
+#endif
+
+ sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock == -1)
+ return -1;
+
+ n = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ &n, sizeof(n)) == -1)
+ goto errexit;
+
+ n = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
+ &n, sizeof(n)) == -1)
+ goto errexit;
+
+#ifdef SO_REUSEPORT
+ n = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
+ &n, sizeof(n)) == -1)
+ goto errexit;
+#endif
+
+ if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) == -1)
+ goto errexit;
+
+ n = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &n, sizeof(n)) == -1)
+ goto errexit;
+
+ if (set_cloexec(sock) == -1 || set_nonblock(sock) == -1)
+ goto errexit;
+
+ eloop_event_add(sock, dhcp6_handledata, NULL);
+
+ return 0;
+
+errexit:
+ close(sock);
+ return -1;
+}
+
+static void
+dhcp6_start1(void *arg)
+{
+ struct interface *ifp = arg;
+ struct if_options *ifo = ifp->options;
+ struct dhcp6_state *state;
+ size_t i;
+ const struct dhcp_compat *dhc;
+
+ state = D6_STATE(ifp);
+ /* Match any DHCPv4 opton to DHCPv6 options if given for easy
+ * configuration */
+ for (i = 0; i < sizeof(ifo->requestmask6); i++) {
+ if (ifo->requestmask6[i] != '\0')
+ break;
+ }
+ if (i == sizeof(ifo->requestmask6)) {
+ for (dhc = dhcp_compats; dhc->dhcp_opt; dhc++) {
+ if (has_option_mask(ifo->requestmask, dhc->dhcp_opt))
+ add_option_mask(ifo->requestmask6,
+ dhc->dhcp6_opt);
+ }
+ if (ifo->fqdn != FQDN_DISABLE ||
+ ifo->options & DHCPCD_HOSTNAME)
+ add_option_mask(ifo->requestmask6, D6_OPTION_FQDN);
+ }
+
+ if (state->state == DH6S_INFORM) {
+ add_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME);
+ dhcp6_startinform(ifp);
+ } else {
+ del_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME);
+ dhcp6_startinit(ifp);
+ }
+}
+
+int
+dhcp6_start(struct interface *ifp, enum DH6S init_state)
+{
+ struct dhcp6_state *state;
+
+ state = D6_STATE(ifp);
+ if (state) {
+ if (state->state == DH6S_DELEGATED) {
+ dhcp6_find_delegates(ifp);
+ return 0;
+ }
+ if (state->state == DH6S_INFORMED &&
+ init_state == DH6S_INFORM)
+ {
+ dhcp6_startinform(ifp);
+ return 0;
+ }
+ /* We're already running DHCP6 */
+ /* XXX: What if the managed flag changes? */
+ return 0;
+ }
+
+ if (!(ifp->options->options & DHCPCD_DHCP6))
+ return 0;
+
+ if (sock == -1 && dhcp6_open() == -1)
+ return -1;
+
+ ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
+ state = D6_STATE(ifp);
+ if (state == NULL)
+ return -1;
+
+ TAILQ_INIT(&state->addrs);
+ if (dhcp6_find_delegates(ifp))
+ return 0;
+
+ state->state = init_state;
+ snprintf(state->leasefile, sizeof(state->leasefile),
+ LEASEFILE6, ifp->name);
+
+ if (ipv6_linklocal(ifp) == NULL) {
+ syslog(LOG_DEBUG,
+ "%s: delaying DHCPv6 soliciation for LL address",
+ ifp->name);
+ ipv6_addlinklocalcallback(ifp, dhcp6_start1, ifp);
+ return 0;
+ }
+
+ dhcp6_start1(ifp);
+ return 0;
+}
+
+void
+dhcp6_reboot(struct interface *ifp)
+{
+ struct dhcp6_state *state;
+
+ state = D6_STATE(ifp);
+ if (state) {
+ switch (state->state) {
+ case DH6S_BOUND:
+ dhcp6_startrebind(ifp);
+ break;
+ case DH6S_INFORMED:
+ dhcp6_startinform(ifp);
+ break;
+ default:
+ dhcp6_startdiscover(ifp);
+ break;
+ }
+ }
+}
+
+static void
+dhcp6_freedrop(struct interface *ifp, int drop, const char *reason)
+{
+ struct dhcp6_state *state;
+
+ eloop_timeout_delete(NULL, ifp);
+
+ /*
+ * As the interface is going away from dhcpcd we need to
+ * remove the delegated addresses, otherwise we lose track
+ * of which interface is delegating as we remeber it by pointer.
+ * So if we need to change this behaviour, we need to change
+ * how we remember which interface delegated.
+ * To make it more interesting, on some OS's with PPP links
+ * there is no guarantee the delegating interface will have
+ * the same name or index so think very hard before changing
+ * this.
+ */
+ if (ifp->options &&
+ ifp->options->options & (DHCPCD_STOPPING | DHCPCD_RELEASE) &&
+ (ifp->options->options & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
+ (DHCPCD_EXITING | DHCPCD_PERSISTENT))
+ dhcp6_delete_delegates(ifp);
+
+ state = D6_STATE(ifp);
+ if (state) {
+ if (ifp->options->options & DHCPCD_RELEASE) {
+ if (ifp->carrier != LINK_DOWN)
+ dhcp6_startrelease(ifp);
+ unlink(state->leasefile);
+ }
+ dhcp6_freedrop_addrs(ifp, drop, NULL);
+ if (drop && state->new &&
+ (ifp->options->options &
+ (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
+ (DHCPCD_EXITING | DHCPCD_PERSISTENT))
+ {
+ if (reason == NULL)
+ reason = "STOP6";
+ script_runreason(ifp, reason);
+ }
+ free(state->send);
+ free(state->recv);
+ free(state->new);
+ free(state->old);
+ free(state->auth.reconf);
+ free(state);
+ ifp->if_data[IF_DATA_DHCP6] = NULL;
+ }
+
+ /* If we don't have any more DHCP6 enabled interfaces,
+ * close the global socket */
+ if (ifaces) {
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ if (D6_STATE(ifp))
+ break;
+ }
+ }
+ if (ifp == NULL && sock != -1) {
+ close(sock);
+ eloop_event_delete(sock);
+ sock = -1;
+ }
+}
+
+void
+dhcp6_drop(struct interface *ifp, const char *reason)
+{
+
+ dhcp6_freedrop(ifp, 1, reason);
+}
+
+void
+dhcp6_free(struct interface *ifp)
+{
+
+ dhcp6_freedrop(ifp, 0, NULL);
+}
+
+void
+dhcp6_handleifa(int cmd, const char *ifname,
+ const struct in6_addr *addr, int flags)
+{
+ struct interface *ifp;
+ struct dhcp6_state *state;
+
+ if (ifaces == NULL)
+ return;
+
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ state = D6_STATE(ifp);
+ if (state == NULL || strcmp(ifp->name, ifname))
+ continue;
+ ipv6_handleifa_addrs(cmd, &state->addrs, addr, flags);
+ }
+
+}
+
+ssize_t
+dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
+ const struct dhcp6_message *m, ssize_t len)
+{
+ const struct dhcp6_state *state;
+ const struct if_options *ifo;
+ struct dhcp_opt *opt, *vo;
+ const struct dhcp6_option *o;
+ size_t i, n;
+ uint16_t ol, oc;
+ char *v, *val, *pfx;
+ const struct ipv6_addr *ap;
+ uint32_t en;
+
+ state = D6_CSTATE(ifp);
+ n = 0;
+ ifo = ifp->options;
+
+ /* Zero our indexes */
+ if (env) {
+ for (i = 0, opt = dhcp6_opts; i < dhcp6_opts_len; i++, opt++)
+ dhcp_zero_index(opt);
+ for (i = 0, opt = ifp->options->dhcp6_override;
+ i < ifp->options->dhcp6_override_len;
+ i++, opt++)
+ dhcp_zero_index(opt);
+ for (i = 0, opt = vivso; i < vivso_len; i++, opt++)
+ dhcp_zero_index(opt);
+ i = strlen(prefix) + strlen("_dhcp6") + 1;
+ pfx = malloc(i);
+ if (pfx == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return 0;
+ }
+ snprintf(pfx, i, "%s_dhcp6", prefix);
+ } else
+ pfx = NULL;
+
+ /* Unlike DHCP, DHCPv6 options *may* occur more than once.
+ * There is also no provision for option concatenation unlike DHCP. */
+ for (o = D6_CFIRST_OPTION(m);
+ len > (ssize_t)sizeof(*o);
+ o = D6_CNEXT_OPTION(o))
+ {
+ ol = ntohs(o->len);
+ len -= sizeof(*o) + ol;
+ if (len < 0) {
+ errno = EINVAL;
+ break;
+ }
+ oc = ntohs(o->code);
+ if (has_option_mask(ifo->nomask6, oc))
+ continue;
+ for (i = 0, opt = ifo->dhcp6_override;
+ i < ifo->dhcp6_override_len;
+ i++, opt++)
+ if (opt->option == oc)
+ break;
+ if (i == ifo->dhcp6_override_len &&
+ oc == D6_OPTION_VENDOR_OPTS &&
+ ol > sizeof(en))
+ {
+ memcpy(&en, D6_COPTION_DATA(o), sizeof(en));
+ en = ntohl(en);
+ vo = vivso_find(en, ifp);
+ } else
+ vo = NULL;
+ if (i == ifo->dhcp6_override_len) {
+ for (i = 0, opt = dhcp6_opts;
+ i < dhcp6_opts_len;
+ i++, opt++)
+ if (opt->option == oc)
+ break;
+ if (i == dhcp6_opts_len)
+ opt = NULL;
+ }
+ if (opt) {
+ n += dhcp_envoption(env == NULL ? NULL : &env[n],
+ pfx, ifp->name,
+ opt, dhcp6_getoption, D6_COPTION_DATA(o), ol);
+ }
+ if (vo) {
+ n += dhcp_envoption(env == NULL ? NULL : &env[n],
+ pfx, ifp->name,
+ vo, dhcp6_getoption,
+ D6_COPTION_DATA(o) + sizeof(en),
+ ol - sizeof(en));
+ }
+ }
+ free(pfx);
+
+ /* It is tempting to remove this section.
+ * However, we need it at least for Delegated Prefixes
+ * (they don't have a DHCPv6 message to parse to get the addressses)
+ * and it's easier for shell scripts to see which addresses have
+ * been added */
+ if (TAILQ_FIRST(&state->addrs)) {
+ if (env) {
+ if (ifo->ia_type == D6_OPTION_IA_PD) {
+ i = strlen(prefix) +
+ strlen("_dhcp6_prefix=");
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ i += strlen(ap->saddr) + 1;
+ }
+ v = val = env[n] = malloc(i);
+ if (v == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return -1;
+ }
+ v += snprintf(val, i, "%s_dhcp6_prefix=",
+ prefix);
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ strcpy(v, ap->saddr);
+ v += strlen(ap->saddr);
+ *v++ = ' ';
+ }
+ *--v = '\0';
+ } else {
+ i = strlen(prefix) +
+ strlen("_dhcp6_ip_address=");
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ i += strlen(ap->saddr) + 1;
+ }
+ v = val = env[n] = malloc(i);
+ if (v == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return -1;
+ }
+ v += snprintf(val, i, "%s_dhcp6_ip_address=",
+ prefix);
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ strcpy(v, ap->saddr);
+ v += strlen(ap->saddr);
+ *v++ = ' ';
+ }
+ *--v = '\0';
+ }
+ }
+ n++;
+ }
+
+ return n;
+}
diff --git a/dhcpcd/dhcp6.h b/dhcpcd/dhcp6.h
new file mode 100644
index 00000000..4c53d427
--- /dev/null
+++ b/dhcpcd/dhcp6.h
@@ -0,0 +1,245 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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 DHCP6_H
+#define DHCP6_H
+
+#include "dhcpcd.h"
+
+#define IN6ADDR_LINKLOCAL_ALLDHCP_INIT \
+ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 }}}
+
+/* UDP port numbers for DHCP */
+#define DHCP6_CLIENT_PORT 546
+#define DHCP6_SERVER_PORT 547
+
+/* DHCP message type */
+#define DHCP6_SOLICIT 1
+#define DHCP6_ADVERTISE 2
+#define DHCP6_REQUEST 3
+#define DHCP6_CONFIRM 4
+#define DHCP6_RENEW 5
+#define DHCP6_REBIND 6
+#define DHCP6_REPLY 7
+#define DHCP6_RELEASE 8
+#define DHCP6_DECLINE 9
+#define DHCP6_RECONFIGURE 10
+#define DHCP6_INFORMATION_REQ 11
+#define DHCP6_RELAY_FLOW 12
+#define DHCP6_RELAY_REPL 13
+
+#define D6_OPTION_CLIENTID 1
+#define D6_OPTION_SERVERID 2
+#define D6_OPTION_IA_NA 3
+#define D6_OPTION_IA_TA 4
+#define D6_OPTION_ORO 6
+#define D6_OPTION_IA_ADDR 5
+#define D6_OPTION_PREFERENCE 7
+#define D6_OPTION_ELAPSED 8
+#define D6_OPTION_AUTH 11
+#define D6_OPTION_UNICAST 12
+#define D6_OPTION_STATUS_CODE 13
+#define D6_OPTION_RAPID_COMMIT 14
+#define D6_OPTION_VENDOR_CLASS 16
+#define D6_OPTION_VENDOR_OPTS 17
+#define D6_OPTION_SIP_SERVERS_NAME 21
+#define D6_OPTION_SIP_SERVERS_ADDRESS 22
+#define D6_OPTION_DNS_SERVERS 23
+#define D6_OPTION_DOMAIN_LIST 24
+#define D6_OPTION_IA_PD 25
+#define D6_OPTION_IAPREFIX 26
+#define D6_OPTION_NIS_SERVERS 27
+#define D6_OPTION_NISP_SERVERS 28
+#define D6_OPTION_NIS_DOMAIN_NAME 29
+#define D6_OPTION_NISP_DOMAIN_NAME 30
+#define D6_OPTION_SNTP_SERVERS 31
+#define D6_OPTION_INFO_REFRESH_TIME 32
+#define D6_OPTION_BCMS_SERVER_D 33
+#define D6_OPTION_BCMS_SERVER_A 34
+#define D6_OPTION_FQDN 39
+#define D6_OPTION_POSIX_TIMEZONE 41
+#define D6_OPTION_TZDB_TIMEZONE 42
+
+#define D6_FQDN_PTR 0x00
+#define D6_FQDN_BOTH 0x01
+#define D6_FQDN_NONE 0x04
+
+#include "dhcp.h"
+#include "ipv6.h"
+
+struct dhcp6_message {
+ uint8_t type;
+ uint8_t xid[3];
+ /* followed by options */
+} __packed;
+
+struct dhcp6_option {
+ uint16_t code;
+ uint16_t len;
+ /* followed by data */
+} __packed;
+
+#define D6_STATUS_OK 0
+#define D6_STATUS_FAIL 1
+#define D6_STATUS_NOADDR 2
+#define D6_STATUS_NOBINDING 3
+#define D6_STATUS_NOTONLINK 4
+#define D6_STATUS_USEMULTICAST 5
+
+#define SOL_MAX_DELAY 1
+#define SOL_TIMEOUT 1
+#define SOL_MAX_RT 120
+#define REQ_TIMEOUT 1
+#define REQ_MAX_RT 30
+#define REQ_MAX_RC 10
+#define CNF_MAX_DELAY 1
+#define CNF_TIMEOUT 1
+#define CNF_MAX_RT 4
+#define CNF_MAX_RD 10
+#define REN_TIMEOUT 10
+#define REN_MAX_RT 600
+#define REB_TIMEOUT 10
+#define REB_MAX_RT 600
+#define INF_MAX_DELAY 1
+#define INF_TIMEOUT 1
+#define INF_MAX_RT 120
+#define REL_TIMEOUT 1
+#define REL_MAX_RC 5
+#define DEC_TIMEOUT 1
+#define DEC_MAX_RC 5
+#define REC_TIMEOUT 2
+#define REC_MAX_RC 8
+#define HOP_COUNT_LIMIT 32
+
+/* RFC4242 3.1 */
+#define IRT_DEFAULT 86400
+#define IRT_MINIMUM 600
+
+#define DHCP6_RAND_MIN -100
+#define DHCP6_RAND_MAX 100
+
+enum DH6S {
+ DH6S_INIT,
+ DH6S_DISCOVER,
+ DH6S_REQUEST,
+ DH6S_BOUND,
+ DH6S_RENEW,
+ DH6S_REBIND,
+ DH6S_CONFIRM,
+ DH6S_INFORM,
+ DH6S_INFORMED,
+ DH6S_RENEW_REQUESTED,
+ DH6S_PROBE,
+ DH6S_DELEGATED,
+ DH6S_RELEASE
+};
+
+struct dhcp6_state {
+ enum DH6S state;
+ time_t start_uptime;
+
+ /* Message retransmission timings */
+ struct timeval RT;
+ int IMD;
+ int RTC;
+ int IRT;
+ int MRC;
+ int MRT;
+ void (*MRCcallback)(void *);
+
+ struct dhcp6_message *send;
+ size_t send_len;
+ struct dhcp6_message *recv;
+ size_t recv_len;
+ struct dhcp6_message *new;
+ size_t new_len;
+ struct dhcp6_message *old;
+ size_t old_len;
+
+ uint32_t renew;
+ uint32_t rebind;
+ uint32_t expire;
+ struct in6_addr unicast;
+ struct ipv6_addrhead addrs;
+ uint32_t lowpl;
+ uint32_t sla;
+ uint8_t sla_set;
+ char leasefile[PATH_MAX];
+ const char *reason;
+
+ struct authstate auth;
+};
+
+#define D6_STATE(ifp) \
+ ((struct dhcp6_state *)(ifp)->if_data[IF_DATA_DHCP6])
+#define D6_CSTATE(ifp) \
+ ((const struct dhcp6_state *)(ifp)->if_data[IF_DATA_DHCP6])
+#define D6_STATE_RUNNING(ifp) (D6_STATE((ifp)) && D6_STATE((ifp))->new)
+#define D6_FIRST_OPTION(m) \
+ ((struct dhcp6_option *) \
+ ((uint8_t *)(m) + sizeof(struct dhcp6_message)))
+#define D6_NEXT_OPTION(o) \
+ ((struct dhcp6_option *) \
+ (((uint8_t *)o) + sizeof(struct dhcp6_option) + ntohs((o)->len)))
+#define D6_OPTION_DATA(o) \
+ ((uint8_t *)(o) + sizeof(struct dhcp6_option))
+#define D6_CFIRST_OPTION(m) \
+ ((const struct dhcp6_option *) \
+ ((const uint8_t *)(m) + sizeof(struct dhcp6_message)))
+#define D6_CNEXT_OPTION(o) \
+ ((const struct dhcp6_option *) \
+ (((const uint8_t *)o) + sizeof(struct dhcp6_option) + ntohs((o)->len)))
+#define D6_COPTION_DATA(o) \
+ ((const uint8_t *)(o) + sizeof(struct dhcp6_option))
+
+#ifdef INET6
+extern struct dhcp_opt *dhcp6_opts;
+extern size_t dhcp6_opts_len;
+
+void dhcp6_printoptions(void);
+int dhcp6_addrexists(const struct ipv6_addr *);
+int dhcp6_find_delegates(struct interface *);
+int dhcp6_start(struct interface *, enum DH6S);
+void dhcp6_reboot(struct interface *);
+ssize_t dhcp6_env(char **, const char *, const struct interface *,
+ const struct dhcp6_message *, ssize_t);
+void dhcp6_free(struct interface *);
+void dhcp6_handleifa(int, const char *, const struct in6_addr *addr, int);
+void dhcp6_drop(struct interface *, const char *);
+#else
+#define dhcp6_printoptions()
+#define dhcp6_addrexists(a) 0
+#define dhcp6_find_delegates(a) 0
+#define dhcp6_start(a, b) 0
+#define dhcp6_reboot(a)
+#define dhcp6_env(a, b, c, d, e)
+#define dhcp6_free(a)
+#define dhcp6_drop(a, b)
+#endif
+
+#endif
diff --git a/dhcpcd/dhcpcd-definitions.conf b/dhcpcd/dhcpcd-definitions.conf
new file mode 100644
index 00000000..84260861
--- /dev/null
+++ b/dhcpcd/dhcpcd-definitions.conf
@@ -0,0 +1,282 @@
+# Copyright (c) 2006-2014 Roy Marples
+# All rights reserved
+
+# DHCP option definitions for dhcpcd(8)
+# These are used to translate DHCP options into shell variables
+# for use in dhcpcd-run-hooks(8)
+# See dhcpcd.conf(5) for details
+
+##############################################################################
+# DHCP RFC2132 options unless otheriwse stated
+define 1 request ipaddress subnet_mask
+# RFC3442 states that the CSR has to come before all other routes
+# For completeness we also specify static routes then routers
+define 121 rfc3442 classless_static_routes
+# Option 249 is an IANA assigned private number used by Windows DHCP servers
+# to provide the exact same information as option 121, classless static routes
+define 249 rfc3442 ms_classless_static_routes
+define 33 request array ipaddress static_routes
+define 3 request array ipaddress routers
+define 2 uint32 time_offset
+define 4 array ipaddress time_servers
+define 5 array ipaddress ien116_name_servers
+define 6 array ipaddress domain_name_servers
+define 7 array ipaddress log_servers
+define 8 array ipaddress cookie_servers
+define 9 array ipaddress lpr_servers
+define 10 array ipaddress impress_servers
+define 11 array ipaddress resource_location_servers
+define 12 string host_name
+define 13 uint16 boot_size
+define 14 string merit_dump
+define 15 string domain_name
+define 16 ipaddress swap_server
+define 17 string root_path
+define 18 string extensions_path
+define 19 byte ip_forwarding
+define 20 byte non_local_source_routing
+define 21 array ipaddress policy_filter
+define 22 int16 max_dgram_reassembly
+define 23 uint16 default_ip_ttl
+define 24 uint32 path_mtu_aging_timeout
+define 25 array uint16 path_mtu_plateau_table
+define 26 uint16 interface_mtu
+define 27 byte all_subnets_local
+define 28 request ipaddress broadcast_address
+define 29 byte perform_mask_discovery
+define 30 byte mask_supplier
+define 31 byte router_discovery
+define 32 ipaddress router_solicitation_address
+define 34 byte trailer_encapsulation
+define 35 uint32 arp_cache_timeout
+define 36 uint16 ieee802_3_encapsulation
+define 37 byte default_tcp_ttl
+define 38 uint32 tcp_keepalive_interval
+define 39 byte tcp_keepalive_garbage
+define 40 string nis_domain
+define 41 array ipaddress nis_servers
+define 42 array ipaddress ntp_servers
+define 43 string vendor_encapsulated_options
+define 44 array ipaddress netbios_name_servers
+define 45 ipaddress netbios_dd_server
+define 46 byte netbios_node_type
+define 47 string netbios_scope
+define 48 array ipaddress font_servers
+define 49 array ipaddress x_display_manager
+define 50 ipaddress dhcp_requested_address
+define 51 request uint32 dhcp_lease_time
+define 52 byte dhcp_option_overload
+define 53 byte dhcp_message_type
+define 54 ipaddress dhcp_server_identifier
+define 55 array byte dhcp_parameter_request_list
+define 56 string dhcp_message
+define 57 uint16 dhcp_max_message_size
+define 58 request uint32 dhcp_renewal_time
+define 59 request uint32 dhcp_rebinding_time
+define 60 binhex vendor_class_identifier
+define 61 binhex dhcp_client_identifier
+define 64 string nisplus_domain
+define 65 array ipaddress nisplus_servers
+define 66 string tftp_server_name
+define 67 string bootfile_name
+define 68 array ipaddress mobile_ip_home_agent
+define 69 array ipaddress smtp_server
+define 70 array ipaddress pop_server
+define 71 array ipaddress nntp_server
+define 72 array ipaddress www_server
+define 73 array ipaddress finger_server
+define 74 array ipaddress irc_server
+define 75 array ipaddress streettalk_server
+define 76 array ipaddress streettalk_directory_assistance_server
+
+# DHCP User Class, RFC3004
+define 77 string user_class
+
+# DHCP Rapid Commit, RFC4039
+define 80 norequest flag rapid_commit
+
+# DHCP Fully Qualified Domain Name, RFC4702
+define 81 embed fqdn
+embed byte flags
+embed byte rcode1
+embed byte rcode2
+embed domain fqdn
+
+# DHCP Novell Directory Services, RFC2241
+define 85 array ipaddress nds_servers
+define 86 string nds_tree_name
+define 87 string nds_context
+
+# DHCP Broadcast and Multicast Control Server, RFC4280
+define 88 domain bcms_controller_names
+define 89 array ipaddress bcms_controller_address
+
+# DHCP Authentication, RFC3118
+define 90 embed auth
+embed byte protocol
+embed byte algorithm
+embed byte rdm
+embed binhex:8 replay
+embed binhex information
+
+# DHCP Leasequery, RFC4388
+define 91 uint32 client_last_transaction_time
+define 92 array ipaddress associated_ip
+
+# DHCP The Open Group's User Authentication Protocol, RFC2485
+define 98 string uap_servers
+
+# DHCP Timezone, RFC4883
+define 100 string posix_timezone
+define 101 string tzdb_timezone
+
+# DHCP Subnet Selection, RFC3011
+define 118 ipaddress subnet_selection
+
+# DHCP Domain Search, RFC3397
+define 119 domain domain_search
+
+# DHCP Session Initiated Protocol Servers, RFC3361
+define 120 rfc3361 sip_server
+
+# DHCP CableLabs Client, RFC3495
+define 122 encap tsp
+encap 1 ipaddress dhcp_server
+encap 2 ipaddress dhcp_secondary_server
+encap 3 rfc3361 provisioning_server
+encap 4 embed as_req_as_rep_backoff
+embed uint32 nominal
+embed uint32 maximum
+embed uint32 retry
+encap 5 embed ap_req_ap_rep_backoff
+embed uint32 nominal
+embed uint32 maximum
+embed uint32 retry
+encap 6 domain kerberos_realm
+encap 7 byte ticket_granting_server_utilization
+encap 8 byte provisioning_timer
+
+# DHCP Vendor-Identifying Vendor Options, RFC3925
+define 124 binhex vivco
+define 125 embed vivso
+embed uint32 enterprise_number
+# Vendor options are shared between DHCP/DHCPv6
+# Their code is matched to the enterprise number defined above
+# see the end of this file for an example
+
+# DHCP IPv6 Rapid Deployment on IPv4 Infrastructures, RFC5969
+define 212 rfc5969 sixrd
+
+##############################################################################
+# DHCPv6 options, RFC3315
+define6 1 binhex client_id
+define6 2 binhex server_id
+
+define6 3 norequest index embed ia_na
+embed binhex:4 iaid
+embed uint32 t1
+embed uint32 t2
+encap 5 option
+encap 13 option
+
+define6 4 norequest index embed ia_ta
+embed uint32 iaid
+encap 5 option
+encap 13 option
+
+define6 5 norequest index embed ia_addr
+embed ip6address ia_addr
+embed uint32 pltime
+embed uint32 vltime
+encap 13 option
+
+define6 6 array uint16 option_request
+define6 7 byte preference
+define6 8 uint16 elased_time
+define6 9 binhex dhcp_relay_msg
+
+define6 11 embed auth
+embed byte protocol
+embed byte algorithm
+embed byte rdm
+embed binhex:8 replay
+embed binhex information
+
+define6 12 ip6address unicast
+
+define6 13 norequest embed status_code
+embed uint16 status_code
+embed string message
+
+define6 14 norequest flag rapid_commit
+define6 15 binhex user_class
+
+define6 16 binhex vivco
+define6 17 embed vivso
+embed uint32 enterprise_number
+# Vendor options are shared between DHCP/DHCPv6
+# Their code is matched to the enterprise number defined above
+# See the end of this file for an example
+
+define6 18 binhex interface_id
+define6 19 byte reconfigure_msg
+define6 20 flag reconfigure_accept
+
+# DHCPv6 Session Initiation Protocol Options, RFC3319
+define6 21 domain sip_servers_names
+define6 22 array ip6address sip_servers_addresses
+
+# DHCPv6 DNS Configuration Options, RFC3646
+define6 23 array ip6address name_servers
+define6 24 domain domain_search
+
+# DHCPv6 Prefix Options, RFC6603
+define6 25 norequest index embed ia_pd
+embed binhex:4 iaid
+embed uint32 t1
+embed uint32 t2
+encap 26 option
+
+define6 26 index embed prefix
+embed uint32 pltime
+embed uint32 vltime
+embed ip6address prefix
+encap 13 option
+
+# DHCPv6 Network Information Service Options, RFC3898
+define6 27 array ip6address nis_servers
+define6 28 array ip6address nisp_servers
+define6 29 domain nis_domain_name
+define6 30 domain nisp_domain_name
+
+# DHCPv6 Simple Network Time Protocol Servers Option, RFC4075
+define6 31 array ip6address sntp_servers
+
+# DHCPv6 Information Refresh Time, RFC4242
+define6 32 uint32 info_refresh_time
+
+# DHCPv6 Broadcast and Multicast Control Server, RFC4280
+define6 33 domain bcms_server_d
+define6 34 array ip6address bcms_server_a
+
+# DHCPv6 Fully Qualified Domain Name, RFC4704
+define6 39 embed fqdn
+embed byte flags
+embed domain fqdn
+
+# DHCPv6 Timezone options, RFC4883
+define6 41 string posix_timezone
+define6 42 string tzdb_timezone
+
+# DHCPv6 Network Time Protocol Server, RFC5908
+define6 56 encap ntp_server
+encap 1 ip6address addr
+encap 2 ip6address mcast_addr
+encap 3 ip6address fqdn
+
+##############################################################################
+# Vendor-Identifying Vendor Options
+# An example:
+#vendopt 12345 encap frobozzco
+#encap 1 string maze_location
+#encap 2 byte grue_probability
diff --git a/dhcpcd/dhcpcd-embedded.h.in b/dhcpcd/dhcpcd-embedded.h.in
new file mode 100644
index 00000000..07f81adf
--- /dev/null
+++ b/dhcpcd/dhcpcd-embedded.h.in
@@ -0,0 +1,31 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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 INITDEFINES @INITDEFINES@
+#define INITDEFINE6S @INITDEFINE6S@
+
+extern const char * const dhcpcd_embedded_conf[];
diff --git a/dhcpcd/dhcpcd-hooks/01-test b/dhcpcd/dhcpcd-hooks/01-test
new file mode 100644
index 00000000..ac71f089
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/01-test
@@ -0,0 +1,6 @@
+# Just echo our DHCP options we have
+
+if [ "$reason" = "TEST" ]; then
+ set | grep "^\(interface\|metric\|pid\|reason\|skip_hooks\)=" | sort
+ set | grep "^\(new_\|old_\|ra_count=\|ra[0-9]*_\)" | sort
+fi
diff --git a/dhcpcd/dhcpcd-hooks/02-dump b/dhcpcd/dhcpcd-hooks/02-dump
new file mode 100644
index 00000000..cbb46eae
--- /dev/null
+++ b/dhcpcd/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/dhcpcd-hooks/10-mtu b/dhcpcd/dhcpcd-hooks/10-mtu
new file mode 100644
index 00000000..f8657cbd
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/10-mtu
@@ -0,0 +1,38 @@
+# Configure the MTU for the interface
+
+mtu_dir="$state_dir/mtu"
+
+set_mtu()
+{
+ local mtu=$1
+
+ if [ -w /sys/class/net/$interface/mtu ]; then
+ echo "$mtu" >/sys/class/net/$interface/mtu
+ else
+ ifconfig "$interface" mtu "$mtu"
+ fi
+}
+
+if [ "$reason" = PREINIT -a -e "$mtu_dir/$interface" ]; then
+ rm "$mtu_dir/$interface"
+elif [ -n "$new_interface_mtu" ] && $if_up; then
+ # The smalled MTU dhcpcd can work with is 576
+ if [ "$new_interface_mtu" -ge 576 ]; then
+ if set_mtu "$new_interface_mtu"; then
+ syslog info "MTU set to $new_interface_mtu"
+ # Save the MTU so we can restore it later
+ if [ ! -e "$mtu_dir/$interface" ]; then
+ mkdir -p "$mtu_dir"
+ echo "$ifmtu" > "$mtu_dir/$interface"
+ fi
+ fi
+ fi
+elif [ -e "$mtu_dir/$interface" ]; then
+ if $if_up || $if_down; then
+ # No MTU in this state, so restore the prior MTU
+ mtu=$(cat "$mtu_dir/$interface")
+ syslog info "MTU restored to $mtu"
+ set_mtu "$mtu"
+ rm "$mtu_dir/$interface"
+ fi
+fi
diff --git a/dhcpcd/dhcpcd-hooks/10-wpa_supplicant b/dhcpcd/dhcpcd-hooks/10-wpa_supplicant
new file mode 100644
index 00000000..a0dc018e
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/10-wpa_supplicant
@@ -0,0 +1,113 @@
+# Start, reconfigure and stop wpa_supplicant per wireless interface.
+# This is needed because wpa_supplicant lacks hotplugging of any kind
+# and the user should not be expected to have to wire it into their system
+# if the base system doesn't do this itself.
+
+if [ -z "$wpa_supplicant_conf" ]; then
+ for x in \
+ /etc/wpa_supplicant/wpa_supplicant-"$interface".conf \
+ /etc/wpa_supplicant/wpa_supplicant.conf \
+ /etc/wpa_supplicant-"$interface".conf \
+ /etc/wpa_supplicant.conf \
+ ; do
+ if [ -s "$x" ]; then
+ wpa_supplicant_conf="$x"
+ break
+ fi
+ done
+fi
+: ${wpa_supplicant_conf:=/etc/wpa_supplicant.conf}
+
+wpa_supplcant_ctrldir() {
+ local dir
+
+ dir=$(key_get_value "[[:space:]]*ctrl_interface=" \
+ "$wpa_supplicant_conf")
+ dir=$(trim "$dir")
+ case "$dir" in
+ DIR=*)
+ dir=${dir##DIR=}
+ dir=${dir%%[[:space:]]GROUP=*}
+ dir=$(trim "$dir")
+ ;;
+ esac
+ printf %s "$dir"
+}
+
+wpa_supplicant_start()
+{
+ local dir err errn
+
+ # Pre flight checks
+ if [ ! -s "$wpa_supplicant_conf" ]; then
+ syslog warn \
+ "$wpa_supplicant_conf does not exist"
+ syslog warn "not interacting with wpa_supplicant(8)"
+ return 1
+ fi
+ dir=$(wpa_supplcant_ctrldir)
+ if [ -z "$dir" ]; then
+ syslog warn \
+ "ctrl_interface not defined in $wpa_supplicant_conf"
+ syslog warn "not interacting with wpa_supplicant(8)"
+ return 1
+ fi
+
+ wpa_cli -p "$dir" -i "$interface" status >/dev/null 2>&1 && return 0
+ syslog info "starting wpa_supplicant"
+ err=$(wpa_supplicant -B -c"$wpa_supplicant_conf" -i"$interface" 2>&1)
+ errn=$?
+ if [ $errn != 0 ]; then
+ syslog err "failed to start wpa_supplicant"
+ syslog err "$err"
+ fi
+ return $errn
+}
+
+wpa_supplicant_reconfigure()
+{
+ local dir err errn
+
+ dir=$(wpa_supplcant_ctrldir)
+ [ -z "$dir" ] && return 1
+ if ! wpa_cli -p "$dir" -i "$interface" status >/dev/null 2>&1; then
+ wpa_supplicant_start
+ return $?
+ fi
+ syslog info "reconfiguring wpa_supplicant"
+ err=$(wpa_cli -p "$dir" -i "$interface" reconfigure 2>&1)
+ errn=$?
+ if [ $errn != 0 ]; then
+ syslog err "failed to reconfigure wpa_supplicant"
+ syslog err "$err"
+ fi
+ return $errn
+}
+
+wpa_supplicant_stop()
+{
+ local dir err errn
+
+ dir=$(wpa_supplcant_ctrldir)
+ [ -z "$dir" ] && return 1
+ wpa_cli -p "$dir" -i "$interface" status >/dev/null 2>&1 || return 0
+ syslog info "stopping wpa_supplicant"
+ err=$(wpa_cli -i"$interface" terminate 2>&1)
+ errn=$?
+ if [ $errn != 0 ]; then
+ syslog err "failed to start wpa_supplicant"
+ syslog err "$err"
+ fi
+ return $errn
+}
+
+if [ "$ifwireless" = "1" ] && \
+ type wpa_supplicant >/dev/null 2>&1 && \
+ type wpa_cli >/dev/null 2>&1
+then
+ case "$reason" in
+ PREINIT) wpa_supplicant_start;;
+ RECONFIGURE) wpa_supplicant_reconfigure;;
+ DEPARTED) wpa_supplicant_stop;;
+ esac
+fi
diff --git a/dhcpcd/dhcpcd-hooks/15-timezone b/dhcpcd/dhcpcd-hooks/15-timezone
new file mode 100644
index 00000000..23ae924c
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/15-timezone
@@ -0,0 +1,51 @@
+# Configure timezone
+
+: ${localtime:=/etc/localtime}
+
+set_zoneinfo()
+{
+ local zoneinfo_dir= zone_file=
+
+ [ -z "$new_tzdb_timezone" ] && return 0
+
+ for d in \
+ /usr/share/zoneinfo \
+ /usr/lib/zoneinfo \
+ /var/share/zoneinfo \
+ /var/zoneinfo \
+ ; do
+ if [ -d "$d" ]; then
+ zoneinfo_dir="$d"
+ break
+ fi
+ done
+
+ if [ -z "$zoneinfo_dir" ]; then
+ syslog warning "timezone directory not found"
+ return 1
+ fi
+
+ zone_file="$zoneinfo_dir/$new_tzdb_timezone"
+ if [ ! -e "$zone_file" ]; then
+ syslog warning "no timezone definition for $new_tzdb_timezone"
+ return 1
+ fi
+
+ syslog info "timezone changed to $new_tzdb_timezone"
+ if [ -h "$localtime" ]; then
+ ln -sf "$zone_file" "$localtime"
+ else
+ cp "$zone_file" "$localtime"
+ fi
+}
+
+# For ease of use, map DHCP6 names onto our DHCP4 names
+case "$reason" in
+BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
+ new_tzdb_timezone="$new_dhcp6_tzdb_timezone"
+ ;;
+esac
+
+if $if_up; then
+ set_zoneinfo
+fi
diff --git a/dhcpcd/dhcpcd-hooks/20-resolv.conf b/dhcpcd/dhcpcd-hooks/20-resolv.conf
new file mode 100644
index 00000000..05316c9f
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/20-resolv.conf
@@ -0,0 +1,165 @@
+# Generate /etc/resolv.conf
+# Support resolvconf(8) if available
+# We can merge other dhcpcd resolv.conf files into one like resolvconf,
+# but resolvconf is preferred as other applications like VPN clients
+# can readily hook into it.
+# 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"
+NL="
+"
+
+build_resolv_conf()
+{
+ local cf="$state_dir/resolv.conf.$ifname"
+ local interfaces= header= search= srvs= servers= x=
+
+ # Build a list of interfaces
+ interfaces=$(list_interfaces "$resolv_conf_dir")
+
+ # Build the resolv.conf
+ if [ -n "$interfaces" ]; then
+ # Build the header
+ for x in ${interfaces}; do
+ header="$header${header:+, }$x"
+ done
+
+ # Build the search list
+ domain=$(cd "$resolv_conf_dir"; \
+ key_get_value "domain " ${interfaces})
+ search=$(cd "$resolv_conf_dir"; \
+ key_get_value "search " ${interfaces})
+ set -- ${domain}
+ domain="$1"
+ [ -n "$2" ] && search="$search $*"
+ [ -n "$search" ] && search="$(uniqify $search)"
+ [ "$domain" = "$search" ] && search=
+ [ -n "$domain" ] && domain="domain $domain$NL"
+ [ -n "$search" ] && search="search $search$NL"
+
+ # Build the nameserver list
+ srvs=$(cd "$resolv_conf_dir"; \
+ key_get_value "nameserver " ${interfaces})
+ for x in $(uniqify ${srvs}); do
+ servers="${servers}nameserver $x$NL"
+ done
+ fi
+ header="$signature_base${header:+ $from }$header"
+
+ # Assemble resolv.conf using our head and tail files
+ [ -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"
+ else
+ echo "# /etc/resolv.conf.head can replace this line" >> "$cf"
+ fi
+ printf %s "$domain$search$servers" >> "$cf"
+ if [ -f /etc/resolv.conf.tail ]; then
+ cat /etc/resolv.conf.tail >> "$cf"
+ else
+ echo "# /etc/resolv.conf.tail can replace this line" >> "$cf"
+ fi
+ if change_file /etc/resolv.conf "$cf"; then
+ chmod 644 /etc/resolv.conf
+ fi
+ rm -f "$cf"
+}
+
+add_resolv_conf()
+{
+ local x= conf="$signature$NL" i=${ra_count:-0} ra=
+
+ while [ $i -ne 0 ]; do
+ eval ra=\$ra${i}_rdnss
+ new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$ra"
+ eval ra=\$ra${i}_dnssl
+ new_domain_search="$new_domain_search${new_domain_search:+ }$ra"
+ i=$(($i - 1))
+ done
+
+ # 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
+ remove_resolv_conf
+ return $?
+ fi
+
+ # Derive a new domain from our various hostname options
+ if [ -z "$new_domain_name" ]; then
+ if [ "$new_dhcp6_fqdn" != "${new_dhcp6_fqdn#*.}" ]; then
+ new_domain_name="${new_dhcp6_fqdn#*.}"
+ elif [ "$new_fqdn" != "${new_fqdn#*.}" ]; then
+ new_domain_name="${new_fqdn#*.}"
+ elif [ "$new_host_name" != "${new_host_name#*.}" ]; then
+ new_domain_name="${new_host_name#*.}"
+ fi
+ fi
+
+ if [ -n "$new_domain_name" ]; then
+ set -- $new_domain_name
+ new_domain_name="$1"
+ if valid_domainname "$new_domain_name"; then
+ conf="${conf}domain $new_domain_name$NL"
+ else
+ syslog err "Invalid domain name: $new_domain_name"
+ fi
+ # 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" -a \
+ "$new_domain_search" != "$new_domain_name" ]
+ then
+ if valid_domainname_list $new_domain_search; then
+ conf="${conf}search $new_domain_search$NL"
+ else
+ syslog err "Invalid domain name in list: $new_domain_search"
+ fi
+ fi
+ for x in ${new_domain_name_servers}; do
+ conf="${conf}nameserver $x$NL"
+ done
+ if type resolvconf >/dev/null 2>&1; then
+ [ -n "$ifmetric" ] && export IF_METRIC="$ifmetric"
+ printf %s "$conf" | resolvconf -a "$ifname"
+ return $?
+ fi
+
+ if [ -e "$resolv_conf_dir/$ifname" ]; then
+ rm -f "$resolv_conf_dir/$ifname"
+ fi
+ [ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
+ printf %s "$conf" > "$resolv_conf_dir/$ifname"
+ build_resolv_conf
+}
+
+remove_resolv_conf()
+{
+ if type resolvconf >/dev/null 2>&1; then
+ resolvconf -d "$ifname" -f
+ else
+ if [ -e "$resolv_conf_dir/$ifname" ]; then
+ rm -f "$resolv_conf_dir/$ifname"
+ fi
+ build_resolv_conf
+ fi
+}
+
+# For ease of use, map DHCP6 names onto our DHCP4 names
+case "$reason" in
+BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
+ new_domain_name_servers="$new_dhcp6_name_servers"
+ new_domain_search="$new_dhcp6_domain_search"
+ ;;
+esac
+
+if $if_up || [ "$reason" = ROUTERADVERT ]; then
+ add_resolv_conf
+elif $if_down; then
+ remove_resolv_conf
+fi
diff --git a/dhcpcd/dhcpcd-hooks/29-lookup-hostname b/dhcpcd/dhcpcd-hooks/29-lookup-hostname
new file mode 100644
index 00000000..8661fccd
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/29-lookup-hostname
@@ -0,0 +1,34 @@
+# Lookup the hostname in DNS if not set
+
+lookup_hostname()
+{
+ [ -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)
+ if [ $? = 0 ]; then
+ echo "$h" | sed 's/\.$//'
+ return 0
+ fi
+ elif type host >/dev/null 2>&1; then
+ h=$(host $new_ip_address)
+ if [ $? = 0 ]; then
+ echo "$h" \
+ | sed 's/.* domain name pointer \(.*\)./\1/'
+ return 0
+ fi
+ fi
+ return 1
+}
+
+set_hostname()
+{
+ if [ -z "$new_host_name" -a -z "$new_fqdn_name" ]; then
+ export new_host_name="$(lookup_hostname)"
+ fi
+}
+
+if $if_up; then
+ set_hostname
+fi
diff --git a/dhcpcd/dhcpcd-hooks/30-hostname b/dhcpcd/dhcpcd-hooks/30-hostname
new file mode 100644
index 00000000..616fb82a
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/30-hostname
@@ -0,0 +1,154 @@
+# Set the hostname from DHCP data if required
+
+# A hostname can either be a short hostname or a FQDN.
+# hostname_fqdn=true
+# hostname_fqdn=false
+# hostname_fqdn=server
+
+# A value of server means just what the server says, don't manipulate it.
+# This could lead to an inconsistent hostname on a DHCPv4 and DHCPv6 network
+# where the DHCPv4 hostname is short and the DHCPv6 has an FQDN.
+# DHCPv6 has no hostname option.
+# RFC4702 section 3.1 says FQDN should be prefered over hostname.
+#
+# As such, the default is hostname_fqdn=true so that a consistent hostname
+# is always assigned.
+: ${hostname_fqdn:=true}
+
+# Some systems don't have hostname(1)
+_hostname()
+{
+ local name=
+
+ if [ -z "$1" ]; then
+ if type hostname >/dev/null 2>&1; then
+ hostname
+ elif [ -r /proc/sys/kernel/hostname ]; then
+ read name </proc/sys/kernel/hostname && echo "$name"
+ elif sysctl kern.hostname >/dev/null 2>&1; then
+ sysctl -n kern.hostname
+ elif sysctl kernel.hostname >/dev/null 2>&1; then
+ sysctl -n kernel.hostname
+ else
+ return 1
+ fi
+ return $?
+ fi
+
+ # Always prefer hostname(1) if we have it
+ if type hostname >/dev/null 2>&1; then
+ hostname "$1"
+ elif [ -w /proc/sys/kernel/hostname ]; then
+ echo "$1" >/proc/sys/kernel/hostname
+ elif sysctl kern.hostname >/dev/null 2>&1; then
+ sysctl -w "kern.hostname=$1"
+ elif sysctl kernel.hostname >/dev/null 2>&1; then
+ sysctl -w "kernel.hostname=$1"
+ else
+ # We know this will fail, but it will now fail
+ # with an error to stdout
+ hostname "$1"
+ fi
+}
+
+need_hostname()
+{
+ local hostname hfqdn=false hshort=false
+
+ case "$force_hostname" in
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) return 0;;
+ esac
+
+ hostname="$(_hostname)"
+ case "$hostname" in
+ ""|"(none)"|localhost|localhost.localdomain) return 0;;
+ esac
+
+ case "$hostname_fqdn" in
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) hfqdn=true;;
+ [Ss][Ee][Rr][Vv][Ee][Rr]) ;;
+ *) hshort=true;;
+ esac
+
+ if [ -n "$old_fqdn" ]; then
+ if ${hfqdn} || ! ${hsort}; then
+ [ "$hostname" = "$old_fqdn" ]
+ else
+ [ "$hostname" = "${old_fqdn%%.*}" ]
+ fi
+ elif [ -n "$old_host_name" ]; then
+ if ${hfqdn}; then
+ if [ -n "$old_domain_name" -a \
+ "$old_host_name" = "${old_host_name#*.}" ]
+ then
+ [ "$hostname" = \
+ "$old_host_name.$old_domain_name" ]
+ else
+ [ "$hostname" = "$old_host_name" ]
+ fi
+ elif ${hshort}; then
+ [ "$hostname" = "${old_host_name%%.*}" ]
+ else
+ [ "$hostname" = "$old_host_name" ]
+ fi
+ else
+ # No old hostname
+ false
+ fi
+}
+
+try_hostname()
+{
+
+ if valid_domainname "$1"; then
+ _hostname "$1"
+ else
+ syslog err "Invalid hostname: $1"
+ fi
+}
+
+set_hostname()
+{
+ local hfqdn=false hshort=false
+
+ need_hostname || return
+
+ case "$hostname_fqdn" in
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) hfqdn=true;;
+ "") ;;
+ *) hshort=true;;
+ esac
+
+ if [ -n "$new_fqdn" ]; then
+ if ${hfqdn} || ! ${hshort}; then
+ try_hostname "$new_fqdn"
+ else
+ try_hostname "${new_fqdn%%.*}"
+ fi
+ elif [ -n "$new_host_name" ]; then
+ if ${hfqdn}; then
+ if [ -n "$new_domain_name" -a \
+ "$new_host_name" = "${new_host_name#*.}" ]
+ then
+ try_hostname "$new_host_name.$new_domain_name"
+ else
+ try_hostname "$new_host_name"
+ fi
+ elif ${hshort}; then
+ try_hostname "${new_host_name%%.*}"
+ else
+ try_hostname "$new_host_name"
+ fi
+ fi
+}
+
+# For ease of use, map DHCP6 names onto our DHCP4 names
+case "$reason" in
+BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
+ new_fqdn="$new_dhcp6_fqdn"
+ ;;
+esac
+
+if $if_up; then
+ set_hostname
+fi
diff --git a/dhcpcd/dhcpcd-hooks/50-dhcpcd-compat b/dhcpcd/dhcpcd-hooks/50-dhcpcd-compat
new file mode 100644
index 00000000..0d6256e6
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/50-dhcpcd-compat
@@ -0,0 +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
+
+GATEWAY=
+for x in $new_routers; do
+ GATEWAY="$GATEWAY${GATEWAY:+,}$x"
+done
+DNS=
+for x in $new_domain_name_servers; do
+ DNS="$DNS${DNS:+,}$x"
+done
+
+r="down"
+case "$reason" in
+RENEW) r="up";;
+BOUND|INFORM|REBIND|REBOOT|TEST|TIMEOUT|IPV4LL) r="new";;
+esac
+
+if [ "$r" != "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
+ done
+fi
+
+set -- /var/lib/dhcpcd-"$INTERFACE".info "$r"
diff --git a/dhcpcd/dhcpcd-hooks/50-ntp.conf b/dhcpcd/dhcpcd-hooks/50-ntp.conf
new file mode 100644
index 00000000..c0781360
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/50-ntp.conf
@@ -0,0 +1,109 @@
+# Sample dhcpcd hook script for ntp
+# Like our resolv.conf hook script, we store a database of ntp.conf files
+# and merge into /etc/ntp.conf
+
+# You can set the env var NTP_CONF to another file like this
+# dhcpcd -e NTP_CONF=/usr/pkg/etc/ntpd.conf
+# or by adding this to /etc/dhcpcd.enter-hook
+# NTP_CONF=/usr/pkg/etc/ntpd.conf
+# to use openntpd from pkgsrc instead of the system provided ntp.
+
+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_service:=ntp}
+ : ${NTP_DHCP_CONF:=/var/lib/ntp/ntp.conf.dhcp}
+fi
+
+: ${ntp_service:=ntpd}
+: ${ntp_restart_cmd:=service_condcommand $ntp_service restart}
+ntp_conf_dir="$state_dir/ntp.conf"
+ntp_conf=${NTP_CONF:-/etc/ntp.conf}
+NL="
+"
+
+build_ntp_conf()
+{
+ local cf="$state_dir/ntp.conf.$ifname"
+ local interfaces= header= srvs= servers= x=
+
+ # Build a list of interfaces
+ interfaces=$(list_interfaces "$ntp_conf_dir")
+
+ if [ -n "$interfaces" ]; then
+ # Build the header
+ for x in ${interfaces}; do
+ 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$NL"
+ done
+ fi
+ fi
+
+ # Merge our config into ntp.conf
+ [ -e "$cf" ] && rm -f "$cf"
+ [ -d "$ntp_conf_dir" ] || mkdir -p "$ntp_conf_dir"
+
+ if [ -n "$NTP_DHCP_CONF" ]; then
+ [ -e "$ntp_conf" ] && 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 %s "$servers" >> "$cf"
+ echo "$signature_base_end${header:+ $from }$header" >> "$cf"
+ else
+ [ -e "$ntp_conf" -a -e "$cf" ] || return
+ fi
+
+ # If we changed anything, restart ntpd
+ if change_file "$ntp_conf" "$cf"; then
+ [ -n "$ntp_restart_cmd" ] && eval $ntp_restart_cmd
+ fi
+}
+
+add_ntp_conf()
+{
+ local cf="$ntp_conf_dir/$ifname" 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"
+ done
+ fi
+ build_ntp_conf
+}
+
+remove_ntp_conf()
+{
+ if [ -e "$ntp_conf_dir/$ifname" ]; then
+ rm "$ntp_conf_dir/$ifname"
+ fi
+ build_ntp_conf
+}
+
+# For ease of use, map DHCP6 names onto our DHCP4 names
+case "$reason" in
+BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
+ new_ntp_servers="$new_dhcp6_sntp_servers"
+;;
+esac
+
+if $if_up; then
+ add_ntp_conf add
+elif $if_down; then
+ remove_ntp_conf del
+fi
diff --git a/dhcpcd/dhcpcd-hooks/50-yp.conf b/dhcpcd/dhcpcd-hooks/50-yp.conf
new file mode 100644
index 00000000..2da68ebc
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/50-yp.conf
@@ -0,0 +1,56 @@
+# Sample dhcpcd hook for ypbind
+# This script is only suitable for the Linux version.
+
+ypbind_pid()
+{
+ [ -s /var/run/ypbind.pid ] && cat /var/run/ypbind.pid
+}
+
+make_yp_conf()
+{
+ [ -z "$new_nis_domain" -a -z "$new_nis_servers" ] && return 0
+ local cf=/etc/yp.conf."$ifname" prefix= x= pid=
+ rm -f "$cf"
+ echo "$signature" > "$cf"
+ if [ -n "$new_nis_domain" ]; then
+ if ! valid_domainname "$new_nis_domain"; then
+ syslog err "Invalid NIS domain name: $new_nis_domain"
+ rm -f "$cf"
+ return 1
+ fi
+ domainname "$new_nis_domain"
+ if [ -n "$new_nis_servers" ]; then
+ prefix="domain $new_nis_domain server "
+ else
+ echo "domain $new_nis_domain broadcast" >> "$cf"
+ fi
+ else
+ prefix="ypserver "
+ fi
+ for x in $new_nis_servers; do
+ echo "$prefix$x" >> "$cf"
+ done
+ save_conf /etc/yp.conf
+ cat "$cf" > /etc/yp.conf
+ rm -f "$cf"
+ pid="$(ypbind_pid)"
+ if [ -n "$pid" ]; then
+ kill -HUP "$pid"
+ fi
+}
+
+restore_yp_conf()
+{
+ [ -n "$old_nis_domain" ] && domainname ""
+ restore_conf /etc/yp.conf || return 0
+ local pid="$(ypbind_pid)"
+ if [ -n "$pid" ]; then
+ kill -HUP "$pid"
+ fi
+}
+
+if $if_up; then
+ make_yp_conf
+elif $if_down; then
+ restore_yp_conf
+fi
diff --git a/dhcpcd/dhcpcd-hooks/50-ypbind b/dhcpcd/dhcpcd-hooks/50-ypbind
new file mode 100644
index 00000000..25e009d7
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/50-ypbind
@@ -0,0 +1,78 @@
+# 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/$ifname"
+ local nd="$(best_domain)"
+
+ local cf=/var/yp/binding/"$new_nis_domain".ypservers
+ if [ -n "$new_nis_servers" ]; then
+ local ncf="$cf.$ifname" 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/$ifname"
+ 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/$ifname"
+elif $if_up || $if_down; then
+ if [ -n "$new_nis_domain" ]; then
+ if valid_domainname "$new_nis_domain"; then
+ make_yp_binding
+ else
+ syslog err "Invalid NIS domain name: $new_nis_domain"
+ fi
+ elif [ -n "$old_nis_domain" ]; then
+ restore_yp_binding
+ fi
+fi
diff --git a/dhcpcd/dhcpcd-hooks/Makefile b/dhcpcd/dhcpcd-hooks/Makefile
new file mode 100644
index 00000000..5982f1b7
--- /dev/null
+++ b/dhcpcd/dhcpcd-hooks/Makefile
@@ -0,0 +1,35 @@
+TOP?= ../
+include ${TOP}/Makefile.inc
+include ${TOP}/config.mk
+
+SCRIPTSDIR= ${LIBEXECDIR}/dhcpcd-hooks
+SCRIPTS= 01-test 02-dump
+SCRIPTS+= 10-mtu 10-wpa_supplicant 15-timezone 20-resolv.conf
+SCRIPTS+= 29-lookup-hostname 30-hostname
+SCRIPTS+= ${HOOKSCRIPTS}
+
+all:
+
+clean:
+
+proginstall:
+ ${INSTALL} -d ${DESTDIR}${SCRIPTSDIR}
+ ${INSTALL} -m ${NONBINMODE} ${SCRIPTS} ${DESTDIR}${SCRIPTSDIR}
+
+install: proginstall
+
+import:
+ ${INSTALL} -d /tmp/${DISTPREFIX}/dhcpcd-hooks
+ for x in ${SCRIPTS}; do \
+ t="/tmp/${DISTPREFIX}/dhcpcd-hooks/$$x"; \
+ if test "$$(sed -ne 1p $$x)" = "#!/bin/sh"; then \
+ echo "#!/bin/sh" >"$$t"; \
+ printf "${IMPORT_SHID}\n" >>"$$t"; \
+ echo "" >>"$$t"; \
+ sed 1d "$$x" >>"$$t"; \
+ else \
+ printf "${IMPORT_SHID}\n" >"$$t"; \
+ echo "" >>"$$t"; \
+ cat "$$x" >>"$$t"; \
+ fi; \
+ done;
diff --git a/dhcpcd/dhcpcd-run-hooks.8.in b/dhcpcd/dhcpcd-run-hooks.8.in
new file mode 100644
index 00000000..b078f07a
--- /dev/null
+++ b/dhcpcd/dhcpcd-run-hooks.8.in
@@ -0,0 +1,150 @@
+.\" Copyright (c) 2006-2013 Roy Marples
+.\" 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.
+.\"
+.Dd June 6, 2013
+.Dt DHCPCD-RUN-HOOKS 8
+.Os
+.Sh NAME
+.Nm dhcpcd-run-hooks
+.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 @HOOKDIR@
+and the user defined hooks are
+.Pa @SYSCONFDIR@/dhcpcd.enter-hook .
+and
+.Pa @SYSCONFDIR@/dhcpcd.exit-hook .
+The default install supplies hook scripts for configuring
+.Pa /etc/resolv.conf
+and the hostname.
+Your distribution may have included other hook scripts to say configure
+ntp or ypbind.
+A test hook is also supplied that simply echos the dhcp variables to the
+console from DISCOVER message.
+.Pp
+Each time
+.Nm
+is invoked,
+.Ev $interface
+is set to the interface that
+.Nm dhcpcd
+is run on and
+.Ev $reason
+is to the reason why
+.Nm
+was invoked.
+DHCP information to be configured is held in variables starting with the word
+new_ and old DHCP information to be removed is held in variables starting with
+the word old_.
+.Nm dhcpcd
+can display the full list of variables it knows how about by using the
+.Fl V , -variables
+argument.
+.Pp
+Here's a list of reasons why
+.Nm
+could be invoked:
+.Bl -tag -width ROUTERADVERT
+.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 | Dv INFORM6
+dhcpcd informed a DHCP server about it's address and obtained other
+configuration details.
+.It Dv BOUND | Dv BOUND6
+dhcpcd obtained a new lease from a DHCP server.
+.It Dv RENEW | Dv RENEW6
+dhcpcd renewed it's lease.
+.It Dv REBIND | Dv REBIND6
+dhcpcd has rebound to a new DHCP server.
+.It Dv REBOOT | Dv REBOOT6
+dhcpcd successfully requested a lease from a DHCP server.
+.It Dv IPV4LL
+dhcpcd failed to contact any DHCP servers but did obtain an IPV4LL address.
+.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 | EXPIRE6
+dhcpcd's lease or state expired and it failed to obtain a new one.
+.It Dv NAK
+dhcpcd received a NAK from the DHCP server.
+This should be treated as EXPIRE.
+.It Dv RECONFIGURE
+dhcpcd has been instructed to reconfigure an interface.
+.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 | Dv STOP6
+dhcpcd stopped running on the interface.
+.It Dv DEPARTED
+The interface has been removed.
+.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.
+.It Dv ROUTERADVERT
+dhcpcd has received an IPv6 Router Advertisment, or one has expired.
+.El
+.Sh FILES
+When
+.Nm
+runs, it loads
+.Pa @SYSCONFDIR@/dhcpcd.enter-hook
+and any scripts found in
+.Pa @HOOKDIR@
+in a lexical order and then finally
+.Pa @SYSCONFDIR@/dhcpcd.exit-hook
+.Sh SEE ALSO
+.Xr dhcpcd 8
+.Sh AUTHORS
+.An Roy Marples Aq Mt roy@marples.name
+.Sh BUGS
+Please report them to
+.Lk http://roy.marples.name/projects/dhcpcd
+.Sh SECURITY CONSIDERATIONS
+Little validation of DHCP options is done in dhcpcd itself.
+Instead, it is up to the hooks to handle any validation needed.
+To this end, some helper functions are provided, such as valid_domainname as
+used by the
+.Pa 20-resolv.conf
+hook to ensure that the hostname is not set to an invalid value.
+valid_path is also provided, but is currently unused by a stock hook script.
diff --git a/dhcpcd/dhcpcd-run-hooks.in b/dhcpcd/dhcpcd-run-hooks.in
new file mode 100644
index 00000000..e0ad618c
--- /dev/null
+++ b/dhcpcd/dhcpcd-run-hooks.in
@@ -0,0 +1,342 @@
+#!/bin/sh
+# dhcpcd client configuration script
+
+# Handy variables and functions for our hooks to use
+case "$reason" in
+ ROUTERADVERT)
+ ifsuffix=":ra";;
+ INFORM6|BOUND6|RENEW6|REBIND6|REBOOT6|EXPIRE6|RELEASE6|STOP6)
+ ifsuffix=":dhcp6";;
+ *)
+ ifsuffix=;;
+esac
+ifname="$interface$ifsuffix"
+
+from=from
+signature_base="# Generated by dhcpcd"
+signature="$signature_base $from $ifname"
+signature_base_end="# End of dhcpcd"
+signature_end="$signature_base_end $from $ifname"
+state_dir=@RUNDIR@/dhcpcd
+_detected_init=false
+
+: ${if_up:=false}
+: ${if_down:=false}
+
+# Ensure that all arguments are unique
+uniqify()
+{
+ local result= i=
+ for i do
+ case " $result " in
+ *" $i "*);;
+ *) result="$result $i";;
+ esac
+ done
+ echo "${result# *}"
+}
+
+# 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 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
+ for i in $interface_order; do
+ if [ $i = "${x##*/}" ]; then
+ unset x
+ break
+ fi
+ done
+ [ -n "$x" ] && ifaces="$ifaces${ifaces:+ }${x##*/}"
+ done
+ echo "$ifaces"
+}
+
+# Trim function
+trim()
+{
+ local var="$*"
+
+ var=${var#"${var%%[![:space:]]*}"}
+ var=${var%"${var##*[![:space:]]}"}
+ if [ -z "$var" ]; then
+ # So it seems our shell doesn't support wctype(3) patterns
+ # Fall back to sed
+ var=$(echo "$*" | sed -e 's/^[[:space:]]*//;s/[[:space:]]*$//')
+ fi
+ printf %s "$var"
+}
+
+# We normally use sed to extract values using a key from a list of files
+# but sed may not always be available at the time.
+key_get_value()
+{
+ local key="$1" value= x= line=
+
+ shift
+ if type sed >/dev/null 2>&1; then
+ sed -n "s/^$key//p" $@
+ else
+ for x do
+ while read line; do
+ case "$line" in
+ "$key"*) echo "${line##$key}";;
+ esac
+ done < "$x"
+ done
+ fi
+}
+
+# We normally use sed to remove markers from a configuration file
+# but sed may not always be available at the time.
+remove_markers()
+{
+ local m1="$1" m2="$2" x= line= in_marker=0
+
+ shift; shift
+ if type sed >/dev/null 2>&1; then
+ 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";;
+ esac
+ done < "$x"
+ done
+ fi
+}
+
+# Compare two files.
+# If different, replace first with second otherwise remove second.
+change_file()
+{
+ 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
+ cat "$2" > "$1"
+ rm -f "$2"
+ return 0
+}
+
+# Save a config file
+save_conf()
+{
+ if [ -f "$1" ]; then
+ rm -f "$1-pre.$interface"
+ cat "$1" > "$1-pre.$interface"
+ fi
+}
+
+# Restore a config file
+restore_conf()
+{
+ [ -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" -is "$interface: $*"
+ fi
+ fi
+}
+
+# Check for a valid domain name as per RFC1123 with the exception of
+# allowing - and _ as they seem to be widely used.
+valid_domainname()
+{
+ local name="$1" label
+
+ [ -z "$name" -o ${#name} -gt 255 ] && return 1
+
+ while [ -n "$name" ]; do
+ label="${name%%.*}"
+ [ -z "$label" -o ${#label} -gt 63 ] && return 1
+ case "$label" in
+ -*|_*|*-|*_) return 1;;
+ *[![:alnum:]-_]*) return 1;;
+ esac
+ [ "$name" = "${name#*.}" ] && break
+ name="${name#*.}"
+ done
+ return 0
+}
+
+valid_domainname_list()
+{
+ local name
+
+ for name do
+ valid_domainname "$name" || return $?
+ done
+ return 0
+}
+
+# Check for a valid path
+valid_path()
+{
+ case "$@" in
+ *[![:alnum:]#%+-_:\.,@~\\/\[\]=\ ]*) return 1;;
+ esac
+ return 0
+}
+
+# With the advent of alternative init systems, it's possible to have
+# more than one installed. So we need to try and guess what one we're
+# using unless overriden by configure.
+detect_init()
+{
+ _service_exists="@SERVICEEXISTS@"
+ _service_cmd="@SERVICECMD@"
+ _service_status="@SERVICESTATUS@"
+
+ [ -n "$_service_cmd" ] && return 0
+
+ if ${_detected_init}; then
+ [ -n "$_service_cmd" ]
+ return $?
+ fi
+
+ # Detect the running init system.
+ # As systemd and OpenRC can be installed on top of legacy init
+ # systems we try to detect them first.
+ _service_status=
+ if [ -x /bin/systemctl -a -S /run/systemd/private ]; then
+ _service_exists="/bin/systemctl --quiet is-enabled \$1.service"
+ _service_status="/bin/systemctl --quiet is-active \$1.service"
+ _service_cmd="/bin/systemctl \$2 \$1.service"
+ elif [ -x /usr/bin/systemctl -a -S /run/systemd/private ]; then
+ _service_exists="/usr/bin/systemctl --quiet is-enabled \$1.service"
+ _service_status="/usr/bin/systemctl --quiet is-active \$1.service"
+ _service_cmd="/usr/bin/systemctl \$2 \$1.service"
+ elif [ -x /sbin/rc-service -a \
+ -s /libexec/rc/init.d/softlevel -o -s /run/openrc/softlevel ]
+ then
+ _service_exists="/sbin/rc-service -e \$1"
+ _service_cmd="/sbin/rc-service \$1 -- -D \$2"
+ elif [ -x /usr/sbin/invoke-rc.d ]; then
+ _service_exists="/usr/sbin/invoke-rc.d --query --quiet \$1 start >/dev/null 2>&1 || [ \$? = 104 ]"
+ _service_cmd="/usr/sbin/invoke-rc.d \$1 \$2"
+ elif [ -x /sbin/service ]; then
+ _service_exists="/sbin/service \$1 >/dev/null 2>&1"
+ _service_cmd="/sbin/service \$1 \$2"
+ elif [ -e /etc/slackware-version -a -d /etc/rc.d ]; then
+ _service_exists="[ -x /etc/rc.d/rc.\$1 ]"
+ _service_cmd="/etc/rc.d/rc.\$1 \$2"
+ _service_status="/etc/rc.d/rc.\$1 status 1>/dev/null 2>&1"
+ else
+ for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do
+ if [ -d $x ]; then
+ _service_exists="[ -x $x/\$1 ]"
+ _service_cmd="$x/\$1 \$2"
+ break
+ fi
+ done
+ if [ -e /etc/arch-release ]; then
+ _service_status="[ -e /var/run/daemons/\$1 ]"
+ fi
+ fi
+
+ _detected_init=true
+ if [ -z "$_service_cmd" ]; then
+ syslog err "could not detect a useable init system"
+ return 1
+ fi
+ return 0
+}
+
+# Check a system service exists
+service_exists()
+{
+
+ if [ -z "$_service_exists" ]; then
+ detect_init || return 1
+ fi
+ eval $_service_exists
+}
+
+# Send a command to a system service
+service_cmd()
+{
+
+ if [ -z "$_service_cmd" ]; then
+ detect_init || return 1
+ fi
+ eval $_service_cmd
+}
+
+# Send a command to a system service if it is running
+service_status()
+{
+
+ if [ -z "$_service_cmd" ]; then
+ detect_init || return 1
+ fi
+ if [ -n "$_service_status" ]; then
+ eval $_service_status
+ else
+ service_command $1 status >/dev/null 2>&1
+ fi
+}
+
+# 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.
+# Thus, the user can create their dhcpcd.enter/exit-hook script to configure
+# /etc/resolv.conf how they want and stop the system scripts ever updating it.
+for hook in \
+ @SYSCONFDIR@/dhcpcd.enter-hook \
+ @HOOKDIR@/* \
+ @SYSCONFDIR@/dhcpcd.exit-hook
+do
+ for skip in $skip_hooks; do
+ case "$hook" in
+ */*~) continue 2;;
+ */"$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"
+ fi
+done
diff --git a/dhcpcd/dhcpcd.8.in b/dhcpcd/dhcpcd.8.in
new file mode 100644
index 00000000..8c12c6ab
--- /dev/null
+++ b/dhcpcd/dhcpcd.8.in
@@ -0,0 +1,672 @@
+.\" Copyright (c) 2006-2014 Roy Marples
+.\" 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.
+.\"
+.Dd January 24, 2014
+.Dt DHCPCD 8
+.Os
+.Sh NAME
+.Nm dhcpcd
+.Nd a DHCP client
+.Sh SYNOPSIS
+.Nm
+.Op Fl 46ABbDdEGgHJKLpqTV
+.Op Fl C , Fl Fl nohook Ar hook
+.Op Fl c , Fl Fl script Ar script
+.Op Fl e , Fl Fl env Ar value
+.Op Fl F , Fl Fl fqdn Ar FQDN
+.Op Fl f , Fl Fl config Ar file
+.Op Fl h , Fl Fl hostname Ar hostname
+.Op Fl I , Fl Fl clientid Ar clientid
+.Op Fl i , Fl Fl vendorclassid Ar vendorclassid
+.Op Fl l , Fl Fl leasetime Ar seconds
+.Op Fl m , Fl Fl metric Ar metric
+.Op Fl O , Fl Fl nooption Ar option
+.Op Fl o , Fl Fl option Ar option
+.Op Fl Q , Fl Fl require Ar option
+.Op Fl r , Fl Fl request Ar address
+.Op Fl S , Fl Fl static Ar value
+.Op Fl s , Fl Fl inform Ar address Ns Op Ar /cidr
+.Op Fl t , Fl Fl timeout Ar seconds
+.Op Fl u , Fl Fl userclass Ar class
+.Op Fl v , Fl Fl vendor Ar code , Ar value
+.Op Fl W , Fl Fl whitelist Ar address Ns Op Ar /cidr
+.Op Fl w , Fl Fl waitip Op 4 | 6
+.Op Fl y , Fl Fl reboot Ar seconds
+.Op Fl X , Fl Fl blacklist Ar address Ns Op Ar /cidr
+.Op Fl Z , Fl Fl denyinterfaces Ar pattern
+.Op Fl z , Fl Fl allowinterfaces Ar pattern
+.Op interface
+.Op ...
+.Nm
+.Fl n , Fl Fl rebind
+.Op interface
+.Nm
+.Fl k , Fl Fl release
+.Op interface
+.Nm
+.Fl U, Fl Fl dumplease
+.Ar interface
+.Nm
+.Fl Fl version
+.Nm
+.Fl x , Fl Fl exit
+.Op interface
+.Sh DESCRIPTION
+.Nm
+is an implementation of the DHCP client specified in
+.Li RFC 2131 .
+.Nm
+gets the host information
+.Po
+IP address, routes, etc
+.Pc
+from a DHCP server and configures the network
+.Ar interface
+of the
+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 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.
+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 .
+.Pp
+.Nm
+is also an implementation of the IPv6 Router Solicitor as specified in
+.Li RFC 4861
+and
+.Li RFC 6106 .
+.Nm
+can optionally handle address and route management itself,
+and will do so by default if Router Solicitation is disabled in the kernel.
+If
+.Nm
+is managing routes,
+.Nm
+sends Neighbor Solicitions to each advertising router periodically and will
+expire the ones that do not respond.
+.Pp
+.Nm
+is also an implemenation of the DHCPv6 client as specified in
+.Li RFC 3315 .
+By default,
+.Nm
+only starts DHCPv6 when instructed to do so by an IPV6 Router Advertisement.
+If no Identity Association is configured,
+then a Non-temporary Address is requested.
+.Ss Local Link configuration
+If
+.Nm
+failed to obtain a lease, it probes for a valid IPv4LL address
+.Po
+aka ZeroConf, aka APIPA
+.Pc .
+Once obtained it restarts the process of looking for a DHCP server to get a
+proper address.
+.Pp
+When using IPv4LL,
+.Nm
+nearly always succeeds and returns an exit code of 0.
+In the rare case it fails, it normally means that there is a reverse ARP proxy
+installed which always defeats IPv4LL probing.
+To disable this behaviour, you can use the
+.Fl L , Fl Fl 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 , Fl Fl background
+and
+.Fl w , Fl Fl 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 , Fl Fl 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 events
+.Nm
+runs
+.Pa @SCRIPT@ ,
+or the script specified by the
+.Fl c , Fl Fl script
+option.
+This script runs each script found in
+.Pa @HOOKDIR@
+in a lexical order.
+The default installation supplies the scripts
+.Pa 01-test ,
+.Pa 10-mtu ,
+.Pa 10-wpa_supplicant ,
+.Pa 15-timezone ,
+.Pa 20-resolv.conf
+and
+.Pa 30-hostname .
+You can disable each script by using the
+.Fl C , Fl Fl nohook
+option.
+See
+.Xr dhcpcd-run-hooks 8
+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
+.Nm
+with the following options:
+.Bl -tag -width indent
+.It Fl b , Fl Fl background
+Background immediately.
+This is useful for startup scripts which don't disable link messages for
+carrier status.
+.It Fl c , Fl Fl script Ar script
+Use this
+.Ar script
+instead of the default
+.Pa @SCRIPT@ .
+.It Fl D , Fl Fl duid
+Generate an
+.Li RFC 4361
+compliant clientid.
+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 it in
+.Pa @SYSCONFDIR@/dhcpcd.duid .
+This file should not be copied to other hosts.
+.It Fl d , Fl Fl debug
+Echo debug messages to the stderr and syslog.
+.It Fl E , Fl Fl lastlease
+If
+.Nm
+cannot obtain a lease, then try to use the last lease acquired for the
+interface.
+If the
+.Fl p, Fl Fl persistent
+option is not given then the lease is used if it hasn't expired.
+.It Fl e , Fl Fl 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 , Fl Fl 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
+does not read a new configuration when this happens - you should rebind if you
+need that functionality.
+.It Fl F , Fl Fl fqdn Ar fqdn
+Requests that the DHCP server updates DNS using FQDN instead of just a
+hostname.
+Valid values for
+.Ar fqdn
+are disable, none, ptr and both.
+.Nm
+itself never does any DNS updates.
+.Nm
+encodes the FQDN hostname as specified in
+.Li RFC1035 .
+.It Fl f , Fl Fl 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 , Fl Fl hostname Ar hostname
+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
+is a FQDN (ie, contains a .) then it will be encoded as such.
+.It Fl I , Fl Fl clientid Ar clientid
+Send the
+.Ar clientid .
+If the string is of the format 01:02:03 then it is encoded as hex.
+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.
+.It Fl i , Fl Fl vendorclassid Ar vendorclassid
+Override the DHCPv4
+.Ar vendorclassid
+field sent.
+The default is
+dhcpcd-<version>:<os>:<machine>:<platform>.
+For example
+.D1 dhcpcd-5.5.6:NetBSD-6.99.5:i386:i386
+If not set then none is sent.
+Some badly configured DHCP servers reject unknown vendorclassids.
+To work around it, try and impersonate Windows by using the MSFT vendorclassid.
+.It Fl k , Fl Fl release Op Ar interface
+This causes an existing
+.Nm
+process running on the
+.Ar interface
+to release its lease and de-configure the
+.Ar interface .
+If no
+.Ar interface
+is specified then this applies to all interfaces.
+If no interfaces are left running,
+.Nm
+will exit.
+.It Fl l , Fl Fl leasetime Ar seconds
+Request a specific lease time in
+.Ar seconds .
+By default
+.Nm
+does not request any lease time and leaves it in the hands of the
+DHCP server.
+.It Fl m , Fl Fl metric Ar metric
+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 n , Fl Fl rebind Op Ar interface
+Notifies
+.Nm
+to reload its configuration and rebind the specified
+.Ar interface .
+If no interface is specified then this applies to all interfaces.
+If
+.Nm
+is not running, then it starts up as normal.
+This may also cause
+.Xr wpa_supplicant 8
+to reload its configuration for each interface as well.
+.It Fl o , Fl Fl option Ar option
+Request the DHCP
+.Ar option
+variable for use in
+.Pa @SCRIPT@ .
+.It Fl p , Fl Fl persistent
+.Nm
+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 or SSH clients connect to this host and they need to be notified of
+the host shutting down.
+You can use this option to stop this from happening.
+.It Fl r , Fl Fl request Op Ar address
+Request the
+.Ar address
+in the DHCP DISCOVER message.
+There is no guarantee this is the address the DHCP server will actually give.
+If no
+.Ar address
+is given then the first address currently assigned to the
+.Ar interface
+is used.
+.It Fl s , Fl Fl inform Op Ar address Ns Op Ar /cidr
+Behaves like
+.Fl r , Fl Fl request
+as above, but sends a DHCP INFORM instead of DISCOVER/REQUEST.
+This does not get a lease as such, just notifies the DHCP server of the
+.Ar address
+in use.
+You should also include the optional
+.Ar cidr
+network number in case the address is not already configured on the interface.
+.Nm
+remains running and pretends it has an infinite lease.
+.Nm
+will not de-configure the interface when it exits.
+If
+.Nm
+fails to contact a DHCP server then it returns a failure instead of falling
+back on IPv4LL.
+.It Fl S, Fl Fl 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 , Fl Fl timeout Ar seconds
+Timeout after
+.Ar seconds ,
+instead of the default 30.
+A setting of 0
+.Ar seconds
+causes
+.Nm
+to wait forever to get a lease.
+If
+.Nm
+is working on a single interface then
+.Nm
+will exit when a timeout occurs, otherwise
+.Nm
+will fork into the background.
+If using IPv4LL then
+.Nm
+start the IPv4LL process after the timeout and then wait a little longer
+before really timing out.
+.It Fl u , Fl Fl userclass Ar class
+Tags the DHCPv4 message with the userclass
+.Ar class .
+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 , Fl Fl vendor Ar code , Ns Ar value
+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
+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 Fl version
+Display both program version and copyright information.
+.Nm
+then exits before doing any configuration.
+.It Fl w , Fl Fl waitip Op 4 | 6
+Wait for an address to be assigned before forking to the background.
+4 means wait for an IPv4 address to be assigned.
+6 means wait for an IPv6 address to be assigned.
+If no argument is given,
+.Nm
+will wait for any address protocol to be assigned.
+It is possible to wait for more than one address protocol and
+.Nm
+will only fork to the background when all waiting conditions are satisfied.
+.It Fl x , Fl Fl exit Op Ar interface
+This will signal an existing
+.Nm
+process running on the
+.Ar interface
+to de-configure the
+.Ar interface
+and exit.
+If no interface is specified, then the above is applied to all interfaces.
+.Nm
+then waits until this process has exited.
+.It Fl y , Fl Fl reboot Ar seconds
+Allow
+.Ar reboot
+seconds before moving to the discover phase if we have an old lease to use.
+The default is 5 seconds.
+A setting of 0 seconds causes
+.Nm
+to skip the reboot phase and go straight into discover.
+.El
+.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
+configured exactly how the the DHCP server wants.
+Here are some options that deal with turning these bits off.
+.Bl -tag -width indent
+.It Fl 4 , Fl Fl ipv4only
+Only configure IPv4.
+.It Fl 6 , Fl Fl ipv6only
+Only confgiure IPv6.
+.It Fl A , Fl Fl noarp
+Don't request or claim the address by ARP.
+This also disables IPv4LL.
+.It Fl B , Fl Fl nobackground
+Don't run in the background when we acquire a lease.
+This is mainly useful for running under the control of another process, such
+as a debugger or a network manager.
+.It Fl C , Fl Fl nohook Ar script
+Don't run this hook script.
+Matches full name, or prefixed with 2 numbers optionally ending with
+.Pa .sh .
+.Pp
+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 , Fl Fl nogateway
+Don't set any default routes.
+.It Fl H , Fl Fl xidhwaddr
+Use the last four bytes of the hardware address as the DHCP xid instead
+of a randomly generated number.
+.It Fl J , Fl Fl 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 , Fl Fl nolink
+Don't receive link messages for carrier status.
+You should only have to use this with buggy device drivers or running
+.Nm
+through a network manager.
+.It Fl L , Fl Fl noipv4ll
+Don't use IPv4LL (aka APIPA, aka Bonjour, aka ZeroConf).
+.It Fl O , Fl Fl nooption Ar option
+Don't request the specified option.
+If no option given, then don't request any options other than those to
+configure the interface and routing.
+.It Fl Q , Fl Fl require Ar option
+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 q , Fl Fl quiet
+Quiet
+.Nm
+on the command line, only warnings and errors will be displayed.
+The messages are still logged though.
+.It Fl T, Fl Fl test
+On receipt of DHCP messages just call
+.Pa @SCRIPT@
+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, Fl Fl 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, Fl Fl variables
+Display a list of option codes and the associated variable for use in
+.Xr dhcpcd-run-hooks 8 .
+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, Fl Fl whitelist Ar address Ns Op /cidr
+Only accept packets from
+.Ar address Ns Op /cidr .
+.Fl X, Fl Fl blacklist
+is ignored if
+.Fl W, Fl Fl whitelist
+is set.
+.It Fl X, Fl Fl blacklist Ar address Ns Op Ar /cidr
+Ignore all packets from
+.Ar address Ns Op Ar /cidr .
+.It Fl Z , Fl Fl 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 Fl z , Fl Fl 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 , Fl Fl denyinterfaces
+then it is still denied.
+.It Fl Fl nodev
+Don't load any
+.Pa /dev
+management modules.
+.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 is 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 for all IPv4
+configuration.
+.Sh FILES
+.Bl -ohang
+.It Pa @SYSCONFDIR@/dhcpcd.conf
+Configuration file for dhcpcd.
+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 de-configure an interface.
+.It Pa @LIBDIR@/dhcpcd/dev
+.Pa /dev
+management modules.
+.It Pa @HOOKDIR@
+A directory containing bourne shell scripts that are run by the above script.
+Each script can be disabled by using the
+.Fl C , Fl Fl nohook
+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 @DBDIR@/dhcpcd-rdm.monotonic
+Stores the monotonic counter used in the
+.Ar replay
+field in Authentication Options.
+.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
+running on the
+.Ar interface .
+.El
+.Sh SEE ALSO
+.Xr fnmatch 3 ,
+.Xr if_nametoindex 3 ,
+.Xr dhcpcd.conf 5 ,
+.Xr resolv.conf 5 ,
+.Xr dhcpcd-run-hooks 8 ,
+.Xr resolvconf 8
+.Sh STANDARDS
+RFC\ 951, RFC\ 1534, RFC\ 2104, RFC\ 2131, RFC\ 2132, RFC\ 2855, RFC\ 3004,
+RFC\ 3118, RFC\ 3315, RFC\ 3361, RFC\ 3633, RFC\ 3396, RFC\ 3397, RFC\ 3442,
+RFC\ 3495, RFC\ 3925, RFC\ 3927, RFC\ 4039, RFC\ 4075, RFC\ 4242, RFC\ 4361,
+RFC\ 4390, RFC\ 4702, RFC\ 4074, RFC\ 4861, RFC\ 4833, RFC\ 5227, RFC\ 5942,
+RFC\ 5969, RFC\ 6106.
+.Sh AUTHORS
+.An Roy Marples Aq Mt roy@marples.name
+.Sh BUGS
+Please report them to
+.Lk http://roy.marples.name/projects/dhcpcd
+.Pp
+If authentication is used and the
+.Pa @DBDIR@/dhcpcd-rdm.monotonic
+file is removed or altered then the DHCP server will need it's notion
+of the last replay value
+.Nm
+sent reset.
+We could change this to use a NTP time stamp instead, but it's
+more likely the RTC on this host is broken which would cause the same result.
diff --git a/dhcpcd/dhcpcd.c b/dhcpcd/dhcpcd.c
new file mode 100644
index 00000000..70210f11
--- /dev/null
+++ b/dhcpcd/dhcpcd.c
@@ -0,0 +1,1477 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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.
+ */
+
+const char dhcpcd_copyright[] = "Copyright (c) 2006-2014 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 <sys/utsname.h>
+
+#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 "config.h"
+#include "arp.h"
+#include "common.h"
+#include "control.h"
+#include "dev.h"
+#include "dhcpcd.h"
+#include "dhcp6.h"
+#include "duid.h"
+#include "eloop.h"
+#include "if-options.h"
+#include "if-pref.h"
+#include "ipv4.h"
+#include "ipv6.h"
+#include "ipv6nd.h"
+#include "net.h"
+#include "platform.h"
+#include "script.h"
+
+struct if_head *ifaces = NULL;
+char vendor[VENDORCLASSID_MAX_LEN];
+int pidfd = -1;
+struct if_options *if_options = NULL;
+int ifac = 0;
+char **ifav = NULL;
+int ifdc = 0;
+char **ifdv = NULL;
+
+sigset_t dhcpcd_sigset;
+const int handle_sigs[] = {
+ SIGALRM,
+ SIGHUP,
+ SIGINT,
+ SIGPIPE,
+ SIGTERM,
+ SIGUSR1,
+ 0
+};
+
+static char *cffile;
+static char *pidfile;
+static int linkfd = -1;
+static char **ifv;
+static int ifc;
+static char **margv;
+static int margc;
+
+static pid_t
+read_pid(void)
+{
+ FILE *fp;
+ pid_t pid;
+
+ if ((fp = fopen(pidfile, "r")) == NULL) {
+ errno = ENOENT;
+ return 0;
+ }
+ if (fscanf(fp, "%d", &pid) != 1)
+ pid = 0;
+ fclose(fp);
+ return pid;
+}
+
+static void
+usage(void)
+{
+
+printf("usage: "PACKAGE"\t[-46ABbDdEGgHJKkLnpqTVw]\n"
+ "\t\t[-C, --nohook hook] [-c, --script script]\n"
+ "\t\t[-e, --env value] [-F, --fqdn FQDN] [-f, --config file]\n"
+ "\t\t[-h, --hostname hostname] [-I, --clientid clientid]\n"
+ "\t\t[-i, --vendorclassid vendorclassid] [-l, --leasetime seconds]\n"
+ "\t\t[-m, --metric metric] [-O, --nooption option]\n"
+ "\t\t[-o, --option option] [-Q, --require option]\n"
+ "\t\t[-r, --request address] [-S, --static value]\n"
+ "\t\t[-s, --inform address[/cidr]] [-t, --timeout seconds]\n"
+ "\t\t[-u, --userclass class] [-v, --vendor code, value]\n"
+ "\t\t[-W, --whitelist address[/cidr]] [-y, --reboot seconds]\n"
+ "\t\t[-X, --blacklist address[/cidr]] [-Z, --denyinterfaces pattern]\n"
+ "\t\t[-z, --allowinterfaces pattern] [interface] [...]\n"
+ " "PACKAGE"\t-k, --release [interface]\n"
+ " "PACKAGE"\t-U, --dumplease interface\n"
+ " "PACKAGE"\t--version\n"
+ " "PACKAGE"\t-x, --exit [interface]\n");
+}
+
+static void
+free_globals(void)
+{
+ int i;
+ size_t n;
+ struct dhcp_opt *opt;
+
+ for (i = 0; i < ifac; i++)
+ free(ifav[i]);
+ free(ifav);
+ for (i = 0; i < ifdc; i++)
+ free(ifdv[i]);
+ free(ifdv);
+
+#ifdef INET
+ for (n = 0, opt = dhcp_opts; n < dhcp_opts_len; n++, opt++)
+ free_dhcp_opt_embenc(opt);
+ free(dhcp_opts);
+#endif
+#ifdef INET6
+ for (n = 0, opt = dhcp6_opts; n < dhcp6_opts_len; n++, opt++)
+ free_dhcp_opt_embenc(opt);
+ free(dhcp6_opts);
+#endif
+ for (n = 0, opt = vivso; n < vivso_len; n++, opt++)
+ free_dhcp_opt_embenc(opt);
+ free(vivso);
+}
+
+static void
+cleanup(void)
+{
+#ifdef DEBUG_MEMORY
+ struct interface *ifp;
+
+ free(duid);
+ free_options(if_options);
+
+ if (ifaces) {
+ while ((ifp = TAILQ_FIRST(ifaces))) {
+ TAILQ_REMOVE(ifaces, ifp, next);
+ free_interface(ifp);
+ }
+ free(ifaces);
+ }
+
+ free_globals();
+#endif
+
+ if (!(options & DHCPCD_FORKED))
+ dev_stop();
+ if (linkfd != -1)
+ close(linkfd);
+ if (pidfd > -1) {
+ if (options & DHCPCD_MASTER) {
+ if (control_stop() == -1)
+ syslog(LOG_ERR, "control_stop: %m");
+ }
+ close(pidfd);
+ unlink(pidfile);
+ }
+#ifdef DEBUG_MEMORY
+ free(pidfile);
+#endif
+
+ if (options & DHCPCD_STARTED && !(options & DHCPCD_FORKED))
+ syslog(LOG_INFO, "exited");
+}
+
+/* ARGSUSED */
+static void
+handle_exit_timeout(__unused void *arg)
+{
+ int timeout;
+
+ syslog(LOG_ERR, "timed out");
+ if (!(options & DHCPCD_IPV4) || !(options & DHCPCD_TIMEOUT_IPV4LL)) {
+ if (options & DHCPCD_MASTER) {
+ /* We've timed out, so remove the waitip requirements.
+ * If the user doesn't like this they can always set
+ * an infinite timeout. */
+ options &=
+ ~(DHCPCD_WAITIP | DHCPCD_WAITIP4 | DHCPCD_WAITIP6);
+ daemonise();
+ return;
+ } else
+ exit(EXIT_FAILURE);
+ }
+ options &= ~DHCPCD_TIMEOUT_IPV4LL;
+ timeout = (PROBE_NUM * PROBE_MAX) + (PROBE_WAIT * 2);
+ syslog(LOG_WARNING, "allowing %d seconds for IPv4LL timeout", timeout);
+ eloop_timeout_add_sec(timeout, handle_exit_timeout, NULL);
+}
+
+pid_t
+daemonise(void)
+{
+#ifdef THERE_IS_NO_FORK
+ return -1;
+#else
+ pid_t pid;
+ char buf = '\0';
+ int sidpipe[2], fd;
+
+ if (options & DHCPCD_DAEMONISE && !(options & DHCPCD_DAEMONISED)) {
+ if (options & DHCPCD_WAITIP4 &&
+ !ipv4_addrexists(NULL))
+ return -1;
+ if (options & DHCPCD_WAITIP6 &&
+ !ipv6nd_addrexists(NULL) &&
+ !dhcp6_addrexists(NULL))
+ return -1;
+ if ((options &
+ (DHCPCD_WAITIP | DHCPCD_WAITIP4 | DHCPCD_WAITIP6)) ==
+ DHCPCD_WAITIP &&
+ !ipv4_addrexists(NULL) &&
+ !ipv6nd_addrexists(NULL) &&
+ !dhcp6_addrexists(NULL))
+ return -1;
+ }
+
+ eloop_timeout_delete(handle_exit_timeout, NULL);
+ if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
+ return 0;
+ /* 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);
+ close(fd);
+ }
+ break;
+ default:
+ /* 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;
+ options |= DHCPCD_FORKED;
+ exit(EXIT_SUCCESS);
+ }
+ options |= DHCPCD_DAEMONISED;
+ return pid;
+#endif
+}
+
+struct interface *
+find_interface(const char *ifname)
+{
+ struct interface *ifp;
+
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ if (strcmp(ifp->name, ifname) == 0)
+ return ifp;
+ }
+ return NULL;
+}
+
+static void
+stop_interface(struct interface *ifp)
+{
+
+ syslog(LOG_INFO, "%s: removing interface", ifp->name);
+ ifp->options->options |= DHCPCD_STOPPING;
+
+ // Remove the interface from our list
+ TAILQ_REMOVE(ifaces, ifp, next);
+ dhcp6_drop(ifp, NULL);
+ ipv6nd_drop(ifp);
+ dhcp_drop(ifp, "STOP");
+ dhcp_close(ifp);
+ eloop_timeout_delete(NULL, ifp);
+ if (ifp->options->options & DHCPCD_DEPARTED)
+ script_runreason(ifp, "DEPARTED");
+ free_interface(ifp);
+ if (!(options & (DHCPCD_MASTER | DHCPCD_TEST)))
+ exit(EXIT_FAILURE);
+}
+
+static void
+configure_interface1(struct interface *ifp)
+{
+ struct if_options *ifo = ifp->options;
+ int ra_global, ra_iface;
+
+ /* Do any platform specific configuration */
+ if_conf(ifp);
+
+ /* If we want to release a lease, we can't really persist the
+ * address either. */
+ if (ifo->options & DHCPCD_RELEASE)
+ ifo->options &= ~DHCPCD_PERSISTENT;
+
+ if (ifp->flags & IFF_POINTOPOINT && !(ifo->options & DHCPCD_INFORM))
+ ifo->options |= DHCPCD_STATIC;
+ if (ifp->flags & IFF_NOARP ||
+ ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))
+ ifo->options &= ~(DHCPCD_ARP | DHCPCD_IPV4LL);
+ if (!(ifp->flags & (IFF_POINTOPOINT | IFF_LOOPBACK | IFF_MULTICAST)))
+ ifo->options &= ~DHCPCD_IPV6RS;
+ if (ifo->options & DHCPCD_LINK && carrier_status(ifp) == LINK_UNKNOWN)
+ ifo->options &= ~DHCPCD_LINK;
+
+ if (ifo->metric != -1)
+ ifp->metric = ifo->metric;
+
+ if (!(ifo->options & DHCPCD_IPV6))
+ ifo->options &= ~DHCPCD_IPV6RS;
+
+ /* We want to disable kernel interface RA as early as possible. */
+ if (ifo->options & DHCPCD_IPV6RS) {
+ ra_global = check_ipv6(NULL, options & DHCPCD_IPV6RA_OWN ? 1:0);
+ ra_iface = check_ipv6(ifp->name,
+ ifp->options->options & DHCPCD_IPV6RA_OWN ? 1 : 0);
+ if (ra_global == -1 || ra_iface == -1)
+ ifo->options &= ~DHCPCD_IPV6RS;
+ else if (ra_iface == 0)
+ ifo->options |= DHCPCD_IPV6RA_OWN;
+ }
+
+ /* 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 (ifp->hwlen > DHCP_CHADDR_LEN)
+ ifo->options |= DHCPCD_CLIENTID;
+
+ /* Firewire and InfiniBand interfaces require ClientID and
+ * the broadcast option being set. */
+ switch (ifp->family) {
+ case ARPHRD_IEEE1394: /* FALLTHROUGH */
+ case ARPHRD_INFINIBAND:
+ ifo->options |= DHCPCD_CLIENTID | DHCPCD_BROADCAST;
+ break;
+ }
+
+ if (!(ifo->options & DHCPCD_IAID)) {
+ /*
+ * An IAID is for identifying a unqiue interface within
+ * the client. It is 4 bytes long. Working out a default
+ * value is problematic.
+ *
+ * Interface name and number are not stable
+ * between different OS's. Some OS's also cannot make
+ * up their mind what the interface should be called
+ * (yes, udev, I'm looking at you).
+ * Also, the name could be longer than 4 bytes.
+ * Also, with pluggable interfaces the name and index
+ * could easily get swapped per actual interface.
+ *
+ * The MAC address is 6 bytes long, the final 3
+ * being unique to the manufacturer and the initial 3
+ * being unique to the organisation which makes it.
+ * We could use the last 4 bytes of the MAC address
+ * as the IAID as it's the most stable part given the
+ * above, but equally it's not guaranteed to be
+ * unique.
+ *
+ * Given the above, and our need to reliably work
+ * between reboots without persitent storage,
+ * generating the IAID from the MAC address is the only
+ * logical default.
+ *
+ * dhclient uses the last 4 bytes of the MAC address.
+ * dibbler uses an increamenting counter.
+ * wide-dhcpv6 uses 0 or a configured value.
+ * odhcp6c uses 1.
+ * Windows 7 uses the first 3 bytes of the MAC address
+ * and an unknown byte.
+ * dhcpcd-6.1.0 and earlier used the interface name,
+ * falling back to interface index if name > 4.
+ */
+ memcpy(ifo->iaid, ifp->hwaddr + ifp->hwlen - sizeof(ifo->iaid),
+ sizeof(ifo->iaid));
+#if 0
+ len = strlen(ifp->name);
+ if (len <= sizeof(ifo->iaid)) {
+ memcpy(ifo->iaid, ifp->name, len);
+ memset(ifo->iaid + len, 0, sizeof(ifo->iaid) - len);
+ } else {
+ /* IAID is the same size as a uint32_t */
+ len = htonl(ifp->index);
+ memcpy(ifo->iaid, &len, sizeof(len));
+ }
+#endif
+ ifo->options |= DHCPCD_IAID;
+ }
+
+#ifdef INET6
+ if (ifo->ia == NULL && ifo->options & DHCPCD_IPV6) {
+ ifo->ia = malloc(sizeof(*ifo->ia));
+ if (ifo->ia == NULL)
+ syslog(LOG_ERR, "%s: %m", __func__);
+ else {
+ if (ifo->ia_type == 0)
+ ifo->ia_type = D6_OPTION_IA_NA;
+ memcpy(ifo->ia->iaid, ifo->iaid, sizeof(ifo->iaid));
+ ifo->ia_len = 1;
+ ifo->ia->sla = NULL;
+ ifo->ia->sla_len = 0;
+ }
+ }
+#endif
+
+ /* If we are not sending an authentication option, don't require it */
+ if (!(ifo->auth.options & DHCPCD_AUTH_SEND))
+ ifo->auth.options &= ~DHCPCD_AUTH_REQUIRE;
+
+}
+
+int
+select_profile(struct interface *ifp, const char *profile)
+{
+ struct if_options *ifo;
+ int ret;
+
+ ret = 0;
+ ifo = read_config(cffile, ifp->name, ifp->ssid, profile);
+ if (ifo == NULL) {
+ syslog(LOG_DEBUG, "%s: no profile %s", ifp->name, profile);
+ ret = -1;
+ goto exit;
+ }
+ if (profile != NULL) {
+ strlcpy(ifp->profile, profile, sizeof(ifp->profile));
+ syslog(LOG_INFO, "%s: selected profile %s",
+ ifp->name, profile);
+ } else
+ *ifp->profile = '\0';
+ free_options(ifp->options);
+ ifp->options = ifo;
+
+exit:
+ if (profile)
+ configure_interface1(ifp);
+ return ret;
+}
+
+static void
+configure_interface(struct interface *ifp, int argc, char **argv)
+{
+
+ select_profile(ifp, NULL);
+ add_options(ifp->name, ifp->options, argc, argv);
+ configure_interface1(ifp);
+}
+
+void
+handle_carrier(int carrier, int flags, const char *ifname)
+{
+ struct interface *ifp;
+
+ ifp = find_interface(ifname);
+ if (ifp == NULL || !(ifp->options->options & DHCPCD_LINK))
+ return;
+
+ if (carrier == LINK_UNKNOWN)
+ carrier = carrier_status(ifp); /* will set ifp->flags */
+ else
+ ifp->flags = flags;
+
+ if (carrier == LINK_UNKNOWN)
+ syslog(LOG_ERR, "%s: carrier_status: %m", ifname);
+ /* IFF_RUNNING is checked, if needed, earlier and is OS dependant */
+ else if (carrier == LINK_DOWN || (ifp->flags & IFF_UP) == 0) {
+ if (ifp->carrier != LINK_DOWN) {
+ if (ifp->carrier == LINK_UP)
+ syslog(LOG_INFO, "%s: carrier lost", ifp->name);
+ ifp->carrier = LINK_DOWN;
+ dhcp_close(ifp);
+ dhcp6_drop(ifp, "EXPIRE6");
+ ipv6nd_drop(ifp);
+ /* Don't blindly delete our knowledge of LL addresses.
+ * We need to listen to what the kernel does with
+ * them as some OS's will remove, mark tentative or
+ * do nothing. */
+ ipv6_free_ll_callbacks(ifp);
+ dhcp_drop(ifp, "NOCARRIER");
+ }
+ } else if (carrier == LINK_UP && ifp->flags & IFF_UP) {
+ if (ifp->carrier != LINK_UP) {
+ syslog(LOG_INFO, "%s: carrier acquired", ifp->name);
+ ifp->carrier = LINK_UP;
+#if !defined(__linux__) && !defined(__NetBSD__)
+ /* BSD does not emit RTM_NEWADDR or RTM_CHGADDR when the
+ * hardware address changes so we have to go
+ * through the disovery process to work it out. */
+ handle_interface(0, ifp->name);
+#endif
+ if (ifp->wireless)
+ getifssid(ifp->name, ifp->ssid);
+ configure_interface(ifp, margc, margv);
+ script_runreason(ifp, "CARRIER");
+ start_interface(ifp);
+ }
+ }
+}
+
+static void
+warn_iaid_conflict(struct interface *ifp, uint8_t *iaid)
+{
+ struct interface *ifn;
+ size_t i;
+
+ TAILQ_FOREACH(ifn, ifaces, next) {
+ if (ifn == ifp)
+ continue;
+ if (memcmp(ifn->options->iaid, iaid,
+ sizeof(ifn->options->iaid)) == 0)
+ break;
+ for (i = 0; i < ifn->options->ia_len; i++) {
+ if (memcmp(&ifn->options->ia[i].iaid, iaid,
+ sizeof(ifn->options->ia[i].iaid)) == 0)
+ break;
+ }
+ }
+
+ /* This is only a problem if the interfaces are on the same network. */
+ if (ifn)
+ syslog(LOG_ERR,
+ "%s: IAID conflicts with one assigned to %s",
+ ifp->name, ifn->name);
+}
+
+void
+start_interface(void *arg)
+{
+ struct interface *ifp = arg;
+ struct if_options *ifo = ifp->options;
+ int nolease;
+ size_t i;
+
+ handle_carrier(LINK_UNKNOWN, 0, ifp->name);
+ if (ifp->carrier == LINK_DOWN) {
+ syslog(LOG_INFO, "%s: waiting for carrier", ifp->name);
+ return;
+ }
+
+ if (ifo->options & (DHCPCD_DUID | DHCPCD_IPV6)) {
+ /* Report client DUID */
+ if (duid == NULL) {
+ if (duid_init(ifp) == 0)
+ return;
+ syslog(LOG_INFO, "DUID %s",
+ hwaddr_ntoa(duid, duid_len));
+ }
+
+ /* Report IAIDs */
+ syslog(LOG_INFO, "%s: IAID %s", ifp->name,
+ hwaddr_ntoa(ifo->iaid, sizeof(ifo->iaid)));
+ warn_iaid_conflict(ifp, ifo->iaid);
+ for (i = 0; i < ifo->ia_len; i++) {
+ if (memcmp(ifo->iaid, ifo->ia[i].iaid,
+ sizeof(ifo->iaid)))
+ {
+ syslog(LOG_INFO, "%s: IAID %s", ifp->name,
+ hwaddr_ntoa(ifo->ia[i].iaid,
+ sizeof(ifo->ia[i].iaid)));
+ warn_iaid_conflict(ifp, ifo->ia[i].iaid);
+ }
+ }
+ }
+
+ if (ifo->options & DHCPCD_IPV6) {
+ if (ifo->options & DHCPCD_IPV6RS &&
+ !(ifo->options & DHCPCD_INFORM))
+ ipv6nd_startrs(ifp);
+
+ if (!(ifo->options & DHCPCD_IPV6RS)) {
+ if (ifo->options & DHCPCD_IA_FORCED)
+ nolease = dhcp6_start(ifp, DH6S_INIT);
+ else {
+ nolease = dhcp6_find_delegates(ifp);
+ /* Enabling the below doesn't really make
+ * sense as there is currently no standard
+ * to push routes via DHCPv6.
+ * (There is an expired working draft,
+ * maybe abandoned?)
+ * You can also get it to work by forcing
+ * an IA as shown above. */
+#if 0
+ /* With no RS or delegates we might
+ * as well try and solicit a DHCPv6 address */
+ if (nolease == 0)
+ nolease = dhcp6_start(ifp, DH6S_INIT);
+#endif
+ }
+ if (nolease == -1)
+ syslog(LOG_ERR,
+ "%s: dhcp6_start: %m", ifp->name);
+ }
+ }
+
+ if (ifo->options & DHCPCD_IPV4)
+ dhcp_start(ifp);
+}
+
+/* ARGSUSED */
+static void
+handle_link(__unused void *arg)
+{
+
+ if (manage_link(linkfd) == -1 && errno != ENXIO && errno != ENODEV)
+ syslog(LOG_ERR, "manage_link: %m");
+}
+
+static void
+init_state(struct interface *ifp, int argc, char **argv)
+{
+ struct if_options *ifo;
+ const char *reason = NULL;
+
+ configure_interface(ifp, argc, argv);
+ ifo = ifp->options;
+
+ if (ifo->options & DHCPCD_IPV4 && ipv4_init() == -1) {
+ syslog(LOG_ERR, "ipv4_init: %m");
+ ifo->options &= ~DHCPCD_IPV4;
+ }
+ if (ifo->options & DHCPCD_IPV6 && ipv6_init() == -1) {
+ syslog(LOG_ERR, "ipv6_init: %m");
+ ifo->options &= ~DHCPCD_IPV6RS;
+ }
+
+ if (!(options & DHCPCD_TEST))
+ script_runreason(ifp, "PREINIT");
+
+ if (ifo->options & DHCPCD_LINK) {
+ switch (carrier_status(ifp)) {
+ case LINK_DOWN:
+ ifp->carrier = LINK_DOWN;
+ reason = "NOCARRIER";
+ break;
+ case LINK_UP:
+ ifp->carrier = LINK_UP;
+ reason = "CARRIER";
+ break;
+ default:
+ ifp->carrier = LINK_UNKNOWN;
+ return;
+ }
+ if (reason && !(options & DHCPCD_TEST))
+ script_runreason(ifp, reason);
+ } else
+ ifp->carrier = LINK_UNKNOWN;
+}
+
+void
+handle_interface(int action, const char *ifname)
+{
+ struct if_head *ifs;
+ struct interface *ifp, *ifn, *ifl = NULL;
+ const char * const argv[] = { ifname };
+ int i;
+
+ if (action == -1) {
+ ifp = find_interface(ifname);
+ if (ifp != NULL) {
+ ifp->options->options |= DHCPCD_DEPARTED;
+ stop_interface(ifp);
+ }
+ 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));
+ TAILQ_FOREACH_SAFE(ifp, ifs, next, ifn) {
+ if (strcmp(ifp->name, ifname) != 0)
+ continue;
+ /* Check if we already have the interface */
+ ifl = find_interface(ifp->name);
+ if (ifl) {
+ /* The flags and hwaddr could have changed */
+ ifl->flags = ifp->flags;
+ ifl->hwlen = ifp->hwlen;
+ if (ifp->hwlen != 0)
+ memcpy(ifl->hwaddr, ifp->hwaddr, ifl->hwlen);
+ } else {
+ TAILQ_REMOVE(ifs, ifp, next);
+ TAILQ_INSERT_TAIL(ifaces, ifp, next);
+ }
+ if (action == 1) {
+ init_state(ifp, margc, margv);
+ start_interface(ifp);
+ }
+ }
+
+ /* Free our discovered list */
+ while ((ifp = TAILQ_FIRST(ifs))) {
+ TAILQ_REMOVE(ifs, ifp, next);
+ free_interface(ifp);
+ }
+ free(ifs);
+}
+
+void
+handle_hwaddr(const char *ifname, const uint8_t *hwaddr, size_t hwlen)
+{
+ struct interface *ifp;
+
+ ifp = find_interface(ifname);
+ if (ifp == NULL)
+ return;
+
+ if (hwlen > sizeof(ifp->hwaddr)) {
+ errno = ENOBUFS;
+ syslog(LOG_ERR, "%s: %s: %m", ifp->name, __func__);
+ return;
+ }
+
+ if (ifp->hwlen == hwlen && memcmp(ifp->hwaddr, hwaddr, hwlen) == 0)
+ return;
+
+ syslog(LOG_INFO, "%s: new hardware address: %s", ifp->name,
+ hwaddr_ntoa(hwaddr, hwlen));
+ ifp->hwlen = hwlen;
+ memcpy(ifp->hwaddr, hwaddr, hwlen);
+}
+
+static void
+if_reboot(struct interface *ifp, int argc, char **argv)
+{
+ int oldopts;
+
+ oldopts = ifp->options->options;
+ script_runreason(ifp, "RECONFIGURE");
+ configure_interface(ifp, argc, argv);
+ dhcp_reboot_newopts(ifp, oldopts);
+ dhcp6_reboot(ifp);
+ start_interface(ifp);
+}
+
+static void
+reconf_reboot(int action, int argc, char **argv, int oi)
+{
+ struct if_head *ifs;
+ struct interface *ifn, *ifp;
+
+ ifs = discover_interfaces(argc - oi, argv + oi);
+ if (ifs == NULL)
+ return;
+
+ while ((ifp = TAILQ_FIRST(ifs))) {
+ TAILQ_REMOVE(ifs, ifp, next);
+ ifn = find_interface(ifp->name);
+ if (ifn) {
+ if (action)
+ if_reboot(ifn, argc, argv);
+ else
+ ipv4_applyaddr(ifn);
+ free_interface(ifp);
+ } else {
+ init_state(ifp, argc, argv);
+ TAILQ_INSERT_TAIL(ifaces, ifp, next);
+ start_interface(ifp);
+ }
+ }
+ free(ifs);
+
+ sort_interfaces();
+}
+
+/* ARGSUSED */
+static void
+sig_reboot(void *arg)
+{
+ siginfo_t *siginfo = arg;
+ struct if_options *ifo;
+
+ syslog(LOG_INFO, "received SIGALRM from PID %d, rebinding",
+ (int)siginfo->si_pid);
+
+ free_globals();
+ ifav = NULL;
+ ifac = 0;
+ ifdc = 0;
+ ifdv = NULL;
+
+ ifo = read_config(cffile, NULL, NULL, NULL);
+ add_options(NULL, 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, ifc, ifv, 0);
+}
+
+static void
+sig_reconf(void *arg)
+{
+ siginfo_t *siginfo = arg;
+ struct interface *ifp;
+
+ syslog(LOG_INFO, "received SIGUSR from PID %d, reconfiguring",
+ (int)siginfo->si_pid);
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ ipv4_applyaddr(ifp);
+ }
+}
+
+static void
+handle_signal(int sig, siginfo_t *siginfo, __unused void *context)
+{
+ struct interface *ifp;
+ int do_release;
+
+ do_release = 0;
+ switch (sig) {
+ case SIGINT:
+ syslog(LOG_INFO, "received SIGINT from PID %d, stopping",
+ (int)siginfo->si_pid);
+ break;
+ case SIGTERM:
+ syslog(LOG_INFO, "received SIGTERM from PID %d, stopping",
+ (int)siginfo->si_pid);
+ break;
+ case SIGALRM:
+ eloop_timeout_add_now(sig_reboot, siginfo);
+ return;
+ case SIGHUP:
+ syslog(LOG_INFO, "received SIGHUP from PID %d, releasing",
+ (int)siginfo->si_pid);
+ do_release = 1;
+ break;
+ case SIGUSR1:
+ eloop_timeout_add_now(sig_reconf, siginfo);
+ return;
+ case SIGPIPE:
+ syslog(LOG_WARNING, "received SIGPIPE");
+ return;
+ default:
+ syslog(LOG_ERR,
+ "received signal %d from PID %d, "
+ "but don't know what to do with it",
+ sig, (int)siginfo->si_pid);
+ return;
+ }
+
+ if (options & DHCPCD_TEST)
+ exit(EXIT_FAILURE);
+
+ /* As drop_dhcp could re-arrange the order, we do it like this. */
+ for (;;) {
+ /* Be sane and drop the last config first */
+ ifp = TAILQ_LAST(ifaces, if_head);
+ if (ifp == NULL)
+ break;
+ if (do_release) {
+ ifp->options->options |= DHCPCD_RELEASE;
+ ifp->options->options &= ~DHCPCD_PERSISTENT;
+ }
+ ifp->options->options |= DHCPCD_EXITING;
+ stop_interface(ifp);
+ }
+ exit(EXIT_FAILURE);
+}
+
+int
+handle_args(struct fd_list *fd, int argc, char **argv)
+{
+ struct interface *ifp;
+ int do_exit = 0, do_release = 0, do_reboot = 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) {
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ len++;
+ if (D6_STATE_RUNNING(ifp))
+ len++;
+ if (ipv6nd_has_ra(ifp))
+ len++;
+ }
+ len = write(fd->fd, &len, sizeof(len));
+ if (len != sizeof(len))
+ return -1;
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ send_interface(fd->fd, ifp);
+ }
+ return 0;
+ }
+ opt = 0;
+ while (argv[++opt] != NULL) {
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ if (strcmp(argv[opt], ifp->name) == 0) {
+ len++;
+ if (D6_STATE_RUNNING(ifp))
+ len++;
+ if (ipv6nd_has_ra(ifp))
+ len++;
+ }
+ }
+ }
+ len = write(fd->fd, &len, sizeof(len));
+ if (len != sizeof(len))
+ return -1;
+ opt = 0;
+ while (argv[++opt] != NULL) {
+ TAILQ_FOREACH(ifp, ifaces, 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;
+ }
+ }
+
+ /* Log the command */
+ len = 0;
+ for (opt = 0; opt < argc; opt++)
+ len += strlen(argv[opt]) + 1;
+ tmp = p = malloc(len + 1);
+ if (tmp == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return -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);
+
+ optind = 0;
+ while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
+ {
+ switch (opt) {
+ case 'g':
+ /* Assumed if below not set */
+ break;
+ case 'k':
+ do_release = 1;
+ break;
+ case 'n':
+ do_reboot = 1;
+ break;
+ case 'x':
+ do_exit = 1;
+ break;
+ }
+ }
+
+ /* We need at least one interface */
+ if (optind == argc) {
+ syslog(LOG_ERR, "%s: no interface", __func__);
+ return -1;
+ }
+
+ if (do_release || do_exit) {
+ for (oi = optind; oi < argc; oi++) {
+ if ((ifp = find_interface(argv[oi])) == NULL)
+ continue;
+ if (do_release) {
+ ifp->options->options |= DHCPCD_RELEASE;
+ ifp->options->options &= ~DHCPCD_PERSISTENT;
+ }
+ ifp->options->options |= DHCPCD_EXITING;
+ stop_interface(ifp);
+ }
+ return 0;
+ }
+
+ reconf_reboot(do_reboot, argc, argv, optind);
+ return 0;
+}
+
+static int
+signal_init(void (*func)(int, siginfo_t *, void *), sigset_t *oldset)
+{
+ unsigned int i;
+ struct sigaction sa;
+ sigset_t newset;
+
+ sigfillset(&newset);
+ if (sigprocmask(SIG_SETMASK, &newset, oldset) == -1)
+ return -1;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = func;
+ sa.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+
+ for (i = 0; handle_sigs[i]; i++) {
+ if (sigaction(handle_sigs[i], &sa, NULL) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct interface *ifp;
+ uint16_t family = 0;
+ int opt, oi = 0, sig = 0, i, control_fd;
+ size_t len;
+ pid_t pid;
+ struct timespec ts;
+ struct utsname utn;
+ const char *platform;
+
+ closefrom(3);
+ openlog(PACKAGE, LOG_PERROR | LOG_PID, LOG_DAEMON);
+ setlogmask(LOG_UPTO(LOG_INFO));
+
+ /* Test for --help and --version */
+ if (argc > 1) {
+ if (strcmp(argv[1], "--help") == 0) {
+ usage();
+ exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[1], "--version") == 0) {
+ printf(""PACKAGE" "VERSION"\n%s\n", dhcpcd_copyright);
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ platform = hardware_platform();
+ if (uname(&utn) == 0)
+ snprintf(vendor, VENDORCLASSID_MAX_LEN,
+ "%s-%s:%s-%s:%s%s%s", PACKAGE, VERSION,
+ utn.sysname, utn.release, utn.machine,
+ platform ? ":" : "", platform ? platform : "");
+ else
+ snprintf(vendor, VENDORCLASSID_MAX_LEN,
+ "%s-%s", PACKAGE, VERSION);
+
+ i = 0;
+ while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
+ {
+ switch (opt) {
+ case '4':
+ family = AF_INET;
+ break;
+ case '6':
+ family = AF_INET6;
+ break;
+ case 'f':
+ cffile = optarg;
+ break;
+ case 'g':
+ sig = SIGUSR1;
+ break;
+ case 'k':
+ sig = SIGHUP;
+ break;
+ case 'n':
+ sig = SIGALRM;
+ break;
+ case 'x':
+ sig = SIGTERM;
+ break;
+ case 'T':
+ i = 1;
+ break;
+ case 'U':
+ i = 2;
+ break;
+ case 'V':
+ i = 3;
+ break;
+ case '?':
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ margv = argv;
+ margc = argc;
+ if_options = read_config(cffile, NULL, NULL, NULL);
+ opt = add_options(NULL, if_options, argc, argv);
+ if (opt != 1) {
+ if (opt == 0)
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ if (i == 3) {
+ printf("Interface options:\n");
+ if_printoptions();
+#ifdef INET
+ if (family == 0 || family == AF_INET) {
+ printf("\nDHCPv4 options:\n");
+ dhcp_printoptions();
+ }
+#endif
+#ifdef INET6
+ if (family == 0 || family == AF_INET6) {
+ printf("\nDHCPv6 options:\n");
+ dhcp6_printoptions();
+ }
+#endif
+#ifdef DEBUG_MEMORY
+ cleanup();
+#endif
+ exit(EXIT_SUCCESS);
+ }
+ 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 &= ~DHCPCD_DAEMONISE;
+#endif
+
+ if (options & DHCPCD_DEBUG)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ if (options & DHCPCD_QUIET) {
+ i = open(_PATH_DEVNULL, O_RDWR);
+ if (i == -1)
+ syslog(LOG_ERR, "%s: open: %m", __func__);
+ else {
+ dup2(i, STDERR_FILENO);
+ close(i);
+ }
+ }
+
+ 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 = malloc(len);
+ if (pidfile == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ exit(EXIT_FAILURE);
+ }
+ if (optind == argc - 1)
+ snprintf(pidfile, len, PIDFILE, "-", argv[optind]);
+ else {
+ snprintf(pidfile, len, PIDFILE, "", "");
+ options |= DHCPCD_MASTER;
+ }
+ }
+
+ if (chdir("/") == -1)
+ syslog(LOG_ERR, "chdir `/': %m");
+ atexit(cleanup);
+
+ if (options & DHCPCD_DUMPLEASE) {
+ if (optind != argc - 1) {
+ syslog(LOG_ERR, "dumplease requires an interface");
+ exit(EXIT_FAILURE);
+ }
+ if (dhcp_dump(argv[optind]) == -1)
+ exit(EXIT_FAILURE);
+ exit(EXIT_SUCCESS);
+ }
+
+ if (!(options & (DHCPCD_MASTER | DHCPCD_TEST))) {
+ control_fd = control_open();
+ if (control_fd != -1) {
+ syslog(LOG_INFO,
+ "sending commands to master dhcpcd process");
+ i = control_send(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, "control_open: %m");
+ }
+ }
+
+ if (geteuid())
+ syslog(LOG_WARNING,
+ PACKAGE " will not work correctly unless run as root");
+
+ if (sig != 0) {
+ pid = read_pid();
+ if (pid != 0)
+ syslog(LOG_INFO, "sending signal %d to pid %d",
+ sig, pid);
+ if (pid == 0 || kill(pid, sig) != 0) {
+ if (sig != SIGALRM && errno != EPERM)
+ 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 || sig == SIGUSR1)
+ exit(EXIT_SUCCESS);
+ /* Spin until it exits */
+ 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() == 0)
+ exit(EXIT_SUCCESS);
+ }
+ syslog(LOG_ERR, "pid %d failed to exit", pid);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (!(options & DHCPCD_TEST)) {
+ if ((pid = read_pid()) > 0 &&
+ kill(pid, 0) == 0)
+ {
+ 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);
+ if (mkdir(DBDIR, 0755) == -1 && errno != EEXIST)
+ syslog(LOG_ERR, "mkdir `%s': %m", DBDIR);
+
+ pidfd = open(pidfile, O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
+ if (pidfd == -1)
+ syslog(LOG_ERR, "open `%s': %m", pidfile);
+ else {
+ /* Lock the file so that only one instance of dhcpcd
+ * runs on an interface */
+ if (flock(pidfd, LOCK_EX | LOCK_NB) == -1) {
+ syslog(LOG_ERR, "flock `%s': %m", pidfile);
+ exit(EXIT_FAILURE);
+ }
+ if (set_cloexec(pidfd) == -1)
+ exit(EXIT_FAILURE);
+ writepid(pidfd, getpid());
+ }
+ }
+
+ syslog(LOG_INFO, "version " VERSION " starting");
+ options |= DHCPCD_STARTED;
+
+#ifdef DEBUG_MEMORY
+ eloop_init();
+#endif
+
+ /* Save signal mask, block and redirect signals to our handler */
+ if (signal_init(handle_signal, &dhcpcd_sigset) == -1) {
+ syslog(LOG_ERR, "signal_setup: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ if (options & DHCPCD_MASTER) {
+ if (control_start() == -1)
+ syslog(LOG_ERR, "control_start: %m");
+ }
+
+ if (open_sockets() == -1) {
+ syslog(LOG_ERR, "open_sockets: %m");
+ exit(EXIT_FAILURE);
+ }
+
+#if 0
+ if (options & DHCPCD_IPV6RS && disable_rtadv() == -1) {
+ syslog(LOG_ERR, "disable_rtadvd: %m");
+ options &= ~DHCPCD_IPV6RS;
+ }
+#endif
+
+ 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_BACKGROUND))
+ options |= DHCPCD_WAITIP;
+
+ /* RTM_NEWADDR goes through the link socket as well which we
+ * need for IPv6 DAD, so we check for DHCPCD_LINK in handle_carrier
+ * instead.
+ * We also need to open this before checking for interfaces below
+ * so that we pickup any new addresses during the discover phase. */
+ if (linkfd == -1) {
+ linkfd = open_link_socket();
+ if (linkfd == -1)
+ syslog(LOG_ERR, "open_link_socket: %m");
+ else
+ eloop_event_add(linkfd, handle_link, NULL);
+ }
+
+ /* Start any dev listening plugin which may want to
+ * change the interface name provided by the kernel */
+ if ((options & (DHCPCD_MASTER | DHCPCD_DEV)) ==
+ (DHCPCD_MASTER | DHCPCD_DEV))
+ dev_start(dev_load);
+
+ ifaces = discover_interfaces(ifc, ifv);
+ for (i = 0; i < ifc; i++) {
+ if (find_interface(ifv[i]) == NULL)
+ syslog(LOG_ERR, "%s: interface not found or invalid",
+ ifv[i]);
+ }
+ if (ifaces == NULL || TAILQ_FIRST(ifaces) == NULL) {
+ 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 (options & DHCPCD_BACKGROUND)
+ daemonise();
+
+ opt = 0;
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ init_state(ifp, argc, argv);
+ if (ifp->carrier != LINK_DOWN)
+ opt = 1;
+ }
+
+ 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);
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ handle_carrier(LINK_UNKNOWN, 0, ifp->name);
+ if (ifp->carrier != LINK_DOWN) {
+ opt = 1;
+ break;
+ }
+ }
+ }
+ if (options & DHCPCD_MASTER)
+ i = if_options->timeout;
+ else if ((ifp = TAILQ_FIRST(ifaces)))
+ i = ifp->options->timeout;
+ else
+ i = 0;
+ if (opt == 0 &&
+ options & DHCPCD_LINK &&
+ !(options & DHCPCD_WAITIP))
+ {
+ syslog(LOG_WARNING, "no interfaces have a carrier");
+ daemonise();
+ } else if (i > 0) {
+ if (options & DHCPCD_IPV4LL)
+ options |= DHCPCD_TIMEOUT_IPV4LL;
+ eloop_timeout_add_sec(i, handle_exit_timeout, NULL);
+ }
+ }
+ free_options(if_options);
+ if_options = NULL;
+
+ sort_interfaces();
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ eloop_timeout_add_sec(0, start_interface, ifp);
+ }
+
+ eloop_start(&dhcpcd_sigset);
+ exit(EXIT_SUCCESS);
+}
diff --git a/dhcpcd/dhcpcd.conf b/dhcpcd/dhcpcd.conf
new file mode 100644
index 00000000..96eaf523
--- /dev/null
+++ b/dhcpcd/dhcpcd.conf
@@ -0,0 +1,35 @@
+# A sample configuration for dhcpcd.
+# See dhcpcd.conf(5) for details.
+
+# Inform the DHCP server of our hostname for DDNS.
+hostname
+
+# Use the hardware address of the interface for the Client ID.
+#clientid
+# or
+# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.
+duid
+
+# Persist interface configuration when dhcpcd exits.
+persistent
+
+# Rapid commit support.
+# Safe to enable by default because it requires the equivalent option set
+# on the server to actually work.
+option rapid_commit
+
+# 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.
+# Some interface drivers reset when changing the MTU so disabled by default.
+#option interface_mtu
+
+# A ServerID is required by RFC2131.
+require dhcp_server_identifier
+
+# 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/dhcpcd.conf.5.in b/dhcpcd/dhcpcd.conf.5.in
new file mode 100644
index 00000000..888b3c14
--- /dev/null
+++ b/dhcpcd/dhcpcd.conf.5.in
@@ -0,0 +1,704 @@
+.\" Copyright (c) 2006-2014 Roy Marples
+.\" 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.
+.\"
+.Dd January 29, 2014
+.Dt DHCPCD.CONF 5
+.Os
+.Sh NAME
+.Nm dhcpcd.conf
+.Nd dhcpcd configuration file
+.Sh DESCRIPTION
+Although
+.Nm dhcpcd
+can do everything from the command line, there are cases where it's just easier
+to do it once in a configuration file.
+Most of the options found in
+.Xr dhcpcd 8
+can be used here.
+The first word on the line is the option and the rest of the line is the value.
+Leading and trailing whitespace for the option and value are trimmed.
+You can escape characters in the value using the \\ character.
+.Pp
+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 authprotocol Ar protocol Ar algorithm Ar rdm
+Authenticate DHCP messages.
+See the Supported Protocols section.
+.It Ic authtoken Ar secretid Ar realm Ar expire Ar key
+Define a shared key for use in authentication.
+.Ar realm can be "" to for use with the
+.Ar delayed
+prptocol.
+.Ar expire
+is the date the token expires and should be formatted "yyy-mm-dd HH:MM".
+You can use the keyword
+.Ar forever
+or
+.Ar 0
+which means the token never expires.
+.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 dev Ar value
+Load the
+.Ar value
+.Pa /dev
+management module.
+.Nm dhcpcd
+will load the first one found to work, if any.
+.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 .
+.Pp
+If the hostname is set, will be will set to the FQDN if possible as per
+RFC 4702 section 3.1.
+If the FQDN option is missing,
+.Nm dhcpcd
+will still try and set a FQDN from the hostname and domain options for
+consistency.
+To override this, set
+.Ic env
+.Va hostname_fqdn=[YES|NO|SERVER] .
+A value of server means just what the server says, don't manipulate it.
+This could lead to an inconsistent hostname on a DHCPv4 and DHCPv6 network
+where the DHCPv4 hostname is short and the DHCPv6 has an FQDN.
+DHCPv6 has no hostname option.
+.It Ic clientid Ar string
+Send the
+.Ar clientid .
+If the string is of the format 01:02:03 then it is encoded as hex.
+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
+.%T "RFC 4361"
+.Re
+compliant DHCP Unique Identifier.
+If persistent storage is available then a DUID-LLT (link local address + time)
+is generated, otherwise DUID-LL is generated (link local address).
+This, plus the IAID will be used as the
+.Ic clientid .
+The DUID-LLT generated will be held in
+.Pa @SYSCONFDIR@/dhcpcd.duid
+and should not be copied to other hosts.
+.It Ic iaid Ar iaid
+Set the Interface Association Identifier to
+.Ar iaid .
+This option must be used in an
+.Ic interface
+block.
+This defaults to the last 4 bytes of the hardware address assigned to the
+interface.
+Each instance of this should be unique within the scope of the client and
+.Nm dhcpcd
+warns if a conflict is detected.
+If there is a conflict, it is only a problem if the conflicted IAIDs are
+used on the same network.
+.It Ic persistent
+.Nm dhcpcd
+normally de-configures the interface and configuration when it exits.
+Sometimes, this isn't desirable if, for example, you have root mounted over
+NFS or SSH clients connect to this host and they need to be notified of
+the host shutting down.
+You can use this option to stop this from happening.
+.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
+.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
+is a FQDN (ie, contains a .) then it will be encoded as such.
+.It Ic hostname_short
+Sends the short hostname to the DHCP server instead of the FQDN.
+This is useful because DHCP servers will not register the FQDN in their
+DNS if the domain part does not match theirs.
+.Pp
+Also, see the
+.Ic env
+option above to control how the hostname is set on the host.
+.It Ic ia_na Op Ar iaid
+Request a DHCPv6 Normal Address for
+.Ar iaid .
+.Ar iaid
+defaults to the
+.Ic iaid
+option as described above.
+You can request more than one ia_na by specifying a unique
+.Ar iaid
+for each one.
+.It Ic ia_ta Op Ar iaid
+Request a DHCPv6 Temporary Address for
+.Ar iaid .
+You can request more than one ia_ta by specifying a unique
+.Ar iaid
+for each one.
+.It Ic ia_pd Op Ar iaid Op Ar interface Op / Ar sla_id Op / Ar prefix_len
+Request a DHCPv6 Delegated Prefix for
+.Ar iaid .
+This option must be used in an
+.Ic interface
+block.
+If no
+.Ar interface
+is given then we will assign a prefix to every other interface with a unique
+.Ar sla_id
+for each, starting from 0.
+Otherwise addresses are only assigned for each
+.Ar interface
+and
+.Ar sla_id .
+You cannot assign a prefix to the requesting interface.
+.Nm dhcpcd
+has to be running for all the interfaces it is delegating to.
+A default
+.Ar prefix_len
+of 64 is assumed.
+.Ar sla_id
+is an integer and is added to the prefix which must fit inside
+.Ar prefix_len
+less the length of the delegated prefix.
+You can specify multiple
+.Ar interface /
+.Ar sla_id /
+.Ar prefix_len
+per
+.Ic ia_pd ,
+space separated.
+IPv6RS should be disabled globally when requesting a Prefix Delegation like so:
+.Pp
+.D1 noipv6rs
+.D1 # Don't touch eth3 at all
+.D1 denyinterfaces eth3
+.Pp
+.D1 interface eth0
+.D1 ia_pd 1 eth1/0 eth2/1
+.Pp
+.D1 # Disable automatic address configuration for eth1
+.D1 # eth1 still gets a delegated prefix
+.D1 interface eth1
+.D1 noipv4
+.D1 noipv6
+.It Ic ipv4only
+Only configure IPv4.
+.It Ic ipv6only
+Only confgiure IPv6.
+.It Ic fqdn Op disable | ptr | both
+ptr just asks the DHCP server to update the PTR
+record of the host in DNS whereas both also updates the A record.
+disable will disable the FQDN option.
+The default is both.
+.Nm dhcpcd
+itself never does any DNS updates.
+.Nm dhcpcd
+encodes the FQDN hostname as specified in
+.Li RFC1035 .
+.It Ic interface Ar interface
+Subsequent options are only parsed for this
+.Ar interface .
+.It Ic ipv6ra_fork
+By default, when
+.Nm dhcpcd
+receives an IPv6 RA,
+.Nm dhcpcd
+will only fork to the background if the RA contains at least one unexpired
+RDNSS option.
+Set this option so to make
+.Nm dhcpcd
+always fork on an RA.
+.It Ic ipv6ra_own
+Disables kernel IPv6 Router Advertisment processing so dhcpcd can manage
+addresses and routes.
+This does not work reliably on any BSD system, probably due to kernel issues.
+.It Ic ipv6ra_own_default
+Each time dhcpcd receives an IPv6 Router Adveristment, dhcpcd will manage
+the default route only.
+This allows dhcpcd to prefer an interface for outbound traffic based on metric
+and/or user selection rather than the kernel.
+This does work reliably on BSD systems.
+.It Ic ipv6rs
+Enables IPv6 Router Advertisment solicitation.
+This is on by default, but is documented here in the case where it is disabled
+globally but needs to be enabled for one interface.
+.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 noalias
+IPv4 addresses added will overwrite a pre-existing address instead of working
+alongside.
+.It Ic noarp
+Don't send any ARP requests.
+This also disables IPv4LL.
+.It Ic noauthrequired
+Don't require authentication even though we requested it.
+.It Ic nodev
+Don't load
+.Pa /dev
+management modules.
+.It Ic nodhcp
+Don't start DHCP or listen to DHCP messages.
+This is only useful when allowing IPv4LL.
+.It Ic nodhcp6
+Don't start DHCPv6 or listen to DHCPv6 messages.
+Normally DHCPv6 is started by a RA instruction or configuration.
+.It Ic nogateway
+Don't install any default routes.
+.It Ic nohook Ar script
+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 noipv4
+Don't attempt to configure an IPv4 address.
+.It Ic noipv4ll
+Don't attempt to obtain an IPv4LL address if we failed to get one via DHCP.
+See
+.Rs
+.%T "RFC 3927"
+.Re
+.It Ic noipv6
+Don't attmept to configure an IPv6 address.
+.It Ic noipv6rs
+Disable solicitation and receipt of IPv6 Router Advertisements.
+.It Ic nolink
+Don't receive link messages about carrier status.
+You should only set this for buggy interface drivers.
+.It Ic option Ar option
+Requests the
+.Ar option
+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
+.Ar option Ns s
+separated by commas, spaces or more
+.Ic option
+lines.
+Prepend dhcp6_ to
+.Ar option
+to request a DHCPv6 option.
+DHCPv4 options are mapped to DHCPv6 where applicable.
+.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 5 seconds.
+A setting of 0 seconds causes
+.Nm dhcpcd
+to skip the REBOOT phase and go straight into DISCOVER.
+This is desirable for mobile users because if you change from network A to
+network B and they use the same subnet and the address from network A isn't
+in use on network B, then the DHCP server will remain silent even if authorative
+which means
+.Nm dhcpcd
+will timeout before moving back to the DISCOVER phase.
+.It Ic release
+.Nm dhcpcd
+will release the lease prior to stopping the interface.
+.It Ic require Ar option
+Requires the
+.Ar option
+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 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
+Timeout after
+.Ar seconds ,
+instead of the default 30.
+A setting of 0
+.Ar seconds
+causes
+.Nm dhcpcd
+to wait forever to get a lease.
+If
+.Nm dhcpcd
+is working on a single interface then
+.Nm dhcpcd
+will exit when a timeout occurs, otherwise
+.Nm dhcpcd
+will fork into the background.
+If using IPv4LL then
+.Nm dhcpcd
+start the IPv4LL process after the timeout and then wait a little longer
+before really timing out.
+.It Ic userclass Ar string
+Tag the DHCPv4 messages with the userclass.
+You can specify more than one.
+.It Ic vendor Ar code , Ns Ar value
+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 vendor 01,192.168.0.2
+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
+Set the DHCP Vendor Class.
+DHCPv6 has it's own option as shown below.
+The default is
+dhcpcd-<version>:<os>:<machine>:<platform>.
+For example
+.D1 dhcpcd-5.5.6:NetBSD-6.99.5:i386:i386
+If not set then none is sent.
+Some badly configured DHCP servers reject unknown vendorclassids.
+To work around it, try and impersonate Windows by using the MSFT vendorclassid.
+.It Ic vendclass Ar en Ar data
+Add the DHCPv6 Vendor Indetifying Vendor Class with the IANA assigned Enterprise
+Number
+.Ar en
+with the
+.Ar data .
+This option can be set more than once to add more data, but the behaviour,
+as per
+.Xr RFC 3925
+is undefined if the Enterprise Number differs.
+.It Ic waitip Op 4 | 6
+Wait for an address to be assigned before forking to the background.
+4 means wait for an IPv4 address to be assigned.
+6 means wait for an IPv6 address to be assigned.
+If no argument is given,
+.Nm
+will wait for any address protocol to be assigned.
+It is possible to wait for more than one address protocol and
+.Nm
+will only fork to the background when all waiting conditions are satisfied.
+.It Ic xidhwaddr
+Use the last four bytes of the hardware address as the DHCP xid instead
+of a randomly generated number.
+.El
+.Ss Defining new options
+DHCP allows for the use of custom options.
+Each option needs to be started with the
+.Ic define
+or
+.Ic define6
+directive.
+This can optionally be followed by both
+.Ic embed
+or
+.Ic encap
+options.
+Both can be specified more than once and
+.Ic embed
+must come before
+.Ic encap .
+.Bl -tag -width indent
+.It Ic define Ar code Ar type Ar variable
+Defines the DHCP option
+.Ar code
+of
+.Ar type
+with a name of
+.Ar variable
+exported to
+.Xr dhcpcd-run-hooks 8 .
+.It Ic define6 Ar code Ar type Ar variable
+Defines the DHCPv6 option
+.Ar code
+of
+.Ar type
+with a name of
+.Ar variable
+exported to
+.Xr dhcpcd-run-hooks 8 ,
+with a prefix of
+.Va _dhcp6 .
+.It Ic vendopt Ar code Ar type Ar variable
+Defines the Vendor-Identifying Vendor Options.
+The
+.Ar code
+is the IANA Enterprise Number which will unqiuely describe the encapsulated
+options.
+.Ar type
+is normally
+.Ar encap .
+.Ar variable
+names the Vendor option to be exported.
+.It Ic embed Ar type Ar variable
+Defines an embedded variable within the defined option.
+The length is determined by the
+.Ar type .
+If the
+.Ar variable
+is not the same as defined in the parent option,
+it is prefixed with the parent
+.Ar variable
+first with an underscore.
+.It Ic encap Ar code Ar type Ar variable
+Defines an encapsulated variable within the defined option.
+The length is determined by the
+.Ar type .
+If the
+.Ar variable
+is not the same as defined in the parent option,
+it is prefixed with the parent
+.Ar variable
+first with an underscore.
+.El
+.Ss Type prefix
+These keywords come before the type itself, to describe it more fully.
+You can use more than one, but they must appear in the order listed below.
+.Bl -tag -width -indent
+.It Ic request
+Requests the option by default without having to be specified in user
+configuration
+.It Ic norequest
+This option cannot be requested, regardless of user configuration
+.It Ic index
+The option can appear more than once and will be indexed.
+.It Ic array
+The option data is split into a space seperated array, each element being
+the same type.
+.El
+.Ss Types to define
+The type directly affects the length of data consumed inside the option.
+Any remaining data is normally discarded.
+Lengths can be specified for string and binhex types, but this is generally
+with other data embedded afterwards in the same option.
+.Bl -tag -width indent
+.It Ic ipaddress
+An IPv4 address, 4 bytes
+.It Ic ip6address
+An IPv6 address, 16 bytes
+.It Ic string Op : Ic length
+A shell escaped string (binary data escaped as octal)
+.It Ic byte
+A byte
+.It Ic int16
+A signed 16bit integer, 2 bytes
+.It Ic uint16
+An unsigned 16bit integer, 2 bytes
+.It Ic int32
+A signed 32bit integer, 4 bytes
+.It Ic uint32
+An unsigned 32bit integer, 4 bytes
+.It Ic flag
+A fixed value (1) to indicate that the option is present, 0 bytes
+.It Ic domain
+A RFC 3397 encoded string
+.It Ic binhex Op : Ic length
+Binary data expressed as hexadecimal
+.It Ic embed
+Contains embedded options (implies encap as well)
+.It Ic encap
+Contains encapsulated options (implies embed as well)
+.It Ic option
+References an option from the global definition
+.El
+.Ss Example definition
+.D1 # DHCP option 81, Fully Qualified Domain Name, RFC4702
+.D1 define 81 embed fqdn
+.D1 embed byte flags
+.D1 embed byte rcode1
+.D1 embed byte rcode2
+.D1 embed domain fqdn
+.Pp
+.D1 # DHCP option 125, Vendor Specific Information Option, RFC3925
+.D1 define 125 encap vsio
+.D1 embed uint32 enterprise_number
+.D1 # Options defined for the enterprise number
+.D1 encap 1 ipaddress ipaddress
+.Ss Supported protocols
+.Bl -tag -width -indent
+.It Ic token
+Sends and expects the token with the secretid 0 in each message.
+.It Ic delayedrealm
+Delayed Authentication.
+.Nm dhcpcd
+will send an authentication option with no key or MAC.
+The server will see this option, and select a key for
+.Nm , writing the
+.Ar realm
+and
+.Ar secretid
+in it.
+.Nm dhcpcd
+will then look for a non-expired token with a matching realm and secretid.
+This token is used to authenicate all other messages.
+.It Ic delayed
+Same as above, but without a realm.
+.El
+.Ss Supported algorithms
+If none specified,
+.Ic hmac-md5
+is the default.
+.Bl -tag -width -indent
+.It Ic hmac-md5
+.El
+.Ss Supported Replay Detection Mechanisms
+If none specified,
+.Ic monotonic
+is the default.
+.Bl -tag -width -indent
+.It Ic monotonic
+.El
+.Sh SEE ALSO
+.Xr fnmatch 3 ,
+.Xr if_nametoindex 3 ,
+.Xr dhcpcd 8 ,
+.Xr dhcpcd-run-hooks 8
+.Sh AUTHORS
+.An Roy Marples Aq Mt roy@marples.name
+.Sh BUGS
+When configuring DHCPv6 you can only select one IA type.
+I can't think of a use case where you would want different types,
+so if you have one then please bring it up for discussion on the
+.Aq Mt dhcpcd-discuss@marples.name
+mailing list.
+.Pp
+Please report them to
+.Lk http://roy.marples.name/projects/dhcpcd
diff --git a/dhcpcd/dhcpcd.h b/dhcpcd/dhcpcd.h
new file mode 100644
index 00000000..67fe6d1c
--- /dev/null
+++ b/dhcpcd/dhcpcd.h
@@ -0,0 +1,94 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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 DHCPCD_H
+#define DHCPCD_H
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include "control.h"
+#include "if-options.h"
+
+#define HWADDR_LEN 20
+#define IF_SSIDSIZE 33
+#define PROFILE_LEN 64
+
+#define LINK_UP 1
+#define LINK_UNKNOWN 0
+#define LINK_DOWN -1
+
+#define IF_DATA_IPV4 0
+#define IF_DATA_DHCP 1
+#define IF_DATA_IPV6 2
+#define IF_DATA_IPV6ND 3
+#define IF_DATA_DHCP6 4
+#define IF_DATA_MAX 5
+
+struct interface {
+ TAILQ_ENTRY(interface) next;
+ char name[IF_NAMESIZE];
+ unsigned int index;
+ int flags;
+ sa_family_t family;
+ unsigned char hwaddr[HWADDR_LEN];
+ size_t hwlen;
+ int metric;
+ int carrier;
+ int wireless;
+ char ssid[IF_SSIDSIZE];
+
+ char profile[PROFILE_LEN];
+ struct if_options *options;
+ void *if_data[IF_DATA_MAX];
+};
+extern TAILQ_HEAD(if_head, interface) *ifaces;
+
+extern char vendor[VENDORCLASSID_MAX_LEN];
+extern sigset_t dhcpcd_sigset;
+extern int pidfd;
+extern int ifac;
+extern char **ifav;
+extern int ifdc;
+extern char **ifdv;
+extern struct if_options *if_options;
+
+extern const int handle_sigs[];
+
+pid_t daemonise(void);
+struct interface *find_interface(const char *);
+int handle_args(struct fd_list *, int, char **);
+void handle_carrier(int, int, const char *);
+void handle_interface(int, const char *);
+void handle_hwaddr(const char *, const unsigned char *, size_t);
+void drop_interface(struct interface *, const char *);
+int select_profile(struct interface *, const char *);
+
+void start_interface(void *);
+
+#endif
diff --git a/dhcpcd/duid.c b/dhcpcd/duid.c
new file mode 100644
index 00000000..a119828f
--- /dev/null
+++ b/dhcpcd/duid.c
@@ -0,0 +1,162 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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 DUID_TIME_EPOCH 946684800
+#define DUID_LLT 1
+#define DUID_LL 3
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef ARPHRD_NETROM
+# define ARPHRD_NETROM 0
+#endif
+
+#include "common.h"
+#include "duid.h"
+#include "net.h"
+
+unsigned char *duid = NULL;
+size_t duid_len = 0;
+
+static size_t
+duid_make(unsigned char *d, const struct interface *ifp, uint16_t type)
+{
+ unsigned char *p;
+ uint16_t u16;
+ time_t t;
+ uint32_t u32;
+
+ p = d;
+ u16 = htons(type);
+ memcpy(p, &u16, 2);
+ p += 2;
+ u16 = htons(ifp->family);
+ memcpy(p, &u16, 2);
+ p += 2;
+ if (type == DUID_LLT) {
+ /* time returns seconds from jan 1 1970, but DUID-LLT is
+ * seconds from jan 1 2000 modulo 2^32 */
+ t = time(NULL) - DUID_TIME_EPOCH;
+ u32 = htonl(t & 0xffffffff);
+ memcpy(p, &u32, 4);
+ p += 4;
+ }
+ /* Finally, add the MAC address of the interface */
+ memcpy(p, ifp->hwaddr, ifp->hwlen);
+ p += ifp->hwlen;
+ return p - d;
+}
+
+static size_t
+duid_get(unsigned char *d, const struct interface *ifp)
+{
+ FILE *f;
+ int x = 0;
+ size_t len = 0;
+ char *line;
+ const struct interface *ifp2;
+
+ /* 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(d, line);
+ break;
+ }
+ len = 0;
+ }
+ fclose(f);
+ if (len)
+ return len;
+ } else {
+ if (errno != ENOENT)
+ syslog(LOG_ERR, "error reading DUID: %s: %m", DUID);
+ }
+
+ /* No file? OK, lets make one based on our interface */
+ if (ifp->family == ARPHRD_NETROM) {
+ syslog(LOG_WARNING, "%s: is a NET/ROM psuedo interface",
+ ifp->name);
+ TAILQ_FOREACH(ifp2, ifaces, next) {
+ if (ifp2->family != ARPHRD_NETROM)
+ break;
+ }
+ if (ifp2) {
+ ifp = ifp2;
+ syslog(LOG_WARNING,
+ "picked interface %s to generate a DUID",
+ ifp->name);
+ } else {
+ syslog(LOG_WARNING,
+ "no interfaces have a fixed hardware address");
+ return duid_make(d, ifp, DUID_LL);
+ }
+ }
+
+ if (!(f = fopen(DUID, "w"))) {
+ syslog(LOG_ERR, "error writing DUID: %s: %m", DUID);
+ return duid_make(d, ifp, DUID_LL);
+ }
+ len = duid_make(d, ifp, DUID_LLT);
+ x = fprintf(f, "%s\n", hwaddr_ntoa(d, len));
+ fclose(f);
+ /* Failed to write the duid? scrub it, we cannot use it */
+ if (x < 1) {
+ syslog(LOG_ERR, "error writing DUID: %s: %m", DUID);
+ unlink(DUID);
+ return duid_make(d, ifp, DUID_LL);
+ }
+ return len;
+}
+
+size_t duid_init(const struct interface *ifp)
+{
+
+ if (duid == NULL) {
+ duid = malloc(DUID_LEN);
+ if (duid == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return 0;
+ }
+ duid_len = duid_get(duid, ifp);
+ }
+ return duid_len;
+}
diff --git a/dhcpcd/duid.h b/dhcpcd/duid.h
new file mode 100644
index 00000000..b3ab00ee
--- /dev/null
+++ b/dhcpcd/duid.h
@@ -0,0 +1,38 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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"
+
+extern unsigned char *duid;
+extern size_t duid_len;
+
+size_t duid_init(const struct interface *);
+
+#endif
diff --git a/dhcpcd/eloop.c b/dhcpcd/eloop.c
new file mode 100644
index 00000000..0a7f69a6
--- /dev/null
+++ b/dhcpcd/eloop.c
@@ -0,0 +1,391 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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.
+ */
+
+/* Needed for ppoll(2) */
+#define _GNU_SOURCE
+
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include "common.h"
+#include "dhcpcd.h"
+#include "eloop.h"
+
+static struct timeval now;
+
+struct event {
+ TAILQ_ENTRY(event) next;
+ int fd;
+ void (*callback)(void *);
+ void *arg;
+ struct pollfd *pollfd;
+};
+static size_t events_len;
+static TAILQ_HEAD (event_head, event) events = TAILQ_HEAD_INITIALIZER(events);
+static struct event_head free_events = TAILQ_HEAD_INITIALIZER(free_events);
+
+struct timeout {
+ TAILQ_ENTRY(timeout) next;
+ struct timeval when;
+ void (*callback)(void *);
+ void *arg;
+ int queue;
+};
+static TAILQ_HEAD (timeout_head, timeout) timeouts
+ = TAILQ_HEAD_INITIALIZER(timeouts);
+static struct timeout_head free_timeouts
+ = TAILQ_HEAD_INITIALIZER(free_timeouts);
+
+static void (*volatile timeout0)(void *);
+static void *volatile timeout0_arg;
+
+static struct pollfd *fds;
+static size_t fds_len;
+
+static void
+eloop_event_setup_fds(void)
+{
+ struct event *e;
+ size_t i;
+
+ i = 0;
+ TAILQ_FOREACH(e, &events, next) {
+ fds[i].fd = e->fd;
+ fds[i].events = POLLIN;
+ fds[i].revents = 0;
+ e->pollfd = &fds[i];
+ i++;
+ }
+}
+
+int
+eloop_event_add(int fd, void (*callback)(void *), void *arg)
+{
+ struct event *e;
+
+ /* We should only have one callback monitoring the fd */
+ TAILQ_FOREACH(e, &events, next) {
+ if (e->fd == fd) {
+ e->callback = callback;
+ e->arg = arg;
+ return 0;
+ }
+ }
+
+ /* Allocate a new event if no free ones already allocated */
+ if ((e = TAILQ_FIRST(&free_events))) {
+ TAILQ_REMOVE(&free_events, e, next);
+ } else {
+ e = malloc(sizeof(*e));
+ if (e == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return -1;
+ }
+ }
+
+ /* Ensure we can actually listen to it */
+ events_len++;
+ if (events_len > fds_len) {
+ fds_len += 5;
+ free(fds);
+ fds = malloc(sizeof(*fds) * fds_len);
+ if (fds == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ free(e);
+ return -1;
+ }
+ }
+
+ /* Now populate the structure and add it to the list */
+ e->fd = fd;
+ e->callback = callback;
+ e->arg = arg;
+ /* The order of events should not matter.
+ * However, some PPP servers love to close the link right after
+ * sending their final message. So to ensure dhcpcd processes this
+ * message (which is likely to be that the DHCP addresses are wrong)
+ * we insert new events at the queue head as the link fd will be
+ * the first event added. */
+ TAILQ_INSERT_HEAD(&events, e, next);
+ eloop_event_setup_fds();
+ return 0;
+}
+
+void
+eloop_event_delete(int fd)
+{
+ struct event *e;
+
+ TAILQ_FOREACH(e, &events, next) {
+ if (e->fd == fd) {
+ TAILQ_REMOVE(&events, e, next);
+ TAILQ_INSERT_TAIL(&free_events, e, next);
+ events_len--;
+ eloop_event_setup_fds();
+ break;
+ }
+ }
+}
+
+int
+eloop_q_timeout_add_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 -1;
+ }
+
+ /* Remove existing timeout if present */
+ TAILQ_FOREACH(t, &timeouts, next) {
+ if (t->callback == callback && t->arg == arg) {
+ TAILQ_REMOVE(&timeouts, t, next);
+ break;
+ }
+ }
+
+ if (t == NULL) {
+ /* No existing, so allocate or grab one from the free pool */
+ if ((t = TAILQ_FIRST(&free_timeouts))) {
+ TAILQ_REMOVE(&free_timeouts, t, next);
+ } else {
+ t = malloc(sizeof(*t));
+ if (t == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return -1;
+ }
+ }
+ }
+
+ 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. */
+ TAILQ_FOREACH(tt, &timeouts, next) {
+ if (timercmp(&t->when, &tt->when, <)) {
+ TAILQ_INSERT_BEFORE(tt, t, next);
+ return 0;
+ }
+ }
+ TAILQ_INSERT_TAIL(&timeouts, t, next);
+ return 0;
+}
+
+int
+eloop_q_timeout_add_sec(int queue, time_t when,
+ void (*callback)(void *), void *arg)
+{
+ struct timeval tv;
+
+ tv.tv_sec = when;
+ tv.tv_usec = 0;
+ return eloop_q_timeout_add_tv(queue, &tv, callback, arg);
+}
+
+int
+eloop_timeout_add_now(void (*callback)(void *), void *arg)
+{
+
+ if (timeout0 != NULL) {
+ syslog(LOG_WARNING, "%s: timeout0 already set", __func__);
+ return eloop_q_timeout_add_sec(0, 0, callback, arg);
+ }
+
+ timeout0 = callback;
+ timeout0_arg = arg;
+ return 0;
+}
+
+/* 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
+eloop_q_timeouts_delete_v(int queue, void *arg,
+ void (*callback)(void *), va_list v)
+{
+ struct timeout *t, *tt;
+ va_list va;
+ void (*f)(void *);
+
+ TAILQ_FOREACH_SAFE(t, &timeouts, next, tt) {
+ if ((queue == 0 || 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 == NULL) {
+ TAILQ_REMOVE(&timeouts, t, next);
+ TAILQ_INSERT_TAIL(&free_timeouts, t, next);
+ }
+ }
+ }
+}
+
+void
+eloop_q_timeouts_delete(int queue, void *arg, void (*callback)(void *), ...)
+{
+ va_list va;
+
+ va_start(va, callback);
+ eloop_q_timeouts_delete_v(queue, arg, callback, va);
+ va_end(va);
+}
+
+void
+eloop_q_timeout_delete(int queue, void (*callback)(void *), void *arg)
+{
+ struct timeout *t, *tt;
+
+ TAILQ_FOREACH_SAFE(t, &timeouts, next, tt) {
+ if (t->queue == queue && t->arg == arg &&
+ (!callback || t->callback == callback))
+ {
+ TAILQ_REMOVE(&timeouts, t, next);
+ TAILQ_INSERT_TAIL(&free_timeouts, t, next);
+ }
+ }
+}
+
+#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
+eloop_cleanup(void)
+{
+ struct event *e;
+ struct timeout *t;
+
+ while ((e = TAILQ_FIRST(&events))) {
+ TAILQ_REMOVE(&events, e, next);
+ free(e);
+ }
+ while ((e = TAILQ_FIRST(&free_events))) {
+ TAILQ_REMOVE(&free_events, e, next);
+ free(e);
+ }
+ while ((t = TAILQ_FIRST(&timeouts))) {
+ TAILQ_REMOVE(&timeouts, t, next);
+ free(t);
+ }
+ while ((t = TAILQ_FIRST(&free_timeouts))) {
+ TAILQ_REMOVE(&free_timeouts, t, next);
+ free(t);
+ }
+ free(fds);
+}
+
+void
+eloop_init(void)
+{
+
+ atexit(eloop_cleanup);
+}
+#endif
+
+__dead void
+eloop_start(const sigset_t *sigmask)
+{
+ int n;
+ struct event *e;
+ struct timeout *t;
+ struct timeval tv;
+ struct timespec ts, *tsp;
+ void (*t0)(void *);
+
+ for (;;) {
+ /* Run all timeouts first */
+ if (timeout0) {
+ t0 = timeout0;
+ timeout0 = NULL;
+ t0(timeout0_arg);
+ continue;
+ }
+ if ((t = TAILQ_FIRST(&timeouts))) {
+ get_monotonic(&now);
+ if (timercmp(&now, &t->when, >)) {
+ TAILQ_REMOVE(&timeouts, t, next);
+ t->callback(t->arg);
+ TAILQ_INSERT_TAIL(&free_timeouts, t, next);
+ continue;
+ }
+ timersub(&t->when, &now, &tv);
+ TIMEVAL_TO_TIMESPEC(&tv, &ts);
+ tsp = &ts;
+ } else
+ /* No timeouts, so wait forever */
+ tsp = NULL;
+
+ if (tsp == NULL && events_len == 0) {
+ syslog(LOG_ERR, "nothing to do");
+ exit(EXIT_FAILURE);
+ }
+
+ n = pollts(fds, events_len, tsp, sigmask);
+ if (n == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "poll: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Process any triggered events. */
+ if (n > 0) {
+ TAILQ_FOREACH(e, &events, next) {
+ if (e->pollfd->revents & (POLLIN | POLLHUP)) {
+ e->callback(e->arg);
+ /* We need to break here as the
+ * callback could destroy the next
+ * fd to process. */
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/dhcpcd/eloop.h b/dhcpcd/eloop.h
new file mode 100644
index 00000000..01ed843d
--- /dev/null
+++ b/dhcpcd/eloop.h
@@ -0,0 +1,57 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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 1
+#endif
+
+#define eloop_timeout_add_tv(a, b, c) \
+ eloop_q_timeout_add_tv(ELOOP_QUEUE, a, b, c)
+#define eloop_timeout_add_sec(a, b, c) \
+ eloop_q_timeout_add_sec(ELOOP_QUEUE, a, b, c)
+#define eloop_timeout_delete(a, b) \
+ eloop_q_timeout_delete(ELOOP_QUEUE, a, b)
+#define eloop_timeouts_delete(a, ...) \
+ eloop_q_timeouts_delete(ELOOP_QUEUE, a, __VA_ARGS__)
+
+int eloop_event_add(int, void (*)(void *), void *);
+void eloop_event_delete(int);
+int eloop_q_timeout_add_sec(int queue, time_t, void (*)(void *), void *);
+int eloop_q_timeout_add_tv(int queue, const struct timeval *, void (*)(void *),
+ void *);
+int eloop_timeout_add_now(void (*)(void *), void *);
+void eloop_q_timeout_delete(int, void (*)(void *), void *);
+void eloop_q_timeouts_delete(int, void *, void (*)(void *), ...);
+void eloop_init(void);
+void eloop_start(const sigset_t *);
+
+#endif
diff --git a/dhcpcd/genembedc b/dhcpcd/genembedc
new file mode 100755
index 00000000..d16daab5
--- /dev/null
+++ b/dhcpcd/genembedc
@@ -0,0 +1,54 @@
+#!/bin/sh
+set -e
+
+: ${TOOL_SED:=sed}
+CONF=${1:-dhcpcd-definitions.conf}
+
+cat <<EOF
+/*
+ * DO NOT EDIT
+ * Automatically generated from dhcpcd-embedded.conf
+ * Ths allows us to simply generate DHCP structure without any C programming
+ */
+
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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>
+
+const char *dhcpcd_embedded_conf[] = {
+EOF
+
+$TOOL_SED \
+ -e 's/#.*$//' \
+ -e '/^$/d' \
+ -e 's/^/"/g' \
+ -e 's/$/\",/g' \
+ -e 's/ [ ]*/ /g' \
+ -e 's/ [ ]*/ /g' \
+ $CONF
+printf "%s\n%s\n" "NULL" "};"
diff --git a/dhcpcd/genembedh b/dhcpcd/genembedh
new file mode 100755
index 00000000..a155cfae
--- /dev/null
+++ b/dhcpcd/genembedh
@@ -0,0 +1,15 @@
+#!/bin/sh
+set -e
+
+: ${TOOL_SED:=sed}
+: ${TOOL_GREP:=grep}
+: ${TOOL_WC:=wc}
+CONF=${1:-dhcpcd-definitions.conf}
+H=${2:-dhcpcd-embedded.h.in}
+
+INITDEFINES=$($TOOL_GREP "^define " $CONF | $TOOL_WC -l)
+INITDEFINE6S=$($TOOL_GREP "^define6 " $CONF | $TOOL_WC -l)
+$TOOL_SED \
+ -e "s/@INITDEFINES@/$INITDEFINES/" \
+ -e "s/@INITDEFINE6S@/$INITDEFINE6S/" \
+ $H
diff --git a/dhcpcd/if-bsd.c b/dhcpcd/if-bsd.c
new file mode 100644
index 00000000..3d44d43f
--- /dev/null
+++ b/dhcpcd/if-bsd.c
@@ -0,0 +1,722 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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/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>
+#ifdef __FreeBSD__ /* Needed so that including netinet6/in6_var.h works */
+# include <net/if_var.h>
+#endif
+#include <net/if_media.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet6/in6_var.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 "dhcp.h"
+#include "if-options.h"
+#include "ipv4.h"
+#include "ipv6.h"
+
+#ifndef RT_ROUNDUP
+#define RT_ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))
+#endif
+
+#define COPYOUT(sin, sa) \
+ sin.s_addr = ((sa) != NULL) ? \
+ (((struct sockaddr_in *)(void *)sa)->sin_addr).s_addr : 0
+
+#define COPYOUT6(sin, sa) \
+ sin.s6_addr = ((sa) != NULL) ? \
+ (((struct sockaddr_in6 *)(void *)sa)->sin6_addr).s6_addr : 0
+
+#ifndef CLLADDR
+# define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
+#endif
+
+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;
+}
+
+#ifdef DEBUG_MEMORY
+static void
+cleanup(void)
+{
+
+ free(link_buf);
+}
+#endif
+
+int
+open_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
+open_link_socket(void)
+{
+ int fd;
+
+#ifdef DEBUG_MEMORY
+ if (link_buf == NULL)
+ atexit(cleanup);
+#endif
+
+ fd = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (fd != -1) {
+ set_cloexec(fd);
+ set_nonblock(fd);
+ }
+ return fd;
+}
+
+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;
+ if (ssid) {
+ 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;
+ memset(nwid, 0, sizeof(nwid));
+ ireq.i_data = &nwid;
+ if (ioctl(socket_afnet, SIOCG80211, &ireq) == 0) {
+ retval = ireq.i_len;
+ if (ssid) {
+ memcpy(ssid, nwid, ireq.i_len);
+ ssid[ireq.i_len] = '\0';
+ }
+ }
+#endif
+ return retval;
+}
+
+/*
+ * FreeBSD allows for Virtual Access Points
+ * We need to check if the interface is a Virtual Interface Master
+ * and if so, don't use it.
+ * This check is made by virtue of being a IEEE80211 device but
+ * returning the SSID gives an error.
+ */
+int
+if_vimaster(const char *ifname)
+{
+ struct ifmediareq ifmr;
+
+ memset(&ifmr, 0, sizeof(ifmr));
+ strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+ if (ioctl(socket_afnet, SIOCGIFMEDIA, &ifmr) == -1)
+ return -1;
+ if (ifmr.ifm_status & IFM_AVALID &&
+ IFM_TYPE(ifmr.ifm_active) == IFM_IEEE80211)
+ {
+ if (getifssid(ifname, NULL) == -1)
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef INET
+int
+if_address(const struct interface *iface, const struct in_addr *address,
+ const struct in_addr *netmask, const struct in_addr *broadcast,
+ int action)
+{
+ struct ifaliasreq ifa;
+ union {
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ } _s;
+
+ memset(&ifa, 0, sizeof(ifa));
+ 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)); \
+ }
+
+ ADDADDR(ifa.ifra_addr, address);
+ ADDADDR(ifa.ifra_mask, netmask);
+ if (action >= 0 && broadcast) {
+ ADDADDR(ifa.ifra_broadaddr, broadcast);
+ }
+#undef ADDADDR
+
+ return ioctl(socket_afnet,
+ action < 0 ? SIOCDIFADDR :
+ action == 2 ? SIOCSIFADDR : SIOCAIFADDR, &ifa);
+}
+
+int
+if_route(const struct rt *rt, int action)
+{
+ const struct dhcp_state *state;
+ union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_dl sdl;
+ struct sockaddr_storage ss;
+ } su;
+ struct rtm
+ {
+ struct rt_msghdr hdr;
+ char buffer[sizeof(su) * 4];
+ } rtm;
+ char *bp = rtm.buffer;
+ size_t l;
+ int retval = 0;
+
+#define ADDSU { \
+ l = RT_ROUNDUP(su.sa.sa_len); \
+ 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); \
+ (&su.sin)->sin_addr = *addr; \
+ ADDSU; \
+ }
+
+ state = D_CSTATE(rt->iface);
+ memset(&rtm, 0, sizeof(rtm));
+ rtm.hdr.rtm_version = RTM_VERSION;
+ rtm.hdr.rtm_seq = 1;
+ if (action == 0)
+ rtm.hdr.rtm_type = RTM_CHANGE;
+ else if (action > 0)
+ rtm.hdr.rtm_type = RTM_ADD;
+ else
+ rtm.hdr.rtm_type = RTM_DELETE;
+ rtm.hdr.rtm_flags = RTF_UP;
+ /* None interface subnet routes are static. */
+ if (rt->gate.s_addr != INADDR_ANY ||
+ rt->net.s_addr != state->net.s_addr ||
+ rt->dest.s_addr != (state->addr.s_addr & state->net.s_addr))
+ rtm.hdr.rtm_flags |= RTF_STATIC;
+ rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
+ if (rt->dest.s_addr == rt->gate.s_addr &&
+ rt->net.s_addr == INADDR_BROADCAST)
+ rtm.hdr.rtm_flags |= RTF_HOST;
+ else if (rt->gate.s_addr == htonl(INADDR_LOOPBACK) &&
+ rt->net.s_addr == INADDR_BROADCAST)
+ rtm.hdr.rtm_flags |= RTF_HOST | RTF_GATEWAY;
+ else {
+ rtm.hdr.rtm_addrs |= RTA_NETMASK;
+ if (rtm.hdr.rtm_flags & RTF_STATIC)
+ rtm.hdr.rtm_flags |= RTF_GATEWAY;
+ if (action >= 0)
+ rtm.hdr.rtm_addrs |= RTA_IFA;
+ }
+
+ ADDADDR(&rt->dest);
+ if ((rtm.hdr.rtm_flags & RTF_HOST &&
+ rt->gate.s_addr != htonl(INADDR_LOOPBACK)) ||
+ !(rtm.hdr.rtm_flags & RTF_STATIC))
+ {
+ /* Make us a link layer socket for the host gateway */
+ memset(&su, 0, sizeof(su));
+ su.sdl.sdl_len = sizeof(struct sockaddr_dl);
+ link_addr(rt->iface->name, &su.sdl);
+ ADDSU;
+ } else
+ ADDADDR(&rt->gate);
+
+ if (rtm.hdr.rtm_addrs & RTA_NETMASK)
+ ADDADDR(&rt->net);
+
+ if (rtm.hdr.rtm_addrs & RTA_IFP) {
+ /* Make us a link layer socket for the host gateway */
+ memset(&su, 0, sizeof(su));
+ su.sdl.sdl_len = sizeof(struct sockaddr_dl);
+ link_addr(rt->iface->name, &su.sdl);
+ ADDSU;
+ }
+
+ if (rtm.hdr.rtm_addrs & RTA_IFA)
+ ADDADDR(&state->addr);
+
+#undef ADDADDR
+#undef ADDSU
+
+ rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
+ if (write(r_fd, &rtm, l) == -1)
+ retval = -1;
+ return retval;
+}
+#endif
+
+#ifdef INET6
+int
+if_address6(const struct ipv6_addr *a, int action)
+{
+ int s, r;
+ struct in6_aliasreq ifa;
+ struct in6_addr mask;
+
+ s = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (s == -1)
+ return -1;
+ memset(&ifa, 0, sizeof(ifa));
+ strlcpy(ifa.ifra_name, a->iface->name, sizeof(ifa.ifra_name));
+ /*
+ * We should not set IN6_IFF_TENTATIVE as the kernel should be
+ * able to work out if it's a new address or not.
+ *
+ * We should set IN6_IFF_AUTOCONF, but the kernel won't let us.
+ * This is probably a safety measure, but still it's not entirely right
+ * either.
+ */
+#if 0
+ if (a->autoconf)
+ ifa.ifra_flags |= IN6_IFF_AUTOCONF;
+#endif
+
+#define ADDADDR(v, addr) { \
+ (v)->sin6_family = AF_INET6; \
+ (v)->sin6_len = sizeof(*v); \
+ (v)->sin6_addr = *addr; \
+ }
+
+ ADDADDR(&ifa.ifra_addr, &a->addr);
+ ipv6_mask(&mask, a->prefix_len);
+ ADDADDR(&ifa.ifra_prefixmask, &mask);
+ ifa.ifra_lifetime.ia6t_vltime = a->prefix_vltime;
+ ifa.ifra_lifetime.ia6t_pltime = a->prefix_pltime;
+#undef ADDADDR
+
+ r = ioctl(s, action < 0 ? SIOCDIFADDR_IN6 : SIOCAIFADDR_IN6, &ifa);
+ close(s);
+ return r;
+}
+
+int
+if_route6(const struct rt6 *rt, int action)
+{
+ union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_in6 sin;
+ struct sockaddr_dl sdl;
+ struct sockaddr_storage ss;
+ } su;
+ struct rtm
+ {
+ struct rt_msghdr hdr;
+ char buffer[sizeof(su) * 4];
+ } rtm;
+ char *bp = rtm.buffer;
+ size_t l;
+ int retval = 0;
+ const struct ipv6_addr_l *lla;
+
+/* KAME based systems want to store the scope inside the sin6_addr
+ * for link local addreses */
+#ifdef __KAME__
+#define SCOPE { \
+ if (IN6_IS_ADDR_LINKLOCAL(&su.sin.sin6_addr)) { \
+ uint16_t scope = htons(su.sin.sin6_scope_id); \
+ memcpy(&su.sin.sin6_addr.s6_addr[2], &scope, \
+ sizeof(scope)); \
+ su.sin.sin6_scope_id = 0; \
+ } \
+ }
+#else
+#define SCOPE
+#endif
+
+#define ADDSU { \
+ l = RT_ROUNDUP(su.sa.sa_len); \
+ memcpy(bp, &su, l); \
+ bp += l; \
+ }
+#define ADDADDRS(addr, scope) { \
+ memset(&su, 0, sizeof(su)); \
+ su.sin.sin6_family = AF_INET6; \
+ su.sin.sin6_len = sizeof(su.sin); \
+ (&su.sin)->sin6_addr = *addr; \
+ su.sin.sin6_scope_id = scope; \
+ SCOPE; \
+ ADDSU; \
+ }
+#define ADDADDR(addr) ADDADDRS(addr, 0)
+
+ memset(&rtm, 0, sizeof(rtm));
+ rtm.hdr.rtm_version = RTM_VERSION;
+ rtm.hdr.rtm_seq = 1;
+ if (action == 0)
+ rtm.hdr.rtm_type = RTM_CHANGE;
+ else if (action > 0)
+ rtm.hdr.rtm_type = RTM_ADD;
+ else
+ rtm.hdr.rtm_type = RTM_DELETE;
+
+ rtm.hdr.rtm_flags = RTF_UP;
+ /* None interface subnet routes are static. */
+ if (IN6_IS_ADDR_UNSPECIFIED(&rt->dest) &&
+ IN6_IS_ADDR_UNSPECIFIED(&rt->net))
+ rtm.hdr.rtm_flags |= RTF_GATEWAY;
+#ifdef RTF_CLONING
+ else
+ rtm.hdr.rtm_flags |= RTF_CLONING;
+#endif
+
+ rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ if (action >= 0)
+ rtm.hdr.rtm_addrs |= RTA_IFP | RTA_IFA;
+
+ ADDADDR(&rt->dest);
+ if (!(rtm.hdr.rtm_flags & RTF_GATEWAY)) {
+ lla = ipv6_linklocal(rt->iface);
+ if (lla == NULL) /* unlikely as we need a LL to get here */
+ return -1;
+ ADDADDRS(&lla->addr, rt->iface->index);
+ } else {
+ lla = NULL;
+ ADDADDRS(&rt->gate, rt->iface->index);
+ }
+
+ if (rtm.hdr.rtm_addrs & RTA_NETMASK) {
+ if (rtm.hdr.rtm_flags & RTF_GATEWAY) {
+ memset(&su, 0, sizeof(su));
+ su.sin.sin6_family = AF_INET6;
+ ADDSU;
+ } else
+ ADDADDR(&rt->net);
+ }
+
+ if (rtm.hdr.rtm_addrs & RTA_IFP) {
+ /* Make us a link layer socket for the host gateway */
+ memset(&su, 0, sizeof(su));
+ su.sdl.sdl_len = sizeof(struct sockaddr_dl);
+ link_addr(rt->iface->name, &su.sdl);
+ ADDSU;
+ }
+
+ if (rtm.hdr.rtm_addrs & RTA_IFA) {
+ if (lla == NULL) {
+ lla = ipv6_linklocal(rt->iface);
+ if (lla == NULL) /* unlikely */
+ return -1;
+ }
+ ADDADDRS(&lla->addr, rt->iface->index);
+ }
+
+#undef ADDADDR
+#undef ADDSU
+#undef SCOPE
+
+ if (action >= 0 && rt->mtu) {
+ rtm.hdr.rtm_inits |= RTV_MTU;
+ rtm.hdr.rtm_rmx.rmx_mtu = rt->mtu;
+ }
+
+ rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
+ if (write(r_fd, &rtm, l) == -1)
+ retval = -1;
+ return retval;
+}
+#endif
+
+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
+ RT_ADVANCE(cp, sa[i]);
+ } else
+ sa[i] = NULL;
+ }
+}
+
+#ifdef INET6
+int
+in6_addr_flags(const char *ifname, const struct in6_addr *addr)
+{
+ int s, flags;
+ struct in6_ifreq ifr6;
+
+ s = socket(AF_INET6, SOCK_DGRAM, 0);
+ flags = -1;
+ if (s != -1) {
+ memset(&ifr6, 0, sizeof(ifr6));
+ strncpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
+ ifr6.ifr_addr.sin6_family = AF_INET6;
+ ifr6.ifr_addr.sin6_addr = *addr;
+ if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) != -1)
+ flags = ifr6.ifr_ifru.ifru_flags6;
+ close(s);
+ }
+ return flags;
+}
+#endif
+
+int
+manage_link(int fd)
+{
+ char *p, *e, *cp;
+ char ifname[IF_NAMESIZE];
+ ssize_t bytes;
+ struct rt_msghdr *rtm;
+ struct if_announcemsghdr *ifan;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ int len;
+ struct sockaddr_dl sdl;
+#ifdef INET
+ struct rt rt;
+#endif
+#if defined(INET6) && !defined(LISTEN_DAD)
+ struct in6_addr ia6;
+ struct sockaddr_in6 *sin6;
+ int ifa_flags;
+#endif
+
+ for (;;) {
+ 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;
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+ e = link_buf + bytes;
+ for (p = link_buf; p < e; p += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)(void *)p;
+ // Ignore messages generated by us
+ if (rtm->rtm_pid == getpid())
+ break;
+ 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)))
+ break;
+ switch (ifm->ifm_data.ifi_link_state) {
+ case LINK_STATE_DOWN:
+ len = LINK_DOWN;
+ break;
+ case LINK_STATE_UP:
+ len = LINK_UP;
+ break;
+ default:
+ /* handle_carrier will re-load
+ * the interface flags and check for
+ * IFF_RUNNING as some drivers that
+ * don't handle link state also don't
+ * set IFF_RUNNING when this routing
+ * message is generated.
+ * As such, it is a race ...*/
+ len = LINK_UNKNOWN;
+ break;
+ }
+ handle_carrier(len, ifm->ifm_flags, ifname);
+ break;
+ case RTM_DELETE:
+ if (~rtm->rtm_addrs &
+ (RTA_DST | RTA_GATEWAY | RTA_NETMASK))
+ break;
+ cp = (char *)(void *)(rtm + 1);
+ sa = (struct sockaddr *)(void *)cp;
+ if (sa->sa_family != AF_INET)
+ break;
+#ifdef INET
+ get_addrs(rtm->rtm_addrs, cp, rti_info);
+ memset(&rt, 0, sizeof(rt));
+ rt.iface = NULL;
+ COPYOUT(rt.dest, rti_info[RTAX_DST]);
+ COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
+ COPYOUT(rt.gate, rti_info[RTAX_GATEWAY]);
+ ipv4_routedeleted(&rt);
+#endif
+ 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) {
+ case AF_LINK:
+#ifdef RTM_CHGADDR
+ if (rtm->rtm_type != RTM_CHGADDR)
+ break;
+#else
+ if (rtm->rtm_type != RTM_NEWADDR)
+ break;
+#endif
+ memcpy(&sdl, rti_info[RTAX_IFA],
+ rti_info[RTAX_IFA]->sa_len);
+ handle_hwaddr(ifname,
+ (const unsigned char*)CLLADDR(&sdl),
+ sdl.sdl_alen);
+ break;
+#ifdef INET
+ 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]);
+ ipv4_handleifa(rtm->rtm_type,
+ NULL, ifname,
+ &rt.dest, &rt.net, &rt.gate);
+ break;
+#endif
+#if defined(INET6) && !defined(LISTEN_DAD)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6*)(void *)
+ rti_info[RTAX_IFA];
+ memcpy(ia6.s6_addr,
+ sin6->sin6_addr.s6_addr,
+ sizeof(ia6.s6_addr));
+ if (rtm->rtm_type == RTM_NEWADDR) {
+ ifa_flags = in6_addr_flags(
+ ifname,
+ &ia6);
+ if (ifa_flags == -1)
+ break;
+ } else
+ ifa_flags = 0;
+ ipv6_handleifa(rtm->rtm_type, NULL,
+ ifname, &ia6, ifa_flags);
+ break;
+#endif
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/dhcpcd/if-linux-wireless.c b/dhcpcd/if-linux-wireless.c
new file mode 100644
index 00000000..2d9519e2
--- /dev/null
+++ b/dhcpcd/if-linux-wireless.c
@@ -0,0 +1,91 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2009-2013 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/types.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"
+#include "config.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/dhcpcd/if-linux.c b/dhcpcd/if-linux.c
new file mode 100644
index 00000000..b737ac10
--- /dev/null
+++ b/dhcpcd/if-linux.c
@@ -0,0 +1,882 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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 <asm/types.h> /* Needed for 2.4 kernels */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+/* Support older kernels */
+#ifndef IFLA_WIRELESS
+# define IFLA_WIRELESS (IFLA_MASTER + 1)
+#endif
+
+/* For some reason, glibc doesn't include newer flags from linux/if.h
+ * However, we cannot include linux/if.h directly as it conflicts
+ * with the glibc version. D'oh! */
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
+#endif
+
+#include <errno.h>
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dev.h"
+#include "dhcp.h"
+#include "ipv4.h"
+#include "ipv6.h"
+#include "net.h"
+
+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
+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->options->options |= DHCPCD_BROADCAST;
+ fclose(fp);
+ return 0;
+}
+
+/* XXX work out Virtal Interface Masters */
+int
+if_vimaster(__unused const char *ifname)
+{
+
+ return 0;
+}
+
+static int
+_open_link_socket(struct sockaddr_nl *nl)
+{
+ int fd;
+
+ if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
+ return -1;
+ nl->nl_family = AF_NETLINK;
+ if (bind(fd, (struct sockaddr *)nl, sizeof(*nl)) == -1)
+ return -1;
+ set_cloexec(fd);
+
+ return fd;
+}
+
+int
+open_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;
+
+#ifdef INET
+ snl.nl_groups |= RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR;
+#endif
+#ifdef INET6
+ snl.nl_groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR;
+#endif
+
+ return _open_link_socket(&snl);
+}
+
+static int
+get_netlink(int fd, int flags,
+ int (*callback)(struct nlmsghdr *))
+{
+ char *buf = NULL, *nbuf;
+ ssize_t buflen = 0, bytes;
+ struct nlmsghdr *nlm;
+ struct sockaddr_nl nladdr;
+ socklen_t nladdr_len = sizeof(nladdr);
+ int r = -1;
+
+ for (;;) {
+ bytes = recv(fd, NULL, 0,
+ flags | MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC);
+ if (bytes == -1) {
+ if (errno == EAGAIN) {
+ r = 0;
+ goto eexit;
+ }
+ 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;
+ }
+ 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 = recvfrom(fd, buf, buflen, flags,
+ (struct sockaddr *)&nladdr, &nladdr_len);
+ if (bytes == -1) {
+ if (errno == EAGAIN) {
+ r = 0;
+ goto eexit;
+ }
+ if (errno == EINTR)
+ continue;
+ goto eexit;
+ }
+
+ /* Check sender */
+ if (nladdr_len != sizeof(nladdr)) {
+ errno = EINVAL;
+ goto eexit;
+ }
+ /* Ignore message if it is not from kernel */
+ if (nladdr.nl_pid != 0)
+ continue;
+
+ for (nlm = (struct nlmsghdr *)(void *)buf;
+ NLMSG_OK(nlm, (size_t)bytes);
+ nlm = NLMSG_NEXT(nlm, bytes))
+ {
+ r = callback(nlm);
+ if (r != 0)
+ goto eexit;
+ }
+ }
+
+eexit:
+ free(buf);
+ return r;
+}
+
+static int
+err_netlink(struct nlmsghdr *nlm)
+{
+ struct nlmsgerr *err;
+ int l;
+
+ if (nlm->nlmsg_type != NLMSG_ERROR)
+ return 0;
+ l = nlm->nlmsg_len - sizeof(*nlm);
+ if ((size_t)l < sizeof(*err)) {
+ errno = EBADMSG;
+ return -1;
+ }
+ err = (struct nlmsgerr *)NLMSG_DATA(nlm);
+ if (err->error == 0)
+ return l;
+ errno = -err->error;
+ return -1;
+}
+
+static int
+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 *)(void *)((char *)rtm +NLMSG_ALIGN(sizeof(*rtm)));
+ len = NLMSG_PAYLOAD(nlm, sizeof(*rtm));
+ memset(&rt, 0, sizeof(rt));
+ rt.dest.s_addr = INADDR_ANY;
+ rt.net.s_addr = INADDR_ANY;
+ rt.gate.s_addr = INADDR_ANY;
+ 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) {
+#ifdef INET
+ inet_cidrtoaddr(rtm->rtm_dst_len, &rt.net);
+ ipv4_routedeleted(&rt);
+#endif
+ }
+ }
+ return 1;
+}
+
+static int
+link_addr(struct nlmsghdr *nlm)
+{
+ int len;
+ struct rtattr *rta;
+ struct ifaddrmsg *ifa;
+ char ifn[IF_NAMESIZE + 1];
+ struct interface *iface;
+#ifdef INET
+ struct in_addr addr, net, dest;
+#endif
+#ifdef INET6
+ struct in6_addr addr6;
+#endif
+
+ 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));
+ switch (ifa->ifa_family) {
+#ifdef INET
+ case AF_INET:
+ 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);
+ }
+ ipv4_handleifa(nlm->nlmsg_type, NULL, ifn, &addr, &net, &dest);
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ memset(&addr6, 0, sizeof(addr6));
+ while (RTA_OK(rta, len)) {
+ switch (rta->rta_type) {
+ case IFA_ADDRESS:
+ memcpy(&addr6.s6_addr, RTA_DATA(rta),
+ sizeof(addr6.s6_addr));
+ break;
+ }
+ rta = RTA_NEXT(rta, len);
+ }
+ ipv6_handleifa(nlm->nlmsg_type, NULL, ifn,
+ &addr6, ifa->ifa_flags);
+ break;
+#endif
+ }
+ return 1;
+}
+
+static short l2addr_len(unsigned short if_type)
+{
+
+ switch (if_type) {
+ case ARPHRD_ETHER: /* FALLTHROUGH */
+ case ARPHRD_IEEE802: /*FALLTHROUGH */
+ case ARPHRD_IEEE80211:
+ return 6;
+ case ARPHRD_IEEE1394:
+ return 8;
+ case ARPHRD_INFINIBAND:
+ return 20;
+ default:
+ return -1;
+ }
+}
+
+static int
+handle_rename(unsigned int ifindex, const char *ifname)
+{
+ struct interface *ifp;
+
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ if (ifp->index == ifindex && strcmp(ifp->name, ifname)) {
+ handle_interface(-1, ifp->name);
+ /* Let dev announce the interface for renaming */
+ if (!dev_listening())
+ handle_interface(1, ifname);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+link_netlink(struct nlmsghdr *nlm)
+{
+ int len;
+ struct rtattr *rta, *hwaddr;
+ struct ifinfomsg *ifi;
+ char ifn[IF_NAMESIZE + 1];
+ struct interface *ifp;
+
+ 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);
+ if ((size_t)len < sizeof(*ifi)) {
+ errno = EBADMSG;
+ return -1;
+ }
+ ifi = NLMSG_DATA(nlm);
+ if (ifi->ifi_flags & IFF_LOOPBACK)
+ return 1;
+ rta = (struct rtattr *)(void *)((char *)ifi +NLMSG_ALIGN(sizeof(*ifi)));
+ len = NLMSG_PAYLOAD(nlm, sizeof(*ifi));
+ *ifn = '\0';
+ hwaddr = NULL;
+
+ while (RTA_OK(rta, len)) {
+ switch (rta->rta_type) {
+ case IFLA_WIRELESS:
+ /* Ignore wireless messages */
+ if (nlm->nlmsg_type == RTM_NEWLINK &&
+ ifi->ifi_change == 0)
+ return 1;
+ break;
+ case IFLA_IFNAME:
+ strlcpy(ifn, RTA_DATA(rta), sizeof(ifn));
+ break;
+ case IFLA_ADDRESS:
+ hwaddr = rta;
+ break;
+ }
+ rta = RTA_NEXT(rta, len);
+ }
+
+ if (nlm->nlmsg_type == RTM_DELLINK) {
+ handle_interface(-1, ifn);
+ return 1;
+ }
+
+ /* Virtual interfaces may not get a valid hardware address
+ * at this point.
+ * To trigger a valid hardware address pickup we need to pretend
+ * that that don't exist until they have one. */
+ if (ifi->ifi_flags & IFF_MASTER && !hwaddr) {
+ handle_interface(-1, ifn);
+ return 1;
+ }
+
+ /* Check for interface name change */
+ if (handle_rename(ifi->ifi_index, ifn))
+ return 1;
+
+ /* Check for a new interface */
+ ifp = find_interface(ifn);
+ if (ifp == NULL) {
+ /* If are listening to a dev manager, let that announce
+ * the interface rather than the kernel. */
+ if (dev_listening() < 1)
+ handle_interface(1, ifn);
+ return 1;
+ }
+
+ /* Re-read hardware address and friends */
+ if (!(ifi->ifi_flags & IFF_UP) && hwaddr) {
+ len = l2addr_len(ifi->ifi_type);
+ if (hwaddr->rta_len == RTA_LENGTH(len))
+ handle_hwaddr(ifn, RTA_DATA(hwaddr), len);
+ }
+
+ handle_carrier(ifi->ifi_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN,
+ ifi->ifi_flags, ifn);
+ return 1;
+}
+
+int
+manage_link(int fd)
+{
+ return get_netlink(fd, MSG_DONTWAIT, &link_netlink);
+}
+
+static int
+send_netlink(struct nlmsghdr *hdr)
+{
+ int r;
+ struct iovec iov;
+ struct msghdr msg;
+ static unsigned int seq;
+
+ memset(&iov, 0, sizeof(iov));
+ iov.iov_base = hdr;
+ iov.iov_len = hdr->nlmsg_len;
+ memset(&msg, 0, sizeof(msg));
+ 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(sock_fd, &msg, 0) != -1)
+ r = get_netlink(sock_fd, 0, &err_netlink);
+ else
+ r = -1;
+ return r;
+}
+
+#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)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+
+ return 0;
+}
+
+static int
+add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data)
+{
+ int len = RTA_LENGTH(sizeof(data));
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, sizeof(data));
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+
+ return 0;
+}
+
+struct nlma
+{
+ struct nlmsghdr hdr;
+ struct ifaddrmsg ifa;
+ char buffer[64];
+};
+
+struct nlmr
+{
+ struct nlmsghdr hdr;
+ struct rtmsg rt;
+ char buffer[256];
+};
+
+#ifdef INET
+int
+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;
+
+ nlm = calloc(1, sizeof(*nlm));
+ if (nlm == NULL)
+ return -1;
+ nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ nlm->hdr.nlmsg_flags = NLM_F_REQUEST;
+ if (action >= 0) {
+ nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
+ nlm->hdr.nlmsg_type = RTM_NEWADDR;
+ } else
+ nlm->hdr.nlmsg_type = RTM_DELADDR;
+ nlm->ifa.ifa_index = iface->index;
+ nlm->ifa.ifa_family = AF_INET;
+ nlm->ifa.ifa_prefixlen = inet_ntocidr(*netmask);
+ /* This creates the aliased interface */
+ add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL,
+ 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 && broadcast)
+ add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST,
+ &broadcast->s_addr, sizeof(broadcast->s_addr));
+
+ if (send_netlink(&nlm->hdr) == -1)
+ retval = -1;
+ free(nlm);
+ return retval;
+}
+
+int
+if_route(const struct rt *rt, int action)
+{
+ struct nlmr *nlm;
+ int retval = 0;
+ struct dhcp_state *state;
+
+ nlm = calloc(1, sizeof(*nlm));
+ if (nlm == NULL)
+ return -1;
+ nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ nlm->hdr.nlmsg_type = RTM_NEWROUTE;
+ if (action == 0)
+ nlm->hdr.nlmsg_flags = NLM_F_REPLACE;
+ else if (action == 1)
+ nlm->hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
+ else
+ nlm->hdr.nlmsg_type = RTM_DELROUTE;
+ nlm->hdr.nlmsg_flags |= NLM_F_REQUEST;
+ nlm->rt.rtm_family = AF_INET;
+ nlm->rt.rtm_table = RT_TABLE_MAIN;
+
+ state = D_STATE(rt->iface);
+ if (action == -1 || action == -2)
+ nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
+ else {
+ nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
+ /* We only change route metrics for kernel routes */
+ if (rt->dest.s_addr ==
+ (state->addr.s_addr & state->net.s_addr) &&
+ rt->net.s_addr == state->net.s_addr)
+ nlm->rt.rtm_protocol = RTPROT_KERNEL;
+ else
+ nlm->rt.rtm_protocol = RTPROT_BOOT;
+ if (rt->gate.s_addr == INADDR_ANY ||
+ (rt->gate.s_addr == rt->dest.s_addr &&
+ rt->net.s_addr == INADDR_BROADCAST))
+ nlm->rt.rtm_scope = RT_SCOPE_LINK;
+ else
+ nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE;
+ nlm->rt.rtm_type = RTN_UNICAST;
+ }
+
+ nlm->rt.rtm_dst_len = inet_ntocidr(rt->net);
+ add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
+ &rt->dest.s_addr, sizeof(rt->dest.s_addr));
+ if (nlm->rt.rtm_protocol == RTPROT_KERNEL) {
+ add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_PREFSRC,
+ &state->addr.s_addr, sizeof(state->addr.s_addr));
+ }
+ /* If destination == gateway then don't add the gateway */
+ if (rt->dest.s_addr != rt->gate.s_addr ||
+ rt->net.s_addr != INADDR_BROADCAST)
+ add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
+ &rt->gate.s_addr, sizeof(rt->gate.s_addr));
+
+ if (rt->gate.s_addr != htonl(INADDR_LOOPBACK))
+ add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, rt->iface->index);
+ add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, rt->metric);
+
+ if (send_netlink(&nlm->hdr) == -1)
+ retval = -1;
+ free(nlm);
+ return retval;
+}
+#endif
+
+#ifdef INET6
+int
+if_address6(const struct ipv6_addr *ap, int action)
+{
+ struct nlma *nlm;
+ struct ifa_cacheinfo cinfo;
+ int retval = 0;
+
+ nlm = calloc(1, sizeof(*nlm));
+ if (nlm == NULL)
+ return -1;
+ nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ nlm->hdr.nlmsg_flags = NLM_F_REQUEST;
+ if (action >= 0) {
+ nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
+ nlm->hdr.nlmsg_type = RTM_NEWADDR;
+ } else
+ nlm->hdr.nlmsg_type = RTM_DELADDR;
+ nlm->ifa.ifa_index = ap->iface->index;
+ nlm->ifa.ifa_family = AF_INET6;
+ nlm->ifa.ifa_prefixlen = ap->prefix_len;
+ /* This creates the aliased interface */
+ add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL,
+ ap->iface->name, strlen(ap->iface->name) + 1);
+ add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL,
+ &ap->addr.s6_addr, sizeof(ap->addr.s6_addr));
+
+ if (action >= 0) {
+ memset(&cinfo, 0, sizeof(cinfo));
+ cinfo.ifa_prefered = ap->prefix_pltime;
+ cinfo.ifa_valid = ap->prefix_vltime;
+ add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_CACHEINFO,
+ &cinfo, sizeof(cinfo));
+ }
+
+ if (send_netlink(&nlm->hdr) == -1)
+ retval = -1;
+ free(nlm);
+ return retval;
+}
+
+static int
+rta_add_attr_32(struct rtattr *rta, unsigned int maxlen,
+ int type, uint32_t data)
+{
+ unsigned int len = RTA_LENGTH(sizeof(data));
+ struct rtattr *subrta;
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, sizeof(data));
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int
+if_route6(const struct rt6 *rt, int action)
+{
+ struct nlmr *nlm;
+ char metricsbuf[32];
+ struct rtattr *metrics = (void *)metricsbuf;
+ int retval = 0;
+
+ nlm = calloc(1, sizeof(*nlm));
+ if (nlm == NULL)
+ return -1;
+ nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ nlm->hdr.nlmsg_type = RTM_NEWROUTE;
+ nlm->hdr.nlmsg_flags = NLM_F_REQUEST;
+ if (action == 0)
+ nlm->hdr.nlmsg_flags |= NLM_F_REPLACE;
+ else if (action == 1)
+ nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
+ else
+ nlm->hdr.nlmsg_type = RTM_DELROUTE;
+ nlm->rt.rtm_family = AF_INET6;
+ nlm->rt.rtm_table = RT_TABLE_MAIN;
+
+ if (action == -1 || action == -2)
+ nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
+ else {
+ nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
+ /* None interface subnet routes are static. */
+ if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) {
+ nlm->rt.rtm_protocol = RTPROT_KERNEL;
+ nlm->rt.rtm_scope = RT_SCOPE_LINK;
+ } else
+ nlm->rt.rtm_protocol = RTPROT_BOOT;
+ nlm->rt.rtm_type = RTN_UNICAST;
+ }
+
+ nlm->rt.rtm_dst_len = ipv6_prefixlen(&rt->net);
+ add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
+ &rt->dest.s6_addr, sizeof(rt->dest.s6_addr));
+
+ /* If destination == gateway then don't add the gateway */
+ if (!IN6_IS_ADDR_UNSPECIFIED(&rt->gate) &&
+ !IN6_ARE_ADDR_EQUAL(&rt->dest, &rt->gate))
+ add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
+ &rt->gate.s6_addr, sizeof(rt->gate.s6_addr));
+
+ add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, rt->iface->index);
+ add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, rt->metric);
+
+ if (rt->mtu) {
+ metrics->rta_type = RTA_METRICS;
+ metrics->rta_len = RTA_LENGTH(0);
+ rta_add_attr_32(metrics, sizeof(metricsbuf), RTAX_MTU, rt->mtu);
+ add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_METRICS,
+ RTA_DATA(metrics), RTA_PAYLOAD(metrics));
+ }
+
+ if (send_netlink(&nlm->hdr) == -1)
+ retval = -1;
+ free(nlm);
+ return retval;
+ errno = ENOTSUP;
+ return -1;
+}
+
+int
+in6_addr_flags(const char *ifname, const struct in6_addr *addr)
+{
+ FILE *fp;
+ char *p, ifaddress[33], address[33], name[IF_NAMESIZE + 1];
+ unsigned int ifindex;
+ int prefix, scope, flags, i;
+
+ fp = fopen("/proc/net/if_inet6", "r");
+ if (fp == NULL)
+ return -1;
+
+ p = ifaddress;
+ for (i = 0; i < (int)sizeof(addr->s6_addr); i++) {
+ p += snprintf(p, 3, "%.2x", addr->s6_addr[i]);
+ }
+ *p = '\0';
+
+ while ((p = get_line(fp))) {
+ i = sscanf(p, "%32[a-f0-9] %x %x %x %x"
+ " %"TOSTRING(IF_NAMESIZE)"s\n",
+ address, &ifindex, &prefix, &scope, &flags, name);
+ if (i != 6 || strlen(address) != 32) {
+ fclose(fp);
+ errno = ENOTSUP;
+ return -1;
+ }
+ if (strcmp(ifname, name) == 0 &&
+ strcmp(ifaddress, address) == 0)
+ {
+ fclose(fp);
+ return flags;
+ }
+ }
+
+ fclose(fp);
+ errno = ESRCH;
+ return -1;
+}
+#endif
diff --git a/dhcpcd/if-options.c b/dhcpcd/if-options.c
new file mode 100644
index 00000000..c7930096
--- /dev/null
+++ b/dhcpcd/if-options.c
@@ -0,0 +1,2050 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 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/types.h>
+#include <sys/queue.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.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 "dhcp.h"
+#include "dhcp6.h"
+#include "dhcpcd-embedded.h"
+#include "if-options.h"
+#include "ipv4.h"
+#include "platform.h"
+
+unsigned long long options = 0;
+
+/* 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
+#define O_IPV6RS O_BASE + 4
+#define O_NOIPV6RS O_BASE + 5
+#define O_IPV6RA_FORK O_BASE + 6
+#define O_IPV6RA_OWN O_BASE + 7
+#define O_IPV6RA_OWN_D O_BASE + 8
+#define O_NOALIAS O_BASE + 9
+#define O_IA_NA O_BASE + 10
+#define O_IA_TA O_BASE + 11
+#define O_IA_PD O_BASE + 12
+#define O_HOSTNAME_SHORT O_BASE + 13
+#define O_DEV O_BASE + 14
+#define O_NODEV O_BASE + 15
+#define O_NOIPV4 O_BASE + 16
+#define O_NOIPV6 O_BASE + 17
+#define O_IAID O_BASE + 18
+#define O_DEFINE O_BASE + 19
+#define O_DEFINE6 O_BASE + 20
+#define O_EMBED O_BASE + 21
+#define O_ENCAP O_BASE + 22
+#define O_VENDOPT O_BASE + 23
+#define O_VENDCLASS O_BASE + 24
+#define O_AUTHPROTOCOL O_BASE + 25
+#define O_AUTHTOKEN O_BASE + 26
+#define O_AUTHNOTREQUIRED O_BASE + 27
+#define O_NODHCP O_BASE + 28
+#define O_NODHCP6 O_BASE + 29
+
+char *dev_load;
+
+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", optional_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},
+ {"ipv6rs", no_argument, NULL, O_IPV6RS},
+ {"noipv6rs", no_argument, NULL, O_NOIPV6RS},
+ {"ipv6ra_fork", no_argument, NULL, O_IPV6RA_FORK},
+ {"ipv6ra_own", no_argument, NULL, O_IPV6RA_OWN},
+ {"ipv6ra_own_default", no_argument, NULL, O_IPV6RA_OWN_D},
+ {"ipv4only", no_argument, NULL, '4'},
+ {"ipv6only", no_argument, NULL, '6'},
+ {"noipv4", no_argument, NULL, O_NOIPV4},
+ {"noipv6", no_argument, NULL, O_NOIPV6},
+ {"noalias", no_argument, NULL, O_NOALIAS},
+ {"iaid", required_argument, NULL, O_IAID},
+ {"ia_na", no_argument, NULL, O_IA_NA},
+ {"ia_ta", no_argument, NULL, O_IA_TA},
+ {"ia_pd", no_argument, NULL, O_IA_PD},
+ {"hostname_short", no_argument, NULL, O_HOSTNAME_SHORT},
+ {"dev", required_argument, NULL, O_DEV},
+ {"nodev", no_argument, NULL, O_NODEV},
+ {"define", required_argument, NULL, O_DEFINE},
+ {"define6", required_argument, NULL, O_DEFINE6},
+ {"embed", required_argument, NULL, O_EMBED},
+ {"encap", required_argument, NULL, O_ENCAP},
+ {"vendopt", required_argument, NULL, O_VENDOPT},
+ {"vendclass", required_argument, NULL, O_VENDCLASS},
+ {"authprotocol", required_argument, NULL, O_AUTHPROTOCOL},
+ {"authtoken", required_argument, NULL, O_AUTHTOKEN},
+ {"noauthrequired", no_argument, NULL, O_AUTHNOTREQUIRED},
+ {"nodhcp", no_argument, NULL, O_NODHCP},
+ {"nodhcp6", no_argument, NULL, O_NODHCP6},
+ {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)))
+ {
+ if (errno == 0)
+ errno = EINVAL;
+ 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, *n;
+
+ match = strdup(value);
+ if (match == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+ p = strchr(match, '=');
+ if (p)
+ *p++ = '\0';
+ l = strlen(match);
+
+ while (lst && lst[i]) {
+ if (match && strncmp(lst[i], match, l) == 0) {
+ if (uniq) {
+ n = strdup(value);
+ if (n == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+ free(lst[i]);
+ lst[i] = n;
+ } else {
+ /* Append a space and the value to it */
+ l = strlen(lst[i]);
+ lv = strlen(p);
+ n = realloc(lst[i], l + lv + 2);
+ if (n == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+ lst[i] = n;
+ lst[i][l] = ' ';
+ memcpy(lst[i] + l + 1, p, lv);
+ lst[i][l + lv + 1] = '\0';
+ }
+ free(match);
+ return lst[i];
+ }
+ i++;
+ }
+
+ n = strdup(value);
+ if (n == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+ newlist = realloc(lst, sizeof(char *) * (i + 2));
+ if (newlist == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+ newlist[i] = n;
+ 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) {
+ if (sbuf)
+ *sbuf++ = 0;
+ l++;
+ }
+ c[3] = '\0';
+ while (*str) {
+ if (++l > slen && sbuf) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ if (*str == '\\') {
+ str++;
+ switch(*str) {
+ case '\0':
+ break;
+ case 'b':
+ if (sbuf)
+ *sbuf++ = '\b';
+ str++;
+ break;
+ case 'n':
+ if (sbuf)
+ *sbuf++ = '\n';
+ str++;
+ break;
+ case 'r':
+ if (sbuf)
+ *sbuf++ = '\r';
+ str++;
+ break;
+ case 't':
+ if (sbuf)
+ *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' && sbuf) {
+ 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' && sbuf) {
+ i = strtol(c, NULL, 8);
+ if (i > 255)
+ i = 255;
+ *sbuf ++= i;
+ } else
+ l--;
+ break;
+ default:
+ if (sbuf)
+ *sbuf++ = *str;
+ str++;
+ break;
+ }
+ } else {
+ if (sbuf)
+ *sbuf++ = *str;
+ str++;
+ }
+ }
+ if (punt_last) {
+ if (sbuf)
+ *--sbuf = '\0';
+ l--;
+ }
+ return l;
+}
+
+static int
+parse_iaid1(uint8_t *iaid, const char *arg, size_t len, int n)
+{
+ unsigned long l;
+ size_t s;
+ uint32_t u32;
+ char *np;
+
+ errno = 0;
+ l = strtoul(arg, &np, 0);
+ if (l <= (unsigned long)UINT32_MAX && errno == 0 && *np == '\0') {
+ if (n)
+ u32 = htonl(l);
+ else
+ u32 = l;
+ memcpy(iaid, &u32, sizeof(u32));
+ return 0;
+ }
+
+ if ((s = parse_string((char *)iaid, len, arg)) < 1) {
+ syslog(LOG_ERR, "%s: invalid IAID", arg);
+ return -1;
+ }
+ if (s < 4)
+ iaid[3] = '\0';
+ if (s < 3)
+ iaid[2] = '\0';
+ if (s < 2)
+ iaid[1] = '\0';
+ return 0;
+}
+
+static int
+parse_iaid(uint8_t *iaid, const char *arg, size_t len)
+{
+
+ return parse_iaid1(iaid, arg, len, 1);
+}
+
+static int
+parse_uint32(uint32_t *i, const char *arg)
+{
+
+ return parse_iaid1((uint8_t *)i, arg, sizeof(uint32_t), 0);
+}
+
+static char **
+splitv(int *argc, char **argv, const char *arg)
+{
+ char **n, **v = argv;
+ char *o = strdup(arg), *p, *t, *nt;
+
+ if (o == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return v;
+ }
+ p = o;
+ while ((t = strsep(&p, ", "))) {
+ nt = strdup(t);
+ if (nt == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+ (*argc)++;
+ n = realloc(v, sizeof(char *) * ((*argc)));
+ if (n == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+ v = n;
+ v[(*argc) - 1] = nt;
+ }
+ free(o);
+ return v;
+}
+
+#ifdef INET
+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 = ipv4_getnetmask(addr->s_addr);
+ return 0;
+}
+#else
+static int
+parse_addr(__unused struct in_addr *addr, __unused struct in_addr *net,
+ __unused const char *arg)
+{
+
+ syslog(LOG_ERR, "No IPv4 support");
+ return -1;
+}
+#endif
+
+static const char *
+set_option_space(const char *arg, const struct dhcp_opt **d, size_t *dl,
+ struct if_options *ifo,
+ uint8_t *request[], uint8_t *require[], uint8_t *no[])
+{
+
+#ifdef INET6
+ if (strncmp(arg, "dhcp6_", strlen("dhcp6_")) == 0) {
+ *d = dhcp6_opts;
+ *dl = dhcp6_opts_len;
+ *request = ifo->requestmask6;
+ *require = ifo->requiremask6;
+ *no = ifo->nomask6;
+ return arg + strlen("dhcp6_");
+ }
+#endif
+
+#ifdef INET
+ *d = dhcp_opts;
+ *dl = dhcp_opts_len;
+#else
+ *d = NULL;
+ *dl = 0;
+#endif
+ *request = ifo->requestmask;
+ *require = ifo->requiremask;
+ *no = ifo->nomask;
+ return arg;
+}
+
+/* Pointer to last defined option */
+static struct dhcp_opt *ldop;
+static struct dhcp_opt *edop;
+
+void
+free_dhcp_opt_embenc(struct dhcp_opt *opt)
+{
+ size_t i;
+ struct dhcp_opt *o;
+
+ free(opt->var);
+
+ for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)
+ free_dhcp_opt_embenc(o);
+ free(opt->embopts);
+ opt->embopts_len = 0;
+ opt->embopts = NULL;
+
+ for (i = 0, o = opt->encopts; i