The Pedigree Project 0.1
network-loopback.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS 1
3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/Processor.h"
6#include "pedigree/kernel/processor/ProcessorInformation.h"
7#include "pedigree/kernel/syscallError.h"
8#include "pedigree/kernel/utilities/utility.h"
9
10#include <errno.h>
11#include <stddef.h>
12
13#include "FileDescriptor.h"
14#include "net-syscalls.h"
15#include "network-namespace.h"
16#include <netinet/in.h>
17#include <netinet/tcp.h>
18#include <sys/un.h>
19
20namespace {
21constexpr uint32_t Loopback = 0x7f000001;
22constexpr socklen_t EndpointLength = offsetof(sockaddr_un, sun_path) + 16;
23
24void endpoint(sockaddr_storage& output, int type, uint32_t address, uint16_t port, uint64_t token) {
25 ByteSet(&output, 0, sizeof(output));
26 auto* local = reinterpret_cast<sockaddr_un*>(&output);
27 local->sun_family = AF_UNIX;
28 local->sun_path[1] = type;
29 MemoryCopy(local->sun_path + 2, &address, 4);
30 MemoryCopy(local->sun_path + 6, &port, 2);
31 MemoryCopy(local->sun_path + 8, &token, 8);
32}
33sockaddr_in decode(const sockaddr_storage& address, socklen_t length) {
34 sockaddr_in result = {};
35 result.sin_family = AF_INET;
36 if (length >= EndpointLength) {
37 const auto* local = reinterpret_cast<const sockaddr_un*>(&address);
38 uint32_t ip;
39 uint16_t port;
40 MemoryCopy(&ip, local->sun_path + 2, 4);
41 MemoryCopy(&port, local->sun_path + 6, 2);
42 result.sin_addr.s_addr = HOST_TO_BIG32(ip);
43 result.sin_port = HOST_TO_BIG16(port);
44 }
45 return result;
46}
47void copyAddress(const sockaddr_in& source, sockaddr_storage* destination, socklen_t* length) {
48 if (destination) {
49 MemoryCopy(destination, &source, *length < sizeof(source) ? *length : sizeof(source));
50 }
51 *length = sizeof(source);
52}
53bool internetAddress(const sockaddr_storage* input, socklen_t length, uint32_t& ip,
54 uint16_t& port) {
55 if (!input || length < sizeof(sockaddr_in) || input->ss_family != AF_INET) {
56 syscallError(EINVAL);
57 return false;
58 }
59 const auto* address = reinterpret_cast<const sockaddr_in*>(input);
60 ip = BIG_TO_HOST32(address->sin_addr.s_addr);
61 port = BIG_TO_HOST16(address->sin_port);
62 return true;
63}
64
65class LoopbackSocket final : public UnixSocketSyscalls {
66 public:
67 LoopbackSocket(int type, int protocol, const NetworkNamespaceRef& space, bool accepted = false)
68 : UnixSocketSyscalls(AF_INET, type, protocol), m_Accepted(accepted) {
69 m_NetworkNamespace = space;
70 }
71 ~LoopbackSocket() override {
73 }
74 bool create() override {
75 if ((getType() != SOCK_STREAM && getType() != SOCK_DGRAM) ||
76 (getProtocol() &&
77 getProtocol() != (getType() == SOCK_STREAM ? IPPROTO_TCP : IPPROTO_UDP))) {
78 syscallError(EPROTONOSUPPORT);
79 return false;
80 }
82 }
83 int bind(const sockaddr_storage* address, socklen_t length) override {
84 uint32_t ip;
85 uint16_t port;
86 if (!internetAddress(address, length, ip, port)) {
87 return -1;
88 }
89 LockGuard<Mutex> guard(m_BindLock);
90 if (m_Closing) {
91 syscallError(EBADF);
92 return -1;
93 }
94 if (m_Token || m_Accepted) {
95 syscallError(EINVAL);
96 return -1;
97 }
98 return bindLocked(ip, port);
99 }
100 int listen(int backlog) override {
101 if (!ensureBound(0)) {
102 return -1;
103 }
104 return UnixSocketSyscalls::listen(backlog);
105 }
106 int connect(const sockaddr_storage* address, socklen_t length) override {
107 sockaddr_storage destination;
108 if (!destinationEndpoint(address, length, destination) || !ensureBound(Loopback)) {
109 return -1;
110 }
111 const int result = UnixSocketSyscalls::connect(&destination, EndpointLength);
112 if (result == 0 || Processor::information().getCurrentThread()->getErrno() == EINPROGRESS) {
113 LockGuard<Mutex> guard(m_BindLock);
114 MemoryCopy(&m_Peer, address, sizeof(m_Peer));
115 if (!m_Peer.sin_addr.s_addr) {
116 m_Peer.sin_addr.s_addr = HOST_TO_BIG32(Loopback);
117 }
118 m_HasPeer = true;
119 }
120 return result;
121 }
122 ssize_t sendto_msg(const msghdr* message, const SharedPointer<SocketRights>& rights) override {
123 if (rights) {
124 syscallError(EOPNOTSUPP);
125 return -1;
126 }
127 if (!m_NetworkNamespace->usable()) {
128 syscallError(ENETUNREACH);
129 return -1;
130 }
131 if (!ensureBound(Loopback)) {
132 return -1;
133 }
134 sockaddr_storage destination;
135 msghdr translated = *message;
136 if (message->msg_name) {
137 if (!destinationEndpoint(reinterpret_cast<const sockaddr_storage*>(message->msg_name),
138 message->msg_namelen, destination)) {
139 return -1;
140 }
141 translated.msg_name = &destination;
142 translated.msg_namelen = EndpointLength;
143 }
144 return UnixSocketSyscalls::sendto_msg(&translated, {});
145 }
146 ssize_t recvfrom_msg(msghdr* message, SharedPointer<SocketRights>* rights) override {
147 sockaddr_storage source = {};
148 msghdr translated = *message;
149 translated.msg_name = &source;
150 translated.msg_namelen = sizeof(source);
151 const ssize_t result = UnixSocketSyscalls::recvfrom_msg(&translated, rights);
152 if (result < 0) {
153 return result;
154 }
155 message->msg_flags = translated.msg_flags;
156 message->msg_controllen = 0;
157 if (message->msg_name && getType() == SOCK_DGRAM) {
158 auto decoded = decode(source, translated.msg_namelen);
159 if (!decoded.sin_addr.s_addr) {
160 decoded.sin_addr.s_addr = HOST_TO_BIG32(Loopback);
161 }
162 socklen_t length = message->msg_namelen;
163 copyAddress(decoded, reinterpret_cast<sockaddr_storage*>(message->msg_name), &length);
164 message->msg_namelen = length;
165 } else {
166 message->msg_namelen = 0;
167 }
168 return result;
169 }
170 int accept(sockaddr_storage* address, socklen_t* length, int flags,
171 DescriptorLease* accepted) override {
172 sockaddr_storage source = {};
173 socklen_t sourceLength = sizeof(source);
174 const int result = UnixSocketSyscalls::accept(&source, &sourceLength, flags, accepted);
175 if (result >= 0 && length) {
176 copyAddress(decode(source, sourceLength), address, length);
177 }
178 return result;
179 }
180 int getsockname(sockaddr_storage* address, socklen_t* length) override {
181 sockaddr_storage source = {};
182 socklen_t sourceLength = sizeof(source);
183 if (UnixSocketSyscalls::getsockname(&source, &sourceLength)) {
184 return -1;
185 }
186 auto decoded = decode(source, sourceLength);
187 if (m_Accepted && !decoded.sin_addr.s_addr) {
188 decoded.sin_addr.s_addr = HOST_TO_BIG32(Loopback);
189 }
190 copyAddress(decoded, address, length);
191 return 0;
192 }
193 int getpeername(sockaddr_storage* address, socklen_t* length) override {
194 if (!m_Accepted) {
195 LockGuard<Mutex> guard(m_BindLock);
196 if (!m_HasPeer) {
197 syscallError(ENOTCONN);
198 return -1;
199 }
200 copyAddress(m_Peer, address, length);
201 return 0;
202 }
203 sockaddr_storage source = {};
204 socklen_t sourceLength = sizeof(source);
205 if (UnixSocketSyscalls::getpeername(&source, &sourceLength)) {
206 return -1;
207 }
208 auto decoded = decode(source, sourceLength);
209 if (!decoded.sin_addr.s_addr) {
210 decoded.sin_addr.s_addr = HOST_TO_BIG32(Loopback);
211 }
212 copyAddress(decoded, address, length);
213 return 0;
214 }
215 int setsockopt(int level, int option, const void* value, socklen_t length) override {
216 if ((level == IPPROTO_TCP && option == TCP_NODELAY && getType() == SOCK_STREAM) ||
217 (level == SOL_SOCKET && option == SO_REUSEADDR)) {
218 if (length < sizeof(int)) {
219 syscallError(EINVAL);
220 return -1;
221 }
222 LockGuard<Mutex> guard(m_BindLock);
223 (level == IPPROTO_TCP ? m_NoDelay : m_ReuseAddress) = *static_cast<const int*>(value) != 0;
224 return 0;
225 }
226 syscallError(ENOPROTOOPT);
227 return -1;
228 }
229 int getsockopt(int level, int option, void* value, socklen_t* length) override {
230 if ((level == IPPROTO_TCP && option == TCP_NODELAY) ||
231 (level == SOL_SOCKET && option == SO_REUSEADDR)) {
232 if (*length < sizeof(int)) {
233 syscallError(EINVAL);
234 return -1;
235 }
236 LockGuard<Mutex> guard(m_BindLock);
237 *static_cast<int*>(value) = level == IPPROTO_TCP ? m_NoDelay : m_ReuseAddress;
238 *length = sizeof(int);
239 return 0;
240 }
241 if (level == SOL_SOCKET && (option == SO_TYPE || option == SO_ERROR)) {
242 return UnixSocketSyscalls::getsockopt(level, option, value, length);
243 }
244 syscallError(ENOPROTOOPT);
245 return -1;
246 }
247 void lastDescriptorClosed() override {
248 uint64_t token;
249 {
250 LockGuard<Mutex> guard(m_BindLock);
251 if (m_Closing) {
252 return;
253 }
254 m_Closing = true;
255 token = m_Token;
256 m_Token = 0;
257 }
258 const auto space = m_NetworkNamespace;
260 if (token) {
261 space->release(token);
262 }
263 }
264
265 protected:
266 UnixSocketSyscalls* createAcceptedSocket() override {
267 return new LoopbackSocket(getType(), getProtocol(), m_NetworkNamespace, true);
268 }
269
270 private:
271 int bindLocked(uint32_t ip, uint16_t port) {
272 const uint64_t token = m_NetworkNamespace->reserve(getType(), ip, port);
273 if (!token) {
274 return -1;
275 }
276 sockaddr_storage local;
277 endpoint(local, getType(), ip, port, token);
278 if (UnixSocketSyscalls::bind(&local, EndpointLength)) {
279 m_NetworkNamespace->release(token);
280 return -1;
281 }
282 m_Token = token;
283 return 0;
284 }
285 bool ensureBound(uint32_t address) {
286 LockGuard<Mutex> guard(m_BindLock);
287 if (m_Closing) {
288 syscallError(EBADF);
289 return false;
290 }
291 return m_Accepted || m_Token || bindLocked(address, 0) == 0;
292 }
293 bool destinationEndpoint(const sockaddr_storage* address, socklen_t length,
294 sockaddr_storage& output) {
295 uint32_t ip, boundIp;
296 uint16_t port;
297 uint64_t token;
298 if (!internetAddress(address, length, ip, port)) {
299 return false;
300 }
301 if (!ip) {
302 ip = Loopback;
303 }
304 if (!m_NetworkNamespace->find(getType(), ip, port, boundIp, token)) {
305 return false;
306 }
307 endpoint(output, getType(), boundIp, port, token);
308 return true;
309 }
310 Mutex m_BindLock;
311 uint64_t m_Token = 0;
312 const bool m_Accepted;
313 bool m_HasPeer = false, m_NoDelay = false, m_ReuseAddress = false, m_Closing = false;
314 sockaddr_in m_Peer = {};
315};
316} // namespace
317
318NetworkSyscalls* posix_network_socket(int domain, int type, int protocol,
319 const NetworkNamespaceRef& space) {
320 if (domain == AF_INET) {
321 auto* socket = new LoopbackSocket(type, protocol, space);
322 if (!socket) {
323 syscallError(ENOMEM);
324 }
325 return socket;
326 }
327 if (domain == 16) {
328 return posix_route_netlink_socket(type, protocol, space);
329 }
330 syscallError(EAFNOSUPPORT);
331 return nullptr;
332}
Definition Mutex.h:56
static ProcessorInformation & information()
virtual int listen(int backlog)
virtual void lastDescriptorClosed()
virtual int bind(const struct sockaddr_storage *address, socklen_t addrlen)
virtual bool create()
Implementation-specific final socket creation logic.