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