Import dhcpcd-8.0.2 with the following changes:

*  NetBSD: Can be build without ARP support but listen to kernel DaD
  *  ND6: Removed NA support from SMALL builds
  *  DHCP: Avoid duplicate read of UDP socket when BPF is also open
  *  IP: Avoid adding address if already exists on OS other than Linux
  *  route: Fixed a NULL de-reference error on static routes
  *  DHCP6: Move to REQUEST if any IA has no-binding in REWNEW/REBIND
  *  IP: Accept packets with IP header options
This commit is contained in:
roy 2019-07-30 10:23:02 +00:00
parent cb0b7d2b9f
commit b6a3a94eb1
18 changed files with 313 additions and 173 deletions

View File

@ -1,7 +1,9 @@
# Start, reconfigure and stop wpa_supplicant per wireless interface.
# This is needed because wpa_supplicant lacks hotplugging of any kind
# and the user should not be expected to have to wire it into their system
# if the base system doesn't do this itself.
#
# This is only needed when using wpa_supplicant-2.5 or older, OR
# when wpa_supplicant has not been built with CONFIG_MATCH_IFACE, OR
# wpa_supplicant was launched without the -M flag to activate
# interface matching.
if [ -z "$wpa_supplicant_conf" ]; then
for x in \

View File

@ -93,7 +93,7 @@ bpf_frame_header_len(const struct interface *ifp)
}
}
static const uint8_t etherbroadcastaddr[] =
static const uint8_t etherbcastaddr[] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
int
@ -104,7 +104,7 @@ bpf_frame_bcast(const struct interface *ifp, const char *frame)
case ARPHRD_ETHER:
return memcmp(frame +
offsetof(struct ether_header, ether_dhost),
etherbroadcastaddr, sizeof(etherbroadcastaddr));
etherbcastaddr, sizeof(etherbcastaddr));
default:
return -1;
}
@ -552,6 +552,12 @@ bpf_arp(struct interface *ifp, int fd)
}
#endif
#define BPF_M_FHLEN 0
#define BPF_M_IPHLEN 1
#define BPF_M_IPLEN 2
#define BPF_M_UDP 3
#define BPF_M_UDPLEN 4
static const struct bpf_insn bpf_bootp_ether[] = {
/* Make sure this is an IP packet. */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
@ -561,17 +567,27 @@ static const struct bpf_insn bpf_bootp_ether[] = {
/* Load frame header length into X. */
BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
/* Copy to M0. */
BPF_STMT(BPF_STX, 0),
/* Copy frame header length to memory */
BPF_STMT(BPF_STX, BPF_M_FHLEN),
};
#define BPF_BOOTP_ETHER_LEN __arraycount(bpf_bootp_ether)
static const struct bpf_insn bpf_bootp_filter[] = {
/* Make sure it's an optionless IPv4 packet. */
/* Make sure it's an IPv4 packet. */
BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x45, 1, 0),
BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0xf0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x40, 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
/* Ensure IP header length is big enough and
* store the IP header length in memory. */
BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x0f),
BPF_STMT(BPF_ALU + BPF_MUL + BPF_K, 4),
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ip), 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
BPF_STMT(BPF_ST, BPF_M_IPHLEN),
/* Make sure it's a UDP packet. */
BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct ip, ip_p)),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),
@ -582,39 +598,42 @@ static const struct bpf_insn bpf_bootp_filter[] = {
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1),
BPF_STMT(BPF_RET + BPF_K, 0),
/* Store IP location in M1. */
/* Store IP length. */
BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_len)),
BPF_STMT(BPF_ST, 1),
/* Store IP length in M2. */
BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_len)),
BPF_STMT(BPF_ST, 2),
BPF_STMT(BPF_ST, BPF_M_IPLEN),
/* Advance to the UDP header. */
BPF_STMT(BPF_MISC + BPF_TXA, 0),
BPF_STMT(BPF_ALU + BPF_ADD + BPF_K, sizeof(struct ip)),
BPF_STMT(BPF_LD + BPF_MEM, BPF_M_IPHLEN),
BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),
BPF_STMT(BPF_MISC + BPF_TAX, 0),
/* Store X in M3. */
BPF_STMT(BPF_STX, 3),
/* Store UDP location */
BPF_STMT(BPF_STX, BPF_M_UDP),
/* Make sure it's from and to the right port. */
BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPS << 16) + BOOTPC, 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
/* Store UDP length in X. */
/* Store UDP length. */
BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct udphdr, uh_ulen)),
BPF_STMT(BPF_ST, BPF_M_UDPLEN),
/* Ensure that UDP length + IP header length == IP length */
/* Copy IP header length to X. */
BPF_STMT(BPF_LDX + BPF_MEM, BPF_M_IPHLEN),
/* Add UDP length (A) to IP header length (X). */
BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),
/* Store result in X. */
BPF_STMT(BPF_MISC + BPF_TAX, 0),
/* Copy IP length in M2 to A. */
BPF_STMT(BPF_LD + BPF_MEM, 2),
/* Ensure IP length - IP header size == UDP length. */
BPF_STMT(BPF_ALU + BPF_SUB + BPF_K, sizeof(struct ip)),
/* Copy IP length to A. */
BPF_STMT(BPF_LD + BPF_MEM, BPF_M_IPLEN),
/* Ensure X == A. */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
/* Advance to the BOOTP packet (UDP X is in M3). */
BPF_STMT(BPF_LD + BPF_MEM, 3),
/* Advance to the BOOTP packet. */
BPF_STMT(BPF_LD + BPF_MEM, BPF_M_UDP),
BPF_STMT(BPF_ALU + BPF_ADD + BPF_K, sizeof(struct udphdr)),
BPF_STMT(BPF_MISC + BPF_TAX, 0),
@ -695,11 +714,10 @@ bpf_bootp(struct interface *ifp, int fd)
}
#endif
/* All passed, return the packet
* (Frame length in M0, IP length in M2). */
BPF_SET_STMT(bp, BPF_LD + BPF_MEM, 0);
/* All passed, return the packet - frame length + ip length */
BPF_SET_STMT(bp, BPF_LD + BPF_MEM, BPF_M_FHLEN);
bp++;
BPF_SET_STMT(bp, BPF_LDX + BPF_MEM, 2);
BPF_SET_STMT(bp, BPF_LDX + BPF_MEM, BPF_M_IPLEN);
bp++;
BPF_SET_STMT(bp, BPF_ALU + BPF_ADD + BPF_X, 0);
bp++;

