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

Merge remote-tracking branch 'origin/llc-support'

This commit is contained in:
Daniel Collins 2017-09-12 13:45:47 +01:00
commit ea2e61ea32
22 changed files with 1757 additions and 153 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)
@ -77,7 +77,7 @@ dist: all
zip -r ipxwrapper-$(VERSION)-src.zip ipxwrapper-$(VERSION)-src/
rm -r ipxwrapper-$(VERSION)-src/
tools: $(TOOLS) tests/addr.exe ipxwrapper.dll wsock32.dll dpwsockx.dll
tools: $(TOOLS) tests/addr.exe tests/ethernet.exe ipxwrapper.dll wsock32.dll dpwsockx.dll
cp ipxwrapper.dll wsock32.dll dpwsockx.dll tools/
tools/%.exe: tools/%.c tools/tools.h src/addr.o
@ -86,6 +86,9 @@ tools/%.exe: tools/%.c tools/tools.h src/addr.o
tests/addr.exe: tests/addr.o tests/tap/basic.o src/addr.o
$(CC) $(CFLAGS) -I./ -o $@ $^ -lwsock32
tests/ethernet.exe: tests/ethernet.o tests/tap/basic.o src/ethernet.o src/addr.o
$(CC) $(CFLAGS) -I./ -o $@ $^ -lwsock32
tests/%.o: tests/%.c
$(CC) $(CFLAGS) -I./ -c -o $@ $<

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
@ -57,6 +59,7 @@ directplay-win32.reg
directplay-win64.reg
tests/05-addr.t
tests/07-ethernet.t
tests/10-socket.t
tests/15-interfaces.t
tests/20-bind.t
@ -66,9 +69,12 @@ tests/40-ip-spx.t
tests/50-dplay.t
tests/addr.c
tests/config.pm
tests/ethernet.c
tests/ptype.pm
tests/lib/IPXWrapper/Capture/IPX.pm
tests/lib/IPXWrapper/Capture/IPXLLC.pm
tests/lib/IPXWrapper/Capture/IPXNovell.pm
tests/lib/IPXWrapper/Capture/IPXOverUDP.pm
tests/lib/IPXWrapper/SPX.pm
tests/lib/IPXWrapper/Tool/Bind.pm

View File

