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

使用smoltcp连接Raw与TCP套接字时出现RST包问题排查

问题描述

我正在构建一个测试框架,需要将IP数据包列表转换为TCP流以实现便捷模拟。为此尝试修改smoltcp的loopback示例完成初始POC,相关依赖及代码如下:

依赖配置

[dependencies]
env_logger = "0.11.3"
hex = "0.4.3"
log = "0.4.21"
managed = "0.8.0"
smoltcp = "0.11.0"

测试代码

use log::debug;
use managed::ManagedSlice;
use smoltcp::{
    iface::{Config, Interface, SocketSet},
    phy::{Loopback, Medium},
    socket::{raw, tcp},
    time::Instant,
    wire::{IpAddress, IpCidr},
};

fn main() {
    env_logger::init();

    let mut device = Loopback::new(Medium::Ip);

    let mut iface = Interface::new(
        Config::new(smoltcp::wire::HardwareAddress::Ip),
        &mut device,
        Instant::now(),
    );
    iface.update_ip_addrs(|ip_addrs| {
        ip_addrs
            .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8))
            .unwrap();
    });

    let server_socket = tcp::Socket::new(
        tcp::SocketBuffer::new(ManagedSlice::Owned(vec![0; 1024])),
        tcp::SocketBuffer::new(ManagedSlice::Owned(vec![0; 1024])),
    );

    let client_packets = vec![
        hex::decode("450000340000400040063cc27f0000017f000001fde804d277368c50000000008002040070af00000204ffd70303000402000000").unwrap(), // syn
    ];

    let client_socket = raw::Socket::new(
        smoltcp::wire::IpVersion::Ipv4,
        smoltcp::wire::IpProtocol::Tcp,
        raw::PacketBuffer::new(
            ManagedSlice::Owned(vec![raw::PacketMetadata::EMPTY; 1024]),
            ManagedSlice::Owned(vec![0; 1024]),
        ),
        raw::PacketBuffer::new(
            ManagedSlice::Owned(vec![raw::PacketMetadata::EMPTY; 1024]),
            ManagedSlice::Owned(vec![0; 1024]),
        ),
    );

    let mut sockets: [_; 2] = Default::default();
    let mut sockets = SocketSet::new(&mut sockets[..]);
    let server_handle = sockets.add(server_socket);
    let client_handle = sockets.add(client_socket);

    iface.poll(Instant::now(), &mut device, &mut sockets);
    let server_socket = sockets.get_mut::<tcp::Socket>(server_handle);
    debug!("server listening");
    server_socket.listen(1234).unwrap();

    let client_socket = sockets.get_mut::<raw::Socket>(client_handle);
    debug!("client connecting");
    client_socket
        .send_slice(client_packets[0].as_slice())
        .unwrap(); // syn, for connect

    iface.poll(Instant::now(), &mut device, &mut sockets);
}

异常日志

server listening
client connecting
127.0.0.1:65000 => 127.0.0.1:1234 (SYN)
127.0.0.1:1234 => 127.0.0.1:65000 (SYN ACK)
127.0.0.1:65000 => 127.0.0.1:1234 (RST)

请问这是遗漏了什么配置或步骤吗?


问题原因

你用raw socket发送手工构造的SYN包后,服务器返回的SYN-ACK会被smoltcp协议栈接收,但此时系统中没有处于SYN_SENT状态的TCP客户端socket来匹配这个报文。协议栈判定这是无效的连接请求,因此自动发送RST包终止连接。raw socket仅负责发送原始数据包,不会维护TCP连接状态机,无法处理后续的握手流程。

解决方案

优先使用smoltcp内置的TCP客户端socket发起连接,它会自动维护TCP连接状态,正确完成三次握手:

修改后的核心代码如下:

// 替换原有的client_packets和raw socket创建逻辑
let client_socket = tcp::Socket::new(
    tcp::SocketBuffer::new(ManagedSlice::Owned(vec![0; 1024])),
    tcp::SocketBuffer::new(ManagedSlice::Owned(vec![0; 1024])),
);

// ... 其他代码保持不变 ...

// 替换发送raw包的逻辑,改为TCP客户端发起连接
let client_socket = sockets.get_mut::<tcp::Socket>(client_handle);
debug!("client connecting");
client_socket.connect(
    (IpAddress::v4(127, 0, 0, 1), 1234),  // 服务器地址和端口
    (IpAddress::v4(127, 0, 0, 1), 65000) // 客户端本地端口
).unwrap();

修改后,协议栈会自动发送SYN、处理SYN-ACK并回复ACK,完成三次握手,不会出现RST包。

如果确实需要使用原始数据包模拟(比如测试自定义TCP报文),需要手动处理服务器返回的SYN-ACK,构造并发送ACK包,同时要配置raw socket过滤规则,避免协议栈干预该连接。这种方式实现复杂,仅在特殊场景下推荐使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:43:15