From 973a68518aa41045fe1413aa3bbe246a4ca04cc4 Mon Sep 17 00:00:00 2001 From: bluhm Date: Sat, 22 May 2021 18:38:40 +0000 Subject: [PATCH] Test more corner cases of IPv4 fragment reassembly. --- regress/sys/netinet/frag/LICENSE | 2 +- regress/sys/netinet/frag/Makefile | 4 +- regress/sys/netinet/frag/frag_overindex.py | 65 ++++++++++++++++++ regress/sys/netinet/frag/frag_overlimit.py | 72 ++++++++++++++++++++ regress/sys/netinet/frag/frag_overreplace.py | 68 ++++++++++++++++++ 5 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 regress/sys/netinet/frag/frag_overindex.py create mode 100644 regress/sys/netinet/frag/frag_overlimit.py create mode 100644 regress/sys/netinet/frag/frag_overreplace.py diff --git a/regress/sys/netinet/frag/LICENSE b/regress/sys/netinet/frag/LICENSE index 866739d7ee3..d5bbbd0a691 100644 --- a/regress/sys/netinet/frag/LICENSE +++ b/regress/sys/netinet/frag/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012-2020 Alexander Bluhm +Copyright (c) 2012-2021 Alexander Bluhm Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/regress/sys/netinet/frag/Makefile b/regress/sys/netinet/frag/Makefile index 29f01c0e202..a534d6201e4 100644 --- a/regress/sys/netinet/frag/Makefile +++ b/regress/sys/netinet/frag/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.10 2020/12/30 21:40:33 kn Exp $ +# $OpenBSD: Makefile,v 1.11 2021/05/22 18:38:40 bluhm Exp $ # The following ports must be installed: # @@ -104,7 +104,7 @@ run-stack-frag_mf1end.py: # the stack allows fragments with MF together with fragments without MF @echo DISABLED -run-stack-frag_queuelimit.py: +run-stack-frag_queuelimit.py run-stack-frag_overlimit.py: # the stack does not limit the amount of fragments during reassembly @echo DISABLED diff --git a/regress/sys/netinet/frag/frag_overindex.py b/regress/sys/netinet/frag/frag_overindex.py new file mode 100644 index 00000000000..52f44a6263c --- /dev/null +++ b/regress/sys/netinet/frag/frag_overindex.py @@ -0,0 +1,65 @@ +#!/usr/local/bin/python3 + +print("ping fragment that overlaps the first fragment at index boundary") + +# index boundary 4096 | +# |--------------| +# .... +# |--------------| +# |XXXX-----| +# |--------------| + +# this should trigger "frag index %d, new %d" log in kernel + +import os +from addr import * +from scapy.all import * + +pid=os.getpid() +eid=pid & 0xffff +payload=b"ABCDEFGHIJKLMNOP" +dummy=b"01234567" +fragsize=64 +boundary=4096 +fragnum=int(boundary/fragsize) +packet=IP(src=LOCAL_ADDR, dst=REMOTE_ADDR)/ \ + ICMP(type='echo-request', id=eid)/ \ + (int((boundary+8)/len(payload)) * payload) +frag=[] +fid=pid & 0xffff +for i in range(fragnum-1): + frag.append(IP(src=LOCAL_ADDR, dst=REMOTE_ADDR, proto=1, id=fid, + frag=(i*fragsize)>>3, flags='MF')/ + bytes(packet)[20+i*fragsize:20+(i+1)*fragsize]) +frag.append(IP(src=LOCAL_ADDR, dst=REMOTE_ADDR, proto=1, id=fid, + frag=(boundary-8)>>3)/ + (dummy+bytes(packet)[20+boundary:20+boundary+8])) +frag.append(IP(src=LOCAL_ADDR, dst=REMOTE_ADDR, proto=1, id=fid, + frag=(boundary-fragsize)>>3, flags='MF')/ + bytes(packet)[20+boundary-fragsize:20+boundary]) +eth=[] +for f in frag: + eth.append(Ether(src=LOCAL_MAC, dst=REMOTE_MAC)/f) + +if os.fork() == 0: + time.sleep(1) + for e in eth: + sendp(e, iface=LOCAL_IF) + time.sleep(0.001) + os._exit(0) + +ans=sniff(iface=LOCAL_IF, timeout=5, filter= + "ip and src "+REMOTE_ADDR+" and dst "+LOCAL_ADDR+" and icmp") +for a in ans: + if a and a.type == ETH_P_IP and \ + a.payload.proto == 1 and \ + a.payload.frag == 0 and \ + 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) + exit(0) +print("NO ECHO REPLY") +exit(1) diff --git a/regress/sys/netinet/frag/frag_overlimit.py b/regress/sys/netinet/frag/frag_overlimit.py new file mode 100644 index 00000000000..376e8edf08c --- /dev/null +++ b/regress/sys/netinet/frag/frag_overlimit.py @@ -0,0 +1,72 @@ +#!/usr/local/bin/python3 + +print("ping fragment at index boundary that cannot be requeued") + +# index boundary 4096 | +# |--------------| +# .... +# |--------------| +# |--------------| +# ....----| +# |XXXX-----| +# |--------------| + +# this should trigger "fragment requeue limit exceeded" log in kernel + +import os +from itertools import chain +from addr import * +from scapy.all import * + +pid=os.getpid() +eid=pid & 0xffff +payload=b"ABCDEFGHIJKLMNOP" +dummy=b"01234567" +fragsize=64 +boundary=4096 +fragnum=int(boundary/fragsize) +packet=IP(src=LOCAL_ADDR, dst=REMOTE_ADDR)/ \ + ICMP(type='echo-request', id=eid)/ \ + (int((boundary+boundary)/len(payload)) * payload) +frag=[] +fid=pid & 0xffff +for i in chain(range(fragnum-1), range(fragnum, fragnum+fragnum-1)): + frag.append(IP(src=LOCAL_ADDR, dst=REMOTE_ADDR, proto=1, id=fid, + frag=(i*fragsize)>>3, flags='MF')/ + bytes(packet)[20+i*fragsize:20+(i+1)*fragsize]) +frag.append(IP(src=LOCAL_ADDR, dst=REMOTE_ADDR, proto=1, id=fid, + frag=(boundary+boundary-fragsize)>>3)/ + bytes(packet)[20+boundary+boundary-fragsize:]) +frag.append(IP(src=LOCAL_ADDR, dst=REMOTE_ADDR, proto=1, id=fid, + frag=(boundary-8)>>3, flags='MF')/ + (dummy+bytes(packet)[20+boundary:20+boundary+8])) +frag.append(IP(src=LOCAL_ADDR, dst=REMOTE_ADDR, proto=1, id=fid, + frag=(boundary-fragsize)>>3, flags='MF')/ + bytes(packet)[20+boundary-fragsize:20+boundary]) +eth=[] +for f in frag: + eth.append(Ether(src=LOCAL_MAC, dst=REMOTE_MAC)/f) + +if os.fork() == 0: + time.sleep(1) + for e in eth: + sendp(e, iface=LOCAL_IF) + time.sleep(0.001) + os._exit(0) + +ans=sniff(iface=LOCAL_IF, timeout=10, filter= + "ip and src "+REMOTE_ADDR+" and dst "+LOCAL_ADDR+" and icmp") +for a in ans: + if a and a.type == ETH_P_IP and \ + a.payload.proto == 1 and \ + a.payload.frag == 0 and \ + 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) + print("ECHO REPLY") + exit(1) +print("no echo reply") +exit(0) diff --git a/regress/sys/netinet/frag/frag_overreplace.py b/regress/sys/netinet/frag/frag_overreplace.py new file mode 100644 index 00000000000..0335d946385 --- /dev/null +++ b/regress/sys/netinet/frag/frag_overreplace.py @@ -0,0 +1,68 @@ +#!/usr/local/bin/python3 + +print("ping fragment that overlaps fragment at index boundary and replace it") + +# index boundary 4096 | +# |--------------| +# .... +# |--------------| +# |XXXX-----| +# |--------------| +# |--------------| + +# this should trigger "frag tail overlap %d" and "frag head overlap %d" + +import os +from addr import * +from scapy.all import * + +pid=os.getpid() +eid=pid & 0xffff +payload=b"ABCDEFGHIJKLMNOP" +dummy=b"01234567" +fragsize=1024 +boundary=4096 +fragnum=int(boundary/fragsize) +packet=IP(src=LOCAL_ADDR, dst=REMOTE_ADDR)/ \ + ICMP(type='echo-request', id=eid)/ \ + (int((boundary+fragsize)/len(payload)) * payload) +frag=[] +fid=pid & 0xffff +for i in range(fragnum-1): + frag.append(IP(src=LOCAL_ADDR, dst=REMOTE_ADDR, proto=1, id=fid, + frag=(i*fragsize)>>3, flags='MF')/ + bytes(packet)[20+i*fragsize:20+(i+1)*fragsize]) +frag.append(IP(src=LOCAL_ADDR, dst=REMOTE_ADDR, proto=1, id=fid, + frag=(boundary-8)>>3, flags='MF')/ + (dummy+bytes(packet)[20+boundary:20+boundary+8])) +frag.append(IP(src=LOCAL_ADDR, dst=REMOTE_ADDR, proto=1, id=fid, + frag=(boundary-fragsize)>>3, flags='MF')/ + bytes(packet)[20+boundary-fragsize:20+boundary]) +frag.append(IP(src=LOCAL_ADDR, dst=REMOTE_ADDR, proto=1, id=fid, + frag=(boundary)>>3)/bytes(packet)[20+boundary:]) +eth=[] +for f in frag: + eth.append(Ether(src=LOCAL_MAC, dst=REMOTE_MAC)/f) + +if os.fork() == 0: + time.sleep(1) + for e in eth: + sendp(e, iface=LOCAL_IF) + time.sleep(0.001) + os._exit(0) + +ans=sniff(iface=LOCAL_IF, timeout=3, filter= + "ip and src "+REMOTE_ADDR+" and dst "+LOCAL_ADDR+" and icmp") +for a in ans: + if a and a.type == ETH_P_IP and \ + a.payload.proto == 1 and \ + a.payload.frag == 0 and \ + 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) + exit(0) +print("NO ECHO REPLY") +exit(1) -- 2.20.1