View File

@ -29,7 +29,7 @@
#define CONFIG_H
#define PACKAGE "dhcpcd"
#define VERSION "8.0.1"
#define VERSION "8.0.2"
#ifndef CONFIG
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"

View File

@ -2329,7 +2329,9 @@ dhcp_arp_new(struct interface *ifp, struct in_addr *addr)
return astate;
}
#endif
#endif /* ARP */
#if defined(ARP) || defined(KERNEL_RFC5227)
static int
dhcp_arp_address(struct interface *ifp)
{
@ -2417,7 +2419,7 @@ dhcp_static(struct interface *ifp)
ia ? &ia->addr : &ifo->req_addr,
ia ? &ia->mask : &ifo->req_mask);
if (state->offer_len)
#ifdef ARP
#if defined(ARP) || defined(KERNEL_RFC5227)
dhcp_arp_bind(ifp);
#else
dhcp_bind(ifp);
@ -3210,7 +3212,7 @@ rapidcommit:
lease->frominfo = 0;
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
#ifdef ARP
#if defined(ARP) || defined(KERNEL_RFC5227)
dhcp_arp_bind(ifp);
#else
dhcp_bind(ifp);
@ -3218,70 +3220,80 @@ rapidcommit:
}
static void *
get_udp_data(void *udp, size_t *len)
get_udp_data(void *packet, size_t *len)
{
struct bootp_pkt *p;
const struct ip *ip = packet;
size_t ip_hl = (size_t)ip->ip_hl * 4;
char *p = packet;
p = (struct bootp_pkt *)udp;
*len = (size_t)ntohs(p->ip.ip_len) - sizeof(p->ip) - sizeof(p->udp);
return (char *)udp + offsetof(struct bootp_pkt, bootp);
p += ip_hl + sizeof(struct udphdr);
*len = (size_t)ntohs(ip->ip_len) - sizeof(struct udphdr) - ip_hl;
return p;
}
static int
valid_udp_packet(void *data, size_t data_len, struct in_addr *from,
int noudpcsum)
valid_udp_packet(void *packet, size_t plen, struct in_addr *from,
unsigned int flags)
{
struct bootp_pkt *p;
uint16_t bytes;
struct ip *ip = packet;
char ip_hlv = *(char *)ip;
size_t ip_hlen;
uint16_t ip_len, uh_sum;
struct udphdr *udp;
if (data_len < sizeof(p->ip)) {
if (from)
if (plen < sizeof(*ip)) {
if (from != NULL)
from->s_addr = INADDR_ANY;
errno = ERANGE;
return -1;
}
p = (struct bootp_pkt *)data;
if (from)
from->s_addr = p->ip.ip_src.s_addr;
if (checksum(&p->ip, sizeof(p->ip)) != 0) {
if (from != NULL)
from->s_addr = ip->ip_src.s_addr;
ip_hlen = (size_t)ip->ip_hl * 4;
if (checksum(ip, ip_hlen) != 0) {
errno = EINVAL;
return -1;
}
bytes = ntohs(p->ip.ip_len);
ip_len = ntohs(ip->ip_len);
/* Check we have a payload */
if (bytes <= sizeof(p->ip) + sizeof(p->udp)) {
if (ip_len <= ip_hlen + sizeof(*udp)) {
errno = ERANGE;
return -1;
}
/* Check we don't go beyond the payload */
if (bytes > data_len) {
if (ip_len > plen) {
errno = ENOBUFS;
return -1;
}
if (noudpcsum == 0) {
uint16_t udpsum, iplen;
if (flags & BPF_PARTIALCSUM)
return 0;
/* This does scribble on the packet, but at this point
* we don't care to keep it. */
iplen = p->ip.ip_len;
udpsum = p->udp.uh_sum;
p->udp.uh_sum = 0;
p->ip.ip_hl = 0;
p->ip.ip_v = 0;
p->ip.ip_tos = 0;
p->ip.ip_len = p->udp.uh_ulen;
p->ip.ip_id = 0;
p->ip.ip_off = 0;
p->ip.ip_ttl = 0;
p->ip.ip_sum = 0;
if (udpsum && checksum(p, bytes) != udpsum) {
errno = EINVAL;
return -1;
}
p->ip.ip_len = iplen;
udp = (struct udphdr *)((char *)ip + ip_hlen);
if (udp->uh_sum == 0)
return 0;
uh_sum = udp->uh_sum;
/* This does scribble on the packet, but at this point
* we don't care to keep it. */
udp->uh_sum = 0;
ip->ip_hl = 0;
ip->ip_v = 0;
ip->ip_tos = 0;
ip->ip_len = udp->uh_ulen;
ip->ip_id = 0;
ip->ip_off = 0;
ip->ip_ttl = 0;
ip->ip_sum = 0;
if (checksum(packet, ip_len) != uh_sum) {
errno = EINVAL;
return -1;
}
*(char *)ip = ip_hlv;
ip->ip_len = htons(ip_len);
return 0;
}
@ -3318,9 +3330,7 @@ dhcp_handlepacket(struct interface *ifp, uint8_t *data, size_t len)
size_t udp_len;
const struct dhcp_state *state = D_CSTATE(ifp);
if (valid_udp_packet(data, len, &from,
state->bpf_flags & RAW_PARTIALCSUM) == -1)
{
if (valid_udp_packet(data, len, &from, state->bpf_flags) == -1) {
if (errno == EINVAL)
logerrx("%s: checksum failure from %s",
ifp->name, inet_ntoa(from));
@ -3382,6 +3392,7 @@ dhcp_readpacket(void *arg)
static void
dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp)
{
const struct dhcp_state *state;
struct sockaddr_in from;
unsigned char buf[10 * 1024]; /* Maximum MTU */
struct iovec iov = {
@ -3403,8 +3414,7 @@ dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp)
ssize_t bytes;
if (ifp != NULL) {
const struct dhcp_state *state = D_CSTATE(ifp);
state = D_CSTATE(ifp);
s = state->udp_fd;
} else
s = ctx->udp_fd;
@ -3424,13 +3434,19 @@ dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp)
logerr(__func__);
return;
}
if (D_CSTATE(ifp) == NULL) {
state = D_CSTATE(ifp);
if (state == NULL) {
logdebugx("%s: received BOOTP for inactive interface",
ifp->name);
return;
}
}
if (state->bpf_fd != -1) {
/* Avoid a duplicate read if BPF is open for the interface. */
return;
}
dhcp_handlebootp(ifp, (struct bootp *)(void *)buf, (size_t)bytes,
&from.sin_addr);
#endif

View File

@ -1523,7 +1523,9 @@ dhcp6_dadcallback(void *arg)
if (valid)
dhcpcd_daemonise(ifp->ctx);
}
#ifdef ND6_ADVERTISE
ipv6nd_advertise(ia);
#endif
}
}
}
@ -1936,6 +1938,7 @@ dhcp6_checkstatusok(const struct interface *ifp,
if ((opt = f(farg, len, D6_OPTION_STATUS_CODE, &opt_len)) == NULL) {
//logdebugx("%s: no status", ifp->name);
state->lerror = 0;
errno = ESRCH;
return 0;
}
@ -1947,7 +1950,8 @@ dhcp6_checkstatusok(const struct interface *ifp,
code = ntohs(code);
if (code == D6_STATUS_OK) {
state->lerror = 0;
return 1;
errno = 0;
return 0;
}
/* Anything after the code is a message. */
@ -1978,7 +1982,8 @@ dhcp6_checkstatusok(const struct interface *ifp,
logfunc("%s: DHCPv6 REPLY: %s", ifp->name, status);
free(sbuf);
state->lerror = code;
return -1;
errno = 0;
return (int)code;
}
const struct ipv6_addr *
@ -2225,7 +2230,7 @@ dhcp6_findia(struct interface *ifp, struct dhcp6_message *m, size_t l,
struct dhcp6_option o;
uint8_t *d, *p;
struct dhcp6_ia_na ia;
int i, e;
int i, e, error;
size_t j;
uint16_t nl;
uint8_t iaid[4];
@ -2314,7 +2319,9 @@ dhcp6_findia(struct interface *ifp, struct dhcp6_message *m, size_t l,
}
} else
ia.t1 = ia.t2 = 0; /* appease gcc */
if (dhcp6_checkstatusok(ifp, NULL, p, o.len) == -1) {
if ((error = dhcp6_checkstatusok(ifp, NULL, p, o.len)) != 0) {
if (error == D6_STATUS_NOBINDING)
state->has_no_binding = true;
e = 1;
continue;
}
@ -2415,7 +2422,7 @@ dhcp6_validatelease(struct interface *ifp,
const char *sfrom, const struct timespec *acquired)
{
struct dhcp6_state *state;
int ok, nia;
int nia, ok_errno;
struct timespec aq;
if (len <= sizeof(*m)) {
@ -2424,8 +2431,10 @@ dhcp6_validatelease(struct interface *ifp,
}
state = D6_STATE(ifp);
if ((ok = dhcp6_checkstatusok(ifp, m, NULL, len) == -1))
errno = 0;
if (dhcp6_checkstatusok(ifp, m, NULL, len) != 0)
return -1;
ok_errno = errno;
state->renew = state->rebind = state->expire = 0;
state->lowpl = ND6_INFINITE_LIFETIME;
@ -2433,9 +2442,10 @@ dhcp6_validatelease(struct interface *ifp,
clock_gettime(CLOCK_MONOTONIC, &aq);
acquired = &aq;
}
state->has_no_binding = false;
nia = dhcp6_findia(ifp, m, len, sfrom, acquired);
if (nia == 0) {
if (state->state != DH6S_CONFIRM && ok != 1) {
if (state->state != DH6S_CONFIRM && ok_errno != 0) {
logerrx("%s: no useable IA found in lease", ifp->name);
return -1;
}
@ -2445,6 +2455,7 @@ dhcp6_validatelease(struct interface *ifp,
* IA's must have existed here otherwise we would
* have rejected it earlier. */
assert(state->new != NULL && state->new_len != 0);
state->has_no_binding = false;
nia = dhcp6_findia(ifp, state->new, state->new_len,
sfrom, acquired);
}
@ -3267,7 +3278,7 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom,
case DHCP6_REPLY:
switch(state->state) {
case DH6S_INFORM:
if (dhcp6_checkstatusok(ifp, r, NULL, len) == -1)
if (dhcp6_checkstatusok(ifp, r, NULL, len) != 0)
return;
break;
case DH6S_CONFIRM:
@ -3315,6 +3326,14 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom,
dhcp6_startdiscover(ifp);
return;
}
/* RFC8415 18.2.10.1 */
if ((state->state == DH6S_RENEW ||
state->state == DH6S_REBIND) &&
state->has_no_binding)
{
dhcp6_startrequest(ifp);
return;
}
if (state->state == DH6S_DISCOVER)
state->state = DH6S_REQUEST;
break;
@ -3927,16 +3946,20 @@ dhcp6_free(struct interface *ifp)
void
dhcp6_abort(struct interface *ifp)
{
#ifdef ND6_ADVERTISE
struct dhcp6_state *state;
struct ipv6_addr *ia;
#endif
eloop_timeout_delete(ifp->ctx->eloop, dhcp6_start1, ifp);
#ifdef ND6_ADVERTISE
state = D6_STATE(ifp);
if (state == NULL)
return;
TAILQ_FOREACH(ia, &state->addrs, next) {
ipv6nd_advertise(ia);
}
#endif
}
void

View File

@ -208,6 +208,7 @@ struct dhcp6_state {
char leasefile[sizeof(LEASEFILE6) + IF_NAMESIZE + (IF_SSIDLEN * 4) +3];
const char *reason;
uint16_t lerror; /* Last error received from DHCPv6 reply. */
bool has_no_binding;
struct authstate auth;
};

View File

@ -24,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd July 23, 2019
.Dd July 25, 2019
.Dt DHCPCD 8
.Os
.Sh NAME
@ -234,12 +234,11 @@ and need to be copied to
.Pa @HOOKDIR@
if you intend to use them.
For example, you could install
.Pa 10-wpa_supplicant
.Pa 29-lookup-hostname
so that
.Nm
can ensure that
.Xr wpa_supplicant 8
is always running on a hot-plugged wireless interface.
can lookup the hostname of the IP address in DNS if no hostname
is given by the lease and one is not already set.
.Ss Fine tuning
You can fine-tune the behaviour of
.Nm
@ -404,10 +403,6 @@ is specified then this applies to all interfaces in Master mode.
If
.Nm
is not running, then it starts up as normal.
This may also cause
.Xr wpa_supplicant 8
to reload its configuration for each interface as well if the
relevant hook script has been installed.
.It Fl N , Fl Fl renew Op Ar interface
Notifies
.Nm

View File

@ -2125,13 +2125,13 @@ exit1:
}
free(ctx.ifaces);
}
free_options(&ctx, ifo);
#ifdef HAVE_OPEN_MEMSTREAM
if (ctx.script_fp)
fclose(ctx.script_fp);
#endif
free(ctx.script_buf);
free(ctx.script_env);
free_options(&ctx, ifo);
rt_dispose(&ctx);
free(ctx.duid);
if (ctx.link_fd != -1) {

View File

@ -51,6 +51,8 @@
#include <netinet6/nd6.h>
#ifdef __NetBSD__
#include <net/if_vlanvar.h> /* Needs netinet/if_ether.h */
#elif defined(__DragonFly__)
#include <net/vlan/if_vlan_var.h>
#else
#include <net/if_vlan_var.h>
#endif
@ -541,7 +543,7 @@ if_route(unsigned char cmd, const struct rt *rt)
* try to encourage someone to fix that by logging a waring during compile.
*/
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#warning OS does not allow IPv6 address sharing
#warning kernel does not allow IPv6 address sharing
if (!gateway_unspec || rt->rt_dest.sa_family!=AF_INET6)
#endif
rtm->rtm_addrs |= RTA_IFP;

View File

@ -2611,73 +2611,92 @@ void
free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo)
{
size_t i;
#ifdef RT_FREE_ROUTE_TABLE
struct interface *ifp;
struct rt *rt;
#endif
struct dhcp_opt *opt;
struct vivco *vo;
#ifdef AUTH
struct token *token;
#endif
if (ifo) {
if (ifo->environ) {
i = 0;
while (ifo->environ[i])
free(ifo->environ[i++]);
free(ifo->environ);
}
if (ifo->config) {
i = 0;
while (ifo->config[i])
free(ifo->config[i++]);
free(ifo->config);
}
rt_headclear0(ctx, &ifo->routes, AF_UNSPEC);
if (ifo->script != default_script)
free(ifo->script);
free(ifo->arping);
free(ifo->blacklist);
free(ifo->fallback);
if (ifo == NULL)
return;
for (opt = ifo->dhcp_override;
ifo->dhcp_override_len > 0;
opt++, ifo->dhcp_override_len--)
free_dhcp_opt_embenc(opt);
free(ifo->dhcp_override);
for (opt = ifo->nd_override;
ifo->nd_override_len > 0;
opt++, ifo->nd_override_len--)
free_dhcp_opt_embenc(opt);
free(ifo->nd_override);
for (opt = ifo->dhcp6_override;
ifo->dhcp6_override_len > 0;
opt++, ifo->dhcp6_override_len--)
free_dhcp_opt_embenc(opt);
free(ifo->dhcp6_override);
for (vo = ifo->vivco;
ifo->vivco_len > 0;
vo++, ifo->vivco_len--)
free(vo->data);
free(ifo->vivco);
for (opt = ifo->vivso_override;
ifo->vivso_override_len > 0;
opt++, ifo->vivso_override_len--)
free_dhcp_opt_embenc(opt);
free(ifo->vivso_override);
if (ifo->environ) {
i = 0;
while (ifo->environ[i])
free(ifo->environ[i++]);
free(ifo->environ);
}
if (ifo->config) {
i = 0;
while (ifo->config[i])
free(ifo->config[i++]);
free(ifo->config);
}
#ifdef RT_FREE_ROUTE_TABLE
/* Stupidly, we don't know the interface when creating the options.
* As such, make sure each route has one so they can goto the
* free list. */
ifp = ctx->ifaces != NULL ? TAILQ_FIRST(ctx->ifaces) : NULL;
if (ifp != NULL) {
RB_TREE_FOREACH(rt, &ifo->routes) {
if (rt->rt_ifp == NULL)
rt->rt_ifp = ifp;
}
}
#endif
rt_headclear0(ctx, &ifo->routes, AF_UNSPEC);
if (ifo->script != default_script)
free(ifo->script);
free(ifo->arping);
free(ifo->blacklist);
free(ifo->fallback);
for (opt = ifo->dhcp_override;
ifo->dhcp_override_len > 0;
opt++, ifo->dhcp_override_len--)
free_dhcp_opt_embenc(opt);
free(ifo->dhcp_override);
for (opt = ifo->nd_override;
ifo->nd_override_len > 0;
opt++, ifo->nd_override_len--)
free_dhcp_opt_embenc(opt);
free(ifo->nd_override);
for (opt = ifo->dhcp6_override;
ifo->dhcp6_override_len > 0;
opt++, ifo->dhcp6_override_len--)
free_dhcp_opt_embenc(opt);
free(ifo->dhcp6_override);
for (vo = ifo->vivco;
ifo->vivco_len > 0;
vo++, ifo->vivco_len--)
free(vo->data);
free(ifo->vivco);
for (opt = ifo->vivso_override;
ifo->vivso_override_len > 0;
opt++, ifo->vivso_override_len--)
free_dhcp_opt_embenc(opt);
free(ifo->vivso_override);
#if defined(INET6) && !defined(SMALL)
for (; ifo->ia_len > 0; ifo->ia_len--)
free(ifo->ia[ifo->ia_len - 1].sla);
for (; ifo->ia_len > 0; ifo->ia_len--)
free(ifo->ia[ifo->ia_len - 1].sla);
#endif
free(ifo->ia);
free(ifo->ia);
#ifdef AUTH
while ((token = TAILQ_FIRST(&ifo->auth.tokens))) {
TAILQ_REMOVE(&ifo->auth.tokens, token, next);
if (token->realm_len)
free(token->realm);
free(token->key);
free(token);
}
#endif
free(ifo);
while ((token = TAILQ_FIRST(&ifo->auth.tokens))) {
TAILQ_REMOVE(&ifo->auth.tokens, token, next);
if (token->realm_len)
free(token->realm);
free(token->key);
free(token);
}
#endif
free(ifo);
}

View File

@ -91,9 +91,6 @@
((addr & IN_CLASSB_NET) == 0xc0a80000))
#endif
#define RAW_EOF 1 << 0
#define RAW_PARTIALCSUM 2 << 0
#ifndef CLLADDR
#ifdef AF_LINK
# define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen)

View File

@ -659,8 +659,13 @@ ipv4_addaddr(struct interface *ifp, const struct in_addr *addr,
ia->mask = *mask;
ia->brd = *bcast;
#ifdef IP_LIFETIME
ia->vltime = vltime;
ia->pltime = pltime;
#else
UNUSED(vltime);
UNUSED(pltime);
#endif
snprintf(ia->saddr, sizeof(ia->saddr), "%s/%d",
inet_ntoa(*addr), inet_ntocidr(*mask));
@ -746,16 +751,36 @@ 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 the netmask or broadcast is different, re-add the addresss.
* If IP addresses do not have lifetimes, there is a very real chance
* that re-adding them will scrub the subnet route temporarily
* which is a bad thing, so avoid it. */
if (ia != NULL &&
(ia->mask.s_addr != lease->mask.s_addr ||
ia->brd.s_addr != lease->brd.s_addr))
ipv4_deladdr(ia, 0);
ia->mask.s_addr == lease->mask.s_addr &&
ia->brd.s_addr == lease->brd.s_addr)
{
#ifndef IP_LIFETIME
logdebugx("%s: IP address %s already exists",
ifp->name, ia->saddr);
#endif
} else {
#ifdef __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);
#endif
#ifndef IP_LIFETIME
if (ipv4_daddaddr(ifp, lease) == -1 && errno != EEXIST)
return;
#endif
}
#ifdef IP_LIFETIME
if (ipv4_daddaddr(ifp, lease) == -1 && errno != EEXIST)
return;
#endif
ia = ipv4_iffindaddr(ifp, &lease->addr, NULL);
if (ia == NULL) {

View File

@ -75,6 +75,10 @@
#define IN_ARE_ADDR_EQUAL(a, b) ((a)->s_addr == (b)->s_addr)
#define IN_IS_ADDR_UNSPECIFIED(a) ((a)->s_addr == INADDR_ANY)
#ifdef __linux__
#define IP_LIFETIME
#endif
struct ipv4_addr {
TAILQ_ENTRY(ipv4_addr) next;
struct in_addr addr;
@ -83,8 +87,10 @@ struct ipv4_addr {
struct interface *iface;
int addr_flags;
unsigned int flags;
#ifdef IP_LIFETIME
uint32_t vltime;
uint32_t pltime;
#endif
char saddr[INET_ADDRSTRLEN + 3];
#ifdef ALIAS_ADDR
char alias[IF_NAMESIZE];

View File

@ -616,8 +616,10 @@ ipv6_deleteaddr(struct ipv6_addr *ia)
}
}
#ifdef ND6_ADVERTISE
/* Advertise the address if it exists on another interface. */
ipv6nd_advertise(ia);
#endif
}
static int
@ -625,11 +627,22 @@ ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now)
{
struct interface *ifp;
uint32_t pltime, vltime;
bool vltime_was_zero;
__printflike(1, 2) void (*logfunc)(const char *, ...);
#ifdef ND6_ADVERTISE
bool vltime_was_zero;
#endif
#ifdef __sun
struct ipv6_state *state;
struct ipv6_addr *ia2;
/* If we re-add then address on Solaris then the prefix
* route will be scrubbed and re-added. Something might
* be using it, so let's avoid it. */
if (ia->flags & IPV6_AF_DADCOMPLETED) {
logdebugx("%s: IP address %s already exists",
ia->iface->name, ia->saddr);
return 0;
}
#endif
/* Remember the interface of the address. */
@ -694,7 +707,9 @@ ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now)
" seconds",
ifp->name, ia->prefix_pltime, ia->prefix_vltime);
#ifdef ND6_ADVERTISE
vltime_was_zero = ia->prefix_vltime == 0;
#endif
if (if_address6(RTM_NEWADDR, ia) == -1) {
logerr(__func__);
/* Restore real pltime and vltime */
@ -758,9 +773,11 @@ ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now)
}
#endif
#ifdef ND6_ADVERTISE
/* Re-advertise the preferred address to be safe. */
if (!vltime_was_zero)
ipv6nd_advertise(ia);
#endif
return 0;
}
@ -1081,9 +1098,11 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
case RTM_DELADDR:
if (ia != NULL) {
TAILQ_REMOVE(&state->addrs, ia, next);
#ifdef ND6_ADVERTISE
/* Advertise the address if it exists on
* another interface. */
ipv6nd_advertise(ia);
#endif
/* We'll free it at the end of the function. */
}
break;
@ -1177,6 +1196,8 @@ out:
/* Done with the ia now, so free it. */
if (cmd == RTM_DELADDR)
ipv6_freeaddr(ia);
else if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE))
ia->flags |= IPV6_AF_DADCOMPLETED;
}
int
@ -1455,8 +1476,10 @@ ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr,
goto err;
ia->iface = ifp;
ia->flags = IPV6_AF_NEW | flags;
ia->addr_flags = addr_flags;
ia->flags = IPV6_AF_NEW | flags;
if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE))
ia->flags |= IPV6_AF_DADCOMPLETED;
ia->prefix_len = prefix_len;
ia->dhcp6_fd = -1;

