libpcap


网络散文 2019-08-29 15:44:07 网络散文
[摘要]一:[libpcap]libpcap使用libpcap是一个网络数据包捕获函数库,功能非常强大,Linux下著名的tcpdump就是以它为基础的。今天我们利用它来完成一个我们自己的网络嗅探器(sniffer)首先先介绍一下本次实验的环境:Ubuntu 11 04,IP:192 168 1 1,广播地

【www.shanpow.com--网络散文】

一:[libpcap]libpcap使用


libpcap是一个网络数据包捕获函数库,功能非常强大,Linux下著名的tcpdump就是以它为基础的。今天我们利用它来完成一个我们自己的网络嗅探器(sniffer)首先先介绍一下本次实验的环境:Ubuntu 11.04,IP:192.168.1.1,广播地址:192.168.1.255,子网掩码:255.255.255.0可以使用下面的命令设置:sudo ifconfig eth0 192.168.1.1 broadcast 192.168.1.255 netmask 255.255.255.01.安装在http://www.tcpdump.org/下载libpcap(tcpdump的源码也可以从这个网站下载)解压./configuremakesudo make install2.使用安装好libpcap后,我们要使用它啦,先写一个简单的程序,并介绍如何使用libpcap库编译它:Makefile:
[plain] view plaincopy
all: test.c  
    gcc -g -Wall -o test test.c -lpcap  
  
clean:  
    rm -rf *.o test  
其后的程序的Makefile均类似,故不再重复test1.c
[cpp] view plaincopy
#include <pcap.h>  
#include <stdio.h>  
  
int main()  
{  
  char errBuf[PCAP_ERRBUF_SIZE], * device;  
    
  device = pcap_lookupdev(errBuf);  
    
  if(device)  
  {  
    printf("success: device: %s\n", device);  
  }  
  else  
  {  
    printf("error: %s\n", errBuf);  
  }  
    
  return 0;  
}  
可以成功编译,不过运行的时候却提示找不到libpcap.so.1,因为libpcap.so.1默认安装到了/usr/local/lib下,我们做一个符号链接到/usr/lib/下即可:运行test的时候输出"no suitable device found",原因是我们没有以root权限运行,root权限运行后就正常了:下面开始正式讲解如何使用libpcap:首先要使用libpcap,我们必须包含pcap.h头文件,可以在/usr/local/include/pcap/pcap.h找到,其中包含了每个类型定义的详细说明。1.获取网络接口首先我们需要获取监听的网络接口:我们可以手动指定或让libpcap自动选择,先介绍如何让libpcap自动选择:char * pcap_lookupdev(char * errbuf)上面这个函数返回第一个合适的网络接口的字符串指针,如果出错,则errbuf存放出错信息字符串,errbuf至少应该是PCAP_ERRBUF_SIZE个字节长度的。注意,很多libpcap函数都有这个参数。pcap_lookupdev()一般可以在跨平台的,且各个平台上的网络接口名称都不相同的情况下使用。如果我们手动指定要监听的网络接口,则这一步跳过,我们在第二步中将要监听的网络接口字符串硬编码在pcap_open_live里。2.释放网络接口在操作为网络接口后,我们应该要释放它:void pcap_close(pcap_t * p)该函数用于关闭pcap_open_live()获取的pcap_t的网络接口对象并释放相关资源。3.打开网络接口获取网络接口后,我们需要打开它:pcap_t * pcap_open_live(const char * device, int snaplen, int promisc, int to_ms, char * errbuf)上面这个函数会返回指定接口的pcap_t类型指针,后面的所有操作都要使用这个指针。第一个参数是第一步获取的网络接口字符串,可以直接使用硬编码。第二个参数是对于每个数据包,从开头要抓多少个字节,我们可以设置这个值来只抓每个数据包的头部,而不关心具体的内容。典型的以太网帧长度是1518字节,但其他的某些协议的数据包会更长一点,但任何一个协议的一个数据包长度都必然小于65535个字节。第三个参数指定是否打开混杂模式(Promiscuous Mode),0表示非混杂模式,任何其他值表示混合模式。如果要打开混杂模式,那么网卡必须也要打开混杂模式,可以使用如下的命令打开eth0混杂模式:ifconfig eth0 promisc第四个参数指定需要等待的毫秒数,超过这个数值后,第3步获取数据包的这几个函数就会立即返回。0表示一直等待直到有数据包到来。第五个参数是存放出错信息的数组。4.获取数据包打开网络接口后就已经开始监听了,那如何知道收到了数据包呢?有下面3种方法:a)u_char * pcap_next(pcap_t * p, struct pcap_pkthdr * h)如果返回值为NULL,表示没有抓到包第一个参数是第2步返回的pcap_t类型的指针第二个参数是保存收到的第一个数据包的pcap_pkthdr类型的指针pcap_pkthdr类型的定义如下:
[cpp] view plaincopy
struct pcap_pkthdr  
{  
  struct timeval ts;    /* time stamp */  
  bpf_u_int32 caplen;   /* length of portion present */  
  bpf_u_int32 len;      /* length this packet (off wire) */  
};  
注意这个函数只要收到一个数据包后就会立即返回b)int pcap_loop(pcap_t * p, int cnt, pcap_handler callback, u_char * user)第一个参数是第2步返回的pcap_t类型的指针第二个参数是需要抓的数据包的个数,一旦抓到了cnt个数据包,pcap_loop立即返回。负数的cnt表示pcap_loop永远循环抓包,直到出现错误。第三个参数是一个回调函数指针,它必须是如下的形式:void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet)第一个参数是pcap_loop的最后一个参数,当收到足够数量的包后pcap_loop会调用callback回调函数,同时将pcap_loop()的user参数传递给它第二个参数是收到的数据包的pcap_pkthdr类型的指针第三个参数是收到的数据包数据c)int pcap_dispatch(pcap_t * p, int cnt, pcap_handler callback, u_char * user)这个函数和pcap_loop()非常类似,只是在超过to_ms毫秒后就会返回(to_ms是pcap_open_live()的第4个参数)
例子:
test2: [cpp] view plaincopy
#include <pcap.h>  
#include <time.h>  
#include <stdlib.h>  
#include <stdio.h>  
  
int main()  
{  
  char errBuf[PCAP_ERRBUF_SIZE], * devStr;  
    
  /* get a device */  
  devStr = pcap_lookupdev(errBuf);  
    
  if(devStr)  
  {  
    printf("success: device: %s\n", devStr);  
  }  
  else  
  {  
    printf("error: %s\n", errBuf);  
    exit(1);  
  }  
    
  /* open a device, wait until a packet arrives */  
  pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);  
    
  if(!device)  
  {  
    printf("error: pcap_open_live(): %s\n", errBuf);  
    exit(1);  
  }  
  
  /* wait a packet to arrive */  
  struct pcap_pkthdr packet;  
  const u_char * pktStr = pcap_next(device, &packet);  
  
  if(!pktStr)  
  {  
    printf("did not capture a packet!\n");  
    exit(1);  
  }  
    
  printf("Packet length: %d\n", packet.len);  
  printf("Number of bytes: %d\n", packet.caplen);  
  printf("Recieved time: %s\n", ctime((const time_t *)&packet.ts.tv_sec));   
    
  pcap_close(device);  
    
  return 0;  
}  
打开两个终端,先ping 192.168.1.10,由于我们的ip是192.168.1.1,因此我们可以收到广播的数据包,另一个终端运行test,就会抓到这个包。下面的这个程序会把收到的数据包内容全部打印出来,运行方式和上一个程序一样:test3:
[cpp] view plaincopy
#include <pcap.h>  
#include <time.h>  
#include <stdlib.h>  
#include <stdio.h>  
  
