]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Rename if_*raw functions to bpf_* so it's more descriptive and move
authorRoy Marples <roy@marples.name>
Wed, 8 Mar 2017 08:41:07 +0000 (08:41 +0000)
committerRoy Marples <roy@marples.name>
Wed, 8 Mar 2017 08:41:07 +0000 (08:41 +0000)
them from if-bsd.c to bpf.c so they can be re-used on SunOS in the future.
Linux is a special snowflake, so the BPF functions for opening, reading and
attaching remain in if-linux.c.
On Linux, open PF_PACKET as SOCK_RAW so we see the datalink frame header
just as BPF elsewhere so we don't have to mess around with offsets.

The BPF filters themselves have been improved to filter out more gunk such
as ensuring BOOTP messages are in an IPv4 packet and from the BOOTPS port.
ARP messages now check protocol family as well.

More improvements to T101.

arp.c
bpf.c
bpf.h [new file with mode: 0644]
dhcp.c
dhcp.h
if-bsd.c
if-linux.c
if.h
ipv4.c
ipv4.h

diff --git a/arp.c b/arp.c
index 716a72313de647d981fc80bd694638eb75feb757..9c0e3adee0355404fb33eebad1ab58813005d017 100644 (file)
--- a/arp.c
+++ b/arp.c
@@ -43,7 +43,7 @@
 #define ELOOP_QUEUE 5
 #include "config.h"
 #include "arp.h"
-#include "if.h"
+#include "bpf.h"
 #include "ipv4.h"
 #include "common.h"
 #include "dhcpcd.h"
@@ -98,7 +98,7 @@ arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip)
        APPEND(&tip, sizeof(tip));
 
        state = ARP_CSTATE(ifp);
-       return if_sendraw(ifp, state->fd, ETHERTYPE_ARP, arp_buffer, len);
+       return bpf_send(ifp, state->fd, ETHERTYPE_ARP, arp_buffer, len);
 
 eexit:
        errno = ENOBUFS;
@@ -119,11 +119,12 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len)
        if (len < sizeof(ar))
                return;
        memcpy(&ar, data, sizeof(ar));
+
+       /* These checks are enforced in the BPF filter. */
+#if 0
        /* Families must match */
        if (ar.ar_hrd != htons(ifp->family))
                return;
-#if 0
-       /* These checks are enforced in the BPF filter. */
        /* Protocol must be IP. */
        if (ar.ar_pro != htons(ETHERTYPE_IP))
                continue;
@@ -131,9 +132,10 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len)
        if (ar.ar_op != htons(ARPOP_REPLY) &&
            ar.ar_op != htons(ARPOP_REQUEST))
                continue;
-#endif
+       /* Protocol length must match in_addr_t */
        if (ar.ar_pln != sizeof(arm.sip.s_addr))
                return;
+#endif
 
        /* Get pointers to the hardware addresses */
        hw_s = data + sizeof(ar);
@@ -190,7 +192,7 @@ arp_close(struct interface *ifp)
 
        if ((state = ARP_STATE(ifp)) != NULL && state->fd != -1) {
                eloop_event_delete(ifp->ctx->eloop, state->fd);
-               if_closeraw(ifp, state->fd);
+               bpf_close(state->fd);
                state->fd = -1;
        }
 }
@@ -209,11 +211,11 @@ arp_read(void *arg)
         * so we have to process the entire buffer. */
        state = ARP_CSTATE(ifp);
        flags = 0;
