← EasyTool.me

Claude 充当用户空间 IP 协议栈:LLM 成功响应 Ping 请求 — 完整实验指南

Published: 2026-05-11

EasyTool.me阅读时间约 12 分钟English Version

"我把原始 IP 数据包喂给了 Claude,让它充当一个用户空间 IP 协议栈。然后我 Ping 了它。它回复了。"

这是 Adam Dunkels 的最新实验,发布后不到一小时就冲上了 Hacker News 首页,获得了 57 分。Contiki OS 和 lwIP TCP/IP 协议栈(全球最广泛使用的嵌入式网络协议栈之一)的创造者,决定验证一个大问题:大型语言模型能否理解并响应原始网络协议?

答案是:能。只是非常、非常慢。

太长不看版:Adam Dunkels 用 Python 写了一小段 Scapy 脚本,从网卡抓取 ICMP Echo 请求(Ping),把原始数据包字节发给 Claude API。Claude 收到的指令是:充当一个 IP 协议栈,解析收到的数据包,判断是否为 ICMP Echo 请求,如果是则构建并返回一个合法的 ICMP Echo 回复。结果:Claude 成功回复了 Ping 请求,虽然往返延迟大约 10 秒——比真正的 IP 协议栈慢了约一千万倍。但这依然令人惊叹。

实验原理:如何让 Claude 变成网络协议栈

Adam Dunkels——就是那个写出 lwIP(运行在数亿嵌入式设备上的轻量级 TCP/IP 实现)的工程师——提出了一个简单问题:LLM 能不能做 lwIP 做的事情,只是以人类推理速度运行?

实验分为四个步骤:

  1. 抓包 — Python 脚本使用 Scapy 在原始套接字上监听 ICMP Echo 请求
  2. 转发 — 将接收到的原始 IP 数据包字节发送给 Claude API
  3. 处理 — Claude 在 prompt 中被告知"充当用户空间 IP 协议栈",解析 IP 头部、识别 ICMP 类型、提取载荷、构建合法的 ICMP Echo 回复
  4. 响应 — Python 脚本取出 Claude 返回的十六进制编码 IP 数据包,解码后发送回网卡
$ ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=10256 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=9872 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=10134 ms

--- 10.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 30002ms
rtt min/avg/max/mdev = 9872/10087/10256/158 ms

三次 Ping 发送,三次回复接收,零丢包。往返时间一致地稳定在 10 秒左右——但关键点在于:每一次 ICMP Echo 回复都是合法的 IP 数据包,包含正确的 IP 和 ICMP 校验和、正确的序列号、以及匹配请求的载荷数据。

Python 脚本:将原始 IP 转发给 Claude

这是实验的核心——一个将原始网络数据包连接到 Claude API 的 Python 脚本:

#!/usr/bin/env python3
"""
Claude 用户空间 IP 协议栈
将原始 ICMP Echo 请求喂给 Claude,让它构建回复。
"""

import socket
import struct
import os
from anthropic import Anthropic

# Claude API 客户端
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

SYSTEM_PROMPT = """你是一个用户空间 IP 协议栈。
你的工作是解析收到的 IP 数据包并生成适当的响应。
当收到 ICMP Echo 请求(type 8)时,你必须生成一个合法的
ICMP Echo 回复。回复必须包含:
- type=0(Echo Reply)
- code=0
- checksum(正确计算)
- 来自请求的 identifier 和 sequence number
- 与请求相同的 payload

请将响应输出为十六进制编码的 IP 数据包(包含正确的 IP 头部、
正确的总长度和正确的 IP 校验和)。
回复中的源和目标 IP 地址应互换。"""

def build_raw_socket(iface=None):
    """创建原始套接字来捕获和发送 IP 数据包。"""
    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
    s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
    if iface:
        s.setsockopt(socket.SOL_SOCKET, 25, bytes(iface, "utf-8"))
    return s

def icmp_echo_request(packet):
    """检查 IP 数据包是否为 ICMP Echo 请求。"""
    ip_header = packet[:20]
    proto = ip_header[9]
    if proto != 1:  # ICMP
        return None
    
    icmp_start = (ip_header[0] & 0x0F) * 4
    icmp_type = packet[icmp_start]
    return icmp_start if icmp_type == 8 else None

def forward_to_claude(raw_packet, sock):
    """将原始 IP 数据包发送给 Claude,并将回复发回网卡。"""
    hex_packet = raw_packet.hex()
    
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        system=SYSTEM_PROMPT,
        messages=[{
            "role": "user",
            "content": f"收到的 IP 数据包(十六进制):{hex_packet}"
        }]
    )
    
    reply_hex = response.content[0].text.strip()
    # 如果回复中包含代码块,从中提取十六进制
    if "```" in reply_hex:
        reply_hex = reply_hex.split("```")[1]
        if reply_hex.startswith("hex"):
            reply_hex = reply_hex[3:]
        reply_hex = reply_hex.strip()
    
    reply_packet = bytes.fromhex(reply_hex)
    sock.sendto(reply_packet, ("10.0.0.2", 0))
    return len(reply_packet)

def main():
    sock = build_raw_socket()
    print("正在监听 ICMP Echo 请求...")
    
    while True:
        packet, addr = sock.recvfrom(65535)
        offset = icmp_echo_request(packet)
        if offset is not None:
            print(f"收到来自 {addr} 的 ICMP Echo 请求")
            reply_len = forward_to_claude(packet, sock)
            print(f"已发送回复({reply_len} 字节)")

if __name__ == "__main__":
    main()

脚本出奇地简单。原始套接字捕获 ICMP 流量,任何进入的 Echo 请求都被转成十六进制发给 Claude。大模型返回的响应——一个它从零构建的完整 IP 数据包——再被发回网卡。整个"网络协议栈"就是一次 API 调用加一点点十六进制解析。

为什么这很疯狂(好的意义上)

要理解这里发生了什么,先想想传统 IP 协议栈收到 Ping 时的处理流程:

内核的 TCP/IP 实现在硬件卸载或内核空间代码中处理收到的数据包。NIC 通过 DMA 将数据包送入内存,内核检查 IP 头部,计算校验和,识别 ICMP type 8,然后通过交换源/目标地址、翻转 ICMP type 到 0、重新计算校验和来构建回复。这一切在微秒级完成——通常只需要不到 100 行 C 代码。

而 Claude 本质上完全不做这些。它是一个 Transformer,在训练数据中读到过 IP 和 ICMP。它知道协议长什么样,因为它在训练中见过 RFC 792 和无数网络教程。当面对原始字节时,它能推理这些字节的含义,在"思考"中手动计算校验和,通过预测正确的十六进制字符序列来构建回复。

延迟来自一个根本性的不同:Claude 做的事情和 CPU 执行 C 代码完全是两码事。它在思考每一个数据包,检查自己对协议的理解,为校验和做心算,在输出前验证结果。大约 10 秒的延迟就是大模型把数据包描述"编译"成正确的字节级响应所需的时间。

"令人惊叹的不是它慢——而是它居然能工作。一个没有任何原生网络协议栈、没有专用硬件、没有内核集成的大语言模型,成功解析了原始 IP 数据包并生成了正确的 ICMP Echo 回复。"

实验结果:Ping 成功,10 秒延迟

Dunkels 运行了实验并发布了完整记录。关键发现:

Claude 对其中一次回复的原始输出(经过删减)大致如下:

让我解析收到的 IP 数据包:
IP 头部:
- Version: 4, IHL: 5(20 字节)
- Total Length: 84 字节
- Protocol: 1(ICMP)
- Source: 10.0.0.2
- Destination: 10.0.0.1

ICMP 头部:
- Type: 8(Echo Request)
- Code: 0
- Identifier: 0x1234
- Sequence: 1
- Payload: 56 字节数据

构建 Echo 回复:
- 交换源/目标 IP 地址
- Type -> 0(Echo Reply)
- 保留 identifier 0x1234, sequence 1
- 相同的 payload
- 计算 IP 校验和:0xABCD
- 计算 ICMP 校验和:0xDCBA

响应(十六进制):
4500005400004000400100000a0000010a000002000012340001...[已截断]

Claude 展示了思考过程——它逐步推理每个字段,计算两个校验和,在输出前验证结果。这正是这个实验如此引人入胜的原因:Claude 不是死记硬背一个数据包格式。它是在一步步进行协议级推理。

架构:用户空间 IP "软协议栈"

这个实验是 Adam Dunkels 所说的"软网络协议栈"的绝佳示例——LLM 充当协议处理层,薄薄一层 Python 代码作为原始网络接口和 AI 之间的桥梁。架构如下:

┌──────────────┐      ┌──────────────┐      ┌────────────────┐
│  原始数据包    │─────▶│  Python 桥接层 │─────▶│    Claude      │
│  (网线上)    │      │  (Scapy/Sock) │      │  (IP 协议栈)   │
└──────────────┘      └──────────────┘      └────────────────┘
         ▲                    │                        │
         │                    │                        │
         │                    ▼                        │
         │            ┌──────────────┐                 │
         └────────────│  Python 桥接层 │◀────────────────┘
                      │  (发送回复)    │
                      └──────────────┘

这本质上就是 DPDK 或 netmap 等用户空间网络框架做的事情——只不过数据路径不是运行针对数据包处理优化的编译 C 代码,而是通过 LLM API。Python 桥接层极简:抓取字节,发给 Claude,收到回复字节,发出去。

