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; }