1#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[cfg(feature = "alloc")]
7use alloc::{collections::BTreeMap, string::String};
8
9use crate::{error::KernelError, process::ProcessId};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
12pub enum NamespaceType {
13 Pid,
14 Mount,
15 Network,
16 User,
17 Ipc,
18 Uts,
19}
20
21impl core::fmt::Display for NamespaceType {
22 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
23 match self {
24 Self::Pid => write!(f, "pid"),
25 Self::Mount => write!(f, "mnt"),
26 Self::Network => write!(f, "net"),
27 Self::User => write!(f, "user"),
28 Self::Ipc => write!(f, "ipc"),
29 Self::Uts => write!(f, "uts"),
30 }
31 }
32}
33
34#[cfg(feature = "alloc")]
35pub struct PidNamespace {
36 container_to_global: BTreeMap<u32, ProcessId>,
37 global_to_container: BTreeMap<u64, u32>,
38 next_container_pid: u32,
39}
40
41#[cfg(feature = "alloc")]
42impl PidNamespace {
43 pub fn new() -> Self {
44 Self {
45 container_to_global: BTreeMap::new(),
46 global_to_container: BTreeMap::new(),
47 next_container_pid: 1,
48 }
49 }
50 pub fn add_process(&mut self, global_pid: ProcessId) -> u32 {
51 let cpid = self.next_container_pid;
52 self.next_container_pid += 1;
53 self.container_to_global.insert(cpid, global_pid);
54 self.global_to_container.insert(global_pid.0, cpid);
55 cpid
56 }
57 pub fn remove_process(&mut self, global_pid: ProcessId) {
58 if let Some(cpid) = self.global_to_container.remove(&global_pid.0) {
59 self.container_to_global.remove(&cpid);
60 }
61 }
62 pub fn translate_pid(&self, container_pid: u32) -> Option<ProcessId> {
63 self.container_to_global.get(&container_pid).copied()
64 }
65 pub fn container_pid(&self, global_pid: ProcessId) -> Option<u32> {
66 self.global_to_container.get(&global_pid.0).copied()
67 }
68 pub fn process_count(&self) -> usize {
69 self.container_to_global.len()
70 }
71 pub fn contains(&self, global_pid: ProcessId) -> bool {
72 self.global_to_container.contains_key(&global_pid.0)
73 }
74}
75
76#[cfg(feature = "alloc")]
77impl Default for PidNamespace {
78 fn default() -> Self {
79 Self::new()
80 }
81}
82
83#[cfg(feature = "alloc")]
84pub struct MountNamespace {
85 root_path: String,
86 mounts: BTreeMap<String, String>,
87}
88
89#[cfg(feature = "alloc")]
90impl MountNamespace {
91 pub fn new(root: &str) -> Self {
92 Self {
93 root_path: String::from(root),
94 mounts: BTreeMap::new(),
95 }
96 }
97 pub fn set_root(&mut self, path: &str) {
98 self.root_path = String::from(path);
99 }
100 pub fn root(&self) -> &str {
101 &self.root_path
102 }
103 pub fn add_mount(&mut self, mountpoint: &str, source: &str) {
104 self.mounts
105 .insert(String::from(mountpoint), String::from(source));
106 }
107 pub fn remove_mount(&mut self, mountpoint: &str) {
108 self.mounts.remove(mountpoint);
109 }
110 pub fn mount_count(&self) -> usize {
111 self.mounts.len()
112 }
113 pub fn resolve_path(&self, path: &str) -> String {
114 if path.starts_with('/') {
115 alloc::format!("{}{}", self.root_path, path)
116 } else {
117 alloc::format!("{}/{}", self.root_path, path)
118 }
119 }
120}
121
122#[cfg(feature = "alloc")]
123impl Default for MountNamespace {
124 fn default() -> Self {
125 Self::new("/")
126 }
127}
128
129#[cfg(feature = "alloc")]
130#[derive(Debug, Clone)]
131pub struct VethInterface {
132 pub name: String,
133 pub peer_name: String,
134 pub ipv4_addr: Option<u32>,
135 pub is_up: bool,
136}
137
138#[cfg(feature = "alloc")]
139pub struct NetworkNamespace {
140 interfaces: BTreeMap<String, VethInterface>,
141 has_loopback: bool,
142}
143
144#[cfg(feature = "alloc")]
145impl NetworkNamespace {
146 pub fn new() -> Self {
147 Self {
148 interfaces: BTreeMap::new(),
149 has_loopback: true,
150 }
151 }
152 pub fn create_veth(&mut self, name: &str, peer_name: &str) -> Result<(), KernelError> {
153 self.interfaces.insert(
154 String::from(name),
155 VethInterface {
156 name: String::from(name),
157 peer_name: String::from(peer_name),
158 ipv4_addr: None,
159 is_up: false,
160 },
161 );
162 Ok(())
163 }
164 pub fn interface_up(&mut self, name: &str) -> Result<(), KernelError> {
165 match self.interfaces.get_mut(name) {
166 Some(i) => {
167 i.is_up = true;
168 Ok(())
169 }
170 None => Err(KernelError::NotFound {
171 resource: "network interface",
172 id: 0,
173 }),
174 }
175 }
176 pub fn assign_ipv4(&mut self, name: &str, addr: u32) -> Result<(), KernelError> {
177 match self.interfaces.get_mut(name) {
178 Some(i) => {
179 i.ipv4_addr = Some(addr);
180 Ok(())
181 }
182 None => Err(KernelError::NotFound {
183 resource: "network interface",
184 id: 0,
185 }),
186 }
187 }
188 pub fn interface_count(&self) -> usize {
189 self.interfaces.len()
190 }
191 pub fn has_loopback(&self) -> bool {
192 self.has_loopback
193 }
194}
195
196#[cfg(feature = "alloc")]
197impl Default for NetworkNamespace {
198 fn default() -> Self {
199 Self::new()
200 }
201}
202
203#[cfg(feature = "alloc")]
204pub struct UtsNamespace {
205 hostname: String,
206 domainname: String,
207}
208
209#[cfg(feature = "alloc")]
210impl UtsNamespace {
211 pub fn new(hostname: &str) -> Self {
212 Self {
213 hostname: String::from(hostname),
214 domainname: String::new(),
215 }
216 }
217 pub fn hostname(&self) -> &str {
218 &self.hostname
219 }
220 pub fn set_hostname(&mut self, name: &str) {
221 self.hostname = String::from(name);
222 }
223 pub fn domainname(&self) -> &str {
224 &self.domainname
225 }
226 pub fn set_domainname(&mut self, name: &str) {
227 self.domainname = String::from(name);
228 }
229}
230
231#[cfg(feature = "alloc")]
232impl Default for UtsNamespace {
233 fn default() -> Self {
234 Self::new("localhost")
235 }
236}
237
238#[cfg(feature = "alloc")]
239pub struct NamespaceSet {
240 pub pid: PidNamespace,
241 pub mount: MountNamespace,
242 pub network: NetworkNamespace,
243 pub uts: UtsNamespace,
244}
245
246#[cfg(feature = "alloc")]
247impl NamespaceSet {
248 pub fn new(hostname: &str) -> Self {
249 Self {
250 pid: PidNamespace::new(),
251 mount: MountNamespace::new("/"),
252 network: NetworkNamespace::new(),
253 uts: UtsNamespace::new(hostname),
254 }
255 }
256}
257
258#[cfg(feature = "alloc")]
259impl Default for NamespaceSet {
260 fn default() -> Self {
261 Self::new("container")
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268
269 #[test]
270 fn test_pid_namespace() {
271 let mut ns = PidNamespace::new();
272 let cpid = ns.add_process(ProcessId(42));
273 assert_eq!(cpid, 1);
274 assert_eq!(ns.translate_pid(1), Some(ProcessId(42)));
275 assert_eq!(ns.container_pid(ProcessId(42)), Some(1));
276 ns.remove_process(ProcessId(42));
277 assert_eq!(ns.process_count(), 0);
278 }
279
280 #[test]
281 fn test_mount_namespace() {
282 let ns = MountNamespace::new("/containers/test");
283 assert_eq!(ns.resolve_path("/bin/sh"), "/containers/test/bin/sh");
284 }
285
286 #[test]
287 fn test_network_namespace() {
288 let mut ns = NetworkNamespace::new();
289 assert!(ns.create_veth("veth0", "veth0-host").is_ok());
290 assert_eq!(ns.interface_count(), 1);
291 assert!(ns.interface_up("veth0").is_ok());
292 assert!(ns.interface_up("nonexistent").is_err());
293 }
294
295 #[test]
296 fn test_uts_namespace() {
297 let mut ns = UtsNamespace::new("test");
298 assert_eq!(ns.hostname(), "test");
299 ns.set_hostname("new");
300 assert_eq!(ns.hostname(), "new");
301 }
302
303 #[test]
304 fn test_namespace_type_display() {
305 assert_eq!(alloc::format!("{}", NamespaceType::Pid), "pid");
306 assert_eq!(alloc::format!("{}", NamespaceType::Mount), "mnt");
307 }
308}