- MemoryPool no more space bug fixed
- TCP client/server initial implementation - TCPWindow reworked, now without MP
This commit is contained in:
parent
5fa42c0326
commit
8ed6bc57ad
@ -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
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,26 +2,16 @@
|
||||
#define ETHERLIB_TCP_WINDOW_H
|
||||
|
||||
#include <stdint.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...
|
||||
//#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.
|
||||
|
@ -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: {
|
||||
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)) {
|
||||
// TODO: if not acknowledged
|
||||
}
|
||||
if (tcpProps->Flags & TCP_FLAG_SYN) {
|
||||
tcps->ackNumber = tcpProps->SequenceNumber + 1;
|
||||
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) {
|
||||
tcps->connState = TCP_STATE_CLOSED;
|
||||
if (tcps->retries < tcps->retryLimit) {
|
||||
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
|
||||
}
|
||||
break;
|
||||
}
|
||||
FETCH_WORD_H2N(&tcps->remoteMSS, mss->value);
|
||||
|
||||
// if previous processing concluded without an error
|
||||
if (ok) {
|
||||
// send ACK
|
||||
int flags = TCP_FLAG_ACK;
|
||||
tcps->sequenceNumber++; // advance sequence number // TODO...
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
@ -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 <stdint.h>
|
||||
#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
|
||||
|
@ -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.
|
||||
*/
|
||||
|
1
utils.h
1
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])
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user