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