⛏️ index : haiku.git

/*
 * 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 <KernelExport.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);
}

// Init
status_t
EthernetService::Init(EthernetInterface *interface)
{
	if (!interface)
		return B_BAD_VALUE;

	fInterface = interface;

	fSendBuffer = fInterface->AllocateSendReceiveBuffer(
		SEND_BUFFER_SIZE + RECEIVE_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: %012" B_PRIx64 ", proto: 0x%hx, %"
		PRIu32 " 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: %012" B_PRIx64 ", proto: 0x%hx, %" B_PRIuSIZE " 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);
}