From d3aae1faa01ae8fa2156fe266c9e6a5bfe785ea2 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Wed, 19 Mar 2014 22:12:16 +0800 Subject: [PATCH] NetTest - Clean up TCP code a little, add lost packet checks --- Tools/NetTest_Runner/include/tcp.h | 16 ++- Tools/NetTest_Runner/tcp.c | 20 +++- Tools/NetTest_Runner/test_tcp.c | 153 ++++++++++++++++++++--------- Tools/nativelib/logging.c | 43 ++++++-- 4 files changed, 171 insertions(+), 61 deletions(-) diff --git a/Tools/NetTest_Runner/include/tcp.h b/Tools/NetTest_Runner/include/tcp.h index 9446b3c9..eb7edf55 100644 --- a/Tools/NetTest_Runner/include/tcp.h +++ b/Tools/NetTest_Runner/include/tcp.h @@ -9,6 +9,18 @@ #include #include +typedef struct { + int IFNum; + int AF; + const void *LAddr; + const void *RAddr; + uint16_t LPort; + uint16_t RPort; + uint16_t Window; + uint32_t LSeq; + uint32_t RSeq; +} tTCPConn; + #define TCP_FIN 0x01 #define TCP_SYN 0x02 #define TCP_RST 0x04 @@ -16,12 +28,14 @@ #define TCP_ACK 0x10 #define TCP_URG 0x20 -extern void TCP_Send(int IF, int AF, const void *IP, short sport, short dport, uint32_t seq, uint32_t ack, uint8_t flags, uint16_t window, size_t data_len, const void *data); +extern void TCP_SendC(const tTCPConn *Conn, uint8_t flags, size_t data_len, const void *data); +extern void TCP_Send(int IF, int AF, const void *IP, short sport, short dport, uint32_t seq, uint32_t ack, uint8_t flags, uint16_t window, size_t data_len, const void *data); // The following skip the next check of each field extern void TCP_SkipCheck_Seq(bool Skip); +extern bool TCP_Pkt_CheckC(size_t len, const void *data, size_t *ofs, size_t *out_len, const tTCPConn *Conn, uint8_t flags); extern bool TCP_Pkt_Check(size_t len, const void *data, size_t *ofs, size_t *out_len, int AF, const void *IP, short sport, short dport, uint32_t seq, uint32_t ack, uint8_t flags); // - Get a field from a previously validated packet diff --git a/Tools/NetTest_Runner/tcp.c b/Tools/NetTest_Runner/tcp.c index 2bdc3af1..d5c76372 100644 --- a/Tools/NetTest_Runner/tcp.c +++ b/Tools/NetTest_Runner/tcp.c @@ -39,6 +39,11 @@ uint16_t TCP_int_GetPseudoHeader(int AF, const void *SrcAddr, const void *DstAdd } } +void TCP_SendC(const tTCPConn *Conn, uint8_t flags, size_t data_len, const void *data) +{ + TCP_Send(Conn->IFNum, Conn->AF, Conn->RAddr, Conn->LPort, Conn->RPort, + Conn->LSeq, Conn->RSeq, flags, Conn->Window, data_len, data); +} void TCP_Send(int IF, int AF, const void *IP, short sport, short dport, uint32_t seq, uint32_t ack, uint8_t flags, uint16_t window, size_t data_len, const void *data @@ -76,6 +81,15 @@ void TCP_SkipCheck_Seq(bool Skip) { gTCP_Skips.Seq = Skip; } + +bool TCP_Pkt_CheckC(size_t len, const void *data, size_t *out_ofs, size_t *len_out, + const tTCPConn *conn, uint8_t flags) +{ + return TCP_Pkt_Check(len, data, out_ofs, len_out, + conn->AF, conn->RAddr, conn->RPort, conn->LPort, conn->RSeq, conn->LSeq, flags + ); +} + bool TCP_Pkt_Check(size_t len, const void *data, size_t *out_ofs, size_t *len_out, int AF, const void *IP, short sport, short dport, uint32_t seq, uint32_t ack, uint8_t flags) @@ -83,7 +97,6 @@ bool TCP_Pkt_Check(size_t len, const void *data, size_t *out_ofs, size_t *len_ou size_t ofs, rlen; if( !IP_Pkt_Check(len, data, &ofs, &rlen, AF, IP, BLOB(HOST_IP), IPPROTO_TCP) ) return false; - // TODO: IP has its own length field, use that? tTCPHeader hdr; TEST_ASSERT_REL(rlen, >=, sizeof(hdr)); @@ -93,7 +106,7 @@ bool TCP_Pkt_Check(size_t len, const void *data, size_t *out_ofs, size_t *len_ou if( !gTCP_Skips.SPort ) TEST_ASSERT_REL( ntohs(hdr.SPort), ==, sport ); TEST_ASSERT_REL( ntohs(hdr.DPort), ==, dport ); if( !gTCP_Skips.Seq ) TEST_ASSERT_REL( ntohl(hdr.Seq), ==, seq ); - if( !gTCP_Skips.Ack ) TEST_ASSERT_REL( ntohl(hdr.Ack), ==, ack ); + if( flags & TCP_ACK ) TEST_ASSERT_REL( ntohl(hdr.Ack), ==, ack ); TEST_ASSERT_REL( hdr.Flags, ==, flags); uint16_t real_cksum = htons(hdr.Checksum); @@ -111,7 +124,8 @@ bool TCP_Pkt_Check(size_t len, const void *data, size_t *out_ofs, size_t *len_ou return true; } -uint32_t TCP_Pkt_GetSeq(size_t len, const void *data, int AF) { +uint32_t TCP_Pkt_GetSeq(size_t len, const void *data, int AF) +{ size_t ofs, rlen; IP_Pkt_Check(len, data, &ofs, &rlen, AF, NULL, NULL, IPPROTO_TCP); diff --git a/Tools/NetTest_Runner/test_tcp.c b/Tools/NetTest_Runner/test_tcp.c index e57398f5..9c4e2e4d 100644 --- a/Tools/NetTest_Runner/test_tcp.c +++ b/Tools/NetTest_Runner/test_tcp.c @@ -23,122 +23,179 @@ bool Test_TCP_Basic(void) char rxbuf[MTU]; const int ERX_TIMEOUT = 1000; // Expect RX timeout (timeout=failure) const int NRX_TIMEOUT = 250; // Not expect RX timeout (timeout=success) - + const int RETX_TIMEOUT = 1000; // OS PARAM - Retransmit timeout + const int LOST_TIMEOUT = 1000; // OS PARAM - Time before sending an ACK + const int DACK_TIMEOUT = 500; // OS PARAM - Timeout for delayed ACKs + const size_t DACK_BYTES = 4096; // OS PARAM - Threshold for delayed ACKs + + tTCPConn testconn = { + .IFNum = 0, .AF = 4, + .RAddr = BLOB(TEST_IP), + .LAddr = BLOB(HOST_IP), + .RPort = 80, + .LPort = 11200, + .Window = 0x1000, + .LSeq = 0x1000, + .RSeq = 0, + }; + const char testblob[] = "HelloWorld, this is some random testing data for TCP\xFF\x00\x66\x12\x12"; const size_t testblob_len = sizeof(testblob); // 1. Test packets to closed port // > RFC793 Pg.65 - const uint16_t our_window = 0x1000; - uint32_t seq_tx = 0x1000; - uint32_t seq_exp = 0x33456; // 1.1. Send SYN packet - TCP_Send(0, 4, BLOB(TEST_IP), 1234, 80, seq_tx, seq_exp, TCP_SYN, 0x1000, testblob_len, testblob); - // Expect a TCP_RST|TCP_ACK with SEQ=0,ACK=SEQ+LEN + TCP_SendC(&testconn, TCP_SYN, testblob_len, testblob); + testconn.RSeq = 0; + testconn.LSeq += testblob_len; + // Expect RST,ACK with SEQ=0 TEST_ASSERT_rx(); - TEST_ASSERT( TCP_Pkt_Check(rxlen, rxbuf, &ofs, &len, 4, BLOB(TEST_IP), 80, 1234, - 0, seq_tx+testblob_len, TCP_RST|TCP_ACK) ); + TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_RST|TCP_ACK) ); TEST_ASSERT_REL(ofs, ==, rxlen); // 1.2. Send a SYN,ACK packet - TCP_Send(0, 4, BLOB(TEST_IP), 1234, 80, seq_tx, seq_exp, TCP_SYN|TCP_ACK, 0x1000, 0, NULL); + testconn.RSeq = 12345; + TCP_SendC(&testconn, TCP_SYN|TCP_ACK, 0, NULL); // Expect a TCP_RST with SEQ=ACK TEST_ASSERT_rx(); - TEST_ASSERT( TCP_Pkt_Check(rxlen, rxbuf, &ofs, &len, 4, BLOB(TEST_IP), 80, 1234, seq_exp, seq_tx+0, TCP_RST) ); + TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_RST) ); TEST_ASSERT_REL(ofs, ==, rxlen); + testconn.LSeq ++; // 1.3. Send a RST packet - TCP_Send(0, 4, BLOB(TEST_IP), 1234, 80, seq_tx, seq_exp, TCP_RST, 0x1000, 0, NULL); + TCP_SendC(&testconn, TCP_RST, 0, NULL); // Expect nothing TEST_ASSERT_no_rx(); + testconn.LSeq ++; // 1.3. Send a RST,ACK packet - TCP_Send(0, 4, BLOB(TEST_IP), 1234, 80, seq_tx, seq_exp, TCP_RST|TCP_ACK, 0x1000, 0, NULL); + TCP_SendC(&testconn, TCP_RST|TCP_ACK, 0, NULL); // Expect nothing TEST_ASSERT_no_rx(); + testconn.LSeq ++; // 2. Establishing connection with a server - const int server_port = 7; - const int local_port = 11234; - Stack_SendCommand("tcp_echo_server %i", server_port); + testconn.RPort = 7; + testconn.LPort = 11239; + Stack_SendCommand("tcp_echo_server %i", testconn.RPort); // >>> STATE: LISTEN // 2.1. Send RST - TCP_Send(0, 4, BLOB(TEST_IP), local_port, server_port, seq_tx, seq_exp, TCP_RST, our_window, 0, NULL); + TCP_SendC(&testconn, TCP_RST, 0, NULL); // - Expect nothing TEST_ASSERT_no_rx(); // 2.2. Send ACK - TCP_Send(0, 4, BLOB(TEST_IP), local_port, server_port, seq_tx, seq_exp, TCP_ACK, our_window, 0, NULL); + TCP_SendC(&testconn, TCP_ACK, 0, NULL); // - Expect RST TEST_ASSERT_rx(); - TEST_ASSERT( TCP_Pkt_Check(rxlen, rxbuf, &ofs, &len, 4, BLOB(TEST_IP), - server_port, local_port, seq_exp, seq_tx+0, TCP_RST) ); + TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_RST) ); + TEST_ASSERT_REL(ofs, ==, rxlen); // 2.3. Begin hanshake (SYN) - // TODO: "If the SYN bit is set, check the security." - TCP_Send(0, 4, BLOB(TEST_IP), local_port, server_port, seq_tx, 0, TCP_SYN, our_window, 0, NULL); + // TODO: Test "If the SYN bit is set, check the security." + TCP_SendC(&testconn, TCP_SYN, 0, NULL); + testconn.LSeq ++; // - Expect SYN,ACK with ACK == SEQ+1 TEST_ASSERT_rx(); TCP_SkipCheck_Seq(true); - TEST_ASSERT( TCP_Pkt_Check(rxlen, rxbuf, &ofs, &len, 4, BLOB(TEST_IP), - server_port, local_port, 0, seq_tx+1, TCP_SYN|TCP_ACK) ); - seq_exp = TCP_Pkt_GetSeq(rxlen, rxbuf, 4); + TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_SYN|TCP_ACK) ); + testconn.RSeq = TCP_Pkt_GetSeq(rxlen, rxbuf, testconn.AF) + 1; // >>> STATE: SYN-RECEIVED // TODO: Test other transitions from SYN-RECEIVED - + // 2.4. Complete handshake, TCP ACK - seq_exp ++; - seq_tx ++; - TCP_Send(0,4,BLOB(TEST_IP), local_port, server_port, seq_tx, seq_exp, TCP_ACK, our_window, 0, NULL); + TCP_SendC(&testconn, TCP_ACK, 0, NULL); // - Expect nothing TEST_ASSERT_no_rx(); // >>> STATE: ESTABLISHED // 2.5. Send data - TCP_Send(0,4,BLOB(TEST_IP), local_port, server_port, seq_tx, seq_exp, - TCP_ACK|TCP_PSH, our_window, testblob_len, testblob); - seq_tx += testblob_len; - // Expect burst delayed ACK - TEST_ASSERT_rx(); - TEST_ASSERT( TCP_Pkt_Check(rxlen, rxbuf, &ofs, &len, 4, BLOB(TEST_IP), - server_port, local_port, seq_exp, seq_tx, TCP_ACK) ); - TEST_ASSERT_REL( len, ==, 0 ); + TCP_SendC(&testconn, TCP_ACK|TCP_PSH, testblob_len, testblob); + testconn.LSeq += testblob_len; // Expect echoed reponse with ACK TEST_ASSERT_rx(); - TEST_ASSERT( TCP_Pkt_Check(rxlen, rxbuf, &ofs, &len, 4, BLOB(TEST_IP), - server_port, local_port, seq_exp, seq_tx, TCP_ACK|TCP_PSH) ); + TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_ACK|TCP_PSH) ); TEST_ASSERT_REL( len, ==, testblob_len ); TEST_ASSERT( memcmp(rxbuf + ofs, testblob, testblob_len) == 0 ); - seq_exp += testblob_len; + testconn.RSeq += testblob_len; + + // Send something short + const char testblob2[] = "test blob two."; + const size_t testblob2_len = sizeof(testblob2); + TCP_SendC(&testconn, TCP_ACK|TCP_PSH, testblob2_len, testblob2); + testconn.LSeq += testblob2_len; + // Expect response with data and ACK + TEST_ASSERT_rx(); + TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_ACK|TCP_PSH) ); + TEST_ASSERT_REL( len, ==, testblob2_len ); + TEST_ASSERT( memcmp(rxbuf + ofs, testblob2, testblob2_len) == 0 ); + + // Wait for just under retransmit time, expecting nothing + #if TEST_TIMERS + TEST_ASSERT( 0 == Net_Receive(0, sizeof(rxbuf), rxbuf, RETX_TIMEOUT-100) ); + // Now expect the previous message + TEST_ASSERT_rx(); + TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_ACK|TCP_PSH) ); + TEST_ASSERT_REL( len, ==, testblob2_len ); + TEST_ASSERT( memcmp(rxbuf + ofs, testblob2, testblob2_len) == 0 ); + #else + TEST_WARN("Not testing retransmit timer"); + #endif + testconn.RSeq += testblob2_len; + + // Send explicit acknowledgement + TCP_SendC(&testconn, TCP_ACK, 0, NULL); + TEST_ASSERT_no_rx(); + + // TODO: Test delayed ACKs (Timeout and data) + // > Requires inhibiting the server's echo response? + + // === Test out-of-order packets === + testconn.LSeq += testblob2_len; // raise sequence number + TCP_SendC(&testconn, TCP_ACK|TCP_PSH, testblob_len, testblob); + // - previous data has not been sent, expect no response for () + // TODO: Should this ACK be delayed? + //TEST_ASSERT_no_rx(); + // - Expect an ACK of the highest received packet + testconn.LSeq -= testblob2_len; + TEST_ASSERT_rx(); + TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_ACK) ); + TEST_ASSERT_REL( len, ==, 0 ); + // - Send missing data + TCP_SendC(&testconn, TCP_ACK, testblob2_len, testblob2); + testconn.LSeq += testblob_len+testblob2_len; // raise sequence number + // - Expect echo response with all sent data + TEST_ASSERT_rx(); + TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_ACK|TCP_PSH) ); + TEST_ASSERT_REL( len, ==, testblob_len+testblob2_len ); + TEST_ASSERT( memcmp(rxbuf + ofs, testblob2, testblob2_len) == 0 ); + TEST_ASSERT( memcmp(rxbuf + ofs+testblob2_len, testblob, testblob_len) == 0 ); + testconn.RSeq += len; // 2.6. Close connection (TCP FIN) - TCP_Send(0,4,BLOB(TEST_IP), local_port, server_port, seq_tx, seq_exp, - TCP_ACK|TCP_FIN, our_window, 0, NULL); - seq_tx ++; // Empty = 1 byte + TCP_SendC(&testconn, TCP_ACK|TCP_FIN, 0, NULL); + testconn.LSeq ++; // Empty = 1 byte // Expect ACK? (Does acess do delayed ACKs here?) TEST_ASSERT_rx(); - TEST_ASSERT( TCP_Pkt_Check(rxlen, rxbuf, &ofs, &len, 4, BLOB(TEST_IP), - server_port, local_port, seq_exp, seq_tx, TCP_ACK) ); + TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_ACK) ); TEST_ASSERT_REL( len, ==, 0 ); // >>> STATE: CLOSE WAIT // Expect FIN TEST_ASSERT_rx(); - TEST_ASSERT( TCP_Pkt_Check(rxlen, rxbuf, &ofs, &len, 4, BLOB(TEST_IP), - server_port, local_port, seq_exp, 0, TCP_FIN) ); + TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_FIN) ); TEST_ASSERT_REL( len, ==, 0 ); // >>> STATE: LAST-ACK // 2.7 Send ACK of FIN - TCP_Send(0,4,BLOB(TEST_IP), local_port, server_port, seq_tx, seq_exp, - TCP_ACK, our_window, 0, NULL); + TCP_SendC(&testconn, TCP_ACK, 0, NULL); // Expect no response TEST_ASSERT_no_rx(); diff --git a/Tools/nativelib/logging.c b/Tools/nativelib/logging.c index 292b8a52..959a9cc6 100644 --- a/Tools/nativelib/logging.c +++ b/Tools/nativelib/logging.c @@ -78,6 +78,10 @@ void Debug(const char *Message, ...) { PUTERR("38", "D") } +char _prn(char byte) +{ + return (' ' <= byte && byte <= 'z') ? byte : '.'; +} void Debug_HexDump(const char *Prefix, const void *Data, size_t Length) { const uint8_t *data = Data; @@ -86,23 +90,44 @@ void Debug_HexDump(const char *Prefix, const void *Data, size_t Length) fprintf(stderr, "[HexDump ]d %s: %i bytes\n", Prefix, (int)Length); for( ofs = 0; ofs + 16 <= Length; ofs += 16 ) { + const uint8_t *d = data + ofs; fprintf(stderr, "[HexDump ]d %s:", Prefix); fprintf(stderr, " %02x %02x %02x %02x %02x %02x %02x %02x", - data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); - data += 8; + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]); fprintf(stderr, " %02x %02x %02x %02x %02x %02x %02x %02x", - data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); - data += 8; + d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + fprintf(stderr, " |%c%c%c%c""%c%c%c%c %c%c%c%c""%c%c%c%c", + _prn(d[ 0]), _prn(d[ 1]), _prn(d[ 2]), _prn(d[ 3]), + _prn(d[ 4]), _prn(d[ 5]), _prn(d[ 6]), _prn(d[ 7]), + _prn(d[ 8]), _prn(d[ 9]), _prn(d[10]), _prn(d[11]), + _prn(d[12]), _prn(d[13]), _prn(d[14]), _prn(d[15]) + ); fprintf(stderr, "\n"); } - fprintf(stderr, "[HexDump ]d %s:", Prefix); - for( ; ofs < Length; ofs ++ ) + if( ofs < Length ) { - if( ofs % 8 == 0 ) fprintf(stderr, " "); - fprintf(stderr, " %02x", data[ofs%16]); + const uint8_t *d = data + ofs; + fprintf(stderr, "[HexDump ]d %s: ", Prefix); + for( int i = 0; i < Length - ofs; i ++ ) + { + if( i == 8 ) fprintf(stderr, " "); + fprintf(stderr, " %02x", d[i]); + } + for( int i = Length - ofs; i < 16; i ++ ) + { + if( i == 8 ) fprintf(stderr, " "); + fprintf(stderr, " "); + } + fprintf(stderr, " |"); + for( int i = 0; i < Length - ofs; i ++ ) + { + if( i == 8 ) fprintf(stderr, " "); + fprintf(stderr, "%c", _prn(d[i])); + } + + fprintf(stderr, "\n"); } - fprintf(stderr, "\n"); LOG_LOCK_RELEASE(); } -- 2.20.1