#include<stdio.h> // 引入标准输入输出库
#include<pcap.h> // 引入pcap库,用于网络数据包捕获
#include<time.h> // 引入时间处理库
#ifndef ETHERTYPE_ARP
#define ETHERTYPE_ARP 0x0806
#endif
// ARP头部结构定义
struct arphdr {
unsigned short ar_hrd; /* 格式/协议类型 */
unsigned short ar_pro; /* 协议类型 */
unsigned char ar_hln; /* 硬件地址长度 */
unsigned char ar_pln; /* 协议地址长度 */
unsigned short ar_op; /* ARP操作码 */
/* ARP数据字段 */
// 这里可以添加源硬件地址、源协议地址、目的硬件地址和目的协议地址的字段
// 但为了简洁,我们将直接在解析时从数据包中读取
};
struct ether_header
{
u_int8_t ether_dhost[6];//目的MAC地址
u_int8_t ether_shost[6];//源MAC地址
u_int16_t e_type;//以太网类型
};
// 定义回调函数,当捕获到数据包时,pcap库会调用这个函数
void call_back(u_char *argument, const struct pcap_pkthdr *pkt_hdr, const u_char *pktdata)
{
unsigned int packet_length = pkt_hdr->len; // 数据包的长度
long timestamp_seconds = pkt_hdr->ts.tv_sec; // 数据包的时间戳(秒)
long timestamp_microseconds = pkt_hdr->ts.tv_usec; // 数据包的时间戳(微秒)
time_t rawtime = timestamp_seconds; // 将秒转换为time_t类型
struct tm *timeinfo; // 定义一个指向tm结构体的指针,用于存储本地时间
char time[20]; // 定义一个字符数组,用于存储格式化后的时间字符串
// 获取本地时间
timeinfo = localtime(&rawtime);
// 格式化时间字符串
strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", timeinfo);
struct ether_header *eth_header = (struct ether_header*)pktdata;
// 输出以太网目的地址
printf("目的MAC地址: ");
for (int i = 0; i < 6; i++) {
printf("%02x", eth_header->ether_dhost[i]);
if (i < 5) {
printf(":");
}
}
printf("\n");
// 输出以太网源地址
printf("源MAC地址: ");
for (int i = 0; i < 6; i++) {
printf("%02x", eth_header->ether_shost[i]);
if (i < 5) {
printf(":");
}
}
printf("\n");
// 输出以太网类型(注意这里是以网络字节序,可能需要转换为主机字节序)
printf("以太网类型: %04x\n", ntohs(eth_header->e_type));
// 检查以太网类型是否为ARP
if (ntohs(eth_header->e_type) == ETHERTYPE_ARP) {
struct arphdr *arp_header = (struct arphdr*)(pktdata + sizeof(struct ether_header));
// 输出硬件类型(通常是1,代表以太网)
printf("硬件类型: %hu\n", ntohs(arp_header->ar_hrd));
// 输出协议类型(通常是0x0800,代表IPv4)
printf("协议类型: %hu\n", ntohs(arp_header->ar_pro));
// 输出硬件地址长度和协议地址长度
printf("硬件地址长度: %hu\n", arp_header->ar_hln);
printf("协议地址长度: %hu\n", arp_header->ar_pln);
// 输出操作码(1为ARP请求,2为ARP响应)
printf("操作码: %hu\n", ntohs(arp_header->ar_op));
// 输出发送方MAC地址(已经在以太网头部中给出)
printf("发送方MAC地址: ");
for (int i = 0; i < 6; i++) {
printf("%02x", eth_header->ether_shost[i]);
if (i < 5) {
printf(":");
}
}
printf("\n");
// 跳过ARP头部到发送方IP地址
struct in_addr sender_ip;
memcpy(&sender_ip, pktdata + sizeof(struct ether_header) + sizeof(struct arphdr) +
arp_header->ar_hln, sizeof(sender_ip));
printf("发送方IP地址: %s\n", inet_ntoa(sender_ip));
// 输出目的MAC地址(已经在以太网头部中给出)
printf("目的MAC地址: ");
for (int i = 0; i < 6; i++) {
printf("%02x", eth_header->ether_dhost[i]);
if (i < 5) {
printf(":");
}
}
printf("\n");
// 跳过ARP头部到目的IP地址
struct in_addr target_ip;
memcpy(&target_ip, pktdata + sizeof(struct ether_header) + sizeof(struct arphdr) +
2 * arp_header->ar_hln + arp_header->ar_pln, sizeof(target_ip));
printf("目的IP地址: %s\n", inet_ntoa(target_ip));
}
// 输出数据包长度和时间
printf("数据包长度:%u\n", packet_length);
printf("时间:%s.%06ld\n\n", time, timestamp_microseconds);
}
int main()
{
char *device = "ens33"; // 定义网络接口名称
printf("%s\n", device); // 输出网络接口名称
char err_content[PCAP_ERRBUF_SIZE]; // 定义一个字符数组,用于存储错误消息
char* filepath = "./input/bingyanglife.pcap"; // 定义pcapng文件的路径
pcap_t *handle = NULL; // 定义pcap句柄
int method = 0; // 定义方法选择变量,0表示从文件读取,1表示从网络接口捕获
// 根据method的值选择打开网络接口或pcapng文件
if (1 == method)
{
handle = pcap_open_live(device, BUFSIZ, 1, 0, err_content); // 打开网络接口进行实时捕获
}
else
{
handle = pcap_open_offline(filepath, err_content); // 打开pcapng文件进行读取
}
// 检查是否成功打开
if (handle == NULL)
{
printf("无法打开文件:%s", err_content); // 输出错误信息
return 1; // 返回错误代码
}