Added a mini networking stack to the boot loader. It speaks basic ARP,

IP, and UDP, as well as a home brewn UDP based protocol, "remote disk",
which provides random access to a single remote file/device. The Open
Firmware flavored boot loader automatically initializes the net stack,
searches for a remote disk, and tries to boot from it, if the boot
device is a network device (e.g. when loading the boot loader via
TFTP).

This is quite nice for developing with a two-machine setup, since one
doesn't even need to install Haiku on the test machine anymore, but can
serve it directly from the development machine. When the networking
support in the kernel is working, this method could even be used to
fully boot, not just for loading kernel and initial modules.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15689 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2005-12-27 22:01:33 +00:00
parent 9d577c1064
commit d561d0ad68
24 changed files with 2815 additions and 1 deletions

View File

@ -0,0 +1,49 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _BOOT_ARP_H
#define _BOOT_ARP_H
#include <boot/net/Ethernet.h>
#include <OS.h>
class ARPService : public EthernetSubService {
// actually ARP for IP over ethernet
public:
ARPService(EthernetService *ethernet);
virtual ~ARPService();
status_t Init();
virtual uint16 EthernetProtocol() const;
virtual void HandleEthernetPacket(EthernetService *ethernet,
const mac_addr_t &targetAddress, const void *data, size_t size);
status_t GetMACForIP(ip_addr_t ip, mac_addr_t &mac);
private:
enum { MAP_ENTRY_COUNT = 10 };
enum { ARP_REQUEST_RETRY_COUNT = 3 };
enum { ARP_REPLY_TIMEOUT = 5000 };
struct MapEntry {
int32 age;
ip_addr_t ip;
mac_addr_t mac;
};
status_t _SendARPPacket(ip_addr_t ip, const mac_addr_t &mac, uint16 opcode);
MapEntry *_FindEntry(ip_addr_t ip);
void _PutEntry(ip_addr_t ip, const mac_addr_t &mac);
EthernetService *fEthernet;
int32 fAge;
MapEntry fEntries[MAP_ENTRY_COUNT];
};
#endif // _BOOT_ARP_H

View File

@ -0,0 +1,49 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _BOOT_NET_CHAIN_BUFFER_H
#define _BOOT_NET_CHAIN_BUFFER_H
#include <SupportDefs.h>
class ChainBuffer {
public:
ChainBuffer(void *data = 0, uint32 size = 0, ChainBuffer *next = NULL,
bool freeData = false);
~ChainBuffer();
void *Data() const { return fData; }
uint32 Size() const { return fSize; }
uint32 TotalSize() const { return fTotalSize; }
ChainBuffer *Next() const { return fNext; }
ChainBuffer *DetachNext();
void Append(ChainBuffer *next);
void Flatten(void *_buffer) const;
private:
// TODO: Implement Create() and Delete(). Make new and delete operators private.
enum {
CHAIN_BUFFER_HEAD = 0x1,
CHAIN_BUFFER_EMBEDDED_DATA = 0x2,
CHAIN_BUFFER_FREE_DATA = 0x4,
CHAIN_BUFFER_ON_STACK = 0x8,
};
void _Init(void *data, uint32 size, ChainBuffer *next, uint32 flags);
void _Destroy();
uint32 fFlags:4;
uint32 fSize:14;
uint32 fTotalSize:14;
void *fData;
ChainBuffer *fNext;
uint8 fBuffer[0];
};
#endif // _BOOT_NET_CHAIN_BUFFER_H

View File

@ -0,0 +1,84 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _BOOT_ETHERNET_H
#define _BOOT_ETHERNET_H
#include <boot/net/NetDefs.h>
#include <util/Vector.h>
class ChainBuffer;
class EthernetService;
// EthernetInterface
class EthernetInterface {
public:
EthernetInterface();
virtual ~EthernetInterface();
ip_addr_t IPAddress() const;
void SetIPAddress(ip_addr_t ipAddress);
virtual mac_addr_t MACAddress() const = 0;
virtual void *AllocateSendReceiveBuffer(size_t size) = 0;
virtual void FreeSendReceiveBuffer(void *buffer) = 0;
virtual ssize_t Send(const void *buffer, size_t size) = 0;
virtual ssize_t Receive(void *buffer, size_t size) = 0;
protected:
ip_addr_t fIPAddress;
};
// EthernetSubService
class EthernetSubService : public NetService {
public:
EthernetSubService(const char *serviceName);
virtual ~EthernetSubService();
virtual uint16 EthernetProtocol() const = 0;
virtual void HandleEthernetPacket(EthernetService *ethernet,
const mac_addr_t &targetAddress, const void *data, size_t size) = 0;
};
// EthernetService
class EthernetService : public NetService {
public:
EthernetService();
virtual ~EthernetService();
status_t Init(EthernetInterface *interface);
mac_addr_t MACAddress() const;
ip_addr_t IPAddress() const;
void SetIPAddress(ip_addr_t ipAddress);
status_t Send(const mac_addr_t &destination, uint16 protocol,
ChainBuffer *buffer);
void ProcessIncomingPackets();
bool RegisterEthernetSubService(EthernetSubService *service);
bool UnregisterEthernetSubService(EthernetSubService *service);
virtual int CountSubNetServices() const;
virtual NetService *SubNetServiceAt(int index) const;
private:
enum {
SEND_BUFFER_SIZE = 2048,
RECEIVE_BUFFER_SIZE = SEND_BUFFER_SIZE,
};
EthernetInterface *fInterface;
void *fSendBuffer;
void *fReceiveBuffer;
Vector<EthernetSubService*> fServices;
};
#endif // _BOOT_ETHERNET_H

View File

@ -0,0 +1,64 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _BOOT_IP_H
#define _BOOT_IP_H
#include <boot/net/Ethernet.h>
class ARPService;
class IPService;
// IPSubService
class IPSubService : public NetService {
public:
IPSubService(const char *serviceName);
virtual ~IPSubService();
virtual uint8 IPProtocol() const = 0;
virtual void HandleIPPacket(IPService *ipService, ip_addr_t sourceIP,
ip_addr_t destinationIP, const void *data, size_t size) = 0;
};
// IPService
class IPService : public EthernetSubService {
// actually IP over ethernet
public:
IPService(EthernetService *ethernet, ARPService *arpService);
virtual ~IPService();
status_t Init();
ip_addr_t IPAddress() const;
virtual uint16 EthernetProtocol() const;
virtual void HandleEthernetPacket(EthernetService *ethernet,
const mac_addr_t &targetAddress, const void *data, size_t size);
status_t Send(ip_addr_t destination, uint8 protocol, ChainBuffer *buffer);
void ProcessIncomingPackets();
bool RegisterIPSubService(IPSubService *service);
bool UnregisterIPSubService(IPSubService *service);
virtual int CountSubNetServices() const;
virtual NetService *SubNetServiceAt(int index) const;
private:
uint16 _Checksum(const ip_header &header);
EthernetService *fEthernet;
ARPService *fARPService;
Vector<IPSubService*> fServices;
};
uint16 ip_checksum(ChainBuffer *buffer);
#endif // _BOOT_IP_H

View File

