1use alloc::string::String;
2use alloc::vec::Vec;
3
4
5
6pub fn udp_queue_lines(port_filter: Option<u16>) -> Vec<String> {
7 let mut lines = Vec::new();
8 let _lock = super::NET_LOCK.lock();
9 unsafe {
10 for i in 0..super::UDP_RX_QUEUE_SIZE {
11 let e = super::UDP_RX_QUEUE[i];
12 if !e.valid {
13 continue;
14 }
15 if let Some(port) = port_filter {
16 if e.dst_port != port {
17 continue;
18 }
19 }
20
21 let preview_len = e.payload_len.min(24);
22 let preview = payload_preview(&e.payload[..preview_len]);
23 lines.push(alloc::format!(
24 "{}:{} -> :{} len={} data='{}'",
25 super::format_ipv4(e.src_ip),
26 e.src_port,
27 e.dst_port,
28 e.payload_len,
29 preview
30 ));
31 }
32 }
33
34 lines
35}
36
37pub fn udp_recv_next_line(port: u16) -> Option<String> {
38 let dgram = udp_recv_next_datagram(port)?;
39 let text = payload_preview(&dgram.payload);
40 Some(alloc::format!(
41 "UDP RX {}:{} -> :{} len={} data='{}'",
42 super::format_ipv4(dgram.src_ip),
43 dgram.src_port,
44 dgram.dst_port,
45 dgram.payload.len(),
46 text
47 ))
48}
49
50pub fn udp_recv_next_datagram(port: u16) -> Option<super::UdpDatagram> {
51 udp_recv_matching(port, |_sip, _sport, _pl| true)
52}
53
54pub fn udp_recv_matching<F>(port: u16, pred: F) -> Option<super::UdpDatagram>
59where
60 F: Fn([u8; 4], u16, &[u8]) -> bool,
61{
62 let _lock = super::NET_LOCK.lock();
63 unsafe {
64 let queue = &*core::ptr::addr_of!(super::UDP_RX_QUEUE);
66 let i = super::udp_queue::find_matching(queue, port, |e| {
67 pred(e.src_ip, e.src_port, e.payload())
68 })?;
69 let e = super::UDP_RX_QUEUE[i];
70 super::UDP_RX_QUEUE[i] = super::UdpRxEntry::empty();
86
87 let mut payload = Vec::with_capacity(e.payload_len);
88 for idx in 0..e.payload_len {
89 payload.push(e.payload[idx]);
90 }
91
92 Some(super::UdpDatagram {
93 src_ip: e.src_ip,
94 src_port: e.src_port,
95 dst_port: e.dst_port,
96 payload,
97 })
98 }
99}
100
101pub fn send_udp_ipv4(
102 dst_ip: [u8; 4],
103 src_port: u16,
104 dst_port: u16,
105 payload: &[u8],
106) -> Result<usize, &'static str> {
107 send_udp_ipv4_from(super::status().ip, dst_ip, src_port, dst_port, payload)
108}
109
110pub(crate) fn send_udp_ipv4_from(
111 src_ip: [u8; 4],
112 dst_ip: [u8; 4],
113 src_port: u16,
114 dst_port: u16,
115 payload: &[u8],
116) -> Result<usize, &'static str> {
117 if payload.len() > 1400 {
118 return Err("payload too large");
119 }
120
121 let total_len = 20usize + 8usize + payload.len();
122 let mut pkt = [0u8; 1536];
123
124 pkt[0] = 0x45;
126 pkt[1] = 0;
127 pkt[2] = ((total_len >> 8) & 0xFF) as u8;
128 pkt[3] = (total_len & 0xFF) as u8;
129 pkt[4] = 0x12;
130 pkt[5] = 0x34;
131 pkt[6] = 0;
132 pkt[7] = 0;
133 pkt[8] = 64;
134 pkt[9] = 17; pkt[12..16].copy_from_slice(&src_ip);
137 pkt[16..20].copy_from_slice(&dst_ip);
138
139 let csum = super::ipv4_header_checksum(&pkt[0..20]);
140 pkt[10] = (csum >> 8) as u8;
141 pkt[11] = csum as u8;
142
143 pkt[20] = (src_port >> 8) as u8;
145 pkt[21] = src_port as u8;
146 pkt[22] = (dst_port >> 8) as u8;
147 pkt[23] = dst_port as u8;
148
149 let udp_len = 8usize + payload.len();
150 pkt[24] = (udp_len >> 8) as u8;
151 pkt[25] = udp_len as u8;
152 pkt[26] = 0;
153 pkt[27] = 0;
154
155 let data_start = 28usize;
156 let data_end = data_start + payload.len();
157 pkt[data_start..data_end].copy_from_slice(payload);
158
159 let mut pseudo = [0u8; 12 + 1408];
165 pseudo[0..4].copy_from_slice(&src_ip);
166 pseudo[4..8].copy_from_slice(&dst_ip);
167 pseudo[8] = 0;
168 pseudo[9] = 17; pseudo[10] = (udp_len >> 8) as u8;
170 pseudo[11] = udp_len as u8;
171 pseudo[12..12 + udp_len].copy_from_slice(&pkt[20..20 + udp_len]);
172 let udp_csum = super::internet_checksum(&pseudo[..12 + udp_len]);
173 let udp_csum = if udp_csum == 0 { 0xFFFF } else { udp_csum };
175 pkt[26] = (udp_csum >> 8) as u8;
176 pkt[27] = udp_csum as u8;
177
178 let sent = super::arp::transmit_nic_ipv4_packet(&pkt[..total_len])?;
179 Ok(sent)
180}
181
182pub(crate) fn parse_udp_ipv4_packet(packet: &[u8]) -> Result<(), &'static str> {
183 let ihl = ((packet[0] & 0x0F) as usize) * 4;
184 if ihl < 20 || packet.len() < ihl + 8 {
185 return Err("udp packet too short");
186 }
187
188 let total_len = (((packet[2] as u16) << 8) | packet[3] as u16) as usize;
189 if total_len < ihl + 8 || total_len > packet.len() {
190 return Err("invalid ipv4 total length");
191 }
192
193 let udp = &packet[ihl..total_len];
194 let src_port = ((udp[0] as u16) << 8) | udp[1] as u16;
195 let dst_port = ((udp[2] as u16) << 8) | udp[3] as u16;
196
197 let dst_ip = [packet[16], packet[17], packet[18], packet[19]];
198 let my_ip = super::status().ip;
199 let is_dhcp_broadcast = dst_ip == [255, 255, 255, 255] && dst_port == 68;
200 let is_dhcp_client_rx = dst_port == 68;
201 if dst_ip != my_ip && !is_dhcp_broadcast && !(my_ip == [0, 0, 0, 0] && is_dhcp_client_rx) {
202 if is_dhcp_client_rx {
203 crate::info!(
204 "UDP Parse: Dropped DHCP packet to {:?} (my_ip={:?})",
205 dst_ip,
206 my_ip
207 );
208 } else {
209 let src_ip = [packet[12], packet[13], packet[14], packet[15]];
213 crate::debug!(
214 "[SYS] UDP Parse: dropped {}:{} -> {}:{} (dst != my_ip={})",
215 super::format_ipv4(src_ip),
216 src_port,
217 super::format_ipv4(dst_ip),
218 dst_port,
219 super::format_ipv4(my_ip)
220 );
221 }
222 return Err("udp packet not for local ip");
223 }
224
225 let udp_len = (((udp[4] as u16) << 8) | udp[5] as u16) as usize;
226 if udp_len < 8 || udp_len > udp.len() {
227 if is_dhcp_client_rx {
228 crate::info!(
229 "UDP Parse: Invalid UDP length {} (buffer {})",
230 udp_len,
231 udp.len()
232 );
233 }
234 return Err("invalid udp length");
235 }
236
237 let payload = &udp[8..udp_len];
238 let src_ip = [packet[12], packet[13], packet[14], packet[15]];
239
240 if is_dhcp_client_rx {
241 crate::info!(
242 "UDP Parse: Accepted DHCP packet from {:?}:{} len={}",
243 src_ip,
244 src_port,
245 payload.len()
246 );
247 }
248
249 udp_queue_push(src_ip, src_port, dst_port, payload);
250
251 unsafe {
252 super::NET_STATE.rx_packets = super::NET_STATE.rx_packets.saturating_add(1);
253 }
254
255 Ok(())
256}
257
258fn udp_queue_push(src_ip: [u8; 4], src_port: u16, dst_port: u16, payload: &[u8]) {
263 let _lock = super::NET_LOCK.lock();
264 unsafe {
265 let age = super::UDP_RX_TICK;
266 super::UDP_RX_TICK = super::UDP_RX_TICK.saturating_add(1);
267
268 let queue = &mut *core::ptr::addr_of_mut!(super::UDP_RX_QUEUE);
269 let _ = super::udp_queue::push(queue, src_ip, src_port, dst_port, payload, age);
270 }
271}
272
273fn payload_preview(payload: &[u8]) -> String {
274 let mut out = String::new();
275 for &b in payload {
276 let c = b as char;
277 if c.is_ascii_graphic() || c == ' ' {
278 out.push(c);
279 } else {
280 out.push('.');
281 }
282 }
283 out
284}
285
286pub fn purge_port(port: u16) -> usize {
297 let _lock = super::NET_LOCK.lock();
298 unsafe {
299 let queue = &mut *core::ptr::addr_of_mut!(super::UDP_RX_QUEUE);
300 super::udp_queue::purge_port(queue, port)
301 }
302}