xref: /haiku/src/system/kernel/fs/vfs_net_boot.cpp (revision 582da17386c4a192ca30270d6b0b95f561cf5843)
1 /*
2  * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "vfs_net_boot.h"
7 
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>
17 
18 #include <DiskDeviceTypes.h>
19 
20 #include <disk_device_manager/KDiskDevice.h>
21 
22 #include <socket_interface.h>
23 
24 #include <KPath.h>
25 
26 
27 static bool
28 string_starts_with(const char* string, const char* prefix)
29 {
30 	size_t stringLen = strlen(string);
31 	size_t prefixLen = strlen(prefix);
32 	return (stringLen >= prefixLen && strncmp(string, prefix, prefixLen) == 0);
33 }
34 
35 
36 static bool
37 is_net_device(KDiskDevice* device)
38 {
39 	const char* path = device->Path();
40 	return (string_starts_with(path, "/dev/disk/virtual/nbd/")
41 		|| string_starts_with(path, "/dev/disk/virtual/remote_disk/"));
42 }
43 
44 
45 static int
46 compare_partitions_net_devices(const void *_a, const void *_b)
47 {
48 	KPartition* a = *(KPartition**)_a;
49 	KPartition* b = *(KPartition**)_b;
50 
51 	bool aIsNetDevice = is_net_device(a->Device());
52 	bool bIsNetDevice = is_net_device(b->Device());
53 
54 	int compare = (int)aIsNetDevice - (int)bIsNetDevice;
55 	if (compare != 0)
56 		return compare;
57 
58 	return compare_image_boot(_a, _b);
59 }
60 
61 
62 class NetStackInitializer {
63 public:
64 	NetStackInitializer(uint64 clientMAC, uint32 clientIP, uint32 netMask)
65 		: fSocketModule(NULL),
66 		  fSocket(-1),
67 		  fLinkSocket(-1),
68 		  fClientMAC(clientMAC),
69 		  fClientIP(clientIP),
70 		  fNetMask(netMask),
71 		  fFoundInterface(false),
72 		  fConfiguredInterface(false)
73 	{
74 	}
75 
76 	~NetStackInitializer()
77 	{
78 		// close control sockets
79 		if (fSocket >= 0)
80 			close(fSocket);
81 
82 		if (fLinkSocket >= 0)
83 			close(fLinkSocket);
84 
85 		// put socket module
86 		if (fSocketModule)
87 			put_module(fSocketModule->info.name);
88 	}
89 
90 	status_t Init()
91 	{
92 		// get the socket module
93 		status_t error = get_module(B_SOCKET_MODULE_NAME,
94 			(module_info**)&fSocketModule);
95 		if (error != B_OK) {
96 			dprintf("NetStackInitializer: Failed to load socket module: %s\n",
97 				strerror(error));
98 			return error;
99 		}
100 
101 		// open a control socket for playing with the stack
102 		fSocket = fSocketModule->socket(AF_INET, SOCK_DGRAM, 0);
103 		if (fSocket < 0) {
104 			dprintf("NetStackInitializer: Failed to open socket: %s\n",
105 				strerror(errno));
106 			return errno;
107 		}
108 
109 		// ... and a link level socket
110 		fLinkSocket = fSocketModule->socket(AF_LINK, SOCK_DGRAM, 0);
111 		if (fLinkSocket < 0) {
112 			dprintf("NetStackInitializer: Failed to open link level socket:"
113 				" %s\n", strerror(errno));
114 			return errno;
115 		}
116 
117 
118 		// now iterate through the existing network devices
119 		KPath path;
120 		error = path.SetTo("/dev/net");
121 		if (error != B_OK)
122 			return error;
123 
124 		_ScanDevices(path);
125 
126 		return (fConfiguredInterface ? B_OK : B_ERROR);
127 	}
128 
129 private:
130 	void _ScanDevices(KPath& path)
131 	{
132 		DIR* dir = opendir(path.Path());
133 		if (!dir) {
134 			dprintf("NetStackInitializer: Failed to opendir() \"%s\": %s\n",
135 				path.Path(), strerror(errno));
136 			return;
137 		}
138 
139 		status_t error = B_OK;
140 
141 		while (dirent* entry = readdir(dir)) {
142 			// skip "." and ".."
143 			if (strcmp(entry->d_name, ".") == 0
144 				|| strcmp(entry->d_name, "..") == 0) {
145 				continue;
146 			}
147 
148 			path.Append(entry->d_name);
149 
150 			struct stat st;
151 			if (stat(path.Path(), &st) == 0) {
152 				if (S_ISDIR(st.st_mode))
153 					_ScanDevices(path);
154 				else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
155 					_ScanDevice(path.Path());
156 			}
157 
158 			path.RemoveLeaf();
159 
160 			if (fFoundInterface)
161 				break;
162 		}
163 
164 		closedir(dir);
165 	}
166 
167 	void _ScanDevice(const char* path)
168 	{
169 		dprintf("NetStackInitializer: scanning device %s\n", path);
170 
171 		// check if this interface is already known
172 		ifreq request;
173 		if (strlen(path) >= IF_NAMESIZE)
174 			return;
175 		strcpy(request.ifr_name, path);
176 
177 		if (ioctl(fSocket, SIOCGIFINDEX, &request, sizeof(request)) < 0) {
178 			// not known yet -- add it
179 			request.ifr_parameter.base_name[0] = '\0';
180 			request.ifr_parameter.device[0] = '\0';
181 			request.ifr_parameter.sub_type = 0;
182 
183 			if (ioctl(fSocket, SIOCAIFADDR, &request, sizeof(request)) < 0) {
184 				dprintf("NetStackInitializer: adding interface failed for "
185 					"device %s: %s\n", path, strerror(errno));
186 				return;
187 			}
188 		}
189 
190 		// bring the interface up (get flags, add IFF_UP)
191 		if (ioctl(fSocket, SIOCGIFFLAGS, &request, sizeof(request)) < 0) {
192 			dprintf("NetStackInitializer: getting flags failed for interface "
193 				"%s: %s\n", path, strerror(errno));
194 			return;
195 		}
196 
197 		int interfaceFlags = request.ifr_flags;
198 		if (!(interfaceFlags & IFF_UP)) {
199 			interfaceFlags |= IFF_UP;
200 			request.ifr_flags = interfaceFlags;
201 			if (ioctl(fSocket, SIOCSIFFLAGS, &request, sizeof(request)) < 0) {
202 				dprintf("NetStackInitializer: failed to bring interface up "
203 					"%s: %s\n", path, strerror(errno));
204 				return;
205 			}
206 		}
207 
208 		// get the MAC address
209 		if (ioctl(fLinkSocket, SIOCGIFADDR, &request, sizeof(request)) < 0) {
210 			dprintf("NetStackInitializer: Getting MAC addresss failed for "
211 				"interface %s: %s\n", path, strerror(errno));
212 			return;
213 		}
214 
215 		sockaddr_dl& link = *(sockaddr_dl*)&request.ifr_addr;
216 		if (link.sdl_type != IFT_ETHER)
217 			return;
218 
219 		if (link.sdl_alen == 0)
220 			return;
221 
222 		uint8* macBytes = (uint8 *)LLADDR(&link);
223 		uint64 macAddress = ((uint64)macBytes[0] << 40)
224 			| ((uint64)macBytes[1] << 32)
225 			| ((uint64)macBytes[2] << 24)
226 			| ((uint64)macBytes[3] << 16)
227 			| ((uint64)macBytes[4] << 8)
228 			| (uint64)macBytes[5];
229 
230 		dprintf("NetStackInitializer: found ethernet interface with MAC "
231 			"address %02x:%02x:%02x:%02x:%02x:%02x; which is%s the one we're "
232 			"looking for\n", macBytes[0], macBytes[1], macBytes[2], macBytes[3],
233 			macBytes[4], macBytes[5], (macAddress == fClientMAC ? "" : "n't"));
234 
235 		if (macAddress != fClientMAC)
236 			return;
237 
238 		fFoundInterface = true;
239 
240 		// configure the interface
241 
242 		// set IP address
243 		sockaddr_in& address = *(sockaddr_in*)&request.ifr_addr;
244 		address.sin_family = AF_INET;
245 		address.sin_len = sizeof(sockaddr_in);
246 		address.sin_port = 0;
247 		address.sin_addr.s_addr = htonl(fClientIP);
248 		memset(&address.sin_zero[0], 0, sizeof(address.sin_zero));
249 		if (ioctl(fSocket, SIOCSIFADDR, &request, sizeof(request)) < 0) {
250 			dprintf("NetStackInitializer: Setting IP addresss failed for "
251 				"interface %s: %s\n", path, strerror(errno));
252 			return;
253 		}
254 
255 		// set net mask
256 		address.sin_addr.s_addr = htonl(fNetMask);
257 		if (ioctl(fSocket, SIOCSIFNETMASK, &request, sizeof(request)) < 0) {
258 			dprintf("NetStackInitializer: Setting net mask failed for "
259 				"interface %s: %s\n", path, strerror(errno));
260 			return;
261 		}
262 
263 		// set broadcast address
264 		address.sin_addr.s_addr = htonl(fClientIP | ~fNetMask);
265 		if (ioctl(fSocket, SIOCSIFBRDADDR, &request, sizeof(request)) < 0) {
266 			dprintf("NetStackInitializer: Setting broadcast address failed for "
267 				"interface %s: %s\n", path, strerror(errno));
268 			return;
269 		}
270 
271 		// set IFF_BROADCAST
272 		if (!(interfaceFlags & IFF_BROADCAST)) {
273 			interfaceFlags |= IFF_BROADCAST;
274 			request.ifr_flags = interfaceFlags;
275 			if (ioctl(fSocket, SIOCSIFFLAGS, &request, sizeof(request)) < 0) {
276 				dprintf("NetStackInitializer: failed to set IFF_BROADCAST flag "
277 					"for interface %s: %s\n", path, strerror(errno));
278 				return;
279 			}
280 		}
281 
282 		// set default route; remove previous one, if any
283 		route_entry route;
284 		memset(&route, 0, sizeof(route_entry));
285 		route.flags = RTF_STATIC | RTF_DEFAULT;
286 
287 		request.ifr_route = route;
288 		ioctl(fSocket, SIOCDELRT, &request, sizeof(request));
289 		if (ioctl(fSocket, SIOCADDRT, &request, sizeof(request)) < 0) {
290 			dprintf("NetStackInitializer: Failed to set default route: %s\n",
291 				strerror(errno));
292 			return;
293 		}
294 
295 		fConfiguredInterface = true;
296 
297 		dprintf("NetStackInitializer: successfully configured boot network "
298 			"interface\n");
299 	}
300 
301 private:
302 	socket_module_info*	fSocketModule;
303 	int					fSocket;
304 	int					fLinkSocket;
305 	uint64				fClientMAC;
306 	uint32				fClientIP;
307 	uint32				fNetMask;
308 	bool				fFoundInterface;
309 	bool				fConfiguredInterface;
310 };
311 
312 
313 // #pragma mark - NetBootMethod
314 
315 
316 NetBootMethod::NetBootMethod(const KMessage& bootVolume, int32 method)
317 	: BootMethod(bootVolume, method)
318 {
319 }
320 
321 
322 NetBootMethod::~NetBootMethod()
323 {
324 }
325 
326 
327 status_t
328 NetBootMethod::Init()
329 {
330 	// We need to bring up the net stack.
331 	status_t status;
332 
333 	uint64 clientMAC;
334 	uint32 clientIP;
335 	uint32 netMask;
336 	if (fBootVolume.FindInt64("client MAC", (int64*)&clientMAC) != B_OK
337 		|| fBootVolume.FindInt32("client IP", (int32*)&clientIP) != B_OK) {
338 		panic("no client MAC or IP address or net mask\n");
339 		return B_ERROR;
340 	}
341 
342 	if (fBootVolume.FindInt32("net mask", (int32*)&netMask) != B_OK) {
343 		// choose default netmask depending on the class of the address
344 		in_addr_t net = htonl(clientIP);
345 		if (IN_CLASSA(net)
346 			|| (ntohl(net) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
347 			// class A, or loopback
348 			netMask = ntohl(IN_CLASSA_NET);
349 		} else if (IN_CLASSB(net)) {
350 			// class B
351 			netMask = ntohl(IN_CLASSB_NET);
352 		} else {
353 			// class C and rest
354 			netMask = ntohl(IN_CLASSC_NET);
355 		}
356 	}
357 
358 	NetStackInitializer initializer(clientMAC, clientIP, netMask);
359 	status = initializer.Init();
360 	if (status != B_OK)
361 		return status;
362 
363 	// TODO: "net root path" should be used for finding the boot device/FS,
364 	// but ATM neither the remote_disk nor the nbd driver are configurable
365 	// at this point.
366 	const char* rootPath = fBootVolume.GetString("net root path", NULL);
367 	dprintf("NetBootMethod::Init(): net stack initialized; root path is: %s\n",
368 		rootPath);
369 
370 	return B_OK;
371 }
372 
373 
374 bool
375 NetBootMethod::IsBootDevice(KDiskDevice* device, bool strict)
376 {
377 	// We support only NBD and RemoteDisk at the moment, so we accept any
378 	// device under /dev/disk/virtual/{nbd,remote_disk}/.
379 	return is_net_device(device);
380 }
381 
382 
383 bool
384 NetBootMethod::IsBootPartition(KPartition* partition, bool& foundForSure)
385 {
386 	// as long as it's BFS, we're fine
387 	return (partition->ContentType()
388 		&& strcmp(partition->ContentType(), kPartitionTypeBFS) == 0);
389 }
390 
391 
392 void
393 NetBootMethod::SortPartitions(KPartition** partitions, int32 count)
394 {
395 	qsort(partitions, count, sizeof(KPartition*),
396 		compare_partitions_net_devices);
397 }
398