From 74cb73fc1d3e651954e722fcc0d7514da8d94332 Mon Sep 17 00:00:00 2001 From: Brian Rectanus Date: Sat, 22 Aug 2009 22:04:32 -0700 Subject: [PATCH] Decode IPv4 options. --- src/decode-events.h | 9 + src/decode-ipv4.c | 1252 ++++++++++++++++++++++++++++++++++++- src/decode-ipv4.h | 89 ++- src/decode-tcp.c | 1 + src/detect-decode-event.h | 7 + src/eidps.c | 1 + 6 files changed, 1331 insertions(+), 28 deletions(-) diff --git a/src/decode-events.h b/src/decode-events.h index 93800bbe64..e64070d6e8 100644 --- a/src/decode-events.h +++ b/src/decode-events.h @@ -10,6 +10,15 @@ enum { IPV4_IPLEN_SMALLER_THAN_HLEN, IPV4_TRUNC_PKT, + /* IPV4 OPTIONS */ + IPV4_OPT_INVALID, + IPV4_OPT_INVALID_LEN, + IPV4_OPT_MALFORMED, + IPV4_OPT_PAD_REQUIRED, + IPV4_OPT_EOL_REQUIRED, + IPV4_OPT_DUPLICATE, + IPV4_OPT_UNKNOWN, + /* IPV6 EVENTS */ IPV6_PKT_TOO_SMALL, IPV6_TRUNC_PKT, diff --git a/src/decode-ipv4.c b/src/decode-ipv4.c index f128c23d48..b5f0525d3b 100644 --- a/src/decode-ipv4.c +++ b/src/decode-ipv4.c @@ -1,15 +1,436 @@ -/* Copyright (c) 2008 Victor Julien */ +/** Copyright (c) 2009 Open Information Security Foundation + * + * \author Victor Julien + * \author Brian Rectanus + */ #include "eidps-common.h" #include "packet-queue.h" #include "decode.h" #include "decode-ipv4.h" #include "decode-events.h" +#include "util-unittest.h" -/* XXX */ +/* Generic validation + * + * [--type--][--len---] + * + * \todo This function needs removed in favor of specific validation. + * + * See: RFC 791 + */ +static int IPV4OptValidateGeneric(Packet *p, const IPV4Opt *o) { + switch (o->type) { + /* See: RFC 4782 */ + case IPV4_OPT_QS: + if (p->IPV4_OPTS[p->IPV4_OPTS_CNT].len < IPV4_OPT_QS_MIN) { + DECODER_SET_EVENT(p,IPV4_OPT_INVALID_LEN); + return -1; + } + break; + /* See: RFC 1108 */ + case IPV4_OPT_SEC: + if (p->IPV4_OPTS[p->IPV4_OPTS_CNT].len != IPV4_OPT_SEC_LEN) { + DECODER_SET_EVENT(p,IPV4_OPT_INVALID_LEN); + return -1; + } + break; + case IPV4_OPT_SID: + if (p->IPV4_OPTS[p->IPV4_OPTS_CNT].len != IPV4_OPT_SID_LEN) { + DECODER_SET_EVENT(p,IPV4_OPT_INVALID_LEN); + return -1; + } + break; + /* See: RFC 2113 */ + case IPV4_OPT_RTRALT: + if (p->IPV4_OPTS[p->IPV4_OPTS_CNT].len != IPV4_OPT_RTRALT_LEN) { + DECODER_SET_EVENT(p,IPV4_OPT_INVALID_LEN); + return -1; + } + break; + default: + /* Should never get here unless there is a coding error */ + DECODER_SET_EVENT(p,IPV4_OPT_UNKNOWN); + return -1; + } + + return 0; +} + +/* Validate route type options + * + * [--type--][--len---][--ptr---][address1]...[addressN] + * + * See: RFC 791 + */ +static int IPV4OptValidateRoute(Packet *p, const IPV4Opt *o) { + uint8_t ptr; + + /* Check length */ + if (o->len < IPV4_OPT_ROUTE_MIN) { + DECODER_SET_EVENT(p,IPV4_OPT_INVALID_LEN); + return -1; + } + + /* Data is required */ + if (o->data == NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; + } + ptr = *o->data; + + /* Address pointer is 1 based and points at least after type+len+ptr, + * must be a incremented by 4 bytes (address size) and cannot extend + * past option length. + */ + if ((ptr < 4) || (ptr % 4) || (ptr > o->len + 1)) { + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; + } + + return 0; +} + +/* Validate timestamp type options + * + * [--type--][--len---][--ptr---][ovfl][flag][rec1----...]...[recN----...] + * NOTE: rec could be 4 (ts only) or 8 (ip+ts) bytes in length. + * + * See: RFC 781 + */ +static int IPV4OptValidateTimestamp(Packet *p, const IPV4Opt *o) { + uint8_t ptr; + uint8_t flag; + uint8_t rec_size; + + /* Check length */ + if (o->len < IPV4_OPT_TS_MIN) { + DECODER_SET_EVENT(p,IPV4_OPT_INVALID_LEN); + return -1; + } + + /* Data is required */ + if (o->data == NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; + } + ptr = *o->data; + + /* We need the flag to determine what is in the option payload */ + if (ptr < 5) { + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; + } + flag = *(o->data + 3) & 0x00ff; + + /* A flag of 1|3 means we have both the ip+ts in each record */ + rec_size = ((flag == 1) || (flag == 3)) ? 8 : 4; + + /* Address pointer is 1 based and points at least after + * type+len+ptr+ovfl+flag, must be incremented by by the rec_size + * and cannot extend past option length. + */ + if (((ptr - 5) % rec_size) || (ptr > o->len + 1)) { + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; + } + + return 0; +} + +/* Validate CIPSO option + * + * [--type--][--len---][--doi---][tags--...] + * + * See: draft-ietf-cipso-ipsecurity-01.txt + * See: FIPS 188 (tags 6 & 7) + */ +static int IPV4OptValidateCIPSO(Packet *p, const IPV4Opt *o) { + uint32_t doi; + uint8_t *tag; + uint16_t len; + + /* Check length */ + if (o->len < IPV4_OPT_CIPSO_MIN) { + DECODER_SET_EVENT(p,IPV4_OPT_INVALID_LEN); + return -1; + } + + /* Data is required */ + if (o->data == NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; + } + doi = *o->data; + tag = o->data + 4; + len = o->len - 1 - 1 - 4; /* Length of tags after header */ + + + /* Domain of Interest (DOI) of 0 is reserved and thus invalid */ + /** \todo Aparently a DOI of zero is fine in practice - verify. */ + if (doi == 0) { +#if 0 + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; +#endif + } + + /* NOTE: We know len has passed min tests prior to this call */ + + /* Check that tags are formatted correctly + * [-ttype--][--tlen--][-tagdata-...] + */ + while (len) { + uint8_t ttype; + uint8_t tlen; + + /* Tag header must fit within option length */ + if (len < 2) { + //printf("CIPSO tag header too large %" PRIu16 " < 2\n", len); + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; + } + + /* Tag header is type+len */ + ttype = *(tag++); + tlen = *(tag++); + + /* Tag length must fit within the option length */ + if (tlen > len) { + //printf("CIPSO tag len too large %" PRIu8 " > %" PRIu16 "\n", tlen, len); + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; + } + + switch(ttype) { + case 0: + /* Tag type 0 is reserved and thus invalid */ + /** \todo Wireshark marks this a padding, but spec says reserved. */ + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; + case 1: + case 2: + case 5: + case 6: + case 7: + /* Tag is at least 4 and at most the remainder of option len */ + if ((tlen < 4) || (tlen > len)) { + //printf("CIPSO tag %" PRIu8 " bad tlen=%" PRIu8 " len=%" PRIu8 "\n", ttype, tlen, len); + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; + } + + /* The alignment octet is always 0 except tag + * type 7, which has no such field. + */ + if ((ttype != 7) && (*tag != 0)) { + //printf("CIPSO tag %" PRIu8 " ao=%" PRIu8 "\n", ttype, tlen); + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + return -1; + } + + /* Skip the rest of the tag payload */ + tag += tlen - 2; + len -= tlen; + + continue; + default: + //printf("CIPSO tag %" PRIu8 " unknown tag\n", ttype); + DECODER_SET_EVENT(p,IPV4_OPT_MALFORMED); + /* \todo May not want to return error here on unknown tag type (at least not for 3|4 */ + return -1; + } + } + + return 0; +} + +/** + * Decode/Validate IPv4 Options. + */ static int DecodeIPV4Options(ThreadVars *tv, Packet *p, uint8_t *pkt, uint16_t len) { - printf("*pkt %" PRIu32 "\n", *pkt); + uint16_t plen = len; + + p->IPV4_OPTS_CNT = 0; + +#ifdef DEBUG + { + uint16_t i; + printf("IPV4OPTS: {"); + for (i = 0; i < len; i++) { + printf("%02" PRIx8 " ", pkt[i]); + } + printf("}\n"); + } +#endif + + /* Options length must be padded to 8byte boundary */ + if (plen % 8) { + DECODER_SET_EVENT(p,IPV4_OPT_PAD_REQUIRED); + /* Warn - we can keep going */ + } + + while (plen) + { + /* single byte options */ + if (*pkt == IPV4_OPT_EOL) { + /** \todo What if more data exist after EOL (possible covert channel or data leakage)? */ + break; + } else if (*pkt == IPV4_OPT_NOP) { + pkt++; + plen--; + + /* multibyte options */ + } else { + if (plen < 2) { + /** \todo What if padding is non-zero (possible covert channel or data leakage)? */ + /** \todo Spec seems to indicate EOL required if there is padding */ + DECODER_SET_EVENT(p,IPV4_OPT_EOL_REQUIRED); + break; + } + + /* Option length is too big for packet */ + if (*(pkt+1) > plen) { + DECODER_SET_EVENT(p,IPV4_OPT_INVALID_LEN); + return -1; + } + + p->IPV4_OPTS[p->IPV4_OPTS_CNT].type = *pkt; + p->IPV4_OPTS[p->IPV4_OPTS_CNT].len = *(pkt+1); + if (plen > 2) + p->IPV4_OPTS[p->IPV4_OPTS_CNT].data = (pkt+2); + else + p->IPV4_OPTS[p->IPV4_OPTS_CNT].data = NULL; + +#ifdef DEBUG + printf("IPV4OPT %" PRIu16 " len %" PRIu16 " @ %" PRIu16 "/%" PRIu16 "\n", + p->IPV4_OPTS[p->IPV4_OPTS_CNT].type, + p->IPV4_OPTS[p->IPV4_OPTS_CNT].len, + (len - plen), + (len - 1)); +#endif + + /* we already know that the total options len is valid, + * so here the len of the specific option must be bad. + * Also check for invalid lengths 0 and 1. */ + if (p->IPV4_OPTS[p->IPV4_OPTS_CNT].len > plen || + p->IPV4_OPTS[p->IPV4_OPTS_CNT].len < 2) { + DECODER_SET_EVENT(p,IPV4_OPT_INVALID_LEN); + return -1; + } + + /* we are parsing the most commonly used opts to prevent + * us from having to walk the opts list for these all the + * time. */ + /** \todo Figure out which IP options are more common and list them first */ + switch (p->IPV4_OPTS[p->IPV4_OPTS_CNT].type) { + case IPV4_OPT_TS: + if (p->ip4vars.o_ts != NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_DUPLICATE); + /* Warn - we can keep going */ + break; + } else if (IPV4OptValidateTimestamp(p,&p->IPV4_OPTS[p->IPV4_OPTS_CNT])) { + return -1; + } + p->ip4vars.o_ts = &p->IPV4_OPTS[p->IPV4_OPTS_CNT]; + break; + case IPV4_OPT_RR: + if (p->ip4vars.o_rr != NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_DUPLICATE); + /* Warn - we can keep going */ + break; + } else if (IPV4OptValidateRoute(p,&p->IPV4_OPTS[p->IPV4_OPTS_CNT]) != 0) { + return -1; + } + p->ip4vars.o_rr = &p->IPV4_OPTS[p->IPV4_OPTS_CNT]; + break; + case IPV4_OPT_QS: + if (p->ip4vars.o_qs != NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_DUPLICATE); + /* Warn - we can keep going */ + break; + } else if (IPV4OptValidateGeneric(p, &p->IPV4_OPTS[p->IPV4_OPTS_CNT])) { + return -1; + } + p->ip4vars.o_qs = &p->IPV4_OPTS[p->IPV4_OPTS_CNT]; + break; + case IPV4_OPT_SEC: + if (p->ip4vars.o_sec != NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_DUPLICATE); + /* Warn - we can keep going */ + break; + } else if (IPV4OptValidateGeneric(p, &p->IPV4_OPTS[p->IPV4_OPTS_CNT])) { + return -1; + } + p->ip4vars.o_sec = &p->IPV4_OPTS[p->IPV4_OPTS_CNT]; + break; + case IPV4_OPT_LSRR: + if (p->ip4vars.o_lsrr != NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_DUPLICATE); + /* Warn - we can keep going */ + break; + } else if (IPV4OptValidateRoute(p,&p->IPV4_OPTS[p->IPV4_OPTS_CNT]) != 0) { + return -1; + } + p->ip4vars.o_lsrr = &p->IPV4_OPTS[p->IPV4_OPTS_CNT]; + break; + case IPV4_OPT_CIPSO: + if (p->ip4vars.o_cipso != NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_DUPLICATE); + /* Warn - we can keep going */ + break; + } else if (IPV4OptValidateCIPSO(p,&p->IPV4_OPTS[p->IPV4_OPTS_CNT]) != 0) { + return -1; + } + p->ip4vars.o_cipso = &p->IPV4_OPTS[p->IPV4_OPTS_CNT]; + break; + case IPV4_OPT_SID: + if (p->ip4vars.o_sid != NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_DUPLICATE); + /* Warn - we can keep going */ + break; + } else if (IPV4OptValidateGeneric(p, &p->IPV4_OPTS[p->IPV4_OPTS_CNT])) { + return -1; + } + p->ip4vars.o_sid = &p->IPV4_OPTS[p->IPV4_OPTS_CNT]; + break; + case IPV4_OPT_SSRR: + if (p->ip4vars.o_ssrr != NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_DUPLICATE); + /* Warn - we can keep going */ + break; + } else if (IPV4OptValidateRoute(p,&p->IPV4_OPTS[p->IPV4_OPTS_CNT]) != 0) { + return -1; + } + p->ip4vars.o_ssrr = &p->IPV4_OPTS[p->IPV4_OPTS_CNT]; + break; + case IPV4_OPT_RTRALT: + if (p->ip4vars.o_rtralt != NULL) { + DECODER_SET_EVENT(p,IPV4_OPT_DUPLICATE); + /* Warn - we can keep going */ + break; + } else if (IPV4OptValidateGeneric(p, &p->IPV4_OPTS[p->IPV4_OPTS_CNT])) { + return -1; + } + p->ip4vars.o_rtralt = &p->IPV4_OPTS[p->IPV4_OPTS_CNT]; + break; + default: +#ifdef DEBUG + printf("IPV4OPT (%" PRIu8 ") len %" PRIu8 "\n", + p->IPV4_OPTS[p->IPV4_OPTS_CNT].type, + p->IPV4_OPTS[p->IPV4_OPTS_CNT].len); +#endif + DECODER_SET_EVENT(p,IPV4_OPT_INVALID); + /* Warn - we can keep going */ + break; + } + + pkt += p->IPV4_OPTS[p->IPV4_OPTS_CNT].len; + plen -= (p->IPV4_OPTS[p->IPV4_OPTS_CNT].len); + p->IPV4_OPTS_CNT++; + } + } return 0; } @@ -42,14 +463,15 @@ static int DecodeIPV4Packet(ThreadVars *tv, Packet *p, uint8_t *pkt, uint16_t le } /* save the options len */ - p->ip4vars.ip_opts_len = IPV4_GET_HLEN(p) - IPV4_HEADER_LEN; - if (p->ip4vars.ip_opts_len > 0) { - DecodeIPV4Options(tv, p, pkt + IPV4_GET_HLEN(p), p->ip4vars.ip_opts_len); + p->ip4vars.ip_opt_len = IPV4_GET_HLEN(p) - IPV4_HEADER_LEN; + if (p->ip4vars.ip_opt_len > 0) { + DecodeIPV4Options(tv, p, pkt + IPV4_HEADER_LEN, p->ip4vars.ip_opt_len); } /* set the address struct */ SET_IPV4_SRC_ADDR(p,&p->src); SET_IPV4_DST_ADDR(p,&p->dst); + return 0; } @@ -136,3 +558,821 @@ void DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, return; } +/* UNITTESTS */ +#ifdef UNITTESTS + +void DecodeIPV4OptionsPrint(Packet *p) { + IPV4Vars *pv = &p->ip4vars; + + printf("DecodeIPV4Options: cnt=%" PRIu8 + ",rr={t=%" PRIu8 ",l=%" PRIu8 ",d=0x%0" PRIxMAX "}" + ",qs={t=%" PRIu8 ",l=%" PRIu8 ",d=0x%0" PRIxMAX "}" + ",ts={t=%" PRIu8 ",l=%" PRIu8 ",d=0x%0" PRIxMAX "}" + ",sec={t=%" PRIu8 ",l=%" PRIu8 ",d=0x%0" PRIxMAX "}" + ",lsrr={t=%" PRIu8 ",l=%" PRIu8 ",d=0x%0" PRIxMAX "}" + ",cipso={t=%" PRIu8 ",l=%" PRIu8 ",d=0x%0" PRIxMAX "}" + ",sid={t=%" PRIu8 ",l=%" PRIu8 ",d=0x%0" PRIxMAX "}" + ",ssrr={t=%" PRIu8 ",l=%" PRIu8 ",d=0x%0" PRIxMAX "}" + ",rtralt={t=%" PRIu8 ",l=%" PRIu8 ",d=0x%0" PRIxMAX "}" + "}\n", + pv->ip_opt_cnt, + (pv->o_rr ? pv->o_rr->type : 0), (pv->o_rr ? pv->o_rr->len : 0), (uintmax_t)(pv->o_rr ? pv->o_rr->data : 0), + (pv->o_qs ? pv->o_qs->type : 0), (pv->o_qs ? pv->o_qs->len : 0), (uintmax_t)(pv->o_qs ? pv->o_qs->data : 0), + (pv->o_ts ? pv->o_ts->type : 0), (pv->o_ts ? pv->o_ts->len : 0), (uintmax_t)(pv->o_ts ? pv->o_ts->data : 0), + (pv->o_sec ? pv->o_sec->type : 0), (pv->o_sec ? pv->o_sec->len : 0), (uintmax_t)(pv->o_sec ? pv->o_sec->data : 0), + (pv->o_lsrr ? pv->o_lsrr->type : 0), (pv->o_lsrr ? pv->o_lsrr->len : 0), (uintmax_t)(pv->o_lsrr ? pv->o_lsrr->data : 0), + (pv->o_cipso ? pv->o_cipso->type : 0), (pv->o_cipso ? pv->o_cipso->len : 0), (uintmax_t)(pv->o_cipso ? pv->o_cipso->data : 0), + (pv->o_sid ? pv->o_sid->type : 0), (pv->o_sid ? pv->o_sid->len : 0), (uintmax_t)(pv->o_sid ? pv->o_sid->data : 0), + (pv->o_ssrr ? pv->o_ssrr->type : 0), (pv->o_ssrr ? pv->o_ssrr->len : 0), (uintmax_t)(pv->o_ssrr ? pv->o_ssrr->data : 0), + (pv->o_rtralt ? pv->o_rtralt->type : 0), (pv->o_rtralt ? pv->o_rtralt->len : 0), (uintmax_t)(pv->o_rtralt ? pv->o_rtralt->data : 0)); +} + +/** \test IPV4 with no options. */ +int DecodeIPV4OptionsNONETest01(void) { + uint8_t raw_opts[] = { }; + Packet p; + ThreadVars tv; + uint8_t *data = (uint8_t *)&p; + uint16_t i; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + if (rc != 0) { + DecodeIPV4OptionsPrint(&p); + return 0; + } + + for (i = 0; i < (uint16_t)sizeof(Packet); i++) { + if (*data) { + /* Should not have modified packet data */ + //printf("Data modified at offset %" PRIu16 "\n", i); + DecodeIPV4OptionsPrint(&p); + return 0; + } + } + + return 1; +} + +/** \test IPV4 with EOL option. */ +int DecodeIPV4OptionsEOLTest01(void) { + uint8_t raw_opts[] = { + IPV4_OPT_EOL, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + uint8_t *data = (uint8_t *)&p; + uint16_t i; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + if (rc != 0) { + DecodeIPV4OptionsPrint(&p); + return 0; + } + + for (i = 0; i < (uint16_t)sizeof(Packet); i++) { + if (*data) { + /* Should not have modified packet data */ + //printf("Data modified at offset %" PRIu16 "\n", i); + DecodeIPV4OptionsPrint(&p); + return 0; + } + } + + return 1; +} + +/** \test IPV4 with NOP option. */ +int DecodeIPV4OptionsNOPTest01(void) { + uint8_t raw_opts[] = { + IPV4_OPT_NOP, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + uint8_t *data = (uint8_t *)&p; + uint16_t i; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + if (rc != 0) { + DecodeIPV4OptionsPrint(&p); + return 0; + } + + for (i = 0; i < (uint16_t)sizeof(Packet); i++) { + if (*data) { + /* Should not have modified packet data */ + //printf("Data modified at offset %" PRIu16 "\n", i); + DecodeIPV4OptionsPrint(&p); + return 0; + } + } + + return 1; +} + +/** \test IPV4 with RR option. */ +int DecodeIPV4OptionsRRTest01(void) { + uint8_t raw_opts[] = { + IPV4_OPT_RR, 0x27, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rr, (uintmax_t)&p.IPV4_OPTS[0]); + if ( (rc == 0) + && (p.IPV4_OPTS_CNT == 1) + && (p.IPV4_OPTS[0].type == IPV4_OPT_RR) + && (p.IPV4_OPTS[0].len == 0x27) + && (p.ip4vars.o_rr == &p.IPV4_OPTS[0])) + { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with RR option (len too large). */ +int DecodeIPV4OptionsRRTest02(void) { + uint8_t raw_opts[] = { + IPV4_OPT_RR, 0xff, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rr, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with RR option (ptr too large). */ +int DecodeIPV4OptionsRRTest03(void) { + uint8_t raw_opts[] = { + IPV4_OPT_RR, 0x27, 0xff, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rr, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with RR option (ptr not in 4 byte increment). */ +int DecodeIPV4OptionsRRTest04(void) { + uint8_t raw_opts[] = { + IPV4_OPT_RR, 0x27, 0x05, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rr, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with QS option. */ +int DecodeIPV4OptionsQSTest01(void) { + uint8_t raw_opts[] = { + IPV4_OPT_QS, 0x08, 0x0d, 0x00, 0xbe, 0xef, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",qs=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_qs, (uintmax_t)&p.IPV4_OPTS[0]); + if ( (rc == 0) + && (p.IPV4_OPTS_CNT == 1) + && (p.IPV4_OPTS[0].type == IPV4_OPT_QS) + && (p.IPV4_OPTS[0].len == 0x08) + && (p.ip4vars.o_qs == &p.IPV4_OPTS[0])) + { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with QS option (len too small) */ +int DecodeIPV4OptionsQSTest02(void) { + uint8_t raw_opts[] = { + IPV4_OPT_QS, 0x07, 0x0d, 0x00, 0xbe, 0xef, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",qs=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_qs, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with TS option. */ +int DecodeIPV4OptionsTSTest01(void) { + uint8_t raw_opts[] = { + IPV4_OPT_TS, 0x24, 0x0d, 0x01, 0x0a, 0x0a, 0x0a, 0x69, + 0x04, 0xce, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ts=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ts, (uintmax_t)&p.IPV4_OPTS[0]); + if ( (rc == 0) + && (p.IPV4_OPTS_CNT == 1) + && (p.IPV4_OPTS[0].type == IPV4_OPT_TS) + && (p.IPV4_OPTS[0].len == 0x24) + && (p.ip4vars.o_ts == &p.IPV4_OPTS[0])) + { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with TS option (ptr too small). */ +int DecodeIPV4OptionsTSTest02(void) { + uint8_t raw_opts[] = { + IPV4_OPT_TS, 0x24, 0x04, 0x01, 0x0a, 0x0a, 0x0a, 0x69, + 0x04, 0xce, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ts=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ts, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with TS option (ptr too large). */ +int DecodeIPV4OptionsTSTest03(void) { + uint8_t raw_opts[] = { + IPV4_OPT_TS, 0x24, 0xff, 0x01, 0x0a, 0x0a, 0x0a, 0x69, + 0x04, 0xce, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ts=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ts, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with TS option (ptr not valid). */ +int DecodeIPV4OptionsTSTest04(void) { + uint8_t raw_opts[] = { + IPV4_OPT_TS, 0x24, 0x0a, 0x01, 0x0a, 0x0a, 0x0a, 0x69, + 0x04, 0xce, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ts=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ts, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with SEC option. */ +int DecodeIPV4OptionsSECTest01(void) { + uint8_t raw_opts[] = { + IPV4_OPT_SEC, 0x0b, 0xf1, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",sec=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_sec, (uintmax_t)&p.IPV4_OPTS[0]); + if ( (rc == 0) + && (p.IPV4_OPTS_CNT == 1) + && (p.IPV4_OPTS[0].type == IPV4_OPT_SEC) + && (p.IPV4_OPTS[0].len == 0x0b) + && (p.ip4vars.o_sec == &p.IPV4_OPTS[0])) + { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with SEC option (invalid length). */ +int DecodeIPV4OptionsSECTest02(void) { + uint8_t raw_opts[] = { + IPV4_OPT_SEC, 0x0a, 0xf1, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",sec=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_sec, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with LSRR option. */ +int DecodeIPV4OptionsLSRRTest01(void) { + uint8_t raw_opts[] = { + IPV4_OPT_LSRR, 0x27, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",lsrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_lsrr, (uintmax_t)&p.IPV4_OPTS[0]); + if ( (rc == 0) + && (p.IPV4_OPTS_CNT == 1) + && (p.IPV4_OPTS[0].type == IPV4_OPT_LSRR) + && (p.IPV4_OPTS[0].len == 0x27) + && (p.ip4vars.o_lsrr == &p.IPV4_OPTS[0])) + { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with LSRR option (len too large). */ +int DecodeIPV4OptionsLSRRTest02(void) { + uint8_t raw_opts[] = { + IPV4_OPT_LSRR, 0xff, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",lsrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_lsrr, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with LSRR option (ptr too large). */ +int DecodeIPV4OptionsLSRRTest03(void) { + uint8_t raw_opts[] = { + IPV4_OPT_LSRR, 0x27, 0xff, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",lsrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_lsrr, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with LSRR option (ptr not in 4 byte increment). */ +int DecodeIPV4OptionsLSRRTest04(void) { + uint8_t raw_opts[] = { + IPV4_OPT_LSRR, 0x27, 0x05, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",lsrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_lsrr, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with CIPSO option. */ +int DecodeIPV4OptionsCIPSOTest01(void) { + uint8_t raw_opts[] = { + IPV4_OPT_CIPSO, 0x18, 0x00, 0x00, 0x00, 0x05, 0x05, 0x12, + 0x00, 0x03, 0x00, 0xef, 0x00, 0xef, 0x00, 0x06, + 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_cipso, (uintmax_t)&p.IPV4_OPTS[0]); + if ( (rc == 0) + && (p.IPV4_OPTS_CNT == 1) + && (p.IPV4_OPTS[0].type == IPV4_OPT_CIPSO) + && (p.IPV4_OPTS[0].len == 0x18) + && (p.ip4vars.o_cipso == &p.IPV4_OPTS[0])) + { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with SID option. */ +int DecodeIPV4OptionsSIDTest01(void) { + uint8_t raw_opts[] = { + IPV4_OPT_SID, 0x04, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",sid=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_sid, (uintmax_t)&p.IPV4_OPTS[0]); + if ( (rc == 0) + && (p.IPV4_OPTS_CNT == 1) + && (p.IPV4_OPTS[0].type == IPV4_OPT_SID) + && (p.IPV4_OPTS[0].len == 0x04) + && (p.ip4vars.o_sid == &p.IPV4_OPTS[0])) + { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with SID option (len invalid. */ +int DecodeIPV4OptionsSIDTest02(void) { + uint8_t raw_opts[] = { + IPV4_OPT_SID, 0x05, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",sid=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_sid, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with SSRR option. */ +int DecodeIPV4OptionsSSRRTest01(void) { + uint8_t raw_opts[] = { + IPV4_OPT_SSRR, 0x27, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ssrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ssrr, (uintmax_t)&p.IPV4_OPTS[0]); + if ( (rc == 0) + && (p.IPV4_OPTS_CNT == 1) + && (p.IPV4_OPTS[0].type == IPV4_OPT_SSRR) + && (p.IPV4_OPTS[0].len == 0x27) + && (p.ip4vars.o_ssrr == &p.IPV4_OPTS[0])) + { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with SSRR option (len too large). */ +int DecodeIPV4OptionsSSRRTest02(void) { + uint8_t raw_opts[] = { + IPV4_OPT_SSRR, 0xff, 0x08, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ssrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ssrr, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with SSRR option (ptr too large). */ +int DecodeIPV4OptionsSSRRTest03(void) { + uint8_t raw_opts[] = { + IPV4_OPT_SSRR, 0x27, 0xff, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ssrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ssrr, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with SSRR option (ptr not in 4 byte increment). */ +int DecodeIPV4OptionsSSRRTest04(void) { + uint8_t raw_opts[] = { + IPV4_OPT_SSRR, 0x27, 0x05, 0xc0, 0xa8, 0x2a, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",ssrr=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_ssrr, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with RTRALT option. */ +int DecodeIPV4OptionsRTRALTTest01(void) { + uint8_t raw_opts[] = { + IPV4_OPT_RTRALT, 0x04, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rtralt=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rtralt, (uintmax_t)&p.IPV4_OPTS[0]); + if ( (rc == 0) + && (p.IPV4_OPTS_CNT == 1) + && (p.IPV4_OPTS[0].type == IPV4_OPT_RTRALT) + && (p.IPV4_OPTS[0].len == 0x04) + && (p.ip4vars.o_rtralt == &p.IPV4_OPTS[0])) + { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +/** \test IPV4 with RTRALT option (len invalid. */ +int DecodeIPV4OptionsRTRALTTest02(void) { + uint8_t raw_opts[] = { + IPV4_OPT_RTRALT, 0x05, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00 + }; + Packet p; + ThreadVars tv; + int rc; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + + rc = DecodeIPV4Options(&tv, &p, raw_opts, sizeof(raw_opts)); + //printf("rc=%d,cnt=%" PRIu16 ",type=%" PRIu8 ",len=%" PRIu8 ",rtralt=%" PRIuMAX "/%" PRIuMAX "\n", rc, p.IPV4_OPTS_CNT, p.IPV4_OPTS[0].type, p.IPV4_OPTS[0].len, (uintmax_t)p.ip4vars.o_rtralt, (uintmax_t)&p.IPV4_OPTS[0]); + if (rc != 0) { + return 1; + } + + DecodeIPV4OptionsPrint(&p); + return 0; +} + +void DecodeIPV4RegisterTests(void) { + UtRegisterTest("DecodeIPV4OptionsNONETest01", DecodeIPV4OptionsNONETest01, 1); + UtRegisterTest("DecodeIPV4OptionsEOLTest01", DecodeIPV4OptionsEOLTest01, 1); + UtRegisterTest("DecodeIPV4OptionsNOPTest01", DecodeIPV4OptionsNOPTest01, 1); + UtRegisterTest("DecodeIPV4OptionsRRTest01", DecodeIPV4OptionsRRTest01, 1); + UtRegisterTest("DecodeIPV4OptionsRRTest02", DecodeIPV4OptionsRRTest02, 1); + UtRegisterTest("DecodeIPV4OptionsRRTest03", DecodeIPV4OptionsRRTest03, 1); + UtRegisterTest("DecodeIPV4OptionsRRTest04", DecodeIPV4OptionsRRTest04, 1); + UtRegisterTest("DecodeIPV4OptionsQSTest01", DecodeIPV4OptionsQSTest01, 1); + UtRegisterTest("DecodeIPV4OptionsQSTest02", DecodeIPV4OptionsQSTest02, 1); + UtRegisterTest("DecodeIPV4OptionsTSTest01", DecodeIPV4OptionsTSTest01, 1); + UtRegisterTest("DecodeIPV4OptionsTSTest02", DecodeIPV4OptionsTSTest02, 1); + UtRegisterTest("DecodeIPV4OptionsTSTest03", DecodeIPV4OptionsTSTest03, 1); + UtRegisterTest("DecodeIPV4OptionsTSTest04", DecodeIPV4OptionsTSTest04, 1); + UtRegisterTest("DecodeIPV4OptionsSECTest01", DecodeIPV4OptionsSECTest01, 1); + UtRegisterTest("DecodeIPV4OptionsSECTest02", DecodeIPV4OptionsSECTest02, 1); + UtRegisterTest("DecodeIPV4OptionsLSRRTest01", DecodeIPV4OptionsLSRRTest01, 1); + UtRegisterTest("DecodeIPV4OptionsLSRRTest02", DecodeIPV4OptionsLSRRTest02, 1); + UtRegisterTest("DecodeIPV4OptionsLSRRTest03", DecodeIPV4OptionsLSRRTest03, 1); + UtRegisterTest("DecodeIPV4OptionsLSRRTest04", DecodeIPV4OptionsLSRRTest04, 1); + UtRegisterTest("DecodeIPV4OptionsCIPSOTest01", DecodeIPV4OptionsCIPSOTest01, 1); + UtRegisterTest("DecodeIPV4OptionsSIDTest01", DecodeIPV4OptionsSIDTest01, 1); + UtRegisterTest("DecodeIPV4OptionsSIDTest02", DecodeIPV4OptionsSIDTest02, 1); + UtRegisterTest("DecodeIPV4OptionsSSRRTest01", DecodeIPV4OptionsSSRRTest01, 1); + UtRegisterTest("DecodeIPV4OptionsSSRRTest02", DecodeIPV4OptionsSSRRTest02, 1); + UtRegisterTest("DecodeIPV4OptionsSSRRTest03", DecodeIPV4OptionsSSRRTest03, 1); + UtRegisterTest("DecodeIPV4OptionsSSRRTest04", DecodeIPV4OptionsSSRRTest04, 1); + UtRegisterTest("DecodeIPV4OptionsRTRALTTest01", DecodeIPV4OptionsRTRALTTest01, 1); + UtRegisterTest("DecodeIPV4OptionsRTRALTTest02", DecodeIPV4OptionsRTRALTTest02, 1); +} + +#endif /* UNITTESTS */ diff --git a/src/decode-ipv4.h b/src/decode-ipv4.h index 59d2051306..5aa91e2630 100644 --- a/src/decode-ipv4.h +++ b/src/decode-ipv4.h @@ -9,30 +9,60 @@ #include #include -#define IPV4_HEADER_LEN 20 -#define IPV4_OPTMAX 40 -#define IPV4_MAXPACKET_LEN 65535 /* maximum packet size */ - -#define IPV4_OPT_EOL 0x00 -#define IPV4_OPT_NOP 0x01 -#define IPV4_OPT_RR 0x07 -#define IPV4_OPT_RTRALT 0x94 -#define IPV4_OPT_TS 0x44 -#define IPV4_OPT_SECURITY 0x82 -#define IPV4_OPT_LSRR 0x83 +#define IPV4_HEADER_LEN 20 /**< Header length */ +#define IPV4_OPTMAX 40 /**< Max options length */ +#define IPV4_MAXPACKET_LEN 65535 /**< Maximum packet size */ + +/** IP Option Types */ +#define IPV4_OPT_EOL 0x00 /**< Option: End of List */ +#define IPV4_OPT_NOP 0x01 /**< Option: No op */ +#define IPV4_OPT_RR 0x07 /**< Option: Record Route */ +#define IPV4_OPT_QS 0x19 /**< Option: Quick Start */ +#define IPV4_OPT_TS 0x44 /**< Option: Timestamp */ +#define IPV4_OPT_SEC 0x82 /**< Option: Security */ +#define IPV4_OPT_LSRR 0x83 /**< Option: Loose Source Route */ +#define IPV4_OPT_CIPSO 0x86 /**< Option: Commercial IP Security */ +#define IPV4_OPT_SID 0x88 /**< Option: Stream Identifier */ +#define IPV4_OPT_SSRR 0x89 /**< Option: Strict Source Route */ +#define IPV4_OPT_RTRALT 0x94 /**< Option: Router Alert */ + +/** IP Option Lengths (fixed) */ +#define IPV4_OPT_SEC_LEN 11 /**< SEC Option Fixed Length */ +#define IPV4_OPT_SID_LEN 4 /**< SID Option Fixed Length */ +#define IPV4_OPT_RTRALT_LEN 4 /**< RTRALT Option Fixed Length */ + +/** IP Option Lengths (variable) */ +#define IPV4_OPT_ROUTE_MIN 3 /**< RR, SRR, LTRR Option Min Length */ +#define IPV4_OPT_QS_MIN 8 /**< QS Option Min Length */ +#define IPV4_OPT_TS_MIN 5 /**< TS Option Min Length */ +#define IPV4_OPT_CIPSO_MIN 10 /**< CIPSO Option Min Length */ + +/** IP Option fields */ +#define IPV4_OPTS ip4vars.ip_opts +#define IPV4_OPTS_CNT ip4vars.ip_opt_cnt + +typedef struct IPV4Opt_ { + /** \todo We may want to break type up into its 3 fields + * as the reassembler may want to know which options + * must be copied to each fragment. + */ + uint8_t type; /**< option type */ + uint8_t len; /**< option length (type+len+data) */ + uint8_t *data; /**< option data */ +} IPV4Opt; typedef struct IPV4Hdr_ { - uint8_t ip_verhl; /* version & header length */ - uint8_t ip_tos; - uint16_t ip_len; /* length */ - uint16_t ip_id; /* id */ - uint16_t ip_off; /* frag offset */ - uint8_t ip_ttl; - uint8_t ip_proto; /* protocol (tcp, udp, etc) */ - uint16_t ip_csum; /* checksum */ - struct in_addr ip_src; - struct in_addr ip_dst; + uint8_t ip_verhl; /**< version & header length */ + uint8_t ip_tos; /**< type of service */ + uint16_t ip_len; /**< length */ + uint16_t ip_id; /**< id */ + uint16_t ip_off; /**< frag offset */ + uint8_t ip_ttl; /**< time to live */ + uint8_t ip_proto; /**< protocol (tcp, udp, etc) */ + uint16_t ip_csum; /**< checksum */ + struct in_addr ip_src;/**< source address */ + struct in_addr ip_dst;/**< destination address */ } IPV4Hdr; #define IPV4_GET_RAW_VER(ip4h) (((ip4h)->ip_verhl & 0xf0) >> 4) @@ -143,8 +173,23 @@ typedef struct IPV4Cache_ /* helper structure with parsed ipv4 info */ typedef struct IPV4Vars_ { - uint8_t ip_opts_len; + uint8_t ip_opt_len; + IPV4Opt ip_opts[IPV4_OPTMAX]; + uint8_t ip_opt_cnt; + + /* These are here for direct access and dup tracking */ + IPV4Opt *o_rr; + IPV4Opt *o_qs; + IPV4Opt *o_ts; + IPV4Opt *o_sec; + IPV4Opt *o_lsrr; + IPV4Opt *o_cipso; + IPV4Opt *o_sid; + IPV4Opt *o_ssrr; + IPV4Opt *o_rtralt; } IPV4Vars; +void DecodeIPV4RegisterTests(void); + #endif /* __DECODE_IPV4_H__ */ diff --git a/src/decode-tcp.c b/src/decode-tcp.c index bcb81a5349..163e535a4c 100644 --- a/src/decode-tcp.c +++ b/src/decode-tcp.c @@ -35,6 +35,7 @@ static int DecodeTCPOptions(ThreadVars *tv, Packet *p, uint8_t *pkt, uint16_t le /* we already know that the total options len is valid, * so here the len of the specific option must be bad. * Also check for invalid lengths 0 and 1. */ + /** \todo Should we check this *before* we set the data so that the incorrect data is not used later on? */ if (p->TCP_OPTS[p->TCP_OPTS_CNT].len > plen || p->TCP_OPTS[p->TCP_OPTS_CNT].len < 2) { DECODER_SET_EVENT(p,TCP_OPT_INVALID_LEN); diff --git a/src/detect-decode-event.h b/src/detect-decode-event.h index 9d7c354247..651f806e7a 100644 --- a/src/detect-decode-event.h +++ b/src/detect-decode-event.h @@ -26,6 +26,13 @@ struct DetectDecodeEvents_ { { "ipv4.hlen_too_small", IPV4_HLEN_TOO_SMALL, }, { "ipv4.iplen_smaller_than_hlen", IPV4_IPLEN_SMALLER_THAN_HLEN, }, { "ipv4.trunc_pkt", IPV4_TRUNC_PKT, }, + { "ipv4.opt_invalid", IPV4_OPT_INVALID, }, + { "ipv4.opt_invalid_len", IPV4_OPT_INVALID_LEN, }, + { "ipv4.opt_malformed", IPV4_OPT_MALFORMED, }, + { "ipv4.opt_pad_required", IPV4_OPT_PAD_REQUIRED, }, + { "ipv4.opt_eol_required", IPV4_OPT_EOL_REQUIRED, }, + { "ipv4.opt_duplicate", IPV4_OPT_DUPLICATE, }, + { "ipv4.opt_unknown", IPV4_OPT_UNKNOWN, }, { "ipv6.pkt_too_small", IPV6_PKT_TOO_SMALL, }, { "ipv6.trunc_pkt", IPV6_TRUNC_PKT, }, { "ipv6.trunc_exthdr", IPV6_TRUNC_EXTHDR, }, diff --git a/src/eidps.c b/src/eidps.c index 11c568d0d2..96ab2547ad 100644 --- a/src/eidps.c +++ b/src/eidps.c @@ -971,6 +971,7 @@ int main(int argc, char **argv) HTTPParserRegisterTests(); DecodePPPoERegisterTests(); DecodeICMPV4RegisterTests(); + DecodeIPV4RegisterTests(); AlpDetectRegisterTests(); ConfRegisterTests(); UtRunTests();