Libpcap是一个比较好用的抓包工具,tcpdump也是基于它实现的,下面介绍一下android上是如何利用它抓包的
调用示例:由于它是基于c的代码,因此使用c++时记得extern "C",下面代码是结合libnet实现的:
#include <pcap.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char **argv) {
char *dev = NULL; /* capture device name */
char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
pcap_t *handle; /* packet capture handle */
char filter_exp[] = "port 80"; /* filter expression [3] */
struct bpf_program fp; /* compiled filter program (expression) */
bpf_u_int32 mask; /* subnet mask */
bpf_u_int32 net; /* ip */
print_app_banner();
/* check for capture device name on command-line */
if (argc == 2) {
dev = argv[1];
} else if (argc > 2) {
printf("error: unrecognized command-line options\n\n");
print_app_usage();
exit(EXIT_FAILURE);
} else {
/* find a capture device if not specified on command-line */
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
printf("Couldn't find default device: %s\n", errbuf);
exit(EXIT_FAILURE);
}
}
/* get network number and mask associated with capture device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
printf("Couldn't get netmask for device %s: %s\n", dev, errbuf);
net = 0;
mask = 0;
}
/* print capture info */
printf("Device: %s\n", dev);
printf("Filter expression: %s\n", filter_exp);
/* open capture device */
handle = pcap_open_live(dev, SNAP_LEN, 1, 1000, errbuf);
if (handle == NULL) {
printf("Couldn't open device %s: %s\n", dev, errbuf);
exit(EXIT_FAILURE);
}
/* compile the filter expression */
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
printf("Couldn't parse filter %s: %s\n", filter_exp,
pcap_geterr(handle));
exit(EXIT_FAILURE);
}
/* apply the compiled filter */
if (pcap_setfilter(handle, &fp) == -1) {
printf("Couldn't install filter %s: %s\n", filter_exp,
pcap_geterr(handle));
exit(EXIT_FAILURE);
}
pcap_setdirection(handle, PCAP_D_IN);
/* now we can set our callback function */
pcap_loop(handle, 0, got_packet, NULL);
/* cleanup */
pcap_freecode(&fp);
pcap_close(handle);
printf("\nCapture complete.\n");
return 0;
}
上面主要函数有两个,分别是:
一、pcap_lookupdev:这个函数的作用就是寻找当前设备的网卡信息并且
返回第一个合适的网络接口的字符串指针,下面是它的具体实现(libpcap/inet.c):
char *pcap_lookupdev(errbuf)
register char *errbuf;
{
pcap_if_t *alldevs;
/* for old BSD systems, including bsdi3 */
#ifndef IF_NAMESIZE
#define IF_NAMESIZE IFNAMSIZ
#endif
static char device[IF_NAMESIZE + 1];
char *ret;
if (pcap_findalldevs(&alldevs, errbuf) == -1)
return (NULL);
if (alldevs == NULL || (alldevs->flags & PCAP_IF_LOOPBACK)) {
/*
* There are no devices on the list, or the first device
* on the list is a loopback device, which means there
* are no non-loopback devices on the list. This means
* we can't return any device.
*
* XXX - why not return a loopback device? If we can't
* capture on it, it won't be on the list, and if it's
* on the list, there aren't any non-loopback devices,
* so why not just supply it as the default device?
*/
(void)strlcpy(errbuf, "no suitable device found",
PCAP_ERRBUF_SIZE);
ret = NULL;
} else {
/*
* Return the name of the first device on the list.
*/
(void)strlcpy(device, alldevs->name, sizeof(device));
ret = device;
}
pcap_freealldevs(alldevs);
return (ret);
}
这里可以看到主要函数是调用了pcap_findalldevs,这个函数在不同平台上的实现不一样,这里主要介绍一下android平台的实现,(libpacp/fad-gifc.c):
int pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf)
{
pcap_if_t *devlist = NULL;
register int fd;
register struct ifreq *ifrp, *ifend, *ifnext;
int n;
struct ifconf ifc;
char *buf = NULL;
unsigned buf_size;
#if defined (HAVE_SOLARIS) || defined (HAVE_HPUX10_20_OR_LATER)
char *p, *q;
#endif
struct ifreq ifrflags, ifrnetmask, ifrbroadaddr, ifrdstaddr;
struct sockaddr *netmask, *broadaddr, *dstaddr;
size_t netmask_size, broadaddr_size, dstaddr_size;
int ret = 0;
/*
* Create a socket from which to fetch the list of interfaces.
*/
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"socket: %s", pcap_strerror(errno));
return (-1);
}
/*
* Start with an 8K buffer, and keep growing the buffer until
* we have more than "sizeof(ifrp->ifr_name) + MAX_SA_LEN"
* bytes left over in the buffer or we fail to get the
* interface list for some reason other than EINVAL (which is
* presumed here to mean "buffer is too small").
*/
buf_size = 8192;
for (;;) {
buf = malloc(buf_size);
if (buf == NULL) {
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"malloc: %s", pcap_strerror(errno));
(void)close(fd);
return (-1);
}
ifc.ifc_len = buf_size;
ifc.ifc_buf = buf;
memset(buf, 0, buf_size);
if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0
&& errno != EINVAL) {
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"SIOCGIFCONF: %s", pcap_strerror(errno));
(void)close(fd);
free(buf);
return (-1);
}
if (ifc.ifc_len < buf_size &&
(buf_size - ifc.ifc_len) > sizeof(ifrp->ifr_name) + MAX_SA_LEN)
break;
free(buf);
buf_size *= 2;
}
ifrp = (struct ifreq *)buf;
ifend = (struct ifreq *)(buf + ifc.ifc_len);
for (; ifrp < ifend; ifrp = ifnext) {
/*
* XXX - what if this isn't an IPv4 address? Can
* we still get the netmask, etc. with ioctls on
* an IPv4 socket?
*
* The answer is probably platform-dependent, and
* if the answer is "no" on more than one platform,
* the way you work around it is probably platform-
* dependent as well.
*/
n = SA_LEN(&ifrp->ifr_addr) + sizeof(ifrp->ifr_name);
if (n < sizeof(*ifrp))
ifnext = ifrp + 1;
else
ifnext = (struct ifreq *)((char *)ifrp + n);
/*
* XXX - The 32-bit compatibility layer for Linux on IA-64
* is slightly broken. It correctly converts the structures
* to and from kernel land from 64 bit to 32 bit but
* doesn't update ifc.ifc_len, leaving it larger than the
* amount really used. This means we read off the end
* of the buffer and encounter an interface with an
* "empty" name. Since this is highly unlikely to ever
* occur in a valid case we can just finish looking for
* interfaces if we see an empty name.
*/
if (!(*ifrp->ifr_name))
break;
/*
* Skip entries that begin with "dummy".
* XXX - what are these? Is this Linux-specific?
* Are there platforms on which we shouldn't do this?
*/
if (strncmp(ifrp->ifr_name, "dummy", 5) == 0)
continue;
/*
* Get the flags for this interface, and skip it if it's
* not up.
*/
strncpy(ifrflags.ifr_name, ifrp->ifr_name,
sizeof(ifrflags.ifr_name));
if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifrflags) < 0) {
if (errno == ENXIO)
continue;
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"SIOCGIFFLAGS: %.*s: %s",
(int)sizeof(ifrflags.ifr_name),
ifrflags.ifr_name,
pcap_strerror(errno));
ret = -1;
break;
}
if (!(ifrflags.ifr_flags & IFF_UP))
continue;
/*
* Get the netmask for this address on this interface.
*/
strncpy(ifrnetmask.ifr_name, ifrp->ifr_name,
sizeof(ifrnetmask.ifr_name));
memcpy(&ifrnetmask.ifr_addr, &ifrp->ifr_addr,
sizeof(ifrnetmask.ifr_addr));
if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifrnetmask) < 0) {
if (errno == EADDRNOTAVAIL) {
/*
* Not available.
*/
netmask = NULL;
netmask_size = 0;
} else {
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"SIOCGIFNETMASK: %.*s: %s",
(int)sizeof(ifrnetmask.ifr_name),
ifrnetmask.ifr_name,
pcap_strerror(errno));
ret = -1;
break;
}
} else {
netmask = &ifrnetmask.ifr_addr;
netmask_size = SA_LEN(netmask);
}
/*
* Get the broadcast address for this address on this
* interface (if any).
*/
if (ifrflags.ifr_flags & IFF_BROADCAST) {
strncpy(ifrbroadaddr.ifr_name, ifrp->ifr_name,
sizeof(ifrbroadaddr.ifr_name));
memcpy(&ifrbroadaddr.ifr_addr, &ifrp->ifr_addr,
sizeof(ifrbroadaddr.ifr_addr));
if (ioctl(fd, SIOCGIFBRDADDR,
(char *)&ifrbroadaddr) < 0) {
if (errno == EADDRNOTAVAIL) {
/*
* Not available.
*/
broadaddr = NULL;
broadaddr_size = 0;
} else {
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"SIOCGIFBRDADDR: %.*s: %s",
(int)sizeof(ifrbroadaddr.ifr_name),
ifrbroadaddr.ifr_name,
pcap_strerror(errno));
ret = -1;
break;
}
} else {
broadaddr = &ifrbroadaddr.ifr_broadaddr;
broadaddr_size = SA_LEN(broadaddr);
}
} else {
/*
* Not a broadcast interface, so no broadcast
* address.
*/
broadaddr = NULL;
broadaddr_size = 0;
}
/*
* Get the destination address for this address on this
* interface (if any).
*/
if (ifrflags.ifr_flags & IFF_POINTOPOINT) {
strncpy(ifrdstaddr.ifr_name, ifrp->ifr_name,
sizeof(ifrdstaddr.ifr_name));
memcpy(&ifrdstaddr.ifr_addr, &ifrp->ifr_addr,
sizeof(ifrdstaddr.ifr_addr));
if (ioctl(fd, SIOCGIFDSTADDR,
(char *)&ifrdstaddr) < 0) {
if (errno == EADDRNOTAVAIL) {
/*
* Not available.
*/
dstaddr = NULL;
dstaddr_size = 0;
} else {
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
"SIOCGIFDSTADDR: %.*s: %s",
(int)sizeof(ifrdstaddr.ifr_name),
ifrdstaddr.ifr_name,
pcap_strerror(errno));
ret = -1;
break;
}
} else {
dstaddr = &ifrdstaddr.ifr_dstaddr;
dstaddr_size = SA_LEN(dstaddr);
}
} else {
/*
* Not a point-to-point interface, so no destination
* address.
*/
dstaddr = NULL;
dstaddr_size = 0;
}
主要过程是:
1、打开一个socket: fd = socket(AF_INET, SOCK_DGRAM, 0);AF_INET代表ipv4, SOCK_DGRAM代表数据包格式
2、获取所有网络接口:ioctl(fd, SIOCGIFCONF, (char *)&ifc),SIOCGIFCONF就是获取所有接口的命令,ioctl是系统提供的和驱动交流的一种接口,看过android框架的应该比较清楚
3、继续调用ioctl接口获取特定网络接口的相关信息,具体可以看代码注释
4、注意的是打开socket需要访问网络权限<uses-permission android:name="android.permission.INTERNET" />
5、另外拥有网络权限你可能也会遇到“no suitable devices”错误,这个是因为你还需要root权限
二、pcap_open_live函数,它的作用就是用你上一个函数返回的字符串指针打开网络接口,并且在dev目录下创建文件(bpf...)来存放网络数据,下面是它的实现:
pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
char *ebuf)
{
int fd;
struct ifreq ifr;
struct bpf_version bv;
#ifdef BIOCGDLTLIST
struct bpf_dltlist bdl;
#endif
#if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT)
u_int spoof_eth_src = 1;
#endif
u_int v;
pcap_t *p;
struct bpf_insn total_insn;
struct bpf_program total_prog;
struct utsname osinfo;
#ifdef HAVE_DAG_API
if (strstr(device, "dag")) {
return dag_open_live(device, snaplen, promisc, to_ms, ebuf);
}
#endif /* HAVE_DAG_API */
#ifdef BIOCGDLTLIST
memset(&bdl, 0, sizeof(bdl));
#endif
p = (pcap_t *)malloc(sizeof(*p));
if (p == NULL) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
pcap_strerror(errno));
return (NULL);
}
memset(p, 0, sizeof(*p));
fd = bpf_open(p, ebuf);
if (fd < 0)
goto bad;
p->fd = fd;
p->snapshot = snaplen;
if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
pcap_strerror(errno));
goto bad;
}
if (bv.bv_major != BPF_MAJOR_VERSION ||
bv.bv_minor < BPF_MINOR_VERSION) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"kernel bpf filter out of date");
goto bad;
}
/*
* Try finding a good size for the buffer; 32768 may be too
* big, so keep cutting it in half until we find a size
* that works, or run out of sizes to try. If the default
* is larger, don't make it smaller.
*
* XXX - there should be a user-accessible hook to set the
* initial buffer size.
*/
if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) || v < 32768)
v = 32768;
for ( ; v != 0; v >>= 1) {
/* Ignore the return value - this is because the call fails
* on BPF systems that don't have kernel malloc. And if
* the call fails, it's no big deal, we just continue to
* use the standard buffer size.
*/
(void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
(void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
break; /* that size worked; we're done */
if (errno != ENOBUFS) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s",
device, pcap_strerror(errno));
goto bad;
}
}
if (v == 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"BIOCSBLEN: %s: No buffer size worked", device);
goto bad;
}
/* Get the data link layer type. */
if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
pcap_strerror(errno));
goto bad;
}
#ifdef _AIX
/*
* AIX's BPF returns IFF_ types, not DLT_ types, in BIOCGDLT.
*/
switch (v) {
case IFT_ETHER:
case IFT_ISO88023:
v = DLT_EN10MB;
break;
case IFT_FDDI:
v = DLT_FDDI;
break;
case IFT_ISO88025:
v = DLT_IEEE802;
break;
case IFT_LOOP:
v = DLT_NULL;
break;
default:
/*
* We don't know what to map this to yet.
*/
snprintf(ebuf, PCAP_ERRBUF_SIZE, "unknown interface type %u",
v);
goto bad;
}
#endif
#if _BSDI_VERSION - 0 >= 199510
/* The SLIP and PPP link layer header changed in BSD/OS 2.1 */
switch (v) {
case DLT_SLIP:
v = DLT_SLIP_BSDOS;
break;
case DLT_PPP:
v = DLT_PPP_BSDOS;
break;
case 11: /*DLT_FR*/
v = DLT_FRELAY;
break;
case 12: /*DLT_C_HDLC*/
v = DLT_CHDLC;
break;
}
#endif
#ifdef PCAP_FDDIPAD
if (v == DLT_FDDI)
p->fddipad = PCAP_FDDIPAD;
else
p->fddipad = 0;
#endif
p->linktype = v;
#ifdef BIOCGDLTLIST
/*
* We know the default link type -- now determine all the DLTs
* this interface supports. If this fails with EINVAL, it's
* not fatal; we just don't get to use the feature later.
*/
if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) == 0) {
u_int i;
int is_ethernet;
bdl.bfl_list = (u_int *) malloc(sizeof(u_int) * (bdl.bfl_len + 1));
if (bdl.bfl_list == NULL) {
(void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
pcap_strerror(errno));
goto bad;
}
if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) < 0) {
(void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
"BIOCGDLTLIST: %s", pcap_strerror(errno));
free(bdl.bfl_list);
goto bad;
}
/*
* OK, for real Ethernet devices, add DLT_DOCSIS to the
* list, so that an application can let you choose it,
* in case you're capturing DOCSIS traffic that a Cisco
* Cable Modem Termination System is putting out onto
* an Ethernet (it doesn't put an Ethernet header onto
* the wire, it puts raw DOCSIS frames out on the wire
* inside the low-level Ethernet framing).
*
* A "real Ethernet device" is defined here as a device
* that has a link-layer type of DLT_EN10MB and that has
* no alternate link-layer types; that's done to exclude
* 802.11 interfaces (which might or might not be the
* right thing to do, but I suspect it is - Ethernet <->
* 802.11 bridges would probably badly mishandle frames
* that don't have Ethernet headers).
*/
if (p->linktype == DLT_EN10MB) {
is_ethernet = 1;
for (i = 0; i < bdl.bfl_len; i++) {
if (bdl.bfl_list[i] != DLT_EN10MB) {
is_ethernet = 0;
break;
}
}
if (is_ethernet) {
/*
* We reserved one more slot at the end of
* the list.
*/
bdl.bfl_list[bdl.bfl_len] = DLT_DOCSIS;
bdl.bfl_len++;
}
}
p->dlt_count = bdl.bfl_len;
p->dlt_list = bdl.bfl_list;
} else {
if (errno != EINVAL) {
(void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
"BIOCGDLTLIST: %s", pcap_strerror(errno));
goto bad;
}
}
#endif
/*
* If this is an Ethernet device, and we don't have a DLT_ list,
* give it a list with DLT_EN10MB and DLT_DOCSIS. (That'd give
* 802.11 interfaces DLT_DOCSIS, which isn't the right thing to
* do, but there's not much we can do about that without finding
* some other way of determining whether it's an Ethernet or 802.11
* device.)
*/
if (p->linktype == DLT_EN10MB && p->dlt_count == 0) {
p->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
/*
* If that fails, just leave the list empty.
*/
if (p->dlt_list != NULL) {
p->dlt_list[0] = DLT_EN10MB;
p->dlt_list[1] = DLT_DOCSIS;
p->dlt_count = 2;
}
}
#if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT)
/*
* Do a BIOCSHDRCMPLT, if defined, to turn that flag on, so
* the link-layer source address isn't forcibly overwritten.
* (Should we ignore errors? Should we do this only if
* we're open for writing?)
*
* XXX - I seem to remember some packet-sending bug in some
* BSDs - check CVS log for "bpf.c"?
*/
if (ioctl(fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1) {
(void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
"BIOCSHDRCMPLT: %s", pcap_strerror(errno));
goto bad;
}
#endif
/* set timeout */
if (to_ms != 0) {
/*
* XXX - is this seconds/nanoseconds in AIX?
* (Treating it as such doesn't fix the timeout
* problem described below.)
*/
struct timeval to;
to.tv_sec = to_ms / 1000;
to.tv_usec = (to_ms * 1000) % 1000000;
if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&to) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
pcap_strerror(errno));
goto bad;
}
}
#ifdef _AIX
#ifdef BIOCIMMEDIATE
/*
* Darren Reed notes that
*
* On AIX (4.2 at least), if BIOCIMMEDIATE is not set, the
* timeout appears to be ignored and it waits until the buffer
* is filled before returning. The result of not having it
* set is almost worse than useless if your BPF filter
* is reducing things to only a few packets (i.e. one every
* second or so).
*
* so we turn BIOCIMMEDIATE mode on if this is AIX.
*
* We don't turn it on for other platforms, as that means we
* get woken up for every packet, which may not be what we want;
* in the Winter 1993 USENIX paper on BPF, they say:
*
* Since a process might want to look at every packet on a
* network and the time between packets can be only a few
* microseconds, it is not possible to do a read system call
* per packet and BPF must collect the data from several
* packets and return it as a unit when the monitoring
* application does a read.
*
* which I infer is the reason for the timeout - it means we
* wait that amount of time, in the hopes that more packets
* will arrive and we'll get them all with one read.
*
* Setting BIOCIMMEDIATE mode on FreeBSD (and probably other
* BSDs) causes the timeout to be ignored.
*
* On the other hand, some platforms (e.g., Linux) don't support
* timeouts, they just hand stuff to you as soon as it arrives;
* if that doesn't cause a problem on those platforms, it may
* be OK to have BIOCIMMEDIATE mode on BSD as well.
*
* (Note, though, that applications may depend on the read
* completing, even if no packets have arrived, when the timeout
* expires, e.g. GUI applications that have to check for input
* while waiting for packets to arrive; a non-zero timeout
* prevents "select()" from working right on FreeBSD and
* possibly other BSDs, as the timer doesn't start until a
* "read()" is done, so the timer isn't in effect if the
* application is blocked on a "select()", and the "select()"
* doesn't get woken up for a BPF device until the buffer
* fills up.)
*/
v = 1;
if (ioctl(p->fd, BIOCIMMEDIATE, &v) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCIMMEDIATE: %s",
pcap_strerror(errno));
goto bad;
}
#endif /* BIOCIMMEDIATE */
#endif /* _AIX */
if (promisc) {
/* set promiscuous mode, okay if it fails */
if (ioctl(p->fd, BIOCPROMISC, NULL) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCPROMISC: %s",
pcap_strerror(errno));
}
}
if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
pcap_strerror(errno));
goto bad;
}
p->bufsize = v;
p->buffer = (u_char *)malloc(p->bufsize);
if (p->buffer == NULL) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
pcap_strerror(errno));
goto bad;
}
#ifdef _AIX
/* For some strange reason this seems to prevent the EFAULT
* problems we have experienced from AIX BPF. */
memset(p->buffer, 0x0, p->bufsize);
#endif
/*
* If there's no filter program installed, there's
* no indication to the kernel of what the snapshot
* length should be, so no snapshotting is done.
*
* Therefore, when we open the device, we install
* an "accept everything" filter with the specified
* snapshot length.
*/
total_insn.code = (u_short)(BPF_RET | BPF_K);
total_insn.jt = 0;
total_insn.jf = 0;
total_insn.k = snaplen;
total_prog.bf_len = 1;
total_prog.bf_insns = &total_insn;
if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s",
pcap_strerror(errno));
goto bad;
}
/*
* On most BPF platforms, either you can do a "select()" or
* "poll()" on a BPF file descriptor and it works correctly,
* or you can do it and it will return "readable" if the
* hold buffer is full but not if the timeout expires *and*
* a non-blocking read will, if the hold buffer is empty
* but the store buffer isn't empty, rotate the buffers
* and return what packets are available.
*
* In the latter case, the fact that a non-blocking read
* will give you the available packets means you can work
* around the failure of "select()" and "poll()" to wake up
* and return "readable" when the timeout expires by using
* the timeout as the "select()" or "poll()" timeout, putting
* the BPF descriptor into non-blocking mode, and read from
* it regardless of whether "select()" reports it as readable
* or not.
*
* However, in FreeBSD 4.3 and 4.4, "select()" and "poll()"
* won't wake up and return "readable" if the timer expires
* and non-blocking reads return EWOULDBLOCK if the hold
* buffer is empty, even if the store buffer is non-empty.
*
* This means the workaround in question won't work.
*
* Therefore, on FreeBSD 4.3 and 4.4, we set "p->selectable_fd"
* to -1, which means "sorry, you can't use 'select()' or 'poll()'
* here". On all other BPF platforms, we set it to the FD for
* the BPF device; in NetBSD, OpenBSD, and Darwin, a non-blocking
* read will, if the hold buffer is empty and the store buffer
* isn't empty, rotate the buffers and return what packets are
* there (and in sufficiently recent versions of OpenBSD
* "select()" and "poll()" should work correctly).
*
* XXX - what about AIX?
*/
p->selectable_fd = p->fd; /* assume select() works until we know otherwise */
if (uname(&osinfo) == 0) {
/*
* We can check what OS this is.
*/
if (strcmp(osinfo.sysname, "FreeBSD") == 0) {
if (strncmp(osinfo.release, "4.3-", 4) == 0 ||
strncmp(osinfo.release, "4.4-", 4) == 0)
p->selectable_fd = -1;
}
}
p->read_op = pcap_read_bpf;
p->inject_op = pcap_inject_bpf;
p->setfilter_op = pcap_setfilter_bpf;
p->setdirection_op = pcap_setdirection_bpf;
p->set_datalink_op = pcap_set_datalink_bpf;
p->getnonblock_op = pcap_getnonblock_fd;
p->setnonblock_op = pcap_setnonblock_fd;
p->stats_op = pcap_stats_bpf;
p->close_op = pcap_close_common;
return (p);
bad:
(void)close(fd);
if (p->dlt_list != NULL)
free(p->dlt_list);
free(p);
return (NULL);
}
我们看到首先通过fd = bpf_open(p, ebuf);来得到一个文件操作符,这个操作符就是创建的/dev/bpf..文件,目的是将这个文件用来存储网络接口数据,然后通过一系列ioctl命令设置网络参数,下面是这些设置参数值:
#define BIOCGBLEN _IOR(B,102, u_int)
#define BIOCSBLEN _IOWR(B,102, u_int)
#define BIOCSETF _IOW(B,103, struct bpf_program)
#define BIOCFLUSH _IO(B,104)
#define BIOCPROMISC _IO(B,105)
#define BIOCGDLT _IOR(B,106, u_int)
#define BIOCGETIF _IOR(B,107, struct ifreq)
#define BIOCSETIF _IOW(B,108, struct ifreq)
#define BIOCSRTIMEOUT _IOW(B,109, struct timeval)
#define BIOCGRTIMEOUT _IOR(B,110, struct timeval)
#define BIOCGSTATS _IOR(B,111, struct bpf_stat)
#define BIOCIMMEDIATE _IOW(B,112, u_int)
#define BIOCVERSION _IOR(B,113, struct bpf_version)
#define BIOCSTCPF _IOW(B,114, struct bpf_program)
#define BIOCSUDPF _IOW(B,115, struct bpf_program)
#else
#define BIOCGBLEN _IOR('B',102, u_int)
#define BIOCSBLEN _IOWR('B',102, u_int)
#define BIOCSETF _IOW('B',103, struct bpf_program)
#define BIOCFLUSH _IO('B',104)
#define BIOCPROMISC _IO('B',105)
#define BIOCGDLT _IOR('B',106, u_int)
#define BIOCGETIF _IOR('B',107, struct ifreq)
#define BIOCSETIF _IOW('B',108, struct ifreq)
#define BIOCSRTIMEOUT _IOW('B',109, struct timeval)
#define BIOCGRTIMEOUT _IOR('B',110, struct timeval)
#define BIOCGSTATS _IOR('B',111, struct bpf_stat)
#define BIOCIMMEDIATE _IOW('B',112, u_int)
#define BIOCVERSION _IOR('B',113, struct bpf_version)
#define BIOCSTCPF _IOW('B',114, struct bpf_program)
#define BIOCSUDPF _IOW('B',115, struct bpf_program)
#endif
后面只要监听前面的/dev/bpf..文件读取数据就行了,注意的是这里读取的数据是链路层的帧数据,需要自己解析