This brings parity with non privsep features.
Aside from the lack of Solaris support, but that's another day.
/* Match the ARP probe to our states.
* Ignore Unicast Poll, RFC1122. */
state = ARP_CSTATE(ifp);
+ if (state == NULL)
+ return;
TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) {
if (IN_ARE_ADDR_EQUAL(&arm.sip, &astate->addr) ||
(IN_IS_ADDR_UNSPECIFIED(&arm.sip) &&
struct dhcpcd_ctx *ctx = ifp->ctx;
struct iarp_state *state;
- if ((state = ARP_STATE(ifp)) == NULL)
- return;
-
#ifdef PRIVSEP
- if ((ctx->options & (DHCPCD_PRIVSEP|DHCPCD_FORKED)) == DHCPCD_PRIVSEP) {
- if (ps_bpf_closearp(ifp) == -1)
+ if (IN_PRIVSEP(ctx)) {
+ if (IN_PRIVSEP_SE(ctx) &&
+ ps_bpf_closearp(ifp) == -1)
logerr(__func__);
+ return;
}
#endif
+ if ((state = ARP_STATE(ifp)) == NULL)
+ return;
+
if (state->bpf_fd == -1)
return;
eloop_event_delete(ctx->eloop, state->bpf_fd);
free(state);
ifp->if_data[IF_DATA_ARP] = NULL;
}
-#ifdef PRIVSEP
- } else if (ifp->ctx->options & DHCPCD_PRIVSEP) {
-#if 0
- if (privsep_arp_bpf(state) == -1)
- logerr(__func__);
-#endif
-#endif
} else if (state->bpf_fd != -1) {
if (bpf_arp(ifp, state->bpf_fd) == -1)
logerr(__func__);
struct iarp_state *state;
#ifdef PRIVSEP
- if (ifp->ctx->options & DHCPCD_PRIVSEP)
+ if (IN_PRIVSEP_SE(ifp->ctx))
return ps_bpf_openarp(ifp) == -1 ? -1 : 0;
#endif
arp_probe(struct arp_state *astate)
{
- if (arp_open(astate->iface) == -1) {
- logerr(__func__);
- return;
- } else {
- const struct iarp_state *state = ARP_CSTATE(astate->iface);
-
- if (bpf_arp(astate->iface, state->bpf_fd) == -1)
- logerr(__func__);
- }
astate->probes = 0;
logdebugx("%s: probing for %s",
astate->iface->name, inet_ntoa(astate->addr));
}
#endif /* ARP */
-static struct arp_state *
+struct arp_state *
arp_find(struct interface *ifp, const struct in_addr *addr)
{
struct iarp_state *state;
struct arp_state *a2;
int r;
- if (arp_open(astate->iface) == -1) {
+ if (!(IN_PRIVSEP(astate->iface->ctx)) && arp_open(astate->iface) == -1)
+ {
logerr(__func__);
return;
}
arp_ifannounceaddr(iff, ia);
}
+void
+arp_change(struct arp_state *astate, const struct in_addr *addr)
+{
+ struct interface *ifp = astate->iface;
+ struct iarp_state *state = ARP_STATE(ifp);
+
+#ifdef PRIVSEP
+ if (!IN_IS_ADDR_UNSPECIFIED(&astate->addr) &&
+ IN_PRIVSEP_SE(ifp->ctx))
+ {
+ if (ps_bpf_deladdr(ifp, &astate->addr) == -1)
+ logerr(__func__);
+ }
+#endif
+
+ if (addr != NULL)
+ astate->addr = *addr;
+ else
+ astate->addr.s_addr = INADDR_ANY;
+
+#ifdef PRIVSEP
+ if (addr != NULL && IN_PRIVSEP_SE(ifp->ctx)) {
+ if (ps_bpf_addaddr(ifp, addr) == -1)
+ logerr(__func__);
+ } else
+#endif
+ if (state->bpf_fd != -1) {
+ if (bpf_arp(ifp, state->bpf_fd) == -1)
+ logerr(__func__); /* try and continue */
+ }
+}
+
struct arp_state *
arp_new(struct interface *ifp, const struct in_addr *addr)
{
struct arp_state *astate;
if ((state = ARP_STATE(ifp)) == NULL) {
+#ifdef PRIVSEP
+ /* We need to ensure ARP is spawned so we can add to it. */
+ if (IN_PRIVSEP_SE(ifp->ctx) && arp_open(ifp) == -1) {
+ logerr(__func__);
+ return NULL;
+ }
+#endif
ifp->if_data[IF_DATA_ARP] = malloc(sizeof(*state));
state = ARP_STATE(ifp);
if (state == NULL) {
return NULL;
}
astate->iface = ifp;
- if (addr)
- astate->addr = *addr;
state = ARP_STATE(ifp);
TAILQ_INSERT_TAIL(&state->arp_states, astate, next);
-
-#ifdef PRIVSEP
- if (ifp->ctx->options & DHCPCD_PRIVSEP) {
-#if 0
- if (privsep_arp_bpf(state) == -1)
- logerr(__func__);
-#endif
- } else
-#endif
- if (state->bpf_fd != -1) {
- if (bpf_arp(ifp, state->bpf_fd) == -1)
- logerr(__func__); /* try and continue */
- }
-
+ arp_change(astate, addr);
return astate;
}
ifp = astate->iface;
eloop_timeout_delete(ifp->ctx->eloop, NULL, astate);
+ arp_change(astate, NULL);
state = ARP_STATE(ifp);
TAILQ_REMOVE(&state->arp_states, astate, next);
if (astate->free_cb)
#ifdef ARP
void arp_packet(struct interface *, uint8_t *, size_t);
struct arp_state *arp_new(struct interface *, const struct in_addr *);
+void arp_change(struct arp_state *, const struct in_addr *);
void arp_probe(struct arp_state *);
void arp_announce(struct arp_state *);
void arp_announceaddr(struct dhcpcd_ctx *, const struct in_addr *);
void arp_ifannounceaddr(struct interface *, const struct in_addr *);
void arp_cancel(struct arp_state *);
+struct arp_state * arp_find(struct interface *, const struct in_addr *);
void arp_free(struct arp_state *);
void arp_freeaddr(struct interface *, const struct in_addr *);
void arp_drop(struct interface *);
struct bpf_insn *bp;
struct iarp_state *state;
uint16_t arp_len;
+ struct arp_state *astate;
+ size_t naddrs;
if (fd == -1)
return 0;
bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr),
false, ifp->hwaddr, ifp->hwlen);
-#ifdef PRIVSEP
- if (ifp->ctx->options & DHCPCD_PRIVSEP) {
- /* No mech to notify of ARP states, so just accept all ARP. */
- BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
- bp++;
- return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
- }
-#endif
-
state = ARP_STATE(ifp);
- if (TAILQ_FIRST(&state->arp_states)) {
- struct arp_state *astate;
- size_t naddrs;
+ /* privsep may not have an initial state yet. */
+ if (state == NULL || TAILQ_FIRST(&state->arp_states) == NULL)
+ goto noaddrs;
- /* Match sender protocol address */
- BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
- sizeof(struct arphdr) + ifp->hwlen);
- bp++;
- naddrs = 0;
- TAILQ_FOREACH(astate, &state->arp_states, next) {
- if (++naddrs > ARP_ADDRS_MAX) {
- errno = ENOBUFS;
- logerr(__func__);
- break;
- }
- BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
- htonl(astate->addr.s_addr), 0, 1);
- bp++;
- BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
- bp++;
+ /* Match sender protocol address */
+ BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
+ sizeof(struct arphdr) + ifp->hwlen);
+ bp++;
+ naddrs = 0;
+ TAILQ_FOREACH(astate, &state->arp_states, next) {
+ if (IN_IS_ADDR_UNSPECIFIED(&astate->addr))
+ continue;
+ if (++naddrs > ARP_ADDRS_MAX) {
+ errno = ENOBUFS;
+ logerr(__func__);
+ break;
}
-
- /* If we didn't match sender, then we're only interested in
- * ARP probes to us, so check the null host sender. */
- BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, INADDR_ANY, 1, 0);
+ BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
+ htonl(astate->addr.s_addr), 0, 1);
bp++;
- BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
+ BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
bp++;
+ }
- /* Match target protocol address */
- BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
- (sizeof(struct arphdr)
- + (size_t)(ifp->hwlen * 2) + sizeof(in_addr_t)));
- bp++;
- naddrs = 0;
- TAILQ_FOREACH(astate, &state->arp_states, next) {
- if (++naddrs > ARP_ADDRS_MAX) {
- /* Already logged error above. */
- break;
- }
- BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
- htonl(astate->addr.s_addr), 0, 1);
- bp++;
- BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
- bp++;
- }
+ /* If we didn't match sender, then we're only interested in
+ * ARP probes to us, so check the null host sender. */
+ BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, INADDR_ANY, 1, 0);
+ bp++;
+ BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
+ bp++;
- /* Return nothing, no protocol address match. */
- BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
+ /* Match target protocol address */
+ BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
+ (sizeof(struct arphdr)
+ + (size_t)(ifp->hwlen * 2) + sizeof(in_addr_t)));
+ bp++;
+ naddrs = 0;
+ TAILQ_FOREACH(astate, &state->arp_states, next) {
+ if (++naddrs > ARP_ADDRS_MAX) {
+ /* Already logged error above. */
+ break;
+ }
+ BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
+ htonl(astate->addr.s_addr), 0, 1);
+ bp++;
+ BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
bp++;
}
+noaddrs:
+ /* Return nothing, no protocol address match. */
+ BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
+ bp++;
+
return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
}
#endif
return;
#ifdef PRIVSEP
- if ((ctx->options & (DHCPCD_PRIVSEP|DHCPCD_FORKED)) == DHCPCD_PRIVSEP) {
+ if (IN_PRIVSEP_SE(ctx)) {
ps_bpf_closebootp(ifp);
if (state->addr != NULL)
ps_inet_closebootp(state->addr);
* address or hwaddr, so move to the next
* arping profile */
if (++state->arping_index < ifo->arping_len) {
- astate->addr.s_addr =
- ifo->arping[state->arping_index];
+ struct in_addr addr = {
+ .s_addr = ifo->arping[state->arping_index]
+ };
+
+ arp_change(astate, &addr);
arp_probe(astate);
return;
}
return;
#ifdef PRIVSEP
- if (ctx->options & DHCPCD_PRIVSEP) {
+ if (IN_PRIVSEP_SE(ctx)) {
if (ps_inet_openbootp(state->addr) == -1)
logerr(__func__);
return;
#ifdef PRIVSEP
/* Ignore double reads */
- if (ifp->ctx->options & DHCPCD_PRIVSEP) {
+ if (IN_PRIVSEP(ifp->ctx)) {
switch (state->state) {
case DHS_BOUND: /* FALLTHROUGH */
case DHS_RENEW:
return;
}
#ifdef PRIVSEP
- if (ctx->options & DHCPCD_PRIVSEP) {
+ if (IN_PRIVSEP(ctx)) {
switch (state->state) {
case DHS_BOUND: /* FALLTHROUGH */
case DHS_RENEW:
state = D_STATE(ifp);
#ifdef PRIVSEP
- if (ifp->ctx->options & DHCPCD_PRIVSEP) {
+ if (IN_PRIVSEP_SE(ifp->ctx)) {
if (ps_bpf_openbootp(ifp) == -1) {
logerr(__func__);
return -1;
dhcpcd_fork_cb(void *arg)
{
struct dhcpcd_ctx *ctx = arg;
- int exit_code = EXIT_FAILURE;
+ int exit_code;
ssize_t len;
len = read(ctx->fork_fd, &exit_code, sizeof(exit_code));
- if (len == -1)
+ if (len == -1) {
logerr(__func__);
- else if ((size_t)len < sizeof(exit_code))
- logerr("%s: truncated read", __func__);
+ exit_code = EXIT_FAILURE;
+ } else if ((size_t)len < sizeof(exit_code)) {
+ logerrx("%s: truncated read %zd (expected %zu)",
+ __func__, len, sizeof(exit_code));
+ exit_code = EXIT_FAILURE;
+ }
eloop_exit(ctx->eloop, exit_code);
}
}
static ssize_t
-ps_bpf_recvmsgcb(void *arg, __unused struct ps_msghdr *psm, struct msghdr *msg)
+ps_bpf_arp_addr(uint8_t cmd, struct ps_process *psp, struct msghdr *msg)
+{
+ struct interface *ifp = &psp->psp_ifp;
+ struct iovec *iov = msg->msg_iov;
+ struct in_addr addr;
+ struct arp_state *astate;
+
+ if (psp == NULL) {
+ errno = ESRCH;
+ return -1;
+ }
+
+ assert(msg->msg_iovlen == 1);
+ assert(iov->iov_len = sizeof(addr));
+ memcpy(&addr, iov->iov_base, sizeof(addr));
+ if (cmd & PS_START) {
+ astate = arp_new(ifp, &addr);
+ if (astate == NULL)
+ return -1;
+ } else if (cmd & PS_DELETE) {
+ astate = arp_find(ifp, &addr);
+ if (astate == NULL) {
+ errno = ESRCH;
+ return -1;
+ }
+ arp_free(astate);
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return bpf_arp(ifp, psp->psp_work_fd);
+}
+
+static ssize_t
+ps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
{
struct ps_process *psp = arg;
struct iovec *iov = msg->msg_iov;
+ if (psm->ps_cmd & (PS_START | PS_DELETE))
+ return ps_bpf_arp_addr(psm->ps_cmd, psp, msg);
+
return bpf_send(&psp->psp_ifp, psp->psp_work_fd, psp->psp_proto,
iov->iov_base, iov->iov_len);
}
.ps_cmd = cmd,
.ps_id = {
.psi_ifindex = ifp->index,
- .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
+ .psi_cmd = (uint8_t)(cmd &
+ ~(PS_START | PS_STOP | PS_DELETE)),
},
};
+ if (psm.ps_id.psi_cmd == PS_BPF_ARP_ADDR)
+ psm.ps_id.psi_cmd = PS_BPF_ARP;
+
return ps_sendpsmdata(ctx, ctx->ps_root_fd, &psm, data, len);
}
return ps_bpf_send(ifp, PS_BPF_ARP | PS_START, ifp, sizeof(*ifp));
}
+ssize_t
+ps_bpf_addaddr(const struct interface *ifp, const struct in_addr *addr)
+{
+
+ return ps_bpf_send(ifp, PS_BPF_ARP_ADDR | PS_START, addr, sizeof(*addr));
+}
+
+ssize_t
+ps_bpf_deladdr(const struct interface *ifp, const struct in_addr *addr)
+{
+
+ return ps_bpf_send(ifp, PS_BPF_ARP_ADDR | PS_DELETE, addr, sizeof(*addr));
+}
+
ssize_t
ps_bpf_closearp(const struct interface *ifp)
{
#ifdef ARP
ssize_t ps_bpf_openarp(const struct interface *);
+ssize_t ps_bpf_addaddr(const struct interface *, const struct in_addr *);
+ssize_t ps_bpf_deladdr(const struct interface *, const struct in_addr *);
ssize_t ps_bpf_closearp(const struct interface *);
ssize_t ps_bpf_sendarp(const struct interface *, const void *, size_t);
#endif
.psr_errno = errno,
};
+#ifdef PRIVSEP_DEBUG
+ logdebugx("%s: result %zd errno %d", __func__, result, errno);
+#endif
+
return write(ctx->ps_root_fd, &psr, sizeof(psr));
}
size_t len = iov->iov_len;
ssize_t err;
- cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
+ cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP | PS_DELETE));
psp = ps_findprocess(ctx, &psm->ps_id);
#ifdef PRIVSEP_DEBUG
logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
#endif
- if (!(psm->ps_cmd & PS_START) && psp != NULL) {
+ if ((!(psm->ps_cmd & PS_START) || cmd == PS_BPF_ARP_ADDR) &&
+ psp != NULL)
+ {
if (psm->ps_cmd & PS_STOP) {
int ret = ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
return ps_sendpsmmsg(ctx, psp->psp_fd, psm, msg);
}
- if (psm->ps_cmd & PS_STOP && psp == NULL)
+ if (psm->ps_cmd & (PS_STOP | PS_DELETE) && psp == NULL)
return 0;
/* All these should just be PS_START */
return NULL;
psp->psp_ctx = ctx;
memcpy(&psp->psp_id, psid, sizeof(psp->psp_id));
+ psp->psp_work_fd = -1;
TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next);
return psp;
}
#define PS_DHCP6 0x03
#define PS_BPF_BOOTP 0x04
#define PS_BPF_ARP 0x05
+#define PS_BPF_ARP_ADDR 0x06
#define PS_IOCTL 0x10
#define PS_SCRIPT 0x11
#define PS_ROUTE 0x13 /* Also used for NETLINK */
#define PS_WRITEPATHUINT 0x14
+#define PS_DELETE 0x20
#define PS_START 0x40
#define PS_STOP 0x80
CMSG_SPACE(sizeof(struct in6_pktinfo) + \
sizeof(int)))
+/* Handy macro to work out if in the privsep engine or not. */
+#define IN_PRIVSEP(ctx) \
+ ((ctx)->options & DHCPCD_PRIVSEP)
+#define IN_PRIVSEP_SE(ctx) \
+ (((ctx)->options & (DHCPCD_PRIVSEP | DHCPCD_FORKED)) == DHCPCD_PRIVSEP)
+
#include "config.h"
#include "arp.h"
#include "dhcp.h"