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