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
swap_uint64(uint64_t data)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
RemoteDisk()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
~RemoteDisk()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
Init(ip_addr_t serverAddress,uint16 serverPort,off_t imageSize)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
ReadAt(void *,off_t pos,void * _buffer,size_t bufferSize)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
WriteAt(void *,off_t pos,const void * buffer,size_t bufferSize)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
GetName(char * nameBuffer,size_t bufferSize) const167d561d0adSIngo 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
Size() const182d561d0adSIngo Weinhold RemoteDisk::Size() const
183d561d0adSIngo Weinhold {
184d561d0adSIngo Weinhold return fImageSize;
185d561d0adSIngo Weinhold }
186d561d0adSIngo Weinhold
18743792b9eSMarcus Overhagen ip_addr_t
ServerIPAddress() const18843792b9eSMarcus Overhagen RemoteDisk::ServerIPAddress() const
18943792b9eSMarcus Overhagen {
19043792b9eSMarcus Overhagen return fServerAddress;
19143792b9eSMarcus Overhagen }
19243792b9eSMarcus Overhagen
19343792b9eSMarcus Overhagen uint16
ServerPort() const19443792b9eSMarcus Overhagen RemoteDisk::ServerPort() const
19543792b9eSMarcus Overhagen {
19643792b9eSMarcus Overhagen return fServerPort;
19743792b9eSMarcus Overhagen }
19843792b9eSMarcus Overhagen
199d561d0adSIngo Weinhold // FindAnyRemoteDisk
200d561d0adSIngo Weinhold RemoteDisk *
FindAnyRemoteDisk()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
_ReadFromPacket(off_t & pos,uint8 * & buffer,size_t & bufferSize)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
_SendRequest(UDPSocket * socket,ip_addr_t serverAddress,uint16 serverPort,remote_disk_header * request,size_t size,uint8 expectedReply,UDPPacket ** _packet)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
_SendRequest(remote_disk_header * request,size_t size,uint8 expectedReply,UDPPacket ** packet)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