Import dhcpcd-7.2.0 with the following changes:

*  BSD: PF_LINK sockets now closed when no longer needed
  *  BSD: Fix detecting interface for scoped routes
  *  script: Allow "" to mean /dev/null
  *  script: Add static routers and routes to env
  *  DHCP: outbound interface is no longer dictated with IP_PKTINFO
  *  DHCP: BPF sockets now closed when no longer needed
  *  DHCPv6: Allow nooption dhcp6_unicast to work
  *  DHCPv6: Don't spam syslog if we always get the same error
  *  route: Log pid which deleted routes of interest

This release fixes PR bin/53705.
This commit is contained in:
roy 2019-04-17 23:33:08 +00:00
parent a5e54c894f
commit dbd4cee32e
24 changed files with 667 additions and 565 deletions

View File

@ -175,17 +175,18 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len)
}
}
void
static void
arp_close(struct interface *ifp)
{
struct iarp_state *state;
if ((state = ARP_STATE(ifp)) != NULL && state->bpf_fd != -1) {
eloop_event_delete(ifp->ctx->eloop, state->bpf_fd);
bpf_close(ifp, state->bpf_fd);
state->bpf_fd = -1;
state->bpf_flags |= BPF_EOF;
}
if ((state = ARP_STATE(ifp)) == NULL || state->bpf_fd == -1)
return;
eloop_event_delete(ifp->ctx->eloop, state->bpf_fd);
bpf_close(ifp, state->bpf_fd);
state->bpf_fd = -1;
state->bpf_flags |= BPF_EOF;
}
static void

View File

@ -90,7 +90,6 @@ struct iarp_state {
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_close(struct interface *);
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 *);

View File

@ -200,51 +200,3 @@ read_hwaddr_aton(uint8_t **data, const char *path)
fclose(fp);
return len;
}
ssize_t
recvmsg_realloc(int fd, struct msghdr *msg, int flags)
{
struct iovec *iov;
ssize_t slen;
size_t len;
void *n;
assert(msg != NULL);
assert(msg->msg_iov != NULL && msg->msg_iovlen > 0);
assert((flags & (MSG_PEEK | MSG_TRUNC)) == 0);
/* Assume we are reallocing the last iovec. */
iov = &msg->msg_iov[msg->msg_iovlen - 1];
for (;;) {
/* Passing MSG_TRUNC should return the actual size needed. */
slen = recvmsg(fd, msg, flags | MSG_PEEK | MSG_TRUNC);
if (slen == -1)
return -1;
if (!(msg->msg_flags & MSG_TRUNC))
break;
len = (size_t)slen;
/* Some kernels return the size of the receive buffer
* on truncation, not the actual size needed.
* So grow the buffer and try again. */
if (iov->iov_len == len)
len++;
else if (iov->iov_len > len)
break;
len = roundup(len, IOVEC_BUFSIZ);
if ((n = realloc(iov->iov_base, len)) == NULL)
return -1;
iov->iov_base = n;
iov->iov_len = len;
}
slen = recvmsg(fd, msg, flags);
if (slen != -1 && msg->msg_flags & MSG_TRUNC) {
/* This should not be possible ... */
errno = ENOBUFS;
return -1;
}
return slen;
}

View File

@ -181,6 +181,4 @@ 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 *);
ssize_t recvmsg_realloc(int, struct msghdr *, int);
#endif

View File

@ -28,7 +28,7 @@
#define CONFIG_H
#define PACKAGE "dhcpcd"
#define VERSION "7.1.1"
#define VERSION "7.2.0"
#ifndef CONFIG
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"

View File

