Sil*_*ler 9 c sockets linux networking ethernet
我对AF_PACKET套接字系列(用于SOCK_RAW套接字)与以太网(IEEE 802.3)的具体关系感到非常困惑。
到目前为止我的理解:
我了解 OSI 模型,以及以太网等第 2 层技术如何融入该模型。
我知道AF_PACKET可以与SOCK_RAW套接字一起使用来接收包含 14 字节以太网标头的数据报,然后是其他一些更高层协议标头,例如 IPv4、IPv6 等,然后是可选的传输层协议,如TCP,最后是有效载荷。
我知道您可以传递诸如ETH_P_ALL或ETH_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 等)?
如果这些问题中的任何一个的答案都是NO,那么应用程序如何以编程方式确定在AF_PACKET套接字上接收数据报时期望的第 2 层报头类型?
警告:这来自蚕食我为使用 的生产软件编写的一些代码,这些代码PF_PACKET仅用于以太网,因此它可能不完整/不准确。
您正在使用ETH_P_ALL它会给予你什么。但是,有许多ETH_P_*符号可供选择(例如ETH_P_802_3_MIN)。
绑定/选择不仅基于socket调用,还基于给定的接口。
首先,您需要从列表中获得所需的接口名称(例如eth0)ifconfig。
然后,使用接口名称获取接口索引ioctl(SIOCGIFINDEX,...)[或者,您可以对其进行硬编码,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;
}
Run Code Online (Sandbox Code Playgroud)