1
0
mirror of https://github.com/solemnwarning/ipxwrapper synced 2024-12-30 16:45:37 +01:00
ipxwrapper/src/ethernet.c
2017-03-29 22:00:47 +01:00

319 lines
7.9 KiB
C

/* IPXWrapper - Ethernet frame handling
* Copyright (C) 2017 Daniel Collins <solemnwarning@solemnwarning.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* This file implements three types of functions:
*
* XXX_frame_size
*
* Returns the size of a whole frame and IPX packet with the given number of
* bytes of payload.
*
* Returns zero if the payload is too large to fit in a frame of this format.
*
* XXX_frame_pack
*
* Serialises a frame and IPX packet to the given buffer, which must be at
* least as large as the size returned by the corresponding XXX_frame_size()
* function.
*
* XXX_frame_unpack
*
* Deserialises a frame, giving the inner IPX packet and its size. Returns
* true on success, false if the frame was invalid.
*/
#define WINSOCK_API_LINKAGE
#include <winsock2.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "addr.h"
#include "ethernet.h"
#define ETHERTYPE_IPX 0x8137
typedef struct ethernet_header ethernet_header;
struct ethernet_header
{
unsigned char dest_mac[6];
unsigned char src_mac[6];
union {
/* Depends on frame type. */
uint16_t ethertype;
uint16_t length;
};
} __attribute__((__packed__));
#define LLC_SAP_NETWARE 0xE0
typedef struct llc_header llc_header;
struct llc_header
{
uint8_t dsap;
uint8_t ssap;
/* TODO: Support for 16-bit control fields? */
uint8_t control;
} __attribute__((__packed__));
static void _pack_ipx_packet(void *buf,
uint8_t type,
addr32_t src_net, addr48_t src_node, uint16_t src_socket,
addr32_t dst_net, addr48_t dst_node, uint16_t dst_socket,
const void *payload, size_t payload_len)
{
novell_ipx_packet *packet = buf;
packet->checksum = 0xFFFF;
packet->length = htons(sizeof(novell_ipx_packet) + payload_len);
packet->hops = 0;
packet->type = type;
addr32_out(packet->dest_net, dst_net);
addr48_out(packet->dest_node, dst_node);
packet->dest_socket = dst_socket;
addr32_out(packet->src_net, src_net);
addr48_out(packet->src_node, src_node);
packet->src_socket = src_socket;
memcpy(packet->data, payload, payload_len);
}
size_t ethII_frame_size(size_t ipx_payload_len)
{
if(ipx_payload_len > NOVELL_IPX_PACKET_MAX_PAYLOAD)
{
return 0;
}
return sizeof(ethernet_header)
+ sizeof(novell_ipx_packet)
+ ipx_payload_len;
}
void ethII_frame_pack(void *frame_buffer,
uint8_t type,
addr32_t src_net, addr48_t src_node, uint16_t src_socket,
addr32_t dst_net, addr48_t dst_node, uint16_t dst_socket,
const void *payload, size_t payload_len)
{
ethernet_header *eth_h = frame_buffer;
addr48_out(eth_h->dest_mac, dst_node);
addr48_out(eth_h->src_mac, src_node);
eth_h->ethertype = htons(ETHERTYPE_IPX);
_pack_ipx_packet(eth_h + 1,
type,
src_net, src_node, src_socket,
dst_net, dst_node, dst_socket,
payload, payload_len);
}
bool ethII_frame_unpack(const novell_ipx_packet **packet, size_t *packet_len, const void *frame_data, size_t frame_len)
{
if(frame_len < sizeof(ethernet_header) + sizeof(novell_ipx_packet))
{
/* Frame is too small to contain all the necessary headers. */
return false;
}
const ethernet_header *eth_h = frame_data;
if(ntohs(eth_h->ethertype) != ETHERTYPE_IPX)
{
/* The ethertype field isn't IPX. */
return false;
}
*packet = (const novell_ipx_packet*)(eth_h + 1);
*packet_len = frame_len - sizeof(ethernet_header);
return true;
}
size_t novell_frame_size(size_t ipx_payload_len)
{
static const size_t OVERHEAD
= sizeof(ethernet_header)
+ sizeof(novell_ipx_packet);
if(ipx_payload_len > NOVELL_IPX_PACKET_MAX_PAYLOAD
|| ipx_payload_len > (1500 - OVERHEAD))
{
return 0;
}
return OVERHEAD + ipx_payload_len;
}
void novell_frame_pack(void *frame_buffer,
uint8_t type,
addr32_t src_net, addr48_t src_node, uint16_t src_socket,
addr32_t dst_net, addr48_t dst_node, uint16_t dst_socket,
const void *payload, size_t payload_len)
{
ethernet_header *eth_h = frame_buffer;
addr48_out(eth_h->dest_mac, dst_node);
addr48_out(eth_h->src_mac, src_node);
eth_h->length = htons(sizeof(novell_ipx_packet) + payload_len);
_pack_ipx_packet(eth_h + 1,
type,
src_net, src_node, src_socket,
dst_net, dst_node, dst_socket,
payload, payload_len);
}
bool novell_frame_unpack(const novell_ipx_packet **packet, size_t *packet_len, const void *frame_data, size_t frame_len)
{
if(frame_len < sizeof(ethernet_header) + sizeof(novell_ipx_packet))
{
/* Frame is too small to contain all the necessary headers. */
return false;
}
const ethernet_header *eth_h = frame_data;
uint16_t payload_len = ntohs(eth_h->length);
if(payload_len > 1500)
{
/* Payload too big, probably an Ethernet II frame. */
return false;
}
else if(payload_len < sizeof(novell_ipx_packet))
{
/* Too small to hold an IPX header. */
return false;
}
else if(payload_len > frame_len - sizeof(ethernet_header))
{
/* Payload length runs past the end of frame_len, was the frame
* somehow truncated?
*/
return false;
}
*packet = (const novell_ipx_packet*)(eth_h + 1);
*packet_len = payload_len;
return true;
}
size_t llc_frame_size(size_t ipx_payload_len)
{
static const size_t OVERHEAD
= sizeof(ethernet_header)
+ sizeof(llc_header)
+ sizeof(novell_ipx_packet);
if(ipx_payload_len > NOVELL_IPX_PACKET_MAX_PAYLOAD
|| ipx_payload_len > (1500 - OVERHEAD))
{
return 0;
}
return OVERHEAD + ipx_payload_len;
}
void llc_frame_pack(void *frame_buffer,
uint8_t type,
addr32_t src_net, addr48_t src_node, uint16_t src_socket,
addr32_t dst_net, addr48_t dst_node, uint16_t dst_socket,
const void *payload, size_t payload_len)
{
ethernet_header *eth_h = frame_buffer;
addr48_out(eth_h->dest_mac, dst_node);
addr48_out(eth_h->src_mac, src_node);
eth_h->length = htons(sizeof(llc_header) + sizeof(novell_ipx_packet) + payload_len);
llc_header *llc_h = (llc_header*)(eth_h + 1);
llc_h->dsap = LLC_SAP_NETWARE;
llc_h->ssap = LLC_SAP_NETWARE;
llc_h->control = 0x03;
_pack_ipx_packet(llc_h + 1,
type,
src_net, src_node, src_socket,
dst_net, dst_node, dst_socket,
payload, payload_len);
}
bool llc_frame_unpack(const novell_ipx_packet **packet, size_t *packet_len, const void *frame_data, size_t frame_len)
{
if(frame_len < (sizeof(ethernet_header) + sizeof(llc_header) + sizeof(novell_ipx_packet)))
{
/* Frame is too small to contain all the necessary headers. */
return false;
}
const ethernet_header *eth_h = frame_data;
const llc_header *llc_h = (const llc_header*)(eth_h + 1);
uint16_t payload_len = ntohs(eth_h->length);
if(payload_len > 1500)
{
/* Payload length too big, probably an Ethernet II frame. */
return false;
}
else if(payload_len < (sizeof(llc_header) + sizeof(novell_ipx_packet)))
{
/* Payload length too short to hold all the headers required
* for an IPX packet.
*/
return false;
}
else if(payload_len > (frame_len - sizeof(ethernet_header)))
{
/* Payload length runs past the end of frame_len, was the frame
* somehow truncated?
*/
return false;
}
else{
/* Payload length looks good. */
}
if(llc_h->dsap != LLC_SAP_NETWARE)
{
/* Not addressed to the Netware SAP. */
return false;
}
if(llc_h->control != 0x03)
{
/* Some link layer control message. Probably. */
return false;
}
*packet = (const novell_ipx_packet*)(llc_h + 1);
*packet_len = payload_len - sizeof(llc_header);
return true;
}