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..文件读取数据就行了,注意的是这里读取的数据是链路层的帧数据,需要自己解析