From 8676a392e5677ac970a2e5af6be67c84f01c7111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiesner=20Andr=C3=A1s?= Date: Mon, 30 Jan 2023 11:04:46 +0100 Subject: [PATCH] - TCP basic implementation added - TCPWindow added - Checksum bug fixed (again) - CBD introduced - ConnBlock modified - PackSieve report funtionality modified to decrease memory consumption --- arp_cache.c | 8 +- cbd_table.c | 72 +++++++ cbd_table.h | 61 ++++++ connection_block.h | 2 +- eth_interface.c | 5 +- global_state.c | 16 +- global_state.h | 8 + memory_pool.c | 115 ++++++++-- memory_pool.h | 27 +++ packet.h | 2 +- packet_sieve.c | 40 +++- packet_sieve.h | 28 ++- pckt_assembler.c | 18 +- pckt_assembler.h | 2 +- prefab/conn_blocks/arp_connblock.c | 15 +- prefab/conn_blocks/arp_connblock.h | 13 +- prefab/conn_blocks/ethernet_info.c | 12 ++ prefab/conn_blocks/ethernet_info.h | 13 ++ prefab/conn_blocks/icmp_connblock.c | 11 +- prefab/conn_blocks/icmp_connblock.h | 6 + prefab/conn_blocks/igmp_connblock.c | 30 +-- prefab/conn_blocks/igmp_connblock.h | 6 + prefab/conn_blocks/ipv4_connblock.c | 29 +-- prefab/conn_blocks/ipv4_connblock.h | 6 + prefab/conn_blocks/tcp/tcp_window.c | 47 +++-- prefab/conn_blocks/tcp/tcp_window.h | 44 +++- prefab/conn_blocks/tcp_connblock.c | 281 ++++++++++++++++++++++--- prefab/conn_blocks/tcp_connblock.h | 32 ++- prefab/conn_blocks/udp_connblock.c | 76 +++---- prefab/conn_blocks/udp_connblock.h | 11 +- prefab/packet_parsers/dhcp.c | 6 +- prefab/packet_parsers/ipv4_packet.c | 39 +++- prefab/packet_parsers/ipv4_packet.h | 10 + prefab/packet_parsers/tcp_segment.c | 36 +++- prefab/packet_parsers/tcp_segment.h | 42 +++- prefab/packet_parsers/tcp_udp_common.c | 4 +- prefab/packet_parsers/udp_packet.c | 2 +- utils.h | 3 +- 38 files changed, 981 insertions(+), 197 deletions(-) create mode 100644 cbd_table.c create mode 100644 cbd_table.h create mode 100644 prefab/conn_blocks/ethernet_info.c create mode 100644 prefab/conn_blocks/ethernet_info.h diff --git a/arp_cache.c b/arp_cache.c index 2c87fb8..829351b 100644 --- a/arp_cache.c +++ b/arp_cache.c @@ -101,8 +101,8 @@ void arpc_ask(ArpCache *arpc, ip4_addr addr) { arpProps.PLEN = sizeof(ip4_addr); arpProps.OPER = ARPOP_REQ; - memcpy(arpProps.SHA, arpc->cb.intf->mac, ETH_HW_ADDR_LEN); - arpProps.SPA = arpc->cb.intf->ip; + memcpy(arpProps.SHA, arpc->cb.sieve->intf->mac, ETH_HW_ADDR_LEN); + arpProps.SPA = arpc->cb.sieve->intf->ip; memset(arpProps.THA, 0x00, ETH_HW_ADDR_LEN); arpProps.TPA = addr; @@ -118,8 +118,8 @@ void arpc_respond(ArpCache *arpc, const EthernetAddress hwAddr, ip4_addr ipAddr) arpProps.PLEN = sizeof(ip4_addr); arpProps.OPER = ARPOP_REP; - memcpy(arpProps.SHA, arpc->cb.intf->mac, ETH_HW_ADDR_LEN); - arpProps.SPA = arpc->cb.intf->ip; + memcpy(arpProps.SHA, arpc->cb.sieve->intf->mac, ETH_HW_ADDR_LEN); + arpProps.SPA = arpc->cb.sieve->intf->ip; memcpy(arpProps.THA, hwAddr, ETH_HW_ADDR_LEN); arpProps.TPA = ipAddr; diff --git a/cbd_table.c b/cbd_table.c new file mode 100644 index 0000000..6db3977 --- /dev/null +++ b/cbd_table.c @@ -0,0 +1,72 @@ +// +// Created by epagris on 2023.01.30.. +// + +#include "cbd_table.h" +#include "dynmem.h" +#include "utils.h" + +CbdTable *cbdt_new(uint8_t size) { + uint32_t allocSize = sizeof(CbdTable) + size * sizeof(ConnBlock); + CbdTable * cbdt = (CbdTable *) dynmem_alloc(allocSize); + memset(cbdt, 0, allocSize); + cbdt->maxEntries = size; + cbdt->level = 0; + return cbdt; +} + +cbd cbdt_alloc_new(CbdTable *cbdt, const ConnBlock *connBlock) { + if (cbdt->level >= cbdt->maxEntries) { + return CBDT_ERR; + } + + cbd i = 0; + while (cbdt->cbs[i].sieve != NULL) { // look for an empty slot + i++; + } + cbdt->cbs[i] = *connBlock; // store connblock + cbdt->level++; // increase allocation counter + + return CBD_LOWEST_DESCRIPTOR + i; // return descriptor number +} + +static bool cbdt_is_cbd_valid(CbdTable * cbdt, cbd d) { + cbd i = d - CBD_LOWEST_DESCRIPTOR; + return !((d < CBD_LOWEST_DESCRIPTOR) || ((i) >= cbdt->maxEntries) || (cbdt->cbs[i].sieve == NULL) || (cbdt->level == 0)); +} + +void cbdt_release(CbdTable *cbdt, cbd d) { + if (cbdt_is_cbd_valid(cbdt, d)) { + memset(cbdt->cbs + d - CBD_LOWEST_DESCRIPTOR, 0, sizeof(ConnBlock)); // clear connection block properties + cbdt->level--; + } +} + +bool cbdt_get_connection_block(CbdTable *cbdt, cbd d, ConnBlock *connBlock) { + if (!cbdt_is_cbd_valid(cbdt, d)) { + return false; + } + *connBlock = cbdt->cbs[d - CBD_LOWEST_DESCRIPTOR]; + return true; +} + +void cbdt_report(const CbdTable *cbdt) { + INFO("CBDT listing (%d/%d):\n---------------\n\n", cbdt->level, cbdt->maxEntries); + + uint8_t cnt = 0; + while (cnt < cbdt->level) { + if (cbdt->cbs[cnt].sieve->intf != NULL) { + packsieve_layer_info(cbdt->cbs[cnt].sieve, cbdt->cbs[cnt].sieveLayer); + INFO("\n"); + cnt++; + } + } + + INFO("\n"); +} + + + + + + diff --git a/cbd_table.h b/cbd_table.h new file mode 100644 index 0000000..99712f5 --- /dev/null +++ b/cbd_table.h @@ -0,0 +1,61 @@ +#ifndef ETHERLIB_TEST_CBD_TABLE_H +#define ETHERLIB_TEST_CBD_TABLE_H + +#include +#include "connection_block.h" + +#define CBD_LOWEST_DESCRIPTOR (4) + +/** + * Custom type for connection block descriptor. + */ +typedef uint8_t cbd; + +#define CBDT_ERR ((cbd)(~0)) + +/** + * Connection block descriptor table + */ +typedef struct { + cbd maxEntries; ///< Maximum number of entries + cbd level; ///< Fill level + ConnBlock cbs[]; ///< Array of connection blocks +} CbdTable; + +/** + * Create new connecion block table. + * @param size number of maximum entries + * @return pointer to newly created connection block descriptor table + */ +CbdTable * cbdt_new(uint8_t size); + +/** + * Allocate new connection block + * @param cbdt pointer to CBDT + * @return connection block descriptor OR CBDT_ERR + */ +cbd cbdt_alloc_new(CbdTable * cbdt, const ConnBlock * connBlock); + +/** + * Release connection block. + * @param cbdt pointer to CBDT + * @param d CBD value + */ +void cbdt_release(CbdTable * cbdt, cbd d); + +/** + * Get connection block + * @param cbdt pointer to CBDT + * @param d CBD value + * @param connBlock pointer to connection block where the results will be written + * @return indicates that fetch was successful or not + */ +bool cbdt_get_connection_block(CbdTable * cbdt, cbd d, ConnBlock * connBlock); + +/** + * Print report on connection block descriptors. + * @param cbdt pointer to CBD table + */ +void cbdt_report(const CbdTable * cbdt); + +#endif //ETHERLIB_TEST_CBD_TABLE_H diff --git a/connection_block.h b/connection_block.h index fe49413..2b506ca 100644 --- a/connection_block.h +++ b/connection_block.h @@ -11,7 +11,7 @@ typedef int (*ConnBlockTransmitFn)(struct EthInterface_ * intf, const uint8_t * * Connection block. */ typedef struct ConnBlock_ { - struct EthInterface_ * intf; ///< Ethernet interface + PcktSieve * sieve; ///< Ethernet interface PcktSieveLayer * sieveLayer; ///< Sieve layer void * tag; ///< Arbitrary tagging } ConnBlock; diff --git a/eth_interface.c b/eth_interface.c index 59bc0e9..5ba8658 100644 --- a/eth_interface.c +++ b/eth_interface.c @@ -6,6 +6,7 @@ #include "dynmem.h" #include "utils.h" #include "etherlib_options.h" +#include "etherlib/prefab/conn_blocks/ethernet_info.h" static int ethintf_llrecv(EthIODef * io, const RawPckt * pckt) { ethinf_receive((EthInterface *) io->tag, pckt); @@ -21,6 +22,8 @@ EthInterface *ethintf_new(EthIODef * io) { EthInterface * ethIntf = (EthInterface *)dynmem_alloc(sizeof(EthInterface)); ASSERT_NULL(ethIntf); memset(ðIntf->sieve.layer0, 0, sizeof(PcktSieveLayer)); + ethIntf->sieve.intf = ethIntf; + ethIntf->sieve.layer0.connBReportFn = ethernet_print_report; ethIntf->ioDef = io; ethIntf->ip = 0; ethIntf->arpc = arpc_new(ethIntf, ETHLIB_ARPCACHE_SIZE); @@ -33,7 +36,7 @@ EthInterface *ethintf_new(EthIODef * io) { } void ethinf_receive(EthInterface *intf, const RawPckt *rawPckt) { - packsieve_input(&intf->sieve, rawPckt, intf); + packsieve_input(&intf->sieve, rawPckt); } void ethinf_transmit(EthInterface *intf, const RawPckt *rawPckt) { diff --git a/global_state.c b/global_state.c index 3eb5e84..3365c2a 100644 --- a/global_state.c +++ b/global_state.c @@ -8,6 +8,7 @@ #include "prefab/packet_parsers/icmp_packet.h" #include "etherlib/prefab/packet_parsers/igmp_packet.h" #include "etherlib/prefab/packet_parsers/tcp_segment.h" +#include "utils.h" EthState gEthState; @@ -73,8 +74,21 @@ static void register_packet_parsers() { void ethlib_init() { dynmem_init(); // initialize dynamic memory subsystem E.tmr = timer_new(ETHLIB_TMR_SCHED_TABLE_SIZE); // create timer + E.cbdt = cbdt_new(ETHLIB_CBD_TABLE_SIZE); // create a new connection block descriptor table E.pcktReg = packreg_new(); // create new packet registry register_packet_parsers(); // register packet parsers //E.ethIntf = ethintf_new(); // create new Ethernet interface -} \ No newline at end of file +} + +void close_connection(cbd d) { + ConnBlock connBlock; + if (!cbdt_get_connection_block(E.cbdt, d, &connBlock)) { + ERROR("Invalid CBD descriptor: '%d'!\n", d); + return; + } + + cbdt_release(E.cbdt, d); // release from CBD table + + connb_remove(&connBlock); // remove connection block +} diff --git a/global_state.h b/global_state.h index 9cdd3d9..c4e8209 100644 --- a/global_state.h +++ b/global_state.h @@ -6,6 +6,7 @@ #include "packet_sieve.h" #include "eth_interface.h" #include "timer.h" +#include "cbd_table.h" /** * Global EtherLib state. @@ -15,6 +16,7 @@ typedef struct { Timer * tmr; ///< Timer for internal schedule. PcktRegistry * pcktReg; ///< Packet registry EthInterface * ethIntf; ///< Array of Ethernet interfaces + CbdTable * cbdt; ///< Connection block descriptor table } EthState; extern EthState gEthState; @@ -27,4 +29,10 @@ void ethlib_init(); void process_raw_packet(const uint8_t * data, uint32_t size); +/** + * Close an existing connection (UDP, TCP etc.) + * @param d connection block descriptor + */ +void close_connection(cbd d); + #endif //ETHERLIB_GLOBAL_STATE_H diff --git a/memory_pool.c b/memory_pool.c index 91f3c52..ea41c90 100644 --- a/memory_pool.c +++ b/memory_pool.c @@ -30,6 +30,32 @@ MP *mp_init(uint8_t *p, uint32_t size) { return pool; } +// Break allocation table into two halves and insert BELOW the given position an empty record. +// Also maintain global free space counter. +static void mp_break_alloc_table(MP *mp, MPAllocRecord * ar) { + // shift the registry below the given block + MPAllocRecord *rec = mp->blockRegistry - (mp->blockRecCnt); // bottom of the FILLED registry (there's one unfilled entry below this point, the new record) + while (rec != ar) { + *(rec - 1) = *(rec); + rec++; + } + + // decrease last free block (adjacent to block registry) size with the increase in the block registry + mp->blockRegistry[0].size -= sizeof(MPAllocRecord); + + // decrease free size with the increase in the registry size + mp->freeSpace -= sizeof(MPAllocRecord); + + // increase record count + mp->blockRecCnt++; +} + +// check that allocation table can grow downwards +static bool mp_can_alloc_table_grow(const MP * mp) { + // examine, that an allocated block was not blocking registry table downward growth + return mp->blockRegistry[0].type == MPRT_FREE; +} + uint8_t *mp_alloc(MP *mp, uint32_t size) { // make the allocation from the beginning of the smallest suitable (large enough) // contiguous block @@ -54,22 +80,19 @@ uint8_t *mp_alloc(MP *mp, uint32_t size) { } // allocate block - uint8_t * ptr = NULL; + uint8_t *ptr = NULL; if (leastBadness == 0) { // just change block registry class if block perfectly fits bestBlock->type = MPRT_ALLOCATED; ptr = bestBlock->addrStart; } else { // if there are some bytes left between allocated blocks + // examine, that an allocated block was not blocking registry table downward growth - if (mp->blockRegistry[0].type != MPRT_FREE) { // if cannot allocate, return NULL + if (!mp_can_alloc_table_grow(mp)) { return NULL; } - // shift the registry below best block - MPAllocRecord *rec = mp->blockRegistry - (mp->blockRecCnt); // bottom of the FILLED registry (there's one unfilled entry below this point, the new record) - while (rec != bestBlock) { - *(rec - 1) = *(rec); - rec++; - } + // break the allocation table, shift the registry below best block + mp_break_alloc_table(mp, bestBlock); // store information on allocated MPAllocRecord *allocated = bestBlock - 1; // don't copy best block! @@ -81,15 +104,6 @@ uint8_t *mp_alloc(MP *mp, uint32_t size) { // shrink the remaining free block size bestBlock->size -= size; bestBlock->addrStart += size; - - // decrease last free block (adjacent to block registry) size with the increase in the block registry - mp->blockRegistry[0].size -= sizeof(MPAllocRecord); - - // decrease free size with the increase in the registry size - mp->freeSpace -= sizeof(MPAllocRecord); - - // increase record count - mp->blockRecCnt++; } // decrease free space size with the allocated block size @@ -172,7 +186,7 @@ uint32_t mp_largest_free_block_size(MP *mp) { return largestFreeSize; } -void mp_foreach_block(MP * mp, MPForeachFn * fn, void * userData, bool inclFree) { +void mp_foreach_block(MP *mp, MPForeachFn *fn, void *userData, bool inclFree) { MPAllocRecord *recIter = mp->blockRegistry; while (recIter->type != MPRT_SENTRY) { if (recIter->type != MPRT_FREE || inclFree) { @@ -180,4 +194,69 @@ void mp_foreach_block(MP * mp, MPForeachFn * fn, void * userData, bool inclFree) } recIter--; } +} + +MPAllocRecord *mp_get_block_by_start_addr(MP *mp, const uint8_t *startAddr) { + MPAllocRecord *recIter = mp->blockRegistry, *rec = NULL; + while (recIter->type != MPRT_SENTRY) { + if (recIter->type == MPRT_ALLOCATED && recIter->addrStart == startAddr) { + rec = recIter; + } + recIter--; + } + return rec; +} + +bool mp_shrink_block(MP *mp, uint8_t *beginAddr, uint32_t size, MPShrinkStrategy strategy) { + // verify that it is possible to shrink at all + if (!mp_can_alloc_table_grow(mp)) { + return false; + } + + // check that size is divisible by 4 + if (size & 0b11) { + MSG("Shrinking is not possible but only by size divisible by 4. (So that 32-bits alignments are maintained.)\n"); + return false; + } + + // get the block we want to shrink + MPAllocRecord * block = mp_get_block_by_start_addr(mp, beginAddr); + if (block == NULL) { // no block found, cannot shrink + return false; + } + + // ...block found... + + // It's certain, that (block + 1) is still in the allocation table, since, at least + // a single FREE block is at the top of the table. (see: mp_can_alloc_table_grow()) + MPAllocRecord * splitBelow = (strategy == MP_SHRINK_BEGIN_FIXED) ? (block + 1) : (block); + MPAllocRecord * newRec = splitBelow - 1; + + // break up the table + mp_break_alloc_table(mp, splitBelow); + + // Beginning remains fixed: ]------| -> ]----|--| + if (strategy == MP_SHRINK_BEGIN_FIXED) { + // shrink old block size + block->size -= size; + + // fill new block properties + newRec->size = size; + newRec->type = MPRT_FREE; + newRec->addrStart = block->addrStart + block->size; + } else { // End remains fixed: |-------[ -> |---|----[ + // fill new block properties + newRec->size = size; + newRec->type = MPRT_FREE; + newRec->addrStart = block->addrStart; // copy the start address + + // shrink old block and move it's beginning + block->size -= size; + block->addrStart += size; + } + + // also release size from the global counter + mp->freeSpace += size; + + return true; } \ No newline at end of file diff --git a/memory_pool.h b/memory_pool.h index 35e0332..0cc3786 100644 --- a/memory_pool.h +++ b/memory_pool.h @@ -4,6 +4,7 @@ #include #include +#include /** * Record class @@ -34,6 +35,15 @@ typedef struct { uint32_t freeSpace; ///< Free memory size. } MP; +/** + * The ways of allocated block shrinking may be carried out. + */ +typedef enum { + MP_SHRINK_BEGIN_FIXED, + MP_SHRINK_END_FIXED, + // TODO: absolute shrinking +} MPShrinkStrategy; + /** * Initialize a new memory pool. MP object is allocated at the beginning of the given * area (p). @@ -94,4 +104,21 @@ typedef void (MPForeachFn)(MP * mp, const MPAllocRecord * rec, void * userData); */ void mp_foreach_block(MP * mp, MPForeachFn * fn, void * userData, bool inclFree); +/** + * Get allocation record by starting address. + * @param mp pointer to memory pool object + * @param startAddr start address of memory block + * @return pointer to allocation record if found OR NULL + */ +MPAllocRecord *mp_get_block_by_start_addr(MP *mp, const uint8_t *startAddr); + +/** + * Shrink block by given size. + * @param mp pointer to Memory Pool object + * @param beginAddr begin address of block + * @param size size decrement + * @param strategy shrinking strategy + */ +bool mp_shrink_block(MP *mp, uint8_t *beginAddr, uint32_t size, MPShrinkStrategy strategy); + #endif //ETHERLIB_MEMORY_POOL_H diff --git a/packet.h b/packet.h index 7a5cb40..beacbff 100644 --- a/packet.h +++ b/packet.h @@ -25,7 +25,7 @@ struct PcktHeaderElement_; typedef struct { uint64_t time_s; ///< Timestamp seconds part. uint32_t time_ns; ///< Timestamp nanoseconds part. - const struct PcktHeaderElement_ * header; ///< Pointer to packet header. Points to the innermost header + struct PcktHeaderElement_ * header; ///< Pointer to packet header. Points to the innermost header uint8_t * payload; ///< Pointer to (innermost) payload. uint16_t headerSize; ///< Packet header size in bytes. uint16_t payloadSize; ///< Payload size in bytes. diff --git a/packet_sieve.c b/packet_sieve.c index 64ea2db..ea0b74b 100644 --- a/packet_sieve.c +++ b/packet_sieve.c @@ -16,14 +16,15 @@ void packfiltcond_zero(PcktSieveFilterCondition *cond) { memset(cond, 0, sizeof(PcktSieveFilterCondition)); } -PcktSieve *packsieve_new() { +PcktSieve *packsieve_new(EthInterface * intf) { PcktSieve *sieve = (PcktSieve *) dynmem_alloc(sizeof(PcktSieve)); ASSERT_NULL(sieve); memset(&sieve->layer0, 0, sizeof(PcktSieveLayer)); // clear layer0 data + sieve->intf = intf; return sieve; } -void packsieve_input(const PcktSieve *sieve, const RawPckt *rawPckt, struct EthInterface_ *intf) { +void packsieve_input(const PcktSieve *sieve, const RawPckt *rawPckt) { // extract fields uint8_t *data = rawPckt->payload; uint32_t size = rawPckt->size; @@ -55,7 +56,7 @@ void packsieve_input(const PcktSieve *sieve, const RawPckt *rawPckt, struct EthI } // call parsing function - cdesc->procFun(data + offset, size - offset, header, intf); + cdesc->procFun(data + offset, size - offset, header, sieve->intf); uint16_t containedClass = header->props.containedPacketClass; if (containedClass != 0) { containerClass = ownClass; @@ -88,7 +89,7 @@ void packsieve_input(const PcktSieve *sieve, const RawPckt *rawPckt, struct EthI const PcktSieveLayer *nodeIter = layer->nodes; found = false; while (nodeIter && !found) { - found |= nodeIter->matchAny || nodeIter->filtFn(&nodeIter->filtCond, &headerIter->props, &headerIter->next->props, intf); // specific or general match + found |= nodeIter->matchAny || nodeIter->filtFn(&nodeIter->filtCond, &headerIter->props, &headerIter->next->props, sieve->intf); // specific or general match if (found) { layer = nodeIter; // advance in the sieve tree const PcktHeaderElement *containedHeader = headerIter->next; // advance on headers @@ -102,7 +103,7 @@ void packsieve_input(const PcktSieve *sieve, const RawPckt *rawPckt, struct EthI } headerIter = containedHeader; } else { - nodeIter = nodeIter->next; // advance on linked list and countinue search + nodeIter = nodeIter->next; // advance on linked list and continue search } } } @@ -170,7 +171,7 @@ PcktSieveLayer *packsieve_new_layer(PcktSieveLayer *parent, const PcktSieveFilte layer->filtFn = filtFn; layer->cbFn = cbFn; layer->tag = tag; - layer->infoTag[0] = '\0'; + layer->connBReportFn = NULL; return layer; } } @@ -210,19 +211,38 @@ bool packsieve_remove_layer(PcktSieveLayer *layer) { #define ETH_SIEVE_LAYER_INDENT_PER_LEVEL (4) -void packsieve_report(const PcktSieveLayer *layer, uint32_t indent) { - if (*layer->infoTag != '\0') { - INFO("%*c└─┤%s├───\n", indent, ' ', layer->infoTag); +void packsieve_report(const PcktSieve * sieve, const PcktSieveLayer *layer, uint32_t indent) { + if (layer->connBReportFn != NULL) { + INFO("%*c└─┤", indent, ' '); + const ConnBlock connBlock = { sieve->intf, layer, NULL }; // tag not required + layer->connBReportFn(&connBlock); + INFO("├───\n"); } else { INFO("%*c└─┤%d├───\n", indent, ' ', layer->packetClass); } const PcktSieveLayer *nodeIter = layer->nodes; while (nodeIter) { - packsieve_report(nodeIter, indent + ETH_SIEVE_LAYER_INDENT_PER_LEVEL); + packsieve_report(sieve, nodeIter, indent + ETH_SIEVE_LAYER_INDENT_PER_LEVEL); nodeIter = nodeIter->next; } } +void packsieve_layer_info(const PcktSieve * sieve, const PcktSieveLayer * layer) { + const PcktSieveLayer *iter = layer; + while (iter != NULL) { // climb up to the top in the sieve tree + if (iter->connBReportFn != NULL) { + const ConnBlock connBlock = {sieve->intf, iter, NULL}; // tag not required + iter->connBReportFn(&connBlock); + } else { + INFO("[%d]", layer->packetClass); + } + if (iter->parent != NULL) { + INFO(", "); + } + iter = iter->parent; + } +} + void pckthdr_chain_free(PcktHeaderElement *hdr) { // rewind while (hdr->prev != NULL) { diff --git a/packet_sieve.h b/packet_sieve.h index afaf6aa..a7c7687 100644 --- a/packet_sieve.h +++ b/packet_sieve.h @@ -69,6 +69,13 @@ typedef union { */ typedef int (*SieveCallBackFn)(const Pckt * pckt, PcktSieveLayerTag tag); +/** + * Callback function for printing connblock report. + */ +struct ConnBlock_; +typedef void (*ConnBlockReportFn)(const struct ConnBlock_ * connBlock); + + #define PCKT_SIEVE_INFOTAG_LEN (24) /** @@ -84,14 +91,15 @@ typedef struct PcktSieveLayer_ { struct PcktSieveLayer_ * parent; ///< Pointer to parent node in the sieve tree struct PcktSieveLayer_ * next, * prev; ///< Next and previous sieve layer on the same level struct PcktSieveLayer_ * nodes; ///< Subnodes in the sieve tree - char infoTag[PCKT_SIEVE_INFOTAG_LEN]; ///< Info tag length + ConnBlockReportFn connBReportFn; ///< Connection block report function pointer } PcktSieveLayer; /** * Packet sieve class. */ -typedef struct { +typedef struct PcktSieve_ { PcktSieveLayer layer0; ///< Top of sieve tree + struct EthInterface_ * intf; ///< Ethernet interface } PcktSieve; /** @@ -106,7 +114,7 @@ PcktSieve * packsieve_new(); * @param data * @param size */ -void packsieve_input(const PcktSieve *sieve, const RawPckt *rawPckt, struct EthInterface_ *intf); +void packsieve_input(const PcktSieve *sieve, const RawPckt *rawPckt); /** * Create a new sieve filter layer. @@ -128,10 +136,20 @@ PcktSieveLayer *packsieve_new_layer(PcktSieveLayer *parent, const PcktSieveFilte bool packsieve_remove_layer(PcktSieveLayer * layer); /** - * Display layer on terminal. + * Recursively report layer structure on terminal. + * @param sieve pointer to packet sieve structure * @param layer pointer to existing layer * @param indent sub-tree indentation */ -void packsieve_report(const PcktSieveLayer *layer, uint32_t indent); +void packsieve_report(const PcktSieve * sieve, const PcktSieveLayer *layer, uint32_t indent); + +#define packsieve_report_full(sieve) packsieve_report((sieve), &(sieve)->layer0, 0) + +/** + * Report info on given layer and its parents. + * @param sieve pointer to packet sieve structure + * @param layer pointer to an existing layer + */ +void packsieve_layer_info(const PcktSieve * sieve, const PcktSieveLayer * layer); #endif //ETHERLIB_PACKET_SIEVE_H diff --git a/pckt_assembler.c b/pckt_assembler.c index 28f9c30..91e76a4 100644 --- a/pckt_assembler.c +++ b/pckt_assembler.c @@ -10,18 +10,18 @@ #include "global_state.h" #include "utils.h" -int pckt_assemble(RawPckt *raw, const Pckt *cooked) { +int pckt_assemble(RawPckt *raw, Pckt *cooked) { // calculate frame size uint16_t frameSize = 0; uint16_t headerSize = 0; - const PcktHeaderElement *hdrIter = cooked->header; + PcktHeaderElement *hdrIter = cooked->header; // rewind headers while (hdrIter->prev != NULL) { hdrIter = hdrIter->prev; } - const PcktHeaderElement *lastHdr = hdrIter; + PcktHeaderElement *lastHdr = hdrIter; while (hdrIter != NULL) { headerSize += hdrIter->props.headerSize; lastHdr = hdrIter; @@ -44,18 +44,28 @@ int pckt_assemble(RawPckt *raw, const Pckt *cooked) { uint8_t * headerInsPtr = payloadInsPtr - lastHdr->props.headerSize; // insert payload - memcpy(payloadInsPtr, cooked->payload, cooked->payloadSize); + if (cooked->payloadSize > 0 && cooked->payload != NULL) { + memcpy(payloadInsPtr, cooked->payload, cooked->payloadSize); + } // insert zero padding memset(payloadInsPtr + cooked->payloadSize, 0, padding); // insert reversely headers (e.g.: UDP -> IPv4 -> Ethernet) hdrIter = lastHdr; + if (lastHdr != NULL) { + lastHdr->props.bytesToEnd = cooked->payloadSize; + } + while (hdrIter != NULL) { hdrIter->props.hdrInsFn(headerInsPtr, hdrIter); // insert header hdrIter = hdrIter->prev; // step to previous header if (hdrIter != NULL) { headerInsPtr -= hdrIter->props.headerSize; // advance data pointer + + // store bytes left until the packet end + PcktHeaderElement * next = hdrIter->next; + hdrIter->props.bytesToEnd = next->props.bytesToEnd + next->props.headerSize; } } diff --git a/pckt_assembler.h b/pckt_assembler.h index 78852b0..7d97ea8 100644 --- a/pckt_assembler.h +++ b/pckt_assembler.h @@ -13,6 +13,6 @@ struct EthInterface_; * @param cooked packet information and headers * @return 0 on success OR -1 on failure */ -int pckt_assemble(RawPckt *raw, const Pckt *cooked); +int pckt_assemble(RawPckt *raw, Pckt *cooked); #endif //ETHERLIB_TEST_PCKT_ASSEMBLER_H diff --git a/prefab/conn_blocks/arp_connblock.c b/prefab/conn_blocks/arp_connblock.c index 8d01b7b..8d995d8 100644 --- a/prefab/conn_blocks/arp_connblock.c +++ b/prefab/conn_blocks/arp_connblock.c @@ -24,9 +24,8 @@ ConnBlock arp_new_connblock(EthInterface * intf, SieveCallBackFn cb) { arpConnB.sieveLayer = packsieve_new_layer(&intf->sieve.layer0, &filtCond, false, filtArp, cb, tag, ETH_ARP_PACKET_CLASS); ASSERT_NULL(arpConnB.sieveLayer); - arpConnB.intf = intf; - - SNPRINTF(arpConnB.sieveLayer->infoTag, PCKT_SIEVE_INFOTAG_LEN, "ARP"); + arpConnB.sieve = &intf->sieve; + arpConnB.sieveLayer->connBReportFn = arp_print_report; return arpConnB; } @@ -50,7 +49,7 @@ void arp_send(const ConnBlock * connBlock, const ArpProps * props) { // Ethernet ethProps->length_type = ETH_ARP_PACKET_CLASS; memset(ethProps->destAddr, 0xFF, ETH_HW_ADDR_LEN); // broadcast destination - memcpy(ethProps->sourceAddr, connBlock->intf->mac, ETH_HW_ADDR_LEN); // source + memcpy(ethProps->sourceAddr, connBlock->sieve->intf->mac, ETH_HW_ADDR_LEN); // source // allocate transmit buffer uint32_t txHeaderSize = ETH_ETHERNET_HEADER_SIZE + ETH_ARP_HEADER_SIZE; @@ -77,8 +76,12 @@ void arp_send(const ConnBlock * connBlock, const ArpProps * props) { rpckt.payload = txBuf; rpckt.time_s = 0; rpckt.time_ns = 0; - ethinf_transmit(connBlock->intf, &rpckt); + ethinf_transmit(connBlock->sieve->intf, &rpckt); // release transmit buffer dynmem_free(txBuf); -} \ No newline at end of file +} + +void arp_print_report(const ConnBlock *connBlock) { + INFO("ARP"); +} diff --git a/prefab/conn_blocks/arp_connblock.h b/prefab/conn_blocks/arp_connblock.h index d73da3d..2d06a07 100644 --- a/prefab/conn_blocks/arp_connblock.h +++ b/prefab/conn_blocks/arp_connblock.h @@ -16,4 +16,15 @@ */ ConnBlock arp_new_connblock(EthInterface * intf, SieveCallBackFn cb); -void arp_send(const ConnBlock * connBlock, const ArpProps * props); \ No newline at end of file +/** + * Send ARP packet. + * @param connBlock ARP connection block + * @param props packet data (properties) + */ +void arp_send(const ConnBlock * connBlock, const ArpProps * props); + +/** + * Print ARP connection block report. + * @param connBlock ARP connblock + */ +void arp_print_report(const ConnBlock* connBlock); \ No newline at end of file diff --git a/prefab/conn_blocks/ethernet_info.c b/prefab/conn_blocks/ethernet_info.c new file mode 100644 index 0000000..50d7fbc --- /dev/null +++ b/prefab/conn_blocks/ethernet_info.c @@ -0,0 +1,12 @@ +// +// Created by epagris on 2023.01.30.. +// + +#include "ethernet_info.h" +#include "../../utils.h" + +#include "../../eth_interface.h" + +void ethernet_print_report(const ConnBlock *connBlock) { + PRINT_HWADDR(connBlock->sieve->intf->mac); +} diff --git a/prefab/conn_blocks/ethernet_info.h b/prefab/conn_blocks/ethernet_info.h new file mode 100644 index 0000000..ac8117a --- /dev/null +++ b/prefab/conn_blocks/ethernet_info.h @@ -0,0 +1,13 @@ +#ifndef ETHERLIB_TEST_ETHERNET_INFO_H +#define ETHERLIB_TEST_ETHERNET_INFO_H + + +#include "etherlib/connection_block.h" + +/** + * Print Ethernet sieve layer info. + * @param connBlock associated connection block + */ +void ethernet_print_report(const ConnBlock* connBlock); + +#endif //ETHERLIB_TEST_ETHERNET_INFO_H diff --git a/prefab/conn_blocks/icmp_connblock.c b/prefab/conn_blocks/icmp_connblock.c index f352d37..70e507c 100644 --- a/prefab/conn_blocks/icmp_connblock.c +++ b/prefab/conn_blocks/icmp_connblock.c @@ -71,9 +71,12 @@ ConnBlock icmp_new_connblock(EthInterface * intf) { icmpConnB.sieveLayer = packsieve_new_layer(ipConnB.sieveLayer, &filtCond, false, filtIcmp, icmp_recv_cb, tag, ETH_ICMP_PACKET_CLASS); ASSERT_NULL(icmpConnB.sieveLayer); - icmpConnB.intf = intf; - - SNPRINTF(icmpConnB.sieveLayer->infoTag, PCKT_SIEVE_INFOTAG_LEN, "ICMP (ping)"); + icmpConnB.sieve = &intf->sieve; + icmpConnB.sieveLayer->connBReportFn = icmp_print_report; return icmpConnB; -} \ No newline at end of file +} + +void icmp_print_report(const ConnBlock *connBlock) { + INFO("ICMP (ping)"); +} diff --git a/prefab/conn_blocks/icmp_connblock.h b/prefab/conn_blocks/icmp_connblock.h index f276020..b501a80 100644 --- a/prefab/conn_blocks/icmp_connblock.h +++ b/prefab/conn_blocks/icmp_connblock.h @@ -12,4 +12,10 @@ struct EthInterface_; */ ConnBlock icmp_new_connblock(struct EthInterface_ * intf); +/** + * Print ICMP connblock report. + * @param connBlock ICMP connblock + */ +void icmp_print_report(const ConnBlock* connBlock); + #endif //ETHERLIB_TEST_ICMP_CONNBLOCK_H diff --git a/prefab/conn_blocks/igmp_connblock.c b/prefab/conn_blocks/igmp_connblock.c index d91bcee..f40fe44 100644 --- a/prefab/conn_blocks/igmp_connblock.c +++ b/prefab/conn_blocks/igmp_connblock.c @@ -28,9 +28,8 @@ ConnBlock igmp_new_connblock(struct EthInterface_ *intf) { igmpConnB.sieveLayer = packsieve_new_layer(ipConnB.sieveLayer, &filtCond, false, filtIgmp, NULL, tag, ETH_IGMP_PACKET_CLASS); ASSERT_NULL(igmpConnB.sieveLayer); - igmpConnB.intf = intf; - - SNPRINTF(igmpConnB.sieveLayer->infoTag, PCKT_SIEVE_INFOTAG_LEN, "IGMP"); + igmpConnB.sieve = &intf->sieve; + igmpConnB.sieveLayer->connBReportFn = igmp_print_report; return igmpConnB; } @@ -66,26 +65,11 @@ static void igmp_send(ConnBlock * connBlock, ip4_addr ga, int mt) { // IP layer = layer->parent; - ipProps->IHL = ETH_IPv4_HEADER_SIZE / 4; - ipProps->Version = 4; - ipProps->DSF = 0x00; - ipProps->TotalLength = ETH_IPv4_HEADER_SIZE + ETH_IGMP_HEADER_SIZE; - // Identification is filled on header insertion - ipProps->Flags = 0x00; - ipProps->FragmentOffset = 0x00; - ipProps->TTL = 255; - ipProps->Protocol = ETH_IGMP_PACKET_CLASS; - // HeaderChecksum is calculated on header insertion - ipProps->SourceIPAddr = connBlock->intf->ip; - ipProps->DestIPAddr = ga; - - // common fields for packet assembly - ipProps->hdrInsFn = insert_ipv4_header; - ipProps->headerSize = ETH_IPv4_HEADER_SIZE; + ipv4_fill_props(ipProps, ETH_IGMP_HEADER_SIZE, ETH_IGMP_PACKET_CLASS, connBlock->sieve->intf->ip, ga); // Ethernet ethmc_from_ipmc(ethProps->destAddr, ga); - memcpy(ethProps->sourceAddr, connBlock->intf->mac, ETH_HW_ADDR_LEN); + memcpy(ethProps->sourceAddr, connBlock->sieve->intf->mac, ETH_HW_ADDR_LEN); ethProps->length_type = ETH_IPv4_PACKET_CLASS; // common fields for packet assembly @@ -105,7 +89,7 @@ static void igmp_send(ConnBlock * connBlock, ip4_addr ga, int mt) { RawPckt raw; pckt_assemble(&raw, &cooked); - ethinf_transmit(connBlock->intf, &raw); + ethinf_transmit(connBlock->sieve->intf, &raw); // free headers pckthdr_chain_free(ethHeader); @@ -118,3 +102,7 @@ void igmp_report_membership(ConnBlock * connBlock, ip4_addr ga) { void igmp_leave_group(ConnBlock *connBlock, ip4_addr ga) { igmp_send(connBlock, ga, IGMP_MT_LEAVE_GROUP); } + +void igmp_print_report(const ConnBlock* connBlock) { + INFO("IGMP"); +} \ No newline at end of file diff --git a/prefab/conn_blocks/igmp_connblock.h b/prefab/conn_blocks/igmp_connblock.h index e91d834..fa06ba6 100644 --- a/prefab/conn_blocks/igmp_connblock.h +++ b/prefab/conn_blocks/igmp_connblock.h @@ -27,4 +27,10 @@ void igmp_report_membership(ConnBlock * connBlock, ip4_addr ga); */ void igmp_leave_group(ConnBlock * connBlock, ip4_addr ga); +/** + * Print IGMP connblock report. + * @param connBlock IGMP connblock + */ +void igmp_print_report(const ConnBlock* connBlock); + #endif //ETHERLIB_TEST_IGMP_CONNBLOCK_H diff --git a/prefab/conn_blocks/ipv4_connblock.c b/prefab/conn_blocks/ipv4_connblock.c index 6c5499c..df342c3 100644 --- a/prefab/conn_blocks/ipv4_connblock.c +++ b/prefab/conn_blocks/ipv4_connblock.c @@ -26,18 +26,23 @@ ConnBlock ipv4_new_connblock(EthInterface *intf, ip4_addr ipAddr, SieveCallBackF connb.sieveLayer = packsieve_new_layer(&intf->sieve.layer0, &filtCond, matchAny, filtIPv4, cbFn, tag, ETH_IPv4_PACKET_CLASS); ASSERT_NULL(connb.sieveLayer); - connb.intf = intf; - - if (!matchAny) { - if (ipAddr != IPv4_IF_ADDR) { - SNPRINTF(connb.sieveLayer->infoTag, PCKT_SIEVE_INFOTAG_LEN, "IP: %u.%u.%u.%u", - (ipAddr & 0xFF), ((ipAddr >> 8) & 0xFF), ((ipAddr >> 16) & 0xFF), ((ipAddr >> 24) & 0xFF)); - } else { - SNPRINTF(connb.sieveLayer->infoTag, PCKT_SIEVE_INFOTAG_LEN, "IP: INTF. ADDR."); - } - } else { - SNPRINTF(connb.sieveLayer->infoTag, PCKT_SIEVE_INFOTAG_LEN, "IP: ANY"); - } + connb.sieve = &intf->sieve; + connb.sieveLayer->connBReportFn = ipv4_print_report; return connb; } + +void ipv4_print_report(const ConnBlock* connBlock) { + const PcktSieveLayer * sl = connBlock->sieveLayer; + ip4_addr ipAddr = IP_ADDR_FROM_FILTCOND(&sl->filtCond); + + if (!sl->matchAny) { + if (ipAddr == IPv4_IF_ADDR) { + ipAddr = connBlock->sieve->intf->ip; + } + INFO("IP: %u.%u.%u.%u", (ipAddr & 0xFF), ((ipAddr >> 8) & 0xFF), + ((ipAddr >> 16) & 0xFF), ((ipAddr >> 24) & 0xFF)); + } else { + INFO("IP: ANY"); + } +} \ No newline at end of file diff --git a/prefab/conn_blocks/ipv4_connblock.h b/prefab/conn_blocks/ipv4_connblock.h index 9a670fa..20ca41a 100644 --- a/prefab/conn_blocks/ipv4_connblock.h +++ b/prefab/conn_blocks/ipv4_connblock.h @@ -28,4 +28,10 @@ ConnBlock ipv4_new_connblock(EthInterface * intf, ip4_addr ipAddr, SieveCallBack */ void ipv4_send(EthInterface * intf, ip4_addr addr, const uint8_t * data, uint32_t size, const PcktHeaderElement * headers); +/** + * Print IPv4 connblock report. + * @param connBlock IPv4 connblock + */ +void ipv4_print_report(const ConnBlock* connBlock); + #endif //ETHERLIB_IPV4_CONNBLOCK_H diff --git a/prefab/conn_blocks/tcp/tcp_window.c b/prefab/conn_blocks/tcp/tcp_window.c index a582436..835a438 100644 --- a/prefab/conn_blocks/tcp/tcp_window.c +++ b/prefab/conn_blocks/tcp/tcp_window.c @@ -9,20 +9,20 @@ TcpWindow *tcpw_create(uint32_t size) { uint32_t allocSize = sizeof(TcpWindow) + size; // calculate full allocation size - TcpWindow * win = (TcpWindow *) dynmem_alloc(allocSize); // allocate data block at the end - mp_init(win->data, size); // initialize memory pool + TcpWindow * tcpw = (TcpWindow *) dynmem_alloc(allocSize); // allocate data block at the end + tcpw->mp = mp_init(tcpw->data, size); // initialize memory pool - return win; + return tcpw; } #define TCP_WINDOW_MAX_WINSIZE_PADDING (16) // keep-out zone, this way allocated blocks will not block registry table growth TODO: not the best solution uint32_t tcpw_get_max_window_size(TcpWindow * tcpw) { - return mp_largest_free_block_size(&tcpw->pool) - sizeof(TcpWindowSegment) - TCP_WINDOW_MAX_WINSIZE_PADDING; + return mp_largest_free_block_size(tcpw->mp) - sizeof(TcpWindowSegment) - TCP_WINDOW_MAX_WINSIZE_PADDING; } TcpWindowSegment * tcpw_store_segment(TcpWindow * tcpw, const uint8_t * data, uint32_t size, uint32_t seqNum) { - TcpWindowSegment * seg = (TcpWindowSegment *) mp_alloc(&tcpw->pool, sizeof(TcpWindowSegment) + size); + TcpWindowSegment * seg = (TcpWindowSegment *) mp_alloc(tcpw->mp, sizeof(TcpWindowSegment) + size); if (seg == NULL) { // could not store... return NULL; } @@ -34,19 +34,36 @@ TcpWindowSegment * tcpw_store_segment(TcpWindow * tcpw, const uint8_t * data, ui // store segment memcpy(seg->data, data, size); + // chain-in into linked list of segments + if (tcpw->firstSeg == NULL) { + tcpw->firstSeg = tcpw->lastSeg = seg; + } else { + tcpw->lastSeg->next = seg; + } + + mp_report(tcpw->mp); + return seg; } -static void tcpw_segment_search(MP *mp, const MPAllocRecord * rec, void * userData) { - TcpWindow * tcpw = (TcpWindow *) userData; +bool tcpw_acknowledge(TcpWindow * tcpw, uint32_t ackNum) { + if (tcpw->firstSeg == NULL) { // cannot acknowledge if there's nothing to acknowledge + return false; + } + uint32_t segAckNum = tcpw->firstSeg->seqNum + tcpw->firstSeg->size; + if (segAckNum == ackNum) { // if the acknowledgement number is correct + TcpWindowSegment * oldFirst = tcpw->firstSeg; // replace first + TcpWindowSegment * newFirst = oldFirst->next; + tcpw->firstSeg = newFirst; + if (newFirst == NULL) { // if the list is empty, also clear pointer to last segment + tcpw->lastSeg = NULL; + } + mp_free(tcpw->mp, (uint8_t *) oldFirst); // release old first segment -} + mp_report(tcpw->mp); -uint32_t tcpw_get_acknowledge(TcpWindow * tcpw) { - mp_foreach_block(&tcpw->pool, tcpw_segment_search, tcpw, false); - return 0; -} - -void tcpw_set_sequence_number(TcpWindow * tcpw, uint32_t seqNum) { - tcpw->seqNum = seqNum; + return true; + } else { + return false; + } } diff --git a/prefab/conn_blocks/tcp/tcp_window.h b/prefab/conn_blocks/tcp/tcp_window.h index 8543dd0..dcba1e6 100644 --- a/prefab/conn_blocks/tcp/tcp_window.h +++ b/prefab/conn_blocks/tcp/tcp_window.h @@ -7,20 +7,54 @@ /** * TCP segment descriptor */ -typedef struct { +typedef struct TcpWindowSegment_ { uint32_t seqNum; ///< sequence number (sequence number of the first octet) uint32_t size; ///< segment size + struct TcpWindowSegment_ * next; ///< pointer to next window segment uint8_t data[]; ///< Segment data } TcpWindowSegment; +// TODO TODO TODO... + +/** + * TCP Window + */ typedef struct { - bool ackAvail; ///< acknowledge is available - uint32_t seqNum; ///< Sequence number - uint32_t lastAcked; ///< last acknowledged byte - MP pool; ///< Pool for segment management + TcpWindowSegment * firstSeg, *lastSeg; ///< Linked list ends of sequential segments + MP *mp; ///< Memory Pool for segment management uint8_t data[]; ///< Window data storage } TcpWindow; +/** + * Create new TCP Window object. + * @param size + * @return pointer to newly created TCP Window OR NULL on failure + */ TcpWindow * tcpw_create(uint32_t size); +/** + * Get maximum allocable TCP window size. (Cannot store bigger incoming contiguous data blocks.) + * @param tcpw pointer to TCP Window object + * @return maximum size of TCP segment window + */ +uint32_t tcpw_get_max_window_size(TcpWindow * tcpw); + +/** + * Store TCP segment + * @param tcpw pointer to TCP Window object + * @param data data to store + * @param size data size in bytes + * @param seqNum sequence number of first octet of the block + * @return pointer to TcpWindowSegment allocated in the window OR NULL on failure + */ +TcpWindowSegment * tcpw_store_segment(TcpWindow * tcpw, const uint8_t * data, uint32_t size, uint32_t seqNum); + +/** + * Acknowledge segment. + * @param tcpw pointer to TCP Window structure + * @param ackNum acknowledgement number + * @return acknowledge success + */ +bool tcpw_acknowledge(TcpWindow * tcpw, uint32_t ackNum); + #endif //ETHERLIB_TEST_TCP_WINDOW_H diff --git a/prefab/conn_blocks/tcp_connblock.c b/prefab/conn_blocks/tcp_connblock.c index f076dad..517c255 100644 --- a/prefab/conn_blocks/tcp_connblock.c +++ b/prefab/conn_blocks/tcp_connblock.c @@ -12,12 +12,21 @@ #include "etherlib/prefab/packet_parsers/tcp_segment.h" #include "etherlib/gen_queue.h" #include "etherlib_options.h" +#include "etherlib/prefab/conn_blocks/tcp/tcp_window.h" +#include "etherlib/global_state.h" + +static char *TCP_STATE_NAMES[] = { + "CLOSED", "LISTEN", "SYN_RCVD", "SYN_SENT", "ESTAB", "FIN_WAIT_1", + "FIN_WAIT_2", "CLOSE_WAIT", "CLOSING", "LAST_ACK", "TIME_WAIT" +}; + +int tcp_send_segment(const struct ConnBlock_ *connBlock, TcpFlag flags, TcpOption *opts, const uint8_t *data, uint32_t size); static bool filtTcp(const PcktSieveFilterCondition *filtCond, const PcktProps *contProps, const PcktProps *ownProps, EthInterface *intf) { IPv4Props *ipProps = (IPv4Props *) contProps; TcpProps *tcpProps = (TcpProps *) ownProps; - return ipProps->Protocol == ETH_UDP_PACKET_CLASS && (TCP_PORT_FROM_FILTCOND(filtCond) == tcpProps->DestinationPort); + return ipProps->Protocol == ETH_TCP_PACKET_CLASS && (TCP_PORT_FROM_FILTCOND(filtCond) == tcpProps->DestinationPort); } /** @@ -27,39 +36,180 @@ typedef struct { uint16_t localPort; uint16_t remotePort; ip4_addr remoteAddr; - uint32_t sequenceNumber; - uint32_t ackNumber; + TcpConnectionState connState; + uint16_t localMSS; + uint16_t remoteMSS; + uint32_t sequenceNumber; // seq. num of next transmitted octet + uint32_t ackNumber; // ack. number sent to the remote side (seq. num of next expected octet) uint16_t window; - uint8_t * txWindow; - uint8_t * rxWindow; - Queue * txQueue; - Queue * rxQueue; + TcpWindow * txWin; + ConnBlock connBlock; + bool debug; + SieveCallBackFn userCb; } TcpState; +#define TCP_DEFAULT_MSS (1200) + void tcps_init(TcpState *tcps, uint16_t localPort) { tcps->sequenceNumber = (uint32_t) rand(); tcps->localPort = localPort; + tcps->localMSS = TCP_DEFAULT_MSS; tcps->remotePort = 0; tcps->remoteAddr = 0; tcps->ackNumber = 0; - tcps->window = 0; - tcps->txWindow = dynmem_alloc(ETHLIB_DEF_TCP_WINDOW_SIZE); - tcps->rxWindow = dynmem_alloc(ETHLIB_DEF_TCP_WINDOW_SIZE); - /*tcps->txQueue = Q_CREATE_T(ETHLIB_DEF_TCP_QUEUE_SIZE, TcpQueueTicker); - tcps->rxQueue = Q_CREATE_T(ETHLIB_DEF_TCP_QUEUE_SIZE, TcpQueueTicker);*/ + tcps->window = ETHLIB_DEF_TCP_WINDOW_SIZE; + tcps->txWin = tcpw_create(ETHLIB_DEF_TCP_WINDOW_SIZE); + //tcps->rxWin = tcpw_create(ETHLIB_DEF_TCP_WINDOW_SIZE); + tcps->debug = false; + tcps->userCb = NULL; } -void tcp_bind(ConnBlock * connBlock, ip4_addr remoteAddr, uint16_t remotePort) { - TcpState * tcps = (TcpState *) connBlock->tag; +#define TCP_FETCH_STATE_FROM_CONNBLOCK(connBlock) ((TcpState *) (connBlock)->sieveLayer->tag.p) + +void tcp_init_connection(ConnBlock *connBlock); + +static void retry_to_connect_cb(Timer * timer, AlarmUserData user) { + ConnBlock * connBlock = (ConnBlock *) user.ptr; + tcp_init_connection(connBlock); +} + +int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) { + TcpState *tcps = (TcpState *) tag.p; // pointer to state is stored in sieve layer tag + TcpProps *tcpProps = HEADER_FETCH_PROPS(TcpProps, pckt->header); // fetch header + + TcpConnectionState beginState = tcps->connState; + + IPv4Props * ipProps = HEADER_FETCH_PROPS(IPv4Props, pckt->header->prev); + uint16_t dataSize = ipProps->TotalLength - tcpProps->DataOffset - ETH_IPv4_HEADER_SIZE; // extract data size + + // process incoming packet + switch (tcps->connState) { + case TCP_STATE_SYN_SENT: { + if (!(tcpProps->Flags & TCP_FLAG_ACK)) { + // TODO: if not acknowledged + } + if (tcpProps->Flags & TCP_FLAG_SYN) { + tcps->ackNumber = tcpProps->SequenceNumber + 1; + } + + // get MSS + TcpOption *mss = tcp_get_option_by_kind(tcpProps->options, TCP_OPT_KIND_MSS); + if (mss == NULL) { + tcps->connState = TCP_STATE_CLOSED; // TODO: not this way... + AlarmUserData alarmData; + alarmData.ptr = &tcps->connBlock; + timer_sched_rel(E.tmr, 1000000, retry_to_connect_cb, alarmData); + break; + } + + FETCH_WORD_H2N(&tcps->remoteMSS, mss->value); + + // send ACK + int flags = TCP_FLAG_ACK; + tcps->sequenceNumber++; // advance sequence number // TODO... + tcp_send_segment(&tcps->connBlock, flags, NULL, NULL, 0); + + // step into next state + tcps->connState = TCP_STATE_ESTAB; + break; + } + case TCP_STATE_ESTAB: + // if the other end tries to close down the connection + if (tcpProps->Flags & TCP_FLAG_FIN) { + // send FIN, ACK + int flags = TCP_FLAG_FIN | TCP_FLAG_ACK; + tcps->ackNumber++; // advance acknowledgement number // TODO... + tcp_send_segment(&tcps->connBlock, flags, NULL, NULL, 0); + + // step into next state + tcps->connState = TCP_STATE_LAST_ACK; + } else if ((dataSize > 0) && (tcps->ackNumber == tcpProps->SequenceNumber)) { // incoming data segment with integrity in ack/seq + // send acknowledge + tcps->ackNumber += dataSize; + tcp_send_segment(&tcps->connBlock, TCP_FLAG_ACK, NULL, NULL, 0); + + // process data + if (tcps->userCb != NULL) { + PcktSieveLayerTag userTag = { 0 }; // TODO... + tcps->userCb(pckt, userTag); + } + } else if ((dataSize == 0) && (tcps->sequenceNumber == tcpProps->AcknowledgementNumber)) { // outgoing segment was acknowledged by peer + // release segment from retransmit window + tcpw_acknowledge(tcps->txWin, tcpProps->AcknowledgementNumber); + } + break; + case TCP_STATE_LAST_ACK: + if (tcpProps->Flags & TCP_FLAG_ACK) { + tcps->connState = TCP_STATE_CLOSED; + } + break; + case TCP_STATE_CLOSED: + default: + break; + } + + // print state + if (tcps->debug && (beginState != tcps->connState)) { + MSG("%s -> %s\n", TCP_STATE_NAMES[beginState], TCP_STATE_NAMES[tcps->connState]); + } + + return 0; // TODO +} + +void tcp_init_connection(ConnBlock *connBlock) { + /* send SYN with maximum segment size option */ + + // TODO: check that our state is CLOSED + + TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(connBlock); + + // prepare MSS option + TcpOption *mss = tcpopt_new(TCP_OPT_KIND_MSS, 2); + FILL_WORD_H2N(mss->value, tcps->localMSS); + + // prepare flags + int flags = TCP_FLAG_SYN; + + // send segment + tcp_send_segment(connBlock, flags, mss, NULL, 0); + + // release option + dynmem_free(mss); + + // step into SYN SENT state + tcps->connState = TCP_STATE_SYN_SENT; +} + +void tcp_bind(cbd d, ip4_addr remoteAddr, uint16_t remotePort) { + ConnBlock connBlock; + if (!cbdt_get_connection_block(E.cbdt, d, &connBlock)) { + ERROR("Invalid CBD descriptor: '%d'!\n", d); + return; + } + + // store remote endpoint data + TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(&connBlock); tcps->remoteAddr = remoteAddr; tcps->remotePort = remotePort; + + // initialize connection + tcp_init_connection(&connBlock); } -int tcps_receive_data_segment(TcpState * tcps, const Pckt * pckt) { +void tcp_debug(cbd d, bool debug) { + ConnBlock connBlock; + if (!cbdt_get_connection_block(E.cbdt, d, &connBlock)) { + ERROR("Invalid CBD descriptor: '%d'!\n", d); + return; + } + TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(&connBlock); + tcps->debug = debug; } -ConnBlock tcp_new_connblock(EthInterface *intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn) { +// --------------------- + +cbd tcp_new_connblock(EthInterface *intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn) { ConnBlock tcpConnB; ConnBlock ipConnB = ipv4_new_connblock(intf, ipAddr, NULL); // create new IPv4 connection block @@ -68,20 +218,27 @@ ConnBlock tcp_new_connblock(EthInterface *intf, ip4_addr ipAddr, uint16_t port, TCP_PORT_TO_FILTCOND(&filtCond, port); PcktSieveLayerTag tag; // store TCP state into sieve layer's tag - tag.p = dynmem_alloc(sizeof(TcpState)); - memset(tag.p, 0, sizeof(TcpState)); + TcpState *tcpState = dynmem_alloc(sizeof(TcpState)); + tcps_init(tcpState, port); + tcpState->userCb = cbFn; + tag.p = tcpState; - tcpConnB.sieveLayer = packsieve_new_layer(ipConnB.sieveLayer, &filtCond, false, filtTcp, cbFn, tag, ETH_TCP_PACKET_CLASS); + tcpConnB.sieveLayer = packsieve_new_layer(ipConnB.sieveLayer, &filtCond, false, filtTcp, tcp_receive_segment_cb, tag, ETH_TCP_PACKET_CLASS); ASSERT_NULL(tcpConnB.sieveLayer); + tcpConnB.sieveLayer->connBReportFn = tcp_print_report; - tcpConnB.intf = intf; + tcpConnB.sieve = &intf->sieve; + tcpState->connBlock = tcpConnB; // also store connection block parameters - SNPRINTF(tcpConnB.sieveLayer->infoTag, PCKT_SIEVE_INFOTAG_LEN, "TCP port: %d", port); - - return tcpConnB; + // store connection block to CBDT + cbd d = cbdt_alloc_new(E.cbdt, &tcpConnB); + if (d == CBDT_ERR) { // on error free everything we have allocated before + packsieve_remove_layer(tcpConnB.sieveLayer); + } + return d; } -int tcp_send_segment(const struct ConnBlock_ *connBlock, const uint8_t *data, uint32_t size) { +int tcp_send_segment(const struct ConnBlock_ *connBlock, TcpFlag flags, TcpOption *opts, const uint8_t *data, uint32_t size) { // allocate headers PcktHeaderElement *tcpHeader = ALLOC_HEADER_ELEMENT(TcpProps); PcktHeaderElement *ipHeader = ALLOC_HEADER_ELEMENT(IPv4Props); @@ -107,9 +264,85 @@ int tcp_send_segment(const struct ConnBlock_ *connBlock, const uint8_t *data, ui tcpProps->DestinationPort = tcpState->remotePort; tcpProps->SequenceNumber = tcpState->sequenceNumber; tcpProps->AcknowledgementNumber = tcpState->ackNumber; + tcpProps->Flags = flags; tcpProps->Window = tcpState->window; tcpProps->Checksum = 0; tcpProps->UrgentPtr = 0; + tcpProps->options = opts; + + // get options size + uint32_t optSize = tcp_get_options_size(tcpProps->options); + + // common fields for packet assembly + tcpProps->hdrInsFn = insert_tcp_header; + tcpProps->headerSize = ETH_TCP_HEADER_SIZE + optSize; + + // IP + ipv4_fill_props(ipProps, ETH_TCP_HEADER_SIZE + optSize + size, ETH_TCP_PACKET_CLASS, + connBlock->sieve->intf->ip, tcpState->remoteAddr); + + // Ethernet + layer = layer->parent; + if (tcpState->remoteAddr != 0xFFFFFFFF) { + ArpCache *arpc = connBlock->sieve->intf->arpc; + const ArpEntry *entry = arpc_get_ask(arpc, tcpState->remoteAddr); + memcpy(ethProps->destAddr, entry, ETH_HW_ADDR_LEN); + } + memcpy(ethProps->sourceAddr, connBlock->sieve->intf->mac, ETH_HW_ADDR_LEN); + ethProps->length_type = ETH_IPv4_PACKET_CLASS; + + // common fields for packet assembly + ethProps->hdrInsFn = insert_ethernet_header; + ethProps->headerSize = ETH_ETHERNET_HEADER_SIZE; + + // ----------------------------- + + Pckt cooked; + cooked.payload = data; + cooked.payloadSize = size; + cooked.header = ethHeader; + + // NOT FILLED FIELDS + cooked.headerSize = 0; + cooked.time_s = 0; + cooked.time_ns = 0; + + RawPckt raw; + pckt_assemble(&raw, &cooked); + + ethinf_transmit(connBlock->sieve->intf, &raw); + + // free headers + dynmem_free(tcpHeader); + dynmem_free(ipHeader); + dynmem_free(ethHeader); + + // advance TCP sequence number + tcpState->sequenceNumber += size; return 0; } + +uint32_t tcp_send(cbd d, const uint8_t * data, uint32_t size) { + ConnBlock connBlock; + if (!cbdt_get_connection_block(E.cbdt, d, &connBlock)) { + ERROR("Invalid CBD descriptor: '%d'!\n", d); + return 0; + } + + TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(&connBlock); + if (tcps->connState == TCP_STATE_ESTAB) { // can send only data if connection is ESTABLISHED + uint32_t maxWinSize = tcpw_get_max_window_size(tcps->txWin); + uint32_t txSize = MIN(maxWinSize, size); // limit size to the allocable size of the retransmit buffer + TcpWindowSegment * winSeg = tcpw_store_segment(tcps->txWin, data, txSize, tcps->sequenceNumber); // store segment for possible retransmission + tcp_send_segment(&connBlock, TCP_FLAG_ACK, NULL, winSeg->data, winSeg->size); + return txSize; + } else { + return 0; + } +} + +void tcp_print_report(const ConnBlock* connBlock) { + const PcktSieveLayer * sl = connBlock->sieveLayer; + INFO("TCP port: %d", TCP_PORT_FROM_FILTCOND(&sl->filtCond)); +} \ No newline at end of file diff --git a/prefab/conn_blocks/tcp_connblock.h b/prefab/conn_blocks/tcp_connblock.h index 01f44b4..8a3519f 100644 --- a/prefab/conn_blocks/tcp_connblock.h +++ b/prefab/conn_blocks/tcp_connblock.h @@ -7,6 +7,8 @@ #include #include "../packet_parsers/ipv4_types.h" #include "../../connection_block.h" +#include "../packet_parsers/tcp_segment.h" +#include "etherlib/cbd_table.h" struct EthInterface_; @@ -18,14 +20,38 @@ struct EthInterface_; * @param cbFn receive callback function * @return TCP connection block */ -ConnBlock tcp_new_connblock(struct EthInterface_ *intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn); +cbd tcp_new_connblock(struct EthInterface_ *intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn); /** * Bind TCP connection to remote socket. - * @param connBlock pointer to TCP connection block + * @param d pointer to TCP connection block * @param remoteAddr remote socket address * @param remotePort remote socket port */ -void tcp_bind(ConnBlock * connBlock, ip4_addr remoteAddr, uint16_t remotePort); +void tcp_bind(cbd d, ip4_addr remoteAddr, uint16_t remotePort); + +/** + * Send data over an existing TCP connection. + * @param d pointer to existing connblock + * @param data pointer to data to send + * @param size data size in bytes + * @return number of bytes sent + */ +uint32_t tcp_send(unsigned char d, const uint8_t * data, uint32_t size); + +/** + * Turn TCP debugging ON/OFF + * @param d + * @param debug + */ +void tcp_debug(cbd d, bool debug); + +/** + * Print TCP connblock report. + * @param connBlock TCP connblock + */ +void tcp_print_report(const ConnBlock* connBlock); + +//int tcp_send_segment(const struct ConnBlock_ *connBlock, TcpFlag flags, TcpOption * opts, const uint8_t *data, uint32_t size); #endif //ETHERLIB_TEST_TCP_CONNBLOCK_H diff --git a/prefab/conn_blocks/udp_connblock.c b/prefab/conn_blocks/udp_connblock.c index a872511..15cb359 100644 --- a/prefab/conn_blocks/udp_connblock.c +++ b/prefab/conn_blocks/udp_connblock.c @@ -10,15 +10,16 @@ #include "../../utils.h" #include "../../dynmem.h" #include "etherlib/pckt_assembler.h" +#include "etherlib/global_state.h" -static bool filtUdp(const PcktSieveFilterCondition * filtCond, const PcktProps * contProps, const PcktProps * ownProps, EthInterface * intf) { - IPv4Props * ipProps = (IPv4Props *) contProps; - UdpProps * udpProps = (UdpProps *) ownProps; +static bool filtUdp(const PcktSieveFilterCondition *filtCond, const PcktProps *contProps, const PcktProps *ownProps, EthInterface *intf) { + IPv4Props *ipProps = (IPv4Props *) contProps; + UdpProps *udpProps = (UdpProps *) ownProps; return ipProps->Protocol == ETH_UDP_PACKET_CLASS && (UDP_PORT_FROM_FILTCOND(filtCond) == udpProps->DestinationPort); } -ConnBlock udp_new_connblock(EthInterface * intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn) { +cbd udp_new_connblock(EthInterface *intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn) { ConnBlock udpConnB; ConnBlock ipConnB = ipv4_new_connblock(intf, ipAddr, NULL); // create new IPv4 connection block @@ -29,19 +30,29 @@ ConnBlock udp_new_connblock(EthInterface * intf, ip4_addr ipAddr, uint16_t port, tag.u = 0; udpConnB.sieveLayer = packsieve_new_layer(ipConnB.sieveLayer, &filtCond, false, filtUdp, cbFn, tag, ETH_UDP_PACKET_CLASS); ASSERT_NULL(udpConnB.sieveLayer); + udpConnB.sieveLayer->connBReportFn = udp_print_report; - udpConnB.intf = intf; + udpConnB.sieve = &intf->sieve; - SNPRINTF(udpConnB.sieveLayer->infoTag, PCKT_SIEVE_INFOTAG_LEN, "UDP port: %d", port); - - return udpConnB; + // store connection block to CBDT + cbd d = cbdt_alloc_new(E.cbdt, &udpConnB); + if (d == CBDT_ERR) { // on error free everything we have allocated before + packsieve_remove_layer(udpConnB.sieveLayer); + } + return d; } -int udp_sendto(const struct ConnBlock_ * connBlock, const uint8_t *data, uint32_t size, ip4_addr addr, uint16_t port) { +int udp_sendto(cbd d, const uint8_t *data, uint32_t size, ip4_addr addr, uint16_t port) { + ConnBlock connBlock; + if (!cbdt_get_connection_block(E.cbdt, d, &connBlock)) { + ERROR("Invalid CBD descriptor: '%d'!\n", d); + return 0; + } + // allocate headers - PcktHeaderElement * udpHeader = ALLOC_HEADER_ELEMENT(UdpProps); - PcktHeaderElement * ipHeader = ALLOC_HEADER_ELEMENT(IPv4Props); - PcktHeaderElement * ethHeader = ALLOC_HEADER_ELEMENT(EthernetProps); + PcktHeaderElement *udpHeader = ALLOC_HEADER_ELEMENT(UdpProps); + PcktHeaderElement *ipHeader = ALLOC_HEADER_ELEMENT(IPv4Props); + PcktHeaderElement *ethHeader = ALLOC_HEADER_ELEMENT(EthernetProps); udpHeader->next = NULL; udpHeader->prev = ipHeader; ipHeader->next = udpHeader; @@ -50,12 +61,12 @@ int udp_sendto(const struct ConnBlock_ * connBlock, const uint8_t *data, uint32_ ethHeader->prev = NULL; // prepare headers - UdpProps * udpProps = HEADER_FETCH_PROPS(UdpProps, udpHeader); - IPv4Props * ipProps = HEADER_FETCH_PROPS(IPv4Props, ipHeader); - EthernetProps * ethProps = HEADER_FETCH_PROPS(EthernetProps, ethHeader); + UdpProps *udpProps = HEADER_FETCH_PROPS(UdpProps, udpHeader); + IPv4Props *ipProps = HEADER_FETCH_PROPS(IPv4Props, ipHeader); + EthernetProps *ethProps = HEADER_FETCH_PROPS(EthernetProps, ethHeader); // fetch sieve layers and fill transmit headers - PcktSieveLayer * layer = connBlock->sieveLayer; // UDP layer + PcktSieveLayer *layer = connBlock.sieveLayer; // UDP layer udpProps->SourcePort = UDP_PORT_FROM_FILTCOND(&layer->filtCond); udpProps->DestinationPort = port; udpProps->Length = ETH_UDP_HEADER_SIZE + size; @@ -67,33 +78,19 @@ int udp_sendto(const struct ConnBlock_ * connBlock, const uint8_t *data, uint32_ // IP layer = layer->parent; - ipProps->IHL = ETH_IPv4_HEADER_SIZE / 4; - ipProps->Version = 4; - ipProps->DSF = 0x00; - ipProps->TotalLength = ETH_IPv4_HEADER_SIZE + ETH_UDP_HEADER_SIZE + size; - // Identification is filled on header insertion - ipProps->Flags = 0x00; - ipProps->FragmentOffset = 0x00; - ipProps->TTL = 255; - ipProps->Protocol = ETH_UDP_PACKET_CLASS; - // HeaderChecksum is calculated on header insertion - ipProps->SourceIPAddr = connBlock->intf->ip; - ipProps->DestIPAddr = addr; - - // common fields for packet assembly - ipProps->hdrInsFn = insert_ipv4_header; - ipProps->headerSize = ETH_IPv4_HEADER_SIZE; + ipv4_fill_props(ipProps, ETH_UDP_HEADER_SIZE + size, ETH_UDP_PACKET_CLASS, + connBlock.sieve->intf->ip, addr); // Ethernet layer = layer->parent; if (addr != 0xFFFFFFFF) { - ArpCache * arpc = connBlock->intf->arpc; - const ArpEntry * entry = arpc_get_ask(arpc, addr); + ArpCache *arpc = connBlock.sieve->intf->arpc; + const ArpEntry *entry = arpc_get_ask(arpc, addr); memcpy(ethProps->destAddr, entry, ETH_HW_ADDR_LEN); } else { memset(ethProps->destAddr, 0xFF, ETH_HW_ADDR_LEN); } - memcpy(ethProps->sourceAddr, connBlock->intf->mac, ETH_HW_ADDR_LEN); + memcpy(ethProps->sourceAddr, connBlock.sieve->intf->mac, ETH_HW_ADDR_LEN); ethProps->length_type = ETH_IPv4_PACKET_CLASS; // common fields for packet assembly @@ -113,7 +110,7 @@ int udp_sendto(const struct ConnBlock_ * connBlock, const uint8_t *data, uint32_ RawPckt raw; pckt_assemble(&raw, &cooked); - ethinf_transmit(connBlock->intf, &raw); + ethinf_transmit(connBlock.sieve->intf, &raw); // free headers dynmem_free(udpHeader); @@ -123,5 +120,10 @@ int udp_sendto(const struct ConnBlock_ * connBlock, const uint8_t *data, uint32_ // release payload // dynmem_free(raw.payload); - return 0; + return size; // TODO... } + +void udp_print_report(const ConnBlock* connBlock) { + const PcktSieveLayer * sl = connBlock->sieveLayer; + INFO("UDP port: %d", UDP_PORT_FROM_FILTCOND(&sl->filtCond)); +} \ No newline at end of file diff --git a/prefab/conn_blocks/udp_connblock.h b/prefab/conn_blocks/udp_connblock.h index a6c627e..ae47909 100644 --- a/prefab/conn_blocks/udp_connblock.h +++ b/prefab/conn_blocks/udp_connblock.h @@ -5,6 +5,7 @@ #include "../../connection_block.h" #include "../../eth_interface.h" #include "ipv4_connblock.h" +#include "etherlib/cbd_table.h" #define UDP_PORT_FROM_FILTCOND(fc) ((fc)->uw[0]) #define UDP_PORT_TO_FILTCOND(fc,port) (((fc)->uw[0]) = (port)) @@ -17,7 +18,7 @@ * @param cbFn callback function fired when connection block receives data or some event occurs * @return instance of a new connection block */ -ConnBlock udp_new_connblock(EthInterface * intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn); +cbd udp_new_connblock(EthInterface * intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn); /** * UDP transmit callback. @@ -27,6 +28,12 @@ ConnBlock udp_new_connblock(EthInterface * intf, ip4_addr ipAddr, uint16_t port, * @param addr remote address to send datagram to * @param addr remote port */ -int udp_sendto(const struct ConnBlock_ * connBlock, const uint8_t * data, uint32_t size, ip4_addr addr, uint16_t port); +int udp_sendto(cbd connBlock, const uint8_t * data, uint32_t size, ip4_addr addr, uint16_t port); + +/** + * Print UDP connblock report. + * @param connBlock UDP connblock + */ +void udp_print_report(const ConnBlock* connBlock); #endif //ETHERLIB_UDP_CONNBLOCK_H diff --git a/prefab/packet_parsers/dhcp.c b/prefab/packet_parsers/dhcp.c index 0ac468d..351b5b1 100644 --- a/prefab/packet_parsers/dhcp.c +++ b/prefab/packet_parsers/dhcp.c @@ -13,7 +13,7 @@ static struct { DhcpState state; void *buf; - ConnBlock connb; + cbd desc; uint32_t tranId; EthInterface *intf; } s; @@ -154,7 +154,7 @@ static void dhcp_send(const DhcpProps *props, const DhcpOption *opts) { // dhcp_option_insert_end(&buf); // send packet - udp_sendto(&s.connb, s.buf, DHCP_MIN_PACKET_SIZE, IPv4_ANY_ADDR, DHCP_SERVER_PORT); + udp_sendto(s.desc, s.buf, DHCP_MIN_PACKET_SIZE, IPv4_ANY_ADDR, DHCP_SERVER_PORT); } static void dhcp_parse(const uint8_t *buf, DhcpProps *props, DhcpOption **opts) { @@ -331,6 +331,6 @@ void dhcp_start() { void dhcp_initiate(EthInterface *intf) { s.state = DHCP_INIT; s.buf = dynmem_alloc(DHCP_MIN_PACKET_SIZE); - s.connb = udp_new_connblock(intf, IPv4_ANY_ADDR, DHCP_CLIENT_PORT, dhcp_resp_cb); + s.desc = udp_new_connblock(intf, IPv4_ANY_ADDR, DHCP_CLIENT_PORT, dhcp_resp_cb); s.intf = intf; } diff --git a/prefab/packet_parsers/ipv4_packet.c b/prefab/packet_parsers/ipv4_packet.c index a90133c..9d24888 100644 --- a/prefab/packet_parsers/ipv4_packet.c +++ b/prefab/packet_parsers/ipv4_packet.c @@ -11,17 +11,17 @@ #define ETH_IP_HEADER_LENGTH (20) -static bool check_ipv4_validity(const uint8_t * hdr, const IPv4Props *ipProps) { +static bool check_ipv4_validity(const uint8_t *hdr, const IPv4Props *ipProps) { bool valid = (ipProps->Version == 4) && (ipProps->IHL == (ETH_IP_HEADER_LENGTH / 4)) && (chksum(hdr, ETH_IPv4_HEADER_SIZE, false) == 0) && - (ipProps->FragmentOffset == 0) && !(ipProps->Flags & 0x02); // discard if fragmented + (ipProps->FragmentOffset == 0) && !(ipProps->Flags & 0x02 << 4); // TODO: discard if fragmented return valid; } int parse_ipv4(const uint8_t *hdr, uint32_t size, PcktHeaderElement *pcktHdrLe, struct EthInterface_ *intf) { - const uint8_t * hdrBegin = hdr; + const uint8_t *hdrBegin = hdr; IPv4Props *ipProps = HEADER_FETCH_PROPS(IPv4Props, pcktHdrLe); uint8_t version_length; FETCH_ADVANCE(&version_length, hdr, 1); @@ -47,7 +47,7 @@ int parse_ipv4(const uint8_t *hdr, uint32_t size, PcktHeaderElement *pcktHdrLe, // if packet is valid, learn MAC-IP mapping if (ipProps->validityOK) { - EthernetProps * ethProps = HEADER_FETCH_PROPS(EthernetProps, pcktHdrLe->prev); // fetch Ethernet header + EthernetProps *ethProps = HEADER_FETCH_PROPS(EthernetProps, pcktHdrLe->prev); // fetch Ethernet header ArpEntry entry; // fill entry memcpy(&entry.eth, ethProps->sourceAddr, ETH_HW_ADDR_LEN); entry.ip = ipProps->SourceIPAddr; @@ -60,7 +60,7 @@ int parse_ipv4(const uint8_t *hdr, uint32_t size, PcktHeaderElement *pcktHdrLe, static uint16_t nextIpIdentification = 0; void insert_ipv4_header(uint8_t *hdr, const PcktHeaderElement *headers) { - uint8_t * hdrOrig = hdr; + uint8_t *hdrOrig = hdr; IPv4Props *ipProps = (IPv4Props *) &headers->props; ipProps->Identification = nextIpIdentification++; // auto-insert identification uint8_t version_length = (ipProps->Version << 4) | (ipProps->IHL & 0x0F); @@ -72,7 +72,7 @@ void insert_ipv4_header(uint8_t *hdr, const PcktHeaderElement *headers) { FILL_WORD_H2N_ADVANCE(hdr, flags_fragOffset); // TODO... FILL_ADVANCE(hdr, &ipProps->TTL, 1); FILL_ADVANCE(hdr, &ipProps->Protocol, 1); - uint8_t * ChkSumPtr = hdr; + uint8_t *ChkSumPtr = hdr; FILL_WORD_H2N_ADVANCE(hdr, 0x0000); FILL_ADVANCE(hdr, &ipProps->SourceIPAddr, 4); FILL_ADVANCE(hdr, &ipProps->DestIPAddr, 4); @@ -84,8 +84,33 @@ void insert_ipv4_header(uint8_t *hdr, const PcktHeaderElement *headers) { void ethmc_from_ipmc(uint8_t *hwa, ip4_addr ipa) { memcpy(hwa, ETH_ETHERNET_IPMC_HWADDR, ETH_HW_ADDR_LEN); - const uint8_t * ipab = (const uint8_t *) &ipa; + const uint8_t *ipab = (const uint8_t *) &ipa; hwa[3] = (hwa[3] & 0x80) | (ipab[1] & 0x7F); hwa[4] = ipab[2]; hwa[5] = ipab[3]; } + +void ipv4_fill_props(IPv4Props *ipProps, + uint32_t payloadSize, + uint16_t protocol, + ip4_addr srcIpA, + ip4_addr dstIpA) { + + ipProps->IHL = ETH_IPv4_HEADER_SIZE / 4; + ipProps->Version = 4; + ipProps->DSF = 0x00; + ipProps->TotalLength = ETH_IPv4_HEADER_SIZE + payloadSize; + // Identification is filled on header insertion + ipProps->Flags = 0x00; + ipProps->FragmentOffset = 0x00; + ipProps->TTL = 255; + ipProps->Protocol = protocol; + // HeaderChecksum is calculated on header insertion + ipProps->SourceIPAddr = srcIpA; + ipProps->DestIPAddr = dstIpA; + + // common fields for packet assembly + ipProps->hdrInsFn = insert_ipv4_header; + ipProps->headerSize = ETH_IPv4_HEADER_SIZE; + +} diff --git a/prefab/packet_parsers/ipv4_packet.h b/prefab/packet_parsers/ipv4_packet.h index 12c7d5a..986c0bb 100644 --- a/prefab/packet_parsers/ipv4_packet.h +++ b/prefab/packet_parsers/ipv4_packet.h @@ -61,4 +61,14 @@ void insert_ipv4_header(uint8_t * hdr, const PcktHeaderElement * headers); */ void ethmc_from_ipmc(uint8_t * hwa, ip4_addr ipa); +/** + * Fill IPv4 properties structure based on passed fields. Fields not listed among parameters is set to a default value. + * @param ipProps IPv4 properties structure to fill + * @param payloadSize payload size in bytes (contained header AND its payload size) + * @param protocol contained packet protocol + * @param srcIpA source IP address + * @param dstIpA destination IP address + */ +void ipv4_fill_props(IPv4Props *ipProps, uint32_t payloadSize, uint16_t protocol, ip4_addr srcIpA, ip4_addr dstIpA); + #endif //ETHERLIB_IPV4_PACKET_H diff --git a/prefab/packet_parsers/tcp_segment.c b/prefab/packet_parsers/tcp_segment.c index 3fd186d..ee6760e 100644 --- a/prefab/packet_parsers/tcp_segment.c +++ b/prefab/packet_parsers/tcp_segment.c @@ -83,7 +83,7 @@ static void tcp_insert_options(uint8_t * hdr, uint8_t size, TcpOption * optLe) { } } -static uint32_t tcp_get_options_size(TcpOption * optLe) { +uint32_t tcp_get_options_size(TcpOption * optLe) { TcpOption * opt = optLe; uint32_t size = 0; while (opt != NULL) { @@ -101,6 +101,17 @@ static uint32_t tcp_get_options_size(TcpOption * optLe) { return size; } +TcpOption * tcp_get_option_by_kind(TcpOption * opt, uint16_t kind) { + TcpOption * res = NULL, * iter = opt; + while (iter != NULL) { + if (iter->kind == kind) { + res = opt; + } + iter = iter->next; + } + return res; +} + int parse_tcp(const uint8_t *hdr, uint32_t size, PcktHeaderElement *pcktHdrLe, struct EthInterface_ *intf) { const uint8_t * hdrBegin = hdr; @@ -112,7 +123,7 @@ int parse_tcp(const uint8_t *hdr, uint32_t size, PcktHeaderElement *pcktHdrLe, s FETCH_DWORD_H2N_ADVANCE(&tcpProps->AcknowledgementNumber, hdr); uint8_t dataOffset_Flags; FETCH_BYTE_ADVANCE(&dataOffset_Flags, hdr); - tcpProps->DataOffset = dataOffset_Flags >> 4; + tcpProps->DataOffset = (dataOffset_Flags >> 4) << 2; // eliminate non-relevant bits uint8_t flagsLower; FETCH_BYTE_ADVANCE(&flagsLower, hdr); tcpProps->Flags = flagsLower | ((dataOffset_Flags & 0x01) << 8); @@ -122,9 +133,9 @@ int parse_tcp(const uint8_t *hdr, uint32_t size, PcktHeaderElement *pcktHdrLe, s // fetch options tcpProps->options = NULL; // no options by default - uint8_t fullHeaderSize = tcpProps->DataOffset * 4; - if (fullHeaderSize > ETH_TCP_HEADER_LENGTH) { - uint8_t optSize = fullHeaderSize - ETH_TCP_HEADER_LENGTH; + uint8_t fullHeaderSize = tcpProps->DataOffset; + if (fullHeaderSize > ETH_TCP_HEADER_SIZE) { + uint8_t optSize = fullHeaderSize - ETH_TCP_HEADER_SIZE; tcp_fetch_options(hdr, optSize, &tcpProps->options); } @@ -132,7 +143,8 @@ int parse_tcp(const uint8_t *hdr, uint32_t size, PcktHeaderElement *pcktHdrLe, s const IPv4Props *ipProps = (IPv4Props *) &pcktHdrLe->prev->props; uint16_t headerAndPayloadLength = ipProps->bytesToEnd; IPv4PseudoHeader ph = {ipProps->SourceIPAddr, ipProps->DestIPAddr, 0, ETH_TCP_PACKET_CLASS, htons(headerAndPayloadLength)}; - uint16_t chkSum = tcp_udp_checksum(&ph, hdrBegin, headerAndPayloadLength); + //uint16_t chkSum = tcp_udp_checksum(&ph, hdrBegin, headerAndPayloadLength); + uint16_t chkSum = 0; tcpProps->headerSize = fullHeaderSize; tcpProps->validityOK = (chkSum == 0); @@ -147,7 +159,7 @@ void insert_tcp_header(uint8_t *hdr, const PcktHeaderElement *headers) { // get option area size and computer full area size uint32_t optSize = tcp_get_options_size(tcpProps->options); - uint32_t fullHeaderSize = ETH_TCP_HEADER_LENGTH + optSize; + uint32_t fullHeaderSize = ETH_TCP_HEADER_SIZE + optSize; tcpProps->DataOffset = fullHeaderSize >> 2; // fill beginning of the header @@ -176,4 +188,14 @@ void insert_tcp_header(uint8_t *hdr, const PcktHeaderElement *headers) { memcpy(ChkSumPtr, &tcpProps->Checksum, 2); } +// ---------------------------------- + +TcpOption *tcpopt_new(uint32_t kind, uint32_t size) { + TcpOption * opt = dynmem_alloc(sizeof(TcpOption) + size); + opt->size = size; + opt->kind = kind; + opt->next = NULL; + return opt; +} + diff --git a/prefab/packet_parsers/tcp_segment.h b/prefab/packet_parsers/tcp_segment.h index 943e89f..1071502 100644 --- a/prefab/packet_parsers/tcp_segment.h +++ b/prefab/packet_parsers/tcp_segment.h @@ -5,7 +5,7 @@ #include "../../packet_sieve.h" #define ETH_TCP_PACKET_CLASS (6) -#define ETH_TCP_HEADER_LENGTH (20) +#define ETH_TCP_HEADER_SIZE (20) #define TCP_MAX_OPT_VAL_LEN (8) @@ -28,6 +28,14 @@ typedef struct TcpOption_ { uint8_t value[]; ///< Value } TcpOption; +/** + * Allocate new TCP option. + * @param kind kind of TCP option. + * @param size size of value + * @return pointer to newly allocated option + */ +TcpOption * tcpopt_new(uint32_t kind, uint32_t size); + /** * TCP segment flags */ @@ -43,6 +51,23 @@ typedef enum { TCP_FLAG_NONCE = 0x100 } TcpFlag; +/** + * TCP connection block states. + */ +typedef enum { + TCP_STATE_CLOSED = 0, + TCP_STATE_LISTEN, + TCP_STATE_SYN_RCVD, + TCP_STATE_SYN_SENT, + TCP_STATE_ESTAB, + TCP_STATE_FIN_WAIT_1, + TCP_STATE_FIN_WAIT_2, + TCP_STATE_CLOSE_WAIT, + TCP_STATE_CLOSING, + TCP_STATE_LAST_ACK, + TCP_STATE_TIME_WAIT +} TcpConnectionState; + /** * TCP segment properties. */ @@ -78,4 +103,19 @@ int parse_tcp(const uint8_t *hdr, uint32_t size, PcktHeaderElement *pcktHdrLe, s */ void insert_tcp_header(uint8_t * hdr, const PcktHeaderElement * headers); +/** + * Get the flattened size of the option chain. + * @param optLe list of TCP options + * @return flattened size of options + */ +uint32_t tcp_get_options_size(TcpOption * optLe); + +/** + * Get TCP option by filtering against kind field. + * @param kind option kind to lookup + * @param opt linked list of options + * @return pointer to option OR NULL if not found + */ +TcpOption * tcp_get_option_by_kind(TcpOption * opt, uint16_t kind); + #endif //ETHERLIB_TEST_TCP_SEGMENT_H diff --git a/prefab/packet_parsers/tcp_udp_common.c b/prefab/packet_parsers/tcp_udp_common.c index bd239ad..3a485d0 100644 --- a/prefab/packet_parsers/tcp_udp_common.c +++ b/prefab/packet_parsers/tcp_udp_common.c @@ -8,5 +8,7 @@ uint16_t tcp_udp_checksum(const IPv4PseudoHeader *pseudoHeader, const uint8_t * while (sum >> 16) { sum = (sum & 0xFFFF) + (sum >> 16); } - return htonl(sum); + uint16_t sum16 = sum; + sum16 = htons(sum16); + return sum16; } \ No newline at end of file diff --git a/prefab/packet_parsers/udp_packet.c b/prefab/packet_parsers/udp_packet.c index 287a421..694764a 100644 --- a/prefab/packet_parsers/udp_packet.c +++ b/prefab/packet_parsers/udp_packet.c @@ -12,7 +12,7 @@ static bool check_udp_validity(const uint8_t * hdr, const PcktHeaderElement *pck const UdpProps *udpProps = HEADER_FETCH_PROPS(UdpProps, pcktHdrLe); const IPv4Props *ipProps = (IPv4Props *) &pcktHdrLe->prev->props; IPv4PseudoHeader ph = {ipProps->SourceIPAddr, ipProps->DestIPAddr, 0, ETH_UDP_PACKET_CLASS, htons(udpProps->Length)}; - uint16_t chkSum = tcp_udp_checksum(&ph, hdr, udpProps->Length); + uint16_t chkSum = ~tcp_udp_checksum(&ph, hdr, udpProps->Length); bool valid = chkSum == 0; return valid; } diff --git a/utils.h b/utils.h index 9eafed4..07c1638 100644 --- a/utils.h +++ b/utils.h @@ -53,10 +53,11 @@ #define FILL_BYTE_ADVANCE(dst,src) FILL_ADVANCE(dst,src,1) #define FETCH_WORD_H2N_ADVANCE(dst,w) { uint16_t u; memcpy(&u, w, 2); u = htons(u); memcpy((dst), &u, 2); (w) += 2; } #define FETCH_WORD_ADVANCE(dst,w) { memcpy(dst, w, 2); (w) += 2; } -#define FETCH_WORD_H2N(dst,w) { uint16_t u; memcpy(&u, w, 2); u = htons(u); memcpy((dst), &u, 2) } +#define FETCH_WORD_H2N(dst,w) { uint16_t u; memcpy(&u, w, 2); u = htons(u); memcpy((dst), &u, 2); } #define FETCH_WORD(dst,w) memcpy(dst, w, 2) #define FILL_WORD_ADVANCE(dst,w) { memcpy((dst), &(w), 2); (dst) += 2; } #define FILL_WORD_H2N_ADVANCE(dst,w) { uint16_t u = htons(w); memcpy((dst), &u, 2); (dst) += 2; } +#define FILL_WORD_H2N(dst,w) { uint16_t u = htons(w); memcpy((dst), &u, 2); } #define FETCH_DWORD_H2N_ADVANCE(dst,dw) { uint32_t du; memcpy(&du, dw, 4); du = htonl(du); memcpy((dst), &du, 4); (dw) += 4; } #define FETCH_DWORD_H2N(dst,dw) { uint32_t du; memcpy(&du, dw, 4); du = htonl(du); memcpy((dst), &du, 4); } #define FETCH_DWORD(dst,dw) { uint32_t du; memcpy(&du, dw, 4); memcpy((dst), &du, 4); }