-       while (!(flags & RAW_EOF)) {
-               bytes = if_readraw(ifp, state->fd, buf, sizeof(buf), &flags);
+       while (!(flags & BPF_EOF)) {
+               bytes = bpf_read(ifp, state->fd, buf, sizeof(buf), &flags);
                if (bytes == -1) {
                        logger(ifp->ctx, LOG_ERR,
-                           "%s: arp if_readrawpacket: %m", ifp->name);
+                           "%s: arp bpf_read: %m", ifp->name);
                        arp_close(ifp);
                        return;
                }
@@ -228,7 +230,7 @@ arp_open(struct interface *ifp)
 
        state = ARP_STATE(ifp);
        if (state->fd == -1) {
-               state->fd = if_openraw(ifp, ETHERTYPE_ARP, bpf_arp);
+               state->fd = bpf_open(ifp, bpf_arp);
                if (state->fd == -1) {
                        logger(ifp->ctx, LOG_ERR, "%s: %s: %m",
                            __func__, ifp->name);
diff --git a/bpf.c b/bpf.c
index 4ab7c9648564362b0c27721b54c462453ee88b2b..1de88739f3e52094060c772992af331b3be06847 100644 (file)
--- a/bpf.c
+++ b/bpf.c
@@ -1,5 +1,5 @@
 /*
- * dhcpcd: BPF arp and bootp functions
+ * dhcpcd: BPF arp and bootp filtering
  * Copyright (c) 2006-2017 Roy Marples <roy@marples.name>
  * All rights reserved
 
@@ -25,6 +25,8 @@
  * SUCH DAMAGE.
  */
 
+#include <sys/ioctl.h>
+
 #include <arpa/inet.h>
 
 #include <net/if.h>
 #include <netinet/if_ether.h>
 
 #ifdef __linux__
+/* Special BPF snowflake. */
 #include <linux/filter.h>
+#define        bpf_insn                sock_filter
 #else
 #include <net/bpf.h>
 #endif
 
 #include <errno.h>
+#include <fcntl.h>
 #include <stddef.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include "common.h"
 #include "arp.h"
+#include "bpf.h"
 #include "dhcp.h"
 #include "if.h"
 
 
 /* BPF helper macros */
 #ifdef __linux__
-#define        BPF_L2L                 0
-#define        BPF_L2I                 0
-#define        BPF_WHOLEPACKET         0x0fffffff /* work around buggy LPF filters */
+#define        BPF_WHOLEPACKET         0x7fffffff /* work around buggy LPF filters */
 #else
-#define        BPF_L2L                 ETHER_ADDR_LEN + ETHER_ADDR_LEN + 2
-#define        BPF_L2I                 3
 #define        BPF_WHOLEPACKET         ~0U
 #endif
 
        (insn)->k = (uint32_t)(v);                              \
 };
 
+size_t
+bpf_frame_header_len(const struct interface *ifp)
+{
+
+       switch(ifp->family) {
+       case ARPHRD_ETHER:
+               return sizeof(struct ether_header);
+       default:
+               return 0;
+       }
+}
+
+#ifndef __linux__
+/* Linux is a special snowflake for opening, attaching and reading BPF.
+ * See if-linux.c for the Linux specific BPF functions. */
+
+const char *bpf_name = "Berkley Packet Filter";
+
+int
+bpf_open(struct interface *ifp, int (*filter)(struct interface *, int))
+{
+       struct ipv4_state *state;
+       int fd = -1;
+       struct ifreq ifr;
+       int ibuf_len = 0;
+       size_t buf_len;
+       struct bpf_version pv;
+#ifdef BIOCIMMEDIATE
+       int flags;
+#endif
+#ifndef O_CLOEXEC
+       int fd_opts;
+#endif
+
+#ifdef _PATH_BPF
+       fd = open(_PATH_BPF, O_RDWR | O_NONBLOCK
+#ifdef O_CLOEXEC
+               | O_CLOEXEC
+#endif
+       );
+#else
+       char device[32];
+       int n = 0;
+
+       do {
+               snprintf(device, sizeof(device), "/dev/bpf%d", n++);
+               fd = open(device, O_RDWR | O_NONBLOCK
+#ifdef O_CLOEXEC
+                               | O_CLOEXEC
+#endif
+               );
+       } while (fd == -1 && errno == EBUSY);
+#endif
+
+       if (fd == -1)
+               return -1;
+
+#ifndef O_CLOEXEC
+       if ((fd_opts = fcntl(fd, F_GETFD)) == -1 ||
+           fcntl(fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1) {
+               close(fd);
+               return -1;
+       }
+#endif
+
+       memset(&pv, 0, sizeof(pv));
+       if (ioctl(fd, BIOCVERSION, &pv) == -1)
+               goto eexit;
+       if (pv.bv_major != BPF_MAJOR_VERSION ||
+           pv.bv_minor < BPF_MINOR_VERSION) {
+               logger(ifp->ctx, LOG_ERR, "BPF version mismatch - recompile");
+               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)
+               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_STATE(ifp);
+       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;
+               state->buffer_len = state->buffer_pos = 0;
+       }
+
+#ifdef BIOCIMMEDIATE
+       flags = 1;
+       if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1)
+               goto eexit;
+#endif
+
+       return fd;
+
+eexit:
+       close(fd);
+       return -1;
+}
+
+/* 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, int *flags)
+{
+       ssize_t fl = (ssize_t)bpf_frame_header_len(ifp);
+       ssize_t bytes;
+       struct ipv4_state *state = IPV4_STATE(ifp);
+
+       struct bpf_hdr packet;
+       const char *payload;
+
+       *flags = 0;
+       for (;;) {
+               if (state->buffer_len == 0) {
+                       bytes = read(fd, state->buffer, state->buffer_size);
+                       if (bytes == -1 || bytes == 0)
+                               return bytes;
+                       state->buffer_len = (size_t)bytes;
+                       state->buffer_pos = 0;
+               }
+               bytes = -1;
+               memcpy(&packet, state->buffer + state->buffer_pos,
+                   sizeof(packet));
+               if (packet.bh_caplen != packet.bh_datalen)
+                       goto next; /* Incomplete packet, drop. */
+               if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
+                   state->buffer_len)
+                       goto next; /* Packet beyond buffer, drop. */
+               payload = state->buffer + state->buffer_pos +
+                   packet.bh_hdrlen + fl;
+               bytes = (ssize_t)packet.bh_caplen - fl;
+               if ((size_t)bytes > len)
+                       bytes = (ssize_t)len;
+               memcpy(data, payload, (size_t)bytes);
+next:
+               state->buffer_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 (bytes != -1)
+                       return bytes;
+       }
+
+       return bytes;
+}
+
+int
+bpf_attach(int fd, void *filter, unsigned int filter_len)
+{
+       struct bpf_program pf;
+
+       /* Install the filter. */
+       memset(&pf, 0, sizeof(pf));
+       pf.bf_insns = filter;
+       pf.bf_len = filter_len;
+       return ioctl(fd, BIOCSETF, &pf);
+}
+#endif
+
+ssize_t
+bpf_send(const struct interface *ifp, int fd, uint16_t protocol,
+    const void *data, size_t len)
+{
+       struct iovec iov[2];
+       struct ether_header eh;
+
+       switch(ifp->family) {
+       case ARPHRD_ETHER:
+               memset(&eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
+               memcpy(&eh.ether_shost, ifp->hwaddr, sizeof(eh.ether_shost));
+               eh.ether_type = htons(protocol);
+               iov[0].iov_base = &eh;
+               iov[0].iov_len = sizeof(eh);
+               break;
+       default:
+               iov[0].iov_base = NULL;
+               iov[0].iov_len = 0;
+               break;
+       }
+       iov[1].iov_base = UNCONST(data);
+       iov[1].iov_len = len;
+       return writev(fd, iov, 2);
+}
+
 static unsigned int
 bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off,
     bool equal, uint8_t *hwaddr, size_t hwaddr_len)
@@ -120,23 +320,20 @@ bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off,
                if (hwaddr_len >= 4) {
                        maclen = sizeof(mac32);
                        memcpy(&mac32, hwaddr, maclen);
-                       BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_ABS,
-                                    BPF_L2L + off);
+                       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_ABS,
-                                    BPF_L2L + off);
+                       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_ABS,
-                                    BPF_L2L + off);
+                       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);
@@ -157,64 +354,81 @@ bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off,
 }
 
 #ifdef ARP
