mirror of
https://github.com/rsmarples/dhcpcd.git
synced 2024-12-01 05:53:47 +08:00
a1f7b32cef
in IPv6 when dhcpcd is overriding the in-kernel RA support. For Linux kernels (3.18+) which support IFA_F_MANAGETEMPADDR the bulk of this changeset is compiled out and the kernel will manage the temporary addresses entirely. For BSD, this is a fully compliant implementation with the caveat that when dhcpcd is restarted the last non deprecated temp address on the interface will be treated as being created when it was last updated rather when it was actually added. Thus this may voilate section 3.3. As dhcpcd won't restart in normal operation, this isn't an issue. For Linux (3.18+) which supports IFA_F_MANAGETEMPADDR, the bulk of this changeset is compiled out as the kernel will manage the temporary addresses. For older Linux this is a fully compliant implementation with the caveat that when restarted new temporary addresses will be generated. Fixes [2ddfcb190f]
1819 lines
44 KiB
C
1819 lines
44 KiB
C
/*
|
|
* dhcpcd - DHCP client daemon
|
|
* Copyright (c) 2006-2015 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-2015 Roy Marples";
|
|
|
|
#define _WITH_DPRINTF /* Stop FreeBSD bitching */
|
|
|
|
#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 <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.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.h"
|
|
#include "if-options.h"
|
|
#include "ipv4.h"
|
|
#include "ipv6.h"
|
|
#include "ipv6nd.h"
|
|
#include "script.h"
|
|
|
|
#ifdef USE_SIGNALS
|
|
const int dhcpcd_handlesigs[] = {
|
|
SIGALRM,
|
|
SIGHUP,
|
|
SIGINT,
|
|
SIGPIPE,
|
|
SIGTERM,
|
|
SIGUSR1,
|
|
0
|
|
};
|
|
|
|
/* Handling signals needs *some* context */
|
|
static struct dhcpcd_ctx *dhcpcd_ctx;
|
|
#endif
|
|
|
|
#if defined(USE_SIGNALS) || !defined(THERE_IS_NO_FORK)
|
|
static pid_t
|
|
read_pid(const char *pidfile)
|
|
{
|
|
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 int
|
|
write_pid(int fd, pid_t pid)
|
|
{
|
|
|
|
if (ftruncate(fd, (off_t)0) == -1)
|
|
return -1;
|
|
lseek(fd, (off_t)0, SEEK_SET);
|
|
return dprintf(fd, "%d\n", (int)pid);
|
|
}
|
|
#endif
|
|
|
|
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(struct dhcpcd_ctx *ctx)
|
|
{
|
|
struct dhcp_opt *opt;
|
|
|
|
if (ctx->ifac) {
|
|
for (; ctx->ifac > 0; ctx->ifac--)
|
|
free(ctx->ifav[ctx->ifac - 1]);
|
|
free(ctx->ifav);
|
|
ctx->ifav = NULL;
|
|
}
|
|
if (ctx->ifdc) {
|
|
for (; ctx->ifdc > 0; ctx->ifdc--)
|
|
free(ctx->ifdv[ctx->ifdc - 1]);
|
|
free(ctx->ifdv);
|
|
ctx->ifdv = NULL;
|
|
}
|
|
if (ctx->ifcc) {
|
|
for (; ctx->ifcc > 0; ctx->ifcc--)
|
|
free(ctx->ifcv[ctx->ifcc - 1]);
|
|
free(ctx->ifcv);
|
|
ctx->ifcv = NULL;
|
|
}
|
|
|
|
#ifdef INET
|
|
if (ctx->dhcp_opts) {
|
|
for (opt = ctx->dhcp_opts;
|
|
ctx->dhcp_opts_len > 0;
|
|
opt++, ctx->dhcp_opts_len--)
|
|
free_dhcp_opt_embenc(opt);
|
|
free(ctx->dhcp_opts);
|
|
ctx->dhcp_opts = NULL;
|
|
}
|
|
#endif
|
|
#ifdef INET6
|
|
if (ctx->dhcp6_opts) {
|
|
for (opt = ctx->dhcp6_opts;
|
|
ctx->dhcp6_opts_len > 0;
|
|
opt++, ctx->dhcp6_opts_len--)
|
|
free_dhcp_opt_embenc(opt);
|
|
free(ctx->dhcp6_opts);
|
|
ctx->dhcp6_opts = NULL;
|
|
}
|
|
#endif
|
|
if (ctx->vivso) {
|
|
for (opt = ctx->vivso;
|
|
ctx->vivso_len > 0;
|
|
opt++, ctx->vivso_len--)
|
|
free_dhcp_opt_embenc(opt);
|
|
free(ctx->vivso);
|
|
ctx->vivso = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
handle_exit_timeout(void *arg)
|
|
{
|
|
struct dhcpcd_ctx *ctx;
|
|
|
|
ctx = arg;
|
|
syslog(LOG_ERR, "timed out");
|
|
if (!(ctx->options & DHCPCD_MASTER)) {
|
|
eloop_exit(ctx->eloop, EXIT_FAILURE);
|
|
return;
|
|
}
|
|
ctx->options |= DHCPCD_NOWAITIP;
|
|
dhcpcd_daemonise(ctx);
|
|
}
|
|
|
|
int
|
|
dhcpcd_oneup(struct dhcpcd_ctx *ctx)
|
|
{
|
|
const struct interface *ifp;
|
|
|
|
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
|
|
if (D_STATE_RUNNING(ifp) ||
|
|
RS_STATE_RUNNING(ifp) ||
|
|
D6_STATE_RUNNING(ifp))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
dhcpcd_ipwaited(struct dhcpcd_ctx *ctx)
|
|
{
|
|
|
|
if (ctx->options & DHCPCD_WAITIP4 &&
|
|
!ipv4_addrexists(ctx, NULL))
|
|
return 0;
|
|
if (ctx->options & DHCPCD_WAITIP6 &&
|
|
!ipv6nd_findaddr(ctx, NULL, 0) &&
|
|
!dhcp6_findaddr(ctx, NULL, 0))
|
|
return 0;
|
|
if (ctx->options & DHCPCD_WAITIP &&
|
|
!(ctx->options & (DHCPCD_WAITIP4 | DHCPCD_WAITIP6)) &&
|
|
!ipv4_addrexists(ctx, NULL) &&
|
|
!ipv6nd_findaddr(ctx, NULL, 0) &&
|
|
!dhcp6_findaddr(ctx, NULL, 0))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* Returns the pid of the child, otherwise 0. */
|
|
pid_t
|
|
dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
|
|
{
|
|
#ifdef THERE_IS_NO_FORK
|
|
eloop_timeout_delete(ctx->eloop, handle_exit_timeout, ctx);
|
|
errno = ENOSYS;
|
|
return 0;
|
|
#else
|
|
pid_t pid;
|
|
char buf = '\0';
|
|
int sidpipe[2], fd;
|
|
|
|
if (ctx->options & DHCPCD_DAEMONISE &&
|
|
!(ctx->options & (DHCPCD_DAEMONISED | DHCPCD_NOWAITIP)))
|
|
{
|
|
if (!dhcpcd_ipwaited(ctx))
|
|
return 0;
|
|
}
|
|
|
|
eloop_timeout_delete(ctx->eloop, handle_exit_timeout, ctx);
|
|
if (ctx->options & DHCPCD_DAEMONISED ||
|
|
!(ctx->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 0;
|
|
}
|
|
syslog(LOG_DEBUG, "forking to background");
|
|
switch (pid = fork()) {
|
|
case -1:
|
|
syslog(LOG_ERR, "fork: %m");
|
|
return 0;
|
|
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);
|
|
write_pid(ctx->pid_fd, pid);
|
|
close(ctx->pid_fd);
|
|
ctx->pid_fd = -1;
|
|
ctx->options |= DHCPCD_FORKED;
|
|
eloop_exit(ctx->eloop, EXIT_SUCCESS);
|
|
return pid;
|
|
}
|
|
ctx->options |= DHCPCD_DAEMONISED;
|
|
return pid;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
stop_interface(struct interface *ifp)
|
|
{
|
|
struct dhcpcd_ctx *ctx;
|
|
|
|
ctx = ifp->ctx;
|
|
syslog(LOG_INFO, "%s: removing interface", ifp->name);
|
|
ifp->options->options |= DHCPCD_STOPPING;
|
|
|
|
dhcp6_drop(ifp, NULL);
|
|
ipv6nd_drop(ifp);
|
|
dhcp_drop(ifp, "STOP");
|
|
arp_close(ifp);
|
|
if (ifp->options->options & DHCPCD_DEPARTED)
|
|
script_runreason(ifp, "DEPARTED");
|
|
else
|
|
script_runreason(ifp, "STOPPED");
|
|
|
|
/* Delete all timeouts for the interfaces */
|
|
eloop_q_timeout_delete(ctx->eloop, 0, NULL, ifp);
|
|
|
|
/* Remove the interface from our list */
|
|
TAILQ_REMOVE(ifp->ctx->ifaces, ifp, next);
|
|
if_free(ifp);
|
|
|
|
if (!(ctx->options & (DHCPCD_MASTER | DHCPCD_TEST)))
|
|
eloop_exit(ctx->eloop, EXIT_FAILURE);
|
|
}
|
|
|
|
static void
|
|
configure_interface1(struct interface *ifp)
|
|
{
|
|
struct if_options *ifo = ifp->options;
|
|
int ra_global, ra_iface;
|
|
#ifdef INET6
|
|
size_t i;
|
|
#endif
|
|
|
|
/* 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) ||
|
|
!(ifp->flags & IFF_MULTICAST))
|
|
ifo->options &= ~DHCPCD_IPV6RS;
|
|
|
|
if (ifo->metric != -1)
|
|
ifp->metric = (unsigned int)ifo->metric;
|
|
|
|
if (!(ifo->options & DHCPCD_IPV4))
|
|
ifo->options &= ~(DHCPCD_DHCP | DHCPCD_IPV4LL);
|
|
|
|
if (!(ifo->options & DHCPCD_IPV6))
|
|
ifo->options &= ~(DHCPCD_IPV6RS | DHCPCD_DHCP6);
|
|
|
|
if (ifo->options & DHCPCD_SLAACPRIVATE &&
|
|
!(ifp->ctx->options & DHCPCD_TEST))
|
|
ifo->options |= DHCPCD_IPV6RA_OWN;
|
|
|
|
/* We want to disable kernel interface RA as early as possible. */
|
|
if (ifo->options & DHCPCD_IPV6RS) {
|
|
/* If not doing any DHCP, disable the RDNSS requirement. */
|
|
if (!(ifo->options & (DHCPCD_DHCP | DHCPCD_DHCP6)))
|
|
ifo->options &= ~DHCPCD_IPV6RA_REQRDNSS;
|
|
ra_global = if_checkipv6(ifp->ctx, NULL,
|
|
ifp->ctx->options & DHCPCD_IPV6RA_OWN ? 1 : 0);
|
|
ra_iface = if_checkipv6(ifp->ctx, ifp,
|
|
ifp->options->options & DHCPCD_IPV6RA_OWN ? 1 : 0);
|
|
if (ra_global == -1 || ra_iface == -1)
|
|
ifo->options &= ~DHCPCD_IPV6RS;
|
|
else if (ra_iface == 0 && !(ifp->ctx->options & DHCPCD_TEST))
|
|
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 there is no hardware address and no ClientID set,
|
|
* force a DUID based ClientID. */
|
|
if (ifp->hwlen > DHCP_CHADDR_LEN)
|
|
ifo->options |= DHCPCD_CLIENTID;
|
|
else if (ifp->hwlen == 0 && !(ifo->options & DHCPCD_CLIENTID))
|
|
ifo->options |= DHCPCD_CLIENTID | DHCPCD_DUID;
|
|
|
|
/* 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.
|
|
*/
|
|
if (ifp->hwlen >= sizeof(ifo->iaid))
|
|
memcpy(ifo->iaid,
|
|
ifp->hwaddr + ifp->hwlen - sizeof(ifo->iaid),
|
|
sizeof(ifo->iaid));
|
|
else {
|
|
uint32_t len;
|
|
|
|
len = (uint32_t)strlen(ifp->name);
|
|
if (len <= sizeof(ifo->iaid)) {
|
|
memcpy(ifo->iaid, ifp->name, len);
|
|
if (len < sizeof(ifo->iaid))
|
|
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));
|
|
}
|
|
}
|
|
ifo->options |= DHCPCD_IAID;
|
|
}
|
|
|
|
#ifdef INET6
|
|
if (ifo->ia_len == 0 && ifo->options & DHCPCD_IPV6) {
|
|
ifo->ia = malloc(sizeof(*ifo->ia));
|
|
if (ifo->ia == NULL)
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
else {
|
|
ifo->ia_len = 1;
|
|
ifo->ia->ia_type = D6_OPTION_IA_NA;
|
|
memcpy(ifo->ia->iaid, ifo->iaid, sizeof(ifo->iaid));
|
|
memset(&ifo->ia->addr, 0, sizeof(ifo->ia->addr));
|
|
ifo->ia->sla = NULL;
|
|
ifo->ia->sla_len = 0;
|
|
}
|
|
} else {
|
|
for (i = 0; i < ifo->ia_len; i++) {
|
|
if (!ifo->ia[i].iaid_set) {
|
|
memcpy(&ifo->ia[i].iaid, ifo->iaid,
|
|
sizeof(ifo->ia[i].iaid));
|
|
ifo->ia[i].iaid_set = 1;
|
|
}
|
|
}
|
|
}
|
|
#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
|
|
dhcpcd_selectprofile(struct interface *ifp, const char *profile)
|
|
{
|
|
struct if_options *ifo;
|
|
char pssid[PROFILE_LEN];
|
|
|
|
if (ifp->ssid_len) {
|
|
ssize_t r;
|
|
|
|
r = print_string(pssid, sizeof(pssid), ESCSTRING,
|
|
ifp->ssid, ifp->ssid_len);
|
|
if (r == -1) {
|
|
syslog(LOG_ERR, "%s: %s: %m", ifp->name, __func__);
|
|
pssid[0] = '\0';
|
|
}
|
|
} else
|
|
pssid[0] = '\0';
|
|
ifo = read_config(ifp->ctx, ifp->name, pssid, profile);
|
|
if (ifo == NULL) {
|
|
syslog(LOG_DEBUG, "%s: no profile %s", ifp->name, profile);
|
|
return -1;
|
|
}
|
|
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;
|
|
if (profile)
|
|
configure_interface1(ifp);
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
configure_interface(struct interface *ifp, int argc, char **argv)
|
|
{
|
|
|
|
dhcpcd_selectprofile(ifp, NULL);
|
|
add_options(ifp->ctx, ifp->name, ifp->options, argc, argv);
|
|
configure_interface1(ifp);
|
|
}
|
|
|
|
static void
|
|
dhcpcd_pollup(void *arg)
|
|
{
|
|
struct interface *ifp = arg;
|
|
int carrier;
|
|
|
|
carrier = if_carrier(ifp); /* will set ifp->flags */
|
|
if (carrier == LINK_UP && !(ifp->flags & IFF_UP)) {
|
|
struct timeval tv;
|
|
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = IF_POLL_UP * 1000;
|
|
eloop_timeout_add_tv(ifp->ctx->eloop, &tv, dhcpcd_pollup, ifp);
|
|
return;
|
|
}
|
|
|
|
dhcpcd_handlecarrier(ifp->ctx, carrier, ifp->flags, ifp->name);
|
|
}
|
|
|
|
void
|
|
dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags,
|
|
const char *ifname)
|
|
{
|
|
struct interface *ifp;
|
|
|
|
ifp = if_find(ctx, ifname);
|
|
if (ifp == NULL || !(ifp->options->options & DHCPCD_LINK))
|
|
return;
|
|
|
|
switch(carrier) {
|
|
case LINK_UNKNOWN:
|
|
carrier = if_carrier(ifp); /* will set ifp->flags */
|
|
break;
|
|
case LINK_UP:
|
|
/* we have a carrier! Still need to check for IFF_UP */
|
|
if (flags & IFF_UP)
|
|
ifp->flags = flags;
|
|
else {
|
|
/* So we need to poll for IFF_UP as there is no
|
|
* kernel notification when it's set. */
|
|
dhcpcd_pollup(ifp);
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
ifp->flags = flags;
|
|
}
|
|
|
|
/* If we here, we don't need to poll for IFF_UP any longer
|
|
* if generated by a kernel event. */
|
|
eloop_timeout_delete(ifp->ctx->eloop, dhcpcd_pollup, ifp);
|
|
|
|
if (carrier == LINK_UNKNOWN) {
|
|
if (errno != ENOTTY) /* For example a PPP link on BSD */
|
|
syslog(LOG_ERR, "%s: carrier_status: %m", ifname);
|
|
} 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;
|
|
script_runreason(ifp, "NOCARRIER");
|
|
dhcp6_drop(ifp, "EXPIRE6");
|
|
ipv6_drop(ifp);
|
|
ipv6nd_drop(ifp);
|
|
dhcp_drop(ifp, "EXPIRE");
|
|
arp_close(ifp);
|
|
}
|
|
} 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. */
|
|
dhcpcd_handleinterface(ctx, 0, ifp->name);
|
|
#endif
|
|
if (ifp->wireless)
|
|
if_getssid(ifp);
|
|
dhcpcd_initstate(ifp);
|
|
script_runreason(ifp, "CARRIER");
|
|
/* RFC4941 Section 3.5 */
|
|
if (ifp->options->options & DHCPCD_IPV6RA_OWN)
|
|
ipv6_gentempifid(ifp);
|
|
dhcpcd_startinterface(ifp);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
warn_iaid_conflict(struct interface *ifp, uint8_t *iaid)
|
|
{
|
|
struct interface *ifn;
|
|
size_t i;
|
|
|
|
TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
|
|
if (ifn == ifp)
|
|
continue;
|
|
if (ifn->options->options & DHCPCD_PFXDLGONLY)
|
|
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 && strcmp(ifp->name, ifn->name))
|
|
syslog(LOG_ERR,
|
|
"%s: IAID conflicts with one assigned to %s",
|
|
ifp->name, ifn->name);
|
|
}
|
|
|
|
static void
|
|
pre_start(struct interface *ifp)
|
|
{
|
|
|
|
/* Add our link-local address before upping the interface
|
|
* so our RFC7217 address beats the hwaddr based one.
|
|
* This is also a safety check incase it was ripped out
|
|
* from under us. */
|
|
if (ifp->options->options & DHCPCD_IPV6 && ipv6_start(ifp) == -1) {
|
|
syslog(LOG_ERR, "%s: ipv6_start: %m", ifp->name);
|
|
ifp->options->options &= ~DHCPCD_IPV6;
|
|
}
|
|
}
|
|
|
|
void
|
|
dhcpcd_startinterface(void *arg)
|
|
{
|
|
struct interface *ifp = arg;
|
|
struct if_options *ifo = ifp->options;
|
|
size_t i;
|
|
char buf[DUID_LEN * 3];
|
|
int carrier;
|
|
struct timeval tv;
|
|
|
|
if (ifo->options & DHCPCD_LINK) {
|
|
switch (ifp->carrier) {
|
|
case LINK_UP:
|
|
break;
|
|
case LINK_DOWN:
|
|
syslog(LOG_INFO, "%s: waiting for carrier", ifp->name);
|
|
return;
|
|
case LINK_UNKNOWN:
|
|
/* No media state available.
|
|
* Loop until both IFF_UP and IFF_RUNNING are set */
|
|
if ((carrier = if_carrier(ifp)) == LINK_UNKNOWN) {
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = IF_POLL_UP * 1000;
|
|
eloop_timeout_add_tv(ifp->ctx->eloop,
|
|
&tv, dhcpcd_startinterface, ifp);
|
|
} else
|
|
dhcpcd_handlecarrier(ifp->ctx, carrier,
|
|
ifp->flags, ifp->name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ifo->options & (DHCPCD_DUID | DHCPCD_IPV6)) {
|
|
/* Report client DUID */
|
|
if (ifp->ctx->duid == NULL) {
|
|
if (duid_init(ifp) == 0)
|
|
return;
|
|
if (!(ifo->options & DHCPCD_PFXDLGONLY))
|
|
syslog(LOG_INFO, "DUID %s",
|
|
hwaddr_ntoa(ifp->ctx->duid,
|
|
ifp->ctx->duid_len,
|
|
buf, sizeof(buf)));
|
|
}
|
|
}
|
|
|
|
if (ifo->options & (DHCPCD_DUID | DHCPCD_IPV6) &&
|
|
!(ifo->options & DHCPCD_PFXDLGONLY))
|
|
{
|
|
/* Report IAIDs */
|
|
syslog(LOG_INFO, "%s: IAID %s", ifp->name,
|
|
hwaddr_ntoa(ifo->iaid, sizeof(ifo->iaid),
|
|
buf, sizeof(buf)));
|
|
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),
|
|
buf, sizeof(buf)));
|
|
warn_iaid_conflict(ifp, ifo->ia[i].iaid);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ifo->options & DHCPCD_IPV6) {
|
|
if (ifo->options & DHCPCD_IPV6RS &&
|
|
!(ifo->options & (DHCPCD_INFORM | DHCPCD_PFXDLGONLY)))
|
|
ipv6nd_startrs(ifp);
|
|
|
|
if (ifo->options & DHCPCD_DHCP6)
|
|
dhcp6_find_delegates(ifp);
|
|
|
|
if (!(ifo->options & DHCPCD_IPV6RS) ||
|
|
ifo->options & DHCPCD_IA_FORCED)
|
|
{
|
|
ssize_t nolease;
|
|
|
|
if (ifo->options & DHCPCD_IA_FORCED)
|
|
nolease = dhcp6_start(ifp, DH6S_INIT);
|
|
else {
|
|
nolease = 0;
|
|
/* 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_PFXDLGONLY)
|
|
return;
|
|
|
|
if (ifo->options & DHCPCD_IPV4)
|
|
dhcp_start(ifp);
|
|
}
|
|
|
|
static void
|
|
dhcpcd_prestartinterface(void *arg)
|
|
{
|
|
struct interface *ifp = arg;
|
|
|
|
pre_start(ifp);
|
|
if (if_up(ifp) == -1)
|
|
syslog(LOG_ERR, "%s: if_up: %m", ifp->name);
|
|
|
|
if (ifp->options->options & DHCPCD_LINK &&
|
|
ifp->carrier == LINK_UNKNOWN)
|
|
{
|
|
int carrier;
|
|
|
|
if ((carrier = if_carrier(ifp)) != LINK_UNKNOWN) {
|
|
dhcpcd_handlecarrier(ifp->ctx, carrier,
|
|
ifp->flags, ifp->name);
|
|
return;
|
|
}
|
|
syslog(LOG_INFO,
|
|
"%s: unknown carrier, waiting for interface flags",
|
|
ifp->name);
|
|
}
|
|
|
|
dhcpcd_startinterface(ifp);
|
|
}
|
|
|
|
static void
|
|
handle_link(void *arg)
|
|
{
|
|
struct dhcpcd_ctx *ctx;
|
|
|
|
ctx = arg;
|
|
if (if_managelink(ctx) == -1) {
|
|
syslog(LOG_ERR, "if_managelink: %m");
|
|
eloop_event_delete(ctx->eloop, ctx->link_fd, 0);
|
|
close(ctx->link_fd);
|
|
ctx->link_fd = -1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dhcpcd_initstate1(struct interface *ifp, int argc, char **argv)
|
|
{
|
|
struct if_options *ifo;
|
|
|
|
configure_interface(ifp, argc, argv);
|
|
ifo = ifp->options;
|
|
|
|
if (ifo->options & DHCPCD_IPV4 && ipv4_init(ifp->ctx) == -1) {
|
|
syslog(LOG_ERR, "ipv4_init: %m");
|
|
ifo->options &= ~DHCPCD_IPV4;
|
|
}
|
|
if (ifo->options & DHCPCD_IPV6 && ipv6_init(ifp->ctx) == NULL) {
|
|
syslog(LOG_ERR, "ipv6_init: %m");
|
|
ifo->options &= ~DHCPCD_IPV6RS;
|
|
}
|
|
|
|
/* Add our link-local address before upping the interface
|
|
* so our RFC7217 address beats the hwaddr based one.
|
|
* This needs to happen before PREINIT incase a hook script
|
|
* inadvertently ups the interface. */
|
|
if (ifo->options & DHCPCD_IPV6 && ipv6_start(ifp) == -1) {
|
|
syslog(LOG_ERR, "%s: ipv6_start: %m", ifp->name);
|
|
ifo->options &= ~DHCPCD_IPV6;
|
|
}
|
|
}
|
|
|
|
void
|
|
dhcpcd_initstate(struct interface *ifp)
|
|
{
|
|
|
|
dhcpcd_initstate1(ifp, ifp->ctx->argc, ifp->ctx->argv);
|
|
}
|
|
|
|
static void
|
|
run_preinit(struct interface *ifp)
|
|
{
|
|
|
|
pre_start(ifp);
|
|
if (ifp->ctx->options & DHCPCD_TEST)
|
|
return;
|
|
|
|
script_runreason(ifp, "PREINIT");
|
|
|
|
if (ifp->options->options & DHCPCD_LINK && ifp->carrier != LINK_UNKNOWN)
|
|
script_runreason(ifp,
|
|
ifp->carrier == LINK_UP ? "CARRIER" : "NOCARRIER");
|
|
}
|
|
|
|
int
|
|
dhcpcd_handleinterface(void *arg, int action, const char *ifname)
|
|
{
|
|
struct dhcpcd_ctx *ctx;
|
|
struct if_head *ifs;
|
|
struct interface *ifp, *iff, *ifn;
|
|
const char * const argv[] = { ifname };
|
|
int i;
|
|
|
|
ctx = arg;
|
|
if (action == -1) {
|
|
ifp = if_find(ctx, ifname);
|
|
if (ifp == NULL) {
|
|
errno = ESRCH;
|
|
return -1;
|
|
}
|
|
syslog(LOG_DEBUG, "%s: interface departed", ifp->name);
|
|
ifp->options->options |= DHCPCD_DEPARTED;
|
|
stop_interface(ifp);
|
|
return 0;
|
|
}
|
|
|
|
/* If running off an interface list, check it's in it. */
|
|
if (ctx->ifc && action != 2) {
|
|
for (i = 0; i < ctx->ifc; i++)
|
|
if (strcmp(ctx->ifv[i], ifname) == 0)
|
|
break;
|
|
if (i >= ctx->ifc)
|
|
return 0;
|
|
}
|
|
|
|
i = -1;
|
|
ifs = if_discover(ctx, -1, UNCONST(argv));
|
|
if (ifs == NULL) {
|
|
syslog(LOG_ERR, "%s: if_discover: %m", __func__);
|
|
return -1;
|
|
}
|
|
TAILQ_FOREACH_SAFE(ifp, ifs, next, ifn) {
|
|
if (strcmp(ifp->name, ifname) != 0)
|
|
continue;
|
|
i = 0;
|
|
/* Check if we already have the interface */
|
|
iff = if_find(ctx, ifp->name);
|
|
if (iff) {
|
|
syslog(LOG_DEBUG, "%s: interface updated", iff->name);
|
|
/* The flags and hwaddr could have changed */
|
|
iff->flags = ifp->flags;
|
|
iff->hwlen = ifp->hwlen;
|
|
if (ifp->hwlen != 0)
|
|
memcpy(iff->hwaddr, ifp->hwaddr, iff->hwlen);
|
|
} else {
|
|
syslog(LOG_DEBUG, "%s: interface added", ifp->name);
|
|
TAILQ_REMOVE(ifs, ifp, next);
|
|
TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);
|
|
dhcpcd_initstate(ifp);
|
|
run_preinit(ifp);
|
|
iff = ifp;
|
|
}
|
|
if (action > 0)
|
|
dhcpcd_prestartinterface(iff);
|
|
}
|
|
|
|
/* Free our discovered list */
|
|
while ((ifp = TAILQ_FIRST(ifs))) {
|
|
TAILQ_REMOVE(ifs, ifp, next);
|
|
if_free(ifp);
|
|
}
|
|
free(ifs);
|
|
|
|
if (i == -1)
|
|
errno = ENOENT;
|
|
return i;
|
|
}
|
|
|
|
void
|
|
dhcpcd_handlehwaddr(struct dhcpcd_ctx *ctx, const char *ifname,
|
|
const uint8_t *hwaddr, uint8_t hwlen)
|
|
{
|
|
struct interface *ifp;
|
|
char buf[sizeof(ifp->hwaddr) * 3];
|
|
|
|
ifp = if_find(ctx, 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, buf, sizeof(buf)));
|
|
ifp->hwlen = hwlen;
|
|
memcpy(ifp->hwaddr, hwaddr, hwlen);
|
|
}
|
|
|
|
static void
|
|
if_reboot(struct interface *ifp, int argc, char **argv)
|
|
{
|
|
unsigned long long oldopts;
|
|
|
|
oldopts = ifp->options->options;
|
|
script_runreason(ifp, "RECONFIGURE");
|
|
configure_interface(ifp, argc, argv);
|
|
dhcp_reboot_newopts(ifp, oldopts);
|
|
dhcp6_reboot(ifp);
|
|
dhcpcd_prestartinterface(ifp);
|
|
}
|
|
|
|
static void
|
|
reload_config(struct dhcpcd_ctx *ctx)
|
|
{
|
|
struct if_options *ifo;
|
|
|
|
free_globals(ctx);
|
|
ifo = read_config(ctx, NULL, NULL, NULL);
|
|
add_options(ctx, NULL, ifo, ctx->argc, ctx->argv);
|
|
/* We need to preserve these two options. */
|
|
if (ctx->options & DHCPCD_MASTER)
|
|
ifo->options |= DHCPCD_MASTER;
|
|
if (ctx->options & DHCPCD_DAEMONISED)
|
|
ifo->options |= DHCPCD_DAEMONISED;
|
|
ctx->options = ifo->options;
|
|
free_options(ifo);
|
|
}
|
|
|
|
static void
|
|
reconf_reboot(struct dhcpcd_ctx *ctx, int action, int argc, char **argv, int oi)
|
|
{
|
|
struct if_head *ifs;
|
|
struct interface *ifn, *ifp;
|
|
|
|
ifs = if_discover(ctx, argc - oi, argv + oi);
|
|
if (ifs == NULL) {
|
|
syslog(LOG_ERR, "%s: if_discover: %m", __func__);
|
|
return;
|
|
}
|
|
|
|
while ((ifp = TAILQ_FIRST(ifs))) {
|
|
TAILQ_REMOVE(ifs, ifp, next);
|
|
ifn = if_find(ctx, ifp->name);
|
|
if (ifn) {
|
|
if (action)
|
|
if_reboot(ifn, argc, argv);
|
|
else
|
|
ipv4_applyaddr(ifn);
|
|
if_free(ifp);
|
|
} else {
|
|
TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);
|
|
dhcpcd_initstate1(ifp, argc, argv);
|
|
run_preinit(ifp);
|
|
dhcpcd_prestartinterface(ifp);
|
|
}
|
|
}
|
|
free(ifs);
|
|
|
|
ipv4_sortinterfaces(ctx);
|
|
}
|
|
|
|
static void
|
|
stop_all_interfaces(struct dhcpcd_ctx *ctx, int do_release)
|
|
{
|
|
struct interface *ifp;
|
|
|
|
/* drop_dhcp could change the order, so we do it like this. */
|
|
for (;;) {
|
|
/* Be sane and drop the last config first,
|
|
* skipping any pseudo interfaces */
|
|
TAILQ_FOREACH_REVERSE(ifp, ctx->ifaces, if_head, next) {
|
|
if (!(ifp->options->options & DHCPCD_PFXDLGONLY))
|
|
break;
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
#ifdef USE_SIGNALS
|
|
struct dhcpcd_siginfo {
|
|
int signo;
|
|
pid_t pid;
|
|
} dhcpcd_siginfo;
|
|
|
|
#define sigmsg "received signal %s from PID %d, %s"
|
|
static void
|
|
handle_signal1(void *arg)
|
|
{
|
|
struct dhcpcd_ctx *ctx;
|
|
struct dhcpcd_siginfo *si;
|
|
struct interface *ifp;
|
|
int do_release;
|
|
|
|
ctx = dhcpcd_ctx;
|
|
si = arg;
|
|
do_release = 0;
|
|
switch (si->signo) {
|
|
case SIGINT:
|
|
syslog(LOG_INFO, sigmsg, "INT", (int)si->pid, "stopping");
|
|
break;
|
|
case SIGTERM:
|
|
syslog(LOG_INFO, sigmsg, "TERM", (int)si->pid, "stopping");
|
|
break;
|
|
case SIGALRM:
|
|
syslog(LOG_INFO, sigmsg, "ALRM", (int)si->pid, "releasing");
|
|
do_release = 1;
|
|
break;
|
|
case SIGHUP:
|
|
syslog(LOG_INFO, sigmsg, "HUP", (int)si->pid, "rebinding");
|
|
reload_config(ctx);
|
|
/* Preserve any options passed on the commandline
|
|
* when we were started. */
|
|
reconf_reboot(ctx, 1, ctx->argc, ctx->argv,
|
|
ctx->argc - ctx->ifc);
|
|
return;
|
|
case SIGUSR1:
|
|
syslog(LOG_INFO, sigmsg, "USR1", (int)si->pid, "reconfiguring");
|
|
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
|
|
ipv4_applyaddr(ifp);
|
|
}
|
|
return;
|
|
case SIGPIPE:
|
|
syslog(LOG_WARNING, "received signal PIPE");
|
|
return;
|
|
default:
|
|
syslog(LOG_ERR,
|
|
"received signal %d from PID %d, "
|
|
"but don't know what to do with it",
|
|
si->signo, (int)si->pid);
|
|
return;
|
|
}
|
|
|
|
if (!(ctx->options & DHCPCD_TEST))
|
|
stop_all_interfaces(ctx, do_release);
|
|
eloop_exit(ctx->eloop, EXIT_FAILURE);
|
|
}
|
|
|
|
static void
|
|
handle_signal(int sig, siginfo_t *siginfo, __unused void *context)
|
|
{
|
|
|
|
/* So that we can operate safely under a signal we instruct
|
|
* eloop to pass a copy of the siginfo structure to handle_signal1
|
|
* as the very first thing to do. */
|
|
dhcpcd_siginfo.signo = sig;
|
|
dhcpcd_siginfo.pid = siginfo ? siginfo->si_pid : 0;
|
|
eloop_timeout_add_now(dhcpcd_ctx->eloop,
|
|
handle_signal1, &dhcpcd_siginfo);
|
|
}
|
|
|
|
static int
|
|
signal_init(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 = handle_signal;
|
|
sa.sa_flags = SA_SIGINFO;
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
for (i = 0; dhcpcd_handlesigs[i]; i++) {
|
|
if (sigaction(dhcpcd_handlesigs[i], &sa, NULL) == -1)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
dhcpcd_getinterfaces(void *arg)
|
|
{
|
|
struct fd_list *fd = arg;
|
|
struct interface *ifp;
|
|
size_t len;
|
|
|
|
len = 0;
|
|
TAILQ_FOREACH(ifp, fd->ctx->ifaces, next) {
|
|
len++;
|
|
if (D_STATE_RUNNING(ifp))
|
|
len++;
|
|
if (RS_STATE_RUNNING(ifp))
|
|
len++;
|
|
if (D6_STATE_RUNNING(ifp))
|
|
len++;
|
|
}
|
|
if (write(fd->fd, &len, sizeof(len)) != sizeof(len))
|
|
return;
|
|
eloop_event_delete(fd->ctx->eloop, fd->fd, 1);
|
|
TAILQ_FOREACH(ifp, fd->ctx->ifaces, next) {
|
|
if (send_interface(fd, ifp) == -1)
|
|
syslog(LOG_ERR, "send_interface %d: %m", fd->fd);
|
|
}
|
|
}
|
|
|
|
int
|
|
dhcpcd_handleargs(struct dhcpcd_ctx *ctx, 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;
|
|
size_t len, l;
|
|
char *tmp, *p;
|
|
|
|
/* Special commands for our control socket
|
|
* as the other end should be blocking until it gets the
|
|
* expected reply we should be safely able just to change the
|
|
* write callback on the fd */
|
|
if (strcmp(*argv, "--version") == 0) {
|
|
return control_queue(fd, UNCONST(VERSION),
|
|
strlen(VERSION) + 1, 0);
|
|
} else if (strcmp(*argv, "--getconfigfile") == 0) {
|
|
return control_queue(fd, UNCONST(fd->ctx->cffile),
|
|
strlen(fd->ctx->cffile) + 1, 0);
|
|
} else if (strcmp(*argv, "--getinterfaces") == 0) {
|
|
eloop_event_add(fd->ctx->eloop, fd->fd, NULL, NULL,
|
|
dhcpcd_getinterfaces, fd);
|
|
return 0;
|
|
} else if (strcmp(*argv, "--listen") == 0) {
|
|
fd->flags |= FD_LISTEN;
|
|
return 0;
|
|
}
|
|
|
|
/* Only priviledged users can control dhcpcd via the socket. */
|
|
if (fd->flags & FD_UNPRIV) {
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
|
|
/* Log the command */
|
|
len = 1;
|
|
for (opt = 0; opt < argc; opt++)
|
|
len += strlen(argv[opt]) + 1;
|
|
tmp = malloc(len);
|
|
if (tmp == NULL)
|
|
return -1;
|
|
p = tmp;
|
|
for (opt = 0; opt < argc; opt++) {
|
|
l = strlen(argv[opt]);
|
|
strlcpy(p, argv[opt], len);
|
|
len -= 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;
|
|
}
|
|
}
|
|
|
|
if (do_release || do_exit) {
|
|
if (optind == argc) {
|
|
stop_all_interfaces(ctx, do_release);
|
|
eloop_exit(ctx->eloop, EXIT_SUCCESS);
|
|
return 0;
|
|
}
|
|
for (oi = optind; oi < argc; oi++) {
|
|
if ((ifp = if_find(ctx, 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;
|
|
}
|
|
|
|
reload_config(ctx);
|
|
/* XXX: Respect initial commandline options? */
|
|
reconf_reboot(ctx, do_reboot, argc, argv, optind);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct dhcpcd_ctx ctx;
|
|
char pidfile[sizeof(PIDFILE) + IF_NAMESIZE + 1];
|
|
struct if_options *ifo;
|
|
struct interface *ifp;
|
|
uint16_t family = 0;
|
|
int opt, oi = 0, i;
|
|
time_t t;
|
|
ssize_t len;
|
|
#if defined(USE_SIGNALS) || !defined(THERE_IS_NO_FORK)
|
|
pid_t pid;
|
|
#endif
|
|
#ifdef USE_SIGNALS
|
|
int sig;
|
|
const char *siga;
|
|
#endif
|
|
struct timespec ts;
|
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
#ifdef USE_SIGNALS
|
|
dhcpcd_ctx = &ctx;
|
|
sig = 0;
|
|
siga = NULL;
|
|
#endif
|
|
closefrom(3);
|
|
#ifdef LOG_PERROR
|
|
openlog(PACKAGE, LOG_PERROR | LOG_PID, LOG_DAEMON);
|
|
#else
|
|
openlog(PACKAGE, LOG_PID, LOG_DAEMON);
|
|
#endif
|
|
setlogmask(LOG_UPTO(LOG_INFO));
|
|
#ifndef LOG_PERROR
|
|
psyslog_prio = LOG_UPTO(LOG_INFO);
|
|
#endif
|
|
|
|
/* Test for --help and --version */
|
|
if (argc > 1) {
|
|
if (strcmp(argv[1], "--help") == 0) {
|
|
usage();
|
|
return EXIT_SUCCESS;
|
|
} else if (strcmp(argv[1], "--version") == 0) {
|
|
printf(""PACKAGE" "VERSION"\n%s\n", dhcpcd_copyright);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|
|
|
|
ifo = NULL;
|
|
ctx.cffile = CONFIG;
|
|
ctx.pid_fd = ctx.control_fd = ctx.control_unpriv_fd = ctx.link_fd = -1;
|
|
TAILQ_INIT(&ctx.control_fds);
|
|
#ifdef PLUGIN_DEV
|
|
ctx.dev_fd = -1;
|
|
#endif
|
|
#ifdef INET
|
|
ctx.udp_fd = -1;
|
|
#endif
|
|
i = 0;
|
|
while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
|
|
{
|
|
switch (opt) {
|
|
case '4':
|
|
if (family) {
|
|
syslog(LOG_ERR, "cannot specify more than one"
|
|
" address family");
|
|
goto exit_failure;
|
|
}
|
|
family = AF_INET;
|
|
break;
|
|
case '6':
|
|
if (family) {
|
|
syslog(LOG_ERR, "cannot specify more than one"
|
|
" address family");
|
|
goto exit_failure;
|
|
}
|
|
family = AF_INET6;
|
|
break;
|
|
case 'f':
|
|
ctx.cffile = optarg;
|
|
break;
|
|
#ifdef USE_SIGNALS
|
|
case 'g':
|
|
sig = SIGUSR1;
|
|
siga = "USR1";
|
|
break;
|
|
case 'k':
|
|
sig = SIGALRM;
|
|
siga = "ARLM";
|
|
break;
|
|
case 'n':
|
|
sig = SIGHUP;
|
|
siga = "HUP";
|
|
break;
|
|
case 'x':
|
|
sig = SIGTERM;
|
|
siga = "TERM";;
|
|
break;
|
|
#endif
|
|
case 'T':
|
|
i = 1;
|
|
break;
|
|
case 'U':
|
|
if (i == 3)
|
|
i = 4;
|
|
else if (i != 4)
|
|
i = 3;
|
|
break;
|
|
case 'V':
|
|
i = 2;
|
|
break;
|
|
case '?':
|
|
usage();
|
|
goto exit_failure;
|
|
}
|
|
}
|
|
|
|
ctx.argv = argv;
|
|
ctx.argc = argc;
|
|
ctx.ifc = argc - optind;
|
|
ctx.ifv = argv + optind;
|
|
|
|
ifo = read_config(&ctx, NULL, NULL, NULL);
|
|
if (ifo == NULL)
|
|
goto exit_failure;
|
|
opt = add_options(&ctx, NULL, ifo, argc, argv);
|
|
if (opt != 1) {
|
|
if (opt == 0)
|
|
usage();
|
|
goto exit_failure;
|
|
}
|
|
if (i == 2) {
|
|
printf("Interface options:\n");
|
|
if (optind == argc - 1) {
|
|
free_options(ifo);
|
|
ifo = read_config(&ctx, argv[optind], NULL, NULL);
|
|
if (ifo == NULL)
|
|
goto exit_failure;
|
|
add_options(&ctx, NULL, ifo, argc, argv);
|
|
}
|
|
if_printoptions();
|
|
#ifdef INET
|
|
if (family == 0 || family == AF_INET) {
|
|
printf("\nDHCPv4 options:\n");
|
|
dhcp_printoptions(&ctx,
|
|
ifo->dhcp_override, ifo->dhcp_override_len);
|
|
}
|
|
#endif
|
|
#ifdef INET6
|
|
if (family == 0 || family == AF_INET6) {
|
|
printf("\nDHCPv6 options:\n");
|
|
dhcp6_printoptions(&ctx,
|
|
ifo->dhcp6_override, ifo->dhcp6_override_len);
|
|
}
|
|
#endif
|
|
goto exit_success;
|
|
}
|
|
ctx.options = ifo->options;
|
|
if (i != 0) {
|
|
if (i == 1)
|
|
ctx.options |= DHCPCD_TEST;
|
|
else
|
|
ctx.options |= DHCPCD_DUMPLEASE;
|
|
if (i == 4)
|
|
ctx.options |= DHCPCD_PFXDLGONLY;
|
|
ctx.options |= DHCPCD_PERSISTENT;
|
|
ctx.options &= ~DHCPCD_DAEMONISE;
|
|
}
|
|
|
|
#ifdef THERE_IS_NO_FORK
|
|
ctx.options &= ~DHCPCD_DAEMONISE;
|
|
#endif
|
|
|
|
if (ctx.options & DHCPCD_DEBUG) {
|
|
setlogmask(LOG_UPTO(LOG_DEBUG));
|
|
#ifndef LOG_PERROR
|
|
psyslog_prio = LOG_UPTO(LOG_DEBUG);
|
|
#endif
|
|
}
|
|
if (ctx.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 (!(ctx.options & (DHCPCD_TEST | DHCPCD_DUMPLEASE))) {
|
|
/* If we have any other args, we should run as a single dhcpcd
|
|
* instance for that interface. */
|
|
if (optind == argc - 1 && !(ctx.options & DHCPCD_MASTER)) {
|
|
const char *per;
|
|
|
|
if (strlen(argv[optind]) > IF_NAMESIZE) {
|
|
syslog(LOG_ERR, "%s: interface name too long",
|
|
argv[optind]);
|
|
goto exit_failure;
|
|
}
|
|
/* Allow a dhcpcd interface per address family */
|
|
switch(family) {
|
|
case AF_INET:
|
|
per = "-4";
|
|
break;
|
|
case AF_INET6:
|
|
per = "-6";
|
|
break;
|
|
default:
|
|
per = "";
|
|
}
|
|
snprintf(pidfile, sizeof(pidfile),
|
|
PIDFILE, "-", argv[optind], per);
|
|
} else {
|
|
snprintf(pidfile, sizeof(pidfile), PIDFILE, "", "", "");
|
|
ctx.options |= DHCPCD_MASTER;
|
|
}
|
|
}
|
|
|
|
if (chdir("/") == -1)
|
|
syslog(LOG_ERR, "chdir `/': %m");
|
|
|
|
/* Freeing allocated addresses from dumping leases can trigger
|
|
* eloop removals as well, so init here. */
|
|
ctx.eloop = eloop_init();
|
|
if (ctx.eloop == NULL) {
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
goto exit_failure;
|
|
}
|
|
|
|
if (ctx.options & DHCPCD_DUMPLEASE) {
|
|
if (optind != argc - 1) {
|
|
syslog(LOG_ERR, "dumplease requires an interface");
|
|
goto exit_failure;
|
|
}
|
|
i = 0;
|
|
/* We need to try and find the interface so we can
|
|
* load the hardware address to compare automated IAID */
|
|
ctx.ifaces = if_discover(&ctx, 1, argv + optind);
|
|
if (ctx.ifaces == NULL) {
|
|
syslog(LOG_ERR, "if_discover: %m");
|
|
goto exit_failure;
|
|
}
|
|
ifp = TAILQ_FIRST(ctx.ifaces);
|
|
if (ifp == NULL) {
|
|
ifp = calloc(1, sizeof(*ifp));
|
|
if (ifp == NULL) {
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
goto exit_failure;
|
|
}
|
|
strlcpy(ifp->name, argv[optind], sizeof(ifp->name));
|
|
ifp->ctx = &ctx;
|
|
TAILQ_INSERT_HEAD(ctx.ifaces, ifp, next);
|
|
}
|
|
configure_interface(ifp, ctx.argc, ctx.argv);
|
|
if (ctx.options & DHCPCD_PFXDLGONLY)
|
|
ifp->options->options |= DHCPCD_PFXDLGONLY;
|
|
if (family == 0 || family == AF_INET) {
|
|
if (dhcp_dump(ifp) == -1)
|
|
i = 1;
|
|
}
|
|
if (family == 0 || family == AF_INET6) {
|
|
if (dhcp6_dump(ifp) == -1)
|
|
i = 1;
|
|
}
|
|
if (i == -1)
|
|
goto exit_failure;
|
|
goto exit_success;
|
|
}
|
|
|
|
#ifdef USE_SIGNALS
|
|
if (!(ctx.options & DHCPCD_TEST) &&
|
|
(sig == 0 || ctx.ifc != 0))
|
|
{
|
|
#endif
|
|
if (ctx.options & DHCPCD_MASTER)
|
|
i = -1;
|
|
else
|
|
i = control_open(&ctx, argv[optind]);
|
|
if (i == -1)
|
|
i = control_open(&ctx, NULL);
|
|
if (i != -1) {
|
|
syslog(LOG_INFO,
|
|
"sending commands to master dhcpcd process");
|
|
len = control_send(&ctx, argc, argv);
|
|
control_close(&ctx);
|
|
if (len > 0) {
|
|
syslog(LOG_DEBUG, "send OK");
|
|
goto exit_success;
|
|
} else {
|
|
syslog(LOG_ERR, "failed to send commands");
|
|
goto exit_failure;
|
|
}
|
|
} else {
|
|
if (errno != ENOENT)
|
|
syslog(LOG_ERR, "control_open: %m");
|
|
}
|
|
#ifdef USE_SIGNALS
|
|
}
|
|
#endif
|
|
|
|
if (geteuid())
|
|
syslog(LOG_WARNING,
|
|
PACKAGE " will not work correctly unless run as root");
|
|
|
|
#ifdef USE_SIGNALS
|
|
if (sig != 0) {
|
|
pid = read_pid(pidfile);
|
|
if (pid != 0)
|
|
syslog(LOG_INFO, "sending signal %s to pid %d",
|
|
siga, pid);
|
|
if (pid == 0 || kill(pid, sig) != 0) {
|
|
if (sig != SIGHUP && errno != EPERM)
|
|
syslog(LOG_ERR, ""PACKAGE" not running");
|
|
if (pid != 0 && errno != ESRCH) {
|
|
syslog(LOG_ERR, "kill: %m");
|
|
goto exit_failure;
|
|
}
|
|
unlink(pidfile);
|
|
if (sig != SIGHUP)
|
|
goto exit_failure;
|
|
} else {
|
|
if (sig == SIGHUP || sig == SIGUSR1)
|
|
goto 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(pidfile) == 0)
|
|
goto exit_success;
|
|
}
|
|
syslog(LOG_ERR, "pid %d failed to exit", pid);
|
|
goto exit_failure;
|
|
}
|
|
}
|
|
|
|
if (!(ctx.options & DHCPCD_TEST)) {
|
|
if ((pid = read_pid(pidfile)) > 0 &&
|
|
kill(pid, 0) == 0)
|
|
{
|
|
syslog(LOG_ERR, ""PACKAGE
|
|
" already running on pid %d (%s)",
|
|
pid, pidfile);
|
|
goto 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);
|
|
|
|
opt = O_WRONLY | O_CREAT | O_NONBLOCK;
|
|
#ifdef O_CLOEXEC
|
|
opt |= O_CLOEXEC;
|
|
#endif
|
|
ctx.pid_fd = open(pidfile, opt, 0664);
|
|
if (ctx.pid_fd == -1)
|
|
syslog(LOG_ERR, "open `%s': %m", pidfile);
|
|
else {
|
|
#ifdef LOCK_EX
|
|
/* Lock the file so that only one instance of dhcpcd
|
|
* runs on an interface */
|
|
if (flock(ctx.pid_fd, LOCK_EX | LOCK_NB) == -1) {
|
|
syslog(LOG_ERR, "flock `%s': %m", pidfile);
|
|
close(ctx.pid_fd);
|
|
ctx.pid_fd = -1;
|
|
goto exit_failure;
|
|
}
|
|
#endif
|
|
#ifndef O_CLOEXEC
|
|
if (fcntl(ctx.pid_fd, F_GETFD, &opt) == -1 ||
|
|
fcntl(ctx.pid_fd, F_SETFD, opt | FD_CLOEXEC) == -1)
|
|
{
|
|
syslog(LOG_ERR, "fcntl: %m");
|
|
close(ctx.pid_fd);
|
|
ctx.pid_fd = -1;
|
|
goto exit_failure;
|
|
}
|
|
#endif
|
|
write_pid(ctx.pid_fd, getpid());
|
|
}
|
|
}
|
|
|
|
|
|
if (ctx.options & DHCPCD_MASTER) {
|
|
if (control_start(&ctx, NULL) == -1)
|
|
syslog(LOG_ERR, "control_start: %m");
|
|
}
|
|
#else
|
|
if (control_start(&ctx,
|
|
ctx.options & DHCPCD_MASTER ? NULL : argv[optind]) == -1)
|
|
{
|
|
syslog(LOG_ERR, "control_start: %m");
|
|
goto exit_failure;
|
|
}
|
|
#endif
|
|
|
|
syslog(LOG_INFO, "version " VERSION " starting");
|
|
ctx.options |= DHCPCD_STARTED;
|
|
#ifdef USE_SIGNALS
|
|
/* Save signal mask, block and redirect signals to our handler */
|
|
if (signal_init(&ctx.sigset) == -1) {
|
|
syslog(LOG_ERR, "signal_setup: %m");
|
|
goto exit_failure;
|
|
}
|
|
#endif
|
|
|
|
/* When running dhcpcd against a single interface, we need to retain
|
|
* the old behaviour of waiting for an IP address */
|
|
if (ctx.ifc == 1 && !(ctx.options & DHCPCD_BACKGROUND))
|
|
ctx.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
|
|
* dhcpcd_handlecarrier instead.
|
|
* We also need to open this before checking for interfaces below
|
|
* so that we pickup any new addresses during the discover phase. */
|
|
ctx.link_fd = if_openlinksocket();
|
|
if (ctx.link_fd == -1)
|
|
syslog(LOG_ERR, "open_link_socket: %m");
|
|
else
|
|
eloop_event_add(ctx.eloop, ctx.link_fd,
|
|
handle_link, &ctx, NULL, NULL);
|
|
|
|
/* Start any dev listening plugin which may want to
|
|
* change the interface name provided by the kernel */
|
|
if ((ctx.options & (DHCPCD_MASTER | DHCPCD_DEV)) ==
|
|
(DHCPCD_MASTER | DHCPCD_DEV))
|
|
dev_start(&ctx);
|
|
|
|
ctx.ifaces = if_discover(&ctx, ctx.ifc, ctx.ifv);
|
|
if (ctx.ifaces == NULL) {
|
|
syslog(LOG_ERR, "if_discover: %m");
|
|
goto exit_failure;
|
|
}
|
|
for (i = 0; i < ctx.ifc; i++) {
|
|
if (if_find(&ctx, ctx.ifv[i]) == NULL)
|
|
syslog(LOG_ERR, "%s: interface not found or invalid",
|
|
ctx.ifv[i]);
|
|
}
|
|
if (TAILQ_FIRST(ctx.ifaces) == NULL) {
|
|
if (ctx.ifc == 0)
|
|
syslog(LOG_ERR, "no valid interfaces found");
|
|
else
|
|
goto exit_failure;
|
|
if (!(ctx.options & DHCPCD_LINK)) {
|
|
syslog(LOG_ERR,
|
|
"aborting as link detection is disabled");
|
|
goto exit_failure;
|
|
}
|
|
}
|
|
|
|
TAILQ_FOREACH(ifp, ctx.ifaces, next) {
|
|
dhcpcd_initstate1(ifp, argc, argv);
|
|
}
|
|
|
|
if (ctx.options & DHCPCD_BACKGROUND && dhcpcd_daemonise(&ctx))
|
|
goto exit_success;
|
|
|
|
opt = 0;
|
|
TAILQ_FOREACH(ifp, ctx.ifaces, next) {
|
|
run_preinit(ifp);
|
|
if (ifp->carrier != LINK_DOWN)
|
|
opt = 1;
|
|
}
|
|
|
|
if (!(ctx.options & DHCPCD_BACKGROUND)) {
|
|
if (ctx.options & DHCPCD_MASTER)
|
|
t = ifo->timeout;
|
|
else if ((ifp = TAILQ_FIRST(ctx.ifaces)))
|
|
t = ifp->options->timeout;
|
|
else
|
|
t = 0;
|
|
if (opt == 0 &&
|
|
ctx.options & DHCPCD_LINK &&
|
|
!(ctx.options & DHCPCD_WAITIP))
|
|
{
|
|
syslog(LOG_WARNING, "no interfaces have a carrier");
|
|
if (dhcpcd_daemonise(&ctx))
|
|
goto exit_success;
|
|
} else if (t > 0 && ctx.options & DHCPCD_DAEMONISE) {
|
|
eloop_timeout_add_sec(ctx.eloop, t,
|
|
handle_exit_timeout, &ctx);
|
|
}
|
|
}
|
|
free_options(ifo);
|
|
ifo = NULL;
|
|
|
|
ipv4_sortinterfaces(&ctx);
|
|
TAILQ_FOREACH(ifp, ctx.ifaces, next) {
|
|
eloop_timeout_add_sec(ctx.eloop, 0,
|
|
dhcpcd_prestartinterface, ifp);
|
|
}
|
|
|
|
i = eloop_start(&ctx);
|
|
goto exit1;
|
|
|
|
exit_success:
|
|
i = EXIT_SUCCESS;
|
|
goto exit1;
|
|
|
|
exit_failure:
|
|
i = EXIT_FAILURE;
|
|
|
|
exit1:
|
|
/* Free memory and close fd's */
|
|
if (ctx.ifaces) {
|
|
while ((ifp = TAILQ_FIRST(ctx.ifaces))) {
|
|
TAILQ_REMOVE(ctx.ifaces, ifp, next);
|
|
if_free(ifp);
|
|
}
|
|
free(ctx.ifaces);
|
|
}
|
|
free(ctx.duid);
|
|
if (ctx.link_fd != -1) {
|
|
eloop_event_delete(ctx.eloop, ctx.link_fd, 0);
|
|
close(ctx.link_fd);
|
|
}
|
|
|
|
free_options(ifo);
|
|
free_globals(&ctx);
|
|
ipv4_ctxfree(&ctx);
|
|
ipv6_ctxfree(&ctx);
|
|
dev_stop(&ctx);
|
|
if (control_stop(&ctx) == -1)
|
|
syslog(LOG_ERR, "control_stop: %m:");
|
|
if (ctx.pid_fd != -1) {
|
|
close(ctx.pid_fd);
|
|
unlink(pidfile);
|
|
}
|
|
eloop_free(ctx.eloop);
|
|
|
|
if (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED))
|
|
syslog(LOG_INFO, "exited");
|
|
closelog();
|
|
return i;
|
|
}
|