pcap-linux.c revision 26175
126175Sfenner/*
226175Sfenner * Copyright (c) 1996
326175Sfenner *	The Regents of the University of California.  All rights reserved.
426175Sfenner *
526175Sfenner * Redistribution and use in source and binary forms, with or without
626175Sfenner * modification, are permitted provided that: (1) source code distributions
726175Sfenner * retain the above copyright notice and this paragraph in its entirety, (2)
826175Sfenner * distributions including binary code include the above copyright notice and
926175Sfenner * this paragraph in its entirety in the documentation or other materials
1026175Sfenner * provided with the distribution, and (3) all advertising materials mentioning
1126175Sfenner * features or use of this software display the following acknowledgement:
1226175Sfenner * ``This product includes software developed by the University of California,
1326175Sfenner * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
1426175Sfenner * the University nor the names of its contributors may be used to endorse
1526175Sfenner * or promote products derived from this software without specific prior
1626175Sfenner * written permission.
1726175Sfenner * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
1826175Sfenner * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
1926175Sfenner * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2026175Sfenner */
2126175Sfenner#ifndef lint
2226175Sfennerstatic const char rcsid[] =
2326175Sfenner    "@(#) $Header: pcap-linux.c,v 1.4 96/12/10 23:15:00 leres Exp $ (LBL)";
2426175Sfenner#endif
2526175Sfenner
2626175Sfenner#include <sys/param.h>
2726175Sfenner#include <sys/ioctl.h>
2826175Sfenner#include <sys/socket.h>
2926175Sfenner#include <sys/time.h>
3026175Sfenner
3126175Sfenner#include <net/if.h>
3226175Sfenner
3326175Sfenner#include <netinet/in.h>
3426175Sfenner
3526175Sfenner#include <errno.h>
3626175Sfenner#include <stdio.h>
3726175Sfenner#include <stdlib.h>
3826175Sfenner#include <string.h>
3926175Sfenner#include <unistd.h>
4026175Sfenner
4126175Sfennerstatic struct ifreq saved_ifr;
4226175Sfenner
4326175Sfenner#include "pcap-int.h"
4426175Sfenner
4526175Sfenner#include "gnuc.h"
4626175Sfenner#ifdef HAVE_OS_PROTO_H
4726175Sfenner#include "os-proto.h"
4826175Sfenner#endif
4926175Sfenner
5026175Sfennervoid linux_restore_ifr(void);
5126175Sfenner
5226175Sfennerint
5326175Sfennerpcap_stats(pcap_t *p, struct pcap_stat *ps)
5426175Sfenner{
5526175Sfenner
5626175Sfenner	*ps = p->md.stat;
5726175Sfenner	return (0);
5826175Sfenner}
5926175Sfenner
6026175Sfennerint
6126175Sfennerpcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
6226175Sfenner{
6326175Sfenner	register int cc;
6426175Sfenner	register int bufsize;
6526175Sfenner	register int caplen;
6626175Sfenner	register u_char *bp;
6726175Sfenner	struct sockaddr from;
6826175Sfenner	int fromlen;
6926175Sfenner
7026175Sfenner	bp = (char *)p->buffer;
7126175Sfenner	bufsize = p->bufsize;
7226175Sfenner	if (p->md.pad > 0) {
7326175Sfenner		bp += p->md.pad;
7426175Sfenner		bufsize -= p->md.pad;
7526175Sfenner		memset(p->buffer, 0, p->md.pad);
7626175Sfenner	}
7726175Sfenner
7826175Sfenneragain:
7926175Sfenner	do {
8026175Sfenner		fromlen = sizeof(from);
8126175Sfenner		cc = recvfrom(p->fd, bp, bufsize, 0, &from, &fromlen);
8226175Sfenner		if (cc < 0) {
8326175Sfenner			/* Don't choke when we get ptraced */
8426175Sfenner			switch (errno) {
8526175Sfenner
8626175Sfenner			case EINTR:
8726175Sfenner					goto again;
8826175Sfenner
8926175Sfenner			case EWOULDBLOCK:
9026175Sfenner				return (0);		/* XXX */
9126175Sfenner			}
9226175Sfenner			sprintf(p->errbuf, "read: %s", pcap_strerror(errno));
9326175Sfenner			return (-1);
9426175Sfenner		}
9526175Sfenner	} while (strcmp(p->md.device, from.sa_data));
9626175Sfenner
9726175Sfenner	/* If we need have leading zero bytes, adjust count */
9826175Sfenner	cc += p->md.pad;
9926175Sfenner	bp = p->buffer;
10026175Sfenner
10126175Sfenner	/* If we need to step over leading junk, adjust count and pointer */
10226175Sfenner	cc -= p->md.skip;
10326175Sfenner	bp += p->md.skip;
10426175Sfenner
10526175Sfenner	/* Captured length can't exceed our read buffer size */
10626175Sfenner	caplen = cc;
10726175Sfenner	if (caplen > bufsize)
10826175Sfenner		caplen = bufsize;
10926175Sfenner
11026175Sfenner	/* Captured length can't exceed the snapshot length */
11126175Sfenner	if (caplen > p->snapshot)
11226175Sfenner		caplen = p->snapshot;
11326175Sfenner
11426175Sfenner	if (p->fcode.bf_insns == NULL ||
11526175Sfenner	    bpf_filter(p->fcode.bf_insns, bp, cc, caplen)) {
11626175Sfenner		struct pcap_pkthdr h;
11726175Sfenner
11826175Sfenner		++p->md.stat.ps_recv;
11926175Sfenner		/* Get timestamp */
12026175Sfenner		if (ioctl(p->fd, SIOCGSTAMP, &h.ts) < 0) {
12126175Sfenner			sprintf(p->errbuf, "SIOCGSTAMP: %s",
12226175Sfenner			    pcap_strerror(errno));
12326175Sfenner			return (-1);
12426175Sfenner		}
12526175Sfenner		h.len = cc;
12626175Sfenner		h.caplen = caplen;
12726175Sfenner		(*callback)(user, &h, bp);
12826175Sfenner		return (1);
12926175Sfenner	}
13026175Sfenner	return (0);
13126175Sfenner}
13226175Sfenner
13326175Sfennerpcap_t *
13426175Sfennerpcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
13526175Sfenner{
13626175Sfenner	register int fd, broadcast;
13726175Sfenner	register pcap_t *p;
13826175Sfenner	struct ifreq ifr;
13926175Sfenner
14026175Sfenner	p = (pcap_t *)malloc(sizeof(*p));
14126175Sfenner	if (p == NULL) {
14226175Sfenner		sprintf(ebuf, "malloc: %s", pcap_strerror(errno));
14326175Sfenner		return (NULL);
14426175Sfenner	}
14526175Sfenner	memset((char *)p, 0, sizeof(*p));
14626175Sfenner	fd = -1;
14726175Sfenner
14826175Sfenner	/* XXX hack - map device name to link layer type */
14926175Sfenner	broadcast = 0;
15026175Sfenner	if (strncmp("eth", device, 3) == 0) {
15126175Sfenner		p->linktype = DLT_EN10MB;
15226175Sfenner		++broadcast;
15326175Sfenner	} else if (strncmp("sl", device, 2) == 0) {
15426175Sfenner		p->linktype = DLT_SLIP;
15526175Sfenner		p->md.pad = 16;
15626175Sfenner	} else if (strncmp("ppp", device, 3) == 0) {
15726175Sfenner		p->linktype = DLT_PPP;
15826175Sfenner		p->md.pad = 4;
15926175Sfenner	} else if (strncmp("lo", device, 2) == 0) {
16026175Sfenner		p->linktype = DLT_NULL;
16126175Sfenner		p->md.pad = 2;
16226175Sfenner		p->md.skip = 12;
16326175Sfenner	} else {
16426175Sfenner		sprintf(ebuf, "linux: unknown physical layer type");
16526175Sfenner		goto bad;
16626175Sfenner	}
16726175Sfenner
16826175Sfenner	/* Linux is full of magic numbers */
16926175Sfenner	fd = socket(PF_INET, SOCK_PACKET, htons(0x0003));
17026175Sfenner	if (fd < 0) {
17126175Sfenner		sprintf(ebuf, "linux socket: %s", pcap_strerror(errno));
17226175Sfenner		goto bad;
17326175Sfenner	}
17426175Sfenner	p->fd = fd;
17526175Sfenner
17626175Sfenner
17726175Sfenner	p->bufsize = 4096;				/* XXX */
17826175Sfenner	p->buffer = (u_char *)malloc(p->bufsize);
17926175Sfenner	if (p->buffer == NULL) {
18026175Sfenner		sprintf(ebuf, "malloc: %s", pcap_strerror(errno));
18126175Sfenner		goto bad;
18226175Sfenner	}
18326175Sfenner
18426175Sfenner	/* XXX */
18526175Sfenner	if (promisc && broadcast) {
18626175Sfenner		memset(&ifr, 0, sizeof(ifr));
18726175Sfenner		strcpy(ifr.ifr_name, device);
18826175Sfenner		if (ioctl(p->fd, SIOCGIFFLAGS, &ifr) < 0 ) {
18926175Sfenner			sprintf(ebuf, "SIOCGIFFLAGS: %s", pcap_strerror(errno));
19026175Sfenner			goto bad;
19126175Sfenner		}
19226175Sfenner		saved_ifr = ifr;
19326175Sfenner		ifr.ifr_flags |= IFF_PROMISC;
19426175Sfenner		if (ioctl(p->fd, SIOCSIFFLAGS, &ifr) < 0 ) {
19526175Sfenner			sprintf(ebuf, "SIOCSIFFLAGS: %s", pcap_strerror(errno));
19626175Sfenner			goto bad;
19726175Sfenner		}
19826175Sfenner		ifr.ifr_flags &= ~IFF_PROMISC;
19926175Sfenner		atexit(linux_restore_ifr);
20026175Sfenner	}
20126175Sfenner
20226175Sfenner	p->md.device = strdup(device);
20326175Sfenner	if (p->md.device == NULL) {
20426175Sfenner		sprintf(ebuf, "malloc: %s", pcap_strerror(errno));
20526175Sfenner		goto bad;
20626175Sfenner	}
20726175Sfenner	p->snapshot = snaplen;
20826175Sfenner
20926175Sfenner	return (p);
21026175Sfennerbad:
21126175Sfenner	if (fd >= 0)
21226175Sfenner		(void)close(fd);
21326175Sfenner	if (p->buffer != NULL)
21426175Sfenner		free(p->buffer);
21526175Sfenner	if (p->md.device != NULL)
21626175Sfenner		free(p->md.device);
21726175Sfenner	free(p);
21826175Sfenner	return (NULL);
21926175Sfenner}
22026175Sfenner
22126175Sfennerint
22226175Sfennerpcap_setfilter(pcap_t *p, struct bpf_program *fp)
22326175Sfenner{
22426175Sfenner
22526175Sfenner	p->fcode = *fp;
22626175Sfenner	return (0);
22726175Sfenner}
22826175Sfenner
22926175Sfennervoid
23026175Sfennerlinux_restore_ifr(void)
23126175Sfenner{
23226175Sfenner	register int fd;
23326175Sfenner
23426175Sfenner	fd = socket(PF_INET, SOCK_PACKET, htons(0x0003));
23526175Sfenner	if (fd < 0)
23626175Sfenner		fprintf(stderr, "linux socket: %s", pcap_strerror(errno));
23726175Sfenner	else if (ioctl(fd, SIOCSIFFLAGS, &saved_ifr) < 0)
23826175Sfenner		fprintf(stderr, "linux SIOCSIFFLAGS: %s", pcap_strerror(errno));
23926175Sfenner}
240