@ -0,0 +1,207 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _BOOT_NET_DEFS_H
#define _BOOT_NET_DEFS_H
#include <string.h>
#include <ByteOrder.h>
#include <SupportDefs.h>
#include <util/kernel_cpp.h>
// Ethernet
#define ETH_ALEN 6
#define ETHERTYPE_IP 0x0800 // IP
#define ETHERTYPE_ARP 0x0806 // Address resolution
#define ETHER_MIN_TRANSFER_UNIT 46
#define ETHER_MAX_TRANSFER_UNIT 1500
struct mac_addr_t {
mac_addr_t() {}
mac_addr_t(uint8 *address)
{
memcpy(this->address, address, ETH_ALEN);
}
mac_addr_t(const mac_addr_t& other)
{
memcpy(address, other.address, sizeof(address));
}
uint64 ToUInt64() const
{
return ((uint64)address[0] << 40)
| ((uint64)address[1] << 32)
| ((uint64)address[2] << 24)
| ((uint64)address[3] << 16)
| ((uint64)address[4] << 8)
| (uint64)address[5];
}
mac_addr_t& operator=(const mac_addr_t& other)
{
memcpy(address, other.address, sizeof(address));
return *this;
}
bool operator==(const mac_addr_t& other) const
{
return memcmp(address, other.address, sizeof(address)) == 0;
}
bool operator!=(const mac_addr_t& other) const
{
return !(*this == other);
}
uint8 address[ETH_ALEN];
} __attribute__ ((__packed__));
extern const mac_addr_t kBroadcastMACAddress;
extern const mac_addr_t kNoMACAddress;
// 10/100 Mb/s ethernet header
struct ether_header {
mac_addr_t destination; /* destination eth addr */
mac_addr_t source; /* source ether addr */
uint16 type; /* packet type ID field */
} __attribute__ ((__packed__));
// #pragma mark -
// Address Resolution Protocol (ARP)
typedef uint32 ip_addr_t;
// ARP protocol opcodes
#define ARPOP_REQUEST 1 /* ARP request. */
#define ARPOP_REPLY 2 /* ARP reply. */
#define ARPOP_RREQUEST 3 /* RARP request. */
#define ARPOP_RREPLY 4 /* RARP reply. */
#define ARPOP_InREQUEST 8 /* InARP request. */
#define ARPOP_InREPLY 9 /* InARP reply. */
#define ARPOP_NAK 10 /* (ATM)ARP NAK. */
// ARP header for IP over ethernet (RFC 826)
struct arp_header {
uint16 hardware_format; /* Format of hardware address. */
uint16 protocol_format; /* Format of protocol address. */
uint8 hardware_length; /* Length of hardware address. */
uint8 protocol_length; /* Length of protocol address. */
uint16 opcode; /* ARP opcode (command). */
// IP over ethernet
mac_addr_t sender_mac; /* Sender hardware address. */
ip_addr_t sender_ip; /* Sender IP address. */
mac_addr_t target_mac; /* Target hardware address. */
ip_addr_t target_ip; /* Target IP address. */
} __attribute__ ((__packed__));
// ARP protocol HARDWARE identifiers.
#define ARPHRD_ETHER 1 /* Ethernet 10/100Mbps. */
// #pragma mark -
// Internet Protocol (IP)
#define INADDR_ANY ((ip_addr_t) 0x00000000)
/* Address to send to all hosts. */
#define INADDR_BROADCAST ((ip_addr_t) 0xffffffff)
/* Address indicating an error return. */
#define INADDR_NONE ((ip_addr_t) 0xffffffff)
// IP packet header (no options
struct ip_header {
#if __BYTE_ORDER == __LITTLE_ENDIAN
uint8 header_length:4; // header length
uint8 version:4; // IP protocol version
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
uint8 version:4; // IP protocol version
uint8 header_length:4; // header length
#endif
uint8 type_of_service; // type of service
uint16 total_length; // total IP packet length
uint16 identifier; // fragment identification
uint16 fragment_offset; // fragment offset and flags (0xe000)
uint8 time_to_live; // time to live
uint8 protocol; // protocol
uint16 checksum; // checksum (header)
ip_addr_t source; // source IP address
ip_addr_t destination; // destination IP address
} __attribute__ ((__packed__));
// IP protocol version 4
#define IP_PROTOCOL_VERSION_4 4
// fragment flags/offset mask
#define IP_DONT_FRAGMENT 0x4000 /* dont fragment flag */
#define IP_FRAGMENT_OFFSET_MASK 0x1fff /* mask for fragment offset */
// Internet implementation parameters.
#define IP_MAX_TIME_TO_LIVE 255 /* maximum time to live */
#define IP_DEFAULT_TIME_TO_LIVE 64 /* default ttl, from RFC 1340 */
// IP protocols
#define IPPROTO_UDP 17
// #pragma mark -
// User Datagram Protocol (UDP)
// UDP header (RFC 768)
struct udp_header
{
uint16 source; // source port
uint16 destination; // destination port
uint16 length; // length of UDP packet (header + data)
uint16 checksum; // checksum
} __attribute__ ((__packed__));
// #pragma mark -
// NetService
// net service names
extern const char *const kEthernetServiceName;
extern const char *const kARPServiceName;
extern const char *const kIPServiceName;
extern const char *const kUDPServiceName;
class NetService {
public:
NetService(const char *name);
virtual ~NetService();
const char *NetServiceName();
virtual int CountSubNetServices() const;
virtual NetService *SubNetServiceAt(int index) const;
virtual NetService *FindSubNetService(const char *name) const;
template<typename ServiceType>
ServiceType *FindSubNetService(const char *name) const
{
// We should actually use dynamic_cast<>(), but we better spare us the
// RTTI stuff.
if (NetService *service = FindSubNetService(name))
return static_cast<ServiceType*>(service);
return NULL;
}
private:
const char *fName;
};
#endif // _BOOT_NET_DEFS_H

View File

@ -0,0 +1,55 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _BOOT_NET_STACK_H
#define _BOOT_NET_STACK_H
#include <SupportDefs.h>
class EthernetInterface;
class EthernetService;
class ARPService;
class IPService;
class UDPService;
class NetStack {
private:
NetStack();
~NetStack();
status_t Init();
public:
static status_t CreateDefault();
static NetStack *Default();
status_t AddEthernetInterface(EthernetInterface *interface);
EthernetInterface *GetEthernetInterface() const
{ return fEthernetInterface; }
EthernetService *GetEthernetService() const { return fEthernetService; }
ARPService *GetARPService() const { return fARPService; }
IPService *GetIPService() const { return fIPService; }
UDPService *GetUDPService() const { return fUDPService; }
private:
static NetStack *sNetStack;
EthernetInterface *fEthernetInterface;
EthernetService *fEthernetService;
ARPService *fARPService;
IPService *fIPService;
UDPService *fUDPService;
};
// net_stack_init() creates the NetStack and calls platform_net_stack_init()
// afterwards, which is supposed to add network interfaces.
status_t net_stack_init();
status_t platform_net_stack_init();
#endif // _BOOT_NET_STACK_H

View File

@ -0,0 +1,51 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _BOOT_REMOTE_DISK_H
#define _BOOT_REMOTE_DISK_H
#include <boot/vfs.h>
#include <boot/net/NetDefs.h>
#include <boot/net/RemoteDiskDefs.h>
class UDPPacket;
class UDPSocket;
class RemoteDisk : public Node {
public:
RemoteDisk();
~RemoteDisk();
status_t Init(ip_addr_t serverAddress, uint16 serverPort, off_t imageSize);
virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer,
size_t bufferSize);
virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer,
size_t bufferSize);
virtual status_t GetName(char *nameBuffer, size_t bufferSize) const;
virtual off_t Size() const;
static RemoteDisk *FindAnyRemoteDisk();
private:
ssize_t _ReadFromPacket(off_t &pos, uint8 *&buffer, size_t &bufferSize);
static status_t _SendRequest(UDPSocket *socket, ip_addr_t serverAddress,
uint16 serverPort, remote_disk_header *request, size_t size,
uint8 expectedReply, UDPPacket **packet);
status_t _SendRequest(remote_disk_header *request, size_t size,
uint8 expectedReply, UDPPacket **packet);
private:
ip_addr_t fServerAddress;
uint16 fServerPort;
off_t fImageSize;
uint64 fRequestID;
UDPSocket *fSocket;
UDPPacket *fPacket;
};
#endif // _BOOT_REMOTE_DISK_H

View File

@ -0,0 +1,63 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _BOOT_REMOTE_DISK_DEFS_H
#define _BOOT_REMOTE_DISK_DEFS_H
#include <stdint.h>
enum {
REMOTE_DISK_SERVER_PORT = 8765,
REMOTE_DISK_BLOCK_SIZE = 1024,
};
enum {
// requests
REMOTE_DISK_HELLO_REQUEST = 0,
// port: client port
REMOTE_DISK_READ_REQUEST = 1,
// port: client port
// offset: byte offset of data to read
// size: number of bytes to read (server might serve more, though)
REMOTE_DISK_WRITE_REQUEST = 2,
// port: client port
// offset: byte offset of data to write
// size: number of bytes to write
// data: the data
// replies
REMOTE_DISK_HELLO_REPLY = 3,
// offset: disk size
REMOTE_DISK_READ_REPLY = 4, // port unused
// offset: byte offset of read data
// size: number of bytes of data read; < 0 => error
// data: read data
REMOTE_DISK_WRITE_REPLY = 5, // port, data unused
// offset: byte offset of data written
// size: number of bytes of data written; < 0 => error
};
// errors
enum {
REMOTE_DISK_IO_ERROR = -1,
REMOTE_DISK_BAD_REQUEST = -2,
};
struct remote_disk_header {
uint64_t offset;
uint64_t request_id;
int16_t size;
uint16_t port;
uint8_t command;
uint8_t data[0];
} __attribute__ ((__packed__));
#endif // _BOOT_REMOTE_DISK_DEFS_H

View File

