From b7b4a953d86c3c4f89a204fe6a55dc38e24bb432 Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Tue, 28 Mar 2017 23:26:44 +0100 Subject: [PATCH 1/7] Refactored Ethernet frame (de)serialisation. Reworked the (de)serialising of Ethernet frames into seperate functions in preparation for supporting IEEE 802.2 LLC frames. --- Makefile | 2 +- manifest.src.txt | 2 + src/ethernet.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++ src/ethernet.h | 88 ++++++++++++++++++++ src/ipxwrapper.h | 35 -------- src/router.c | 88 ++++++++------------ src/winsock.c | 118 +++++++++++++++------------ 7 files changed, 397 insertions(+), 144 deletions(-) create mode 100644 src/ethernet.c create mode 100644 src/ethernet.h diff --git a/Makefile b/Makefile index 273fafa..e4fa65e 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/manifest.src.txt b/manifest.src.txt index f9b64aa..2de0b9a 100644 --- a/manifest.src.txt +++ b/manifest.src.txt @@ -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 diff --git a/src/ethernet.c b/src/ethernet.c new file mode 100644 index 0000000..8438140 --- /dev/null +++ b/src/ethernet.c @@ -0,0 +1,208 @@ +/* IPXWrapper - Ethernet frame handling + * Copyright (C) 2017 Daniel Collins + * + * 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 + +#include +#include +#include + +#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; +} diff --git a/src/ethernet.h b/src/ethernet.h new file mode 100644 index 0000000..d63b497 --- /dev/null +++ b/src/ethernet.h @@ -0,0 +1,88 @@ +/* IPXWrapper - Ethernet frame handling + * Copyright (C) 2017 Daniel Collins + * + * 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 +#include + +#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 */ diff --git a/src/ipxwrapper.h b/src/ipxwrapper.h index 33207fc..ea34dec 100644 --- a/src/ipxwrapper.h +++ b/src/ipxwrapper.h @@ -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; diff --git a/src/router.c b/src/router.c index f6447f7..2a94dc1 100644 --- a/src/router.c +++ b/src/router.c @@ -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) diff --git a/src/winsock.c b/src/winsock.c index 893de37..ec37edc 100644 --- a/src/winsock.c +++ b/src/winsock.c @@ -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) { From 659641d436ca349144f150ea088436da303c258e Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Wed, 29 Mar 2017 20:34:32 +0100 Subject: [PATCH 2/7] Added unit tests for Ethernet frame (de)serialising functions. --- Makefile | 5 +- manifest.src.txt | 2 + tests/07-ethernet.t | 29 +++ tests/ethernet.c | 507 ++++++++++++++++++++++++++++++++++++++++++++ tests/tap/basic.c | 37 ++++ tests/tap/basic.h | 2 + 6 files changed, 581 insertions(+), 1 deletion(-) create mode 100644 tests/07-ethernet.t create mode 100644 tests/ethernet.c diff --git a/Makefile b/Makefile index e4fa65e..dbd1d83 100644 --- a/Makefile +++ b/Makefile @@ -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 $@ $< diff --git a/manifest.src.txt b/manifest.src.txt index 2de0b9a..4e41654 100644 --- a/manifest.src.txt +++ b/manifest.src.txt @@ -59,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 @@ -68,6 +69,7 @@ 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 diff --git a/tests/07-ethernet.t b/tests/07-ethernet.t new file mode 100644 index 0000000..133346a --- /dev/null +++ b/tests/07-ethernet.t @@ -0,0 +1,29 @@ +# IPXWrapper test suite +# Copyright (C) 2017 Daniel Collins +# +# 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); diff --git a/tests/ethernet.c b/tests/ethernet.c new file mode 100644 index 0000000..57cd2cd --- /dev/null +++ b/tests/ethernet.c @@ -0,0 +1,507 @@ +/* IPXWrapper test suite + * Copyright (C) 2017 Daniel Collins + * + * 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 + +#include +#include + +#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, 1456, 1500); + CHECK_FRAME_SIZE(novell_frame_size, 1457, 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 */ + ); + + return 0; +} diff --git a/tests/tap/basic.c b/tests/tap/basic.c index 92a749b..45b664c 100644 --- a/tests/tap/basic.c +++ b/tests/tap/basic.c @@ -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. */ diff --git a/tests/tap/basic.h b/tests/tap/basic.h index c002df9..a32eb2e 100644 --- a/tests/tap/basic.h +++ b/tests/tap/basic.h @@ -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, ...) From 323341fc0c7f7569d397d344df186e89be6f6a91 Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Wed, 29 Mar 2017 20:35:38 +0100 Subject: [PATCH 3/7] Initialise source MAC when serialising Ethernet frames. --- src/ethernet.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ethernet.c b/src/ethernet.c index 8438140..2b3e9f5 100644 --- a/src/ethernet.c +++ b/src/ethernet.c @@ -107,6 +107,7 @@ void ethII_frame_pack(void *frame_buffer, 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, @@ -162,6 +163,7 @@ void novell_frame_pack(void *frame_buffer, 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, From bc778d9f3bc8da6d43cc3bd9f50a04badf6d3fe2 Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Wed, 29 Mar 2017 22:00:47 +0100 Subject: [PATCH 4/7] Implemented support for 802.2 LLC framing. --- src/config.c | 3 +- src/config.h | 3 +- src/ethernet.c | 108 +++++++++++++++ src/ethernet.h | 9 ++ src/ipxconfig.cpp | 1 + src/router.c | 8 ++ src/winsock.c | 15 ++ tests/ethernet.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 483 insertions(+), 2 deletions(-) diff --git a/src/config.c b/src/config.c index f246b11..37d7f2f 100644 --- a/src/config.c +++ b/src/config.c @@ -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", diff --git a/src/config.h b/src/config.h index cd6872b..170ffb7 100644 --- a/src/config.h +++ b/src/config.h @@ -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 { diff --git a/src/ethernet.c b/src/ethernet.c index 2b3e9f5..3c11d10 100644 --- a/src/ethernet.c +++ b/src/ethernet.c @@ -62,6 +62,19 @@ struct ethernet_header }; } __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, @@ -208,3 +221,98 @@ bool novell_frame_unpack(const novell_ipx_packet **packet, size_t *packet_len, c 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; +} diff --git a/src/ethernet.h b/src/ethernet.h index d63b497..3eadaba 100644 --- a/src/ethernet.h +++ b/src/ethernet.h @@ -85,4 +85,13 @@ void novell_frame_pack(void *frame_buffer, 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 */ diff --git a/src/ipxconfig.cpp b/src/ipxconfig.cpp index f758669..1568703 100644 --- a/src/ipxconfig.cpp +++ b/src/ipxconfig.cpp @@ -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); diff --git a/src/router.c b/src/router.c index 2a94dc1..d3f8955 100644 --- a/src/router.c +++ b/src/router.c @@ -517,6 +517,14 @@ static void _handle_pcap_frame(u_char *user, const struct pcap_pkthdr *pkt_heade return; } + break; + + case FRAME_TYPE_LLC: + if(!llc_frame_unpack(&ipx, &ipx_len, pkt_data, pkt_header->caplen)) + { + return; + } + break; } diff --git a/src/winsock.c b/src/winsock.c index ec37edc..d198427 100644 --- a/src/winsock.c +++ b/src/winsock.c @@ -59,6 +59,9 @@ static int _max_ipx_payload(void) case FRAME_TYPE_ETH_II: case FRAME_TYPE_NOVELL: return 1500 - (14 + sizeof(novell_ipx_packet)); + + case FRAME_TYPE_LLC: + return 1500 - (17 + sizeof(novell_ipx_packet)); } abort(); @@ -1227,6 +1230,10 @@ static DWORD ipx_send_packet( 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 */ @@ -1267,6 +1274,14 @@ static DWORD ipx_send_packet( dest_net, dest_node, dest_socket, data, data_size); break; + + 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; } /* Transmit the frame. */ diff --git a/tests/ethernet.c b/tests/ethernet.c index 57cd2cd..dd527c8 100644 --- a/tests/ethernet.c +++ b/tests/ethernet.c @@ -503,5 +503,343 @@ int main() 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, 1453, 1500); + CHECK_FRAME_SIZE(llc_frame_size, 1454, 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; } From 9d76f3ef00a8d563225047543eca609492bdd73f Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Wed, 29 Mar 2017 22:19:31 +0100 Subject: [PATCH 5/7] Extended end-to-end test suite to cover LLC encapsulation. --- manifest.src.txt | 2 + tests/30-eth-ipx.t | 18 ++++++ tests/lib/IPXWrapper/Capture/IPXLLC.pm | 81 ++++++++++++++++++++++++++ tests/lib/IPXWrapper/Util.pm | 16 +++++ 4 files changed, 117 insertions(+) create mode 100644 tests/lib/IPXWrapper/Capture/IPXLLC.pm diff --git a/manifest.src.txt b/manifest.src.txt index 4e41654..fae5d1d 100644 --- a/manifest.src.txt +++ b/manifest.src.txt @@ -73,6 +73,8 @@ 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 diff --git a/tests/30-eth-ipx.t b/tests/30-eth-ipx.t index c3e6cf4..24a0da0 100644 --- a/tests/30-eth-ipx.t +++ b/tests/30-eth-ipx.t @@ -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; @@ -563,4 +564,21 @@ describe "IPXWrapper using Novell Ethernet encapsulation" => sub 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; + }; + + it_should_behave_like "ipx over ethernet"; +}; + runtests unless caller; diff --git a/tests/lib/IPXWrapper/Capture/IPXLLC.pm b/tests/lib/IPXWrapper/Capture/IPXLLC.pm new file mode 100644 index 0000000..a7f2607 --- /dev/null +++ b/tests/lib/IPXWrapper/Capture/IPXLLC.pm @@ -0,0 +1,81 @@ +# IPXWrapper test suite +# Copyright (C) 2017 Daniel Collins +# +# 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, 1500, 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; diff --git a/tests/lib/IPXWrapper/Util.pm b/tests/lib/IPXWrapper/Util.pm index ba4a2b6..cfd8f3e 100644 --- a/tests/lib/IPXWrapper/Util.pm +++ b/tests/lib/IPXWrapper/Util.pm @@ -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 @@ -158,6 +159,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) = @_; From b58ef3eb1eff3b4906328deff6e782e68dd29f7a Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Sun, 20 Aug 2017 16:44:49 +0100 Subject: [PATCH 6/7] getsockopt: Return correct maxpkt when using Ethernet frame types. --- src/winsock.c | 4 +-- tests/15-interfaces.t | 64 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/winsock.c b/src/winsock.c index d198427..50e8796 100644 --- a/src/winsock.c +++ b/src/winsock.c @@ -58,10 +58,10 @@ static int _max_ipx_payload(void) { case FRAME_TYPE_ETH_II: case FRAME_TYPE_NOVELL: - return 1500 - (14 + sizeof(novell_ipx_packet)); + return 1500 - sizeof(novell_ipx_packet); case FRAME_TYPE_LLC: - return 1500 - (17 + sizeof(novell_ipx_packet)); + return 1500 - (3 + sizeof(novell_ipx_packet)); } abort(); diff --git a/tests/15-interfaces.t b/tests/15-interfaces.t index 0236d06..b968877 100644 --- a/tests/15-interfaces.t +++ b/tests/15-interfaces.t @@ -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 From ba7fa5a77f38949d3a3691c2dac16c9222ebef29 Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Sun, 20 Aug 2017 22:59:49 +0100 Subject: [PATCH 7/7] Allow sending Novell/LLC frames up to the maximum size. --- src/ethernet.c | 22 +++--- tests/30-eth-ipx.t | 94 +++++++++++++++++++++++ tests/ethernet.c | 8 +- tests/lib/IPXWrapper/Capture/IPX.pm | 2 +- tests/lib/IPXWrapper/Capture/IPXLLC.pm | 2 +- tests/lib/IPXWrapper/Capture/IPXNovell.pm | 2 +- tests/lib/IPXWrapper/Util.pm | 6 +- tools/ipx-recv.c | 2 +- tools/ipx-send.c | 19 ++++- 9 files changed, 132 insertions(+), 25 deletions(-) diff --git a/src/ethernet.c b/src/ethernet.c index 3c11d10..e249a77 100644 --- a/src/ethernet.c +++ b/src/ethernet.c @@ -154,17 +154,15 @@ bool ethII_frame_unpack(const novell_ipx_packet **packet, size_t *packet_len, co 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)) + || ipx_payload_len > (1500 - sizeof(novell_ipx_packet))) { return 0; } - return OVERHEAD + ipx_payload_len; + return sizeof(ethernet_header) + + sizeof(novell_ipx_packet) + + ipx_payload_len; } void novell_frame_pack(void *frame_buffer, @@ -224,18 +222,16 @@ bool novell_frame_unpack(const novell_ipx_packet **packet, size_t *packet_len, c 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)) + || ipx_payload_len > (1500 - (sizeof(llc_header) + sizeof(novell_ipx_packet)))) { return 0; } - return OVERHEAD + ipx_payload_len; + return sizeof(ethernet_header) + + sizeof(llc_header) + + sizeof(novell_ipx_packet) + + ipx_payload_len; } void llc_frame_pack(void *frame_buffer, diff --git a/tests/30-eth-ipx.t b/tests/30-eth-ipx.t index 24a0da0..2e21130 100644 --- a/tests/30-eth-ipx.t +++ b/tests/30-eth-ipx.t @@ -44,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 { @@ -502,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; @@ -542,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"; @@ -559,6 +651,7 @@ 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"; @@ -576,6 +669,7 @@ describe "IPXWrapper using LLC (802.2) Ethernet encapsulation" => sub $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"; diff --git a/tests/ethernet.c b/tests/ethernet.c index dd527c8..6190abd 100644 --- a/tests/ethernet.c +++ b/tests/ethernet.c @@ -241,8 +241,8 @@ int main() CHECK_FRAME_SIZE(novell_frame_size, 0, 44); CHECK_FRAME_SIZE(novell_frame_size, 50, 94); - CHECK_FRAME_SIZE(novell_frame_size, 1456, 1500); - CHECK_FRAME_SIZE(novell_frame_size, 1457, 0); + CHECK_FRAME_SIZE(novell_frame_size, 1470, 1514); + CHECK_FRAME_SIZE(novell_frame_size, 1471, 0); /* +===================+ * | novell_frame_pack | @@ -510,8 +510,8 @@ int main() CHECK_FRAME_SIZE(llc_frame_size, 0, 47); CHECK_FRAME_SIZE(llc_frame_size, 50, 97); - CHECK_FRAME_SIZE(llc_frame_size, 1453, 1500); - CHECK_FRAME_SIZE(llc_frame_size, 1454, 0); + CHECK_FRAME_SIZE(llc_frame_size, 1467, 1514); + CHECK_FRAME_SIZE(llc_frame_size, 1468, 0); /* +================+ * | llc_frame_pack | diff --git a/tests/lib/IPXWrapper/Capture/IPX.pm b/tests/lib/IPXWrapper/Capture/IPX.pm index 6063285..e991851 100644 --- a/tests/lib/IPXWrapper/Capture/IPX.pm +++ b/tests/lib/IPXWrapper/Capture/IPX.pm @@ -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); diff --git a/tests/lib/IPXWrapper/Capture/IPXLLC.pm b/tests/lib/IPXWrapper/Capture/IPXLLC.pm index a7f2607..462f52a 100644 --- a/tests/lib/IPXWrapper/Capture/IPXLLC.pm +++ b/tests/lib/IPXWrapper/Capture/IPXLLC.pm @@ -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); diff --git a/tests/lib/IPXWrapper/Capture/IPXNovell.pm b/tests/lib/IPXWrapper/Capture/IPXNovell.pm index bd5cbf6..656f190 100644 --- a/tests/lib/IPXWrapper/Capture/IPXNovell.pm +++ b/tests/lib/IPXWrapper/Capture/IPXNovell.pm @@ -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); diff --git a/tests/lib/IPXWrapper/Util.pm b/tests/lib/IPXWrapper/Util.pm index cfd8f3e..b4323f4 100644 --- a/tests/lib/IPXWrapper/Util.pm +++ b/tests/lib/IPXWrapper/Util.pm @@ -55,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; } diff --git a/tools/ipx-recv.c b/tools/ipx-recv.c index 320a1f9..d21b564 100644 --- a/tools/ipx-recv.c +++ b/tools/ipx-recv.c @@ -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) { diff --git a/tools/ipx-send.c b/tools/ipx-send.c index 8335201..5228d75 100644 --- a/tools/ipx-send.c +++ b/tools/ipx-send.c @@ -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 ]\n" "[-t ]\n" "[-d ]\n" + "[-l ]\n" "[-b (enable SO_BROADCAST)]\n" "[-r (enable SO_REUSEADDR)]\n" "\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);