- TCP basic implementation added

- TCPWindow added
- Checksum bug fixed (again)
- CBD introduced
- ConnBlock modified
- PackSieve report funtionality modified to decrease memory consumption
This commit is contained in:
Wiesner András 2023-01-30 11:04:46 +01:00
parent 51696f7341
commit 8676a392e5
38 changed files with 981 additions and 197 deletions

View File

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

72
cbd_table.c Normal file
View File

@ -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");
}

61
cbd_table.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef ETHERLIB_TEST_CBD_TABLE_H
#define ETHERLIB_TEST_CBD_TABLE_H
#include <stdint.h>
#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

View File

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

View File

@ -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(&ethIntf->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) {

View File

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

View File

@ -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

View File

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

View File

@ -4,6 +4,7 @@
#include <etherlib_options.h>
#include <stdint.h>
#include <stdbool.h>
/**
* 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

View File

@ -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.

View File

@ -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) {

View File

@ -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

View File

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

View File

@ -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

View File

@ -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);
}
}
void arp_print_report(const ConnBlock *connBlock) {
INFO("ARP");
}

View File

@ -16,4 +16,15 @@
*/
ConnBlock arp_new_connblock(EthInterface * intf, SieveCallBackFn cb);
void arp_send(const ConnBlock * connBlock, const ArpProps * props);
/**
* 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);

View File

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

View File

@ -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

View File

@ -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;
}
}
void icmp_print_report(const ConnBlock *connBlock) {
INFO("ICMP (ping)");
}

View File

@ -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

View File

@ -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");
}

View File

@ -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

View File

@ -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");
}
}

View File

@ -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

View File

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

View File

@ -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

View File

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

View File

@ -7,6 +7,8 @@
#include <stdint.h>
#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

View File

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

View File

@ -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

View File

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

View File

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

View File

@ -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

View File

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

View File

@ -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

View File

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

View File

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

View File

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