@ -60,7 +60,8 @@ main_config_t get_main_config(void)
/* Check for valid frame_type */
if( config.frame_type != FRAME_TYPE_ETH_II
&& config.frame_type != FRAME_TYPE_NOVELL)
&& config.frame_type != FRAME_TYPE_NOVELL
&& config.frame_type != FRAME_TYPE_LLC)
{
log_printf(LOG_WARNING, "Ignoring unknown frame_type %u",

View File

@ -29,7 +29,8 @@ extern "C" {
enum main_config_frame_type
{
FRAME_TYPE_ETH_II = 1,
FRAME_TYPE_NOVELL = 2
FRAME_TYPE_NOVELL = 2,
FRAME_TYPE_LLC = 3,
};
typedef struct main_config {

314
src/ethernet.c Normal file
View File

@ -0,0 +1,314 @@
/* 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)
{
if(ipx_payload_len > NOVELL_IPX_PACKET_MAX_PAYLOAD
|| ipx_payload_len > (1500 - sizeof(novell_ipx_packet)))
{
return 0;
}
return sizeof(ethernet_header)
+ sizeof(novell_ipx_packet)
+ 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)
{
if(ipx_payload_len > NOVELL_IPX_PACKET_MAX_PAYLOAD
|| ipx_payload_len > (1500 - (sizeof(llc_header) + sizeof(novell_ipx_packet))))
{
return 0;
}
return sizeof(ethernet_header)
+ sizeof(llc_header)
+ sizeof(novell_ipx_packet)
+ 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;
}

97
src/ethernet.h Normal file
View File

@ -0,0 +1,97 @@
/* 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);
size_t llc_frame_size(size_t 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 dest_net, addr48_t dest_node, uint16_t dest_socket,
const void *payload, size_t payload_len);
bool llc_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

@ -636,6 +636,7 @@ static void main_window_init()
ComboBox_AddString(wh.opt_frame_type, "Ethernet II");
ComboBox_AddString(wh.opt_frame_type, "Novell \"raw\" 802.3");
ComboBox_AddString(wh.opt_frame_type, "IEEE 802.2 (LLC)");
ComboBox_SetCurSel(wh.opt_frame_type, main_config.frame_type - 1);

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,50 @@ 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;
case FRAME_TYPE_LLC:
if(!llc_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 +553,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,25 @@ 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 - sizeof(novell_ipx_packet);
case FRAME_TYPE_LLC:
return 1500 - (3 + sizeof(novell_ipx_packet));
}
abort();
}
else{
return MAX_DATA_SIZE;
}
}
#define PUSH_NAME(name) \
@ -1198,64 +1215,76 @@ 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;
case FRAME_TYPE_LLC:
frame_size = llc_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));
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;
free(frame);
return WSAEMSGSIZE;
}
frame->length = htons(packet_size);
}
else{
/* Unknown frame type configured. */
abort();
case FRAME_TYPE_LLC:
llc_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)
{

29
tests/07-ethernet.t Normal file
View File

@ -0,0 +1,29 @@
# IPXWrapper test suite
# 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.
use strict;
use warnings;
use FindBin;
require "$FindBin::Bin/config.pm";
our $remote_ip_a;
# Unit tests implemented by ethernet.exe, so run it on the test system and pass
# the (TAP) output/exit status to our parent.
system("ssh", $remote_ip_a, "Z:\\tests\\ethernet.exe");
exit($? >> 8);

View File

@ -33,6 +33,7 @@ our ($remote_mac_b, $remote_ip_b);
use constant {
IP_MAX_DATA_SIZE => 8192,
ETHER_MAX_DATA_SIZE => 1470,
LLC_MAX_DATA_SIZE => 1467,
};
my @expected_addrs;
@ -173,6 +174,7 @@ describe "IPXWrapper" => sub
{
reg_delete_key($remote_ip_a, "HKCU\\Software\\IPXWrapper");
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "use_pcap", 1);
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "frame_type", 1);
reg_set_addr( $remote_ip_a, "HKCU\\Software\\IPXWrapper\\$remote_mac_a", "net", "00:00:00:01");
reg_set_addr( $remote_ip_a, "HKCU\\Software\\IPXWrapper\\$remote_mac_b", "net", "00:00:00:02");
@ -196,6 +198,68 @@ describe "IPXWrapper" => sub
it_should_behave_like "getsockopt";
};
};
describe "using Novell Ethernet encapsulation" => sub
{
before all => sub
{
reg_delete_key($remote_ip_a, "HKCU\\Software\\IPXWrapper");
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "use_pcap", 1);
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "frame_type", 2);
reg_set_addr( $remote_ip_a, "HKCU\\Software\\IPXWrapper\\$remote_mac_a", "net", "00:00:00:01");
reg_set_addr( $remote_ip_a, "HKCU\\Software\\IPXWrapper\\$remote_mac_b", "net", "00:00:00:02");
@expected_addrs = (
{
net => "00:00:00:01",
node => $remote_mac_a,
maxpkt => ETHER_MAX_DATA_SIZE,
},
{
net => "00:00:00:02",
node => $remote_mac_b,
maxpkt => ETHER_MAX_DATA_SIZE,
},
);
};
describe "getsockopt" => sub
{
it_should_behave_like "getsockopt";
};
};
describe "using LLC (802.2) Ethernet encapsulation" => sub
{
before all => sub
{
reg_delete_key($remote_ip_a, "HKCU\\Software\\IPXWrapper");
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "use_pcap", 1);
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "frame_type", 3);
reg_set_addr( $remote_ip_a, "HKCU\\Software\\IPXWrapper\\$remote_mac_a", "net", "00:00:00:01");
reg_set_addr( $remote_ip_a, "HKCU\\Software\\IPXWrapper\\$remote_mac_b", "net", "00:00:00:02");
@expected_addrs = (
{
net => "00:00:00:01",
node => $remote_mac_a,
maxpkt => LLC_MAX_DATA_SIZE,
},
{
net => "00:00:00:02",
node => $remote_mac_b,
maxpkt => LLC_MAX_DATA_SIZE,
},
);
};
describe "getsockopt" => sub
{
it_should_behave_like "getsockopt";
};
};
};
sub get_first_addr_node

