Update to dhcpcd-9.2.0 with the following changes:

* route: ensure IPv4LL routes come last in priority
 * DHCP: fix many issues with extending the last lease
 * privsep: don't read control group from config in privsep
 * privsep: only the master process responds to signals
 * privsep: use a socketpair for stderr/stdin rather than dupping /dev/null
 * privsep: right limit stdin/stderr/stdout
 * privsep: dumping a lease is now run in a sandbox
 * options: check if kernel supports INET or INET6 before enabling default
 * options: let clientid override a prior duid
 * options: allow -1 to represent infinity for requested lease time
 * dhcpcd: fix a crash initing a new interface after route overflow
This commit is contained in:
roy 2020-09-06 14:54:28 +00:00
parent 61b23c6195
commit cd40ae1fd5
25 changed files with 368 additions and 254 deletions

View File

@ -29,7 +29,7 @@
#define CONFIG_H
#define PACKAGE "dhcpcd"
#define VERSION "9.1.4"
#define VERSION "9.2.0"
#ifndef PRIVSEP_USER
# define PRIVSEP_USER "_" PACKAGE

View File

@ -777,7 +777,7 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type)
(type == DHCP_REQUEST &&
state->addr->mask.s_addr == lease->mask.s_addr &&
(state->new == NULL || IS_DHCP(state->new)) &&
!(state->added & STATE_FAKE))))
!(state->added & (STATE_FAKE | STATE_EXPIRED)))))
bootp->ciaddr = state->addr->addr.s_addr;
bootp->op = BOOTREQUEST;
@ -836,7 +836,7 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type)
if (type == DHCP_DECLINE ||
(type == DHCP_REQUEST &&
(state->addr == NULL ||
state->added & STATE_FAKE ||
state->added & (STATE_FAKE | STATE_EXPIRED) ||
lease->addr.s_addr != state->addr->addr.s_addr)))
{
putip = true;
@ -1745,7 +1745,7 @@ send_message(struct interface *ifp, uint8_t type,
goto fail;
len = (size_t)r;
if (!(state->added & STATE_FAKE) &&
if (!(state->added & (STATE_FAKE | STATE_EXPIRED)) &&
state->addr != NULL &&
ipv4_iffindaddr(ifp, &state->lease.addr, NULL) != NULL)
from.s_addr = state->lease.addr.s_addr;
@ -1869,14 +1869,16 @@ dhcp_discover(void *arg)
state->state = DHS_DISCOVER;
dhcp_new_xid(ifp);
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
if (ifo->fallback)
eloop_timeout_add_sec(ifp->ctx->eloop,
ifo->reboot, dhcp_fallback, ifp);
if (!(state->added & STATE_EXPIRED)) {
if (ifo->fallback)
eloop_timeout_add_sec(ifp->ctx->eloop,
ifo->reboot, dhcp_fallback, ifp);
#ifdef IPV4LL
else if (ifo->options & DHCPCD_IPV4LL)
eloop_timeout_add_sec(ifp->ctx->eloop,
ifo->reboot, ipv4ll_start, ifp);
else if (ifo->options & DHCPCD_IPV4LL)
eloop_timeout_add_sec(ifp->ctx->eloop,
ifo->reboot, ipv4ll_start, ifp);
#endif
}
if (ifo->options & DHCPCD_REQUEST)
loginfox("%s: soliciting a DHCP lease (requesting %s)",
ifp->name, inet_ntoa(ifo->req_addr));
@ -1896,31 +1898,22 @@ dhcp_request(void *arg)
send_request(ifp);
}
static void
dhcp_expire1(struct interface *ifp)
{
struct dhcp_state *state = D_STATE(ifp);
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
dhcp_drop(ifp, "EXPIRE");
dhcp_unlink(ifp->ctx, state->leasefile);
state->interval = 0;
if (!(ifp->options->options & DHCPCD_LINK) || ifp->carrier > LINK_DOWN)
dhcp_discover(ifp);
}
static void
dhcp_expire(void *arg)
{
struct interface *ifp = arg;
struct dhcp_state *state = D_STATE(ifp);
if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {
logwarnx("%s: DHCP lease expired, extending lease", ifp->name);
return;
state->added |= STATE_EXPIRED;
} else {
logerrx("%s: DHCP lease expired", ifp->name);
dhcp_drop(ifp, "EXPIRE");
dhcp_unlink(ifp->ctx, state->leasefile);
}
logerrx("%s: DHCP lease expired", ifp->name);
dhcp_expire1(ifp);
state->interval = 0;
dhcp_discover(ifp);
}
#if defined(ARP) || defined(IN_IFF_DUPLICATED)
@ -2291,7 +2284,9 @@ dhcp_bind(struct interface *ifp)
return;
}
if (state->reason == NULL) {
if (state->old && !(state->added & STATE_FAKE)) {
if (state->old &&
!(state->added & (STATE_FAKE | STATE_EXPIRED)))
{
if (state->old->yiaddr == state->new->yiaddr &&
lease->server.s_addr &&
state->state != DHS_REBIND)
@ -2364,19 +2359,6 @@ dhcp_bind(struct interface *ifp)
eloop_event_add(ctx->eloop, state->udp_rfd, dhcp_handleifudp, ifp);
}
static void
dhcp_lastlease(void *arg)
{
struct interface *ifp = arg;
struct dhcp_state *state = D_STATE(ifp);
loginfox("%s: timed out contacting a DHCP server, using last lease",
ifp->name);
dhcp_bind(ifp);
state->interval = 0;
dhcp_discover(ifp);
}
static size_t
dhcp_message_new(struct bootp **bootp,
const struct in_addr *addr, const struct in_addr *mask)
@ -2475,6 +2457,26 @@ dhcp_arp_bind(struct interface *ifp)
}
#endif
static void
dhcp_lastlease(void *arg)
{
struct interface *ifp = arg;
struct dhcp_state *state = D_STATE(ifp);
loginfox("%s: timed out contacting a DHCP server, using last lease",
ifp->name);
#if defined(ARP) || defined(KERNEL_RFC5227)
dhcp_arp_bind(ifp);
#else
dhcp_bind(ifp);
#endif
/* Set expired here because dhcp_bind() -> ipv4_addaddr() will reset
* state */
state->added |= STATE_EXPIRED;
state->interval = 0;
dhcp_discover(ifp);
}
static void
dhcp_static(struct interface *ifp)
{

View File

@ -24,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd May 31, 2020
.Dd September 2, 2020
.Dt DHCPCD 8
.Os
.Sh NAME
@ -385,8 +385,10 @@ If no interfaces are left running,
.Nm
will exit.
.It Fl l , Fl Fl leasetime Ar seconds
Request a specific lease time in
Request a lease time of
.Ar seconds .
.Ar -1
represents an infinite lease time.
By default
.Nm
does not request any lease time and leaves it in the hands of the

View File

@ -336,7 +336,7 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
#ifdef THERE_IS_NO_FORK
eloop_timeout_delete(ctx->eloop, handle_exit_timeout, ctx);
errno = ENOSYS;
return 0;
return;
#else
int i;
unsigned int logopts = loggetopts();
@ -361,8 +361,8 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
/* Don't use loginfo because this makes no sense in a log. */
if (!(logopts & LOGERR_QUIET))
(void)fprintf(stderr, "forked to background, child pid %d\n",
getpid());
(void)fprintf(stderr,
"forked to background, child pid %d\n", getpid());
i = EXIT_SUCCESS;
if (write(ctx->fork_fd, &i, sizeof(i)) == -1)
logerr("write");
@ -371,11 +371,18 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
close(ctx->fork_fd);
ctx->fork_fd = -1;
if (isatty(loggeterrfd())) {
logopts &= ~LOGERR_ERR;
logsetopts(logopts);
logseterrfd(-1);
}
/*
* Stop writing to stderr.
* On the happy path, only the master process writes to stderr,
* so this just stops wasting fprintf calls to nowhere.
* All other calls - ie errors in privsep processes or script output,
* will error when printing.
* If we *really* want to fix that, then we need to suck
* stderr/stdout in the master process and either disacrd it or pass
* it to the launcher process and then to stderr.
*/
logopts &= ~LOGERR_ERR;
logsetopts(logopts);
#endif
}
@ -1153,6 +1160,15 @@ dhcpcd_setlinkrcvbuf(struct dhcpcd_ctx *ctx)
}
#endif
static void
dhcpcd_runprestartinterface(void *arg)
{
struct interface *ifp = arg;
run_preinit(ifp);
dhcpcd_prestartinterface(ifp);
}
void
dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx)
{
@ -1215,9 +1231,11 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx)
continue;
}
TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);
if (ifp->active)
if (ifp->active) {
dhcpcd_initstate(ifp, 0);
eloop_timeout_add_sec(ctx->eloop, 0,
dhcpcd_prestartinterface, ifp);
dhcpcd_runprestartinterface, ifp);
}
}
free(ifaces);
@ -1765,6 +1783,24 @@ dhcpcd_fork_cb(void *arg)
eloop_exit(ctx->eloop, exit_code);
}
static void
dhcpcd_stderr_cb(void *arg)
{
struct dhcpcd_ctx *ctx = arg;
char log[BUFSIZ];
ssize_t len;
len = read(ctx->stderr_fd, log, sizeof(log));
if (len == -1) {
if (errno != ECONNRESET)
logerr(__func__);
return;
}
log[len] = '\0';
fprintf(stderr, "%s", log);
}
int
main(int argc, char **argv)
{
@ -1778,7 +1814,7 @@ main(int argc, char **argv)
ssize_t len;
#if defined(USE_SIGNALS) || !defined(THERE_IS_NO_FORK)
pid_t pid;
int sigpipe[2];
int fork_fd[2], stderr_fd[2];
#endif
#ifdef USE_SIGNALS
int sig = 0;
@ -2100,11 +2136,20 @@ printpidfile:
}
#endif
#ifdef PRIVSEP
ps_init(&ctx);
#endif
#ifndef SMALL
if (ctx.options & DHCPCD_DUMPLEASE &&
ioctl(fileno(stdin), FIONREAD, &i, sizeof(i)) == 0 &&
i > 0)
{
ctx.options |= DHCPCD_FORKED; /* pretend child process */
#ifdef PRIVSEP
if (IN_PRIVSEP(&ctx) && ps_mastersandbox(&ctx) == -1)
goto exit_failure;
#endif
ifp = calloc(1, sizeof(*ifp));
if (ifp == NULL) {
logerr(__func__);
@ -2153,6 +2198,14 @@ printpidfile:
ctx.control_fd = control_open(NULL, AF_UNSPEC,
ctx.options & DHCPCD_DUMPLEASE);
if (ctx.control_fd != -1) {
#ifdef PRIVSEP
ctx.options &= ~DHCPCD_FORKED;
if (IN_PRIVSEP(&ctx) && ps_mastersandbox(&ctx) == -1) {
ctx.options |= DHCPCD_FORKED;
goto exit_failure;
}
ctx.options |= DHCPCD_FORKED;
#endif
if (!(ctx.options & DHCPCD_DUMPLEASE))
loginfox("sending commands to dhcpcd process");
len = control_send(&ctx, argc, argv);
@ -2206,29 +2259,40 @@ printpidfile:
if (freopen(_PATH_DEVNULL, "r", stdin) == NULL)
logerr("%s: freopen stdin", __func__);
#ifdef PRIVSEP
ps_init(&ctx);
#endif
#ifdef USE_SIGNALS
if (pipe(sigpipe) == -1) {
logerr("pipe");
#if defined(USE_SIGNALS) && !defined(THERE_IS_NO_FORK)
if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fork_fd) == -1 ||
xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, stderr_fd) == -1)
{
logerr("socketpair");
goto exit_failure;
}
#ifdef HAVE_CAPSICUM
if (ps_rights_limit_fdpair(sigpipe) == -1) {
logerr("ps_rights_limit_fdpair");
goto exit_failure;
}
#endif
switch (pid = fork()) {
case -1:
logerr("fork");
goto exit_failure;
case 0:
ctx.fork_fd = sigpipe[1];
close(sigpipe[0]);
ctx.fork_fd = fork_fd[1];
close(fork_fd[0]);
#ifdef PRIVSEP_RIGHTS
if (ps_rights_limit_fd(fork_fd[1]) == -1) {
logerr("ps_rights_limit_fdpair");
goto exit_failure;
}
#endif
/*
* Redirect stderr to the stderr socketpair.
* Redirect stdout as well.
* dhcpcd doesn't output via stdout, but something in
* a called script might.
*
* Do NOT rights limit this fd as it will affect scripts.
* For example, cmp reports insufficient caps on FreeBSD.
*/
if (dup2(stderr_fd[1], STDERR_FILENO) == -1 ||
dup2(stderr_fd[1], STDOUT_FILENO) == -1)
logerr("dup2");
close(stderr_fd[0]);
close(stderr_fd[1]);
if (setsid() == -1) {
logerr("%s: setsid", __func__);
goto exit_failure;
@ -2248,10 +2312,22 @@ printpidfile:
break;
default:
ctx.options |= DHCPCD_FORKED; /* A lie */
ctx.fork_fd = sigpipe[0];
close(sigpipe[1]);
ctx.fork_fd = fork_fd[0];
close(fork_fd[1]);
ctx.stderr_fd = stderr_fd[0];
close(stderr_fd[1]);
#ifdef PRIVSEP_RIGHTS
if (ps_rights_limit_fd(fork_fd[0]) == -1 ||
ps_rights_limit_fd(stderr_fd[0]) == 1)
{
logerr("ps_rights_limit_fdpair");
goto exit_failure;
}
#endif
setproctitle("[launcher]");
eloop_event_add(ctx.eloop, ctx.fork_fd, dhcpcd_fork_cb, &ctx);
eloop_event_add(ctx.eloop, ctx.stderr_fd, dhcpcd_stderr_cb,
&ctx);
goto run_loop;
}
@ -2270,22 +2346,6 @@ printpidfile:
if_disable_rtadv();
#endif
if (isatty(STDOUT_FILENO) &&
freopen(_PATH_DEVNULL, "r", stdout) == NULL)
logerr("%s: freopen stdout", __func__);
if (isatty(STDERR_FILENO)) {
int fd = dup(STDERR_FILENO);
if (fd == -1)
logerr("%s: dup", __func__);
else if (logseterrfd(fd) == -1)
logerr("%s: logseterrfd", __func__);
else if (freopen(_PATH_DEVNULL, "r", stderr) == NULL) {
logseterrfd(-1);
logerr("%s: freopen stderr", __func__);
}
}
#ifdef PRIVSEP
if (IN_PRIVSEP(&ctx) && ps_start(&ctx) == -1) {
logerr("ps_start");

View File

@ -24,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd June 18, 2020
.Dd September 2, 2020
.Dt DHCPCD.CONF 5
.Os
.Sh NAME
@ -448,8 +448,14 @@ Enables IPv6 Router Advertisement solicitation.
This is on by default, but is documented here in the case where it is disabled
globally but needs to be enabled for one interface.
.It Ic leasetime Ar seconds
Request a leasetime of
Request a lease time of
.Ar seconds .
.Ar -1
represents an infinite lease time.
By default
.Nm dhcpcd
does not request any lease time and leaves it in the hands of the
DHCP server.
.It Ic link_rcvbuf Ar size
Override the size of the link receive buffer from the kernel default.
While

View File

@ -96,7 +96,6 @@ TAILQ_HEAD(if_head, interface);
#include "privsep.h"
#ifdef INET6
/* dhcpcd requires CMSG_SPACE to evaluate to a compile time constant. */
#if defined(__QNX) || \
(defined(__NetBSD_Version__) && __NetBSD_Version__ < 600000000)
@ -113,16 +112,13 @@ TAILQ_HEAD(if_head, interface);
#define CMSG_SPACE(len) (ALIGN(sizeof(struct cmsghdr)) + ALIGN(len))
#endif
#define IP6BUFLEN (CMSG_SPACE(sizeof(struct in6_pktinfo)) + \
CMSG_SPACE(sizeof(int)))
#endif
struct passwd;
struct dhcpcd_ctx {
char pidfile[sizeof(PIDFILE) + IF_NAMESIZE + 1];
char vendor[256];
int fork_fd; /* FD for the fork init signal pipe */
int stderr_fd; /* FD for logging to stderr */
const char *cffile;
unsigned long long options;
char *logfile;

View File

@ -703,9 +703,10 @@ eloop_start(struct eloop *eloop, sigset_t *signals)
if (eloop->exitnow)
break;
if (_eloop_nsig != 0 && eloop->signal_cb != NULL) {
if (_eloop_nsig != 0) {
n = _eloop_sig[--_eloop_nsig];
eloop->signal_cb(n, eloop->signal_cb_ctx);
if (eloop->signal_cb != NULL)
eloop->signal_cb(n, eloop->signal_cb_ctx);
continue;
}

View File

@ -59,6 +59,8 @@
#define SET_CONFIG_BLOCK(ifo) ((ifo)->options |= DHCPCD_FORKED)
#define CLEAR_CONFIG_BLOCK(ifo) ((ifo)->options &= ~DHCPCD_FORKED)
static unsigned long long default_options;
const struct option cf_options[] = {
{"background", no_argument, NULL, 'b'},
{"script", required_argument, NULL, 'c'},
@ -759,6 +761,10 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
break;
case 'l':
ARG_REQUIRED;
if (strcmp(arg, "-1") == 0) {
ifo->leasetime = DHCP_INFINITE_LIFETIME;
break;
}
ifo->leasetime = (uint32_t)strtou(arg, NULL,
0, 0, UINT32_MAX, &e);
if (e) {
@ -1021,6 +1027,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
}
ifo->options |= DHCPCD_CLIENTID;
ifo->clientid[0] = (uint8_t)s;
ifo->options &= ~DHCPCD_DUID;
break;
case 'J':
ifo->options |= DHCPCD_BROADCAST;
@ -1204,13 +1211,23 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
ifo->options |= DHCPCD_ONESHOT;
break;
case '4':
#ifdef INET
ifo->options &= ~DHCPCD_IPV6;
ifo->options |= DHCPCD_IPV4;
break;
#else
logerrx("INET has been compiled out");
return -1;
#endif
case '6':
#ifdef INET6
ifo->options &= ~DHCPCD_IPV4;
ifo->options |= DHCPCD_IPV6;
break;
#else
logerrx("INET6 has been compiled out");
return -1;
#endif
case O_IPV4:
ifo->options |= DHCPCD_IPV4;
break;
@ -2090,6 +2107,12 @@ invalid_token:
break;
case O_CONTROLGRP:
ARG_REQUIRED;
#ifdef PRIVSEP
/* Control group is already set by this point.
* We don't need to pledge getpw either with this. */
if (IN_PRIVSEP(ctx))
break;
#endif
#ifdef _REENTRANT
l = sysconf(_SC_GETGR_R_SIZE_MAX);
if (l == -1)
@ -2326,18 +2349,30 @@ read_config(struct dhcpcd_ctx *ctx,
/* Seed our default options */
if ((ifo = default_config(ctx)) == NULL)
return NULL;
ifo->options |= DHCPCD_DAEMONISE | DHCPCD_GATEWAY;
#ifdef PLUGIN_DEV
ifo->options |= DHCPCD_DEV;
#endif
if (default_options == 0) {
default_options |= DHCPCD_DAEMONISE | DHCPCD_GATEWAY;
#ifdef INET
ifo->options |= DHCPCD_IPV4 | DHCPCD_ARP | DHCPCD_DHCP | DHCPCD_IPV4LL;
skip = socket(PF_INET, SOCK_DGRAM, 0);
if (skip != -1) {
close(skip);
default_options |= DHCPCD_IPV4 | DHCPCD_ARP |
DHCPCD_DHCP | DHCPCD_IPV4LL;
}
#endif
#ifdef INET6
ifo->options |= DHCPCD_IPV6 | DHCPCD_IPV6RS;
ifo->options |= DHCPCD_IPV6RA_AUTOCONF | DHCPCD_IPV6RA_REQRDNSS;
ifo->options |= DHCPCD_DHCP6;
skip = socket(PF_INET6, SOCK_DGRAM, 0);
if (skip != -1) {
close(skip);
default_options |= DHCPCD_IPV6 | DHCPCD_IPV6RS |
DHCPCD_IPV6RA_AUTOCONF | DHCPCD_IPV6RA_REQRDNSS |
DHCPCD_DHCP6;
}
#endif
#ifdef PLUGIN_DEV
default_options |= DHCPCD_DEV;
#endif
}
ifo->options |= default_options;
CLEAR_CONFIG_BLOCK(ifo);

View File

@ -995,3 +995,50 @@ out:
return -1;
#endif
}
int
xsocketpair(int domain, int type, int protocol, int fd[2])
{
int s;
#if !defined(HAVE_SOCK_CLOEXEC) || !defined(HAVE_SOCK_NONBLOCK)
int xflags, xtype = type;
#endif
#ifndef HAVE_SOCK_CLOEXEC
if (xtype & SOCK_CLOEXEC)
type &= ~SOCK_CLOEXEC;
#endif
#ifndef HAVE_SOCK_NONBLOCK
if (xtype & SOCK_NONBLOCK)
type &= ~SOCK_NONBLOCK;
#endif
if ((s = socketpair(domain, type, protocol, fd)) == -1)
return -1;
#ifndef HAVE_SOCK_CLOEXEC
if ((xtype & SOCK_CLOEXEC) && ((xflags = fcntl(fd[0], F_GETFD)) == -1 ||
fcntl(fd[0], F_SETFD, xflags | FD_CLOEXEC) == -1))
goto out;
if ((xtype & SOCK_CLOEXEC) && ((xflags = fcntl(fd[1], F_GETFD)) == -1 ||
fcntl(fd[1], F_SETFD, xflags | FD_CLOEXEC) == -1))
goto out;
#endif
#ifndef HAVE_SOCK_NONBLOCK
if ((xtype & SOCK_NONBLOCK) && ((xflags = fcntl(fd[0], F_GETFL)) == -1 ||
fcntl(fd[0], F_SETFL, xflags | O_NONBLOCK) == -1))
goto out;
if ((xtype & SOCK_NONBLOCK) && ((xflags = fcntl(fd[1], F_GETFL)) == -1 ||
fcntl(fd[1], F_SETFL, xflags | O_NONBLOCK) == -1))
goto out;
#endif
return s;
#if !defined(HAVE_SOCK_CLOEXEC) || !defined(HAVE_SOCK_NONBLOCK)
out:
close(fd[0]);
close(fd[1]);
return -1;
#endif
}

View File

@ -224,6 +224,8 @@ int if_setmac(struct interface *ifp, void *, uint8_t);
#ifndef SOCK_CXNB
#define SOCK_CXNB SOCK_CLOEXEC | SOCK_NONBLOCK
#endif
int xsocket(int, int, int);
int xsocketpair(int, int, int, int[2]);
int if_route(unsigned char, const struct rt *rt);
int if_initrt(struct dhcpcd_ctx *, rb_tree_t *, int);
@ -259,7 +261,6 @@ int if_getlifetime6(struct ipv6_addr *);
int if_machinearch(char *, size_t);
struct interface *if_findifpfromcmsg(struct dhcpcd_ctx *,
struct msghdr *, int *);
int xsocket(int, int, int);
#ifdef __linux__
int if_linksocket(struct sockaddr_nl *, int, int);

View File

@ -661,8 +661,13 @@ ipv4_addaddr(struct interface *ifp, const struct in_addr *addr,
ia->mask = *mask;
ia->brd = *bcast;
#ifdef IP_LIFETIME
ia->vltime = vltime;
ia->pltime = pltime;
if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {
/* We don't want the kernel to expire the address. */
ia->vltime = ia->pltime = DHCP_INFINITE_LIFETIME;
} else {
ia->vltime = vltime;
ia->pltime = pltime;
}
#else
UNUSED(vltime);
UNUSED(pltime);

View File

@ -129,6 +129,7 @@ bool inet_getroutes(struct dhcpcd_ctx *, rb_tree_t *);
#define STATE_ADDED 0x01
#define STATE_FAKE 0x02
#define STATE_EXPIRED 0x04
int ipv4_deladdr(struct ipv4_addr *, int);
struct ipv4_addr *ipv4_addaddr(struct interface *,

View File

@ -111,6 +111,7 @@ ipv4ll_subnetroute(rb_tree_t *routes, struct interface *ifp)
in.s_addr = INADDR_ANY;
sa_in_init(&rt->rt_gateway, &in);
sa_in_init(&rt->rt_ifa, &state->addr->addr);
rt->rt_dflags |= RTDF_IPV4LL;
return rt_proto_add(routes, rt) ? 1 : 0;
}
@ -134,6 +135,10 @@ ipv4ll_defaultroute(rb_tree_t *routes, struct interface *ifp)
sa_in_init(&rt->rt_netmask, &in);
sa_in_init(&rt->rt_gateway, &in);
sa_in_init(&rt->rt_ifa, &state->addr->addr);
rt->rt_dflags |= RTDF_IPV4LL;
#ifdef HAVE_ROUTE_METRIC
rt->rt_metric += 10000;
#endif
return rt_proto_add(routes, rt) ? 1 : 0;
}

View File

@ -679,6 +679,14 @@ ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now)
/* Adjust plftime and vltime based on acquired time */
pltime = ia->prefix_pltime;
vltime = ia->prefix_vltime;
if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {
/* We don't want the kernel to expire the address.
* The saved times will be re-applied to the ia
* before exiting this function. */
ia->prefix_vltime = ia->prefix_pltime = ND6_INFINITE_LIFETIME;
}
if (timespecisset(&ia->acquired) &&
(ia->prefix_pltime != ND6_INFINITE_LIFETIME ||
ia->prefix_vltime != ND6_INFINITE_LIFETIME))
@ -1092,33 +1100,22 @@ ipv6_anyglobal(struct interface *sifp)
struct interface *ifp;
struct ipv6_state *state;
struct ipv6_addr *ia;
#ifdef BSD
bool forwarding;
#if defined(PRIVSEP) && defined(HAVE_PLEDGE)
/* BSD forwarding is either on or off.
* Linux forwarding is technically the same as it's
* configured by the "all" interface.
* Per interface only affects IsRouter of NA messages. */
#if defined(PRIVSEP) && (defined(HAVE_PLEDGE) || defined(__linux__))
if (IN_PRIVSEP(sifp->ctx))
forwarding = ps_root_ip6forwarding(sifp->ctx, NULL) == 1;
forwarding = ps_root_ip6forwarding(sifp->ctx, NULL) != 0;
else
#endif
forwarding = ip6_forwarding(NULL) == 1;
#endif
forwarding = ip6_forwarding(NULL) != 0;
TAILQ_FOREACH(ifp, sifp->ctx->ifaces, next) {
#ifdef BSD
if (ifp != sifp && !forwarding)
continue;
#else
#if defined(PRIVSEP) && defined(__linux__)
if (IN_PRIVSEP(sifp->ctx)) {
if (ifp != sifp &&
ps_root_ip6forwarding(sifp->ctx, ifp->name) != 1)
continue;
} else
#endif
if (ifp != sifp && ip6_forwarding(ifp->name) != 1)
continue;
#endif
state = IPV6_STATE(ifp);
if (state == NULL)

View File

@ -544,11 +544,11 @@ ipv6nd_advertise(struct ipv6_addr *ia)
na->nd_na_flags_reserved = ND_NA_FLAG_OVERRIDE;
#if defined(PRIVSEP) && (defined(__linux__) || defined(HAVE_PLEDGE))
if (IN_PRIVSEP(ctx)) {
if (ps_root_ip6forwarding(ctx, ifp->name) == 1)
if (ps_root_ip6forwarding(ctx, ifp->name) != 0)
na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
} else
#endif
if (ip6_forwarding(ifp->name) == 1)
if (ip6_forwarding(ifp->name) != 0)
na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
na->nd_na_target = ia->addr;

View File

@ -52,7 +52,6 @@
struct logctx {
char log_buf[BUFSIZ];
unsigned int log_opts;
FILE *log_err;
#ifndef SMALL
FILE *log_file;
#ifdef LOGERR_TAG
@ -120,14 +119,13 @@ vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
int len = 0, e;
va_list a;
#ifndef SMALL
FILE *err = ctx->log_err == NULL ? stderr : ctx->log_err;
bool log_pid;
#ifdef LOGERR_TAG
bool log_tag;
#endif
if ((stream == err && ctx->log_opts & LOGERR_ERR_DATE) ||
(stream != err && ctx->log_opts & LOGERR_LOG_DATE))
if ((stream == stderr && ctx->log_opts & LOGERR_ERR_DATE) ||
(stream != stderr && ctx->log_opts & LOGERR_LOG_DATE))
{
if ((e = logprintdate(stream)) == -1)
return -1;
@ -135,8 +133,8 @@ vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
}
#ifdef LOGERR_TAG
log_tag = ((stream == err && ctx->log_opts & LOGERR_ERR_TAG) ||
(stream != err && ctx->log_opts & LOGERR_LOG_TAG));
log_tag = ((stream == stderr && ctx->log_opts & LOGERR_ERR_TAG) ||
(stream != stderr && ctx->log_opts & LOGERR_LOG_TAG));
if (log_tag) {
if (ctx->log_tag == NULL)
ctx->log_tag = getprogname();
@ -146,8 +144,8 @@ vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
}
#endif
log_pid = ((stream == err && ctx->log_opts & LOGERR_ERR_PID) ||
(stream != err && ctx->log_opts & LOGERR_LOG_PID));
log_pid = ((stream == stderr && ctx->log_opts & LOGERR_ERR_PID) ||
(stream != stderr && ctx->log_opts & LOGERR_LOG_PID));
if (log_pid) {
if ((e = fprintf(stream, "[%d]", getpid())) == -1)
return -1;
@ -204,12 +202,7 @@ vlogmessage(int pri, const char *fmt, va_list args)
(pri <= LOG_ERR ||
(!(ctx->log_opts & LOGERR_QUIET) && pri <= LOG_INFO) ||
(ctx->log_opts & LOGERR_DEBUG && pri <= LOG_DEBUG)))
{
FILE *err;
err = ctx->log_err == NULL ? stderr : ctx->log_err;
len = vlogprintf_r(ctx, err, fmt, args);
}
len = vlogprintf_r(ctx, stderr, fmt, args);
if (!(ctx->log_opts & LOGERR_LOG))
return len;
@ -369,30 +362,6 @@ logsettag(const char *tag)
}
#endif
int
loggeterrfd(void)
{
struct logctx *ctx = &_logctx;
FILE *err = ctx->log_err == NULL ? stderr : ctx->log_err;
return fileno(err);
}
int
logseterrfd(int fd)
{
struct logctx *ctx = &_logctx;
if (ctx->log_err != NULL)
fclose(ctx->log_err);
if (fd == -1) {
ctx->log_err = NULL;
return 0;
}
ctx->log_err = fdopen(fd, "a");
return ctx->log_err == NULL ? -1 : 0;
}
int
logopen(const char *path)
{

View File

@ -97,8 +97,6 @@ void logsetopts(unsigned int);
void logsettag(const char *);
#endif
int loggeterrfd(void);
int logseterrfd(int);
int logopen(const char *);
void logclose(void);
int logreopen(void);

View File

@ -40,7 +40,6 @@
#include <assert.h>
#include <pwd.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -170,17 +169,6 @@ ps_bpf_start_bpf(void *arg)
return -1;
}
static void
ps_bpf_signal_bpfcb(int sig, void *arg)
{
struct dhcpcd_ctx *ctx = arg;
if (sig != SIGTERM)
return;
eloop_exit(ctx->eloop, EXIT_SUCCESS);
}
ssize_t
ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
{
@ -249,7 +237,7 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
start = ps_dostart(ctx,
&psp->psp_pid, &psp->psp_fd,
ps_bpf_recvmsg, NULL, psp,
ps_bpf_start_bpf, ps_bpf_signal_bpfcb,
ps_bpf_start_bpf, NULL,
PSF_DROPPRIVS);
switch (start) {
case -1:

View File

@ -27,7 +27,6 @@
*/
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
@ -95,18 +94,6 @@ ps_ctl_recvmsg(void *arg)
logerr(__func__);
}
static void
ps_ctl_signalcb(int sig, void *arg)
{
struct dhcpcd_ctx *ctx = arg;
if (sig != SIGTERM)
return;
shutdown(ctx->ps_control_fd, SHUT_RDWR);
eloop_exit(ctx->eloop, EXIT_SUCCESS);
}
ssize_t
ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len)
{
@ -238,20 +225,18 @@ ps_ctl_start(struct dhcpcd_ctx *ctx)
int data_fd[2], listen_fd[2];
pid_t pid;
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, data_fd) == -1)
return -1;
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1)
if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, data_fd) == -1 ||
xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1)
return -1;
#ifdef PRIVSEP_RIGHTS
if (ps_rights_limit_fdpair(data_fd) == -1)
return -1;
if (ps_rights_limit_fdpair(listen_fd) == -1)
if (ps_rights_limit_fdpair(data_fd) == -1 ||
ps_rights_limit_fdpair(listen_fd) == -1)
return -1;
#endif
pid = ps_dostart(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd,
ps_ctl_recvmsg, ps_ctl_dodispatch, ctx,
ps_ctl_startcb, ps_ctl_signalcb,
ps_ctl_startcb, NULL,
PSF_DROPPRIVS);
if (pid == -1)

View File

@ -34,7 +34,6 @@
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -291,18 +290,6 @@ ps_inet_recvmsg(void *arg)
logerr(__func__);
}
static void
ps_inet_signalcb(int sig, void *arg)
{
struct dhcpcd_ctx *ctx = arg;
if (sig != SIGTERM)
return;
shutdown(ctx->ps_inet_fd, SHUT_RDWR);
eloop_exit(ctx->eloop, EXIT_SUCCESS);
}
ssize_t
ps_inet_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
{
@ -347,7 +334,7 @@ ps_inet_start(struct dhcpcd_ctx *ctx)
pid = ps_dostart(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd,
ps_inet_recvmsg, ps_inet_dodispatch, ctx,
ps_inet_startcb, ps_inet_signalcb,
ps_inet_startcb, NULL,
PSF_DROPPRIVS);
#ifdef HAVE_CAPSICUM
@ -576,7 +563,7 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
start = ps_dostart(ctx,
&psp->psp_pid, &psp->psp_fd,
ps_inet_recvmsgpsp, NULL, psp,
start_func, ps_inet_signalcb,
start_func, NULL,
PSF_DROPPRIVS);
switch (start) {
case -1:

View File

@ -638,27 +638,46 @@ ps_root_startcb(void *arg)
/* Open network sockets for sending.
* This is a small bit wasteful for non sandboxed OS's
* but makes life very easy for unicasting DHCPv6 in non master
* mode as we no longer care about address selection. */
* mode as we no longer care about address selection.
* We can't call shutdown SHUT_RD on the socket because it's
* not connectd. All we can do is try and set a zero sized
* receive buffer and just let it overflow.
* Reading from it just to drain it is a waste of CPU time. */
#ifdef INET
if (ctx->options & DHCPCD_IPV4) {
int buflen = 1;
ctx->udp_wfd = xsocket(PF_INET,
SOCK_RAW | SOCK_CXNB, IPPROTO_UDP);
if (ctx->udp_wfd == -1)
logerr("%s: dhcp_openraw", __func__);
else if (setsockopt(ctx->udp_wfd, SOL_SOCKET, SO_RCVBUF,
&buflen, sizeof(buflen)) == -1)
logerr("%s: setsockopt SO_RCVBUF DHCP", __func__);
}
#endif
#ifdef INET6
if (ctx->options & DHCPCD_IPV6) {
int buflen = 1;
ctx->nd_fd = ipv6nd_open(false);
if (ctx->nd_fd == -1)
logerr("%s: ipv6nd_open", __func__);
else if (setsockopt(ctx->nd_fd, SOL_SOCKET, SO_RCVBUF,
&buflen, sizeof(buflen)) == -1)
logerr("%s: setsockopt SO_RCVBUF ND", __func__);
}
#endif
#ifdef DHCP6
if (ctx->options & DHCPCD_IPV6) {
int buflen = 1;
ctx->dhcp6_wfd = dhcp6_openraw();
if (ctx->dhcp6_wfd == -1)
logerr("%s: dhcp6_openraw", __func__);
else if (setsockopt(ctx->dhcp6_wfd, SOL_SOCKET, SO_RCVBUF,
&buflen, sizeof(buflen)) == -1)
logerr("%s: setsockopt SO_RCVBUF DHCP6", __func__);
}
#endif
@ -674,22 +693,14 @@ ps_root_startcb(void *arg)
}
static void
ps_root_signalcb(int sig, void *arg)
ps_root_signalcb(int sig, __unused void *arg)
{
struct dhcpcd_ctx *ctx = arg;
if (sig == SIGCHLD) {
while (waitpid(-1, NULL, WNOHANG) > 0)
;
return;
}
if (sig != SIGTERM)
return;
shutdown(ctx->ps_root_fd, SHUT_RDWR);
shutdown(ctx->ps_data_fd, SHUT_RDWR);
eloop_exit(ctx->eloop, EXIT_SUCCESS);
}
int (*handle_interface)(void *, int, const char *);

View File

@ -34,6 +34,7 @@
* Spawn an unpriv process to send/receive common network data.
* Then drop all privs and start running.
* Every process aside from the privileged actioneer is chrooted.
* All privsep processes ignore signals - only the master process accepts them.
*
* dhcpcd will maintain the config file in the chroot, no need to handle
* this in a script or something.
@ -74,6 +75,8 @@
#ifdef HAVE_CAPSICUM
#include <sys/capsicum.h>
#include <capsicum_helpers.h>
#define ps_rights_limit_stdio caph_limit_stdio
#endif
#ifdef HAVE_UTIL_H
#include <util.h>
@ -109,7 +112,7 @@ ps_init(struct dhcpcd_ctx *ctx)
return 0;
}
int
static int
ps_dropprivs(struct dhcpcd_ctx *ctx)
{
struct passwd *pw = ctx->ps_user;
@ -121,9 +124,10 @@ ps_dropprivs(struct dhcpcd_ctx *ctx)
if (chdir("/") == -1)
logerr("%s: chdir `/'", __func__);
if (setgroups(1, &pw->pw_gid) == -1 ||
if ((setgroups(1, &pw->pw_gid) == -1 ||
setgid(pw->pw_gid) == -1 ||
setuid(pw->pw_uid) == -1)
setuid(pw->pw_uid) == -1) &&
(errno != EPERM || ctx->options & DHCPCD_FORKED))
{
logerr("failed to drop privileges");
return -1;
@ -162,7 +166,7 @@ ps_dropprivs(struct dhcpcd_ctx *ctx)
/* Prohibit writing to files.
* Obviously this won't work if we are using a logfile
* or redirecting stderr to a file. */
if (ctx->logfile == NULL && isatty(loggeterrfd())) {
if (ctx->logfile == NULL) {
if (setrlimit(RLIMIT_FSIZE, &rzero) == -1)
logerr("setrlimit RLIMIT_FSIZE");
}
@ -283,12 +287,10 @@ ps_dostart(struct dhcpcd_ctx *ctx,
void *recv_ctx, int (*callback)(void *), void (*signal_cb)(int, void *),
unsigned int flags)
{
int stype;
int fd[2];
pid_t pid;
stype = SOCK_CLOEXEC | SOCK_NONBLOCK;
if (socketpair(AF_UNIX, SOCK_DGRAM | stype, 0, fd) == -1) {
if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1) {
logerr("%s: socketpair", __func__);
return -1;
}
@ -341,6 +343,14 @@ ps_dostart(struct dhcpcd_ctx *ctx,
close(ctx->ps_root_fd);
ctx->ps_root_fd = -1;
}
#ifdef PRIVSEP_RIGHTS
/* We cannot limit the root process in any way. */
if (ps_rights_limit_stdio() == -1) {
logerr("ps_rights_limit_stdio");
goto errexit;
}
#endif
}
if (priv_fd != &ctx->ps_inet_fd && ctx->ps_inet_fd != -1) {
@ -471,9 +481,10 @@ ps_mastersandbox(struct dhcpcd_ctx *ctx)
}
#ifdef PRIVSEP_RIGHTS
if ((ps_rights_limit_ioctl(ctx->pf_inet_fd) == -1 ||
ps_rights_limit_fd(ctx->link_fd) == -1) &&
errno != ENOSYS)
if ((ctx->pf_inet_fd != -1 &&
ps_rights_limit_ioctl(ctx->pf_inet_fd) == -1) ||
(ctx->link_fd != -1 && ps_rights_limit_fd(ctx->link_fd) == -1) ||
ps_rights_limit_stdio() == -1)
{
logerr("%s: cap_rights_limit", __func__);
return -1;
@ -645,12 +656,12 @@ ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd,
iovlen = 1;
len = writev(fd, iov, iovlen);
#ifdef PRIVSEP_DEBUG
logdebugx("%s: %zd", __func__, len);
#endif
if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED &&
!(ctx->options & DHCPCD_PRIVSEPROOT))
eloop_exit(ctx->eloop, len == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
if (len == -1) {
logerr(__func__);
if (ctx->options & DHCPCD_FORKED &&
!(ctx->options & DHCPCD_PRIVSEPROOT))
eloop_exit(ctx->eloop, EXIT_FAILURE);
}
return len;
}
@ -789,10 +800,9 @@ ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd)
};
ssize_t len = recvmsg(rfd, &msg, 0);
#ifdef PRIVSEP_DEBUG
logdebugx("%s: recv fd %d, %zd bytes", __func__, rfd, len);
#endif
if (len == -1)
logerr("%s: recvmsg", __func__);
if (len == -1 || len == 0) {
if (ctx->options & DHCPCD_FORKED &&
!(ctx->options & DHCPCD_PRIVSEPROOT))
@ -803,12 +813,12 @@ ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd)
iov[0].iov_len = (size_t)len;
len = ps_sendcmdmsg(wfd, cmd, &msg);
#ifdef PRIVSEP_DEBUG
logdebugx("%s: send fd %d, %zu bytes", __func__, wfd, len);
#endif
if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED &&
!(ctx->options & DHCPCD_PRIVSEPROOT))
eloop_exit(ctx->eloop, len == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
if (len == -1) {
logerr("ps_sendcmdmsg");
if (ctx->options & DHCPCD_FORKED &&
!(ctx->options & DHCPCD_PRIVSEPROOT))
eloop_exit(ctx->eloop, EXIT_FAILURE);
}
return len;
}

