使用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
相关产品推荐
相关产品推荐

