diff options
Diffstat (limited to 'src/bpf.c')
| -rw-r--r-- | src/bpf.c | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/src/bpf.c b/src/bpf.c new file mode 100644 index 000000000000..21b73af207be --- /dev/null +++ b/src/bpf.c @@ -0,0 +1,713 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * dhcpcd: BPF arp and bootp filtering + * Copyright (c) 2006-2021 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. + */ + +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <arpa/inet.h> + +#include <net/if.h> +#include <netinet/in.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 <paths.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" +#include "logerr.h" + +/* BPF helper macros */ +#ifdef __linux__ +#define BPF_WHOLEPACKET 0x7fffffff /* work around buggy LPF filters */ +#else +#define BPF_WHOLEPACKET ~0U +#endif + +/* Macros to update the BPF structure */ +#define BPF_SET_STMT(insn, c, v) { \ + (insn)->code = (c); \ + (insn)->jt = 0; \ + (insn)->jf = 0; \ + (insn)->k = (uint32_t)(v); \ +} + +#define BPF_SET_JUMP(insn, c, v, t, f) { \ + (insn)->code = (c); \ + (insn)->jt = (t); \ + (insn)->jf = (f); \ + (insn)->k = (uint32_t)(v); \ +} + +size_t +bpf_frame_header_len(const struct interface *ifp) +{ + + switch (ifp->hwtype) { + case ARPHRD_ETHER: + return sizeof(struct ether_header); + default: + return 0; + } +} + +void * +bpf_frame_header_src(const struct interface *ifp, void *fh, size_t *len) +{ + uint8_t *f = fh; + + switch (ifp->hwtype) { + case ARPHRD_ETHER: + *len = sizeof(((struct ether_header *)0)->ether_shost); + return f + offsetof(struct ether_header, ether_shost); + default: + *len = 0; + errno = ENOTSUP; + return NULL; + } +} + +void * +bpf_frame_header_dst(const struct interface *ifp, void *fh, size_t *len) +{ + uint8_t *f = fh; + + switch (ifp->hwtype) { + case ARPHRD_ETHER: + *len = sizeof(((struct ether_header *)0)->ether_dhost); + return f + offsetof(struct ether_header, ether_dhost); + default: + *len = 0; + errno = ENOTSUP; + return NULL; + } +} + +static const uint8_t etherbcastaddr[] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +int +bpf_frame_bcast(const struct interface *ifp, const void *frame) +{ + + switch (ifp->hwtype) { + case ARPHRD_ETHER: + return memcmp((const char *)frame + + offsetof(struct ether_header, ether_dhost), + etherbcastaddr, sizeof(etherbcastaddr)); + default: + return -1; + } +} + +#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"; + +struct bpf * +bpf_open(const struct interface *ifp, + int (*filter)(const struct bpf *, const struct in_addr *), + const struct in_addr *ia) +{ + struct bpf *bpf; + struct bpf_version pv = { .bv_major = 0, .bv_minor = 0 }; + struct ifreq ifr = { .ifr_flags = 0 }; + int ibuf_len = 0; +#ifdef BIOCIMMEDIATE + unsigned int flags; +#endif +#ifndef O_CLOEXEC + int fd_opts; +#endif + + bpf = calloc(1, sizeof(*bpf)); + if (bpf == NULL) + return NULL; + bpf->bpf_ifp = ifp; + +#ifdef _PATH_BPF + bpf->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++); + bpf->bpf_fd = open(device, O_RDWR | O_NONBLOCK +#ifdef O_CLOEXEC + | O_CLOEXEC +#endif + ); + } while (bpf->bpf_fd == -1 && errno == EBUSY); +#endif + + if (bpf->bpf_fd == -1) + goto eexit; + +#ifndef O_CLOEXEC + if ((fd_opts = fcntl(bpf->bpf_fd, F_GETFD)) == -1 || + fcntl(bpf->bpf_fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1) + goto eexit; +#endif + + if (ioctl(bpf->bpf_fd, BIOCVERSION, &pv) == -1) + goto eexit; + if (pv.bv_major != BPF_MAJOR_VERSION || + pv.bv_minor < BPF_MINOR_VERSION) { + logerrx("BPF version mismatch - recompile"); + goto eexit; + } + + strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); + if (ioctl(bpf->bpf_fd, BIOCSETIF, &ifr) == -1) + goto eexit; + +#ifdef BIOCIMMEDIATE + flags = 1; + if (ioctl(bpf->bpf_fd, BIOCIMMEDIATE, &flags) == -1) + goto eexit; +#endif + + 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: + 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 bpf *bpf, void *data, size_t len) +{ + ssize_t bytes; + struct bpf_hdr packet; + const char *payload; + + bpf->bpf_flags &= ~BPF_EOF; + for (;;) { + 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. */ + if (bytes == -1 && errno == EINVAL) { + lseek(bpf->bpf_fd, 0, SEEK_SET); + continue; + } +#endif + if (bytes == -1 || bytes == 0) + return bytes; + bpf->bpf_len = (size_t)bytes; + bpf->bpf_pos = 0; + } + bytes = -1; + 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 += 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: + bpf->bpf_pos += BPF_WORDALIGN(packet.bh_hdrlen + + packet.bh_caplen); + if (bpf->bpf_pos >= bpf->bpf_len) { + bpf->bpf_len = bpf->bpf_pos = 0; + bpf->bpf_flags |= BPF_EOF; + } + if (bytes != -1) + return bytes; + } + + /* NOTREACHED */ +} + +int +bpf_attach(int fd, void *filter, unsigned int filter_len) +{ + struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len }; + + /* Install the filter. */ + return ioctl(fd, BIOCSETF, &pf); +} + +#ifdef BIOCSETWF +static int +bpf_wattach(int fd, void *filter, unsigned int filter_len) +{ + struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len }; + + /* Install the filter. */ + return ioctl(fd, BIOCSETWF, &pf); +} +#endif +#endif + +#ifndef __sun +/* SunOS is special too - sending via BPF goes nowhere. */ +ssize_t +bpf_send(const struct bpf *bpf, uint16_t protocol, + const void *data, size_t len) +{ + struct iovec iov[2]; + struct ether_header eh; + + switch(bpf->bpf_ifp->hwtype) { + case ARPHRD_ETHER: + memset(&eh.ether_dhost, 0xff, sizeof(eh.ether_dhost)); + 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); + 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(bpf->bpf_fd, iov, 2); +} +#endif + +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 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; + } + + 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 [] = { + /* 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), + + /* Load frame header length into X */ + BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)), + + /* Make sure the hardware type 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_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_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, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + /* Make sure the protocol length matches. */ + 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), +}; +#define BPF_ARP_FILTER_LEN __arraycount(bpf_arp_filter) + +/* 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 + \ + BPF_CMP_HWADDR_LEN + BPF_ARP_ADDRS_LEN + +static int +bpf_arp_rw(const struct bpf *bpf, const struct in_addr *ia, bool recv) +{ + const struct interface *ifp = bpf->bpf_ifp; + struct bpf_insn buf[BPF_ARP_LEN + 1]; + struct bpf_insn *bp; + uint16_t arp_len; + + bp = buf; + /* Check frame header. */ + switch(ifp->hwtype) { + case ARPHRD_ETHER: + memcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether)); + bp += BPF_ARP_ETHER_LEN; + arp_len = sizeof(struct ether_header)+sizeof(struct ether_arp); + 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 += 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 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 (!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 + +#ifdef ARPHRD_NONE +static const struct bpf_insn bpf_bootp_none[] = { +}; +#define BPF_BOOTP_NONE_LEN __arraycount(bpf_bootp_none) +#endif + +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), + + /* Advance to the IP header. */ + BPF_STMT(BPF_LDX + BPF_K, sizeof(struct ether_header)), +}; +#define BPF_BOOTP_ETHER_LEN __arraycount(bpf_bootp_ether) + +static const struct bpf_insn bpf_bootp_base[] = { + /* Make sure it's an IPv4 packet. */ + BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0), + BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0xf0), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x40, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + + /* Make sure it's a UDP packet. */ + 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_IND, offsetof(struct ip, ip_off)), + BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1), + BPF_STMT(BPF_RET + BPF_K, 0), + + /* Advance to the UDP header. */ + BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0), + BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x0f), + BPF_STMT(BPF_ALU + BPF_MUL + BPF_K, 4), + BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0), + BPF_STMT(BPF_MISC + BPF_TAX, 0), +}; +#define BPF_BOOTP_BASE_LEN __arraycount(bpf_bootp_base) + +static const struct bpf_insn bpf_bootp_read[] = { + /* 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), +}; +#define BPF_BOOTP_READ_LEN __arraycount(bpf_bootp_read) + +#ifdef BIOCSETWF +static const struct bpf_insn bpf_bootp_write[] = { + /* 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, (BOOTPC << 16) + BOOTPS, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), +}; +#define BPF_BOOTP_WRITE_LEN __arraycount(bpf_bootp_write) +#endif + +#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_BASE_LEN + BPF_BOOTP_READ_LEN + \ + BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4 + +static int +bpf_bootp_rw(const struct bpf *bpf, bool read) +{ + struct bpf_insn buf[BPF_BOOTP_LEN + 1]; + struct bpf_insn *bp; + + bp = buf; + /* Check frame header. */ + switch(bpf->bpf_ifp->hwtype) { +#ifdef ARPHRD_NONE + case ARPHRD_NONE: + memcpy(bp, bpf_bootp_none, sizeof(bpf_bootp_none)); + bp += BPF_BOOTP_NONE_LEN; + break; +#endif + case ARPHRD_ETHER: + memcpy(bp, bpf_bootp_ether, sizeof(bpf_bootp_ether)); + bp += BPF_BOOTP_ETHER_LEN; + break; + default: + errno = EINVAL; + return -1; + } + + /* Copy in the main filter. */ + memcpy(bp, bpf_bootp_base, sizeof(bpf_bootp_base)); + bp += BPF_BOOTP_BASE_LEN; + +#ifdef BIOCSETWF + if (!read) { + memcpy(bp, bpf_bootp_write, sizeof(bpf_bootp_write)); + bp += BPF_BOOTP_WRITE_LEN; + + /* All passed, return the packet. */ + BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET); + bp++; + + return bpf_wattach(bpf->bpf_fd, buf, (unsigned int)(bp - buf)); + } +#else + UNUSED(read); +#endif + + memcpy(bp, bpf_bootp_read, sizeof(bpf_bootp_read)); + bp += BPF_BOOTP_READ_LEN; + + /* All passed, return the packet. */ + BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET); + bp++; + + return bpf_attach(bpf->bpf_fd, buf, (unsigned int)(bp - buf)); +} + +int +bpf_bootp(const struct bpf *bpf, __unused const struct in_addr *ia) +{ + +#ifdef BIOCSETWF + if (bpf_bootp_rw(bpf, true) == -1 || + bpf_bootp_rw(bpf, false) == -1 || + ioctl(bpf->bpf_fd, BIOCLOCK) == -1) + return -1; + return 0; +#else +#ifdef PRIVSEP +#if defined(__sun) /* Solaris cannot send via BPF. */ +#elif defined(BIOCSETF) +#warning No BIOCSETWF support - a compromised BPF can be used as a raw socket +#else +#warning A compromised PF_PACKET socket can be used as a raw socket +#endif +#endif + return bpf_bootp_rw(bpf, true); +#endif +} |
