pcap-linux.c revision 39291
1/*
2 * Copyright (c) 1996, 1997
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21#ifndef lint
22static const char rcsid[] =
23    "@(#) $Header: pcap-linux.c,v 1.15 97/10/02 22:39:37 leres Exp $ (LBL)";
24#endif
25
26#include <sys/param.h>
27#include <sys/ioctl.h>
28#include <sys/socket.h>
29#include <sys/time.h>
30
31#include <net/if.h>
32#ifdef HAVE_NET_IF_ARP_H
33#include <net/if_arp.h>
34#else
35#include <linux/if_arp.h>
36#endif
37#include <linux/if_ether.h>
38
39#include <netinet/in.h>
40
41#include <errno.h>
42#include <malloc.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47
48static struct ifreq saved_ifr;
49
50#include "pcap-int.h"
51
52#include "gnuc.h"
53#ifdef HAVE_OS_PROTO_H
54#include "os-proto.h"
55#endif
56
57void linux_restore_ifr(void);
58
59int
60pcap_stats(pcap_t *p, struct pcap_stat *ps)
61{
62
63	*ps = p->md.stat;
64	return (0);
65}
66
67int
68pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
69{
70	register int cc;
71	register int bufsize;
72	register int caplen;
73	register u_char *bp;
74	struct sockaddr from;
75	int fromlen;
76
77	bp = p->buffer + p->offset;
78	bufsize = p->bufsize;
79	if (p->md.pad > 0) {
80		memset(bp, 0, p->md.pad);
81		bp += p->md.pad;
82		bufsize -= p->md.pad;
83	}
84
85again:
86	do {
87		fromlen = sizeof(from);
88		cc = recvfrom(p->fd, bp, bufsize, 0, &from, &fromlen);
89		if (cc < 0) {
90			/* Don't choke when we get ptraced */
91			switch (errno) {
92
93			case EINTR:
94					goto again;
95
96			case EWOULDBLOCK:
97				return (0);		/* XXX */
98			}
99			sprintf(p->errbuf, "read: %s", pcap_strerror(errno));
100			return (-1);
101		}
102	} while (strcmp(p->md.device, from.sa_data));
103
104	/* If we need have leading zero bytes, adjust count */
105	cc += p->md.pad;
106	bp = p->buffer + p->offset;
107
108	/* If we need to step over leading junk, adjust count and pointer */
109	cc -= p->md.skip;
110	bp += p->md.skip;
111
112	/* Captured length can't exceed our read buffer size */
113	caplen = cc;
114	if (caplen > bufsize)
115		caplen = bufsize;
116
117	/* Captured length can't exceed the snapshot length */
118	if (caplen > p->snapshot)
119		caplen = p->snapshot;
120
121	if (p->fcode.bf_insns == NULL ||
122	    bpf_filter(p->fcode.bf_insns, bp, cc, caplen)) {
123		struct pcap_pkthdr h;
124
125		++p->md.stat.ps_recv;
126		/* Get timestamp */
127		if (ioctl(p->fd, SIOCGSTAMP, &h.ts) < 0) {
128			sprintf(p->errbuf, "SIOCGSTAMP: %s",
129			    pcap_strerror(errno));
130			return (-1);
131		}
132		h.len = cc;
133		h.caplen = caplen;
134		(*callback)(user, &h, bp);
135		return (1);
136	}
137	return (0);
138}
139
140pcap_t *
141pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
142{
143	register int fd, broadcast;
144	register pcap_t *p;
145	struct ifreq ifr;
146	struct sockaddr sa;
147
148	p = (pcap_t *)malloc(sizeof(*p));
149	if (p == NULL) {
150		sprintf(ebuf, "malloc: %s", pcap_strerror(errno));
151		return (NULL);
152	}
153	memset(p, 0, sizeof(*p));
154	fd = -1;
155
156	fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL));
157	if (fd < 0) {
158		sprintf(ebuf, "socket: %s", pcap_strerror(errno));
159		goto bad;
160	}
161	p->fd = fd;
162
163	/* Bind to the interface name */
164	memset(&sa, 0, sizeof(sa));
165	sa.sa_family = AF_INET;
166	(void)strncpy(sa.sa_data, device, sizeof(sa.sa_data));
167	if (bind(p->fd, &sa, sizeof(sa))) {
168		sprintf(ebuf, "bind: %s: %s", device, pcap_strerror(errno));
169		goto bad;
170	}
171
172	memset(&ifr, 0, sizeof(ifr));
173	strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
174	if (ioctl(p->fd, SIOCGIFHWADDR, &ifr) < 0 ) {
175		sprintf(ebuf, "SIOCGIFHWADDR: %s", pcap_strerror(errno));
176		goto bad;
177	}
178	broadcast = 0;
179	switch (ifr.ifr_hwaddr.sa_family) {
180
181	case ARPHRD_ETHER:
182	case ARPHRD_METRICOM:
183		p->linktype = DLT_EN10MB;
184		p->offset = 2;
185		++broadcast;
186		break;
187
188	case ARPHRD_EETHER:
189		p->linktype = DLT_EN3MB;
190		++broadcast;
191		break;
192
193	case ARPHRD_AX25:
194		p->linktype = DLT_AX25;
195		++broadcast;
196		break;
197
198	case ARPHRD_PRONET:
199		p->linktype = DLT_PRONET;
200		break;
201
202	case ARPHRD_CHAOS:
203		p->linktype = DLT_CHAOS;
204		break;
205
206	case ARPHRD_IEEE802:
207		p->linktype = DLT_IEEE802;
208		++broadcast;
209		break;
210
211	case ARPHRD_ARCNET:
212		p->linktype = DLT_ARCNET;
213		++broadcast;
214		break;
215
216	case ARPHRD_SLIP:
217	case ARPHRD_CSLIP:
218	case ARPHRD_SLIP6:
219	case ARPHRD_CSLIP6:
220	case ARPHRD_PPP:
221		p->linktype = DLT_RAW;
222		break;
223
224	case ARPHRD_LOOPBACK:
225		p->linktype = DLT_NULL;
226		p->md.pad = 2;
227		p->md.skip = 12;
228		break;
229
230#ifdef ARPHRD_FDDI
231	/* Not all versions of the kernel has this define */
232	case ARPHRD_FDDI:
233		p->linktype = DLT_FDDI;
234		++broadcast;
235		break;
236#endif
237
238#ifdef notdef
239	case ARPHRD_LOCALTLK:
240	case ARPHRD_NETROM:
241	case ARPHRD_APPLETLK:
242	case ARPHRD_DLCI:
243	case ARPHRD_RSRVD:
244	case ARPHRD_ADAPT:
245	case ARPHRD_TUNNEL:
246	case ARPHRD_TUNNEL6:
247	case ARPHRD_FRAD:
248	case ARPHRD_SKIP:
249		/* XXX currently do not know what to do with these... */
250		abort();
251#endif
252
253	default:
254		sprintf(ebuf, "unknown physical layer type 0x%x",
255		    ifr.ifr_hwaddr.sa_family);
256		goto bad;
257	}
258
259	/* Base the buffer size on the interface MTU */
260	memset(&ifr, 0, sizeof(ifr));
261	strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
262	if (ioctl(p->fd, SIOCGIFMTU, &ifr) < 0 ) {
263		sprintf(ebuf, "SIOCGIFMTU: %s", pcap_strerror(errno));
264		goto bad;
265	}
266
267	/* Leave room for link header (which is never large under linux...) */
268	p->bufsize = ifr.ifr_mtu + 64;
269
270	p->buffer = (u_char *)malloc(p->bufsize + p->offset);
271	if (p->buffer == NULL) {
272		sprintf(ebuf, "malloc: %s", pcap_strerror(errno));
273		goto bad;
274	}
275
276	/* XXX */
277	if (promisc && broadcast) {
278		memset(&ifr, 0, sizeof(ifr));
279		strcpy(ifr.ifr_name, device);
280		if (ioctl(p->fd, SIOCGIFFLAGS, &ifr) < 0 ) {
281			sprintf(ebuf, "SIOCGIFFLAGS: %s", pcap_strerror(errno));
282			goto bad;
283		}
284		saved_ifr = ifr;
285		ifr.ifr_flags |= IFF_PROMISC;
286		if (ioctl(p->fd, SIOCSIFFLAGS, &ifr) < 0 ) {
287			sprintf(ebuf, "SIOCSIFFLAGS: %s", pcap_strerror(errno));
288			goto bad;
289		}
290		ifr.ifr_flags &= ~IFF_PROMISC;
291		atexit(linux_restore_ifr);
292	}
293
294	p->md.device = strdup(device);
295	if (p->md.device == NULL) {
296		sprintf(ebuf, "malloc: %s", pcap_strerror(errno));
297		goto bad;
298	}
299	p->snapshot = snaplen;
300
301	return (p);
302bad:
303	if (fd >= 0)
304		(void)close(fd);
305	if (p->buffer != NULL)
306		free(p->buffer);
307	if (p->md.device != NULL)
308		free(p->md.device);
309	free(p);
310	return (NULL);
311}
312
313int
314pcap_setfilter(pcap_t *p, struct bpf_program *fp)
315{
316
317	p->fcode = *fp;
318	return (0);
319}
320
321void
322linux_restore_ifr(void)
323{
324	register int fd;
325
326	fd = socket(PF_INET, SOCK_PACKET, htons(0x0003));
327	if (fd < 0)
328		fprintf(stderr, "linux socket: %s", pcap_strerror(errno));
329	else if (ioctl(fd, SIOCSIFFLAGS, &saved_ifr) < 0)
330		fprintf(stderr, "linux SIOCSIFFLAGS: %s", pcap_strerror(errno));
331}
332