Project

General

Profile

Bug #9423 ยป suricata-ipopts-any-reproducer.c

Leonid Zaytsev, 11/13/2026 10:32 AM

 
/* 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);
}
    (1-1/1)