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