前情提要——
smoltcp路由规则
Iface poll的时候,会对socket集中的每个socket检查是否写入或读出。这里对写入(socket_egress)的情况进行分析。socket_egress会对所有的active的socket进行dispatch ip,也就是加上ip头和链路头。此时会先对目标ip进行分析,查看其是否在自己的neighbor cache里,在就直接封装进链路头然后就可以发送了,如果不在就会发送arp来确认mac。接受到arp rply后就把其装进cache。可是cache的容量就4,是否意味着需要频繁的发送arp。
cache是可以调的,要在编译时加参数,见 GitHub - smoltcp-rs/smoltcp: a smol tcp/ip stack
Configuration
smoltcp has some configuration settings that are set at compile time, affecting sizesand counts of buffers.
They can be set in two ways:
Via Cargo features: enable a feature like <name>-<value>
. name
must be in lowercase and use dashes instead of underscores. For example. iface-max-addr-count-3
. Only a selection of values is available, check Cargo.toml
for the list.
Via environment variables at build time: set the variable named SMOLTCP_<value>
. For example SMOLTCP_IFACE_MAX_ADDR_COUNT=3 cargo build
. You can also set them in the [env]
section of .cargo/config.toml
. Any value can be set, unlike with Cargo features.
Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting with different values, compilation fails.
现在尝试给loopback的iface添加多个ip,但是仅有127.0.0.1会响应Arp、存入cache中,而其他的不会:
RECVFROM不返回,不知道为啥
当绑定同一个ip时是可以接收到的:
绑了另外一个ip后:
let socket = UdpSocket::bind("127.0.0.1:34254")?;
socket.connect("127.0.0.2:34254")?;
let listener = UdpSocket::bind("127.0.0.2:34254")?;
let msg = "Hello, loopback!";
socket.send(msg.as_bytes())?;
let mut buf = [0; 1024];
let (amt, _src) = listener.recv_from(&mut buf)?;
smoltcp如何选择发包网卡
从smoltcp loopback demo产生疑问
观察到 socket set 与 iface 是分离的,在smoltcp中创建的socket set直接在对应iface下poll https://code.dragonos.org.cn/xref/smoltcp-0.9.1/examples/loopback.rs#117 :
117 let mut sockets: [_; 2] = Default::default();
118 let mut sockets = SocketSet::new(&mut sockets[..]);
119 let server_handle = sockets.add(server_socket);
120 let client_handle = sockets.add(client_socket);
121
122 let mut did_listen = false;
123 let mut did_connect = false;
124 let mut done = false;
125 while !done && clock.elapsed() < Instant::from_millis(10_000) {
126 iface.poll(clock.elapsed(), &mut device, &mut sockets);
再参考DragonOS下关于socket接收到信息的回调过程 https://code.dragonos.org.cn/xref/DragonOS-0.1.9/kernel/src/net/net_core.rs#122 :
122 pub fn poll_ifaces() {
123 let guard = NET_DRIVERS.read_irqsave();
124 if guard.len() == 0 {
125 kwarn!("poll_ifaces: No net driver found!");
126 return;
127 }
128 let mut sockets = SOCKET_SET.lock_irqsave();
129 for (_, iface) in guard.iter() {
130 iface.poll(&mut sockets).ok();
131 }
132 let _ = send_event(&sockets);
133 }
好像没什么问题了。主要是,在链路层传输时,譬如loopback网卡ping自己127.0.0.1时,即使已经知道自己ip为127.0.0.1,仍然要进行一次ARP,以获取自己的mac地址到邻居地址缓存(neighbor cache)中,这个行为比较奇怪(但不影响)
不过还有一个问题,linux内核中,路由是有单独一个子系统的,但在smoltcp的设计中,路由和iface系统是在一块的。因此继续沿用smoltcp、netdevice还要添加上路由的接口。
没道理的,怎么可能127.0.0.2不在127.0.0.1~8/8的cidr下?
return None;
}
// Discard packets with non-unicast source addresses.
if !source_protocol_addr.is_unicast() || !source_hardware_addr.is_unicast() {
net_debug!("arp: non-unicast source address");
return None;
}
if !self.in_same_network(&IpAddress::Ipv4(source_protocol_addr)) {
net_debug!("arp: source IP address not in same network as us");
return None;
}
// Fill the ARP cache from any ARP packet aimed at us (both request or response).
// We fill from requests too because if someone is requesting our address they
// are probably going to talk to us, so we avoid having to request their address
// when we later reply to them.
self.neighbor_cache.fill(
source_protocol_addr.into(),
source_hardware_addr.into(),
let mut packet = ArpPacket::new_unchecked(frame.payload_mut());
arp_repr.emit(&mut packet);
})
}
EthernetPacket::Ip(packet) => {
self.dispatch_ip(tx_token, PacketMeta::default(), packet, frag)
}
}
}
fn in_same_network(&self, addr: &IpAddress) -> bool {
self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr))
}
fn route(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
// Send directly.
// note: no need to use `self.is_broadcast()` to check for subnet-local broadcast addrs
// here because `in_same_network` will already return true.
if self.in_same_network(addr) || addr.is_broadcast() {
return Some(*addr);
}
除非发包网卡有问题,或者说是socket poll的过程有问题
UdpSocket发包问题
尝试在os使用udpsocket bind 12580端口,然后在本机使用udpsocket连接无线网卡ip的12580端口,并发送数据给该端口。
表现:os接受到数据,但是无法往本机发数据。
不断调试发现,问题在于如果访问外部ip,不会主动发送arp,导致发包错误。似乎外部ip主动发送数据到dragon时,dragon做出response会发出arp,则后续向该ip可以建立通信;如果是dragon主动第一次向某外部ip发包,则不会触发发送arp?