- TCP: set_accept_cb() added; semaphore release bugs fixed
This commit is contained in:
parent
cea600b3a4
commit
ca97d1718a
@ -3,16 +3,16 @@
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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);
|
||||
|
@ -103,7 +103,7 @@ typedef struct {
|
||||
|
||||
// ---------------
|
||||
|
||||
BlockingFifo * rxFifo; ///< Receive FIFO
|
||||
EthBlockingFifo * rxFifo; ///< Receive FIFO
|
||||
|
||||
} TcpState;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user