What an iptables / netfilter line looks like
The key=value sample below is fed verbatim into the engine to produce every parser on this page.
IN=eth0 OUT= MAC=00:1a:2b:3c:4d:5e SRC=203.0.113.45 DST=192.0.2.10 LEN=60 TTL=54 PROTO=TCP SPT=51234 DPT=22 WINDOW=1024 SYN
IN=eth0 OUT= MAC=00:1a:2b:3c:4d:5e SRC=198.51.100.77 DST=192.0.2.20 LEN=40 TTL=118 PROTO=TCP SPT=40112 DPT=3389 WINDOW=512 SYN Detected fields
The engine classified this sample as kv and consolidated 11 fields across 2 lines. Fields marked literal were identical on every sample line, so they are baked into the pattern as anchors rather than captured.
- in : literal · literal
- out : literal · literal
- mac : mac · literal
- src : ipv4
- dst : ipv4
- len : number
- ttl : number
- proto : literal · literal
- spt : port
- dpt : port
- window : number
Regex (named capture groups)
# sample: IN=eth0 OUT= MAC=00:1a:2b:3c:4d:5e SRC=203.0.113.45 DST=192.0.2.10 LEN=60 TTL=54 PROTO=TCP SPT=51234 DPT=22 WINDOW=1024 SYN
# groups: src=203.0.113.45, dst=192.0.2.10, len=60, ttl=54, spt=51234, dpt=22, window=1024
^(?=.*?(?<![\w.\-])IN=eth0)(?=.*?(?<![\w.\-])OUT=|)(?=.*?(?<![\w.\-])MAC=00:1a:2b:3c:4d:5e)(?=.*?(?<![\w.\-])SRC=(?<src>\d{1,3}(?:\.\d{1,3}){3}))(?=.*?(?<![\w.\-])DST=(?<dst>\d{1,3}(?:\.\d{1,3}){3}))(?=.*?(?<![\w.\-])LEN=(?<len>-?\d+(?:\.\d+)?))(?=.*?(?<![\w.\-])TTL=(?<ttl>-?\d+(?:\.\d+)?))(?=.*?(?<![\w.\-])PROTO=TCP)(?=.*?(?<![\w.\-])SPT=(?<spt>\d{1,5}))(?=.*?(?<![\w.\-])DPT=(?<dpt>\d{1,5}))(?=.*?(?<![\w.\-])WINDOW=(?<window>-?\d+(?:\.\d+)?)).*$ - note a single linear template could not reproduce every input line — fields are captured with order-independent lookaheads instead
Grok pattern (Logstash / Elastic)
IN=eth0(?: OUT=)? MAC=00:1a:2b:3c:4d:5e SRC=%{IPV4:src} DST=%{IPV4:dst} LEN=%{NUMBER:len} TTL=%{NUMBER:ttl} PROTO=TCP SPT=%{INT:spt} DPT=%{INT:dpt} WINDOW=%{NUMBER:window} - note kv-structured input — consider the Logstash kv filter instead of (or after) grok
- note constant field "mac" embedded as literal anchor "00:1a:2b:3c:4d:5e" (varying=false)
- note 1 optional field(s) wrapped in (?:…)? inline regex — grok has no native optional syntax
Wazuh decoder (OS_Regex XML)
<!--
Generated by LogForge - Wazuh decoder (OS_Regex dialect, not PCRE)
sample: IN=eth0 OUT= MAC=00:1a:2b:3c:4d:5e SRC=203.0.113.45 DST=192.0.2.10 LEN=60 TTL=54 PROTO=TCP SPT=51234 DPT=22 WINDOW=1024 SYN
test with: /var/ossec/bin/wazuh-logtest
-->
<decoder name="iptables-kv">
<prematch>^IN=\w+ OUT=</prematch>
</decoder>
<decoder name="iptables-kv">
<parent>iptables-kv</parent>
<regex offset="after_parent"> SRC=(\d+.\d+.\d+.\d+)</regex>
<order>srcip</order>
</decoder>
<decoder name="iptables-kv">
<parent>iptables-kv</parent>
<regex offset="after_parent"> DST=(\d+.\d+.\d+.\d+)</regex>
<order>dstip</order>
</decoder>
<decoder name="iptables-kv">
<parent>iptables-kv</parent>
<regex offset="after_parent"> LEN=(\d+)</regex>
<order>len</order>
</decoder>
<decoder name="iptables-kv">
<parent>iptables-kv</parent>
<regex offset="after_parent"> TTL=(\d+)</regex>
<order>ttl</order>
</decoder>
<decoder name="iptables-kv">
<parent>iptables-kv</parent>
<regex offset="after_parent"> SPT=(\d+)</regex>
<order>srcport</order>
</decoder>
<decoder name="iptables-kv">
<parent>iptables-kv</parent>
<regex offset="after_parent"> DPT=(\d+)</regex>
<order>dstport</order>
</decoder>
<decoder name="iptables-kv">
<parent>iptables-kv</parent>
<regex offset="after_parent"> WINDOW=(\d+)</regex>
<order>window</order>
</decoder>
- note constant field "in" skipped (identical in every line)
- note constant field "out" skipped (identical in every line)
- note constant field "mac" skipped (identical in every line)
- note field "src" mapped to Wazuh conventional field "srcip"
- note field "dst" mapped to Wazuh conventional field "dstip"
- note constant field "proto" skipped (identical in every line)
- note field "spt" mapped to Wazuh conventional field "srcport"
- note field "dpt" mapped to Wazuh conventional field "dstport"
- note kv fields are extracted by same-named sibling decoders (offset="after_parent"), so per-line field order/absence is tolerated — the shared name is what makes Wazuh evaluate every sibling
- note decoder order and prematch specificity may need site-specific tuning (other decoders in your ruleset can shadow these) — validate with /var/ossec/bin/wazuh-logtest
rsyslog template / liblognorm rulebase
version=2
# iptables — liblognorm v2 rulebase (generated by LogForge)
# Usage with rsyslog (mmnormalize runs liblognorm):
# module(load="mmnormalize")
# action(type="mmnormalize" rulebase="/etc/rsyslog.d/iptables.rb" useRawMsg="on")
# Literal "%" is escaped as "%%"; raw tabs are written as \x09.
rule=iptables:IN=eth0 OUT= MAC=00:1a:2b:3c:4d:5e SRC=%src:ipv4% DST=%dst:ipv4% LEN=%len:number% TTL=%ttl:number% PROTO=TCP SPT=%spt:number% DPT=%dpt:number% WINDOW=%window:number% SYN
- note kv structure: rsyslog offers mmfields (fast, fixed single-char separator, untyped) and mmnormalize (this rulebase, typed fields + literal anchors); mmnormalize was chosen for typed extraction
- note trailing literal " SYN" reconstructed from line 1
- note chosen parser types: src=ipv4, dst=ipv4, len=number, ttl=number, spt=number, dpt=number, window=number
FAQ
- Why is OUT= (or IN=) empty in my iptables log line?
- That is expected. For an inbound packet netfilter fills IN= with the ingress interface and leaves OUT= empty; for an outbound packet the reverse. So a bare 'OUT=' with no value is normal, not corruption — your parser must accept empty-valued keys rather than assuming every KEY= has a value.
- What is the --log-prefix and why does it matter?
- It is a literal string you attach to a LOG rule (e.g. '[UFW BLOCK] ') that the kernel prepends to every matching packet's log line. Since netfilter itself does not name the rule, the prefix is how you attribute a logged packet to a specific rule or policy — parse it as your rule/action label.
- How are TCP flags represented in an iptables log?
- As bare uppercase words (SYN, ACK, PSH, RST, FIN, URG) sitting among the KEY=VALUE tokens with no key of their own. A parser must collect these standalone flag words separately rather than expecting every token to be KEY=VALUE. A packet with only SYN set, repeated across many destination ports, is the signature of a port scan.
- Where do iptables logs actually appear on disk?
- They come from the kernel (netfilter LOG target), so they land wherever kernel messages go: dmesg, /var/log/kern.log (Debian/Ubuntu), /var/log/messages (RHEL), or the systemd journal. There is no dedicated iptables file; the log-prefix is what lets you filter your firewall lines out of the general kernel stream.
Try it on your own iptables / netfilter lines
Paste a few real lines, review the detected fields, and copy whichever format your stack needs. Free, no account, nothing uploaded.
Open this sample in LogForge →