View File

@ -149,6 +149,16 @@
# define IN6_IFF_DETACHED 0
#endif
/*
* ND6 Advertising is only used for IP address sharing to prefer
* the address on a specific interface.
* This just fails to work on OpenBSD and causes erroneous duplicate
* address messages on BSD's other then NetBSD.
*/
#if !defined(SMALL) && (defined(__NetBSD__) || defined(__linux__))
# define ND6_ADVERTISE
#endif
#ifdef INET6
TAILQ_HEAD(ipv6_addrhead, ipv6_addr);
struct ipv6_addr {

View File

@ -389,6 +389,7 @@ ipv6nd_sendrsprobe(void *arg)
}
}
#ifdef ND6_ADVERTISE
static void
ipv6nd_sendadvertisement(void *arg)
{
@ -433,13 +434,7 @@ ipv6nd_sendadvertisement(void *arg)
s = ctx->nd_fd;
#endif
if (sendmsg(s, &msg, 0) == -1)
#ifdef __OpenBSD__
/* This isn't too critical as they don't support IPv6 address sharing */
#warning Cannot send NA messages on OpenBSD
logdebug(__func__);
#else
logerr(__func__);
#endif
if (++ia->na_count < MAX_NEIGHBOR_ADVERTISEMENT) {
eloop_timeout_add_sec(ctx->eloop,
@ -526,6 +521,9 @@ ipv6nd_advertise(struct ipv6_addr *ia)
eloop_timeout_delete(ctx->eloop, ipv6nd_sendadvertisement, iaf);
ipv6nd_sendadvertisement(iaf);
}
#elif !defined(SMALL)
#warning kernel does not support userland sending ND6 advertisements
#endif /* ND6_ADVERTISE */
static void
ipv6nd_expire(void *arg)
@ -908,7 +906,9 @@ try_script:
return;
}
}
#ifdef ND6_ADVERTISE
ipv6nd_advertise(ia);
#endif
}
}

View File

@ -392,8 +392,10 @@ rt_free(struct rt *rt)
struct dhcpcd_ctx *ctx;
assert(rt != NULL);
assert(rt->rt_ifp != NULL);
assert(rt->rt_ifp->ctx != NULL);
if (rt->rt_ifp == NULL) {
free(rt);
return;
}
ctx = rt->rt_ifp->ctx;
rb_tree_insert_node(&ctx->froutes, rt);

View File

@ -213,10 +213,11 @@ make_env(const struct interface *ifp, const char *reason)
if (tmpfd == -1)
goto eexit;
unlink(tmpfile);
fp = fopen(tmpfile, "w+");
close(tmpfd);
if (fp == NULL)
fp = fdopen(tmpfd, "w+");
if (fp == NULL) {
close(tmpfd);
goto eexit;
}
#endif
#ifdef INET