diff options
Diffstat (limited to 'src/detect-engine-iponly.c')
-rw-r--r-- | src/detect-engine-iponly.c | 2453 |
1 files changed, 2453 insertions, 0 deletions
diff --git a/src/detect-engine-iponly.c b/src/detect-engine-iponly.c new file mode 100644 index 0000000..03b4649 --- /dev/null +++ b/src/detect-engine-iponly.c @@ -0,0 +1,2453 @@ +/* Copyright (C) 2007-2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Victor Julien <victor@inliniac.net> + * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com> + * + * Signatures that only inspect IP addresses are processed here + * We use radix trees for src dst ipv4 and ipv6 addresses + * This radix trees hold information for subnets and hosts in a + * hierarchical distribution + */ + +#include "suricata-common.h" +#include "detect.h" +#include "decode.h" +#include "flow.h" + +#include "detect-parse.h" +#include "detect-engine.h" + +#include "detect-engine-siggroup.h" +#include "detect-engine-address.h" +#include "detect-engine-proto.h" +#include "detect-engine-port.h" +#include "detect-engine-mpm.h" +#include "detect-engine-build.h" + +#include "detect-engine-threshold.h" +#include "detect-engine-iponly.h" +#include "detect-threshold.h" +#include "util-classification-config.h" +#include "util-rule-vars.h" + +#include "flow-util.h" +#include "util-debug.h" +#include "util-unittest.h" +#include "util-unittest-helper.h" +#include "util-print.h" +#include "util-byte.h" +#include "util-profiling.h" +#include "util-validate.h" +#include "util-cidr.h" + +#ifdef OS_WIN32 +#include <winsock.h> +#else +#include <netinet/in.h> +#endif /* OS_WIN32 */ + +/** + * \brief This function creates a new IPOnlyCIDRItem + * + * \retval IPOnlyCIDRItem address of the new instance + */ +static IPOnlyCIDRItem *IPOnlyCIDRItemNew(void) +{ + SCEnter(); + IPOnlyCIDRItem *item = NULL; + + item = SCMalloc(sizeof(IPOnlyCIDRItem)); + if (unlikely(item == NULL)) + SCReturnPtr(NULL, "IPOnlyCIDRItem"); + memset(item, 0, sizeof(IPOnlyCIDRItem)); + + SCReturnPtr(item, "IPOnlyCIDRItem"); +} + +static uint8_t IPOnlyCIDRItemCompare(IPOnlyCIDRItem *head, + IPOnlyCIDRItem *item) +{ + uint8_t i = 0; + for (; i < head->netmask / 32 || i < 1; i++) { + if (item->ip[i] < head->ip[i]) + //if (*(uint8_t *)(item->ip + i) < *(uint8_t *)(head->ip + i)) + return 1; + } + return 0; +} + +//declaration for using it already +static IPOnlyCIDRItem *IPOnlyCIDRItemInsert(IPOnlyCIDRItem *head, + IPOnlyCIDRItem *item); + +static int InsertRange( + IPOnlyCIDRItem **pdd, IPOnlyCIDRItem *dd, const uint32_t first_in, const uint32_t last_in) +{ + DEBUG_VALIDATE_BUG_ON(dd == NULL); + DEBUG_VALIDATE_BUG_ON(pdd == NULL); + + uint32_t first = first_in; + uint32_t last = last_in; + + dd->netmask = 32; + /* Find the maximum netmask starting from current address first + * and not crossing last. + * To extend the mask, we need to start from a power of 2. + * And we need to pay attention to unsigned overflow back to 0.0.0.0 + */ + while (dd->netmask > 0 && (first & (1UL << (32 - dd->netmask))) == 0 && + first + (1UL << (32 - (dd->netmask - 1))) - 1 <= last) { + dd->netmask--; + } + dd->ip[0] = htonl(first); + first += 1UL << (32 - dd->netmask); + // case whatever-255.255.255.255 looping to 0.0.0.0/0 + while (first <= last && first != 0) { + IPOnlyCIDRItem *new = IPOnlyCIDRItemNew(); + if (new == NULL) + goto error; + new->negated = dd->negated; + new->family = dd->family; + new->netmask = 32; + while (new->netmask > 0 && (first & (1UL << (32 - new->netmask))) == 0 && + first + (1UL << (32 - (new->netmask - 1))) - 1 <= last) { + new->netmask--; + } + new->ip[0] = htonl(first); + first += 1UL << (32 - new->netmask); + dd = IPOnlyCIDRItemInsert(dd, new); + } + // update head of list + *pdd = dd; + return 0; +error: + return -1; +} + +/** + * \internal + * \brief Parses an ipv4/ipv6 address string and updates the result into the + * IPOnlyCIDRItem instance sent as the argument. + * + * \param pdd Double pointer to the IPOnlyCIDRItem instance which should be updated + * with the address (in cidr) details from the parsed ip string. + * \param str Pointer to address string that has to be parsed. + * + * \retval 0 On successfully parsing the address string. + * \retval -1 On failure. + */ +static int IPOnlyCIDRItemParseSingle(IPOnlyCIDRItem **pdd, const char *str) +{ + char buf[256] = ""; + char *ip = NULL, *ip2 = NULL; + char *mask = NULL; + int r = 0; + IPOnlyCIDRItem *dd = *pdd; + + while (*str != '\0' && *str == ' ') + str++; + + SCLogDebug("str %s", str); + strlcpy(buf, str, sizeof(buf)); + ip = buf; + + /* first handle 'any' */ + if (strcasecmp(str, "any") == 0) { + /* if any, insert 0.0.0.0/0 and ::/0 as well */ + SCLogDebug("adding 0.0.0.0/0 and ::/0 as we\'re handling \'any\'"); + + IPOnlyCIDRItemParseSingle(&dd, "0.0.0.0/0"); + BUG_ON(dd->family == 0); + + dd->next = IPOnlyCIDRItemNew(); + if (dd->next == NULL) + goto error; + + IPOnlyCIDRItemParseSingle(&dd->next, "::/0"); + BUG_ON(dd->family == 0); + + SCLogDebug("address is \'any\'"); + return 0; + } + + /* handle the negation case */ + if (ip[0] == '!') { + dd->negated = (dd->negated)? 0 : 1; + ip++; + } + + /* see if the address is an ipv4 or ipv6 address */ + if ((strchr(str, ':')) == NULL) { + /* IPv4 Address */ + struct in_addr in; + + dd->family = AF_INET; + + if ((mask = strchr(ip, '/')) != NULL) { + /* 1.2.3.4/xxx format (either dotted or cidr notation */ + ip[mask - ip] = '\0'; + mask++; + uint32_t netmask = 0; + size_t u = 0; + + if ((strchr (mask, '.')) == NULL) { + /* 1.2.3.4/24 format */ + + for (u = 0; u < strlen(mask); u++) { + if(!isdigit((unsigned char)mask[u])) + goto error; + } + + uint8_t cidr; + if (StringParseU8RangeCheck(&cidr, 10, 0, (const char *)mask, 0, 32) < 0) + goto error; + + dd->netmask = cidr; + netmask = CIDRGet(cidr); + } else { + /* 1.2.3.4/255.255.255.0 format */ + r = inet_pton(AF_INET, mask, &in); + if (r <= 0) + goto error; + + int cidr = CIDRFromMask(in.s_addr); + if (cidr < 0) + goto error; + + dd->netmask = (uint8_t)cidr; + } + + r = inet_pton(AF_INET, ip, &in); + if (r <= 0) + goto error; + + dd->ip[0] = in.s_addr & netmask; + + } else if ((ip2 = strchr(ip, '-')) != NULL) { + /* 1.2.3.4-1.2.3.6 range format */ + ip[ip2 - ip] = '\0'; + ip2++; + + uint32_t first, last; + + r = inet_pton(AF_INET, ip, &in); + if (r <= 0) + goto error; + first = SCNtohl(in.s_addr); + + r = inet_pton(AF_INET, ip2, &in); + if (r <= 0) + goto error; + last = SCNtohl(in.s_addr); + + /* a > b is illegal, a = b is ok */ + if (first > last) + goto error; + + SCLogDebug("Creating CIDR range for [%s - %s]", ip, ip2); + return InsertRange(pdd, dd, first, last); + } else { + /* 1.2.3.4 format */ + r = inet_pton(AF_INET, ip, &in); + if (r <= 0) + goto error; + + /* single host */ + dd->ip[0] = in.s_addr; + dd->netmask = 32; + } + } else { + /* IPv6 Address */ + struct in6_addr in6, mask6; + uint32_t ip6addr[4], netmask[4]; + + dd->family = AF_INET6; + + if ((mask = strchr(ip, '/')) != NULL) { + mask[0] = '\0'; + mask++; + + r = inet_pton(AF_INET6, ip, &in6); + if (r <= 0) + goto error; + + /* Format is cidr val */ + if (StringParseU8RangeCheck(&dd->netmask, 10, 0, + (const char *)mask, 0, 128) < 0) { + goto error; + } + + memcpy(&ip6addr, &in6.s6_addr, sizeof(ip6addr)); + CIDRGetIPv6(dd->netmask, &mask6); + memcpy(&netmask, &mask6.s6_addr, sizeof(netmask)); + + dd->ip[0] = ip6addr[0] & netmask[0]; + dd->ip[1] = ip6addr[1] & netmask[1]; + dd->ip[2] = ip6addr[2] & netmask[2]; + dd->ip[3] = ip6addr[3] & netmask[3]; + } else { + r = inet_pton(AF_INET6, ip, &in6); + if (r <= 0) + goto error; + + memcpy(dd->ip, &in6.s6_addr, sizeof(dd->ip)); + dd->netmask = 128; + } + + } + + BUG_ON(dd->family == 0); + return 0; + +error: + return -1; +} + +/** + * \brief Setup a single address string, parse it and add the resulting + * Address items in cidr format to the list of gh + * + * \param gh Pointer to the IPOnlyCIDRItem list Head to which the + * resulting Address-Range(s) from the parsed ip string has to + * be added. + * \param s Pointer to the ip address string to be parsed. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +static int IPOnlyCIDRItemSetup(IPOnlyCIDRItem **gh, char *s) +{ + SCLogDebug("gh %p, s %s", *gh, s); + + /* parse the address */ + if (IPOnlyCIDRItemParseSingle(gh, s) == -1) { + SCLogError("address parsing error \"%s\"", s); + goto error; + } + + return 0; + +error: + return -1; +} + + +/** + * \brief This function insert a IPOnlyCIDRItem + * to a list of IPOnlyCIDRItems sorted by netmask + * ascending + * \param head Pointer to the head of IPOnlyCIDRItems list + * \param item Pointer to the item to insert in the list + * + * \retval IPOnlyCIDRItem address of the new head if apply + */ +static IPOnlyCIDRItem *IPOnlyCIDRItemInsertReal(IPOnlyCIDRItem *head, + IPOnlyCIDRItem *item) +{ + IPOnlyCIDRItem *it, *prev = NULL; + + if (item == NULL) + return head; + + /* Compare with the head */ + if (item->netmask < head->netmask || (item->netmask == head->netmask && IPOnlyCIDRItemCompare(head, item))) { + item->next = head; + return item; + } + + if (item->netmask == head->netmask && !IPOnlyCIDRItemCompare(head, item)) { + item->next = head->next; + head->next = item; + return head; + } + + for (prev = it = head; + it != NULL && it->netmask < item->netmask; + it = it->next) + prev = it; + + if (it == NULL) { + prev->next = item; + item->next = NULL; + } else { + item->next = it; + prev->next = item; + } + + return head; +} + +/** + * \brief This function insert a IPOnlyCIDRItem list + * to a list of IPOnlyCIDRItems sorted by netmask + * ascending + * \param head Pointer to the head of IPOnlyCIDRItems list + * \param item Pointer to the list of items to insert in the list + * + * \retval IPOnlyCIDRItem address of the new head if apply + */ +static IPOnlyCIDRItem *IPOnlyCIDRItemInsert(IPOnlyCIDRItem *head, + IPOnlyCIDRItem *item) +{ + IPOnlyCIDRItem *it, *prev = NULL; + + /* The first element */ + if (head == NULL) { + SCLogDebug("Head is NULL to insert item (%p)",item); + return item; + } + + if (item == NULL) { + SCLogDebug("Item is NULL"); + return head; + } + + SCLogDebug("Inserting item(%p)->netmask %u head %p", item, item->netmask, head); + + prev = item; + while (prev != NULL) { + it = prev->next; + + /* Separate from the item list */ + prev->next = NULL; + + //SCLogDebug("Before:"); + //IPOnlyCIDRListPrint(head); + head = IPOnlyCIDRItemInsertReal(head, prev); + //SCLogDebug("After:"); + //IPOnlyCIDRListPrint(head); + prev = it; + } + + return head; +} + +/** + * \brief This function free a IPOnlyCIDRItem list + * \param tmphead Pointer to the list + */ +void IPOnlyCIDRListFree(IPOnlyCIDRItem *tmphead) +{ + SCEnter(); +#ifdef DEBUG + uint32_t i = 0; +#endif + IPOnlyCIDRItem *it, *next = NULL; + + if (tmphead == NULL) { + SCLogDebug("temphead is NULL"); + return; + } + + it = tmphead; + next = it->next; + + while (it != NULL) { +#ifdef DEBUG + i++; + SCLogDebug("Item(%p) %"PRIu32" removed", it, i); +#endif + SCFree(it); + it = next; + + if (next != NULL) + next = next->next; + } + SCReturn; +} + +/** + * \brief This function update a list of IPOnlyCIDRItems + * setting the signature internal id (signum) to "i" + * + * \param tmphead Pointer to the list + * \param i number of signature internal id + */ +static void IPOnlyCIDRListSetSigNum(IPOnlyCIDRItem *tmphead, SigIntId i) +{ + while (tmphead != NULL) { + tmphead->signum = i; + tmphead = tmphead->next; + } +} + +#ifdef UNITTESTS +/** + * \brief This function print a IPOnlyCIDRItem list + * \param tmphead Pointer to the head of IPOnlyCIDRItems list + */ +static void IPOnlyCIDRListPrint(IPOnlyCIDRItem *tmphead) +{ +#ifdef DEBUG + uint32_t i = 0; + + while (tmphead != NULL) { + i++; + SCLogDebug("Item %"PRIu32" has netmask %"PRIu8" negated:" + " %s; IP: %s; signum: %"PRIu32, i, tmphead->netmask, + (tmphead->negated) ? "yes":"no", + inet_ntoa(*(struct in_addr*)&tmphead->ip[0]), + tmphead->signum); + tmphead = tmphead->next; + } +#endif +} +#endif + +/** \brief user data for storing signature id's in the radix tree + * + * Bit array representing signature internal id's (Signature::num). + */ +typedef struct SigNumArray_ { + uint8_t *array; /* bit array of sig nums */ + uint32_t size; /* size in bytes of the array */ +} SigNumArray; + +/** + * \brief This function print a SigNumArray, it's used with the + * radix tree print function to help debugging + * \param tmp Pointer to the head of SigNumArray + */ +static void SigNumArrayPrint(void *tmp) +{ + SigNumArray *sna = (SigNumArray *)tmp; + for (uint32_t u = 0; u < sna->size; u++) { + uint8_t bitarray = sna->array[u]; + for (uint8_t i = 0; i < 8; i++) { + if (bitarray & 0x01) + printf("%" PRIu32 " ", u * 8 + i); + bitarray = bitarray >> 1; + } + } +} + +/** + * \brief This function creates a new SigNumArray with the + * size fixed to the io_ctx->max_idx + * \param de_ctx Pointer to the current detection context + * \param io_ctx Pointer to the current ip only context + * + * \retval SigNumArray address of the new instance + */ +static SigNumArray *SigNumArrayNew(DetectEngineCtx *de_ctx, + DetectEngineIPOnlyCtx *io_ctx) +{ + SigNumArray *new = SCMalloc(sizeof(SigNumArray)); + + if (unlikely(new == NULL)) { + FatalError("Fatal error encountered in SigNumArrayNew. Exiting..."); + } + memset(new, 0, sizeof(SigNumArray)); + + new->array = SCMalloc(io_ctx->max_idx / 8 + 1); + if (new->array == NULL) { + exit(EXIT_FAILURE); + } + + memset(new->array, 0, io_ctx->max_idx / 8 + 1); + new->size = io_ctx->max_idx / 8 + 1; + + SCLogDebug("max idx= %u", io_ctx->max_idx); + + return new; +} + +/** + * \brief This function creates a new SigNumArray with the + * same data as the argument + * + * \param orig Pointer to the original SigNumArray to copy + * + * \retval SigNumArray address of the new instance + */ +static SigNumArray *SigNumArrayCopy(SigNumArray *orig) +{ + SigNumArray *new = SCMalloc(sizeof(SigNumArray)); + + if (unlikely(new == NULL)) { + FatalError("Fatal error encountered in SigNumArrayCopy. Exiting..."); + } + + memset(new, 0, sizeof(SigNumArray)); + new->size = orig->size; + + new->array = SCMalloc(orig->size); + if (new->array == NULL) { + exit(EXIT_FAILURE); + } + + memcpy(new->array, orig->array, orig->size); + return new; +} + +/** + * \brief This function free() a SigNumArray + * \param orig Pointer to the original SigNumArray to copy + */ +static void SigNumArrayFree(void *tmp) +{ + SigNumArray *sna = (SigNumArray *)tmp; + + if (sna == NULL) + return; + + if (sna->array != NULL) + SCFree(sna->array); + + SCFree(sna); +} + +/** + * \brief This function parses and return a list of IPOnlyCIDRItem + * + * \param s Pointer to the string of the addresses + * (in the format of signatures) + * \param negate flag to indicate if all this string is negated or not + * + * \retval 0 if success + * \retval -1 if fails + */ +static IPOnlyCIDRItem *IPOnlyCIDRListParse2( + const DetectEngineCtx *de_ctx, const char *s, int negate) +{ + size_t x = 0; + size_t u = 0; + int o_set = 0, n_set = 0, d_set = 0; + int depth = 0; + size_t size = strlen(s); + char address[8196] = ""; + const char *rule_var_address = NULL; + char *temp_rule_var_address = NULL; + IPOnlyCIDRItem *head; + IPOnlyCIDRItem *subhead; + head = subhead = NULL; + + SCLogDebug("s %s negate %s", s, negate ? "true" : "false"); + + for (u = 0, x = 0; u < size && x < sizeof(address); u++) { + address[x] = s[u]; + x++; + + if (!o_set && s[u] == '!') { + n_set = 1; + x--; + } else if (s[u] == '[') { + if (!o_set) { + o_set = 1; + x = 0; + } + depth++; + } else if (s[u] == ']') { + if (depth == 1) { + address[x - 1] = '\0'; + x = 0; + + if ( (subhead = IPOnlyCIDRListParse2(de_ctx, address, + (negate + n_set) % 2)) == NULL) + goto error; + + head = IPOnlyCIDRItemInsert(head, subhead); + n_set = 0; + } + depth--; + } else if (depth == 0 && s[u] == ',') { + if (o_set == 1) { + o_set = 0; + } else if (d_set == 1) { + address[x - 1] = '\0'; + + rule_var_address = SCRuleVarsGetConfVar(de_ctx, address, + SC_RULE_VARS_ADDRESS_GROUPS); + if (rule_var_address == NULL) + goto error; + + if ((negate + n_set) % 2) { + temp_rule_var_address = SCMalloc(strlen(rule_var_address) + 3); + if (unlikely(temp_rule_var_address == NULL)) { + goto error; + } + + snprintf(temp_rule_var_address, strlen(rule_var_address) + 3, + "[%s]", rule_var_address); + } else { + temp_rule_var_address = SCStrdup(rule_var_address); + if (unlikely(temp_rule_var_address == NULL)) { + goto error; + } + } + + subhead = IPOnlyCIDRListParse2(de_ctx, temp_rule_var_address, + (negate + n_set) % 2); + head = IPOnlyCIDRItemInsert(head, subhead); + + d_set = 0; + n_set = 0; + + SCFree(temp_rule_var_address); + + } else { + address[x - 1] = '\0'; + + subhead = IPOnlyCIDRItemNew(); + if (subhead == NULL) + goto error; + + if (!((negate + n_set) % 2)) + subhead->negated = 0; + else + subhead->negated = 1; + + if (IPOnlyCIDRItemSetup(&subhead, address) < 0) { + IPOnlyCIDRListFree(subhead); + subhead = NULL; + goto error; + } + head = IPOnlyCIDRItemInsert(head, subhead); + + n_set = 0; + } + x = 0; + } else if (depth == 0 && s[u] == '$') { + d_set = 1; + } else if (depth == 0 && u == size - 1) { + if (x == sizeof(address)) { + address[x - 1] = '\0'; + } else { + address[x] = '\0'; + } + x = 0; + + if (d_set == 1) { + rule_var_address = SCRuleVarsGetConfVar(de_ctx, address, + SC_RULE_VARS_ADDRESS_GROUPS); + if (rule_var_address == NULL) + goto error; + + if ((negate + n_set) % 2) { + temp_rule_var_address = SCMalloc(strlen(rule_var_address) + 3); + if (unlikely(temp_rule_var_address == NULL)) { + goto error; + } + snprintf(temp_rule_var_address, strlen(rule_var_address) + 3, + "[%s]", rule_var_address); + } else { + temp_rule_var_address = SCStrdup(rule_var_address); + if (unlikely(temp_rule_var_address == NULL)) { + goto error; + } + } + subhead = IPOnlyCIDRListParse2(de_ctx, temp_rule_var_address, + (negate + n_set) % 2); + head = IPOnlyCIDRItemInsert(head, subhead); + + d_set = 0; + + SCFree(temp_rule_var_address); + } else { + subhead = IPOnlyCIDRItemNew(); + if (subhead == NULL) + goto error; + + if (!((negate + n_set) % 2)) + subhead->negated = 0; + else + subhead->negated = 1; + + if (IPOnlyCIDRItemSetup(&subhead, address) < 0) { + IPOnlyCIDRListFree(subhead); + subhead = NULL; + goto error; + } + head = IPOnlyCIDRItemInsert(head, subhead); + } + n_set = 0; + } + } + + return head; + +error: + SCLogError("Error parsing addresses"); + return head; +} + + +/** + * \brief Parses an address group sent as a character string and updates the + * IPOnlyCIDRItem list + * + * \param gh Pointer to the IPOnlyCIDRItem list + * \param str Pointer to the character string containing the address group + * that has to be parsed. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +static int IPOnlyCIDRListParse(const DetectEngineCtx *de_ctx, IPOnlyCIDRItem **gh, const char *str) +{ + SCLogDebug("gh %p, str %s", gh, str); + + if (gh == NULL) + goto error; + + *gh = IPOnlyCIDRListParse2(de_ctx, str, 0); + if (*gh == NULL) { + SCLogDebug("IPOnlyCIDRListParse2 returned null"); + goto error; + } + + return 0; + +error: + return -1; +} + +/** + * \brief Parses an address group sent as a character string and updates the + * IPOnlyCIDRItem lists src and dst of the Signature *s + * + * \param s Pointer to the signature structure + * \param addrstr Pointer to the character string containing the address group + * that has to be parsed. + * \param flag to indicate if we are parsing the src string or the dst string + * + * \retval 0 On success. + * \retval -1 On failure. + */ +int IPOnlySigParseAddress(const DetectEngineCtx *de_ctx, + Signature *s, const char *addrstr, char flag) +{ + SCLogDebug("Address Group \"%s\" to be parsed now", addrstr); + + /* pass on to the address(list) parser */ + if (flag == 0) { + if (strcasecmp(addrstr, "any") == 0) { + s->flags |= SIG_FLAG_SRC_ANY; + if (IPOnlyCIDRListParse(de_ctx, &s->cidr_src, "[0.0.0.0/0,::/0]") < 0) + goto error; + + } else if (IPOnlyCIDRListParse(de_ctx, &s->cidr_src, (char *)addrstr) < 0) { + goto error; + } + + /* IPOnlyCIDRListPrint(s->CidrSrc); */ + } else { + if (strcasecmp(addrstr, "any") == 0) { + s->flags |= SIG_FLAG_DST_ANY; + if (IPOnlyCIDRListParse(de_ctx, &s->cidr_dst, "[0.0.0.0/0,::/0]") < 0) + goto error; + + } else if (IPOnlyCIDRListParse(de_ctx, &s->cidr_dst, (char *)addrstr) < 0) { + goto error; + } + + /* IPOnlyCIDRListPrint(s->CidrDst); */ + } + + return 0; + +error: + SCLogError("failed to parse addresses"); + return -1; +} + +/** + * \brief Setup the IP Only detection engine context + * + * \param de_ctx Pointer to the current detection engine + * \param io_ctx Pointer to the current ip only detection engine + */ +void IPOnlyInit(DetectEngineCtx *de_ctx, DetectEngineIPOnlyCtx *io_ctx) +{ + io_ctx->tree_ipv4src = SCRadixCreateRadixTree(SigNumArrayFree, SigNumArrayPrint); + io_ctx->tree_ipv4dst = SCRadixCreateRadixTree(SigNumArrayFree, SigNumArrayPrint); + io_ctx->tree_ipv6src = SCRadixCreateRadixTree(SigNumArrayFree, SigNumArrayPrint); + io_ctx->tree_ipv6dst = SCRadixCreateRadixTree(SigNumArrayFree, SigNumArrayPrint); + + io_ctx->sig_mapping = SCCalloc(1, de_ctx->sig_array_len * sizeof(uint32_t)); + if (io_ctx->sig_mapping == NULL) { + FatalError("Unable to allocate iponly signature tracking area"); + } + io_ctx->sig_mapping_size = 0; +} + +SigIntId IPOnlyTrackSigNum(DetectEngineIPOnlyCtx *io_ctx, SigIntId signum) +{ + SigIntId loc = io_ctx->sig_mapping_size; + io_ctx->sig_mapping[loc] = signum; + io_ctx->sig_mapping_size++; + return loc; +} + +/** + * \brief Print stats of the IP Only engine + * + * \param de_ctx Pointer to the current detection engine + * \param io_ctx Pointer to the current ip only detection engine + */ +void IPOnlyPrint(DetectEngineCtx *de_ctx, DetectEngineIPOnlyCtx *io_ctx) +{ + /* XXX: how are we going to print the stats now? */ +} + +/** + * \brief Deinitialize the IP Only detection engine context + * + * \param de_ctx Pointer to the current detection engine + * \param io_ctx Pointer to the current ip only detection engine + */ +void IPOnlyDeinit(DetectEngineCtx *de_ctx, DetectEngineIPOnlyCtx *io_ctx) +{ + + if (io_ctx == NULL) + return; + + if (io_ctx->tree_ipv4src != NULL) + SCRadixReleaseRadixTree(io_ctx->tree_ipv4src); + io_ctx->tree_ipv4src = NULL; + + if (io_ctx->tree_ipv4dst != NULL) + SCRadixReleaseRadixTree(io_ctx->tree_ipv4dst); + io_ctx->tree_ipv4dst = NULL; + + if (io_ctx->tree_ipv6src != NULL) + SCRadixReleaseRadixTree(io_ctx->tree_ipv6src); + io_ctx->tree_ipv6src = NULL; + + if (io_ctx->tree_ipv6dst != NULL) + SCRadixReleaseRadixTree(io_ctx->tree_ipv6dst); + io_ctx->tree_ipv6dst = NULL; + + if (io_ctx->sig_mapping != NULL) + SCFree(io_ctx->sig_mapping); + io_ctx->sig_mapping = NULL; +} + +static inline +int IPOnlyMatchCompatSMs(ThreadVars *tv, + DetectEngineThreadCtx *det_ctx, + Signature *s, Packet *p) +{ + KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_MATCH); + SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_MATCH]; + if (smd) { + while (1) { + DEBUG_VALIDATE_BUG_ON(!(sigmatch_table[smd->type].flags & SIGMATCH_IPONLY_COMPAT)); + KEYWORD_PROFILING_START; + if (sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx) > 0) { + KEYWORD_PROFILING_END(det_ctx, smd->type, 1); + if (smd->is_last) + break; + smd++; + continue; + } + KEYWORD_PROFILING_END(det_ctx, smd->type, 0); + return 0; + } + } + return 1; +} + +/** + * \brief Match a packet against the IP Only detection engine contexts + * + * \param de_ctx Pointer to the current detection engine + * \param io_ctx Pointer to the current ip only detection engine + * \param io_ctx Pointer to the current ip only thread detection engine + * \param p Pointer to the Packet to match against + */ +void IPOnlyMatchPacket(ThreadVars *tv, const DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, const DetectEngineIPOnlyCtx *io_ctx, Packet *p) +{ + SigNumArray *src = NULL; + SigNumArray *dst = NULL; + void *user_data_src = NULL, *user_data_dst = NULL; + + SCEnter(); + + if (p->src.family == AF_INET) { + (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)&GET_IPV4_SRC_ADDR_U32(p), + io_ctx->tree_ipv4src, &user_data_src); + } else if (p->src.family == AF_INET6) { + (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)&GET_IPV6_SRC_ADDR(p), + io_ctx->tree_ipv6src, &user_data_src); + } + + if (p->dst.family == AF_INET) { + (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)&GET_IPV4_DST_ADDR_U32(p), + io_ctx->tree_ipv4dst, &user_data_dst); + } else if (p->dst.family == AF_INET6) { + (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)&GET_IPV6_DST_ADDR(p), + io_ctx->tree_ipv6dst, &user_data_dst); + } + + src = user_data_src; + dst = user_data_dst; + + if (src == NULL || dst == NULL) + SCReturn; + + uint32_t u; + for (u = 0; u < src->size; u++) { + SCLogDebug("And %"PRIu8" & %"PRIu8, src->array[u], dst->array[u]); + + uint8_t bitarray = dst->array[u] & src->array[u]; + + /* We have to move the logic of the signature checking + * to the main detect loop, in order to apply the + * priority of actions (pass, drop, reject, alert) */ + if (bitarray) { + /* We have a match :) Let's see from which signum's */ + uint8_t i = 0; + + for (; i < 8; i++, bitarray = bitarray >> 1) { + if (bitarray & 0x01) { + Signature *s = de_ctx->sig_array[io_ctx->sig_mapping[u * 8 + i]]; + + if ((s->proto.flags & DETECT_PROTO_IPV4) && !PKT_IS_IPV4(p)) { + SCLogDebug("ip version didn't match"); + continue; + } + if ((s->proto.flags & DETECT_PROTO_IPV6) && !PKT_IS_IPV6(p)) { + SCLogDebug("ip version didn't match"); + continue; + } + + if (DetectProtoContainsProto(&s->proto, IP_GET_IPPROTO(p)) == 0) { + SCLogDebug("proto didn't match"); + continue; + } + + /* check the source & dst port in the sig */ + if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) { + if (!(s->flags & SIG_FLAG_DP_ANY)) { + if (p->flags & PKT_IS_FRAGMENT) + continue; + + DetectPort *dport = DetectPortLookupGroup(s->dp,p->dp); + if (dport == NULL) { + SCLogDebug("dport didn't match."); + continue; + } + } + if (!(s->flags & SIG_FLAG_SP_ANY)) { + if (p->flags & PKT_IS_FRAGMENT) + continue; + + DetectPort *sport = DetectPortLookupGroup(s->sp,p->sp); + if (sport == NULL) { + SCLogDebug("sport didn't match."); + continue; + } + } + } else if ((s->flags & (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) != (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) { + SCLogDebug("port-less protocol and sig needs ports"); + continue; + } + + if (!IPOnlyMatchCompatSMs(tv, det_ctx, s, p)) { + continue; + } + + SCLogDebug("Signum %"PRIu32" match (sid: %"PRIu32", msg: %s)", + u * 8 + i, s->id, s->msg); + + if (s->sm_arrays[DETECT_SM_LIST_POSTMATCH] != NULL) { + KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_POSTMATCH); + SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_POSTMATCH]; + + SCLogDebug("running match functions, sm %p", smd); + + if (smd != NULL) { + while (1) { + KEYWORD_PROFILING_START; + (void)sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx); + KEYWORD_PROFILING_END(det_ctx, smd->type, 1); + if (smd->is_last) + break; + smd++; + } + } + } + AlertQueueAppend(det_ctx, s, p, 0, 0); + } + } + } + } + SCReturn; +} + +/** + * \brief Build the radix trees from the lists of parsed addresses in CIDR format + * the result should be 4 radix trees: src/dst ipv4 and src/dst ipv6 + * holding SigNumArrays, each of them with a hierarchical relation + * of subnets and hosts + * + * \param de_ctx Pointer to the current detection engine + */ +void IPOnlyPrepare(DetectEngineCtx *de_ctx) +{ + SCLogDebug("Preparing Final Lists"); + + /* + IPOnlyCIDRListPrint((de_ctx->io_ctx).ip_src); + IPOnlyCIDRListPrint((de_ctx->io_ctx).ip_dst); + */ + + IPOnlyCIDRItem *src, *dst; + SCRadixNode *node = NULL; + + /* Prepare Src radix trees */ + for (src = (de_ctx->io_ctx).ip_src; src != NULL; ) { + if (src->family == AF_INET) { + /* + SCLogDebug("To IPv4"); + SCLogDebug("Item has netmask %"PRIu16" negated: %s; IP: %s; " + "signum: %"PRIu16, src->netmask, + (src->negated) ? "yes":"no", + inet_ntoa( *(struct in_addr*)&src->ip[0]), + src->signum); + */ + + void *user_data = NULL; + if (src->netmask == 32) + (void)SCRadixFindKeyIPV4ExactMatch((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv4src, + &user_data); + else + (void)SCRadixFindKeyIPV4Netblock((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv4src, + src->netmask, &user_data); + if (user_data == NULL) { + SCLogDebug("Exact match not found"); + + /** Not found, look if there's a subnet of this range with + * bigger netmask */ + (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv4src, + &user_data); + if (user_data == NULL) { + SCLogDebug("best match not found"); + + /* Not found, insert a new one */ + SigNumArray *sna = SigNumArrayNew(de_ctx, &de_ctx->io_ctx); + + /* Update the sig */ + uint8_t tmp = (uint8_t)(1 << (src->signum % 8)); + + if (src->negated > 0) + /* Unset it */ + sna->array[src->signum / 8] &= ~tmp; + else + /* Set it */ + sna->array[src->signum / 8] |= tmp; + + if (src->netmask == 32) + node = SCRadixAddKeyIPV4((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv4src, sna); + else + node = SCRadixAddKeyIPV4Netblock((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv4src, + sna, src->netmask); + + if (node == NULL) + SCLogError("Error inserting in the " + "src ipv4 radix tree"); + } else { + SCLogDebug("Best match found"); + + /* Found, copy the sig num table, add this signum and insert */ + SigNumArray *sna = NULL; + sna = SigNumArrayCopy((SigNumArray *) user_data); + + /* Update the sig */ + uint8_t tmp = (uint8_t)(1 << (src->signum % 8)); + + if (src->negated > 0) + /* Unset it */ + sna->array[src->signum / 8] &= ~tmp; + else + /* Set it */ + sna->array[src->signum / 8] |= tmp; + + if (src->netmask == 32) + node = SCRadixAddKeyIPV4((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv4src, sna); + else + node = SCRadixAddKeyIPV4Netblock((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv4src, sna, + src->netmask); + + if (node == NULL) { + char tmpstr[64]; + PrintInet(src->family, &src->ip[0], tmpstr, sizeof(tmpstr)); + SCLogError("Error inserting in the" + " src ipv4 radix tree ip %s netmask %" PRIu8, + tmpstr, src->netmask); + //SCRadixPrintTree((de_ctx->io_ctx).tree_ipv4src); + exit(-1); + } + } + } else { + SCLogDebug("Exact match found"); + + /* it's already inserted. Update it */ + SigNumArray *sna = (SigNumArray *)user_data; + + /* Update the sig */ + uint8_t tmp = (uint8_t)(1 << (src->signum % 8)); + + if (src->negated > 0) + /* Unset it */ + sna->array[src->signum / 8] &= ~tmp; + else + /* Set it */ + sna->array[src->signum / 8] |= tmp; + } + } else if (src->family == AF_INET6) { + SCLogDebug("To IPv6"); + + void *user_data = NULL; + if (src->netmask == 128) + (void)SCRadixFindKeyIPV6ExactMatch((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv6src, + &user_data); + else + (void)SCRadixFindKeyIPV6Netblock((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv6src, + src->netmask, &user_data); + + if (user_data == NULL) { + /* Not found, look if there's a subnet of this range with bigger netmask */ + (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv6src, + &user_data); + + if (user_data == NULL) { + /* Not found, insert a new one */ + SigNumArray *sna = SigNumArrayNew(de_ctx, &de_ctx->io_ctx); + + /* Update the sig */ + uint8_t tmp = (uint8_t)(1 << (src->signum % 8)); + + if (src->negated > 0) + /* Unset it */ + sna->array[src->signum / 8] &= ~tmp; + else + /* Set it */ + sna->array[src->signum / 8] |= tmp; + + if (src->netmask == 128) + node = SCRadixAddKeyIPV6((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv6src, sna); + else + node = SCRadixAddKeyIPV6Netblock((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv6src, + sna, src->netmask); + if (node == NULL) + SCLogError("Error inserting in the src " + "ipv6 radix tree"); + } else { + /* Found, copy the sig num table, add this signum and insert */ + SigNumArray *sna = NULL; + sna = SigNumArrayCopy((SigNumArray *)user_data); + + /* Update the sig */ + uint8_t tmp = (uint8_t)(1 << (src->signum % 8)); + if (src->negated > 0) + /* Unset it */ + sna->array[src->signum / 8] &= ~tmp; + else + /* Set it */ + sna->array[src->signum / 8] |= tmp; + + if (src->netmask == 128) + node = SCRadixAddKeyIPV6((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv6src, sna); + else + node = SCRadixAddKeyIPV6Netblock((uint8_t *)&src->ip[0], + (de_ctx->io_ctx).tree_ipv6src, + sna, src->netmask); + if (node == NULL) + SCLogError("Error inserting in the src " + "ipv6 radix tree"); + } + } else { + /* it's already inserted. Update it */ + SigNumArray *sna = (SigNumArray *)user_data; + + /* Update the sig */ + uint8_t tmp = (uint8_t)(1 << (src->signum % 8)); + if (src->negated > 0) + /* Unset it */ + sna->array[src->signum / 8] &= ~tmp; + else + /* Set it */ + sna->array[src->signum / 8] |= tmp; + } + } + IPOnlyCIDRItem *tmpaux = src; + src = src->next; + SCFree(tmpaux); + } + + SCLogDebug("dsts:"); + + /* Prepare Dst radix trees */ + for (dst = (de_ctx->io_ctx).ip_dst; dst != NULL; ) { + if (dst->family == AF_INET) { + + SCLogDebug("To IPv4"); + SCLogDebug("Item has netmask %"PRIu8" negated: %s; IP: %s; signum:" + " %"PRIu32"", dst->netmask, (dst->negated)?"yes":"no", + inet_ntoa(*(struct in_addr*)&dst->ip[0]), dst->signum); + + void *user_data = NULL; + if (dst->netmask == 32) + (void) SCRadixFindKeyIPV4ExactMatch((uint8_t *) &dst->ip[0], + (de_ctx->io_ctx).tree_ipv4dst, + &user_data); + else + (void) SCRadixFindKeyIPV4Netblock((uint8_t *) &dst->ip[0], + (de_ctx->io_ctx).tree_ipv4dst, + dst->netmask, + &user_data); + + if (user_data == NULL) { + SCLogDebug("Exact match not found"); + + /** + * Not found, look if there's a subnet of this range + * with bigger netmask + */ + (void) SCRadixFindKeyIPV4BestMatch((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv4dst, + &user_data); + if (user_data == NULL) { + SCLogDebug("Best match not found"); + + /** Not found, insert a new one */ + SigNumArray *sna = SigNumArrayNew(de_ctx, &de_ctx->io_ctx); + + /** Update the sig */ + uint8_t tmp = (uint8_t)(1 << (dst->signum % 8)); + if (dst->negated > 0) + /** Unset it */ + sna->array[dst->signum / 8] &= ~tmp; + else + /** Set it */ + sna->array[dst->signum / 8] |= tmp; + + if (dst->netmask == 32) + node = SCRadixAddKeyIPV4((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv4dst, sna); + else + node = SCRadixAddKeyIPV4Netblock((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv4dst, + sna, dst->netmask); + + if (node == NULL) + SCLogError("Error inserting in the dst " + "ipv4 radix tree"); + } else { + SCLogDebug("Best match found"); + + /* Found, copy the sig num table, add this signum and insert */ + SigNumArray *sna = NULL; + sna = SigNumArrayCopy((SigNumArray *) user_data); + + /* Update the sig */ + uint8_t tmp = (uint8_t)(1 << (dst->signum % 8)); + if (dst->negated > 0) + /* Unset it */ + sna->array[dst->signum / 8] &= ~tmp; + else + /* Set it */ + sna->array[dst->signum / 8] |= tmp; + + if (dst->netmask == 32) + node = SCRadixAddKeyIPV4((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv4dst, sna); + else + node = SCRadixAddKeyIPV4Netblock((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv4dst, + sna, dst->netmask); + + if (node == NULL) + SCLogError("Error inserting in the dst " + "ipv4 radix tree"); + } + } else { + SCLogDebug("Exact match found"); + + /* it's already inserted. Update it */ + SigNumArray *sna = (SigNumArray *)user_data; + + /* Update the sig */ + uint8_t tmp = (uint8_t)(1 << (dst->signum % 8)); + if (dst->negated > 0) + /* Unset it */ + sna->array[dst->signum / 8] &= ~tmp; + else + /* Set it */ + sna->array[dst->signum / 8] |= tmp; + } + } else if (dst->family == AF_INET6) { + SCLogDebug("To IPv6"); + + void *user_data = NULL; + if (dst->netmask == 128) + (void) SCRadixFindKeyIPV6ExactMatch((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv6dst, + &user_data); + else + (void) SCRadixFindKeyIPV6Netblock((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv6dst, + dst->netmask, &user_data); + + if (user_data == NULL) { + /** Not found, look if there's a subnet of this range with + * bigger netmask + */ + (void) SCRadixFindKeyIPV6BestMatch((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv6dst, + &user_data); + + if (user_data == NULL) { + /* Not found, insert a new one */ + SigNumArray *sna = SigNumArrayNew(de_ctx, &de_ctx->io_ctx); + + /* Update the sig */ + uint8_t tmp = (uint8_t)(1 << (dst->signum % 8)); + if (dst->negated > 0) + /* Unset it */ + sna->array[dst->signum / 8] &= ~tmp; + else + /* Set it */ + sna->array[dst->signum / 8] |= tmp; + + if (dst->netmask == 128) + node = SCRadixAddKeyIPV6((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv6dst, sna); + else + node = SCRadixAddKeyIPV6Netblock((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv6dst, + sna, dst->netmask); + + if (node == NULL) + SCLogError("Error inserting in the dst " + "ipv6 radix tree"); + } else { + /* Found, copy the sig num table, add this signum and insert */ + SigNumArray *sna = NULL; + sna = SigNumArrayCopy((SigNumArray *)user_data); + + /* Update the sig */ + uint8_t tmp = (uint8_t)(1 << (dst->signum % 8)); + if (dst->negated > 0) + /* Unset it */ + sna->array[dst->signum / 8] &= ~tmp; + else + /* Set it */ + sna->array[dst->signum / 8] |= tmp; + + if (dst->netmask == 128) + node = SCRadixAddKeyIPV6((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv6dst, sna); + else + node = SCRadixAddKeyIPV6Netblock((uint8_t *)&dst->ip[0], + (de_ctx->io_ctx).tree_ipv6dst, + sna, dst->netmask); + + if (node == NULL) + SCLogError("Error inserting in the dst " + "ipv6 radix tree"); + } + } else { + /* it's already inserted. Update it */ + SigNumArray *sna = (SigNumArray *)user_data; + + /* Update the sig */ + uint8_t tmp = (uint8_t)(1 << (dst->signum % 8)); + if (dst->negated > 0) + /* Unset it */ + sna->array[dst->signum / 8] &= ~tmp; + else + /* Set it */ + sna->array[dst->signum / 8] |= tmp; + } + } + IPOnlyCIDRItem *tmpaux = dst; + dst = dst->next; + SCFree(tmpaux); + } + + /* print all the trees: for debugging it might print too much info + SCLogDebug("Radix tree src ipv4:"); + SCRadixPrintTree((de_ctx->io_ctx).tree_ipv4src); + SCLogDebug("Radix tree src ipv6:"); + SCRadixPrintTree((de_ctx->io_ctx).tree_ipv6src); + SCLogDebug("__________________"); + + SCLogDebug("Radix tree dst ipv4:"); + SCRadixPrintTree((de_ctx->io_ctx).tree_ipv4dst); + SCLogDebug("Radix tree dst ipv6:"); + SCRadixPrintTree((de_ctx->io_ctx).tree_ipv6dst); + SCLogDebug("__________________"); + */ +} + +/** + * \brief Add a signature to the lists of Addresses in CIDR format (sorted) + * this step is necessary to build the radix tree with a hierarchical + * relation between nodes + * \param de_ctx Pointer to the current detection engine context + * \param de_ctx Pointer to the current ip only detection engine contest + * \param s Pointer to the current signature + */ +void IPOnlyAddSignature(DetectEngineCtx *de_ctx, DetectEngineIPOnlyCtx *io_ctx, + Signature *s) +{ + if (!(s->type == SIG_TYPE_IPONLY)) + return; + + SigIntId mapped_signum = IPOnlyTrackSigNum(io_ctx, s->num); + SCLogDebug("Adding IPs from rule: %" PRIu32 " (%s) as %" PRIu32 " mapped to %" PRIu32 "\n", + s->id, s->msg, s->num, mapped_signum); + /* Set the internal signum to the list before merging */ + IPOnlyCIDRListSetSigNum(s->cidr_src, mapped_signum); + + IPOnlyCIDRListSetSigNum(s->cidr_dst, mapped_signum); + + /** + * ipv4 and ipv6 are mixed, but later we will separate them into + * different trees + */ + io_ctx->ip_src = IPOnlyCIDRItemInsert(io_ctx->ip_src, s->cidr_src); + io_ctx->ip_dst = IPOnlyCIDRItemInsert(io_ctx->ip_dst, s->cidr_dst); + + if (mapped_signum > io_ctx->max_idx) + io_ctx->max_idx = mapped_signum; + + /** no longer ref to this, it's in the table now */ + s->cidr_src = NULL; + s->cidr_dst = NULL; +} + +#ifdef UNITTESTS +/** + * \test check that we set a Signature as IPOnly because it has no rule + * option appending a SigMatch and no port is fixed + */ + +static int IPOnlyTestSig01(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF(de_ctx == NULL); + de_ctx->flags |= DE_QUIET; + + Signature *s = SigInit(de_ctx,"alert tcp any any -> any any (sid:400001; rev:1;)"); + FAIL_IF(s == NULL); + + FAIL_IF(SignatureIsIPOnly(de_ctx, s) == 0); + SigFree(de_ctx, s); + DetectEngineCtxFree(de_ctx); + PASS; +} + +/** + * \test check that we don't set a Signature as IPOnly because it has no rule + * option appending a SigMatch but a port is fixed + */ + +static int IPOnlyTestSig02 (void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF(de_ctx == NULL); + de_ctx->flags |= DE_QUIET; + + Signature *s = SigInit(de_ctx,"alert tcp any any -> any 80 (sid:400001; rev:1;)"); + FAIL_IF(s == NULL); + + FAIL_IF(SignatureIsIPOnly(de_ctx, s) == 0); + SigFree(de_ctx, s); + DetectEngineCtxFree(de_ctx); + PASS; +} + +/** + * \test check that we set don't set a Signature as IPOnly + * because it has rule options appending a SigMatch like content, and pcre + */ + +static int IPOnlyTestSig03 (void) +{ + int result = 1; + DetectEngineCtx *de_ctx; + Signature *s=NULL; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + de_ctx->flags |= DE_QUIET; + + /* combination of pcre and content */ + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (pcre and content) \"; content:\"php\"; pcre:\"/require(_once)?/i\"; classtype:misc-activity; sid:400001; rev:1;)"); + if (s == NULL) { + goto end; + } + if(SignatureIsIPOnly(de_ctx, s)) + { + printf("got a IPOnly signature (content): "); + result=0; + } + SigFree(de_ctx, s); + + /* content */ + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (content) \"; content:\"match something\"; classtype:misc-activity; sid:400001; rev:1;)"); + if (s == NULL) { + goto end; + } + if(SignatureIsIPOnly(de_ctx, s)) + { + printf("got a IPOnly signature (content): "); + result=0; + } + SigFree(de_ctx, s); + + /* uricontent */ + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (uricontent) \"; uricontent:\"match something\"; classtype:misc-activity; sid:400001; rev:1;)"); + if (s == NULL) { + goto end; + } + if(SignatureIsIPOnly(de_ctx, s)) + { + printf("got a IPOnly signature (uricontent): "); + result=0; + } + SigFree(de_ctx, s); + + /* pcre */ + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (pcre) \"; pcre:\"/e?idps rule[sz]/i\"; classtype:misc-activity; sid:400001; rev:1;)"); + if (s == NULL) { + goto end; + } + if(SignatureIsIPOnly(de_ctx, s)) + { + printf("got a IPOnly signature (pcre): "); + result=0; + } + SigFree(de_ctx, s); + + /* flow */ + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (flow) \"; flow:to_server; classtype:misc-activity; sid:400001; rev:1;)"); + if (s == NULL) { + goto end; + } + if(SignatureIsIPOnly(de_ctx, s)) + { + printf("got a IPOnly signature (flow): "); + result=0; + } + SigFree(de_ctx, s); + + /* dsize */ + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (dsize) \"; dsize:100; classtype:misc-activity; sid:400001; rev:1;)"); + if (s == NULL) { + goto end; + } + if(SignatureIsIPOnly(de_ctx, s)) + { + printf("got a IPOnly signature (dsize): "); + result=0; + } + SigFree(de_ctx, s); + + /* flowbits */ + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (flowbits) \"; flowbits:unset; classtype:misc-activity; sid:400001; rev:1;)"); + if (s == NULL) { + goto end; + } + if(SignatureIsIPOnly(de_ctx, s)) + { + printf("got a IPOnly signature (flowbits): "); + result=0; + } + SigFree(de_ctx, s); + + /* flowvar */ + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (flowvar) \"; pcre:\"/(?<flow_var>.*)/i\"; flowvar:var,\"str\"; classtype:misc-activity; sid:400001; rev:1;)"); + if (s == NULL) { + goto end; + } + if(SignatureIsIPOnly(de_ctx, s)) + { + printf("got a IPOnly signature (flowvar): "); + result=0; + } + SigFree(de_ctx, s); + + /* pktvar */ + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"SigTest40-03 sig is not IPOnly (pktvar) \"; pcre:\"/(?<pkt_var>.*)/i\"; pktvar:var,\"str\"; classtype:misc-activity; sid:400001; rev:1;)"); + if (s == NULL) { + goto end; + } + if(SignatureIsIPOnly(de_ctx, s)) + { + printf("got a IPOnly signature (pktvar): "); + result=0; + } + SigFree(de_ctx, s); + +end: + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test + */ +static int IPOnlyTestSig04 (void) +{ + int result = 1; + + IPOnlyCIDRItem *head = NULL; + IPOnlyCIDRItem *new; + + new = IPOnlyCIDRItemNew(); + new->netmask= 10; + + head = IPOnlyCIDRItemInsert(head, new); + + new = IPOnlyCIDRItemNew(); + new->netmask= 11; + + head = IPOnlyCIDRItemInsert(head, new); + + new = IPOnlyCIDRItemNew(); + new->netmask= 9; + + head = IPOnlyCIDRItemInsert(head, new); + + new = IPOnlyCIDRItemNew(); + new->netmask= 10; + + head = IPOnlyCIDRItemInsert(head, new); + + new = IPOnlyCIDRItemNew(); + new->netmask= 10; + + head = IPOnlyCIDRItemInsert(head, new); + + IPOnlyCIDRListPrint(head); + new = head; + if (new->netmask != 9) { + result = 0; + goto end; + } + new = new->next; + if (new->netmask != 10) { + result = 0; + goto end; + } + new = new->next; + if (new->netmask != 10) { + result = 0; + goto end; + } + new = new->next; + if (new->netmask != 10) { + result = 0; + goto end; + } + new = new->next; + if (new->netmask != 11) { + result = 0; + goto end; + } + +end: + IPOnlyCIDRListFree(head); + return result; +} + +/** + * \test Test a set of ip only signatures making use a lot of + * addresses for src and dst (all should match) + */ +static int IPOnlyTestSig05(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 1; + uint8_t numsigs = 7; + + Packet *p[1]; + + p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); + + const char *sigs[numsigs]; + sigs[0]= "alert tcp 192.168.1.5 any -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)"; + sigs[1]= "alert tcp any any -> 192.168.1.1 any (msg:\"Testing dst ip (sid 2)\"; sid:2;)"; + sigs[2]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)"; + sigs[3]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)"; + sigs[4]= "alert tcp 192.168.1.0/24 any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)"; + sigs[5]= "alert tcp any any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)"; + sigs[6]= "alert tcp 192.168.1.0/24 any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 7)\"; content:\"Hi all\";sid:7;)"; + + /* Sid numbers (we could extract them from the sig) */ + uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7}; + uint32_t results[7] = { 1, 1, 1, 1, 1, 1, 1}; + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + return result; +} + +/** + * \test Test a set of ip only signatures making use a lot of + * addresses for src and dst (none should match) + */ +static int IPOnlyTestSig06(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 1; + uint8_t numsigs = 7; + + Packet *p[1]; + + p[0] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "80.58.0.33", "195.235.113.3"); + + const char *sigs[numsigs]; + sigs[0]= "alert tcp 192.168.1.5 any -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)"; + sigs[1]= "alert tcp any any -> 192.168.1.1 any (msg:\"Testing dst ip (sid 2)\"; sid:2;)"; + sigs[2]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)"; + sigs[3]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)"; + sigs[4]= "alert tcp 192.168.1.0/24 any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)"; + sigs[5]= "alert tcp any any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)"; + sigs[6]= "alert tcp 192.168.1.0/24 any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 7)\"; content:\"Hi all\";sid:7;)"; + + /* Sid numbers (we could extract them from the sig) */ + uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7}; + uint32_t results[7] = { 0, 0, 0, 0, 0, 0, 0}; + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + return result; +} + +/* \todo fix it. We have disabled this unittest because 599 exposes 608, + * which is why these unittests fail. When we fix 608, we need to renable + * these sigs */ +#if 0 +/** + * \test Test a set of ip only signatures making use a lot of + * addresses for src and dst (all should match) + */ +static int IPOnlyTestSig07(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 1; + uint8_t numsigs = 7; + + Packet *p[1]; + + p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); + + char *sigs[numsigs]; + sigs[0]= "alert tcp 192.168.1.5 any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 1)\"; sid:1;)"; + sigs[1]= "alert tcp [192.168.1.2,192.168.1.5,192.168.1.4] any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 2)\"; sid:2;)"; + sigs[2]= "alert tcp [192.168.1.0/24,!192.168.1.1] any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)"; + sigs[3]= "alert tcp [192.0.0.0/8,!192.168.0.0/16,192.168.1.0/24,!192.168.1.1] any -> [192.168.1.0/24,!192.168.1.5] any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)"; + sigs[4]= "alert tcp any any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)"; + sigs[5]= "alert tcp any any -> [192.168.0.0/16,!192.168.1.0/24,192.168.1.1] any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)"; + sigs[6]= "alert tcp [78.129.202.0/24,192.168.1.5,78.129.205.64,78.129.214.103,78.129.223.19,78.129.233.17,78.137.168.33,78.140.132.11,78.140.133.15,78.140.138.105,78.140.139.105,78.140.141.107,78.140.141.114,78.140.143.103,78.140.143.13,78.140.145.144,78.140.170.164,78.140.23.18,78.143.16.7,78.143.46.124,78.157.129.71] any -> 192.168.1.1 any (msg:\"ET RBN Known Russian Business Network IP TCP - BLOCKING (246)\"; sid:7;)"; /* real sid:"2407490" */ + + /* Sid numbers (we could extract them from the sig) */ + uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7}; + uint32_t results[7] = { 1, 1, 1, 1, 1, 1, 1}; + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + return result; +} +#endif + +/** + * \test Test a set of ip only signatures making use a lot of + * addresses for src and dst (none should match) + */ +static int IPOnlyTestSig08(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 1; + uint8_t numsigs = 7; + + Packet *p[1]; + + p[0] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP,"192.168.1.1","192.168.1.5"); + + const char *sigs[numsigs]; + sigs[0]= "alert tcp 192.168.1.5 any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 1)\"; sid:1;)"; + sigs[1]= "alert tcp [192.168.1.2,192.168.1.5,192.168.1.4] any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 2)\"; sid:2;)"; + sigs[2]= "alert tcp [192.168.1.0/24,!192.168.1.1] any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)"; + sigs[3]= "alert tcp [192.0.0.0/8,!192.168.0.0/16,192.168.1.0/24,!192.168.1.1] any -> [192.168.1.0/24,!192.168.1.5] any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)"; + sigs[4]= "alert tcp any any -> !192.168.1.5 any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)"; + sigs[5]= "alert tcp any any -> [192.168.0.0/16,!192.168.1.0/24,192.168.1.1] any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)"; + sigs[6]= "alert tcp [78.129.202.0/24,192.168.1.5,78.129.205.64,78.129.214.103,78.129.223.19,78.129.233.17,78.137.168.33,78.140.132.11,78.140.133.15,78.140.138.105,78.140.139.105,78.140.141.107,78.140.141.114,78.140.143.103,78.140.143.13,78.140.145.144,78.140.170.164,78.140.23.18,78.143.16.7,78.143.46.124,78.157.129.71] any -> 192.168.1.1 any (msg:\"ET RBN Known Russian Business Network IP TCP - BLOCKING (246)\"; sid:7;)"; /* real sid:"2407490" */ + + /* Sid numbers (we could extract them from the sig) */ + uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7}; + uint32_t results[7] = { 0, 0, 0, 0, 0, 0, 0}; + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + return result; +} + +/** + * \test Test a set of ip only signatures making use a lot of + * addresses for src and dst (all should match) + */ +static int IPOnlyTestSig09(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 1; + uint8_t numsigs = 7; + + Packet *p[1]; + + p[0] = UTHBuildPacketIPV6SrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565", "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562"); + + const char *sigs[numsigs]; + sigs[0]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)"; + sigs[1]= "alert tcp any any -> 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562 any (msg:\"Testing dst ip (sid 2)\"; sid:2;)"; + sigs[2]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)"; + sigs[3]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> 3FFE:FFFF:7654:FEDA:1245:BA98:3210:0/96 any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)"; + sigs[4]= "alert tcp 3FFE:FFFF:7654:FEDA:0:0:0:0/64 any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)"; + sigs[5]= "alert tcp any any -> 3FFE:FFFF:7654:FEDA:0:0:0:0/64 any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)"; + sigs[6]= "alert tcp 3FFE:FFFF:7654:FEDA:0:0:0:0/64 any -> 3FFE:FFFF:7654:FEDA:0:0:0:0/64 any (msg:\"Testing src/dst ip (sid 7)\"; content:\"Hi all\";sid:7;)"; + + /* Sid numbers (we could extract them from the sig) */ + uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7}; + uint32_t results[7] = { 1, 1, 1, 1, 1, 1, 1}; + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + return result; +} + +/** + * \test Test a set of ip only signatures making use a lot of + * addresses for src and dst (none should match) + */ +static int IPOnlyTestSig10(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 1; + uint8_t numsigs = 7; + + Packet *p[1]; + + p[0] = UTHBuildPacketIPV6SrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562", "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565"); + + const char *sigs[numsigs]; + sigs[0]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)"; + sigs[1]= "alert tcp any any -> 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562 any (msg:\"Testing dst ip (sid 2)\"; sid:2;)"; + sigs[2]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562 any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)"; + sigs[3]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565 any -> !3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562/96 any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)"; + sigs[4]= "alert tcp !3FFE:FFFF:7654:FEDA:0:0:0:0/64 any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)"; + sigs[5]= "alert tcp any any -> !3FFE:FFFF:7654:FEDA:0:0:0:0/64 any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)"; + sigs[6]= "alert tcp 3FFE:FFFF:7654:FEDA:0:0:0:0/64 any -> 3FFE:FFFF:7654:FEDB:0:0:0:0/64 any (msg:\"Testing src/dst ip (sid 7)\"; content:\"Hi all\";sid:7;)"; + + /* Sid numbers (we could extract them from the sig) */ + uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7}; + uint32_t results[7] = { 0, 0, 0, 0, 0, 0, 0}; + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + return result; +} + +/* \todo fix it. We have disabled this unittest because 599 exposes 608, + * which is why these unittests fail. When we fix 608, we need to renable + * these sigs */ +#if 0 +/** + * \test Test a set of ip only signatures making use a lot of + * addresses for src and dst (all should match) with ipv4 and ipv6 mixed + */ +static int IPOnlyTestSig11(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 2; + uint8_t numsigs = 7; + + Packet *p[2]; + + p[0] = UTHBuildPacketIPV6SrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565", "3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562"); + p[1] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP,"192.168.1.1","192.168.1.5"); + + char *sigs[numsigs]; + sigs[0]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.1 any -> 3FFE:FFFF:7654:FEDA:0:0:0:0/64,192.168.1.5 any (msg:\"Testing src/dst ip (sid 1)\"; sid:1;)"; + sigs[1]= "alert tcp [192.168.1.1,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.4,192.168.1.5,!192.168.1.0/24] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.0/24] any (msg:\"Testing src/dst ip (sid 2)\"; sid:2;)"; + sigs[2]= "alert tcp [3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.1] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.5] any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)"; + sigs[3]= "alert tcp [3FFE:FFFF:0:0:0:0:0:0/32,!3FFE:FFFF:7654:FEDA:0:0:0:0/64,3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.1] any -> [3FFE:FFFF:7654:FEDA:0:0:0:0/64,192.168.1.0/24,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565] any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)"; + sigs[4]= "alert tcp any any -> any any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)"; + sigs[5]= "alert tcp any any -> [3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:0:0:0:0/64,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.5] any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)"; + sigs[6]= "alert tcp [78.129.202.0/24,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.1,78.129.205.64,78.129.214.103,78.129.223.19,78.129.233.17,78.137.168.33,78.140.132.11,78.140.133.15,78.140.138.105,78.140.139.105,78.140.141.107,78.140.141.114,78.140.143.103,78.140.143.13,78.140.145.144,78.140.170.164,78.140.23.18,78.143.16.7,78.143.46.124,78.157.129.71] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.0.0.0/8] any (msg:\"ET RBN Known Russian Business Network IP TCP - BLOCKING (246)\"; sid:7;)"; /* real sid:"2407490" */ + + /* Sid numbers (we could extract them from the sig) */ + uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7}; + uint32_t results[2][7] = {{ 1, 1, 1, 1, 1, 1, 1}, { 1, 1, 1, 1, 1, 1, 1}}; + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + return result; +} +#endif + +/** + * \test Test a set of ip only signatures making use a lot of + * addresses for src and dst (none should match) with ipv4 and ipv6 mixed + */ +static int IPOnlyTestSig12(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 2; + uint8_t numsigs = 7; + + Packet *p[2]; + + p[0] = UTHBuildPacketIPV6SrcDst((uint8_t *)buf, buflen, IPPROTO_TCP,"3FBE:FFFF:7654:FEDA:1245:BA98:3210:4562","3FBE:FFFF:7654:FEDA:1245:BA98:3210:4565"); + p[1] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP,"195.85.1.1","80.198.1.5"); + + const char *sigs[numsigs]; + sigs[0]= "alert tcp 3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.1 any -> 3FFE:FFFF:7654:FEDA:0:0:0:0/64,192.168.1.5 any (msg:\"Testing src/dst ip (sid 1)\"; sid:1;)"; + sigs[1]= "alert tcp [192.168.1.1,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.4,192.168.1.5,!192.168.1.0/24] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.0/24] any (msg:\"Testing src/dst ip (sid 2)\"; sid:2;)"; + sigs[2]= "alert tcp [3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.1] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.5] any (msg:\"Testing src/dst ip (sid 3)\"; sid:3;)"; + sigs[3]= "alert tcp [3FFE:FFFF:0:0:0:0:0:0/32,!3FFE:FFFF:7654:FEDA:0:0:0:0/64,3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.1] any -> [3FFE:FFFF:7654:FEDA:0:0:0:0/64,192.168.1.0/24,!3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565] any (msg:\"Testing src/dst ip (sid 4)\"; sid:4;)"; + sigs[4]= "alert tcp any any -> [!3FBE:FFFF:7654:FEDA:1245:BA98:3210:4565,!80.198.1.5] any (msg:\"Testing src/dst ip (sid 5)\"; sid:5;)"; + sigs[5]= "alert tcp any any -> [3FFE:FFFF:7654:FEDA:0:0:0:0/64,!3FFE:FFFF:7654:FEDA:0:0:0:0/64,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.168.1.5] any (msg:\"Testing src/dst ip (sid 6)\"; sid:6;)"; + sigs[6]= "alert tcp [78.129.202.0/24,3FFE:FFFF:7654:FEDA:1245:BA98:3210:4565,192.168.1.1,78.129.205.64,78.129.214.103,78.129.223.19,78.129.233.17,78.137.168.33,78.140.132.11,78.140.133.15,78.140.138.105,78.140.139.105,78.140.141.107,78.140.141.114,78.140.143.103,78.140.143.13,78.140.145.144,78.140.170.164,78.140.23.18,78.143.16.7,78.143.46.124,78.157.129.71] any -> [3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562,192.0.0.0/8] any (msg:\"ET RBN Known Russian Business Network IP TCP - BLOCKING (246)\"; sid:7;)"; /* real sid:"2407490" */ + + /* Sid numbers (we could extract them from the sig) */ + uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7}; + uint32_t results[2][7] = {{ 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}}; + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + return result; +} + +static int IPOnlyTestSig13(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF(de_ctx == NULL); + de_ctx->flags |= DE_QUIET; + + Signature *s = SigInit(de_ctx, + "alert tcp any any -> any any (msg:\"Test flowbits ip only\"; " + "flowbits:set,myflow1; sid:1; rev:1;)"); + FAIL_IF(s == NULL); + + FAIL_IF(SignatureIsIPOnly(de_ctx, s) == 0); + SigFree(de_ctx, s); + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int IPOnlyTestSig14(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF(de_ctx == NULL); + de_ctx->flags |= DE_QUIET; + + Signature *s = SigInit(de_ctx, + "alert tcp any any -> any any (msg:\"Test flowbits ip only\"; " + "flowbits:set,myflow1; flowbits:isset,myflow2; sid:1; rev:1;)"); + FAIL_IF(s == NULL); + + FAIL_IF(SignatureIsIPOnly(de_ctx, s) == 1); + SigFree(de_ctx, s); + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int IPOnlyTestSig15(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 1; + uint8_t numsigs = 7; + + Packet *p[1]; + Flow f; + GenericVar flowvar; + memset(&f, 0, sizeof(Flow)); + memset(&flowvar, 0, sizeof(GenericVar)); + FLOW_INITIALIZE(&f); + + p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); + + p[0]->flow = &f; + p[0]->flow->flowvar = &flowvar; + p[0]->flags |= PKT_HAS_FLOW; + p[0]->flowflags |= FLOW_PKT_TOSERVER; + + const char *sigs[numsigs]; + sigs[0]= "alert tcp 192.168.1.5 any -> any any (msg:\"Testing src ip (sid 1)\"; " + "flowbits:set,one; sid:1;)"; + sigs[1]= "alert tcp any any -> 192.168.1.1 any (msg:\"Testing dst ip (sid 2)\"; " + "flowbits:set,two; sid:2;)"; + sigs[2]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 3)\"; " + "flowbits:set,three; sid:3;)"; + sigs[3]= "alert tcp 192.168.1.5 any -> 192.168.1.1 any (msg:\"Testing src/dst ip (sid 4)\"; " + "flowbits:set,four; sid:4;)"; + sigs[4]= "alert tcp 192.168.1.0/24 any -> any any (msg:\"Testing src/dst ip (sid 5)\"; " + "flowbits:set,five; sid:5;)"; + sigs[5]= "alert tcp any any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 6)\"; " + "flowbits:set,six; sid:6;)"; + sigs[6]= "alert tcp 192.168.1.0/24 any -> 192.168.0.0/16 any (msg:\"Testing src/dst ip (sid 7)\"; " + "flowbits:set,seven; content:\"Hi all\"; sid:7;)"; + + /* Sid numbers (we could extract them from the sig) */ + uint32_t sid[7] = { 1, 2, 3, 4, 5, 6, 7}; + uint32_t results[7] = { 1, 1, 1, 1, 1, 1, 1}; + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + FLOW_DESTROY(&f); + return result; +} + +/** + * \brief Unittest to show #599. We fail to match if we have negated addresses. + */ +static int IPOnlyTestSig16(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 1; + uint8_t numsigs = 2; + + Packet *p[1]; + + p[0] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "100.100.0.0", "50.0.0.0"); + + const char *sigs[numsigs]; + sigs[0]= "alert tcp !100.100.0.1 any -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)"; + sigs[1]= "alert tcp any any -> !50.0.0.1 any (msg:\"Testing dst ip (sid 2)\"; sid:2;)"; + + /* Sid numbers (we could extract them from the sig) */ + uint32_t sid[2] = { 1, 2}; + uint32_t results[2] = { 1, 1}; + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + return result; +} + +/** + * \brief Unittest to show #611. Ports on portless protocols. + */ +static int IPOnlyTestSig17(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 1; + uint8_t numsigs = 2; + + Packet *p[1]; + + p[0] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_ICMP, "100.100.0.0", "50.0.0.0"); + + const char *sigs[numsigs]; + sigs[0]= "alert ip 100.100.0.0 80 -> any any (msg:\"Testing src ip (sid 1)\"; sid:1;)"; + sigs[1]= "alert ip any any -> 50.0.0.0 123 (msg:\"Testing dst ip (sid 2)\"; sid:2;)"; + + uint32_t sid[2] = { 1, 2}; + uint32_t results[2] = { 0, 0}; /* neither should match */ + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + return result; +} + +/** + * \brief Unittest to show #3568 -- IP address range handling + */ +static int IPOnlyTestSig18(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + + uint8_t numpkts = 4; + uint8_t numsigs = 4; + + Packet *p[4]; + + p[0] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "10.10.10.1", "50.0.0.1"); + p[1] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "220.10.10.1", "5.0.0.1"); + p[2] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "0.0.0.1", "50.0.0.1"); + p[3] = UTHBuildPacketSrcDst((uint8_t *)buf, buflen, IPPROTO_TCP, "255.255.255.254", "5.0.0.1"); + + const char *sigs[numsigs]; + // really many IP addresses + sigs[0]= "alert ip 1.2.3.4-219.6.7.8 any -> any any (sid:1;)"; + sigs[1]= "alert ip 51.2.3.4-253.1.2.3 any -> any any (sid:2;)"; + sigs[2]= "alert ip 0.0.0.0-50.0.0.2 any -> any any (sid:3;)"; + sigs[3]= "alert ip 50.0.0.0-255.255.255.255 any -> any any (sid:4;)"; + + uint32_t sid[4] = { 1, 2, 3, 4, }; + uint32_t results[4][4] = { + { 1, 0, 1, 0, }, { 0, 1, 0, 1}, { 0, 0, 1, 0 }, { 0, 0, 0, 1}}; + + result = UTHGenericTest(p, numpkts, sigs, sid, (uint32_t *) results, numsigs); + + UTHFreePackets(p, numpkts); + + FAIL_IF(result != 1); + + PASS; +} + +/** \test build IP-only tree */ +static int IPOnlyTestBug5066v1(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF(de_ctx == NULL); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig( + de_ctx, "alert ip [1.2.3.4/24,1.2.3.64/27] any -> any any (sid:1;)"); + FAIL_IF_NULL(s); + s = DetectEngineAppendSig(de_ctx, "alert ip [1.2.3.4/24] any -> any any (sid:2;)"); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int IPOnlyTestBug5066v2(void) +{ + IPOnlyCIDRItem *x = IPOnlyCIDRItemNew(); + FAIL_IF_NULL(x); + + FAIL_IF(IPOnlyCIDRItemParseSingle(&x, "1.2.3.4/24") != 0); + + char ip[16]; + PrintInet(AF_INET, (const void *)&x->ip[0], ip, sizeof(ip)); + SCLogDebug("ip %s netmask %d", ip, x->netmask); + + FAIL_IF_NOT(strcmp(ip, "1.2.3.0") == 0); + FAIL_IF_NOT(x->netmask == 24); + + IPOnlyCIDRListFree(x); + PASS; +} + +static int IPOnlyTestBug5066v3(void) +{ + IPOnlyCIDRItem *x = IPOnlyCIDRItemNew(); + FAIL_IF_NULL(x); + + FAIL_IF(IPOnlyCIDRItemParseSingle(&x, "1.2.3.64/26") != 0); + + char ip[16]; + PrintInet(AF_INET, (const void *)&x->ip[0], ip, sizeof(ip)); + SCLogDebug("ip %s netmask %d", ip, x->netmask); + + FAIL_IF_NOT(strcmp(ip, "1.2.3.64") == 0); + FAIL_IF_NOT(x->netmask == 26); + + IPOnlyCIDRListFree(x); + PASS; +} + +static int IPOnlyTestBug5066v4(void) +{ + IPOnlyCIDRItem *x = IPOnlyCIDRItemNew(); + FAIL_IF_NULL(x); + + FAIL_IF(IPOnlyCIDRItemParseSingle(&x, "2000::1:1/122") != 0); + + char ip[64]; + PrintInet(AF_INET6, (const void *)&x->ip, ip, sizeof(ip)); + SCLogDebug("ip %s netmask %d", ip, x->netmask); + + FAIL_IF_NOT(strcmp(ip, "2000:0000:0000:0000:0000:0000:0001:0000") == 0); + FAIL_IF_NOT(x->netmask == 122); + + IPOnlyCIDRListFree(x); + PASS; +} + +static int IPOnlyTestBug5066v5(void) +{ + IPOnlyCIDRItem *x = IPOnlyCIDRItemNew(); + FAIL_IF_NULL(x); + + FAIL_IF(IPOnlyCIDRItemParseSingle(&x, "2000::1:40/122") != 0); + + char ip[64]; + PrintInet(AF_INET6, (const void *)&x->ip, ip, sizeof(ip)); + SCLogDebug("ip %s netmask %d", ip, x->netmask); + + FAIL_IF_NOT(strcmp(ip, "2000:0000:0000:0000:0000:0000:0001:0040") == 0); + FAIL_IF_NOT(x->netmask == 122); + + IPOnlyCIDRListFree(x); + PASS; +} + +static int IPOnlyTestBug5168v1(void) +{ + IPOnlyCIDRItem *x = IPOnlyCIDRItemNew(); + FAIL_IF_NULL(x); + + FAIL_IF(IPOnlyCIDRItemParseSingle(&x, "1.2.3.64/0.0.0.0") != 0); + + char ip[16]; + PrintInet(AF_INET, (const void *)&x->ip[0], ip, sizeof(ip)); + SCLogDebug("ip %s netmask %d", ip, x->netmask); + + FAIL_IF_NOT(strcmp(ip, "0.0.0.0") == 0); + FAIL_IF_NOT(x->netmask == 0); + + IPOnlyCIDRListFree(x); + PASS; +} + +static int IPOnlyTestBug5168v2(void) +{ + IPOnlyCIDRItem *x = IPOnlyCIDRItemNew(); + FAIL_IF_NULL(x); + FAIL_IF(IPOnlyCIDRItemParseSingle(&x, "0.0.0.5/0.0.0.5") != -1); + IPOnlyCIDRListFree(x); + PASS; +} + +#endif /* UNITTESTS */ + +void IPOnlyRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("IPOnlyTestSig01", IPOnlyTestSig01); + UtRegisterTest("IPOnlyTestSig02", IPOnlyTestSig02); + UtRegisterTest("IPOnlyTestSig03", IPOnlyTestSig03); + UtRegisterTest("IPOnlyTestSig04", IPOnlyTestSig04); + + UtRegisterTest("IPOnlyTestSig05", IPOnlyTestSig05); + UtRegisterTest("IPOnlyTestSig06", IPOnlyTestSig06); +/* \todo fix it. We have disabled this unittest because 599 exposes 608, + * which is why these unittests fail. When we fix 608, we need to renable + * these sigs */ +#if 0 + UtRegisterTest("IPOnlyTestSig07", IPOnlyTestSig07, 1); +#endif + UtRegisterTest("IPOnlyTestSig08", IPOnlyTestSig08); + + UtRegisterTest("IPOnlyTestSig09", IPOnlyTestSig09); + UtRegisterTest("IPOnlyTestSig10", IPOnlyTestSig10); +/* \todo fix it. We have disabled this unittest because 599 exposes 608, + * which is why these unittests fail. When we fix 608, we need to renable + * these sigs */ +#if 0 + UtRegisterTest("IPOnlyTestSig11", IPOnlyTestSig11, 1); +#endif + UtRegisterTest("IPOnlyTestSig12", IPOnlyTestSig12); + UtRegisterTest("IPOnlyTestSig13", IPOnlyTestSig13); + UtRegisterTest("IPOnlyTestSig14", IPOnlyTestSig14); + UtRegisterTest("IPOnlyTestSig15", IPOnlyTestSig15); + UtRegisterTest("IPOnlyTestSig16", IPOnlyTestSig16); + + UtRegisterTest("IPOnlyTestSig17", IPOnlyTestSig17); + UtRegisterTest("IPOnlyTestSig18", IPOnlyTestSig18); + + UtRegisterTest("IPOnlyTestBug5066v1", IPOnlyTestBug5066v1); + UtRegisterTest("IPOnlyTestBug5066v2", IPOnlyTestBug5066v2); + UtRegisterTest("IPOnlyTestBug5066v3", IPOnlyTestBug5066v3); + UtRegisterTest("IPOnlyTestBug5066v4", IPOnlyTestBug5066v4); + UtRegisterTest("IPOnlyTestBug5066v5", IPOnlyTestBug5066v5); + + UtRegisterTest("IPOnlyTestBug5168v1", IPOnlyTestBug5168v1); + UtRegisterTest("IPOnlyTestBug5168v2", IPOnlyTestBug5168v2); +#endif + + return; +} + |