strat9_kernel/shell/commands/net/
mod.rs1mod ifconfig;
3mod netcmd;
4mod nslookup;
5mod ping;
6mod telnet;
7
8use crate::{
9 shell::ShellError,
10 shell_println,
11 vfs::{self, OpenFlags},
12};
13use alloc::string::String;
14pub use ifconfig::cmd_ifconfig;
15pub use netcmd::cmd_net;
16pub use nslookup::cmd_nslookup;
17pub use ping::cmd_ping;
18pub use telnet::cmd_telnet;
19
20pub(super) fn shell_sleep_ms(ms: u64) {
23 let ticks_to_wait = (ms + 9) / 10; let start = crate::process::scheduler::ticks();
25 loop {
26 crate::process::yield_task();
27 if crate::process::scheduler::ticks().wrapping_sub(start) >= ticks_to_wait {
28 break;
29 }
30 }
31}
32
33fn build_ping_path<'a>(buf: &'a mut [u8; 80], target: &str) -> &'a str {
35 let prefix = b"/net/ping/";
36 let tlen = target.len().min(buf.len() - prefix.len());
37 buf[..prefix.len()].copy_from_slice(prefix);
38 buf[prefix.len()..prefix.len() + tlen].copy_from_slice(&target.as_bytes()[..tlen]);
39 let total = prefix.len() + tlen;
40 core::str::from_utf8(&buf[..total]).unwrap_or("/net/ping/0.0.0.0")
41}
42
43pub(super) fn cmd_ping_impl(args: &[String]) -> Result<(), ShellError> {
45 let target = if args.is_empty() {
46 match vfs::open("/net/gateway", OpenFlags::READ) {
47 Ok(fd) => {
48 let mut buf = [0u8; 64];
49 let n = vfs::read(fd, &mut buf).unwrap_or(0);
50 let _ = vfs::close(fd);
51 let s = core::str::from_utf8(&buf[..n]).unwrap_or("").trim();
52 if s.is_empty() || s.starts_with("0.0.0.0") {
53 shell_println!("No gateway available. Usage: ping <ip>");
54 return Ok(());
55 }
56 String::from(s)
57 }
58 Err(_) => {
59 shell_println!("/net not available. Is strate-net running?");
60 return Ok(());
61 }
62 }
63 } else {
64 args[0].clone()
65 };
66
67 let count: u32 = if args.len() > 1 {
68 args[1].parse().unwrap_or(4)
69 } else {
70 4
71 };
72
73 shell_println!("PING {} ({} packets)", target, count);
74
75 let mut path_buf = [0u8; 80];
76 let path = build_ping_path(&mut path_buf, &target);
77
78 let mut sent: u32 = 0;
79 let mut received: u32 = 0;
80
81 for seq in 0..count {
82 if crate::shell::is_interrupted() {
83 shell_println!("^C");
84 break;
85 }
86 match vfs::open(path, OpenFlags::WRITE) {
87 Ok(fd) => {
88 let mut req = [0u8; 4];
89 req[0..2].copy_from_slice(&(seq as u16).to_le_bytes());
90 let _ = vfs::write(fd, &req);
91 let _ = vfs::close(fd);
92 sent += 1;
93 }
94 Err(_) => {
95 shell_println!(" send failed (seq={})", seq);
96 sent += 1;
97 continue;
98 }
99 }
100
101 let mut got_reply = false;
102 for _ in 0..20 {
103 shell_sleep_ms(50);
104
105 match vfs::open(path, OpenFlags::READ) {
106 Ok(fd) => {
107 let mut reply_buf = [0u8; 10];
108 match vfs::read(fd, &mut reply_buf) {
109 Ok(n) if n >= 10 => {
110 let _ = vfs::close(fd);
111 let rtt_us = u64::from_le_bytes([
112 reply_buf[2],
113 reply_buf[3],
114 reply_buf[4],
115 reply_buf[5],
116 reply_buf[6],
117 reply_buf[7],
118 reply_buf[8],
119 reply_buf[9],
120 ]);
121 let rtt_ms = rtt_us / 1000;
122 let rtt_frac = (rtt_us % 1000) / 100;
123 shell_println!(
124 " Reply from {}: seq={} time={}.{}ms",
125 target,
126 seq,
127 rtt_ms,
128 rtt_frac
129 );
130 received += 1;
131 got_reply = true;
132 break;
133 }
134 _ => {
135 let _ = vfs::close(fd);
136 }
137 }
138 }
139 Err(_) => break,
140 }
141 }
142
143 if !got_reply {
144 shell_println!(" Request timeout: seq={}", seq);
145 }
146 }
147
148 let loss = if sent > 0 {
149 ((sent - received) * 100) / sent
150 } else {
151 100
152 };
153 shell_println!("--- {} ping statistics ---", target);
154 shell_println!(
155 "{} transmitted, {} received, {}% loss",
156 sent,
157 received,
158 loss
159 );
160
161 Ok(())
162}
163
164pub(super) fn cmd_ifconfig_impl(args: &[String]) -> Result<(), ShellError> {
166 if !args.is_empty() {
167 match args[0].as_str() {
168 "inet" => {
169 if args.len() != 2 {
170 shell_println!("Usage: ifconfig inet <ipv4/prefix>");
171 return Err(ShellError::InvalidArguments);
172 }
173 let path = alloc::format!("/net/ip/set/{}", args[1]);
174 write_path(&path, b"1")?;
175 shell_println!("ifconfig: inet set to {}", args[1]);
176 return Ok(());
177 }
178 "gateway" => {
179 if args.len() == 2 && args[1].as_str() == "clear" {
180 write_path("/net/route/default/clear", b"1")?;
181 shell_println!("ifconfig: default gateway cleared");
182 return Ok(());
183 }
184 if args.len() != 2 {
185 shell_println!("Usage: ifconfig gateway <ipv4|clear>");
186 return Err(ShellError::InvalidArguments);
187 }
188 let path = alloc::format!("/net/route/default/set/{}", args[1]);
189 write_path(&path, b"1")?;
190 shell_println!("ifconfig: default gateway set to {}", args[1]);
191 return Ok(());
192 }
193 "dns" => {
194 if args.len() < 2 || args.len() > 4 {
195 shell_println!("Usage: ifconfig dns <ipv4> [ipv4] [ipv4]");
196 shell_println!(" ifconfig dns clear");
197 return Err(ShellError::InvalidArguments);
198 }
199 if args.len() == 2 && args[1].as_str() == "clear" {
200 write_path("/net/dns/set/0/0.0.0.0", b"1")?;
201 write_path("/net/dns/set/1/0.0.0.0", b"1")?;
202 write_path("/net/dns/set/2/0.0.0.0", b"1")?;
203 shell_println!("ifconfig: DNS cleared");
204 return Ok(());
205 }
206 write_path("/net/dns/set/0/0.0.0.0", b"1")?;
207 write_path("/net/dns/set/1/0.0.0.0", b"1")?;
208 write_path("/net/dns/set/2/0.0.0.0", b"1")?;
209 for (idx, ip) in args[1..].iter().enumerate() {
210 let path = alloc::format!("/net/dns/set/{}/{}", idx, ip);
211 write_path(&path, b"1")?;
212 }
213 shell_println!("ifconfig: DNS updated");
214 return Ok(());
215 }
216 "dhcp" => {
217 if args.len() != 2 {
218 shell_println!("Usage: ifconfig dhcp <on|off>");
219 return Err(ShellError::InvalidArguments);
220 }
221 match args[1].as_str() {
222 "on" => {
223 write_path("/net/dhcp/enable", b"1")?;
224 shell_println!("ifconfig: DHCP enabled");
225 return Ok(());
226 }
227 "off" => {
228 write_path("/net/dhcp/disable", b"1")?;
229 shell_println!("ifconfig: DHCP disabled");
230 return Ok(());
231 }
232 _ => {
233 shell_println!("Usage: ifconfig dhcp <on|off>");
234 return Err(ShellError::InvalidArguments);
235 }
236 }
237 }
238 _ => {
239 shell_println!("Usage: ifconfig");
240 shell_println!(" ifconfig inet <ipv4/prefix>");
241 shell_println!(" ifconfig gateway <ipv4|clear>");
242 shell_println!(" ifconfig dns <ipv4> [ipv4] [ipv4]");
243 shell_println!(" ifconfig dns clear");
244 shell_println!(" ifconfig dhcp <on|off>");
245 return Err(ShellError::InvalidArguments);
246 }
247 }
248 }
249
250 let read_file = |path: &str| -> String {
251 match vfs::open(path, OpenFlags::READ) {
252 Ok(fd) => {
253 let mut buf = [0u8; 96];
254 let n = vfs::read(fd, &mut buf).unwrap_or(0);
255 let _ = vfs::close(fd);
256 let s = core::str::from_utf8(&buf[..n]).unwrap_or("").trim();
257 String::from(s)
258 }
259 Err(_) => String::from("(unavailable)"),
260 }
261 };
262
263 let ip = read_file("/net/ip");
264 let gw = read_file("/net/gateway");
265 let route = read_file("/net/route");
266 let routes = read_file("/net/routes");
267 let dns = read_file("/net/dns");
268 let dhcp = read_file("/net/dhcp");
269
270 shell_println!("em0:");
271 shell_println!(" inet {}", ip);
272 shell_println!(" dhcp {}", dhcp);
273 shell_println!(" gateway {}", gw);
274 shell_println!(" route {}", route);
275 shell_println!(" routes {}", routes);
276 shell_println!(" dns {}", dns);
277
278 Ok(())
279}
280
281fn write_path(path: &str, data: &[u8]) -> Result<(), ShellError> {
283 let fd = vfs::open(path, OpenFlags::WRITE).map_err(|_| ShellError::ExecutionFailed)?;
284 let res = vfs::write(fd, data).map(|_| ());
285 let _ = vfs::close(fd);
286 res.map_err(|_| ShellError::ExecutionFailed)
287}
288
289pub(super) fn cmd_net_impl(args: &[String]) -> Result<(), ShellError> {
291 if args.is_empty() {
292 shell_println!("Usage: net route <show|add|del|default> ...");
293 return Err(ShellError::InvalidArguments);
294 }
295
296 match args[0].as_str() {
297 "route" => {
298 if args.len() < 2 {
299 shell_println!("Usage: net route <show|add|del|default> ...");
300 return Err(ShellError::InvalidArguments);
301 }
302
303 match args[1].as_str() {
304 "show" => {
305 let routes = match vfs::open("/net/routes", OpenFlags::READ) {
306 Ok(fd) => {
307 let mut buf = [0u8; 256];
308 let n = vfs::read(fd, &mut buf).unwrap_or(0);
309 let _ = vfs::close(fd);
310 String::from(core::str::from_utf8(&buf[..n]).unwrap_or("").trim())
311 }
312 Err(_) => String::from("(unavailable)"),
313 };
314 shell_println!("routes:");
315 shell_println!("{}", routes);
316 Ok(())
317 }
318 "add" => {
319 if args.len() != 4 {
320 shell_println!("Usage: net route add <cidr> <gateway>");
321 return Err(ShellError::InvalidArguments);
322 }
323 let path = alloc::format!("/net/route/add/{}/{}", args[2], args[3]);
324 write_path(&path, b"1")?;
325 shell_println!("net route add: ok ({} via {})", args[2], args[3]);
326 Ok(())
327 }
328 "del" => {
329 if args.len() != 3 {
330 shell_println!("Usage: net route del <cidr>");
331 return Err(ShellError::InvalidArguments);
332 }
333 let path = alloc::format!("/net/route/del/{}", args[2]);
334 write_path(&path, b"1")?;
335 shell_println!("net route del: ok ({})", args[2]);
336 Ok(())
337 }
338 "default" => {
339 if args.len() == 4 && args[2].as_str() == "set" {
340 let path = alloc::format!("/net/route/default/set/{}", args[3]);
341 write_path(&path, b"1")?;
342 shell_println!("net route default: ok (via {})", args[3]);
343 return Ok(());
344 }
345 if args.len() == 3 && args[2].as_str() == "clear" {
346 write_path("/net/route/default/clear", b"1")?;
347 shell_println!("net route default: cleared");
348 return Ok(());
349 }
350 shell_println!("Usage: net route default <set <gateway>|clear>");
351 Err(ShellError::InvalidArguments)
352 }
353 _ => {
354 shell_println!("Usage: net route <show|add|del|default> ...");
355 Err(ShellError::InvalidArguments)
356 }
357 }
358 }
359 _ => {
360 shell_println!("Usage: net route <show|add|del|default> ...");
361 Err(ShellError::InvalidArguments)
362 }
363 }
364}