[BACK]Return to bpf.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / external / bsd / dhcpcd / dist / src

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>