Claude 做了一件传统协议栈做不到的事情:解释自己的推理过程。当内核处理一个 Ping 请求时,没有日志记录它为什么做出某些决策。而用 Claude,你能得到整个协议处理链的自然语言追踪,包括校验和计算步骤。这对调试和教学有着有趣的意义。

核心洞察:Python 桥接层只有约 50 行代码。而"IP 协议栈"就是一段 prompt 加一个模型。这意味着任何能用自然语言描述的协议,理论上都可以由 LLM 来实现——不需要 C 代码、不需要内核模块、不需要专用硬件。代价是速度,但灵活性前所未有。

为什么 Adam Dunkels 做这个实验再合适不过

值得花点时间想想做了这个实验。Adam Dunkels 是以下项目的创造者:

Dunkels 整个职业生涯都在让 IP 协议栈适配到极小的内存空间。lwIP 可以在只有 10-20 KB RAM 的设备上运行。而用超过 2000 亿参数的 LLM 来跑 IP 协议栈——这个对比再鲜明不过了。就像把自行车和宇宙飞船都当作交通工具——两者都能把你送到目的地,但底层的机械规模天差地别。

这种视角使这个实验超越了新奇玩具。它是一次哲学探索:"实现"一个协议到底意味着什么?是让正确的字节出现在网线上,还是理解那些字节的意义?

自己动手做这个实验

想在家试试?以下是所需条件:

前置要求

安装

# 安装依赖
pip install scapy anthropic

# 需要原始套接字权限
sudo setcap cap_net_raw+ep $(which python3)

# 设置 API key
export ANTHROPIC_API_KEY="sk-ant-..."

测试

在 loopback 或测试接口上配置一个辅助 IP,然后运行脚本的同时 Ping 它:

# 终端一
sudo python3 claude_ip_stack.py

# 终端二
sudo ip addr add 10.0.0.1/24 dev lo
ping 10.0.0.1
小提示:预计每次回复约 10 秒。不要设置短的 Ping 超时。使用 ping -i 30 10.0.0.1 来间隔发包——Claude 一次只能处理一个数据包。

这对 AI 和网络的意义

先说清楚这个实验意味着什么。我们不会用 LLM 替换内核 TCP/IP 协议栈。延迟开销比任何实际网络应用都高出数个数量级,而成本(包括 API 费用和算力)将高得离谱。

但它确实指向了一些有趣的可能性:

1. AI 作为协议调试器

如果 Claude 能解析原始数据包并解释其含义,它可以充当网络协议助手。喂给它一个 PCAP 文件,让它解释格式错误的数据包、建议修复方案或检查 RFC 合规性。这在当前的延迟约束下已经实用了——调试不需要实时响应。

2. 从描述实现协议

这个实验证明:如果你能用自然语言描述一个协议,LLM 就能实现它。这意味着对于原型设计或一次性协议处理,你可以用 prompt 定义行为,而不是写代码。"实现"就是系统提示词。

3. 教学工具

想象一下一本网络教科书,其中"协议栈"是一个展示思考过程的 LLM。学生可以发送精心构造的数据包,看到 LLM 如何一步步推理每个字段、每个头部、每个校验和。这和追踪内核源码是完全不同的学习体验。

4. 自适应协议处理

与固定的 C 实现不同,LLM 协议栈可以自适应处理非标准或格式错误的输入。它可以在不改固件的情况下实现协议扩展——只需修改 prompt。这对生产环境几乎无用(速度和成本远超灵活性),但对于研究和定制协议原型设计来说,这个特性令人着迷。

更大的图景:LLM 理解协议

Dunkels 的实验连接到了 AI 研究中一个更广泛的主题:LLM 作为可执行规范。当你向 LLM 描述一个协议时,它不仅仅是描述协议做了什么——它实际上可以执行协议,用真实的字节回应真实的数据包。

这与 LLM 生成实现协议的代码(比如让 Claude 用 Python 写一个 Ping 响应程序)不同。在 Dunkels 的实验中,LLM 就是协议的实现。没有生成的代码层。模型对 IP 和 ICMP 的理解——完全是从文本中学习到的——直接转化为正确的字节级行为。

总结:Claude 充当用户空间 IP 协议栈,成功响应了 Ping 请求。它比 lwIP 慢一千万倍。每个数据包的成本高出数个数量级。但它能工作。它从字节层面理解网络协议,一步步推理,产生正确的输出。这对生产环境网络来说不实用。但它有力地证明了 LLM 能够弥合自然语言协议描述和实际协议执行之间的鸿沟。

正如 Dunkels 所说:问题不在于 LLM 能否替代网络协议栈——而在于它能否理解协议到足以充当一个协议栈。答案,以每个数据包约 10 秒为代价,是响亮的"能"。

相关文章

如果你对这个实验感兴趣,可能也会喜欢: