⛏️ index : haiku.git

/*
 * Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Axel DΓΆrfler, axeld@pinc-software.de
 */


#include <ethernet.h>
#include <net_datalink_protocol.h>
#include <net_device.h>
#include <net_datalink.h>
#include <net_stack.h>
#include <NetBufferUtilities.h>

#include <ByteOrder.h>
#include <KernelExport.h>

#include <net/if.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <new>
#include <string.h>


struct ethernet_frame_protocol : net_datalink_protocol {
};


static const uint8 kBroadcastAddress[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

struct net_buffer_module_info* gBufferModule;


int32
ethernet_deframe(net_device* device, net_buffer* buffer)
{
	//dprintf("asked to deframe buffer for device %s\n", device->name);

	NetBufferHeaderRemover<ether_header> bufferHeader(buffer);
	if (bufferHeader.Status() != B_OK)
		return bufferHeader.Status();

	ether_header& header = bufferHeader.Data();
	uint16 type = B_BENDIAN_TO_HOST_INT16(header.type);

	struct sockaddr_dl& source = *(struct sockaddr_dl*)buffer->source;
	struct sockaddr_dl& destination = *(struct sockaddr_dl*)buffer->destination;

	source.sdl_len = sizeof(sockaddr_dl);
	source.sdl_family = AF_LINK;
	source.sdl_index = device->index;
	source.sdl_type = IFT_ETHER;
	source.sdl_e_type = header.type;
	source.sdl_nlen = source.sdl_slen = 0;
	source.sdl_alen = ETHER_ADDRESS_LENGTH;
	memcpy(source.sdl_data, header.source, ETHER_ADDRESS_LENGTH);

	destination.sdl_len = sizeof(sockaddr_dl);
	destination.sdl_family = AF_LINK;
	destination.sdl_index = device->index;
	destination.sdl_type = IFT_ETHER;
	destination.sdl_e_type = header.type;
	destination.sdl_nlen = destination.sdl_slen = 0;
	destination.sdl_alen = ETHER_ADDRESS_LENGTH;
	memcpy(destination.sdl_data, header.destination, ETHER_ADDRESS_LENGTH);

	// Mark buffer if it was a broadcast/multicast packet
	if (!memcmp(header.destination, kBroadcastAddress, ETHER_ADDRESS_LENGTH))
		buffer->msg_flags |= MSG_BCAST;
	else if ((header.destination[0] & 0x01) != 0)
		buffer->msg_flags |= MSG_MCAST;

	// Translate the ethernet specific type to a generic one if possible
	switch (type) {
		case ETHER_TYPE_IP:
			buffer->type = B_NET_FRAME_TYPE_IPV4;
			break;
		case ETHER_TYPE_IPV6:
			buffer->type = B_NET_FRAME_TYPE_IPV6;
			break;
		case ETHER_TYPE_IPX:
			buffer->type = B_NET_FRAME_TYPE_IPX;
			break;

		default:
			buffer->type = B_NET_FRAME_TYPE(IFT_ETHER, type);
			break;
	}

	return B_OK;
}


//	#pragma mark -


status_t
ethernet_frame_init(struct net_interface* interface, net_domain* domain,
	net_datalink_protocol** _protocol)
{
	net_stack_module_info* stack;
	status_t status = get_module(NET_STACK_MODULE_NAME, (module_info**)&stack);
	if (status < B_OK)
		return status;

	status = stack->register_device_deframer(interface->device,
		&ethernet_deframe);

	put_module(NET_STACK_MODULE_NAME);

	if (status != B_OK)
		return status;

	ethernet_frame_protocol* protocol
		= new(std::nothrow) ethernet_frame_protocol;
	if (protocol == NULL)
		return B_NO_MEMORY;

	*_protocol = protocol;
	return B_OK;
}


status_t
ethernet_frame_uninit(net_datalink_protocol* protocol)
{
	net_stack_module_info* stack;
	if (get_module(NET_STACK_MODULE_NAME, (module_info**)&stack) == B_OK) {
		stack->unregister_device_deframer(protocol->interface->device);
		put_module(NET_STACK_MODULE_NAME);
	}

	delete protocol;
	return B_OK;
}


status_t
ethernet_frame_send_data(net_datalink_protocol* protocol, net_buffer* buffer)
{
	struct sockaddr_dl& source = *(struct sockaddr_dl*)buffer->source;
	struct sockaddr_dl& destination = *(struct sockaddr_dl*)buffer->destination;

	if (source.sdl_family != AF_LINK || source.sdl_type != IFT_ETHER)
		return B_ERROR;

	NetBufferPrepend<ether_header> bufferHeader(buffer);
	if (bufferHeader.Status() != B_OK)
		return bufferHeader.Status();

	ether_header &header = bufferHeader.Data();

	header.type = source.sdl_e_type;
	memcpy(header.source, LLADDR(&source), ETHER_ADDRESS_LENGTH);
	if ((buffer->msg_flags & MSG_BCAST) != 0)
		memcpy(header.destination, kBroadcastAddress, ETHER_ADDRESS_LENGTH);
	else
		memcpy(header.destination, LLADDR(&destination), ETHER_ADDRESS_LENGTH);

	bufferHeader.Sync();
		// make sure the framing is already written to the buffer at this point

	return protocol->next->module->send_data(protocol->next, buffer);
}


status_t
ethernet_frame_up(net_datalink_protocol* protocol)
{
	return protocol->next->module->interface_up(protocol->next);
}


void
ethernet_frame_down(net_datalink_protocol* protocol)
{
	return protocol->next->module->interface_down(protocol->next);
}


status_t
ethernet_frame_change_address(net_datalink_protocol* protocol,
	net_interface_address* address, int32 option,
	const struct sockaddr* oldAddress, const struct sockaddr* newAddress)
{
	return protocol->next->module->change_address(protocol->next, address,
		option, oldAddress, newAddress);
}


status_t
ethernet_frame_control(net_datalink_protocol* protocol, int32 option,
	void* argument, size_t length)
{
	return protocol->next->module->control(protocol->next, option, argument,
		length);
}


static status_t
ethernet_frame_join_multicast(net_datalink_protocol* protocol,
	const sockaddr* address)
{
	return protocol->next->module->join_multicast(protocol->next, address);
}


static status_t
ethernet_frame_leave_multicast(net_datalink_protocol* protocol,
	const sockaddr* address)
{
	return protocol->next->module->leave_multicast(protocol->next, address);
}


static status_t
ethernet_frame_std_ops(int32 op, ...)
{
	switch (op) {
		case B_MODULE_INIT:
			return get_module(NET_BUFFER_MODULE_NAME,
				(module_info**)&gBufferModule);
		case B_MODULE_UNINIT:
			put_module(NET_BUFFER_MODULE_NAME);
			return B_OK;

		default:
			return B_ERROR;
	}
}


static net_datalink_protocol_module_info sEthernetFrameModule = {
	{
		"network/datalink_protocols/ethernet_frame/v1",
		0,
		ethernet_frame_std_ops
	},
	ethernet_frame_init,
	ethernet_frame_uninit,
	ethernet_frame_send_data,
	ethernet_frame_up,
	ethernet_frame_down,
	ethernet_frame_change_address,
	ethernet_frame_control,
	ethernet_frame_join_multicast,
	ethernet_frame_leave_multicast,
};

module_info* modules[] = {
	(module_info*)&sEthernetFrameModule,
	NULL
};