@ -0,0 +1,107 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _BOOT_UDP_H
#define _BOOT_UDP_H
#include <boot/net/IP.h>
// UDPPacket
class UDPPacket {
public:
UDPPacket();
~UDPPacket();
status_t SetTo(const void *data, size_t size, ip_addr_t sourceAddress,
uint16 sourcePort, ip_addr_t destinationAddress,
uint16 destinationPort);
UDPPacket *Next() const;
void SetNext(UDPPacket *next);
const void *Data() const;
size_t DataSize() const;
ip_addr_t SourceAddress() const;
uint16 SourcePort() const;
ip_addr_t DestinationAddress() const;
uint16 DestinationPort() const;
private:
UDPPacket *fNext;
void *fData;
size_t fSize;
ip_addr_t fSourceAddress;
ip_addr_t fDestinationAddress;
uint16 fSourcePort;
uint16 fDestinationPort;
};
class UDPService;
// UDPSocket
class UDPSocket {
public:
UDPSocket();
~UDPSocket();
ip_addr_t Address() const { return fAddress; }
uint16 Port() const { return fPort; }
status_t Bind(ip_addr_t address, uint16 port);
status_t Send(ip_addr_t destinationAddress, uint16 destinationPort,
ChainBuffer *buffer);
status_t Send(ip_addr_t destinationAddress, uint16 destinationPort,
const void *data, size_t size);
status_t Receive(UDPPacket **packet, bigtime_t timeout = 0);
void PushPacket(UDPPacket *packet);
UDPPacket *PopPacket();
private:
UDPService *fUDPService;
UDPPacket *fFirstPacket;
UDPPacket *fLastPacket;
ip_addr_t fAddress;
uint16 fPort;
};
// UDPService
class UDPService : public IPSubService {
public:
UDPService(IPService *ipService);
virtual ~UDPService();
status_t Init();
virtual uint8 IPProtocol() const;
virtual void HandleIPPacket(IPService *ipService, ip_addr_t sourceIP,
ip_addr_t destinationIP, const void *data, size_t size);
status_t Send(uint16 sourcePort, ip_addr_t destinationAddress,
uint16 destinationPort, ChainBuffer *buffer);
void ProcessIncomingPackets();
status_t BindSocket(UDPSocket *socket, ip_addr_t address, uint16 port);
void UnbindSocket(UDPSocket *socket);
private:
uint16 _ChecksumBuffer(ChainBuffer *buffer, ip_addr_t source,
ip_addr_t destination, uint16 length);
uint16 _ChecksumData(const void *data, uint16 length, ip_addr_t source,
ip_addr_t destination);
UDPSocket *_FindSocket(ip_addr_t address, uint16 port);
IPService *fIPService;
Vector<UDPSocket*> fSockets;
};
#endif // _BOOT_UDP_H

View File