@ -86,9 +86,9 @@
#define IPDEFTTL 64 /* RFC1340 */
#endif
/* NetBSD-7 has an incomplete IP_PKTINFO implementation. */
#if defined(__NetBSD_Version__) && __NetBSD_Version__ < 800000000
#undef IP_PKTINFO
/* Support older systems with different defines */
#if !defined(IP_RECVPKTINFO) && defined(IP_PKTINFO)
#define IP_RECVPKTINFO IP_PKTINFO
#endif
/* Assert the correct structure size for on wire */
@ -129,6 +129,9 @@ static void dhcp_arp_conflicted(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
@ -447,7 +450,7 @@ decode_rfc3442_rt(struct rt_head *routes, struct interface *ifp,
memcpy(&gateway.s_addr, p, 4);
p += 4;
/* A host route is normally set by having the
/* An on-link host route is normally set by having the
* gateway match the destination or assigned address */
if (gateway.s_addr == dest.s_addr ||
(gateway.s_addr == bootp->yiaddr ||
@ -455,17 +458,14 @@ decode_rfc3442_rt(struct rt_head *routes, struct interface *ifp,
{
gateway.s_addr = INADDR_ANY;
netmask.s_addr = INADDR_BROADCAST;
rt->rt_flags = RTF_HOST;
}
if (netmask.s_addr == INADDR_BROADCAST)
rt->rt_flags = RTF_HOST;
sa_in_init(&rt->rt_dest, &dest);
sa_in_init(&rt->rt_netmask, &netmask);
sa_in_init(&rt->rt_gateway, &gateway);
/* If CIDR is 32 then it's a host route. */
if (cidr == 32)
rt->rt_flags = RTF_HOST;
TAILQ_INSERT_TAIL(routes, rt, rt_next);
n++;
}
@ -638,7 +638,7 @@ get_option_routes(struct rt_head *routes, struct interface *ifp,
if ((rt = rt_new(ifp)) == NULL)
return -1;
/* A host route is normally set by having the
/* A on-link host route is normally set by having the
* gateway match the destination or assigned address */
if (gateway.s_addr == dest.s_addr ||
(gateway.s_addr == bootp->yiaddr ||
@ -646,12 +646,15 @@ get_option_routes(struct rt_head *routes, struct interface *ifp,
{
gateway.s_addr = INADDR_ANY;
netmask.s_addr = INADDR_BROADCAST;
rt->rt_flags = RTF_HOST;
} else
netmask.s_addr = route_netmask(dest.s_addr);
if (netmask.s_addr == INADDR_BROADCAST)
rt->rt_flags = RTF_HOST;
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++;
}
@ -1587,6 +1590,11 @@ dhcp_close(struct interface *ifp)
state->bpf_fd = -1;
state->bpf_flags |= BPF_EOF;
}
if (state->udp_fd != -1) {
eloop_event_delete(ifp->ctx->eloop, state->udp_fd);
close(state->udp_fd);
state->udp_fd = -1;
}
state->interval = 0;
}
@ -1604,11 +1612,15 @@ dhcp_openudp(struct interface *ifp)
n = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)
goto eexit;
#ifdef IP_RECVPKTINFO
if (setsockopt(s, IPPROTO_IP, IP_RECVPKTINFO, &n, sizeof(n)) == -1)
goto eexit;
#endif
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(BOOTPC);
if (ifp) {
struct dhcp_state *state = D_STATE(ifp);
const struct dhcp_state *state = D_CSTATE(ifp);
if (state->addr)
sin.sin_addr.s_addr = state->addr->addr.s_addr;
@ -1699,12 +1711,8 @@ dhcp_sendudp(struct interface *ifp, struct in_addr *to, void *data, size_t len)
struct msghdr msg;
struct sockaddr_in sin;
struct iovec iov[1];
struct dhcp_state *state = D_STATE(ifp);
ssize_t r;
#ifdef IP_PKTINFO
uint8_t cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))];
struct cmsghdr *cm;
struct in_pktinfo ipi;
#endif
iov[0].iov_base = data;
iov[0].iov_len = len;
@ -1723,29 +1731,15 @@ dhcp_sendudp(struct interface *ifp, struct in_addr *to, void *data, size_t len)
msg.msg_iov = iov;
msg.msg_iovlen = 1;
#ifdef IP_PKTINFO
/* Set the outbound interface */
msg.msg_control = cmsg;
msg.msg_controllen = sizeof(cmsg);
memset(&ipi, 0, sizeof(ipi));
ipi.ipi_ifindex = ifp->index;
cm = CMSG_FIRSTHDR(&msg);
if (cm == NULL) {
errno = ESRCH;
return -1;
s = state->udp_fd;
if (s == -1) {
s = dhcp_openudp(ifp);
if (s == -1)
return -1;
}
cm->cmsg_level = IPPROTO_IP;
cm->cmsg_type = IP_PKTINFO;
cm->cmsg_len = CMSG_LEN(sizeof(ipi));
memcpy(CMSG_DATA(cm), &ipi, sizeof(ipi));
#endif
s = dhcp_openudp(ifp);
if (s == -1)
return -1;
r = sendmsg(s, &msg, 0);
close(s);
if (state->udp_fd == -1)
close(s);
return r;
}
@ -1803,7 +1797,7 @@ send_message(struct interface *ifp, uint8_t type,
else
to.s_addr = INADDR_ANY;
/* If unicasting, try and void sending by BPF so we don't
/* If unicasting, try and avoid sending by BPF so we don't
* use a L2 broadcast. */
if (to.s_addr != INADDR_ANY && to.s_addr != INADDR_BROADCAST) {
if (dhcp_sendudp(ifp, &to, bootp, len) != -1)
@ -2068,11 +2062,6 @@ dhcp_arp_probed(struct arp_state *astate)
return;
}
arp_free(astate);
#ifdef KERNEL_RFC5227
/* As arping is finished, close the ARP socket.
* The kernel will handle ACD from here. */
arp_close(ifp);
#endif
dhcpcd_startinterface(ifp);
return;
}
@ -2150,11 +2139,6 @@ dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
return;
}
arp_free(astate);
#ifdef KERNEL_RFC5227
/* As arping is finished, close the ARP socket.
* The kernel will handle ACD from here. */
arp_close(ifp);
#endif
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
dhcpcd_startinterface(ifp);
return;
@ -2209,11 +2193,22 @@ dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
return;
}
}
static void
dhcp_arp_announced(struct arp_state *state)
{
// TODO: DHCP addresses handle ACD?
//#ifdef KERNEL_RFC5227
arp_free(state);
//#endif
}
#endif
void
dhcp_bind(struct interface *ifp)
{
struct dhcpcd_ctx *ctx = ifp->ctx;
struct dhcp_state *state = D_STATE(ifp);
struct if_options *ifo = ifp->options;
struct dhcp_lease *lease = &state->lease;
@ -2289,10 +2284,10 @@ dhcp_bind(struct interface *ifp)
lease->leasetime);
}
}
if (ifp->ctx->options & DHCPCD_TEST) {
if (ctx->options & DHCPCD_TEST) {
state->reason = "TEST";
script_runreason(ifp, state->reason);
eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
eloop_exit(ctx->eloop, EXIT_SUCCESS);
return;
}
if (state->reason == NULL) {
@ -2311,26 +2306,42 @@ dhcp_bind(struct interface *ifp)
if (lease->leasetime == ~0U)
lease->renewaltime = lease->rebindtime = lease->leasetime;
else {
eloop_timeout_add_sec(ifp->ctx->eloop,
eloop_timeout_add_sec(ctx->eloop,
(time_t)lease->renewaltime, dhcp_startrenew, ifp);
eloop_timeout_add_sec(ifp->ctx->eloop,
eloop_timeout_add_sec(ctx->eloop,
(time_t)lease->rebindtime, dhcp_rebind, ifp);
eloop_timeout_add_sec(ifp->ctx->eloop,
eloop_timeout_add_sec(ctx->eloop,
(time_t)lease->leasetime, dhcp_expire, ifp);
logdebugx("%s: renew in %"PRIu32" seconds, rebind in %"PRIu32
" seconds",
ifp->name, lease->renewaltime, lease->rebindtime);
}
state->state = DHS_BOUND;
/* Re-apply the filter because we need to accept any XID anymore. */
if (bpf_bootp(ifp, state->bpf_fd) == -1)
logerr(__func__); /* try to continue */
if (!state->lease.frominfo &&
!(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
if (write_lease(ifp, state->new, state->new_len) == -1)
logerr(__func__);
ipv4_applyaddr(ifp);
#ifdef IP_PKTINFO
/* Close the BPF filter as we can now receive the DHCP renew 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__);
else
eloop_event_add(ctx->eloop,
state->udp_fd, dhcp_handleifudp, ifp);
}
}
#endif
}
static void
@ -2384,6 +2395,20 @@ dhcp_message_new(struct bootp **bootp,
}
#ifdef ARP
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->announced_cb = dhcp_arp_announced;
return astate;
}
static int
dhcp_arp_address(struct interface *ifp)
{
@ -2400,10 +2425,9 @@ 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);
if ((astate = arp_new(ifp, &addr)) == NULL)
astate = dhcp_arp_new(ifp, &addr);
if (astate == NULL)
return -1;
astate->probed_cb = dhcp_arp_probed;
astate->conflicted_cb = dhcp_arp_conflicted;
#ifdef IN_IFF_TENTATIVE
if (ia == NULL || ia->addr_flags & IN_IFF_NOTUSEABLE) {
@ -2440,7 +2464,8 @@ static void
dhcp_arp_bind(struct interface *ifp)
{
if (dhcp_arp_address(ifp) == 1)
if (ifp->ctx->options & DHCPCD_TEST ||
dhcp_arp_address(ifp) == 1)
dhcp_bind(ifp);
}
#endif
@ -2879,14 +2904,11 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
#define LOGDHCP(l, m) \
log_dhcp((l), (m), ifp, bootp, bootp_len, from, 1)
/* Handled in our BPF filter. */
#if 0
if (bootp->op != BOOTREPLY) {
logdebugx("%s: op (%d) is not BOOTREPLY",
ifp->name, bootp->op);
return;
}
#endif
if (state->xid != ntohl(bootp->xid)) {
if (state->state != DHS_BOUND && state->state != DHS_NONE)
@ -3190,6 +3212,7 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
state->reason = "TEST";
script_runreason(ifp, state->reason);
eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
state->bpf_flags |= BPF_EOF;
return;
}
eloop_timeout_delete(ifp->ctx->eloop, send_discover, ifp);
@ -3336,6 +3359,30 @@ valid_udp_packet(void *data, size_t data_len, struct in_addr *from,
return 0;
}
static void
dhcp_handlebootp(struct interface *ifp, struct bootp *bootp, size_t len,
struct in_addr *from)
{
size_t v;
/* udp_len must be correct because the values are checked in
* valid_udp_packet(). */
if (len < offsetof(struct bootp, vend)) {
logerrx("%s: truncated packet (%zu) from %s",
ifp->name, len, inet_ntoa(*from));
return;
}
/* To make our IS_DHCP macro easy, ensure the vendor
* area has at least 4 octets. */
v = len - offsetof(struct bootp, vend);
while (v < 4) {
bootp->vend[v++] = '\0';
len++;
}
dhcp_handledhcp(ifp, bootp, len, from);
}
static void
dhcp_handlepacket(struct interface *ifp, uint8_t *data, size_t len)
{
@ -3370,22 +3417,7 @@ dhcp_handlepacket(struct interface *ifp, uint8_t *data, size_t len)
* dhcpcd can work fine without the vendor area being sent.
*/
bootp = get_udp_data(data, &udp_len);
/* udp_len must be correct because the values are checked in
* valid_udp_packet(). */
if (udp_len < offsetof(struct bootp, vend)) {
logerrx("%s: truncated packet (%zu) from %s",
ifp->name, udp_len, inet_ntoa(from));
return;
}
/* To make our IS_DHCP macro easy, ensure the vendor
* area has at least 4 octets. */
len = udp_len - offsetof(struct bootp, vend);
while (len < 4) {
bootp->vend[len++] = '\0';
udp_len++;
}
dhcp_handledhcp(ifp, bootp, udp_len, &from);
dhcp_handlebootp(ifp, bootp, udp_len, &from);
}
static void
@ -3420,24 +3452,77 @@ dhcp_readpacket(void *arg)
state->bpf_flags &= ~BPF_READING;
}
static void
dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp)
{
struct sockaddr_in from;
unsigned char buf[10 * 1024]; /* Maximum MTU */
struct iovec iov = {
.iov_base = buf,
.iov_len = sizeof(buf),
};
#ifdef IP_PKTINFO
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;
if (ifp != NULL) {
const struct dhcp_state *state = D_CSTATE(ifp);
s = state->udp_fd;
} else
s = ctx->udp_fd;
bytes = recvmsg(s, &msg, 0);
if (bytes == -1) {
logerr(__func__);
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;
}
}
dhcp_handlebootp(ifp, (struct bootp *)buf, (size_t)bytes,
&from.sin_addr);
#endif
}
static void
dhcp_handleudp(void *arg)
{
struct dhcpcd_ctx *ctx;
uint8_t buffer[MTU_MAX];
struct dhcpcd_ctx *ctx = arg;
ctx = arg;
/* Just read what's in the UDP fd and discard it as we always read
* from the raw fd */
if (read(ctx->udp_fd, buffer, sizeof(buffer)) == -1) {
logerr(__func__);
eloop_event_delete(ctx->eloop, ctx->udp_fd);
close(ctx->udp_fd);
ctx->udp_fd = -1;
}
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_openbpf(struct interface *ifp)
{
@ -3547,6 +3632,7 @@ dhcp_initstate(struct interface *ifp)
state->state = DHS_NONE;
/* 0 is a valid fd, so init to -1 */
state->bpf_fd = -1;
state->udp_fd = -1;
#ifdef ARPING
state->arping_index = -1;
#endif
@ -3676,12 +3762,9 @@ dhcp_start1(void *arg)
if (ifo->arping_len && state->arping_index < ifo->arping_len) {
struct arp_state *astate;
astate = arp_new(ifp, NULL);
if (astate) {
astate->probed_cb = dhcp_arp_probed;
astate->conflicted_cb = dhcp_arp_conflicted;
astate = dhcp_arp_new(ifp, NULL);
if (astate)
dhcp_arp_probed(astate);
}
return;
}
#endif
@ -3691,13 +3774,11 @@ dhcp_start1(void *arg)
return;
}
if (ifo->options & DHCPCD_DHCP && dhcp_openbpf(ifp) == -1)
return;
if (ifo->options & DHCPCD_INFORM) {
dhcp_inform(ifp);
return;
}
if (ifp->hwlen == 0 && ifo->clientid[0] == '\0') {
logwarnx("%s: needs a clientid to configure", ifp->name);
dhcp_drop(ifp, "FAIL");

View File

@ -216,6 +216,7 @@ struct dhcp_state {
int bpf_fd;
unsigned int bpf_flags;
int udp_fd;
struct ipv4_addr *addr;
uint8_t added;

View File

@ -168,7 +168,7 @@ static const char * const dhcp6_statuses[] = {
"No Prefix Available"
};
static void dhcp6_bind(struct interface *, const char *);
static void dhcp6_bind(struct interface *, const char *, const char *);
static void dhcp6_failinform(void *);
static int dhcp6_listen(struct dhcpcd_ctx *, struct ipv6_addr *);
static void dhcp6_recvaddr(void *);
@ -797,8 +797,7 @@ dhcp6_makemessage(struct interface *ifp)
m = state->new;
ml = state->new_len;
}
unicast = NULL;
/* Depending on state, get the unicast address */
switch(state->state) {
case DH6S_INIT: /* FALLTHROUGH */
case DH6S_DISCOVER:
@ -806,7 +805,6 @@ dhcp6_makemessage(struct interface *ifp)
break;
case DH6S_REQUEST:
type = DHCP6_REQUEST;
unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len);
break;
case DH6S_CONFIRM:
type = DHCP6_CONFIRM;
@ -816,20 +814,33 @@ dhcp6_makemessage(struct interface *ifp)
break;
case DH6S_RENEW:
type = DHCP6_RENEW;
unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len);
break;
case DH6S_INFORM:
type = DHCP6_INFORMATION_REQ;
break;
case DH6S_RELEASE:
type = DHCP6_RELEASE;
unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len);
break;
default:
errno = EINVAL;
return -1;
}
switch(state->state) {
case DH6S_REQUEST: /* FALLTHROUGH */
case DH6S_RENEW: /* FALLTHROUGH */
case DH6S_RELEASE:
if (has_option_mask(ifo->nomask6, D6_OPTION_UNICAST)) {
unicast = NULL;
break;
}
unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len);
break;
default:
unicast = NULL;
break;
}
/* In non master mode we listen and send from fixed addresses.
* We should try and match an address we have to unicast to,
* but for now this is the safest policy. */
@ -1157,9 +1168,12 @@ dhcp6_update_auth(struct interface *ifp, struct dhcp6_message *m, size_t len)
static int
dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
{
struct dhcp6_state *state;
struct dhcpcd_ctx *ctx;
struct sockaddr_in6 dst;
struct dhcp6_state *state = D6_STATE(ifp);
struct dhcpcd_ctx *ctx = ifp->ctx;
struct sockaddr_in6 dst = {
.sin6_family = AF_INET6,
.sin6_port = htons(DHCP6_SERVER_PORT),
};
struct timespec RTprev;
double rnd;
time_t ms;
@ -1168,18 +1182,22 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
const struct in6_addr alldhcp = IN6ADDR_LINKLOCAL_ALLDHCP_INIT;
struct ipv6_addr *lla;
int s;
struct iovec iov = {
.iov_base = state->send, .iov_len = state->send_len,
};
unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 };
struct msghdr msg = {
.msg_name = &dst, .msg_namelen = sizeof(dst),
.msg_iov = &iov, .msg_iovlen = 1,
};
if (!callback && ifp->carrier <= LINK_DOWN)
return 0;
memset(&dst, 0, sizeof(dst));
dst.sin6_family = AF_INET6;
dst.sin6_port = htons(DHCP6_SERVER_PORT);
#ifdef HAVE_SA_LEN
dst.sin6_len = sizeof(dst);
#endif
state = D6_STATE(ifp);
lla = ipv6_linklocal(ifp);
/* We need to ensure we have sufficient scope to unicast the address */
/* XXX FIXME: We should check any added addresses we have like from
@ -1280,7 +1298,7 @@ logsend:
/* Wait the initial delay */
if (state->IMD != 0) {
state->IMD = 0;
eloop_timeout_add_tv(ifp->ctx->eloop,
eloop_timeout_add_tv(ctx->eloop,
&state->RT, callback, ifp);
return 0;
}
@ -1301,31 +1319,21 @@ logsend:
}
#endif
ctx = ifp->ctx;
ctx->sndhdr.msg_name = (void *)&dst;
ctx->sndhdr.msg_iov[0].iov_base = state->send;
ctx->sndhdr.msg_iov[0].iov_len = state->send_len;
/* Set the outbound interface */
if (IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &alldhcp)) {
struct cmsghdr *cm;
struct in6_pktinfo pi;
struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
dst.sin6_scope_id = ifp->index;
cm = CMSG_FIRSTHDR(&ctx->sndhdr);
msg.msg_control = ctl;
msg.msg_controllen = sizeof(ctl);
cm = CMSG_FIRSTHDR(&msg);
if (cm == NULL) /* unlikely */
return -1;
cm->cmsg_level = IPPROTO_IPV6;
cm->cmsg_type = IPV6_PKTINFO;
cm->cmsg_len = CMSG_LEN(sizeof(pi));
memset(&pi, 0, sizeof(pi));
pi.ipi6_ifindex = ifp->index;
memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
} else {
/* Remove the control buffer as we're not dictating
* which interface to use for outgoing messages. */
ctx->sndhdr.msg_control = NULL;
ctx->sndhdr.msg_controllen = 0;
}
if (ctx->dhcp6_fd != -1)
@ -1337,7 +1345,7 @@ logsend:
return -1;
}
if (sendmsg(s, &ctx->sndhdr, 0) == -1) {
if (sendmsg(s, &msg, 0) == -1) {
logerr("%s: %s: sendmsg", __func__, ifp->name);
/* Allow DHCPv6 to continue .... the errors
* would be rate limited by the protocol.
@ -1345,19 +1353,13 @@ logsend:
* associate with an access point. */
}
/* Restore the control buffer assignment. */
if (!IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &alldhcp)) {
ctx->sndhdr.msg_control = ctx->sndbuf;
ctx->sndhdr.msg_controllen = sizeof(ctx->sndbuf);
}
state->RTC++;
if (callback) {
if (state->MRC == 0 || state->RTC < state->MRC)
eloop_timeout_add_tv(ifp->ctx->eloop,
eloop_timeout_add_tv(ctx->eloop,
&state->RT, callback, ifp);
else if (state->MRC != 0 && state->MRCcallback)
eloop_timeout_add_tv(ifp->ctx->eloop,
eloop_timeout_add_tv(ctx->eloop,
&state->RT, state->MRCcallback, ifp);
else
logwarnx("%s: sent %d times with no reply",
@ -1650,7 +1652,7 @@ dhcp6_fail(struct interface *ifp)
break;
}
dhcp6_bind(ifp, NULL);
dhcp6_bind(ifp, NULL, NULL);
switch (state->state) {
case DH6S_BOUND:
@ -1911,13 +1913,16 @@ static int
dhcp6_checkstatusok(const struct interface *ifp,
struct dhcp6_message *m, uint8_t *p, size_t len)
{
struct dhcp6_state *state;
uint8_t *opt;
uint16_t opt_len, code;
size_t mlen;
void * (*f)(void *, size_t, uint16_t, uint16_t *), *farg;
char buf[32], *sbuf;
const char *status;
logfunc_t *logfunc;
state = D6_STATE(ifp);
f = p ? dhcp6_findoption : dhcp6_findmoption;
if (p)
farg = p;
@ -1925,6 +1930,7 @@ dhcp6_checkstatusok(const struct interface *ifp,
farg = m;
if ((opt = f(farg, len, D6_OPTION_STATUS_CODE, &opt_len)) == NULL) {
//logdebugx("%s: no status", ifp->name);
state->lerror = 0;
return 0;
}
@ -1934,8 +1940,10 @@ dhcp6_checkstatusok(const struct interface *ifp,
}
memcpy(&code, opt, sizeof(code));
code = ntohs(code);
if (code == D6_STATUS_OK)
if (code == D6_STATUS_OK) {
state->lerror = 0;
return 1;
}
/* Anything after the code is a message. */
opt += sizeof(code);
@ -1958,8 +1966,13 @@ dhcp6_checkstatusok(const struct interface *ifp,
status = sbuf;
}
logerrx("%s: DHCPv6 REPLY: %s", ifp->name, status);
if (state->lerror == code || state->state == DH6S_INIT)
logfunc = logdebugx;
else
logfunc = logerrx;
logfunc("%s: DHCPv6 REPLY: %s", ifp->name, status);
free(sbuf);
state->lerror = code;
return -1;
}
@ -2927,7 +2940,7 @@ dhcp6_find_delegates(struct interface *ifp)
#endif
static void
dhcp6_bind(struct interface *ifp, const char *op)
dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
{
struct dhcp6_state *state = D6_STATE(ifp);
bool has_new = false;
@ -2943,8 +2956,7 @@ dhcp6_bind(struct interface *ifp, const char *op)
}
lognewinfo = has_new ? loginfox : logdebugx;
if (op != NULL)
lognewinfo("%s: %s received from %s",
ifp->name, op, ifp->ctx->sfrom);
lognewinfo("%s: %s received from %s", ifp->name, op, sfrom);
state->reason = NULL;
if (state->state != DH6S_ITIMEDOUT)
@ -3176,7 +3188,8 @@ dhcp6_bind(struct interface *ifp, const char *op)
}
static void
dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len)
dhcp6_recvif(struct interface *ifp, const char *sfrom,
struct dhcp6_message *r, size_t len)
{
struct dhcpcd_ctx *ctx;
size_t i;
@ -3211,8 +3224,7 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len)
}
if (dhcp6_findmoption(r, len, D6_OPTION_SERVERID, NULL) == NULL) {
logdebugx("%s: no DHCPv6 server ID from %s",
ifp->name, ctx->sfrom);
logdebugx("%s: no DHCPv6 server ID from %s", ifp->name, sfrom);
return;
}
@ -3225,14 +3237,14 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len)
!dhcp6_findmoption(r, len, (uint16_t)opt->option, NULL))
{
logwarnx("%s: reject DHCPv6 (no option %s) from %s",
ifp->name, opt->var, ctx->sfrom);
ifp->name, opt->var, sfrom);
return;
}
if (has_option_mask(ifo->rejectmask6, opt->option) &&
dhcp6_findmoption(r, len, (uint16_t)opt->option, NULL))
{
logwarnx("%s: reject DHCPv6 (option %s) from %s",
ifp->name, opt->var, ctx->sfrom);
ifp->name, opt->var, sfrom);
return;
}
}
@ -3245,7 +3257,7 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len)
(uint8_t *)r, len, 6, r->type, auth, auth_len) == NULL)
{
logerr("%s: authentication failed from %s",
ifp->name, ctx->sfrom);
ifp->name, sfrom);
return;
}
if (state->auth.token)
@ -3256,11 +3268,10 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len)
} else if (ifo->auth.options & DHCPCD_AUTH_SEND) {
if (ifo->auth.options & DHCPCD_AUTH_REQUIRE) {
logerr("%s: no authentication from %s",
ifp->name, ctx->sfrom);
ifp->name, sfrom);
return;
}
logwarnx("%s: no authentication from %s",
ifp->name, ctx->sfrom);
logwarnx("%s: no authentication from %s", ifp->name, sfrom);
}
#endif
@ -3274,8 +3285,7 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len)
return;
break;
case DH6S_CONFIRM:
if (dhcp6_validatelease(ifp, r, len,
ctx->sfrom, NULL) == -1)
if (dhcp6_validatelease(ifp, r, len, sfrom, NULL) == -1)
{
dhcp6_startdiscover(ifp);
return;
@ -3297,8 +3307,7 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len)
case DH6S_REQUEST: /* FALLTHROUGH */
case DH6S_RENEW: /* FALLTHROUGH */
case DH6S_REBIND:
if (dhcp6_validatelease(ifp, r, len,
ctx->sfrom, NULL) == -1)
if (dhcp6_validatelease(ifp, r, len, sfrom, NULL) == -1)
{
/*
* If we can't use the lease, fallback to
@ -3366,7 +3375,7 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len)
logerrx("%s: invalid INF_MAX_RT %u",
ifp->name, max_rt);
}
if (dhcp6_validatelease(ifp, r, len, ctx->sfrom, NULL) == -1)
if (dhcp6_validatelease(ifp, r, len, sfrom, NULL) == -1)
return;
break;
case DHCP6_RECONFIGURE:
@ -3374,12 +3383,12 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len)
if (auth == NULL) {
#endif
logerrx("%s: unauthenticated %s from %s",
ifp->name, op, ctx->sfrom);
ifp->name, op, sfrom);
if (ifo->auth.options & DHCPCD_AUTH_REQUIRE)
return;
#ifdef AUTH
}
loginfox("%s: %s from %s", ifp->name, op, ctx->sfrom);
loginfox("%s: %s from %s", ifp->name, op, sfrom);
o = dhcp6_findmoption(r, len, D6_OPTION_RECONF_MSG, &ol);
if (o == NULL) {
logerrx("%s: missing Reconfigure Message option",
@ -3456,10 +3465,10 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len)
ia = TAILQ_FIRST(&state->addrs);
if (ia == NULL)
loginfox("%s: ADV (no address) from %s",
ifp->name, ctx->sfrom);
ifp->name, sfrom);
else
loginfox("%s: ADV %s from %s",
ifp->name, ia->saddr, ctx->sfrom);
ifp->name, ia->saddr, sfrom);
if (ifp->ctx->options & DHCPCD_TEST)
break;
dhcp6_startrequest(ifp);
@ -3467,97 +3476,81 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len)
}
}
dhcp6_bind(ifp, op);
dhcp6_bind(ifp, op, sfrom);
}
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;
size_t len;
ssize_t bytes;
char sfrom[INET6_ADDRSTRLEN];
struct interface *ifp;
struct dhcp6_message *r;
const struct dhcp6_state *state;
uint8_t *o;
uint16_t ol;
ctx->rcvhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
s = ia != NULL ? ia->dhcp6_fd : ctx->dhcp6_fd;
bytes = recvmsg_realloc(s, &ctx->rcvhdr, 0);
bytes = recvmsg(s, &msg, 0);
if (bytes == -1) {
logerr("%s: recvmsg_realloc", __func__);
logerr(__func__);
return;
}
len = (size_t)bytes;
ctx->sfrom = inet_ntop(AF_INET6, &ctx->from.sin6_addr,
ctx->ntopbuf, sizeof(ctx->ntopbuf));
inet_ntop(AF_INET6, &from.sin6_addr, sfrom, sizeof(sfrom));
if (len < sizeof(struct dhcp6_message)) {
logerrx("DHCPv6 packet too short from %s", ctx->sfrom);
logerrx("DHCPv6 packet too short from %s", sfrom);
return;
}
if (ia != NULL)
ifp = ia->iface;
else {
struct cmsghdr *cm;
struct in6_pktinfo pi;
pi.ipi6_ifindex = 0;
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&ctx->rcvhdr);
cm;
cm = (struct cmsghdr *)CMSG_NXTHDR(&ctx->rcvhdr, cm))
{
if (cm->cmsg_level != IPPROTO_IPV6)
continue;
switch(cm->cmsg_type) {
case IPV6_PKTINFO:
if (cm->cmsg_len == CMSG_LEN(sizeof(pi)))
memcpy(&pi, CMSG_DATA(cm), sizeof(pi));
break;
}
}
if (pi.ipi6_ifindex == 0) {
logerrx("DHCPv6 reply did not contain index from %s",
ctx->sfrom);
return;
}
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (ifp->index == (unsigned int)pi.ipi6_ifindex)
break;
}
ifp = if_findifpfromcmsg(ctx, &msg, NULL);
if (ifp == NULL) {
logerrx("DHCPv6 reply for unexpected interface from %s",
ctx->sfrom);
logerr(__func__);
return;
}
}
r = (struct dhcp6_message *)ctx->rcvhdr.msg_iov[0].iov_base;
r = (struct dhcp6_message *)buf;
o = dhcp6_findmoption(r, len, D6_OPTION_CLIENTID, &ol);
if (o == NULL || ol != ctx->duid_len ||
memcmp(o, ctx->duid, ol) != 0)
{
logdebugx("%s: incorrect client ID from %s",
ifp->name, ctx->sfrom);
ifp->name, sfrom);
return;
}
if (dhcp6_findmoption(r, len, D6_OPTION_SERVERID, NULL) == NULL) {
logdebugx("%s: no DHCPv6 server ID from %s",
ifp->name, ctx->sfrom);
ifp->name, sfrom);
return;
}
if (r->type == DHCP6_RECONFIGURE) {
logdebugx("%s: RECONFIGURE6 recv from %s,"
" sending to all interfaces",
ifp->name, ctx->sfrom);
ifp->name, sfrom);
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
state = D6_CSTATE(ifp);
if (state != NULL && state->send != NULL)
dhcp6_recvif(ifp, r, len);
dhcp6_recvif(ifp, sfrom, r, len);
}
return;
}
@ -3591,7 +3584,7 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
state->send->xid[0],
state->send->xid[1],
state->send->xid[2],
ctx->sfrom);
sfrom);
return;
}
logdebugx("%s: redirecting DHCP6 message to %s",
@ -3599,7 +3592,7 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
ifp = ifp1;
}
dhcp6_recvif(ifp, r, len);
dhcp6_recvif(ifp, sfrom, r, len);
}
static void
@ -3812,6 +3805,7 @@ dhcp6_start(struct interface *ifp, enum DH6S init_state)
gogogo:
state->state = init_state;
state->lerror = 0;
dhcp_set_leasefile(state->leasefile, sizeof(state->leasefile),
AF_INET6, ifp);
if (ipv6_linklocal(ifp) == NULL) {
@ -3831,18 +3825,20 @@ dhcp6_reboot(struct interface *ifp)
struct dhcp6_state *state;
state = D6_STATE(ifp);
if (state) {
switch (state->state) {
case DH6S_BOUND:
dhcp6_startrebind(ifp);
break;
case DH6S_INFORMED:
dhcp6_startinform(ifp);
break;
default:
dhcp6_startdiscover(ifp);
break;
}
if (state == NULL)
return;
state->lerror = 0;
switch (state->state) {
case DH6S_BOUND:
dhcp6_startrebind(ifp);
break;
case DH6S_INFORMED:
dhcp6_startinform(ifp);
break;
default:
dhcp6_startdiscover(ifp);
break;
}
}

View File

@ -206,7 +206,7 @@ struct dhcp6_state {
/* The +3 is for the possible .pd extension for prefix delegation */
char leasefile[sizeof(LEASEFILE6) + IF_NAMESIZE + (IF_SSIDLEN * 4) +3];
const char *reason;
uint16_t lerror; /* Last error received from DHCPv6 reply. */
struct authstate auth;
};