-static const struct bpf_insn arp_bpf_filter [] = {
+
+static const struct bpf_insn bpf_arp_ether [] = {
        /* Ensure packet is at least correct size. */
        BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),
-       BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K,
-                BPF_L2L + sizeof(struct arphdr)
-                + (ETHER_ADDR_LEN * 2)
-                + (sizeof(in_addr_t) * 2), 1, 0),
+       BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0),
        BPF_STMT(BPF_RET + BPF_K, 0),
-#if BPF_L2L > 0
-       /* Make sure this is an ARP packet. */
+
+       /* Check this is an ARP packet. */
        BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
                 offsetof(struct ether_header, ether_type)),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 1, 0),
        BPF_STMT(BPF_RET + BPF_K, 0),
-#endif
+
+       /* Load frame header length into X */
+       BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
+
+       /* Make sure the hardware family matches. */
+       BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_hrd)),
+       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0),
+       BPF_STMT(BPF_RET + BPF_K, 0),
+
+       /* Make sure the hardware length matches. */
+       BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_hln)),
+       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,
+                sizeof((struct ether_arp *)0)->arp_sha, 1, 0),
+       BPF_STMT(BPF_RET + BPF_K, 0),
+};
+#define bpf_arp_ether_len      __arraycount(bpf_arp_ether)
+
+static const struct bpf_insn bpf_arp_filter [] = {
        /* Make sure this is for IP. */
-       BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
-                BPF_L2L + offsetof(struct arphdr, ar_pro)),
+       BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_pro)),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
        BPF_STMT(BPF_RET + BPF_K, 0),
        /* Make sure this is an ARP REQUEST. */
-       BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
-                BPF_L2L + offsetof(struct arphdr, ar_op)),
+       BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_op)),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),
        /* or ARP REPLY. */
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 1),
        BPF_STMT(BPF_RET + BPF_K, 0),
-       /* Make sure the hardware length matches. */
-       BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
-                BPF_L2L + offsetof(struct arphdr, ar_hln)),
-       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHER_ADDR_LEN, 1, 0),
-       BPF_STMT(BPF_RET + BPF_K, 0),
        /* Make sure the protocol length matches. */
-       BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
-                BPF_L2L + offsetof(struct arphdr, ar_pln)),
+       BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_pln)),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(in_addr_t), 1, 0),
        BPF_STMT(BPF_RET + BPF_K, 0),
-       /* Pass back the whole packet. */
-       BPF_STMT(BPF_RET + BPF_K, BPF_WHOLEPACKET),
 };
-#define arp_bpf_filter_len     __arraycount(arp_bpf_filter)
-#define arp_bpf_extra          ((ARP_ADDRS_MAX * 2) * 2) + 2
+#define bpf_arp_filter_len     __arraycount(bpf_arp_filter)
+#define bpf_arp_extra          ((ARP_ADDRS_MAX * 2) * 2) + 2
 
 int
-bpf_arp(struct interface *ifp, int s)
+bpf_arp(struct interface *ifp, int fd)
 {
        size_t bpf_hw = ((((size_t)ifp->hwlen / 4) + 2) * 2) + 1;
-       struct bpf_insn bpf[arp_bpf_filter_len + bpf_hw + arp_bpf_extra];
+       struct bpf_insn bpf[3 + bpf_arp_filter_len + bpf_hw + bpf_arp_extra];
        struct bpf_insn *bp;
        struct iarp_state *state;
 
-       if (s == -1)
+       if (fd == -1)
                return 0;
-       memcpy(bpf, arp_bpf_filter, sizeof(arp_bpf_filter));
-       bp = &bpf[arp_bpf_filter_len];
+       bp = bpf;
+       /* Check frame header. */
+       switch(ifp->family) {
+       case ARPHRD_ETHER:
+               memcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether));
+               bp += bpf_arp_ether_len;
+               break;
+       default:
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* Copy in the main filter. */
+       memcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter));
+       bp += bpf_arp_filter_len;
 
        /* Ensure it's not from us. */
-       bp--;
        bp += bpf_cmp_hwaddr(bp, bpf_hw, sizeof(struct arphdr),
                             false, ifp->hwaddr, ifp->hwlen);
 
@@ -224,8 +438,8 @@ bpf_arp(struct interface *ifp, int s)
                size_t naddrs;
 
                /* Match sender protocol address */
-               BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_ABS,
-                            BPF_L2L + sizeof(struct arphdr) + ifp->hwlen);
+               BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
+                            sizeof(struct arphdr) + ifp->hwlen);
                bp++;
                naddrs = 0;
                TAILQ_FOREACH(astate, &state->arp_states, next) {
@@ -242,8 +456,9 @@ bpf_arp(struct interface *ifp, int s)
                }
 
                /* Match target protocol address */
-               BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_ABS,
-                            BPF_L2L + sizeof(struct arphdr) + ifp->hwlen);
+               BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
+                            (sizeof(struct arphdr)
+                            + (size_t)(ifp->hwlen * 2) + sizeof(in_addr_t)));
                bp++;
                naddrs = 0;
                TAILQ_FOREACH(astate, &state->arp_states, next) {
@@ -264,113 +479,157 @@ bpf_arp(struct interface *ifp, int s)
                bp++;
        }
 
-       /* Replace ETHER_ADDR_LEN for Infiniband if needed. */
-       if (ifp->hwlen != ETHER_ADDR_LEN) {
-               bpf[1].k += (uint32_t)(ifp->hwlen - ETHER_ADDR_LEN) * 2;
-               bpf[BPF_L2I + 11].k = ifp->hwlen;
-       }
-
-       return if_bpf_attach(s, bpf, (unsigned int)(bp - bpf));
+       return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
 }
 #endif
 
