对于我为 OSX 构建的一个小工具,我想捕获从某个以太网控制器发送和接收的数据包的长度。
当我获取以太网卡时,我还会获得额外的信息,例如最大数据包大小、链接速度等。
当我启动(我称之为)“trafficMonitor”时,我会这样启动它:
static void initializeTrafficMonitor(const char* interfaceName, int packetSize) {
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t* sessionHandle = pcap_open_live(interfaceName, packetSize, 1, 100, errbuf);
if (sessionHandle == NULL)
{
printf("Error opening session for device %s: %s\n", interfaceName, errbuf);
return;
}
pcap_loop(sessionHandle, -1, packetReceived, NULL);
}
提供interfaceName
的是接口的 BSD 名称,例如en0
. 该packetSize
变量是一个整数,我为该以太网适配器提供最大数据包大小(当时这似乎是合乎逻辑的)。例如,我的 WiFi 适配器的数据包大小是1538
.
我的回调方法被调用packetReceived
,看起来像这样:
void packetReceived(u_char* args, const struct pcap_pkthdr* header, const u_char* packet) {
struct pcap_work_item* item = malloc(sizeof(struct pcap_pkthdr) + header->caplen);
item->header = *header;
memcpy(item->data, packet, header->caplen);
threadpool_add(threadPool, handlePacket, item, 0);
}
我将数据包的所有属性填充到一个新结构中,并启动一个工作线程来分析数据包并处理结果。这是为了不让 pcap 等待,并尝试解决在添加此工作线程方法之前已经存在的问题。
handlePacket
方法是这样的:
void handlePacket(void* args) {
const struct pcap_work_item* workItem = args;
const struct sniff_ethernet* ethernet = (struct sniff_ethernet*)(workItem->data);
u_int size_ip;
const struct sniff_ip* ip = (struct sniff_ip*)(workItem->data + SIZE_ETHERNET);
size_ip = IP_HL(ip) * 4;
if (size_ip < 20) {
return;
}
const u_int16_t type = ether_packet(&workItem->header, workItem->data);
switch (ntohs(type)) {
case ETHERTYPE_IP: {
char sourceIP[INET_ADDRSTRLEN];
char destIP[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &ip->ip_src, sourceIP, sizeof(sourceIP));
inet_ntop(AF_INET, &ip->ip_dst, destIP, sizeof(destIP));
[refToSelf registerPacketTransferFromSource:sourceIP destinationIP:destIP packetLength:workItem->header.caplen packetType:ethernet->ether_type];
break;
}
case ETHERTYPE_IPV6: {
// handle v6
char sourceIP[INET6_ADDRSTRLEN];
char destIP[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &ip->ip_src, sourceIP, sizeof(sourceIP));
inet_ntop(AF_INET6, &ip->ip_dst, destIP, sizeof(destIP));
[refToSelf registerPacketTransferFromSource:sourceIP destinationIP:destIP packetLength:workItem->header.caplen packetType:ethernet->ether_type];
break;
}
}
}
根据以太网数据包的类型,我试图确定它是使用 IPv4 还是 IPv6 地址发送的数据包。在确定之后,我将一些详细信息发送到 ObjectiveC 方法(源 IP 地址、目标 IP 地址和数据包长度)。
我将数据包转换为 tcpdump 网站 (http://www.tcpdump.org/pcap.html) 上解释的结构。
问题是 pcap 似乎跟不上接收/发送的数据包。要么我没有嗅探所有数据包,要么数据包长度错误。
有没有人有任何指针我需要调整我的代码以使 pcap 全部捕获它们或者我有某种问题。
这些方法是从我的 ObjectiveC 应用程序中调用的,并且refToSelf
是对 objC 类的引用。
编辑:我在后台线程中调用 initializeTrafficMonitor,因为 pcap_loop 是阻塞的。