xref: /openbsd-src/sbin/dhcpleased/bpf.c (revision 8e2e76e822b65b23c7ae5c633b7c0714b2950c5e)
1*8e2e76e8Sflorian /*	$OpenBSD: bpf.c,v 1.2 2021/03/02 19:20:13 florian Exp $	*/
257419a7fSflorian 
357419a7fSflorian /* BPF socket interface code, originally contributed by Archie Cobbs. */
457419a7fSflorian 
557419a7fSflorian /*
657419a7fSflorian  * Copyright (c) 1995, 1996, 1998, 1999
757419a7fSflorian  * The Internet Software Consortium.    All rights reserved.
857419a7fSflorian  *
957419a7fSflorian  * Redistribution and use in source and binary forms, with or without
1057419a7fSflorian  * modification, are permitted provided that the following conditions
1157419a7fSflorian  * are met:
1257419a7fSflorian  *
1357419a7fSflorian  * 1. Redistributions of source code must retain the above copyright
1457419a7fSflorian  *    notice, this list of conditions and the following disclaimer.
1557419a7fSflorian  * 2. Redistributions in binary form must reproduce the above copyright
1657419a7fSflorian  *    notice, this list of conditions and the following disclaimer in the
1757419a7fSflorian  *    documentation and/or other materials provided with the distribution.
1857419a7fSflorian  * 3. Neither the name of The Internet Software Consortium nor the names
1957419a7fSflorian  *    of its contributors may be used to endorse or promote products derived
2057419a7fSflorian  *    from this software without specific prior written permission.
2157419a7fSflorian  *
2257419a7fSflorian  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
2357419a7fSflorian  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
2457419a7fSflorian  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2557419a7fSflorian  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2657419a7fSflorian  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
2757419a7fSflorian  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2857419a7fSflorian  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2957419a7fSflorian  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
3057419a7fSflorian  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
3157419a7fSflorian  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3257419a7fSflorian  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3357419a7fSflorian  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3457419a7fSflorian  * SUCH DAMAGE.
3557419a7fSflorian  *
3657419a7fSflorian  * This software has been written for the Internet Software Consortium
3757419a7fSflorian  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
3857419a7fSflorian  * Enterprises.  To learn more about the Internet Software Consortium,
3957419a7fSflorian  * see ``http://www.vix.com/isc''.  To learn more about Vixie
4057419a7fSflorian  * Enterprises, see ``http://www.vix.com''.
4157419a7fSflorian  */
4257419a7fSflorian 
4357419a7fSflorian #include <sys/types.h>
4457419a7fSflorian #include <sys/ioctl.h>
4557419a7fSflorian 
4657419a7fSflorian #include <net/bpf.h>
4757419a7fSflorian #include <net/if.h>
4857419a7fSflorian #include <net/ethertypes.h>
4957419a7fSflorian #include <netinet/in.h>
5057419a7fSflorian #include <netinet/ip.h>
5157419a7fSflorian 
5257419a7fSflorian #include <fcntl.h>
5357419a7fSflorian #include <string.h>
5457419a7fSflorian #include <unistd.h>
5557419a7fSflorian 
5657419a7fSflorian #include "bpf.h"
5757419a7fSflorian #include "log.h"
5857419a7fSflorian 
5957419a7fSflorian #define	CLIENT_PORT	68
6057419a7fSflorian 
6157419a7fSflorian /*
6257419a7fSflorian  * Packet filter program.
6357419a7fSflorian  */
6457419a7fSflorian struct bpf_insn dhcp_bpf_filter[] = {
6557419a7fSflorian 	/* Make sure this is an IP packet. */
6657419a7fSflorian 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
6757419a7fSflorian 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
6857419a7fSflorian 
6957419a7fSflorian 	/* Make sure it's a UDP packet. */
7057419a7fSflorian 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
7157419a7fSflorian 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
7257419a7fSflorian 
7357419a7fSflorian 	/* Make sure this isn't a fragment. */
7457419a7fSflorian 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
7557419a7fSflorian 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
7657419a7fSflorian 
7757419a7fSflorian 	/* Get the IP header length. */
7857419a7fSflorian 	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
7957419a7fSflorian 
8057419a7fSflorian 	/* Make sure it's to the right port. */
8157419a7fSflorian 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
8257419a7fSflorian 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, CLIENT_PORT, 0, 1),
8357419a7fSflorian 
8457419a7fSflorian 	/* If we passed all the tests, ask for the whole packet. */
8557419a7fSflorian 	BPF_STMT(BPF_RET+BPF_K, (unsigned int)-1),
8657419a7fSflorian 
8757419a7fSflorian 	/* Otherwise, drop it. */
8857419a7fSflorian 	BPF_STMT(BPF_RET+BPF_K, 0),
8957419a7fSflorian };
9057419a7fSflorian 
9157419a7fSflorian /*
9257419a7fSflorian  * Packet write filter program:
9357419a7fSflorian  * 'ip and udp and src port bootpc and dst port bootps'
9457419a7fSflorian  */
9557419a7fSflorian struct bpf_insn dhcp_bpf_wfilter[] = {
9657419a7fSflorian 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, 14),
9757419a7fSflorian 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 12),
9857419a7fSflorian 
9957419a7fSflorian 	/* Make sure this is an IP packet. */
10057419a7fSflorian 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
10157419a7fSflorian 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10),
10257419a7fSflorian 
10357419a7fSflorian 	/* Make sure it's a UDP packet. */
10457419a7fSflorian 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
10557419a7fSflorian 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8),
10657419a7fSflorian 
10757419a7fSflorian 	/* Make sure this isn't a fragment. */
10857419a7fSflorian 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
10957419a7fSflorian 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0),	/* patched */
11057419a7fSflorian 
11157419a7fSflorian 	/* Get the IP header length. */
11257419a7fSflorian 	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
11357419a7fSflorian 
11457419a7fSflorian 	/* Make sure it's from the right port. */
11557419a7fSflorian 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14),
11657419a7fSflorian 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 68, 0, 3),
11757419a7fSflorian 
11857419a7fSflorian 	/* Make sure it is to the right ports. */
11957419a7fSflorian 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
12057419a7fSflorian 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1),
12157419a7fSflorian 
12257419a7fSflorian 	/* If we passed all the tests, ask for the whole packet. */
12357419a7fSflorian 	BPF_STMT(BPF_RET+BPF_K, (unsigned int)-1),
12457419a7fSflorian 
12557419a7fSflorian 	/* Otherwise, drop it. */
12657419a7fSflorian 	BPF_STMT(BPF_RET+BPF_K, 0),
12757419a7fSflorian };
12857419a7fSflorian 
12957419a7fSflorian int
get_bpf_sock(const char * name)13057419a7fSflorian get_bpf_sock(const char *name)
13157419a7fSflorian {
13257419a7fSflorian 	struct bpf_program	 p;
13357419a7fSflorian 	struct ifreq		 ifr;
13457419a7fSflorian 	u_int			 sz;
13557419a7fSflorian 	int			 flag = 1, fildrop = BPF_FILDROP_CAPTURE;
13657419a7fSflorian 	int			 bpffd;
13757419a7fSflorian 
13857419a7fSflorian 	if ((bpffd = open("/dev/bpf", O_RDWR | O_CLOEXEC | O_NONBLOCK)) == -1)
13957419a7fSflorian 		fatal("open(/dev/bpf)");
14057419a7fSflorian 
14157419a7fSflorian 	sz = BPFLEN;
14257419a7fSflorian 	/* Set the BPF buffer length. */
14357419a7fSflorian 	if (ioctl(bpffd, BIOCSBLEN, &sz) == -1)
14457419a7fSflorian 		fatal("BIOCSBLEN");
14557419a7fSflorian 	if (sz != BPFLEN)
14657419a7fSflorian 		fatal("BIOCSBLEN, expected %u, got %u", BPFLEN, sz);
14757419a7fSflorian 
14857419a7fSflorian 	/*
14957419a7fSflorian 	 * Set immediate mode so that reads return as soon as a packet
15057419a7fSflorian 	 * comes in, rather than waiting for the input buffer to fill
15157419a7fSflorian 	 * with packets.
15257419a7fSflorian 	 */
15357419a7fSflorian 	if (ioctl(bpffd, BIOCIMMEDIATE, &flag) == -1)
15457419a7fSflorian 		fatal("BIOCIMMEDIATE");
15557419a7fSflorian 
15657419a7fSflorian 	if (ioctl(bpffd, BIOCSFILDROP, &fildrop) == -1)
15757419a7fSflorian 		fatal("BIOCSFILDROP");
15857419a7fSflorian 
15957419a7fSflorian 	/* Set up the bpf filter program structure. */
16057419a7fSflorian 	p.bf_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn);
16157419a7fSflorian 	p.bf_insns = dhcp_bpf_filter;
16257419a7fSflorian 
16357419a7fSflorian 	if (ioctl(bpffd, BIOCSETF, &p) == -1)
16457419a7fSflorian 		fatal("BIOCSETF");
16557419a7fSflorian 
16657419a7fSflorian 	/* Set up the bpf write filter program structure. */
16757419a7fSflorian 	p.bf_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct bpf_insn);
16857419a7fSflorian 	p.bf_insns = dhcp_bpf_wfilter;
16957419a7fSflorian 
17057419a7fSflorian 	if (dhcp_bpf_wfilter[7].k == 0x1fff)
17157419a7fSflorian 		dhcp_bpf_wfilter[7].k = htons(IP_MF|IP_OFFMASK);
17257419a7fSflorian 
17357419a7fSflorian 	if (ioctl(bpffd, BIOCSETWF, &p) == -1)
17457419a7fSflorian 		fatal("BIOCSETWF");
17557419a7fSflorian 
176*8e2e76e8Sflorian 	strlcpy(ifr.ifr_name, name, IFNAMSIZ);
177*8e2e76e8Sflorian 	if (ioctl(bpffd, BIOCSETIF, &ifr) == -1) {
178*8e2e76e8Sflorian 		log_warn("BIOCSETIF"); /* interface might have disappeared */
179*8e2e76e8Sflorian 		close(bpffd);
180*8e2e76e8Sflorian 		return -1;
181*8e2e76e8Sflorian 	}
182*8e2e76e8Sflorian 
18357419a7fSflorian 	if (ioctl(bpffd, BIOCLOCK, NULL) == -1)
18457419a7fSflorian 		fatal("BIOCLOCK");
18557419a7fSflorian 
18657419a7fSflorian 	return bpffd;
18757419a7fSflorian }
188