View File

@ -584,7 +584,7 @@ This has no effect on DHCPv6 other than skipping the reboot phase.
.Nm
will try to do as much as it can by default.
However, there are sometimes situations where you don't want the things to be
configured exactly how the the DHCP server wants.
configured exactly how the DHCP server wants.
Here are some options that deal with turning these bits off.
.Pp
Note that when

View File

@ -54,6 +54,7 @@ const char dhcpcd_copyright[] = "Copyright (c) 2006-2019 Roy Marples";
#include "dev.h"
#include "dhcp-common.h"
#include "dhcpcd.h"
#include "dhcp.h"
#include "dhcp6.h"
#include "duid.h"
#include "eloop.h"
@ -978,7 +979,12 @@ dhcpcd_prestartinterface(void *arg)
if ((!(ifp->ctx->options & DHCPCD_MASTER) ||
ifp->options->options & DHCPCD_IF_UP) &&
if_up(ifp) == -1)
if_up(ifp) == -1
#ifdef __sun
/* Interface could not yet be plumbed. */
&& errno != ENXIO
#endif
)
logerr("%s: %s", __func__, ifp->name);
if (ifp->options->options & DHCPCD_LINK &&
@ -1606,9 +1612,6 @@ main(int argc, char **argv)
ctx.cffile = CONFIG;
ctx.control_fd = ctx.control_unpriv_fd = ctx.link_fd = -1;
ctx.pf_inet_fd = -1;
#ifdef IFLR_ACTIVE
ctx.pf_link_fd = -1;
#endif
TAILQ_INIT(&ctx.control_fds);
#ifdef PLUGIN_DEV
@ -2142,7 +2145,6 @@ exit1:
#endif
dev_stop(&ctx);
eloop_free(ctx.eloop);
free(ctx.iov[0].iov_base);
if (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED))
loginfox(PACKAGE " exited");

