【问题标题】:AF_PACKET and EthernetAF_PACKET 和以太网
【发布时间】:2019-01-05 21:26:47
【问题描述】:

我很困惑 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 或其他)。

我不明白的地方:

  • 我不明白 AF_PACKET 是否应该专门用于以太网设备,而不是其他第 2 层技术,例如 Wifi、蓝牙、令牌环、Infiniband 等。

  • 我不明白以太网设备与使用 14 字节以太网标头的第 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 字节以太网标头的数据包?


我的问题:

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

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

如果这两个问题的答案是,那么应用程序如何以编程方式确定在AF_PACKET 套接字上接收数据报时期望的第 2 层标头类型?

【问题讨论】:

  • 来自packet(7)? Packet sockets are used to receive or send raw packets at the device driver (OSI Layer 2) level. They allow the user to implement protocol modules in user space on top of the physical layer. AF_PACKET 中使用的协议必须通过其他方式检测,因为内核不支持(或不知道)使用的协议。 AF_PACKET 可能适用于任何东西,只是内核不知道适用于什么。
  • 那么,为什么环回接口会收到带有 14 字节以太网标头的数据包?
  • 以太网 (IEEE 802.3) 帧头与 Wi-Fi (IEEE 802.11) 帧头非常不同。您看到的是不同协议帧头到以太网帧头的“标准化”。我不确定使用 64 位 MAC 寻址的 IEEE 协议或使用其他寻址或根本不寻址的协议如何发挥作用。
  • 我认为答案是,环回接口使用的协议使用 14 字节以太网标头(如果有的话)。更具体地说,它是implemented,地址长度为6字节,支持TCP/IP协议。
  • @KamilCuk,TCP 和 IP 都不关心数据链路协议或其寻址。 IP 在具有 48 位 MAC 地址的以太网、具有 DLCI 编号的帧中继、具有 VPI/VCI 的 ATM 或没有寻址的 PPP 上运行良好。此外,TCP 可以在 IPv4、IPX 或 IPv6 上运行,而不关心承载它的网络协议。这就是层分离和封装的美妙之处;每一层都不关心其他层使用什么协议。

标签: c linux sockets networking ethernet


【解决方案1】:

警告:这来自于蚕食我为使用 PF_PACKET 的生产软件编写的一些代码,该代码仅用于以太网,因此它可能不完整/不准确。

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

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

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

然后,使用接口名称中的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(netsock,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;
}

【讨论】:

    猜你喜欢
    • 2021-12-06
    • 2012-10-10
    • 2012-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-25
    • 2021-12-07
    相关资源
    最近更新 更多