对于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
或其他)。我不了解的内容
:与其他第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字节以太网报头的数据包?
我的问题
使用AF_PACKET
是否意味着您保证总是接收带有14字节以太网报头的数据包?
如果是的话,这是否还意味着AF_PACKET
仅用于以太网设备(与其他第二层技术(例如Wifi,令牌环,蓝牙,Infiniband等)相反)?
如果这两个问题的答案均为否,那么当在AF_PACKET
套接字上接收数据报时,应用程序如何以编程方式确定期望使用哪种类型的2层报头? / p>
答案 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;
}