]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
privsep: Enable Capsicum for all processes.
authorRoy Marples <roy@marples.name>
Tue, 19 May 2020 15:19:05 +0000 (16:19 +0100)
committerRoy Marples <roy@marples.name>
Tue, 19 May 2020 15:19:05 +0000 (16:19 +0100)
Except for the priviledged process.
This is quite an in-depth change:
 * ARP is now one process per address
 * BPF flags are now returned via privsep
 * BPF write filters are locked when supported
 * The root process sends to the network

The last step is done by opening RAW sockets and then sending a UDP
header (where applicable) to avoid binding to an address
which is already in use by the reader sockets.

This is slightly wasteful for OS's without sandboxing but does
have the very nice side effect of not needing a source address
to unicast DHCPs replies from which makes the code smaller.

27 files changed:
src/arp.c
src/arp.h
src/bpf.c
src/bpf.h
src/control.c
src/dhcp.c
src/dhcp.h
src/dhcp6.c
src/dhcp6.h
src/dhcpcd.c
src/dhcpcd.h
src/eloop.c
src/if-bsd.c
src/if.c
src/if.h
src/ipv4.c
src/ipv4.h
src/ipv6.c
src/ipv6nd.c
src/ipv6nd.h
src/privsep-bpf.c
src/privsep-bpf.h
src/privsep-inet.c
src/privsep-inet.h
src/privsep-root.c
src/privsep.c
src/privsep.h

index dfb94403ac934729489322187329d68589c90e17..7cc4f8aeb1c1e923feddac0402f0f54e72decf18 100644 (file)
--- a/src/arp.c
+++ b/src/arp.c
 __CTASSERT(sizeof(struct arphdr) == 8);
 
 static ssize_t
-arp_request(const struct interface *ifp,
-    const struct in_addr *sip, const struct in_addr *tip)
+arp_request(const struct arp_state *astate,
+    const struct in_addr *sip)
 {
+       const struct interface *ifp = astate->iface;
+       const struct in_addr *tip = &astate->addr;
        uint8_t arp_buffer[ARP_LEN];
        struct arphdr ar;
        size_t len;
        uint8_t *p;
-       const struct iarp_state *state;
 
        ar.ar_hrd = htons(ifp->hwtype);
        ar.ar_pro = htons(ETHERTYPE_IP);
@@ -107,12 +108,11 @@ arp_request(const struct interface *ifp,
 
 #ifdef PRIVSEP
        if (ifp->ctx->options & DHCPCD_PRIVSEP)
-               return ps_bpf_sendarp(ifp, arp_buffer, len);
+               return ps_bpf_sendarp(ifp, tip, arp_buffer, len);
 #endif
-       state = ARP_CSTATE(ifp);
        /* Note that well formed ethernet will add extra padding
         * to ensure that the packet is at least 60 bytes (64 including FCS). */
-       return bpf_send(ifp, state->bpf_fd, ETHERTYPE_ARP, arp_buffer, len);
+       return bpf_send(astate->bpf, ETHERTYPE_ARP, arp_buffer, len);
 
 eexit:
        errno = ENOBUFS;
@@ -179,7 +179,7 @@ arp_found(struct arp_state *astate, const struct arp_msg *amsg)
            eloop_timespec_diff(&now, &astate->defend, NULL) < DEFEND_INTERVAL)
                logwarnx("%s: %d second defence failed for %s",
                    ifp->name, DEFEND_INTERVAL, inet_ntoa(astate->addr));
-       else if (arp_request(ifp, &astate->addr, &astate->addr) == -1)
+       else if (arp_request(astate, &astate->addr) == -1)
                logerr(__func__);
        else {
                logdebugx("%s: defended address %s",
@@ -222,7 +222,7 @@ arp_validate(const struct interface *ifp, struct arphdr *arp)
 }
 
 void
-arp_packet(struct interface *ifp, uint8_t *data, size_t len)
+arp_packet(struct interface *ifp, uint8_t *data, size_t len, uint8_t bpf_flags)
 {
        size_t fl = bpf_frame_header_len(ifp), falen;
        const struct interface *ifn;
@@ -292,108 +292,39 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len)
                if (IN_ARE_ADDR_EQUAL(&arm.sip, &astate->addr) ||
                    (IN_IS_ADDR_UNSPECIFIED(&arm.sip) &&
                    IN_ARE_ADDR_EQUAL(&arm.tip, &astate->addr) &&
-                   state->bpf_flags & BPF_BCAST))
+                   bpf_flags & BPF_BCAST))
                        arp_found(astate, &arm);
        }
 }
 
-static void
-arp_close(struct interface *ifp)
-{
-       struct dhcpcd_ctx *ctx = ifp->ctx;
-       struct iarp_state *state;
-
-#ifdef PRIVSEP
-       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);
-       bpf_close(ifp, state->bpf_fd);
-       state->bpf_fd = -1;
-       state->bpf_flags |= BPF_EOF;
-}
-
-static void
-arp_tryfree(struct iarp_state *state)
-{
-       struct interface *ifp = state->ifp;
-
-       /* If there are no more ARP states, close the socket. */
-       if (TAILQ_FIRST(&state->arp_states) == NULL) {
-               arp_close(ifp);
-               if (state->bpf_flags & BPF_READING)
-                       state->bpf_flags |= BPF_EOF;
-               else {
-                       free(state);
-                       ifp->if_data[IF_DATA_ARP] = NULL;
-               }
-       } else if (state->bpf_fd != -1) {
-               if (bpf_arp(ifp, state->bpf_fd) == -1)
-                       logerr(__func__);
-       }
-}
-
 static void
 arp_read(void *arg)
 {
-       struct iarp_state *state = arg;
-       struct interface *ifp = state->ifp;
+       struct arp_state *astate = arg;
+       struct bpf *bpf = astate->bpf;
+       struct interface *ifp = astate->iface;
        uint8_t buf[ARP_LEN];
        ssize_t bytes;
+       struct in_addr addr = astate->addr;
 
        /* 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->bpf_flags &= ~BPF_EOF;
-       state->bpf_flags |= BPF_READING;
-       while (!(state->bpf_flags & BPF_EOF)) {
-               bytes = bpf_read(ifp, state->bpf_fd, buf, sizeof(buf),
-                                &state->bpf_flags);
+       bpf->bpf_flags &= ~BPF_EOF;
+       while (!(bpf->bpf_flags & BPF_EOF)) {
+               bytes = bpf_read(bpf, buf, sizeof(buf));
                if (bytes == -1) {
                        logerr("%s: %s", __func__, ifp->name);
-                       arp_close(ifp);
-                       break;
+                       arp_free(astate);
+                       return;
                }
-               arp_packet(ifp, buf, (size_t)bytes);
+               arp_packet(ifp, buf, (size_t)bytes, bpf->bpf_flags);
                /* Check we still have a state after processing. */
-               if ((state = ARP_STATE(ifp)) == NULL)
+               if ((astate = arp_find(ifp, &addr)) == NULL)
+                       break;
+               if ((bpf = astate->bpf) == NULL)
                        break;
        }
-       if (state != NULL) {
-               state->bpf_flags &= ~BPF_READING;
-               /* Try and free the state if nothing left to do. */
-               arp_tryfree(state);
-       }
-}
-
-static int
-arp_open(struct interface *ifp)
-{
-       struct iarp_state *state;
-
-#ifdef PRIVSEP
-       if (IN_PRIVSEP_SE(ifp->ctx))
-               return ps_bpf_openarp(ifp) == -1 ? -1 : 0;
-#endif
-
-       state = ARP_STATE(ifp);
-       if (state->bpf_fd == -1) {
-               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, state);
-       }
-       return state->bpf_fd;
 }
 
 static void
@@ -425,7 +356,7 @@ arp_probe1(void *arg)
            ifp->name, inet_ntoa(astate->addr),
            astate->probes ? astate->probes : PROBE_NUM, PROBE_NUM,
            (float)delay / MSEC_PER_SEC);
-       if (arp_request(ifp, NULL, &astate->addr) == -1)
+       if (arp_request(astate, NULL) == -1)
                logerr(__func__);
 }
 
@@ -436,11 +367,6 @@ arp_probe(struct arp_state *astate)
        astate->probes = 0;
        logdebugx("%s: probing for %s",
            astate->iface->name, inet_ntoa(astate->addr));
-       if (!(IN_PRIVSEP(astate->iface->ctx)) && arp_open(astate->iface) == -1)
-       {
-               logerr(__func__);
-               return;
-       }
        arp_probe1(astate);
 }
 #endif /* ARP */
@@ -501,7 +427,7 @@ arp_announce1(void *arg)
                goto skip_request;
 #endif
 
-       if (arp_request(ifp, &astate->addr, &astate->addr) == -1)
+       if (arp_request(astate, &astate->addr) == -1)
                logerr(__func__);
 
 #ifndef __linux__
@@ -524,12 +450,6 @@ arp_announce(struct arp_state *astate)
        struct arp_state *a2;
        int r;
 
-       if (!(IN_PRIVSEP(astate->iface->ctx)) && arp_open(astate->iface) == -1)
-       {
-               logerr(__func__);
-               return;
-       }
-
        /* Cancel any other ARP announcements for this address. */
        TAILQ_FOREACH(ifp, astate->iface->ctx->ifaces, next) {
                state = ARP_STATE(ifp);
@@ -608,22 +528,12 @@ 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) {
                        logerr(__func__);
                        return NULL;
                }
-               state->ifp = ifp;
-               state->bpf_fd = -1;
-               state->bpf_flags = 0;
                TAILQ_INIT(&state->arp_states);
        } else {
                if (addr && (astate = arp_find(ifp, addr)))
@@ -635,12 +545,31 @@ arp_new(struct interface *ifp, const struct in_addr *addr)
                return NULL;
        }
        astate->iface = ifp;
+       astate->addr = *addr;
+
+#ifdef PRIVSEP
+       if (IN_PRIVSEP(ifp->ctx)) {
+               if (ps_bpf_openarp(ifp, addr) == -1) {
+                       logerr(__func__);
+                       free(astate);
+                       return NULL;
+               }
+       } else
+#endif
+       {
+               astate->bpf = bpf_open(ifp, bpf_arp, addr);
+               if (astate->bpf == NULL) {
+                       logerr(__func__);
+                       free(astate);
+                       return NULL;
+               }
+               eloop_event_add(ifp->ctx->eloop, astate->bpf->bpf_fd,
+                   arp_read, astate);
+       }
+
+
        state = ARP_STATE(ifp);
        TAILQ_INSERT_TAIL(&state->arp_states, astate, next);
-       if (state->bpf_fd != -1) {
-               if (bpf_arp(ifp, state->bpf_fd) == -1)
-                       logerr(__func__); /* try and continue */
-       }
        return astate;
 }
 
@@ -655,19 +584,36 @@ void
 arp_free(struct arp_state *astate)
 {
        struct interface *ifp;
+       struct dhcpcd_ctx *ctx;
        struct iarp_state *state;
 
        if (astate == NULL)
                return;
 
        ifp = astate->iface;
-       eloop_timeout_delete(ifp->ctx->eloop, NULL, astate);
+       ctx = ifp->ctx;
+       eloop_timeout_delete(ctx->eloop, NULL, astate);
+
        state = ARP_STATE(ifp);
        TAILQ_REMOVE(&state->arp_states, astate, next);
        if (astate->free_cb)
                astate->free_cb(astate);
+
+#ifdef PRIVSEP
+       if (IN_PRIVSEP(ctx) && ps_bpf_closearp(ifp, &astate->addr) == -1)
+               logerr(__func__);
+#endif
+       if (astate->bpf != NULL) {
+               eloop_event_delete(ctx->eloop, astate->bpf->bpf_fd);
+               bpf_close(astate->bpf);
+       }
+
        free(astate);
-       arp_tryfree(state);
+
+       if (TAILQ_FIRST(&state->arp_states) == NULL) {
+               free(state);
+               ifp->if_data[IF_DATA_ARP] = NULL;
+       }
 }
 
 void
@@ -688,6 +634,4 @@ arp_drop(struct interface *ifp)
        while ((state = ARP_STATE(ifp)) != NULL &&
            (astate = TAILQ_FIRST(&state->arp_states)) != NULL)
                arp_free(astate);
-
-       /* No need to close because the last free will close */
 }
index 182041c849d21f0d5448fa4f8b196dee9995cbd5..460aeee6031b2739689edc916d4d11456e42616b 100644 (file)
--- a/src/arp.h
+++ b/src/arp.h
@@ -41,6 +41,7 @@
 #define RATE_LIMIT_INTERVAL    60
 #define DEFEND_INTERVAL                10
 
+#include "bpf.h"
 #include "dhcpcd.h"
 #include "if.h"
 
