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 status_t error = B_OK; 125 126 while (dirent* entry = readdir(dir)) { 127 // skip "." and ".." 128 if (strcmp(entry->d_name, ".") == 0 129 || strcmp(entry->d_name, "..") == 0) { 130 continue; 131 } 132 133 path.Append(entry->d_name); 134 135 struct stat st; 136 if (stat(path.Path(), &st) == 0) { 137 if (S_ISDIR(st.st_mode)) 138 _ScanDevices(path); 139 else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) 140 _ScanDevice(path.Path()); 141 } 142 143 path.RemoveLeaf(); 144 145 if (fFoundInterface) 146 break; 147 } 148 149 closedir(dir); 150 } 151 152 void _ScanDevice(const char* path) 153 { 154 dprintf("NetStackInitializer: scanning device %s\n", path); 155 156 // check if this interface is already known 157 ifreq request; 158 if (strlen(path) >= IF_NAMESIZE) 159 return; 160 strcpy(request.ifr_name, path); 161 162 if (ioctl(fSocket, SIOCGIFINDEX, &request, sizeof(request)) < 0) { 163 // not known yet -- add it 164 request.ifr_parameter.base_name[0] = '\0'; 165 request.ifr_parameter.device[0] = '\0'; 166 request.ifr_parameter.sub_type = 0; 167 168 if (ioctl(fSocket, SIOCAIFADDR, &request, sizeof(request)) < 0) { 169 dprintf("NetStackInitializer: adding interface failed for " 170 "device %s: %s\n", path, strerror(errno)); 171 return; 172 } 173 } 174 175 // bring the interface up (get flags, add IFF_UP) 176 if (ioctl(fSocket, SIOCGIFFLAGS, &request, sizeof(request)) < 0) { 177 dprintf("NetStackInitializer: getting flags failed for interface " 178 "%s: %s\n", path, strerror(errno)); 179 return; 180 } 181 182 int interfaceFlags = request.ifr_flags; 183 if (!(interfaceFlags & IFF_UP)) { 184 interfaceFlags |= IFF_UP; 185 request.ifr_flags = interfaceFlags; 186 if (ioctl(fSocket, SIOCSIFFLAGS, &request, sizeof(request)) < 0) { 187 dprintf("NetStackInitializer: failed to bring interface up " 188 "%s: %s\n", path, strerror(errno)); 189 return; 190 } 191 } 192 193 // get the MAC address 194 if (ioctl(fLinkSocket, SIOCGIFADDR, &request, sizeof(request)) < 0) { 195 dprintf("NetStackInitializer: Getting MAC addresss failed for " 196 "interface %s: %s\n", path, strerror(errno)); 197 return; 198 } 199 200 sockaddr_dl& link = *(sockaddr_dl*)&request.ifr_addr; 201 if (link.sdl_type != IFT_ETHER) 202 return; 203 204 if (link.sdl_alen == 0) 205 return; 206 207 uint8* macBytes = (uint8 *)LLADDR(&link); 208 uint64 macAddress = ((uint64)macBytes[0] << 40) 209 | ((uint64)macBytes[1] << 32) 210 | ((uint64)macBytes[2] << 24) 211 | ((uint64)macBytes[3] << 16) 212 | ((uint64)macBytes[4] << 8) 213 | (uint64)macBytes[5]; 214 215 dprintf("NetStackInitializer: found ethernet interface with MAC " 216 "address %02x:%02x:%02x:%02x:%02x:%02x; which is%s the one we're " 217 "looking for\n", macBytes[0], macBytes[1], macBytes[2], macBytes[3], 218 macBytes[4], macBytes[5], (macAddress == fClientMAC ? "" : "n't")); 219 220 if (macAddress != fClientMAC) 221 return; 222 223 fFoundInterface = true; 224 225 // configure the interface 226 227 // set IP address 228 sockaddr_in& address = *(sockaddr_in*)&request.ifr_addr; 229 address.sin_family = AF_INET; 230 address.sin_len = sizeof(sockaddr_in); 231 address.sin_port = 0; 232 address.sin_addr.s_addr = htonl(fClientIP); 233 memset(&address.sin_zero[0], 0, sizeof(address.sin_zero)); 234 if (ioctl(fSocket, SIOCSIFADDR, &request, sizeof(request)) < 0) { 235 dprintf("NetStackInitializer: Setting IP addresss failed for " 236 "interface %s: %s\n", path, strerror(errno)); 237 return; 238 } 239 240 // set net mask 241 address.sin_addr.s_addr = htonl(fNetMask); 242 if (ioctl(fSocket, SIOCSIFNETMASK, &request, sizeof(request)) < 0) { 243 dprintf("NetStackInitializer: Setting net mask failed for " 244 "interface %s: %s\n", path, strerror(errno)); 245 return; 246 } 247 248 // set broadcast address 249 address.sin_addr.s_addr = htonl(fClientIP | ~fNetMask); 250 if (ioctl(fSocket, SIOCSIFBRDADDR, &request, sizeof(request)) < 0) { 251 dprintf("NetStackInitializer: Setting broadcast address failed for " 252 "interface %s: %s\n", path, strerror(errno)); 253 return; 254 } 255 256 // set IFF_BROADCAST 257 if (!(interfaceFlags & IFF_BROADCAST)) { 258 interfaceFlags |= IFF_BROADCAST; 259 request.ifr_flags = interfaceFlags; 260 if (ioctl(fSocket, SIOCSIFFLAGS, &request, sizeof(request)) < 0) { 261 dprintf("NetStackInitializer: failed to set IFF_BROADCAST flag " 262 "for interface %s: %s\n", path, strerror(errno)); 263 return; 264 } 265 } 266 267 // set default route; remove previous one, if any 268 route_entry route; 269 memset(&route, 0, sizeof(route_entry)); 270 route.flags = RTF_STATIC | RTF_DEFAULT; 271 272 request.ifr_route = route; 273 ioctl(fSocket, SIOCDELRT, &request, sizeof(request)); 274 if (ioctl(fSocket, SIOCADDRT, &request, sizeof(request)) < 0) { 275 dprintf("NetStackInitializer: Failed to set default route: %s\n", 276 strerror(errno)); 277 return; 278 } 279 280 fConfiguredInterface = true; 281 282 dprintf("NetStackInitializer: successfully configured boot network " 283 "interface\n"); 284 } 285 286 private: 287 int fSocket; 288 int fLinkSocket; 289 uint64 fClientMAC; 290 uint32 fClientIP; 291 uint32 fNetMask; 292 bool fFoundInterface; 293 bool fConfiguredInterface; 294 }; 295 296 297 // #pragma mark - NetBootMethod 298 299 300 NetBootMethod::NetBootMethod(const KMessage& bootVolume, int32 method) 301 : BootMethod(bootVolume, method) 302 { 303 } 304 305 306 NetBootMethod::~NetBootMethod() 307 { 308 } 309 310 311 status_t 312 NetBootMethod::Init() 313 { 314 // We need to bring up the net stack. 315 status_t status; 316 317 uint64 clientMAC; 318 uint32 clientIP; 319 uint32 netMask; 320 if (fBootVolume.FindInt64("client MAC", (int64*)&clientMAC) != B_OK 321 || fBootVolume.FindInt32("client IP", (int32*)&clientIP) != B_OK) { 322 panic("no client MAC or IP address or net mask\n"); 323 return B_ERROR; 324 } 325 326 if (fBootVolume.FindInt32("net mask", (int32*)&netMask) != B_OK) { 327 // choose default netmask depending on the class of the address 328 in_addr_t net = htonl(clientIP); 329 if (IN_CLASSA(net) 330 || (ntohl(net) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) { 331 // class A, or loopback 332 netMask = ntohl(IN_CLASSA_NET); 333 } else if (IN_CLASSB(net)) { 334 // class B 335 netMask = ntohl(IN_CLASSB_NET); 336 } else { 337 // class C and rest 338 netMask = ntohl(IN_CLASSC_NET); 339 } 340 } 341 342 NetStackInitializer initializer(clientMAC, clientIP, netMask); 343 status = initializer.Init(); 344 if (status != B_OK) 345 return status; 346 347 // TODO: "net root path" should be used for finding the boot device/FS, 348 // but ATM neither the remote_disk nor the nbd driver are configurable 349 // at this point. 350 const char* rootPath = fBootVolume.GetString("net root path", NULL); 351 dprintf("NetBootMethod::Init(): net stack initialized; root path is: %s\n", 352 rootPath); 353 354 return B_OK; 355 } 356 357 358 bool 359 NetBootMethod::IsBootDevice(KDiskDevice* device, bool strict) 360 { 361 // We support only NBD and RemoteDisk at the moment, so we accept any 362 // device under /dev/disk/virtual/{nbd,remote_disk}/. 363 return is_net_device(device); 364 } 365 366 367 bool 368 NetBootMethod::IsBootPartition(KPartition* partition, bool& foundForSure) 369 { 370 // as long as it's BFS, we're fine 371 return (partition->ContentType() 372 && strcmp(partition->ContentType(), kPartitionTypeBFS) == 0); 373 } 374 375 376 void 377 NetBootMethod::SortPartitions(KPartition** partitions, int32 count) 378 { 379 qsort(partitions, count, sizeof(KPartition*), 380 compare_partitions_net_devices); 381 } 382