- MemoryPool no more space bug fixed

- TCP client/server initial implementation
- TCPWindow reworked, now without MP
This commit is contained in:
Wiesner András 2023-11-05 17:06:31 +01:00
parent 5fa42c0326
commit 8ed6bc57ad
8 changed files with 332 additions and 176 deletions

View File

@ -90,6 +90,11 @@ uint8_t *mp_alloc(MP *mp, uint32_t size) {
recIter--; // step to next block recIter--; // step to next block
} }
// if no suitable block found, then return NULL
if (bestBlock == NULL) {
return NULL;
}
// allocate block // allocate block
uint8_t *ptr = NULL; uint8_t *ptr = NULL;
if (leastBadness == 0) { // just change block registry class if block perfectly fits if (leastBadness == 0) { // just change block registry class if block perfectly fits

View File

@ -30,7 +30,7 @@ typedef union {
uint16_t uw[8]; uint16_t uw[8];
uint32_t u[4]; uint32_t u[4];
uint64_t lu[2]; uint64_t lu[2];
int16_t sw[8]; int16_t sw[8]; // signed word
int32_t i[4]; int32_t i[4];
int64_t li[2]; int64_t li[2];
uint8_t d[16]; uint8_t d[16];

View File

@ -7,63 +7,68 @@
#include "tcp_window.h" #include "tcp_window.h"
#include "../../../dynmem.h" #include "../../../dynmem.h"
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
TcpWindow *tcpw_create(uint32_t size) { TcpWindow *tcpw_create(uint32_t size) {
uint32_t allocSize = sizeof(TcpWindow) + size; // calculate full allocation size uint32_t allocSize = sizeof(TcpWindow) + size; // calculate full allocation size
TcpWindow * tcpw = (TcpWindow *) dynmem_alloc(allocSize); // allocate data block at the end 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; 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 #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) { static inline uint32_t tcpw_get_occupied_size(TcpWindow * tcpw) {
return mp_largest_free_block_size(tcpw->mp) - sizeof(TcpWindowSegment) - TCP_WINDOW_MAX_WINSIZE_PADDING; return tcpw->nextWritten - tcpw->firstUnACK;
} }
TcpWindowSegment * tcpw_store_segment(TcpWindow * tcpw, const uint8_t * data, uint32_t size, uint32_t seqNum) { uint32_t tcpw_get_max_window_size(TcpWindow * tcpw) {
TcpWindowSegment * seg = (TcpWindowSegment *) mp_alloc(tcpw->mp, sizeof(TcpWindowSegment) + size); return tcpw->size - tcpw_get_occupied_size(tcpw);
if (seg == NULL) { // could not store... }
return NULL;
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 // calculate copy size, limit if required
seg->size = size; uint32_t freeArea = tcpw_get_max_window_size(tcpw);
seg->seqNum = seqNum; uint32_t copySize = MIN(size, freeArea);
// store segment // determine first block size
memcpy(seg->data, data, size); uint32_t nextWriteIndex = (tcpw->nextWritten - tcpw->firstByteOffset); // determinte next write index
uint32_t spaceToBufEnd = tcpw->size - nextWriteIndex; // space to buffer end
// chain-in into linked list of segments uint32_t firstBlockSize = MIN(spaceToBufEnd, copySize); // first block size
if (tcpw->firstSeg == NULL) { uint32_t secondBlockSize = (firstBlockSize < copySize) ? copySize - firstBlockSize : 0; // second block size
tcpw->firstSeg = tcpw->lastSeg = seg; memcpy(tcpw->data + nextWriteIndex, data, firstBlockSize); // copy first block
} else { if (secondBlockSize > 0) { // copy second block if needed
tcpw->lastSeg->next = seg; 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) { 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; 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;
}
} }

View File

@ -2,26 +2,16 @@
#define ETHERLIB_TCP_WINDOW_H #define ETHERLIB_TCP_WINDOW_H
#include <stdint.h> #include <stdint.h>
#include "../../../memory_pool.h" //#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...
/** /**
* TCP Window * TCP Window
*/ */
typedef struct { typedef struct {
TcpWindowSegment * firstSeg, *lastSeg; ///< Linked list ends of sequential segments uint32_t nextWritten; ///< Sequence number of next written octet (Sequence number of next unacknowledged octet)
MP *mp; ///< Memory Pool for segment management 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 uint8_t data[]; ///< Window data storage
} TcpWindow; } TcpWindow;
@ -32,6 +22,13 @@ typedef struct {
*/ */
TcpWindow * tcpw_create(uint32_t size); 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.) * Get maximum allocable TCP window size. (Cannot store bigger incoming contiguous data blocks.)
* @param tcpw pointer to TCP Window object * @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); uint32_t tcpw_get_max_window_size(TcpWindow * tcpw);
/** /** TODO: rename function
* Store TCP segment * Store TCP segment
* @param tcpw pointer to TCP Window object * @param tcpw pointer to TCP Window object
* @param data data to store * @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 * @param seqNum sequence number of first octet of the block
* @return pointer to TcpWindowSegment allocated in the window OR NULL on failure * @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. * Acknowledge segment.

View File

@ -14,6 +14,19 @@
#include "../../prefab/conn_blocks/tcp/tcp_window.h" #include "../../prefab/conn_blocks/tcp/tcp_window.h"
#include "../../global_state.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[] = { static char *TCP_STATE_NAMES[] = {
"CLOSED", "LISTEN", "SYN_RCVD", "SYN_SENT", "ESTAB", "FIN_WAIT_1", "CLOSED", "LISTEN", "SYN_RCVD", "SYN_SENT", "ESTAB", "FIN_WAIT_1",
"FIN_WAIT_2", "CLOSE_WAIT", "CLOSING", "LAST_ACK", "TIME_WAIT" "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; IPv4Props *ipProps = (IPv4Props *) contProps;
TcpProps *tcpProps = (TcpProps *) ownProps; TcpProps *tcpProps = (TcpProps *) ownProps;
return ipProps->Protocol == ETH_TCP_PACKET_CLASS && (TCP_PORT_FROM_FILTCOND(filtCond) == tcpProps->DestinationPort); TCP_EXTRACT_FILTCOND(filtCond);
}
/** bool references_us = (local_port == tcpProps->DestinationPort) && // it's our local port
* TCP state. (((remote_port == tcpProps->SourcePort) && // it's the current connections remote port
*/ (remote_addr == ipProps->SourceIPAddr)) || // the packet has come from the known origin
typedef struct { (remote_addr == 0)); // the latter conditions only matter if the control block is connected to a remote station
uint16_t localPort;
uint16_t remotePort; return ipProps->Protocol == ETH_TCP_PACKET_CLASS && references_us;
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;
#define TCP_DEFAULT_MSS (1200) #define TCP_DEFAULT_MSS (1200)
#define TCP_DEFAULT_RETRY_LIMIT (5) #define TCP_DEFAULT_RETRY_LIMIT (5)
#define TCP_DEFAULT_RETRY_TO_HS (100) #define TCP_DEFAULT_RETRY_TO_HS (100)
void tcps_init(TcpState *tcps, uint16_t localPort) { void tcps_init(TcpState *tcps, uint16_t localPort) {
tcps->sequenceNumber = (uint32_t) rand(); tcps->connState = TCP_STATE_CLOSED;
tcps->localPort = localPort; tcps->localPort = localPort;
tcps->localMSS = TCP_DEFAULT_MSS; tcps->localMSS = TCP_DEFAULT_MSS;
tcps->remotePort = 0; tcps->remotePort = 0;
tcps->remoteAddr = 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->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->retryLimit = TCP_DEFAULT_RETRY_LIMIT;
tcps->retryTO = TCP_DEFAULT_RETRY_TO_HS; tcps->retryTO = TCP_DEFAULT_RETRY_TO_HS;
tcps->retries = 0; tcps->retries = 0;
tcps->debug = false; tcps->debug = false;
tcps->userCb = NULL; tcps->userCb = NULL;
} }
@ -78,7 +81,35 @@ void tcp_init_connection(ConnBlock *connBlock);
static void retry_to_connect_cb(Timer *timer, AlarmUserData user) { static void retry_to_connect_cb(Timer *timer, AlarmUserData user) {
ConnBlock *connBlock = (ConnBlock *) user.ptr; 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) { 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 // process incoming packet
switch (tcps->connState) { switch (tcps->connState) {
case TCP_STATE_SYN_SENT: { case TCP_STATE_SYN_SENT: { /* SYN segment sent to the server */
if (!(tcpProps->Flags & TCP_FLAG_ACK)) {
// TODO: if not acknowledged
}
if (tcpProps->Flags & TCP_FLAG_SYN) {
tcps->ackNumber = tcpProps->SequenceNumber + 1;
}
// get MSS bool ok = false; // operations were fine
TcpOption *mss = tcp_get_option_by_kind(tcpProps->options, TCP_OPT_KIND_MSS);
if (mss == NULL) { // if no ACK received, then it's likely an error
tcps->connState = TCP_STATE_CLOSED; if (!(tcpProps->Flags & TCP_FLAG_ACK)) {
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
}
break; break;
} }
FETCH_WORD_H2N(&tcps->remoteMSS, mss->value);
// send ACK // SYN and ACK received
int flags = TCP_FLAG_ACK; if ((tcpProps->Flags & TCP_FLAG_SYN) && (tcpProps->Flags & TCP_FLAG_ACK)) {
tcps->sequenceNumber++; // advance sequence number // TODO... // store rxAckNum
tcp_send_segment(&tcps->connBlock, flags, NULL, NULL, 0); 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; 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 the other end tries to close down the connection
if (tcpProps->Flags & TCP_FLAG_FIN) { if (tcpProps->Flags & TCP_FLAG_FIN) {
// send FIN, ACK // send FIN, ACK
int flags = TCP_FLAG_FIN | TCP_FLAG_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); tcp_send_segment(&tcps->connBlock, flags, NULL, NULL, 0);
// step into next state // step into next state
tcps->connState = TCP_STATE_LAST_ACK; 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 // send acknowledge
tcps->ackNumber += dataSize; tcps->rxAckNum += dataSize;
tcp_send_segment(&tcps->connBlock, TCP_FLAG_ACK, NULL, NULL, 0); tcp_send_segment(&tcps->connBlock, TCP_FLAG_ACK, NULL, NULL, 0);
// process data // process data
@ -146,22 +231,27 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) {
PcktSieveLayerTag userTag = {0}; // TODO... PcktSieveLayerTag userTag = {0}; // TODO...
tcps->userCb(pckt, userTag); 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 // release segment from retransmit window
tcpw_acknowledge(tcps->txWin, tcpProps->AcknowledgementNumber); tcpw_acknowledge(tcps->txWin, tcpProps->AcknowledgementNumber);
} }
break; break;
case TCP_STATE_LAST_ACK:
case TCP_STATE_LAST_ACK: /* last ACK received */
if (tcpProps->Flags & TCP_FLAG_ACK) { if (tcpProps->Flags & TCP_FLAG_ACK) {
tcps->connState = TCP_STATE_CLOSED; tcps->connState = TCP_STATE_CLOSED;
ret = SIEVE_LAYER_REMOVE_THIS; // if peer closed the connection, remove sieve layer ret = SIEVE_LAYER_REMOVE_THIS; // if peer closed the connection, remove sieve layer
} }
break; break;
case TCP_STATE_CLOSED:
case TCP_STATE_CLOSED: /* connection is CLOSED */
default: default:
break; break;
} }
// save remote window size
tcps->remoteWindow = tcpProps->Window;
// print state // print state
if (tcps->debug && (beginState != tcps->connState)) { if (tcps->debug && (beginState != tcps->connState)) {
MSG("%s -> %s\n", TCP_STATE_NAMES[beginState], TCP_STATE_NAMES[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 return 0; // TODO
} }
void tcp_init_connection(ConnBlock *connBlock) { void tcp_bind(cbd d, ip4_addr remoteAddr, uint16_t remotePort) {
/* send SYN with maximum segment size option */ 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 // store remote station data
TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(&connBlock);
TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(connBlock); tcps->remoteAddr = remoteAddr;
tcps->remotePort = remotePort;
// prepare MSS option // prepare MSS option
TcpOption *mss = tcpopt_new(TCP_OPT_KIND_MSS, 2); TcpOption *mss = tcpopt_new(TCP_OPT_KIND_MSS, 2);
@ -193,7 +288,7 @@ void tcp_init_connection(ConnBlock *connBlock) {
int flags = TCP_FLAG_SYN; int flags = TCP_FLAG_SYN;
// send segment // send segment
tcp_send_segment(connBlock, flags, mss, NULL, 0); tcp_send_segment(&connBlock, flags, mss, NULL, 0);
// release option // release option
dynmem_free(mss); dynmem_free(mss);
@ -202,20 +297,17 @@ void tcp_init_connection(ConnBlock *connBlock) {
tcps->connState = TCP_STATE_SYN_SENT; tcps->connState = TCP_STATE_SYN_SENT;
} }
void tcp_bind(cbd d, ip4_addr remoteAddr, uint16_t remotePort) { void tcp_listen(cbd d) {
ConnBlock connBlock; ConnBlock connBlock;
if (!cbdt_get_connection_block(E.cbdt, d, &connBlock)) { if (!cbdt_get_connection_block(E.cbdt, d, &connBlock)) {
ERROR("Invalid CBD descriptor: '%d'!\n", d); ERROR("Invalid CBD descriptor: '%d'!\n", d);
return; return;
} }
// store remote endpoint data
TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(&connBlock); TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(&connBlock);
tcps->remoteAddr = remoteAddr; if ((tcps->userCb != NULL) && (tcps->connState == TCP_STATE_CLOSED)) {
tcps->remotePort = remotePort; tcps->connState = TCP_STATE_LISTEN;
}
// initialize connection
tcp_init_connection(&connBlock);
} }
void tcp_debug(cbd d, bool debug) { 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; ConnBlock tcpConnB;
connb_init_defaults(&tcpConnB); connb_init_defaults(&tcpConnB);
ConnBlock ipConnB = ipv4_new_connblock(intf, ipAddr, NULL); // create new IPv4 connection block 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 PcktSieveLayerTag tag; // store TCP state into sieve layer's tag
TcpState *tcpState = dynmem_alloc(sizeof(TcpState)); TcpState *tcpState = dynmem_alloc(sizeof(TcpState));
tcps_init(tcpState, port); tcps_init(tcpState, port);
tcpState->userCb = cbFn; tcpState->userCb = cbFn;
tag.p = tcpState; 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); ASSERT_NULL(tcpConnB.sieveLayer);
tcpConnB.sieveLayer->connBReportFn = tcp_print_report; 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; 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) { int tcp_send_segment(const struct ConnBlock_ *connBlock, TcpFlag flags, TcpOption *opts, const uint8_t *data, uint32_t size) {
// allocate headers // allocate headers
PcktHeaderElement *tcpHeader = ALLOC_HEADER_ELEMENT(TcpProps); 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 // fetch sieve layers and fill transmit headers
tcpProps->SourcePort = tcpState->localPort; tcpProps->SourcePort = tcpState->localPort;
tcpProps->DestinationPort = tcpState->remotePort; tcpProps->DestinationPort = tcpState->remotePort;
tcpProps->SequenceNumber = tcpState->sequenceNumber; tcpProps->SequenceNumber = tcpState->txSeqNum;
tcpProps->AcknowledgementNumber = tcpState->ackNumber; tcpProps->AcknowledgementNumber = tcpState->rxAckNum;
tcpProps->Flags = flags; tcpProps->Flags = flags;
tcpProps->Window = tcpState->window; tcpProps->Window = tcpState->localWindow;
tcpProps->Checksum = 0; tcpProps->Checksum = 0;
tcpProps->UrgentPtr = 0; tcpProps->UrgentPtr = 0;
tcpProps->options = opts; tcpProps->options = opts;
@ -342,7 +440,7 @@ int tcp_send_segment(const struct ConnBlock_ *connBlock, TcpFlag flags, TcpOptio
ethinf_transmit(connBlock->sieve->intf, &raw); ethinf_transmit(connBlock->sieve->intf, &raw);
// advance TCP sequence number // advance TCP sequence number
tcpState->sequenceNumber += size; tcpState->txSeqNum += size;
// free headers // free headers
release_resources: 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); TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(&connBlock);
if (tcps->connState == TCP_STATE_ESTAB) { // can send only data if connection is ESTABLISHED 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 maxWinSize = tcpw_get_max_window_size(tcps->txWin);
uint32_t txSize = MIN(maxWinSize, size); // limit size to the allocable size of the retransmit buffer uint32_t txSize = MIN(tcps->remoteWindow, MIN(maxWinSize, size)); // limit size to the allocatable size of the retransmit buffer
TcpWindowSegment *winSeg = tcpw_store_segment(tcps->txWin, data, txSize, tcps->sequenceNumber); // store segment for possible retransmission tcpw_store_segment(tcps->txWin, data, txSize, tcps->txSeqNum); // store segment for possible retransmission
tcp_send_segment(&connBlock, TCP_FLAG_ACK, NULL, winSeg->data, winSeg->size); tcp_send_segment(&connBlock, TCP_FLAG_ACK, NULL, data, txSize);
return txSize; return txSize;
} else { } else {
return 0; 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) { void tcp_print_report(const ConnBlock *connBlock) {
const PcktSieveLayer *sl = connBlock->sieveLayer; 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]);
}
} }

View File

@ -1,17 +1,88 @@
#ifndef ETHERLIB_TCP_CONNBLOCK_H #ifndef ETHERLIB_TCP_CONNBLOCK_H
#define ETHERLIB_TCP_CONNBLOCK_H #define ETHERLIB_TCP_CONNBLOCK_H
#define TCP_PORT_FROM_FILTCOND(fc) ((fc)->uw[7]) #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_PORT_TO_FILTCOND(fc,port) (((fc)->uw[7]) = (port)) #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 <stdint.h> #include <stdint.h>
#include "../packet_parsers/ipv4_types.h" #include "../packet_parsers/ipv4_types.h"
#include "../../connection_block.h" #include "../../connection_block.h"
#include "../packet_parsers/tcp_segment.h" #include "../packet_parsers/tcp_segment.h"
#include "../../cbd_table.h" #include "../../cbd_table.h"
#include "../conn_blocks/tcp/tcp_window.h"
struct EthInterface_; 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 * Create new TCP connection block
* @param intf associated Ethernet interface * @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. * Bind TCP connection to remote socket.
* @param d pointer to TCP connection block * @param d connection block descriptor
* @param remoteAddr remote socket address * @param remoteAddr remote socket address
* @param remotePort remote socket port * @param remotePort remote socket port
*/ */
void tcp_bind(cbd d, ip4_addr remoteAddr, uint16_t remotePort); 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. * Send data over an existing TCP connection.
* @param d pointer to existing connblock * @param d pointer to existing connblock

View File

@ -36,38 +36,6 @@ typedef struct TcpOption_ {
*/ */
TcpOption * tcpopt_new(uint32_t kind, uint32_t size); 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. * TCP segment properties.
*/ */

View File

@ -38,6 +38,7 @@
#define INFO(...) MSG(__VA_ARGS__) #define INFO(...) MSG(__VA_ARGS__)
#define IPv4(a,b,c,d) ((a) | (b << 8) | (c << 16) | (d << 24)) #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_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]) #define PRINT_HWADDR(hwaddr) MSG("%02x:%02x:%02x:%02x:%02x:%02x", (hwaddr)[0], (hwaddr)[1], (hwaddr)[2], (hwaddr)[3], (hwaddr)[4], (hwaddr)[5])