1
0
mirror of https://github.com/solemnwarning/ipxwrapper synced 2024-12-30 16:45:37 +01:00

Refactored Ethernet frame (de)serialisation.

Reworked the (de)serialising of Ethernet frames into seperate functions in
preparation for supporting IEEE 802.2 LLC frames.
This commit is contained in:
Daniel Collins 2017-03-28 23:26:44 +01:00
parent d65fb92480
commit b7b4a953d8
7 changed files with 397 additions and 144 deletions

View File

@ -46,7 +46,7 @@ VERSION := git
IPXWRAPPER_DEPS := src/ipxwrapper.o src/winsock.o src/ipxwrapper_stubs.o src/log.o src/common.o \
src/interface.o src/router.o src/ipxwrapper.def src/addrcache.o src/config.o src/addr.o \
src/firewall.o src/wpcap_stubs.o
src/firewall.o src/wpcap_stubs.o src/ethernet.o
BIN_FILES := $(shell cat manifest.bin.txt)
SRC_FILES := $(shell cat manifest.src.txt)

View File

@ -17,6 +17,8 @@ src/common.c
src/common.h
src/config.c
src/config.h
src/ethernet.c
src/ethernet.h
src/directplay.c
src/dpwsockx.def
src/dpwsockx_stubs.txt

208
src/ethernet.c Normal file
View File

@ -0,0 +1,208 @@
/* 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__));
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);
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);
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;
}

88
src/ethernet.h Normal file
View File

@ -0,0 +1,88 @@
/* 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.
*/
#ifndef IPXWRAPPER_ETHERNET_H
#define IPXWRAPPER_ETHERNET_H
#include <stdbool.h>
#include <stdint.h>
#include "addr.h"
/* An IPX packet in its normal form on the wire */
#define NOVELL_IPX_PACKET_MAX_PAYLOAD ((uint16_t)(0xFFFF) - sizeof(novell_ipx_packet))
typedef struct novell_ipx_packet novell_ipx_packet;
struct novell_ipx_packet {
uint16_t checksum;
uint16_t length;
uint8_t hops;
uint8_t type;
unsigned char dest_net[4];
unsigned char dest_node[6];
uint16_t dest_socket;
unsigned char src_net[4];
unsigned char src_node[6];
uint16_t src_socket;
unsigned char data[0];
} __attribute__((__packed__));
/* This file declares 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.
*/
size_t ethII_frame_size(size_t 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 dest_net, addr48_t dest_node, uint16_t dest_socket,
const void *payload, size_t payload_len);
bool ethII_frame_unpack(const novell_ipx_packet **packet, size_t *packet_len,
const void *frame_data, size_t frame_len);
size_t novell_frame_size(size_t 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 dest_net, addr48_t dest_node, uint16_t dest_socket,
const void *payload, size_t payload_len);
bool novell_frame_unpack(const novell_ipx_packet **packet, size_t *packet_len,
const void *frame_data, size_t frame_len);
#endif /* !IPXWRAPPER_ETHERNET_H */

View File

@ -90,41 +90,6 @@ struct ipx_packet {
char data[1];
} __attribute__((__packed__));
typedef struct real_ipx_packet real_ipx_packet_t;
struct real_ipx_packet {
uint16_t checksum;
uint16_t length;
uint8_t hops;
uint8_t type;
unsigned char dest_net[4];
unsigned char dest_node[6];
uint16_t dest_socket;
unsigned char src_net[4];
unsigned char src_node[6];
uint16_t src_socket;
unsigned char data[0];
} __attribute__((__packed__));
typedef struct ethernet_frame ethernet_frame_t;
struct ethernet_frame
{
unsigned char dest_mac[6];
unsigned char src_mac[6];
union {
/* Depends on frame type. */
uint16_t ethertype;
uint16_t length;
};
real_ipx_packet_t packet;
} __attribute__((__packed__));
#define IPX_MAGIC_SPXLOOKUP 1
typedef struct spxlookup_req spxlookup_req_t;

