From 8ed6bc57ad63184ea9dd9b8eb983fde88e540999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiesner=20Andr=C3=A1s?= Date: Sun, 5 Nov 2023 17:06:31 +0100 Subject: [PATCH] - MemoryPool no more space bug fixed - TCP client/server initial implementation - TCPWindow reworked, now without MP --- memory_pool.c | 5 + packet_sieve.h | 2 +- prefab/conn_blocks/tcp/tcp_window.c | 77 ++++---- prefab/conn_blocks/tcp/tcp_window.h | 31 ++-- prefab/conn_blocks/tcp_connblock.c | 277 +++++++++++++++++++--------- prefab/conn_blocks/tcp_connblock.h | 83 ++++++++- prefab/packet_parsers/tcp_segment.h | 32 ---- utils.h | 1 + 8 files changed, 332 insertions(+), 176 deletions(-) diff --git a/memory_pool.c b/memory_pool.c index dc941d3..8688bd4 100644 --- a/memory_pool.c +++ b/memory_pool.c @@ -90,6 +90,11 @@ uint8_t *mp_alloc(MP *mp, uint32_t size) { recIter--; // step to next block } + // if no suitable block found, then return NULL + if (bestBlock == NULL) { + return NULL; + } + // allocate block uint8_t *ptr = NULL; if (leastBadness == 0) { // just change block registry class if block perfectly fits diff --git a/packet_sieve.h b/packet_sieve.h index 6077062..ee108a1 100644 --- a/packet_sieve.h +++ b/packet_sieve.h @@ -30,7 +30,7 @@ typedef union { uint16_t uw[8]; uint32_t u[4]; uint64_t lu[2]; - int16_t sw[8]; + int16_t sw[8]; // signed word int32_t i[4]; int64_t li[2]; uint8_t d[16]; diff --git a/prefab/conn_blocks/tcp/tcp_window.c b/prefab/conn_blocks/tcp/tcp_window.c index cd6735c..b03a1c3 100644 --- a/prefab/conn_blocks/tcp/tcp_window.c +++ b/prefab/conn_blocks/tcp/tcp_window.c @@ -7,63 +7,68 @@ #include "tcp_window.h" #include "../../../dynmem.h" +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + TcpWindow *tcpw_create(uint32_t size) { uint32_t allocSize = sizeof(TcpWindow) + size; // calculate full allocation size TcpWindow * tcpw = (TcpWindow *) dynmem_alloc(allocSize); // allocate data block at the end - tcpw->mp = mp_init(tcpw->data, size); // initialize memory pool - + memset(tcpw, 0, sizeof(TcpWindow)); + tcpw->size = size; return tcpw; } +void tcpw_set_seqnum_offset(TcpWindow * tcpw, uint32_t offset) { + tcpw->firstByteOffset = offset; +} + #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->mp) - sizeof(TcpWindowSegment) - TCP_WINDOW_MAX_WINSIZE_PADDING; +static inline uint32_t tcpw_get_occupied_size(TcpWindow * tcpw) { + return tcpw->nextWritten - tcpw->firstUnACK; } -TcpWindowSegment * tcpw_store_segment(TcpWindow * tcpw, const uint8_t * data, uint32_t size, uint32_t seqNum) { - TcpWindowSegment * seg = (TcpWindowSegment *) mp_alloc(tcpw->mp, sizeof(TcpWindowSegment) + size); - if (seg == NULL) { // could not store... - return NULL; +uint32_t tcpw_get_max_window_size(TcpWindow * tcpw) { + return tcpw->size - tcpw_get_occupied_size(tcpw); +} + +uint32_t tcpw_store_segment(TcpWindow * tcpw, const uint8_t * data, uint32_t size, uint32_t seqNum) { + // check that seqNum is the continuation of the previous content + if (seqNum != (tcpw->nextWritten)) { // if not, then cannot store + return 0; } - // store segment descriptor - seg->size = size; - seg->seqNum = seqNum; + // calculate copy size, limit if required + uint32_t freeArea = tcpw_get_max_window_size(tcpw); + uint32_t copySize = MIN(size, freeArea); - // 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; + // determine first block size + uint32_t nextWriteIndex = (tcpw->nextWritten - tcpw->firstByteOffset); // determinte next write index + uint32_t spaceToBufEnd = tcpw->size - nextWriteIndex; // space to buffer end + uint32_t firstBlockSize = MIN(spaceToBufEnd, copySize); // first block size + uint32_t secondBlockSize = (firstBlockSize < copySize) ? copySize - firstBlockSize : 0; // second block size + memcpy(tcpw->data + nextWriteIndex, data, firstBlockSize); // copy first block + if (secondBlockSize > 0) { // copy second block if needed + memcpy(tcpw->data, data + firstBlockSize, secondBlockSize); } - //mp_report(tcpw->mp); + // adjust indices + tcpw->nextWritten += copySize; // advance next written + if ((tcpw->nextWritten - tcpw->firstByteOffset) >= tcpw->size) { // advance first byte offset + tcpw->firstByteOffset -= tcpw->size; + } - return seg; + return copySize; } bool tcpw_acknowledge(TcpWindow * tcpw, uint32_t ackNum) { - if (tcpw->firstSeg == NULL) { // cannot acknowledge if there's nothing to acknowledge + bool invalid = tcpw_get_occupied_size(tcpw) == 0 || // storage is empty + ackNum > (tcpw->firstUnACK + tcpw_get_occupied_size(tcpw)); // acknowledgement sequence number exceeds what we have stored + if (invalid) { 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); + tcpw->firstUnACK = ackNum; + + return true; - return true; - } else { - return false; - } } diff --git a/prefab/conn_blocks/tcp/tcp_window.h b/prefab/conn_blocks/tcp/tcp_window.h index 52918c7..5ae9e65 100644 --- a/prefab/conn_blocks/tcp/tcp_window.h +++ b/prefab/conn_blocks/tcp/tcp_window.h @@ -2,26 +2,16 @@ #define ETHERLIB_TCP_WINDOW_H #include -#include "../../../memory_pool.h" - -/** - * TCP segment descriptor - */ -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... +//#include "../../../memory_pool.h" /** * TCP Window */ typedef struct { - TcpWindowSegment * firstSeg, *lastSeg; ///< Linked list ends of sequential segments - MP *mp; ///< Memory Pool for segment management + uint32_t nextWritten; ///< Sequence number of next written octet (Sequence number of next unacknowledged octet) + uint32_t firstUnACK; ///< Sequence number of last acknowledged octet + 1 + uint32_t firstByteOffset; ///< Sequence number offset of the array's first byte + uint32_t size; ///< Size of the storage area uint8_t data[]; ///< Window data storage } TcpWindow; @@ -32,6 +22,13 @@ typedef struct { */ TcpWindow * tcpw_create(uint32_t size); +/** + * Set sequence number offset + * @param tcpw + * @param offset + */ +void tcpw_set_seqnum_offset(TcpWindow * tcpw, uint32_t offset); + /** * Get maximum allocable TCP window size. (Cannot store bigger incoming contiguous data blocks.) * @param tcpw pointer to TCP Window object @@ -39,7 +36,7 @@ TcpWindow * tcpw_create(uint32_t size); */ uint32_t tcpw_get_max_window_size(TcpWindow * tcpw); -/** +/** TODO: rename function * Store TCP segment * @param tcpw pointer to TCP Window object * @param data data to store @@ -47,7 +44,7 @@ uint32_t tcpw_get_max_window_size(TcpWindow * tcpw); * @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); +uint32_t tcpw_store_segment(TcpWindow * tcpw, const uint8_t * data, uint32_t size, uint32_t seqNum); /** * Acknowledge segment. diff --git a/prefab/conn_blocks/tcp_connblock.c b/prefab/conn_blocks/tcp_connblock.c index e59aefa..c0d1c3d 100644 --- a/prefab/conn_blocks/tcp_connblock.c +++ b/prefab/conn_blocks/tcp_connblock.c @@ -14,6 +14,19 @@ #include "../../prefab/conn_blocks/tcp/tcp_window.h" #include "../../global_state.h" +// TODO: - retransmission hiányzik +// TODO: - állapotváltás esetleg a küldés előtt? (külcsönös kizárás és társai...) +// TODO: - ne próbáljunk két kapcsolatot nyitni ugyanarra a portra... +// TODO: - ez az "ok" változó nem túl jó... +// TODO: - retry connection nincs kész + +// ---------- + +static inline cbd tcp_new_connblock_filtcond(EthInterface *intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn, + const PcktSieveFilterCondition *filtCond); + +// ---------- + 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" @@ -25,49 +38,39 @@ static bool filtTcp(const PcktSieveFilterCondition *filtCond, const PcktProps *c IPv4Props *ipProps = (IPv4Props *) contProps; TcpProps *tcpProps = (TcpProps *) ownProps; - return ipProps->Protocol == ETH_TCP_PACKET_CLASS && (TCP_PORT_FROM_FILTCOND(filtCond) == tcpProps->DestinationPort); -} + TCP_EXTRACT_FILTCOND(filtCond); -/** - * TCP state. - */ -typedef struct { - uint16_t localPort; - uint16_t remotePort; - ip4_addr remoteAddr; - 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; - TcpWindow *txWin; - ConnBlock connBlock; - cbd d; // connection block descriptor - uint8_t retryLimit; - uint8_t retries; - uint8_t retryTO; // retry timeout - bool8_t debug; - SieveCallBackFn userCb; -} TcpState; + bool references_us = (local_port == tcpProps->DestinationPort) && // it's our local port + (((remote_port == tcpProps->SourcePort) && // it's the current connections remote port + (remote_addr == ipProps->SourceIPAddr)) || // the packet has come from the known origin + (remote_addr == 0)); // the latter conditions only matter if the control block is connected to a remote station + + return ipProps->Protocol == ETH_TCP_PACKET_CLASS && references_us; +} #define TCP_DEFAULT_MSS (1200) #define TCP_DEFAULT_RETRY_LIMIT (5) #define TCP_DEFAULT_RETRY_TO_HS (100) void tcps_init(TcpState *tcps, uint16_t localPort) { - tcps->sequenceNumber = (uint32_t) rand(); + tcps->connState = TCP_STATE_CLOSED; + tcps->localPort = localPort; tcps->localMSS = TCP_DEFAULT_MSS; tcps->remotePort = 0; tcps->remoteAddr = 0; - tcps->ackNumber = 0; - tcps->window = ETHLIB_DEF_TCP_WINDOW_SIZE; + + tcps->txSeqNum = (uint32_t) rand(); + tcps->rxAckNum = 0; + + tcps->localWindow = ETHLIB_DEF_TCP_WINDOW_SIZE; tcps->txWin = tcpw_create(ETHLIB_DEF_TCP_WINDOW_SIZE); - //tcps->rxWin = tcpw_create(ETHLIB_DEF_TCP_WINDOW_SIZE); + tcpw_set_seqnum_offset(tcps->txWin, tcps->txSeqNum); + tcps->retryLimit = TCP_DEFAULT_RETRY_LIMIT; tcps->retryTO = TCP_DEFAULT_RETRY_TO_HS; tcps->retries = 0; + tcps->debug = false; tcps->userCb = NULL; } @@ -78,7 +81,35 @@ 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); + //tcp_init_connection(connBlock); TODO +} + +static inline cbd tcps_create_based_on_listening(const TcpState *source, uint16_t remotePort, ip4_addr remoteAddr, ConnBlock *connBlock) { + // construct filter condition + PcktSieveFilterCondition filtCond; + TCP_LOCAL_PORT_TO_FILTCOND(&filtCond, source->localPort); + TCP_REMOTE_PORT_TO_FILTCOND(&filtCond, remotePort); + TCP_REMOTE_ADDR_TO_FILTCOND(&filtCond, remoteAddr); + + // create connblock + EthInterface *intf = source->connBlock.sieve->intf; // retrieve interface + + cbd d = tcp_new_connblock_filtcond(intf, IPv4_IF_ADDR, source->localPort, source->userCb, &filtCond); + if (!cbdt_get_connection_block(E.cbdt, d, connBlock)) { + ERROR("Invalid CBD descriptor: '%d'!\n", d); + return CBDT_ERR; + } + + // alter fields + TcpState *target = TCP_FETCH_STATE_FROM_CONNBLOCK(connBlock); + target->txSeqNum = source->txSeqNum; + target->rxAckNum = source->rxAckNum; + target->remoteAddr = remoteAddr; + target->remotePort = remotePort; + target->connState = TCP_STATE_ESTAB; + tcpw_set_seqnum_offset(target->txWin, target->txSeqNum); + + return d; } int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) { @@ -95,50 +126,104 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) { // 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; - } + case TCP_STATE_SYN_SENT: { /* SYN segment sent to the server */ - // get MSS - TcpOption *mss = tcp_get_option_by_kind(tcpProps->options, TCP_OPT_KIND_MSS); - if (mss == NULL) { - tcps->connState = TCP_STATE_CLOSED; - if (tcps->retries < tcps->retryLimit) { - AlarmUserData alarmData; - alarmData.ptr = &tcps->connBlock; - timer_sched_rel(E.tmr, tcps->retryTO * 10000, retry_to_connect_cb, alarmData); - willRetry = true; // indicate that operation is going to be reattempted - } + bool ok = false; // operations were fine + + // if no ACK received, then it's likely an error + if (!(tcpProps->Flags & TCP_FLAG_ACK)) { 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); + // SYN and ACK received + if ((tcpProps->Flags & TCP_FLAG_SYN) && (tcpProps->Flags & TCP_FLAG_ACK)) { + // store rxAckNum + tcps->rxAckNum = tcpProps->SequenceNumber + 1; + + // get MSS + TcpOption *mss = tcp_get_option_by_kind(tcpProps->options, TCP_OPT_KIND_MSS); + if (mss != NULL) { // if MSS is included in the segment + FETCH_WORD_H2N(&tcps->remoteMSS, mss->value); // store remote Maximum Segment Size + ok = true; + } else { // if MSS is NOT included in the received segment... + tcps->connState = TCP_STATE_CLOSED; // close the initiating connection + if (tcps->retries < tcps->retryLimit) { // schedule a connection retry + AlarmUserData alarmData; + alarmData.ptr = &tcps->connBlock; + timer_sched_rel(E.tmr, tcps->retryTO * 10000, retry_to_connect_cb, alarmData); + willRetry = true; // indicate that operation is going to be reattempted + } + } + + // if previous processing concluded without an error + if (ok) { + // send ACK + int flags = TCP_FLAG_ACK; + tcps->txSeqNum++; // advance sequence number // TODO... + tcp_send_segment(&tcps->connBlock, flags, NULL, NULL, 0); + + // save filter values + PcktSieveFilterCondition *filtCond = &tcps->connBlock.sieveLayer->filtCond; + TCP_REMOTE_PORT_TO_FILTCOND(filtCond, tcpProps->SourcePort); + TCP_REMOTE_ADDR_TO_FILTCOND(filtCond, ipProps->SourceIPAddr); + + // step into next state + tcps->connState = TCP_STATE_ESTAB; + } + + } - // step into next state - tcps->connState = TCP_STATE_ESTAB; break; } - case TCP_STATE_ESTAB: + + case TCP_STATE_LISTEN: { /* Listening for incoming connections */ + if (tcpProps->Flags & TCP_FLAG_SYN) { // no ACK, just SYN + + bool ok = false; + + // store rxAckNum + tcps->rxAckNum = tcpProps->SequenceNumber + 1; + + // get MSS + TcpOption *mss = tcp_get_option_by_kind(tcpProps->options, TCP_OPT_KIND_MSS); + if (mss != NULL) { // if MSS is included in the segment + FETCH_WORD_H2N(&tcps->remoteMSS, mss->value); // store remote Maximum Segment Size + ok = true; + } + + // if previous processing concluded without an error + if (ok) { + // send ACK + int flags = TCP_FLAG_ACK | TCP_FLAG_SYN; + tcps->txSeqNum++; // advance sequence number // TODO... + + // create connection based on the listening connection + ConnBlock connB; + tcps_create_based_on_listening(tcps, tcpProps->SourcePort, ipProps->SourceIPAddr, &connB); + tcp_send_segment(&connB, flags, NULL, NULL, 0); + + TcpState *target = TCP_FETCH_STATE_FROM_CONNBLOCK(&connB); + target->txSeqNum++; + } + + } + + break; + } + + case TCP_STATE_ESTAB: /* Connection established */ // 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... + tcps->rxAckNum++; // 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 + } else if ((dataSize > 0) && (tcps->rxAckNum == tcpProps->SequenceNumber)) { // incoming data segment with integrity in ack/seq // send acknowledge - tcps->ackNumber += dataSize; + tcps->rxAckNum += dataSize; tcp_send_segment(&tcps->connBlock, TCP_FLAG_ACK, NULL, NULL, 0); // process data @@ -146,22 +231,27 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) { PcktSieveLayerTag userTag = {0}; // TODO... tcps->userCb(pckt, userTag); } - } else if ((dataSize == 0) && (tcps->sequenceNumber == tcpProps->AcknowledgementNumber)) { // outgoing segment was acknowledged by peer + } else if ((dataSize == 0) && (tcps->txSeqNum == 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: + + case TCP_STATE_LAST_ACK: /* last ACK received */ if (tcpProps->Flags & TCP_FLAG_ACK) { tcps->connState = TCP_STATE_CLOSED; ret = SIEVE_LAYER_REMOVE_THIS; // if peer closed the connection, remove sieve layer } break; - case TCP_STATE_CLOSED: + + case TCP_STATE_CLOSED: /* connection is CLOSED */ default: break; } + // save remote window size + tcps->remoteWindow = tcpProps->Window; + // print state if (tcps->debug && (beginState != tcps->connState)) { MSG("%s -> %s\n", TCP_STATE_NAMES[beginState], TCP_STATE_NAMES[tcps->connState]); @@ -178,12 +268,17 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) { return 0; // TODO } -void tcp_init_connection(ConnBlock *connBlock) { - /* send SYN with maximum segment size option */ +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; + } - // TODO: check that our state is CLOSED - - TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(connBlock); + // store remote station data + TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(&connBlock); + tcps->remoteAddr = remoteAddr; + tcps->remotePort = remotePort; // prepare MSS option TcpOption *mss = tcpopt_new(TCP_OPT_KIND_MSS, 2); @@ -193,7 +288,7 @@ void tcp_init_connection(ConnBlock *connBlock) { int flags = TCP_FLAG_SYN; // send segment - tcp_send_segment(connBlock, flags, mss, NULL, 0); + tcp_send_segment(&connBlock, flags, mss, NULL, 0); // release option dynmem_free(mss); @@ -202,20 +297,17 @@ void tcp_init_connection(ConnBlock *connBlock) { tcps->connState = TCP_STATE_SYN_SENT; } -void tcp_bind(cbd d, ip4_addr remoteAddr, uint16_t remotePort) { +void tcp_listen(cbd d) { 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); + if ((tcps->userCb != NULL) && (tcps->connState == TCP_STATE_CLOSED)) { + tcps->connState = TCP_STATE_LISTEN; + } } void tcp_debug(cbd d, bool debug) { @@ -231,23 +323,19 @@ void tcp_debug(cbd d, bool debug) { // --------------------- -cbd tcp_new_connblock(EthInterface *intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn) { +static inline cbd tcp_new_connblock_filtcond(EthInterface *intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn, const PcktSieveFilterCondition *filtCond) { ConnBlock tcpConnB; connb_init_defaults(&tcpConnB); ConnBlock ipConnB = ipv4_new_connblock(intf, ipAddr, NULL); // create new IPv4 connection block - PcktSieveFilterCondition filtCond; - packfiltcond_zero(&filtCond); - TCP_PORT_TO_FILTCOND(&filtCond, port); - PcktSieveLayerTag tag; // store TCP state into sieve layer's tag 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, tcp_receive_segment_cb, 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; @@ -263,6 +351,16 @@ cbd tcp_new_connblock(EthInterface *intf, ip4_addr ipAddr, uint16_t port, SieveC return d; } +cbd tcp_new_connblock(EthInterface *intf, ip4_addr ipAddr, uint16_t port, SieveCallBackFn cbFn) { + PcktSieveFilterCondition filtCond; + packfiltcond_zero(&filtCond); + TCP_LOCAL_PORT_TO_FILTCOND(&filtCond, port); + TCP_REMOTE_PORT_TO_FILTCOND(&filtCond, 0); + TCP_REMOTE_ADDR_TO_FILTCOND(&filtCond, 0); + + return tcp_new_connblock_filtcond(intf, ipAddr, port, cbFn, &filtCond); +} + 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); @@ -287,10 +385,10 @@ int tcp_send_segment(const struct ConnBlock_ *connBlock, TcpFlag flags, TcpOptio // fetch sieve layers and fill transmit headers tcpProps->SourcePort = tcpState->localPort; tcpProps->DestinationPort = tcpState->remotePort; - tcpProps->SequenceNumber = tcpState->sequenceNumber; - tcpProps->AcknowledgementNumber = tcpState->ackNumber; + tcpProps->SequenceNumber = tcpState->txSeqNum; + tcpProps->AcknowledgementNumber = tcpState->rxAckNum; tcpProps->Flags = flags; - tcpProps->Window = tcpState->window; + tcpProps->Window = tcpState->localWindow; tcpProps->Checksum = 0; tcpProps->UrgentPtr = 0; tcpProps->options = opts; @@ -342,7 +440,7 @@ int tcp_send_segment(const struct ConnBlock_ *connBlock, TcpFlag flags, TcpOptio ethinf_transmit(connBlock->sieve->intf, &raw); // advance TCP sequence number - tcpState->sequenceNumber += size; + tcpState->txSeqNum += size; // free headers release_resources: @@ -363,9 +461,9 @@ uint32_t tcp_send(cbd d, const uint8_t *data, uint32_t size) { 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); + uint32_t txSize = MIN(tcps->remoteWindow, MIN(maxWinSize, size)); // limit size to the allocatable size of the retransmit buffer + tcpw_store_segment(tcps->txWin, data, txSize, tcps->txSeqNum); // store segment for possible retransmission + tcp_send_segment(&connBlock, TCP_FLAG_ACK, NULL, data, txSize); return txSize; } else { return 0; @@ -374,5 +472,10 @@ uint32_t tcp_send(cbd d, const uint8_t *data, uint32_t size) { void tcp_print_report(const ConnBlock *connBlock) { const PcktSieveLayer *sl = connBlock->sieveLayer; - INFO("TCP port: %d", TCP_PORT_FROM_FILTCOND(&sl->filtCond)); + TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(connBlock); + if (tcps->connState != TCP_STATE_ESTAB) { + INFO("TCP port: %d - %s", tcps->localPort, TCP_STATE_NAMES[tcps->connState]); + } else { + INFO("TCP :%d -- %u.%u.%u.%u:%d - %s", tcps->localPort, EXPLODE_IPv4(tcps->remoteAddr), tcps->remotePort, TCP_STATE_NAMES[tcps->connState]); + } } \ No newline at end of file diff --git a/prefab/conn_blocks/tcp_connblock.h b/prefab/conn_blocks/tcp_connblock.h index 862f254..126deaf 100644 --- a/prefab/conn_blocks/tcp_connblock.h +++ b/prefab/conn_blocks/tcp_connblock.h @@ -1,17 +1,88 @@ #ifndef ETHERLIB_TCP_CONNBLOCK_H #define ETHERLIB_TCP_CONNBLOCK_H -#define TCP_PORT_FROM_FILTCOND(fc) ((fc)->uw[7]) -#define TCP_PORT_TO_FILTCOND(fc,port) (((fc)->uw[7]) = (port)) +#define TCP_EXTRACT_FILTCOND(fc) uint16_t local_port = ((fc)->uw[4]); uint16_t remote_port = ((fc)->uw[5]); ip4_addr remote_addr = ((fc)->u[0]) +#define TCP_LOCAL_PORT_TO_FILTCOND(fc,local_port) ((fc)->uw[4]) = (local_port) +#define TCP_REMOTE_PORT_TO_FILTCOND(fc,remote_port) ((fc)->uw[5]) = (remote_port) +#define TCP_REMOTE_ADDR_TO_FILTCOND(fc,remote_addr) ((fc)->u[0] = (remote_addr)) #include #include "../packet_parsers/ipv4_types.h" #include "../../connection_block.h" #include "../packet_parsers/tcp_segment.h" #include "../../cbd_table.h" +#include "../conn_blocks/tcp/tcp_window.h" struct EthInterface_; +/** + * TCP segment flags + */ +typedef enum { + TCP_FLAG_FIN = 0x01, + TCP_FLAG_SYN = 0x02, + TCP_FLAG_RESET = 0x04, + TCP_FLAG_PUSH = 0x08, + TCP_FLAG_ACK = 0x10, + TCP_FLAG_URGENT = 0x20, + TCP_FLAG_ECNECHO = 0x40, + TCP_FLAG_CWR = 0x80, + 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 state. + */ +typedef struct { + uint16_t localPort; ///< Local port + uint16_t remotePort; ///< Remote port this CB is bound to + ip4_addr remoteAddr; ///< Remote IP-address this CB is bound to + TcpConnectionState connState; ///< TCP connection state + uint16_t localMSS; ///< Local Maximum Segment Size + uint16_t remoteMSS; ///< Remote Maximum Segment Size + + // --------------- + + uint32_t txSeqNum; ///< Seq. num of next transmitted octet (TX) + uint32_t rxAckNum; ///< Seq. num of the next EXPECTED octet (RX) (Ack. number sent to the remote side) + uint16_t localWindow; ///< Maximum number of bytes we are willing (able) to accept + uint16_t remoteWindow; ///< Maximum number of bytes the peer is willing (able) to accept + TcpWindow *txWin; ///< Transmit window OBJECT + + // --------------- + + ConnBlock connBlock; ///< Connection block (copy) + cbd d; ///< Connection block descriptor + + // --------------- + + uint8_t retryLimit; ///< Maximum number of retransmission reties + uint8_t retries; ///< Current attempts number + uint8_t retryTO; ///< Retry timeout + + // --------------- + + bool8_t debug; ///< Turns on/off debug mode + SieveCallBackFn userCb; ///< User callback function +} TcpState; + /** * Create new TCP connection block * @param intf associated Ethernet interface @@ -24,12 +95,18 @@ cbd tcp_new_connblock(struct EthInterface_ *intf, ip4_addr ipAddr, uint16_t port /** * Bind TCP connection to remote socket. - * @param d pointer to TCP connection block + * @param d connection block descriptor * @param remoteAddr remote socket address * @param remotePort remote socket port */ void tcp_bind(cbd d, ip4_addr remoteAddr, uint16_t remotePort); +/** + * Make the TCP connection listening for incoming connections. + * @param d connection block descriptor + */ +void tcp_listen(cbd d); + /** * Send data over an existing TCP connection. * @param d pointer to existing connblock diff --git a/prefab/packet_parsers/tcp_segment.h b/prefab/packet_parsers/tcp_segment.h index 827640b..28afa6b 100644 --- a/prefab/packet_parsers/tcp_segment.h +++ b/prefab/packet_parsers/tcp_segment.h @@ -36,38 +36,6 @@ typedef struct TcpOption_ { */ TcpOption * tcpopt_new(uint32_t kind, uint32_t size); -/** - * TCP segment flags - */ -typedef enum { - TCP_FLAG_FIN = 0x01, - TCP_FLAG_SYN = 0x02, - TCP_FLAG_RESET = 0x04, - TCP_FLAG_PUSH = 0x08, - TCP_FLAG_ACK = 0x10, - TCP_FLAG_URGENT = 0x20, - TCP_FLAG_ECNECHO = 0x40, - TCP_FLAG_CWR = 0x80, - 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. */ diff --git a/utils.h b/utils.h index a83d15b..551cd14 100644 --- a/utils.h +++ b/utils.h @@ -38,6 +38,7 @@ #define INFO(...) MSG(__VA_ARGS__) #define IPv4(a,b,c,d) ((a) | (b << 8) | (c << 16) | (d << 24)) +#define EXPLODE_IPv4(ip) (ip & 0xFF), ((ip >> 8) & 0xFF), ((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF) #define PRINT_IPv4(ip) MSG("%u.%u.%u.%u", (ip & 0xFF), ((ip >> 8) & 0xFF), ((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)) #define PRINT_HWADDR(hwaddr) MSG("%02x:%02x:%02x:%02x:%02x:%02x", (hwaddr)[0], (hwaddr)[1], (hwaddr)[2], (hwaddr)[3], (hwaddr)[4], (hwaddr)[5])