The Pedigree Project 0.1
route-netlink.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/ConditionVariable.h"
5#include "pedigree/kernel/process/Process.h"
6#include "pedigree/kernel/process/TerminationDeferral.h"
7#include "pedigree/kernel/process/Thread.h"
8#include "pedigree/kernel/processor/Processor.h"
9#include "pedigree/kernel/processor/ProcessorInformation.h"
10#include "pedigree/kernel/syscallError.h"
11#include "pedigree/kernel/utilities/utility.h"
12
13#include <errno.h>
14
15#include "net-syscalls.h"
16#include "network-namespace.h"
17
18namespace {
19struct Header {
20 uint32_t length;
21 uint16_t type, flags;
22 uint32_t sequence, pid;
23};
24struct Address {
25 uint16_t family, padding;
26 uint32_t pid, groups;
27};
28struct Reply {
29 Header header;
30 int32_t error;
31 Header request;
32};
33static_assert(sizeof(Header) == 16 && sizeof(Reply) == 36, "Linux netlink ABI");
34int unsupported() {
35 syscallError(EOPNOTSUPP);
36 return -1;
37}
38uint32_t currentPid() {
39 auto* process = Processor::information().getCurrentThread()->getParent();
40 return process->getUserspaceId(process->pidNamespace().get());
41}
42
43class RouteSocket final : public NetworkSyscalls {
44 public:
45 RouteSocket(int type, int protocol, const NetworkNamespaceRef& space)
46 : NetworkSyscalls(16, type, protocol) {
47 m_NetworkNamespace = space;
48 }
49 ~RouteSocket() override {
51 }
52 bool create() override {
53 if ((getType() != SOCK_RAW && getType() != SOCK_DGRAM) || getProtocol()) {
54 syscallError(EPROTONOSUPPORT);
55 return false;
56 }
57 return true;
58 }
59 int bind(const sockaddr_storage* address, socklen_t length) override {
60 if (length < sizeof(Address) || address->ss_family != 16) {
61 syscallError(EINVAL);
62 return -1;
63 }
64 Address value;
65 MemoryCopy(&value, address, sizeof(value));
66 if (value.groups) {
67 syscallError(EOPNOTSUPP);
68 return -1;
69 }
70 LockGuard<Mutex> guard(m_Lock);
71 if (m_Closed) {
72 syscallError(EBADF);
73 return -1;
74 }
75 if (m_Bound) {
76 syscallError(EINVAL);
77 return -1;
78 }
79 m_Pid =
80 m_NetworkNamespace->reserveRoutePort(value.pid ? value.pid : currentPid(), value.pid != 0);
81 if (!m_Pid) {
82 return -1;
83 }
84 m_Bound = true;
85 return 0;
86 }
87 ssize_t sendto_msg(const msghdr* message, const SharedPointer<SocketRights>& rights) override {
88 if (rights || message->msg_flags) {
89 return unsupported();
90 }
91 if (message->msg_name) {
92 Address destination;
93 if (message->msg_namelen < sizeof(destination)) {
94 syscallError(EINVAL);
95 return -1;
96 }
97 MemoryCopy(&destination, message->msg_name, sizeof(destination));
98 if (destination.family != 16 || destination.pid || destination.groups) {
99 return unsupported();
100 }
101 }
102 uint8_t bytes[4096];
103 size_t length = 0;
104 for (size_t i = 0; i < static_cast<size_t>(message->msg_iovlen); ++i) {
105 const auto& vector = message->msg_iov[i];
106 if (vector.iov_len > sizeof(bytes) - length) {
107 syscallError(EMSGSIZE);
108 return -1;
109 }
110 MemoryCopy(bytes + length, vector.iov_base, vector.iov_len);
111 length += vector.iov_len;
112 }
113 Header header;
114 if (length < sizeof(header)) {
115 syscallError(EINVAL);
116 return -1;
117 }
118 MemoryCopy(&header, bytes, sizeof(header));
119 if (header.length < sizeof(header) || header.length > length || length - header.length > 3 ||
120 !(header.flags & 1)) {
121 syscallError(EINVAL);
122 return -1;
123 }
124 {
125 LockGuard<Mutex> guard(m_Lock);
126 if (m_Closed) {
127 syscallError(EBADF);
128 return -1;
129 }
130 if (m_Count == 16) {
131 syscallError(ENOBUFS);
132 return -1;
133 }
134 if (!m_Bound) {
135 m_Pid = m_NetworkNamespace->reserveRoutePort(currentPid(), false);
136 if (!m_Pid) {
137 return -1;
138 }
139 m_Bound = true;
140 }
141 const int error = request(header, bytes + sizeof(header), header.length - sizeof(header));
142 if (error || (header.flags & 4)) {
143 Reply& reply = m_Replies[(m_Head + m_Count) % 16];
144 reply = {{sizeof(Reply), 2, 0, header.sequence, m_Pid}, -error, header};
145 if (!m_Count++) {
146 ++m_Generations.read;
147 }
148 }
149 }
150 m_Changed.broadcast();
151 notifyReadiness(ReadyRead);
152 return length;
153 }
154 ssize_t recvfrom_msg(msghdr* message, SharedPointer<SocketRights>* rights) override {
155 TerminationDeferral lifetime;
156 if (rights) {
157 rights->reset();
158 }
159 const int inputFlags = message->msg_flags;
160 if (inputFlags & ~(MSG_DONTWAIT | MSG_TRUNC | MSG_WAITALL)) {
161 return unsupported();
162 }
163 m_Lock.acquire();
164 while (!m_Count && !m_Closed) {
165 if (!isBlocking() || (inputFlags & MSG_DONTWAIT)) {
166 m_Lock.release();
167 syscallError(EAGAIN);
168 return -1;
169 }
170 ConditionVariable::Error error = ConditionVariable::NoError;
171 if (!m_Changed.wait(m_Lock, error)) {
173 m_Lock.release();
174 }
175 syscallError(EINTR);
176 return -1;
177 }
178 }
179 if (m_Closed) {
180 m_Lock.release();
181 syscallError(EBADF);
182 return -1;
183 }
184 const Reply reply = m_Replies[m_Head];
185 m_Head = (m_Head + 1) % 16;
186 if (m_Count-- == 16) {
187 ++m_Generations.write;
188 }
189 m_Lock.release();
190 size_t copied = 0;
191 for (size_t i = 0; i < static_cast<size_t>(message->msg_iovlen) && copied < sizeof(reply);
192 ++i) {
193 const auto& vector = message->msg_iov[i];
194 const size_t amount =
195 vector.iov_len < sizeof(reply) - copied ? vector.iov_len : sizeof(reply) - copied;
196 MemoryCopy(vector.iov_base, reinterpret_cast<const uint8_t*>(&reply) + copied, amount);
197 copied += amount;
198 }
199 if (message->msg_name) {
200 const Address address = {16, 0, 0, 0};
201 MemoryCopy(message->msg_name, &address,
202 message->msg_namelen < sizeof(address) ? message->msg_namelen : sizeof(address));
203 message->msg_namelen = sizeof(address);
204 }
205 message->msg_controllen = 0;
206 message->msg_flags = copied < sizeof(reply) ? MSG_TRUNC : 0;
207 notifyReadiness(ReadyWrite);
208 return inputFlags & MSG_TRUNC ? sizeof(reply) : copied;
209 }
210 int getsockname(sockaddr_storage* address, socklen_t* length) override {
211 LockGuard<Mutex> guard(m_Lock);
212 const Address value = {16, 0, m_Pid, 0};
213 MemoryCopy(address, &value, *length < sizeof(value) ? *length : sizeof(value));
214 *length = sizeof(value);
215 return 0;
216 }
217 int getpeername(sockaddr_storage*, socklen_t*) override {
218 syscallError(ENOTCONN);
219 return -1;
220 }
221 int connect(const sockaddr_storage*, socklen_t) override {
222 return unsupported();
223 }
224 int listen(int) override {
225 return unsupported();
226 }
227 int accept(sockaddr_storage*, socklen_t*, int, DescriptorLease*) override {
228 return unsupported();
229 }
230 int shutdown(int) override {
231 return unsupported();
232 }
233 int setsockopt(int, int, const void*, socklen_t) override {
234 syscallError(ENOPROTOOPT);
235 return -1;
236 }
237 int getsockopt(int level, int option, void* value, socklen_t* length) override {
238 if (level != SOL_SOCKET || (option != SO_TYPE && option != SO_ERROR) || *length < sizeof(int)) {
239 syscallError(ENOPROTOOPT);
240 return -1;
241 }
242 *static_cast<int*>(value) = option == SO_TYPE ? getType() : 0;
243 *length = sizeof(int);
244 return 0;
245 }
246 bool canPoll() const override {
247 return true;
248 }
249 ReadyMask queryReady(bool reading, bool writing) override {
250 LockGuard<Mutex> guard(m_Lock);
251 return (m_Closed ? ReadyInvalid | ReadyHangup
252 : (reading && m_Count ? ReadyRead : ReadyNone) |
253 (writing && m_Count < 16 ? ReadyWrite : ReadyNone)) |
254 pendingReceiveReadiness();
255 }
257 LockGuard<Mutex> guard(m_Lock);
258 return withReceiveErrorGeneration(m_Generations);
259 }
260 void lastDescriptorClosed() override {
261 if (!beginDescriptorClose()) {
262 return;
263 }
264 {
265 LockGuard<Mutex> guard(m_Lock);
266 m_Closed = true;
267 if (m_Bound) {
268 m_NetworkNamespace->releaseRoutePort(m_Pid);
269 }
270 }
271 m_Changed.broadcast();
272 m_ReadinessNotifications.closeAndWait();
274 }
275
276 private:
277 int request(const Header& header, const uint8_t* payload, size_t length) {
278 if (header.type == 16) {
279 struct Link {
280 uint8_t family, padding;
281 uint16_t type;
282 int32_t index;
283 uint32_t flags, change;
284 } link;
285 if (length != sizeof(link)) {
286 return EINVAL;
287 }
288 MemoryCopy(&link, payload, sizeof(link));
289 if (link.index != 1) {
290 return ENODEV;
291 }
292 if (link.family || link.type) {
293 return EAFNOSUPPORT;
294 }
295 return m_NetworkNamespace->configureLink(link.flags, link.change);
296 }
297 if (header.type == 20) {
298 struct InterfaceAddress {
299 uint8_t family, prefix, flags, scope;
300 uint32_t index;
301 } address;
302 if (length < sizeof(address)) {
303 return EINVAL;
304 }
305 MemoryCopy(&address, payload, sizeof(address));
306 if (address.index != 1) {
307 return ENODEV;
308 }
309 if (address.family != AF_INET || address.scope != 254) {
310 return EAFNOSUPPORT;
311 }
312 if (!(header.flags & 0x400) || (address.flags & ~0x80)) {
313 return EOPNOTSUPP;
314 }
315 uint32_t local = 0, peer = 0;
316 size_t offset = sizeof(address);
317 while (offset < length) {
318 struct Attribute {
319 uint16_t length, type;
320 } attribute;
321 if (length - offset < sizeof(attribute)) {
322 return EINVAL;
323 }
324 MemoryCopy(&attribute, payload + offset, sizeof(attribute));
325 if (attribute.length != 8 || attribute.length > length - offset) {
326 return EINVAL;
327 }
328 uint32_t value;
329 MemoryCopy(&value, payload + offset + sizeof(attribute), sizeof(value));
330 if (attribute.type == 1) {
331 peer = BIG_TO_HOST32(value);
332 } else if (attribute.type == 2) {
333 local = BIG_TO_HOST32(value);
334 } else {
335 return EOPNOTSUPP;
336 }
337 offset += (attribute.length + 3) & ~size_t(3);
338 }
339 if (!local || (peer && peer != local)) {
340 return EINVAL;
341 }
342 return m_NetworkNamespace->configureAddress(local, address.prefix, header.flags & 0x200);
343 }
344 return EOPNOTSUPP;
345 }
346 Mutex m_Lock;
347 ConditionVariable m_Changed;
348 Reply m_Replies[16] = {};
349 size_t m_Head = 0, m_Count = 0;
350 uint32_t m_Pid = 0;
351 bool m_Bound = false, m_Closed = false;
352 ReadinessGenerations m_Generations;
353};
354} // namespace
355
356NetworkSyscalls* posix_route_netlink_socket(int type, int protocol,
357 const NetworkNamespaceRef& space) {
358 auto* socket = new RouteSocket(type, protocol, space);
359 if (!socket) {
360 syscallError(ENOMEM);
361 }
362 return socket;
363}
static bool mutexAcquired(Error error)
Definition Mutex.h:56
bool beginDescriptorClose()
virtual ReadyMask queryReady(bool reading, bool writing)
virtual void lastDescriptorClosed()
ReadinessGenerations readinessGenerations() override
size_t getUserspaceId() const
Definition Process.h:504
Process * getParent()
Definition Process.h:620
static ProcessorInformation & information()
void notifyReadiness(ReadyMask mask)
Definition Readiness.cc:201
void closeReadiness(ReadyMask mask=ReadyInvalid|ReadyHangup)
Definition Readiness.cc:208
T * get() const