View File

@ -29,6 +29,7 @@
#include "ipxwrapper.h"
#include "interface.h"
#include "addrcache.h"
#include "ethernet.h"
static bool router_running = false;
static WSAEVENT router_event = WSA_INVALID_EVENT;
@ -497,67 +498,42 @@ static void _handle_pcap_frame(u_char *user, const struct pcap_pkthdr *pkt_heade
{
ipx_interface_t *iface = (ipx_interface_t*)(user);
if(pkt_header->caplen < sizeof(ethernet_frame_t))
const novell_ipx_packet *ipx;
size_t ipx_len;
switch(main_config.frame_type)
{
/* Frame isn't big enough to contain a full IPX header. */
return;
case FRAME_TYPE_ETH_II:
if(!ethII_frame_unpack(&ipx, &ipx_len, pkt_data, pkt_header->caplen))
{
return;
}
break;
case FRAME_TYPE_NOVELL:
if(!novell_frame_unpack(&ipx, &ipx_len, pkt_data, pkt_header->caplen))
{
return;
}
break;
}
ethernet_frame_t *frame = (ethernet_frame_t*)(pkt_data);
uint16_t ipx_packet_len = ntohs(frame->packet.length);
if(main_config.frame_type == FRAME_TYPE_ETH_II)
{
/* Configured for standard Ethernet. */
if(ntohs(frame->ethertype) != 0x8137)
{
/* The ethertype field isn't IPX. */
return;
}
}
else if(main_config.frame_type == FRAME_TYPE_NOVELL)
{
/* Configured for Novell "raw" Ethernet. */
uint16_t eth_payload_len = ntohs(frame->length);
if(eth_payload_len > 1500)
{
/* Too big, must be an Ethernet II frame (or garbage). */
return;
}
else if(eth_payload_len < sizeof(frame->packet))
{
/* Too small to hold an IPX header. */
return;
}
else if(eth_payload_len < ipx_packet_len)
{
/* Too small to hold the IPX payload within. */
return;
}
}
else{
/* Unknown frame type configured. */
abort();
}
if(frame->packet.checksum != 0xFFFF)
if(ipx->checksum != 0xFFFF)
{
/* The "checksum" field doesn't have the magic IPX value. */
return;
}
if(ipx_packet_len > (pkt_header->caplen - (sizeof(*frame) - sizeof(frame->packet))))
if(ntohs(ipx->length) > ipx_len)
{
/* The "length" field in the IPX header is too big. */
return;
}
{
addr48_t dest = addr48_in(frame->packet.dest_node);
addr48_t dest = addr48_in(ipx->dest_node);
addr48_t bcast = addr48_in((unsigned char[]){0xFF,0xFF,0xFF,0xFF,0xFF,0xFF});
if(dest != iface->mac_addr && dest != bcast)
@ -569,17 +545,17 @@ static void _handle_pcap_frame(u_char *user, const struct pcap_pkthdr *pkt_heade
}
}
_deliver_packet(frame->packet.type,
addr32_in(frame->packet.src_net),
addr48_in(frame->packet.src_node),
frame->packet.src_socket,
_deliver_packet(ipx->type,
addr32_in(ipx->src_net),
addr48_in(ipx->src_node),
ipx->src_socket,
addr32_in(frame->packet.dest_net),
addr48_in(frame->packet.dest_node),
frame->packet.dest_socket,
addr32_in(ipx->dest_net),
addr48_in(ipx->dest_node),
ipx->dest_socket,
frame->packet.data,
(ntohs(frame->packet.length) - sizeof(real_ipx_packet_t)));
ipx->data,
(ntohs(ipx->length) - sizeof(novell_ipx_packet)));
}
static DWORD router_main(void *arg)

View File

@ -29,6 +29,7 @@
#include "interface.h"
#include "router.h"
#include "addrcache.h"
#include "ethernet.h"
struct sockaddr_ipx_ext {
short sa_family;
@ -49,9 +50,22 @@ static size_t strsize(void *str, bool unicode)
static int _max_ipx_payload(void)
{
return ipx_use_pcap
? (ETHERNET_MTU - sizeof(real_ipx_packet_t))
: MAX_DATA_SIZE;
if(ipx_use_pcap)
{
/* TODO: Use real interface MTU */
switch(main_config.frame_type)
{
case FRAME_TYPE_ETH_II:
case FRAME_TYPE_NOVELL:
return 1500 - (14 + sizeof(novell_ipx_packet));
}
abort();
}
else{
return MAX_DATA_SIZE;
}
}
#define PUSH_NAME(name) \
@ -1198,64 +1212,64 @@ static DWORD ipx_send_packet(
ipx_interface_t *iface = ipx_interface_by_addr(src_net, src_node);
if(iface)
{
size_t packet_size = sizeof(real_ipx_packet_t) + data_size;
size_t frame_size = sizeof(ethernet_frame_t) + data_size;
ethernet_frame_t *frame = malloc(frame_size);
/* Calculate the frame size and check we can actually
* fit this much data in it.
*/
size_t frame_size;
switch(main_config.frame_type)
{
case FRAME_TYPE_ETH_II:
frame_size = ethII_frame_size(data_size);
break;
case FRAME_TYPE_NOVELL:
frame_size = novell_frame_size(data_size);
break;
}
/* TODO: Check frame_size against interface MTU */
if(frame_size == 0)
{
log_printf(LOG_ERROR,
"Tried sending a %u byte packet, too large for the selected frame type",
(unsigned int)(data_size));
return WSAEMSGSIZE;
}
log_printf(LOG_DEBUG, "...frame size = %u", (unsigned int)(frame_size));
/* Serialise the frame. */
void *frame = malloc(frame_size);
if(!frame)
{
return ERROR_OUTOFMEMORY;
}
log_printf(LOG_DEBUG, "...packet size = %u, frame size = %u",
(unsigned int)(packet_size), (unsigned int)(frame_size));
addr48_out(frame->dest_mac, dest_node);
addr48_out(frame->src_mac, iface->mac_addr);
if(main_config.frame_type == FRAME_TYPE_ETH_II)
switch(main_config.frame_type)
{
/* Configured for standard Ethernet. */
frame->ethertype = htons(0x8137);
}
else if(main_config.frame_type == FRAME_TYPE_NOVELL)
{
/* Configured for Novell "raw" Ethernet. */
if(packet_size > 1500)
{
/* Can't fit a payload this big into a
* Novell Ethernet frame.
*/
case FRAME_TYPE_ETH_II:
ethII_frame_pack(frame,
type,
src_net, src_node, src_socket,
dest_net, dest_node, dest_socket,
data, data_size);
break;
log_printf(LOG_ERROR,
"Tried sending a %u byte packet in a Novell (\"raw\") Ethernet frame",
(unsigned int)(packet_size));
free(frame);
return WSAEMSGSIZE;
}
frame->length = htons(packet_size);
}
else{
/* Unknown frame type configured. */
abort();
case FRAME_TYPE_NOVELL:
novell_frame_pack(frame,
type,
src_net, src_node, src_socket,
dest_net, dest_node, dest_socket,
data, data_size);
break;
}
frame->packet.checksum = 0xFFFF;
frame->packet.length = htons(packet_size);
frame->packet.hops = 0;
frame->packet.type = type;
addr32_out(frame->packet.dest_net, dest_net);
addr48_out(frame->packet.dest_node, dest_node);
frame->packet.dest_socket = dest_socket;
addr32_out(frame->packet.src_net, src_net);
addr48_out(frame->packet.src_node, src_node);
frame->packet.src_socket = src_socket;
memcpy(frame->packet.data, data, data_size);
/* Transmit the frame. */
if(pcap_sendpacket(iface->pcap, (void*)(frame), frame_size) == 0)
{