summaryrefslogtreecommitdiffstats
path: root/dhcpcd/dhcpcd.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2014-01-30 13:29:46 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2014-01-30 16:23:03 +0100
commitf2ed769880271654297a4be420f26ab94d39666b (patch)
tree4cbfc23184993e8ef11bb1d60b307cbb3644a259 /dhcpcd/dhcpcd.c
parentarphole: New test (diff)
downloadrtems-libbsd-f2ed769880271654297a4be420f26ab94d39666b.tar.bz2
DHCPCD(8): Import
Import DHCPCD(8) from: http://roy.marples.name/projects/dhcpcd/ The upstream sources can be obtained via: fossil clone http://roy.marples.name/projects/dhcpcd The imported version is 2014-01-29 19:46:44 [6b209507bb].
Diffstat (limited to 'dhcpcd/dhcpcd.c')
-rw-r--r--dhcpcd/dhcpcd.c1477
1 files changed, 1477 insertions, 0 deletions
diff --git a/dhcpcd/dhcpcd.c b/dhcpcd/dhcpcd.c
new file mode 100644
index 00000000..70210f11
--- /dev/null
+++ b/dhcpcd/dhcpcd.c
@@ -0,0 +1,1477 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2014 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+const char dhcpcd_copyright[] = "Copyright (c) 2006-2014 Roy Marples";
+
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "config.h"
+#include "arp.h"
+#include "common.h"
+#include "control.h"
+#include "dev.h"
+#include "dhcpcd.h"
+#include "dhcp6.h"
+#include "duid.h"
+#include "eloop.h"
+#include "if-options.h"
+#include "if-pref.h"
+#include "ipv4.h"
+#include "ipv6.h"
+#include "ipv6nd.h"
+#include "net.h"
+#include "platform.h"
+#include "script.h"
+
+struct if_head *ifaces = NULL;
+char vendor[VENDORCLASSID_MAX_LEN];
+int pidfd = -1;
+struct if_options *if_options = NULL;
+int ifac = 0;
+char **ifav = NULL;
+int ifdc = 0;
+char **ifdv = NULL;
+
+sigset_t dhcpcd_sigset;
+const int handle_sigs[] = {
+ SIGALRM,
+ SIGHUP,
+ SIGINT,
+ SIGPIPE,
+ SIGTERM,
+ SIGUSR1,
+ 0
+};
+
+static char *cffile;
+static char *pidfile;
+static int linkfd = -1;
+static char **ifv;
+static int ifc;
+static char **margv;
+static int margc;
+
+static pid_t
+read_pid(void)
+{
+ FILE *fp;
+ pid_t pid;
+
+ if ((fp = fopen(pidfile, "r")) == NULL) {
+ errno = ENOENT;
+ return 0;
+ }
+ if (fscanf(fp, "%d", &pid) != 1)
+ pid = 0;
+ fclose(fp);
+ return pid;
+}
+
+static void
+usage(void)
+{
+
+printf("usage: "PACKAGE"\t[-46ABbDdEGgHJKkLnpqTVw]\n"
+ "\t\t[-C, --nohook hook] [-c, --script script]\n"
+ "\t\t[-e, --env value] [-F, --fqdn FQDN] [-f, --config file]\n"
+ "\t\t[-h, --hostname hostname] [-I, --clientid clientid]\n"
+ "\t\t[-i, --vendorclassid vendorclassid] [-l, --leasetime seconds]\n"
+ "\t\t[-m, --metric metric] [-O, --nooption option]\n"
+ "\t\t[-o, --option option] [-Q, --require option]\n"
+ "\t\t[-r, --request address] [-S, --static value]\n"
+ "\t\t[-s, --inform address[/cidr]] [-t, --timeout seconds]\n"
+ "\t\t[-u, --userclass class] [-v, --vendor code, value]\n"
+ "\t\t[-W, --whitelist address[/cidr]] [-y, --reboot seconds]\n"
+ "\t\t[-X, --blacklist address[/cidr]] [-Z, --denyinterfaces pattern]\n"
+ "\t\t[-z, --allowinterfaces pattern] [interface] [...]\n"
+ " "PACKAGE"\t-k, --release [interface]\n"
+ " "PACKAGE"\t-U, --dumplease interface\n"
+ " "PACKAGE"\t--version\n"
+ " "PACKAGE"\t-x, --exit [interface]\n");
+}
+
+static void
+free_globals(void)
+{
+ int i;
+ size_t n;
+ struct dhcp_opt *opt;
+
+ for (i = 0; i < ifac; i++)
+ free(ifav[i]);
+ free(ifav);
+ for (i = 0; i < ifdc; i++)
+ free(ifdv[i]);
+ free(ifdv);
+
+#ifdef INET
+ for (n = 0, opt = dhcp_opts; n < dhcp_opts_len; n++, opt++)
+ free_dhcp_opt_embenc(opt);
+ free(dhcp_opts);
+#endif
+#ifdef INET6
+ for (n = 0, opt = dhcp6_opts; n < dhcp6_opts_len; n++, opt++)
+ free_dhcp_opt_embenc(opt);
+ free(dhcp6_opts);
+#endif
+ for (n = 0, opt = vivso; n < vivso_len; n++, opt++)
+ free_dhcp_opt_embenc(opt);
+ free(vivso);
+}
+
+static void
+cleanup(void)
+{
+#ifdef DEBUG_MEMORY
+ struct interface *ifp;
+
+ free(duid);
+ free_options(if_options);
+
+ if (ifaces) {
+ while ((ifp = TAILQ_FIRST(ifaces))) {
+ TAILQ_REMOVE(ifaces, ifp, next);
+ free_interface(ifp);
+ }
+ free(ifaces);
+ }
+
+ free_globals();
+#endif
+
+ if (!(options & DHCPCD_FORKED))
+ dev_stop();
+ if (linkfd != -1)
+ close(linkfd);
+ if (pidfd > -1) {
+ if (options & DHCPCD_MASTER) {
+ if (control_stop() == -1)
+ syslog(LOG_ERR, "control_stop: %m");
+ }
+ close(pidfd);
+ unlink(pidfile);
+ }
+#ifdef DEBUG_MEMORY
+ free(pidfile);
+#endif
+
+ if (options & DHCPCD_STARTED && !(options & DHCPCD_FORKED))
+ syslog(LOG_INFO, "exited");
+}
+
+/* ARGSUSED */
+static void
+handle_exit_timeout(__unused void *arg)
+{
+ int timeout;
+
+ syslog(LOG_ERR, "timed out");
+ if (!(options & DHCPCD_IPV4) || !(options & DHCPCD_TIMEOUT_IPV4LL)) {
+ if (options & DHCPCD_MASTER) {
+ /* We've timed out, so remove the waitip requirements.
+ * If the user doesn't like this they can always set
+ * an infinite timeout. */
+ options &=
+ ~(DHCPCD_WAITIP | DHCPCD_WAITIP4 | DHCPCD_WAITIP6);
+ daemonise();
+ return;
+ } else
+ exit(EXIT_FAILURE);
+ }
+ options &= ~DHCPCD_TIMEOUT_IPV4LL;
+ timeout = (PROBE_NUM * PROBE_MAX) + (PROBE_WAIT * 2);
+ syslog(LOG_WARNING, "allowing %d seconds for IPv4LL timeout", timeout);
+ eloop_timeout_add_sec(timeout, handle_exit_timeout, NULL);
+}
+
+pid_t
+daemonise(void)
+{
+#ifdef THERE_IS_NO_FORK
+ return -1;
+#else
+ pid_t pid;
+ char buf = '\0';
+ int sidpipe[2], fd;
+
+ if (options & DHCPCD_DAEMONISE && !(options & DHCPCD_DAEMONISED)) {
+ if (options & DHCPCD_WAITIP4 &&
+ !ipv4_addrexists(NULL))
+ return -1;
+ if (options & DHCPCD_WAITIP6 &&
+ !ipv6nd_addrexists(NULL) &&
+ !dhcp6_addrexists(NULL))
+ return -1;
+ if ((options &
+ (DHCPCD_WAITIP | DHCPCD_WAITIP4 | DHCPCD_WAITIP6)) ==
+ DHCPCD_WAITIP &&
+ !ipv4_addrexists(NULL) &&
+ !ipv6nd_addrexists(NULL) &&
+ !dhcp6_addrexists(NULL))
+ return -1;
+ }
+
+ eloop_timeout_delete(handle_exit_timeout, NULL);
+ if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
+ return 0;
+ /* Setup a signal pipe so parent knows when to exit. */
+ if (pipe(sidpipe) == -1) {
+ syslog(LOG_ERR, "pipe: %m");
+ return -1;
+ }
+ syslog(LOG_DEBUG, "forking to background");
+ switch (pid = fork()) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ case 0:
+ setsid();
+ /* Notify parent it's safe to exit as we've detached. */
+ close(sidpipe[0]);
+ if (write(sidpipe[1], &buf, 1) == -1)
+ syslog(LOG_ERR, "failed to notify parent: %m");
+ close(sidpipe[1]);
+ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+ break;
+ default:
+ /* Wait for child to detach */
+ close(sidpipe[1]);
+ if (read(sidpipe[0], &buf, 1) == -1)
+ syslog(LOG_ERR, "failed to read child: %m");
+ close(sidpipe[0]);
+ break;
+ }
+ /* Done with the fd now */
+ if (pid != 0) {
+ syslog(LOG_INFO, "forked to background, child pid %d",pid);
+ writepid(pidfd, pid);
+ close(pidfd);
+ pidfd = -1;
+ options |= DHCPCD_FORKED;
+ exit(EXIT_SUCCESS);
+ }
+ options |= DHCPCD_DAEMONISED;
+ return pid;
+#endif
+}
+
+struct interface *
+find_interface(const char *ifname)
+{
+ struct interface *ifp;
+
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ if (strcmp(ifp->name, ifname) == 0)
+ return ifp;
+ }
+ return NULL;
+}
+
+static void
+stop_interface(struct interface *ifp)
+{
+
+ syslog(LOG_INFO, "%s: removing interface", ifp->name);
+ ifp->options->options |= DHCPCD_STOPPING;
+
+ // Remove the interface from our list
+ TAILQ_REMOVE(ifaces, ifp, next);
+ dhcp6_drop(ifp, NULL);
+ ipv6nd_drop(ifp);
+ dhcp_drop(ifp, "STOP");
+ dhcp_close(ifp);
+ eloop_timeout_delete(NULL, ifp);
+ if (ifp->options->options & DHCPCD_DEPARTED)
+ script_runreason(ifp, "DEPARTED");
+ free_interface(ifp);
+ if (!(options & (DHCPCD_MASTER | DHCPCD_TEST)))
+ exit(EXIT_FAILURE);
+}
+
+static void
+configure_interface1(struct interface *ifp)
+{
+ struct if_options *ifo = ifp->options;
+ int ra_global, ra_iface;
+
+ /* Do any platform specific configuration */
+ if_conf(ifp);
+
+ /* If we want to release a lease, we can't really persist the
+ * address either. */
+ if (ifo->options & DHCPCD_RELEASE)
+ ifo->options &= ~DHCPCD_PERSISTENT;
+
+ if (ifp->flags & IFF_POINTOPOINT && !(ifo->options & DHCPCD_INFORM))
+ ifo->options |= DHCPCD_STATIC;
+ if (ifp->flags & IFF_NOARP ||
+ ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))
+ ifo->options &= ~(DHCPCD_ARP | DHCPCD_IPV4LL);
+ if (!(ifp->flags & (IFF_POINTOPOINT | IFF_LOOPBACK | IFF_MULTICAST)))
+ ifo->options &= ~DHCPCD_IPV6RS;
+ if (ifo->options & DHCPCD_LINK && carrier_status(ifp) == LINK_UNKNOWN)
+ ifo->options &= ~DHCPCD_LINK;
+
+ if (ifo->metric != -1)
+ ifp->metric = ifo->metric;
+
+ if (!(ifo->options & DHCPCD_IPV6))
+ ifo->options &= ~DHCPCD_IPV6RS;
+
+ /* We want to disable kernel interface RA as early as possible. */
+ if (ifo->options & DHCPCD_IPV6RS) {
+ ra_global = check_ipv6(NULL, options & DHCPCD_IPV6RA_OWN ? 1:0);
+ ra_iface = check_ipv6(ifp->name,
+ ifp->options->options & DHCPCD_IPV6RA_OWN ? 1 : 0);
+ if (ra_global == -1 || ra_iface == -1)
+ ifo->options &= ~DHCPCD_IPV6RS;
+ else if (ra_iface == 0)
+ ifo->options |= DHCPCD_IPV6RA_OWN;
+ }
+
+ /* If we haven't specified a ClientID and our hardware address
+ * length is greater than DHCP_CHADDR_LEN then we enforce a ClientID
+ * of the hardware address family and the hardware address. */
+ if (ifp->hwlen > DHCP_CHADDR_LEN)
+ ifo->options |= DHCPCD_CLIENTID;
+
+ /* Firewire and InfiniBand interfaces require ClientID and
+ * the broadcast option being set. */
+ switch (ifp->family) {
+ case ARPHRD_IEEE1394: /* FALLTHROUGH */
+ case ARPHRD_INFINIBAND:
+ ifo->options |= DHCPCD_CLIENTID | DHCPCD_BROADCAST;
+ break;
+ }
+
+ if (!(ifo->options & DHCPCD_IAID)) {
+ /*
+ * An IAID is for identifying a unqiue interface within
+ * the client. It is 4 bytes long. Working out a default
+ * value is problematic.
+ *
+ * Interface name and number are not stable
+ * between different OS's. Some OS's also cannot make
+ * up their mind what the interface should be called
+ * (yes, udev, I'm looking at you).
+ * Also, the name could be longer than 4 bytes.
+ * Also, with pluggable interfaces the name and index
+ * could easily get swapped per actual interface.
+ *
+ * The MAC address is 6 bytes long, the final 3
+ * being unique to the manufacturer and the initial 3
+ * being unique to the organisation which makes it.
+ * We could use the last 4 bytes of the MAC address
+ * as the IAID as it's the most stable part given the
+ * above, but equally it's not guaranteed to be
+ * unique.
+ *
+ * Given the above, and our need to reliably work
+ * between reboots without persitent storage,
+ * generating the IAID from the MAC address is the only
+ * logical default.
+ *
+ * dhclient uses the last 4 bytes of the MAC address.
+ * dibbler uses an increamenting counter.
+ * wide-dhcpv6 uses 0 or a configured value.
+ * odhcp6c uses 1.
+ * Windows 7 uses the first 3 bytes of the MAC address
+ * and an unknown byte.
+ * dhcpcd-6.1.0 and earlier used the interface name,
+ * falling back to interface index if name > 4.
+ */
+ memcpy(ifo->iaid, ifp->hwaddr + ifp->hwlen - sizeof(ifo->iaid),
+ sizeof(ifo->iaid));
+#if 0
+ len = strlen(ifp->name);
+ if (len <= sizeof(ifo->iaid)) {
+ memcpy(ifo->iaid, ifp->name, len);
+ memset(ifo->iaid + len, 0, sizeof(ifo->iaid) - len);
+ } else {
+ /* IAID is the same size as a uint32_t */
+ len = htonl(ifp->index);
+ memcpy(ifo->iaid, &len, sizeof(len));
+ }
+#endif
+ ifo->options |= DHCPCD_IAID;
+ }
+
+#ifdef INET6
+ if (ifo->ia == NULL && ifo->options & DHCPCD_IPV6) {
+ ifo->ia = malloc(sizeof(*ifo->ia));
+ if (ifo->ia == NULL)
+ syslog(LOG_ERR, "%s: %m", __func__);
+ else {
+ if (ifo->ia_type == 0)
+ ifo->ia_type = D6_OPTION_IA_NA;
+ memcpy(ifo->ia->iaid, ifo->iaid, sizeof(ifo->iaid));
+ ifo->ia_len = 1;
+ ifo->ia->sla = NULL;
+ ifo->ia->sla_len = 0;
+ }
+ }
+#endif
+
+ /* If we are not sending an authentication option, don't require it */
+ if (!(ifo->auth.options & DHCPCD_AUTH_SEND))
+ ifo->auth.options &= ~DHCPCD_AUTH_REQUIRE;
+
+}
+
+int
+select_profile(struct interface *ifp, const char *profile)
+{
+ struct if_options *ifo;
+ int ret;
+
+ ret = 0;
+ ifo = read_config(cffile, ifp->name, ifp->ssid, profile);
+ if (ifo == NULL) {
+ syslog(LOG_DEBUG, "%s: no profile %s", ifp->name, profile);
+ ret = -1;
+ goto exit;
+ }
+ if (profile != NULL) {
+ strlcpy(ifp->profile, profile, sizeof(ifp->profile));
+ syslog(LOG_INFO, "%s: selected profile %s",
+ ifp->name, profile);
+ } else
+ *ifp->profile = '\0';
+ free_options(ifp->options);
+ ifp->options = ifo;
+
+exit:
+ if (profile)
+ configure_interface1(ifp);
+ return ret;
+}
+
+static void
+configure_interface(struct interface *ifp, int argc, char **argv)
+{
+
+ select_profile(ifp, NULL);
+ add_options(ifp->name, ifp->options, argc, argv);
+ configure_interface1(ifp);
+}
+
+void
+handle_carrier(int carrier, int flags, const char *ifname)
+{
+ struct interface *ifp;
+
+ ifp = find_interface(ifname);
+ if (ifp == NULL || !(ifp->options->options & DHCPCD_LINK))
+ return;
+
+ if (carrier == LINK_UNKNOWN)
+ carrier = carrier_status(ifp); /* will set ifp->flags */
+ else
+ ifp->flags = flags;
+
+ if (carrier == LINK_UNKNOWN)
+ syslog(LOG_ERR, "%s: carrier_status: %m", ifname);
+ /* IFF_RUNNING is checked, if needed, earlier and is OS dependant */
+ else if (carrier == LINK_DOWN || (ifp->flags & IFF_UP) == 0) {
+ if (ifp->carrier != LINK_DOWN) {
+ if (ifp->carrier == LINK_UP)
+ syslog(LOG_INFO, "%s: carrier lost", ifp->name);
+ ifp->carrier = LINK_DOWN;
+ dhcp_close(ifp);
+ dhcp6_drop(ifp, "EXPIRE6");
+ ipv6nd_drop(ifp);
+ /* Don't blindly delete our knowledge of LL addresses.
+ * We need to listen to what the kernel does with
+ * them as some OS's will remove, mark tentative or
+ * do nothing. */
+ ipv6_free_ll_callbacks(ifp);
+ dhcp_drop(ifp, "NOCARRIER");
+ }
+ } else if (carrier == LINK_UP && ifp->flags & IFF_UP) {
+ if (ifp->carrier != LINK_UP) {
+ syslog(LOG_INFO, "%s: carrier acquired", ifp->name);
+ ifp->carrier = LINK_UP;
+#if !defined(__linux__) && !defined(__NetBSD__)
+ /* BSD does not emit RTM_NEWADDR or RTM_CHGADDR when the
+ * hardware address changes so we have to go
+ * through the disovery process to work it out. */
+ handle_interface(0, ifp->name);
+#endif
+ if (ifp->wireless)
+ getifssid(ifp->name, ifp->ssid);
+ configure_interface(ifp, margc, margv);
+ script_runreason(ifp, "CARRIER");
+ start_interface(ifp);
+ }
+ }
+}
+
+static void
+warn_iaid_conflict(struct interface *ifp, uint8_t *iaid)
+{
+ struct interface *ifn;
+ size_t i;
+
+ TAILQ_FOREACH(ifn, ifaces, next) {
+ if (ifn == ifp)
+ continue;
+ if (memcmp(ifn->options->iaid, iaid,
+ sizeof(ifn->options->iaid)) == 0)
+ break;
+ for (i = 0; i < ifn->options->ia_len; i++) {
+ if (memcmp(&ifn->options->ia[i].iaid, iaid,
+ sizeof(ifn->options->ia[i].iaid)) == 0)
+ break;
+ }
+ }
+
+ /* This is only a problem if the interfaces are on the same network. */
+ if (ifn)
+ syslog(LOG_ERR,
+ "%s: IAID conflicts with one assigned to %s",
+ ifp->name, ifn->name);
+}
+
+void
+start_interface(void *arg)
+{
+ struct interface *ifp = arg;
+ struct if_options *ifo = ifp->options;
+ int nolease;
+ size_t i;
+
+ handle_carrier(LINK_UNKNOWN, 0, ifp->name);
+ if (ifp->carrier == LINK_DOWN) {
+ syslog(LOG_INFO, "%s: waiting for carrier", ifp->name);
+ return;
+ }
+
+ if (ifo->options & (DHCPCD_DUID | DHCPCD_IPV6)) {
+ /* Report client DUID */
+ if (duid == NULL) {
+ if (duid_init(ifp) == 0)
+ return;
+ syslog(LOG_INFO, "DUID %s",
+ hwaddr_ntoa(duid, duid_len));
+ }
+
+ /* Report IAIDs */
+ syslog(LOG_INFO, "%s: IAID %s", ifp->name,
+ hwaddr_ntoa(ifo->iaid, sizeof(ifo->iaid)));
+ warn_iaid_conflict(ifp, ifo->iaid);
+ for (i = 0; i < ifo->ia_len; i++) {
+ if (memcmp(ifo->iaid, ifo->ia[i].iaid,
+ sizeof(ifo->iaid)))
+ {
+ syslog(LOG_INFO, "%s: IAID %s", ifp->name,
+ hwaddr_ntoa(ifo->ia[i].iaid,
+ sizeof(ifo->ia[i].iaid)));
+ warn_iaid_conflict(ifp, ifo->ia[i].iaid);
+ }
+ }
+ }
+
+ if (ifo->options & DHCPCD_IPV6) {
+ if (ifo->options & DHCPCD_IPV6RS &&
+ !(ifo->options & DHCPCD_INFORM))
+ ipv6nd_startrs(ifp);
+
+ if (!(ifo->options & DHCPCD_IPV6RS)) {
+ if (ifo->options & DHCPCD_IA_FORCED)
+ nolease = dhcp6_start(ifp, DH6S_INIT);
+ else {
+ nolease = dhcp6_find_delegates(ifp);
+ /* Enabling the below doesn't really make
+ * sense as there is currently no standard
+ * to push routes via DHCPv6.
+ * (There is an expired working draft,
+ * maybe abandoned?)
+ * You can also get it to work by forcing
+ * an IA as shown above. */
+#if 0
+ /* With no RS or delegates we might
+ * as well try and solicit a DHCPv6 address */
+ if (nolease == 0)
+ nolease = dhcp6_start(ifp, DH6S_INIT);
+#endif
+ }
+ if (nolease == -1)
+ syslog(LOG_ERR,
+ "%s: dhcp6_start: %m", ifp->name);
+ }
+ }
+
+ if (ifo->options & DHCPCD_IPV4)
+ dhcp_start(ifp);
+}
+
+/* ARGSUSED */
+static void
+handle_link(__unused void *arg)
+{
+
+ if (manage_link(linkfd) == -1 && errno != ENXIO && errno != ENODEV)
+ syslog(LOG_ERR, "manage_link: %m");
+}
+
+static void
+init_state(struct interface *ifp, int argc, char **argv)
+{
+ struct if_options *ifo;
+ const char *reason = NULL;
+
+ configure_interface(ifp, argc, argv);
+ ifo = ifp->options;
+
+ if (ifo->options & DHCPCD_IPV4 && ipv4_init() == -1) {
+ syslog(LOG_ERR, "ipv4_init: %m");
+ ifo->options &= ~DHCPCD_IPV4;
+ }
+ if (ifo->options & DHCPCD_IPV6 && ipv6_init() == -1) {
+ syslog(LOG_ERR, "ipv6_init: %m");
+ ifo->options &= ~DHCPCD_IPV6RS;
+ }
+
+ if (!(options & DHCPCD_TEST))
+ script_runreason(ifp, "PREINIT");
+
+ if (ifo->options & DHCPCD_LINK) {
+ switch (carrier_status(ifp)) {
+ case LINK_DOWN:
+ ifp->carrier = LINK_DOWN;
+ reason = "NOCARRIER";
+ break;
+ case LINK_UP:
+ ifp->carrier = LINK_UP;
+ reason = "CARRIER";
+ break;
+ default:
+ ifp->carrier = LINK_UNKNOWN;
+ return;
+ }
+ if (reason && !(options & DHCPCD_TEST))
+ script_runreason(ifp, reason);
+ } else
+ ifp->carrier = LINK_UNKNOWN;
+}
+
+void
+handle_interface(int action, const char *ifname)
+{
+ struct if_head *ifs;
+ struct interface *ifp, *ifn, *ifl = NULL;
+ const char * const argv[] = { ifname };
+ int i;
+
+ if (action == -1) {
+ ifp = find_interface(ifname);
+ if (ifp != NULL) {
+ ifp->options->options |= DHCPCD_DEPARTED;
+ stop_interface(ifp);
+ }
+ return;
+ }
+
+ /* If running off an interface list, check it's in it. */
+ if (ifc) {
+ for (i = 0; i < ifc; i++)
+ if (strcmp(ifv[i], ifname) == 0)
+ break;
+ if (i >= ifc)
+ return;
+ }
+
+ ifs = discover_interfaces(-1, UNCONST(argv));
+ TAILQ_FOREACH_SAFE(ifp, ifs, next, ifn) {
+ if (strcmp(ifp->name, ifname) != 0)
+ continue;
+ /* Check if we already have the interface */
+ ifl = find_interface(ifp->name);
+ if (ifl) {
+ /* The flags and hwaddr could have changed */
+ ifl->flags = ifp->flags;
+ ifl->hwlen = ifp->hwlen;
+ if (ifp->hwlen != 0)
+ memcpy(ifl->hwaddr, ifp->hwaddr, ifl->hwlen);
+ } else {
+ TAILQ_REMOVE(ifs, ifp, next);
+ TAILQ_INSERT_TAIL(ifaces, ifp, next);
+ }
+ if (action == 1) {
+ init_state(ifp, margc, margv);
+ start_interface(ifp);
+ }
+ }
+
+ /* Free our discovered list */
+ while ((ifp = TAILQ_FIRST(ifs))) {
+ TAILQ_REMOVE(ifs, ifp, next);
+ free_interface(ifp);
+ }
+ free(ifs);
+}
+
+void
+handle_hwaddr(const char *ifname, const uint8_t *hwaddr, size_t hwlen)
+{
+ struct interface *ifp;
+
+ ifp = find_interface(ifname);
+ if (ifp == NULL)
+ return;
+
+ if (hwlen > sizeof(ifp->hwaddr)) {
+ errno = ENOBUFS;
+ syslog(LOG_ERR, "%s: %s: %m", ifp->name, __func__);
+ return;
+ }
+
+ if (ifp->hwlen == hwlen && memcmp(ifp->hwaddr, hwaddr, hwlen) == 0)
+ return;
+
+ syslog(LOG_INFO, "%s: new hardware address: %s", ifp->name,
+ hwaddr_ntoa(hwaddr, hwlen));
+ ifp->hwlen = hwlen;
+ memcpy(ifp->hwaddr, hwaddr, hwlen);
+}
+
+static void
+if_reboot(struct interface *ifp, int argc, char **argv)
+{
+ int oldopts;
+
+ oldopts = ifp->options->options;
+ script_runreason(ifp, "RECONFIGURE");
+ configure_interface(ifp, argc, argv);
+ dhcp_reboot_newopts(ifp, oldopts);
+ dhcp6_reboot(ifp);
+ start_interface(ifp);
+}
+
+static void
+reconf_reboot(int action, int argc, char **argv, int oi)
+{
+ struct if_head *ifs;
+ struct interface *ifn, *ifp;
+
+ ifs = discover_interfaces(argc - oi, argv + oi);
+ if (ifs == NULL)
+ return;
+
+ while ((ifp = TAILQ_FIRST(ifs))) {
+ TAILQ_REMOVE(ifs, ifp, next);
+ ifn = find_interface(ifp->name);
+ if (ifn) {
+ if (action)
+ if_reboot(ifn, argc, argv);
+ else
+ ipv4_applyaddr(ifn);
+ free_interface(ifp);
+ } else {
+ init_state(ifp, argc, argv);
+ TAILQ_INSERT_TAIL(ifaces, ifp, next);
+ start_interface(ifp);
+ }
+ }
+ free(ifs);
+
+ sort_interfaces();
+}
+
+/* ARGSUSED */
+static void
+sig_reboot(void *arg)
+{
+ siginfo_t *siginfo = arg;
+ struct if_options *ifo;
+
+ syslog(LOG_INFO, "received SIGALRM from PID %d, rebinding",
+ (int)siginfo->si_pid);
+
+ free_globals();
+ ifav = NULL;
+ ifac = 0;
+ ifdc = 0;
+ ifdv = NULL;
+
+ ifo = read_config(cffile, NULL, NULL, NULL);
+ add_options(NULL, ifo, margc, margv);
+ /* We need to preserve these two options. */
+ if (options & DHCPCD_MASTER)
+ ifo->options |= DHCPCD_MASTER;
+ if (options & DHCPCD_DAEMONISED)
+ ifo->options |= DHCPCD_DAEMONISED;
+ options = ifo->options;
+ free_options(ifo);
+ reconf_reboot(1, ifc, ifv, 0);
+}
+
+static void
+sig_reconf(void *arg)
+{
+ siginfo_t *siginfo = arg;
+ struct interface *ifp;
+
+ syslog(LOG_INFO, "received SIGUSR from PID %d, reconfiguring",
+ (int)siginfo->si_pid);
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ ipv4_applyaddr(ifp);
+ }
+}
+
+static void
+handle_signal(int sig, siginfo_t *siginfo, __unused void *context)
+{
+ struct interface *ifp;
+ int do_release;
+
+ do_release = 0;
+ switch (sig) {
+ case SIGINT:
+ syslog(LOG_INFO, "received SIGINT from PID %d, stopping",
+ (int)siginfo->si_pid);
+ break;
+ case SIGTERM:
+ syslog(LOG_INFO, "received SIGTERM from PID %d, stopping",
+ (int)siginfo->si_pid);
+ break;
+ case SIGALRM:
+ eloop_timeout_add_now(sig_reboot, siginfo);
+ return;
+ case SIGHUP:
+ syslog(LOG_INFO, "received SIGHUP from PID %d, releasing",
+ (int)siginfo->si_pid);
+ do_release = 1;
+ break;
+ case SIGUSR1:
+ eloop_timeout_add_now(sig_reconf, siginfo);
+ return;
+ case SIGPIPE:
+ syslog(LOG_WARNING, "received SIGPIPE");
+ return;
+ default:
+ syslog(LOG_ERR,
+ "received signal %d from PID %d, "
+ "but don't know what to do with it",
+ sig, (int)siginfo->si_pid);
+ return;
+ }
+
+ if (options & DHCPCD_TEST)
+ exit(EXIT_FAILURE);
+
+ /* As drop_dhcp could re-arrange the order, we do it like this. */
+ for (;;) {
+ /* Be sane and drop the last config first */
+ ifp = TAILQ_LAST(ifaces, if_head);
+ if (ifp == NULL)
+ break;
+ if (do_release) {
+ ifp->options->options |= DHCPCD_RELEASE;
+ ifp->options->options &= ~DHCPCD_PERSISTENT;
+ }
+ ifp->options->options |= DHCPCD_EXITING;
+ stop_interface(ifp);
+ }
+ exit(EXIT_FAILURE);
+}
+
+int
+handle_args(struct fd_list *fd, int argc, char **argv)
+{
+ struct interface *ifp;
+ int do_exit = 0, do_release = 0, do_reboot = 0;
+ int opt, oi = 0;
+ ssize_t len;
+ size_t l;
+ struct iovec iov[2];
+ char *tmp, *p;
+
+ if (fd != NULL) {
+ /* Special commands for our control socket */
+ if (strcmp(*argv, "--version") == 0) {
+ len = strlen(VERSION) + 1;
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(ssize_t);
+ iov[1].iov_base = UNCONST(VERSION);
+ iov[1].iov_len = len;
+ if (writev(fd->fd, iov, 2) == -1) {
+ syslog(LOG_ERR, "writev: %m");
+ return -1;
+ }
+ return 0;
+ } else if (strcmp(*argv, "--getconfigfile") == 0) {
+ len = strlen(cffile ? cffile : CONFIG) + 1;
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(ssize_t);
+ iov[1].iov_base = cffile ? cffile : UNCONST(CONFIG);
+ iov[1].iov_len = len;
+ if (writev(fd->fd, iov, 2) == -1) {
+ syslog(LOG_ERR, "writev: %m");
+ return -1;
+ }
+ return 0;
+ } else if (strcmp(*argv, "--getinterfaces") == 0) {
+ len = 0;
+ if (argc == 1) {
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ len++;
+ if (D6_STATE_RUNNING(ifp))
+ len++;
+ if (ipv6nd_has_ra(ifp))
+ len++;
+ }
+ len = write(fd->fd, &len, sizeof(len));
+ if (len != sizeof(len))
+ return -1;
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ send_interface(fd->fd, ifp);
+ }
+ return 0;
+ }
+ opt = 0;
+ while (argv[++opt] != NULL) {
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ if (strcmp(argv[opt], ifp->name) == 0) {
+ len++;
+ if (D6_STATE_RUNNING(ifp))
+ len++;
+ if (ipv6nd_has_ra(ifp))
+ len++;
+ }
+ }
+ }
+ len = write(fd->fd, &len, sizeof(len));
+ if (len != sizeof(len))
+ return -1;
+ opt = 0;
+ while (argv[++opt] != NULL) {
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ if (strcmp(argv[opt], ifp->name) == 0)
+ send_interface(fd->fd, ifp);
+ }
+ }
+ return 0;
+ } else if (strcmp(*argv, "--listen") == 0) {
+ fd->listener = 1;
+ return 0;
+ }
+ }
+
+ /* Log the command */
+ len = 0;
+ for (opt = 0; opt < argc; opt++)
+ len += strlen(argv[opt]) + 1;
+ tmp = p = malloc(len + 1);
+ if (tmp == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return -1;
+ }
+ for (opt = 0; opt < argc; opt++) {
+ l = strlen(argv[opt]);
+ strlcpy(p, argv[opt], l + 1);
+ p += l;
+ *p++ = ' ';
+ }
+ *--p = '\0';
+ syslog(LOG_INFO, "control command: %s", tmp);
+ free(tmp);
+
+ optind = 0;
+ while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
+ {
+ switch (opt) {
+ case 'g':
+ /* Assumed if below not set */
+ break;
+ case 'k':
+ do_release = 1;
+ break;
+ case 'n':
+ do_reboot = 1;
+ break;
+ case 'x':
+ do_exit = 1;
+ break;
+ }
+ }
+
+ /* We need at least one interface */
+ if (optind == argc) {
+ syslog(LOG_ERR, "%s: no interface", __func__);
+ return -1;
+ }
+
+ if (do_release || do_exit) {
+ for (oi = optind; oi < argc; oi++) {
+ if ((ifp = find_interface(argv[oi])) == NULL)
+ continue;
+ if (do_release) {
+ ifp->options->options |= DHCPCD_RELEASE;
+ ifp->options->options &= ~DHCPCD_PERSISTENT;
+ }
+ ifp->options->options |= DHCPCD_EXITING;
+ stop_interface(ifp);
+ }
+ return 0;
+ }
+
+ reconf_reboot(do_reboot, argc, argv, optind);
+ return 0;
+}
+
+static int
+signal_init(void (*func)(int, siginfo_t *, void *), sigset_t *oldset)
+{
+ unsigned int i;
+ struct sigaction sa;
+ sigset_t newset;
+
+ sigfillset(&newset);
+ if (sigprocmask(SIG_SETMASK, &newset, oldset) == -1)
+ return -1;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = func;
+ sa.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+
+ for (i = 0; handle_sigs[i]; i++) {
+ if (sigaction(handle_sigs[i], &sa, NULL) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct interface *ifp;
+ uint16_t family = 0;
+ int opt, oi = 0, sig = 0, i, control_fd;
+ size_t len;
+ pid_t pid;
+ struct timespec ts;
+ struct utsname utn;
+ const char *platform;
+
+ closefrom(3);
+ openlog(PACKAGE, LOG_PERROR | LOG_PID, LOG_DAEMON);
+ setlogmask(LOG_UPTO(LOG_INFO));
+
+ /* Test for --help and --version */
+ if (argc > 1) {
+ if (strcmp(argv[1], "--help") == 0) {
+ usage();
+ exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[1], "--version") == 0) {
+ printf(""PACKAGE" "VERSION"\n%s\n", dhcpcd_copyright);
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ platform = hardware_platform();
+ if (uname(&utn) == 0)
+ snprintf(vendor, VENDORCLASSID_MAX_LEN,
+ "%s-%s:%s-%s:%s%s%s", PACKAGE, VERSION,
+ utn.sysname, utn.release, utn.machine,
+ platform ? ":" : "", platform ? platform : "");
+ else
+ snprintf(vendor, VENDORCLASSID_MAX_LEN,
+ "%s-%s", PACKAGE, VERSION);
+
+ i = 0;
+ while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
+ {
+ switch (opt) {
+ case '4':
+ family = AF_INET;
+ break;
+ case '6':
+ family = AF_INET6;
+ break;
+ case 'f':
+ cffile = optarg;
+ break;
+ case 'g':
+ sig = SIGUSR1;
+ break;
+ case 'k':
+ sig = SIGHUP;
+ break;
+ case 'n':
+ sig = SIGALRM;
+ break;
+ case 'x':
+ sig = SIGTERM;
+ break;
+ case 'T':
+ i = 1;
+ break;
+ case 'U':
+ i = 2;
+ break;
+ case 'V':
+ i = 3;
+ break;
+ case '?':
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ margv = argv;
+ margc = argc;
+ if_options = read_config(cffile, NULL, NULL, NULL);
+ opt = add_options(NULL, if_options, argc, argv);
+ if (opt != 1) {
+ if (opt == 0)
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ if (i == 3) {
+ printf("Interface options:\n");
+ if_printoptions();
+#ifdef INET
+ if (family == 0 || family == AF_INET) {
+ printf("\nDHCPv4 options:\n");
+ dhcp_printoptions();
+ }
+#endif
+#ifdef INET6
+ if (family == 0 || family == AF_INET6) {
+ printf("\nDHCPv6 options:\n");
+ dhcp6_printoptions();
+ }
+#endif
+#ifdef DEBUG_MEMORY
+ cleanup();
+#endif
+ exit(EXIT_SUCCESS);
+ }
+ options = if_options->options;
+ if (i != 0) {
+ if (i == 1)
+ options |= DHCPCD_TEST;
+ else
+ options |= DHCPCD_DUMPLEASE;
+ options |= DHCPCD_PERSISTENT;
+ options &= ~DHCPCD_DAEMONISE;
+ }
+
+#ifdef THERE_IS_NO_FORK
+ options &= ~DHCPCD_DAEMONISE;
+#endif
+
+ if (options & DHCPCD_DEBUG)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ if (options & DHCPCD_QUIET) {
+ i = open(_PATH_DEVNULL, O_RDWR);
+ if (i == -1)
+ syslog(LOG_ERR, "%s: open: %m", __func__);
+ else {
+ dup2(i, STDERR_FILENO);
+ close(i);
+ }
+ }
+
+ if (!(options & (DHCPCD_TEST | DHCPCD_DUMPLEASE))) {
+ /* If we have any other args, we should run as a single dhcpcd
+ * instance for that interface. */
+ len = strlen(PIDFILE) + IF_NAMESIZE + 2;
+ pidfile = malloc(len);
+ if (pidfile == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ exit(EXIT_FAILURE);
+ }
+ if (optind == argc - 1)
+ snprintf(pidfile, len, PIDFILE, "-", argv[optind]);
+ else {
+ snprintf(pidfile, len, PIDFILE, "", "");
+ options |= DHCPCD_MASTER;
+ }
+ }
+
+ if (chdir("/") == -1)
+ syslog(LOG_ERR, "chdir `/': %m");
+ atexit(cleanup);
+
+ if (options & DHCPCD_DUMPLEASE) {
+ if (optind != argc - 1) {
+ syslog(LOG_ERR, "dumplease requires an interface");
+ exit(EXIT_FAILURE);
+ }
+ if (dhcp_dump(argv[optind]) == -1)
+ exit(EXIT_FAILURE);
+ exit(EXIT_SUCCESS);
+ }
+
+ if (!(options & (DHCPCD_MASTER | DHCPCD_TEST))) {
+ control_fd = control_open();
+ if (control_fd != -1) {
+ syslog(LOG_INFO,
+ "sending commands to master dhcpcd process");
+ i = control_send(argc, argv);
+ if (i > 0) {
+ syslog(LOG_DEBUG, "send OK");
+ exit(EXIT_SUCCESS);
+ } else {
+ syslog(LOG_ERR, "failed to send commands");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ if (errno != ENOENT)
+ syslog(LOG_ERR, "control_open: %m");
+ }
+ }
+
+ if (geteuid())
+ syslog(LOG_WARNING,
+ PACKAGE " will not work correctly unless run as root");
+
+ if (sig != 0) {
+ pid = read_pid();
+ if (pid != 0)
+ syslog(LOG_INFO, "sending signal %d to pid %d",
+ sig, pid);
+ if (pid == 0 || kill(pid, sig) != 0) {
+ if (sig != SIGALRM && errno != EPERM)
+ syslog(LOG_ERR, ""PACKAGE" not running");
+ if (pid != 0 && errno != ESRCH) {
+ syslog(LOG_ERR, "kill: %m");
+ exit(EXIT_FAILURE);
+ }
+ unlink(pidfile);
+ if (sig != SIGALRM)
+ exit(EXIT_FAILURE);
+ } else {
+ if (sig == SIGALRM || sig == SIGUSR1)
+ exit(EXIT_SUCCESS);
+ /* Spin until it exits */
+ syslog(LOG_INFO, "waiting for pid %d to exit", pid);
+ ts.tv_sec = 0;
+ ts.tv_nsec = 100000000; /* 10th of a second */
+ for(i = 0; i < 100; i++) {
+ nanosleep(&ts, NULL);
+ if (read_pid() == 0)
+ exit(EXIT_SUCCESS);
+ }
+ syslog(LOG_ERR, "pid %d failed to exit", pid);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (!(options & DHCPCD_TEST)) {
+ if ((pid = read_pid()) > 0 &&
+ kill(pid, 0) == 0)
+ {
+ syslog(LOG_ERR, ""PACKAGE
+ " already running on pid %d (%s)",
+ pid, pidfile);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Ensure we have the needed directories */
+ if (mkdir(RUNDIR, 0755) == -1 && errno != EEXIST)
+ syslog(LOG_ERR, "mkdir `%s': %m", RUNDIR);
+ if (mkdir(DBDIR, 0755) == -1 && errno != EEXIST)
+ syslog(LOG_ERR, "mkdir `%s': %m", DBDIR);
+
+ pidfd = open(pidfile, O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
+ if (pidfd == -1)
+ syslog(LOG_ERR, "open `%s': %m", pidfile);
+ else {
+ /* Lock the file so that only one instance of dhcpcd
+ * runs on an interface */
+ if (flock(pidfd, LOCK_EX | LOCK_NB) == -1) {
+ syslog(LOG_ERR, "flock `%s': %m", pidfile);
+ exit(EXIT_FAILURE);
+ }
+ if (set_cloexec(pidfd) == -1)
+ exit(EXIT_FAILURE);
+ writepid(pidfd, getpid());
+ }
+ }
+
+ syslog(LOG_INFO, "version " VERSION " starting");
+ options |= DHCPCD_STARTED;
+
+#ifdef DEBUG_MEMORY
+ eloop_init();
+#endif
+
+ /* Save signal mask, block and redirect signals to our handler */
+ if (signal_init(handle_signal, &dhcpcd_sigset) == -1) {
+ syslog(LOG_ERR, "signal_setup: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ if (options & DHCPCD_MASTER) {
+ if (control_start() == -1)
+ syslog(LOG_ERR, "control_start: %m");
+ }
+
+ if (open_sockets() == -1) {
+ syslog(LOG_ERR, "open_sockets: %m");
+ exit(EXIT_FAILURE);
+ }
+
+#if 0
+ if (options & DHCPCD_IPV6RS && disable_rtadv() == -1) {
+ syslog(LOG_ERR, "disable_rtadvd: %m");
+ options &= ~DHCPCD_IPV6RS;
+ }
+#endif
+
+ ifc = argc - optind;
+ ifv = argv + optind;
+
+ /* When running dhcpcd against a single interface, we need to retain
+ * the old behaviour of waiting for an IP address */
+ if (ifc == 1 && !(options & DHCPCD_BACKGROUND))
+ options |= DHCPCD_WAITIP;
+
+ /* RTM_NEWADDR goes through the link socket as well which we
+ * need for IPv6 DAD, so we check for DHCPCD_LINK in handle_carrier
+ * instead.
+ * We also need to open this before checking for interfaces below
+ * so that we pickup any new addresses during the discover phase. */
+ if (linkfd == -1) {
+ linkfd = open_link_socket();
+ if (linkfd == -1)
+ syslog(LOG_ERR, "open_link_socket: %m");
+ else
+ eloop_event_add(linkfd, handle_link, NULL);
+ }
+
+ /* Start any dev listening plugin which may want to
+ * change the interface name provided by the kernel */
+ if ((options & (DHCPCD_MASTER | DHCPCD_DEV)) ==
+ (DHCPCD_MASTER | DHCPCD_DEV))
+ dev_start(dev_load);
+
+ ifaces = discover_interfaces(ifc, ifv);
+ for (i = 0; i < ifc; i++) {
+ if (find_interface(ifv[i]) == NULL)
+ syslog(LOG_ERR, "%s: interface not found or invalid",
+ ifv[i]);
+ }
+ if (ifaces == NULL || TAILQ_FIRST(ifaces) == NULL) {
+ if (ifc == 0)
+ syslog(LOG_ERR, "no valid interfaces found");
+ else
+ exit(EXIT_FAILURE);
+ if (!(options & DHCPCD_LINK)) {
+ syslog(LOG_ERR,
+ "aborting as link detection is disabled");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (options & DHCPCD_BACKGROUND)
+ daemonise();
+
+ opt = 0;
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ init_state(ifp, argc, argv);
+ if (ifp->carrier != LINK_DOWN)
+ opt = 1;
+ }
+
+ if (!(options & DHCPCD_BACKGROUND)) {
+ /* If we don't have a carrier, we may have to wait for a second
+ * before one becomes available if we brought an interface up */
+ if (opt == 0 &&
+ options & DHCPCD_LINK &&
+ options & DHCPCD_WAITUP &&
+ !(options & DHCPCD_WAITIP))
+ {
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ nanosleep(&ts, NULL);
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ handle_carrier(LINK_UNKNOWN, 0, ifp->name);
+ if (ifp->carrier != LINK_DOWN) {
+ opt = 1;
+ break;
+ }
+ }
+ }
+ if (options & DHCPCD_MASTER)
+ i = if_options->timeout;
+ else if ((ifp = TAILQ_FIRST(ifaces)))
+ i = ifp->options->timeout;
+ else
+ i = 0;
+ if (opt == 0 &&
+ options & DHCPCD_LINK &&
+ !(options & DHCPCD_WAITIP))
+ {
+ syslog(LOG_WARNING, "no interfaces have a carrier");
+ daemonise();
+ } else if (i > 0) {
+ if (options & DHCPCD_IPV4LL)
+ options |= DHCPCD_TIMEOUT_IPV4LL;
+ eloop_timeout_add_sec(i, handle_exit_timeout, NULL);
+ }
+ }
+ free_options(if_options);
+ if_options = NULL;
+
+ sort_interfaces();
+ TAILQ_FOREACH(ifp, ifaces, next) {
+ eloop_timeout_add_sec(0, start_interface, ifp);
+ }
+
+ eloop_start(&dhcpcd_sigset);
+ exit(EXIT_SUCCESS);
+}