Import dhcpcd-8.0.0 with the following changes:

*  ARP now supports many requests
  *  Routing tables now use Red-Black Trees
  *  Script variables are no longer allocated manually
This commit is contained in:
roy 2019-07-24 09:54:49 +00:00
parent 6964063b9b
commit 141ff30ff1
50 changed files with 1888 additions and 1923 deletions

View File

@ -93,7 +93,7 @@ wpa_supplicant_stop()
err=$(wpa_cli -i"$interface" terminate 2>&1)
errn=$?
if [ $errn != 0 ]; then
syslog err "failed to start wpa_supplicant"
syslog err "failed to stop wpa_supplicant"
syslog err "$err"
fi
return $errn

View File

@ -19,6 +19,7 @@ build_resolv_conf()
interfaces=$(list_interfaces "$resolv_conf_dir")
# Build the resolv.conf
header=
if [ -n "$interfaces" ]; then
# Build the header
for x in ${interfaces}; do
@ -69,30 +70,26 @@ build_resolv_conf()
}
# Extract any ND DNS options from the RA
# For now, we ignore the lifetime of the DNS options unless they
# are absent or zero.
# In this case they are removed from consideration.
# See draft-gont-6man-slaac-dns-config-issues-01 for issues
# regarding DNS option lifetime in ND messages.
# Obey the lifetimes
eval_nd_dns()
{
eval ltime=\$nd${i}_rdnss${j}_lifetime
if [ -z "$ltime" ] || [ "$ltime" = 0 ]; then
rdnss=
else
eval rdnsstime=\$nd${i}_rdnss${j}_lifetime
[ -z "$rdnsstime" ] && return 1
ltime=$(($rdnsstime - $offset))
if [ "$ltime" -gt 0 ]; then
eval rdnss=\$nd${i}_rdnss${j}_servers
[ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
fi
eval ltime=\$nd${i}_dnssl${j}_lifetime
if [ -z "$ltime" ] || [ "$ltime" = 0 ]; then
dnssl=
else
eval dnssltime=\$nd${i}_dnssl${j}_lifetime
[ -z "$dnssltime" ] && return 1
ltime=$(($dnssltime - $offset))
if [ "$ltime" -gt 0 ]; then
eval dnssl=\$nd${i}_dnssl${j}_search
[ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
fi
[ -z "${rdnss}${dnssl}" ] && return 1
[ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
[ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
j=$(($j + 1))
return 0
}
@ -106,12 +103,16 @@ add_resolv_conf()
i=1
j=1
while true; do
eval acquired=\$nd${i}_acquired
[ -z "$acquired" ] && break
eval now=\$nd${i}_now
[ -z "$now" ] && break
offset=$(($now - $acquired))
while true; do
eval_nd_dns || break
done
i=$(($i + 1))
j=1
eval_nd_dns || break
done
[ -n "$new_rdnss" ] && \
new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss"

View File

@ -62,6 +62,7 @@ build_ntp_conf()
# Build a list of interfaces
interfaces=$(list_interfaces "$ntp_conf_dir")
header=
servers=
if [ -n "$interfaces" ]; then
# Build the header

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - ARP handler
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -36,6 +37,7 @@
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
@ -62,8 +64,9 @@
/* Assert the correct structure size for on wire */
__CTASSERT(sizeof(struct arphdr) == 8);
ssize_t
arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip)
static ssize_t
arp_request(const struct interface *ifp,
const struct in_addr *sip, const struct in_addr *tip)
{
uint8_t arp_buffer[ARP_LEN];
struct arphdr ar;
@ -74,7 +77,7 @@ arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip)
ar.ar_hrd = htons(ifp->family);
ar.ar_pro = htons(ETHERTYPE_IP);
ar.ar_hln = ifp->hwlen;
ar.ar_pln = sizeof(sip);
ar.ar_pln = sizeof(tip->s_addr);
ar.ar_op = htons(ARPOP_REQUEST);
p = arp_buffer;
@ -93,9 +96,12 @@ arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip)
APPEND(&ar, sizeof(ar));
APPEND(ifp->hwaddr, ifp->hwlen);
APPEND(&sip, sizeof(sip));
if (sip != NULL)
APPEND(&sip->s_addr, sizeof(sip->s_addr));
else
ZERO(sizeof(tip->s_addr));
ZERO(ifp->hwlen);
APPEND(&tip, sizeof(tip));
APPEND(&tip->s_addr, sizeof(tip->s_addr));
state = ARP_CSTATE(ifp);
return bpf_send(ifp, state->bpf_fd, ETHERTYPE_ARP, arp_buffer, len);
@ -105,6 +111,77 @@ eexit:
return -1;
}
static void
arp_report_conflicted(const struct arp_state *astate,
const struct arp_msg *amsg)
{
char buf[HWADDR_LEN * 3];
if (amsg == NULL) {
logerrx("%s: DAD detected %s",
astate->iface->name, inet_ntoa(astate->addr));
return;
}
logerrx("%s: hardware address %s claims %s",
astate->iface->name,
hwaddr_ntoa(amsg->sha, astate->iface->hwlen, buf, sizeof(buf)),
inet_ntoa(astate->addr));
}
static void
arp_found(struct arp_state *astate, const struct arp_msg *amsg)
{
struct interface *ifp;
struct ivp4_addr *ia;
#ifndef KERNEL_RFC5227
struct timespec now, defend;
#endif
arp_report_conflicted(astate, amsg);
ifp = astate->iface;
#pragma GCC diagnostic push /* GCC is clearly wrong about this warning. */
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
/* If we haven't added the address we're doing a probe. */
ia = ipv4_iffindaddr(ifp, &astate->addr, NULL);
#pragma GCC diagnostic pop
if (ia == NULL) {
if (astate->found_cb != NULL)
astate->found_cb(astate, amsg);
return;
}
#ifndef KERNEL_RFC5227
/* RFC 3927 Section 2.5 says a defence should
* broadcast an ARP announcement.
* Because the kernel will also unicast a reply to the
* hardware address which requested the IP address
* the other IPv4LL client will receieve two ARP
* messages.
* If another conflict happens within DEFEND_INTERVAL
* then we must drop our address and negotiate a new one. */
defend.tv_sec = astate->defend.tv_sec + DEFEND_INTERVAL;
defend.tv_nsec = astate->defend.tv_nsec;
clock_gettime(CLOCK_MONOTONIC, &now);
if (timespeccmp(&defend, &now, >))
logwarnx("%s: %d second defence failed for %s",
ifp->name, DEFEND_INTERVAL, inet_ntoa(astate->addr));
else if (arp_request(ifp, &astate->addr, &astate->addr) == -1)
logerr(__func__);
else {
logdebugx("%s: defended address %s",
ifp->name, inet_ntoa(astate->addr));
astate->defend = now;
return;
}
#endif
if (astate->defend_failed_cb != NULL)
astate->defend_failed_cb(astate);
}
static void
arp_packet(struct interface *ifp, uint8_t *data, size_t len)
{
@ -164,14 +241,15 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len)
memcpy(&arm.tha, hw_t, ar.ar_hln);
memcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln);
/* Run the conflicts */
/* Match the ARP probe to our states.
* Ignore Unicast Poll, RFC1122. */
state = ARP_CSTATE(ifp);
TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) {
if (arm.sip.s_addr != astate->addr.s_addr &&
arm.tip.s_addr != astate->addr.s_addr)
continue;
if (astate->conflicted_cb)
astate->conflicted_cb(astate, &arm);
if (IN_ARE_ADDR_EQUAL(&arm.sip, &astate->addr) ||
(IN_IS_ADDR_UNSPECIFIED(&arm.sip) &&
IN_ARE_ADDR_EQUAL(&arm.tip, &astate->addr) &&
state->bpf_flags & BPF_BCAST))
arp_found(astate, &arm);
}
}
@ -243,7 +321,7 @@ arp_read(void *arg)
}
}
int
static int
arp_open(struct interface *ifp)
{
struct iarp_state *state;
@ -265,7 +343,8 @@ arp_probed(void *arg)
{
struct arp_state *astate = arg;
astate->probed_cb(astate);
timespecclear(&astate->defend);
astate->not_found_cb(astate);
}
static void
@ -290,7 +369,7 @@ arp_probe1(void *arg)
ifp->name, inet_ntoa(astate->addr),
astate->probes ? astate->probes : PROBE_NUM, PROBE_NUM,
timespec_to_double(&tv));
if (arp_request(ifp, 0, astate->addr.s_addr) == -1)
if (arp_request(ifp, NULL, &astate->addr) == -1)
logerr(__func__);
}
@ -314,6 +393,23 @@ arp_probe(struct arp_state *astate)
}
#endif /* ARP */
static struct arp_state *
arp_find(struct interface *ifp, const struct in_addr *addr)
{
struct iarp_state *state;
struct arp_state *astate;
if ((state = ARP_STATE(ifp)) == NULL)
goto out;
TAILQ_FOREACH(astate, &state->arp_states, next) {
if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp)
return astate;
}
out:
errno = ESRCH;
return NULL;
}
static void
arp_announced(void *arg)
{
@ -342,7 +438,7 @@ arp_announce1(void *arg)
logdebugx("%s: ARP announcing %s (%d of %d)",
ifp->name, inet_ntoa(astate->addr),
astate->claims, ANNOUNCE_NUM);
if (arp_request(ifp, astate->addr.s_addr, astate->addr.s_addr) == -1)
if (arp_request(ifp, &astate->addr, &astate->addr) == -1)
logerr(__func__);
eloop_timeout_add_sec(ifp->ctx->eloop, ANNOUNCE_WAIT,
astate->claims < ANNOUNCE_NUM ? arp_announce1 : arp_announced,
@ -400,73 +496,45 @@ arp_announce(struct arp_state *astate)
arp_announce1(astate);
}
void
arp_announceaddr(struct dhcpcd_ctx *ctx, const struct in_addr *ia)
{
struct interface *ifp;
struct ipv4_addr *iaf;
struct arp_state *astate;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
iaf = ipv4_iffindaddr(ifp, ia, NULL);
#ifdef IN_IFF_NOTUSEABLE
if (iaf && !(iaf->addr_flags & IN_IFF_NOTUSEABLE))
#else
if (iaf)
#endif
break;
}
if (ifp == NULL)
return;
astate = arp_find(ifp, ia);
if (astate != NULL)
arp_announce(astate);
}
void
arp_ifannounceaddr(struct interface *ifp, const struct in_addr *ia)
{
struct arp_state *astate;
astate = arp_new(ifp, ia);
if (astate != NULL)
arp_announce(astate);
astate = arp_find(ifp, ia);
if (astate == NULL) {
astate = arp_new(ifp, ia);
if (astate == NULL)
return;
astate->announced_cb = arp_free;
}
arp_announce(astate);
}
void
arp_report_conflicted(const struct arp_state *astate,
const struct arp_msg *amsg)
arp_announceaddr(struct dhcpcd_ctx *ctx, const struct in_addr *ia)
{
struct interface *ifp, *iff = NULL;
struct ipv4_addr *iap;
if (amsg != NULL) {
char buf[HWADDR_LEN * 3];
logerrx("%s: hardware address %s claims %s",
astate->iface->name,
hwaddr_ntoa(amsg->sha, astate->iface->hwlen,
buf, sizeof(buf)),
inet_ntoa(astate->failed));
} else
logerrx("%s: DAD detected %s",
astate->iface->name, inet_ntoa(astate->failed));
}
struct arp_state *
arp_find(struct interface *ifp, const struct in_addr *addr)
{
struct iarp_state *state;
struct arp_state *astate;
if ((state = ARP_STATE(ifp)) == NULL)
goto out;
TAILQ_FOREACH(astate, &state->arp_states, next) {
if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp)
return astate;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (!ifp->active || ifp->carrier <= LINK_DOWN)
continue;
iap = ipv4_iffindaddr(ifp, ia, NULL);
if (iap == NULL)
continue;
#ifdef IN_IFF_NOTUSEABLE
if (!(iap->addr_flags & IN_IFF_NOTUSEABLE))
continue;
#endif
if (iff != NULL && iff->metric < ifp->metric)
continue;
iff = ifp;
}
out:
errno = ESRCH;
return NULL;
if (iff == NULL)
return;
arp_ifannounceaddr(iff, ia);
}
struct arp_state *
@ -532,61 +600,24 @@ arp_free(struct arp_state *astate)
arp_tryfree(ifp);
}
static void
arp_free_but1(struct interface *ifp, struct arp_state *astate)
{
struct iarp_state *state;
if ((state = ARP_STATE(ifp)) != NULL) {
struct arp_state *p, *n;
TAILQ_FOREACH_SAFE(p, &state->arp_states, next, n) {
if (p != astate)
arp_free(p);
}
}
}
void
arp_free_but(struct arp_state *astate)
arp_freeaddr(struct interface *ifp, const struct in_addr *ia)
{
struct arp_state *astate;
arp_free_but1(astate->iface, astate);
astate = arp_find(ifp, ia);
arp_free(astate);
}
void
arp_drop(struct interface *ifp)
{
arp_free_but1(ifp, NULL);
arp_close(ifp);
}
void
arp_handleifa(int cmd, struct ipv4_addr *addr)
{
struct iarp_state *state;
struct arp_state *astate, *asn;
struct arp_state *astate;
state = ARP_STATE(addr->iface);
if (state == NULL)
return;
while ((state = ARP_STATE(ifp)) != NULL &&
(astate = TAILQ_FIRST(&state->arp_states)) != NULL)
arp_free(astate);
TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, asn) {
if (astate->addr.s_addr != addr->addr.s_addr)
continue;
if (cmd == RTM_DELADDR)
arp_free(astate);
#ifdef IN_IFF_DUPLICATED
if (cmd != RTM_NEWADDR)
continue;
if (addr->addr_flags & IN_IFF_DUPLICATED) {
if (astate->conflicted_cb)
astate->conflicted_cb(astate, NULL);
} else if (!(addr->addr_flags & IN_IFF_NOTUSEABLE)) {
if (astate->probed_cb)
astate->probed_cb(astate);
}
#endif
}
/* No need to close because the last free will close */
}

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -63,15 +64,16 @@ struct arp_state {
TAILQ_ENTRY(arp_state) next;
struct interface *iface;
void (*probed_cb)(struct arp_state *);
void (*found_cb)(struct arp_state *, const struct arp_msg *);
void (*not_found_cb)(struct arp_state *);
void (*announced_cb)(struct arp_state *);
void (*conflicted_cb)(struct arp_state *, const struct arp_msg *);
void (*defend_failed_cb)(struct arp_state *);
void (*free_cb)(struct arp_state *);
struct in_addr addr;
int probes;
int claims;
struct in_addr failed;
struct timespec defend;
};
TAILQ_HEAD(arp_statehead, arp_state);
@ -87,20 +89,14 @@ struct iarp_state {
((const struct iarp_state *)(ifp)->if_data[IF_DATA_ARP])
#ifdef ARP
int arp_open(struct interface *);
ssize_t arp_request(const struct interface *, in_addr_t, in_addr_t);
void arp_probe(struct arp_state *);
void arp_report_conflicted(const struct arp_state *, const struct arp_msg *);
struct arp_state *arp_new(struct interface *, const struct in_addr *);
struct arp_state *arp_find(struct interface *, const struct in_addr *);
void arp_probe(struct arp_state *);
void arp_announce(struct arp_state *);
void arp_announceaddr(struct dhcpcd_ctx *, const struct in_addr *);
void arp_ifannounceaddr(struct interface *, const struct in_addr *);
void arp_cancel(struct arp_state *);
void arp_free(struct arp_state *);
void arp_free_but(struct arp_state *);
void arp_freeaddr(struct interface *, const struct in_addr *);
void arp_drop(struct interface *);
void arp_handleifa(int, struct ipv4_addr *);
#endif /* ARP */
#endif /* ARP_H */

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -30,6 +31,7 @@
#include <fcntl.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd: BPF arp and bootp filtering
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -84,7 +85,7 @@ size_t
bpf_frame_header_len(const struct interface *ifp)
{
switch(ifp->family) {
switch (ifp->family) {
case ARPHRD_ETHER:
return sizeof(struct ether_header);
default:
@ -92,6 +93,23 @@ bpf_frame_header_len(const struct interface *ifp)
}
}
static const uint8_t etherbroadcastaddr[] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
int
bpf_frame_bcast(const struct interface *ifp, const char *frame)
{
switch (ifp->family) {
case ARPHRD_ETHER:
return memcmp(frame +
offsetof(struct ether_header, ether_dhost),
etherbroadcastaddr, sizeof(etherbroadcastaddr));
default:
return -1;
}
}
#ifndef __linux__
/* Linux is a special snowflake for opening, attaching and reading BPF.
* See if-linux.c for the Linux specific BPF functions. */
@ -227,8 +245,12 @@ bpf_read(struct interface *ifp, int fd, void *data, size_t len,
if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
state->buffer_len)
goto next; /* Packet beyond buffer, drop. */
payload = state->buffer + state->buffer_pos +
packet.bh_hdrlen + fl;
payload = state->buffer + state->buffer_pos + packet.bh_hdrlen;
if (bpf_frame_bcast(ifp, payload) == 0)
*flags |= BPF_BCAST;
else
*flags &= ~BPF_BCAST;
payload += fl;
bytes = (ssize_t)packet.bh_caplen - fl;
if ((size_t)bytes > len)
bytes = (ssize_t)len;

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd: BPF arp and bootp filtering
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -31,11 +32,13 @@
#define BPF_READING (1U << 0)
#define BPF_EOF (1U << 1)
#define BPF_PARTIALCSUM (1U << 2)
#define BPF_BCAST (1U << 3)
#include "dhcpcd.h"
extern const char *bpf_name;
size_t bpf_frame_header_len(const struct interface *);
int bpf_frame_bcast(const struct interface *, const char *frame);
int bpf_open(struct interface *, int (*)(struct interface *, int));
int bpf_close(struct interface *, int);
int bpf_attach(int, void *, unsigned int);

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -56,54 +57,6 @@
/* Most route(4) messages are less than 256 bytes. */
#define IOVEC_BUFSIZ 256
ssize_t
setvar(char **e, const char *prefix, const char *var, const char *value)
{
size_t len = strlen(var) + strlen(value) + 3;
if (prefix)
len += strlen(prefix) + 1;
if ((*e = malloc(len)) == NULL) {
logerr(__func__);
return -1;
}
if (prefix)
snprintf(*e, len, "%s_%s=%s", prefix, var, value);
else
snprintf(*e, len, "%s=%s", var, value);
return (ssize_t)len;
}
ssize_t
setvard(char **e, const char *prefix, const char *var, size_t value)
{
char buffer[32];
snprintf(buffer, sizeof(buffer), "%zu", value);
return setvar(e, prefix, var, buffer);
}
ssize_t
addvar(char ***e, const char *prefix, const char *var, const char *value)
{
ssize_t len;
len = setvar(*e, prefix, var, value);
if (len != -1)
(*e)++;
return (ssize_t)len;
}
ssize_t
addvard(char ***e, const char *prefix, const char *var, size_t value)
{
char buffer[32];
snprintf(buffer, sizeof(buffer), "%zu", value);
return addvar(e, prefix, var, buffer);
}
const char *
hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen)
{

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -30,12 +31,9 @@
#include <sys/param.h>
#include <sys/time.h>
#include <stdint.h>
#include <stdio.h>
#include "config.h"
#include "defs.h"
#include "dhcpcd.h"
#ifndef HOSTNAME_MAX_LEN
#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
#endif
@ -130,6 +128,29 @@
# endif
#endif
/* Needed for rbtree(3) compat */
#ifndef __RCSID
#define __RCSID(a)
#endif
#ifndef __predict_false
# if __GNUC__ > 2
# define __predict_true(exp) __builtin_expect((exp) != 0, 1)
# define __predict_false(exp) __builtin_expect((exp) != 0, 0)
#else
# define __predict_true(exp) (exp)
# define __predict_false(exp) (exp)
# endif
#endif
#ifndef __BEGIN_DECLS
# if defined(__cplusplus)
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS };
# else /* __BEGIN_DECLS */
# define __BEGIN_DECLS
# define __END_DECLS
# endif /* __BEGIN_DECLS */
#endif /* __BEGIN_DECLS */
#ifndef __fallthrough
# if __GNUC__ >= 7
# define __fallthrough __attribute__((fallthrough))
@ -173,11 +194,6 @@ void get_line_free(void);
extern int clock_monotonic;
int get_monotonic(struct timespec *);
ssize_t setvar(char **, const char *, const char *, const char *);
ssize_t setvard(char **, const char *, const char *, size_t);
ssize_t addvar(char ***, const char *, const char *, const char *);
ssize_t addvard(char ***, const char *, const char *, size_t);
const char *hwaddr_ntoa(const void *, size_t, char *, size_t);
size_t hwaddr_aton(uint8_t *, const char *);
size_t read_hwaddr_aton(uint8_t **, const char *);

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -28,7 +29,7 @@
#define CONFIG_H
#define PACKAGE "dhcpcd"
#define VERSION "7.2.3"
#define VERSION "8.0.0"
#ifndef CONFIG
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -36,8 +37,6 @@
#include <string.h>
#include <unistd.h>
#include <arpa/nameser.h> /* after normal includes for sunos */
#include "config.h"
#include "common.h"
@ -46,13 +45,7 @@
#include "if.h"
#include "ipv6.h"
#include "logerr.h"
/* Support very old arpa/nameser.h as found in OpenBSD */
#ifndef NS_MAXDNAME
#define NS_MAXCDNAME MAXCDNAME
#define NS_MAXDNAME MAXDNAME
#define NS_MAXLABEL MAXLABEL
#endif
#include "script.h"
const char *
dhcp_get_hostname(char *buf, size_t buf_len, const struct if_options *ifo)
@ -624,41 +617,9 @@ dhcp_optlen(const struct dhcp_opt *opt, size_t dl)
return (ssize_t)sz;
}
/* It's possible for DHCPv4 to contain an IPv6 address */
static ssize_t
ipv6_printaddr(char *s, size_t sl, const uint8_t *d, const char *ifname)
{
char buf[INET6_ADDRSTRLEN];
const char *p;
size_t l;
p = inet_ntop(AF_INET6, d, buf, sizeof(buf));
if (p == NULL)
return -1;
l = strlen(p);
if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80)
l += 1 + strlen(ifname);
if (s == NULL)
return (ssize_t)l;
if (sl < l) {
errno = ENOMEM;
return -1;
}
s += strlcpy(s, p, sl);
if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80) {
*s++ = '%';
s += strlcpy(s, ifname, sl);
}
*s = '\0';
return (ssize_t)l;
}
static ssize_t
print_option(char *s, size_t len, const struct dhcp_opt *opt,
print_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt,
int vname,
const uint8_t *data, size_t dl, const char *ifname)
{
const uint8_t *e, *t;
@ -667,52 +628,58 @@ print_option(char *s, size_t len, const struct dhcp_opt *opt,
uint32_t u32;
int32_t s32;
struct in_addr addr;
ssize_t bytes = 0, sl;
ssize_t sl;
size_t l;
#ifdef INET
char *tmp;
#endif
/* Ensure a valid length */
dl = (size_t)dhcp_optlen(opt, dl);
if ((ssize_t)dl == -1)
return 0;
if (fprintf(fp, "%s", prefix) == -1)
return -1;
if (vname) {
if (fprintf(fp, "_%s", opt->var) == -1)
return -1;
}
if (fputc('=', fp) == EOF)
return -1;
if (dl == 0)
return 1;
if (opt->type & OT_RFC1035) {
sl = decode_rfc1035(s, len, data, dl);
char domain[NS_MAXDNAME];
sl = decode_rfc1035(domain, sizeof(domain), data, dl);
if (sl == 0 || sl == -1)
return sl;
if (s != NULL) {
if (valid_domainname(s, opt->type) == -1)
return -1;
}
return sl;
if (valid_domainname(domain, opt->type) == -1)
return -1;
return efprintf(fp, "%s", domain);
}
#ifdef INET
if (opt->type & OT_RFC3361) {
if ((tmp = decode_rfc3361(data, dl)) == NULL)
return -1;
l = strlen(tmp);
sl = print_string(s, len, opt->type, (uint8_t *)tmp, l);
free(tmp);
return sl;
}
if (opt->type & OT_RFC3361)
return print_rfc3361(fp, data, dl);
if (opt->type & OT_RFC3442)
return decode_rfc3442(s, len, data, dl);
return print_rfc3442(fp, data, dl);
#endif
if (opt->type & OT_STRING)
return print_string(s, len, opt->type, data, dl);
if (opt->type & OT_STRING) {
char buf[1024];
if (opt->type & OT_FLAG) {
if (s) {
*s++ = '1';
*s = '\0';
}
return 1;
if (print_string(buf, sizeof(buf), opt->type, data, dl) == -1)
return -1;
return efprintf(fp, "%s", buf);
}
if (opt->type & OT_FLAG)
return efprintf(fp, "1");
if (opt->type & OT_BITFLAG) {
/* bitflags are a string, MSB first, such as ABCDEFGH
* where A is 10000000, B is 01000000, etc. */
bytes = 0;
for (l = 0, sl = sizeof(opt->bitflags) - 1;
l < sizeof(opt->bitflags);
l++, sl--)
@ -722,109 +689,80 @@ print_option(char *s, size_t len, const struct dhcp_opt *opt,
opt->bitflags[l] != '0' &&
*data & (1 << sl))
{
if (s)
*s++ = opt->bitflags[l];
bytes++;
if (fputc(opt->bitflags[l], fp) == EOF)
return -1;
}
}
if (s)
*s = '\0';
return bytes;
}
if (!s) {
if (opt->type & OT_UINT8)
l = 3;
else if (opt->type & OT_INT8)
l = 4;
else if (opt->type & OT_UINT16) {
l = 5;
dl /= 2;
} else if (opt->type & OT_INT16) {
l = 6;
dl /= 2;
} else if (opt->type & OT_UINT32) {
l = 10;
dl /= 4;
} else if (opt->type & OT_INT32) {
l = 11;
dl /= 4;
} else if (opt->type & OT_ADDRIPV4) {
l = 16;
dl /= 4;
} else if (opt->type & OT_ADDRIPV6) {
e = data + dl;
l = 0;
while (data < e) {
if (l)
l++; /* space */
sl = ipv6_printaddr(NULL, 0, data, ifname);
if (sl == -1)
return l == 0 ? -1 : (ssize_t)l;
l += (size_t)sl;
data += 16;
}
return (ssize_t)l;
} else {
errno = EINVAL;
if (fputc('\0', fp) == EOF)
return -1;
}
return (ssize_t)(l * dl);
return 1;
}
t = data;
e = data + dl;
while (data < e) {
if (data != t) {
*s++ = ' ';
bytes++;
len--;
if (fputc(' ', fp) == EOF)
return -1;
}
if (opt->type & OT_UINT8) {
sl = snprintf(s, len, "%u", *data);
if (fprintf(fp, "%u", *data) == -1)
return -1;
data++;
} else if (opt->type & OT_INT8) {
sl = snprintf(s, len, "%d", *data);
if (fprintf(fp, "%d", *data) == -1)
return -1;
data++;
} else if (opt->type & OT_UINT16) {
memcpy(&u16, data, sizeof(u16));
u16 = ntohs(u16);
sl = snprintf(s, len, "%u", u16);
if (fprintf(fp, "%u", u16) == -1)
return -1;
data += sizeof(u16);
} else if (opt->type & OT_INT16) {
memcpy(&u16, data, sizeof(u16));
s16 = (int16_t)ntohs(u16);
sl = snprintf(s, len, "%d", s16);
if (fprintf(fp, "%d", s16) == -1)
return -1;
data += sizeof(u16);
} else if (opt->type & OT_UINT32) {
memcpy(&u32, data, sizeof(u32));
u32 = ntohl(u32);
sl = snprintf(s, len, "%u", u32);
if (fprintf(fp, "%u", u32) == -1)
return -1;
data += sizeof(u32);
} else if (opt->type & OT_INT32) {
memcpy(&u32, data, sizeof(u32));
s32 = (int32_t)ntohl(u32);
sl = snprintf(s, len, "%d", s32);
if (fprintf(fp, "%d", s32) == -1)
return -1;
data += sizeof(u32);
} else if (opt->type & OT_ADDRIPV4) {
memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
sl = snprintf(s, len, "%s", inet_ntoa(addr));
if (fprintf(fp, "%s", inet_ntoa(addr)) == -1)
return -1;
data += sizeof(addr.s_addr);
} else if (opt->type & OT_ADDRIPV6) {
sl = ipv6_printaddr(s, len, data, ifname);
char buf[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, data, buf, sizeof(buf)) == NULL)
return -1;
if (fprintf(fp, "%s", buf) == -1)
return -1;
if (data[0] == 0xfe && (data[1] & 0xc0) == 0x80) {
if (fprintf(fp,"%%%s", ifname) == -1)
return -1;
}
data += 16;
} else {
errno = EINVAL;
return -1;
}
if (sl == -1)
return bytes == 0 ? -1 : bytes;
len -= (size_t)sl;
bytes += sl;
s += sl;
}
return bytes;
if (fputc('\0', fp) == EOF)
return -1;
return 1;
}
int
@ -859,61 +797,15 @@ dhcp_set_leasefile(char *leasefile, size_t len, int family,
ifp->name, ssid);
}
static size_t
dhcp_envoption1(char **env, const char *prefix,
const struct dhcp_opt *opt, int vname, const uint8_t *od, size_t ol,
const char *ifname)
{
ssize_t len;
size_t e;
char *v, *val;
int r;
/* Ensure a valid length */
ol = (size_t)dhcp_optlen(opt, ol);
if ((ssize_t)ol == -1)
return 0;
len = print_option(NULL, 0, opt, od, ol, ifname);
if (len < 0)
return 0;
if (vname)
e = strlen(opt->var) + 1;
else
e = 0;
if (prefix)
e += strlen(prefix);
e += (size_t)len + 2;
if (env == NULL)
return e;
v = val = *env = malloc(e);
if (v == NULL)
return 0;
if (vname)
r = snprintf(val, e, "%s_%s=", prefix, opt->var);
else
r = snprintf(val, e, "%s=", prefix);
if (r != -1 && len != 0) {
v += r;
if (print_option(v, (size_t)len + 1, opt, od, ol, ifname) == -1)
r = -1;
}
if (r == -1) {
free(val);
return 0;
}
return e;
}
size_t
dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
void
dhcp_envoption(struct dhcpcd_ctx *ctx, FILE *fp, const char *prefix,
const char *ifname, struct dhcp_opt *opt,
const uint8_t *(*dgetopt)(struct dhcpcd_ctx *,
size_t *, unsigned int *, size_t *,
const uint8_t *, size_t, struct dhcp_opt **),
const uint8_t *od, size_t ol)
{
size_t e, i, n, eos, eol;
size_t i, eos, eol;
ssize_t eo;
unsigned int eoc;
const uint8_t *eod;
@ -923,52 +815,36 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
/* If no embedded or encapsulated options, it's easy */
if (opt->embopts_len == 0 && opt->encopts_len == 0) {
if (!(opt->type & OT_RESERVED)) {
if (dhcp_envoption1(env == NULL ? NULL : &env[0],
prefix, opt, 1, od, ol, ifname))
return 1;
else
logerr("%s: %s %d",
ifname, __func__, opt->option);
}
return 0;
if (opt->type & OT_RESERVED)
return;
if (print_option(fp, prefix, opt, 1, od, ol, ifname) == -1)
logerr("%s: %s %d", ifname, __func__, opt->option);
return;
}
/* Create a new prefix based on the option */
if (env) {
if (opt->type & OT_INDEX) {
if (opt->index > 999) {
errno = ENOBUFS;
logerr(__func__);
return 0;
}
}
e = strlen(prefix) + strlen(opt->var) + 2 +
(opt->type & OT_INDEX ? 3 : 0);
pfx = malloc(e);
if (pfx == NULL) {
logerr(__func__);
return 0;
}
if (opt->type & OT_INDEX)
snprintf(pfx, e, "%s_%s%d", prefix,
opt->var, ++opt->index);
else
snprintf(pfx, e, "%s_%s", prefix, opt->var);
} else
pfx = NULL;
if (opt->type & OT_INDEX) {
if (asprintf(&pfx, "%s_%s%d",
prefix, opt->var, ++opt->index) == -1)
pfx = NULL;
} else {
if (asprintf(&pfx, "%s_%s", prefix, opt->var) == -1)
pfx = NULL;
}
if (pfx == NULL) {
logerr(__func__);
return;
}
/* Embedded options are always processed first as that
* is a fixed layout */
n = 0;
for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) {
eo = dhcp_optlen(eopt, ol);
if (eo == -1) {
if (env == NULL)
logerrx("%s: %s %d.%d/%zu: "
"malformed embedded option",
ifname, __func__, opt->option,
eopt->option, i);
logerrx("%s: %s %d.%d/%zu: "
"malformed embedded option",
ifname, __func__, opt->option,
eopt->option, i);
goto out;
}
if (eo == 0) {
@ -977,9 +853,9 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
* This may not be an error as some options like
* DHCP FQDN in RFC4702 have a string as the last
* option which is optional. */
if (env == NULL &&
(ol != 0 || !(eopt->type & OT_OPTIONAL)))
logerrx("%s: %s %d.%d/%zu: missing embedded option",
if (ol != 0 || !(eopt->type & OT_OPTIONAL))
logerrx("%s: %s %d.%d/%zu: "
"missing embedded option",
ifname, __func__, opt->option,
eopt->option, i);
goto out;
@ -989,10 +865,8 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
* This avoids new_fqdn_fqdn which would be silly. */
if (!(eopt->type & OT_RESERVED)) {
ov = strcmp(opt->var, eopt->var);
if (dhcp_envoption1(env == NULL ? NULL : &env[n],
pfx, eopt, ov, od, (size_t)eo, ifname))
n++;
else if (env == NULL)
if (print_option(fp, pfx, eopt, ov, od, (size_t)eo,
ifname) == -1)
logerr("%s: %s %d.%d/%zu",
ifname, __func__,
opt->option, eopt->option, i);
@ -1023,19 +897,16 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
i < opt->encopts_len;
i++, eopt++)
{
if (eopt->option == eoc) {
if (eopt->type & OT_OPTION) {
if (oopt == NULL)
/* Report error? */
continue;
}
n += dhcp_envoption(ctx,
env == NULL ? NULL : &env[n], pfx,
ifname,
eopt->type & OT_OPTION ? oopt:eopt,
dgetopt, eod, eol);
break;
if (eopt->option != eoc)
continue;
if (eopt->type & OT_OPTION) {
if (oopt == NULL)
/* Report error? */
continue;
}
dhcp_envoption(ctx, fp, pfx, ifname,
eopt->type & OT_OPTION ? oopt:eopt,
dgetopt, eod, eol);
}
od += eos + eol;
ol -= eos + eol;
@ -1043,11 +914,7 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
}
out:
if (env)
free(pfx);
/* Return number of options found */
return n;
free(pfx);
}
void
@ -1070,7 +937,7 @@ dhcp_read_lease_fd(int fd, void **lease)
size_t sz;
void *buf;
ssize_t len;
if (fstat(fd, &st) != 0)
goto out;
if (!S_ISREG(st.st_mode)) {

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -33,9 +34,18 @@
#include <stdint.h>
#include <arpa/nameser.h> /* after normal includes for sunos */
#include "common.h"
#include "dhcpcd.h"
/* Support very old arpa/nameser.h as found in OpenBSD */
#ifndef NS_MAXDNAME
#define NS_MAXCDNAME MAXCDNAME
#define NS_MAXDNAME MAXDNAME
#define NS_MAXLABEL MAXLABEL
#endif
/* Max MTU - defines dhcp option length */
#define IP_UDP_SIZE 28
#define MTU_MAX 1500 - IP_UDP_SIZE
@ -111,8 +121,8 @@ ssize_t decode_rfc1035(char *, size_t, const uint8_t *, size_t);
ssize_t print_string(char *, size_t, int, const uint8_t *, size_t);
int dhcp_set_leasefile(char *, size_t, int, const struct interface *);
size_t dhcp_envoption(struct dhcpcd_ctx *,
char **, const char *, const char *, struct dhcp_opt *,
void dhcp_envoption(struct dhcpcd_ctx *,
FILE *, const char *, const char *, struct dhcp_opt *,
const uint8_t *(*dgetopt)(struct dhcpcd_ctx *,
size_t *, unsigned int *, size_t *,
const uint8_t *, size_t, struct dhcp_opt **),

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -47,6 +48,7 @@
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -124,8 +126,9 @@ static const char * const dhcp_params[] = {
};
static int dhcp_openbpf(struct interface *);
static void dhcp_start1(void *);
#ifdef ARP
static void dhcp_arp_conflicted(struct arp_state *, const struct arp_msg *);
static void dhcp_arp_found(struct arp_state *, const struct arp_msg *);
#endif
static void dhcp_handledhcp(struct interface *, struct bootp *, size_t,
const struct in_addr *);
@ -339,23 +342,25 @@ get_option_uint8(struct dhcpcd_ctx *ctx,
}
ssize_t
decode_rfc3442(char *out, size_t len, const uint8_t *p, size_t pl)
print_rfc3442(FILE *fp, const uint8_t *data, size_t data_len)
{
const uint8_t *e;
size_t bytes = 0, ocets;
int b;
const uint8_t *p = data, *e;
size_t ocets;
uint8_t cidr;
struct in_addr addr;
char *o = out;
/* Minimum is 5 -first is CIDR and a router length of 4 */
if (pl < 5) {
if (data_len < 5) {
errno = EINVAL;
return -1;
}
e = p + pl;
e = p + data_len;
while (p < e) {
if (p != data) {
if (fputc(' ', fp) == EOF)
return -1;
}
cidr = *p++;
if (cidr > 32) {
errno = EINVAL;
@ -366,45 +371,29 @@ decode_rfc3442(char *out, size_t len, const uint8_t *p, size_t pl)
errno = ERANGE;
return -1;
}
if (!out) {
p += 4 + ocets;
bytes += ((4 * 4) * 2) + 4;
continue;
}
if ((((4 * 4) * 2) + 4) > len) {
errno = ENOBUFS;
return -1;
}
if (o != out) {
*o++ = ' ';
len--;
}
/* If we have ocets then we have a destination and netmask */
addr.s_addr = 0;
if (ocets > 0) {
addr.s_addr = 0;
memcpy(&addr.s_addr, p, ocets);
b = snprintf(o, len, "%s/%d", inet_ntoa(addr), cidr);
p += ocets;
} else
b = snprintf(o, len, "0.0.0.0/0");
o += b;
len -= (size_t)b;
}
if (fprintf(fp, "%s/%d", inet_ntoa(addr), cidr) == -1)
return -1;
/* Finally, snag the router */
memcpy(&addr.s_addr, p, 4);
p += 4;
b = snprintf(o, len, " %s", inet_ntoa(addr));
o += b;
len -= (size_t)b;
if (fprintf(fp, " %s", inet_ntoa(addr)) == -1)
return -1;
}
if (out)
return o - out;
return (ssize_t)bytes;
if (fputc('\0', fp) == EOF)
return -1;
return 1;
}
static int
decode_rfc3442_rt(struct rt_head *routes, struct interface *ifp,
decode_rfc3442_rt(rb_tree_t *routes, struct interface *ifp,
const uint8_t *data, size_t dl, const struct bootp *bootp)
{
const uint8_t *p = data;
@ -467,22 +456,18 @@ decode_rfc3442_rt(struct rt_head *routes, struct interface *ifp,
sa_in_init(&rt->rt_dest, &dest);
sa_in_init(&rt->rt_netmask, &netmask);
sa_in_init(&rt->rt_gateway, &gateway);
TAILQ_INSERT_TAIL(routes, rt, rt_next);
n++;
if (rt_proto_add(routes, rt))
n = 1;
}
return n;
}
char *
decode_rfc3361(const uint8_t *data, size_t dl)
ssize_t
print_rfc3361(FILE *fp, const uint8_t *data, size_t dl)
{
uint8_t enc;
size_t l;
ssize_t r;
char *sip = NULL;
char sip[NS_MAXDNAME];
struct in_addr addr;
char *p;
if (dl < 2) {
errno = EINVAL;
@ -493,13 +478,10 @@ decode_rfc3361(const uint8_t *data, size_t dl)
dl--;
switch (enc) {
case 0:
if ((r = decode_rfc1035(NULL, 0, data, dl)) > 0) {
l = (size_t)r + 1;
sip = malloc(l);
if (sip == NULL)
return 0;
decode_rfc1035(sip, l, data, dl);
}
if (decode_rfc1035(sip, sizeof(sip), data, dl) == -1)
return -1;
if (efprintf(fp, "%s", sip) == -1)
return -1;
break;
case 1:
if (dl == 0 || dl % 4 != 0) {
@ -507,25 +489,27 @@ decode_rfc3361(const uint8_t *data, size_t dl)
break;
}
addr.s_addr = INADDR_BROADCAST;
l = ((dl / sizeof(addr.s_addr)) * ((4 * 4) + 1)) + 1;
sip = p = malloc(l);
if (sip == NULL)
return 0;
while (dl != 0) {
for (;
dl != 0;
data += sizeof(addr.s_addr), dl -= sizeof(addr.s_addr))
{
memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
data += sizeof(addr.s_addr);
p += snprintf(p, l - (size_t)(p - sip),
"%s ", inet_ntoa(addr));
dl -= sizeof(addr.s_addr);
if (fprintf(fp, "%s", inet_ntoa(addr)) == -1)
return -1;
if (dl != 0) {
if (fputc(' ', fp) == EOF)
return -1;
}
}
*--p = '\0';
if (fputc('\0', fp) == EOF)
return -1;
break;
default:
errno = EINVAL;
return 0;
}
return sip;
return 1;
}
static char *
@ -581,7 +565,7 @@ route_netmask(uint32_t ip_in)
* If we have a CSR then we only use that.
* Otherwise we add static routes and then routers. */
static int
get_option_routes(struct rt_head *routes, struct interface *ifp,
get_option_routes(rb_tree_t *routes, struct interface *ifp,
const struct bootp *bootp, size_t bootp_len)
{
struct if_options *ifo = ifp->options;
@ -656,9 +640,8 @@ get_option_routes(struct rt_head *routes, struct interface *ifp,
sa_in_init(&rt->rt_dest, &dest);
sa_in_init(&rt->rt_netmask, &netmask);
sa_in_init(&rt->rt_gateway, &gateway);
TAILQ_INSERT_TAIL(routes, rt, rt_next);
n++;
if (rt_proto_add(routes, rt))
n++;
}
}
@ -667,7 +650,7 @@ get_option_routes(struct rt_head *routes, struct interface *ifp,
p = get_option(ifp->ctx, bootp, bootp_len, DHO_ROUTER, &len);
else
p = NULL;
if (p) {
if (p && len % 4 == 0) {
e = p + len;
dest.s_addr = INADDR_ANY;
netmask.s_addr = INADDR_ANY;
@ -679,8 +662,8 @@ get_option_routes(struct rt_head *routes, struct interface *ifp,
sa_in_init(&rt->rt_dest, &dest);
sa_in_init(&rt->rt_netmask, &netmask);
sa_in_init(&rt->rt_gateway, &gateway);
TAILQ_INSERT_TAIL(routes, rt, rt_next);
n++;
if (rt_proto_add(routes, rt))
n++;
}
}
@ -707,7 +690,7 @@ dhcp_get_mtu(const struct interface *ifp)
/* Grab our routers from the DHCP message and apply any MTU value
* the message contains */
int
dhcp_get_routes(struct rt_head *routes, struct interface *ifp)
dhcp_get_routes(rb_tree_t *routes, struct interface *ifp)
{
const struct dhcp_state *state;
@ -1299,9 +1282,8 @@ dhcp_getoption(struct dhcpcd_ctx *ctx,
}
ssize_t
dhcp_env(char **env, const char *prefix,
const struct bootp *bootp, size_t bootp_len,
const struct interface *ifp)
dhcp_env(FILE *fenv, const char *prefix, const struct interface *ifp,
const struct bootp *bootp, size_t bootp_len)
{
const struct if_options *ifo;
const uint8_t *p;
@ -1309,109 +1291,73 @@ dhcp_env(char **env, const char *prefix,
struct in_addr net;
struct in_addr brd;
struct dhcp_opt *opt, *vo;
size_t e, i, pl;
char **ep;
char cidr[4], safe[(BOOTP_FILE_LEN * 4) + 1];
size_t i, pl;
char safe[(BOOTP_FILE_LEN * 4) + 1];
uint8_t overl = 0;
uint32_t en;
e = 0;
ifo = ifp->options;
if (get_option_uint8(ifp->ctx, &overl, bootp, bootp_len,
DHO_OPTSOVERLOADED) == -1)
overl = 0;
if (env == NULL) {
if (bootp->yiaddr || bootp->ciaddr)
e += 5;
if (*bootp->file && !(overl & 1))
e++;
if (*bootp->sname && !(overl & 2))
e++;
for (i = 0, opt = ifp->ctx->dhcp_opts;
i < ifp->ctx->dhcp_opts_len;
i++, opt++)
{
if (has_option_mask(ifo->nomask, opt->option))
continue;
if (dhcp_getoverride(ifo, opt->option))
continue;
p = get_option(ifp->ctx, bootp, bootp_len,
opt->option, &pl);
if (!p)
continue;
e += dhcp_envoption(ifp->ctx, NULL, NULL, ifp->name,
opt, dhcp_getoption, p, pl);
}
for (i = 0, opt = ifo->dhcp_override;
i < ifo->dhcp_override_len;
i++, opt++)
{
if (has_option_mask(ifo->nomask, opt->option))
continue;
p = get_option(ifp->ctx, bootp, bootp_len,
opt->option, &pl);
if (!p)
continue;
e += dhcp_envoption(ifp->ctx, NULL, NULL, ifp->name,
opt, dhcp_getoption, p, pl);
}
return (ssize_t)e;
}
ep = env;
if (bootp->yiaddr || bootp->ciaddr) {
/* Set some useful variables that we derive from the DHCP
* message but are not necessarily in the options */
addr.s_addr = bootp->yiaddr ? bootp->yiaddr : bootp->ciaddr;
addvar(&ep, prefix, "ip_address", inet_ntoa(addr));
if (efprintf(fenv, "%s_ip_address=%s",
prefix, inet_ntoa(addr)) == -1)
return -1;
if (get_option_addr(ifp->ctx, &net,
bootp, bootp_len, DHO_SUBNETMASK) == -1)
{
bootp, bootp_len, DHO_SUBNETMASK) == -1) {
net.s_addr = ipv4_getnetmask(addr.s_addr);
addvar(&ep, prefix,
"subnet_mask", inet_ntoa(net));
if (efprintf(fenv, "%s_subnet_mask=%s",
prefix, inet_ntoa(net)) == -1)
return -1;
}
snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net));
addvar(&ep, prefix, "subnet_cidr", cidr);
if (efprintf(fenv, "%s_subnet_cidr=%d",
prefix, inet_ntocidr(net))== -1)
return -1;
if (get_option_addr(ifp->ctx, &brd,
bootp, bootp_len, DHO_BROADCAST) == -1)
{
brd.s_addr = addr.s_addr | ~net.s_addr;
addvar(&ep, prefix,
"broadcast_address", inet_ntoa(brd));
if (efprintf(fenv, "%s_broadcast_address=%s",
prefix, inet_ntoa(brd)) == -1)
return -1;
}
addr.s_addr = bootp->yiaddr & net.s_addr;
addvar(&ep, prefix,
"network_number", inet_ntoa(addr));
if (efprintf(fenv, "%s_network_number=%s",
prefix, inet_ntoa(addr)) == -1)
return -1;
}
if (*bootp->file && !(overl & 1)) {
print_string(safe, sizeof(safe), OT_STRING,
bootp->file, sizeof(bootp->file));
addvar(&ep, prefix, "filename", safe);
if (efprintf(fenv, "%s_filename=%s", prefix, safe) == -1)
return -1;
}
if (*bootp->sname && !(overl & 2)) {
print_string(safe, sizeof(safe), OT_STRING | OT_DOMAIN,
bootp->sname, sizeof(bootp->sname));
addvar(&ep, prefix, "server_name", safe);
if (efprintf(fenv, "%s_server_name=%s", prefix, safe) == -1)
return -1;
}
/* Zero our indexes */
if (env) {
for (i = 0, opt = ifp->ctx->dhcp_opts;
i < ifp->ctx->dhcp_opts_len;
i++, opt++)
dhcp_zero_index(opt);
for (i = 0, opt = ifp->options->dhcp_override;
i < ifp->options->dhcp_override_len;
i++, opt++)
dhcp_zero_index(opt);
for (i = 0, opt = ifp->ctx->vivso;
i < ifp->ctx->vivso_len;
i++, opt++)
dhcp_zero_index(opt);
}
for (i = 0, opt = ifp->ctx->dhcp_opts;
i < ifp->ctx->dhcp_opts_len;
i++, opt++)
dhcp_zero_index(opt);
for (i = 0, opt = ifp->options->dhcp_override;
i < ifp->options->dhcp_override_len;
i++, opt++)
dhcp_zero_index(opt);
for (i = 0, opt = ifp->ctx->vivso;
i < ifp->ctx->vivso_len;
i++, opt++)
dhcp_zero_index(opt);
for (i = 0, opt = ifp->ctx->dhcp_opts;
i < ifp->ctx->dhcp_opts_len;
@ -1424,7 +1370,7 @@ dhcp_env(char **env, const char *prefix,
p = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl);
if (p == NULL)
continue;
ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name,
dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name,
opt, dhcp_getoption, p, pl);
if (opt->option != DHO_VIVSO || pl <= (int)sizeof(uint32_t))
@ -1437,7 +1383,7 @@ dhcp_env(char **env, const char *prefix,
/* Skip over en + total size */
p += sizeof(en) + 1;
pl -= sizeof(en) + 1;
ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name,
dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name,
vo, dhcp_getoption, p, pl);
}
@ -1450,11 +1396,11 @@ dhcp_env(char **env, const char *prefix,
p = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl);
if (p == NULL)
continue;
ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name,
dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name,
opt, dhcp_getoption, p, pl);
}
return ep - env;
return 1;
}
static void
@ -1497,7 +1443,7 @@ get_lease(struct interface *ifp,
}
if (get_option_uint32(ctx, &lease->leasetime,
bootp, len, DHO_LEASETIME) != 0)
lease->leasetime = ~0U; /* Default to infinite lease */
lease->leasetime = DHCP_INFINITE_LIFETIME;
if (get_option_uint32(ctx, &lease->renewaltime,
bootp, len, DHO_RENEWALTIME) != 0)
lease->renewaltime = 0;
@ -1923,35 +1869,6 @@ dhcp_request(void *arg)
send_request(ifp);
}
static int
dhcp_leaseextend(struct interface *ifp)
{
#ifdef ARP
if (ifp->options->options & DHCPCD_ARP) {
const struct dhcp_state *state;
struct arp_state *astate;
state = D_CSTATE(ifp);
if ((astate = arp_new(ifp, &state->lease.addr)) == NULL)
return -1;
astate->conflicted_cb = dhcp_arp_conflicted;
#ifndef KERNEL_RFC5227
if (arp_open(ifp) == -1)
return -1;
#endif
logwarnx("%s: extending lease until DaD failure or DHCP",
ifp->name);
return 0;
}
#endif
logwarnx("%s: extending lease", ifp->name);
return 0;
}
static void
dhcp_expire1(struct interface *ifp)
{
@ -1970,12 +1887,12 @@ dhcp_expire(void *arg)
{
struct interface *ifp = arg;
logerrx("%s: DHCP lease expired", ifp->name);
if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {
if (dhcp_leaseextend(ifp) == 0)
return;
logerr(__func__);
logwarnx("%s: DHCP lease expired, extending lease", ifp->name);
return;
}
logerrx("%s: DHCP lease expired", ifp->name);
dhcp_expire1(ifp);
}
@ -2041,9 +1958,78 @@ dhcp_rebind(void *arg)
send_rebind(ifp);
}
static void
dhcp_finish_dad(struct interface *ifp, struct in_addr *ia)
{
struct dhcp_state *state = D_STATE(ifp);
if (state->state != DHS_PROBE)
return;
if (state->offer == NULL || state->offer->yiaddr != ia->s_addr)
return;
logdebugx("%s: DAD completed for %s", ifp->name, inet_ntoa(*ia));
if (!(ifp->options->options & DHCPCD_INFORM))
dhcp_bind(ifp);
#ifndef IN_IFF_DUPLICATED
else {
struct bootp *bootp;
size_t len;
bootp = state->new;
len = state->new_len;
state->new = state->offer;
state->new_len = state->offer_len;
get_lease(ifp, &state->lease, state->new, state->new_len);
ipv4_applyaddr(ifp);
state->new = bootp;
state->new_len = len;
}
#endif
/* If we forked, stop here. */
if (ifp->ctx->options & DHCPCD_FORKED)
return;
#ifdef IPV4LL
/* Stop IPv4LL now we have a working DHCP address */
ipv4ll_drop(ifp);
#endif
if (ifp->options->options & DHCPCD_INFORM)
dhcp_inform(ifp);
}
static void
dhcp_addr_duplicated(struct interface *ifp, struct in_addr *ia)
{
struct dhcp_state *state = D_STATE(ifp);
#ifdef IN_IFF_DUPLICATED
struct ipv4_addr *iap;
#endif
if ((state->offer == NULL || state->offer->yiaddr != ia->s_addr) &&
!IN_ARE_ADDR_EQUAL(ia, &state->lease.addr))
return;
/* RFC 2131 3.1.5, Client-server interaction */
logerrx("%s: DAD detected %s", ifp->name, inet_ntoa(*ia));
unlink(state->leasefile);
if (!(ifp->options->options & DHCPCD_STATIC) && !state->lease.frominfo)
dhcp_decline(ifp);
#ifdef IN_IFF_DUPLICATED
if ((iap = ipv4_iffindaddr(ifp, ia, NULL)) != NULL)
ipv4_deladdr(iap, 0);
#endif
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
eloop_timeout_add_sec(ifp->ctx->eloop,
DHCP_RAND_MAX, dhcp_discover, ifp);
}
#ifdef ARP
static void
dhcp_arp_probed(struct arp_state *astate)
dhcp_arp_not_found(struct arp_state *astate)
{
struct interface *ifp;
struct dhcp_state *state;
@ -2069,56 +2055,21 @@ dhcp_arp_probed(struct arp_state *astate)
}
#endif
/* Already bound so DAD has worked */
if (state->state == DHS_BOUND)
return;
logdebugx("%s: DAD completed for %s",
ifp->name, inet_ntoa(astate->addr));
if (!(ifo->options & DHCPCD_INFORM))
dhcp_bind(ifp);
#ifndef IN_IFF_DUPLICATED
else {
struct bootp *bootp;
size_t len;
bootp = state->new;
len = state->new_len;
state->new = state->offer;
state->new_len = state->offer_len;
get_lease(ifp, &state->lease, state->new, state->new_len);
ipv4_applyaddr(astate->iface);
state->new = bootp;
state->new_len = len;
}
#endif
/* If we forked, stop here. */
if (ifp->ctx->options & DHCPCD_FORKED)
return;
#ifdef IPV4LL
/* Stop IPv4LL now we have a working DHCP address */
ipv4ll_drop(ifp);
#endif
if (ifo->options & DHCPCD_INFORM)
dhcp_inform(ifp);
dhcp_finish_dad(ifp, &astate->addr);
}
static void
dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
dhcp_arp_found(struct arp_state *astate, const struct arp_msg *amsg)
{
struct in_addr addr;
#ifdef ARPING
struct interface *ifp;
struct dhcp_state *state;
#ifdef ARPING
struct if_options *ifo;
#endif
ifp = astate->iface;
state = D_STATE(ifp);
#ifdef ARPING
ifo = ifp->options;
if (state->arping_index != -1 &&
state->arping_index < ifo->arping_len &&
@ -2127,17 +2078,14 @@ dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
{
char buf[HWADDR_LEN * 3];
astate->failed.s_addr = ifo->arping[state->arping_index];
arp_report_conflicted(astate, amsg);
hwaddr_ntoa(amsg->sha, ifp->hwlen, buf, sizeof(buf));
if (dhcpcd_selectprofile(ifp, buf) == -1 &&
dhcpcd_selectprofile(ifp,
inet_ntoa(astate->failed)) == -1)
dhcpcd_selectprofile(ifp, inet_ntoa(amsg->sip)) == -1)
{
/* We didn't find a profile for this
* address or hwaddr, so move to the next
* arping profile */
dhcp_arp_probed(astate);
dhcp_arp_not_found(astate);
return;
}
arp_free(astate);
@ -2147,65 +2095,20 @@ dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
}
#endif
/* RFC 2131 3.1.5, Client-server interaction
* NULL amsg means IN_IFF_DUPLICATED */
if (amsg == NULL || (state->offer &&
(amsg->sip.s_addr == state->offer->yiaddr ||
(amsg->sip.s_addr == 0 &&
amsg->tip.s_addr == state->offer->yiaddr))))
{
#ifdef IN_IFF_DUPLICATED
struct ipv4_addr *ia;
#endif
if (amsg)
astate->failed.s_addr = state->offer->yiaddr;
else
astate->failed = astate->addr;
arp_report_conflicted(astate, amsg);
unlink(state->leasefile);
#ifdef ARP
if (!(ifp->options->options & DHCPCD_STATIC) &&
!state->lease.frominfo)
dhcp_decline(ifp);
#endif
#ifdef IN_IFF_DUPLICATED
if ((ia = ipv4_iffindaddr(ifp, &astate->addr, NULL)) != NULL)
ipv4_deladdr(ia, 1);
#endif
arp_free(astate);
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
eloop_timeout_add_sec(ifp->ctx->eloop,
DHCP_RAND_MAX, dhcp_discover, ifp);
return;
}
/* Bound address */
if (amsg && state->addr &&
amsg->sip.s_addr == state->addr->addr.s_addr)
{
astate->failed = state->addr->addr;
arp_report_conflicted(astate, amsg);
if (state->state == DHS_BOUND) {
/* For now, just report the duplicated address */
} else {
arp_free(astate);
dhcp_expire1(ifp);
}
return;
}
addr = astate->addr;
arp_free(astate);
dhcp_addr_duplicated(ifp, &addr);
}
#ifdef KERNEL_RFC5227
static void
dhcp_arp_announced(struct arp_state *state)
{
// TODO: DHCP addresses handle ACD?
//#ifdef KERNEL_RFC5227
arp_free(state);
//#endif
}
#endif
#endif /* KERNEL_RFC5227 */
#endif /* ARP */
void
dhcp_bind(struct interface *ifp)
@ -2232,17 +2135,17 @@ dhcp_bind(struct interface *ifp)
loginfox("%s: using static address %s/%d",
ifp->name, inet_ntoa(lease->addr),
inet_ntocidr(lease->mask));
lease->leasetime = ~0U;
lease->leasetime = DHCP_INFINITE_LIFETIME;
state->reason = "STATIC";
} else if (ifo->options & DHCPCD_INFORM) {
loginfox("%s: received approval for %s",
ifp->name, inet_ntoa(lease->addr));
lease->leasetime = ~0U;
lease->leasetime = DHCP_INFINITE_LIFETIME;
state->reason = "INFORM";
} else {
if (lease->frominfo)
state->reason = "TIMEOUT";
if (lease->leasetime == ~0U) {
if (lease->leasetime == DHCP_INFINITE_LIFETIME) {
lease->renewaltime =
lease->rebindtime =
lease->leasetime;
@ -2305,7 +2208,7 @@ dhcp_bind(struct interface *ifp)
else
state->reason = "BOUND";
}
if (lease->leasetime == ~0U)
if (lease->leasetime == DHCP_INFINITE_LIFETIME)
lease->renewaltime = lease->rebindtime = lease->leasetime;
else {
eloop_timeout_add_sec(ctx->eloop,
@ -2359,12 +2262,6 @@ dhcp_lastlease(void *arg)
if (ifp->ctx->options & DHCPCD_FORKED)
return;
state->interval = 0;
if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND &&
dhcp_leaseextend(ifp) == -1)
{
logerr("%s: %s", ifp->name, __func__);
dhcp_expire(ifp);
}
dhcp_discover(ifp);
}
@ -2397,17 +2294,32 @@ dhcp_message_new(struct bootp **bootp,
}
#ifdef ARP
#ifndef KERNEL_RFC5227
static void
dhcp_arp_defend_failed(struct arp_state *astate)
{
dhcp_drop(astate->iface, "EXPIRED");
dhcp_start1(astate->iface);
}
#endif
static struct arp_state *
dhcp_arp_new(struct interface *ifp, struct in_addr *addr)
{
struct arp_state *astate;
astate = arp_new(ifp, addr);
if (astate == NULL)
return NULL;
astate->probed_cb = dhcp_arp_probed;
astate->conflicted_cb = dhcp_arp_conflicted;
astate->found_cb = dhcp_arp_found;
astate->not_found_cb = dhcp_arp_not_found;
#ifdef KERNEL_RFC5227
astate->announced_cb = dhcp_arp_announced;
#else
astate->defend_failed_cb = dhcp_arp_defend_failed;
#endif
return astate;
}
@ -2417,7 +2329,6 @@ dhcp_arp_address(struct interface *ifp)
struct dhcp_state *state;
struct in_addr addr;
struct ipv4_addr *ia;
struct arp_state *astate;
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
@ -2427,10 +2338,6 @@ dhcp_arp_address(struct interface *ifp)
/* If the interface already has the address configured
* then we can't ARP for duplicate detection. */
ia = ipv4_iffindaddr(ifp, &addr, NULL);
astate = dhcp_arp_new(ifp, &addr);
if (astate == NULL)
return -1;
#ifdef IN_IFF_NOTUSEABLE
if (ia == NULL || ia->addr_flags & IN_IFF_NOTUSEABLE) {
state->state = DHS_PROBE;
@ -2439,7 +2346,8 @@ dhcp_arp_address(struct interface *ifp)
get_lease(ifp, &l, state->offer, state->offer_len);
/* Add the address now, let the kernel handle DAD. */
ipv4_addaddr(ifp, &l.addr, &l.mask, &l.brd);
ipv4_addaddr(ifp, &l.addr, &l.mask, &l.brd,
l.leasetime, l.rebindtime);
} else
loginfox("%s: waiting for DAD on %s",
ifp->name, inet_ntoa(addr));
@ -2447,8 +2355,13 @@ dhcp_arp_address(struct interface *ifp)
}
#else
if (ifp->options->options & DHCPCD_ARP && ia == NULL) {
struct arp_state *astate;
struct dhcp_lease l;
astate = dhcp_arp_new(ifp, &addr);
if (astate == NULL)
return -1;
state->state = DHS_PROBE;
get_lease(ifp, &l, state->offer, state->offer_len);
loginfox("%s: probing address %s/%d",
@ -2705,9 +2618,14 @@ dhcp_drop(struct interface *ifp, const char *reason)
return;
}
#ifdef ARP
if (state->addr != NULL)
arp_freeaddr(ifp, &state->addr->addr);
#endif
#ifdef ARPING
state->arping_index = -1;
#endif
if (ifp->options->options & DHCPCD_RELEASE &&
!(ifp->options->options & DHCPCD_INFORM))
{
@ -3653,15 +3571,9 @@ dhcp_init(struct interface *ifp)
const struct if_options *ifo;
uint8_t len;
char buf[(sizeof(ifo->clientid) - 1) * 3];
int r;
r = dhcp_initstate(ifp);
if (r == -1)
if (dhcp_initstate(ifp) == -1)
return -1;
else if (r == 1) {
/* Now is a good time to find IPv4 routes */
if_initrt(ifp->ctx, AF_INET);
}
state = D_STATE(ifp);
state->state = DHS_INIT;
@ -3771,7 +3683,7 @@ dhcp_start1(void *arg)
astate = dhcp_arp_new(ifp, NULL);
if (astate)
dhcp_arp_probed(astate);
dhcp_arp_not_found(astate);
return;
}
#endif
@ -3848,7 +3760,7 @@ dhcp_start1(void *arg)
state->offer = NULL;
state->offer_len = 0;
} else if (!(ifo->options & DHCPCD_LASTLEASE_EXTEND) &&
state->lease.leasetime != ~0U &&
state->lease.leasetime != DHCP_INFINITE_LIFETIME &&
stat(state->leasefile, &st) == 0)
{
time_t now;
@ -4023,8 +3935,10 @@ dhcp_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid)
return;
#ifdef IN_IFF_NOTUSEABLE
if (ia->addr_flags & IN_IFF_NOTUSEABLE)
return;
if (!(ia->addr_flags & IN_IFF_NOTUSEABLE))
dhcp_finish_dad(ifp, &ia->addr);
else if (ia->addr_flags & IN_IFF_DUPLICATED)
dhcp_addr_duplicated(ifp, &ia->addr);
#endif
ifo = ifp->options;

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -182,6 +183,10 @@ struct dhcp_lease {
uint32_t cookie;
};
#ifndef DHCP_INFINITE_LIFETIME
# define DHCP_INFINITE_LIFETIME (~0U)
#endif
enum DHS {
DHS_NONE,
DHS_INIT,
@ -245,15 +250,15 @@ struct dhcp_state {
#include "dhcpcd.h"
#include "if-options.h"
char *decode_rfc3361(const uint8_t *, size_t);
ssize_t decode_rfc3442(char *, size_t, const uint8_t *p, size_t);
ssize_t print_rfc3361(FILE *, const uint8_t *, size_t);
ssize_t print_rfc3442(FILE *, const uint8_t *, size_t);
void dhcp_printoptions(const struct dhcpcd_ctx *,
const struct dhcp_opt *, size_t);
uint16_t dhcp_get_mtu(const struct interface *);
int dhcp_get_routes(struct rt_head *, struct interface *);
ssize_t dhcp_env(char **, const char *, const struct bootp *, size_t,
const struct interface *);
int dhcp_get_routes(rb_tree_t *, struct interface *);
ssize_t dhcp_env(FILE *, const char *, const struct interface *,
const struct bootp *, size_t);
void dhcp_handleifa(int, struct ipv4_addr *, pid_t pid);
void dhcp_drop(struct interface *, const char *);

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -582,10 +583,14 @@ dhcp6_delegateaddr(struct in6_addr *addr, struct interface *ifp,
#define BIT(n) (1UL << (n))
#define BIT_MASK(len) (BIT(len) - 1)
if (ia->sla_max == 0)
if (ia->sla_max == 0) {
/* Work out the real sla_max from our bits used */
ia->sla_max = (uint32_t)BIT_MASK(asla.prefix_len -
prefix->prefix_len);
bits = asla.prefix_len - prefix->prefix_len;
/* Make static analysis happy.
* Bits cannot be bigger than 32 thanks to fls32. */
assert(bits <= 32);
ia->sla_max = (uint32_t)BIT_MASK(bits);
}
}
if (ipv6_userprefix(&prefix->prefix, prefix->prefix_len,
@ -2845,26 +2850,11 @@ dhcp6_delegate_prefix(struct interface *ifp)
struct dhcp6_state *s = D6_STATE(ifd);
ipv6_addaddrs(&s->addrs);
/*
* Can't add routes here because that will trigger
* interface sorting which may break the current
* enumeration.
* This doesn't really matter thanks to DaD because
* calling the script will be delayed and routes
* will get re-built if needed first.
* This only cause minor confusion when dhcpcd is
* restarted and confirms a lease where prior delegation
* has already been assigned, because it will log it
* added routes after the script has run.
* The routes should still be there and fine though.
*/
dhcp6_script_try_run(ifd, 1);
}
}
/* Now all addresses have been added, rebuild the routing table. */
if_initrt(ifp->ctx, AF_INET6);
rt_build(ifp->ctx, AF_INET6);
}
@ -2929,7 +2919,6 @@ dhcp6_find_delegates(struct interface *ifp)
state = D6_STATE(ifp);
state->state = DH6S_DELEGATED;
ipv6_addaddrs(&state->addrs);
if_initrt(ifp->ctx, AF_INET6);
rt_build(ifp->ctx, AF_INET6);
dhcp6_script_try_run(ifp, 1);
}
@ -3167,7 +3156,6 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
else
lognewinfo("%s: expire in %"PRIu32" seconds",
ifp->name, state->expire);
if_initrt(ifp->ctx, AF_INET6);
rt_build(ifp->ctx, AF_INET6);
if (!timed_out)
dhcp6_writelease(ifp);
@ -3971,24 +3959,22 @@ dhcp6_handleifa(int cmd, struct ipv6_addr *ia, pid_t pid)
}
ssize_t
dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
dhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp,
const struct dhcp6_message *m, size_t len)
{
const struct if_options *ifo;
struct dhcp_opt *opt, *vo;
const uint8_t *p;
struct dhcp6_option o;
size_t i, n;
size_t i;
char *pfx;
uint32_t en;
const struct dhcpcd_ctx *ctx;
#ifndef SMALL
const struct dhcp6_state *state;
const struct ipv6_addr *ap;
char *v, *val;
#endif
n = 0;
if (m == NULL)
goto delegated;
@ -4003,28 +3989,20 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
ctx = ifp->ctx;
/* Zero our indexes */
if (env) {
for (i = 0, opt = ctx->dhcp6_opts;
i < ctx->dhcp6_opts_len;
i++, opt++)
dhcp_zero_index(opt);
for (i = 0, opt = ifp->options->dhcp6_override;
i < ifp->options->dhcp6_override_len;
i++, opt++)
dhcp_zero_index(opt);
for (i = 0, opt = ctx->vivso;
i < ctx->vivso_len;
i++, opt++)
dhcp_zero_index(opt);
i = strlen(prefix) + strlen("_dhcp6") + 1;
pfx = malloc(i);
if (pfx == NULL) {
logerr(__func__);
return -1;
}
snprintf(pfx, i, "%s_dhcp6", prefix);
} else
pfx = NULL;
for (i = 0, opt = ctx->dhcp6_opts;
i < ctx->dhcp6_opts_len;
i++, opt++)
dhcp_zero_index(opt);
for (i = 0, opt = ifp->options->dhcp6_override;
i < ifp->options->dhcp6_override_len;
i++, opt++)
dhcp_zero_index(opt);
for (i = 0, opt = ctx->vivso;
i < ctx->vivso_len;
i++, opt++)
dhcp_zero_index(opt);
if (asprintf(&pfx, "%s_dhcp6", prefix) == -1)
return -1;
/* Unlike DHCP, DHCPv6 options *may* occur more than once.
* There is also no provision for option concatenation unlike DHCP. */
@ -4070,15 +4048,13 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
opt = NULL;
}
if (opt) {
n += dhcp_envoption(ifp->ctx,
env == NULL ? NULL : &env[n],
pfx, ifp->name,
dhcp_envoption(ifp->ctx,
fp, pfx, ifp->name,
opt, dhcp6_getoption, p, o.len);
}
if (vo) {
n += dhcp_envoption(ifp->ctx,
env == NULL ? NULL : &env[n],
pfx, ifp->name,
dhcp_envoption(ifp->ctx,
fp, pfx, ifp->name,
vo, dhcp6_getoption,
p + sizeof(en),
o.len - sizeof(en));
@ -4090,38 +4066,29 @@ delegated:
#ifndef SMALL
/* Needed for Delegated Prefixes */
state = D6_CSTATE(ifp);
i = 0;
TAILQ_FOREACH(ap, &state->addrs, next) {
if (ap->delegating_prefix) {
i += strlen(ap->saddr) + 1;
}
if (ap->delegating_prefix)
break;
}
if (env && i) {
i += strlen(prefix) + strlen("_delegated_dhcp6_prefix=");
v = val = env[n] = malloc(i);
if (v == NULL) {
logerr(__func__);
if (ap == NULL)
return 1;
if (fprintf(fp, "%s_delegated_dhcp6_prefix=", prefix) == -1)
return -1;
TAILQ_FOREACH(ap, &state->addrs, next) {
if (ap->delegating_prefix == NULL)
continue;
if (ap != TAILQ_FIRST(&state->addrs)) {
if (fputc(' ', fp) == EOF)
return -1;
}
if (fprintf(fp, "%s", ap->saddr) == -1)
return -1;
}
v += snprintf(val, i, "%s_delegated_dhcp6_prefix=", prefix);
TAILQ_FOREACH(ap, &state->addrs, next) {
if (ap->delegating_prefix) {
/* Can't use stpcpy(3) due to "security" */
const char *sap = ap->saddr;
do
*v++ = *sap;
while (*++sap != '\0');
*v++ = ' ';
}
}
*--v = '\0';
}
if (i)
n++;
if (fputc('\0', fp) == EOF)
return -1;
#endif
return (ssize_t)n;
return 1;
}
int

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -228,7 +229,7 @@ size_t dhcp6_find_delegates(struct interface *);
int dhcp6_start(struct interface *, enum DH6S);
void dhcp6_reboot(struct interface *);
void dhcp6_renew(struct interface *);
ssize_t dhcp6_env(char **, const char *, const struct interface *,
ssize_t dhcp6_env(FILE *, const char *, const struct interface *,
const struct dhcp6_message *, size_t);
void dhcp6_free(struct interface *);
void dhcp6_handleifa(int, struct ipv6_addr *, pid_t);

View File

@ -1,3 +1,5 @@
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
.\" Copyright (c) 2006-2019 Roy Marples
.\" All rights reserved
.\"
@ -22,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd January 2, 2019
.Dd July 23, 2019
.Dt DHCPCD 8
.Os
.Sh NAME
@ -352,7 +354,7 @@ rather than
.Xr syslog 3 .
The
.Ar logfile
is s reopened when
is reopened when
.Nm
receives the
.Dv SIGUSR2

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -738,9 +739,6 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags,
#ifdef INET
dhcp_abort(ifp);
#endif
#ifdef INET6
ipv6nd_expire(ifp, 0);
#endif
#ifdef DHCP6
dhcp6_abort(ifp);
#endif
@ -785,7 +783,7 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags,
/* Set any IPv6 Routers we remembered to expire
* faster than they would normally as we
* maybe on a new network. */
ipv6nd_expire(ifp, RTR_CARRIER_EXPIRE);
ipv6nd_startexpire(ifp);
#endif
/* RFC4941 Section 3.5 */
ipv6_gentempifid(ifp);
@ -1003,6 +1001,7 @@ dhcpcd_handleinterface(void *arg, int action, const char *ifname)
struct if_head *ifs;
struct interface *ifp, *iff;
const char * const argv[] = { ifname };
int e;
ctx = arg;
if (action == -1) {
@ -1026,13 +1025,17 @@ dhcpcd_handleinterface(void *arg, int action, const char *ifname)
logerr(__func__);
return -1;
}
ifp = if_find(ifs, ifname);
if (ifp == NULL) {
/* This can happen if an interface is quickly added
* and then removed. */
errno = ENOENT;
return -1;
e = -1;
goto out;
}
e = 1;
/* Check if we already have the interface */
iff = if_find(ctx->ifaces, ifp->name);
@ -1061,6 +1064,7 @@ dhcpcd_handleinterface(void *arg, int action, const char *ifname)
dhcpcd_prestartinterface(iff);
}
out:
/* Free our discovered list */
while ((ifp = TAILQ_FIRST(ifs))) {
TAILQ_REMOVE(ifs, ifp, next);
@ -1068,7 +1072,7 @@ dhcpcd_handleinterface(void *arg, int action, const char *ifname)
}
free(ifs);
return 1;
return e;
}
static void
@ -1081,7 +1085,8 @@ dhcpcd_handlelink(void *arg)
dhcpcd_linkoverflow(ctx);
return;
}
logerr(__func__);
if (errno != ENOTSUP)
logerr(__func__);
}
}
@ -1093,6 +1098,22 @@ dhcpcd_checkcarrier(void *arg)
dhcpcd_handlecarrier(ifp->ctx, LINK_UNKNOWN, ifp->flags, ifp->name);
}
#ifndef SMALL
static void
dhcpcd_setlinkrcvbuf(struct dhcpcd_ctx *ctx)
{
socklen_t socklen;
if (ctx->link_rcvbuf == 0)
return;
socklen = sizeof(ctx->link_rcvbuf);
if (setsockopt(ctx->link_fd, SOL_SOCKET,
SO_RCVBUF, &ctx->link_rcvbuf, socklen) == -1)
logerr(__func__);
}
#endif
void
dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx)
{
@ -1113,10 +1134,17 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx)
eloop_exit(ctx->eloop, EXIT_FAILURE);
return;
}
#ifndef SMALL
dhcpcd_setlinkrcvbuf(ctx);
#endif
eloop_event_add(ctx->eloop, ctx->link_fd, dhcpcd_handlelink, ctx);
/* Work out the current interfaces. */
ifaces = if_discover(ctx, &ifaddrs, ctx->ifc, ctx->ifv);
if (ifaces == NULL) {
logerr(__func__);
return;
}
/* Punt departed interfaces */
TAILQ_FOREACH_SAFE(ifp, ctx->ifaces, next, ifn) {
@ -1126,21 +1154,23 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx)
}
/* Add new interfaces */
TAILQ_FOREACH_SAFE(ifp, ifaces, next, ifn) {
while ((ifp = TAILQ_FIRST(ifaces)) != NULL ) {
TAILQ_REMOVE(ifaces, ifp, next);
ifp1 = if_find(ctx->ifaces, ifp->name);
if (ifp1 != NULL) {
/* If the interface already exists,
* check carrier state. */
eloop_timeout_add_sec(ctx->eloop, 0,
dhcpcd_checkcarrier, ifp1);
if_free(ifp);
continue;
}
TAILQ_REMOVE(ifaces, ifp, next);
TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);
if (ifp->active)
eloop_timeout_add_sec(ctx->eloop, 0,
dhcpcd_prestartinterface, ifp);
}
free(ifaces);
/* Update address state. */
if_markaddrsstale(ctx->ifaces);
@ -1958,6 +1988,9 @@ printpidfile:
logerr("%s: if_opensockets", __func__);
goto exit_failure;
}
#ifndef SMALL
dhcpcd_setlinkrcvbuf(&ctx);
#endif
/* When running dhcpcd against a single interface, we need to retain
* the old behaviour of waiting for an IP address */
@ -2057,7 +2090,6 @@ printpidfile:
free_options(&ctx, ifo);
ifo = NULL;
if_sortinterfaces(&ctx);
TAILQ_FOREACH(ifp, ctx.ifaces, next) {
if (ifp->active)
eloop_timeout_add_sec(ctx.eloop, 0,
@ -2114,5 +2146,11 @@ exit1:
if (ctx.options & DHCPCD_FORKED)
_exit(i); /* so atexit won't remove our pidfile */
#endif
#ifdef HAVE_OPEN_MEMSTREAM
if (ctx.script_fp)
fclose(ctx.script_fp);
#endif
free(ctx.script_buf);
free(ctx.script_env);
return i;
}

View File

@ -1,3 +1,5 @@
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
.\" Copyright (c) 2006-2019 Roy Marples
.\" All rights reserved
.\"
@ -428,6 +430,12 @@ globally but needs to be enabled for one interface.
.It Ic leasetime Ar seconds
Request a leasetime of
.Ar seconds .
.It Ic link_rcvbuf Ar size
Override the size of the link receive buffer from the kernel default.
While
.Nm dhcpcd
will recover from link buffer overflows,
this may not be desirable on heavily loaded systems.
.It Ic logfile Ar logfile
Writes to the specified
.Ar logfile

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -136,13 +137,18 @@ struct dhcpcd_ctx {
size_t duid_len;
struct if_head *ifaces;
struct rt_head routes; /* our routes */
struct rt_head kroutes; /* all kernel routes */
struct rt_head froutes; /* free routes for re-use */
rb_tree_t routes; /* our routes */
#ifdef RT_FREE_ROUTE_TABLE
rb_tree_t froutes; /* free routes for re-use */
#endif
size_t rt_order; /* route order storage */
int pf_inet_fd;
void *priv;
int link_fd;
#ifndef SMALL
int link_rcvbuf;
#endif
int seq; /* route message sequence no */
int sseq; /* successful seq no sent */
@ -151,6 +157,14 @@ struct dhcpcd_ctx {
#endif
struct eloop *eloop;
#ifdef HAVE_OPEN_MEMSTREAM
FILE *script_fp;
#endif
char *script_buf;
size_t script_buflen;
char **script_env;
size_t script_envlen;
int control_fd;
int control_unpriv_fd;
struct fd_list_head control_fds;

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2015 Roy Marples <roy@marples.name>

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* eloop - portable event based main loop.
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* BSD interface driver for dhcpcd
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -496,6 +497,8 @@ if_route(unsigned char cmd, const struct rt *rt)
bool gateway_unspec;
assert(rt != NULL);
assert(rt->rt_ifp != NULL);
assert(rt->rt_ifp->ctx != NULL);
ctx = rt->rt_ifp->ctx;
#define ADDSA(sa) do { \
@ -695,15 +698,13 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
}
int
if_initrt(struct dhcpcd_ctx *ctx, int af)
if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af)
{
struct rt_msghdr *rtm;
int mib[6];
size_t needed;
char *buf, *p, *end;
struct rt rt;
rt_headclear(&ctx->kroutes, af);
struct rt rt, *rtn;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
@ -730,10 +731,15 @@ if_initrt(struct dhcpcd_ctx *ctx, int af)
errno = EINVAL;
break;
}
if (if_copyrt(ctx, &rt, rtm) == 0) {
rt.rt_dflags |= RTDF_INIT;
rt_recvrt(RTM_ADD, &rt, rtm->rtm_pid);
if (if_copyrt(ctx, &rt, rtm) != 0)
continue;
if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
logerr(__func__);
break;
}
memcpy(rtn, &rt, sizeof(*rtn));
if (rb_tree_insert_node(kroutes, rtn) != rtn)
rt_free(rtn);
}
free(buf);
return p == end ? 0 : -1;
@ -1063,7 +1069,7 @@ if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
return 0;
if (if_copyrt(ctx, &rt, rtm) == -1)
return errno == ENOTSUP ? 0 : -1;
return -1;
#ifdef INET6
/*
@ -1306,7 +1312,6 @@ if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
#ifdef RTM_DESYNC
case RTM_DESYNC:
dhcpcd_linkoverflow(ctx);
return 0;
#endif
}
@ -1326,12 +1331,13 @@ if_handlelink(struct dhcpcd_ctx *ctx)
return -1;
if (len == 0)
return 0;
if ((size_t)len < offsetof(struct rt_msghdr, rtm_index) ||
len < rtm.hdr.rtm_msglen)
{
if (len < rtm.hdr.rtm_msglen) {
errno = EINVAL;
return -1;
}
/* We generally treat rtm.hdr has an array so we can easily
* access the following data. */
/* coverity[callee_ptr_arith] */
return if_dispatch(ctx, &rtm.hdr);
}

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -64,7 +65,7 @@
#define O_IPV6RS O_BASE + 4
#define O_NOIPV6RS O_BASE + 5
#define O_IPV6RA_FORK O_BASE + 6
// unused O_BASE + 7
#define O_LINK_RCVBUF O_BASE + 7
// unused O_BASE + 8
#define O_NOALIAS O_BASE + 9
#define O_IA_NA O_BASE + 10
@ -204,6 +205,7 @@ const struct option cf_options[] = {
{"lastleaseextend", no_argument, NULL, O_LASTLEASE_EXTEND},
{"inactive", no_argument, NULL, O_INACTIVE},
{"mudurl", required_argument, NULL, O_MUDURL},
{"link_rcvbuf", required_argument, NULL, O_LINK_RCVBUF},
{NULL, 0, NULL, '\0'}
};
@ -724,8 +726,9 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
logerr(__func__);
return -1;
}
parse_str(ifo->script, dl, arg, PARSE_STRING_NULL);
if (ifo->script[0] == '\0' ||
s = parse_str(ifo->script, dl, arg, PARSE_STRING_NULL);
if (s == -1 ||
ifo->script[0] == '\0' ||
strcmp(ifo->script, "/dev/null") == 0)
{
free(ifo->script);
@ -1122,16 +1125,14 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
*fp = ' ';
return -1;
}
if ((rt = rt_new0(ctx)) == NULL) {
*fp = ' ';
*fp = ' ';
if ((rt = rt_new0(ctx)) == NULL)
return -1;
}
sa_in_init(&rt->rt_dest, &addr);
sa_in_init(&rt->rt_netmask, &addr2);
sa_in_init(&rt->rt_gateway, &addr3);
TAILQ_INSERT_TAIL(&ifo->routes, rt, rt_next);
*fp = ' ';
add_environ(&ifo->config, arg, 0);
if (rt_proto_add_ctx(&ifo->routes, rt, ctx))
add_environ(&ifo->config, arg, 0);
} else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
if (parse_addr(&addr, NULL, p) == -1)
return -1;
@ -1141,8 +1142,8 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
sa_in_init(&rt->rt_dest, &addr2);
sa_in_init(&rt->rt_netmask, &addr2);
sa_in_init(&rt->rt_gateway, &addr);
TAILQ_INSERT_TAIL(&ifo->routes, rt, rt_next);
add_environ(&ifo->config, arg, 0);
if (rt_proto_add_ctx(&ifo->routes, rt, ctx))
add_environ(&ifo->config, arg, 0);
} else if (strncmp(arg, "interface_mtu=",
strlen("interface_mtu=")) == 0 ||
strncmp(arg, "mtu=", strlen("mtu=")) == 0)
@ -1868,6 +1869,7 @@ err_sla:
ifo->vivco_len + 1, sizeof(*ifo->vivco));
if (vivco == NULL) {
logerr( __func__);
free(np);
return -1;
}
ifo->vivco = vivco;
@ -2160,6 +2162,16 @@ err_sla:
}
*ifo->mudurl = (uint8_t)s;
break;
case O_LINK_RCVBUF:
#ifndef SMALL
ARG_REQUIRED;
ctx->link_rcvbuf = (int)strtoi(arg, NULL, 0, 0, INT32_MAX, &e);
if (e) {
logerrx("failed to convert link_rcvbuf %s", arg);
return -1;
}
#endif
break;
default:
return 0;
}
@ -2269,7 +2281,7 @@ default_config(struct dhcpcd_ctx *ctx)
ifo->script = UNCONST(default_script);
ifo->metric = -1;
ifo->auth.options |= DHCPCD_AUTH_REQUIRE;
TAILQ_INIT(&ifo->routes);
rb_tree_init(&ifo->routes, &rt_compare_proto_ops);
#ifdef AUTH
TAILQ_INIT(&ifo->auth.tokens);
#endif
@ -2327,6 +2339,9 @@ read_config(struct dhcpcd_ctx *ctx,
buf = NULL;
buflen = 0;
/* Reset route order */
ctx->rt_order = 0;
/* Parse our embedded options file */
if (ifname == NULL && !(ctx->options & DHCPCD_PRINT_PIDFILE)) {
/* Space for initial estimates */

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -181,7 +182,7 @@ struct if_options {
struct in_addr req_addr;
struct in_addr req_mask;
struct in_addr req_brd;
struct rt_head routes;
rb_tree_t routes;
struct in6_addr req_addr6;
uint8_t req_prefix_len;
unsigned int mtu;

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -59,7 +60,6 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "common.h"
#include "dev.h"
@ -704,58 +704,6 @@ if_domtu(const struct interface *ifp, short int mtu)
return ifr.ifr_mtu;
}
/* Interface comparer for working out ordering. */
static int
if_cmp(const struct interface *si, const struct interface *ti)
{
#ifdef INET
int r;
#endif
/* Check active first */
if (si->active > ti->active)
return -1;
if (si->active < ti->active)
return 1;
/* Check carrier status next */
if (si->carrier > ti->carrier)
return -1;
if (si->carrier < ti->carrier)
return 1;
#ifdef INET
if (D_STATE_RUNNING(si) && !D_STATE_RUNNING(ti))
return -1;
if (!D_STATE_RUNNING(si) && D_STATE_RUNNING(ti))
return 1;
#endif
#ifdef INET6
if (RS_STATE_RUNNING(si) && !RS_STATE_RUNNING(ti))
return -1;
if (!RS_STATE_RUNNING(si) && RS_STATE_RUNNING(ti))
return 1;
#endif
#ifdef DHCP6
if (D6_STATE_RUNNING(si) && !D6_STATE_RUNNING(ti))
return -1;
if (!D6_STATE_RUNNING(si) && D6_STATE_RUNNING(ti))
return 1;
#endif
#ifdef INET
/* Special attention needed here due to states and IPv4LL. */
if ((r = ipv4_ifcmp(si, ti)) != 0)
return r;
#endif
/* Finally, metric */
if (si->metric < ti->metric)
return -1;
if (si->metric > ti->metric)
return 1;
return 0;
}
#ifdef ALIAS_ADDR
int
if_makealias(char *alias, size_t alias_len, const char *ifname, int lun)
@ -767,35 +715,6 @@ if_makealias(char *alias, size_t alias_len, const char *ifname, int lun)
}
#endif
/* Sort the interfaces into a preferred order - best first, worst last. */
void
if_sortinterfaces(struct dhcpcd_ctx *ctx)
{
struct if_head sorted;
struct interface *ifp, *ift;
if (ctx->ifaces == NULL ||
(ifp = TAILQ_FIRST(ctx->ifaces)) == NULL ||
TAILQ_NEXT(ifp, next) == NULL)
return;
TAILQ_INIT(&sorted);
TAILQ_REMOVE(ctx->ifaces, ifp, next);
TAILQ_INSERT_HEAD(&sorted, ifp, next);
while ((ifp = TAILQ_FIRST(ctx->ifaces))) {
TAILQ_REMOVE(ctx->ifaces, ifp, next);
TAILQ_FOREACH(ift, &sorted, next) {
if (if_cmp(ifp, ift) == -1) {
TAILQ_INSERT_BEFORE(ift, ifp, next);
break;
}
}
if (ift == NULL)
TAILQ_INSERT_TAIL(&sorted, ifp, next);
}
TAILQ_CONCAT(ctx->ifaces, &sorted, next);
}
struct interface *
if_findifpfromcmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, int *hoplimit)
{

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -123,7 +124,6 @@ void if_deletestaleaddrs(struct if_head *);
struct interface *if_find(struct if_head *, const char *);
struct interface *if_findindex(struct if_head *, unsigned int);
struct interface *if_loopback(struct dhcpcd_ctx *);
void if_sortinterfaces(struct dhcpcd_ctx *);
void if_free(struct interface *);
int if_domtu(const struct interface *, short int);
#define if_getmtu(ifp) if_domtu((ifp), 0)
@ -186,7 +186,7 @@ int if_handlelink(struct dhcpcd_ctx *);
#endif
int if_route(unsigned char, const struct rt *rt);
int if_initrt(struct dhcpcd_ctx *, int);
int if_initrt(struct dhcpcd_ctx *, rb_tree_t *, int);
#ifdef INET
int if_address(unsigned char, const struct ipv4_addr *);

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -38,6 +39,7 @@
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -247,10 +249,10 @@ ipv4_ifcmp(const struct interface *si, const struct interface *ti)
}
static int
inet_dhcproutes(struct rt_head *routes, struct interface *ifp)
inet_dhcproutes(rb_tree_t *routes, struct interface *ifp, bool *have_default)
{
const struct dhcp_state *state;
struct rt_head nroutes;
rb_tree_t nroutes;
struct rt *rt, *r = NULL;
struct in_addr in;
uint16_t mtu;
@ -263,7 +265,7 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp)
/* An address does have to exist. */
assert(state->addr);
TAILQ_INIT(&nroutes);
rb_tree_init(&nroutes, &rt_compare_proto_ops);
/* First, add a subnet route. */
if (!(ifp->flags & IFF_POINTOPOINT) &&
@ -283,12 +285,12 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp)
//in.s_addr = INADDR_ANY;
//sa_in_init(&rt->rt_gateway, &in);
rt->rt_gateway.sa_family = AF_UNSPEC;
TAILQ_INSERT_HEAD(&nroutes, rt, rt_next);
rt_proto_add(&nroutes, rt);
}
/* If any set routes, grab them, otherwise DHCP routes. */
if (TAILQ_FIRST(&ifp->options->routes)) {
TAILQ_FOREACH(r, &ifp->options->routes, rt_next) {
if (RB_TREE_MIN(&ifp->options->routes)) {
RB_TREE_FOREACH(r, &ifp->options->routes) {
if (sa_is_unspecified(&r->rt_gateway))
break;
if ((rt = rt_new0(ifp->ctx)) == NULL)
@ -296,14 +298,14 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp)
memcpy(rt, r, sizeof(*rt));
rt_setif(rt, ifp);
rt->rt_dflags = RTDF_STATIC;
TAILQ_INSERT_TAIL(&nroutes, rt, rt_next);
rt_proto_add(&nroutes, rt);
}
} else {
if (dhcp_get_routes(&nroutes, ifp) == -1)
return -1;
}
/* If configured, Install a gateway to the desintion
/* If configured, install a gateway to the desintion
* for P2P interfaces. */
if (ifp->flags & IFF_POINTOPOINT &&
has_option_mask(ifp->options->dstmask, DHO_ROUTER))
@ -315,20 +317,26 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp)
sa_in_init(&rt->rt_netmask, &in);
sa_in_init(&rt->rt_gateway, &state->addr->brd);
sa_in_init(&rt->rt_ifa, &state->addr->addr);
TAILQ_INSERT_HEAD(routes, rt, rt_next);
rt_proto_add(&nroutes, rt);
}
/* Copy our address as the source address and set mtu */
mtu = dhcp_get_mtu(ifp);
n = 0;
TAILQ_FOREACH(rt, &nroutes, rt_next) {
while ((rt = RB_TREE_MIN(&nroutes)) != NULL) {
rb_tree_remove_node(&nroutes, rt);
rt->rt_mtu = mtu;
if (!(rt->rt_dflags & RTDF_STATIC))
rt->rt_dflags |= RTDF_DHCP;
sa_in_init(&rt->rt_ifa, &state->addr->addr);
n++;
if (rb_tree_insert_node(routes, rt) != rt) {
rt_free(rt);
continue;
}
if (rt_is_default(rt))
*have_default = true;
n = 1;
}
TAILQ_CONCAT(routes, &nroutes, rt_next);
return n;
}
@ -336,20 +344,23 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp)
/* We should check to ensure the routers are on the same subnet
* OR supply a host route. If not, warn and add a host route. */
static int
inet_routerhostroute(struct rt_head *routes, struct interface *ifp)
inet_routerhostroute(rb_tree_t *routes, struct interface *ifp)
{
struct rt *rt, *rth;
struct rt *rt, *rth, *rtp;
struct sockaddr_in *dest, *netmask, *gateway;
const char *cp, *cp2, *cp3, *cplim;
struct if_options *ifo;
const struct dhcp_state *state;
struct in_addr in;
rb_tree_t troutes;
/* Don't add a host route for these interfaces. */
if (ifp->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
return 0;
TAILQ_FOREACH(rt, routes, rt_next) {
rb_tree_init(&troutes, &rt_compare_proto_ops);
RB_TREE_FOREACH(rt, routes) {
if (rt->rt_dest.sa_family != AF_INET)
continue;
if (!sa_is_unspecified(&rt->rt_dest) ||
@ -357,13 +368,14 @@ inet_routerhostroute(struct rt_head *routes, struct interface *ifp)
continue;
gateway = satosin(&rt->rt_gateway);
/* Scan for a route to match */
TAILQ_FOREACH(rth, routes, rt_next) {
RB_TREE_FOREACH(rth, routes) {
if (rth == rt)
break;
/* match host */
if (sa_cmp(&rth->rt_dest, &rt->rt_gateway) == 0)
break;
/* match subnet */
/* XXX ADD TO RT_COMARE? XXX */
cp = (const char *)&gateway->sin_addr.s_addr;
dest = satosin(&rth->rt_dest);
cp2 = (const char *)&dest->sin_addr.s_addr;
@ -408,6 +420,7 @@ inet_routerhostroute(struct rt_head *routes, struct interface *ifp)
ifp->name,
sa_addrtop(&rt->rt_gateway, buf, sizeof(buf)));
}
if ((rth = rt_new(ifp)) == NULL)
return -1;
rth->rt_flags |= RTF_HOST;
@ -418,24 +431,36 @@ inet_routerhostroute(struct rt_head *routes, struct interface *ifp)
sa_in_init(&rth->rt_gateway, &in);
rth->rt_mtu = dhcp_get_mtu(ifp);
sa_in_init(&rth->rt_ifa, &state->addr->addr);
TAILQ_INSERT_BEFORE(rt, rth, rt_next);
/* We need to insert the host route just before the router. */
while ((rtp = RB_TREE_MAX(routes)) != NULL) {
rb_tree_remove_node(routes, rtp);
rt_proto_add(&troutes, rtp);
if (rtp == rt)
break;
}
rt_proto_add(routes, rth);
/* troutes is now reversed, so add backwards again. */
while ((rtp = RB_TREE_MAX(&troutes)) != NULL) {
rb_tree_remove_node(&troutes, rtp);
rt_proto_add(routes, rtp);
}
}
return 0;
}
bool
inet_getroutes(struct dhcpcd_ctx *ctx, struct rt_head *routes)
inet_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes)
{
struct interface *ifp;
#ifdef IPV4LL
struct rt def;
bool have_default;
bool have_default = false;
#endif
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (!ifp->active)
continue;
if (inet_dhcproutes(routes, ifp) == -1)
if (inet_dhcproutes(routes, ifp, &have_default) == -1)
return false;
#ifdef IPV4LL
if (ipv4ll_subnetroute(routes, ifp) == -1)
@ -447,15 +472,13 @@ inet_getroutes(struct dhcpcd_ctx *ctx, struct rt_head *routes)
#ifdef IPV4LL
/* If there is no default route, see if we can use an IPv4LL one. */
memset(&def, 0, sizeof(def));
def.rt_dest.sa_family = AF_INET;
have_default = (rt_find(routes, &def) != NULL);
if (!have_default) {
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (ifp->active &&
ipv4ll_defaultroute(routes, ifp) == 1)
break;
}
if (have_default)
return true;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (ifp->active &&
ipv4ll_defaultroute(routes, ifp) == 1)
break;
}
#endif
@ -468,11 +491,6 @@ ipv4_deladdr(struct ipv4_addr *addr, int keeparp)
int r;
struct ipv4_state *state;
struct ipv4_addr *ap;
#ifdef ARP
struct arp_state *astate;
#else
UNUSED(keeparp);
#endif
logdebugx("%s: deleting IP address %s",
addr->iface->name, addr->saddr);
@ -484,8 +502,8 @@ ipv4_deladdr(struct ipv4_addr *addr, int keeparp)
logerr("%s: %s", addr->iface->name, __func__);
#ifdef ARP
if (!keeparp && (astate = arp_find(addr->iface, &addr->addr)) != NULL)
arp_free(astate);
if (!keeparp)
arp_freeaddr(addr->iface, &addr->addr);
#endif
state = IPV4_STATE(addr->iface);
@ -523,6 +541,7 @@ delete_address(struct interface *ifp)
ifo->options & DHCPCD_INFORM ||
(ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0))
return 0;
arp_freeaddr(ifp, &state->addr->addr);
r = ipv4_deladdr(state->addr, 0);
return r;
}
@ -597,7 +616,8 @@ find_lun:
struct ipv4_addr *
ipv4_addaddr(struct interface *ifp, const struct in_addr *addr,
const struct in_addr *mask, const struct in_addr *bcast)
const struct in_addr *mask, const struct in_addr *bcast,
uint32_t vltime, uint32_t pltime)
{
struct ipv4_state *state;
struct ipv4_addr *ia;
@ -637,6 +657,8 @@ ipv4_addaddr(struct interface *ifp, const struct in_addr *addr,
ia->mask = *mask;
ia->brd = *bcast;
ia->vltime = vltime;
ia->pltime = pltime;
snprintf(ia->saddr, sizeof(ia->saddr), "%s/%d",
inet_ntoa(*addr), inet_ntocidr(*mask));
@ -679,7 +701,8 @@ ipv4_daddaddr(struct interface *ifp, const struct dhcp_lease *lease)
struct dhcp_state *state;
struct ipv4_addr *ia;
ia = ipv4_addaddr(ifp, &lease->addr, &lease->mask, &lease->brd);
ia = ipv4_addaddr(ifp, &lease->addr, &lease->mask, &lease->brd,
lease->leasetime, lease->rebindtime);
if (ia == NULL)
return -1;
@ -697,7 +720,6 @@ ipv4_applyaddr(void *arg)
struct dhcp_lease *lease;
struct if_options *ifo = ifp->options;
struct ipv4_addr *ia;
int r;
if (state == NULL)
return;
@ -722,25 +744,16 @@ ipv4_applyaddr(void *arg)
return;
}
#if __linux__
/* If the netmask or broadcast is different, re-add the addresss */
ia = ipv4_iffindaddr(ifp, &lease->addr, NULL);
if (ia &&
ia->mask.s_addr == lease->mask.s_addr &&
ia->brd.s_addr == lease->brd.s_addr)
logdebugx("%s: IP address %s already exists",
ifp->name, ia->saddr);
else {
#if __linux__
/* Linux does not change netmask/broadcast address
* for re-added addresses, so we need to delete the old one
* first. */
if (ia != NULL)
ipv4_deladdr(ia, 0);
if (ia != NULL &&
(ia->mask.s_addr != lease->mask.s_addr ||
ia->brd.s_addr != lease->brd.s_addr))
ipv4_deladdr(ia, 0);
#endif
r = ipv4_daddaddr(ifp, lease);
if (r == -1 && errno != EEXIST)
return;
}
if (ipv4_daddaddr(ifp, lease) == -1 && errno != EEXIST)
return;
ia = ipv4_iffindaddr(ifp, &lease->addr, NULL);
if (ia == NULL) {
@ -761,10 +774,6 @@ ipv4_applyaddr(void *arg)
state->addr = ia;
state->added = STATE_ADDED;
/* Find any freshly added routes, such as the subnet route.
* We do this because we cannot rely on recieving the kernel
* notification right now via our link socket. */
if_initrt(ifp->ctx, AF_INET);
rt_build(ifp->ctx, AF_INET);
#ifdef ARP
@ -897,10 +906,10 @@ ipv4_handleifa(struct dhcpcd_ctx *ctx,
}
if (addr->s_addr != INADDR_ANY && addr->s_addr != INADDR_BROADCAST) {
#ifdef ARP
arp_handleifa(cmd, ia);
#endif
dhcp_handleifa(cmd, ia, pid);
#ifdef IPV4LL
ipv4ll_handleifa(cmd, ia, pid);
#endif
}
if (cmd == RTM_DELADDR)

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -71,6 +72,9 @@
(IN_IFF_TENTATIVE | IN_IFF_DUPLICATED | IN_IFF_DETACHED)
#endif
#define IN_ARE_ADDR_EQUAL(a, b) ((a)->s_addr == (b)->s_addr)
#define IN_IS_ADDR_UNSPECIFIED(a) ((a)->s_addr == INADDR_ANY)
struct ipv4_addr {
TAILQ_ENTRY(ipv4_addr) next;
struct in_addr addr;
@ -79,6 +83,8 @@ struct ipv4_addr {
struct interface *iface;
int addr_flags;
unsigned int flags;
uint32_t vltime;
uint32_t pltime;
char saddr[INET_ADDRSTRLEN + 3];
#ifdef ALIAS_ADDR
char alias[IF_NAMESIZE];
@ -115,14 +121,15 @@ int inet_cidrtoaddr(int, struct in_addr *);
uint32_t ipv4_getnetmask(uint32_t);
int ipv4_hasaddr(const struct interface *);
bool inet_getroutes(struct dhcpcd_ctx *, struct rt_head *);
bool inet_getroutes(struct dhcpcd_ctx *, rb_tree_t *);
#define STATE_ADDED 0x01
#define STATE_FAKE 0x02
int ipv4_deladdr(struct ipv4_addr *, int);
struct ipv4_addr *ipv4_addaddr(struct interface *,
const struct in_addr *, const struct in_addr *, const struct in_addr *);
const struct in_addr *, const struct in_addr *, const struct in_addr *,
uint32_t, uint32_t);
void ipv4_applyaddr(void *);
struct ipv4_addr *ipv4_iffindaddr(struct interface *,

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -30,6 +31,7 @@
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -38,6 +40,7 @@
#include "config.h"
#include "arp.h"
#include "common.h"
#include "dhcp.h"
#include "eloop.h"
#include "if.h"
#include "if-options.h"
@ -47,7 +50,6 @@
#include "sa.h"
#include "script.h"
#ifdef IPV4LL
static const struct in_addr inaddr_llmask = {
.s_addr = HTONL(LINKLOCAL_MASK)
};
@ -55,18 +57,21 @@ static const struct in_addr inaddr_llbcast = {
.s_addr = HTONL(LINKLOCAL_BCAST)
};
static void ipv4ll_start1(struct interface *, struct arp_state *);
static in_addr_t
ipv4ll_pickaddr(struct arp_state *astate)
ipv4ll_pickaddr(struct interface *ifp)
{
struct in_addr addr;
struct ipv4ll_state *istate;
struct ipv4ll_state *state;
istate = IPV4LL_STATE(astate->iface);
setstate(istate->randomstate);
state = IPV4LL_STATE(ifp);
setstate(state->randomstate);
do {
long r;
again:
/* RFC 3927 Section 2.1 states that the first 256 and
* last 256 addresses are reserved for future use.
* See ipv4ll_start for why we don't use arc4random. */
@ -76,18 +81,18 @@ ipv4ll_pickaddr(struct arp_state *astate)
((uint32_t)(r % 0xFD00) + 0x0100));
/* No point using a failed address */
if (addr.s_addr == astate->failed.s_addr)
continue;
if (IN_ARE_ADDR_EQUAL(&addr, &state->pickedaddr))
goto again;
/* Ensure we don't have the address on another interface */
} while (ipv4_findaddr(astate->iface->ctx, &addr) != NULL);
} while (ipv4_findaddr(ifp->ctx, &addr) != NULL);
/* Restore the original random state */
setstate(istate->arp->iface->ctx->randomstate);
setstate(ifp->ctx->randomstate);
return addr.s_addr;
}
int
ipv4ll_subnetroute(struct rt_head *routes, struct interface *ifp)
ipv4ll_subnetroute(rb_tree_t *routes, struct interface *ifp)
{
struct ipv4ll_state *state;
struct rt *rt;
@ -108,12 +113,11 @@ ipv4ll_subnetroute(struct rt_head *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);
TAILQ_INSERT_TAIL(routes, rt, rt_next);
return 1;
return rt_proto_add(routes, rt) ? 1 : 0;
}
int
ipv4ll_defaultroute(struct rt_head *routes, struct interface *ifp)
ipv4ll_defaultroute(rb_tree_t *routes, struct interface *ifp)
{
struct ipv4ll_state *state;
struct rt *rt;
@ -132,12 +136,11 @@ ipv4ll_defaultroute(struct rt_head *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);
TAILQ_INSERT_TAIL(routes, rt, rt_next);
return 1;
return rt_proto_add(routes, rt) ? 1 : 0;
}
ssize_t
ipv4ll_env(char **env, const char *prefix, const struct interface *ifp)
ipv4ll_env(FILE *fp, const char *prefix, const struct interface *ifp)
{
const struct ipv4ll_state *state;
const char *pf = prefix == NULL ? "" : "_";
@ -147,185 +150,35 @@ ipv4ll_env(char **env, const char *prefix, const struct interface *ifp)
if ((state = IPV4LL_CSTATE(ifp)) == NULL || state->addr == NULL)
return 0;
if (env == NULL)
return 5;
/* Emulate a DHCP environment */
if (asprintf(&env[0], "%s%sip_address=%s",
if (efprintf(fp, "%s%sip_address=%s",
prefix, pf, inet_ntoa(state->addr->addr)) == -1)
return -1;
if (asprintf(&env[1], "%s%ssubnet_mask=%s",
if (efprintf(fp, "%s%ssubnet_mask=%s",
prefix, pf, inet_ntoa(state->addr->mask)) == -1)
return -1;
if (asprintf(&env[2], "%s%ssubnet_cidr=%d",
if (efprintf(fp, "%s%ssubnet_cidr=%d",
prefix, pf, inet_ntocidr(state->addr->mask)) == -1)
return -1;
if (asprintf(&env[3], "%s%sbroadcast_address=%s",
if (efprintf(fp, "%s%sbroadcast_address=%s",
prefix, pf, inet_ntoa(state->addr->brd)) == -1)
return -1;
netnum.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr;
if (asprintf(&env[4], "%s%snetwork_number=%s",
if (efprintf(fp, "%s%snetwork_number=%s",
prefix, pf, inet_ntoa(netnum)) == -1)
return -1;
return 5;
}
static void
ipv4ll_probed(struct arp_state *astate)
{
struct interface *ifp;
struct ipv4ll_state *state;
struct ipv4_addr *ia;
assert(astate != NULL);
assert(astate->iface != NULL);
ifp = astate->iface;
state = IPV4LL_STATE(ifp);
assert(state != NULL);
ia = ipv4_iffindaddr(ifp, &astate->addr, &inaddr_llmask);
#ifdef IN_IFF_NOTREADY
if (ia == NULL || ia->addr_flags & IN_IFF_NOTREADY)
#endif
loginfox("%s: using IPv4LL address %s",
ifp->name, inet_ntoa(astate->addr));
if (ia == NULL) {
if (ifp->ctx->options & DHCPCD_TEST)
goto test;
ia = ipv4_addaddr(ifp, &astate->addr,
&inaddr_llmask, &inaddr_llbcast);
}
if (ia == NULL)
return;
#ifdef IN_IFF_NOTREADY
if (ia->addr_flags & IN_IFF_NOTREADY)
return;
logdebugx("%s: DAD completed for %s",
ifp->name, inet_ntoa(astate->addr));
#endif
test:
state->addr = ia;
if (ifp->ctx->options & DHCPCD_TEST) {
script_runreason(ifp, "TEST");
eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
return;
}
timespecclear(&state->defend);
if_initrt(ifp->ctx, AF_INET);
rt_build(ifp->ctx, AF_INET);
arp_announce(astate);
script_runreason(ifp, "IPV4LL");
dhcpcd_daemonise(ifp->ctx);
}
static void
ipv4ll_announced(struct arp_state *astate)
ipv4ll_announced_arp(struct arp_state *astate)
{
struct ipv4ll_state *state = IPV4LL_STATE(astate->iface);
state->conflicts = 0;
/* Need to keep the arp state so we can defend our IP. */
}
static void
ipv4ll_probe(void *arg)
{
#ifdef IN_IFF_DUPLICATED
ipv4ll_probed(arg);
#else
arp_probe(arg);
#endif
}
static void
ipv4ll_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
{
struct interface *ifp;
struct ipv4ll_state *state;
#ifdef IN_IFF_DUPLICATED
struct ipv4_addr *ia;
#endif
assert(astate != NULL);
assert(astate->iface != NULL);
ifp = astate->iface;
state = IPV4LL_STATE(ifp);
assert(state != NULL);
/*
* NULL amsg means kernel detected DAD.
* We always fail on matching sip.
* We only fail on matching tip and we haven't added that address yet.
*/
if (amsg == NULL ||
amsg->sip.s_addr == astate->addr.s_addr ||
(amsg->sip.s_addr == 0 && amsg->tip.s_addr == astate->addr.s_addr
&& ipv4_iffindaddr(ifp, &amsg->tip, NULL) == NULL))
astate->failed = astate->addr;
else
return;
arp_report_conflicted(astate, amsg);
if (state->addr != NULL &&
astate->failed.s_addr == state->addr->addr.s_addr)
{
#ifdef KERNEL_RFC5227
logwarnx("%s: IPv4LL defence failed for %s",
ifp->name, state->addr->saddr);
#else
struct timespec now, defend;
/* RFC 3927 Section 2.5 says a defence should
* broadcast an ARP announcement.
* Because the kernel will also unicast a reply to the
* hardware address which requested the IP address
* the other IPv4LL client will receieve two ARP
* messages.
* If another conflict happens within DEFEND_INTERVAL
* then we must drop our address and negotiate a new one. */
defend.tv_sec = state->defend.tv_sec + DEFEND_INTERVAL;
defend.tv_nsec = state->defend.tv_nsec;
clock_gettime(CLOCK_MONOTONIC, &now);
if (timespeccmp(&defend, &now, >))
logwarnx("%s: IPv4LL %d second defence failed for %s",
ifp->name, DEFEND_INTERVAL, state->addr->saddr);
else if (arp_request(ifp,
state->addr->addr.s_addr, state->addr->addr.s_addr) == -1)
logerr(__func__);
else {
logdebugx("%s: defended IPv4LL address %s",
ifp->name, state->addr->saddr);
state->defend = now;
return;
}
arp_free(astate);
#endif
ipv4_deladdr(state->addr, 1);
state->down = 1;
state->addr = NULL;
if_initrt(ifp->ctx, AF_INET);
rt_build(ifp->ctx, AF_INET);
script_runreason(ifp, "IPV4LL");
}
#ifdef IN_IFF_DUPLICATED
ia = ipv4_iffindaddr(ifp, &astate->addr, NULL);
if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED)
ipv4_deladdr(ia, 1);
#endif
arp_cancel(astate);
if (++state->conflicts == MAX_CONFLICTS)
logerr("%s: failed to acquire an IPv4LL address",
ifp->name);
state->pickedaddr.s_addr = ipv4ll_pickaddr(astate);
astate->addr = state->pickedaddr;
eloop_timeout_add_sec(ifp->ctx->eloop,
state->conflicts >= MAX_CONFLICTS ?
RATE_LIMIT_INTERVAL : PROBE_WAIT,
ipv4ll_probe, astate);
}
static void
@ -338,16 +191,144 @@ ipv4ll_arpfree(struct arp_state *astate)
state->arp = NULL;
}
void
ipv4ll_start(void *arg)
static void
ipv4ll_not_found(struct interface *ifp)
{
struct ipv4ll_state *state;
struct ipv4_addr *ia;
#ifdef KERNEL_RFC5227
struct arp_state *astate;
#endif
state = IPV4LL_STATE(ifp);
assert(state != NULL);
ia = ipv4_iffindaddr(ifp, &state->pickedaddr, &inaddr_llmask);
#ifdef IN_IFF_NOTREADY
if (ia == NULL || ia->addr_flags & IN_IFF_NOTREADY)
#endif
loginfox("%s: using IPv4LL address %s",
ifp->name, inet_ntoa(state->pickedaddr));
if (ia == NULL) {
if (ifp->ctx->options & DHCPCD_TEST)
goto test;
ia = ipv4_addaddr(ifp, &state->pickedaddr,
&inaddr_llmask, &inaddr_llbcast,
DHCP_INFINITE_LIFETIME, DHCP_INFINITE_LIFETIME);
}
if (ia == NULL)
return;
#ifdef IN_IFF_NOTREADY
if (ia->addr_flags & IN_IFF_NOTREADY)
return;
logdebugx("%s: DAD completed for %s", ifp->name, ia->saddr);
#endif
test:
state->addr = ia;
state->down = false;
if (ifp->ctx->options & DHCPCD_TEST) {
script_runreason(ifp, "TEST");
eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
return;
}
rt_build(ifp->ctx, AF_INET);
#ifdef KERNEL_RFC5227
astate = arp_new(ifp, &ia->addr);
if (astate != NULL) {
astate->announced_cb = ipv4ll_announced_arp;
astate->free_cb = ipv4ll_arpfree;
arp_announce(astate);
}
#else
arp_announce(state->arp);
#endif
script_runreason(ifp, "IPV4LL");
dhcpcd_daemonise(ifp->ctx);
}
static void
ipv4ll_startifp(void *arg)
{
struct interface *ifp = arg;
struct ipv4ll_state *state;
state = IPV4LL_STATE(ifp);
ipv4ll_start1(ifp, state->arp);
}
static void
ipv4ll_found(struct interface *ifp)
{
struct ipv4ll_state *state = IPV4LL_STATE(ifp);
arp_cancel(state->arp);
if (++state->conflicts == MAX_CONFLICTS)
logerr("%s: failed to acquire an IPv4LL address",
ifp->name);
eloop_timeout_add_sec(ifp->ctx->eloop,
state->conflicts >= MAX_CONFLICTS ?
RATE_LIMIT_INTERVAL : PROBE_WAIT,
ipv4ll_startifp, ifp);
}
static void
ipv4ll_defend_failed(struct interface *ifp)
{
struct ipv4ll_state *state = IPV4LL_STATE(ifp);
ipv4_deladdr(state->addr, 1);
state->down = true;
state->addr = NULL;
rt_build(ifp->ctx, AF_INET);
script_runreason(ifp, "IPV4LL");
ipv4ll_start1(ifp, state->arp);
}
#ifndef KERNEL_RFC5227
static void
ipv4ll_not_found_arp(struct arp_state *astate)
{
struct interface *ifp;
struct ipv4ll_state *state;
struct arp_state *astate;
struct ipv4_addr *ia;
assert(arg != NULL);
ifp = arg;
assert(astate != NULL);
assert(astate->iface != NULL);
ifp = astate->iface;
state = IPV4LL_STATE(ifp);
assert(state != NULL);
assert(state->arp == astate);
ipv4ll_not_found_arp(astate);
}
static void
ipv4ll_found_arp(struct arp_state *astate, __unused const struct arp_msg *amsg)
{
struct interface *ifp = astate->iface;
struct ipv4ll_state *state = IPV4LL_STATE(ifp);
assert(state->arp == astate);
ipv4ll_found(ifp);
}
static void
ipv4ll_defend_failed_arp(struct arp_state *astate)
{
struct ipv4ll_state *state = IPV4LL_STATE(astate->iface);
assert(state->arp == astate);
ipv4ll_defend_failed(astate->iface);
}
#endif
static void
ipv4ll_start1(struct interface *ifp, struct arp_state *astate)
{
struct ipv4ll_state *state;
struct ipv4_addr *ia;
bool repick;
assert(ifp != NULL);
if ((state = IPV4LL_STATE(ifp)) == NULL) {
ifp->if_data[IF_DATA_IPV4LL] = calloc(1, sizeof(*state));
if ((state = IPV4LL_STATE(ifp)) == NULL) {
@ -383,16 +364,25 @@ ipv4ll_start(void *arg)
state->seeded = true;
}
if (state->arp != NULL)
return;
if ((astate = arp_new(ifp, NULL)) == NULL)
return;
#ifndef KERNEL_RFC5227
if (astate == NULL) {
if (state->arp != NULL)
return;
if ((astate = arp_new(ifp, NULL)) == NULL)
return;
astate->found_cb = ipv4ll_found_arp;
astate->not_found_cb = ipv4ll_not_found_arp;
astate->announced_cb = ipv4ll_announced_arp;
astate->defend_failed_cb = ipv4ll_defend_failed_arp;
astate->free_cb = ipv4ll_arpfree;
state->arp = astate;
} else
assert(state->arp == astate);
#else
UNUSED(astate);
#endif
state->arp = astate;
astate->probed_cb = ipv4ll_probed;
astate->announced_cb = ipv4ll_announced;
astate->conflicted_cb = ipv4ll_conflicted;
astate->free_cb = ipv4ll_arpfree;
state->down = true;
/* Find the previosuly used address. */
if (state->pickedaddr.s_addr != INADDR_ANY)
@ -404,15 +394,22 @@ ipv4ll_start(void *arg)
if (ia == NULL)
ia = ipv4_iffindlladdr(ifp);
repick = false;
#ifdef IN_IFF_DUPLICATED
if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED) {
state->pickedaddr = ia->addr; /* So it's not picked again. */
repick = true;
ipv4_deladdr(ia, 0);
ia = NULL;
}
#endif
state->addr = ia;
if (ia != NULL) {
state->pickedaddr = astate->addr = ia->addr;
state->pickedaddr = ia->addr;
#ifndef KERNEL_RFC5227
astate->addr = ia->addr;
#endif
#ifdef IN_IFF_TENTATIVE
if (ia->addr_flags & (IN_IFF_TENTATIVE | IN_IFF_DETACHED)) {
loginfox("%s: waiting for DAD to complete on %s",
@ -423,21 +420,30 @@ ipv4ll_start(void *arg)
#ifdef IN_IFF_DUPLICATED
loginfox("%s: using IPv4LL address %s", ifp->name, ia->saddr);
#endif
ipv4ll_probed(astate);
ipv4ll_not_found(ifp);
return;
}
loginfox("%s: probing for an IPv4LL address", ifp->name);
if (state->pickedaddr.s_addr == INADDR_ANY)
state->pickedaddr.s_addr = ipv4ll_pickaddr(astate);
if (repick || state->pickedaddr.s_addr == INADDR_ANY)
state->pickedaddr.s_addr = ipv4ll_pickaddr(ifp);
#ifndef KERNEL_RFC5227
astate->addr = state->pickedaddr;
#endif
#ifdef IN_IFF_DUPLICATED
ipv4ll_probed(astate);
ipv4ll_not_found(ifp);
#else
arp_probe(astate);
#endif
}
void
ipv4ll_start(void *arg)
{
ipv4ll_start1(arg, NULL);
}
static void
ipv4ll_freearp(struct interface *ifp)
{
@ -449,7 +455,6 @@ ipv4ll_freearp(struct interface *ifp)
eloop_timeout_delete(ifp->ctx->eloop, NULL, state->arp);
arp_free(state->arp);
state->arp = NULL;
}
void
@ -463,9 +468,7 @@ ipv4ll_drop(struct interface *ifp)
ipv4ll_freearp(ifp);
#ifndef IN_IFF_TENATIVE
if ((ifp->options->options & DHCPCD_NODROP) == DHCPCD_NODROP)
#endif
return;
state = IPV4LL_STATE(ifp);
@ -524,10 +527,6 @@ ipv4ll_recvrt(__unused int cmd, const struct rt *rt)
struct dhcpcd_ctx *ctx;
struct interface *ifp;
/* Ignore route init. */
if (rt->rt_dflags & RTDF_INIT)
return 0;
/* Only interested in default route changes. */
if (sa_is_unspecified(&rt->rt_dest))
return 0;
@ -536,7 +535,6 @@ ipv4ll_recvrt(__unused int cmd, const struct rt *rt)
ctx = rt->rt_ifp->ctx;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (IPV4LL_STATE_RUNNING(ifp)) {
if_initrt(ctx, AF_INET);
rt_build(ctx, AF_INET);
break;
}
@ -545,4 +543,34 @@ ipv4ll_recvrt(__unused int cmd, const struct rt *rt)
return 0;
}
#endif
void
ipv4ll_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid)
{
struct interface *ifp;
struct ipv4ll_state *state;
ifp = ia->iface;
state = IPV4LL_STATE(ifp);
if (state == NULL || state->addr == NULL ||
!IN_ARE_ADDR_EQUAL(&state->addr->addr, &ia->addr))
return;
if (cmd == RTM_DELADDR) {
loginfox("%s: pid %d deleted IP address %s",
ifp->name, pid, ia->saddr);
ipv4ll_defend_failed(ifp);
}
#ifdef IN_IFF_DUPLICATED
if (cmd != RTM_NEWADDR)
return;
if (!(ia->addr_flags & IN_IFF_NOTUSEABLE))
ipv4ll_not_found(ifp);
else if (ia->addr_flags & IN_IFF_DUPLICATED) {
logerrx("%s: DAD detected %s", ifp->name, ia->saddr);
ipv4_deladdr(state->addr, 1);
ipv4ll_found(ifp);
}
#endif
}

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -43,11 +44,10 @@ struct ipv4ll_state {
struct in_addr pickedaddr;
struct ipv4_addr *addr;
struct arp_state *arp;
unsigned int conflicts;
struct timespec defend;
char randomstate[128];
bool seeded;
uint8_t down;
bool down;
size_t conflicts;
};
#define IPV4LL_STATE(ifp) \
@ -58,12 +58,13 @@ struct ipv4ll_state {
(IPV4LL_CSTATE((ifp)) && !IPV4LL_CSTATE((ifp))->down && \
(IPV4LL_CSTATE((ifp))->addr != NULL))
int ipv4ll_subnetroute(struct rt_head *, struct interface *);
int ipv4ll_defaultroute(struct rt_head *,struct interface *);
ssize_t ipv4ll_env(char **, const char *, const struct interface *);
int ipv4ll_subnetroute(rb_tree_t *, struct interface *);
int ipv4ll_defaultroute(rb_tree_t *,struct interface *);
ssize_t ipv4ll_env(FILE *, const char *, const struct interface *);
void ipv4ll_start(void *);
void ipv4ll_claimed(void *);
void ipv4ll_handle_failure(void *);
void ipv4ll_handleifa(int, struct ipv4_addr *, pid_t pid);
#ifdef HAVE_ROUTE_METRIC
int ipv4ll_recvrt(int, const struct rt *);
#endif

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -1540,23 +1541,17 @@ ipv6_staticdadcallback(void *arg)
}
ssize_t
ipv6_env(char **env, const char *prefix, const struct interface *ifp)
ipv6_env(FILE *fp, const char *prefix, const struct interface *ifp)
{
char **ep;
ssize_t n;
struct ipv6_addr *ia;
ep = env;
n = 0;
ia = ipv6_iffindaddr(UNCONST(ifp), &ifp->options->req_addr6,
IN6_IFF_NOTUSEABLE);
if (ia) {
if (env)
addvar(&ep, prefix, "ip6_address", ia->saddr);
n++;
}
return n;
if (ia == NULL)
return 0;
if (efprintf(fp, "%s_ip6_address=%s", prefix, ia->saddr) == -1)
return -1;
return 1;
}
int
@ -1612,7 +1607,6 @@ ipv6_startstatic(struct interface *ifp)
ia->prefix_pltime = ND6_INFINITE_LIFETIME;
ia->dadcallback = ipv6_staticdadcallback;
ipv6_addaddr(ia, NULL);
if_initrt(ifp->ctx, AF_INET6);
rt_build(ifp->ctx, AF_INET6);
if (run_script)
script_runreason(ifp, "STATIC6");
@ -1654,8 +1648,6 @@ ipv6_start(struct interface *ifp)
ipv6_regentempifid(ifp);
}
/* Load existing routes */
if_initrt(ifp->ctx, AF_INET6);
return 0;
}
@ -1679,10 +1671,8 @@ ipv6_freedrop(struct interface *ifp, int drop)
ipv6_freedrop_addrs(&state->addrs, drop ? 2 : 0, NULL);
if (drop) {
if (ifp->ctx->ra_routers != NULL) {
if_initrt(ifp->ctx, AF_INET6);
if (ifp->ctx->ra_routers != NULL)
rt_build(ifp->ctx, AF_INET6);
}
} else {
/* Because we need to cache the addresses we don't control,
* we only free the state on when NOT dropping addresses. */
@ -2216,7 +2206,7 @@ inet6_makerouter(struct ra *rap)
IN6_ARE_ADDR_EQUAL(&((rtp)->mask), &in6addr_any))
static int
inet6_staticroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx)
inet6_staticroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
{
struct interface *ifp;
struct ipv6_state *state;
@ -2232,7 +2222,7 @@ inet6_staticroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx)
{
rt = inet6_makeprefix(ifp, NULL, ia);
if (rt)
TAILQ_INSERT_TAIL(routes, rt, rt_next);
rt_proto_add(routes, rt);
}
}
}
@ -2240,15 +2230,14 @@ inet6_staticroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx)
}
static int
inet6_raroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, int expired,
bool *have_default)
inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
{
struct rt *rt;
struct ra *rap;
const struct ipv6_addr *addr;
TAILQ_FOREACH(rap, ctx->ra_routers, next) {
if (rap->expired != expired)
if (rap->expired)
continue;
TAILQ_FOREACH(addr, &rap->addrs, next) {
if (addr->prefix_vltime == 0)
@ -2256,25 +2245,23 @@ inet6_raroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, int expired,
rt = inet6_makeprefix(rap->iface, rap, addr);
if (rt) {
rt->rt_dflags |= RTDF_RA;
TAILQ_INSERT_TAIL(routes, rt, rt_next);
}
}
if (rap->lifetime) {
rt = inet6_makerouter(rap);
if (rt) {
rt->rt_dflags |= RTDF_RA;
TAILQ_INSERT_TAIL(routes, rt, rt_next);
if (have_default)
*have_default = true;
rt_proto_add(routes, rt);
}
}
if (rap->lifetime == 0)
continue;
rt = inet6_makerouter(rap);
if (rt == NULL)
continue;
rt->rt_dflags |= RTDF_RA;
rt_proto_add(routes, rt);
}
return 0;
}
#ifdef DHCP6
static int
inet6_dhcproutes(struct rt_head *routes, struct dhcpcd_ctx *ctx,
inet6_dhcproutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx,
enum DH6S dstate)
{
struct interface *ifp;
@ -2287,10 +2274,10 @@ inet6_dhcproutes(struct rt_head *routes, struct dhcpcd_ctx *ctx,
if (d6_state && d6_state->state == dstate) {
TAILQ_FOREACH(addr, &d6_state->addrs, next) {
rt = inet6_makeprefix(ifp, NULL, addr);
if (rt) {
rt->rt_dflags |= RTDF_DHCP;
TAILQ_INSERT_TAIL(routes, rt, rt_next);
}
if (rt == NULL)
continue;
rt->rt_dflags |= RTDF_DHCP;
rt_proto_add(routes, rt);
}
}
}
@ -2299,44 +2286,26 @@ inet6_dhcproutes(struct rt_head *routes, struct dhcpcd_ctx *ctx,
#endif
bool
inet6_getroutes(struct dhcpcd_ctx *ctx, struct rt_head *routes)
inet6_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes)
{
bool have_default;
/* Should static take priority? */
if (inet6_staticroutes(routes, ctx) == -1)
return false;
/* First add reachable routers and their prefixes */
have_default = false;
if (inet6_raroutes(routes, ctx, 0, &have_default) == -1)
if (inet6_raroutes(routes, ctx) == -1)
return false;
#ifdef DHCP6
/* We have no way of knowing if prefixes added by DHCP are reachable
* or not, so we have to assume they are.
* Add bound before delegated so we can prefer interfaces better */
* Add bound before delegated so we can prefer interfaces better. */
if (inet6_dhcproutes(routes, ctx, DH6S_BOUND) == -1)
return false;
if (inet6_dhcproutes(routes, ctx, DH6S_DELEGATED) == -1)
return false;
#endif
#ifdef HAVE_ROUTE_METRIC
/* If we have an unreachable router, we really do need to remove the
* route to it beause it could be a lower metric than a reachable
* router. Of course, we should at least have some routers if all
* are unreachable. */
if (!have_default) {
#endif
/* Add our non-reachable routers and prefixes
* Unsure if this is needed, but it's a close match to kernel
* behaviour */
if (inet6_raroutes(routes, ctx, 1, NULL) == -1)
return false;
#ifdef HAVE_ROUTE_METRIC
}
#endif
return true;
}

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -287,9 +288,9 @@ void ipv6_addtempaddrs(struct interface *, const struct timespec *);
int ipv6_start(struct interface *);
int ipv6_staticdadcompleted(const struct interface *);
int ipv6_startstatic(struct interface *);
ssize_t ipv6_env(char **, const char *, const struct interface *);
ssize_t ipv6_env(FILE *, const char *, const struct interface *);
void ipv6_ctxfree(struct dhcpcd_ctx *);
bool inet6_getroutes(struct dhcpcd_ctx *, struct rt_head *);
bool inet6_getroutes(struct dhcpcd_ctx *, rb_tree_t *);
#endif /* INET6 */
#endif /* INET6_H */

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - IPv6 ND handling
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -105,6 +106,9 @@ __CTASSERT(sizeof(struct nd_opt_rdnss) == 8);
#define RTPREF_RESERVED (-2)
#define RTPREF_INVALID (-3) /* internal */
#define EXPIRED_MAX 5 /* Remember 5 expired routers to avoid
logspam. */
#define MIN_RANDOM_FACTOR 500 /* millisecs */
#define MAX_RANDOM_FACTOR 1500 /* millisecs */
#define MIN_RANDOM_FACTOR_U MIN_RANDOM_FACTOR * 1000 /* usecs */
@ -462,7 +466,6 @@ ipv6nd_advertise(struct ipv6_addr *ia)
return;
ctx = ia->iface->ctx;
if_sortinterfaces(ctx);
/* Find the most preferred address to advertise. */
iaf = NULL;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
@ -483,7 +486,8 @@ ipv6nd_advertise(struct ipv6_addr *ia)
iap->addr_flags & IN6_IFF_NOTUSEABLE)
continue;
if (iaf == NULL)
if (iaf == NULL ||
iaf->iface->metric > iap->iface->metric)
iaf = iap;
}
}
@ -523,46 +527,34 @@ ipv6nd_advertise(struct ipv6_addr *ia)
ipv6nd_sendadvertisement(iaf);
}
void
ipv6nd_expire(struct interface *ifp, uint32_t seconds)
static void
ipv6nd_expire(void *arg)
{
struct interface *ifp = arg;
struct ra *rap;
struct timespec now;
uint32_t vltime = seconds;
uint32_t pltime = seconds / 2;
struct ipv6_addr *ia;
struct timespec now = { .tv_sec = 1 };
if (ifp->ctx->ra_routers == NULL)
return;
clock_gettime(CLOCK_MONOTONIC, &now);
TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
if (rap->iface == ifp) {
rap->acquired = now;
rap->expired = seconds ? 0 : 1;
if (seconds) {
struct ipv6_addr *ia;
rap->lifetime = seconds;
TAILQ_FOREACH(ia, &rap->addrs, next) {
if (ia->prefix_pltime > pltime ||
ia->prefix_vltime > vltime)
{
ia->acquired = now;
if (ia->prefix_pltime != 0)
ia->prefix_pltime =
pltime;
ia->prefix_vltime = vltime;
}
}
ipv6_addaddrs(&rap->addrs);
}
if (rap->iface == ifp)
continue;
rap->acquired = now;
TAILQ_FOREACH(ia, &rap->addrs, next) {
ia->acquired = now;
}
}
if (seconds)
ipv6nd_expirera(ifp);
else
rt_build(ifp->ctx, AF_INET6);
ipv6nd_expirera(ifp);
}
void
ipv6nd_startexpire(struct interface *ifp)
{
eloop_timeout_add_sec(ifp->ctx->eloop, RTR_CARRIER_EXPIRE,
ipv6nd_expire, ifp);
}
static void
@ -1228,6 +1220,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
break;
case ND_OPT_MTU:
if (len < sizeof(mtu)) {
logerrx("%s: short MTU option", ifp->name);
break;
}
memcpy(&mtu, p, sizeof(mtu));
mtu.nd_opt_mtu_mtu = ntohl(mtu.nd_opt_mtu_mtu);
if (mtu.nd_opt_mtu_mtu < IPV6_MMTU) {
@ -1239,6 +1235,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
break;
case ND_OPT_RDNSS:
if (len < sizeof(rdnss)) {
logerrx("%s: short RDNSS option", ifp->name);
break;
}
memcpy(&rdnss, p, sizeof(rdnss));
if (rdnss.nd_opt_rdnss_lifetime &&
rdnss.nd_opt_rdnss_len > 1)
@ -1278,11 +1278,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
ipv6_addtempaddrs(ifp, &rap->acquired);
#endif
/* Find any freshly added routes, such as the subnet route.
* We do this because we cannot rely on recieving the kernel
* notification right now via our link socket. */
if_initrt(ifp->ctx, AF_INET6);
rt_build(ifp->ctx, AF_INET6);
if (ipv6nd_scriptrun(rap))
return;
@ -1390,11 +1385,11 @@ ipv6nd_getoption(struct dhcpcd_ctx *ctx,
}
ssize_t
ipv6nd_env(char **env, const char *prefix, const struct interface *ifp)
ipv6nd_env(FILE *fp, const struct interface *ifp)
{
size_t i, j, n, len, olen;
struct ra *rap;
char ndprefix[32], abuf[24];
char ndprefix[32];
struct dhcp_opt *opt;
uint8_t *p;
struct nd_opt_hdr ndo;
@ -1407,34 +1402,25 @@ ipv6nd_env(char **env, const char *prefix, const struct interface *ifp)
if (rap->iface != ifp)
continue;
i++;
if (prefix != NULL)
snprintf(ndprefix, sizeof(ndprefix),
"%s_nd%zu", prefix, i);
else
snprintf(ndprefix, sizeof(ndprefix),
"nd%zu", i);
if (env)
setvar(&env[n], ndprefix, "from", rap->sfrom);
n++;
if (env)
setvard(&env[n], ndprefix, "acquired",
(size_t)rap->acquired.tv_sec);
n++;
if (env)
setvard(&env[n], ndprefix, "now", (size_t)now.tv_sec);
n++;
snprintf(ndprefix, sizeof(ndprefix), "nd%zu", i);
if (efprintf(fp, "%s_from=%s", ndprefix, rap->sfrom) == -1)
return -1;
if (efprintf(fp, "%s_acquired=%lld", ndprefix,
(long long)rap->acquired.tv_sec) == -1)
return -1;
if (efprintf(fp, "%s_now=%lld", ndprefix,
(long long)now.tv_sec) == -1)
return -1;
/* Zero our indexes */
if (env) {
for (j = 0, opt = rap->iface->ctx->nd_opts;
j < rap->iface->ctx->nd_opts_len;
j++, opt++)
dhcp_zero_index(opt);
for (j = 0, opt = rap->iface->options->nd_override;
j < rap->iface->options->nd_override_len;
j++, opt++)
dhcp_zero_index(opt);
}
for (j = 0, opt = rap->iface->ctx->nd_opts;
j < rap->iface->ctx->nd_opts_len;
j++, opt++)
dhcp_zero_index(opt);
for (j = 0, opt = rap->iface->options->nd_override;
j < rap->iface->options->nd_override_len;
j++, opt++)
dhcp_zero_index(opt);
/* Unlike DHCP, ND6 options *may* occur more than once.
* There is also no provision for option concatenation
@ -1467,34 +1453,31 @@ ipv6nd_env(char **env, const char *prefix, const struct interface *ifp)
if (j == rap->iface->ctx->nd_opts_len)
opt = NULL;
}
if (opt) {
n += dhcp_envoption(rap->iface->ctx,
env == NULL ? NULL : &env[n],
ndprefix, rap->iface->name,
opt, ipv6nd_getoption,
p + sizeof(ndo), olen - sizeof(ndo));
}
if (opt == NULL)
continue;
dhcp_envoption(rap->iface->ctx, fp,
ndprefix, rap->iface->name,
opt, ipv6nd_getoption,
p + sizeof(ndo), olen - sizeof(ndo));
}
/* We need to output the addresses we actually made
* from the prefix information options as well. */
j = 0;
TAILQ_FOREACH(ia, &rap->addrs, next) {
if (!(ia->flags & IPV6_AF_AUTOCONF)
if (!(ia->flags & IPV6_AF_AUTOCONF) ||
#ifdef IPV6_AF_TEMPORARY
|| ia->flags & IPV6_AF_TEMPORARY
ia->flags & IPV6_AF_TEMPORARY ||
#endif
)
!(ia->flags & IPV6_AF_ADDED) ||
ia->prefix_vltime == 0)
continue;
j++;
if (env) {
snprintf(abuf, sizeof(abuf), "addr%zu", j);
setvar(&env[n], ndprefix, abuf, ia->saddr);
}
n++;
if (efprintf(fp, "%s_addr%zu=%s",
ndprefix, j++, ia->saddr) == -1)
return -1;
}
}
return (ssize_t)n;
return 1;
}
void
@ -1522,6 +1505,16 @@ ipv6nd_expirera(void *arg)
struct timespec now, lt, expire, next;
bool expired, valid, validone;
struct ipv6_addr *ia;
size_t len, olen;
uint8_t *p;
struct nd_opt_hdr ndo;
#if 0
struct nd_opt_prefix_info pi;
#endif
struct nd_opt_dnssl dnssl;
struct nd_opt_rdnss rdnss;
uint32_t ltime;
size_t nexpired = 0;
ifp = arg;
clock_gettime(CLOCK_MONOTONIC, &now);
@ -1536,8 +1529,7 @@ ipv6nd_expirera(void *arg)
lt.tv_sec = (time_t)rap->lifetime;
lt.tv_nsec = 0;
timespecadd(&rap->acquired, &lt, &expire);
if (rap->lifetime == 0 || timespeccmp(&now, &expire, >))
{
if (timespeccmp(&now, &expire, >)) {
if (!rap->expired) {
logwarnx("%s: %s: router expired",
ifp->name, rap->sfrom);
@ -1588,14 +1580,79 @@ ipv6nd_expirera(void *arg)
}
}
/* XXX FixMe!
* We need to extract the lifetime from each option and check
* if that has expired or not.
* If it has, zero the option out in the returned data. */
/* Work out expiry for ND options */
len = rap->data_len - sizeof(struct nd_router_advert);
for (p = rap->data + sizeof(struct nd_router_advert);
len >= sizeof(ndo);
p += olen, len -= olen)
{
memcpy(&ndo, p, sizeof(ndo));
olen = (size_t)(ndo.nd_opt_len * 8);
if (olen > len) {
errno = EINVAL;
break;
}
/* No valid lifetimes are left on the RA, so we might
* as well punt it. */
if (!valid && !validone)
if (has_option_mask(rap->iface->options->nomasknd,
ndo.nd_opt_type))
continue;
switch (ndo.nd_opt_type) {
/* Prefix info is already checked in the above loop. */
#if 0
case ND_OPT_PREFIX_INFORMATION:
if (len < sizeof(pi))
break;
memcpy(&pi, p, sizeof(pi));
ltime = pi.nd_opt_pi_valid_time;
break;
#endif
case ND_OPT_DNSSL:
if (len < sizeof(dnssl))
continue;
memcpy(&dnssl, p, sizeof(dnssl));
ltime = dnssl.nd_opt_dnssl_lifetime;
break;
case ND_OPT_RDNSS:
if (len < sizeof(rdnss))
continue;
memcpy(&rdnss, p, sizeof(rdnss));
ltime = rdnss.nd_opt_rdnss_lifetime;
break;
default:
continue;
}
if (ltime == 0)
continue;
if (ltime == ND6_INFINITE_LIFETIME) {
validone = true;
continue;
}
lt.tv_sec = (time_t)ntohl(ltime);
lt.tv_nsec = 0;
timespecadd(&rap->acquired, &lt, &expire);
if (timespeccmp(&now, &expire, >)) {
expired = true;
continue;
}
timespecsub(&expire, &now, &lt);
if (!timespecisset(&next) ||
timespeccmp(&next, &lt, >))
{
next = lt;
validone = true;
}
}
if (valid || validone)
continue;
/* Router has expired. Let's not keep a lot of them.
* We should work out if all the options have expired .... */
if (++nexpired > EXPIRED_MAX)
ipv6nd_free_ra(rap);
}
@ -1603,6 +1660,7 @@ ipv6nd_expirera(void *arg)
eloop_timeout_add_tv(ifp->ctx->eloop,
&next, ipv6nd_expirera, ifp);
if (expired) {
logwarnx("%s: part of Router Advertisement expired", ifp->name);
rt_build(ifp->ctx, AF_INET6);
script_runreason(ifp, "ROUTERADVERT");
}

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - IPv6 ND handling
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -95,7 +96,7 @@ struct rs_state {
void ipv6nd_printoptions(const struct dhcpcd_ctx *,
const struct dhcp_opt *, size_t);
void ipv6nd_startrs(struct interface *);
ssize_t ipv6nd_env(char **, const char *, const struct interface *);
ssize_t ipv6nd_env(FILE *, const struct interface *);
const struct ipv6_addr *ipv6nd_iffindaddr(const struct interface *ifp,
const struct in6_addr *addr, unsigned int flags);
struct ipv6_addr *ipv6nd_findaddr(struct dhcpcd_ctx *,
@ -107,7 +108,7 @@ int ipv6nd_hasradhcp(const struct interface *);
void ipv6nd_handleifa(int, struct ipv6_addr *, pid_t);
int ipv6nd_dadcompleted(const struct interface *);
void ipv6nd_advertise(struct ipv6_addr *);
void ipv6nd_expire(struct interface *, uint32_t);
void ipv6nd_startexpire(struct interface *);
void ipv6nd_drop(struct interface *);
void ipv6nd_neighbour(struct dhcpcd_ctx *, struct in6_addr *, int);
#endif /* INET6 */

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* logerr: errx with logging
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -91,7 +92,7 @@ getprogname(void)
#ifndef SMALL
/* Write the time, syslog style. month day time - */
static void
static int
logprintdate(FILE *stream)
{
struct timeval tv;
@ -100,19 +101,22 @@ logprintdate(FILE *stream)
char buf[32];
if (gettimeofday(&tv, NULL) == -1)
return;
return -1;
now = tv.tv_sec;
tzset();
localtime_r(&now, &tmnow);
strftime(buf, sizeof(buf), "%b %d %T ", &tmnow);
fprintf(stream, "%s", buf);
if (localtime_r(&now, &tmnow) == NULL)
return -1;
if (strftime(buf, sizeof(buf), "%b %d %T ", &tmnow) == 0)
return -1;
return fprintf(stream, "%s", buf);
}
#endif
__printflike(3, 0) static void
__printflike(3, 0) static int
vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
{
int len = 0, e;
va_list a;
#ifndef SMALL
bool log_pid;
@ -122,7 +126,11 @@ vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
if ((stream == stderr && ctx->log_opts & LOGERR_ERR_DATE) ||
(stream != stderr && ctx->log_opts & LOGERR_LOG_DATE))
logprintdate(stream);
{
if ((e = logprintdate(stream)) == -1)
return -1;
len += e;
}
#ifdef LOGERR_TAG
log_tag = ((stream == stderr && ctx->log_opts & LOGERR_ERR_TAG) ||
@ -130,29 +138,43 @@ vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
if (log_tag) {
if (ctx->log_tag == NULL)
ctx->log_tag = getprogname();
fprintf(stream, "%s", ctx->log_tag);
if ((e = fprintf(stream, "%s", ctx->log_tag)) == -1)
return -1;
len += e;
}
#endif
log_pid = ((stream == stderr && ctx->log_opts & LOGERR_ERR_PID) ||
(stream != stderr && ctx->log_opts & LOGERR_LOG_PID));
if (log_pid)
fprintf(stream, "[%d]", getpid());
if (log_pid) {
if ((e = fprintf(stream, "[%d]", getpid())) == -1)
return -1;
len += e;
}
#ifdef LOGERR_TAG
if (log_tag || log_pid)
#else
if (log_pid)
#endif
fprintf(stream, ": ");
{
if ((e = fprintf(stream, ": ")) == -1)
return -1;
len += e;
}
#else
UNUSED(ctx);
#endif
va_copy(a, args);
vfprintf(stream, fmt, a);
fputc('\n', stream);
e = vfprintf(stream, fmt, a);
if (fputc('\n', stream) == EOF)
e = -1;
else if (e != -1)
e++;
va_end(a);
return e == -1 ? -1 : len + e;
}
/*
@ -170,30 +192,31 @@ vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
#endif
__printflike(2, 0) static void
__printflike(2, 0) static int
vlogmessage(int pri, const char *fmt, va_list args)
{
struct logctx *ctx = &_logctx;
int len = 0;
if (ctx->log_opts & LOGERR_ERR &&
(pri <= LOG_ERR ||
(!(ctx->log_opts & LOGERR_QUIET) && pri <= LOG_INFO) ||
(ctx->log_opts & LOGERR_DEBUG && pri <= LOG_DEBUG)))
vlogprintf_r(ctx, stderr, fmt, args);
len = vlogprintf_r(ctx, stderr, fmt, args);
if (!(ctx->log_opts & LOGERR_LOG))
return;
return len;
#ifdef SMALL
vsyslog(pri, fmt, args);
#else
if (ctx->log_file == NULL) {
vsyslog(pri, fmt, args);
return;
return len;
}
if (pri == LOG_DEBUG && !(ctx->log_opts & LOGERR_DEBUG))
return;
vlogprintf_r(ctx, ctx->log_file, fmt, args);
return len;
return vlogprintf_r(ctx, ctx->log_file, fmt, args);
#endif
}
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* logerr: errx with logging
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - route management
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -29,6 +30,7 @@
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -44,6 +46,66 @@
#include "route.h"
#include "sa.h"
/* Needed for NetBSD-6, 7 and 8. */
#ifndef RB_TREE_FOREACH_SAFE
#ifndef RB_TREE_PREV
#define RB_TREE_NEXT(T, N) rb_tree_iterate((T), (N), RB_DIR_RIGHT)
#define RB_TREE_PREV(T, N) rb_tree_iterate((T), (N), RB_DIR_LEFT)
#endif
#define RB_TREE_FOREACH_SAFE(N, T, S) \
for ((N) = RB_TREE_MIN(T); \
(N) && ((S) = RB_TREE_NEXT((T), (N)), 1); \
(N) = (S))
#define RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \
for ((N) = RB_TREE_MAX(T); \
(N) && ((S) = RB_TREE_PREV((T), (N)), 1); \
(N) = (S))
#endif
#ifdef RT_FREE_ROUTE_TABLE_STATS
static size_t croutes;
static size_t nroutes;
static size_t froutes;
static size_t mroutes;
#endif
static void
rt_maskedaddr(struct sockaddr *dst,
const struct sockaddr *addr, const struct sockaddr *netmask)
{
const char *addrp = addr->sa_data, *netmaskp = netmask->sa_data;
char *dstp = dst->sa_data;
const char *addre = (char *)dst + sa_len(addr);
const char *netmaske = (char *)dst + MIN(sa_len(addr), sa_len(netmask));
dst->sa_family = addr->sa_family;
#ifdef HAVE_SA_LEN
dst->sa_len = addr->sa_len;
#endif
if (sa_is_unspecified(netmask)) {
if (addre > dstp)
memcpy(dstp, addrp, (size_t)(addre - dstp));
return;
}
while (dstp < netmaske)
*dstp++ = *addrp++ & *netmaskp++;
if (dstp < addre)
memset(dstp, 0, (size_t)(addre - dstp));
}
int
rt_cmp_dest(const struct rt *rt1, const struct rt *rt2)
{
union sa_ss ma1 = { .sa.sa_family = AF_UNSPEC };
union sa_ss ma2 = { .sa.sa_family = AF_UNSPEC };
rt_maskedaddr(&ma1.sa, &rt1->rt_dest, &rt1->rt_netmask);
rt_maskedaddr(&ma2.sa, &rt2->rt_dest, &rt2->rt_netmask);
return sa_cmp(&ma1.sa, &ma2.sa);
}
/*
* On some systems, host routes have no need for a netmask.
* However DHCP specifies host routes using an all-ones netmask.
@ -59,13 +121,99 @@ rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2)
return sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask);
}
static int
rt_compare_os(__unused void *context, const void *node1, const void *node2)
{
const struct rt *rt1 = node1, *rt2 = node2;
int c;
/* Sort by masked destination. */
c = rt_cmp_dest(rt1, rt2);
if (c != 0)
return c;
#ifdef HAVE_ROUTE_METRIC
c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);
#endif
return c;
}
static int
rt_compare_proto(__unused void *context, const void *node1, const void *node2)
{
const struct rt *rt1 = node1, *rt2 = node2;
int c;
struct interface *ifp1, *ifp2;
assert(rt1->rt_ifp != NULL);
assert(rt2->rt_ifp != NULL);
ifp1 = rt1->rt_ifp;
ifp2 = rt2->rt_ifp;
/* Prefer interfaces with a carrier. */
c = ifp1->carrier - ifp2->carrier;
if (c != 0)
return -c;
/* Lower metric interfaces come first. */
c = (int)(ifp1->metric - ifp2->metric);
if (c != 0)
return c;
/* Finally the order in which the route was given to us. */
if (rt1->rt_order > rt2->rt_order)
return 1;
if (rt1->rt_order < rt2->rt_order)
return -1;
return 0;
}
static const rb_tree_ops_t rt_compare_os_ops = {
.rbto_compare_nodes = rt_compare_os,
.rbto_compare_key = rt_compare_os,
.rbto_node_offset = offsetof(struct rt, rt_tree),
.rbto_context = NULL
};
const rb_tree_ops_t rt_compare_proto_ops = {
.rbto_compare_nodes = rt_compare_proto,
.rbto_compare_key = rt_compare_proto,
.rbto_node_offset = offsetof(struct rt, rt_tree),
.rbto_context = NULL
};
#ifdef RT_FREE_ROUTE_TABLE
static int
rt_compare_free(__unused void *context, const void *node1, const void *node2)
{
return node1 == node2 ? 0 : node1 < node2 ? -1 : 1;
}
static const rb_tree_ops_t rt_compare_free_ops = {
.rbto_compare_nodes = rt_compare_free,
.rbto_compare_key = rt_compare_free,
.rbto_node_offset = offsetof(struct rt, rt_tree),
.rbto_context = NULL
};
#endif
void
rt_init(struct dhcpcd_ctx *ctx)
{
TAILQ_INIT(&ctx->routes);
TAILQ_INIT(&ctx->kroutes);
TAILQ_INIT(&ctx->froutes);
rb_tree_init(&ctx->routes, &rt_compare_os_ops);
#ifdef RT_FREE_ROUTE_TABLE
rb_tree_init(&ctx->froutes, &rt_compare_free_ops);
#endif
}
bool
rt_is_default(const struct rt *rt)
{
return sa_is_unspecified(&rt->rt_dest) &&
sa_is_unspecified(&rt->rt_netmask);
}
static void
@ -92,9 +240,7 @@ rt_desc(const char *cmd, const struct rt *rt)
else
loginfox("%s: %s host route to %s via %s",
ifname, cmd, dest, gateway);
} else if (sa_is_unspecified(&rt->rt_dest) &&
sa_is_unspecified(&rt->rt_netmask))
{
} else if (rt_is_default(rt)) {
if (gateway_unspec)
loginfox("%s: %s default route",
ifname, cmd);
@ -114,42 +260,44 @@ rt_desc(const char *cmd, const struct rt *rt)
}
void
rt_headclear0(struct dhcpcd_ctx *ctx, struct rt_head *rts, int af)
rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af)
{
struct rt *rt, *rtn;
if (rts == NULL)
return;
assert(ctx != NULL);
#ifdef RT_FREE_ROUTE_TABLE
assert(&ctx->froutes != rts);
#endif
TAILQ_FOREACH_SAFE(rt, rts, rt_next, rtn) {
RB_TREE_FOREACH_SAFE(rt, rts, rtn) {
if (af != AF_UNSPEC &&
rt->rt_dest.sa_family != af &&
rt->rt_gateway.sa_family != af)
continue;
TAILQ_REMOVE(rts, rt, rt_next);
TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next);
rb_tree_remove_node(rts, rt);
rt_free(rt);
}
}
void
rt_headclear(struct rt_head *rts, int af)
rt_headclear(rb_tree_t *rts, int af)
{
struct rt *rt;
if (rts == NULL || (rt = TAILQ_FIRST(rts)) == NULL)
if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL)
return;
rt_headclear0(rt->rt_ifp->ctx, rts, af);
}
static void
rt_headfree(struct rt_head *rts)
rt_headfree(rb_tree_t *rts)
{
struct rt *rt;
while ((rt = TAILQ_FIRST(rts))) {
TAILQ_REMOVE(rts, rt, rt_next);
while ((rt = RB_TREE_MIN(rts)) != NULL) {
rb_tree_remove_node(rts, rt);
free(rt);
}
}
@ -160,8 +308,14 @@ rt_dispose(struct dhcpcd_ctx *ctx)
assert(ctx != NULL);
rt_headfree(&ctx->routes);
rt_headfree(&ctx->kroutes);
#ifdef RT_FREE_ROUTE_TABLE
rt_headfree(&ctx->froutes);
#ifdef RT_FREE_ROUTE_TABLE_STATS
logdebugx("free route list used %zu times", froutes);
logdebugx("new routes from route free list %zu", nroutes);
logdebugx("maximum route free list size %zu", mroutes);
#endif
#endif
}
struct rt *
@ -170,9 +324,16 @@ rt_new0(struct dhcpcd_ctx *ctx)
struct rt *rt;
assert(ctx != NULL);
if ((rt = TAILQ_FIRST(&ctx->froutes)) != NULL)
TAILQ_REMOVE(&ctx->froutes, rt, rt_next);
else if ((rt = malloc(sizeof(*rt))) == NULL) {
#ifdef RT_FREE_ROUTE_TABLE
if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) {
rb_tree_remove_node(&ctx->froutes, rt);
#ifdef RT_FREE_ROUTE_TABLE_STATS
croutes--;
nroutes++;
#endif
} else
#endif
if ((rt = malloc(sizeof(*rt))) == NULL) {
logerr(__func__);
return NULL;
}
@ -204,13 +365,47 @@ rt_new(struct interface *ifp)
return rt;
}
struct rt *
rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
{
rt->rt_order = ctx->rt_order++;
if (rb_tree_insert_node(tree, rt) == rt)
return rt;
rt_free(rt);
return NULL;
}
struct rt *
rt_proto_add(rb_tree_t *tree, struct rt *rt)
{
assert (rt->rt_ifp != NULL);
return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
}
void
rt_free(struct rt *rt)
{
#ifdef RT_FREE_ROUTE_TABLE
struct dhcpcd_ctx *ctx;
assert(rt != NULL);
assert(rt->rt_ifp != NULL);
assert(rt->rt_ifp->ctx != NULL);
TAILQ_INSERT_TAIL(&rt->rt_ifp->ctx->froutes, rt, rt_next);
ctx = rt->rt_ifp->ctx;
rb_tree_insert_node(&ctx->froutes, rt);
#ifdef RT_FREE_ROUTE_TABLE_STATS
croutes++;
froutes++;
if (croutes > mroutes)
mroutes = croutes;
#endif
#else
free(rt);
#endif
}
void
@ -222,51 +417,12 @@ rt_freeif(struct interface *ifp)
if (ifp == NULL)
return;
ctx = ifp->ctx;
TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) {
RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) {
if (rt->rt_ifp == ifp) {
TAILQ_REMOVE(&ctx->routes, rt, rt_next);
rb_tree_remove_node(&ctx->routes, rt);
rt_free(rt);
}
}
TAILQ_FOREACH_SAFE(rt, &ctx->kroutes, rt_next, rtn) {
if (rt->rt_ifp == ifp) {
TAILQ_REMOVE(&ctx->kroutes, rt, rt_next);
rt_free(rt);
}
}
}
struct rt *
rt_find(struct rt_head *rts, const struct rt *f)
{
struct rt *rt;
assert(rts != NULL);
assert(f != NULL);
TAILQ_FOREACH(rt, rts, rt_next) {
if (sa_cmp(&rt->rt_dest, &f->rt_dest) == 0 &&
#ifdef HAVE_ROUTE_METRIC
(f->rt_ifp == NULL ||
rt->rt_ifp->metric == f->rt_ifp->metric) &&
#endif
rt_cmp_netmask(f, rt) == 0)
return rt;
}
return NULL;
}
static void
rt_kfree(struct rt *rt)
{
struct dhcpcd_ctx *ctx;
struct rt *f;
assert(rt != NULL);
ctx = rt->rt_ifp->ctx;
if ((f = rt_find(&ctx->kroutes, rt)) != NULL) {
TAILQ_REMOVE(&ctx->kroutes, f, rt_next);
rt_free(f);
}
}
/* If something other than dhcpcd removes a route,
@ -278,32 +434,23 @@ rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
struct rt *f;
assert(rt != NULL);
assert(rt->rt_ifp != NULL);
assert(rt->rt_ifp->ctx != NULL);
ctx = rt->rt_ifp->ctx;
f = rt_find(&ctx->kroutes, rt);
switch(cmd) {
case RTM_DELETE:
f = rb_tree_find_node(&ctx->routes, rt);
if (f != NULL) {
TAILQ_REMOVE(&ctx->kroutes, f, rt_next);
rt_free(f);
}
if ((f = rt_find(&ctx->routes, rt)) != NULL) {
char buf[32];
TAILQ_REMOVE(&ctx->routes, f, rt_next);
rb_tree_remove_node(&ctx->routes, f);
snprintf(buf, sizeof(buf), "pid %d deleted", pid);
rt_desc(buf, f);
rt_free(f);
}
break;
case RTM_ADD:
if (f != NULL)
break;
if ((f = rt_new(rt->rt_ifp)) == NULL)
break;
memcpy(f, rt, sizeof(*f));
TAILQ_INSERT_TAIL(&ctx->kroutes, f, rt_next);
break;
}
#if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
@ -313,10 +460,10 @@ rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
}
static bool
rt_add(struct rt *nrt, struct rt *ort)
rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
{
struct dhcpcd_ctx *ctx;
bool change;
bool change, kroute, result;
assert(nrt != NULL);
ctx = nrt->rt_ifp->ctx;
@ -338,9 +485,9 @@ rt_add(struct rt *nrt, struct rt *ort)
rt_desc(ort == NULL ? "adding" : "changing", nrt);
change = false;
change = kroute = result = false;
if (ort == NULL) {
ort = rt_find(&ctx->kroutes, nrt);
ort = rb_tree_find_node(kroutes, nrt);
if (ort != NULL &&
((ort->rt_flags & RTF_REJECT &&
nrt->rt_flags & RTF_REJECT) ||
@ -353,6 +500,7 @@ rt_add(struct rt *nrt, struct rt *ort)
if (ort->rt_mtu == nrt->rt_mtu)
return true;
change = true;
kroute = true;
}
} else if (ort->rt_dflags & RTDF_FAKE &&
!(nrt->rt_dflags & RTDF_FAKE) &&
@ -379,8 +527,10 @@ rt_add(struct rt *nrt, struct rt *ort)
#endif
if (change) {
if (if_route(RTM_CHANGE, nrt) != -1)
return true;
if (if_route(RTM_CHANGE, nrt) != -1) {
result = true;
goto out;
}
if (errno != ESRCH)
logerr("if_route (CHG)");
}
@ -392,9 +542,9 @@ rt_add(struct rt *nrt, struct rt *ort)
if (ort != NULL) {
if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
logerr("if_route (DEL)");
rt_kfree(ort);
}
return true;
result = true;
goto out;
}
/* If the kernel claims the route exists we need to rip out the
@ -412,7 +562,7 @@ rt_add(struct rt *nrt, struct rt *ort)
if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
logerr("if_route (DEL)");
else
rt_kfree(ort);
kroute = false;
}
#ifdef ROUTE_PER_GATEWAY
/* The OS allows many routes to the same dest with different gateways.
@ -425,13 +575,23 @@ rt_add(struct rt *nrt, struct rt *ort)
}
}
#endif
if (if_route(RTM_ADD, nrt) != -1)
return true;
if (if_route(RTM_ADD, nrt) != -1) {
result = true;
goto out;
}
#ifdef HAVE_ROUTE_METRIC
logerr:
#endif
logerr("if_route (ADD)");
return false;
out:
if (kroute) {
rb_tree_remove_node(kroutes, ort);
rt_free(ort);
}
return result;
}
static bool
@ -443,10 +603,6 @@ rt_delete(struct rt *rt)
retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
if (!retval && errno != ENOENT && errno != ESRCH)
logerr(__func__);
/* Remove the route from our kernel table so we can add a
* IPv4LL default route if possible. */
else
rt_kfree(rt);
return retval;
}
@ -462,14 +618,15 @@ rt_cmp(const struct rt *r1, const struct rt *r2)
}
static bool
rt_doroute(struct rt *rt)
rt_doroute(rb_tree_t *kroutes, struct rt *rt)
{
struct dhcpcd_ctx *ctx;
struct rt *or;
ctx = rt->rt_ifp->ctx;
/* Do we already manage it? */
if ((or = rt_find(&ctx->routes, rt))) {
or = rb_tree_find_node(&ctx->routes, rt);
if (or != NULL) {
if (rt->rt_dflags & RTDF_FAKE)
return true;
if (or->rt_dflags & RTDF_FAKE ||
@ -478,19 +635,20 @@ rt_doroute(struct rt *rt)
sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||
or->rt_mtu != rt->rt_mtu)
{
if (!rt_add(rt, or))
if (!rt_add(kroutes, rt, or))
return false;
}
TAILQ_REMOVE(&ctx->routes, or, rt_next);
rb_tree_remove_node(&ctx->routes, or);
rt_free(or);
} else {
if (rt->rt_dflags & RTDF_FAKE) {
if ((or = rt_find(&ctx->kroutes, rt)) == NULL)
or = rb_tree_find_node(kroutes, rt);
if (or == NULL)
return false;
if (!rt_cmp(rt, or))
return false;
} else {
if (!rt_add(rt, NULL))
if (!rt_add(kroutes, rt, NULL))
return false;
}
}
@ -501,16 +659,15 @@ rt_doroute(struct rt *rt)
void
rt_build(struct dhcpcd_ctx *ctx, int af)
{
struct rt_head routes, added;
rb_tree_t routes, added, kroutes;
struct rt *rt, *rtn;
unsigned long long o;
/* We need to have the interfaces in the correct order to ensure
* our routes are managed correctly. */
if_sortinterfaces(ctx);
TAILQ_INIT(&routes);
TAILQ_INIT(&added);
rb_tree_init(&routes, &rt_compare_proto_ops);
rb_tree_init(&added, &rt_compare_os_ops);
rb_tree_init(&kroutes, &rt_compare_os_ops);
if_initrt(ctx, &kroutes, af);
ctx->rt_order = 0;
switch (af) {
#ifdef INET
@ -527,26 +684,34 @@ rt_build(struct dhcpcd_ctx *ctx, int af)
#endif
}
TAILQ_FOREACH_SAFE(rt, &routes, rt_next, rtn) {
if (rt->rt_dest.sa_family != af &&
rt->rt_gateway.sa_family != af)
RB_TREE_FOREACH_SAFE(rt, &routes, rtn) {
if ((rt->rt_dest.sa_family != af &&
rt->rt_dest.sa_family != AF_UNSPEC) ||
(rt->rt_gateway.sa_family != af &&
rt->rt_gateway.sa_family != AF_UNSPEC))
continue;
/* Is this route already in our table? */
if ((rt_find(&added, rt)) != NULL)
if (rb_tree_find_node(&added, rt) != NULL)
continue;
if (rt_doroute(rt)) {
TAILQ_REMOVE(&routes, rt, rt_next);
TAILQ_INSERT_TAIL(&added, rt, rt_next);
if (rt_doroute(&kroutes, rt)) {
rb_tree_remove_node(&routes, rt);
if (rb_tree_insert_node(&added, rt) != rt) {
errno = EEXIST;
logerr(__func__);
rt_free(rt);
}
}
}
/* Remove old routes we used to manage. */
TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) {
if (rt->rt_dest.sa_family != af &&
rt->rt_gateway.sa_family != af)
RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) {
if ((rt->rt_dest.sa_family != af &&
rt->rt_dest.sa_family != AF_UNSPEC) ||
(rt->rt_gateway.sa_family != af &&
rt->rt_gateway.sa_family != AF_UNSPEC))
continue;
TAILQ_REMOVE(&ctx->routes, rt, rt_next);
if (rt_find(&added, rt) == NULL) {
rb_tree_remove_node(&ctx->routes, rt);
if (rb_tree_find_node(&added, rt) == NULL) {
o = rt->rt_ifp->options ?
rt->rt_ifp->options->options :
ctx->options;
@ -555,12 +720,21 @@ rt_build(struct dhcpcd_ctx *ctx, int af)
(DHCPCD_EXITING | DHCPCD_PERSISTENT))
rt_delete(rt);
}
TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next);
rt_free(rt);
}
/* XXX This needs to be optimised. */
while ((rt = RB_TREE_MIN(&added)) != NULL) {
rb_tree_remove_node(&added, rt);
if (rb_tree_insert_node(&ctx->routes, rt) != rt) {
errno = EEXIST;
logerr(__func__);
rt_free(rt);
}
}
rt_headclear(&ctx->routes, af);
TAILQ_CONCAT(&ctx->routes, &added, rt_next);
getfail:
rt_headclear(&routes, AF_UNSPEC);
rt_headclear(&kroutes, AF_UNSPEC);
}

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - route management
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -28,6 +29,10 @@
#ifndef ROUTE_H
#define ROUTE_H
#ifdef HAVE_SYS_RBTREE_H
#include <sys/rbtree.h>
#endif
#include <sys/socket.h>
#include <net/route.h>
@ -36,6 +41,17 @@
#include "dhcpcd.h"
#include "sa.h"
/*
* Enable the route free list by default as
* memory usage is still reported as low/unchanged even
* when dealing with millions of routes.
*/
#if !defined(RT_FREE_ROUTE_TABLE)
#define RT_FREE_ROUTE_TABLE 1
#elif RT_FREE_ROUTE_TABLE == 0
#undef RT_FREE_ROUTE_TABLE
#endif
/* Some systems have route metrics.
* OpenBSD route priority is not this. */
#ifndef HAVE_ROUTE_METRIC
@ -56,7 +72,6 @@
#endif
struct rt {
TAILQ_ENTRY(rt) rt_next;
union sa_ss rt_ss_dest;
#define rt_dest rt_ss_dest.sa
union sa_ss rt_ss_netmask;
@ -72,26 +87,31 @@ struct rt {
unsigned int rt_metric;
#endif
unsigned int rt_dflags;
#define RTDF_INIT 0x01 /* Generated by if_initrt() */
#define RTDF_IFA_ROUTE 0x02 /* Address generated route */
#define RTDF_FAKE 0x04 /* Maybe us on lease reboot */
#define RTDF_RA 0x08 /* Router Advertisement */
#define RTDF_DHCP 0x10 /* DHCP route */
#define RTDF_STATIC 0x20 /* Configured in dhcpcd */
size_t rt_order;
rb_node_t rt_tree;
};
TAILQ_HEAD(rt_head, rt);
extern const rb_tree_ops_t rt_compare_proto_ops;
void rt_init(struct dhcpcd_ctx *);
void rt_dispose(struct dhcpcd_ctx *);
struct rt * rt_find(struct rt_head *, const struct rt *);
void rt_free(struct rt *);
void rt_freeif(struct interface *);
void rt_headclear0(struct dhcpcd_ctx *, struct rt_head *, int);
void rt_headclear(struct rt_head *, int);
void rt_headfreeif(struct rt_head *);
bool rt_is_default(const struct rt *);
void rt_headclear0(struct dhcpcd_ctx *, rb_tree_t *, int);
void rt_headclear(rb_tree_t *, int);
void rt_headfreeif(rb_tree_t *);
struct rt * rt_new0(struct dhcpcd_ctx *);
void rt_setif(struct rt *, struct interface *);
struct rt * rt_new(struct interface *);
struct rt * rt_proto_add_ctx(rb_tree_t *, struct rt *, struct dhcpcd_ctx *);
struct rt * rt_proto_add(rb_tree_t *, struct rt *);
int rt_cmp_dest(const struct rt *, const struct rt *);
void rt_recvrt(int, const struct rt *, pid_t);
void rt_build(struct dhcpcd_ctx *, int);

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Socket Address handling for dhcpcd
* Copyright (c) 2015-2019 Roy Marples <roy@marples.name>
@ -37,6 +38,7 @@
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
@ -92,6 +94,30 @@ sa_addrlen(const struct sockaddr *sa)
}
}
#ifndef HAVE_SA_LEN
socklen_t
sa_len(const struct sockaddr *sa)
{
switch (sa->sa_family) {
#ifdef AF_LINK
case AF_LINK:
return sizeof(struct sockaddr_dl);
#endif
#ifdef AF_PACKET
case AF_PACKET:
return sizeof(struct sockaddr_ll);
#endif
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
default:
return sizeof(struct sockaddr);
}
}
#endif
bool
sa_is_unspecified(const struct sockaddr *sa)
{
@ -339,6 +365,13 @@ sa_addrtop(const struct sockaddr *sa, char *buf, socklen_t len)
const void *addr;
assert(buf != NULL);
assert(len > 0);
if (sa->sa_family == 0) {
*buf = '\0';
return NULL;
}
#ifdef AF_LINK
#ifndef CLLADDR
#define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen)

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Socket Address handling for dhcpcd
* Copyright (c) 2015-2019 Roy Marples <roy@marples.name>
@ -29,6 +30,7 @@
#define SA_H
#include <sys/socket.h>
#include <netinet/in.h>
union sa_ss {
struct sockaddr sa;
@ -54,6 +56,11 @@ union sa_ss {
socklen_t sa_addroffset(const struct sockaddr *sa);
socklen_t sa_addrlen(const struct sockaddr *sa);
#ifdef HAVE_SA_LEN
#define sa_len(sa) ((sa)->sa_len)
#else
socklen_t sa_len(const struct sockaddr *sa);
#endif
bool sa_is_unspecified(const struct sockaddr *);
bool sa_is_allones(const struct sockaddr *);
bool sa_is_loopback(const struct sockaddr *);

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -36,6 +37,7 @@
#include <errno.h>
#include <signal.h>
#include <spawn.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -56,7 +58,7 @@
#define RC_SVCNAME "RC_SVCNAME"
#endif
#define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
#define DEFAULT_PATH "/usr/bin:/usr/sbin:/bin:/sbin"
static const char * const if_params[] = {
"interface",
@ -120,104 +122,24 @@ exec_script(const struct dhcpcd_ctx *ctx, char *const *argv, char *const *env)
}
#ifdef INET
static char *
make_var(const char *prefix, const char *var)
{
size_t len;
char *v;
len = strlen(prefix) + strlen(var) + 2;
if ((v = malloc(len)) == NULL) {
logerr(__func__);
return NULL;
}
snprintf(v, len, "%s_%s", prefix, var);
return v;
}
static int
append_config(char ***env, size_t *len,
const char *prefix, const char *const *config)
append_config(FILE *fp, const char *prefix, const char *const *config)
{
size_t i, j, e1;
char **ne, *eq, **nep, *p;
int ret;
size_t i;
if (config == NULL)
return 0;
ne = *env;
ret = 0;
/* Do we need to replace existing config rather than append? */
for (i = 0; config[i] != NULL; i++) {
eq = strchr(config[i], '=');
e1 = (size_t)(eq - config[i] + 1);
for (j = 0; j < *len; j++) {
if (strncmp(ne[j], prefix, strlen(prefix)) == 0 &&
ne[j][strlen(prefix)] == '_' &&
strncmp(ne[j] + strlen(prefix) + 1,
config[i], e1) == 0)
{
p = make_var(prefix, config[i]);
if (p == NULL) {
ret = -1;
break;
}
free(ne[j]);
ne[j] = p;
break;
}
}
if (j == *len) {
j++;
p = make_var(prefix, config[i]);
if (p == NULL) {
ret = -1;
break;
}
nep = realloc(ne, sizeof(char *) * (j + 1));
if (nep == NULL) {
logerr(__func__);
free(p);
ret = -1;
break;
}
ne = nep;
ne[j - 1] = p;
*len = j;
}
if (efprintf(fp, "%s_%s", prefix, config[i]) == -1)
return -1;
}
*env = ne;
return ret;
return 1;
}
#endif
static ssize_t
arraytostr(const char *const *argv, char **s)
{
const char *const *ap;
char *p;
size_t len, l;
if (*argv == NULL)
return 0;
len = 0;
ap = argv;
while (*ap)
len += strlen(*ap++) + 1;
*s = p = malloc(len);
if (p == NULL)
return -1;
ap = argv;
while (*ap) {
l = strlen(*ap) + 1;
memcpy(p, *ap, l);
p += l;
ap++;
}
return (ssize_t)len;
}
#define PROTO_LINK 0
#define PROTO_DHCP 1
#define PROTO_IPV4LL 2
@ -233,15 +155,32 @@ static const char *protocols[] = {
"static6"
};
static ssize_t
make_env(const struct interface *ifp, const char *reason, char ***argv)
int
efprintf(FILE *fp, const char *fmt, ...)
{
int protocol, r;
char **env, **nenv, *p;
size_t e, elen, l;
#if defined(INET) || defined(INET6)
ssize_t n;
#endif
va_list args;
int r;
va_start(args, fmt);
r = vfprintf(fp, fmt, args);
va_end(args);
if (r == -1)
return -1;
/* Write a trailing NULL so we can easily create env strings. */
if (fputc('\0', fp) == EOF)
return -1;
return r;
}
static ssize_t
make_env(const struct interface *ifp, const char *reason)
{
struct dhcpcd_ctx *ctx = ifp->ctx;
FILE *fp;
char **env, **envp, *buf, *bufp, *endp, *path;
size_t nenv;
long buf_pos, i;
int protocol;
const struct if_options *ifo = ifp->options;
const struct interface *ifp2;
int af;
@ -255,6 +194,31 @@ make_env(const struct interface *ifp, const char *reason, char ***argv)
const struct dhcp6_state *d6_state;
#endif
#ifdef HAVE_OPEN_MEMSTREAM
if (ctx->script_fp == NULL) {
fp = open_memstream(&ctx->script_buf, &ctx->script_buflen);
if (fp == NULL)
goto eexit;
ctx->script_fp = fp;
} else {
fp = ctx->script_fp;
rewind(fp);
}
#else
char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX";
int tmpfd;
fp = NULL;
tmpfd = mkstemp(tmpfile);
if (tmpfd == -1)
goto eexit;
unlink(tmpfile);
fp = fopen(tmpfile, "w+");
close(tmpfd);
if (fp == NULL)
goto eexit;
#endif
#ifdef INET
state = D_STATE(ifp);
#ifdef IPV4LL
@ -309,71 +273,60 @@ make_env(const struct interface *ifp, const char *reason, char ***argv)
protocol = PROTO_DHCP;
#endif
/* When dumping the lease, we only want to report interface and
reason - the other interface variables are meaningless */
if (ifp->ctx->options & DHCPCD_DUMPLEASE)
elen = 2;
else
elen = 11;
#define EMALLOC(i, l) if ((env[(i)] = malloc((l))) == NULL) goto eexit;
/* Make our env + space for profile, wireless and debug */
env = calloc(1, sizeof(char *) * (elen + 5 + 1));
if (env == NULL)
/* Needed for scripts */
path = getenv("PATH");
if (efprintf(fp, "PATH=%s", path == NULL ? DEFAULT_PATH:path) == -1)
goto eexit;
if (efprintf(fp, "interface=%s", ifp->name) == -1)
goto eexit;
if (efprintf(fp, "reason=%s", reason) == -1)
goto eexit;
e = strlen("interface") + strlen(ifp->name) + 2;
EMALLOC(0, e);
snprintf(env[0], e, "interface=%s", ifp->name);
e = strlen("reason") + strlen(reason) + 2;
EMALLOC(1, e);
snprintf(env[1], e, "reason=%s", reason);
if (ifp->ctx->options & DHCPCD_DUMPLEASE)
goto dumplease;
e = 20;
EMALLOC(2, e);
snprintf(env[2], e, "pid=%d", getpid());
EMALLOC(3, e);
snprintf(env[3], e, "ifcarrier=%s",
if (efprintf(fp, "pid=%d", getpid()) == -1)
goto eexit;
if (efprintf(fp, "ifcarrier=%s",
ifp->carrier == LINK_UNKNOWN ? "unknown" :
ifp->carrier == LINK_UP ? "up" : "down");
EMALLOC(4, e);
snprintf(env[4], e, "ifmetric=%d", ifp->metric);
EMALLOC(5, e);
snprintf(env[5], e, "ifwireless=%d", ifp->wireless);
EMALLOC(6, e);
snprintf(env[6], e, "ifflags=%u", ifp->flags);
EMALLOC(7, e);
snprintf(env[7], e, "ifmtu=%d", if_getmtu(ifp));
l = e = strlen("interface_order=");
ifp->carrier == LINK_UP ? "up" : "down") == -1)
goto eexit;
if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1)
goto eexit;
if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1)
goto eexit;
if (efprintf(fp, "ifflags=%u", ifp->flags) == -1)
goto eexit;
if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1)
goto eexit;
if (fprintf(fp, "interface_order=") == -1)
goto eexit;
TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
e += strlen(ifp2->name) + 1;
if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) {
if (fputc(' ', fp) == EOF)
return -1;
}
if (fprintf(fp, "%s", ifp2->name) == -1)
return -1;
}
EMALLOC(8, e);
p = env[8];
strlcpy(p, "interface_order=", e);
e -= l;
p += l;
TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
l = strlcpy(p, ifp2->name, e);
p += l;
e -= l;
*p++ = ' ';
e--;
}
*--p = '\0';
if (fputc('\0', fp) == EOF)
return -1;
if (strcmp(reason, "STOPPED") == 0) {
env[9] = strdup("if_up=false");
if (ifo->options & DHCPCD_RELEASE)
env[10] = strdup("if_down=true");
else
env[10] = strdup("if_down=false");
if (efprintf(fp, "if_up=false") == -1)
goto eexit;
if (efprintf(fp, "if_down=%s",
ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1)
goto eexit;
} else if (strcmp(reason, "TEST") == 0 ||
strcmp(reason, "PREINIT") == 0 ||
strcmp(reason, "CARRIER") == 0 ||
strcmp(reason, "UNKNOWN") == 0)
{
env[9] = strdup("if_up=false");
env[10] = strdup("if_down=false");
if (efprintf(fp, "if_up=false") == -1)
goto eexit;
if (efprintf(fp, "if_down=false") == -1)
goto eexit;
} else if (1 == 2 /* appease ifdefs */
#ifdef INET
|| (protocol == PROTO_DHCP && state && state->new)
@ -390,24 +343,23 @@ make_env(const struct interface *ifp, const char *reason, char ***argv)
#endif
)
{
env[9] = strdup("if_up=true");
env[10] = strdup("if_down=false");
} else {
env[9] = strdup("if_up=false");
env[10] = strdup("if_down=true");
}
if (env[9] == NULL || env[10] == NULL)
goto eexit;
if (protocols[protocol] != NULL) {
r = asprintf(&env[elen], "protocol=%s", protocols[protocol]);
if (r == -1)
if (efprintf(fp, "if_up=true") == -1)
goto eexit;
if (efprintf(fp, "if_down=false") == -1)
goto eexit;
} else {
if (efprintf(fp, "if_up=false") == -1)
goto eexit;
if (efprintf(fp, "if_down=true") == -1)
goto eexit;
}
if (protocols[protocol] != NULL) {
if (efprintf(fp, "protocol=%s", protocols[protocol]) == -1)
goto eexit;
elen++;
}
if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {
e = 20;
EMALLOC(elen, e);
snprintf(env[elen++], e, "if_afwaiting=%d", af);
if (efprintf(fp, "if_afwaiting=%d", af) == -1)
goto eexit;
}
if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) {
TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
@ -416,75 +368,42 @@ make_env(const struct interface *ifp, const char *reason, char ***argv)
}
}
if (af != AF_MAX) {
e = 20;
EMALLOC(elen, e);
snprintf(env[elen++], e, "af_waiting=%d", af);
if (efprintf(fp, "af_waiting=%d", af) == -1)
goto eexit;
}
if (ifo->options & DHCPCD_DEBUG) {
e = strlen("syslog_debug=true") + 1;
EMALLOC(elen, e);
snprintf(env[elen++], e, "syslog_debug=true");
if (efprintf(fp, "syslog_debug=true") == -1)
goto eexit;
}
if (*ifp->profile) {
e = strlen("profile=") + strlen(ifp->profile) + 1;
EMALLOC(elen, e);
snprintf(env[elen++], e, "profile=%s", ifp->profile);
if (efprintf(fp, "profile=%s", ifp->profile) == -1)
goto eexit;
}
if (ifp->wireless) {
static const char *pfx = "ifssid=";
size_t pfx_len;
ssize_t psl;
char pssid[IF_SSIDLEN * 4];
pfx_len = strlen(pfx);
psl = print_string(NULL, 0, OT_ESCSTRING,
(const uint8_t *)ifp->ssid, ifp->ssid_len);
if (psl != -1) {
EMALLOC(elen, pfx_len + (size_t)psl + 1);
memcpy(env[elen], pfx, pfx_len);
print_string(env[elen] + pfx_len, (size_t)psl + 1,
OT_ESCSTRING,
(const uint8_t *)ifp->ssid, ifp->ssid_len);
elen++;
if (print_string(pssid, sizeof(pssid), OT_ESCSTRING,
ifp->ssid, ifp->ssid_len) != -1)
{
if (efprintf(fp, "ifssid=%s", pssid) == -1)
goto eexit;
}
}
#ifdef INET
if (protocol == PROTO_DHCP && state && state->old) {
n = dhcp_env(NULL, NULL, state->old, state->old_len, ifp);
if (n == -1)
if (dhcp_env(fp, "old", ifp,
state->old, state->old_len) == -1)
goto eexit;
if (n > 0) {
nenv = realloc(env, sizeof(char *) *
(elen + (size_t)n + 1));
if (nenv == NULL)
goto eexit;
env = nenv;
n = dhcp_env(env + elen, "old",
state->old, state->old_len, ifp);
if (n == -1)
goto eexit;
elen += (size_t)n;
}
if (append_config(&env, &elen, "old",
if (append_config(fp, "old",
(const char *const *)ifo->config) == -1)
goto eexit;
}
#endif
#ifdef DHCP6
if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) {
n = dhcp6_env(NULL, NULL, ifp,
d6_state->old, d6_state->old_len);
if (n > 0) {
nenv = realloc(env, sizeof(char *) *
(elen + (size_t)n + 1));
if (nenv == NULL)
goto eexit;
env = nenv;
n = dhcp6_env(env + elen, "old", ifp,
d6_state->old, d6_state->old_len);
if (n == -1)
goto eexit;
elen += (size_t)n;
}
if (dhcp6_env(fp, "old", ifp,
d6_state->old, d6_state->old_len) == -1)
goto eexit;
}
#endif
@ -492,144 +411,114 @@ dumplease:
#ifdef INET
#ifdef IPV4LL
if (protocol == PROTO_IPV4LL) {
n = ipv4ll_env(NULL, NULL, ifp);
if (n > 0) {
nenv = realloc(env, sizeof(char *) *
(elen + (size_t)n + 1));
if (nenv == NULL)
goto eexit;
env = nenv;
if ((n = ipv4ll_env(env + elen,
istate->down ? "old" : "new", ifp)) == -1)
goto eexit;
elen += (size_t)n;
}
if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1)
goto eexit;
}
#endif
if (protocol == PROTO_DHCP && state && state->new) {
n = dhcp_env(NULL, NULL, state->new, state->new_len, ifp);
if (n > 0) {
nenv = realloc(env, sizeof(char *) *
(elen + (size_t)n + 1));
if (nenv == NULL)
goto eexit;
env = nenv;
n = dhcp_env(env + elen, "new",
state->new, state->new_len, ifp);
if (n == -1)
goto eexit;
elen += (size_t)n;
}
if (append_config(&env, &elen, "new",
if (dhcp_env(fp, "new", ifp,
state->new, state->new_len) == -1)
goto eexit;
if (append_config(fp, "new",
(const char *const *)ifo->config) == -1)
goto eexit;
}
#endif
#ifdef INET6
if (protocol == PROTO_STATIC6) {
n = ipv6_env(NULL, NULL, ifp);
if (n > 0) {
nenv = realloc(env, sizeof(char *) *
(elen + (size_t)n + 1));
if (nenv == NULL)
goto eexit;
env = nenv;
n = ipv6_env(env + elen, "new", ifp);
if (n == -1)
goto eexit;
elen += (size_t)n;
}
if (ipv6_env(fp, "new", ifp) == -1)
goto eexit;
}
#ifdef DHCP6
if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) {
n = dhcp6_env(NULL, NULL, ifp,
d6_state->new, d6_state->new_len);
if (n > 0) {
nenv = realloc(env, sizeof(char *) *
(elen + (size_t)n + 1));
if (nenv == NULL)
goto eexit;
env = nenv;
n = dhcp6_env(env + elen, "new", ifp,
d6_state->new, d6_state->new_len);
if (n == -1)
goto eexit;
elen += (size_t)n;
}
if (dhcp6_env(fp, "new", ifp,
d6_state->new, d6_state->new_len) == -1)
goto eexit;
}
#endif
if (protocol == PROTO_RA) {
n = ipv6nd_env(NULL, NULL, ifp);
if (n > 0) {
nenv = realloc(env, sizeof(char *) *
(elen + (size_t)n + 1));
if (nenv == NULL)
goto eexit;
env = nenv;
n = ipv6nd_env(env + elen, NULL, ifp);
if (n == -1)
goto eexit;
elen += (size_t)n;
}
if (ipv6nd_env(fp, ifp) == -1)
goto eexit;
}
#endif
/* Add our base environment */
if (ifo->environ) {
e = 0;
while (ifo->environ[e++])
;
nenv = realloc(env, sizeof(char *) * (elen + e + 1));
if (nenv == NULL)
goto eexit;
env = nenv;
e = 0;
while (ifo->environ[e]) {
env[elen + e] = strdup(ifo->environ[e]);
if (env[elen + e] == NULL)
for (i = 0; ifo->environ[i] != NULL; i++)
if (efprintf(fp, "%s", ifo->environ[i]) == -1)
goto eexit;
e++;
}
elen += e;
}
env[elen] = NULL;
*argv = env;
return (ssize_t)elen;
/* Convert buffer to argv */
fflush(fp);
buf_pos = ftell(fp);
if (buf_pos == -1) {
logerr(__func__);
goto eexit;
}
#ifdef HAVE_OPEN_MEMSTREAM
buf = ctx->script_buf;
#else
size_t buf_len = (size_t)buf_pos;
if (ctx->script_buflen < buf_len) {
buf = realloc(ctx->script_buf, buf_len);
if (buf == NULL)
goto eexit;
ctx->script_buf = buf;
ctx->script_buflen = buf_len;
}
buf = ctx->script_buf;
rewind(fp);
if (fread(buf, sizeof(char), buf_len, fp) != buf_len)
goto eexit;
fclose(fp);
fp = NULL;
#endif
nenv = 0;
endp = buf + buf_pos;
for (bufp = buf; bufp < endp; bufp++) {
if (*bufp == '\0')
nenv++;
}
if (ctx->script_envlen < nenv) {
env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env));
if (env == NULL)
goto eexit;
ctx->script_env = env;
ctx->script_envlen = nenv;
}
bufp = buf;
envp = ctx->script_env;
*envp++ = bufp++;
endp--; /* Avoid setting the last \0 to an invalid pointer */
for (; bufp < endp; bufp++) {
if (*bufp == '\0')
*envp++ = bufp + 1;
}
*envp = NULL;
return (ssize_t)nenv;
eexit:
logerr(__func__);
if (env) {
nenv = env;
while (*nenv)
free(*nenv++);
free(env);
}
#ifndef HAVE_OPEN_MEMSTREAM
if (fp != NULL)
fclose(fp);
#endif
return -1;
}
static int
send_interface1(struct fd_list *fd, const struct interface *iface,
send_interface1(struct fd_list *fd, const struct interface *ifp,
const char *reason)
{
char **env, **ep, *s;
size_t elen;
int retval;
struct dhcpcd_ctx *ctx = ifp->ctx;
if (make_env(iface, reason, &env) == -1)
if (make_env(ifp, reason) == -1)
return -1;
s = NULL;
elen = (size_t)arraytostr((const char *const *)env, &s);
if ((ssize_t)elen == -1) {
free(s);
retval = -1;
} else
retval = control_queue(fd, s, elen, 1);
ep = env;
while (*ep)
free(*ep++);
free(env);
return retval;
return control_queue(fd, ctx->script_buf, ctx->script_buflen, 1);
}
int
@ -696,10 +585,8 @@ send_interface(struct fd_list *fd, const struct interface *ifp)
int
script_runreason(const struct interface *ifp, const char *reason)
{
struct dhcpcd_ctx *ctx = ifp->ctx;
char *argv[2];
char **env = NULL, **ep;
char *svcname, *path, *bigenv;
size_t e, elen = 0;
pid_t pid;
int status = 0;
struct fd_list *fd;
@ -709,8 +596,7 @@ script_runreason(const struct interface *ifp, const char *reason)
return 0;
/* Make our env */
elen = (size_t)make_env(ifp, reason, &env);
if (elen == (size_t)-1) {
if (make_env(ifp, reason) == -1) {
logerr(__func__);
return -1;
}
@ -722,43 +608,7 @@ script_runreason(const struct interface *ifp, const char *reason)
argv[1] = NULL;
logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason);
/* Resize for PATH and RC_SVCNAME */
svcname = getenv(RC_SVCNAME);
ep = reallocarray(env, elen + 2 + (svcname ? 1 : 0), sizeof(char *));
if (ep == NULL) {
elen = 0;
goto out;
}
env = ep;
/* Add path to it */
path = getenv("PATH");
if (path) {
e = strlen("PATH") + strlen(path) + 2;
env[elen] = malloc(e);
if (env[elen] == NULL) {
elen = 0;
goto out;
}
snprintf(env[elen], e, "PATH=%s", path);
} else {
env[elen] = strdup(DEFAULT_PATH);
if (env[elen] == NULL) {
elen = 0;
goto out;
}
}
if (svcname) {
e = strlen(RC_SVCNAME) + strlen(svcname) + 2;
env[++elen] = malloc(e);
if (env[elen] == NULL) {
elen = 0;
goto out;
}
snprintf(env[elen], e, "%s=%s", RC_SVCNAME, svcname);
}
env[++elen] = NULL;
pid = exec_script(ifp->ctx, argv, env);
pid = exec_script(ctx, argv, ctx->script_env);
if (pid == -1)
logerr("%s: %s", __func__, argv[0]);
else if (pid != 0) {
@ -781,36 +631,16 @@ script_runreason(const struct interface *ifp, const char *reason)
send_listeners:
/* Send to our listeners */
bigenv = NULL;
status = 0;
TAILQ_FOREACH(fd, &ifp->ctx->control_fds, next) {
TAILQ_FOREACH(fd, &ctx->control_fds, next) {
if (!(fd->flags & FD_LISTEN))
continue;
if (bigenv == NULL) {
elen = (size_t)arraytostr((const char *const *)env,
&bigenv);
if ((ssize_t)elen == -1) {
logerr("%s: arraytostr", ifp->name);
break;
}
}
if (control_queue(fd, bigenv, elen, 1) == -1)
if (control_queue(fd, ctx->script_buf, ctx->script_buflen, 1)
== -1)
logerr("%s: control_queue", __func__);
else
status = 1;
}
if (!status)
free(bigenv);
out:
/* Cleanup */
ep = env;
while (*ep)
free(*ep++);
free(env);
if (elen == 0) {
logerr(__func__);
return -1;
}
return WEXITSTATUS(status);
}

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
@ -30,6 +31,7 @@
#include "control.h"
__printflike(2, 3) int efprintf(FILE *, const char *, ...);
void if_printoptions(void);
int send_interface(struct fd_list *, const struct interface *);
int script_runreason(const struct interface *, const char *);