PACKET - Linux手册页
Linux程序员手册 第7部分
更新日期: 2020-02-09
名称
packet-设备级别的数据包接口
语法
#include <sys/socket.h> #include <linux/if_packet.h> #include <net/ethernet.h> /* the L2 protocols */ packet_socket = socket(AF_PACKET, int socket_type, int protocol);
说明
数据包套接字用于在设备驱动程序(OSI第2层)级别上接收或发送原始数据包。它们允许用户在物理层顶部的用户空间中实现协议模块。
对于包含链接级头的原始数据包,socket_type为SOCK_RAW;对于已删除链接级头的熟数据包,socket_type为SOCK_DGRAM。链接级头信息以sockaddr_ll结构的通用格式提供。 protocol是网络字节顺序的IEEE 802.3协议编号。请参阅包含文件以获取允许的协议列表。当协议设置为htons(ETH_P_ALL)时,将接收所有协议。该协议类型的所有传入数据包在传递给内核中实现的协议之前,都会传递给数据包套接字。
为了创建数据包套接字,进程必须在控制其网络名称空间的用户名称空间中具有CAP_NET_RAW功能。
SOCK_RAW数据包在设备驱动程序之间传递,而数据包数据没有任何更改。当接收到数据包时,该地址仍会被解析并以标准的sockaddr_ll地址结构传递。传输数据包时,用户提供的缓冲区应包含物理层头。然后将该数据包按原样排队到目标地址定义的接口的网络驱动程序中。一些设备驱动程序总是添加其他标头。 SOCK_RAW与Linux 2.0的过时AF_INET / SOCK_PACKET类似但不兼容。
SOCK_DGRAM在更高的级别上运行。在将数据包传递给用户之前,将删除物理头。通过SOCK_DGRAM数据包套接字发送的数据包在排队之前,根据sockaddr_ll目标地址中的信息获得了合适的物理层头。
默认情况下,所有指定协议类型的数据包都会传递到数据包套接字。要仅从特定接口获取数据包,请使用bind(2)在sockaddr_ll结构中指定一个地址,以将数据包套接字绑定到接口。用于绑定的字段是sll_family(应为AF_PACKET),sll_protocol和sll_ifindex。
数据包套接字不支持connect(2)操作。
当将MSG_TRUNC标志传递给recvmsg(2),recv(2)或recvfrom(2)时,即使返回的数据包的实际长度比缓冲区长,也总是返回该数据包的实际长度。
Address types
sockaddr_ll结构是与设备无关的物理层地址。
struct sockaddr_ll { unsigned short sll_family; /* Always AF_PACKET */ unsigned short sll_protocol; /* Physical-layer protocol */ int sll_ifindex; /* Interface number */ unsigned short sll_hatype; /* ARP hardware type */ unsigned char sll_pkttype; /* Packet type */ unsigned char sll_halen; /* Length of address */ unsigned char sll_addr[8]; /* Physical-layer address */ };
此结构的字段如下:
- *
- sll_protocol是包含文件中定义的按网络字节顺序的标准以太网协议类型。它默认为套接字的协议。
- *
- sll_ifindex是接口的接口索引(请参见netdevice(7)); 0匹配任何接口(仅允许绑定)。 sll_hatype是包含文件中定义的ARP类型。
- *
- sll_pkttype包含数据包类型。有效类型包括:发往本地主机的数据包的PACKET_HOST,物理层广播数据包的PACKET_BROADCAST,发送到物理层多播地址的数据包的PACKET_MULTICAST,发往设备的其他主机的数据包的PACKET_OTHERHOST混杂模式中的驱动程序,以及来自本地主机的数据包的PACKET_OUTGOING,该数据包循环回到数据包套接字。这些类型仅对接收有意义。
- *
- sll_addr和sll_halen包含物理层(例如IEEE 802.3)地址及其长度。确切的解释取决于设备。
发送数据包时,只需指定sll_family,sll_addr,sll_halen,sll_ifindex和sll_protocol。其他字段应为0。sll_hatype和sll_pkttype在收到的数据包上设置,以供您参考。
Socket options
通过调用级别为SOL_PACKET的setsockopt(2)来配置数据包套接字选项。
- PACKET_ADD_MEMBERSHIP
- PACKET_DROP_MEMBERSHIP
- 数据包套接字可用于配置物理层多播和混杂模式。 PACKET_ADD_MEMBERSHIP添加绑定,而PACKET_DROP_MEMBERSHIP删除绑定。他们俩都希望使用packet_mreq结构作为参数:
struct packet_mreq { int mr_ifindex; /* interface index */ unsigned short mr_type; /* action */ unsigned short mr_alen; /* address length */ unsigned char mr_address[8]; /* physical-layer address */ };
- mr_ifindex包含应更改其状态的接口的接口索引。 mr_type字段指定要执行的操作。 PACKET_MR_PROMISC启用在共享介质(通常称为"混杂模式")上接收所有数据包,PACKET_MR_MULTICAST将套接字绑定到mr_address和mr_alen中指定的物理层多播组,而PACKET_MR_ALLMULTI将套接字设置为接收所有到达该接口的多播数据包。接口。
- 此外,传统的ioctl SIOCSIFFLAGS,SIOCADDMULTI,SIOCDELMULTI可以用于同一目的。
- PACKET_AUXDATA(since Linux 2.6.21)
- 如果启用了此二进制选项,则数据包套接字将将元数据结构与recvmsg(2)控制字段中的每个数据包一起传递。可以使用cmsg(3)读取结构。定义为
struct tpacket_auxdata { __u32 tp_status; __u32 tp_len; /* packet length */ __u32 tp_snaplen; /* captured length */ __u16 tp_mac; __u16 tp_net; __u16 tp_vlan_tci; __u16 tp_vlan_tpid; /* Since Linux 3.14; earlier, these were unused padding bytes */ };
- PACKET_FANOUT(since Linux 3.1)
- 为了扩展跨线程的处理,数据包套接字可以形成一个扇出组。在这种模式下,每个匹配的数据包仅排队到组中的一个套接字上。套接字通过调用级别为SOL_PACKET和选项PACKET_FANOUT的setsockopt(2)加入扇出组。每个网络名称空间最多可以有65536个独立的组。套接字通过在整数选项值的前16位中编码ID来选择组。加入分组的第一个数据包套接字将隐式创建它。要成功加入现有组,后续的数据包套接字必须具有相同的协议,设备设置,扇出模式和标志(请参见下文)。数据包套接字只能通过关闭套接字才能离开扇出组。当最后一个套接字关闭时,该组将被删除。
- Fanout supports multiple algorithms to spread traffic between sockets,
as follows:
- *
- 默认模式为PACKET_FANOUT_HASH,将数据包从同一流发送到同一套接字,以保持按流排序。对于每个数据包,它通过将数据包流哈希与组中套接字的数量取模来选择一个套接字,其中流哈希是网络层地址和可选的传输层端口字段上的哈希。
- *
- 负载平衡模式PACKET_FANOUT_LB实现循环算法。
- *
- PACKET_FANOUT_CPU根据数据包到达的CPU选择套接字。
- *
- PACKET_FANOUT_ROLLOVER处理单个套接字上的所有数据,当一个被积压时移至下一个。
- *
- PACKET_FANOUT_RND使用伪随机数生成器选择套接字。
- *
- PACKET_FANOUT_QM(从Linux 3.14开始可用)使用记录的接收到的skb的queue_mapping选择套接字。
- 扇出模式可以采用其他选项。 IP分段会导致来自同一流的数据包具有不同的流哈希值。如果设置了标志PACKET_FANOUT_FLAG_DEFRAG,即使在这种情况下,也可以在应用扇出之前对数据包进行碎片整理。扇出模式和选项在整数选项值的后16位中传递。标志PACKET_FANOUT_FLAG_ROLLOVER启用翻转机制作为备份策略:如果原始扇出算法选择了一个积压的套接字,则该数据包将翻转到下一个可用的套接字。
- PACKET_LOSS(with PACKET_TX_RING)
- 当在传输环上遇到格式错误的数据包时,默认值为将其tp_status重置为TP_STATUS_WRONG_FORMAT并立即中止传输。格式错误的数据包会阻塞自身,并随后阻止已排队的数据包被发送。必须修复格式错误,将关联的tp_status重置为TP_STATUS_SEND_REQUEST,然后通过send(2)重新启动传输过程。但是,如果设置了PACKET_LOSS,则将跳过任何格式错误的数据包,将其tp_status重置为TP_STATUS_AVAILABLE,然后继续进行传输过程。
- PACKET_RESERVE(with PACKET_RX_RING)
- 默认情况下,数据包接收环会在元数据结构和对齐填充之后立即写入数据包。此整数选项保留额外的净空。
- PACKET_RX_RING
- 创建用于异步数据包接收的内存映射环形缓冲区。数据包套接字保留应用程序地址空间的连续区域,将其布置在数据包插槽阵列中,然后将数据包(最多tp_snaplen)复制到后续插槽中。每个数据包之前都有类似于tpacket_auxdata的元数据结构。协议字段将从元数据头开始的数据偏移量编码。 tp_net将偏移量存储到网络层。如果数据包套接字的类型为SOCK_DGRAM,则tp_mac是相同的。如果类型为SOCK_RAW,则该字段将偏移量存储到链接层帧。数据包套接字和应用程序通过tp_status字段通信环的头和尾。数据包套接字拥有tp_status等于TP_STATUS_KERNEL的所有插槽。填充插槽后,它将更改插槽的状态以将所有权转让给应用程序。在正常操作期间,新的tp_status值至少将TP_STATUS_USER位置1,以指示已存储接收到的数据包。应用程序完成数据包处理后,通过将tp_status设置为TP_STATUS_KERNEL,将插槽的所有权转移回套接字。
- 数据包套接字实现了数据包环的多种变体。 Linux内核源代码树中的Documentation / networking / packet_mmap.txt中描述了实现细节。
- PACKET_STATISTICS
- 以结构形式检索数据包套接字统计信息
struct tpacket_stats { unsigned int tp_packets; /* Total packet count */ unsigned int tp_drops; /* Dropped packet count */ };
- 接收统计信息将重置内部计数器。当使用变体TPACKET_V3的环时,统计信息结构有所不同。
- PACKET_TIMESTAMP(with PACKET_RX_RING; since Linux 2.6.36)
- 数据包接收环始终将时间戳存储在元数据头中。默认情况下,这是将数据包复制到环网时生成的软件生成的时间戳。此整数选项选择时间戳记的类型。除默认值外,它还支持Linux内核源代码树的Documentation / networking / timestamping.txt中描述的两种硬件格式。
- PACKET_TX_RING(since Linux 2.6.31)
- 创建一个内存映射的环形缓冲区以进行数据包传输。此选项类似于PACKET_RX_RING,并采用相同的参数。应用程序将数据包写入tp_status等于TP_STATUS_AVAILABLE的插槽,并通过将tp_status更改为TP_STATUS_SEND_REQUEST来安排传输时间。当准备好发送数据包时,应用程序将调用send(2)或其变体。此调用的buf和len字段将被忽略。如果使用sendto(2)或sendmsg(2)传递了地址,则该地址将覆盖套接字默认值。成功传输后,套接字将tp_status重置为TP_STATUS_AVAILABLE。除非设置了PACKET_LOSS,否则它会立即中止错误传输。
- PACKET_VERSION(with PACKET_RX_RING; since Linux 2.6.27)
- 默认情况下,PACKET_RX_RING创建变体TPACKET_V1的数据包接收环。要创建另一个变体,请在创建环之前通过设置此整数选项来配置所需的变体。
- PACKET_QDISC_BYPASS(since Linux 3.14)
- 默认情况下,通过数据包套接字发送的数据包会通过内核的qdisc(流量控制)层,这对于绝大多数用例来说都很好。对于使用旨在对网络进行暴力攻击的数据包套接字的流量生成器设备(例如,以类似于pktgen的方式测试负载下的设备),可以通过将此整数选项设置为1来绕过此层。副作用是避免了在qdisc层中进行数据包缓冲,当网络设备的传输队列繁忙时,这将导致丢弃量增加;因此,使用风险自负。
Ioctls
SIOCGSTAMP可用于接收最后收到的数据包的时间戳。参数是一个struct timeval变量。
此外,netdevice(7)和socket(7)中定义的所有标准ioctl在数据包套接字上均有效。
Error handling
数据包套接字不执行任何错误处理,只是将数据包传递到设备驱动程序时发生了错误。他们没有未决错误的概念。
错误说明
- EADDRNOTAVAIL
- 未知的组播组地址已传递。
- EFAULT
- 用户传递了无效的内存地址。
- EINVAL
- 无效的论点。
- EMSGSIZE
- 数据包大于接口MTU。
- ENETDOWN
- 接口不可用。
- ENOBUFS
- 没有足够的内存来分配数据包。
- ENODEV
- 接口地址中指定的未知设备名称或接口索引。
- ENOENT
- 没有收到数据包。
- ENOTCONN
- 没有传递接口地址。
- ENXIO
- 接口地址包含无效的接口索引。
- EPERM
- 用户没有足够的权限来执行此操作。
此外,低级驱动程序可能会生成其他错误。
版本
AF_PACKET是Linux 2.2中的新功能。早期的Linux版本仅支持SOCK_PACKET。
备注
对于可移植程序,建议通过pcap(3)使用AF_PACKET;尽管这仅涵盖AF_PACKET功能的一部分。
SOCK_DGRAM数据包套接字不尝试为IEEE 802.3帧创建或解析IEEE 802.2 LLC头。当将ETH_P_802_3指定为发送内核的协议时,将创建802.3帧并填写长度字段;用户必须提供LLC标头才能获得完全符合要求的数据包。传入的802.3数据包不在DSAP / SSAP协议字段上多路复用;而是将它们以ETH_P_802_2协议(以LLC头作为前缀)提供给用户。因此无法绑定到ETH_P_802_3;而是绑定到ETH_P_802_2并自己进行协议多路复用。发送的默认值是标准以太网DIX封装,其中已填写协议。
数据包套接字不受输入或输出防火墙链的约束。
Compatibility
在Linux 2.0中,获取数据包套接字的唯一方法是通过调用:
套接字(AF_INET,SOCK_PACKET,协议)
仍然支持,但是不建议使用,强烈建议不要这样做。两种方法之间的主要区别是SOCK_PACKET使用旧的sockaddr_pkt结构指定一个接口,该接口不提供物理层独立性。
struct sockaddr_pkt { unsigned short spkt_family; unsigned char spkt_device[14]; unsigned short spkt_protocol; };
spkt_family包含设备类型,spkt_protocol是IEEE 802.3协议类型(如中所定义),而spkt_device是设备名称(以空值结尾的字符串),例如eth0。
该结构已过时,不应在新代码中使用。
BUGS
IEEE 802.2 / 803.3 LLC处理可能被认为是错误。
套接字过滤器未记录。
MSG_TRUNC recvmsg(2)扩展名很丑陋,应替换为控制消息。当前无法通过SOCK_DGRAM获得数据包的原始目标地址。
另外参见
socket(2),pcap(3),capabilities(7),ip(7),raw(7),socket(7)
用于标准IP以太网封装的RFC 894。用于IEEE 802.3 IP封装的RFC 1700。
物理层协议的包含文件。
Linux内核源代码树。 /Documentation/networking/filter.txt描述了如何将Berkeley数据包过滤器应用于数据包套接字。 /tools/testing/selftests/net/psock_tpacket.c包含所有可用版本的PACKET_RX_RING和PACKET_TX_RING的示例源代码。
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。