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