void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)  
{  
  int * id = (int *)arg;  
    
  printf("id: %d\n", ++(*id));  
  printf("Packet length: %d\n", pkthdr->len);  
  printf("Number of bytes: %d\n", pkthdr->caplen);  
  printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));   
    
  int i;  
  for(i=0; i<pkthdr->len; ++i)  
  {  
    printf(" %02x", packet[i]);  
    if( (i + 1) % 16 == 0 )  
    {  
      printf("\n");  
    }  
  }  
    
  printf("\n\n");  
}  
  
int main()  
{  
  char errBuf[PCAP_ERRBUF_SIZE], * devStr;  
    
  /* get a device */  
  devStr = pcap_lookupdev(errBuf);  
    
  if(devStr)  
  {  
    printf("success: device: %s\n", devStr);  
  }  
  else  
  {  
    printf("error: %s\n", errBuf);  
    exit(1);  
  }  
    
  /* open a device, wait until a packet arrives */  
  pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);  
    
  if(!device)  
  {  
    printf("error: pcap_open_live(): %s\n", errBuf);  
    exit(1);  
  }  
    
  /* wait loop forever */  
  int id = 0;  
  pcap_loop(device, -1, getPacket, (u_char*)&id);  
    
  pcap_close(device);  
  
  return 0;  
}  
从上图可以看出,如果我们没有按Ctrl+c,test会一直抓到包,因为我们将pcap_loop()设置为永远循环由于ping属于icmp协议,并且发出icmp协议数据包之前必须先通过arp协议获取目的主机的mac地址,因此我们抓到的包是arp协议的,而arp协议的数据包长度正好是42字节(14字节的以太网帧头+28字节的arp数据)。具体内容请参考相关网络协议说明。5.分析数据包我们既然已经抓到数据包了,那么我们要开始分析了,这部分留给读者自己完成,具体内容可以参考相关的网络协议说明。在本文的最后,我会示范性的写一个分析arp协议的sniffer,仅供参考。要特别注意一点,网络上的数据是网络字节顺序的,因此分析前需要转换为主机字节顺序(ntohs()函数)。6.过滤数据包我们抓到的数据包往往很多,如何过滤掉我们不感兴趣的数据包呢?几乎所有的操作系统(BSD, AIX, Mac OS, Linux等)都会在内核中提供过滤数据包的方法,主要都是基于BSD Packet Filter(BPF)结构的。libpcap利用BPF来过滤数据包。过滤数据包需要完成3件事:a) 构造一个过滤表达式b) 编译这个表达式c) 应用这个过滤器a)BPF使用一种类似于汇编语言的语法书写过滤表达式,不过libpcap和tcpdump都把它封装成更高级且更容易的语法了,具体可以man tcpdump,以下是一些例子:src host 192.168.1.177只接收源ip地址是192.168.1.177的数据包dst port 80只接收tcp/udp的目的端口是80的数据包not tcp只接收不使用tcp协议的数据包tcp[13] == 0x02 and (dst port 22 or dst port 23)只接收SYN标志位置位且目标端口是22或23的数据包(tcp首部开始的第13个字节)icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo只接收icmp的ping请求和ping响应的数据包ehter dst 00:e0:09:c1:0e:82只接收以太网mac地址是00:e0:09:c1:0e:82的数据包ip[8] == 5只接收ip的ttl=5的数据包(ip首部开始的第8个字节)b)构造完过滤表达式后,我们需要编译它,使用如下函数:int pcap_compile(pcap_t * p, struct bpf_program * fp, char * str, int optimize, bpf_u_int32 netmask)fp:这是一个传出参数,存放编译后的bpfstr:过滤表达式optimize:是否需要优化过滤表达式metmask:简单设置为0即可c)最后我们需要应用这个过滤表达式:int pcap_setfilter(pcap_t * p,  struct bpf_program * fp)第二个参数fp就是前一步pcap_compile()的第二个参数应用完过滤表达式之后我们便可以使用pcap_loop()或pcap_next()等抓包函数来抓包了。下面的程序演示了如何过滤数据包,我们只接收目的端口是80的数据包:test4.c
[cpp] view plaincopy
#include <pcap.h>  
#include <time.h>  
#include <stdlib.h>  
#include <stdio.h>  
  
void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)  
{  
  int * id = (int *)arg;  
    
  printf("id: %d\n", ++(*id));  
  printf("Packet length: %d\n", pkthdr->len);  
  printf("Number of bytes: %d\n", pkthdr->caplen);  
  printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));   
    
  int i;  
  for(i=0; i<pkthdr->len; ++i)  
  {  
    printf(" %02x", packet[i]);  
    if( (i + 1) % 16 == 0 )  
    {  
      printf("\n");  
    }  
  }  
    
  printf("\n\n");  
}  
  
int main()  
{  
  char errBuf[PCAP_ERRBUF_SIZE], * devStr;  
    
  /* get a device */  
  devStr = pcap_lookupdev(errBuf);  
    
  if(devStr)  
  {  
    printf("success: device: %s\n", devStr);  
  }  
  else  
  {  
    printf("error: %s\n", errBuf);  
    exit(1);  
  }  
    
  /* open a device, wait until a packet arrives */  
  pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);  
    
  if(!device)  
  {  
    printf("error: pcap_open_live(): %s\n", errBuf);  
    exit(1);  
  }  
    
  /* construct a filter */  
  struct bpf_program filter;  
  pcap_compile(device, &filter, "dst port 80", 1, 0);  
  pcap_setfilter(device, &filter);  
    
  /* wait loop forever */  
  int id = 0;  
  pcap_loop(device, -1, getPacket, (u_char*)&id);  
    
  pcap_close(device);  
  
  return 0;  
}  
在下面的这一个例子中,客户机通过tcp的9732端口连接服务器,发送字符"A",之后服务器将"A"+1即"B"返回给客户机,具体实现可以参考:http://blog.csdn.net/htttw/article/details/7519964服务器的ip是192.168.56.101,客户机的ip是192.168.56.1服务器:
Makefile: [plain] view plaincopy
all: tcp_client.c tcp_server.c  
    gcc -g -Wall -o tcp_client tcp_client.c  
    gcc -g -Wall -o tcp_server tcp_server.c  
  
clean:  
    rm -rf *.o tcp_client tcp_server  
tcp_server: [cpp] view plaincopy
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
  
