知道如何获取适配器的信息了,那我们就开始一项更具意义的工作,打开适配器并捕获数据包。编写一个程序,将每一个 通过适配器的数据包 打印出来。
打开设备的函数是 pcap_open() 。
(Open a generic source in order to capture / send (WinPcap only) traffic.)
pcap_t* pcap_open ( const char * source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth * auth, char * errbuf )
· snaplen 制定要捕获数据包中的哪些部分。 在一些操作系统中 (比如 xBSD 和 Win32), 驱动可以被配置成 只捕获数据包的初始化部分 : 这样可以减少应用程序间复制数据的量,从而提高捕获效率。本例中,我们将值定为65535,它比我们能遇到的最大的MTU还要大。因此,我们确信我们总能收到完整的数据包。
· flags : 最最重要的flag是用来指示适配器是否要被设置成 混杂模式 。 一般情况下,适配器只接收发给它自己的数据包, 而那些在其他机器之间通讯的数据包,将会被丢弃 。 相反,如果适配器是混杂模式,那么不管这个数据包是不是发给我的,我都会去捕获。也就是说,我会去捕获所有的数据包。 这意味着在一个共享媒介(比如总线型以太网),WinPcap能捕获其他主机的所有的数据包。 大多数用于数据捕获的应用程序都会将适配器设置成混杂模式 ,所以,我们也会在下面的范例中,使用混杂模式。
· read_timeout(to_ms): 指定 读取数据的超时时间 ,以毫秒计(1s=1000ms)。在适配器上进行读取操作(比如用 pcap_dispatch() 或 pcap_next_ex() ) 都会在 to_ms 毫秒时间内响应,即使在网络上没有可用的数据包。 在统计模式下 , to_ms 还可以用来定义 统计的时间间隔 。 将 to_ms 设置为0意味着没有超时,那么如果没有数据包到达的话,读操作将永远不会返回。 如果设置成-1,则情况恰好相反,无论有没有数据包到达,读操作都会立即返回。
- Returns:
- A pointer to a 'pcap_t' which can be used as a parameter to the following calls ( pcap_compile() and so on) and that specifies an opened WinPcap session. In case of problems, it returns NULL and the 'errbuf' variable keeps the error message.
- Warning:
-
The source cannot be larger than PCAP_BUF_SIZE.
The following formats are not allowed as 'source' strings:
- rpcap:// [to open the first local adapter]
- rpcap://hostname/ [to open the first remote adapter]
int pcap_dispatch ( pcap_t * p, int cnt, pcap_handler callback, u_char * user )
Collect a group of packets.
int pcap_loop ( pcap_t * p, int cnt, pcap_handler callback, u_char * user )
Collect a group of packets.
pcap_dispatch()与pcap_loop()的区别:
当适配器被打开,捕获工作就可以用 pcap_dispatch() 或 pcap_loop() 进行。 这两个函数非常的相似, 区别就是 pcap_ dispatch() 当超时时间到了(timeout expires)就返回 (尽管不能保证) ,而 pcap_loop() 不会因此而返回 ,只有当 cnt 数据包被捕获,所以,pcap_loop()会在一小段时间内,阻塞网络的利用。 pcap_loop() 对于我们这个简单的范例来说,可以满足需求,不过, pcap_dispatch() 函数一般用于比较复杂的程序中。
这两个函数都有一个 回调 参数, packet_handler 指向一个可以接收数据包的函数 。 这个函数会在收到每个新的数据包并收到一个通用状态时被libpcap所调用 ( 与函数 pcap_loop() 和 pcap_dispatch() 中的 user 参数相似),数据包的首部一般有一些诸如时间戳,数据包长度的信息,还有包含了协议首部的实际数据。 注意:冗余校验码CRC不再支持,因为帧到达适配器,并经过校验确认以后,适配器就会将CRC删除,与此同时,大部分适配器会直接丢弃CRC错误的数据包,所以,WinPcap没法捕获到它们。
上面的程序将每一个数据包的时间戳和长度从 pcap_pkthdr 的首部解析出来,并打印在屏幕上。
请注意,使用 pcap_loop() 函数可能会遇到障碍,主要因为它直接由数据包捕获驱动所调用。因此,用户程序是不能直接控制它的。另一个实现方法(也是提高可读性的方法),是使用 pcap_next_ex() 函数。有关这个函数的使用,请看 ( 不用回调方法捕获数据包 ).
struct pcap_pkthdr{ timeval ts // time stamp bpf_u_int32 caplen // length of portion present bpf_u_int32 len // length this packet (off wire) };
Detailed Description
Header of a packet in the dump file.
Each packet in the dump file is prepended with this generic header. This gets around the problem of different headers for different packet interfaces.

1 #include " pcap.h " 2 #pragma comment(lib, "wpcap.lib") 3 #pragma comment(lib, "Packet.lib") 4 #pragma comment(lib, "wsock32.lib") 5 6 7 #include " pcap.h " 8 9 /* packet handler 函数原型 */ 10 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char * pkt_data); 11 12 main() 13 { 14 pcap_if_t * alldevs; 15 pcap_if_t * d; 16 int inum; 17 int i= 0 ; 18 pcap_t * adhandle; 19 char errbuf[PCAP_ERRBUF_SIZE]; 20 21 /* 获取本机设备列表 */ 22 if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == - 1 ) 23 { 24 fprintf(stderr, " Error in pcap_findalldevs: %s\n " , errbuf); 25 exit( 1 ); 26 } 27 28 /* 打印列表 */ 29 for (d=alldevs; d; d=d-> next) 30 { 31 printf( " %d. %s " , ++i, d-> name); 32 if (d-> description) 33 printf( " (%s)\n " , d-> description); 34 else 35 printf( " (No description available)\n " ); 36 } 37 38 if (i== 0 ) 39 { 40 printf( " \nNo interfaces found! Make sure WinPcap is installed.\n " ); 41 return - 1 ; 42 } 43 44 printf( " Enter the interface number (1-%d): " ,i); 45 scanf( " %d " , & inum); 46 47 if (inum < 1 || inum > i) 48 { 49 printf( " \nInterface number out of range.\n " ); 50 /* 释放设备列表 */ 51 pcap_freealldevs(alldevs); 52 return - 1 ; 53 } 54 55 /* 跳转到选中的适配器 */ 56 for (d=alldevs, i= 0 ; i< inum- 1 ;d=d->next, i++ ); 57 58 /* 打开设备 */ 59 if ( (adhandle= pcap_open(d->name, // 设备名 60 65536 , // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容 61 PCAP_OPENFLAG_PROMISCUOUS, // 混杂模式 62 1000 , // 读取超时时间 63 NULL, // 远程机器验证 64 errbuf // 错误缓冲池 65 ) ) == NULL) 66 { 67 fprintf(stderr, " \nUnable to open the adapter. %s is not supported by WinPcap\n " , d-> name); 68 /* 释放设备列表 */ 69 pcap_freealldevs(alldevs); 70 return - 1 ; 71 } 72 73 printf( " \nlistening on %s...\n " , d-> description); 74 75 /* 释放设备列表 */ 76 pcap_freealldevs(alldevs); 77 78 /* 开始捕获 */ 79 pcap_loop(adhandle, 0 , packet_handler, NULL); 80 81 return 0 ; 82 } 83 84 85 /* 每次捕获到数据包时,libpcap都会自动调用这个回调函数 */ 86 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char * pkt_data) 87 { 88 struct tm * ltime; 89 char timestr[ 16 ]; 90 time_t local_tv_sec; 91 92 /* 将时间戳转换成可识别的格式 */ 93 local_tv_sec = header-> ts.tv_sec; 94 ltime=localtime(& local_tv_sec); 95 strftime( timestr, sizeof timestr, " %H:%M:%S " , ltime); 96 97 printf( " %s,%.6d len:%d\n " , timestr, header->ts.tv_usec, header-> len); 98 }
*结果:
时间戳(精确到微妙),包长度;
timeval
The timeval structure is used to specify time values. It is associated with the Berkeley Software Distribution (BSD) file Time.h.
struct timeval { long tv_sec; // seconds long tv_usec; // and microseconds };
Members
- tv_sec
- Time value, in seconds.
- tv_usec
- Time value, in microseconds.
localtime
Converts a time value and corrects for the local time zone.
struct tm *localtime( const time_t *timer );
struct tm { int tm_sec; /* seconds after the minute - [0,59] */ int tm_min; /* minutes after the hour - [0,59] */ int tm_hour; /* hours since midnight - [0,23] */ int tm_mday; /* day of the month - [1,31] */ int tm_mon; /* months since January - [0,11] */ int tm_year; /* years since 1900 */ int tm_wday; /* days since Sunday - [0,6] */ int tm_yday; /* days since January 1 - [0,365] */ int tm_isdst; /* daylight savings time flag */ };
asctime, _wasctime
Converts a tm time structure to a character string.
char *asctime( const struct tm * timeptr ); wchar_t *_wasctime( const struct tm *timeptr );
Return Value
asctime returns a pointer to the character string result; _wasctime returns a pointer to the wide-character string result. There is no error return value.
time
Gets the system time.
time_t time( time_t *timer );
strftime, wcsftime
Format a time string.
size_t strftime( char *strDest, size_t maxsize, const char *format, const struct tm * timeptr ); size_t wcsftime( wchar_t *strDest, size_t maxsize, const wchar_t *format, const struct tm *timeptr );
Return Value
strftime returns the number of characters placed in strDest if the total number of resulting characters, including the terminating null, is not more than maxsize .
wcsftime returns the corresponding number of wide characters. Otherwise, the functions return 0, and the contents of strDest is indeterminate.
void
pcap_freealldevs ( pcap_if_t * alldevsp )
Free an interface list returned by pcap_findalldevs() .