xref: /openbsd-src/lib/libpcap/pcap-bpf.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: pcap-bpf.c,v 1.12 2000/04/26 21:25:53 jakob Exp $	*/
2 
3 /*
4  * Copyright (c) 1993, 1994, 1995, 1996, 1998
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that: (1) source code distributions
9  * retain the above copyright notice and this paragraph in its entirety, (2)
10  * distributions including binary code include the above copyright notice and
11  * this paragraph in its entirety in the documentation or other materials
12  * provided with the distribution, and (3) all advertising materials mentioning
13  * features or use of this software display the following acknowledgement:
14  * ``This product includes software developed by the University of California,
15  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
16  * the University nor the names of its contributors may be used to endorse
17  * or promote products derived from this software without specific prior
18  * written permission.
19  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
20  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22  */
23 #ifndef lint
24 static const char rcsid[] =
25     "@(#) $Header: /home/cvs/src/lib/libpcap/pcap-bpf.c,v 1.12 2000/04/26 21:25:53 jakob Exp $ (LBL)";
26 #endif
27 
28 #include <sys/param.h>			/* optionally get BSD define */
29 #include <sys/time.h>
30 #include <sys/timeb.h>
31 #include <sys/socket.h>
32 #include <sys/ioctl.h>
33 
34 #include <net/if.h>
35 
36 #include <ctype.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <netdb.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include "pcap-int.h"
46 
47 #ifdef HAVE_OS_PROTO_H
48 #include "os-proto.h"
49 #endif
50 
51 #include "gencode.h"
52 
53 int
54 pcap_stats(pcap_t *p, struct pcap_stat *ps)
55 {
56 	struct bpf_stat s;
57 
58 	if (ioctl(p->fd, BIOCGSTATS, (caddr_t)&s) < 0) {
59 		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGSTATS: %s",
60 		    pcap_strerror(errno));
61 		return (-1);
62 	}
63 
64 	ps->ps_recv = s.bs_recv;
65 	ps->ps_drop = s.bs_drop;
66 	return (0);
67 }
68 
69 int
70 pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
71 {
72 	int cc;
73 	int n = 0;
74 	register u_char *bp, *ep;
75 
76  again:
77 	cc = p->cc;
78 	if (p->cc == 0) {
79 		cc = read(p->fd, (char *)p->buffer, p->bufsize);
80 		if (cc < 0) {
81 			/* Don't choke when we get ptraced */
82 			switch (errno) {
83 
84 			case EINTR:
85 				goto again;
86 
87 			case EWOULDBLOCK:
88 				return (0);
89 #if defined(sun) && !defined(BSD)
90 			/*
91 			 * Due to a SunOS bug, after 2^31 bytes, the kernel
92 			 * file offset overflows and read fails with EINVAL.
93 			 * The lseek() to 0 will fix things.
94 			 */
95 			case EINVAL:
96 				if (lseek(p->fd, 0L, SEEK_CUR) +
97 				    p->bufsize < 0) {
98 					(void)lseek(p->fd, 0L, SEEK_SET);
99 					goto again;
100 				}
101 				/* fall through */
102 #endif
103 			}
104 			snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read: %s",
105 			    pcap_strerror(errno));
106 			return (-1);
107 		}
108 		bp = p->buffer;
109 	} else
110 		bp = p->bp;
111 
112 	/*
113 	 * Loop through each packet.
114 	 */
115 #define bhp ((struct bpf_hdr *)bp)
116 	ep = bp + cc;
117 	while (bp < ep) {
118 		register int caplen, hdrlen;
119 		caplen = bhp->bh_caplen;
120 		hdrlen = bhp->bh_hdrlen;
121 		/*
122 		 * XXX A bpf_hdr matches a pcap_pkthdr.
123 		 */
124 		(*callback)(user, (struct pcap_pkthdr*)bp, bp + hdrlen);
125 		bp += BPF_WORDALIGN(caplen + hdrlen);
126 		if (++n >= cnt && cnt > 0) {
127 			p->bp = bp;
128 			p->cc = ep - bp;
129 			return (n);
130 		}
131 	}
132 #undef bhp
133 	p->cc = 0;
134 	return (n);
135 }
136 
137 int
138 pcap_inject(pcap_t *p, const void *buf, size_t len)
139 {
140 	return (write(p->fd, buf, len));
141 }
142 
143 static __inline int
144 bpf_open(pcap_t *p, char *errbuf)
145 {
146 	int fd;
147 	int n = 0;
148 	char device[sizeof "/dev/bpf0000000000"];
149 
150 	/*
151 	 * Go through all the minors and find one that isn't in use.
152 	 */
153 	do {
154 		(void)snprintf(device, sizeof device, "/dev/bpf%d", n++);
155 		fd = open(device, O_RDWR);
156 		if (fd < 0 && errno == EACCES)
157 			fd = open(device, O_RDONLY);
158 	} while (fd < 0 && errno == EBUSY);
159 
160 	/*
161 	 * XXX better message for all minors used
162 	 */
163 	if (fd < 0)
164 		snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
165 		    device, pcap_strerror(errno));
166 
167 	return (fd);
168 }
169 
170 pcap_t *
171 pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
172 {
173 	int fd;
174 	struct ifreq ifr;
175 	struct bpf_version bv;
176 	u_int v;
177 	pcap_t *p;
178 
179 	p = (pcap_t *)malloc(sizeof(*p));
180 	if (p == NULL) {
181 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
182 		    pcap_strerror(errno));
183 		return (NULL);
184 	}
185 	bzero(p, sizeof(*p));
186 	fd = bpf_open(p, ebuf);
187 	if (fd < 0)
188 		goto bad;
189 
190 	p->fd = fd;
191 	p->snapshot = snaplen;
192 
193 	if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
194 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
195 		    pcap_strerror(errno));
196 		goto bad;
197 	}
198 	if (bv.bv_major != BPF_MAJOR_VERSION ||
199 	    bv.bv_minor < BPF_MINOR_VERSION) {
200 		snprintf(ebuf, PCAP_ERRBUF_SIZE,
201 		    "kernel bpf filter out of date");
202 		goto bad;
203 	}
204 	v = 32768;	/* XXX this should be a user-accessible hook */
205 	/* Ignore the return value - this is because the call fails on
206 	 * BPF systems that don't have kernel malloc.  And if the call
207 	 * fails, it's no big deal, we just continue to use the standard
208 	 * buffer size.
209 	 */
210 	(void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
211 
212 	(void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
213 	if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
214 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s",
215 		    device, pcap_strerror(errno));
216 		goto bad;
217 	}
218 	/* Get the data link layer type. */
219 	if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) {
220 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
221 		    pcap_strerror(errno));
222 		goto bad;
223 	}
224 #if _BSDI_VERSION - 0 >= 199510
225 	/* The SLIP and PPP link layer header changed in BSD/OS 2.1 */
226 	switch (v) {
227 
228 	case DLT_SLIP:
229 		v = DLT_SLIP_BSDOS;
230 		break;
231 
232 	case DLT_PPP:
233 		v = DLT_PPP_BSDOS;
234 		break;
235 	}
236 #endif
237 	p->linktype = v;
238 
239 	/* set timeout */
240 	if (to_ms != 0) {
241 		struct timeval to;
242 		to.tv_sec = to_ms / 1000;
243 		to.tv_usec = (to_ms * 1000) % 1000000;
244 		if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&to) < 0) {
245 			snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
246 			    pcap_strerror(errno));
247 			goto bad;
248 		}
249 	}
250 	if (promisc)
251 		/* set promiscuous mode, okay if it fails */
252 		(void)ioctl(p->fd, BIOCPROMISC, NULL);
253 
254 	if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
255 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
256 		    pcap_strerror(errno));
257 		goto bad;
258 	}
259 	p->bufsize = v;
260 	p->buffer = (u_char *)malloc(p->bufsize);
261 	if (p->buffer == NULL) {
262 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
263 		    pcap_strerror(errno));
264 		goto bad;
265 	}
266 
267 	return (p);
268  bad:
269 	(void)close(fd);
270 	free(p);
271 	return (NULL);
272 }
273 
274 int
275 pcap_setfilter(pcap_t *p, struct bpf_program *fp)
276 {
277 	/*
278 	 * It looks that BPF code generated by gen_protochain() is not
279 	 * compatible with some of kernel BPF code (for example BSD/OS 3.1).
280 	 * Take a safer side for now.
281 	 */
282 	if (no_optimize)
283 		p->fcode = *fp;
284 	else if (p->sf.rfile != NULL)
285 		p->fcode = *fp;
286 	else if (ioctl(p->fd, BIOCSETF, (caddr_t)fp) < 0) {
287 		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s",
288 		    pcap_strerror(errno));
289 		return (-1);
290 	}
291 	return (0);
292 }
293