文章目录

  • 一、libpcap简介
  • 二、libpcap下载
  • 三、libpcap安装
  • 四、libpcap验证
  • 五、Qt安装
  • 5.1、命令行安装
  • 5.2、Qt下载安装
  • 六、libpcap工作原理
  • 七、libpcap主要接口函数API
  • 1、获得用户捕获网络数据包的数据包捕获描述字
  • 2、打开以前保存捕获数据包的文件,用于读取
  • 3、打开用于保存捕获数据包的文件,用于写入
  • 4、用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名指针
  • 5、获得指定网络设备的网络号和掩码
  • 6、捕获并处理数据包
  • 7、捕获并处理数据包
  • 8、向调用pcap_dump_open()函数打开的文件输出一个数据包
  • 9、将str参数指定的字符串编译到过滤程序中
  • 10、指定一个过滤程序
  • 11、返回数据链路层类型
  • 12、返回pcap_open_live被调用后的snapshot参数值
  • 13、返回当前系统主机字节与被打开文件的字节顺序是否不同
  • 14、返回写入被打开文件所使用的pcap函数的主版本号
  • 15、返回写入被打开文件所使用的pcap函数的辅版本号
  • 16、向pcap_stat结构赋值
  • 17、返回被打开文件的文件名
  • 18、返回被打开文件的文件描述字号码
  • 19、在标准输出设备上显示最后一个pcap库错误消息
  • 20、返回最后一个pcap库错误消息
  • 21、如果strerror()函数不可用,则可调用pcap_strerror函数替代
  • 22、关闭p参数相应的文件,并释放资源
  • 23、函数返回pcap_dumper_t类型的指针
  • 24、用来关闭pcap_dump_open打开的文件
  • 25、刷新缓冲区,把捕获的数据包从缓冲区真正拷贝到文件
  • 26、输出数据到文件
  • 八、libpcap数据包结构
  • 九、使用libpcap编码


一、libpcap简介

LibpcapPacket Capture Libray的英文缩写,即数据包捕获函数库。该库提供的C函数接口用于捕捉经过指定网络接口的数据包,该接口应该是被设为混杂模式。这个在原始套接子中有提到。

著名的软件TCPDUMP就是在Libpcap的基础上开发而成的。Libpcap提供的接口函数实现和封装了与数据包截获有关的过程。

Libpcap提供了用户级别的网络数据包捕获接口,并充分考虑到应用程序的可移植性。Libpcap可以在绝大多数Linux平台上运行。在Windows平台上,也有一款与其功能类似的开发库:Wincap

它的工作在上层应用程序与网络接口之间。

主要功能:

  • 数据包捕获:捕获流经网卡的原始数据包
  • 自定义数据包发送:构造任何格式的原始数据包
  • 流量采集与统计:采集网络中的流量信息
  • 规则过滤:提供自带规则过滤功能,按需要选择过滤规则

它的应用范围非常广泛,典型应用包括玩罗协议分析器,网络流量发生器,网络入侵检测系统,网络扫描器和其他安全工具。


二、libpcap下载

官网下载: http://www.tcpdump.org/

Linux下使用Qt + libpcap_打开文件


三、libpcap安装

tar -zxvf libpcap-1.10.4.tar.gz		//解压
cd libpcap-1.10.4					//进入文件夹
sudo ./configure					//配置
sudo make							//编译
sudo make install					//安装

Linux下使用Qt + libpcap_qt_02

Linux下使用Qt + libpcap_qt_03


Linux下使用Qt + libpcap_打开文件_04


四、libpcap验证

通过find命令或者locate命令查询pcap.solibpcap.h文件是否存在,安装成功。

find /usr -name "libpcap*so*"

locate pcap.h

Linux下使用Qt + libpcap_linux_05


Linux下使用Qt + libpcap_#define_06


五、Qt安装

5.1、命令行安装

1、判断gcc是否安装

gcc -v

Linux下使用Qt + libpcap_打开文件_07


没有安装的话记得安装:

sudo apt-get install gcc

2、判断g++是否安装

gcc -v

Linux下使用Qt + libpcap_#define_08


没有安装的话记得安装:

sudo apt-get install g++

3、判断是否安装clang编译器

clang  -v

Linux下使用Qt + libpcap_IP_09

没有安装的话记得安装:

sudo apt-get install clang

4、判断是否安装clang++编译器

clang++  -v

Linux下使用Qt + libpcap_#define_10

没有安装的话记得安装:

sudo apt-get install clang++

5、判断是否安装make

clang++  -v

Linux下使用Qt + libpcap_linux_11

没有安装的话记得安装:

sudo apt-get install make

6、安装make-guile

sudo apt-get install make-guile

7、安装cmake

sudo apt-get install cmake

8、安装Qt5组件

sudo apt-get install build-essential

9、安装Qt的开发工具

sudo apt-get install qtbase5-dev
sudo apt-get install qtchooser
sudo apt-get install qt5-qmake
sudo apt-get install qtbase5-dev-tools

10、安装qtcreator

sudo apt-get install qtcreator

11、安装qt

sudo apt-get install qt5*

5.2、Qt下载安装

官网下载: https://download.qt.io/archive/qt/

Linux下使用Qt + libpcap_#define_12


Linux下使用Qt + libpcap_IP_13


下载完成之后,解压并安装:

sudo chmod -R 777 qt-opensource-linux-x6405.14.2.run
sudo ./qt-opensource-linux-x6405.14.2.run

之后按照引导安装即可。

安装完成之后,新建一个工程验证安装是否成功即可!

Linux下使用Qt + libpcap_IP_14


六、libpcap工作原理

libpcap主要由两部分组成:网络分接头(network tap)和数据过滤器(packet filter)

  • 网络分接头从网络设备驱动程序中收集数据,进行拷贝;
  • 过滤器决定是否接收该数据包;

libpcap利用BSD packet filter(BPF)算法对网卡接收到的链路层数据包进行过滤,BPF算法的基本思想是在有BPF监听的网络中,网卡驱动将接收到的数据包复制一份交给BPF过滤器,过滤器根据用户定义的规则决定是否接收该数据包,以及需要拷贝该数据包的哪些内容,然后将过滤后的数据交给过滤器关联的上层应用程序。

libpcap的包捕获机制就是在数据链路层加一个旁路处理,当一个数据包到达网络接口时,libpcap首先利用已经创建的套接字从数据链路层驱动程序中获得该数据包的拷贝,再将数据包发给BPF过滤器,BPF过滤器根据用户定义的规则将数据包逐一匹配,匹配成功的放入内核缓冲区,匹配失败直接丢弃。如果没有设置过滤规则,那么多有的数据包都将放入内核缓冲区并且传递给用户层缓冲区。


七、libpcap主要接口函数API

1、获得用户捕获网络数据包的数据包捕获描述字

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用于传递错误消息。

2、打开以前保存捕获数据包的文件,用于读取

pcap_t *pcap_open_offline(char *fname, char *ebuf)
  • fname: 指定打开的文件名。该文件中的数据格式与tcpdump和tcpslice兼容。"-"为标准输入。
  • ebuf: 仅在pcap_open_offline函数出错时返回NULL用于传递错误消息。

3、打开用于保存捕获数据包的文件,用于写入

pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)
  • p: 为调用pcap_open_offline()pcap_open_live()函数后返回的pcap结构指针。
  • fname: 指定打开的文件名。如果返回NULL,则可调用pcap_geterr()函数获取错误消息。

4、用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名指针

char *pcap_lookupdev(char *errbuf)
  • 如果函数出错,则返回NULL,同时errbuf中存放相关的错误消息。

5、获得指定网络设备的网络号和掩码

int pcap_lookupnet(char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf)
  • netp参数和maskp参数都是bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相关的错误消息。

6、捕获并处理数据包

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()函数获取错误消息。

7、捕获并处理数据包

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()函数将始终循环运行,除非出现错误。

8、向调用pcap_dump_open()函数打开的文件输出一个数据包