#define PORT 9832  
#define SERVER_IP "192.168.56.101"  
  
int main()  
{  
  /* create a socket */  
  int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);  
    
  struct sockaddr_in server_addr;  
  server_addr.sin_family = AF_INET;  
  server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);  
  server_addr.sin_port = htons(PORT);  
    
  /* bind with the local file */  
  bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));  
    
  /* listen */  
  listen(server_sockfd, 5);  
    
  char ch;  
  int client_sockfd;  
  struct sockaddr_in client_addr;  
  socklen_t len = sizeof(client_addr);  
  while(1)  
  {  
    printf("server waiting:\n");  
      
    /* accept a connection */  
    client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);  
      
    /* exchange data */  
    read(client_sockfd, &ch, 1);  
    printf("get char from client: %c\n", ch);  
    ++ch;  
    write(client_sockfd, &ch, 1);  
      
    /* close the socket */  
    close(client_sockfd);  
  }  
    
  return 0;  
}  
tcp_client: [cpp] view plaincopy
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
  
#define PORT 9832  
#define SERVER_IP "192.168.56.101"  
  
int main()  
{  
  /* create a socket */  
  int sockfd = socket(AF_INET, SOCK_STREAM, 0);  
    
  struct sockaddr_in address;  
  address.sin_family = AF_INET;  
  address.sin_addr.s_addr = inet_addr(SERVER_IP);  
  address.sin_port = htons(PORT);  
    
  /* connect to the server */  
  int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));  
  if(result == -1)  
  {  
    perror("connect failed: ");  
    exit(1);  
  }  
    
  /* exchange data */  
  char ch = "A";  
  write(sockfd, &ch, 1);  
  read(sockfd, &ch, 1);  
  printf("get char from server: %c\n", ch);  
    
  /* close the socket */  
  close(sockfd);  
    
  return 0;  
}  
运行方法如下,首先在服务器上运行tcp_server,然后运行我们的监听器,然后在客户机上运行tcp_client,注意,我们可以先清空arp缓存,这样就可以看到整个通信过程(包括一开始的arp广播)在客户机上运行下列命令来清空记录服务器的arp缓存:sudo arp -d 192.168.56.101arp -a后发现已经删除了记录服务器的arp缓存抓包的结果如下所示,由于包太多了,无法全部截图,因此我把所有内容保存在下面的文本中了:全部的包如下: [plain] view plaincopy
hutao@hutao-VirtualBox:~/test3$ sudo ./test  
success: device: eth0  
id: 1  
Packet length: 60  
Number of bytes: 60  
Recieved time: Sat Apr 28 19:57:50 2012  
 ff ff ff ff ff ff 0a 00 27 00 00 00 08 06 00 01  
 08 00 06 04 00 01 0a 00 27 00 00 00 c0 a8 38 01  
 00 00 00 00 00 00 c0 a8 38 65 00 00 00 00 00 00  
 00 00 00 00 00 00 00 00 00 00 00 00  
  
id: 2  
Packet length: 42  
Number of bytes: 42  
Recieved time: Sat Apr 28 19:57:50 2012  
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 06 00 01  
 08 00 06 04 00 02 08 00 27 9c ff b1 c0 a8 38 65  
 0a 00 27 00 00 00 c0 a8 38 01  
  
id: 3  
Packet length: 74  
Number of bytes: 74  
Recieved time: Sat Apr 28 19:57:50 2012  
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
 00 3c d4 af 40 00 40 06 74 55 c0 a8 38 01 c0 a8  
 38 65 8e 20 26 68 79 e1 63 8c 00 00 00 00 a0 02  
 39 08 d4 13 00 00 02 04 05 b4 04 02 08 0a 00 14  
 b7 23 00 00 00 00 01 03 03 06  
  
id: 4  
Packet length: 74  
Number of bytes: 74  
Recieved time: Sat Apr 28 19:57:50 2012  
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00  
 00 3c 00 00 40 00 40 06 49 05 c0 a8 38 65 c0 a8  
 38 01 26 68 8e 20 b6 c4 e6 e5 79 e1 63 8d a0 12  
 38 90 f1 e5 00 00 02 04 05 b4 04 02 08 0a 00 57  
 a1 2c 00 14 b7 23 01 03 03 05  
  
id: 5  
Packet length: 66  
Number of bytes: 66  
Recieved time: Sat Apr 28 19:57:50 2012  
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
 00 34 d4 b0 40 00 40 06 74 5c c0 a8 38 01 c0 a8  
 38 65 8e 20 26 68 79 e1 63 8d b6 c4 e6 e6 80 10  
 00 e5 fb c1 00 00 01 01 08 0a 00 14 b7 24 00 57  
 a1 2c  
  
id: 6  
Packet length: 67  
Number of bytes: 67  
Recieved time: Sat Apr 28 19:57:50 2012  
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
 00 35 d4 b1 40 00 40 06 74 5a c0 a8 38 01 c0 a8  
 38 65 8e 20 26 68 79 e1 63 8d b6 c4 e6 e6 80 18  
 00 e5 ba b7 00 00 01 01 08 0a 00 14 b7 25 00 57  
 a1 2c 41  
  
id: 7  
Packet length: 66  
Number of bytes: 66  
Recieved time: Sat Apr 28 19:57:50 2012  
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00  
 00 34 47 cb 40 00 40 06 01 42 c0 a8 38 65 c0 a8  
 38 01 26 68 8e 20 b6 c4 e6 e6 79 e1 63 8e 80 10  
 01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14  
 b7 25  
  
id: 8  
Packet length: 67  
Number of bytes: 67  
Recieved time: Sat Apr 28 19:57:50 2012  
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00  
 00 35 47 cc 40 00 40 06 01 40 c0 a8 38 65 c0 a8  
 38 01 26 68 8e 20 b6 c4 e6 e6 79 e1 63 8e 80 18  
 01 c5 f1 de 00 00 01 01 08 0a 00 57 a1 2e 00 14  
 b7 25 42  
  
id: 9  
Packet length: 66  
Number of bytes: 66  
Recieved time: Sat Apr 28 19:57:50 2012  
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00  
 00 34 47 cd 40 00 40 06 01 40 c0 a8 38 65 c0 a8  
 38 01 26 68 8e 20 b6 c4 e6 e7 79 e1 63 8e 80 11  
 01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14  
 b7 25  
  
id: 10  
Packet length: 66  
Number of bytes: 66  
Recieved time: Sat Apr 28 19:57:50 2012  
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
 00 34 d4 b2 40 00 40 06 74 5a c0 a8 38 01 c0 a8  
 38 65 8e 20 26 68 79 e1 63 8e b6 c4 e6 e7 80 10  
 00 e5 fb bc 00 00 01 01 08 0a 00 14 b7 25 00 57  
 a1 2e  
  
