xref: /haiku/src/system/kernel/fs/vfs_net_boot.cpp (revision 22440f4105cafc95cc1d49f9bc65bb395c527d86)
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 <KPath.h>
23 
24 
25 static bool
26 string_starts_with(const char* string, const char* prefix)
27 {
28 	size_t stringLen = strlen(string);
29 	size_t prefixLen = strlen(prefix);
30 	return (stringLen >= prefixLen && strncmp(string, prefix, prefixLen) == 0);
31 }
32 
33 
34 static bool
35 is_net_device(KDiskDevice* device)
36 {
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/"));
40 }
41 
42 
43 static int
44 compare_partitions_net_devices(const void *_a, const void *_b)
45 {
46 	KPartition* a = *(KPartition**)_a;
47 	KPartition* b = *(KPartition**)_b;
48 
49 	bool aIsNetDevice = is_net_device(a->Device());
50 	bool bIsNetDevice = is_net_device(b->Device());
51 
52 	int compare = (int)aIsNetDevice - (int)bIsNetDevice;
53 	if (compare != 0)
54 		return compare;
55 
56 	return compare_image_boot(_a, _b);
57 }
58 
59 
60 class NetStackInitializer {
61 public:
62 	NetStackInitializer(uint64 clientMAC, uint32 clientIP, uint32 netMask)
63 		:
64 		fSocket(-1),
65 		fLinkSocket(-1),
66 		fClientMAC(clientMAC),
67 		fClientIP(clientIP),
68 		fNetMask(netMask),
69 		fFoundInterface(false),
70 		fConfiguredInterface(false)
71 	{
72 	}
73 
74 	~NetStackInitializer()
75 	{
76 		// close control sockets
77 		if (fSocket >= 0)
78 			close(fSocket);
79 
80 		if (fLinkSocket >= 0)
81 			close(fLinkSocket);
82 	}
83 
84 	status_t Init()
85 	{
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;
92 		}
93 
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;
100 		}
101 
102 
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;
108 
109 		_ScanDevices(path);
110 
111 		return fConfiguredInterface ? B_OK : B_ERROR;
112 	}
113 
114 private:
115 	void _ScanDevices(KPath& path)
116 	{
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;
122 		}
123 
124 		while (dirent* entry = readdir(dir)) {
125 			// skip "." and ".."
126 			if (strcmp(entry->d_name, ".") == 0
127 				|| strcmp(entry->d_name, "..") == 0) {
128 				continue;
129 			}
130 
131 			path.Append(entry->d_name);
132 
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());
139 			}
140 
141 			path.RemoveLeaf();
142 
143 			if (fFoundInterface)
144 				break;
145 		}
146 
147 		closedir(dir);
148 	}
149 
150 	void _ScanDevice(const char* path)
151 	{
152 		dprintf("NetStackInitializer: scanning device %s\n", path);
153 
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);
159 
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;
170 
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;
176 			}
177 		}
178 
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;
184 		}
185 
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;
194 			}
195 		}
196 
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;
202 		}
203 
204 		sockaddr_dl& link = *(sockaddr_dl*)&request.ifr_addr;
205 		if (link.sdl_type != IFT_ETHER)
206 			return;
207 
208 		if (link.sdl_alen == 0)
209 			return;
210 
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];
218 
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"));
223 
224 		if (macAddress != fClientMAC)
225 			return;
226 
227 		fFoundInterface = true;
228 
229 		// configure the interface
230 
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;
242 		}
243 
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;
250 		}
251 
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;
258 		}
259 
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;
268 			}
269 		}
270 
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;
275 
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;
282 		}
283 
284 		fConfiguredInterface = true;
285 
286 		dprintf("NetStackInitializer: successfully configured boot network "
287 			"interface\n");
288 	}
289 
290 private:
291 	int					fSocket;
292 	int					fLinkSocket;
293 	uint64				fClientMAC;
294 	uint32				fClientIP;
295 	uint32				fNetMask;
296 	bool				fFoundInterface;
297 	bool				fConfiguredInterface;
298 };
299 
300 
301 // #pragma mark - NetBootMethod
302 
303 
304 NetBootMethod::NetBootMethod(const KMessage& bootVolume, int32 method)
305 	: BootMethod(bootVolume, method)
306 {
307 }
308 
309 
310 NetBootMethod::~NetBootMethod()
311 {
312 }
313 
314 
315 status_t
316 NetBootMethod::Init()
317 {
318 	// We need to bring up the net stack.
319 	status_t status;
320 
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;
328 	}
329 
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;
342 		}
343 	}
344 
345 	NetStackInitializer initializer(clientMAC, clientIP, netMask);
346 	status = initializer.Init();
347 	if (status != B_OK)
348 		return status;
349 
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);
356 
357 	return B_OK;
358 }
359 
360 
361 bool
362 NetBootMethod::IsBootDevice(KDiskDevice* device, bool strict)
363 {
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);
367 }
368 
369 
370 bool
371 NetBootMethod::IsBootPartition(KPartition* partition, bool& foundForSure)
372 {
373 	// as long as it's BFS, we're fine
374 	return (partition->ContentType()
375 		&& strcmp(partition->ContentType(), kPartitionTypeBFS) == 0);
376 }
377 
378 
379 void
380 NetBootMethod::SortPartitions(KPartition** partitions, int32 count)
381 {
382 	qsort(partitions, count, sizeof(KPartition*),
383 		compare_partitions_net_devices);
384 }
385