View File

@ -142,14 +142,10 @@ struct dhcpcd_ctx {
struct rt_head froutes; /* free routes for re-use */
int pf_inet_fd;
#ifdef IFLR_ACTIVE
int pf_link_fd;
#endif
void *priv;
int link_fd;
int seq; /* route message sequence no */
int sseq; /* successful seq no sent */
struct iovec iov[1]; /* generic iovec buffer */
#ifdef USE_SIGNALS
sigset_t sigset;
@ -184,15 +180,6 @@ struct dhcpcd_ctx {
uint8_t *secret;
size_t secret_len;
unsigned char ctlbuf[IP6BUFLEN];
struct sockaddr_in6 from;
struct msghdr sndhdr;
struct iovec sndiov[1];
unsigned char sndbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
struct msghdr rcvhdr;
char ntopbuf[INET6_ADDRSTRLEN];
const char *sfrom;
int nd_fd;
struct ra_head *ra_routers;

View File

@ -90,6 +90,7 @@ duid_machineuuid(char *uuid, size_t uuid_len)
fclose(fp);
r = len == 1 ? -1 : 0;
#else
UNUSED(uuid);
r = -1;
errno = ENOSYS;
#endif

View File

@ -99,14 +99,20 @@
#endif
#ifdef INET6
static void
ifa_scope(struct sockaddr_in6 *, unsigned int);
static void ifa_setscope(struct sockaddr_in6 *, unsigned int);
static unsigned int ifa_getscope(const struct sockaddr_in6 *);
#endif
struct priv {
int pf_inet6_fd;
};
struct rtm
{
struct rt_msghdr hdr;
char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
};
int
if_init(__unused struct interface *iface)
{
@ -418,9 +424,13 @@ if_findsa(struct dhcpcd_ctx *ctx, const struct sockaddr *sa)
case AF_INET6:
{
const struct sockaddr_in6 *sin;
unsigned int scope;
struct ipv6_addr *ia;
sin = (const void *)sa;
scope = ifa_getscope(sin);
if (scope != 0)
return if_findindex(ctx->ifaces, scope);
if ((ia = ipv6_findmaskaddr(ctx, &sin->sin6_addr)))
return ia->iface;
break;
@ -458,11 +468,7 @@ int
if_route(unsigned char cmd, const struct rt *rt)
{
struct dhcpcd_ctx *ctx;
struct rtm
{
struct rt_msghdr hdr;
char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
} rtmsg;
struct rtm rtmsg;
struct rt_msghdr *rtm = &rtmsg.hdr;
char *bp = rtmsg.buffer;
struct sockaddr_dl sdl;
@ -577,7 +583,7 @@ if_route(unsigned char cmd, const struct rt *rt)
if_copysa(&gateway.sa, &rt->rt_gateway);
#ifdef INET6
if (gateway.sa.sa_family == AF_INET6)
ifa_scope(&gateway.sin6, rt->rt_ifp->index);
ifa_setscope(&gateway.sin6, rt->rt_ifp->index);
#endif
ADDSA(&gateway.sa);
}
@ -605,19 +611,27 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
{
const struct sockaddr *rti_info[RTAX_MAX];
if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY)) {
errno = EINVAL;
return -1;
}
#ifdef RTF_CLONED
if (rtm->rtm_flags & RTF_CLONED)
if (rtm->rtm_flags & RTF_CLONED) {
errno = ENOTSUP;
return -1;
}
#endif
#ifdef RTF_LOCAL
if (rtm->rtm_flags & RTF_LOCAL)
if (rtm->rtm_flags & RTF_LOCAL) {
errno = ENOTSUP;
return -1;
}
#endif
#ifdef RTF_BROADCAST
if (rtm->rtm_flags & RTF_BROADCAST)
if (rtm->rtm_flags & RTF_BROADCAST) {
errno = ENOTSUP;
return -1;
}
#endif
get_addrs(rtm->rtm_addrs, rtm + 1, rti_info);
@ -688,7 +702,7 @@ if_initrt(struct dhcpcd_ctx *ctx, int af)
rtm = (void *)p;
if (if_copyrt(ctx, &rt, rtm) == 0) {
rt.rt_dflags |= RTDF_INIT;
rt_recvrt(RTM_ADD, &rt);
rt_recvrt(RTM_ADD, &rt, rtm->rtm_pid);
}
}
free(buf);
@ -751,7 +765,7 @@ if_addrflags(const struct interface *ifp, const struct in_addr *addr,
#ifdef INET6
static void
ifa_scope(struct sockaddr_in6 *sin, unsigned int ifindex)
ifa_setscope(struct sockaddr_in6 *sin, unsigned int ifindex)
{
#ifdef __KAME__
@ -771,6 +785,23 @@ ifa_scope(struct sockaddr_in6 *sin, unsigned int ifindex)
#endif
}
static unsigned int
ifa_getscope(const struct sockaddr_in6 *sin)
{
#ifdef __KAME__
uint16_t scope;
#endif
if (!IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))
return 0;
#ifdef __KAME__
memcpy(&scope, &sin->sin6_addr.s6_addr[2], sizeof(scope));
return (unsigned int)ntohs(scope);
#else
return (unsigned int)sin->sin6_scope_id;
#endif
}
int
if_address6(unsigned char cmd, const struct ipv6_addr *ia)
{
@ -810,7 +841,7 @@ if_address6(unsigned char cmd, const struct ipv6_addr *ia)
}
ADDADDR(&ifa.ifra_addr, &ia->addr);
ifa_scope(&ifa.ifra_addr, ia->iface->index);
ifa_setscope(&ifa.ifra_addr, ia->iface->index);
ipv6_mask(&mask, ia->prefix_len);
ADDADDR(&ifa.ifra_prefixmask, &mask);
@ -887,7 +918,7 @@ if_addrflags6(const struct interface *ifp, const struct in6_addr *addr,
strlcpy(ifr6.ifr_name, ifp->name, sizeof(ifr6.ifr_name));
ifr6.ifr_addr.sin6_family = AF_INET6;
ifr6.ifr_addr.sin6_addr = *addr;
ifa_scope(&ifr6.ifr_addr, ifp->index);
ifa_setscope(&ifr6.ifr_addr, ifp->index);
priv = (struct priv *)ifp->ctx->priv;
if (ioctl(priv->pf_inet6_fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
flags = ifr6.ifr_ifru.ifru_flags6;
@ -908,7 +939,7 @@ if_getlifetime6(struct ipv6_addr *ia)
strlcpy(ifr6.ifr_name, ia->iface->name, sizeof(ifr6.ifr_name));
ifr6.ifr_addr.sin6_family = AF_INET6;
ifr6.ifr_addr.sin6_addr = ia->addr;
ifa_scope(&ifr6.ifr_addr, ia->iface->index);
ifa_setscope(&ifr6.ifr_addr, ia->iface->index);
priv = (struct priv *)ia->iface->ctx->priv;
if (ioctl(priv->pf_inet6_fd, SIOCGIFALIFETIME_IN6, &ifr6) == -1)
return -1;
@ -1010,7 +1041,7 @@ if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
}
#endif
rt_recvrt(rtm->rtm_type, &rt);
rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
}
static void
@ -1095,7 +1126,7 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
ifra.ifra_addr.sin_len = sizeof(ifra.ifra_addr);
ifra.ifra_addr.sin_addr = addr;
if (ioctl(ctx->pf_inet_fd, SIOCGIFALIAS, &ifra) == -1) {
if (errno != EADDRNOTAVAIL)
if (errno != ENXIO && errno != EADDRNOTAVAIL)
logerr("%s: SIOCGIFALIAS", __func__);
if (ifam->ifam_type != RTM_DELADDR)
break;
@ -1229,18 +1260,16 @@ if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
int
if_handlelink(struct dhcpcd_ctx *ctx)
{
struct msghdr msg;
struct rtm rtm;
struct iovec iov = { .iov_base = &rtm, .iov_len = sizeof(rtm) };
struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
ssize_t len;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = ctx->iov;
msg.msg_iovlen = 1;
len = recvmsg_realloc(ctx->link_fd, &msg, 0);
len = recvmsg(ctx->link_fd, &msg, 0);
if (len == -1)
return -1;
if (len != 0)
if_dispatch(ctx, ctx->iov[0].iov_base);
if_dispatch(ctx, &rtm.hdr);
return 0;
}

View File

@ -207,11 +207,12 @@ const struct option cf_options[] = {
{NULL, 0, NULL, '\0'}
};
static const char *default_script = SCRIPT;
static char *
add_environ(struct if_options *ifo, const char *value, int uniq)
add_environ(char ***array, const char *value, int uniq)
{
char **newlist;
char **lst = ifo->environ;
char **newlist, **list = *array;
size_t i = 0, l, lv;
char *match = NULL, *p, *n;
@ -229,8 +230,8 @@ add_environ(struct if_options *ifo, const char *value, int uniq)
*p++ = '\0';
l = strlen(match);
while (lst && lst[i]) {
if (match && strncmp(lst[i], match, l) == 0) {
while (list && list[i]) {
if (match && strncmp(list[i], match, l) == 0) {
if (uniq) {
n = strdup(value);
if (n == NULL) {
@ -238,25 +239,25 @@ add_environ(struct if_options *ifo, const char *value, int uniq)
free(match);
return NULL;
}
free(lst[i]);
lst[i] = n;
free(list[i]);
list[i] = n;
} else {
/* Append a space and the value to it */
l = strlen(lst[i]);
l = strlen(list[i]);
lv = strlen(p);
n = realloc(lst[i], l + lv + 2);
n = realloc(list[i], l + lv + 2);
if (n == NULL) {
logerr(__func__);
free(match);
return NULL;
}
lst[i] = n;
lst[i][l] = ' ';
memcpy(lst[i] + l + 1, p, lv);
lst[i][l + lv + 1] = '\0';
list[i] = n;
list[i][l] = ' ';
memcpy(list[i] + l + 1, p, lv);
list[i][l + lv + 1] = '\0';
}
free(match);
return lst[i];
return list[i];
}
i++;
}
@ -267,7 +268,7 @@ add_environ(struct if_options *ifo, const char *value, int uniq)
logerr(__func__);
return NULL;
}
newlist = reallocarray(lst, i + 2, sizeof(char *));
newlist = reallocarray(list, i + 2, sizeof(char *));
if (newlist == NULL) {
logerr(__func__);
free(n);
@ -275,26 +276,31 @@ add_environ(struct if_options *ifo, const char *value, int uniq)
}
newlist[i] = n;
newlist[i + 1] = NULL;
ifo->environ = newlist;
*array = newlist;
return newlist[i];
}
#define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0)
#define PARSE_STRING 0
#define PARSE_STRING_NULL 1
#define PARSE_HWADDR 2
#define parse_string(a, b, c) parse_str((a), (b), (c), PARSE_STRING)
#define parse_hwaddr(a, b, c) parse_str((a), (b), (c), PARSE_HWADDR)
static ssize_t
parse_string_hwaddr(char *sbuf, size_t slen, const char *str, int clid)
parse_str(char *sbuf, size_t slen, const char *str, int flags)
{
size_t l;
const char *p;
int i, punt_last = 0;
const char *p, *end;
int i;
char c[4], cmd;
end = str + strlen(str);
/* If surrounded by quotes then it's a string */
if (*str == '"') {
str++;
l = strlen(str);
p = str + l - 1;
if (*p == '"')
punt_last = 1;
p = end - 1;
if (*p == '"') {
str++;
end = p;
}
} else {
l = (size_t)hwaddr_aton(NULL, str);
if ((ssize_t) l != -1 && l > 1) {
@ -311,13 +317,13 @@ parse_string_hwaddr(char *sbuf, size_t slen, const char *str, int clid)
l = 0;
/* If processing a string on the clientid, first byte should be
* 0 to indicate a non hardware type */
if (clid && *str) {
if (flags == PARSE_HWADDR && *str) {
if (sbuf)
*sbuf++ = 0;
l++;
}
c[3] = '\0';
while (*str) {
while (str < end) {
if (++l > slen && sbuf) {
errno = ENOBUFS;
return -1;
@ -385,11 +391,8 @@ parse_string_hwaddr(char *sbuf, size_t slen, const char *str, int clid)
str++;
}
}
if (punt_last) {
if (sbuf)
*--sbuf = '\0';
l--;
}
if (flags == PARSE_STRING_NULL && sbuf)
*sbuf = '\0';
return (ssize_t)l;
}
@ -654,7 +657,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
int e, i, t;
long l;
unsigned long u;
char *p = NULL, *bp, *fp, *np, **nconf;
char *p = NULL, *bp, *fp, *np;
ssize_t s;
struct in_addr addr, addr2;
in_addr_t *naddr;
@ -708,17 +711,33 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
break;
case 'c':
ARG_REQUIRED;
free(ifo->script);
ifo->script = strdup(arg);
if (ifo->script == NULL)
if (ifo->script != default_script)
free(ifo->script);
s = parse_str(NULL, 0, arg, PARSE_STRING_NULL);
if (s == 0) {
ifo->script = NULL;
break;
}
dl = (size_t)s;
if (s == -1 || (ifo->script = malloc(dl)) == NULL) {
ifo->script = NULL;
logerr(__func__);
return -1;
}
parse_str(ifo->script, dl, arg, PARSE_STRING_NULL);
if (ifo->script[0] == '\0' ||
strcmp(ifo->script, "/dev/null") == 0)
{
free(ifo->script);
ifo->script = NULL;
}
break;
case 'd':
ifo->options |= DHCPCD_DEBUG;
break;
case 'e':
ARG_REQUIRED;
add_environ(ifo, arg, 1);
add_environ(&ifo->environ, arg, 1);
break;
case 'h':
if (!arg) {
@ -969,7 +988,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
return -1;
}
snprintf(p, dl, "skip_hooks=%s", arg);
add_environ(ifo, p, 0);
add_environ(&ifo->environ, p, 0);
free(p);
break;
case 'D':
@ -1006,8 +1025,8 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
/* Strings have a type of 0 */;
ifo->clientid[1] = 0;
if (arg)
s = parse_string_hwaddr((char *)ifo->clientid + 1,
CLIENTID_MAX_LEN, arg, 1);
s = parse_hwaddr((char *)ifo->clientid + 1,
CLIENTID_MAX_LEN, arg);
else
s = 0;
if (s == -1) {
@ -1112,6 +1131,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
sa_in_init(&rt->rt_gateway, &addr3);
TAILQ_INSERT_TAIL(&ifo->routes, rt, rt_next);
*fp = ' ';
add_environ(&ifo->config, arg, 0);
} else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
if (parse_addr(&addr, NULL, p) == -1)
return -1;
@ -1122,6 +1142,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
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);
} else if (strncmp(arg, "interface_mtu=",
strlen("interface_mtu=")) == 0 ||
strncmp(arg, "mtu=", strlen("mtu=")) == 0)
@ -1149,40 +1170,8 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
} else
ifo->req_prefix_len = 128;
}
} else {
dl = 0;
if (ifo->config != NULL) {
while (ifo->config[dl] != NULL) {
if (strncmp(ifo->config[dl], arg,
(size_t)(p - arg)) == 0)
{
p = strdup(arg);
if (p == NULL) {
logerr(__func__);
return -1;
}
free(ifo->config[dl]);
ifo->config[dl] = p;
return 1;
}
dl++;
}
}
p = strdup(arg);
if (p == NULL) {
logerr(__func__);
return -1;
}
nconf = reallocarray(ifo->config, dl+2, sizeof(char *));
if (nconf == NULL) {
logerr(__func__);
free(p);
return -1;
}
ifo->config = nconf;
ifo->config[dl] = p;
ifo->config[dl + 1] = NULL;
}
} else
add_environ(&ifo->config, arg, 1);
break;
case 'W':
if (parse_addr(&addr, &addr2, arg) != 0)
@ -2277,6 +2266,7 @@ default_config(struct dhcpcd_ctx *ctx)
ifo->options |= DHCPCD_IF_UP | DHCPCD_LINK | DHCPCD_INITIAL_DELAY;
ifo->timeout = DEFAULT_TIMEOUT;
ifo->reboot = DEFAULT_REBOOT;
ifo->script = UNCONST(default_script);
ifo->metric = -1;
ifo->auth.options |= DHCPCD_AUTH_REQUIRE;
TAILQ_INIT(&ifo->routes);
@ -2626,7 +2616,8 @@ free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo)
free(ifo->config);
}
rt_headclear0(ctx, &ifo->routes, AF_UNSPEC);
free(ifo->script);
if (ifo->script != default_script)
free(ifo->script);
free(ifo->arping);
free(ifo->blacklist);
free(ifo->fallback);

View File

@ -109,12 +109,6 @@ if_opensockets(struct dhcpcd_ctx *ctx)
if (ctx->pf_inet_fd == -1)
return -1;
#ifdef IFLR_ACTIVE
ctx->pf_link_fd = xsocket(PF_LINK, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (ctx->pf_link_fd == -1)
return -1;
#endif
return 0;
}
@ -124,10 +118,6 @@ if_closesockets(struct dhcpcd_ctx *ctx)
if (ctx->pf_inet_fd != -1)
close(ctx->pf_inet_fd);
#ifdef IFLR_ACTIVE
if (ctx->pf_link_fd != -1)
close(ctx->pf_link_fd);
#endif
if (ctx->priv) {
if_closesockets_os(ctx);
@ -146,9 +136,15 @@ if_carrier(struct interface *ifp)
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == -1)
r = ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr);
if (r != -1)
ifp->flags = (unsigned int)ifr.ifr_flags;
#ifdef __sun
return if_carrier_os(ifp);
#else
if (r == -1)
return LINK_UNKNOWN;
ifp->flags = (unsigned int)ifr.ifr_flags;
#ifdef SIOCGIFMEDIA
memset(&ifmr, 0, sizeof(ifmr));
@ -165,6 +161,7 @@ if_carrier(struct interface *ifp)
#else
r = ifr.ifr_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN;
#endif
#endif /* __sun */
return r;
}
@ -342,12 +339,10 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs,
struct ifreq ifr;
#endif
#ifdef IFLR_ACTIVE
struct if_laddrreq iflr;
struct if_laddrreq iflr = { .flags = IFLR_PREFIX };
int link_fd;
#endif
#ifdef IFLR_ACTIVE
memset(&iflr, 0, sizeof(iflr));
#endif
#elif AF_PACKET
const struct sockaddr_ll *sll;
#endif
@ -356,11 +351,21 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs,
logerr(__func__);
return NULL;
}
TAILQ_INIT(ifs);
if (getifaddrs(ifaddrs) == -1) {
logerr(__func__);
goto out;
free(ifs);
return NULL;
}
TAILQ_INIT(ifs);
#ifdef IFLR_ACTIVE
link_fd = xsocket(PF_LINK, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (link_fd == -1) {
logerr(__func__);
free(ifs);
return NULL;
}
#endif
for (ifa = *ifaddrs; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr != NULL) {
@ -457,7 +462,7 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs,
MIN(ifa->ifa_addr->sa_len, sizeof(iflr.addr)));
iflr.flags = IFLR_PREFIX;
iflr.prefixlen = (unsigned int)sdl->sdl_alen * NBBY;
if (ioctl(ctx->pf_link_fd, SIOCGLIFADDR, &iflr) == -1 ||
if (ioctl(link_fd, SIOCGLIFADDR, &iflr) == -1 ||
!(iflr.flags & IFLR_ACTIVE))
{
if_free(ifp);
@ -610,7 +615,9 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs,
TAILQ_INSERT_TAIL(ifs, ifp, next);
}
out:
#ifdef IFLR_ACTIVE
close(link_fd);
#endif
return ifs;
}
@ -711,6 +718,11 @@ if_domtu(const struct interface *ifp, short int mtu)
int r;
struct ifreq ifr;
#ifdef __sun
if (mtu == 0)
return if_mtu_os(ifp);
#endif
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
ifr.ifr_mtu = mtu;
@ -772,6 +784,17 @@ if_cmp(const struct interface *si, const struct interface *ti)
return 0;
}
#ifdef ALIAS_ADDR
int
if_makealias(char *alias, size_t alias_len, const char *ifname, int lun)
{
if (lun == 0)
return strlcpy(alias, ifname, alias_len);
return snprintf(alias, alias_len, "%s:%u", ifname, lun);
}
#endif
/* Sort the interfaces into a preferred order - best first, worst last. */
void
if_sortinterfaces(struct dhcpcd_ctx *ctx)
@ -801,6 +824,68 @@ if_sortinterfaces(struct dhcpcd_ctx *ctx)
TAILQ_CONCAT(ctx->ifaces, &sorted, next);
}
struct interface *
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)
struct in_pktinfo ipi;
#endif
#ifdef INET6
struct in6_pktinfo ipi6;
#else
UNUSED(hoplimit);
#endif
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(msg);
cm;
cm = (struct cmsghdr *)CMSG_NXTHDR(msg, cm))
{
#if defined(INET) && defined(IP_PKTINFO)
if (cm->cmsg_level == IPPROTO_IP) {
switch(cm->cmsg_type) {
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
#ifdef INET6
if (cm->cmsg_level == IPPROTO_IPV6) {
switch(cm->cmsg_type) {
case IPV6_PKTINFO:
if (cm->cmsg_len != CMSG_LEN(sizeof(ipi6)))
continue;
memcpy(&ipi6, CMSG_DATA(cm), sizeof(ipi6));
ifindex = (unsigned int)ipi6.ipi6_ifindex;
break;
case IPV6_HOPLIMIT:
if (cm->cmsg_len != CMSG_LEN(sizeof(int)))
continue;
if (hoplimit == NULL)
break;
memcpy(hoplimit, CMSG_DATA(cm), sizeof(int));
break;
}
}
#endif
}
/* Find the receiving interface */
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (ifp->index == ifindex)
break;
}
if (ifp == NULL)
errno = ESRCH;
return ifp;
}
int
xsocket(int domain, int type, int protocol)
{

View File

@ -129,6 +129,13 @@ int if_domtu(const struct interface *, short int);
#define if_setmtu(ifp, mtu) if_domtu((ifp), (mtu))
int if_carrier(struct interface *);
#ifdef ALIAS_ADDR
int if_makealias(char *, size_t, const char *, int);
#endif
int if_carrier_os(struct interface *);
int if_mtu_os(const struct interface *);
/*
* Helper to decode an interface name of bge0:1 to
* devname = bge0, drvname = bge0, ppa = 0, lun = 1.
@ -209,5 +216,7 @@ int if_getlifetime6(struct ipv6_addr *);
#endif
int if_machinearch(char *, size_t);
struct interface *if_findifpfromcmsg(struct dhcpcd_ctx *,
struct msghdr *, int *);
int xsocket(int, int, int);
#endif

View File

@ -563,10 +563,12 @@ ipv4_aliasaddr(struct ipv4_addr *ia, struct ipv4_addr **repl)
lun = 0;
state = IPV4_STATE(ia->iface);
find_lun:
if (lun == 0)
strlcpy(alias, ia->iface->name, sizeof(alias));
else
snprintf(alias, sizeof(alias), "%s:%u", ia->iface->name, lun);
if (if_makealias(alias, IF_NAMESIZE, ia->iface->name, lun) >=
IF_NAMESIZE)
{
errno = ENOMEM;
return -1;
}
TAILQ_FOREACH(iap, &state->addrs, next) {
if (iap->alias[0] != '\0' && iap->addr.s_addr == INADDR_ANY) {
/* No address assigned? Lets use it. */

View File

@ -305,6 +305,8 @@ ipv4ll_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
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");
}

