Annotation of src/external/bsd/dhcpcd/dist/src/bpf.c, Revision 1.1
1.1 ! roy 1: /*
! 2: * dhcpcd: BPF arp and bootp filtering
! 3: * Copyright (c) 2006-2017 Roy Marples <roy@marples.name>
! 4: * All rights reserved
! 5:
! 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions
! 8: * are met:
! 9: * 1. Redistributions of source code must retain the above copyright
! 10: * notice, this list of conditions and the following disclaimer.
! 11: * 2. Redistributions in binary form must reproduce the above copyright
! 12: * notice, this list of conditions and the following disclaimer in the
! 13: * documentation and/or other materials provided with the distribution.
! 14: *
! 15: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 16: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 17: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 18: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 19: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 20: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 21: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 22: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 23: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 24: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 25: * SUCH DAMAGE.
! 26: */
! 27:
! 28: #include <sys/ioctl.h>
! 29: #include <sys/socket.h>
! 30:
! 31: #include <arpa/inet.h>
! 32:
! 33: #include <net/if.h>
! 34: #include <netinet/in.h>
! 35: #include <netinet/if_ether.h>
! 36:
! 37: #ifdef __linux__
! 38: /* Special BPF snowflake. */
! 39: #include <linux/filter.h>
! 40: #define bpf_insn sock_filter
! 41: #else
! 42: #include <net/bpf.h>
! 43: #endif
! 44:
! 45: #include <errno.h>
! 46: #include <fcntl.h>
! 47: #include <paths.h>
! 48: #include <stddef.h>
! 49: #include <stdlib.h>
! 50: #include <string.h>
! 51: #include <syslog.h>
! 52:
! 53: #include "common.h"
! 54: #include "arp.h"
! 55: #include "bpf.h"
! 56: #include "dhcp.h"
! 57: #include "if.h"
! 58:
! 59: #define ARP_ADDRS_MAX 3
! 60:
! 61: /* BPF helper macros */
! 62: #ifdef __linux__
! 63: #define BPF_WHOLEPACKET 0x7fffffff /* work around buggy LPF filters */
! 64: #else
! 65: #define BPF_WHOLEPACKET ~0U
! 66: #endif
! 67:
! 68: /* Macros to update the BPF structure */
! 69: #define BPF_SET_STMT(insn, c, v) { \
! 70: (insn)->code = (c); \
! 71: (insn)->jt = 0; \
! 72: (insn)->jf = 0; \
! 73: (insn)->k = (uint32_t)(v); \
! 74: };
! 75:
! 76: #define BPF_SET_JUMP(insn, c, v, t, f) { \
! 77: (insn)->code = (c); \
! 78: (insn)->jt = (t); \
! 79: (insn)->jf = (f); \
! 80: (insn)->k = (uint32_t)(v); \
! 81: };
! 82:
! 83: size_t
! 84: bpf_frame_header_len(const struct interface *ifp)
! 85: {
! 86:
! 87: switch(ifp->family) {
! 88: case ARPHRD_ETHER:
! 89: return sizeof(struct ether_header);
! 90: default:
! 91: return 0;
! 92: }
! 93: }
! 94:
! 95: #ifndef __linux__
! 96: /* Linux is a special snowflake for opening, attaching and reading BPF.
! 97: * See if-linux.c for the Linux specific BPF functions. */
! 98:
! 99: const char *bpf_name = "Berkley Packet Filter";
! 100:
! 101: int
! 102: bpf_open(struct interface *ifp, int (*filter)(struct interface *, int))
! 103: {
! 104: struct ipv4_state *state;
! 105: int fd = -1;
! 106: struct ifreq ifr;
! 107: int ibuf_len = 0;
! 108: size_t buf_len;
! 109: struct bpf_version pv;
! 110: #ifdef BIOCIMMEDIATE
! 111: int flags;
! 112: #endif
! 113: #ifndef O_CLOEXEC
! 114: int fd_opts;
! 115: #endif
! 116:
! 117: #ifdef _PATH_BPF
! 118: fd = open(_PATH_BPF, O_RDWR | O_NONBLOCK
! 119: #ifdef O_CLOEXEC
! 120: | O_CLOEXEC
! 121: #endif
! 122: );
! 123: #else
! 124: char device[32];
! 125: int n = 0;
! 126:
! 127: do {
! 128: snprintf(device, sizeof(device), "/dev/bpf%d", n++);
! 129: fd = open(device, O_RDWR | O_NONBLOCK
! 130: #ifdef O_CLOEXEC
! 131: | O_CLOEXEC
! 132: #endif
! 133: );
! 134: } while (fd == -1 && errno == EBUSY);
! 135: #endif
! 136:
! 137: if (fd == -1)
! 138: return -1;
! 139:
! 140: #ifndef O_CLOEXEC
! 141: if ((fd_opts = fcntl(fd, F_GETFD)) == -1 ||
! 142: fcntl(fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1) {
! 143: close(fd);
! 144: return -1;
! 145: }
! 146: #endif
! 147:
! 148: memset(&pv, 0, sizeof(pv));
! 149: if (ioctl(fd, BIOCVERSION, &pv) == -1)
! 150: goto eexit;
! 151: if (pv.bv_major != BPF_MAJOR_VERSION ||
! 152: pv.bv_minor < BPF_MINOR_VERSION) {
! 153: syslog(LOG_ERR, "BPF version mismatch - recompile");
! 154: goto eexit;
! 155: }
! 156:
! 157: if (filter(ifp, fd) != 0)
! 158: goto eexit;
! 159:
! 160: memset(&ifr, 0, sizeof(ifr));
! 161: strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
! 162: if (ioctl(fd, BIOCSETIF, &ifr) == -1)
! 163: goto eexit;
! 164:
! 165: /* Get the required BPF buffer length from the kernel. */
! 166: if (ioctl(fd, BIOCGBLEN, &ibuf_len) == -1)
! 167: goto eexit;
! 168: buf_len = (size_t)ibuf_len;
! 169: state = IPV4_STATE(ifp);
! 170: if (state->buffer_size != buf_len) {
! 171: void *nb;
! 172:
! 173: if ((nb = realloc(state->buffer, buf_len)) == NULL)
! 174: goto eexit;
! 175: state->buffer = nb;
! 176: state->buffer_size = buf_len;
! 177: state->buffer_len = state->buffer_pos = 0;
! 178: }
! 179:
! 180: #ifdef BIOCIMMEDIATE
! 181: flags = 1;
! 182: if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1)
! 183: goto eexit;
! 184: #endif
! 185:
! 186: return fd;
! 187:
! 188: eexit:
! 189: close(fd);
! 190: return -1;
! 191: }
! 192:
! 193: /* BPF requires that we read the entire buffer.
! 194: * So we pass the buffer in the API so we can loop on >1 packet. */
! 195: ssize_t
! 196: bpf_read(struct interface *ifp, int fd, void *data, size_t len, int *flags)
! 197: {
! 198: ssize_t fl = (ssize_t)bpf_frame_header_len(ifp);
! 199: ssize_t bytes;
! 200: struct ipv4_state *state = IPV4_STATE(ifp);
! 201:
! 202: struct bpf_hdr packet;
! 203: const char *payload;
! 204:
! 205: *flags = 0;
! 206: for (;;) {
! 207: if (state->buffer_len == 0) {
! 208: bytes = read(fd, state->buffer, state->buffer_size);
! 209: #if defined(__sun)
! 210: /* After 2^31 bytes, the kernel offset overflows.
! 211: * To work around this bug, lseek 0. */
! 212: if (bytes == -1 && errno == EINVAL) {
! 213: lseek(fd, 0, SEEK_SET);
! 214: continue;
! 215: }
! 216: #endif
! 217: if (bytes == -1 || bytes == 0)
! 218: return bytes;
! 219: state->buffer_len = (size_t)bytes;
! 220: state->buffer_pos = 0;
! 221: }
! 222: bytes = -1;
! 223: memcpy(&packet, state->buffer + state->buffer_pos,
! 224: sizeof(packet));
! 225: if (packet.bh_caplen != packet.bh_datalen)
! 226: goto next; /* Incomplete packet, drop. */
! 227: if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
! 228: state->buffer_len)
! 229: goto next; /* Packet beyond buffer, drop. */
! 230: payload = state->buffer + state->buffer_pos +
! 231: packet.bh_hdrlen + fl;
! 232: bytes = (ssize_t)packet.bh_caplen - fl;
! 233: if ((size_t)bytes > len)
! 234: bytes = (ssize_t)len;
! 235: memcpy(data, payload, (size_t)bytes);
! 236: next:
! 237: state->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
! 238: packet.bh_caplen);
! 239: if (state->buffer_pos >= state->buffer_len) {
! 240: state->buffer_len = state->buffer_pos = 0;
! 241: *flags |= BPF_EOF;
! 242: }
! 243: if (bytes != -1)
! 244: return bytes;
! 245: }
! 246:
! 247: return bytes;
! 248: }
! 249:
! 250: int
! 251: bpf_attach(int fd, void *filter, unsigned int filter_len)
! 252: {
! 253: struct bpf_program pf;
! 254:
! 255: /* Install the filter. */
! 256: memset(&pf, 0, sizeof(pf));
! 257: pf.bf_insns = filter;
! 258: pf.bf_len = filter_len;
! 259: return ioctl(fd, BIOCSETF, &pf);
! 260: }
! 261: #endif
! 262:
! 263: #ifndef __sun
! 264: /* SunOS is special too - sending via BPF goes nowhere. */
! 265: ssize_t
! 266: bpf_send(const struct interface *ifp, int fd, uint16_t protocol,
! 267: const void *data, size_t len)
! 268: {
! 269: struct iovec iov[2];
! 270: struct ether_header eh;
! 271:
! 272: switch(ifp->family) {
! 273: case ARPHRD_ETHER:
! 274: memset(&eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
! 275: memcpy(&eh.ether_shost, ifp->hwaddr, sizeof(eh.ether_shost));
! 276: eh.ether_type = htons(protocol);
! 277: iov[0].iov_base = &eh;
! 278: iov[0].iov_len = sizeof(eh);
! 279: break;
! 280: default:
! 281: iov[0].iov_base = NULL;
! 282: iov[0].iov_len = 0;
! 283: break;
! 284: }
! 285: iov[1].iov_base = UNCONST(data);
! 286: iov[1].iov_len = len;
! 287: return writev(fd, iov, 2);
! 288: }
! 289: #endif
! 290:
! 291: static unsigned int
! 292: bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off,
! 293: bool equal, uint8_t *hwaddr, size_t hwaddr_len)
! 294: {
! 295: struct bpf_insn *bp;
! 296: size_t maclen, nlft, njmps;
! 297: uint32_t mac32;
! 298: uint16_t mac16;
! 299: uint8_t jt, jf;
! 300:
! 301: /* Calc the number of jumps */
! 302: if ((hwaddr_len / 4) >= 128) {
! 303: errno = EINVAL;
! 304: return 0;
! 305: }
! 306: njmps = (hwaddr_len / 4) * 2; /* 2 instructions per check */
! 307: /* We jump after the 1st check. */
! 308: if (njmps)
! 309: njmps -= 2;
! 310: nlft = hwaddr_len % 4;
! 311: if (nlft) {
! 312: njmps += (nlft / 2) * 2;
! 313: nlft = nlft % 2;
! 314: if (nlft)
! 315: njmps += 2;
! 316:
! 317: }
! 318:
! 319: /* Skip to positive finish. */
! 320: njmps++;
! 321: if (equal) {
! 322: jt = (uint8_t)njmps;
! 323: jf = 0;
! 324: } else {
! 325: jt = 0;
! 326: jf = (uint8_t)njmps;
! 327: }
! 328:
! 329: bp = bpf;
! 330: for (; hwaddr_len > 0;
! 331: hwaddr += maclen, hwaddr_len -= maclen, off += maclen)
! 332: {
! 333: if (bpf_len < 3) {
! 334: errno = ENOBUFS;
! 335: return 0;
! 336: }
! 337: bpf_len -= 3;
! 338:
! 339: if (hwaddr_len >= 4) {
! 340: maclen = sizeof(mac32);
! 341: memcpy(&mac32, hwaddr, maclen);
! 342: BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND, off);
! 343: bp++;
! 344: BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
! 345: htonl(mac32), jt, jf);
! 346: } else if (hwaddr_len >= 2) {
! 347: maclen = sizeof(mac16);
! 348: memcpy(&mac16, hwaddr, maclen);
! 349: BPF_SET_STMT(bp, BPF_LD + BPF_H + BPF_IND, off);
! 350: bp++;
! 351: BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
! 352: htons(mac16), jt, jf);
! 353: } else {
! 354: maclen = sizeof(*hwaddr);
! 355: BPF_SET_STMT(bp, BPF_LD + BPF_B + BPF_IND, off);
! 356: bp++;
! 357: BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
! 358: *hwaddr, jt, jf);
! 359: }
! 360: if (jt)
! 361: jt = (uint8_t)(jt - 2);
! 362: if (jf)
! 363: jf = (uint8_t)(jf - 2);
! 364: bp++;
! 365: }
! 366:
! 367: /* Last step is always return failure.
! 368: * Next step is a positive finish. */
! 369: BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
! 370: bp++;
! 371:
! 372: return (unsigned int)(bp - bpf);
! 373: }
! 374:
! 375: #ifdef ARP
! 376:
! 377: static const struct bpf_insn bpf_arp_ether [] = {
! 378: /* Ensure packet is at least correct size. */
! 379: BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),
! 380: BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0),
! 381: BPF_STMT(BPF_RET + BPF_K, 0),
! 382:
! 383: /* Check this is an ARP packet. */
! 384: BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
! 385: offsetof(struct ether_header, ether_type)),
! 386: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 1, 0),
! 387: BPF_STMT(BPF_RET + BPF_K, 0),
! 388:
! 389: /* Load frame header length into X */
! 390: BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
! 391:
! 392: /* Make sure the hardware family matches. */
! 393: BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_hrd)),
! 394: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0),
! 395: BPF_STMT(BPF_RET + BPF_K, 0),
! 396:
! 397: /* Make sure the hardware length matches. */
! 398: BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_hln)),
! 399: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,
! 400: sizeof((struct ether_arp *)0)->arp_sha, 1, 0),
! 401: BPF_STMT(BPF_RET + BPF_K, 0),
! 402: };
! 403: #define bpf_arp_ether_len __arraycount(bpf_arp_ether)
! 404:
! 405: static const struct bpf_insn bpf_arp_filter [] = {
! 406: /* Make sure this is for IP. */
! 407: BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_pro)),
! 408: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
! 409: BPF_STMT(BPF_RET + BPF_K, 0),
! 410: /* Make sure this is an ARP REQUEST. */
! 411: BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_op)),
! 412: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),
! 413: /* or ARP REPLY. */
! 414: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 1),
! 415: BPF_STMT(BPF_RET + BPF_K, 0),
! 416: /* Make sure the protocol length matches. */
! 417: BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_pln)),
! 418: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(in_addr_t), 1, 0),
! 419: BPF_STMT(BPF_RET + BPF_K, 0),
! 420: };
! 421: #define bpf_arp_filter_len __arraycount(bpf_arp_filter)
! 422: #define bpf_arp_extra (((ARP_ADDRS_MAX + 1) * 2) * 2) + 2
! 423:
! 424: int
! 425: bpf_arp(struct interface *ifp, int fd)
! 426: {
! 427: size_t bpf_hw = ((((size_t)ifp->hwlen / 4) + 2) * 2) + 1;
! 428: struct bpf_insn bpf[3 + bpf_arp_filter_len + bpf_hw + bpf_arp_extra];
! 429: struct bpf_insn *bp;
! 430: struct iarp_state *state;
! 431:
! 432: if (fd == -1)
! 433: return 0;
! 434:
! 435: bp = bpf;
! 436: /* Check frame header. */
! 437: switch(ifp->family) {
! 438: case ARPHRD_ETHER:
! 439: memcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether));
! 440: bp += bpf_arp_ether_len;
! 441: break;
! 442: default:
! 443: errno = EINVAL;
! 444: return -1;
! 445: }
! 446:
! 447: /* Copy in the main filter. */
! 448: memcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter));
! 449: bp += bpf_arp_filter_len;
! 450:
! 451: /* Ensure it's not from us. */
! 452: bp += bpf_cmp_hwaddr(bp, bpf_hw, sizeof(struct arphdr),
! 453: false, ifp->hwaddr, ifp->hwlen);
! 454:
! 455: state = ARP_STATE(ifp);
! 456: if (TAILQ_FIRST(&state->arp_states)) {
! 457: struct arp_state *astate;
! 458: size_t naddrs;
! 459:
! 460: /* Match sender protocol address */
! 461: BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
! 462: sizeof(struct arphdr) + ifp->hwlen);
! 463: bp++;
! 464: naddrs = 0;
! 465: TAILQ_FOREACH(astate, &state->arp_states, next) {
! 466: if (++naddrs > ARP_ADDRS_MAX) {
! 467: errno = ENOBUFS;
! 468: syslog(LOG_ERR, "%s: %m", __func__);
! 469: break;
! 470: }
! 471: BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
! 472: htonl(astate->addr.s_addr), 0, 1);
! 473: bp++;
! 474: BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
! 475: bp++;
! 476: }
! 477:
! 478: /* If we didn't match sender, then we're only interested in
! 479: * ARP probes to us, so check the null host sender. */
! 480: BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, INADDR_ANY, 1, 0);
! 481: bp++;
! 482: BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
! 483: bp++;
! 484:
! 485: /* Match target protocol address */
! 486: BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
! 487: (sizeof(struct arphdr)
! 488: + (size_t)(ifp->hwlen * 2) + sizeof(in_addr_t)));
! 489: bp++;
! 490: naddrs = 0;
! 491: TAILQ_FOREACH(astate, &state->arp_states, next) {
! 492: if (++naddrs > ARP_ADDRS_MAX) {
! 493: /* Already logged error above. */
! 494: break;
! 495: }
! 496: BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
! 497: htonl(astate->addr.s_addr), 0, 1);
! 498: bp++;
! 499: BPF_SET_STMT(bp, BPF_RET + BPF_K,
! 500: BPF_WHOLEPACKET);
! 501: bp++;
! 502: }
! 503:
! 504: /* Return nothing, no protocol address match. */
! 505: BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
! 506: bp++;
! 507: }
! 508:
! 509: return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
! 510: }
! 511: #endif
! 512:
! 513: static const struct bpf_insn bpf_bootp_ether[] = {
! 514: /* Make sure this is an IP packet. */
! 515: BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
! 516: offsetof(struct ether_header, ether_type)),
! 517: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
! 518: BPF_STMT(BPF_RET + BPF_K, 0),
! 519:
! 520: /* Load frame header length into X. */
! 521: BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
! 522: /* Copy to M0. */
! 523: BPF_STMT(BPF_STX, 0),
! 524: };
! 525: #define BPF_BOOTP_ETHER_LEN __arraycount(bpf_bootp_ether)
! 526:
! 527: static const struct bpf_insn bpf_bootp_filter[] = {
! 528: /* Make sure it's an IPv4 packet. */
! 529: BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
! 530: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x45, 1, 0),
! 531: BPF_STMT(BPF_RET + BPF_K, 0),
! 532:
! 533: /* Make sure it's a UDP packet. */
! 534: BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct ip, ip_p)),
! 535: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),
! 536: BPF_STMT(BPF_RET + BPF_K, 0),
! 537:
! 538: /* Make sure this isn't a fragment. */
! 539: BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_off)),
! 540: BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1),
! 541: BPF_STMT(BPF_RET + BPF_K, 0),
! 542:
! 543: /* Store IP location in M1. */
! 544: BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_len)),
! 545: BPF_STMT(BPF_ST, 1),
! 546:
! 547: /* Store IP length in M2. */
! 548: BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_len)),
! 549: BPF_STMT(BPF_ST, 2),
! 550:
! 551: /* Advance to the UDP header. */
! 552: BPF_STMT(BPF_MISC + BPF_TXA, 0),
! 553: BPF_STMT(BPF_ALU + BPF_ADD + BPF_K, sizeof(struct ip)),
! 554: BPF_STMT(BPF_MISC + BPF_TAX, 0),
! 555:
! 556: /* Store X in M3. */
! 557: BPF_STMT(BPF_STX, 3),
! 558:
! 559: /* Make sure it's from and to the right port. */
! 560: BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
! 561: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPS << 16) + BOOTPC, 1, 0),
! 562: BPF_STMT(BPF_RET + BPF_K, 0),
! 563:
! 564: /* Store UDP length in X. */
! 565: BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct udphdr, uh_ulen)),
! 566: BPF_STMT(BPF_MISC + BPF_TAX, 0),
! 567: /* Copy IP length in M2 to A. */
! 568: BPF_STMT(BPF_LD + BPF_MEM, 2),
! 569: /* Ensure IP length - IP header size == UDP length. */
! 570: BPF_STMT(BPF_ALU + BPF_SUB + BPF_K, sizeof(struct ip)),
! 571: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0),
! 572: BPF_STMT(BPF_RET + BPF_K, 0),
! 573:
! 574: /* Advance to the BOOTP packet (UDP X is in M3). */
! 575: BPF_STMT(BPF_LD + BPF_MEM, 3),
! 576: BPF_STMT(BPF_ALU + BPF_ADD + BPF_K, sizeof(struct udphdr)),
! 577: BPF_STMT(BPF_MISC + BPF_TAX, 0),
! 578:
! 579: /* Make sure it's BOOTREPLY. */
! 580: BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct bootp, op)),
! 581: BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0),
! 582: BPF_STMT(BPF_RET + BPF_K, 0),
! 583: };
! 584: #define BPF_BOOTP_FILTER_LEN __arraycount(bpf_bootp_filter)
! 585: #define BPF_BOOTP_CHADDR_LEN ((BOOTP_CHADDR_LEN / 4) * 3)
! 586: #define BPF_BOOTP_XID_LEN 4 /* BOUND check is 4 instructions */
! 587:
! 588: #define BPF_BOOTP_LEN BPF_BOOTP_ETHER_LEN + BPF_BOOTP_FILTER_LEN \
! 589: + BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4
! 590:
! 591: int
! 592: bpf_bootp(struct interface *ifp, int fd)
! 593: {
! 594: const struct dhcp_state *state = D_CSTATE(ifp);
! 595: struct bpf_insn bpf[BPF_BOOTP_LEN];
! 596: struct bpf_insn *bp;
! 597:
! 598: if (fd == -1)
! 599: return 0;
! 600:
! 601: bp = bpf;
! 602: /* Check frame header. */
! 603: switch(ifp->family) {
! 604: case ARPHRD_ETHER:
! 605: memcpy(bp, bpf_bootp_ether, sizeof(bpf_bootp_ether));
! 606: bp += BPF_BOOTP_ETHER_LEN;
! 607: break;
! 608: default:
! 609: errno = EINVAL;
! 610: return -1;
! 611: }
! 612:
! 613: /* Copy in the main filter. */
! 614: memcpy(bp, bpf_bootp_filter, sizeof(bpf_bootp_filter));
! 615: bp += BPF_BOOTP_FILTER_LEN;
! 616:
! 617: if (ifp->hwlen <= sizeof(((struct bootp *)0)->chaddr))
! 618: bp += bpf_cmp_hwaddr(bp, BPF_BOOTP_CHADDR_LEN,
! 619: offsetof(struct bootp, chaddr),
! 620: true, ifp->hwaddr, ifp->hwlen);
! 621:
! 622: /* Make sure the BOOTP packet is for us. */
! 623: if (state->state == DHS_BOUND) {
! 624: /* If bound, we only expect FORCERENEW messages
! 625: * and they need to be unicast to us.
! 626: * Move back to the IP header in M0 and check dst. */
! 627: BPF_SET_STMT(bp, BPF_LDX + BPF_W + BPF_MEM, 0);
! 628: bp++;
! 629: BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
! 630: offsetof(struct ip, ip_dst));
! 631: bp++;
! 632: BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
! 633: htonl(state->lease.addr.s_addr), 1, 0);
! 634: bp++;
! 635: BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
! 636: bp++;
! 637: } else {
! 638: /* As we're not bound, we need to check xid to ensure
! 639: * it's a reply to our transaction. */
! 640: BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
! 641: offsetof(struct bootp, xid));
! 642: bp++;
! 643: BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
! 644: state->xid, 1, 0);
! 645: bp++;
! 646: BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
! 647: bp++;
! 648: }
! 649:
! 650: /* All passed, return the packet
! 651: * (Frame length in M0, IP length in M2). */
! 652: BPF_SET_STMT(bp, BPF_LD + BPF_MEM, 0);
! 653: bp++;
! 654: BPF_SET_STMT(bp, BPF_LDX + BPF_MEM, 2);
! 655: bp++;
! 656: BPF_SET_STMT(bp, BPF_ALU + BPF_ADD + BPF_X, 0);
! 657: bp++;
! 658: BPF_SET_STMT(bp, BPF_RET + BPF_A, 0);
! 659: bp++;
! 660:
! 661: return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
! 662: }
CVSweb <webmaster@jp.NetBSD.org>