You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下Python3使用AF_INET原始套接字捕获自定义IPv4包问题

解决原始套接字接收自定义IPv4包的问题

核心问题原因

你创建的IPPROTO_RAW类型原始套接字在Linux系统中仅用于发送,内核不会将任何IP数据包投递到这类套接字,这就是recvfrom无法捕获数据的根本原因。另外,系统返回"ICMP: Protocol Unreachable"是因为内核收到自定义协议包后,发现没有进程监听对应的协议号,因此主动返回不可达响应。


接收自定义IPv4包的解决方案

方案1:使用对应自定义协议号的SOCK_RAW套接字

针对每个自定义协议号,创建单独的SOCK_RAW套接字,内核会将匹配该协议号的IP包投递到这个套接字。

代码示例

假设你的自定义协议号为253(IANA保留的私有协议范围为240-254,可选用未分配的编号):

import socket

# 自定义协议号(根据实际需求修改)
CUSTOM_PROTOCOL = 253
INTERFACE = "enp3s0f1"
SO_BINDTODEVICE = 25

# 创建接收专用的原始套接字,指定自定义协议号
recv_sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, CUSTOM_PROTOCOL)
# 绑定到指定接口,仅接收该接口的数据包
recv_sock.setsockopt(socket.SOL_SOCKET, SO_BINDTODEVICE, INTERFACE.encode("utf-8"))

# 循环接收数据
while True:
    data, addr = recv_sock.recvfrom(65535)
    # 解析IPv4头(前20字节为基本IP头)
    ip_header = data[:20]
    received_proto = ip_header[9]
    if received_proto == CUSTOM_PROTOCOL:
        payload = data[20:]
        print(f"收到自定义协议包,源地址:{addr},负载:{payload}")

说明

  • 发送时可继续使用原有的IPPROTO_RAW套接字,但需在自行封装的IP头中将protocol字段设置为自定义协议号。
  • 绑定SO_BINDTODEVICE后,该套接字只会接收指定接口的数据包,符合你的需求。

方案2:使用PF_PACKET套接字(接收多种自定义协议)

如果需要在同一个套接字中接收多种自定义协议,可使用PF_PACKET类型的套接字直接捕获以太网帧,自行解析IP头中的协议字段进行筛选。

代码示例

import socket

INTERFACE = "enp3s0f1"
ETH_P_IP = 0x0800  # 仅捕获IPv4以太网帧

# 创建PF_PACKET原始套接字
sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_IP))
sock.bind((INTERFACE, 0))

# 定义需要监听的自定义协议号列表
TARGET_PROTOCOLS = {253, 254}

while True:
    data, addr = sock.recvfrom(65535)
    # 跳过以太网帧头(前14字节),提取IP数据包
    ip_packet = data[14:]
    ip_header = ip_packet[:20]
    received_proto = ip_header[9]
    # 筛选目标自定义协议
    if received_proto in TARGET_PROTOCOLS:
        payload = ip_packet[20:]
        print(f"收到自定义协议{received_proto},源接口:{addr[0]},负载:{payload}")

关键问题解答

  1. 能否在同一套接字上接收多种自定义协议?

    • 使用AF_INET类型的SOCK_RAW套接字不行,每个这类套接字只能绑定一个特定的IP协议号。
    • 使用PF_PACKET套接字可以实现,通过自行解析IP头的协议字段,在一个套接字中筛选并处理多种自定义协议。
  2. 如何仅接收单个接口的数据?

    • 两种方案都可以通过设置SO_BINDTODEVICE套接字选项(方案1),或者绑定接口时指定接口名(方案2的sock.bind((INTERFACE, 0)))来实现仅接收目标接口的数据包。

内容的提问来源于stack exchange,提问作者WakkaTrout

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 20:20:59