id: 11  
Packet length: 66  
Number of bytes: 66  
Recieved time: Sat Apr 28 19:57:50 2012  
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
 00 34 d4 b3 40 00 40 06 74 59 c0 a8 38 01 c0 a8  
 38 65 8e 20 26 68 79 e1 63 8e b6 c4 e6 e7 80 11  
 00 e5 fb bb 00 00 01 01 08 0a 00 14 b7 25 00 57  
 a1 2e  
  
id: 12  
Packet length: 66  
Number of bytes: 66  
Recieved time: Sat Apr 28 19:57:50 2012  
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00  
 00 34 47 ce 40 00 40 06 01 3f c0 a8 38 65 c0 a8  
 38 01 26 68 8e 20 b6 c4 e6 e8 79 e1 63 8f 80 10  
 01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14  
 b7 25  
  
id: 13  
Packet length: 66  
Number of bytes: 66  
Recieved time: Sat Apr 28 19:57:50 2012  
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
 00 34 d4 b4 40 00 40 06 74 58 c0 a8 38 01 c0 a8  
 38 65 8e 20 26 68 79 e1 63 8f b6 c4 e6 e8 80 10  
 00 e5 fb b9 00 00 01 01 08 0a 00 14 b7 26 00 57  
 a1 2e  
仔细研究即可发现服务器与客户机是如何通过tcp通信的。
下面的这个程序可以获取eth0的ip和子网掩码等信息:
test5: [cpp] view plaincopy
#include <stdio.h>  
#include <stdlib.h>  
#include <pcap.h>  
#include <errno.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
  
int main()  
{  
  /* ask pcap to find a valid device for use to sniff on */  
  char * dev;   /* name of the device */   
  char errbuf[PCAP_ERRBUF_SIZE];  
  dev = pcap_lookupdev(errbuf);  
  
  /* error checking */  
  if(!dev)  
  {  
    printf("pcap_lookupdev() error: %s\n", errbuf);  
    exit(1);  
  }  
  
  /* print out device name */  
  printf("dev name: %s\n", dev);  
  
  /* ask pcap for the network address and mask of the device */  
  bpf_u_int32 netp;   /* ip */  
  bpf_u_int32 maskp;  /* subnet mask */  
  int ret;            /* return code */  
  ret = pcap_lookupnet(dev, &netp, &maskp, errbuf);  
  
  if(ret == -1)  
  {  
    printf("pcap_lookupnet() error: %s\n", errbuf);  
    exit(1);  
  }  
  
  /* get the network address in a human readable form */  
  char * net;   /* dot notation of the network address */  
  char * mask;  /* dot notation of the network mask */  
  struct in_addr addr;  
  
  addr.s_addr = netp;  
  net = inet_ntoa(addr);  
  
  if(!net)  
  {  
    perror("inet_ntoa() ip error: ");  
    exit(1);  
  }  
  
  printf("ip: %s\n", net);  
  
  /* do the same as above for the device"s mask */  
  addr.s_addr = maskp;  
  mask = inet_ntoa(addr);  
    
  if(!mask)  
  {  
    perror("inet_ntoa() sub mask error: ");  
    exit(1);  
  }  
    
  printf("sub mask: %s\n", mask);  
  
  return 0;  
}  
结果如图:int pcap_lookupnet(const char * device, bpf_u_int32 * netp, bpf_u_int32 * maskp, char * errbuf)可以获取指定设备的ip地址,子网掩码等信息
netp:传出参数,指定网络接口的ip地址
maskp:传出参数,指定网络接口的子网掩码
pcap_lookupnet()失败返回-1我们使用inet_ntoa()将其转换为可读的点分十进制形式的字符串本文的绝大部分来源于libpcap的官方文档:libpcapHakin9LuisMartinGarcia.pdf,可以在官网下载,文档只有9页,不过很详细,还包括了数据链路层,网络层,传输层,应用层等的分析。很好!

二:[libpcap]libcap使用小结


1. 安装
下载http://www.tcpdump.org/#old-releases ,我下的libpcap-1.0.0.tar.gz
#tar -zxvf libpcap-1.0.0.tar.gz
#cd libpcap-1.0.0
#./configure
#make;make install
安装到默认的目录/usr/lib/和/usr/include/
2. 使用
使用libcap库编写程序时,需要加入头文件#include <pcap.h>
然后在编译的时候加上-lpcap
3. Libcap编程步骤
1)设置sniff设备
char *dev, errbuf[PCAP_ERRBUF_SIZE];
dev = pcap_lookupdev(errbuf);
if (dev == NULL)
{
fprintf(stderr, "couldn"t find default device:%s", errbuf);
return -1;
}
这是查找默认的设备,也可以指定设备,如
char *dev = "eth0";
2)打开设备
pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms,
     char *ebuf)
第一个参数是设备名,snaplen是一个整型值,定义由pcap抓取的包的最大 字节数。 promisc,当设置为true时,使接口处于混杂模式(不管怎样,即
使设置为false,在一些特定情形下接口可能还是 处于混杂模式)。to_ms 是读超时(read time out),单位为毫秒(0表示没有超时;在一些平台上,这
意味着你可能会一直等待直到收到足够数量的包,所以你应该使用一个非零 值)。最后,ebuf用于保存出错信息(如errbuf)。函数返回会话句柄。 
pcap_t *handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if (handle == NULL)
{
fprintf(stderr, "couldn"t open device %s:%s", dev, errbuf);
return -1;
}
3)通信过滤
int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize,
 bpf_u_int32 netmask)
第一个参数是会话句柄(如pcap_t *handle)。接下来的参数用于指向存放编译 后过滤器的空间。然后是过滤表达式。下一个optimize整数决定表达式是否 是“优化的”(0为 false、1为true)。最后,要指定网络掩码netmask。函数
失败时返回-1;其它值表示成功。 
为了获取网络掩码要用到下面这个函数,
int pcap_lookupnet(const char *device, bpf_u_int32 *netp,
bpf_u_int32 *maskp, char *errbuf);
第一个参数是指定设备名,第二个参数是待获取的嗅探设备的ip,第三个参 数是待嗅探设备的网络掩码,第四个参数是错误信息。返回值为0表示成功, -1表示失败。
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1)
{
fprintf(stderr, "couldn"t parse filter %s:%s ", filter_exp,  pcap_geterr(handle));
return -1;
}
4)开始sniff
u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
第一个参数是会话句柄。第二个参数是一个指针,它指向的结构用于存放数 据包的一般信息,如捕获的时间,包长度,组成包的各部分长度。pcap_next() 返回的*u_char指向捕获的包。
pcap_next()获取的是一个数据包,对于多个数据包的获取,就要用pcap_loop()
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
第一个参数是会话句柄。接下来的cnt参数告诉pcap_loop()返回之前应该嗅 探到多少个包(负数表示一直嗅探直到出错为止)。第三个就是回调函数。 最后一个参数的作用是传递附加的自定义数据给回调函数,在一些应用时有
用,很多时候直接设为NULL就行。pcap_dispatch()的用法几乎一样,唯一 的区别是pcap_dispatch()只处理第一批从系统中收到的包,而pcap_loop()会
继续处理接下来的包直到达到指定数量为止。 
回调函数的原型如下:
void xxx_packet(u_char *args, const struct pcap_pkthdr *header,
  const u_char *packet);
