strat9_kernel/shell/commands/net/
mod.rs1mod ifconfig;
3mod netcmd;
4mod nslookup;
5mod ping_userspace;
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_userspace::cmd_ping;
18use strat9_abi::ip::{is_ipv4_literal_candidate, parse_ipv4_literal};
19pub use telnet::cmd_telnet;
20
21pub(super) fn read_elf(path: &str) -> Result<alloc::vec::Vec<u8>, ShellError> {
24 let fd = vfs::open(path, OpenFlags::READ).map_err(|_| {
25 shell_println!("{}: not found", path);
26 ShellError::ExecutionFailed
27 })?;
28 let data = vfs::read_all(fd).map_err(|_| {
29 let _ = vfs::close(fd);
30 shell_println!("{}: read error", path);
31 ShellError::ExecutionFailed
32 })?;
33 let _ = vfs::close(fd);
34 Ok(data)
35}
36
37pub(super) fn spawn_elf_with_args(
40 bin_path: &str,
41 task_name: &'static str,
42 args: &[String],
43) -> Result<(), ShellError> {
44 let data = read_elf(bin_path)?;
45 let arg_strs: alloc::vec::Vec<&str> = args.iter().map(|s| s.as_str()).collect();
46 match crate::process::elf::load_and_run_elf_with_args(&data, task_name, &arg_strs) {
47 Ok(task_id) => {
48 shell_println!("{}: started (task={})", task_name, task_id);
49 Ok(())
50 }
51 Err(e) => {
52 shell_println!("{}: failed to launch: {}", task_name, e);
53 Err(ShellError::ExecutionFailed)
54 }
55 }
56}
57
58pub(super) fn cmd_ping_spawn_impl(args: &[String]) -> Result<(), ShellError> {
60 if let Some(target) = args.first() {
62 if is_ipv4_literal_candidate(target) && parse_ipv4_literal(target).is_none() {
63 shell_println!("ping: invalid IPv4 address: {}", target);
64 return Ok(());
65 }
66 }
67 spawn_elf_with_args("/initfs/bin/ping", "ping", args)
68}
69
70pub(super) fn shell_sleep_ms(ms: u64) {
73 let ticks_to_wait = (ms + 9) / 10; let start = crate::process::scheduler::ticks();
75 loop {
76 crate::process::yield_task();
77 if crate::process::scheduler::ticks().wrapping_sub(start) >= ticks_to_wait {
78 break;
79 }
80 }
81}
82
83pub(super) fn cmd_ifconfig_impl(args: &[String]) -> Result<(), ShellError> {
85 if !args.is_empty() {
86 match args[0].as_str() {
87 "inet" => {
88 if args.len() != 2 {
89 shell_println!("Usage: ifconfig inet <ipv4/prefix>");
90 return Err(ShellError::InvalidArguments);
91 }
92 let path = alloc::format!("/net/ip/set/{}", args[1]);
93 write_path(&path, b"1")?;
94 shell_println!("ifconfig: inet set to {}", args[1]);
95 return Ok(());
96 }
97 "gateway" => {
98 if args.len() == 2 && args[1].as_str() == "clear" {
99 write_path("/net/route/default/clear", b"1")?;
100 shell_println!("ifconfig: default gateway cleared");
101 return Ok(());
102 }
103 if args.len() != 2 {
104 shell_println!("Usage: ifconfig gateway <ipv4|clear>");
105 return Err(ShellError::InvalidArguments);
106 }
107 let path = alloc::format!("/net/route/default/set/{}", args[1]);
108 write_path(&path, b"1")?;
109 shell_println!("ifconfig: default gateway set to {}", args[1]);
110 return Ok(());
111 }
112 "dns" => {
113 if args.len() < 2 || args.len() > 4 {
114 shell_println!("Usage: ifconfig dns <ipv4> [ipv4] [ipv4]");
115 shell_println!(" ifconfig dns clear");
116 return Err(ShellError::InvalidArguments);
117 }
118 if args.len() == 2 && args[1].as_str() == "clear" {
119 write_path("/net/dns/set/0/0.0.0.0", b"1")?;
120 write_path("/net/dns/set/1/0.0.0.0", b"1")?;
121 write_path("/net/dns/set/2/0.0.0.0", b"1")?;
122 shell_println!("ifconfig: DNS cleared");
123 return Ok(());
124 }
125 write_path("/net/dns/set/0/0.0.0.0", b"1")?;
126 write_path("/net/dns/set/1/0.0.0.0", b"1")?;
127 write_path("/net/dns/set/2/0.0.0.0", b"1")?;
128 for (idx, ip) in args[1..].iter().enumerate() {
129 let path = alloc::format!("/net/dns/set/{}/{}", idx, ip);
130 write_path(&path, b"1")?;
131 }
132 shell_println!("ifconfig: DNS updated");
133 return Ok(());
134 }
135 "dhcp" => {
136 if args.len() != 2 {
137 shell_println!("Usage: ifconfig dhcp <on|off>");
138 return Err(ShellError::InvalidArguments);
139 }
140 match args[1].as_str() {
141 "on" => {
142 write_path("/net/dhcp/enable", b"1")?;
143 shell_println!("ifconfig: DHCP enabled");
144 return Ok(());
145 }
146 "off" => {
147 write_path("/net/dhcp/disable", b"1")?;
148 shell_println!("ifconfig: DHCP disabled");
149 return Ok(());
150 }
151 _ => {
152 shell_println!("Usage: ifconfig dhcp <on|off>");
153 return Err(ShellError::InvalidArguments);
154 }
155 }
156 }
157 _ => {
158 shell_println!("Usage: ifconfig");
159 shell_println!(" ifconfig inet <ipv4/prefix>");
160 shell_println!(" ifconfig gateway <ipv4|clear>");
161 shell_println!(" ifconfig dns <ipv4> [ipv4] [ipv4]");
162 shell_println!(" ifconfig dns clear");
163 shell_println!(" ifconfig dhcp <on|off>");
164 return Err(ShellError::InvalidArguments);
165 }
166 }
167 }
168
169 let read_file = |path: &str| -> String {
170 match vfs::open(path, OpenFlags::READ) {
171 Ok(fd) => {
172 let mut buf = [0u8; 96];
173 let n = vfs::read(fd, &mut buf).unwrap_or(0);
174 let _ = vfs::close(fd);
175 let s = core::str::from_utf8(&buf[..n]).unwrap_or("").trim();
176 String::from(s)
177 }
178 Err(_) => String::from("(unavailable)"),
179 }
180 };
181
182 let ip = read_file("/net/ip");
183 let ip6 = read_file("/net/ip6");
184 let gw = read_file("/net/gateway");
185 let gw6 = read_file("/net/ip6/gateway");
186 let route = read_file("/net/route");
187 let routes = read_file("/net/routes");
188 let dns = read_file("/net/dns");
189 let dhcp = read_file("/net/dhcp");
190
191 shell_println!("em0:");
192 shell_println!(" inet {}", ip);
193 shell_println!(" inet6 {}", ip6);
194 shell_println!(" dhcp {}", dhcp);
195 shell_println!(" gateway {}", gw);
196 shell_println!(" gateway6 {}", gw6);
197 shell_println!(" route {}", route);
198 shell_println!(" routes {}", routes);
199 shell_println!(" dns {}", dns);
200
201 Ok(())
202}
203
204fn write_path(path: &str, data: &[u8]) -> Result<(), ShellError> {
206 let fd = vfs::open(path, OpenFlags::WRITE).map_err(|_| ShellError::ExecutionFailed)?;
207 let res = vfs::write(fd, data).map(|_| ());
208 let _ = vfs::close(fd);
209 res.map_err(|_| ShellError::ExecutionFailed)
210}
211
212pub(super) fn cmd_net_impl(args: &[String]) -> Result<(), ShellError> {
214 if args.is_empty() {
215 shell_println!("Usage: net route <show|add|del|default> ...");
216 return Err(ShellError::InvalidArguments);
217 }
218
219 match args[0].as_str() {
220 "route" => {
221 if args.len() < 2 {
222 shell_println!("Usage: net route <show|add|del|default> ...");
223 return Err(ShellError::InvalidArguments);
224 }
225
226 match args[1].as_str() {
227 "show" => {
228 let routes = match vfs::open("/net/routes", OpenFlags::READ) {
229 Ok(fd) => {
230 let mut buf = [0u8; 256];
231 let n = vfs::read(fd, &mut buf).unwrap_or(0);
232 let _ = vfs::close(fd);
233 String::from(core::str::from_utf8(&buf[..n]).unwrap_or("").trim())
234 }
235 Err(_) => String::from("(unavailable)"),
236 };
237 shell_println!("routes:");
238 shell_println!("{}", routes);
239 Ok(())
240 }
241 "add" => {
242 if args.len() != 4 {
243 shell_println!("Usage: net route add <cidr> <gateway>");
244 return Err(ShellError::InvalidArguments);
245 }
246 let path = alloc::format!("/net/route/add/{}/{}", args[2], args[3]);
247 write_path(&path, b"1")?;
248 shell_println!("net route add: ok ({} via {})", args[2], args[3]);
249 Ok(())
250 }
251 "del" => {
252 if args.len() != 3 {
253 shell_println!("Usage: net route del <cidr>");
254 return Err(ShellError::InvalidArguments);
255 }
256 let path = alloc::format!("/net/route/del/{}", args[2]);
257 write_path(&path, b"1")?;
258 shell_println!("net route del: ok ({})", args[2]);
259 Ok(())
260 }
261 "default" => {
262 if args.len() == 4 && args[2].as_str() == "set" {
263 let path = alloc::format!("/net/route/default/set/{}", args[3]);
264 write_path(&path, b"1")?;
265 shell_println!("net route default: ok (via {})", args[3]);
266 return Ok(());
267 }
268 if args.len() == 3 && args[2].as_str() == "clear" {
269 write_path("/net/route/default/clear", b"1")?;
270 shell_println!("net route default: cleared");
271 return Ok(());
272 }
273 shell_println!("Usage: net route default <set <gateway>|clear>");
274 Err(ShellError::InvalidArguments)
275 }
276 _ => {
277 shell_println!("Usage: net route <show|add|del|default> ...");
278 Err(ShellError::InvalidArguments)
279 }
280 }
281 }
282 _ => {
283 shell_println!("Usage: net route <show|add|del|default> ...");
284 Err(ShellError::InvalidArguments)
285 }
286 }
287}