View File

@ -23,6 +23,7 @@ use FindBin;
use lib "$FindBin::Bin/lib/";
use IPXWrapper::Capture::IPX;
use IPXWrapper::Capture::IPXLLC;
use IPXWrapper::Capture::IPXNovell;
use IPXWrapper::Tool::IPXRecv;
use IPXWrapper::Util;
@ -43,6 +44,7 @@ my $node_c_net = "00:00:00:00";
my $ipx_eth_capture_class;
my $ipx_eth_send_func;
my $max_payload_size;
shared_examples_for "ipx over ethernet" => sub
{
@ -501,6 +503,96 @@ shared_examples_for "ipx over ethernet" => sub
};
};
it "can transmit the largest possible packet" => sub
{
my $capture = $ipx_eth_capture_class->new($local_dev_a);
run_remote_cmd(
$remote_ip_a, "Z:\\tools\\ipx-send.exe",
"-l" => $max_payload_size,
"-s" => "4324", "-h" => $remote_mac_a,
"00:00:00:01", $local_mac_a, "6789",
);
sleep(1);
my @packets = $capture->read_available();
cmp_hashes_partial(\@packets, [
{
src_network => "00:00:00:01",
src_node => $remote_mac_a,
src_socket => 4324,
dst_network => "00:00:00:01",
dst_node => $local_mac_a,
dst_socket => 6789,
data => (chr(0xAA) x $max_payload_size),
},
]);
};
it "refuses to transmit larger than the largest possible packet" => sub
{
my $capture = $ipx_eth_capture_class->new($local_dev_a);
trap {
run_remote_cmd(
$remote_ip_a, "Z:\\tools\\ipx-send.exe",
"-l" => $max_payload_size + 1,
"-s" => "1111", "-h" => $remote_mac_a,
"00:00:00:01", $local_mac_a, "2222",
);
};
sleep(1);
# Ensure sendto() failed with WSAEMSGSIZE
like($trap->die(), qr/^sendto: 10040$/m);
# Ensure no packets were transmitted.
my @packets = $capture->read_available();
cmp_hashes_partial(\@packets, []);
};
it "can receive the largest possible packet" => sub
{
my $capture = IPXWrapper::Tool::IPXRecv->new(
$remote_ip_a,
"00:00:00:00", $remote_mac_a, "3456",
);
$ipx_eth_send_func->($local_dev_a,
tc => 0,
type => 0,
dest_network => "00:00:00:01",
dest_node => $remote_mac_a,
dest_socket => 3456,
src_network => "00:00:00:01",
src_node => $local_mac_a,
src_socket => 4567,
data => ("x" x $max_payload_size),
);
sleep(1);
my @packets = $capture->kill_and_read();
cmp_hashes_partial(\@packets, [
{
src_network => "00:00:00:01",
src_node => $local_mac_a,
src_socket => 4567,
data => ("x" x $max_payload_size),
},
]);
};
before all => sub
{
$ptype_capture_class = $ipx_eth_capture_class;
@ -541,6 +633,7 @@ describe "IPXWrapper using Ethernet encapsulation" => sub
$ipx_eth_capture_class = "IPXWrapper::Capture::IPX";
$ipx_eth_send_func = \&send_ipx_packet_ethernet;
$max_payload_size = 1470;
};
it_should_behave_like "ipx over ethernet";
@ -558,6 +651,25 @@ describe "IPXWrapper using Novell Ethernet encapsulation" => sub
$ipx_eth_capture_class = "IPXWrapper::Capture::IPXNovell";
$ipx_eth_send_func = \&send_ipx_packet_novell;
$max_payload_size = 1470;
};
it_should_behave_like "ipx over ethernet";
};
describe "IPXWrapper using LLC (802.2) Ethernet encapsulation" => sub
{
before all => sub
{
reg_delete_key($remote_ip_a, "HKCU\\Software\\IPXWrapper");
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "use_pcap", 1);
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "frame_type", 3);
reg_set_addr( $remote_ip_a, "HKCU\\Software\\IPXWrapper\\$remote_mac_a", "net", "00:00:00:01");
reg_set_addr( $remote_ip_a, "HKCU\\Software\\IPXWrapper\\$remote_mac_b", "net", "00:00:00:00");
$ipx_eth_capture_class = "IPXWrapper::Capture::IPXLLC";
$ipx_eth_send_func = \&send_ipx_packet_llc;
$max_payload_size = 1467;
};
it_should_behave_like "ipx over ethernet";

