Skip to content

detect/detection_filter: extend unique_on with src_ip|dst_ip - v8#15743

Open
oferda4 wants to merge 2 commits into
OISF:mainfrom
oferda4:feat/detect-detection-filter-unique_on-ips
Open

detect/detection_filter: extend unique_on with src_ip|dst_ip - v8#15743
oferda4 wants to merge 2 commits into
OISF:mainfrom
oferda4:feat/detect-detection-filter-unique_on-ips

Conversation

@oferda4

@oferda4 oferda4 commented Jun 27, 2026

Copy link
Copy Markdown

Add optional unique_on {src_ip|dst_ip} to detection_filter for distinct IP address counting within the seconds window. Features:

Runtime uses a hash table per threshold entry for tracking unique IP addresses (both IPv4 and IPv6).
Follows detection_filter semantics: alerting starts after the threshold (>count), not at it.
On window expiry, the window is reset and the current packet's IP is recorded as the first distinct of the new window. Validation:
unique_on src_ip/dst_ip works with any IP protocol (unlike port-based unique_on which requires tcp/udp/sctp). Memory management:
Hash table memory is bounded by detect.thresholds.memcap.
Reuses existing counters: bitmap_memuse and bitmap_alloc_fail. Refactoring:
Added ThresholdDistinctAdd helper to consolidate port/IP tracking logic and reduce code duplication. Tests:
C unit tests for parsing unique_on src_ip and dst_ip options.

Changes:
v2:

  • Update documentation
  • Increase unit tests coverage
  • Document use of hash table

v3:

  • Cleanup unit tests

v4:

  • Added validation to reject duplicate unique_on options
  • Fixed memory accounting in ThresholdDistinctReset: counters are now properly decremented if hash table re-init fails
  • Hash table is now pre-sized to count buckets instead of a fixed 64

v5:

  • Fixed use-after-free: HashTableAdd stores pointers without copying, so ThresholdDistinctAddIP now allocates owned copies of IP address bytes. A Free callback (ThresholdDistinctIPFree) is registered with the hash table to automatically free copies on reset/free.
  • Handle HashTableAdd allocation failure by freeing the IP copy
  • Simplified window expiry: te->current_count = 1 is now unconditional
  • Added documentation on two-phase memory allocation model (init-time bucket array vs. runtime per-entry copies)

v6:

  • Replace HashTable with pre-allocated flat buffer (Address *distinct_ip_buf) for IP deduplication — eliminates all per-packet SCMalloc calls
  • Add 4 new unit tests: IPv4 basic + buffer-full boundary, duplicate suppression, window reset, dst_ip with track by_src
  • Restrict port-protocol validation to unique_on src_port/dst_port only (not src_ip/dst_ip)

v7:

  • Simplified else if to else in ThresholdDistinctInit (only remaining case after port branch)
  • Clarified scan_limit comment: current_count can exceed max_count (counts all distinct IPs seen, but only max_count are stored in the buffer)
  • Added DF_UNIQUE_IP_MAX_COUNT (10000) cap for IP-based unique_on to bound per-entry buffer size

v8:

  • ThresholdDistinctAdd now returns void; the classic-counting fallback (current_count++) is handled inside the helper, removing the bool return and simplifying the call sites
  • Moved DF_UNIQUE_IP_MAX_COUNT define into detect-detection-filter.c (its only user) instead of the shared header
  • Lowered DF_UNIQUE_IP_MAX_COUNT cap from 10000 to 1024

Previous PR: #15194

Ticket: https://redmine.openinfosecfoundation.org/issues/8250
SV_BRANCH=OISF/suricata-verify#2924

oferda4 added 2 commits June 27, 2026 14:29
Convert DetectDetectionFilterDistinctAllocFailFallback from the
old result/goto pattern to FAIL_IF/PASS macros.

Ticket: 8250
Add optional unique_on {src_ip|dst_ip} to detection_filter for
distinct IP address counting within the seconds window.
Features:
- Runtime uses a hash table per threshold entry for tracking
  unique IP addresses (both IPv4 and IPv6).
- Hash table is pre-sized to the count value for optimal memory usage.
- Follows detection_filter semantics: alerting starts after the
  threshold (>count), not at it.
- On window expiry, the window is reset and the current packet's
  IP is recorded as the first distinct of the new window.
Validation:
- unique_on src_ip/dst_ip works with any IP protocol (unlike
  port-based unique_on which requires tcp/udp/sctp).
- Duplicate unique_on options are rejected with an error.
Memory management:
- Hash table memory is bounded by detect.thresholds.memcap.
- Reuses existing counters: bitmap_memuse and bitmap_alloc_fail.
- Memory counters are correctly decremented on hash table reset failure.
Refactoring:
- Added ThresholdDistinctAdd helper to consolidate port/IP
  tracking logic and reduce code duplication.
Tests:
- C unit tests for parsing unique_on src_ip and dst_ip options.

Ticket: 8250
@github-actions

Copy link
Copy Markdown

NOTE: This PR may contain new authors.

@codecov

codecov Bot commented Jun 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.51741% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.97%. Comparing base (17dc065) to head (b17dc47).

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #15743      +/-   ##
==========================================
+ Coverage   82.95%   82.97%   +0.01%     
==========================================
  Files        1003     1003              
  Lines      275096   275451     +355     
==========================================
+ Hits       228217   228550     +333     
- Misses      46879    46901      +22     
Flag Coverage Δ
fuzzcorpus 61.46% <31.00%> (-0.02%) ⬇️
livemode 18.49% <2.00%> (+0.13%) ⬆️
netns 22.67% <2.00%> (-0.04%) ⬇️
pcap 45.33% <7.00%> (-0.05%) ⬇️
suricata-verify 66.93% <74.00%> (-0.03%) ⬇️
unittests 58.50% <96.51%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant