libpcap原理分析

来源:互联网 发布:最早一批网络歌曲 编辑:程序博客网 时间:2024/06/14 19:42

  

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