为什么pcap_sendpacket在Thunderbolt接口上失败?

时间:2014-06-10 16:11:42

标签: macos osx-mavericks pcap libpcap

在一个多平台项目中,我使用pcap获取所有网络接口的列表,打开每个接口(用户无法选择使用哪些接口)和发送/接收数据包(以太网类型0x88e1 / HomePlugAV)。这在Windows和Mac OS X上运行良好,但有时在Mac OS X上pcap_sendpacketnetworksetup -listallhardwareports列为“硬件端口:Thunderbolt 1”的接口上运行一段时间后失败。错误是:

  

发送:没有可用的缓冲区空间

在机器启动后运行程序时,错误发生需要一些时间。当错误发生一次并且我停止我的程序时,当我重新启动程序而不重新启动计算机时会立即发生错误。

ifconfig -v en9:

en9: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500 index 8
    eflags=80<TXSTART>
    options=60<TSO4,TSO6>
    ether b2:00:1e:94:9b:c1 
    media: autoselect <full-duplex>
    status: inactive
    type: Ethernet
    scheduler: QFQ 

networksetup -listallhardwareports(仅相关部分):

Hardware Port: Thunderbolt 1
Device: en9
Ethernet Address: b2:00:1e:94:9b:c1

测试表明,在OS X 10.9上,接口最初没有启动,但在OS X 10.9.2和10.9.3上,接口启动后启动并运行。

在OS X 10.9上ifconfig最初说:

en5: flags=8822<BROADCAST,SMART,SIMPLEX,MULTICAST> mtu 1500 index 8

{1}后问题行为在OS X 10.9上是相同的。

  1. 为什么ifconfig en5 up在Thunderbolt适配器上失败?
  2. 我的程序在打开之前如何检测到这是一个令人不安的界面?我知道我可以打开界面并尝试发送一个数据包,但我更愿意事先做一个干净的检测。

2 个答案:

答案 0 :(得分:2)

作为一种解决方法,你可以忽略&#34; Thunderbolt 1&#34;接口:

#include <stdio.h>

#include <pcap/pcap.h>

#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SCNetworkConfiguration.h>

const char thunderbolt[] = "Thunderbolt 1";

// Build with -framework CoreFoundation -framework SystemConfiguration
int main(int argc, char * argv[])
{
    // See: https://opensource.apple.com/source/configd/configd-596.13/SystemConfiguration.fproj/SCNetworkInterface.c
    // get Ethernet, Firewire, Thunderbolt, and AirPort interfaces
    CFArrayRef niArrayRef = SCNetworkInterfaceCopyAll();

    // Find out the thunderbolt iface
    char thunderboltInterface[4] = "";
    if(niArrayRef) {
        CFIndex cnt = CFArrayGetCount(niArrayRef);
        for(CFIndex idx = 0; idx < cnt; ++idx) {
            SCNetworkInterfaceRef tSCNetworkInterfaceRef = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(niArrayRef, idx);
            if(tSCNetworkInterfaceRef) {
                CFStringRef BSDName = SCNetworkInterfaceGetBSDName(tSCNetworkInterfaceRef);
                const char * interfaceName = (BSDName == NULL) ? "none" : CFStringGetCStringPtr(BSDName, kCFStringEncodingUTF8);

                CFStringRef localizedDisplayName = SCNetworkInterfaceGetLocalizedDisplayName(tSCNetworkInterfaceRef);
                const char * interfaceType = (localizedDisplayName == NULL) ? "none" : CFStringGetCStringPtr(localizedDisplayName, kCFStringEncodingUTF8);

                printf("%s : %s\n", interfaceName, interfaceType);

                if(strcmp(interfaceType, thunderbolt) == 0) {
                    // Make a copy this time
                    CFStringGetCString(BSDName, thunderboltInterface, sizeof(thunderboltInterface), kCFStringEncodingUTF8);
                }
            }
        }
    }
    printf("%s => %s\n", thunderbolt, thunderboltInterface);

    CFRelease(niArrayRef);
    return 0;
}

答案 1 :(得分:1)

我在猜测

  

在机器启动后运行程序时,错误发生需要一些时间。当错误发生一次并且我停止我的程序时,当我重新启动程序而不重新启动计算机时会立即发生错误。

这里可能发生的是接口处于非活动状态,因此不会传输给它发送的数据包(并释放它们的mbuf), aren'丢弃,但是,只是留在接口的队列中进行传输。最终队列填满或尝试为数据包分配一些资源失败,并且接口的驱动程序返回ENOBUFS错误。

这可以说是OS X的错误。

  

在一个多平台项目中,我使用pcap获取所有网络接口的列表,打开每个接口(用户无法选择使用哪些接口)和发送/接收数据包(以太网类型0x88e1 / HomePlugAV)。

我怀疑你没有发送所有接口;并非所有接口都具有具有以太网类型字段的链路层标头类型 - 例如,lo0没有。

如果您正在构建以太网数据包,则只需要发送具有pcap_datalink()的链路层标头类型(由DLT_EN10MB返回)的接口(“10MB”是历史工件;它指的是除了旧的实验性3MB Xerox以太网之外的所有以太网类型,它们具有不同的链路层头。)

你可能也不想打扰那些在某种意义上不“活跃”的界面(除了“up up”之外的某种意义);遗憾的是,没有与平台无关的API来确定这一点,所以你将不得不在这里使用#ifdefs。这可能会排除数据包堆积不存在的接口,并最终导致ENOBUFS错误。