对于我为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正在阻塞。
答案 0 :(得分:3)
这是哪个版本的OS X?在Lion之前的版本中,使用BPF的系统上的libpcap的默认缓冲区大小(例如OS X)为32K字节; 1992 called, they want their 4MB workstations and 10Mb Ethernets back。在Lion中,Apple将libpcap更新为1.1.1版;在libpcap 1.1.0中,默认的BPF缓冲区大小增加到512MB(大多数(如果不是所有具有BPF的系统)的最大值)。
如果是Snow Leopard,请尝试切换到新的pcap_create()
/ pcap_activate()
API,并使用pcap_set_buffer_size()
将缓冲区大小设置为512MB。如果这是Lion或更高版本,那将无济于事。
如果您的程序无法跟上平均数据包速率,这将无济于事,但如果有临时突发超过平均值。
如果您的程序无法跟上平均数据包速率,那么,如果您只想要数据包的IP地址,请尝试设置快照长度(您称之为“packetSize”) )大到足以捕获以太网报头和IPv4和IPv6的IP地址的值。对于IPv4,34个字节就足够了(libpcap或BPF可以将其舍入到更大的值),因为这是14个字节的以太网标头+ 20个字节的IPv4标头,没有选项。对于IPv6,它是54字节,用于14字节的以太网报头和40字节的IPv6报头。所以使用packetSize值为54。
请注意,在这种情况下,您应该使用len
的{{1}}字段 NOT caplen
字段来计算数据包长度。 struct pcap_pkthdr
是捕获的数据量,不会超过指定的快照长度; caplen
是“在线”的长度。
此外,您可能希望尝试在同一个线程中运行pcap_loop()和所有处理,并避免为数据包数据分配缓冲区并复制它,以查看 是否加快了处理速度起来。如果你有在不同的线程中执行它们,请确保在完成后释放数据包数据。