The Pedigree Project 0.1
network-namespace.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS 1
3#include "network-namespace.h"
4#include "pedigree/kernel/LockGuard.h"
5#include "pedigree/kernel/syscallError.h"
6#include "pedigree/kernel/utilities/utility.h"
7
8#include <errno.h>
9
10#include "PosixSubsystem.h"
11#include "net-syscalls.h"
12#include <netinet/in.h>
13
14namespace {
15uint64_t nextNamespace = 0;
16constexpr uint32_t Loopback = 0x7f000001;
17uint64_t endpointKey(int type, uint32_t address, uint16_t port) {
18 return (uint64_t(type) << 48) | (uint64_t(address) << 16) | port;
19}
20} // namespace
21
22PosixNetworkNamespace::PosixNetworkNamespace(const UserNamespaceRef& owner)
23 : m_Owner(owner), m_Identity(__atomic_add_fetch(&nextNamespace, 1, __ATOMIC_RELAXED)) {}
24
25bool posix_network_namespace_prepare(const UserNamespaceRef& owner, NetworkNamespaceRef& result) {
27 if (!result) {
28 SYSCALL_ERROR(OutOfMemory);
29 return false;
30 }
31 return true;
32}
33
34bool PosixNetworkNamespace::usable() const {
35 LockGuard<Mutex> guard(m_Lock);
36 return m_Up && m_Address;
37}
38uint32_t PosixNetworkNamespace::flags() const {
39 LockGuard<Mutex> guard(m_Lock);
40 return 8 | (m_Up ? 1 | 0x40 : 0);
41}
42uint32_t PosixNetworkNamespace::address() const {
43 LockGuard<Mutex> guard(m_Lock);
44 return m_Address;
45}
46int PosixNetworkNamespace::configureAddress(uint32_t address, unsigned prefix, bool exclusive) {
47 if (!posix_namespace_capable(m_Owner, PosixCapabilities::NetAdmin)) {
48 return EPERM;
49 }
50 if (address != Loopback || prefix != 8) {
51 return EINVAL;
52 }
53 LockGuard<Mutex> guard(m_Lock);
54 if (exclusive && m_Address) {
55 return EEXIST;
56 }
57 m_Address = address;
58 return 0;
59}
60int PosixNetworkNamespace::configureLink(uint32_t flags, uint32_t changed) {
61 if (!posix_namespace_capable(m_Owner, PosixCapabilities::NetAdmin)) {
62 return EPERM;
63 }
64 if (changed & ~1U) {
65 return EOPNOTSUPP;
66 }
67 LockGuard<Mutex> guard(m_Lock);
68 if (changed & 1) {
69 m_Up = flags & 1;
70 }
71 return 0;
72}
73
74uint64_t PosixNetworkNamespace::reserve(int type, uint32_t address, uint16_t& port) {
75 if (port && port < 1024 && !posix_namespace_capable(m_Owner, PosixCapabilities::NetBindService)) {
76 syscallError(EACCES);
77 return 0;
78 }
79 LockGuard<Mutex> guard(m_Lock);
80 if (address && address != m_Address) {
81 syscallError(EADDRNOTAVAIL);
82 return 0;
83 }
84 auto available = [&](uint16_t candidate) {
85 for (auto it = m_Bindings.begin(); it != m_Bindings.end(); ++it) {
86 if ((it.key() >> 48) == static_cast<unsigned>(type) && uint16_t(it.key()) == candidate) {
87 const uint32_t existing = it.key() >> 16;
88 if (!existing || !address || existing == address) {
89 return false;
90 }
91 }
92 }
93 return true;
94 };
95 if (port) {
96 if (!available(port)) {
97 syscallError(EADDRINUSE);
98 return 0;
99 }
100 } else {
101 unsigned attempts = 0;
102 do {
103 if (++attempts > 28232) {
104 syscallError(EADDRINUSE);
105 return 0;
106 }
107 port = m_NextPort++;
108 if (m_NextPort == 61000) {
109 m_NextPort = 32768;
110 }
111 } while (!available(port));
112 }
113 const uint64_t token = ++m_NextToken;
114 if (!m_Bindings.tryInsert(endpointKey(type, address, port), token)) {
115 syscallError(ENOMEM);
116 return 0;
117 }
118 return token;
119}
120void PosixNetworkNamespace::release(uint64_t token) {
121 LockGuard<Mutex> guard(m_Lock);
122 for (auto it = m_Bindings.begin(); it != m_Bindings.end(); ++it) {
123 if (it.value() == token) {
124 m_Bindings.remove(it.key());
125 return;
126 }
127 }
128}
129uint32_t PosixNetworkNamespace::reserveRoutePort(uint32_t preferred, bool fixed) {
130 LockGuard<Mutex> guard(m_Lock);
131 if (m_RoutePorts.lookup(preferred)) {
132 if (fixed) {
133 syscallError(EADDRINUSE);
134 return 0;
135 }
136 do {
137 preferred = m_NextRoutePort++;
138 } while (!preferred || m_RoutePorts.lookup(preferred));
139 }
140 if (!m_RoutePorts.tryInsert(preferred, true)) {
141 syscallError(ENOMEM);
142 return 0;
143 }
144 return preferred;
145}
146void PosixNetworkNamespace::releaseRoutePort(uint32_t port) {
147 LockGuard<Mutex> guard(m_Lock);
148 m_RoutePorts.remove(port);
149}
150bool PosixNetworkNamespace::find(int type, uint32_t address, uint16_t port, uint32_t& boundAddress,
151 uint64_t& token) {
152 LockGuard<Mutex> guard(m_Lock);
153 if (!m_Up || !m_Address) {
154 syscallError(ENETUNREACH);
155 return false;
156 }
157 if (address != m_Address) {
158 syscallError(ENETUNREACH);
159 return false;
160 }
161 boundAddress = address;
162 token = m_Bindings.lookup(endpointKey(type, address, port));
163 if (!token) {
164 boundAddress = 0;
165 token = m_Bindings.lookup(endpointKey(type, 0, port));
166 }
167 if (!token) {
168 syscallError(ECONNREFUSED);
169 }
170 return token != 0;
171}
172
173int posix_network_ioctl(NetworkSyscalls& socket, unsigned long request, uintptr_t argument) {
174 const auto& space = socket.networkNamespace();
175 if (!space) {
176 syscallError(ENOTTY);
177 return -1;
178 }
179 if (request != 0x8910 && request != 0x8913 && request != 0x8914 && request != 0x8915 &&
180 request != 0x8916 && request != 0x891b && request != 0x8921 && request != 0x8933) {
181 syscallError(ENOTTY);
182 return -1;
183 }
184 struct IfRequest {
185 char name[16];
186 union {
187 int index;
188 int mtu;
189 uint16_t flags;
190 sockaddr address;
191 char padding[sizeof(uintptr_t) == 8 ? 24 : 16];
192 } value;
193 } snapshot = {};
194 if (!PosixSubsystem::copyFromUser(&snapshot, reinterpret_cast<void*>(argument),
195 sizeof(snapshot))) {
196 syscallError(EFAULT);
197 return -1;
198 }
199 if (request == 0x8910) {
200 if (snapshot.value.index != 1) {
201 syscallError(ENODEV);
202 return -1;
203 }
204 ByteSet(snapshot.name, 0, sizeof(snapshot.name));
205 MemoryCopy(snapshot.name, "lo", 2);
206 } else {
207 if (snapshot.name[0] != 'l' || snapshot.name[1] != 'o' || snapshot.name[2]) {
208 syscallError(ENODEV);
209 return -1;
210 }
211 switch (request) {
212 case 0x8933:
213 snapshot.value.index = 1;
214 break;
215 case 0x8913:
216 snapshot.value.flags = space->flags();
217 break;
218 case 0x8921:
219 snapshot.value.mtu = 65536;
220 break;
221 case 0x8915:
222 case 0x891b: {
223 sockaddr_in address = {};
224 address.sin_family = AF_INET;
225 address.sin_addr.s_addr = HOST_TO_BIG32(request == 0x8915 ? space->address() : 0xff000000);
226 MemoryCopy(&snapshot.value.address, &address, sizeof(address));
227 break;
228 }
229 case 0x8914: {
230 const int error = space->configureLink(snapshot.value.flags, 1);
231 if (error) {
232 syscallError(error);
233 return -1;
234 }
235 break;
236 }
237 case 0x8916: {
238 sockaddr_in address;
239 MemoryCopy(&address, &snapshot.value.address, sizeof(address));
240 const int error =
241 address.sin_family != AF_INET
242 ? EINVAL
243 : space->configureAddress(BIG_TO_HOST32(address.sin_addr.s_addr), 8, false);
244 if (error) {
245 syscallError(error);
246 return -1;
247 }
248 break;
249 }
250 }
251 }
252 if (!PosixSubsystem::copyToUser(reinterpret_cast<void*>(argument), &snapshot, sizeof(snapshot))) {
253 syscallError(EFAULT);
254 return -1;
255 }
256 return 0;
257}
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static SharedPointer< PosixNetworkNamespace > tryAllocate(Args...)
bool tryInsert(const K &key, const E &value)
Definition Tree.h:169
Iterator begin()
Definition Tree.h:402
void remove(const K &key)
Definition Tree.h:301
E lookup(const K &key) const
Definition Tree.h:193
Iterator end()
Definition Tree.h:427