1、 使用 pcap 库(libpcap)
libpcap是一个强大的网络抓包库,它提供了跨平台的接口,可以方便地捕获网络数据包。使用libpcap,可以实现各种网络分析、监控和调试工具。
#include <pcap.h>
#include <stdio.h>
int main() {
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *all_devices, *device;
pcap_t *handle;
// 获取所有设备
if (pcap_findalldevs(&all_devices, errbuf) == -1) {
printf("无法获取设备: %s\n", errbuf);
return -1;
}
// 使用第一个设备
device = all_devices;
if (device == NULL) {
printf("找不到设备\n");
return -1;
}
// 打开设备
handle = pcap_open_live(device->name, BUFSIZ, 1, 1000, errbuf);
if (handle == NULL) {
printf("无法打开设备: %s\n", errbuf);
return -1;
}
printf("正在监听设备: %s\n", device->name);
// 抓包循环
struct pcap_pkthdr header;
const u_char *packet;
while ((packet = pcap_next(handle, &header)) != NULL) {
printf("抓到的包大小: %d 字节\n", header.len);
}
pcap_close(handle);
pcap_freealldevs(all_devices);
return 0;
}
2、使用 raw socket (原始套接字)
C语言支持使用原始套接字直接访问网络层的数据包。这是一种低级方式,适合学习和研究网络协议的实现。
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
int main() {
int sockfd;
char buffer[65536];
struct sockaddr saddr;
socklen_t saddr_len = sizeof(saddr);
// 创建原始套接字
sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sockfd < 0) {
perror("创建套接字失败");
return 1;
}
while (1) {
// 接收数据包
ssize_t data_size = recvfrom(sockfd, buffer, sizeof(buffer), 0, &saddr, &saddr_len);
if (data_size < 0) {
perror("数据接收失败");
return 1;
}
printf("捕获到的数据包大小: %ld 字节\n", data_size);
// 进一步解析数据包,例如读取IP头和TCP/UDP头等
}
close(sockfd);
return 0;
}
3、使用 libnetfilter_queue 抓取数据包
libnetfilter_queue是一个基于Linux内核netfilter框架的库,实现用户空间程序拦截、修改和转发网络数据包。相比于libpcap,libnetfilter_queue更适合需要对数据包进行修改或重定向的场景。
#include <stdio.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
static int callback(struct nfq_q_handle *qhandle, struct nfgenmsg *nfmsg,
struct nfq_data *nfa, void *data) {
unsigned char *packet_data;
int packet_id = 0;
struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr(nfa);
if (ph) {
packet_id = ntohl(ph->packet_id);
}
int len = nfq_get_payload(nfa, &packet_data);
printf("捕获到的数据包长度: %d\n", len);
// 返回 NF_ACCEPT 表示允许该包继续流动
return nfq_set_verdict(qhandle, packet_id, NF_ACCEPT, 0, NULL);
}
int main() {
struct nfq_handle *handle = nfq_open();
if (!handle) {
perror("无法打开NFQUEUE句柄");
return 1;
}
struct nfq_q_handle *qhandle = nfq_create_queue(handle, 0, &callback, NULL);
if (!qhandle) {
perror("无法绑定到队列");
nfq_close(handle);
return 1;
}
nfq_set_mode(qhandle, NFQNL_COPY_PACKET, 0xffff);
int fd = nfq_fd(handle);
char buffer[4096];
while (1) {
int len = recv(fd, buffer, sizeof(buffer), 0);
nfq_handle_packet(handle, buffer, len);
}
nfq_destroy_queue(qhandle);
nfq_close(handle);
return 0;
}