1d561d0adSIngo Weinhold /* 2d561d0adSIngo Weinhold * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>. 3d561d0adSIngo Weinhold * All rights reserved. Distributed under the terms of the MIT License. 4d561d0adSIngo Weinhold */ 5d561d0adSIngo Weinhold 6d561d0adSIngo Weinhold #include <boot/net/RemoteDisk.h> 7d561d0adSIngo Weinhold 8d561d0adSIngo Weinhold #include <new> 9d561d0adSIngo Weinhold 10d561d0adSIngo Weinhold #include <endian.h> 11d561d0adSIngo Weinhold #include <stdio.h> 12d561d0adSIngo Weinhold 13d561d0adSIngo Weinhold #include <OS.h> 14d561d0adSIngo Weinhold #include <SupportDefs.h> 15d561d0adSIngo Weinhold 16d561d0adSIngo Weinhold #include <boot/net/UDP.h> 17d561d0adSIngo Weinhold 18d561d0adSIngo Weinhold 1976cd432cSIngo Weinhold static const bigtime_t kRequestTimeout = 100000LL; 2076cd432cSIngo Weinhold 2195d4ed67SJonathan Schleifer 2295d4ed67SJonathan Schleifer using std::nothrow; 2395d4ed67SJonathan Schleifer 2495d4ed67SJonathan Schleifer 25d561d0adSIngo Weinhold #if __BYTE_ORDER == __LITTLE_ENDIAN 26d561d0adSIngo Weinhold 27d561d0adSIngo Weinhold static inline 28d561d0adSIngo Weinhold uint64_t swap_uint64(uint64_t data) 29d561d0adSIngo Weinhold { 30d561d0adSIngo Weinhold return ((data & 0xff) << 56) 31d561d0adSIngo Weinhold | ((data & 0xff00) << 40) 32d561d0adSIngo Weinhold | ((data & 0xff0000) << 24) 33d561d0adSIngo Weinhold | ((data & 0xff000000) << 8) 34d561d0adSIngo Weinhold | ((data >> 8) & 0xff000000) 35d561d0adSIngo Weinhold | ((data >> 24) & 0xff0000) 36d561d0adSIngo Weinhold | ((data >> 40) & 0xff00) 37d561d0adSIngo Weinhold | ((data >> 56) & 0xff); 38d561d0adSIngo Weinhold } 39d561d0adSIngo Weinhold 40d561d0adSIngo Weinhold #define host_to_net64(data) swap_uint64(data) 41d561d0adSIngo Weinhold #define net_to_host64(data) swap_uint64(data) 42d561d0adSIngo Weinhold 43d561d0adSIngo Weinhold #endif 44d561d0adSIngo Weinhold 45d561d0adSIngo Weinhold #if __BYTE_ORDER == __BIG_ENDIAN 46d561d0adSIngo Weinhold #define host_to_net64(data) (data) 47d561d0adSIngo Weinhold #define net_to_host64(data) (data) 48d561d0adSIngo Weinhold #endif 49d561d0adSIngo Weinhold 50d561d0adSIngo Weinhold #undef htonll 51d561d0adSIngo Weinhold #undef ntohll 52d561d0adSIngo Weinhold #define htonll(data) host_to_net64(data) 53d561d0adSIngo Weinhold #define ntohll(data) net_to_host64(data) 54d561d0adSIngo Weinhold 55d561d0adSIngo Weinhold 56d561d0adSIngo Weinhold // constructor 57d561d0adSIngo Weinhold RemoteDisk::RemoteDisk() 58d561d0adSIngo Weinhold : fServerAddress(INADDR_ANY), 59d561d0adSIngo Weinhold fServerPort(0), 60d561d0adSIngo Weinhold fImageSize(0), 61d561d0adSIngo Weinhold fRequestID(0), 62d561d0adSIngo Weinhold fSocket(NULL), 63d561d0adSIngo Weinhold fPacket(NULL) 64d561d0adSIngo Weinhold { 65d561d0adSIngo Weinhold } 66d561d0adSIngo Weinhold 67d561d0adSIngo Weinhold // destructor 68d561d0adSIngo Weinhold RemoteDisk::~RemoteDisk() 69d561d0adSIngo Weinhold { 70d561d0adSIngo Weinhold delete fSocket; 71d561d0adSIngo Weinhold delete fPacket; 72d561d0adSIngo Weinhold } 73d561d0adSIngo Weinhold 74d561d0adSIngo Weinhold // Init 75d561d0adSIngo Weinhold status_t 76d561d0adSIngo Weinhold RemoteDisk::Init(ip_addr_t serverAddress, uint16 serverPort, off_t imageSize) 77d561d0adSIngo Weinhold { 78d561d0adSIngo Weinhold fServerAddress = serverAddress; 79d561d0adSIngo Weinhold fServerPort = serverPort; 80d561d0adSIngo Weinhold fImageSize = imageSize; 81d561d0adSIngo Weinhold 82d561d0adSIngo Weinhold // create and bind socket 83d561d0adSIngo Weinhold fSocket = new(nothrow) UDPSocket; 84d561d0adSIngo Weinhold if (!fSocket) 85d561d0adSIngo Weinhold return B_NO_MEMORY; 86d561d0adSIngo Weinhold 87d561d0adSIngo Weinhold status_t error = fSocket->Bind(INADDR_ANY, 6666); 88d561d0adSIngo Weinhold if (error != B_OK) 89d561d0adSIngo Weinhold return error; 90d561d0adSIngo Weinhold 91d561d0adSIngo Weinhold return B_OK; 92d561d0adSIngo Weinhold } 93d561d0adSIngo Weinhold 94d561d0adSIngo Weinhold // ReadAt 95d561d0adSIngo Weinhold ssize_t 96d561d0adSIngo Weinhold RemoteDisk::ReadAt(void */*cookie*/, off_t pos, void *_buffer, 97d561d0adSIngo Weinhold size_t bufferSize) 98d561d0adSIngo Weinhold { 99d561d0adSIngo Weinhold if (!fSocket) 100d561d0adSIngo Weinhold return B_NO_INIT; 101d561d0adSIngo Weinhold 102d561d0adSIngo Weinhold uint8 *buffer = (uint8*)_buffer; 103d561d0adSIngo Weinhold if (!buffer || pos < 0) 104d561d0adSIngo Weinhold return B_BAD_VALUE; 105d561d0adSIngo Weinhold 106d561d0adSIngo Weinhold if (bufferSize == 0) 107d561d0adSIngo Weinhold return 0; 108d561d0adSIngo Weinhold 109d561d0adSIngo Weinhold // Check whether the current packet already contains the beginning of the 110d561d0adSIngo Weinhold // data to read. 111d561d0adSIngo Weinhold ssize_t bytesRead = _ReadFromPacket(pos, buffer, bufferSize); 112d561d0adSIngo Weinhold 113d561d0adSIngo Weinhold // If there still remains something to be read, we need to get it from the 114d561d0adSIngo Weinhold // server. 115d561d0adSIngo Weinhold status_t error = B_OK; 116d561d0adSIngo Weinhold while (bufferSize > 0) { 117d561d0adSIngo Weinhold // prepare request 118d561d0adSIngo Weinhold remote_disk_header request; 119d561d0adSIngo Weinhold request.offset = htonll(pos); 120d561d0adSIngo Weinhold uint32 toRead = min_c(bufferSize, REMOTE_DISK_BLOCK_SIZE); 121d561d0adSIngo Weinhold request.size = htons(toRead); 122d561d0adSIngo Weinhold request.command = REMOTE_DISK_READ_REQUEST; 123d561d0adSIngo Weinhold 124d561d0adSIngo Weinhold // send request 125d561d0adSIngo Weinhold UDPPacket *packet; 126d561d0adSIngo Weinhold error = _SendRequest(&request, sizeof(request), REMOTE_DISK_READ_REPLY, 127d561d0adSIngo Weinhold &packet); 128d561d0adSIngo Weinhold if (error != B_OK) 129d561d0adSIngo Weinhold break; 130d561d0adSIngo Weinhold 131d561d0adSIngo Weinhold // check for errors 132d561d0adSIngo Weinhold int16 packetSize = ntohs(((remote_disk_header*)packet->Data())->size); 133d561d0adSIngo Weinhold if (packetSize < 0) { 134d561d0adSIngo Weinhold if (packetSize == REMOTE_DISK_IO_ERROR) 135d561d0adSIngo Weinhold error = B_IO_ERROR; 136d561d0adSIngo Weinhold else if (packetSize == REMOTE_DISK_BAD_REQUEST) 137d561d0adSIngo Weinhold error = B_BAD_VALUE; 138d561d0adSIngo Weinhold break; 139d561d0adSIngo Weinhold } 140d561d0adSIngo Weinhold 141d561d0adSIngo Weinhold // make the reply packet the current packet 142d561d0adSIngo Weinhold delete fPacket; 143d561d0adSIngo Weinhold fPacket = packet; 144d561d0adSIngo Weinhold 145d561d0adSIngo Weinhold // read from the packet 146d561d0adSIngo Weinhold size_t packetBytesRead = _ReadFromPacket(pos, buffer, bufferSize); 147d561d0adSIngo Weinhold if (packetBytesRead == 0) 148d561d0adSIngo Weinhold break; 149d561d0adSIngo Weinhold bytesRead += packetBytesRead; 150d561d0adSIngo Weinhold } 151d561d0adSIngo Weinhold 152d561d0adSIngo Weinhold // only return an error, when we were not able to read anything at all 153d561d0adSIngo Weinhold return (bytesRead == 0 ? error : bytesRead); 154d561d0adSIngo Weinhold } 155d561d0adSIngo Weinhold 156d561d0adSIngo Weinhold // WriteAt 157d561d0adSIngo Weinhold ssize_t 158d561d0adSIngo Weinhold RemoteDisk::WriteAt(void */*cookie*/, off_t pos, const void *buffer, 159d561d0adSIngo Weinhold size_t bufferSize) 160d561d0adSIngo Weinhold { 161d561d0adSIngo Weinhold // Not needed in the boot loader. 162d561d0adSIngo Weinhold return B_PERMISSION_DENIED; 163d561d0adSIngo Weinhold } 164d561d0adSIngo Weinhold 165d561d0adSIngo Weinhold // GetName 166d561d0adSIngo Weinhold status_t 167d561d0adSIngo Weinhold RemoteDisk::GetName(char *nameBuffer, size_t bufferSize) const 168d561d0adSIngo Weinhold { 169d561d0adSIngo Weinhold if (!nameBuffer) 170d561d0adSIngo Weinhold return B_BAD_VALUE; 171d561d0adSIngo Weinhold 172*211483cbSFredrik Holmqvist snprintf(nameBuffer, bufferSize, 173*211483cbSFredrik Holmqvist "RemoteDisk:%" B_PRIu32 ".%" B_PRIu32 ".%" B_PRIu32 ".%" B_PRIu32 ":%hd", 174d561d0adSIngo Weinhold (fServerAddress >> 24) & 0xff, (fServerAddress >> 16) & 0xff, 175d561d0adSIngo Weinhold (fServerAddress >> 8) & 0xff, fServerAddress & 0xff, fServerPort); 176d561d0adSIngo Weinhold 177d561d0adSIngo Weinhold return B_OK; 178d561d0adSIngo Weinhold } 179d561d0adSIngo Weinhold 180d561d0adSIngo Weinhold // Size 181d561d0adSIngo Weinhold off_t 182d561d0adSIngo Weinhold RemoteDisk::Size() const 183d561d0adSIngo Weinhold { 184d561d0adSIngo Weinhold return fImageSize; 185d561d0adSIngo Weinhold } 186d561d0adSIngo Weinhold 18743792b9eSMarcus Overhagen ip_addr_t 18843792b9eSMarcus Overhagen RemoteDisk::ServerIPAddress() const 18943792b9eSMarcus Overhagen { 19043792b9eSMarcus Overhagen return fServerAddress; 19143792b9eSMarcus Overhagen } 19243792b9eSMarcus Overhagen 19343792b9eSMarcus Overhagen uint16 19443792b9eSMarcus Overhagen RemoteDisk::ServerPort() const 19543792b9eSMarcus Overhagen { 19643792b9eSMarcus Overhagen return fServerPort; 19743792b9eSMarcus Overhagen } 19843792b9eSMarcus Overhagen 199d561d0adSIngo Weinhold // FindAnyRemoteDisk 200d561d0adSIngo Weinhold RemoteDisk * 201d561d0adSIngo Weinhold RemoteDisk::FindAnyRemoteDisk() 202d561d0adSIngo Weinhold { 203d561d0adSIngo Weinhold // create a socket and bind it 204d561d0adSIngo Weinhold UDPSocket socket; 205d561d0adSIngo Weinhold status_t error = socket.Bind(INADDR_ANY, 6665); 206d561d0adSIngo Weinhold if (error != B_OK) { 207d561d0adSIngo Weinhold printf("RemoteDisk::GetAnyRemoteDisk(): Failed to bind socket.\n"); 208d561d0adSIngo Weinhold return NULL; 209d561d0adSIngo Weinhold } 210d561d0adSIngo Weinhold 211d561d0adSIngo Weinhold // prepare request 212d561d0adSIngo Weinhold remote_disk_header request; 213d561d0adSIngo Weinhold request.command = REMOTE_DISK_HELLO_REQUEST; 214d561d0adSIngo Weinhold 215d561d0adSIngo Weinhold // send request 216d561d0adSIngo Weinhold UDPPacket *packet; 217d561d0adSIngo Weinhold error = _SendRequest(&socket, INADDR_BROADCAST, REMOTE_DISK_SERVER_PORT, 218d561d0adSIngo Weinhold &request, sizeof(request), REMOTE_DISK_HELLO_REPLY, &packet); 219d561d0adSIngo Weinhold if (error != B_OK) { 220d561d0adSIngo Weinhold printf("RemoteDisk::GetAnyRemoteDisk(): Got no server reply.\n"); 221d561d0adSIngo Weinhold return NULL; 222d561d0adSIngo Weinhold } 223d561d0adSIngo Weinhold remote_disk_header *reply = (remote_disk_header*)packet->Data(); 224d561d0adSIngo Weinhold 225d561d0adSIngo Weinhold // create a RemoteDisk object 226d561d0adSIngo Weinhold RemoteDisk *remoteDisk = new(nothrow) RemoteDisk; 227d561d0adSIngo Weinhold if (remoteDisk) { 228d561d0adSIngo Weinhold error = remoteDisk->Init(packet->SourceAddress(), ntohs(reply->port), 229d561d0adSIngo Weinhold ntohll(reply->offset)); 230d561d0adSIngo Weinhold if (error != B_OK) { 231d561d0adSIngo Weinhold delete remoteDisk; 232d561d0adSIngo Weinhold remoteDisk = NULL; 233d561d0adSIngo Weinhold } 234d561d0adSIngo Weinhold } 235d561d0adSIngo Weinhold 236d561d0adSIngo Weinhold delete packet; 237d561d0adSIngo Weinhold 238d561d0adSIngo Weinhold return remoteDisk; 239d561d0adSIngo Weinhold } 240d561d0adSIngo Weinhold 241d561d0adSIngo Weinhold // _ReadFromPacket 242d561d0adSIngo Weinhold ssize_t 243d561d0adSIngo Weinhold RemoteDisk::_ReadFromPacket(off_t &pos, uint8 *&buffer, size_t &bufferSize) 244d561d0adSIngo Weinhold { 245d561d0adSIngo Weinhold if (!fPacket) 246d561d0adSIngo Weinhold return 0; 247d561d0adSIngo Weinhold 248d561d0adSIngo Weinhold remote_disk_header *header = (remote_disk_header*)fPacket->Data(); 249d561d0adSIngo Weinhold uint64 packetOffset = ntohll(header->offset); 250d561d0adSIngo Weinhold uint32 packetSize = ntohs(header->size); 251d561d0adSIngo Weinhold if (packetOffset > (uint64)pos || packetOffset + packetSize <= (uint64)pos) 252d561d0adSIngo Weinhold return 0; 253d561d0adSIngo Weinhold 254d561d0adSIngo Weinhold // we do indeed have some bytes already 255d561d0adSIngo Weinhold size_t toCopy = size_t(packetOffset + packetSize - (uint64)pos); 256d561d0adSIngo Weinhold if (toCopy > bufferSize) 257d561d0adSIngo Weinhold toCopy = bufferSize; 258d561d0adSIngo Weinhold memcpy(buffer, header->data + (pos - packetOffset), toCopy); 259d561d0adSIngo Weinhold 260d561d0adSIngo Weinhold pos += toCopy; 261d561d0adSIngo Weinhold buffer += toCopy; 262d561d0adSIngo Weinhold bufferSize -= toCopy; 263d561d0adSIngo Weinhold return toCopy; 264d561d0adSIngo Weinhold } 265d561d0adSIngo Weinhold 266d561d0adSIngo Weinhold // _SendRequest 267d561d0adSIngo Weinhold status_t 268d561d0adSIngo Weinhold RemoteDisk::_SendRequest(UDPSocket *socket, ip_addr_t serverAddress, 269d561d0adSIngo Weinhold uint16 serverPort, remote_disk_header *request, size_t size, 270d561d0adSIngo Weinhold uint8 expectedReply, UDPPacket **_packet) 271d561d0adSIngo Weinhold { 272d561d0adSIngo Weinhold request->port = htons(socket->Port()); 273d561d0adSIngo Weinhold 274d561d0adSIngo Weinhold // try sending the request 3 times at most 275d561d0adSIngo Weinhold for (int i = 0; i < 3; i++) { 276d561d0adSIngo Weinhold // send request 277d561d0adSIngo Weinhold status_t error = socket->Send(serverAddress, serverPort, request, size); 278d561d0adSIngo Weinhold if (error != B_OK) 279d561d0adSIngo Weinhold return error; 280d561d0adSIngo Weinhold 281d561d0adSIngo Weinhold // receive reply 28276cd432cSIngo Weinhold bigtime_t timeout = system_time() + kRequestTimeout; 283d561d0adSIngo Weinhold do { 284d561d0adSIngo Weinhold UDPPacket *packet; 285d561d0adSIngo Weinhold error = socket->Receive(&packet, timeout - system_time()); 286d561d0adSIngo Weinhold if (error == B_OK) { 287d561d0adSIngo Weinhold // got something; check, if it is looks good 288d561d0adSIngo Weinhold if (packet->DataSize() >= sizeof(remote_disk_header)) { 289d561d0adSIngo Weinhold remote_disk_header *reply 290d561d0adSIngo Weinhold = (remote_disk_header*)packet->Data(); 291d561d0adSIngo Weinhold if (reply->request_id == request->request_id 292d561d0adSIngo Weinhold && reply->command == expectedReply) { 293d561d0adSIngo Weinhold *_packet = packet; 294d561d0adSIngo Weinhold return B_OK; 295d561d0adSIngo Weinhold } 296d561d0adSIngo Weinhold } 297d561d0adSIngo Weinhold 298d561d0adSIngo Weinhold // reply not OK 299d561d0adSIngo Weinhold delete packet; 300d561d0adSIngo Weinhold } else if (error != B_TIMED_OUT && error != B_WOULD_BLOCK) 301d561d0adSIngo Weinhold return error; 302d561d0adSIngo Weinhold 303d561d0adSIngo Weinhold } while (timeout > system_time()); 304d561d0adSIngo Weinhold } 305d561d0adSIngo Weinhold 306d561d0adSIngo Weinhold // no reply 307d561d0adSIngo Weinhold return B_ERROR; 308d561d0adSIngo Weinhold } 309d561d0adSIngo Weinhold 310d561d0adSIngo Weinhold // _SendRequest 311d561d0adSIngo Weinhold status_t 312d561d0adSIngo Weinhold RemoteDisk::_SendRequest(remote_disk_header *request, size_t size, 313d561d0adSIngo Weinhold uint8 expectedReply, UDPPacket **packet) 314d561d0adSIngo Weinhold { 315d561d0adSIngo Weinhold request->request_id = fRequestID++; 316d561d0adSIngo Weinhold return _SendRequest(fSocket, fServerAddress, fServerPort, request, size, 317d561d0adSIngo Weinhold expectedReply, packet); 318d561d0adSIngo Weinhold } 319