Skip to content

Commit

Permalink
pf tests: add more fragmentation test cases
Browse files Browse the repository at this point in the history
Add more test cases for pf fragment hole counter.  Also look into
final fragment of echo reply and check total length of IP packet.

MFC after:	1 week
Obtained from:	OpenBSD, bluhm <bluhm@openbsd.org>, 640736615b
Sponsored by:	Rubicon Communications, LLC ("Netgate")

(cherry picked from commit db100bd93036855c7688dc088b811dc7b660f51d)
  • Loading branch information
kprovost authored and fichtner committed Feb 25, 2025
1 parent 5be39bc commit b8ab1d0
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 0 deletions.
4 changes: 4 additions & 0 deletions tests/sys/netpfil/pf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ ${PACKAGE}FILES+= CVE-2019-5597.py \
frag-overindex.py \
frag-overlimit.py \
frag-overreplace.py \
frag-overhole.py \
frag-adjhole.py \
pfsync_defer.py \
pft_ether.py \
rdr-srcport.py \
Expand All @@ -73,6 +75,8 @@ ${PACKAGE}FILESMODE_fragcommon.py= 0555
${PACKAGE}FILESMODE_frag-overindex.py= 0555
${PACKAGE}FILESMODE_frag-overlimit.py= 0555
${PACKAGE}FILESMODE_frag-overreplace.py= 0555
${PACKAGE}FILESMODE_frag-overhole.py= 0555
${PACKAGE}FILESMODE_frag-adjhole.py= 0555
${PACKAGE}FILESMODE_pfsync_defer.py= 0555
${PACKAGE}FILESMODE_pft_ether.py= 0555

Expand Down
58 changes: 58 additions & 0 deletions tests/sys/netpfil/pf/frag-adjhole.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env python3
#
# Copyright (c) 2025 Alexander Bluhm <bluhm@openbsd.org>

from fragcommon import *

# |--------|
# |--------|
# |-------|
# |----|

def send(src, dst, send_if, recv_if):
pid = os.getpid()
eid = pid & 0xffff
payload = b"ABCDEFGHIJKLMNOP" * 2
packet = sp.IP(src=src, dst=dst)/ \
sp.ICMP(type='echo-request', id=eid) / payload
frag = []
fid = pid & 0xffff
frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
flags='MF') / bytes(packet)[20:36])
frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
frag=2, flags='MF') / bytes(packet)[36:52])
frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
frag=1, flags='MF') / bytes(packet)[28:44])
frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
frag=4) / bytes(packet)[52:60])
eth=[]
for f in frag:
eth.append(sp.Ether()/f)
if os.fork() == 0:
time.sleep(1)
sp.sendp(eth, iface=send_if)
os._exit(0)

ans = sp.sniff(iface=recv_if, timeout=3, filter=
"ip and src " + dst + " and dst " + src + " and icmp")
for a in ans:
if a and a.type == sp.ETH_P_IP and \
a.payload.proto == 1 and \
a.payload.frag == 0 and a.payload.flags == 0 and \
sp.icmptypes[a.payload.payload.type] == 'echo-reply':
id = a.payload.payload.id
print("id=%#x" % (id))
if id != eid:
print("WRONG ECHO REPLY ID")
exit(2)
data = a.payload.payload.payload.load
print("payload=%s" % (data))
if data == payload:
exit(0)
print("PAYLOAD!=%s" % (payload))
exit(1)
print("NO ECHO REPLY")
exit(2)

if __name__ == '__main__':
main(send)
83 changes: 83 additions & 0 deletions tests/sys/netpfil/pf/frag-overhole.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
#
# Copyright (c) 2025 Alexander Bluhm <bluhm@openbsd.org>

from fragcommon import *

# index boundary 4096 |
# |--------------|
# ....
# |--------------|
# |----------|
# |XXXX----------|
# |XXXX----|
# |---|

# this should trigger "frag tail overlap %d" and "frag head overlap %d"
def send(src, dst, send_if, recv_if):
pid = os.getpid()
eid = pid & 0xffff
payload = b"ABCDEFGHIJKLMNOP"
dummy = b"01234567"
fragsize = 1024
boundary = 4096
fragnum = int(boundary / fragsize)
packet = sp.IP(src=src, dst=dst)/ \
sp.ICMP(type='echo-request', id=eid)/ \
((int((boundary + fragsize) / len(payload)) + 1) * payload)
packet_length = len(packet)
frag = []
fid = pid & 0xffff
for i in range(fragnum-1):
frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
frag=(i * fragsize)>>3, flags='MF')/
bytes(packet)[20 + i * fragsize:20 + (i + 1) * fragsize])
frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
frag=(boundary - fragsize) >> 3, flags='MF')/
bytes(packet)[20 + boundary - fragsize:20 + boundary - len(dummy)])
frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
frag=(boundary - len(dummy)) >> 3, flags='MF')/
(dummy+bytes(packet)[20 + boundary:20 + boundary + fragsize]))
frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
frag=(boundary - 8 - len(dummy)) >> 3, flags='MF')/
(dummy+bytes(packet)[20 + boundary - 8:20 + boundary]))
frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
frag=(boundary + fragsize) >> 3)/bytes(packet)[20 + boundary + fragsize:])
eth=[]
for f in frag:
eth.append(sp.Ether() / f)

if os.fork() == 0:
time.sleep(1)
for e in eth:
sp.sendp(e, iface=send_if)
time.sleep(0.001)
os._exit(0)

ans = sp.sniff(iface=recv_if, timeout=3, filter=
"ip and src " + dst + " and dst " + src + " and icmp")
for a in ans:
if a and a.type == sp.ETH_P_IP and \
a.payload.proto == 1 and \
a.payload.frag == 0 and \
sp.icmptypes[a.payload.payload.type] == 'echo-reply':
id = a.payload.payload.id
print("id=%#x" % (id))
if id != eid:
print("WRONG ECHO REPLY ID")
exit(2)
if a and a.type == sp.ETH_P_IP and \
a.payload.proto == 1 and \
a.payload.frag > 0 and \
a.payload.flags == '':
length = (a.payload.frag << 3) + a.payload.len
print("len=%d" % (length))
if length != packet_length:
print("WRONG ECHO REPLY LENGTH")
exit(1)
exit(0)
print("NO ECHO REPLY")
exit(1)

if __name__ == '__main__':
main(send)
38 changes: 38 additions & 0 deletions tests/sys/netpfil/pf/fragmentation_pass.sh
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,42 @@ overlimit_cleanup()
pft_cleanup
}

atf_test_case "overhole" "cleanup"
overhole_head()
{
atf_set descr 'ping fragment at index boundary which modifies pf hole counter'
atf_set require.user root
atf_set require.progs scapy
}

overhole_body()
{
frag_common overhole
}

overhole_cleanup()
{
pft_cleanup
}

atf_test_case "adjhole" "cleanup"
adjhole_head()
{
atf_set descr 'overlapping ping fragments which modifies pf hole counter'
atf_set require.user root
atf_set require.progs scapy
}

adjhole_body()
{
frag_common adjhole
}

adjhole_cleanup()
{
pft_cleanup
}

atf_test_case "reassemble" "cleanup"
reassemble_head()
{
Expand Down Expand Up @@ -476,6 +512,8 @@ atf_init_test_cases()
atf_add_test_case "overreplace"
atf_add_test_case "overindex"
atf_add_test_case "overlimit"
atf_add_test_case "overhole"
atf_add_test_case "adjhole"
atf_add_test_case "reassemble"
atf_add_test_case "no_df"
atf_add_test_case "reassemble_slowpath"
Expand Down

0 comments on commit b8ab1d0

Please sign in to comment.