diff --git a/src/decode-tcp.c b/src/decode-tcp.c index a1b1c11e13..ada2381929 100644 --- a/src/decode-tcp.c +++ b/src/decode-tcp.c @@ -367,6 +367,134 @@ static int TCPV6CalculateInvalidChecksumtest04(void) (uint16_t *)(raw_ipv6 + 54), 32)); } +/** \test Get the wscale of 2 */ +static int TCPGetWscaleTest01(void) +{ + int retval = 0; + static uint8_t raw_tcp[] = {0xda, 0xc1, 0x00, 0x50, 0xb6, 0x21, 0x7f, 0x58, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x16, 0xd0, + 0x8a, 0xaf, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, + 0x04, 0x02, 0x08, 0x0a, 0x00, 0x62, 0x88, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x02}; + Packet p; + IPV4Hdr ip4h; + ThreadVars tv; + DecodeThreadVars dtv; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + memset(&dtv, 0, sizeof(DecodeThreadVars)); + memset(&ip4h, 0, sizeof(IPV4Hdr)); + + p.src.family = AF_INET; + p.dst.family = AF_INET; + p.ip4h = &ip4h; + + + FlowInitConfig(FLOW_QUIET); + DecodeTCP(&tv, &dtv, &p, raw_tcp, sizeof(raw_tcp), NULL); + FlowShutdown(); + + if (p.tcph == NULL) { + printf("tcp packet decode failed: "); + goto end; + } + + uint8_t wscale = TCP_GET_WSCALE(&p); + if (wscale != 2) { + printf("wscale %"PRIu8", expected 2: ", wscale); + goto end; + } + + retval = 1; +end: + return retval; +} + +/** \test Get the wscale of 15, so see if return 0 properly */ +static int TCPGetWscaleTest02(void) +{ + int retval = 0; + static uint8_t raw_tcp[] = {0xda, 0xc1, 0x00, 0x50, 0xb6, 0x21, 0x7f, 0x58, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x16, 0xd0, + 0x8a, 0xaf, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, + 0x04, 0x02, 0x08, 0x0a, 0x00, 0x62, 0x88, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x0f}; + Packet p; + IPV4Hdr ip4h; + ThreadVars tv; + DecodeThreadVars dtv; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + memset(&dtv, 0, sizeof(DecodeThreadVars)); + memset(&ip4h, 0, sizeof(IPV4Hdr)); + + p.src.family = AF_INET; + p.dst.family = AF_INET; + p.ip4h = &ip4h; + + FlowInitConfig(FLOW_QUIET); + DecodeTCP(&tv, &dtv, &p, raw_tcp, sizeof(raw_tcp), NULL); + FlowShutdown(); + + if (p.tcph == NULL) { + printf("tcp packet decode failed: "); + goto end; + } + + uint8_t wscale = TCP_GET_WSCALE(&p); + if (wscale != 0) { + printf("wscale %"PRIu8", expected 0: ", wscale); + goto end; + } + + retval = 1; +end: + return retval; +} + +/** \test Get the wscale, but it's missing, so see if return 0 properly */ +static int TCPGetWscaleTest03(void) +{ + int retval = 0; + static uint8_t raw_tcp[] = {0xda, 0xc1, 0x00, 0x50, 0xb6, 0x21, 0x7f, 0x59, + 0xdd, 0xa3, 0x6f, 0xf8, 0x80, 0x10, 0x05, 0xb4, + 0x7c, 0x70, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, + 0x00, 0x62, 0x88, 0x9e, 0x00, 0x00, 0x00, 0x00}; + Packet p; + IPV4Hdr ip4h; + ThreadVars tv; + DecodeThreadVars dtv; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + memset(&dtv, 0, sizeof(DecodeThreadVars)); + memset(&ip4h, 0, sizeof(IPV4Hdr)); + + p.src.family = AF_INET; + p.dst.family = AF_INET; + p.ip4h = &ip4h; + + FlowInitConfig(FLOW_QUIET); + DecodeTCP(&tv, &dtv, &p, raw_tcp, sizeof(raw_tcp), NULL); + FlowShutdown(); + + if (p.tcph == NULL) { + printf("tcp packet decode failed: "); + goto end; + } + + uint8_t wscale = TCP_GET_WSCALE(&p); + if (wscale != 0) { + printf("wscale %"PRIu8", expected 0: ", wscale); + goto end; + } + + retval = 1; +end: + return retval; +} void DecodeTCPRegisterTests(void) { @@ -378,4 +506,7 @@ void DecodeTCPRegisterTests(void) TCPV6CalculateValidChecksumtest03, 1); UtRegisterTest("TCPV6CalculateInvalidChecksumtest04", TCPV6CalculateInvalidChecksumtest04, 0); + UtRegisterTest("TCPGetWscaleTest01", TCPGetWscaleTest01, 1); + UtRegisterTest("TCPGetWscaleTest02", TCPGetWscaleTest02, 1); + UtRegisterTest("TCPGetWscaleTest03", TCPGetWscaleTest03, 1); } diff --git a/src/decode-tcp.h b/src/decode-tcp.h index d9d4d604cc..165e13172f 100644 --- a/src/decode-tcp.h +++ b/src/decode-tcp.h @@ -41,6 +41,9 @@ #define TCP_OPT_TS_LEN 10 #define TCP_OPT_MSS_LEN 4 +/** Max valid wscale value. */ +#define TCP_WSCALE_MAX 14 + #define TCP_OPTS tcpvars.tcp_opts #define TCP_OPTS_CNT tcpvars.tcp_opt_cnt @@ -66,6 +69,9 @@ #define TCP_GET_TS2(p) ((p)->tcpc.ts2 != 0 ? \ (p)->tcpc.ts2 : (p)->tcpvars.ts ? ((p)->tcpc.ts2 = (uint32_t)ntohl((*(uint32_t *)((p)->tcpvars.ts->data+4)))) : 0) +/** macro for getting the wscale from the packet. */ +#define TCP_GET_WSCALE(p) ((p)->tcpvars.ws ? (((*(uint8_t *)(p)->tcpvars.ws->data) <= TCP_WSCALE_MAX) ? (*(uint8_t *)((p)->tcpvars.ws->data)) : 0) : 0) + #define TCP_GET_OFFSET(p) TCP_GET_RAW_OFFSET(p->tcph) #define TCP_GET_HLEN(p) TCP_GET_OFFSET(p) << 2 #define TCP_GET_SRC_PORT(p) TCP_GET_RAW_SRC_PORT(p->tcph) diff --git a/src/stream-tcp-private.h b/src/stream-tcp-private.h index ed803348d4..adbfdff719 100644 --- a/src/stream-tcp-private.h +++ b/src/stream-tcp-private.h @@ -44,6 +44,7 @@ enum #define STREAMTCP_FLAG_MIDSTREAM 0x01 /*Flag for mid stream session*/ #define STREAMTCP_FLAG_MIDSTREAM_ESTABLISHED 0x02 /*Flag for mid stream established session*/ +#define STREAMTCP_FLAG_SERVER_WSCALE 0x08 /**< Server supports wscale (even though it can be 0) */ /* Macro's for comparing Sequence numbers * Page 810 from TCP/IP Illustrated, Volume 2. */ diff --git a/src/stream-tcp.c b/src/stream-tcp.c index 7155aca8a7..d914aa42f6 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -309,17 +309,15 @@ static int StreamTcpPacketStateNone(ThreadVars *tv, Packet *p, StreamTcpThread * ssn->client.last_ts = *p->tcpvars.ts->data; ssn->server.window = TCP_GET_WINDOW(p); + if (p->tcpvars.ws != NULL) { + ssn->flags |= STREAMTCP_FLAG_SERVER_WSCALE; + ssn->server.wscale = TCP_GET_WSCALE(p); + } + #ifdef DEBUG printf("StreamTcpPacketStateNone (%p): ssn->client.isn %" PRIu32 ", ssn->client.next_seq %" PRIu32 ", ssn->client.last_ack %"PRIu32"\n", ssn, ssn->client.isn, ssn->client.next_seq, ssn->client.last_ack); #endif - - if (p->tcpvars.ws != NULL) { -#ifdef DEBUG - printf("StreamTcpPacketStateNone (%p): p->tcpvars.ws %p, %02x\n", ssn, p->tcpvars.ws, *p->tcpvars.ws->data); -#endif - ssn->server.wscale = *p->tcpvars.ws->data; - } break; } case TH_SYN|TH_ACK: @@ -352,18 +350,17 @@ static int StreamTcpPacketStateNone(ThreadVars *tv, Packet *p, StreamTcpThread * ssn->client.next_seq = ssn->client.isn + 1; ssn->client.last_ack = TCP_GET_ACK(p); + /** If the client has a wscale option the server had it too, + * so set the wscale for the server to max. Otherwise none + * will have the wscale opt just like it should. */ + if (p->tcpvars.ws != NULL) { + ssn->client.wscale = TCP_GET_WSCALE(p); + ssn->server.wscale = TCP_WSCALE_MAX; + } #ifdef DEBUG printf("StreamTcpPacketStateNone (%p): ssn->client.isn %"PRIu32", ssn->client.next_seq %"PRIu32", ssn->client.last_ack %"PRIu32"\n", ssn, ssn->client.isn, ssn->client.next_seq, ssn->client.last_ack); -#endif - if (p->tcpvars.ws != NULL) { -#ifdef DEBUG - printf("StreamTcpPacketStateNone (%p): p->tcpvars.ws %p, %02x\n", ssn, p->tcpvars.ws, *p->tcpvars.ws->data); -#endif - ssn->client.wscale = *p->tcpvars.ws->data; - } -#ifdef DEBUG printf("StreamTcpPacketStateNone (%p): ssn->server.isn %"PRIu32", ssn->server.next_seq %"PRIu32", ssn->server.last_ack %"PRIu32"\n", ssn, ssn->server.isn, ssn->server.next_seq, ssn->server.last_ack); #endif @@ -411,9 +408,10 @@ static int StreamTcpPacketStateNone(ThreadVars *tv, Packet *p, StreamTcpThread * ssn, ssn->client.last_ack, ssn->server.last_ack); #endif - /** \todo window scaling for midstream pickups */ - ssn->client.wscale = 0; - ssn->server.wscale = 0; + /** window scaling for midstream pickups, we can't do much other + * than assume that it's set to the max value: 14 */ + ssn->client.wscale = TCP_WSCALE_MAX; + ssn->server.wscale = TCP_WSCALE_MAX; StreamTcpReassembleHandleSegment(ssn, &ssn->client, p); break; @@ -502,11 +500,10 @@ static int StreamTcpPacketStateSynSent(ThreadVars *tv, Packet *p, StreamTcpThrea ssn->client.last_ack = TCP_GET_ACK(p); ssn->server.last_ack = ssn->server.isn + 1; - if (ssn->server.wscale != 0 && p->tcpvars.ws != NULL) { -#ifdef DEBUG - printf("StreamTcpPacketStateSynSent (%p): p->tcpvars.ws %p, %02x\n", ssn, p->tcpvars.ws, *p->tcpvars.ws->data); -#endif - ssn->client.wscale = *p->tcpvars.ws->data; + /** check for the presense of the ws ptr to determine if we + * support wscale at all */ + if (ssn->flags & STREAMTCP_FLAG_SERVER_WSCALE && p->tcpvars.ws != NULL) { + ssn->client.wscale = TCP_GET_WSCALE(p); } else { ssn->client.wscale = 0; } @@ -583,9 +580,10 @@ static int StreamTcpPacketStateSynRecv(ThreadVars *tv, Packet *p, StreamTcpThrea if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) { ssn->client.window = TCP_GET_WINDOW(p); ssn->server.next_win = ssn->server.last_ack + ssn->server.window; - /** \todo window scaling need to be addressed for midstream pickups */ - ssn->server.wscale = 0; - ssn->client.wscale = 0; + /** window scaling for midstream pickups, we can't do much other + * than assume that it's set to the max value: 14 */ + ssn->server.wscale = TCP_WSCALE_MAX; + ssn->client.wscale = TCP_WSCALE_MAX; } #ifdef DEBUG printf("StreamTcpPacketStateSynRecv (%p): pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ", ACK %" PRIu32 "\n",