xref: /openbsd/usr.sbin/dhcrelay/bpf.c (revision a6445c1d)
1 /*	$OpenBSD: bpf.c,v 1.8 2014/10/25 03:23:49 lteo Exp $ */
2 
3 /* BPF socket interface code, originally contributed by Archie Cobbs. */
4 
5 /*
6  * Copyright (c) 1995, 1996, 1998, 1999
7  * The Internet Software Consortium.    All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of The Internet Software Consortium nor the names
19  *    of its contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * This software has been written for the Internet Software Consortium
37  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38  * Enterprises.  To learn more about the Internet Software Consortium,
39  * see ``http://www.vix.com/isc''.  To learn more about Vixie
40  * Enterprises, see ``http://www.vix.com''.
41  */
42 
43 #include "dhcpd.h"
44 #include <sys/ioctl.h>
45 #include <sys/uio.h>
46 
47 #include <net/bpf.h>
48 #include <net/if_types.h>
49 
50 #include <netinet/ip.h>
51 #include <netinet/udp.h>
52 #include <netinet/if_ether.h>
53 
54 #define BPF_FORMAT "/dev/bpf%d"
55 
56 /*
57  * Called by get_interface_list for each interface that's discovered.
58  * Opens a packet filter for each interface and adds it to the select
59  * mask.
60  */
61 int
62 if_register_bpf(struct interface_info *info)
63 {
64 	char filename[50];
65 	int sock, b;
66 
67 	/* Open a BPF device */
68 	for (b = 0; 1; b++) {
69 		snprintf(filename, sizeof(filename), BPF_FORMAT, b);
70 		sock = open(filename, O_RDWR, 0);
71 		if (sock == -1) {
72 			if (errno == EBUSY)
73 				continue;
74 			else
75 				error("Can't find free bpf: %m");
76 		} else
77 			break;
78 	}
79 
80 	/* Set the BPF device to point at this interface. */
81 	if (ioctl(sock, BIOCSETIF, info->ifp) == -1)
82 		error("Can't attach interface %s to bpf device %s: %m",
83 		    info->name, filename);
84 
85 	return (sock);
86 }
87 
88 void
89 if_register_send(struct interface_info *info)
90 {
91 	/*
92 	 * If we're using the bpf API for sending and receiving, we
93 	 * don't need to register this interface twice.
94 	 */
95 	info->wfdesc = info->rfdesc;
96 }
97 
98 /*
99  * Packet filter program: 'ip and udp and dst port SERVER_PORT'
100  */
101 struct bpf_insn dhcp_bpf_filter[] = {
102 	/* Make sure this is an IP packet... */
103 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
104 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
105 
106 	/* Make sure it's a UDP packet... */
107 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
108 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
109 
110 	/* Make sure this isn't a fragment... */
111 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
112 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
113 
114 	/* Get the IP header length... */
115 	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
116 
117 	/* Make sure it's to the right port... */
118 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
119 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SERVER_PORT, 0, 1),
120 
121 	/* If we passed all the tests, ask for the whole packet. */
122 	BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
123 
124 	/* Otherwise, drop it. */
125 	BPF_STMT(BPF_RET+BPF_K, 0),
126 };
127 
128 int dhcp_bpf_filter_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn);
129 
130 /*
131  * Packet filter program: encapsulated 'ip and udp and dst port SERVER_PORT'
132  */
133 struct bpf_insn dhcp_bpf_efilter[] = {
134 	/* Make sure this is an encapsulated AF_INET packet... */
135 	BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 0),
136 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, AF_INET << 24, 0, 10),
137 
138 	/* Make sure it's an IPIP packet... */
139 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 21),
140 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_IPIP, 0, 8),
141 
142 	/* Make sure it's an encapsulated UDP packet... */
143 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 41),
144 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
145 
146 	/* Make sure this isn't a fragment... */
147 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 38),
148 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
149 
150 	/* Get the IP header length... */
151 	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 32),
152 
153 	/* Make sure it's to the right port... */
154 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 34),
155 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SERVER_PORT, 0, 1),
156 
157 	/* If we passed all the tests, ask for the whole packet. */
158 	BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
159 
160 	/* Otherwise, drop it. */
161 	BPF_STMT(BPF_RET+BPF_K, 0),
162 };
163 
164 int dhcp_bpf_efilter_len = sizeof(dhcp_bpf_efilter) / sizeof(struct bpf_insn);
165 
166 /*
167  * Packet write filter program: 'ip and udp and src port SERVER_PORT'
168  */
169 struct bpf_insn dhcp_bpf_wfilter[] = {
170 	/* Make sure this is an IP packet... */
171 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
172 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
173 
174 	/* Make sure it's a UDP packet... */
175 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
176 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
177 
178 	/* Make sure this isn't a fragment... */
179 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
180 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
181 
182 	/* Get the IP header length... */
183 	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
184 
185 	/* Make sure it's from the right port... */
186 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14),
187 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SERVER_PORT, 0, 1),
188 
189 	/* If we passed all the tests, ask for the whole packet. */
190 	BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
191 
192 	/* Otherwise, drop it. */
193 	BPF_STMT(BPF_RET+BPF_K, 0),
194 };
195 
196 int dhcp_bpf_wfilter_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct bpf_insn);
197 
198 void
199 if_register_receive(struct interface_info *info)
200 {
201 	struct bpf_version v;
202 	struct bpf_program p;
203 	int flag = 1, sz, cmplt = 0;
204 
205 	/* Open a BPF device and hang it on this interface... */
206 	info->rfdesc = if_register_bpf(info);
207 
208 	/* Make sure the BPF version is in range... */
209 	if (ioctl(info->rfdesc, BIOCVERSION, &v) == -1)
210 		error("Can't get BPF version: %m");
211 
212 	if (v.bv_major != BPF_MAJOR_VERSION ||
213 	    v.bv_minor < BPF_MINOR_VERSION)
214 		error("Kernel BPF version out of range - recompile dhcpd!");
215 
216 	/*
217 	 * Set immediate mode so that reads return as soon as a packet
218 	 * comes in, rather than waiting for the input buffer to fill
219 	 * with packets.
220 	 */
221 	if (ioctl(info->rfdesc, BIOCIMMEDIATE, &flag) == -1)
222 		error("Can't set immediate mode on bpf device: %m");
223 
224 	/* make sure kernel fills in the source ethernet address */
225 	if (ioctl(info->rfdesc, BIOCSHDRCMPLT, &cmplt) == -1)
226 		error("Can't set header complete flag on bpf device: %m");
227 
228 	/* Get the required BPF buffer length from the kernel. */
229 	if (ioctl(info->rfdesc, BIOCGBLEN, &sz) == -1)
230 		error("Can't get bpf buffer length: %m");
231 	info->rbuf_max = sz;
232 	info->rbuf = malloc(info->rbuf_max);
233 	if (!info->rbuf)
234 		error("Can't allocate %lu bytes for bpf input buffer.",
235 		    (unsigned long)info->rbuf_max);
236 	info->rbuf_offset = 0;
237 	info->rbuf_len = 0;
238 
239 	/* Set up the bpf filter program structure. */
240 	if (info->hw_address.htype == HTYPE_IPSEC_TUNNEL) {
241 		p.bf_len = dhcp_bpf_efilter_len;
242 		p.bf_insns = dhcp_bpf_efilter;
243 	} else {
244 		p.bf_len = dhcp_bpf_filter_len;
245 		p.bf_insns = dhcp_bpf_filter;
246 	}
247 	if (ioctl(info->rfdesc, BIOCSETF, &p) == -1)
248 		error("Can't install packet filter program: %m");
249 
250 	/* Set up the bpf write filter program structure. */
251 	p.bf_len = dhcp_bpf_wfilter_len;
252 	p.bf_insns = dhcp_bpf_wfilter;
253 
254 	if (ioctl(info->rfdesc, BIOCSETWF, &p) == -1)
255 		error("Can't install write filter program: %m");
256 
257 	/* make sure these settings cannot be changed after dropping privs */
258 	if (ioctl(info->rfdesc, BIOCLOCK) == -1)
259 		error("Failed to lock bpf descriptor: %m");
260 }
261 
262 ssize_t
263 send_packet(struct interface_info *interface,
264     struct dhcp_packet *raw, size_t len, struct in_addr from,
265     struct sockaddr_in *to, struct hardware *hto)
266 {
267 	unsigned char buf[256];
268 	struct iovec iov[2];
269 	int result, bufp = 0;
270 
271 	if (interface->hw_address.htype == HTYPE_IPSEC_TUNNEL) {
272 		socklen_t slen = sizeof(*to);
273 		result = sendto(server_fd, raw, len, 0,
274 		    (struct sockaddr *)to, slen);
275 		goto done;
276 	}
277 
278 	/* Assemble the headers... */
279 	assemble_hw_header(interface, buf, &bufp, hto);
280 	assemble_udp_ip_header(interface, buf, &bufp, from.s_addr,
281 	    to->sin_addr.s_addr, to->sin_port, (unsigned char *)raw, len);
282 
283 	/* Fire it off */
284 	iov[0].iov_base = (char *)buf;
285 	iov[0].iov_len = bufp;
286 	iov[1].iov_base = (char *)raw;
287 	iov[1].iov_len = len;
288 
289 	result = writev(interface->wfdesc, iov, 2);
290  done:
291 	if (result == -1)
292 		warning("send_packet: %m");
293 	return (result);
294 }
295 
296 ssize_t
297 receive_packet(struct interface_info *interface, unsigned char *buf,
298     size_t len, struct sockaddr_in *from, struct hardware *hfrom)
299 {
300 	int length = 0, offset = 0;
301 	struct bpf_hdr hdr;
302 
303 	/*
304 	 * All this complexity is because BPF doesn't guarantee that
305 	 * only one packet will be returned at a time.  We're getting
306 	 * what we deserve, though - this is a terrible abuse of the BPF
307 	 * interface.  Sigh.
308 	 */
309 
310 	/* Process packets until we get one we can return or until we've
311 	 * done a read and gotten nothing we can return...
312 	 */
313 	do {
314 		/* If the buffer is empty, fill it. */
315 		if (interface->rbuf_offset == interface->rbuf_len) {
316 			length = read(interface->rfdesc, interface->rbuf,
317 			    interface->rbuf_max);
318 			if (length <= 0)
319 				return (length);
320 			interface->rbuf_offset = 0;
321 			interface->rbuf_len = length;
322 		}
323 
324 		/*
325 		 * If there isn't room for a whole bpf header, something
326 		 * went wrong, but we'll ignore it and hope it goes
327 		 * away... XXX
328 		 */
329 		if (interface->rbuf_len - interface->rbuf_offset <
330 		    sizeof(hdr)) {
331 			interface->rbuf_offset = interface->rbuf_len;
332 			continue;
333 		}
334 
335 		/* Copy out a bpf header... */
336 		memcpy(&hdr, &interface->rbuf[interface->rbuf_offset],
337 		    sizeof(hdr));
338 
339 		/*
340 		 * If the bpf header plus data doesn't fit in what's
341 		 * left of the buffer, stick head in sand yet again...
342 		 */
343 		if (interface->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen >
344 		    interface->rbuf_len) {
345 			interface->rbuf_offset = interface->rbuf_len;
346 			continue;
347 		}
348 
349 		/*
350 		 * If the captured data wasn't the whole packet, or if
351 		 * the packet won't fit in the input buffer, all we can
352 		 * do is drop it.
353 		 */
354 		if (hdr.bh_caplen != hdr.bh_datalen) {
355 			interface->rbuf_offset += hdr.bh_hdrlen = hdr.bh_caplen;
356 			continue;
357 		}
358 
359 		/* Skip over the BPF header... */
360 		interface->rbuf_offset += hdr.bh_hdrlen;
361 
362 		/* Decode the physical header... */
363 		offset = decode_hw_header(interface,
364 		    interface->rbuf, interface->rbuf_offset, hfrom);
365 
366 		/*
367 		 * If a physical layer checksum failed (dunno of any
368 		 * physical layer that supports this, but WTH), skip
369 		 * this packet.
370 		 */
371 		if (offset < 0) {
372 			interface->rbuf_offset += hdr.bh_caplen;
373 			continue;
374 		}
375 		interface->rbuf_offset += offset;
376 		hdr.bh_caplen -= offset;
377 
378 		/* Decode the IP and UDP headers... */
379 		offset = decode_udp_ip_header(interface, interface->rbuf,
380 		    interface->rbuf_offset, from, NULL, hdr.bh_caplen);
381 
382 		/* If the IP or UDP checksum was bad, skip the packet... */
383 		if (offset < 0) {
384 			interface->rbuf_offset += hdr.bh_caplen;
385 			continue;
386 		}
387 		interface->rbuf_offset += offset;
388 		hdr.bh_caplen -= offset;
389 
390 		/*
391 		 * If there's not enough room to stash the packet data,
392 		 * we have to skip it (this shouldn't happen in real
393 		 * life, though).
394 		 */
395 		if (hdr.bh_caplen > len) {
396 			interface->rbuf_offset += hdr.bh_caplen;
397 			continue;
398 		}
399 
400 		/* Copy out the data in the packet... */
401 		memcpy(buf, interface->rbuf + interface->rbuf_offset,
402 		    hdr.bh_caplen);
403 		interface->rbuf_offset += hdr.bh_caplen;
404 		return (hdr.bh_caplen);
405 	} while (!length);
406 	return (0);
407 }
408