GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/dhclient/bpf.c Lines: 0 154 0.0 %
Date: 2017-11-07 Branches: 0 64 0.0 %

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