AF_PACKET和以太网

时间:2019-01-05 21:26:47

标签: c linux sockets networking ethernet

对于AF_PACKET套接字系列(用于SOCK_RAW套接字)与以太网(IEEE 802.3)的确切联系,我感到非常困惑。

到目前为止,我了解的信息:

  • 我了解OSI模型以及第2层技术如何 以太网适合模型。

  • 我知道AF_PACKET可以与SOCK_RAW套接字一起用于 接收包含14字节以太网报头的数据报,后面跟一些 其他较高层协议标头,例如IPv4,IPv6等, 其次是可选的传输层协议,例如TCP,以及 最后是有效载荷。

  • 我知道您可以将ETH_P_ALLETH_P_IP等标志传递为 socket的协议参数以具有内核过滤器数据包 通过只向您发送包含特定报头的数据包 类型。

  • 我了解使用AF_PACKET系列创建的套接字可以接收或发送sockaddr_ll类型的端点,该端点与特定的MAC地址(EUI-48地址)以及特定的网络相关联界面(例如eth0或其他)。

我不了解的内容

  • 与其他第2层技术(例如Wifi,蓝牙,令牌环,Infiniband等)相反,我不知道AF_PACKET是否应该专门用于以太网设备。

  • 我不了解以太网设备与使用14字节 Ethernet标头 的第2层协议之间的关系。以太网标头为14个字节,可以定义为以下形式:struct eth_hdr { char dest_address[6]; char source_address[6]; uint16_t ethertype; };换句话说,此标头仅用于物理以太网设备吗?看来答案是,因为如果在环回接口上使用AF_PACKET,我仍然会收到包含14字节以太网头的数据包。但是环回不是以太网设备。那么为什么它会接收包含以太网标头的数据包?

  • 如果AF_PACKET可以与非以太网设备一起使用,ETH_P_ALL协议标志是否表示仅接受专门具有14字节以太网报头的数据包?

    < / li>


我的问题

使用AF_PACKET是否意味着您保证总是接收带有14字节以太网报头的数据包?

如果是的话,这是否还意味着AF_PACKET仅用于以太网设备(与其他第二层技术(例如Wifi,令牌环,蓝牙,Infiniband等)相反)?

如果这两个问题的答案均​​为,那么当在AF_PACKET套接字上接收数据报时,应用程序如何以编程方式确定期望使用哪种类型的2层报头? / p>

1 个答案:

答案 0 :(得分:1)

注意事项:这是由于吞噬了我为使用PF_PACKET的生产软件编写的一些代码,而这些代码仅用于以太网,因此 可能不完整/不准确。

您使用的是ETH_P_ALL,它会给您任何东西。但是,有许多ETH_P_*符号可供选择(例如ETH_P_802_3_MIN)。

绑定/选择不仅基于socket调用,而且还基于给定的接口

首先,您需要从eth0中获得的列表中想要的接口 name (例如ifconfig)。

然后,使用接口名称中的ioctl(SIOCGIFINDEX,...)获得接口 index [或者,您可以对其进行硬编码,因为ifconfig将按索引顺序将它们打印出来]。

然后,根据接口索引bind到该接口。

由于您知道接口的类型(例如,选择了eth0或wifi等),此后,您应该能够消化物理层标头,因为您知道它是struct eth_hdr还是不是。

请注意,您还可以使用许多其他SIOCGIF* ioctl来获取接口列表和其他信息,这些信息可能使您可以辨别接口类型[以及因此需要的物理标头]

无论如何,这是我所做的一些示例代码:

int
init(const char *intf)
// intf -- interface name (e.g. eth0, etc. -- whatever comes from ifconfig)
{
    int err;

#if 1
    int styp = SOCK_RAW;
#else
    int styp = SOCK_DGRAM;
#endif

    int netsock = socket(PF_PACKET,styp,htons(ETH_P_ALL));

    struct ifreq ifr;
    memset(&ifr,0,sizeof(ifr));
    strncpy(ifr.ifr_name,intf,sizeof(ifr.ifr_name));

    // get the index number of the interface
    err = ioctl(ntc->ntc_sock,SIOCGIFINDEX,&ifr);
    if (err < 0)
        do_whatever;

    printf("init: IFRIDX ifr_ifindex=%d\n",ifr.ifr_ifindex);
    int ifidx = ifr.ifr_ifindex;

    struct sockaddr_ll addr;
    addr.sll_family = AF_PACKET;
    addr.sll_protocol = htons(ETH_P_ALL);
    addr.sll_ifindex = ifidx;

    err = bind(netsock,(struct sockaddr *) &addr,
        sizeof(struct sockaddr_ll));
    if (err < 0)
        do_whatever;

    return netsock;
}