@@ -66,24 +67,22 @@ struct arp_msg {
 struct arp_state {
        TAILQ_ENTRY(arp_state) next;
        struct interface *iface;
+       struct in_addr addr;
+       struct bpf *bpf;
+
+       int probes;
+       int claims;
+       struct timespec defend;
 
        void (*found_cb)(struct arp_state *, const struct arp_msg *);
        void (*not_found_cb)(struct arp_state *);
        void (*announced_cb)(struct arp_state *);
        void (*defend_failed_cb)(struct arp_state *);
        void (*free_cb)(struct arp_state *);
-
-       struct in_addr addr;
-       int probes;
-       int claims;
-       struct timespec defend;
 };
 TAILQ_HEAD(arp_statehead, arp_state);
 
 struct iarp_state {
-       struct interface *ifp;
-       int bpf_fd;
-       unsigned int bpf_flags;
        struct arp_statehead arp_states;
 };
 
@@ -93,7 +92,7 @@ struct iarp_state {
        ((const struct iarp_state *)(ifp)->if_data[IF_DATA_ARP])
 
 #ifdef ARP
-void arp_packet(struct interface *, uint8_t *, size_t);
+void arp_packet(struct interface *, uint8_t *, size_t, uint8_t);
 struct arp_state *arp_new(struct interface *, const struct in_addr *);
 void arp_probe(struct arp_state *);
 void arp_announce(struct arp_state *);
index 96be4e5305402cd2e49f741048358b67571a317f..0342c415386a4dc8571b41e5e0032c762cc7b15a 100644 (file)
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -57,8 +57,6 @@
 #include "if.h"
 #include "logerr.h"
 
-#define        ARP_ADDRS_MAX   3
-
 /* BPF helper macros */
 #ifdef __linux__
 #define        BPF_WHOLEPACKET         0x7fffffff /* work around buggy LPF filters */
@@ -129,12 +127,12 @@ static const uint8_t etherbcastaddr[] =
     { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
 int
-bpf_frame_bcast(const struct interface *ifp, const char *frame)
+bpf_frame_bcast(const struct interface *ifp, const void *frame)
 {
 
        switch (ifp->hwtype) {
        case ARPHRD_ETHER:
-               return memcmp(frame +
+               return memcmp((const char *)frame +
                    offsetof(struct ether_header, ether_dhost),
                    etherbcastaddr, sizeof(etherbcastaddr));
        default:
@@ -148,15 +146,15 @@ bpf_frame_bcast(const struct interface *ifp, const char *frame)
 
 const char *bpf_name = "Berkley Packet Filter";
 
-int
-bpf_open(struct interface *ifp, int (*filter)(struct interface *, int))
+struct bpf *
+bpf_open(const struct interface *ifp,
+    int (*filter)(const struct bpf *, const struct in_addr *),
+    const struct in_addr *ia)
 {
-       struct ipv4_state *state;
-       int fd = -1;
-       struct ifreq ifr;
+       struct bpf *bpf;
+       struct bpf_version pv = { .bv_major = 0, .bv_minor = 0 };
+       struct ifreq ifr = { .ifr_flags = 0 };
        int ibuf_len = 0;
-       size_t buf_len;
-       struct bpf_version pv;
 #ifdef BIOCIMMEDIATE
        unsigned int flags;
 #endif
@@ -174,29 +172,31 @@ bpf_open(struct interface *ifp, int (*filter)(struct interface *, int))
        char device[32];
        int n = 0;
 
+       bpf = calloc(1, sizeof(*bpf));
+       if (bpf == NULL)
+               return NULL;
+       bpf->bpf_ifp = ifp;
+
        do {
                snprintf(device, sizeof(device), "/dev/bpf%d", n++);
-               fd = open(device, O_RDWR | O_NONBLOCK
+               bpf->bpf_fd = open(device, O_RDWR | O_NONBLOCK
 #ifdef O_CLOEXEC
                                | O_CLOEXEC
 #endif
                );
-       } while (fd == -1 && errno == EBUSY);
+       } while (bpf->bpf_fd == -1 && errno == EBUSY);
 #endif
 
-       if (fd == -1)
-               return -1;
+       if (bpf->bpf_fd == -1)
+               goto eexit;
 
 #ifndef O_CLOEXEC
-       if ((fd_opts = fcntl(fd, F_GETFD)) == -1 ||
-           fcntl(fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1) {
-               close(fd);
-               return -1;
-       }
+       if ((fd_opts = fcntl(bpf->bpf_fd, F_GETFD)) == -1 ||
+           fcntl(bpf->bpf_fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1)
+               goto eexit;
 #endif
 
-       memset(&pv, 0, sizeof(pv));
-       if (ioctl(fd, BIOCVERSION, &pv) == -1)
+       if (ioctl(bpf->bpf_fd, BIOCVERSION, &pv) == -1)
                goto eexit;
        if (pv.bv_major != BPF_MAJOR_VERSION ||
            pv.bv_minor < BPF_MINOR_VERSION) {
@@ -204,59 +204,49 @@ bpf_open(struct interface *ifp, int (*filter)(struct interface *, int))
                goto eexit;
        }
 
-       if (filter(ifp, fd) != 0)
-               goto eexit;
-
-       memset(&ifr, 0, sizeof(ifr));
        strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
-       if (ioctl(fd, BIOCSETIF, &ifr) == -1)
+       if (ioctl(bpf->bpf_fd, BIOCSETIF, &ifr) == -1)
                goto eexit;
 
-       /* Get the required BPF buffer length from the kernel. */
-       if (ioctl(fd, BIOCGBLEN, &ibuf_len) == -1)
-               goto eexit;
-       buf_len = (size_t)ibuf_len;
-       state = ipv4_getstate(ifp);
-       if (state == NULL)
-               goto eexit;
-       if (state->buffer_size != buf_len) {
-               void *nb;
-
-               if ((nb = realloc(state->buffer, buf_len)) == NULL)
-                       goto eexit;
-               state->buffer = nb;
-               state->buffer_size = buf_len;
-       }
-
 #ifdef BIOCIMMEDIATE
        flags = 1;
-       if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1)
+       if (ioctl(bpf->bpf_fd, BIOCIMMEDIATE, &flags) == -1)
                goto eexit;
 #endif
 
-       return fd;
+       if (filter(bpf, ia) != 0)
+               goto eexit;
+
+       /* Get the required BPF buffer length from the kernel. */
+       if (ioctl(bpf->bpf_fd, BIOCGBLEN, &ibuf_len) == -1)
+               goto eexit;
+       bpf->bpf_size = (size_t)ibuf_len;
+       bpf->bpf_buffer = malloc(bpf->bpf_size);
+       if (bpf->bpf_buffer == NULL)
+               goto eexit;
+       return bpf;
 
 eexit:
-       close(fd);
-       return -1;
+       if (bpf->bpf_fd != -1)
+               close(bpf->bpf_fd);
+       free(bpf);
+       return NULL;
 }
 
 /* BPF requires that we read the entire buffer.
  * So we pass the buffer in the API so we can loop on >1 packet. */
 ssize_t
-bpf_read(struct interface *ifp, int fd, void *data, size_t len,
-    unsigned int *flags)
+bpf_read(struct bpf *bpf, void *data, size_t len)
 {
        ssize_t bytes;
-       struct ipv4_state *state = IPV4_STATE(ifp);
-
        struct bpf_hdr packet;
        const char *payload;
 
-       *flags &= ~BPF_EOF;
+       bpf->bpf_flags &= ~BPF_EOF;
        for (;;) {
-               if (state->buffer_len == 0) {
-                       bytes = read(fd, state->buffer, state->buffer_size);
+               if (bpf->bpf_len == 0) {
+                       bytes = read(bpf->bpf_fd, bpf->bpf_buffer,
+                           bpf->bpf_size);
 #if defined(__sun)
                        /* After 2^31 bytes, the kernel offset overflows.
                         * To work around this bug, lseek 0. */
@@ -267,31 +257,31 @@ bpf_read(struct interface *ifp, int fd, void *data, size_t len,
 #endif
                        if (bytes == -1 || bytes == 0)
                                return bytes;
-                       state->buffer_len = (size_t)bytes;
-                       state->buffer_pos = 0;
+                       bpf->bpf_len = (size_t)bytes;
+                       bpf->bpf_pos = 0;
                }
                bytes = -1;
-               memcpy(&packet, state->buffer + state->buffer_pos,
-                   sizeof(packet));
-               if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
-                   state->buffer_len)
+               payload = (const char *)bpf->bpf_buffer + bpf->bpf_pos;
+               memcpy(&packet, payload, sizeof(packet));
+               if (bpf->bpf_pos + packet.bh_caplen + packet.bh_hdrlen >
+                   bpf->bpf_len)
                        goto next; /* Packet beyond buffer, drop. */
-               payload = state->buffer + state->buffer_pos + packet.bh_hdrlen;
-               if (bpf_frame_bcast(ifp, payload) == 0)
-                       *flags |= BPF_BCAST;
-               else
-                       *flags &= ~BPF_BCAST;
+               payload += packet.bh_hdrlen;
                if (packet.bh_caplen > len)
                        bytes = (ssize_t)len;
                else
                        bytes = (ssize_t)packet.bh_caplen;
+               if (bpf_frame_bcast(bpf->bpf_ifp, payload) == 0)
+                       bpf->bpf_flags |= BPF_BCAST;
+               else
+                       bpf->bpf_flags &= ~BPF_BCAST;
                memcpy(data, payload, (size_t)bytes);
 next:
-               state->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
+               bpf->bpf_pos += BPF_WORDALIGN(packet.bh_hdrlen +
                    packet.bh_caplen);
-               if (state->buffer_pos >= state->buffer_len) {
-                       state->buffer_len = state->buffer_pos = 0;
-                       *flags |= BPF_EOF;
+               if (bpf->bpf_pos >= bpf->bpf_len) {
+                       bpf->bpf_len = bpf->bpf_pos = 0;
+                       bpf->bpf_flags |= BPF_EOF;
                }
                if (bytes != -1)
                        return bytes;
@@ -324,16 +314,17 @@ bpf_wattach(int fd, void *filter, unsigned int filter_len)
 #ifndef __sun
 /* SunOS is special too - sending via BPF goes nowhere. */
 ssize_t
-bpf_send(const struct interface *ifp, int fd, uint16_t protocol,
+bpf_send(const struct bpf *bpf, uint16_t protocol,
     const void *data, size_t len)
 {
        struct iovec iov[2];
        struct ether_header eh;
 
-       switch(ifp->hwtype) {
+       switch(bpf->bpf_ifp->hwtype) {
        case ARPHRD_ETHER:
                memset(&eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
-               memcpy(&eh.ether_shost, ifp->hwaddr, sizeof(eh.ether_shost));
+               memcpy(&eh.ether_shost, bpf->bpf_ifp->hwaddr,
+                   sizeof(eh.ether_shost));
                eh.ether_type = htons(protocol);
                iov[0].iov_base = &eh;
                iov[0].iov_len = sizeof(eh);
@@ -345,19 +336,105 @@ bpf_send(const struct interface *ifp, int fd, uint16_t protocol,
        }
        iov[1].iov_base = UNCONST(data);
        iov[1].iov_len = len;
-       return writev(fd, iov, 2);
+       return writev(bpf->bpf_fd, iov, 2);
 }
 #endif
 
-int
-bpf_close(struct interface *ifp, int fd)
+void
+bpf_close(struct bpf *bpf)
+{
+
+       close(bpf->bpf_fd);
+       free(bpf->bpf_buffer);
+       free(bpf);
+}
+
+#ifdef ARP
+#define BPF_CMP_HWADDR_LEN     ((((HWADDR_LEN / 4) + 2) * 2) + 1)
+static unsigned int
+bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off,
+    bool equal, const uint8_t *hwaddr, size_t hwaddr_len)
 {
-       struct ipv4_state *state = IPV4_STATE(ifp);
+       struct bpf_insn *bp;
+       size_t maclen, nlft, njmps;
+       uint32_t mac32;
+       uint16_t mac16;
+       uint8_t jt, jf;
+
+       /* Calc the number of jumps */
+       if ((hwaddr_len / 4) >= 128) {
+               errno = EINVAL;
+               return 0;
+       }
+       njmps = (hwaddr_len / 4) * 2; /* 2 instructions per check */
+       /* We jump after the 1st check. */
+       if (njmps)
+               njmps -= 2;
+       nlft = hwaddr_len % 4;
+       if (nlft) {
+               njmps += (nlft / 2) * 2;
+               nlft = nlft % 2;
+               if (nlft)
+                       njmps += 2;
+
+       }
+
+       /* Skip to positive finish. */
+       njmps++;
+       if (equal) {
+               jt = (uint8_t)njmps;
+               jf = 0;
+       } else {
+               jt = 0;
+               jf = (uint8_t)njmps;
+       }
 
-       /* Rewind the buffer on closing. */
-       state->buffer_len = state->buffer_pos = 0;
-       return close(fd);
+       bp = bpf;
+       for (; hwaddr_len > 0;
+            hwaddr += maclen, hwaddr_len -= maclen, off += maclen)
+       {
+               if (bpf_len < 3) {
+                       errno = ENOBUFS;
+                       return 0;
+               }
+               bpf_len -= 3;
+
+               if (hwaddr_len >= 4) {
+                       maclen = sizeof(mac32);
+                       memcpy(&mac32, hwaddr, maclen);
+                       BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND, off);
+                       bp++;
+                       BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
+                                    htonl(mac32), jt, jf);
+               } else if (hwaddr_len >= 2) {
+                       maclen = sizeof(mac16);
+                       memcpy(&mac16, hwaddr, maclen);
+                       BPF_SET_STMT(bp, BPF_LD + BPF_H + BPF_IND, off);
+                       bp++;
+                       BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
+                                    htons(mac16), jt, jf);
+               } else {
+                       maclen = sizeof(*hwaddr);
+                       BPF_SET_STMT(bp, BPF_LD + BPF_B + BPF_IND, off);
+                       bp++;
+                       BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
+                                    *hwaddr, jt, jf);
+               }
+               if (jt)
+                       jt = (uint8_t)(jt - 2);
+               if (jf)
+                       jf = (uint8_t)(jf - 2);
+               bp++;
+       }
+
+       /* Last step is always return failure.
+        * Next step is a positive finish. */
+       BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
+       bp++;
+
+       return (unsigned int)(bp - bpf);
 }
+#endif
 
 #ifdef ARP
 static const struct bpf_insn bpf_arp_ether [] = {
@@ -401,22 +478,22 @@ static const struct bpf_insn bpf_arp_filter [] = {
 };
 #define BPF_ARP_FILTER_LEN     __arraycount(bpf_arp_filter)
 
-#define BPF_ARP_ADDRS_LEN      1 + (ARP_ADDRS_MAX * 2) + 3 + \
-                               (ARP_ADDRS_MAX * 2) + 1
+/* One address is two checks of two statements. */
+#define BPF_NADDRS             1
+#define BPF_ARP_ADDRS_LEN      5 + ((BPF_NADDRS * 2) * 2)
 
-#define BPF_ARP_LEN            BPF_ARP_ETHER_LEN + BPF_ARP_FILTER_LEN
+#define BPF_ARP_LEN            BPF_ARP_ETHER_LEN + BPF_ARP_FILTER_LEN + \
+                               BPF_CMP_HWADDR_LEN + BPF_ARP_ADDRS_LEN
 
-int
-bpf_arp(struct interface *ifp, int fd)
+static int
+bpf_arp_rw(const struct bpf *bpf, const struct in_addr *ia, bool recv)
 {
-       struct bpf_insn bpf[BPF_ARP_LEN + 1];
+       const struct interface *ifp = bpf->bpf_ifp;
+       struct bpf_insn buf[BPF_ARP_LEN + 1];
        struct bpf_insn *bp;
        uint16_t arp_len;
 
-       if (fd == -1)
-               return 0;
-
-       bp = bpf;
+       bp = buf;
        /* Check frame header. */
        switch(ifp->hwtype) {
        case ARPHRD_ETHER:
@@ -433,20 +510,60 @@ bpf_arp(struct interface *ifp, int fd)
        memcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter));
        bp += BPF_ARP_FILTER_LEN;
 
-       /* Past the filer so return the packet. */
+       /* Ensure it's not from us. */
+       bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr),
+                            !recv, ifp->hwaddr, ifp->hwlen);
+
+       /* Match sender protocol address */
+       BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
+           sizeof(struct arphdr) + ifp->hwlen);
+       bp++;
+       BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);
+       bp++;
        BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
        bp++;
 
-       if (bpf_attach(fd, bpf, (unsigned int)(bp - bpf)) == -1)
-               return -1;
+       /* 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++;
+
+       /* 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++;
+       BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);
+       bp++;
+       BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
+       bp++;
+
+       /* No match, drop it */
+       BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
+       bp++;
 
 #ifdef BIOCSETWF
-       if (bpf_wattach(fd, bpf, (unsigned int)(bp - bpf)) == -1 ||
-           ioctl(fd, BIOCLOCK) == -1)
-               return -1;
+       if (!recv)
+               return bpf_wattach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
 #endif
 
+       return bpf_attach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
+}
+
+int
+bpf_arp(const struct bpf *bpf, const struct in_addr *ia)
+{
+
+#ifdef BIOCSETWF
+       if (bpf_arp_rw(bpf, ia, true) == -1 ||
+           bpf_arp_rw(bpf, ia, false) == -1 ||
+           ioctl(bpf->bpf_fd, BIOCLOCK) == -1)
+               return -1;
        return 0;
+#else
+       return bpf_arp_rw(bpf, ia, true);
+#endif
 }
 #endif
 
@@ -523,17 +640,14 @@ static const struct bpf_insn bpf_bootp_write[] = {
                                BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4
 
 static int
-bpf_bootp_rw(struct interface *ifp, int fd, bool read)
+bpf_bootp_rw(const struct bpf *bpf, bool read)
 {
-       struct bpf_insn bpf[BPF_BOOTP_LEN + 1];
+       struct bpf_insn buf[BPF_BOOTP_LEN + 1];
        struct bpf_insn *bp;
 
-       if (fd == -1)
-               return 0;
-
-       bp = bpf;
+       bp = buf;
        /* Check frame header. */
-       switch(ifp->hwtype) {
+       switch(bpf->bpf_ifp->hwtype) {
 #ifdef ARPHRD_NONE
        case ARPHRD_NONE:
                memcpy(bp, bpf_bootp_none, sizeof(bpf_bootp_none));
@@ -562,7 +676,7 @@ bpf_bootp_rw(struct interface *ifp, int fd, bool read)
                BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
                bp++;
 
-               return bpf_wattach(fd, bpf, (unsigned int)(bp - bpf));
+               return bpf_wattach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
        }
 #else
        UNUSED(read);
@@ -575,20 +689,20 @@ bpf_bootp_rw(struct interface *ifp, int fd, bool read)
        BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
        bp++;
 
-       return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
+       return bpf_attach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
 }
 
 int
-bpf_bootp(struct interface *ifp, int fd)
+bpf_bootp(const struct bpf *bpf, __unused const struct in_addr *ia)
 {
 
 #ifdef BIOCSETWF
-       if (bpf_bootp_rw(ifp, fd, true) == -1 ||
-           bpf_bootp_rw(ifp, fd, false) == -1 ||
-           ioctl(fd, BIOCLOCK) == -1)
+       if (bpf_bootp_rw(bpf, true) == -1 ||
+           bpf_bootp_rw(bpf, false) == -1 ||
+           ioctl(bpf->bpf_fd, BIOCLOCK) == -1)
                return -1;
        return 0;
 #else
-       return bpf_bootp_rw(ifp, fd, true);
+       return bpf_bootp_rw(bpf, true);
 #endif
 }
index 97e55d11698358f9ac7ff8841db0999c0ab37384..bf38fdfff81aaa9d58ee4ab540a8794d536135c1 100644 (file)
--- a/src/bpf.h
+++ b/src/bpf.h
 #ifndef BPF_HEADER
 #define BPF_HEADER
 
-#define        BPF_READING             (1U << 0)
-#define        BPF_EOF                 (1U << 1)
-#define        BPF_PARTIALCSUM         (1U << 2)
-#define        BPF_BCAST               (1U << 3)
+#define        BPF_EOF                 0x01
+#define        BPF_PARTIALCSUM         0x02
+#define        BPF_BCAST               0x04
 
 /*
  * Even though we program the BPF filter should we trust it?
 
 #include "dhcpcd.h"
 
+struct bpf {
+       const struct interface *bpf_ifp;
+       int bpf_fd;
+       uint8_t bpf_flags;
+       void *bpf_buffer;
+       size_t bpf_size;
+       size_t bpf_len;
+       size_t bpf_pos;
+};
+
 extern const char *bpf_name;
 size_t bpf_frame_header_len(const struct interface *);
 void *bpf_frame_header_src(const struct interface *, void *, size_t *);
 void *bpf_frame_header_dst(const struct interface *, void *, size_t *);
-int bpf_frame_bcast(const struct interface *, const char *frame);
-int bpf_open(struct interface *, int (*)(struct interface *, int));
-int bpf_close(struct interface *, int);
+int bpf_frame_bcast(const struct interface *, const void *);
+struct bpf * bpf_open(const struct interface *,
+    int (*)(const struct bpf *, const struct in_addr *),
+    const struct in_addr *);
+void bpf_close(struct bpf *);
 int bpf_attach(int, void *, unsigned int);
-ssize_t bpf_send(const struct interface *, int, uint16_t, const void *, size_t);
-ssize_t bpf_read(struct interface *, int, void *, size_t, unsigned int *);
-int bpf_arp(struct interface *, int);
-int bpf_bootp(struct interface *, int);
+ssize_t bpf_send(const struct bpf *, uint16_t, const void *, size_t);
+ssize_t bpf_read(struct bpf *, void *, size_t);
+int bpf_arp(const struct bpf *, const struct in_addr *);
+int bpf_bootp(const struct bpf *, const struct in_addr *);
 #endif
index f41fce1518f53c0582b82df583ac94f719da2740..4dc8a16a47eeac2d50377046c6ee0a877ce62dd6 100644 (file)
@@ -198,10 +198,8 @@ make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family,
 {
        int fd;
 
-#define SOCK_FLAGS     SOCK_CLOEXEC | SOCK_NONBLOCK
-       if ((fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_FLAGS, 0)) == -1)
+       if ((fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0)) == -1)
                return -1;
-#undef SOCK_FLAGS
        memset(sa, 0, sizeof(*sa));
        sa->sun_family = AF_UNIX;
        if (unpriv)
index 0a0c0a84a150be8af56d35ba89a625752740bb44..e3f59726c8beb135bd5836d9e81b3e05f734ff77 100644 (file)
@@ -1443,6 +1443,7 @@ get_lease(struct interface *ifp,
        if (get_option_uint32(ctx, &lease->renewaltime,
            bootp, len, DHO_RENEWALTIME) != 0)
                lease->renewaltime = 0;
+       lease->renewaltime = 5;
        if (get_option_uint32(ctx, &lease->rebindtime,
            bootp, len, DHO_REBINDTIME) != 0)
                lease->rebindtime = 0;
@@ -1537,16 +1538,15 @@ dhcp_close(struct interface *ifp)
        }
 #endif
 
-       if (state->bpf_fd != -1) {
-               eloop_event_delete(ctx->eloop, state->bpf_fd);
-               bpf_close(ifp, state->bpf_fd);
-               state->bpf_fd = -1;
-               state->bpf_flags |= BPF_EOF;
+       if (state->bpf != NULL) {
+               eloop_event_delete(ctx->eloop, state->bpf->bpf_fd);
+               bpf_close(state->bpf);
+               state->bpf = NULL;
        }
-       if (state->udp_fd != -1) {
-               eloop_event_delete(ctx->eloop, state->udp_fd);
-               close(state->udp_fd);
-               state->udp_fd = -1;
+       if (state->udp_rfd != -1) {
+               eloop_event_delete(ctx->eloop, state->udp_rfd);
+               close(state->udp_rfd);
+               state->udp_rfd = -1;
        }
 
        state->interval = 0;
@@ -1559,7 +1559,7 @@ dhcp_openudp(struct in_addr *ia)
        struct sockaddr_in sin;
        int n;
 
-       if ((s = xsocket(PF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP)) == -1)
+       if ((s = xsocket(PF_INET, SOCK_DGRAM | SOCK_CXNB, IPPROTO_UDP)) == -1)
                return -1;
 
        n = 1;
@@ -1572,6 +1572,11 @@ dhcp_openudp(struct in_addr *ia)
        if (setsockopt(s, IPPROTO_IP, IP_RECVPKTINFO, &n, sizeof(n)) == -1)
                goto errexit;
 #endif
+#ifdef SO_RERROR
+       if (setsockopt(s, SOL_SOCKET, SO_RERROR, &n, sizeof(n)) == -1)
+               goto errexit;
+#endif
+
        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = htons(BOOTPC);
@@ -1669,33 +1674,28 @@ dhcp_sendudp(struct interface *ifp, struct in_addr *to, void *data, size_t len)
                .sin_len = sizeof(sin),
 #endif
        };
+       struct udphdr udp = {
+           .uh_sport = htons(BOOTPC),
+           .uh_dport = htons(BOOTPS),
+           .uh_ulen = htons(sizeof(udp) + len),
+       };
        struct iovec iov[] = {
-               { .iov_base = data, .iov_len = len }
+           { .iov_base = &udp, .iov_len = sizeof(udp), },
+           { .iov_base = data, .iov_len = len, },
        };
        struct msghdr msg = {
                .msg_name = (void *)&sin,
                .msg_namelen = sizeof(sin),
                .msg_iov = iov,
-               .msg_iovlen = 1,
+               .msg_iovlen = __arraycount(iov),
        };
-       struct dhcp_state *state = D_STATE(ifp);
-       ssize_t r;
-       int fd;
+       struct dhcpcd_ctx *ctx = ifp->ctx;
 
 #ifdef PRIVSEP
-       if (ifp->ctx->options & DHCPCD_PRIVSEP)
-               return ps_inet_sendbootp(state->addr, &msg);
+       if (ctx->options & DHCPCD_PRIVSEP)
+               return ps_inet_sendbootp(ifp, &msg);
 #endif
-       fd = state->udp_fd;
-       if (fd == -1) {
-               fd = dhcp_openudp(&state->addr->addr);
-               if (fd == -1)
-                       return -1;
-       }
-       r = sendmsg(fd, &msg, 0);
-       if (state->udp_fd == -1)
-               close(fd);
-       return r;
+       return sendmsg(ctx->udp_wfd, &msg, 0);
 }
 
 static void
@@ -1771,8 +1771,7 @@ send_message(struct interface *ifp, uint8_t type,
         * interface we want to configure because we can't dictate the
         * interface via IP_PKTINFO unlike for IPv6.
         */
-       if (to.s_addr != INADDR_BROADCAST)
-       {
+       if (to.s_addr != INADDR_BROADCAST) {
                if (dhcp_sendudp(ifp, &to, bootp, len) != -1)
                        goto out;
                logerr("%s: dhcp_sendudp", ifp->name);
@@ -1791,8 +1790,7 @@ send_message(struct interface *ifp, uint8_t type,
                free(udp);
 #endif
        } else {
-               r = bpf_send(ifp, state->bpf_fd,
-                   ETHERTYPE_IP, (uint8_t *)udp, ulen);
+               r = bpf_send(state->bpf, ETHERTYPE_IP, udp, ulen);
                free(udp);
        }
        /* If we failed to send a raw packet this normally means
@@ -2073,30 +2071,84 @@ dhcp_addr_duplicated(struct interface *ifp, struct in_addr *ia)
 }
 #endif
 
-#if defined(ARP) && (!defined(KERNEL_RFC5227) || defined(ARPING))
+#ifdef ARP
+#ifndef KERNEL_RFC5227
 static void
-dhcp_arp_not_found(struct arp_state *astate)
+dhcp_arp_defend_failed(struct arp_state *astate)
 {
-       struct interface *ifp;
+       struct interface *ifp = astate->iface;
+
+       dhcp_drop(ifp, "EXPIRED");
+       dhcp_start1(ifp);
+}
+#endif
+
+#if !defined(KERNEL_RFC5227) || defined(ARPING)
+static void dhcp_arp_not_found(struct arp_state *);
+
+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->found_cb = dhcp_arp_found;
+       astate->not_found_cb = dhcp_arp_not_found;
+#ifdef KERNEL_RFC5227
+       astate->announced_cb = dhcp_arp_announced;
+#else
+       astate->announced_cb = NULL;
+       astate->defend_failed_cb = dhcp_arp_defend_failed;
+#endif
+       return astate;
+}
+#endif
+
 #ifdef ARPING
+static int
+dhcp_arping(struct interface *ifp)
+{
        struct dhcp_state *state;
        struct if_options *ifo;
+       struct arp_state *astate;
+       struct in_addr addr;
+
+       state = D_STATE(ifp);
+       ifo = ifp->options;
+
+       if (ifo->arping_len == 0 || state->arping_index > ifo->arping_len)
+               return 0;
+
+       if (state->arping_index + 1 == ifo->arping_len) {
+               state->arping_index++;
+               dhcpcd_startinterface(ifp);
+               return 1;
+       }
+
+       addr.s_addr = ifo->arping[++state->arping_index];
+       astate = dhcp_arp_new(ifp, &addr);
+       if (astate == NULL) {
+               logerr(__func__);
+               return -1;
+       }
+       arp_probe(astate);
+       return 1;
+}
 #endif
 
+#if !defined(KERNEL_RFC5227) || defined(ARPING)
+static void
+dhcp_arp_not_found(struct arp_state *astate)
+{
+       struct interface *ifp;
+
        ifp = astate->iface;
 #ifdef ARPING
-       state = D_STATE(ifp);
-       ifo = ifp->options;
-       if (ifo->arping_len && state->arping_index < ifo->arping_len) {
-               /* We didn't find a profile for this
-                * address or hwaddr, so move to the next
-                * arping profile */
-               if (++state->arping_index < ifo->arping_len) {
-                       arp_probe(astate);
-                       return;
-               }
+       if (dhcp_arping(ifp) == 1) {
                arp_free(astate);
-               dhcpcd_startinterface(ifp);
                return;
        }
 #endif
@@ -2146,6 +2198,7 @@ dhcp_arp_found(struct arp_state *astate, const struct arp_msg *amsg)
        arp_free(astate);
        dhcp_addr_duplicated(ifp, &addr);
 }
+#endif
 
 #ifdef KERNEL_RFC5227
 static void
@@ -2304,8 +2357,8 @@ dhcp_bind(struct interface *ifp)
        }
 #endif
 
-       state->udp_fd = dhcp_openudp(&state->addr->addr);
-       if (state->udp_fd == -1) {
+       state->udp_rfd = dhcp_openudp(&state->addr->addr);
+       if (state->udp_rfd == -1) {
                logerr(__func__);
                /* Address sharing without master mode is not supported.
                 * It's also possible another DHCP client could be running,
@@ -2314,7 +2367,7 @@ dhcp_bind(struct interface *ifp)
                dhcp_openbpf(ifp);
                return;
        }
-       eloop_event_add(ctx->eloop, state->udp_fd, dhcp_handleifudp, ifp);
+       eloop_event_add(ctx->eloop, state->udp_rfd, dhcp_handleifudp, ifp);
 }
 
 static void
@@ -2361,41 +2414,6 @@ dhcp_message_new(struct bootp **bootp,
        return sizeof(**bootp);
 }
 
-#ifdef ARP
-#ifndef KERNEL_RFC5227
-static void
-dhcp_arp_defend_failed(struct arp_state *astate)
-{
-       struct interface *ifp = astate->iface;
-
-       dhcp_drop(ifp, "EXPIRED");
-       dhcp_start1(ifp);
-}
-#endif
-
-#if !defined(KERNEL_RFC5227) || defined(ARPING)
-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->found_cb = dhcp_arp_found;
-       astate->not_found_cb = dhcp_arp_not_found;
-#ifdef KERNEL_RFC5227
-       astate->announced_cb = dhcp_arp_announced;
-#else
-       astate->announced_cb = NULL;
-       astate->defend_failed_cb = dhcp_arp_defend_failed;
-#endif
-       return astate;
-}
-#endif
-#endif /* ARP */
-
 #if defined(ARP) || defined(KERNEL_RFC5227)
 static int
 dhcp_arp_address(struct interface *ifp)
@@ -3231,7 +3249,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;
+                       state->bpf->bpf_flags |= BPF_EOF;
                        return;
                }
                eloop_timeout_delete(ifp->ctx->eloop, send_discover, ifp);
@@ -3446,15 +3464,15 @@ dhcp_handlebootp(struct interface *ifp, struct bootp *bootp, size_t len,
 }
 
 void
-dhcp_packet(struct interface *ifp, uint8_t *data, size_t len)
+dhcp_packet(struct interface *ifp, uint8_t *data, size_t len, uint8_t bpf_flags)
 {
        struct bootp *bootp;
        struct in_addr from;
        size_t udp_len;
-       const struct dhcp_state *state = D_CSTATE(ifp);
        size_t fl = bpf_frame_header_len(ifp);
-
 #ifdef PRIVSEP
+       const struct dhcp_state *state = D_CSTATE(ifp);
+
        /* Ignore double reads */
        if (IN_PRIVSEP(ifp->ctx)) {
                switch (state->state) {
@@ -3470,8 +3488,8 @@ dhcp_packet(struct interface *ifp, uint8_t *data, size_t len)
        /* Trim frame header */
        if (fl != 0) {
                if (len < fl) {
-                       logerrx("%s: %s: short frame header",
-                           __func__, ifp->name);
+                       logerrx("%s: %s: short frame header %zu",
+                           __func__, ifp->name, len);
                        return;
                }
                len -= fl;
@@ -3487,7 +3505,7 @@ dhcp_packet(struct interface *ifp, uint8_t *data, size_t len)
                return;
        }
 
-       if (!checksums_valid(data, &from, state->bpf_flags)) {
+       if (!checksums_valid(data, &from, bpf_flags)) {
                logerrx("%s: checksum failure from %s",
                    ifp->name, inet_ntoa(from));
                return;
@@ -3511,15 +3529,11 @@ dhcp_readbpf(void *arg)
        uint8_t buf[FRAMELEN_MAX];
        ssize_t bytes;
        struct dhcp_state *state = D_STATE(ifp);
+       struct bpf *bpf = state->bpf;
 
-       /* 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->bpf_flags &= ~BPF_EOF;
-       state->bpf_flags |= BPF_READING;
-       while (!(state->bpf_flags & BPF_EOF)) {
-               bytes = bpf_read(ifp, state->bpf_fd, buf, sizeof(buf),
-                                &state->bpf_flags);
+       bpf->bpf_flags &= ~BPF_EOF;
+       while (!(bpf->bpf_flags & BPF_EOF)) {
+               bytes = bpf_read(bpf, buf, sizeof(buf));
                if (bytes == -1) {
                        if (state->state != DHS_NONE) {
                                logerr("%s: %s", __func__, ifp->name);
@@ -3527,13 +3541,13 @@ dhcp_readbpf(void *arg)
                        }
                        break;
                }
-               dhcp_packet(ifp, buf, (size_t)bytes);
+               dhcp_packet(ifp, buf, (size_t)bytes, bpf->bpf_flags);
                /* Check we still have a state after processing. */
                if ((state = D_STATE(ifp)) == NULL)
                        break;
+               if ((bpf = state->bpf) == NULL)
+                       break;
        }
-       if (state != NULL)
-               state->bpf_flags &= ~BPF_READING;
 }
 
 void
@@ -3557,7 +3571,7 @@ dhcp_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg)
                return;
        }
 
-       if (state->bpf_fd != -1) {
+       if (state->bpf != NULL) {
                /* Avoid a duplicate read if BPF is open for the interface. */
                return;
        }
@@ -3610,9 +3624,9 @@ dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp)
 
        if (ifp != NULL) {
                state = D_CSTATE(ifp);
-               s = state->udp_fd;
+               s = state->udp_rfd;
        } else
-               s = ctx->udp_fd;
+               s = ctx->udp_rfd;
 
        bytes = recvmsg(s, &msg, 0);
        if (bytes == -1) {
@@ -3657,11 +3671,11 @@ dhcp_openbpf(struct interface *ifp)
        }
 #endif
 
-       if (state->bpf_fd != -1)
+       if (state->bpf != NULL)
                return 0;
 
-       state->bpf_fd = bpf_open(ifp, bpf_bootp);
-       if (state->bpf_fd == -1) {
+       state->bpf = bpf_open(ifp, bpf_bootp, NULL);
+       if (state->bpf == NULL) {
                if (errno == ENOENT) {
                        logerrx("%s not found", bpf_name);
                        /* May as well disable IPv4 entirely at
@@ -3673,7 +3687,7 @@ dhcp_openbpf(struct interface *ifp)
        }
 
        eloop_event_add(ifp->ctx->eloop,
-           state->bpf_fd, dhcp_readbpf, ifp);
+           state->bpf->bpf_fd, dhcp_readbpf, ifp);
        return 0;
 }
 
@@ -3707,10 +3721,14 @@ dhcp_free(struct interface *ifp)
                }
        }
        if (ifp == NULL) {
-               if (ctx->udp_fd != -1) {
-                       eloop_event_delete(ctx->eloop, ctx->udp_fd);
-                       close(ctx->udp_fd);
-                       ctx->udp_fd = -1;
+               if (ctx->udp_rfd != -1) {
+                       eloop_event_delete(ctx->eloop, ctx->udp_rfd);
+                       close(ctx->udp_rfd);
+                       ctx->udp_rfd = -1;
+               }
+               if (ctx->udp_wfd != -1) {
+                       close(ctx->udp_wfd);
+                       ctx->udp_wfd = -1;
                }
 
                free(ctx->opt_buffer);
@@ -3734,8 +3752,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;
+       state->udp_rfd = -1;
 #ifdef ARPING
        state->arping_index = -1;
 #endif
@@ -3849,14 +3866,21 @@ dhcp_start1(void *arg)
         * Only do this in master mode so we don't swallow messages
         * for dhcpcd running on another interface. */
        if ((ctx->options & (DHCPCD_MASTER|DHCPCD_PRIVSEP)) == DHCPCD_MASTER
-           && ctx->udp_fd == -1)
+           && ctx->udp_rfd == -1)
        {
-               ctx->udp_fd = dhcp_openudp(NULL);
-               if (ctx->udp_fd == -1) {
+               ctx->udp_rfd = dhcp_openudp(NULL);
+               if (ctx->udp_rfd == -1) {
+                       logerr(__func__);
+                       return;
+               }
+               eloop_event_add(ctx->eloop, ctx->udp_rfd, dhcp_handleudp, ctx);
+       }
+       if (!IN_PRIVSEP(ctx) && ctx->udp_wfd == -1) {
+               ctx->udp_wfd = xsocket(PF_INET, SOCK_RAW|SOCK_CXNB,IPPROTO_UDP);
+               if (ctx->udp_wfd == -1) {
                        logerr(__func__);
                        return;
                }
-               eloop_event_add(ctx->eloop, ctx->udp_fd, dhcp_handleudp, ctx);
        }
 
        if (dhcp_init(ifp) == -1) {
@@ -3873,11 +3897,7 @@ dhcp_start1(void *arg)
 
 #ifdef ARPING
        if (ifo->arping_len && state->arping_index < ifo->arping_len) {
-               struct arp_state *astate;
-
-               astate = dhcp_arp_new(ifp, NULL);
-               if (astate)
-                       dhcp_arp_not_found(astate);
+               dhcp_arping(ifp);
                return;
        }
 #endif
index 5b01a52cb8fbbdb2555ec09c09ca895d423af7f6..dd0a63d0103e88e286196ca95b066dc0c48bf4b2 100644 (file)
@@ -41,6 +41,7 @@
 #include <stdint.h>
 
 #include "arp.h"
+#include "bpf.h"
 #include "auth.h"
 #include "dhcp-common.h"
 
@@ -221,9 +222,8 @@ struct dhcp_state {
        uint32_t xid;
        int socket;
 
-       int bpf_fd;
-       unsigned int bpf_flags;
-       int udp_fd;
+       struct bpf *bpf;
+       int udp_rfd;
        struct ipv4_addr *addr;
        uint8_t added;
 
@@ -256,7 +256,7 @@ ssize_t print_rfc3361(FILE *, const uint8_t *, size_t);
 ssize_t print_rfc3442(FILE *, const uint8_t *, size_t);
 
 int dhcp_openudp(struct in_addr *);
-void dhcp_packet(struct interface *, uint8_t *, size_t);
+void dhcp_packet(struct interface *, uint8_t *, size_t, uint8_t);
 void dhcp_recvmsg(struct dhcpcd_ctx *, struct msghdr *);
 void dhcp_printoptions(const struct dhcpcd_ctx *,
     const struct dhcp_opt *, size_t);
index 57f1a57be05b29209b6299a41bc594bc70447674..777d30a863bc325e662e64bf0e9d28c4b21f7b7f 100644 (file)
  */
 
 #include <sys/utsname.h>
+#include <sys/types.h>
 
 #include <netinet/in.h>
+#include <netinet/ip6.h>
 
 #include <assert.h>
 #include <ctype.h>
@@ -1197,22 +1199,26 @@ dhcp6_update_auth(struct interface *ifp, struct dhcp6_message *m, size_t len)
 }
 #endif
 
+static const struct in6_addr alldhcp = IN6ADDR_LINKLOCAL_ALLDHCP_INIT;
 static int
 dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
 {
        struct dhcp6_state *state = D6_STATE(ifp);
        struct dhcpcd_ctx *ctx = ifp->ctx;
+       unsigned int RT;
+       bool broadcast = true;
        struct sockaddr_in6 dst = {
            .sin6_family = AF_INET6,
            .sin6_port = htons(DHCP6_SERVER_PORT),
        };
-       unsigned int RT;
-       const char *broad_uni;
-       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,
+       struct udphdr udp = {
+           .uh_sport = htons(DHCP6_CLIENT_PORT),
+           .uh_dport = htons(DHCP6_SERVER_PORT),
+           .uh_ulen = htons(sizeof(udp) + state->send_len),
+       };
+       struct iovec iov[] = {
+           { .iov_base = &udp, .iov_len = sizeof(udp), },
+           { .iov_base = state->send, .iov_len = state->send_len, },
        };
        union {
                struct cmsghdr hdr;
@@ -1220,39 +1226,39 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
        } cmsgbuf = { .buf = { 0 } };
        struct msghdr msg = {
            .msg_name = &dst, .msg_namelen = sizeof(dst),
-           .msg_iov = &iov, .msg_iovlen = 1,
+           .msg_iov = iov, .msg_iovlen = __arraycount(iov),
        };
+       char uaddr[INET6_ADDRSTRLEN];
 
        if (!callback && ifp->carrier <= LINK_DOWN)
                return 0;
 
-#ifdef HAVE_SA_LEN
-       dst.sin6_len = sizeof(dst);
-#endif
-
-       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
-        * a Router Advertisement */
-       if (IN6_IS_ADDR_UNSPECIFIED(&state->unicast) ||
-           (state->state == DH6S_REQUEST &&
-           (!IN6_IS_ADDR_LINKLOCAL(&state->unicast) || lla == NULL)))
-       {
-               dst.sin6_addr = alldhcp;
-               broad_uni = "broadcasting";
-       } else {
-               dst.sin6_addr = state->unicast;
-               broad_uni = "unicasting";
+       if (!IN6_IS_ADDR_UNSPECIFIED(&state->unicast)) {
+               switch (state->send->type) {
+               case DHCP6_SOLICIT:     /* FALLTHROUGH */
+               case DHCP6_CONFIRM:     /* FALLTHROUGH */
+               case DHCP6_REBIND:
+                       /* Unicasting is denied for these types. */
+                       break;
+               default:
+                       broadcast = false;
+                       inet_ntop(AF_INET6, &state->unicast, uaddr,
+                           sizeof(uaddr));
+                       break;
+               }
        }
+       dst.sin6_addr = broadcast ? alldhcp : state->unicast;
 
        if (!callback) {
-               logdebugx("%s: %s %s with xid 0x%02x%02x%02x",
+               logdebugx("%s: %s %s with xid 0x%02x%02x%02x%s%s",
                    ifp->name,
-                   broad_uni,
+                   broadcast ? "broadcasting" : "unicasting",
                    dhcp6_get_op(state->send->type),
                    state->send->xid[0],
                    state->send->xid[1],
-                   state->send->xid[2]);
+                   state->send->xid[2],
+                   !broadcast ? " " : "",
+                   !broadcast ? uaddr : "");
                RT = 0;
        } else {
                if (state->IMD &&
@@ -1266,7 +1272,6 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
                         * 1 second grace seems to be the sweet spot. */
                        if (ifp->flags & IFF_POINTOPOINT)
                                state->RT += MSEC_PER_SEC;
-                       broad_uni = "delaying";
                } else if (state->RTC == 0)
                        state->RT = state->IRT * MSEC_PER_SEC;
 
@@ -1287,14 +1292,17 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
                    * ((float)lr / DHCP6_RAND_DIV));
 
                if (ifp->carrier > LINK_DOWN)
-                       logdebugx("%s: %s %s (xid 0x%02x%02x%02x),"
+                       logdebugx("%s: %s %s (xid 0x%02x%02x%02x)%s%s,"
                            " next in %0.1f seconds",
                            ifp->name,
-                           broad_uni,
+                           state->IMD != 0 ? "delaying" :
+                           broadcast ? "broadcasting" : "unicasting",
                            dhcp6_get_op(state->send->type),
                            state->send->xid[0],
                            state->send->xid[1],
                            state->send->xid[2],
+                           state->IMD == 0 && !broadcast ? " " : "",
+                           state->IMD == 0 && !broadcast ? uaddr : "",
                            (float)RT / MSEC_PER_SEC);
 
                /* Wait the initial delay */
@@ -1321,7 +1329,7 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
 #endif
 
        /* Set the outbound interface */
-       if (IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &alldhcp)) {
+       if (broadcast) {
                struct cmsghdr *cm;
                struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
 
@@ -1339,46 +1347,13 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
 
 #ifdef PRIVSEP
        if (IN_PRIVSEP(ifp->ctx)) {
-               struct ipv6_addr *ia;
-
-               if (IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &alldhcp))
-                       ia = lla;
-               else {
-                       /* Find an IA to send from */
-                       TAILQ_FOREACH(ia, &state->addrs, next) {
-                               if (ia->flags & IPV6_AF_STALE)
-                                       continue;
-                               if (ia->addr_flags & IN6_IFF_NOTUSEABLE)
-                                       continue;
-                               if (ia->ia_type == D6_OPTION_IA_PD)
-                                       continue;
-                               break;
-                       }
-               }
-               if (ia == NULL) {
-                       if (lla == NULL) {
-                               logerrx("%s: no address to send from",
-                                   ifp->name);
-                               return -1;
-                       }
-                       ia = lla;
-               }
-               if (ps_inet_senddhcp6(ia, &msg) == -1)
+               if (ps_inet_senddhcp6(ifp, &msg) == -1)
                        logerr(__func__);
                goto sent;
        }
 #endif
 
-       if (ctx->dhcp6_fd != -1)
-               s = ctx->dhcp6_fd;
-       else if (lla != NULL && lla->dhcp6_fd != -1)
-               s = lla->dhcp6_fd;
-       else {
-               logerrx("%s: no socket to send from", ifp->name);
-               return -1;
-       }
-
-       if (sendmsg(s, &msg, 0) == -1) {
+       if (sendmsg(ctx->dhcp6_wfd, &msg, 0) == -1) {
                logerr("%s: %s: sendmsg", __func__, ifp->name);
                /* Allow DHCPv6 to continue .... the errors
                 * would be rate limited by the protocol.
@@ -3722,7 +3697,7 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
        int s;
        ssize_t bytes;
 
-       s = ia != NULL ? ia->dhcp6_fd : ctx->dhcp6_fd;
+       s = ia != NULL ? ia->dhcp6_fd : ctx->dhcp6_rfd;
        bytes = recvmsg(s, &msg, 0);
        if (bytes == -1) {
                logerr(__func__);
@@ -3749,22 +3724,36 @@ dhcp6_recvctx(void *arg)
        dhcp6_recv(ctx, NULL);
 }
 
+int
+dhcp6_openraw(void)
+{
+       int fd, v;
+
+       fd = socket(PF_INET6, SOCK_RAW | SOCK_CXNB, IPPROTO_UDP);
+       if (fd == -1)
+               return -1;
+
+       v = 1;
+       if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &v, sizeof(v)) == -1)
+               return -1;
+
+       v = offsetof(struct udphdr, uh_sum);
+       if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &v, sizeof(v)) == -1)
+               return -1;
+
+       return fd;
+}
+
 int
 dhcp6_openudp(unsigned int ifindex, struct in6_addr *ia)
 {
        struct sockaddr_in6 sa;
        int n, s;
 
-#define SOCK_FLAGS     SOCK_CLOEXEC | SOCK_NONBLOCK
-       s = xsocket(PF_INET6, SOCK_DGRAM | SOCK_FLAGS, IPPROTO_UDP);
-#undef SOCK_FLAGS
+       s = xsocket(PF_INET6, SOCK_DGRAM | SOCK_CXNB, IPPROTO_UDP);
        if (s == -1)
                goto errexit;
 
-       n = 1;
-       if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n)) == -1)
-               goto errexit;
-
        memset(&sa, 0, sizeof(sa));
        sa.sin6_family = AF_INET6;
        sa.sin6_port = htons(DHCP6_CLIENT_PORT);
@@ -3784,6 +3773,12 @@ dhcp6_openudp(unsigned int ifindex, struct in6_addr *ia)
        if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &n, sizeof(n)) == -1)
                goto errexit;
 
+#ifdef SO_RERROR
+       n = 1;
+       if (setsockopt(s, SOL_SOCKET, SO_RERROR, &n, sizeof(n)) == -1)
+               goto errexit;
+#endif
+
        return s;
 
 errexit:
@@ -3836,14 +3831,22 @@ dhcp6_start1(void *arg)
        const struct dhcp_compat *dhc;
 
        if ((ctx->options & (DHCPCD_MASTER|DHCPCD_PRIVSEP)) == DHCPCD_MASTER &&
-           ctx->dhcp6_fd == -1)
+           ctx->dhcp6_rfd == -1)
        {
-               ctx->dhcp6_fd = dhcp6_openudp(0, NULL);
-               if (ctx->dhcp6_fd == -1) {
+               ctx->dhcp6_rfd = dhcp6_openudp(0, NULL);
+               if (ctx->dhcp6_rfd == -1) {
+                       logerr(__func__);
+                       return;
+               }
+               eloop_event_add(ctx->eloop, ctx->dhcp6_rfd, dhcp6_recvctx, ctx);
+       }
+
+       if (!IN_PRIVSEP(ctx) && ctx->dhcp6_wfd == -1) {
+               ctx->dhcp6_wfd = dhcp6_openraw();
+               if (ctx->dhcp6_wfd == -1) {
                        logerr(__func__);
                        return;
                }
-               eloop_event_add(ctx->eloop, ctx->dhcp6_fd, dhcp6_recvctx, ctx);
        }
 
        state = D6_STATE(ifp);
@@ -4055,10 +4058,10 @@ dhcp6_freedrop(struct interface *ifp, int drop, const char *reason)
                                break;
                }
        }
-       if (ifp == NULL && ctx->dhcp6_fd != -1) {
-               eloop_event_delete(ctx->eloop, ctx->dhcp6_fd);
-               close(ctx->dhcp6_fd);
-               ctx->dhcp6_fd = -1;
+       if (ifp == NULL && ctx->dhcp6_rfd != -1) {
+               eloop_event_delete(ctx->eloop, ctx->dhcp6_rfd);
+               close(ctx->dhcp6_rfd);
+               ctx->dhcp6_rfd = -1;
        }
 }
 
index d099837573cae449d270c74ef1aed21a610cc6b7..329b515aa0234737fc3cbf286778b3e2b1a15e1f 100644 (file)
@@ -226,6 +226,7 @@ struct dhcp6_state {
        (D6_CSTATE((ifp)) &&                                                   \
        D6_CSTATE((ifp))->reason && dhcp6_dadcompleted((ifp)))
 
+int dhcp6_openraw(void);
 int dhcp6_openudp(unsigned int, struct in6_addr *);
 void dhcp6_recvmsg(struct dhcpcd_ctx *, struct msghdr *, struct ipv6_addr *);
 void dhcp6_printoptions(const struct dhcpcd_ctx *,
index 10e5e6ee226b11d1db96565fa4f2975d8003a2aa..38702d90e0b10769aa30b847de9cba32c571f17d 100644 (file)
@@ -71,6 +71,9 @@ const char dhcpcd_copyright[] = "Copyright (c) 2006-2020 Roy Marples";
 #include "privsep.h"
 #include "script.h"
 
+#ifdef HAVE_CAPSICUM
+#include <sys/capsicum.h>
+#endif
 #ifdef HAVE_UTIL_H
 #include <util.h>
 #endif
@@ -379,6 +382,7 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
 static void
 dhcpcd_drop(struct interface *ifp, int stop)
 {
+       return;
 
 #ifdef DHCP6
        dhcp6_drop(ifp, stop ? NULL : "EXPIRE6");
@@ -1844,13 +1848,15 @@ main(int argc, char **argv)
        ctx.dev_fd = -1;
 #endif
 #ifdef INET
-       ctx.udp_fd = -1;
+       ctx.udp_rfd = -1;
+       ctx.udp_wfd = -1;
 #endif
 #if defined(INET6) && !defined(__sun)
        ctx.nd_fd = -1;
 #endif
 #ifdef DHCP6
-       ctx.dhcp6_fd = -1;
+       ctx.dhcp6_rfd = -1;
+       ctx.dhcp6_wfd = -1;
 #endif
 #ifdef PRIVSEP
        ctx.ps_root_fd = ctx.ps_data_fd = -1;
index 5d896738559726e1fbfa3ecdc4f4a37d0fa057ae..1eda2dcda4231acbe3272f5f748404578ec13bcb 100644 (file)
@@ -206,7 +206,8 @@ struct dhcpcd_ctx {
        struct dhcp_opt *dhcp_opts;
        size_t dhcp_opts_len;
 
-       int udp_fd;
+       int udp_rfd;
+       int udp_wfd;
 
        /* Our aggregate option buffer.
         * We ONLY use this when options are split, which for most purposes is
@@ -223,11 +224,11 @@ struct dhcpcd_ctx {
 #endif
        struct ra_head *ra_routers;
 
-       int dhcp6_fd;
-
        struct dhcp_opt *nd_opts;
        size_t nd_opts_len;
 #ifdef DHCP6
+       int dhcp6_rfd;
+       int dhcp6_wfd;
        struct dhcp_opt *dhcp6_opts;
        size_t dhcp6_opts_len;
 #endif
index 7596f59c3ad00d950b19ea71e595f5d592a4a29b..dfa794966e415d7e7e7d9e64bbf80287732bae50 100644 (file)
@@ -1023,12 +1023,13 @@ eloop_start(struct eloop *eloop, sigset_t *signals)
                        continue;
                }
 
-               eloop_reduce_timers(eloop);
-
                t = TAILQ_FIRST(&eloop->timeouts);
                if (t == NULL && eloop->events_len == 0)
                        break;
 
+               if (t != NULL)
+                       eloop_reduce_timers(eloop);
+
                if (t != NULL && t->seconds == 0 && t->nseconds == 0) {
                        TAILQ_REMOVE(&eloop->timeouts, t, next);
                        t->callback(t->arg);
index 5849b52edf499e656d59b28fb4f43862177f8bfc..2d9bbb2a00b6176c5f57303e270d5fe1af689551 100644 (file)
@@ -168,12 +168,16 @@ if_opensockets_os(struct dhcpcd_ctx *ctx)
        priv->pf_inet6_fd = -1;
 #endif
 
-#define SOCK_FLAGS     (SOCK_CLOEXEC | SOCK_NONBLOCK)
-       ctx->link_fd = xsocket(PF_ROUTE, SOCK_RAW | SOCK_FLAGS, AF_UNSPEC);
-#undef SOCK_FLAGS
+       ctx->link_fd = xsocket(PF_ROUTE, SOCK_RAW | SOCK_CXNB, AF_UNSPEC);
        if (ctx->link_fd == -1)
                return -1;
 
+#ifdef SO_RERROR
+       n = 1;
+       if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RERROR, &n,sizeof(n)) == -1)
+               goto errexit;
+#endif
+
        /* Ignore our own route(4) messages.
         * Sadly there is no way of doing this for route(4) messages
         * generated from addresses we add/delete. */
index f703d7c2b4fa555e9027c69c4189c20ad911a4e7..627be05a4398f3d688ce67925f6941b9708d956e 100644 (file)
--- a/src/if.c
+++ b/src/if.c
@@ -394,7 +394,7 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs,
        }
        TAILQ_INIT(ifs);
 
-#ifdef HAVE_CAPSICUM
+#if defined(PRIVSEP) && defined(HAVE_CAPSICUM)
        if (ctx->options & DHCPCD_PRIVSEP) {
                if (ps_root_getifaddrs(ctx, ifaddrs) == -1) {
                        logerr("ps_root_getifaddrs");
@@ -911,13 +911,6 @@ xsocket(int domain, int type, int protocol)
                goto out;
 #endif
 
-#ifdef SO_RERROR
-       /* Tell recvmsg(2) to return ENOBUFS if the receiving socket overflows. */
-       on = 1;
-       if (setsockopt(s, SOL_SOCKET, SO_RERROR, &on, sizeof(on)) == -1)
-               logerr("%s: SO_RERROR", __func__);
-#endif
-
        return s;
 
 #if !defined(HAVE_SOCK_CLOEXEC) || !defined(HAVE_SOCK_NONBLOCK)
index 73f8b91524775435b67ba9dccde120f591644ef5..23d94914c4d9db275013ad2e9df01b2202b6f154 100644 (file)
--- a/src/if.h
+++ b/src/if.h
@@ -210,6 +210,9 @@ int if_setmac(struct interface *ifp, void *, uint8_t);
 #else
 # define SOCK_NONBLOCK 0x20000000
 #endif
+#ifndef SOCK_CXNB
+#define        SOCK_CXNB       SOCK_CLOEXEC | SOCK_NONBLOCK
+#endif
 
 int if_route(unsigned char, const struct rt *rt);
 int if_initrt(struct dhcpcd_ctx *, rb_tree_t *, int);
index 7c2dc53ceb047ff30a5bb180132e0165e7de0f7e..c6a76ea3367d590cac8f149f0f09aa247f59775d 100644 (file)
@@ -565,8 +565,6 @@ ipv4_getstate(struct interface *ifp)
                        return NULL;
                }
                TAILQ_INIT(&state->addrs);
-               state->buffer_size = state->buffer_len = state->buffer_pos = 0;
-               state->buffer = NULL;
        }
        return state;
 }
@@ -963,6 +961,5 @@ ipv4_free(struct interface *ifp)
                TAILQ_REMOVE(&state->addrs, ia, next);
                free(ia);
        }
-       free(state->buffer);
        free(state);
 }
index 21f64b5f4efc83be335058b61ed019582f79aa6e..3b933a7113b0dc45b42811fd947d10ce9e16074a 100644 (file)
@@ -110,10 +110,6 @@ TAILQ_HEAD(ipv4_addrhead, ipv4_addr);
 
 struct ipv4_state {
        struct ipv4_addrhead addrs;
-
-       /* Buffer for BPF */
-       size_t buffer_size, buffer_len, buffer_pos;
-       char *buffer;
 };
 
 #define IPV4_STATE(ifp)                                                               \
index 4299425053b3ea6cb57067eac4d39e55c6613b12..9e3b62f6578823e3c104dbe354534aaa3b98acdd 100644 (file)
@@ -140,7 +140,10 @@ ipv6_init(struct dhcpcd_ctx *ctx)
 #ifndef __sun
        ctx->nd_fd = -1;
 #endif
-       ctx->dhcp6_fd = -1;
+#ifdef DHCP6
+       ctx->dhcp6_rfd = -1;
+       ctx->dhcp6_wfd = -1;
+#endif
        return 0;
 }
 
index 09595bc2c6c3b4f48664a33183b39c07604cda38..924dc9de546ce9c320e8c0995c7a72d9d2696dcd 100644 (file)
@@ -188,36 +188,45 @@ ipv6nd_printoptions(const struct dhcpcd_ctx *ctx,
        }
 }
 
-static int
-ipv6nd_open0(void)
+int
+ipv6nd_open(bool recv)
 {
        int fd, on;
        struct icmp6_filter filt;
 
-#define SOCK_FLAGS     SOCK_CLOEXEC | SOCK_NONBLOCK
-       fd = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6);
-#undef SOCK_FLAGS
+       fd = xsocket(PF_INET6, SOCK_RAW | SOCK_CXNB, IPPROTO_ICMPV6);
        if (fd == -1)
                return -1;
 
+       ICMP6_FILTER_SETBLOCKALL(&filt);
+
        /* RFC4861 4.1 */
        on = 255;
        if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
            &on, sizeof(on)) == -1)
                goto eexit;
 
-       on = 1;
-       if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
-           &on, sizeof(on)) == -1)
-               goto eexit;
+       if (recv) {
+               on = 1;
+               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;
+               on = 1;
+               if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+                   &on, sizeof(on)) == -1)
+                       goto eexit;
+
+               ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+
+#ifdef SO_RERROR
+               on = 1;
+               if (setsockopt(fd, SOL_SOCKET, SO_RERROR,
+                   &on, sizeof(on)) == -1)
+                       goto eexit;
+#endif
+       }
 
-       ICMP6_FILTER_SETBLOCKALL(&filt);
-       ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
        if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER,
            &filt, sizeof(filt)) == -1)
                goto eexit;
@@ -231,7 +240,7 @@ eexit:
 
 #ifdef __sun
 int
-ipv6nd_open(struct interface *ifp)
+ipv6nd_openif(struct interface *ifp)
 {
        int fd;
        struct ipv6_mreq mreq = {
@@ -244,7 +253,7 @@ ipv6nd_open(struct interface *ifp)
        if (state->nd_fd != -1)
                return state->nd_fd;
 
-       fd = ipv6nd_open0();
+       fd = ipv6nd_open0(true);
        if (fd == -1)
                return -1;
 
@@ -266,24 +275,6 @@ ipv6nd_open(struct interface *ifp)
        eloop_event_add(ifp->ctx->eloop, fd, ipv6nd_handledata, ifp);
        return fd;
 }
-#else
-int
-ipv6nd_open(struct dhcpcd_ctx *ctx)
-{
-       int fd;
-
-       if (ctx->nd_fd != -1)
-               return ctx->nd_fd;
-
-       fd = ipv6nd_open0();
-       if (fd == -1)
-               return -1;
-
-       ctx->nd_fd = fd;
-       if (!(IN_PRIVSEP(ctx)))
-               eloop_event_add(ctx->eloop, fd, ipv6nd_handledata, ctx);
-       return fd;
-}
 #endif
 
 static int
@@ -340,6 +331,7 @@ ipv6nd_sendrsprobe(void *arg)
        struct cmsghdr *cm;
        struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
        int s;
+       struct dhcpcd_ctx *ctx = ifp->ctx;
 
        if (ipv6_linklocal(ifp) == NULL) {
                logdebugx("%s: delaying Router Solicitation for LL address",
@@ -372,6 +364,14 @@ ipv6nd_sendrsprobe(void *arg)
 #ifdef __sun
        s = state->nd_fd;
 #else
+       if (ctx->nd_fd == -1) {
+               ctx->nd_fd = ipv6nd_open(true);
+               if (ctx->nd_fd == -1) {
+                       logerr(__func__);
+                       return;
+               }
+               eloop_event_add(ctx->eloop, ctx->nd_fd, ipv6nd_handledata, ctx);
+       }
        s = ifp->ctx->nd_fd;
 #endif
        if (sendmsg(s, &msg, 0) == -1) {
@@ -2002,20 +2002,6 @@ ipv6nd_startrs1(void *arg)
 #endif
        }
 
-       if (!(IN_PRIVSEP(ifp->ctx))) {
-#ifdef __sun
-               if (ipv6nd_open(ifp) == -1) {
-                       logerr(__func__);
-                       return;
-               }
-#else
-               if (ipv6nd_open(ifp->ctx) == -1) {
-                       logerr(__func__);
-                       return;
-               }
-#endif
-       }
-
        /* Always make a new probe as the underlying hardware
         * address could have changed. */
        ipv6nd_makersprobe(ifp);
index 8d61699996d8cf70b316a88f7b76ffff31a18329..baa59404ffa5ca9a9033861106bce474ab83b07c 100644 (file)
@@ -98,10 +98,9 @@ struct rs_state {
 #define        RETRANS_TIMER                   1000    /* milliseconds */
 #define        DELAY_FIRST_PROBE_TIME          5       /* seconds */
 
+int ipv6nd_open(bool);
 #ifdef __sun
-int ipv6nd_open(struct interface *);
-#else
-int ipv6nd_open(struct dhcpcd_ctx *);
+int ipv6nd_openif(struct interface *);
 #endif
 void ipv6nd_recvmsg(struct dhcpcd_ctx *, struct msghdr *);
 int ipv6nd_rtpref(struct ra *);
index ed8fa5b81812315df090655a6375407a95793f4a..4fb4b142b5e147a14d6b42f0ff0d2ee968c97528 100644 (file)
@@ -62,26 +62,28 @@ static void
 ps_bpf_recvbpf(void *arg)
 {
        struct ps_process *psp = arg;
-       unsigned int flags;
-       uint8_t buf[FRAMELEN_MAX];
+       struct bpf *bpf = psp->psp_bpf;
+       uint8_t buf[sizeof(bpf->bpf_flags) + FRAMELEN_MAX];
        ssize_t len;
        struct ps_msghdr psm = {
                .ps_id = psp->psp_id,
                .ps_cmd = psp->psp_id.psi_cmd,
        };
 
+       bpf->bpf_flags &= ~BPF_EOF;
        /* A BPF read can read more than one filtered packet at time.
         * This mechanism allows us to read each packet from the buffer. */
-       flags = 0;
-       while (!(flags & BPF_EOF)) {
-               len = bpf_read(&psp->psp_ifp, psp->psp_work_fd,
-                   buf, sizeof(buf), &flags);
+       while (!(bpf->bpf_flags & BPF_EOF)) {
+               len = bpf_read(bpf,
+                   buf + sizeof(bpf->bpf_flags),
+                   sizeof(buf) - sizeof(bpf->bpf_flags));
                if (len == -1)
                        logerr(__func__);
                if (len == -1 || len == 0)
                        break;
+               memcpy(buf, &bpf->bpf_flags, sizeof(bpf->bpf_flags));
                len = ps_sendpsmdata(psp->psp_ctx, psp->psp_ctx->ps_data_fd,
-                   &psm, buf, (size_t)len);
+                   &psm, buf, (size_t)len + sizeof(bpf->bpf_flags));
                if (len == -1 && errno != ECONNRESET)
                        logerr(__func__);
                if (len == -1 || len == 0)
@@ -89,61 +91,13 @@ ps_bpf_recvbpf(void *arg)
        }
 }
 
-#ifdef ARP
-#if !defined(HAVE_CAPSICUM) && !defined(HAVE_PLEDGE)
-static ssize_t
-ps_bpf_arp_addr(uint16_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);
-}
-#endif
-#endif
-
 static ssize_t
-ps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
+ps_bpf_recvmsgcb(void *arg, __unused struct ps_msghdr *psm, struct msghdr *msg)
 {
        struct ps_process *psp = arg;
        struct iovec *iov = msg->msg_iov;
 
-#ifdef ARP
-       if (psm->ps_cmd & (PS_START | PS_DELETE))
-#if !defined(HAVE_CAPSICUM) && !defined(HAVE_PLEDGE)
-               return ps_bpf_arp_addr(psm->ps_cmd, psp, msg);
-#else
-               return 0;
-#endif
-#endif
-
-       return bpf_send(&psp->psp_ifp, psp->psp_work_fd, psp->psp_proto,
+       return bpf_send(psp->psp_bpf, psp->psp_proto,
            iov->iov_base, iov->iov_len);
 }
 
@@ -162,6 +116,8 @@ ps_bpf_start_bpf(void *arg)
 {
        struct ps_process *psp = arg;
        struct dhcpcd_ctx *ctx = psp->psp_ctx;
+       char *addr;
+       struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
 #ifdef HAVE_CAPSICUM
        cap_rights_t rights;
 
@@ -170,23 +126,30 @@ ps_bpf_start_bpf(void *arg)
        cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_IOCTL);
 #endif
 
-       setproctitle("[BPF %s] %s", psp->psp_protostr, psp->psp_ifname);
-
+       if (ia->s_addr == INADDR_ANY) {
+               ia = NULL;
+               addr = NULL;
+       } else
+               addr = inet_ntoa(*ia);
+       setproctitle("[BPF %s] %s%s%s", psp->psp_protostr, psp->psp_ifname,
+           addr != NULL ? " " : "", addr != NULL ? addr : "");
        ps_freeprocesses(ctx, psp);
 
-       psp->psp_work_fd = bpf_open(&psp->psp_ifp, psp->psp_filter);
-       if (psp->psp_work_fd == -1)
+       psp->psp_bpf = bpf_open(&psp->psp_ifp, psp->psp_filter, ia);
+       if (psp->psp_bpf == NULL)
                logerr("%s: bpf_open",__func__);
 #ifdef HAVE_CAPSICUM
-       else if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 &&
+       else if (cap_rights_limit(psp->psp_bpf->bpf_fd, &rights) == -1 &&
            errno != ENOSYS)
                logerr("%s: cap_rights_limit", __func__);
 #endif
        else if (eloop_event_add(ctx->eloop,
-           psp->psp_work_fd, ps_bpf_recvbpf, psp) == -1)
+           psp->psp_bpf->bpf_fd, ps_bpf_recvbpf, psp) == -1)
                logerr("%s: eloop_event_add", __func__);
-       else
+       else {
+               psp->psp_work_fd = psp->psp_bpf->bpf_fd;
                return 0;
+       }
 
        eloop_exit(ctx->eloop, EXIT_FAILURE);
        return -1;
@@ -306,17 +269,24 @@ ps_bpf_dispatch(struct dhcpcd_ctx *ctx,
 {
        struct iovec *iov = msg->msg_iov;
        struct interface *ifp;
+       uint8_t bpf_flags, *bpf;
+       size_t bpf_len;
 
        ifp = if_findindex(ctx->ifaces, psm->ps_id.psi_ifindex);
+       bpf = iov->iov_base;
+       bpf_len = iov->iov_len;
+       memcpy(&bpf_flags, bpf, sizeof(bpf_flags));
+       bpf += sizeof(bpf_flags);
+       bpf_len -= sizeof(bpf_flags);
 
        switch (psm->ps_cmd) {
 #ifdef ARP
        case PS_BPF_ARP:
-               arp_packet(ifp, iov->iov_base, iov->iov_len);
+               arp_packet(ifp, bpf, bpf_len, bpf_flags);
                break;
 #endif
        case PS_BPF_BOOTP:
-               dhcp_packet(ifp, iov->iov_base, iov->iov_len);
+               dhcp_packet(ifp, bpf, bpf_len, bpf_flags);
                break;
        default:
                errno = ENOTSUP;
@@ -327,59 +297,48 @@ ps_bpf_dispatch(struct dhcpcd_ctx *ctx,
 }
 
 static ssize_t
-ps_bpf_send(const struct interface *ifp, uint16_t cmd,
-    const void *data, size_t len)
+ps_bpf_send(const struct interface *ifp, const struct in_addr *ia,
+    uint16_t cmd, const void *data, size_t len)
 {
        struct dhcpcd_ctx *ctx = ifp->ctx;
        struct ps_msghdr psm = {
                .ps_cmd = cmd,
                .ps_id = {
                        .psi_ifindex = ifp->index,
-                       .psi_cmd = (uint8_t)(cmd &
-                           ~(PS_START | PS_STOP | PS_DELETE)),
+                       .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
                },
        };
 
-       if (psm.ps_id.psi_cmd == PS_BPF_ARP_ADDR)
-               psm.ps_id.psi_cmd = PS_BPF_ARP;
+       if (ia != NULL)
+               psm.ps_id.psi_addr.psa_in_addr = *ia;
 
        return ps_sendpsmdata(ctx, ctx->ps_root_fd, &psm, data, len);
 }
 
 #ifdef ARP
 ssize_t
-ps_bpf_openarp(const struct interface *ifp)
-{
-
-       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)
+ps_bpf_openarp(const struct interface *ifp, const struct in_addr *ia)
 {
 
-       return ps_bpf_send(ifp, PS_BPF_ARP_ADDR | PS_START, addr, sizeof(*addr));
+       assert(ia != NULL);
+       return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_START,
+           ifp, sizeof(*ifp));
 }
 
 ssize_t
-ps_bpf_deladdr(const struct interface *ifp, const struct in_addr *addr)
+ps_bpf_closearp(const struct interface *ifp, const struct in_addr *ia)
 {
 
-       return ps_bpf_send(ifp, PS_BPF_ARP_ADDR | PS_DELETE, addr, sizeof(*addr));
+       return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_STOP, NULL, 0);
 }
 
 ssize_t
-ps_bpf_closearp(const struct interface *ifp)
-{
-
-       return ps_bpf_send(ifp, PS_BPF_ARP | PS_STOP, NULL, 0);
-}
-
-ssize_t
-ps_bpf_sendarp(const struct interface *ifp, const void *data, size_t len)
+ps_bpf_sendarp(const struct interface *ifp, const struct in_addr *ia,
+    const void *data, size_t len)
 {
 
-       return ps_bpf_send(ifp, PS_BPF_ARP, data, len);
+       assert(ia != NULL);
+       return ps_bpf_send(ifp, ia, PS_BPF_ARP, data, len);
 }
 #endif
 
@@ -387,19 +346,20 @@ ssize_t
 ps_bpf_openbootp(const struct interface *ifp)
 {
 
-       return ps_bpf_send(ifp, PS_BPF_BOOTP | PS_START, ifp, sizeof(*ifp));
+       return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_START,
+           ifp, sizeof(*ifp));
 }
 
 ssize_t
 ps_bpf_closebootp(const struct interface *ifp)
 {
 
-       return ps_bpf_send(ifp, PS_BPF_BOOTP | PS_STOP, NULL, 0);
+       return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_STOP, NULL, 0);
 }
 
 ssize_t
 ps_bpf_sendbootp(const struct interface *ifp, const void *data, size_t len)
 {
 
-       return ps_bpf_send(ifp, PS_BPF_BOOTP, data, len);
+       return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP, data, len);
 }
index b03900f5b15451c231b726d1ceda0e21daa0d570..a9960490ac03b977b9c32ddbcadf3685d3826658 100644 (file)
@@ -35,11 +35,10 @@ ssize_t ps_bpf_dispatch(struct dhcpcd_ctx *,
     struct ps_msghdr *, struct msghdr *);
 
 #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);
+ssize_t ps_bpf_openarp(const struct interface *, const struct in_addr *);
+ssize_t ps_bpf_closearp(const struct interface *, const struct in_addr *);
+ssize_t ps_bpf_sendarp(const struct interface *, const struct in_addr *,
+    const void *, size_t);
 #endif
 
 ssize_t ps_bpf_openbootp(const struct interface *);
index 74cdf722520bfd39ec3ca0dec0951d810ae6ec65..ee2be9d99726fadd48acf04c26d25c1c34f5cf02 100644 (file)
@@ -55,7 +55,7 @@ ps_inet_recvbootp(void *arg)
 {
        struct dhcpcd_ctx *ctx = arg;
 
-       if (ps_recvmsg(ctx, ctx->udp_fd, PS_BOOTP, ctx->ps_inet_fd) == -1)
+       if (ps_recvmsg(ctx, ctx->udp_rfd, PS_BOOTP, ctx->ps_inet_fd) == -1)
                logerr(__func__);
 }
 #endif
@@ -86,7 +86,7 @@ ps_inet_recvdhcp6(void *arg)
 {
        struct dhcpcd_ctx *ctx = arg;
 
-       if (ps_recvmsg(ctx, ctx->dhcp6_fd, PS_DHCP6, ctx->ps_inet_fd) == -1)
+       if (ps_recvmsg(ctx, ctx->dhcp6_rfd, PS_DHCP6, ctx->ps_inet_fd) == -1)
                logerr(__func__);
 }
 #endif
@@ -99,7 +99,7 @@ ps_inet_startcb(void *arg)
 #ifdef HAVE_CAPSICUM
        cap_rights_t rights;
 
-       cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+       cap_rights_init(&rights, CAP_RECV, CAP_EVENT);
 #endif
 
        if (ctx->options & DHCPCD_MASTER)
@@ -120,31 +120,32 @@ ps_inet_startcb(void *arg)
        if ((ctx->options & (DHCPCD_IPV4 | DHCPCD_MASTER)) ==
            (DHCPCD_IPV4 | DHCPCD_MASTER))
        {
-               ctx->udp_fd = dhcp_openudp(NULL);
-               if (ctx->udp_fd == -1)
+               ctx->udp_rfd = dhcp_openudp(NULL);
+               if (ctx->udp_rfd == -1)
                        logerr("%s: dhcp_open", __func__);
 #ifdef HAVE_CAPSICUM
-               else if (cap_rights_limit(ctx->udp_fd, &rights) == -1
+               else if (cap_rights_limit(ctx->udp_rfd, &rights) == -1
                    && errno != ENOSYS)
                {
                        logerr("%s: cap_rights_limit", __func__);
-                       close(ctx->udp_fd);
-                       ctx->udp_fd = -1;
+                       close(ctx->udp_rfd);
+                       ctx->udp_rfd = -1;
                }
 #endif
-               else if (eloop_event_add(ctx->eloop, ctx->udp_fd,
+               else if (eloop_event_add(ctx->eloop, ctx->udp_rfd,
                    ps_inet_recvbootp, ctx) == -1)
                {
                        logerr("%s: eloop_event_add DHCP", __func__);
-                       close(ctx->udp_fd);
-                       ctx->udp_fd = -1;
+                       close(ctx->udp_rfd);
+                       ctx->udp_rfd = -1;
                } else
                        ret++;
        }
 #endif
 #if defined(INET6) && !defined(__sun)
        if (ctx->options & DHCPCD_IPV6) {
-               if (ipv6nd_open(ctx) == -1)
+               ctx->nd_fd = ipv6nd_open(true);
+               if (ctx->nd_fd == -1)
                        logerr("%s: ipv6nd_open", __func__);
 #ifdef HAVE_CAPSICUM
                else if (cap_rights_limit(ctx->nd_fd, &rights) == -1
@@ -169,24 +170,24 @@ ps_inet_startcb(void *arg)
        if ((ctx->options & (DHCPCD_DHCP6 | DHCPCD_MASTER)) ==
            (DHCPCD_DHCP6 | DHCPCD_MASTER))
        {
-               ctx->dhcp6_fd = dhcp6_openudp(0, NULL);
-               if (ctx->dhcp6_fd == -1)
+               ctx->dhcp6_rfd = dhcp6_openudp(0, NULL);
+               if (ctx->dhcp6_rfd == -1)
                        logerr("%s: dhcp6_open", __func__);
 #ifdef HAVE_CAPSICUM
-               else if (cap_rights_limit(ctx->dhcp6_fd, &rights) == -1
+               else if (cap_rights_limit(ctx->dhcp6_rfd, &rights) == -1
                    && errno != ENOSYS)
                {
                        logerr("%s: cap_rights_limit", __func__);
-                       close(ctx->dhcp6_fd);
-                       ctx->dhcp6_fd = -1;
+                       close(ctx->dhcp6_rfd);
+                       ctx->dhcp6_rfd = -1;
                }
 #endif
-               else if (eloop_event_add(ctx->eloop, ctx->dhcp6_fd,
+               else if (eloop_event_add(ctx->eloop, ctx->dhcp6_rfd,
                    ps_inet_recvdhcp6, ctx) == -1)
                {
                        logerr("%s: eloop_event_add DHCP6", __func__);
-                       close(ctx->dhcp6_fd);
-                       ctx->dhcp6_fd = -1;
+                       close(ctx->dhcp6_rfd);
+                       ctx->dhcp6_rfd = -1;
                } else
                        ret++;
        }
@@ -200,23 +201,22 @@ ps_inet_startcb(void *arg)
 }
 
 static ssize_t
-ps_inet_recvmsg_cb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
+ps_inet_sendmsg(struct dhcpcd_ctx *ctx,
+    struct ps_msghdr *psm, struct msghdr *msg)
 {
-       struct dhcpcd_ctx *ctx = arg;
        struct ps_process *psp;
        int s;
 
        psp = ps_findprocess(ctx, &psm->ps_id);
        if (psp != NULL) {
                s = psp->psp_work_fd;
-               logerrx("psp found fd %d", s);
                goto dosend;
        }
 
        switch (psm->ps_cmd) {
 #ifdef INET
        case PS_BOOTP:
-               s = ctx->udp_fd;
+               s = ctx->udp_wfd;
                break;
 #endif
 #if defined(INET6) && !defined(__sun)
@@ -226,7 +226,7 @@ ps_inet_recvmsg_cb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
 #endif
 #ifdef DHCP6
        case PS_DHCP6:
-               s = ctx->dhcp6_fd;
+               s = ctx->dhcp6_wfd;
                break;
 #endif
        default:
@@ -235,17 +235,16 @@ ps_inet_recvmsg_cb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
        }
 
 dosend:
-
        return sendmsg(s, msg, 0);
 }
 
-/* Receive from state engine, send message on wire. */
 static void
 ps_inet_recvmsg(void *arg)
 {
        struct dhcpcd_ctx *ctx = arg;
 
-       if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, ps_inet_recvmsg_cb, ctx) == -1)
+       /* Receive shutdown */
+       if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, NULL, NULL) == -1)
                logerr(__func__);
 }
 
@@ -310,11 +309,9 @@ ps_inet_start(struct dhcpcd_ctx *ctx)
            PSF_DROPPRIVS);
 
 #ifdef HAVE_CAPSICUM
-#if 0          /* This breaks sendmsg() */
-       if (cap_enter() == -1 && errno != ENOSYS)
+       if (pid == 0 && cap_enter() == -1 && errno != ENOSYS)
                logerr("%s: cap_enter", __func__);
 #endif
-#endif
 #ifdef HAVE_PLEDGE
        if (pid == 0 && pledge("stdio inet", NULL) == -1)
                logerr("%s: pledge", __func__);
@@ -350,7 +347,7 @@ ps_inet_listenin(void *arg)
 #ifdef HAVE_CAPSICUM
        cap_rights_t rights;
 
-       cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+       cap_rights_init(&rights, CAP_RECV, CAP_EVENT);
 #endif
 
        inet_ntop(AF_INET, ia, buf, sizeof(buf));
@@ -401,7 +398,7 @@ ps_inet_listennd(void *arg)
 #ifdef HAVE_CAPSICUM
        cap_rights_t rights;
 
-       cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+       cap_rights_init(&rights, CAP_RECV, CAP_EVENT);
 #endif
 
        setproctitle("[ND network proxy]");
@@ -453,7 +450,7 @@ ps_inet_listenin6(void *arg)
 #ifdef HAVE_CAPSICUM
        cap_rights_t rights;
 
-       cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+       cap_rights_init(&rights, CAP_RECV, CAP_EVENT);
 #endif
 
        inet_ntop(AF_INET6, ia, buf, sizeof(buf));
@@ -486,28 +483,18 @@ ps_inet_listenin6(void *arg)
 }
 #endif
 
-static ssize_t
-ps_inet_recvmsgpsp_cb(void *arg, __unused struct ps_msghdr *psm,
-    struct msghdr *msg)
-{
-       struct ps_process *psp = arg;
-
-       return sendmsg(psp->psp_work_fd, msg, 0);
-}
-
 static void
 ps_inet_recvmsgpsp(void *arg)
 {
        struct ps_process *psp = arg;
 
-       if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd,
-           ps_inet_recvmsgpsp_cb, psp) == -1)
+       /* Receive shutdown. */
+       if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, NULL, NULL) == -1)
                logerr(__func__);
 }
 
 ssize_t
-ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm,
-    __unused struct msghdr *msg)
+ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
 {
        uint16_t cmd;
        struct ps_process *psp;
@@ -515,6 +502,9 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm,
        pid_t start;
 
        cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
+       if (cmd == psm->ps_cmd)
+               return ps_inet_sendmsg(ctx, psm, msg);
+
        psp = ps_findprocess(ctx, &psm->ps_id);
 
 #ifdef PRIVSEP_DEBUG
@@ -573,11 +563,9 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm,
                return -1;
        case 0:
 #ifdef HAVE_CAPSICUM
-#if 0          /* This breaks sendmsg() */
                if (cap_enter() == -1 && errno != ENOSYS)
                        logerr("%s: cap_enter", __func__);
 #endif
-#endif
 #ifdef HAVE_PLEDGE
                if (pledge("stdio inet", NULL) == -1)
                        logerr("%s: pledge", __func__);
@@ -622,13 +610,10 @@ ps_inet_closebootp(struct ipv4_addr *ia)
 }
 
 ssize_t
-ps_inet_sendbootp(struct ipv4_addr *ia, const struct msghdr *msg)
+ps_inet_sendbootp(struct interface *ifp, const struct msghdr *msg)
 {
-       struct dhcpcd_ctx *ctx = ia->iface->ctx;
 
-       if (ctx->options & DHCPCD_MASTER)
-               return ps_sendmsg(ctx, ctx->ps_inet_fd, PS_BOOTP, 0, msg);
-       return ps_inet_in_docmd(ia, PS_BOOTP, msg);
+       return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_BOOTP, 0, msg);
 }
 #endif /* INET */
 
@@ -674,7 +659,7 @@ ssize_t
 ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg)
 {
 
-       return ps_sendmsg(ifp->ctx, ifp->ctx->ps_inet_fd, PS_ND, 0, msg);
+       return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_ND, 0, msg);
 }
 #endif
 
@@ -710,13 +695,10 @@ ps_inet_closedhcp6(struct ipv6_addr *ia)
 }
 
 ssize_t
-ps_inet_senddhcp6(struct ipv6_addr *ia, const struct msghdr *msg)
+ps_inet_senddhcp6(struct interface *ifp, const struct msghdr *msg)
 {
-       struct dhcpcd_ctx *ctx = ia->iface->ctx;
 
-       if (ctx->options & DHCPCD_MASTER)
-               return ps_sendmsg(ctx, ctx->ps_inet_fd, PS_DHCP6, 0, msg);
-       return ps_inet_in6_docmd(ia, PS_DHCP6, msg);
+       return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_DHCP6, 0, msg);
 }
 #endif /* DHCP6 */
 #endif /* INET6 */
index 01933a728b7979e5d9bfd5b2bc4bc52b2cc32564..7aa0b3cb65f30c6977a31a8830b58022b6997e35 100644 (file)
@@ -38,7 +38,7 @@ ssize_t ps_inet_dispatch(void *, struct ps_msghdr *, struct msghdr *);
 struct ipv4_addr;
 ssize_t ps_inet_openbootp(struct ipv4_addr *);
 ssize_t ps_inet_closebootp(struct ipv4_addr *);
-ssize_t ps_inet_sendbootp(struct ipv4_addr *, const struct msghdr *);
+ssize_t ps_inet_sendbootp(struct interface *, const struct msghdr *);
 #endif
 
 #ifdef INET6
@@ -51,7 +51,7 @@ ssize_t ps_inet_sendnd(struct interface *, const struct msghdr *);
 #ifdef DHCP6
 ssize_t ps_inet_opendhcp6(struct ipv6_addr *);
 ssize_t ps_inet_closedhcp6(struct ipv6_addr *);
-ssize_t ps_inet_senddhcp6(struct ipv6_addr *, const struct msghdr *);
+ssize_t ps_inet_senddhcp6(struct interface *, const struct msghdr *);
 #endif /* DHCP6 */
 #endif /* INET6 */
 #endif
index a449d7e39112cde1366fbdf0c3a2af7f3bf7e0c7..69b9e3716eb2bcdb18c9b275c376b739ea276e92 100644 (file)
 #include <fcntl.h>
 #include <pwd.h>
 #include <signal.h>
+#include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "common.h"
 #include "dhcpcd.h"
+#include "dhcp6.h"
 #include "eloop.h"
 #include "if.h"
+#include "ipv6nd.h"
 #include "logerr.h"
 #include "privsep.h"
 #include "sa.h"
@@ -393,16 +396,14 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
        ssize_t err;
        bool free_rdata= false;
 
-       cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP | PS_DELETE));
+       cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
        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) || cmd == PS_BPF_ARP_ADDR) &&
-           psp != NULL)
-       {
+       if (psp != NULL) {
                if (psm->ps_cmd & PS_STOP) {
                        int ret = ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
 
@@ -412,7 +413,7 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
                return ps_sendpsmmsg(ctx, psp->psp_fd, psm, msg);
        }
 
-       if (psm->ps_cmd & (PS_STOP | PS_DELETE) && psp == NULL)
+       if (psm->ps_cmd & PS_STOP && psp == NULL)
                return 0;
 
        /* All these should just be PS_START */
@@ -517,6 +518,27 @@ ps_root_startcb(void *arg)
                    ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
        ctx->ps_root_pid = getpid();
        ctx->options |= DHCPCD_PRIVSEPROOT;
+
+       /* Open network sockets for sending.
+        * This is a small bit wasteful for non sandboxed OS's
+        * but makes life very easy for unicasting DHCPv6 in non master
+        * mode as we no longer care about address selection. */
+#ifdef INET
+       ctx->udp_wfd = xsocket(PF_INET, SOCK_RAW | SOCK_CXNB, IPPROTO_UDP);
+       if (ctx->udp_wfd == -1)
+               return -1;
+#endif
+#ifdef INET6
+       ctx->nd_fd = ipv6nd_open(false);
+       if (ctx->nd_fd == -1)
+               return -1;
+#endif
+#ifdef DHCP6
+       ctx->dhcp6_wfd = dhcp6_openraw();
+       if (ctx->dhcp6_wfd == -1)
+               return -1;
+#endif
+
        return 0;
 }
 
@@ -567,7 +589,6 @@ ps_root_start(struct dhcpcd_ctx *ctx)
        int fd[2];
        pid_t pid;
 
-#define        SOCK_CXNB       SOCK_CLOEXEC | SOCK_NONBLOCK
        if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1)
                return -1;
 
@@ -586,20 +607,16 @@ ps_root_start(struct dhcpcd_ctx *ctx)
        close(fd[1]);
        if (eloop_event_add(ctx->eloop, ctx->ps_data_fd,
            ps_root_dispatch, ctx) == -1)
-               logerr(__func__);
+               return -1;
 
-       if ((ctx->ps_eloop = eloop_new()) == NULL) {
-               logerr(__func__);
+       if ((ctx->ps_eloop = eloop_new()) == NULL)
                return -1;
-       }
 
        if (eloop_signal_set_cb(ctx->ps_eloop,
            dhcpcd_signals, dhcpcd_signals_len,
            ps_root_readerrorsig, ctx) == -1)
-       {
-               logerr(__func__);
                return -1;
-       }
+
        return pid;
 }
 
index 621aed28e777b7cf1831701b0b54ee9fb94af66f..6d71f81dff2344672fb9a664bbfaa67dc71b0cac 100644 (file)
@@ -322,6 +322,7 @@ ps_start(struct dhcpcd_ctx *ctx)
 
        switch (pid = ps_root_start(ctx)) {
        case -1:
+               logerr("ps_root_start");
                return -1;
        case 0:
                return 0;
@@ -378,14 +379,6 @@ ps_stop(struct dhcpcd_ctx *ctx)
 void
 ps_freeprocess(struct ps_process *psp)
 {
-#ifdef INET
-       struct ipv4_state *istate = IPV4_STATE(&psp->psp_ifp);
-
-       if (istate != NULL) {
-               free(istate->buffer);
-               free(istate);
-       }
-#endif
 
        TAILQ_REMOVE(&psp->psp_ctx->ps_processes, psp, next);
        if (psp->psp_fd != -1) {
@@ -396,6 +389,10 @@ ps_freeprocess(struct ps_process *psp)
                eloop_event_delete(psp->psp_ctx->eloop, psp->psp_work_fd);
                close(psp->psp_work_fd);
        }
+#ifdef INET
+       if (psp->psp_bpf != NULL)
+               bpf_close(psp->psp_bpf);
+#endif
        free(psp);
 }
 
@@ -460,21 +457,20 @@ ssize_t
 ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd,
     struct ps_msghdr *psm, const struct msghdr *msg)
 {
-       assert(msg == NULL || msg->msg_iovlen == 1);
-
        struct iovec iov[] = {
                { .iov_base = UNCONST(psm), .iov_len = sizeof(*psm) },
                { .iov_base = NULL, },  /* name */
                { .iov_base = NULL, },  /* control */
-               { .iov_base = NULL, },  /* payload */
+               { .iov_base = NULL, },  /* payload 1 */
+               { .iov_base = NULL, },  /* payload 2 */
+               { .iov_base = NULL, },  /* payload 3 */
        };
-       int iovlen = __arraycount(iov);
+       int iovlen;
        ssize_t len;
 
        if (msg != NULL) {
                struct iovec *iovp = &iov[1];
-
-               assert(msg->msg_iovlen == 1);
+               size_t i;
 
                psm->ps_namelen = msg->msg_namelen;
                psm->ps_controllen = (socklen_t)msg->msg_controllen;
@@ -484,10 +480,18 @@ ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd,
                iovp++;
                iovp->iov_base = msg->msg_control;
                iovp->iov_len = msg->msg_controllen;
-               iovp++;
-               iovp->iov_base = msg->msg_iov[0].iov_base;
-               iovp->iov_len = msg->msg_iov[0].iov_len;
-               iovlen = __arraycount(iov);
+               iovlen = 3;
+
+               for (i = 0; i < (size_t)msg->msg_iovlen; i++) {
+                       if ((size_t)iovlen + i > __arraycount(iov)) {
+                               errno = ENOBUFS;
+                               return -1;
+                       }
+                       iovp++;
+                       iovp->iov_base = msg->msg_iov[i].iov_base;
+                       iovp->iov_len = msg->msg_iov[i].iov_len;
+               }
+               iovlen += i;
        } else
                iovlen = 1;
 
@@ -519,15 +523,16 @@ ssize_t
 ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
     const struct msghdr *msg)
 {
-       assert(msg->msg_iovlen == 1);
-
        struct ps_msghdr psm = {
                .ps_cmd = cmd,
                .ps_flags = flags,
                .ps_namelen = msg->msg_namelen,
                .ps_controllen = (socklen_t)msg->msg_controllen,
-               .ps_datalen = msg->msg_iov[0].iov_len,
        };
+       size_t i;
+
+       for (i = 0; i < (size_t)msg->msg_iovlen; i++)
+               psm.ps_datalen += msg->msg_iov[i].iov_len;
 
 #if 0  /* For debugging structure padding. */
        logerrx("psa.family %lu %zu", offsetof(struct ps_addr, psa_family), sizeof(psm.ps_id.psi_addr.psa_family));
@@ -699,6 +704,9 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd,
        if (ps_unrollmsg(&msg, &psm.psm_hdr, psm.psm_data, dlen) == -1)
                return -1;
 
+       if (callback == NULL)
+               return 0;
+
        errno = 0;
        return callback(cbctx, &psm.psm_hdr, &msg);
 }
