Import dhcpcd-8.1.2 with the following changes:
* hooks: STOPPED is now run on timeout and exit * BSD: Use IP_REVCIF rather than IN_PKTINFO * DHCP: When rebinding, ensure we have a DHCP ARP state * RA: Sort routers when reachability changes * RA: Apply hoplimit, reachable and retrans timer values to kernel * RA: Warn if advertised MTU > interface MTU * dhcpcd: Report SSID connection to when we gain carrier * DHCP: Fix corruption of address flags when renewing
This commit is contained in:
parent
40ee987d84
commit
8105b4b2ec
|
@ -206,7 +206,6 @@ arp_validate(const struct interface *ifp, struct arphdr *arp)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
arp_packet(struct interface *ifp, uint8_t *data, size_t len)
|
||||
{
|
||||
|
@ -280,9 +279,9 @@ arp_close(struct interface *ifp)
|
|||
}
|
||||
|
||||
static void
|
||||
arp_tryfree(struct interface *ifp)
|
||||
arp_tryfree(struct iarp_state *state)
|
||||
{
|
||||
struct iarp_state *state = ARP_STATE(ifp);
|
||||
struct interface *ifp = state->ifp;
|
||||
|
||||
/* If there are no more ARP states, close the socket. */
|
||||
if (TAILQ_FIRST(&state->arp_states) == NULL) {
|
||||
|
@ -302,15 +301,14 @@ arp_tryfree(struct interface *ifp)
|
|||
static void
|
||||
arp_read(void *arg)
|
||||
{
|
||||
struct interface *ifp = arg;
|
||||
struct iarp_state *state;
|
||||
struct iarp_state *state = arg;
|
||||
struct interface *ifp = state->ifp;
|
||||
uint8_t buf[ARP_LEN];
|
||||
ssize_t bytes;
|
||||
|
||||
/* Some RAW mechanisms are generic file descriptors, not sockets.
|
||||
* This means we have no kernel call to just get one packet,
|
||||
* so we have to process the entire buffer. */
|
||||
state = ARP_STATE(ifp);
|
||||
state->bpf_flags &= ~BPF_EOF;
|
||||
state->bpf_flags |= BPF_READING;
|
||||
while (!(state->bpf_flags & BPF_EOF)) {
|
||||
|
@ -329,7 +327,7 @@ arp_read(void *arg)
|
|||
if (state != NULL) {
|
||||
state->bpf_flags &= ~BPF_READING;
|
||||
/* Try and free the state if nothing left to do. */
|
||||
arp_tryfree(ifp);
|
||||
arp_tryfree(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,7 +341,7 @@ arp_open(struct interface *ifp)
|
|||
state->bpf_fd = bpf_open(ifp, bpf_arp);
|
||||
if (state->bpf_fd == -1)
|
||||
return -1;
|
||||
eloop_event_add(ifp->ctx->eloop, state->bpf_fd, arp_read, ifp);
|
||||
eloop_event_add(ifp->ctx->eloop, state->bpf_fd, arp_read, state);
|
||||
}
|
||||
return state->bpf_fd;
|
||||
}
|
||||
|
@ -571,6 +569,7 @@ arp_new(struct interface *ifp, const struct in_addr *addr)
|
|||
logerr(__func__);
|
||||
return NULL;
|
||||
}
|
||||
state->ifp = ifp;
|
||||
state->bpf_fd = -1;
|
||||
state->bpf_flags = 0;
|
||||
TAILQ_INIT(&state->arp_states);
|
||||
|
@ -618,7 +617,7 @@ arp_free(struct arp_state *astate)
|
|||
if (astate->free_cb)
|
||||
astate->free_cb(astate);
|
||||
free(astate);
|
||||
arp_tryfree(ifp);
|
||||
arp_tryfree(state);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -78,6 +78,7 @@ struct arp_state {
|
|||
TAILQ_HEAD(arp_statehead, arp_state);
|
||||
|
||||
struct iarp_state {
|
||||
struct interface *ifp;
|
||||
int bpf_fd;
|
||||
unsigned int bpf_flags;
|
||||
struct arp_statehead arp_states;
|
||||
|
|
|
@ -274,9 +274,8 @@ control_stop(struct dhcpcd_ctx *ctx)
|
|||
|
||||
if (ctx->control_fd == -1)
|
||||
return 0;
|
||||
eloop_event_delete(ctx->eloop, ctx->control_fd);
|
||||
close(ctx->control_fd);
|
||||
ctx->control_fd = -1;
|
||||
|
||||
control_close(ctx);
|
||||
if (unlink(ctx->control_sock) == -1)
|
||||
retval = -1;
|
||||
|
||||
|
@ -455,6 +454,7 @@ control_close(struct dhcpcd_ctx *ctx)
|
|||
{
|
||||
|
||||
if (ctx->control_fd != -1) {
|
||||
eloop_event_delete(ctx->eloop, ctx->control_fd);
|
||||
close(ctx->control_fd);
|
||||
ctx->control_fd = -1;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#define CONFIG_H
|
||||
|
||||
#define PACKAGE "dhcpcd"
|
||||
#define VERSION "8.1.1"
|
||||
#define VERSION "8.1.2"
|
||||
|
||||
#ifndef CONFIG
|
||||
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"
|
||||
|
|
|
@ -41,6 +41,10 @@
|
|||
#include <netinet/udp.h>
|
||||
#undef __FAVOR_BSD
|
||||
|
||||
#ifdef AF_LINK
|
||||
# include <net/if_dl.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
@ -132,9 +136,7 @@ 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 *);
|
||||
#ifdef IP_PKTINFO
|
||||
static void dhcp_handleifudp(void *);
|
||||
#endif
|
||||
static int dhcp_initstate(struct interface *);
|
||||
|
||||
void
|
||||
|
@ -1550,7 +1552,10 @@ dhcp_openudp(struct interface *ifp)
|
|||
n = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)
|
||||
goto eexit;
|
||||
#ifdef IP_RECVPKTINFO
|
||||
#ifdef IP_RECVIF
|
||||
if (setsockopt(s, IPPROTO_IP, IP_RECVIF, &n, sizeof(n)) == -1)
|
||||
goto eexit;
|
||||
#else
|
||||
if (setsockopt(s, IPPROTO_IP, IP_RECVPKTINFO, &n, sizeof(n)) == -1)
|
||||
goto eexit;
|
||||
#endif
|
||||
|
@ -1647,39 +1652,36 @@ dhcp_makeudppacket(size_t *sz, const uint8_t *data, size_t length,
|
|||
static ssize_t
|
||||
dhcp_sendudp(struct interface *ifp, struct in_addr *to, void *data, size_t len)
|
||||
{
|
||||
int s;
|
||||
struct msghdr msg;
|
||||
struct sockaddr_in sin;
|
||||
struct iovec iov[1];
|
||||
struct sockaddr_in sin = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr = *to,
|
||||
.sin_port = htons(BOOTPS),
|
||||
#ifdef HAVE_SA_LEN
|
||||
.sin_len = sizeof(sin),
|
||||
#endif
|
||||
};
|
||||
struct iovec iov[] = {
|
||||
{ .iov_base = data, .iov_len = len }
|
||||
};
|
||||
struct msghdr msg = {
|
||||
.msg_name = (void *)&sin,
|
||||
.msg_namelen = sizeof(sin),
|
||||
.msg_iov = iov,
|
||||
.msg_iovlen = 1,
|
||||
};
|
||||
struct dhcp_state *state = D_STATE(ifp);
|
||||
ssize_t r;
|
||||
int fd;
|
||||
|
||||
iov[0].iov_base = data;
|
||||
iov[0].iov_len = len;
|
||||
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr = *to;
|
||||
sin.sin_port = htons(BOOTPS);
|
||||
#ifdef HAVE_SA_LEN
|
||||
sin.sin_len = sizeof(sin);
|
||||
#endif
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_name = (void *)&sin;
|
||||
msg.msg_namelen = sizeof(sin);
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
s = state->udp_fd;
|
||||
if (s == -1) {
|
||||
s = dhcp_openudp(ifp);
|
||||
if (s == -1)
|
||||
fd = state->udp_fd;
|
||||
if (fd == -1) {
|
||||
fd = dhcp_openudp(ifp);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
}
|
||||
r = sendmsg(s, &msg, 0);
|
||||
r = sendmsg(fd, &msg, 0);
|
||||
if (state->udp_fd == -1)
|
||||
close(s);
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -1780,7 +1782,7 @@ send_message(struct interface *ifp, uint8_t type,
|
|||
* As such we remove it from consideration without actually
|
||||
* stopping the interface. */
|
||||
if (r == -1) {
|
||||
logerr("%s: if_sendraw", ifp->name);
|
||||
logerr("%s: bpf_send", ifp->name);
|
||||
switch(errno) {
|
||||
case ENETDOWN:
|
||||
case ENETRESET:
|
||||
|
@ -2257,30 +2259,27 @@ dhcp_bind(struct interface *ifp)
|
|||
|
||||
ipv4_applyaddr(ifp);
|
||||
|
||||
#ifdef IP_PKTINFO
|
||||
/* Close the BPF filter as we can now receive DHCP messages
|
||||
* on a UDP socket. */
|
||||
if (state->udp_fd == -1 ||
|
||||
(state->old != NULL && state->old->yiaddr != state->new->yiaddr))
|
||||
{
|
||||
dhcp_close(ifp);
|
||||
/* If not in master mode, open an address specific socket. */
|
||||
if (ctx->udp_fd == -1) {
|
||||
state->udp_fd = dhcp_openudp(ifp);
|
||||
if (state->udp_fd == -1) {
|
||||
logerr(__func__);
|
||||
/* Address sharing without master mode is
|
||||
* not supported. It's also possible another
|
||||
* DHCP client could be running which is
|
||||
* even worse.
|
||||
* We still need to work, so re-open BPF. */
|
||||
dhcp_openbpf(ifp);
|
||||
} else
|
||||
eloop_event_add(ctx->eloop,
|
||||
state->udp_fd, dhcp_handleifudp, ifp);
|
||||
}
|
||||
if (!(state->udp_fd == -1 ||
|
||||
(state->old != NULL && state->old->yiaddr != state->new->yiaddr)))
|
||||
return;
|
||||
dhcp_close(ifp);
|
||||
|
||||
/* If not in master mode, open an address specific socket. */
|
||||
if (ctx->udp_fd != -1)
|
||||
return;
|
||||
state->udp_fd = dhcp_openudp(ifp);
|
||||
if (state->udp_fd == -1) {
|
||||
logerr(__func__);
|
||||
/* Address sharing without master mode is not supported.
|
||||
* It's also possible another DHCP client could be running,
|
||||
* which is even worse.
|
||||
* We still need to work, so re-open BPF. */
|
||||
dhcp_openbpf(ifp);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
eloop_event_add(ctx->eloop, state->udp_fd, dhcp_handleifudp, ifp);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2609,6 +2608,11 @@ dhcp_reboot(struct interface *ifp)
|
|||
ifp->name, inet_ntoa(state->lease.addr));
|
||||
|
||||
#ifdef ARP
|
||||
#ifndef KERNEL_RFC5227
|
||||
/* Create the DHCP ARP state so we can defend it. */
|
||||
(void)dhcp_arp_new(ifp, &state->lease.addr);
|
||||
#endif
|
||||
|
||||
/* If the address exists on the interface and no other interface
|
||||
* is currently using it then announce it to ensure this
|
||||
* interface gets the reply. */
|
||||
|
@ -3315,7 +3319,7 @@ checksums_valid(void *packet,
|
|||
struct ip *ip = packet;
|
||||
union pip {
|
||||
struct ip ip;
|
||||
uint16_t w[sizeof(struct ip)];
|
||||
uint16_t w[sizeof(struct ip) / 2];
|
||||
} pip = {
|
||||
.ip.ip_p = IPPROTO_UDP,
|
||||
.ip.ip_src = ip->ip_src,
|
||||
|
@ -3350,7 +3354,7 @@ checksums_valid(void *packet,
|
|||
uh_sump = udpp + offsetof(struct udphdr, uh_sum);
|
||||
memset(uh_sump, 0, sizeof(udp.uh_sum));
|
||||
|
||||
/* Checksum psuedo header and then UDP + payload. */
|
||||
/* Checksum pseudo header and then UDP + payload. */
|
||||
in_cksum(pip.w, sizeof(pip.w), &csum);
|
||||
csum = in_cksum(udpp, ntohs(udp.uh_ulen), &csum);
|
||||
|
||||
|
@ -3450,6 +3454,35 @@ dhcp_readbpf(void *arg)
|
|||
state->bpf_flags &= ~BPF_READING;
|
||||
}
|
||||
|
||||
static void
|
||||
dhcp_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg)
|
||||
{
|
||||
struct sockaddr_in *from = (struct sockaddr_in *)msg->msg_name;
|
||||
struct iovec *iov = &msg->msg_iov[0];
|
||||
struct interface *ifp;
|
||||
const struct dhcp_state *state;
|
||||
|
||||
ifp = if_findifpfromcmsg(ctx, msg, NULL);
|
||||
if (ifp == NULL) {
|
||||
logerr(__func__);
|
||||
return;
|
||||
}
|
||||
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 *)iov->iov_base, iov->iov_len,
|
||||
&from->sin_addr);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp)
|
||||
{
|
||||
|
@ -3460,16 +3493,15 @@ dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp)
|
|||
.iov_base = buf,
|
||||
.iov_len = sizeof(buf),
|
||||
};
|
||||
#ifdef IP_PKTINFO
|
||||
#ifdef IP_RECVIF
|
||||
unsigned char ctl[CMSG_SPACE(sizeof(struct sockaddr_dl))] = { 0 };
|
||||
#else
|
||||
unsigned char ctl[CMSG_SPACE(sizeof(struct in_pktinfo))] = { 0 };
|
||||
char sfrom[INET_ADDRSTRLEN];
|
||||
#endif
|
||||
struct msghdr msg = {
|
||||
.msg_name = &from, .msg_namelen = sizeof(from),
|
||||
.msg_iov = &iov, .msg_iovlen = 1,
|
||||
#ifdef IP_PKTINFO
|
||||
.msg_control = ctl, .msg_controllen = sizeof(ctl),
|
||||
#endif
|
||||
};
|
||||
int s;
|
||||
ssize_t bytes;
|
||||
|
@ -3486,31 +3518,8 @@ dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp)
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef IP_PKTINFO
|
||||
inet_ntop(AF_INET, &from.sin_addr, sfrom, sizeof(sfrom));
|
||||
|
||||
if (ifp == NULL) {
|
||||
ifp = if_findifpfromcmsg(ctx, &msg, NULL);
|
||||
if (ifp == NULL) {
|
||||
logerr(__func__);
|
||||
return;
|
||||
}
|
||||
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
|
||||
iov.iov_len = (size_t)bytes;
|
||||
dhcp_recvmsg(ctx, &msg);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -3521,16 +3530,24 @@ dhcp_handleudp(void *arg)
|
|||
dhcp_readudp(ctx, NULL);
|
||||
}
|
||||
|
||||
#ifdef IP_PKTINFO
|
||||
static void
|
||||
dhcp_handleifudp(void *arg)
|
||||
{
|
||||
struct interface *ifp = arg;
|
||||
|
||||
dhcp_readudp(ifp->ctx, ifp);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
dhcp_open(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
|
||||
if (ctx->udp_fd != -1 || (ctx->udp_fd = dhcp_openudp(NULL)) == -1)
|
||||
return ctx->udp_fd;
|
||||
|
||||
eloop_event_add(ctx->eloop, ctx->udp_fd, dhcp_handleudp, ctx);
|
||||
return ctx->udp_fd;
|
||||
}
|
||||
|
||||
static int
|
||||
dhcp_openbpf(struct interface *ifp)
|
||||
|
@ -3740,16 +3757,13 @@ dhcp_start1(void *arg)
|
|||
* ICMP port unreachable message back to the DHCP server.
|
||||
* Only do this in master mode so we don't swallow messages
|
||||
* for dhcpcd running on another interface. */
|
||||
if (ctx->udp_fd == -1 && ctx->options & DHCPCD_MASTER) {
|
||||
ctx->udp_fd = dhcp_openudp(NULL);
|
||||
if (ctx->udp_fd == -1) {
|
||||
if (ctx->options & DHCPCD_MASTER) {
|
||||
if (dhcp_open(ctx) == -1) {
|
||||
/* Don't log an error if some other process
|
||||
* is handling this. */
|
||||
if (errno != EADDRINUSE)
|
||||
logerr("%s: dhcp_openudp", __func__);
|
||||
} else
|
||||
eloop_event_add(ctx->eloop,
|
||||
ctx->udp_fd, dhcp_handleudp, ctx);
|
||||
logerr("%s: dhcp_open", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
if (dhcp_init(ifp) == -1) {
|
||||
|
@ -4008,7 +4022,7 @@ dhcp_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid)
|
|||
* to drop the lease. */
|
||||
dhcp_drop(ifp, "EXPIRE");
|
||||
dhcp_start1(ifp);
|
||||
return NULL;
|
||||
return ia;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3465,23 +3465,10 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom,
|
|||
}
|
||||
|
||||
static void
|
||||
dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
|
||||
dhcp6_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, struct ipv6_addr *ia)
|
||||
{
|
||||
struct sockaddr_in6 from;
|
||||
unsigned char buf[64 * 1024]; /* Maximum UDP message size */
|
||||
struct iovec iov = {
|
||||
.iov_base = buf,
|
||||
.iov_len = sizeof(buf),
|
||||
};
|
||||
unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 };
|
||||
struct msghdr msg = {
|
||||
.msg_name = &from, .msg_namelen = sizeof(from),
|
||||
.msg_iov = &iov, .msg_iovlen = 1,
|
||||
.msg_control = ctl, .msg_controllen = sizeof(ctl),
|
||||
};
|
||||
int s;
|
||||
size_t len;
|
||||
ssize_t bytes;
|
||||
struct sockaddr_in6 *from = msg->msg_name;
|
||||
size_t len = msg->msg_iov[0].iov_len;
|
||||
char sfrom[INET6_ADDRSTRLEN];
|
||||
struct interface *ifp;
|
||||
struct dhcp6_message *r;
|
||||
|
@ -3489,14 +3476,7 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
|
|||
uint8_t *o;
|
||||
uint16_t ol;
|
||||
|
||||
s = ia != NULL ? ia->dhcp6_fd : ctx->dhcp6_fd;
|
||||
bytes = recvmsg(s, &msg, 0);
|
||||
if (bytes == -1) {
|
||||
logerr(__func__);
|
||||
return;
|
||||
}
|
||||
len = (size_t)bytes;
|
||||
inet_ntop(AF_INET6, &from.sin6_addr, sfrom, sizeof(sfrom));
|
||||
inet_ntop(AF_INET6, &from->sin6_addr, sfrom, sizeof(sfrom));
|
||||
if (len < sizeof(struct dhcp6_message)) {
|
||||
logerrx("DHCPv6 packet too short from %s", sfrom);
|
||||
return;
|
||||
|
@ -3505,14 +3485,14 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
|
|||
if (ia != NULL)
|
||||
ifp = ia->iface;
|
||||
else {
|
||||
ifp = if_findifpfromcmsg(ctx, &msg, NULL);
|
||||
ifp = if_findifpfromcmsg(ctx, msg, NULL);
|
||||
if (ifp == NULL) {
|
||||
logerr(__func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
r = (struct dhcp6_message *)buf;
|
||||
r = (struct dhcp6_message *)msg->msg_iov[0].iov_base;
|
||||
o = dhcp6_findmoption(r, len, D6_OPTION_CLIENTID, &ol);
|
||||
if (o == NULL || ol != ctx->duid_len ||
|
||||
memcmp(o, ctx->duid, ol) != 0)
|
||||
|
@ -3580,6 +3560,35 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
|
|||
dhcp6_recvif(ifp, sfrom, r, len);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
|
||||
{
|
||||
struct sockaddr_in6 from;
|
||||
unsigned char buf[64 * 1024]; /* Maximum UDP message size */
|
||||
struct iovec iov = {
|
||||
.iov_base = buf,
|
||||
.iov_len = sizeof(buf),
|
||||
};
|
||||
unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 };
|
||||
struct msghdr msg = {
|
||||
.msg_name = &from, .msg_namelen = sizeof(from),
|
||||
.msg_iov = &iov, .msg_iovlen = 1,
|
||||
.msg_control = ctl, .msg_controllen = sizeof(ctl),
|
||||
};
|
||||
int s;
|
||||
ssize_t bytes;
|
||||
|
||||
s = ia != NULL ? ia->dhcp6_fd : ctx->dhcp6_fd;
|
||||
bytes = recvmsg(s, &msg, 0);
|
||||
if (bytes == -1) {
|
||||
logerr(__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
iov.iov_len = (size_t)bytes;
|
||||
dhcp6_recvmsg(ctx, &msg, ia);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcp6_recvaddr(void *arg)
|
||||
{
|
||||
|
@ -3677,6 +3686,18 @@ dhcp6_activateinterfaces(struct interface *ifp)
|
|||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
dhcp6_open(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
|
||||
if (ctx->dhcp6_fd != -1 ||
|
||||
(ctx->dhcp6_fd = dhcp6_listen(ctx, NULL)) == -1)
|
||||
return ctx->dhcp6_fd;
|
||||
|
||||
eloop_event_add(ctx->eloop, ctx->dhcp6_fd, dhcp6_recvctx, ctx);
|
||||
return ctx->dhcp6_fd;
|
||||
}
|
||||
|
||||
static void
|
||||
dhcp6_start1(void *arg)
|
||||
{
|
||||
|
@ -3687,11 +3708,9 @@ dhcp6_start1(void *arg)
|
|||
size_t i;
|
||||
const struct dhcp_compat *dhc;
|
||||
|
||||
if (ctx->dhcp6_fd == -1 && ctx->options & DHCPCD_MASTER) {
|
||||
ctx->dhcp6_fd = dhcp6_listen(ctx, NULL);
|
||||
if (ctx->dhcp6_fd == -1)
|
||||
if (ctx->options & DHCPCD_MASTER) {
|
||||
if (dhcp6_open(ctx) == -1)
|
||||
return;
|
||||
eloop_event_add(ctx->eloop, ctx->dhcp6_fd, dhcp6_recvctx, ctx);
|
||||
}
|
||||
|
||||
state = D6_STATE(ifp);
|
||||
|
|
|
@ -186,6 +186,12 @@ handle_exit_timeout(void *arg)
|
|||
ctx = arg;
|
||||
logerrx("timed out");
|
||||
if (!(ctx->options & DHCPCD_MASTER)) {
|
||||
struct interface *ifp;
|
||||
|
||||
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
|
||||
if (ifp->active == IF_ACTIVE_USER)
|
||||
script_runreason(ifp, "STOPPED");
|
||||
}
|
||||
eloop_exit(ctx->eloop, EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
@ -702,6 +708,21 @@ dhcpcd_initstate(struct interface *ifp, unsigned long long options)
|
|||
dhcpcd_initstate1(ifp, ifp->ctx->argc, ifp->ctx->argv, options);
|
||||
}
|
||||
|
||||
static void
|
||||
dhcpcd_reportssid(struct interface *ifp)
|
||||
{
|
||||
char pssid[IF_SSIDLEN * 4];
|
||||
|
||||
if (print_string(pssid, sizeof(pssid), OT_ESCSTRING,
|
||||
ifp->ssid, ifp->ssid_len) == -1)
|
||||
{
|
||||
logerr(__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
loginfox("%s: connected to Access Point `%s'", ifp->name, pssid);
|
||||
}
|
||||
|
||||
void
|
||||
dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags,
|
||||
const char *ifname)
|
||||
|
@ -773,6 +794,7 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags,
|
|||
if (ifp->ssid_len != olen ||
|
||||
memcmp(ifp->ssid, ossid, ifp->ssid_len))
|
||||
{
|
||||
dhcpcd_reportssid(ifp);
|
||||
#ifdef NOCARRIER_PRESERVE_IP
|
||||
dhcpcd_drop(ifp, 0);
|
||||
#endif
|
||||
|
@ -970,7 +992,8 @@ run_preinit(struct interface *ifp)
|
|||
return;
|
||||
|
||||
script_runreason(ifp, "PREINIT");
|
||||
|
||||
if (ifp->wireless)
|
||||
dhcpcd_reportssid(ifp);
|
||||
if (ifp->options->options & DHCPCD_LINK && ifp->carrier != LINK_UNKNOWN)
|
||||
script_runreason(ifp,
|
||||
ifp->carrier == LINK_UP ? "CARRIER" : "NOCARRIER");
|
||||
|
@ -1989,6 +2012,12 @@ printpidfile:
|
|||
ctx.options & DHCPCD_IPV4 ? " [ip4]" : "",
|
||||
ctx.options & DHCPCD_IPV6 ? " [ip6]" : "");
|
||||
|
||||
#ifdef BSD
|
||||
/* Disable the kernel RTADV sysctl as early as possible. */
|
||||
if (ctx.options & DHCPCD_IPV6 && ctx.options & DHCPCD_IPV6RS)
|
||||
if_disable_rtadv();
|
||||
#endif
|
||||
|
||||
if (if_opensockets(&ctx) == -1) {
|
||||
logerr("%s: if_opensockets", __func__);
|
||||
goto exit_failure;
|
||||
|
@ -2127,6 +2156,7 @@ exit1:
|
|||
if_free(ifp);
|
||||
}
|
||||
free(ctx.ifaces);
|
||||
ctx.ifaces = NULL;
|
||||
}
|
||||
free_options(&ctx, ifo);
|
||||
#ifdef HAVE_OPEN_MEMSTREAM
|
||||
|
|
|
@ -830,7 +830,8 @@ eloop_new(void)
|
|||
return eloop;
|
||||
}
|
||||
|
||||
void eloop_free(struct eloop *eloop)
|
||||
void
|
||||
eloop_clear(struct eloop *eloop)
|
||||
{
|
||||
struct eloop_event *e;
|
||||
struct eloop_timeout *t;
|
||||
|
@ -839,6 +840,12 @@ void eloop_free(struct eloop *eloop)
|
|||
return;
|
||||
|
||||
free(eloop->event_fds);
|
||||
eloop->event_fds = NULL;
|
||||
eloop->events_len = 0;
|
||||
eloop->events_maxfd = -1;
|
||||
eloop->signals = NULL;
|
||||
eloop->signals_len = 0;
|
||||
|
||||
while ((e = TAILQ_FIRST(&eloop->events))) {
|
||||
TAILQ_REMOVE(&eloop->events, e, next);
|
||||
free(e);
|
||||
|
@ -855,11 +862,23 @@ void eloop_free(struct eloop *eloop)
|
|||
TAILQ_REMOVE(&eloop->free_timeouts, t, next);
|
||||
free(t);
|
||||
}
|
||||
#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
|
||||
close(eloop->poll_fd);
|
||||
#elif defined(HAVE_POLL)
|
||||
|
||||
#if defined(HAVE_POLL)
|
||||
free(eloop->fds);
|
||||
eloop->fds = NULL;
|
||||
eloop->fds_len = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
eloop_free(struct eloop *eloop)
|
||||
{
|
||||
|
||||
#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
|
||||
if (eloop != NULL)
|
||||
close(eloop->poll_fd);
|
||||
#endif
|
||||
eloop_clear(eloop);
|
||||
free(eloop);
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,7 @@ int eloop_signal_mask(struct eloop *, sigset_t *oldset);
|
|||
|
||||
struct eloop * eloop_new(void);
|
||||
int eloop_requeue(struct eloop *);
|
||||
void eloop_clear(struct eloop *);
|
||||
void eloop_free(struct eloop *);
|
||||
void eloop_exit(struct eloop *, int);
|
||||
int eloop_start(struct eloop *, sigset_t *);
|
||||
|
|
|
@ -1130,6 +1130,10 @@ if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm)
|
|||
case LINK_STATE_UNKNOWN:
|
||||
link_state = LINK_UNKNOWN;
|
||||
break;
|
||||
#ifdef LINK_STATE_FULL_DUPLEX
|
||||
case LINK_STATE_HALF_DUPLEX: /* FALLTHROUGH */
|
||||
case LINK_STATE_FULL_DUPLEX: /* FALLTHROUGH */
|
||||
#endif
|
||||
case LINK_STATE_UP:
|
||||
link_state = LINK_UP;
|
||||
break;
|
||||
|
@ -1484,6 +1488,42 @@ inet6_sysctl(int code, int val, int action)
|
|||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
if_applyra(const struct ra *rap)
|
||||
{
|
||||
#ifdef SIOCSIFINFO_IN6
|
||||
struct in6_ndireq ndi = { .ndi.chlim = 0 };
|
||||
struct priv *priv = rap->iface->ctx->priv;
|
||||
int error;
|
||||
|
||||
strlcpy(ndi.ifname, rap->iface->name, sizeof(ndi.ifname));
|
||||
if (ioctl(priv->pf_inet6_fd, SIOCGIFINFO_IN6, &ndi) == -1)
|
||||
return -1;
|
||||
|
||||
ndi.ndi.linkmtu = rap->mtu;
|
||||
ndi.ndi.chlim = rap->hoplimit;
|
||||
ndi.ndi.retrans = rap->retrans;
|
||||
ndi.ndi.basereachable = rap->reachable;
|
||||
error = ioctl(priv->pf_inet6_fd, SIOCSIFINFO_IN6, &ndi);
|
||||
if (error == -1 && errno == EINVAL) {
|
||||
/*
|
||||
* Very likely that this is caused by a dodgy MTU
|
||||
* setting specific to the interface.
|
||||
* Let's set it to "unspecified" and try again.
|
||||
* Doesn't really matter as we fix the MTU against the
|
||||
* routes we add as not all OS support SIOCSIFINFO_IN6.
|
||||
*/
|
||||
ndi.ndi.linkmtu = 0;
|
||||
error = ioctl(priv->pf_inet6_fd, SIOCSIFINFO_IN6, &ndi);
|
||||
}
|
||||
return error;
|
||||
#else
|
||||
#warning OS does not allow setting of RA bits hoplimit, retrans or reachable
|
||||
UNUSED(rap);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef IPV6_MANAGETEMPADDR
|
||||
#ifndef IPV6CTL_TEMPVLTIME
|
||||
#define get_inet6_sysctlbyname(code) inet6_sysctlbyname(code, 0, 0)
|
||||
|
@ -1619,6 +1659,22 @@ set_ifxflags(int s, const struct interface *ifp)
|
|||
#define ND6_NDI_FLAGS
|
||||
#endif
|
||||
|
||||
void
|
||||
if_disable_rtadv(void)
|
||||
{
|
||||
#if defined(IPV6CTL_ACCEPT_RTADV) && !defined(ND6_IFF_ACCEPT_RTADV)
|
||||
int ra = get_inet6_sysctl(IPV6CTL_ACCEPT_RTADV);
|
||||
|
||||
if (ra == -1) {
|
||||
if (errno != ENOENT)
|
||||
logerr("IPV6CTL_ACCEPT_RTADV");
|
||||
else if (ra != 0)
|
||||
if (set_inet6_sysctl(IPV6CTL_ACCEPT_RTADV, 0) == -1)
|
||||
logerr("IPV6CTL_ACCEPT_RTADV");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
if_setup_inet6(const struct interface *ifp)
|
||||
{
|
||||
|
@ -1690,21 +1746,6 @@ if_setup_inet6(const struct interface *ifp)
|
|||
logerr("%s: set_ifxflags", ifp->name);
|
||||
#endif
|
||||
|
||||
#if defined(IPV6CTL_ACCEPT_RTADV) && !defined(ND6_IFF_ACCEPT_RTADV)
|
||||
/* If we cannot control ra per interface, disable it globally. */
|
||||
if (ifp->options->options & DHCPCD_IPV6RS) {
|
||||
int ra = get_inet6_sysctl(IPV6CTL_ACCEPT_RTADV);
|
||||
|
||||
if (ra == -1) {
|
||||
if (errno != ENOENT)
|
||||
logerr("IPV6CTL_ACCEPT_RTADV");
|
||||
else if (ra != 0)
|
||||
if (set_inet6_sysctl(IPV6CTL_ACCEPT_RTADV, 0) == -1)
|
||||
logerr("IPV6CTL_ACCEPT_RTADV");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(IPV6CTL_ACCEPT_RTADV) || defined(ND6_IFF_ACCEPT_RTADV)
|
||||
/* Flush the kernel knowledge of advertised routers
|
||||
* and prefixes so the kernel does not expire prefixes
|
||||
|
|
|
@ -740,9 +740,13 @@ if_findifpfromcmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, int *hoplimit)
|
|||
struct cmsghdr *cm;
|
||||
unsigned int ifindex = 0;
|
||||
struct interface *ifp;
|
||||
#if defined(INET) && defined(IP_PKTINFO)
|
||||
#ifdef INET
|
||||
#ifdef IP_RECVIF
|
||||
struct sockaddr_dl sdl;
|
||||
#else
|
||||
struct in_pktinfo ipi;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef INET6
|
||||
struct in6_pktinfo ipi6;
|
||||
#else
|
||||
|
@ -753,15 +757,27 @@ if_findifpfromcmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, int *hoplimit)
|
|||
cm;
|
||||
cm = (struct cmsghdr *)CMSG_NXTHDR(msg, cm))
|
||||
{
|
||||
#if defined(INET) && defined(IP_PKTINFO)
|
||||
#ifdef INET
|
||||
if (cm->cmsg_level == IPPROTO_IP) {
|
||||
switch(cm->cmsg_type) {
|
||||
#ifdef IP_RECVIF
|
||||
case IP_RECVIF:
|
||||
if (cm->cmsg_len <
|
||||
offsetof(struct sockaddr_dl, sdl_index) +
|
||||
sizeof(sdl.sdl_index))
|
||||
continue;
|
||||
memcpy(&sdl, CMSG_DATA(cm),
|
||||
MIN(sizeof(sdl), cm->cmsg_len));
|
||||
ifindex = sdl.sdl_index;
|
||||
break;
|
||||
#else
|
||||
case IP_PKTINFO:
|
||||
if (cm->cmsg_len != CMSG_LEN(sizeof(ipi)))
|
||||
continue;
|
||||
memcpy(&ipi, CMSG_DATA(cm), sizeof(ipi));
|
||||
ifindex = (unsigned int)ipi.ipi_ifindex;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include "dhcpcd.h"
|
||||
#include "ipv4.h"
|
||||
#include "ipv6.h"
|
||||
#include "ipv6nd.h"
|
||||
#include "route.h"
|
||||
|
||||
#define EUI64_ADDR_LEN 8
|
||||
|
@ -195,6 +196,7 @@ int if_addrflags(const struct interface *, const struct in_addr *,
|
|||
#endif
|
||||
|
||||
#ifdef INET6
|
||||
void if_disable_rtadv(void);
|
||||
void if_setup_inet6(const struct interface *);
|
||||
#ifdef IPV6_MANAGETEMPADDR
|
||||
int ip6_use_tempaddr(const char *ifname);
|
||||
|
@ -205,6 +207,7 @@ int ip6_temp_valid_lifetime(const char *ifname);
|
|||
#endif
|
||||
int ip6_forwarding(const char *ifname);
|
||||
|
||||
int if_applyra(const struct ra *);
|
||||
int if_address6(unsigned char, const struct ipv6_addr *);
|
||||
int if_addrflags6(const struct interface *, const struct in6_addr *,
|
||||
const char *);
|
||||
|
|
|
@ -654,7 +654,7 @@ ipv4_addaddr(struct interface *ifp, const struct in_addr *addr,
|
|||
#endif
|
||||
ia->flags = IPV4_AF_NEW;
|
||||
} else
|
||||
ia->flags |= ~IPV4_AF_NEW;
|
||||
ia->flags &= ~IPV4_AF_NEW;
|
||||
|
||||
ia->mask = *mask;
|
||||
ia->brd = *bcast;
|
||||
|
@ -687,7 +687,8 @@ ipv4_addaddr(struct interface *ifp, const struct in_addr *addr,
|
|||
if (errno != EEXIST)
|
||||
logerr("%s: if_addaddress",
|
||||
__func__);
|
||||
free(ia);
|
||||
if (ia->flags & IPV4_AF_NEW)
|
||||
free(ia);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -941,7 +942,7 @@ ipv4_handleifa(struct dhcpcd_ctx *ctx,
|
|||
#endif
|
||||
}
|
||||
|
||||
if (cmd == RTM_DELADDR && ia != NULL)
|
||||
if (cmd == RTM_DELADDR)
|
||||
free(ia);
|
||||
}
|
||||
|
||||
|
@ -951,15 +952,13 @@ ipv4_free(struct interface *ifp)
|
|||
struct ipv4_state *state;
|
||||
struct ipv4_addr *ia;
|
||||
|
||||
if (ifp) {
|
||||
state = IPV4_STATE(ifp);
|
||||
if (state) {
|
||||
while ((ia = TAILQ_FIRST(&state->addrs))) {
|
||||
TAILQ_REMOVE(&state->addrs, ia, next);
|
||||
free(ia);
|
||||
}
|
||||
free(state->buffer);
|
||||
free(state);
|
||||
}
|
||||
if (ifp == NULL || (state = IPV4_STATE(ifp)) == NULL)
|
||||
return;
|
||||
|
||||
while ((ia = TAILQ_FIRST(&state->addrs))) {
|
||||
TAILQ_REMOVE(&state->addrs, ia, next);
|
||||
free(ia);
|
||||
}
|
||||
free(state->buffer);
|
||||
free(state);
|
||||
}
|
||||
|
|
|
@ -196,36 +196,41 @@ ipv6nd_printoptions(const struct dhcpcd_ctx *ctx,
|
|||
static int
|
||||
ipv6nd_open0(void)
|
||||
{
|
||||
int s, on;
|
||||
int fd, on;
|
||||
struct icmp6_filter filt;
|
||||
|
||||
#define SOCK_FLAGS SOCK_CLOEXEC | SOCK_NONBLOCK
|
||||
s = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6);
|
||||
fd = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6);
|
||||
#undef SOCK_FLAGS
|
||||
if (s == -1)
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
/* RFC4861 4.1 */
|
||||
on = 255;
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
|
||||
&on, sizeof(on)) == -1)
|
||||
goto eexit;
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
|
||||
&on, sizeof(on)) == -1)
|
||||
goto eexit;
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
|
||||
&on, sizeof(on)) == -1)
|
||||
goto eexit;
|
||||
|
||||
ICMP6_FILTER_SETBLOCKALL(&filt);
|
||||
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
|
||||
if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER,
|
||||
if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER,
|
||||
&filt, sizeof(filt)) == -1)
|
||||
goto eexit;
|
||||
|
||||
return s;
|
||||
return fd;
|
||||
|
||||
eexit:
|
||||
close(s);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -233,7 +238,7 @@ eexit:
|
|||
static int
|
||||
ipv6nd_open(struct interface *ifp)
|
||||
{
|
||||
int s;
|
||||
int fd;
|
||||
struct ipv6_mreq mreq = {
|
||||
.ipv6mr_multiaddr = IN6ADDR_LINKLOCAL_ALLNODES_INIT,
|
||||
.ipv6mr_interface = ifp->index
|
||||
|
@ -244,52 +249,44 @@ ipv6nd_open(struct interface *ifp)
|
|||
if (state->nd_fd != -1)
|
||||
return state->nd_fd;
|
||||
|
||||
s = ipv6nd_open0();
|
||||
if (s == -1)
|
||||
fd = ipv6nd_open0();
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF,
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF,
|
||||
&ifindex, sizeof(ifindex)) == -1)
|
||||
{
|
||||
close(s);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP,
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
|
||||
&mreq, sizeof(mreq)) == -1)
|
||||
{
|
||||
close(s);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->nd_fd = s;
|
||||
eloop_event_add(ifp->ctx->eloop, s, ipv6nd_handledata, ifp);
|
||||
return s;
|
||||
state->nd_fd = fd;
|
||||
eloop_event_add(ifp->ctx->eloop, fd, ipv6nd_handledata, ifp);
|
||||
return fd;
|
||||
}
|
||||
#else
|
||||
static int
|
||||
ipv6nd_open(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
int s, on;
|
||||
int fd;
|
||||
|
||||
if (ctx->nd_fd != -1)
|
||||
return ctx->nd_fd;
|
||||
|
||||
s = ipv6nd_open0();
|
||||
if (s == -1)
|
||||
fd = ipv6nd_open0();
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
|
||||
&on, sizeof(on)) == -1)
|
||||
{
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->nd_fd = s;
|
||||
eloop_event_add(ctx->eloop, s, ipv6nd_handledata, ctx);
|
||||
return s;
|
||||
ctx->nd_fd = fd;
|
||||
eloop_event_add(ctx->eloop, fd, ipv6nd_handledata, ctx);
|
||||
return fd;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -559,6 +556,76 @@ ipv6nd_startexpire(struct interface *ifp)
|
|||
ipv6nd_expire, ifp);
|
||||
}
|
||||
|
||||
static int
|
||||
ipv6nd_rtpref(struct ra *rap)
|
||||
{
|
||||
|
||||
switch (rap->flags & ND_RA_FLAG_RTPREF_MASK) {
|
||||
case ND_RA_FLAG_RTPREF_HIGH:
|
||||
return RTPREF_HIGH;
|
||||
case ND_RA_FLAG_RTPREF_MEDIUM:
|
||||
case ND_RA_FLAG_RTPREF_RSV:
|
||||
return RTPREF_MEDIUM;
|
||||
case ND_RA_FLAG_RTPREF_LOW:
|
||||
return RTPREF_LOW;
|
||||
default:
|
||||
logerrx("%s: impossible RA flag %x", __func__, rap->flags);
|
||||
return RTPREF_INVALID;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
static void
|
||||
ipv6nd_sortrouters(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
struct ra_head sorted_routers = TAILQ_HEAD_INITIALIZER(sorted_routers);
|
||||
struct ra *ra1, *ra2;
|
||||
|
||||
while ((ra1 = TAILQ_FIRST(ctx->ra_routers)) != NULL) {
|
||||
TAILQ_REMOVE(ctx->ra_routers, ra1, next);
|
||||
TAILQ_FOREACH(ra2, &sorted_routers, next) {
|
||||
if (ra1->iface->metric < ra2->iface->metric)
|
||||
continue;
|
||||
if (ra1->expired && !ra2->expired)
|
||||
continue;
|
||||
if (ra1->lifetime == 0 && ra2->lifetime != 0)
|
||||
continue;
|
||||
if (!ra1->isreachable && ra2->reachable)
|
||||
continue;
|
||||
if (ipv6nd_rtpref(ra1) < ipv6nd_rtpref(ra2))
|
||||
continue;
|
||||
/* All things being equal, prefer older routers. */
|
||||
if (timespeccmp(&ra1->acquired, &ra2->acquired, >=))
|
||||
continue;
|
||||
TAILQ_INSERT_BEFORE(ra2, ra1, next);
|
||||
break;
|
||||
}
|
||||
if (ra2 == NULL)
|
||||
TAILQ_INSERT_TAIL(&sorted_routers, ra1, next);
|
||||
}
|
||||
|
||||
TAILQ_CONCAT(ctx->ra_routers, &sorted_routers, next);
|
||||
}
|
||||
|
||||
static void
|
||||
ipv6nd_applyra(struct dhcpcd_ctx *ctx, struct interface *ifp)
|
||||
{
|
||||
struct ra *rap;
|
||||
struct rs_state *state = RS_STATE(ifp);
|
||||
|
||||
TAILQ_FOREACH(rap, ctx->ra_routers, next) {
|
||||
if (rap->iface == ifp)
|
||||
break;
|
||||
}
|
||||
|
||||
if (rap == NULL)
|
||||
return;
|
||||
|
||||
state->retrans = rap->retrans;
|
||||
if (if_applyra(rap) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* Neighbour reachability.
|
||||
*
|
||||
|
@ -585,23 +652,20 @@ ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, bool reachable)
|
|||
break;
|
||||
}
|
||||
|
||||
if (rap == NULL || rap->expired)
|
||||
if (rap == NULL || rap->expired || rap->reachable == reachable)
|
||||
return;
|
||||
|
||||
if (reachable) {
|
||||
if (rap->isreachable)
|
||||
return;
|
||||
loginfox("%s: %s is reachable again",
|
||||
rap->iface->name, rap->sfrom);
|
||||
rap->isreachable = true;
|
||||
rap->isreachable = reachable;
|
||||
loginfox("%s: %s is %s", rap->iface->name, rap->sfrom,
|
||||
reachable ? "reachable again" : "unreachable");
|
||||
|
||||
/* See if we can install a reachable default router. */
|
||||
ipv6nd_sortrouters(ctx);
|
||||
ipv6nd_applyra(ctx, rap->iface);
|
||||
rt_build(ctx, AF_INET6);
|
||||
|
||||
if (reachable)
|
||||
return;
|
||||
} else {
|
||||
if (!rap->isreachable)
|
||||
return;
|
||||
logwarnx("%s: %s is unreachable",
|
||||
rap->iface->name, rap->sfrom);
|
||||
rap->isreachable = false;
|
||||
}
|
||||
|
||||
/* If we have no reachable default routers, try and solicit one. */
|
||||
TAILQ_FOREACH(rapr, ctx->ra_routers, next) {
|
||||
|
@ -722,42 +786,6 @@ ipv6nd_free(struct interface *ifp)
|
|||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
rtpref(struct ra *rap)
|
||||
{
|
||||
|
||||
switch (rap->flags & ND_RA_FLAG_RTPREF_MASK) {
|
||||
case ND_RA_FLAG_RTPREF_HIGH:
|
||||
return (RTPREF_HIGH);
|
||||
case ND_RA_FLAG_RTPREF_MEDIUM:
|
||||
case ND_RA_FLAG_RTPREF_RSV:
|
||||
return (RTPREF_MEDIUM);
|
||||
case ND_RA_FLAG_RTPREF_LOW:
|
||||
return (RTPREF_LOW);
|
||||
default:
|
||||
logerrx("rtpref: impossible RA flag %x", rap->flags);
|
||||
return (RTPREF_INVALID);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
static void
|
||||
add_router(struct dhcpcd_ctx *ctx, struct ra *router)
|
||||
{
|
||||
struct ra *rap;
|
||||
|
||||
TAILQ_FOREACH(rap, ctx->ra_routers, next) {
|
||||
if (router->iface->metric < rap->iface->metric ||
|
||||
(router->iface->metric == rap->iface->metric &&
|
||||
rtpref(router) > rtpref(rap)))
|
||||
{
|
||||
TAILQ_INSERT_BEFORE(rap, router, next);
|
||||
return;
|
||||
}
|
||||
}
|
||||
TAILQ_INSERT_TAIL(ctx->ra_routers, router, next);
|
||||
}
|
||||
|
||||
static int
|
||||
ipv6nd_scriptrun(struct ra *rap)
|
||||
{
|
||||
|
@ -965,6 +993,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
|
|||
struct dhcp_opt *dho;
|
||||
bool new_rap, new_data, has_address;
|
||||
uint32_t old_lifetime;
|
||||
int ifmtu;
|
||||
__printflike(1, 2) void (*logfunc)(const char *, ...);
|
||||
#ifdef IPV6_MANAGETEMPADDR
|
||||
uint8_t new_ap;
|
||||
|
@ -1074,20 +1103,25 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
|
|||
if (!new_rap && rap->lifetime == 0 && old_lifetime != 0)
|
||||
logwarnx("%s: %s: no longer a default router",
|
||||
ifp->name, rap->sfrom);
|
||||
if (nd_ra->nd_ra_reachable) {
|
||||
if (nd_ra->nd_ra_curhoplimit != 0)
|
||||
rap->hoplimit = nd_ra->nd_ra_curhoplimit;
|
||||
else
|
||||
rap->hoplimit = IPV6_DEFHLIM;
|
||||
if (nd_ra->nd_ra_reachable != 0) {
|
||||
rap->reachable = ntohl(nd_ra->nd_ra_reachable);
|
||||
if (rap->reachable > MAX_REACHABLE_TIME)
|
||||
rap->reachable = 0;
|
||||
}
|
||||
if (nd_ra->nd_ra_retransmit) {
|
||||
struct rs_state *state = RS_STATE(ifp);
|
||||
|
||||
state->retrans = rap->retrans = ntohl(nd_ra->nd_ra_retransmit);
|
||||
}
|
||||
} else
|
||||
rap->reachable = REACHABLE_TIME;
|
||||
if (nd_ra->nd_ra_retransmit != 0)
|
||||
rap->retrans = ntohl(nd_ra->nd_ra_retransmit);
|
||||
else
|
||||
rap->retrans = RETRANS_TIMER;
|
||||
rap->expired = false;
|
||||
rap->hasdns = false;
|
||||
rap->isreachable = true;
|
||||
has_address = false;
|
||||
rap->mtu = 0;
|
||||
|
||||
#ifdef IPV6_AF_TEMPORARY
|
||||
ipv6_markaddrsstale(ifp, IPV6_AF_TEMPORARY);
|
||||
|
@ -1245,22 +1279,30 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
|
|||
|
||||
case ND_OPT_MTU:
|
||||
if (len < sizeof(mtu)) {
|
||||
logerrx("%s: short MTU option", ifp->name);
|
||||
logfunc("%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) {
|
||||
logerrx("%s: invalid MTU %d",
|
||||
logfunc("%s: invalid MTU %d",
|
||||
ifp->name, mtu.nd_opt_mtu_mtu);
|
||||
break;
|
||||
}
|
||||
rap->mtu = mtu.nd_opt_mtu_mtu;
|
||||
ifmtu = if_getmtu(ifp);
|
||||
if (ifmtu == -1)
|
||||
logerr("if_getmtu");
|
||||
else if (mtu.nd_opt_mtu_mtu > (uint32_t)ifmtu) {
|
||||
logfunc("%s: advertised MTU %d"
|
||||
" is greater than link MTU %d",
|
||||
ifp->name, mtu.nd_opt_mtu_mtu, ifmtu);
|
||||
rap->mtu = (uint32_t)ifmtu;
|
||||
} else
|
||||
rap->mtu = mtu.nd_opt_mtu_mtu;
|
||||
break;
|
||||
|
||||
case ND_OPT_RDNSS:
|
||||
if (len < sizeof(rdnss)) {
|
||||
logerrx("%s: short RDNSS option", ifp->name);
|
||||
logfunc("%s: short RDNSS option", ifp->name);
|
||||
break;
|
||||
}
|
||||
memcpy(&rdnss, p, sizeof(rdnss));
|
||||
|
@ -1295,12 +1337,15 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
|
|||
ifp->name);
|
||||
|
||||
if (new_rap)
|
||||
add_router(ifp->ctx, rap);
|
||||
TAILQ_INSERT_TAIL(ctx->ra_routers, rap, next);
|
||||
if (new_data)
|
||||
ipv6nd_sortrouters(ifp->ctx);
|
||||
|
||||
if (ifp->ctx->options & DHCPCD_TEST) {
|
||||
script_runreason(ifp, "TEST");
|
||||
goto handle_flag;
|
||||
}
|
||||
ipv6nd_applyra(ifp->ctx, ifp);
|
||||
ipv6_addaddrs(&rap->addrs);
|
||||
#ifdef IPV6_MANAGETEMPADDR
|
||||
ipv6_addtempaddrs(ifp, &rap->acquired);
|
||||
|
@ -1718,11 +1763,52 @@ ipv6nd_drop(struct interface *ifp)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ipv6nd_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg)
|
||||
{
|
||||
struct sockaddr_in6 *from = (struct sockaddr_in6 *)msg->msg_name;
|
||||
char sfrom[INET6_ADDRSTRLEN];
|
||||
int hoplimit = 0;
|
||||
struct icmp6_hdr *icp;
|
||||
struct interface *ifp;
|
||||
size_t len = msg->msg_iov[0].iov_len;
|
||||
|
||||
inet_ntop(AF_INET6, &from->sin6_addr, sfrom, sizeof(sfrom));
|
||||
if ((size_t)len < sizeof(struct icmp6_hdr)) {
|
||||
logerrx("IPv6 ICMP packet too short from %s", sfrom);
|
||||
return;
|
||||
}
|
||||
|
||||
ifp = if_findifpfromcmsg(ctx, msg, &hoplimit);
|
||||
if (ifp == NULL) {
|
||||
logerr(__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Don't do anything if the user hasn't configured it. */
|
||||
if (ifp->active != IF_ACTIVE_USER ||
|
||||
!(ifp->options->options & DHCPCD_IPV6))
|
||||
return;
|
||||
|
||||
icp = (struct icmp6_hdr *)msg->msg_iov[0].iov_base;
|
||||
if (icp->icmp6_code == 0) {
|
||||
switch(icp->icmp6_type) {
|
||||
case ND_ROUTER_ADVERT:
|
||||
ipv6nd_handlera(ctx, from, sfrom,
|
||||
ifp, icp, (size_t)len, hoplimit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
logerrx("invalid IPv6 type %d or code %d from %s",
|
||||
icp->icmp6_type, icp->icmp6_code, sfrom);
|
||||
}
|
||||
|
||||
static void
|
||||
ipv6nd_handledata(void *arg)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx;
|
||||
int s;
|
||||
int fd;
|
||||
struct sockaddr_in6 from;
|
||||
unsigned char buf[64 * 1024]; /* Maximum ICMPv6 size */
|
||||
struct iovec iov = {
|
||||
|
@ -1736,60 +1822,27 @@ ipv6nd_handledata(void *arg)
|
|||
.msg_control = ctl, .msg_controllen = sizeof(ctl),
|
||||
};
|
||||
ssize_t len;
|
||||
char sfrom[INET6_ADDRSTRLEN];
|
||||
int hoplimit = 0;
|
||||
struct icmp6_hdr *icp;
|
||||
struct interface *ifp;
|
||||
|
||||
#ifdef __sun
|
||||
struct interface *ifp;
|
||||
struct rs_state *state;
|
||||
|
||||
ifp = arg;
|
||||
state = RS_STATE(ifp);
|
||||
ctx = ifp->ctx;
|
||||
s = state->nd_fd;
|
||||
fd = state->nd_fd;
|
||||
#else
|
||||
ctx = arg;
|
||||
s = ctx->nd_fd;
|
||||
fd = ctx->nd_fd;
|
||||
#endif
|
||||
len = recvmsg(s, &msg, 0);
|
||||
len = recvmsg(fd, &msg, 0);
|
||||
if (len == -1) {
|
||||
logerr(__func__);
|
||||
return;
|
||||
}
|
||||
inet_ntop(AF_INET6, &from.sin6_addr, sfrom, sizeof(sfrom));
|
||||
if ((size_t)len < sizeof(struct icmp6_hdr)) {
|
||||
logerrx("IPv6 ICMP packet too short from %s", sfrom);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __sun
|
||||
if_findifpfromcmsg(ctx, &msg, &hoplimit);
|
||||
#else
|
||||
ifp = if_findifpfromcmsg(ctx, &msg, &hoplimit);
|
||||
if (ifp == NULL) {
|
||||
logerr(__func__);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Don't do anything if the user hasn't configured it. */
|
||||
if (ifp->active != IF_ACTIVE_USER ||
|
||||
!(ifp->options->options & DHCPCD_IPV6))
|
||||
return;
|
||||
|
||||
icp = (struct icmp6_hdr *)buf;
|
||||
if (icp->icmp6_code == 0) {
|
||||
switch(icp->icmp6_type) {
|
||||
case ND_ROUTER_ADVERT:
|
||||
ipv6nd_handlera(ctx, &from, sfrom,
|
||||
ifp, icp, (size_t)len, hoplimit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
logerrx("invalid IPv6 type %d or code %d from %s",
|
||||
icp->icmp6_type, icp->icmp6_code, sfrom);
|
||||
iov.iov_len = (size_t)len;
|
||||
ipv6nd_recvmsg(ctx, &msg);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -50,6 +50,7 @@ struct ra {
|
|||
uint32_t reachable;
|
||||
uint32_t retrans;
|
||||
uint32_t mtu;
|
||||
uint8_t hoplimit;
|
||||
struct ipv6_addrhead addrs;
|
||||
bool hasdns;
|
||||
bool expired;
|
||||
|
@ -78,6 +79,10 @@ struct rs_state {
|
|||
#define RTR_SOLICITATION_INTERVAL 4 /* seconds */
|
||||
#define MAX_RTR_SOLICITATIONS 3 /* times */
|
||||
#define MAX_NEIGHBOR_ADVERTISEMENT 3 /* 3 transmissions */
|
||||
|
||||
#ifndef IPV6_DEFHLIM
|
||||
#define IPV6_DEFHLIM 64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* On carrier up, expire known routers after RTR_CARRIER_EXPIRE seconds. */
|
||||
|
|
|
@ -86,9 +86,9 @@ if_printoptions(void)
|
|||
}
|
||||
|
||||
static int
|
||||
exec_script(const struct dhcpcd_ctx *ctx, char *const *argv, char *const *env)
|
||||
script_exec(const struct dhcpcd_ctx *ctx, char *const *argv, char *const *env)
|
||||
{
|
||||
pid_t pid;
|
||||
pid_t pid = 0;
|
||||
posix_spawnattr_t attr;
|
||||
int r;
|
||||
#ifdef USE_SIGNALS
|
||||
|
@ -173,14 +173,55 @@ efprintf(FILE *fp, const char *fmt, ...)
|
|||
return r;
|
||||
}
|
||||
|
||||
static char **
|
||||
script_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len)
|
||||
{
|
||||
char **env, **envp, *bufp, *endp;
|
||||
size_t nenv;
|
||||
|
||||
/* Count the terminated env strings.
|
||||
* Assert that the terminations are correct. */
|
||||
nenv = 0;
|
||||
endp = buf + len;
|
||||
for (bufp = buf; bufp < endp; bufp++) {
|
||||
if (*bufp == '\0') {
|
||||
#ifndef NDEBUG
|
||||
if (bufp + 1 < endp)
|
||||
assert(*(bufp + 1) != '\0');
|
||||
#endif
|
||||
nenv++;
|
||||
}
|
||||
}
|
||||
assert(*(bufp - 1) == '\0');
|
||||
|
||||
if (ctx->script_envlen < nenv) {
|
||||
env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env));
|
||||
if (env == NULL)
|
||||
return NULL;
|
||||
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 ctx->script_env;
|
||||
}
|
||||
|
||||
static long
|
||||
make_env(const struct interface *ifp, const char *reason)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = ifp->ctx;
|
||||
FILE *fp;
|
||||
char **env, **envp, *bufp, *endp, *path;
|
||||
size_t nenv;
|
||||
long buf_pos, i;
|
||||
char *path;
|
||||
int protocol = PROTO_LINK;
|
||||
const struct if_options *ifo = ifp->options;
|
||||
const struct interface *ifp2;
|
||||
|
@ -377,7 +418,7 @@ make_env(const struct interface *ifp, const char *reason)
|
|||
if (efprintf(fp, "syslog_debug=true") == -1)
|
||||
goto eexit;
|
||||
}
|
||||
if (*ifp->profile) {
|
||||
if (*ifp->profile != '\0') {
|
||||
if (efprintf(fp, "profile=%s", ifp->profile) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
|
@ -476,38 +517,8 @@ dumplease:
|
|||
fp = NULL;
|
||||
#endif
|
||||
|
||||
/* Count the terminated env strings.
|
||||
* Assert that the terminations are correct. */
|
||||
nenv = 0;
|
||||
endp = ctx->script_buf + buf_pos;
|
||||
for (bufp = ctx->script_buf; bufp < endp; bufp++) {
|
||||
if (*bufp == '\0') {
|
||||
#ifndef NDEBUG
|
||||
if (bufp + 1 < endp)
|
||||
assert(*(bufp + 1) != '\0');
|
||||
#endif
|
||||
nenv++;
|
||||
}
|
||||
}
|
||||
assert(*(bufp - 1) == '\0');
|
||||
|
||||
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 = ctx->script_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;
|
||||
if (script_buftoenv(ctx, ctx->script_buf, (size_t)buf_pos) == NULL)
|
||||
goto eexit;
|
||||
|
||||
return buf_pos - 1;
|
||||
|
||||
|
@ -620,7 +631,7 @@ script_runreason(const struct interface *ifp, const char *reason)
|
|||
argv[1] = NULL;
|
||||
logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason);
|
||||
|
||||
pid = exec_script(ctx, argv, ctx->script_env);
|
||||
pid = script_exec(ctx, argv, ctx->script_env);
|
||||
if (pid == -1)
|
||||
logerr("%s: %s", __func__, argv[0]);
|
||||
else if (pid != 0) {
|
||||
|
|
Loading…
Reference in New Issue