View File

@ -129,29 +129,14 @@ int
ipv6_init(struct dhcpcd_ctx *ctx)
{
if (ctx->sndhdr.msg_iovlen == 1)
if (ctx->ra_routers != NULL)
return 0;
if (ctx->ra_routers == NULL) {
ctx->ra_routers = malloc(sizeof(*ctx->ra_routers));
if (ctx->ra_routers == NULL)
return -1;
}
ctx->ra_routers = malloc(sizeof(*ctx->ra_routers));
if (ctx->ra_routers == NULL)
return -1;
TAILQ_INIT(ctx->ra_routers);
ctx->sndhdr.msg_namelen = sizeof(struct sockaddr_in6);
ctx->sndhdr.msg_iov = ctx->sndiov;
ctx->sndhdr.msg_iovlen = 1;
ctx->sndhdr.msg_control = ctx->sndbuf;
ctx->sndhdr.msg_controllen = sizeof(ctx->sndbuf);
ctx->rcvhdr.msg_name = &ctx->from;
ctx->rcvhdr.msg_namelen = sizeof(ctx->from);
ctx->rcvhdr.msg_iov = ctx->iov;
ctx->rcvhdr.msg_iovlen = 1;
ctx->rcvhdr.msg_control = ctx->ctlbuf;
// controllen is set at recieve
//ctx->rcvhdr.msg_controllen = sizeof(ctx->rcvbuf);
ctx->nd_fd = -1;
ctx->dhcp6_fd = -1;
return 0;
@ -639,6 +624,10 @@ ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now)
uint32_t pltime, vltime;
bool vltime_was_zero;
__printflike(1, 2) void (*logfunc)(const char *, ...);
#ifdef __sun
struct ipv6_state *state;
struct ipv6_addr *ia2;
#endif
/* Remember the interface of the address. */
ifp = ia->iface;
@ -774,13 +763,13 @@ ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now)
}
#ifdef ALIAS_ADDR
/* Find the next logical aliase address we can use. */
/* Find the next logical alias address we can use. */
static int
ipv6_aliasaddr(struct ipv6_addr *ia, struct ipv6_addr **repl)
{
struct ipv6_state *state;
struct ipv6_addr *iap;
unsigned int unit;
unsigned int lun;
char alias[IF_NAMESIZE];
if (ia->alias[0] != '\0')
@ -799,12 +788,14 @@ ipv6_aliasaddr(struct ipv6_addr *ia, struct ipv6_addr **repl)
}
}
unit = 0;
lun = 0;
find_unit:
if (unit == 0)
strlcpy(alias, ia->iface->name, sizeof(alias));
else
snprintf(alias, sizeof(alias), "%s:%u", ia->iface->name, unit);
if (if_makealias(alias, IF_NAMESIZE, ia->iface->name, lun) >=
IF_NAMESIZE)
{
errno = ENOMEM;
return -1;
}
TAILQ_FOREACH(iap, &state->addrs, next) {
if (iap->alias[0] == '\0')
continue;
@ -820,11 +811,11 @@ find_unit:
}
if (iap != NULL) {
if (unit == UINT_MAX) {
if (lun == UINT_MAX) {
errno = ERANGE;
return -1;
}
unit++;
lun++;
goto find_unit;
}

View File

@ -274,10 +274,17 @@ ipv6nd_sendrsprobe(void *arg)
{
struct interface *ifp = arg;
struct dhcpcd_ctx *ctx;
struct rs_state *state;
struct sockaddr_in6 dst;
struct rs_state *state = RS_STATE(ifp);
struct sockaddr_in6 dst = { .sin6_family = AF_INET6 };
struct iovec iov = { .iov_base = state->rs, .iov_len = state->rslen };
unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 };
struct msghdr msg = {
.msg_name = &dst, .msg_namelen = sizeof(dst),
.msg_iov = &iov, .msg_iovlen = 1,
.msg_control = ctl, .msg_controllen = sizeof(ctl),
};
struct cmsghdr *cm;
struct in6_pktinfo pi;
struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
if (ipv6_linklocal(ifp) == NULL) {
logdebugx("%s: delaying Router Solicitation for LL address",
@ -286,8 +293,6 @@ ipv6nd_sendrsprobe(void *arg)
return;
}
memset(&dst, 0, sizeof(dst));
dst.sin6_family = AF_INET6;
#ifdef HAVE_SA_LEN
dst.sin6_len = sizeof(dst);
#endif
@ -297,25 +302,19 @@ ipv6nd_sendrsprobe(void *arg)
return;
}
state = RS_STATE(ifp);
ctx = ifp->ctx;
ctx->sndhdr.msg_name = (void *)&dst;
ctx->sndhdr.msg_iov[0].iov_base = state->rs;
ctx->sndhdr.msg_iov[0].iov_len = state->rslen;
/* Set the outbound interface */
cm = CMSG_FIRSTHDR(&ctx->sndhdr);
cm = CMSG_FIRSTHDR(&msg);
if (cm == NULL) /* unlikely */
return;
cm->cmsg_level = IPPROTO_IPV6;
cm->cmsg_type = IPV6_PKTINFO;
cm->cmsg_len = CMSG_LEN(sizeof(pi));
memset(&pi, 0, sizeof(pi));
pi.ipi6_ifindex = ifp->index;
memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
logdebugx("%s: sending Router Solicitation", ifp->name);
if (sendmsg(ctx->nd_fd, &ctx->sndhdr, 0) == -1) {
if (sendmsg(ctx->nd_fd, &msg, 0) == -1) {
logerr(__func__);
/* Allow IPv6ND to continue .... at most a few errors
* would be logged.
@ -341,41 +340,42 @@ ipv6nd_sendadvertisement(void *arg)
struct ipv6_addr *ia = arg;
struct interface *ifp = ia->iface;
struct dhcpcd_ctx *ctx = ifp->ctx;
struct sockaddr_in6 dst;
struct sockaddr_in6 dst = {
.sin6_family = AF_INET6,
.sin6_scope_id = ifp->index,
};
struct iovec iov = { .iov_base = ia->na, .iov_len = ia->na_len };
unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 };
struct msghdr msg = {
.msg_name = &dst, .msg_namelen = sizeof(dst),
.msg_iov = &iov, .msg_iovlen = 1,
.msg_control = ctl, .msg_controllen = sizeof(ctl),
};
struct cmsghdr *cm;
struct in6_pktinfo pi;
struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
const struct rs_state *state = RS_CSTATE(ifp);
if (state == NULL || ifp->carrier <= LINK_DOWN)
goto freeit;
memset(&dst, 0, sizeof(dst));
dst.sin6_family = AF_INET6;
#ifdef SIN6_LEN
dst.sin6_len = sizeof(dst);
#endif
dst.sin6_scope_id = ifp->index;
if (inet_pton(AF_INET6, ALLNODES, &dst.sin6_addr) != 1) {
logerr(__func__);
return;
}
ctx->sndhdr.msg_name = (void *)&dst;
ctx->sndhdr.msg_iov[0].iov_base = ia->na;
ctx->sndhdr.msg_iov[0].iov_len = ia->na_len;
/* Set the outbound interface. */
cm = CMSG_FIRSTHDR(&ctx->sndhdr);
cm = CMSG_FIRSTHDR(&msg);
assert(cm != NULL);
cm->cmsg_level = IPPROTO_IPV6;
cm->cmsg_type = IPV6_PKTINFO;
cm->cmsg_len = CMSG_LEN(sizeof(pi));
memset(&pi, 0, sizeof(pi));
pi.ipi6_ifindex = ifp->index;
memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
logdebugx("%s: sending NA for %s", ifp->name, ia->saddr);
if (sendmsg(ctx->nd_fd, &ctx->sndhdr, 0) == -1)
if (sendmsg(ctx->nd_fd, &msg, 0) == -1)
logerr(__func__);
if (++ia->na_count < MAX_NEIGHBOR_ADVERTISEMENT) {
@ -872,8 +872,9 @@ dhcp6_start(__unused struct interface *ifp, __unused enum DH6S init_state)
#endif
static void
ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
struct icmp6_hdr *icp, size_t len, int hoplimit)
ipv6nd_handlera(struct dhcpcd_ctx *ctx,
const struct sockaddr_in6 *from, const char *sfrom,
struct interface *ifp, struct icmp6_hdr *icp, size_t len, int hoplimit)
{
size_t i, olen;
struct nd_router_advert *nd_ra;
@ -895,33 +896,29 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
if (ifp == NULL) {
#ifdef DEBUG_RS
logdebugx("RA for unexpected interface from %s",
ctx->sfrom);
logdebugx("RA for unexpected interface from %s", sfrom);
#endif
return;
}
if (len < sizeof(struct nd_router_advert)) {
logerrx("IPv6 RA packet too short from %s", ctx->sfrom);
logerrx("IPv6 RA packet too short from %s", sfrom);
return;
}
/* RFC 4861 7.1.2 */
if (hoplimit != 255) {
logerrx("invalid hoplimit(%d) in RA from %s",
hoplimit, ctx->sfrom);
logerrx("invalid hoplimit(%d) in RA from %s", hoplimit, sfrom);
return;
}
if (!IN6_IS_ADDR_LINKLOCAL(&ctx->from.sin6_addr)) {
logerrx("RA from non local address %s", ctx->sfrom);
if (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) {
logerrx("RA from non local address %s", sfrom);
return;
}
if (!(ifp->options->options & DHCPCD_IPV6RS)) {
#ifdef DEBUG_RS
logerrx("%s: unexpected RA from %s",
ifp->name, ctx->sfrom);
logerrx("%s: unexpected RA from %s", ifp->name, sfrom);
#endif
return;
}
@ -930,20 +927,20 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
if (ipv6_linklocal(ifp) == NULL) {
#ifdef DEBUG_RS
logdebugx("%s: received RA from %s (no link-local)",
ifp->name, ctx->sfrom);
ifp->name, sfrom);
#endif
return;
}
if (ipv6_iffindaddr(ifp, &ctx->from.sin6_addr, IN6_IFF_TENTATIVE)) {
if (ipv6_iffindaddr(ifp, &from->sin6_addr, IN6_IFF_TENTATIVE)) {
logdebugx("%s: ignoring RA from ourself %s",
ifp->name, ctx->sfrom);
ifp->name, sfrom);
return;
}
TAILQ_FOREACH(rap, ctx->ra_routers, next) {
if (ifp == rap->iface &&
IN6_ARE_ADDR_EQUAL(&rap->from, &ctx->from.sin6_addr))
IN6_ARE_ADDR_EQUAL(&rap->from, &from->sin6_addr))
break;
}
@ -968,8 +965,8 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
return;
}
rap->iface = ifp;
rap->from = ctx->from.sin6_addr;
strlcpy(rap->sfrom, ctx->sfrom, sizeof(rap->sfrom));
rap->from = from->sin6_addr;
strlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom));
TAILQ_INIT(&rap->addrs);
new_rap = true;
} else
@ -991,8 +988,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
* in accordance with the own prefix times which would result in too
* much needless log spam. */
logfunc = new_rap ? loginfox : logdebugx,
logfunc("%s: Router Advertisement from %s",
ifp->name, ctx->sfrom);
logfunc("%s: Router Advertisement from %s", ifp->name, rap->sfrom);
clock_gettime(CLOCK_MONOTONIC, &rap->acquired);
rap->flags = nd_ra->nd_ra_flags_reserved;
@ -1053,10 +1049,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
}
if (dho != NULL)
logwarnx("%s: reject RA (option %s) from %s",
ifp->name, dho->var, ctx->sfrom);
ifp->name, dho->var, rap->sfrom);
else
logwarnx("%s: reject RA (option %d) from %s",
ifp->name, ndo.nd_opt_type, ctx->sfrom);
ifp->name, ndo.nd_opt_type, rap->sfrom);
if (new_rap)
ipv6nd_removefreedrop_ra(rap, 0, 0);
else
@ -1196,7 +1192,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
dho->option))
{
logwarnx("%s: reject RA (no option %s) from %s",
ifp->name, dho->var, ctx->sfrom);
ifp->name, dho->var, rap->sfrom);
if (new_rap)
ipv6nd_removefreedrop_ra(rap, 0, 0);
else
@ -1584,8 +1580,8 @@ ipv6nd_drop(struct interface *ifp)
}
static void
ipv6nd_handlena(struct dhcpcd_ctx *ctx, struct interface *ifp,
struct icmp6_hdr *icp, size_t len, int hoplimit)
ipv6nd_handlena(struct dhcpcd_ctx *ctx, const char *sfrom,
struct interface *ifp, struct icmp6_hdr *icp, size_t len, int hoplimit)
{
struct nd_neighbor_advert *nd_na;
struct in6_addr nd_na_target;
@ -1596,22 +1592,20 @@ ipv6nd_handlena(struct dhcpcd_ctx *ctx, struct interface *ifp,
if (ifp == NULL) {
#ifdef DEBUG_NS
logdebugx("NA for unexpected interface from %s",
ctx->sfrom);
logdebugx("NA for unexpected interface from %s", sfrom);
#endif
return;
}
if ((size_t)len < sizeof(struct nd_neighbor_advert)) {
logerrx("%s: IPv6 NA too short from %s",
ifp->name, ctx->sfrom);
logerrx("%s: IPv6 NA too short from %s", ifp->name, sfrom);
return;
}
/* RFC 4861 7.1.2 */
if (hoplimit != 255) {
logerrx("invalid hoplimit(%d) in NA from %s",
hoplimit, ctx->sfrom);
hoplimit, sfrom);
return;
}
@ -1625,7 +1619,7 @@ ipv6nd_handlena(struct dhcpcd_ctx *ctx, struct interface *ifp,
memcpy(&nd_na_target, &nd_na->nd_na_target, sizeof(nd_na_target));
if (IN6_IS_ADDR_MULTICAST(&nd_na_target)) {
logerrx("%s: NA multicast address %s (%s)",
ifp->name, taddr, ctx->sfrom);
ifp->name, taddr, sfrom);
return;
}
@ -1637,20 +1631,20 @@ ipv6nd_handlena(struct dhcpcd_ctx *ctx, struct interface *ifp,
if (rap == NULL) {
#ifdef DEBUG_NS
logdebugx("%s: unexpected NA from %s for %s",
ifp->name, ctx->sfrom, taddr);
ifp->name, sfrom, taddr);
#endif
return;
}
#ifdef DEBUG_NS
logdebugx("%s: %sNA for %s from %s",
ifp->name, is_solicited ? "solicited " : "", taddr, ctx->sfrom);
ifp->name, is_solicited ? "solicited " : "", taddr, sfrom);
#endif
/* Node is no longer a router, so remove it from consideration */
if (!is_router && !rap->expired) {
loginfox("%s: %s not a router (%s)",
ifp->name, taddr, ctx->sfrom);
ifp->name, taddr, sfrom);
rap->expired = 1;
rt_build(ifp->ctx, AF_INET6);
script_runreason(ifp, "ROUTERADVERT");
@ -1665,81 +1659,63 @@ static void
ipv6nd_handledata(void *arg)
{
struct dhcpcd_ctx *ctx;
struct sockaddr_in6 from;
unsigned char buf[64 * 1024]; /* Maximum ICMPv6 size */
struct iovec iov = {
.iov_base = buf,
.iov_len = sizeof(buf),
};
unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int))] = { 0 };
struct msghdr msg = {
.msg_name = &from, .msg_namelen = sizeof(from),
.msg_iov = &iov, .msg_iovlen = 1,
.msg_control = ctl, .msg_controllen = sizeof(ctl),
};
ssize_t len;
struct cmsghdr *cm;
int hoplimit;
struct in6_pktinfo pkt;
char sfrom[INET6_ADDRSTRLEN];
int hoplimit = 0;
struct icmp6_hdr *icp;
struct interface *ifp;
ctx = arg;
ctx->rcvhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
CMSG_SPACE(sizeof(int));
len = recvmsg_realloc(ctx->nd_fd, &ctx->rcvhdr, 0);
len = recvmsg(ctx->nd_fd, &msg, 0);
if (len == -1) {
logerr(__func__);
return;
}
ctx->sfrom = inet_ntop(AF_INET6, &ctx->from.sin6_addr,
ctx->ntopbuf, INET6_ADDRSTRLEN);
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", ctx->sfrom);
logerrx("IPv6 ICMP packet too short from %s", sfrom);
return;
}
pkt.ipi6_ifindex = 0;
hoplimit = 0;
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&ctx->rcvhdr);
cm;
cm = (struct cmsghdr *)CMSG_NXTHDR(&ctx->rcvhdr, cm))
{
if (cm->cmsg_level != IPPROTO_IPV6)
continue;
switch(cm->cmsg_type) {
case IPV6_PKTINFO:
if (cm->cmsg_len == CMSG_LEN(sizeof(pkt)))
memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt));
break;
case IPV6_HOPLIMIT:
if (cm->cmsg_len == CMSG_LEN(sizeof(int)))
memcpy(&hoplimit, CMSG_DATA(cm), sizeof(int));
break;
}
}
if (pkt.ipi6_ifindex == 0) {
logerrx("IPv6 RA/NA did not contain index from %s", ctx->sfrom);
ifp = if_findifpfromcmsg(ctx, &msg, &hoplimit);
if (ifp == NULL) {
logerr(__func__);
return;
}
/* Find the receiving interface */
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (ifp->index == (unsigned int)pkt.ipi6_ifindex)
break;
}
/* Don't do anything if the user hasn't configured it. */
if (ifp != NULL &&
(ifp->active != IF_ACTIVE_USER ||
!(ifp->options->options & DHCPCD_IPV6)))
if (ifp->active != IF_ACTIVE_USER ||
!(ifp->options->options & DHCPCD_IPV6))
return;
icp = (struct icmp6_hdr *)ctx->rcvhdr.msg_iov[0].iov_base;
icp = (struct icmp6_hdr *)buf;
if (icp->icmp6_code == 0) {
switch(icp->icmp6_type) {
case ND_NEIGHBOR_ADVERT:
ipv6nd_handlena(ctx, ifp, icp, (size_t)len,
hoplimit);
ipv6nd_handlena(ctx, sfrom,
ifp, icp, (size_t)len, hoplimit);
return;
case ND_ROUTER_ADVERT:
ipv6nd_handlera(ctx, ifp, icp, (size_t)len,
hoplimit);
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, ctx->sfrom);
icp->icmp6_type, icp->icmp6_code, sfrom);
}
static void