-static const struct bpf_insn bootp_bpf_filter[] = {
-       /* Ensure packet is at least correct size. */
-       BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),
-       BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K,
-                BPF_L2L + sizeof(struct ip) + sizeof(struct udphdr)
-                + offsetof(struct bootp, vend), 1, 0),
-       BPF_STMT(BPF_RET + BPF_K, 0),
-#if BPF_L2L
+static const struct bpf_insn bpf_bootp_ether[] = {
        /* Make sure this is an IP packet. */
        BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
                 offsetof(struct ether_header, ether_type)),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
        BPF_STMT(BPF_RET + BPF_K, 0),
-#endif
+
+       /* Load frame header length into X. */
+       BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
+       /* Copy to M0. */
+       BPF_STMT(BPF_STX, 0),
+};
+#define BPF_BOOTP_ETHER_LEN    __arraycount(bpf_bootp_ether)
+
+static const struct bpf_insn bpf_bootp_filter[] = {
+       /* Make sure it's an IPv4 packet. */
+       BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
+       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x45, 1, 0),
+       BPF_STMT(BPF_RET + BPF_K, 0),
+
        /* Make sure it's a UDP packet. */
-       BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
-                BPF_L2L + offsetof(struct bootp_pkt, ip.ip_p)),
+       BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct ip, ip_p)),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),
        BPF_STMT(BPF_RET + BPF_K, 0),
+
        /* Make sure this isn't a fragment. */
-       BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
-                BPF_L2L + offsetof(struct bootp_pkt, ip.ip_off)),
+       BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_off)),
        BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1),
        BPF_STMT(BPF_RET + BPF_K, 0),
-       /* Make sure it's to the right port. */
-       BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
-                BPF_L2L + offsetof(struct bootp_pkt, udp.uh_dport)),
-       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTPC, 1, 0),
+
+       /* Store IP location in M1. */
+       BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_len)),
+       BPF_STMT(BPF_ST, 1),
+
+       /* Store IP length in M2. */
+       BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_len)),
+       BPF_STMT(BPF_ST, 2),
+
+       /* Advance to the UDP header. */
+       BPF_STMT(BPF_MISC + BPF_TXA, 0),
+       BPF_STMT(BPF_ALU + BPF_ADD + BPF_K, sizeof(struct ip)),
+       BPF_STMT(BPF_MISC + BPF_TAX, 0),
+
+       /* Store X in M3. */
+       BPF_STMT(BPF_STX, 3),
+
+       /* Make sure it's from and to the right port. */
+       BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
+       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPS << 16) + BOOTPC, 1, 0),
+       BPF_STMT(BPF_RET + BPF_K, 0),
+
+       /* Store UDP length in X. */
+       BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct udphdr, uh_ulen)),
+       BPF_STMT(BPF_MISC + BPF_TAX, 0),
+       /* Copy IP length in M2 to A. */
+       BPF_STMT(BPF_LD + BPF_MEM, 2),
+       /* Ensure IP length - IP header size == UDP length. */
+       BPF_STMT(BPF_ALU + BPF_SUB + BPF_K, sizeof(struct ip)),
+       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0),
        BPF_STMT(BPF_RET + BPF_K, 0),
+
+       /* Advance to the BOOTP packet (UDP X is in M3). */
+       BPF_STMT(BPF_LD + BPF_MEM, 3),
+       BPF_STMT(BPF_ALU + BPF_ADD + BPF_K, sizeof(struct udphdr)),
+       BPF_STMT(BPF_MISC + BPF_TAX, 0),
+
        /* Make sure it's BOOTREPLY. */
-       BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
-                BPF_L2L + offsetof(struct bootp_pkt, bootp.op)),
+       BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct bootp, op)),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0),
        BPF_STMT(BPF_RET + BPF_K, 0),
-       /* Pass back the whole packet. */
-       BPF_STMT(BPF_RET + BPF_K, BPF_WHOLEPACKET),
 };
-#define bootp_bpf_filter_len   __arraycount(bootp_bpf_filter)
-#define        bootp_bpf_extra         3 + ((BOOTP_CHADDR_LEN / 4) * 3)
+#define BPF_BOOTP_FILTER_LEN   __arraycount(bpf_bootp_filter)
+#define BPF_BOOTP_CHADDR_LEN   ((BOOTP_CHADDR_LEN / 4) * 3)
+#define        BPF_BOOTP_XID_LEN       4 /* BOUND check is 4 instructions */
+
+#define BPF_BOOTP_LEN          BPF_BOOTP_ETHER_LEN + BPF_BOOTP_FILTER_LEN \
+                               + BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4
 
 int
 bpf_bootp(struct interface *ifp, int fd)
 {
        const struct dhcp_state *state = D_CSTATE(ifp);
-       struct bpf_insn bpf[bootp_bpf_filter_len + bootp_bpf_extra];
+       struct bpf_insn bpf[BPF_BOOTP_LEN];
        struct bpf_insn *bp;
-       unsigned int bpf_len = bootp_bpf_extra;
 
        if (fd == -1)
                return 0;
 
-       memcpy(bpf, bootp_bpf_filter, sizeof(bootp_bpf_filter));
-       bp = &bpf[bootp_bpf_filter_len];
+       bp = bpf;
+       /* Check frame header. */
+       switch(ifp->family) {
+       case ARPHRD_ETHER:
+               memcpy(bp, bpf_bootp_ether, sizeof(bpf_bootp_ether));
+               bp += BPF_BOOTP_ETHER_LEN;
+               break;
+       default:
+               errno = EINVAL;
+               return -1;
+       }
 
-       if (state->state != DHS_BOUND ||
-           ifp->hwlen <= sizeof(((struct bootp *)0)->chaddr))
-               bp--;
+       /* Copy in the main filter. */
+       memcpy(bp, bpf_bootp_filter, sizeof(bpf_bootp_filter));
+       bp += BPF_BOOTP_FILTER_LEN;
+
+       if (ifp->hwlen <= sizeof(((struct bootp *)0)->chaddr))
+               bp += bpf_cmp_hwaddr(bp, BPF_BOOTP_CHADDR_LEN,
+                                    offsetof(struct bootp, chaddr),
+                                    true, ifp->hwaddr, ifp->hwlen);
 
        /* Make sure the BOOTP packet is for us. */
        if (state->state == DHS_BOUND) {
                /* If bound, we only expect FORCERENEW messages
-                * and they need to be unicast to us. */
-               BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_ABS,
-                            BPF_L2L + offsetof(struct bootp_pkt, ip.ip_dst));
+                * and they need to be unicast to us.
+                * Move back to the IP header in M0 and check dst. */
+               BPF_SET_STMT(bp, BPF_LDX + BPF_W + BPF_MEM, 0);
+               bp++;
+               BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
+                            offsetof(struct ip, ip_dst));
                bp++;
                BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
                             htonl(state->lease.addr.s_addr), 1, 0);
                bp++;
                BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
                bp++;
-               bpf_len -= 3;
        } else {
                /* As we're not bound, we need to check xid to ensure
                 * it's a reply to our transaction. */
-               BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_ABS,
-                            BPF_L2L + offsetof(struct bootp_pkt, bootp.xid));
+               BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
+                            offsetof(struct bootp, xid));
                bp++;
                BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
                             state->xid, 1, 0);
                bp++;
                BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
                bp++;
-               bpf_len -= 3;
        }
 
-       if (ifp->hwlen <= sizeof(((struct bootp *)0)->chaddr))
-               bp += bpf_cmp_hwaddr(bp, bpf_len,
-                               offsetof(struct bootp_pkt, bootp.chaddr),
-                               true, ifp->hwaddr, ifp->hwlen);
-
-       if (state->state != DHS_BOUND ||
-           ifp->hwlen <= sizeof(((struct bootp *)0)->chaddr))
-       {
-               BPF_SET_STMT(bp, BPF_RET + BPF_K,
-                            BPF_WHOLEPACKET);
-               bp++;
-       }
+       /* All passed, return the packet
+        * (Frame length in M0, IP length in M2). */
+       BPF_SET_STMT(bp, BPF_LD + BPF_MEM, 0);
+       bp++;
+       BPF_SET_STMT(bp, BPF_LDX + BPF_MEM, 2);
+       bp++;
+       BPF_SET_STMT(bp, BPF_ALU + BPF_ADD + BPF_X, 0);
+       bp++;
+       BPF_SET_STMT(bp, BPF_RET + BPF_A, 0);
+       bp++;
 
-       return if_bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
+       return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
 }
diff --git a/bpf.h b/bpf.h
new file mode 100644 (file)
index 0000000..e53877e
--- /dev/null
+++ b/bpf.h
@@ -0,0 +1,45 @@
+/*
+ * dhcpcd: BPF arp and bootp filtering
+ * Copyright (c) 2006-2017 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef BPF_HEADER
+#define BPF_HEADER
+
+#define BPF_EOF                        1 << 0
+#define BPF_PARTIALCSUM                2 << 0
+
+#include "dhcpcd.h"
+
+extern const char *bpf_name;
+size_t bpf_frame_header_len(const struct interface *);
+int bpf_open(struct interface *, int (*)(struct interface *, int));
+int bpf_attach(int, void *, unsigned int);
+#define        bpf_close       close
+ssize_t bpf_send(const struct interface *, int, uint16_t, const void *, size_t);
+ssize_t bpf_read(struct interface *, int, void *, size_t, int *);
+int bpf_arp(struct interface *, int);
+int bpf_bootp(struct interface *, int);
+#endif
diff --git a/dhcp.c b/dhcp.c
index 1ef8499bbaa90450139b3baecc0f0d34d7b44927..39193b5b30f4c8c323743d0c4add8531d0ac9b22 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -54,6 +54,7 @@
 #define ELOOP_QUEUE 2
 #include "config.h"
 #include "arp.h"
+#include "bpf.h"
 #include "common.h"
 #include "dhcp.h"
 #include "dhcpcd.h"
@@ -1537,8 +1538,8 @@ dhcp_new_xid(struct interface *ifp)
                state->xid = arc4random();
 
        /* As the XID changes, re-apply the filter. */
-       if (state->raw_fd != -1)
-               bpf_bootp(ifp, state->raw_fd);
+       if (state->bpf_fd != -1)
+               bpf_bootp(ifp, state->bpf_fd);
 }
 
 void
@@ -1549,10 +1550,10 @@ dhcp_close(struct interface *ifp)
        if (state == NULL)
                return;
 
