aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:29:22 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:29:22 -0800
commitf7c5421560640d23fc10803b9d59a9ff1d83e467 (patch)
treed6ae69d0d3f4a4d760a3254ec326bca4a8afacfe
parentd97c47cad830d00c9da685cc4ea157d6185f6c97 (diff)
downloadexternal_dhcpcd-f7c5421560640d23fc10803b9d59a9ff1d83e467.zip
external_dhcpcd-f7c5421560640d23fc10803b9d59a9ff1d83e467.tar.gz
external_dhcpcd-f7c5421560640d23fc10803b9d59a9ff1d83e467.tar.bz2
auto import from //depot/cupcake/@135843
-rw-r--r--Android.mk66
-rw-r--r--Makefile51
-rw-r--r--README67
-rw-r--r--android.conf6
-rw-r--r--bpf-filter.h101
-rw-r--r--bpf.c206
-rw-r--r--client.c1839
-rw-r--r--client.h35
-rw-r--r--common.c327
-rw-r--r--common.h87
-rw-r--r--config.h75
-rw-r--r--configure.c420
-rw-r--r--configure.h41
-rw-r--r--dhcp.c1251
-rw-r--r--dhcp.h178
-rw-r--r--dhcpcd-hooks/01-test7
-rw-r--r--dhcpcd-hooks/10-mtu5
-rw-r--r--dhcpcd-hooks/20-dns.conf32
-rw-r--r--dhcpcd-hooks/20-resolv.conf107
-rw-r--r--dhcpcd-hooks/29-lookup-hostname34
-rw-r--r--dhcpcd-hooks/30-hostname28
-rw-r--r--dhcpcd-hooks/50-dhcpcd-compat31
-rw-r--r--dhcpcd-hooks/50-ntp.conf82
-rw-r--r--dhcpcd-hooks/50-yp.conf49
-rw-r--r--dhcpcd-hooks/90-NetworkManager8
-rw-r--r--dhcpcd-hooks/95-configured26
-rw-r--r--dhcpcd-hooks/Makefile13
-rwxr-xr-xdhcpcd-run-hooks31
-rw-r--r--dhcpcd-run-hooks.8113
-rw-r--r--dhcpcd-run-hooks.8.in113
-rw-r--r--dhcpcd-run-hooks.in138
-rw-r--r--dhcpcd.8430
-rw-r--r--dhcpcd.8.in430
-rw-r--r--dhcpcd.c1038
-rw-r--r--dhcpcd.conf13
-rw-r--r--dhcpcd.conf.5155
-rw-r--r--dhcpcd.conf.5.in155
-rw-r--r--dhcpcd.h95
-rw-r--r--if-bsd.c241
-rw-r--r--if-linux.c379
-rw-r--r--logger.c89
-rw-r--r--logger.h43
-rw-r--r--lpf.c168
-rw-r--r--mk/cc.mk28
-rw-r--r--mk/depend.mk11
-rw-r--r--mk/dist.mk31
-rw-r--r--mk/files.mk9
-rw-r--r--mk/man.mk25
-rw-r--r--mk/os-BSD.mk5
-rw-r--r--mk/os-Darwin.mk5
-rw-r--r--mk/os-Linux.mk8
-rw-r--r--mk/os.mk7
-rw-r--r--mk/prog.mk68
-rw-r--r--mk/scripts.mk9
-rw-r--r--mk/sys.mk14
-rw-r--r--net.c707
-rw-r--r--net.h176
-rw-r--r--showlease.c349
-rw-r--r--signals.c125
-rw-r--r--signals.h37
60 files changed, 10417 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..5cac7ea
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,66 @@
+# Copyright 2006 The Android Open Source Project
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_PATH:= $(call my-dir)
+
+etc_dir := $(TARGET_OUT)/etc/dhcpcd
+hooks_dir := dhcpcd-hooks
+hooks_target := $(etc_dir)/$(hooks_dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := common.c dhcp.c dhcpcd.c logger.c net.c \
+ signals.c configure.c client.c if-linux.c lpf.c
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_SHARED_LIBRARIES := libc libcutils
+LOCAL_MODULE = dhcpcd
+LOCAL_MODULE_TAGS := user development
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := showlease.c
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_SHARED_LIBRARIES := libc
+LOCAL_MODULE = showlease
+LOCAL_MODULE_TAGS := user development
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dhcpcd.conf
+LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(etc_dir)
+LOCAL_SRC_FILES := android.conf
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dhcpcd-run-hooks
+LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(etc_dir)
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := 01-test
+LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(hooks_target)
+LOCAL_SRC_FILES := $(hooks_dir)/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := 20-dns.conf
+LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(hooks_target)
+LOCAL_SRC_FILES := $(hooks_dir)/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := 95-configured
+LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(hooks_target)
+LOCAL_SRC_FILES := $(hooks_dir)/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+endif
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a6b066b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,51 @@
+# Makefile based on BSD make.
+# Our mk stubs also work with GNU make.
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+PROG= dhcpcd
+SRCS= common.c dhcp.c dhcpcd.c logger.c net.c signals.c
+SRCS+= configure.c client.c
+SRCS+= ${SRC_IF} ${SRC_PF}
+
+LIBEXECDIR?= ${PREFIX}/system/etc/dhcpcd
+SCRIPT= ${LIBEXECDIR}/dhcpcd-run-hooks
+HOOKDIR= ${LIBEXECDIR}/dhcpcd-hooks
+
+BINDIR= ${PREFIX}/sbin
+DBDIR= /data/misc/dhcp
+SYSCONFDIR?= ${PREFIX}/system/etc/dhcpcd
+
+MAN= dhcpcd.conf.5 dhcpcd.8 dhcpcd-run-hooks.8
+CLEANFILES= dhcpcd.conf.5 dhcpcd.8 dhcpcd-run-hooks.8
+
+SCRIPTS= dhcpcd-run-hooks
+SCRIPTSDIR= ${LIBEXECDIR}
+CLEANFILES+= dhcpcd-run-hooks
+
+FILES= dhcpcd.conf
+FILESDIR= ${SYSCONFDIR}
+
+CPPFLAGS+= -DDBDIR=\"${DBDIR}\"
+CPPFLAGS+= -DSCRIPT=\"${SCRIPT}\"
+CPPFLAGS+= -DSYSCONFDIR=\"${SYSCONFDIR}\"
+LDADD+= ${LIBRT}
+
+SUBDIRS= dhcpcd-hooks
+
+.SUFFIXES: .in .sh.in
+
+SED_DBDIR= -e 's:@DBDIR@:${DBDIR}:g'
+SED_HOOKDIR= -e 's:@HOOKDIR@:${HOOKDIR}:g'
+SED_SCRIPT= -e 's:@SCRIPT@:${SCRIPT}:g'
+SED_SYS= -e 's:@SYSCONFDIR@:${SYSCONFDIR}:g'
+
+.in:
+ ${SED} ${SED_DBDIR} ${SED_HOOKDIR} ${SED_SCRIPT} ${SED_SYS} $< > $@
+
+.sh.in.sh:
+ ${SED} ${SED_HOOKDIR} ${SED_SCRIPT} ${SED_SYS} $< > $@
+
+MK= mk
+include ${MK}/sys.mk
+include ${MK}/os.mk
+include ${MK}/prog.mk
diff --git a/README b/README
new file mode 100644
index 0000000..50294fe
--- /dev/null
+++ b/README
@@ -0,0 +1,67 @@
+dhcpcd-4 - DHCP client daemon
+Copyright 2006-2008 Roy Marples <roy@marples.name>
+
+
+Installation
+------------
+Edit config.h to match your building requirements.
+Then just make; make install
+man dhcpcd for command line options
+
+
+Notes
+-----
+If you're cross compiling you may need to set the below knobs to avoid
+automatic tests.
+OS=BSD | Linux
+
+If you're building for an MMU-less system where fork() does not work, you
+should add -DTHERE_IS_NO_FORK to your CPPFLAGS.
+This also puts the --no-background flag on and stops the --background flag
+from working.
+
+You can change the default dir with these knobs.
+For example, to satisfy FHS compliance you would do this:-
+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.
+
+
+Hooks
+-----
+Not all the hooks in dhcpcd-hooks are installed by default.
+By default we install 01-test, 10-mtu, 20-resolv.conf and 30-hostname.
+To add more simply add them in the HOOKSCRIPTS variable.
+make HOOKSCRIPTS=50-ntp install
+
+
+Compatibility
+-------------
+If you require compatibility with dhcpcd-3 and older style variables,
+you can install 50-dhcpcd-compat into the directory $LIBEXECDIR/dhcpcd-hooks
+We don't install this by default.
+You should also add -DCMDLINE_COMPAT to your CPPFLAGS if you need to be fully
+commandline compatible with prior versions.
+
+dhcpcd-3 enabled DUID support by default - this has changed in dhcpcd-4.
+You can enable it via the --duid, -D command line option or by using the
+duid directive in dhcpcd.conf.
+If CMDLINE_COMPAT is defined the we renable DUID support by default IF
+the dhcpcd.duid file exits. This keeps the clients working as they were,
+which is good.
+
+dhcpcd-4 is NOT fully commandline compatible with dhcpcd-2 and older and
+changes the meaning of some options.
+
+
+ChangeLog
+---------
+We no longer supply a ChangeLog.
+However, you're more than welcome to read the git commit comments at
+http://git.marples.name/?p=dhcpcd.git;a=summary
diff --git a/android.conf b/android.conf
new file mode 100644
index 0000000..5aa418d
--- /dev/null
+++ b/android.conf
@@ -0,0 +1,6 @@
+# dhcpcd configuration for Android Wi-Fi interface
+# See dhcpcd.conf(5) for details.
+
+interface tiwlan0
+# dhcpcd-run-hooks uses these options.
+option subnet_mask, routers, domain_name_servers
diff --git a/bpf-filter.h b/bpf-filter.h
new file mode 100644
index 0000000..881f678
--- /dev/null
+++ b/bpf-filter.h
@@ -0,0 +1,101 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef BPF_ETHCOOK
+# define BPF_ETHCOOK 0
+#endif
+#ifndef BPF_WHOLEPACKET
+# define BPF_WHOLEPACKET ~0U
+#endif
+static const struct bpf_insn const 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 const 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/bpf.c b/bpf.c
new file mode 100644
index 0000000..96e53a1
--- /dev/null
+++ b/bpf.c
@@ -0,0 +1,206 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.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 <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "logger.h"
+#include "net.h"
+#include "bpf-filter.h"
+
+int
+open_socket(struct interface *iface, int protocol)
+{
+ int fd = -1;
+ int *fdp = NULL;
+ 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 = xmalloc(sizeof(char) * PATH_MAX);
+ 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;
+
+ if (ioctl(fd, BIOCVERSION, &pv) == -1)
+ goto eexit;
+ if (pv.bv_major != BPF_MAJOR_VERSION ||
+ pv.bv_minor < BPF_MINOR_VERSION) {
+ logger(LOG_ERR, "BPF version mismatch - recompile " PACKAGE);
+ goto eexit;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, iface->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 (iface->buffer_size != (size_t)buf_len) {
+ free(iface->buffer);
+ iface->buffer_size = buf_len;
+ iface->buffer = xmalloc(buf_len);
+ iface->buffer_len = iface->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;
+ fdp = &iface->arp_fd;
+ } else {
+ pf.bf_insns = UNCONST(dhcp_bpf_filter);
+ pf.bf_len = dhcp_bpf_filter_len;
+ fdp = &iface->raw_fd;
+ }
+ if (ioctl(fd, BIOCSETF, &pf) == -1)
+ goto eexit;
+ if (set_cloexec(fd) == -1)
+ goto eexit;
+ if (fdp) {
+ if (*fdp != -1)
+ close(*fdp);
+ *fdp = fd;
+ }
+ return fd;
+
+eexit:
+ free(iface->buffer);
+ iface->buffer = NULL;
+ close(fd);
+ return -1;
+}
+
+ssize_t
+send_raw_packet(const struct interface *iface, int protocol,
+ const void *data, ssize_t len)
+{
+ struct iovec iov[2];
+ struct ether_header hw;
+ int fd;
+
+ 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;
+ if (protocol == ETHERTYPE_ARP)
+ fd = iface->arp_fd;
+ else
+ fd = iface->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
+get_raw_packet(struct interface *iface, int protocol,
+ void *data, ssize_t len)
+{
+ int fd = -1;
+ struct bpf_hdr packet;
+ ssize_t bytes;
+ const unsigned char *payload;
+
+ if (protocol == ETHERTYPE_ARP)
+ fd = iface->arp_fd;
+ else
+ fd = iface->raw_fd;
+
+ for (;;) {
+ if (iface->buffer_len == 0) {
+ bytes = read(fd, iface->buffer, iface->buffer_size);
+ if (bytes == -1)
+ return errno == EAGAIN ? 0 : -1;
+ else if ((size_t)bytes < sizeof(packet))
+ return -1;
+ iface->buffer_len = bytes;
+ iface->buffer_pos = 0;
+ }
+ bytes = -1;
+ memcpy(&packet, iface->buffer + iface->buffer_pos,
+ sizeof(packet));
+ if (packet.bh_caplen != packet.bh_datalen)
+ goto next; /* Incomplete packet, drop. */
+ if (iface->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
+ iface->buffer_len)
+ goto next; /* Packet beyond buffer, drop. */
+ payload = iface->buffer + packet.bh_hdrlen + ETHER_HDR_LEN;
+ bytes = packet.bh_caplen - ETHER_HDR_LEN;
+ if (bytes > len)
+ bytes = len;
+ memcpy(data, payload, bytes);
+next:
+ iface->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
+ packet.bh_caplen);
+ if (iface->buffer_pos >= iface->buffer_len)
+ iface->buffer_len = iface->buffer_pos = 0;
+ if (bytes != -1)
+ return bytes;
+ }
+}
diff --git a/client.c b/client.c
new file mode 100644
index 0000000..c34d318
--- /dev/null
+++ b/client.c
@@ -0,0 +1,1839 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#ifdef __linux__
+# include <netinet/ether.h>
+#endif
+
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "client.h"
+#include "configure.h"
+#include "dhcp.h"
+#include "dhcpcd.h"
+#include "net.h"
+#include "logger.h"
+#include "signals.h"
+
+#define IPV4LL_LEASETIME 2
+
+/* Some platforms don't define INFTIM */
+#ifndef INFTIM
+# define INFTIM -1
+#endif
+
+#define STATE_INIT 0
+#define STATE_DISCOVERING 1
+#define STATE_REQUESTING 2
+#define STATE_BOUND 3
+#define STATE_RENEWING 4
+#define STATE_REBINDING 5
+#define STATE_REBOOT 6
+#define STATE_RENEW_REQUESTED 7
+#define STATE_INIT_IPV4LL 8
+#define STATE_PROBING 9
+#define STATE_ANNOUNCING 10
+
+/* Constants taken from RFC 2131. */
+#define T1 0.5
+#define T2 0.875
+#define DHCP_BASE 4
+#define DHCP_MAX 64
+#define DHCP_RAND_MIN -1
+#define DHCP_RAND_MAX 1
+#define DHCP_ARP_FAIL 10
+
+/* We should define a maximum for the NAK exponential backoff */
+#define NAKOFF_MAX 60
+
+#define SOCKET_CLOSED 0
+#define SOCKET_OPEN 1
+
+/* These are for IPV4LL, RFC 3927. */
+#define PROBE_WAIT 1
+#define PROBE_NUM 3
+#define PROBE_MIN 1
+#define PROBE_MAX 2
+#define ANNOUNCE_WAIT 2
+/* BSD systems always do a grauitous ARP when assigning an address,
+ * so we can do one less announce. */
+#ifdef BSD
+# define ANNOUNCE_NUM 1
+#else
+# define ANNOUNCE_NUM 2
+#endif
+#define ANNOUNCE_INTERVAL 2
+#define MAX_CONFLICTS 10
+#define RATE_LIMIT_INTERVAL 60
+#define DEFEND_INTERVAL 10
+
+
+/* number of usecs in a second. */
+#define USECS_SECOND 1000000
+/* As we use timevals, we should use the usec part for
+ * greater randomisation. */
+#define DHCP_RAND_MIN_U DHCP_RAND_MIN * USECS_SECOND
+#define DHCP_RAND_MAX_U DHCP_RAND_MAX * USECS_SECOND
+#define PROBE_MIN_U PROBE_MIN * USECS_SECOND
+#define PROBE_MAX_U PROBE_MAX * USECS_SECOND
+
+#define timernorm(tvp) \
+ do { \
+ while ((tvp)->tv_usec >= 1000000) { \
+ (tvp)->tv_sec++; \
+ (tvp)->tv_usec -= 1000000; \
+ } \
+ } while (0 /* CONSTCOND */);
+
+#define timerneg(tvp) ((tvp)->tv_sec < 0 || (tvp)->tv_usec < 0)
+
+struct if_state {
+ int options;
+ struct interface *interface;
+ struct dhcp_message *offer;
+ struct dhcp_message *new;
+ struct dhcp_message *old;
+ struct dhcp_lease lease;
+ struct timeval timeout;
+ struct timeval stop;
+ struct timeval exit;
+ int state;
+ int messages;
+ time_t nakoff;
+ uint32_t xid;
+ int socket;
+ int *pid_fd;
+ int signal_fd;
+ int carrier;
+ int probes;
+ int claims;
+ int conflicts;
+ time_t defend;
+ struct in_addr fail;
+};
+
+#define LINK_UP 1
+#define LINK_UNKNOWN 0
+#define LINK_DOWN -1
+
+struct dhcp_op {
+ uint8_t value;
+ const char *name;
+};
+
+static const struct dhcp_op const dhcp_ops[] = {
+ { DHCP_DISCOVER, "DHCP_DISCOVER" },
+ { DHCP_OFFER, "DHCP_OFFER" },
+ { DHCP_REQUEST, "DHCP_REQUEST" },
+ { DHCP_DECLINE, "DHCP_DECLINE" },
+ { DHCP_ACK, "DHCP_ACK" },
+ { DHCP_NAK, "DHCP_NAK" },
+ { DHCP_RELEASE, "DHCP_RELEASE" },
+ { DHCP_INFORM, "DHCP_INFORM" },
+ { 0, NULL }
+};
+
+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;
+}
+
+#ifdef THERE_IS_NO_FORK
+#define daemonise(a,b) 0
+#else
+static int
+daemonise(struct if_state *state, const struct options *options)
+{
+ pid_t pid;
+ sigset_t full;
+ sigset_t old;
+ char buf = '\0';
+ int sidpipe[2];
+
+ if (state->options & DHCPCD_DAEMONISED ||
+ !(options->options & DHCPCD_DAEMONISE))
+ return 0;
+
+ sigfillset(&full);
+ sigprocmask(SIG_SETMASK, &full, &old);
+
+ /* Setup a signal pipe so parent knows when to exit. */
+ if (pipe(sidpipe) == -1) {
+ logger(LOG_ERR,"pipe: %s", strerror(errno));
+ return -1;
+ }
+
+ logger(LOG_DEBUG, "forking to background");
+ switch (pid = fork()) {
+ case -1:
+ logger(LOG_ERR, "fork: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ case 0:
+ setsid();
+ /* Notify parent it's safe to exit as we've detached. */
+ close(sidpipe[0]);
+ write(sidpipe[1], &buf, 1);
+ close(sidpipe[1]);
+ close_fds();
+ break;
+ default:
+ /* Reset signals as we're the parent about to exit. */
+ signal_reset();
+ /* Wait for child to detach */
+ close(sidpipe[1]);
+ read(sidpipe[0], &buf, 1);
+ close(sidpipe[0]);
+ break;
+ }
+
+ /* Done with the fd now */
+ if (pid != 0) {
+ writepid(*state->pid_fd, pid);
+ close(*state->pid_fd);
+ *state->pid_fd = -1;
+ }
+
+ sigprocmask(SIG_SETMASK, &old, NULL);
+ if (pid == 0) {
+ state->options |= DHCPCD_DAEMONISED;
+ timerclear(&state->exit);
+ return 0;
+ }
+ state->options |= DHCPCD_PERSISTENT | DHCPCD_FORKED;
+ return -1;
+}
+#endif
+
+#define THIRTY_YEARS_IN_SECONDS 946707779
+static size_t
+get_duid(unsigned char *duid, const struct interface *iface)
+{
+ FILE *f;
+ uint16_t type = 0;
+ uint16_t hw = 0;
+ uint32_t ul;
+ time_t t;
+ int x = 0;
+ unsigned char *p = duid;
+ size_t len = 0, l = 0;
+ char *buffer = NULL, *line, *option;
+
+ /* 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 ((get_line(&buffer, &len, f))) {
+ line = buffer;
+ while ((option = strsep(&line, " \t")))
+ if (*option != '\0')
+ break;
+ if (!option || *option == '\0' || *option == '#')
+ continue;
+ l = hwaddr_aton(NULL, option);
+ if (l && l <= DUID_LEN) {
+ hwaddr_aton(duid, option);
+ break;
+ }
+ l = 0;
+ }
+ fclose(f);
+ free(buffer);
+ if (l)
+ return l;
+ } else {
+ if (errno != ENOENT)
+ return 0;
+ }
+
+ /* No file? OK, lets make one based on our interface */
+ if (!(f = fopen(DUID, "w")))
+ return 0;
+ type = htons(1); /* DUI-D-LLT */
+ memcpy(p, &type, 2);
+ p += 2;
+ hw = htons(iface->family);
+ memcpy(p, &hw, 2);
+ p += 2;
+ /* time returns seconds from jan 1 1970, but DUID-LLT is
+ * seconds from jan 1 2000 modulo 2^32 */
+ t = time(NULL) - THIRTY_YEARS_IN_SECONDS;
+ ul = htonl(t & 0xffffffff);
+ memcpy(p, &ul, 4);
+ p += 4;
+ /* Finally, add the MAC address of the interface */
+ memcpy(p, iface->hwaddr, iface->hwlen);
+ p += iface->hwlen;
+ len = p - duid;
+ x = fprintf(f, "%s\n", hwaddr_ntoa(duid, len));
+ fclose(f);
+ /* Failed to write the duid? scrub it, we cannot use it */
+ if (x < 1) {
+ len = 0;
+ unlink(DUID);
+ }
+ return len;
+}
+
+static struct dhcp_message*
+ipv4ll_get_dhcp(uint32_t old_addr)
+{
+ uint32_t u32;
+ struct dhcp_message *dhcp;
+ uint8_t *p;
+
+ dhcp = xzalloc(sizeof(*dhcp));
+ /* Put some LL options in */
+ p = dhcp->options;
+ *p++ = DHO_SUBNETMASK;
+ *p++ = sizeof(u32);
+ u32 = htonl(LINKLOCAL_MASK);
+ memcpy(p, &u32, sizeof(u32));
+ p += sizeof(u32);
+ *p++ = DHO_BROADCAST;
+ *p++ = sizeof(u32);
+ u32 = htonl(LINKLOCAL_BRDC);
+ memcpy(p, &u32, sizeof(u32));
+ p += sizeof(u32);
+ *p++ = DHO_END;
+
+ for (;;) {
+ dhcp->yiaddr = htonl(LINKLOCAL_ADDR |
+ (((uint32_t)abs((int)arc4random())
+ % 0xFD00) + 0x0100));
+ if (dhcp->yiaddr != old_addr &&
+ IN_LINKLOCAL(ntohl(dhcp->yiaddr)))
+ break;
+ }
+ return dhcp;
+}
+
+static double
+timeval_to_double(struct timeval *tv)
+{
+ return tv->tv_sec * 1.0 + tv->tv_usec * 1.0e-6;
+}
+
+static void
+get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
+{
+ time_t t;
+
+ lease->frominfo = 0;
+ lease->addr.s_addr = dhcp->yiaddr;
+
+ if (get_option_addr(&lease->net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
+ lease->net.s_addr = get_netmask(dhcp->yiaddr);
+ if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) == 0) {
+ /* Ensure that we can use the lease */
+ t = 0;
+ if (t + (time_t)lease->leasetime < t) {
+ logger(LOG_WARNING, "lease of %u would overflow, "
+ "treating as infinite", lease->leasetime);
+ lease->leasetime = ~0U; /* Infinite lease */
+ }
+ } else
+ lease->leasetime = DEFAULT_LEASETIME;
+ 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;
+}
+
+static int
+get_old_lease(struct if_state *state)
+{
+ struct interface *iface = state->interface;
+ struct dhcp_lease *lease = &state->lease;
+ struct dhcp_message *dhcp = NULL;
+ struct timeval tv;
+ unsigned int offset = 0;
+ struct stat sb;
+
+ if (stat(iface->leasefile, &sb) == -1) {
+ if (errno != ENOENT)
+ logger(LOG_ERR, "stat: %s", strerror(errno));
+ goto eexit;
+ }
+ if (!IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
+ logger(LOG_INFO, "trying to use old lease in `%s'",
+ iface->leasefile);
+ if ((dhcp = read_lease(iface)) == NULL) {
+ logger(LOG_INFO, "read_lease: %s", strerror(errno));
+ goto eexit;
+ }
+ get_lease(&state->lease, dhcp);
+ lease->frominfo = 1;
+ lease->leasedfrom = sb.st_mtime;
+
+ /* Vitaly important we remove the server information here */
+ state->lease.server.s_addr = 0;
+ dhcp->servername[0] = '\0';
+
+ if (!IN_LINKLOCAL(ntohl(dhcp->yiaddr))) {
+ if (!(state->options & DHCPCD_LASTLEASE))
+ goto eexit;
+
+ /* Ensure that we can still use the lease */
+ if (gettimeofday(&tv, NULL) == -1) {
+ logger(LOG_ERR, "gettimeofday: %s", strerror(errno));
+ goto eexit;
+ }
+
+ offset = tv.tv_sec - lease->leasedfrom;
+ if (lease->leasedfrom &&
+ tv.tv_sec - lease->leasedfrom > (time_t)lease->leasetime)
+ {
+ logger(LOG_ERR, "lease expired %u seconds ago",
+ offset + lease->leasetime);
+ /* Persistent interfaces should still try and use the
+ * lease if we can't contact a DHCP server.
+ * We just set the timeout to 1 second. */
+ if (state->options & DHCPCD_PERSISTENT)
+ offset = lease->renewaltime - 1;
+ else
+ goto eexit;
+ }
+ }
+
+ if (lease->leasedfrom == 0)
+ offset = 0;
+ iface->start_uptime = uptime();
+ state->timeout.tv_sec = lease->renewaltime - offset;
+ free(state->old);
+ state->old = state->new;
+ state->new = NULL;
+ state->offer = dhcp;
+ return 0;
+
+eexit:
+ lease->addr.s_addr = 0;
+ free(dhcp);
+ return -1;
+}
+
+static int
+client_setup(struct if_state *state, const struct options *options)
+{
+ struct interface *iface = state->interface;
+ struct dhcp_lease *lease = &state->lease;
+ struct in_addr addr;
+ struct timeval tv;
+ size_t len = 0;
+ unsigned char *duid = NULL;
+ uint32_t ul;
+
+ state->state = STATE_INIT;
+ state->nakoff = 1;
+ state->options = options->options;
+ timerclear(&tv);
+
+ if (options->request_address.s_addr == 0 &&
+ (options->options & DHCPCD_INFORM ||
+ options->options & DHCPCD_REQUEST ||
+ (options->options & DHCPCD_DAEMONISED &&
+ !(options->options & DHCPCD_BACKGROUND))))
+ {
+ if (get_old_lease(state) != 0)
+ return -1;
+ timerclear(&state->timeout);
+
+ if (!(options->options & DHCPCD_DAEMONISED) &&
+ IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
+ {
+ logger(LOG_ERR, "cannot request a link local address");
+ return -1;
+ }
+ } else {
+ lease->addr.s_addr = options->request_address.s_addr;
+ lease->net.s_addr = options->request_netmask.s_addr;
+ }
+
+ if (options->options & DHCPCD_REQUEST &&
+ state->options & DHCPCD_ARP &&
+ !state->offer)
+ {
+ state->offer = xzalloc(sizeof(*state->offer));
+ state->offer->yiaddr = options->request_address.s_addr;
+ state->state = STATE_PROBING;
+ state->xid = arc4random();
+ }
+
+ /* If INFORMing, ensure the interface has the address */
+ if (state->options & DHCPCD_INFORM &&
+ has_address(iface->name, &lease->addr, &lease->net) < 1)
+ {
+ addr.s_addr = lease->addr.s_addr | ~lease->net.s_addr;
+ logger(LOG_DEBUG, "adding IP address %s/%d",
+ inet_ntoa(lease->addr), inet_ntocidr(lease->net));
+ if (add_address(iface->name, &lease->addr,
+ &lease->net, &addr) == -1)
+ {
+ logger(LOG_ERR, "add_address: %s", strerror(errno));
+ return -1;
+ }
+ iface->addr.s_addr = lease->addr.s_addr;
+ iface->net.s_addr = lease->net.s_addr;
+ }
+
+ if (*options->clientid) {
+ iface->clientid = xmalloc(options->clientid[0] + 1);
+ memcpy(iface->clientid,
+ options->clientid, options->clientid[0] + 1);
+ } else if (options->options & DHCPCD_CLIENTID) {
+ if (options->options & DHCPCD_DUID) {
+ duid = xmalloc(DUID_LEN);
+ if ((len = get_duid(duid, iface)) == 0)
+ logger(LOG_ERR, "get_duid: %s",
+ strerror(errno));
+ }
+
+ if (len > 0) {
+ logger(LOG_DEBUG, "DUID = %s",
+ hwaddr_ntoa(duid, len));
+
+ iface->clientid = xmalloc(len + 6);
+ iface->clientid[0] = len + 5;
+ iface->clientid[1] = 255; /* RFC 4361 */
+
+ /* IAID is 4 bytes, so if the iface name is 4 bytes
+ * or less, use it */
+ ul = strlen(iface->name);
+ if (ul < 5) {
+ memcpy(iface->clientid + 2, iface->name, ul);
+ if (ul < 4)
+ memset(iface->clientid + 2 + ul,
+ 0, 4 - ul);
+ } else {
+ /* Name isn't 4 bytes, so use the index */
+ ul = htonl(if_nametoindex(iface->name));
+ memcpy(iface->clientid + 2, &ul, 4);
+ }
+
+ memcpy(iface->clientid + 6, duid, len);
+ free(duid);
+ }
+ if (len == 0) {
+ len = iface->hwlen + 1;
+ iface->clientid = xmalloc(len + 1);
+ iface->clientid[0] = len;
+ iface->clientid[1] = iface->family;
+ memcpy(iface->clientid + 2, iface->hwaddr, iface->hwlen);
+ }
+ }
+
+ if (state->options & DHCPCD_LINK) {
+ open_link_socket(iface);
+ switch (carrier_status(iface->name)) {
+ case 0:
+ state->carrier = LINK_DOWN;
+ break;
+ case 1:
+ state->carrier = LINK_UP;
+ break;
+ default:
+ state->carrier = LINK_UNKNOWN;
+ }
+ }
+
+ if (options->timeout > 0 &&
+ !(state->options & DHCPCD_DAEMONISED))
+ {
+ if (state->options & DHCPCD_IPV4LL) {
+ state->stop.tv_sec = options->timeout;
+ if (!(state->options & DHCPCD_BACKGROUND))
+ state->exit.tv_sec = state->stop.tv_sec + 10;
+ } else if (!(state->options & DHCPCD_BACKGROUND))
+ state->exit.tv_sec = options->timeout;
+ }
+ return 0;
+}
+
+static int
+do_socket(struct if_state *state, int mode)
+{
+ if (state->interface->raw_fd != -1) {
+ close(state->interface->raw_fd);
+ state->interface->raw_fd = -1;
+ }
+ if (mode == SOCKET_CLOSED) {
+ if (state->interface->udp_fd != -1) {
+ close(state->interface->udp_fd);
+ state->interface->udp_fd = -1;
+ }
+ if (state->interface->arp_fd != -1) {
+ close(state->interface->arp_fd);
+ state->interface->arp_fd = -1;
+ }
+ }
+
+ /* Always have the UDP socket open to avoid the kernel sending
+ * ICMP unreachable messages. */
+ /* For systems without SO_BINDTODEVICE, (ie BSD ones) we may get an
+ * error or EADDRINUSE when binding to INADDR_ANY as another dhcpcd
+ * instance could be running.
+ * Oddly enough, we don't care about this as the socket is there
+ * just to please the kernel - we don't care for reading from it. */
+ if (mode == SOCKET_OPEN &&
+ state->interface->udp_fd == -1 &&
+ open_udp_socket(state->interface) == -1 &&
+ (errno != EADDRINUSE || state->interface->addr.s_addr != 0))
+ logger(LOG_ERR, "open_udp_socket: %s", strerror(errno));
+
+ if (mode == SOCKET_OPEN)
+ if (open_socket(state->interface, ETHERTYPE_IP) == -1) {
+ logger(LOG_ERR, "open_socket: %s", strerror(errno));
+ return -1;
+ }
+ state->socket = mode;
+ return 0;
+}
+
+static ssize_t
+send_message(struct if_state *state, int type, const struct options *options)
+{
+ struct dhcp_message *dhcp;
+ uint8_t *udp;
+ ssize_t len, r;
+ struct in_addr from, to;
+ in_addr_t a = 0;
+
+ if (state->carrier == LINK_DOWN)
+ return 0;
+ if (type == DHCP_RELEASE)
+ logger(LOG_DEBUG, "sending %s with xid 0x%x",
+ get_dhcp_op(type), state->xid);
+ else
+ logger(LOG_DEBUG,
+ "sending %s with xid 0x%x, next in %0.2f seconds",
+ get_dhcp_op(type), state->xid,
+ timeval_to_double(&state->timeout));
+ state->messages++;
+ if (state->messages < 0)
+ state->messages = INT_MAX;
+ /* 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. */
+ if (state->interface->udp_fd == -1) {
+ a = state->interface->addr.s_addr;
+ state->interface->addr.s_addr = 0;
+ }
+ len = make_message(&dhcp, state->interface, &state->lease, state->xid,
+ type, options);
+ if (state->interface->udp_fd == -1)
+ state->interface->addr.s_addr = a;
+ from.s_addr = dhcp->ciaddr;
+ if (from.s_addr)
+ to.s_addr = state->lease.server.s_addr;
+ else
+ to.s_addr = 0;
+ if (to.s_addr && to.s_addr != INADDR_BROADCAST) {
+ r = send_packet(state->interface, to, (uint8_t *)dhcp, len);
+ if (r == -1)
+ logger(LOG_ERR, "send_packet: %s", strerror(errno));
+ } else {
+ len = make_udp_packet(&udp, (uint8_t *)dhcp, len, from, to);
+ r = send_raw_packet(state->interface, ETHERTYPE_IP, udp, len);
+ free(udp);
+ if (r == -1)
+ logger(LOG_ERR, "send_raw_packet: %s", strerror(errno));
+ }
+ free(dhcp);
+ /* Failed to send the packet? Return to the init state */
+ if (r == -1) {
+ state->state = STATE_INIT;
+ timerclear(&state->timeout);
+ timerclear(&state->stop);
+ do_socket(state, SOCKET_CLOSED);
+ }
+ return r;
+}
+
+static void
+drop_config(struct if_state *state, const char *reason,
+ const struct options *options)
+{
+ if (state->new || strcmp(reason, "FAIL") == 0) {
+ configure(state->interface, reason, NULL, state->new,
+ &state->lease, options, 0);
+ free(state->old);
+ state->old = NULL;
+ free(state->new);
+ state->new = NULL;
+ }
+ state->lease.addr.s_addr = 0;
+}
+
+static void
+reduce_timers(struct if_state *state, const struct timeval *tv)
+{
+ if (timerisset(&state->exit)) {
+ timersub(&state->exit, tv, &state->exit);
+ if (!timerisset(&state->exit))
+ state->exit.tv_sec = -1;
+ }
+ if (timerisset(&state->stop)) {
+ timersub(&state->stop, tv, &state->stop);
+ if (!timerisset(&state->stop))
+ state->stop.tv_sec = -1;
+ }
+ if (timerisset(&state->timeout)) {
+ timersub(&state->timeout, tv, &state->timeout);
+ if (!timerisset(&state->timeout))
+ state->timeout.tv_sec = -1;
+ }
+}
+
+static struct timeval *
+get_lowest_timer(struct if_state *state)
+{
+ struct timeval *ref = NULL;
+
+ if (timerisset(&state->exit))
+ ref = &state->exit;
+ if (timerisset(&state->stop)) {
+ if (!ref || timercmp(&state->stop, ref, <))
+ ref = &state->stop;
+ }
+ if (timerisset(&state->timeout)) {
+ if (!ref || timercmp(&state->timeout, ref, <))
+ ref = &state->timeout;
+ }
+ return ref;
+}
+
+static int
+wait_for_fd(struct if_state *state, int *fd)
+{
+ struct pollfd fds[4]; /* signal, link, raw, arp */
+ struct interface *iface = state->interface;
+ int i, r, nfds = 0, msecs = -1;
+ struct timeval start, stop, diff, *ref;
+ static int lastinf = 0;
+
+ /* Ensure that we haven't already timed out */
+ ref = get_lowest_timer(state);
+ if (ref && timerneg(ref))
+ return 0;
+
+ /* We always listen to signals */
+ fds[nfds].fd = state->signal_fd;
+ fds[nfds].events = POLLIN;
+ nfds++;
+ /* And links */
+ if (iface->link_fd != -1) {
+ fds[nfds].fd = iface->link_fd;
+ fds[nfds].events = POLLIN;
+ nfds++;
+ }
+
+ if (state->lease.leasetime == ~0U &&
+ state->state == STATE_BOUND)
+ {
+ if (!lastinf) {
+ logger(LOG_DEBUG, "waiting for infinity");
+ lastinf = 1;
+ }
+ ref = NULL;
+ } else if (state->carrier == LINK_DOWN && !ref) {
+ if (!lastinf) {
+ logger(LOG_DEBUG, "waiting for carrier");
+ lastinf = 1;
+ }
+ if (timerisset(&state->exit))
+ ref = &state->exit;
+ else
+ ref = NULL;
+ } else {
+ if (iface->raw_fd != -1) {
+ fds[nfds].fd = iface->raw_fd;
+ fds[nfds].events = POLLIN;
+ nfds++;
+ }
+ if (iface->arp_fd != -1) {
+ fds[nfds].fd = iface->arp_fd;
+ fds[nfds].events = POLLIN;
+ nfds++;
+ }
+ }
+
+ /* Wait and then reduce the timers.
+ * If we reduce a timer to zero, set it negative to indicate timeout.
+ * We cannot reliably use select as there is no guarantee we will
+ * actually wait the whole time if greater than 31 days according
+ * to POSIX. So we loop on poll if needed as it's limitation of
+ * INT_MAX milliseconds is known. */
+ for (;;) {
+ get_monotonic(&start);
+ if (ref) {
+ lastinf = 0;
+ if (ref->tv_sec > INT_MAX / 1000 ||
+ (ref->tv_sec == INT_MAX / 1000 &&
+ (ref->tv_usec + 999) / 1000 > INT_MAX % 1000))
+ msecs = INT_MAX;
+ else
+ msecs = ref->tv_sec * 1000 +
+ (ref->tv_usec + 999) / 1000;
+ } else
+ msecs = -1;
+ r = poll(fds, nfds, msecs);
+ get_monotonic(&stop);
+ timersub(&stop, &start, &diff);
+ reduce_timers(state, &diff);
+ if (r == -1) {
+ if (errno != EINTR)
+ logger(LOG_ERR, "poll: %s", strerror(errno));
+ return -1;
+ }
+ if (r)
+ break;
+ /* We should not have an infinite timeout if we get here */
+ if (timerneg(ref))
+ return 0;
+ }
+
+ /* We configured our array in the order we should deal with them */
+ for (i = 0; i < nfds; i++)
+ if (fds[i].revents & POLLIN) {
+ *fd = fds[i].fd;
+ return r;
+ }
+ return r;
+}
+
+static int
+handle_signal(int sig, struct if_state *state, const struct options *options)
+{
+ struct dhcp_lease *lease = &state->lease;
+
+ switch (sig) {
+ case SIGINT:
+ logger(LOG_INFO, "received SIGINT, stopping");
+ if (!(state->options & DHCPCD_PERSISTENT))
+ drop_config(state, "STOP", options);
+ return -1;
+ case SIGTERM:
+ logger(LOG_INFO, "received SIGTERM, stopping");
+ if (!(state->options & DHCPCD_PERSISTENT))
+ drop_config(state, "STOP", options);
+ return -1;
+ case SIGALRM:
+ logger(LOG_INFO, "received SIGALRM, renewing lease");
+ do_socket(state, SOCKET_CLOSED);
+ state->state = STATE_RENEW_REQUESTED;
+ timerclear(&state->timeout);
+ timerclear(&state->stop);
+ return 1;
+ case SIGHUP:
+ logger(LOG_INFO, "received SIGHUP, releasing lease");
+ if (lease->addr.s_addr &&
+ !IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
+ {
+ do_socket(state, SOCKET_OPEN);
+ state->xid = arc4random();
+ send_message(state, DHCP_RELEASE, options);
+ do_socket(state, SOCKET_CLOSED);
+ }
+ drop_config(state, "RELEASE", options);
+ return -1;
+ default:
+ logger (LOG_ERR,
+ "received signal %d, but don't know what to do with it",
+ sig);
+ }
+
+ return 0;
+}
+
+static int bind_dhcp(struct if_state *state, const struct options *options)
+{
+ struct interface *iface = state->interface;
+ struct dhcp_lease *lease = &state->lease;
+ const char *reason = NULL;
+ struct timeval start, stop, diff;
+ int retval;
+
+ free(state->old);
+ state->old = state->new;
+ state->new = state->offer;
+ state->offer = NULL;
+ state->messages = 0;
+ state->conflicts = 0;
+ state->defend = 0;
+ timerclear(&state->exit);
+ if (clock_monotonic)
+ get_monotonic(&lease->boundtime);
+
+ if (options->options & DHCPCD_INFORM) {
+ if (options->request_address.s_addr != 0)
+ lease->addr.s_addr = options->request_address.s_addr;
+ else
+ lease->addr.s_addr = iface->addr.s_addr;
+ logger(LOG_INFO, "received approval for %s",
+ inet_ntoa(lease->addr));
+ state->state = STATE_BOUND;
+ state->lease.leasetime = ~0U;
+ timerclear(&state->stop);
+ reason = "INFORM";
+ } else if (IN_LINKLOCAL(htonl(state->new->yiaddr))) {
+ get_lease(lease, state->new);
+ logger(LOG_INFO, "using IPv4LL address %s",
+ inet_ntoa(lease->addr));
+ state->state = STATE_INIT;
+ timerclear(&state->timeout);
+ reason = "IPV4LL";
+ } else {
+ if (gettimeofday(&start, NULL) == 0)
+ lease->leasedfrom = start.tv_sec;
+
+ get_lease(lease, state->new);
+ if (lease->frominfo)
+ reason = "TIMEOUT";
+
+ if (lease->leasetime == ~0U) {
+ lease->renewaltime = lease->rebindtime = lease->leasetime;
+ logger(LOG_INFO, "leased %s for infinity",
+ inet_ntoa(lease->addr));
+ state->state = STATE_BOUND;
+ timerclear(&state->stop);
+ } else {
+ if (lease->rebindtime >= lease->leasetime) {
+ lease->rebindtime = lease->leasetime * T2;
+ logger(LOG_ERR,
+ "rebind time greater than lease "
+ "time, forcing to %u seconds",
+ lease->rebindtime);
+ }
+ if (lease->renewaltime > lease->rebindtime) {
+ lease->renewaltime = lease->leasetime * T1;
+ logger(LOG_ERR,
+ "renewal time greater than rebind time, "
+ "forcing to %u seconds",
+ lease->renewaltime);
+ }
+ if (!lease->renewaltime)
+ lease->renewaltime = lease->leasetime * T1;
+ if (!lease->rebindtime)
+ lease->rebindtime = lease->leasetime * T2;
+ logger(LOG_INFO,
+ "leased %s for %u seconds",
+ inet_ntoa(lease->addr), lease->leasetime);
+ state->stop.tv_sec = lease->renewaltime;
+ state->stop.tv_usec = 0;
+ }
+ state->state = STATE_BOUND;
+ }
+
+ state->xid = 0;
+ timerclear(&state->timeout);
+ if (!reason) {
+ if (state->old) {
+ if (state->old->yiaddr == state->new->yiaddr &&
+ lease->server.s_addr)
+ reason = "RENEW";
+ else
+ reason = "REBIND";
+ } else
+ reason = "BOUND";
+ }
+ /* If we have a monotonic clock we can safely substract the
+ * script execution time from our timers.
+ * Otherwise we can't as the script may update the real time. */
+ if (clock_monotonic)
+ get_monotonic(&start);
+ retval = configure(iface, reason, state->new, state->old,
+ &state->lease, options, 1);
+ if (clock_monotonic) {
+ get_monotonic(&stop);
+ timersub(&stop, &start, &diff);
+ reduce_timers(state, &diff);
+ }
+ if (retval != 0)
+ return -1;
+ return daemonise(state, options);
+}
+
+static int
+handle_timeout_fail(struct if_state *state, const struct options *options)
+{
+ struct dhcp_lease *lease = &state->lease;
+ struct interface *iface = state->interface;
+ int gotlease = -1;
+ const char *reason = NULL;
+
+ timerclear(&state->stop);
+ timerclear(&state->exit);
+ if (state->state != STATE_DISCOVERING)
+ state->messages = 0;
+
+ switch (state->state) {
+ case STATE_INIT: /* FALLTHROUGH */
+ case STATE_DISCOVERING: /* FALLTHROUGH */
+ case STATE_REQUESTING:
+ if (IN_LINKLOCAL(ntohl(iface->addr.s_addr))) {
+ if (!(state->options & DHCPCD_DAEMONISED))
+ logger(LOG_ERR, "timed out");
+ } else {
+ if (iface->addr.s_addr != 0 &&
+ !(state->options & DHCPCD_INFORM))
+ logger(LOG_ERR, "lost lease");
+ else if (state->carrier != LINK_DOWN ||
+ !(state->options & DHCPCD_DAEMONISED))
+ logger(LOG_ERR, "timed out");
+ }
+ do_socket(state, SOCKET_CLOSED);
+ if (state->options & DHCPCD_INFORM ||
+ state->options & DHCPCD_TEST)
+ return -1;
+
+ if (state->carrier != LINK_DOWN &&
+ (state->options & DHCPCD_IPV4LL ||
+ state->options & DHCPCD_LASTLEASE))
+ gotlease = get_old_lease(state);
+
+ if (state->carrier != LINK_DOWN &&
+ state->options & DHCPCD_IPV4LL &&
+ gotlease != 0)
+ {
+ logger(LOG_INFO, "probing for an IPV4LL address");
+ free(state->offer);
+ state->offer = ipv4ll_get_dhcp(0);
+ gotlease = 0;
+ }
+
+ if (gotlease == 0 &&
+ state->offer->yiaddr != iface->addr.s_addr)
+ {
+ state->state = STATE_PROBING;
+ state->claims = 0;
+ state->probes = 0;
+ if (iface->addr.s_addr)
+ state->conflicts = 0;
+ return 1;
+ }
+
+ if (gotlease == 0)
+ return bind_dhcp(state, options);
+
+ if (iface->addr.s_addr)
+ reason = "EXPIRE";
+ else
+ reason = "FAIL";
+ drop_config(state, reason, options);
+ if (!(state->options & DHCPCD_DAEMONISED) &&
+ (state->options & DHCPCD_DAEMONISE))
+ return -1;
+ state->state = STATE_RENEW_REQUESTED;
+ return 1;
+ case STATE_BOUND:
+ logger(LOG_INFO, "renewing lease of %s",inet_ntoa(lease->addr));
+ if (state->carrier != LINK_DOWN)
+ do_socket(state, SOCKET_OPEN);
+ state->xid = arc4random();
+ state->state = STATE_RENEWING;
+ state->stop.tv_sec = lease->rebindtime - lease->renewaltime;
+ break;
+ case STATE_RENEWING:
+ logger(LOG_ERR, "failed to renew, attempting to rebind");
+ state->state = STATE_REBINDING;
+ if (lease->server.s_addr == 0)
+ state->stop.tv_sec = options->timeout;
+ else
+ state->stop.tv_sec = lease->rebindtime - \
+ lease->renewaltime;
+ lease->server.s_addr = 0;
+ break;
+ case STATE_REBINDING:
+ logger(LOG_ERR, "failed to rebind");
+ reason = "EXPIRE";
+ drop_config(state, reason, options);
+ state->state = STATE_INIT;
+ break;
+ case STATE_PROBING: /* FALLTHROUGH */
+ case STATE_ANNOUNCING:
+ /* We should have lost carrier here and exit timer went */
+ logger(LOG_ERR, "timed out");
+ return -1;
+ default:
+ logger(LOG_DEBUG, "handle_timeout_failed: invalid state %d",
+ state->state);
+ }
+
+ /* This effectively falls through into the handle_timeout funtion */
+ return 1;
+}
+
+static int
+handle_timeout(struct if_state *state, const struct options *options)
+{
+ struct dhcp_lease *lease = &state->lease;
+ struct interface *iface = state->interface;
+ int i = 0;
+ struct in_addr addr;
+ struct timeval tv;
+
+ timerclear(&state->timeout);
+ if (timerneg(&state->exit))
+ return handle_timeout_fail(state, options);
+
+ if (state->state == STATE_RENEW_REQUESTED &&
+ IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
+ {
+ state->state = STATE_PROBING;
+ free(state->offer);
+ state->offer = read_lease(state->interface);
+ state->probes = 0;
+ state->claims = 0;
+ }
+ switch (state->state) {
+ case STATE_INIT_IPV4LL:
+ state->state = STATE_PROBING;
+ free(state->offer);
+ state->offer = ipv4ll_get_dhcp(0);
+ state->probes = 0;
+ state->claims = 0;
+ /* FALLTHROUGH */
+ case STATE_PROBING:
+ if (iface->arp_fd == -1)
+ open_socket(iface, ETHERTYPE_ARP);
+ if (state->probes < PROBE_NUM) {
+ if (state->probes == 0) {
+ addr.s_addr = state->offer->yiaddr;
+ logger(LOG_INFO, "checking %s is available"
+ " on attached networks",
+ inet_ntoa(addr));
+ }
+ state->probes++;
+ if (state->probes < PROBE_NUM) {
+ state->timeout.tv_sec = PROBE_MIN;
+ state->timeout.tv_usec = arc4random() %
+ (PROBE_MAX_U - PROBE_MIN_U);
+ timernorm(&state->timeout);
+ } else {
+ state->timeout.tv_sec = ANNOUNCE_WAIT;
+ state->timeout.tv_usec = 0;
+ }
+ logger(LOG_DEBUG,
+ "sending ARP probe (%d of %d), next in %0.2f seconds",
+ state->probes, PROBE_NUM,
+ timeval_to_double(&state->timeout));
+ if (send_arp(iface, ARPOP_REQUEST, 0,
+ state->offer->yiaddr) == -1)
+ {
+ logger(LOG_ERR, "send_arp: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+ } else {
+ /* We've waited for ANNOUNCE_WAIT after the final probe
+ * so the address is now ours */
+ if (IN_LINKLOCAL(htonl(state->offer->yiaddr))) {
+ i = bind_dhcp(state, options);
+ state->state = STATE_ANNOUNCING;
+ state->timeout.tv_sec = ANNOUNCE_INTERVAL;
+ state->timeout.tv_usec = 0;
+ return i;
+ }
+ state->state = STATE_REQUESTING;
+ }
+ break;
+ case STATE_ANNOUNCING:
+ if (iface->arp_fd == -1)
+ open_socket(iface, ETHERTYPE_ARP);
+ if (state->claims < ANNOUNCE_NUM) {
+ state->claims++;
+ if (state->claims < ANNOUNCE_NUM) {
+ state->timeout.tv_sec = ANNOUNCE_INTERVAL;
+ state->timeout.tv_usec = 0;
+ logger(LOG_DEBUG,
+ "sending ARP announce (%d of %d),"
+ " next in %0.2f seconds",
+ state->claims, ANNOUNCE_NUM,
+ timeval_to_double(&state->timeout));
+ } else
+ logger(LOG_DEBUG,
+ "sending ARP announce (%d of %d)",
+ state->claims, ANNOUNCE_NUM);
+ i = send_arp(iface, ARPOP_REQUEST,
+ state->new->yiaddr, state->new->yiaddr);
+ if (i == -1) {
+ logger(LOG_ERR, "send_arp: %s", strerror(errno));
+ return -1;
+ }
+ }
+ if (state->claims < ANNOUNCE_NUM)
+ return 0;
+ if (IN_LINKLOCAL(htonl(state->new->yiaddr))) {
+ /* We should pretend to be at the end
+ * of the DHCP negotation cycle */
+ state->state = STATE_INIT;
+ state->messages = DHCP_MAX / DHCP_BASE;
+ state->probes = 0;
+ state->claims = 0;
+ timerclear(&state->stop);
+ goto dhcp_timeout;
+ } else {
+ state->state = STATE_BOUND;
+ close(iface->arp_fd);
+ iface->arp_fd = -1;
+ if (lease->leasetime != ~0U) {
+ state->stop.tv_sec = lease->renewaltime;
+ state->stop.tv_usec = 0;
+ if (clock_monotonic) {
+ get_monotonic(&tv);
+ timersub(&tv, &lease->boundtime, &tv);
+ timersub(&state->stop, &tv, &state->stop);
+ } else {
+ state->stop.tv_sec -=
+ (ANNOUNCE_INTERVAL * ANNOUNCE_NUM);
+ }
+ logger(LOG_DEBUG, "renew in %ld seconds",
+ (long int)state->stop.tv_sec);
+ }
+ }
+ return 0;
+ }
+
+ if (timerneg(&state->stop))
+ return handle_timeout_fail(state, options);
+
+ switch (state->state) {
+ case STATE_BOUND: /* FALLTHROUGH */
+ case STATE_RENEW_REQUESTED:
+ timerclear(&state->stop);
+ /* FALLTHROUGH */
+ case STATE_INIT:
+ do_socket(state, SOCKET_OPEN);
+ state->xid = arc4random();
+ iface->start_uptime = uptime();
+ break;
+ }
+
+ switch(state->state) {
+ case STATE_RENEW_REQUESTED:
+ /* If a renew was requested (ie, didn't timeout) we actually
+ * enter the REBIND state so that we broadcast to all servers.
+ * We need to do this for when we change networks. */
+ lease->server.s_addr = 0;
+ state->messages = 0;
+ if (lease->addr.s_addr && !(state->options & DHCPCD_INFORM)) {
+ logger(LOG_INFO, "rebinding lease of %s",
+ inet_ntoa(lease->addr));
+ state->state = STATE_REBINDING;
+ state->stop.tv_sec = options->timeout;
+ state->stop.tv_usec = 0;
+ break;
+ }
+ /* FALLTHROUGH */
+ case STATE_INIT:
+ if (state->carrier == LINK_DOWN)
+ return 0;
+ if (lease->addr.s_addr == 0 ||
+ IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
+ {
+ logger(LOG_INFO, "broadcasting for a lease");
+ state->state = STATE_DISCOVERING;
+ } else if (state->options & DHCPCD_INFORM) {
+ logger(LOG_INFO, "broadcasting inform for %s",
+ inet_ntoa(lease->addr));
+ state->state = STATE_REQUESTING;
+ } else {
+ logger(LOG_INFO, "broadcasting for a lease of %s",
+ inet_ntoa(lease->addr));
+ state->state = STATE_REQUESTING;
+ }
+ if (!lease->addr.s_addr && !timerisset(&state->stop)) {
+ state->stop.tv_sec = DHCP_MAX + DHCP_RAND_MIN;
+ state->stop.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+ timernorm(&state->stop);
+ }
+ break;
+ }
+
+dhcp_timeout:
+ if (state->carrier == LINK_DOWN) {
+ timerclear(&state->timeout);
+ return 0;
+ }
+ state->timeout.tv_sec = DHCP_BASE;
+ for (i = 0; i < state->messages; i++) {
+ state->timeout.tv_sec *= 2;
+ if (state->timeout.tv_sec > DHCP_MAX) {
+ state->timeout.tv_sec = DHCP_MAX;
+ break;
+ }
+ }
+ state->timeout.tv_sec += DHCP_RAND_MIN;
+ state->timeout.tv_usec = arc4random() %
+ (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+ timernorm(&state->timeout);
+
+ /* We send the message here so that the timeout is reported */
+ switch (state->state) {
+ case STATE_DISCOVERING:
+ send_message(state, DHCP_DISCOVER, options);
+ break;
+ case STATE_REQUESTING:
+ if (state->options & DHCPCD_INFORM) {
+ send_message(state, DHCP_INFORM, options);
+ break;
+ }
+ /* FALLTHROUGH */
+ case STATE_RENEWING: /* FALLTHROUGH */
+ case STATE_REBINDING:
+ if (iface->raw_fd == -1)
+ do_socket(state, SOCKET_OPEN);
+ send_message(state, DHCP_REQUEST, options);
+ break;
+ }
+
+ return 0;
+}
+
+static void
+log_dhcp(int lvl, const char *msg, const struct dhcp_message *dhcp)
+{
+ char *a;
+ struct in_addr addr;
+ int r;
+
+ if (strcmp(msg, "NAK:") == 0)
+ a = get_option_string(dhcp, DHO_MESSAGE);
+ else {
+ addr.s_addr = dhcp->yiaddr;
+ a = xstrdup(inet_ntoa(addr));
+ }
+ r = get_option_addr(&addr.s_addr, dhcp, DHO_SERVERID);
+ if (dhcp->servername[0] && r == 0)
+ logger(lvl, "%s %s from %s `%s'", msg, a,
+ inet_ntoa(addr), dhcp->servername);
+ else if (r == 0)
+ logger(lvl, "%s %s from %s", msg, a, inet_ntoa(addr));
+ else
+ logger(lvl, "%s %s", msg, a);
+ free(a);
+}
+
+static int
+handle_dhcp(struct if_state *state, struct dhcp_message **dhcpp,
+ const struct options *options)
+{
+ struct dhcp_message *dhcp = *dhcpp;
+ struct interface *iface = state->interface;
+ struct dhcp_lease *lease = &state->lease;
+ uint8_t type, tmp;
+ struct in_addr addr;
+ size_t i;
+ int r;
+
+ /* reset the message counter */
+ state->messages = 0;
+
+ /* We have to have DHCP type to work */
+ if (get_option_uint8(&type, dhcp, DHO_MESSAGETYPE) == -1) {
+ log_dhcp(LOG_ERR, "no DHCP type in", dhcp);
+ return 0;
+ }
+
+ /* Ensure that it's not from a blacklisted server.
+ * We should expand this to check IP and/or hardware address
+ * at the packet level. */
+ if (options->blacklist_len != 0 &&
+ get_option_addr(&addr.s_addr, dhcp, DHO_SERVERID) == 0)
+ {
+ for (i = 0; i < options->blacklist_len; i++) {
+ if (options->blacklist[i] != addr.s_addr)
+ continue;
+ if (dhcp->servername[0])
+ logger(LOG_WARNING,
+ "ignoring blacklisted server %s `%s'",
+ inet_ntoa(addr), dhcp->servername);
+ else
+ logger(LOG_WARNING,
+ "ignoring blacklisted server %s",
+ inet_ntoa(addr));
+ return 0;
+ }
+ }
+
+ /* We should restart on a NAK */
+ if (type == DHCP_NAK) {
+ log_dhcp(LOG_WARNING, "NAK:", dhcp);
+ drop_config(state, "EXPIRE", options);
+ do_socket(state, SOCKET_CLOSED);
+ state->state = STATE_INIT;
+ /* If we constantly get NAKS then we should slowly back off */
+ if (state->nakoff == 0) {
+ state->nakoff = 1;
+ timerclear(&state->timeout);
+ } else {
+ state->timeout.tv_sec = state->nakoff;
+ state->timeout.tv_usec = 0;
+ state->nakoff *= 2;
+ if (state->nakoff > NAKOFF_MAX)
+ state->nakoff = NAKOFF_MAX;
+ }
+ return 0;
+ }
+
+ /* No NAK, so reset the backoff */
+ state->nakoff = 1;
+
+ /* Ensure that all required options are present */
+ for (i = 1; i < 255; i++) {
+ if (has_option_mask(options->requiremask, i) &&
+ get_option_uint8(&tmp, dhcp, i) != 0)
+ {
+ log_dhcp(LOG_WARNING, "reject", dhcp);
+ return 0;
+ }
+ }
+
+ if (type == DHCP_OFFER && state->state == STATE_DISCOVERING) {
+ lease->addr.s_addr = dhcp->yiaddr;
+ get_option_addr(&lease->server.s_addr, dhcp, DHO_SERVERID);
+ log_dhcp(LOG_INFO, "offered", dhcp);
+ if (state->options & DHCPCD_TEST) {
+ run_script(options, iface->name, "TEST", dhcp, NULL);
+ /* Fake the fact we forked so we return 0 to userland */
+ state->options |= DHCPCD_FORKED;
+ return -1;
+ }
+ free(state->offer);
+ state->offer = dhcp;
+ *dhcpp = NULL;
+ timerclear(&state->timeout);
+ if (state->options & DHCPCD_ARP &&
+ iface->addr.s_addr != state->offer->yiaddr)
+ {
+ /* If the interface already has the address configured
+ * then we can't ARP for duplicate detection. */
+ addr.s_addr = state->offer->yiaddr;
+ if (!has_address(iface->name, &addr, NULL)) {
+ state->state = STATE_PROBING;
+ state->claims = 0;
+ state->probes = 0;
+ state->conflicts = 0;
+ timerclear(&state->stop);
+ return 1;
+ }
+ }
+ state->state = STATE_REQUESTING;
+ return 1;
+ }
+
+ if (type == DHCP_OFFER) {
+ log_dhcp(LOG_INFO, "ignoring offer of", dhcp);
+ return 0;
+ }
+
+ /* We should only be dealing with acks */
+ if (type != DHCP_ACK) {
+ log_dhcp(LOG_ERR, "not ACK or OFFER", dhcp);
+ return 0;
+ }
+
+ switch (state->state) {
+ case STATE_RENEW_REQUESTED:
+ case STATE_REQUESTING:
+ case STATE_RENEWING:
+ case STATE_REBINDING:
+ if (!(state->options & DHCPCD_INFORM)) {
+ get_option_addr(&lease->server.s_addr,
+ dhcp, DHO_SERVERID);
+ log_dhcp(LOG_INFO, "acknowledged", dhcp);
+ }
+ free(state->offer);
+ state->offer = dhcp;
+ *dhcpp = NULL;
+ break;
+ default:
+ logger(LOG_ERR, "wrong state %d", state->state);
+ }
+
+ do_socket(state, SOCKET_CLOSED);
+ r = bind_dhcp(state, options);
+ if (!(state->options & DHCPCD_ARP)) {
+ if (!(state->options & DHCPCD_INFORM))
+ logger(LOG_DEBUG, "renew in %ld seconds",
+ (long int)state->stop.tv_sec);
+ return r;
+ }
+ state->state = STATE_ANNOUNCING;
+ if (state->options & DHCPCD_FORKED)
+ return r;
+ return 1;
+}
+
+static int
+handle_dhcp_packet(struct if_state *state, const struct options *options)
+{
+ uint8_t *packet;
+ struct interface *iface = state->interface;
+ struct dhcp_message *dhcp = NULL;
+ const uint8_t *pp;
+ uint8_t *p;
+ ssize_t bytes;
+ int retval = -1;
+
+ /* We loop through until our buffer is empty.
+ * The benefit is that if we get >1 DHCP packet in our buffer and
+ * the first one fails for any reason, we can use the next. */
+ packet = xmalloc(udp_dhcp_len);
+ for(;;) {
+ bytes = get_raw_packet(iface, ETHERTYPE_IP,
+ packet, udp_dhcp_len);
+ if (bytes == 0) {
+ retval = 0;
+ break;
+ }
+ if (bytes == -1)
+ break;
+ if (valid_udp_packet(packet) == -1)
+ continue;
+ bytes = get_udp_data(&pp, packet);
+ if ((size_t)bytes > sizeof(*dhcp)) {
+ logger(LOG_ERR, "packet greater than DHCP size");
+ continue;
+ }
+ if (!dhcp)
+ dhcp = xmalloc(sizeof(*dhcp));
+ memcpy(dhcp, pp, bytes);
+ if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
+ logger(LOG_DEBUG, "bogus cookie, ignoring");
+ continue;
+ }
+ /* Ensure it's the right transaction */
+ if (state->xid != dhcp->xid) {
+ logger(LOG_DEBUG,
+ "ignoring packet with xid 0x%x as"
+ " it's not ours (0x%x)",
+ dhcp->xid, state->xid);
+ continue;
+ }
+ /* Ensure packet is for us */
+ if (iface->hwlen <= sizeof(dhcp->chaddr) &&
+ memcmp(dhcp->chaddr, iface->hwaddr, iface->hwlen))
+ {
+ logger(LOG_DEBUG, "xid 0x%x is not for our hwaddr %s",
+ dhcp->xid,
+ hwaddr_ntoa(dhcp->chaddr, sizeof(dhcp->chaddr)));
+ continue;
+ }
+ /* We should ensure that the packet is terminated correctly
+ * if we have space for the terminator */
+ if ((size_t)bytes < sizeof(struct dhcp_message)) {
+ p = (uint8_t *)dhcp + bytes - 1;
+ while (p > dhcp->options && *p == DHO_PAD)
+ p--;
+ if (*p != DHO_END)
+ *++p = DHO_END;
+ }
+ retval = handle_dhcp(state, &dhcp, options);
+ if (retval == 0 && state->options & DHCPCD_TEST)
+ state->options |= DHCPCD_FORKED;
+ break;
+ }
+
+ free(packet);
+ free(dhcp);
+ return retval;
+}
+
+static int
+handle_arp_packet(struct if_state *state)
+{
+ struct arphdr reply;
+ uint32_t reply_s;
+ uint32_t reply_t;
+ uint8_t arp_reply[sizeof(reply) + 2 * sizeof(reply_s) + 2 * HWADDR_LEN];
+ uint8_t *hw_s, *hw_t;
+ ssize_t bytes;
+ struct interface *iface = state->interface;
+
+ state->fail.s_addr = 0;
+ for(;;) {
+ bytes = get_raw_packet(iface, ETHERTYPE_ARP,
+ arp_reply, sizeof(arp_reply));
+ if (bytes == 0 || bytes == -1)
+ return (int)bytes;
+ /* We must have a full ARP header */
+ if ((size_t)bytes < sizeof(reply))
+ continue;
+ memcpy(&reply, arp_reply, sizeof(reply));
+ /* Protocol must be IP. */
+ if (reply.ar_pro != htons(ETHERTYPE_IP))
+ continue;
+ if (reply.ar_pln != sizeof(reply_s))
+ continue;
+ /* Only these types are recognised */
+ if (reply.ar_op != htons(ARPOP_REPLY) &&
+ reply.ar_op != htons(ARPOP_REQUEST))
+ continue;
+
+ /* Get pointers to the hardware addreses */
+ hw_s = arp_reply + sizeof(reply);
+ hw_t = hw_s + reply.ar_hln + reply.ar_pln;
+ /* Ensure we got all the data */
+ if ((hw_t + reply.ar_hln + reply.ar_pln) - arp_reply > bytes)
+ continue;
+ /* Ignore messages from ourself */
+ if (reply.ar_hln == iface->hwlen &&
+ memcmp(hw_s, iface->hwaddr, iface->hwlen) == 0)
+ continue;
+ /* Copy out the IP addresses */
+ memcpy(&reply_s, hw_s + reply.ar_hln, reply.ar_pln);
+ memcpy(&reply_t, hw_t + reply.ar_hln, reply.ar_pln);
+
+ /* Check for conflict */
+ if (state->offer &&
+ (reply_s == state->offer->yiaddr ||
+ (reply_s == 0 && reply_t == state->offer->yiaddr)))
+ state->fail.s_addr = state->offer->yiaddr;
+
+ /* Handle IPv4LL conflicts */
+ if (IN_LINKLOCAL(htonl(iface->addr.s_addr)) &&
+ (reply_s == iface->addr.s_addr ||
+ (reply_s == 0 && reply_t == iface->addr.s_addr)))
+ state->fail.s_addr = iface->addr.s_addr;
+
+ if (state->fail.s_addr) {
+ logger(LOG_ERR, "hardware address %s claims %s",
+ hwaddr_ntoa((unsigned char *)hw_s,
+ (size_t)reply.ar_hln),
+ inet_ntoa(state->fail));
+ errno = EEXIST;
+ return -1;
+ }
+ }
+}
+
+static int
+handle_arp_fail(struct if_state *state, const struct options *options)
+{
+ time_t up;
+ int cookie = state->offer->cookie;
+
+ if (!IN_LINKLOCAL(htonl(state->fail.s_addr))) {
+ state->state = STATE_INIT;
+ free(state->offer);
+ state->offer = NULL;
+ state->lease.addr.s_addr = 0;
+ if (!cookie)
+ return 1;
+ state->timeout.tv_sec = DHCP_ARP_FAIL;
+ state->timeout.tv_usec = 0;
+ do_socket(state, SOCKET_OPEN);
+ send_message(state, DHCP_DECLINE, options);
+ do_socket(state, SOCKET_CLOSED);
+ return 0;
+ }
+
+ if (state->fail.s_addr == state->interface->addr.s_addr) {
+ if (state->state == STATE_PROBING)
+ /* This should only happen when SIGALRM or
+ * link when down/up and we have a conflict. */
+ drop_config(state, "EXPIRE", options);
+ else {
+ up = uptime();
+ if (state->defend + DEFEND_INTERVAL > up) {
+ drop_config(state, "EXPIRE", options);
+ state->conflicts = -1;
+ /* drop through to set conflicts to 0 */
+ } else {
+ state->defend = up;
+ return 0;
+ }
+ }
+ }
+ do_socket(state, SOCKET_CLOSED);
+ state->conflicts++;
+ timerclear(&state->stop);
+ if (state->conflicts > MAX_CONFLICTS) {
+ logger(LOG_ERR, "failed to obtain an IPv4LL address");
+ state->state = STATE_INIT;
+ timerclear(&state->timeout);
+ if (!(state->options & DHCPCD_DAEMONISED) &&
+ (state->options & DHCPCD_DAEMONISE))
+ return -1;
+ return 1;
+ }
+ state->state = STATE_INIT_IPV4LL;
+ state->timeout.tv_sec = PROBE_WAIT;
+ state->timeout.tv_usec = 0;
+ return 0;
+}
+
+static int
+handle_link(struct if_state *state)
+{
+ int retval;
+
+ retval = link_changed(state->interface);
+ if (retval == -1) {
+ logger(LOG_ERR, "link_changed: %s", strerror(errno));
+ return -1;
+ }
+ if (retval == 0)
+ return 0;
+
+ timerclear(&state->timeout);
+ switch (carrier_status(state->interface->name)) {
+ case -1:
+ logger(LOG_ERR, "carrier_status: %s", strerror(errno));
+ return -1;
+ case 0:
+ if (state->carrier != LINK_DOWN) {
+ logger(LOG_INFO, "carrier lost");
+ state->carrier = LINK_DOWN;
+ do_socket(state, SOCKET_CLOSED);
+ if (state->state != STATE_BOUND)
+ timerclear(&state->stop);
+ }
+ break;
+ default:
+ if (state->carrier != LINK_UP) {
+ logger(LOG_INFO, "carrier acquired");
+ state->state = STATE_RENEW_REQUESTED;
+ state->carrier = LINK_UP;
+ timerclear(&state->stop);
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+int
+dhcp_run(const struct options *options, int *pid_fd)
+{
+ struct interface *iface;
+ struct if_state *state = NULL;
+ int fd = -1, r = 0, sig;
+
+ iface = read_interface(options->interface, options->metric);
+ if (!iface) {
+ logger(LOG_ERR, "read_interface: %s", strerror(errno));
+ goto eexit;
+ }
+ logger(LOG_DEBUG, "hardware address = %s",
+ hwaddr_ntoa(iface->hwaddr, iface->hwlen));
+ state = xzalloc(sizeof(*state));
+ state->pid_fd = pid_fd;
+ state->interface = iface;
+ if (!(options->options & DHCPCD_TEST))
+ run_script(options, iface->name, "PREINIT", NULL, NULL);
+
+ if (client_setup(state, options) == -1)
+ goto eexit;
+ if (signal_init() == -1)
+ goto eexit;
+ if (signal_setup() == -1)
+ goto eexit;
+ state->signal_fd = signal_fd();
+
+ if (state->options & DHCPCD_BACKGROUND &&
+ !(state->options & DHCPCD_DAEMONISED))
+ if (daemonise(state, options) == -1)
+ goto eexit;
+
+ if (state->carrier == LINK_DOWN)
+ logger(LOG_INFO, "waiting for carrier");
+
+ for (;;) {
+ if (r == 0)
+ r = handle_timeout(state, options);
+ else if (r > 0) {
+ if (fd == state->signal_fd) {
+ if ((sig = signal_read()) != -1)
+ r = handle_signal(sig, state, options);
+ } else if (fd == iface->link_fd)
+ r = handle_link(state);
+ else if (fd == iface->raw_fd)
+ r = handle_dhcp_packet(state, options);
+ else if (fd == iface->arp_fd) {
+ if ((r = handle_arp_packet(state)) == -1)
+ r = handle_arp_fail(state, options);
+ } else
+ r = 0;
+ }
+ if (r == -1)
+ break;
+ if (r == 0) {
+ fd = -1;
+ r = wait_for_fd(state, &fd);
+ if (r == -1 && errno == EINTR) {
+ r = 1;
+ fd = state->signal_fd;
+ }
+ } else
+ r = 0;
+ }
+
+eexit:
+ if (iface) {
+ do_socket(state, SOCKET_CLOSED);
+ if (iface->link_fd != -1)
+ close(iface->link_fd);
+ free_routes(iface->routes);
+ free(iface->clientid);
+ free(iface->buffer);
+ free(iface);
+ }
+
+ if (state) {
+ if (state->options & DHCPCD_FORKED)
+ r = 0;
+ if (state->options & DHCPCD_DAEMONISED)
+ unlink(options->pidfile);
+ free(state->offer);
+ free(state->new);
+ free(state->old);
+ free(state);
+ }
+
+ return r;
+}
diff --git a/client.h b/client.h
new file mode 100644
index 0000000..35a5e37
--- /dev/null
+++ b/client.h
@@ -0,0 +1,35 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include "dhcpcd.h"
+
+int dhcp_run(const struct options *, int *);
+
+#endif
diff --git a/common.c b/common.c
new file mode 100644
index 0000000..d90c7d2
--- /dev/null
+++ b/common.c
@@ -0,0 +1,327 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#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>
+#ifdef BSD
+# include <paths.h>
+#endif
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "logger.h"
+
+#ifndef _PATH_DEVNULL
+# define _PATH_DEVNULL "/dev/null"
+#endif
+
+int clock_monotonic = 0;
+
+/* Handy routine to read very long lines in text files.
+ * This means we read the whole line and avoid any nasty buffer overflows. */
+ssize_t
+get_line(char **line, size_t *len, FILE *fp)
+{
+ char *p;
+ size_t last = 0;
+
+ while(!feof(fp)) {
+ if (*line == NULL || last != 0) {
+ *len += BUFSIZ;
+ *line = xrealloc(*line, *len);
+ }
+ p = *line + last;
+ memset(p, 0, BUFSIZ);
+ fgets(p, BUFSIZ, fp);
+ last += strlen(p);
+ if (last && (*line)[last - 1] == '\n') {
+ (*line)[last - 1] = '\0';
+ break;
+ }
+ }
+ return last;
+}
+
+/* Simple hack to return a random number without arc4random */
+#ifndef HAVE_ARC4RANDOM
+uint32_t arc4random(void)
+{
+ int fd;
+ static unsigned long seed = 0;
+
+ if (!seed) {
+ fd = open("/dev/urandom", 0);
+ if (fd == -1 || read(fd, &seed, sizeof(seed)) == -1)
+ seed = time(0);
+ if (fd >= 0)
+ close(fd);
+ srandom(seed);
+ }
+
+ return (uint32_t)random();
+}
+#endif
+
+/* strlcpy is nice, shame glibc does not define it */
+#if HAVE_STRLCPY
+#else
+size_t
+strlcpy(char *dst, const char *src, size_t size)
+{
+ 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;
+}
+#endif
+
+#if HAVE_CLOSEFROM
+#else
+int
+closefrom(int fd)
+{
+ int max = getdtablesize();
+ int i;
+ int r = 0;
+
+ for (i = fd; i < max; i++)
+ r += close(i);
+ return r;
+}
+#endif
+
+/* Close our fd's */
+int
+close_fds(void)
+{
+ int fd;
+
+ if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1)
+ return -1;
+
+ dup2(fd, fileno(stdin));
+ dup2(fd, fileno(stdout));
+ dup2(fd, fileno(stderr));
+ if (fd > 2)
+ close(fd);
+ return 0;
+}
+
+int
+set_cloexec(int fd)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFD, 0)) == -1
+ || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ {
+ logger(LOG_ERR, "fcntl: %s", strerror(errno));
+ 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)
+ {
+ logger(LOG_ERR, "fcntl: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+/* 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 == 0) {
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+ posix_clock = CLOCK_MONOTONIC;
+ clock_monotonic = 1;
+ }
+ 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 == 0) {
+ if (mach_timebase_info(&info) == KERN_SUCCESS) {
+ factor = (double)info.numer / (double)info.denom;
+ clock_monotonic = 1;
+ }
+ 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) {
+ logger(LOG_WARNING, NO_MONOTONIC);
+ posix_clock_set = 1;
+ }
+ return gettimeofday(tp, NULL);
+}
+
+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;
+}
+
+void *
+xmalloc(size_t s)
+{
+ void *value = malloc(s);
+
+ if (value)
+ return value;
+ logger(LOG_ERR, "memory exhausted");
+ exit (EXIT_FAILURE);
+ /* NOTREACHED */
+}
+
+void *
+xzalloc(size_t s)
+{
+ void *value = xmalloc(s);
+
+ memset(value, 0, s);
+ return value;
+}
+
+void *
+xrealloc(void *ptr, size_t s)
+{
+ void *value = realloc(ptr, s);
+
+ if (value)
+ return (value);
+ logger(LOG_ERR, "memory exhausted");
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *value;
+
+ if (!str)
+ return NULL;
+
+ if ((value = strdup(str)))
+ return value;
+
+ logger(LOG_ERR, "memory exhausted");
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..2522663
--- /dev/null
+++ b/common.h
@@ -0,0 +1,87 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+/* string.h pulls in features.h so the below define checks work */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+
+#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
+# define _unused __attribute__((__unused__))
+#else
+# define _unused
+#endif
+
+#ifndef HAVE_ARC4RANDOM
+# ifdef __GLIBC__
+uint32_t arc4random(void);
+#else
+# define HAVE_ARC4RANDOM
+# endif
+#endif
+
+#ifndef HAVE_STRLCPY
+# define HAVE_STRLCPY 1
+#endif
+/* Only GLIBC doesn't support strlcpy */
+#ifdef __GLIBC__
+# if !defined(__UCLIBC__) && !defined (__dietlibc__)
+# undef HAVE_STRLCPY
+size_t strlcpy(char *, const char *, size_t);
+# endif
+#endif
+
+#ifndef HAVE_CLOSEFROM
+# if defined(__NetBSD__) || defined(__OpenBSD__)
+# define HAVE_CLOSEFROM 1
+# endif
+#endif
+#ifndef HAVE_CLOSEFROM
+int closefrom(int);
+#endif
+
+int close_fds(void);
+int set_cloexec(int);
+int set_nonblock(int);
+ssize_t get_line(char **, size_t *, FILE *);
+extern int clock_monotonic;
+int get_monotonic(struct timeval *);
+time_t uptime(void);
+int writepid(int, pid_t);
+void *xrealloc(void *, size_t);
+void *xmalloc(size_t);
+void *xzalloc(size_t);
+char *xstrdup(const char *);
+
+#endif
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..cff7079
--- /dev/null
+++ b/config.h
@@ -0,0 +1,75 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define PACKAGE "dhcpcd"
+#define VERSION "4.0.1"
+
+/*
+ * By default we don't add a local link route if we got a routeable address.
+ * This is because dhcpcd can't really decide which interface should allow
+ * link local routing when we have more than one interface.
+ * Ideally the host network scripts should add the link local route for us.
+ * If not, you can define this to get dhcpcd to always add the link local route.
+ */
+// #define IPV4LL_ALWAYSROUTE
+
+/* Some systems do not have a working fork. */
+/* #define THERE_IS_NO_FORK */
+
+/* Paths to things */
+#ifndef SYSCONFDIR
+# define SYSCONFDIR "/system/etc/dhcpcd"
+#endif
+#ifndef LIBEXECDIR
+# define LIBEXECDIR "/system/etc/dhcpcd"
+#endif
+#ifndef RUNDIR
+# define RUNDIR "/data/misc/dhcp"
+#endif
+#ifndef DBDIR
+# define DBDIR "/data/misc/dhcp"
+#endif
+
+#ifndef CONFIG
+# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"
+#endif
+#ifndef SCRIPT
+# define SCRIPT LIBEXECDIR "/" PACKAGE "-run-hooks"
+#endif
+#ifndef DUID
+# define DUID SYSCONFDIR "/" PACKAGE ".duid"
+#endif
+#ifndef LEASEFILE
+# define LEASEFILE DBDIR "/" PACKAGE "-%s.lease"
+#endif
+#ifndef PIDFILE
+# define PIDFILE RUNDIR "/" PACKAGE "-%s.pid"
+#endif
+
+#endif
diff --git a/configure.c b/configure.c
new file mode 100644
index 0000000..dad3bce
--- /dev/null
+++ b/configure.c
@@ -0,0 +1,420 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "configure.h"
+#include "dhcp.h"
+#include "dhcpcd.h"
+#include "logger.h"
+#include "net.h"
+#include "signals.h"
+
+#define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
+
+
+static int
+exec_script(char *const *argv, char *const *env)
+{
+ pid_t pid;
+ sigset_t full;
+ sigset_t old;
+
+ /* OK, we need to block signals */
+ sigfillset(&full);
+ sigprocmask(SIG_SETMASK, &full, &old);
+ signal_reset();
+
+ switch (pid = vfork()) {
+ case -1:
+ logger(LOG_ERR, "vfork: %s", strerror(errno));
+ break;
+ case 0:
+ sigprocmask(SIG_SETMASK, &old, NULL);
+ execve(argv[0], argv, env);
+ logger(LOG_ERR, "%s: %s", argv[0], strerror(errno));
+ _exit(127);
+ /* NOTREACHED */
+ }
+
+ /* Restore our signals */
+ signal_setup();
+ sigprocmask(SIG_SETMASK, &old, NULL);
+ return pid;
+}
+
+int
+run_script(const struct options *options, const char *iface,
+ const char *reason,
+ const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
+{
+ char *const argv[2] = { UNCONST(options->script), NULL };
+ char **env = NULL, **ep;
+ char *path;
+ ssize_t e, elen;
+ pid_t pid;
+ int status = 0;
+
+ logger(LOG_DEBUG, "executing `%s', reason %s", options->script, reason);
+
+ /* Make our env */
+ elen = 5;
+ env = xmalloc(sizeof(char *) * (elen + 1));
+ path = getenv("PATH");
+ if (path) {
+ e = strlen("PATH") + strlen(path) + 2;
+ env[0] = xmalloc(e);
+ snprintf(env[0], e, "PATH=%s", path);
+ } else
+ env[0] = xstrdup(DEFAULT_PATH);
+ e = strlen("interface") + strlen(iface) + 2;
+ env[1] = xmalloc(e);
+ snprintf(env[1], e, "interface=%s", iface);
+ e = strlen("reason") + strlen(reason) + 2;
+ env[2] = xmalloc(e);
+ snprintf(env[2], e, "reason=%s", reason);
+ e = 20;
+ env[3] = xmalloc(e);
+ snprintf(env[3], e, "pid=%d", getpid());
+ env[4] = xmalloc(e);
+ snprintf(env[4], e, "metric=%d", options->metric);
+ if (dhcpo) {
+ e = configure_env(NULL, NULL, dhcpo, options);
+ if (e > 0) {
+ env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+ elen += configure_env(env + elen, "old", dhcpo, options);
+ }
+ }
+ if (dhcpn) {
+ e = configure_env(NULL, NULL, dhcpn, options);
+ if (e > 0) {
+ env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+ elen += configure_env(env + elen, "new", dhcpn, options);
+ }
+ }
+ /* Add our base environment */
+ if (options->environ) {
+ e = 0;
+ while (options->environ[e++])
+ ;
+ env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+ e = 0;
+ while (options->environ[e]) {
+ env[elen + e] = xstrdup(options->environ[e]);
+ e++;
+ }
+ elen += e;
+ }
+ env[elen] = '\0';
+
+ pid = exec_script(argv, env);
+ if (pid == -1)
+ status = -1;
+ else if (pid != 0) {
+ /* Wait for the script to finish */
+ while (waitpid(pid, &status, 0) == -1) {
+ if (errno != EINTR) {
+ logger(LOG_ERR, "waitpid: %s", strerror(errno));
+ status = -1;
+ break;
+ }
+ }
+ }
+
+ /* Cleanup */
+ ep = env;
+ while (*ep)
+ free(*ep++);
+ free(env);
+ return status;
+}
+
+static struct rt *
+reverse_routes(struct rt *routes)
+{
+ struct rt *rt;
+ struct rt *rtn = NULL;
+
+ while (routes) {
+ rt = routes->next;
+ routes->next = rtn;
+ rtn = routes;
+ routes = rt;
+ }
+ return rtn;
+}
+
+static int
+delete_route(const char *iface, struct rt *rt, int metric)
+{
+ char *addr;
+ int retval;
+
+ addr = xstrdup(inet_ntoa(rt->dest));
+ logger(LOG_DEBUG, "deleting route %s/%d via %s",
+ addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
+ free(addr);
+ retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric);
+ if (retval != 0 && errno != ENOENT && errno != ESRCH)
+ logger(LOG_ERR," del_route: %s", strerror(errno));
+ return retval;
+}
+
+static int
+delete_routes(struct interface *iface, int metric)
+{
+ struct rt *rt;
+ struct rt *rtn;
+ int retval = 0;
+
+ rt = reverse_routes(iface->routes);
+ while (rt) {
+ rtn = rt->next;
+ retval += delete_route(iface->name, rt, metric);
+ free(rt);
+ rt = rtn;
+ }
+ iface->routes = NULL;
+
+ return retval;
+}
+
+static int
+in_routes(const struct rt *routes, const struct rt *rt)
+{
+ while (routes) {
+ if (routes->dest.s_addr == rt->dest.s_addr &&
+ routes->net.s_addr == rt->net.s_addr &&
+ routes->gate.s_addr == rt->gate.s_addr)
+ return 0;
+ routes = routes->next;
+ }
+ return -1;
+}
+
+static int
+configure_routes(struct interface *iface, const struct dhcp_message *dhcp,
+ const struct options *options)
+{
+ struct rt *rt, *ort;
+ struct rt *rtn = NULL, *nr = NULL;
+ int remember;
+ int retval = 0;
+ char *addr;
+
+ ort = get_option_routes(dhcp);
+
+#ifdef IPV4LL_ALWAYSROUTE
+ if (options->options & DHCPCD_IPV4LL &&
+ IN_PRIVATE(ntohl(dhcp->yiaddr)))
+ {
+ for (rt = ort; rt; rt = rt->next) {
+ /* Check if we have already got a link locale route
+ * dished out by the DHCP server */
+ if (rt->dest.s_addr == htonl(LINKLOCAL_ADDR) &&
+ rt->net.s_addr == htonl(LINKLOCAL_MASK))
+ break;
+ rtn = rt;
+ }
+
+ if (!rt) {
+ rt = xmalloc(sizeof(*rt));
+ rt->dest.s_addr = htonl(LINKLOCAL_ADDR);
+ rt->net.s_addr = htonl(LINKLOCAL_MASK);
+ rt->gate.s_addr = 0;
+ rt->next = NULL;
+ if (rtn)
+ rtn->next = rt;
+ else
+ ort = rt;
+ }
+ }
+#endif
+
+ /* Now remove old routes we no longer use.
+ * We should do this in reverse order. */
+ iface->routes = reverse_routes(iface->routes);
+ for (rt = iface->routes; rt; rt = rt->next)
+ if (in_routes(ort, rt) != 0)
+ delete_route(iface->name, rt, options->metric);
+
+ for (rt = ort; rt; rt = rt->next) {
+ /* Don't set default routes if not asked to */
+ if (rt->dest.s_addr == 0 &&
+ rt->net.s_addr == 0 &&
+ !(options->options & DHCPCD_GATEWAY))
+ continue;
+
+ addr = xstrdup(inet_ntoa(rt->dest));
+ logger(LOG_DEBUG, "adding route to %s/%d via %s",
+ addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
+ free(addr);
+ remember = add_route(iface->name, &rt->dest,
+ &rt->net, &rt->gate,
+ options->metric);
+ retval += remember;
+
+ /* If we failed to add the route, we may have already added it
+ ourselves. If so, remember it again. */
+ if (remember < 0) {
+ if (errno != EEXIST)
+ logger(LOG_ERR, "add_route: %s",
+ strerror(errno));
+ if (in_routes(iface->routes, rt) == 0)
+ remember = 1;
+ }
+
+ /* This login is split from above due to the #ifdef below */
+ if (remember >= 0) {
+ if (nr) {
+ rtn->next = xmalloc(sizeof(*rtn));
+ rtn = rtn->next;
+ } else {
+ nr = rtn = xmalloc(sizeof(*rtn));
+ }
+ rtn->dest.s_addr = rt->dest.s_addr;
+ rtn->net.s_addr = rt->net.s_addr;
+ rtn->gate.s_addr = rt->gate.s_addr;
+ rtn->next = NULL;
+ }
+ }
+ free_routes(ort);
+ free_routes(iface->routes);
+ iface->routes = nr;
+ return retval;
+}
+
+static int
+delete_address(struct interface *iface)
+{
+ int retval;
+ logger(LOG_DEBUG, "deleting IP address %s/%d",
+ inet_ntoa(iface->addr),
+ inet_ntocidr(iface->net));
+ retval = del_address(iface->name, &iface->addr, &iface->net);
+ if (retval == -1 && errno != EADDRNOTAVAIL)
+ logger(LOG_ERR, "del_address: %s", strerror(errno));
+ iface->addr.s_addr = 0;
+ iface->net.s_addr = 0;
+ return retval;
+}
+
+int
+configure(struct interface *iface, const char *reason,
+ const struct dhcp_message *dhcp, const struct dhcp_message *old,
+ const struct dhcp_lease *lease, const struct options *options,
+ int up)
+{
+ struct in_addr addr;
+ struct in_addr net;
+ struct in_addr brd;
+#ifdef __linux__
+ struct in_addr dest;
+ struct in_addr gate;
+#endif
+
+ /* Grab our IP config */
+ if (dhcp == NULL)
+ up = 0;
+ else {
+ addr.s_addr = dhcp->yiaddr;
+ if (addr.s_addr == 0)
+ addr.s_addr = lease->addr.s_addr;
+ /* Ensure we have all the needed values */
+ if (get_option_addr(&net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
+ net.s_addr = get_netmask(addr.s_addr);
+ if (get_option_addr(&brd.s_addr, dhcp, DHO_BROADCAST) == -1)
+ brd.s_addr = addr.s_addr | ~net.s_addr;
+ }
+
+ /* If we aren't up, then reset the interface as much as we can */
+ if (!up) {
+ /* Only reset things if we had set them before */
+ if (iface->addr.s_addr != 0) {
+ delete_routes(iface, options->metric);
+ delete_address(iface);
+ }
+
+ run_script(options, iface->name, reason, NULL, old);
+ return 0;
+ }
+
+ /* This also changes netmask */
+ if (!(options->options & DHCPCD_INFORM) ||
+ !has_address(iface->name, &addr, &net)) {
+ logger(LOG_DEBUG, "adding IP address %s/%d",
+ inet_ntoa(addr), inet_ntocidr(net));
+ if (add_address(iface->name, &addr, &net, &brd) == -1 &&
+ errno != EEXIST)
+ {
+ logger(LOG_ERR, "add_address: %s", strerror(errno));
+ return -1;
+ }
+ }
+
+ /* Now delete the old address if different */
+ if (iface->addr.s_addr != addr.s_addr &&
+ iface->addr.s_addr != 0)
+ delete_address(iface);
+
+#ifdef __linux__
+ /* On linux, we need to change the subnet route to have our metric. */
+ if (iface->addr.s_addr != lease->addr.s_addr &&
+ options->metric > 0 && net.s_addr != INADDR_BROADCAST)
+ {
+ dest.s_addr = addr.s_addr & net.s_addr;
+ gate.s_addr = 0;
+ add_route(iface->name, &dest, &net, &gate, options->metric);
+ del_route(iface->name, &dest, &net, &gate, 0);
+ }
+#endif
+
+ configure_routes(iface, dhcp, options);
+ up = (iface->addr.s_addr != addr.s_addr ||
+ iface->net.s_addr != net.s_addr);
+ iface->addr.s_addr = addr.s_addr;
+ iface->net.s_addr = net.s_addr;
+
+ if (!lease->frominfo)
+ if (write_lease(iface, dhcp) == -1)
+ logger(LOG_ERR, "write_lease: %s", strerror(errno));
+
+ run_script(options, iface->name, reason, dhcp, old);
+ return 0;
+}
diff --git a/configure.h b/configure.h
new file mode 100644
index 0000000..fe065db
--- /dev/null
+++ b/configure.h
@@ -0,0 +1,41 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DHCPCONFIG_H
+#define DHCPCONFIG_H
+
+#include "dhcpcd.h"
+#include "dhcp.h"
+#include "net.h"
+
+int run_script(const struct options *, const char *, const char *,
+ const struct dhcp_message *, const struct dhcp_message *);
+int configure(struct interface *, const char *,
+ const struct dhcp_message *, const struct dhcp_message *,
+ const struct dhcp_lease *, const struct options *, int);
+
+#endif
diff --git a/dhcp.c b/dhcp.c
new file mode 100644
index 0000000..2469429
--- /dev/null
+++ b/dhcp.c
@@ -0,0 +1,1251 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+
+#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 IPV4 (1 << 6)
+#define STRING (1 << 7)
+#define PAIR (1 << 8)
+#define ARRAY (1 << 9)
+#define RFC3361 (1 << 10)
+#define RFC3397 (1 << 11)
+#define RFC3442 (1 << 12)
+
+#define IPV4R IPV4 | REQUEST
+
+/* Our aggregate option buffer.
+ * We ONLY use this when options are split, which for most purposes is
+ * practically never. See RFC3396 for details. */
+static uint8_t *dhcp_opt_buffer = NULL;
+
+struct dhcp_opt {
+ uint8_t option;
+ int type;
+ const char *var;
+};
+
+static const struct dhcp_opt const dhcp_opts[] = {
+ { 1, IPV4 | REQUEST, "subnet_mask" },
+ { 2, UINT32, "time_offset" },
+ { 3, IPV4 | ARRAY | REQUEST, "routers" },
+ { 4, IPV4 | ARRAY, "time_servers" },
+ { 5, IPV4 | ARRAY, "ien116_name_servers" },
+ { 6, IPV4 | ARRAY, "domain_name_servers" },
+ { 7, IPV4 | ARRAY, "log_servers" },
+ { 8, IPV4 | ARRAY, "cookie_servers" },
+ { 9, IPV4 | ARRAY, "lpr_servers" },
+ { 10, IPV4 | ARRAY, "impress_servers" },
+ { 11, IPV4 | ARRAY, "resource_location_servers" },
+ { 12, STRING, "host_name" },
+ { 13, UINT16, "boot_size" },
+ { 14, STRING, "merit_dump" },
+ { 15, STRING, "domain_name" },
+ { 16, IPV4, "swap_server" },
+ { 17, STRING, "root_path" },
+ { 18, STRING, "extensions_path" },
+ { 19, UINT8, "ip_forwarding" },
+ { 20, UINT8, "non_local_source_routing" },
+ { 21, IPV4 | ARRAY, "policy_filter" },
+ { 22, SINT16, "max_dgram_reassembly" },
+ { 23, UINT16, "default_ip_ttl" },
+ { 24, UINT32, "path_mtu_aging_timeout" },
+ { 25, UINT16 | ARRAY, "path_mtu_plateau_table" },
+ { 26, UINT16, "interface_mtu" },
+ { 27, UINT8, "all_subnets_local" },
+ { 28, IPV4 | REQUEST, "broadcast_address" },
+ { 29, UINT8, "perform_mask_discovery" },
+ { 30, UINT8, "mask_supplier" },
+ { 31, UINT8, "router_discovery" },
+ { 32, IPV4, "router_solicitation_address" },
+ { 33, IPV4 | ARRAY | REQUEST, "static_routes" },
+ { 34, UINT8, "trailer_encapsulation" },
+ { 35, UINT32, "arp_cache_timeout" },
+ { 36, UINT16, "ieee802_3_encapsulation" },
+ { 37, UINT8, "default_tcp_ttl" },
+ { 38, UINT32, "tcp_keepalive_interval" },
+ { 39, UINT8, "tcp_keepalive_garbage" },
+ { 40, STRING, "nis_domain" },
+ { 41, IPV4 | ARRAY, "nis_servers" },
+ { 42, IPV4 | ARRAY, "ntp_servers" },
+ { 43, STRING, "vendor_encapsulated_options" },
+ { 44, IPV4 | ARRAY, "netbios_name_servers" },
+ { 45, IPV4, "netbios_dd_server" },
+ { 46, UINT8, "netbios_node_type" },
+ { 47, STRING, "netbios_scope" },
+ { 48, IPV4 | ARRAY, "font_servers" },
+ { 49, IPV4 | ARRAY, "x_display_manager" },
+ { 50, IPV4, "dhcp_requested_address" },
+ { 51, UINT32 | REQUEST, "dhcp_lease_time" },
+ { 52, UINT8, "dhcp_option_overload" },
+ { 53, UINT8, "dhcp_message_type" },
+ { 54, IPV4, "dhcp_server_identifier" },
+ { 55, UINT8 | ARRAY, "dhcp_parameter_request_list" },
+ { 56, STRING, "dhcp_message" },
+ { 57, UINT16, "dhcp_max_message_size" },
+ { 58, UINT32 | REQUEST, "dhcp_renewal_time" },
+ { 59, UINT32 | REQUEST, "dhcp_rebinding_time" },
+ { 64, STRING, "nisplus_domain" },
+ { 65, IPV4 | ARRAY, "nisplus_servers" },
+ { 66, STRING, "tftp_server_name" },
+ { 67, STRING, "bootfile_name" },
+ { 68, IPV4 | ARRAY, "mobile_ip_home_agent" },
+ { 69, IPV4 | ARRAY, "smtp_server" },
+ { 70, IPV4 | ARRAY, "pop_server" },
+ { 71, IPV4 | ARRAY, "nntp_server" },
+ { 72, IPV4 | ARRAY, "www_server" },
+ { 73, IPV4 | ARRAY, "finger_server" },
+ { 74, IPV4 | ARRAY, "irc_server" },
+ { 75, IPV4 | ARRAY, "streettalk_server" },
+ { 76, IPV4 | ARRAY, "streettalk_directory_assistance_server" },
+ { 77, STRING, "user_class" },
+ { 81, STRING | RFC3397, "fqdn_name" },
+ { 85, IPV4 | ARRAY, "nds_servers" },
+ { 86, STRING, "nds_tree_name" },
+ { 87, STRING, "nds_context" },
+ { 88, STRING | RFC3397, "bcms_controller_names" },
+ { 89, IPV4 | ARRAY, "bcms_controller_address" },
+ { 91, UINT32, "client_last_transaction_time" },
+ { 92, IPV4 | ARRAY, "associated_ip" },
+ { 98, STRING, "uap_servers" },
+ { 112, IPV4 | ARRAY, "netinfo_server_address" },
+ { 113, STRING, "netinfo_server_tag" },
+ { 114, STRING, "default_url" },
+ { 118, IPV4, "subnet_selection" },
+ { 119, STRING | RFC3397, "domain_search" },
+ { 121, RFC3442 | REQUEST, "classless_static_routes" },
+ { 249, RFC3442, "ms_classless_static_routes" },
+ { 0, 0, NULL }
+};
+
+void
+print_options(void)
+{
+ const struct dhcp_opt *opt;
+
+ for (opt = dhcp_opts; opt->option; opt++)
+ if (opt->var)
+ printf("%03d %s\n", opt->option, opt->var);
+}
+
+int make_option_mask(uint8_t *mask, char **opts, int add)
+{
+ char *token, *p = *opts, *t;
+ const struct dhcp_opt *opt;
+ int match, n;
+
+ while ((token = strsep(&p, ", "))) {
+ if (*token == '\0')
+ continue;
+ for (opt = dhcp_opts; opt->option; opt++) {
+ if (!opt->var)
+ continue;
+ 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 == 1)
+ add_option_mask(mask,
+ opt->option);
+ else
+ del_option_mask(mask,
+ opt->option);
+ break;
+ }
+ }
+ if (!opt->option) {
+ *opts = token;
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+valid_length(uint8_t option, int dl, int *type)
+{
+ const struct dhcp_opt *opt;
+ ssize_t sz;
+
+ if (dl == 0)
+ return -1;
+
+ for (opt = dhcp_opts; opt->option; opt++) {
+ if (opt->option != option)
+ continue;
+
+ if (type)
+ *type = opt->type;
+
+ if (opt->type == 0 || opt->type & STRING || opt->type & RFC3442)
+ return 0;
+
+ sz = 0;
+ if (opt->type & UINT32 || opt->type & IPV4)
+ sz = sizeof(uint32_t);
+ if (opt->type & UINT16)
+ sz = sizeof(uint16_t);
+ if (opt->type & UINT8)
+ sz = sizeof(uint8_t);
+ if (opt->type & IPV4 || opt->type & ARRAY)
+ return dl % sz;
+ return (dl == sz ? 0 : -1);
+ }
+
+ /* unknown option, so let it pass */
+ return 0;
+}
+
+static void
+free_option_buffer(void)
+{
+ free(dhcp_opt_buffer);
+}
+
+#define get_option_raw(dhcp, opt) get_option(dhcp, opt, NULL, NULL)
+static const uint8_t *
+get_option(const struct dhcp_message *dhcp, uint8_t opt, int *len, int *type)
+{
+ 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 (!dhcp_opt_buffer) {
+ dhcp_opt_buffer = xmalloc(sizeof(struct dhcp_message));
+ atexit(free_option_buffer);
+ }
+ if (!bp)
+ bp = dhcp_opt_buffer;
+ memcpy(bp, op, ol);
+ bp += ol;
+ }
+ ol = *p;
+ 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 (valid_length(opt, bl, type) == -1) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (len)
+ *len = bl;
+ if (bp) {
+ memcpy(bp, op, ol);
+ return (const uint8_t *)&dhcp_opt_buffer;
+ }
+ if (op)
+ return op;
+ errno = ENOENT;
+ return NULL;
+}
+
+int
+get_option_addr(uint32_t *a, const struct dhcp_message *dhcp, uint8_t option)
+{
+ const uint8_t *p = get_option_raw(dhcp, option);
+
+ if (!p)
+ return -1;
+ memcpy(a, p, sizeof(*a));
+ return 0;
+}
+
+int
+get_option_uint32(uint32_t *i, const struct dhcp_message *dhcp, uint8_t option)
+{
+ uint32_t a;
+
+ if (get_option_addr(&a, dhcp, option) == -1)
+ return -1;
+
+ *i = ntohl(a);
+ return 0;
+}
+
+int
+get_option_uint16(uint16_t *i, const struct dhcp_message *dhcp, uint8_t option)
+{
+ const uint8_t *p = get_option_raw(dhcp, option);
+ uint16_t d;
+
+ if (!p)
+ return -1;
+ memcpy(&d, p, sizeof(d));
+ *i = ntohs(d);
+ return 0;
+}
+
+int
+get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option)
+{
+ const uint8_t *p = get_option_raw(dhcp, option);
+
+ if (!p)
+ return -1;
+ *i = *(p);
+ return 0;
+}
+
+/* Decode an RFC3397 DNS search order option into a space
+ * seperated string. Returns length of string (including
+ * terminating zero) or zero on error. out may be NULL
+ * to just determine output length. */
+static ssize_t
+decode_rfc3397(char *out, ssize_t len, int pl, const uint8_t *p)
+{
+ const uint8_t *r, *q = p;
+ int count = 0, l, hops;
+ uint8_t ltype;
+
+ while (q - p < pl) {
+ r = NULL;
+ hops = 0;
+ /* We check we are inside our length again incase
+ * the data is NOT terminated correctly. */
+ while ((l = *q++) && q - p < pl) {
+ 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 - 1) = ' ';
+ if (r)
+ q = r;
+ }
+
+ /* change last space to zero terminator */
+ if (out)
+ *(out - 1) = 0;
+
+ return count;
+}
+
+static ssize_t
+decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p)
+{
+ const uint8_t *e;
+ ssize_t bytes = 0;
+ ssize_t b;
+ uint8_t cidr;
+ uint8_t ocets;
+ 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, (size_t)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 *
+decode_rfc3442_rt(int dl, const uint8_t *data)
+{
+ const uint8_t *p = data;
+ const uint8_t *e;
+ uint8_t cidr;
+ uint8_t ocets;
+ struct rt *routes = NULL;
+ struct rt *rt = NULL;
+
+ /* Minimum is 5 -first is CIDR and a router length of 4 */
+ if (dl < 5)
+ return NULL;
+
+ e = p + dl;
+ while (p < e) {
+ cidr = *p++;
+ if (cidr > 32) {
+ free_routes(routes);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (rt) {
+ rt->next = xzalloc(sizeof(*rt));
+ rt = rt->next;
+ } else {
+ routes = rt = xzalloc(sizeof(*routes));
+ }
+ rt->next = NULL;
+
+ ocets = (cidr + 7) / 8;
+ /* If we have ocets then we have a destination and netmask */
+ if (ocets > 0) {
+ memcpy(&rt->dest.s_addr, p, (size_t)ocets);
+ memset(&rt->net.s_addr, 255, (size_t)ocets - 1);
+ memset((uint8_t *)&rt->net.s_addr +
+ (ocets - 1),
+ (256 - (1 << (32 - cidr) % 8)), 1);
+ p += ocets;
+ } else {
+ rt->dest.s_addr = 0;
+ rt->net.s_addr = 0;
+ }
+
+ /* Finally, snag the router */
+ memcpy(&rt->gate.s_addr, p, 4);
+ p += 4;
+ }
+ return routes;
+}
+
+static 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 = xmalloc(l);
+ 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 = xmalloc(l);
+ while (l != 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));
+ l -= sizeof(addr.s_addr);
+ }
+ *--p = '\0';
+ break;
+ default:
+ errno = EINVAL;
+ return 0;
+ }
+
+ return sip;
+}
+
+char *
+get_option_string(const struct dhcp_message *dhcp, uint8_t option)
+{
+ int type = 0;
+ int len;
+ const uint8_t *p;
+ char *s;
+
+ p = get_option(dhcp, option, &len, &type);
+ if (!p || *p == '\0')
+ return NULL;
+
+ if (type & RFC3397) {
+ type = decode_rfc3397(NULL, 0, len, p);
+ if (!type) {
+ errno = EINVAL;
+ return NULL;
+ }
+ s = xmalloc(sizeof(char) * type);
+ decode_rfc3397(s, type, len, p);
+ return s;
+ }
+
+ if (type & RFC3361)
+ return decode_rfc3361(len, p);
+
+ s = xmalloc(sizeof(char) * (len + 1));
+ 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 *
+get_option_routes(const struct dhcp_message *dhcp)
+{
+ const uint8_t *p;
+ const uint8_t *e;
+ struct rt *routes = NULL;
+ struct rt *route = NULL;
+ int len;
+
+ /* If we have CSR's then we MUST use these only */
+ p = get_option(dhcp, DHO_CSR, &len, NULL);
+ /* Check for crappy MS option */
+ if (!p)
+ p = get_option(dhcp, DHO_MSCSR, &len, NULL);
+ if (p) {
+ routes = decode_rfc3442_rt(len, p);
+ if (routes)
+ return routes;
+ }
+
+ /* OK, get our static routes first. */
+ p = get_option(dhcp, DHO_STATICROUTE, &len, NULL);
+ if (p) {
+ e = p + len;
+ while (p < e) {
+ if (route) {
+ route->next = xmalloc(sizeof(*route));
+ route = route->next;
+ } else
+ routes = route = xmalloc(sizeof(*routes));
+ route->next = 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);
+ }
+ }
+
+ /* Now grab our routers */
+ p = get_option(dhcp, DHO_ROUTER, &len, NULL);
+ if (p) {
+ e = p + len;
+ while (p < e) {
+ if (route) {
+ route->next = xzalloc(sizeof(*route));
+ route = route->next;
+ } else
+ routes = route = xzalloc(sizeof(*route));
+ memcpy(&route->gate.s_addr, p, 4);
+ p += 4;
+ }
+ }
+
+ return routes;
+}
+
+static size_t
+encode_rfc1035(const char *src, uint8_t *dst, size_t len)
+{
+ const char *c = src;
+ uint8_t *p = dst;
+ uint8_t *lp = p++;
+
+ if (len == 0)
+ return 0;
+ while (c < src + len) {
+ if (*c == '\0')
+ break;
+ if (*c == '.') {
+ /* Skip the trailing . */
+ if (c == src + len - 1)
+ break;
+ *lp = p - lp - 1;
+ if (*lp == '\0')
+ return p - dst;
+ lp = p++;
+ } else
+ *p++ = (uint8_t) *c;
+ c++;
+ }
+ *lp = p - lp - 1;
+ *p++ = '\0';
+ return p - dst;
+}
+
+ssize_t
+make_message(struct dhcp_message **message,
+ const struct interface *iface, const struct dhcp_lease *lease,
+ uint32_t xid, uint8_t type, const struct options *options)
+{
+ struct dhcp_message *dhcp;
+ uint8_t *m, *lp, *p;
+ uint8_t *n_params = NULL;
+ time_t up = uptime() - iface->start_uptime;
+ uint32_t ul;
+ uint16_t sz;
+ const struct dhcp_opt *opt;
+
+ dhcp = xzalloc(sizeof (*dhcp));
+ m = (uint8_t *)dhcp;
+ p = dhcp->options;
+
+ if ((type == DHCP_INFORM ||
+ type == DHCP_RELEASE ||
+ type == DHCP_REQUEST) &&
+ !IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
+ {
+ dhcp->ciaddr = iface->addr.s_addr;
+ /* Just incase we haven't actually configured the address yet */
+ if (type == DHCP_INFORM && iface->addr.s_addr == 0)
+ dhcp->ciaddr = lease->addr.s_addr;
+ /* Zero the address if we're currently on a different subnet */
+ if (type == DHCP_REQUEST &&
+ iface->net.s_addr != lease->net.s_addr)
+ dhcp->ciaddr = 0;
+ }
+
+ dhcp->op = DHCP_BOOTREQUEST;
+ dhcp->hwtype = iface->family;
+ switch (iface->family) {
+ case ARPHRD_ETHER:
+ case ARPHRD_IEEE802:
+ dhcp->hwlen = ETHER_ADDR_LEN;
+ memcpy(&dhcp->chaddr, &iface->hwaddr, ETHER_ADDR_LEN);
+ break;
+ case ARPHRD_IEEE1394:
+ case ARPHRD_INFINIBAND:
+ dhcp->hwlen = 0;
+ if (dhcp->ciaddr == 0)
+ dhcp->flags = htons(BROADCAST_FLAG);
+ break;
+ }
+
+ if (up < 0 || up > (time_t)UINT16_MAX)
+ dhcp->secs = htons((uint16_t)UINT16_MAX);
+ else
+ dhcp->secs = htons(up);
+ dhcp->xid = xid;
+ dhcp->cookie = htonl(MAGIC_COOKIE);
+
+ *p++ = DHO_MESSAGETYPE;
+ *p++ = 1;
+ *p++ = type;
+
+ if (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;
+ }
+ sz = htons(sz);
+ memcpy(p, &sz, 2);
+ p += 2;
+ }
+
+ if (iface->clientid) {
+ *p++ = DHO_CLIENTID;
+ memcpy(p, iface->clientid, iface->clientid[0] + 1);
+ p += iface->clientid[0] + 1;
+ }
+
+ if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
+ if (options->userclass[0]) {
+ *p++ = DHO_USERCLASS;
+ memcpy(p, options->userclass, options->userclass[0] + 1);
+ p += options->userclass[0] + 1;
+ }
+
+ if (options->vendorclassid[0]) {
+ *p++ = DHO_VENDORCLASSID;
+ memcpy(p, options->vendorclassid,
+ options->vendorclassid[0] + 1);
+ p += options->vendorclassid[0] + 1;
+ }
+ }
+
+ if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
+#define PUTADDR(_type, _val) \
+ { \
+ *p++ = _type; \
+ *p++ = 4; \
+ memcpy(p, &_val.s_addr, 4); \
+ p += 4; \
+ }
+ if (lease->addr.s_addr &&
+ lease->addr.s_addr != iface->addr.s_addr &&
+ !IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
+ {
+ PUTADDR(DHO_IPADDRESS, lease->addr);
+ if (lease->server.s_addr)
+ PUTADDR(DHO_SERVERID, lease->server);
+ }
+#undef PUTADDR
+
+ if (options->leasetime != 0) {
+ *p++ = DHO_LEASETIME;
+ *p++ = 4;
+ ul = htonl(options->leasetime);
+ memcpy(p, &ul, 4);
+ p += 4;
+ }
+ }
+
+ if (type == DHCP_DISCOVER ||
+ type == DHCP_INFORM ||
+ type == DHCP_REQUEST)
+ {
+ if (options->hostname[0]) {
+ *p++ = DHO_HOSTNAME;
+ memcpy(p, options->hostname, options->hostname[0] + 1);
+ p += options->hostname[0] + 1;
+ }
+ if (options->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
+ */
+ *p++ = (options->fqdn & 0x09) | 0x04;
+ *p++ = 0; /* from server for PTR RR */
+ *p++ = 0; /* from server for A RR if S=1 */
+ ul = encode_rfc1035(options->hostname + 1, p,
+ options->hostname[0]);
+ *lp += ul;
+ p += ul;
+ }
+
+ /* vendor is already encoded correctly, so just add it */
+ if (options->vendor[0]) {
+ *p++ = DHO_VENDOR;
+ memcpy(p, options->vendor, options->vendor[0] + 1);
+ p += options->vendor[0] + 1;
+ }
+
+ *p++ = DHO_PARAMETERREQUESTLIST;
+ n_params = p;
+ *p++ = 0;
+ for (opt = dhcp_opts; opt->option; opt++) {
+ if (!(opt->type & REQUEST ||
+ has_option_mask(options->requestmask, opt->option)))
+ continue;
+ switch (opt->option) {
+ case DHO_RENEWALTIME: /* FALLTHROUGH */
+ case DHO_REBINDTIME:
+ if (type == DHCP_INFORM)
+ continue;
+ break;
+ }
+ *p++ = opt->option;
+ }
+ *n_params = p - n_params - 1;
+ }
+ *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
+
+ *message = dhcp;
+ return p - m;
+}
+
+ssize_t
+write_lease(const struct interface *iface, 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;
+
+ fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
+#ifdef ANDROID
+ if (fd == -1 && errno == EACCES) {
+ /* the lease file might have been created when dhcpcd was running as root */
+ unlink(iface->leasefile);
+ fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
+ }
+#endif
+ 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(const struct interface *iface)
+{
+ int fd;
+ struct dhcp_message *dhcp;
+ ssize_t bytes;
+
+ fd = open(iface->leasefile, O_RDONLY);
+ if (fd == -1)
+ return NULL;
+ dhcp = xmalloc(sizeof(*dhcp));
+ memset(dhcp, 0, sizeof(*dhcp));
+ bytes = read(fd, dhcp, sizeof(*dhcp));
+ close(fd);
+ if (bytes < 0) {
+ free(dhcp);
+ dhcp = NULL;
+ }
+ return dhcp;
+}
+
+static ssize_t
+print_string(char *s, ssize_t len, int dl, const uint8_t *data)
+{
+ uint8_t c;
+ const uint8_t *e;
+ ssize_t bytes = 0;
+ ssize_t r;
+
+ e = data + dl;
+ while (data < e) {
+ c = *data++;
+ 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 */
+ 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;
+}
+
+static ssize_t
+print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
+{
+ 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 = xmalloc(l);
+ decode_rfc3397(tmp, l, dl, data);
+ l = print_string(s, len, l - 1, (uint8_t *)tmp);
+ free(tmp);
+ return l;
+ }
+
+ if (type & RFC3442)
+ return decode_rfc3442(s, len, dl, data);
+
+ if (type & STRING) {
+ /* Some DHCP servers return NULL strings */
+ if (*data == '\0')
+ return 0;
+ return print_string(s, len, dl, data);
+ }
+
+ if (!s) {
+ if (type & UINT8)
+ l = 3;
+ else if (type & UINT16)
+ l = 5;
+ else if (type & SINT16)
+ l = 6;
+ else if (type & UINT32)
+ l = 10;
+ else if (type & SINT32)
+ l = 11;
+ else if (type & IPV4)
+ l = 16;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+ return (l + 1) * dl;
+ }
+
+ t = data;
+ e = data + dl;
+ while (data < e) {
+ if (data != t) {
+ *s++ = ' ';
+ bytes++;
+ len--;
+ }
+ if (type & UINT8) {
+ l = snprintf(s, len, "%d", *data);
+ data++;
+ } else if (type & UINT16) {
+ memcpy(&u16, data, sizeof(u16));
+ u16 = ntohs(u16);
+ l = snprintf(s, len, "%d", 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, "%d", 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 & IPV4) {
+ memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
+ l = snprintf(s, len, "%s", inet_ntoa(addr));
+ data += sizeof(addr.s_addr);
+ } else
+ l = 0;
+ len -= l;
+ bytes += l;
+ s += l;
+ }
+
+ return bytes;
+}
+
+static void
+setvar(char ***e, const char *prefix, const char *var, const char *value)
+{
+ size_t len = strlen(prefix) + strlen(var) + strlen(value) + 4;
+
+ **e = xmalloc(len);
+ snprintf(**e, len, "%s_%s=%s", prefix, var, value);
+ (*e)++;
+}
+
+ssize_t
+configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
+ const struct options *options)
+{
+ unsigned int i;
+ const uint8_t *p;
+ int pl;
+ struct in_addr addr;
+ struct in_addr net;
+ struct in_addr brd;
+ char *val, *v;
+ const struct dhcp_opt *opt;
+ ssize_t len, e = 0;
+ char **ep;
+ char cidr[4];
+ uint8_t overl = 0;
+
+ get_option_uint8(&overl, dhcp, DHO_OPTIONSOVERLOADED);
+
+ if (!env) {
+ for (opt = dhcp_opts; opt->option; opt++) {
+ if (!opt->var)
+ continue;
+ if (has_option_mask(options->nomask, opt->option))
+ continue;
+ if (get_option_raw(dhcp, opt->option))
+ e++;
+ }
+ if (dhcp->yiaddr)
+ e += 5;
+ if (*dhcp->bootfile && !(overl & 1))
+ e++;
+ if (*dhcp->servername && !(overl & 2))
+ e++;
+ return e;
+ }
+
+ ep = env;
+ if (dhcp->yiaddr) {
+ /* Set some useful variables that we derive from the DHCP
+ * message but are not necessarily in the options */
+ addr.s_addr = dhcp->yiaddr;
+ setvar(&ep, prefix, "ip_address", inet_ntoa(addr));
+ if (get_option_addr(&net.s_addr, dhcp, DHO_SUBNETMASK) == -1) {
+ net.s_addr = get_netmask(addr.s_addr);
+ setvar(&ep, prefix, "subnet_mask", inet_ntoa(net));
+ }
+ i = inet_ntocidr(net);
+ snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net));
+ setvar(&ep, prefix, "subnet_cidr", cidr);
+ if (get_option_addr(&brd.s_addr, dhcp, DHO_BROADCAST) == -1) {
+ brd.s_addr = addr.s_addr | ~net.s_addr;
+ setvar(&ep, prefix, "broadcast_address", inet_ntoa(net));
+ }
+ 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);
+
+ for (opt = dhcp_opts; opt->option; opt++) {
+ if (!opt->var)
+ continue;
+ if (has_option_mask(options->nomask, opt->option))
+ continue;
+ val = NULL;
+ p = get_option(dhcp, opt->option, &pl, NULL);
+ if (!p)
+ continue;
+ /* We only want the FQDN name */
+ if (opt->option == DHO_FQDN) {
+ p += 3;
+ pl -= 3;
+ }
+ len = print_option(NULL, 0, opt->type, pl, p);
+ if (len < 0)
+ return -1;
+ e = strlen(prefix) + strlen(opt->var) + len + 4;
+ v = val = *ep++ = xmalloc(e);
+ v += snprintf(val, e, "%s_%s=", prefix, opt->var);
+ if (len != 0)
+ print_option(v, len, opt->type, pl, p);
+ }
+
+ return ep - env;
+}
diff --git a/dhcp.h b/dhcp.h
new file mode 100644
index 0000000..e584452
--- /dev/null
+++ b/dhcp.h
@@ -0,0 +1,178 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DHCP_H
+#define DHCP_H
+
+#include <arpa/inet.h>
+
+#include <stdint.h>
+
+#include "config.h"
+#include "dhcpcd.h"
+#include "net.h"
+
+/* Max MTU - defines dhcp option length */
+#define MTU_MAX 1500
+#define MTU_MIN 576
+
+/* 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
+
+/* 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_FQDN = 81,
+ DHO_DNSSEARCH = 119, /* RFC 3397 */
+ DHO_CSR = 121, /* RFC 3442 */
+ 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 (20 + 8)
+#define DHCP_BASE_LEN (4 + 4 + 2 + 2 + 4 + 4 + 4 + 4 + 4)
+#define DHCP_RESERVE_LEN (4 + 4 + 4 + 4 + 2)
+#define DHCP_FIXED_LEN (DHCP_BASE_LEN + DHCP_CHADDR_LEN + \
+ + SERVERNAME_LEN + BOOTFILE_LEN)
+#define DHCP_OPTION_LEN (MTU_MAX - DHCP_FIXED_LEN - DHCP_UDP_LEN \
+ - DHCP_RESERVE_LEN)
+
+/* Some crappy DHCP servers require the BOOTP minimum length */
+#define BOOTP_MESSAGE_LENTH_MIN 300
+
+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 */
+};
+
+struct dhcp_lease {
+ struct in_addr addr;
+ struct in_addr net;
+ uint32_t leasetime;
+ uint32_t renewaltime;
+ uint32_t rebindtime;
+ struct in_addr server;
+ time_t leasedfrom;
+ struct timeval boundtime;
+ uint8_t frominfo;
+};
+
+#define add_option_mask(var, val) (var[val >> 3] |= 1 << (val & 7))
+#define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
+#define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7)))
+int make_option_mask(uint8_t *, char **, int);
+void print_options(void);
+char *get_option_string(const struct dhcp_message *, uint8_t);
+int get_option_addr(uint32_t *, const struct dhcp_message *, uint8_t);
+int get_option_uint32(uint32_t *, const struct dhcp_message *, uint8_t);
+int get_option_uint16(uint16_t *, const struct dhcp_message *, uint8_t);
+int get_option_uint8(uint8_t *, const struct dhcp_message *, uint8_t);
+struct rt *get_option_routes(const struct dhcp_message *);
+ssize_t configure_env(char **, const char *, const struct dhcp_message *,
+ const struct options *);
+
+ssize_t make_message(struct dhcp_message **,
+ const struct interface *, const struct dhcp_lease *,
+ uint32_t, uint8_t, const struct options *);
+int valid_dhcp_packet(unsigned char *);
+
+ssize_t write_lease(const struct interface *, const struct dhcp_message *);
+struct dhcp_message *read_lease(const struct interface *iface);
+#endif
diff --git a/dhcpcd-hooks/01-test b/dhcpcd-hooks/01-test
new file mode 100644
index 0000000..609b3a1
--- /dev/null
+++ b/dhcpcd-hooks/01-test
@@ -0,0 +1,7 @@
+# Just echo our DHCP options we have
+
+case ${reason} in
+TEST) set | grep "^\(interface\|metric\|pid\|reason\|skip_hooks\)=" | sort
+ set | grep "^\(new_\|old_\)" | sort
+ ;;
+esac
diff --git a/dhcpcd-hooks/10-mtu b/dhcpcd-hooks/10-mtu
new file mode 100644
index 0000000..4265b48
--- /dev/null
+++ b/dhcpcd-hooks/10-mtu
@@ -0,0 +1,5 @@
+# Configure the MTU for the interface
+
+if [ -n "${new_interface_mtu}" ]; then
+ ifconfig "${interface}" mtu "${new_interface_mtu}"
+fi
diff --git a/dhcpcd-hooks/20-dns.conf b/dhcpcd-hooks/20-dns.conf
new file mode 100644
index 0000000..a92e91d
--- /dev/null
+++ b/dhcpcd-hooks/20-dns.conf
@@ -0,0 +1,32 @@
+# Set net.<iface>.dnsN properties that contain the
+# DNS server addresses given by the DHCP server.
+
+set_dns_props()
+{
+ case "${new_domain_name_servers}" in
+ "") return 0;;
+ esac
+
+ count=1
+ for i in 1 2 3 4; do
+ setprop dhcp.${interface}.dns${i} ""
+ done
+
+ count=1
+ for dnsaddr in ${new_domain_name_servers}; do
+ setprop dhcp.${interface}.dns${count} ${dnsaddr}
+ count=$(($count + 1))
+ done
+}
+
+unset_dns_props()
+{
+ for i in 1 2 3 4; do
+ setprop dhcp.${interface}.dns${i} ""
+ done
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) set_dns_props;;
+EXPIRE|FAIL|IPV4LL|RELEASE|STOP) unset_dns_props;;
+esac
diff --git a/dhcpcd-hooks/20-resolv.conf b/dhcpcd-hooks/20-resolv.conf
new file mode 100644
index 0000000..e757ddf
--- /dev/null
+++ b/dhcpcd-hooks/20-resolv.conf
@@ -0,0 +1,107 @@
+# 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"
+
+build_resolv_conf()
+{
+ local cf="/etc/resolv.conf.${interface}"
+ 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
+ search=$(cd "${resolv_conf_dir}"; \
+ key_get_value "search " ${interfaces})
+ [ -n "${search}" ] && search="search $(uniqify ${search})\n"
+
+ # Build the nameserver list
+ srvs=$(cd "${resolv_conf_dir}"; \
+ key_get_value "nameserver " ${interfaces})
+ for x in $(uniqify ${srvs}); do
+ servers="${servers}nameserver ${x}\n"
+ done
+ fi
+ header="${signature_base}${header:+ ${from} }${header}"
+
+ # Assemble resolv.conf using our head and tail files
+ [ -f "${cf}" ] && rm -f "${cf}"
+ 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 "${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
+ mv -f "${cf}" /etc/resolv.conf
+}
+
+add_resolv_conf()
+{
+ local x= conf="${signature}\n"
+
+ # If we don't have any configuration, remove it
+ if [ -z "${new_domain_name_servers}" -a \
+ -z "${new_domain_name}" -a \
+ -z "${new_domain_search}" ]; then
+ remove_resolv_conf
+ return $?
+ fi
+
+ if [ -n "${new_domain_search}" ]; then
+ conf="${conf}search ${new_domain_search}\n"
+ elif [ -n "${new_domain_name}" ]; then
+ conf="${conf}search ${new_domain_name}\n"
+ fi
+ for x in ${new_domain_name_servers}; do
+ conf="${conf}nameserver ${x}\n"
+ done
+ if type resolvconf >/dev/null 2>&1; then
+ printf "${conf}" | resolvconf -a "${interface}"
+ return $?
+ fi
+
+ if [ -e "${resolv_conf_dir}/${interface}" ]; then
+ rm -f "${resolv_conf_dir}/${interface}"
+ fi
+ if [ ! -d "${resolv_conf_dir}" ]; then
+ mkdir -p "${resolv_conf_dir}"
+ fi
+ printf "${conf}" > "${resolv_conf_dir}/${interface}"
+ build_resolv_conf
+}
+
+remove_resolv_conf()
+{
+ if type resolvconf >/dev/null 2>&1; then
+ resolvconf -d "${interface}" -f
+ else
+ if [ -e "${resolv_conf_dir}/${interface}" ]; then
+ rm -f "${resolv_conf_dir}/${interface}"
+ fi
+ build_resolv_conf
+ fi
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) add_resolv_conf;;
+PREINIT|EXPIRE|FAIL|IPV4LL|RELEASE|STOP) remove_resolv_conf;;
+esac
diff --git a/dhcpcd-hooks/29-lookup-hostname b/dhcpcd-hooks/29-lookup-hostname
new file mode 100644
index 0000000..3dfade3
--- /dev/null
+++ b/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
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) set_hostname;;
+esac
diff --git a/dhcpcd-hooks/30-hostname b/dhcpcd-hooks/30-hostname
new file mode 100644
index 0000000..b2e5fc8
--- /dev/null
+++ b/dhcpcd-hooks/30-hostname
@@ -0,0 +1,28 @@
+# Set the hostname from DHCP data if required
+
+need_hostname()
+{
+ case "$(hostname)" in
+ ""|"(none)"|localhost|localhost.localdomain)
+ [ -n "${new_host_name}" -o -n "${new_fqdn_name}" ];;
+ "${old_host_name}"|"${old_fqdn_name}")
+ true;;
+ *)
+ false;;
+ esac
+}
+
+set_hostname()
+{
+ if need_hostname; then
+ if [ -n "${new_host_name}" ]; then
+ hostname "${new_host_name}"
+ else
+ hostname "${new_fqdn_name}"
+ fi
+ fi
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) set_hostname;;
+esac
diff --git a/dhcpcd-hooks/50-dhcpcd-compat b/dhcpcd-hooks/50-dhcpcd-compat
new file mode 100644
index 0000000..cba40a4
--- /dev/null
+++ b/dhcpcd-hooks/50-dhcpcd-compat
@@ -0,0 +1,31 @@
+# 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
+
+x="down"
+case "${reason}" in
+RENEW) x="up";;
+BOUND|INFORM|REBIND|REBOOT|TEST|TIMEOUT|IPV4LL) x="new";;
+esac
+set -- "" "${x}"
diff --git a/dhcpcd-hooks/50-ntp.conf b/dhcpcd-hooks/50-ntp.conf
new file mode 100644
index 0000000..536f14e
--- /dev/null
+++ b/dhcpcd-hooks/50-ntp.conf
@@ -0,0 +1,82 @@
+# 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
+
+# Detect OpenRC or BSD rc
+# Distributions may want to just have their command here instead of this
+if type rc-service >/dev/null 2>&1 && rc-service --exists ntpd; then
+ ntpd_restart_cmd="rc-service ntpd -- --ifstarted --quiet restart"
+elif [ -x /etc/rc.d/ntpd ]; then
+ ntpd_restart_cmd="/etc/rc.d/ntpd restart"
+elif [ -x /usr/local/etc/rc.d/ntpd ]; then
+ ntpd_restart_cmd="/usr/local/etc/rc.d/ntpd restart"
+fi
+
+ntp_conf_dir="${state_dir}/ntp.conf"
+
+build_ntp_conf()
+{
+ local cf="/etc/ntp.conf.${interface}"
+ 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}\n"
+ done
+ fi
+ fi
+
+ # Merge our config into ntp.conf
+ [ -e "${cf}" ] && rm -f "${cf}"
+ remove_markers "${signature_base}" "${signature_base_end}" \
+ /etc/ntp.conf > "${cf}"
+ if [ -n "${servers}" ]; then
+ echo "${signature_base}${header:+ ${from} }${header}" >> "${cf}"
+ printf "${search}${servers}" >> "${cf}"
+ echo "${signature_base_end}${header:+ ${from} }${header}" >> "${cf}"
+ fi
+
+ # If we changed anything, restart ntpd
+ if change_file /etc/ntp.conf "${cf}"; then
+ [ -n "${ntpd_restart_cmd}" ] && ${ntpd_restart_cmd}
+ fi
+}
+
+add_ntp_conf()
+{
+ local cf="${ntp_conf_dir}/${interface}" x=
+
+ [ -e "${cf}" ] && rm "${cf}"
+ [ -d "${ntp_conf_dir}" ] || mkdir -p "${ntp_conf_dir}"
+ if [ -n "${new_ntp_servers}" ]; then
+ for x in ${new_ntp_servers}; do
+ echo "server ${x}" >> "${cf}"
+ done
+ fi
+ build_ntp_conf
+}
+
+remove_ntp_conf()
+{
+ if [ -e "${ntp_conf_dir}/${interface}" ]; then
+ rm "${ntp_conf_dir}/${interface}"
+ fi
+ build_ntp_conf
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) add_ntp_conf add;;
+PREINIT|EXPIRE|FAIL|IPV4LL|RELEASE|STOP) remove_ntp_conf del;;
+esac
diff --git a/dhcpcd-hooks/50-yp.conf b/dhcpcd-hooks/50-yp.conf
new file mode 100644
index 0000000..a2296eb
--- /dev/null
+++ b/dhcpcd-hooks/50-yp.conf
@@ -0,0 +1,49 @@
+# 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."${interface}" prefix= x= pid=
+ rm -f "${cf}"
+ echo "${signature}" > "${cf}"
+ if [ -n "${new_nis_domain}" ]; then
+ domainname "${new_nis_domain}"
+ if [ -n "${new_nis_servers}" ]; then
+ prefix="domain ${new_nis_domain} server "
+ else
+ echo "domain ${new_nis_domain} broadcast" >> "${cf}"
+ fi
+ else
+ prefix="ypserver "
+ fi
+ for x in ${new_nis_servers}; do
+ echo "${prefix}${x}" >> "${cf}"
+ done
+ save_conf /etc/yp.conf
+ mv -f "${cf}" /etc/yp.conf
+ 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
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) make_yp_conf;;
+EXPIRE|FAIL|IPV4LL|RELEASE|STOP) restore_yp_conf;;
+esac
diff --git a/dhcpcd-hooks/90-NetworkManager b/dhcpcd-hooks/90-NetworkManager
new file mode 100644
index 0000000..c4d69fe
--- /dev/null
+++ b/dhcpcd-hooks/90-NetworkManager
@@ -0,0 +1,8 @@
+# Hook for NetworkManager-0.7.0
+# NOTE: NetworkManager will override the script dhcpcd calls, so this hook
+# only makes sense if NetworkManager is patched NOT to override the
+# script dhcpcd would call.
+
+if [ -x /usr/libexec/nm-dhcp-client.action ]; then
+ /usr/libexec/nm-dhcp-client.action
+fi
diff --git a/dhcpcd-hooks/95-configured b/dhcpcd-hooks/95-configured
new file mode 100644
index 0000000..93f1c43
--- /dev/null
+++ b/dhcpcd-hooks/95-configured
@@ -0,0 +1,26 @@
+# This script runs last, after all network configuration
+# has completed. It sets a property to let the framework
+# know that setting up the interface is complete.
+
+# For debugging:
+setprop dhcp.${interface}.reason "${reason}"
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT)
+ setprop dhcp.${interface}.ipaddress "${new_ip_address}"
+ setprop dhcp.${interface}.gateway "${new_routers%% *}"
+ setprop dhcp.${interface}.mask "${new_subnet_mask}"
+ setprop dhcp.${interface}.leasetime "${new_dhcp_lease_time}"
+ setprop dhcp.${interface}.server "${new_dhcp_server_identifier}"
+
+ setprop dhcp.${interface}.result "ok"
+ ;;
+
+EXPIRE|FAIL|IPV4LL|STOP)
+ setprop dhcp.${interface}.result "failed"
+ ;;
+
+RELEASE)
+ setprop dhcp.${interface}.result "released"
+ ;;
+esac
diff --git a/dhcpcd-hooks/Makefile b/dhcpcd-hooks/Makefile
new file mode 100644
index 0000000..cfb19f7
--- /dev/null
+++ b/dhcpcd-hooks/Makefile
@@ -0,0 +1,13 @@
+LIBEXECDIR?= ${PREFIX}/libexec
+HOOKDIR= ${LIBEXECDIR}/dhcpcd-hooks
+SYSTEMSCRIPTS= 01-test 10-mtu 20-resolv.conf 30-hostname
+FILES= ${SYSTEMSCRIPTS} ${HOOKSCRIPTS}
+FILESDIR= ${HOOKDIR}
+
+all:
+
+MK= ../mk
+include ${MK}/os.mk
+include ${MK}/sys.mk
+include ${MK}/files.mk
+install: _filesinstall
diff --git a/dhcpcd-run-hooks b/dhcpcd-run-hooks
new file mode 100755
index 0000000..db9c4f8
--- /dev/null
+++ b/dhcpcd-run-hooks
@@ -0,0 +1,31 @@
+#!/system/bin/sh
+# dhcpcd client configuration script
+
+# Handy variables and functions for our hooks to use
+from="from"
+signature_base="# Generated by dhcpcd"
+signature="${signature_base} ${from} ${interface}"
+signature_base_end="# End of dhcpcd"
+signature_end="${signature_base_end} ${from} ${interface}"
+state_dir="/data/misc/dhcpcd"
+
+# 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 \
+ /system/etc/dhcpcd/dhcpcd.enter-hook \
+ /system/etc/dhcpcd/dhcpcd-hooks/* \
+ /system/etc/dhcpcd/dhcpcd.exit-hook
+do
+ for skip in ${skip_hooks}; do
+ case "${hook}" in
+ "${skip}") continue 2;;
+ */[0-9][0-9]"-${skip}") continue 2;;
+ */[0-9][0-9]"-${skip}.sh") continue 2;;
+ esac
+ done
+ if ls "${hook}" >/dev/null 2>&1; then
+ . "${hook}"
+ fi
+done
diff --git a/dhcpcd-run-hooks.8 b/dhcpcd-run-hooks.8
new file mode 100644
index 0000000..dca5378
--- /dev/null
+++ b/dhcpcd-run-hooks.8
@@ -0,0 +1,113 @@
+.\" Copyright 2006-2008 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 August 14, 2008
+.Dt DHCPCD.SH 8 SMM
+.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 /system/etc/dhcpcd/dhcpcd-hooks
+and the user defined hooks are
+.Pa /system/etc/dhcpcd/dhcpcd.enter-hook .
+and
+.Pa /system/etc/dhcpcd/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 indent
+.It Dv PREINIT
+dhcpcd is starting up and any pre-initialisation should be done.
+.It Dv INFORM
+dhcpcd informed a DHCP server about it's address and obtained other
+configuration details.
+.It Dv BOUND
+dhcpcd obtained a new lease from a DHCP server.
+.It Dv RENEW
+dhcpcd renewed it's lease.
+.It Dv REBIND
+dhcpcd has rebound to a new DHCP server.
+.It Dv REBOOT
+dhcpcd successfully requested a lease from a DHCP server.
+.It Dv EXPIRE
+dhcpcd's lease expired and it failed to obtain a new one.
+.It Dv IPV4LL
+dhcpcd failed to contact any DHCP servers but did obtain an IPV4LL address.
+.It Dv FAIL
+dhcpcd failed to contact any DHCP servers or use an old lease.
+.It Dv TIMEOUT
+dhcpcd failed to contact any DHCP servers but was able to use an old lease.
+.It Dv TEST
+dhcpcd received an OFFER from a DHCP server but will not configure the
+interface.
+This is primarily used to test the variables are filled correctly for the
+script to process them.
+.El
+.Sh FILES
+When
+.Nm
+runs, it loads
+.Pa /system/etc/dhcpcd/dhcpcd.enter-hook
+and any scripts found in
+.Pa /system/etc/dhcpcd/dhcpcd-hooks
+in a lexical order and then finally
+.Pa /system/etc/dhcpcd/dhcpcd.exit-hook
+.Sh SEE ALSO
+.Xr dhcpcd 8
+.Sh AUTHORS
+.An Roy Marples <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd-run-hooks.8.in b/dhcpcd-run-hooks.8.in
new file mode 100644
index 0000000..72669f5
--- /dev/null
+++ b/dhcpcd-run-hooks.8.in
@@ -0,0 +1,113 @@
+.\" Copyright 2006-2008 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 August 14, 2008
+.Dt DHCPCD.SH 8 SMM
+.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 indent
+.It Dv PREINIT
+dhcpcd is starting up and any pre-initialisation should be done.
+.It Dv INFORM
+dhcpcd informed a DHCP server about it's address and obtained other
+configuration details.
+.It Dv BOUND
+dhcpcd obtained a new lease from a DHCP server.
+.It Dv RENEW
+dhcpcd renewed it's lease.
+.It Dv REBIND
+dhcpcd has rebound to a new DHCP server.
+.It Dv REBOOT
+dhcpcd successfully requested a lease from a DHCP server.
+.It Dv EXPIRE
+dhcpcd's lease expired and it failed to obtain a new one.
+.It Dv IPV4LL
+dhcpcd failed to contact any DHCP servers but did obtain an IPV4LL address.
+.It Dv FAIL
+dhcpcd failed to contact any DHCP servers or use an old lease.
+.It Dv TIMEOUT
+dhcpcd failed to contact any DHCP servers but was able to use an old lease.
+.It Dv TEST
+dhcpcd received an OFFER from a DHCP server but will not configure the
+interface.
+This is primarily used to test the variables are filled correctly for the
+script to process them.
+.El
+.Sh FILES
+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 <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd-run-hooks.in b/dhcpcd-run-hooks.in
new file mode 100644
index 0000000..1e5d5b3
--- /dev/null
+++ b/dhcpcd-run-hooks.in
@@ -0,0 +1,138 @@
+#!/bin/sh
+# dhcpcd client configuration script
+
+# Handy variables and functions for our hooks to use
+from="from"
+signature_base="# Generated by dhcpcd"
+signature="${signature_base} ${from} ${interface}"
+signature_base_end="# End of dhcpcd"
+signature_end="${signature_base_end} ${from} ${interface}"
+state_dir="/var/run/dhcpcd"
+
+# Ensure that all arguments are unique
+uniqify()
+{
+ local result=
+
+ while [ -n "$1" ]; do
+ case " ${result} " in
+ *" $1 "*);;
+ *) result="${result}${result:+ }$1";;
+ esac
+ shift
+ done
+ echo "${result}"
+}
+
+# List interface config files in a dir
+# We may wish to control the order at some point rather than just lexical
+list_interfaces()
+{
+ local x= interfaces=
+ for x in "$1"/*; do
+ [ -e "${x}" ] || continue
+ interfaces="${interfaces}${interfaces:+ }${x##*/}"
+ done
+ echo "${interfaces}"
+}
+
+# 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
+# It different, replace first with second otherwise remove second
+change_file()
+{
+ if type cmp >/dev/null 2>&1; then
+ cmp -s "$1" "$2"
+ elif type diff >/dev/null 2>&1; then
+ diff -q "$1" "$2" >/dev/null
+ else
+ # Hopefully we're only working on small text files ...
+ [ "$(cat "$1")" = "$(cat "$2")" ]
+ fi
+ if [ $? -eq 0 ]; then
+ rm -f "$2"
+ return 1
+ fi
+ mv -f "$2" "$1"
+ return 0
+}
+
+# Save a config file
+save_conf()
+{
+ if [ -f "$1" ]; then
+ rm -f "$1"-pre."${interface}"
+ mv -f "$1" "$1"-pre."${interface}"
+ fi
+}
+
+# Restore a config file
+restore_conf()
+{
+ [ -f "$1"-pre."${interface}" ] || return 1
+ rm -f "$1"
+ mv -f "$1"-pre."${interface}" "$1"
+}
+
+
+# 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
+ "${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.8 b/dhcpcd.8
new file mode 100644
index 0000000..ac6150c
--- /dev/null
+++ b/dhcpcd.8
@@ -0,0 +1,430 @@
+.\" Copyright 2006-2008 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 August 20, 2008
+.Dt DHCPCD 8 SMM
+.Sh NAME
+.Nm dhcpcd
+.Nd an RFC 2131 compliant DHCP client
+.Sh SYNOPSIS
+.Nm
+.Op Fl bdknpqABDEGKLSTV
+.Op Fl c , -script Ar script
+.Op Fl f , -config Ar file
+.Op Fl h , -hostname Ar hostname
+.Op Fl i , -vendorclassid Ar vendorclassid
+.Op Fl l , -leasetime Ar seconds
+.Op Fl m , -metric Ar metric
+.Op Fl o , -option Ar option
+.Op Fl r , -request Ar address
+.Op Fl s , -inform Ar address Ns Op Ar /cidr
+.Op Fl t , -timeout Ar seconds
+.Op Fl u , -userclass Ar class
+.Op Fl v , -vendor Ar code , Ar value
+.Op Fl C , -nohook Ar hook
+.Op Fl F , -fqdn Ar FQDN
+.Op Fl I , -clientid Ar clientid
+.Op Fl O , -nooption Ar option
+.Op Fl Q , -require Ar option
+.Op Fl X , -blacklist Ar address
+.Ar interface
+.Nm
+.Fl k , -release
+.Ar interface
+.Nm
+.Fl x , -exit
+.Ar 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 currenly blank, (null) or localhost then
+.Nm
+sets the hostname to the one supplied by the DHCP server.
+.Nm
+then daemonises and waits for the lease renewal time to lapse.
+Then it attempts to renew its lease and reconfigure if the new lease changes.
+.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 , -noipv4ll
+option.
+.Ss Hooking into DHCP events
+.Nm
+runs
+.Pa /system/etc/dhcpcd/dhcpcd-run-hooks ,
+or the script specified by the
+.Fl c , -script
+option.
+This script runs each script found in
+.Pa /system/etc/dhcpcd/dhcpcd-hooks
+in a lexical order.
+The default installation supplies the scripts
+.Pa 01-test ,
+.Pa 10-mtu ,
+.Pa 20-resolv.conf
+and
+.Pa 30-hostname .
+You can disable each script by using the
+.Fl C , -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 , -background
+Background immediately.
+This is useful for startup scripts which don't disable link messages for
+carrier status.
+.It Fl c , -script Ar script
+Use this
+.Ar script
+instead of the default
+.Pa /system/etc/dhcpcd/dhcpcd-run-hooks .
+.It Fl d , -debug
+Echo debug and informational messages to the console.
+Subsequent debug options stop
+.Nm
+from daemonising.
+.It Fl f , -config Ar file
+Specify a config to load instead of
+.Pa /system/etc/dhcpcd/dhcpcd.conf .
+.Nm
+always processes the config file before any command line options.
+.It Fl h , -hostname Ar hostname
+By default,
+.Nm
+sends the current hostname to the DHCP server so it can register in DNS.
+You can use this option to specify the
+.Ar hostname
+sent, or an empty string to
+stop any
+.Ar hostname
+from being sent.
+.It Fl i , -vendorclassid Ar vendorclassid
+Override the
+.Ar vendorclassid
+field sent. The default is
+dhcpcd <version>.
+If not set then none is sent.
+.It Fl k , -release
+This causes an existing
+.Nm
+process running on the
+.Ar interface
+to release its lease, deconfigure the
+.Ar interface
+and then exit.
+.Nm
+then waits until this process has exited.
+.It Fl l , -leasetime Ar seconds
+Request a specific lease time in
+.Ar seconds .
+By default
+.Nm
+does not request any lease time and leaves the it in the hands of the
+DHCP server.
+.It Fl m , -metric Ar metric
+Added routes will use the
+.Ar metric
+on systems where this is supported
+.Po
+presently only Linux
+.Pc .
+Route metrics allow the addition of routes to the same destination across
+different interfaces, the lower the metric the more it is preferred.
+.It Fl o , -option Ar option
+Request the DHCP
+.Ar option
+variable for use in
+.Pa /system/etc/dhcpcd/dhcpcd-run-hooks .
+.It Fl n , -rebind
+Notifies an existing
+.Nm
+process running on the
+.Ar interface
+to rebind it's lease.
+.Nm
+will not re-configure itself or use any other command line arguments.
+.Nm
+will timeout the rebind after 30 seconds at which point the lease will be
+expired and
+.Nm
+will enter the discovery state to obtain a new lease.
+Use the
+.Fl t , -timeout
+option to change this.
+If
+.Nm
+is not running, then it starts up as normal.
+This option used to be renew, but rebind is more accurate as we need to
+broadcast the request instead of unicasting.
+.It Fl p , -persistent
+.Nm
+normally deconfigures the
+.Ar interface
+and configuration when it exits.
+Sometimes, this isn't desirable if for example you have root mounted over NFS.
+You can use this option to stop this from happening.
+.It Fl r , -request Op Ar address
+.Nm
+normally sends a DHCP DISCOVER to find servers to offer an address.
+.Nm
+then requests the address used.
+You can use this option to skip the BROADCAST step and just request the
+.Ar address .
+The downside is if you request an
+.Ar address
+the DHCP server does not know about or the DHCP server is not
+authorative, it will remain silent.
+In this situation, we go back to the init state and DISCOVER again.
+If no
+.Ar address
+is given then the first address currently assigned to the
+.Ar interface
+is used.
+.It Fl s , -inform Op Ar address Ns Op Ar /cidr
+Behaves like
+.Fl r , -request
+as above, but sends a DHCP INFORM instead of a 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 t , -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.
+.It Fl u , -userclass Ar class
+Tags the DHCP message with the userclass
+.Ar class .
+DHCP servers use this give members of the class DHCP options other than the
+default, without having to know things like hardware address or hostname.
+.It Fl v , -vendor Ar code , Ns Ar value
+Add an enscapulated vendor option.
+.Ar code
+should be between 1 and 254 inclusive.
+Examples.
+.Pp
+Set the vendor option 01 with an IP address.
+.D1 dhcpcd \-v 01,192.168.0.2 eth0
+Set the vendor option 02 with a hex code.
+.D1 dhcpcd \-v 02,01:02:03:04:05 eth0
+Do the above and set a third option with a string and not an IP address.
+.D1 dhcpcd \-v 01,192.168.0.2 \-v 02,01:02:03:04:05 \-v 03,\e"192.168.0.2\e" eth0
+.It Fl x , -exit
+This will signal an existing
+.Nm
+process running on the
+.Ar interface
+to deconfigure the
+.Ar interface
+and exit.
+.Nm
+then waits until this process has exited.
+.It Fl D , -duid
+Generate an
+.Li RFC 4361
+compliant clientid.
+This requires persistent storage and not all DHCP servers work with it so it's
+not enabled by default.
+.Nm
+generates the DUID and stores in it
+.Pa /system/etc/dhcpcd/dhcpcd.duid
+This file should not be copied to other hosts.
+.It Fl E , -lastlease
+If
+.Nm
+cannot obtain a lease, then try to use the last lease acquired for the
+interface.
+If the
+.Fl p, -persistent
+option is not given then the lease is used if it hasn't expired.
+.It Fl F , -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.
+The current hostname or the hostname specified using the
+.Fl h , -hostname
+option must be a FQDN.
+.Nm
+itself never does any DNS updates.
+.Nm
+encodes the FQDN hostname as specified in
+.Li RFC1035 .
+.It Fl I , -clientid Ar clientid
+Change the default clientid sent from the interface hardware address.
+If the string is of the format 01:02:03 then it is encoded as hex.
+If not set then none is sent.
+.El
+.Ss Restriciting 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 q , -quiet
+Quiet
+.Nm
+on the command line, only warnings and errors will be displayed.
+The messages are still logged though.
+.It Fl A , -noarp
+Don't request or claim the address by ARP.
+This also disables IPv4LL.
+.It Fl B , -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 , -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 dhcpcd from touching your DNS or MTU settings you would do:-
+.D1 dhcpcd -C resolv.conf -C mtu eth0
+.It Fl G , -nogateway
+Don't set any default routes.
+.It Fl K , -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 , -noipv4ll
+Don't use IPv4LL (aka APIPA, aka Bonjour, aka ZeroConf).
+.It Fl O , -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 , -require Ar option
+Requires the
+.Ar option
+to be present in all DHCP messages, otherwise the message is ignored.
+.It Fl T, -test
+On receipt of OFFER messages just call
+.Pa /system/etc/dhcpcd/dhcpcd-run-hooks
+with the reason of TEST which echo's the DHCP variables found in the message
+to the console.
+The interface configuration isn't touched and neither are any configuration
+files.
+.It Fl V, -variables
+Display a list of option codes and the associated variable for use in
+.Xr dhcpcd-run-hooks 8 .
+.It Fl X, -blacklist Ar address
+Ignores all DHCP messages which have this
+.Ar address
+as the server ID.
+This may be expanded in future releases to ignore all packets
+matching either the IP or hardware
+.Ar address .
+.El
+.Sh NOTES
+.Nm
+requires a Berkley Packet Filter, or BPF device on BSD based systems and a
+Linux Socket Filter, or LPF device on Linux based systems.
+.Sh FILES
+.Bl -ohang
+.It Pa /system/etc/dhcpcd/dhcpcd.conf
+Configuration file for dhcpcd.
+If you always use the same options, put them here.
+.It Pa /system/etc/dhcpcd/dhcpcd.duid
+Text file that holds the DUID used to identify the host.
+.It Pa /system/etc/dhcpcd/dhcpcd-run-hooks
+Bourne shell script that is run to configure or deconfigure an interface.
+.It Pa /system/etc/dhcpcd/dhcpcd-hooks
+A directory containing bourne shell scripts that are run by the above script.
+Each script can be disabled by using the
+.Fl C , -nohook
+option described above.
+.It Pa /data/misc/dhcp/dhcpcd\- Ns Ar interface Ns .lease
+The actual DHCP message send by the server. We use this when reading the last
+lease and use the files mtime as when it was issued.
+.It Pa /var/run/dhcpcd\- Ns Ar interface Ns .pid
+Stores the PID of
+.Nm
+running on the
+.Ar interface .
+.El
+.Sh SEE ALSO
+.Xr dhcpcd.conf 5 ,
+.Xr dhcpcd-run-hooks 8 ,
+.Xr resolv.conf 5 ,
+.Xr resolvconf 8 ,
+.Sh STANDARDS
+RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396, RFC 3397,
+RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702.
+.Sh AUTHORS
+.An Roy Marples <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd.8.in b/dhcpcd.8.in
new file mode 100644
index 0000000..6c82d3f
--- /dev/null
+++ b/dhcpcd.8.in
@@ -0,0 +1,430 @@
+.\" Copyright 2006-2008 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 August 20, 2008
+.Dt DHCPCD 8 SMM
+.Sh NAME
+.Nm dhcpcd
+.Nd an RFC 2131 compliant DHCP client
+.Sh SYNOPSIS
+.Nm
+.Op Fl bdknpqABDEGKLSTV
+.Op Fl c , -script Ar script
+.Op Fl f , -config Ar file
+.Op Fl h , -hostname Ar hostname
+.Op Fl i , -vendorclassid Ar vendorclassid
+.Op Fl l , -leasetime Ar seconds
+.Op Fl m , -metric Ar metric
+.Op Fl o , -option Ar option
+.Op Fl r , -request Ar address
+.Op Fl s , -inform Ar address Ns Op Ar /cidr
+.Op Fl t , -timeout Ar seconds
+.Op Fl u , -userclass Ar class
+.Op Fl v , -vendor Ar code , Ar value
+.Op Fl C , -nohook Ar hook
+.Op Fl F , -fqdn Ar FQDN
+.Op Fl I , -clientid Ar clientid
+.Op Fl O , -nooption Ar option
+.Op Fl Q , -require Ar option
+.Op Fl X , -blacklist Ar address
+.Ar interface
+.Nm
+.Fl k , -release
+.Ar interface
+.Nm
+.Fl x , -exit
+.Ar 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 currenly blank, (null) or localhost then
+.Nm
+sets the hostname to the one supplied by the DHCP server.
+.Nm
+then daemonises and waits for the lease renewal time to lapse.
+Then it attempts to renew its lease and reconfigure if the new lease changes.
+.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 , -noipv4ll
+option.
+.Ss Hooking into DHCP events
+.Nm
+runs
+.Pa @SCRIPT@ ,
+or the script specified by the
+.Fl c , -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 20-resolv.conf
+and
+.Pa 30-hostname .
+You can disable each script by using the
+.Fl C , -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 , -background
+Background immediately.
+This is useful for startup scripts which don't disable link messages for
+carrier status.
+.It Fl c , -script Ar script
+Use this
+.Ar script
+instead of the default
+.Pa @SCRIPT@ .
+.It Fl d , -debug
+Echo debug and informational messages to the console.
+Subsequent debug options stop
+.Nm
+from daemonising.
+.It Fl f , -config Ar file
+Specify a config to load instead of
+.Pa @SYSCONFDIR@/dhcpcd.conf .
+.Nm
+always processes the config file before any command line options.
+.It Fl h , -hostname Ar hostname
+By default,
+.Nm
+sends the current hostname to the DHCP server so it can register in DNS.
+You can use this option to specify the
+.Ar hostname
+sent, or an empty string to
+stop any
+.Ar hostname
+from being sent.
+.It Fl i , -vendorclassid Ar vendorclassid
+Override the
+.Ar vendorclassid
+field sent. The default is
+dhcpcd <version>.
+If not set then none is sent.
+.It Fl k , -release
+This causes an existing
+.Nm
+process running on the
+.Ar interface
+to release its lease, deconfigure the
+.Ar interface
+and then exit.
+.Nm
+then waits until this process has exited.
+.It Fl l , -leasetime Ar seconds
+Request a specific lease time in
+.Ar seconds .
+By default
+.Nm
+does not request any lease time and leaves the it in the hands of the
+DHCP server.
+.It Fl m , -metric Ar metric
+Added routes will use the
+.Ar metric
+on systems where this is supported
+.Po
+presently only Linux
+.Pc .
+Route metrics allow the addition of routes to the same destination across
+different interfaces, the lower the metric the more it is preferred.
+.It Fl o , -option Ar option
+Request the DHCP
+.Ar option
+variable for use in
+.Pa @SCRIPT@ .
+.It Fl n , -rebind
+Notifies an existing
+.Nm
+process running on the
+.Ar interface
+to rebind it's lease.
+.Nm
+will not re-configure itself or use any other command line arguments.
+.Nm
+will timeout the rebind after 30 seconds at which point the lease will be
+expired and
+.Nm
+will enter the discovery state to obtain a new lease.
+Use the
+.Fl t , -timeout
+option to change this.
+If
+.Nm
+is not running, then it starts up as normal.
+This option used to be renew, but rebind is more accurate as we need to
+broadcast the request instead of unicasting.
+.It Fl p , -persistent
+.Nm
+normally deconfigures the
+.Ar interface
+and configuration when it exits.
+Sometimes, this isn't desirable if for example you have root mounted over NFS.
+You can use this option to stop this from happening.
+.It Fl r , -request Op Ar address
+.Nm
+normally sends a DHCP DISCOVER to find servers to offer an address.
+.Nm
+then requests the address used.
+You can use this option to skip the BROADCAST step and just request the
+.Ar address .
+The downside is if you request an
+.Ar address
+the DHCP server does not know about or the DHCP server is not
+authorative, it will remain silent.
+In this situation, we go back to the init state and DISCOVER again.
+If no
+.Ar address
+is given then the first address currently assigned to the
+.Ar interface
+is used.
+.It Fl s , -inform Op Ar address Ns Op Ar /cidr
+Behaves like
+.Fl r , -request
+as above, but sends a DHCP INFORM instead of a 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 t , -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.
+.It Fl u , -userclass Ar class
+Tags the DHCP message with the userclass
+.Ar class .
+DHCP servers use this give members of the class DHCP options other than the
+default, without having to know things like hardware address or hostname.
+.It Fl v , -vendor Ar code , Ns Ar value
+Add an enscapulated vendor option.
+.Ar code
+should be between 1 and 254 inclusive.
+Examples.
+.Pp
+Set the vendor option 01 with an IP address.
+.D1 dhcpcd \-v 01,192.168.0.2 eth0
+Set the vendor option 02 with a hex code.
+.D1 dhcpcd \-v 02,01:02:03:04:05 eth0
+Do the above and set a third option with a string and not an IP address.
+.D1 dhcpcd \-v 01,192.168.0.2 \-v 02,01:02:03:04:05 \-v 03,\e"192.168.0.2\e" eth0
+.It Fl x , -exit
+This will signal an existing
+.Nm
+process running on the
+.Ar interface
+to deconfigure the
+.Ar interface
+and exit.
+.Nm
+then waits until this process has exited.
+.It Fl D , -duid
+Generate an
+.Li RFC 4361
+compliant clientid.
+This requires persistent storage and not all DHCP servers work with it so it's
+not enabled by default.
+.Nm
+generates the DUID and stores in it
+.Pa @SYSCONFDIR@/dhcpcd.duid
+This file should not be copied to other hosts.
+.It Fl E , -lastlease
+If
+.Nm
+cannot obtain a lease, then try to use the last lease acquired for the
+interface.
+If the
+.Fl p, -persistent
+option is not given then the lease is used if it hasn't expired.
+.It Fl F , -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.
+The current hostname or the hostname specified using the
+.Fl h , -hostname
+option must be a FQDN.
+.Nm
+itself never does any DNS updates.
+.Nm
+encodes the FQDN hostname as specified in
+.Li RFC1035 .
+.It Fl I , -clientid Ar clientid
+Change the default clientid sent from the interface hardware address.
+If the string is of the format 01:02:03 then it is encoded as hex.
+If not set then none is sent.
+.El
+.Ss Restriciting 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 q , -quiet
+Quiet
+.Nm
+on the command line, only warnings and errors will be displayed.
+The messages are still logged though.
+.It Fl A , -noarp
+Don't request or claim the address by ARP.
+This also disables IPv4LL.
+.It Fl B , -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 , -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 dhcpcd from touching your DNS or MTU settings you would do:-
+.D1 dhcpcd -C resolv.conf -C mtu eth0
+.It Fl G , -nogateway
+Don't set any default routes.
+.It Fl K , -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 , -noipv4ll
+Don't use IPv4LL (aka APIPA, aka Bonjour, aka ZeroConf).
+.It Fl O , -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 , -require Ar option
+Requires the
+.Ar option
+to be present in all DHCP messages, otherwise the message is ignored.
+.It Fl T, -test
+On receipt of OFFER messages just call
+.Pa @SCRIPT@
+with the reason of TEST which echo's the DHCP variables found in the message
+to the console.
+The interface configuration isn't touched and neither are any configuration
+files.
+.It Fl V, -variables
+Display a list of option codes and the associated variable for use in
+.Xr dhcpcd-run-hooks 8 .
+.It Fl X, -blacklist Ar address
+Ignores all DHCP messages which have this
+.Ar address
+as the server ID.
+This may be expanded in future releases to ignore all packets
+matching either the IP or hardware
+.Ar address .
+.El
+.Sh NOTES
+.Nm
+requires a Berkley Packet Filter, or BPF device on BSD based systems and a
+Linux Socket Filter, or LPF device on Linux based systems.
+.Sh FILES
+.Bl -ohang
+.It Pa @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 deconfigure an interface.
+.It Pa @HOOKDIR@
+A directory containing bourne shell scripts that are run by the above script.
+Each script can be disabled by using the
+.Fl C , -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 /var/run/dhcpcd\- Ns Ar interface Ns .pid
+Stores the PID of
+.Nm
+running on the
+.Ar interface .
+.El
+.Sh SEE ALSO
+.Xr dhcpcd.conf 5 ,
+.Xr dhcpcd-run-hooks 8 ,
+.Xr resolv.conf 5 ,
+.Xr resolvconf 8 ,
+.Sh STANDARDS
+RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396, RFC 3397,
+RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702.
+.Sh AUTHORS
+.An Roy Marples <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd.c b/dhcpcd.c
new file mode 100644
index 0000000..e674bd2
--- /dev/null
+++ b/dhcpcd.c
@@ -0,0 +1,1038 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+const char copyright[] = "Copyright (c) 2006-2008 Roy Marples";
+
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "config.h"
+#include "client.h"
+#include "dhcpcd.h"
+#include "dhcp.h"
+#include "net.h"
+#include "logger.h"
+
+#ifdef ANDROID
+#include <linux/capability.h>
+#include <linux/prctl.h>
+#include <cutils/properties.h>
+#include <private/android_filesystem_config.h>
+#endif
+
+/* Don't set any optional arguments here so we retain POSIX
+ * compatibility with getopt */
+#define OPTS "bc:df:h:i:kl:m:no:pqr:s:t:u:v:xABC:DEF:GI:KLO:Q:TVX:"
+
+static int doversion = 0;
+static int dohelp = 0;
+static const struct option longopts[] = {
+ {"background", no_argument, NULL, 'b'},
+ {"script", required_argument, NULL, 'c'},
+ {"debug", no_argument, NULL, 'd'},
+ {"config", required_argument, NULL, 'f'},
+ {"hostname", optional_argument, NULL, 'h'},
+ {"vendorclassid", optional_argument, NULL, 'i'},
+ {"release", no_argument, NULL, 'k'},
+ {"leasetime", required_argument, NULL, 'l'},
+ {"metric", required_argument, NULL, 'm'},
+ {"rebind", no_argument, NULL, 'n'},
+ {"option", required_argument, NULL, 'o'},
+ {"persistent", no_argument, NULL, 'p'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"request", optional_argument, NULL, 'r'},
+ {"inform", optional_argument, NULL, 's'},
+ {"timeout", required_argument, NULL, 't'},
+ {"userclass", required_argument, NULL, 'u'},
+ {"vendor", required_argument, NULL, 'v'},
+ {"exit", no_argument, NULL, 'x'},
+ {"noarp", no_argument, NULL, 'A'},
+ {"nobackground", no_argument, NULL, 'B'},
+ {"nohook", required_argument, NULL, 'C'},
+ {"duid", no_argument, NULL, 'D'},
+ {"lastlease", no_argument, NULL, 'E'},
+ {"fqdn", optional_argument, NULL, 'F'},
+ {"nogateway", no_argument, NULL, 'G'},
+ {"clientid", optional_argument, NULL, 'I'},
+ {"nolink", no_argument, NULL, 'K'},
+ {"noipv4ll", no_argument, NULL, 'L'},
+ {"nooption", optional_argument, NULL, 'O'},
+ {"require", required_argument, NULL, 'Q'},
+ {"test", no_argument, NULL, 'T'},
+ {"variables", no_argument, NULL, 'V'},
+ {"blacklist", required_argument, NULL, 'X'},
+ {"help", no_argument, &dohelp, 1},
+ {"version", no_argument, &doversion, 1},
+#ifdef CMDLINE_COMPAT
+ {"classid", optional_argument, NULL, 'i'},
+ {"renew", no_argument, NULL, 'n'},
+ {"nohostname", no_argument, NULL, 'H'},
+ {"nomtu", no_argument, NULL, 'M'},
+ {"nontp", no_argument, NULL, 'N'},
+ {"nodns", no_argument, NULL, 'R'},
+ {"msscr", no_argument, NULL, 'S'},
+ {"nonis", no_argument, NULL, 'Y'},
+#endif
+ {NULL, 0, NULL, '\0'}
+};
+
+#ifdef CMDLINE_COMPAT
+# define EXTRA_OPTS "HMNRSY"
+#endif
+
+#ifndef EXTRA_OPTS
+# define EXTRA_OPTS
+#endif
+
+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)))
+ {
+ logger(LOG_ERR, "`%s' out of range", s);
+ return -1;
+ }
+
+ return (int)n;
+}
+
+static pid_t
+read_pid(const char *pidfile)
+{
+ FILE *fp;
+ pid_t pid = 0;
+
+ if ((fp = fopen(pidfile, "r")) == NULL) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ fscanf(fp, "%d", &pid);
+ fclose(fp);
+
+ return pid;
+}
+
+static void
+usage(void)
+{
+ printf("usage: "PACKAGE" [-dknpqxADEGHKLOTV] [-c script] [-f file ] [-h hostname]\n"
+ " [-i classID ] [-l leasetime] [-m metric] [-o option] [-r ipaddr]\n"
+ " [-s ipaddr] [-t timeout] [-u userclass] [-F none|ptr|both]\n"
+ " [-I clientID] [-C hookscript] [-Q option] [-X ipaddr] <interface>\n");
+}
+
+static char *
+add_environ(struct options *options, const char *value, int uniq)
+{
+ char **newlist;
+ char **lst = options->environ;
+ size_t i = 0, l, lv;
+ char *match = NULL, *p;
+
+ match = xstrdup(value);
+ p = strchr(match, '=');
+ if (p)
+ *p++ = '\0';
+ l = strlen(match);
+
+ while (lst && lst[i]) {
+ if (match && strncmp(lst[i], match, l) == 0) {
+ if (uniq) {
+ free(lst[i]);
+ lst[i] = xstrdup(value);
+ } else {
+ /* Append a space and the value to it */
+ l = strlen(lst[i]);
+ lv = strlen(p);
+ lst[i] = xrealloc(lst[i], l + lv + 2);
+ lst[i][l] = ' ';
+ memcpy(lst[i] + l + 1, p, lv);
+ lst[i][l + lv + 1] = '\0';
+ }
+ free(match);
+ return lst[i];
+ }
+ i++;
+ }
+
+ newlist = xrealloc(lst, sizeof(char *) * (i + 2));
+ newlist[i] = xstrdup(value);
+ newlist[i + 1] = NULL;
+ options->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, char *str, int clid)
+{
+ ssize_t l;
+ char *p;
+ int i;
+ char c[4];
+
+ /* If surrounded by quotes then it's a string */
+ if (*str == '"') {
+ str++;
+ l = strlen(str);
+ p = str + l - 1;
+ if (*p == '"')
+ *p = '\0';
+ } 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) {
+ *sbuf++ = 0;
+ l++;
+ }
+ c[3] = '\0';
+ while (*str) {
+ if (++l > slen) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ if (*str == '\\') {
+ str++;
+ switch(*str++) {
+ case '\0':
+ break;
+ case 'b':
+ *sbuf++ = '\b';
+ break;
+ case 'n':
+ *sbuf++ = '\n';
+ break;
+ case 'r':
+ *sbuf++ = '\r';
+ break;
+ case 't':
+ *sbuf++ = '\t';
+ break;
+ case 'x':
+ /* Grab a hex code */
+ c[1] = '\0';
+ for (i = 0; i < 2; i++) {
+ if (isxdigit((unsigned char)*str) == 0)
+ break;
+ c[i] = *str++;
+ }
+ if (c[1] != '\0') {
+ c[2] = '\0';
+ *sbuf++ = strtol(c, NULL, 16);
+ } else
+ l--;
+ break;
+ case '0':
+ /* Grab an octal code */
+ c[2] = '\0';
+ for (i = 0; i < 3; i++) {
+ if (*str < '0' || *str > '7')
+ break;
+ c[i] = *str++;
+ }
+ if (c[2] != '\0') {
+ i = strtol(c, NULL, 8);
+ if (i > 255)
+ i = 255;
+ *sbuf ++= i;
+ } else
+ l--;
+ break;
+ default:
+ *sbuf++ = *str++;
+ }
+ } else
+ *sbuf++ = *str++;
+ }
+ return l;
+}
+
+static int
+parse_option(int opt, char *oarg, struct options *options)
+{
+ int i;
+ char *p;
+ ssize_t s;
+ struct in_addr addr;
+
+ switch(opt) {
+ case 'b':
+ options->options |= DHCPCD_BACKGROUND;
+ break;
+ case 'c':
+ strlcpy(options->script, oarg, sizeof(options->script));
+ break;
+ case 'h':
+ if (oarg)
+ s = parse_string(options->hostname + 1,
+ HOSTNAME_MAX_LEN, oarg);
+ else
+ s = 0;
+ if (s == -1) {
+ logger(LOG_ERR, "hostname: %s", strerror(errno));
+ return -1;
+ }
+ if (s != 0 && options->hostname[1] == '.') {
+ logger(LOG_ERR, "hostname cannot begin with a .");
+ return -1;
+ }
+ options->hostname[0] = (uint8_t)s;
+ break;
+ case 'i':
+ if (oarg)
+ s = parse_string((char *)options->vendorclassid + 1,
+ VENDORCLASSID_MAX_LEN, oarg);
+ else
+ s = 0;
+ if (s == -1) {
+ logger(LOG_ERR, "vendorclassid: %s", strerror(errno));
+ return -1;
+ }
+ *options->vendorclassid = (uint8_t)s;
+ break;
+ case 'l':
+ if (*oarg == '-') {
+ logger(LOG_ERR,
+ "leasetime must be a positive value");
+ return -1;
+ }
+ errno = 0;
+ options->leasetime = (uint32_t)strtol(oarg, NULL, 0);
+ if (errno == EINVAL || errno == ERANGE) {
+ logger(LOG_ERR, "`%s' out of range", oarg);
+ return -1;
+ }
+ break;
+ case 'm':
+ options->metric = atoint(oarg);
+ if (options->metric < 0) {
+ logger(LOG_ERR, "metric must be a positive value");
+ return -1;
+ }
+ break;
+ case 'o':
+ if (make_option_mask(options->requestmask, &oarg, 1) != 0) {
+ logger(LOG_ERR, "unknown option `%s'", oarg);
+ return -1;
+ }
+ break;
+ case 'p':
+ options->options |= DHCPCD_PERSISTENT;
+ break;
+ case 'q':
+ setloglevel(LOG_WARNING);
+ break;
+ case 's':
+ options->options |= DHCPCD_INFORM;
+ options->options |= DHCPCD_PERSISTENT;
+ options->options &= ~DHCPCD_ARP;
+ if (!oarg || *oarg == '\0') {
+ options->request_address.s_addr = 0;
+ break;
+ } else {
+ if ((p = strchr(oarg, '/'))) {
+ /* nullify the slash, so the -r option
+ * can read the address */
+ *p++ = '\0';
+ if (sscanf(p, "%d", &i) != 1 ||
+ inet_cidrtoaddr(i, &options->request_netmask) != 0)
+ {
+ logger(LOG_ERR,
+ "`%s' is not a valid CIDR",
+ p);
+ return -1;
+ }
+ }
+ }
+ /* FALLTHROUGH */
+ case 'r':
+ if (!(options->options & DHCPCD_INFORM))
+ options->options |= DHCPCD_REQUEST;
+ if (oarg && !inet_aton(oarg, &options->request_address)) {
+ logger(LOG_ERR, "`%s' is not a valid IP address",
+ oarg);
+ return -1;
+ }
+ break;
+ case 't':
+ options->timeout = atoint(oarg);
+ if (options->timeout < 0) {
+ logger (LOG_ERR, "timeout must be a positive value");
+ return -1;
+ }
+ break;
+ case 'u':
+ s = USERCLASS_MAX_LEN - options->userclass[0] - 1;
+ s = parse_string((char *)options->userclass + options->userclass[0] + 2,
+ s, oarg);
+ if (s == -1) {
+ logger(LOG_ERR, "userclass: %s", strerror(errno));
+ return -1;
+ }
+ if (s != 0) {
+ options->userclass[options->userclass[0] + 1] = s;
+ options->userclass[0] += s + 1;
+ }
+ break;
+ case 'v':
+ p = strchr(oarg, ',');
+ if (!p || !p[1]) {
+ logger(LOG_ERR, "invalid vendor format");
+ return -1;
+ }
+ *p = '\0';
+ i = atoint(oarg);
+ oarg = p + 1;
+ if (i < 1 || i > 254) {
+ logger(LOG_ERR, "vendor option should be between"
+ " 1 and 254 inclusive");
+ return -1;
+ }
+ s = VENDOR_MAX_LEN - options->vendor[0] - 2;
+ if (inet_aton(oarg, &addr) == 1) {
+ if (s < 6) {
+ s = -1;
+ errno = ENOBUFS;
+ } else
+ memcpy(options->vendor + options->vendor[0] + 3,
+ &addr.s_addr, sizeof(addr.s_addr));
+ } else {
+ s = parse_string((char *)options->vendor + options->vendor[0] + 3,
+ s, oarg);
+ }
+ if (s == -1) {
+ logger(LOG_ERR, "vendor: %s", strerror(errno));
+ return -1;
+ }
+ if (s != 0) {
+ options->vendor[options->vendor[0] + 1] = i;
+ options->vendor[options->vendor[0] + 2] = s;
+ options->vendor[0] += s + 2;
+ }
+ break;
+ case 'A':
+ options->options &= ~DHCPCD_ARP;
+ /* IPv4LL requires ARP */
+ options->options &= ~DHCPCD_IPV4LL;
+ break;
+ case 'B':
+ options->options &= ~DHCPCD_DAEMONISE;
+ break;
+ case 'C':
+ /* Commas to spaces for shell */
+ while ((p = strchr(oarg, ',')))
+ *p = ' ';
+ s = strlen("skip_hooks=") + strlen(oarg) + 1;
+ p = xmalloc(sizeof(char) * s);
+ snprintf(p, s, "skip_hooks=%s", oarg);
+ add_environ(options, p, 0);
+ free(p);
+ break;
+ case 'D':
+ options->options |= DHCPCD_DUID;
+ break;
+ case 'E':
+ options->options |= DHCPCD_LASTLEASE;
+ break;
+ case 'F':
+ if (!oarg) {
+ options->fqdn = FQDN_BOTH;
+ break;
+ }
+ if (strcmp(oarg, "none") == 0)
+ options->fqdn = FQDN_NONE;
+ else if (strcmp(oarg, "ptr") == 0)
+ options->fqdn = FQDN_PTR;
+ else if (strcmp(oarg, "both") == 0)
+ options->fqdn = FQDN_BOTH;
+ else if (strcmp(oarg, "disable") == 0)
+ options->fqdn = FQDN_DISABLE;
+ else {
+ logger(LOG_ERR, "invalid value `%s' for FQDN",
+ oarg);
+ return -1;
+ }
+ break;
+ case 'G':
+ options->options &= ~DHCPCD_GATEWAY;
+ break;
+ case 'I':
+ /* Strings have a type of 0 */;
+ options->clientid[1] = 0;
+ if (oarg)
+ s = parse_string_hwaddr((char *)options->clientid + 1,
+ CLIENTID_MAX_LEN, oarg, 1);
+ else
+ s = 0;
+ if (s == -1) {
+ logger(LOG_ERR, "clientid: %s", strerror(errno));
+ return -1;
+ }
+ options->clientid[0] = (uint8_t)s;
+ if (s == 0) {
+ options->options &= ~DHCPCD_DUID;
+ options->options &= ~DHCPCD_CLIENTID;
+ }
+ break;
+ case 'K':
+ options->options &= ~DHCPCD_LINK;
+ break;
+ case 'L':
+ options->options &= ~DHCPCD_IPV4LL;
+ break;
+ case 'O':
+ if (make_option_mask(options->requestmask, &oarg, -1) != 0 ||
+ make_option_mask(options->requiremask, &oarg, -1) != 0 ||
+ make_option_mask(options->nomask, &oarg, 1) != 0)
+ {
+ logger(LOG_ERR, "unknown option `%s'", oarg);
+ return -1;
+ }
+ break;
+ case 'Q':
+ if (make_option_mask(options->requiremask, &oarg, 1) != 0 ||
+ make_option_mask(options->requestmask, &oarg, 1) != 0)
+ {
+ logger(LOG_ERR, "unknown option `%s'", oarg);
+ return -1;
+ }
+ break;
+ case 'X':
+ if (!inet_aton(oarg, &addr)) {
+ logger(LOG_ERR, "`%s' is not a valid IP address",
+ oarg);
+ return -1;
+ }
+ options->blacklist = xrealloc(options->blacklist,
+ sizeof(in_addr_t) * (options->blacklist_len + 1));
+ options->blacklist[options->blacklist_len] = addr.s_addr;
+ options->blacklist_len++;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+parse_config_line(const char *opt, char *line, struct options *options)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(longopts) / sizeof(longopts[0]); i++) {
+ if (!longopts[i].name ||
+ strcmp(longopts[i].name, opt) != 0)
+ continue;
+
+ if (longopts[i].has_arg == required_argument && !line) {
+ fprintf(stderr,
+ PACKAGE ": option requires an argument -- %s\n",
+ opt);
+ return -1;
+ }
+
+ return parse_option(longopts[i].val, line, options);
+ }
+
+ fprintf(stderr, PACKAGE ": unknown option -- %s\n", opt);
+ return -1;
+}
+
+#ifdef ANDROID
+void switchUser() {
+ gid_t groups[] = { AID_INET, AID_SHELL };
+ setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+
+ prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+ setgid(AID_DHCP);
+ setuid(AID_DHCP);
+
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap;
+ header.version = _LINUX_CAPABILITY_VERSION;
+ header.pid = 0;
+ cap.effective = cap.permitted =
+ (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
+ (1 << CAP_NET_BROADCAST) | (1 << CAP_NET_BIND_SERVICE);
+ cap.inheritable = 0;
+ capset(&header, &cap);
+}
+#endif /* ANDROID */
+
+int
+main(int argc, char **argv)
+{
+ struct options *options;
+ int opt;
+ int option_index = 0;
+ char *prefix;
+ pid_t pid;
+ int debug = 0;
+ int i, r;
+ int pid_fd = -1;
+ int sig = 0;
+ int retval = EXIT_FAILURE;
+ char *line, *option, *p, *buffer = NULL;
+ size_t len = 0;
+ FILE *f;
+ char *cf = NULL;
+ char *intf = NULL;
+ struct timespec ts;
+
+#ifdef ANDROID
+ switchUser();
+#endif
+ closefrom(3);
+ /* Saves calling fflush(stream) in the logger */
+ setlinebuf(stdout);
+ openlog(PACKAGE, LOG_PID, LOG_LOCAL0);
+ setlogprefix(PACKAGE ": ");
+
+ options = xzalloc(sizeof(*options));
+ options->options |= DHCPCD_CLIENTID | DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
+ options->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
+ options->timeout = DEFAULT_TIMEOUT;
+ strlcpy(options->script, SCRIPT, sizeof(options->script));
+
+ options->vendorclassid[0] = snprintf((char *)options->vendorclassid + 1,
+ VENDORCLASSID_MAX_LEN,
+ "%s %s", PACKAGE, VERSION);
+
+#ifdef CMDLINE_COMPAT
+ add_option_mask(options->requestmask, DHO_DNSSERVER);
+ add_option_mask(options->requestmask, DHO_DNSDOMAIN);
+ add_option_mask(options->requestmask, DHO_DNSSEARCH);
+ add_option_mask(options->requestmask, DHO_NISSERVER);
+ add_option_mask(options->requestmask, DHO_NISDOMAIN);
+ add_option_mask(options->requestmask, DHO_NTPSERVER);
+
+ /* If the duid file exists, then enable duid by default
+ * This means we don't break existing clients that easily :) */
+ if ((f = fopen(DUID, "r"))) {
+ options->options |= DHCPCD_DUID;
+ fclose(f);
+ }
+#endif
+
+ gethostname(options->hostname + 1, sizeof(options->hostname));
+ if (strcmp(options->hostname + 1, "(none)") == 0 ||
+ strcmp(options->hostname + 1, "localhost") == 0)
+ options->hostname[1] = '\0';
+ *options->hostname = strlen(options->hostname + 1);
+
+ while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS,
+ longopts, &option_index)) != -1)
+ {
+ switch (opt) {
+ case 0:
+ if (longopts[option_index].flag)
+ break;
+ logger(LOG_ERR, "option `%s' should set a flag",
+ longopts[option_index].name);
+ goto abort;
+ case 'f':
+ cf = optarg;
+ break;
+ case 'V':
+ print_options();
+ goto abort;
+ case '?':
+ usage();
+ goto abort;
+ }
+ }
+
+ if (doversion)
+ printf(""PACKAGE" "VERSION"\n%s\n", copyright);
+
+ if (dohelp)
+ usage();
+
+ if (optind < argc) {
+ if (strlen(argv[optind]) >= IF_NAMESIZE) {
+ logger(LOG_ERR,
+ "`%s' too long for an interface name (max=%d)",
+ argv[optind], IF_NAMESIZE);
+ goto abort;
+ }
+ strlcpy(options->interface, argv[optind],
+ sizeof(options->interface));
+ } else {
+ /* If only version was requested then exit now */
+ if (doversion || dohelp) {
+ retval = 0;
+ goto abort;
+ }
+
+ logger(LOG_ERR, "no interface specified");
+ goto abort;
+ }
+
+ /* Parse our options file */
+ f = fopen(cf ? cf : CONFIG, "r");
+ if (f) {
+ r = 1;
+ while ((get_line(&buffer, &len, f))) {
+ line = buffer;
+ while ((option = strsep(&line, " \t")))
+ if (*option != '\0')
+ break;
+ if (!option || *option == '\0' || *option == '#')
+ continue;
+ /* Trim leading whitespace */
+ if (line) {
+ while (*line != '\0' && (*line == ' ' || *line == '\t'))
+ line++;
+ }
+ /* Trim trailing whitespace */
+ if (line && *line) {
+ p = line + strlen(line) - 1;
+ while (p != line &&
+ (*p == ' ' || *p == '\t') &&
+ *(p - 1) != '\\')
+ *p-- = '\0';
+ }
+ if (strcmp(option, "interface") == 0) {
+ free(intf);
+ intf = xstrdup(line);
+ continue;
+ }
+ /* If we're in an interface block don't use these
+ * options unless it's for us */
+ if (intf && strcmp(intf, options->interface) != 0)
+ continue;
+ r = parse_config_line(option, line, options);
+ if (r != 1)
+ break;
+ }
+ free(buffer);
+ free(intf);
+ fclose(f);
+ if (r == 0)
+ usage();
+ if (r != 1)
+ goto abort;
+ } else {
+ if (errno != ENOENT || cf) {
+ logger(LOG_ERR, "fopen `%s': %s", cf ? cf : CONFIG,
+ strerror(errno));
+ goto abort;
+ }
+ }
+
+ optind = 0;
+ while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS,
+ longopts, &option_index)) != -1)
+ {
+ switch (opt) {
+ case 'd':
+ debug++;
+ switch (debug) {
+ case 1:
+ setloglevel(LOG_DEBUG);
+ break;
+ case 2:
+ options->options &= ~DHCPCD_DAEMONISE;
+ break;
+ }
+ break;
+ case 'f':
+ break;
+ case 'k':
+ sig = SIGHUP;
+ break;
+ case 'n':
+ sig = SIGALRM;
+ break;
+ case 'x':
+ sig = SIGTERM;
+ break;
+ case 'T':
+ options->options |= DHCPCD_TEST | DHCPCD_PERSISTENT;
+ break;
+#ifdef CMDLINE_COMPAT
+ case 'H': /* FALLTHROUGH */
+ case 'M':
+ del_option_mask(options->requestmask, DHO_MTU);
+ break;
+ case 'N':
+ del_option_mask(options->requestmask, DHO_NTPSERVER);
+ break;
+ case 'R':
+ del_option_mask(options->requestmask, DHO_DNSSERVER);
+ del_option_mask(options->requestmask, DHO_DNSDOMAIN);
+ del_option_mask(options->requestmask, DHO_DNSSEARCH);
+ break;
+ case 'S':
+ add_option_mask(options->requestmask, DHO_MSCSR);
+ break;
+ case 'Y':
+ del_option_mask(options->requestmask, DHO_NISSERVER);
+ del_option_mask(options->requestmask, DHO_NISDOMAIN);
+ break;
+#endif
+ default:
+ i = parse_option(opt, optarg, options);
+ if (i == 1)
+ break;
+ if (i == 0)
+ usage();
+ goto abort;
+ }
+ }
+
+#ifdef THERE_IS_NO_FORK
+ options->options &= ~DHCPCD_DAEMONISE;
+#endif
+
+#ifndef ANDROID
+ /* android runs us as user "dhcp" */
+ if (geteuid())
+ logger(LOG_WARNING, PACKAGE " will not work correctly unless"
+ " run as root");
+#endif
+
+ if (options->options & DHCPCD_TEST) {
+ if (options->options & DHCPCD_REQUEST ||
+ options->options & DHCPCD_INFORM) {
+ logger(LOG_ERR,
+ "cannot test with --inform or --request");
+ goto abort;
+ }
+
+ if (options->options & DHCPCD_LASTLEASE) {
+ logger(LOG_ERR, "cannot test with --lastlease");
+ goto abort;
+ }
+
+ if (sig != 0) {
+ logger(LOG_ERR,
+ "cannot test with --release or --renew");
+ goto abort;
+ }
+ }
+
+ prefix = xmalloc(sizeof(char) * (IF_NAMESIZE + 3));
+ snprintf(prefix, IF_NAMESIZE, "%s: ", options->interface);
+ setlogprefix(prefix);
+ snprintf(options->pidfile, sizeof(options->pidfile), PIDFILE,
+ options->interface);
+ free(prefix);
+
+ if (options->request_address.s_addr == 0 &&
+ (options->options & DHCPCD_INFORM ||
+ options->options & DHCPCD_REQUEST))
+ {
+ errno = 0;
+ if (get_address(options->interface,
+ &options->request_address,
+ &options->request_netmask) != 1)
+ {
+ if (errno)
+ logger(LOG_ERR, "get_address: %s",
+ strerror(errno));
+ else
+ logger(LOG_ERR, "no existing address");
+ goto abort;
+ }
+ }
+ if (IN_LINKLOCAL(ntohl(options->request_address.s_addr))) {
+ logger(LOG_ERR,
+ "you are not allowed to request a link local address");
+ goto abort;
+ }
+
+ chdir("/");
+ umask(022);
+
+ if (sig != 0 && !(options->options & DHCPCD_DAEMONISED)) {
+#ifdef ANDROID
+ char pidpropname[PROPERTY_KEY_MAX];
+ char pidpropval[PROPERTY_VALUE_MAX];
+
+ i = -1;
+ if (snprintf(pidpropname,
+ sizeof(pidpropname),
+ "dhcp.%s.pid", options->interface) >= PROPERTY_KEY_MAX) {
+ goto abort;
+ }
+ property_get(pidpropname, pidpropval, NULL);
+ if (strlen(pidpropval) == 0) {
+ goto abort;
+ }
+ pid = atoi(pidpropval);
+#else
+ i = -1;
+ pid = read_pid(options->pidfile);
+#endif
+ if (pid != 0)
+ logger(LOG_INFO, "sending signal %d to pid %d",
+ sig, pid);
+
+ if (!pid || (i = kill(pid, sig))) {
+ if (sig != SIGALRM)
+ logger(LOG_ERR, ""PACKAGE" not running");
+ unlink(options->pidfile);
+ }
+ if (i == 0) {
+ if (sig == SIGALRM) {
+ retval = EXIT_SUCCESS;
+ goto abort;
+ }
+ /* Spin until it exits */
+ logger(LOG_INFO, "waiting for pid %d to exit", pid);
+ ts.tv_sec = 0;
+ ts.tv_nsec = 100000000; /* 10th of a second */
+ for(i = 0; i < 100; i++) {
+ nanosleep(&ts, NULL);
+ if (read_pid(options->pidfile) == 0) {
+ retval = EXIT_SUCCESS;
+ break;
+ }
+ }
+ if (retval != EXIT_SUCCESS)
+ logger(LOG_ERR, "pid %d failed to exit", pid);
+ goto abort;
+ }
+ if (sig != SIGALRM)
+ goto abort;
+ }
+
+ if (!(options->options & DHCPCD_TEST) &&
+ !(options->options & DHCPCD_DAEMONISED))
+ {
+#ifdef ANDROID
+ char pidpropname[PROPERTY_KEY_MAX];
+ char pidpropval[PROPERTY_VALUE_MAX];
+#endif
+#ifndef ANDROID
+ if ((pid = read_pid(options->pidfile)) > 0 &&
+ kill(pid, 0) == 0)
+ {
+ logger(LOG_ERR, ""PACKAGE
+ " already running on pid %d (%s)",
+ pid, options->pidfile);
+ goto abort;
+ }
+#endif
+ pid_fd = open(options->pidfile,
+ O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
+ if (pid_fd == -1) {
+ logger(LOG_ERR, "open `%s': %s",
+ options->pidfile, strerror(errno));
+ goto abort;
+ }
+
+ /* Lock the file so that only one instance of dhcpcd runs
+ * on an interface */
+ if (flock(pid_fd, LOCK_EX | LOCK_NB) == -1) {
+ logger(LOG_ERR, "flock `%s': %s",
+ options->pidfile, strerror(errno));
+ goto abort;
+ }
+
+ if (set_cloexec(pid_fd) == -1)
+ goto abort;
+#ifdef ANDROID
+ if (snprintf(pidpropname,
+ sizeof(pidpropname),
+ "dhcp.%s.pid", options->interface) >= PROPERTY_KEY_MAX) {
+ goto abort;
+ }
+ if (snprintf(pidpropval, sizeof(pidpropval), "%d", getpid()) >= PROPERTY_VALUE_MAX) {
+ goto abort;
+ }
+ property_set(pidpropname, pidpropval);
+#else
+ writepid(pid_fd, getpid());
+#endif
+ logger(LOG_INFO, PACKAGE " " VERSION " starting");
+ }
+
+ /* Terminate the encapsulated options */
+ if (options->vendor[0]) {
+ options->vendor[0]++;
+ options->vendor[options->vendor[0]] = DHO_END;
+ }
+
+ if (dhcp_run(options, &pid_fd) == 0)
+ retval = EXIT_SUCCESS;
+
+abort:
+ /* If we didn't daemonise then we need to punt the pidfile now */
+ if (pid_fd > -1) {
+ close(pid_fd);
+ unlink(options->pidfile);
+ }
+ if (options->environ) {
+ len = 0;
+ while (options->environ[len])
+ free(options->environ[len++]);
+ free(options->environ);
+ }
+ free(options->blacklist);
+ free(options);
+ exit(retval);
+ /* NOTREACHED */
+}
diff --git a/dhcpcd.conf b/dhcpcd.conf
new file mode 100644
index 0000000..cce1795
--- /dev/null
+++ b/dhcpcd.conf
@@ -0,0 +1,13 @@
+# A sample configuration for dhcpcd.
+# See dhcpcd.conf(5) for details.
+
+# dhcpcd-run-hooks uses these options.
+option domain_name_servers, domain_name, domain_search, host_name
+
+# Most distros have ntp support.
+option ntp_servers
+
+# We should behave nicely on networks and respect their MTU.
+# However, a lot of buggy DHCP servers set invalid MTUs so this is not
+# enabled by default.
+#option interface_mtu
diff --git a/dhcpcd.conf.5 b/dhcpcd.conf.5
new file mode 100644
index 0000000..217ecba
--- /dev/null
+++ b/dhcpcd.conf.5
@@ -0,0 +1,155 @@
+.\" Copyright 2006-2008 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 August 18, 2008
+.Dt DHCPCD.CONF 5 SMM
+.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 background
+Background immediately.
+This is useful for startup scripts which don't disable link messages for
+carrier status.
+.It Ic clientid Ar string
+Change the default clientid sent from the interface hardware address.
+If the string is of the format 01:02:03 then it is encoded as hex.
+If not set then none is sent.
+.It Ic duid
+Generate an
+.Rs
+.%T "RFC 4361"
+.Re
+compliant clientid.
+This requires persistent storage and not all DHCP servers work with it so it's
+not enabled by default.
+The duid generated will be held in
+.Pa /system/etc/dhcpcd/dhcpcd.duid
+and should not be copied to other hosts.
+.It Ic hostname Ar name
+Sends specified
+.Ar hostname
+to the DHCP server so it can be registered in DNS. If
+.Ar hostname
+if a FQDN (ie, contains a .) then it will be encoded as such.
+.It Ic fqdn Op none | ptr | both
+none disables FQDN encoding, ptr just asks the DHCP server to update the PTR
+record of the host in DNS whereas both also updates the A record.
+The current hostname or the hostname specified using the
+.Ic hostname
+option must be a FQDN.
+.Nm dhcpcd
+itself never does any DNS updates.
+.Nm dhcpcd
+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 leasetime Ar seconds
+Request a leasetime of
+.Ar seconds .
+.It Ic noarp
+Don't send any ARP requests.
+This also disables IPv4LL.
+.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 .
+.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 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 options seperated by commas, spaces or more option lines.
+.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 seperated by commas, spaces or more require lines.
+.It Ic script Ar script
+Use
+.Ar script
+instead of the default
+.Pa /system/etc/dhcpcd/dhcpcd-run-hooks .
+.It Ic timeout Ar seconds
+The default timeout for waiting for a DHCP response is 30 seconds which may
+be too long or too short and can be changed here.
+.It Ic userclass Ar string
+Tag the DHCP messages with the userclass.
+You can specify more than one.
+.It Ic vendor Ar code , Ns Ar value
+Add an enscapulated vendor option.
+.Ar code
+should be between 1 and 254 inclusive.
+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"
+.It Ic vendorclassid Ar string
+Change the default vendorclassid sent from dhcpcd-version.
+If not set then none is sent.
+.El
+.Sh SEE ALSO
+.Xr dhcpcd-run-hooks 8 ,
+.Xr dhcpcd 8
+.Sh AUTHORS
+.An Roy Marples <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in
new file mode 100644
index 0000000..899aea3
--- /dev/null
+++ b/dhcpcd.conf.5.in
@@ -0,0 +1,155 @@
+.\" Copyright 2006-2008 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 August 18, 2008
+.Dt DHCPCD.CONF 5 SMM
+.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 background
+Background immediately.
+This is useful for startup scripts which don't disable link messages for
+carrier status.
+.It Ic clientid Ar string
+Change the default clientid sent from the interface hardware address.
+If the string is of the format 01:02:03 then it is encoded as hex.
+If not set then none is sent.
+.It Ic duid
+Generate an
+.Rs
+.%T "RFC 4361"
+.Re
+compliant clientid.
+This requires persistent storage and not all DHCP servers work with it so it's
+not enabled by default.
+The duid generated will be held in
+.Pa @SYSCONFDIR@/dhcpcd.duid
+and should not be copied to other hosts.
+.It Ic hostname Ar name
+Sends specified
+.Ar hostname
+to the DHCP server so it can be registered in DNS. If
+.Ar hostname
+if a FQDN (ie, contains a .) then it will be encoded as such.
+.It Ic fqdn Op none | ptr | both
+none disables FQDN encoding, ptr just asks the DHCP server to update the PTR
+record of the host in DNS whereas both also updates the A record.
+The current hostname or the hostname specified using the
+.Ic hostname
+option must be a FQDN.
+.Nm dhcpcd
+itself never does any DNS updates.
+.Nm dhcpcd
+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 leasetime Ar seconds
+Request a leasetime of
+.Ar seconds .
+.It Ic noarp
+Don't send any ARP requests.
+This also disables IPv4LL.
+.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 .
+.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 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 options seperated by commas, spaces or more option lines.
+.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 seperated by commas, spaces or more require lines.
+.It Ic script Ar script
+Use
+.Ar script
+instead of the default
+.Pa @SCRIPT@ .
+.It Ic timeout Ar seconds
+The default timeout for waiting for a DHCP response is 30 seconds which may
+be too long or too short and can be changed here.
+.It Ic userclass Ar string
+Tag the DHCP messages with the userclass.
+You can specify more than one.
+.It Ic vendor Ar code , Ns Ar value
+Add an enscapulated vendor option.
+.Ar code
+should be between 1 and 254 inclusive.
+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"
+.It Ic vendorclassid Ar string
+Change the default vendorclassid sent from dhcpcd-version.
+If not set then none is sent.
+.El
+.Sh SEE ALSO
+.Xr dhcpcd-run-hooks 8 ,
+.Xr dhcpcd 8
+.Sh AUTHORS
+.An Roy Marples <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd.h b/dhcpcd.h
new file mode 100644
index 0000000..1cd2b5d
--- /dev/null
+++ b/dhcpcd.h
@@ -0,0 +1,95 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DHCPCD_H
+#define DHCPCD_H
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <limits.h>
+
+#include "common.h"
+
+#define DEFAULT_TIMEOUT 30
+#define DEFAULT_LEASETIME 3600 /* 1 hour */
+
+#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
+#define VENDORCLASSID_MAX_LEN 48
+#define CLIENTID_MAX_LEN 48
+#define USERCLASS_MAX_LEN 255
+#define VENDOR_MAX_LEN 255
+
+#define DHCPCD_ARP (1 << 0)
+#define DHCPCD_DOMAIN (1 << 2)
+#define DHCPCD_GATEWAY (1 << 3)
+#define DHCPCD_LASTLEASE (1 << 7)
+#define DHCPCD_INFORM (1 << 8)
+#define DHCPCD_REQUEST (1 << 9)
+#define DHCPCD_IPV4LL (1 << 10)
+#define DHCPCD_DUID (1 << 11)
+#define DHCPCD_PERSISTENT (1 << 12)
+#define DHCPCD_DAEMONISE (1 << 14)
+#define DHCPCD_DAEMONISED (1 << 15)
+#define DHCPCD_TEST (1 << 16)
+#define DHCPCD_FORKED (1 << 17)
+#define DHCPCD_HOSTNAME (1 << 18)
+#define DHCPCD_CLIENTID (1 << 19)
+#define DHCPCD_LINK (1 << 20)
+#define DHCPCD_BACKGROUND (1 << 21)
+
+struct options {
+ char interface[IF_NAMESIZE];
+ int metric;
+ uint8_t requestmask[256 / 8];
+ uint8_t requiremask[256 / 8];
+ uint8_t nomask[256 / 8];
+ uint32_t leasetime;
+ time_t timeout;
+ int options;
+
+ struct in_addr request_address;
+ struct in_addr request_netmask;
+
+ char **environ;
+ char script[PATH_MAX];
+ char pidfile[PATH_MAX];
+
+ char hostname[HOSTNAME_MAX_LEN + 1];
+ int fqdn;
+ uint8_t vendorclassid[VENDORCLASSID_MAX_LEN + 1];
+ char clientid[CLIENTID_MAX_LEN + 1];
+ uint8_t userclass[USERCLASS_MAX_LEN + 1];
+ uint8_t vendor[VENDOR_MAX_LEN + 1];
+
+ size_t blacklist_len;
+ in_addr_t *blacklist;
+};
+#endif
diff --git a/if-bsd.c b/if-bsd.c
new file mode 100644
index 0000000..bbf1a95
--- /dev/null
+++ b/if-bsd.c
@@ -0,0 +1,241 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "net.h"
+
+/* Darwin doesn't define this for some very odd reason */
+#ifndef SA_SIZE
+# define SA_SIZE(sa) \
+ ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
+ sizeof(long) : \
+ 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
+#endif
+
+int
+if_address(const char *ifname, const struct in_addr *address,
+ const struct in_addr *netmask, const struct in_addr *broadcast,
+ int action)
+{
+ int s;
+ int retval;
+ struct ifaliasreq ifa;
+ union {
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ } _s;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return -1;
+
+ memset(&ifa, 0, sizeof(ifa));
+ strlcpy(ifa.ifra_name, ifname, sizeof(ifa.ifra_name));
+
+#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) {
+ ADDADDR(ifa.ifra_broadaddr, broadcast);
+ }
+#undef ADDADDR
+
+ if (action < 0)
+ retval = ioctl(s, SIOCDIFADDR, &ifa);
+ else
+ retval = ioctl(s, SIOCAIFADDR, &ifa);
+ close(s);
+ return retval;
+}
+
+int
+if_route(const char *ifname, const struct in_addr *destination,
+ const struct in_addr *netmask, const struct in_addr *gateway,
+ _unused int metric, int action)
+{
+ int s;
+ static int seq;
+ union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+#ifdef INET6
+ struct sockaddr_in6 sin6;
+#endif
+ struct sockaddr_dl sdl;
+ struct sockaddr_storage ss;
+ } su;
+ struct rtm
+ {
+ struct rt_msghdr hdr;
+ char buffer[sizeof(su) * 3];
+ } rtm;
+ char *bp = rtm.buffer;
+ size_t l;
+ unsigned char *hwaddr;
+ size_t hwlen = 0;
+ int retval = 0;
+
+ if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
+ return -1;
+
+ memset(&rtm, 0, sizeof(rtm));
+ rtm.hdr.rtm_version = RTM_VERSION;
+ rtm.hdr.rtm_seq = ++seq;
+ 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 | RTF_STATIC;
+ if (netmask->s_addr == INADDR_BROADCAST)
+ rtm.hdr.rtm_flags |= RTF_HOST;
+
+ /* This order is important */
+ rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+
+#define ADDADDR(_addr) \
+ memset (&su, 0, sizeof(su)); \
+ su.sin.sin_family = AF_INET; \
+ su.sin.sin_len = sizeof(su.sin); \
+ memcpy (&su.sin.sin_addr, _addr, sizeof(su.sin.sin_addr)); \
+ l = SA_SIZE (&(su.sa)); \
+ memcpy (bp, &(su), l); \
+ bp += l;
+
+ ADDADDR(destination);
+
+ if (gateway->s_addr == INADDR_ANY) {
+ /* Make us a link layer socket */
+ hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
+ do_interface(ifname, hwaddr, &hwlen, NULL, 0, 0);
+ memset(&su, 0, sizeof(su));
+ su.sdl.sdl_len = sizeof(su.sdl);
+ su.sdl.sdl_family = AF_LINK;
+ su.sdl.sdl_nlen = strlen(ifname);
+ memcpy(&su.sdl.sdl_data, ifname, (size_t)su.sdl.sdl_nlen);
+ su.sdl.sdl_alen = hwlen;
+ memcpy(((unsigned char *)&su.sdl.sdl_data) + su.sdl.sdl_nlen,
+ hwaddr, (size_t)su.sdl.sdl_alen);
+
+ l = SA_SIZE(&(su.sa));
+ memcpy(bp, &su, l);
+ bp += l;
+ free(hwaddr);
+ } else {
+ rtm.hdr.rtm_flags |= RTF_GATEWAY;
+ ADDADDR(gateway);
+ }
+
+ ADDADDR(netmask);
+#undef ADDADDR
+
+ rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
+ if (write(s, &rtm, l) == -1)
+ retval = -1;
+ close(s);
+ return retval;
+}
+
+int
+open_link_socket(struct interface *iface)
+{
+ int fd;
+
+ fd = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (fd == -1)
+ return -1;
+ set_cloexec(fd);
+ if (iface->link_fd != -1)
+ close(iface->link_fd);
+ iface->link_fd = fd;
+ return 0;
+}
+
+#define BUFFER_LEN 2048
+int
+link_changed(struct interface *iface)
+{
+ char buffer[2048], *p;
+ ssize_t bytes;
+ struct rt_msghdr *rtm;
+ struct if_msghdr *ifm;
+ int i;
+
+ if ((i = if_nametoindex(iface->name)) == -1)
+ return -1;
+ for (;;) {
+ bytes = recv(iface->link_fd, buffer, BUFFER_LEN, MSG_DONTWAIT);
+ if (bytes == -1) {
+ if (errno == EAGAIN)
+ return 0;
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+ for (p = buffer; bytes > 0;
+ bytes -= ((struct rt_msghdr *)p)->rtm_msglen,
+ p += ((struct rt_msghdr *)p)->rtm_msglen)
+ {
+ rtm = (struct rt_msghdr *)p;
+ if (rtm->rtm_type != RTM_IFINFO)
+ continue;
+ ifm = (struct if_msghdr *)p;
+ if (ifm->ifm_index != i)
+ continue;
+
+ /* Link changed */
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/if-linux.c b/if-linux.c
new file mode 100644
index 0000000..1009270
--- /dev/null
+++ b/if-linux.c
@@ -0,0 +1,379 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <asm/types.h> /* Needed for 2.4 kernels */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Support older kernels */
+#ifndef IFLA_WIRELESS
+# define IFLA_WIRELSSS (IFLFA_MASTER + 1)
+#endif
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "net.h"
+
+#define BUFFERLEN 256
+
+int
+open_link_socket(struct interface *iface)
+{
+ int fd;
+ struct sockaddr_nl nl;
+
+ if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
+ return -1;
+ memset(&nl, 0, sizeof(nl));
+ nl.nl_family = AF_NETLINK;
+ nl.nl_groups = RTMGRP_LINK;
+ if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1)
+ return -1;
+ set_cloexec(fd);
+ if (iface->link_fd != -1)
+ close(iface->link_fd);
+ iface->link_fd = fd;
+ return 0;
+}
+
+static int
+get_netlink(int fd, int flags,
+ int (*callback)(struct nlmsghdr *, const char *),
+ const char *ifname)
+{
+ char *buffer = NULL;
+ ssize_t bytes;
+ struct nlmsghdr *nlm;
+ int r = -1;
+
+ buffer = xzalloc(sizeof(char) * BUFFERLEN);
+ for (;;) {
+ bytes = recv(fd, buffer, BUFFERLEN, flags);
+ if (bytes == -1) {
+ if (errno == EAGAIN) {
+ r = 0;
+ goto eexit;
+ }
+ if (errno == EINTR)
+ continue;
+ goto eexit;
+ }
+ for (nlm = (struct nlmsghdr *)buffer;
+ NLMSG_OK(nlm, (size_t)bytes);
+ nlm = NLMSG_NEXT(nlm, bytes))
+ {
+ r = callback(nlm, ifname);
+ if (r != 0)
+ goto eexit;
+ }
+ }
+
+eexit:
+ free(buffer);
+ return r;
+}
+
+static int
+err_netlink(struct nlmsghdr *nlm, _unused const char *ifname)
+{
+ 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_netlink(struct nlmsghdr *nlm, const char *ifname)
+{
+ int len;
+ struct rtattr *rta;
+ struct ifinfomsg *ifi;
+ char ifn[IF_NAMESIZE + 1];
+
+ 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 0;
+ rta = (struct rtattr *) ((char *)ifi + NLMSG_ALIGN(sizeof(*ifi)));
+ len = NLMSG_PAYLOAD(nlm, sizeof(*ifi));
+ *ifn = '\0';
+ 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 0;
+ break;
+ case IFLA_IFNAME:
+ strlcpy(ifn, RTA_DATA(rta), sizeof(ifn));
+ break;
+ }
+ rta = RTA_NEXT(rta, len);
+ }
+
+ if (strncmp(ifname, ifn, sizeof(ifn)) == 0)
+ return 1;
+ return 0;
+}
+
+int
+link_changed(struct interface *iface)
+{
+ return get_netlink(iface->link_fd, MSG_DONTWAIT,
+ &link_netlink, iface->name);
+}
+
+static int
+send_netlink(struct nlmsghdr *hdr)
+{
+ int fd, r;
+ struct sockaddr_nl nl;
+ struct iovec iov;
+ struct msghdr msg;
+ static unsigned int seq;
+
+ if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
+ return -1;
+ memset(&nl, 0, sizeof(nl));
+ nl.nl_family = AF_NETLINK;
+ if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) {
+ close(fd);
+ return -1;
+ }
+ memset(&iov, 0, sizeof(iov));
+ iov.iov_base = hdr;
+ iov.iov_len = hdr->nlmsg_len;
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = &nl;
+ msg.msg_namelen = sizeof(nl);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ /* Request a reply */
+ hdr->nlmsg_flags |= NLM_F_ACK;
+ hdr->nlmsg_seq = ++seq;
+
+ if (sendmsg(fd, &msg, 0) != -1)
+ r = get_netlink(fd, 0, &err_netlink, NULL);
+ else
+ r = -1;
+ close(fd);
+ 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];
+};
+
+int
+if_address(const char *ifname,
+ const struct in_addr *address, const struct in_addr *netmask,
+ const struct in_addr *broadcast, int action)
+{
+ struct nlma *nlm;
+ int retval = 0;
+
+ nlm = xzalloc(sizeof(*nlm));
+ 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;
+ if (!(nlm->ifa.ifa_index = if_nametoindex(ifname))) {
+ free(nlm);
+ errno = ENODEV;
+ return -1;
+ }
+ 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,
+ ifname, strlen(ifname) + 1);
+ add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL,
+ &address->s_addr, sizeof(address->s_addr));
+ if (action >= 0)
+ 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 char *ifname,
+ const struct in_addr *destination, const struct in_addr *netmask,
+ const struct in_addr *gateway, int metric, int action)
+{
+ struct nlmr *nlm;
+ unsigned int ifindex;
+ int retval = 0;
+
+
+ if (!(ifindex = if_nametoindex(ifname))) {
+ errno = ENODEV;
+ return -1;
+ }
+
+ nlm = xzalloc(sizeof(*nlm));
+ 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 > 0)
+ /*
+ * ers@google:
+ * commented out NLM_F_EXCL here and below. We
+ * sometimes keep one interface up while we are
+ * configuring the other one, and this flag
+ * causes route addition to fail.
+ */
+ nlm->hdr.nlmsg_flags = NLM_F_CREATE /* | NLM_F_EXCL */;
+ 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;
+
+ if (action < 0)
+ nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
+ else {
+ nlm->hdr.nlmsg_flags |= NLM_F_CREATE /*| NLM_F_EXCL*/;
+ nlm->rt.rtm_protocol = RTPROT_BOOT;
+ if (gateway->s_addr == INADDR_ANY)
+ 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(*netmask);
+ add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
+ &destination->s_addr, sizeof(destination->s_addr));
+ add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
+ &gateway->s_addr, sizeof(gateway->s_addr));
+
+ add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex);
+ add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric);
+
+ if (send_netlink(&nlm->hdr) == -1)
+ retval = -1;
+ free(nlm);
+ return retval;
+}
diff --git a/logger.c b/logger.c
new file mode 100644
index 0000000..15c6cf7
--- /dev/null
+++ b/logger.c
@@ -0,0 +1,89 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "common.h"
+#include "logger.h"
+
+static int loglevel = LOG_INFO;
+static char logprefix[12] = {0};
+
+void
+setloglevel(int level)
+{
+ loglevel = level;
+}
+
+void
+setlogprefix(const char *prefix)
+{
+ strlcpy(logprefix, prefix, sizeof(logprefix));
+}
+
+void
+logger(int level, const char *fmt, ...)
+{
+ va_list p, p2;
+ FILE *f = stderr;
+ size_t len, fmt2len;
+ char *fmt2, *pf;
+
+ va_start(p, fmt);
+ va_copy(p2, p);
+
+ if (level <= LOG_ERR || level <= loglevel) {
+ fprintf(f, "%s", logprefix);
+ vfprintf(f, fmt, p);
+ fputc('\n', f);
+ }
+
+ if (level < LOG_DEBUG || level <= loglevel) {
+ len = strlen(logprefix);
+ fmt2len = strlen(fmt) + len + 1;
+ fmt2 = pf = malloc(sizeof(char) * fmt2len);
+ if (fmt2) {
+ strlcpy(pf, logprefix, fmt2len);
+ pf += len;
+ strlcpy(pf, fmt, fmt2len - len);
+ vsyslog(level, fmt2, p2);
+ free(fmt2);
+ } else {
+ vsyslog(level, fmt, p2);
+ syslog(LOG_ERR, "logger: memory exhausted");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ va_end(p2);
+ va_end(p);
+}
diff --git a/logger.h b/logger.h
new file mode 100644
index 0000000..8ac76b6
--- /dev/null
+++ b/logger.h
@@ -0,0 +1,43 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#if defined(__GNUC__)
+# define _PRINTF_LIKE(_one, _two) __attribute__ ((__format__ (__printf__, _one, _two)))
+#else
+# define _PRINTF_LIKE(_one, _two)
+#endif
+
+#include <syslog.h>
+
+void setloglevel(int);
+void setlogprefix(const char *);
+void logger(int, const char *, ...) _PRINTF_LIKE (2, 3);
+
+#endif
diff --git a/lpf.c b/lpf.c
new file mode 100644
index 0000000..ae5dd03
--- /dev/null
+++ b/lpf.c
@@ -0,0 +1,168 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef __linux__
+# include <asm/types.h> /* needed for 2.4 kernels for the below header */
+# include <linux/filter.h>
+# include <netpacket/packet.h>
+# define bpf_insn sock_filter
+# define BPF_SKIPTYPE
+# define BPF_ETHCOOK -ETH_HLEN
+# define BPF_WHOLEPACKET 0x0fffffff /* work around buggy LPF filters */
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "net.h"
+#include "bpf-filter.h"
+
+/* Broadcast address for IPoIB */
+static const uint8_t ipv4_bcast_addr[] = {
+ 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0x12, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+};
+
+int
+open_socket(struct interface *iface, int protocol)
+{
+ int s;
+ union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_ll sll;
+ struct sockaddr_storage ss;
+ } su;
+ struct sock_fprog pf;
+ int *fd;
+
+ if ((s = socket(PF_PACKET, SOCK_DGRAM, htons(protocol))) == -1)
+ return -1;
+
+ memset(&su, 0, sizeof(su));
+ su.sll.sll_family = PF_PACKET;
+ su.sll.sll_protocol = htons(protocol);
+ if (!(su.sll.sll_ifindex = if_nametoindex(iface->name))) {
+ errno = ENOENT;
+ goto eexit;
+ }
+ /* Install the DHCP filter */
+ memset(&pf, 0, sizeof(pf));
+ if (protocol == ETHERTYPE_ARP) {
+ pf.filter = UNCONST(arp_bpf_filter);
+ pf.len = arp_bpf_filter_len;
+ } else {
+ pf.filter = UNCONST(dhcp_bpf_filter);
+ pf.len = dhcp_bpf_filter_len;
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) != 0)
+ goto eexit;
+ if (set_cloexec(s) == -1)
+ goto eexit;
+ if (set_nonblock(s) == -1)
+ goto eexit;
+ if (bind(s, &su.sa, sizeof(su)) == -1)
+ goto eexit;
+ if (protocol == ETHERTYPE_ARP)
+ fd = &iface->arp_fd;
+ else
+ fd = &iface->raw_fd;
+ if (*fd != -1)
+ close(*fd);
+ *fd = s;
+ return s;
+
+eexit:
+ close(s);
+ return -1;
+}
+
+ssize_t
+send_raw_packet(const struct interface *iface, int protocol,
+ const void *data, ssize_t len)
+{
+ union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_ll sll;
+ struct sockaddr_storage ss;
+ } su;
+ int fd;
+
+ memset(&su, 0, sizeof(su));
+ su.sll.sll_family = AF_PACKET;
+ su.sll.sll_protocol = htons(protocol);
+ if (!(su.sll.sll_ifindex = if_nametoindex(iface->name))) {
+ errno = ENOENT;
+ return -1;
+ }
+ su.sll.sll_hatype = htons(iface->family);
+ su.sll.sll_halen = iface->hwlen;
+ if (iface->family == ARPHRD_INFINIBAND)
+ memcpy(&su.sll.sll_addr,
+ &ipv4_bcast_addr, sizeof(ipv4_bcast_addr));
+ else
+ memset(&su.sll.sll_addr, 0xff, iface->hwlen);
+ if (protocol == ETHERTYPE_ARP)
+ fd = iface->arp_fd;
+ else
+ fd = iface->raw_fd;
+
+ return sendto(fd, data, len, 0, &su.sa, sizeof(su));
+}
+
+ssize_t
+get_raw_packet(struct interface *iface, int protocol, void *data, ssize_t len)
+{
+ ssize_t bytes;
+ int fd = -1;
+
+ if (protocol == ETHERTYPE_ARP)
+ fd = iface->arp_fd;
+ else
+ fd = iface->raw_fd;
+ bytes = read(fd, data, len);
+ if (bytes == -1)
+ return errno == EAGAIN ? 0 : -1;
+ return bytes;
+}
diff --git a/mk/cc.mk b/mk/cc.mk
new file mode 100644
index 0000000..fcf0535
--- /dev/null
+++ b/mk/cc.mk
@@ -0,0 +1,28 @@
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+# Setup some good default CFLAGS
+CFLAGS?= -Os
+
+# Default to using the C99 standard
+CSTD?= c99
+_CSTD_SH= if test -n "${CSTD}"; then echo "-std=${CSTD}"; else echo ""; fi
+_CSTD!= ${_CSTD_SH}
+CFLAGS+= ${_CSTD}$(shell ${_CSTD_SH})
+
+# Try and use some good cc flags if we're building from git
+_CCFLAGS= -pedantic -Wall -Wunused -Wimplicit -Wshadow -Wformat=2 \
+ -Wmissing-declarations -Wno-missing-prototypes -Wwrite-strings \
+ -Wbad-function-cast -Wnested-externs -Wcomment -Winline \
+ -Wchar-subscripts -Wcast-align -Wno-format-nonliteral \
+ -Wdeclaration-after-statement -Wsequence-point -Wextra
+_CC_FLAGS_SH= if ! test -d .git; then echo ""; else for f in ${_CCFLAGS}; do \
+ if ${CC} $$f -S -o /dev/null -xc /dev/null >/dev/null 2>&1; \
+ then printf "%s" "$$f "; fi \
+ done; fi
+_CC_FLAGS!= ${_CC_FLAGS_SH}
+CFLAGS+= ${_CC_FLAGS}$(shell ${_CC_FLAGS_SH})
+
+_GGDB_SH= if test "${DEBUG}" = "yes"; then echo "-ggdb -DDEBUG"; else echo ""; fi
+_GGDB!= ${_GGDB_SH}
+GGDB= ${_GGDB}$(shell ${_GGDB_SH})
+CFLAGS+= ${GGDB}
diff --git a/mk/depend.mk b/mk/depend.mk
new file mode 100644
index 0000000..a4d717a
--- /dev/null
+++ b/mk/depend.mk
@@ -0,0 +1,11 @@
+# This only works for make implementations that always include a .depend if
+# it exists. Only GNU make does not do this.
+
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+CLEANFILES+= .depend
+
+.depend: ${SRCS}
+ ${CC} ${CFLAGS} -MM ${SRCS} > .depend
+
+depend: .depend
diff --git a/mk/dist.mk b/mk/dist.mk
new file mode 100644
index 0000000..3d9385b
--- /dev/null
+++ b/mk/dist.mk
@@ -0,0 +1,31 @@
+# rules to make a distribution tarball from a git repo
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+GITREF?= HEAD
+DISTPREFIX?= ${PROG}-${VERSION}
+DISTFILE?= ${DISTPREFIX}.tar.bz2
+
+CLEANFILES+= *.tar.bz2
+
+_VERSION_SH= sed -n 's/\#define VERSION[[:space:]]*"\(.*\)".*/\1/p' config.h
+_VERSION!= ${_VERSION_SH}
+VERSION= ${_VERSION}$(shell ${_VERSION_SH})
+
+_SNAP_SH= date -u +%Y%m%d%H%M
+_SNAP!= ${_SNAP_SH}
+SNAP= ${_SNAP}$(shell ${_SNAP_SH})
+SNAPDIR= ${DISTPREFIX}-${SNAP}
+SNAPFILE= ${SNAPDIR}.tar.bz2
+
+dist:
+ git archive --prefix=${DISTPREFIX}/ ${GITREF} | bzip2 > ${DISTFILE}
+
+snapshot:
+ mkdir /tmp/${SNAPDIR}
+ cp -RPp * /tmp/${SNAPDIR}
+ (cd /tmp/${SNAPDIR}; make clean)
+ tar -cvjpf ${SNAPFILE} -C /tmp ${SNAPDIR}
+ rm -rf /tmp/${SNAPDIR}
+ ls -l ${SNAPFILE}
+
+snap: snapshot
diff --git a/mk/files.mk b/mk/files.mk
new file mode 100644
index 0000000..0682b03
--- /dev/null
+++ b/mk/files.mk
@@ -0,0 +1,9 @@
+# Quick and dirty files
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+FILESDIR?= ${BINDIR}
+FILESMODE?= ${NONBINMODE}
+
+_filesinstall:
+ ${INSTALL} -d ${DESTDIR}${FILESDIR}
+ ${INSTALL} -m ${FILESMODE} ${FILES} ${DESTDIR}${FILESDIR}
diff --git a/mk/man.mk b/mk/man.mk
new file mode 100644
index 0000000..f1570bb
--- /dev/null
+++ b/mk/man.mk
@@ -0,0 +1,25 @@
+# rules to install manpages
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+_MANPREFIX_SH= if [ -n "${PREFIX}" ]; then echo "${PREFIX}"; else echo "/usr/share"; fi
+_MANPREFIX!= ${_MANPREFIX_SH}
+MANPREFIX?= ${_MANPREFIX}$(shell ${_MANPREFIX_SH})
+
+MANDIR?= ${MANPREFIX}/man/man
+MANMODE?= 0444
+
+_MAN5_SH= for man in ${MAN}; do case $$man in *.5) echo $$man;; esac; done
+_MAN5!= ${_MAN5_SH}
+MAN5= ${_MAN5}$(shell ${_MAN5_SH})
+
+_MAN8_SH= for man in ${MAN}; do case $$man in *.8) echo $$man;; esac; done
+_MAN8!= ${_MAN8_SH}
+MAN8= ${_MAN8}$(shell ${_MAN8_SH})
+
+_man: ${MAN}
+
+_maninstall: _man
+ ${INSTALL} -d ${DESTDIR}${MANDIR}5
+ ${INSTALL} -m ${MANMODE} ${MAN5} ${DESTDIR}${MANDIR}5
+ ${INSTALL} -d ${DESTDIR}${MANDIR}8
+ ${INSTALL} -m ${MANMODE} ${MAN8} ${DESTDIR}${MANDIR}8
diff --git a/mk/os-BSD.mk b/mk/os-BSD.mk
new file mode 100644
index 0000000..f9d3397
--- /dev/null
+++ b/mk/os-BSD.mk
@@ -0,0 +1,5 @@
+# Setup OS specific variables
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+SRC_PF= bpf.c
+SRC_IF= if-bsd.c
diff --git a/mk/os-Darwin.mk b/mk/os-Darwin.mk
new file mode 100644
index 0000000..f2c3104
--- /dev/null
+++ b/mk/os-Darwin.mk
@@ -0,0 +1,5 @@
+# Setup OS specific variables
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+include ${MK}/os-BSD.mk
+LINK_RPATH= --rpath
diff --git a/mk/os-Linux.mk b/mk/os-Linux.mk
new file mode 100644
index 0000000..2d316b1
--- /dev/null
+++ b/mk/os-Linux.mk
@@ -0,0 +1,8 @@
+# Setup OS specific variables
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+SRC_PF= lpf.c
+SRC_IF= if-linux.c
+
+CPPFLAGS+= -D_BSD_SOURCE -D_XOPEN_SOURCE=600
+LIBRT= -lrt
diff --git a/mk/os.mk b/mk/os.mk
new file mode 100644
index 0000000..f3426e4
--- /dev/null
+++ b/mk/os.mk
@@ -0,0 +1,7 @@
+# Setup OS specific variables
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+_OS_SH= case `uname -s` in Linux) echo "Linux";; Darwin) echo "Darwin";; *) echo "BSD";; esac
+_OS!= ${_OS_SH}
+OS= ${_OS}$(shell ${_OS_SH})
+include ${MK}/os-${OS}.mk
diff --git a/mk/prog.mk b/mk/prog.mk
new file mode 100644
index 0000000..d970b2d
--- /dev/null
+++ b/mk/prog.mk
@@ -0,0 +1,68 @@
+# rules to build a program
+# based on FreeBSD's bsd.prog.mk
+
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+include ${MK}/cc.mk
+
+OBJS+= ${SRCS:.c=.o}
+
+# If building for /, ensure we use the libc in / if different from
+# the default one in /usr/lib
+LINK_RPATH?= -Wl,-rpath
+_RPATH_SH= if test "${PREFIX}" = "" -o "${PREIX}" = "/"; then \
+ echo "${LINK_RPATH}=${PREFIX}/${LIBNAME}"; \
+ else \
+ echo ""; \
+ fi
+_RPATH!= ${_RPATH_SH}
+LDFLAGS+= ${_RPATH}$(shell ${_RPATH_SH})
+
+# If building for /, ensure we use the linker in /libexec if different from
+# the default one in /usr/libexec
+_DYNLINK_SH= if test "${PREFIX}" = "" -o "${PREFIX}" = "/" && test -e /libexec/ld.elf_so; then \
+ echo "-Wl,-dynamic-linker=/libexec/ld.elf_so"; \
+ else \
+ echo ""; \
+ fi
+_DYNLINK!= ${_DYNLINK_SH}
+LDFLAGS+= ${_DYNLINK}$(shell ${_DYNLINK_SH})
+
+all: ${PROG} ${SCRIPTS} _man
+
+.c.o:
+ ${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
+
+${PROG}: ${OBJS}
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}
+
+# We could save about 600 bytes by building it like this
+# instead of the more traditional method above
+small: ${SRCS}
+ echo "" > _${PROG}.c
+ for src in ${SRCS}; do echo "#include \"$$src\"" >> _${PROG}.c; done
+ ${CC} ${CFLAGS} ${CPPFLAGS} -c _${PROG}.c -o _${PROG}.o
+ ${CC} ${LDFLAGS} -o ${PROG} _${PROG}.o ${LDADD}
+
+_proginstall: ${PROG}
+ ${INSTALL} -d ${DESTDIR}${BINDIR}
+ ${INSTALL} -m ${BINMODE} ${PROG} ${DESTDIR}${BINDIR}
+ ${INSTALL} -d ${DESTDIR}${DBDIR}
+
+include ${MK}/depend.mk
+include ${MK}/files.mk
+include ${MK}/scripts.mk
+include ${MK}/man.mk
+include ${MK}/dist.mk
+
+install: _proginstall _scriptsinstall _filesinstall _maninstall
+ for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@; cd ..; done
+
+clean:
+ rm -f ${OBJS} ${PROG} _${PROG}.c _${PROG}.o ${PROG}.core ${CLEANFILES}
+
+LINTFLAGS?= -hx
+LINTFLAGS+= -X 159,247,352
+
+lint: ${SRCS:.c=.c}
+ ${LINT} ${LINTFLAGS} ${CFLAGS:M-[DIU]*} $^ ${.ALLSRC}
diff --git a/mk/scripts.mk b/mk/scripts.mk
new file mode 100644
index 0000000..d295163
--- /dev/null
+++ b/mk/scripts.mk
@@ -0,0 +1,9 @@
+# Quick and dirty scripts
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+SCRIPTSDIR?= ${BINDIR}
+SCRIPTSMODE?= ${BINMODE}
+
+_scriptsinstall: ${SCRIPTS}
+ ${INSTALL} -d ${DESTDIR}${SCRIPTSDIR}
+ ${INSTALL} -m ${SCRIPTSMODE} ${SCRIPTS} ${DESTDIR}${SCRIPTSDIR}
diff --git a/mk/sys.mk b/mk/sys.mk
new file mode 100644
index 0000000..81882ab
--- /dev/null
+++ b/mk/sys.mk
@@ -0,0 +1,14 @@
+# Simple defaults
+
+BINDIR?= ${PREFIX}/usr/bin
+BINMODE?= 0755
+NONBINMODE?= 0644
+
+SYSCONFDIR?= ${PREFIX}/etc
+
+INSTALL?= install
+SED?= sed
+
+_LIBNAME_SH= case `readlink /lib` in "") echo "lib";; *) basename `readlink /lib`;; esac
+_LIBNAME!= ${_LIBNAME_SH}
+LIBNAME?= ${_LIBNAME}$(shell ${_LIBNAME_SH})
diff --git a/net.c b/net.c
new file mode 100644
index 0000000..b905573
--- /dev/null
+++ b/net.c
@@ -0,0 +1,707 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <arpa/inet.h>
+#include <netinet/in_systm.h>
+#ifdef __linux__
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */
+#include <netinet/udp.h>
+#undef __FAVOR_BSD
+#ifdef SIOCGIFMEDIA
+#include <net/if_media.h>
+#endif
+#include <arpa/inet.h>
+#ifdef AF_LINK
+# include <net/if_dl.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "logger.h"
+#include "net.h"
+#include "signals.h"
+
+int
+inet_ntocidr(struct in_addr address)
+{
+ int cidr = 0;
+ uint32_t mask = htonl(address.s_addr);
+
+ while (mask) {
+ cidr++;
+ mask <<= 1;
+ }
+
+ return cidr;
+}
+
+int
+inet_cidrtoaddr(int cidr, struct in_addr *addr)
+{
+ int ocets;
+
+ if (cidr < 0 || cidr > 32) {
+ errno = EINVAL;
+ return -1;
+ }
+ ocets = (cidr + 7) / 8;
+
+ addr->s_addr = 0;
+ if (ocets > 0) {
+ memset(&addr->s_addr, 255, (size_t)ocets - 1);
+ memset((unsigned char *)&addr->s_addr + (ocets - 1),
+ (256 - (1 << (32 - cidr) % 8)), 1);
+ }
+
+ return 0;
+}
+
+uint32_t
+get_netmask(uint32_t addr)
+{
+ uint32_t dst;
+
+ if (addr == 0)
+ return 0;
+
+ dst = htonl(addr);
+ if (IN_CLASSA(dst))
+ return ntohl(IN_CLASSA_NET);
+ if (IN_CLASSB (dst))
+ return ntohl(IN_CLASSB_NET);
+ if (IN_CLASSC (dst))
+ return ntohl(IN_CLASSC_NET);
+
+ return 0;
+}
+
+char *
+hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
+{
+ static char buffer[(HWADDR_LEN * 3) + 1];
+ char *p = buffer;
+ size_t i;
+
+ for (i = 0; i < hwlen && i < HWADDR_LEN; i++) {
+ if (i > 0)
+ *p ++= ':';
+ p += snprintf(p, 3, "%.2x", hwaddr[i]);
+ }
+
+ *p ++= '\0';
+
+ return buffer;
+}
+
+size_t
+hwaddr_aton(unsigned char *buffer, const char *addr)
+{
+ char c[3];
+ const char *p = addr;
+ unsigned char *bp = buffer;
+ size_t len = 0;
+
+ c[2] = '\0';
+ while (*p) {
+ c[0] = *p++;
+ c[1] = *p++;
+ /* Ensure that digits are hex */
+ if (isxdigit((unsigned char)c[0]) == 0 ||
+ isxdigit((unsigned char)c[1]) == 0)
+ {
+ errno = EINVAL;
+ return 0;
+ }
+ /* We should have at least two entries 00:01 */
+ if (len == 0 && *p == '\0') {
+ errno = EINVAL;
+ return 0;
+ }
+ /* Ensure that next data is EOL or a seperator with data */
+ if (!(*p == '\0' || (*p == ':' && *(p + 1) != '\0'))) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (*p)
+ p++;
+ if (bp)
+ *bp++ = (unsigned char)strtol(c, NULL, 16);
+ len++;
+ }
+ return len;
+}
+
+int
+do_interface(const char *ifname,
+ _unused unsigned char *hwaddr, _unused size_t *hwlen,
+ struct in_addr *addr, struct in_addr *net, int get)
+{
+ int s;
+ struct ifconf ifc;
+ int retval = 0, found = 0;
+ int len = 10 * sizeof(struct ifreq);
+ int lastlen = 0;
+ char *p;
+ union {
+ char *buffer;
+ struct ifreq *ifr;
+ } ifreqs;
+ struct sockaddr_in address;
+ struct ifreq *ifr;
+ struct sockaddr_in netmask;
+#ifdef AF_LINK
+ struct sockaddr_dl *sdl;
+#endif
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return -1;
+
+ /* Not all implementations return the needed buffer size for
+ * SIOGIFCONF so we loop like so for all until it works */
+ memset(&ifc, 0, sizeof(ifc));
+ for (;;) {
+ ifc.ifc_len = len;
+ ifc.ifc_buf = xmalloc((size_t)len);
+ if (ioctl(s, SIOCGIFCONF, &ifc) == -1) {
+ if (errno != EINVAL || lastlen != 0) {
+ close(s);
+ free(ifc.ifc_buf);
+ return -1;
+ }
+ } else {
+ if (ifc.ifc_len == lastlen)
+ break;
+ lastlen = ifc.ifc_len;
+ }
+
+ free(ifc.ifc_buf);
+ ifc.ifc_buf = NULL;
+ len *= 2;
+ }
+
+ for (p = (char *)ifc.ifc_buf; p < (char *)ifc.ifc_buf + ifc.ifc_len;) {
+ /* Cast the ifc buffer to an ifreq cleanly */
+ ifreqs.buffer = p;
+ ifr = ifreqs.ifr;
+
+#ifndef __linux__
+ if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
+ p += offsetof(struct ifreq, ifr_ifru) +
+ ifr->ifr_addr.sa_len;
+ else
+#endif
+ p += sizeof(*ifr);
+
+ if (strcmp(ifname, ifr->ifr_name) != 0)
+ continue;
+
+ found = 1;
+
+#ifdef AF_LINK
+ if (hwaddr && hwlen && ifr->ifr_addr.sa_family == AF_LINK) {
+ sdl = xmalloc(ifr->ifr_addr.sa_len);
+ memcpy(sdl, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
+ *hwlen = sdl->sdl_alen;
+ memcpy(hwaddr, LLADDR(sdl), *hwlen);
+ free(sdl);
+ retval = 1;
+ break;
+ }
+#endif
+
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ memcpy(&address, &ifr->ifr_addr, sizeof(address));
+ if (ioctl(s, SIOCGIFNETMASK, ifr) == -1)
+ continue;
+ memcpy(&netmask, &ifr->ifr_addr, sizeof(netmask));
+ if (get) {
+ addr->s_addr = address.sin_addr.s_addr;
+ net->s_addr = netmask.sin_addr.s_addr;
+ retval = 1;
+ break;
+ } else {
+ if (address.sin_addr.s_addr == addr->s_addr &&
+ (!net ||
+ netmask.sin_addr.s_addr == net->s_addr))
+ {
+ retval = 1;
+ break;
+ }
+ }
+ }
+
+ }
+
+ if (!found)
+ errno = ENXIO;
+ close(s);
+ free(ifc.ifc_buf);
+ return retval;
+}
+
+int
+up_interface(const char *ifname)
+{
+ int s;
+ struct ifreq ifr;
+ int retval = -1;
+#ifdef __linux__
+ char *p;
+#endif
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return -1;
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+#ifdef __linux__
+ /* We can only bring the real interface up */
+ if ((p = strchr(ifr.ifr_name, ':')))
+ *p = '\0';
+#endif
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) {
+ if ((ifr.ifr_flags & IFF_UP))
+ retval = 0;
+ else {
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(s, SIOCSIFFLAGS, &ifr) == 0)
+ retval = 0;
+ }
+ }
+ close(s);
+ return retval;
+}
+
+int
+carrier_status(const char *ifname)
+{
+ int s;
+ struct ifreq ifr;
+ int retval = -1;
+#ifdef SIOCGIFMEDIA
+ struct ifmediareq ifmr;
+#endif
+#ifdef __linux__
+ char *p;
+#endif
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return -1;
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+#ifdef __linux__
+ /* We can only test the real interface up */
+ if ((p = strchr(ifr.ifr_name, ':')))
+ *p = '\0';
+#endif
+ if ((retval = ioctl(s, SIOCGIFFLAGS, &ifr)) == 0) {
+ if (ifr.ifr_flags & IFF_UP && ifr.ifr_flags & IFF_RUNNING)
+ retval = 1;
+ else
+ retval = 0;
+ }
+
+#ifdef SIOCGIFMEDIA
+ if (retval == 1) {
+ memset(&ifmr, 0, sizeof(ifmr));
+ strncpy(ifmr.ifm_name, ifr.ifr_name, sizeof(ifmr.ifm_name));
+ if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1 &&
+ ifmr.ifm_status & IFM_AVALID)
+ {
+ if (!(ifmr.ifm_status & IFM_ACTIVE))
+ retval = 0;
+ }
+ }
+#endif
+ close(s);
+ return retval;
+}
+
+struct interface *
+read_interface(const char *ifname, _unused int metric)
+{
+ int s;
+ struct ifreq ifr;
+ struct interface *iface = NULL;
+ unsigned char *hwaddr = NULL;
+ size_t hwlen = 0;
+ sa_family_t family = 0;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return NULL;
+
+#ifdef __linux__
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1)
+ goto eexit;
+
+ switch (ifr.ifr_hwaddr.sa_family) {
+ case ARPHRD_ETHER:
+ case ARPHRD_IEEE802:
+ hwlen = ETHER_ADDR_LEN;
+ break;
+ case ARPHRD_IEEE1394:
+ hwlen = EUI64_ADDR_LEN;
+ case ARPHRD_INFINIBAND:
+ hwlen = INFINIBAND_ADDR_LEN;
+ break;
+ }
+
+ hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
+ memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, hwlen);
+ family = ifr.ifr_hwaddr.sa_family;
+#else
+ ifr.ifr_metric = metric;
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCSIFMETRIC, &ifr) == -1)
+ goto eexit;
+
+ hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
+ if (do_interface(ifname, hwaddr, &hwlen, NULL, NULL, 0) != 1)
+ goto eexit;
+
+ family = ARPHRD_ETHER;
+#endif
+
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFMTU, &ifr) == -1)
+ goto eexit;
+
+ /* Ensure that the MTU is big enough for DHCP */
+ if (ifr.ifr_mtu < MTU_MIN) {
+ ifr.ifr_mtu = MTU_MIN;
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCSIFMTU, &ifr) == -1)
+ goto eexit;
+ }
+
+ if (up_interface(ifname) != 0)
+ goto eexit;
+
+ iface = xzalloc(sizeof(*iface));
+ strlcpy(iface->name, ifname, IF_NAMESIZE);
+ snprintf(iface->leasefile, PATH_MAX, LEASEFILE, ifname);
+ memcpy(&iface->hwaddr, hwaddr, hwlen);
+ iface->hwlen = hwlen;
+
+ iface->family = family;
+ iface->arpable = !(ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK));
+
+ /* 0 is a valid fd, so init to -1 */
+ iface->raw_fd = -1;
+ iface->udp_fd = -1;
+ iface->arp_fd = -1;
+ iface->link_fd = -1;
+
+eexit:
+ close(s);
+ free(hwaddr);
+ return iface;
+}
+
+int
+do_mtu(const char *ifname, short int mtu)
+{
+ struct ifreq ifr;
+ int r;
+ int s;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_mtu = mtu;
+ r = ioctl(s, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr);
+ close(s);
+ if (r == -1)
+ return -1;
+ return ifr.ifr_mtu;
+}
+
+void
+free_routes(struct rt *routes)
+{
+ struct rt *r;
+
+ while (routes) {
+ r = routes->next;
+ free(routes);
+ routes = r;
+ }
+}
+
+int
+open_udp_socket(struct interface *iface)
+{
+ int s;
+ union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ } su;
+ int n;
+#ifdef SO_BINDTODEVICE
+ struct ifreq ifr;
+#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));
+ 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;
+ memset(&su, 0, sizeof(su));
+ su.sin.sin_family = AF_INET;
+ su.sin.sin_port = htons(DHCP_CLIENT_PORT);
+ su.sin.sin_addr.s_addr = iface->addr.s_addr;
+ if (bind(s, &su.sa, sizeof(su)) == -1)
+ goto eexit;
+
+ iface->udp_fd = s;
+ set_cloexec(s);
+ return 0;
+
+eexit:
+ close(s);
+ return -1;
+}
+
+ssize_t
+send_packet(const struct interface *iface, struct in_addr to,
+ const uint8_t *data, ssize_t len)
+{
+ union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ } su;
+
+ memset(&su, 0, sizeof(su));
+ su.sin.sin_family = AF_INET;
+ su.sin.sin_addr.s_addr = to.s_addr;
+ su.sin.sin_port = htons(DHCP_SERVER_PORT);
+
+ return sendto(iface->udp_fd, data, len, 0, &su.sa, sizeof(su));
+}
+
+struct udp_dhcp_packet
+{
+ struct ip ip;
+ struct udphdr udp;
+ struct dhcp_message dhcp;
+};
+const size_t udp_dhcp_len = sizeof(struct udp_dhcp_packet);
+
+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;
+}
+
+ssize_t
+make_udp_packet(uint8_t **packet, 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 = xzalloc(sizeof(*udpp));
+ 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 = 5;
+ ip->ip_id = 0;
+ ip->ip_tos = IPTOS_LOWDELAY;
+ ip->ip_len = htons (sizeof(*ip) + sizeof(*udp) + length);
+ ip->ip_id = 0;
+ ip->ip_off = htons(IP_DF); /* Don't fragment */
+ ip->ip_ttl = IPDEFTTL;
+
+ ip->ip_sum = checksum(ip, sizeof(*ip));
+
+ *packet = (uint8_t *)udpp;
+ return sizeof(*ip) + sizeof(*udp) + length;
+}
+
+ssize_t
+get_udp_data(const uint8_t **data, const uint8_t *udp)
+{
+ struct udp_dhcp_packet packet;
+
+ memcpy(&packet, udp, sizeof(packet));
+ *data = udp + offsetof(struct udp_dhcp_packet, dhcp);
+ return ntohs(packet.ip.ip_len) - sizeof(packet.ip) - sizeof(packet.udp);
+}
+
+int
+valid_udp_packet(const uint8_t *data)
+{
+ struct udp_dhcp_packet packet;
+ uint16_t bytes;
+ uint16_t ipsum;
+ uint16_t iplen;
+ uint16_t udpsum;
+ struct in_addr source;
+ struct in_addr dest;
+ int retval = 0;
+
+ memcpy(&packet, data, sizeof(packet));
+ bytes = ntohs(packet.ip.ip_len);
+ ipsum = packet.ip.ip_sum;
+ iplen = packet.ip.ip_len;
+ udpsum = packet.udp.uh_sum;
+
+ if (0 != checksum(&packet.ip, sizeof(packet.ip))) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ packet.ip.ip_sum = 0;
+ memcpy(&source, &packet.ip.ip_src, sizeof(packet.ip.ip_src));
+ memcpy(&dest, &packet.ip.ip_dst, sizeof(packet.ip.ip_dst));
+ memset(&packet.ip, 0, sizeof(packet.ip));
+ packet.udp.uh_sum = 0;
+
+ packet.ip.ip_p = IPPROTO_UDP;
+ memcpy(&packet.ip.ip_src, &source, sizeof(packet.ip.ip_src));
+ memcpy(&packet.ip.ip_dst, &dest, sizeof(packet.ip.ip_dst));
+ packet.ip.ip_len = packet.udp.uh_ulen;
+ if (udpsum && udpsum != checksum(&packet, bytes)) {
+ errno = EINVAL;
+ retval = -1;
+ }
+
+ return retval;
+}
+
+int
+send_arp(const struct interface *iface, int op, in_addr_t sip, in_addr_t tip)
+{
+ struct arphdr *arp;
+ size_t arpsize;
+ uint8_t *p;
+ int retval;
+
+ arpsize = sizeof(*arp) + 2 * iface->hwlen + 2 * sizeof(sip);
+ arp = xmalloc(arpsize);
+ arp->ar_hrd = htons(iface->family);
+ arp->ar_pro = htons(ETHERTYPE_IP);
+ arp->ar_hln = iface->hwlen;
+ arp->ar_pln = sizeof(sip);
+ arp->ar_op = htons(op);
+ p = (uint8_t *)arp;
+ p += sizeof(*arp);
+ memcpy(p, iface->hwaddr, iface->hwlen);
+ p += iface->hwlen;
+ memcpy(p, &sip, sizeof(sip));
+ p += sizeof(sip);
+ /* ARP requests should ignore this */
+ retval = iface->hwlen;
+ while (retval--)
+ *p++ = '\0';
+ memcpy(p, &tip, sizeof(tip));
+ p += sizeof(tip);
+ retval = send_raw_packet(iface, ETHERTYPE_ARP, arp, arpsize);
+ free(arp);
+ return retval;
+}
diff --git a/net.h b/net.h
new file mode 100644
index 0000000..3ade025
--- /dev/null
+++ b/net.h
@@ -0,0 +1,176 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef INTERFACE_H
+#define INTERFACE_H
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <limits.h>
+
+#include "config.h"
+
+#ifndef DUID_LEN
+# define DUID_LEN 128 + 2
+#endif
+
+#define EUI64_ADDR_LEN 8
+#define INFINIBAND_ADDR_LEN 20
+
+/* Linux 2.4 doesn't define this */
+#ifndef ARPHRD_IEEE1394
+# define ARPHRD_IEEE1394 24
+#endif
+
+/* The BSD's don't define this yet */
+#ifndef ARPHRD_INFINIBAND
+# define ARPHRD_INFINIBAND 32
+#endif
+
+#define HWADDR_LEN 20
+
+/* Work out if we have a private address or not
+ * 10/8
+ * 172.16/12
+ * 192.168/16
+ */
+#ifndef IN_PRIVATE
+# define IN_PRIVATE(addr) (((addr & IN_CLASSA_NET) == 0x0a000000) || \
+ ((addr & 0xfff00000) == 0xac100000) || \
+ ((addr & IN_CLASSB_NET) == 0xc0a80000))
+#endif
+
+#define LINKLOCAL_ADDR 0xa9fe0000
+#define LINKLOCAL_MASK IN_CLASSB_NET
+#define LINKLOCAL_BRDC (LINKLOCAL_ADDR | ~LINKLOCAL_MASK)
+
+#ifndef IN_LINKLOCAL
+# define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)
+#endif
+
+/* There is an argument that this should be converted to an STAIL using
+ * queue(3). However, that isn't readily available on all libc's that
+ * dhcpcd works on. The only benefit of STAILQ over this is the ability to
+ * quickly loop backwards through the list - currently we reverse the list
+ * and then move through it forwards. This isn't that much of a big deal
+ * though as the norm is to just have one default route, and an IPV4LL route.
+ * You can (and do) get more routes in the DHCP message, but not enough to
+ * really warrant a change to STAIL queue for performance reasons. */
+struct rt {
+ struct in_addr dest;
+ struct in_addr net;
+ struct in_addr gate;
+ struct rt *next;
+};
+
+struct interface
+{
+ char name[IF_NAMESIZE];
+ sa_family_t family;
+ unsigned char hwaddr[HWADDR_LEN];
+ size_t hwlen;
+ int arpable;
+
+ int raw_fd;
+ int udp_fd;
+ int arp_fd;
+ int link_fd;
+ size_t buffer_size, buffer_len, buffer_pos;
+ unsigned char *buffer;
+
+ struct in_addr addr;
+ struct in_addr net;
+ struct rt *routes;
+
+ char leasefile[PATH_MAX];
+ time_t start_uptime;
+
+ unsigned char *clientid;
+};
+
+uint32_t get_netmask(uint32_t);
+char *hwaddr_ntoa(const unsigned char *, size_t);
+size_t hwaddr_aton(unsigned char *, const char *);
+
+struct interface *read_interface(const char *, int);
+int do_mtu(const char *, short int);
+#define get_mtu(iface) do_mtu(iface, 0)
+#define set_mtu(iface, mtu) do_mtu(iface, mtu)
+
+int inet_ntocidr(struct in_addr);
+int inet_cidrtoaddr(int, struct in_addr *);
+
+int up_interface(const char *);
+int do_interface(const char *, unsigned char *, size_t *,
+ struct in_addr *, struct in_addr *, int);
+int if_address(const char *, const struct in_addr *, const struct in_addr *,
+ const struct in_addr *, int);
+#define add_address(ifname, addr, net, brd) \
+ if_address(ifname, addr, net, brd, 1)
+#define del_address(ifname, addr, net) \
+ if_address(ifname, addr, net, NULL, -1)
+#define has_address(ifname, addr, net) \
+ do_interface(ifname, NULL, NULL, addr, net, 0)
+#define get_address(ifname, addr, net) \
+ do_interface(ifname, NULL, NULL, addr, net, 1)
+
+int if_route(const char *, const struct in_addr *, const struct in_addr *,
+ const struct in_addr *, int, int);
+#define add_route(ifname, dest, mask, gate, metric) \
+ if_route(ifname, dest, mask, gate, metric, 1)
+#define change_route(ifname, dest, mask, gate, metric) \
+ if_route(ifname, dest, mask, gate, metric, 0)
+#define del_route(ifname, dest, mask, gate, metric) \
+ if_route(ifname, dest, mask, gate, metric, -1)
+void free_routes(struct rt *);
+
+int open_udp_socket(struct interface *);
+const size_t udp_dhcp_len;
+ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t,
+ struct in_addr, struct in_addr);
+ssize_t get_udp_data(const uint8_t **, const uint8_t *);
+int valid_udp_packet(const uint8_t *);
+
+int open_socket(struct interface *, int);
+ssize_t send_packet(const struct interface *, struct in_addr,
+ const uint8_t *, ssize_t);
+ssize_t send_raw_packet(const struct interface *, int,
+ const void *, ssize_t);
+ssize_t get_raw_packet(struct interface *, int, void *, ssize_t);
+
+int send_arp(const struct interface *, int, in_addr_t, in_addr_t);
+
+int open_link_socket(struct interface *);
+int link_changed(struct interface *);
+int carrier_status(const char *);
+#endif
diff --git a/showlease.c b/showlease.c
new file mode 100644
index 0000000..9bee4ab
--- /dev/null
+++ b/showlease.c
@@ -0,0 +1,349 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "dhcp.h"
+#include "config.h"
+
+#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 IPV4 (1 << 6)
+#define STRING (1 << 7)
+#define PAIR (1 << 8)
+#define ARRAY (1 << 9)
+#define RFC3361 (1 << 10)
+#define RFC3397 (1 << 11)
+#define RFC3442 (1 << 12)
+
+struct dhcp_opt {
+ uint8_t option;
+ int type;
+ const char *var;
+};
+
+static const struct dhcp_opt const dhcp_opts[] = {
+ { 1, IPV4 | REQUEST, "subnet_mask" },
+ { 2, UINT32, "time_offset" },
+ { 3, IPV4 | ARRAY | REQUEST, "routers" },
+ { 4, IPV4 | ARRAY, "time_servers" },
+ { 5, IPV4 | ARRAY, "ien116_name_servers" },
+ { 6, IPV4 | ARRAY, "domain_name_servers" },
+ { 7, IPV4 | ARRAY, "log_servers" },
+ { 8, IPV4 | ARRAY, "cookie_servers" },
+ { 9, IPV4 | ARRAY, "lpr_servers" },
+ { 10, IPV4 | ARRAY, "impress_servers" },
+ { 11, IPV4 | ARRAY, "resource_location_servers" },
+ { 12, STRING, "host_name" },
+ { 13, UINT16, "boot_size" },
+ { 14, STRING, "merit_dump" },
+ { 15, STRING, "domain_name" },
+ { 16, IPV4, "swap_server" },
+ { 17, STRING, "root_path" },
+ { 18, STRING, "extensions_path" },
+ { 19, UINT8, "ip_forwarding" },
+ { 20, UINT8, "non_local_source_routing" },
+ { 21, IPV4 | ARRAY, "policy_filter" },
+ { 22, SINT16, "max_dgram_reassembly" },
+ { 23, UINT16, "default_ip_ttl" },
+ { 24, UINT32, "path_mtu_aging_timeout" },
+ { 25, UINT16 | ARRAY, "path_mtu_plateau_table" },
+ { 26, UINT16, "interface_mtu" },
+ { 27, UINT8, "all_subnets_local" },
+ { 28, IPV4 | REQUEST, "broadcast_address" },
+ { 29, UINT8, "perform_mask_discovery" },
+ { 30, UINT8, "mask_supplier" },
+ { 31, UINT8, "router_discovery" },
+ { 32, IPV4, "router_solicitation_address" },
+ { 33, IPV4 | ARRAY | REQUEST, "static_routes" },
+ { 34, UINT8, "trailer_encapsulation" },
+ { 35, UINT32, "arp_cache_timeout" },
+ { 36, UINT16, "ieee802_3_encapsulation" },
+ { 37, UINT8, "default_tcp_ttl" },
+ { 38, UINT32, "tcp_keepalive_interval" },
+ { 39, UINT8, "tcp_keepalive_garbage" },
+ { 40, STRING, "nis_domain" },
+ { 41, IPV4 | ARRAY, "nis_servers" },
+ { 42, IPV4 | ARRAY, "ntp_servers" },
+ { 43, STRING, "vendor_encapsulated_options" },
+ { 44, IPV4 | ARRAY, "netbios_name_servers" },
+ { 45, IPV4, "netbios_dd_server" },
+ { 46, UINT8, "netbios_node_type" },
+ { 47, STRING, "netbios_scope" },
+ { 48, IPV4 | ARRAY, "font_servers" },
+ { 49, IPV4 | ARRAY, "x_display_manager" },
+ { 50, IPV4, "dhcp_requested_address" },
+ { 51, UINT32 | REQUEST, "dhcp_lease_time" },
+ { 52, UINT8, "dhcp_option_overload" },
+ { 53, UINT8, "dhcp_message_type" },
+ { 54, IPV4, "dhcp_server_identifier" },
+ { 55, UINT8 | ARRAY, "dhcp_parameter_request_list" },
+ { 56, STRING, "dhcp_message" },
+ { 57, UINT16, "dhcp_max_message_size" },
+ { 58, UINT32 | REQUEST, "dhcp_renewal_time" },
+ { 59, UINT32 | REQUEST, "dhcp_rebinding_time" },
+ { 64, STRING, "nisplus_domain" },
+ { 65, IPV4 | ARRAY, "nisplus_servers" },
+ { 66, STRING, "tftp_server_name" },
+ { 67, STRING, "bootfile_name" },
+ { 68, IPV4 | ARRAY, "mobile_ip_home_agent" },
+ { 69, IPV4 | ARRAY, "smtp_server" },
+ { 70, IPV4 | ARRAY, "pop_server" },
+ { 71, IPV4 | ARRAY, "nntp_server" },
+ { 72, IPV4 | ARRAY, "www_server" },
+ { 73, IPV4 | ARRAY, "finger_server" },
+ { 74, IPV4 | ARRAY, "irc_server" },
+ { 75, IPV4 | ARRAY, "streettalk_server" },
+ { 76, IPV4 | ARRAY, "streettalk_directory_assistance_server" },
+ { 77, STRING, "user_class" },
+ { 85, IPV4 | ARRAY, "nds_servers" },
+ { 86, STRING, "nds_tree_name" },
+ { 87, STRING, "nds_context" },
+ { 88, STRING | RFC3397, "bcms_controller_names" },
+ { 89, IPV4 | ARRAY, "bcms_controller_address" },
+ { 91, UINT32, "client_last_transaction_time" },
+ { 92, IPV4 | ARRAY, "associated_ip" },
+ { 98, STRING, "uap_servers" },
+ { 112, IPV4 | ARRAY, "netinfo_server_address" },
+ { 113, STRING, "netinfo_server_tag" },
+ { 114, STRING, "default_url" },
+ { 118, IPV4, "subnet_selection" },
+ { 119, STRING | RFC3397, "domain_search" },
+ { 121, RFC3442 | REQUEST, "classless_static_routes" },
+ { 249, RFC3442, "ms-classless_static_routes" },
+ { 0, 0, NULL }
+};
+
+struct dhcp_message *
+get_lease(const char *leasefile)
+{
+ int fd;
+ struct dhcp_message *dhcp;
+ ssize_t bytes;
+
+ fd = open(leasefile, O_RDONLY);
+ if (fd == -1)
+ return NULL;
+ dhcp = malloc(sizeof(*dhcp));
+ memset(dhcp, 0, sizeof(*dhcp));
+ bytes = read(fd, dhcp, sizeof(*dhcp));
+ close(fd);
+ if (bytes < 0) {
+ free(dhcp);
+ dhcp = NULL;
+ }
+ return dhcp;
+}
+
+static uint8_t *dhcp_opt_buffer = NULL;
+
+static int
+valid_length(uint8_t option, int dl, int *type)
+{
+ const struct dhcp_opt *opt;
+ ssize_t sz;
+
+ if (dl == 0)
+ return -1;
+
+ for (opt = dhcp_opts; opt->option; opt++) {
+ if (opt->option != option)
+ continue;
+
+ if (type)
+ *type = opt->type;
+
+ if (opt->type == 0 || opt->type & STRING || opt->type & RFC3442)
+ return 0;
+
+ sz = 0;
+ if (opt->type & UINT32 || opt->type & IPV4)
+ sz = sizeof(uint32_t);
+ if (opt->type & UINT16)
+ sz = sizeof(uint16_t);
+ if (opt->type & UINT8)
+ sz = sizeof(uint8_t);
+ if (opt->type & IPV4 || opt->type & ARRAY)
+ return dl % sz;
+ return (dl == sz ? 0 : -1);
+ }
+
+ /* unknown option, so let it pass */
+ return 0;
+}
+
+static void
+free_option_buffer(void)
+{
+ free(dhcp_opt_buffer);
+}
+
+
+#define get_option_raw(dhcp, opt) get_option(dhcp, opt, NULL, NULL)
+static const uint8_t *
+get_option(const struct dhcp_message *dhcp, uint8_t opt, int *len, int *type)
+{
+ 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 (!dhcp_opt_buffer) {
+ dhcp_opt_buffer = malloc(sizeof(struct dhcp_message));
+ atexit(free_option_buffer);
+ }
+ if (!bp)
+ bp = dhcp_opt_buffer;
+ memcpy(bp, op, ol);
+ bp += ol;
+ }
+ ol = *p;
+ 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 (valid_length(o, bl, type) == -1) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (len)
+ *len = bl;
+ if (bp) {
+ memcpy(bp, op, ol);
+ return (const uint8_t *)&dhcp_opt_buffer;
+ }
+ if (op)
+ return op;
+ errno = ENOENT;
+ return NULL;
+}
+
+int
+get_option_addr(uint32_t *a, const struct dhcp_message *dhcp, uint8_t option)
+{
+ const uint8_t *p = get_option_raw(dhcp, option);
+
+ if (!p)
+ return -1;
+ memcpy(a, p, sizeof(*a));
+ return 0;
+}
+
+int
+get_option_uint32(uint32_t *i, const struct dhcp_message *dhcp, uint8_t option)
+{
+ uint32_t a;
+
+ if (get_option_addr(&a, dhcp, option) == -1)
+ return -1;
+
+ *i = ntohl(a);
+ return 0;
+}
+
+uint32_t
+get_netmask(uint32_t addr)
+{
+ uint32_t dst;
+
+ if (addr == 0)
+ return 0;
+
+ dst = htonl(addr);
+ if (IN_CLASSA(dst))
+ return ntohl(IN_CLASSA_NET);
+ if (IN_CLASSB (dst))
+ return ntohl(IN_CLASSB_NET);
+ if (IN_CLASSC (dst))
+ return ntohl(IN_CLASSC_NET);
+
+ return 0;
+}
+
+void showlease(struct dhcp_lease *lease)
+{
+ printf("addr: %s\n", inet_ntoa(lease->addr));
+ printf("net: %s\n", inet_ntoa(lease->net));
+ printf("leasetime: %d\n", lease->leasetime);
+ printf("renew: %d\n", lease->renewaltime);
+ printf("rebind: %d\n", lease->rebindtime);
+ printf("server: %s\n", inet_ntoa(lease->server));
+}
+#define MAX_LEASETIME 2147460
+
+int
+main(int argc, char *argv[])
+{
+ struct dhcp_message *dhcp;
+ struct dhcp_lease *lease;
+ char leasefile[PATH_MAX];
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <interface>\n", argv[0]);
+ exit(1);
+ }
+ snprintf(leasefile, PATH_MAX, LEASEFILE, argv[1]);
+ if ((dhcp = get_lease(leasefile)) == NULL) {
+ fprintf(stderr, "Couldn't read lease file: %s\n", strerror(errno));
+ exit(1);
+ }
+ lease = malloc(sizeof(*lease));
+ lease->frominfo = 0;
+ lease->addr.s_addr = dhcp->yiaddr;
+
+ if (get_option_addr(&lease->net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
+ lease->net.s_addr = get_netmask(dhcp->yiaddr);
+ if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) != 0)
+ lease->leasetime = DEFAULT_LEASETIME;
+ get_option_addr(&lease->server.s_addr, dhcp, DHO_SERVERID);
+ /* Dm: limit lease time value to avoid negative numbers when
+ converting to milliseconds */
+ if ((lease->leasetime != ~0U) && (lease->leasetime > MAX_LEASETIME))
+ lease->leasetime = MAX_LEASETIME;
+ 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;
+ showlease(lease);
+ free(lease);
+ return 0;
+}
diff --git a/signals.c b/signals.c
new file mode 100644
index 0000000..58679d6
--- /dev/null
+++ b/signals.c
@@ -0,0 +1,125 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "signals.h"
+
+static int signal_pipe[2];
+
+static const int handle_sigs[] = {
+ SIGHUP,
+ SIGALRM,
+ SIGTERM,
+ SIGINT
+};
+
+static void
+signal_handler(int sig)
+{
+ int serrno = errno;
+
+ write(signal_pipe[1], &sig, sizeof(sig));
+ /* Restore errno */
+ errno = serrno;
+}
+
+int
+signal_fd(void)
+{
+ return (signal_pipe[0]);
+}
+
+/* Read a signal from the signal pipe. Returns 0 if there is
+ * no signal, -1 on error (and sets errno appropriately), and
+ * your signal on success */
+int
+signal_read(void)
+{
+ int sig = -1;
+ char buf[16];
+ size_t bytes;
+
+ memset(buf, 0, sizeof(buf));
+ bytes = read(signal_pipe[0], buf, sizeof(buf));
+ if (bytes >= sizeof(sig))
+ memcpy(&sig, buf, sizeof(sig));
+ return sig;
+}
+
+/* Call this before doing anything else. Sets up the socket pair
+ * and installs the signal handler */
+int
+signal_init(void)
+{
+ if (pipe(signal_pipe) == -1)
+ return -1;
+ /* Don't block on read */
+ if (set_nonblock(signal_pipe[0]) == -1)
+ return -1;
+ /* Stop any scripts from inheriting us */
+ if (set_cloexec(signal_pipe[0]) == -1)
+ return -1;
+ if (set_cloexec(signal_pipe[1]) == -1)
+ return -1;
+ return 0;
+}
+
+static int
+signal_handle(void (*func)(int))
+{
+ unsigned int i;
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = func;
+ sigemptyset(&sa.sa_mask);
+
+ for (i = 0; i < sizeof(handle_sigs) / sizeof(handle_sigs[0]); i++)
+ if (sigaction(handle_sigs[i], &sa, NULL) == -1)
+ return -1;
+ return 0;
+}
+
+int
+signal_setup(void)
+{
+ return signal_handle(signal_handler);
+}
+
+int
+signal_reset(void)
+{
+ return signal_handle(SIG_DFL);
+}
diff --git a/signals.h b/signals.h
new file mode 100644
index 0000000..f784a68
--- /dev/null
+++ b/signals.h
@@ -0,0 +1,37 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef SIGNAL_H
+#define SIGNAL_H
+
+int signal_init(void);
+int signal_setup(void);
+int signal_reset(void);
+int signal_fd(void);
+int signal_read(void);
+
+#endif