]> git.ipfire.org Git - thirdparty/suricata-verify.git/commitdiff
test/luaxform: Ensure ip.src/ip.dst work with transforms 2730/head 2731/head
authorJeff Lucovsky <jlucovsky@oisf.net>
Wed, 22 Oct 2025 14:57:46 +0000 (10:57 -0400)
committerVictor Julien <vjulien@oisf.net>
Wed, 29 Oct 2025 15:33:51 +0000 (15:33 +0000)
Demonstrate that the sticky buffers ip.src/ip.dst work with transforms.
The sample lua script creates a string "cidr" from an ipv4 src/dst
address.

E.g., "1.2.3.0/24"

The tests require 9.0 or greater; when issue 8016 is addressed,
additional version constraints will be added.

Issue: 8015

tests/lua/lua-transform-09/README.md [new file with mode: 0644]
tests/lua/lua-transform-09/test.rules [new file with mode: 0644]
tests/lua/lua-transform-09/test.yaml [new file with mode: 0644]
tests/lua/lua-transform-09/transform.lua [new file with mode: 0644]

diff --git a/tests/lua/lua-transform-09/README.md b/tests/lua/lua-transform-09/README.md
new file mode 100644 (file)
index 0000000..801afba
--- /dev/null
@@ -0,0 +1 @@
+Ensure Lua transform works with the ip.src/ip.dst sticky buffers.
diff --git a/tests/lua/lua-transform-09/test.rules b/tests/lua/lua-transform-09/test.rules
new file mode 100644 (file)
index 0000000..dc4896a
--- /dev/null
@@ -0,0 +1,2 @@
+alert http any any -> any any (msg: "test ip cdr script"; flow:established; ip.src; luaxform: transform.lua, 0xffffff00; content:"10.20.48.0/24";sid: 1;)
+alert http any any -> any any (msg: "test ip cdr script"; flow:established; ip.dst; luaxform: transform.lua, 0xffff0000; content:"10.50.0.0/16";sid: 2;)
diff --git a/tests/lua/lua-transform-09/test.yaml b/tests/lua/lua-transform-09/test.yaml
new file mode 100644 (file)
index 0000000..4f3ef65
--- /dev/null
@@ -0,0 +1,22 @@
+requires:
+  min-version: 9
+
+args:
+  - --set default-rule-path=${TEST_DIR}
+  - --set security.lua.allow-rules=true
+
+pcap: ../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
diff --git a/tests/lua/lua-transform-09/transform.lua b/tests/lua/lua-transform-09/transform.lua
new file mode 100644 (file)
index 0000000..df8b762
--- /dev/null
@@ -0,0 +1,90 @@
+-- luaxform: Convert IPv4 + hex/netmask to CIDR
+-- Example: input="192.168.1.45", arg="0xffffff00" → "192.168.1.0/24"
+
+-- Trim leading/trailing whitespace
+local function trim(s)
+    return s:match("^%s*(.-)%s*$")
+end
+
+-- Parse a mask string (hex format like 0xffffff00)
+local function parse_mask(mask_hex)
+    if not mask_hex then return nil end
+    mask_hex = trim(mask_hex)
+    -- Remove optional 0x/0X prefix
+    local hex = mask_hex:match("^0[xX](%x+)$") or mask_hex
+    local num = tonumber(hex, 16)
+    return num
+end
+
+-- Count the number of 1 bits in a 32-bit number
+local function count_bits(n)
+    local count = 0
+    for i = 0, 31 do
+        if (n & (1 << i)) ~= 0 then count = count + 1 end
+    end
+    return count
+end
+
+-- Convert IPv4 string + mask to CIDR notation
+local function ip_to_cidr2(ip_str, mask_hex)
+    if not ip_str or not mask_hex then return nil end
+
+    local mask = parse_mask(mask_hex)
+    if not mask then return nil end
+
+    local a, b, c, d = ip_str:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")
+    if not a then return nil end
+
+    local ip = (tonumber(a) << 24) | (tonumber(b) << 16) | (tonumber(c) << 8) | tonumber(d)
+    local net = ip & mask
+    local bits = count_bits(mask)
+
+    return string.format("%d.%d.%d.%d/%d",
+        (net >> 24) & 0xFF,
+        (net >> 16) & 0xFF,
+        (net >> 8) & 0xFF,
+        net & 0xFF,
+        bits
+    )
+end
+
+-- Convert a 4-byte host-order IPv4 address and a mask to CIDR
+local function ip_to_cidr(ip_bytes, mask_hex)
+    if not ip_bytes or #ip_bytes ~= 4 then return nil end
+
+    -- Parse the mask
+    local mask = parse_mask(mask_hex)
+    if not mask then return nil end
+
+    -- Convert host-order bytes to IP number
+    local a, b, c, d = string.byte(ip_bytes, 1, 4)
+    local ip = (a << 24) | (b << 16) | (c << 8) | d
+
+    -- Compute network
+    local net = ip & mask
+
+    -- Count prefix length
+    local bits = count_bits(mask)
+
+    -- Convert back to dotted-decimal
+    return string.format("%d.%d.%d.%d/%d",
+        (net >> 24) & 0xFF,
+        (net >> 16) & 0xFF,
+        (net >> 8) & 0xFF,
+        net & 0xFF,
+        bits
+    )
+end
+
+-- Transform an IPv4 address and a netmask into a CIDR
+-- example: 1.2.3.4 0xffffff00 returns 1.2.3.0/24
+function transform(input, args)
+    if not input or not args or not args[1] then
+        return nil, 0
+    end
+
+    local cidr = ip_to_cidr(input, args[1])
+    if not cidr then return nil, 0 end
+
+    return cidr, #cidr
+end