1use alloc::string::String;
17use alloc::vec::Vec;
18
19use crate::kernel::reclock::RecoverableMutex;
20
21static HTTPS_ATTEMPT_ORDINAL: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
27
28pub fn next_https_attempt_ordinal() -> u32 {
29 HTTPS_ATTEMPT_ORDINAL.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
30}
31
32pub(crate) mod addr_util;
40pub(crate) mod arp;
41pub(crate) mod arp_cache;
42pub(crate) mod dhcp;
43pub(crate) mod dns;
44pub(crate) mod http;
45pub(crate) mod icmp;
46pub(crate) mod tcp;
47pub(crate) mod tcp_state;
48pub(crate) mod udp;
49pub(crate) mod eth_frame;
50pub(crate) mod tx_queue;
51pub(crate) mod reassembly;
52pub(crate) mod udp_queue;
53
54pub use addr_util::{
55 format_ipv4, format_mac, parse_host_port, parse_host_port_with_default, parse_ipv4, parse_mac,
56};
57pub(crate) use addr_util::{internet_checksum, ipv4_header_checksum};
58
59pub use arp::{
61 arp_cache_lines, build_arp_request, get_next_hop_ip, ingest_ethernet_frame,
62 ingest_nic_ethernet_frame, ingest_nic_ipv4_packet,
63 nic_pop_tx_ipv4_packet, nic_take_tx_ipv4_chunk, transmit_nic_ethernet_frame,
64 transmit_nic_ipv4_packet,
65};
66pub use dhcp::{
67 dhcp_acquire, dhcp_release, dhcp_start_async, dhcp_status_line, dhcp_tick,
68 set_dhcp_auto_pending,
69};
70pub use dns::{
71 build_dns_query, dns_query_a_via_udp, ntp_query, ntp_sync, parse_dns_a_response,
72};
73pub use http::{
74 extract_http_body_binary, http_get_binary, http_get_real, http_get_real_with_referer, http_post_real, http_request_real,
75 https_get_real, parse_http_response, parse_http_response_bytes, parse_http_response_text,
76};
77pub use icmp::{
78 build_icmp_echo_request, parse_icmp_echo_reply_ipv4, ping_once_real,
79};
80pub use tcp::{
81 tcp_close_real, tcp_connect_real, tcp_current_snd_seq, tcp_is_socket_closed,
82 tcp_acked_through, tcp_recv_peek_nonempty, tcp_recv_real, tcp_retransmit_real, tcp_send_real,
83 tcp_socket_lines,
84};
85pub use udp::{
86 send_udp_ipv4, udp_queue_lines, udp_recv_next_datagram,
87 udp_recv_next_line,
88};
89
90pub(crate) static NET_LOCK: RecoverableMutex<()> = RecoverableMutex::new(());
92
93pub fn force_release_locks(cid: i16) {
94 unsafe {
95 NET_LOCK.force_release_if_core(cid);
96 }
97}
98
99#[derive(Copy, Clone)]
101pub struct NetworkState {
102 pub link_up: bool,
103 pub nic_backend: NicBackend,
104 pub mac: [u8; 6],
105 pub ip: [u8; 4],
106 pub gateway: [u8; 4],
107 pub dns: [u8; 4],
108 pub dns2: [u8; 4],
109 pub usb_eth_present: bool,
110 pub usb_eth_addr: u8,
111 pub usb_eth_bulk_in: u8,
112 pub usb_eth_bulk_out: u8,
113 pub usb_eth_mps: u16,
114 pub usb_eth_is_rndis: bool,
115 pub tx_packets: u64,
116 pub rx_packets: u64,
117 pub raw_rx_frames: u64,
121 pub dhcp_active: bool,
122 pub dhcp_server: [u8; 4],
123 pub dhcp_lease_secs: u32,
124 pub dhcp_subnet: [u8; 4],
125 pub dhcp_t1_secs: u32,
126 pub dhcp_t2_secs: u32,
127}
128
129#[derive(Copy, Clone)]
130pub enum NicBackend {
131 SoftwareLoopback,
132 UsbEthernet,
133}
134
135pub use arp_cache::ArpEntry;
139
140#[derive(Copy, Clone)]
141pub struct PingReply {
142 pub src_ip: [u8; 4],
143 pub identifier: u16,
144 pub sequence: u16,
145 pub payload_len: usize,
146}
147
148const ARP_CACHE_SIZE: usize = 16;
149static mut ARP_CACHE: [ArpEntry; ARP_CACHE_SIZE] = [ArpEntry::empty(); ARP_CACHE_SIZE];
150static mut ARP_TICK: u32 = 1;
151static mut ARP_NEG_IP: [u8; 4] = [0; 4];
156static mut ARP_NEG_UNTIL_MS: u64 = 0;
157static mut PING_SEQ: u16 = 1;
158static DNS_TXID: core::sync::atomic::AtomicU16 = core::sync::atomic::AtomicU16::new(0x1200);
161static DNS_SRC_PORT: core::sync::atomic::AtomicU16 =
165 core::sync::atomic::AtomicU16::new(DNS_SRC_PORT_BASE);
166pub(super) const DNS_SRC_PORT_BASE: u16 = 53000;
167pub(super) const DNS_SRC_PORT_RANGE: u16 = 512; const DNS_CACHE_SIZE: usize = 32;
172const DNS_CACHE_NAME_MAX: usize = 64;
173const DNS_CACHE_TTL_MS: u64 = 60_000; #[derive(Copy, Clone)]
176struct DnsCacheEntry {
177 valid: bool,
178 name: [u8; DNS_CACHE_NAME_MAX],
179 name_len: usize,
180 ip: [u8; 4],
181 expires_ms: u64,
182}
183
184impl DnsCacheEntry {
185 const fn empty() -> Self {
186 Self {
187 valid: false,
188 name: [0; DNS_CACHE_NAME_MAX],
189 name_len: 0,
190 ip: [0; 4],
191 expires_ms: 0,
192 }
193 }
194}
195
196static mut DNS_CACHE: [DnsCacheEntry; DNS_CACHE_SIZE] = [DnsCacheEntry::empty(); DNS_CACHE_SIZE];
197
198pub(crate) fn next_dns_src_port() -> u16 {
200 use core::sync::atomic::Ordering;
201 let raw = DNS_SRC_PORT.fetch_add(1, Ordering::Relaxed);
203 if !(DNS_SRC_PORT_BASE..DNS_SRC_PORT_BASE + DNS_SRC_PORT_RANGE).contains(&raw) {
204 DNS_SRC_PORT.store(DNS_SRC_PORT_BASE + 1, Ordering::Relaxed);
205 return DNS_SRC_PORT_BASE;
206 }
207 raw
208}
209
210pub(crate) fn next_dns_txid() -> u16 {
212 DNS_TXID.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
213}
214
215pub(crate) fn dns_cache_lookup(name: &str) -> Option<[u8; 4]> {
217 let nb = name.as_bytes();
218 if nb.len() > DNS_CACHE_NAME_MAX {
219 return None;
220 }
221 let now = crate::kernel::timer::get_system_time_ms();
222 unsafe {
223 for e in DNS_CACHE.iter() {
224 if e.valid
225 && e.name_len == nb.len()
226 && &e.name[..e.name_len] == nb
227 && now < e.expires_ms
228 {
229 return Some(e.ip);
230 }
231 }
232 }
233 None
234}
235
236pub(crate) fn dns_cache_insert(name: &str, ip: [u8; 4]) {
238 let nb = name.as_bytes();
239 if nb.is_empty() || nb.len() > DNS_CACHE_NAME_MAX {
240 return;
241 }
242 let now = crate::kernel::timer::get_system_time_ms();
243 let expires_ms = now.saturating_add(DNS_CACHE_TTL_MS);
244 unsafe {
245 let mut free_idx: Option<usize> = None;
247 let mut oldest_idx = 0usize;
248 let mut oldest_exp = u64::MAX;
249 for (i, e) in DNS_CACHE.iter().enumerate() {
250 if e.valid && e.name_len == nb.len() && &e.name[..e.name_len] == nb {
251 free_idx = Some(i);
252 break;
253 }
254 if free_idx.is_none() && (!e.valid || now >= e.expires_ms) {
255 free_idx = Some(i);
256 }
257 if e.expires_ms < oldest_exp {
258 oldest_exp = e.expires_ms;
259 oldest_idx = i;
260 }
261 }
262 let idx = free_idx.unwrap_or(oldest_idx);
263 let mut entry = DnsCacheEntry::empty();
264 entry.valid = true;
265 entry.name[..nb.len()].copy_from_slice(nb);
266 entry.name_len = nb.len();
267 entry.ip = ip;
268 entry.expires_ms = expires_ms;
269 DNS_CACHE[idx] = entry;
270 }
271}
272
273const UDP_RX_QUEUE_SIZE: usize = 32;
274const TCP_SOCKET_TABLE_SIZE: usize = 32;
281const NIC_TX_QUEUE_SIZE: usize = 64;
290const NIC_TX_MAX_PAYLOAD: usize = 1536;
291
292pub(crate) use udp_queue::UdpRxEntry;
296
297static mut UDP_RX_QUEUE: [UdpRxEntry; UDP_RX_QUEUE_SIZE] = [UdpRxEntry::empty(); UDP_RX_QUEUE_SIZE];
298static mut UDP_RX_TICK: u32 = 1;
299
300#[derive(Copy, Clone)]
301struct NicTxEntry {
302 valid: bool,
303 len: usize,
304 offset: usize,
305 payload: [u8; NIC_TX_MAX_PAYLOAD],
306 age: u32,
307}
308
309impl NicTxEntry {
310 const fn empty() -> Self {
311 Self {
312 valid: false,
313 len: 0,
314 offset: 0,
315 payload: [0; NIC_TX_MAX_PAYLOAD],
316 age: 0,
317 }
318 }
319}
320
321static mut NIC_TX_QUEUE: [NicTxEntry; NIC_TX_QUEUE_SIZE] = [NicTxEntry::empty(); NIC_TX_QUEUE_SIZE];
322static mut NIC_TX_DROPS: u32 = 0;
325static mut NIC_TX_TICK: u32 = 1;
326
327use tcp_state::TcpState;
330
331#[derive(Clone)]
332struct TcpSocket {
333 valid: bool,
334 local_port: u16,
335 remote_ip: [u8; 4],
336 remote_port: u16,
337 state: TcpState,
338 snd_nxt: u32,
339 snd_una: u32,
341 rcv_nxt: u32,
342 rx_buffer: Vec<u8>,
343 fin_received: bool,
344}
345
346impl TcpSocket {
347 const fn empty() -> Self {
348 Self {
349 valid: false,
350 local_port: 0,
351 remote_ip: [0; 4],
352 remote_port: 0,
353 state: TcpState::Closed,
354 snd_nxt: 0,
355 snd_una: 0,
356 rcv_nxt: 0,
357 rx_buffer: Vec::new(),
358 fin_received: false,
359 }
360 }
361}
362
363static mut TCP_SOCKETS: [TcpSocket; TCP_SOCKET_TABLE_SIZE] = [
364 TcpSocket::empty(),
365 TcpSocket::empty(),
366 TcpSocket::empty(),
367 TcpSocket::empty(),
368 TcpSocket::empty(),
369 TcpSocket::empty(),
370 TcpSocket::empty(),
371 TcpSocket::empty(),
372 TcpSocket::empty(),
373 TcpSocket::empty(),
374 TcpSocket::empty(),
375 TcpSocket::empty(),
376 TcpSocket::empty(),
377 TcpSocket::empty(),
378 TcpSocket::empty(),
379 TcpSocket::empty(),
380 TcpSocket::empty(),
381 TcpSocket::empty(),
382 TcpSocket::empty(),
383 TcpSocket::empty(),
384 TcpSocket::empty(),
385 TcpSocket::empty(),
386 TcpSocket::empty(),
387 TcpSocket::empty(),
388 TcpSocket::empty(),
389 TcpSocket::empty(),
390 TcpSocket::empty(),
391 TcpSocket::empty(),
392 TcpSocket::empty(),
393 TcpSocket::empty(),
394 TcpSocket::empty(),
395 TcpSocket::empty(),
396];
397static mut TCP_EPHEMERAL_PORT: u16 = 40000;
398
399pub struct UdpDatagram {
400 pub src_ip: [u8; 4],
401 pub src_port: u16,
402 pub dst_port: u16,
403 pub payload: Vec<u8>,
404}
405
406pub struct HttpGetResult {
407 pub remote_ip: [u8; 4],
408 pub local_port: u16,
409 pub status_code: u16,
410 pub reason: String,
411 pub headers: alloc::collections::BTreeMap<alloc::string::String, alloc::string::String>,
412 pub body: String,
413 pub body_bytes: alloc::vec::Vec<u8>,
415}
416
417pub struct HttpsGetResult {
418 pub remote_ip: [u8; 4],
419 pub local_port: u16,
420 pub tls_version: String,
421 pub cipher_suite: String,
422 pub cert_subject: String,
423 pub cert_issuer: String,
424 pub cert_not_after: String,
425 pub status_code: u16,
426 pub reason: String,
427 pub headers: alloc::collections::BTreeMap<alloc::string::String, alloc::string::String>,
428 pub body: String,
429 pub body_bytes: alloc::vec::Vec<u8>,
432}
433
434pub struct DhcpLease {
435 pub ip: [u8; 4],
436 pub gateway: [u8; 4],
437 pub dns: [u8; 4],
438 pub dns2: [u8; 4],
439 pub server: [u8; 4],
440 pub subnet: [u8; 4],
441 pub lease_secs: u32,
442 pub t1_secs: u32,
443 pub t2_secs: u32,
444}
445
446struct DhcpAckInfo {
447 msg_type: u8,
448 yiaddr: [u8; 4],
449 server: [u8; 4],
450 router: [u8; 4],
451 dns: [u8; 4],
452 dns2: [u8; 4],
453 subnet: [u8; 4],
454 lease_secs: u32,
455 t1_secs: u32,
456 t2_secs: u32,
457}
458
459static mut NET_STATE: NetworkState = NetworkState {
460 link_up: false,
461 nic_backend: NicBackend::SoftwareLoopback,
462 mac: [0x02, 0x00, 0xA1, 0xCE, 0x00, 0x01],
463 ip: [0, 0, 0, 0],
474 gateway: [0, 0, 0, 0],
475 dns: [0, 0, 0, 0],
476 dns2: [0, 0, 0, 0],
477 usb_eth_present: false,
478 usb_eth_addr: 0,
479 usb_eth_bulk_in: 0,
480 usb_eth_bulk_out: 0,
481 usb_eth_mps: 0,
482 usb_eth_is_rndis: false,
483 tx_packets: 0,
484 rx_packets: 0,
485 raw_rx_frames: 0,
486 dhcp_active: false,
487 dhcp_server: [0; 4],
488 dhcp_lease_secs: 0,
489 dhcp_subnet: [0; 4],
490 dhcp_t1_secs: 0,
491 dhcp_t2_secs: 0,
492};
493
494static mut DHCP_XID: u32 = 0x41C3_0001;
495static mut DHCP_SERVICE_TICK: u64 = 0;
496static mut DHCP_LEASE_START_TICK: u64 = 0;
497static mut DHCP_RENEW_STAGE: u8 = 0;
498static mut DHCP_NEXT_RENEW_TICK: u64 = 0;
499static mut DHCP_AUTO_PENDING: bool = true;
500static mut DHCP_LAST_AUTO_ATTEMPT_TICK: u64 = 0;
501static mut DHCP_ASYNC_STATE: u8 = 0; static mut DHCP_ASYNC_XID: u32 = 0;
503static mut DHCP_ASYNC_ATTEMPT: usize = 0;
504static mut DHCP_ASYNC_DEADLINE_TICK: u64 = 0;
505static mut DHCP_ASYNC_RENEWING: bool = false;
506static mut DHCP_ASYNC_REBINDING: bool = false;
507static mut LAST_PING_REPLY: Option<PingReply> = None;
508
509pub fn init() {
512 let _lock = NET_LOCK.lock();
513 unsafe {
514 LAST_PING_REPLY = None;
515 NET_STATE.link_up = false;
516 NET_STATE.nic_backend = NicBackend::SoftwareLoopback;
517 ARP_CACHE = [ArpEntry::empty(); ARP_CACHE_SIZE];
518 ARP_TICK = 1;
519 PING_SEQ = 1;
520 DNS_TXID.store(0x1200, core::sync::atomic::Ordering::Relaxed);
521 DNS_SRC_PORT.store(DNS_SRC_PORT_BASE, core::sync::atomic::Ordering::Relaxed);
522 DNS_CACHE = [DnsCacheEntry::empty(); DNS_CACHE_SIZE];
523 UDP_RX_QUEUE = [UdpRxEntry::empty(); UDP_RX_QUEUE_SIZE];
524 UDP_RX_TICK = 1;
525 NIC_TX_QUEUE = [NicTxEntry::empty(); NIC_TX_QUEUE_SIZE];
526 NIC_TX_TICK = 1;
527 TCP_SOCKETS = [
528 TcpSocket::empty(),
529 TcpSocket::empty(),
530 TcpSocket::empty(),
531 TcpSocket::empty(),
532 TcpSocket::empty(),
533 TcpSocket::empty(),
534 TcpSocket::empty(),
535 TcpSocket::empty(),
536 TcpSocket::empty(),
537 TcpSocket::empty(),
538 TcpSocket::empty(),
539 TcpSocket::empty(),
540 TcpSocket::empty(),
541 TcpSocket::empty(),
542 TcpSocket::empty(),
543 TcpSocket::empty(),
544 TcpSocket::empty(),
545 TcpSocket::empty(),
546 TcpSocket::empty(),
547 TcpSocket::empty(),
548 TcpSocket::empty(),
549 TcpSocket::empty(),
550 TcpSocket::empty(),
551 TcpSocket::empty(),
552 TcpSocket::empty(),
553 TcpSocket::empty(),
554 TcpSocket::empty(),
555 TcpSocket::empty(),
556 TcpSocket::empty(),
557 TcpSocket::empty(),
558 TcpSocket::empty(),
559 TcpSocket::empty(),
560 ];
561 TCP_EPHEMERAL_PORT = 40000;
562 DHCP_XID = 0x41C3_0001;
563 DHCP_SERVICE_TICK = 0;
564 DHCP_LEASE_START_TICK = 0;
565 DHCP_RENEW_STAGE = 0;
566 DHCP_AUTO_PENDING = true;
567 DHCP_LAST_AUTO_ATTEMPT_TICK = 0;
568 DHCP_ASYNC_STATE = 0;
569 DHCP_ASYNC_XID = 0;
570 DHCP_ASYNC_ATTEMPT = 0;
571 DHCP_ASYNC_DEADLINE_TICK = 0;
572 DHCP_ASYNC_RENEWING = false;
573 NET_STATE.dhcp_active = false;
574 NET_STATE.dhcp_server = [0; 4];
575 NET_STATE.dhcp_lease_secs = 0;
576 NET_STATE.dhcp_subnet = [0; 4];
577 NET_STATE.dhcp_t1_secs = 0;
578 NET_STATE.dhcp_t2_secs = 0;
579 }
580
581 if self_test() {
582 crate::debug!("[NET] core initialized (IPv4/UDP/TCP/DNS/ARP/ICMP/HTTP/HTTPS parser ready)");
583 } else {
584 crate::debug!("[NET] core initialized with warnings (self-test failed)");
585 }
586}
587
588pub fn status() -> &'static mut NetworkState {
589 unsafe { &mut NET_STATE }
590}
591
592pub fn status_line() -> String {
593 let _lock = NET_LOCK.lock();
594 let st = status();
595
596 let nic = if st.usb_eth_present {
597 alloc::format!(
598 "usb-eth(addr={},in={},out={},mps={},type={})",
599 st.usb_eth_addr,
600 st.usb_eth_bulk_in,
601 st.usb_eth_bulk_out,
602 st.usb_eth_mps,
603 if st.usb_eth_is_rndis {
604 "rndis"
605 } else {
606 "cdc-ecm"
607 }
608 )
609 } else {
610 String::from("none")
611 };
612
613 let mut dns_list = alloc::vec::Vec::new();
614 if st.dns != [0, 0, 0, 0] {
615 dns_list.push(format_ipv4(st.dns));
616 }
617 if st.dns2 != [0, 0, 0, 0] && st.dns2 != st.dns {
618 dns_list.push(format_ipv4(st.dns2));
619 }
620 for fallback in &[[8, 8, 8, 8], [1, 1, 1, 1]] {
621 if st.dns != *fallback && st.dns2 != *fallback {
622 dns_list.push(format_ipv4(*fallback));
623 }
624 }
625 let dns_display = if dns_list.is_empty() {
626 String::from("none")
627 } else {
628 dns_list.join(", ")
629 };
630
631 alloc::format!(
632 "link={}\nbackend={}\nnic={}\nip={}\nmask={}\ngw={}\ndns={}\ntx={}\nrx={}",
633 if st.link_up { "up" } else { "down" },
634 nic_backend_name(st.nic_backend),
635 nic,
636 format_ipv4(st.ip),
637 format_ipv4(st.dhcp_subnet),
638 format_ipv4(st.gateway),
639 dns_display,
640 st.tx_packets,
641 st.rx_packets
642 )
643}
644
645pub fn nic_debug_info() -> String {
646 let _lock = NET_LOCK.lock();
647 let st = status();
648 let async_state = unsafe { DHCP_ASYNC_STATE };
649 let async_attempt = unsafe { DHCP_ASYNC_ATTEMPT };
650 let async_xid = unsafe { DHCP_ASYNC_XID };
651 let tick = unsafe { DHCP_SERVICE_TICK };
652
653 let mut tx_q_count = 0;
654 unsafe {
655 for e in &NIC_TX_QUEUE {
656 if e.valid {
657 tx_q_count += 1;
658 }
659 }
660 }
661
662 let state_str = match async_state {
663 0 => {
664 if st.dhcp_active {
665 "Bound"
666 } else {
667 "Idle"
668 }
669 }
670 1 => "WaitOffer",
671 2 => "WaitAck",
672 _ => "Unknown",
673 };
674
675 alloc::format!(
676 "NIC DEBUG INFO:\n\
677 Backend: {} (MAC: {})\n\
678 USB Eth: present={} type={} addr={} in={} out={} mps={}\n\
679 Packets: TX={} RX={} RAW_RX_FRAMES={}\n\
680 TX Queue: {}/{} used\n\
681 DHCP State: active={} auto_pending={}\n\
682 DHCP Async: state={} ({}) attempt={} xid={:#010X} tick={}",
683 nic_backend_name(st.nic_backend),
684 format_mac(st.mac),
685 st.usb_eth_present,
686 if st.usb_eth_is_rndis {
687 "rndis"
688 } else {
689 "cdc-ecm"
690 },
691 st.usb_eth_addr,
692 st.usb_eth_bulk_in,
693 st.usb_eth_bulk_out,
694 st.usb_eth_mps,
695 st.tx_packets,
696 st.rx_packets,
697 st.raw_rx_frames,
698 tx_q_count,
699 NIC_TX_QUEUE_SIZE,
700 st.dhcp_active,
701 unsafe { DHCP_AUTO_PENDING },
702 async_state,
703 state_str,
704 async_attempt,
705 async_xid,
706 tick
707 )
708}
709
710pub fn register_usb_ethernet(
711 address: u8,
712 bulk_in_ep: u8,
713 bulk_out_ep: u8,
714 max_packet: u16,
715 is_rndis: bool,
716 mac: [u8; 6],
717) {
718 let _lock = NET_LOCK.lock();
719 unsafe {
720 NET_STATE.usb_eth_present = true;
721 NET_STATE.nic_backend = NicBackend::UsbEthernet;
722 NET_STATE.usb_eth_addr = address;
723 NET_STATE.usb_eth_bulk_in = bulk_in_ep;
724 NET_STATE.usb_eth_bulk_out = bulk_out_ep;
725 NET_STATE.usb_eth_mps = max_packet;
726 NET_STATE.usb_eth_is_rndis = is_rndis;
727 NET_STATE.mac = mac;
728 NET_STATE.link_up = true;
729 DHCP_AUTO_PENDING = true;
730 DHCP_LAST_AUTO_ATTEMPT_TICK = 0;
731 }
732
733 crate::info!(
734 "NET: USB Ethernet registered at addr {} (in ep {}, out ep {}, mps {}, type={})",
735 address,
736 bulk_in_ep,
737 bulk_out_ep,
738 max_packet,
739 if is_rndis { "RNDIS" } else { "CDC-ECM" }
740 );
741}
742
743pub fn nic_backend_name(backend: NicBackend) -> &'static str {
744 match backend {
745 NicBackend::SoftwareLoopback => "software-loopback",
746 NicBackend::UsbEthernet => "usb-ethernet",
747 }
748}
749
750fn self_test() -> bool {
751 let mut query = [0u8; 128];
752 let qlen = match dns::build_dns_query("example.com", 0xCAFE, &mut query) {
753 Ok(n) => n,
754 Err(_) => return false,
755 };
756
757 if qlen < 20 {
758 return false;
759 }
760
761 let response: [u8; 45] = [
762 0xCA, 0xFE, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, b'e', b'x',
763 b'a', b'm', b'p', b'l', b'e', 0x03, b'c', b'o', b'm', 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0,
764 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x04, 192, 168, 1, 100,
765 ];
766
767 if !matches!(
768 dns::parse_dns_a_response(&response, 0xCAFE, "example.com"),
769 Ok(Some([192, 168, 1, 100]))
770 ) {
771 return false;
772 }
773
774
775
776 let req = match http::build_http_get_request_pub("example.com", "/") {
777 Ok(v) => v,
778 Err(_) => return false,
779 };
780 if !req.starts_with("GET /") {
781 return false;
782 }
783
784 let sample = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nhey";
785 let parsed = match http::parse_http_response(sample) {
786 Ok(v) => v,
787 Err(_) => return false,
788 };
789 if parsed.0 != 200 || parsed.3 != "hey" {
790 return false;
791 }
792
793
794
795 let mut dhcp_discover = [0u8; 320];
796 let discover_len = match dhcp::build_dhcp_discover_payload_pub(
797 0x1122_3344,
798 [2, 0, 0xA1, 0xCE, 0, 1],
799 68,
800 67,
801 &mut dhcp_discover,
802 ) {
803 Ok(v) => v,
804 Err(_) => return false,
805 };
806 if discover_len < 244 {
807 return false;
808 }
809
810 let mut dhcp_ack = [0u8; 320];
811 let ack_len = match dhcp::build_dhcp_ack_payload_pub(
812 0x1122_3344,
813 [2, 0, 0xA1, 0xCE, 0, 1],
814 [192, 168, 2, 200],
815 [192, 168, 2, 1],
816 [192, 168, 2, 1],
817 3600,
818 &mut dhcp_ack,
819 ) {
820 Ok(v) => v,
821 Err(_) => return false,
822 };
823 let parsed_ack = match dhcp::parse_dhcp_ack_payload_pub(&dhcp_ack[..ack_len], 0x1122_3344) {
824 Ok(v) => v,
825 Err(_) => return false,
826 };
827 if parsed_ack.msg_type != 5 {
828 return false;
829 }
830 if parsed_ack.yiaddr != [192, 168, 2, 200] {
831 return false;
832 }
833 if parsed_ack.subnet != [255, 255, 255, 0] {
834 return false;
835 }
836 if parsed_ack.t1_secs == 0 || parsed_ack.t2_secs == 0 {
837 return false;
838 }
839
840 true
841}