- FTP, TCP
This commit is contained in:
parent
499b5f030c
commit
96bc37f271
@ -1,6 +1,7 @@
|
||||
#include "ftp_server.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <etherlib/prefab/conn_blocks/tcp_connblock.h>
|
||||
|
||||
@ -8,10 +9,21 @@
|
||||
|
||||
/* FTP response messages */
|
||||
|
||||
static char ftpGreeting[] = "220 Service ready for new user.\r\n";
|
||||
static char ftpNotImplemented[] = "502 Command not implemented.\r\n";
|
||||
static char ftpLoginOK[] = "230 User logged in, proceed.\r\n";
|
||||
static char ftpRootDirName[] = "257 \"/\" is the current directory\r\n";
|
||||
static const char ftpOpeningDataConn[] = "150 Opening data connection.\r\n";
|
||||
static const char ftpCmdOk[] = "200 Command OK.\r\n";
|
||||
static const char ftpGreeting[] = "220 Service ready for new user.\r\n";
|
||||
static const char ftpClosingDataConn[] = "226 Closing data connection.\r\n";
|
||||
static const char ftpEnteringPassivePrefix[] = "227 Entering passive mode ";
|
||||
static const char ftpLoginOK[] = "230 User logged in, proceed.\r\n";
|
||||
static const char ftpFileActionCplt[] = "250 Requested file action complete.\r\n";
|
||||
static const char ftpRootDirName[] = "257 \"/\" is the current directory\r\n";
|
||||
static const char ftpSyntaxError[] = "501 Syntax error in command or parameters\r\n";
|
||||
static const char ftpNotImplemented[] = "502 Command not implemented.\r\n";
|
||||
static const char ftpCmdNotImplForParam[] = "504 Command not implemented for that parameter\r\n";
|
||||
|
||||
#define FTPS_DYNAMIC_RESPONSE_MAXLEN (63)
|
||||
|
||||
static char ftpsDynResp[FTPS_DYNAMIC_RESPONSE_MAXLEN + 1] = {0};
|
||||
|
||||
/* Implemented commands */
|
||||
|
||||
@ -29,6 +41,9 @@ static char *ftpImplementedCmdsStr[] = {
|
||||
|
||||
// ------ additional commands
|
||||
"PWD",
|
||||
"CWD",
|
||||
"PASV",
|
||||
"LIST",
|
||||
"\0" // SENTRY!
|
||||
};
|
||||
|
||||
@ -45,7 +60,10 @@ typedef enum { // ordering must be the same as in the string version above
|
||||
NOOP,
|
||||
|
||||
// ------ additional commands
|
||||
PWD
|
||||
PWD,
|
||||
CWD,
|
||||
PASV,
|
||||
LIST
|
||||
} Ftp_ImplementedCmds;
|
||||
|
||||
// -----------
|
||||
@ -61,7 +79,7 @@ int ftps_ctrl_recv(cbd d) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ftps_accept_cb(cbd d) {
|
||||
int ftps_ctrl_accept_cb(cbd d) {
|
||||
s.connCtrl = d;
|
||||
uint32_t event = FTPEVT_NEW_CONNECTION;
|
||||
ETHLIB_OS_QUEUE_PUSH(s.eventQ, &event);
|
||||
@ -73,12 +91,36 @@ void ftps_thread(void *param);
|
||||
void ftps_init() {
|
||||
osThreadAttr_t attr;
|
||||
memset(&attr, 0, sizeof(osThreadAttr_t));
|
||||
attr.stack_size = 2048;
|
||||
attr.stack_size = 3072;
|
||||
attr.priority = osPriorityNormal;
|
||||
attr.name = "ftp";
|
||||
osThreadNew(ftps_thread, NULL, &attr);
|
||||
}
|
||||
|
||||
char *ftps_get_word(char *word, char *str, uint32_t maxLen) {
|
||||
char *iter = str;
|
||||
uint32_t len = 0;
|
||||
|
||||
// skip leading whitespaces
|
||||
while ((*iter <= ' ') && (*iter != '\0')) {
|
||||
iter++;
|
||||
}
|
||||
|
||||
// look for end of word, and copy until copy size is reached
|
||||
while (*iter > ' ') {
|
||||
if (len < maxLen) {
|
||||
word[len] = *iter;
|
||||
}
|
||||
|
||||
len++;
|
||||
iter++;
|
||||
}
|
||||
word[len] = '\0'; // insert null termination
|
||||
|
||||
// return the position of the next whitespace
|
||||
return iter;
|
||||
}
|
||||
|
||||
bool ftps_is_command_implemented(char *cmd, uint32_t len, uint32_t *cmdCode) {
|
||||
char *iter = ftpImplementedCmdsStr[0];
|
||||
bool ret = false;
|
||||
@ -109,14 +151,29 @@ bool ftps_is_command_implemented(char *cmd, uint32_t len, uint32_t *cmdCode) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ftps_rtrim(char * str) {
|
||||
char * iter = str + strlen(str) - 1;
|
||||
void ftps_rtrim(char *str) {
|
||||
char *iter = str + strlen(str) - 1;
|
||||
while ((*iter <= ' ') && (iter >= str)) {
|
||||
*iter = '\0';
|
||||
iter--;
|
||||
}
|
||||
}
|
||||
|
||||
// data reception on data port
|
||||
int ftps_data_recv(cbd d) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// accept new data connection
|
||||
int ftps_data_accept_cb(cbd d) {
|
||||
s.connData = d; // set data connection ID
|
||||
ETHLIB_OS_SEM_POST(s.dataConnHasOpened); // release data connection semaphore
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FTPS_TYPE_ASCII 'A'
|
||||
#define FTPS_TYPE_IMAGE 'I'
|
||||
|
||||
void ftps_process_event(uint32_t event) {
|
||||
const char *resp = NULL; // response towards the FTP client
|
||||
char *cmdArg = NULL; // command argument
|
||||
@ -169,12 +226,71 @@ void ftps_process_event(uint32_t event) {
|
||||
s.connState = FTPS_ESTABLISHED;
|
||||
}
|
||||
} else if (s.connState == FTPS_ESTABLISHED) {
|
||||
switch (cmdCode)
|
||||
{
|
||||
switch (cmdCode) {
|
||||
case PWD: // print working directory name
|
||||
resp = ftpRootDirName;
|
||||
break;
|
||||
case TYPE: //
|
||||
case CWD:
|
||||
resp = ftpFileActionCplt;
|
||||
break;
|
||||
case TYPE: { // set transfer format
|
||||
char typeChar[2]; // get type character
|
||||
ftps_get_word(typeChar, cmdArg, 1); // (first parameter)
|
||||
|
||||
// if no parameter was defined
|
||||
if (typeChar[0] == '\0') {
|
||||
goto syntax_error;
|
||||
}
|
||||
|
||||
// if parameter is defined, then act accordingly
|
||||
switch (typeChar[0]) {
|
||||
case FTPS_TYPE_ASCII: // ASCII
|
||||
case FTPS_TYPE_IMAGE: // Image (binary)
|
||||
// I don't know if there's any difference between ASCII and Image transfers, since bytes are 8-bit long
|
||||
goto cmd_ok;
|
||||
break;
|
||||
default: // other formats are not implemented
|
||||
goto cmd_param_not_implemented;
|
||||
break;
|
||||
}
|
||||
|
||||
} break;
|
||||
case PASV: { // enter passive mode
|
||||
if (!s.passiveMode) {
|
||||
// start data connection listener socket
|
||||
uint16_t port;
|
||||
do { // randomize a port number not in use currently
|
||||
port = (rand() % (0xFFFF - 1024)) + 1024; // it's a nice practice if we pick a port number greater than 1024
|
||||
s.connDataS = tcp_new_connblock(s.intf, IPv4_IF_ADDR, port, ftps_data_recv);
|
||||
} while (s.connDataS == 0); // retry if connection block could not be opened
|
||||
|
||||
// convert to server socket
|
||||
tcp_set_accept_callback(s.connDataS, ftps_data_accept_cb);
|
||||
tcp_listen(s.connDataS);
|
||||
|
||||
MSG("port: %u\n", port);
|
||||
|
||||
// turn passive mode on and save passive mode port
|
||||
s.passiveMode = true;
|
||||
s.passiveModePort = port;
|
||||
}
|
||||
|
||||
// compile response message
|
||||
SNPRINTF(ftpsDynResp, FTPS_DYNAMIC_RESPONSE_MAXLEN, "%s (%u,%u,%u,%u,%u,%u)\r\n", // no space in the whole address-port compound
|
||||
ftpEnteringPassivePrefix, EXPLODE_IPv4(s.intf->ip), (s.passiveModePort >> 8) & 0xFF, s.passiveModePort & 0xFF);
|
||||
|
||||
// set response
|
||||
resp = ftpsDynResp;
|
||||
} break;
|
||||
case LIST: { // list files
|
||||
ETHLIB_OS_SEM_WAIT(s.dataConnHasOpened); // wait for data connection to establish
|
||||
tcp_send(s.connCtrl, (const uint8_t *)ftpOpeningDataConn, strlen(ftpOpeningDataConn)); // opening data connection
|
||||
tcp_send(s.connData, (const uint8_t *)"\r\n", 2); // sending listing data
|
||||
tcp_send(s.connCtrl, (const uint8_t *)ftpClosingDataConn, strlen(ftpClosingDataConn)); // closing data connection
|
||||
close_connection(s.connData); // actually close connection
|
||||
s.connData = 0; // clear data connection CBD
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -187,19 +303,41 @@ resp_and_return:
|
||||
if (resp != NULL) {
|
||||
tcp_send(s.connCtrl, (const uint8_t *)resp, strlen(resp));
|
||||
}
|
||||
return;
|
||||
|
||||
// -----
|
||||
syntax_error:
|
||||
resp = ftpSyntaxError;
|
||||
goto resp_and_return;
|
||||
|
||||
cmd_param_not_implemented:
|
||||
resp = ftpCmdNotImplForParam;
|
||||
goto resp_and_return;
|
||||
|
||||
cmd_ok:
|
||||
resp = ftpCmdOk;
|
||||
goto resp_and_return;
|
||||
}
|
||||
|
||||
void ftps_thread(void *param) {
|
||||
// set interface
|
||||
s.intf = get_default_interface();
|
||||
|
||||
// initialize event queue
|
||||
s.eventQ = ETHLIB_OS_QUEUE_CREATE(FTPS_EVENT_QUEUE_LENGTH, uint32_t);
|
||||
|
||||
// initialize control connection
|
||||
s.connCtrlS = tcp_new_connblock(get_default_interface(), IPv4_IF_ADDR, FTPS_CTRL_PORT, ftps_ctrl_recv);
|
||||
s.connCtrlS = tcp_new_connblock(s.intf, IPv4_IF_ADDR, FTPS_CTRL_PORT, ftps_ctrl_recv);
|
||||
tcp_set_accept_callback(s.connCtrlS, ftps_ctrl_accept_cb); // set callback for accepting an incoming connection
|
||||
tcp_listen(s.connCtrlS); // make it listen for incoming connections
|
||||
tcp_set_accept_callback(s.connCtrlS, ftps_accept_cb); // set callback for accepting an incoming connection
|
||||
|
||||
// initialize connection state
|
||||
s.connState = FTPS_IDLE;
|
||||
s.passiveMode = false; // at the beginning we disable passive mode
|
||||
s.passiveModePort = 0;
|
||||
|
||||
// initialize semaphore which synchronizes data connection state
|
||||
s.dataConnHasOpened = ETHLIB_OS_SEM_CREATE(1);
|
||||
|
||||
// process loop
|
||||
for (;;) {
|
||||
|
@ -10,8 +10,6 @@
|
||||
|
||||
#define FTPS_LINEBUF_LEN (63)
|
||||
|
||||
#define FTPS_ROOT_DIR_NAME "ftp_root"
|
||||
|
||||
/**
|
||||
* FTP connection state machine states.
|
||||
*/
|
||||
@ -30,13 +28,20 @@ typedef enum {
|
||||
FTPEVT_CTRL_RECVD, // a control message has been received
|
||||
} Ftps_ConnEvents;
|
||||
|
||||
/**
|
||||
* FTP server state.
|
||||
*/
|
||||
typedef struct {
|
||||
EthInterface * intf; // pointer to associated Ethernet interface
|
||||
bool passiveMode; // indicates whether the server is operating in passive mode
|
||||
uint16_t passiveModePort; // passive mode port once the passive mode is activated
|
||||
cbd connCtrlS; // control connection server
|
||||
cbd connCtrl; // established control connection
|
||||
cbd connDataS; // data connection server
|
||||
cbd connData; // established data connection
|
||||
Ftps_ConnState connState; // connection state
|
||||
ETHLIB_OS_QUEUE_TYPE eventQ; // event queue
|
||||
ETHLIB_OS_SEM_TYPE dataConnHasOpened; // semaphore synchronizing data connection opening
|
||||
char lineBuffer[FTPS_LINEBUF_LEN + 1]; // line buffer
|
||||
} Ftps_State;
|
||||
|
||||
|
@ -11,12 +11,12 @@ typedef int (*ConnBlockTransmitFn)(struct EthInterface_ * intf, const uint8_t *
|
||||
* Connection block.
|
||||
*/
|
||||
typedef struct ConnBlock_ {
|
||||
PcktSieve * sieve; ///< Ethernet interface
|
||||
PcktSieve * sieve; ///< Pointer to the packet sieve
|
||||
PcktSieveLayer * sieveLayer; ///< Sieve layer
|
||||
} ConnBlock;
|
||||
|
||||
typedef struct ConstConnBlock_ {
|
||||
const PcktSieve * sieve; ///< Ethernet interface
|
||||
const PcktSieve * sieve; ///< Pointer to the packet sieve
|
||||
const PcktSieveLayer * sieveLayer; ///< Sieve layer
|
||||
} ConstConnBlock;
|
||||
|
||||
|
@ -91,6 +91,13 @@ void close_connection(cbd d) {
|
||||
|
||||
cbdt_release(E.cbdt, d); // release from CBD table
|
||||
|
||||
// connb_remove() calls pcktsieve_remove_layer() which does not guarantee
|
||||
// that the layer is instantaneously removed, since some protocols (like TCP)
|
||||
// must gracefully shutdown the connection and the protocol implementation
|
||||
// will then remove the layer. As long as the protocol implementation does
|
||||
// not attempt to use the CBD released above, the cleanup actions will
|
||||
// execute silently in the background without raising errors.
|
||||
|
||||
connb_remove(&connBlock); // remove connection block
|
||||
}
|
||||
|
||||
|
@ -257,6 +257,7 @@ PcktSieveLayer* packsieve_new_layer(PcktSieveLayer *parent, const PcktSieveFilte
|
||||
layer->tag = tag;
|
||||
layer->connBReportFn = NULL;
|
||||
layer->txTsCb = NULL;
|
||||
layer->mgmtCb = NULL;
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
@ -267,6 +268,13 @@ bool packsieve_remove_layer(const PcktSieveLayer *layer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// dispath management message if callback is defined
|
||||
if (layer->mgmtCb != NULL) {
|
||||
if (layer->mgmtCb(layer, PSLEVT_REMOVE) == PSMGMT_RET_ABORT) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// remove parent elements if their only subnode is the one we're deleting
|
||||
PcktSieveLayer *parent;
|
||||
while (layer != NULL && layer->nodes == NULL) {
|
||||
|
@ -78,8 +78,28 @@ typedef int (*SieveCallBackFn)(const Pckt *pckt, PcktSieveLayerTag tag);
|
||||
struct ConnBlock_;
|
||||
typedef void (*ConnBlockReportFn)(const struct ConnBlock_ *connBlock);
|
||||
|
||||
/**
|
||||
* Timestamp callback function.
|
||||
*/
|
||||
typedef void(*TxTimeStampCBFn)(uint32_t ts_s, uint32_t ts_ns, uint32_t tag);
|
||||
|
||||
/**
|
||||
* Management event types.
|
||||
*/
|
||||
typedef enum {
|
||||
PSLEVT_NONE, ///< No event.
|
||||
PSLEVT_REMOVE ///< The sievele layer is about to get deleted.
|
||||
} PcktSieveLayerMgmtEvent;
|
||||
|
||||
// Management callback return values
|
||||
#define PSMGMT_RET_PROCEED (0) // proceed with processing
|
||||
#define PSMGMT_RET_ABORT (1) // abort processing
|
||||
|
||||
/**
|
||||
* Packet sieve layer management function.
|
||||
*/
|
||||
typedef int(*MgmtCBFn)(const struct PcktSieveLayer_ * layer, PcktSieveLayerMgmtEvent event);
|
||||
|
||||
#define PCKT_SIEVE_INFOTAG_LEN (24)
|
||||
|
||||
/**
|
||||
@ -97,6 +117,7 @@ typedef struct PcktSieveLayer_ {
|
||||
struct PcktSieveLayer_ *next, *prev; ///< Next and previous sieve layer on the same level
|
||||
struct PcktSieveLayer_ *nodes; ///< Subnodes in the sieve tree
|
||||
ConnBlockReportFn connBReportFn; ///< Connection block report function pointer
|
||||
MgmtCBFn mgmtCb; ///< Management callback function
|
||||
} PcktSieveLayer;
|
||||
|
||||
/**
|
||||
|
@ -27,7 +27,7 @@ void tcpw_set_seqnum_offset(TcpWindow * tcpw, uint32_t offset) {
|
||||
tcpw->firstUnACK = offset;
|
||||
}
|
||||
|
||||
static inline uint32_t tcpw_get_occupied_size(TcpWindow * tcpw) {
|
||||
uint32_t tcpw_get_occupied_size(TcpWindow * tcpw) {
|
||||
return tcpw->nextWritten - tcpw->firstUnACK;
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,15 @@ void tcpw_set_seqnum_offset(TcpWindow * tcpw, uint32_t offset);
|
||||
*/
|
||||
uint32_t tcpw_get_max_window_size(TcpWindow * tcpw);
|
||||
|
||||
/**
|
||||
* TODO: rename function
|
||||
* Get used area size of this window.
|
||||
* @param tcpw pointer to TCP Window object
|
||||
* @return used area size in bytes
|
||||
*
|
||||
*/
|
||||
uint32_t tcpw_get_occupied_size(TcpWindow * tcpw);
|
||||
|
||||
/** TODO: rename function
|
||||
* Store TCP segment
|
||||
* @param tcpw pointer to TCP Window object
|
||||
|
@ -45,7 +45,7 @@ static bool filtTcp(const PcktSieveFilterCondition *filtCond, const PcktProps *c
|
||||
(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;
|
||||
return (ipProps->Protocol == ETH_TCP_PACKET_CLASS) && references_us;
|
||||
}
|
||||
|
||||
#define MAX_TCP_WINDOW_SIZE (1200)
|
||||
@ -57,15 +57,16 @@ static inline TcpState *tcps_create() {
|
||||
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);
|
||||
|
||||
ETHLIB_OS_SEM_POST(tcps->txInProgress); // post the TX in progress, since no transmission is in progress
|
||||
tcps->eventFlags = osEventFlagsNew(NULL); // create event flags for internal event synchronization
|
||||
osEventFlagsSet(tcps->eventFlags, EVTFLAG_ALL_ACKED); // everything is ACKed in the beginning
|
||||
tcps->processMtx = ETHLIB_OS_RECMTX_CREATE(); // create mutex protecting the internal state
|
||||
return tcps;
|
||||
}
|
||||
|
||||
static inline void tcps_release_client_related_objects(TcpState *tcps) {
|
||||
tcpw_destroy(tcps->txWin);
|
||||
ETHLIB_OS_SEM_DESTROY(tcps->txBufNotFull);
|
||||
ETHLIB_OS_SEM_DESTROY(tcps->txInProgress);
|
||||
eth_bfifo_destroy(tcps->rxFifo);
|
||||
}
|
||||
|
||||
@ -78,6 +79,10 @@ static inline void tcps_remove_and_cleanup(TcpState *tcps) {
|
||||
tcps_release_client_related_objects(tcps);
|
||||
}
|
||||
|
||||
ETHLIB_OS_SEM_DESTROY(tcps->txInProgress); // TODO: ezt a szerver bezárásakor nem kell vizsgálni
|
||||
osEventFlagsDelete(tcps->eventFlags);
|
||||
ETHLIB_OS_MTX_DESTROY(tcps->processMtx);
|
||||
|
||||
dynmem_free(tcps);
|
||||
}
|
||||
|
||||
@ -147,10 +152,76 @@ static inline cbd tcps_create_based_on_listening(const TcpState *source, uint16_
|
||||
return d;
|
||||
}
|
||||
|
||||
// ----------------------------
|
||||
|
||||
static bool tcp_close(TcpState *tcps) {
|
||||
TcpConnectionState cs = tcps->connState;
|
||||
|
||||
// a closed connection cannot be more closed...
|
||||
if (cs == TCP_STATE_CLOSED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// act according to the current state
|
||||
switch (cs) {
|
||||
case TCP_STATE_ESTAB: // close connecion from established state
|
||||
// take send semaphore
|
||||
ETHLIB_OS_SEM_WAIT(tcps->txInProgress);
|
||||
|
||||
MSG("TX SEM\n");
|
||||
|
||||
// wait for the TX FIFO contents to get transmitted
|
||||
osEventFlagsWait(tcps->eventFlags, EVTFLAG_ALL_ACKED, osFlagsWaitAll | osFlagsNoClear, osWaitForever); // FIXME: ez így nem lesz jó, mert, ha valamiért nem kapjuk meg az ACK-t, akkor itt ragadunk
|
||||
|
||||
MSG("FLAG\n");
|
||||
|
||||
// lock the mutex -----
|
||||
ETHLIB_OS_MTX_LOCK(tcps->processMtx);
|
||||
// --------------------
|
||||
|
||||
// send a FIN-ACK segment
|
||||
tcp_send_segment(&(tcps->connBlock), TCP_FLAG_FIN | TCP_FLAG_ACK, NULL, NULL, 0);
|
||||
tcps->txSeqNum++; // increase own sequence number
|
||||
|
||||
// step into FIN WAIT-1 state
|
||||
tcps->connState = TCP_STATE_FIN_WAIT_1;
|
||||
|
||||
// unlock the mutex -----
|
||||
ETHLIB_OS_MTX_UNLOCK(tcps->processMtx);
|
||||
// --------------------
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int tcp_mgmt_cb(const PcktSieveLayer *layer, PcktSieveLayerMgmtEvent event) {
|
||||
TcpState *tcps = TCP_FETCH_STATE_FROM_SIEVE_LAYER(layer);
|
||||
|
||||
switch (event) {
|
||||
case PSLEVT_REMOVE: { // connection removal is in progress
|
||||
return tcp_close(tcps) ? PSMGMT_RET_PROCEED : PSMGMT_RET_ABORT; // abort further removal processing if further steps are required to close the connection
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return PSMGMT_RET_PROCEED;
|
||||
}
|
||||
|
||||
// ----------------------------
|
||||
|
||||
int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag 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
|
||||
|
||||
// lock the mutex -----
|
||||
ETHLIB_OS_MTX_LOCK(tcps->processMtx);
|
||||
// --------------------
|
||||
|
||||
TcpConnectionState beginState = tcps->connState;
|
||||
|
||||
IPv4Props *ipProps = HEADER_FETCH_PROPS(IPv4Props, pckt->header->prev);
|
||||
@ -175,6 +246,8 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) {
|
||||
// store rxAckNum
|
||||
tcps->rxAckNum = tcpProps->SequenceNumber + 1;
|
||||
|
||||
// MSG("SYN ACK\n");
|
||||
|
||||
// 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
|
||||
@ -205,6 +278,9 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) {
|
||||
|
||||
// step into next state
|
||||
tcps->connState = TCP_STATE_ESTAB;
|
||||
|
||||
// set event flags
|
||||
osEventFlagsSet(tcps->eventFlags, EVTFLAG_ESTAB);
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,8 +323,7 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) {
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case TCP_STATE_ESTAB: /* Connection established */
|
||||
// if the other end tries to close down the connection
|
||||
@ -282,6 +357,13 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) {
|
||||
// release segment from retransmit window
|
||||
tcpw_acknowledge(tcps->txWin, tcpProps->AcknowledgementNumber);
|
||||
|
||||
// set or clear all ACKed flag according to TX window state
|
||||
if (tcpw_get_occupied_size(tcps->txWin) == 0) {
|
||||
osEventFlagsSet(tcps->eventFlags, EVTFLAG_ALL_ACKED);
|
||||
} else {
|
||||
osEventFlagsClear(tcps->eventFlags, EVTFLAG_ALL_ACKED);
|
||||
}
|
||||
|
||||
// release the transmit semaphore
|
||||
ETHLIB_OS_SEM_POST(tcps->txBufNotFull);
|
||||
|
||||
@ -296,12 +378,51 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) {
|
||||
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);
|
||||
// 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_FIN_WAIT_1: { /* our FIN has been sent, waiting for the peer's one */
|
||||
uint16_t flags = tcpProps->Flags;
|
||||
bool rxACKok = tcpProps->AcknowledgementNumber == tcps->txSeqNum; // check if received ACK was correct
|
||||
bool sendACK = false; // set if we want to emit an ACK
|
||||
|
||||
if ((flags & TCP_FLAG_ACK) && (!(flags & TCP_FLAG_FIN))) { // only ACK received
|
||||
tcps->connState = TCP_STATE_FIN_WAIT_2; // this case don't send ACK
|
||||
} else if ((!(flags & TCP_FLAG_ACK)) && (flags & TCP_FLAG_FIN)) { // only FIN received
|
||||
tcps->connState = TCP_STATE_CLOSING;
|
||||
sendACK = true;
|
||||
} else if ((flags & TCP_FLAG_ACK) && (flags & TCP_FLAG_FIN)) { // received ACK and FIN
|
||||
tcps->connState = TCP_STATE_CLOSED;
|
||||
ret = SIEVE_LAYER_REMOVE_THIS;
|
||||
sendACK = true;
|
||||
}
|
||||
|
||||
// send ACK if required and received ACK was correct
|
||||
if (sendACK && rxACKok) {
|
||||
tcps->rxAckNum++; // acknowledge the FIN-ACK packet
|
||||
tcp_send_segment(&(tcps->connBlock), TCP_FLAG_ACK, NULL, NULL, 0);
|
||||
}
|
||||
} break;
|
||||
|
||||
case TCP_STATE_FIN_WAIT_2: { /* waiting on peer's FIN */
|
||||
if (tcpProps->Flags & TCP_FLAG_FIN) {
|
||||
tcps->rxAckNum++; // acknowledge the FIN packet
|
||||
tcp_send_segment(&(tcps->connBlock), TCP_FLAG_ACK, NULL, NULL, 0);
|
||||
tcps->connState = TCP_STATE_CLOSED;
|
||||
ret = SIEVE_LAYER_REMOVE_THIS;
|
||||
}
|
||||
} break;
|
||||
|
||||
case TCP_STATE_CLOSING: /* waiting on arrival of ACK sent by the peer against our FIN */ /* connection is closing waiting on peers ACK */
|
||||
if ((tcpProps->Flags & TCP_FLAG_ACK) && (tcpProps->AcknowledgementNumber == tcps->txSeqNum)) { // ACK received
|
||||
tcps->connState = TCP_STATE_CLOSED;
|
||||
ret = SIEVE_LAYER_REMOVE_THIS;
|
||||
}
|
||||
break;
|
||||
|
||||
case TCP_STATE_CLOSED: /* connection is CLOSED */
|
||||
default:
|
||||
break;
|
||||
@ -323,8 +444,13 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) {
|
||||
tcpopt_chain_free(tcpProps->options);
|
||||
}
|
||||
|
||||
// unlock the mutex -----
|
||||
ETHLIB_OS_MTX_UNLOCK(tcps->processMtx);
|
||||
// --------------------
|
||||
|
||||
// release descriptor if not meaningful anymore
|
||||
if (ret == SIEVE_LAYER_REMOVE_THIS) {
|
||||
tcps->connBlock.sieveLayer->mgmtCb = NULL;
|
||||
cbdt_release(E.cbdt, tcps->d); // release descriptor
|
||||
tcps_remove_and_cleanup(tcps); // release TCP state
|
||||
}
|
||||
@ -332,6 +458,7 @@ int tcp_receive_segment_cb(const Pckt *pckt, PcktSieveLayerTag tag) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: mindent egy függvénybe kellene tenni, mert így nagyon sok versenyhelyet alakulhat ki
|
||||
void tcp_bind(cbd d, ip4_addr remoteAddr, uint16_t remotePort) {
|
||||
ConnBlock connBlock;
|
||||
if (!cbdt_get_connection_block(E.cbdt, d, &connBlock)) {
|
||||
@ -339,8 +466,14 @@ void tcp_bind(cbd d, ip4_addr remoteAddr, uint16_t remotePort) {
|
||||
return;
|
||||
}
|
||||
|
||||
// store remote station data
|
||||
// fetch TCP state
|
||||
TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(&connBlock);
|
||||
|
||||
// lock the mutex -----
|
||||
ETHLIB_OS_MTX_LOCK(tcps->processMtx);
|
||||
// --------------------
|
||||
|
||||
// set peer address and port
|
||||
tcps->remoteAddr = remoteAddr;
|
||||
tcps->remotePort = remotePort;
|
||||
|
||||
@ -359,6 +492,13 @@ void tcp_bind(cbd d, ip4_addr remoteAddr, uint16_t remotePort) {
|
||||
|
||||
// step into SYN SENT state
|
||||
tcps->connState = TCP_STATE_SYN_SENT;
|
||||
|
||||
// unlock the mutex -----
|
||||
ETHLIB_OS_MTX_UNLOCK(tcps->processMtx);
|
||||
// --------------------
|
||||
|
||||
// wait for getting into Established state
|
||||
osEventFlagsWait(tcps->eventFlags, EVTFLAG_ESTAB, osFlagsWaitAll | osFlagsNoClear, osWaitForever);
|
||||
}
|
||||
|
||||
void tcp_listen(cbd d) {
|
||||
@ -368,12 +508,22 @@ void tcp_listen(cbd d) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fetch state
|
||||
TcpState *tcps = TCP_FETCH_STATE_FROM_CONNBLOCK(&connBlock);
|
||||
|
||||
// lock the mutex -----
|
||||
ETHLIB_OS_MTX_LOCK(tcps->processMtx);
|
||||
// --------------------
|
||||
|
||||
if ((tcps->streamCb != NULL) && (tcps->connState == TCP_STATE_CLOSED)) { // step into listen state
|
||||
tcps->connState = TCP_STATE_LISTEN;
|
||||
tcps->isServer = true;
|
||||
tcps_release_client_related_objects(tcps);
|
||||
tcps_release_client_related_objects(tcps); // release client-only resources
|
||||
}
|
||||
|
||||
// unlock the mutex -----
|
||||
ETHLIB_OS_MTX_UNLOCK(tcps->processMtx);
|
||||
// --------------------
|
||||
}
|
||||
|
||||
void tcp_set_accept_callback(cbd d, TcpAcceptCbFn acb) {
|
||||
@ -415,6 +565,7 @@ static inline cbd tcp_new_connblock_filtcond(EthInterface *intf, ip4_addr ipAddr
|
||||
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;
|
||||
tcpConnB.sieveLayer->mgmtCb = tcp_mgmt_cb;
|
||||
|
||||
tcpConnB.sieve = &intf->sieve;
|
||||
tcpState->connBlock = tcpConnB; // also store connection block parameters
|
||||
@ -565,6 +716,9 @@ uint32_t tcp_send(cbd d, const uint8_t *data, uint32_t size) {
|
||||
// store segment for possible retransmission
|
||||
uint32_t txSize = tcpw_store(tcps->txWin, data, sizeLeft, tcps->txSeqNum);
|
||||
|
||||
// clear all ACK-ed flag
|
||||
osEventFlagsClear(tcps->eventFlags, EVTFLAG_ALL_ACKED);
|
||||
|
||||
// send segment with data (no options)
|
||||
tcp_send_segment(&connBlock, TCP_FLAG_ACK, NULL, data, txSize);
|
||||
|
||||
|
@ -52,6 +52,9 @@ typedef enum {
|
||||
typedef int (*StreamCallBackFn)(cbd d);
|
||||
typedef int (*TcpAcceptCbFn)(cbd d);
|
||||
|
||||
#define EVTFLAG_ESTAB (0x00000001)
|
||||
#define EVTFLAG_ALL_ACKED (0x00000002)
|
||||
|
||||
/**
|
||||
* TCP state.
|
||||
*/
|
||||
@ -103,11 +106,20 @@ typedef struct {
|
||||
|
||||
// ---------------
|
||||
|
||||
ETHLIB_OS_MTX_TYPE processMtx; ///< a mutex protecting TCP state changes (this is how we can manipulate the internal state without raising race conditions)
|
||||
|
||||
// ---------------
|
||||
|
||||
osEventFlagsId_t eventFlags; ///< Semaphore for synchronizing on getting into established state
|
||||
|
||||
// ---------------
|
||||
|
||||
EthBlockingFifo * rxFifo; ///< Receive FIFO
|
||||
|
||||
} TcpState;
|
||||
|
||||
#define TCP_FETCH_STATE_FROM_CONNBLOCK(connBlock) ((TcpState *) (connBlock)->sieveLayer->tag.p)
|
||||
#define TCP_FETCH_STATE_FROM_SIEVE_LAYER(layer) ((TcpState *) (layer)->tag.p)
|
||||
|
||||
/**
|
||||
* Create new TCP connection block
|
||||
|
Loading…
x
Reference in New Issue
Block a user