第一个参数是传给pcap_loop()的最后一个数据,每次回调函数被调用时都可 以取得这个数据,第二个参数是pcap头结构,包含到达时间,长度等。最 后一个参数packet指向被pcap_loop()嗅探到的整个数据包的首地址,根据这
个首地址就可以解析出以太网头、IP头、TCP头以及正文数据。
pcap头结构如下
struct pcap_pkthdr {
     struct timeval ts; /* time stamp */
     bpf_u_int32 caplen; /* length of portion present */
    bpf_u_int32 len; /* length this packet (off wire) */
};
5)关闭会话
pcap_close(pcap_t *handle);

三:[libpcap]libpcap璇﹁В


libpcap(Packet Capture Library),即数据包捕获函数库,是Unix/Linux平台下的网络数据包捕获函数库。它是一个独立于系统的用户层包捕获的API接口,为底层网络监测提供了一个可移植的框架。
一、libpcap工作原理
libpcap主要由两部份组成:网络分接头(Network Tap)和数据过滤器(Packet Filter)。网络分接头从网络设备驱动程序中收集数据拷贝,过滤器决定是否接收该数据包。Libpcap利用BSD Packet Filter(BPF)算法对网卡接收到的链路层数据包进行过滤。BPF算法的基本思想是在有BPF监听的网络中,网卡驱动将接收到的数据包复制一份交给BPF过滤器,过滤器根据用户定义的规则决定是否接收此数据包以及需要拷贝该数据包的那些内容,然后将过滤后的数据给与过滤器相关联的上层应用程序。
libpcap的包捕获机制就是在数据链路层加一个旁路处理。当一个数据包到达网络接口时,libpcap首先利用已经创建的Socket从链路层驱动程序中获得该数据包的拷贝,再通过Tap函数将数据包发给BPF过滤器。BPF过滤器根据用户已经定义好的过滤规则对数据包进行逐一匹配,匹配成功则放入内核缓冲区,并传递给用户缓冲区,匹配失败则直接丢弃。如果没有设置过滤规则,所有数据包都将放入内核缓冲区,并传递给用户层缓冲区。
二、libpcap的抓包框架
pcap_lookupdev()函数用于查找网络设备,返回可被pcap_open_live()函数调用的网络设备名指针。
pcap_open_live()函数用于打开网络设备,并且返回用于捕获网络数据包的数据包捕获描述字。对于此网络设备的操作都要基于此网络设备描述字。
pcap_lookupnet()函数获得指定网络设备的网络号和掩码。
pcap_compile()函数用于将用户制定的过滤策略编译到过滤程序中。
pcap_setfilter()函数用于设置过滤器。
pcap_loop()函数pcap_dispatch()函数用于捕获数据包,捕获后还可以进行处理,此外pcap_next()和pcap_next_ex()两个函数也可以用来捕获数据包。
pcap_close()函数用于关闭网络设备,释放资源。
其实pcap的应用程序格式很简单,总的来说可以可以分为以下5部分,相信看了以下的5部分,大概能对pcap的总体布局有个大概的了解了吧:
1.我们从决定用哪一个接口进行嗅探开始。在Linux中,这可能是eth0,而在BSD系统中则可能是xl1等等。我们也可以用一个字符串来定义这个设备,或者采用pcap提供的接口名来工作。
2.初始化pcap。在这里我们要告诉pcap对什么设备进行嗅探。假如愿意的话,我们还可以嗅探多个设备。怎样区分它们呢?使用 文件句柄。就像打开一个文件进行读写一样,必须命名我们的嗅探“会话”,以此使它们各自区别开来。
3.假如我们只想嗅探特定的传输(如TCP/IP包,发往端口23的包等等),我们必须创建一个规则集合,编译并且使用它。这个过程分为三个相互紧密关联的阶段。规则集合被置于一个字符串内,并且被转换成能被pcap读的格式(因此编译它)。编译实际上就是在我们的程序里调用一个不被外部程序使用的函数。接下来我们要告诉 pcap使用它来过滤出我们想要的那一个会话。
4.最后,我们告诉pcap进入它的主体执行循环。在这个阶段内pcap一直工作到它接收了所有我们想要的包为止。每当它收到一个包就调用另一个已经定义好的函数,这个函数可以做我们想要的任何工作,它可以剖析所部获的包并给用户打印出结果,它可以将结果保存为一个文件,或者什么也不作。
5.在嗅探到所需的数据后,我们要关闭会话并结束。
三、实现libpcap的每一个步骤
(1)设置设备
这是很简单的。有两种方法设置想要嗅探的设备。
第一种,我们可以简单的让用户告诉我们。考察下面的程序:
   #include
   #include
   int main(int argc, char *argv[])
   {
   char *dev = argv[1];
   printf("Device: %s", dev);
   return(0);
   }
用户通过传递给程序的第一个参数来指定设备。字符串“dev”以pcap能“理解”的格式保存了我们要嗅探的接口的名字(当然,用户必须给了我们一个真正存在的接口)。
另一种也是同样的简单。来看这段程序:
   #include
   #include
   int main()
   {
   char *dev, errbuf[PCAP_ERRBUF_SIZE];
   dev = pcap_lookupdev(errbuf);
   printf("Device: %s", dev);
   return(0);
   }
(2)打开设备进行嗅探
创建一个嗅探会话的任务真的非常简单。为此,我们使用pcap_open_live()函数。此函数的原型(根据pcap的手册页)如下:
   pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
其第一个参数是我们在上一节中指定的设备,snaplen是整形的,它定义了将被pcap捕捉的最大字节数。当promisc设为true时将置指定接口为混杂模式(然而,当它置为false时接口仍处于混杂模式的非凡情况也是有可能的)。to_ms是读取时的超时值,单位是毫秒(假如为0则一直嗅探直到错误发生,为-1则不确定)。最后,ebuf是一个我们可以存入任何错误信息的字符串(就像上面的errbuf)。此函数返回其会话句柄。
混杂模式与非混杂模式的区别:这两种方式区别很大。一般来说,非混杂模式的嗅探器中,主机仅嗅探那些跟它直接有关的通信,如发向它的,从它发出的,或经它路由的等都会被嗅探器捕捉。而在混杂模式中则嗅探传输线路上的所有通信。在非交换式网络中,这将是整个网络的通信。这样做最明显的优点就是使更多的包被嗅探到,它们因你嗅探网络的原因或者对你有帮助,或者没有。但是,混杂模式是可被探测到的。一个主机可以通过高强度的测试判定另一台主机是否正在进行混杂模式的嗅探。其次,它仅在非交换式的网络环境中有效工作(如集线器,或者交换中的ARP层面)。再次,在高负荷的网络中,主机的系统资源将消耗的非常严重。
(3)过滤通信
实现这一过程由pcap_compile()与pcap_setfilter()这两个函数完成。
在使用我们自己的过滤器前必须编译它。过滤表达式被保存在一个字符串中(字符数组)。其句法在tcpdump的手册页中被证实非常好。我建议你亲自阅读它。但是我们将使用简单的测试表达式,这样你可能很轻易理解我的例子。
我们调用pcap_compile()来编译它,其原型是这样定义的:
   int pcap_compile(pcap_t *p, strUCt bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
第一个参数是会话句柄。接下来的是我们存储被编译的过滤器版本的地址的引用。再接下来的则是表达式本身,存储在规定的字符串格式里。再下边是一个定义表达式是否被优化的整形量(0为false,1为true,标准规定)。最后,我们必须指定应用此过滤器的网络掩码。函数返回-1为失败,其他的任何值都表明是成功的。
表达式被编译之后就可以使用了。现在进入pcap_setfilter()。仿照我们介绍pcap的格式,先来看一看pcap_setfilter()的原型:
   int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
这非常直观,第一个参数是会话句柄,第二个参数是被编译表达式版本的引用(可推测出它与pcap_compile()的第二个参数相同)。
下面的代码示例可能能使你更好的理解:
   #include
   pcap_t *handle; /* 会话的句柄 */
   char dev[] = "eth0"; /* 执行嗅探的设备 */
   char errbuf[PCAP_ERRBUF_SIZE]; /* 存储错误 信息的字符串 */
   struct bpf_program filter; /*已经编译好的过滤表达式*/
   char filter_app[] = "port 23"; /* 过滤表达式*/
   bpf_u_int32 mask; /* 执行嗅探的设备的网络掩码 */
   bpf_u_int32 net; /* 执行嗅探的设备的IP地址 */
   pcap_lookupnet(dev, &net, &mask, errbuf);
   handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf);
   pcap_compile(handle, &filter, filter_app, 0, net);
   pcap_setfilter(handle, &filter);
这个程序使嗅探器嗅探经由端口23的所有通信,使用混杂模式,设备是eth0。
(4)实际的嗅探
有两种手段捕捉包。我们可以一次只捕捉一个包,也可以进入一个循环,等捕捉到多个包再进行处理。我们将先看看怎样去捕捉单个包,然后再看看使用循环的方法。为此,我们使用函数pcap_next()。
pcap_next()的原型及其简单:
   u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
第一个参数是会话句柄,第二个参数是指向一个包括了当前数据包总体信息(被捕捉时的时间,包的长度,其被指定的部分长度)的结构体的指针(在这里只有一个片断,只作为一个示例)。pcap_next()返回一个u_char指针给被这个结构体描述的包。我们将稍后讨论这种实际读取包本身的手段。
   这里有一个演示怎样使用pcap_next()来嗅探一个包的例子:
   #include
   #include
   int main()
   {
   pcap_t *handle; /* 会话句柄 */
   char *dev; /* 执行嗅探的设备 */
   char errbuf[PCAP_ERRBUF_SIZE]; /* 存储错误信息的字符串 */
  
   struct bpf_program filter; /* 已经编译好的过滤器 */
   char filter_app[] = "port 23"; /* 过滤表达式 */
   bpf_u_int32 mask; /* 所在网络的掩码 */
   bpf_u_int32 net; /* 主机的IP地址 */
   struct pcap_pkthdr header; /* 由pcap.h定义 */
   const u_char *packet; /* 实际的包 */
   /* Define the device */
   dev = pcap_lookupdev(errbuf);
   /* 探查设备属性 */
   pcap_lookupnet(dev, &net, &mask, errbuf);
   /* 以混杂模式打开会话 */
   handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf);
   /* 编译并应用过滤器 */
   pcap_compile(handle, &filter, filter_app, 0, net);
   pcap_setfilter(handle, &filter);
   /* 截获一个包 */
   packet = pcap_next(handle, &header);
   /* 打印它的长度 */
   printf("Jacked a packet with length of [%d]
   ", header.len);
   /* 关闭会话 */
   pcap_close(handle);
   return(0);
   }
这个程序嗅探被pcap_lookupdev()返回的设备并将它置为混杂模式。它发现第一个包经过端口23(telnet)并且告诉用户此包的大小(以字 节为单位)。这个程序又包含了一个新的调用pcap_close(),我们将在后面讨论(尽管它的名字就足够证实它自己的作用)。
实际上很少有嗅探程序会真正的使用pcap_next()。通常,它们使用pcap_loop()或者 pcap_dispatch()(它就是用了pcap_loop())。
pcap_loop()的原型如下:
   int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
第一个参数是会话句柄,接下来是一个整型,它告诉pcap_loop()在返回前应捕捉多少个数据包(若为负值则表示应该一直工作直至错误发生)。第三个参数是回调函数的名称(正像其标识符所指,无括号)。最后一个参数在有些应用里有用,但更多时候则置为NULL。假设我们有我们自己的想送往回调函数的参数,另外还有pcap_loop()发送的参数,这就需要用到它。很明显,必须是一个u_char类型的指针以确保结果正确;正像我们稍后见到的,pcap使用了很有意思的方法以u_char指针的形势传递信息。pcap_dispatch()的用法几乎相同。唯一不同的是它们如何处理超时(还记得在调用pcap_open_live()时怎样设置超时吗?这就是它起作用的地方)。Pcap_loop()忽略超时而pcap_dispatch()则不。关于它们之间区别的更深入的讨论请参见pcap的手册页。
回调函数的原型:
   void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
让我们更细致的考察它。首先,你会注重到该函数返回void类型,这是符合逻辑的,因为pcap_loop()不知道如何去处理一个回调返回值。第一个参数相应于pcap_loop()的最后一个参数。每当回调函数被老婆 调用时,无论最后一个参数传给pcap_loop()什么值,这个值都会传给我们回调函数的第一个参数。第二个参数是pcap头文件定义的,它包括数据包被嗅探的时间、大小等信息。结构体pcap_pkhdr在pcap.h中定义如下:
   struct pcap_pkthdr {
   struct timeval ts; /* 时间戳 */
   bpf_u_int32 caplen; /* 已捕捉部分的长度 */
   bpf_u_int32 len; /* 该包的脱机长度 */
   };
