diff --git a/src/decode-tcp.c b/src/decode-tcp.c index 2bd09cf149..4f38691e16 100644 --- a/src/decode-tcp.c +++ b/src/decode-tcp.c @@ -113,6 +113,21 @@ static int DecodeTCPOptions(ThreadVars *tv, Packet *p, uint8_t *pkt, uint16_t le } } break; + case TCP_OPT_SACK: + SCLogDebug("SACK option, len %u", p->TCP_OPTS[p->TCP_OPTS_CNT].len); + if (p->TCP_OPTS[p->TCP_OPTS_CNT].len < TCP_OPT_SACK_MIN_LEN || + p->TCP_OPTS[p->TCP_OPTS_CNT].len > TCP_OPT_SACK_MAX_LEN || + !((p->TCP_OPTS[p->TCP_OPTS_CNT].len - 2) % 8 == 0)) + { + DECODER_SET_EVENT(p,TCP_OPT_INVALID_LEN); + } else { + if (p->tcpvars.sack != NULL) { + DECODER_SET_EVENT(p,TCP_OPT_DUPLICATE); + } else { + p->tcpvars.sack = &p->TCP_OPTS[p->TCP_OPTS_CNT]; + } + } + break; } pkt += p->TCP_OPTS[p->TCP_OPTS_CNT].len; @@ -411,6 +426,72 @@ end: SCFree(p); return retval; } + +static int TCPGetSackTest01(void) +{ + int retval = 0; + static uint8_t raw_tcp[] = { + 0x00, 0x50, 0x06, 0xa6, 0xfa, 0x87, 0x0b, 0xf5, + 0xf1, 0x59, 0x02, 0xe0, 0xa0, 0x10, 0x3e, 0xbc, + 0x1d, 0xe7, 0x00, 0x00, 0x01, 0x01, 0x05, 0x12, + 0xf1, 0x59, 0x13, 0xfc, 0xf1, 0x59, 0x1f, 0x64, + 0xf1, 0x59, 0x08, 0x94, 0xf1, 0x59, 0x0e, 0x48 }; + static uint8_t raw_tcp_sack[] = { + 0xf1, 0x59, 0x13, 0xfc, 0xf1, 0x59, 0x1f, 0x64, + 0xf1, 0x59, 0x08, 0x94, 0xf1, 0x59, 0x0e, 0x48 }; + Packet *p = SCMalloc(SIZE_OF_PACKET); + if (p == NULL) + return 0; + IPV4Hdr ip4h; + ThreadVars tv; + DecodeThreadVars dtv; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(p, 0, SIZE_OF_PACKET); + p->pkt = (uint8_t *)(p + 1); + 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; + } + + if (p->tcpvars.sack == NULL) { + printf("tcp packet sack not decoded: "); + goto end; + } + + int sack = TCP_GET_SACK_CNT(p); + if (sack != 2) { + printf("expected 2 sack records, got %u: ", TCP_GET_SACK_CNT(p)); + goto end; + } + + uint8_t *sackptr = TCP_GET_SACK_PTR(p); + if (sackptr == NULL) { + printf("no sack data: "); + goto end; + } + + if (memcmp(sackptr, raw_tcp_sack, 16) != 0) { + printf("malformed sack data: "); + goto end; + } + + retval = 1; +end: + SCFree(p); + return retval; +} #endif /* UNITTESTS */ void DecodeTCPRegisterTests(void) @@ -427,5 +508,6 @@ void DecodeTCPRegisterTests(void) UtRegisterTest("TCPGetWscaleTest01", TCPGetWscaleTest01, 1); UtRegisterTest("TCPGetWscaleTest02", TCPGetWscaleTest02, 1); UtRegisterTest("TCPGetWscaleTest03", TCPGetWscaleTest03, 1); + UtRegisterTest("TCPGetSackTest01", TCPGetSackTest01, 1); #endif /* UNITTESTS */ } diff --git a/src/decode-tcp.h b/src/decode-tcp.h index 6cbe7ca79c..6bea58f19b 100644 --- a/src/decode-tcp.h +++ b/src/decode-tcp.h @@ -56,6 +56,8 @@ #define TCP_OPT_WS_LEN 3 #define TCP_OPT_TS_LEN 10 #define TCP_OPT_MSS_LEN 4 +#define TCP_OPT_SACK_MIN_LEN 10 /* hdr 2, 1 pair 8 = 10 */ +#define TCP_OPT_SACK_MAX_LEN 34 /* hdr 2, 4 pair 32= 34 */ /** Max valid wscale value. */ #define TCP_WSCALE_MAX 14 @@ -89,6 +91,9 @@ /** 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_SACK_PTR(p) (p)->tcpvars.sack ? (p)->tcpvars.sack->data : NULL +#define TCP_GET_SACK_CNT(p) ((p)->tcpvars.sack ? (((p)->tcpvars.sack->len - 2) / 8) : 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) @@ -131,11 +136,12 @@ typedef struct TCPVars_ uint8_t hlen; uint8_t tcp_opt_len; - TCPOpt tcp_opts[TCP_OPTMAX]; uint8_t tcp_opt_cnt; + TCPOpt tcp_opts[TCP_OPTMAX]; /* ptrs to commonly used and needed opts */ TCPOpt *sackok; + TCPOpt *sack; TCPOpt *ws; TCPOpt *ts; TCPOpt *mss; @@ -152,8 +158,10 @@ typedef struct TCPCache_ { #define CLEAR_TCP_PACKET(p) { \ (p)->tcph = NULL; \ + (p)->tcpvars.tcp_opt_len = 0; \ (p)->tcpvars.tcp_opt_cnt = 0; \ (p)->tcpvars.sackok = NULL; \ + (p)->tcpvars.sack = NULL; \ (p)->tcpvars.ts = NULL; \ (p)->tcpvars.ws = NULL; \ (p)->tcpvars.mss = NULL; \