xref: /freebsd-src/sbin/dhclient/bpf.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
147c08596SBrooks Davis /*	$OpenBSD: bpf.c,v 1.13 2004/05/05 14:28:58 deraadt Exp $	*/
247c08596SBrooks Davis 
347c08596SBrooks Davis /* BPF socket interface code, originally contributed by Archie Cobbs. */
447c08596SBrooks Davis 
58a16b7a1SPedro F. Giffuni /*-
68a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
78a16b7a1SPedro F. Giffuni  *
8*abf5bff7SFranco Fichtner  * Copyright (c) 2021 Franco Fichtner <franco@opnsense.org>
947c08596SBrooks Davis  * Copyright (c) 1995, 1996, 1998, 1999
1047c08596SBrooks Davis  * The Internet Software Consortium.    All rights reserved.
1147c08596SBrooks Davis  *
1247c08596SBrooks Davis  * Redistribution and use in source and binary forms, with or without
1347c08596SBrooks Davis  * modification, are permitted provided that the following conditions
1447c08596SBrooks Davis  * are met:
1547c08596SBrooks Davis  *
1647c08596SBrooks Davis  * 1. Redistributions of source code must retain the above copyright
1747c08596SBrooks Davis  *    notice, this list of conditions and the following disclaimer.
1847c08596SBrooks Davis  * 2. Redistributions in binary form must reproduce the above copyright
1947c08596SBrooks Davis  *    notice, this list of conditions and the following disclaimer in the
2047c08596SBrooks Davis  *    documentation and/or other materials provided with the distribution.
2147c08596SBrooks Davis  * 3. Neither the name of The Internet Software Consortium nor the names
2247c08596SBrooks Davis  *    of its contributors may be used to endorse or promote products derived
2347c08596SBrooks Davis  *    from this software without specific prior written permission.
2447c08596SBrooks Davis  *
2547c08596SBrooks Davis  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
2647c08596SBrooks Davis  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
2747c08596SBrooks Davis  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2847c08596SBrooks Davis  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2947c08596SBrooks Davis  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
3047c08596SBrooks Davis  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3147c08596SBrooks Davis  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3247c08596SBrooks Davis  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
3347c08596SBrooks Davis  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
3447c08596SBrooks Davis  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3547c08596SBrooks Davis  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3647c08596SBrooks Davis  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3747c08596SBrooks Davis  * SUCH DAMAGE.
3847c08596SBrooks Davis  *
3947c08596SBrooks Davis  * This software has been written for the Internet Software Consortium
4047c08596SBrooks Davis  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
4147c08596SBrooks Davis  * Enterprises.  To learn more about the Internet Software Consortium,
4247c08596SBrooks Davis  * see ``http://www.vix.com/isc''.  To learn more about Vixie
4347c08596SBrooks Davis  * Enterprises, see ``http://www.vix.com''.
4447c08596SBrooks Davis  */
4547c08596SBrooks Davis 
468794fdbbSBrooks Davis #include <sys/cdefs.h>
4747c08596SBrooks Davis #include "dhcpd.h"
48235eb530SPawel Jakub Dawidek #include "privsep.h"
49b881b8beSRobert Watson #include <sys/capsicum.h>
5047c08596SBrooks Davis #include <sys/ioctl.h>
5147c08596SBrooks Davis #include <sys/uio.h>
5247c08596SBrooks Davis 
5347c08596SBrooks Davis #include <net/bpf.h>
5447c08596SBrooks Davis #include <netinet/in_systm.h>
5547c08596SBrooks Davis #include <netinet/ip.h>
5647c08596SBrooks Davis #include <netinet/udp.h>
5747c08596SBrooks Davis #include <netinet/if_ether.h>
5847c08596SBrooks Davis 
59377421dfSMariusz Zaborski #include <capsicum_helpers.h>
60377421dfSMariusz Zaborski 
6147c08596SBrooks Davis #define BPF_FORMAT "/dev/bpf%d"
6247c08596SBrooks Davis 
6347c08596SBrooks Davis /*
6447c08596SBrooks Davis  * Called by get_interface_list for each interface that's discovered.
6547c08596SBrooks Davis  * Opens a packet filter for each interface and adds it to the select
6647c08596SBrooks Davis  * mask.
6747c08596SBrooks Davis  */
6847c08596SBrooks Davis int
if_register_bpf(struct interface_info * info,int flags)69b0f1b32aSPawel Jakub Dawidek if_register_bpf(struct interface_info *info, int flags)
7047c08596SBrooks Davis {
7147c08596SBrooks Davis 	char filename[50];
7247c08596SBrooks Davis 	int sock, b;
7347c08596SBrooks Davis 
7447c08596SBrooks Davis 	/* Open a BPF device */
75b0f1b32aSPawel Jakub Dawidek 	for (b = 0;; b++) {
7647c08596SBrooks Davis 		snprintf(filename, sizeof(filename), BPF_FORMAT, b);
77b0f1b32aSPawel Jakub Dawidek 		sock = open(filename, flags);
7847c08596SBrooks Davis 		if (sock < 0) {
7947c08596SBrooks Davis 			if (errno == EBUSY)
8047c08596SBrooks Davis 				continue;
8147c08596SBrooks Davis 			else
8247c08596SBrooks Davis 				error("Can't find free bpf: %m");
8347c08596SBrooks Davis 		} else
8447c08596SBrooks Davis 			break;
8547c08596SBrooks Davis 	}
8647c08596SBrooks Davis 
8747c08596SBrooks Davis 	/* Set the BPF device to point at this interface. */
8847c08596SBrooks Davis 	if (ioctl(sock, BIOCSETIF, info->ifp) < 0)
8947c08596SBrooks Davis 		error("Can't attach interface %s to bpf device %s: %m",
9047c08596SBrooks Davis 		    info->name, filename);
9147c08596SBrooks Davis 
921e7fe2fbSLuiz Otavio O Souza 	/* Tag the packets with the proper VLAN PCP setting. */
931e7fe2fbSLuiz Otavio O Souza 	if (info->client->config->vlan_pcp != 0) {
941e7fe2fbSLuiz Otavio O Souza 		if (ioctl(sock, BIOCSETVLANPCP,
951e7fe2fbSLuiz Otavio O Souza 		    &info->client->config->vlan_pcp) < 0)
961e7fe2fbSLuiz Otavio O Souza 			error( "Can't set the VLAN PCP tag on interface %s: %m",
971e7fe2fbSLuiz Otavio O Souza 			    info->name);
981e7fe2fbSLuiz Otavio O Souza 	}
991e7fe2fbSLuiz Otavio O Souza 
10047c08596SBrooks Davis 	return (sock);
10147c08596SBrooks Davis }
10247c08596SBrooks Davis 
103b0f1b32aSPawel Jakub Dawidek /*
104b0f1b32aSPawel Jakub Dawidek  * Packet write filter program:
105b0f1b32aSPawel Jakub Dawidek  * 'ip and udp and src port bootps and dst port (bootps or bootpc)'
106b0f1b32aSPawel Jakub Dawidek  */
1075851803fSFranco Fichtner static const struct bpf_insn dhcp_bpf_wfilter[] = {
108b0f1b32aSPawel Jakub Dawidek 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, 14),
109b0f1b32aSPawel Jakub Dawidek 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 12),
110b0f1b32aSPawel Jakub Dawidek 
111b0f1b32aSPawel Jakub Dawidek 	/* Make sure this is an IP packet... */
112b0f1b32aSPawel Jakub Dawidek 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
113b0f1b32aSPawel Jakub Dawidek 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10),
114b0f1b32aSPawel Jakub Dawidek 
115b0f1b32aSPawel Jakub Dawidek 	/* Make sure it's a UDP packet... */
116b0f1b32aSPawel Jakub Dawidek 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
117b0f1b32aSPawel Jakub Dawidek 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8),
118b0f1b32aSPawel Jakub Dawidek 
119b0f1b32aSPawel Jakub Dawidek 	/* Make sure this isn't a fragment... */
120b0f1b32aSPawel Jakub Dawidek 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
1215851803fSFranco Fichtner 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, IP_MF|IP_OFFMASK, 6, 0),
122b0f1b32aSPawel Jakub Dawidek 
123b0f1b32aSPawel Jakub Dawidek 	/* Get the IP header length... */
124b0f1b32aSPawel Jakub Dawidek 	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
125b0f1b32aSPawel Jakub Dawidek 
126b0f1b32aSPawel Jakub Dawidek 	/* Make sure it's from the right port... */
127b0f1b32aSPawel Jakub Dawidek 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14),
1285851803fSFranco Fichtner 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, LOCAL_PORT, 0, 3),
129b0f1b32aSPawel Jakub Dawidek 
130b0f1b32aSPawel Jakub Dawidek 	/* Make sure it is to the right ports ... */
131b0f1b32aSPawel Jakub Dawidek 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
1325851803fSFranco Fichtner 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, REMOTE_PORT, 0, 1),
133b0f1b32aSPawel Jakub Dawidek 
134b0f1b32aSPawel Jakub Dawidek 	/* If we passed all the tests, ask for the whole packet. */
135b0f1b32aSPawel Jakub Dawidek 	BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
136b0f1b32aSPawel Jakub Dawidek 
137b0f1b32aSPawel Jakub Dawidek 	/* Otherwise, drop it. */
138b0f1b32aSPawel Jakub Dawidek 	BPF_STMT(BPF_RET+BPF_K, 0),
139b0f1b32aSPawel Jakub Dawidek };
140b0f1b32aSPawel Jakub Dawidek 
14147c08596SBrooks Davis void
if_register_send(struct interface_info * info)14247c08596SBrooks Davis if_register_send(struct interface_info *info)
14347c08596SBrooks Davis {
1447008be5bSPawel Jakub Dawidek 	cap_rights_t rights;
145b0f1b32aSPawel Jakub Dawidek 	struct bpf_version v;
146b0f1b32aSPawel Jakub Dawidek 	struct bpf_program p;
1479b683f8dSPhilip Paeps 	int sock, on = 1;
1489b683f8dSPhilip Paeps 
149b0f1b32aSPawel Jakub Dawidek 	/* Open a BPF device and hang it on this interface... */
150b0f1b32aSPawel Jakub Dawidek 	info->wfdesc = if_register_bpf(info, O_WRONLY);
151b0f1b32aSPawel Jakub Dawidek 
152b0f1b32aSPawel Jakub Dawidek 	/* Make sure the BPF version is in range... */
153b0f1b32aSPawel Jakub Dawidek 	if (ioctl(info->wfdesc, BIOCVERSION, &v) < 0)
154b0f1b32aSPawel Jakub Dawidek 		error("Can't get BPF version: %m");
155b0f1b32aSPawel Jakub Dawidek 
156b0f1b32aSPawel Jakub Dawidek 	if (v.bv_major != BPF_MAJOR_VERSION ||
157b0f1b32aSPawel Jakub Dawidek 	    v.bv_minor < BPF_MINOR_VERSION)
158b0f1b32aSPawel Jakub Dawidek 		error("Kernel BPF version out of range - recompile dhcpd!");
159b0f1b32aSPawel Jakub Dawidek 
160b0f1b32aSPawel Jakub Dawidek 	/* Set up the bpf write filter program structure. */
1615851803fSFranco Fichtner 	p.bf_insns = __DECONST(struct bpf_insn *, dhcp_bpf_wfilter);
1625851803fSFranco Fichtner 	p.bf_len = nitems(dhcp_bpf_wfilter);
163b0f1b32aSPawel Jakub Dawidek 
164b0f1b32aSPawel Jakub Dawidek 	if (ioctl(info->wfdesc, BIOCSETWF, &p) < 0)
165b0f1b32aSPawel Jakub Dawidek 		error("Can't install write filter program: %m");
166b0f1b32aSPawel Jakub Dawidek 
167b0f1b32aSPawel Jakub Dawidek 	if (ioctl(info->wfdesc, BIOCLOCK, NULL) < 0)
168b0f1b32aSPawel Jakub Dawidek 		error("Cannot lock bpf");
1699b683f8dSPhilip Paeps 
1707008be5bSPawel Jakub Dawidek 	cap_rights_init(&rights, CAP_WRITE);
171377421dfSMariusz Zaborski 	if (caph_rights_limit(info->wfdesc, &rights) < 0)
1723b2ed065SPawel Jakub Dawidek 		error("Can't limit bpf descriptor: %m");
1733b2ed065SPawel Jakub Dawidek 
1749b683f8dSPhilip Paeps 	/*
1759b683f8dSPhilip Paeps 	 * Use raw socket for unicast send.
1769b683f8dSPhilip Paeps 	 */
1779b683f8dSPhilip Paeps 	if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1)
1789b683f8dSPhilip Paeps 		error("socket(SOCK_RAW): %m");
1799b683f8dSPhilip Paeps 	if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on,
1809b683f8dSPhilip Paeps 	    sizeof(on)) == -1)
1819b683f8dSPhilip Paeps 		error("setsockopt(IP_HDRINCL): %m");
1829b683f8dSPhilip Paeps 	info->ufdesc = sock;
18347c08596SBrooks Davis }
18447c08596SBrooks Davis 
18547c08596SBrooks Davis /*
18647c08596SBrooks Davis  * Packet filter program...
18747c08596SBrooks Davis  */
1885851803fSFranco Fichtner static const struct bpf_insn dhcp_bpf_filter[] = {
189*abf5bff7SFranco Fichtner 	/* Use relative index (0) for IP packet... */
190*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, 0),
191*abf5bff7SFranco Fichtner 
192*abf5bff7SFranco Fichtner 	/*
193*abf5bff7SFranco Fichtner 	 * Test whether this is a VLAN packet...
194*abf5bff7SFranco Fichtner 	 *
195*abf5bff7SFranco Fichtner 	 * In case the server packet is using a VLAN ID
196*abf5bff7SFranco Fichtner 	 * of 0, meaning an untagged priority was set, the
197*abf5bff7SFranco Fichtner 	 * response shall be read and replied to.
198*abf5bff7SFranco Fichtner 	 */
199*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 12),
200*abf5bff7SFranco Fichtner 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_VLAN, 0, 4),
201*abf5bff7SFranco Fichtner 
202*abf5bff7SFranco Fichtner 	/* Test whether it has a VID of 0 */
203*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14),
204*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_ALU + BPF_AND + BPF_K, EVL_VLID_MASK),
205*abf5bff7SFranco Fichtner 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 17),
206*abf5bff7SFranco Fichtner 
207*abf5bff7SFranco Fichtner 	/* Correct the relative index for VLAN packet (4)... */
208*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, 4),
209*abf5bff7SFranco Fichtner 
21047c08596SBrooks Davis 	/* Make sure this is an IP packet... */
211*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 12),
212*abf5bff7SFranco Fichtner 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 14),
21347c08596SBrooks Davis 
21447c08596SBrooks Davis 	/* Make sure it's a UDP packet... */
215*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, 23),
216*abf5bff7SFranco Fichtner 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 12),
21747c08596SBrooks Davis 
21847c08596SBrooks Davis 	/* Make sure this isn't a fragment... */
219*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 20),
220*abf5bff7SFranco Fichtner 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, IP_MF|IP_OFFMASK, 10, 0),
22147c08596SBrooks Davis 
222*abf5bff7SFranco Fichtner 	/*
223*abf5bff7SFranco Fichtner 	 * Get the IP header length...
224*abf5bff7SFranco Fichtner 	 *
225*abf5bff7SFranco Fichtner 	 * To find the correct position of the IP header
226*abf5bff7SFranco Fichtner 	 * length field store the index (0 or 4) in the
227*abf5bff7SFranco Fichtner 	 * accumulator and compare it with 0.
228*abf5bff7SFranco Fichtner 	 */
229*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_MISC + BPF_TXA, 0),
230*abf5bff7SFranco Fichtner 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 2),
231*abf5bff7SFranco Fichtner 	/* Store IP header length of IP packet in index. */
23247c08596SBrooks Davis 	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
233*abf5bff7SFranco Fichtner 	/* Skip over following VLAN handling instruction. */
234*abf5bff7SFranco Fichtner 	BPF_JUMP(BPF_JMP + BPF_JA, 1, 0, 0),
235*abf5bff7SFranco Fichtner 	/* Store IP header length of VLAN packet in index. */
236*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 18),
237*abf5bff7SFranco Fichtner 	/* Add IP header length to previous relative index. */
238*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),
239*abf5bff7SFranco Fichtner 	/* Move result back to index to reach UDP header below. */
240*abf5bff7SFranco Fichtner 	BPF_STMT(BPF_MISC + BPF_TAX, 0),
24147c08596SBrooks Davis 
24247c08596SBrooks Davis 	/* Make sure it's to the right port... */
24347c08596SBrooks Davis 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
2445851803fSFranco Fichtner 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, LOCAL_PORT, 0, 1),
24547c08596SBrooks Davis 
24647c08596SBrooks Davis 	/* If we passed all the tests, ask for the whole packet. */
24747c08596SBrooks Davis 	BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
24847c08596SBrooks Davis 
24947c08596SBrooks Davis 	/* Otherwise, drop it. */
25047c08596SBrooks Davis 	BPF_STMT(BPF_RET+BPF_K, 0),
25147c08596SBrooks Davis };
25247c08596SBrooks Davis 
25347c08596SBrooks Davis void
if_register_receive(struct interface_info * info)25447c08596SBrooks Davis if_register_receive(struct interface_info *info)
25547c08596SBrooks Davis {
2563b2ed065SPawel Jakub Dawidek 	static const unsigned long cmds[2] = { SIOCGIFFLAGS, SIOCGIFMEDIA };
2577008be5bSPawel Jakub Dawidek 	cap_rights_t rights;
25847c08596SBrooks Davis 	struct bpf_version v;
25947c08596SBrooks Davis 	struct bpf_program p;
26047c08596SBrooks Davis 	int flag = 1, sz;
26147c08596SBrooks Davis 
26247c08596SBrooks Davis 	/* Open a BPF device and hang it on this interface... */
263b0f1b32aSPawel Jakub Dawidek 	info->rfdesc = if_register_bpf(info, O_RDONLY);
26447c08596SBrooks Davis 
26547c08596SBrooks Davis 	/* Make sure the BPF version is in range... */
26647c08596SBrooks Davis 	if (ioctl(info->rfdesc, BIOCVERSION, &v) < 0)
26747c08596SBrooks Davis 		error("Can't get BPF version: %m");
26847c08596SBrooks Davis 
26947c08596SBrooks Davis 	if (v.bv_major != BPF_MAJOR_VERSION ||
27047c08596SBrooks Davis 	    v.bv_minor < BPF_MINOR_VERSION)
27147c08596SBrooks Davis 		error("Kernel BPF version out of range - recompile dhcpd!");
27247c08596SBrooks Davis 
27347c08596SBrooks Davis 	/*
27447c08596SBrooks Davis 	 * Set immediate mode so that reads return as soon as a packet
27547c08596SBrooks Davis 	 * comes in, rather than waiting for the input buffer to fill
27647c08596SBrooks Davis 	 * with packets.
27747c08596SBrooks Davis 	 */
27847c08596SBrooks Davis 	if (ioctl(info->rfdesc, BIOCIMMEDIATE, &flag) < 0)
27947c08596SBrooks Davis 		error("Can't set immediate mode on bpf device: %m");
28047c08596SBrooks Davis 
28147c08596SBrooks Davis 	/* Get the required BPF buffer length from the kernel. */
28247c08596SBrooks Davis 	if (ioctl(info->rfdesc, BIOCGBLEN, &sz) < 0)
28347c08596SBrooks Davis 		error("Can't get bpf buffer length: %m");
28447c08596SBrooks Davis 	info->rbuf_max = sz;
28547c08596SBrooks Davis 	info->rbuf = malloc(info->rbuf_max);
28647c08596SBrooks Davis 	if (!info->rbuf)
28747c08596SBrooks Davis 		error("Can't allocate %lu bytes for bpf input buffer.",
28847c08596SBrooks Davis 		    (unsigned long)info->rbuf_max);
28947c08596SBrooks Davis 	info->rbuf_offset = 0;
29047c08596SBrooks Davis 	info->rbuf_len = 0;
29147c08596SBrooks Davis 
29247c08596SBrooks Davis 	/* Set up the bpf filter program structure. */
2935851803fSFranco Fichtner 	p.bf_insns = __DECONST(struct bpf_insn *, dhcp_bpf_filter);
2945851803fSFranco Fichtner 	p.bf_len = nitems(dhcp_bpf_filter);
29547c08596SBrooks Davis 
29647c08596SBrooks Davis 	if (ioctl(info->rfdesc, BIOCSETF, &p) < 0)
29747c08596SBrooks Davis 		error("Can't install packet filter program: %m");
29847c08596SBrooks Davis 
29947c08596SBrooks Davis 	if (ioctl(info->rfdesc, BIOCLOCK, NULL) < 0)
30047c08596SBrooks Davis 		error("Cannot lock bpf");
3013b2ed065SPawel Jakub Dawidek 
302bb7a82acSChristian Brueffer 	cap_rights_init(&rights, CAP_IOCTL, CAP_EVENT, CAP_READ);
303377421dfSMariusz Zaborski 	if (caph_rights_limit(info->rfdesc, &rights) < 0)
3043b2ed065SPawel Jakub Dawidek 		error("Can't limit bpf descriptor: %m");
305377421dfSMariusz Zaborski 	if (caph_ioctls_limit(info->rfdesc, cmds, 2) < 0)
3063b2ed065SPawel Jakub Dawidek 		error("Can't limit ioctls for bpf descriptor: %m");
30747c08596SBrooks Davis }
30847c08596SBrooks Davis 
309e8da5003SPawel Jakub Dawidek void
send_packet_unpriv(int privfd,struct dhcp_packet * raw,size_t len,struct in_addr from,struct in_addr to)310235eb530SPawel Jakub Dawidek send_packet_unpriv(int privfd, struct dhcp_packet *raw, size_t len,
311235eb530SPawel Jakub Dawidek     struct in_addr from, struct in_addr to)
312235eb530SPawel Jakub Dawidek {
313235eb530SPawel Jakub Dawidek 	struct imsg_hdr hdr;
314235eb530SPawel Jakub Dawidek 	struct buf *buf;
315235eb530SPawel Jakub Dawidek 	int errs;
316235eb530SPawel Jakub Dawidek 
317235eb530SPawel Jakub Dawidek 	hdr.code = IMSG_SEND_PACKET;
318235eb530SPawel Jakub Dawidek 	hdr.len = sizeof(hdr) +
319235eb530SPawel Jakub Dawidek 	    sizeof(size_t) + len +
320235eb530SPawel Jakub Dawidek 	    sizeof(from) + sizeof(to);
321235eb530SPawel Jakub Dawidek 
322235eb530SPawel Jakub Dawidek 	if ((buf = buf_open(hdr.len)) == NULL)
323235eb530SPawel Jakub Dawidek 		error("buf_open: %m");
324235eb530SPawel Jakub Dawidek 
325235eb530SPawel Jakub Dawidek 	errs = 0;
326235eb530SPawel Jakub Dawidek 	errs += buf_add(buf, &hdr, sizeof(hdr));
327235eb530SPawel Jakub Dawidek 	errs += buf_add(buf, &len, sizeof(len));
328235eb530SPawel Jakub Dawidek 	errs += buf_add(buf, raw, len);
329235eb530SPawel Jakub Dawidek 	errs += buf_add(buf, &from, sizeof(from));
330235eb530SPawel Jakub Dawidek 	errs += buf_add(buf, &to, sizeof(to));
331235eb530SPawel Jakub Dawidek 	if (errs)
332235eb530SPawel Jakub Dawidek 		error("buf_add: %m");
333235eb530SPawel Jakub Dawidek 
334235eb530SPawel Jakub Dawidek 	if (buf_close(privfd, buf) == -1)
335235eb530SPawel Jakub Dawidek 		error("buf_close: %m");
336235eb530SPawel Jakub Dawidek }
337235eb530SPawel Jakub Dawidek 
338235eb530SPawel Jakub Dawidek void
send_packet_priv(struct interface_info * interface,struct imsg_hdr * hdr,int fd)339235eb530SPawel Jakub Dawidek send_packet_priv(struct interface_info *interface, struct imsg_hdr *hdr, int fd)
34047c08596SBrooks Davis {
34147c08596SBrooks Davis 	unsigned char buf[256];
34247c08596SBrooks Davis 	struct iovec iov[2];
3439b683f8dSPhilip Paeps 	struct msghdr msg;
344235eb530SPawel Jakub Dawidek 	struct dhcp_packet raw;
345235eb530SPawel Jakub Dawidek 	size_t len;
346235eb530SPawel Jakub Dawidek 	struct in_addr from, to;
34747c08596SBrooks Davis 	int result, bufp = 0;
34847c08596SBrooks Davis 
349235eb530SPawel Jakub Dawidek 	if (hdr->len < sizeof(*hdr) + sizeof(size_t))
350235eb530SPawel Jakub Dawidek 		error("corrupted message received");
351235eb530SPawel Jakub Dawidek 	buf_read(fd, &len, sizeof(len));
352235eb530SPawel Jakub Dawidek 	if (hdr->len != sizeof(*hdr) + sizeof(size_t) + len +
353235eb530SPawel Jakub Dawidek 	    sizeof(from) + sizeof(to)) {
354235eb530SPawel Jakub Dawidek 		error("corrupted message received");
355235eb530SPawel Jakub Dawidek 	}
356235eb530SPawel Jakub Dawidek 	if (len > sizeof(raw))
357235eb530SPawel Jakub Dawidek 		error("corrupted message received");
358235eb530SPawel Jakub Dawidek 	buf_read(fd, &raw, len);
359235eb530SPawel Jakub Dawidek 	buf_read(fd, &from, sizeof(from));
360235eb530SPawel Jakub Dawidek 	buf_read(fd, &to, sizeof(to));
361235eb530SPawel Jakub Dawidek 
36247c08596SBrooks Davis 	/* Assemble the headers... */
363ba019ae5SPawel Jakub Dawidek 	if (to.s_addr == INADDR_BROADCAST)
364d1f4d854SPawel Jakub Dawidek 		assemble_hw_header(interface, buf, &bufp);
365ba019ae5SPawel Jakub Dawidek 	assemble_udp_ip_header(buf, &bufp, from.s_addr, to.s_addr,
366235eb530SPawel Jakub Dawidek 	    htons(REMOTE_PORT), (unsigned char *)&raw, len);
36747c08596SBrooks Davis 
3683a0fc7d8SPawel Jakub Dawidek 	iov[0].iov_base = buf;
36947c08596SBrooks Davis 	iov[0].iov_len = bufp;
370235eb530SPawel Jakub Dawidek 	iov[1].iov_base = &raw;
37147c08596SBrooks Davis 	iov[1].iov_len = len;
37247c08596SBrooks Davis 
3739b683f8dSPhilip Paeps 	/* Fire it off */
374ba019ae5SPawel Jakub Dawidek 	if (to.s_addr == INADDR_BROADCAST)
37547c08596SBrooks Davis 		result = writev(interface->wfdesc, iov, 2);
3769b683f8dSPhilip Paeps 	else {
377ba019ae5SPawel Jakub Dawidek 		struct sockaddr_in sato;
378ba019ae5SPawel Jakub Dawidek 
379ba019ae5SPawel Jakub Dawidek 		sato.sin_addr = to;
380ba019ae5SPawel Jakub Dawidek 		sato.sin_port = htons(REMOTE_PORT);
381ba019ae5SPawel Jakub Dawidek 		sato.sin_family = AF_INET;
382ba019ae5SPawel Jakub Dawidek 		sato.sin_len = sizeof(sato);
383ba019ae5SPawel Jakub Dawidek 
3849b683f8dSPhilip Paeps 		memset(&msg, 0, sizeof(msg));
385ba019ae5SPawel Jakub Dawidek 		msg.msg_name = (struct sockaddr *)&sato;
386ba019ae5SPawel Jakub Dawidek 		msg.msg_namelen = sizeof(sato);
3879b683f8dSPhilip Paeps 		msg.msg_iov = iov;
3889b683f8dSPhilip Paeps 		msg.msg_iovlen = 2;
3899b683f8dSPhilip Paeps 		result = sendmsg(interface->ufdesc, &msg, 0);
3909b683f8dSPhilip Paeps 	}
3919b683f8dSPhilip Paeps 
39247c08596SBrooks Davis 	if (result < 0)
39347c08596SBrooks Davis 		warning("send_packet: %m");
39447c08596SBrooks Davis }
39547c08596SBrooks Davis 
39647c08596SBrooks Davis ssize_t
receive_packet(struct interface_info * interface,unsigned char * buf,size_t len,struct sockaddr_in * from,struct hardware * hfrom)39747c08596SBrooks Davis receive_packet(struct interface_info *interface, unsigned char *buf,
39847c08596SBrooks Davis     size_t len, struct sockaddr_in *from, struct hardware *hfrom)
39947c08596SBrooks Davis {
40047c08596SBrooks Davis 	int length = 0, offset = 0;
40147c08596SBrooks Davis 	struct bpf_hdr hdr;
40247c08596SBrooks Davis 
40347c08596SBrooks Davis 	/*
40447c08596SBrooks Davis 	 * All this complexity is because BPF doesn't guarantee that
40547c08596SBrooks Davis 	 * only one packet will be returned at a time.  We're getting
40647c08596SBrooks Davis 	 * what we deserve, though - this is a terrible abuse of the BPF
40747c08596SBrooks Davis 	 * interface.  Sigh.
40847c08596SBrooks Davis 	 */
40947c08596SBrooks Davis 
41047c08596SBrooks Davis 	/* Process packets until we get one we can return or until we've
41147c08596SBrooks Davis 	 * done a read and gotten nothing we can return...
41247c08596SBrooks Davis 	 */
41347c08596SBrooks Davis 	do {
41447c08596SBrooks Davis 		/* If the buffer is empty, fill it. */
415ebe609b4SBrooks Davis 		if (interface->rbuf_offset >= interface->rbuf_len) {
41647c08596SBrooks Davis 			length = read(interface->rfdesc, interface->rbuf,
41747c08596SBrooks Davis 			    interface->rbuf_max);
41847c08596SBrooks Davis 			if (length <= 0)
41947c08596SBrooks Davis 				return (length);
42047c08596SBrooks Davis 			interface->rbuf_offset = 0;
42147c08596SBrooks Davis 			interface->rbuf_len = length;
42247c08596SBrooks Davis 		}
42347c08596SBrooks Davis 
42447c08596SBrooks Davis 		/*
42547c08596SBrooks Davis 		 * If there isn't room for a whole bpf header, something
42647c08596SBrooks Davis 		 * went wrong, but we'll ignore it and hope it goes
42747c08596SBrooks Davis 		 * away... XXX
42847c08596SBrooks Davis 		 */
42947c08596SBrooks Davis 		if (interface->rbuf_len - interface->rbuf_offset <
43047c08596SBrooks Davis 		    sizeof(hdr)) {
43147c08596SBrooks Davis 			interface->rbuf_offset = interface->rbuf_len;
43247c08596SBrooks Davis 			continue;
43347c08596SBrooks Davis 		}
43447c08596SBrooks Davis 
43547c08596SBrooks Davis 		/* Copy out a bpf header... */
43647c08596SBrooks Davis 		memcpy(&hdr, &interface->rbuf[interface->rbuf_offset],
43747c08596SBrooks Davis 		    sizeof(hdr));
43847c08596SBrooks Davis 
43947c08596SBrooks Davis 		/*
44047c08596SBrooks Davis 		 * If the bpf header plus data doesn't fit in what's
44147c08596SBrooks Davis 		 * left of the buffer, stick head in sand yet again...
44247c08596SBrooks Davis 		 */
44347c08596SBrooks Davis 		if (interface->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen >
44447c08596SBrooks Davis 		    interface->rbuf_len) {
44547c08596SBrooks Davis 			interface->rbuf_offset = interface->rbuf_len;
44647c08596SBrooks Davis 			continue;
44747c08596SBrooks Davis 		}
44847c08596SBrooks Davis 
4494eae015dSBrooks Davis 		/* Skip over the BPF header... */
4504eae015dSBrooks Davis 		interface->rbuf_offset += hdr.bh_hdrlen;
4514eae015dSBrooks Davis 
45247c08596SBrooks Davis 		/*
45347c08596SBrooks Davis 		 * If the captured data wasn't the whole packet, or if
45447c08596SBrooks Davis 		 * the packet won't fit in the input buffer, all we can
45547c08596SBrooks Davis 		 * do is drop it.
45647c08596SBrooks Davis 		 */
45747c08596SBrooks Davis 		if (hdr.bh_caplen != hdr.bh_datalen) {
458289d89d8SBrooks Davis 			interface->rbuf_offset =
459289d89d8SBrooks Davis 			    BPF_WORDALIGN(interface->rbuf_offset +
460289d89d8SBrooks Davis 			    hdr.bh_caplen);
46147c08596SBrooks Davis 			continue;
46247c08596SBrooks Davis 		}
46347c08596SBrooks Davis 
46447c08596SBrooks Davis 		/* Decode the physical header... */
46547c08596SBrooks Davis 		offset = decode_hw_header(interface->rbuf,
46647c08596SBrooks Davis 		    interface->rbuf_offset, hfrom);
46747c08596SBrooks Davis 
46847c08596SBrooks Davis 		/*
46947c08596SBrooks Davis 		 * If a physical layer checksum failed (dunno of any
47047c08596SBrooks Davis 		 * physical layer that supports this, but WTH), skip
47147c08596SBrooks Davis 		 * this packet.
47247c08596SBrooks Davis 		 */
47347c08596SBrooks Davis 		if (offset < 0) {
474289d89d8SBrooks Davis 			interface->rbuf_offset =
475289d89d8SBrooks Davis 			    BPF_WORDALIGN(interface->rbuf_offset +
476289d89d8SBrooks Davis 			    hdr.bh_caplen);
47747c08596SBrooks Davis 			continue;
47847c08596SBrooks Davis 		}
47947c08596SBrooks Davis 		interface->rbuf_offset += offset;
48047c08596SBrooks Davis 		hdr.bh_caplen -= offset;
48147c08596SBrooks Davis 
48247c08596SBrooks Davis 		/* Decode the IP and UDP headers... */
48347c08596SBrooks Davis 		offset = decode_udp_ip_header(interface->rbuf,
48447c08596SBrooks Davis 		    interface->rbuf_offset, from, NULL, hdr.bh_caplen);
48547c08596SBrooks Davis 
48647c08596SBrooks Davis 		/* If the IP or UDP checksum was bad, skip the packet... */
48747c08596SBrooks Davis 		if (offset < 0) {
488289d89d8SBrooks Davis 			interface->rbuf_offset =
489289d89d8SBrooks Davis 			    BPF_WORDALIGN(interface->rbuf_offset +
490289d89d8SBrooks Davis 			    hdr.bh_caplen);
49147c08596SBrooks Davis 			continue;
49247c08596SBrooks Davis 		}
49347c08596SBrooks Davis 		interface->rbuf_offset += offset;
49447c08596SBrooks Davis 		hdr.bh_caplen -= offset;
49547c08596SBrooks Davis 
49647c08596SBrooks Davis 		/*
49747c08596SBrooks Davis 		 * If there's not enough room to stash the packet data,
49847c08596SBrooks Davis 		 * we have to skip it (this shouldn't happen in real
49947c08596SBrooks Davis 		 * life, though).
50047c08596SBrooks Davis 		 */
50147c08596SBrooks Davis 		if (hdr.bh_caplen > len) {
502289d89d8SBrooks Davis 			interface->rbuf_offset =
503289d89d8SBrooks Davis 			    BPF_WORDALIGN(interface->rbuf_offset +
504289d89d8SBrooks Davis 			    hdr.bh_caplen);
50547c08596SBrooks Davis 			continue;
50647c08596SBrooks Davis 		}
50747c08596SBrooks Davis 
50847c08596SBrooks Davis 		/* Copy out the data in the packet... */
50947c08596SBrooks Davis 		memcpy(buf, interface->rbuf + interface->rbuf_offset,
51047c08596SBrooks Davis 		    hdr.bh_caplen);
511289d89d8SBrooks Davis 		interface->rbuf_offset =
512289d89d8SBrooks Davis 		    BPF_WORDALIGN(interface->rbuf_offset +
513289d89d8SBrooks Davis 		    hdr.bh_caplen);
51447c08596SBrooks Davis 		return (hdr.bh_caplen);
51547c08596SBrooks Davis 	} while (!length);
51647c08596SBrooks Davis 	return (0);
51747c08596SBrooks Davis }
518