The Pedigree Project 0.1
ProcFs-network.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS 1
3
4#include "pedigree/kernel/process/TerminationDeferral.h"
5#include "pedigree/kernel/process/Thread.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/processor/ProcessorInformation.h"
8#include "pedigree/kernel/utilities/utility.h"
9
10#include "ProcFs.h"
13#include "modules/system/network-stack/NetworkStack.h"
14#include "network-namespace.h"
15
16namespace {
17class NetworkFile final : public File {
18 public:
19 NetworkFile(size_t inode, Filesystem* filesystem, File* parent)
20 : File(String("interfaces"), 0, 0, 0, inode, filesystem, 0, parent) {
21 setPermissionsOnly(FILE_UR | FILE_GR | FILE_OR);
22 setUidOnly(0);
23 setGidOnly(0);
24 }
25
26 uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool = true) override {
27 if (!size)
28 return 0;
29 const String contents = generateString();
30 if (location >= contents.length())
31 return 0;
32 if (size > contents.length() - location)
33 size = contents.length() - location;
34 MemoryCopy(reinterpret_cast<void*>(buffer), contents.cstr() + location, size);
35 return size;
36 }
37
38 uint64_t writeBytewise(uint64_t, uint64_t, uintptr_t, bool = true) override {
39 return 0;
40 }
41
42 size_t getSize() override {
43 return generateString().length();
44 }
45
46 private:
47 bool isBytewise() const override {
48 return true;
49 }
50
51 static String generateString() {
52 String contents;
53 const auto space = posix_sandbox_network(*Processor::information().getCurrentThread());
54 if (space) {
55 contents.Format("lo: %s, mtu 65536\n IPv4: %s Netmask: 255.0.0.0\n",
56 space->flags() & 1 ? "up" : "down",
57 space->address() ? "127.0.0.1" : "0.0.0.0");
58 return contents;
59 }
60 auto* stack = NetworkStack::instanceIfAvailable();
61 if (!stack)
62 return String("No network devices.\n");
63
64 TerminationDeferral lifetime;
65 for (size_t index = 0;; ++index) {
67 if (!stack->acquireDevice(index, device))
68 break;
69
70 struct Snapshot {
71 struct netif* interface;
72 char name[2];
73 unsigned number;
74 unsigned mtu;
75 bool up;
76 bool link;
77 bool defaultRoute;
78 unsigned macLength;
79 unsigned char mac[NETIF_MAX_HWADDR_LEN];
80 ip4_addr_t address, netmask, gateway;
81 ip6_addr_t ipv6[LWIP_IPV6_NUM_ADDRESSES];
82 bool ipv6Valid[LWIP_IPV6_NUM_ADDRESSES];
83 } snapshot = {};
84 snapshot.interface = device.interface();
85
86 // The lease pins the device; the callback copies configuration where
87 // DHCP and IPv6 address updates are serialized.
88 const auto copy = [](void* context) {
89 auto& result = *static_cast<Snapshot*>(context);
90 auto* iface = result.interface;
91 MemoryCopy(result.name, iface->name, sizeof(result.name));
92 result.number = iface->num;
93 result.mtu = iface->mtu;
94 result.up = netif_is_up(iface);
95 result.link = netif_is_link_up(iface);
96 result.defaultRoute = iface == netif_default;
97 result.macLength = iface->hwaddr_len;
98 if (result.macLength > sizeof(result.mac))
99 result.macLength = sizeof(result.mac);
100 MemoryCopy(result.mac, iface->hwaddr, result.macLength);
101 result.address = *netif_ip4_addr(iface);
102 result.netmask = *netif_ip4_netmask(iface);
103 result.gateway = *netif_ip4_gw(iface);
104 for (size_t i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
105 result.ipv6[i] = *netif_ip6_addr(iface, i);
106 result.ipv6Valid[i] = ip6_addr_isvalid(netif_ip6_addr_state(iface, i));
107 }
108 };
109 if (tcpip_callback_wait(copy, &snapshot) != ERR_OK) {
110 contents += "Network status unavailable.\n";
111 break;
112 }
113
114 String line;
115 line.Format("%c%c%u: %s, link flag %s, mtu %u%s\n", snapshot.name[0], snapshot.name[1],
116 snapshot.number, snapshot.up ? "up" : "down", snapshot.link ? "up" : "down",
117 snapshot.mtu, snapshot.defaultRoute ? ", default route" : "");
118 contents += line;
119 String name;
120 device.device()->getName(name);
121 contents += " Device: ";
122 contents += name;
123 contents += "\n MAC:";
124 for (unsigned i = 0; i < snapshot.macLength; ++i) {
125 line.Format("%s%02x", i ? ":" : " ", snapshot.mac[i]);
126 contents += line;
127 }
128 contents += "\n";
129 char address[IP4ADDR_STRLEN_MAX], netmask[IP4ADDR_STRLEN_MAX], gateway[IP4ADDR_STRLEN_MAX];
130 ip4addr_ntoa_r(&snapshot.address, address, sizeof(address));
131 ip4addr_ntoa_r(&snapshot.netmask, netmask, sizeof(netmask));
132 ip4addr_ntoa_r(&snapshot.gateway, gateway, sizeof(gateway));
133 line.Format(" IPv4: %s Netmask: %s Gateway: %s\n", address, netmask, gateway);
134 contents += line;
135 for (size_t i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
136 if (!snapshot.ipv6Valid[i])
137 continue;
138 char ipv6[IP6ADDR_STRLEN_MAX];
139 if (ip6addr_ntoa_r(&snapshot.ipv6[i], ipv6, sizeof(ipv6))) {
140 contents += " IPv6: ";
141 contents += ipv6;
142 contents += "\n";
143 }
144 }
145 }
146 if (!contents.length())
147 contents = String("No network devices.\n");
148 return contents;
149 }
150};
151
152class NetworkDevFile final : public File {
153 public:
154 NetworkDevFile(size_t inode, Filesystem* filesystem, File* parent)
155 : File(String("dev"), 0, 0, 0, inode, filesystem, 0, parent) {
156 setPermissionsOnly(FILE_UR | FILE_GR | FILE_OR);
157 setUidOnly(0);
158 setGidOnly(0);
159 }
160
161 uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool = true) override {
162 String contents = generateString();
163 if (location >= contents.length())
164 return 0;
165 size = min(size, contents.length() - location);
166 MemoryCopy(reinterpret_cast<void*>(buffer), contents.cstr() + location, size);
167 return size;
168 }
169 uint64_t writeBytewise(uint64_t, uint64_t, uintptr_t, bool = true) override {
170 return 0;
171 }
172 size_t getSize() override {
173 return generateString().length();
174 }
175
176 private:
177 bool isBytewise() const override {
178 return true;
179 }
180
181 static String generateString() {
182 String contents(
183 "Inter-| Receive | Transmit\n"
184 " face |bytes packets errs drop fifo frame compressed multicast|bytes packets "
185 "errs drop fifo colls carrier compressed\n");
186 if (posix_sandbox_network(*Processor::information().getCurrentThread())) {
187 contents += "lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
188 return contents;
189 }
190 auto* stack = NetworkStack::instanceIfAvailable();
191 if (!stack)
192 return contents;
193 TerminationDeferral lifetime;
194 for (size_t index = 0;; ++index) {
196 if (!stack->acquireDevice(index, device))
197 break;
198 struct Snapshot {
199 struct netif* interface;
200 char name[2];
201 unsigned number;
202 } snapshot = {device.interface(), {}, 0};
203 const auto copy = [](void* context) {
204 auto& result = *static_cast<Snapshot*>(context);
205 MemoryCopy(result.name, result.interface->name, sizeof(result.name));
206 result.number = result.interface->num;
207 };
208 if (tcpip_callback_wait(copy, &snapshot) != ERR_OK)
209 continue;
210 String line;
211 line.Format("%c%c%u: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n", snapshot.name[0], snapshot.name[1],
212 snapshot.number);
213 contents += line;
214 }
215 return contents;
216 }
217};
218} // namespace
219
220void ProcFs::initialiseNetworkFile() {
221 auto* directory = new ProcFsDirectory(String("net"), 0, 0, 0, getNextInode(), this, 0, m_pRoot);
222 directory->setPermissions(FILE_UR | FILE_UX | FILE_GR | FILE_GX | FILE_OR | FILE_OX);
223 m_pRoot->addEntry(directory->getName(), directory);
224 auto* interfaces = new NetworkFile(getNextInode(), this, directory);
225 directory->addEntry(interfaces->getName(), interfaces);
226 auto* devices = new NetworkDevFile(getNextInode(), this, directory);
227 directory->addEntry(devices->getName(), devices);
228}
Definition File.h:75
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition File.cc:1369
virtual bool isBytewise() const
Definition File.cc:1365
void setGidOnly(size_t gid)
Definition File.cc:1448
void setPermissionsOnly(uint32_t perms)
Definition File.cc:1440
void setUidOnly(size_t uid)
Definition File.cc:1444
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition File.cc:1376
static ProcessorInformation & information()
@ ERR_OK
Definition err.h:82
#define LWIP_IPV6_NUM_ADDRESSES
Definition opt.h:2179
#define netif_is_up(netif)
Definition netif.h:420
struct netif * netif_default
Definition netif.c:124
#define netif_is_link_up(netif)
Definition netif.h:432
#define NETIF_MAX_HWADDR_LEN
Definition netif.h:82
Definition netif.h:244
u8_t hwaddr_len
Definition netif.h:320
err_t tcpip_callback_wait(tcpip_callback_fn function, void *ctx)
Definition tcpip.c:365