2 * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de.
3 * Distributed under the terms of the MIT License.
6 #include "vfs_net_boot.h"
11 #include <net/if_dl.h>
12 #include <net/if_types.h>
13 #include <netinet/in.h>
15 #include <sys/socket.h>
16 #include <sys/sockio.h>
18 #include <DiskDeviceTypes.h>
20 #include <disk_device_manager/KDiskDevice.h>
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);
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/"));
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
;
56 return compare_image_boot(_a
, _b
);
60 class NetStackInitializer
{
62 NetStackInitializer(uint64 clientMAC
, uint32 clientIP
, uint32 netMask
)
66 fClientMAC(clientMAC
),
69 fFoundInterface(false),
70 fConfiguredInterface(false)
74 ~NetStackInitializer()
76 // close control sockets
86 // open a control socket for playing with the stack
87 fSocket
= socket(AF_INET
, SOCK_DGRAM
, 0);
89 dprintf("NetStackInitializer: Failed to open socket: %s\n",
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
));
103 // now iterate through the existing network devices
105 status_t error
= path
.SetTo("/dev/net");
111 return fConfiguredInterface
? B_OK
: B_ERROR
;
115 void _ScanDevices(KPath
& path
)
117 DIR* dir
= opendir(path
.Path());
119 dprintf("NetStackInitializer: Failed to opendir() \"%s\": %s\n",
120 path
.Path(), strerror(errno
));
124 while (dirent
* entry
= readdir(dir
)) {
126 if (strcmp(entry
->d_name
, ".") == 0
127 || strcmp(entry
->d_name
, "..") == 0) {
131 path
.Append(entry
->d_name
);
134 if (stat(path
.Path(), &st
) == 0) {
135 if (S_ISDIR(st
.st_mode
))
137 else if (S_ISBLK(st
.st_mode
) || S_ISCHR(st
.st_mode
))
138 _ScanDevice(path
.Path());
150 void _ScanDevice(const char* path
)
152 dprintf("NetStackInitializer: scanning device %s\n", path
);
154 // check if this interface is already known
156 if (strlen(path
) >= IF_NAMESIZE
)
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
));
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
));
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
));
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
));
204 sockaddr_dl
& link
= *(sockaddr_dl
*)&request
.ifr_addr
;
205 if (link
.sdl_type
!= IFT_ETHER
)
208 if (link
.sdl_alen
== 0)
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
)
227 fFoundInterface
= true;
229 // configure the interface
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
));
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
));
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
));
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
));
271 // set default route; remove previous one, if any
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",
284 fConfiguredInterface
= true;
286 dprintf("NetStackInitializer: successfully configured boot network "
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()
316 NetBootMethod::Init()
318 // We need to bring up the net stack.
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");
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
)) {
338 netMask
= IN_CLASSB_NET
;
341 netMask
= IN_CLASSC_NET
;
345 NetStackInitializer
initializer(clientMAC
, clientIP
, netMask
);
346 status
= initializer
.Init();
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
353 const char* rootPath
= fBootVolume
.GetString("net root path", NULL
);
354 dprintf("NetBootMethod::Init(): net stack initialized; root path is: %s\n",
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
);
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);
380 NetBootMethod::SortPartitions(KPartition
** partitions
, int32 count
)
382 qsort(partitions
, count
, sizeof(KPartition
*),
383 compare_partitions_net_devices
);