From 15e0e03e63563013f772fb5d43b6fe2cdce3da65 Mon Sep 17 00:00:00 2001 From: Jeff Lucovsky Date: Fri, 22 May 2026 10:55:51 -0400 Subject: [PATCH 1/5] tests: add cidr dataset type tests Ten suricata-verify tests covering the cidr dataset type: IPv4 isset/isnotset, dst matching, multi-rule, IPv6 isset/isnotset, mixed IPv4+IPv6, /32 host-exact precision, boundary conditions, and empty dataset behavior. All tests require min-version: 9 and verify per-IP alert counts alongside total alert counts. Issue: 8124 --- tests/datasets-cidr/README.md | 102 ++++++++++++++++++ .../input-ipv4.pcap | Bin 0 -> 459 bytes .../datasets-cidr-01-ipv4-isset/rfc1918.lst | 3 + .../datasets-cidr-01-ipv4-isset/test.rules | 1 + .../datasets-cidr-01-ipv4-isset/test.yaml | 46 ++++++++ .../datasets-cidr-01-ipv4-isset/writepcap.py | 37 +++++++ .../rfc1918.lst | 3 + .../datasets-cidr-02-ipv4-isnotset/test.rules | 1 + .../datasets-cidr-02-ipv4-isnotset/test.yaml | 45 ++++++++ .../datasets-cidr-03-ipv4-dst/google-dns.lst | 1 + .../datasets-cidr-03-ipv4-dst/test.rules | 1 + .../datasets-cidr-03-ipv4-dst/test.yaml | 24 +++++ .../datasets-cidr-04-multi-rule/rfc1918.lst | 3 + .../datasets-cidr-04-multi-rule/test.rules | 2 + .../datasets-cidr-04-multi-rule/test.yaml | 47 ++++++++ .../input-ipv6.pcap | Bin 0 -> 452 bytes .../datasets-cidr-05-ipv6-isset/test.rules | 1 + .../datasets-cidr-05-ipv6-isset/test.yaml | 40 +++++++ .../datasets-cidr-05-ipv6-isset/ula.lst | 1 + .../datasets-cidr-05-ipv6-isset/writepcap.py | 31 ++++++ .../datasets-cidr-06-ipv6-isnotset/test.rules | 1 + .../datasets-cidr-06-ipv6-isnotset/test.yaml | 40 +++++++ .../datasets-cidr-06-ipv6-isnotset/ula.lst | 1 + .../datasets-cidr-07-mixed/input-mixed.pcap | Bin 0 -> 305 bytes .../datasets-cidr-07-mixed/mixed.lst | 2 + .../datasets-cidr-07-mixed/test.rules | 1 + .../datasets-cidr-07-mixed/test.yaml | 35 ++++++ .../datasets-cidr-07-mixed/writepcap.py | 25 +++++ .../datasets-cidr-08-host-exact/host32.lst | 1 + .../datasets-cidr-08-host-exact/test.rules | 1 + .../datasets-cidr-08-host-exact/test.yaml | 34 ++++++ .../datasets-cidr-09-boundary/boundary.lst | 1 + .../input-boundary.pcap | Bin 0 -> 372 bytes .../datasets-cidr-09-boundary/test.rules | 2 + .../datasets-cidr-09-boundary/test.yaml | 47 ++++++++ .../datasets-cidr-09-boundary/writepcap.py | 31 ++++++ .../datasets-cidr-10-empty/empty.lst | 0 .../datasets-cidr-10-empty/test.rules | 2 + .../datasets-cidr-10-empty/test.yaml | 53 +++++++++ 39 files changed, 666 insertions(+) create mode 100644 tests/datasets-cidr/README.md create mode 100644 tests/datasets-cidr/datasets-cidr-01-ipv4-isset/input-ipv4.pcap create mode 100644 tests/datasets-cidr/datasets-cidr-01-ipv4-isset/rfc1918.lst create mode 100644 tests/datasets-cidr/datasets-cidr-01-ipv4-isset/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-01-ipv4-isset/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-01-ipv4-isset/writepcap.py create mode 100644 tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/rfc1918.lst create mode 100644 tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-03-ipv4-dst/google-dns.lst create mode 100644 tests/datasets-cidr/datasets-cidr-03-ipv4-dst/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-03-ipv4-dst/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-04-multi-rule/rfc1918.lst create mode 100644 tests/datasets-cidr/datasets-cidr-04-multi-rule/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-04-multi-rule/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-05-ipv6-isset/input-ipv6.pcap create mode 100644 tests/datasets-cidr/datasets-cidr-05-ipv6-isset/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-05-ipv6-isset/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-05-ipv6-isset/ula.lst create mode 100644 tests/datasets-cidr/datasets-cidr-05-ipv6-isset/writepcap.py create mode 100644 tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/ula.lst create mode 100644 tests/datasets-cidr/datasets-cidr-07-mixed/input-mixed.pcap create mode 100644 tests/datasets-cidr/datasets-cidr-07-mixed/mixed.lst create mode 100644 tests/datasets-cidr/datasets-cidr-07-mixed/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-07-mixed/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-07-mixed/writepcap.py create mode 100644 tests/datasets-cidr/datasets-cidr-08-host-exact/host32.lst create mode 100644 tests/datasets-cidr/datasets-cidr-08-host-exact/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-08-host-exact/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-09-boundary/boundary.lst create mode 100644 tests/datasets-cidr/datasets-cidr-09-boundary/input-boundary.pcap create mode 100644 tests/datasets-cidr/datasets-cidr-09-boundary/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-09-boundary/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-09-boundary/writepcap.py create mode 100644 tests/datasets-cidr/datasets-cidr-10-empty/empty.lst create mode 100644 tests/datasets-cidr/datasets-cidr-10-empty/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-10-empty/test.yaml diff --git a/tests/datasets-cidr/README.md b/tests/datasets-cidr/README.md new file mode 100644 index 0000000000..c0e75f17ae --- /dev/null +++ b/tests/datasets-cidr/README.md @@ -0,0 +1,102 @@ +# CIDR Dataset Tests + +This directory contains suricata-verify tests for the `cidr` dataset type. CIDR +datasets store IPv4 and IPv6 CIDR blocks in radix trees and are queried via the +`ip.src` or `ip.dst` sticky buffers using `dataset:isset` or `dataset:isnotset`. + +The dataset file format is plain text, one CIDR notation per line (e.g. +`192.168.0.0/16`, `fc00::/7`). + +## Test Overview + +### datasets-cidr-01-ipv4-isset + +Basic IPv4 isset test. Five DNS packets are sent to 8.8.8.8 with varying source +IPs. The CIDR file `rfc1918.lst` contains the three RFC1918 blocks. Three source +addresses (192.168.1.5, 10.0.0.50, 172.17.0.1) fall within those blocks and +trigger sid:1. The remaining two (1.2.3.4, 203.0.113.1) are public and do not. + +Generates: `input-ipv4.pcap` (shared by tests 02, 03, 04, 08, 10) + +### datasets-cidr-02-ipv4-isnotset + +Inverts test 01. Uses `isnotset` so sid:1 fires for the two public source IPs +(1.2.3.4 and 203.0.113.1) that are not covered by `rfc1918.lst`. Reuses +`../datasets-cidr-01-ipv4-isset/input-ipv4.pcap`. + +### datasets-cidr-03-ipv4-dst + +Matches on `ip.dst` instead of `ip.src`. All five packets in the shared pcap +go to 8.8.8.8, which falls inside `8.8.0.0/16` as loaded from `google-dns.lst`. +All five packets trigger sid:1. Reuses `../datasets-cidr-01-ipv4-isset/input-ipv4.pcap`. + +### datasets-cidr-04-multi-rule + +Two rules reference the same dataset simultaneously. Sid:1 uses `ip.src` isset +and fires for the three RFC1918 source addresses. Sid:2 uses `ip.dst` isnotset +and fires for all five packets because 8.8.8.8 is a public address not covered +by the RFC1918 dataset. Total alert count is 8. Reuses the shared ipv4 pcap. + +### datasets-cidr-05-ipv6-isset + +Basic IPv6 isset test. Four DNS packets carry IPv6 source addresses. The CIDR +file `ula.lst` contains `fc00::/7`, which covers all ULA addresses. Both +`fc00::1` and `fd12:3456::1` fall within `fc00::/7`; `2001:db8::1` and +`fe80::1` do not. + +Generates: `input-ipv6.pcap` (shared by test 06) + +### datasets-cidr-06-ipv6-isnotset + +Inverts test 05. Uses `isnotset` so sid:1 fires for the two non-ULA source +addresses (2001:db8::1 and fe80::1). Reuses +`../datasets-cidr-05-ipv6-isset/input-ipv6.pcap`. + +### datasets-cidr-07-mixed + +A single CIDR file `mixed.lst` contains both an IPv4 block (`192.168.0.0/16`) +and an IPv6 block (`fc00::/7`). The pcap contains three packets: an IPv4 packet +from 192.168.1.5, an IPv6 packet from fc00::1, and an IPv4 packet from 1.2.3.4. +Only the first two match. + +Generates: `input-mixed.pcap` + +### datasets-cidr-08-host-exact + +Tests `/32` precision. The file `host32.lst` contains only `192.168.1.5/32`. +From the five-packet shared pcap, only the packet sourced from exactly +192.168.1.5 matches. The other two RFC1918 sources (10.0.0.50 and 172.17.0.1) +do not match despite being private addresses. Reuses the shared ipv4 pcap. + +### datasets-cidr-09-boundary + +Tests the exact boundary of `192.168.0.0/16`. Four packets probe the first +address of the range (192.168.0.0), the last address (192.168.255.255), one +address just above (192.169.0.1), and one address just below (192.167.255.255). +Sid:1 (isset) fires for the two in-range addresses; sid:2 (isnotset) fires for +the two out-of-range addresses. + +Generates: `input-boundary.pcap` + +### datasets-cidr-10-empty + +Tests behavior with an empty CIDR file. The `empty.lst` file contains no +entries. Sid:1 (isset) never fires because no IP can match an empty dataset. +Sid:2 (isnotset) fires for all five packets from the shared ipv4 pcap because +every IP is absent from the empty dataset. Reuses the shared ipv4 pcap. + +## Shared pcap relationships + +``` +input-ipv4.pcap (in datasets-cidr-01-ipv4-isset/) + used by: 01, 02, 03, 04, 08, 10 + +input-ipv6.pcap (in datasets-cidr-05-ipv6-isset/) + used by: 05, 06 + +input-mixed.pcap (in datasets-cidr-07-mixed/) + used by: 07 only + +input-boundary.pcap (in datasets-cidr-09-boundary/) + used by: 09 only +``` diff --git a/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/input-ipv4.pcap b/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/input-ipv4.pcap new file mode 100644 index 0000000000000000000000000000000000000000..f419672bf9096cbbe9f507d95616cce248635c65 GIT binary patch literal 459 zcmca|c+)~A1{MYw`2U}Qff2~j2p7nbDP>}C2eLun2PU<68li~s`m)QZI1f}B+5`CKPcoFc^W2 zy$mu|;57pi0b`>p2phX^;u=8)MzFD0K*r{CFfbD^c0na!W7Fn=!Ub&XRgkgb!VD|~ bjD1%{*jT+qrx^+%uDu2__Pz)MD;{G3js08B literal 0 HcmV?d00001 diff --git a/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/rfc1918.lst b/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/rfc1918.lst new file mode 100644 index 0000000000..bc4609bb9b --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/rfc1918.lst @@ -0,0 +1,3 @@ +192.168.0.0/16 +10.0.0.0/8 +172.16.0.0/12 diff --git a/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/test.rules b/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/test.rules new file mode 100644 index 0000000000..3cce559da7 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"RFC1918 src"; ip.src; dataset:isset,rfc1918,type cidr,load rfc1918.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/test.yaml b/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/test.yaml new file mode 100644 index 0000000000..1f4fcd58f5 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/test.yaml @@ -0,0 +1,46 @@ +requires: + min-version: 9 + +pcap: input-ipv4.pcap + +args: + - -k none + +checks: + - filter: + count: 3 + match: + event_type: alert + - filter: + count: 3 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.1.5" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "10.0.0.50" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "172.17.0.1" + - filter: + count: 0 + match: + event_type: alert + src_ip: "1.2.3.4" + - filter: + count: 0 + match: + event_type: alert + src_ip: "203.0.113.1" diff --git a/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/writepcap.py b/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/writepcap.py new file mode 100644 index 0000000000..9fb0fa3c27 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-01-ipv4-isset/writepcap.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +from scapy.all import Ether, IP, UDP, DNS, DNSQR, wrpcap + +pkts = [] + +# 192.168.1.5 - in RFC1918 (192.168.0.0/16) - should match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.1.5', dst='8.8.8.8') / + UDP(sport=1234, dport=53) / + DNS(id=1, rd=1, qd=DNSQR(qname='example.com'))) + +# 10.0.0.50 - in RFC1918 (10.0.0.0/8) - should match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='10.0.0.50', dst='8.8.8.8') / + UDP(sport=1235, dport=53) / + DNS(id=2, rd=1, qd=DNSQR(qname='example.com'))) + +# 172.17.0.1 - in RFC1918 (172.16.0.0/12) - should match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='172.17.0.1', dst='8.8.8.8') / + UDP(sport=1236, dport=53) / + DNS(id=3, rd=1, qd=DNSQR(qname='example.com'))) + +# 1.2.3.4 - public IP - should NOT match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='1.2.3.4', dst='8.8.8.8') / + UDP(sport=1237, dport=53) / + DNS(id=4, rd=1, qd=DNSQR(qname='example.com'))) + +# 203.0.113.1 - public IP (TEST-NET-3) - should NOT match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='203.0.113.1', dst='8.8.8.8') / + UDP(sport=1238, dport=53) / + DNS(id=5, rd=1, qd=DNSQR(qname='example.com'))) + +wrpcap('input-ipv4.pcap', pkts) +print("Wrote input-ipv4.pcap with %d packets" % len(pkts)) diff --git a/tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/rfc1918.lst b/tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/rfc1918.lst new file mode 100644 index 0000000000..bc4609bb9b --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/rfc1918.lst @@ -0,0 +1,3 @@ +192.168.0.0/16 +10.0.0.0/8 +172.16.0.0/12 diff --git a/tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/test.rules b/tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/test.rules new file mode 100644 index 0000000000..c590df4c86 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"Not RFC1918 src"; ip.src; dataset:isnotset,rfc1918,type cidr,load rfc1918.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/test.yaml b/tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/test.yaml new file mode 100644 index 0000000000..3ddbe822d4 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-02-ipv4-isnotset/test.yaml @@ -0,0 +1,45 @@ +requires: + min-version: 9 + +pcap: ../datasets-cidr-01-ipv4-isset/input-ipv4.pcap + +args: + - -k none + +checks: + - filter: + count: 2 + match: + event_type: alert + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "1.2.3.4" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "203.0.113.1" + - filter: + count: 0 + match: + event_type: alert + src_ip: "192.168.1.5" + - filter: + count: 0 + match: + event_type: alert + src_ip: "10.0.0.50" + - filter: + count: 0 + match: + event_type: alert + src_ip: "172.17.0.1" diff --git a/tests/datasets-cidr/datasets-cidr-03-ipv4-dst/google-dns.lst b/tests/datasets-cidr/datasets-cidr-03-ipv4-dst/google-dns.lst new file mode 100644 index 0000000000..d4c44f5c23 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-03-ipv4-dst/google-dns.lst @@ -0,0 +1 @@ +8.8.0.0/16 diff --git a/tests/datasets-cidr/datasets-cidr-03-ipv4-dst/test.rules b/tests/datasets-cidr/datasets-cidr-03-ipv4-dst/test.rules new file mode 100644 index 0000000000..c3c5d3e8be --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-03-ipv4-dst/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"Google DNS dst"; ip.dst; dataset:isset,google-dns,type cidr,load google-dns.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-03-ipv4-dst/test.yaml b/tests/datasets-cidr/datasets-cidr-03-ipv4-dst/test.yaml new file mode 100644 index 0000000000..d3aa3eb3f7 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-03-ipv4-dst/test.yaml @@ -0,0 +1,24 @@ +requires: + min-version: 9 + +pcap: ../datasets-cidr-01-ipv4-isset/input-ipv4.pcap + +args: + - -k none + +checks: + - filter: + count: 5 + match: + event_type: alert + - filter: + count: 5 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 5 + match: + event_type: alert + alert.signature_id: 1 + dest_ip: "8.8.8.8" diff --git a/tests/datasets-cidr/datasets-cidr-04-multi-rule/rfc1918.lst b/tests/datasets-cidr/datasets-cidr-04-multi-rule/rfc1918.lst new file mode 100644 index 0000000000..bc4609bb9b --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-04-multi-rule/rfc1918.lst @@ -0,0 +1,3 @@ +192.168.0.0/16 +10.0.0.0/8 +172.16.0.0/12 diff --git a/tests/datasets-cidr/datasets-cidr-04-multi-rule/test.rules b/tests/datasets-cidr/datasets-cidr-04-multi-rule/test.rules new file mode 100644 index 0000000000..008612cd0d --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-04-multi-rule/test.rules @@ -0,0 +1,2 @@ +alert ip any any -> any any (msg:"RFC1918 src isset"; ip.src; dataset:isset,rfc1918,type cidr,load rfc1918.lst; sid:1;) +alert ip any any -> any any (msg:"RFC1918 dst isnotset"; ip.dst; dataset:isnotset,rfc1918,type cidr,load rfc1918.lst; sid:2;) diff --git a/tests/datasets-cidr/datasets-cidr-04-multi-rule/test.yaml b/tests/datasets-cidr/datasets-cidr-04-multi-rule/test.yaml new file mode 100644 index 0000000000..b362496e4a --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-04-multi-rule/test.yaml @@ -0,0 +1,47 @@ +requires: + min-version: 9 + +pcap: ../datasets-cidr-01-ipv4-isset/input-ipv4.pcap + +args: + - -k none + +checks: + - filter: + count: 8 + match: + event_type: alert + - filter: + count: 3 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.1.5" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "10.0.0.50" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "172.17.0.1" + - filter: + count: 5 + match: + event_type: alert + alert.signature_id: 2 + - filter: + count: 5 + match: + event_type: alert + alert.signature_id: 2 + dest_ip: "8.8.8.8" diff --git a/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/input-ipv6.pcap b/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/input-ipv6.pcap new file mode 100644 index 0000000000000000000000000000000000000000..c57d3b2d705a1a9b9c0547e07bfd20b7f46620e0 GIT binary patch literal 452 zcmca|c+)~A1{MYw`2U}Qff2~j2p7nzO5$ON2C_l;KNv7DGBLBTw%tttiZG}OI{X3A z2*9Yo$h!lI%rJ^C=9>1gx5tM#!pv4M=_hh6b9|!7Nun MR{fjBz=Fpr07^DgBme*a literal 0 HcmV?d00001 diff --git a/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/test.rules b/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/test.rules new file mode 100644 index 0000000000..e32523ace6 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"ULA src"; ip.src; dataset:isset,ula,type cidr,load ula.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/test.yaml b/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/test.yaml new file mode 100644 index 0000000000..877393701a --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/test.yaml @@ -0,0 +1,40 @@ +requires: + min-version: 9 + +pcap: input-ipv6.pcap + +args: + - -k none + +checks: + - filter: + count: 2 + match: + event_type: alert + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "fc00::1" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "fd12:3456::1" + - filter: + count: 0 + match: + event_type: alert + src_ip: "2001:db8::1" + - filter: + count: 0 + match: + event_type: alert + src_ip: "fe80::1" diff --git a/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/ula.lst b/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/ula.lst new file mode 100644 index 0000000000..fecb0d3dce --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/ula.lst @@ -0,0 +1 @@ +fc00::/7 diff --git a/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/writepcap.py b/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/writepcap.py new file mode 100644 index 0000000000..3fc8d29e1f --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/writepcap.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +from scapy.all import Ether, IPv6, UDP, DNS, DNSQR, wrpcap + +pkts = [] + +# fc00::1 - in ULA range (fc00::/7) - should match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='fc00::1', dst='2001:db8::53') / + UDP(sport=1234, dport=53) / + DNS(id=1, rd=1, qd=DNSQR(qname='example.com'))) + +# fd12:3456::1 - in ULA range (fc00::/7 covers fd::/8 too) - should match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='fd12:3456::1', dst='2001:db8::53') / + UDP(sport=1235, dport=53) / + DNS(id=2, rd=1, qd=DNSQR(qname='example.com'))) + +# 2001:db8::1 - documentation prefix, not ULA - should NOT match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='2001:db8::1', dst='2001:db8::53') / + UDP(sport=1236, dport=53) / + DNS(id=3, rd=1, qd=DNSQR(qname='example.com'))) + +# fe80::1 - link-local, not ULA - should NOT match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='fe80::1', dst='2001:db8::53') / + UDP(sport=1237, dport=53) / + DNS(id=4, rd=1, qd=DNSQR(qname='example.com'))) + +wrpcap('input-ipv6.pcap', pkts) +print("Wrote input-ipv6.pcap with %d packets" % len(pkts)) diff --git a/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/test.rules b/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/test.rules new file mode 100644 index 0000000000..d10b07de6b --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"Not ULA src"; ip.src; dataset:isnotset,ula,type cidr,load ula.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/test.yaml b/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/test.yaml new file mode 100644 index 0000000000..9f904ee236 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/test.yaml @@ -0,0 +1,40 @@ +requires: + min-version: 9 + +pcap: ../datasets-cidr-05-ipv6-isset/input-ipv6.pcap + +args: + - -k none + +checks: + - filter: + count: 2 + match: + event_type: alert + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "2001:db8::1" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "fe80::1" + - filter: + count: 0 + match: + event_type: alert + src_ip: "fc00::1" + - filter: + count: 0 + match: + event_type: alert + src_ip: "fd12:3456::1" diff --git a/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/ula.lst b/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/ula.lst new file mode 100644 index 0000000000..fecb0d3dce --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/ula.lst @@ -0,0 +1 @@ +fc00::/7 diff --git a/tests/datasets-cidr/datasets-cidr-07-mixed/input-mixed.pcap b/tests/datasets-cidr/datasets-cidr-07-mixed/input-mixed.pcap new file mode 100644 index 0000000000000000000000000000000000000000..d1d50eb90f0667e7ed0c0d573bf18b8638d53b2a GIT binary patch literal 305 zcmca|c+)~A1{MYw`2U}Qff2~j2^YxP70 any any (msg:"Mixed CIDR src"; ip.src; dataset:isset,mixed,type cidr,load mixed.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-07-mixed/test.yaml b/tests/datasets-cidr/datasets-cidr-07-mixed/test.yaml new file mode 100644 index 0000000000..d280442298 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-07-mixed/test.yaml @@ -0,0 +1,35 @@ +requires: + min-version: 9 + +pcap: input-mixed.pcap + +args: + - -k none + +checks: + - filter: + count: 2 + match: + event_type: alert + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.1.5" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "fc00::1" + - filter: + count: 0 + match: + event_type: alert + src_ip: "1.2.3.4" diff --git a/tests/datasets-cidr/datasets-cidr-07-mixed/writepcap.py b/tests/datasets-cidr/datasets-cidr-07-mixed/writepcap.py new file mode 100644 index 0000000000..9b970dc9db --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-07-mixed/writepcap.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +from scapy.all import Ether, IP, IPv6, UDP, DNS, DNSQR, wrpcap + +pkts = [] + +# IPv4: 192.168.1.5 - in 192.168.0.0/16 - should match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.1.5', dst='8.8.8.8') / + UDP(sport=1234, dport=53) / + DNS(id=1, rd=1, qd=DNSQR(qname='example.com'))) + +# IPv6: fc00::1 - in fc00::/7 - should match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='fc00::1', dst='2001:db8::1') / + UDP(sport=1235, dport=53) / + DNS(id=2, rd=1, qd=DNSQR(qname='example.com'))) + +# IPv4: 1.2.3.4 - public IP, not in any CIDR - should NOT match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='1.2.3.4', dst='8.8.8.8') / + UDP(sport=1236, dport=53) / + DNS(id=3, rd=1, qd=DNSQR(qname='example.com'))) + +wrpcap('input-mixed.pcap', pkts) +print("Wrote input-mixed.pcap with %d packets" % len(pkts)) diff --git a/tests/datasets-cidr/datasets-cidr-08-host-exact/host32.lst b/tests/datasets-cidr/datasets-cidr-08-host-exact/host32.lst new file mode 100644 index 0000000000..56addab521 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-08-host-exact/host32.lst @@ -0,0 +1 @@ +192.168.1.5/32 diff --git a/tests/datasets-cidr/datasets-cidr-08-host-exact/test.rules b/tests/datasets-cidr/datasets-cidr-08-host-exact/test.rules new file mode 100644 index 0000000000..9fad40fcfb --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-08-host-exact/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"Exact host /32"; ip.src; dataset:isset,host32,type cidr,load host32.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-08-host-exact/test.yaml b/tests/datasets-cidr/datasets-cidr-08-host-exact/test.yaml new file mode 100644 index 0000000000..73578774a9 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-08-host-exact/test.yaml @@ -0,0 +1,34 @@ +requires: + min-version: 9 + +pcap: ../datasets-cidr-01-ipv4-isset/input-ipv4.pcap + +args: + - -k none + +checks: + - filter: + count: 1 + match: + event_type: alert + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.1.5" + - filter: + count: 0 + match: + event_type: alert + src_ip: "10.0.0.50" + - filter: + count: 0 + match: + event_type: alert + src_ip: "172.17.0.1" diff --git a/tests/datasets-cidr/datasets-cidr-09-boundary/boundary.lst b/tests/datasets-cidr/datasets-cidr-09-boundary/boundary.lst new file mode 100644 index 0000000000..e2c6a23343 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-09-boundary/boundary.lst @@ -0,0 +1 @@ +192.168.0.0/16 diff --git a/tests/datasets-cidr/datasets-cidr-09-boundary/input-boundary.pcap b/tests/datasets-cidr/datasets-cidr-09-boundary/input-boundary.pcap new file mode 100644 index 0000000000000000000000000000000000000000..40f34a9230d98955daf1debd0ff8ddee1ebec996 GIT binary patch literal 372 zcmca|c+)~A1{MYw`2U}Qff2~j2^Yv(&Be{&4rGJye=uNRWMXDv}mw1dKJ|C&Ji22bM!!dlhVKAp;8@V*vo~Qtto& literal 0 HcmV?d00001 diff --git a/tests/datasets-cidr/datasets-cidr-09-boundary/test.rules b/tests/datasets-cidr/datasets-cidr-09-boundary/test.rules new file mode 100644 index 0000000000..b36735af5f --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-09-boundary/test.rules @@ -0,0 +1,2 @@ +alert ip any any -> any any (msg:"Boundary isset"; ip.src; dataset:isset,boundary,type cidr,load boundary.lst; sid:1;) +alert ip any any -> any any (msg:"Boundary isnotset"; ip.src; dataset:isnotset,boundary,type cidr,load boundary.lst; sid:2;) diff --git a/tests/datasets-cidr/datasets-cidr-09-boundary/test.yaml b/tests/datasets-cidr/datasets-cidr-09-boundary/test.yaml new file mode 100644 index 0000000000..741e477da9 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-09-boundary/test.yaml @@ -0,0 +1,47 @@ +requires: + min-version: 9 + +pcap: input-boundary.pcap + +args: + - -k none + +checks: + - filter: + count: 4 + match: + event_type: alert + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.0.0" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.255.255" + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 2 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 2 + src_ip: "192.169.0.1" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 2 + src_ip: "192.167.255.255" diff --git a/tests/datasets-cidr/datasets-cidr-09-boundary/writepcap.py b/tests/datasets-cidr/datasets-cidr-09-boundary/writepcap.py new file mode 100644 index 0000000000..58aca62f51 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-09-boundary/writepcap.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +from scapy.all import Ether, IP, UDP, DNS, DNSQR, wrpcap + +pkts = [] + +# 192.168.0.0 - first address of 192.168.0.0/16 - should match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.0.0', dst='8.8.8.8') / + UDP(sport=1234, dport=53) / + DNS(id=1, rd=1, qd=DNSQR(qname='example.com'))) + +# 192.168.255.255 - last address of 192.168.0.0/16 - should match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.255.255', dst='8.8.8.8') / + UDP(sport=1235, dport=53) / + DNS(id=2, rd=1, qd=DNSQR(qname='example.com'))) + +# 192.169.0.1 - one above 192.168.0.0/16 - should NOT match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.169.0.1', dst='8.8.8.8') / + UDP(sport=1236, dport=53) / + DNS(id=3, rd=1, qd=DNSQR(qname='example.com'))) + +# 192.167.255.255 - one below 192.168.0.0/16 - should NOT match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.167.255.255', dst='8.8.8.8') / + UDP(sport=1237, dport=53) / + DNS(id=4, rd=1, qd=DNSQR(qname='example.com'))) + +wrpcap('input-boundary.pcap', pkts) +print("Wrote input-boundary.pcap with %d packets" % len(pkts)) diff --git a/tests/datasets-cidr/datasets-cidr-10-empty/empty.lst b/tests/datasets-cidr/datasets-cidr-10-empty/empty.lst new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/datasets-cidr/datasets-cidr-10-empty/test.rules b/tests/datasets-cidr/datasets-cidr-10-empty/test.rules new file mode 100644 index 0000000000..0c256c1b40 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-10-empty/test.rules @@ -0,0 +1,2 @@ +alert ip any any -> any any (msg:"Empty CIDR isset"; ip.src; dataset:isset,empty-cidr,type cidr,load empty.lst; sid:1;) +alert ip any any -> any any (msg:"Empty CIDR isnotset"; ip.src; dataset:isnotset,empty-cidr,type cidr,load empty.lst; sid:2;) diff --git a/tests/datasets-cidr/datasets-cidr-10-empty/test.yaml b/tests/datasets-cidr/datasets-cidr-10-empty/test.yaml new file mode 100644 index 0000000000..dd0a6c1d30 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-10-empty/test.yaml @@ -0,0 +1,53 @@ +requires: + min-version: 9 + +pcap: ../datasets-cidr-01-ipv4-isset/input-ipv4.pcap + +args: + - -k none + +checks: + - filter: + count: 5 + match: + event_type: alert + - filter: + count: 0 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 5 + match: + event_type: alert + alert.signature_id: 2 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 2 + src_ip: "192.168.1.5" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 2 + src_ip: "10.0.0.50" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 2 + src_ip: "172.17.0.1" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 2 + src_ip: "1.2.3.4" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 2 + src_ip: "203.0.113.1" From ce00e5c6607095cc635cafde07a866e1dda1ceb3 Mon Sep 17 00:00:00 2001 From: Jeff Lucovsky Date: Mon, 1 Jun 2026 09:53:49 -0400 Subject: [PATCH 2/5] tests: add cidr dataset feature gap tests Nine suricata-verify tests covering additional CIDR behaviors: - datasets-cidr-11-set: dataset:set fires once per unique source host - datasets-cidr-12-unset: dataset:unset removes pre-loaded host entries; fires once per address then not again - datasets-cidr-13-save-reject: save option rejected at parse time - datasets-cidr-14-state-reject: state option rejected at parse time - datasets-cidr-15-mask: dataset:set with mask 24 fires once per unique /24 prefix, not once per host - datasets-cidr-16-lua: Lua can retrieve a CIDR dataset via dataset.get() - datasets-cidr-17-mask-hex-prefix: mask 0x18 (hex) behaves like mask 24 - datasets-cidr-18-mask-bitmask: mask 0xffffff00 (IPv4 bitmask) behaves like mask 24 - datasets-cidr-19-unset-mask: dataset:unset with mask 24 removes the pre-loaded /24 netblock on first match; does not fire on subsequent packets from the same /24 after removal Issue: 8124 --- .../datasets-cidr-11-set/input.pcap | Bin 0 -> 372 bytes .../datasets-cidr-11-set/test.rules | 1 + .../datasets-cidr-11-set/test.yaml | 27 ++++++++++++ .../datasets-cidr-11-set/writepcap.py | 29 +++++++++++++ .../datasets-cidr-12-unset/host.lst | 2 + .../datasets-cidr-12-unset/input.pcap | Bin 0 -> 459 bytes .../datasets-cidr-12-unset/test.rules | 1 + .../datasets-cidr-12-unset/test.yaml | 33 ++++++++++++++ .../datasets-cidr-12-unset/writepcap.py | 35 +++++++++++++++ .../datasets-cidr-13-save-reject/test.rules | 1 + .../datasets-cidr-13-save-reject/test.yaml | 13 ++++++ .../datasets-cidr-14-state-reject/test.rules | 1 + .../datasets-cidr-14-state-reject/test.yaml | 13 ++++++ .../datasets-cidr-15-mask/input.pcap | Bin 0 -> 372 bytes .../datasets-cidr-15-mask/test.rules | 1 + .../datasets-cidr-15-mask/test.yaml | 41 ++++++++++++++++++ .../datasets-cidr-15-mask/writepcap.py | 31 +++++++++++++ .../datasets-cidr-16-lua/.cidr-check.lua.swp | Bin 0 -> 12288 bytes .../datasets-cidr-16-lua/cidr-check.lua | 29 +++++++++++++ .../datasets-cidr-16-lua/cidr.lst | 3 ++ .../datasets-cidr-16-lua/test.rules | 2 + .../datasets-cidr-16-lua/test.yaml | 25 +++++++++++ .../test.rules | 1 + .../test.yaml | 37 ++++++++++++++++ .../datasets-cidr-18-mask-bitmask/test.rules | 1 + .../datasets-cidr-18-mask-bitmask/test.yaml | 37 ++++++++++++++++ .../datasets-cidr-19-unset-mask/cidr.lst | 2 + .../datasets-cidr-19-unset-mask/input.pcap | Bin 0 -> 459 bytes .../datasets-cidr-19-unset-mask/test.rules | 1 + .../datasets-cidr-19-unset-mask/test.yaml | 35 +++++++++++++++ .../datasets-cidr-19-unset-mask/writepcap.py | 37 ++++++++++++++++ 31 files changed, 439 insertions(+) create mode 100644 tests/datasets-cidr/datasets-cidr-11-set/input.pcap create mode 100644 tests/datasets-cidr/datasets-cidr-11-set/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-11-set/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-11-set/writepcap.py create mode 100644 tests/datasets-cidr/datasets-cidr-12-unset/host.lst create mode 100644 tests/datasets-cidr/datasets-cidr-12-unset/input.pcap create mode 100644 tests/datasets-cidr/datasets-cidr-12-unset/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-12-unset/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-12-unset/writepcap.py create mode 100644 tests/datasets-cidr/datasets-cidr-13-save-reject/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-13-save-reject/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-14-state-reject/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-14-state-reject/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-15-mask/input.pcap create mode 100644 tests/datasets-cidr/datasets-cidr-15-mask/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-15-mask/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-15-mask/writepcap.py create mode 100644 tests/datasets-cidr/datasets-cidr-16-lua/.cidr-check.lua.swp create mode 100644 tests/datasets-cidr/datasets-cidr-16-lua/cidr-check.lua create mode 100644 tests/datasets-cidr/datasets-cidr-16-lua/cidr.lst create mode 100644 tests/datasets-cidr/datasets-cidr-16-lua/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-16-lua/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-19-unset-mask/cidr.lst create mode 100644 tests/datasets-cidr/datasets-cidr-19-unset-mask/input.pcap create mode 100644 tests/datasets-cidr/datasets-cidr-19-unset-mask/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-19-unset-mask/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-19-unset-mask/writepcap.py diff --git a/tests/datasets-cidr/datasets-cidr-11-set/input.pcap b/tests/datasets-cidr/datasets-cidr-11-set/input.pcap new file mode 100644 index 0000000000000000000000000000000000000000..21e538ad15e709a95348525091aaaf6264b0d744 GIT binary patch literal 372 zcmca|c+)~A1{MYw`2U}Qff2~*>6Fb%=4NJa2eLu any any (msg:"CIDR set new host"; ip.src; dataset:set,dynamic-cidr,type cidr; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-11-set/test.yaml b/tests/datasets-cidr/datasets-cidr-11-set/test.yaml new file mode 100644 index 0000000000..48b699a098 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-11-set/test.yaml @@ -0,0 +1,27 @@ +requires: + min-version: 9 + +pcap: input.pcap + +args: + - -k none + +checks: + # set fires only once per unique source address (new entry only) + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "1.2.3.4" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "5.6.7.8" diff --git a/tests/datasets-cidr/datasets-cidr-11-set/writepcap.py b/tests/datasets-cidr/datasets-cidr-11-set/writepcap.py new file mode 100644 index 0000000000..40bdf97bfe --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-11-set/writepcap.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +from scapy.all import Ether, IP, UDP, DNS, DNSQR, wrpcap + +pkts = [] + +# 2 packets from 1.2.3.4 - first fires 'set' (new), second does not +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='1.2.3.4', dst='8.8.8.8') / + UDP(sport=1234, dport=53) / + DNS(id=1, rd=1, qd=DNSQR(qname='example.com'))) + +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='1.2.3.4', dst='8.8.8.8') / + UDP(sport=1235, dport=53) / + DNS(id=2, rd=1, qd=DNSQR(qname='example.com'))) + +# 2 packets from 5.6.7.8 - first fires 'set' (new), second does not +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='5.6.7.8', dst='8.8.8.8') / + UDP(sport=1236, dport=53) / + DNS(id=3, rd=1, qd=DNSQR(qname='example.com'))) + +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='5.6.7.8', dst='8.8.8.8') / + UDP(sport=1237, dport=53) / + DNS(id=4, rd=1, qd=DNSQR(qname='example.com'))) + +wrpcap('input.pcap', pkts) +print("Wrote input.pcap with %d packets" % len(pkts)) diff --git a/tests/datasets-cidr/datasets-cidr-12-unset/host.lst b/tests/datasets-cidr/datasets-cidr-12-unset/host.lst new file mode 100644 index 0000000000..4065cb7caa --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-12-unset/host.lst @@ -0,0 +1,2 @@ +1.2.3.4 +5.6.7.8 diff --git a/tests/datasets-cidr/datasets-cidr-12-unset/input.pcap b/tests/datasets-cidr/datasets-cidr-12-unset/input.pcap new file mode 100644 index 0000000000000000000000000000000000000000..83ad8991f4328132f3896f24683e19ecc24333a6 GIT binary patch literal 459 zcmca|c+)~A1{MYw`2U}Qff2}A-YJ`vm&nZE4rGJye=uNRWMXDvm|KvO%AA~^3ld}m8Jn9-lChV;#)>mA5is^h z3Snd8rm?cIgB;7jas^~8KQ99_0b>QzNHX>+*jR1`76Qh`q!TtaViG41fL(hHWGp8u I11lb50gPc-YybcN literal 0 HcmV?d00001 diff --git a/tests/datasets-cidr/datasets-cidr-12-unset/test.rules b/tests/datasets-cidr/datasets-cidr-12-unset/test.rules new file mode 100644 index 0000000000..58d9696cfc --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-12-unset/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"CIDR unset host"; ip.src; dataset:unset,host-unset,type cidr,load host.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-12-unset/test.yaml b/tests/datasets-cidr/datasets-cidr-12-unset/test.yaml new file mode 100644 index 0000000000..5d43335c24 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-12-unset/test.yaml @@ -0,0 +1,33 @@ +requires: + min-version: 9 + +pcap: input.pcap + +args: + - -k none + +checks: + # unset fires once per pre-loaded host address (removes it on first match) + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "1.2.3.4" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "5.6.7.8" + # 9.9.9.9 was never in the set, unset does not fire + - filter: + count: 0 + match: + event_type: alert + src_ip: "9.9.9.9" diff --git a/tests/datasets-cidr/datasets-cidr-12-unset/writepcap.py b/tests/datasets-cidr/datasets-cidr-12-unset/writepcap.py new file mode 100644 index 0000000000..e01ef169c8 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-12-unset/writepcap.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +from scapy.all import Ether, IP, UDP, DNS, DNSQR, wrpcap + +pkts = [] + +# 2 packets from 1.2.3.4 (pre-loaded as /32) - first unsets it, second does not match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='1.2.3.4', dst='8.8.8.8') / + UDP(sport=1234, dport=53) / + DNS(id=1, rd=1, qd=DNSQR(qname='example.com'))) + +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='1.2.3.4', dst='8.8.8.8') / + UDP(sport=1235, dport=53) / + DNS(id=2, rd=1, qd=DNSQR(qname='example.com'))) + +# 2 packets from 5.6.7.8 (pre-loaded as /32) - first unsets it, second does not match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='5.6.7.8', dst='8.8.8.8') / + UDP(sport=1236, dport=53) / + DNS(id=3, rd=1, qd=DNSQR(qname='example.com'))) + +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='5.6.7.8', dst='8.8.8.8') / + UDP(sport=1237, dport=53) / + DNS(id=4, rd=1, qd=DNSQR(qname='example.com'))) + +# 1 packet from 9.9.9.9 (never in set) - does not match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='9.9.9.9', dst='8.8.8.8') / + UDP(sport=1238, dport=53) / + DNS(id=5, rd=1, qd=DNSQR(qname='example.com'))) + +wrpcap('input.pcap', pkts) +print("Wrote input.pcap with %d packets" % len(pkts)) diff --git a/tests/datasets-cidr/datasets-cidr-13-save-reject/test.rules b/tests/datasets-cidr/datasets-cidr-13-save-reject/test.rules new file mode 100644 index 0000000000..2ccbf1ec98 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-13-save-reject/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"CIDR with save"; ip.src; dataset:isset,my-cidr,type cidr,save out.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-13-save-reject/test.yaml b/tests/datasets-cidr/datasets-cidr-13-save-reject/test.yaml new file mode 100644 index 0000000000..a17f68518e --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-13-save-reject/test.yaml @@ -0,0 +1,13 @@ +requires: + min-version: 9 + pcap: false + +exit-code: 1 + +args: + - --engine-analysis + +checks: + - shell: + args: grep "save/state is not supported for CIDR datasets" suricata.log | wc -l | xargs + expect: 1 diff --git a/tests/datasets-cidr/datasets-cidr-14-state-reject/test.rules b/tests/datasets-cidr/datasets-cidr-14-state-reject/test.rules new file mode 100644 index 0000000000..27c47ef07c --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-14-state-reject/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"CIDR with state"; ip.src; dataset:isset,my-cidr,type cidr,state cidr.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-14-state-reject/test.yaml b/tests/datasets-cidr/datasets-cidr-14-state-reject/test.yaml new file mode 100644 index 0000000000..a17f68518e --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-14-state-reject/test.yaml @@ -0,0 +1,13 @@ +requires: + min-version: 9 + pcap: false + +exit-code: 1 + +args: + - --engine-analysis + +checks: + - shell: + args: grep "save/state is not supported for CIDR datasets" suricata.log | wc -l | xargs + expect: 1 diff --git a/tests/datasets-cidr/datasets-cidr-15-mask/input.pcap b/tests/datasets-cidr/datasets-cidr-15-mask/input.pcap new file mode 100644 index 0000000000000000000000000000000000000000..488e66a4f5916f0f6dbb0da1132db7d804a23ddf GIT binary patch literal 372 zcmca|c+)~A1{MYw`2U}Qff300(kYu&Ifs+M9modZ|6stt$i&RT%E92uz+eed<{-G@ z+kq8~tQ$zWqI zgN+qrU?O1b()omqUHZ|wFb2Bz1lZWCU}IM>u;4Kk E0G7v99RL6T literal 0 HcmV?d00001 diff --git a/tests/datasets-cidr/datasets-cidr-15-mask/test.rules b/tests/datasets-cidr/datasets-cidr-15-mask/test.rules new file mode 100644 index 0000000000..0a9c7595de --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-15-mask/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"CIDR set with mask 24"; ip.src; dataset:set,masked-cidr,type cidr,mask 24; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-15-mask/test.yaml b/tests/datasets-cidr/datasets-cidr-15-mask/test.yaml new file mode 100644 index 0000000000..2e95eefc2a --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-15-mask/test.yaml @@ -0,0 +1,41 @@ +requires: + min-version: 9 + +pcap: input.pcap + +args: + - -k none + +checks: + # set+mask 24 fires once per unique /24 prefix, not once per host address + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + # first IP in 192.168.1.0/24 triggers the set + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.1.5" + # second IP from the same /24 does not trigger set (prefix already present) + - filter: + count: 0 + match: + event_type: alert + src_ip: "192.168.1.99" + # first IP in 192.168.2.0/24 triggers the set + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.2.1" + # fourth packet from 192.168.1.0/24 does not trigger set again + - filter: + count: 0 + match: + event_type: alert + src_ip: "192.168.1.200" diff --git a/tests/datasets-cidr/datasets-cidr-15-mask/writepcap.py b/tests/datasets-cidr/datasets-cidr-15-mask/writepcap.py new file mode 100644 index 0000000000..97bf02d589 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-15-mask/writepcap.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +from scapy.all import Ether, IP, UDP, DNS, DNSQR, wrpcap + +pkts = [] + +# 192.168.1.5 - first IP in 192.168.1.0/24; set+mask 24 adds the /24 (new -> fires) +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.1.5', dst='8.8.8.8') / + UDP(sport=1234, dport=53) / + DNS(id=1, rd=1, qd=DNSQR(qname='example.com'))) + +# 192.168.1.99 - same /24 already in set; set does not fire (not new) +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.1.99', dst='8.8.8.8') / + UDP(sport=1235, dport=53) / + DNS(id=2, rd=1, qd=DNSQR(qname='example.com'))) + +# 192.168.2.1 - different /24; adds 192.168.2.0/24 (new -> fires) +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.2.1', dst='8.8.8.8') / + UDP(sport=1236, dport=53) / + DNS(id=3, rd=1, qd=DNSQR(qname='example.com'))) + +# 192.168.1.200 - back to first /24 which is already in set; does not fire +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.1.200', dst='8.8.8.8') / + UDP(sport=1237, dport=53) / + DNS(id=4, rd=1, qd=DNSQR(qname='example.com'))) + +wrpcap('input.pcap', pkts) +print("Wrote input.pcap with %d packets" % len(pkts)) diff --git a/tests/datasets-cidr/datasets-cidr-16-lua/.cidr-check.lua.swp b/tests/datasets-cidr/datasets-cidr-16-lua/.cidr-check.lua.swp new file mode 100644 index 0000000000000000000000000000000000000000..8c4409aa36e1dd9598cb9704e2044bd948526cc2 GIT binary patch literal 12288 zcmeI2L2J}N6vt;h3RSUq@E|B>O z52o*1TR#Fe%nFh3xzVKboiK9k(i7ZsSG;3r(O=?TR^iBW{+ycR8p01;Rwu)=O!PrBvqmEbb0p5Iyy zAe#sf0U|&IhyW2F0z`la5P|=gfZuJgSJ>aFy2p$4+*+DzlO`fS1c(3;AOb{y2oM1x zKm>>Y5g-CY;2$KwC1a0Q7&}Cj|Noos|HHG4eMEggy+yr2Jx4u6-9z0)#i&)(w=;}= zM!iE#QLj-4sD0E`)CJT}%>Nzr74-#G*87B_K?H~Z5g-CYfCvx)B0vO)01+Spe?dSQ z$pD8{zOV+8%4jwh9No&HQ;-gNJuo^0KTw9Hg%Muo26ng?gEqH)*I{7@n{__GM=%g> zZ`|30`i?dhOJU3%xIzf!Tv}w=1oCaqwNVnt&d z-&Zyob8EEew}WFRGh$#n=Iia=T0Y1+ay94z!zr@vMPK@YoLFHaTNR3vN3t?H6_ zFnd_KdC=?%dSukN-8mxT2UcP9K)s*_Z!C&4ZkBjtmqSlQ>!+p-yN x3pJ~l<$`BWg<` any any (msg:"RFC1918 CIDR isset"; ip.src; dataset:isset,rfc1918-cidr,type cidr,load cidr.lst; sid:1;) +alert ip any any -> any any (msg:"Lua CIDR dataset access"; lua:cidr-check.lua; sid:2;) diff --git a/tests/datasets-cidr/datasets-cidr-16-lua/test.yaml b/tests/datasets-cidr/datasets-cidr-16-lua/test.yaml new file mode 100644 index 0000000000..8d9ecc585c --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-16-lua/test.yaml @@ -0,0 +1,25 @@ +requires: + min-version: 9 + +pcap: ../datasets-cidr-01-ipv4-isset/input-ipv4.pcap + +args: + - -k none + +checks: + # sid:1 (isset) fires for RFC1918 sources: 192.168.1.5, 10.0.0.50, 172.17.0.1 + - filter: + count: 3 + match: + event_type: alert + alert.signature_id: 1 + # sid:2 (lua) fires for every packet when Lua can access the CIDR dataset + - filter: + count: 5 + match: + event_type: alert + alert.signature_id: 2 + # no warning about "CIDR dataset get failed" in the log + - shell: + args: grep "CIDR dataset get failed" suricata.log | wc -l | xargs + expect: 0 diff --git a/tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.rules b/tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.rules new file mode 100644 index 0000000000..265513698a --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"CIDR set with mask 0x18 (hex prefix == /24)"; ip.src; dataset:set,masked-cidr,type cidr,mask 0x18; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.yaml b/tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.yaml new file mode 100644 index 0000000000..1cdc883902 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.yaml @@ -0,0 +1,37 @@ +requires: + min-version: 9 + +pcap: ../datasets-cidr-15-mask/input.pcap + +args: + - -k none + +checks: + # mask 0x18 is hex for 24; behavior identical to mask 24 + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.1.5" + - filter: + count: 0 + match: + event_type: alert + src_ip: "192.168.1.99" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.2.1" + - filter: + count: 0 + match: + event_type: alert + src_ip: "192.168.1.200" diff --git a/tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.rules b/tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.rules new file mode 100644 index 0000000000..54844e5ebb --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"CIDR set with mask 0xffffff00 (bitmask == /24)"; ip.src; dataset:set,masked-cidr,type cidr,mask 0xffffff00; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.yaml b/tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.yaml new file mode 100644 index 0000000000..ce8e5ea0c5 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.yaml @@ -0,0 +1,37 @@ +requires: + min-version: 9 + +pcap: ../datasets-cidr-15-mask/input.pcap + +args: + - -k none + +checks: + # mask 0xffffff00 is the IPv4 bitmask for /24; behavior identical to mask 24 + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.1.5" + - filter: + count: 0 + match: + event_type: alert + src_ip: "192.168.1.99" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.2.1" + - filter: + count: 0 + match: + event_type: alert + src_ip: "192.168.1.200" diff --git a/tests/datasets-cidr/datasets-cidr-19-unset-mask/cidr.lst b/tests/datasets-cidr/datasets-cidr-19-unset-mask/cidr.lst new file mode 100644 index 0000000000..87ef5264d8 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-19-unset-mask/cidr.lst @@ -0,0 +1,2 @@ +192.168.1.0/24 +192.168.2.0/24 diff --git a/tests/datasets-cidr/datasets-cidr-19-unset-mask/input.pcap b/tests/datasets-cidr/datasets-cidr-19-unset-mask/input.pcap new file mode 100644 index 0000000000000000000000000000000000000000..324b4cb2d0fa972d9d1fc83d882c09bad3bc3aec GIT binary patch literal 459 zcmca|c+)~A1{MYw`2U}Qff2|N>66X6dw_+(9modZ|6stt$i&RT%E92uz+eed<{-G@ z+kq8~tQ=O*vnvJ^BI^3 z7|VZzu(8X39azD{2zKoikg*Yk49o any any (msg:"CIDR unset with mask 24"; ip.src; dataset:unset,prefix-unset,type cidr,load cidr.lst,mask 24; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-19-unset-mask/test.yaml b/tests/datasets-cidr/datasets-cidr-19-unset-mask/test.yaml new file mode 100644 index 0000000000..f63cb6b731 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-19-unset-mask/test.yaml @@ -0,0 +1,35 @@ +requires: + min-version: 9 + +pcap: input.pcap + +args: + - -k none + +checks: + # unset,mask 24 fires once per /24 loaded from file (removes it on first match) + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + # first packet from 192.168.1.5 masks to 192.168.1.0/24 and removes it + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.1.5" + # second packet from 192.168.1.5: /24 already removed, does not fire + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "192.168.2.1" + # 10.0.0.0/24 was not in the dataset, never fires + - filter: + count: 0 + match: + event_type: alert + src_ip: "10.0.0.1" diff --git a/tests/datasets-cidr/datasets-cidr-19-unset-mask/writepcap.py b/tests/datasets-cidr/datasets-cidr-19-unset-mask/writepcap.py new file mode 100644 index 0000000000..e9897bf1d9 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-19-unset-mask/writepcap.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +from scapy.all import Ether, IP, UDP, DNS, DNSQR, wrpcap + +pkts = [] + +# 2 packets from 192.168.1.5 (in 192.168.1.0/24) +# unset,mask 24: first masks to /24 and removes it (fires), second finds /24 gone (no fire) +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.1.5', dst='8.8.8.8') / + UDP(sport=1234, dport=53) / + DNS(id=1, rd=1, qd=DNSQR(qname='example.com'))) + +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.1.5', dst='8.8.8.8') / + UDP(sport=1235, dport=53) / + DNS(id=2, rd=1, qd=DNSQR(qname='example.com'))) + +# 2 packets from 192.168.2.1 (in 192.168.2.0/24) +# unset,mask 24: first removes /24 (fires), second finds /24 gone (no fire) +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.2.1', dst='8.8.8.8') / + UDP(sport=1236, dport=53) / + DNS(id=3, rd=1, qd=DNSQR(qname='example.com'))) + +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='192.168.2.1', dst='8.8.8.8') / + UDP(sport=1237, dport=53) / + DNS(id=4, rd=1, qd=DNSQR(qname='example.com'))) + +# 1 packet from 10.0.0.1: masked to 10.0.0.0/24, not in dataset, no fire +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IP(src='10.0.0.1', dst='8.8.8.8') / + UDP(sport=1238, dport=53) / + DNS(id=5, rd=1, qd=DNSQR(qname='example.com'))) + +wrpcap('input.pcap', pkts) +print("Wrote input.pcap with %d packets" % len(pkts)) From a33734cc227e8200186631f00dbc84a1d74aa4c1 Mon Sep 17 00:00:00 2001 From: Jeff Lucovsky Date: Mon, 1 Jun 2026 10:26:28 -0400 Subject: [PATCH 3/5] tests/cidr: fix test correctness issues - tests 05/06/07: add a per-test suricata.yaml with ipv6-compress: yes so eve.json IPv6 addresses appear in RFC5952 compressed form, matching the human-readable src_ip filters in test.yaml. - tests 15/17/18: replace different-source-IP pcap with same-5-tuple packets per /24 so intra-/24 ordering is deterministic (same flow -> same worker -> serial processing). - test 16: add --set default-rule-path=. so the Lua script is found relative to the test directory rather than the system rules dir. Issue: 8124 --- .../datasets-cidr-05-ipv6-isset/suricata.yaml | 8 ++++++++ .../suricata.yaml | 8 ++++++++ .../datasets-cidr-07-mixed/suricata.yaml | 8 ++++++++ .../datasets-cidr-15-mask/input.pcap | Bin 372 -> 372 bytes .../datasets-cidr-15-mask/test.yaml | 18 +++-------------- .../datasets-cidr-15-mask/writepcap.py | 19 +++++++++--------- .../datasets-cidr-16-lua/test.yaml | 1 + .../test.yaml | 12 +---------- .../datasets-cidr-18-mask-bitmask/test.yaml | 10 --------- 9 files changed, 39 insertions(+), 45 deletions(-) create mode 100644 tests/datasets-cidr/datasets-cidr-05-ipv6-isset/suricata.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/suricata.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-07-mixed/suricata.yaml diff --git a/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/suricata.yaml b/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/suricata.yaml new file mode 100644 index 0000000000..13886ea648 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-05-ipv6-isset/suricata.yaml @@ -0,0 +1,8 @@ +%YAML 1.1 +--- +outputs: + - eve-log: + enabled: true + ipv6-compress: yes + types: + - alert diff --git a/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/suricata.yaml b/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/suricata.yaml new file mode 100644 index 0000000000..13886ea648 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-06-ipv6-isnotset/suricata.yaml @@ -0,0 +1,8 @@ +%YAML 1.1 +--- +outputs: + - eve-log: + enabled: true + ipv6-compress: yes + types: + - alert diff --git a/tests/datasets-cidr/datasets-cidr-07-mixed/suricata.yaml b/tests/datasets-cidr/datasets-cidr-07-mixed/suricata.yaml new file mode 100644 index 0000000000..13886ea648 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-07-mixed/suricata.yaml @@ -0,0 +1,8 @@ +%YAML 1.1 +--- +outputs: + - eve-log: + enabled: true + ipv6-compress: yes + types: + - alert diff --git a/tests/datasets-cidr/datasets-cidr-15-mask/input.pcap b/tests/datasets-cidr/datasets-cidr-15-mask/input.pcap index 488e66a4f5916f0f6dbb0da1132db7d804a23ddf..ba1f3343743918bc76e5998e9d35534c9154bcaf 100644 GIT binary patch delta 85 zcmeyu^o41H1pA9AvRQM_F-?>Y05jVz0GURN1rt}wg83&e0{My)-*K~FVlZV;jVPYP U2$A5t43*%7NE9)!Ok6Du0Dx&8WB>pF delta 108 zcmeyu^o41H1pAjx*{sSroD<~(z|6#XK&F!5i~}nelR1EZ fires) +# 2 packets from 192.168.1.5 using the same 5-tuple (same flow, same worker). +# First masks to 192.168.1.0/24 and adds it (new -> fires); second finds it +# already present (no fire). pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / IP(src='192.168.1.5', dst='8.8.8.8') / UDP(sport=1234, dport=53) / DNS(id=1, rd=1, qd=DNSQR(qname='example.com'))) -# 192.168.1.99 - same /24 already in set; set does not fire (not new) pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / - IP(src='192.168.1.99', dst='8.8.8.8') / - UDP(sport=1235, dport=53) / + IP(src='192.168.1.5', dst='8.8.8.8') / + UDP(sport=1234, dport=53) / DNS(id=2, rd=1, qd=DNSQR(qname='example.com'))) -# 192.168.2.1 - different /24; adds 192.168.2.0/24 (new -> fires) +# 2 packets from 192.168.2.1 using the same 5-tuple (same flow, same worker). +# First adds 192.168.2.0/24 (new -> fires); second finds it already present. pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / IP(src='192.168.2.1', dst='8.8.8.8') / - UDP(sport=1236, dport=53) / + UDP(sport=1234, dport=53) / DNS(id=3, rd=1, qd=DNSQR(qname='example.com'))) -# 192.168.1.200 - back to first /24 which is already in set; does not fire pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / - IP(src='192.168.1.200', dst='8.8.8.8') / - UDP(sport=1237, dport=53) / + IP(src='192.168.2.1', dst='8.8.8.8') / + UDP(sport=1234, dport=53) / DNS(id=4, rd=1, qd=DNSQR(qname='example.com'))) wrpcap('input.pcap', pkts) diff --git a/tests/datasets-cidr/datasets-cidr-16-lua/test.yaml b/tests/datasets-cidr/datasets-cidr-16-lua/test.yaml index 8d9ecc585c..1fef596411 100644 --- a/tests/datasets-cidr/datasets-cidr-16-lua/test.yaml +++ b/tests/datasets-cidr/datasets-cidr-16-lua/test.yaml @@ -5,6 +5,7 @@ pcap: ../datasets-cidr-01-ipv4-isset/input-ipv4.pcap args: - -k none + - --set default-rule-path=. checks: # sid:1 (isset) fires for RFC1918 sources: 192.168.1.5, 10.0.0.50, 172.17.0.1 diff --git a/tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.yaml b/tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.yaml index 1cdc883902..81113a8fa4 100644 --- a/tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.yaml +++ b/tests/datasets-cidr/datasets-cidr-17-mask-hex-prefix/test.yaml @@ -7,7 +7,7 @@ args: - -k none checks: - # mask 0x18 is hex for 24; behavior identical to mask 24 + # mask 0x18 is hex for /24; behavior identical to mask 24 - filter: count: 2 match: @@ -19,19 +19,9 @@ checks: event_type: alert alert.signature_id: 1 src_ip: "192.168.1.5" - - filter: - count: 0 - match: - event_type: alert - src_ip: "192.168.1.99" - filter: count: 1 match: event_type: alert alert.signature_id: 1 src_ip: "192.168.2.1" - - filter: - count: 0 - match: - event_type: alert - src_ip: "192.168.1.200" diff --git a/tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.yaml b/tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.yaml index ce8e5ea0c5..687c9b58fc 100644 --- a/tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.yaml +++ b/tests/datasets-cidr/datasets-cidr-18-mask-bitmask/test.yaml @@ -19,19 +19,9 @@ checks: event_type: alert alert.signature_id: 1 src_ip: "192.168.1.5" - - filter: - count: 0 - match: - event_type: alert - src_ip: "192.168.1.99" - filter: count: 1 match: event_type: alert alert.signature_id: 1 src_ip: "192.168.2.1" - - filter: - count: 0 - match: - event_type: alert - src_ip: "192.168.1.200" From f77adb5b6fbbaad8c9da602e057c220e2f736726 Mon Sep 17 00:00:00 2001 From: Jeff Lucovsky Date: Sat, 22 Aug 2026 10:44:36 -0400 Subject: [PATCH 4/5] tests/cidr: add IPv6 set/unset and rejection coverage Seven suricata-verify tests: - datasets-cidr-20-ipv6-set: dataset:set on IPv6 sources, fires once per unique host (mirror of test 11 for IPv6). - datasets-cidr-21-ipv6-unset: dataset:unset on preloaded IPv6 hosts. - datasets-cidr-22-mask-isset-reject: mask option rejected at rule load when combined with isset. - datasets-cidr-23-mask-out-of-range: non-contiguous bitmask (0x00ff00ff) rejected at rule load. - datasets-cidr-24-mask-non-cidr-reject: mask option rejected when used with a non-CIDR dataset type. - datasets-cidr-25-datarep-cidr-reject: datarep keyword rejected at rule load when combined with type cidr. - datasets-cidr-26-mask-zero-reject: mask 0 rejected at rule load. Rejection tests use --engine-analysis and grep suricata.log for the specific error message; they require exit-code 1. Issue: 8124 --- tests/datasets-cidr/README.md | 161 +++++++++++++++++- .../datasets-cidr-20-ipv6-set/input-ipv6.pcap | Bin 0 -> 452 bytes .../datasets-cidr-20-ipv6-set/suricata.yaml | 8 + .../datasets-cidr-20-ipv6-set/test.rules | 1 + .../datasets-cidr-20-ipv6-set/test.yaml | 27 +++ .../datasets-cidr-20-ipv6-set/writepcap.py | 29 ++++ .../datasets-cidr-21-ipv6-unset/host.lst | 2 + .../input-ipv6.pcap | Bin 0 -> 559 bytes .../datasets-cidr-21-ipv6-unset/suricata.yaml | 8 + .../datasets-cidr-21-ipv6-unset/test.rules | 1 + .../datasets-cidr-21-ipv6-unset/test.yaml | 33 ++++ .../datasets-cidr-21-ipv6-unset/writepcap.py | 35 ++++ .../test.rules | 1 + .../test.yaml | 13 ++ .../test.rules | 1 + .../test.yaml | 13 ++ .../test.rules | 1 + .../test.yaml | 13 ++ .../test.rules | 1 + .../test.yaml | 14 ++ .../test.rules | 1 + .../test.yaml | 13 ++ 22 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 tests/datasets-cidr/datasets-cidr-20-ipv6-set/input-ipv6.pcap create mode 100644 tests/datasets-cidr/datasets-cidr-20-ipv6-set/suricata.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-20-ipv6-set/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-20-ipv6-set/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-20-ipv6-set/writepcap.py create mode 100644 tests/datasets-cidr/datasets-cidr-21-ipv6-unset/host.lst create mode 100644 tests/datasets-cidr/datasets-cidr-21-ipv6-unset/input-ipv6.pcap create mode 100644 tests/datasets-cidr/datasets-cidr-21-ipv6-unset/suricata.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-21-ipv6-unset/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-21-ipv6-unset/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-21-ipv6-unset/writepcap.py create mode 100644 tests/datasets-cidr/datasets-cidr-22-mask-isset-reject/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-22-mask-isset-reject/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-23-mask-out-of-range/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-23-mask-out-of-range/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-24-mask-non-cidr-reject/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-24-mask-non-cidr-reject/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-25-datarep-cidr-reject/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-25-datarep-cidr-reject/test.yaml create mode 100644 tests/datasets-cidr/datasets-cidr-26-mask-zero-reject/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-26-mask-zero-reject/test.yaml diff --git a/tests/datasets-cidr/README.md b/tests/datasets-cidr/README.md index c0e75f17ae..1ff2181837 100644 --- a/tests/datasets-cidr/README.md +++ b/tests/datasets-cidr/README.md @@ -9,6 +9,16 @@ The dataset file format is plain text, one CIDR notation per line (e.g. ## Test Overview +Tests are grouped by what they exercise: + +- 01-10 -- basic type behavior (isset/isnotset for IPv4, IPv6, mixed, + host-exact precision, boundary conditions, empty dataset) +- 11-12 -- set/unset behavior on IPv4 +- 13-14, 22-26 -- rejection tests (options that must fail rule load) +- 15, 17-19 -- the `mask` option and its notations +- 16 -- Lua access to a CIDR dataset +- 20-21 -- set/unset behavior on IPv6 + ### datasets-cidr-01-ipv4-isset Basic IPv4 isset test. Five DNS packets are sent to 8.8.8.8 with varying source @@ -85,11 +95,139 @@ entries. Sid:1 (isset) never fires because no IP can match an empty dataset. Sid:2 (isnotset) fires for all five packets from the shared ipv4 pcap because every IP is absent from the empty dataset. Reuses the shared ipv4 pcap. +### datasets-cidr-11-set + +Basic `dataset:set` behavior on IPv4. Four packets from two unique sources +(1.2.3.4 and 5.6.7.8, two packets each). Sid:1 fires exactly once per +unique source: the first packet inserts a new /32 entry (`set` returns +match), the second packet finds it already present (`set` returns no +match). Total alert count is 2. + +Generates: `input.pcap` + +### datasets-cidr-12-unset + +Basic `dataset:unset` behavior on IPv4. `host.lst` preloads 1.2.3.4 and +5.6.7.8. Five packets: two from 1.2.3.4, two from 5.6.7.8, one from +9.9.9.9 (never in the set). Sid:1 fires on the first packet from each +preloaded source (the `unset` removes the entry); the second packet from +each source no longer matches. 9.9.9.9 never matches. Total 2 alerts. + +Generates: `input.pcap` + +### datasets-cidr-13-save-reject + +Rule uses the `save` option on a CIDR dataset. Rule load must fail with +`save/state is not supported for CIDR datasets`. Test runs +`--engine-analysis` (no pcap) and greps the log for that exact string; +requires `exit-code: 1`. + +### datasets-cidr-14-state-reject + +Same shape as 13, using the `state` option instead of `save`. Same +expected error message. + +### datasets-cidr-15-mask + +`dataset:set,type cidr,mask 24`. Four packets across two /24s: two +packets each from 192.168.1.5 and 192.168.2.1. With mask 24 the /24 +prefix (192.168.1.0/24, 192.168.2.0/24) is what gets inserted, so +sid:1 fires exactly once per unique /24. Packets are constructed with +the same 5-tuple within each /24 so autofp routes them to the same +worker, keeping ordering deterministic. + +Generates: `input.pcap` (shared by tests 17 and 18) + +### datasets-cidr-16-lua + +Two rules. Sid:1 is a standard CIDR `isset` against `rfc1918.lst` (via +`cidr.lst`). Sid:2 uses `lua:cidr-check.lua`; the Lua script calls +`dataset:get("rfc1918-cidr")` and returns 1 when the retrieval succeeds. +Uses `--set default-rule-path=.` so the Lua script is found relative to +the test directory rather than the system rules dir. Reuses the shared +`datasets-cidr-01-ipv4-isset/input-ipv4.pcap`. + +### datasets-cidr-17-mask-hex-prefix + +Hex-prefix form of the mask option: `mask 0x18` (== 24). Same rule and +expected behavior as test 15; reuses that test's `input.pcap`. Confirms +the parser accepts hex prefix lengths and treats them identically to +their decimal form. + +### datasets-cidr-18-mask-bitmask + +IPv4 bitmask form of the mask option: `mask 0xffffff00` (== /24). Same +rule shape and expected behavior as test 15; reuses that test's +`input.pcap`. Confirms the parser converts an IPv4 dotted-mask numeric +value to its prefix-length equivalent. + +### datasets-cidr-19-unset-mask + +`dataset:unset,type cidr,mask 24` with `cidr.lst` preloading two /24 +prefixes (192.168.1.0/24, 192.168.2.0/24). Four packets across those +two /24s (mirror of test 15's traffic pattern). Sid:1 fires exactly +once per /24 as the `unset` removes the netblock; subsequent packets in +the same /24 no longer match. + +Generates: `input.pcap` + +### datasets-cidr-20-ipv6-set + +IPv6 mirror of test 11. Four packets from two unique IPv6 sources +(fc00::1 and 2001:db8::1, two packets each). Sid:1 fires once per unique +source (the first packet inserts a new /128 entry; the second finds it +already present). Uses a per-test `suricata.yaml` with `ipv6-compress: +yes` so eve.json emits addresses in RFC 5952 compressed form to match +the `src_ip` filters in `test.yaml`. + +Generates: `input-ipv6.pcap` + +### datasets-cidr-21-ipv6-unset + +IPv6 mirror of test 12. `host.lst` preloads fc00::1 and 2001:db8::1. +Five packets: two from fc00::1, two from 2001:db8::1, one from fe80::1 +(never in the set). Sid:1 fires on the first packet from each preloaded +source; second packet from each source no longer matches; fe80::1 never +matches. Also uses `ipv6-compress: yes`. + +Generates: `input-ipv6.pcap` + +### datasets-cidr-22-mask-isset-reject + +Rule combines the `mask` option with `isset` on a CIDR dataset. Rule +load must fail with `mask is only supported for CIDR datasets with 'set' +and 'unset' commands`. `--engine-analysis`, no pcap, `exit-code: 1`. + +### datasets-cidr-23-mask-out-of-range + +Rule uses a non-contiguous IPv4 bitmask (`mask 0x00ff00ff`). Rule load +must fail with `not a contiguous IPv4 netmask`. `--engine-analysis`, +no pcap, `exit-code: 1`. + +### datasets-cidr-24-mask-non-cidr-reject + +Rule combines the `mask` option with `type ipv4` (a non-CIDR type). +Rule load must fail with `mask is only supported for CIDR datasets`. +`--engine-analysis`, no pcap, `exit-code: 1`. + +### datasets-cidr-25-datarep-cidr-reject + +Rule uses the `datarep` keyword with `type cidr`. Rule load must fail; +the datarep type parser doesn't accept `cidr`, so the signature is +rejected and the log contains `error parsing signature`. +`--engine-analysis`, no pcap, `exit-code: 1`. + +### datasets-cidr-26-mask-zero-reject + +Rule uses `mask 0` (which would match every address). Rule load must +fail with `prefix length 0 is not allowed`. `--engine-analysis`, no +pcap, `exit-code: 1`. + ## Shared pcap relationships ``` input-ipv4.pcap (in datasets-cidr-01-ipv4-isset/) - used by: 01, 02, 03, 04, 08, 10 + used by: 01, 02, 03, 04, 08, 10, 16 input-ipv6.pcap (in datasets-cidr-05-ipv6-isset/) used by: 05, 06 @@ -99,4 +237,25 @@ input-mixed.pcap (in datasets-cidr-07-mixed/) input-boundary.pcap (in datasets-cidr-09-boundary/) used by: 09 only + +input.pcap (in datasets-cidr-11-set/) + used by: 11 only + +input.pcap (in datasets-cidr-12-unset/) + used by: 12 only + +input.pcap (in datasets-cidr-15-mask/) + used by: 15, 17, 18 + +input.pcap (in datasets-cidr-19-unset-mask/) + used by: 19 only + +input-ipv6.pcap (in datasets-cidr-20-ipv6-set/) + used by: 20 only + +input-ipv6.pcap (in datasets-cidr-21-ipv6-unset/) + used by: 21 only ``` + +Tests 13, 14, 22, 23, 24, 25, and 26 do not use a pcap; they are +rejection tests that exercise `--engine-analysis` and grep the log. diff --git a/tests/datasets-cidr/datasets-cidr-20-ipv6-set/input-ipv6.pcap b/tests/datasets-cidr/datasets-cidr-20-ipv6-set/input-ipv6.pcap new file mode 100644 index 0000000000000000000000000000000000000000..d1aded9dce23df9e6de5af92a7af89ab74cb4b8c GIT binary patch literal 452 zcmca|c+)~A1{MYw`2U}Qff2}wmh#LJS;x*04PpcFe=uNRWMXDvZM&NQ6k$*mboc|J z5r9#Fk#`4z%Mi?RiNTaXl~Iv_5ojjJSRi0etw_u*$Vp{R&d&u2GJ>qSzn)4~DKIb* guu6C%A*;}Qfx}nl(-@cuSmm>cN>-&Zu;8%@0K&0SqyPW_ literal 0 HcmV?d00001 diff --git a/tests/datasets-cidr/datasets-cidr-20-ipv6-set/suricata.yaml b/tests/datasets-cidr/datasets-cidr-20-ipv6-set/suricata.yaml new file mode 100644 index 0000000000..13886ea648 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-20-ipv6-set/suricata.yaml @@ -0,0 +1,8 @@ +%YAML 1.1 +--- +outputs: + - eve-log: + enabled: true + ipv6-compress: yes + types: + - alert diff --git a/tests/datasets-cidr/datasets-cidr-20-ipv6-set/test.rules b/tests/datasets-cidr/datasets-cidr-20-ipv6-set/test.rules new file mode 100644 index 0000000000..82435d270e --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-20-ipv6-set/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"IPv6 CIDR set new host"; ip.src; dataset:set,ipv6-dynamic,type cidr; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-20-ipv6-set/test.yaml b/tests/datasets-cidr/datasets-cidr-20-ipv6-set/test.yaml new file mode 100644 index 0000000000..410ec923c4 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-20-ipv6-set/test.yaml @@ -0,0 +1,27 @@ +requires: + min-version: 9 + +pcap: input-ipv6.pcap + +args: + - -k none + +checks: + # set fires only once per unique IPv6 source address (new entry only) + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "fc00::1" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "2001:db8::1" diff --git a/tests/datasets-cidr/datasets-cidr-20-ipv6-set/writepcap.py b/tests/datasets-cidr/datasets-cidr-20-ipv6-set/writepcap.py new file mode 100644 index 0000000000..a43824c8dc --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-20-ipv6-set/writepcap.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +from scapy.all import Ether, IPv6, UDP, DNS, DNSQR, wrpcap + +pkts = [] + +# 2 packets from fc00::1 - first fires 'set' (new), second does not +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='fc00::1', dst='2001:db8::53') / + UDP(sport=1234, dport=53) / + DNS(id=1, rd=1, qd=DNSQR(qname='example.com'))) + +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='fc00::1', dst='2001:db8::53') / + UDP(sport=1234, dport=53) / + DNS(id=2, rd=1, qd=DNSQR(qname='example.com'))) + +# 2 packets from 2001:db8::1 - first fires 'set' (new), second does not +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='2001:db8::1', dst='2001:db8::53') / + UDP(sport=1234, dport=53) / + DNS(id=3, rd=1, qd=DNSQR(qname='example.com'))) + +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='2001:db8::1', dst='2001:db8::53') / + UDP(sport=1234, dport=53) / + DNS(id=4, rd=1, qd=DNSQR(qname='example.com'))) + +wrpcap('input-ipv6.pcap', pkts) +print("Wrote input-ipv6.pcap with %d packets" % len(pkts)) diff --git a/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/host.lst b/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/host.lst new file mode 100644 index 0000000000..3727c46a32 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/host.lst @@ -0,0 +1,2 @@ +fc00::1 +2001:db8::1 diff --git a/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/input-ipv6.pcap b/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/input-ipv6.pcap new file mode 100644 index 0000000000000000000000000000000000000000..64ad341ded382a08df44a3e06ef537e74c93d858 GIT binary patch literal 559 zcmca|c+)~A1{MYw`2U}Qff2}Ql=94yHDYIo2C_l;KNv7DGBLBTw%tttiZG}OI{X3A z2*9Yo$h!lUjsP!VFV6e{hPzUipMGd>@H@W literal 0 HcmV?d00001 diff --git a/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/suricata.yaml b/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/suricata.yaml new file mode 100644 index 0000000000..13886ea648 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/suricata.yaml @@ -0,0 +1,8 @@ +%YAML 1.1 +--- +outputs: + - eve-log: + enabled: true + ipv6-compress: yes + types: + - alert diff --git a/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/test.rules b/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/test.rules new file mode 100644 index 0000000000..4d93376e44 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"IPv6 CIDR unset host"; ip.src; dataset:unset,ipv6-host-unset,type cidr,load host.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/test.yaml b/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/test.yaml new file mode 100644 index 0000000000..1b9203bb74 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/test.yaml @@ -0,0 +1,33 @@ +requires: + min-version: 9 + +pcap: input-ipv6.pcap + +args: + - -k none + +checks: + # unset fires once per pre-loaded IPv6 address (removes it on first match) + - filter: + count: 2 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "fc00::1" + - filter: + count: 1 + match: + event_type: alert + alert.signature_id: 1 + src_ip: "2001:db8::1" + # fe80::1 was never in the set, unset does not fire + - filter: + count: 0 + match: + event_type: alert + src_ip: "fe80::1" diff --git a/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/writepcap.py b/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/writepcap.py new file mode 100644 index 0000000000..59cb3f5eb5 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-21-ipv6-unset/writepcap.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +from scapy.all import Ether, IPv6, UDP, DNS, DNSQR, wrpcap + +pkts = [] + +# 2 packets from fc00::1 (pre-loaded as /128) - first unsets it, second no longer matches +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='fc00::1', dst='2001:db8::53') / + UDP(sport=1234, dport=53) / + DNS(id=1, rd=1, qd=DNSQR(qname='example.com'))) + +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='fc00::1', dst='2001:db8::53') / + UDP(sport=1234, dport=53) / + DNS(id=2, rd=1, qd=DNSQR(qname='example.com'))) + +# 2 packets from 2001:db8::1 (pre-loaded as /128) - first unsets it, second no longer matches +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='2001:db8::1', dst='2001:db8::53') / + UDP(sport=1234, dport=53) / + DNS(id=3, rd=1, qd=DNSQR(qname='example.com'))) + +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='2001:db8::1', dst='2001:db8::53') / + UDP(sport=1234, dport=53) / + DNS(id=4, rd=1, qd=DNSQR(qname='example.com'))) + +# 1 packet from fe80::1 (never in set) - does not match +pkts.append(Ether(dst='ff:ff:ff:ff:ff:ff', src='00:01:02:03:04:05') / + IPv6(src='fe80::1', dst='2001:db8::53') / + UDP(sport=1234, dport=53) / + DNS(id=5, rd=1, qd=DNSQR(qname='example.com'))) + +wrpcap('input-ipv6.pcap', pkts) +print("Wrote input-ipv6.pcap with %d packets" % len(pkts)) diff --git a/tests/datasets-cidr/datasets-cidr-22-mask-isset-reject/test.rules b/tests/datasets-cidr/datasets-cidr-22-mask-isset-reject/test.rules new file mode 100644 index 0000000000..b2a450d9f9 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-22-mask-isset-reject/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"CIDR isset with mask"; ip.src; dataset:isset,my-cidr,type cidr,mask 24; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-22-mask-isset-reject/test.yaml b/tests/datasets-cidr/datasets-cidr-22-mask-isset-reject/test.yaml new file mode 100644 index 0000000000..75bed4138e --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-22-mask-isset-reject/test.yaml @@ -0,0 +1,13 @@ +requires: + min-version: 9 + pcap: false + +exit-code: 1 + +args: + - --engine-analysis + +checks: + - shell: + args: grep "mask is only supported for CIDR datasets with 'set' and 'unset' commands" suricata.log | wc -l | xargs + expect: 1 diff --git a/tests/datasets-cidr/datasets-cidr-23-mask-out-of-range/test.rules b/tests/datasets-cidr/datasets-cidr-23-mask-out-of-range/test.rules new file mode 100644 index 0000000000..08cac9b6e2 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-23-mask-out-of-range/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"CIDR set with non-contiguous bitmask"; ip.src; dataset:set,my-cidr,type cidr,mask 0x00ff00ff; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-23-mask-out-of-range/test.yaml b/tests/datasets-cidr/datasets-cidr-23-mask-out-of-range/test.yaml new file mode 100644 index 0000000000..2d6cf5fc00 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-23-mask-out-of-range/test.yaml @@ -0,0 +1,13 @@ +requires: + min-version: 9 + pcap: false + +exit-code: 1 + +args: + - --engine-analysis + +checks: + - shell: + args: grep "not a contiguous IPv4 netmask" suricata.log | wc -l | xargs + expect: 1 diff --git a/tests/datasets-cidr/datasets-cidr-24-mask-non-cidr-reject/test.rules b/tests/datasets-cidr/datasets-cidr-24-mask-non-cidr-reject/test.rules new file mode 100644 index 0000000000..d027502038 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-24-mask-non-cidr-reject/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"mask with non-CIDR type"; ip.src; dataset:set,my-ipv4,type ipv4,mask 24; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-24-mask-non-cidr-reject/test.yaml b/tests/datasets-cidr/datasets-cidr-24-mask-non-cidr-reject/test.yaml new file mode 100644 index 0000000000..695a427405 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-24-mask-non-cidr-reject/test.yaml @@ -0,0 +1,13 @@ +requires: + min-version: 9 + pcap: false + +exit-code: 1 + +args: + - --engine-analysis + +checks: + - shell: + args: grep "mask is only supported for CIDR datasets" suricata.log | wc -l | xargs + expect: 1 diff --git a/tests/datasets-cidr/datasets-cidr-25-datarep-cidr-reject/test.rules b/tests/datasets-cidr/datasets-cidr-25-datarep-cidr-reject/test.rules new file mode 100644 index 0000000000..51abe9334c --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-25-datarep-cidr-reject/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"datarep with cidr type"; ip.src; datarep:my-rep,>,10,type cidr,load rep.lst; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-25-datarep-cidr-reject/test.yaml b/tests/datasets-cidr/datasets-cidr-25-datarep-cidr-reject/test.yaml new file mode 100644 index 0000000000..58c3100c45 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-25-datarep-cidr-reject/test.yaml @@ -0,0 +1,14 @@ +requires: + min-version: 9 + pcap: false + +exit-code: 1 + +args: + - --engine-analysis + +checks: + # datarep does not support type cidr; the sig fails to parse. + - shell: + args: grep 'error parsing signature' suricata.log | wc -l | xargs + expect: 1 diff --git a/tests/datasets-cidr/datasets-cidr-26-mask-zero-reject/test.rules b/tests/datasets-cidr/datasets-cidr-26-mask-zero-reject/test.rules new file mode 100644 index 0000000000..88741d18b5 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-26-mask-zero-reject/test.rules @@ -0,0 +1 @@ +alert ip any any -> any any (msg:"CIDR set with mask 0"; ip.src; dataset:set,my-cidr,type cidr,mask 0; sid:1;) diff --git a/tests/datasets-cidr/datasets-cidr-26-mask-zero-reject/test.yaml b/tests/datasets-cidr/datasets-cidr-26-mask-zero-reject/test.yaml new file mode 100644 index 0000000000..18e14ef88b --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-26-mask-zero-reject/test.yaml @@ -0,0 +1,13 @@ +requires: + min-version: 9 + pcap: false + +exit-code: 1 + +args: + - --engine-analysis + +checks: + - shell: + args: grep "prefix length 0 is not allowed" suricata.log | wc -l | xargs + expect: 1 From 766286a7589ac546a349899172f90c5bb576a769 Mon Sep 17 00:00:00 2001 From: Jeff Lucovsky Date: Sun, 23 Aug 2026 09:45:21 -0400 Subject: [PATCH 5/5] tests/cidr: add luaxform-equivalent using CIDR dataset Two rules mirror the semantics of tests/lua/lua-transform-09 without per-packet Lua invocation. That test uses luaxform to mask an IPv4 address to a network and content-matches the resulting CIDR string; here isset on a type-cidr dataset walks a radix tree that already contains the target prefix. Reuses ../../lua/lua-transform-01/test.pcap so the alert counts (4 for sid:1 on ip.src in 10.20.48.0/24, 4 for sid:2 on ip.dst in 10.50.0.0/16) are directly comparable to lua-transform-09. Issue: 8124 --- tests/datasets-cidr/README.md | 21 +++++++++++++++++++ .../net-dst.lst | 1 + .../net-src.lst | 1 + .../test.rules | 2 ++ .../test.yaml | 21 +++++++++++++++++++ 5 files changed, 46 insertions(+) create mode 100644 tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/net-dst.lst create mode 100644 tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/net-src.lst create mode 100644 tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/test.rules create mode 100644 tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/test.yaml diff --git a/tests/datasets-cidr/README.md b/tests/datasets-cidr/README.md index 1ff2181837..6ccd12572b 100644 --- a/tests/datasets-cidr/README.md +++ b/tests/datasets-cidr/README.md @@ -18,6 +18,9 @@ Tests are grouped by what they exercise: - 15, 17-19 -- the `mask` option and its notations - 16 -- Lua access to a CIDR dataset - 20-21 -- set/unset behavior on IPv6 +- 27 -- CIDR-dataset equivalent of the luaxform IP->CIDR-string pattern + used by `tests/lua/lua-transform-09`, reusing that test's pcap and + assertions ### datasets-cidr-01-ipv4-isset @@ -223,6 +226,20 @@ Rule uses `mask 0` (which would match every address). Rule load must fail with `prefix length 0 is not allowed`. `--engine-analysis`, no pcap, `exit-code: 1`. +### datasets-cidr-27-luaxform-equivalent + +Demonstrates that a CIDR-dataset `isset` match replaces the +luaxform-based IP->CIDR-string pattern used by +`tests/lua/lua-transform-09`, without per-packet Lua invocation. Two +rules mirror lua-transform-09's semantics: + +- sid:1 uses `ip.src` with `net-src.lst` containing `10.20.48.0/24` +- sid:2 uses `ip.dst` with `net-dst.lst` containing `10.50.0.0/16` + +Reuses `../../lua/lua-transform-01/test.pcap` (the same pcap that +lua-transform-09 loads via `../lua-transform-01/test.pcap`) and +asserts the same 4+4 alert counts. + ## Shared pcap relationships ``` @@ -259,3 +276,7 @@ input-ipv6.pcap (in datasets-cidr-21-ipv6-unset/) Tests 13, 14, 22, 23, 24, 25, and 26 do not use a pcap; they are rejection tests that exercise `--engine-analysis` and grep the log. + +Test 27 reuses `../../lua/lua-transform-01/test.pcap` from a +different test group to mirror the assertions of +`tests/lua/lua-transform-09`. diff --git a/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/net-dst.lst b/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/net-dst.lst new file mode 100644 index 0000000000..2ddc1d2060 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/net-dst.lst @@ -0,0 +1 @@ +10.50.0.0/16 diff --git a/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/net-src.lst b/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/net-src.lst new file mode 100644 index 0000000000..d79eae923e --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/net-src.lst @@ -0,0 +1 @@ +10.20.48.0/24 diff --git a/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/test.rules b/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/test.rules new file mode 100644 index 0000000000..f08517a713 --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/test.rules @@ -0,0 +1,2 @@ +alert http any any -> any any (msg:"src in 10.20.48.0/24"; flow:established; ip.src; dataset:isset,net-src,type cidr,load net-src.lst; sid:1;) +alert http any any -> any any (msg:"dst in 10.50.0.0/16"; flow:established; ip.dst; dataset:isset,net-dst,type cidr,load net-dst.lst; sid:2;) diff --git a/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/test.yaml b/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/test.yaml new file mode 100644 index 0000000000..6e5dceb34c --- /dev/null +++ b/tests/datasets-cidr/datasets-cidr-27-luaxform-equivalent/test.yaml @@ -0,0 +1,21 @@ +requires: + min-version: 9 + +# Reuses the same pcap as tests/lua/lua-transform-09 so the CIDR-dataset +# rules produce the same alert counts (4 for sid:1, 4 for sid:2) that the +# luaxform equivalents produce there. Demonstrates that a CIDR-dataset +# isset match replaces the luaxform IP->CIDR-string pattern without any +# per-packet Lua invocation. +pcap: ../../lua/lua-transform-01/test.pcap + +checks: + - filter: + count: 4 + match: + event_type: alert + alert.signature_id: 1 + - filter: + count: 4 + match: + event_type: alert + alert.signature_id: 2