@ -32,6 +32,7 @@ if $(TARGET_ARCH) = x86 {
KernelLd boot_loader :
boot_platform_$(TARGET_BOOT_PLATFORM).o
boot_loader.a
boot_net.a
boot_partitions.a
# file systems

View File

@ -84,3 +84,4 @@ SEARCH on [ FGristFiles stage2_crt0.S ]
SubInclude HAIKU_TOP src system boot loader file_systems ;
SubInclude HAIKU_TOP src system boot loader net ;

View File

@ -0,0 +1,216 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <boot/net/ARP.h>
#include <stdio.h>
#include <boot/net/ChainBuffer.h>
//#define TRACE_ARP
#ifdef TRACE_ARP
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
// constructor
ARPService::ARPService(EthernetService *ethernet)
: EthernetSubService(kARPServiceName),
fEthernet(ethernet),
fAge(0)
{
// clear table
for (int i = 0; i < MAP_ENTRY_COUNT; i++)
fEntries[i].ip = INADDR_ANY;
}
// destructor
ARPService::~ARPService()
{
if (fEthernet)
fEthernet->UnregisterEthernetSubService(this);
}
// Init
status_t
ARPService::Init()
{
if (!fEthernet)
return B_BAD_VALUE;
if (!fEthernet->RegisterEthernetSubService(this))
return B_NO_MEMORY;
return B_OK;
}
// EthernetProtocol
uint16
ARPService::EthernetProtocol() const
{
return ETHERTYPE_ARP;
}
// HandleEthernetPacket
void
ARPService::HandleEthernetPacket(EthernetService *ethernet,
const mac_addr_t &targetAddress, const void *data, size_t size)
{
TRACE(("ARPService::HandleEthernetPacket(): %lu - %lu bytes\n", size,
sizeof(ip_header)));
if (size < sizeof(arp_header))
return;
arp_header *header = (arp_header*)data;
// check packet validity
if (header->hardware_format != htons(ARPHRD_ETHER)
|| header->protocol_format != htons(ETHERTYPE_IP)
|| header->hardware_length != sizeof(mac_addr_t)
|| header->protocol_length != sizeof(ip_addr_t)
// valid sender MAC?
|| header->sender_mac == kNoMACAddress
|| header->sender_mac == kBroadcastMACAddress
// do we support the opcode?
|| (header->opcode != htons(ARPOP_REQUEST)
&& header->opcode != htons(ARPOP_REPLY))) {
return;
}
// if this is a request, we continue only, if we have the targeted IP
if (header->opcode == htons(ARPOP_REQUEST)
&& (fEthernet->IPAddress() == INADDR_ANY
|| header->target_ip != htonl(fEthernet->IPAddress()))) {
return;
}
// if this is a reqly, we accept it only, if it was directly sent to us
if (header->opcode == htons(ARPOP_REPLY)
&& (targetAddress != fEthernet->MACAddress()
|| header->target_mac != targetAddress)) {
return;
}
// if sender IP looks valid, enter the mapping
if (header->sender_ip != htonl(INADDR_ANY)
&& header->sender_ip != htonl(INADDR_BROADCAST)) {
_PutEntry(ntohl(header->sender_ip), header->sender_mac);
}
// if this is a request, send a reply
if (header->opcode == htons(ARPOP_REQUEST)) {
_SendARPPacket(ntohl(header->sender_ip), header->sender_mac,
ARPOP_REPLY);
}
}
// GetMACForIP
status_t
ARPService::GetMACForIP(ip_addr_t ip, mac_addr_t &mac)
{
TRACE(("ARPService::GetMACForIP(%08lx)\n", ip));
if (ip == INADDR_ANY)
return B_BAD_VALUE;
if (ip == INADDR_BROADCAST) {
mac = kBroadcastMACAddress;
TRACE(("ARPService::GetMACForIP(%08lx) done: %012llx\n", ip,
mac.ToUInt64()));
return B_OK;
}
// already known?
if (MapEntry *entry = _FindEntry(ip)) {
mac = entry->mac;
TRACE(("ARPService::GetMACForIP(%08lx) done: %012llx\n", ip,
mac.ToUInt64()));
return B_OK;
}
for (int i = 0; i < ARP_REQUEST_RETRY_COUNT; i++) {
// send request
status_t error = _SendARPPacket(ip, kBroadcastMACAddress,
ARPOP_REQUEST);
if (error != B_OK) {
TRACE(("ARPService::GetMACForIP(%08lx) failed: sending failed\n",
ip));
return error;
}
bigtime_t startTime = system_time();
do {
fEthernet->ProcessIncomingPackets();
// received reply?
if (MapEntry *entry = _FindEntry(ip)) {
mac = entry->mac;
TRACE(("ARPService::GetMACForIP(%08lx) done: %012llx\n", ip,
mac.ToUInt64()));
return B_OK;
}
} while (system_time() - startTime < ARP_REPLY_TIMEOUT);
}
TRACE(("ARPService::GetMACForIP(%08lx) failed: no reply\n", ip));
return EHOSTUNREACH;
}
// _SendARPPacket
status_t
ARPService::_SendARPPacket(ip_addr_t ip, const mac_addr_t &mac, uint16 opcode)
{
// prepare ARP header
arp_header header;
ChainBuffer headerBuffer(&header, sizeof(header));
header.hardware_format = htons(ARPHRD_ETHER);
header.protocol_format = htons(ETHERTYPE_IP);
header.hardware_length = sizeof(mac_addr_t);
header.protocol_length = sizeof(ip_addr_t);
header.opcode = htons(opcode);
header.sender_mac = fEthernet->MACAddress();
header.sender_ip = htonl(fEthernet->IPAddress());
header.target_mac = (mac == kBroadcastMACAddress ? kNoMACAddress : mac);
header.target_ip = htonl(ip);
return fEthernet->Send(mac, ETHERTYPE_ARP, &headerBuffer);
}
// _FindEntry
ARPService::MapEntry *
ARPService::_FindEntry(ip_addr_t ip)
{
if (ip == INADDR_ANY)
return NULL;
for (int i = 0; i < MAP_ENTRY_COUNT; i++) {
if (ip == fEntries[i].ip)
return fEntries + i;
}
return NULL;
}
// _PutEntry
void
ARPService::_PutEntry(ip_addr_t ip, const mac_addr_t &mac)
{
// find empty/oldest slot
MapEntry *entry = fEntries;
for (int i = 0; i < MAP_ENTRY_COUNT; i++) {
if (fEntries[i].ip == INADDR_ANY) {
entry = fEntries + i;
break;
}
if (fAge - fEntries[i].age > fAge - entry->age)
entry = fEntries + i;
}
entry->age = fAge++;
entry->ip = ip;
entry->mac = mac;
}

View File

@ -0,0 +1,106 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <boot/net/ChainBuffer.h>
#include <stdlib.h>
#include <string.h>
#include <util/kernel_cpp.h>
// constructor
ChainBuffer::ChainBuffer(void *data, uint32 size, ChainBuffer *next,
bool freeData)
{
_Init(data, size, next,
CHAIN_BUFFER_ON_STACK | (freeData ? CHAIN_BUFFER_FREE_DATA : 0));
}
// destructor
ChainBuffer::~ChainBuffer()
{
_Destroy();
}
// DetachNext
ChainBuffer *
ChainBuffer::DetachNext()
{
if (!fNext)
return NULL;
ChainBuffer *next = fNext;
fNext = NULL;
next->fFlags |= CHAIN_BUFFER_HEAD;
fTotalSize = fSize;
return next;
}
// Append
void
ChainBuffer::Append(ChainBuffer *next)
{
if (!next)
return;
if (fNext)
fNext->Append(next);
else
fNext = next;
fTotalSize = fSize + fNext->fTotalSize;
}
// Flatten
void
ChainBuffer::Flatten(void *_buffer) const
{
if (uint8 *buffer = (uint8*)_buffer) {
if (fData && fSize > 0) {
memcpy(buffer, fData, fSize);
buffer += fSize;
}
if (fNext)
fNext->Flatten(buffer);
}
}
// _Init
void
ChainBuffer::_Init(void *data, uint32 size, ChainBuffer *next, uint32 flags)
{
fFlags = flags | CHAIN_BUFFER_HEAD;
fSize = size;
fTotalSize = fSize;
fData = data;
fNext = NULL;
Append(next);
}
// _Destroy
void
ChainBuffer::_Destroy()
{
ChainBuffer *next = fNext;
fNext = NULL;
if ((fFlags & CHAIN_BUFFER_FREE_DATA) && fData) {
free(fData);
fData = NULL;
}
if (!(fFlags & CHAIN_BUFFER_EMBEDDED_DATA))
fSize = 0;
fTotalSize = fSize;
if (next) {
if (next->fFlags & CHAIN_BUFFER_ON_STACK)
next->_Destroy();
else
delete next;
}
}

View File

@ -0,0 +1,247 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <boot/net/Ethernet.h>
#include <stdio.h>
#include <boot/net/ChainBuffer.h>
//#define TRACE_ETHERNET
#ifdef TRACE_ETHERNET
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
// #pragma mark - EthernetInterface
// constructor
EthernetInterface::EthernetInterface()
: fIPAddress(INADDR_ANY)
{
}
// destructor
EthernetInterface::~EthernetInterface()
{
}
// IPAddress
ip_addr_t
EthernetInterface::IPAddress() const
{
return fIPAddress;
}
// SetIPAddress
void
EthernetInterface::SetIPAddress(ip_addr_t ipAddress)
{
fIPAddress = ipAddress;
}
// #pragma mark - EthernetSubService
// constructor
EthernetSubService::EthernetSubService(const char *serviceName)
: NetService(serviceName)
{
}
// destructor
EthernetSubService::~EthernetSubService()
{
}
// #pragma mark - EthernetService
// constructor
EthernetService::EthernetService()
: NetService(kEthernetServiceName),
fInterface(NULL),
fSendBuffer(NULL),
fReceiveBuffer(NULL)
{
}
// destructor
EthernetService::~EthernetService()
{
if (fSendBuffer)
fInterface->FreeSendReceiveBuffer(fSendBuffer);
delete fInterface;
}
// Init
status_t
EthernetService::Init(EthernetInterface *interface)
{
if (!interface)
return B_BAD_VALUE;
fInterface = interface;
fSendBuffer = fInterface->AllocateSendReceiveBuffer(
2 * SEND_BUFFER_SIZE);
if (!fSendBuffer)
return B_NO_MEMORY;
fReceiveBuffer = (uint8*)fSendBuffer + SEND_BUFFER_SIZE;
return B_OK;
}
// MACAddress
mac_addr_t
EthernetService::MACAddress() const
{
return fInterface->MACAddress();
}
// IPAddress
ip_addr_t
EthernetService::IPAddress() const
{
return fInterface->IPAddress();
}
// SetIPAddress
void
EthernetService::SetIPAddress(ip_addr_t ipAddress)
{
fInterface->SetIPAddress(ipAddress);
}
// Send
status_t
EthernetService::Send(const mac_addr_t &destination, uint16 protocol,
ChainBuffer *buffer)
{
TRACE(("EthernetService::Send(to: %012llx, proto: 0x%hx, %lu bytes)\n",
destination.ToUInt64(), protocol, (buffer ? buffer->TotalSize() : 0)));
if (!fInterface || !fSendBuffer)
return B_NO_INIT;
// sending has time, but we need to handle incoming packets as soon as
// possible
ProcessIncomingPackets();
if (!buffer)
return B_BAD_VALUE;
// data too long?
size_t dataSize = buffer->TotalSize();
if (dataSize > ETHER_MAX_TRANSFER_UNIT)
return B_BAD_VALUE;
// prepend ethernet header
ether_header header;
ChainBuffer headerBuffer(&header, sizeof(header), buffer);
header.source = fInterface->MACAddress();
header.destination = destination;
header.type = htons(protocol);
// flatten
size_t totalSize = headerBuffer.TotalSize();
headerBuffer.Flatten(fSendBuffer);
// pad data, if necessary
if (dataSize < ETHER_MIN_TRANSFER_UNIT) {
size_t paddingSize = ETHER_MIN_TRANSFER_UNIT - dataSize;
memset((uint8*)fSendBuffer + totalSize, 0, paddingSize);
totalSize += paddingSize;
}
// send
ssize_t bytesSent = fInterface->Send(fSendBuffer, totalSize);
if (bytesSent < 0)
return bytesSent;
if (bytesSent != (ssize_t)totalSize)
return B_ERROR;
return B_OK;
}
// ProcessIncomingPackets
void
EthernetService::ProcessIncomingPackets()
{
if (!fInterface || !fReceiveBuffer)
return;
for (;;) {
// read from the interface
ssize_t bytesReceived = fInterface->Receive(fReceiveBuffer,
RECEIVE_BUFFER_SIZE);
if (bytesReceived < 0)
return;
// basic sanity checks (packet too small/too big)
if (bytesReceived
< (ssize_t)sizeof(ether_header) + ETHER_MIN_TRANSFER_UNIT
|| bytesReceived
> (ssize_t)sizeof(ether_header) + ETHER_MAX_TRANSFER_UNIT) {
continue;
}
// is the packet intended for us?
ether_header *header = (ether_header*)fReceiveBuffer;
if (header->destination != kBroadcastMACAddress
&& header->destination != fInterface->MACAddress()) {
continue;
}
TRACE(("EthernetService::ProcessIncomingPackets(): received ethernet "
"frame: to: %012llx, proto: 0x%hx, %ld bytes\n",
header->destination.ToUInt64(), ntohs(header->type),
bytesReceived - (ssize_t)sizeof(ether_header)));
// find a service handling this kind of packet
int serviceCount = fServices.Count();
for (int i = 0; i < serviceCount; i++) {
EthernetSubService *service = fServices.ElementAt(i);
if (service->EthernetProtocol() == ntohs(header->type)) {
service->HandleEthernetPacket(this, header->destination,
(uint8*)fReceiveBuffer + sizeof(ether_header),
bytesReceived - sizeof(ether_header));
break;
}
}
}
}
// RegisterEthernetSubService
bool
EthernetService::RegisterEthernetSubService(EthernetSubService *service)
{
return (service && fServices.Add(service) == B_OK);
}
// UnregisterEthernetSubService
bool
EthernetService::UnregisterEthernetSubService(EthernetSubService *service)
{
return (service && fServices.Remove(service) >= 0);
}
// CountSubNetServices
int
EthernetService::CountSubNetServices() const
{
return fServices.Count();
}
// SubNetServiceAt
NetService *
EthernetService::SubNetServiceAt(int index) const
{
return fServices.ElementAt(index);
}

View File

@ -0,0 +1,266 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <boot/net/IP.h>
#include <stdio.h>
#include <boot/net/ARP.h>
#include <boot/net/ChainBuffer.h>
//#define TRACE_IP
#ifdef TRACE_IP
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
// #pragma mark - IPSubService
// constructor
IPSubService::IPSubService(const char *serviceName)
: NetService(serviceName)
{
}
// destructor
IPSubService::~IPSubService()
{
}
// #pragma mark - IPService
// constructor
IPService::IPService(EthernetService *ethernet, ARPService *arpService)
: EthernetSubService(kIPServiceName),
fEthernet(ethernet),
fARPService(arpService)
{
}
// destructor
IPService::~IPService()
{
if (fEthernet)
fEthernet->UnregisterEthernetSubService(this);
}
// Init
status_t
IPService::Init()
{
if (!fEthernet)
return B_BAD_VALUE;
if (!fEthernet->RegisterEthernetSubService(this))
return B_NO_MEMORY;
return B_OK;
}
// IPAddress
ip_addr_t
IPService::IPAddress() const
{
return (fEthernet ? fEthernet->IPAddress() : INADDR_ANY);
}
// EthernetProtocol
uint16
IPService::EthernetProtocol() const
{
return ETHERTYPE_IP;
}
// HandleEthernetPacket
void
IPService::HandleEthernetPacket(EthernetService *ethernet,
const mac_addr_t &targetAddress, const void *data, size_t size)
{
TRACE(("IPService::HandleEthernetPacket(): %lu - %lu bytes\n", size,
sizeof(ip_header)));
if (!data || size < sizeof(ip_header))
return;
// check header
const ip_header *header = (const ip_header*)data;
// header length OK?
int headerLength = header->header_length * 4;
if (headerLength < 20 || headerLength > (int)size
// IP V4?
|| header->version != IP_PROTOCOL_VERSION_4
// length OK?
|| ntohs(header->total_length) > size
// broadcast or our IP?
|| (header->destination != htonl(INADDR_BROADCAST)
&& (fEthernet->IPAddress() == INADDR_ANY
|| header->destination != htonl(fEthernet->IPAddress())))
// checksum OK?
|| _Checksum(*header) != 0) {
return;
}
// find a service handling this kind of packet
int serviceCount = fServices.Count();
for (int i = 0; i < serviceCount; i++) {
IPSubService *service = fServices.ElementAt(i);
if (service->IPProtocol() == header->protocol) {
service->HandleIPPacket(this, ntohl(header->source),
ntohl(header->destination),
(uint8*)data + headerLength, size - headerLength);
break;
}
}
}
// Send
status_t
IPService::Send(ip_addr_t destination, uint8 protocol, ChainBuffer *buffer)
{
TRACE(("IPService::Send(to: %08lx, proto: %lu, %lu bytes)\n", destination,
(uint32)protocol, (buffer ? buffer->TotalSize() : 0)));
if (!buffer)
return B_BAD_VALUE;
if (!fEthernet || !fARPService)
return B_NO_INIT;
// prepare header
ip_header header;
ChainBuffer headerBuffer(&header, sizeof(header), buffer);
header.header_length = 5; // 5 32 bit words, no options
header.version = IP_PROTOCOL_VERSION_4;
header.type_of_service = 0;
header.total_length = htons(headerBuffer.TotalSize());
header.identifier = 0;
header.fragment_offset = IP_DONT_FRAGMENT;
header.time_to_live = IP_DEFAULT_TIME_TO_LIVE;
header.protocol = protocol;
header.checksum = 0;
header.source = htonl(fEthernet->IPAddress());
header.destination = htonl(destination);
// compute check sum
header.checksum = htons(_Checksum(header));
// get target MAC address
mac_addr_t targetMAC;
status_t error = fARPService->GetMACForIP(destination, targetMAC);
if (error != B_OK)
return error;
// send the packet
return fEthernet->Send(targetMAC, ETHERTYPE_IP, &headerBuffer);
}
// ProcessIncomingPackets
void
IPService::ProcessIncomingPackets()
{
if (fEthernet)
fEthernet->ProcessIncomingPackets();
}
// RegisterIPSubService
bool
IPService::RegisterIPSubService(IPSubService *service)
{
return (service && fServices.Add(service) == B_OK);
}
// UnregisterIPSubService
bool
IPService::UnregisterIPSubService(IPSubService *service)
{
return (service && fServices.Remove(service) >= 0);
}
// CountSubNetServices
int
IPService::CountSubNetServices() const
{
return fServices.Count();
}
// SubNetServiceAt
NetService *
IPService::SubNetServiceAt(int index) const
{
return fServices.ElementAt(index);
}
// _Checksum
uint16
IPService::_Checksum(const ip_header &header)
{
ChainBuffer buffer((void*)&header, header.header_length * 4);
return ip_checksum(&buffer);
}
// #pragma mark -
// ip_checksum
uint16
ip_checksum(ChainBuffer *buffer)
{
// ChainBuffer iterator returning a stream of uint16 (big endian).
struct Iterator {
Iterator(ChainBuffer *buffer)
: fBuffer(buffer),
fOffset(-1)
{
_Next();
}
bool HasNext() const
{
return fBuffer;
}
uint16 Next()
{
uint16 byte = _NextByte();
return (byte << 8) | _NextByte();
}
private:
void _Next()
{
while (fBuffer) {
fOffset++;
if (fOffset < (int)fBuffer->Size())
break;
fOffset = -1;
fBuffer = fBuffer->Next();
}
}
uint8 _NextByte()
{
uint8 byte = (fBuffer ? ((uint8*)fBuffer->Data())[fOffset] : 0);
_Next();
return byte;
}
ChainBuffer *fBuffer;
int fOffset;
};
Iterator it(buffer);
uint32 checksum = 0;
while (it.HasNext()) {
checksum += it.Next();
while (checksum >> 16)
checksum = (checksum & 0xffff) + (checksum >> 16);
}
return ~checksum;
}

View File

@ -0,0 +1,18 @@
SubDir HAIKU_TOP src system boot loader net ;
UsePrivateHeaders kernel ;
SubDirC++Flags -fno-rtti ;
KernelStaticLibrary boot_net.a :
ARP.cpp
ChainBuffer.cpp
Ethernet.cpp
IP.cpp
NetDefs.cpp
NetStack.cpp
RemoteDisk.cpp
UDP.cpp
: -fno-pic
;

View File

@ -0,0 +1,65 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <boot/net/NetDefs.h>
#include <stdio.h>
const mac_addr_t kBroadcastMACAddress(
(uint8[6]){0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
const mac_addr_t kNoMACAddress((uint8[6]){0, 0, 0, 0, 0, 0});
// net service names
const char *const kEthernetServiceName = "ethernet";
const char *const kARPServiceName = "arp";
const char *const kIPServiceName = "ip";
const char *const kUDPServiceName = "udp";
// constructor
NetService::NetService(const char *name)
: fName(name)
{
}
// destructor
NetService::~NetService()
{
}
// NetServiceName
const char *
NetService::NetServiceName()
{
return fName;
}
// CountSubNetServices
int
NetService::CountSubNetServices() const
{
return 0;
}
// SubNetServiceAt
NetService *
NetService::SubNetServiceAt(int index) const
{
return NULL;
}
// FindSubNetService
NetService *
NetService::FindSubNetService(const char *name) const
{
int count = CountSubNetServices();
for (int i = 0; i < count; i++) {
NetService *service = SubNetServiceAt(i);
if (strcmp(service->NetServiceName(), name) == 0)
return service;
}
return NULL;
}

View File

@ -0,0 +1,139 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <boot/net/NetStack.h>
#include <new>
#include <stdio.h>
#include <boot/net/ARP.h>
#include <boot/net/Ethernet.h>
#include <boot/net/IP.h>
#include <boot/net/UDP.h>
// sNetStack
NetStack *NetStack::sNetStack = NULL;
// constructor
NetStack::NetStack()
: fEthernetInterface(NULL),
fEthernetService(NULL),
fARPService(NULL),
fIPService(NULL),
fUDPService(NULL)
{
}
// destructor
NetStack::~NetStack()
{
delete fUDPService;
delete fIPService;
delete fARPService;
delete fEthernetService;
delete fEthernetInterface;
}
// Init
status_t
NetStack::Init()
{
// create services
// ethernet service
fEthernetService = new(nothrow) EthernetService;
if (!fEthernetService)
return B_NO_MEMORY;
// ARP service
fARPService = new(nothrow) ARPService(fEthernetService);
if (!fARPService)
return B_NO_MEMORY;
status_t error = fARPService->Init();
if (error != B_OK)
return error;
// IP service
fIPService = new(nothrow) IPService(fEthernetService, fARPService);
if (!fIPService)
return B_NO_MEMORY;
error = fIPService->Init();
if (error != B_OK)
return error;
// UDP service
fUDPService = new(nothrow) UDPService(fIPService);
if (!fUDPService)
return B_NO_MEMORY;
error = fUDPService->Init();
if (error != B_OK)
return error;
return B_OK;
}
// CreateDefault
status_t
NetStack::CreateDefault()
{
if (sNetStack)
return B_OK;
NetStack *netStack = new(nothrow) NetStack;
if (!netStack)
return B_NO_MEMORY;
status_t error = netStack->Init();
if (error != B_OK)
delete netStack;
sNetStack = netStack;
return B_OK;
}
// Default
NetStack *
NetStack::Default()
{
return sNetStack;
}
// AddEthernetInterface
status_t
NetStack::AddEthernetInterface(EthernetInterface *interface)
{
if (!interface)
return B_BAD_VALUE;
// we support only one network interface at the moment
if (fEthernetInterface)
return B_BAD_VALUE;
if (!fEthernetService)
return B_NO_INIT;
status_t error = fEthernetService->Init(interface);
if (error != B_OK)
return error;
fEthernetInterface = interface;
return B_OK;
}
// #pragma mark -
status_t
net_stack_init()
{
status_t error = NetStack::CreateDefault();
if (error != B_OK)
return error;
return platform_net_stack_init();
}

View File

@ -0,0 +1,299 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <boot/net/RemoteDisk.h>
#include <new>
#include <endian.h>
#include <stdio.h>
#include <OS.h>
#include <SupportDefs.h>
#include <boot/net/UDP.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN
static inline
uint64_t swap_uint64(uint64_t data)
{
return ((data & 0xff) << 56)
| ((data & 0xff00) << 40)
| ((data & 0xff0000) << 24)
| ((data & 0xff000000) << 8)
| ((data >> 8) & 0xff000000)
| ((data >> 24) & 0xff0000)
| ((data >> 40) & 0xff00)
| ((data >> 56) & 0xff);
}
#define host_to_net64(data) swap_uint64(data)
#define net_to_host64(data) swap_uint64(data)
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
#define host_to_net64(data) (data)
#define net_to_host64(data) (data)
#endif
#undef htonll
#undef ntohll
#define htonll(data) host_to_net64(data)
#define ntohll(data) net_to_host64(data)
// constructor
RemoteDisk::RemoteDisk()
: fServerAddress(INADDR_ANY),
fServerPort(0),
fImageSize(0),
fRequestID(0),
fSocket(NULL),
fPacket(NULL)
{
}
// destructor
RemoteDisk::~RemoteDisk()
{
delete fSocket;
delete fPacket;
}
// Init
status_t
RemoteDisk::Init(ip_addr_t serverAddress, uint16 serverPort, off_t imageSize)
{
fServerAddress = serverAddress;
fServerPort = serverPort;
fImageSize = imageSize;
// create and bind socket
fSocket = new(nothrow) UDPSocket;
if (!fSocket)
return B_NO_MEMORY;
status_t error = fSocket->Bind(INADDR_ANY, 6666);
if (error != B_OK)
return error;
return B_OK;
}
// ReadAt
ssize_t
RemoteDisk::ReadAt(void */*cookie*/, off_t pos, void *_buffer,
size_t bufferSize)
{
if (!fSocket)
return B_NO_INIT;
uint8 *buffer = (uint8*)_buffer;
if (!buffer || pos < 0)
return B_BAD_VALUE;
if (bufferSize == 0)
return 0;
// Check whether the current packet already contains the beginning of the
// data to read.
ssize_t bytesRead = _ReadFromPacket(pos, buffer, bufferSize);
// If there still remains something to be read, we need to get it from the
// server.
status_t error = B_OK;
while (bufferSize > 0) {
// prepare request
remote_disk_header request;
request.offset = htonll(pos);
uint32 toRead = min_c(bufferSize, REMOTE_DISK_BLOCK_SIZE);
request.size = htons(toRead);
request.command = REMOTE_DISK_READ_REQUEST;
// send request
UDPPacket *packet;
error = _SendRequest(&request, sizeof(request), REMOTE_DISK_READ_REPLY,
&packet);
if (error != B_OK)
break;
// check for errors
int16 packetSize = ntohs(((remote_disk_header*)packet->Data())->size);
if (packetSize < 0) {
if (packetSize == REMOTE_DISK_IO_ERROR)
error = B_IO_ERROR;
else if (packetSize == REMOTE_DISK_BAD_REQUEST)
error = B_BAD_VALUE;
break;
}
// make the reply packet the current packet
delete fPacket;
fPacket = packet;
// read from the packet
size_t packetBytesRead = _ReadFromPacket(pos, buffer, bufferSize);
if (packetBytesRead == 0)
break;
bytesRead += packetBytesRead;
}
// only return an error, when we were not able to read anything at all
return (bytesRead == 0 ? error : bytesRead);
}
// WriteAt
ssize_t
RemoteDisk::WriteAt(void */*cookie*/, off_t pos, const void *buffer,
size_t bufferSize)
{
// Not needed in the boot loader.
return B_PERMISSION_DENIED;
}
// GetName
status_t
RemoteDisk::GetName(char *nameBuffer, size_t bufferSize) const
{
if (!nameBuffer)
return B_BAD_VALUE;
snprintf(nameBuffer, bufferSize, "RemoteDisk:%ld.%ld.%ld.%ld:%hd",
(fServerAddress >> 24) & 0xff, (fServerAddress >> 16) & 0xff,
(fServerAddress >> 8) & 0xff, fServerAddress & 0xff, fServerPort);
return B_OK;
}
// Size
off_t
RemoteDisk::Size() const
{
return fImageSize;
}
// FindAnyRemoteDisk
RemoteDisk *
RemoteDisk::FindAnyRemoteDisk()
{
// create a socket and bind it
UDPSocket socket;
status_t error = socket.Bind(INADDR_ANY, 6665);
if (error != B_OK) {
printf("RemoteDisk::GetAnyRemoteDisk(): Failed to bind socket.\n");
return NULL;
}
// prepare request
remote_disk_header request;
request.command = REMOTE_DISK_HELLO_REQUEST;
// send request
UDPPacket *packet;
error = _SendRequest(&socket, INADDR_BROADCAST, REMOTE_DISK_SERVER_PORT,
&request, sizeof(request), REMOTE_DISK_HELLO_REPLY, &packet);
if (error != B_OK) {
printf("RemoteDisk::GetAnyRemoteDisk(): Got no server reply.\n");
return NULL;
}
remote_disk_header *reply = (remote_disk_header*)packet->Data();
// create a RemoteDisk object
RemoteDisk *remoteDisk = new(nothrow) RemoteDisk;
if (remoteDisk) {
error = remoteDisk->Init(packet->SourceAddress(), ntohs(reply->port),
ntohll(reply->offset));
if (error != B_OK) {
delete remoteDisk;
remoteDisk = NULL;
}
}
delete packet;
return remoteDisk;
}
// _ReadFromPacket
ssize_t
RemoteDisk::_ReadFromPacket(off_t &pos, uint8 *&buffer, size_t &bufferSize)
{
if (!fPacket)
return 0;
remote_disk_header *header = (remote_disk_header*)fPacket->Data();
uint64 packetOffset = ntohll(header->offset);
uint32 packetSize = ntohs(header->size);
if (packetOffset > (uint64)pos || packetOffset + packetSize <= (uint64)pos)
return 0;
// we do indeed have some bytes already
size_t toCopy = size_t(packetOffset + packetSize - (uint64)pos);
if (toCopy > bufferSize)
toCopy = bufferSize;
memcpy(buffer, header->data + (pos - packetOffset), toCopy);
pos += toCopy;
buffer += toCopy;
bufferSize -= toCopy;
return toCopy;
}
// _SendRequest
status_t
RemoteDisk::_SendRequest(UDPSocket *socket, ip_addr_t serverAddress,
uint16 serverPort, remote_disk_header *request, size_t size,
uint8 expectedReply, UDPPacket **_packet)
{
request->port = htons(socket->Port());
// try sending the request 3 times at most
for (int i = 0; i < 3; i++) {
// send request
status_t error = socket->Send(serverAddress, serverPort, request, size);
if (error != B_OK)
return error;
// receive reply
bigtime_t timeout = system_time() + 10000LL;
do {
UDPPacket *packet;
error = socket->Receive(&packet, timeout - system_time());
if (error == B_OK) {
// got something; check, if it is looks good
if (packet->DataSize() >= sizeof(remote_disk_header)) {
remote_disk_header *reply
= (remote_disk_header*)packet->Data();
if (reply->request_id == request->request_id
&& reply->command == expectedReply) {
*_packet = packet;
return B_OK;
}
}
// reply not OK
delete packet;
} else if (error != B_TIMED_OUT && error != B_WOULD_BLOCK)
return error;
} while (timeout > system_time());
}
// no reply
return B_ERROR;
}
// _SendRequest
status_t
RemoteDisk::_SendRequest(remote_disk_header *request, size_t size,
uint8 expectedReply, UDPPacket **packet)
{
request->request_id = fRequestID++;
return _SendRequest(fSocket, fServerAddress, fServerPort, request, size,
expectedReply, packet);
}

View File

@ -0,0 +1,437 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <boot/net/UDP.h>
#include <stdio.h>
#include <OS.h>
#include <boot/net/ChainBuffer.h>
#include <boot/net/NetStack.h>
//#define TRACE_UDP
#ifdef TRACE_UDP
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
// #pragma mark - UDPPacket
// constructor
UDPPacket::UDPPacket()
: fNext(NULL),
fData(NULL),
fSize(0)
{
}
// destructor
UDPPacket::~UDPPacket()
{
free(fData);
}
// SetTo
status_t
UDPPacket::SetTo(const void *data, size_t size, ip_addr_t sourceAddress,
uint16 sourcePort, ip_addr_t destinationAddress, uint16 destinationPort)
{
if (!data)
return B_BAD_VALUE;
// clone the data
fData = malloc(size);
if (!fData)
return B_NO_MEMORY;
memcpy(fData, data, size);
fSize = size;
fSourceAddress = sourceAddress;
fDestinationAddress = destinationAddress;
fSourcePort = sourcePort;
fDestinationPort = destinationPort;
return B_OK;
}
// Next
UDPPacket *
UDPPacket::Next() const
{
return fNext;
}
// SetNext
void
UDPPacket::SetNext(UDPPacket *next)
{
fNext = next;
}
// Data
const void *
UDPPacket::Data() const
{
return fData;
}
// DataSize
size_t
UDPPacket::DataSize() const
{
return fSize;
}
// SourceAddress
ip_addr_t
UDPPacket::SourceAddress() const
{
return fSourceAddress;
}
// SourcePort
uint16
UDPPacket::SourcePort() const
{
return fSourcePort;
}
// DestinationAddress
ip_addr_t
UDPPacket::DestinationAddress() const
{
return fDestinationAddress;
}
// DestinationPort
uint16
UDPPacket::DestinationPort() const
{
return fDestinationPort;
}
// #pragma mark - UDPSocket
// constructor
UDPSocket::UDPSocket()
: fUDPService(NetStack::Default()->GetUDPService()),
fFirstPacket(NULL),
fLastPacket(NULL),
fAddress(INADDR_ANY),
fPort(0)
{
}
// destructor
UDPSocket::~UDPSocket()
{
if (fPort != 0 && fUDPService)
fUDPService->UnbindSocket(this);
}
// Bind
status_t
UDPSocket::Bind(ip_addr_t address, uint16 port)
{
if (!fUDPService) {
printf("UDPSocket::Bind(): no UDP service\n");
return B_NO_INIT;
}
if (address == INADDR_BROADCAST || port == 0) {
printf("UDPSocket::Bind(): broadcast IP or port 0\n");
return B_BAD_VALUE;
}
if (fPort != 0) {
printf("UDPSocket::Bind(): already bound\n");
return EALREADY; // correct code?
}
status_t error = fUDPService->BindSocket(this, address, port);
if (error != B_OK) {
printf("UDPSocket::Bind(): service BindSocket() failed\n");
return error;
}
fAddress = address;
fPort = port;
return B_OK;
}
// Send
status_t
UDPSocket::Send(ip_addr_t destinationAddress, uint16 destinationPort,
ChainBuffer *buffer)
{
if (!fUDPService)
return B_NO_INIT;
return fUDPService->Send(fPort, destinationAddress, destinationPort,
buffer);
}
// Send
status_t
UDPSocket::Send(ip_addr_t destinationAddress, uint16 destinationPort,
const void *data, size_t size)
{
if (!data)
return B_BAD_VALUE;
ChainBuffer buffer((void*)data, size);
return Send(destinationAddress, destinationPort, &buffer);
}
// Receive
status_t
UDPSocket::Receive(UDPPacket **_packet, bigtime_t timeout)
{
if (!fUDPService)
return B_NO_INIT;
if (!_packet)
return B_BAD_VALUE;
bigtime_t startTime = system_time();
for (;;) {
fUDPService->ProcessIncomingPackets();
if ((*_packet = PopPacket()))
return B_OK;
if (system_time() - startTime > timeout)
return (timeout == 0 ? B_WOULD_BLOCK : B_TIMED_OUT);
}
}
// PushPacket
void
UDPSocket::PushPacket(UDPPacket *packet)
{
if (fLastPacket)
fLastPacket->SetNext(packet);
else
fFirstPacket = packet;
fLastPacket = packet;
packet->SetNext(NULL);
}
// PopPacket
UDPPacket *
UDPSocket::PopPacket()
{
if (!fFirstPacket)
return NULL;
UDPPacket *packet = fFirstPacket;
fFirstPacket = packet->Next();
if (!fFirstPacket)
fLastPacket = NULL;
packet->SetNext(NULL);
return packet;
}
// #pragma mark - UDPService
// constructor
UDPService::UDPService(IPService *ipService)
: IPSubService(kUDPServiceName),
fIPService(ipService)
{
}
// destructor
UDPService::~UDPService()
{
if (fIPService)
fIPService->UnregisterIPSubService(this);
}
// Init
status_t
UDPService::Init()
{
if (!fIPService)
return B_BAD_VALUE;
if (!fIPService->RegisterIPSubService(this))
return B_NO_MEMORY;
return B_OK;
}
// IPProtocol
uint8
UDPService::IPProtocol() const
{
return IPPROTO_UDP;
}
// HandleIPPacket
void
UDPService::HandleIPPacket(IPService *ipService, ip_addr_t sourceIP,
ip_addr_t destinationIP, const void *data, size_t size)
{
TRACE(("UDPService::HandleIPPacket(): source: %08lx, destination: %08lx, "
"%lu - %lu bytes\n", sourceIP, destinationIP, size,
sizeof(udp_header)));
if (!data || size < sizeof(udp_header))
return;
// check the header
const udp_header *header = (const udp_header*)data;
uint16 length = ntohs(header->length);
if (length < sizeof(udp_header) || length > size
|| (header->checksum != 0 // 0 => checksum disabled
&& _ChecksumData(data, length, sourceIP, destinationIP) != 0)) {
TRACE(("UDPService::HandleIPPacket(): dropping packet -- invalid size "
"or checksum\n"));
return;
}
// find the target socket
UDPSocket *socket = _FindSocket(destinationIP, header->destination);
if (!socket)
return;
// create a UDPPacket and queue it in the socket
UDPPacket *packet = new(nothrow) UDPPacket;
if (!packet)
return;
status_t error = packet->SetTo((uint8*)data + sizeof(udp_header),
length - sizeof(udp_header), sourceIP, header->source, destinationIP,
header->destination);
if (error == B_OK)
socket->PushPacket(packet);
else
delete packet;
}
// Send
status_t
UDPService::Send(uint16 sourcePort, ip_addr_t destinationAddress,
uint16 destinationPort, ChainBuffer *buffer)
{
TRACE(("UDPService::Send(source port: %hu, to: %08lx:%hu, %lu bytes)\n",
sourcePort, destinationAddress, destinationPort,
(buffer ? buffer->TotalSize() : 0)));
if (!fIPService)
return B_NO_INIT;
if (!buffer)
return B_BAD_VALUE;
// prepend the UDP header
udp_header header;
ChainBuffer headerBuffer(&header, sizeof(header), buffer);
header.source = htons(sourcePort);
header.destination = htons(destinationPort);
header.length = htons(headerBuffer.TotalSize());
// compute the checksum
header.checksum = 0;
header.checksum = htons(_ChecksumBuffer(&headerBuffer,
fIPService->IPAddress(), destinationAddress,
headerBuffer.TotalSize()));
// 0 means checksum disabled; 0xffff is equivalent in this case
if (header.checksum == 0)
header.checksum = 0xffff;
return fIPService->Send(destinationAddress, IPPROTO_UDP, &headerBuffer);
}
// ProcessIncomingPackets
void
UDPService::ProcessIncomingPackets()
{
if (fIPService)
fIPService->ProcessIncomingPackets();
}
// BindSocket
status_t
UDPService::BindSocket(UDPSocket *socket, ip_addr_t address, uint16 port)
{
if (!socket)
return B_BAD_VALUE;
if (_FindSocket(address, port)) {
printf("UDPService::BindSocket(): address in use\n");
return EADDRINUSE;
}
return fSockets.Add(socket);
}
// UnbindSocket
void
UDPService::UnbindSocket(UDPSocket *socket)
{
fSockets.Remove(socket);
}
// _ChecksumBuffer
uint16
UDPService::_ChecksumBuffer(ChainBuffer *buffer, ip_addr_t source,
ip_addr_t destination, uint16 length)
{
// The checksum is calculated over a pseudo-header plus the UDP packet.
// So we temporarily prepend the pseudo-header.
struct pseudo_header {
ip_addr_t source;
ip_addr_t destination;
uint8 pad;
uint8 protocol;
uint16 length;
} __attribute__ ((__packed__));
pseudo_header header = {
htonl(source),
htonl(destination),
0,
IPPROTO_UDP,
htons(length)
};
ChainBuffer headerBuffer(&header, sizeof(header), buffer);
uint16 checksum = ip_checksum(&headerBuffer);
headerBuffer.DetachNext();
return checksum;
}
// _ChecksumData
uint16
UDPService::_ChecksumData(const void *data, uint16 length, ip_addr_t source,
ip_addr_t destination)
{
ChainBuffer buffer((void*)data, length);
return _ChecksumBuffer(&buffer, source, destination, length);
}
// _FindSocket
UDPSocket *
UDPService::_FindSocket(ip_addr_t address, uint16 port)
{
int count = fSockets.Count();
for (int i = 0; i < count; i++) {
UDPSocket *socket = fSockets.ElementAt(i);
if ((address == INADDR_ANY || socket->Address() == INADDR_ANY
|| socket->Address() == address)
&& port == socket->Port()) {
return socket;
}
}
return NULL;
}

View File

@ -10,8 +10,10 @@ KernelMergeObject boot_platform_openfirmware.o :
heap.cpp
menu.cpp
mmu.cpp
network.cpp
openfirmware.c
start.c
support.cpp
video.cpp
# generic

View File

@ -12,6 +12,8 @@
#include <boot/vfs.h>
#include <boot/stdio.h>
#include <boot/stage2.h>
#include <boot/net/NetStack.h>
#include <boot/net/RemoteDisk.h>
#include <util/kernel_cpp.h>
#include <string.h>
@ -97,7 +99,24 @@ platform_get_boot_device(struct stage2_args *args, Node **_device)
char type[16];
of_getprop(node, "device_type", type, sizeof(type));
printf("boot type = %s\n", type);
if (strcmp("block", type)) {
// If the boot device is a network device, we try to find a
// "remote disk" at this point.
if (strcmp(type, "network") == 0) {
// init the net stack
status_t error = net_stack_init();
if (error != B_OK)
return error;
// init a remote disk, if possible
RemoteDisk *remoteDisk = RemoteDisk::FindAnyRemoteDisk();
if (!remoteDisk)
return B_ENTRY_NOT_FOUND;
*_device = remoteDisk;
return B_OK;
}
if (strcmp("block", type) != 0) {
printf("boot device is not a block device!\n");
return B_ENTRY_NOT_FOUND;
}

View File

@ -0,0 +1,254 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <OS.h>
#include <boot/net/Ethernet.h>
#include <boot/net/NetStack.h>
#include "openfirmware.h"
//#define TRACE_NETWORK
#ifdef TRACE_NETWORK
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#ifdef TRACE_NETWORK
static void
hex_dump(const void *_data, int length)
{
uint8 *data = (uint8*)_data;
for (int i = 0; i < length; i++) {
if (i % 4 == 0) {
if (i % 32 == 0) {
if (i != 0)
printf("\n");
printf("%03x: ", i);
} else
printf(" ");
}
printf("%02x", data[i]);
}
printf("\n");
}
#else // !TRACE_NETWORK
#define hex_dump(data, length)
#endif // !TRACE_NETWORK
class OFEthernetInterface : public EthernetInterface {
public:
OFEthernetInterface();
virtual ~OFEthernetInterface();
status_t Init(const char *device);
virtual mac_addr_t MACAddress() const;
virtual void *AllocateSendReceiveBuffer(size_t size);
virtual void FreeSendReceiveBuffer(void *buffer);
virtual ssize_t Send(const void *buffer, size_t size);
virtual ssize_t Receive(void *buffer, size_t size);
private:
int fHandle;
mac_addr_t fMACAddress;
};
// constructor
OFEthernetInterface::OFEthernetInterface()
: EthernetInterface(),
fHandle(OF_FAILED),
fMACAddress(kNoMACAddress)
{
}
// destructor
OFEthernetInterface::~OFEthernetInterface()
{
if (fHandle != OF_FAILED)
of_close(fHandle);
}
// Init
status_t
OFEthernetInterface::Init(const char *device)
{
if (!device)
return B_BAD_VALUE;
// open device
fHandle = of_open(device);
if (fHandle == OF_FAILED) {
printf("opening ethernet device failed\n");
return B_ERROR;
}
int package = of_instance_to_package(fHandle);
// get MAC address
int bytesRead = of_getprop(package, "local-mac-address", &fMACAddress,
sizeof(fMACAddress));
if (bytesRead == OF_FAILED || bytesRead < (int)sizeof(fMACAddress)) {
// Failed to get the MAC address of the network device. The system may
// have a global standard MAC address.
bytesRead = of_getprop(gChosen, "mac-address", &fMACAddress,
sizeof(fMACAddress));
if (bytesRead == OF_FAILED || bytesRead < (int)sizeof(fMACAddress)) {
printf("Failed to get MAC address\n");
return B_ERROR;
}
}
// get IP address
// Note: This is a non-standardized way. On my Mac mini the response of the
// DHCP server is stored as property of /chosen. We try to get that it and
// use the IP address we find in there.
struct {
uint8 irrelevant[16];
uint32 ip_address;
// ...
} dhcpResponse;
bytesRead = of_getprop(gChosen, "dhcp-response", &dhcpResponse,
sizeof(dhcpResponse));
if (bytesRead != OF_FAILED && bytesRead == (int)sizeof(dhcpResponse))
SetIPAddress(ntohl(dhcpResponse.ip_address));
return B_OK;
}
// MACAddress
mac_addr_t
OFEthernetInterface::MACAddress() const
{
return fMACAddress;
}
// AllocateSendReceiveBuffer
void *
OFEthernetInterface::AllocateSendReceiveBuffer(size_t size)
{
void *dmaMemory;
if (of_call_method(fHandle, "dma-alloc", 1, 1, size, &dmaMemory)
== OF_FAILED) {
return NULL;
}
return dmaMemory;
}
// FreeSendReceiveBuffer
void
OFEthernetInterface::FreeSendReceiveBuffer(void *buffer)
{
if (buffer)
of_call_method(fHandle, "dma-free", 1, 0, buffer);
}
// Send
ssize_t
OFEthernetInterface::Send(const void *buffer, size_t size)
{
TRACE(("OFEthernetInterface::Send(%p, %lu)\n", buffer, size));
if (!buffer)
return B_BAD_VALUE;
hex_dump(buffer, size);
int result = of_write(fHandle, buffer, size);
return (result == OF_FAILED ? B_ERROR : result);
}
// Receive
ssize_t
OFEthernetInterface::Receive(void *buffer, size_t size)
{
if (!buffer)
return B_BAD_VALUE;
int result = of_read(fHandle, buffer, size);
if (result != OF_FAILED && result >= 0) {
TRACE(("OFEthernetInterface::Receive(%p, %lu): received %d bytes\n",
buffer, size, result));
hex_dump(buffer, result);
}
return (result == OF_FAILED ? B_ERROR : result);
}
// #pragma mark -
// platform_net_stack_init
status_t
platform_net_stack_init()
{
// Note: At the moment we only do networking at all, if the boot device
// is a network device. If it isn't, we simply fail here. For serious
// support we would want to iterate through the device tree and add all
// network devices.
// get boot path
char bootPath[192];
int length = of_getprop(gChosen, "bootpath", bootPath, sizeof(bootPath));
if (length <= 1)
return B_ERROR;
// we chop off parameters; otherwise opening the network device might have
// side effects
char *lastComponent = strrchr(bootPath, '/');
char *parameters = strchr((lastComponent ? lastComponent : bootPath), ':');
if (parameters)
*parameters = '\0';
// get device node
int node = of_finddevice(bootPath);
if (node == OF_FAILED)
return B_ERROR;
// get device type
char type[16];
if (of_getprop(node, "device_type", type, sizeof(type)) == OF_FAILED
|| strcmp("network", type) != 0) {
return B_ERROR;
}
// create an EthernetInterface object for the device
OFEthernetInterface *interface = new(nothrow) OFEthernetInterface;
if (!interface)
return B_NO_MEMORY;
status_t error = interface->Init(bootPath);
if (error != B_OK) {
delete interface;
return error;
}
// add it to the net stack
error = NetStack::Default()->AddEthernetInterface(interface);
if (error != B_OK) {
delete interface;
return error;
}
return B_OK;
}

View File

@ -0,0 +1,15 @@
/*
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <OS.h>
#include "openfirmware.h"
bigtime_t
system_time(void)
{
int result = of_milliseconds();
return (result == OF_FAILED ? 0 : bigtime_t(result) * 1000);
}