#![no_std] #![no_main] mod device; mod stack; use device::PacketDevice; use lancer_core::dns::{ DNS_TYPE_A, DNS_TYPE_PTR, build_mdns_a_response, build_mdns_ptr_response, dns_name_eq, dns_skip_name, }; use lancer_core::net_config::NetConfig; use lancer_core::packet_ring::{PacketRingReader, PacketRingWriter}; use lancer_core::sync_unsafe::SyncUnsafe; use lancer_user::syscall; use smoltcp::socket::{dhcpv4, dns, icmp, udp}; use smoltcp::time::Duration; use smoltcp::time::Instant; use smoltcp::wire::{DnsQueryType, IpAddress, IpCidr, IpListenEndpoint, Ipv4Address, Ipv6Address}; use stack::{MAX_SHELL_SOCKETS, MAX_UDP_SOCKETS}; const PACKET_RING_BASE_SLOT: u64 = 64; const PACKET_RING_FRAME_COUNT: u64 = 16; const NOTIF_SLOT: u64 = 3; const DRIVER_NOTIF_SLOT: u64 = 4; const SHELL_RING_BASE_SLOT: u64 = 96; const SHELL_RING_FRAME_COUNT: u64 = 4; const SHELL_NOTIF_SLOT: u64 = 6; const INIT_RING_BASE_SLOT: u64 = 128; const INIT_RING_FRAME_COUNT: u64 = 2; const INIT_NOTIF_SLOT: u64 = 8; const RING_BASE_VADDR: u64 = 0x4000_0000; const RING_HALF_SIZE: usize = 32768; const SHELL_RING_BASE: u64 = 0x5000_0000; const SHELL_RING_HALF: usize = 8192; const INIT_RING_BASE: u64 = 0x6000_0000; const INIT_RING_HALF: usize = 4096; const ECHO_PORT: u16 = 7; const SHELL_PORT: u16 = 22; const MAC: [u8; 6] = [0x52, 0x54, 0x00, 0x12, 0x34, 0x56]; const NOTIFY_BIT_RX: u64 = 1; const NOTIFY_BIT_TX: u64 = 2; const MSG_DATA: u8 = 0; const MSG_CONNECT: u8 = 1; const MSG_DISCONNECT: u8 = 2; const MSG_DNS_QUERY: u8 = 3; const MSG_DNS_RESULT: u8 = 4; const MSG_PING_REQUEST: u8 = 5; const MSG_PING_RESULT: u8 = 6; const MSG_PING_DONE: u8 = 7; const MSG_UDP_BIND: u8 = 8; const MSG_UDP_SEND: u8 = 9; const MSG_UDP_RECV: u8 = 10; const MSG_UDP_CLOSE: u8 = 11; const MSG_UDP_BOUND: u8 = 12; const MSG_DNS_QUERY6: u8 = 13; const MSG_DNS_RESULT6: u8 = 14; const MSG_PING_REQUEST6: u8 = 15; const MSG_IFCONFIG_REQUEST: u8 = 16; const MSG_IFCONFIG_RESULT: u8 = 17; const MSG_ARP_REQUEST: u8 = 18; const MSG_ARP_ENTRY: u8 = 19; const MSG_ARP_DONE: u8 = 20; const MSG_NETSTAT_REQUEST: u8 = 21; const MSG_NETSTAT_ENTRY: u8 = 22; const MSG_NETSTAT_ENTRY_UDP: u8 = 23; const MSG_NETSTAT_DONE: u8 = 24; const MSG_MCAST_JOIN: u8 = 25; const MSG_MCAST_LEAVE: u8 = 26; const MSG_MCAST_RESULT: u8 = 27; const MAX_PENDING_DNS: usize = 4; const MAX_PINGS: usize = 8; const PING_IDENT: u16 = 0x4C41; const PING_TIMEOUT_MS: i64 = 2000; const PING_INTERVAL_MS: i64 = 1000; #[derive(Clone, Copy, PartialEq, Eq)] enum RingSource { Shell, Init, } struct PendingPing { conn_id: u8, target: IpAddress, total: u8, sent: u8, received: u8, next_send_ms: i64, send_times: [i64; MAX_PINGS], replied_mask: u8, timeout_mask: u8, send_failures: u8, ring_source: RingSource, } static mut PENDING_PING: Option = None; struct PendingDns { conn_id: u8, handle: dns::QueryHandle, is_v6: bool, ring_source: RingSource, } static mut PENDING_DNS: [Option; MAX_PENDING_DNS] = [const { None }; MAX_PENDING_DNS]; static mut UDP_RING_SOURCE: [RingSource; MAX_UDP_SOCKETS] = [RingSource::Shell; MAX_UDP_SOCKETS]; fn log_ip(prefix: &str, a: u8, b: u8, c: u8, d: u8) { lancer_user::show!(net, "{}{}.{}.{}.{}", prefix, a, b, c, d); } fn send_router_solicitation(tx: &PacketRingWriter, mac: &[u8; 6]) { let mut frame = [0u8; 70]; frame[0..6].copy_from_slice(&[0x33, 0x33, 0x00, 0x00, 0x00, 0x02]); frame[6..12].copy_from_slice(mac); frame[12] = 0x86; frame[13] = 0xDD; frame[14] = 0x60; let payload_len: u16 = 16; frame[18] = (payload_len >> 8) as u8; frame[19] = payload_len as u8; frame[20] = 58; frame[21] = 255; let src_addr = stack::eui64_link_local_bytes(mac); frame[22..38].copy_from_slice(&src_addr); frame[38..54].copy_from_slice(&[0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02]); frame[54] = 133; frame[55] = 0; frame[56] = 0; frame[57] = 0; frame[58..62].copy_from_slice(&[0; 4]); frame[62] = 1; frame[63] = 1; frame[64..70].copy_from_slice(mac); let cksum = icmpv6_checksum(&frame[22..38], &frame[38..54], &frame[54..70]); frame[56] = (cksum >> 8) as u8; frame[57] = cksum as u8; tx.try_push(&frame[..70]); lancer_user::show!(net, "sent router solicitation"); } fn icmpv6_checksum(src: &[u8], dst: &[u8], icmpv6: &[u8]) -> u16 { let mut sum = 0u32; let add_u16 = |sum: &mut u32, hi: u8, lo: u8| { *sum += ((hi as u32) << 8) | lo as u32; }; (0..8).for_each(|i| { add_u16(&mut sum, src[i * 2], src[i * 2 + 1]); add_u16(&mut sum, dst[i * 2], dst[i * 2 + 1]); }); let upper_len = icmpv6.len() as u32; sum += upper_len; sum += 58u32; let mut i = 0usize; core::iter::from_fn(|| match i + 1 < icmpv6.len() { true => { add_u16(&mut sum, icmpv6[i], icmpv6[i + 1]); i += 2; Some(()) } false => None, }) .count(); if i < icmpv6.len() { add_u16(&mut sum, icmpv6[i], 0); } sum = (sum >> 16) + (sum & 0xFFFF); sum += sum >> 16; !(sum as u16) } static mut SLAAC_CONFIGURED: bool = false; static NET_CONFIG: SyncUnsafe = SyncUnsafe::new(NetConfig::empty()); fn configure_slaac(net: &mut stack::NetStack, slaac: &device::SlaacInfo) { let already = unsafe { (&raw const SLAAC_CONFIGURED).read() }; if already { return; } let iid = eui64_interface_id(&MAC); let mut addr_bytes = [0u8; 16]; addr_bytes.copy_from_slice(&slaac.prefix); let prefix_bits = slaac.prefix_len as usize; (prefix_bits / 8..16).for_each(|i| { let bit_start = match i == prefix_bits / 8 { true => prefix_bits % 8, false => 0, }; match bit_start == 0 { true => addr_bytes[i] = iid[i], false => { let host_mask = 0xFFu8 >> bit_start; addr_bytes[i] = (addr_bytes[i] & !host_mask) | (iid[i] & host_mask); } } }); let ipv6_addr = Ipv6Address::from_octets(addr_bytes); net.iface.update_ip_addrs(|addrs| { let has_global = addrs.iter().any(|a| match a { IpCidr::Ipv6(cidr) => !cidr.address().is_unicast_link_local(), _ => false, }); match has_global { true => {} false => { let _ = addrs.push(IpCidr::Ipv6(smoltcp::wire::Ipv6Cidr::new( ipv6_addr, slaac.prefix_len, ))); } } }); let router = Ipv6Address::from_octets(slaac.router); let _ = net.iface.routes_mut().add_default_ipv6_route(router); unsafe { (&raw mut SLAAC_CONFIGURED).write(true) }; let cfg = unsafe { NET_CONFIG.as_mut_unchecked() }; cfg.set_ipv6_global(addr_bytes, slaac.prefix_len); lancer_user::show!(net, "slaac global ipv6 configured"); } fn eui64_interface_id(mac: &[u8; 6]) -> [u8; 16] { let mut iid = [0u8; 16]; iid[8] = mac[0] ^ 0x02; iid[9] = mac[1]; iid[10] = mac[2]; iid[11] = 0xFF; iid[12] = 0xFE; iid[13] = mac[3]; iid[14] = mac[4]; iid[15] = mac[5]; iid } fn handle_dhcp_event(net: &mut stack::NetStack, ever_configured: &mut bool) { let event = { let dhcp = net.sockets.get_mut::(net.dhcp_handle); dhcp.poll() }; match event { Some(dhcpv4::Event::Configured(config)) => { *ever_configured = true; let addr = config.address; net.iface.update_ip_addrs(|addrs| { match addrs.iter_mut().find(|a| matches!(a, IpCidr::Ipv4(_))) { Some(slot) => *slot = IpCidr::Ipv4(addr), None => { let _ = addrs.push(IpCidr::Ipv4(addr)); } } }); if let Some(router) = config.router { let _ = net.iface.routes_mut().add_default_ipv4_route(router); let octets = router.octets(); log_ip("DHCP: gateway ", octets[0], octets[1], octets[2], octets[3]); } let octets = addr.address().octets(); log_ip( "DHCP: acquired ", octets[0], octets[1], octets[2], octets[3], ); let mut dns_addrs: [IpAddress; 3] = [IpAddress::v4(0, 0, 0, 0); 3]; let dns_count = config.dns_servers.len().min(3); let mut dns_octets = [[0u8; 4]; 3]; (0..dns_count).for_each(|i| { dns_addrs[i] = IpAddress::Ipv4(config.dns_servers[i]); dns_octets[i] = config.dns_servers[i].octets(); let o = dns_octets[i]; log_ip("DHCP: dns ", o[0], o[1], o[2], o[3]); }); let gw_octets = config.router.map(|r| r.octets()); let dns = net.sockets.get_mut::(net.dns_handle); dns.update_servers(&dns_addrs[..dns_count]); let cfg = unsafe { NET_CONFIG.as_mut_unchecked() }; cfg.set_ipv4(octets, addr.prefix_len()); if let Some(gw) = gw_octets { cfg.set_gateway(gw); } cfg.set_dns(&dns_octets, dns_count as u8); } Some(dhcpv4::Event::Deconfigured) if *ever_configured => { lancer_user::show!(net, "dhcp lease lost"); net.iface.update_ip_addrs(|addrs| { if let Some(idx) = (0..addrs.len()).find(|&i| matches!(addrs[i], IpCidr::Ipv4(_))) { addrs.swap_remove(idx); } }); net.iface.routes_mut().remove_default_ipv4_route(); let cfg = unsafe { NET_CONFIG.as_mut_unchecked() }; cfg.clear_ipv4(); } _ => {} } } fn tx_for_source<'a>( source: RingSource, shell_tx: &'a PacketRingWriter, init_tx: Option<&'a PacketRingWriter>, ) -> &'a PacketRingWriter { match source { RingSource::Shell => shell_tx, RingSource::Init => init_tx.expect("init ring required for Init-sourced request"), } } #[allow(clippy::deref_addrof)] fn poll_pending_dns( net: &mut stack::NetStack, shell_tx: &PacketRingWriter, init_tx: Option<&PacketRingWriter>, ) -> (bool, bool) { let pending: &mut [Option; MAX_PENDING_DNS] = unsafe { &mut *(&raw mut PENDING_DNS) }; let mut any_shell = false; let mut any_init = false; pending.iter_mut().for_each(|slot| { let should_clear = match slot { Some(pd) => { let tx = tx_for_source(pd.ring_source, shell_tx, init_tx); let dns = net.sockets.get_mut::(net.dns_handle); match dns.get_query_result(pd.handle) { Ok(addrs) => { match pd.is_v6 { false => { let v4 = addrs.iter().find_map(|a| match a { IpAddress::Ipv4(v4) => Some(v4.octets()), _ => None, }); let mut msg = [0u8; 8]; msg[0] = pd.conn_id; msg[1] = MSG_DNS_RESULT; match v4 { Some(octets) => { msg[2] = 0; msg[3] = octets[0]; msg[4] = octets[1]; msg[5] = octets[2]; msg[6] = octets[3]; let _ = tx.try_push(&msg[..7]); } None => { msg[2] = 2; let _ = tx.try_push(&msg[..3]); } } } true => { let v6 = addrs.iter().find_map(|a| match a { IpAddress::Ipv6(v6) => Some(v6.octets()), _ => None, }); let mut msg = [0u8; 20]; msg[0] = pd.conn_id; msg[1] = MSG_DNS_RESULT6; match v6 { Some(octets) => { msg[2] = 0; msg[3..19].copy_from_slice(&octets); let _ = tx.try_push(&msg[..19]); } None => { msg[2] = 2; let _ = tx.try_push(&msg[..3]); } } } } match pd.ring_source { RingSource::Shell => any_shell = true, RingSource::Init => any_init = true, } true } Err(dns::GetQueryResultError::Pending) => false, Err(dns::GetQueryResultError::Failed) => { let result_type = match pd.is_v6 { true => MSG_DNS_RESULT6, false => MSG_DNS_RESULT, }; let msg = [pd.conn_id, result_type, 2]; let _ = tx.try_push(&msg); match pd.ring_source { RingSource::Shell => any_shell = true, RingSource::Init => any_init = true, } true } } } None => false, }; if should_clear { *slot = None; } }); (any_shell, any_init) } #[allow(clippy::deref_addrof)] fn handle_dns_query( net: &mut stack::NetStack, tx: &PacketRingWriter, conn_id: u8, hostname_bytes: &[u8], is_v6: bool, source: RingSource, ) { let result_type = match is_v6 { false => MSG_DNS_RESULT, true => MSG_DNS_RESULT6, }; let name = match core::str::from_utf8(hostname_bytes) { Ok(s) => s, Err(_) => { let msg = [conn_id, result_type, 2]; let _ = tx.try_push(&msg); return; } }; let query_type = match is_v6 { false => DnsQueryType::A, true => DnsQueryType::Aaaa, }; let dns = net.sockets.get_mut::(net.dns_handle); match dns.start_query(net.iface.context(), name, query_type) { Ok(handle) => { let pending: &mut [Option; MAX_PENDING_DNS] = unsafe { &mut *(&raw mut PENDING_DNS) }; match pending.iter_mut().find(|s| s.is_none()) { Some(slot) => { *slot = Some(PendingDns { conn_id, handle, is_v6, ring_source: source, }); } None => { dns.cancel_query(handle); let msg = [conn_id, result_type, 2]; let _ = tx.try_push(&msg); } } } Err(_) => { let msg = [conn_id, result_type, 2]; let _ = tx.try_push(&msg); } } } fn icmp_checksum(data: &[u8]) -> u16 { let mut sum = 0u32; let mut i = 0usize; core::iter::from_fn(|| match i + 1 < data.len() { true => { sum += ((data[i] as u32) << 8) | (data[i + 1] as u32); i += 2; Some(()) } false => None, }) .count(); if i < data.len() { sum += (data[i] as u32) << 8; } sum = (sum >> 16) + (sum & 0xFFFF); sum += sum >> 16; !(sum as u16) } #[allow(clippy::deref_addrof)] fn ping_send(net: &mut stack::NetStack, now_ms: i64) { let pending: &mut Option = unsafe { &mut *(&raw mut PENDING_PING) }; let pp = match pending.as_mut() { Some(pp) => pp, None => return, }; if pp.sent >= pp.total || now_ms < pp.next_send_ms { return; } let seq = pp.sent as u16; let icmp_type = match pp.target { IpAddress::Ipv4(_) => 8u8, IpAddress::Ipv6(_) => 128u8, }; let mut packet = [0u8; 12]; packet[0] = icmp_type; packet[1] = 0; packet[4] = (PING_IDENT >> 8) as u8; packet[5] = PING_IDENT as u8; packet[6] = (seq >> 8) as u8; packet[7] = seq as u8; let ts = now_ms as u32; packet[8] = (ts >> 24) as u8; packet[9] = (ts >> 16) as u8; packet[10] = (ts >> 8) as u8; packet[11] = ts as u8; match pp.target { IpAddress::Ipv4(_) => { let cksum = icmp_checksum(&packet); packet[2] = (cksum >> 8) as u8; packet[3] = cksum as u8; } IpAddress::Ipv6(_) => {} } let socket = net.sockets.get_mut::(net.icmp_handle); match socket.send_slice(&packet, pp.target) { Ok(()) => { pp.send_failures = 0; pp.send_times[pp.sent as usize] = now_ms; pp.sent += 1; pp.next_send_ms = now_ms + PING_INTERVAL_MS; } Err(_) => { pp.send_failures += 1; match pp.send_failures >= 4 { true => { pp.send_failures = 0; pp.send_times[pp.sent as usize] = 0; pp.sent += 1; pp.next_send_ms = now_ms + PING_INTERVAL_MS; } false => { pp.next_send_ms = now_ms + 200; } } } } } #[allow(clippy::deref_addrof)] fn ping_recv( net: &mut stack::NetStack, shell_tx: &PacketRingWriter, init_tx: Option<&PacketRingWriter>, now_ms: i64, ) -> (bool, bool) { let pending: &mut Option = unsafe { &mut *(&raw mut PENDING_PING) }; let pp = match pending.as_mut() { Some(pp) => pp, None => return (false, false), }; let tx = tx_for_source(pp.ring_source, shell_tx, init_tx); let socket = net.sockets.get_mut::(net.icmp_handle); let mut buf = [0u8; 64]; let mut any_result = false; core::iter::from_fn(|| match socket.recv_slice(&mut buf) { Ok((len, _addr)) if len >= 8 => { let typ = buf[0]; let ident = ((buf[4] as u16) << 8) | buf[5] as u16; let seq = ((buf[6] as u16) << 8) | buf[7] as u16; let reply_type = match pp.target { IpAddress::Ipv4(_) => 0u8, IpAddress::Ipv6(_) => 129u8, }; if typ == reply_type && ident == PING_IDENT && seq < pp.sent as u16 { let seq_idx = seq as u8; if pp.replied_mask & (1 << seq_idx) == 0 { pp.replied_mask |= 1 << seq_idx; pp.received += 1; let rtt = (now_ms - pp.send_times[seq_idx as usize]).max(0) as u16; let msg = [ pp.conn_id, MSG_PING_RESULT, seq_idx, 0, (rtt >> 8) as u8, rtt as u8, ]; let _ = tx.try_push(&msg); any_result = true; } } Some(()) } _ => None, }) .take(4) .count(); match any_result { true => match pp.ring_source { RingSource::Shell => (true, false), RingSource::Init => (false, true), }, false => (false, false), } } #[allow(clippy::deref_addrof)] fn ping_timeout( shell_tx: &PacketRingWriter, init_tx: Option<&PacketRingWriter>, now_ms: i64, ) -> (bool, bool) { let pending: &mut Option = unsafe { &mut *(&raw mut PENDING_PING) }; let pp = match pending.as_mut() { Some(pp) => pp, None => return (false, false), }; let tx = tx_for_source(pp.ring_source, shell_tx, init_tx); let mut any_timeout = false; (0..pp.sent).for_each(|seq| { let mask = 1u8 << seq; if pp.replied_mask & mask != 0 || pp.timeout_mask & mask != 0 { return; } if now_ms - pp.send_times[seq as usize] > PING_TIMEOUT_MS { pp.timeout_mask |= mask; let msg = [pp.conn_id, MSG_PING_RESULT, seq, 1, 0, 0]; let _ = tx.try_push(&msg); any_timeout = true; } }); match any_timeout { true => match pp.ring_source { RingSource::Shell => (true, false), RingSource::Init => (false, true), }, false => (false, false), } } #[allow(clippy::deref_addrof)] fn ping_check_done( shell_tx: &PacketRingWriter, init_tx: Option<&PacketRingWriter>, ) -> (bool, bool) { let pending: &mut Option = unsafe { &mut *(&raw mut PENDING_PING) }; let pp = match pending.as_ref() { Some(pp) => pp, None => return (false, false), }; let all_accounted = pp.sent == pp.total && (pp.replied_mask | pp.timeout_mask) == ((1u16 << pp.total) - 1) as u8; match all_accounted { true => { let source = pp.ring_source; let tx = tx_for_source(source, shell_tx, init_tx); let msg = [pp.conn_id, MSG_PING_DONE, pp.received, pp.total]; let _ = tx.try_push(&msg); *pending = None; match source { RingSource::Shell => (true, false), RingSource::Init => (false, true), } } false => (false, false), } } #[allow(clippy::deref_addrof)] fn handle_ping_request( tx: &PacketRingWriter, conn_id: u8, data: &[u8], now_ms: i64, source: RingSource, ) { let pending: &mut Option = unsafe { &mut *(&raw mut PENDING_PING) }; if pending.is_some() { let msg = [conn_id, MSG_PING_DONE, 0, 0]; let _ = tx.try_push(&msg); return; } if data.len() < 5 { let msg = [conn_id, MSG_PING_DONE, 0, 0]; let _ = tx.try_push(&msg); return; } let target = IpAddress::Ipv4(Ipv4Address::new(data[0], data[1], data[2], data[3])); let count = data[4].clamp(1, MAX_PINGS as u8); *pending = Some(PendingPing { conn_id, target, total: count, sent: 0, received: 0, next_send_ms: now_ms, send_times: [0i64; MAX_PINGS], replied_mask: 0, timeout_mask: 0, send_failures: 0, ring_source: source, }); } #[allow(clippy::deref_addrof)] fn handle_ping_request6( tx: &PacketRingWriter, conn_id: u8, data: &[u8], now_ms: i64, source: RingSource, ) { let pending: &mut Option = unsafe { &mut *(&raw mut PENDING_PING) }; if pending.is_some() { let msg = [conn_id, MSG_PING_DONE, 0, 0]; let _ = tx.try_push(&msg); return; } if data.len() < 17 { let msg = [conn_id, MSG_PING_DONE, 0, 0]; let _ = tx.try_push(&msg); return; } let mut addr_bytes = [0u8; 16]; addr_bytes.copy_from_slice(&data[..16]); let target = IpAddress::Ipv6(Ipv6Address::from_octets(addr_bytes)); let count = data[16].clamp(1, MAX_PINGS as u8); *pending = Some(PendingPing { conn_id, target, total: count, sent: 0, received: 0, next_send_ms: now_ms, send_times: [0i64; MAX_PINGS], replied_mask: 0, timeout_mask: 0, send_failures: 0, ring_source: source, }); } #[allow(clippy::deref_addrof)] fn handle_udp_bind( net: &mut stack::NetStack, tx: &PacketRingWriter, conn_id: u8, data: &[u8], source: RingSource, ) { if data.len() < 2 || (conn_id as usize) >= MAX_UDP_SOCKETS { let msg = [conn_id, MSG_UDP_BOUND, 1]; let _ = tx.try_push(&msg); return; } let udp_sources: &mut [RingSource; MAX_UDP_SOCKETS] = unsafe { &mut *(&raw mut UDP_RING_SOURCE) }; udp_sources[conn_id as usize] = source; let port = ((data[0] as u16) << 8) | data[1] as u16; let socket = net .sockets .get_mut::(net.udp_handles[conn_id as usize]); socket.close(); match socket.bind(IpListenEndpoint { addr: None, port }) { Ok(()) => { lancer_user::show!(net, "udp bind ok"); let msg = [conn_id, MSG_UDP_BOUND, 0]; let _ = tx.try_push(&msg); } Err(_) => { let msg = [conn_id, MSG_UDP_BOUND, 1]; let _ = tx.try_push(&msg); } } } #[allow(clippy::deref_addrof)] fn handle_udp_send(net: &mut stack::NetStack, conn_id: u8, data: &[u8], source: RingSource) { if data.len() < 7 || (conn_id as usize) >= MAX_UDP_SOCKETS { return; } let udp_sources: &mut [RingSource; MAX_UDP_SOCKETS] = unsafe { &mut *(&raw mut UDP_RING_SOURCE) }; udp_sources[conn_id as usize] = source; let ip = Ipv4Address::new(data[0], data[1], data[2], data[3]); let port = ((data[4] as u16) << 8) | data[5] as u16; let payload = &data[6..]; let socket = net .sockets .get_mut::(net.udp_handles[conn_id as usize]); if !socket.is_open() { let ephemeral = 49152 + conn_id as u16; let _ = socket.bind(IpListenEndpoint { addr: None, port: ephemeral, }); } let _ = socket.send_slice(payload, (IpAddress::Ipv4(ip), port)); } fn handle_udp_close(net: &mut stack::NetStack, conn_id: u8) { if (conn_id as usize) >= MAX_UDP_SOCKETS { return; } let socket = net .sockets .get_mut::(net.udp_handles[conn_id as usize]); socket.close(); } #[allow(clippy::deref_addrof)] fn poll_udp_recv( net: &mut stack::NetStack, shell_tx: &PacketRingWriter, init_tx: Option<&PacketRingWriter>, ) -> (bool, bool) { let udp_sources: &[RingSource; MAX_UDP_SOCKETS] = unsafe { &*(&raw const UDP_RING_SOURCE) }; let mut any_shell = false; let mut any_init = false; (0..MAX_UDP_SOCKETS).for_each(|i| { let socket = net.sockets.get_mut::(net.udp_handles[i]); if socket.is_open() && socket.can_recv() { lancer_user::show!(net, "udp socket has data"); } let tx = tx_for_source(udp_sources[i], shell_tx, init_tx); let source = udp_sources[i]; let mut buf = [0u8; 128]; core::iter::from_fn(|| match socket.can_recv() { true => match socket.recv_slice(&mut buf[8..126]) { Ok((len, endpoint)) => { match endpoint.endpoint.addr { IpAddress::Ipv4(ip) => { let o = ip.octets(); let port = endpoint.endpoint.port; buf[0] = i as u8; buf[1] = MSG_UDP_RECV; buf[2] = o[0]; buf[3] = o[1]; buf[4] = o[2]; buf[5] = o[3]; buf[6] = (port >> 8) as u8; buf[7] = port as u8; let _ = tx.try_push(&buf[..8 + len]); match source { RingSource::Shell => any_shell = true, RingSource::Init => any_init = true, } } IpAddress::Ipv6(_) => {} } Some(()) } Err(_) => None, }, false => None, }) .take(4) .count(); }); (any_shell, any_init) } fn handle_ifconfig_request(tx: &PacketRingWriter, conn_id: u8) { let cfg: &NetConfig = unsafe { NET_CONFIG.as_ref_unchecked() }; let mut msg = [0u8; 64]; msg[0] = conn_id; msg[1] = MSG_IFCONFIG_RESULT; let n = cfg.serialize(&mut msg[2..]); let _ = tx.try_push(&msg[..2 + n]); } fn handle_arp_request(tx: &PacketRingWriter, conn_id: u8, now_ms: i64) { let table = device::arp_table(); table.iter().filter(|e| e.valid).for_each(|entry| { let age_secs = ((now_ms - entry.timestamp_ms) / 1000).max(0) as u16; let mut msg = [0u8; 14]; msg[0] = conn_id; msg[1] = MSG_ARP_ENTRY; msg[2..6].copy_from_slice(&entry.ip); msg[6..12].copy_from_slice(&entry.mac); msg[12] = (age_secs >> 8) as u8; msg[13] = age_secs as u8; let _ = tx.try_push(&msg); }); let done = [conn_id, MSG_ARP_DONE]; let _ = tx.try_push(&done); } fn handle_netstat_request(net: &mut stack::NetStack, tx: &PacketRingWriter, conn_id: u8) { let encode_tcp = |socket: &smoltcp::socket::tcp::Socket, msg: &mut [u8; 20], conn: u8| -> usize { msg[0] = conn; msg[1] = MSG_NETSTAT_ENTRY; msg[2] = 6; msg[3] = match socket.state() { smoltcp::socket::tcp::State::Closed => 0, smoltcp::socket::tcp::State::Listen => 1, smoltcp::socket::tcp::State::SynSent => 2, smoltcp::socket::tcp::State::SynReceived => 3, smoltcp::socket::tcp::State::Established => 4, smoltcp::socket::tcp::State::FinWait1 => 5, smoltcp::socket::tcp::State::FinWait2 => 6, smoltcp::socket::tcp::State::CloseWait => 7, smoltcp::socket::tcp::State::Closing => 8, smoltcp::socket::tcp::State::LastAck => 9, smoltcp::socket::tcp::State::TimeWait => 10, }; let (lip, lport) = match socket.local_endpoint() { Some(ep) => { let ip = match ep.addr { IpAddress::Ipv4(a) => a.octets(), _ => [0; 4], }; (ip, ep.port) } None => { let listen = socket.listen_endpoint(); let ip = match listen.addr { Some(IpAddress::Ipv4(a)) => a.octets(), _ => [0; 4], }; (ip, listen.port) } }; msg[4..8].copy_from_slice(&lip); msg[8] = (lport >> 8) as u8; msg[9] = lport as u8; let remote = socket.remote_endpoint(); let rip = match remote { Some(ep) => match ep.addr { IpAddress::Ipv4(a) => a.octets(), _ => [0; 4], }, None => [0; 4], }; let rport = remote.map_or(0u16, |ep| ep.port); msg[10..14].copy_from_slice(&rip); msg[14] = (rport >> 8) as u8; msg[15] = rport as u8; let sq = socket.send_queue() as u16; let rq = socket.recv_queue() as u16; msg[16] = (sq >> 8) as u8; msg[17] = sq as u8; msg[18] = (rq >> 8) as u8; msg[19] = rq as u8; 20 }; let mut msg = [0u8; 20]; let echo_sock = net .sockets .get_mut::(net.tcp_handle); let len = encode_tcp(echo_sock, &mut msg, conn_id); let _ = tx.try_push(&msg[..len]); net.shell_handles.iter().for_each(|&h| { let sock = net.sockets.get_mut::(h); let len = encode_tcp(sock, &mut msg, conn_id); let _ = tx.try_push(&msg[..len]); }); net.udp_handles.iter().for_each(|&h| { let sock = net.sockets.get_mut::(h); let port = sock.endpoint().port; if port != 0 { let rq = sock.payload_recv_capacity() as u16; let mut umsg = [0u8; 6]; umsg[0] = conn_id; umsg[1] = MSG_NETSTAT_ENTRY_UDP; umsg[2] = (port >> 8) as u8; umsg[3] = port as u8; umsg[4] = (rq >> 8) as u8; umsg[5] = rq as u8; let _ = tx.try_push(&umsg); } }); let done = [conn_id, MSG_NETSTAT_DONE]; let _ = tx.try_push(&done); } fn is_multicast_addr(octet: u8) -> bool { (224..=239).contains(&octet) } fn handle_mcast_join(net: &mut stack::NetStack, tx: &PacketRingWriter, conn_id: u8, data: &[u8]) { let status = match data.len() >= 4 && is_multicast_addr(data[0]) { true => { let addr = Ipv4Address::new(data[0], data[1], data[2], data[3]); match net.iface.join_multicast_group(addr) { Ok(()) => 0u8, Err(_) => 1u8, } } false => 1u8, }; let msg = [conn_id, MSG_MCAST_RESULT, status]; let _ = tx.try_push(&msg); } fn handle_mcast_leave(net: &mut stack::NetStack, tx: &PacketRingWriter, conn_id: u8, data: &[u8]) { let status = match data.len() >= 4 && is_multicast_addr(data[0]) { true => { let addr = Ipv4Address::new(data[0], data[1], data[2], data[3]); match net.iface.leave_multicast_group(addr) { Ok(()) => 0u8, Err(_) => 1u8, } } false => 1u8, }; let msg = [conn_id, MSG_MCAST_RESULT, status]; let _ = tx.try_push(&msg); } #[derive(Clone, Copy, PartialEq, Eq)] enum MdnsName { LancerLocal, LancerTcpLocal, Unknown, } const MDNS_MULTICAST_DEST: (IpAddress, u16) = (IpAddress::Ipv4(Ipv4Address::new(224, 0, 0, 251)), 5353); fn poll_mdns(net: &mut stack::NetStack) { let cfg: &NetConfig = unsafe { NET_CONFIG.as_ref_unchecked() }; let ip = cfg.ipv4; if ip == [0, 0, 0, 0] { return; } let mdns_handle = net.mdns_handle; let socket = net.sockets.get_mut::(mdns_handle); let mut recv_buf = [0u8; 1500]; let mut resp_buf = [0u8; 1500]; core::iter::from_fn(|| match socket.can_recv() { true => match socket.recv_slice(&mut recv_buf) { Ok((len, endpoint)) if len >= 12 => { let flags = ((recv_buf[2] as u16) << 8) | recv_buf[3] as u16; let is_query = flags & 0x8000 == 0; let qdcount = ((recv_buf[4] as u16) << 8) | recv_buf[5] as u16; if is_query && qdcount >= 1 { let mut qpos = 12usize; let wire = &recv_buf[..len]; (0..qdcount.min(16)).for_each(|_| { if qpos >= len { return; } let lancer_local: &[&[u8]] = &[b"lancer", b"local"]; let svc_local: &[&[u8]] = &[b"_lancer", b"_tcp", b"local"]; let (name, after_name) = match dns_name_eq(wire, qpos, lancer_local) { Some(end) => (MdnsName::LancerLocal, end), None => match dns_name_eq(wire, qpos, svc_local) { Some(end) => (MdnsName::LancerTcpLocal, end), None => match dns_skip_name(wire, qpos) { Some(end) => (MdnsName::Unknown, end), None => { qpos = len; return; } }, }, }; match after_name + 4 <= len { true => { let qtype = ((recv_buf[after_name] as u16) << 8) | recv_buf[after_name + 1] as u16; let qclass = ((recv_buf[after_name + 2] as u16) << 8) | recv_buf[after_name + 3] as u16; let qu_bit = qclass & 0x8000 != 0; qpos = after_name + 4; let resp_len = match (name, qtype) { (MdnsName::LancerLocal, DNS_TYPE_A) | (MdnsName::LancerLocal, 255) => { build_mdns_a_response(&mut resp_buf, ip).unwrap_or(0) } (MdnsName::LancerTcpLocal, DNS_TYPE_PTR) | (MdnsName::LancerTcpLocal, 255) => { build_mdns_ptr_response(&mut resp_buf, ip, SHELL_PORT) .unwrap_or(0) } _ => 0, }; if resp_len > 0 { let dest = match qu_bit { true => (endpoint.endpoint.addr, endpoint.endpoint.port), false => MDNS_MULTICAST_DEST, }; let _ = socket.send_slice(&resp_buf[..resp_len], dest); } } false => { qpos = len; } } }); } Some(()) } _ => None, }, false => None, }) .take(4) .count(); } #[unsafe(no_mangle)] pub extern "C" fn lancer_main() -> ! { lancer_user::show!(net, "starting"); if !(0..PACKET_RING_FRAME_COUNT).all(|i| syscall::frame_map(PACKET_RING_BASE_SLOT + i, RING_BASE_VADDR + i * 4096, 1) >= 0) { lancer_user::show!(net, error, "packet ring frame_map failed"); syscall::exit(); } let rx_ring_base = RING_BASE_VADDR as *mut u8; let tx_ring_base = (RING_BASE_VADDR + RING_HALF_SIZE as u64) as *mut u8; let rx_reader = unsafe { PacketRingReader::attach(rx_ring_base, RING_HALF_SIZE) }; let tx_writer = unsafe { PacketRingWriter::init(tx_ring_base, RING_HALF_SIZE, 2048) }; let mut dev = PacketDevice { rx: rx_reader, tx: tx_writer, }; if !(0..SHELL_RING_FRAME_COUNT).all(|i| syscall::frame_map(SHELL_RING_BASE_SLOT + i, SHELL_RING_BASE + i * 4096, 1) >= 0) { lancer_user::show!(net, error, "shell ring frame_map failed"); syscall::exit(); } let shell_tx_base = SHELL_RING_BASE as *mut u8; let shell_rx_base = (SHELL_RING_BASE + SHELL_RING_HALF as u64) as *mut u8; let shell_tx = unsafe { PacketRingWriter::init(shell_tx_base, SHELL_RING_HALF, 128) }; let _ = unsafe { PacketRingWriter::init(shell_rx_base, SHELL_RING_HALF, 128) }; let shell_rx = unsafe { PacketRingReader::attach(shell_rx_base, SHELL_RING_HALF) }; lancer_user::show!(net, "shell ring mapped"); let has_init_ring = (0..INIT_RING_FRAME_COUNT).all(|i| syscall::frame_map(INIT_RING_BASE_SLOT + i, INIT_RING_BASE + i * 4096, 1) >= 0); let (init_tx, init_rx) = match has_init_ring { true => { let itx_base = INIT_RING_BASE as *mut u8; let irx_base = (INIT_RING_BASE + INIT_RING_HALF as u64) as *mut u8; let itx = unsafe { PacketRingWriter::init(itx_base, INIT_RING_HALF, 128) }; let _ = unsafe { PacketRingWriter::init(irx_base, INIT_RING_HALF, 128) }; let irx = unsafe { PacketRingReader::attach(irx_base, INIT_RING_HALF) }; lancer_user::show!(net, "init ring mapped"); (Some(itx), Some(irx)) } false => { lancer_user::show!(net, "init ring not available"); (None, None) } }; let now_ms = match syscall::clock_monotonic_ms() { ms if ms >= 0 => ms, _ => 0, }; let now = Instant::from_millis(now_ms); let mut net = stack::create(&mut dev, MAC, now); if stack::MDNS_JOIN_FAILED.load(core::sync::atomic::Ordering::Relaxed) { lancer_user::show!(net, warn, "failed to join mdns multicast group"); } let cfg = unsafe { NET_CONFIG.as_mut_unchecked() }; cfg.set_mac(MAC); cfg.set_ipv6_link_local(stack::eui64_link_local_bytes(&MAC)); lancer_user::show!(net, "interface up, ipv6 link-local + awaiting dhcpv4"); send_router_solicitation(&dev.tx, &MAC); { let socket = net .sockets .get_mut::(net.tcp_handle); match socket.listen(ECHO_PORT) { Ok(()) => {} Err(_) => { lancer_user::show!(net, warn, "echo listen failed"); } } } lancer_user::show!(net, "tcp echo listening on :7"); let shell_handles = net.shell_handles; { let socket = net .sockets .get_mut::(shell_handles[0]); match socket.listen(SHELL_PORT) { Ok(()) => {} Err(_) => { lancer_user::show!(net, warn, "shell listen failed"); } } } lancer_user::show!(net, "tcp shell listening on :22"); let mut echo_buf = [0u8; 1024]; let mut shell_ring_buf = [0u8; 128]; let mut init_ring_buf = [0u8; 128]; let mut pkt_buf = [0u8; 128]; let mut shell_connected = [false; MAX_SHELL_SOCKETS]; let mut dhcp_configured = false; let init_tx_ref = init_tx.as_ref(); loop { let now_ms = syscall::clock_monotonic_ms().max(0); let now = Instant::from_millis(now_ms); net.iface.poll(now, &mut dev, &mut net.sockets); handle_dhcp_event(&mut net, &mut dhcp_configured); if let Some(slaac) = device::take_slaac_prefix() { configure_slaac(&mut net, &slaac); } poll_mdns(&mut net); let (dns_shell, dns_init) = poll_pending_dns(&mut net, &shell_tx, init_tx_ref); ping_send(&mut net, now_ms); let (pr_shell, pr_init) = ping_recv(&mut net, &shell_tx, init_tx_ref, now_ms); let (pt_shell, pt_init) = ping_timeout(&shell_tx, init_tx_ref, now_ms); let (pd_shell, pd_init) = ping_check_done(&shell_tx, init_tx_ref); let (ur_shell, ur_init) = poll_udp_recv(&mut net, &shell_tx, init_tx_ref); { let socket = net .sockets .get_mut::(net.tcp_handle); if socket.may_recv() && socket.may_send() { match socket.recv_slice(&mut echo_buf) { Ok(n) if n > 0 => { let _ = socket.send_slice(&echo_buf[..n]); } _ => {} } } let needs_relisten = match socket.state() { smoltcp::socket::tcp::State::CloseWait => { socket.close(); false } smoltcp::socket::tcp::State::TimeWait | smoltcp::socket::tcp::State::LastAck | smoltcp::socket::tcp::State::Closing | smoltcp::socket::tcp::State::FinWait1 | smoltcp::socket::tcp::State::FinWait2 | smoltcp::socket::tcp::State::Closed => true, _ => false, }; if needs_relisten { socket.abort(); let _ = socket.listen(ECHO_PORT); } } let mut any_shell_notify = dns_shell || pr_shell || pt_shell || pd_shell || ur_shell; let mut any_init_notify = dns_init || pr_init || pt_init || pd_init || ur_init; (0..MAX_SHELL_SOCKETS).for_each(|i| { let socket = net .sockets .get_mut::(shell_handles[i]); let fully_open = socket.may_send() && socket.may_recv(); if fully_open && !shell_connected[i] { shell_connected[i] = true; let ctrl = [i as u8, MSG_CONNECT]; if shell_tx.try_push(&ctrl) { any_shell_notify = true; } } if !fully_open && shell_connected[i] { shell_connected[i] = false; let ctrl = [i as u8, MSG_DISCONNECT]; if shell_tx.try_push(&ctrl) { any_shell_notify = true; } } if socket.state() == smoltcp::socket::tcp::State::CloseWait { socket.close(); } if shell_connected[i] && socket.may_recv() { let pushed = core::iter::from_fn(|| match shell_tx.has_space() && socket.may_recv() { true => match socket.recv_slice(&mut pkt_buf[2..126]) { Ok(n) if n > 0 => { pkt_buf[0] = i as u8; pkt_buf[1] = MSG_DATA; shell_tx.try_push(&pkt_buf[..2 + n]).then_some(()) } _ => None, }, false => None, }) .take(8) .count(); if pushed > 0 { any_shell_notify = true; } } }); let any_listening = (0..MAX_SHELL_SOCKETS).any(|i| { net.sockets .get_mut::(shell_handles[i]) .is_listening() }); if !any_listening && let Some(i) = (0..MAX_SHELL_SOCKETS).find(|&i| { let s = net .sockets .get_mut::(shell_handles[i]); !s.is_open() && !s.is_listening() }) { let s = net .sockets .get_mut::(shell_handles[i]); s.abort(); s.set_nagle_enabled(false); s.set_ack_delay(None); s.set_keep_alive(Some(Duration::from_secs(30))); let _ = s.listen(SHELL_PORT); } core::iter::from_fn(|| match shell_rx.try_pop(&mut shell_ring_buf) { Some(n) if n >= 2 => { let conn_id = shell_ring_buf[0] as usize; let msg_type = shell_ring_buf[1]; match msg_type { MSG_DNS_QUERY if n > 2 => { handle_dns_query( &mut net, &shell_tx, conn_id as u8, &shell_ring_buf[2..n], false, RingSource::Shell, ); any_shell_notify = true; } MSG_DNS_QUERY6 if n > 2 => { handle_dns_query( &mut net, &shell_tx, conn_id as u8, &shell_ring_buf[2..n], true, RingSource::Shell, ); any_shell_notify = true; } MSG_PING_REQUEST if n >= 7 => { handle_ping_request( &shell_tx, conn_id as u8, &shell_ring_buf[2..n], now_ms, RingSource::Shell, ); any_shell_notify = true; } MSG_PING_REQUEST6 if n >= 19 => { handle_ping_request6( &shell_tx, conn_id as u8, &shell_ring_buf[2..n], now_ms, RingSource::Shell, ); any_shell_notify = true; } MSG_UDP_BIND if n >= 4 => { handle_udp_bind( &mut net, &shell_tx, conn_id as u8, &shell_ring_buf[2..n], RingSource::Shell, ); any_shell_notify = true; } MSG_UDP_SEND if n > 8 => { handle_udp_send( &mut net, conn_id as u8, &shell_ring_buf[2..n], RingSource::Shell, ); } MSG_UDP_CLOSE => { handle_udp_close(&mut net, conn_id as u8); } MSG_IFCONFIG_REQUEST => { handle_ifconfig_request(&shell_tx, conn_id as u8); any_shell_notify = true; } MSG_ARP_REQUEST => { handle_arp_request(&shell_tx, conn_id as u8, now_ms); any_shell_notify = true; } MSG_NETSTAT_REQUEST => { handle_netstat_request(&mut net, &shell_tx, conn_id as u8); any_shell_notify = true; } MSG_MCAST_JOIN if n >= 6 => { handle_mcast_join( &mut net, &shell_tx, conn_id as u8, &shell_ring_buf[2..n], ); any_shell_notify = true; } MSG_MCAST_LEAVE if n >= 6 => { handle_mcast_leave( &mut net, &shell_tx, conn_id as u8, &shell_ring_buf[2..n], ); any_shell_notify = true; } _ if conn_id < MAX_SHELL_SOCKETS => { let socket = net .sockets .get_mut::(shell_handles[conn_id]); match msg_type { MSG_DATA if n > 2 && socket.may_send() => { let _ = socket.send_slice(&shell_ring_buf[2..n]); } MSG_DISCONNECT => { socket.close(); shell_connected[conn_id] = false; handle_udp_close(&mut net, conn_id as u8); } _ => {} } } _ => {} } Some(()) } _ => None, }) .take(64) .count(); if let Some(irx) = &init_rx && let Some(itx) = &init_tx { core::iter::from_fn(|| match irx.try_pop(&mut init_ring_buf) { Some(n) if n >= 2 => { let conn_id = init_ring_buf[0]; let msg_type = init_ring_buf[1]; match msg_type { MSG_DNS_QUERY if n > 2 => { handle_dns_query( &mut net, itx, conn_id, &init_ring_buf[2..n], false, RingSource::Init, ); any_init_notify = true; } MSG_DNS_QUERY6 if n > 2 => { handle_dns_query( &mut net, itx, conn_id, &init_ring_buf[2..n], true, RingSource::Init, ); any_init_notify = true; } MSG_PING_REQUEST if n >= 7 => { handle_ping_request( itx, conn_id, &init_ring_buf[2..n], now_ms, RingSource::Init, ); any_init_notify = true; } MSG_PING_REQUEST6 if n >= 19 => { handle_ping_request6( itx, conn_id, &init_ring_buf[2..n], now_ms, RingSource::Init, ); any_init_notify = true; } MSG_UDP_BIND if n >= 4 => { handle_udp_bind( &mut net, itx, conn_id, &init_ring_buf[2..n], RingSource::Init, ); any_init_notify = true; } MSG_UDP_SEND if n > 8 => { handle_udp_send( &mut net, conn_id, &init_ring_buf[2..n], RingSource::Init, ); } MSG_UDP_CLOSE => { handle_udp_close(&mut net, conn_id); } MSG_IFCONFIG_REQUEST => { handle_ifconfig_request(itx, conn_id); any_init_notify = true; } MSG_ARP_REQUEST => { handle_arp_request(itx, conn_id, now_ms); any_init_notify = true; } MSG_NETSTAT_REQUEST => { handle_netstat_request(&mut net, itx, conn_id); any_init_notify = true; } MSG_MCAST_JOIN if n >= 6 => { handle_mcast_join(&mut net, itx, conn_id, &init_ring_buf[2..n]); any_init_notify = true; } MSG_MCAST_LEAVE if n >= 6 => { handle_mcast_leave(&mut net, itx, conn_id, &init_ring_buf[2..n]); any_init_notify = true; } _ => {} } Some(()) } _ => None, }) .take(64) .count(); } if any_shell_notify { syscall::notify_signal(SHELL_NOTIF_SLOT, NOTIFY_BIT_RX); } if any_init_notify { syscall::notify_signal(INIT_NOTIF_SLOT, NOTIFY_BIT_TX); } core::iter::from_fn(|| { let now = Instant::from_millis(syscall::clock_monotonic_ms()); matches!( net.iface.poll(now, &mut dev, &mut net.sockets), smoltcp::iface::PollResult::SocketStateChanged ) .then_some(()) }) .take(16) .count(); syscall::notify_signal(DRIVER_NOTIF_SLOT, NOTIFY_BIT_TX); let post_ms = syscall::clock_monotonic_ms().max(0); let post_now = Instant::from_millis(post_ms); let delay = net.iface.poll_delay(post_now, &net.sockets); let ping_active = unsafe { (&raw const PENDING_PING).read().is_some() }; let max_sleep = match ping_active { true => 100i64, false => 200i64, }; match delay { Some(d) if d == Duration::ZERO => { syscall::sched_yield(); } Some(d) => { let deadline = post_ms + (d.total_millis() as i64).min(max_sleep); core::iter::from_fn(|| { syscall::sched_yield(); let (status, _) = syscall::notify_poll(NOTIF_SLOT); match status >= 0 { true => None, false => (syscall::clock_monotonic_ms() < deadline).then_some(()), } }) .take(100) .count(); } None => { syscall::notify_wait(NOTIF_SLOT); } } } }