diff --git a/tests/flow-pkt-recursion/README.md b/tests/flow-pkt-recursion/README.md new file mode 100644 index 000000000..6ad98acdb --- /dev/null +++ b/tests/flow-pkt-recursion/README.md @@ -0,0 +1,25 @@ +# Test Purpose + +Tests comparing flows with and without recursion level set. Ignoring +recursion level in flows is useful for devices that run inline IPS and +terminate an unencrypted tunnel, like an IPv6 tunnel. Terminating the +tunnel causes ingress request and reply traffic to have different +headers. e.g. + +request: IPv4]ICMP] -> |IPS| -> IPv6]IPv4]ICMP] +reply: <- |IPS| <- IPv6]IPv4]ICMP] + +The terminated tests are checking when the suricata is an inline IPS +device that is terminating a tunnel. Both flow directions are tested, +first packet ingress on non-tunneled interface and first packet ingress +on tunneled interface. +This is the case where recursion level can affect the flows when IPS is +running inline IPS and terminating a tunnel. + +Middleware tests check when the suricata device is analysing tunneled +packets and is not a tunnel terminator. +This case should not be affected by recursion level in flows. + +## PCAP + +This PCAP was generated with scapy. \ No newline at end of file diff --git a/tests/flow-pkt-recursion/flow-pkt-recursion-middleware-excluded/test.yaml b/tests/flow-pkt-recursion/flow-pkt-recursion-middleware-excluded/test.yaml new file mode 100644 index 000000000..d55ff2e29 --- /dev/null +++ b/tests/flow-pkt-recursion/flow-pkt-recursion-middleware-excluded/test.yaml @@ -0,0 +1,18 @@ +# Exclude recursion-level from flow matching +# Re-use the pcap from the middleware-included test. +command: | + ${SRCDIR}/src/suricata -c "${SRCDIR}/suricata.yaml" -l "${OUTPUT_DIR}" \ + -r "${TEST_DIR}/../flow-pkt-recursion-middleware-included/test.pcap" \ + --set "threshold-file=${SRCDIR}/threshold.config" \ + --set "classification-file=${SRCDIR}`[ -f ${SRCDIR}/etc/classification.config ] && printf '/etc'`/classification.config" \ + --set "reference-config-file=${SRCDIR}`[ -f ${SRCDIR}/etc/reference.config ] && printf '/etc'`/reference.config" \ + --set 'decoder.recursion-level.use-for-tracking=false' + +checks: + - filter: + count: 1 + match: + event_type: flow + proto: ICMP + flow.pkts_toserver: 1 + flow.pkts_toclient: 1 diff --git a/tests/flow-pkt-recursion/flow-pkt-recursion-middleware-included/test.pcap b/tests/flow-pkt-recursion/flow-pkt-recursion-middleware-included/test.pcap new file mode 100644 index 000000000..5efa32306 Binary files /dev/null and b/tests/flow-pkt-recursion/flow-pkt-recursion-middleware-included/test.pcap differ diff --git a/tests/flow-pkt-recursion/flow-pkt-recursion-middleware-included/test.yaml b/tests/flow-pkt-recursion/flow-pkt-recursion-middleware-included/test.yaml new file mode 100644 index 000000000..e7012bace --- /dev/null +++ b/tests/flow-pkt-recursion/flow-pkt-recursion-middleware-included/test.yaml @@ -0,0 +1,8 @@ +checks: + - filter: + count: 1 + match: + event_type: flow + proto: ICMP + flow.pkts_toserver: 1 + flow.pkts_toclient: 1 diff --git a/tests/flow-pkt-recursion/flow-pkt-recursion-terminated-excluded/test.yaml b/tests/flow-pkt-recursion/flow-pkt-recursion-terminated-excluded/test.yaml new file mode 100644 index 000000000..59a1a1b65 --- /dev/null +++ b/tests/flow-pkt-recursion/flow-pkt-recursion-terminated-excluded/test.yaml @@ -0,0 +1,19 @@ +# Exclude recursion-level from flow matching +# Re-use the pcap from the middleware-included test. +command: | + ${SRCDIR}/src/suricata -c "${SRCDIR}/suricata.yaml" -l "${OUTPUT_DIR}" \ + -r "${TEST_DIR}/../flow-pkt-recursion-terminated-included/test.pcap" \ + --set "threshold-file=${SRCDIR}/threshold.config" \ + --set "classification-file=${SRCDIR}`[ -f ${SRCDIR}/etc/classification.config ] && printf '/etc'`/classification.config" \ + --set "reference-config-file=${SRCDIR}`[ -f ${SRCDIR}/etc/reference.config ] && printf '/etc'`/reference.config" \ + --set 'decoder.recursion-level.use-for-tracking=false' + +checks: + # All packets should be caught as being in one flow + - filter: + count: 2 + match: + event_type: flow + proto: ICMP + flow.pkts_toserver: 1 + flow.pkts_toclient: 1 \ No newline at end of file diff --git a/tests/flow-pkt-recursion/flow-pkt-recursion-terminated-included/test.pcap b/tests/flow-pkt-recursion/flow-pkt-recursion-terminated-included/test.pcap new file mode 100644 index 000000000..d3efbd010 Binary files /dev/null and b/tests/flow-pkt-recursion/flow-pkt-recursion-terminated-included/test.pcap differ diff --git a/tests/flow-pkt-recursion/flow-pkt-recursion-terminated-included/test.yaml b/tests/flow-pkt-recursion/flow-pkt-recursion-terminated-included/test.yaml new file mode 100644 index 000000000..879a9c6b4 --- /dev/null +++ b/tests/flow-pkt-recursion/flow-pkt-recursion-terminated-included/test.yaml @@ -0,0 +1,9 @@ +checks: + # None of the flows are joined due to different recursion levels + - filter: + count: 4 + match: + event_type: flow + proto: ICMP + flow.pkts_toserver: 1 + flow.pkts_toclient: 0 diff --git a/tests/flow-pkt-recursion/test.py b/tests/flow-pkt-recursion/test.py new file mode 100644 index 000000000..cc1410145 --- /dev/null +++ b/tests/flow-pkt-recursion/test.py @@ -0,0 +1,35 @@ +from pathlib import Path + +from scapy.all import ICMP, IP, Ether, IPv6, PcapWriter, Raw + +mac_1, mac_2 = 'cb:cf:2b:50:a7:61', '49:a2:25:1a:07:4a' + +request = Ether(src=mac_1, dst=mac_2) +reply = Ether(src=mac_2, dst=mac_1) + +ip_1, ip_2 = '1.1.1.1', '2.2.2.2' +ipv6_1, ipv6_2 = 'fd01::1.1.1.1', 'fd02::2.2.2.2' + +payload = Raw(b'#JSb[abR^79aV(kDAN,(C\n\\A+p V+MF7\rd9Z&&9D31.;T%\x0ct.#') +icmp_echo = ICMP(type=8, seq=1) / payload +icmp_reply = ICMP(type=0, seq=1) / payload + +test = 'flow-pkt-recursion' + +middleware_pcap = Path.cwd() / f'{test}-middleware-included' / 'test.pcap' +with PcapWriter(str(middleware_pcap)) as pcap: + # Flow of IPv6 tunneled packets in both directions + pcap.write(request / IPv6(src=ipv6_1, dst=ipv6_2) / IP(src=ip_1, dst=ip_2) / icmp_echo) + pcap.write(reply / IPv6(src=ipv6_2, dst=ipv6_1) / IP(src=ip_2, dst=ip_1) / icmp_reply) + +terminated_pcap = Path.cwd() / f'{test}-terminated-included' / 'test.pcap' +with PcapWriter(str(terminated_pcap)) as pcap: + # Flow of tunnel terminated on Suricata device, echo originates + # from Suricata device + pcap.write(request / IP(src=ip_1, dst=ip_2) / icmp_echo) + pcap.write(reply / IPv6(src=ipv6_2, dst=ipv6_1) / IP(src=ip_2, dst=ip_1) / icmp_reply) + + # Flow of tunnel terminated on Suricata device, reply originates + # from Suricata device + pcap.write(reply / IPv6(src=ipv6_2, dst=ipv6_1) / IP(src=ip_2, dst=ip_1) / icmp_echo) + pcap.write(request / IP(src=ip_1, dst=ip_2) / icmp_reply)