xref: /openbsd/sbin/dhclient/bpf.c (revision f050b4cb)
1 /*	$OpenBSD: bpf.c,v 1.56 2017/07/07 15:14:47 krw 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 <sys/ioctl.h>
44 #include <sys/queue.h>
45 #include <sys/socket.h>
46 #include <sys/types.h>
47 
48 #include <net/bpf.h>
49 #include <net/if.h>
50 
51 #include <netinet/in.h>
52 #include <netinet/ip.h>
53 #include <netinet/udp.h>
54 #include <netinet/if_ether.h>
55 
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 
64 #include "dhcp.h"
65 #include "dhcpd.h"
66 #include "log.h"
67 
68 void if_register_bpf(struct interface_info *ifi);
69 
70 /*
71  * Called by get_interface_list for each interface that's discovered.
72  * Opens a packet filter for each interface and adds it to the select
73  * mask.
74  */
75 void
76 if_register_bpf(struct interface_info *ifi)
77 {
78 	struct ifreq ifr;
79 	int sock;
80 
81 	if ((sock = open("/dev/bpf", O_RDWR | O_CLOEXEC)) == -1)
82 		fatal("Can't open bpf");
83 
84 	/* Set the BPF device to point at this interface. */
85 	strlcpy(ifr.ifr_name, ifi->name, IFNAMSIZ);
86 	if (ioctl(sock, BIOCSETIF, &ifr) < 0)
87 		fatal("Can't attach interface %s to /dev/bpf", ifi->name);
88 
89 	ifi->bfdesc = sock;
90 }
91 
92 void
93 if_register_send(struct interface_info *ifi)
94 {
95 	int sock, on = 1;
96 
97 	/*
98 	 * Use raw socket for unicast send.
99 	 */
100 	if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1)
101 		fatal("socket(SOCK_RAW)");
102 	if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on,
103 	    sizeof(on)) == -1)
104 		fatal("setsockopt(IP_HDRINCL)");
105 	if (setsockopt(sock, IPPROTO_IP, SO_RTABLE, &ifi->rdomain,
106 	    sizeof(ifi->rdomain)) == -1)
107 		fatal("setsockopt(SO_RTABLE)");
108 
109 	ifi->ufdesc = sock;
110 }
111 
112 /*
113  * Packet filter program.
114  *
115  * XXX: Changes to the filter program may require changes to the
116  * constant offsets used in if_register_receive to patch the BPF program!
117  */
118 struct bpf_insn dhcp_bpf_filter[] = {
119 	/* Make sure this is an IP packet. */
120 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
121 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
122 
123 	/* Make sure it's a UDP packet. */
124 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
125 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
126 
127 	/* Make sure this isn't a fragment. */
128 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
129 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
130 
131 	/* Get the IP header length. */
132 	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
133 
134 	/* Make sure it's to the right port. */
135 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
136 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1),		/* patch */
137 
138 	/* If we passed all the tests, ask for the whole packet. */
139 	BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
140 
141 	/* Otherwise, drop it. */
142 	BPF_STMT(BPF_RET+BPF_K, 0),
143 };
144 
145 int dhcp_bpf_filter_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn);
146 
147 /*
148  * Packet write filter program:
149  * 'ip and udp and src port bootps and dst port (bootps or bootpc)'
150  */
151 struct bpf_insn dhcp_bpf_wfilter[] = {
152 	BPF_STMT(BPF_LD + BPF_B + BPF_IND, 14),
153 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 12),
154 
155 	/* Make sure this is an IP packet. */
156 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
157 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10),
158 
159 	/* Make sure it's a UDP packet. */
160 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
161 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8),
162 
163 	/* Make sure this isn't a fragment. */
164 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
165 	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0),	/* patched */
166 
167 	/* Get the IP header length. */
168 	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
169 
170 	/* Make sure it's from the right port. */
171 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14),
172 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 68, 0, 3),
173 
174 	/* Make sure it is to the right ports. */
175 	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
176 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1),
177 
178 	/* If we passed all the tests, ask for the whole packet. */
179 	BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
180 
181 	/* Otherwise, drop it. */
182 	BPF_STMT(BPF_RET+BPF_K, 0),
183 };
184 
185 int dhcp_bpf_wfilter_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct bpf_insn);
186 
187 void
188 if_register_receive(struct interface_info *ifi)
189 {
190 	struct bpf_version v;
191 	struct bpf_program p;
192 	int flag = 1, sz;
193 
194 	/* Open a BPF device and hang it on this interface. */
195 	if_register_bpf(ifi);
196 
197 	/* Make sure the BPF version is in range. */
198 	if (ioctl(ifi->bfdesc, BIOCVERSION, &v) < 0)
199 		fatal("Can't get BPF version");
200 
201 	if (v.bv_major != BPF_MAJOR_VERSION ||
202 	    v.bv_minor < BPF_MINOR_VERSION)
203 		fatalx("Kernel BPF version out of range - recompile "
204 		    "dhclient!");
205 
206 	/*
207 	 * Set immediate mode so that reads return as soon as a packet
208 	 * comes in, rather than waiting for the input buffer to fill
209 	 * with packets.
210 	 */
211 	if (ioctl(ifi->bfdesc, BIOCIMMEDIATE, &flag) < 0)
212 		fatal("Can't set immediate mode on bpf device");
213 
214 	if (ioctl(ifi->bfdesc, BIOCSFILDROP, &flag) < 0)
215 		fatal("Can't set filter-drop mode on bpf device");
216 
217 	/* Get the required BPF buffer length from the kernel. */
218 	if (ioctl(ifi->bfdesc, BIOCGBLEN, &sz) < 0)
219 		fatal("Can't get bpf buffer length");
220 	ifi->rbuf_max = sz;
221 	ifi->rbuf = malloc(ifi->rbuf_max);
222 	if (!ifi->rbuf)
223 		fatalx("Can't allocate %lu bytes for bpf input buffer.",
224 		    (unsigned long)ifi->rbuf_max);
225 	ifi->rbuf_offset = 0;
226 	ifi->rbuf_len = 0;
227 
228 	/* Set up the bpf filter program structure. */
229 	p.bf_len = dhcp_bpf_filter_len;
230 	p.bf_insns = dhcp_bpf_filter;
231 
232 	/* Patch the server port into the BPF program.
233 	 *
234 	 * XXX: changes to filter program may require changes to the
235 	 * insn number(s) used below!
236 	 */
237 	dhcp_bpf_filter[8].k = LOCAL_PORT;
238 
239 	if (ioctl(ifi->bfdesc, BIOCSETF, &p) < 0)
240 		fatal("Can't install packet filter program");
241 
242 	/* Set up the bpf write filter program structure. */
243 	p.bf_len = dhcp_bpf_wfilter_len;
244 	p.bf_insns = dhcp_bpf_wfilter;
245 
246 	if (dhcp_bpf_wfilter[7].k == 0x1fff)
247 		dhcp_bpf_wfilter[7].k = htons(IP_MF|IP_OFFMASK);
248 
249 	if (ioctl(ifi->bfdesc, BIOCSETWF, &p) < 0)
250 		fatal("Can't install write filter program");
251 
252 	if (ioctl(ifi->bfdesc, BIOCLOCK, NULL) < 0)
253 		fatalx("Cannot lock bpf");
254 }
255 
256 ssize_t
257 send_packet(struct interface_info *ifi, struct in_addr from, struct in_addr to)
258 {
259 	struct sockaddr_in dest;
260 	struct ether_header eh;
261 	struct ip ip;
262 	struct udphdr udp;
263 	struct iovec iov[4];
264 	struct msghdr msg;
265 	struct dhcp_packet *packet = &ifi->sent_packet;
266 	ssize_t result;
267 	int iovcnt = 0, len = ifi->sent_packet_length;
268 
269 	memset(&dest, 0, sizeof(dest));
270 	dest.sin_family = AF_INET;
271 	dest.sin_port = htons(REMOTE_PORT);
272 	dest.sin_addr.s_addr = to.s_addr;
273 
274 	if (to.s_addr == INADDR_BROADCAST) {
275 		assemble_eh_header(ifi->hw_address, &eh);
276 		iov[0].iov_base = &eh;
277 		iov[0].iov_len = sizeof(eh);
278 		iovcnt++;
279 	}
280 
281 	ip.ip_v = 4;
282 	ip.ip_hl = 5;
283 	ip.ip_tos = IPTOS_LOWDELAY;
284 	ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
285 	ip.ip_id = 0;
286 	ip.ip_off = 0;
287 	ip.ip_ttl = 128;
288 	ip.ip_p = IPPROTO_UDP;
289 	ip.ip_sum = 0;
290 	ip.ip_src.s_addr = from.s_addr;
291 	ip.ip_dst.s_addr = to.s_addr;
292 	ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
293 	iov[iovcnt].iov_base = &ip;
294 	iov[iovcnt].iov_len = sizeof(ip);
295 	iovcnt++;
296 
297 	udp.uh_sport = htons(LOCAL_PORT);
298 	udp.uh_dport = htons(REMOTE_PORT);
299 	udp.uh_ulen = htons(sizeof(udp) + len);
300 	udp.uh_sum = 0;
301 	udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
302 	    checksum((unsigned char *)packet, len,
303 	    checksum((unsigned char *)&ip.ip_src,
304 	    2 * sizeof(ip.ip_src),
305 	    IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
306 	iov[iovcnt].iov_base = &udp;
307 	iov[iovcnt].iov_len = sizeof(udp);
308 	iovcnt++;
309 
310 	iov[iovcnt].iov_base = packet;
311 	iov[iovcnt].iov_len = len;
312 	iovcnt++;
313 
314 	if (to.s_addr == INADDR_BROADCAST) {
315 		result = writev(ifi->bfdesc, iov, iovcnt);
316 	} else {
317 		memset(&msg, 0, sizeof(msg));
318 		msg.msg_name = (struct sockaddr *)&dest;
319 		msg.msg_namelen = sizeof(dest);
320 		msg.msg_iov = iov;
321 		msg.msg_iovlen = iovcnt;
322 		result = sendmsg(ifi->ufdesc, &msg, 0);
323 	}
324 
325 	if (result == -1)
326 		log_warn("send_packet");
327 	return (result);
328 }
329 
330 ssize_t
331 receive_packet(struct interface_info *ifi, struct sockaddr_in *from,
332     struct ether_addr *hfrom)
333 {
334 	struct dhcp_packet *packet = &ifi->recv_packet;
335 	int length = 0, offset = 0;
336 	struct bpf_hdr hdr;
337 
338 	/*
339 	 * All this complexity is because BPF doesn't guarantee that
340 	 * only one packet will be returned at a time.  We're getting
341 	 * what we deserve, though - this is a terrible abuse of the BPF
342 	 * interface.  Sigh.
343 	 */
344 
345 	/* Process packets until we get one we can return or until we've
346 	 * done a read and gotten nothing we can return.
347 	 */
348 	do {
349 		/* If the buffer is empty, fill it. */
350 		if (ifi->rbuf_offset >= ifi->rbuf_len) {
351 			length = read(ifi->bfdesc, ifi->rbuf, ifi->rbuf_max);
352 			if (length <= 0)
353 				return (length);
354 			ifi->rbuf_offset = 0;
355 			ifi->rbuf_len = length;
356 		}
357 
358 		/*
359 		 * If there isn't room for a whole bpf header, something
360 		 * went wrong, but we'll ignore it and hope it goes
361 		 * away. XXX
362 		 */
363 		if (ifi->rbuf_len - ifi->rbuf_offset < sizeof(hdr)) {
364 			ifi->rbuf_offset = ifi->rbuf_len;
365 			continue;
366 		}
367 
368 		/* Copy out a bpf header. */
369 		memcpy(&hdr, &ifi->rbuf[ifi->rbuf_offset], sizeof(hdr));
370 
371 		/*
372 		 * If the bpf header plus data doesn't fit in what's
373 		 * left of the buffer, stick head in sand yet again.
374 		 */
375 		if (ifi->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen >
376 		    ifi->rbuf_len) {
377 			ifi->rbuf_offset = ifi->rbuf_len;
378 			continue;
379 		}
380 
381 		/*
382 		 * If the captured data wasn't the whole packet, or if
383 		 * the packet won't fit in the input buffer, all we can
384 		 * do is drop it.
385 		 */
386 		if (hdr.bh_caplen != hdr.bh_datalen) {
387 			ifi->rbuf_offset = BPF_WORDALIGN(
388 			    ifi->rbuf_offset + hdr.bh_hdrlen +
389 			    hdr.bh_caplen);
390 			continue;
391 		}
392 
393 		/* Skip over the BPF header. */
394 		ifi->rbuf_offset += hdr.bh_hdrlen;
395 
396 		/* Decode the physical header. */
397 		offset = decode_hw_header(ifi->rbuf + ifi->rbuf_offset,
398 		    hdr.bh_caplen, hfrom);
399 
400 		/*
401 		 * If a physical layer checksum failed (dunno of any
402 		 * physical layer that supports this, but WTH), skip
403 		 * this packet.
404 		 */
405 		if (offset < 0) {
406 			ifi->rbuf_offset = BPF_WORDALIGN(
407 			    ifi->rbuf_offset + hdr.bh_caplen);
408 			continue;
409 		}
410 		ifi->rbuf_offset += offset;
411 		hdr.bh_caplen -= offset;
412 
413 		/* Decode the IP and UDP headers. */
414 		offset = decode_udp_ip_header(ifi->rbuf + ifi->rbuf_offset,
415 		    hdr.bh_caplen, from);
416 
417 		/* If the IP or UDP checksum was bad, skip the packet. */
418 		if (offset < 0) {
419 			ifi->rbuf_offset = BPF_WORDALIGN(
420 			    ifi->rbuf_offset + hdr.bh_caplen);
421 			continue;
422 		}
423 		ifi->rbuf_offset += offset;
424 		hdr.bh_caplen -= offset;
425 
426 		/*
427 		 * If there's not enough room to stash the packet data,
428 		 * we have to skip it (this shouldn't happen in real
429 		 * life, though).
430 		 */
431 		if (hdr.bh_caplen > sizeof(*packet)) {
432 			ifi->rbuf_offset = BPF_WORDALIGN(
433 			    ifi->rbuf_offset + hdr.bh_caplen);
434 			continue;
435 		}
436 
437 		/* Copy out the data in the packet. */
438 		memset(packet, DHO_END, sizeof(*packet));
439 		memcpy(packet, ifi->rbuf + ifi->rbuf_offset, hdr.bh_caplen);
440 		ifi->rbuf_offset = BPF_WORDALIGN(ifi->rbuf_offset +
441 		    hdr.bh_caplen);
442 		return (hdr.bh_caplen);
443 	} while (!length);
444 	return (0);
445 }
446