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; }