Import dhcpcd-8.0.4 to vendor branch.
[dragonfly.git] / contrib / dhcpcd / src / bpf.c
blob06bbf3f1e1b5e1b1c2c2fc62de192c6ab147271e
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3 * dhcpcd: BPF arp and bootp filtering
4 * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
5 * All rights reserved
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
32 #include <arpa/inet.h>
34 #include <net/if.h>
35 #include <netinet/in.h>
36 #include <netinet/if_ether.h>
38 #ifdef __linux__
39 /* Special BPF snowflake. */
40 #include <linux/filter.h>
41 #define bpf_insn sock_filter
42 #else
43 #include <net/bpf.h>
44 #endif
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <paths.h>
49 #include <stddef.h>
50 #include <stdlib.h>
51 #include <string.h>
53 #include "common.h"
54 #include "arp.h"
55 #include "bpf.h"
56 #include "dhcp.h"
57 #include "if.h"
58 #include "logerr.h"
60 #define ARP_ADDRS_MAX 3
62 /* BPF helper macros */
63 #ifdef __linux__
64 #define BPF_WHOLEPACKET 0x7fffffff /* work around buggy LPF filters */
65 #else
66 #define BPF_WHOLEPACKET ~0U
67 #endif
69 /* Macros to update the BPF structure */
70 #define BPF_SET_STMT(insn, c, v) { \
71 (insn)->code = (c); \
72 (insn)->jt = 0; \
73 (insn)->jf = 0; \
74 (insn)->k = (uint32_t)(v); \
77 #define BPF_SET_JUMP(insn, c, v, t, f) { \
78 (insn)->code = (c); \
79 (insn)->jt = (t); \
80 (insn)->jf = (f); \
81 (insn)->k = (uint32_t)(v); \
84 size_t
85 bpf_frame_header_len(const struct interface *ifp)
88 switch (ifp->family) {
89 case ARPHRD_ETHER:
90 return sizeof(struct ether_header);
91 default:
92 return 0;
96 static const uint8_t etherbcastaddr[] =
97 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
99 int
100 bpf_frame_bcast(const struct interface *ifp, const char *frame)
103 switch (ifp->family) {
104 case ARPHRD_ETHER:
105 return memcmp(frame +
106 offsetof(struct ether_header, ether_dhost),
107 etherbcastaddr, sizeof(etherbcastaddr));
108 default:
109 return -1;
113 #ifndef __linux__
114 /* Linux is a special snowflake for opening, attaching and reading BPF.
115 * See if-linux.c for the Linux specific BPF functions. */
117 const char *bpf_name = "Berkley Packet Filter";
120 bpf_open(struct interface *ifp, int (*filter)(struct interface *, int))
122 struct ipv4_state *state;
123 int fd = -1;
124 struct ifreq ifr;
125 int ibuf_len = 0;
126 size_t buf_len;
127 struct bpf_version pv;
128 #ifdef BIOCIMMEDIATE
129 unsigned int flags;
130 #endif
131 #ifndef O_CLOEXEC
132 int fd_opts;
133 #endif
135 #ifdef _PATH_BPF
136 fd = open(_PATH_BPF, O_RDWR | O_NONBLOCK
137 #ifdef O_CLOEXEC
138 | O_CLOEXEC
139 #endif
141 #else
142 char device[32];
143 int n = 0;
145 do {
146 snprintf(device, sizeof(device), "/dev/bpf%d", n++);
147 fd = open(device, O_RDWR | O_NONBLOCK
148 #ifdef O_CLOEXEC
149 | O_CLOEXEC
150 #endif
152 } while (fd == -1 && errno == EBUSY);
153 #endif
155 if (fd == -1)
156 return -1;
158 #ifndef O_CLOEXEC
159 if ((fd_opts = fcntl(fd, F_GETFD)) == -1 ||
160 fcntl(fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1) {
161 close(fd);
162 return -1;
164 #endif
166 memset(&pv, 0, sizeof(pv));
167 if (ioctl(fd, BIOCVERSION, &pv) == -1)
168 goto eexit;
169 if (pv.bv_major != BPF_MAJOR_VERSION ||
170 pv.bv_minor < BPF_MINOR_VERSION) {
171 logerrx("BPF version mismatch - recompile");
172 goto eexit;
175 if (filter(ifp, fd) != 0)
176 goto eexit;
178 memset(&ifr, 0, sizeof(ifr));
179 strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
180 if (ioctl(fd, BIOCSETIF, &ifr) == -1)
181 goto eexit;
183 /* Get the required BPF buffer length from the kernel. */
184 if (ioctl(fd, BIOCGBLEN, &ibuf_len) == -1)
185 goto eexit;
186 buf_len = (size_t)ibuf_len;
187 state = ipv4_getstate(ifp);
188 if (state == NULL)
189 goto eexit;
190 if (state->buffer_size != buf_len) {
191 void *nb;
193 if ((nb = realloc(state->buffer, buf_len)) == NULL)
194 goto eexit;
195 state->buffer = nb;
196 state->buffer_size = buf_len;
199 #ifdef BIOCIMMEDIATE
200 flags = 1;
201 if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1)
202 goto eexit;
203 #endif
205 return fd;
207 eexit:
208 close(fd);
209 return -1;
212 /* BPF requires that we read the entire buffer.
213 * So we pass the buffer in the API so we can loop on >1 packet. */
214 ssize_t
215 bpf_read(struct interface *ifp, int fd, void *data, size_t len,
216 unsigned int *flags)
218 ssize_t fl = (ssize_t)bpf_frame_header_len(ifp);
219 ssize_t bytes;
220 struct ipv4_state *state = IPV4_STATE(ifp);
222 struct bpf_hdr packet;
223 const char *payload;
225 *flags &= ~BPF_EOF;
226 for (;;) {
227 if (state->buffer_len == 0) {
228 bytes = read(fd, state->buffer, state->buffer_size);
229 #if defined(__sun)
230 /* After 2^31 bytes, the kernel offset overflows.
231 * To work around this bug, lseek 0. */
232 if (bytes == -1 && errno == EINVAL) {
233 lseek(fd, 0, SEEK_SET);
234 continue;
236 #endif
237 if (bytes == -1 || bytes == 0)
238 return bytes;
239 state->buffer_len = (size_t)bytes;
240 state->buffer_pos = 0;
242 bytes = -1;
243 memcpy(&packet, state->buffer + state->buffer_pos,
244 sizeof(packet));
245 if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
246 state->buffer_len)
247 goto next; /* Packet beyond buffer, drop. */
248 payload = state->buffer + state->buffer_pos + packet.bh_hdrlen;
249 if (bpf_frame_bcast(ifp, payload) == 0)
250 *flags |= BPF_BCAST;
251 else
252 *flags &= ~BPF_BCAST;
253 payload += fl;
254 bytes = (ssize_t)packet.bh_caplen - fl;
255 if ((size_t)bytes > len)
256 bytes = (ssize_t)len;
257 memcpy(data, payload, (size_t)bytes);
258 next:
259 state->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
260 packet.bh_caplen);
261 if (state->buffer_pos >= state->buffer_len) {
262 state->buffer_len = state->buffer_pos = 0;
263 *flags |= BPF_EOF;
265 if (bytes != -1)
266 return bytes;
269 /* NOTREACHED */
273 bpf_attach(int fd, void *filter, unsigned int filter_len)
275 struct bpf_program pf;
277 /* Install the filter. */
278 memset(&pf, 0, sizeof(pf));
279 pf.bf_insns = filter;
280 pf.bf_len = filter_len;
281 return ioctl(fd, BIOCSETF, &pf);
283 #endif
285 #ifndef __sun
286 /* SunOS is special too - sending via BPF goes nowhere. */
287 ssize_t
288 bpf_send(const struct interface *ifp, int fd, uint16_t protocol,
289 const void *data, size_t len)
291 struct iovec iov[2];
292 struct ether_header eh;
294 switch(ifp->family) {
295 case ARPHRD_ETHER:
296 memset(&eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
297 memcpy(&eh.ether_shost, ifp->hwaddr, sizeof(eh.ether_shost));
298 eh.ether_type = htons(protocol);
299 iov[0].iov_base = &eh;
300 iov[0].iov_len = sizeof(eh);
301 break;
302 default:
303 iov[0].iov_base = NULL;
304 iov[0].iov_len = 0;
305 break;
307 iov[1].iov_base = UNCONST(data);
308 iov[1].iov_len = len;
309 return writev(fd, iov, 2);
311 #endif
314 bpf_close(struct interface *ifp, int fd)
316 struct ipv4_state *state = IPV4_STATE(ifp);
318 /* Rewind the buffer on closing. */
319 state->buffer_len = state->buffer_pos = 0;
320 return close(fd);
323 /* Normally this is needed by bootp.
324 * Once that uses this again, the ARP guard here can be removed. */
325 #ifdef ARP
326 #define BPF_CMP_HWADDR_LEN ((((HWADDR_LEN / 4) + 2) * 2) + 1)
327 static unsigned int
328 bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off,
329 bool equal, uint8_t *hwaddr, size_t hwaddr_len)
331 struct bpf_insn *bp;
332 size_t maclen, nlft, njmps;
333 uint32_t mac32;
334 uint16_t mac16;
335 uint8_t jt, jf;
337 /* Calc the number of jumps */
338 if ((hwaddr_len / 4) >= 128) {
339 errno = EINVAL;
340 return 0;
342 njmps = (hwaddr_len / 4) * 2; /* 2 instructions per check */
343 /* We jump after the 1st check. */
344 if (njmps)
345 njmps -= 2;
346 nlft = hwaddr_len % 4;
347 if (nlft) {
348 njmps += (nlft / 2) * 2;
349 nlft = nlft % 2;
350 if (nlft)
351 njmps += 2;
355 /* Skip to positive finish. */
356 njmps++;
357 if (equal) {
358 jt = (uint8_t)njmps;
359 jf = 0;
360 } else {
361 jt = 0;
362 jf = (uint8_t)njmps;
365 bp = bpf;
366 for (; hwaddr_len > 0;
367 hwaddr += maclen, hwaddr_len -= maclen, off += maclen)
369 if (bpf_len < 3) {
370 errno = ENOBUFS;
371 return 0;
373 bpf_len -= 3;
375 if (hwaddr_len >= 4) {
376 maclen = sizeof(mac32);
377 memcpy(&mac32, hwaddr, maclen);
378 BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND, off);
379 bp++;
380 BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
381 htonl(mac32), jt, jf);
382 } else if (hwaddr_len >= 2) {
383 maclen = sizeof(mac16);
384 memcpy(&mac16, hwaddr, maclen);
385 BPF_SET_STMT(bp, BPF_LD + BPF_H + BPF_IND, off);
386 bp++;
387 BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
388 htons(mac16), jt, jf);
389 } else {
390 maclen = sizeof(*hwaddr);
391 BPF_SET_STMT(bp, BPF_LD + BPF_B + BPF_IND, off);
392 bp++;
393 BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
394 *hwaddr, jt, jf);
396 if (jt)
397 jt = (uint8_t)(jt - 2);
398 if (jf)
399 jf = (uint8_t)(jf - 2);
400 bp++;
403 /* Last step is always return failure.
404 * Next step is a positive finish. */
405 BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
406 bp++;
408 return (unsigned int)(bp - bpf);
410 #endif
412 #ifdef ARP
414 static const struct bpf_insn bpf_arp_ether [] = {
415 /* Ensure packet is at least correct size. */
416 BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),
417 BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0),
418 BPF_STMT(BPF_RET + BPF_K, 0),
420 /* Check this is an ARP packet. */
421 BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
422 offsetof(struct ether_header, ether_type)),
423 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 1, 0),
424 BPF_STMT(BPF_RET + BPF_K, 0),
426 /* Load frame header length into X */
427 BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
429 /* Make sure the hardware family matches. */
430 BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_hrd)),
431 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0),
432 BPF_STMT(BPF_RET + BPF_K, 0),
434 /* Make sure the hardware length matches. */
435 BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_hln)),
436 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,
437 sizeof(((struct ether_arp *)0)->arp_sha), 1, 0),
438 BPF_STMT(BPF_RET + BPF_K, 0),
440 #define BPF_ARP_ETHER_LEN __arraycount(bpf_arp_ether)
442 static const struct bpf_insn bpf_arp_filter [] = {
443 /* Make sure this is for IP. */
444 BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_pro)),
445 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
446 BPF_STMT(BPF_RET + BPF_K, 0),
447 /* Make sure this is an ARP REQUEST. */
448 BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_op)),
449 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),
450 /* or ARP REPLY. */
451 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0),
452 BPF_STMT(BPF_RET + BPF_K, 0),
453 /* Make sure the protocol length matches. */
454 BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_pln)),
455 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(in_addr_t), 1, 0),
456 BPF_STMT(BPF_RET + BPF_K, 0),
458 #define BPF_ARP_FILTER_LEN __arraycount(bpf_arp_filter)
460 #define BPF_ARP_ADDRS_LEN 1 + (ARP_ADDRS_MAX * 2) + 3 + \
461 (ARP_ADDRS_MAX * 2) + 1
463 #define BPF_ARP_LEN BPF_ARP_ETHER_LEN + BPF_ARP_FILTER_LEN + \
464 BPF_CMP_HWADDR_LEN + BPF_ARP_ADDRS_LEN
467 bpf_arp(struct interface *ifp, int fd)
469 struct bpf_insn bpf[BPF_ARP_LEN];
470 struct bpf_insn *bp;
471 struct iarp_state *state;
472 uint16_t arp_len;
474 if (fd == -1)
475 return 0;
477 bp = bpf;
478 /* Check frame header. */
479 switch(ifp->family) {
480 case ARPHRD_ETHER:
481 memcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether));
482 bp += BPF_ARP_ETHER_LEN;
483 arp_len = sizeof(struct ether_header)+sizeof(struct ether_arp);
484 break;
485 default:
486 errno = EINVAL;
487 return -1;
490 /* Copy in the main filter. */
491 memcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter));
492 bp += BPF_ARP_FILTER_LEN;
494 /* Ensure it's not from us. */
495 bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr),
496 false, ifp->hwaddr, ifp->hwlen);
498 state = ARP_STATE(ifp);
499 if (TAILQ_FIRST(&state->arp_states)) {
500 struct arp_state *astate;
501 size_t naddrs;
503 /* Match sender protocol address */
504 BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
505 sizeof(struct arphdr) + ifp->hwlen);
506 bp++;
507 naddrs = 0;
508 TAILQ_FOREACH(astate, &state->arp_states, next) {
509 if (++naddrs > ARP_ADDRS_MAX) {
510 errno = ENOBUFS;
511 logerr(__func__);
512 break;
514 BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
515 htonl(astate->addr.s_addr), 0, 1);
516 bp++;
517 BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
518 bp++;
521 /* If we didn't match sender, then we're only interested in
522 * ARP probes to us, so check the null host sender. */
523 BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, INADDR_ANY, 1, 0);
524 bp++;
525 BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
526 bp++;
528 /* Match target protocol address */
529 BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
530 (sizeof(struct arphdr)
531 + (size_t)(ifp->hwlen * 2) + sizeof(in_addr_t)));
532 bp++;
533 naddrs = 0;
534 TAILQ_FOREACH(astate, &state->arp_states, next) {
535 if (++naddrs > ARP_ADDRS_MAX) {
536 /* Already logged error above. */
537 break;
539 BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
540 htonl(astate->addr.s_addr), 0, 1);
541 bp++;
542 BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
543 bp++;
546 /* Return nothing, no protocol address match. */
547 BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
548 bp++;
551 return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));
553 #endif
555 #define BPF_M_FHLEN 0
556 #define BPF_M_IPHLEN 1
557 #define BPF_M_IPLEN 2
558 #define BPF_M_UDP 3
559 #define BPF_M_UDPLEN 4
561 #ifdef ARPHRD_NONE
562 static const struct bpf_insn bpf_bootp_none[] = {
563 /* Set the frame header length to zero. */
564 BPF_STMT(BPF_LD + BPF_IMM, 0),
565 BPF_STMT(BPF_ST, BPF_M_FHLEN),
567 #define BPF_BOOTP_NONE_LEN __arraycount(bpf_bootp_none)
568 #endif
570 static const struct bpf_insn bpf_bootp_ether[] = {
571 /* Make sure this is an IP packet. */
572 BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
573 offsetof(struct ether_header, ether_type)),
574 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
575 BPF_STMT(BPF_RET + BPF_K, 0),
577 /* Load frame header length into X. */
578 BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
579 /* Copy frame header length to memory */
580 BPF_STMT(BPF_STX, BPF_M_FHLEN),
582 #define BPF_BOOTP_ETHER_LEN __arraycount(bpf_bootp_ether)
584 static const struct bpf_insn bpf_bootp_filter[] = {
585 /* Make sure it's an IPv4 packet. */
586 BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
587 BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0xf0),
588 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x40, 1, 0),
589 BPF_STMT(BPF_RET + BPF_K, 0),
591 /* Ensure IP header length is big enough and
592 * store the IP header length in memory. */
593 BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
594 BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x0f),
595 BPF_STMT(BPF_ALU + BPF_MUL + BPF_K, 4),
596 BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ip), 1, 0),
597 BPF_STMT(BPF_RET + BPF_K, 0),
598 BPF_STMT(BPF_ST, BPF_M_IPHLEN),
600 /* Make sure it's a UDP packet. */
601 BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct ip, ip_p)),
602 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),
603 BPF_STMT(BPF_RET + BPF_K, 0),
605 /* Make sure this isn't a fragment. */
606 BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_off)),
607 BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1),
608 BPF_STMT(BPF_RET + BPF_K, 0),
610 /* Store IP length. */
611 BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_len)),
612 BPF_STMT(BPF_ST, BPF_M_IPLEN),
614 /* Advance to the UDP header. */
615 BPF_STMT(BPF_LD + BPF_MEM, BPF_M_IPHLEN),
616 BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),
617 BPF_STMT(BPF_MISC + BPF_TAX, 0),
619 /* Store UDP location */
620 BPF_STMT(BPF_STX, BPF_M_UDP),
622 /* Make sure it's from and to the right port. */
623 BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
624 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPS << 16) + BOOTPC, 1, 0),
625 BPF_STMT(BPF_RET + BPF_K, 0),
627 /* Store UDP length. */
628 BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct udphdr, uh_ulen)),
629 BPF_STMT(BPF_ST, BPF_M_UDPLEN),
631 /* Ensure that UDP length + IP header length == IP length */
632 /* Copy IP header length to X. */
633 BPF_STMT(BPF_LDX + BPF_MEM, BPF_M_IPHLEN),
634 /* Add UDP length (A) to IP header length (X). */
635 BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),
636 /* Store result in X. */
637 BPF_STMT(BPF_MISC + BPF_TAX, 0),
638 /* Copy IP length to A. */
639 BPF_STMT(BPF_LD + BPF_MEM, BPF_M_IPLEN),
640 /* Ensure X == A. */
641 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0),
642 BPF_STMT(BPF_RET + BPF_K, 0),
644 /* Advance to the BOOTP packet. */
645 BPF_STMT(BPF_LD + BPF_MEM, BPF_M_UDP),
646 BPF_STMT(BPF_ALU + BPF_ADD + BPF_K, sizeof(struct udphdr)),
647 BPF_STMT(BPF_MISC + BPF_TAX, 0),
649 /* Make sure it's BOOTREPLY. */
650 BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct bootp, op)),
651 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0),
652 BPF_STMT(BPF_RET + BPF_K, 0),
655 #define BPF_BOOTP_FILTER_LEN __arraycount(bpf_bootp_filter)
656 #define BPF_BOOTP_CHADDR_LEN ((BOOTP_CHADDR_LEN / 4) * 3)
657 #define BPF_BOOTP_XID_LEN 4 /* BOUND check is 4 instructions */
659 #define BPF_BOOTP_LEN BPF_BOOTP_ETHER_LEN + BPF_BOOTP_FILTER_LEN \
660 + BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4
663 bpf_bootp(struct interface *ifp, int fd)
665 #if 0
666 const struct dhcp_state *state = D_CSTATE(ifp);
667 #endif
668 struct bpf_insn bpf[BPF_BOOTP_LEN];
669 struct bpf_insn *bp;
671 if (fd == -1)
672 return 0;
674 bp = bpf;
675 /* Check frame header. */
676 switch(ifp->family) {
677 #ifdef ARPHRD_NONE
678 case ARPHRD_NONE:
679 memcpy(bp, bpf_bootp_none, sizeof(bpf_bootp_none));
680 bp += BPF_BOOTP_NONE_LEN;
681 break;
682 #endif
683 case ARPHRD_ETHER:
684 memcpy(bp, bpf_bootp_ether, sizeof(bpf_bootp_ether));
685 bp += BPF_BOOTP_ETHER_LEN;
686 break;
687 default:
688 errno = EINVAL;
689 return -1;
692 /* Copy in the main filter. */
693 memcpy(bp, bpf_bootp_filter, sizeof(bpf_bootp_filter));
694 bp += BPF_BOOTP_FILTER_LEN;
696 /* These checks won't work when same IP exists on other interfaces. */
697 #if 0
698 if (ifp->hwlen <= sizeof(((struct bootp *)0)->chaddr))
699 bp += bpf_cmp_hwaddr(bp, BPF_BOOTP_CHADDR_LEN,
700 offsetof(struct bootp, chaddr),
701 true, ifp->hwaddr, ifp->hwlen);
703 /* Make sure the BOOTP packet is for us. */
704 if (state->state == DHS_BOUND) {
705 /* If bound, we only expect FORCERENEW messages
706 * and they need to be unicast to us.
707 * Move back to the IP header in M0 and check dst. */
708 BPF_SET_STMT(bp, BPF_LDX + BPF_W + BPF_MEM, 0);
709 bp++;
710 BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
711 offsetof(struct ip, ip_dst));
712 bp++;
713 BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
714 htonl(state->lease.addr.s_addr), 1, 0);
715 bp++;
716 BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
717 bp++;
718 } else {
719 /* As we're not bound, we need to check xid to ensure
720 * it's a reply to our transaction. */
721 BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
722 offsetof(struct bootp, xid));
723 bp++;
724 BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
725 state->xid, 1, 0);
726 bp++;
727 BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
728 bp++;
730 #endif
732 /* All passed, return the packet - frame length + ip length */
733 BPF_SET_STMT(bp, BPF_LD + BPF_MEM, BPF_M_FHLEN);
734 bp++;
735 BPF_SET_STMT(bp, BPF_LDX + BPF_MEM, BPF_M_IPLEN);
736 bp++;
737 BPF_SET_STMT(bp, BPF_ALU + BPF_ADD + BPF_X, 0);
738 bp++;
739 BPF_SET_STMT(bp, BPF_RET + BPF_A, 0);
740 bp++;
742 return bpf_attach(fd, bpf, (unsigned int)(bp - bpf));