这些量都相当明了。最后一个参数在它们中是最有意思的,也最让pcap程序新手感到迷惑。这又是一个u_char指针,它包含了被pcap_loop()嗅探到的所有包。
但是你怎样使用这个我们在原型里称为packet的变量呢?一个数据包包含许多属性,因此你可以想象它不只是一个字符串,而实质上是一个结构体的集合(比如,一个TCP/IP包会有一个以太网的头部,一个IP头部,一个TCP头部,还有此包的有效载荷)。这个u_char就是这些结构体的串联版本。为了使用它,我们必须作一些有趣的匹配工作。
下面这些是一些数据包的结构体:
   /* 以太网帧头部 */
   struct sniff_ethernet {
   u_char ether_dhost[ETHER_ADDR_LEN]; /* 目的主机的地址 */
   u_char ether_shost[ETHER_ADDR_LEN]; /* 源主机的地址 */
   u_short ether_type; /* IP? ARP? RARP? etc */
   };
   /* IP数据包的头部 */
   struct sniff_ip {
   #if BYTE_ORDER == LITTLE_ENDIAN
   u_int ip_hl:4, /* 头部长度 */
   ip_v:4; /* 版本号 */
   #if BYTE_ORDER == BIG_ENDIAN
   u_int ip_v:4, /* 版本号 */
   ip_hl:4; /* 头部长度 */
   #endif
   #endif /* not _IP_VHL */
   u_char ip_tos; /* 服务的类型 */
   u_short ip_len; /* 总长度 */
   u_short ip_id; /*包标志号 */
   u_short ip_off; /* 碎片偏移 */
   #define IP_RF 0x8000 /* 保留的碎片标志 */
   #define IP_DF 0x4000 /* dont fragment flag */
   #define IP_MF 0x2000 /* 多碎片标志*/
   #define IP_OFFMASK 0x1fff /*分段位 */
   u_char ip_ttl; /* 数据包的生存时间 */
   u_char ip_p; /* 所使用的协议 */
   u_short ip_sum; /* 校验和 */
   struct in_addr ip_src,ip_dst; /* 源地址、目的地址*/
   };
   /* TCP 数据包的头部 */
   struct sniff_tcp {
   u_short th_sport; /* 源端口 */
   u_short th_dport; /* 目的端口 */
   tcp_seq th_seq; /* 包序号 */
   tcp_seq th_ack; /* 确认序号 */
   #if BYTE_ORDER == LITTLE_ENDIAN
   u_int th_x2:4, /* 还没有用到 */
   th_off:4; /* 数据偏移 */
   #endif
   #if BYTE_ORDER == BIG_ENDIAN
   u_int th_off:4, /* 数据偏移*/
   th_x2:4; /*还没有用到 */
   #endif
   u_char th_flags;
   #define TH_FIN 0x01
   #define TH_SYN 0x02
   #define TH_RST 0x04
   #define TH_PUSH 0x08
   #define TH_ACK 0x10
   #define TH_URG 0x20
   #define TH_ECE 0x40
   #define TH_CWR 0x80
   #define TH_FLAGS (TH_FINTH_SYNTH_RSTTH_ACKTH_URGTH_ECETH_CWR)
   u_short th_win; /* TCP滑动窗口 */
   u_short th_sum; /* 头部校验和 */
   u_short th_urp; /* 紧急服务位 */
   };
pcap嗅探数据包时正是使用的这些结构。接下来,它简单的创建一个u_char字符串并且将这些结构体填入。那么我们怎样才能区分它们呢?预备好见证指针最实用的好处之一吧。
我们再一次假定要对以太网上的TCP/IP包进行处理。同样的手段可以应用于任何数据包,唯一的区别是你实际所使用的结构体的类型。让我们从声明分解u_char包的变量开始:
   const struct sniff_ethernet *ethernet; /* 以太网帧头部*/
   const struct sniff_ip *ip; /* IP包头部 */
   const struct sniff_tcp *tcp; /* TCP包头部 */
   const char *payload; /* 数据包的有效载荷*/
   /*为了让它的可读性好,我们计算每个结构体中的变量大小*/
   int size_ethernet = sizeof(struct sniff_ethernet);
   int size_ip = sizeof(struct sniff_ip);
   int size_tcp = sizeof(struct sniff_tcp);
   现在我们开始让人感到有些神秘的匹配:
   ethernet = (struct sniff_ethernet*)(packet);
   ip = (struct sniff_ip*)(packet + size_ethernet);
   tcp = (struct sniff_tcp*)(packet + size_ethernet + size_ip);
   payload = (u_char *)(packet + size_ethernet + size_ip + size_tcp);
此处如何工作?考虑u_char在内存中的层次。基本的,当pcap将这些结构体填入u_char的时候是将这些数据存入一个字符串中,那个字符串将被送入我们的回调函数中。反向转换是这样的,不考虑这些结构体制中的值,它们的大小将是一致的。例如在我的平台上,一个sniff_ethernet结构体的大小是14字节。一个sniff_ip结构体是20字节,一个sniff_tcp结构体也是20字节。 u_char指针正是包含了内存地址的一个变量,这也是指针的实质,它指向内存的一个区域。简单而言,我们说指针指向的地址为x,假如三个结构体恰好线性排列,第一个(sniff_ethernet)被装载到内存地址的x处则我们很轻易的发现其他结构体的地址,让我们以表格显示之:
   Variable Location (in bytes)
   sniff_ethernet X
   sniff_ip X + 14
   sniff_tcp X + 14 + 20
   payload X + 14 + 20 + 20
结构体sniff_ethernet正好在x处,紧接着它的sniff_ip则位于x加上它本身占用的空间(此例为14字节),依此类推可得全部地址。
注重:你没有假定你的变量也是同样大小是很重要的。你应该总是使用sizeof()来确保尺寸的正确。这是因为这些结构体中的每个成员在不同平台下可以有不同的尺寸。
下面是主要函数接口:
pcap_t *pcap_open_live(char *device, int snaplen,
   int promisc, int to_ms, char *ebuf)
   获得用于捕获网络数据包的数据包捕获描述字。device参数为指定打开
   的网络设备名。snaplen参数定义捕获数据的最大字节数。promisc指定
   是否将网络接口置于混杂模式。to_ms参数指定超时时间(毫秒)。
   ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消
   息。
pcap_t *pcap_open_offline(char *fname, char *ebuf)
   打开以前保存捕获数据包的文件,用于读取。fname参数指定打开的文
   件名。该文件中的数据格式与tcpdump和tcpslice兼容。"-"为标准输
   入。ebuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传
   递错误消息。
pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)
   打开用于保存捕获数据包的文件,用于写入。fname参数为"-"时表示
   标准输出。出错时返回NULL。p参数为调用pcap_open_offline()或
   pcap_open_live()函数后返回的pcap结构指针。fname参数指定打开
   的文件名。如果返回NULL,则可调用pcap_geterr()函数获取错误消
   息。
char *pcap_lookupdev(char *errbuf)
   用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络
   设备名指针。如果函数出错,则返回NULL,同时errbuf中存放相关的
   错误消息。
int pcap_lookupnet(char *device, bpf_u_int32 *netp,
   bpf_u_int32 *maskp, char *errbuf)
   获得指定网络设备的网络号和掩码。netp参数和maskp参数都是
   bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相
   关的错误消息。
int pcap_dispatch(pcap_t *p, int cnt,
   pcap_handler callback, u_char *user)
   捕获并处理数据包。cnt参数指定函数返回前所处理数据包的最大值。
   cnt=-1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有
   数据包,直到产生以下错误之一:读取到EOF;超时读取。callback
   参数指定一个带有三个参数的回调函数,这三个参数为:一个从
   pcap_dispatch()函数传递过来的u_char指针,一个pcap_pkthdr结构
   的指针,和一个数据包大小的u_char指针。如果成功则返回读取到的
   字节数。读取到EOF时则返回零值。出错时则返回-1,此时可调用
   pcap_perror()或pcap_geterr()函数获取错误消息。