-       if (state->raw_fd != -1) {
-               eloop_event_delete(ifp->ctx->eloop, state->raw_fd);
-               if_closeraw(ifp, state->raw_fd);
-               state->raw_fd = -1;
+       if (state->bpf_fd != -1) {
+               eloop_event_delete(ifp->ctx->eloop, state->bpf_fd);
+               bpf_close(state->bpf_fd);
+               state->bpf_fd = -1;
        }
 
        state->interval = 0;
@@ -1776,7 +1777,7 @@ send_message(struct interface *ifp, uint8_t type,
                        logger(ifp->ctx, LOG_ERR, "dhcp_makeudppacket: %m");
                        r = 0;
                } else {
-                       r = if_sendraw(ifp, state->raw_fd,
+                       r = bpf_send(ifp, state->bpf_fd,
                            ETHERTYPE_IP, (uint8_t *)udp, ulen);
                        free(udp);
                }
@@ -2289,7 +2290,7 @@ dhcp_bind(struct interface *ifp)
        }
        state->state = DHS_BOUND;
        /* Re-apply the filter because we need to accept any XID anymore. */
-       bpf_bootp(ifp, state->raw_fd);
+       bpf_bootp(ifp, state->bpf_fd);
        if (!state->lease.frominfo &&
            !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
                if (write_lease(ifp, state->new, state->new_len) == -1)
@@ -3288,8 +3289,8 @@ dhcp_readpacket(void *arg)
         * This means we have no kernel call to just get one packet,
         * so we have to process the entire buffer. */
        flags = 0;
-       while (!(flags & RAW_EOF)) {
-               bytes = if_readraw(ifp, state->raw_fd, buf,sizeof(buf), &flags);
+       while (!(flags & BPF_EOF)) {
+               bytes = bpf_read(ifp, state->bpf_fd, buf,sizeof(buf), &flags);
                if (bytes == -1) {
                        logger(ifp->ctx, LOG_ERR,
                            "%s: dhcp if_readrawpacket: %m", ifp->name);
@@ -3325,12 +3326,12 @@ dhcp_open(struct interface *ifp)
        struct dhcp_state *state;
 
        state = D_STATE(ifp);
-       if (state->raw_fd == -1) {
-               state->raw_fd = if_openraw(ifp, ETHERTYPE_IP, bpf_bootp);
-               if (state->raw_fd == -1) {
+       if (state->bpf_fd == -1) {
+               state->bpf_fd = bpf_open(ifp, bpf_bootp);
+               if (state->bpf_fd == -1) {
                        if (errno == ENOENT) {
                                logger(ifp->ctx, LOG_ERR,
-                                   "%s not found", if_pfname);
+                                   "%s not found", bpf_name);
                                /* May as well disable IPv4 entirely at
                                 * this point as we really need it. */
                                ifp->options->options &= ~DHCPCD_IPV4;
@@ -3340,7 +3341,7 @@ dhcp_open(struct interface *ifp)
                        return -1;
                }
                eloop_event_add(ifp->ctx->eloop,
-                   state->raw_fd, dhcp_readpacket, ifp);
+                   state->bpf_fd, dhcp_readpacket, ifp);
        }
        return 0;
 }
@@ -3353,7 +3354,7 @@ dhcp_dump(struct interface *ifp)
        ifp->if_data[IF_DATA_DHCP] = state = calloc(1, sizeof(*state));
        if (state == NULL)
                goto eexit;
-       state->raw_fd = -1;
+       state->bpf_fd = -1;
        dhcp_set_leasefile(state->leasefile, sizeof(state->leasefile),
            AF_INET, ifp);
        state->new_len = read_lease(ifp, &state->new);
@@ -3423,7 +3424,7 @@ dhcp_init(struct interface *ifp)
                if (state == NULL)
                        return -1;
                /* 0 is a valid fd, so init to -1 */
-               state->raw_fd = -1;
+               state->bpf_fd = -1;
 
 #ifdef ARPING
                state->arping_index = -1;
diff --git a/dhcp.h b/dhcp.h
index 4af1a19864c59d4ea242200be69839e3acf2c44e..525e01478744d647f208a040a92f72dc2c612c8e 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
@@ -213,7 +213,7 @@ struct dhcp_state {
        uint32_t xid;
        int socket;
 
-       int raw_fd;
+       int bpf_fd;
        struct ipv4_addr *addr;
        uint8_t added;
 
index 58809cc60036a3491207334712d80f2e4800214f..97674083c90843dd8a469aed7bd54fa24b131a99 100644 (file)
--- a/if-bsd.c
+++ b/if-bsd.c
@@ -609,183 +609,6 @@ if_initrt(struct dhcpcd_ctx *ctx, int af)
 #endif
 
 #ifdef INET
-const char *if_pfname = "Berkley Packet Filter";
-
-void
-if_closeraw(__unused struct interface *ifp, int fd)
-{
-
-       close(fd);
-}
-
-int
-if_openraw(struct interface *ifp, __unused uint16_t protocol,
-    int (*filter)(struct interface *, int))
-{
-       struct ipv4_state *state;
-       int fd = -1;
-       struct ifreq ifr;
-       int ibuf_len = 0;
-       size_t buf_len;
-       struct bpf_version pv;
-#ifdef BIOCIMMEDIATE
-       int flags;
-#endif
-#ifndef O_CLOEXEC
-       int fd_opts;
-#endif
-
-#ifdef _PATH_BPF
-       fd = open(_PATH_BPF, O_RDWR | O_NONBLOCK
-#ifdef O_CLOEXEC
-               | O_CLOEXEC
-#endif
-       );
-#else
-       char device[32];
-       int n = 0;
-
-       do {
-               snprintf(device, sizeof(device), "/dev/bpf%d", n++);
-               fd = open(device, O_RDWR | O_NONBLOCK
-#ifdef O_CLOEXEC
-                               | O_CLOEXEC
-#endif
-               );
-       } while (fd == -1 && errno == EBUSY);
-#endif
-
-       if (fd == -1)
-               return -1;
-
-#ifndef O_CLOEXEC
-       if ((fd_opts = fcntl(fd, F_GETFD)) == -1 ||
-           fcntl(fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1) {
-               close(fd);
-               return -1;
-       }
-#endif
-       state = IPV4_STATE(ifp);
-       memset(&pv, 0, sizeof(pv));
-       if (ioctl(fd, BIOCVERSION, &pv) == -1)
-               goto eexit;
-       if (pv.bv_major != BPF_MAJOR_VERSION ||
-           pv.bv_minor < BPF_MINOR_VERSION) {
-               logger(ifp->ctx, LOG_ERR, "BPF version mismatch - recompile");
-               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)
-               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;
-       if (state->buffer_size != buf_len) {
-               free(state->buffer);
-               state->buffer = malloc(buf_len);
-               if (state->buffer == NULL)
-                       goto eexit;
-               state->buffer_size = buf_len;
-               state->buffer_len = state->buffer_pos = 0;
-       }
-
-#ifdef BIOCIMMEDIATE
-       flags = 1;
-       if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1)
-               goto eexit;
-#endif
-
-       return fd;
-
-eexit:
-       free(state->buffer);
-       state->buffer = NULL;
-       close(fd);
-       return -1;
-}
-
-ssize_t
-if_sendraw(__unused const struct interface *ifp, int fd, uint16_t protocol,
-    const void *data, size_t len)
-{
-       struct iovec iov[2];
-       struct ether_header hw;
-
-       memset(&hw, 0, ETHER_HDR_LEN);
-       memset(&hw.ether_dhost, 0xff, ETHER_ADDR_LEN);
-       hw.ether_type = htons(protocol);
-       iov[0].iov_base = &hw;
-       iov[0].iov_len = ETHER_HDR_LEN;
-       iov[1].iov_base = UNCONST(data);
-       iov[1].iov_len = len;
-       return writev(fd, iov, 2);
-}
-
-/* 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
-if_readraw(struct interface *ifp, int fd, void *data, size_t len, int *flags)
-{
-       struct bpf_hdr packet;
-       ssize_t bytes;
-       const char *payload;
-       struct ipv4_state *state;
-
-       state = IPV4_STATE(ifp);
-       *flags = 0;
-       for (;;) {
-               if (state->buffer_len == 0) {
-                       bytes = read(fd, state->buffer, state->buffer_size);
-                       if (bytes == -1 || bytes == 0)
-                               return bytes;
-                       state->buffer_len = (size_t)bytes;
-                       state->buffer_pos = 0;
-               }
-               bytes = -1;
-               memcpy(&packet, state->buffer + state->buffer_pos,
-                   sizeof(packet));
-               if (packet.bh_caplen != packet.bh_datalen)
-                       goto next; /* Incomplete packet, drop. */
-               if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
-                   state->buffer_len)
-                       goto next; /* Packet beyond buffer, drop. */
-               payload = state->buffer + state->buffer_pos +
-                   packet.bh_hdrlen + ETHER_HDR_LEN;
-               bytes = (ssize_t)packet.bh_caplen - ETHER_HDR_LEN;
-               if ((size_t)bytes > len)
-                       bytes = (ssize_t)len;
-               memcpy(data, payload, (size_t)bytes);
-next:
-               state->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
-                   packet.bh_caplen);
-               if (state->buffer_pos >= state->buffer_len) {
-                       state->buffer_len = state->buffer_pos = 0;
-                       *flags |= RAW_EOF;
-               }
-               if (bytes != -1)
-                       return bytes;
-       }
-}
-
-int
-if_bpf_attach(int s, struct bpf_insn *filter, unsigned int filter_len)
-{
-       struct bpf_program pf;
-
-       /* Install the filter. */
-       memset(&pf, 0, sizeof(pf));
-       pf.bf_insns = filter;
-       pf.bf_len = filter_len;
-       return ioctl(s, BIOCSETF, &pf);
-}
-
 int
 if_address(unsigned char cmd, const struct ipv4_addr *ia)
 {
index 8cda7dd162dce37cb0b76bb44b99d6b2cd57225b..01ebf1d08f6c7513990619a98604c0111f3f3a04 100644 (file)
@@ -58,6 +58,7 @@
 #include <unistd.h>
 
 #include "config.h"
+#include "bpf.h"
 #include "common.h"
 #include "dev.h"
 #include "dhcp.h"
@@ -1286,19 +1287,15 @@ if_initrt(struct dhcpcd_ctx *ctx, int af)
 
 
 #ifdef INET
-const char *if_pfname = "Packet Socket";
-
-void
-if_closeraw(__unused struct interface *ifp, int fd)
-{
-
-       close(fd);
-}
+/* Linux is a special snowflake when it comes to BPF. */
+const char *bpf_name = "Packet Socket";
+#define        BPF_BUFFER_LEN          1500
 
 int
-if_openraw(struct interface *ifp, uint16_t protocol,
-    int (*filter)(struct interface *, int))
+bpf_open(struct interface *ifp, int (*filter)(struct interface *, int))
 {
+       struct ipv4_state *state = IPV4_STATE(ifp);
+/* Linux is a special snowflake for opening BPF. */
        int s;
        union sockunion {
                struct sockaddr sa;
@@ -1310,10 +1307,21 @@ if_openraw(struct interface *ifp, uint16_t protocol,
 #endif
 
 #define SF     SOCK_CLOEXEC | SOCK_NONBLOCK
-       if ((s = xsocket(PF_PACKET, SOCK_DGRAM | SF, htons(protocol))) == -1)
+       if ((s = xsocket(PF_PACKET, SOCK_RAW | SF, htons(ETH_P_ALL))) == -1)
                return -1;
 #undef SF
 
+       /* Allocate a suitably large buffer for a single packet. */
+       if (state->buffer_size < ETH_DATA_LEN) {
+               void *nb;
+
+               if ((nb = realloc(state->buffer, ETH_DATA_LEN)) == NULL)
+                       goto eexit;
+               state->buffer = nb;
+               state->buffer_size = ETH_DATA_LEN;
+               state->buffer_len = state->buffer_pos = 0;
+       }
+
        if (filter(ifp, s) != 0)
                goto eexit;
 
@@ -1327,77 +1335,54 @@ if_openraw(struct interface *ifp, uint16_t protocol,
 
        memset(&su, 0, sizeof(su));
        su.sll.sll_family = PF_PACKET;
-       su.sll.sll_protocol = htons(protocol);
+       su.sll.sll_protocol = htons(ETH_P_ALL);
        su.sll.sll_ifindex = (int)ifp->index;
        if (bind(s, &su.sa, sizeof(su.sll)) == -1)
                goto eexit;
        return s;
 
 eexit:
+       free(state->buffer);
+       state->buffer = NULL;
        close(s);
        return -1;
 }
 
+/* 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
-if_sendraw(const struct interface *ifp, int fd, uint16_t protocol,
-    const void *data, size_t len)
+bpf_read(struct interface *ifp, int s, void *data, size_t len, int *flags)
 {
-       union sockunion {
-               struct sockaddr sa;
-               struct sockaddr_ll sll;
-               struct sockaddr_storage ss;
-       } su;
-
-       memset(&su, 0, sizeof(su));
-       su.sll.sll_family = AF_PACKET;
-       su.sll.sll_protocol = htons(protocol);
-       su.sll.sll_ifindex = (int)ifp->index;
-       su.sll.sll_hatype = htons(ifp->family);
-       su.sll.sll_halen = (unsigned char)ifp->hwlen;
-       if (ifp->family == ARPHRD_INFINIBAND) {
-               /* sockaddr_ll is not big enough for IPoIB which is why
-                * sockaddr_storage is included in the union.
-                * Ugly as sin, but it works. */
-               /* coverity[buffer_size] */
-               /* coverity[overrun-buffer-arg] */
-               memcpy(&su.sll.sll_addr,
-                   &ipv4_bcast_addr, sizeof(ipv4_bcast_addr));
-       } else
-               memset(&su.sll.sll_addr, 0xff, ifp->hwlen);
-
-       return sendto(fd, data, len, 0, &su.sa, sizeof(su.sll));
-}
+       ssize_t bytes;
+       struct ipv4_state *state = IPV4_STATE(ifp);
 
-ssize_t
-if_readraw(__unused struct interface *ifp, int fd,
-    void *data, size_t len, int *flags)
-{
        struct iovec iov = {
-               .iov_base = data,
-               .iov_len = len,
-       };
-       struct msghdr msg = {
-               .msg_iov = &iov,
-               .msg_iovlen = 1,
+               .iov_base = state->buffer,
+               .iov_len = state->buffer_size
        };
+       struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
 #ifdef PACKET_AUXDATA
        unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
        struct cmsghdr *cmsg;
        struct tpacket_auxdata *aux;
 #endif
 
-       ssize_t bytes;
-
 #ifdef PACKET_AUXDATA
        msg.msg_control = cmsgbuf;
        msg.msg_controllen = sizeof(cmsgbuf);
 #endif
 
-       bytes = recvmsg(fd, &msg, 0);
+       bytes = recvmsg(s, &msg, 0);
        if (bytes == -1)
                return -1;
-       *flags = RAW_EOF; /* We only ever read one packet. */
+       *flags = BPF_EOF; /* We only ever read one packet. */
        if (bytes) {
+               ssize_t fl = (ssize_t)bpf_frame_header_len(ifp);
+
+               bytes -= fl;
+               if ((size_t)bytes > len)
+                       bytes = (ssize_t)len;
+               memcpy(data, state->buffer + fl, (size_t)bytes);
 #ifdef PACKET_AUXDATA
                for (cmsg = CMSG_FIRSTHDR(&msg);
                     cmsg;
@@ -1407,7 +1392,7 @@ if_readraw(__unused struct interface *ifp, int fd,
                            cmsg->cmsg_type == PACKET_AUXDATA) {
                                aux = (void *)CMSG_DATA(cmsg);
                                if (aux->tp_status & TP_STATUS_CSUMNOTREADY)
-                                       *flags |= RAW_PARTIALCSUM;
+                                       *flags |= BPF_PARTIALCSUM;
                        }
                }
 #endif
@@ -1416,7 +1401,7 @@ if_readraw(__unused struct interface *ifp, int fd,
 }
 
 int
-if_bpf_attach(int s, struct bpf_insn *filter, unsigned int filter_len)
+bpf_attach(int s, void *filter, unsigned int filter_len)
 {
        struct sock_fprog pf;
 
diff --git a/if.h b/if.h
index 59259e837872ea14b96fed72424aaaad456c7e9b..f1303c902cd5e4569944e9653200dffbd2c83c89 100644 (file)
--- a/if.h
+++ b/if.h
@@ -171,20 +171,6 @@ int if_handlelink(struct dhcpcd_ctx *);
 #endif
 
 #ifdef INET
-#ifdef __linux__
-#define        bpf_insn        sock_filter
-#endif
-struct bpf_insn;
-extern const char *if_pfname;
-int if_openraw(struct interface *, uint16_t, int (*)(struct interface *, int));
-ssize_t if_sendraw(const struct interface *, int, uint16_t,
-    const void *, size_t);
-ssize_t if_readraw(struct interface *, int, void *, size_t, int *);
-void if_closeraw(struct interface *, int);
-int if_bpf_attach(int, struct bpf_insn *, unsigned int);
-int bpf_arp(struct interface *, int);
-int bpf_bootp(struct interface *, int);
-
 int if_address(unsigned char, const struct ipv4_addr *);
 int if_addrflags(const struct interface *, const struct in_addr *,
     const char *);
diff --git a/ipv4.c b/ipv4.c
index 7c2bd4e28d8b13cea82f32882355cf95252c6e82..5e5902ddca8e5d1811601e96645b677aa002ab6d 100644 (file)
--- a/ipv4.c
+++ b/ipv4.c
@@ -533,10 +533,8 @@ ipv4_getstate(struct interface *ifp)
                        return NULL;
                }
                TAILQ_INIT(&state->addrs);
-#ifdef BSD
                state->buffer_size = state->buffer_len = state->buffer_pos = 0;
                state->buffer = NULL;
-#endif
        }
        return state;
 }
@@ -937,9 +935,7 @@ ipv4_free(struct interface *ifp)
                                TAILQ_REMOVE(&state->addrs, ia, next);
                                free(ia);
                        }
-#ifdef BSD
                        free(state->buffer);
-#endif
                        free(state);
                }
        }
diff --git a/ipv4.h b/ipv4.h
index a7dec35a43d8c925c8618b20b2c723ddb562ebd7..9d1858e967147b21d3705aa2b48b0b90b8e31bf3 100644 (file)
--- a/ipv4.h
+++ b/ipv4.h
@@ -95,11 +95,9 @@ TAILQ_HEAD(ipv4_addrhead, ipv4_addr);
 struct ipv4_state {
        struct ipv4_addrhead addrs;
 
-#ifdef BSD
        /* Buffer for BPF */
        size_t buffer_size, buffer_len, buffer_pos;
        char *buffer;
-#endif
 };
 
 #define IPV4_STATE(ifp)                                                               \