|
/* Copyright (C) 2007-2021 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.
|
|
*/
|
|
|
|
/*
|
|
* Isolated reproducer for Suricata tag suricata-8.0.7.
|
|
* The option table, parser, matcher and option flags below are copied from
|
|
* src/detect-ipopts.c, src/decode-ipv4.h and src/suricata-common.h.
|
|
* Other engine types/macros are minimal stubs. This does not run Suricata's
|
|
* packet decoder or complete detection pipeline.
|
|
* Build: cc -std=c11 -O2 suricata-ipopts-any-reproducer.c -o ipopts-repro
|
|
* Run: ./ipopts-repro
|
|
* Source: https://github.com/OISF/suricata/tree/suricata-8.0.7
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
typedef void DetectEngineThreadCtx;
|
|
typedef void Signature;
|
|
typedef void SigMatchCtx;
|
|
typedef struct { uint16_t ipopt; } DetectIpOptsData;
|
|
typedef struct { int ipv4; struct { struct { struct { uint16_t opts_set; } ip4; } vars; } l3; } Packet;
|
|
#define DEBUG_VALIDATE_BUG_ON(x) assert(!(x))
|
|
#define PKT_IS_PSEUDOPKT(p) 0
|
|
#define PacketIsIPv4(p) ((p)->ipv4)
|
|
#define SCMalloc malloc
|
|
#define unlikely(x) (x)
|
|
#define SCLogError(...) ((void)0)
|
|
|
|
#define BIT_U16(n) ((uint16_t)(1 << (n)))
|
|
#define IPV4_OPT_FLAG_EOL BIT_U16(1)
|
|
#define IPV4_OPT_FLAG_NOP BIT_U16(2)
|
|
#define IPV4_OPT_FLAG_RR BIT_U16(3)
|
|
#define IPV4_OPT_FLAG_TS BIT_U16(4)
|
|
#define IPV4_OPT_FLAG_QS BIT_U16(5)
|
|
#define IPV4_OPT_FLAG_LSRR BIT_U16(6)
|
|
#define IPV4_OPT_FLAG_SSRR BIT_U16(7)
|
|
#define IPV4_OPT_FLAG_SID BIT_U16(8)
|
|
#define IPV4_OPT_FLAG_SEC BIT_U16(9)
|
|
#define IPV4_OPT_FLAG_CIPSO BIT_U16(10)
|
|
#define IPV4_OPT_FLAG_RTRALT BIT_U16(11)
|
|
#define IPV4_OPT_FLAG_ESEC BIT_U16(12)
|
|
struct DetectIpOpts_ {
|
|
const char *ipopt_name; /**< ip option name */
|
|
uint16_t code; /**< ip option flag value */
|
|
} ipopts[] = {
|
|
{
|
|
"ts",
|
|
IPV4_OPT_FLAG_TS,
|
|
},
|
|
{
|
|
"rr",
|
|
IPV4_OPT_FLAG_RR,
|
|
},
|
|
{
|
|
"qs",
|
|
IPV4_OPT_FLAG_QS,
|
|
},
|
|
{
|
|
"sec",
|
|
IPV4_OPT_FLAG_SEC,
|
|
},
|
|
{
|
|
"lsrr",
|
|
IPV4_OPT_FLAG_LSRR,
|
|
},
|
|
{
|
|
"esec",
|
|
IPV4_OPT_FLAG_ESEC,
|
|
},
|
|
{
|
|
"cipso",
|
|
IPV4_OPT_FLAG_CIPSO,
|
|
},
|
|
{
|
|
"satid",
|
|
IPV4_OPT_FLAG_SID,
|
|
},
|
|
{
|
|
"ssrr",
|
|
IPV4_OPT_FLAG_SSRR,
|
|
},
|
|
{
|
|
"rtralt",
|
|
IPV4_OPT_FLAG_RTRALT,
|
|
},
|
|
{
|
|
"eol",
|
|
IPV4_OPT_FLAG_EOL,
|
|
},
|
|
{
|
|
"nop",
|
|
IPV4_OPT_FLAG_NOP,
|
|
},
|
|
{
|
|
"any",
|
|
0xffff,
|
|
},
|
|
{ NULL, 0 },
|
|
};
|
|
static int DetectIpOptsMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
|
|
const Signature *s, const SigMatchCtx *ctx)
|
|
{
|
|
DEBUG_VALIDATE_BUG_ON(PKT_IS_PSEUDOPKT(p));
|
|
|
|
const DetectIpOptsData *de = (const DetectIpOptsData *)ctx;
|
|
|
|
if (!de || !PacketIsIPv4(p))
|
|
return 0;
|
|
|
|
return (p->l3.vars.ip4.opts_set & de->ipopt) == de->ipopt;
|
|
}
|
|
static DetectIpOptsData *DetectIpOptsParse (const char *rawstr)
|
|
{
|
|
if (rawstr == NULL || strlen(rawstr) == 0)
|
|
return NULL;
|
|
|
|
int i;
|
|
bool found = false;
|
|
for(i = 0; ipopts[i].ipopt_name != NULL; i++) {
|
|
if((strcasecmp(ipopts[i].ipopt_name,rawstr)) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
SCLogError("unknown IP option specified \"%s\"", rawstr);
|
|
return NULL;
|
|
}
|
|
|
|
DetectIpOptsData *de = SCMalloc(sizeof(DetectIpOptsData));
|
|
if (unlikely(de == NULL))
|
|
return NULL;
|
|
|
|
de->ipopt = ipopts[i].code;
|
|
|
|
return de;
|
|
}
|
|
|
|
int main(void) {
|
|
const char *names[] = { "any", "rr", "ts", "nop", "eol" };
|
|
const uint16_t masks[] = { 0, IPV4_OPT_FLAG_RR, IPV4_OPT_FLAG_TS,
|
|
IPV4_OPT_FLAG_NOP, IPV4_OPT_FLAG_EOL,
|
|
IPV4_OPT_FLAG_RR | IPV4_OPT_FLAG_TS, 0x1ffe };
|
|
puts("opts_set any rr ts nop eol");
|
|
for (unsigned i = 0; i < sizeof(masks)/sizeof(masks[0]); i++) {
|
|
Packet p = { .ipv4 = 1 };
|
|
p.l3.vars.ip4.opts_set = masks[i];
|
|
printf("0x%04x ", masks[i]);
|
|
for (unsigned j = 0; j < sizeof(names)/sizeof(names[0]); j++) {
|
|
DetectIpOptsData *de = DetectIpOptsParse(names[j]);
|
|
assert(de);
|
|
printf("%d ", DetectIpOptsMatch(NULL, &p, NULL, de));
|
|
free(de);
|
|
}
|
|
puts("");
|
|
}
|
|
uint16_t possible = 0;
|
|
for (unsigned i = 0; ipopts[i].ipopt_name; i++)
|
|
if (strcmp(ipopts[i].ipopt_name, "any")) possible |= ipopts[i].code;
|
|
unsigned checked = 0, matches = 0;
|
|
DetectIpOptsData *de = DetectIpOptsParse("any");
|
|
for (unsigned mask = 0; mask <= UINT16_MAX; mask++) {
|
|
if (mask & (uint16_t)~possible) continue;
|
|
Packet p = { .ipv4 = 1 };
|
|
p.l3.vars.ip4.opts_set = mask;
|
|
checked++;
|
|
matches += DetectIpOptsMatch(NULL, &p, NULL, de);
|
|
}
|
|
printf("possible_flags=0x%04x, tested_subsets=%u, any_matches=%u\n", possible, checked, matches);
|
|
assert(possible == 0x1ffe && checked == 4096 && matches == 0);
|
|
Packet ipv6 = { .ipv4 = 0 };
|
|
assert(DetectIpOptsMatch(NULL, &ipv6, NULL, de) == 0);
|
|
free(de);
|
|
}
|