int pcap_loop(pcap_t *p, int cnt,
   pcap_handler callback, u_char *user)
   功能基本与pcap_dispatch()函数相同,只不过此函数在cnt个数据包
   被处理或出现错误时才返回,但读取超时不会返回。而如果为
   pcap_open_live()函数指定了一个非零值的超时设置,然后调用
   pcap_dispatch()函数,则当超时发生时pcap_dispatch()函数会返回。
   cnt参数为负值时pcap_loop()函数将始终循环运行,除非出现错误。
void pcap_dump(u_char *user, struct pcap_pkthdr *h,
   u_char *sp)
   向调用pcap_dump_open()函数打开的文件输出一个数据包。该函数可
   作为pcap_dispatch()函数的回调函数。
int pcap_compile(pcap_t *p, struct bpf_program *fp,
   char *str, int optimize, bpf_u_int32 netmask)
   将str参数指定的字符串编译到过滤程序中。fp是一个bpf_program结
   构的指针,在pcap_compile()函数中被赋值。optimize参数控制结果
   代码的优化。netmask参数指定本地网络的网络掩码。
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
   指定一个过滤程序。fp参数是bpf_program结构指针,通常取自
   pcap_compile()函数调用。出错时返回-1;成功时返回0。
u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
   返回指向下一个数据包的u_char指针。
int pcap_datalink(pcap_t *p)
   返回数据链路层类型,例如DLT_EN10MB。
int pcap_snapshot(pcap_t *p)
   返回pcap_open_live被调用后的snapshot参数值。
int pcap_is_swapped(pcap_t *p)
   返回当前系统主机字节与被打开文件的字节顺序是否不同。
int pcap_major_version(pcap_t *p)
   返回写入被打开文件所使用的pcap函数的主版本号。
int pcap_minor_version(pcap_t *p)
   返回写入被打开文件所使用的pcap函数的辅版本号。
int pcap_stats(pcap_t *p, struct pcap_stat *ps)
   向pcap_stat结构赋值。成功时返回0。这些数值包括了从开始
   捕获数据以来至今共捕获到的数据包统计。如果出错或不支持
   数据包统计,则返回-1,且可调用pcap_perror()或
   pcap_geterr()函数来获取错误消息。
FILE *pcap_file(pcap_t *p)
   返回被打开文件的文件名。
int pcap_fileno(pcap_t *p)
   返回被打开文件的文件描述字号码。
void pcap_perror(pcap_t *p, char *prefix)
   在标准输出设备上显示最后一个pcap库错误消息。以prefix参
   数指定的字符串为消息头。
char *pcap_geterr(pcap_t *p)
   返回最后一个pcap库错误消息。
char *pcap_strerror(int error)
   如果strerror()函数不可用,则可调用pcap_strerror函数替代。
void pcap_close(pcap_t *p)
   关闭p参数相应的文件,并释放资源。

本文来源:https://www.shanpow.com/wx/432444/

《libpcap.doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档

文档为doc格式

相关阅读
  • 女朋友走了网络散文【汇编六篇】 女朋友走了网络散文【汇编六篇】
  • 同学录留言古风三篇 同学录留言古风三篇
  • 疫情期间老师的感悟 疫情期间老师的感悟
  • 网络覆盖工程什么网络扶智工程信息服务工程什么 网络覆盖工程什么网络扶智工程信息服务工程什么
  • 2020难忘的网课为题疫情作文5篇_特殊的寒假作文精选 2020难忘的网课为题疫情作文5篇_特殊的寒假作文精选
  • 浅谈网络环境下教与学3篇 浅谈网络环境下教与学3篇
  • 2020年疫情期间的生活的作文500字 2020年疫情期间的生活的作文500字
  • 2020年的疫情期间的生活作文500字5篇 2020年的疫情期间的生活作文500字5篇
为您推荐
  • 疫情期间的生活作文500字5篇
    疫情期间的生活作文500字5篇
    一场新型冠状病毒引发的肺炎疫情笼罩了中华大地,人们开始足不出户,生活变得压抑起来,为了不影响企业生产和学生上课,远程上班,网络教学成为我们生活的一部分。今天小编
  • 疫情期间在家上网课感想作文500字
    疫情期间在家上网课感想作文500字
    疫情期间,教育局为了不让我们耽误学习,开展了“停课不停学”,也就是上网课。但是开网课的心本来是好的,但是它有利也有弊。以下是小编为大家准
  • 新时期图书出版编辑的职业素养要求
    新时期图书出版编辑的职业素养要求
    张笑城【摘 要】新时期,随着社会发展和科技进步,传统图书出版业面临诸多新的挑战。图书出版编辑只有不断提升自身职业素养,才能更好地保证图书质量,适应时代发展。本文
  • 2020防控疫情期间教师线上直播上课心得体会
    2020防控疫情期间教师线上直播上课心得体会
    万万没想到2020年的春天,以这样的方式开始六年级下册的教学,万万没想到我以50多岁的高龄成为一名网络主播,下面我来和大家分享我的做法、成效和思考。 一、 我的
  • 关于此次疫情观后感心得体会5篇
    关于此次疫情观后感心得体会5篇
    疫情在新春时节肆虐,让抗击疫情发展有了更大的艰巨性。关于此次疫情防控的心得怎么写?下面是小编给大家带来的有关学生此次疫情感悟作文,仅供大家参阅。学生此次疫情感悟
  • 2020年十大网络完结小说排行榜
    2020年十大网络完结小说排行榜
    说到络小说,就不得不提17k中文和起点中文,大家耳熟能详的络小说基本上都出自这俩巨头旗下。这是个信息爆炸的时代,之于文字工作者,是最好的时代,也是最坏的时代。下面是小编整理的2020年十大络完结小说排
  • 《在战疫中成长》精选观后感600字作文5篇
    《在战疫中成长》精选观后感600字作文5篇
    《在战“疫”中成长》精选观后感600字作文1随着“疫情”的发展,有些人为了吸引眼球,哗众取宠,制造“危机”感。网络上的一些骗子,趁机散播谣言。这时,我们不要盲
  • 疫情防空我在行动征文
    疫情防空我在行动征文
    关于疫情防空我在行动征文最新大全5篇武汉疫情发生后,网络上舆情汹涌。有的人在高喊“武汉加油”,有的人积极给大家提供防疫措施。以下是小编给
  • 2020年网络开学典礼观后感作文篇
    2020年网络开学典礼观后感作文篇
    一场突如其来的病毒肆虐,让我们经历了一个特殊的寒假,也让我们看到了万众一心,众志成城的中国力量。在这特殊时期,我区教育行政部门用一种别开生面的网络模式进行了开学
  • 网上直播授课教师个人心得体会5篇
    网上直播授课教师个人心得体会5篇
    疫情期间,教师们依然在辛勤的工作着,就是为了让学生不落下课程,努力的做到停课不停教。教师们通过手机微信和其它网络来教给同学们知识。那你知道网上直播授课教师个人心