xref: /dragonfly/contrib/dhcpcd/src/bpf.c (revision a31d3627)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * dhcpcd: BPF arp and bootp filtering
4  * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
5  * All rights reserved
6 
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.
15  *
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.
27  */
28 
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
31 
32 #include <arpa/inet.h>
33 
34 #include <net/if.h>
35 #include <netinet/in.h>
36 #include <netinet/if_ether.h>
37 
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
45 
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <paths.h>
49 #include <stddef.h>
50 #include <stdlib.h>
51 #include <string.h>
52 
53 #include "common.h"
54 #include "arp.h"
55 #include "bpf.h"
56 #include "dhcp.h"
57 #include "if.h"
58 #include "logerr.h"
59 
60 /* BPF helper macros */
61 #ifdef __linux__
62 #define	BPF_WHOLEPACKET		0x7fffffff /* work around buggy LPF filters */
63 #else
64 #define	BPF_WHOLEPACKET		~0U
65 #endif
66 
67 /* Macros to update the BPF structure */
68 #define	BPF_SET_STMT(insn, c, v) {				\
69 	(insn)->code = (c);					\
70 	(insn)->jt = 0;						\
71 	(insn)->jf = 0;						\
72 	(insn)->k = (uint32_t)(v);				\
73 }
74 
75 #define	BPF_SET_JUMP(insn, c, v, t, f) {			\
76 	(insn)->code = (c);					\
77 	(insn)->jt = (t);					\
78 	(insn)->jf = (f);					\
79 	(insn)->k = (uint32_t)(v);				\
80 }
81 
82 size_t
83 bpf_frame_header_len(const struct interface *ifp)
84 {
85 
86 	switch (ifp->hwtype) {
87 	case ARPHRD_ETHER:
88 		return sizeof(struct ether_header);
89 	default:
90 		return 0;
91 	}
92 }
93 
94 void *
95 bpf_frame_header_src(const struct interface *ifp, void *fh, size_t *len)
96 {
97 	uint8_t *f = fh;
98 
99 	switch (ifp->hwtype) {
100 	case ARPHRD_ETHER:
101 		*len = sizeof(((struct ether_header *)0)->ether_shost);
102 		return f + offsetof(struct ether_header, ether_shost);
103 	default:
104 		*len = 0;
105 		errno =	ENOTSUP;
106 		return NULL;
107 	}
108 }
109 
110 void *
111 bpf_frame_header_dst(const struct interface *ifp, void *fh, size_t *len)
112 {
113 	uint8_t *f = fh;
114 
115 	switch (ifp->hwtype) {
116 	case ARPHRD_ETHER:
117 		*len = sizeof(((struct ether_header *)0)->ether_dhost);
118 		return f + offsetof(struct ether_header, ether_dhost);
119 	default:
120 		*len = 0;
121 		errno =	ENOTSUP;
122 		return NULL;
123 	}
124 }
125 
126 static const uint8_t etherbcastaddr[] =
127     { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
128 
129 int
130 bpf_frame_bcast(const struct interface *ifp, const void *frame)
131 {
132 
133 	switch (ifp->hwtype) {
134 	case ARPHRD_ETHER:
135 		return memcmp((const char *)frame +
136 		    offsetof(struct ether_header, ether_dhost),
137 		    etherbcastaddr, sizeof(etherbcastaddr));
138 	default:
139 		return -1;
140 	}
141 }
142 
143 #ifndef __linux__
144 /* Linux is a special snowflake for opening, attaching and reading BPF.
145  * See if-linux.c for the Linux specific BPF functions. */
146 
147 const char *bpf_name = "Berkley Packet Filter";
148 
149 struct bpf *
150 bpf_open(const struct interface *ifp,
151     int (*filter)(const struct bpf *, const struct in_addr *),
152     const struct in_addr *ia)
153 {
154 	struct bpf *bpf;
155 	struct bpf_version pv = { .bv_major = 0, .bv_minor = 0 };
156 	struct ifreq ifr = { .ifr_flags = 0 };
157 	int ibuf_len = 0;
158 #ifdef BIOCIMMEDIATE
159 	unsigned int flags;
160 #endif
161 #ifndef O_CLOEXEC
162 	int fd_opts;
163 #endif
164 
165 	bpf = calloc(1, sizeof(*bpf));
166 	if (bpf == NULL)
167 		return NULL;
168 	bpf->bpf_ifp = ifp;
169 
170 #ifdef _PATH_BPF
171 	bpf->bpf_fd = open(_PATH_BPF, O_RDWR | O_NONBLOCK
172 #ifdef O_CLOEXEC
173 		| O_CLOEXEC
174 #endif
175 	);
176 #else
177 	char device[32];
178 	int n = 0;
179 
180 	do {
181 		snprintf(device, sizeof(device), "/dev/bpf%d", n++);
182 		bpf->bpf_fd = open(device, O_RDWR | O_NONBLOCK
183 #ifdef O_CLOEXEC
184 				| O_CLOEXEC
185 #endif
186 		);
187 	} while (bpf->bpf_fd == -1 && errno == EBUSY);
188 #endif
189 
190 	if (bpf->bpf_fd == -1)
191 		goto eexit;
192 
193 #ifndef O_CLOEXEC
194 	if ((fd_opts = fcntl(bpf->bpf_fd, F_GETFD)) == -1 ||
195 	    fcntl(bpf->bpf_fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1)
196 		goto eexit;
197 #endif
198 
199 	if (ioctl(bpf->bpf_fd, BIOCVERSION, &pv) == -1)
200 		goto eexit;
201 	if (pv.bv_major != BPF_MAJOR_VERSION ||
202 	    pv.bv_minor < BPF_MINOR_VERSION) {
203 		logerrx("BPF version mismatch - recompile");
204 		goto eexit;
205 	}
206 
207 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
208 	if (ioctl(bpf->bpf_fd, BIOCSETIF, &ifr) == -1)
209 		goto eexit;
210 
211 #ifdef BIOCIMMEDIATE
212 	flags = 1;
213 	if (ioctl(bpf->bpf_fd, BIOCIMMEDIATE, &flags) == -1)
214 		goto eexit;
215 #endif
216 
217 	if (filter(bpf, ia) != 0)
218 		goto eexit;
219 
220 	/* Get the required BPF buffer length from the kernel. */
221 	if (ioctl(bpf->bpf_fd, BIOCGBLEN, &ibuf_len) == -1)
222 		goto eexit;
223 	bpf->bpf_size = (size_t)ibuf_len;
224 	bpf->bpf_buffer = malloc(bpf->bpf_size);
225 	if (bpf->bpf_buffer == NULL)
226 		goto eexit;
227 	return bpf;
228 
229 eexit:
230 	if (bpf->bpf_fd != -1)
231 		close(bpf->bpf_fd);
232 	free(bpf);
233 	return NULL;
234 }
235 
236 /* BPF requires that we read the entire buffer.
237  * So we pass the buffer in the API so we can loop on >1 packet. */
238 ssize_t
239 bpf_read(struct bpf *bpf, void *data, size_t len)
240 {
241 	ssize_t bytes;
242 	struct bpf_hdr packet;
243 	const char *payload;
244 
245 	bpf->bpf_flags &= ~BPF_EOF;
246 	for (;;) {
247 		if (bpf->bpf_len == 0) {
248 			bytes = read(bpf->bpf_fd, bpf->bpf_buffer,
249 			    bpf->bpf_size);
250 #if defined(__sun)
251 			/* After 2^31 bytes, the kernel offset overflows.
252 			 * To work around this bug, lseek 0. */
253 			if (bytes == -1 && errno == EINVAL) {
254 				lseek(bpf->bpf_fd, 0, SEEK_SET);
255 				continue;
256 			}
257 #endif
258 			if (bytes == -1 || bytes == 0)
259 				return bytes;
260 			bpf->bpf_len = (size_t)bytes;
261 			bpf->bpf_pos = 0;
262 		}
263 		bytes = -1;
264 		payload = (const char *)bpf->bpf_buffer + bpf->bpf_pos;
265 		memcpy(&packet, payload, sizeof(packet));
266 		if (bpf->bpf_pos + packet.bh_caplen + packet.bh_hdrlen >
267 		    bpf->bpf_len)
268 			goto next; /* Packet beyond buffer, drop. */
269 		payload += packet.bh_hdrlen;
270 		if (packet.bh_caplen > len)
271 			bytes = (ssize_t)len;
272 		else
273 			bytes = (ssize_t)packet.bh_caplen;
274 		if (bpf_frame_bcast(bpf->bpf_ifp, payload) == 0)
275 			bpf->bpf_flags |= BPF_BCAST;
276 		else
277 			bpf->bpf_flags &= ~BPF_BCAST;
278 		memcpy(data, payload, (size_t)bytes);
279 next:
280 		bpf->bpf_pos += BPF_WORDALIGN(packet.bh_hdrlen +
281 		    packet.bh_caplen);
282 		if (bpf->bpf_pos >= bpf->bpf_len) {
283 			bpf->bpf_len = bpf->bpf_pos = 0;
284 			bpf->bpf_flags |= BPF_EOF;
285 		}
286 		if (bytes != -1)
287 			return bytes;
288 	}
289 
290 	/* NOTREACHED */
291 }
292 
293 int
294 bpf_attach(int fd, void *filter, unsigned int filter_len)
295 {
296 	struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };
297 
298 	/* Install the filter. */
299 	return ioctl(fd, BIOCSETF, &pf);
300 }
301 
302 #ifdef BIOCSETWF
303 static int
304 bpf_wattach(int fd, void *filter, unsigned int filter_len)
305 {
306 	struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };
307 
308 	/* Install the filter. */
309 	return ioctl(fd, BIOCSETWF, &pf);
310 }
311 #endif
312 #endif
313 
314 #ifndef __sun
315 /* SunOS is special too - sending via BPF goes nowhere. */
316 ssize_t
317 bpf_send(const struct bpf *bpf, uint16_t protocol,
318     const void *data, size_t len)
319 {
320 	struct iovec iov[2];
321 	struct ether_header eh;
322 
323 	switch(bpf->bpf_ifp->hwtype) {
324 	case ARPHRD_ETHER:
325 		memset(&eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
326 		memcpy(&eh.ether_shost, bpf->bpf_ifp->hwaddr,
327 		    sizeof(eh.ether_shost));
328 		eh.ether_type = htons(protocol);
329 		iov[0].iov_base = &eh;
330 		iov[0].iov_len = sizeof(eh);
331 		break;
332 	default:
333 		iov[0].iov_base = NULL;
334 		iov[0].iov_len = 0;
335 		break;
336 	}
337 	iov[1].iov_base = UNCONST(data);
338 	iov[1].iov_len = len;
339 	return writev(bpf->bpf_fd, iov, 2);
340 }
341 #endif
342 
343 void
344 bpf_close(struct bpf *bpf)
345 {
346 
347 	close(bpf->bpf_fd);
348 	free(bpf->bpf_buffer);
349 	free(bpf);
350 }
351 
352 #ifdef ARP
353 #define BPF_CMP_HWADDR_LEN	((((HWADDR_LEN / 4) + 2) * 2) + 1)
354 static unsigned int
355 bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off,
356     bool equal, const uint8_t *hwaddr, size_t hwaddr_len)
357 {
358 	struct bpf_insn *bp;
359 	size_t maclen, nlft, njmps;
360 	uint32_t mac32;
361 	uint16_t mac16;
362 	uint8_t jt, jf;
363 
364 	/* Calc the number of jumps */
365 	if ((hwaddr_len / 4) >= 128) {
366 		errno = EINVAL;
367 		return 0;
368 	}
369 	njmps = (hwaddr_len / 4) * 2; /* 2 instructions per check */
370 	/* We jump after the 1st check. */
371 	if (njmps)
372 		njmps -= 2;
373 	nlft = hwaddr_len % 4;
374 	if (nlft) {
375 		njmps += (nlft / 2) * 2;
376 		nlft = nlft % 2;
377 		if (nlft)
378 			njmps += 2;
379 
380 	}
381 
382 	/* Skip to positive finish. */
383 	njmps++;
384 	if (equal) {
385 		jt = (uint8_t)njmps;
386 		jf = 0;
387 	} else {
388 		jt = 0;
389 		jf = (uint8_t)njmps;
390 	}
391 
392 	bp = bpf;
393 	for (; hwaddr_len > 0;
394 	     hwaddr += maclen, hwaddr_len -= maclen, off += maclen)
395 	{
396 		if (bpf_len < 3) {
397 			errno = ENOBUFS;
398 			return 0;
399 		}
400 		bpf_len -= 3;
401 
402 		if (hwaddr_len >= 4) {
403 			maclen = sizeof(mac32);
404 			memcpy(&mac32, hwaddr, maclen);
405 			BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND, off);
406 			bp++;
407 			BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
408 			             htonl(mac32), jt, jf);
409 		} else if (hwaddr_len >= 2) {
410 			maclen = sizeof(mac16);
411 			memcpy(&mac16, hwaddr, maclen);
412 			BPF_SET_STMT(bp, BPF_LD + BPF_H + BPF_IND, off);
413 			bp++;
414 			BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
415 			             htons(mac16), jt, jf);
416 		} else {
417 			maclen = sizeof(*hwaddr);
418 			BPF_SET_STMT(bp, BPF_LD + BPF_B + BPF_IND, off);
419 			bp++;
420 			BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
421 			             *hwaddr, jt, jf);
422 		}
423 		if (jt)
424 			jt = (uint8_t)(jt - 2);
425 		if (jf)
426 			jf = (uint8_t)(jf - 2);
427 		bp++;
428 	}
429 
430 	/* Last step is always return failure.
431 	 * Next step is a positive finish. */
432 	BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
433 	bp++;
434 
435 	return (unsigned int)(bp - bpf);
436 }
437 #endif
438 
439 #ifdef ARP
440 static const struct bpf_insn bpf_arp_ether [] = {
441 	/* Check this is an ARP packet. */
442 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
443 	         offsetof(struct ether_header, ether_type)),
444 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 1, 0),
445 	BPF_STMT(BPF_RET + BPF_K, 0),
446 
447 	/* Load frame header length into X */
448 	BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
449 
450 	/* Make sure the hardware type matches. */
451 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_hrd)),
452 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0),
453 	BPF_STMT(BPF_RET + BPF_K, 0),
454 
455 	/* Make sure the hardware length matches. */
456 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_hln)),
457 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,
458 	         sizeof(((struct ether_arp *)0)->arp_sha), 1, 0),
459 	BPF_STMT(BPF_RET + BPF_K, 0),
460 };
461 #define BPF_ARP_ETHER_LEN	__arraycount(bpf_arp_ether)
462 
463 static const struct bpf_insn bpf_arp_filter [] = {
464 	/* Make sure this is for IP. */
465 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_pro)),
466 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
467 	BPF_STMT(BPF_RET + BPF_K, 0),
468 	/* Make sure this is an ARP REQUEST. */
469 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_op)),
470 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),
471 	/* or ARP REPLY. */
472 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0),
473 	BPF_STMT(BPF_RET + BPF_K, 0),
474 	/* Make sure the protocol length matches. */
475 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_pln)),
476 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(in_addr_t), 1, 0),
477 	BPF_STMT(BPF_RET + BPF_K, 0),
478 };
479 #define BPF_ARP_FILTER_LEN	__arraycount(bpf_arp_filter)
480 
481 /* One address is two checks of two statements. */
482 #define BPF_NADDRS		1
483 #define BPF_ARP_ADDRS_LEN	5 + ((BPF_NADDRS * 2) * 2)
484 
485 #define BPF_ARP_LEN		BPF_ARP_ETHER_LEN + BPF_ARP_FILTER_LEN + \
486 				BPF_CMP_HWADDR_LEN + BPF_ARP_ADDRS_LEN
487 
488 static int
489 bpf_arp_rw(const struct bpf *bpf, const struct in_addr *ia, bool recv)
490 {
491 	const struct interface *ifp = bpf->bpf_ifp;
492 	struct bpf_insn buf[BPF_ARP_LEN + 1];
493 	struct bpf_insn *bp;
494 	uint16_t arp_len;
495 
496 	bp = buf;
497 	/* Check frame header. */
498 	switch(ifp->hwtype) {
499 	case ARPHRD_ETHER:
500 		memcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether));
501 		bp += BPF_ARP_ETHER_LEN;
502 		arp_len = sizeof(struct ether_header)+sizeof(struct ether_arp);
503 		break;
504 	default:
505 		errno = EINVAL;
506 		return -1;
507 	}
508 
509 	/* Copy in the main filter. */
510 	memcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter));
511 	bp += BPF_ARP_FILTER_LEN;
512 
513 	/* Ensure it's not from us. */
514 	bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr),
515 	                     !recv, ifp->hwaddr, ifp->hwlen);
516 
517 	/* Match sender protocol address */
518 	BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
519 	    sizeof(struct arphdr) + ifp->hwlen);
520 	bp++;
521 	BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);
522 	bp++;
523 	BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
524 	bp++;
525 
526 	/* If we didn't match sender, then we're only interested in
527 	 * ARP probes to us, so check the null host sender. */
528 	BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, INADDR_ANY, 1, 0);
529 	bp++;
530 	BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
531 	bp++;
532 
533 	/* Match target protocol address */
534 	BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND, (sizeof(struct arphdr) +
535 	    (size_t)(ifp->hwlen * 2) + sizeof(in_addr_t)));
536 	bp++;
537 	BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);
538 	bp++;
539 	BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
540 	bp++;
541 
542 	/* No match, drop it */
543 	BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
544 	bp++;
545 
546 #ifdef BIOCSETWF
547 	if (!recv)
548 		return bpf_wattach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
549 #endif
550 
551 	return bpf_attach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
552 }
553 
554 int
555 bpf_arp(const struct bpf *bpf, const struct in_addr *ia)
556 {
557 
558 #ifdef BIOCSETWF
559 	if (bpf_arp_rw(bpf, ia, true) == -1 ||
560 	    bpf_arp_rw(bpf, ia, false) == -1 ||
561 	    ioctl(bpf->bpf_fd, BIOCLOCK) == -1)
562 		return -1;
563 	return 0;
564 #else
565 	return bpf_arp_rw(bpf, ia, true);
566 #endif
567 }
568 #endif
569 
570 #ifdef ARPHRD_NONE
571 static const struct bpf_insn bpf_bootp_none[] = {
572 };
573 #define BPF_BOOTP_NONE_LEN	__arraycount(bpf_bootp_none)
574 #endif
575 
576 static const struct bpf_insn bpf_bootp_ether[] = {
577 	/* Make sure this is an IP packet. */
578 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
579 	         offsetof(struct ether_header, ether_type)),
580 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
581 	BPF_STMT(BPF_RET + BPF_K, 0),
582 
583 	/* Advance to the IP header. */
584 	BPF_STMT(BPF_LDX + BPF_K, sizeof(struct ether_header)),
585 };
586 #define BPF_BOOTP_ETHER_LEN	__arraycount(bpf_bootp_ether)
587 
588 static const struct bpf_insn bpf_bootp_base[] = {
589 	/* Make sure it's an IPv4 packet. */
590 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
591 	BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0xf0),
592 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x40, 1, 0),
593 	BPF_STMT(BPF_RET + BPF_K, 0),
594 
595 	/* Make sure it's a UDP packet. */
596 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct ip, ip_p)),
597 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),
598 	BPF_STMT(BPF_RET + BPF_K, 0),
599 
600 	/* Make sure this isn't a fragment. */
601 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_off)),
602 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1),
603 	BPF_STMT(BPF_RET + BPF_K, 0),
604 
605 	/* Advance to the UDP header. */
606 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
607 	BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x0f),
608 	BPF_STMT(BPF_ALU + BPF_MUL + BPF_K, 4),
609 	BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),
610 	BPF_STMT(BPF_MISC + BPF_TAX, 0),
611 };
612 #define BPF_BOOTP_BASE_LEN	__arraycount(bpf_bootp_base)
613 
614 static const struct bpf_insn bpf_bootp_read[] = {
615 	/* Make sure it's from and to the right port. */
616 	BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
617 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPS << 16) + BOOTPC, 1, 0),
618 	BPF_STMT(BPF_RET + BPF_K, 0),
619 };
620 #define BPF_BOOTP_READ_LEN	__arraycount(bpf_bootp_read)
621 
622 #ifdef BIOCSETWF
623 static const struct bpf_insn bpf_bootp_write[] = {
624 	/* Make sure it's from and to the right port. */
625 	BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
626 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPC << 16) + BOOTPS, 1, 0),
627 	BPF_STMT(BPF_RET + BPF_K, 0),
628 };
629 #define BPF_BOOTP_WRITE_LEN	__arraycount(bpf_bootp_write)
630 #endif
631 
632 #define BPF_BOOTP_CHADDR_LEN	((BOOTP_CHADDR_LEN / 4) * 3)
633 #define	BPF_BOOTP_XID_LEN	4 /* BOUND check is 4 instructions */
634 
635 #define BPF_BOOTP_LEN		BPF_BOOTP_ETHER_LEN + \
636 				BPF_BOOTP_BASE_LEN + BPF_BOOTP_READ_LEN + \
637 				BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4
638 
639 static int
640 bpf_bootp_rw(const struct bpf *bpf, bool read)
641 {
642 	struct bpf_insn buf[BPF_BOOTP_LEN + 1];
643 	struct bpf_insn *bp;
644 
645 	bp = buf;
646 	/* Check frame header. */
647 	switch(bpf->bpf_ifp->hwtype) {
648 #ifdef ARPHRD_NONE
649 	case ARPHRD_NONE:
650 		memcpy(bp, bpf_bootp_none, sizeof(bpf_bootp_none));
651 		bp += BPF_BOOTP_NONE_LEN;
652 		break;
653 #endif
654 	case ARPHRD_ETHER:
655 		memcpy(bp, bpf_bootp_ether, sizeof(bpf_bootp_ether));
656 		bp += BPF_BOOTP_ETHER_LEN;
657 		break;
658 	default:
659 		errno = EINVAL;
660 		return -1;
661 	}
662 
663 	/* Copy in the main filter. */
664 	memcpy(bp, bpf_bootp_base, sizeof(bpf_bootp_base));
665 	bp += BPF_BOOTP_BASE_LEN;
666 
667 #ifdef BIOCSETWF
668 	if (!read) {
669 		memcpy(bp, bpf_bootp_write, sizeof(bpf_bootp_write));
670 		bp += BPF_BOOTP_WRITE_LEN;
671 
672 		/* All passed, return the packet. */
673 		BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
674 		bp++;
675 
676 		return bpf_wattach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
677 	}
678 #else
679 	UNUSED(read);
680 #endif
681 
682 	memcpy(bp, bpf_bootp_read, sizeof(bpf_bootp_read));
683 	bp += BPF_BOOTP_READ_LEN;
684 
685 	/* All passed, return the packet. */
686 	BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
687 	bp++;
688 
689 	return bpf_attach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
690 }
691 
692 int
693 bpf_bootp(const struct bpf *bpf, __unused const struct in_addr *ia)
694 {
695 
696 #ifdef BIOCSETWF
697 	if (bpf_bootp_rw(bpf, true) == -1 ||
698 	    bpf_bootp_rw(bpf, false) == -1 ||
699 	    ioctl(bpf->bpf_fd, BIOCLOCK) == -1)
700 		return -1;
701 	return 0;
702 #else
703 #ifdef PRIVSEP
704 #if defined(__sun) /* Solaris cannot send via BPF. */
705 #elif defined(BIOCSETF)
706 #warning No BIOCSETWF support - a compromised BPF can be used as a raw socket
707 #else
708 #warning A compromised PF_PACKET socket can be used as a raw socket
709 #endif
710 #endif
711 	return bpf_bootp_rw(bpf, true);
712 #endif
713 }
714