关于smoltcp路由的讨论

前情提要——

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还要添加上路由的接口。


merge完主线怎么又有新问题了……?

没道理的,怎么可能127.0.0.2不在127.0.0.1~8/8的cidr下?

除非发包网卡有问题,或者说是socket poll的过程有问题

没得打debug好烦啊啊啊啊

UdpSocket发包问题

尝试在os使用udpsocket bind 12580端口,然后在本机使用udpsocket连接无线网卡ip的12580端口,并发送数据给该端口。

表现:os接受到数据,但是无法往本机发数据。

不断调试发现,问题在于如果访问外部ip,不会主动发送arp,导致发包错误。似乎外部ip主动发送数据到dragon时,dragon做出response会发出arp,则后续向该ip可以建立通信;如果是dragon主动第一次向某外部ip发包,则不会触发发送arp?

成功ping通qemu网关历程

  • icmpPacket是通过udp socket通信的,开始测试dragon和宿主机的通信。
  • dragon负责listen宿主机,接受数据包后进行处理然后发回宿主机。
  • 表现:dragon可以正常接受宿主机发送的数据,但是无法发送数据去往宿主机。
  • 发现dragon发包往不在neighbor cache中的ip时,没有发送arp确认mac地址。
  • 偶然尝试先在宿主机往dragon发包,dragon接受到后再运行udp服务程序,此时宿主机可以接受到dragon发来的数据包。
  • 研究arp发送机制的底层代码,感觉是在neighbor cache查找中出现问题。

    返回的是NotFound,不会进行发arp的动作。把virtonet放在列表前面,让virtonet先poll,发现可以发arp了,且位于第二的lo网卡也可以发arp。
  • 既然可以发送arp,dragon自然也就能ping宿主机本地网卡ip了。

真正的可信服的原因sig仍在研究。

2 个赞