From ca97d1718ad3d07471b99825f20cda18e49d8c12 Mon Sep 17 00:00:00 2001 From: Epagris Date: Tue, 23 Apr 2024 23:33:26 +0200 Subject: [PATCH] - TCP: set_accept_cb() added; semaphore release bugs fixed --- prefab/conn_blocks/tcp_connblock.c | 298 +++++++++++++++-------------- prefab/conn_blocks/tcp_connblock.h | 2 +- 2 files changed, 155 insertions(+), 145 deletions(-) diff --git a/prefab/conn_blocks/tcp_connblock.c b/prefab/conn_blocks/tcp_connblock.c index 6feb65b..934a8c1 100644 --- a/prefab/conn_blocks/tcp_connblock.c +++ b/prefab/conn_blocks/tcp_connblock.c @@ -3,16 +3,16 @@ #include #include -#include "../packet_parsers/packet_parsers.h" -#include "../../utils.h" #include "../../dynmem.h" -#include "../../pckt_assembler.h" #include "../../eth_interface.h" -#include "ipv4_connblock.h" #include "../../gen_queue.h" -#include "etherlib_options.h" -#include "../../prefab/conn_blocks/tcp/tcp_window.h" #include "../../global_state.h" +#include "../../pckt_assembler.h" +#include "../../prefab/conn_blocks/tcp/tcp_window.h" +#include "../../utils.h" +#include "../packet_parsers/packet_parsers.h" +#include "etherlib_options.h" +#include "ipv4_connblock.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...) @@ -29,22 +29,21 @@ static inline cbd tcp_new_connblock_filtcond(EthInterface *intf, ip4_addr ipAddr // ---------- 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" -}; + "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; + IPv4Props *ipProps = (IPv4Props *)contProps; + TcpProps *tcpProps = (TcpProps *)ownProps; TCP_EXTRACT_FILTCOND(filtCond); bool references_us = (local_port == tcpProps->DestinationPort) && // it's our local port - (((remote_port == tcpProps->SourcePort) && // it's the current connections remote 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 + (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; } @@ -54,9 +53,11 @@ static bool filtTcp(const PcktSieveFilterCondition *filtCond, const PcktProps *c static inline TcpState *tcps_create() { TcpState *tcps = dynmem_alloc(sizeof(TcpState)); uint32_t winSize = MAX(ETHLIB_DEF_TCP_WINDOW_SIZE, MAX_TCP_WINDOW_SIZE); - tcps->txWin = tcpw_create(winSize); // create transmit window + tcps->txWin = tcpw_create(winSize); // create transmit window tcps->txBufNotFull = ETHLIB_OS_SEM_CREATE(1); // create transmit buffer not full semaphore + ETHLIB_OS_SEM_POST(tcps->txBufNotFull); // post the tx buffer semaphore tcps->txInProgress = ETHLIB_OS_SEM_CREATE(1); // create transmission in progress semaphore + ETHLIB_OS_SEM_POST(tcps->txInProgress); return tcps; } @@ -92,7 +93,7 @@ void tcps_init(TcpState *tcps, uint16_t localPort) { tcps->remotePort = 0; tcps->remoteAddr = 0; - tcps->txSeqNum = (uint32_t) rand(); + tcps->txSeqNum = (uint32_t)rand(); tcps->rxAckNum = 0; tcps->lastTxSeqNumAcked = tcps->txSeqNum; @@ -114,8 +115,8 @@ void tcps_init(TcpState *tcps, uint16_t localPort) { 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); TODO + ConnBlock *connBlock = (ConnBlock *)user.ptr; + // tcp_init_connection(connBlock); TODO } static inline cbd tcps_create_based_on_listening(const TcpState *source, uint16_t remotePort, ip4_addr remoteAddr, ConnBlock *connBlock) { @@ -147,7 +148,7 @@ static inline cbd tcps_create_based_on_listening(const TcpState *source, uint16_ } int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) { - TcpState *tcps = (TcpState *) tag.p; // pointer to state is stored in sieve layer 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; @@ -160,152 +161,150 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) { // process incoming packet switch (tcps->connState) { - case TCP_STATE_SYN_SENT: { /* SYN segment sent to the server */ + case TCP_STATE_SYN_SENT: { /* SYN segment sent to the server */ - bool ok = false; // operations were fine - - // if no ACK received, then it's likely an error - if (!(tcpProps->Flags & TCP_FLAG_ACK)) { - break; - } - - // 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... - tcpw_set_seqnum_offset(tcps->txWin, tcps->txSeqNum); - 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; - } - - } + bool ok = false; // operations were fine + // if no ACK received, then it's likely an error + if (!(tcpProps->Flags & TCP_FLAG_ACK)) { break; } - case TCP_STATE_LISTEN: { /* Listening for incoming connections */ - if (tcpProps->Flags & TCP_FLAG_SYN) { // no ACK, just SYN + // SYN and ACK received + if ((tcpProps->Flags & TCP_FLAG_SYN) && (tcpProps->Flags & TCP_FLAG_ACK)) { + // store rxAckNum + tcps->rxAckNum = tcpProps->SequenceNumber + 1; - 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; + // 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 | TCP_FLAG_SYN; - tcps->txSeqNum++; // advance sequence number // TODO... - - // create connection based on the listening connection - ConnBlock connB; - cbd client_d = 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++; - tcpw_set_seqnum_offset(target->txWin, target->txSeqNum); - - if ((client_d > 0) && (tcps->acceptCb != NULL)) { - tcps->acceptCb(client_d); - } - } - } - 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->rxAckNum++; // advance acknowledgement number // TODO... + // if previous processing concluded without an error + if (ok) { + // send ACK + int flags = TCP_FLAG_ACK; + tcps->txSeqNum++; // advance sequence number // TODO... + tcpw_set_seqnum_offset(tcps->txWin, tcps->txSeqNum); 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_LAST_ACK; - } else if ((dataSize > 0) && (tcps->rxAckNum == tcpProps->SequenceNumber)) { // incoming data segment with integrity in ack/seq - // try to store packet content into the FIFO - uint32_t pushSize = eth_bfifo_push_try(tcps->rxFifo, pckt->payload, pckt->payloadSize); + tcps->connState = TCP_STATE_ESTAB; + } + } - // adjust receive window size - tcps->localWindow = eth_bfifo_get_free(tcps->rxFifo); + break; + } - // send acknowledge - tcps->rxAckNum += pushSize; + case TCP_STATE_LISTEN: { /* Listening for incoming connections */ + if (tcpProps->Flags & TCP_FLAG_SYN) { // no ACK, just SYN - // send acknowledge - tcp_send_segment(&tcps->connBlock, TCP_FLAG_ACK, NULL, NULL, 0); + bool ok = false; - // process data - if (tcps->streamCb != NULL) { - tcps->streamCb(tcps->d); - } - } else if ((dataSize == 0) && (tcps->txSeqNum == tcpProps->AcknowledgementNumber)) { // it is a valid ACK segment - if (tcps->lastTxSeqNumAcked < tcps->txSeqNum) { // outgoing segment was acknowledged by peer - // release segment from retransmit window - tcpw_acknowledge(tcps->txWin, tcpProps->AcknowledgementNumber); + // store rxAckNum + tcps->rxAckNum = tcpProps->SequenceNumber + 1; - // release the transmit semaphore - ETHLIB_OS_SEM_POST(tcps->txBufNotFull); + // 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; + } - // store last TX sequence number transmitted in ACK - tcps->lastTxSeqNumAcked = tcps->txSeqNum; - } else if (tcps->lastTxSeqNumAcked == tcps->txSeqNum) { // Keep-Alive, respond with some ACK - tcp_send_segment(&tcps->connBlock, TCP_FLAG_ACK, NULL, NULL, 0); + // 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; + cbd client_d = 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++; + tcpw_set_seqnum_offset(target->txWin, target->txSeqNum); + + if ((client_d > 0) && (tcps->acceptCb != NULL)) { + tcps->acceptCb(client_d); } } - break; + } - case TCP_STATE_LAST_ACK: /* last ACK received */ - if (tcpProps->Flags & TCP_FLAG_ACK) { - tcps->connState = TCP_STATE_CLOSED; + 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->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->rxAckNum == tcpProps->SequenceNumber)) { // incoming data segment with integrity in ack/seq + // try to store packet content into the FIFO + uint32_t pushSize = eth_bfifo_push_try(tcps->rxFifo, pckt->payload, pckt->payloadSize); + + // adjust receive window size + tcps->localWindow = eth_bfifo_get_free(tcps->rxFifo); + + // send acknowledge + tcps->rxAckNum += pushSize; + + // send acknowledge + tcp_send_segment(&tcps->connBlock, TCP_FLAG_ACK, NULL, NULL, 0); + + // process data + if (tcps->streamCb != NULL) { + tcps->streamCb(tcps->d); + } + } else if ((dataSize == 0) && (tcps->txSeqNum == tcpProps->AcknowledgementNumber)) { // it is a valid ACK segment + if (tcps->lastTxSeqNumAcked < tcps->txSeqNum) { // outgoing segment was acknowledged by peer + // release segment from retransmit window + tcpw_acknowledge(tcps->txWin, tcpProps->AcknowledgementNumber); + + // release the transmit semaphore ETHLIB_OS_SEM_POST(tcps->txBufNotFull); - ETHLIB_OS_SEM_WAIT(tcps->txInProgress); - ret = SIEVE_LAYER_REMOVE_THIS; // if peer closed the connection, remove sieve layer - } - break; - case TCP_STATE_CLOSED: /* connection is CLOSED */ - default: - break; + // store last TX sequence number transmitted in ACK + tcps->lastTxSeqNumAcked = tcps->txSeqNum; + } else if (tcps->lastTxSeqNumAcked == tcps->txSeqNum) { // Keep-Alive, respond with some ACK + tcp_send_segment(&tcps->connBlock, TCP_FLAG_ACK, NULL, NULL, 0); + } + } + break; + + case TCP_STATE_LAST_ACK: /* last ACK received */ + if (tcpProps->Flags & TCP_FLAG_ACK) { + tcps->connState = TCP_STATE_CLOSED; + //ETHLIB_OS_SEM_POST(tcps->txBufNotFull); FIXME! + //ETHLIB_OS_SEM_WAIT(tcps->txInProgress); + ret = SIEVE_LAYER_REMOVE_THIS; // if peer closed the connection, remove sieve layer + } + break; + + case TCP_STATE_CLOSED: /* connection is CLOSED */ + default: + break; } // save remote window size @@ -377,6 +376,17 @@ void tcp_listen(cbd d) { } } +void tcp_set_accept_callback(cbd d, TcpAcceptCbFn acb) { + 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->acceptCb = acb; +} + void tcp_debug(cbd d, bool debug) { ConnBlock connBlock; if (!cbdt_get_connection_block(E.cbdt, d, &connBlock)) { @@ -447,7 +457,7 @@ int tcp_send_segment(const struct ConnBlock_ *connBlock, TcpFlag flags, TcpOptio // get TCP state PcktSieveLayer *layer = connBlock->sieveLayer; // TCP layer - TcpState *tcpState = (TcpState *) layer->tag.p; + TcpState *tcpState = (TcpState *)layer->tag.p; // fetch sieve layers and fill transmit headers tcpProps->SourcePort = tcpState->localPort; @@ -509,8 +519,8 @@ int tcp_send_segment(const struct ConnBlock_ *connBlock, TcpFlag flags, TcpOptio // advance TCP sequence number tcpState->txSeqNum += size; - // free headers - release_resources: +// free headers +release_resources: dynmem_free(tcpHeader); dynmem_free(ipHeader); dynmem_free(ethHeader); diff --git a/prefab/conn_blocks/tcp_connblock.h b/prefab/conn_blocks/tcp_connblock.h index 37d0677..cf419a4 100644 --- a/prefab/conn_blocks/tcp_connblock.h +++ b/prefab/conn_blocks/tcp_connblock.h @@ -103,7 +103,7 @@ typedef struct { // --------------- - BlockingFifo * rxFifo; ///< Receive FIFO + EthBlockingFifo * rxFifo; ///< Receive FIFO } TcpState;