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:
commit
ea2e61ea32
7
Makefile
7
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)
|
||||
@ -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 $@ $<
|
||||
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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
314
src/ethernet.c
Normal 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
97
src/ethernet.h
Normal 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 */
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
96
src/router.c
96
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,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)
|
||||
|
131
src/winsock.c
131
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,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
29
tests/07-ethernet.t
Normal 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);
|
@ -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
|
||||
|
@ -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
845
tests/ethernet.c
Normal 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;
|
||||
}
|
@ -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);
|
||||
|
81
tests/lib/IPXWrapper/Capture/IPXLLC.pm
Normal file
81
tests/lib/IPXWrapper/Capture/IPXLLC.pm
Normal 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;
|
@ -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);
|
||||
|
@ -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) = @_;
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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, ...)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user