Skip to main content

CIDR Match

Network

Synopsis

Writes true into a target field when an IP address falls inside any of the configured CIDR ranges, or equals any of the configured addresses.

Schema

- cidr_match:
field: <ident>
ranges: <string[]>
description: <text>
if: <script>
ignore_failure: <boolean>
ignore_missing: <boolean>
on_failure: <processor[]>
on_success: <processor[]>
tag: <string>
target_field: <ident>

Configuration

The following fields are used to define the processor:

FieldRequiredDefaultDescription
fieldY-Field containing the IP address to test
rangesY-CIDR ranges and/or bare IP addresses to test against. At least one is required
descriptionN-Explanatory note
ifN-Condition to run
ignore_failureNfalseSee Handling Failures
ignore_missingNfalseIf true, quietly exit if field doesn't exist
on_failureN-See Handling Failures
on_successN-See Handling Success
tagN-Identifier
disabledNfalseWhen true, the processor is skipped and the event continues to the next one. Lets you take a processor out of the path without removing its configuration
target_fieldNfieldField to store the result. Defaults to field, replacing the address with a boolean

Details

An entry in ranges is read as a CIDR range if it contains a /, and as a single address otherwise. The two forms can be mixed freely in one list, so a set of subnets and a handful of individual hosts need only one processor.

The result distinguishes three outcomes, and the third is the one to plan for:

ResultMeaning
trueThe address falls inside at least one range, or equals one of the addresses
falseThe address is valid and matched nothing
nullThe field's value is not a valid IP address

An unparseable address is not an error. It writes null, so a malformed value is distinguishable from a genuine non-match — test for == false rather than relying on falsiness if that distinction matters.

A malformed entry in ranges is an error, since that is a configuration mistake rather than bad data.

IPv4 and IPv6 are both accepted, and matching is by address value rather than text, so an address is tested against a range correctly regardless of how it was written.

note

target_field defaults to field, which replaces the address with a boolean. Set it explicitly to keep the address.

Examples

Internal Address Check

Flagging traffic from private address space...

{
"source": {"ip": "10.4.2.17"}
}
- cidr_match:
field: source.ip
ranges:
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
target_field: source.internal

leaving the address in place:

{
"source": {
"ip": "10.4.2.17",
"internal": true
}
}

Ranges and Single Addresses

An entry without a / is matched as one address...

{
"destination": {"ip": "8.8.8.8"}
}
- cidr_match:
field: destination.ip
ranges:
- "192.0.2.0/24"
- "8.8.8.8"
- "1.1.1.1"
target_field: destination.known_resolver

so subnets and individual hosts share one list:

{
"destination": {
"ip": "8.8.8.8",
"known_resolver": true
}
}

An Unparseable Address

A value that is not an IP address writes null rather than failing...

{
"source": {"ip": "unknown"}
}
- cidr_match:
field: source.ip
ranges: ["10.0.0.0/8"]
target_field: source.internal

keeping it distinguishable from a genuine non-match:

{
"source": {
"ip": "unknown",
"internal": null
}
}

IPv6

IPv6 ranges work the same way...

{
"source": {"ip": "fd00::42"}
}
- cidr_match:
field: source.ip
ranges: ["fc00::/7"]
target_field: source.unique_local

matching on address value, not on text:

{
"source": {
"ip": "fd00::42",
"unique_local": true
}
}