View File

@ -272,7 +272,7 @@ rt_kfree(struct rt *rt)
/* If something other than dhcpcd removes a route,
* we need to remove it from our internal table. */
void
rt_recvrt(int cmd, const struct rt *rt)
rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
{
struct dhcpcd_ctx *ctx;
struct rt *f;
@ -288,8 +288,11 @@ rt_recvrt(int cmd, const struct rt *rt)
rt_free(f);
}
if ((f = rt_find(&ctx->routes, rt)) != NULL) {
char buf[32];
TAILQ_REMOVE(&ctx->routes, f, rt_next);
rt_desc("deleted", f);
snprintf(buf, sizeof(buf), "pid %d deleted", pid);
rt_desc(buf, f);
rt_free(f);
}
break;
@ -303,7 +306,7 @@ rt_recvrt(int cmd, const struct rt *rt)
break;
}
#if defined(INET) && defined(HAVE_ROUTE_METRIC)
#if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
if (rt->rt_dest.sa_family == AF_INET)
ipv4ll_recvrt(cmd, rt);
#endif

View File

@ -92,7 +92,7 @@ void rt_headfreeif(struct rt_head *);
struct rt * rt_new0(struct dhcpcd_ctx *);
void rt_setif(struct rt *, struct interface *);
struct rt * rt_new(struct interface *);
void rt_recvrt(int, const struct rt *);
void rt_recvrt(int, const struct rt *, pid_t);
void rt_build(struct dhcpcd_ctx *, int);
#endif

View File

@ -704,9 +704,7 @@ script_runreason(const struct interface *ifp, const char *reason)
int status = 0;
struct fd_list *fd;
if (ifp->options->script &&
(ifp->options->script[0] == '\0' ||
strcmp(ifp->options->script, "/dev/null") == 0) &&
if (ifp->options->script == NULL &&
TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)
return 0;
@ -717,12 +715,10 @@ script_runreason(const struct interface *ifp, const char *reason)
return -1;
}
if (ifp->options->script &&
(ifp->options->script[0] == '\0' ||
strcmp(ifp->options->script, "/dev/null") == 0))
if (ifp->options->script == NULL)
goto send_listeners;
argv[0] = ifp->options->script ? ifp->options->script : UNCONST(SCRIPT);
argv[0] = ifp->options->script;
argv[1] = NULL;
logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason);