好的,以下是你提供的代码加上详细的注释。该代码使用 pcap
库来捕获网络数据包,过滤 ICMP 数据包,并打印相关信息。
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h> // 需要包含pcap库
#include <arpa/inet.h> // 用于处理IP地址
#include <time.h>
int main(int argc, const char* argv[])
{
pcap_if_t* alldevs; // 用于存储所有可用的网络接口
pcap_if_t* d; // 当前遍历到的网络接口
int inum; // 用户选择的接口编号
int i = 0; // 遍历计数器
pcap_t* adhandle; // 捕获句柄
int res; // 返回值,用于检测函数执行结果
char errbuf[PCAP_ERRBUF_SIZE]; // 存储错误信息的缓冲区
struct tm* ltime; // 时间结构体,用于格式化时间戳
char timestr[16]; // 存储格式化后的时间字符串
struct pcap_pkthdr* header; // 包头信息指针
const u_char* pkt_data; // 指向数据包内容的指针
time_t local_tv_sec; // 存储本地时间秒数部分
u_int netmask; // 子网掩码
// 定义数据包过滤条件,这里只捕获ICMP协议的数据包(如ping)
char packet_filter[] = "icmp";
struct bpf_program fcode; // 用于存放编译后的过滤程序
/* 获取本机设备列表 */
if (pcap_findalldevs_ex((char*)PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
{
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
exit(1); // 如果获取失败,输出错误信息并退出程序。
}
/* 打印设备列表 */
for (d = alldevs; d; d = d->next)
{
printf("%d. %s", ++i, d->name); // 输出设备名称和序号。
if (d->description)
printf(" (%s)\n", d->description); // 如果有描述,输出描述。
else
printf(" (No description available)\n");
/* 遍历每个接口的地址 */
if (d->addresses != NULL) {
pcap_addr_t *addr = d->addresses;
for (; addr != NULL; addr = addr->next) {
struct sockaddr_in *ip = (struct sockaddr_in *)addr->addr;
struct sockaddr_in *netmask = (struct sockaddr_in *)addr->netmask;
if (ip->sin_family == AF_INET) { // 确保是IPv4地址类型。
printf(" IP Address: %s\n", inet_ntoa(ip->sin_addr)); // 打印IPv4地址。
printf(" Netmask: %s\n", inet_ntoa(netmask->sin_addr)); // 打印子网掩码。
}
}
}
}
if (i == 0)
{
printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
return -1;
}
printf("Enter the interface number (1-%d):", i);
scanf("%d", &inum); // 提示用户输入要监听的接口编号。
if (inum < 1 || inum > i)
{
printf("\nInterface number out of range.\n");
pcap_freealldevs(alldevs); //释放设备列表内存空间。
return -1;
}
/* 跳转到用户选择的适配器 */
for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++);
/* 打开设备进行数据包捕获 */
if ((adhandle = pcap_open(d->name, // 要打开的设备名
65536, // 要捕捉的数据包大小(最大为65535)
/* 确保能捕获到不同数据链路层上的每个数据包的全部内容 */
PCAP_OPENFLAG_PROMISCUOUS, /* 混杂模式,即接收所有经过该设备的数据包 */
1000, /* 超时时间,单位毫秒 */
NULL, /* 不进行远程机器验证 */
errbuf /* 错误缓冲区 */)) == NULL)
{
fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
pcap_freealldevs(alldevs); /*释放设备列表内存空间。*/
return -1;
}
printf("\nlistening on %s %s...\n",d->name, d->description);
/* 设置过滤器以只抓取特定类型的数据包 */
if (d->addresses != NULL)
netmask = ((struct sockaddr_in*)(d->addresses->netmask))->sin_addr.S_un.S_addr;
else
netmask = 0xffffff;
if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) >= 0)
{
if(pcap_setfilter(adhandle,&fcode)<0){
fprintf(stderr,"\nError setting the filter.\n");
pcap_freealldevs(alldevs);
return -1;
}
} else {
fprintf(stderr,"\nError setting the filter.\n");
pcap_freealldevs(alldevs);
return -1;
}
pcap_freealldevs(alldevs); /*释放设备列表内存空间。*/
printf("11111111111111111\n");
/* 循环获取数据包并处理 */
while ((res = pcap_next_ex(adhandle, &header,&pkt_data)) >= 0) {
if(res==0)
continue; /* 超时,不做处理 */
local_tv_sec=header -> ts.tv_sec ;
ltime=localtime(&local_tv_sec);
strftime(timestr,sizeof timestr,"%H:%M:%S",ltime);
printf("%s ,%.6d len:%d\n" ,timestr ,header -> ts.tv_usec ,header -> len );
int len= header -> caplen +1 ;
for(i=1;i<len;i++) {
printf("%.2x ", pkt_data[i-1]); /* 按字节打印出抓取到的数据,以16进制形式显示*/
if((i%16)==0 )
printf("\n");
}
printf("\n-----------------------------------------------------------------\n");
packetHandler(NULL ,header,pkt_data );
}
if(res==-1){
printf("Error reading the packets: % s \ n" ,pcap_geterr(adhandle));
return -1;
}
return 0;
}
注释说明:
- 此代码使用
libpcap
库来查找、列出并选择网络接口,然后在选定接口上以混杂模式捕获数据包。在这个例子中,它仅关注 ICMP 协议(例如 ping)的流量。 - 使用了
pcap_findalldevs_ex
函数获取系统中的所有网络接口,并让用户选择一个进行监控。 - 一旦成功打开选定的网络接口,就设置一个 BPF (Berkeley Packet Filter)以限制捕获的数据类型,只关注 ICMP 数据流,并开始循环读取数据包。
请确保在运行此代码之前已经安装好相关库,如 libpcap 和相应的开发环境。如果有进一步的问题,请随时提问!
内容由零声教学AI助手提供,问题来源于学员提问