xref: /freebsd/contrib/libpcap/pcap-linux.c (revision 4cf49a43559ed9fdad601bdcccd2c55963008675)
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
22 static 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 
48 static 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 
57 void linux_restore_ifr(void);
58 
59 int
60 pcap_stats(pcap_t *p, struct pcap_stat *ps)
61 {
62 
63 	*ps = p->md.stat;
64 	return (0);
65 }
66 
67 int
68 pcap_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 
85 again:
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 
140 pcap_t *
141 pcap_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);
302 bad:
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 
313 int
314 pcap_setfilter(pcap_t *p, struct bpf_program *fp)
315 {
316 
317 	p->fcode = *fp;
318 	return (0);
319 }
320 
321 void
322 linux_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