void pcap_dump(u_char *user, struct pcap_pkthdr *h, u_char *sp)
  • 该函数可作为pcap_dispatch()函数的回调函数

9、将str参数指定的字符串编译到过滤程序中

int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
  • fp: 是一个bpf_program结构的指针,在pcap_compile()函数中被赋值。
  • optimize: 控制结果代码的优化。
  • netmask: 指定本地网络的网络掩码。

10、指定一个过滤程序

int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
  • fp:是bpf_program结构指针,通常取自pcap_compile()函数调用。出错时返回-1;成功时返回0。

11、返回数据链路层类型

int pcap_datalink(pcap_t *p)
  • 例如: DLT_EN10MB

12、返回pcap_open_live被调用后的snapshot参数值

int pcap_snapshot(pcap_t *p)

13、返回当前系统主机字节与被打开文件的字节顺序是否不同

int pcap_is_swapped(pcap_t *p)

14、返回写入被打开文件所使用的pcap函数的主版本号

int pcap_major_version(pcap_t *p)

15、返回写入被打开文件所使用的pcap函数的辅版本号

int pcap_minor_version(pcap_t *p)

16、向pcap_stat结构赋值

int pcap_stats(pcap_t *p, struct pcap_stat *ps)
  • 成功时返回0。
  • 这些数值包括了从开始捕获数据以来至今共捕获到的数据包统计。
  • 如果出错或不支持数据包统计,则返回-1,
  • 且可调用pcap_perror()pcap_geterr()函数来获取错误消息。

17、返回被打开文件的文件名

FILE *pcap_file(pcap_t *p)

18、返回被打开文件的文件描述字号码

int pcap_fileno(pcap_t *p)

19、在标准输出设备上显示最后一个pcap库错误消息

void pcap_perror(pcap_t *p, char *prefix)
  • 以prefix参数指定的字符串为消息头。

20、返回最后一个pcap库错误消息

char *pcap_geterr(pcap_t *p)

21、如果strerror()函数不可用,则可调用pcap_strerror函数替代

char *pcap_strerror(int error)

22、关闭p参数相应的文件,并释放资源

void pcap_close(pcap_t *p)

23、函数返回pcap_dumper_t类型的指针

pcap_dumper_t *pcap_dump_open(pcap_t *p, const char *file)
  • file是文件名,可以是绝对路径,例如:/home/iona/packet.pcap;

24、用来关闭pcap_dump_open打开的文件

void   pcap_dump_close(pcap_dumper_t *p);
  • 入参是pcap_dump_open返回的指针

25、刷新缓冲区,把捕获的数据包从缓冲区真正拷贝到文件

int  pcap_dump_flush(pcap_dumper_t *p)

26、输出数据到文件

void   pcap_dump(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
  • 与pcap_loop的第二个参数回调函数void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet) 形式完全相同,可以直接当pcap_loop的第二个参数

八、libpcap数据包结构

/*pcap头文件定义的,它包括数据包被嗅探的时间、大小等信息*/

struct pcap_pkthdr {
	struct timeval ts;     	/* 时间戳 */
	bpf_u_int32 caplen;     /* 已捕捉部分的长度 */
	bpf_u_int32 len;        /* 该包的脱机长度 */
};

/* 以太网帧头部 */
struct sniff_ethernet {
	u_char ether_dhost[ETHER_ADDR_LEN]; /* 目的主机的地址 */
	u_char ether_shost[ETHER_ADDR_LEN]; /* 源主机的地址 */
	u_short ether_type; 				/* IP:0x0800;IPV6:0x86DD; ARP:0x0806;RARP:0x8035 */
};

/* 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; 					/* 所使用的协议:1 ICMP;2 IGMP;4 IP;6 TCP;17 UDP;89 OSPF */
	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; /* 紧急服务位 */
};

/* UDP header */
 struct sniff_udp
 {
  	uint16_t sport;        		/* source port */
  	uint16_t dport;        		/* destination port */
  	uint16_t udp_length;
  	uint16_t udp_sum;        	/* checksum */
};

九、使用libpcap编码