Mercurial > hg > dhcpcd
view src/bpf.c @ 5228:82c7e8204e9b draft
BPF: Set write filters where supported
While here make Capsicum and Pledge more granular so we can
easily Pledge the BPF BOOTP process but not the ARP one.
| author | Roy Marples <roy@marples.name> |
|---|---|
| date | Fri, 15 May 2020 20:23:55 +0100 |
| parents | a70f6ddefe3c |
| children | bb468c1a3b46 |
line wrap: on
line source
/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd: BPF arp and bootp filtering * Copyright (c) 2006-2020 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" #define ARP_ADDRS_MAX 3 /* 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 char *frame) { switch (ifp->hwtype) { case ARPHRD_ETHER: return memcmp(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"; 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 unsigned 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) { logerrx("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_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) 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, unsigned int *flags) { ssize_t bytes; struct ipv4_state *state = IPV4_STATE(ifp); struct bpf_hdr packet; const char *payload; *flags &= ~BPF_EOF; for (;;) { if (state->buffer_len == 0) { bytes = read(fd, state->buffer, state->buffer_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(fd, 0, SEEK_SET); continue; } #endif 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 (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; if (bpf_frame_bcast(ifp, payload) == 0) *flags |= BPF_BCAST; else *flags &= ~BPF_BCAST; if (packet.bh_caplen > len) bytes = (ssize_t)len; else bytes = (ssize_t)packet.bh_caplen; 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; } /* 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 interface *ifp, int fd, uint16_t protocol, const void *data, size_t len) { struct iovec iov[2]; struct ether_header eh; switch(ifp->hwtype) { 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); } #endif int bpf_close(struct interface *ifp, int fd) { struct ipv4_state *state = IPV4_STATE(ifp); /* Rewind the buffer on closing. */ state->buffer_len = state->buffer_pos = 0; return close(fd); } /* Normally this is needed by bootp. * Once that uses this again, the ARP guard here can be removed. */ #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, 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) #define BPF_ARP_ADDRS_LEN 1 + (ARP_ADDRS_MAX * 2) + 3 + \ (ARP_ADDRS_MAX * 2) + 1 #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(struct interface *ifp, int fd, bool read) { struct bpf_insn bpf[BPF_ARP_LEN]; struct bpf_insn *bp; struct iarp_state *state; uint16_t arp_len; struct arp_state *astate; size_t naddrs; if (fd == -1) return 0; bp = bpf; /* 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; #ifdef BIOCSETWF if (!read) { /* All passed, return the packet. */ BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET); bp++; return bpf_wattach(fd, bpf, (unsigned int)(bp - bpf)); } #else UNUSED(read); #endif /* Ensure it's not from us. */ bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr), false, ifp->hwaddr, ifp->hwlen); state = ARP_STATE(ifp); /* privsep may not have an initial state yet. */ if (state == NULL || TAILQ_FIRST(&state->arp_states) == NULL) goto noaddrs; /* Match sender protocol address */ BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND, sizeof(struct arphdr) + ifp->hwlen); bp++; naddrs = 0; TAILQ_FOREACH(astate, &state->arp_states, next) { if (IN_IS_ADDR_UNSPECIFIED(&astate->addr)) continue; if (++naddrs > ARP_ADDRS_MAX) { errno = ENOBUFS; logerr(__func__); break; } BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(astate->addr.s_addr), 0, 1); bp++; BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len); bp++; } /* 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++; naddrs = 0; TAILQ_FOREACH(astate, &state->arp_states, next) { if (++naddrs > ARP_ADDRS_MAX) { /* Already logged error above. */ break; } BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(astate->addr.s_addr), 0, 1); bp++; BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len); bp++; } noaddrs: /* Return nothing, no protocol address match. */ BPF_SET_STMT(bp, BPF_RET + BPF_K, 0); bp++; return bpf_attach(fd, bpf, (unsigned int)(bp - bpf)); } int bpf_arp(struct interface *ifp, int fd) { #ifdef BIOCSETWF if (bpf_arp_rw(ifp, fd, false) == -1) return -1; #endif return bpf_arp_rw(ifp, fd, true); } #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) #define BOOTP_MIN_SIZE sizeof(struct ip) + sizeof(struct udphdr) + \ sizeof(struct bootp) 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(struct interface *ifp, int fd, bool read) { #if 0 const struct dhcp_state *state = D_CSTATE(ifp); #endif struct bpf_insn bpf[BPF_BOOTP_LEN]; struct bpf_insn *bp; if (fd == -1) return 0; bp = bpf; /* Check frame header. */ switch(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(fd, bpf, (unsigned int)(bp - bpf)); } #else UNUSED(read); #endif memcpy(bp, bpf_bootp_read, sizeof(bpf_bootp_read)); bp += BPF_BOOTP_READ_LEN; /* These checks won't work when same IP exists on other interfaces. */ #if 0 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. * 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++; } 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_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++; } #endif /* All passed, return the packet. */ BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET); bp++; return bpf_attach(fd, bpf, (unsigned int)(bp - bpf)); } int bpf_bootp(struct interface *ifp, int fd) { #ifdef BIOCSETWF if (bpf_bootp_rw(ifp, fd, true) == -1 || bpf_bootp_rw(ifp, fd, false) == -1 || ioctl(fd, BIOCLOCK) == -1) return -1; return 0; #else return bpf_bootp_rw(ifp, fd, true); #endif }
