sockaddr_ll

awaitO / 2024-10-23 / 原文

在Linux下接收数据链路层的数据包

  1. 原始的方法,即创建一个类型为SOCK_PACKET的socket,该方法很普遍,但是缺乏灵活性;
  2. 新的方法,引入了帧过滤功能和性能上的提升,即创建一个指定协议簇为 PF_PACKET的socket,这需要root权限(类似于创建一个raw socket),并且socket的第三个参数必须指定一个以太网帧类型(Ethernet frame type,可以在头文件<linux/if_ether.h>中找到);使用第二种方法时,socket的第二个参数可以被设置为SOCK_DGRAM,主要区别是当指定SOCK_DGRAM时,获取的数据包是去掉了数据链路层的头(link-layer header),当指定SOCK_RAW时,获取的数据包是一个完整的数据链路层数据包, SOCK_PACKET只返返回完整的数据链路层数据包,示例,接收完整的数据链路层数据包,可以这样写:
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))

sockaddr_ll 定义在头文件 <linux/if_packet.h>

struct sockaddr_ll {
	unsigned short	sll_family; 
	__be16		sll_protocol;
	int		sll_ifindex;
	unsigned short	sll_hatype; // 接收时使用
	unsigned char	sll_pkttype; // 接收时使用
	unsigned char	sll_halen; 
	unsigned char	sll_addr[8]; 
};

sll_family : 一般为AF_PACKET
sll_protocol : 标准以太网协议类型,按网络字节顺序。定义在中。
sll_ifindex : interface索引,0 匹配所有的网络接口卡;
sll_hatype : ARP 硬件地址类型(hardware address type) 定义在中,常用 ARPHRD_ETHER
sll_pkttype : 包含了packet类型。

名称 目标
PACKET_HOST 0 本地主机
PACKET_BROADCAST 1 广播
PACKET_MULTICAST 2 物理层多播地址
PACKET_OTHERHOST 3 其它在混杂模式下被设备捕获的主机
PACKET_OUTGOING 4 本地回环包
... ... ...

sll_addr : 物理层地址
ssl_halen : 物理层地址长度

当发送数据包时,指定 sll_family, sll_addr, sll_halen, sll_ifindex, sll_protocol 就足够了。其它字段设置为0; sll_hatype和 sll_pkttype是在接收数据包时使用的; 如果要bind, 只需要使用 sll_protocol和 sll_ifindex;