vfs: check userland buffers before reading them.
[haiku.git] / src / system / kernel / fs / vfs_net_boot.cpp
blob8880c890f2ba55b69be75a8ce9822f175ad343d1
1 /*
2 * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include "vfs_net_boot.h"
8 #include <dirent.h>
9 #include <errno.h>
10 #include <net/if.h>
11 #include <net/if_dl.h>
12 #include <net/if_types.h>
13 #include <netinet/in.h>
14 #include <stdio.h>
15 #include <sys/socket.h>
16 #include <sys/sockio.h>
18 #include <DiskDeviceTypes.h>
20 #include <disk_device_manager/KDiskDevice.h>
22 #include <KPath.h>
25 static bool
26 string_starts_with(const char* string, const char* prefix)
28 size_t stringLen = strlen(string);
29 size_t prefixLen = strlen(prefix);
30 return (stringLen >= prefixLen && strncmp(string, prefix, prefixLen) == 0);
34 static bool
35 is_net_device(KDiskDevice* device)
37 const char* path = device->Path();
38 return (string_starts_with(path, "/dev/disk/virtual/nbd/")
39 || string_starts_with(path, "/dev/disk/virtual/remote_disk/"));
43 static int
44 compare_partitions_net_devices(const void *_a, const void *_b)
46 KPartition* a = *(KPartition**)_a;
47 KPartition* b = *(KPartition**)_b;
49 bool aIsNetDevice = is_net_device(a->Device());
50 bool bIsNetDevice = is_net_device(b->Device());
52 int compare = (int)aIsNetDevice - (int)bIsNetDevice;
53 if (compare != 0)
54 return compare;
56 return compare_image_boot(_a, _b);
60 class NetStackInitializer {
61 public:
62 NetStackInitializer(uint64 clientMAC, uint32 clientIP, uint32 netMask)
64 fSocket(-1),
65 fLinkSocket(-1),
66 fClientMAC(clientMAC),
67 fClientIP(clientIP),
68 fNetMask(netMask),
69 fFoundInterface(false),
70 fConfiguredInterface(false)
74 ~NetStackInitializer()
76 // close control sockets
77 if (fSocket >= 0)
78 close(fSocket);
80 if (fLinkSocket >= 0)
81 close(fLinkSocket);
84 status_t Init()
86 // open a control socket for playing with the stack
87 fSocket = socket(AF_INET, SOCK_DGRAM, 0);
88 if (fSocket < 0) {
89 dprintf("NetStackInitializer: Failed to open socket: %s\n",
90 strerror(errno));
91 return errno;
94 // ... and a link level socket
95 fLinkSocket = socket(AF_LINK, SOCK_DGRAM, 0);
96 if (fLinkSocket < 0) {
97 dprintf("NetStackInitializer: Failed to open link level socket:"
98 " %s\n", strerror(errno));
99 return errno;
103 // now iterate through the existing network devices
104 KPath path;
105 status_t error = path.SetTo("/dev/net");
106 if (error != B_OK)
107 return error;
109 _ScanDevices(path);
111 return fConfiguredInterface ? B_OK : B_ERROR;
114 private:
115 void _ScanDevices(KPath& path)
117 DIR* dir = opendir(path.Path());
118 if (!dir) {
119 dprintf("NetStackInitializer: Failed to opendir() \"%s\": %s\n",
120 path.Path(), strerror(errno));
121 return;
124 while (dirent* entry = readdir(dir)) {
125 // skip "." and ".."
126 if (strcmp(entry->d_name, ".") == 0
127 || strcmp(entry->d_name, "..") == 0) {
128 continue;
131 path.Append(entry->d_name);
133 struct stat st;
134 if (stat(path.Path(), &st) == 0) {
135 if (S_ISDIR(st.st_mode))
136 _ScanDevices(path);
137 else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
138 _ScanDevice(path.Path());
141 path.RemoveLeaf();
143 if (fFoundInterface)
144 break;
147 closedir(dir);
150 void _ScanDevice(const char* path)
152 dprintf("NetStackInitializer: scanning device %s\n", path);
154 // check if this interface is already known
155 ifreq request;
156 if (strlen(path) >= IF_NAMESIZE)
157 return;
158 strcpy(request.ifr_name, path);
160 if (ioctl(fSocket, SIOCGIFINDEX, &request, sizeof(request)) < 0) {
161 // not known yet -- add it
162 ifaliasreq aliasRequest;
163 strcpy(aliasRequest.ifra_name, path);
164 aliasRequest.ifra_addr.ss_family = AF_UNSPEC;
165 aliasRequest.ifra_addr.ss_len = 2;
166 aliasRequest.ifra_broadaddr.ss_family = AF_UNSPEC;
167 aliasRequest.ifra_broadaddr.ss_len = 2;
168 aliasRequest.ifra_mask.ss_family = AF_UNSPEC;
169 aliasRequest.ifra_mask.ss_len = 2;
171 if (ioctl(fSocket, SIOCAIFADDR, &aliasRequest,
172 sizeof(aliasRequest)) < 0) {
173 dprintf("NetStackInitializer: adding interface failed for "
174 "device %s: %s\n", path, strerror(errno));
175 return;
179 // bring the interface up (get flags, add IFF_UP)
180 if (ioctl(fSocket, SIOCGIFFLAGS, &request, sizeof(request)) < 0) {
181 dprintf("NetStackInitializer: getting flags failed for interface "
182 "%s: %s\n", path, strerror(errno));
183 return;
186 int interfaceFlags = request.ifr_flags;
187 if (!(interfaceFlags & IFF_UP)) {
188 interfaceFlags |= IFF_UP;
189 request.ifr_flags = interfaceFlags;
190 if (ioctl(fSocket, SIOCSIFFLAGS, &request, sizeof(request)) < 0) {
191 dprintf("NetStackInitializer: failed to bring interface up "
192 "%s: %s\n", path, strerror(errno));
193 return;
197 // get the MAC address
198 if (ioctl(fLinkSocket, SIOCGIFADDR, &request, sizeof(request)) < 0) {
199 dprintf("NetStackInitializer: Getting MAC addresss failed for "
200 "interface %s: %s\n", path, strerror(errno));
201 return;
204 sockaddr_dl& link = *(sockaddr_dl*)&request.ifr_addr;
205 if (link.sdl_type != IFT_ETHER)
206 return;
208 if (link.sdl_alen == 0)
209 return;
211 uint8* macBytes = (uint8 *)LLADDR(&link);
212 uint64 macAddress = ((uint64)macBytes[0] << 40)
213 | ((uint64)macBytes[1] << 32)
214 | ((uint64)macBytes[2] << 24)
215 | ((uint64)macBytes[3] << 16)
216 | ((uint64)macBytes[4] << 8)
217 | (uint64)macBytes[5];
219 dprintf("NetStackInitializer: found ethernet interface with MAC "
220 "address %02x:%02x:%02x:%02x:%02x:%02x; which is%s the one we're "
221 "looking for\n", macBytes[0], macBytes[1], macBytes[2], macBytes[3],
222 macBytes[4], macBytes[5], (macAddress == fClientMAC ? "" : "n't"));
224 if (macAddress != fClientMAC)
225 return;
227 fFoundInterface = true;
229 // configure the interface
231 // set IP address
232 sockaddr_in& address = *(sockaddr_in*)&request.ifr_addr;
233 address.sin_family = AF_INET;
234 address.sin_len = sizeof(sockaddr_in);
235 address.sin_port = 0;
236 address.sin_addr.s_addr = htonl(fClientIP);
237 memset(&address.sin_zero[0], 0, sizeof(address.sin_zero));
238 if (ioctl(fSocket, SIOCSIFADDR, &request, sizeof(request)) < 0) {
239 dprintf("NetStackInitializer: Setting IP addresss failed for "
240 "interface %s: %s\n", path, strerror(errno));
241 return;
244 // set net mask
245 address.sin_addr.s_addr = htonl(fNetMask);
246 if (ioctl(fSocket, SIOCSIFNETMASK, &request, sizeof(request)) < 0) {
247 dprintf("NetStackInitializer: Setting net mask failed for "
248 "interface %s: %s\n", path, strerror(errno));
249 return;
252 // set broadcast address
253 address.sin_addr.s_addr = htonl(fClientIP | ~fNetMask);
254 if (ioctl(fSocket, SIOCSIFBRDADDR, &request, sizeof(request)) < 0) {
255 dprintf("NetStackInitializer: Setting broadcast address failed for "
256 "interface %s: %s\n", path, strerror(errno));
257 return;
260 // set IFF_BROADCAST
261 if (!(interfaceFlags & IFF_BROADCAST)) {
262 interfaceFlags |= IFF_BROADCAST;
263 request.ifr_flags = interfaceFlags;
264 if (ioctl(fSocket, SIOCSIFFLAGS, &request, sizeof(request)) < 0) {
265 dprintf("NetStackInitializer: failed to set IFF_BROADCAST flag "
266 "for interface %s: %s\n", path, strerror(errno));
267 return;
271 // set default route; remove previous one, if any
272 route_entry route;
273 memset(&route, 0, sizeof(route_entry));
274 route.flags = RTF_STATIC | RTF_DEFAULT;
276 request.ifr_route = route;
277 ioctl(fSocket, SIOCDELRT, &request, sizeof(request));
278 if (ioctl(fSocket, SIOCADDRT, &request, sizeof(request)) < 0) {
279 dprintf("NetStackInitializer: Failed to set default route: %s\n",
280 strerror(errno));
281 return;
284 fConfiguredInterface = true;
286 dprintf("NetStackInitializer: successfully configured boot network "
287 "interface\n");
290 private:
291 int fSocket;
292 int fLinkSocket;
293 uint64 fClientMAC;
294 uint32 fClientIP;
295 uint32 fNetMask;
296 bool fFoundInterface;
297 bool fConfiguredInterface;
301 // #pragma mark - NetBootMethod
304 NetBootMethod::NetBootMethod(const KMessage& bootVolume, int32 method)
305 : BootMethod(bootVolume, method)
310 NetBootMethod::~NetBootMethod()
315 status_t
316 NetBootMethod::Init()
318 // We need to bring up the net stack.
319 status_t status;
321 uint64 clientMAC;
322 uint32 clientIP = 0;
323 uint32 netMask;
324 if (fBootVolume.FindInt64("client MAC", (int64*)&clientMAC) != B_OK
325 || fBootVolume.FindInt32("client IP", (int32*)&clientIP) != B_OK) {
326 panic("no client MAC or IP address or net mask\n");
327 return B_ERROR;
330 if (fBootVolume.FindInt32("net mask", (int32*)&netMask) != B_OK) {
331 // choose default netmask depending on the class of the address
332 if (IN_CLASSA(clientIP)
333 || (clientIP >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
334 // class A, or loopback
335 netMask = IN_CLASSA_NET;
336 } else if (IN_CLASSB(clientIP)) {
337 // class B
338 netMask = IN_CLASSB_NET;
339 } else {
340 // class C and rest
341 netMask = IN_CLASSC_NET;
345 NetStackInitializer initializer(clientMAC, clientIP, netMask);
346 status = initializer.Init();
347 if (status != B_OK)
348 return status;
350 // TODO: "net root path" should be used for finding the boot device/FS,
351 // but ATM neither the remote_disk nor the nbd driver are configurable
352 // at this point.
353 const char* rootPath = fBootVolume.GetString("net root path", NULL);
354 dprintf("NetBootMethod::Init(): net stack initialized; root path is: %s\n",
355 rootPath);
357 return B_OK;
361 bool
362 NetBootMethod::IsBootDevice(KDiskDevice* device, bool strict)
364 // We support only NBD and RemoteDisk at the moment, so we accept any
365 // device under /dev/disk/virtual/{nbd,remote_disk}/.
366 return is_net_device(device);
370 bool
371 NetBootMethod::IsBootPartition(KPartition* partition, bool& foundForSure)
373 // as long as it's BFS, we're fine
374 return (partition->ContentType()
375 && strcmp(partition->ContentType(), kPartitionTypeBFS) == 0);
379 void
380 NetBootMethod::SortPartitions(KPartition** partitions, int32 count)
382 qsort(partitions, count, sizeof(KPartition*),
383 compare_partitions_net_devices);