index 7fb5ca9b590ef8f11ee7a85ade40fdef504b6110..b98813e577725439fd935da261181cb6a9fa9fe3 100644 (file)
@@ -40,7 +40,6 @@
 #define        PS_DHCP6                0x0003
 #define        PS_BPF_BOOTP            0x0004
 #define        PS_BPF_ARP              0x0005
-#define        PS_BPF_ARP_ADDR         0x0006
 
 /* Generic commands */
 #define        PS_IOCTL                0x0010
@@ -62,7 +61,6 @@
 #define        PS_WRITEPATHUINT        0x0201
 
 /* Process commands */
-#define        PS_DELETE               0x2000
 #define        PS_START                0x4000
 #define        PS_STOP                 0x8000
 
@@ -119,6 +117,7 @@ struct ps_msg {
        uint8_t psm_data[PS_BUFLEN];
 };
 
+struct bpf;
 struct ps_process {
        TAILQ_ENTRY(ps_process) next;
        struct dhcpcd_ctx *psp_ctx;
@@ -132,8 +131,9 @@ struct ps_process {
        const char *psp_protostr;
 
 #ifdef INET
-       int (*psp_filter)(struct interface *, int);
+       int (*psp_filter)(const struct bpf *, const struct in_addr *);
        struct interface psp_ifp; /* Move BPF gubbins elsewhere */
+       struct bpf *psp_bpf;
 #endif
 };
 TAILQ_HEAD(ps_process_head, ps_process);