845
tests/ethernet.c Normal file
View File

@ -0,0 +1,845 @@
/* IPXWrapper test suite
* 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.
*/
#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#include "tests/tap/basic.h"
#include "src/ethernet.h"
#define CHECK_FRAME_SIZE(func, input, output) \
is_int((output), func(input), #func "(" #input ") returns " #output)
#define UNPACK_GOOD_FRAME(func, desc, expect_ipx_off, expect_ipx_len, frame_len, ...) \
{ \
const unsigned char FRAME[frame_len] = { __VA_ARGS__ }; \
\
const novell_ipx_packet *ipx; \
size_t ipx_len; \
\
ok(func(&ipx, &ipx_len, FRAME, frame_len), #func "(<" desc ">) succeeds"); \
ok((ipx == (FRAME + expect_ipx_off)), #func "(<" desc ">) returns the correct payload address"); \
is_int(expect_ipx_len, ipx_len, #func "(<" desc ">) returns the correct payload length"); \
}
#define UNPACK_BAD_FRAME(func, desc, frame_len, ...) \
{ \
const unsigned char FRAME[frame_len] = { __VA_ARGS__ }; \
\
const novell_ipx_packet *ipx; \
size_t ipx_len; \
\
ok(!func(&ipx, &ipx_len, FRAME, frame_len), #func "(<" desc ">) fails"); \
}
int main()
{
plan_lazy();
/* +------------------+
* | ethII_frame_size |
* +------------------+
*/
CHECK_FRAME_SIZE(ethII_frame_size, 0, 44);
CHECK_FRAME_SIZE(ethII_frame_size, 50, 94);
CHECK_FRAME_SIZE(ethII_frame_size, 2000, 2044);
/* +------------------+
* | ethII_frame_pack |
* +------------------+
*/
{
uint8_t ptype = 0x42;
addr32_t src_net = addr32_in((unsigned char[]){ 0xDE, 0xAD, 0xBE, 0xEF });
addr48_t src_node = addr48_in((unsigned char[]){ 0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D });
uint16_t src_socket = htons(1234);
addr32_t dst_net = addr32_in((unsigned char[]){ 0xBE, 0xEF, 0x0D, 0xAD });
addr48_t dst_node = addr48_in((unsigned char[]){ 0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00 });
uint16_t dst_socket = htons(9876);
static const char payload[] = { 0x00, 0xFF, 0x12, 0x34 };
unsigned char buf[1024];
ethII_frame_pack(&buf,
ptype,
src_net, src_node, src_socket,
dst_net, dst_node, dst_socket,
payload, sizeof(payload));
static const unsigned char expect[] = {
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x81, 0x37, /* Ethertype */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x22, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
/* Payload */
0x00, 0xFF, 0x12, 0x34,
};
is_blob(expect, buf, sizeof(expect), "ethII_frame_pack() serialises correctly");
}
/* +--------------------+
* | ethII_frame_unpack |
* +--------------------+
*/
/* Frame with smallest possible IPX packet (30 bytes) */
UNPACK_GOOD_FRAME(ethII_frame_unpack,
"frame with 30 byte packet",
14, /* Offset of IPX packet */
30, /* Length of IPX packet */
/* Frame length */
44,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x81, 0x37, /* Ethertype */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1E, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
);
/* Frame with wrong Ethertype (valid raw frame) */
UNPACK_BAD_FRAME(ethII_frame_unpack,
"frame with wrong Ethertype",
/* Frame length */
44,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x1E, /* Payload length */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1E, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
);
/* Frame with 2000 byte IPX packet */
UNPACK_GOOD_FRAME(ethII_frame_unpack,
"frame with 2000 byte packet",
14, /* Offset of IPX packet */
2000, /* Length of IPX packet */
/* Frame length */
2014,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x81, 0x37, /* Ethertype */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x07, 0xD0, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
/* IPX payload (uninitialised) */
);
/* Frame too short to hold all the headers */
UNPACK_BAD_FRAME(ethII_frame_unpack,
"truncated frame - too short",
/* Frame length */
43,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x81, 0x37, /* Ethertype */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1D, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, /* Source socket (truncated) */
);
/* +-------------------+
* | novell_frame_size |
* +-------------------+
*/
CHECK_FRAME_SIZE(novell_frame_size, 0, 44);
CHECK_FRAME_SIZE(novell_frame_size, 50, 94);
CHECK_FRAME_SIZE(novell_frame_size, 1470, 1514);
CHECK_FRAME_SIZE(novell_frame_size, 1471, 0);
/* +===================+
* | novell_frame_pack |
* +===================+
*/
{
uint8_t ptype = 0x42;
addr32_t src_net = addr32_in((unsigned char[]){ 0xDE, 0xAD, 0xBE, 0xEF });
addr48_t src_node = addr48_in((unsigned char[]){ 0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D });
uint16_t src_socket = htons(1234);
addr32_t dst_net = addr32_in((unsigned char[]){ 0xBE, 0xEF, 0x0D, 0xAD });
addr48_t dst_node = addr48_in((unsigned char[]){ 0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00 });
uint16_t dst_socket = htons(9876);
static const char payload[] = { 0x00, 0xFF, 0x12, 0x34 };
unsigned char buf[1024];
novell_frame_pack(&buf,
ptype,
src_net, src_node, src_socket,
dst_net, dst_node, dst_socket,
payload, sizeof(payload));
static const unsigned char expect[] = {
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x22, /* Payload length */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x22, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
/* Payload */
0x00, 0xFF, 0x12, 0x34,
};
is_blob(expect, buf, sizeof(expect), "novell_frame_pack() serialises correctly");
}
/* +---------------------+
* | novell_frame_unpack |
* +---------------------+
*/
/* Frame with smallest possible IPX packet (30 bytes) */
UNPACK_GOOD_FRAME(novell_frame_unpack,
"frame with 30 byte packet",
14, /* Offset of IPX packet */
30, /* Length of IPX packet */
/* Frame length */
44,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x1E, /* Payload length */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1E, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
);
/* Frame with an Ethernet II Ethertype rather than a length */
UNPACK_BAD_FRAME(novell_frame_unpack,
"frame with 30 byte packet and an Ethertype",
/* Frame length */
44,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x81, 0x37, /* Ethertype */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1E, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
);
/* Frame with largest allowable IPX packet (1500 bytes) */
UNPACK_GOOD_FRAME(novell_frame_unpack,
"frame with 1500 byte packet",
14, /* Offset of IPX packet */
1500, /* Length of IPX packet */
/* Frame length */
1514,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x05, 0xDC, /* Payload length */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x05, 0xDC, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
/* IPX payload (uninitialised) */
);
/* Frame with 1501 length header (undefined behaviour) */
UNPACK_BAD_FRAME(novell_frame_unpack,
"frame with 1501 byte packet",
/* Frame length */
1515,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x05, 0xDD, /* Payload length */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x05, 0xDD, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
/* IPX payload (uninitialised) */
);
/* Valid IPX packet within, but Ethernet payload length is too short */
UNPACK_BAD_FRAME(novell_frame_unpack,
"frame with valid packet but 29 bytes in length header",
/* Frame length */
48,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x1D, /* Payload length */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x22, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
/* IPX payload */
0x00, 0xFF, 0x12, 0x34,
);
/* Frame too short to hold all the headers */
UNPACK_BAD_FRAME(novell_frame_unpack,
"truncated frame - too short",
/* Frame length */
43,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x1D, /* Payload length */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1D, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, /* Source socket (truncated) */
);
/* Length runs past frame end */
UNPACK_BAD_FRAME(novell_frame_unpack,
"truncated frame - length runs past end",
/* Frame length */
44,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x1F, /* Payload length */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1F, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
);
/* +----------------+
* | llc_frame_size |
* +----------------+
*/
CHECK_FRAME_SIZE(llc_frame_size, 0, 47);
CHECK_FRAME_SIZE(llc_frame_size, 50, 97);
CHECK_FRAME_SIZE(llc_frame_size, 1467, 1514);
CHECK_FRAME_SIZE(llc_frame_size, 1468, 0);
/* +================+
* | llc_frame_pack |
* +================+
*/
{
uint8_t ptype = 0x42;
addr32_t src_net = addr32_in((unsigned char[]){ 0xDE, 0xAD, 0xBE, 0xEF });
addr48_t src_node = addr48_in((unsigned char[]){ 0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D });
uint16_t src_socket = htons(1234);
addr32_t dst_net = addr32_in((unsigned char[]){ 0xBE, 0xEF, 0x0D, 0xAD });
addr48_t dst_node = addr48_in((unsigned char[]){ 0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00 });
uint16_t dst_socket = htons(9876);
static const char payload[] = { 0x00, 0xFF, 0x12, 0x34 };
unsigned char buf[1024];
llc_frame_pack(&buf,
ptype,
src_net, src_node, src_socket,
dst_net, dst_node, dst_socket,
payload, sizeof(payload));
static const unsigned char expect[] = {
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x25, /* Payload length */
/* LLC header */
0xE0, /* DSAP */
0xE0, /* SSAP */
0x03, /* Control */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x22, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
/* Payload */
0x00, 0xFF, 0x12, 0x34,
};
is_blob(expect, buf, sizeof(expect), "llc_frame_pack() serialises correctly");
}
/* +------------------+
* | llc_frame_unpack |
* +------------------+
*/
/* Frame with smallest possible IPX packet (30 bytes) */
UNPACK_GOOD_FRAME(llc_frame_unpack,
"frame with 30 byte packet",
17, /* Offset of IPX packet */
30, /* Length of IPX packet */
/* Frame length */
47,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x21, /* Payload length */
/* LLC header */
0xE0, /* DSAP */
0xE0, /* SSAP */
0x03, /* Control */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1E, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
);
/* Frame with an Ethernet II Ethertype rather than a length */
UNPACK_BAD_FRAME(llc_frame_unpack,
"frame with 30 byte packet and an Ethertype",
/* Frame length */
47,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x81, 0x37, /* Ethertype */
/* LLC header */
0xE0, /* DSAP */
0xE0, /* SSAP */
0x03, /* Control */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1E, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
);
/* Frame with largest allowable IPX packet (1497 bytes) */
UNPACK_GOOD_FRAME(llc_frame_unpack,
"frame with 1497 byte packet",
17, /* Offset of IPX packet */
1497, /* Length of IPX packet */
/* Frame length */
1514,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x05, 0xDC, /* Payload length */
/* LLC header */
0xE0, /* DSAP */
0xE0, /* SSAP */
0x03, /* Control */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x05, 0xD9, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
/* IPX payload (uninitialised) */
);
/* Frame with 1501 length header (undefined behaviour) */
UNPACK_BAD_FRAME(llc_frame_unpack,
"frame with 1498 byte packet",
/* Frame length */
1515,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x05, 0xDD, /* Payload length */
/* LLC header */
0xE0, /* DSAP */
0xE0, /* SSAP */
0x03, /* Control */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x05, 0xDA, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
/* IPX payload (uninitialised) */
);
/* Valid IPX packet within, but Ethernet payload length is too short */
UNPACK_BAD_FRAME(llc_frame_unpack,
"frame with valid packet but 32 bytes in length header",
/* Frame length */
47,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x20, /* Payload length */
/* LLC header */
0xE0, /* DSAP */
0xE0, /* SSAP */
0x03, /* Control */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1E, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
);
/* Frame too short to hold all the headers */
UNPACK_BAD_FRAME(llc_frame_unpack,
"truncated frame - too short",
/* Frame length */
46,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x20, /* Payload length */
/* LLC header */
0xE0, /* DSAP */
0xE0, /* SSAP */
0x03, /* Control */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1D, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, /* Source socket (truncated) */
);
/* Length runs past frame end */
UNPACK_BAD_FRAME(llc_frame_unpack,
"truncated frame - length runs past end",
/* Frame length */
47,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x22, /* Payload length */
/* LLC header */
0xE0, /* DSAP */
0xE0, /* SSAP */
0x03, /* Control */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1F, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
);
/* Wrong DSAP */
UNPACK_BAD_FRAME(llc_frame_unpack,
"frame with wrong DSAP",
/* Frame length */
47,
/* Ethernet header */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination MAC */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source MAC */
0x00, 0x21, /* Payload length */
/* LLC header */
0xE1, /* DSAP */
0xE0, /* SSAP */
0x03, /* Control */
/* IPX header */
0xFF, 0xFF, /* Checksum */
0x00, 0x1E, /* Length */
0x00, /* Hops */
0x42, /* Type */
0xBE, 0xEF, 0x0D, 0xAD, /* Destination network */
0x99, 0xB0, 0x77, 0x1E, 0x50, 0x00, /* Destination node */
0x26, 0x94, /* Destination socket */
0xDE, 0xAD, 0xBE, 0xEF, /* Source network */
0x0B, 0xAD, 0x0B, 0xEE, 0xF0, 0x0D, /* Source node */
0x04, 0xD2, /* Source socket */
);
return 0;
}

View File

@ -28,7 +28,7 @@ sub new
my ($class, $dev) = @_;
my $err;
my $pcap = Net::Pcap::pcap_open_live($dev, 1500, 0, 1, \$err)
my $pcap = Net::Pcap::pcap_open_live($dev, 2000, 0, 1, \$err)
or die("Cannot open device $dev: $err");
return bless(\$pcap, $class);

View File

@ -0,0 +1,81 @@
# IPXWrapper test suite
# 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.
use strict;
use warnings;
package IPXWrapper::Capture::IPXLLC;
use Net::Pcap;
use NetPacket::Ethernet;
use NetPacket::IPX;
sub new
{
my ($class, $dev) = @_;
my $err;
my $pcap = Net::Pcap::pcap_open_live($dev, 2000, 0, 1, \$err)
or die("Cannot open device $dev: $err");
return bless(\$pcap, $class);
}
sub read_available
{
my ($self) = @_;
my @packets = ();
Net::Pcap::pcap_dispatch($$self, -1, sub
{
my ($user_data, $header, $packet) = @_;
my $ether = NetPacket::Ethernet->decode($packet);
if($ether->{type} <= 1500 && substr($ether->{data}, 0, 5) eq "\x{E0}\x{E0}\x{03}\x{FF}\x{FF}")
{
my $ipx = NetPacket::IPX->decode(substr($ether->{data}, 3));
my %packet = (
src_mac => $ether->{src_mac},
dst_mac => $ether->{dest_mac},
tc => $ipx->{tc},
type => $ipx->{type},
src_network => $ipx->{src_network},
src_node => $ipx->{src_node},
src_socket => $ipx->{src_socket},
dst_network => $ipx->{dest_network},
dst_node => $ipx->{dest_node},
dst_socket => $ipx->{dest_socket},
data => $ipx->{data},
);
# Skip if the frame length is wrong.
return if(($ether->{type} - 33) != length($packet{data}));
push(@packets, \%packet);
}
}, undef);
return @packets;
}
1;

View File

@ -28,7 +28,7 @@ sub new
my ($class, $dev) = @_;
my $err;
my $pcap = Net::Pcap::pcap_open_live($dev, 1500, 0, 1, \$err)
my $pcap = Net::Pcap::pcap_open_live($dev, 2000, 0, 1, \$err)
or die("Cannot open device $dev: $err");
return bless(\$pcap, $class);

View File

@ -32,6 +32,7 @@ our @EXPORT = qw(
send_ipx_over_udp
send_ipx_packet_ethernet
send_ipx_packet_novell
send_ipx_packet_llc
cmp_hashes_partial
@ -54,12 +55,14 @@ sub run_remote_cmd
note(join(" ", @command));
my $output = "";
IPC::Run::run(\@command, ">&" => \$output)
or die("Failure running $exe_name:\n$output");
my $ok = IPC::Run::run(\@command, ">&" => \$output);
# Oh line endings, how do I hate thee? Let me count the ways.
$output =~ s/\r//g;
die("Failure running $exe_name:\n$output")
unless($ok);
return $output;
}
@ -158,6 +161,21 @@ sub send_ipx_packet_novell
$enc_packet);
}
sub send_ipx_packet_llc
{
my ($dev, %options) = @_;
my $packet = NetPacket::IPX->new(%options);
my $enc_packet = $packet->encode();
# Prefix IPX packet with LLC header
$enc_packet = pack("C3", 0xE0, 0xE0, 0x03).$enc_packet;
_send_ethernet_frame($dev,
$packet->{dest_node}, $packet->{src_node}, length($enc_packet),
$enc_packet);
}
sub cmp_hashes_partial
{
my ($got, $expect) = @_;

View File

@ -568,6 +568,43 @@ is_hex(unsigned long wanted, unsigned long seen, const char *format, ...)
}
/*
* Takes pointers to an expected region of memory and a seen region of memory
* and assumes the test passes if the len bytes onwards from them match.
* Otherwise reports any bytes which didn't match.
*/
int
is_blob(const void *wanted, const void *seen, size_t len, const char *format, ...)
{
int success;
size_t i;
fflush(stderr);
check_diag_files();
success = (memcmp(wanted, seen, len) == 0);
if (success)
printf("ok %lu", testnum++);
else {
for(i = 0; i < len; ++i)
{
unsigned char wanted_b = ((unsigned char*)(wanted))[i];
unsigned char seen_b = ((unsigned char*)(seen))[i];
if(wanted_b != seen_b)
{
diag("offset %x: wanted %02x, seen %02x",
(unsigned)(i), (unsigned)(wanted_b), (unsigned)(seen_b));
}
}
printf("not ok %lu", testnum++);
_failed++;
}
PRINT_DESC(" - ", format);
putchar('\n');
return success;
}
/*
* Bail out with an error.
*/

View File

@ -95,6 +95,8 @@ int is_string(const char *wanted, const char *seen, const char *format, ...)
__attribute__((__format__(printf, 3, 4)));
int is_hex(unsigned long wanted, unsigned long seen, const char *format, ...)
__attribute__((__format__(printf, 3, 4)));
int is_blob(const void *wanted, const void *seen, size_t len, const char *format, ...)
__attribute__((__format__(printf, 4, 5)));
/* Bail out with an error. sysbail appends strerror(errno). */
void bail(const char *format, ...)

View File

@ -28,7 +28,7 @@
#include "tools.h"
const int MAX_SOCKETS = 32;
const int BUFSIZE = 64;
const int BUFSIZE = 2048;
static void usage(const char *argv0)
{

View File

@ -43,7 +43,7 @@ int main(int argc, char **argv)
BOOL reuse = FALSE;
int opt;
while((opt = getopt(argc, argv, "n:h:s:t:d:br")) != -1)
while((opt = getopt(argc, argv, "n:h:s:t:d:l:br")) != -1)
{
if(opt == 'n')
{
@ -65,6 +65,13 @@ int main(int argc, char **argv)
{
data = optarg;
}
else if(opt == 'l')
{
int len = atoi(optarg);
assert((data = malloc(len)) != NULL);
memset((char*)(data), 0xAA, len);
}
else if(opt == 'b')
{
bcast = TRUE;
@ -87,6 +94,7 @@ int main(int argc, char **argv)
"[-s <local socket number>]\n"
"[-t <packet type>]\n"
"[-d <payload>]\n"
"[-l <payload length>]\n"
"[-b (enable SO_BROADCAST)]\n"
"[-r (enable SO_REUSEADDR)]\n"
"<remote network number>\n"
@ -132,7 +140,14 @@ int main(int argc, char **argv)
printf("Bound to local address: %s\n", formatted_addr);
}
assert(sendto(sock, data, strlen(data), 0, (struct sockaddr*)(&remote_addr), sizeof(remote_addr)) == strlen(data));
int sr = sendto(sock, data, strlen(data), 0, (struct sockaddr*)(&remote_addr), sizeof(remote_addr));
if(sr == -1)
{
fprintf(stderr, "sendto: %u\n", (unsigned int)(WSAGetLastError()));
return 1;
}
assert(sr == strlen(data));
closesocket(sock);