View File

@ -166,7 +166,6 @@ TAILQ_HEAD(ps_process_head, ps_process);
#endif
int ps_init(struct dhcpcd_ctx *);
int ps_dropprivs(struct dhcpcd_ctx *);
int ps_start(struct dhcpcd_ctx *);
int ps_stop(struct dhcpcd_ctx *);
int ps_mastersandbox(struct dhcpcd_ctx *);

View File

@ -168,6 +168,14 @@ rt_compare_proto(void *context, const void *node1, const void *node2)
if (c != 0)
return -c;
#ifdef INET
/* IPv4LL routes always come last */
if (rt1->rt_dflags & RTDF_IPV4LL && !(rt2->rt_dflags & RTDF_IPV4LL))
return -1;
else if (!(rt1->rt_dflags & RTDF_IPV4LL) && rt2->rt_dflags & RTDF_IPV4LL)
return 1;
#endif
/* Lower metric interfaces come first. */
c = (int)(ifp1->metric - ifp2->metric);
if (c != 0)

View File

@ -102,8 +102,9 @@ struct rt {
#define RTPREF_RESERVED (-2)
#define RTPREF_INVALID (-3) /* internal */
unsigned int rt_dflags;
#define RTDF_IFA_ROUTE 0x02 /* Address generated route */
#define RTDF_FAKE 0x04 /* Maybe us on lease reboot */
#define RTDF_IFA_ROUTE 0x01 /* Address generated route */
#define RTDF_FAKE 0x02 /* Maybe us on lease reboot */
#define RTDF_IPV4LL 0x04 /* IPv4LL route */
#define RTDF_RA 0x08 /* Router Advertisement */
#define RTDF_DHCP 0x10 /* DHCP route */
#define RTDF_STATIC 0x20 /* Configured in dhcpcd */