diff --git a/headers/private/kernel/boot/net/ARP.h b/headers/private/kernel/boot/net/ARP.h new file mode 100644 index 0000000000..518968c23d --- /dev/null +++ b/headers/private/kernel/boot/net/ARP.h @@ -0,0 +1,49 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef _BOOT_ARP_H +#define _BOOT_ARP_H + +#include + +#include + +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 diff --git a/headers/private/kernel/boot/net/ChainBuffer.h b/headers/private/kernel/boot/net/ChainBuffer.h new file mode 100644 index 0000000000..0fcda53968 --- /dev/null +++ b/headers/private/kernel/boot/net/ChainBuffer.h @@ -0,0 +1,49 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef _BOOT_NET_CHAIN_BUFFER_H +#define _BOOT_NET_CHAIN_BUFFER_H + +#include + +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 diff --git a/headers/private/kernel/boot/net/Ethernet.h b/headers/private/kernel/boot/net/Ethernet.h new file mode 100644 index 0000000000..673ddb4c1a --- /dev/null +++ b/headers/private/kernel/boot/net/Ethernet.h @@ -0,0 +1,84 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef _BOOT_ETHERNET_H +#define _BOOT_ETHERNET_H + +#include +#include + +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 fServices; +}; + + +#endif // _BOOT_ETHERNET_H diff --git a/headers/private/kernel/boot/net/IP.h b/headers/private/kernel/boot/net/IP.h new file mode 100644 index 0000000000..cc6a38e91e --- /dev/null +++ b/headers/private/kernel/boot/net/IP.h @@ -0,0 +1,64 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef _BOOT_IP_H +#define _BOOT_IP_H + +#include + +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 fServices; +}; + +uint16 ip_checksum(ChainBuffer *buffer); + + +#endif // _BOOT_IP_H diff --git a/headers/private/kernel/boot/net/NetDefs.h b/headers/private/kernel/boot/net/NetDefs.h new file mode 100644 index 0000000000..44a67a27f4 --- /dev/null +++ b/headers/private/kernel/boot/net/NetDefs.h @@ -0,0 +1,207 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef _BOOT_NET_DEFS_H +#define _BOOT_NET_DEFS_H + +#include + +#include +#include + +#include + +// 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 + 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(service); + return NULL; + } + +private: + const char *fName; +}; + +#endif // _BOOT_NET_DEFS_H diff --git a/headers/private/kernel/boot/net/NetStack.h b/headers/private/kernel/boot/net/NetStack.h new file mode 100644 index 0000000000..e2baeaaa5a --- /dev/null +++ b/headers/private/kernel/boot/net/NetStack.h @@ -0,0 +1,55 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef _BOOT_NET_STACK_H +#define _BOOT_NET_STACK_H + +#include + +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 diff --git a/headers/private/kernel/boot/net/RemoteDisk.h b/headers/private/kernel/boot/net/RemoteDisk.h new file mode 100644 index 0000000000..f862e4e97c --- /dev/null +++ b/headers/private/kernel/boot/net/RemoteDisk.h @@ -0,0 +1,51 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef _BOOT_REMOTE_DISK_H +#define _BOOT_REMOTE_DISK_H + +#include +#include +#include + +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 diff --git a/headers/private/kernel/boot/net/RemoteDiskDefs.h b/headers/private/kernel/boot/net/RemoteDiskDefs.h new file mode 100644 index 0000000000..dfdd36d590 --- /dev/null +++ b/headers/private/kernel/boot/net/RemoteDiskDefs.h @@ -0,0 +1,63 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef _BOOT_REMOTE_DISK_DEFS_H +#define _BOOT_REMOTE_DISK_DEFS_H + +#include + +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 diff --git a/headers/private/kernel/boot/net/UDP.h b/headers/private/kernel/boot/net/UDP.h new file mode 100644 index 0000000000..fd1bdd6495 --- /dev/null +++ b/headers/private/kernel/boot/net/UDP.h @@ -0,0 +1,107 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef _BOOT_UDP_H +#define _BOOT_UDP_H + +#include + +// 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 fSockets; +}; + +#endif // _BOOT_UDP_H diff --git a/src/system/boot/Jamfile b/src/system/boot/Jamfile index 70fae82d07..098507c395 100644 --- a/src/system/boot/Jamfile +++ b/src/system/boot/Jamfile @@ -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 diff --git a/src/system/boot/loader/Jamfile b/src/system/boot/loader/Jamfile index fa27203503..e39df51df4 100644 --- a/src/system/boot/loader/Jamfile +++ b/src/system/boot/loader/Jamfile @@ -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 ; diff --git a/src/system/boot/loader/net/ARP.cpp b/src/system/boot/loader/net/ARP.cpp new file mode 100644 index 0000000000..e766768b70 --- /dev/null +++ b/src/system/boot/loader/net/ARP.cpp @@ -0,0 +1,216 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include + +#include + + +//#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; +} diff --git a/src/system/boot/loader/net/ChainBuffer.cpp b/src/system/boot/loader/net/ChainBuffer.cpp new file mode 100644 index 0000000000..5d2e036f97 --- /dev/null +++ b/src/system/boot/loader/net/ChainBuffer.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include +#include + +#include + +// 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; + } +} diff --git a/src/system/boot/loader/net/Ethernet.cpp b/src/system/boot/loader/net/Ethernet.cpp new file mode 100644 index 0000000000..d576b05470 --- /dev/null +++ b/src/system/boot/loader/net/Ethernet.cpp @@ -0,0 +1,247 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include + +#include + + +//#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); +} diff --git a/src/system/boot/loader/net/IP.cpp b/src/system/boot/loader/net/IP.cpp new file mode 100644 index 0000000000..d204d3efaa --- /dev/null +++ b/src/system/boot/loader/net/IP.cpp @@ -0,0 +1,266 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include + +#include +#include + + +//#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; +} diff --git a/src/system/boot/loader/net/Jamfile b/src/system/boot/loader/net/Jamfile new file mode 100644 index 0000000000..b2f1fdabe0 --- /dev/null +++ b/src/system/boot/loader/net/Jamfile @@ -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 +; diff --git a/src/system/boot/loader/net/NetDefs.cpp b/src/system/boot/loader/net/NetDefs.cpp new file mode 100644 index 0000000000..009e4c01bb --- /dev/null +++ b/src/system/boot/loader/net/NetDefs.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include + +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; +} diff --git a/src/system/boot/loader/net/NetStack.cpp b/src/system/boot/loader/net/NetStack.cpp new file mode 100644 index 0000000000..8f02bd590f --- /dev/null +++ b/src/system/boot/loader/net/NetStack.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include + +#include + +#include +#include +#include +#include + + +// 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(); +} + diff --git a/src/system/boot/loader/net/RemoteDisk.cpp b/src/system/boot/loader/net/RemoteDisk.cpp new file mode 100644 index 0000000000..9137ae2463 --- /dev/null +++ b/src/system/boot/loader/net/RemoteDisk.cpp @@ -0,0 +1,299 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include + +#include +#include + +#include +#include + +#include + + +#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); +} diff --git a/src/system/boot/loader/net/UDP.cpp b/src/system/boot/loader/net/UDP.cpp new file mode 100644 index 0000000000..c224452eda --- /dev/null +++ b/src/system/boot/loader/net/UDP.cpp @@ -0,0 +1,437 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include + +#include + +#include +#include + + +//#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; +} diff --git a/src/system/boot/platform/openfirmware/Jamfile b/src/system/boot/platform/openfirmware/Jamfile index 4cfba1d26e..2abc008add 100644 --- a/src/system/boot/platform/openfirmware/Jamfile +++ b/src/system/boot/platform/openfirmware/Jamfile @@ -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 diff --git a/src/system/boot/platform/openfirmware/devices.cpp b/src/system/boot/platform/openfirmware/devices.cpp index 9640cae2a2..bb458ebde3 100644 --- a/src/system/boot/platform/openfirmware/devices.cpp +++ b/src/system/boot/platform/openfirmware/devices.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -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; } diff --git a/src/system/boot/platform/openfirmware/network.cpp b/src/system/boot/platform/openfirmware/network.cpp new file mode 100644 index 0000000000..46e84e2197 --- /dev/null +++ b/src/system/boot/platform/openfirmware/network.cpp @@ -0,0 +1,254 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include +#include +#include + +#include + +#include +#include + +#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; +} diff --git a/src/system/boot/platform/openfirmware/support.cpp b/src/system/boot/platform/openfirmware/support.cpp new file mode 100644 index 0000000000..278c17a386 --- /dev/null +++ b/src/system/boot/platform/openfirmware/support.cpp @@ -0,0 +1,15 @@ +/* + * Copyright 2005, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include "openfirmware.h" + +bigtime_t +system_time(void) +{ + int result = of_milliseconds(); + return (result == OF_FAILED ? 0 : bigtime_t(result) * 1000); +}